diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..882041dbb5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,5 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + } +} \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..d7ad356d73 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.env +.env.example +docker-compose.yml +Dockerfile diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000..905ee6e609 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +MYSQL_ROOT_PASSWORD=masterkey diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000000..d164875220 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,92 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + push: + branches: [ "*" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} diff --git a/.gitignore b/.gitignore index fb0072a5ed..36447cbe5f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ config_db.inc.php /custom_config.inc.php **/._* upload_area/** +.env diff --git a/Dockerfile b/Dockerfile index c10a1a693b..7be6ef9c08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,21 @@ -FROM pensiero/apache-php-mysql:latest +FROM php:7.4-apache -RUN apt update -q && apt install -yqq --force-yes \ - mysql-server +RUN apt update && apt upgrade -y +RUN apt install -y \ + default-mysql-client \ + zlib1g-dev \ + libpng-dev \ + libjpeg-dev \ + libfreetype-dev +RUN docker-php-ext-install mysqli && \ + docker-php-ext-enable mysqli && \ + docker-php-ext-configure gd --with-freetype --with-jpeg && \ + docker-php-ext-install gd +RUN apt clean -# Start mysql -RUN /etc/init.d/mysql 'start' +WORKDIR /var/www/html -WORKDIR /var/www/public -COPY . ./ +COPY . . +COPY ./docker/php.ini-production /usr/local/etc/php/conf.d/php.ini + +RUN chown -R www-data:www-data /var/www/html/gui/templates_c \ No newline at end of file diff --git a/README.containers.md b/README.containers.md new file mode 100644 index 0000000000..75d719b21a --- /dev/null +++ b/README.containers.md @@ -0,0 +1,62 @@ +# Containers +## compose +```yml +networks: + testlink: + name: testlink +services: + db: + image: mysql:8.3.0 + networks: + - testlink + restart: unless-stopped + user: mysql + environment: + - MYSQL_USER=teste + - MYSQL_PASSWORD=teste + - MYSQL_ROOT_PASSWORD=teste + - MYSQL_DATABASE=testlink + volumes: + - mysql:/var/lib/mysql + + maildev: + image: maildev/maildev:latest + networks: + - testlink + ports: + - 1080:1080 + - 1025:1025 + restart: unless-stopped + environment: + - NODE_TLS_REJECT_UNAUTHORIZED=0 + + app: &app + image: ghcr.io/neiesc/testlink-code:testlink_1_9_20_fixed + restart: unless-stopped + depends_on: + db: + condition: service_started + maildev: + condition: service_started + networks: + - testlink + ports: + - 8090:80 + volumes: + - ./logs:/var/testlink/logs:Z + - ./upload_area:/var/testlink/upload_area:Z + + restore: + <<: *app + depends_on: + app: + condition: service_started + restart: no + ports: [] + profiles: + - tools + command: ['/bin/bash', '-c', 'cd ./docs/db_sample && ./restore_sample.sh'] + +volumes: + mysql: +``` \ No newline at end of file diff --git a/README.md b/README.md index 590da268be..cf1b5b31e7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Next TestLink version will 2.x with a new UX based on the Dashio - Bootstrap Adm ## Contents 1. [Introduction](#1-introduction) - 2. [Release notes / Critical Configuration Notes](2-release-notes--critical-configuration-notes) + 2. [Release notes / Critical Configuration Notes](#2-release-notes--critical-configuration-notes) 3. [System Requirements](#3-system-requirements---server) 4. [Installation & SECURITY](#4-installation--security) 5. [Upgrade and Migration](#5-upgrade-and-migration) @@ -25,6 +25,7 @@ Next TestLink version will 2.x with a new UX based on the Dashio - Bootstrap Adm 16. [User cries: I WANT HELP !!!](#16-user-cries-i-want-help-) 17. [Use Mantis documentation](#17-use-mantis-documentation) 18. [Link to GITORIOUS COMMITS](#18-link-to-gitorious-commits) +19. [Running Testlink using Docker](./docker/README.md) ## 1. Introduction @@ -178,6 +179,11 @@ ATTENTION: we have not enough resources to test on all kind of browsers. ## 4. Installation & SECURITY +### With containers +use [README.containers.md](README.containers.md) + +### Without containers + The following details the basic steps for installation on any system. Instructions may seem unix-centric but should work on Windows systems. @@ -227,6 +233,18 @@ Take a look at [bug 5147][5147], [bug 5148][5148], [bug 4977][4977] and You should also need to configure write access for logging, upload and template directories. + +### SELINUX +If you use Linux Operating System, SELINUX can create some issues: + +**ATTENTION with /var/www/html and selinux** +https://stackoverflow.com/questions/45311124/directory-is-not-writable-centos7-apache-2-4-6-php-5-4-16 + +If SELINUX is enabled you will need to run following command before been able to adjust folder rights +*[root@dogbert ~]\# chcon -R -t httpd_sys_rw_content_t /var/www/html/* + +You may find more information searching in the Internet: +TestLink & SELINUX ** FCKEDITOR UPLOAD ** **ATTENTION: We now use CKEDITOR** (see [forum post][cke]) @@ -755,7 +773,7 @@ BEFORE the commit ID. => then Nike => Just DO IT [upgf]: http://forum.testlink.org/viewforum.php?f=11 [uupg]: http://forum.testlink.org/viewforum.php?f=58 [tucf]: http://www.testlink.org/ -[mbug]: http://www.testlink.org/mantis/ +[mbug]: http://mantis.testlink.org/ [twt]: http://twitter.com/#!/TLOpenSource [free]: http://www.freetest.net.br [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet diff --git a/README.plugins b/README.plugins deleted file mode 100644 index 38896d895f..0000000000 --- a/README.plugins +++ /dev/null @@ -1,75 +0,0 @@ -=============================================================== - TestLink - README.plugins -=============================================================== - -Writing Plugins: - -To create a new plugin, create a new folder with the name of the plugin under -"plugins" folder at the base dir, for e.g. TLTest as provided in the -distribution. This should be a unique named plugin in the system. The name of -the folder acts as the name of the plugin. - -The structure inside the plugin folder will be -+ plugins -+--< Name of Plugin > -+------ < Plugin Main Class File > -+------ pages -+---------< Smarty Template files > -+---------< PHP Files to process Smarty templates > -+------ lang -+---------< language code > -+------------ strings.txt -+---------< language code > -+------------ strings.txt - -Plugin Main Class: -The plugin main class should reside directly under the plugins/ -folder. This class should extend from core TestlinkPlugin class and expose -the following properties -* name -* description -* version -* author -* url -These information will be displayed in the Plugins page, which can be used to -enable or disable a plugin in the current Testlink installation (This has not -yet been implemented). - -The plugin main class also needs to have the following methods: -* register: The basic information about the plugin including name, description, - version, author, url are maintained here. This method is mandatory -* config: Returns an array of the default configuration fields that are required - for the plugin. These values are stored in Globals against the plugin and act - as defaults whenever they are requested using plugin_config_get (See below) -* hooks: Returns an array of the hooks that this plugin listens to. The list of - hooks are defined in events_inc.php. The array value will be the method in the - plugin class that defines what needs to be done when that plugin happens. - -Utilities that will help in writing the plugin class and support files: -* $db : This variable will be available for the plugins to do any DB related - operations. The users can directly do a `doDBConnect($db)` to connect to the - $db and then use any of the methods available in database.class.php -* plugin_file_path: Function to get an absolute path to a file inside of the - plugin. For e.g. any reference to a file from one php file to another can be - done using this. This avoids the user understanding how to reference files - inside the plugin. -* plugin_config_set: Set a configuration value in the Database. The values are - stored in the plugins_configuration table. You can set a value at two levels: - Either at a testproject level or available to all test projects -* plugin_config_get: Get a configuration value from the database. The value will - be fetched from the requested testprojectId (Passed in as a paramter) and if its - not there, it will fetch a value available at "All projects" level. If its not - available there, then it picks it up from the Global Defaults (Setup through the - `config` method of the plugin) and then default to the "default" parameter provided - in the method definition. See `plugin_config_get` in `plugin_api.php` for details -* plugin_lang_get: Get a string value according to the language configured in - TestLink for the current user. See lang directory in TLTest plugin for more info - -Writing Templates that will help in Plugin Configuration: -The user might want to create pages that will help in configuring a plugin at a -testproject level or for all projects. These template files will need to reside -inside `pages` folder. We have exported the tlSmarty class so that users can write -smarty templates. The php file to support the smarty templates are also inside the -`pages` folder. Please see config.php and config.tpl inside -plugins/TLTest/pages for an example - diff --git a/README.plugins.md b/README.plugins.md new file mode 100644 index 0000000000..0fd416584a --- /dev/null +++ b/README.plugins.md @@ -0,0 +1,75 @@ +=============================================================== + TestLink - README.plugins +=============================================================== + +Writing Plugins: + +To create a new plugin, create a new folder with the name of the plugin under +"plugins" folder at the base dir, for e.g. TLTest as provided in the +distribution. This should be a unique named plugin in the system. The name of +the folder acts as the name of the plugin. + +The structure inside the plugin folder will be ++ plugins ++--< Name of Plugin > ++------ < Plugin Main Class File > ++------ pages ++---------< Smarty Template files > ++---------< PHP Files to process Smarty templates > ++------ lang ++---------< language code > ++------------ strings.txt ++---------< language code > ++------------ strings.txt + +Plugin Main Class: +The plugin main class should reside directly under the plugins/ +folder. This class should extend from core TestlinkPlugin class and expose +the following properties +* name +* description +* version +* author +* url +These information will be displayed in the Plugins page, which can be used to +enable or disable a plugin in the current Testlink installation (This has not +yet been implemented). + +The plugin main class also needs to have the following methods: +* register: The basic information about the plugin including name, description, + version, author, url are maintained here. This method is mandatory +* config: Returns an array of the default configuration fields that are required + for the plugin. These values are stored in Globals against the plugin and act + as defaults whenever they are requested using plugin_config_get (See below) +* hooks: Returns an array of the hooks that this plugin listens to. The list of + hooks are defined in events_inc.php. The array value will be the method in the + plugin class that defines what needs to be done when that plugin happens. + +Utilities that will help in writing the plugin class and support files: +* $db : This variable will be available for the plugins to do any DB related + operations. The users can directly do a `doDBConnect($db)` to connect to the + $db and then use any of the methods available in database.class.php +* plugin_file_path: Function to get an absolute path to a file inside of the + plugin. For e.g. any reference to a file from one php file to another can be + done using this. This avoids the user understanding how to reference files + inside the plugin. +* plugin_config_set: Set a configuration value in the Database. The values are + stored in the plugins_configuration table. You can set a value at two levels: + Either at a testproject level or available to all test projects +* plugin_config_get: Get a configuration value from the database. The value will + be fetched from the requested testprojectId (Passed in as a paramter) and if its + not there, it will fetch a value available at "All projects" level. If its not + available there, then it picks it up from the Global Defaults (Setup through the + `config` method of the plugin) and then default to the "default" parameter provided + in the method definition. See `plugin_config_get` in `plugin_api.php` for details +* plugin_lang_get: Get a string value according to the language configured in + TestLink for the current user. See lang directory in TLTest plugin for more info + +Writing Templates that will help in Plugin Configuration: +The user might want to create pages that will help in configuring a plugin at a +testproject level or for all projects. These template files will need to reside +inside `pages` folder. We have exported the tlSmarty class so that users can write +smarty templates. The php file to support the smarty templates are also inside the +`pages` folder. Please see config.php and config.tpl inside +plugins/TLTest/pages for an example + diff --git a/cfg/const.inc.php b/cfg/const.inc.php index 6e6394c36f..ccae4069b2 100644 --- a/cfg/const.inc.php +++ b/cfg/const.inc.php @@ -1,967 +1,1217 @@ -exec_cfg->expand_collapse - very important do not change values, logic depends on values*/ -define('LAST_USER_CHOICE',2); -define('COLLAPSE', 0); -define('EXPAND',1 ); - -// used in several functions instead of MAGIC NUMBERS - Don't change -define('ALL_PRODUCTS', 0); -define('TP_ALL_STATUS', null); -define('FILTER_BY_PRODUCT', 1); -define('FILTER_BY_TESTPROJECT', 1); -define('TP_STATUS_ACTIVE', 1); - -define('DO_LANG_GET',1 ); -define('DONT_DO_LANG_GET',0 ); - -define('LANG_GET_NO_WARNING',true); - -define('DSN', FALSE); // for method connect() of database.class -define('ANY_BUILD', null); -define('GET_NO_EXEC', 1); - -define('FILTER_OPTION_ANY', 0); - -/** @uses planTCNavigator.php */ -define('FILTER_BY_BUILD_OFF', 0); -define('FILTER_BY_OWNER_OFF', 0); -define('FILTER_BY_TC_STATUS_OFF', null); -define('FILTER_BY_KEYWORD_OFF', null); -define('FILTER_BY_ASSIGNED_TO_OFF', 0); -define('SEARCH_BY_CUSTOM_FIELDS_OFF', null); -define('COLOR_BY_TC_STATUS_OFF', null); -define('CREATE_TC_STATUS_COUNTERS_OFF', 0); - -// moved from testSetRemove.php -define('WRITE_BUTTON_ONLY_IF_LINKED', 1); - -// moved from tc_exec_assignment.php -define('FILTER_BY_TC_OFF', null); -define('FILTER_BY_EXECUTE_STATUS_OFF', null); -define('ALL_USERS_FILTER', null); -define('ADD_BLANK_OPTION', true); - -// -define('FILTER_BY_SHOW_ON_EXECUTION', 1); - -define('GET_ALSO_NOT_EXECUTED', null); -define('GET_ONLY_EXECUTED', 'executed'); - -// generateTestSpecTree() -define('FOR_PRINTING', 1); -define('NOT_FOR_PRINTING', 0); - -define('HIDE_TESTCASES', 1); -define('SHOW_TESTCASES', 0); -define('FILTER_INACTIVE_TESTCASES', 1); -define('DO_NOT_FILTER_INACTIVE_TESTCASES', 0); - -define('ACTION_TESTCASE_DISABLE', 0); -define('IGNORE_INACTIVE_TESTCASES', 1); -define('IGNORE_ACTIVE_TESTCASES', 2); - -define('DO_ON_TESTCASE_CLICK', 1); -define('NO_ADDITIONAL_ARGS', ''); -define('NO_KEYWORD_ID_TO_FILTER', 0); - - -define('RECURSIVE_MODE', TRUE); -define('NO_NODE_TYPE_TO_FILTER', null); -define('ANY_OWNER', null); - -define('ALL_BUILDS', 'a'); -define('ALL_PLATFORMS', 'a'); -define('ALL_TEST_SUITES', 'all'); - -/** @todo use consts ACTIVE || INACTIVE, OPEN || CLOSED*/ -define('GET_ACTIVE_BUILD', 1); -define('GET_INACTIVE_BUILD', 0); -define('GET_OPEN_BUILD', 1); -define('GET_CLOSED_BUILD', 0); - -define('AUTOMATIC_ID', 0); -define('NO_FILTER_SHOW_ON_EXEC', null); -define('DONT_REFRESH', 0); -define('DEFAULT_TC_ORDER', 0); - -// bug_interface->buildViewBugLink() -define('GET_BUG_SUMMARY', true); - -// gen_spec_view() -define('DO_PRUNE', 1); - -// executeTestCase() -define('AUTOMATION_RESULT_KO', -1); -define('AUTOMATION_NOTES_KO', -1); - -define('REMOVEME', 'd8ba8cfb-ca92-4fa5-83c2-551977d405fb'); - -/** Constants for plugins */ -/** Plugin configuration types */ -define('CONFIG_TYPE_STRING', 0); -define('CONFIG_TYPE_INT', 1); -define('CONFIG_TYPE_FLOAT', 2); -define('CONFIG_TYPE_COMPLEX', 3); - -/** To indicate the plugin configuration belongs to ANY PROJECT */ -define('TL_ANY_PROJECT', -1); - -/** Constants for events */ -define('EVENT_TYPE_CREATE', 1); -define('EVENT_TYPE_UPDATE', 2); -define('EVENT_TYPE_DELETE', 3); -define('EVENT_TYPE_OUTPUT', 4); - - - - -// --------------------------------------------------------------------------------- -/* [GUI] */ - -/** - * @uses planAddTC_m1-tpl - * - **/ -define('TL_STYLE_FOR_ADDED_TC', 'background-color:yellow;'); - -/** Browser Cookie keeptime */ -define('TL_COOKIE_KEEPTIME', (time()+60*60*24*30)); // 30 days - -// needed for drap and drop feature -define('TL_DRAG_DROP_DIR', 'gui/drag_and_drop/'); -define('TL_DRAG_DROP_JS_DIR', TL_DRAG_DROP_DIR. 'js/'); -define('TL_DRAG_DROP_FOLDER_CSS', TL_DRAG_DROP_DIR . 'css/drag-drop-folder-tree.css'); -define('TL_DRAG_DROP_CONTEXT_MENU_CSS', TL_DRAG_DROP_DIR . 'css/context-menu.css'); - -/** Mark up inactive objects (test projects, etc) in GUI lists */ -define('TL_INACTIVE_MARKUP', '* '); - -/** @var string Delimiter used when created a test suite path, concatenating test suite names */ -$g_testsuite_sep='/'; - -/** - * using niftycorners - * @TODO havlatm: move to smarty templates - configuration should not contain HTML elements - **/ -define('MENU_ITEM_OPEN', '
'); - -/** - * Used to force the max len of this field, during the automatic creation of requirements - * or other import features - */ -$g_field_size = new stdClass(); -$g_field_size->node_name = 100; -$g_field_size->testsuite_name = 100; -$g_field_size->testcase_name = 100; -$g_field_size->testproject_name = 100; - -// requirements and req_spec tables field sizes -$g_field_size->req_docid = 64; -$g_field_size->req_title = 100; -$g_field_size->requirement_title = 100; -$g_field_size->docid = 64; - -// execution table -$g_field_size->bug_id = 16; - - -// -------------------------------------------------------------------------------------- -/* [LOCALIZATION] */ - -/** - * String that will used as prefix, to generate an string when a label to be localized - * is passed to lang_get() to be translated, by the label is not present in the strings file. - * The resulting string will be: TL_LOCALIZE_TAG . label - * @example code specifies the key of string: lang_get('hello') -> shows "LOCALIZE: Hello" - */ -define('TL_LOCALIZE_TAG','LOCALIZE: '); - -/** @var string Default format of date */ -$tlCfg->date_format ='%d/%m/%Y'; - -/** @var string Default format of datetime */ -$tlCfg->timestamp_format = '%d/%m/%Y %H:%M:%S'; - - -/** - * @var array List of supported localizations (used in user preferences to choose one) - * DEV: Please Maintain the alphabetical order when adding new locales. - * Also check inc.ext_js_tpl to set localization for ExtJS Components. - **/ -$tlCfg->locales = array('cs_CZ' => 'Czech','de_DE' => 'German','en_GB' => 'English (wide/UK)', - 'en_US' => 'English (US)','es_AR' => 'Spanish (Argentine)', - 'es_ES' => 'Spanish','fi_FI' => 'Finnish','fr_FR' => 'Français', - 'id_ID' => 'Indonesian','it_IT' => 'Italian','ja_JP' => 'Japanese', - 'ko_KR' => 'Korean','nl_NL' => 'Dutch','pl_PL' => 'Polski', - 'pt_BR' => 'Portuguese (Brazil)','pt_PT' => 'Portuguese', - 'ru_RU' => 'Russian','zh_CN' => 'Chinese Simplified'); - -/** - * Format of date - see strftime() in PHP manual - * NOTE: setting according local is done in testlinkInitPage() using setDateTimeFormats() - */ -/** @var array Localized format of date */ -$tlCfg->locales_date_format = array('cs_CZ' => '%d.%m.%Y','de_DE' => '%d.%m.%Y','en_GB' => '%d/%m/%Y', - 'en_US' => '%m/%d/%Y','es_AR' => '%d/%m/%Y','es_ES' => '%d/%m/%Y', - 'fi_FI' => '%d/%m/%Y','fr_FR' => '%d/%m/%Y','id_ID' => '%d/%m/%Y', - 'it_IT' => '%d/%m/%Y','ja_JP' => '%Y/%m/%d','ko_KR' => '%Y/%m/%d', - 'nl_NL' => '%d-%m-%Y','pl_PL' => '%d.%m.%Y','pt_BR' => '%d/%m/%Y', - 'pt_PT' => '%d/%m/%Y','ru_RU' => '%d.%m.%Y','zh_CN' => '%Y-%m-%d'); - -/** @var array Localized format of full timestamp */ -$tlCfg->locales_timestamp_format = array('cs_CZ' => '%d.%m.%Y %H:%M:%S','de_DE' => '%d.%m.%Y %H:%M:%S', - 'en_GB' => '%d/%m/%Y %H:%M:%S','en_US' => '%m/%d/%Y %H:%M:%S', - 'es_AR' => '%d/%m/%Y %H:%M:%S','es_ES' => '%d/%m/%Y %H:%M:%S', - 'fi_FI' => '%d/%m/%Y %H:%M:%S','fr_FR' => '%d/%m/%Y %H:%M:%S', - 'id_ID' => '%d/%m/%Y %H:%M:%S','it_IT' => '%d/%m/%Y %H:%M:%S', - 'ja_JP' => '%Y/%m/%d %H:%M:%S','ko_KR' => '%Y/%m/%d %H:%M:%S', - 'nl_NL' => '%d-%m-%Y %H:%M:%S','pl_PL' => '%d.%m.%Y %H:%M:%S', - 'pt_BR' => '%d/%m/%Y %H:%M:%S','pt_PT' => '%d/%m/%Y %H:%M:%S', - 'ru_RU' => '%d.%m.%Y %H:%M:%S','zh_CN' => '%Y-%m-%d %H:%M:%S'); - -/** @var array localized date format for smarty templates (html_select_date function) - * deprecated since use of datepicker */ -$tlCfg->locales_html_select_date_field_order = array('cs_CZ' => 'dmY','de_DE' => 'dmY','en_GB' => 'dmY', - 'en_US' => 'mdY','es_AR' => 'dmY','es_ES' => 'dmY','fi_FI' => 'dmY', - 'fr_FR' => 'dmY','id_ID' => 'dmY','it_IT' => 'dmY','ja_JP' => 'Ymd', - 'ko_KR' => 'Ymd','nl_NL' => 'dmY','pl_PL' => 'dmY','pt_BR' => 'dmY', - 'pt_PT' => 'dmY','ru_RU' => 'dmY','zh_CN' => 'Ymd'); - - - -// -------------------------------------------------------------------------------------- -/* ATTACHMENTS */ - -/** Attachment key constants (do not change) */ -define('TL_REPOSITORY_TYPE_DB', 1); -define('TL_REPOSITORY_TYPE_FS', 2); - -define('TL_REPOSITORY_COMPRESSIONTYPE_NONE', 1); -define('TL_REPOSITORY_COMPRESSIONTYPE_GZIP', 2); - - -// Two models to manage attachment interface in the execution screen -// $att_model_m1 -> shows upload button and title -// -$att_model_m1 = new stdClass(); -$att_model_m2 = new stdClass(); - -$att_model_m1->show_upload_btn = true; -$att_model_m1->show_title = true; -$att_model_m1->num_cols = 4; -$att_model_m1->show_upload_column = false; - -// $att_model_m2 -> hides upload button and title -// -$att_model_m2->show_upload_btn = false; -$att_model_m2->show_title = false; -$att_model_m2->num_cols = 5; -$att_model_m2->show_upload_column = true; - - -// -------------------------------------------------------------------------------------- -/* [Test execution] */ -/** - * Note: do not change existing values (you can enhance arrays of course more into custom_config) - * If you add new statuses, please use custom_strings.txt to add your localized strings - */ - -/** User can define own test status(es) by modifying - * - $tlCfg->results['status_code'] (in custom_config.inc.php) - * - $tlCfg->results['status_label'] (in custom_config.inc.php) - * - $tlCfg->results['status_label_for_exec_ui'] (in custom_config.inc.php) - * - $tlCfg->results['charts']['status_colour'] (in custom_config.inc.php) - * - /locale//custom_strings.txt - * - /gui/themes/default/css/custom.css - * - * DO NOT define Custom test status(es) in this file - use custom_config.inc.php - */ - -/** - * @var array List of Test Case execution results (status_key -> DB code). - * The code is used in DB to store results (not GUI). - * Do not do localisation here, i.e do not change "passed" by your national language. - */ -$tlCfg->results['status_code'] = array('failed' => 'f','blocked' => 'b', - 'passed' => 'p','not_run' => 'n', - 'not_available' => 'x','unknown' => 'u', - 'all' => 'a'); - -/* for some reports */ -$tlCfg->results['status_order'] = array('not_run' => 'n', - 'passed' => 'p', - 'failed' => 'f', - 'blocked' => 'b'); - -/** - * Used to get localized string to show to users - * Order is important, because this will be display order on GUI - * - * @var array key: status ID - * value: id to use with lang_get() to get the string, from strings.txt (or custom_strings.txt) - * - * @example use the next code to get localized string of a status - * - * $results_cfg = config_get('results'); - * lang_get($results_cfg['status_label']["passed"]); - * - */ -$tlCfg->results['status_label'] = array('not_run' => 'test_status_not_run', - 'passed' => 'test_status_passed', - 'failed' => 'test_status_failed', - 'blocked'=> 'test_status_blocked', - 'all' => 'test_status_all_status', - 'not_available' => 'test_status_not_available', - 'unknown' => 'test_status_unknown'); - -// Is RIGHT to have this configuration DIFFERENT from $tlCfg->results['status_label'], -// because you must choose to not allow some of previous status be available -// on execution page. -// See this as a subset of $tlCfg->results['status_label'] -// -// Used to generate radio and buttons at user interface level. -// -// IMPORTANT NOTE ABOUT ORDER: -// Order is important, because this will be display order on User Interface -// And will be used on every feature that will need to do ordering -// according Test Case Execution Status. -// -// -// key => verbose status as defined in $tlCfg->results['status_code'] -// value => string id defined in the strings.txt file, -// used to localize the strings. -// -$tlCfg->results['status_label_for_exec_ui'] = array('not_run' => 'test_status_not_run', - 'passed' => 'test_status_passed', - 'failed' => 'test_status_failed', - 'blocked' => 'test_status_blocked'); - - -$tlCfg->results['status_icons_for_exec_ui'] = - array('passed' => array('img' => 'test_status_passed', - 'title' => 'click_passed'), - 'failed' => array('img' => 'test_status_failed', - 'title' => 'click_failed'), - 'blocked' => array('img' => 'test_status_blocked', - 'title' => 'click_blocked')); - -$tlCfg->results['status_icons_for_exec_next_ui'] = - array('passed' => array('img' => 'test_status_passed_next', - 'title' => 'click_passed_next'), - 'failed' => array('img' => 'test_status_failed_next', - 'title' => 'click_failed_next'), - 'blocked' => array('img' => 'test_status_blocked_next', - 'title' => 'click_blocked_next')); - - -$tlCfg->results['execStatusToExclude'] = array(); -$tlCfg->results['execStatusToExclude']['testcase'] = - array($tlCfg->results['status_code']['all']); - -$tlCfg->results['execStatusToExclude']['step'] = - array($tlCfg->results['status_code']['all']); - - -/** - * Selected execution result by default. Values is key from $tlCfg->results['status_label'] - * @var string - **/ -$tlCfg->results['default_status'] = 'not_run'; - -/** - * Status colours for charts - use just RGB (not colour names) - * Colours should be compiant with definition in CSS - **/ -$tlCfg->results['charts']['status_colour'] = array('not_run' => '000000','passed' => '006400', - 'failed' => 'B22222','blocked' => '00008B'); - -/* - * arrays for new filter types (BUGID 2455, BUGID 3026) - * used for testcase execution - */ -$tlCfg->execution_filter_methods['status_code'] = array('latest_execution' => 1, - 'all_builds' => 2, - 'any_build' => 3, - 'specific_build' => 4, - 'current_build' => 5); - -$tlCfg->execution_filter_methods['status_label'] = array('latest_execution' => 'filter_result_latest_execution', - 'all_builds' => 'filter_result_all_builds', - 'any_build' => 'filter_result_any_build', - 'specific_build' => 'filter_result_specific_build', - 'current_build' => 'filter_result_current_build'); - -$tlCfg->execution_filter_methods['default_type'] = $tlCfg->execution_filter_methods['status_code']['current_build']; - -/* - * same as above, but without current build - * these are used for testcase execution assignment - */ -$tlCfg->execution_assignment_filter_methods['status_code'] = array('latest_execution' => 1, - 'all_builds' => 2, - 'any_build' => 3, - 'specific_build' => 4); - -$tlCfg->execution_assignment_filter_methods['status_label'] = array('latest_execution' => 'filter_result_latest_execution', - 'all_builds' => 'filter_result_all_builds', - 'any_build' => 'filter_result_any_build', - 'specific_build' => 'filter_result_specific_build'); - -// CRITIC NOTICE -// This values has to have certain coerence with -// $js_key_to_select on init_filter_result() in tlTestCaseFilterControl.class.php -// -$tlCfg->execution_assignment_filter_methods['default_type'] = - $tlCfg->execution_assignment_filter_methods['status_code']['specific_build']; - - - -// -------------------------------------------------------------------------------------- -/* [Users & Roles] */ - -define('TL_USER_NOBODY', -1); -define('TL_USER_SOMEBODY', -2); //new user for new filtertypes in 2455 & 3026 -define('TL_NO_USER', TL_USER_NOBODY); -define('TL_USER_ANYBODY', 0); - -/** Follows CODES present in roles table - DO NOT CHANGE ON ANY CIRCUNSTANCE */ -define('TL_ROLES_MANAGER', 1); -define('TL_ROLES_ADMIN', 8); -define('TL_ROLES_LEADER', 9); -define('TL_ROLES_TESTER', 7); -define('TL_ROLES_GUEST', 5); -define('TL_ROLES_NO_RIGHTS', 3); -define('TL_ROLES_UNDEFINED', 0); -define('TL_ROLES_INHERITED', 0); - -// Roles with id > to this role can be deleted from user interface -define('TL_LAST_SYSTEM_ROLE', 9); - - -// used on user management page to give different colour -// to different roles. -// If you don't want use colouring then configure in this way -// $g_role_colour = array ( ); -$g_role_colour = array ( - 'admin' => 'white', - 'tester' => 'wheat', - 'leader' => 'acqua', - 'senior tester' => '#FFA', - 'guest' => 'pink', - 'test designer' => 'cyan', - '' => 'grey', - '' => 'seashell'); - - -// -------------------------------------------------------------------------------------- -/** LDAP authentication errors */ -define( 'ERROR_LDAP_AUTH_FAILED',1400); -define( 'ERROR_LDAP_SERVER_CONNECT_FAILED',1401); -define( 'ERROR_LDAP_UPDATE_FAILED',1402 ); -define( 'ERROR_LDAP_USER_NOT_FOUND',1403); -define( 'ERROR_LDAP_BIND_FAILED',1404); -define( 'ERROR_LDAP_START_TLS_FAILED',1405); - -// -------------------------------------------------------------------------------------- -/* [Priority, Urgency, Importance] */ - -/** @var array importance levels */ -$tlCfg->importance_levels = array(HIGH => 3,MEDIUM => 2,LOW => 1); -$tlCfg->importance['code_label'] = array(HIGH => 'high',MEDIUM => 'medium',LOW => 'low'); - - -/** @var integer Default Test case Importance offered in GUI */ -$tlCfg->testcase_importance_default = MEDIUM; - -/** @var integer Default Test case Urgency offered in GUI */ -$tlCfg->testcase_urgency_default = MEDIUM; - -/** - * @var array Used to get localized string to show to users - * key: numeric code - * value: id to use with lang_get() to get the string, from strings.txt (or custom_strings.txt) - * @since 1.8 - */ -$tlCfg->urgency['code_label'] = array(HIGH => 'urgency_high',MEDIUM => 'urgency_medium',LOW => 'urgency_low'); - - -/* priority is calculated using importance and urgency */ -$tlCfg->priority['code_label'] = array(HIGH => 'high_priority',MEDIUM => 'medium_priority',LOW => 'low_priority'); - -// -------------------------------------------------------------------------------------- -/* [States & Review] */ - - -define('TL_REL_TYPE_PARENT_CHILD', 1); -define('TL_REL_TYPE_BLOCKS_DEPENDS', 2); -define('TL_REL_TYPE_RELATED', 3); - - -/** - * data status constants are applicable for data like requirement, test case, Test Plan - * @since 2.0 - */ -/** Review status: design phase; data are not available for review or using */ -define('TL_REVIEW_STATUS_DRAFT', 1); - -/** Review status: data was reviewed and are available for using */ -define('TL_REVIEW_STATUS_FINAL', 2); - -/** Review status: data wait for review */ -define('TL_REVIEW_STATUS_REVIEW', 3); - -/** Review status: data are not applicable for using (not listed in reports and lists) */ -define('TL_REVIEW_STATUS_OBSOLETE', 4); -define('TL_REVIEW_STATUS_FUTURE', 5); - -/** - * @var array localization identifiers for review states - * @since 2.0 - **/ -$tlCfg->text_status_labels = array( - TL_REVIEW_STATUS_DRAFT => 'review_status_draft', - TL_REVIEW_STATUS_FINAL => 'review_status_final', - TL_REVIEW_STATUS_REVIEW => 'review_status_review', - TL_REVIEW_STATUS_OBSOLETE => 'review_status_obsolete', - TL_REVIEW_STATUS_FUTURE => 'review_status_future'); - -/** - * - **/ -define('TL_REQ_STATUS_VALID', 'V'); -define('TL_REQ_STATUS_NOT_TESTABLE','N'); -define('TL_REQ_STATUS_DRAFT','D'); -define('TL_REQ_STATUS_REVIEW','R'); -define('TL_REQ_STATUS_REWORK','W'); -define('TL_REQ_STATUS_FINISH','F'); -define('TL_REQ_STATUS_IMPLEMENTED','I'); -define('TL_REQ_STATUS_OBSOLETE','O'); - -// key: status; value: text label -$tlCfg->req_cfg = new stdClass(); -$tlCfg->req_cfg->status_labels = array(TL_REQ_STATUS_DRAFT => 'req_status_draft', - TL_REQ_STATUS_REVIEW => 'req_status_review', - TL_REQ_STATUS_REWORK => 'req_status_rework', - TL_REQ_STATUS_FINISH => 'req_status_finish', - TL_REQ_STATUS_IMPLEMENTED => 'req_status_implemented', - TL_REQ_STATUS_VALID => 'review_status_valid', - TL_REQ_STATUS_NOT_TESTABLE => 'req_status_not_testable', - TL_REQ_STATUS_OBSOLETE => 'req_status_obsolete'); - -/** - * Types of requirements (with respect to standards) - *
    - *
  • Info -informational character, project and user documentation. - * The type is not testable = not used for testing logic (except metrics).
  • - *
  • Feature - valid and testable functional definition (default selection)
  • - *
  • Use case
  • - *
  • Interface - user interface, communication protocols
  • - *
  • Non-functional - performance, infrastructure, robustness, security, safety, etc.
  • - *
  • Constrain - Constraints and Limitations
  • - *
- * - * CRITIC: DO NOT REMOVE ANY OF THIS CONSTANTS, BECAUSE TL EXPECT THIS TO BE DEFINED - * - * @since TestLink 1.9 - * - * IMPORTANT NOTICE: this value will be written on DB on field of type CHAR(1) - **/ -define('TL_REQ_TYPE_INFO', '1'); -define('TL_REQ_TYPE_FEATURE','2'); -define('TL_REQ_TYPE_USE_CASE','3'); -define('TL_REQ_TYPE_INTERFACE','4'); -define('TL_REQ_TYPE_NON_FUNCTIONAL','5'); -define('TL_REQ_TYPE_CONSTRAIN','6'); -define('TL_REQ_TYPE_SYSTEM_FUNCTION','7'); - - -/** - * @var array localization identifiers for requirements types - * @since TestLink 1.9 - **/ -$tlCfg->req_cfg->type_labels = array( - TL_REQ_TYPE_INFO => 'req_type_info', - TL_REQ_TYPE_FEATURE => 'req_type_feature', - TL_REQ_TYPE_USE_CASE => 'req_type_use_case', - TL_REQ_TYPE_INTERFACE => 'req_type_interface', - TL_REQ_TYPE_NON_FUNCTIONAL => 'req_type_non_functional', - TL_REQ_TYPE_CONSTRAIN => 'req_type_constrain', - TL_REQ_TYPE_SYSTEM_FUNCTION => 'req_type_system_function'); - - - -/** - * All possible types of requirement relations (BUGID 1748). - * - * Important: - * When you add your own relation types here, you also have to add localization strings - * and configure those below. - * - * Add you types ONLY AFTER LAST RESERVED - * - * @since TestLink 1.9 - * - * IMPORTANT NOTICE this will be written on DB on an INT field - **/ -define('TL_REQ_REL_TYPE_PARENT_CHILD', 1); -define('TL_REQ_REL_TYPE_BLOCKS_DEPENDS', 2); -define('TL_REQ_REL_TYPE_RELATED', 3); -define('TL_REQ_REL_TYPE_RESERVED_1', 4); -define('TL_REQ_REL_TYPE_RESERVED_2', 5); -define('TL_REQ_REL_TYPE_RESERVED_3', 6); -define('TL_REQ_REL_TYPE_RESERVED_4', 7); -define('TL_REQ_REL_TYPE_RESERVED_5', 8); -define('TL_REQ_REL_TYPE_RESERVED_6', 9); - - - -/** - * Localization identifiers for requirement relation types (BUGID 1748). - * Types, which are configured above, have to be configured - * here too with attributes "source" and "destination". - * - * Last value will be selected in GUI as default. - * - * Form has to be like this: - * - * $tlCfg->req_cfg->rel_type_labels = array( - * RELATIONNAME => array( - * 'source' => 'SOURCE_LOCALIZATION_KEY', - * 'destination' => 'DESTINATION_LOCALIZATION_KEY'), - * ... - * - * @since TestLink 1.9 - **/ -$tlCfg->req_cfg->rel_type_labels = array( - TL_REQ_REL_TYPE_PARENT_CHILD => array( - 'source' => 'req_rel_is_parent_of', - 'destination' => 'req_rel_is_child_of'), - TL_REQ_REL_TYPE_BLOCKS_DEPENDS => array( - 'source' => 'req_rel_blocks', - 'destination' => 'req_rel_depends'), - TL_REQ_REL_TYPE_RELATED => array( // this is a flat relation, so strings are identical - 'source' => 'req_rel_is_related_to', - 'destination' => 'req_rel_is_related_to') - ); - - - -$tlCfg->req_cfg->rel_type_description = array(TL_REQ_REL_TYPE_PARENT_CHILD => 'parent_child', - TL_REQ_REL_TYPE_BLOCKS_DEPENDS => 'blocks_depends', - TL_REQ_REL_TYPE_RELATED => 'related_to'); - - -/** - * @var array controls is expected_coverage must be requested at user interface. - * following conditions (OR LOGIC) must be verified to request value: - * - * a. key is NOT PRESENT (!isset()) - * b. key is present with value TRUE - * - * Working in this way configuration is simplified. - * - * @since TestLink 1.9 - **/ -$tlCfg->req_cfg->type_expected_coverage = array(TL_REQ_TYPE_INFO => false); - - - -// IMPORTANT NOTICE: this value will be written on DB on field of type CHAR(1) -define('TL_REQ_SPEC_TYPE_SECTION', '1'); -define('TL_REQ_SPEC_TYPE_USER_REQ_SPEC', '2'); -define('TL_REQ_SPEC_TYPE_SYSTEM_REQ_SPEC', '3'); - - -// define('TL_REQ_SPEC_TYPE_FUNCTIONAL_AND_DATA', 1); -// define('TL_REQ_SPEC_TYPE_LOOK_AND_FEEL',2); -// define('TL_REQ_SPEC_TYPE_USABILITY_AND_HUMANITY',3); -// define('TL_REQ_SPEC_TYPE_PERFORMANCE',4); -// define('TL_REQ_SPEC_TYPE_OPERATIONAL_AND_ENVIRONMENTAL',5); -// define('TL_REQ_SPEC_TYPE_MAINTAINABILITY_AND_SUPPORT',6); -// define('TL_REQ_SPEC_TYPE_SECURITY',7); -// define('TL_REQ_SPEC_TYPE_CULTURAL_AND_POLITICAL',8); -// define('TL_REQ_SPEC_TYPE_LEGAL',9); - -$tlCfg->req_spec_cfg = new stdClass(); -$tlCfg->req_spec_cfg->type_labels = array( - TL_REQ_SPEC_TYPE_SECTION => 'req_spec_type_section', - TL_REQ_SPEC_TYPE_USER_REQ_SPEC => 'req_spec_type_user_req_spec', - TL_REQ_SPEC_TYPE_SYSTEM_REQ_SPEC => 'req_spec_type_system_req_spec'); - - -/** - * @deprecated 1.9 - * @todo havlatm: replace by $tlCfg->req_cfg->type_labels - **/ -define('TL_REQ_TYPE_1', 'V'); -define('TL_REQ_TYPE_2', 'N'); -define('NON_TESTABLE_REQ', 'n'); -define('VALID_REQ', 'v'); - - -// -------------------------------------------------------------------------------------- -/* [CUSTOM FIELDS] */ - -// /** -// * Custom field constrains for HTML inputs use values to created to get/show custom field contents -// *
    -// *
  • for string,numeric,float,email: size & maxlenght of the input type text.
  • -// *
  • for list,email size of the select input.
  • -// *
-// */ -// $tlCfg->gui->custom_fields->sizes = array( -// 'string' => 50, -// 'numeric'=> 10, -// 'float' => 10, -// 'email' => 50, -// 'list' => 1, -// 'multiselection list' => 5, -// 'text area' => array('cols' => 40, 'rows' => 6) -// ); - - - -// 20080815 - franciscom -// Use this variable (on custom_config.inc.php) to define new Custom Field types. -// IMPORTANT: -// check $custom_field_types property on cfield_mgr.class.php -// to avoid overwrite of standard types. -// -// $tlCfg->gui->custom_fields->types = null; - -// Use this variable (on custom_config.inc.php) -// to define possible values behaviour for new Custom Field types. -// -// IMPORTANT: -// check $possible_values_cfg property on cfield_mgr.class.php -// to avoid overwrite of standard values. -// -// $tlCfg->gui->custom_fields->possible_values_cfg = null; - -// Format string follows date() spec - see PHP Manual -// We can not use $g_timestamp_format, because format strings for date() and strftime() -// uses same LETTER with different meanings (Bad Luck!) -$tlCfg->gui = new stdClass(); -$tlCfg->gui->custom_fields = new stdClass(); -$tlCfg->gui->custom_fields->time_format = 'H:i:s'; - - -// -------------------------------------------------------------------------------------- -/* [MISC] */ - -/** - * Review types - user can define type for his review comment (disabled by default) - * @since TestLink version 2.0 - **/ -$tlCfg->review_types = array(1 => 'undefined',2 => 'typo',3 => 'recommendation', - 4 => 'question',5 => 'unclear',6 => 'major problem'); - -/** - * Top Menu definition - * - * structure - * - label: label to display, will be localized - * - url: resource to access when users click on menu item - * - right: user right need to display menu item. (can be an array) - * null => no right check needed - * - condition: specific condition = ['','TestPlanAvailable','ReqMgmtEnabled'] - * - shortcut: keyboard HTML shortcut - * - target: window/frame name (mainframe in the most of cases) - * - imgKey - * - * @since TestLink version 1.9 - */ -$tlCfg->guiTopMenu[1] = array('label' => 'home','url' => 'index.php','right' => null, - 'imgKey' => 'home', - 'condition'=>'','shortcut'=>'h','target'=>'_parent'); - -$tlCfg->guiTopMenu[2] = array('label' => 'title_requirements', - 'imgKey' => 'requirements', - 'url' => 'lib/general/frmWorkArea.php?feature=reqSpecMgmt', - 'right' => array('mgt_view_req','mgt_modify_req'), - 'condition'=>'ReqMgmtEnabled', - 'shortcut'=>'r','target'=>'mainframe'); - -$tlCfg->guiTopMenu[3] = array('label' => 'title_specification', - 'imgKey' => 'test_specification', - 'url' => 'lib/general/frmWorkArea.php?feature=editTc', - 'right' => array('mgt_view_tc','mgt_modify_tc'), - 'condition'=>'', - 'shortcut'=>'t','target'=>'mainframe'); - -$tlCfg->guiTopMenu[4] = array('label' => 'title_execute', - 'imgKey' => 'execution', - 'url' => 'lib/general/frmWorkArea.php?feature=executeTest', - 'right' => array('testplan_execute','exec_ro_access'), - 'condition'=>'TestPlanAvailable', - 'shortcut'=>'e','target'=>'mainframe'); - -$tlCfg->guiTopMenu[5] = array('label' => 'title_results', - 'imgKey' => 'report', - 'url' => 'lib/general/frmWorkArea.php?feature=showMetrics', - 'right' => 'testplan_metrics','condition'=>'TestPlanAvailable', - 'shortcut'=>'r','target'=>'mainframe'); - -$tlCfg->guiTopMenu[6] = array('label' => 'title_admin', - 'imgKey' => 'user', - 'url' => 'lib/usermanagement/usersView.php', - 'right' => 'mgt_users','condition'=>'', - 'shortcut'=>'u','target'=>'mainframe'); - -$tlCfg->guiTopMenu[7] = array('label' => 'title_events', - 'imgKey' => 'events', - 'url' => 'lib/events/eventviewer.php', - 'right' => array('events_mgt', 'mgt_view_events'),'condition'=>'', - 'shortcut'=>'v','target'=>'mainframe'); -$tlCfg->guiTopMenu[8] = array('label' => 'title_plugins', - 'imgKey' => 'plugins', - 'url' => 'lib/plugins/pluginView.php', - 'right' => array('mgt_plugins'),'condition'=>'', - 'shortcut'=>'p','target'=>'mainframe'); - - -define( 'PARTIAL_URL_TL_FILE_FORMATS_DOCUMENT', 'docs/tl-file-formats.pdf'); - - -// Configure Charts dimension -$tlCfg->results['charts']['dimensions'] = - array('topLevelSuitesBarChart' => array('chartTitle' => 'results_top_level_suites', - 'XSize' => 900,'YSize' => 400,'beginX' => 40, 'beginY' => 100, - 'legendXAngle' => 35 ), - 'keywordBarChart' => array('chartTitle' => 'results_by_keyword', - 'XSize' => 900,'YSize' => 400,'beginX' => 40, 'beginY' => 100, - 'legendXAngle' => 25 ), - 'ownerBarChart' => array('chartTitle' => 'results_by_tester', - 'XSize' => 900,'YSize' => 400,'beginX' => 40, 'beginY' => 100, - 'legendXAngle' => 35 ), - 'overallPieChart' => array('chartTitle' => 'results_by_tester', - 'XSize' => 400,'YSize' => 400,'radius' => 150, 'legendX' => 10, 'legendY' => 15 ), - 'platformPieChart' => array('chartTitle' => 'results_by_tester', - 'XSize' => 400,'YSize' => 400,'radius' => 150, 'legendX' => 10, 'legendY' => 15 ) - ); - -// if you need to define new one, start on 20 please. -// see strings.txt for labels -// $TLS_testCaseStatus_KEY => $TLS_testCaseStatus_draft -// -$tlCfg->testCaseStatus = array( 'draft' => 1, 'readyForReview' => 2, - 'reviewInProgress' => 3, 'rework' => 4, - 'obsolete' => 5, 'future' => 6, 'final' => 7 ); - - -/** @uses testcase.class.php */ -// if you need to define new one, start on 20 please. -// see strings.txt for labels -// $TLS_execution_type_KEY => $TLS_execution_type_manual -$tlCfg->execution_type = array( 'manual' => 1, 'auto' => 2); - -// To be removed -define('TESTCASE_EXECUTION_TYPE_MANUAL', $tlCfg->execution_type['manual']); -define('TESTCASE_EXECUTION_TYPE_AUTO', $tlCfg->execution_type['auto']); - -// for link_status field on req_coverage table -define('LINK_TC_REQ_OPEN', 1); -define('LINK_TC_REQ_CLOSED_BY_EXEC', 2); -define('LINK_TC_REQ_CLOSED_BY_NEW_TCVERSION', 3); -define('LINK_TC_REQ_CLOSED_BY_NEW_REQVERSION', 4); - -// for link_status field on testcase_relations table -define('LINK_TC_RELATION_OPEN', 1); -define('LINK_TC_RELATION_CLOSED_BY_EXEC', 2); -define('LINK_TC_RELATION_CLOSED_BY_NEW_TCVERSION', 3); - - -define('USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS', 1); -define('USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS',2); -define('USE_LATEST_EXEC_ON_TESTPLAN_PLAT_FOR_COUNTERS',3); - - -// END +exec_cfg->expand_collapse + * very important do not change values, logic depends on values + */ +define('LAST_USER_CHOICE', 2); +define('COLLAPSE', 0); +define('EXPAND', 1); + +// used in several functions instead of MAGIC NUMBERS - Don't change +define('ALL_PRODUCTS', 0); +define('TP_ALL_STATUS', null); +define('FILTER_BY_PRODUCT', 1); +define('FILTER_BY_TESTPROJECT', 1); +define('TP_STATUS_ACTIVE', 1); + +define('DO_LANG_GET', 1); +define('DONT_DO_LANG_GET', 0); + +define('LANG_GET_NO_WARNING', true); + +define('DSN', false); // for method connect() of database.class +define('ANY_BUILD', null); +define('GET_NO_EXEC', 1); + +define('FILTER_OPTION_ANY', 0); + +/** + * + * @uses planTCNavigator.php + */ +define('FILTER_BY_BUILD_OFF', 0); +define('FILTER_BY_OWNER_OFF', 0); +define('FILTER_BY_TC_STATUS_OFF', null); +define('FILTER_BY_KEYWORD_OFF', null); +define('FILTER_BY_ASSIGNED_TO_OFF', 0); +define('SEARCH_BY_CUSTOM_FIELDS_OFF', null); +define('COLOR_BY_TC_STATUS_OFF', null); +define('CREATE_TC_STATUS_COUNTERS_OFF', 0); + +// moved from testSetRemove.php +define('WRITE_BUTTON_ONLY_IF_LINKED', 1); + +// moved from tc_exec_assignment.php +define('FILTER_BY_TC_OFF', null); +define('FILTER_BY_EXECUTE_STATUS_OFF', null); +define('ALL_USERS_FILTER', null); +define('ADD_BLANK_OPTION', true); + +// +define('FILTER_BY_SHOW_ON_EXECUTION', 1); + +define('GET_ALSO_NOT_EXECUTED', null); +define('GET_ONLY_EXECUTED', 'executed'); + +// generateTestSpecTree() +define('FOR_PRINTING', 1); +define('NOT_FOR_PRINTING', 0); + +define('HIDE_TESTCASES', 1); +define('SHOW_TESTCASES', 0); +define('FILTER_INACTIVE_TESTCASES', 1); +define('DO_NOT_FILTER_INACTIVE_TESTCASES', 0); + +define('ACTION_TESTCASE_DISABLE', 0); +define('IGNORE_INACTIVE_TESTCASES', 1); +define('IGNORE_ACTIVE_TESTCASES', 2); + +define('DO_ON_TESTCASE_CLICK', 1); +define('NO_ADDITIONAL_ARGS', ''); +define('NO_KEYWORD_ID_TO_FILTER', 0); + +define('RECURSIVE_MODE', true); +define('NO_NODE_TYPE_TO_FILTER', null); +define('ANY_OWNER', null); + +define('ALL_BUILDS', 'a'); +define('ALL_PLATFORMS', 'a'); +define('ALL_TEST_SUITES', 'all'); + +/** + * + * @todo use consts ACTIVE || INACTIVE, OPEN || CLOSED + */ +define('GET_ACTIVE_BUILD', 1); +define('GET_INACTIVE_BUILD', 0); +define('GET_OPEN_BUILD', 1); +define('GET_CLOSED_BUILD', 0); + +define('AUTOMATIC_ID', 0); +define('NO_FILTER_SHOW_ON_EXEC', null); +define('DONT_REFRESH', 0); +define('DEFAULT_TC_ORDER', 0); + +// bug_interface->buildViewBugLink() +define('GET_BUG_SUMMARY', true); + +// gen_spec_view() +define('DO_PRUNE', 1); + +// executeTestCase() +define('AUTOMATION_RESULT_KO', - 1); +define('AUTOMATION_NOTES_KO', - 1); + +define('REMOVEME', 'd8ba8cfb-ca92-4fa5-83c2-551977d405fb'); + +/** + * Constants for plugins + */ +/** + * Plugin configuration types + */ +define('CONFIG_TYPE_STRING', 0); +define('CONFIG_TYPE_INT', 1); +define('CONFIG_TYPE_FLOAT', 2); +define('CONFIG_TYPE_COMPLEX', 3); + +/** + * To indicate the plugin configuration belongs to ANY PROJECT + */ +define('TL_ANY_PROJECT', - 1); + +/** + * Constants for events + */ +define('EVENT_TYPE_CREATE', 1); +define('EVENT_TYPE_UPDATE', 2); +define('EVENT_TYPE_DELETE', 3); +define('EVENT_TYPE_OUTPUT', 4); + +// --------------------------------------------------------------------------------- +/* [GUI] */ + +/** + * + * @uses planAddTC_m1-tpl + * + */ +define('TL_STYLE_FOR_ADDED_TC', 'background-color:yellow;'); + +/** + * Browser Cookie keeptime + */ +define('TL_COOKIE_KEEPTIME', (time() + 60 * 60 * 24 * 30)); // 30 days + +// needed for drap and drop feature +define('TL_DRAG_DROP_DIR', 'gui/drag_and_drop/'); +define('TL_DRAG_DROP_JS_DIR', TL_DRAG_DROP_DIR . 'js/'); +define('TL_DRAG_DROP_FOLDER_CSS', + TL_DRAG_DROP_DIR . 'css/drag-drop-folder-tree.css'); +define('TL_DRAG_DROP_CONTEXT_MENU_CSS', + TL_DRAG_DROP_DIR . 'css/context-menu.css'); + +/** + * Mark up inactive objects (test projects, etc) in GUI lists + */ +define('TL_INACTIVE_MARKUP', '* '); + +/** @var string Delimiter used when created a test suite path, concatenating test suite names */ +$g_testsuite_sep = '/'; + +/** + * using niftycorners + * + * @todo havlatm: move to smarty templates - configuration should not contain HTML elements + */ +define('MENU_ITEM_OPEN', '
'); + +/** + * Used to force the max len of this field, during the automatic creation of requirements + * or other import features + */ +$g_field_size = new stdClass(); +$g_field_size->node_name = 100; +$g_field_size->testsuite_name = 100; +$g_field_size->testcase_name = 100; +$g_field_size->testproject_name = 100; + +// requirements and req_spec tables field sizes +$g_field_size->req_docid = 64; +$g_field_size->req_title = 100; +$g_field_size->requirement_title = 100; +$g_field_size->docid = 64; + +// execution table +$g_field_size->bug_id = 16; + +// -------------------------------------------------------------------------------------- +/* [LOCALIZATION] */ + +/** + * String that will used as prefix, to generate an string when a label to be localized + * is passed to lang_get() to be translated, by the label is not present in the strings file. + * The resulting string will be: TL_LOCALIZE_TAG . label + * + * @example code specifies the key of string: lang_get('hello') -> shows "LOCALIZE: Hello" + */ +define('TL_LOCALIZE_TAG', 'LOCALIZE: '); + +/** @var string Default format of date */ +$tlCfg->date_format = '%d/%m/%Y'; + +/** @var string Default format of datetime */ +$tlCfg->timestamp_format = '%d/%m/%Y %H:%M:%S'; + +/** + * + * @var array List of supported localizations (used in user preferences to choose one) + * DEV: Please Maintain the alphabetical order when adding new locales. + * Also check inc.ext_js_tpl to set localization for ExtJS Components. + */ +$tlCfg->locales = array( + 'cs_CZ' => 'Czech', + 'de_DE' => 'German', + 'en_GB' => 'English (wide/UK)', + 'en_US' => 'English (US)', + 'es_AR' => 'Spanish (Argentine)', + 'es_ES' => 'Spanish', + 'fi_FI' => 'Finnish', + 'fr_FR' => 'Français', + 'id_ID' => 'Indonesian', + 'it_IT' => 'Italian', + 'ja_JP' => 'Japanese', + 'ko_KR' => 'Korean', + 'nl_NL' => 'Dutch', + 'pl_PL' => 'Polski', + 'pt_BR' => 'Portuguese (Brazil)', + 'pt_PT' => 'Portuguese', + 'ru_RU' => 'Russian', + 'zh_CN' => 'Chinese Simplified' +); + +/** + * Format of date - see strftime() in PHP manual + * NOTE: setting according local is done in testlinkInitPage() using setDateTimeFormats() + */ +/** @var array Localized format of date */ +$tlCfg->locales_date_format = array( + 'cs_CZ' => '%d.%m.%Y', + 'de_DE' => '%d.%m.%Y', + 'en_GB' => '%d/%m/%Y', + 'en_US' => '%m/%d/%Y', + 'es_AR' => '%d/%m/%Y', + 'es_ES' => '%d/%m/%Y', + 'fi_FI' => '%d/%m/%Y', + 'fr_FR' => '%d/%m/%Y', + 'id_ID' => '%d/%m/%Y', + 'it_IT' => '%d/%m/%Y', + 'ja_JP' => '%Y/%m/%d', + 'ko_KR' => '%Y/%m/%d', + 'nl_NL' => '%d-%m-%Y', + 'pl_PL' => '%d.%m.%Y', + 'pt_BR' => '%d/%m/%Y', + 'pt_PT' => '%d/%m/%Y', + 'ru_RU' => '%d.%m.%Y', + 'zh_CN' => '%Y-%m-%d' +); + +/** @var array Localized format of full timestamp */ +$tlCfg->locales_timestamp_format = array( + 'cs_CZ' => '%d.%m.%Y %H:%M:%S', + 'de_DE' => '%d.%m.%Y %H:%M:%S', + 'en_GB' => '%d/%m/%Y %H:%M:%S', + 'en_US' => '%m/%d/%Y %H:%M:%S', + 'es_AR' => '%d/%m/%Y %H:%M:%S', + 'es_ES' => '%d/%m/%Y %H:%M:%S', + 'fi_FI' => '%d/%m/%Y %H:%M:%S', + 'fr_FR' => '%d/%m/%Y %H:%M:%S', + 'id_ID' => '%d/%m/%Y %H:%M:%S', + 'it_IT' => '%d/%m/%Y %H:%M:%S', + 'ja_JP' => '%Y/%m/%d %H:%M:%S', + 'ko_KR' => '%Y/%m/%d %H:%M:%S', + 'nl_NL' => '%d-%m-%Y %H:%M:%S', + 'pl_PL' => '%d.%m.%Y %H:%M:%S', + 'pt_BR' => '%d/%m/%Y %H:%M:%S', + 'pt_PT' => '%d/%m/%Y %H:%M:%S', + 'ru_RU' => '%d.%m.%Y %H:%M:%S', + 'zh_CN' => '%Y-%m-%d %H:%M:%S' +); + +/** + * + * @var array localized date format for smarty templates (html_select_date function) + * deprecated since use of datepicker + */ +$tlCfg->locales_html_select_date_field_order = array( + 'cs_CZ' => 'dmY', + 'de_DE' => 'dmY', + 'en_GB' => 'dmY', + 'en_US' => 'mdY', + 'es_AR' => 'dmY', + 'es_ES' => 'dmY', + 'fi_FI' => 'dmY', + 'fr_FR' => 'dmY', + 'id_ID' => 'dmY', + 'it_IT' => 'dmY', + 'ja_JP' => 'Ymd', + 'ko_KR' => 'Ymd', + 'nl_NL' => 'dmY', + 'pl_PL' => 'dmY', + 'pt_BR' => 'dmY', + 'pt_PT' => 'dmY', + 'ru_RU' => 'dmY', + 'zh_CN' => 'Ymd' +); + +// -------------------------------------------------------------------------------------- +/* ATTACHMENTS */ + +/** + * Attachment key constants (do not change) + */ +define('TL_REPOSITORY_TYPE_DB', 1); +define('TL_REPOSITORY_TYPE_FS', 2); + +define('TL_REPOSITORY_COMPRESSIONTYPE_NONE', 1); +define('TL_REPOSITORY_COMPRESSIONTYPE_GZIP', 2); + +// Two models to manage attachment interface in the execution screen +// $att_model_m1 -> shows upload button and title +// +$att_model_m1 = new stdClass(); +$att_model_m2 = new stdClass(); + +$att_model_m1->show_upload_btn = true; +$att_model_m1->show_title = true; +$att_model_m1->num_cols = 4; +$att_model_m1->show_upload_column = false; + +// $att_model_m2 -> hides upload button and title +// +$att_model_m2->show_upload_btn = false; +$att_model_m2->show_title = false; +$att_model_m2->num_cols = 5; +$att_model_m2->show_upload_column = true; + +// -------------------------------------------------------------------------------------- +/* [Test execution] */ +/** + * Note: do not change existing values (you can enhance arrays of course more into custom_config) + * If you add new statuses, please use custom_strings.txt to add your localized strings + */ + +/** + * User can define own test status(es) by modifying + * - $tlCfg->results['status_code'] (in custom_config.inc.php) + * - $tlCfg->results['status_label'] (in custom_config.inc.php) + * - $tlCfg->results['status_label_for_exec_ui'] (in custom_config.inc.php) + * - $tlCfg->results['charts']['status_colour'] (in custom_config.inc.php) + * - /locale//custom_strings.txt + * - /gui/themes/default/css/custom.css + * + * DO NOT define Custom test status(es) in this file - use custom_config.inc.php + */ + +/** + * + * @var array List of Test Case execution results (status_key -> DB code). + * The code is used in DB to store results (not GUI). + * Do not do localisation here, i.e do not change "passed" by your national language. + */ +$tlCfg->results['status_code'] = array( + 'failed' => 'f', + 'blocked' => 'b', + 'passed' => 'p', + 'not_run' => 'n', + 'not_available' => 'x', + 'unknown' => 'u', + 'all' => 'a' +); + +/* for some reports */ +$tlCfg->results['status_order'] = array( + 'not_run' => 'n', + 'passed' => 'p', + 'failed' => 'f', + 'blocked' => 'b' +); + +/** + * Used to get localized string to show to users + * Order is important, because this will be display order on GUI + * + * @var array key: status ID + * value: id to use with lang_get() to get the string, from strings.txt (or custom_strings.txt) + * + * @example use the next code to get localized string of a status + * + * $results_cfg = config_get('results'); + * lang_get($results_cfg['status_label']["passed"]); + * + */ +$tlCfg->results['status_label'] = array( + 'not_run' => 'test_status_not_run', + 'passed' => 'test_status_passed', + 'failed' => 'test_status_failed', + 'blocked' => 'test_status_blocked', + 'all' => 'test_status_all_status', + 'not_available' => 'test_status_not_available', + 'unknown' => 'test_status_unknown' +); + +// Is RIGHT to have this configuration DIFFERENT from $tlCfg->results['status_label'], +// because you must choose to not allow some of previous status be available +// on execution page. +// See this as a subset of $tlCfg->results['status_label'] +// +// Used to generate radio and buttons at user interface level. +// +// IMPORTANT NOTE ABOUT ORDER: +// Order is important, because this will be display order on User Interface +// And will be used on every feature that will need to do ordering +// according Test Case Execution Status. +// +// +// key => verbose status as defined in $tlCfg->results['status_code'] +// value => string id defined in the strings.txt file, +// used to localize the strings. +// +$tlCfg->results['status_label_for_exec_ui'] = array( + 'not_run' => 'test_status_not_run', + 'passed' => 'test_status_passed', + 'failed' => 'test_status_failed', + 'blocked' => 'test_status_blocked' +); + +$tlCfg->results['status_icons_for_exec_ui'] = array( + 'passed' => array( + 'img' => 'test_status_passed', + 'title' => 'click_passed' + ), + 'failed' => array( + 'img' => 'test_status_failed', + 'title' => 'click_failed' + ), + 'blocked' => array( + 'img' => 'test_status_blocked', + 'title' => 'click_blocked' + ) +); + +$tlCfg->results['status_icons_for_exec_next_ui'] = array( + 'passed' => array( + 'img' => 'test_status_passed_next', + 'title' => 'click_passed_next' + ), + 'failed' => array( + 'img' => 'test_status_failed_next', + 'title' => 'click_failed_next' + ), + 'blocked' => array( + 'img' => 'test_status_blocked_next', + 'title' => 'click_blocked_next' + ) +); + +$tlCfg->results['execStatusToExclude'] = array(); +$tlCfg->results['execStatusToExclude']['testcase'] = array( + $tlCfg->results['status_code']['all'] +); + +$tlCfg->results['execStatusToExclude']['step'] = array( + $tlCfg->results['status_code']['all'] +); + +/** + * Selected execution result by default. + * Values is key from $tlCfg->results['status_label'] + * + * @var string + */ +$tlCfg->results['default_status'] = 'not_run'; + +/** + * Status colours for charts - use just RGB (not colour names) + * Colours should be compiant with definition in CSS + */ +$tlCfg->results['charts']['status_colour'] = array( + 'not_run' => '000000', + 'passed' => '006400', + 'failed' => 'B22222', + 'blocked' => '00008B' +); + +/* + * arrays for new filter types (BUGID 2455, BUGID 3026) + * used for testcase execution + */ +$tlCfg->execution_filter_methods['status_code'] = array( + 'latest_execution' => 1, + 'all_builds' => 2, + 'any_build' => 3, + 'specific_build' => 4, + 'current_build' => 5 +); + +$tlCfg->execution_filter_methods['status_label'] = array( + 'latest_execution' => 'filter_result_latest_execution', + 'all_builds' => 'filter_result_all_builds', + 'any_build' => 'filter_result_any_build', + 'specific_build' => 'filter_result_specific_build', + 'current_build' => 'filter_result_current_build' +); + +$tlCfg->execution_filter_methods['default_type'] = $tlCfg->execution_filter_methods['status_code']['current_build']; + +/* + * same as above, but without current build + * these are used for testcase execution assignment + */ +$tlCfg->execution_assignment_filter_methods['status_code'] = array( + 'latest_execution' => 1, + 'all_builds' => 2, + 'any_build' => 3, + 'specific_build' => 4 +); + +$tlCfg->execution_assignment_filter_methods['status_label'] = array( + 'latest_execution' => 'filter_result_latest_execution', + 'all_builds' => 'filter_result_all_builds', + 'any_build' => 'filter_result_any_build', + 'specific_build' => 'filter_result_specific_build' +); + +// CRITIC NOTICE +// This values has to have certain coerence with +// $js_key_to_select on init_filter_result() in tlTestCaseFilterControl.class.php +// +$tlCfg->execution_assignment_filter_methods['default_type'] = $tlCfg->execution_assignment_filter_methods['status_code']['specific_build']; + +// -------------------------------------------------------------------------------------- +/* [Users & Roles] */ + +define('TL_USER_NOBODY', - 1); +define('TL_USER_SOMEBODY', - 2); // new user for new filtertypes in 2455 & 3026 +define('TL_NO_USER', TL_USER_NOBODY); +define('TL_USER_ANYBODY', 0); + +/** + * Follows CODES present in roles table - DO NOT CHANGE ON ANY CIRCUNSTANCE + */ +define('TL_ROLES_MANAGER', 1); +define('TL_ROLES_ADMIN', 8); +define('TL_ROLES_LEADER', 9); +define('TL_ROLES_TESTER', 7); +define('TL_ROLES_GUEST', 5); +define('TL_ROLES_NO_RIGHTS', 3); +define('TL_ROLES_UNDEFINED', 0); +define('TL_ROLES_INHERITED', 0); + +// Roles with id > to this role can be deleted from user interface +define('TL_LAST_SYSTEM_ROLE', 9); + +// used on user management page to give different colour +// to different roles. +// If you don't want use colouring then configure in this way +// $g_role_colour = array ( ); +$g_role_colour = array( + 'admin' => 'white', + 'tester' => 'wheat', + 'leader' => 'acqua', + 'senior tester' => '#FFA', + 'guest' => 'pink', + 'test designer' => 'cyan', + '' => 'grey', + '' => 'seashell' +); + +// -------------------------------------------------------------------------------------- +/** + * LDAP authentication errors + */ +define('ERROR_LDAP_AUTH_FAILED', 1400); +define('ERROR_LDAP_SERVER_CONNECT_FAILED', 1401); +define('ERROR_LDAP_UPDATE_FAILED', 1402); +define('ERROR_LDAP_USER_NOT_FOUND', 1403); +define('ERROR_LDAP_BIND_FAILED', 1404); +define('ERROR_LDAP_START_TLS_FAILED', 1405); + +// -------------------------------------------------------------------------------------- +/* [Priority, Urgency, Importance] */ + +/** @var array importance levels */ +$tlCfg->importance_levels = array( + HIGH => 3, + MEDIUM => 2, + LOW => 1 +); +$tlCfg->importance['code_label'] = array( + HIGH => 'high', + MEDIUM => 'medium', + LOW => 'low' +); + +/** @var integer Default Test case Importance offered in GUI */ +$tlCfg->testcase_importance_default = MEDIUM; + +/** @var integer Default Test case Urgency offered in GUI */ +$tlCfg->testcase_urgency_default = MEDIUM; + +/** + * + * @var array Used to get localized string to show to users + * key: numeric code + * value: id to use with lang_get() to get the string, from strings.txt (or custom_strings.txt) + * @since 1.8 + */ +$tlCfg->urgency['code_label'] = array( + HIGH => 'urgency_high', + MEDIUM => 'urgency_medium', + LOW => 'urgency_low' +); + +/* priority is calculated using importance and urgency */ +$tlCfg->priority['code_label'] = array( + HIGH => 'high_priority', + MEDIUM => 'medium_priority', + LOW => 'low_priority' +); + +// -------------------------------------------------------------------------------------- +/* [States & Review] */ + +define('TL_REL_TYPE_PARENT_CHILD', 1); +define('TL_REL_TYPE_BLOCKS_DEPENDS', 2); +define('TL_REL_TYPE_RELATED', 3); +define('TL_REL_TYPE_AUTOMATION_PARENT_CHILD', 4); +define('TL_REL_TYPE_EXECUTE_TOGETHER', 5); + +/** + * data status constants are applicable for data like requirement, test case, Test Plan + * + * @since 2.0 + */ +/** + * Review status: design phase; data are not available for review or using + */ +define('TL_REVIEW_STATUS_DRAFT', 1); + +/** + * Review status: data was reviewed and are available for using + */ +define('TL_REVIEW_STATUS_FINAL', 2); + +/** + * Review status: data wait for review + */ +define('TL_REVIEW_STATUS_REVIEW', 3); + +/** + * Review status: data are not applicable for using (not listed in reports and lists) + */ +define('TL_REVIEW_STATUS_OBSOLETE', 4); +define('TL_REVIEW_STATUS_FUTURE', 5); + +/** + * + * @var array localization identifiers for review states + * @since 2.0 + */ +$tlCfg->text_status_labels = array( + TL_REVIEW_STATUS_DRAFT => 'review_status_draft', + TL_REVIEW_STATUS_FINAL => 'review_status_final', + TL_REVIEW_STATUS_REVIEW => 'review_status_review', + TL_REVIEW_STATUS_OBSOLETE => 'review_status_obsolete', + TL_REVIEW_STATUS_FUTURE => 'review_status_future' +); + +/** + */ +define('TL_REQ_STATUS_VALID', 'V'); +define('TL_REQ_STATUS_NOT_TESTABLE', 'N'); +define('TL_REQ_STATUS_DRAFT', 'D'); +define('TL_REQ_STATUS_REVIEW', 'R'); +define('TL_REQ_STATUS_REWORK', 'W'); +define('TL_REQ_STATUS_FINISH', 'F'); +define('TL_REQ_STATUS_IMPLEMENTED', 'I'); +define('TL_REQ_STATUS_OBSOLETE', 'O'); + +// key: status; value: text label +$tlCfg->req_cfg = new stdClass(); +$tlCfg->req_cfg->status_labels = array( + TL_REQ_STATUS_DRAFT => 'req_status_draft', + TL_REQ_STATUS_REVIEW => 'req_status_review', + TL_REQ_STATUS_REWORK => 'req_status_rework', + TL_REQ_STATUS_FINISH => 'req_status_finish', + TL_REQ_STATUS_IMPLEMENTED => 'req_status_implemented', + TL_REQ_STATUS_VALID => 'review_status_valid', + TL_REQ_STATUS_NOT_TESTABLE => 'req_status_not_testable', + TL_REQ_STATUS_OBSOLETE => 'req_status_obsolete' +); + +/** + * Types of requirements (with respect to standards) + *
    + *
  • Info -informational character, project and user documentation. + * The type is not testable = not used for testing logic (except metrics).
  • + *
  • Feature - valid and testable functional definition (default selection)
  • + *
  • Use case
  • + *
  • Interface - user interface, communication protocols
  • + *
  • Non-functional - performance, infrastructure, robustness, security, safety, etc.
  • + *
  • Constrain - Constraints and Limitations
  • + *
+ * + * CRITIC: DO NOT REMOVE ANY OF THIS CONSTANTS, BECAUSE TL EXPECT THIS TO BE DEFINED + * + * @since TestLink 1.9 + * + * IMPORTANT NOTICE: this value will be written on DB on field of type CHAR(1) + */ +define('TL_REQ_TYPE_INFO', '1'); +define('TL_REQ_TYPE_FEATURE', '2'); +define('TL_REQ_TYPE_USE_CASE', '3'); +define('TL_REQ_TYPE_INTERFACE', '4'); +define('TL_REQ_TYPE_NON_FUNCTIONAL', '5'); +define('TL_REQ_TYPE_CONSTRAIN', '6'); +define('TL_REQ_TYPE_SYSTEM_FUNCTION', '7'); + +/** + * + * @var array localization identifiers for requirements types + * @since TestLink 1.9 + */ +$tlCfg->req_cfg->type_labels = array( + TL_REQ_TYPE_INFO => 'req_type_info', + TL_REQ_TYPE_FEATURE => 'req_type_feature', + TL_REQ_TYPE_USE_CASE => 'req_type_use_case', + TL_REQ_TYPE_INTERFACE => 'req_type_interface', + TL_REQ_TYPE_NON_FUNCTIONAL => 'req_type_non_functional', + TL_REQ_TYPE_CONSTRAIN => 'req_type_constrain', + TL_REQ_TYPE_SYSTEM_FUNCTION => 'req_type_system_function' +); + +/** + * All possible types of requirement relations (BUGID 1748). + * + * Important: + * When you add your own relation types here, you also have to add localization strings + * and configure those below. + * + * Add you types ONLY AFTER LAST RESERVED + * + * @since TestLink 1.9 + * + * IMPORTANT NOTICE this will be written on DB on an INT field + */ +define('TL_REQ_REL_TYPE_PARENT_CHILD', 1); +define('TL_REQ_REL_TYPE_BLOCKS_DEPENDS', 2); +define('TL_REQ_REL_TYPE_RELATED', 3); +define('TL_REQ_REL_TYPE_RESERVED_1', 4); +define('TL_REQ_REL_TYPE_RESERVED_2', 5); +define('TL_REQ_REL_TYPE_RESERVED_3', 6); +define('TL_REQ_REL_TYPE_RESERVED_4', 7); +define('TL_REQ_REL_TYPE_RESERVED_5', 8); +define('TL_REQ_REL_TYPE_RESERVED_6', 9); + +/** + * Localization identifiers for requirement relation types (BUGID 1748). + * Types, which are configured above, have to be configured + * here too with attributes "source" and "destination". + * + * Last value will be selected in GUI as default. + * + * Form has to be like this: + * + * $tlCfg->req_cfg->rel_type_labels = array( + * RELATIONNAME => array( + * 'source' => 'SOURCE_LOCALIZATION_KEY', + * 'destination' => 'DESTINATION_LOCALIZATION_KEY'), + * ... + * + * @since TestLink 1.9 + */ +$tlCfg->req_cfg->rel_type_labels = array( + TL_REQ_REL_TYPE_PARENT_CHILD => array( + 'source' => 'req_rel_is_parent_of', + 'destination' => 'req_rel_is_child_of' + ), + TL_REQ_REL_TYPE_BLOCKS_DEPENDS => array( + 'source' => 'req_rel_blocks', + 'destination' => 'req_rel_depends' + ), + TL_REQ_REL_TYPE_RELATED => array( // this is a flat relation, so strings are identical + 'source' => 'req_rel_is_related_to', + 'destination' => 'req_rel_is_related_to' + ) +); + +$tlCfg->req_cfg->rel_type_description = array( + TL_REQ_REL_TYPE_PARENT_CHILD => 'parent_child', + TL_REQ_REL_TYPE_BLOCKS_DEPENDS => 'blocks_depends', + TL_REQ_REL_TYPE_RELATED => 'related_to' +); + +/** + * + * @var array controls is expected_coverage must be requested at user interface. + * following conditions (OR LOGIC) must be verified to request value: + * + * a. key is NOT PRESENT (!isset()) + * b. key is present with value TRUE + * + * Working in this way configuration is simplified. + * + * @since TestLink 1.9 + */ +$tlCfg->req_cfg->type_expected_coverage = array( + TL_REQ_TYPE_INFO => false +); + +// IMPORTANT NOTICE: this value will be written on DB on field of type CHAR(1) +define('TL_REQ_SPEC_TYPE_SECTION', '1'); +define('TL_REQ_SPEC_TYPE_USER_REQ_SPEC', '2'); +define('TL_REQ_SPEC_TYPE_SYSTEM_REQ_SPEC', '3'); + +// define('TL_REQ_SPEC_TYPE_FUNCTIONAL_AND_DATA', 1); +// define('TL_REQ_SPEC_TYPE_LOOK_AND_FEEL',2); +// define('TL_REQ_SPEC_TYPE_USABILITY_AND_HUMANITY',3); +// define('TL_REQ_SPEC_TYPE_PERFORMANCE',4); +// define('TL_REQ_SPEC_TYPE_OPERATIONAL_AND_ENVIRONMENTAL',5); +// define('TL_REQ_SPEC_TYPE_MAINTAINABILITY_AND_SUPPORT',6); +// define('TL_REQ_SPEC_TYPE_SECURITY',7); +// define('TL_REQ_SPEC_TYPE_CULTURAL_AND_POLITICAL',8); +// define('TL_REQ_SPEC_TYPE_LEGAL',9); + +$tlCfg->req_spec_cfg = new stdClass(); +$tlCfg->req_spec_cfg->type_labels = array( + TL_REQ_SPEC_TYPE_SECTION => 'req_spec_type_section', + TL_REQ_SPEC_TYPE_USER_REQ_SPEC => 'req_spec_type_user_req_spec', + TL_REQ_SPEC_TYPE_SYSTEM_REQ_SPEC => 'req_spec_type_system_req_spec' +); + +/** + * + * @deprecated 1.9 + * @todo havlatm: replace by $tlCfg->req_cfg->type_labels + */ +define('TL_REQ_TYPE_1', 'V'); +define('TL_REQ_TYPE_2', 'N'); +define('NON_TESTABLE_REQ', 'n'); +define('VALID_REQ', 'v'); + +// -------------------------------------------------------------------------------------- +/* [CUSTOM FIELDS] */ + +// /** +// * Custom field constrains for HTML inputs use values to created to get/show custom field contents +// *
    +// *
  • for string,numeric,float,email: size & maxlenght of the input type text.
  • +// *
  • for list,email size of the select input.
  • +// *
+// */ +// $tlCfg->gui->custom_fields->sizes = array( +// 'string' => 50, +// 'numeric'=> 10, +// 'float' => 10, +// 'email' => 50, +// 'list' => 1, +// 'multiselection list' => 5, +// 'text area' => array('cols' => 40, 'rows' => 6) +// ); + +// 20080815 - franciscom +// Use this variable (on custom_config.inc.php) to define new Custom Field types. +// IMPORTANT: +// check $custom_field_types property on cfield_mgr.class.php +// to avoid overwrite of standard types. +// +// $tlCfg->gui->custom_fields->types = null; + +// Use this variable (on custom_config.inc.php) +// to define possible values behaviour for new Custom Field types. +// +// IMPORTANT: +// check $possible_values_cfg property on cfield_mgr.class.php +// to avoid overwrite of standard values. +// +// $tlCfg->gui->custom_fields->possible_values_cfg = null; + +// Format string follows date() spec - see PHP Manual +// We can not use $g_timestamp_format, because format strings for date() and strftime() +// uses same LETTER with different meanings (Bad Luck!) +$tlCfg->gui = new stdClass(); +$tlCfg->gui->custom_fields = new stdClass(); +$tlCfg->gui->custom_fields->time_format = 'H:i:s'; + +// -------------------------------------------------------------------------------------- +/* [MISC] */ + +/** + * Review types - user can define type for his review comment (disabled by default) + * + * @since TestLink version 2.0 + */ +$tlCfg->review_types = array( + 1 => 'undefined', + 2 => 'typo', + 3 => 'recommendation', + 4 => 'question', + 5 => 'unclear', + 6 => 'major problem' +); + +/** + * Top Menu definition + * + * structure + * - label: label to display, will be localized + * - url: resource to access when users click on menu item + * - right: user right need to display menu item. (can be an array) + * null => no right check needed + * - condition: specific condition = ['','TestPlanAvailable','ReqMgmtEnabled'] + * - shortcut: keyboard HTML shortcut + * - target: window/frame name (mainframe in the most of cases) + * - imgKey + * + * @since TestLink version 1.9 + */ +$tlCfg->guiTopMenu[1] = array( + 'label' => 'home', + 'url' => 'index.php', + 'right' => null, + 'imgKey' => 'home', + 'condition' => '', + 'shortcut' => 'h', + 'target' => '_parent' +); + +$tlCfg->guiTopMenu[2] = array( + 'label' => 'title_requirements', + 'imgKey' => 'requirements', + 'url' => 'lib/general/frmWorkArea.php?feature=reqSpecMgmt', + 'right' => array( + 'mgt_view_req', + 'mgt_modify_req' + ), + 'condition' => 'ReqMgmtEnabled', + 'shortcut' => 'r', + 'target' => 'mainframe' +); + +$tlCfg->guiTopMenu[3] = array( + 'label' => 'title_specification', + 'imgKey' => 'test_specification', + 'url' => 'lib/general/frmWorkArea.php?feature=editTc', + 'right' => array( + 'mgt_view_tc', + 'mgt_modify_tc' + ), + 'condition' => '', + 'shortcut' => 't', + 'target' => 'mainframe' +); + +$tlCfg->guiTopMenu[4] = array( + 'label' => 'title_execute', + 'imgKey' => 'execution', + 'url' => 'lib/general/frmWorkArea.php?feature=executeTest', + 'right' => array( + 'testplan_execute', + 'exec_ro_access' + ), + 'condition' => 'TestPlanAvailable', + 'shortcut' => 'e', + 'target' => 'mainframe' +); + +$tlCfg->guiTopMenu[5] = array( + 'label' => 'title_results', + 'imgKey' => 'report', + 'url' => 'lib/general/frmWorkArea.php?feature=showMetrics', + 'right' => 'testplan_metrics', + 'condition' => 'TestPlanAvailable', + 'shortcut' => 'r', + 'target' => 'mainframe' +); + +$tlCfg->guiTopMenu[6] = array( + 'label' => 'title_admin', + 'imgKey' => 'user', + 'url' => 'lib/usermanagement/usersView.php', + 'right' => 'mgt_users', + 'condition' => '', + 'shortcut' => 'u', + 'target' => 'mainframe' +); + +$tlCfg->guiTopMenu[7] = array( + 'label' => 'title_events', + 'imgKey' => 'events', + 'url' => 'lib/events/eventviewer.php', + 'right' => array( + 'events_mgt', + 'mgt_view_events' + ), + 'condition' => '', + 'shortcut' => 'v', + 'target' => 'mainframe' +); +$tlCfg->guiTopMenu[8] = array( + 'label' => 'title_plugins', + 'imgKey' => 'plugins', + 'url' => 'lib/plugins/pluginView.php', + 'right' => array( + 'mgt_plugins' + ), + 'condition' => '', + 'shortcut' => 'p', + 'target' => 'mainframe' +); + +define('PARTIAL_URL_TL_FILE_FORMATS_DOCUMENT', 'docs/tl-file-formats.pdf'); + +// Configure Charts dimension +$tlCfg->results['charts']['dimensions'] = array( + 'topLevelSuitesBarChart' => array( + 'chartTitle' => 'results_top_level_suites', + 'XSize' => 900, + 'YSize' => 400, + 'beginX' => 40, + 'beginY' => 100, + 'legendXAngle' => 35 + ), + 'keywordBarChart' => array( + 'chartTitle' => 'results_by_keyword', + 'XSize' => 900, + 'YSize' => 400, + 'beginX' => 40, + 'beginY' => 100, + 'legendXAngle' => 25 + ), + 'ownerBarChart' => array( + 'chartTitle' => 'results_by_tester', + 'XSize' => 900, + 'YSize' => 400, + 'beginX' => 40, + 'beginY' => 100, + 'legendXAngle' => 35 + ), + 'overallPieChart' => array( + 'chartTitle' => 'results_by_tester', + 'XSize' => 400, + 'YSize' => 400, + 'radius' => 150, + 'legendX' => 10, + 'legendY' => 15 + ), + 'platformPieChart' => array( + 'chartTitle' => 'results_by_tester', + 'XSize' => 400, + 'YSize' => 400, + 'radius' => 150, + 'legendX' => 10, + 'legendY' => 15 + ) +); + +// if you need to define new one, start on 20 please. +// see strings.txt for labels +// $TLS_testCaseStatus_KEY => $TLS_testCaseStatus_draft +// +$tlCfg->testCaseStatus = array( + 'draft' => 1, + 'readyForReview' => 2, + 'reviewInProgress' => 3, + 'rework' => 4, + 'obsolete' => 5, + 'future' => 6, + 'final' => 7 +); + +// see strings.txt for labels +// $TLS_testCaseStatus_hint_KEY => $TLS_testCaseStatus_hint_draft +// +$tlCfg->testCaseStatusDisplayHintOnTestDesign = [ + 'draft' => '' /* 'testCaseStatus_hint_draft' */, + 'readyForReview' => '', + 'reviewInProgress' => '', + 'rework' => '', + 'obsolete' => 'testCaseStatus_hint_obsolete', + 'future' => '', + 'final' => '' +]; +// ----------------------------------------------------------------------------------------- + +/** + * + * @uses testcase.class.php + */ +// if you need to define new one, start on 20 please. +// see strings.txt for labels +// $TLS_execution_type_KEY => $TLS_execution_type_manual +$tlCfg->execution_type = array( + 'manual' => 1, + 'auto' => 2 +); + +// To be removed +define('TESTCASE_EXECUTION_TYPE_MANUAL', $tlCfg->execution_type['manual']); +define('TESTCASE_EXECUTION_TYPE_AUTO', $tlCfg->execution_type['auto']); + +// for link_status field on req_coverage table +define('LINK_TC_REQ_OPEN', 1); +define('LINK_TC_REQ_CLOSED_BY_EXEC', 2); +define('LINK_TC_REQ_CLOSED_BY_NEW_TCVERSION', 3); +define('LINK_TC_REQ_CLOSED_BY_NEW_REQVERSION', 4); + +// for link_status field on testcase_relations table +define('LINK_TC_RELATION_OPEN', 1); +define('LINK_TC_RELATION_CLOSED_BY_EXEC', 2); +define('LINK_TC_RELATION_CLOSED_BY_NEW_TCVERSION', 3); + +define('USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS', 1); +define('USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS', 2); +define('USE_LATEST_EXEC_ON_TESTPLAN_PLAT_FOR_COUNTERS', 3); + + +// END diff --git a/cfg/oauth_samples/oauth.azuread.inc.php b/cfg/oauth_samples/oauth.azuread.inc.php new file mode 100644 index 0000000000..c7815ff9fa --- /dev/null +++ b/cfg/oauth_samples/oauth.azuread.inc.php @@ -0,0 +1,57 @@ +OAuthServers[] +// can be anything you want that make this configuration +// does not overwrite other or will be overwritten +// +// HOW TO use this file ? +// 1. copy this file to +// [TESTLINK_INSTALL]/cfg/ +// +// 2. configure according your application +// +// 3. add the following line to your custom_config.inc.php +// require('aouth.azuread.inc.php'); +// +// ------------------------------------------------------------- +$tlCfg->OAuthServers['azuread'] = array(); + +$tlCfg->OAuthServers['azuread']['redirect_uri'] = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . + $_SERVER['HTTP_HOST'] . '/login.php'; + +$tlCfg->OAuthServers['azuread']['oauth_client_id'] = 'CHANGE_WITH_CLIENT_ID'; +$tlCfg->OAuthServers['azuread']['oauth_client_secret'] = 'CHANGE_WITH_CLIENT_SECRET'; + +// https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0/.well-known/openid-configuration +$azureADBaseURL = 'https://login.microsoftonline.com/CHANGE_WITH_TENANT_ID'; +$msGraphURL = 'https://graph.microsoft.com'; +$tlCfg->OAuthServers['azuread']['oauth_url'] = $azureADBaseURL . + '/oauth2/v2.0/authorize'; + +$tlCfg->OAuthServers['azuread']['token_url'] = $azureADBaseURL . + '/oauth2/v2.0/token'; + +$tlCfg->OAuthServers['azuread']['oauth_profile'] = $msGraphURL . '/oidc/userinfo'; + +$tlCfg->OAuthServers['azuread']['oauth_enabled'] = true; +$tlCfg->OAuthServers['azuread']['oauth_name'] = 'azuread'; // do not change this +$tlCfg->OAuthServers['azuread']['oauth_force_single'] = true; +$tlCfg->OAuthServers['azuread']['oauth_grant_type'] = 'authorization_code'; + +// the domain you want to whitelist (email domains) +$tlCfg->OAuthServers['azuread']['oauth_domain'] = 'autsoft.hu'; + +$tlCfg->OAuthServers['azuread']['oauth_scope'] = 'https://graph.microsoft.com/mail.read https://graph.microsoft.com/user.read openid profile email'; diff --git a/cfg/oauth_samples/oauth.github.inc.php b/cfg/oauth_samples/oauth.github.inc.php new file mode 100644 index 0000000000..05bede3a7e --- /dev/null +++ b/cfg/oauth_samples/oauth.github.inc.php @@ -0,0 +1,44 @@ +OAuthServers[] +# can be anything you want that make this configuration +# does not overwrite other or will be overwritten +# +# HOW TO use this file ? +# 1. copy this file to +# [TESTLINK_INSTALL]/cfg/ +# +# 2. configure according your application +# +# 3. add the following line to your custom_config.inc.php +# require('aouth.github.inc.php'); +# +# ########################################################### +# This is a working example for test site +# http://fman.hopto.org/ +# +# You need to create the configuration for your site +# This is only a working example that is useful +# for the TestLink Development Team +# +$tlCfg->OAuthServers['github'] = array(); +$tlCfg->OAuthServers['github']['redirect_uri'] = 'http://fman.hopto.org/login.php?oauth=github'; + +$tlCfg->OAuthServers['github']['oauth_client_id'] = 'aa5f70a8de342fb95043'; +$tlCfg->OAuthServers['github']['oauth_client_secret'] = 'c8d61d5ec4ed4eb2ac81064c27043ddef351107e'; + +$tlCfg->OAuthServers['github']['oauth_enabled'] = true; +$tlCfg->OAuthServers['github']['oauth_name'] = 'github'; + diff --git a/cfg/oauth_samples/oauth.gitlab.inc.php b/cfg/oauth_samples/oauth.gitlab.inc.php new file mode 100644 index 0000000000..5b0559c67a --- /dev/null +++ b/cfg/oauth_samples/oauth.gitlab.inc.php @@ -0,0 +1,44 @@ +OAuthServers[] +# can be anything you want that make this configuration +# does not overwrite other or will be overwritten +# +# HOW TO use this file ? +# 1. copy this file to +# [TESTLINK_INSTALL]/cfg/ +# +# 2. configure according your application +# +# 3. add the following line to your custom_config.inc.php +# require('aouth.gitlab.inc.php'); +# +# ############################################################## +# +# This is a working example for test site +# http://fman.hopto.org/ +# +# You need to create the configuration for your site +# This is only a working example that is useful +# for the TestLink Development Team +# +$tlCfg->OAuthServers['gitlab'] = array(); + +$tlCfg->OAuthServers['gitlab']['redirect_uri'] = 'http://fman.hopto.org/login.php?oauth=gitlab'; + +$tlCfg->OAuthServers['gitlab']['oauth_enabled'] = true; +$tlCfg->OAuthServers['gitlab']['oauth_name'] = 'gitlab'; + +$tlCfg->OAuthServers['gitlab']['oauth_client_id'] = '27a03c93d60b5ddb4e0cef92149678fbe37c099733605e046a5428a9da4177ba'; + +$tlCfg->OAuthServers['gitlab']['oauth_client_secret'] = 'c157df291b81dbfd8084d38b155029baded3cf76c7449670bd2da889fe8b99eb'; diff --git a/cfg/oauth_samples/oauth.google.inc.php b/cfg/oauth_samples/oauth.google.inc.php new file mode 100644 index 0000000000..1e403a8739 --- /dev/null +++ b/cfg/oauth_samples/oauth.google.inc.php @@ -0,0 +1,52 @@ +OAuthServers[] +# can be anything you want that make this configuration +# does not overwrite other or will be overwritten +# +# HOW TO use this file ? +# 1. copy this file to +# [TESTLINK_INSTALL]/cfg/ +# +# 2. configure according your application +# +# 3. add the following line to your custom_config.inc.php +# require('aouth.google.inc.php'); +# +# ############################################################# +# Client implemented using +# https://github.com/thephpleague/oauth2-google +# # +# This is a working example for test site +# http://fman.hopto.org/ +# +# You need to create the configuration for your site +# This is only a working example that is useful +# for the TestLink Development Team +# +$tlCfg->OAuthServers['google'] = array(); +$tlCfg->OAuthServers['google']['redirect_uri'] = 'http://fman.hopto.org/login.php?oauth=google'; + +$tlCfg->OAuthServers['google']['oauth_enabled'] = true; +$tlCfg->OAuthServers['google']['oauth_name'] = 'google'; + +// Get from /gui/themes/default/images +$tlCfg->OAuthServers['google']['oauth_client_id'] = '860603525614-fscj9cgr2dvks51uh6odl67skec536fd.apps.googleusercontent.com'; + +$tlCfg->OAuthServers['google']['oauth_client_secret'] = '_YOKquNTa4Fux-OMJoxDBuov'; + +// Needed when you use the cURL implementation +// Can be authorization_code (by default), client_credentials or password +// $tlCfg->OAuthServers['google']['oauth_grant_type'] = 'authorization_code'; +//$tlCfg->OAuthServers['google']['oauth_url'] = 'https://accounts.google.com/o/oauth2/auth'; +//$tlCfg->OAuthServers['google']['token_url'] = 'https://accounts.google.com/o/oauth2/token'; + +// false => then the only user will be selected automatically (applied for google) +//$tlCfg->OAuthServers['google']['oauth_force_single'] = false; + +// the domain you want to whitelist +//$tlCfg->OAuthServers['google']['oauth_domain'] = 'google.com'; +//$tlCfg->OAuthServers['google']['oauth_profile'] = 'https://www.googleapis.com/oauth2/v1/userinfo'; +//$tlCfg->OAuthServers['google']['oauth_scope'] = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'; diff --git a/cfg/oauth_samples/oauth.microsoft.inc.php b/cfg/oauth_samples/oauth.microsoft.inc.php new file mode 100644 index 0000000000..b8ec24badd --- /dev/null +++ b/cfg/oauth_samples/oauth.microsoft.inc.php @@ -0,0 +1,37 @@ +OAuthServers[] +// can be anything you want that make this configuration +// does not overwrite other or will be overwritten +// +// HOW TO use this file ? +// 1. copy this file to +// [TESTLINK_INSTALL]/cfg/ +// +// 2. configure according your application +// +// 3. add the following line to your custom_config.inc.php +// require('aouth.microsoft.inc.php'); +// +// ------------------------------------------------------------- +$tlCfg->OAuthServers['microsoft'] = array(); +$tlCfg->OAuthServers['microsoft']['redirect_uri'] = ''; + +$tlCfg->OAuthServers['microsoft']['oauth_enabled'] = true; +$tlCfg->OAuthServers['microsoft']['oauth_name'] = 'microsoft'; +$tlCfg->OAuthServers['microsoft']['oauth_client_id'] = 'CLIENT_ID'; +$tlCfg->OAuthServers['microsoft']['oauth_client_secret'] = 'CLIENT_SECRET'; + +// Can be authorization_code (by default), client_credentials or password +$tlCfg->OAuthServers['microsoft']['oauth_grant_type'] = 'authorization_code'; +$tlCfg->OAuthServers['microsoft']['oauth_url'] = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'; + +$tlCfg->OAuthServers['microsoft']['token_url'] = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; + +$tlCfg->OAuthServers['microsoft']['oauth_force_single'] = true; +$tlCfg->OAuthServers['microsoft']['oauth_profile'] = 'https://graph.microsoft.com/v1.0/me'; +$tlCfg->OAuthServers['microsoft']['oauth_scope'] = 'User.Read'; + diff --git a/cfg/reports.cfg.php b/cfg/reports.cfg.php index ac915e9d5c..3611900b19 100644 --- a/cfg/reports.cfg.php +++ b/cfg/reports.cfg.php @@ -1,15 +1,17 @@ reports_formats = array( FORMAT_HTML => 'format_html', - FORMAT_MSWORD => 'format_pseudo_msword', - FORMAT_MAIL_HTML => 'format_mail_html'); - -/** Mime Content Type */ -$tlCfg->reports_applications = - array(FORMAT_HTML => 'text/html', - FORMAT_XLS => 'application/vnd.ms-excel', - FORMAT_MSWORD => 'application/vnd.ms-word'); - +/** + * supported document formats (value = localization ID) + */ +$tlCfg->reports_formats = array( + FORMAT_HTML => 'format_html', + FORMAT_MSWORD => 'format_pseudo_msword', + FORMAT_MAIL_HTML => 'format_mail_html' +); -/** Report file extenssion */ -$tlCfg->reports_file_extension = - array(FORMAT_HTML => 'html',FORMAT_XLS => 'xls',FORMAT_MSWORD => 'doc'); +/** + * Mime Content Type + */ +$tlCfg->reports_applications = array( + FORMAT_HTML => 'text/html', + FORMAT_XLS => 'application/vnd.ms-excel', + FORMAT_MSWORD => 'application/vnd.ms-word' +); +/** + * Report file extenssion + */ +$tlCfg->reports_file_extension = array( + FORMAT_HTML => 'html', + FORMAT_XLS => 'xls', + FORMAT_MSWORD => 'doc' +); -/** - * @VAR $tlCfg->reports_list['report_identifier'] - * definition of default set of reports - * title - title string identifier - * url - http path (without testPlanId and format) - * enabled - availability - * 1. all (everytime), - * 2. bts (if bug tracker is connected only), - * 3. req (if project has available requirements only) +/** + * + * @var $tlCfg->reports_list['report_identifier'] definition of default set of reports + * title - title string identifier + * url - http path (without testPlanId and format) + * enabled - availability + * 1. all (everytime), + * 2. bts (if bug tracker is connected only), + * 3. req (if project has available requirements only) */ -$tlCfg->reports_list['test_plan'] = array( - 'title' => 'link_report_test_plan', - 'url' => 'lib/results/printDocOptions.php?type=' . DOC_TEST_PLAN_DESIGN, - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=test_plan', - 'format' => 'format_html,format_pseudo_msword' +$tlCfg->reports_list['test_plan'] = array( + 'title' => 'link_report_test_plan', + 'url' => 'lib/results/printDocOptions.php?type=' . DOC_TEST_PLAN_DESIGN, + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=test_plan', + 'format' => 'format_html,format_pseudo_msword' ); -$tlCfg->reports_list['test_report'] = array( - 'title' => 'link_report_test_report', - 'url' => 'lib/results/printDocOptions.php?type=' . DOC_TEST_PLAN_EXECUTION, - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=test_report', - 'format' => 'format_html,format_pseudo_msword' +$tlCfg->reports_list['test_report'] = array( + 'title' => 'link_report_test_report', + 'url' => 'lib/results/printDocOptions.php?type=' . DOC_TEST_PLAN_EXECUTION, + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=test_report', + 'format' => 'format_html,format_pseudo_msword' ); -$tlCfg->reports_list['test_report_on_build'] = array( - 'title' => 'link_report_test_report_on_build', - 'url' => 'lib/results/printDocOptions.php?type=' . DOC_TEST_PLAN_EXECUTION_ON_BUILD, - 'enabled' => 'all', - 'format' => 'format_html,format_pseudo_msword' +$tlCfg->reports_list['test_report_on_build'] = array( + 'title' => 'link_report_test_report_on_build', + 'url' => 'lib/results/printDocOptions.php?type=' . + DOC_TEST_PLAN_EXECUTION_ON_BUILD, + 'enabled' => 'all', + 'format' => 'format_html,format_pseudo_msword' ); -$tlCfg->reports_list['metrics_tp_general'] = array( - 'title' => 'link_report_general_tp_metrics', - 'url' => 'lib/results/resultsGeneral.php', - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0&type=metrics_tp_general', - 'format' => 'format_html,format_pseudo_ods' +$tlCfg->reports_list['metrics_tp_general'] = array( + 'title' => 'link_report_general_tp_metrics', + 'url' => 'lib/results/resultsGeneral.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0&type=metrics_tp_general', + 'format' => 'format_html,format_pseudo_ods' ); -$tlCfg->reports_list['report_by_tsuite'] = - array('title' => 'link_report_by_tsuite', - 'url' => 'lib/results/resultsByTSuite.php', - 'enabled' => 'all', - 'directLink' => - '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0&type=report_by_tsuite', - 'format' => 'format_html' +$tlCfg->reports_list['report_by_tsuite'] = array( + 'title' => 'link_report_by_tsuite', + 'url' => 'lib/results/resultsByTSuite.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0&type=report_by_tsuite', + 'format' => 'format_html' ); -$tlCfg->reports_list['baseline_l1l2'] = - array('title' => 'baseline_l1l2', - 'url' => 'lib/results/baselinel1l2.php', - 'enabled' => 'all', - 'directLink' => - '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0' . - '&type=baseline_l1l2', - 'format' => 'format_html' +$tlCfg->reports_list['baseline_l1l2'] = array( + 'title' => 'baseline_l1l2', + 'url' => 'lib/results/baselinel1l2.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0' . + '&type=baseline_l1l2', + 'format' => 'format_html' ); - -$tlCfg->reports_list['results_by_tester_per_build'] = array( - 'title' => 'link_report_by_tester_per_build', - 'url' => 'lib/results/resultsByTesterPerBuild.php', - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0&type=results_by_tester_per_build', - 'format' => 'format_html' +$tlCfg->reports_list['results_by_tester_per_build'] = array( + 'title' => 'link_report_by_tester_per_build', + 'url' => 'lib/results/resultsByTesterPerBuild.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0&type=results_by_tester_per_build', + 'format' => 'format_html' ); -$tlCfg->reports_list['assigned_tc_overview'] = array( - 'title' => 'link_assigned_tc_overview', - 'url' => 'lib/testcases/tcAssignedToUser.php?show_all_users=1&show_inactive_and_closed=1', - 'enabled' => 'all', 'directLink' => '', - 'format' => 'format_html' +$tlCfg->reports_list['assigned_tc_overview'] = array( + 'title' => 'link_assigned_tc_overview', + 'url' => 'lib/testcases/tcAssignedToUser.php?show_all_users=1&show_inactive_and_closed=1', + 'enabled' => 'all', + 'directLink' => '', + 'format' => 'format_html' ); // will be released in future because refactoring is not completed -//$tlCfg->reports_list['results_custom_query'] = array( -// 'title' => 'link_report_metrics_more_builds', -// 'url' => 'lib/results/resultsMoreBuildsGUI.php', -// 'enabled' => 'all', 'directLink' => '', -// 'format' => 'format_html,format_ods,format_xls,format_mail_html' -//); -$tlCfg->reports_list['results_matrix'] = array( - 'title' => 'link_report_test', - 'url' => 'lib/results/resultsTC.php', - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=results_matrix', - 'format' => 'format_html,format_pseudo_ods' +// $tlCfg->reports_list['results_custom_query'] = array( +// 'title' => 'link_report_metrics_more_builds', +// 'url' => 'lib/results/resultsMoreBuildsGUI.php', +// 'enabled' => 'all', 'directLink' => '', +// 'format' => 'format_html,format_ods,format_xls,format_mail_html' +// ); +$tlCfg->reports_list['results_matrix'] = array( + 'title' => 'link_report_test', + 'url' => 'lib/results/resultsTC.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=results_matrix', + 'format' => 'format_html,format_pseudo_ods' ); -$tlCfg->reports_list['results_flat'] = array( - 'title' => 'link_report_test_flat', - 'url' => 'lib/results/resultsTCFlat.php', - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=results_flat', - 'format' => 'format_html,format_mail_html' +$tlCfg->reports_list['results_flat'] = array( + 'title' => 'link_report_test_flat', + 'url' => 'lib/results/resultsTCFlat.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=results_flat', + 'format' => 'format_html,format_mail_html' ); /* 20191210 */ -$tlCfg->reports_list['abslatest_results_matrix'] = array( - 'title' => 'link_report_test_absolute_latest_exec', - 'url' => 'lib/results/resultsTCAbsoluteLatest.php', - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=results_matrix', - 'format' => 'format_html,format_pseudo_ods' +$tlCfg->reports_list['abslatest_results_matrix'] = array( + 'title' => 'link_report_test_absolute_latest_exec', + 'url' => 'lib/results/resultsTCAbsoluteLatest.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=results_matrix', + 'format' => 'format_html,format_pseudo_ods' ); - -$tlCfg->reports_list['list_tc_failed'] = array( - 'title' => 'link_report_failed', - 'url' => 'lib/results/resultsByStatus.php?type=' . $tlCfg->results['status_code']['failed'], - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=list_tc_failed', - 'format' => 'format_html,format_pseudo_ods' +$tlCfg->reports_list['list_tc_failed'] = array( + 'title' => 'link_report_failed', + 'url' => 'lib/results/resultsByStatus.php?type=' . + $tlCfg->results['status_code']['failed'], + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=list_tc_failed', + 'format' => 'format_html,format_pseudo_ods' ); -$tlCfg->reports_list['list_tc_blocked'] = array( - 'title' => 'link_report_blocked_tcs', - 'url' => 'lib/results/resultsByStatus.php?type=' . $tlCfg->results['status_code']['blocked'], - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=list_tc_blocked', - 'format' => 'format_html,format_pseudo_ods' +$tlCfg->reports_list['list_tc_blocked'] = array( + 'title' => 'link_report_blocked_tcs', + 'url' => 'lib/results/resultsByStatus.php?type=' . + $tlCfg->results['status_code']['blocked'], + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=list_tc_blocked', + 'format' => 'format_html,format_pseudo_ods' ); -$tlCfg->reports_list['list_tc_not_run'] = array( - 'title' => 'link_report_not_run', - 'url' => 'lib/results/resultsByStatus.php?type=' . $tlCfg->results['status_code']['not_run'], - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=list_tc_not_run', - 'format' => 'format_html,format_pseudo_ods', - 'misc' => array('bugs_not_linked' => false) +$tlCfg->reports_list['list_tc_not_run'] = array( + 'title' => 'link_report_not_run', + 'url' => 'lib/results/resultsByStatus.php?type=' . + $tlCfg->results['status_code']['not_run'], + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=list_tc_not_run', + 'format' => 'format_html,format_pseudo_ods', + 'misc' => array( + 'bugs_not_linked' => false + ) ); -$tlCfg->reports_list['never_run'] = array( - 'title' => 'link_report_never_run', - 'url' => 'lib/results/neverRunByPP.php', - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=never_run', - 'format' => 'format_html,format_pseudo_ods', - 'misc' => array('bugs_not_linked' => false) +$tlCfg->reports_list['never_run'] = array( + 'title' => 'link_report_never_run', + 'url' => 'lib/results/neverRunByPP.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=never_run', + 'format' => 'format_html,format_pseudo_ods', + 'misc' => array( + 'bugs_not_linked' => false + ) ); - $tlCfg->reports_list['tcases_without_tester'] = array( - 'title' => 'link_report_tcases_without_tester', - 'url' => 'lib/results/testCasesWithoutTester.php', - 'enabled' => 'all', 'directLink' => '', - 'format' => 'format_html' + 'title' => 'link_report_tcases_without_tester', + 'url' => 'lib/results/testCasesWithoutTester.php', + 'enabled' => 'all', + 'directLink' => '', + 'format' => 'format_html' ); -$tlCfg->reports_list['charts_basic'] = array( - 'title' => 'link_charts', - 'url' => 'lib/results/charts.php', - 'enabled' => 'all', - 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=charts_basic', - 'format' => 'format_html' +$tlCfg->reports_list['charts_basic'] = array( + 'title' => 'link_charts', + 'url' => 'lib/results/charts.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&type=charts_basic', + 'format' => 'format_html' ); -$tlCfg->reports_list['results_requirements'] = array( - 'title' => 'link_report_reqs_coverage', - 'url' => 'lib/results/resultsReqs.php', - 'enabled' => 'req', - 'directLink' => '', - 'format' => 'format_html' +$tlCfg->reports_list['results_requirements'] = array( + 'title' => 'link_report_reqs_coverage', + 'url' => 'lib/results/resultsReqs.php', + 'enabled' => 'req', + 'directLink' => '', + 'format' => 'format_html' ); - -// disabled TICKET 37006 - disabled uncovered_testcases report -//$tlCfg->reports_list['uncovered_testcases'] = array( -// 'title' => 'link_report_uncovered_testcases', -// 'url' => 'lib/results/uncoveredTestCases.php', -// 'enabled' => 'req', -// 'format' => 'format_html' -//); - -$tlCfg->reports_list['list_problems'] = array( - 'title' => 'link_report_total_bugs', - 'url' => 'lib/results/resultsBugs.php?type=0', - 'enabled' => 'bts', - 'directLink' => '', - 'format' => 'format_html' +// disabled TICKET 37006 - disabled uncovered_testcases report +// $tlCfg->reports_list['uncovered_testcases'] = array( +// 'title' => 'link_report_uncovered_testcases', +// 'url' => 'lib/results/uncoveredTestCases.php', +// 'enabled' => 'req', +// 'format' => 'format_html' +// ); + +$tlCfg->reports_list['list_problems'] = array( + 'title' => 'link_report_total_bugs', + 'url' => 'lib/results/resultsBugs.php?type=0', + 'enabled' => 'bts', + 'directLink' => '', + 'format' => 'format_html' ); -$tlCfg->reports_list['issues_all_exec'] = array( - 'title' => 'link_report_total_bugs_all_exec', - 'url' => 'lib/results/resultsBugs.php?type=1', - 'enabled' => 'bts', - 'directLink' => '', - 'format' => 'format_html' +$tlCfg->reports_list['issues_all_exec'] = array( + 'title' => 'link_report_total_bugs_all_exec', + 'url' => 'lib/results/resultsBugs.php?type=1', + 'enabled' => 'bts', + 'directLink' => '', + 'format' => 'format_html' ); -$tlCfg->reports_list['tcases_with_cf'] = array( - 'title' => 'link_report_tcases_with_cf', - 'url' => 'lib/results/testCasesWithCF.php', - 'enabled' => 'all', 'directLink' => '', - 'format' => 'format_html' +$tlCfg->reports_list['tcases_with_cf'] = array( + 'title' => 'link_report_tcases_with_cf', + 'url' => 'lib/results/testCasesWithCF.php', + 'enabled' => 'all', + 'directLink' => '', + 'format' => 'format_html' ); -$tlCfg->reports_list['tplan_with_cf'] = array( - 'title' => 'link_report_tplans_with_cf', - 'url' => 'lib/results/testPlanWithCF.php', - 'enabled' => 'all', 'directLink' => '', - 'format' => 'format_html' +$tlCfg->reports_list['tplan_with_cf'] = array( + 'title' => 'link_report_tplans_with_cf', + 'url' => 'lib/results/testPlanWithCF.php', + 'enabled' => 'all', + 'directLink' => '', + 'format' => 'format_html' ); -$tlCfg->reports_list['free_tcases'] = array( -'title' => 'link_report_free_testcases_on_testproject', -'url' => 'lib/results/freeTestCases.php', -'enabled' => 'all', 'directLink' => '', -'format' => 'format_html' +$tlCfg->reports_list['free_tcases'] = array( + 'title' => 'link_report_free_testcases_on_testproject', + 'url' => 'lib/results/freeTestCases.php', + 'enabled' => 'all', + 'directLink' => '', + 'format' => 'format_html' ); $report = 'report_exec_timeline'; -$tlCfg->reports_list[$report] = array( - 'title' => 'link_report_exec_timeline', - 'url' => 'lib/results/execTimelineStats.php', - 'enabled' => 'all', - 'directLink' => - '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0' . - '&type=' . $report, - 'format' => 'format_html' +$tlCfg->reports_list[$report] = array( + 'title' => 'link_report_exec_timeline', + 'url' => 'lib/results/execTimelineStats.php', + 'enabled' => 'all', + 'directLink' => '%slnl.php?apikey=%s&tproject_id=%s&tplan_id=%s&format=0' . + '&type=' . $report, + 'format' => 'format_html' ); - - - // Add custom configuration clearstatcache(); $f2inc = TL_ABS_PATH . 'cfg/custom_reports.cfg.php'; -if ( file_exists($f2inc) ) { - require_once($f2inc); +if (file_exists($f2inc)) { + require_once $f2inc; } diff --git a/cfg/userRightMatrix.php b/cfg/userRightMatrix.php index 278f9f0230..ece058a5d9 100644 --- a/cfg/userRightMatrix.php +++ b/cfg/userRightMatrix.php @@ -1,84 +1,148 @@ - array ("user_role_assignment",)); -$proj_admin=array("$proj_admin_url/projectEdit.php" => array("mgt_modify_product",)); -$test_exec=array("$test_exec_url/execnavigator.php" => array("testplan_execute",)); - - -$tplan_admin= array("$tplan_admin_url/planupdatetc.php" => array("testplan_planning",), - "$tplan_admin_url/planaddtc.php" => array("testplan_planning",), - "$tplan_admin_url/planaddtcnavigator.php" => array("testplan_planning",), - "$tplan_admin_url/planedit.php" => array("testplan_planning",), - "$tplan_admin_url/plannew.php" => array("testplan_planning",), - "$tplan_admin_url/planpriority.php" => array("testplan_planning",), - "$tplan_admin_url/planupdatetc.php" => array("testplan_planning",), - "$tplan_admin_url/planmilestoneedit.php" => array("testplan_planning",), - "$tplan_admin_url/plantcnavigator.php" => array("testplan_planning",), - "$tplan_admin_url/plantcremove.php" => array("testplan_planning",)); - -$reports=array( "$reports_url/resultsallbuilds.php" => array("testplan_metrics",), - "$reports_url/resultsbugs.php" => array("testplan_metrics",), - "$reports_url/resultsbuild.php" => array("testplan_metrics",), - "$reports_url/resultsbystatus.php" => array("testplan_metrics",), - "$reports_url/resultsgeneral.php" => array("testplan_metrics",), - "$reports_url/resultsnavigator.php" => array("testplan_metrics",), - "$reports_url/resultssend.php" => array("testplan_metrics",), - "$reports_url/resultstc.php" => array("testplan_metrics",)); - - -$tc_admin=array("$tc_admin_url/containeredit.php" => array("mgt_modify_tc","mgt_view_tc",), - "$tc_admin_url/tcedit.php" => array("mgt_modify_tc","mgt_view_tc",), - "$tc_admin_url/tcimport.php" => array("mgt_modify_tc","mgt_view_tc",), - "$tc_admin_url/searchform.php" => null, - "$tc_admin_url/searchdata.php" => null, - "$tc_admin_url/listtestcases.php" => null); - - -$print_data=array("$print_url/printdata.php" => null, - "$print_url/selectdata.php" => null); - - - -$cf_admin=array("$cf_admin_url/cfieldsEdit.php" => array("cfield_management",), - "$cf_admin_url/cfieldsView.php" => array("cfield_view",), - "$cf_admin_url/cfieldsTProjectAssign.php" => array("cfield_management",)); - - -// build rigth matrix -$g_userRights=$user_admin+$proj_admin+$test_exec+$print_data+ - $tplan_admin+$reports+$tc_admin+$cf_admin; - -?> \ No newline at end of file + array( + "user_role_assignment" + ) +); +$proj_admin = array( + "$proj_admin_url/projectEdit.php" => array( + "mgt_modify_product" + ) +); +$test_exec = array( + "$test_exec_url/execnavigator.php" => array( + "testplan_execute" + ) +); + +$tplan_admin = array( + "$tplan_admin_url/planupdatetc.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/planaddtc.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/planaddtcnavigator.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/planedit.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/plannew.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/planpriority.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/planupdatetc.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/planmilestoneedit.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/plantcnavigator.php" => array( + "testplan_planning" + ), + "$tplan_admin_url/plantcremove.php" => array( + "testplan_planning" + ) +); + +$reports = array( + "$reports_url/resultsallbuilds.php" => array( + "testplan_metrics" + ), + "$reports_url/resultsbugs.php" => array( + "testplan_metrics" + ), + "$reports_url/resultsbuild.php" => array( + "testplan_metrics" + ), + "$reports_url/resultsbystatus.php" => array( + "testplan_metrics" + ), + "$reports_url/resultsgeneral.php" => array( + "testplan_metrics" + ), + "$reports_url/resultsnavigator.php" => array( + "testplan_metrics" + ), + "$reports_url/resultssend.php" => array( + "testplan_metrics" + ), + "$reports_url/resultstc.php" => array( + "testplan_metrics" + ) +); + +$tc_admin = array( + "$tc_admin_url/containeredit.php" => array( + "mgt_modify_tc", + "mgt_view_tc" + ), + "$tc_admin_url/tcedit.php" => array( + "mgt_modify_tc", + "mgt_view_tc" + ), + "$tc_admin_url/tcimport.php" => array( + "mgt_modify_tc", + "mgt_view_tc" + ), + "$tc_admin_url/searchform.php" => null, + "$tc_admin_url/searchdata.php" => null, + "$tc_admin_url/listtestcases.php" => null +); + +$print_data = array( + "$print_url/printdata.php" => null, + "$print_url/selectdata.php" => null +); + +$cf_admin = array( + "$cf_admin_url/cfieldsEdit.php" => array( + "cfield_management" + ), + "$cf_admin_url/cfieldsView.php" => array( + "cfield_view" + ), + "$cf_admin_url/cfieldsTProjectAssign.php" => array( + "cfield_management" + ) +); + +// build rigth matrix +$g_userRights = $user_admin + $proj_admin + $test_exec + $print_data + + $tplan_admin + $reports + $tc_admin + $cf_admin; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000..e4a30fdf15 --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "config" : { + "platform" : { + "php" : "8.2.12" + } + }, + "require" : { + "php-http/guzzle6-adapter" : "~2.0", + "slim/slim" : "4.*", + "slim/psr7" : "~1.7", + "nyholm/psr7" : "~1.8", + "nyholm/psr7-server" : "~1.1", + "guzzlehttp/psr7" : "^1.6", + "http-interop/http-factory-guzzle" : "^1.0", + "laminas/laminas-diactoros" : "^2.2", + "adodb/adodb-php" : "^5.22", + "phpmailer/phpmailer" : "^6.1", + "smarty/smarty" : "^3.1", + "psr/log" : "^1.1", + "symfony/polyfill-mbstring" : "^1.15", + "league/oauth2-client" : "^2.4", + "omines/oauth2-gitlab" : "^3.2", + "league/oauth2-github" : "^2.0", + "league/oauth2-google" : "^3.0", + "wp-statistics/pchart" : "~1.2" + } +} \ No newline at end of file diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000..7dbf81a7dd --- /dev/null +++ b/composer.lock @@ -0,0 +1,2343 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "ce9d3d6d1e7230d3e77d8e1477ed420a", + "packages": [ + { + "name": "adodb/adodb-php", + "version": "v5.22.9", + "source": { + "type": "git", + "url": "https://github.com/ADOdb/ADOdb.git", + "reference": "a568bfeb72d6b5942df747adc36b95165a083e60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ADOdb/ADOdb/zipball/a568bfeb72d6b5942df747adc36b95165a083e60", + "reference": "a568bfeb72d6b5942df747adc36b95165a083e60", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "autoload": { + "files": [ + "adodb.inc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "John Lim", + "email": "jlim@natsoft.com", + "role": "Author" + }, + { + "name": "Damien Regad", + "role": "Current maintainer" + }, + { + "name": "Mark Newnham", + "role": "Developer" + } + ], + "description": "ADOdb is a PHP database abstraction layer library", + "homepage": "https://adodb.org/", + "keywords": [ + "abstraction", + "database", + "layer", + "library", + "php" + ], + "support": { + "issues": "https://github.com/ADOdb/ADOdb/issues", + "source": "https://github.com/ADOdb/ADOdb" + }, + "time": "2025-05-01T11:49:24+00:00" + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.5.8", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/a52f0440530b54fa079ce76e8c5d196a42cad981", + "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.9", + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.17" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/6.5.8" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-06-20T22:16:07+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-05-21T12:31:43+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/e4490cabc77465aaee90b20cfc9a770f8c04be6b", + "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.9.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-04-17T16:00:37+00:00" + }, + { + "name": "http-interop/http-factory-guzzle", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/http-interop/http-factory-guzzle.git", + "reference": "8f06e92b95405216b237521cc64c804dd44c4a81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/8f06e92b95405216b237521cc64c804dd44c4a81", + "reference": "8f06e92b95405216b237521cc64c804dd44c4a81", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.7||^2.0", + "php": ">=7.3", + "psr/http-factory": "^1.0" + }, + "provide": { + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "guzzlehttp/psr7": "Includes an HTTP factory starting in version 2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Factory\\Guzzle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "An HTTP Factory using Guzzle PSR7", + "keywords": [ + "factory", + "http", + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/http-interop/http-factory-guzzle/issues", + "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.2.0" + }, + "time": "2021-07-21T13:50:14+00:00" + }, + { + "name": "laminas/laminas-diactoros", + "version": "2.26.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-diactoros.git", + "reference": "6584d44eb8e477e89d453313b858daac6183cddc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/6584d44eb8e477e89d453313b858daac6183cddc", + "reference": "6584d44eb8e477e89d453313b858daac6183cddc", + "shasum": "" + }, + "require": { + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1" + }, + "conflict": { + "zendframework/zend-diactoros": "*" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "ext-gd": "*", + "ext-libxml": "*", + "http-interop/http-factory-tests": "^0.9.0", + "laminas/laminas-coding-standard": "^2.5", + "php-http/psr7-integration-tests": "^1.2", + "phpunit/phpunit": "^9.5.28", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.6" + }, + "type": "library", + "extra": { + "laminas": { + "module": "Laminas\\Diactoros", + "config-provider": "Laminas\\Diactoros\\ConfigProvider" + } + }, + "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php", + "src/functions/create_uploaded_file.legacy.php", + "src/functions/marshal_headers_from_sapi.legacy.php", + "src/functions/marshal_method_from_sapi.legacy.php", + "src/functions/marshal_protocol_version_from_sapi.legacy.php", + "src/functions/marshal_uri_from_sapi.legacy.php", + "src/functions/normalize_server.legacy.php", + "src/functions/normalize_uploaded_files.legacy.php", + "src/functions/parse_cookie_header.legacy.php" + ], + "psr-4": { + "Laminas\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "psr", + "psr-17", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-diactoros/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-diactoros/issues", + "rss": "https://github.com/laminas/laminas-diactoros/releases.atom", + "source": "https://github.com/laminas/laminas-diactoros" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2023-10-29T16:17:44+00:00" + }, + { + "name": "league/oauth2-client", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "9df2924ca644736c835fc60466a3a60390d334f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9", + "reference": "9df2924ca644736c835fc60466a3a60390d334f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "php": "^7.1 || >=8.0.0 <8.5.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.5", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-client/issues", + "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1" + }, + "time": "2025-02-26T04:37:30+00:00" + }, + { + "name": "league/oauth2-github", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-github.git", + "reference": "e63d64f3ec167c09232d189c6b0c397458a99357" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-github/zipball/e63d64f3ec167c09232d189c6b0c397458a99357", + "reference": "e63d64f3ec167c09232d189c6b0c397458a99357", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.0" + }, + "require-dev": { + "mockery/mockery": "~0.9", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steven Maguire", + "email": "stevenmaguire@gmail.com", + "homepage": "https://github.com/stevenmaguire" + } + ], + "description": "Github OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "authorisation", + "authorization", + "client", + "github", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-github/issues", + "source": "https://github.com/thephpleague/oauth2-github/tree/master" + }, + "time": "2017-01-26T01:14:51+00:00" + }, + { + "name": "league/oauth2-google", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-google.git", + "reference": "6b79441f244040760bed5fdcd092a2bda7cf34c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-google/zipball/6b79441f244040760bed5fdcd092a2bda7cf34c6", + "reference": "6b79441f244040760bed5fdcd092a2bda7cf34c6", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.0" + }, + "require-dev": { + "eloquent/phony-phpunit": "^2.0", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^6.0", + "squizlabs/php_codesniffer": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "http://shadowhand.me" + } + ], + "description": "Google OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "Authentication", + "authorization", + "client", + "google", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-google/issues", + "source": "https://github.com/thephpleague/oauth2-google/tree/3.0.4" + }, + "time": "2021-01-27T16:09:03+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "nyholm/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2024-09-09T07:06:30+00:00" + }, + { + "name": "nyholm/psr7-server", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7-server.git", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7-server/zipball/4335801d851f554ca43fa6e7d2602141538854dc", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "nyholm/nsa": "^1.1", + "nyholm/psr7": "^1.3", + "phpunit/phpunit": "^7.0 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nyholm\\Psr7Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "Helper classes to handle PSR-7 server requests", + "homepage": "http://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7-server/issues", + "source": "https://github.com/Nyholm/psr7-server/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2023-11-08T09:30:43+00:00" + }, + { + "name": "omines/oauth2-gitlab", + "version": "3.6.0", + "source": { + "type": "git", + "url": "https://github.com/omines/oauth2-gitlab.git", + "reference": "5d8afd581c3d40dc469d03fa42965c449e95de9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/omines/oauth2-gitlab/zipball/5d8afd581c3d40dc469d03fa42965c449e95de9a", + "reference": "5d8afd581c3d40dc469d03fa42965c449e95de9a", + "shasum": "" + }, + "require": { + "league/oauth2-client": "^2.4.1", + "php": ">=8.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.37.1", + "guzzlehttp/psr7": "^2.6.1", + "http-interop/http-factory-guzzle": "^1.2", + "infection/infection": "^0.27.7", + "m4tthumphrey/php-gitlab-api": "^11.12", + "mockery/mockery": "^1.6.6", + "php-http/guzzle7-adapter": "^1.0.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.41", + "phpstan/phpstan-mockery": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpunit/phpunit": "^10.4.2" + }, + "suggest": { + "m4tthumphrey/php-gitlab-api": "For further API usage using the acquired OAuth2 token" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Omines\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niels Keurentjes", + "email": "niels.keurentjes@omines.com", + "homepage": "https://www.omines.nl/" + } + ], + "description": "GitLab OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "authorisation", + "authorization", + "client", + "gitlab", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/omines/oauth2-gitlab/issues", + "source": "https://github.com/omines/oauth2-gitlab/tree/3.6.0" + }, + "time": "2023-11-06T21:46:04+00:00" + }, + { + "name": "php-http/guzzle6-adapter", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/guzzle6-adapter.git", + "reference": "9d1a45eb1c59f12574552e81fb295e9e53430a56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/guzzle6-adapter/zipball/9d1a45eb1c59f12574552e81fb295e9e53430a56", + "reference": "9d1a45eb1c59f12574552e81fb295e9e53430a56", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0", + "php": "^7.1 || ^8.0", + "php-http/httplug": "^2.0", + "psr/http-client": "^1.0" + }, + "provide": { + "php-http/async-client-implementation": "1.0", + "php-http/client-implementation": "1.0", + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "ext-curl": "*", + "php-http/client-integration-tests": "^2.0 || ^3.0", + "phpunit/phpunit": "^7.4 || ^8.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Adapter\\Guzzle6\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David de Boer", + "email": "david@ddeboer.nl" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Guzzle 6 HTTP Adapter", + "homepage": "http://httplug.io", + "keywords": [ + "Guzzle", + "http" + ], + "support": { + "issues": "https://github.com/php-http/guzzle6-adapter/issues", + "source": "https://github.com/php-http/guzzle6-adapter/tree/v2.0.2" + }, + "abandoned": "guzzlehttp/guzzle or php-http/guzzle7-adapter", + "time": "2021-03-02T10:52:33+00:00" + }, + { + "name": "php-http/httplug", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/5cad731844891a4c282f3f3e1b582c46839d22f4", + "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/promise": "^1.1", + "psr/http-client": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "support": { + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/2.4.1" + }, + "time": "2024-09-23T11:39:58+00:00" + }, + { + "name": "php-http/promise", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/promise.git", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", + "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/1.3.1" + }, + "time": "2024-03-15T13:55:21+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.10.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.2", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2025-04-24T15:19:31+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, + "time": "2023-04-04T09:50:52+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "slim/psr7", + "version": "1.7.1", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-Psr7.git", + "reference": "fe98653e7983010aa85c1d137c9b9ad5a1cd187d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/fe98653e7983010aa85c1d137c9b9ad5a1cd187d", + "reference": "fe98653e7983010aa85c1d137c9b9ad5a1cd187d", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.5", + "php": "^8.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.0 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/polyfill-php80": "^1.29" + }, + "provide": { + "psr/http-factory-implementation": "^1.0", + "psr/http-message-implementation": "^1.0 || ^2.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.4", + "ext-json": "*", + "http-interop/http-factory-tests": "^1.0 || ^2.0", + "php-http/psr7-integration-tests": "^1.4", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.2", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.6 || ^10", + "squizlabs/php_codesniffer": "^3.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Psr7\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "https://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "https://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "https://www.lgse.com" + } + ], + "description": "Strict PSR-7 implementation", + "homepage": "https://www.slimframework.com", + "keywords": [ + "http", + "psr-7", + "psr7" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-Psr7/issues", + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.7.1" + }, + "time": "2025-05-13T14:24:12+00:00" + }, + { + "name": "slim/slim", + "version": "4.14.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim.git", + "reference": "5943393b88716eb9e82c4161caa956af63423913" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/5943393b88716eb9e82c4161caa956af63423913", + "reference": "5943393b88716eb9e82c4161caa956af63423913", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.4", + "ext-simplexml": "*", + "guzzlehttp/psr7": "^2.6", + "httpsoft/http-message": "^1.1", + "httpsoft/http-server-request": "^1.1", + "laminas/laminas-diactoros": "^2.17 || ^3", + "nyholm/psr7": "^1.8", + "nyholm/psr7-server": "^1.1", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^9.6", + "slim/http": "^1.3", + "slim/psr7": "^1.6", + "squizlabs/php_codesniffer": "^3.10", + "vimeo/psalm": "^5.24" + }, + "suggest": { + "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", + "ext-xml": "Needed to support XML format in BodyParsingMiddleware", + "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", + "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\": "Slim" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" + } + ], + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "https://www.slimframework.com", + "keywords": [ + "api", + "framework", + "micro", + "router" + ], + "support": { + "docs": "https://www.slimframework.com/docs/v4/", + "forum": "https://discourse.slimframework.com/", + "irc": "irc://irc.freenode.net:6667/slimphp", + "issues": "https://github.com/slimphp/Slim/issues", + "rss": "https://www.slimframework.com/blog/feed.rss", + "slack": "https://slimphp.slack.com/", + "source": "https://github.com/slimphp/Slim", + "wiki": "https://github.com/slimphp/Slim/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/slimphp", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slim/slim", + "type": "tidelift" + } + ], + "time": "2024-06-13T08:54:48+00:00" + }, + { + "name": "smarty/smarty", + "version": "v3.1.46", + "source": { + "type": "git", + "url": "https://github.com/smarty-php/smarty.git", + "reference": "b3ade90dece67812410954528e0039fb5b73bcf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/smarty-php/smarty/zipball/b3ade90dece67812410954528e0039fb5b73bcf7", + "reference": "b3ade90dece67812410954528e0039fb5b73bcf7", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^6.5 || ^5.7 || ^4.8", + "smarty/smarty-lexer": "^3.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "libs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Monte Ohrt", + "email": "monte@ohrt.com" + }, + { + "name": "Uwe Tews", + "email": "uwe.tews@googlemail.com" + }, + { + "name": "Rodney Rehm", + "email": "rodney.rehm@medialize.de" + } + ], + "description": "Smarty - the compiling PHP template engine", + "homepage": "http://www.smarty.net", + "keywords": [ + "templating" + ], + "support": { + "forum": "http://www.smarty.net/forums/", + "irc": "irc://irc.freenode.org/smarty", + "issues": "https://github.com/smarty-php/smarty/issues", + "source": "https://github.com/smarty-php/smarty/tree/v3.1.46" + }, + "time": "2022-08-01T21:58:13+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "wp-statistics/pchart", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/wp-statistics/pchart.git", + "reference": "721d03460b66de3d52a890c65e7d5cbf3a656059" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-statistics/pchart/zipball/721d03460b66de3d52a890c65e7d5cbf3a656059", + "reference": "721d03460b66de3d52a890c65e7d5cbf3a656059", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "pChart": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mostafa Soufi", + "email": "mostafa.soufi@hotmail.com" + } + ], + "description": "A PHP Class to build Charts", + "support": { + "issues": "https://github.com/wp-statistics/pchart/issues", + "source": "https://github.com/wp-statistics/pchart/tree/1.2.3" + }, + "abandoned": true, + "time": "2022-11-14T13:06:09+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "platform-overrides": { + "php": "8.2.12" + }, + "plugin-api-version": "2.6.0" +} diff --git a/composer.phar b/composer.phar new file mode 100644 index 0000000000..bb6ba648b0 Binary files /dev/null and b/composer.phar differ diff --git a/config.inc.php b/config.inc.php index 30ea042637..c19b884ef3 100644 --- a/config.inc.php +++ b/config.inc.php @@ -1,2205 +1,2309 @@ -api = new stdClass(); -$tlCfg->cookie = new stdClass(); -$tlCfg->document_generator = new stdClass(); - -$tlCfg->spec_cfg = new stdClass(); - -$tlCfg->exec_cfg = new stdClass(); -$tlCfg->exec_cfg->view_mode = new stdClass(); -$tlCfg->exec_cfg->exec_mode = new stdClass(); - - -$tlCfg->UDFStripHTMLTags = true; - -// allow to define additional execution types other than -// defined in testcase.class.php -// array(code => lblkey) -// code int value > latest standard execution code defined. -// lblkey => key to be used on lang_get() call. -// -$tlCfg->custom_execution_types = null; - -$tlCfg->gui = new stdClass(); -$tlCfg->gui->custom_fields = new stdClass(); -$tlCfg->testcase_cfg = new stdClass(); -$tlCfg->req_cfg = new stdClass(); -$tlCfg->validation_cfg = new stdClass(); -$tlCfg->custom_fields = new stdClass(); -$tlCfg->req_spec_cfg = new stdClass(); -$tlCfg->diffEngine = new stdClass(); -$tlCfg->tplanDesign = new stdClass(); - -$tlCfg->notifications = new stdClass(); -$tlCfg->proxy = new stdClass(); - -$tlCfg->reqTCLinks = new stdClass(); - - -$tlCfg->keywords = new stdClass(); -$tlCfg->keywords->onDeleteCheckFrozenTCVersions = TRUE; -$tlCfg->keywords->onDeleteCheckExecutedTCVersions = TRUE; - -// main key testproject PREFIX -// element array -// 'addTCLinkIntoITS' true => add note to Issue Tracker to issue with -// ISSUE ID similar to the KEYWORD (see kwPrefix below) -// -// 'kwPrefix' to remove from keyword to create the ISSUE ID -// -$tlCfg->keywords->byTestProject = array(); - -$tlCfg->keywords->headsUpTSuiteOnExec = 'CMD_OPEN_ON_EXEC'; - -$tlCfg->accessWithoutLogin = array(); - - -/** @uses database access definition (generated automatically by TL installer) */ -@include_once('config_db.inc.php'); -if( !defined('DB_TABLE_PREFIX') ) { - define('DB_TABLE_PREFIX','' ); -} - -/** The root dir for the testlink installation with trailing slash */ -define('TL_ABS_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR); - -/** Just for documentation */ -$tlCfg->testlinkdotorg = 'http://www.testlink.org'; - -/** GUI themes (base for CSS and images)- modify if you create own one */ -$tlCfg->theme_dir = 'gui/themes/default/'; - -/** Dir for compiled templates */ -$tlCfg->temp_dir = TL_ABS_PATH . 'gui' . DIRECTORY_SEPARATOR . - 'templates_c' . DIRECTORY_SEPARATOR; -if (($tpltmp = getenv('TESTLINK_TEMPLATES_C'))) { - $tlCfg->temp_dir = trim($tpltmp); -} - -/** default filenames of CSS files of current GUI theme */ -define('TL_CSS_MAIN', 'testlink.css'); -define('TL_CSS_PRINT', 'tl_print.css'); -define('TL_CSS_DOCUMENTS', 'tl_documents.css'); - -define('TL_THEME_BASE_DIR', $tlCfg->theme_dir); -define('TL_THEME_IMG_DIR', $tlCfg->theme_dir . 'images/'); -define('TL_THEME_CSS_DIR', $tlCfg->theme_dir . 'css/'); -define('TL_TESTLINK_CSS', TL_THEME_CSS_DIR . TL_CSS_MAIN); -define('TL_PRINT_CSS', TL_THEME_CSS_DIR . TL_CSS_PRINT); - -// name of your custom.css, place it in same folder that standard TL css -// null or '' => do not use -$tlCfg->custom_css = null; - - -/** Include constants and magic numbers (users should not change it)*/ -require_once(TL_ABS_PATH . 'cfg' . DIRECTORY_SEPARATOR . 'const.inc.php'); - - -// ---------------------------------------------------------------------------- -/** @var string used to have (when needed) a possibility to identify different TL instances - @since 1.9.4 used on mail subject when mail logger is used - */ -$tlCfg->instance_name = 'Main TestLink Instance'; - -// do not use blanks or special characters, use a short string -$tlCfg->instance_id = 'TLM'; - -$tlCfg->gui->ux = 'tl-classic'; - -/** - * Copied from MantisBT - * - * Prefix for all TestLink cookies - * This should be an identifier which does not include spaces or periods, - * and should be unique per TestLink installation, especially if - * $tlCfg->cookie_path is not restricting the cookies' scope to the actual - * TestLink directory. - * @see $tlCfg->cookie->path - * @global string $tlCfg->cookie->prefix - */ -$tlCfg->cookie->prefix = 'TESTLINK1920'; - -/** - * @link http://php.net/function.setcookie - * - */ -$tlCfg->cookie->expire = (time()+60*60*24*30); // 30 days; -$tlCfg->cookie->domain = ''; -$tlCfg->cookie->secure = false; -$tlCfg->cookie->httponly = false; - -$tlCfg->cookie->testProjectMemory = $tlCfg->cookie->prefix . - '_PROJ_ID_USER_ID_'; - -/** - * Copied from MantisBT - * - * Specifies the path under which a cookie is visible - * All scripts in this directory and its sub-directories will be able - * to access TestLink cookies. - * It is recommended to set this to the actual TestLink path. - * @link http://php.net/function.setcookie - * @global string $tlCfg->cookie->path - */ - $tlCfg->cookie->path = '/'; - - -/* [ROLE INHERITANCE] */ -/** - * possible values - * - * 'testproject' - * 'global' - * - * 'testproject' - * till a role is specifically assigned to test plan, test plan role - * will be inherited from test project role. - * - * IMPORTANT NOTICE - * test project role can be specifically assigned or inherited from - * user's global role. - * - * if test project specifically assigned role changes, and test plan role was inherited, then it will also changes, due to inheritance. - * - * - * 'global' - * till a role is specifically assigned to test plan, test plan role - * will be inherited from user's global role, and NOT from test project - * specifically assigned role. - * - * if test project specifically assigned role changes, will not be changed. - * - */ - $tlCfg->testplan_role_inheritance_mode = 'testproject'; - - -/* [LOCALIZATION] */ - -/** @var string Default localization for users */ -// The value must be available in $$tlCfg->locales (see cfg/const.inc.php). -// Note: An attempt will be done to establish the default locale -// automatically using $_SERVER['HTTP_ACCEPT_LANGUAGE'] -$tlCfg->default_language = 'en_GB'; - -/** - * @var string Charset 'UTF-8' is only officially supported charset (Require - * MySQL version >= 4.1) 'ISO-8859-1' or another Charset could be set for - * backward compatability by experienced users. However we have not resources - * to support such patches. - **/ -$tlCfg->charset = 'UTF-8'; - -/** - * @var string characters used to surround a description in the user interface - * (for example role) - **/ -$tlCfg->gui_separator_open = '['; -$tlCfg->gui_separator_close = ']'; -$tlCfg->gui_room = '[ %s ]'; - -/** @var string Title separators are used when componing an title using several strings */ -$tlCfg->gui_title_separator_1 = ' : '; // object : name (Test Specification : My best product) -$tlCfg->gui_title_separator_2 = ' - '; // parent - child - -/** - * @var string delimiter used to create DOC ID in this way: - * . g_testcase_cfg->glue_character . - * Could not be empty - */ -$tlCfg->testcase_cfg->glue_character = '-'; - -/** - * fonts set used to draw charts - **/ -$tlCfg->charts_font_path = TL_ABS_PATH . "third_party/pchart/Fonts/tahoma.ttf"; -/** - * font size used to draw charts - **/ -$tlCfg->charts_font_size = 8; - - -// ---------------------------------------------------------------------------- -/* [SERVER ENVIRONMENT] */ - - -/** - * TICKET 4969: Add Setting to Force HTTPS - */ -$tlCfg->force_https = false; - - -/** - * @var integer Set the session timeout for inactivity [minutes]. - * Default high value disables this feature. - */ -$tlCfg->sessionInactivityTimeout = 9900; - -/** - * Set the session timeout value (in minutes). - * This will prevent sessions timing out after very short periods of time - * Warning: your server could block this settings - **/ -//ini_set('session.cache_expire',900); - -/** - * Set the session garbage collection timeout value (in seconds) - * The default session garbage collection in php is set to 1440 seconds (24 minutes) - * If you want sessions to last longer this must be set to a higher value. - * You may need to set this in your global php.ini if the settings don't take effect. - */ -//ini_set('session.gc_maxlifetime', 54000); - -$tlCfg->notifications->userSignUp = new stdClass(); -$tlCfg->notifications->userSignUp->enabled = TRUE; // @see notifyGlobalAdmins() -$tlCfg->notifications->userSignUp->to = new stdClass(); -$tlCfg->notifications->userSignUp->to->roles = array(TL_ROLES_ADMIN); -$tlCfg->notifications->userSignUp->to->users = null; // i.e. array('login01','login02'); - -// ---------------------------------------------------------------------------- -/* [LOGGING] */ - -/** Error reporting - do we want php errors to show up for users */ -/** configure on custom_config.inc.php */ -/** error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING); */ -/** error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING); */ -error_reporting(E_ALL); - -/** @var string Default level of logging (NONE, ERROR, INFO, DEBUG, EXTENDED) - * is not used by tlLogger, we need to change this in future. - */ -$tlCfg->log_level = 'ERROR'; - -/** @var boolean show smarty debug window */ -$tlCfg->smarty_debug = false; - -/** - * @var string Path to store logs - - * for security reasons (see http://itsecuritysolutions.org/2012-08-13-TestLink-1.9.3-multiple-vulnerabilities/) - * put it out of reach via web or configure access denied. - */ -$tlCfg->log_path = '/var/testlink/logs/'; /* unix example */ -if (($lp = getenv('TESTLINK_LOG_PATH'))) { - $tlCfg->log_path = trim($lp); -} - -/** - * @var string How to warning user when security weak points exists. - * - * 'SCREEN': messages will displayed on login screen, and tl desktop - * 'FILE': a file with a list is created but users are not notified via GUI - * user will receive a message on screen. (default) - * 'SILENT': same that FILE, but user will not receive message on screen. - */ -$tlCfg->config_check_warning_mode = 'FILE'; - -/** - * ONCE_FOR_SESSION - * ALWAYS - */ -$tlCfg->config_check_warning_frequence = 'ONCE_FOR_SESSION'; - -/** - * - */ -$tlCfg->userDocOnDesktop = OFF; - - -/** - * Configure if individual logging data stores are enabled of disabled - * Possibile values to identify loggers: 'db','file' - * $g_loggerCfg=null; all loggers enabled - * $g_loggerCfg['db']['enable']=true/false; - * $g_loggerCfg['file']['enable']=true/false; - * $g_loggerCfg['mail']['enable']=true/false; - */ -$g_loggerCfg = array('mail' => array('enable' => false)); - -/** @var integer All events older this value [days] are removed from the db, during login */ -$g_removeEventsOlderThan = 30; - - -/** @var map keys: 'all' + values present on proprety of logger class $loggerTypeDomain - * values can be only these defined on logger.class.php - * @since 1.9.4 - * example array('all' => array('INFO','AUDIT'), - * 'mail' => array('ERROR')) - * - * $tlCfg->loggerFilter = array('db' => array('DEBUG','AUDIT','WARNING','ERROR'), - * 'file' => array('NONE')); - * - */ -$tlCfg->loggerFilter = null; // default defined on logger.class.php ; - -// ---------------------------------------------------------------------------- -/* [SMTP] */ - -/** - * @var string SMTP server name or IP address ("localhost" should work in the most cases) - * Configure using custom_config.inc.php - * @uses lib/functions/email_api.php - */ -$g_smtp_host = '[smtp_host_not_configured]'; # SMTP server MUST BE configured - -# Configure using custom_config.inc.php -$g_tl_admin_email = '[testlink_sysadmin_email_not_configured]'; # for problem/error notification -$g_from_email = '[from_email_not_configured]'; # email sender -$g_return_path_email = '[return_path_email_not_configured]'; - -/** - * Email notification priority (low by default) - * Urgent = 1, Not Urgent = 5, Disable = 0 - **/ -$g_mail_priority = 5; - -/** - * Taken from mantis for phpmailer config - * select the method to mail by: - * PHPMAILER_METHOD_MAIL - mail() - * PHPMAILER_METHOD_SENDMAIL - sendmail - * PHPMAILER_METHOD_SMTP - SMTP - */ -$g_phpMailer_method = PHPMAILER_METHOD_SMTP; - -/** Configure only if SMTP server requires authentication */ -$g_smtp_username = ''; # user -$g_smtp_password = ''; # password - -/** - * This control the connection mode to SMTP server. - * Can be '', 'ssl','tls' - * @global string $g_smtp_connection_mode - */ -$g_smtp_connection_mode = ''; - -/** - * The smtp port to use. The typical SMTP ports are 25 and 587. The port to use - * will depend on the SMTP server configuration and hence others may be used. - * @global int $g_smtp_port - */ -$g_smtp_port = 25; - - -/** - * @see https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting - * Opportunistic TLS - */ -$g_SMTPAutoTLS = false; - -// ---------------------------------------------------------------------------- -/* [User Authentication] */ - -/** - * Login authentication method: - * 'MD5' => use password stored on db => will be deprecated and DB used. - * 'DB' => Same as MD5 use password stored on db - * 'LDAP' => use password from LDAP Server - */ -$tlCfg->authentication['domain'] = array('DB' => array('description' => 'DB', 'allowPasswordManagement' => true) , - 'LDAP' => array('description' => 'LDAP', 'allowPasswordManagement' => false) ); - -/* Default Authentication method */ -$tlCfg->authentication['method'] = 'DB'; - -// Applies only if authentication methos is DB. -// Used when: -// 1. user sign up -// -// null => only check password IS NOT EMPTY -// -// $tlCfg->passwordChecks = array('minlen' => 8,'maxlen' => 20,'number' => true,'letter' => true, -// 'capital' => true, 'symbol' => true); -$tlCfg->passwordChecks = null; - -// Applies ONLY to the HTML input. -// If auth method is DB, password will be stored as MD5 HASH that requires 32 chars (128 bits) -$tlCfg->loginPagePasswordMaxLenght = 40; - -/** - * Standard logout url, used also when SSO is used and hint to skip SSO is used. - * '' => use standard TestLink page - */ -$tlCfg->logoutUrl = ''; - -// users that will not allow expiration date management on GUI -$tlCfg->noExpDateUsers = array('admin'); - - -/** - * OAUTH auth - * Configure this on custom_config.inc.php - */ - -$tlCfg->OAuthServers = array(); - -// Google -// $tlCfg->OAuthServers = array(); -// $tlCfg->OAuthServers[1]['oauth_enabled'] = true; -// $tlCfg->OAuthServers[1]['oauth_name'] = 'google'; - -// Get from /gui/themes/default/images -// $tlCfg->OAuthServers[1]['oauth_client_id'] = 'CLIENT_ID'; -// $tlCfg->OAuthServers[1]['oauth_client_secret'] = 'CLIENT_SECRET'; -// Can be authorization_code (by default), client_credentials or password -// $tlCfg->OAuthServers[1]['oauth_grant_type'] = 'authorization_code'; -// $tlCfg->OAuthServers[1]['oauth_url'] = 'https://accounts.google.com/o/oauth2/auth'; -// $tlCfg->OAuthServers[1]['token_url'] = 'https://accounts.google.com/o/oauth2/token'; -// false => then the only user will be selected automatically (applied for google) -// $tlCfg->OAuthServers[1]['oauth_force_single'] = false; -// the domain you want to whitelist -// $tlCfg->OAuthServers[1]['oauth_domain'] = 'google.com'; -// $tlCfg->OAuthServers[1]['oauth_profile'] = 'https://www.googleapis.com/oauth2/v1/userinfo'; -// $tlCfg->OAuthServers[1]['oauth_scope'] = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'; - -// Github -// $tlCfg->OAuthServers[2]['oauth_enabled'] = true; -// $tlCfg->OAuthServers[2]['oauth_name'] = 'github'; -// $tlCfg->OAuthServers[2]['oauth_client_id'] = 'CLIENT_ID'; -// $tlCfg->OAuthServers[2]['oauth_client_secret'] = 'CLIENT_SECRET'; - -// Can be authorization_code (by default), client_credentials or password -// $tlCfg->OAuthServers[2]['oauth_grant_type'] = 'authorization_code'; -// $tlCfg->OAuthServers[2]['oauth_url'] = 'https://github.com/login/oauth/authorize'; - -// $tlCfg->OAuthServers[2]['token_url'] = 'https://github.com/login/oauth/access_token'; -// false => then the only user will be selected automatically (applied for google) -// $tlCfg->OAuthServers[2]['oauth_force_single'] = false; -// $tlCfg->OAuthServers[2]['oauth_profile'] = 'https://api.github.com/user'; -// $tlCfg->OAuthServers[2]['oauth_scope'] = 'user:email'; - -//Microsoft -//$tlCfg->OAuthServers[1]['oauth_enabled'] = true; -//$tlCfg->OAuthServers[1]['oauth_name'] = 'microsoft'; -//$tlCfg->OAuthServers[1]['oauth_client_id'] = 'CLIENT_ID'; -//$tlCfg->OAuthServers[1]['oauth_client_secret'] = 'CLIENT_SECRET'; - -// Can be authorization_code (by default), client_credentials or password -//$tlCfg->OAuthServers[1]['oauth_grant_type'] = 'authorization_code'; -//$tlCfg->OAuthServers[1]['oauth_url'] = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'; - -//$tlCfg->OAuthServers[1]['token_url'] = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; -//$tlCfg->OAuthServers[1]['oauth_force_single'] = true; -//$tlCfg->OAuthServers[1]['oauth_profile'] = 'https://graph.microsoft.com/v1.0/me'; -//$tlCfg->OAuthServers[1]['oauth_scope'] = 'User.Read'; - -//$tlCfg->OAuthServers[1]['redirect_uri'] = 'TESTLINKURL/microsoftoauth.php'; - - -// Azure AD -// Fill in CLIENT_ID,CLIENT_SECRET,YOURTESTLINKSERVER and TENANTID with your information -// See this article for registering an application: https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code -// Make sure, you grant admint consent for it: https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-user-consent - -// $tlCfg->OAuthServers[1]['oauth_enabled'] = true; -// $tlCfg->OAuthServers[1]['oauth_name'] = 'azuread'; //do not change this - -// $tlCfg->OAuthServers[1]['oauth_client_id'] = 'CLIENT_ID'; -// $tlCfg->OAuthServers[1]['oauth_client_secret'] = 'CLIENT_SECRET'; -// $tlCfg->OAuthServers[1]['redirect_uri'] = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . '/login.php'; - -// $tlCfg->OAuthServers[1]['oauth_force_single'] = true; - -// $tlCfg->OAuthServers[1]['oauth_grant_type'] = 'authorization_code'; -// $tlCfg->OAuthServers[1]['oauth_url'] = 'https://login.microsoftonline.com/TENANTID/oauth2/authorize'; -// $tlCfg->OAuthServers[1]['token_url'] = 'https://login.microsoftonline.com/TENANTID/oauth2/token'; -// the domain you want to whitelist (email domains) -// $tlCfg->OAuthServers[1]['oauth_domain'] = 'autsoft.hu'; -// $tlCfg->OAuthServers[1]['oauth_profile'] = 'https://login.microsoftonline.com/TENANTID/openid/userinfo'; -// $tlCfg->OAuthServers[1]['oauth_scope'] = 'https://graph.microsoft.com/mail.read https://graph.microsoft.com/user.read openid profile email'; - -/** - * Single Sign On authentication - * - * SSO_method: CLIENT_CERTIFICATE, tested with Apache Webserver - * SSP_method: WEBSERVER_VAR, tested with Apache and Shibboleth Service Provider. - */ -$tlCfg->authentication['SSO_enabled'] = false; -$tlCfg->authentication['SSO_logout_destination'] = 'YOUR LOGOUT DESTINATION'; - -// Tested with Apache Webserver -//$tlCfg->authentication['SSO_method'] = 'CLIENT_CERTIFICATE'; -//$tlCfg->authentication['SSO_uid_field'] = 'SSL_CLIENT_S_DN_Email'; - -// Tested with Apache and Shibboleth Service Provider -//$tlCfg->authentication['SSO_method'] = 'WEBSERVER_VAR'; -//$tlCfg->authentication['SSO_uid_field'] = 'REMOTE_USER'; -//$tlCfg->authentication['SSO_user_target_dbfield'] = 'email'; - - -/** - * LDAP authentication credentials, Multiple LDAP Servers can be used. - * User will be authenticaded against each server (one after other using array index order) - * till authentication succeed or all servers have been used. - */ -$tlCfg->authentication['ldap'] = array(); -$tlCfg->authentication['ldap'][1]['ldap_server'] = 'localhost'; -$tlCfg->authentication['ldap'][1]['ldap_port'] = '389'; -$tlCfg->authentication['ldap'][1]['ldap_version'] = '3'; // could be '2' in some cases -$tlCfg->authentication['ldap'][1]['ldap_root_dn'] = 'dc=mycompany,dc=com'; -$tlCfg->authentication['ldap'][1]['ldap_bind_dn'] = ''; // Left empty for anonymous LDAP binding -$tlCfg->authentication['ldap'][1]['ldap_bind_passwd'] = ''; // Left empty for anonymous LDAP binding -$tlCfg->authentication['ldap'][1]['ldap_tls'] = false; // true -> use tls - -// Following configuration parameters are used to build -// ldap filter and ldap attributes used by ldap_search() -// -// filter => "(&$t_ldap_organization($t_ldap_uid_field=$t_username))"; -// attributess => array( $t_ldap_uid_field, 'dn' ); -// -// This can be used to manage situation like explained on post on forum: -// ActiveDirectory + users in AD group -// -$tlCfg->authentication['ldap'][1]['ldap_organization'] = ''; // e.g. '(organizationname=*Traffic)' -$tlCfg->authentication['ldap'][1]['ldap_uid_field'] = 'uid'; // Use 'sAMAccountName' for Active Directory - -// Configure following fields in custom_config.inc.php according your configuration -$tlCfg->authentication['ldap'][1]['ldap_email_field'] = 'mail'; -$tlCfg->authentication['ldap'][1]['ldap_firstname_field'] = 'givenname'; -$tlCfg->authentication['ldap'][1]['ldap_surname_field'] = 'sn'; - - -// Follows Mantisbt idea. -// True if user does not exist on DB, but can be get from LDAP, -// the user will be created AUTOMATICALLY with default user role. -// Create user with following data from LDAP -// mail -// name -// surname -$tlCfg->authentication['ldap_automatic_user_creation'] = false; - - -/** Enable/disable Users to create accounts on login page */ -$tlCfg->user_self_signup = TRUE; - -/** What happens when Administrator push the Reset Password Button - 'send_password_by_mail' - 'display_on_screen' -*/ -$tlCfg->password_reset_send_method = 'send_password_by_mail'; - -/** - * Validating new user login names - * Taken mantisbt version 1.2.5 - www.mantisbt.org and adapted - * - * The regular expression to use when validating new user login names - * The default regular expression allows a-z, A-Z, 0-9, +, -, dot, @ and underscore. - * For testing regular expressions, use http://rubular.com/. - * For regular expression to englihs, use http://xenon.stanford.edu/~xusch/regexp/analyzer.html - */ -$tlCfg->validation_cfg->user_login_valid_regex='/^([a-z\d\-.+_@]+(@[a-z\d\-.]+\.[a-z]{2,4})?)$/i'; - -/** - * Validating user email addresses - * Example of other possibilities: - * - * $regex = "/^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*" . - * "@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i"; - * $regex = "/^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/"; - * - **/ -// -// This expression does not allow Top Level Domian (last part of domain name) longer than 4 -// If you need to change this -// Configure this on custom_config.inc.php -$tlCfg->validation_cfg->user_email_valid_regex_js = "/^(\w+)([-+.][\w]+)*@(\w[-\w]*\.){1,5}([A-Za-z]){2,4}$/"; -$tlCfg->validation_cfg->user_email_valid_regex_php = "/^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/U"; - - -// -------------------------------------------------------------------------------------- -/* [API] */ - -/** XML-RPC API availability - do less than promised ;) - FALSE => user are not able to generate and set his/her API key. - XML-RPC server do not check this config in order to answer or not a call. - */ - -$tlCfg->api->enabled = TRUE; - -// used to display API ID info in the *View pages -$tlCfg->api->id_format = "[ID: %s ]"; - - -// --------------------------------------------------------------------------------- -/* [GUI LAYOUT] */ - -/** Company logo (used by navigation bar and login page page) */ -$tlCfg->logo_login = 'tl-logo-transparent-25.png'; -$tlCfg->logo_navbar = 'tl-logo-transparent-12.5.png'; - -/** Height of the navbar always displayed */ -$tlCfg->navbar_height = 70; - -/** Login page could show an informational text */ -$tlCfg->login_info = ''; // Empty by default - - - -/** - * controls if pagination (via Javascript) will be enabled - */ -$tlCfg->gui->projectView = new stdClass(); -$tlCfg->gui->projectView->pagination = new stdClass(); -$tlCfg->gui->projectView->pagination->enabled = true; -$tlCfg->gui->projectView->pagination->length = '[20, 40, 60, -1], [20, 40, 60, "All"]'; - -$tlCfg->gui->usersAssign = new stdClass(); -$tlCfg->gui->usersAssign->pagination = new stdClass(); -$tlCfg->gui->usersAssign->pagination->enabled = true; -$tlCfg->gui->usersAssign->pagination->length = '[20, 40, 60, -1], [20, 40, 60, "All"]'; - -$tlCfg->gui->planView = new stdClass(); -$tlCfg->gui->planView->pagination = new stdClass(); -$tlCfg->gui->planView->pagination->enabled = true; -$tlCfg->gui->planView->pagination->length = '[20, 40, 60, -1], [20, 40, 60, "All"]'; -$tlCfg->gui->planView->itemQtyForTopButton = 10; - -$tlCfg->gui->buildView = new stdClass(); -$tlCfg->gui->buildView->itemQtyForTopButton = 10; - - -/** - * controls if operation area (buttons) starts open ('' or 'inline') or closed ('none') on: - * - test suite management - * - test case management - * - req. spec management - * - req. management - */ -$tlCfg->gui->op_area_display = new stdClass(); - -// test_spec_container => test project, test suite -$tlCfg->gui->op_area_display->test_spec_container = 'none'; // '' -$tlCfg->gui->op_area_display->test_case = 'none'; // 'inline' -$tlCfg->gui->op_area_display->req_spec_container = 'none'; // 'inline' -$tlCfg->gui->op_area_display->req = 'none'; // 'inline' - - - -/** - * @var string Availability of Test Project specific background colour - * 'background' -> standard behaviour for 1.6.x you can have a different - * background colour for every test project. - * 'none' -> new behaviour no background color change - */ -$tlCfg->gui->testproject_coloring = 'none'; // I'm sorry default is not coloring using coloring is a pain - // and useless -/** @TODO havlatm4francisco Ok, then merge these two attributes into one */ -/** default background color */ -$tlCfg->gui->background_color = '#9BD'; - -// ENABLED: on features that assign user role to test projects and test plan, colour user name -// according GLOBAL role -// DISABLED: do not color [STANDARD BEHAVIOUR] -$tlCfg->gui->usersAssignGlobalRoleColoring = DISABLED; - - -// Enable/disable rounded corners via javascript -$tlCfg->gui->round_corners = new stdClass(); -$tlCfg->gui->round_corners->exec_history = ENABLED; -$tlCfg->gui->round_corners->tc_title = ENABLED; -$tlCfg->gui->round_corners->tc_spec = ENABLED; - -/** - * Display name definition (used to build a human readable display name for users) - * Example of values: - * '%first% %last%' -> John Cook - * '%last%, %first%' -> Cook John - * '%first% %last% %login%' -> John Cook [ux555] - **/ -$tlCfg->username_format = '%login%'; - -/** Configure the frame frmWorkArea navigator width */ -$tlCfg->frame_workarea_default_width = "30%"; - -/** true => icon edit will be added into as indication an edit features */ -$tlCfg->gui->show_icon_edit = false; - -/** - * '' => test project name - * 'prefix' => prefix : test project name - * - * ATTENTION : * is used to indicate test project is INACTIVE - * see also $tlCfg->gui->tprojects_combo_order_by - */ -$tlCfg->gui->tprojects_combo_format = 'prefix'; - - -/** - * Order to use when building a testproject combobox (value must be SQL compliant) - * For example: - * 'ORDER BY name' - * 'ORDER_BY nodes_hierarchy.id DESC' -> similar effect to order last created firts - **/ -// $tlCfg->gui->tprojects_combo_order_by = 'ORDER BY nodes_hierarchy.id DESC'; -$tlCfg->gui->tprojects_combo_order_by = 'ORDER BY TPROJ.prefix ASC'; - - - -/** Configure the input size of test case search by id on navigation bar. - * This value will be added to the length of the prefix to dynamically set input size. - * Example: prefix is "projectA-" -> length of prefix is 9 - * Now the here defined value (default: 6) will be added to the prefix length - * -> Input field will have an input size of 15 - **/ -$tlCfg->gui->dynamic_quick_tcase_search_input_size = 6; - -// used to round percentages on metricsDashboard.php -$tlCfg->dashboard_precision = 2; - -/** - * Choose what kind of webeditor you want to use in every TL area. This configuration - * will be used if no element with search key (area) is found on this structure. - * Every element is a mp with this configuration keys: - * - * 'type': - * 'ckeditor' - * 'tinymce' ==> will be deprecated in future versions - * 'none' -> use plain text area input field - * 'toolbar': only applicable for type = 'fckeditor', 'ckeditor' - * name of ToolbarSet (See: http://docs.fckeditor.net/ for more information about ToolbarSets) - * TestLink stores own definitions in /cfg/tl_ckeditor_config.js - * - * - * The next keys/areas are supported: - * 'all' (default setting), - * 'design', 'steps_design', 'testplan', 'build', 'testproject', 'role', 'requirement', 'requirement_spec'. - * - * Examples: - * - * // Copy this to custom_config.inc.php if you want use 'tinymce' as default. - * $tlCfg->gui->text_editor['all'] = array( 'type' => 'tinymce'); - * // Copy this to custom_config.inc.php if you want use 'nome' as default. - * $tlCfg->gui->text_editor['all'] = array( 'type' => 'none'); - * //This configuration is useful only if default type is set to 'fckeditor' - * $tlCfg->gui->text_editor['design'] = array('toolbar' => 'tl_mini'); - * - * $tlCfg->gui->text_editor['testplan'] = array( 'type' => 'none'); - * $tlCfg->gui->text_editor['build'] = array( 'type' => 'fckeditor','toolbar' => 'tl_mini'); - * $tlCfg->gui->text_editor['testproject'] = array( 'type' => 'tinymce'); - * $tlCfg->gui->text_editor['role'] = array( 'type' => 'tinymce'); - * $tlCfg->gui->text_editor['requirement'] = array( 'type' => 'none'); - * $tlCfg->gui->text_editor['requirement_spec'] = array( 'type' => 'none'); - * - * - * Hint: After doing configuration changes, clean you Browser's cookies and cache - */ -/* -$tlCfg->gui->text_editor = array(); -$tlCfg->gui->text_editor['all'] = array('type' => 'fckeditor', - 'toolbar' => 'tl_default', - 'configFile' => 'cfg/tl_ckeditor_config.js',); -$tlCfg->gui->text_editor['execution'] = array( 'type' => 'none'); -*/ - -$tlCfg->gui->text_editor = array(); -$tlCfg->gui->text_editor['all'] = array('type' => 'ckeditor','toolbar' => 'Testlink', - 'configFile' => 'cfg/tl_ckeditor_config.js', - 'height' => 150); - -// mini toolbar for test case steps edit -$tlCfg->gui->text_editor['steps_design'] = array('type' => 'ckeditor','toolbar' => 'TestlinkMini', - 'configFile' => 'cfg/tl_ckeditor_config.js', - 'height' => 100); - -$tlCfg->gui->text_editor['execution'] = array( 'type' => 'none'); -$tlCfg->gui->text_editor['edit_execution'] = array( 'type' => 'none', 'cols' => 80, 'rows' => 20); -$tlCfg->gui->text_editor['display_execution_notes'] = array('type' => 'none', 'cols' => 80, 'rows' => 20); - -/** User can choose order of menu areas */ -$tlCfg->gui->layoutMainPageLeft = array( 'testProject' => 1, 'userAdministration' => 2 , - 'requirements' => 3, 'testSpecification' => 4, - 'general' => 5); -$tlCfg->gui->layoutMainPageRight = array( 'testPlan' => 1, 'testExecution' => 2 , - 'testPlanContents' => 3); - -/** - * Enable warning on a changed content before an user leave a page. - * - * Tested in: - * - IE8 OK - * - Firefox 3 OK - * - Chrome FAIL - * - * Does not work in Webkit browsers (Chrome, Safari) when using frames. - * Bug in webkit: https://bugs.webkit.org/show_bug.cgi?id=19418 - */ - -// seems that with config options that will be used on javascript via smarty template variables -// we are having problems using FALSE/TRUE => use 0/1 (or our CONSTANT DISABLED/ENABLED) -$tlCfg->gui->checkNotSaved = ENABLED; - - -// ---------------------------------------------------------------------------- -/* [GUI: TREE] */ - -/** Default ordering value for new Test Suites and Test Cases to separate them */ -$tlCfg->treemenu_default_testsuite_order = 1; -$tlCfg->treemenu_default_testcase_order = 1000; - -/** show/hide testcase id on tree menu */ -$tlCfg->treemenu_show_testcase_id = TRUE; - -/** Reorder test cases based on TC Name or External ID in tree on - * test suite level using reorder button - */ -// 'EXTERNAL_ID' -> Sort on Test Case External ID field displayed on tree.(Default) -// 'NAME' -> Sort on Test Case Name field - -$tlCfg->testcase_reorder_by = 'EXTERNAL_ID'; -// $tlCfg->testcase_reorder_by = 'NAME'; - -// ---------------------------------------------------------------------------- -/* [GUI: Javascript libraries] */ - -// May be in future another table sort engine will be better -// kryogenix.org -> Stuart Langridge sortTable -// '' (empty string) -> disable table sorting feature -$g_sort_table_engine='kryogenix.org'; - - -// -------------------------------------------------------------------------------------- -/* [Reports] */ -$tlCfg->reportsCfg=new stdClass(); - -//Displayed execution statuses to use on reports (ordered). */ -$tlCfg->reportsCfg->exec_status = $tlCfg->results['status_label_for_exec_ui']; - -/** - * Default Offset in seconds for reporting start date (reports with date range) - * @uses lib/results/resultsMoreBuilds.php - */ -$tlCfg->reportsCfg->start_date_offset = (7*24*60*60); // one week - -// minutes part is ignored but must be configured. -// Hint: set always to :00 -$tlCfg->reportsCfg->start_time = '00:00'; - -// Result matrix (resultsTC.php) -$tlCfg->resultMatrixReport = new stdClass(); - -// Shows an extra column with the result of the latest execution on -// the lastest CREATED build -$tlCfg->resultMatrixReport->buildColumns['showExecutionResultLatestCreatedBuild'] = true; - -// Result matrix (resultsTC.php) -// Shows an extra column with the note of latest execution on -// the lastest CREATED build -$tlCfg->resultMatrixReport->buildColumns['showExecutionNoteLatestCreatedBuild'] = true; - -// Show build columns in revers order. The latest build is to the left -$tlCfg->resultMatrixReport->buildColumns['latestBuildOnLeft'] = false; - -// After having got performance and usability issue, a limit on max qty of builds -// allowed on data extration has been set. -// Is absolutely arbitrary -// -$tlCfg->resultMatrixReport->buildQtyLimit = 6; - -// ORDER BY sql clause, refers to builds table columns -$tlCfg->resultMatrixReport->buildOrderByClause = " ORDER BY name ASC"; - - -// Show all available status details for test plans on metrics dashboard -$tlCfg->metrics_dashboard = new stdClass(); -$tlCfg->metrics_dashboard->show_test_plan_status = false; - - - - -// ---------------------------------------------------------------------------- -/* [GENERATED DOCUMENTATION] */ - -/** - * Texts and settings for printed documents - * Image is expected in directory /gui/themes//images/ - * Leave text values empty if you would like to hide parameters. - */ -$tlCfg->document_generator->company_name = 'TestLink Community [configure $tlCfg->document_generator->company_name]'; -$tlCfg->document_generator->company_copyright = '2012 © TestLink Community'; -$tlCfg->document_generator->confidential_msg = ''; - -// Logo for generated documents -$tlCfg->document_generator->company_logo = $tlCfg->logo_login; -$tlCfg->document_generator->company_logo_height = '53'; - -/** CSS used in printed html documents */ -$tlCfg->document_generator->css_template = 'css/tl_documents.css'; - -// CSS file for Requirement Specification Document, Requirement and Requirement Spec Print View -$tlCfg->document_generator->requirement_css_template = 'css/tl_documents.css'; - -/** Misc settings */ -// Display test case version when creating: -// - test spec document -// - test reports -$tlCfg->document_generator->tc_version_enabled = TRUE; - - - -// ---------------------------------------------------------------------------- -/* [Test Executions] */ - -// $tlCfg->exec_cfg->enable_test_automation = DISABLED; - -// ENABLED -> enable XML-RPC calls to external test automation server -// new buttons will be displayed on execution pages -// DISABLED -> disable -$tlCfg->exec_cfg->enable_test_automation = DISABLED; - - -// ASCending -> last execution at bottom -// DESCending -> last execution on top [STANDARD BEHAVIOUR] -$tlCfg->exec_cfg->history_order = 'DESC'; - -// TRUE -> the whole execution history for the choosen build will be showed -// FALSE -> just last execution for the choosen build will be showed [STANDARD BEHAVIOUR] -$tlCfg->exec_cfg->history_on = FALSE; - -// TRUE -> test case VERY LAST (i.e. in any build) execution status will be displayed [STANDARD BEHAVIOUR] -// FALSE -> only last result on current build. -$tlCfg->exec_cfg->show_last_exec_any_build = TRUE; - -// TRUE -> History for all builds will be shown -// FALSE -> Only history of the current build will be shown [STANDARD BEHAVIOUR] -$tlCfg->exec_cfg->show_history_all_builds = FALSE; - -// TRUE -> History for all platforms (if any exists for test plan) will be shown -// FALSE -> Only history of the current platform will be shown [STANDARD BEHAVIOUR] -$tlCfg->exec_cfg->show_history_all_platforms = FALSE; - -// different models for the attachments management on execution page -// $att_model_m1 -> shows upload button and title -// $att_model_m2 -> hides upload button and title -$tlCfg->exec_cfg->att_model = $att_model_m2; //defined in const.inc.php - -// IVU -// Default Value -// USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS -// USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS -// USE_LATEST_EXEC_ON_TESTPLAN_PLAT_FOR_COUNTERS -$tlCfg->exec_cfg->tcases_counters_mode = array(); -$tlCfg->exec_cfg->tcases_counters_mode['with_platforms'] = - USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS; - -$tlCfg->exec_cfg->tcases_counters_mode['without_platforms'] = - USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS; - - -$tlCfg->exec_cfg->tcases_counters_mode_domain = array(); -$tlCfg->exec_cfg->tcases_counters_mode_domain['with_platforms'] = - array('USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS', - 'USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS', - 'USE_LATEST_EXEC_ON_TESTPLAN_PLAT_FOR_COUNTERS'); - -$tlCfg->exec_cfg->tcases_counters_mode_domain['without_platforms'] = - array('USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS', - 'USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS'); - - - - -// ENABLED -> test cases will be coloured according to test case status -$tlCfg->exec_cfg->enable_tree_testcases_colouring = ENABLED; - -// ENABLED -> test cases will be coloured according to execution status on build selected for execution -// DISABLED -> test cases will be coloured according status on latest execution regardless of selected build -// see http://mantis.testlink.org/view.php?id=3450 for more details -$tlCfg->exec_cfg->testcases_colouring_by_selected_build = ENABLED; - -// ENABLED -> test case counters will be coloured according to test case status -$tlCfg->exec_cfg->enable_tree_counters_colouring = ENABLED; - - -// This can help to avoid performance problems. -// Controls what happens on right frame when user clicks on a testsuite on tree. -// ENABLED -> show all test cases -// see $tlCfg->exec_cfg->show_testsuite_contents_deep -// -// DISABLED -> nothing happens, to execute a test case you need to click on test case -$tlCfg->exec_cfg->show_testsuite_contents = DISABLED; - -// @since 1.9.13 -// works in 'team' with $tlCfg->exec_cfg->show_testsuite_contents -// children: only direct children. -// deep: all test cases present in test suite and test suites in any level below -// selected test suite. -// IMPORTANT NOTICE: -// selecting deep can create performance issues. -// -$tlCfg->exec_cfg->show_testsuite_contents_deep = 'children'; - - -// ENABLED -> enable testcase counters by status on tree -$tlCfg->exec_cfg->enable_tree_testcase_counters = ENABLED; - - -// Define list of roles that are affected by: -// $tlCfg->exec_cfg->view_mode and $tlCfg->exec_cfg->exec_mode -// User must reconfigure if define other simple tester roles -// -// In addition (till code changes) also roles that verify this condition: -// $effective_role->hasRight('testplan_execute') and !$effective_role->hasRight('testplan_planning') -// Will be affected by: -// $tlCfg->exec_cfg->view_mode and $tlCfg->exec_cfg->exec_mode -// -$tlCfg->exec_cfg->simple_tester_roles=array(TL_ROLES_TESTER); - -// Filter Test cases a user with tester role can VIEW depending on -// test execution assignment. -// all: all test cases. -// assigned_to_me: test cases assigned to logged user. -// assigned_to_me_or_free: test cases assigned to logged user or not assigned -$tlCfg->exec_cfg->view_mode->tester='assigned_to_me'; - -// Filter Test cases a user with tester role can EXECUTE depending on -// test execution assignment. -// all: all test cases. -// assigned_to_me: test cases assigned to logged user. -// assigned_to_me_or_free: test cases assigned to logged user or not assigned -$tlCfg->exec_cfg->exec_mode->tester='assigned_to_me'; - - -// How to set defaut values for execution fields (standard & custom) -// clean => all clean -// latest => get as much as possible values from latest execution on -// same context => test plan,platform, build -$tlCfg->exec_cfg->exec_mode->new_exec='clean'; - - -// @since 1.9.15 -// Before 1.9.15 save & move to next worked JUST inside -// a test suite => save_and_move = 'limited' -// 1.9.15 will move on whole test project -// save_and_move = 'unlimited' -$tlCfg->exec_cfg->exec_mode->save_and_move='unlimited'; - -$tlCfg->exec_cfg->exec_mode->addLinkToTLChecked = false; -$tlCfg->exec_cfg->exec_mode->addLinkToTLPrintViewChecked = false; -$tlCfg->exec_cfg->exec_mode->assignTaskChecked = false; - - -/** User filter in Test Execution navigator - default value */ -// logged_user -> combo will be set to logged user -// none -> no filter applied by default -$tlCfg->exec_cfg->user_filter_default='none'; - - -// 'horizontal' -> step and results on the same row -// 'vertical' -> steps on one row, results in the row bellow -$tlCfg->exec_cfg->steps_results_layout = 'horizontal'; - -// true => on single test case execution feature, notes and result -// can be provided for each step -// false => pre 1.9.10 behaviour -// -$tlCfg->exec_cfg->steps_exec = true; - -// this setting will work on AND mode with: -// $tlCfg->exec_cfg->steps_exec -$tlCfg->exec_cfg->steps_exec_attachments = true; - -// When textarea is displayed to allow user to write execution notes -// at step level, choose what to display: -// 'empty' -// 'latest' => latest execution notes. -$tlCfg->exec_cfg->steps_exec_notes_default = 'empty'; - - -// 'empty' -// 'latest' => latest execution notes. -$tlCfg->exec_cfg->steps_exec_status_default = 'empty'; - -// Parameters to show notes/details when entering test execution feature -// EXPAND: show expanded/open -// COLLAPSE: show collapsed/closede -// LAST_USER_CHOICE: get status from cookie that holds last user choice.[STANDARD BEHAVIOUR] -$tlCfg->exec_cfg->expand_collapse = new stdClass(); -$tlCfg->exec_cfg->expand_collapse->testplan_notes = LAST_USER_CHOICE; -$tlCfg->exec_cfg->expand_collapse->platform_description = LAST_USER_CHOICE; -$tlCfg->exec_cfg->expand_collapse->build_description = LAST_USER_CHOICE; -$tlCfg->exec_cfg->expand_collapse->testsuite_details = LAST_USER_CHOICE; - - - -$tlCfg->exec_cfg->copyLatestExecIssues = new stdClass(); - -// true => When saving an execution, a new option will be displayed, and user will be -// able to do a choice -// COPY OR NOT issues linked to latest execution to the new execution -// DEAFULT false => no option on GUI - -$tlCfg->exec_cfg->copyLatestExecIssues->enabled = FALSE; - - -// value to set as default -$tlCfg->exec_cfg->copyLatestExecIssues->default = FALSE; - -// you can choose only between columns present on -// (see exec.inc.php, function get_bugs_for_exec()) -$tlCfg->exec_cfg->bugs_order_clause = ' ORDER BY builds.name,step_number,bug_id '; - -$tlCfg->exec_cfg->features = new stdClass(); -$tlCfg->exec_cfg->features->attachments = new stdClass(); -$tlCfg->exec_cfg->features->attachments->enabled = true; -$tlCfg->exec_cfg->features->exec_duration = new stdClass(); -$tlCfg->exec_cfg->features->exec_duration->enabled = true; - -$tlCfg->exec_cfg->issues = new stdClass(); -$tlCfg->exec_cfg->issues->tcase_level = new stdClass(); -$tlCfg->exec_cfg->issues->tcstep_level = new stdClass(); - -/** - * %%STEPNUMBER%%,%%TCNAME%%,%%PROJECTNAME%%,%%PLANNAME%% - * %%BUILDNAME%%,%%PLATFNAME%%,%%EXECTSISO%%, - * %%TCPATHNAME%% - * - * /saado/TS100/SAA-4:WSTEPS Executed ON (ISO FORMAT): 2018-02-25CET10:00 - */ -$tlCfg->exec_cfg->issues->tcase_level->subject = -'$$issue_subject_tcname %%TCPATHNAME%% - $$issue_subject_execon %%EXECTSISO%% '; - -/* -$tlCfg->exec_cfg->issues->tcstep_level->subject = -'$$issue_on_step %%STEPNUMBER%% - $$issue_subject_tcname %%TCNAME%% - ' . -'$$issue_subject_projectname %%PROJECTNAME%% - ' . -'$$issue_subject_planname %%PLANNAME%% - ' . -'$$issue_subject_buildname %%BUILDNAME%% - ' . -'$$issue_subject_platfname %%PLATFNAME%%'; -*/ - -$tlCfg->exec_cfg->issues->tcstep_level->subject = '$$issue_on_step %%STEPNUMBER%% - $$issue_subject_tcname %%TCNAME%% '; - - -// ---------------------------------------------------------------------- -/* [Test Specification] */ - -// TRUE will be displayed when displayed a test case -$tlCfg->spec_cfg->show_tplan_usage = TRUE; - -// 'horizontal' -> step and results on the same row -// 'vertical' -> steps on one row, results in the row bellow -$tlCfg->spec_cfg->steps_results_layout = 'horizontal'; - - -// ENABLED -> User will see a test suite filter while creating test specification -// DISABLED -> no filter available -// $g_spec_cfg->show_tsuite_filter = ENABLED; -$tlCfg->spec_cfg->show_tsuite_filter = ENABLED; - -// ENABLED -> every time user do some operation on test specification -// tree is updated on screen. -// DISABLED -> tree will not be updated, user can update it manually. -// Anyway on user interface user will be able to change this choice -// $g_spec_cfg->automatic_tree_refresh = ENABLED; -$tlCfg->spec_cfg->automatic_tree_refresh = ENABLED; - - -// To avoid perfomance problems on search test case feature, -// we can decide when to inform user that results can not be displayed -// due to too many results. -$tlCfg->testcase_cfg->search=new stdClass(); -$tlCfg->testcase_cfg->search->max_qty_for_display=200; - - -$tlCfg->testcase_cfg->duplicated_name_algorithm = new stdClass(); -// 'stringPrefix' => use duplicated_name_algorithm->text -// 'counterSuffix' => creare name as : -// test case title + (number of existent test cases +1) -// example: My Test Title 2 -// duplicated_name_algorithm->text is used as sprintf format mask -$tlCfg->testcase_cfg->duplicated_name_algorithm->type = 'stringPrefix'; -$tlCfg->testcase_cfg->duplicated_name_algorithm->text = "%Y%m%d-%H:%M:%S"; - -// $tlCfg->testcase_cfg->duplicated_name_algorithm->type = 'counterSuffix'; -// $tlCfg->testcase_cfg->duplicated_name_algorithm->text = " (%s)"; - - -// TICKET 6422: Estimation in Test specification as mandatory field -// Implemented using HTML5 -$tlCfg->testcase_cfg->estimated_execution_duration = new stdClass(); -// $tlCfg->testcase_cfg->estimated_execution_duration->required = 'required'; -$tlCfg->testcase_cfg->estimated_execution_duration->required = ''; - - - -// There are some preconfigured standard types which you can use, -// additionally you can configure your own types. -$tlCfg->testcase_cfg->relations = new stdClass(); -$tlCfg->testcase_cfg->relations->enable = TRUE; -$tlCfg->testcase_cfg->relations->interproject_linking = FALSE; - - -/** - * Localization identifiers for test cases relation types - * Types, which are configured above, have to be configured - * here too with attributes "source" and "destination". - * - * Last value will be selected in GUI as default. - * - * Form has to be like this: - * - * ... = array( - * RELATIONNAME => array( - * 'source' => 'SOURCE_LOCALIZATION_KEY', - * 'destination' => 'DESTINATION_LOCALIZATION_KEY'), - * ... - * - * @since TestLink 1.9.12 - **/ - -$tlCfg->testcase_cfg->relations->type_labels = array( - TL_REL_TYPE_PARENT_CHILD => array('source' => 'parent_of','destination' => 'child_of'), - TL_REL_TYPE_BLOCKS_DEPENDS => array('source' => 'blocks','destination' => 'depends'), - TL_REL_TYPE_RELATED => array('source' => 'related_to','destination' => 'related_to') -); - - - -$tlCfg->testcase_cfg->relations->type_description = array(TL_REL_TYPE_PARENT_CHILD => 'parent_child', - TL_REL_TYPE_BLOCKS_DEPENDS => 'blocks_depends', - TL_REL_TYPE_RELATED => 'related_to'); - - - - -// @since 1.9.18 -// TRUE => After a test case version has been executed -// attachment on test case spec can not be added/removed -// -// FALSE -// -// This means that at GUI Level, will not be possible: -// add a new attachment to an Executed Test Case Version -// delete an attachment from Executed Test Case Version -$tlCfg->testcase_cfg->downloadOnlyAfterExec = TRUE; - -// This means that at GUI Level, will not be possible: -// add a new req version link to an Executed Test Case Version -// delete a req version link from Executed Test Case Version -$tlCfg->testcase_cfg->reqLinkingDisabledAfterExec = TRUE; - -// Effects on Linked Requirements Version after -// execution of a Test Case Version -$tlCfg->testcase_cfg->freezeReqVersionAfterExec = TRUE; - - -// Effects on TCVersion N when TCVersion N+1 is created -$tlCfg->testcase_cfg->freezeTCVersionOnNewTCVersion = TRUE; -$tlCfg->testcase_cfg->freezeTCVRelationsOnNewTCVersion = TRUE; - -// Because: -// The Relation must be frozen (cannot be deleted) when -// a new version of the test case is created. -// -// It seems confusing that relation can be added, then -// this new configuration will allow this operation -// only on latest test case version -// -$tlCfg->testcase_cfg->addTCVRelationsOnlyOnLatestTCVersion = TRUE; - - -// Not Already Implemented -//$tlCfg->testcase_cfg->allowAddTCVRelationsOnOldTCVersion = TRUE; - -//$tlCfg->testcase_cfg->frozenNotExecutedTCVDelAttachtments = FALSE; -//$tlCfg->testcase_cfg->frozenNotExecutedTCVAddAttachtments = FALSE; -//$tlCfg->testcase_cfg->frozenNotExecutedTCVAddTCVRel = FALSE; -//$tlCfg->testcase_cfg->frozenNotExecutedTCVDelTCVRel = FALSE; -//$tlCfg->testcase_cfg->frozenNotExecutedTCVAddREQVLink = FALSE; -//$tlCfg->testcase_cfg->frozenNotExecutedTCVDelREQVLink = FALSE; - - -// Effects on Req Version to TCVersion LINK -// when a new version of a linked Test Case is created -// If LINK is frozen, then this means that link can not be deleted. -// $tlCfg->reqTCLinks->freezeLinkOnNewTCVersion = FALSE; -// -// Important Notice: -// Req Version to TCVersion Link can be done ONLY TO LATEST TCV. -// -// This means that : -// -// on GUI on the Requirements Area on TEST CASE Specification Feature: -// this option has NO EFFECT -// -// on GUI on the Coverage Area on REQUIREMENT Specification Feature: -// this option has EFFECT -// -// on GUI on the Assign Requirements Feature: -// this option has EFFECT -// -$tlCfg->reqTCLinks->freezeLinkOnNewTCVersion = TRUE; - -// Effects on Req Version to TCVersion LINK -// when a new version of a linked Req Version is created -$tlCfg->reqTCLinks->freezeLinkOnNewREQVersion = TRUE; - - -// Effects on BOTH ends of Req Version to TCVersion LINK -// when a new version of a linked TC Version is created -$tlCfg->reqTCLinks->freezeBothEndsOnNewTCVersion = TRUE; - -// Effects on BOTH ends of Req Version to TCVersion LINK -// when a new version of a linked REQ Version is created -$tlCfg->reqTCLinks->freezeBothEndsOnNewREQVersion = TRUE; - - -// Effects on REQ Version N when REQ Version N+1 is created -$tlCfg->req_cfg->freezeREQVersionOnNewREQVersion = TRUE; - - -/** text template for a new items: - Test Case: summary, steps, expected_results, preconditions - -*/ -// object members has SAME NAME that FCK editor objects. -// the logic present on tcEdit.php is dependent of this rule. -// every text object contains an object with following members: type and value -// -// Possible values for type member: -// none: template will not be used, default will be a clean editor screen. -// -// string: value of value member is assigned to FCK object -// string_id: value member is used in a lang_get() call, and return value -// is assigned to FCK object. Configure string_id on custom_strings.txt -// value: value member is used as file name. -// file is readed and it's contains assigned to FCK object -// -// any other value for type, results on '' assigned to FCK object - -$tlCfg->testcase_template = new stdClass(); - -$tlCfg->testcase_template->summary = new stdClass(); -$tlCfg->testcase_template->summary->type = 'none'; -$tlCfg->testcase_template->summary->value = ''; - -$tlCfg->testcase_template->steps = new stdClass(); -$tlCfg->testcase_template->steps->type = 'none'; -$tlCfg->testcase_template->steps->value = ''; - -$tlCfg->testcase_template->expected_results = new stdClass(); -$tlCfg->testcase_template->expected_results->type = 'none'; -$tlCfg->testcase_template->expected_results->value = ''; - -$tlCfg->testcase_template->preconditions = new stdClass(); -$tlCfg->testcase_template->preconditions->type = 'none'; -$tlCfg->testcase_template->preconditions->value = ''; - - -/** text template for a new Test Suite description */ -$tlCfg->testsuite_template = new stdClass(); -$tlCfg->testsuite_template->details = new stdClass(); -$tlCfg->testsuite_template->details->type = 'none'; -$tlCfg->testsuite_template->details->value = ''; - -$tlCfg->project_template = new stdClass(); -$tlCfg->project_template->notes = new stdClass(); -$tlCfg->project_template->notes->type = 'none'; -$tlCfg->project_template->notes->value = ''; - -$tlCfg->testplan_template = new stdClass(); -$tlCfg->testplan_template->notes = new stdClass(); -$tlCfg->testplan_template->notes->type = 'none'; -$tlCfg->testplan_template->notes->value = ''; - -$tlCfg->execution_template = new stdClass(); -$tlCfg->execution_template->bulk_exec_notes = new stdClass(); -$tlCfg->execution_template->bulk_exec_notes->type = 'none'; -$tlCfg->execution_template->bulk_exec_notes->value = ''; - -$tlCfg->execution_template->notes = new stdClass(); -$tlCfg->execution_template->notes->type = 'none'; -$tlCfg->execution_template->notes->value = ''; - -$tlCfg->build_template = new stdClass(); -$tlCfg->build_template->notes = new stdClass(); -$tlCfg->build_template->notes->type = 'none'; -$tlCfg->build_template->notes->value = ''; - -$tlCfg->requirement_template = new stdClass(); -$tlCfg->requirement_template->scope = new stdClass(); -$tlCfg->requirement_template->scope->type = 'none'; -$tlCfg->requirement_template->scope->value = ''; - -$tlCfg->req_spec_template = new stdClass(); -$tlCfg->req_spec_template->scope = new stdClass(); -$tlCfg->req_spec_template->scope->type = 'none'; -$tlCfg->req_spec_template->scope->value = ''; - -$tlCfg->role_template = new stdClass(); -$tlCfg->role_template->notes = new stdClass(); -$tlCfg->role_template->notes->type = 'none'; -$tlCfg->role_template->notes->value = ''; - - -$tlCfg->platform_template = new stdClass(); -$tlCfg->platform_template->notes = new stdClass(); -$tlCfg->platform_template->notes->type = 'none'; -$tlCfg->platform_template->notes->value = ''; - -// ---------------------------------------------------------------------------- -/* [ATTACHMENTS] */ - -/** Attachment feature availability */ -$tlCfg->attachments = new stdClass(); -$tlCfg->attachments->enabled = TRUE; - -// TRUE -> when you upload a file you can give no title -$tlCfg->attachments->allow_empty_title = TRUE; - -// $tlCfg->attachments->allow_empty_title == TRUE, you can ask the system -// to do something -// -// 'none' -> just write on db an empty title -// 'use_filename' -> use filename as title -//$tlCfg->attachments->action_on_save_empty_title='use_filename'; -// -$tlCfg->attachments->action_on_save_empty_title = 'none'; - -// Remember that title is used as link description for download -// then if title is empty, what the system has to do when displaying ? -// 'show_icon' -> the $tlCfg->attachments->access_icon will be used. -// 'show_label' -> the value of $tlCfg->attachments->access_string will be used . -$tlCfg->attachments->action_on_display_empty_title = 'show_icon'; - -// Set display order of uploaded files -$tlCfg->attachments->order_by = " ORDER BY date_added DESC "; - - -// need to be moved AFTER include of custom_config -// -// $tlCfg->attachments->access_icon = ''; -$tlCfg->attachments->access_string = "[*]"; - -/** - * Files that are allowed. Separate items by commas. - * eg. 'doc,xls,gif,png,jpg' - */ -$tlCfg->attachments->allowed_files = 'doc,xls,gif,png,jpg,xlsx,csv'; - - -/** - * Process filename against XSS - * Thanks to http://owasp.org/index.php/Unrestricted_File_Upload - * '/^[a-zA-Z0-9]{1,20}\.[a-zA-Z0-9]{1,10}$/'; - * added - and _. - * - * NO CHECK if -> $g_attachments->allowed_filenames_regexp = ''; - * - */ -$tlCfg->attachments->allowed_filenames_regexp = '/^[a-zA-Z0-9_-]{1,20}\.[a-zA-Z0-9]{1,10}$/'; - - -/** the type of the repository can be database or filesystem - * TL_REPOSITORY_TYPE_DB => database - * TL_REPOSITORY_TYPE_FS => filesystem - **/ -$g_repositoryType = TL_REPOSITORY_TYPE_FS; - -/** - * TL_REPOSITORY_TYPE_FS: the where the filesystem repository should be located - * We recommend to change the directory for security reason. - * (see http://itsecuritysolutions.org/2012-08-13-TestLink-1.9.3-multiple-vulnerabilities/) - * Put it out of reach via web or configure access denied. - * - **/ -$g_repositoryPath = '/var/testlink/upload_area/'; /* unix example */ -if (($upa = getenv('TESTLINK_UPLOAD_AREA'))) { - $g_repositoryPath = trim($upa); -} - -/** - * compression used within the repository - * TL_REPOSITORY_COMPRESSIONTYPE_NONE => no compression - * TL_REPOSITORY_COMPRESSIONTYPE_GZIP => gzip compression - */ -$g_repositoryCompressionType = TL_REPOSITORY_COMPRESSIONTYPE_NONE; - -// the maximum allowed file size for each repository entry, default 1MB. -// Also check your PHP settings (default is usually 2MBs) -$tlCfg->repository_max_filesize = 1; //MB - - - - -// ---------------------------------------------------------------------------- -/* [Requirements] */ - -// HAS TO BE REMOVED - > req_doc_id UNIQUE INSIDE test project -// true : you want req_doc_id UNIQUE IN THE WHOLE DB (system_wide) -// false: you want req_doc_id UNIQUE INSIDE a SRS -// $tlCfg->req_cfg->reqdoc_id->is_system_wide = FALSE; - -$tlCfg->req_cfg->monitor_enabled = true; - -// truncate log message to this amount of chars for reqCompareVersions -$tlCfg->req_cfg->log_message_len = 200; - -/** - * Test Case generation from Requirements - use_req_spec_as_testsuite_name - * FALSE => test cases are created and assigned to a test suite - * with name $tlCfg->req_cfg->default_testsuite_name - * TRUE => REQuirement Specification Title is used as testsuite name - */ -$tlCfg->req_cfg->use_req_spec_as_testsuite_name = TRUE; -$tlCfg->req_cfg->default_testsuite_name = "Auto-created Test cases"; -$tlCfg->req_cfg->testsuite_details = "Test Cases in the Test Suite are generated from Requirements. " . - "A refinement of test scenario is highly recommended."; -$tlCfg->req_cfg->testcase_summary_prefix = "The Test Case was generated from the assigned requirement.
"; - - -// If the following value is enabled, then the summary prefix string will include the -// title and version number of the assigned requirement. -$tlCfg->req_cfg->use_testcase_summary_prefix_with_title_and_version = ENABLED; - -// If above option is enabled, the following string will be used as a template for the tc summary prefix. -// It has to include four variables in the form of "%s". The first and second one will be used internally by the system. -// The third one will then be replaced by the title of the originating Requirement, -// the fourth one by its version number. -// Attention: If there aren't exactly three variables in it, the operation will fail. -$tlCfg->req_cfg->testcase_summary_prefix_with_title_and_version = "The Test Case was generated from the assigned" . - " requirement
\"%s\" (version %s).
"; - -/** - * ENABLED: When generating Test Cases from Requirements, copy the scope of the Requirement - * to the summary of the newly created Test Case. - */ -$tlCfg->req_cfg->copy_req_scope_to_tc_summary = DISABLED; - - -// To avoid perfomance problems on search Requirements feature, -// we can decide when to inform user that results can not be displayed -// due to too many results. -$tlCfg->req_cfg->search=new stdClass(); -$tlCfg->req_cfg->search->max_qty_for_display=200; - - -// ENABLED: allow N level depth tree -// DISABLED: just one level -$tlCfg->req_cfg->child_requirements_mgmt = ENABLED; - - -// ENABLED: ask for this value on user interface and use on several features -// DISABLED: do not ask, do not use -$tlCfg->req_cfg->expected_coverage_management = ENABLED; - -// Show Child Requirements on Requirement Specification Print View -// ENABLED: Requirement Specification including Child Requirements are shown -// DIABLED: ONLY Requirement Specification is shown -$tlCfg->req_cfg->show_child_reqs_on_reqspec_print_view = DISABLED; - -// -// Order of test cases status in this array, is used to undestand -// to what status set requirement in the requirements report. -// Standard algorithm, present in getReqCoverage(), is: -// -// if at least one of Test Cases linked to Requirement has status FAILED -// Requirement Coverage Status = FAILED -// else if at least one of Test Cases linked to Requirement has status BLOCKED -// Requirement Coverage Status = BLOCKED -// else if ALL Test Cases linked to Requirement has status NOT RUN -// Requirement Coverage Status = NOT RUN -// else if ALL Test Cases linked to Requirement has status PASSED -// Requirement Coverage Status = PASSED -// else -// Requirement Coverage Status = Partially Passed -// -// This logic is implemented using following config parameter -$tlCfg->req_cfg->coverageStatusAlgorithm['checkOrder']=array('atLeastOne','all'); -$tlCfg->req_cfg->coverageStatusAlgorithm['checkType']['atLeastOne']=array('failed','blocked'); -$tlCfg->req_cfg->coverageStatusAlgorithm['checkType']['all']=array('passed'); - -// Configure here what status has to get requirement when check of type 'all' fails like -// in following situation (Mantis 2171) -// -// If particular requirement has assigned more than one test cases, and: -// - at least one of assigned test cases was not yet executed -// - the rest of assigned test cases was executed and passed -// then on the "Requirements based report" this particular requirement is not shown at all (in any section). -// -// $tlCfg->req_cfg->coverageStatusAlgorithm['checkFail']['all']='partially_passed'; -// $tlCfg->req_cfg->coverageStatusAlgorithm['displayOrder']=array('passed','failed', -// 'blocked','not_run','partially_passed'); -// 20100819 - asimon - fix not needed anymore after rewrite of req based report -//$tlCfg->req_cfg->coverageStatusAlgorithm['checkFail']['all']='failed'; -//$tlCfg->req_cfg->coverageStatusAlgorithm['displayOrder']=array('passed','failed','blocked','not_run'); - - - - -// truncate log message to this amount of chars for reqSpecCompareRevisions -$tlCfg->req_spec_cfg->log_message_len = 200; - - -// Linking between requirements/requirement specifications -// -$tlCfg->internal_links = new stdClass(); - -// -// TRUE: links to other requirements/requirement specifications are -// automatically generated from the corresponding Document ID, enclosed by tags (like BBCode). -// -// Usage: -// link to requirements: [req]req_doc_id[/req] -// link to requirement specifications: [req_spec]req_spec_doc_id[/req_spec] -// -// The test project of the requirement / requirement specification and an anchor -// to jump to can also be specified: -// [req tproj= anchor=]req_doc_id[/req] -// This syntax also works for requirement specifications. -// -// FALSE: no links are automatically created. -// -$tlCfg->internal_links->enable = TRUE; - -// how a linked document (requirement/requirement specification) should be displayed. -// posible values: -// 'window': new window/tab will be used (depending on browser configuration) -// 'frame' : same frame as the clicked link -// 'popup' (default): popup window (ATTENTION to browser pop-up block). -// -$tlCfg->internal_links->target = 'popup'; - -// title for automatically created link -// possible values: -// 'string': lang_get() will be used to localize -// 'none': no title will be generated, only link with ID -// 'item' (default): localized type of item (e.g. "Requirement: ", "Req Spec") -// will be used as title for the generated link -// -$tlCfg->internal_links->req_link_title = new stdClass(); -$tlCfg->internal_links->req_link_title->type = 'item'; -$tlCfg->internal_links->req_link_title->value = ''; - -$tlCfg->internal_links->req_spec_link_title = new stdClass(); -$tlCfg->internal_links->req_spec_link_title->type = 'item'; -$tlCfg->internal_links->req_spec_link_title->value = ''; - - -// Relations between requirement documents: -// -// The relation types have to be configured in cfg/const.inc.php -// and their respective localization values in locale strings.txt. - -// There are some preconfigured standard types which you can use, -// additionally you can configure your own types. -$tlCfg->req_cfg->relations = new stdClass(); -$tlCfg->req_cfg->relations->enable = TRUE; -$tlCfg->req_cfg->relations->interproject_linking = FALSE; - -// Requirement/testcase diff -// default value of lines to show before and after each difference -$tlCfg->diffEngine->context = 5; - - -// Configuration for Requirement Import using DOCBOOK format -$tlCfg->req_cfg->importDocBook = new stdClass(); -$tlCfg->req_cfg->importDocBook->requirement= "sect3"; -$tlCfg->req_cfg->importDocBook->title= "title"; -$tlCfg->req_cfg->importDocBook->paragraph= "para"; -$tlCfg->req_cfg->importDocBook->ordered_list="orderedlist"; -$tlCfg->req_cfg->importDocBook->list_item="listitem"; -$tlCfg->req_cfg->importDocBook->table="informaltable"; -$tlCfg->req_cfg->importDocBook->table_group="tgroup"; -$tlCfg->req_cfg->importDocBook->table_head="thead"; -$tlCfg->req_cfg->importDocBook->table_body="tbody"; -$tlCfg->req_cfg->importDocBook->table_row="row"; -$tlCfg->req_cfg->importDocBook->table_entry="entry"; -$tlCfg->req_cfg->importDocBook->list_item_children = array('para','title'); -$tlCfg->req_cfg->importDocBook->table_entry_children = array('para'); - - -// If an external tool is used for requirement management, enable this setting. -// You will get an additional field on requirement specifications where you -// can enter the total count of requirements so that external requirements -// are also counted for metrics/statistics. -$tlCfg->req_cfg->external_req_management = DISABLED; - - -//If enabled an icon next to Document ID field will show up that allows -//to insert the last defined Requirement Document ID within the same Project -//into Document ID field -$tlCfg->req_cfg->allow_insertion_of_last_doc_id = DISABLED; - - -// used ONLY to configure the mask (text) . -// algorithm type is fixed HARDCODED -// -$tlCfg->req_cfg->duplicated_name_algorithm = new stdClass(); -$tlCfg->req_cfg->duplicated_name_algorithm->text = " (%s)"; - -$tlCfg->req_cfg->duplicated_docid_algorithm = new stdClass(); -$tlCfg->req_cfg->duplicated_docid_algorithm->text = " (%s)"; - - -// ---------------------------------------------------------------------------- -/* [TREE FILTER CONFIGURATION] */ - -/* In the following section, you can configure which filters shall be used - * in combination with the tree view for testcases and requirements. - * There are five available filter modes (four for the testcase tree, - * one for requirement documents), which are used for the different features - * as listed here: - * - * For Test Cases: - * --> execution_mode - * execution of testcases - * --> edit_mode - * create and edit testcases - * assign keywords to testcases - * assign requirements to testcases - * --> plan_mode - * assign testcase execution to users - * update linked versions for testplan - * set urgent testcases - * --> plan_add_mode - * add/remove testcases to/from testplan - * - * For Requirements: - * There are no modes defined, there is only one filter configuration. - * The filters configured here will be used for requirement editing. - */ -$tlCfg->tree_filter_cfg = new stdClass(); -$tlCfg->tree_filter_cfg->testcases = new stdClass(); -$tlCfg->tree_filter_cfg->requirements = new stdClass(); - -// These are the available filter modes for testcases: -$tlCfg->tree_filter_cfg->testcases->execution_mode = new stdClass(); -$tlCfg->tree_filter_cfg->testcases->edit_mode = new stdClass(); -$tlCfg->tree_filter_cfg->testcases->plan_mode = new stdClass(); -$tlCfg->tree_filter_cfg->testcases->plan_add_mode = new stdClass(); - -// If you disable one of these items here, you switch -// the complete filter panel off for a specific mode/feature. -// You should rather do this here instead of individually disabling all the filters, -// if you don't want to have any filters at all for a given feature. -$tlCfg->tree_filter_cfg->testcases->execution_mode->show_filters = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->show_filters = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->show_filters = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->show_filters = ENABLED; -$tlCfg->tree_filter_cfg->requirements->show_filters = ENABLED; - -// Detailed settings for each mode. -// Here, the single filter fields can be disabled if not wanted. -// Also, the choice of advanced filter mode can be disabled. -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_tc_id = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_testcase_name = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_toplevel_testsuite = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_keywords = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_priority = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_execution_type = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_assigned_user = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_custom_fields = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_result = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_bugs = ENABLED; -$tlCfg->tree_filter_cfg->testcases->execution_mode->advanced_filter_mode_choice = ENABLED; - -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_tc_id = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_testcase_name = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_toplevel_testsuite = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_keywords = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_platforms = ENABLED; - -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_active_inactive = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_importance = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_execution_type = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_custom_fields = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_workflow_status = ENABLED; -$tlCfg->tree_filter_cfg->testcases->edit_mode->advanced_filter_mode_choice = ENABLED; - -$tlCfg->tree_filter_cfg->testcases->edit_mode - ->filter_workflow_status_values = array(); - -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_tc_id = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_testcase_name = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_toplevel_testsuite = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_keywords = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_priority = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_execution_type = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_assigned_user = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_custom_fields = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_result = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->advanced_filter_mode_choice = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_mode->setting_build_inactive_out = FALSE; -$tlCfg->tree_filter_cfg->testcases->plan_mode->setting_build_close_out = FALSE; - - -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_tc_id = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_testcase_name = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_toplevel_testsuite = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_keywords = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_active_inactive = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_importance = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_execution_type = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_workflow_status = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_custom_fields = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->advanced_filter_mode_choice = ENABLED; -$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_platforms = ENABLED; - - -$tlCfg->tree_filter_cfg->requirements->filter_doc_id = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_title = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_status = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_type = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_spec_type = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_coverage = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_relation = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_tc_id = ENABLED; -$tlCfg->tree_filter_cfg->requirements->filter_custom_fields = ENABLED; -$tlCfg->tree_filter_cfg->requirements->advanced_filter_mode_choice = ENABLED; - -// ENABLED -> Every time the user does some operation on requirement spec, -// the tree will be updated on screen. [DEFAULT] -// DISABLED -> The tree will not be updated automatically, but the user can update it manually. -// On graphical user interface any user will is able to change this setting. -$tlCfg->tree_filter_cfg->requirements->automatic_tree_refresh = ENABLED; - - - -/* [Assign test cases to test plan] */ -$tlCfg->tplanDesign->hideTestCaseWithStatusIn = array($tlCfg->testCaseStatus['obsolete'] => 'obsolete', - $tlCfg->testCaseStatus['future'] => 'future' ); - -// ---------------------------------------------------------------------------- -/* [MISC FUNCTIONALITY] */ - -/** Maximum uploadfile size to importing stuff in TL */ -// Also check your PHP settings (default is usually 2MBs) -// unit BYTES is required by MAX_FILE_SIZE HTML option -$tlCfg->import_file_max_size_bytes = '409600'; - -/** Maximum line size of the imported file */ -$tlCfg->import_max_row = '10000'; // in chars - -/** Set the default role used for new users */ -// - created from the login page. -// - created using user management features -$tlCfg->default_roleid = TL_ROLES_GUEST; - -// only show custom fields if their value isn't empty -$tlCfg->custom_fields->show_custom_fields_without_value = true; - -/** used to check size in char for TEXT AREA type custom fields */ -// can not be greater that column definition on DB -// 0 => do not check. -$tlCfg->custom_fields->max_length = 255; - -// sizes for HTML INPUTS -// for list, multiselection list -// - MAXIMUM number of items displayed at once -// -// for checkbox,radio is useless -// Hint: more than 120 produce weird effects on user interface -// -$tlCfg->custom_fields->sizes = array('string' => 100, - 'numeric' => 10, - 'float' => 10, - 'email' => 100, - 'list' => 1, - 'multiselection list' => 5, - 'text area' => array('rows' => 6, 'cols' => 80), - 'script' => 100, - 'server' => 100); - -// Use this variable (on custom_config.inc.php) to define new Custom Field types. -// IMPORTANT: -// check $custom_field_types property on cfield_mgr.class.php -// to avoid overwrite of standard types. -// -$tlCfg->gui->custom_fields->types = null; - -// Use this variable (on custom_config.inc.php) -// to define possible values behaviour for new Custom Field types. -// -// IMPORTANT: -// check $possible_values_cfg property on cfield_mgr.class.php -// to avoid overwrite of standard values. -// -$tlCfg->gui->custom_fields->possible_values_cfg = null; - - -/** - * Check unique titles of Test Project, Test Suite and Test Case - * ENABLED => Check [STANDARD BEHAVIOUR] - * DISABLED => don't check - **/ -$tlCfg->check_names_for_duplicates = ENABLED; - -/** - * Action for duplication check (only if check_names_for_duplicates=ENABLED) - * 'allow_repeat' => allow the name to be repeated (backward compatibility) - * 'generate_new' => generate a new name using $g_prefix_name_for_copy - * 'block' => return with an error - **/ -$tlCfg->action_on_duplicate_name = 'generate_new'; - -/** - * String checking and conversions - * Allow automatically convert www URLs and email adresses into clickable links - * used by function string_display_links() for example by custom fields. - * Valid values = ENABLED/DISABLED. - **/ -$tlCfg->html_make_links = ENABLED; - -/** - * Define the valid html tags for "content driven" single-line and multi-line fields. - * Do NOT include tags with parameters (eg. ), img and href. - * It's used by function string_display_links() for example by custom fields. - */ -$tlCfg->html_valid_tags = 'p, li, ul, ol, br, pre, i, b, u, em'; -$tlCfg->html_valid_tags_single_line = 'i, b, u, em'; - -/** - * Defines the threshold values for filtering TC by a priority according to the formula - * LOW => all Tc's with (urgency*importance) < LOW_Threshold - * HIGH => all Tc's with (urgency*importance) >= HIGH_Threshold - * MEDIUM => all Tc's with (urgency*importance) >= LOW_Threshold AND (urgency*importance) < HIGH_Threshold - */ -$tlCfg->urgencyImportance = new stdClass(); -$tlCfg->urgencyImportance->threshold['low'] = 3; -$tlCfg->urgencyImportance->threshold['high'] = 6; - -/** - * @var boolean Demo mode disables some functionality - * user edit disable - * role create ENABLED - * user create ENABLED - * special users manage DISABLE - */ -$tlCfg->demoMode = OFF; -$tlCfg->demoSpecialUsers = array('admin'); - -/** - * If enabled, every Ext JS table in TestLink will offer an export button, - * which generates a file with the contents of the table. - * ATTENTION: This feature is fully experimental. Enable at your own risk! - * Enabling it can cause broken tables. - */ -$tlCfg->enableTableExportButton = DISABLED; - - -/** - * Taken from Mantis to implement better login security - * and solve TICKET 4342. - */ -$tlCfg->auth_cookie = $tlCfg->cookie->prefix . - "TESTLINK_USER_AUTH_COOKIE"; - -/** -Used when creating a Test Suite using copy -and you have choose $g_action_on_duplicate_name = 'generate_new' -if the name exist. -*/ -$g_prefix_name_for_copy = strftime("%Y%m%d-%H:%M:%S", time()); - - - -/** - * Configurable templates this can help if you want to use a non standard template. - * i.e. you want to develop a new one without loosing the original template. - * key: original TL template name WITHOUT extension - * value: whatever name you want, only constrain you have to copy your template - * ON SAME FOLDER where original template is. - * See example below - */ -$g_tpl = array('inc_exec_controls' => 'inc_exec_img_controls.tpl'); -//$g_tpl = array('inc_exec_controls' => 'inc_exec_controls.tpl'); -$g_tpl['login'] = 'login/login-model-marcobiedermann.tpl'; - -// Example -// $g_tpl = array('tcView' => 'custom_tcView.tpl', -// 'tcSearchView' => 'myOwnTCSearchView.tpl', -// 'tcEdit' => 'tcEdit_ultraCool.tpl'); - -/** Add o replace images */ -$tlCfg->images = array(); - - - -// ---------------------------------------------------------------------------- -/* [PROXY] */ -/* Used only */ -/* mantissoapInterface.class.php */ -/* jirasoapInterface.class.php */ -/* jirarestInterface.class.php */ -$tlCfg->proxy->host = null; -$tlCfg->proxy->port = null; -$tlCfg->proxy->login = null; -$tlCfg->proxy->password = null; - - -/** Plugins feature */ -define('TL_PLUGIN_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR); - -// ----- End of Config ------------------------------------------------------------------ -// -------------------------------------------------------------------------------------- -// DO NOT DO CHANGES BELOW -// -------------------------------------------------------------------------------------- - -/** Functions for check request status */ -require_once('configCheck.php'); - - -if( !defined('TL_JQUERY') ) -{ - define('TL_JQUERY','jquery-2.2.4.min.js' ); -} - -if( !defined('TL_DATATABLES_DIR') ) -{ - define('TL_DATATABLES_DIR','DataTables-1.10.4' ); -} - -/** root of testlink directory location seen through the web server */ -/* 20070106 - franciscom - this statement it's not 100% right - better use $_SESSION['basehref'] in the scripts. */ -define('TL_BASE_HREF', get_home_url(array('force_https' => $tlCfg->force_https))); - -clearstatcache(); -if ( file_exists( TL_ABS_PATH . 'custom_config.inc.php' ) ) -{ - require_once( TL_ABS_PATH . 'custom_config.inc.php' ); -} - - -if( !isset($tlCfg->attachments->access_icon) ) { - $tlCfg->attachments->access_icon = - ''; -} - - -// Important to do this only after custom_* to use (if exists) redefinition of -// $tlCfg->results['status_label_for_exec_ui'] -$tlCfg->reportsCfg->exec_status = $tlCfg->results['status_label_for_exec_ui']; - - -/** Support for localization */ -// @TODO move the code out of config and do it only once and -// not always in any include! -// @TODO a better parsing function should be include -$serverLanguage = false; -if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) -{ - @list($code) = explode(",",$_SERVER['HTTP_ACCEPT_LANGUAGE']); - @list($a,$b) = explode("-",$code); - if ($a && $b) - { - $a = strtolower($a); - $b = strtoupper($a); - $serverLanguage = $a."_".$b; - } -} - -if(false !== $serverLanguage) -{ - if (array_key_exists($serverLanguage,$tlCfg->locales)) - { - $tlCfg->default_language = $serverLanguage; - } -} -define ('TL_DEFAULT_LOCALE', $tlCfg->default_language); - -// Reverted execution status is used for two applications. -// 1. To map code to CSS, Please if you add an status you need to add a corresponding CSS Class -// in the CSS files (see the gui directory) -// 2. to decode from code to some more human oriented to use in code -// -/** Revered list of Test Case execution results */ -$tlCfg->results['code_status'] = array_flip($tlCfg->results['status_code']); - -// Enable CSRF global protection -$tlCfg->csrf_filter_enabled = TRUE; - -// --------------------------------------------------------------------------------- -/** Converted and derived variables (Users should not modify this section) */ -define('REFRESH_SPEC_TREE',$tlCfg->spec_cfg->automatic_tree_refresh ? 1 : 0); -define('TL_SORT_TABLE_ENGINE',$g_sort_table_engine); -define("TL_REPOSITORY_MAXFILESIZE", 1024*1024*$tlCfg->repository_max_filesize); - -define('TL_XMLEXPORT_HEADER', "charset . "\"?>\n"); - - - -// --------------------------------------------------------------------------------- -// when a role is deleted, a new role must be assigned to all users -// having role to be deleted -// A right choice seems to be using $g_default_roleid. -// You can change this adding a config line in custom_config.inc.php -// @TODO martin: remove - use directly $tlCfg->default_roleid; -$g_role_replace_for_deleted_roles = $tlCfg->default_roleid; - - -/** - * Using "|" in the testsuite name causes malformed URLs - * regexp used to check for chars not allowed in: - * test project, test suite and testcase names. - * @TODO martin: encode harm characters @see http://cz.php.net/urlencode (and remove the parameter) - * - * now is preg pattern - */ -$g_ereg_forbidden = "/[|]/i"; - - - -/** - * @TODO remove from TL - unfinished refactorization; - * use $tlCfg instead of old variables and constants - */ -define('TL_IMPORT_ROW_MAX', $tlCfg->import_max_row); -define('TL_TPL_CHARSET', $tlCfg->charset); -define('TITLE_SEP',$tlCfg->gui_title_separator_1); -define('TITLE_SEP_TYPE2',$tlCfg->gui_title_separator_2); -define('TITLE_SEP_TYPE3',$tlCfg->gui_title_separator_2); // obsolete: use type 1,2 -define('TL_FRMWORKAREA_LEFT_FRAME_WIDTH', $tlCfg->frame_workarea_default_width); -define('TL_TEMP_PATH', $tlCfg->temp_dir); - -$tlCfg->gui->title_separator_1 = $tlCfg->gui_title_separator_1; -$tlCfg->gui->title_separator_2 = $tlCfg->gui_title_separator_2; -$tlCfg->gui->role_separator_open = $tlCfg->gui_separator_open; -$tlCfg->gui->role_separator_close = $tlCfg->gui_separator_close; - -$tlCfg->gui->version_separator_open = $tlCfg->gui_separator_open; -$tlCfg->gui->version_separator_close = $tlCfg->gui_separator_close; - - -/** - * Globals for Events storage - */ -$g_event_cache = array(); - -/** - * Globals for Plugins - */ +api = new stdClass(); +$tlCfg->cookie = new stdClass(); +$tlCfg->document_generator = new stdClass(); + +$tlCfg->spec_cfg = new stdClass(); + +$tlCfg->exec_cfg = new stdClass(); +$tlCfg->exec_cfg->view_mode = new stdClass(); +$tlCfg->exec_cfg->exec_mode = new stdClass(); + +$tlCfg->UDFStripHTMLTags = true; + +// allow to define additional execution types other than +// defined in testcase.class.php +// array(code => lblkey) +// code int value > latest standard execution code defined. +// lblkey => key to be used on lang_get() call. +// +$tlCfg->custom_execution_types = null; + +$tlCfg->gui = new stdClass(); +$tlCfg->gui->custom_fields = new stdClass(); +$tlCfg->testcase_cfg = new stdClass(); +$tlCfg->req_cfg = new stdClass(); +$tlCfg->validation_cfg = new stdClass(); +$tlCfg->custom_fields = new stdClass(); +$tlCfg->req_spec_cfg = new stdClass(); +$tlCfg->diffEngine = new stdClass(); +$tlCfg->tplanDesign = new stdClass(); + +$tlCfg->notifications = new stdClass(); +$tlCfg->proxy = new stdClass(); + +$tlCfg->reqTCLinks = new stdClass(); + +$tlCfg->keywords = new stdClass(); + +$tlCfg->keywords->annotations = [ + "@TestCaseSpecDisplay:" +]; + +$tlCfg->keywords->onDeleteCheckFrozenTCVersions = true; +$tlCfg->keywords->onDeleteCheckExecutedTCVersions = true; + +// main key testproject PREFIX +// element array +// 'addTCLinkIntoITS' true => add note to Issue Tracker to issue with +// ISSUE ID similar to the KEYWORD (see kwPrefix below) +// +// 'kwPrefix' to remove from keyword to create the ISSUE ID +// +$tlCfg->keywords->byTestProject = array(); + +$tlCfg->keywords->headsUpTSuiteOnExec = 'CMD_OPEN_ON_EXEC'; + +$tlCfg->accessWithoutLogin = array(); + +$tlCfg->platforms = new stdClass(); +$tlCfg->platforms->allowedOnAssign = [ + 'enable_on_design' => false, + 'enable_on_execution' => true, + 'is_open' => true +]; + +/** + * + * @uses database access definition (generated automatically by TL installer) + */ +@include_once ('config_db.inc.php'); +if (! defined('DB_TABLE_PREFIX')) { + define('DB_TABLE_PREFIX', ''); +} + +/** + * The root dir for the testlink installation with trailing slash + */ +define('TL_ABS_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR); + +/** + * Just for documentation + */ +$tlCfg->testlinkdotorg = 'http://www.testlink.org'; + +/** + * GUI themes (base for CSS and images)- modify if you create own one + */ +$tlCfg->theme_dir = 'gui/themes/default/'; + +/** + * Dir for compiled templates + */ +$tlCfg->temp_dir = TL_ABS_PATH . 'gui' . DIRECTORY_SEPARATOR . 'templates_c' . + DIRECTORY_SEPARATOR; +if ($tpltmp = getenv('TESTLINK_TEMPLATES_C')) { + $tlCfg->temp_dir = trim($tpltmp); +} + +/** + * default filenames of CSS files of current GUI theme + */ +define('TL_CSS_MAIN', 'testlink.css'); +define('TL_CSS_PRINT', 'tl_print.css'); +define('TL_CSS_DOCUMENTS', 'tl_documents.css'); + +define('TL_THEME_BASE_DIR', $tlCfg->theme_dir); +define('TL_THEME_IMG_DIR', $tlCfg->theme_dir . 'images/'); +define('TL_THEME_CSS_DIR', $tlCfg->theme_dir . 'css/'); +define('TL_TESTLINK_CSS', TL_THEME_CSS_DIR . TL_CSS_MAIN); +define('TL_PRINT_CSS', TL_THEME_CSS_DIR . TL_CSS_PRINT); + +// name of your custom.css, place it in same folder that standard TL css +// null or '' => do not use +$tlCfg->custom_css = null; + +/** + * Include constants and magic numbers (users should not change it) + */ +require_once (TL_ABS_PATH . 'cfg' . DIRECTORY_SEPARATOR . 'const.inc.php'); + +// ---------------------------------------------------------------------------- +/** + * + * @var string used to have (when needed) a possibility to identify different TL instances + * @since 1.9.4 used on mail subject when mail logger is used + */ +$tlCfg->instance_name = 'Main TestLink Instance'; + +// do not use blanks or special characters, use a short string +$tlCfg->instance_id = 'TLM'; + +$tlCfg->gui->ux = 'tl-classic'; + +/** + * Copied from MantisBT + * + * Prefix for all TestLink cookies + * This should be an identifier which does not include spaces or periods, + * and should be unique per TestLink installation, especially if + * $tlCfg->cookie_path is not restricting the cookies' scope to the actual + * TestLink directory. + * + * @see $tlCfg->cookie->path + * @global string $tlCfg->cookie->prefix + */ +$tlCfg->cookie->prefix = 'TESTLINK1920'; + +/** + * + * @link http://php.net/function.setcookie + * + */ +$tlCfg->cookie->expire = (time() + 60 * 60 * 24 * 30); // 30 days; +$tlCfg->cookie->domain = ''; +$tlCfg->cookie->secure = false; +$tlCfg->cookie->httponly = false; + +$tlCfg->cookie->testProjectMemory = $tlCfg->cookie->prefix . '_PROJ_ID_USER_ID_'; + +/** + * Copied from MantisBT + * + * Specifies the path under which a cookie is visible + * All scripts in this directory and its sub-directories will be able + * to access TestLink cookies. + * It is recommended to set this to the actual TestLink path. + * + * @link http://php.net/function.setcookie + * @global string $tlCfg->cookie->path + */ +$tlCfg->cookie->path = '/'; + +/* [ROLE INHERITANCE] */ +/** + * possible values + * + * 'testproject' + * 'global' + * + * 'testproject' + * till a role is specifically assigned to test plan, test plan role + * will be inherited from test project role. + * + * IMPORTANT NOTICE + * test project role can be specifically assigned or inherited from + * user's global role. + * + * if test project specifically assigned role changes, and test plan role was inherited, then it will also changes, due to inheritance. + * + * + * 'global' + * till a role is specifically assigned to test plan, test plan role + * will be inherited from user's global role, and NOT from test project + * specifically assigned role. + * + * if test project specifically assigned role changes, will not be changed. + */ +$tlCfg->testplan_role_inheritance_mode = 'testproject'; + +/* [LOCALIZATION] */ + +/** @var string Default localization for users */ +// The value must be available in $$tlCfg->locales (see cfg/const.inc.php). +// Note: An attempt will be done to establish the default locale +// automatically using $_SERVER['HTTP_ACCEPT_LANGUAGE'] +$tlCfg->default_language = 'en_GB'; + +/** + * + * @var string Charset 'UTF-8' is only officially supported charset (Require + * MySQL version >= 4.1) 'ISO-8859-1' or another Charset could be set for + * backward compatability by experienced users. However we have not resources + * to support such patches. + */ +$tlCfg->charset = 'UTF-8'; + +/** + * + * @var string characters used to surround a description in the user interface + * (for example role) + */ +$tlCfg->gui_separator_open = '['; +$tlCfg->gui_separator_close = ']'; +$tlCfg->gui_room = '[ %s ]'; + +/** @var string Title separators are used when componing an title using several strings */ +$tlCfg->gui_title_separator_1 = ' : '; // object : name (Test Specification : My best product) +$tlCfg->gui_title_separator_2 = ' - '; // parent - child + +/** + * + * @var string delimiter used to create DOC ID in this way: + * . g_testcase_cfg->glue_character . + * Could not be empty + */ +$tlCfg->testcase_cfg->glue_character = '-'; + +$tlCfg->testcase_cfg->import = new stdClass(); +$tlCfg->testcase_cfg->import->wordwrap = new stdClass(); + +/* 0 => do not apply wordwrap() */ +$tlCfg->testcase_cfg->import->wordwrap->summary = 0; +$tlCfg->testcase_cfg->import->wordwrap->preconditions = 0; +$tlCfg->testcase_cfg->import->wordwrap->actions = 0; +$tlCfg->testcase_cfg->import->wordwrap->expected_results = 0; + +/** + * fonts set used to draw charts + */ +$tlCfg->charts_font_path = TL_ABS_PATH . + "vendor/wp-statistics/pchart/src/Fonts/tahoma.ttf"; +/** + * font size used to draw charts + */ +$tlCfg->charts_font_size = 8; + +// ---------------------------------------------------------------------------- +/* [SERVER ENVIRONMENT] */ + +/** + * TICKET 4969: Add Setting to Force HTTPS + */ +$tlCfg->force_https = false; + +/** + * + * @var integer Set the session timeout for inactivity [minutes]. + * Default high value disables this feature. + */ +$tlCfg->sessionInactivityTimeout = 9900; + +/** + * Set the session timeout value (in minutes). + * This will prevent sessions timing out after very short periods of time + * Warning: your server could block this settings + */ +// ini_set('session.cache_expire',900); + +/** + * Set the session garbage collection timeout value (in seconds) + * The default session garbage collection in php is set to 1440 seconds (24 minutes) + * If you want sessions to last longer this must be set to a higher value. + * You may need to set this in your global php.ini if the settings don't take effect. + */ +// ini_set('session.gc_maxlifetime', 60*90); + +$tlCfg->notifications->userSignUp = new stdClass(); +$tlCfg->notifications->userSignUp->enabled = true; // @see notifyGlobalAdmins() +$tlCfg->notifications->userSignUp->to = new stdClass(); +$tlCfg->notifications->userSignUp->to->roles = array( + TL_ROLES_ADMIN +); +$tlCfg->notifications->userSignUp->to->users = null; // i.e. array('login01','login02'); + +// ---------------------------------------------------------------------------- +/* [LOGGING] */ + +/** + * Error reporting - do we want php errors to show up for users + */ +/** + * configure on custom_config.inc.php + */ +/** + * error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING); + */ +/** + * error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING); + */ +error_reporting(E_ALL); + +/** + * + * @var string Default level of logging (NONE, ERROR, INFO, DEBUG, EXTENDED) + * is not used by tlLogger, we need to change this in future. + */ +$tlCfg->log_level = 'ERROR'; + +/** @var boolean show smarty debug window */ +$tlCfg->smarty_debug = false; + +/** + * + * @var string Path to store logs - + * for security reasons (see http://itsecuritysolutions.org/2012-08-13-TestLink-1.9.3-multiple-vulnerabilities/) + * put it out of reach via web or configure access denied. + */ +$tlCfg->log_path = '/var/testlink/logs/'; /* unix example */ +if ($lp = getenv('TESTLINK_LOG_PATH')) { + $tlCfg->log_path = trim($lp); +} + +/** + * + * @var string How to warning user when security weak points exists. + * + * 'SCREEN': messages will displayed on login screen, and tl desktop + * 'FILE': a file with a list is created but users are not notified via GUI + * user will receive a message on screen. (default) + * 'SILENT': same that FILE, but user will not receive message on screen. + */ +$tlCfg->config_check_warning_mode = 'FILE'; + +/** + * ONCE_FOR_SESSION + * ALWAYS + */ +$tlCfg->config_check_warning_frequence = 'ONCE_FOR_SESSION'; + +/** + */ +$tlCfg->userDocOnDesktop = OFF; + +/** + * Configure if individual logging data stores are enabled of disabled + * Possibile values to identify loggers: 'db','file' + * $g_loggerCfg=null; all loggers enabled + * $g_loggerCfg['db']['enable']=true/false; + * $g_loggerCfg['file']['enable']=true/false; + * $g_loggerCfg['mail']['enable']=true/false; + */ +$g_loggerCfg = array( + 'mail' => array( + 'enable' => false + ) +); + +/** @var integer All events older this value [days] are removed from the db, during login */ +$g_removeEventsOlderThan = 30; + +/** + * + * @var map keys: 'all' + values present on proprety of logger class $loggerTypeDomain + * values can be only these defined on logger.class.php + * @since 1.9.4 + * example array('all' => array('INFO','AUDIT'), + * 'mail' => array('ERROR')) + * + * $tlCfg->loggerFilter = array('db' => array('DEBUG','AUDIT','WARNING','ERROR'), + * 'file' => array('NONE')); + * + */ +$tlCfg->loggerFilter = null; // default defined on logger.class.php ; + +// ---------------------------------------------------------------------------- +/* [SMTP] */ + +/** + * + * @var string SMTP server name or IP address ("localhost" should work in the most cases) + * Configure using custom_config.inc.php + * @uses lib/functions/email_api.php + */ +$g_smtp_host = '[smtp_host_not_configured]'; # SMTP server MUST BE configured + +# Configure using custom_config.inc.php +$g_tl_admin_email = '[testlink_sysadmin_email_not_configured]'; # for problem/error notification +$g_from_email = '[from_email_not_configured]'; # email sender +$g_return_path_email = '[return_path_email_not_configured]'; + +/** + * Email notification priority (low by default) + * Urgent = 1, Not Urgent = 5, Disable = 0 + */ +$g_mail_priority = 5; + +/** + * Taken from mantis for phpmailer config + * select the method to mail by: + * PHPMAILER_METHOD_MAIL - mail() + * PHPMAILER_METHOD_SENDMAIL - sendmail + * PHPMAILER_METHOD_SMTP - SMTP + */ +$g_phpMailer_method = PHPMAILER_METHOD_SMTP; + +/** + * Configure only if SMTP server requires authentication + */ +$g_smtp_username = ''; # user +$g_smtp_password = ''; # password + +/** + * This control the connection mode to SMTP server. + * Can be '', 'ssl','tls' + * + * @global string $g_smtp_connection_mode + */ +$g_smtp_connection_mode = ''; + +/** + * The smtp port to use. + * The typical SMTP ports are 25 and 587. The port to use + * will depend on the SMTP server configuration and hence others may be used. + * + * @global int $g_smtp_port + */ +$g_smtp_port = 25; + +/** + * + * @see https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting Opportunistic TLS + */ +$g_SMTPAutoTLS = false; + +// ---------------------------------------------------------------------------- +/* [User Authentication] */ + +/** + * Login authentication method: + * 'MD5' => use password stored on db => will be deprecated and DB used. + * 'DB' => Same as MD5 use password stored on db + * 'LDAP' => use password from LDAP Server + */ +$tlCfg->authentication['domain'] = array( + 'DB' => array( + 'description' => 'DB', + 'allowPasswordManagement' => true + ), + 'LDAP' => array( + 'description' => 'LDAP', + 'allowPasswordManagement' => false + ) +); + +/* Default Authentication method */ +$tlCfg->authentication['method'] = 'DB'; + +// Applies only if authentication method is DB. +// Used when: +// 1. user sign up +// +// null => only check password IS NOT EMPTY +// +// $tlCfg->passwordChecks = array('minlen' => 8,'maxlen' => 20,'number' => true,'letter' => true, +// 'capital' => true, 'symbol' => true); +$tlCfg->passwordChecks = null; + +// Applies ONLY to the HTML input. +// If auth method is DB, password will be stored as MD5 HASH that requires 32 chars (128 bits) +$tlCfg->loginPagePasswordMaxLenght = 40; + +/** + * Standard logout url, used also when SSO is used and hint to skip SSO is used. + * '' => use standard TestLink page + */ +$tlCfg->logoutUrl = ''; + +// users that will not allow expiration date management on GUI +$tlCfg->noExpDateUsers = array( + 'admin' +); + +/** + * OAUTH auth + * Configure this on custom_config.inc.php + */ + +$tlCfg->OAuthServers = array(); + +// Google +// see cfg/oauth_samples/oauth.google.inc.php + +// Github +// see cfg/oauth_samples/oauth.github.inc.php + +// Gitlab +// see cfg/oauth_samples/oauth.gitlab.inc.php + +// Microsoft +// see cfg/oauth_samples/oauth.microsoft.inc.php + +// Azure AD +// see cfg/oauth_samples/oauth.azuread.inc.php + +/** + * Single Sign On authentication + * + * SSO_method: CLIENT_CERTIFICATE, tested with Apache Webserver + * SSP_method: WEBSERVER_VAR, tested with Apache and Shibboleth Service Provider. + */ +$tlCfg->authentication['SSO_enabled'] = false; +$tlCfg->authentication['SSO_logout_destination'] = 'YOUR LOGOUT DESTINATION'; + +// Tested with Apache Webserver +// $tlCfg->authentication['SSO_method'] = 'CLIENT_CERTIFICATE'; +// $tlCfg->authentication['SSO_uid_field'] = 'SSL_CLIENT_S_DN_Email'; + +// Tested with Apache and Shibboleth Service Provider +// $tlCfg->authentication['SSO_method'] = 'WEBSERVER_VAR'; +// $tlCfg->authentication['SSO_uid_field'] = 'REMOTE_USER'; +// $tlCfg->authentication['SSO_user_target_dbfield'] = 'email'; + +// Allow to restrict authentication to SSO +$tlCfg->authentication['sso_only'] = false; + +/** + * LDAP authentication credentials, Multiple LDAP Servers can be used. + * User will be authenticaded against each server (one after other using array index order) + * till authentication succeed or all servers have been used. + */ +$tlCfg->authentication['ldap'] = array(); +$tlCfg->authentication['ldap'][1]['ldap_server'] = 'localhost'; +$tlCfg->authentication['ldap'][1]['ldap_port'] = '389'; +$tlCfg->authentication['ldap'][1]['ldap_version'] = '3'; // could be '2' in some cases +$tlCfg->authentication['ldap'][1]['ldap_root_dn'] = 'dc=mycompany,dc=com'; +$tlCfg->authentication['ldap'][1]['ldap_bind_dn'] = ''; // Left empty for anonymous LDAP binding +$tlCfg->authentication['ldap'][1]['ldap_bind_passwd'] = ''; // Left empty for anonymous LDAP binding +$tlCfg->authentication['ldap'][1]['ldap_tls'] = false; // true -> use tls + +// Following configuration parameters are used to build +// ldap filter and ldap attributes used by ldap_search() +// +// filter => "(&$t_ldap_organization($t_ldap_uid_field=$t_username))"; +// attributess => array( $t_ldap_uid_field, 'dn' ); +// +// This can be used to manage situation like explained on post on forum: +// ActiveDirectory + users in AD group +// +$tlCfg->authentication['ldap'][1]['ldap_organization'] = ''; // e.g. '(organizationname=*Traffic)' +$tlCfg->authentication['ldap'][1]['ldap_uid_field'] = 'uid'; // Use 'sAMAccountName' for Active Directory + +// Configure following fields in custom_config.inc.php according your configuration +$tlCfg->authentication['ldap'][1]['ldap_email_field'] = 'mail'; +$tlCfg->authentication['ldap'][1]['ldap_firstname_field'] = 'givenname'; +$tlCfg->authentication['ldap'][1]['ldap_surname_field'] = 'sn'; + +// Follows Mantisbt idea. +// true if user does not exist on DB, but can be get from LDAP, +// the user will be created AUTOMATICALLY with default user role. +// Create user with following data from LDAP +// mail +// name +// surname +$tlCfg->authentication['ldap_automatic_user_creation'] = false; + +/** + * Enable/disable Users to create accounts on login page + */ +$tlCfg->user_self_signup = true; + +/** + * What happens when Administrator push the Reset Password Button + * 'send_password_by_mail' + * 'display_on_screen' + */ +$tlCfg->password_reset_send_method = 'send_password_by_mail'; + +/** + * Validating new user login names + * Taken mantisbt version 1.2.5 - www.mantisbt.org and adapted + * + * The regular expression to use when validating new user login names + * The default regular expression allows a-z, A-Z, 0-9, +, -, dot, @ and underscore. + * For testing regular expressions, use http://rubular.com/. + * For regular expression to englihs, use http://xenon.stanford.edu/~xusch/regexp/analyzer.html + */ +$tlCfg->validation_cfg->user_login_valid_regex = '/^([a-z\d\-.+_@]+(@[a-z\d\-.]+\.[a-z]{2,4})?)$/i'; + +/** + * Validating user email addresses + * Example of other possibilities: + * + * $regex = "/^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*" . + * "@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i"; + * $regex = "/^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/"; + * + */ +// +// This expression does not allow Top Level Domian (last part of domain name) longer than 4 +// If you need to change this +// Configure this on custom_config.inc.php +$tlCfg->validation_cfg->user_email_valid_regex_js = "/^(\w+)([-+.][\w]+)*@(\w[-\w]*\.){1,5}([A-Za-z]){2,4}$/"; +$tlCfg->validation_cfg->user_email_valid_regex_php = "/^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/U"; + +// -------------------------------------------------------------------------------------- +/* [API] */ + +/** + * XML-RPC API availability - do less than promised ;) + * false => user are not able to generate and set his/her API key. + * XML-RPC server do not check this config in order to answer or not a call. + */ + +$tlCfg->api->enabled = true; + +// used to display API ID info in the *View pages +$tlCfg->api->id_format = "[ID: %s ]"; + +// --------------------------------------------------------------------------------- +/* [GUI LAYOUT] */ + +/** + * Company logo (used by navigation bar and login page page) + */ +$tlCfg->logo_login = 'tl-logo-transparent-25.png'; +$tlCfg->logo_navbar = 'tl-logo-transparent-12.5.png'; + +/** + * Height of the navbar always displayed + */ +$tlCfg->navbar_height = 70; + +/** + * Login page could show an informational text + */ +$tlCfg->login_info = ''; // Empty by default + +/** + * controls if pagination (via Javascript) will be enabled + */ +$tlCfg->gui->projectView = new stdClass(); +$tlCfg->gui->projectView->pagination = new stdClass(); +$tlCfg->gui->projectView->pagination->enabled = true; +$tlCfg->gui->projectView->pagination->length = '[20, 40, 60, -1], [20, 40, 60, "All"]'; + +$tlCfg->gui->usersAssign = new stdClass(); +$tlCfg->gui->usersAssign->pagination = new stdClass(); +$tlCfg->gui->usersAssign->pagination->enabled = true; +$tlCfg->gui->usersAssign->pagination->length = '[20, 40, 60, -1], [20, 40, 60, "All"]'; + +$tlCfg->gui->planView = new stdClass(); +$tlCfg->gui->planView->pagination = new stdClass(); +$tlCfg->gui->planView->pagination->enabled = true; +$tlCfg->gui->planView->pagination->length = '[20, 40, 60, -1], [20, 40, 60, "All"]'; +$tlCfg->gui->planView->itemQtyForTopButton = 10; + +$tlCfg->gui->buildView = new stdClass(); +$tlCfg->gui->buildView->pagination = new stdClass(); +$tlCfg->gui->buildView->pagination->enabled = true; +$tlCfg->gui->buildView->pagination->length = '[20, 40, 60, -1], [20, 40, 60, "All"]'; +$tlCfg->gui->buildView->itemQtyForTopButton = 10; + +$tlCfg->gui->keywordsView = new stdClass(); +$tlCfg->gui->keywordsView->pagination = new stdClass(); +$tlCfg->gui->keywordsView->pagination->enabled = true; +$tlCfg->gui->keywordsView->pagination->length = '[40, 60, 80, -1], [40, 60, 80, "All"]'; +$tlCfg->gui->keywordsView->itemQtyForTopButton = 10; + +/** + * controls if operation area (buttons) starts open ('' or 'inline') or closed ('none') on: + * - test suite management + * - test case management + * - req. + * spec management + * - req. management + */ +$tlCfg->gui->op_area_display = new stdClass(); + +// test_spec_container => test project, test suite +$tlCfg->gui->op_area_display->test_spec_container = 'none'; // '' +$tlCfg->gui->op_area_display->test_case = 'none'; // 'inline' +$tlCfg->gui->op_area_display->req_spec_container = 'none'; // 'inline' +$tlCfg->gui->op_area_display->req = 'none'; // 'inline' + +/** + * + * @var string Availability of Test Project specific background colour + * 'background' -> standard behaviour for 1.6.x you can have a different + * background colour for every test project. + * 'none' -> new behaviour no background color change + */ +$tlCfg->gui->testproject_coloring = 'none'; // I'm sorry default is not coloring using coloring is a pain + // and useless +/** + * + * @todo havlatm4francisco Ok, then merge these two attributes into one + */ +/** + * default background color + */ +$tlCfg->gui->background_color = '#9BD'; + +// ENABLED: on features that assign user role to test projects and test plan, colour user name +// according GLOBAL role +// DISABLED: do not color [STANDARD BEHAVIOUR] +$tlCfg->gui->usersAssignGlobalRoleColoring = DISABLED; + +// Enable/disable rounded corners via javascript +$tlCfg->gui->round_corners = new stdClass(); +$tlCfg->gui->round_corners->exec_history = ENABLED; +$tlCfg->gui->round_corners->tc_title = ENABLED; +$tlCfg->gui->round_corners->tc_spec = ENABLED; + +/** + * Display name definition (used to build a human readable display name for users) + * Example of values: + * '%first% %last%' -> John Cook + * '%last%, %first%' -> Cook John + * '%first% %last% %login%' -> John Cook [ux555] + */ +$tlCfg->username_format = '%login%'; + +/** + * Configure the frame frmWorkArea navigator width + */ +$tlCfg->frame_workarea_default_width = "30%"; + +/** + * true => icon edit will be added into as indication an edit features + */ +$tlCfg->gui->show_icon_edit = false; + +/** + * '' => test project name + * 'prefix' => prefix : test project name + * + * ATTENTION : * is used to indicate test project is INACTIVE + * see also $tlCfg->gui->tprojects_combo_order_by + */ +$tlCfg->gui->tprojects_combo_format = 'prefix'; + +/** + * Order to use when building a testproject combobox (value must be SQL compliant) + * For example: + * 'ORDER BY name' + * 'ORDER_BY nodes_hierarchy.id DESC' -> similar effect to order last created firts + */ +// $tlCfg->gui->tprojects_combo_order_by = 'ORDER BY nodes_hierarchy.id DESC'; +$tlCfg->gui->tprojects_combo_order_by = 'ORDER BY TPROJ.prefix ASC'; + +/** + * Configure the input size of test case search by id on navigation bar. + * This value will be added to the length of the prefix to dynamically set input size. + * Example: prefix is "projectA-" -> length of prefix is 9 + * Now the here defined value (default: 6) will be added to the prefix length + * -> Input field will have an input size of 15 + */ +$tlCfg->gui->dynamic_quick_tcase_search_input_size = 6; + +// used to round percentages on metricsDashboard.php +$tlCfg->dashboard_precision = 2; + +/** + * Choose what kind of webeditor you want to use in every TL area. + * This configuration + * will be used if no element with search key (area) is found on this structure. + * Every element is a mp with this configuration keys: + * + * 'type': + * 'ckeditor' + * 'tinymce' ==> will be deprecated in future versions + * 'none' -> use plain text area input field + * 'toolbar': only applicable for type = 'fckeditor', 'ckeditor' + * name of ToolbarSet (See: http://docs.fckeditor.net/ for more information about ToolbarSets) + * TestLink stores own definitions in /cfg/tl_ckeditor_config.js + * + * + * The next keys/areas are supported: + * 'all' (default setting), + * 'design', 'steps_design', 'testplan', 'build', 'testproject', 'role', 'requirement', 'requirement_spec'. + * + * Examples: + * + * // Copy this to custom_config.inc.php if you want use 'tinymce' as default. + * $tlCfg->gui->text_editor['all'] = array( 'type' => 'tinymce'); + * // Copy this to custom_config.inc.php if you want use 'nome' as default. + * $tlCfg->gui->text_editor['all'] = array( 'type' => 'none'); + * //This configuration is useful only if default type is set to 'fckeditor' + * $tlCfg->gui->text_editor['design'] = array('toolbar' => 'tl_mini'); + * + * $tlCfg->gui->text_editor['testplan'] = array( 'type' => 'none'); + * $tlCfg->gui->text_editor['build'] = array( 'type' => 'fckeditor','toolbar' => 'tl_mini'); + * $tlCfg->gui->text_editor['testproject'] = array( 'type' => 'tinymce'); + * $tlCfg->gui->text_editor['role'] = array( 'type' => 'tinymce'); + * $tlCfg->gui->text_editor['requirement'] = array( 'type' => 'none'); + * $tlCfg->gui->text_editor['requirement_spec'] = array( 'type' => 'none'); + * + * + * Hint: After doing configuration changes, clean you Browser's cookies and cache + */ +/* + * $tlCfg->gui->text_editor = array(); + * $tlCfg->gui->text_editor['all'] = array('type' => 'fckeditor', + * 'toolbar' => 'tl_default', + * 'configFile' => 'cfg/tl_ckeditor_config.js',); + * $tlCfg->gui->text_editor['execution'] = array( 'type' => 'none'); + */ + +$tlCfg->gui->text_editor = array(); +$tlCfg->gui->text_editor['all'] = [ + 'type' => 'ckeditor', + 'toolbar' => 'Testlink', + 'configFile' => 'cfg/tl_ckeditor_config.js', + 'height' => 150 +]; + +// mini toolbar for test case steps edit +$tlCfg->gui->text_editor['steps_design'] = [ + 'type' => 'ckeditor', + 'toolbar' => 'TestlinkMini', + 'configFile' => 'cfg/tl_ckeditor_config.js', + 'height' => 100 +]; + +// +$tlCfg->gui->text_editor['preconditions'] = [ + 'type' => 'ckeditor', + 'toolbar' => 'Testlink', + 'configFile' => 'cfg/tl_ckeditor_config.js', + 'height' => 150 +]; + +$tlCfg->gui->text_editor['summary'] = [ + 'type' => 'ckeditor', + 'toolbar' => 'Testlink', + 'configFile' => 'cfg/tl_ckeditor_config.js', + 'height' => 600 +]; + +$tlCfg->gui->text_editor['execution'] = array( + 'type' => 'none' +); +$tlCfg->gui->text_editor['edit_execution'] = array( + 'type' => 'none', + 'cols' => 80, + 'rows' => 20 +); +$tlCfg->gui->text_editor['display_execution_notes'] = array( + 'type' => 'none', + 'cols' => 80, + 'rows' => 20 +); + +/** + * User can choose order of menu areas + */ +$tlCfg->gui->layoutMainPageLeft = array( + 'testProject' => 1, + 'userAdministration' => 2, + 'requirements' => 3, + 'testSpecification' => 4, + 'general' => 5 +); +$tlCfg->gui->layoutMainPageRight = array( + 'testPlan' => 1, + 'testExecution' => 2, + 'testPlanContents' => 3 +); + +/** + * Enable warning on a changed content before an user leave a page. + * + * Tested in: + * - IE8 OK + * - Firefox 3 OK + * - Chrome FAIL + * + * Does not work in Webkit browsers (Chrome, Safari) when using frames. + * Bug in webkit: https://bugs.webkit.org/show_bug.cgi?id=19418 + */ + +// seems that with config options that will be used on javascript via smarty template variables +// we are having problems using false/true => use 0/1 (or our CONSTANT DISABLED/ENABLED) +$tlCfg->gui->checkNotSaved = ENABLED; + +// ---------------------------------------------------------------------------- +/* [GUI: TREE] */ + +/** + * Default ordering value for new Test Suites and Test Cases to separate them + */ +$tlCfg->treemenu_default_testsuite_order = 1; +$tlCfg->treemenu_default_testcase_order = 1000; + +/** + * show/hide testcase id on tree menu + */ +$tlCfg->treemenu_show_testcase_id = true; + +/** + * Reorder test cases based on TC Name or External ID in tree on + * test suite level using reorder button + */ +// 'EXTERNAL_ID' -> Sort on Test Case External ID field displayed on tree.(Default) +// 'NAME' -> Sort on Test Case Name field + +$tlCfg->testcase_reorder_by = 'EXTERNAL_ID'; +// $tlCfg->testcase_reorder_by = 'NAME'; + +// ---------------------------------------------------------------------------- +/* [GUI: Javascript libraries] */ + +// May be in future another table sort engine will be better +// kryogenix.org -> Stuart Langridge sortTable +// '' (empty string) -> disable table sorting feature +$g_sort_table_engine = 'kryogenix.org'; + +// -------------------------------------------------------------------------------------- +/* [Reports] */ +$tlCfg->reportsCfg = new stdClass(); + +// Displayed execution statuses to use on reports (ordered). */ +$tlCfg->reportsCfg->exec_status = $tlCfg->results['status_label_for_exec_ui']; + +/** + * Default Offset in seconds for reporting start date (reports with date range) + * + * @uses lib/results/resultsMoreBuilds.php + */ +$tlCfg->reportsCfg->start_date_offset = (7 * 24 * 60 * 60); // one week + +// minutes part is ignored but must be configured. +// Hint: set always to :00 +$tlCfg->reportsCfg->start_time = '00:00'; + +// Result matrix (resultsTC.php) +$tlCfg->resultMatrixReport = new stdClass(); + +// Shows an extra column with the result of the latest execution on +// the lastest CREATED build +$tlCfg->resultMatrixReport->buildColumns['showExecutionResultLatestCreatedBuild'] = true; + +// Result matrix (resultsTC.php) +// Shows an extra column with the note of latest execution on +// the lastest CREATED build +$tlCfg->resultMatrixReport->buildColumns['showExecutionNoteLatestCreatedBuild'] = true; + +// Show build columns in revers order. The latest build is to the left +$tlCfg->resultMatrixReport->buildColumns['latestBuildOnLeft'] = false; + +// After having got performance and usability issue, a limit on max qty of builds +// allowed on data extration has been set. +// Is absolutely arbitrary +// +$tlCfg->resultMatrixReport->buildQtyLimit = 6; + +// ORDER BY sql clause, refers to builds table columns +$tlCfg->resultMatrixReport->buildOrderByClause = " ORDER BY name ASC"; + +// Show all available status details for test plans on metrics dashboard +$tlCfg->metrics_dashboard = new stdClass(); +$tlCfg->metrics_dashboard->show_test_plan_status = false; + +// ---------------------------------------------------------------------------- +/* [GENERATED DOCUMENTATION] */ + +/** + * Texts and settings for printed documents + * Image is expected in directory /gui/themes//images/ + * Leave text values empty if you would like to hide parameters. + */ +$tlCfg->document_generator->company_name = 'TestLink Community [configure $tlCfg->document_generator->company_name]'; +$tlCfg->document_generator->company_copyright = '2021 © TestLink Community'; +$tlCfg->document_generator->confidential_msg = ''; + +// Logo for generated documents +$tlCfg->document_generator->company_logo = $tlCfg->logo_login; +$tlCfg->document_generator->company_logo_height = '53'; + +/** + * CSS used in printed html documents + */ +$tlCfg->document_generator->css_template = 'css/tl_documents.css'; + +// CSS file for Requirement Specification Document, Requirement and Requirement Spec Print View +$tlCfg->document_generator->requirement_css_template = 'css/tl_documents.css'; + +/** + * Misc settings + */ +// Display test case version when creating: +// - test spec document +// - test reports +$tlCfg->document_generator->tc_version_enabled = true; + +// ---------------------------------------------------------------------------- +/* [Test Executions] */ + +// $tlCfg->exec_cfg->enable_test_automation = DISABLED; + +// ENABLED -> enable XML-RPC calls to external test automation server +// new buttons will be displayed on execution pages +// DISABLED -> disable +$tlCfg->exec_cfg->enable_test_automation = DISABLED; + +// ASCending -> last execution at bottom +// DESCending -> last execution on top [STANDARD BEHAVIOUR] +$tlCfg->exec_cfg->history_order = 'DESC'; + +// true -> the whole execution history for the choosen build will be showed +// false -> just last execution for the choosen build will be showed [STANDARD BEHAVIOUR] +$tlCfg->exec_cfg->history_on = false; + +// true -> test case VERY LAST (i.e. in any build) execution status will be displayed [STANDARD BEHAVIOUR] +// false -> only last result on current build. +$tlCfg->exec_cfg->show_last_exec_any_build = true; + +// true -> History for all builds will be shown +// false -> Only history of the current build will be shown [STANDARD BEHAVIOUR] +$tlCfg->exec_cfg->show_history_all_builds = false; + +// true -> History for all platforms (if any exists for test plan) will be shown +// false -> Only history of the current platform will be shown [STANDARD BEHAVIOUR] +$tlCfg->exec_cfg->show_history_all_platforms = false; + +// different models for the attachments management on execution page +// $att_model_m1 -> shows upload button and title +// $att_model_m2 -> hides upload button and title +$tlCfg->exec_cfg->att_model = $att_model_m2; // defined in const.inc.php + +// IVU +// Default Value +// USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS +// USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS +// USE_LATEST_EXEC_ON_TESTPLAN_PLAT_FOR_COUNTERS +$tlCfg->exec_cfg->tcases_counters_mode = array(); +$tlCfg->exec_cfg->tcases_counters_mode['with_platforms'] = USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS; + +$tlCfg->exec_cfg->tcases_counters_mode['without_platforms'] = USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS; + +$tlCfg->exec_cfg->tcases_counters_mode_domain = array(); +$tlCfg->exec_cfg->tcases_counters_mode_domain['with_platforms'] = array( + 'USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS', + 'USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS', + 'USE_LATEST_EXEC_ON_TESTPLAN_PLAT_FOR_COUNTERS' +); + +$tlCfg->exec_cfg->tcases_counters_mode_domain['without_platforms'] = array( + 'USE_LATEST_EXEC_ON_CONTEX_FOR_COUNTERS', + 'USE_LATEST_EXEC_ON_TESTPLAN_FOR_COUNTERS' +); + +// ENABLED -> test cases will be coloured according to test case status +$tlCfg->exec_cfg->enable_tree_testcases_colouring = ENABLED; + +// ENABLED -> test cases will be coloured according to execution status on build selected for execution +// DISABLED -> test cases will be coloured according status on latest execution regardless of selected build +// see http://mantis.testlink.org/view.php?id=3450 for more details +$tlCfg->exec_cfg->testcases_colouring_by_selected_build = ENABLED; + +// ENABLED -> test case counters will be coloured according to test case status +$tlCfg->exec_cfg->enable_tree_counters_colouring = ENABLED; + +// This can help to avoid performance problems. +// Controls what happens on right frame when user clicks on a testsuite on tree. +// ENABLED -> show all test cases +// see $tlCfg->exec_cfg->show_testsuite_contents_deep +// +// DISABLED -> nothing happens, to execute a test case you need to click on test case +$tlCfg->exec_cfg->show_testsuite_contents = DISABLED; + +// @since 1.9.13 +// works in 'team' with $tlCfg->exec_cfg->show_testsuite_contents +// children: only direct children. +// deep: all test cases present in test suite and test suites in any level below +// selected test suite. +// IMPORTANT NOTICE: +// selecting deep can create performance issues. +// +$tlCfg->exec_cfg->show_testsuite_contents_deep = 'children'; + +// ENABLED -> enable testcase counters by status on tree +$tlCfg->exec_cfg->enable_tree_testcase_counters = ENABLED; + +// Define list of roles that are affected by: +// $tlCfg->exec_cfg->view_mode and $tlCfg->exec_cfg->exec_mode +// User must reconfigure if define other simple tester roles +// +// In addition (till code changes) also roles that verify this condition: +// $effective_role->hasRight('testplan_execute') and !$effective_role->hasRight('testplan_planning') +// Will be affected by: +// $tlCfg->exec_cfg->view_mode and $tlCfg->exec_cfg->exec_mode +// +$tlCfg->exec_cfg->simple_tester_roles = array( + TL_ROLES_TESTER +); + +// Filter Test cases a user with tester role can VIEW depending on +// test execution assignment. +// all: all test cases. +// assigned_to_me: test cases assigned to logged user. +// assigned_to_me_or_free: test cases assigned to logged user or not assigned +$tlCfg->exec_cfg->view_mode->tester = 'assigned_to_me'; + +// Filter Test cases a user with tester role can EXECUTE depending on +// test execution assignment. +// all: all test cases. +// assigned_to_me: test cases assigned to logged user. +// assigned_to_me_or_free: test cases assigned to logged user or not assigned +$tlCfg->exec_cfg->exec_mode->tester = 'assigned_to_me'; + +// How to set defaut values for execution fields (standard & custom) +// clean => all clean +// latest => get as much as possible values from latest execution on +// same context => test plan,platform, build +$tlCfg->exec_cfg->exec_mode->new_exec = 'clean'; + +// @since 1.9.15 +// Before 1.9.15 save & move to next worked JUST inside +// a test suite => save_and_move = 'limited' +// 1.9.15 will move on whole test project +// save_and_move = 'unlimited' +$tlCfg->exec_cfg->exec_mode->save_and_move = 'unlimited'; + +$tlCfg->exec_cfg->exec_mode->addLinkToTLChecked = false; +$tlCfg->exec_cfg->exec_mode->addLinkToTLPrintViewChecked = false; +$tlCfg->exec_cfg->exec_mode->assignTaskChecked = false; + +/** + * User filter in Test Execution navigator - default value + */ +// logged_user -> combo will be set to logged user +// none -> no filter applied by default +$tlCfg->exec_cfg->user_filter_default = 'none'; + +// 'horizontal' -> step and results on the same row +// 'vertical' -> steps on one row, results in the row bellow +$tlCfg->exec_cfg->steps_results_layout = 'horizontal'; + +// true => on single test case execution feature, notes and result +// can be provided for each step +// false => pre 1.9.10 behaviour +// +$tlCfg->exec_cfg->steps_exec = true; + +// this setting will work on AND mode with: +// $tlCfg->exec_cfg->steps_exec +$tlCfg->exec_cfg->steps_exec_attachments = true; + +// When textarea is displayed to allow user to write execution notes +// at step level, choose what to display: +// 'empty' +// 'latest' => latest execution notes. +$tlCfg->exec_cfg->steps_exec_notes_default = 'empty'; + +// 'empty' +// 'latest' => latest execution notes. +$tlCfg->exec_cfg->steps_exec_status_default = 'empty'; + +// Parameters to show notes/details when entering test execution feature +// EXPAND: show expanded/open +// COLLAPSE: show collapsed/closede +// LAST_USER_CHOICE: get status from cookie that holds last user choice.[STANDARD BEHAVIOUR] +$tlCfg->exec_cfg->expand_collapse = new stdClass(); +$tlCfg->exec_cfg->expand_collapse->testplan_notes = LAST_USER_CHOICE; +$tlCfg->exec_cfg->expand_collapse->platform_description = LAST_USER_CHOICE; +$tlCfg->exec_cfg->expand_collapse->build_description = LAST_USER_CHOICE; +$tlCfg->exec_cfg->expand_collapse->testsuite_details = LAST_USER_CHOICE; + +$tlCfg->exec_cfg->copyLatestExecIssues = new stdClass(); + +// true => When saving an execution, a new option will be displayed, and user will be +// able to do a choice +// COPY OR NOT issues linked to latest execution to the new execution +// DEAFULT false => no option on GUI + +$tlCfg->exec_cfg->copyLatestExecIssues->enabled = false; + +// value to set as default +$tlCfg->exec_cfg->copyLatestExecIssues->default = false; + +// you can choose only between columns present on +// (see exec.inc.php, function get_bugs_for_exec()) +$tlCfg->exec_cfg->bugs_order_clause = ' ORDER BY builds.name,step_number,bug_id '; + +$tlCfg->exec_cfg->features = new stdClass(); +$tlCfg->exec_cfg->features->attachments = new stdClass(); +$tlCfg->exec_cfg->features->attachments->enabled = true; +$tlCfg->exec_cfg->features->exec_duration = new stdClass(); +$tlCfg->exec_cfg->features->exec_duration->enabled = true; + +$tlCfg->exec_cfg->issues = new stdClass(); +$tlCfg->exec_cfg->issues->tcase_level = new stdClass(); +$tlCfg->exec_cfg->issues->tcstep_level = new stdClass(); + +/** + * %%STEPNUMBER%%,%%TCNAME%%,%%PROJECTNAME%%,%%PLANNAME%% + * %%BUILDNAME%%,%%PLATFNAME%%,%%EXECTSISO%%, + * %%TCPATHNAME%% + * + * /saado/TS100/SAA-4:WSTEPS Executed ON (ISO FORMAT): 2018-02-25CET10:00 + */ +$tlCfg->exec_cfg->issues->tcase_level->subject = '$$issue_subject_tcname %%TCPATHNAME%% - $$issue_subject_execon %%EXECTSISO%% '; + +/* + * $tlCfg->exec_cfg->issues->tcstep_level->subject = + * '$$issue_on_step %%STEPNUMBER%% - $$issue_subject_tcname %%TCNAME%% - ' . + * '$$issue_subject_projectname %%PROJECTNAME%% - ' . + * '$$issue_subject_planname %%PLANNAME%% - ' . + * '$$issue_subject_buildname %%BUILDNAME%% - ' . + * '$$issue_subject_platfname %%PLATFNAME%%'; + */ + +$tlCfg->exec_cfg->issues->tcstep_level->subject = '$$issue_on_step %%STEPNUMBER%% - $$issue_subject_tcname %%TCNAME%% '; + +// ---------------------------------------------------------------------- +/* [Test Specification] */ + +// true will be displayed when displayed a test case +$tlCfg->spec_cfg->show_tplan_usage = true; + +// 'horizontal' -> step and results on the same row +// 'vertical' -> steps on one row, results in the row bellow +$tlCfg->spec_cfg->steps_results_layout = 'horizontal'; + +// ENABLED -> User will see a test suite filter while creating test specification +// DISABLED -> no filter available +// $g_spec_cfg->show_tsuite_filter = ENABLED; +$tlCfg->spec_cfg->show_tsuite_filter = ENABLED; + +// ENABLED -> every time user do some operation on test specification +// tree is updated on screen. +// DISABLED -> tree will not be updated, user can update it manually. +// Anyway on user interface user will be able to change this choice +// $g_spec_cfg->automatic_tree_refresh = ENABLED; +$tlCfg->spec_cfg->automatic_tree_refresh = ENABLED; + +// To avoid perfomance problems on search test case feature, +// we can decide when to inform user that results can not be displayed +// due to too many results. +$tlCfg->testcase_cfg->search = new stdClass(); +$tlCfg->testcase_cfg->search->max_qty_for_display = 200; + +$tlCfg->testcase_cfg->duplicated_name_algorithm = new stdClass(); +// 'stringPrefix' => use duplicated_name_algorithm->text +// 'counterSuffix' => creare name as : +// test case title + (number of existent test cases +1) +// example: My Test Title 2 +// duplicated_name_algorithm->text is used as sprintf format mask +$tlCfg->testcase_cfg->duplicated_name_algorithm->type = 'stringPrefix'; +$tlCfg->testcase_cfg->duplicated_name_algorithm->text = "%Y%m%d-%H:%M:%S"; + +// $tlCfg->testcase_cfg->duplicated_name_algorithm->type = 'counterSuffix'; +// $tlCfg->testcase_cfg->duplicated_name_algorithm->text = " (%s)"; + +// TICKET 6422: Estimation in Test specification as mandatory field +// Implemented using HTML5 +$tlCfg->testcase_cfg->estimated_execution_duration = new stdClass(); +// $tlCfg->testcase_cfg->estimated_execution_duration->required = 'required'; +$tlCfg->testcase_cfg->estimated_execution_duration->required = ''; + +// There are some preconfigured standard types which you can use, +// additionally you can configure your own types. +$tlCfg->testcase_cfg->relations = new stdClass(); +$tlCfg->testcase_cfg->relations->enable = true; +$tlCfg->testcase_cfg->relations->interproject_linking = false; + +/** + * Localization identifiers for test cases relation types + * Types, which are configured above, have to be configured + * here too with attributes "source" and "destination". + * + * Last value will be selected in GUI as default. + * + * Form has to be like this: + * + * ... = array( + * RELATIONNAME => array( + * 'source' => 'SOURCE_LOCALIZATION_KEY', + * 'destination' => 'DESTINATION_LOCALIZATION_KEY'), + * ... + * + * @since TestLink 1.9.12 + */ + +$tlCfg->testcase_cfg->relations->type_labels = array( + TL_REL_TYPE_PARENT_CHILD => [ + 'source' => 'parent_of', + 'destination' => 'child_of' + ], + TL_REL_TYPE_BLOCKS_DEPENDS => [ + 'source' => 'blocks', + 'destination' => 'depends' + ], + TL_REL_TYPE_RELATED => [ + 'source' => 'related_to', + 'destination' => 'related_to' + ], + TL_REL_TYPE_AUTOMATION_PARENT_CHILD => [ + 'source' => 'automates_also', + 'destination' => 'is_automated_by' + ], + TL_REL_TYPE_EXECUTE_TOGETHER => [ + 'source' => 'executed_me_and_also', + 'destination' => 'executed_me_and_also' + ] +); + +$tlCfg->testcase_cfg->relations->type_description = [ + TL_REL_TYPE_PARENT_CHILD => 'parent_child', + TL_REL_TYPE_BLOCKS_DEPENDS => 'blocks_depends', + TL_REL_TYPE_RELATED => 'related_to', + TL_REL_TYPE_AUTOMATION_PARENT_CHILD => 'automation_script', + TL_REL_TYPE_EXECUTE_TOGETHER => 'executed_me_and_also' +]; + +// @since 1.9.18 +// true => After a test case version has been executed +// attachment on test case spec can not be added/removed +// +// false +// +// This means that at GUI Level, will not be possible: +// add a new attachment to an Executed Test Case Version +// delete an attachment from Executed Test Case Version +$tlCfg->testcase_cfg->downloadOnlyAfterExec = true; + +// This means that at GUI Level, will not be possible: +// add a new req version link to an Executed Test Case Version +// delete a req version link from Executed Test Case Version +$tlCfg->testcase_cfg->reqLinkingDisabledAfterExec = true; + +// Effects on Linked Requirements Version after +// execution of a Test Case Version +$tlCfg->testcase_cfg->freezeReqVersionAfterExec = true; + +// Effects on TCVersion N when TCVersion N+1 is created +$tlCfg->testcase_cfg->freezeTCVersionOnNewTCVersion = true; +$tlCfg->testcase_cfg->freezeTCVRelationsOnNewTCVersion = true; + +// Because: +// The Relation must be frozen (cannot be deleted) when +// a new version of the test case is created. +// +// It seems confusing that relation can be added, then +// this new configuration will allow this operation +// only on latest test case version +// +$tlCfg->testcase_cfg->addTCVRelationsOnlyOnLatestTCVersion = true; + +// Not Already Implemented +// $tlCfg->testcase_cfg->allowAddTCVRelationsOnOldTCVersion = true; + +// $tlCfg->testcase_cfg->frozenNotExecutedTCVDelAttachtments = false; +// $tlCfg->testcase_cfg->frozenNotExecutedTCVAddAttachtments = false; +// $tlCfg->testcase_cfg->frozenNotExecutedTCVAddTCVRel = false; +// $tlCfg->testcase_cfg->frozenNotExecutedTCVDelTCVRel = false; +// $tlCfg->testcase_cfg->frozenNotExecutedTCVAddREQVLink = false; +// $tlCfg->testcase_cfg->frozenNotExecutedTCVDelREQVLink = false; + +// Change order using CSS flexbox model +// @used-by tcEdit.tpl +$tlCfg->testcase_cfg->viewerFieldsOrder = new stdClass(); +$tlCfg->testcase_cfg->viewerFieldsOrder->summary = 3; +$tlCfg->testcase_cfg->viewerFieldsOrder->spaceOne = 2; +$tlCfg->testcase_cfg->viewerFieldsOrder->preconditions = 1; + +// Effects on Req Version to TCVersion LINK +// when a new version of a linked Test Case is created +// If LINK is frozen, then this means that link can not be deleted. +// $tlCfg->reqTCLinks->freezeLinkOnNewTCVersion = false; +// +// Important Notice: +// Req Version to TCVersion Link can be done ONLY TO LATEST TCV. +// +// This means that : +// +// on GUI on the Requirements Area on TEST CASE Specification Feature: +// this option has NO EFFECT +// +// on GUI on the Coverage Area on REQUIREMENT Specification Feature: +// this option has EFFECT +// +// on GUI on the Assign Requirements Feature: +// this option has EFFECT +// +$tlCfg->reqTCLinks->freezeLinkOnNewTCVersion = true; + +// Effects on Req Version to TCVersion LINK +// when a new version of a linked Req Version is created +$tlCfg->reqTCLinks->freezeLinkOnNewREQVersion = true; + +// Effects on BOTH ends of Req Version to TCVersion LINK +// when a new version of a linked TC Version is created +$tlCfg->reqTCLinks->freezeBothEndsOnNewTCVersion = true; + +// Effects on BOTH ends of Req Version to TCVersion LINK +// when a new version of a linked REQ Version is created +$tlCfg->reqTCLinks->freezeBothEndsOnNewREQVersion = true; + +// Effects on REQ Version N when REQ Version N+1 is created +$tlCfg->req_cfg->freezeREQVersionOnNewREQVersion = true; + +/** + * text template for a new items: + * Test Case: summary, steps, expected_results, preconditions + */ +// object members has SAME NAME that FCK editor objects. +// the logic present on tcEdit.php is dependent of this rule. +// every text object contains an object with following members: type and value +// +// Possible values for type member: +// none: template will not be used, default will be a clean editor screen. +// +// string: value of value member is assigned to FCK object +// string_id: value member is used in a lang_get() call, and return value +// is assigned to FCK object. Configure string_id on custom_strings.txt +// value: value member is used as file name. +// file is readed and it's contains assigned to FCK object +// +// any other value for type, results on '' assigned to FCK object + +$tlCfg->testcase_template = new stdClass(); + +$tlCfg->testcase_template->summary = new stdClass(); +$tlCfg->testcase_template->summary->type = 'none'; +$tlCfg->testcase_template->summary->value = ''; + +$tlCfg->testcase_template->steps = new stdClass(); +$tlCfg->testcase_template->steps->type = 'none'; +$tlCfg->testcase_template->steps->value = ''; + +$tlCfg->testcase_template->expected_results = new stdClass(); +$tlCfg->testcase_template->expected_results->type = 'none'; +$tlCfg->testcase_template->expected_results->value = ''; + +$tlCfg->testcase_template->preconditions = new stdClass(); +$tlCfg->testcase_template->preconditions->type = 'none'; +$tlCfg->testcase_template->preconditions->value = ''; + +/** + * text template for a new Test Suite description + */ +$tlCfg->testsuite_template = new stdClass(); +$tlCfg->testsuite_template->details = new stdClass(); +$tlCfg->testsuite_template->details->type = 'none'; +$tlCfg->testsuite_template->details->value = ''; + +$tlCfg->project_template = new stdClass(); +$tlCfg->project_template->notes = new stdClass(); +$tlCfg->project_template->notes->type = 'none'; +$tlCfg->project_template->notes->value = ''; + +$tlCfg->testplan_template = new stdClass(); +$tlCfg->testplan_template->notes = new stdClass(); +$tlCfg->testplan_template->notes->type = 'none'; +$tlCfg->testplan_template->notes->value = ''; + +$tlCfg->execution_template = new stdClass(); +$tlCfg->execution_template->bulk_exec_notes = new stdClass(); +$tlCfg->execution_template->bulk_exec_notes->type = 'none'; +$tlCfg->execution_template->bulk_exec_notes->value = ''; + +$tlCfg->execution_template->notes = new stdClass(); +$tlCfg->execution_template->notes->type = 'none'; +$tlCfg->execution_template->notes->value = ''; + +$tlCfg->build_template = new stdClass(); +$tlCfg->build_template->notes = new stdClass(); +$tlCfg->build_template->notes->type = 'none'; +$tlCfg->build_template->notes->value = ''; + +$tlCfg->requirement_template = new stdClass(); +$tlCfg->requirement_template->scope = new stdClass(); +$tlCfg->requirement_template->scope->type = 'none'; +$tlCfg->requirement_template->scope->value = ''; + +$tlCfg->req_spec_template = new stdClass(); +$tlCfg->req_spec_template->scope = new stdClass(); +$tlCfg->req_spec_template->scope->type = 'none'; +$tlCfg->req_spec_template->scope->value = ''; + +$tlCfg->role_template = new stdClass(); +$tlCfg->role_template->notes = new stdClass(); +$tlCfg->role_template->notes->type = 'none'; +$tlCfg->role_template->notes->value = ''; + +$tlCfg->platform_template = new stdClass(); +$tlCfg->platform_template->notes = new stdClass(); +$tlCfg->platform_template->notes->type = 'none'; +$tlCfg->platform_template->notes->value = ''; + +// ---------------------------------------------------------------------------- +/* [ATTACHMENTS] */ + +/** + * Attachment feature availability + */ +$tlCfg->attachments = new stdClass(); +$tlCfg->attachments->enabled = true; + +// true -> when you upload a file you can give no title +$tlCfg->attachments->allow_empty_title = true; + +// $tlCfg->attachments->allow_empty_title == true, you can ask the system +// to do something +// +// 'none' -> just write on db an empty title +// 'use_filename' -> use filename as title +// $tlCfg->attachments->action_on_save_empty_title='use_filename'; +// +$tlCfg->attachments->action_on_save_empty_title = 'none'; + +// Remember that title is used as link description for download +// then if title is empty, what the system has to do when displaying ? +// 'show_icon' -> the $tlCfg->attachments->access_icon will be used. +// 'show_label' -> the value of $tlCfg->attachments->access_string will be used . +$tlCfg->attachments->action_on_display_empty_title = 'show_icon'; + +// Set display order of uploaded files +$tlCfg->attachments->order_by = " ORDER BY date_added DESC "; + +// need to be moved AFTER include of custom_config +// +// $tlCfg->attachments->access_icon = ''; +$tlCfg->attachments->access_string = "[*]"; + +/** + * Files that are allowed. + * Separate items by commas. + * eg. 'doc,xls,gif,png,jpg' + */ +$tlCfg->attachments->allowed_files = 'doc,xls,gif,png,jpg,xlsx,csv'; + +/** + * Process filename against XSS + * Thanks to http://owasp.org/index.php/Unrestricted_File_Upload + * '/^[a-zA-Z0-9]{1,20}\.[a-zA-Z0-9]{1,10}$/'; + * added - and _. + * + * NO CHECK if -> $g_attachments->allowed_filenames_regexp = ''; + */ +$tlCfg->attachments->allowed_filenames_regexp = '/^[a-zA-Z0-9_-]{1,20}\.[a-zA-Z0-9]{1,10}$/'; + +/** + * the type of the repository can be database or filesystem + * TL_REPOSITORY_TYPE_DB => database + * TL_REPOSITORY_TYPE_FS => filesystem + */ +$g_repositoryType = TL_REPOSITORY_TYPE_FS; + +/** + * TL_REPOSITORY_TYPE_FS: the where the filesystem repository should be located + * We recommend to change the directory for security reason. + * (see http://itsecuritysolutions.org/2012-08-13-TestLink-1.9.3-multiple-vulnerabilities/) + * Put it out of reach via web or configure access denied. + */ +$g_repositoryPath = '/var/testlink/upload_area/'; /* unix example */ +if ($upa = getenv('TESTLINK_UPLOAD_AREA')) { + $g_repositoryPath = trim($upa); +} + +/** + * compression used within the repository + * TL_REPOSITORY_COMPRESSIONTYPE_NONE => no compression + * TL_REPOSITORY_COMPRESSIONTYPE_GZIP => gzip compression + */ +$g_repositoryCompressionType = TL_REPOSITORY_COMPRESSIONTYPE_NONE; + +// the maximum allowed file size for each repository entry, default 1MB. +// Also check your PHP settings (default is usually 2MBs) +$tlCfg->repository_max_filesize = 1; // MB + +// ---------------------------------------------------------------------------- +/* [Requirements] */ + +// HAS TO BE REMOVED - > req_doc_id UNIQUE INSIDE test project +// true : you want req_doc_id UNIQUE IN THE WHOLE DB (system_wide) +// false: you want req_doc_id UNIQUE INSIDE a SRS +// $tlCfg->req_cfg->reqdoc_id->is_system_wide = false; + +$tlCfg->req_cfg->monitor_enabled = true; + +// truncate log message to this amount of chars for reqCompareVersions +$tlCfg->req_cfg->log_message_len = 200; + +/** + * Test Case generation from Requirements - use_req_spec_as_testsuite_name + * false => test cases are created and assigned to a test suite + * with name $tlCfg->req_cfg->default_testsuite_name + * true => REQuirement Specification Title is used as testsuite name + */ +$tlCfg->req_cfg->use_req_spec_as_testsuite_name = true; +$tlCfg->req_cfg->default_testsuite_name = "Auto-created Test cases"; +$tlCfg->req_cfg->testsuite_details = "Test Cases in the Test Suite are generated from Requirements. " . + "A refinement of test scenario is highly recommended."; +$tlCfg->req_cfg->testcase_summary_prefix = "The Test Case was generated from the assigned requirement.
"; + +// If the following value is enabled, then the summary prefix string will include the +// title and version number of the assigned requirement. +$tlCfg->req_cfg->use_testcase_summary_prefix_with_title_and_version = ENABLED; + +// If above option is enabled, the following string will be used as a template for the tc summary prefix. +// It has to include four variables in the form of "%s". The first and second one will be used internally by the system. +// The third one will then be replaced by the title of the originating Requirement, +// the fourth one by its version number. +// Attention: If there aren't exactly three variables in it, the operation will fail. +$tlCfg->req_cfg->testcase_summary_prefix_with_title_and_version = "The Test Case was generated from the assigned" . + " requirement
\"%s\" (version %s).
"; + +/** + * ENABLED: When generating Test Cases from Requirements, copy the scope of the Requirement + * to the summary of the newly created Test Case. + */ +$tlCfg->req_cfg->copy_req_scope_to_tc_summary = DISABLED; + +// To avoid perfomance problems on search Requirements feature, +// we can decide when to inform user that results can not be displayed +// due to too many results. +$tlCfg->req_cfg->search = new stdClass(); +$tlCfg->req_cfg->search->max_qty_for_display = 200; + +// ENABLED: allow N level depth tree +// DISABLED: just one level +$tlCfg->req_cfg->child_requirements_mgmt = ENABLED; + +// ENABLED: ask for this value on user interface and use on several features +// DISABLED: do not ask, do not use +$tlCfg->req_cfg->expected_coverage_management = ENABLED; + +// Show Child Requirements on Requirement Specification Print View +// ENABLED: Requirement Specification including Child Requirements are shown +// DIABLED: ONLY Requirement Specification is shown +$tlCfg->req_cfg->show_child_reqs_on_reqspec_print_view = DISABLED; + +// +// Order of test cases status in this array, is used to undestand +// to what status set requirement in the requirements report. +// Standard algorithm, present in getReqCoverage(), is: +// +// if at least one of Test Cases linked to Requirement has status FAILED +// Requirement Coverage Status = FAILED +// else if at least one of Test Cases linked to Requirement has status BLOCKED +// Requirement Coverage Status = BLOCKED +// else if ALL Test Cases linked to Requirement has status NOT RUN +// Requirement Coverage Status = NOT RUN +// else if ALL Test Cases linked to Requirement has status PASSED +// Requirement Coverage Status = PASSED +// else +// Requirement Coverage Status = Partially Passed +// +// This logic is implemented using following config parameter +$tlCfg->req_cfg->coverageStatusAlgorithm['checkOrder'] = array( + 'atLeastOne', + 'all' +); +$tlCfg->req_cfg->coverageStatusAlgorithm['checkType']['atLeastOne'] = array( + 'failed', + 'blocked' +); +$tlCfg->req_cfg->coverageStatusAlgorithm['checkType']['all'] = array( + 'passed' +); + +// Configure here what status has to get requirement when check of type 'all' fails like +// in following situation (Mantis 2171) +// +// If particular requirement has assigned more than one test cases, and: +// - at least one of assigned test cases was not yet executed +// - the rest of assigned test cases was executed and passed +// then on the "Requirements based report" this particular requirement is not shown at all (in any section). +// +// $tlCfg->req_cfg->coverageStatusAlgorithm['checkFail']['all']='partially_passed'; +// $tlCfg->req_cfg->coverageStatusAlgorithm['displayOrder']=array('passed','failed', +// 'blocked','not_run','partially_passed'); +// 20100819 - asimon - fix not needed anymore after rewrite of req based report +// $tlCfg->req_cfg->coverageStatusAlgorithm['checkFail']['all']='failed'; +// $tlCfg->req_cfg->coverageStatusAlgorithm['displayOrder']=array('passed','failed','blocked','not_run'); + +// truncate log message to this amount of chars for reqSpecCompareRevisions +$tlCfg->req_spec_cfg->log_message_len = 200; + +// Linking between requirements/requirement specifications +// +$tlCfg->internal_links = new stdClass(); + +// +// true: links to other requirements/requirement specifications are +// automatically generated from the corresponding Document ID, enclosed by tags (like BBCode). +// +// Usage: +// link to requirements: [req]req_doc_id[/req] +// link to requirement specifications: [req_spec]req_spec_doc_id[/req_spec] +// +// The test project of the requirement / requirement specification and an anchor +// to jump to can also be specified: +// [req tproj= anchor=]req_doc_id[/req] +// This syntax also works for requirement specifications. +// +// false: no links are automatically created. +// +$tlCfg->internal_links->enable = true; + +// how a linked document (requirement/requirement specification) should be displayed. +// posible values: +// 'window': new window/tab will be used (depending on browser configuration) +// 'frame' : same frame as the clicked link +// 'popup' (default): popup window (ATTENTION to browser pop-up block). +// +$tlCfg->internal_links->target = 'popup'; + +// title for automatically created link +// possible values: +// 'string': lang_get() will be used to localize +// 'none': no title will be generated, only link with ID +// 'item' (default): localized type of item (e.g. "Requirement: ", "Req Spec") +// will be used as title for the generated link +// +$tlCfg->internal_links->req_link_title = new stdClass(); +$tlCfg->internal_links->req_link_title->type = 'item'; +$tlCfg->internal_links->req_link_title->value = ''; + +$tlCfg->internal_links->req_spec_link_title = new stdClass(); +$tlCfg->internal_links->req_spec_link_title->type = 'item'; +$tlCfg->internal_links->req_spec_link_title->value = ''; + +// Relations between requirement documents: +// +// The relation types have to be configured in cfg/const.inc.php +// and their respective localization values in locale strings.txt. + +// There are some preconfigured standard types which you can use, +// additionally you can configure your own types. +$tlCfg->req_cfg->relations = new stdClass(); +$tlCfg->req_cfg->relations->enable = true; +$tlCfg->req_cfg->relations->interproject_linking = false; + +// Requirement/testcase diff +// default value of lines to show before and after each difference +$tlCfg->diffEngine->context = 5; + +// Configuration for Requirement Import using DOCBOOK format +$tlCfg->req_cfg->importDocBook = new stdClass(); +$tlCfg->req_cfg->importDocBook->requirement = "sect3"; +$tlCfg->req_cfg->importDocBook->title = "title"; +$tlCfg->req_cfg->importDocBook->paragraph = "para"; +$tlCfg->req_cfg->importDocBook->ordered_list = "orderedlist"; +$tlCfg->req_cfg->importDocBook->list_item = "listitem"; +$tlCfg->req_cfg->importDocBook->table = "informaltable"; +$tlCfg->req_cfg->importDocBook->table_group = "tgroup"; +$tlCfg->req_cfg->importDocBook->table_head = "thead"; +$tlCfg->req_cfg->importDocBook->table_body = "tbody"; +$tlCfg->req_cfg->importDocBook->table_row = "row"; +$tlCfg->req_cfg->importDocBook->table_entry = "entry"; +$tlCfg->req_cfg->importDocBook->list_item_children = array( + 'para', + 'title' +); +$tlCfg->req_cfg->importDocBook->table_entry_children = array( + 'para' +); + +// If an external tool is used for requirement management, enable this setting. +// You will get an additional field on requirement specifications where you +// can enter the total count of requirements so that external requirements +// are also counted for metrics/statistics. +$tlCfg->req_cfg->external_req_management = DISABLED; + +// If enabled an icon next to Document ID field will show up that allows +// to insert the last defined Requirement Document ID within the same Project +// into Document ID field +$tlCfg->req_cfg->allow_insertion_of_last_doc_id = DISABLED; + +// used ONLY to configure the mask (text) . +// algorithm type is fixed HARDCODED +// +$tlCfg->req_cfg->duplicated_name_algorithm = new stdClass(); +$tlCfg->req_cfg->duplicated_name_algorithm->text = " (%s)"; + +$tlCfg->req_cfg->duplicated_docid_algorithm = new stdClass(); +$tlCfg->req_cfg->duplicated_docid_algorithm->text = " (%s)"; + +// ---------------------------------------------------------------------------- +/* [TREE FILTER CONFIGURATION] */ + +/* + * In the following section, you can configure which filters shall be used + * in combination with the tree view for testcases and requirements. + * There are five available filter modes (four for the testcase tree, + * one for requirement documents), which are used for the different features + * as listed here: + * + * For Test Cases: + * --> execution_mode + * execution of testcases + * --> edit_mode + * create and edit testcases + * assign keywords to testcases + * assign requirements to testcases + * --> plan_mode + * assign testcase execution to users + * update linked versions for testplan + * set urgent testcases + * --> plan_add_mode + * add/remove testcases to/from testplan + * + * For Requirements: + * There are no modes defined, there is only one filter configuration. + * The filters configured here will be used for requirement editing. + */ +$tlCfg->tree_filter_cfg = new stdClass(); +$tlCfg->tree_filter_cfg->testcases = new stdClass(); +$tlCfg->tree_filter_cfg->requirements = new stdClass(); + +// These are the available filter modes for testcases: +$tlCfg->tree_filter_cfg->testcases->execution_mode = new stdClass(); +$tlCfg->tree_filter_cfg->testcases->edit_mode = new stdClass(); +$tlCfg->tree_filter_cfg->testcases->plan_mode = new stdClass(); +$tlCfg->tree_filter_cfg->testcases->plan_add_mode = new stdClass(); + +// If you disable one of these items here, you switch +// the complete filter panel off for a specific mode/feature. +// You should rather do this here instead of individually disabling all the filters, +// if you don't want to have any filters at all for a given feature. +$tlCfg->tree_filter_cfg->testcases->execution_mode->show_filters = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->show_filters = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->show_filters = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->show_filters = ENABLED; +$tlCfg->tree_filter_cfg->requirements->show_filters = ENABLED; + +// Detailed settings for each mode. +// Here, the single filter fields can be disabled if not wanted. +// Also, the choice of advanced filter mode can be disabled. +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_tc_id = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_testcase_name = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_toplevel_testsuite = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_keywords = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_priority = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_execution_type = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_assigned_user = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_custom_fields = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_result = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->filter_bugs = ENABLED; +$tlCfg->tree_filter_cfg->testcases->execution_mode->advanced_filter_mode_choice = ENABLED; + +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_tc_id = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_testcase_name = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_toplevel_testsuite = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_keywords = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_platforms = ENABLED; + +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_active_inactive = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_importance = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_execution_type = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_custom_fields = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_workflow_status = ENABLED; +$tlCfg->tree_filter_cfg->testcases->edit_mode->advanced_filter_mode_choice = ENABLED; + +$tlCfg->tree_filter_cfg->testcases->edit_mode->filter_workflow_status_values = array(); + +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_tc_id = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_testcase_name = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_toplevel_testsuite = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_keywords = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_priority = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_execution_type = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_assigned_user = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_custom_fields = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->filter_result = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->advanced_filter_mode_choice = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_mode->setting_build_inactive_out = false; +$tlCfg->tree_filter_cfg->testcases->plan_mode->setting_build_close_out = false; + +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_tc_id = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_testcase_name = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_toplevel_testsuite = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_keywords = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_active_inactive = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_importance = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_execution_type = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_workflow_status = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_custom_fields = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->advanced_filter_mode_choice = ENABLED; +$tlCfg->tree_filter_cfg->testcases->plan_add_mode->filter_platforms = ENABLED; + +$tlCfg->tree_filter_cfg->requirements->filter_doc_id = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_title = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_status = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_type = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_spec_type = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_coverage = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_relation = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_tc_id = ENABLED; +$tlCfg->tree_filter_cfg->requirements->filter_custom_fields = ENABLED; +$tlCfg->tree_filter_cfg->requirements->advanced_filter_mode_choice = ENABLED; + +// ENABLED -> Every time the user does some operation on requirement spec, +// the tree will be updated on screen. [DEFAULT] +// DISABLED -> The tree will not be updated automatically, but the user can update it manually. +// On graphical user interface any user will is able to change this setting. +$tlCfg->tree_filter_cfg->requirements->automatic_tree_refresh = ENABLED; + +/* [Assign test cases to test plan] */ +$tlCfg->tplanDesign->hideTestCaseWithStatusIn = array( + $tlCfg->testCaseStatus['obsolete'] => 'obsolete', + $tlCfg->testCaseStatus['future'] => 'future' +); + +// ---------------------------------------------------------------------------- +/* [MISC FUNCTIONALITY] */ + +/** + * Maximum uploadfile size to importing stuff in TL + */ +// Also check your PHP settings (default is usually 2MBs) +// unit BYTES is required by MAX_FILE_SIZE HTML option +$tlCfg->import_file_max_size_bytes = '800000'; + +/** + * Maximum line size of the imported file + */ +$tlCfg->import_max_row = '10000'; // in chars + +/** + * Set the default role used for new users + */ +// - created from the login page. +// - created using user management features +$tlCfg->default_roleid = TL_ROLES_GUEST; + +// only show custom fields if their value isn't empty +$tlCfg->custom_fields->show_custom_fields_without_value = true; + +/** + * used to check size in char for TEXT AREA type custom fields + */ +// can not be greater that column definition on DB +// 0 => do not check. +$tlCfg->custom_fields->max_length = 255; + +// sizes for HTML INPUTS +// for list, multiselection list +// - MAXIMUM number of items displayed at once +// +// for checkbox,radio is useless +// Hint: more than 120 produce weird effects on user interface +// +$tlCfg->custom_fields->sizes = array( + 'string' => 100, + 'numeric' => 10, + 'float' => 10, + 'email' => 100, + 'list' => 1, + 'multiselection list' => 5, + 'text area' => array( + 'rows' => 6, + 'cols' => 80 + ), + 'script' => 100, + 'server' => 100 +); + +// Use this variable (on custom_config.inc.php) to define new Custom Field types. +// IMPORTANT: +// check $custom_field_types property on cfield_mgr.class.php +// to avoid overwrite of standard types. +// +$tlCfg->gui->custom_fields->types = null; + +// Use this variable (on custom_config.inc.php) +// to define possible values behaviour for new Custom Field types. +// +// IMPORTANT: +// check $possible_values_cfg property on cfield_mgr.class.php +// to avoid overwrite of standard values. +// +$tlCfg->gui->custom_fields->possible_values_cfg = null; + +/** + * Check unique titles of Test Project, Test Suite and Test Case + * ENABLED => Check [STANDARD BEHAVIOUR] + * DISABLED => don't check + */ +$tlCfg->check_names_for_duplicates = ENABLED; + +/** + * Action for duplication check (only if check_names_for_duplicates=ENABLED) + * 'allow_repeat' => allow the name to be repeated (backward compatibility) + * 'generate_new' => generate a new name using $g_prefix_name_for_copy + * 'block' => return with an error + */ +$tlCfg->action_on_duplicate_name = 'generate_new'; + +/** + * String checking and conversions + * Allow automatically convert www URLs and email adresses into clickable links + * used by function string_display_links() for example by custom fields. + * Valid values = ENABLED/DISABLED. + */ +$tlCfg->html_make_links = ENABLED; + +/** + * Define the valid html tags for "content driven" single-line and multi-line fields. + * Do NOT include tags with parameters (eg. ), img and href. + * It's used by function string_display_links() for example by custom fields. + */ +$tlCfg->html_valid_tags = 'p, li, ul, ol, br, pre, i, b, u, em'; +$tlCfg->html_valid_tags_single_line = 'i, b, u, em'; + +/** + * Defines the threshold values for filtering TC by a priority according to the formula + * LOW => all Tc's with (urgency*importance) < LOW_Threshold + * HIGH => all Tc's with (urgency*importance) >= HIGH_Threshold + * MEDIUM => all Tc's with (urgency*importance) >= LOW_Threshold AND (urgency*importance) < HIGH_Threshold + */ +$tlCfg->urgencyImportance = new stdClass(); +$tlCfg->urgencyImportance->threshold['low'] = 3; +$tlCfg->urgencyImportance->threshold['high'] = 6; + +/** + * + * @var boolean Demo mode disables some functionality + * user edit disable + * role create ENABLED + * user create ENABLED + * special users manage DISABLE + */ +$tlCfg->demoMode = OFF; +$tlCfg->demoSpecialUsers = array( + 'admin' +); + +/** + * If enabled, every Ext JS table in TestLink will offer an export button, + * which generates a file with the contents of the table. + * ATTENTION: This feature is fully experimental. Enable at your own risk! + * Enabling it can cause broken tables. + */ +$tlCfg->enableTableExportButton = DISABLED; + +/** + * Taken from Mantis to implement better login security + * and solve TICKET 4342. + */ +$tlCfg->auth_cookie = $tlCfg->cookie->prefix . "TESTLINK_USER_AUTH_COOKIE"; + +/** + * Used when creating a Test Suite using copy + * and you have choose $g_action_on_duplicate_name = 'generate_new' + * if the name exist. + */ +$g_prefix_name_for_copy = date("Y-m-d-H:i:s", time()); + +/** + * Configurable templates this can help if you want to use a non standard template. + * i.e. you want to develop a new one without loosing the original template. + * key: original TL template name WITHOUT extension + * value: whatever name you want, only constrain you have to copy your template + * ON SAME FOLDER where original template is. + * See example below + */ +$g_tpl = array( + 'inc_exec_controls' => 'inc_exec_img_controls.tpl' +); +// $g_tpl = array('inc_exec_controls' => 'inc_exec_controls.tpl'); +$g_tpl['login'] = 'login/login-model-marcobiedermann.tpl'; + +// Example +// $g_tpl = array('tcView' => 'custom_tcView.tpl', +// 'tcSearchView' => 'myOwnTCSearchView.tpl', +// 'tcEdit' => 'tcEdit_ultraCool.tpl'); + +/** + * Add o replace images + */ +$tlCfg->images = array(); + +// ---------------------------------------------------------------------------- +/* [REST API using Slim - Begin] */ +$tlCfg->restAPI = new stdClass(); + +// CRITIC +// This will work if your url to test link +// is something like +// +// https://testlink.antartic.org/ +// +$tlCfg->restAPI->basePath = "/lib/api/rest/v3"; + +// If your URL is like this +// https://myserver.ibiza.org/testlink/ +// You need to use: +// $basePath = "/testlink/lib/api/rest/v3"; +// +// The standard .htaccess provided with testlink, +// that is similar to the .htaccess provided by MantisBT +// it's ok!!! +// No need to proceed as detailed in this documentation +// - https://www.slimframework.com/docs/v4/start/web-servers.html +// Section: Running in a sub-directory +// +// - https://akrabat.com/running-slim-4-in-a-subdirectory/ +// BUT this is a good example to understand how to configure +// +/* [REST API using Slim - End] */ + +// ---------------------------------------------------------------------------- +/* [PROXY] */ +/* Used only */ +/* mantissoapInterface.class.php */ +/* jirasoapInterface.class.php */ +/* jirarestInterface.class.php */ +$tlCfg->proxy->host = null; +$tlCfg->proxy->port = null; +$tlCfg->proxy->login = null; +$tlCfg->proxy->password = null; + +/** + * Plugins feature + */ +define('TL_PLUGIN_PATH', + dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR); + +// ----- End of Config ------------------------------------------------------------------ +// -------------------------------------------------------------------------------------- +// DO NOT DO CHANGES BELOW +// -------------------------------------------------------------------------------------- + +/** + * Functions for check request status + */ +require_once ('configCheck.php'); + +if (! defined('TL_JQUERY')) { + define('TL_JQUERY', 'jquery-2.2.4.min.js'); +} + +if (! defined('TL_DATATABLES_DIR')) { + define('TL_DATATABLES_DIR', 'DataTables-1.10.24'); +} + +/** + * root of testlink directory location seen through the web server + */ +/* + * 20070106 - franciscom - this statement it's not 100% right + * better use $_SESSION['basehref'] in the scripts. + */ +define('TL_BASE_HREF', + get_home_url(array( + 'force_https' => $tlCfg->force_https + ))); + +clearstatcache(); +if (file_exists(TL_ABS_PATH . 'custom_config.inc.php')) { + require_once (TL_ABS_PATH . 'custom_config.inc.php'); +} + +if (! isset($tlCfg->attachments->access_icon)) { + $tlCfg->attachments->access_icon = ''; +} + +// Important to do this only after custom_* to use (if exists) redefinition of +// $tlCfg->results['status_label_for_exec_ui'] +$tlCfg->reportsCfg->exec_status = $tlCfg->results['status_label_for_exec_ui']; + +/** + * Support for localization + */ +// @TODO move the code out of config and do it only once and +// not always in any include! +// @TODO a better parsing function should be include +$serverLanguage = false; +if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + @list ($code) = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']); + @list ($a, $b) = explode("-", $code); + if ($a && $b) { + $a = strtolower($a); + $b = strtoupper($a); + $serverLanguage = $a . "_" . $b; + } +} + +if (false !== $serverLanguage && + array_key_exists($serverLanguage, $tlCfg->locales)) { + $tlCfg->default_language = $serverLanguage; +} +define('TL_DEFAULT_LOCALE', $tlCfg->default_language); + +// Reverted execution status is used for two applications. +// 1. To map code to CSS, Please if you add an status you need to add a corresponding CSS Class +// in the CSS files (see the gui directory) +// 2. to decode from code to some more human oriented to use in code +// +/** + * Revered list of Test Case execution results + */ +$tlCfg->results['code_status'] = array_flip($tlCfg->results['status_code']); + +// Enable CSRF global protection +$tlCfg->csrf_filter_enabled = true; + +// --------------------------------------------------------------------------------- +/** + * Converted and derived variables (Users should not modify this section) + */ +define('REFRESH_SPEC_TREE', $tlCfg->spec_cfg->automatic_tree_refresh ? 1 : 0); +define('TL_SORT_TABLE_ENGINE', $g_sort_table_engine); +define("TL_REPOSITORY_MAXFILESIZE", + 1024 * 1024 * $tlCfg->repository_max_filesize); + +define('TL_XMLEXPORT_HEADER', + "charset . "\"?>\n"); + +// --------------------------------------------------------------------------------- +// when a role is deleted, a new role must be assigned to all users +// having role to be deleted +// A right choice seems to be using $g_default_roleid. +// You can change this adding a config line in custom_config.inc.php +// @TODO martin: remove - use directly $tlCfg->default_roleid; +$g_role_replace_for_deleted_roles = $tlCfg->default_roleid; + +/** + * Using "|" in the testsuite name causes malformed URLs + * regexp used to check for chars not allowed in: + * test project, test suite and testcase names. + * + * @todo martin: encode harm characters @see http://cz.php.net/urlencode (and remove the parameter) + * + * now is preg pattern + */ +$g_ereg_forbidden = "/[|]/i"; + +/** + * + * @todo remove from TL - unfinished refactorization; + * use $tlCfg instead of old variables and constants + */ +define('TL_IMPORT_ROW_MAX', $tlCfg->import_max_row); +define('TL_TPL_CHARSET', $tlCfg->charset); +define('TITLE_SEP', $tlCfg->gui_title_separator_1); +define('TITLE_SEP_TYPE2', $tlCfg->gui_title_separator_2); +define('TITLE_SEP_TYPE3', $tlCfg->gui_title_separator_2); // obsolete: use type 1,2 +define('TL_FRMWORKAREA_LEFT_FRAME_WIDTH', $tlCfg->frame_workarea_default_width); +define('TL_TEMP_PATH', $tlCfg->temp_dir); + +$tlCfg->gui->title_separator_1 = $tlCfg->gui_title_separator_1; +$tlCfg->gui->title_separator_2 = $tlCfg->gui_title_separator_2; +$tlCfg->gui->role_separator_open = $tlCfg->gui_separator_open; +$tlCfg->gui->role_separator_close = $tlCfg->gui_separator_close; + +$tlCfg->gui->version_separator_open = $tlCfg->gui_separator_open; +$tlCfg->gui->version_separator_close = $tlCfg->gui_separator_close; + +/** + * Globals for Events storage + */ +$g_event_cache = array(); + +/** + * Globals for Plugins + */ $g_plugin_config_cache = array(); // ----- END OF FILE -------------------------------------------------------------------- diff --git a/custom/_cf_radio_head.php b/custom/_cf_radio_head.php index 2ec9963a7f..1a1e949f59 100644 --- a/custom/_cf_radio_head.php +++ b/custom/_cf_radio_head.php @@ -1,86 +1,82 @@ -custom_fields->types = array(100 => 'radio head'); - * $tlCfg->custom_fields->possible_values_cfg = array('radio head' => 1); - * - * - * - * ----------------------------------------------------------------------------- -*/ -/* - function: string_input_radio_head - returns an string with the html need to display "radio head" custom field. - Is normally called by string_custom_field_input() - - args: p_field_def: contains the definition of the custom field - (including it's field id) - - p_input_name: html input name - - p_custom_field_value: html input value - htmlspecialchars() must be applied to this - argument by caller. - - returns: html string - - rev: based on Mantis 1.2.0a1 code - - */ -function string_input_radio_head($p_field_def, $p_input_name, $p_custom_field_value ) -{ - $str_out=''; - $t_values = explode( '|', $p_field_def['possible_values']); - $t_checked_values = explode( '|', $p_custom_field_value ); - foreach( $t_values as $t_option ) - { - $str_out .= ' ' . $t_option . '  '; - } - else - { - $str_out .= ' value="' . $t_option . '"> ' . $t_option . '  '; - } - } - return $str_out; -} - -/* - function: build_cfield_radio_head - support function useful for method used to write "radio head" CF values to db. - Is normally called by _build_cfield() - - args: custom_field_value: value to be converted to be written to db. - - returns: value converted - - - */ - -function build_cfield_radio_head($custom_field_value) -{ - if( count($value) > 1) - { - $value=implode('|',$custom_field_value); - } - else - { - $value=is_array($custom_field_value) ? $custom_field_value[0] :$custom_field_value; - } - return $value; - -} -?> \ No newline at end of file +custom_fields->types = array(100 => 'radio head'); + * $tlCfg->custom_fields->possible_values_cfg = array('radio head' => 1); + * + * + * + * ----------------------------------------------------------------------------- + */ +/* + * function: string_input_radio_head + * returns an string with the html need to display "radio head" custom field. + * Is normally called by string_custom_field_input() + * + * args: p_field_def: contains the definition of the custom field + * (including it's field id) + * + * p_input_name: html input name + * + * p_custom_field_value: html input value + * htmlspecialchars() must be applied to this + * argument by caller. + * + * returns: html string + * + * rev: based on Mantis 1.2.0a1 code + * + */ +function string_input_radio_head($p_field_def, $p_input_name, + $p_custom_field_value) +{ + $str_out = ''; + $t_values = explode('|', $p_field_def['possible_values']); + $t_checked_values = explode('|', $p_custom_field_value); + foreach ($t_values as $t_option) { + $str_out .= ' ' . + $t_option . '  '; + } else { + $str_out .= ' value="' . $t_option . '"> ' . $t_option . + '  '; + } + } + return $str_out; +} + +/* + * function: build_cfield_radio_head + * support function useful for method used to write "radio head" CF values to db. + * Is normally called by _build_cfield() + * + * args: custom_field_value: value to be converted to be written to db. + * + * returns: value converted + * + * + */ +function build_cfield_radio_head($custom_field_value) +{ + if (count($value) > 1) { + $value = implode('|', $custom_field_value); + } else { + $value = is_array($custom_field_value) ? $custom_field_value[0] : $custom_field_value; + } + return $value; +} +?> diff --git a/custom_config.inc.php.example b/custom_config.inc.php.example index 89a012365c..18da7ddde3 100644 --- a/custom_config.inc.php.example +++ b/custom_config.inc.php.example @@ -214,6 +214,8 @@ * - $tlCfg->results['charts']['status_colour'] (in this file) * - /locale//custom_strings.txt (see custom_strings.txt.example) * - /gui/themes/default/css/custom.css (see custom.css.example) + * - $tlCfg->results['status_icons_for_exec_ui'] = + * - $tlCfg->results['status_icons_for_exec_next_ui'] = */ // This Example shows how to add the status 'my_status' diff --git a/custom_config.inc.php.example.gitlab_oauth b/custom_config.inc.php.example.gitlab_oauth new file mode 100644 index 0000000000..efed807bff --- /dev/null +++ b/custom_config.inc.php.example.gitlab_oauth @@ -0,0 +1,32 @@ +OAuthServers['gitlab']['oauth_enabled'] = true; +$tlCfg->OAuthServers['gitlab']['oauth_name'] = 'gitlab'; +$tlCfg->OAuthServers['gitlab']['oauth_client_id'] = +'27a03c93d60b5ddb4e0cef92149678fbe37c099733605e046a5428a9da4177ba'; +$tlCfg->OAuthServers['gitlab']['oauth_client_secret'] = 'c157df291b81dbfd8084d38b155029baded3cf76c7449670bd2da889fe8b99eb'; +$tlCfg->OAuthServers['gitlab']['redirect_uri'] = 'http://fman.hopto.org/login.php?oauth=gitlab'; + + +// The following configuration options are not needed when using +// +// https://github.com/thephpleague/oauth2-client +// https://github.com/omines/oauth2-gitlab +// +// Can be authorization_code (by default), client_credentials or password +// +//$tlCfg->OAuthServers[2]['oauth_grant_type'] = 'authorization_code'; +//$tlCfg->OAuthServers[2]['oauth_url'] = 'https://gitlab.com/oauth/authorize'; +//$tlCfg->OAuthServers[2]['token_url'] = 'https://gitlab.com/oauth/token'; +//$tlCfg->OAuthServers[2]['oauth_force_single'] = false; +// +//$tlCfg->OAuthServers[2]['oauth_profile'] = 'https://api.github.com/user'; +//$tlCfg->OAuthServers[2]['oauth_profile'] = 'https://gitlab.com/api/v4/user'; +//$tlCfg->OAuthServers[2]['oauth_scope'] = 'read_user'; \ No newline at end of file diff --git a/custom_config.inc.php.github.testlinkOauth.php b/custom_config.inc.php.github.testlinkOauth.php new file mode 100644 index 0000000000..84d1aff379 --- /dev/null +++ b/custom_config.inc.php.github.testlinkOauth.php @@ -0,0 +1,28 @@ +OAuthServers[2]['redirect_uri'] = 'http://fman.hopto.org/login.php?oauth=github'; + +$tlCfg->OAuthServers[2]['oauth_client_id'] = 'aa5f70a8de342fb95043'; +$tlCfg->OAuthServers[2]['oauth_client_secret'] = 'c8d61d5ec4ed4eb2ac81064c27043ddef351107e'; + +$tlCfg->OAuthServers[2]['oauth_enabled'] = true; +$tlCfg->OAuthServers[2]['oauth_name'] = 'github'; + +// Can be authorization_code (by default), client_credentials or password +$tlCfg->OAuthServers[2]['oauth_grant_type'] = 'authorization_code'; +$tlCfg->OAuthServers[2]['oauth_url'] = 'https://github.com/login/oauth/authorize'; + +$tlCfg->OAuthServers[2]['token_url'] = 'https://github.com/login/oauth/access_token'; +$tlCfg->OAuthServers[2]['oauth_force_single'] = false; +$tlCfg->OAuthServers[2]['oauth_profile'] = 'https://api.github.com/user'; +$tlCfg->OAuthServers[2]['oauth_scope'] = 'user:email'; + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..f4235d21f7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,58 @@ +networks: + testlink: + name: testlink +services: + db: + image: mysql:8.3.0 + networks: + - testlink + restart: unless-stopped + user: mysql + environment: + - MYSQL_USER=teste + - MYSQL_PASSWORD=teste + - MYSQL_ROOT_PASSWORD=teste + - MYSQL_DATABASE=testlink + volumes: + - mysql:/var/lib/mysql + + maildev: + image: maildev/maildev:latest + networks: + - testlink + ports: + - 1080:1080 + - 1025:1025 + restart: unless-stopped + environment: + - NODE_TLS_REJECT_UNAUTHORIZED=0 + + app: &app + build: . + restart: unless-stopped + depends_on: + db: + condition: service_started + maildev: + condition: service_started + networks: + - testlink + ports: + - 8090:80 + volumes: + - ./logs:/var/testlink/logs:Z + - ./upload_area:/var/testlink/upload_area:Z + + restore: + <<: *app + depends_on: + app: + condition: service_started + restart: no + ports: [] + profiles: + - tools + command: ['/bin/bash', '-c', 'cd ./docs/db_sample && ./restore_sample.sh'] + +volumes: + mysql: diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000..b01fae3732 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,94 @@ +# Running Testlink using Docker + +## Create a dotenv file for Docker + +You're going to need a file named `.env` in the project root directory. You can use `.env.example` or create your own. + +```bash +cp -n .env.example .env +``` + +## Build the image + +To build a docker image of testlink, you can use the following command: + +```bash +docker build --tag testlink:1.9.20 --tag testlink:latest . +``` + +Alternatively, build without cached layers: + +```bash +docker build --no-cache --tag testlink-code:1.9.20 --tag testlink:latest . +``` + +## Starting up Testlink using `docker compose` + +```bash +docker compose up -d +``` + +You should now be able to open https://localhost:8080 in your browser to proceed with the Testlink setup + +### Database configuration + +Based on the default `docker-compose.yml` and `.env` configuration, you'll use the following settings for the database setup: + +| Key | Value | +| - | - | +| Database type | MySQL/MariaDB (5.6+ / 10.+) | +| Database host | testlink-mysql | +| Database admin login | root | +| Database admin password | masterkey | + +You can provide your own values for `Database name`, `TestLink DB login` and `TestLink DB password`. + +### Email configuration + +Copy the mail configuration to your installation with: + +```bash +cp -n docker/custom_config.inc.php ./ +``` + +You can view the test emails at http://localhost:1080 + +### Restoring the sample database + +There is a sample database in `docs/db_sample` which you can restore with: + +```bash +docker compose up testlink-restore +``` + +## Troubleshooting + +### Creating the Testlink database user manually + +You'll need to create the testlink user yourself should you be presented with the following error during setup: + +> 1045 - Access denied for user 'testlink'@'172.29.0.3' (using password: YES) +> TestLink ::: Fatal Error +> Connect to database testlink on Host testlink-mysql fails +> DBMS Error Message: 1045 - Access denied for user 'testlink'@'172.29.0.3' (using password: YES) + +Connect to the app or database container and, using the `mysql` CLI, execute the following commands: + +```sql +/* update the database name, user name and password + values based on what you specified during setup */ +USE `testlink`; +CREATE USER 'testlink'@'%' IDENTIFIED BY 'masterkey'; +GRANT SELECT, UPDATE, DELETE, INSERT ON *.* TO 'testlink'@'%' WITH GRANT OPTION; +``` + +### Resetting your database + +If you wish to reset your database, you'll need to delete the mysql volume and `config_db.inc.php`. + +```bash +docker compose down +docker volume rm testlink-code_mysql +/bin/rm -f config_db.inc.php +docker compose up -d +``` diff --git a/docker/custom_config.inc.php b/docker/custom_config.inc.php new file mode 100644 index 0000000000..a808a91318 --- /dev/null +++ b/docker/custom_config.inc.php @@ -0,0 +1,19 @@ +log_level = 'DEBUG'; + +# custom configuration file for email +$g_tl_admin_email = 'gandalf@teamst.org'; +$g_from_email = 'testlink_system@teamst.org'; +$g_return_path_email = 'no_reply@teamst.org'; +$g_smtp_host = 'testlink-maildev'; + +# Urgent = 1, Not Urgent = 5, Disable = 0 +$g_mail_priority = 5; + +$g_smtp_username = ''; +$g_smtp_password = ''; +$g_smtp_connection_mode = ''; +$g_smtp_port = 1025; + +?> diff --git a/docker/php.ini-production b/docker/php.ini-production new file mode 100644 index 0000000000..93a942e4c4 --- /dev/null +++ b/docker/php.ini-production @@ -0,0 +1,1933 @@ +[PHP] + +;;;;;;;;;;;;;;;;;;; +; About php.ini ; +;;;;;;;;;;;;;;;;;;; +; PHP's initialization file, generally called php.ini, is responsible for +; configuring many of the aspects of PHP's behavior. + +; PHP attempts to find and load this configuration from a number of locations. +; The following is a summary of its search order: +; 1. SAPI module specific location. +; 2. The PHPRC environment variable. (As of PHP 5.2.0) +; 3. A number of predefined registry keys on Windows (As of PHP 5.2.0) +; 4. Current working directory (except CLI) +; 5. The web server's directory (for SAPI modules), or directory of PHP +; (otherwise in Windows) +; 6. The directory from the --with-config-file-path compile time option, or the +; Windows directory (C:\windows or C:\winnt) +; See the PHP docs for more specific information. +; http://php.net/configuration.file + +; The syntax of the file is extremely simple. Whitespace and lines +; beginning with a semicolon are silently ignored (as you probably guessed). +; Section headers (e.g. [Foo]) are also silently ignored, even though +; they might mean something in the future. + +; Directives following the section heading [PATH=/www/mysite] only +; apply to PHP files in the /www/mysite directory. Directives +; following the section heading [HOST=www.example.com] only apply to +; PHP files served from www.example.com. Directives set in these +; special sections cannot be overridden by user-defined INI files or +; at runtime. Currently, [PATH=] and [HOST=] sections only work under +; CGI/FastCGI. +; http://php.net/ini.sections + +; Directives are specified using the following syntax: +; directive = value +; Directive names are *case sensitive* - foo=bar is different from FOO=bar. +; Directives are variables used to configure PHP or PHP extensions. +; There is no name validation. If PHP can't find an expected +; directive because it is not set or is mistyped, a default value will be used. + +; The value can be a string, a number, a PHP constant (e.g. E_ALL or M_PI), one +; of the INI constants (On, Off, True, False, Yes, No and None) or an expression +; (e.g. E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a +; previously set variable or directive (e.g. ${foo}) + +; Expressions in the INI file are limited to bitwise operators and parentheses: +; | bitwise OR +; ^ bitwise XOR +; & bitwise AND +; ~ bitwise NOT +; ! boolean NOT + +; Boolean flags can be turned on using the values 1, On, True or Yes. +; They can be turned off using the values 0, Off, False or No. + +; An empty string can be denoted by simply not writing anything after the equal +; sign, or by using the None keyword: + +; foo = ; sets foo to an empty string +; foo = None ; sets foo to an empty string +; foo = "None" ; sets foo to the string 'None' + +; If you use constants in your value, and these constants belong to a +; dynamically loaded extension (either a PHP extension or a Zend extension), +; you may only use these constants *after* the line that loads the extension. + +;;;;;;;;;;;;;;;;;;; +; About this file ; +;;;;;;;;;;;;;;;;;;; +; PHP comes packaged with two INI files. One that is recommended to be used +; in production environments and one that is recommended to be used in +; development environments. + +; php.ini-production contains settings which hold security, performance and +; best practices at its core. But please be aware, these settings may break +; compatibility with older or less security conscience applications. We +; recommending using the production ini in production and testing environments. + +; php.ini-development is very similar to its production variant, except it is +; much more verbose when it comes to errors. We recommend using the +; development version only in development environments, as errors shown to +; application users can inadvertently leak otherwise secure information. + +; This is php.ini-production INI file. + +;;;;;;;;;;;;;;;;;;; +; Quick Reference ; +;;;;;;;;;;;;;;;;;;; +; The following are all the settings which are different in either the production +; or development versions of the INIs with respect to PHP's default behavior. +; Please see the actual settings later in the document for more details as to why +; we recommend these changes in PHP's behavior. + +; display_errors +; Default Value: On +; Development Value: On +; Production Value: Off + +; display_startup_errors +; Default Value: Off +; Development Value: On +; Production Value: Off + +; error_reporting +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT + +; html_errors +; Default Value: On +; Development Value: On +; Production value: On + +; log_errors +; Default Value: Off +; Development Value: On +; Production Value: On + +; max_input_time +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) + +; output_buffering +; Default Value: Off +; Development Value: 4096 +; Production Value: 4096 + +; register_argc_argv +; Default Value: On +; Development Value: Off +; Production Value: Off + +; request_order +; Default Value: None +; Development Value: "GP" +; Production Value: "GP" + +; session.gc_divisor +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 + +; session.sid_bits_per_character +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 + +; short_open_tag +; Default Value: On +; Development Value: Off +; Production Value: Off + +; variables_order +; Default Value: "EGPCS" +; Development Value: "GPCS" +; Production Value: "GPCS" + +;;;;;;;;;;;;;;;;;;;; +; php.ini Options ; +;;;;;;;;;;;;;;;;;;;; +; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" +;user_ini.filename = ".user.ini" + +; To disable this feature set this option to empty value +;user_ini.filename = + +; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes) +;user_ini.cache_ttl = 300 + +;;;;;;;;;;;;;;;;;;;; +; Language Options ; +;;;;;;;;;;;;;;;;;;;; + +; Enable the PHP scripting language engine under Apache. +; http://php.net/engine +engine = On + +; This directive determines whether or not PHP will recognize code between +; tags as PHP source which should be processed as such. It is +; generally recommended that should be used and that this feature +; should be disabled, as enabling it may result in issues when generating XML +; documents, however this remains supported for backward compatibility reasons. +; Note that this directive does not control the would work. +; http://php.net/syntax-highlighting +;highlight.string = #DD0000 +;highlight.comment = #FF9900 +;highlight.keyword = #007700 +;highlight.default = #0000BB +;highlight.html = #000000 + +; If enabled, the request will be allowed to complete even if the user aborts +; the request. Consider enabling it if executing long requests, which may end up +; being interrupted by the user or a browser timing out. PHP's default behavior +; is to disable this feature. +; http://php.net/ignore-user-abort +;ignore_user_abort = On + +; Determines the size of the realpath cache to be used by PHP. This value should +; be increased on systems where PHP opens many files to reflect the quantity of +; the file operations performed. +; Note: if open_basedir is set, the cache is disabled +; http://php.net/realpath-cache-size +;realpath_cache_size = 4096k + +; Duration of time, in seconds for which to cache realpath information for a given +; file or directory. For systems with rarely changing files, consider increasing this +; value. +; http://php.net/realpath-cache-ttl +;realpath_cache_ttl = 120 + +; Enables or disables the circular reference collector. +; http://php.net/zend.enable-gc +zend.enable_gc = On + +; If enabled, scripts may be written in encodings that are incompatible with +; the scanner. CP936, Big5, CP949 and Shift_JIS are the examples of such +; encodings. To use this feature, mbstring extension must be enabled. +; Default: Off +;zend.multibyte = Off + +; Allows to set the default encoding for the scripts. This value will be used +; unless "declare(encoding=...)" directive appears at the top of the script. +; Only affects if zend.multibyte is set. +; Default: "" +;zend.script_encoding = + +;;;;;;;;;;;;;;;;; +; Miscellaneous ; +;;;;;;;;;;;;;;;;; + +; Decides whether PHP may expose the fact that it is installed on the server +; (e.g. by adding its signature to the Web server header). It is no security +; threat in any way, but it makes it possible to determine whether you use PHP +; on your server or not. +; http://php.net/expose-php +expose_php = On + +;;;;;;;;;;;;;;;;;;; +; Resource Limits ; +;;;;;;;;;;;;;;;;;;; + +; Maximum execution time of each script, in seconds +; http://php.net/max-execution-time +; Note: This directive is hardcoded to 0 for the CLI SAPI +max_execution_time = 120 + +; Maximum amount of time each script may spend parsing request data. It's a good +; idea to limit this time on productions servers in order to eliminate unexpectedly +; long running scripts. +; Note: This directive is hardcoded to -1 for the CLI SAPI +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) +; http://php.net/max-input-time +max_input_time = 60 + +; Maximum input variable nesting level +; http://php.net/max-input-nesting-level +;max_input_nesting_level = 64 + +; How many GET/POST/COOKIE input variables may be accepted +; max_input_vars = 1000 + +; Maximum amount of memory a script may consume (128MB) +; http://php.net/memory-limit +memory_limit = 128M + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Error handling and logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; This directive informs PHP of which errors, warnings and notices you would like +; it to take action for. The recommended way of setting values for this +; directive is through the use of the error level constants and bitwise +; operators. The error level constants are below here for convenience as well as +; some common settings and their meanings. +; By default, PHP is set to take action on all errors, notices and warnings EXCEPT +; those related to E_NOTICE and E_STRICT, which together cover best practices and +; recommended coding standards in PHP. For performance reasons, this is the +; recommend error reporting setting. Your production server shouldn't be wasting +; resources complaining about best practices and coding standards. That's what +; development servers and development settings are for. +; Note: The php.ini-development file has this setting as E_ALL. This +; means it pretty much reports everything which is exactly what you want during +; development and early testing. +; +; Error Level Constants: +; E_ALL - All errors and warnings (includes E_STRICT as of PHP 5.4.0) +; E_ERROR - fatal run-time errors +; E_RECOVERABLE_ERROR - almost fatal run-time errors +; E_WARNING - run-time warnings (non-fatal errors) +; E_PARSE - compile-time parse errors +; E_NOTICE - run-time notices (these are warnings which often result +; from a bug in your code, but it's possible that it was +; intentional (e.g., using an uninitialized variable and +; relying on the fact it is automatically initialized to an +; empty string) +; E_STRICT - run-time notices, enable to have PHP suggest changes +; to your code which will ensure the best interoperability +; and forward compatibility of your code +; E_CORE_ERROR - fatal errors that occur during PHP's initial startup +; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's +; initial startup +; E_COMPILE_ERROR - fatal compile-time errors +; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) +; E_USER_ERROR - user-generated error message +; E_USER_WARNING - user-generated warning message +; E_USER_NOTICE - user-generated notice message +; E_DEPRECATED - warn about code that will not work in future versions +; of PHP +; E_USER_DEPRECATED - user-generated deprecation warnings +; +; Common Values: +; E_ALL (Show all errors, warnings and notices including coding standards.) +; E_ALL & ~E_NOTICE (Show all errors, except for notices) +; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) +; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT +; http://php.net/error-reporting +error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT + +; This directive controls whether or not and where PHP will output errors, +; notices and warnings too. Error output is very useful during development, but +; it could be very dangerous in production environments. Depending on the code +; which is triggering the error, sensitive information could potentially leak +; out of your application such as database usernames and passwords or worse. +; For production environments, we recommend logging errors rather than +; sending them to STDOUT. +; Possible Values: +; Off = Do not display any errors +; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) +; On or stdout = Display errors to STDOUT +; Default Value: On +; Development Value: On +; Production Value: Off +; http://php.net/display-errors +display_errors = Off + +; The display of errors which occur during PHP's startup sequence are handled +; separately from display_errors. PHP's default behavior is to suppress those +; errors from clients. Turning the display of startup errors on can be useful in +; debugging configuration problems. We strongly recommend you +; set this to 'off' for production servers. +; Default Value: Off +; Development Value: On +; Production Value: Off +; http://php.net/display-startup-errors +display_startup_errors = Off + +; Besides displaying errors, PHP can also log errors to locations such as a +; server-specific log, STDERR, or a location specified by the error_log +; directive found below. While errors should not be displayed on productions +; servers they should still be monitored and logging is a great way to do that. +; Default Value: Off +; Development Value: On +; Production Value: On +; http://php.net/log-errors +log_errors = On + +; Set maximum length of log_errors. In error_log information about the source is +; added. The default is 1024 and 0 allows to not apply any maximum length at all. +; http://php.net/log-errors-max-len +log_errors_max_len = 1024 + +; Do not log repeated messages. Repeated errors must occur in same file on same +; line unless ignore_repeated_source is set true. +; http://php.net/ignore-repeated-errors +ignore_repeated_errors = Off + +; Ignore source of message when ignoring repeated messages. When this setting +; is On you will not log errors with repeated messages from different files or +; source lines. +; http://php.net/ignore-repeated-source +ignore_repeated_source = Off + +; If this parameter is set to Off, then memory leaks will not be shown (on +; stdout or in the log). This has only effect in a debug compile, and if +; error reporting includes E_WARNING in the allowed list +; http://php.net/report-memleaks +report_memleaks = On + +; This setting is on by default. +;report_zend_debug = 0 + +; Store the last error/warning message in $php_errormsg (boolean). Setting this value +; to On can assist in debugging and is appropriate for development servers. It should +; however be disabled on production servers. +; This directive is DEPRECATED. +; Default Value: Off +; Development Value: Off +; Production Value: Off +; http://php.net/track-errors +;track_errors = Off + +; Turn off normal error reporting and emit XML-RPC error XML +; http://php.net/xmlrpc-errors +;xmlrpc_errors = 0 + +; An XML-RPC faultCode +;xmlrpc_error_number = 0 + +; When PHP displays or logs an error, it has the capability of formatting the +; error message as HTML for easier reading. This directive controls whether +; the error message is formatted as HTML or not. +; Note: This directive is hardcoded to Off for the CLI SAPI +; Default Value: On +; Development Value: On +; Production value: On +; http://php.net/html-errors +html_errors = On + +; If html_errors is set to On *and* docref_root is not empty, then PHP +; produces clickable error messages that direct to a page describing the error +; or function causing the error in detail. +; You can download a copy of the PHP manual from http://php.net/docs +; and change docref_root to the base URL of your local copy including the +; leading '/'. You must also specify the file extension being used including +; the dot. PHP's default behavior is to leave these settings empty, in which +; case no links to documentation are generated. +; Note: Never use this feature for production boxes. +; http://php.net/docref-root +; Examples +;docref_root = "/phpmanual/" + +; http://php.net/docref-ext +;docref_ext = .html + +; String to output before an error message. PHP's default behavior is to leave +; this setting blank. +; http://php.net/error-prepend-string +; Example: +;error_prepend_string = "" + +; String to output after an error message. PHP's default behavior is to leave +; this setting blank. +; http://php.net/error-append-string +; Example: +;error_append_string = "" + +; Log errors to specified file. PHP's default behavior is to leave this value +; empty. +; http://php.net/error-log +; Example: +;error_log = php_errors.log +; Log errors to syslog (Event Log on Windows). +;error_log = syslog + +;windows.show_crt_warning +; Default value: 0 +; Development value: 0 +; Production value: 0 + +;;;;;;;;;;;;;;;;; +; Data Handling ; +;;;;;;;;;;;;;;;;; + +; The separator used in PHP generated URLs to separate arguments. +; PHP's default setting is "&". +; http://php.net/arg-separator.output +; Example: +;arg_separator.output = "&" + +; List of separator(s) used by PHP to parse input URLs into variables. +; PHP's default setting is "&". +; NOTE: Every character in this directive is considered as separator! +; http://php.net/arg-separator.input +; Example: +;arg_separator.input = ";&" + +; This directive determines which super global arrays are registered when PHP +; starts up. G,P,C,E & S are abbreviations for the following respective super +; globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty +; paid for the registration of these arrays and because ENV is not as commonly +; used as the others, ENV is not recommended on productions servers. You +; can still get access to the environment variables through getenv() should you +; need to. +; Default Value: "EGPCS" +; Development Value: "GPCS" +; Production Value: "GPCS"; +; http://php.net/variables-order +variables_order = "GPCS" + +; This directive determines which super global data (G,P & C) should be +; registered into the super global array REQUEST. If so, it also determines +; the order in which that data is registered. The values for this directive +; are specified in the same manner as the variables_order directive, +; EXCEPT one. Leaving this value empty will cause PHP to use the value set +; in the variables_order directive. It does not mean it will leave the super +; globals array REQUEST empty. +; Default Value: None +; Development Value: "GP" +; Production Value: "GP" +; http://php.net/request-order +request_order = "GP" + +; This directive determines whether PHP registers $argv & $argc each time it +; runs. $argv contains an array of all the arguments passed to PHP when a script +; is invoked. $argc contains an integer representing the number of arguments +; that were passed when the script was invoked. These arrays are extremely +; useful when running scripts from the command line. When this directive is +; enabled, registering these variables consumes CPU cycles and memory each time +; a script is executed. For performance reasons, this feature should be disabled +; on production servers. +; Note: This directive is hardcoded to On for the CLI SAPI +; Default Value: On +; Development Value: Off +; Production Value: Off +; http://php.net/register-argc-argv +register_argc_argv = Off + +; When enabled, the ENV, REQUEST and SERVER variables are created when they're +; first used (Just In Time) instead of when the script starts. If these +; variables are not used within a script, having this directive on will result +; in a performance gain. The PHP directive register_argc_argv must be disabled +; for this directive to have any affect. +; http://php.net/auto-globals-jit +auto_globals_jit = On + +; Whether PHP will read the POST data. +; This option is enabled by default. +; Most likely, you won't want to disable this option globally. It causes $_POST +; and $_FILES to always be empty; the only way you will be able to read the +; POST data will be through the php://input stream wrapper. This can be useful +; to proxy requests or to process the POST data in a memory efficient fashion. +; http://php.net/enable-post-data-reading +;enable_post_data_reading = Off + +; Maximum size of POST data that PHP will accept. +; Its value may be 0 to disable the limit. It is ignored if POST data reading +; is disabled through enable_post_data_reading. +; http://php.net/post-max-size +post_max_size = 8M + +; Automatically add files before PHP document. +; http://php.net/auto-prepend-file +auto_prepend_file = + +; Automatically add files after PHP document. +; http://php.net/auto-append-file +auto_append_file = + +; By default, PHP will output a media type using the Content-Type header. To +; disable this, simply set it to be empty. +; +; PHP's built-in default media type is set to text/html. +; http://php.net/default-mimetype +default_mimetype = "text/html" + +; PHP's default character set is set to UTF-8. +; http://php.net/default-charset +default_charset = "UTF-8" + +; PHP internal character encoding is set to empty. +; If empty, default_charset is used. +; http://php.net/internal-encoding +;internal_encoding = + +; PHP input character encoding is set to empty. +; If empty, default_charset is used. +; http://php.net/input-encoding +;input_encoding = + +; PHP output character encoding is set to empty. +; If empty, default_charset is used. +; See also output_buffer. +; http://php.net/output-encoding +;output_encoding = + +;;;;;;;;;;;;;;;;;;;;;;;;; +; Paths and Directories ; +;;;;;;;;;;;;;;;;;;;;;;;;; + +; UNIX: "/path1:/path2" +;include_path = ".:/php/includes" +; +; Windows: "\path1;\path2" +;include_path = ".;c:\php\includes" +; +; PHP's default setting for include_path is ".;/path/to/php/pear" +; http://php.net/include-path + +; The root of the PHP pages, used only if nonempty. +; if PHP was not compiled with FORCE_REDIRECT, you SHOULD set doc_root +; if you are running php as a CGI under any web server (other than IIS) +; see documentation for security issues. The alternate is to use the +; cgi.force_redirect configuration below +; http://php.net/doc-root +doc_root = + +; The directory under which PHP opens the script using /~username used only +; if nonempty. +; http://php.net/user-dir +user_dir = + +; Directory in which the loadable extensions (modules) reside. +; http://php.net/extension-dir +; extension_dir = "./" +; On windows: +; extension_dir = "ext" + +; Directory where the temporary files should be placed. +; Defaults to the system default (see sys_get_temp_dir) +; sys_temp_dir = "/tmp" + +; Whether or not to enable the dl() function. The dl() function does NOT work +; properly in multithreaded servers, such as IIS or Zeus, and is automatically +; disabled on them. +; http://php.net/enable-dl +enable_dl = Off + +; cgi.force_redirect is necessary to provide security running PHP as a CGI under +; most web servers. Left undefined, PHP turns this on by default. You can +; turn it off here AT YOUR OWN RISK +; **You CAN safely turn this off for IIS, in fact, you MUST.** +; http://php.net/cgi.force-redirect +;cgi.force_redirect = 1 + +; if cgi.nph is enabled it will force cgi to always sent Status: 200 with +; every request. PHP's default behavior is to disable this feature. +;cgi.nph = 1 + +; if cgi.force_redirect is turned on, and you are not running under Apache or Netscape +; (iPlanet) web servers, you MAY need to set an environment variable name that PHP +; will look for to know it is OK to continue execution. Setting this variable MAY +; cause security issues, KNOW WHAT YOU ARE DOING FIRST. +; http://php.net/cgi.redirect-status-env +;cgi.redirect_status_env = + +; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's +; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok +; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting +; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting +; of zero causes PHP to behave as before. Default is 1. You should fix your scripts +; to use SCRIPT_FILENAME rather than PATH_TRANSLATED. +; http://php.net/cgi.fix-pathinfo +;cgi.fix_pathinfo=1 + +; if cgi.discard_path is enabled, the PHP CGI binary can safely be placed outside +; of the web tree and people will not be able to circumvent .htaccess security. +; http://php.net/cgi.dicard-path +;cgi.discard_path=1 + +; FastCGI under IIS (on WINNT based OS) supports the ability to impersonate +; security tokens of the calling client. This allows IIS to define the +; security context that the request runs under. mod_fastcgi under Apache +; does not currently support this feature (03/17/2002) +; Set to 1 if running under IIS. Default is zero. +; http://php.net/fastcgi.impersonate +;fastcgi.impersonate = 1 + +; Disable logging through FastCGI connection. PHP's default behavior is to enable +; this feature. +;fastcgi.logging = 0 + +; cgi.rfc2616_headers configuration option tells PHP what type of headers to +; use when sending HTTP response code. If set to 0, PHP sends Status: header that +; is supported by Apache. When this option is set to 1, PHP will send +; RFC2616 compliant header. +; Default is zero. +; http://php.net/cgi.rfc2616-headers +;cgi.rfc2616_headers = 0 + +; cgi.check_shebang_line controls whether CGI PHP checks for line starting with #! +; (shebang) at the top of the running script. This line might be needed if the +; script support running both as stand-alone script and via PHP CGI<. PHP in CGI +; mode skips this line and ignores its content if this directive is turned on. +; http://php.net/cgi.check-shebang-line +;cgi.check_shebang_line=1 + +;;;;;;;;;;;;;;;; +; File Uploads ; +;;;;;;;;;;;;;;;; + +; Whether to allow HTTP file uploads. +; http://php.net/file-uploads +file_uploads = On + +; Temporary directory for HTTP uploaded files (will use system default if not +; specified). +; http://php.net/upload-tmp-dir +;upload_tmp_dir = + +; Maximum allowed size for uploaded files. +; http://php.net/upload-max-filesize +upload_max_filesize = 2M + +; Maximum number of files that can be uploaded via a single request +max_file_uploads = 20 + +;;;;;;;;;;;;;;;;;; +; Fopen wrappers ; +;;;;;;;;;;;;;;;;;; + +; Whether to allow the treatment of URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-fopen +allow_url_fopen = On + +; Whether to allow include/require to open URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-include +allow_url_include = Off + +; Define the anonymous ftp password (your email address). PHP's default setting +; for this is empty. +; http://php.net/from +;from="john@doe.com" + +; Define the User-Agent string. PHP's default setting for this is empty. +; http://php.net/user-agent +;user_agent="PHP" + +; Default timeout for socket based streams (seconds) +; http://php.net/default-socket-timeout +default_socket_timeout = 60 + +; If your scripts have to deal with files from Macintosh systems, +; or you are running on a Mac and need to deal with files from +; unix or win32 systems, setting this flag will cause PHP to +; automatically detect the EOL character in those files so that +; fgets() and file() will work regardless of the source of the file. +; http://php.net/auto-detect-line-endings +;auto_detect_line_endings = Off + +;;;;;;;;;;;;;;;;;;;;;; +; Dynamic Extensions ; +;;;;;;;;;;;;;;;;;;;;;; + +; If you wish to have an extension loaded automatically, use the following +; syntax: +; +; extension=modulename +; +; For example: +; +; extension=mysqli +; +; When the extension library to load is not located in the default extension +; directory, You may specify an absolute path to the library file: +; +; extension=/path/to/extension/mysqli.so +; +; Note : The syntax used in previous PHP versions ('extension=.so' and +; 'extension='php_.dll') is supported for legacy reasons and may be +; deprecated in a future PHP major version. So, when it is possible, please +; move to the new ('extension=) syntax. +; +; Notes for Windows environments : +; +; - Many DLL files are located in the extensions/ (PHP 4) or ext/ (PHP 5+) +; extension folders as well as the separate PECL DLL download (PHP 5+). +; Be sure to appropriately set the extension_dir directive. +; +;extension=bz2 +;extension=curl +;extension=fileinfo +;extension=gd2 +;extension=gettext +;extension=gmp +;extension=intl +;extension=imap +;extension=interbase +;extension=ldap +;extension=mbstring +;extension=exif ; Must be after mbstring as it depends on it +;extension=mysqli +;extension=oci8_12c ; Use with Oracle Database 12c Instant Client +;extension=odbc +;extension=openssl +;extension=pdo_firebird +;extension=pdo_mysql +;extension=pdo_oci +;extension=pdo_odbc +;extension=pdo_pgsql +;extension=pdo_sqlite +;extension=pgsql +;extension=shmop + +; The MIBS data available in the PHP distribution must be installed. +; See http://www.php.net/manual/en/snmp.installation.php +;extension=snmp + +;extension=soap +;extension=sockets +;extension=sqlite3 +;extension=tidy +;extension=xmlrpc +;extension=xsl + +;;;;;;;;;;;;;;;;;;; +; Module Settings ; +;;;;;;;;;;;;;;;;;;; + +[CLI Server] +; Whether the CLI web server uses ANSI color coding in its terminal output. +cli_server.color = On + +[Date] +; Defines the default timezone used by the date functions +; http://php.net/date.timezone +;date.timezone = + +; http://php.net/date.default-latitude +;date.default_latitude = 31.7667 + +; http://php.net/date.default-longitude +;date.default_longitude = 35.2333 + +; http://php.net/date.sunrise-zenith +;date.sunrise_zenith = 90.583333 + +; http://php.net/date.sunset-zenith +;date.sunset_zenith = 90.583333 + +[filter] +; http://php.net/filter.default +;filter.default = unsafe_raw + +; http://php.net/filter.default-flags +;filter.default_flags = + +[iconv] +; Use of this INI entry is deprecated, use global input_encoding instead. +; If empty, default_charset or input_encoding or iconv.input_encoding is used. +; The precedence is: default_charset < intput_encoding < iconv.input_encoding +;iconv.input_encoding = + +; Use of this INI entry is deprecated, use global internal_encoding instead. +; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. +; The precedence is: default_charset < internal_encoding < iconv.internal_encoding +;iconv.internal_encoding = + +; Use of this INI entry is deprecated, use global output_encoding instead. +; If empty, default_charset or output_encoding or iconv.output_encoding is used. +; The precedence is: default_charset < output_encoding < iconv.output_encoding +; To use an output encoding conversion, iconv's output handler must be set +; otherwise output encoding conversion cannot be performed. +;iconv.output_encoding = + +[imap] +; rsh/ssh logins are disabled by default. Use this INI entry if you want to +; enable them. Note that the IMAP library does not filter mailbox names before +; passing them to rsh/ssh command, thus passing untrusted data to this function +; with rsh/ssh enabled is insecure. +;imap.enable_insecure_rsh=0 + +[intl] +;intl.default_locale = +; This directive allows you to produce PHP errors when some error +; happens within intl functions. The value is the level of the error produced. +; Default is 0, which does not produce any errors. +;intl.error_level = E_WARNING +;intl.use_exceptions = 0 + +[sqlite3] +; Directory pointing to SQLite3 extensions +; http://php.net/sqlite3.extension-dir +;sqlite3.extension_dir = + +; SQLite defensive mode flag (only available from SQLite 3.26+) +; When the defensive flag is enabled, language features that allow ordinary +; SQL to deliberately corrupt the database file are disabled. This forbids +; writing directly to the schema, shadow tables (eg. FTS data tables), or +; the sqlite_dbpage virtual table. +; https://www.sqlite.org/c3ref/c_dbconfig_defensive.html +; (for older SQLite versions, this flag has no use) +;sqlite3.defensive = 1 + +[Pcre] +;PCRE library backtracking limit. +; http://php.net/pcre.backtrack-limit +;pcre.backtrack_limit=100000 + +;PCRE library recursion limit. +;Please note that if you set this value to a high number you may consume all +;the available process stack and eventually crash PHP (due to reaching the +;stack size limit imposed by the Operating System). +; http://php.net/pcre.recursion-limit +;pcre.recursion_limit=100000 + +;Enables or disables JIT compilation of patterns. This requires the PCRE +;library to be compiled with JIT support. +;pcre.jit=1 + +[Pdo] +; Whether to pool ODBC connections. Can be one of "strict", "relaxed" or "off" +; http://php.net/pdo-odbc.connection-pooling +;pdo_odbc.connection_pooling=strict + +;pdo_odbc.db2_instance_name + +[Pdo_mysql] +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/pdo_mysql.cache_size +pdo_mysql.cache_size = 2000 + +; Default socket name for local MySQL connects. If empty, uses the built-in +; MySQL defaults. +; http://php.net/pdo_mysql.default-socket +pdo_mysql.default_socket= + +[Phar] +; http://php.net/phar.readonly +;phar.readonly = On + +; http://php.net/phar.require-hash +;phar.require_hash = On + +;phar.cache_list = + +[mail function] +; For Win32 only. +; http://php.net/smtp +SMTP = localhost +; http://php.net/smtp-port +smtp_port = 25 + +; For Win32 only. +; http://php.net/sendmail-from +;sendmail_from = me@example.com + +; For Unix only. You may supply arguments as well (default: "sendmail -t -i"). +; http://php.net/sendmail-path +;sendmail_path = + +; Force the addition of the specified parameters to be passed as extra parameters +; to the sendmail binary. These parameters will always replace the value of +; the 5th parameter to mail(). +;mail.force_extra_parameters = + +; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename +mail.add_x_header = Off + +; The path to a log file that will log all mail() calls. Log entries include +; the full path of the script, line number, To address and headers. +;mail.log = +; Log mail to syslog (Event Log on Windows). +;mail.log = syslog + +[ODBC] +; http://php.net/odbc.default-db +;odbc.default_db = Not yet implemented + +; http://php.net/odbc.default-user +;odbc.default_user = Not yet implemented + +; http://php.net/odbc.default-pw +;odbc.default_pw = Not yet implemented + +; Controls the ODBC cursor model. +; Default: SQL_CURSOR_STATIC (default). +;odbc.default_cursortype + +; Allow or prevent persistent links. +; http://php.net/odbc.allow-persistent +odbc.allow_persistent = On + +; Check that a connection is still valid before reuse. +; http://php.net/odbc.check-persistent +odbc.check_persistent = On + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/odbc.max-persistent +odbc.max_persistent = -1 + +; Maximum number of links (persistent + non-persistent). -1 means no limit. +; http://php.net/odbc.max-links +odbc.max_links = -1 + +; Handling of LONG fields. Returns number of bytes to variables. 0 means +; passthru. +; http://php.net/odbc.defaultlrl +odbc.defaultlrl = 4096 + +; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. +; See the documentation on odbc_binmode and odbc_longreadlen for an explanation +; of odbc.defaultlrl and odbc.defaultbinmode +; http://php.net/odbc.defaultbinmode +odbc.defaultbinmode = 1 + +;birdstep.max_links = -1 + +[Interbase] +; Allow or prevent persistent links. +ibase.allow_persistent = 1 + +; Maximum number of persistent links. -1 means no limit. +ibase.max_persistent = -1 + +; Maximum number of links (persistent + non-persistent). -1 means no limit. +ibase.max_links = -1 + +; Default database name for ibase_connect(). +;ibase.default_db = + +; Default username for ibase_connect(). +;ibase.default_user = + +; Default password for ibase_connect(). +;ibase.default_password = + +; Default charset for ibase_connect(). +;ibase.default_charset = + +; Default timestamp format. +ibase.timestampformat = "%Y-%m-%d %H:%M:%S" + +; Default date format. +ibase.dateformat = "%Y-%m-%d" + +; Default time format. +ibase.timeformat = "%H:%M:%S" + +[MySQLi] + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/mysqli.max-persistent +mysqli.max_persistent = -1 + +; Allow accessing, from PHP's perspective, local files with LOAD DATA statements +; http://php.net/mysqli.allow_local_infile +;mysqli.allow_local_infile = On + +; Allow or prevent persistent links. +; http://php.net/mysqli.allow-persistent +mysqli.allow_persistent = On + +; Maximum number of links. -1 means no limit. +; http://php.net/mysqli.max-links +mysqli.max_links = -1 + +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/mysqli.cache_size +mysqli.cache_size = 2000 + +; Default port number for mysqli_connect(). If unset, mysqli_connect() will use +; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the +; compile-time value defined MYSQL_PORT (in that order). Win32 will only look +; at MYSQL_PORT. +; http://php.net/mysqli.default-port +mysqli.default_port = 3306 + +; Default socket name for local MySQL connects. If empty, uses the built-in +; MySQL defaults. +; http://php.net/mysqli.default-socket +mysqli.default_socket = + +; Default host for mysql_connect() (doesn't apply in safe mode). +; http://php.net/mysqli.default-host +mysqli.default_host = + +; Default user for mysql_connect() (doesn't apply in safe mode). +; http://php.net/mysqli.default-user +mysqli.default_user = + +; Default password for mysqli_connect() (doesn't apply in safe mode). +; Note that this is generally a *bad* idea to store passwords in this file. +; *Any* user with PHP access can run 'echo get_cfg_var("mysqli.default_pw") +; and reveal this password! And of course, any users with read access to this +; file will be able to reveal the password as well. +; http://php.net/mysqli.default-pw +mysqli.default_pw = + +; Allow or prevent reconnect +mysqli.reconnect = Off + +[mysqlnd] +; Enable / Disable collection of general statistics by mysqlnd which can be +; used to tune and monitor MySQL operations. +; http://php.net/mysqlnd.collect_statistics +mysqlnd.collect_statistics = On + +; Enable / Disable collection of memory usage statistics by mysqlnd which can be +; used to tune and monitor MySQL operations. +; http://php.net/mysqlnd.collect_memory_statistics +mysqlnd.collect_memory_statistics = Off + +; Records communication from all extensions using mysqlnd to the specified log +; file. +; http://php.net/mysqlnd.debug +;mysqlnd.debug = + +; Defines which queries will be logged. +; http://php.net/mysqlnd.log_mask +;mysqlnd.log_mask = 0 + +; Default size of the mysqlnd memory pool, which is used by result sets. +; http://php.net/mysqlnd.mempool_default_size +;mysqlnd.mempool_default_size = 16000 + +; Size of a pre-allocated buffer used when sending commands to MySQL in bytes. +; http://php.net/mysqlnd.net_cmd_buffer_size +;mysqlnd.net_cmd_buffer_size = 2048 + +; Size of a pre-allocated buffer used for reading data sent by the server in +; bytes. +; http://php.net/mysqlnd.net_read_buffer_size +;mysqlnd.net_read_buffer_size = 32768 + +; Timeout for network requests in seconds. +; http://php.net/mysqlnd.net_read_timeout +;mysqlnd.net_read_timeout = 31536000 + +; SHA-256 Authentication Plugin related. File with the MySQL server public RSA +; key. +; http://php.net/mysqlnd.sha256_server_public_key +;mysqlnd.sha256_server_public_key = + +[OCI8] + +; Connection: Enables privileged connections using external +; credentials (OCI_SYSOPER, OCI_SYSDBA) +; http://php.net/oci8.privileged-connect +;oci8.privileged_connect = Off + +; Connection: The maximum number of persistent OCI8 connections per +; process. Using -1 means no limit. +; http://php.net/oci8.max-persistent +;oci8.max_persistent = -1 + +; Connection: The maximum number of seconds a process is allowed to +; maintain an idle persistent connection. Using -1 means idle +; persistent connections will be maintained forever. +; http://php.net/oci8.persistent-timeout +;oci8.persistent_timeout = -1 + +; Connection: The number of seconds that must pass before issuing a +; ping during oci_pconnect() to check the connection validity. When +; set to 0, each oci_pconnect() will cause a ping. Using -1 disables +; pings completely. +; http://php.net/oci8.ping-interval +;oci8.ping_interval = 60 + +; Connection: Set this to a user chosen connection class to be used +; for all pooled server requests with Oracle 11g Database Resident +; Connection Pooling (DRCP). To use DRCP, this value should be set to +; the same string for all web servers running the same application, +; the database pool must be configured, and the connection string must +; specify to use a pooled server. +;oci8.connection_class = + +; High Availability: Using On lets PHP receive Fast Application +; Notification (FAN) events generated when a database node fails. The +; database must also be configured to post FAN events. +;oci8.events = Off + +; Tuning: This option enables statement caching, and specifies how +; many statements to cache. Using 0 disables statement caching. +; http://php.net/oci8.statement-cache-size +;oci8.statement_cache_size = 20 + +; Tuning: Enables statement prefetching and sets the default number of +; rows that will be fetched automatically after statement execution. +; http://php.net/oci8.default-prefetch +;oci8.default_prefetch = 100 + +; Compatibility. Using On means oci_close() will not close +; oci_connect() and oci_new_connect() connections. +; http://php.net/oci8.old-oci-close-semantics +;oci8.old_oci_close_semantics = Off + +[PostgreSQL] +; Allow or prevent persistent links. +; http://php.net/pgsql.allow-persistent +pgsql.allow_persistent = On + +; Detect broken persistent links always with pg_pconnect(). +; Auto reset feature requires a little overheads. +; http://php.net/pgsql.auto-reset-persistent +pgsql.auto_reset_persistent = Off + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/pgsql.max-persistent +pgsql.max_persistent = -1 + +; Maximum number of links (persistent+non persistent). -1 means no limit. +; http://php.net/pgsql.max-links +pgsql.max_links = -1 + +; Ignore PostgreSQL backends Notice message or not. +; Notice message logging require a little overheads. +; http://php.net/pgsql.ignore-notice +pgsql.ignore_notice = 0 + +; Log PostgreSQL backends Notice message or not. +; Unless pgsql.ignore_notice=0, module cannot log notice message. +; http://php.net/pgsql.log-notice +pgsql.log_notice = 0 + +[bcmath] +; Number of decimal digits for all bcmath functions. +; http://php.net/bcmath.scale +bcmath.scale = 0 + +[browscap] +; http://php.net/browscap +;browscap = extra/browscap.ini + +[Session] +; Handler used to store/retrieve data. +; http://php.net/session.save-handler +session.save_handler = files + +; Argument passed to save_handler. In the case of files, this is the path +; where data files are stored. Note: Windows users have to change this +; variable in order to use PHP's session functions. +; +; The path can be defined as: +; +; session.save_path = "N;/path" +; +; where N is an integer. Instead of storing all the session files in +; /path, what this will do is use subdirectories N-levels deep, and +; store the session data in those directories. This is useful if +; your OS has problems with many files in one directory, and is +; a more efficient layout for servers that handle many sessions. +; +; NOTE 1: PHP will not create this directory structure automatically. +; You can use the script in the ext/session dir for that purpose. +; NOTE 2: See the section on garbage collection below if you choose to +; use subdirectories for session storage +; +; The file storage module creates files using mode 600 by default. +; You can change that by using +; +; session.save_path = "N;MODE;/path" +; +; where MODE is the octal representation of the mode. Note that this +; does not overwrite the process's umask. +; http://php.net/session.save-path +;session.save_path = "/tmp" + +; Whether to use strict session mode. +; Strict session mode does not accept uninitialized session ID and regenerate +; session ID if browser sends uninitialized session ID. Strict mode protects +; applications from session fixation via session adoption vulnerability. It is +; disabled by default for maximum compatibility, but enabling it is encouraged. +; https://wiki.php.net/rfc/strict_sessions +session.use_strict_mode = 0 + +; Whether to use cookies. +; http://php.net/session.use-cookies +session.use_cookies = 1 + +; http://php.net/session.cookie-secure +;session.cookie_secure = + +; This option forces PHP to fetch and use a cookie for storing and maintaining +; the session id. We encourage this operation as it's very helpful in combating +; session hijacking when not specifying and managing your own session id. It is +; not the be-all and end-all of session hijacking defense, but it's a good start. +; http://php.net/session.use-only-cookies +session.use_only_cookies = 1 + +; Name of the session (used as cookie name). +; http://php.net/session.name +session.name = PHPSESSID + +; Initialize session on request startup. +; http://php.net/session.auto-start +session.auto_start = 0 + +; Lifetime in seconds of cookie or, if 0, until browser is restarted. +; http://php.net/session.cookie-lifetime +session.cookie_lifetime = 0 + +; The path for which the cookie is valid. +; http://php.net/session.cookie-path +session.cookie_path = / + +; The domain for which the cookie is valid. +; http://php.net/session.cookie-domain +session.cookie_domain = + +; Whether or not to add the httpOnly flag to the cookie, which makes it inaccessible to browser scripting languages such as JavaScript. +; http://php.net/session.cookie-httponly +session.cookie_httponly = + +; Handler used to serialize data. php is the standard serializer of PHP. +; http://php.net/session.serialize-handler +session.serialize_handler = php + +; Defines the probability that the 'garbage collection' process is started +; on every session initialization. The probability is calculated by using +; gc_probability/gc_divisor. Where session.gc_probability is the numerator +; and gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.gc-probability +session.gc_probability = 1 + +; Defines the probability that the 'garbage collection' process is started on every +; session initialization. The probability is calculated by using the following equation: +; gc_probability/gc_divisor. Where session.gc_probability is the numerator and +; session.gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. Increasing this value to 1000 will give you +; a 0.1% chance the gc will run on any give request. For high volume production servers, +; this is a more efficient approach. +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 +; http://php.net/session.gc-divisor +session.gc_divisor = 1000 + +; After this number of seconds, stored data will be seen as 'garbage' and +; cleaned up by the garbage collection process. +; http://php.net/session.gc-maxlifetime +session.gc_maxlifetime = 3600 + +; NOTE: If you are using the subdirectory option for storing session files +; (see session.save_path above), then garbage collection does *not* +; happen automatically. You will need to do your own garbage +; collection through a shell script, cron entry, or some other method. +; For example, the following script would is the equivalent of +; setting session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes): +; find /path/to/sessions -cmin +24 -type f | xargs rm + +; Check HTTP Referer to invalidate externally stored URLs containing ids. +; HTTP_REFERER has to contain this substring for the session to be +; considered as valid. +; http://php.net/session.referer-check +session.referer_check = + +; Set to {nocache,private,public,} to determine HTTP caching aspects +; or leave this empty to avoid sending anti-caching headers. +; http://php.net/session.cache-limiter +session.cache_limiter = nocache + +; Document expires after n minutes. +; http://php.net/session.cache-expire +session.cache_expire = 180 + +; trans sid support is disabled by default. +; Use of trans sid may risk your users' security. +; Use this option with caution. +; - User may send URL contains active session ID +; to other person via. email/irc/etc. +; - URL that contains active session ID may be stored +; in publicly accessible computer. +; - User may access your site with the same session ID +; always using URL stored in browser's history or bookmarks. +; http://php.net/session.use-trans-sid +session.use_trans_sid = 0 + +; Set session ID character length. This value could be between 22 to 256. +; Shorter length than default is supported only for compatibility reason. +; Users should use 32 or more chars. +; http://php.net/session.sid-length +; Default Value: 32 +; Development Value: 26 +; Production Value: 26 +session.sid_length = 26 + +; The URL rewriter will look for URLs in a defined set of HTML tags. +;
is special; if you include them here, the rewriter will +; add a hidden field with the info which is otherwise appended +; to URLs. tag's action attribute URL will not be modified +; unless it is specified. +; Note that all valid entries require a "=", even if no value follows. +; Default Value: "a=href,area=href,frame=src,form=" +; Development Value: "a=href,area=href,frame=src,form=" +; Production Value: "a=href,area=href,frame=src,form=" +; http://php.net/url-rewriter.tags +session.trans_sid_tags = "a=href,area=href,frame=src,form=" + +; URL rewriter does not rewrite absolute URLs by default. +; To enable rewrites for absolute pathes, target hosts must be specified +; at RUNTIME. i.e. use ini_set() +; tags is special. PHP will check action attribute's URL regardless +; of session.trans_sid_tags setting. +; If no host is defined, HTTP_HOST will be used for allowed host. +; Example value: php.net,www.php.net,wiki.php.net +; Use "," for multiple hosts. No spaces are allowed. +; Default Value: "" +; Development Value: "" +; Production Value: "" +;session.trans_sid_hosts="" + +; Define how many bits are stored in each character when converting +; the binary hash data to something readable. +; Possible values: +; 4 (4 bits: 0-9, a-f) +; 5 (5 bits: 0-9, a-v) +; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 +; http://php.net/session.hash-bits-per-character +session.sid_bits_per_character = 5 + +; Enable upload progress tracking in $_SESSION +; Default Value: On +; Development Value: On +; Production Value: On +; http://php.net/session.upload-progress.enabled +;session.upload_progress.enabled = On + +; Cleanup the progress information as soon as all POST data has been read +; (i.e. upload completed). +; Default Value: On +; Development Value: On +; Production Value: On +; http://php.net/session.upload-progress.cleanup +;session.upload_progress.cleanup = On + +; A prefix used for the upload progress key in $_SESSION +; Default Value: "upload_progress_" +; Development Value: "upload_progress_" +; Production Value: "upload_progress_" +; http://php.net/session.upload-progress.prefix +;session.upload_progress.prefix = "upload_progress_" + +; The index name (concatenated with the prefix) in $_SESSION +; containing the upload progress information +; Default Value: "PHP_SESSION_UPLOAD_PROGRESS" +; Development Value: "PHP_SESSION_UPLOAD_PROGRESS" +; Production Value: "PHP_SESSION_UPLOAD_PROGRESS" +; http://php.net/session.upload-progress.name +;session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS" + +; How frequently the upload progress should be updated. +; Given either in percentages (per-file), or in bytes +; Default Value: "1%" +; Development Value: "1%" +; Production Value: "1%" +; http://php.net/session.upload-progress.freq +;session.upload_progress.freq = "1%" + +; The minimum delay between updates, in seconds +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.upload-progress.min-freq +;session.upload_progress.min_freq = "1" + +; Only write session data when session data is changed. Enabled by default. +; http://php.net/session.lazy-write +;session.lazy_write = On + +[Assertion] +; Switch whether to compile assertions at all (to have no overhead at run-time) +; -1: Do not compile at all +; 0: Jump over assertion at run-time +; 1: Execute assertions +; Changing from or to a negative value is only possible in php.ini! (For turning assertions on and off at run-time, see assert.active, when zend.assertions = 1) +; Default Value: 1 +; Development Value: 1 +; Production Value: -1 +; http://php.net/zend.assertions +zend.assertions = -1 + +; Assert(expr); active by default. +; http://php.net/assert.active +;assert.active = On + +; Throw an AssertationException on failed assertions +; http://php.net/assert.exception +;assert.exception = On + +; Issue a PHP warning for each failed assertion. (Overridden by assert.exception if active) +; http://php.net/assert.warning +;assert.warning = On + +; Don't bail out by default. +; http://php.net/assert.bail +;assert.bail = Off + +; User-function to be called if an assertion fails. +; http://php.net/assert.callback +;assert.callback = 0 + +; Eval the expression with current error_reporting(). Set to true if you want +; error_reporting(0) around the eval(). +; http://php.net/assert.quiet-eval +;assert.quiet_eval = 0 + +[COM] +; path to a file containing GUIDs, IIDs or filenames of files with TypeLibs +; http://php.net/com.typelib-file +;com.typelib_file = + +; allow Distributed-COM calls +; http://php.net/com.allow-dcom +;com.allow_dcom = true + +; autoregister constants of a components typlib on com_load() +; http://php.net/com.autoregister-typelib +;com.autoregister_typelib = true + +; register constants casesensitive +; http://php.net/com.autoregister-casesensitive +;com.autoregister_casesensitive = false + +; show warnings on duplicate constant registrations +; http://php.net/com.autoregister-verbose +;com.autoregister_verbose = true + +; The default character set code-page to use when passing strings to and from COM objects. +; Default: system ANSI code page +;com.code_page= + +[mbstring] +; language for internal character representation. +; This affects mb_send_mail() and mbstring.detect_order. +; http://php.net/mbstring.language +;mbstring.language = Japanese + +; Use of this INI entry is deprecated, use global internal_encoding instead. +; internal/script encoding. +; Some encoding cannot work as internal encoding. (e.g. SJIS, BIG5, ISO-2022-*) +; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. +; The precedence is: default_charset < internal_encoding < iconv.internal_encoding +;mbstring.internal_encoding = + +; Use of this INI entry is deprecated, use global input_encoding instead. +; http input encoding. +; mbstring.encoding_traslation = On is needed to use this setting. +; If empty, default_charset or input_encoding or mbstring.input is used. +; The precedence is: default_charset < intput_encoding < mbsting.http_input +; http://php.net/mbstring.http-input +;mbstring.http_input = + +; Use of this INI entry is deprecated, use global output_encoding instead. +; http output encoding. +; mb_output_handler must be registered as output buffer to function. +; If empty, default_charset or output_encoding or mbstring.http_output is used. +; The precedence is: default_charset < output_encoding < mbstring.http_output +; To use an output encoding conversion, mbstring's output handler must be set +; otherwise output encoding conversion cannot be performed. +; http://php.net/mbstring.http-output +;mbstring.http_output = + +; enable automatic encoding translation according to +; mbstring.internal_encoding setting. Input chars are +; converted to internal encoding by setting this to On. +; Note: Do _not_ use automatic encoding translation for +; portable libs/applications. +; http://php.net/mbstring.encoding-translation +;mbstring.encoding_translation = Off + +; automatic encoding detection order. +; "auto" detect order is changed according to mbstring.language +; http://php.net/mbstring.detect-order +;mbstring.detect_order = auto + +; substitute_character used when character cannot be converted +; one from another +; http://php.net/mbstring.substitute-character +;mbstring.substitute_character = none + +; overload(replace) single byte functions by mbstring functions. +; mail(), ereg(), etc are overloaded by mb_send_mail(), mb_ereg(), +; etc. Possible values are 0,1,2,4 or combination of them. +; For example, 7 for overload everything. +; 0: No overload +; 1: Overload mail() function +; 2: Overload str*() functions +; 4: Overload ereg*() functions +; http://php.net/mbstring.func-overload +;mbstring.func_overload = 0 + +; enable strict encoding detection. +; Default: Off +;mbstring.strict_detection = On + +; This directive specifies the regex pattern of content types for which mb_output_handler() +; is activated. +; Default: mbstring.http_output_conv_mimetype=^(text/|application/xhtml\+xml) +;mbstring.http_output_conv_mimetype= + +[gd] +; Tell the jpeg decode to ignore warnings and try to create +; a gd image. The warning will then be displayed as notices +; disabled by default +; http://php.net/gd.jpeg-ignore-warning +;gd.jpeg_ignore_warning = 1 + +[exif] +; Exif UNICODE user comments are handled as UCS-2BE/UCS-2LE and JIS as JIS. +; With mbstring support this will automatically be converted into the encoding +; given by corresponding encode setting. When empty mbstring.internal_encoding +; is used. For the decode settings you can distinguish between motorola and +; intel byte order. A decode setting cannot be empty. +; http://php.net/exif.encode-unicode +;exif.encode_unicode = ISO-8859-15 + +; http://php.net/exif.decode-unicode-motorola +;exif.decode_unicode_motorola = UCS-2BE + +; http://php.net/exif.decode-unicode-intel +;exif.decode_unicode_intel = UCS-2LE + +; http://php.net/exif.encode-jis +;exif.encode_jis = + +; http://php.net/exif.decode-jis-motorola +;exif.decode_jis_motorola = JIS + +; http://php.net/exif.decode-jis-intel +;exif.decode_jis_intel = JIS + +[Tidy] +; The path to a default tidy configuration file to use when using tidy +; http://php.net/tidy.default-config +;tidy.default_config = /usr/local/lib/php/default.tcfg + +; Should tidy clean and repair output automatically? +; WARNING: Do not use this option if you are generating non-html content +; such as dynamic images +; http://php.net/tidy.clean-output +tidy.clean_output = Off + +[soap] +; Enables or disables WSDL caching feature. +; http://php.net/soap.wsdl-cache-enabled +soap.wsdl_cache_enabled=1 + +; Sets the directory name where SOAP extension will put cache files. +; http://php.net/soap.wsdl-cache-dir +soap.wsdl_cache_dir="/tmp" + +; (time to live) Sets the number of second while cached file will be used +; instead of original one. +; http://php.net/soap.wsdl-cache-ttl +soap.wsdl_cache_ttl=86400 + +; Sets the size of the cache limit. (Max. number of WSDL files to cache) +soap.wsdl_cache_limit = 5 + +[sysvshm] +; A default size of the shared memory segment +;sysvshm.init_mem = 10000 + +[ldap] +; Sets the maximum number of open links or -1 for unlimited. +ldap.max_links = -1 + +[dba] +;dba.default_handler= + +[opcache] +; Determines if Zend OPCache is enabled +;opcache.enable=1 + +; Determines if Zend OPCache is enabled for the CLI version of PHP +;opcache.enable_cli=0 + +; The OPcache shared memory storage size. +;opcache.memory_consumption=128 + +; The amount of memory for interned strings in Mbytes. +;opcache.interned_strings_buffer=8 + +; The maximum number of keys (scripts) in the OPcache hash table. +; Only numbers between 200 and 1000000 are allowed. +;opcache.max_accelerated_files=10000 + +; The maximum percentage of "wasted" memory until a restart is scheduled. +;opcache.max_wasted_percentage=5 + +; When this directive is enabled, the OPcache appends the current working +; directory to the script key, thus eliminating possible collisions between +; files with the same name (basename). Disabling the directive improves +; performance, but may break existing applications. +;opcache.use_cwd=1 + +; When disabled, you must reset the OPcache manually or restart the +; webserver for changes to the filesystem to take effect. +;opcache.validate_timestamps=1 + +; How often (in seconds) to check file timestamps for changes to the shared +; memory storage allocation. ("1" means validate once per second, but only +; once per request. "0" means always validate) +;opcache.revalidate_freq=2 + +; Enables or disables file search in include_path optimization +;opcache.revalidate_path=0 + +; If disabled, all PHPDoc comments are dropped from the code to reduce the +; size of the optimized code. +;opcache.save_comments=1 + +; Allow file existence override (file_exists, etc.) performance feature. +;opcache.enable_file_override=0 + +; A bitmask, where each bit enables or disables the appropriate OPcache +; passes +;opcache.optimization_level=0xffffffff + +;opcache.inherited_hack=1 +;opcache.dups_fix=0 + +; The location of the OPcache blacklist file (wildcards allowed). +; Each OPcache blacklist file is a text file that holds the names of files +; that should not be accelerated. The file format is to add each filename +; to a new line. The filename may be a full path or just a file prefix +; (i.e., /var/www/x blacklists all the files and directories in /var/www +; that start with 'x'). Line starting with a ; are ignored (comments). +;opcache.blacklist_filename= + +; Allows exclusion of large files from being cached. By default all files +; are cached. +;opcache.max_file_size=0 + +; Check the cache checksum each N requests. +; The default value of "0" means that the checks are disabled. +;opcache.consistency_checks=0 + +; How long to wait (in seconds) for a scheduled restart to begin if the cache +; is not being accessed. +;opcache.force_restart_timeout=180 + +; OPcache error_log file name. Empty string assumes "stderr". +;opcache.error_log= + +; All OPcache errors go to the Web server log. +; By default, only fatal errors (level 0) or errors (level 1) are logged. +; You can also enable warnings (level 2), info messages (level 3) or +; debug messages (level 4). +;opcache.log_verbosity_level=1 + +; Preferred Shared Memory back-end. Leave empty and let the system decide. +;opcache.preferred_memory_model= + +; Protect the shared memory from unexpected writing during script execution. +; Useful for internal debugging only. +;opcache.protect_memory=0 + +; Allows calling OPcache API functions only from PHP scripts which path is +; started from specified string. The default "" means no restriction +;opcache.restrict_api= + +; Mapping base of shared memory segments (for Windows only). All the PHP +; processes have to map shared memory into the same address space. This +; directive allows to manually fix the "Unable to reattach to base address" +; errors. +;opcache.mmap_base= + +; Enables and sets the second level cache directory. +; It should improve performance when SHM memory is full, at server restart or +; SHM reset. The default "" disables file based caching. +;opcache.file_cache= + +; Enables or disables opcode caching in shared memory. +;opcache.file_cache_only=0 + +; Enables or disables checksum validation when script loaded from file cache. +;opcache.file_cache_consistency_checks=1 + +; Implies opcache.file_cache_only=1 for a certain process that failed to +; reattach to the shared memory (for Windows only). Explicitly enabled file +; cache is required. +;opcache.file_cache_fallback=1 + +; Enables or disables copying of PHP code (text segment) into HUGE PAGES. +; This should improve performance, but requires appropriate OS configuration. +;opcache.huge_code_pages=1 + +; Validate cached file permissions. +;opcache.validate_permission=0 + +; Prevent name collisions in chroot'ed environment. +;opcache.validate_root=0 + +; If specified, it produces opcode dumps for debugging different stages of +; optimizations. +;opcache.opt_debug_level=0 + +[curl] +; A default value for the CURLOPT_CAINFO option. This is required to be an +; absolute path. +;curl.cainfo = + +[openssl] +; The location of a Certificate Authority (CA) file on the local filesystem +; to use when verifying the identity of SSL/TLS peers. Most users should +; not specify a value for this directive as PHP will attempt to use the +; OS-managed cert stores in its absence. If specified, this value may still +; be overridden on a per-stream basis via the "cafile" SSL stream context +; option. +;openssl.cafile= + +; If openssl.cafile is not specified or if the CA file is not found, the +; directory pointed to by openssl.capath is searched for a suitable +; certificate. This value must be a correctly hashed certificate directory. +; Most users should not specify a value for this directive as PHP will +; attempt to use the OS-managed cert stores in its absence. If specified, +; this value may still be overridden on a per-stream basis via the "capath" +; SSL stream context option. +;openssl.capath= + +; Local Variables: +; tab-width: 4 +; End: diff --git a/docs/db_sample/README.TXT b/docs/db_sample/README.TXT index a77140abbe..e162915b3f 100644 --- a/docs/db_sample/README.TXT +++ b/docs/db_sample/README.TXT @@ -3,23 +3,22 @@ * This script is distributed under the GNU General Public License 2 or later. * * Filename $RCSfile: README.TXT,v $ - * - * @version $Revision: 1.2 $ - * @modified $Date: 2009/05/03 14:37:59 $ by $Author: franciscom $ + * @modified 2024/02/14 20:06:12 by $Author: dcoomber * @Author: francisco.mancardi@gmail.com * * rev: */ -restore-postgres.bat: this bat will help you to restore postgres-testlinkAPI.sql backup - Remember that: - - you need to create DB before doing restore. - - you need may be to change user name - - -postgres-testlinkAPI.sql: Postgres PLAIN backup of a TL DB that can be used to test API Calls. - All php API samples must run without problems againts this DB. - Password for admin user: admin - + testlink_sample.sql: MySQL TL sample DB Password for admin user: admin - \ No newline at end of file + +After restoring the DB dump, you'll need to grant execute permissions on the +`UDFStripHTMLTags` user-defined function in order for test case search to work. + +The example below assumes that your database name is `testlink_sample` and +that your TestLink user is `testlink`: + +``` +use testlink_sample; +grant execute on function UDFStripHTMLTags TO 'testlink'@'%'; +``` diff --git a/docs/db_sample/restore-postgres.bat b/docs/db_sample/restore-postgres.bat deleted file mode 100644 index c964198aca..0000000000 --- a/docs/db_sample/restore-postgres.bat +++ /dev/null @@ -1,2 +0,0 @@ -psql --username postgres testlinkAPI < postgres-testlinkAPI.sql -pause \ No newline at end of file diff --git a/docs/db_sample/restore_sample.sh b/docs/db_sample/restore_sample.sh new file mode 100755 index 0000000000..caecaa4306 --- /dev/null +++ b/docs/db_sample/restore_sample.sh @@ -0,0 +1,34 @@ +#!/bin/bash -e + +DB_HOST=testlink-mysql +DB_ROOT_USER=root +DB_ROOT_PASS=masterkey +TL_DB_NAME=testlink_sample +TL_DB_USER=testlink +TL_DB_PASS=masterkey + +# Restore the database +echo "Creating database '${TL_DB_NAME}'..." +echo "CREATE DATABASE IF NOT EXISTS \`${TL_DB_NAME}\`;" | mysql -h ${DB_HOST} -u ${DB_ROOT_USER} --password=${DB_ROOT_PASS} +echo "Restoring database '${TL_DB_NAME}' from DB dump..." +mysql -h ${DB_HOST} -u ${DB_ROOT_USER} --password=${DB_ROOT_PASS} --database=${TL_DB_NAME} < testlink_sample.sql + +# Create the testlink user +echo "Creating user '${TL_DB_USER}'..." +echo "CREATE USER IF NOT EXISTS '${TL_DB_USER}'@'%' IDENTIFIED BY '${TL_DB_PASS}';" | mysql -h ${DB_HOST} -u ${DB_ROOT_USER} --password=${DB_ROOT_PASS} +echo "GRANT SELECT, UPDATE, DELETE, INSERT ON \`${TL_DB_NAME}\`.* TO '${TL_DB_USER}'@'%' WITH GRANT OPTION;" | mysql -h ${DB_HOST} -u ${DB_ROOT_USER} --password=${DB_ROOT_PASS} +echo "GRANT EXECUTE ON FUNCTION \`${TL_DB_NAME}\`.UDFStripHTMLTags TO '${TL_DB_USER}'@'%';" | mysql -h ${DB_HOST} -u ${DB_ROOT_USER} --password=${DB_ROOT_PASS} + +# Update config_db.inc.php +echo "Creating 'config_db.inc.php' file..." +cat > ../../config_db.inc.php<< EOF + show it during specification design', + `enable_on_design` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '1=> user can write/manage it during specification design', + `show_on_execution` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '1=> show it during test case execution', + `enable_on_execution` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '1=> user can write/manage it during test case execution', + `show_on_testplan_design` tinyint unsigned NOT NULL DEFAULT '0', + `enable_on_testplan_design` tinyint unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_custom_fields_name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `custom_fields` +-- + +LOCK TABLES `custom_fields` WRITE; +/*!40000 ALTER TABLE `custom_fields` DISABLE KEYS */; +/*!40000 ALTER TABLE `custom_fields` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `db_version` +-- + +DROP TABLE IF EXISTS `db_version`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `db_version` ( + `version` varchar(50) NOT NULL DEFAULT 'unknown', + `upgrade_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `notes` text, + PRIMARY KEY (`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `db_version` +-- + +LOCK TABLES `db_version` WRITE; +/*!40000 ALTER TABLE `db_version` DISABLE KEYS */; +INSERT INTO `db_version` VALUES ('DB 1.9.20','2022-02-04 14:59:03','TestLink 1.9.20 Raijin'); +/*!40000 ALTER TABLE `db_version` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `events` +-- + +DROP TABLE IF EXISTS `events`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `events` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `transaction_id` int unsigned NOT NULL DEFAULT '0', + `log_level` smallint unsigned NOT NULL DEFAULT '0', + `source` varchar(45) DEFAULT NULL, + `description` text NOT NULL, + `fired_at` int unsigned NOT NULL DEFAULT '0', + `activity` varchar(45) DEFAULT NULL, + `object_id` int unsigned DEFAULT NULL, + `object_type` varchar(45) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `transaction_id` (`transaction_id`), + KEY `fired_at` (`fired_at`) +) ENGINE=InnoDB AUTO_INCREMENT=302 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `events` +-- + +LOCK TABLES `events` WRITE; +/*!40000 ALTER TABLE `events` DISABLE KEYS */; +INSERT INTO `events` VALUES (265,50,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:18:\"audit_login_failed\";s:6:\"params\";a:2:{i:0;s:5:\"admin\";i:1;s:12:\"192.168.65.1\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1707059206,'LOGIN_FAILED',1,'users'),(266,51,16,'GUI - Test Project ID : 2','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_login_succeeded\";s:6:\"params\";a:2:{i:0;s:5:\"admin\";i:1;s:12:\"192.168.65.1\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1707059211,'LOGIN',1,'users'),(267,52,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: DataTablesSelector - in /var/www/testlink/gui/templates_c/6e35d38c467944a048705ebd8774a98926e2ced6_0.file.DataTables.inc.tpl.php - Line 35',1707059214,'PHP',0,NULL),(268,52,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to get property \'value\' of non-object - in /var/www/testlink/gui/templates_c/6e35d38c467944a048705ebd8774a98926e2ced6_0.file.DataTables.inc.tpl.php - Line 35',1707059214,'PHP',0,NULL),(269,53,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: DataTablesSelector - in /var/www/testlink/gui/templates_c/6e35d38c467944a048705ebd8774a98926e2ced6_0.file.DataTables.inc.tpl.php - Line 35',1707059224,'PHP',0,NULL),(270,53,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to get property \'value\' of non-object - in /var/www/testlink/gui/templates_c/6e35d38c467944a048705ebd8774a98926e2ced6_0.file.DataTables.inc.tpl.php - Line 35',1707059224,'PHP',0,NULL),(271,53,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: DataTablesSelector - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 31',1707059224,'PHP',0,NULL),(272,53,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to get property \'value\' of non-object - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 31',1707059224,'PHP',0,NULL),(273,53,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: DataTablesSelector - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 48',1707059224,'PHP',0,NULL),(274,53,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to get property \'value\' of non-object - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 48',1707059224,'PHP',0,NULL),(275,53,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: DataTablesSelector - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 49',1707059224,'PHP',0,NULL),(276,53,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to get property \'value\' of non-object - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 49',1707059224,'PHP',0,NULL),(277,53,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: DataTablesSelector - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 51',1707059224,'PHP',0,NULL),(278,53,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to get property \'value\' of non-object - in /var/www/testlink/gui/templates_c/8c2b5b97f935ad5a2c97c9ca634a8aad40cbfb8f_0.file.DataTablesColumnFiltering.inc.tpl.php - Line 51',1707059224,'PHP',0,NULL),(279,54,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 298',1707059232,'PHP',0,NULL),(280,55,2,'GUI - Test Project ID : 2','E_WARNING\ngetimagesize(http://localhost:8080/gui/themes/default/images/tl-logo-transparent-25.png): failed to open stream: Cannot assign requested address - in /var/www/testlink/lib/functions/print.inc.php - Line 694',1707059578,'PHP',0,NULL),(281,56,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059648,'PHP',0,NULL),(282,57,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059672,'PHP',0,NULL),(283,58,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059672,'PHP',0,NULL),(284,59,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059678,'PHP',0,NULL),(285,59,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059678,'PHP',0,NULL),(286,59,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/gui/templates_c/76a998150d2db34c4f158f1d58a406eac7079c26_0.file.inc_exec_show_tc_exec.tpl.php - Line 242',1707059678,'PHP',0,NULL),(287,60,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059678,'PHP',0,NULL),(288,60,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059678,'PHP',0,NULL),(289,60,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/gui/templates_c/76a998150d2db34c4f158f1d58a406eac7079c26_0.file.inc_exec_show_tc_exec.tpl.php - Line 242',1707059678,'PHP',0,NULL),(290,61,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059679,'PHP',0,NULL),(291,61,2,'GUI - Test Project ID : 2','E_NOTICE\nUndefined index: build - in /var/www/testlink/lib/functions/cfield_mgr.class.php - Line 489',1707059679,'PHP',0,NULL),(292,61,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/gui/templates_c/76a998150d2db34c4f158f1d58a406eac7079c26_0.file.inc_exec_show_tc_exec.tpl.php - Line 242',1707059679,'PHP',0,NULL),(293,62,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 706',1707059689,'PHP',0,NULL),(294,62,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 711',1707059689,'PHP',0,NULL),(295,62,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 748',1707059689,'PHP',0,NULL),(296,62,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 1172',1707059689,'PHP',0,NULL),(297,63,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 706',1707059692,'PHP',0,NULL),(298,63,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 711',1707059692,'PHP',0,NULL),(299,63,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 748',1707059692,'PHP',0,NULL),(300,63,2,'GUI - Test Project ID : 2','E_NOTICE\nTrying to access array offset on value of type null - in /var/www/testlink/lib/functions/specview.php - Line 1172',1707059692,'PHP',0,NULL),(301,64,16,'GUI - Test Project ID : 2','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:17:\"audit_user_logout\";s:6:\"params\";a:1:{i:0;s:5:\"admin\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1707059711,'LOGOUT',1,'users'); +/*!40000 ALTER TABLE `events` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Temporary table structure for view `exec_by_date_time` +-- + +DROP TABLE IF EXISTS `exec_by_date_time`; +/*!50001 DROP VIEW IF EXISTS `exec_by_date_time`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `exec_by_date_time` AS SELECT + 1 AS `testplan_name`, + 1 AS `yyyy_mm_dd`, + 1 AS `yyyy_mm`, + 1 AS `hh`, + 1 AS `hour`, + 1 AS `id`, + 1 AS `build_id`, + 1 AS `tester_id`, + 1 AS `execution_ts`, + 1 AS `status`, + 1 AS `testplan_id`, + 1 AS `tcversion_id`, + 1 AS `tcversion_number`, + 1 AS `platform_id`, + 1 AS `execution_type`, + 1 AS `execution_duration`, + 1 AS `notes` */; +SET character_set_client = @saved_cs_client; + +-- +-- Table structure for table `execution_bugs` +-- + +DROP TABLE IF EXISTS `execution_bugs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `execution_bugs` ( + `execution_id` int unsigned NOT NULL DEFAULT '0', + `bug_id` varchar(64) NOT NULL DEFAULT '0', + `tcstep_id` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`execution_id`,`bug_id`,`tcstep_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `execution_bugs` +-- + +LOCK TABLES `execution_bugs` WRITE; +/*!40000 ALTER TABLE `execution_bugs` DISABLE KEYS */; +/*!40000 ALTER TABLE `execution_bugs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `execution_tcsteps` +-- + +DROP TABLE IF EXISTS `execution_tcsteps`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `execution_tcsteps` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `execution_id` int unsigned NOT NULL DEFAULT '0', + `tcstep_id` int unsigned NOT NULL DEFAULT '0', + `notes` text, + `status` char(1) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `execution_tcsteps_idx1` (`execution_id`,`tcstep_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `execution_tcsteps` +-- + +LOCK TABLES `execution_tcsteps` WRITE; +/*!40000 ALTER TABLE `execution_tcsteps` DISABLE KEYS */; +/*!40000 ALTER TABLE `execution_tcsteps` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `execution_tcsteps_wip` +-- + +DROP TABLE IF EXISTS `execution_tcsteps_wip`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `execution_tcsteps_wip` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `tcstep_id` int unsigned NOT NULL DEFAULT '0', + `testplan_id` int unsigned NOT NULL DEFAULT '0', + `platform_id` int unsigned NOT NULL DEFAULT '0', + `build_id` int unsigned NOT NULL DEFAULT '0', + `tester_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `notes` text, + `status` char(1) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `execution_tcsteps_wip_idx1` (`tcstep_id`,`testplan_id`,`platform_id`,`build_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `execution_tcsteps_wip` +-- + +LOCK TABLES `execution_tcsteps_wip` WRITE; +/*!40000 ALTER TABLE `execution_tcsteps_wip` DISABLE KEYS */; +/*!40000 ALTER TABLE `execution_tcsteps_wip` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `executions` +-- + +DROP TABLE IF EXISTS `executions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `executions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `build_id` int NOT NULL DEFAULT '0', + `tester_id` int unsigned DEFAULT NULL, + `execution_ts` datetime DEFAULT NULL, + `status` char(1) DEFAULT NULL, + `testplan_id` int unsigned NOT NULL DEFAULT '0', + `tcversion_id` int unsigned NOT NULL DEFAULT '0', + `tcversion_number` smallint unsigned NOT NULL DEFAULT '1', + `platform_id` int unsigned NOT NULL DEFAULT '0', + `execution_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 -> manual, 2 -> automated', + `execution_duration` decimal(6,2) DEFAULT NULL COMMENT 'NULL will be considered as NO DATA Provided by user', + `notes` text, + PRIMARY KEY (`id`), + KEY `executions_idx1` (`testplan_id`,`tcversion_id`,`platform_id`,`build_id`), + KEY `executions_idx2` (`execution_type`), + KEY `executions_idx3` (`tcversion_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `executions` +-- + +LOCK TABLES `executions` WRITE; +/*!40000 ALTER TABLE `executions` DISABLE KEYS */; +/*!40000 ALTER TABLE `executions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `inventory` +-- + +DROP TABLE IF EXISTS `inventory`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `inventory` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `testproject_id` int unsigned NOT NULL, + `owner_id` int unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `ipaddress` varchar(255) NOT NULL, + `content` text, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modification_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `inventory_idx1` (`testproject_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `inventory` +-- + +LOCK TABLES `inventory` WRITE; +/*!40000 ALTER TABLE `inventory` DISABLE KEYS */; +/*!40000 ALTER TABLE `inventory` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `issuetrackers` +-- + +DROP TABLE IF EXISTS `issuetrackers`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `issuetrackers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `type` int DEFAULT '0', + `cfg` text, + PRIMARY KEY (`id`), + UNIQUE KEY `issuetrackers_uidx1` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `issuetrackers` +-- + +LOCK TABLES `issuetrackers` WRITE; +/*!40000 ALTER TABLE `issuetrackers` DISABLE KEYS */; +/*!40000 ALTER TABLE `issuetrackers` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `keywords` +-- + +DROP TABLE IF EXISTS `keywords`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `keywords` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `keyword` varchar(100) NOT NULL DEFAULT '', + `testproject_id` int unsigned NOT NULL DEFAULT '0', + `notes` text, + PRIMARY KEY (`id`), + UNIQUE KEY `keyword_testproject_id` (`keyword`,`testproject_id`), + KEY `testproject_id` (`testproject_id`), + KEY `keyword` (`keyword`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `keywords` +-- + +LOCK TABLES `keywords` WRITE; +/*!40000 ALTER TABLE `keywords` DISABLE KEYS */; +INSERT INTO `keywords` VALUES (1,'Mechanical',2,''),(2,'Resistance',2,''),(3,'Tear Resistance',2,''),(4,'Mountability',2,''),(5,'Subjective',2,''),(6,'Objective',2,''); +/*!40000 ALTER TABLE `keywords` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Temporary table structure for view `latest_exec_by_context` +-- + +DROP TABLE IF EXISTS `latest_exec_by_context`; +/*!50001 DROP VIEW IF EXISTS `latest_exec_by_context`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_exec_by_context` AS SELECT + 1 AS `tcversion_id`, + 1 AS `testplan_id`, + 1 AS `build_id`, + 1 AS `platform_id`, + 1 AS `id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `latest_exec_by_testplan` +-- + +DROP TABLE IF EXISTS `latest_exec_by_testplan`; +/*!50001 DROP VIEW IF EXISTS `latest_exec_by_testplan`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_exec_by_testplan` AS SELECT + 1 AS `tcversion_id`, + 1 AS `testplan_id`, + 1 AS `id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `latest_exec_by_testplan_plat` +-- + +DROP TABLE IF EXISTS `latest_exec_by_testplan_plat`; +/*!50001 DROP VIEW IF EXISTS `latest_exec_by_testplan_plat`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_exec_by_testplan_plat` AS SELECT + 1 AS `tcversion_id`, + 1 AS `testplan_id`, + 1 AS `platform_id`, + 1 AS `id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `latest_req_version` +-- + +DROP TABLE IF EXISTS `latest_req_version`; +/*!50001 DROP VIEW IF EXISTS `latest_req_version`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_req_version` AS SELECT + 1 AS `req_id`, + 1 AS `version` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `latest_req_version_id` +-- + +DROP TABLE IF EXISTS `latest_req_version_id`; +/*!50001 DROP VIEW IF EXISTS `latest_req_version_id`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_req_version_id` AS SELECT + 1 AS `req_id`, + 1 AS `version`, + 1 AS `req_version_id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `latest_rspec_revision` +-- + +DROP TABLE IF EXISTS `latest_rspec_revision`; +/*!50001 DROP VIEW IF EXISTS `latest_rspec_revision`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_rspec_revision` AS SELECT + 1 AS `req_spec_id`, + 1 AS `testproject_id`, + 1 AS `revision` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `latest_tcase_version_id` +-- + +DROP TABLE IF EXISTS `latest_tcase_version_id`; +/*!50001 DROP VIEW IF EXISTS `latest_tcase_version_id`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_tcase_version_id` AS SELECT + 1 AS `testcase_id`, + 1 AS `version`, + 1 AS `tcversion_id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `latest_tcase_version_number` +-- + +DROP TABLE IF EXISTS `latest_tcase_version_number`; +/*!50001 DROP VIEW IF EXISTS `latest_tcase_version_number`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `latest_tcase_version_number` AS SELECT + 1 AS `testcase_id`, + 1 AS `version` */; +SET character_set_client = @saved_cs_client; + +-- +-- Table structure for table `milestones` +-- + +DROP TABLE IF EXISTS `milestones`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `milestones` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `testplan_id` int unsigned NOT NULL DEFAULT '0', + `target_date` date NOT NULL, + `start_date` date DEFAULT NULL, + `a` tinyint unsigned NOT NULL DEFAULT '0', + `b` tinyint unsigned NOT NULL DEFAULT '0', + `c` tinyint unsigned NOT NULL DEFAULT '0', + `name` varchar(100) NOT NULL DEFAULT 'undefined', + PRIMARY KEY (`id`), + UNIQUE KEY `name_testplan_id` (`name`,`testplan_id`), + KEY `testplan_id` (`testplan_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `milestones` +-- + +LOCK TABLES `milestones` WRITE; +/*!40000 ALTER TABLE `milestones` DISABLE KEYS */; +/*!40000 ALTER TABLE `milestones` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `node_types` +-- + +DROP TABLE IF EXISTS `node_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `node_types` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `description` varchar(100) NOT NULL DEFAULT 'testproject', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `node_types` +-- + +LOCK TABLES `node_types` WRITE; +/*!40000 ALTER TABLE `node_types` DISABLE KEYS */; +INSERT INTO `node_types` VALUES (1,'testproject'),(2,'testsuite'),(3,'testcase'),(4,'testcase_version'),(5,'testplan'),(6,'requirement_spec'),(7,'requirement'),(8,'requirement_version'),(9,'testcase_step'),(10,'requirement_revision'); +/*!40000 ALTER TABLE `node_types` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `nodes_hierarchy` +-- + +DROP TABLE IF EXISTS `nodes_hierarchy`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `nodes_hierarchy` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) DEFAULT NULL, + `parent_id` int unsigned DEFAULT NULL, + `node_type_id` int unsigned NOT NULL DEFAULT '1', + `node_order` int unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `pid_m_nodeorder` (`parent_id`,`node_order`), + KEY `nodes_hierarchy_node_type_id` (`node_type_id`) +) ENGINE=InnoDB AUTO_INCREMENT=163 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `nodes_hierarchy` +-- + +LOCK TABLES `nodes_hierarchy` WRITE; +/*!40000 ALTER TABLE `nodes_hierarchy` DISABLE KEYS */; +INSERT INTO `nodes_hierarchy` VALUES (2,'Formula One Pirelli Dry Tyres',NULL,1,1),(3,'Formula One Pirelli Wet Tyres',NULL,1,1),(4,'P Zero Red (Supersoft)',2,5,0),(5,'P Zero Yellow (Soft)',2,5,0),(6,'P Zero White (Medium)',2,5,0),(7,'P Zero Silver (Hard)',2,5,0),(8,'Cinturato Green (Intermediate)',3,5,0),(9,'Cinturato Blue (Wet)',3,5,0),(10,'Grip',2,2,1),(11,'Braking',2,2,2),(12,'Driving comfort',2,2,3),(13,'Internal noise level',2,2,4),(14,'Exterior noise levels',2,2,5),(15,'Tyre wear',2,2,6),(16,'BRK - Single-Wheel Braking, Driving, and Lateral Traction',11,2,1),(17,'BRK - Stopping Distance',11,2,2),(18,'GRIP - Slalom',10,2,1),(19,'GRIP - Lane Change',10,2,2),(20,'GRIP - Hydroplaning',10,2,3),(21,'DRVC - Cornering Response',12,2,1),(22,'DRVC - J-Turn',12,2,2),(23,'DRVC - Tethered Circle',12,2,3),(24,'TYW - Stresses & motions in the tyre footprint',15,2,1),(25,'TYW - Tyre forces & moments',15,2,2),(26,'INO - Lateral, longitudinal & vertical spring rates',13,2,1),(27,'EXNO - Lateral, longitudinal & vertical spring rates',14,2,1),(28,'TYW - Thermal Profile/Reliability',15,2,3),(29,'INO - High Speed Operation',13,2,2),(30,'EXNO - High Speed Operation',14,2,2),(31,'GRIP-SL-001',18,3,100),(32,'',31,4,0),(33,'GRIP-SL-002',18,3,101),(34,'',33,4,0),(35,'GRIP-LC-001',19,3,100),(36,'',35,4,0),(37,'GRIP-LC-002',19,3,101),(38,'',37,4,0),(39,'GRIP-LC-003',19,3,100),(40,'',39,4,0),(41,'GRIP-LC-004',19,3,101),(42,'',41,4,0),(43,'GRIP-HYD-101',20,3,100),(44,'',43,4,0),(45,'GRIP-HYD-102',20,3,101),(46,'',45,4,0),(47,'GRIP-HYD-103',20,3,100),(48,'',47,4,0),(49,'GRIP-HYD-104',20,3,101),(50,'',49,4,0),(51,'BRK-AA-101',16,3,100),(52,'',51,4,0),(53,'BRK-AA-102',16,3,101),(54,'',53,4,0),(55,'BRK-AA-103',16,3,100),(56,'',55,4,0),(57,'BRK-AA-104',16,3,101),(58,'',57,4,0),(59,'BRK-BB-101',17,3,100),(60,'',59,4,0),(61,'BRK-BB-102',17,3,101),(62,'',61,4,0),(63,'BRK-BB-103',17,3,100),(64,'',63,4,0),(65,'BRK-BB-104',17,3,101),(66,'',65,4,0),(67,'DRV-AX-101',21,3,100),(68,'',67,4,0),(69,'DRV-AX-102',21,3,101),(70,'',69,4,0),(71,'DRV-AX-103',21,3,100),(72,'',71,4,0),(73,'DRV-AX-104',21,3,101),(74,'',73,4,0),(75,'DRV-JT-801',22,3,100),(76,'',75,4,0),(77,'DRV-JT-802',22,3,101),(78,'',77,4,0),(79,'DRV-JT-803',22,3,100),(80,'',79,4,0),(81,'DRV-JT-804',22,3,101),(82,'',81,4,0),(83,'DRV-TC-701',23,3,100),(84,'',83,4,0),(85,'DRV-TC-702',23,3,101),(86,'',85,4,0),(87,'DRV-TC-703',23,3,100),(88,'',87,4,0),(89,'DRV-TC-704',23,3,101),(90,'',89,4,0),(91,'DRV-TC-705',23,3,101),(92,'',91,4,0),(93,'INO-ZX-701',26,3,100),(94,'',93,4,0),(95,'INO-ZX-702',26,3,101),(96,'',95,4,0),(97,'INO-ZX-703',26,3,100),(98,'',97,4,0),(99,'INO-ZX-704',26,3,101),(100,'',99,4,0),(101,'INO-ZX-705',26,3,101),(102,'',101,4,0),(103,'INO-ZW-601',29,3,100),(104,'',103,4,0),(105,'INO-ZW-602',29,3,101),(106,'',105,4,0),(107,'INO-ZW-603',29,3,100),(108,'',107,4,0),(109,'INO-ZW-604',29,3,101),(110,'',109,4,0),(111,'INO-ZW-605',29,3,101),(112,'',111,4,0),(113,'EXNO-ZW-601',27,3,100),(114,'',113,4,0),(115,'EXNO-ZW-602',27,3,101),(116,'',115,4,0),(117,'EXNO-ZW-603',27,3,100),(118,'',117,4,0),(119,'EXNO-ZW-604',27,3,101),(120,'',119,4,0),(121,'EXNO-ZW-605',27,3,101),(122,'',121,4,0),(123,'EXNO-WW-001',30,3,100),(124,'',123,4,0),(125,'EXNO-WW-002',30,3,101),(126,'',125,4,0),(127,'EXNO-WW-003',30,3,100),(128,'',127,4,0),(129,'EXNO-WW-004',30,3,101),(130,'',129,4,0),(131,'EXNO-WW-005',30,3,101),(132,'',131,4,0),(133,'TYW-77-001',24,3,100),(134,'',133,4,0),(135,'TYW-77-002',24,3,101),(136,'',135,4,0),(137,'TYW-77-003',24,3,100),(138,'',137,4,0),(139,'TYW-77-004',24,3,101),(140,'',139,4,0),(141,'TYW-77-005',24,3,101),(142,'',141,4,0),(143,'TYW-77-101',25,3,100),(144,'',143,4,0),(145,'TYW-77-102',25,3,101),(146,'',145,4,0),(147,'TYW-77-103',25,3,100),(148,'',147,4,0),(149,'TYW-77-104',25,3,101),(150,'',149,4,0),(151,'TYW-77-105',25,3,101),(152,'',151,4,0),(153,'TYW-77-201',28,3,100),(154,'',153,4,0),(155,'TYW-77-202',28,3,101),(156,'',155,4,0),(157,'TYW-77-203',28,3,100),(158,'',157,4,0),(159,'TYW-77-204',28,3,101),(160,'',159,4,0),(161,'TYW-77-205',28,3,101),(162,'',161,4,0); +/*!40000 ALTER TABLE `nodes_hierarchy` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `object_keywords` +-- + +DROP TABLE IF EXISTS `object_keywords`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `object_keywords` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `fk_id` int unsigned NOT NULL DEFAULT '0', + `fk_table` varchar(30) DEFAULT '', + `keyword_id` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `udx01_object_keywords` (`fk_id`,`fk_table`,`keyword_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `object_keywords` +-- + +LOCK TABLES `object_keywords` WRITE; +/*!40000 ALTER TABLE `object_keywords` DISABLE KEYS */; +/*!40000 ALTER TABLE `object_keywords` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `platforms` +-- + +DROP TABLE IF EXISTS `platforms`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `platforms` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `testproject_id` int unsigned NOT NULL, + `notes` text NOT NULL, + `enable_on_design` tinyint unsigned NOT NULL DEFAULT '0', + `enable_on_execution` tinyint unsigned NOT NULL DEFAULT '1', + `is_open` tinyint unsigned NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_platforms` (`testproject_id`,`name`) +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `platforms` +-- + +LOCK TABLES `platforms` WRITE; +/*!40000 ALTER TABLE `platforms` DISABLE KEYS */; +INSERT INTO `platforms` VALUES (1,'Ferrari',2,'',0,1,1),(2,'Mc Laren',2,'',0,1,1),(3,'Red Bull',2,'',0,1,1),(4,'Mercedes',2,'',0,1,1),(7,'Renault',2,'',0,1,1); +/*!40000 ALTER TABLE `platforms` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `plugins` +-- + +DROP TABLE IF EXISTS `plugins`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `plugins` ( + `id` int NOT NULL AUTO_INCREMENT, + `basename` varchar(100) NOT NULL, + `enabled` tinyint(1) NOT NULL DEFAULT '0', + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `plugins` +-- + +LOCK TABLES `plugins` WRITE; +/*!40000 ALTER TABLE `plugins` DISABLE KEYS */; +/*!40000 ALTER TABLE `plugins` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `plugins_configuration` +-- + +DROP TABLE IF EXISTS `plugins_configuration`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `plugins_configuration` ( + `id` int NOT NULL AUTO_INCREMENT, + `testproject_id` int NOT NULL, + `config_key` varchar(255) NOT NULL, + `config_type` int NOT NULL, + `config_value` varchar(255) NOT NULL, + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `plugins_configuration` +-- + +LOCK TABLES `plugins_configuration` WRITE; +/*!40000 ALTER TABLE `plugins_configuration` DISABLE KEYS */; +/*!40000 ALTER TABLE `plugins_configuration` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `req_coverage` +-- + +DROP TABLE IF EXISTS `req_coverage`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `req_coverage` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `req_id` int NOT NULL, + `req_version_id` int NOT NULL, + `testcase_id` int NOT NULL, + `tcversion_id` int NOT NULL, + `link_status` int NOT NULL DEFAULT '1', + `is_active` int NOT NULL DEFAULT '1', + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `review_requester_id` int unsigned DEFAULT NULL, + `review_request_ts` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `req_coverage_full_link` (`req_id`,`req_version_id`,`testcase_id`,`tcversion_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='relation test case version ** requirement version'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `req_coverage` +-- + +LOCK TABLES `req_coverage` WRITE; +/*!40000 ALTER TABLE `req_coverage` DISABLE KEYS */; +/*!40000 ALTER TABLE `req_coverage` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `req_monitor` +-- + +DROP TABLE IF EXISTS `req_monitor`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `req_monitor` ( + `req_id` int NOT NULL, + `user_id` int NOT NULL, + `testproject_id` int NOT NULL, + PRIMARY KEY (`req_id`,`user_id`,`testproject_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `req_monitor` +-- + +LOCK TABLES `req_monitor` WRITE; +/*!40000 ALTER TABLE `req_monitor` DISABLE KEYS */; +/*!40000 ALTER TABLE `req_monitor` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `req_relations` +-- + +DROP TABLE IF EXISTS `req_relations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `req_relations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `source_id` int unsigned NOT NULL, + `destination_id` int unsigned NOT NULL, + `relation_type` smallint unsigned NOT NULL DEFAULT '1', + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `req_relations` +-- + +LOCK TABLES `req_relations` WRITE; +/*!40000 ALTER TABLE `req_relations` DISABLE KEYS */; +/*!40000 ALTER TABLE `req_relations` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `req_revisions` +-- + +DROP TABLE IF EXISTS `req_revisions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `req_revisions` ( + `parent_id` int unsigned NOT NULL, + `id` int unsigned NOT NULL, + `revision` smallint unsigned NOT NULL DEFAULT '1', + `req_doc_id` varchar(64) DEFAULT NULL, + `name` varchar(100) DEFAULT NULL, + `scope` text, + `status` char(1) NOT NULL DEFAULT 'V', + `type` char(1) DEFAULT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `is_open` tinyint(1) NOT NULL DEFAULT '1', + `expected_coverage` int NOT NULL DEFAULT '1', + `log_message` text, + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifier_id` int unsigned DEFAULT NULL, + `modification_ts` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `req_revisions_uidx1` (`parent_id`,`revision`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `req_revisions` +-- + +LOCK TABLES `req_revisions` WRITE; +/*!40000 ALTER TABLE `req_revisions` DISABLE KEYS */; +/*!40000 ALTER TABLE `req_revisions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `req_specs` +-- + +DROP TABLE IF EXISTS `req_specs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `req_specs` ( + `id` int unsigned NOT NULL, + `testproject_id` int unsigned NOT NULL, + `doc_id` varchar(64) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `req_spec_uk1` (`doc_id`,`testproject_id`), + KEY `testproject_id` (`testproject_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Dev. Documents (e.g. System Requirements Specification)'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `req_specs` +-- + +LOCK TABLES `req_specs` WRITE; +/*!40000 ALTER TABLE `req_specs` DISABLE KEYS */; +/*!40000 ALTER TABLE `req_specs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `req_specs_revisions` +-- + +DROP TABLE IF EXISTS `req_specs_revisions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `req_specs_revisions` ( + `parent_id` int unsigned NOT NULL, + `id` int unsigned NOT NULL, + `revision` smallint unsigned NOT NULL DEFAULT '1', + `doc_id` varchar(64) DEFAULT NULL, + `name` varchar(100) DEFAULT NULL, + `scope` text, + `total_req` int NOT NULL DEFAULT '0', + `status` int unsigned DEFAULT '1', + `type` char(1) DEFAULT NULL, + `log_message` text, + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifier_id` int unsigned DEFAULT NULL, + `modification_ts` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `req_specs_revisions_uidx1` (`parent_id`,`revision`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `req_specs_revisions` +-- + +LOCK TABLES `req_specs_revisions` WRITE; +/*!40000 ALTER TABLE `req_specs_revisions` DISABLE KEYS */; +/*!40000 ALTER TABLE `req_specs_revisions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `req_versions` +-- + +DROP TABLE IF EXISTS `req_versions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `req_versions` ( + `id` int unsigned NOT NULL, + `version` smallint unsigned NOT NULL DEFAULT '1', + `revision` smallint unsigned NOT NULL DEFAULT '1', + `scope` text, + `status` char(1) NOT NULL DEFAULT 'V', + `type` char(1) DEFAULT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `is_open` tinyint(1) NOT NULL DEFAULT '1', + `expected_coverage` int NOT NULL DEFAULT '1', + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifier_id` int unsigned DEFAULT NULL, + `modification_ts` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `log_message` text, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `req_versions` +-- + +LOCK TABLES `req_versions` WRITE; +/*!40000 ALTER TABLE `req_versions` DISABLE KEYS */; +/*!40000 ALTER TABLE `req_versions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `reqmgrsystems` +-- + +DROP TABLE IF EXISTS `reqmgrsystems`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `reqmgrsystems` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `type` int DEFAULT '0', + `cfg` text, + PRIMARY KEY (`id`), + UNIQUE KEY `reqmgrsystems_uidx1` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `reqmgrsystems` +-- + +LOCK TABLES `reqmgrsystems` WRITE; +/*!40000 ALTER TABLE `reqmgrsystems` DISABLE KEYS */; +/*!40000 ALTER TABLE `reqmgrsystems` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `requirements` +-- + +DROP TABLE IF EXISTS `requirements`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `requirements` ( + `id` int unsigned NOT NULL, + `srs_id` int unsigned NOT NULL, + `req_doc_id` varchar(64) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `requirements_req_doc_id` (`srs_id`,`req_doc_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `requirements` +-- + +LOCK TABLES `requirements` WRITE; +/*!40000 ALTER TABLE `requirements` DISABLE KEYS */; +/*!40000 ALTER TABLE `requirements` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `rights` +-- + +DROP TABLE IF EXISTS `rights`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `rights` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `description` varchar(100) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `rights_descr` (`description`) +) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `rights` +-- + +LOCK TABLES `rights` WRITE; +/*!40000 ALTER TABLE `rights` DISABLE KEYS */; +INSERT INTO `rights` VALUES (18,'cfield_management'),(17,'cfield_view'),(22,'events_mgt'),(9,'mgt_modify_key'),(12,'mgt_modify_product'),(11,'mgt_modify_req'),(7,'mgt_modify_tc'),(16,'mgt_testplan_create'),(13,'mgt_users'),(20,'mgt_view_events'),(8,'mgt_view_key'),(10,'mgt_view_req'),(6,'mgt_view_tc'),(21,'mgt_view_usergroups'),(24,'platform_management'),(25,'platform_view'),(26,'project_inventory_management'),(27,'project_inventory_view'),(14,'role_management'),(19,'system_configuration'),(2,'testplan_create_build'),(1,'testplan_execute'),(3,'testplan_metrics'),(4,'testplan_planning'),(5,'testplan_user_role_assignment'),(23,'testproject_user_role_assignment'),(15,'user_role_assignment'); +/*!40000 ALTER TABLE `rights` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `risk_assignments` +-- + +DROP TABLE IF EXISTS `risk_assignments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `risk_assignments` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `testplan_id` int unsigned NOT NULL DEFAULT '0', + `node_id` int unsigned NOT NULL DEFAULT '0', + `risk` char(1) NOT NULL DEFAULT '2', + `importance` char(1) NOT NULL DEFAULT 'M', + PRIMARY KEY (`id`), + UNIQUE KEY `risk_assignments_tplan_node_id` (`testplan_id`,`node_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `risk_assignments` +-- + +LOCK TABLES `risk_assignments` WRITE; +/*!40000 ALTER TABLE `risk_assignments` DISABLE KEYS */; +/*!40000 ALTER TABLE `risk_assignments` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `role_rights` +-- + +DROP TABLE IF EXISTS `role_rights`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `role_rights` ( + `role_id` int NOT NULL DEFAULT '0', + `right_id` int NOT NULL DEFAULT '0', + PRIMARY KEY (`role_id`,`right_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `role_rights` +-- + +LOCK TABLES `role_rights` WRITE; +/*!40000 ALTER TABLE `role_rights` DISABLE KEYS */; +INSERT INTO `role_rights` VALUES (4,3),(4,6),(4,7),(4,8),(4,9),(4,10),(4,11),(5,3),(5,6),(5,8),(6,1),(6,2),(6,3),(6,6),(6,7),(6,8),(6,9),(6,11),(6,25),(6,27),(7,1),(7,3),(7,6),(7,8),(8,1),(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8),(8,9),(8,10),(8,11),(8,12),(8,13),(8,14),(8,15),(8,16),(8,17),(8,18),(8,19),(8,20),(8,21),(8,22),(8,23),(8,24),(8,25),(8,26),(8,27),(9,1),(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),(9,9),(9,10),(9,11),(9,15),(9,16),(9,24),(9,25),(9,26),(9,27); +/*!40000 ALTER TABLE `role_rights` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `roles` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `description` varchar(100) NOT NULL DEFAULT '', + `notes` text, + PRIMARY KEY (`id`), + UNIQUE KEY `role_rights_roles_descr` (`description`) +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + +LOCK TABLES `roles` WRITE; +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (1,'',NULL),(2,'',NULL),(3,'',NULL),(4,'test designer',NULL),(5,'guest',NULL),(6,'senior tester',NULL),(7,'tester',NULL),(8,'admin',NULL),(9,'leader',NULL); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tcsteps` +-- + +DROP TABLE IF EXISTS `tcsteps`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tcsteps` ( + `id` int unsigned NOT NULL, + `step_number` int NOT NULL DEFAULT '1', + `actions` text, + `expected_results` text, + `active` tinyint(1) NOT NULL DEFAULT '1', + `execution_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 -> manual, 2 -> automated', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tcsteps` +-- + +LOCK TABLES `tcsteps` WRITE; +/*!40000 ALTER TABLE `tcsteps` DISABLE KEYS */; +/*!40000 ALTER TABLE `tcsteps` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tcversions` +-- + +DROP TABLE IF EXISTS `tcversions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tcversions` ( + `id` int unsigned NOT NULL, + `tc_external_id` int unsigned DEFAULT NULL, + `version` smallint unsigned NOT NULL DEFAULT '1', + `layout` smallint unsigned NOT NULL DEFAULT '1', + `status` smallint unsigned NOT NULL DEFAULT '1', + `summary` text, + `preconditions` text, + `importance` smallint unsigned NOT NULL DEFAULT '2', + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater_id` int unsigned DEFAULT NULL, + `modification_ts` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `active` tinyint(1) NOT NULL DEFAULT '1', + `is_open` tinyint(1) NOT NULL DEFAULT '1', + `execution_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 -> manual, 2 -> automated', + `estimated_exec_duration` decimal(6,2) DEFAULT NULL COMMENT 'NULL will be considered as NO DATA Provided by user', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tcversions` +-- + +LOCK TABLES `tcversions` WRITE; +/*!40000 ALTER TABLE `tcversions` DISABLE KEYS */; +INSERT INTO `tcversions` VALUES (32,1,1,1,1,'','',2,1,'2022-05-01 10:08:43',1,'2022-05-01 12:11:47',1,1,1,NULL),(34,2,1,1,1,'','',2,1,'2022-05-01 10:08:56',1,'2022-05-01 12:12:01',1,1,1,NULL),(36,3,1,1,1,'','',2,1,'2022-05-01 10:20:26',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(38,4,1,1,1,'','',2,1,'2022-05-01 10:20:26',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(40,5,1,1,1,'','',2,1,'2022-05-01 10:20:26',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(42,6,1,1,1,'','',2,1,'2022-05-01 10:20:26',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(44,7,1,1,1,'','',2,1,'2022-05-01 10:21:22',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(46,8,1,1,1,'','',2,1,'2022-05-01 10:21:22',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(48,9,1,1,1,'','',2,1,'2022-05-01 10:21:22',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(50,10,1,1,1,'','',2,1,'2022-05-01 10:21:22',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(52,11,1,1,1,'','',2,1,'2022-05-01 10:22:27',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(54,12,1,1,1,'','',2,1,'2022-05-01 10:22:27',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(56,13,1,1,1,'','',2,1,'2022-05-01 10:22:27',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(58,14,1,1,1,'','',2,1,'2022-05-01 10:22:27',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(60,15,1,1,1,'','',2,1,'2022-05-01 10:23:12',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(62,16,1,1,1,'','',2,1,'2022-05-01 10:23:12',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(64,17,1,1,1,'','',2,1,'2022-05-01 10:23:12',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(66,18,1,1,1,'','',2,1,'2022-05-01 10:23:12',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(68,19,1,1,1,'','',2,1,'2022-05-01 10:24:07',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(70,20,1,1,1,'','',2,1,'2022-05-01 10:24:07',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(72,21,1,1,1,'','',2,1,'2022-05-01 10:24:07',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(74,22,1,1,1,'','',2,1,'2022-05-01 10:24:07',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(76,23,1,1,1,'','',2,1,'2022-05-01 10:24:50',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(78,24,1,1,1,'','',2,1,'2022-05-01 10:24:51',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(80,25,1,1,1,'','',2,1,'2022-05-01 10:24:51',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(82,26,1,1,1,'','',2,1,'2022-05-01 10:24:51',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(84,27,1,1,1,'','',2,1,'2022-05-01 10:25:56',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(86,28,1,1,1,'','',2,1,'2022-05-01 10:25:56',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(88,29,1,1,1,'','',2,1,'2022-05-01 10:25:56',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(90,30,1,1,1,'','',2,1,'2022-05-01 10:25:56',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(92,31,1,1,1,'','',2,1,'2022-05-01 10:25:56',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(94,32,1,1,1,'','',2,1,'2022-05-01 10:26:50',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(96,33,1,1,1,'','',2,1,'2022-05-01 10:26:50',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(98,34,1,1,1,'','',2,1,'2022-05-01 10:26:51',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(100,35,1,1,1,'','',2,1,'2022-05-01 10:26:51',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(102,36,1,1,1,'','',2,1,'2022-05-01 10:26:51',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(104,37,1,1,1,'','',2,1,'2022-05-01 10:27:44',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(106,38,1,1,1,'','',2,1,'2022-05-01 10:27:44',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(108,39,1,1,1,'','',2,1,'2022-05-01 10:27:44',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(110,40,1,1,1,'','',2,1,'2022-05-01 10:27:44',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(112,41,1,1,1,'','',2,1,'2022-05-01 10:27:44',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(114,42,1,1,1,'','',2,1,'2022-05-01 10:28:26',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(116,43,1,1,1,'','',2,1,'2022-05-01 10:28:26',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(118,44,1,1,1,'','',2,1,'2022-05-01 10:28:27',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(120,45,1,1,1,'','',2,1,'2022-05-01 10:28:27',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(122,46,1,1,1,'','',2,1,'2022-05-01 10:28:27',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(124,47,1,1,1,'','',2,1,'2022-05-01 10:29:04',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(126,48,1,1,1,'','',2,1,'2022-05-01 10:29:04',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(128,49,1,1,1,'','',2,1,'2022-05-01 10:29:04',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(130,50,1,1,1,'','',2,1,'2022-05-01 10:29:04',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(132,51,1,1,1,'','',2,1,'2022-05-01 10:29:04',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(134,52,1,1,1,'','',2,1,'2022-05-01 10:29:45',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(136,53,1,1,1,'','',2,1,'2022-05-01 10:29:45',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(138,54,1,1,1,'','',2,1,'2022-05-01 10:29:45',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(140,55,1,1,1,'','',2,1,'2022-05-01 10:29:45',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(142,56,1,1,1,'','',2,1,'2022-05-01 10:29:45',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(144,57,1,1,1,'','',2,1,'2022-05-01 10:30:20',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(146,58,1,1,1,'','',2,1,'2022-05-01 10:30:20',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(148,59,1,1,1,'','',2,1,'2022-05-01 10:30:20',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(150,60,1,1,1,'','',2,1,'2022-05-01 10:30:20',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(152,61,1,1,1,'','',2,1,'2022-05-01 10:30:20',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(154,62,1,1,1,'','',2,1,'2022-05-01 10:30:46',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(156,63,1,1,1,'','',2,1,'2022-05-01 10:30:46',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(158,64,1,1,1,'','',2,1,'2022-05-01 10:30:46',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(160,65,1,1,1,'','',2,1,'2022-05-01 10:30:46',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(162,66,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(164,67,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(166,69,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(168,71,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(170,73,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(172,75,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(174,77,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(176,79,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(178,81,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(180,83,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(182,85,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(184,87,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(186,89,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(188,91,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(190,93,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(192,95,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(194,97,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(196,99,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(198,101,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(200,103,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(202,105,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(204,107,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(206,109,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(208,111,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(210,113,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(212,115,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(214,117,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(216,119,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(218,121,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(220,123,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(222,125,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(224,127,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(226,129,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(228,131,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(230,133,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(232,135,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(234,137,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(236,139,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(238,141,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(240,143,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(242,145,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(244,147,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(246,149,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(248,151,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(250,153,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(252,155,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(254,157,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(256,159,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL),(258,161,1,1,1,'','',2,1,'2022-05-01 10:30:47',NULL,'2022-05-01 12:13:31',1,1,1,NULL); +/*!40000 ALTER TABLE `tcversions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Temporary table structure for view `tcversions_without_keywords` +-- + +DROP TABLE IF EXISTS `tcversions_without_keywords`; +/*!50001 DROP VIEW IF EXISTS `tcversions_without_keywords`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `tcversions_without_keywords` AS SELECT + 1 AS `testcase_id`, + 1 AS `id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Temporary table structure for view `tcversions_without_platforms` +-- + +DROP TABLE IF EXISTS `tcversions_without_platforms`; +/*!50001 DROP VIEW IF EXISTS `tcversions_without_platforms`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `tcversions_without_platforms` AS SELECT + 1 AS `testcase_id`, + 1 AS `id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Table structure for table `testcase_keywords` +-- + +DROP TABLE IF EXISTS `testcase_keywords`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testcase_keywords` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `testcase_id` int unsigned NOT NULL DEFAULT '0', + `tcversion_id` int unsigned NOT NULL DEFAULT '0', + `keyword_id` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `idx01_testcase_keywords` (`testcase_id`,`tcversion_id`,`keyword_id`), + KEY `idx02_testcase_keywords` (`tcversion_id`) +) ENGINE=InnoDB AUTO_INCREMENT=133 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testcase_keywords` +-- + +LOCK TABLES `testcase_keywords` WRITE; +/*!40000 ALTER TABLE `testcase_keywords` DISABLE KEYS */; +INSERT INTO `testcase_keywords` VALUES (1,31,92,1),(2,31,92,6),(3,33,96,2),(4,33,96,6),(5,35,100,1),(6,35,100,6),(7,37,104,2),(8,37,104,6),(9,39,108,1),(10,39,108,6),(11,41,112,2),(12,41,112,6),(13,43,116,1),(14,43,116,6),(15,45,120,2),(16,45,120,6),(17,47,124,1),(18,47,124,6),(19,49,128,2),(20,49,128,6),(21,51,132,1),(22,51,132,6),(23,53,136,2),(24,53,136,6),(25,55,140,1),(26,55,140,6),(27,57,144,2),(28,57,144,6),(29,59,148,1),(30,59,148,6),(31,61,152,2),(32,61,152,6),(33,63,156,1),(34,63,156,6),(35,65,160,2),(36,65,160,6),(37,67,164,1),(38,67,164,6),(39,69,166,2),(40,69,166,6),(41,71,168,1),(42,71,168,6),(43,73,170,2),(44,73,170,6),(45,75,172,1),(46,75,172,6),(47,77,174,2),(48,77,174,6),(49,79,176,1),(50,79,176,6),(51,81,178,2),(52,81,178,6),(53,83,180,1),(54,83,180,6),(55,85,182,2),(56,85,182,6),(57,87,184,1),(58,87,184,6),(59,89,186,2),(60,89,186,6),(61,91,188,2),(62,91,188,6),(63,93,190,1),(64,93,190,6),(65,95,192,2),(66,95,192,6),(67,97,194,1),(68,97,194,6),(69,99,196,2),(70,99,196,6),(71,101,198,2),(72,101,198,6),(73,103,200,1),(74,103,200,6),(75,105,202,2),(76,105,202,6),(77,107,204,1),(78,107,204,6),(79,109,206,2),(80,109,206,6),(81,111,208,2),(82,111,208,6),(83,113,210,1),(84,113,210,6),(85,115,212,2),(86,115,212,6),(87,117,214,1),(88,117,214,6),(89,119,216,2),(90,119,216,6),(91,121,218,2),(92,121,218,6),(93,123,220,1),(94,123,220,6),(95,125,222,2),(96,125,222,6),(97,127,224,1),(98,127,224,6),(99,129,226,2),(100,129,226,6),(101,131,228,2),(102,131,228,6),(103,133,230,1),(104,133,230,6),(105,135,232,2),(106,135,232,6),(107,137,234,1),(108,137,234,6),(109,139,236,2),(110,139,236,6),(111,141,238,2),(112,141,238,6),(113,143,240,1),(114,143,240,6),(115,145,242,2),(116,145,242,6),(117,147,244,1),(118,147,244,6),(119,149,246,2),(120,149,246,6),(121,151,248,2),(122,151,248,6),(123,153,250,1),(124,153,250,6),(125,155,252,2),(126,155,252,6),(127,157,254,1),(128,157,254,6),(129,159,256,2),(130,159,256,6),(131,161,258,2),(132,161,258,6); +/*!40000 ALTER TABLE `testcase_keywords` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testcase_platforms` +-- + +DROP TABLE IF EXISTS `testcase_platforms`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testcase_platforms` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `testcase_id` int unsigned NOT NULL DEFAULT '0', + `tcversion_id` int unsigned NOT NULL DEFAULT '0', + `platform_id` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `idx01_testcase_platform` (`testcase_id`,`tcversion_id`,`platform_id`), + KEY `idx02_testcase_platform` (`tcversion_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testcase_platforms` +-- + +LOCK TABLES `testcase_platforms` WRITE; +/*!40000 ALTER TABLE `testcase_platforms` DISABLE KEYS */; +/*!40000 ALTER TABLE `testcase_platforms` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testcase_relations` +-- + +DROP TABLE IF EXISTS `testcase_relations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testcase_relations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `source_id` int unsigned NOT NULL, + `destination_id` int unsigned NOT NULL, + `link_status` tinyint(1) NOT NULL DEFAULT '1', + `relation_type` smallint unsigned NOT NULL DEFAULT '1', + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testcase_relations` +-- + +LOCK TABLES `testcase_relations` WRITE; +/*!40000 ALTER TABLE `testcase_relations` DISABLE KEYS */; +/*!40000 ALTER TABLE `testcase_relations` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testcase_script_links` +-- + +DROP TABLE IF EXISTS `testcase_script_links`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testcase_script_links` ( + `tcversion_id` int unsigned NOT NULL DEFAULT '0', + `project_key` varchar(64) NOT NULL, + `repository_name` varchar(64) NOT NULL, + `code_path` varchar(255) NOT NULL, + `branch_name` varchar(64) DEFAULT NULL, + `commit_id` varchar(40) DEFAULT NULL, + PRIMARY KEY (`tcversion_id`,`project_key`,`repository_name`,`code_path`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testcase_script_links` +-- + +LOCK TABLES `testcase_script_links` WRITE; +/*!40000 ALTER TABLE `testcase_script_links` DISABLE KEYS */; +/*!40000 ALTER TABLE `testcase_script_links` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testplan_platforms` +-- + +DROP TABLE IF EXISTS `testplan_platforms`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testplan_platforms` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `testplan_id` int unsigned NOT NULL, + `platform_id` int unsigned NOT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_testplan_platforms` (`testplan_id`,`platform_id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COMMENT='Connects a testplan with platforms'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testplan_platforms` +-- + +LOCK TABLES `testplan_platforms` WRITE; +/*!40000 ALTER TABLE `testplan_platforms` DISABLE KEYS */; +INSERT INTO `testplan_platforms` VALUES (1,4,1,1),(2,4,2,1),(3,4,4,1),(4,4,3,1); +/*!40000 ALTER TABLE `testplan_platforms` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testplan_tcversions` +-- + +DROP TABLE IF EXISTS `testplan_tcversions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testplan_tcversions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `testplan_id` int unsigned NOT NULL DEFAULT '0', + `tcversion_id` int unsigned NOT NULL DEFAULT '0', + `node_order` int unsigned NOT NULL DEFAULT '1', + `urgency` smallint NOT NULL DEFAULT '2', + `platform_id` int unsigned NOT NULL DEFAULT '0', + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `testplan_tcversions_tplan_tcversion` (`testplan_id`,`tcversion_id`,`platform_id`) +) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testplan_tcversions` +-- + +LOCK TABLES `testplan_tcversions` WRITE; +/*!40000 ALTER TABLE `testplan_tcversions` DISABLE KEYS */; +INSERT INTO `testplan_tcversions` VALUES (1,4,32,1000,2,1,1,'2022-05-01 10:33:49'),(2,4,32,1000,2,2,1,'2022-05-01 10:33:49'),(3,4,32,1000,2,4,1,'2022-05-01 10:33:49'),(4,4,32,1000,2,3,1,'2022-05-01 10:33:49'),(5,4,34,1010,2,1,1,'2022-05-01 10:33:49'),(6,4,34,1010,2,2,1,'2022-05-01 10:33:49'),(7,4,34,1010,2,4,1,'2022-05-01 10:33:49'),(8,4,34,1010,2,3,1,'2022-05-01 10:33:49'),(9,4,44,1000,2,1,1,'2022-05-01 10:34:05'),(10,4,44,1000,2,2,1,'2022-05-01 10:34:05'),(11,4,44,1000,2,4,1,'2022-05-01 10:34:05'),(12,4,48,1000,2,1,1,'2022-05-01 10:34:05'),(13,4,48,1000,2,2,1,'2022-05-01 10:34:05'),(14,4,48,1000,2,4,1,'2022-05-01 10:34:05'),(15,4,46,1010,2,1,1,'2022-05-01 10:34:05'),(16,4,46,1010,2,2,1,'2022-05-01 10:34:05'),(17,4,46,1010,2,4,1,'2022-05-01 10:34:05'),(18,4,50,1010,2,1,1,'2022-05-01 10:34:05'),(19,4,50,1010,2,2,1,'2022-05-01 10:34:05'),(20,4,50,1010,2,4,1,'2022-05-01 10:34:05'),(21,4,94,1000,2,4,1,'2022-05-01 10:34:41'),(22,4,94,1000,2,3,1,'2022-05-01 10:34:41'),(23,4,98,1000,2,4,1,'2022-05-01 10:34:41'),(24,4,98,1000,2,3,1,'2022-05-01 10:34:41'),(25,4,96,1010,2,4,1,'2022-05-01 10:34:41'),(26,4,96,1010,2,3,1,'2022-05-01 10:34:41'),(27,4,100,1010,2,4,1,'2022-05-01 10:34:41'),(28,4,100,1010,2,3,1,'2022-05-01 10:34:41'),(29,4,102,1010,2,4,1,'2022-05-01 10:34:41'),(30,4,102,1010,2,3,1,'2022-05-01 10:34:41'),(31,4,104,1000,2,4,1,'2022-05-01 10:34:41'),(32,4,104,1000,2,3,1,'2022-05-01 10:34:41'),(33,4,108,1000,2,4,1,'2022-05-01 10:34:41'),(34,4,108,1000,2,3,1,'2022-05-01 10:34:41'),(35,4,106,1010,2,4,1,'2022-05-01 10:34:41'),(36,4,106,1010,2,3,1,'2022-05-01 10:34:41'),(37,4,110,1010,2,4,1,'2022-05-01 10:34:41'),(38,4,110,1010,2,3,1,'2022-05-01 10:34:41'),(39,4,112,1010,2,4,1,'2022-05-01 10:34:41'),(40,4,112,1010,2,3,1,'2022-05-01 10:34:41'),(41,4,144,1000,2,1,1,'2022-05-01 10:34:56'),(42,4,144,1000,2,2,1,'2022-05-01 10:34:56'),(43,4,144,1000,2,4,1,'2022-05-01 10:34:56'),(44,4,144,1000,2,3,1,'2022-05-01 10:34:56'),(45,4,148,1000,2,1,1,'2022-05-01 10:34:56'),(46,4,148,1000,2,2,1,'2022-05-01 10:34:56'),(47,4,148,1000,2,4,1,'2022-05-01 10:34:56'),(48,4,148,1000,2,3,1,'2022-05-01 10:34:56'),(49,4,146,1010,2,1,1,'2022-05-01 10:34:56'),(50,4,146,1010,2,2,1,'2022-05-01 10:34:56'),(51,4,146,1010,2,4,1,'2022-05-01 10:34:56'),(52,4,146,1010,2,3,1,'2022-05-01 10:34:56'),(53,4,150,1010,2,1,1,'2022-05-01 10:34:56'),(54,4,150,1010,2,2,1,'2022-05-01 10:34:56'),(55,4,150,1010,2,4,1,'2022-05-01 10:34:56'),(56,4,150,1010,2,3,1,'2022-05-01 10:34:56'),(57,4,152,1010,2,1,1,'2022-05-01 10:34:56'),(58,4,152,1010,2,2,1,'2022-05-01 10:34:56'),(59,4,152,1010,2,4,1,'2022-05-01 10:34:56'),(60,4,152,1010,2,3,1,'2022-05-01 10:34:56'); +/*!40000 ALTER TABLE `testplan_tcversions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testplans` +-- + +DROP TABLE IF EXISTS `testplans`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testplans` ( + `id` int unsigned NOT NULL, + `testproject_id` int unsigned NOT NULL DEFAULT '0', + `notes` text, + `active` tinyint(1) NOT NULL DEFAULT '1', + `is_open` tinyint(1) NOT NULL DEFAULT '1', + `is_public` tinyint(1) NOT NULL DEFAULT '1', + `api_key` varchar(64) NOT NULL DEFAULT '829a2ded3ed0829a2dedd8ab81dfa2c77e8235bc3ed0d8ab81dfa2c77e8235bc', + PRIMARY KEY (`id`), + UNIQUE KEY `testplans_api_key` (`api_key`), + KEY `testplans_testproject_id_active` (`testproject_id`,`active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testplans` +-- + +LOCK TABLES `testplans` WRITE; +/*!40000 ALTER TABLE `testplans` DISABLE KEYS */; +INSERT INTO `testplans` VALUES (4,2,'

P Zero™ Red, a supersoft for street circuits. Of the four slick tyres, this is the only one to remain unchanged from the 2011 season. It showed itself to be particularly versatile, offering high peaks of performance over slow and twisty circuits that are characterised by slippery asphalt and low lateral loadings. This is the ideal compound for street circuits or semipermanent facilities.

',1,1,1,'829a2ded3ed0829a2dedd8ab81dfa2c77e8235bc3ed0d8ab81dfa2c77e8235ba'),(5,2,'

P Zero™ Yellow, softer with less blistering. The new soft tyre is well suited to circuits with low tyre wear. It is designed to offer a high level of grip coupled with a significant amount of degradation, resulting in a comparatively short lifespan that will give the teams a greater number of options with pit stop strategy and even closer racing. Compared to the equivalent tyre in 2011, the new soft offers greater thermal resistance to reduce the risk of blistering. Tested for the first time during free practice at last year’s Abu Dhabi Grand Prix, the new soft tyre is set to be one of the most frequent nominations in 2012, together with the new medium tyre. This combination offers a great deal of flexibility and also a rapid warm-up time.

',1,1,1,'829a2ded3ed0829a2dedd8ab81dfa2c77e8235bc3ed0d8ab81dfa2c77e8235bb'),(6,2,'

P Zero™ White, the medium tyre that is well suited to all conditions. This extremely versatile tyre adapts itself well to all sorts of track conditions, particularly when asphalt and circuit characteristics are variable. The brand new P Zero™ White is intended as the ‘option’ tyre on tracks with high temperatures or abrasive surfaces and as the ‘prime’ tyre on tracks that are less severe with fewer demands on the tyres. The new medium compound was tried out last year during free practice at the German Grand Prix and made another appearance during the young driver test in Abu Dhabi.

',1,1,1,'829a2ded3ed0829a2dedd8ab81dfa2c77e8235bc3ed0d8ab81dfa2c77e8235bc'),(7,2,'

P Zero™ Silver, hard but not inflexible. The new hard tyre guarantees maximum durability and the least degradation, together with optimal resistance to the most extreme conditions, but is not as hard as the equivalent tyre last year. The P Zero™ Silver is ideal for long runs, taking more time to warm up, as well as being suited to circuits with abrasive asphalt, big lateral forces and high temperatures. The new P Zero™ Silver was tested at the Barcelona circuit by Pirelli’s test driver Lucas di Grassi, and is the only one of the new compounds that the regular drivers have not yet experienced.

',1,1,1,'829a2ded3ed0829a2dedd8ab81dfa2c77e8235bc3ed0d8ab81dfa2c77e8235bd'),(8,3,'

Cinturato™ Green, the intermediate for light rain. After the excellent performances seen from this tyre throughout the 2011 season during particularly demanding races such as the Canadian Grand Prix, Pirelli’s engineers decided not to make any changes to the intermediate tyres. The shallower grooves compared to the full wet tyres mean that the intermediates do not drain away as much water, making this the ideal choice for wet or drying asphalt, without compromising on performance.

',1,1,1,'829a2ded3ed0829a2dedd8ab81dfa2c77e8235bc3ed0d8ab81dfa2c77e8235be'),(9,3,'

Cinturato™ Blue, the full wets. Of the two wet tyres, only the full wet has been significantly altered compared to the 2011 version. The changes relate to the rear tyres, which use a different profile in order to optimise the dispersal of water in case of aquaplaning and guarantee a greater degree of driving precision. Characterised by deep grooves, similar to those seen on a road car tyre, the wet tyres are designed to expel more than 60 litres of water per second at a speed of 300 kph: six times more than a road car tyre, which disperses about 10 litres per second at a much lower speed.

',1,1,1,'829a2ded3ed0829a2dedd8ab81dfa2c77e8235bc3ed0d8ab81dfa2c77e8235bf'); +/*!40000 ALTER TABLE `testplans` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testproject_codetracker` +-- + +DROP TABLE IF EXISTS `testproject_codetracker`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testproject_codetracker` ( + `testproject_id` int unsigned NOT NULL, + `codetracker_id` int unsigned NOT NULL, + PRIMARY KEY (`testproject_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testproject_codetracker` +-- + +LOCK TABLES `testproject_codetracker` WRITE; +/*!40000 ALTER TABLE `testproject_codetracker` DISABLE KEYS */; +/*!40000 ALTER TABLE `testproject_codetracker` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testproject_issuetracker` +-- + +DROP TABLE IF EXISTS `testproject_issuetracker`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testproject_issuetracker` ( + `testproject_id` int unsigned NOT NULL, + `issuetracker_id` int unsigned NOT NULL, + PRIMARY KEY (`testproject_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testproject_issuetracker` +-- + +LOCK TABLES `testproject_issuetracker` WRITE; +/*!40000 ALTER TABLE `testproject_issuetracker` DISABLE KEYS */; +/*!40000 ALTER TABLE `testproject_issuetracker` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testproject_reqmgrsystem` +-- + +DROP TABLE IF EXISTS `testproject_reqmgrsystem`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testproject_reqmgrsystem` ( + `testproject_id` int unsigned NOT NULL, + `reqmgrsystem_id` int unsigned NOT NULL, + PRIMARY KEY (`testproject_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testproject_reqmgrsystem` +-- + +LOCK TABLES `testproject_reqmgrsystem` WRITE; +/*!40000 ALTER TABLE `testproject_reqmgrsystem` DISABLE KEYS */; +/*!40000 ALTER TABLE `testproject_reqmgrsystem` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testprojects` +-- + +DROP TABLE IF EXISTS `testprojects`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testprojects` ( + `id` int unsigned NOT NULL, + `notes` text, + `color` varchar(12) NOT NULL DEFAULT '#9BD', + `active` tinyint(1) NOT NULL DEFAULT '1', + `option_reqs` tinyint(1) NOT NULL DEFAULT '0', + `option_priority` tinyint(1) NOT NULL DEFAULT '0', + `option_automation` tinyint(1) NOT NULL DEFAULT '0', + `options` text, + `prefix` varchar(16) NOT NULL, + `tc_counter` int unsigned NOT NULL DEFAULT '0', + `is_public` tinyint(1) NOT NULL DEFAULT '1', + `issue_tracker_enabled` tinyint(1) NOT NULL DEFAULT '0', + `code_tracker_enabled` tinyint(1) NOT NULL DEFAULT '0', + `reqmgr_integration_enabled` tinyint(1) NOT NULL DEFAULT '0', + `api_key` varchar(64) NOT NULL DEFAULT '0d8ab81dfa2c77e8235bc829a2ded3edfa2c78235bc829a27eded3ed0d8ab81d', + PRIMARY KEY (`id`), + UNIQUE KEY `testprojects_prefix` (`prefix`), + UNIQUE KEY `testprojects_api_key` (`api_key`), + KEY `testprojects_id_active` (`id`,`active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testprojects` +-- + +LOCK TABLES `testprojects` WRITE; +/*!40000 ALTER TABLE `testprojects` DISABLE KEYS */; +INSERT INTO `testprojects` VALUES (2,'

In accordance with the regulations laid down by the FIA (Fédération Internationale de l\'Automobile) Pirelli will supply two different types of tyre designed for two different types of use.
\r\nThe first type of tyre has been designed for dry surfaces, while the second is for wet surfaces.

','',1,0,0,0,'O:8:\"stdClass\":4:{s:19:\"requirementsEnabled\";i:1;s:19:\"testPriorityEnabled\";i:1;s:17:\"automationEnabled\";i:1;s:16:\"inventoryEnabled\";i:1;}','PDT',66,1,0,0,0,'0d8ab81dfa2c77e8235bc829a2ded3edfa2c78235bc829a27eded3ed0d8ab81d'),(3,'

in accordance with the regulations laid down by the FIA (Fédération Internationale de l\'Automobile) Pirelli will supply two different types of tyre designed for two different types of use.
\r\nThe first type of tyre has been designed for dry surfaces, while the second is for wet surfaces.

','',1,0,0,0,'O:8:\"stdClass\":4:{s:19:\"requirementsEnabled\";i:1;s:19:\"testPriorityEnabled\";i:1;s:17:\"automationEnabled\";i:1;s:16:\"inventoryEnabled\";i:1;}','PWT',0,1,0,0,0,'0d8ab81dfa2c77e8235bc829a2ded3edfa2c78235bc829a27eded3ed0d8ab81e'); +/*!40000 ALTER TABLE `testprojects` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `testsuites` +-- + +DROP TABLE IF EXISTS `testsuites`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `testsuites` ( + `id` int unsigned NOT NULL, + `details` text, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `testsuites` +-- + +LOCK TABLES `testsuites` WRITE; +/*!40000 ALTER TABLE `testsuites` DISABLE KEYS */; +INSERT INTO `testsuites` VALUES (10,''),(11,'


\r\n
\r\n
\r\n

'),(12,''),(13,''),(14,''),(15,'


\r\n

'),(16,''),(17,''),(18,''),(19,''),(20,''),(21,''),(22,''),(23,''),(24,''),(25,''),(26,''),(27,''),(28,''),(29,''),(30,''); +/*!40000 ALTER TABLE `testsuites` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `text_templates` +-- + +DROP TABLE IF EXISTS `text_templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `text_templates` ( + `id` int unsigned NOT NULL, + `type` smallint unsigned NOT NULL, + `title` varchar(100) NOT NULL, + `template_data` text, + `author_id` int unsigned DEFAULT NULL, + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `is_public` tinyint(1) NOT NULL DEFAULT '0', + UNIQUE KEY `idx_text_templates` (`type`,`title`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Global Project Templates'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `text_templates` +-- + +LOCK TABLES `text_templates` WRITE; +/*!40000 ALTER TABLE `text_templates` DISABLE KEYS */; +/*!40000 ALTER TABLE `text_templates` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `transactions` +-- + +DROP TABLE IF EXISTS `transactions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `transactions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `entry_point` varchar(45) NOT NULL DEFAULT '', + `start_time` int unsigned NOT NULL DEFAULT '0', + `end_time` int unsigned NOT NULL DEFAULT '0', + `user_id` int unsigned NOT NULL DEFAULT '0', + `session_id` varchar(45) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `transactions` +-- + +LOCK TABLES `transactions` WRITE; +/*!40000 ALTER TABLE `transactions` DISABLE KEYS */; +INSERT INTO `transactions` VALUES (1,'/tl193-untouched/lib/general/navBar.php',1335862631,1335862631,210,'95p2svhjqusr1vjbhgj9bird23'),(2,'/tl193-untouched/lib/general/mainPage.php',1335862631,1335862631,210,'95p2svhjqusr1vjbhgj9bird23'),(3,'/tl193-untouched/lib/general/mainPage.php',1335862696,1335862696,210,'95p2svhjqusr1vjbhgj9bird23'),(4,'/tl193-untouched/lib/general/navBar.php',1335862696,1335862696,210,'95p2svhjqusr1vjbhgj9bird23'),(5,'/development/tl-old/tl193-untouched/login.php',1335862713,1335862713,1,'190eh67vq7bsrde26gde2g46c3'),(6,'/tl193-untouched/lib/general/mainPage.php',1335862714,1335862714,1,'190eh67vq7bsrde26gde2g46c3'),(7,'/tl193-untouched/lib/project/projectEdit.php',1335862746,1335862746,1,'190eh67vq7bsrde26gde2g46c3'),(8,'/tl193-untouched/lib/project/projectEdit.php',1335864014,1335864014,1,'190eh67vq7bsrde26gde2g46c3'),(9,'/tl193-untouched/lib/project/projectEdit.php',1335864053,1335864053,1,'190eh67vq7bsrde26gde2g46c3'),(10,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864120,1335864120,1,'190eh67vq7bsrde26gde2g46c3'),(11,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864160,1335864160,1,'190eh67vq7bsrde26gde2g46c3'),(12,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864193,1335864193,1,'190eh67vq7bsrde26gde2g46c3'),(13,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864246,1335864246,1,'190eh67vq7bsrde26gde2g46c3'),(14,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864293,1335864294,1,'190eh67vq7bsrde26gde2g46c3'),(15,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864326,1335864326,1,'190eh67vq7bsrde26gde2g46c3'),(16,'/tl193-untouched/lib/project/projectEdit.php',1335864347,1335864347,1,'190eh67vq7bsrde26gde2g46c3'),(17,'/tl193-untouched/lib/plan/buildEdit.php',1335864400,1335864400,1,'190eh67vq7bsrde26gde2g46c3'),(18,'/tl193-untouched/lib/plan/buildEdit.php',1335864414,1335864414,1,'190eh67vq7bsrde26gde2g46c3'),(19,'/tl193-untouched/lib/plan/buildEdit.php',1335864428,1335864429,1,'190eh67vq7bsrde26gde2g46c3'),(20,'/tl193-untouched/lib/plan/buildEdit.php',1335864459,1335864459,1,'190eh67vq7bsrde26gde2g46c3'),(21,'/tl193-untouched/lib/plan/buildEdit.php',1335864466,1335864466,1,'190eh67vq7bsrde26gde2g46c3'),(22,'/tl193-untouched/lib/plan/buildEdit.php',1335864473,1335864473,1,'190eh67vq7bsrde26gde2g46c3'),(23,'/lib/keywords/keywordsEdit.php',1335866991,1335866991,1,'190eh67vq7bsrde26gde2g46c3'),(24,'/lib/keywords/keywordsEdit.php',1335867005,1335867005,1,'190eh67vq7bsrde26gde2g46c3'),(25,'/lib/keywords/keywordsEdit.php',1335867022,1335867022,1,'190eh67vq7bsrde26gde2g46c3'),(26,'/lib/keywords/keywordsEdit.php',1335867034,1335867034,1,'190eh67vq7bsrde26gde2g46c3'),(27,'/lib/keywords/keywordsEdit.php',1335867048,1335867048,1,'190eh67vq7bsrde26gde2g46c3'),(28,'/lib/keywords/keywordsEdit.php',1335867057,1335867057,1,'190eh67vq7bsrde26gde2g46c3'),(29,'/tl193-untouched/lib/testcases/tcEdit.php',1335867108,1335867108,1,'190eh67vq7bsrde26gde2g46c3'),(30,'/tl193-untouched/lib/testcases/tcEdit.php',1335867121,1335867121,1,'190eh67vq7bsrde26gde2g46c3'),(31,'/tl193-untouched/lib/testcases/tcImport.php',1335867626,1335867626,1,'190eh67vq7bsrde26gde2g46c3'),(32,'/tl193-untouched/lib/testcases/tcImport.php',1335867682,1335867682,1,'190eh67vq7bsrde26gde2g46c3'),(33,'/tl193-untouched/lib/testcases/tcImport.php',1335867747,1335867747,1,'190eh67vq7bsrde26gde2g46c3'),(34,'/tl193-untouched/lib/testcases/tcImport.php',1335867792,1335867792,1,'190eh67vq7bsrde26gde2g46c3'),(35,'/tl193-untouched/lib/testcases/tcImport.php',1335867847,1335867847,1,'190eh67vq7bsrde26gde2g46c3'),(36,'/tl193-untouched/lib/testcases/tcImport.php',1335867890,1335867891,1,'190eh67vq7bsrde26gde2g46c3'),(37,'/tl193-untouched/lib/testcases/tcImport.php',1335867956,1335867956,1,'190eh67vq7bsrde26gde2g46c3'),(38,'/tl193-untouched/lib/testcases/tcImport.php',1335868010,1335868011,1,'190eh67vq7bsrde26gde2g46c3'),(39,'/tl193-untouched/lib/testcases/tcImport.php',1335868064,1335868064,1,'190eh67vq7bsrde26gde2g46c3'),(40,'/tl193-untouched/lib/testcases/tcImport.php',1335868106,1335868107,1,'190eh67vq7bsrde26gde2g46c3'),(41,'/tl193-untouched/lib/testcases/tcImport.php',1335868144,1335868144,1,'190eh67vq7bsrde26gde2g46c3'),(42,'/tl193-untouched/lib/testcases/tcImport.php',1335868185,1335868185,1,'190eh67vq7bsrde26gde2g46c3'),(43,'/tl193-untouched/lib/testcases/tcImport.php',1335868220,1335868220,1,'190eh67vq7bsrde26gde2g46c3'),(44,'/tl193-untouched/lib/testcases/tcImport.php',1335868246,1335868247,1,'190eh67vq7bsrde26gde2g46c3'),(45,'/lib/plan/planAddTCNavigator.php',1335868398,1335868399,1,'190eh67vq7bsrde26gde2g46c3'),(46,'/tl193-untouched/lib/plan/planAddTC.php',1335868429,1335868429,1,'190eh67vq7bsrde26gde2g46c3'),(47,'/tl193-untouched/lib/plan/planAddTC.php',1335868446,1335868446,1,'190eh67vq7bsrde26gde2g46c3'),(48,'/tl193-untouched/lib/plan/planAddTC.php',1335868481,1335868481,1,'190eh67vq7bsrde26gde2g46c3'),(49,'/tl193-untouched/lib/plan/planAddTC.php',1335868496,1335868496,1,'190eh67vq7bsrde26gde2g46c3'),(50,'/login.php',1707059206,1707059206,0,NULL),(51,'/login.php',1707059211,1707059211,1,'4u1jb7quupeh336m1aj69j5qu5'),(52,'/lib/project/projectView.php',1707059214,1707059214,1,'4u1jb7quupeh336m1aj69j5qu5'),(53,'/lib/usermanagement/usersAssign.php',1707059224,1707059224,1,'4u1jb7quupeh336m1aj69j5qu5'),(54,'/lib/cfields/cfieldsTprojectAssign.php',1707059232,1707059232,1,'4u1jb7quupeh336m1aj69j5qu5'),(55,'/lib/results/printDocument.php',1707059578,1707059578,1,'4u1jb7quupeh336m1aj69j5qu5'),(56,'/lib/plan/buildView.php',1707059648,1707059648,1,'4u1jb7quupeh336m1aj69j5qu5'),(57,'/lib/execute/execDashboard.php',1707059672,1707059672,1,'4u1jb7quupeh336m1aj69j5qu5'),(58,'/lib/execute/execDashboard.php',1707059672,1707059672,1,'4u1jb7quupeh336m1aj69j5qu5'),(59,'/lib/execute/execSetResults.php',1707059678,1707059678,1,'4u1jb7quupeh336m1aj69j5qu5'),(60,'/lib/execute/execSetResults.php',1707059678,1707059678,1,'4u1jb7quupeh336m1aj69j5qu5'),(61,'/lib/execute/execSetResults.php',1707059679,1707059679,1,'4u1jb7quupeh336m1aj69j5qu5'),(62,'/lib/plan/planAddTC.php',1707059689,1707059689,1,'4u1jb7quupeh336m1aj69j5qu5'),(63,'/lib/plan/planAddTC.php',1707059692,1707059692,1,'4u1jb7quupeh336m1aj69j5qu5'),(64,'/logout.php',1707059711,1707059711,1,'4u1jb7quupeh336m1aj69j5qu5'); +/*!40000 ALTER TABLE `transactions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Temporary table structure for view `tsuites_tree_depth_2` +-- + +DROP TABLE IF EXISTS `tsuites_tree_depth_2`; +/*!50001 DROP VIEW IF EXISTS `tsuites_tree_depth_2`*/; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE VIEW `tsuites_tree_depth_2` AS SELECT + 1 AS `prefix`, + 1 AS `testproject_name`, + 1 AS `level1_name`, + 1 AS `level2_name`, + 1 AS `testproject_id`, + 1 AS `level1_id`, + 1 AS `level2_id` */; +SET character_set_client = @saved_cs_client; + +-- +-- Table structure for table `user_assignments` +-- + +DROP TABLE IF EXISTS `user_assignments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_assignments` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `type` int unsigned NOT NULL DEFAULT '1', + `feature_id` int unsigned NOT NULL DEFAULT '0', + `user_id` int unsigned DEFAULT '0', + `build_id` int unsigned DEFAULT '0', + `deadline_ts` datetime DEFAULT NULL, + `assigner_id` int unsigned DEFAULT '0', + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `status` int unsigned DEFAULT '1', + PRIMARY KEY (`id`), + KEY `user_assignments_feature_id` (`feature_id`) +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_assignments` +-- + +LOCK TABLES `user_assignments` WRITE; +/*!40000 ALTER TABLE `user_assignments` DISABLE KEYS */; +INSERT INTO `user_assignments` VALUES (1,1,1,4,1,NULL,1,'2022-05-01 10:36:33',1),(2,1,2,3,1,NULL,1,'2022-05-01 10:36:33',1),(3,1,4,2,1,NULL,1,'2022-05-01 10:36:33',1),(4,1,3,5,1,NULL,1,'2022-05-01 10:36:33',1),(5,1,5,4,1,NULL,1,'2022-05-01 10:36:33',1),(6,1,6,3,1,NULL,1,'2022-05-01 10:36:33',1),(7,1,8,2,1,NULL,1,'2022-05-01 10:36:33',1),(8,1,7,5,1,NULL,1,'2022-05-01 10:36:33',1),(9,1,9,4,1,NULL,1,'2022-05-01 10:36:58',1),(10,1,10,3,1,NULL,1,'2022-05-01 10:36:58',1),(11,1,11,5,1,NULL,1,'2022-05-01 10:36:58',1),(12,1,12,4,1,NULL,1,'2022-05-01 10:36:58',1),(13,1,13,3,1,NULL,1,'2022-05-01 10:36:58',1),(14,1,14,5,1,NULL,1,'2022-05-01 10:36:58',1),(15,1,22,2,1,NULL,1,'2022-05-01 10:37:46',1),(16,1,24,2,1,NULL,1,'2022-05-01 10:37:46',1),(17,1,23,5,1,NULL,1,'2022-05-01 10:37:46',1),(18,1,26,2,1,NULL,1,'2022-05-01 10:37:46',1),(19,1,25,5,1,NULL,1,'2022-05-01 10:37:46',1),(20,1,28,2,1,NULL,1,'2022-05-01 10:37:46',1),(21,1,27,5,1,NULL,1,'2022-05-01 10:37:46',1),(22,1,30,2,1,NULL,1,'2022-05-01 10:37:46',1); +/*!40000 ALTER TABLE `user_assignments` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_group` +-- + +DROP TABLE IF EXISTS `user_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_group` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(100) NOT NULL, + `description` text, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_user_group` (`title`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_group` +-- + +LOCK TABLES `user_group` WRITE; +/*!40000 ALTER TABLE `user_group` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_group_assign` +-- + +DROP TABLE IF EXISTS `user_group_assign`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_group_assign` ( + `usergroup_id` int unsigned NOT NULL, + `user_id` int unsigned NOT NULL, + UNIQUE KEY `idx_user_group_assign` (`usergroup_id`,`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_group_assign` +-- + +LOCK TABLES `user_group_assign` WRITE; +/*!40000 ALTER TABLE `user_group_assign` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_group_assign` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_testplan_roles` +-- + +DROP TABLE IF EXISTS `user_testplan_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_testplan_roles` ( + `user_id` int NOT NULL DEFAULT '0', + `testplan_id` int NOT NULL DEFAULT '0', + `role_id` int NOT NULL DEFAULT '0', + PRIMARY KEY (`user_id`,`testplan_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_testplan_roles` +-- + +LOCK TABLES `user_testplan_roles` WRITE; +/*!40000 ALTER TABLE `user_testplan_roles` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_testplan_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_testproject_roles` +-- + +DROP TABLE IF EXISTS `user_testproject_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_testproject_roles` ( + `user_id` int NOT NULL DEFAULT '0', + `testproject_id` int NOT NULL DEFAULT '0', + `role_id` int NOT NULL DEFAULT '0', + PRIMARY KEY (`user_id`,`testproject_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_testproject_roles` +-- + +LOCK TABLES `user_testproject_roles` WRITE; +/*!40000 ALTER TABLE `user_testproject_roles` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_testproject_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `login` varchar(100) NOT NULL DEFAULT '', + `password` varchar(255) NOT NULL DEFAULT '', + `role_id` int unsigned NOT NULL DEFAULT '0', + `email` varchar(100) NOT NULL DEFAULT '', + `first` varchar(50) NOT NULL DEFAULT '', + `last` varchar(50) NOT NULL DEFAULT '', + `locale` varchar(10) NOT NULL DEFAULT 'en_GB', + `default_testproject_id` int DEFAULT NULL, + `active` tinyint(1) NOT NULL DEFAULT '1', + `script_key` varchar(32) DEFAULT NULL, + `cookie_string` varchar(64) NOT NULL DEFAULT '', + `auth_method` varchar(10) DEFAULT '', + `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `expiration_date` date DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `users_login` (`login`), + UNIQUE KEY `users_cookie_string` (`cookie_string`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COMMENT='User information'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + +LOCK TABLES `users` WRITE; +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES (1,'admin','$2y$10$R2V1vQ8341Pamp7xz5XwPuMGNWlfkukqeanCHHFZE19rHbZDDGeD6',8,'','Testlink','Administrator','en_GB',NULL,1,NULL,'36ddb442ea04877d8ff949e6120843c405cf032131f34b2d54f379eac0cc1b88',NULL,'2022-05-01 10:36:33',NULL),(2,'Mark.Webber','9651cbc7c0b5fb1a81f2858a07813c82',8,'Mark.Webber@formulaone.com','Mark','Webber','en_GB',NULL,1,'DEVKEY-Webber','b',NULL,'2022-05-01 10:36:33',NULL),(3,'Lewis.Hamilton','9651cbc7c0b5fb1a81f2858a07813c82',9,'Lewis.Hamilton@formulaone.com','Lewis','Hamilton','it_IT',NULL,1,'DEVKEY-Hamilton','c',NULL,'2022-05-01 10:36:33',NULL),(4,'Fernando.Alonso','9651cbc7c0b5fb1a81f2858a07813c82',6,'Fernando.Alonso@formulaone.com','Fernando','Alonso','en_GB',NULL,1,'DEVKEY-Alonso','d',NULL,'2022-05-01 10:36:33',NULL),(5,'Michael.Schumacher','9651cbc7c0b5fb1a81f2858a07813c82',8,'Michael.Schumacher@formulaone.com','Michael','Schumacher','en_GB',NULL,1,'DEVKEY-Schumacher','e',NULL,'2022-05-01 10:36:33',NULL); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Dumping routines for database 'testlink' +-- +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; +/*!50003 DROP FUNCTION IF EXISTS `UDFStripHTMLTags` */; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = latin1 */ ; +/*!50003 SET character_set_results = latin1 */ ; +/*!50003 SET collation_connection = latin1_swedish_ci */ ; + +DELIMITER ;; +CREATE DEFINER=`testlink`@`%` FUNCTION `UDFStripHTMLTags`(Dirty TEXT) RETURNS text CHARSET utf8mb3 + DETERMINISTIC +BEGIN +DECLARE iStart, iEnd, iLength int; + WHILE Locate( '<', Dirty ) > 0 And Locate( '>', Dirty, Locate( '<', Dirty )) > 0 DO + BEGIN + SET iStart = Locate( '<', Dirty ), iEnd = Locate( '>', Dirty, Locate('<', Dirty )); + SET iLength = ( iEnd - iStart) + 1; + IF iLength > 0 THEN + BEGIN + SET Dirty = Insert( Dirty, iStart, iLength, ''); + END; + END IF; + END; + END WHILE; +RETURN Dirty; +END ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; + +-- +-- Final view structure for view `exec_by_date_time` +-- + +/*!50001 DROP VIEW IF EXISTS `exec_by_date_time`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `exec_by_date_time` AS select `NHTPL`.`name` AS `testplan_name`,date_format(`E`.`execution_ts`,'%Y-%m-%d') AS `yyyy_mm_dd`,date_format(`E`.`execution_ts`,'%Y-%m') AS `yyyy_mm`,date_format(`E`.`execution_ts`,'%H') AS `hh`,date_format(`E`.`execution_ts`,'%k') AS `hour`,`E`.`id` AS `id`,`E`.`build_id` AS `build_id`,`E`.`tester_id` AS `tester_id`,`E`.`execution_ts` AS `execution_ts`,`E`.`status` AS `status`,`E`.`testplan_id` AS `testplan_id`,`E`.`tcversion_id` AS `tcversion_id`,`E`.`tcversion_number` AS `tcversion_number`,`E`.`platform_id` AS `platform_id`,`E`.`execution_type` AS `execution_type`,`E`.`execution_duration` AS `execution_duration`,`E`.`notes` AS `notes` from ((`executions` `E` join `testplans` `TPL` on((`TPL`.`id` = `E`.`testplan_id`))) join `nodes_hierarchy` `NHTPL` on((`NHTPL`.`id` = `TPL`.`id`))) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_exec_by_context` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_exec_by_context`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_exec_by_context` AS select `executions`.`tcversion_id` AS `tcversion_id`,`executions`.`testplan_id` AS `testplan_id`,`executions`.`build_id` AS `build_id`,`executions`.`platform_id` AS `platform_id`,max(`executions`.`id`) AS `id` from `executions` group by `executions`.`tcversion_id`,`executions`.`testplan_id`,`executions`.`build_id`,`executions`.`platform_id` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_exec_by_testplan` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_exec_by_testplan`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_exec_by_testplan` AS select `executions`.`tcversion_id` AS `tcversion_id`,`executions`.`testplan_id` AS `testplan_id`,max(`executions`.`id`) AS `id` from `executions` group by `executions`.`tcversion_id`,`executions`.`testplan_id` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_exec_by_testplan_plat` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_exec_by_testplan_plat`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_exec_by_testplan_plat` AS select `executions`.`tcversion_id` AS `tcversion_id`,`executions`.`testplan_id` AS `testplan_id`,`executions`.`platform_id` AS `platform_id`,max(`executions`.`id`) AS `id` from `executions` group by `executions`.`tcversion_id`,`executions`.`testplan_id`,`executions`.`platform_id` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_req_version` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_req_version`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_req_version` AS select `RQ`.`id` AS `req_id`,max(`RQV`.`version`) AS `version` from ((`nodes_hierarchy` `NHRQV` join `requirements` `RQ` on((`RQ`.`id` = `NHRQV`.`parent_id`))) join `req_versions` `RQV` on((`RQV`.`id` = `NHRQV`.`id`))) group by `RQ`.`id` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_req_version_id` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_req_version_id`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_req_version_id` AS select `LRQVN`.`req_id` AS `req_id`,`LRQVN`.`version` AS `version`,`REQV`.`id` AS `req_version_id` from ((`latest_req_version` `LRQVN` join `nodes_hierarchy` `NHRQV` on((`NHRQV`.`parent_id` = `LRQVN`.`req_id`))) join `req_versions` `REQV` on(((`REQV`.`id` = `NHRQV`.`id`) and (`REQV`.`version` = `LRQVN`.`version`)))) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_rspec_revision` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_rspec_revision`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_rspec_revision` AS select `RSR`.`parent_id` AS `req_spec_id`,`RS`.`testproject_id` AS `testproject_id`,max(`RSR`.`revision`) AS `revision` from (`req_specs_revisions` `RSR` join `req_specs` `RS` on((`RS`.`id` = `RSR`.`parent_id`))) group by `RSR`.`parent_id`,`RS`.`testproject_id` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_tcase_version_id` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_tcase_version_id`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_tcase_version_id` AS select `LTCVN`.`testcase_id` AS `testcase_id`,`LTCVN`.`version` AS `version`,`TCV`.`id` AS `tcversion_id` from ((`latest_tcase_version_number` `LTCVN` join `nodes_hierarchy` `NHTCV` on((`NHTCV`.`parent_id` = `LTCVN`.`testcase_id`))) join `tcversions` `TCV` on(((`TCV`.`id` = `NHTCV`.`id`) and (`TCV`.`version` = `LTCVN`.`version`)))) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `latest_tcase_version_number` +-- + +/*!50001 DROP VIEW IF EXISTS `latest_tcase_version_number`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `latest_tcase_version_number` AS select `NH_TC`.`id` AS `testcase_id`,max(`TCV`.`version`) AS `version` from ((`nodes_hierarchy` `NH_TC` join `nodes_hierarchy` `NH_TCV` on((`NH_TCV`.`parent_id` = `NH_TC`.`id`))) join `tcversions` `TCV` on((`NH_TCV`.`id` = `TCV`.`id`))) group by `testcase_id` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `tcversions_without_keywords` +-- + +/*!50001 DROP VIEW IF EXISTS `tcversions_without_keywords`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `tcversions_without_keywords` AS select `NHTCV`.`parent_id` AS `testcase_id`,`NHTCV`.`id` AS `id` from `nodes_hierarchy` `NHTCV` where ((`NHTCV`.`node_type_id` = 4) and exists(select 1 from `testcase_keywords` `TCK` where (`TCK`.`tcversion_id` = `NHTCV`.`id`)) is false) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `tcversions_without_platforms` +-- + +/*!50001 DROP VIEW IF EXISTS `tcversions_without_platforms`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `tcversions_without_platforms` AS select `NHTCV`.`parent_id` AS `testcase_id`,`NHTCV`.`id` AS `id` from `nodes_hierarchy` `NHTCV` where ((`NHTCV`.`node_type_id` = 4) and exists(select 1 from `testcase_platforms` `TCPL` where (`TCPL`.`tcversion_id` = `NHTCV`.`id`)) is false) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `tsuites_tree_depth_2` +-- + +/*!50001 DROP VIEW IF EXISTS `tsuites_tree_depth_2`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 SET collation_connection = utf8mb4_0900_ai_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */ +/*!50001 VIEW `tsuites_tree_depth_2` AS select `TPRJ`.`prefix` AS `prefix`,`NHTPRJ`.`name` AS `testproject_name`,`NHTS_L1`.`name` AS `level1_name`,`NHTS_L2`.`name` AS `level2_name`,`NHTPRJ`.`id` AS `testproject_id`,`NHTS_L1`.`id` AS `level1_id`,`NHTS_L2`.`id` AS `level2_id` from (((`testprojects` `TPRJ` join `nodes_hierarchy` `NHTPRJ` on((`TPRJ`.`id` = `NHTPRJ`.`id`))) left join `nodes_hierarchy` `NHTS_L1` on((`NHTS_L1`.`parent_id` = `NHTPRJ`.`id`))) left join `nodes_hierarchy` `NHTS_L2` on((`NHTS_L2`.`parent_id` = `NHTS_L1`.`id`))) where ((`NHTPRJ`.`node_type_id` = 1) and (`NHTS_L1`.`node_type_id` = 2) and (`NHTS_L2`.`node_type_id` = 2)) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2024-02-04 15:57:11 diff --git a/docs/db_sample/tl193_tyres_example_20120501_05.sql b/docs/db_sample/tl193_tyres_example_20120501_05.sql deleted file mode 100644 index 585760a2a1..0000000000 --- a/docs/db_sample/tl193_tyres_example_20120501_05.sql +++ /dev/null @@ -1,1329 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.16, for Linux (i686) --- --- Host: localhost Database: tl193_sample01 --- ------------------------------------------------------ --- Server version 5.5.16 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Current Database: `tl193_sample01` --- - -CREATE DATABASE /*!32312 IF NOT EXISTS*/ `tl193_sample01` /*!40100 DEFAULT CHARACTER SET utf8 */; - -USE `tl193_sample01`; - --- --- Table structure for table `assignment_status` --- - -DROP TABLE IF EXISTS `assignment_status`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `assignment_status` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `description` varchar(100) NOT NULL DEFAULT 'unknown', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `assignment_status` --- - -LOCK TABLES `assignment_status` WRITE; -/*!40000 ALTER TABLE `assignment_status` DISABLE KEYS */; -INSERT INTO `assignment_status` VALUES (1,'open'),(2,'closed'),(3,'completed'),(4,'todo_urgent'),(5,'todo'); -/*!40000 ALTER TABLE `assignment_status` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `assignment_types` --- - -DROP TABLE IF EXISTS `assignment_types`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `assignment_types` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `fk_table` varchar(30) DEFAULT '', - `description` varchar(100) NOT NULL DEFAULT 'unknown', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `assignment_types` --- - -LOCK TABLES `assignment_types` WRITE; -/*!40000 ALTER TABLE `assignment_types` DISABLE KEYS */; -INSERT INTO `assignment_types` VALUES (1,'testplan_tcversions','testcase_execution'),(2,'tcversions','testcase_review'); -/*!40000 ALTER TABLE `assignment_types` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `attachments` --- - -DROP TABLE IF EXISTS `attachments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `attachments` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `fk_id` int(10) unsigned NOT NULL DEFAULT '0', - `fk_table` varchar(250) DEFAULT '', - `title` varchar(250) DEFAULT '', - `description` varchar(250) DEFAULT '', - `file_name` varchar(250) NOT NULL DEFAULT '', - `file_path` varchar(250) DEFAULT '', - `file_size` int(11) NOT NULL DEFAULT '0', - `file_type` varchar(250) NOT NULL DEFAULT '', - `date_added` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `content` longblob, - `compression_type` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `attachments` --- - -LOCK TABLES `attachments` WRITE; -/*!40000 ALTER TABLE `attachments` DISABLE KEYS */; -/*!40000 ALTER TABLE `attachments` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `builds` --- - -DROP TABLE IF EXISTS `builds`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `builds` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `testplan_id` int(10) unsigned NOT NULL DEFAULT '0', - `name` varchar(100) NOT NULL DEFAULT 'undefined', - `notes` text, - `active` tinyint(1) NOT NULL DEFAULT '1', - `is_open` tinyint(1) NOT NULL DEFAULT '1', - `author_id` int(10) unsigned DEFAULT NULL, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `release_date` date DEFAULT NULL, - `closed_on_date` date DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`testplan_id`,`name`), - KEY `testplan_id` (`testplan_id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='Available builds'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `builds` --- - -LOCK TABLES `builds` WRITE; -/*!40000 ALTER TABLE `builds` DISABLE KEYS */; -INSERT INTO `builds` VALUES (1,4,'PZ X.1','',1,1,NULL,'2012-05-01 09:26:40',NULL,NULL),(2,4,'PZ X.2','',1,1,NULL,'2012-05-01 09:26:54',NULL,NULL),(3,4,'PZ X.3','',1,1,NULL,'2012-05-01 09:27:08',NULL,NULL); -/*!40000 ALTER TABLE `builds` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `cfield_design_values` --- - -DROP TABLE IF EXISTS `cfield_design_values`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cfield_design_values` ( - `field_id` int(10) NOT NULL DEFAULT '0', - `node_id` int(10) NOT NULL DEFAULT '0', - `value` varchar(4000) NOT NULL DEFAULT '', - PRIMARY KEY (`field_id`,`node_id`), - KEY `idx_cfield_design_values` (`node_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `cfield_design_values` --- - -LOCK TABLES `cfield_design_values` WRITE; -/*!40000 ALTER TABLE `cfield_design_values` DISABLE KEYS */; -/*!40000 ALTER TABLE `cfield_design_values` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `cfield_execution_values` --- - -DROP TABLE IF EXISTS `cfield_execution_values`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cfield_execution_values` ( - `field_id` int(10) NOT NULL DEFAULT '0', - `execution_id` int(10) NOT NULL DEFAULT '0', - `testplan_id` int(10) NOT NULL DEFAULT '0', - `tcversion_id` int(10) NOT NULL DEFAULT '0', - `value` varchar(4000) NOT NULL DEFAULT '', - PRIMARY KEY (`field_id`,`execution_id`,`testplan_id`,`tcversion_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `cfield_execution_values` --- - -LOCK TABLES `cfield_execution_values` WRITE; -/*!40000 ALTER TABLE `cfield_execution_values` DISABLE KEYS */; -/*!40000 ALTER TABLE `cfield_execution_values` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `cfield_node_types` --- - -DROP TABLE IF EXISTS `cfield_node_types`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cfield_node_types` ( - `field_id` int(10) NOT NULL DEFAULT '0', - `node_type_id` int(10) NOT NULL DEFAULT '0', - PRIMARY KEY (`field_id`,`node_type_id`), - KEY `idx_custom_fields_assign` (`node_type_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `cfield_node_types` --- - -LOCK TABLES `cfield_node_types` WRITE; -/*!40000 ALTER TABLE `cfield_node_types` DISABLE KEYS */; -/*!40000 ALTER TABLE `cfield_node_types` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `cfield_testplan_design_values` --- - -DROP TABLE IF EXISTS `cfield_testplan_design_values`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cfield_testplan_design_values` ( - `field_id` int(10) NOT NULL DEFAULT '0', - `link_id` int(10) NOT NULL DEFAULT '0' COMMENT 'point to testplan_tcversion id', - `value` varchar(4000) NOT NULL DEFAULT '', - PRIMARY KEY (`field_id`,`link_id`), - KEY `idx_cfield_tplan_design_val` (`link_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `cfield_testplan_design_values` --- - -LOCK TABLES `cfield_testplan_design_values` WRITE; -/*!40000 ALTER TABLE `cfield_testplan_design_values` DISABLE KEYS */; -/*!40000 ALTER TABLE `cfield_testplan_design_values` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `cfield_testprojects` --- - -DROP TABLE IF EXISTS `cfield_testprojects`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cfield_testprojects` ( - `field_id` int(10) unsigned NOT NULL DEFAULT '0', - `testproject_id` int(10) unsigned NOT NULL DEFAULT '0', - `display_order` smallint(5) unsigned NOT NULL DEFAULT '1', - `location` smallint(5) unsigned NOT NULL DEFAULT '1', - `active` tinyint(1) NOT NULL DEFAULT '1', - `required_on_design` tinyint(1) NOT NULL DEFAULT '0', - `required_on_execution` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`field_id`,`testproject_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `cfield_testprojects` --- - -LOCK TABLES `cfield_testprojects` WRITE; -/*!40000 ALTER TABLE `cfield_testprojects` DISABLE KEYS */; -/*!40000 ALTER TABLE `cfield_testprojects` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `custom_fields` --- - -DROP TABLE IF EXISTS `custom_fields`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `custom_fields` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `name` varchar(64) NOT NULL DEFAULT '', - `label` varchar(64) NOT NULL DEFAULT '' COMMENT 'label to display on user interface', - `type` smallint(6) NOT NULL DEFAULT '0', - `possible_values` varchar(4000) NOT NULL DEFAULT '', - `default_value` varchar(4000) NOT NULL DEFAULT '', - `valid_regexp` varchar(255) NOT NULL DEFAULT '', - `length_min` int(10) NOT NULL DEFAULT '0', - `length_max` int(10) NOT NULL DEFAULT '0', - `show_on_design` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '1=> show it during specification design', - `enable_on_design` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '1=> user can write/manage it during specification design', - `show_on_execution` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '1=> show it during test case execution', - `enable_on_execution` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '1=> user can write/manage it during test case execution', - `show_on_testplan_design` tinyint(3) unsigned NOT NULL DEFAULT '0', - `enable_on_testplan_design` tinyint(3) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `idx_custom_fields_name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `custom_fields` --- - -LOCK TABLES `custom_fields` WRITE; -/*!40000 ALTER TABLE `custom_fields` DISABLE KEYS */; -/*!40000 ALTER TABLE `custom_fields` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `db_version` --- - -DROP TABLE IF EXISTS `db_version`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `db_version` ( - `version` varchar(50) NOT NULL DEFAULT 'unknown', - `upgrade_ts` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `notes` text -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `db_version` --- - -LOCK TABLES `db_version` WRITE; -/*!40000 ALTER TABLE `db_version` DISABLE KEYS */; -INSERT INTO `db_version` VALUES ('DB 1.4','2012-05-01 10:56:28','TestLink 1.9.1'); -/*!40000 ALTER TABLE `db_version` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `events` --- - -DROP TABLE IF EXISTS `events`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `events` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `transaction_id` int(10) unsigned NOT NULL DEFAULT '0', - `log_level` smallint(5) unsigned NOT NULL DEFAULT '0', - `source` varchar(45) DEFAULT NULL, - `description` text NOT NULL, - `fired_at` int(10) unsigned NOT NULL DEFAULT '0', - `activity` varchar(45) DEFAULT NULL, - `object_id` int(10) unsigned DEFAULT NULL, - `object_type` varchar(45) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `transaction_id` (`transaction_id`), - KEY `fired_at` (`fired_at`) -) ENGINE=InnoDB AUTO_INCREMENT=265 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `events` --- - -LOCK TABLES `events` WRITE; -/*!40000 ALTER TABLE `events` DISABLE KEYS */; -INSERT INTO `events` VALUES (1,1,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(2,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(3,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(4,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(5,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(6,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(7,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(8,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(9,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(10,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(11,2,2,'GUI','E_NOTICE\nUndefined index: testprojectOptions - in /hdextra/development/tl-old/tl193-untouched/lib/general/mainPage.php - Line 63',1335862631,'PHP',NULL,NULL),(12,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/general/mainPage.php - Line 63',1335862631,'PHP',NULL,NULL),(13,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(14,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(15,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(16,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(17,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(18,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(19,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(20,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(21,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(22,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(23,2,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862631,'PHP',NULL,NULL),(24,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(25,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(26,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(27,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(28,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(29,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(30,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(31,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(32,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(33,3,2,'GUI','E_NOTICE\nUndefined index: testprojectOptions - in /hdextra/development/tl-old/tl193-untouched/lib/general/mainPage.php - Line 63',1335862696,'PHP',NULL,NULL),(34,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/general/mainPage.php - Line 63',1335862696,'PHP',NULL,NULL),(35,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(36,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(37,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(38,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(39,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(40,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(41,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(42,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(43,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(44,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(45,3,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(46,4,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlUser.class.php - Line 726',1335862696,'PHP',NULL,NULL),(47,5,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_login_succeeded\";s:6:\"params\";a:2:{i:0;s:5:\"admin\";i:1;s:9:\"127.0.0.1\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335862713,'LOGIN',1,'users'),(48,6,2,'GUI','No project found: Assume a new installation and redirect to create it',1335862714,NULL,NULL,NULL),(49,7,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_testproject_created\";s:6:\"params\";a:1:{i:0;s:22:\"TestLink 193 - Reports\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335862746,'CREATE',1,'testprojects'),(50,8,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_testproject_created\";s:6:\"params\";a:1:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864014,'CREATE',2,'testprojects'),(51,9,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_testproject_created\";s:6:\"params\";a:1:{i:0;s:29:\"Formula One Pirelli Wet Tyres\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864053,'CREATE',3,'testprojects'),(52,10,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:22:\"audit_testplan_created\";s:6:\"params\";a:2:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:22:\"P Zero Red (Supersoft)\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864120,'CREATED',4,'testplans'),(53,11,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:22:\"audit_testplan_created\";s:6:\"params\";a:2:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:20:\"P Zero Yellow (Soft)\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864160,'CREATED',5,'testplans'),(54,12,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:22:\"audit_testplan_created\";s:6:\"params\";a:2:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:21:\"P Zero White (Medium)\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864193,'CREATED',6,'testplans'),(55,13,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:22:\"audit_testplan_created\";s:6:\"params\";a:2:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:20:\"P Zero Silver (Hard)\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864246,'CREATED',7,'testplans'),(56,14,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:22:\"audit_testplan_created\";s:6:\"params\";a:2:{i:0;s:29:\"Formula One Pirelli Wet Tyres\";i:1;s:30:\"Cinturato Green (Intermediate)\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864293,'CREATED',8,'testplans'),(57,15,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:22:\"audit_testplan_created\";s:6:\"params\";a:2:{i:0;s:29:\"Formula One Pirelli Wet Tyres\";i:1;s:20:\"Cinturato Blue (Wet)\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864326,'CREATED',9,'testplans'),(58,16,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:40:\"audit_all_user_roles_removed_testproject\";s:6:\"params\";a:1:{i:0;s:22:\"TestLink 193 - Reports\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864347,'ASSIGN',1,'testprojects'),(59,16,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_testproject_deleted\";s:6:\"params\";a:1:{i:0;s:22:\"TestLink 193 - Reports\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864347,'DELETE',1,'testprojects'),(60,17,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:19:\"audit_build_created\";s:6:\"params\";a:3:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:22:\"P Zero Red (Supersoft)\";i:2;s:11:\"ZeroRed X.1\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864400,'CREATE',1,'builds'),(61,18,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:19:\"audit_build_created\";s:6:\"params\";a:3:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:22:\"P Zero Red (Supersoft)\";i:2;s:11:\"ZeroRed X.2\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864414,'CREATE',2,'builds'),(62,19,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:19:\"audit_build_created\";s:6:\"params\";a:3:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:22:\"P Zero Red (Supersoft)\";i:2;s:11:\"ZeroRed X.3\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864428,'CREATE',3,'builds'),(63,20,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:17:\"audit_build_saved\";s:6:\"params\";a:3:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:22:\"P Zero Red (Supersoft)\";i:2;s:6:\"PZ X.1\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864459,'SAVE',1,'builds'),(64,21,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:17:\"audit_build_saved\";s:6:\"params\";a:3:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:22:\"P Zero Red (Supersoft)\";i:2;s:6:\"PZ X.2\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864466,'SAVE',2,'builds'),(65,22,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:17:\"audit_build_saved\";s:6:\"params\";a:3:{i:0;s:29:\"Formula One Pirelli Dry Tyres\";i:1;s:22:\"P Zero Red (Supersoft)\";i:2;s:6:\"PZ X.3\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335864473,'SAVE',3,'builds'),(66,23,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_keyword_created\";s:6:\"params\";a:1:{i:0;s:10:\"Mechanical\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335866991,'CREATE',1,'keywords'),(67,24,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_keyword_created\";s:6:\"params\";a:1:{i:0;s:10:\"Resistance\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867005,'CREATE',2,'keywords'),(68,25,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_keyword_created\";s:6:\"params\";a:1:{i:0;s:15:\"Tear Resistance\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867022,'CREATE',3,'keywords'),(69,26,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_keyword_created\";s:6:\"params\";a:1:{i:0;s:12:\"Mountability\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867034,'CREATE',4,'keywords'),(70,27,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_keyword_created\";s:6:\"params\";a:1:{i:0;s:10:\"Subjective\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867048,'CREATE',5,'keywords'),(71,28,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:21:\"audit_keyword_created\";s:6:\"params\";a:1:{i:0;s:9:\"Objective\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867057,'CREATE',6,'keywords'),(72,29,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:11:\"GRIP-SL-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867108,'ASSIGN',31,'nodes_hierarchy'),(73,29,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"GRIP-SL-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867108,'ASSIGN',31,'nodes_hierarchy'),(74,30,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"GRIP-SL-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867121,'ASSIGN',33,'nodes_hierarchy'),(75,30,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"GRIP-SL-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867121,'ASSIGN',33,'nodes_hierarchy'),(76,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:11:\"GRIP-LC-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',35,'nodes_hierarchy'),(77,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"GRIP-LC-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',35,'nodes_hierarchy'),(78,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"GRIP-LC-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',37,'nodes_hierarchy'),(79,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"GRIP-LC-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',37,'nodes_hierarchy'),(80,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:11:\"GRIP-LC-003\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',39,'nodes_hierarchy'),(81,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"GRIP-LC-003\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',39,'nodes_hierarchy'),(82,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"GRIP-LC-004\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',41,'nodes_hierarchy'),(83,31,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"GRIP-LC-004\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867626,'ASSIGN',41,'nodes_hierarchy'),(84,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:12:\"GRIP-HYD-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',43,'nodes_hierarchy'),(85,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:12:\"GRIP-HYD-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',43,'nodes_hierarchy'),(86,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:12:\"GRIP-HYD-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',45,'nodes_hierarchy'),(87,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:12:\"GRIP-HYD-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',45,'nodes_hierarchy'),(88,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:12:\"GRIP-HYD-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',47,'nodes_hierarchy'),(89,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:12:\"GRIP-HYD-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',47,'nodes_hierarchy'),(90,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:12:\"GRIP-HYD-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',49,'nodes_hierarchy'),(91,32,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:12:\"GRIP-HYD-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867682,'ASSIGN',49,'nodes_hierarchy'),(92,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"BRK-AA-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',51,'nodes_hierarchy'),(93,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-AA-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',51,'nodes_hierarchy'),(94,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"BRK-AA-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',53,'nodes_hierarchy'),(95,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-AA-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',53,'nodes_hierarchy'),(96,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"BRK-AA-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',55,'nodes_hierarchy'),(97,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-AA-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',55,'nodes_hierarchy'),(98,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"BRK-AA-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',57,'nodes_hierarchy'),(99,33,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-AA-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867747,'ASSIGN',57,'nodes_hierarchy'),(100,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"BRK-BB-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',59,'nodes_hierarchy'),(101,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-BB-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',59,'nodes_hierarchy'),(102,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"BRK-BB-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',61,'nodes_hierarchy'),(103,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-BB-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',61,'nodes_hierarchy'),(104,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"BRK-BB-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',63,'nodes_hierarchy'),(105,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-BB-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',63,'nodes_hierarchy'),(106,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"BRK-BB-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',65,'nodes_hierarchy'),(107,34,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"BRK-BB-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867792,'ASSIGN',65,'nodes_hierarchy'),(108,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"DRV-AX-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',67,'nodes_hierarchy'),(109,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-AX-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',67,'nodes_hierarchy'),(110,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"DRV-AX-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',69,'nodes_hierarchy'),(111,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-AX-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',69,'nodes_hierarchy'),(112,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"DRV-AX-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',71,'nodes_hierarchy'),(113,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-AX-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',71,'nodes_hierarchy'),(114,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"DRV-AX-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',73,'nodes_hierarchy'),(115,35,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-AX-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867847,'ASSIGN',73,'nodes_hierarchy'),(116,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"DRV-JT-801\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867890,'ASSIGN',75,'nodes_hierarchy'),(117,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-JT-801\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867890,'ASSIGN',75,'nodes_hierarchy'),(118,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"DRV-JT-802\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867891,'ASSIGN',77,'nodes_hierarchy'),(119,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-JT-802\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867891,'ASSIGN',77,'nodes_hierarchy'),(120,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"DRV-JT-803\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867891,'ASSIGN',79,'nodes_hierarchy'),(121,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-JT-803\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867891,'ASSIGN',79,'nodes_hierarchy'),(122,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"DRV-JT-804\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867891,'ASSIGN',81,'nodes_hierarchy'),(123,36,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-JT-804\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867891,'ASSIGN',81,'nodes_hierarchy'),(124,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"DRV-TC-701\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',83,'nodes_hierarchy'),(125,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-TC-701\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',83,'nodes_hierarchy'),(126,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"DRV-TC-702\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',85,'nodes_hierarchy'),(127,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-TC-702\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',85,'nodes_hierarchy'),(128,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"DRV-TC-703\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',87,'nodes_hierarchy'),(129,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-TC-703\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',87,'nodes_hierarchy'),(130,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"DRV-TC-704\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',89,'nodes_hierarchy'),(131,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-TC-704\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',89,'nodes_hierarchy'),(132,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"DRV-TC-705\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',91,'nodes_hierarchy'),(133,37,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"DRV-TC-705\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335867956,'ASSIGN',91,'nodes_hierarchy'),(134,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"INO-ZX-701\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868010,'ASSIGN',93,'nodes_hierarchy'),(135,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZX-701\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868010,'ASSIGN',93,'nodes_hierarchy'),(136,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"INO-ZX-702\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868010,'ASSIGN',95,'nodes_hierarchy'),(137,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZX-702\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868010,'ASSIGN',95,'nodes_hierarchy'),(138,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"INO-ZX-703\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868011,'ASSIGN',97,'nodes_hierarchy'),(139,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZX-703\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868011,'ASSIGN',97,'nodes_hierarchy'),(140,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"INO-ZX-704\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868011,'ASSIGN',99,'nodes_hierarchy'),(141,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZX-704\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868011,'ASSIGN',99,'nodes_hierarchy'),(142,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"INO-ZX-705\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868011,'ASSIGN',101,'nodes_hierarchy'),(143,38,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZX-705\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868011,'ASSIGN',101,'nodes_hierarchy'),(144,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"INO-ZW-601\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',103,'nodes_hierarchy'),(145,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZW-601\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',103,'nodes_hierarchy'),(146,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"INO-ZW-602\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',105,'nodes_hierarchy'),(147,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZW-602\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',105,'nodes_hierarchy'),(148,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"INO-ZW-603\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',107,'nodes_hierarchy'),(149,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZW-603\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',107,'nodes_hierarchy'),(150,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"INO-ZW-604\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',109,'nodes_hierarchy'),(151,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZW-604\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',109,'nodes_hierarchy'),(152,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"INO-ZW-605\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',111,'nodes_hierarchy'),(153,39,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"INO-ZW-605\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868064,'ASSIGN',111,'nodes_hierarchy'),(154,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:11:\"EXNO-ZW-601\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868106,'ASSIGN',113,'nodes_hierarchy'),(155,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-ZW-601\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868106,'ASSIGN',113,'nodes_hierarchy'),(156,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"EXNO-ZW-602\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868106,'ASSIGN',115,'nodes_hierarchy'),(157,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-ZW-602\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868106,'ASSIGN',115,'nodes_hierarchy'),(158,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:11:\"EXNO-ZW-603\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868107,'ASSIGN',117,'nodes_hierarchy'),(159,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-ZW-603\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868107,'ASSIGN',117,'nodes_hierarchy'),(160,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"EXNO-ZW-604\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868107,'ASSIGN',119,'nodes_hierarchy'),(161,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-ZW-604\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868107,'ASSIGN',119,'nodes_hierarchy'),(162,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"EXNO-ZW-605\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868107,'ASSIGN',121,'nodes_hierarchy'),(163,40,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-ZW-605\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868107,'ASSIGN',121,'nodes_hierarchy'),(164,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:11:\"EXNO-WW-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',123,'nodes_hierarchy'),(165,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-WW-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',123,'nodes_hierarchy'),(166,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"EXNO-WW-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',125,'nodes_hierarchy'),(167,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-WW-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',125,'nodes_hierarchy'),(168,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:11:\"EXNO-WW-003\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',127,'nodes_hierarchy'),(169,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-WW-003\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',127,'nodes_hierarchy'),(170,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"EXNO-WW-004\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',129,'nodes_hierarchy'),(171,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-WW-004\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',129,'nodes_hierarchy'),(172,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:11:\"EXNO-WW-005\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',131,'nodes_hierarchy'),(173,41,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:11:\"EXNO-WW-005\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868144,'ASSIGN',131,'nodes_hierarchy'),(174,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"TYW-77-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',133,'nodes_hierarchy'),(175,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-001\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',133,'nodes_hierarchy'),(176,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',135,'nodes_hierarchy'),(177,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-002\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',135,'nodes_hierarchy'),(178,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"TYW-77-003\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',137,'nodes_hierarchy'),(179,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-003\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',137,'nodes_hierarchy'),(180,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-004\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',139,'nodes_hierarchy'),(181,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-004\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',139,'nodes_hierarchy'),(182,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-005\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',141,'nodes_hierarchy'),(183,42,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-005\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868185,'ASSIGN',141,'nodes_hierarchy'),(184,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"TYW-77-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',143,'nodes_hierarchy'),(185,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-101\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',143,'nodes_hierarchy'),(186,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',145,'nodes_hierarchy'),(187,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-102\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',145,'nodes_hierarchy'),(188,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"TYW-77-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',147,'nodes_hierarchy'),(189,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-103\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',147,'nodes_hierarchy'),(190,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',149,'nodes_hierarchy'),(191,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-104\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',149,'nodes_hierarchy'),(192,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-105\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',151,'nodes_hierarchy'),(193,43,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-105\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868220,'ASSIGN',151,'nodes_hierarchy'),(194,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"TYW-77-201\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',153,'nodes_hierarchy'),(195,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-201\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',153,'nodes_hierarchy'),(196,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-202\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',155,'nodes_hierarchy'),(197,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-202\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',155,'nodes_hierarchy'),(198,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Mechanical\";i:1;s:10:\"TYW-77-203\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',157,'nodes_hierarchy'),(199,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-203\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',157,'nodes_hierarchy'),(200,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-204\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',159,'nodes_hierarchy'),(201,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-204\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',159,'nodes_hierarchy'),(202,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:10:\"Resistance\";i:1;s:10:\"TYW-77-205\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868246,'ASSIGN',161,'nodes_hierarchy'),(203,44,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:25:\"audit_keyword_assigned_tc\";s:6:\"params\";a:2:{i:0;s:9:\"Objective\";i:1;s:10:\"TYW-77-205\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868247,'ASSIGN',161,'nodes_hierarchy'),(204,45,2,'GUI','E_NOTICE\nTrying to get property of non-object - in /hdextra/development/tl-old/tl193-untouched/lib/functions/tlTestCaseFilterControl.class.php - Line 880',1335868398,'PHP',NULL,NULL),(205,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-1 : GRIP-SL-001\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(206,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-1 : GRIP-SL-001\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(207,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-1 : GRIP-SL-001\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(208,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-1 : GRIP-SL-001\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(209,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-2 : GRIP-SL-002\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(210,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-2 : GRIP-SL-002\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(211,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-2 : GRIP-SL-002\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(212,46,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-2 : GRIP-SL-002\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868429,'ASSIGN',4,'testplans'),(213,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-7 : GRIP-HYD-101\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(214,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-7 : GRIP-HYD-101\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(215,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-7 : GRIP-HYD-101\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(216,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-9 : GRIP-HYD-103\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(217,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-9 : GRIP-HYD-103\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(218,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-9 : GRIP-HYD-103\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(219,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-8 : GRIP-HYD-102\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(220,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-8 : GRIP-HYD-102\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(221,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:20:\"PDT-8 : GRIP-HYD-102\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(222,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:21:\"PDT-10 : GRIP-HYD-104\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(223,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:21:\"PDT-10 : GRIP-HYD-104\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(224,47,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:21:\"PDT-10 : GRIP-HYD-104\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868446,'ASSIGN',4,'testplans'),(225,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-32 : INO-ZX-701\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(226,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-32 : INO-ZX-701\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(227,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-34 : INO-ZX-703\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(228,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-34 : INO-ZX-703\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(229,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-33 : INO-ZX-702\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(230,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-33 : INO-ZX-702\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(231,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-35 : INO-ZX-704\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(232,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-35 : INO-ZX-704\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(233,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-36 : INO-ZX-705\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(234,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-36 : INO-ZX-705\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(235,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-37 : INO-ZW-601\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(236,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-37 : INO-ZW-601\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(237,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-39 : INO-ZW-603\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(238,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-39 : INO-ZW-603\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(239,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-38 : INO-ZW-602\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(240,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-38 : INO-ZW-602\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(241,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-40 : INO-ZW-604\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(242,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-40 : INO-ZW-604\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(243,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-41 : INO-ZW-605\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(244,48,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-41 : INO-ZW-605\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868481,'ASSIGN',4,'testplans'),(245,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-57 : TYW-77-101\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(246,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-57 : TYW-77-101\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(247,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-57 : TYW-77-101\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(248,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-57 : TYW-77-101\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(249,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-59 : TYW-77-103\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(250,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-59 : TYW-77-103\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(251,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-59 : TYW-77-103\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(252,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-59 : TYW-77-103\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(253,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-58 : TYW-77-102\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(254,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-58 : TYW-77-102\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(255,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-58 : TYW-77-102\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(256,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-58 : TYW-77-102\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(257,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-60 : TYW-77-104\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(258,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-60 : TYW-77-104\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(259,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-60 : TYW-77-104\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(260,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-60 : TYW-77-104\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(261,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-61 : TYW-77-105\";i:1;s:1:\"1\";i:2;s:41:\"P Zero Red (Supersoft) - Platform:Ferrari\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(262,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-61 : TYW-77-105\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mc Laren\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(263,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-61 : TYW-77-105\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Mercedes\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'),(264,49,16,'GUI','O:18:\"tlMetaStringHelper\":4:{s:5:\"label\";s:26:\"audit_tc_added_to_testplan\";s:6:\"params\";a:3:{i:0;s:19:\"PDT-61 : TYW-77-105\";i:1;s:1:\"1\";i:2;s:42:\"P Zero Red (Supersoft) - Platform:Red Bull\";}s:13:\"bDontLocalize\";b:0;s:14:\"bDontFireEvent\";b:0;}',1335868496,'ASSIGN',4,'testplans'); -/*!40000 ALTER TABLE `events` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `execution_bugs` --- - -DROP TABLE IF EXISTS `execution_bugs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `execution_bugs` ( - `execution_id` int(10) unsigned NOT NULL DEFAULT '0', - `bug_id` varchar(16) NOT NULL DEFAULT '0', - PRIMARY KEY (`execution_id`,`bug_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `execution_bugs` --- - -LOCK TABLES `execution_bugs` WRITE; -/*!40000 ALTER TABLE `execution_bugs` DISABLE KEYS */; -/*!40000 ALTER TABLE `execution_bugs` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `executions` --- - -DROP TABLE IF EXISTS `executions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `executions` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `build_id` int(10) NOT NULL DEFAULT '0', - `tester_id` int(10) unsigned DEFAULT NULL, - `execution_ts` datetime DEFAULT NULL, - `status` char(1) DEFAULT NULL, - `testplan_id` int(10) unsigned NOT NULL DEFAULT '0', - `tcversion_id` int(10) unsigned NOT NULL DEFAULT '0', - `tcversion_number` smallint(5) unsigned NOT NULL DEFAULT '1', - `platform_id` int(10) unsigned NOT NULL DEFAULT '0', - `execution_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 -> manual, 2 -> automated', - `notes` text, - PRIMARY KEY (`id`), - KEY `executions_idx1` (`testplan_id`,`tcversion_id`,`platform_id`,`build_id`), - KEY `executions_idx2` (`execution_type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `executions` --- - -LOCK TABLES `executions` WRITE; -/*!40000 ALTER TABLE `executions` DISABLE KEYS */; -/*!40000 ALTER TABLE `executions` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `inventory` --- - -DROP TABLE IF EXISTS `inventory`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `inventory` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `testproject_id` int(10) unsigned NOT NULL, - `owner_id` int(10) unsigned NOT NULL, - `name` varchar(255) NOT NULL, - `ipaddress` varchar(255) NOT NULL, - `content` text, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modification_ts` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', - PRIMARY KEY (`id`), - KEY `inventory_idx1` (`testproject_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `inventory` --- - -LOCK TABLES `inventory` WRITE; -/*!40000 ALTER TABLE `inventory` DISABLE KEYS */; -/*!40000 ALTER TABLE `inventory` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `keywords` --- - -DROP TABLE IF EXISTS `keywords`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `keywords` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `keyword` varchar(100) NOT NULL DEFAULT '', - `testproject_id` int(10) unsigned NOT NULL DEFAULT '0', - `notes` text, - PRIMARY KEY (`id`), - KEY `testproject_id` (`testproject_id`), - KEY `keyword` (`keyword`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `keywords` --- - -LOCK TABLES `keywords` WRITE; -/*!40000 ALTER TABLE `keywords` DISABLE KEYS */; -INSERT INTO `keywords` VALUES (1,'Mechanical',2,''),(2,'Resistance',2,''),(3,'Tear Resistance',2,''),(4,'Mountability',2,''),(5,'Subjective',2,''),(6,'Objective',2,''); -/*!40000 ALTER TABLE `keywords` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `milestones` --- - -DROP TABLE IF EXISTS `milestones`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `milestones` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `testplan_id` int(10) unsigned NOT NULL DEFAULT '0', - `target_date` date DEFAULT NULL, - `start_date` date NOT NULL DEFAULT '0000-00-00', - `a` tinyint(3) unsigned NOT NULL DEFAULT '0', - `b` tinyint(3) unsigned NOT NULL DEFAULT '0', - `c` tinyint(3) unsigned NOT NULL DEFAULT '0', - `name` varchar(100) NOT NULL DEFAULT 'undefined', - PRIMARY KEY (`id`), - UNIQUE KEY `name_testplan_id` (`name`,`testplan_id`), - KEY `testplan_id` (`testplan_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `milestones` --- - -LOCK TABLES `milestones` WRITE; -/*!40000 ALTER TABLE `milestones` DISABLE KEYS */; -/*!40000 ALTER TABLE `milestones` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `node_types` --- - -DROP TABLE IF EXISTS `node_types`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `node_types` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `description` varchar(100) NOT NULL DEFAULT 'testproject', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `node_types` --- - -LOCK TABLES `node_types` WRITE; -/*!40000 ALTER TABLE `node_types` DISABLE KEYS */; -INSERT INTO `node_types` VALUES (1,'testproject'),(2,'testsuite'),(3,'testcase'),(4,'testcase_version'),(5,'testplan'),(6,'requirement_spec'),(7,'requirement'),(8,'requirement_version'),(9,'testcase_step'),(10,'requirement_revision'); -/*!40000 ALTER TABLE `node_types` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `nodes_hierarchy` --- - -DROP TABLE IF EXISTS `nodes_hierarchy`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `nodes_hierarchy` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(100) DEFAULT NULL, - `parent_id` int(10) unsigned DEFAULT NULL, - `node_type_id` int(10) unsigned NOT NULL DEFAULT '1', - `node_order` int(10) unsigned DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `pid_m_nodeorder` (`parent_id`,`node_order`) -) ENGINE=InnoDB AUTO_INCREMENT=163 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `nodes_hierarchy` --- - -LOCK TABLES `nodes_hierarchy` WRITE; -/*!40000 ALTER TABLE `nodes_hierarchy` DISABLE KEYS */; -INSERT INTO `nodes_hierarchy` VALUES (2,'Formula One Pirelli Dry Tyres',NULL,1,1),(3,'Formula One Pirelli Wet Tyres',NULL,1,1),(4,'P Zero Red (Supersoft)',2,5,0),(5,'P Zero Yellow (Soft)',2,5,0),(6,'P Zero White (Medium)',2,5,0),(7,'P Zero Silver (Hard)',2,5,0),(8,'Cinturato Green (Intermediate)',3,5,0),(9,'Cinturato Blue (Wet)',3,5,0),(10,'Grip',2,2,1),(11,'Braking',2,2,2),(12,'Driving comfort',2,2,3),(13,'Internal noise level',2,2,4),(14,'Exterior noise levels',2,2,5),(15,'Tyre wear',2,2,6),(16,'BRK - Single-Wheel Braking, Driving, and Lateral Traction',11,2,1),(17,'BRK - Stopping Distance',11,2,2),(18,'GRIP - Slalom',10,2,1),(19,'GRIP - Lane Change',10,2,2),(20,'GRIP - Hydroplaning',10,2,3),(21,'DRVC - Cornering Response',12,2,1),(22,'DRVC - J-Turn',12,2,2),(23,'DRVC - Tethered Circle',12,2,3),(24,'TYW - Stresses & motions in the tyre footprint',15,2,1),(25,'TYW - Tyre forces & moments',15,2,2),(26,'INO - Lateral, longitudinal & vertical spring rates',13,2,1),(27,'EXNO - Lateral, longitudinal & vertical spring rates',14,2,1),(28,'TYW - Thermal Profile/Reliability',15,2,3),(29,'INO - High Speed Operation',13,2,2),(30,'EXNO - High Speed Operation',14,2,2),(31,'GRIP-SL-001',18,3,100),(32,'',31,4,0),(33,'GRIP-SL-002',18,3,101),(34,'',33,4,0),(35,'GRIP-LC-001',19,3,100),(36,'',35,4,0),(37,'GRIP-LC-002',19,3,101),(38,'',37,4,0),(39,'GRIP-LC-003',19,3,100),(40,'',39,4,0),(41,'GRIP-LC-004',19,3,101),(42,'',41,4,0),(43,'GRIP-HYD-101',20,3,100),(44,'',43,4,0),(45,'GRIP-HYD-102',20,3,101),(46,'',45,4,0),(47,'GRIP-HYD-103',20,3,100),(48,'',47,4,0),(49,'GRIP-HYD-104',20,3,101),(50,'',49,4,0),(51,'BRK-AA-101',16,3,100),(52,'',51,4,0),(53,'BRK-AA-102',16,3,101),(54,'',53,4,0),(55,'BRK-AA-103',16,3,100),(56,'',55,4,0),(57,'BRK-AA-104',16,3,101),(58,'',57,4,0),(59,'BRK-BB-101',17,3,100),(60,'',59,4,0),(61,'BRK-BB-102',17,3,101),(62,'',61,4,0),(63,'BRK-BB-103',17,3,100),(64,'',63,4,0),(65,'BRK-BB-104',17,3,101),(66,'',65,4,0),(67,'DRV-AX-101',21,3,100),(68,'',67,4,0),(69,'DRV-AX-102',21,3,101),(70,'',69,4,0),(71,'DRV-AX-103',21,3,100),(72,'',71,4,0),(73,'DRV-AX-104',21,3,101),(74,'',73,4,0),(75,'DRV-JT-801',22,3,100),(76,'',75,4,0),(77,'DRV-JT-802',22,3,101),(78,'',77,4,0),(79,'DRV-JT-803',22,3,100),(80,'',79,4,0),(81,'DRV-JT-804',22,3,101),(82,'',81,4,0),(83,'DRV-TC-701',23,3,100),(84,'',83,4,0),(85,'DRV-TC-702',23,3,101),(86,'',85,4,0),(87,'DRV-TC-703',23,3,100),(88,'',87,4,0),(89,'DRV-TC-704',23,3,101),(90,'',89,4,0),(91,'DRV-TC-705',23,3,101),(92,'',91,4,0),(93,'INO-ZX-701',26,3,100),(94,'',93,4,0),(95,'INO-ZX-702',26,3,101),(96,'',95,4,0),(97,'INO-ZX-703',26,3,100),(98,'',97,4,0),(99,'INO-ZX-704',26,3,101),(100,'',99,4,0),(101,'INO-ZX-705',26,3,101),(102,'',101,4,0),(103,'INO-ZW-601',29,3,100),(104,'',103,4,0),(105,'INO-ZW-602',29,3,101),(106,'',105,4,0),(107,'INO-ZW-603',29,3,100),(108,'',107,4,0),(109,'INO-ZW-604',29,3,101),(110,'',109,4,0),(111,'INO-ZW-605',29,3,101),(112,'',111,4,0),(113,'EXNO-ZW-601',27,3,100),(114,'',113,4,0),(115,'EXNO-ZW-602',27,3,101),(116,'',115,4,0),(117,'EXNO-ZW-603',27,3,100),(118,'',117,4,0),(119,'EXNO-ZW-604',27,3,101),(120,'',119,4,0),(121,'EXNO-ZW-605',27,3,101),(122,'',121,4,0),(123,'EXNO-WW-001',30,3,100),(124,'',123,4,0),(125,'EXNO-WW-002',30,3,101),(126,'',125,4,0),(127,'EXNO-WW-003',30,3,100),(128,'',127,4,0),(129,'EXNO-WW-004',30,3,101),(130,'',129,4,0),(131,'EXNO-WW-005',30,3,101),(132,'',131,4,0),(133,'TYW-77-001',24,3,100),(134,'',133,4,0),(135,'TYW-77-002',24,3,101),(136,'',135,4,0),(137,'TYW-77-003',24,3,100),(138,'',137,4,0),(139,'TYW-77-004',24,3,101),(140,'',139,4,0),(141,'TYW-77-005',24,3,101),(142,'',141,4,0),(143,'TYW-77-101',25,3,100),(144,'',143,4,0),(145,'TYW-77-102',25,3,101),(146,'',145,4,0),(147,'TYW-77-103',25,3,100),(148,'',147,4,0),(149,'TYW-77-104',25,3,101),(150,'',149,4,0),(151,'TYW-77-105',25,3,101),(152,'',151,4,0),(153,'TYW-77-201',28,3,100),(154,'',153,4,0),(155,'TYW-77-202',28,3,101),(156,'',155,4,0),(157,'TYW-77-203',28,3,100),(158,'',157,4,0),(159,'TYW-77-204',28,3,101),(160,'',159,4,0),(161,'TYW-77-205',28,3,101),(162,'',161,4,0); -/*!40000 ALTER TABLE `nodes_hierarchy` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `object_keywords` --- - -DROP TABLE IF EXISTS `object_keywords`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `object_keywords` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `fk_id` int(10) unsigned NOT NULL DEFAULT '0', - `fk_table` varchar(30) DEFAULT '', - `keyword_id` int(10) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `object_keywords` --- - -LOCK TABLES `object_keywords` WRITE; -/*!40000 ALTER TABLE `object_keywords` DISABLE KEYS */; -/*!40000 ALTER TABLE `object_keywords` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `platforms` --- - -DROP TABLE IF EXISTS `platforms`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `platforms` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(100) NOT NULL, - `testproject_id` int(10) unsigned NOT NULL, - `notes` text NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `idx_platforms` (`testproject_id`,`name`) -) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `platforms` --- - -LOCK TABLES `platforms` WRITE; -/*!40000 ALTER TABLE `platforms` DISABLE KEYS */; -INSERT INTO `platforms` VALUES (1,'Ferrari',2,''),(2,'Mc Laren',2,''),(3,'Red Bull',2,''),(4,'Mercedes',2,''),(7,'Renault',2,''); -/*!40000 ALTER TABLE `platforms` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `req_coverage` --- - -DROP TABLE IF EXISTS `req_coverage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `req_coverage` ( - `req_id` int(10) NOT NULL, - `testcase_id` int(10) NOT NULL, - KEY `req_testcase` (`req_id`,`testcase_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='relation test case ** requirements'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `req_coverage` --- - -LOCK TABLES `req_coverage` WRITE; -/*!40000 ALTER TABLE `req_coverage` DISABLE KEYS */; -/*!40000 ALTER TABLE `req_coverage` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `req_relations` --- - -DROP TABLE IF EXISTS `req_relations`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `req_relations` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `source_id` int(10) unsigned NOT NULL, - `destination_id` int(10) unsigned NOT NULL, - `relation_type` smallint(5) unsigned NOT NULL DEFAULT '1', - `author_id` int(10) unsigned DEFAULT NULL, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `req_relations` --- - -LOCK TABLES `req_relations` WRITE; -/*!40000 ALTER TABLE `req_relations` DISABLE KEYS */; -/*!40000 ALTER TABLE `req_relations` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `req_revisions` --- - -DROP TABLE IF EXISTS `req_revisions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `req_revisions` ( - `parent_id` int(10) unsigned NOT NULL, - `id` int(10) unsigned NOT NULL, - `revision` smallint(5) unsigned NOT NULL DEFAULT '1', - `req_doc_id` varchar(64) DEFAULT NULL, - `name` varchar(100) DEFAULT NULL, - `scope` text, - `status` char(1) NOT NULL DEFAULT 'V', - `type` char(1) DEFAULT NULL, - `active` tinyint(1) NOT NULL DEFAULT '1', - `is_open` tinyint(1) NOT NULL DEFAULT '1', - `expected_coverage` int(10) NOT NULL DEFAULT '1', - `log_message` text, - `author_id` int(10) unsigned DEFAULT NULL, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modifier_id` int(10) unsigned DEFAULT NULL, - `modification_ts` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - PRIMARY KEY (`id`), - UNIQUE KEY `req_revisions_uidx1` (`parent_id`,`revision`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `req_revisions` --- - -LOCK TABLES `req_revisions` WRITE; -/*!40000 ALTER TABLE `req_revisions` DISABLE KEYS */; -/*!40000 ALTER TABLE `req_revisions` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `req_specs` --- - -DROP TABLE IF EXISTS `req_specs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `req_specs` ( - `id` int(10) unsigned NOT NULL, - `testproject_id` int(10) unsigned NOT NULL, - `doc_id` varchar(64) NOT NULL, - `scope` text, - `total_req` int(10) NOT NULL DEFAULT '0', - `type` char(1) DEFAULT 'n', - `author_id` int(10) unsigned DEFAULT NULL, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modifier_id` int(10) unsigned DEFAULT NULL, - `modification_ts` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - PRIMARY KEY (`id`), - UNIQUE KEY `req_spec_uk1` (`doc_id`,`testproject_id`), - KEY `testproject_id` (`testproject_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Dev. Documents (e.g. System Requirements Specification)'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `req_specs` --- - -LOCK TABLES `req_specs` WRITE; -/*!40000 ALTER TABLE `req_specs` DISABLE KEYS */; -/*!40000 ALTER TABLE `req_specs` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `req_versions` --- - -DROP TABLE IF EXISTS `req_versions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `req_versions` ( - `id` int(10) unsigned NOT NULL, - `version` smallint(5) unsigned NOT NULL DEFAULT '1', - `revision` smallint(5) unsigned NOT NULL DEFAULT '1', - `scope` text, - `status` char(1) NOT NULL DEFAULT 'V', - `type` char(1) DEFAULT NULL, - `active` tinyint(1) NOT NULL DEFAULT '1', - `is_open` tinyint(1) NOT NULL DEFAULT '1', - `expected_coverage` int(10) NOT NULL DEFAULT '1', - `author_id` int(10) unsigned DEFAULT NULL, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modifier_id` int(10) unsigned DEFAULT NULL, - `modification_ts` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `log_message` text, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `req_versions` --- - -LOCK TABLES `req_versions` WRITE; -/*!40000 ALTER TABLE `req_versions` DISABLE KEYS */; -/*!40000 ALTER TABLE `req_versions` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `requirements` --- - -DROP TABLE IF EXISTS `requirements`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `requirements` ( - `id` int(10) unsigned NOT NULL, - `srs_id` int(10) unsigned NOT NULL, - `req_doc_id` varchar(64) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `requirements_req_doc_id` (`srs_id`,`req_doc_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `requirements` --- - -LOCK TABLES `requirements` WRITE; -/*!40000 ALTER TABLE `requirements` DISABLE KEYS */; -/*!40000 ALTER TABLE `requirements` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `rights` --- - -DROP TABLE IF EXISTS `rights`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `rights` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `description` varchar(100) NOT NULL DEFAULT '', - PRIMARY KEY (`id`), - UNIQUE KEY `rights_descr` (`description`) -) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `rights` --- - -LOCK TABLES `rights` WRITE; -/*!40000 ALTER TABLE `rights` DISABLE KEYS */; -INSERT INTO `rights` VALUES (18,'cfield_management'),(17,'cfield_view'),(22,'events_mgt'),(9,'mgt_modify_key'),(12,'mgt_modify_product'),(11,'mgt_modify_req'),(7,'mgt_modify_tc'),(16,'mgt_testplan_create'),(13,'mgt_users'),(20,'mgt_view_events'),(8,'mgt_view_key'),(10,'mgt_view_req'),(6,'mgt_view_tc'),(21,'mgt_view_usergroups'),(24,'platform_management'),(25,'platform_view'),(26,'project_inventory_management'),(27,'project_inventory_view'),(14,'role_management'),(19,'system_configuration'),(2,'testplan_create_build'),(1,'testplan_execute'),(3,'testplan_metrics'),(4,'testplan_planning'),(5,'testplan_user_role_assignment'),(23,'testproject_user_role_assignment'),(15,'user_role_assignment'); -/*!40000 ALTER TABLE `rights` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `risk_assignments` --- - -DROP TABLE IF EXISTS `risk_assignments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `risk_assignments` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `testplan_id` int(10) unsigned NOT NULL DEFAULT '0', - `node_id` int(10) unsigned NOT NULL DEFAULT '0', - `risk` char(1) NOT NULL DEFAULT '2', - `importance` char(1) NOT NULL DEFAULT 'M', - PRIMARY KEY (`id`), - UNIQUE KEY `risk_assignments_tplan_node_id` (`testplan_id`,`node_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `risk_assignments` --- - -LOCK TABLES `risk_assignments` WRITE; -/*!40000 ALTER TABLE `risk_assignments` DISABLE KEYS */; -/*!40000 ALTER TABLE `risk_assignments` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `role_rights` --- - -DROP TABLE IF EXISTS `role_rights`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `role_rights` ( - `role_id` int(10) NOT NULL DEFAULT '0', - `right_id` int(10) NOT NULL DEFAULT '0', - PRIMARY KEY (`role_id`,`right_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `role_rights` --- - -LOCK TABLES `role_rights` WRITE; -/*!40000 ALTER TABLE `role_rights` DISABLE KEYS */; -INSERT INTO `role_rights` VALUES (4,3),(4,6),(4,7),(4,8),(4,9),(4,10),(4,11),(5,3),(5,6),(5,8),(6,1),(6,2),(6,3),(6,6),(6,7),(6,8),(6,9),(6,11),(6,25),(6,27),(7,1),(7,3),(7,6),(7,8),(8,1),(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8),(8,9),(8,10),(8,11),(8,12),(8,13),(8,14),(8,15),(8,16),(8,17),(8,18),(8,19),(8,20),(8,21),(8,22),(8,23),(8,24),(8,25),(8,26),(8,27),(9,1),(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),(9,9),(9,10),(9,11),(9,15),(9,16),(9,24),(9,25),(9,26),(9,27); -/*!40000 ALTER TABLE `role_rights` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `roles` --- - -DROP TABLE IF EXISTS `roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `roles` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `description` varchar(100) NOT NULL DEFAULT '', - `notes` text, - PRIMARY KEY (`id`), - UNIQUE KEY `role_rights_roles_descr` (`description`) -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `roles` --- - -LOCK TABLES `roles` WRITE; -/*!40000 ALTER TABLE `roles` DISABLE KEYS */; -INSERT INTO `roles` VALUES (1,'',NULL),(2,'',NULL),(3,'',NULL),(4,'test designer',NULL),(5,'guest',NULL),(6,'senior tester',NULL),(7,'tester',NULL),(8,'admin',NULL),(9,'leader',NULL); -/*!40000 ALTER TABLE `roles` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `tcsteps` --- - -DROP TABLE IF EXISTS `tcsteps`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `tcsteps` ( - `id` int(10) unsigned NOT NULL, - `step_number` int(11) NOT NULL DEFAULT '1', - `actions` text, - `expected_results` text, - `active` tinyint(1) NOT NULL DEFAULT '1', - `execution_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 -> manual, 2 -> automated', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `tcsteps` --- - -LOCK TABLES `tcsteps` WRITE; -/*!40000 ALTER TABLE `tcsteps` DISABLE KEYS */; -/*!40000 ALTER TABLE `tcsteps` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `tcversions` --- - -DROP TABLE IF EXISTS `tcversions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `tcversions` ( - `id` int(10) unsigned NOT NULL, - `tc_external_id` int(10) unsigned DEFAULT NULL, - `version` smallint(5) unsigned NOT NULL DEFAULT '1', - `layout` smallint(5) unsigned NOT NULL DEFAULT '1', - `status` smallint(5) unsigned NOT NULL DEFAULT '1', - `summary` text, - `preconditions` text, - `importance` smallint(5) unsigned NOT NULL DEFAULT '2', - `author_id` int(10) unsigned DEFAULT NULL, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updater_id` int(10) unsigned DEFAULT NULL, - `modification_ts` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `active` tinyint(1) NOT NULL DEFAULT '1', - `is_open` tinyint(1) NOT NULL DEFAULT '1', - `execution_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1 -> manual, 2 -> automated', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `tcversions` --- - -LOCK TABLES `tcversions` WRITE; -/*!40000 ALTER TABLE `tcversions` DISABLE KEYS */; -INSERT INTO `tcversions` VALUES (32,1,1,1,1,'','',2,1,'2012-05-01 10:08:43',1,'2012-05-01 12:11:47',1,1,1),(34,2,1,1,1,'','',2,1,'2012-05-01 10:08:56',1,'2012-05-01 12:12:01',1,1,1),(36,3,1,1,1,'','',2,1,'2012-05-01 10:20:26',NULL,'0000-00-00 00:00:00',1,1,1),(38,4,1,1,1,'','',2,1,'2012-05-01 10:20:26',NULL,'0000-00-00 00:00:00',1,1,1),(40,5,1,1,1,'','',2,1,'2012-05-01 10:20:26',NULL,'0000-00-00 00:00:00',1,1,1),(42,6,1,1,1,'','',2,1,'2012-05-01 10:20:26',NULL,'0000-00-00 00:00:00',1,1,1),(44,7,1,1,1,'','',2,1,'2012-05-01 10:21:22',NULL,'0000-00-00 00:00:00',1,1,1),(46,8,1,1,1,'','',2,1,'2012-05-01 10:21:22',NULL,'0000-00-00 00:00:00',1,1,1),(48,9,1,1,1,'','',2,1,'2012-05-01 10:21:22',NULL,'0000-00-00 00:00:00',1,1,1),(50,10,1,1,1,'','',2,1,'2012-05-01 10:21:22',NULL,'0000-00-00 00:00:00',1,1,1),(52,11,1,1,1,'','',2,1,'2012-05-01 10:22:27',NULL,'0000-00-00 00:00:00',1,1,1),(54,12,1,1,1,'','',2,1,'2012-05-01 10:22:27',NULL,'0000-00-00 00:00:00',1,1,1),(56,13,1,1,1,'','',2,1,'2012-05-01 10:22:27',NULL,'0000-00-00 00:00:00',1,1,1),(58,14,1,1,1,'','',2,1,'2012-05-01 10:22:27',NULL,'0000-00-00 00:00:00',1,1,1),(60,15,1,1,1,'','',2,1,'2012-05-01 10:23:12',NULL,'0000-00-00 00:00:00',1,1,1),(62,16,1,1,1,'','',2,1,'2012-05-01 10:23:12',NULL,'0000-00-00 00:00:00',1,1,1),(64,17,1,1,1,'','',2,1,'2012-05-01 10:23:12',NULL,'0000-00-00 00:00:00',1,1,1),(66,18,1,1,1,'','',2,1,'2012-05-01 10:23:12',NULL,'0000-00-00 00:00:00',1,1,1),(68,19,1,1,1,'','',2,1,'2012-05-01 10:24:07',NULL,'0000-00-00 00:00:00',1,1,1),(70,20,1,1,1,'','',2,1,'2012-05-01 10:24:07',NULL,'0000-00-00 00:00:00',1,1,1),(72,21,1,1,1,'','',2,1,'2012-05-01 10:24:07',NULL,'0000-00-00 00:00:00',1,1,1),(74,22,1,1,1,'','',2,1,'2012-05-01 10:24:07',NULL,'0000-00-00 00:00:00',1,1,1),(76,23,1,1,1,'','',2,1,'2012-05-01 10:24:50',NULL,'0000-00-00 00:00:00',1,1,1),(78,24,1,1,1,'','',2,1,'2012-05-01 10:24:51',NULL,'0000-00-00 00:00:00',1,1,1),(80,25,1,1,1,'','',2,1,'2012-05-01 10:24:51',NULL,'0000-00-00 00:00:00',1,1,1),(82,26,1,1,1,'','',2,1,'2012-05-01 10:24:51',NULL,'0000-00-00 00:00:00',1,1,1),(84,27,1,1,1,'','',2,1,'2012-05-01 10:25:56',NULL,'0000-00-00 00:00:00',1,1,1),(86,28,1,1,1,'','',2,1,'2012-05-01 10:25:56',NULL,'0000-00-00 00:00:00',1,1,1),(88,29,1,1,1,'','',2,1,'2012-05-01 10:25:56',NULL,'0000-00-00 00:00:00',1,1,1),(90,30,1,1,1,'','',2,1,'2012-05-01 10:25:56',NULL,'0000-00-00 00:00:00',1,1,1),(92,31,1,1,1,'','',2,1,'2012-05-01 10:25:56',NULL,'0000-00-00 00:00:00',1,1,1),(94,32,1,1,1,'','',2,1,'2012-05-01 10:26:50',NULL,'0000-00-00 00:00:00',1,1,1),(96,33,1,1,1,'','',2,1,'2012-05-01 10:26:50',NULL,'0000-00-00 00:00:00',1,1,1),(98,34,1,1,1,'','',2,1,'2012-05-01 10:26:51',NULL,'0000-00-00 00:00:00',1,1,1),(100,35,1,1,1,'','',2,1,'2012-05-01 10:26:51',NULL,'0000-00-00 00:00:00',1,1,1),(102,36,1,1,1,'','',2,1,'2012-05-01 10:26:51',NULL,'0000-00-00 00:00:00',1,1,1),(104,37,1,1,1,'','',2,1,'2012-05-01 10:27:44',NULL,'0000-00-00 00:00:00',1,1,1),(106,38,1,1,1,'','',2,1,'2012-05-01 10:27:44',NULL,'0000-00-00 00:00:00',1,1,1),(108,39,1,1,1,'','',2,1,'2012-05-01 10:27:44',NULL,'0000-00-00 00:00:00',1,1,1),(110,40,1,1,1,'','',2,1,'2012-05-01 10:27:44',NULL,'0000-00-00 00:00:00',1,1,1),(112,41,1,1,1,'','',2,1,'2012-05-01 10:27:44',NULL,'0000-00-00 00:00:00',1,1,1),(114,42,1,1,1,'','',2,1,'2012-05-01 10:28:26',NULL,'0000-00-00 00:00:00',1,1,1),(116,43,1,1,1,'','',2,1,'2012-05-01 10:28:26',NULL,'0000-00-00 00:00:00',1,1,1),(118,44,1,1,1,'','',2,1,'2012-05-01 10:28:27',NULL,'0000-00-00 00:00:00',1,1,1),(120,45,1,1,1,'','',2,1,'2012-05-01 10:28:27',NULL,'0000-00-00 00:00:00',1,1,1),(122,46,1,1,1,'','',2,1,'2012-05-01 10:28:27',NULL,'0000-00-00 00:00:00',1,1,1),(124,47,1,1,1,'','',2,1,'2012-05-01 10:29:04',NULL,'0000-00-00 00:00:00',1,1,1),(126,48,1,1,1,'','',2,1,'2012-05-01 10:29:04',NULL,'0000-00-00 00:00:00',1,1,1),(128,49,1,1,1,'','',2,1,'2012-05-01 10:29:04',NULL,'0000-00-00 00:00:00',1,1,1),(130,50,1,1,1,'','',2,1,'2012-05-01 10:29:04',NULL,'0000-00-00 00:00:00',1,1,1),(132,51,1,1,1,'','',2,1,'2012-05-01 10:29:04',NULL,'0000-00-00 00:00:00',1,1,1),(134,52,1,1,1,'','',2,1,'2012-05-01 10:29:45',NULL,'0000-00-00 00:00:00',1,1,1),(136,53,1,1,1,'','',2,1,'2012-05-01 10:29:45',NULL,'0000-00-00 00:00:00',1,1,1),(138,54,1,1,1,'','',2,1,'2012-05-01 10:29:45',NULL,'0000-00-00 00:00:00',1,1,1),(140,55,1,1,1,'','',2,1,'2012-05-01 10:29:45',NULL,'0000-00-00 00:00:00',1,1,1),(142,56,1,1,1,'','',2,1,'2012-05-01 10:29:45',NULL,'0000-00-00 00:00:00',1,1,1),(144,57,1,1,1,'','',2,1,'2012-05-01 10:30:20',NULL,'0000-00-00 00:00:00',1,1,1),(146,58,1,1,1,'','',2,1,'2012-05-01 10:30:20',NULL,'0000-00-00 00:00:00',1,1,1),(148,59,1,1,1,'','',2,1,'2012-05-01 10:30:20',NULL,'0000-00-00 00:00:00',1,1,1),(150,60,1,1,1,'','',2,1,'2012-05-01 10:30:20',NULL,'0000-00-00 00:00:00',1,1,1),(152,61,1,1,1,'','',2,1,'2012-05-01 10:30:20',NULL,'0000-00-00 00:00:00',1,1,1),(154,62,1,1,1,'','',2,1,'2012-05-01 10:30:46',NULL,'0000-00-00 00:00:00',1,1,1),(156,63,1,1,1,'','',2,1,'2012-05-01 10:30:46',NULL,'0000-00-00 00:00:00',1,1,1),(158,64,1,1,1,'','',2,1,'2012-05-01 10:30:46',NULL,'0000-00-00 00:00:00',1,1,1),(160,65,1,1,1,'','',2,1,'2012-05-01 10:30:46',NULL,'0000-00-00 00:00:00',1,1,1),(162,66,1,1,1,'','',2,1,'2012-05-01 10:30:47',NULL,'0000-00-00 00:00:00',1,1,1); -/*!40000 ALTER TABLE `tcversions` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `testcase_keywords` --- - -DROP TABLE IF EXISTS `testcase_keywords`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `testcase_keywords` ( - `testcase_id` int(10) unsigned NOT NULL DEFAULT '0', - `keyword_id` int(10) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`testcase_id`,`keyword_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `testcase_keywords` --- - -LOCK TABLES `testcase_keywords` WRITE; -/*!40000 ALTER TABLE `testcase_keywords` DISABLE KEYS */; -INSERT INTO `testcase_keywords` VALUES (31,1),(31,6),(33,2),(33,6),(35,1),(35,6),(37,2),(37,6),(39,1),(39,6),(41,2),(41,6),(43,1),(43,6),(45,2),(45,6),(47,1),(47,6),(49,2),(49,6),(51,1),(51,6),(53,2),(53,6),(55,1),(55,6),(57,2),(57,6),(59,1),(59,6),(61,2),(61,6),(63,1),(63,6),(65,2),(65,6),(67,1),(67,6),(69,2),(69,6),(71,1),(71,6),(73,2),(73,6),(75,1),(75,6),(77,2),(77,6),(79,1),(79,6),(81,2),(81,6),(83,1),(83,6),(85,2),(85,6),(87,1),(87,6),(89,2),(89,6),(91,2),(91,6),(93,1),(93,6),(95,2),(95,6),(97,1),(97,6),(99,2),(99,6),(101,2),(101,6),(103,1),(103,6),(105,2),(105,6),(107,1),(107,6),(109,2),(109,6),(111,2),(111,6),(113,1),(113,6),(115,2),(115,6),(117,1),(117,6),(119,2),(119,6),(121,2),(121,6),(123,1),(123,6),(125,2),(125,6),(127,1),(127,6),(129,2),(129,6),(131,2),(131,6),(133,1),(133,6),(135,2),(135,6),(137,1),(137,6),(139,2),(139,6),(141,2),(141,6),(143,1),(143,6),(145,2),(145,6),(147,1),(147,6),(149,2),(149,6),(151,2),(151,6),(153,1),(153,6),(155,2),(155,6),(157,1),(157,6),(159,2),(159,6),(161,2),(161,6); -/*!40000 ALTER TABLE `testcase_keywords` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `testplan_platforms` --- - -DROP TABLE IF EXISTS `testplan_platforms`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `testplan_platforms` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `testplan_id` int(10) unsigned NOT NULL, - `platform_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `idx_testplan_platforms` (`testplan_id`,`platform_id`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='Connects a testplan with platforms'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `testplan_platforms` --- - -LOCK TABLES `testplan_platforms` WRITE; -/*!40000 ALTER TABLE `testplan_platforms` DISABLE KEYS */; -INSERT INTO `testplan_platforms` VALUES (1,4,1),(2,4,2),(4,4,3),(3,4,4); -/*!40000 ALTER TABLE `testplan_platforms` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `testplan_tcversions` --- - -DROP TABLE IF EXISTS `testplan_tcversions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `testplan_tcversions` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `testplan_id` int(10) unsigned NOT NULL DEFAULT '0', - `tcversion_id` int(10) unsigned NOT NULL DEFAULT '0', - `node_order` int(10) unsigned NOT NULL DEFAULT '1', - `urgency` smallint(5) NOT NULL DEFAULT '2', - `platform_id` int(10) unsigned NOT NULL DEFAULT '0', - `author_id` int(10) unsigned DEFAULT NULL, - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `testplan_tcversions_tplan_tcversion` (`testplan_id`,`tcversion_id`,`platform_id`) -) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `testplan_tcversions` --- - -LOCK TABLES `testplan_tcversions` WRITE; -/*!40000 ALTER TABLE `testplan_tcversions` DISABLE KEYS */; -INSERT INTO `testplan_tcversions` VALUES (1,4,32,1000,2,1,1,'2012-05-01 10:33:49'),(2,4,32,1000,2,2,1,'2012-05-01 10:33:49'),(3,4,32,1000,2,4,1,'2012-05-01 10:33:49'),(4,4,32,1000,2,3,1,'2012-05-01 10:33:49'),(5,4,34,1010,2,1,1,'2012-05-01 10:33:49'),(6,4,34,1010,2,2,1,'2012-05-01 10:33:49'),(7,4,34,1010,2,4,1,'2012-05-01 10:33:49'),(8,4,34,1010,2,3,1,'2012-05-01 10:33:49'),(9,4,44,1000,2,1,1,'2012-05-01 10:34:05'),(10,4,44,1000,2,2,1,'2012-05-01 10:34:05'),(11,4,44,1000,2,4,1,'2012-05-01 10:34:05'),(12,4,48,1000,2,1,1,'2012-05-01 10:34:05'),(13,4,48,1000,2,2,1,'2012-05-01 10:34:05'),(14,4,48,1000,2,4,1,'2012-05-01 10:34:05'),(15,4,46,1010,2,1,1,'2012-05-01 10:34:05'),(16,4,46,1010,2,2,1,'2012-05-01 10:34:05'),(17,4,46,1010,2,4,1,'2012-05-01 10:34:05'),(18,4,50,1010,2,1,1,'2012-05-01 10:34:05'),(19,4,50,1010,2,2,1,'2012-05-01 10:34:05'),(20,4,50,1010,2,4,1,'2012-05-01 10:34:05'),(21,4,94,1000,2,4,1,'2012-05-01 10:34:41'),(22,4,94,1000,2,3,1,'2012-05-01 10:34:41'),(23,4,98,1000,2,4,1,'2012-05-01 10:34:41'),(24,4,98,1000,2,3,1,'2012-05-01 10:34:41'),(25,4,96,1010,2,4,1,'2012-05-01 10:34:41'),(26,4,96,1010,2,3,1,'2012-05-01 10:34:41'),(27,4,100,1010,2,4,1,'2012-05-01 10:34:41'),(28,4,100,1010,2,3,1,'2012-05-01 10:34:41'),(29,4,102,1010,2,4,1,'2012-05-01 10:34:41'),(30,4,102,1010,2,3,1,'2012-05-01 10:34:41'),(31,4,104,1000,2,4,1,'2012-05-01 10:34:41'),(32,4,104,1000,2,3,1,'2012-05-01 10:34:41'),(33,4,108,1000,2,4,1,'2012-05-01 10:34:41'),(34,4,108,1000,2,3,1,'2012-05-01 10:34:41'),(35,4,106,1010,2,4,1,'2012-05-01 10:34:41'),(36,4,106,1010,2,3,1,'2012-05-01 10:34:41'),(37,4,110,1010,2,4,1,'2012-05-01 10:34:41'),(38,4,110,1010,2,3,1,'2012-05-01 10:34:41'),(39,4,112,1010,2,4,1,'2012-05-01 10:34:41'),(40,4,112,1010,2,3,1,'2012-05-01 10:34:41'),(41,4,144,1000,2,1,1,'2012-05-01 10:34:56'),(42,4,144,1000,2,2,1,'2012-05-01 10:34:56'),(43,4,144,1000,2,4,1,'2012-05-01 10:34:56'),(44,4,144,1000,2,3,1,'2012-05-01 10:34:56'),(45,4,148,1000,2,1,1,'2012-05-01 10:34:56'),(46,4,148,1000,2,2,1,'2012-05-01 10:34:56'),(47,4,148,1000,2,4,1,'2012-05-01 10:34:56'),(48,4,148,1000,2,3,1,'2012-05-01 10:34:56'),(49,4,146,1010,2,1,1,'2012-05-01 10:34:56'),(50,4,146,1010,2,2,1,'2012-05-01 10:34:56'),(51,4,146,1010,2,4,1,'2012-05-01 10:34:56'),(52,4,146,1010,2,3,1,'2012-05-01 10:34:56'),(53,4,150,1010,2,1,1,'2012-05-01 10:34:56'),(54,4,150,1010,2,2,1,'2012-05-01 10:34:56'),(55,4,150,1010,2,4,1,'2012-05-01 10:34:56'),(56,4,150,1010,2,3,1,'2012-05-01 10:34:56'),(57,4,152,1010,2,1,1,'2012-05-01 10:34:56'),(58,4,152,1010,2,2,1,'2012-05-01 10:34:56'),(59,4,152,1010,2,4,1,'2012-05-01 10:34:56'),(60,4,152,1010,2,3,1,'2012-05-01 10:34:56'); -/*!40000 ALTER TABLE `testplan_tcversions` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `testplans` --- - -DROP TABLE IF EXISTS `testplans`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `testplans` ( - `id` int(10) unsigned NOT NULL, - `testproject_id` int(10) unsigned NOT NULL DEFAULT '0', - `notes` text, - `active` tinyint(1) NOT NULL DEFAULT '1', - `is_open` tinyint(1) NOT NULL DEFAULT '1', - `is_public` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - KEY `testplans_testproject_id_active` (`testproject_id`,`active`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `testplans` --- - -LOCK TABLES `testplans` WRITE; -/*!40000 ALTER TABLE `testplans` DISABLE KEYS */; -INSERT INTO `testplans` VALUES (4,2,'

P Zero™ Red, a supersoft for street circuits. Of the four slick tyres, this is the only one to remain unchanged from the 2011 season. It showed itself to be particularly versatile, offering high peaks of performance over slow and twisty circuits that are characterised by slippery asphalt and low lateral loadings. This is the ideal compound for street circuits or semipermanent facilities.

',1,1,1),(5,2,'

P Zero™ Yellow, softer with less blistering. The new soft tyre is well suited to circuits with low tyre wear. It is designed to offer a high level of grip coupled with a significant amount of degradation, resulting in a comparatively short lifespan that will give the teams a greater number of options with pit stop strategy and even closer racing. Compared to the equivalent tyre in 2011, the new soft offers greater thermal resistance to reduce the risk of blistering. Tested for the first time during free practice at last year’s Abu Dhabi Grand Prix, the new soft tyre is set to be one of the most frequent nominations in 2012, together with the new medium tyre. This combination offers a great deal of flexibility and also a rapid warm-up time.

',1,1,1),(6,2,'

P Zero™ White, the medium tyre that is well suited to all conditions. This extremely versatile tyre adapts itself well to all sorts of track conditions, particularly when asphalt and circuit characteristics are variable. The brand new P Zero™ White is intended as the ‘option’ tyre on tracks with high temperatures or abrasive surfaces and as the ‘prime’ tyre on tracks that are less severe with fewer demands on the tyres. The new medium compound was tried out last year during free practice at the German Grand Prix and made another appearance during the young driver test in Abu Dhabi.

',1,1,1),(7,2,'

P Zero™ Silver, hard but not inflexible. The new hard tyre guarantees maximum durability and the least degradation, together with optimal resistance to the most extreme conditions, but is not as hard as the equivalent tyre last year. The P Zero™ Silver is ideal for long runs, taking more time to warm up, as well as being suited to circuits with abrasive asphalt, big lateral forces and high temperatures. The new P Zero™ Silver was tested at the Barcelona circuit by Pirelli’s test driver Lucas di Grassi, and is the only one of the new compounds that the regular drivers have not yet experienced.

',1,1,1),(8,3,'

Cinturato™ Green, the intermediate for light rain. After the excellent performances seen from this tyre throughout the 2011 season during particularly demanding races such as the Canadian Grand Prix, Pirelli’s engineers decided not to make any changes to the intermediate tyres. The shallower grooves compared to the full wet tyres mean that the intermediates do not drain away as much water, making this the ideal choice for wet or drying asphalt, without compromising on performance.

',1,1,1),(9,3,'

Cinturato™ Blue, the full wets. Of the two wet tyres, only the full wet has been significantly altered compared to the 2011 version. The changes relate to the rear tyres, which use a different profile in order to optimise the dispersal of water in case of aquaplaning and guarantee a greater degree of driving precision. Characterised by deep grooves, similar to those seen on a road car tyre, the wet tyres are designed to expel more than 60 litres of water per second at a speed of 300 kph: six times more than a road car tyre, which disperses about 10 litres per second at a much lower speed.

',1,1,1); -/*!40000 ALTER TABLE `testplans` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `testprojects` --- - -DROP TABLE IF EXISTS `testprojects`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `testprojects` ( - `id` int(10) unsigned NOT NULL, - `notes` text, - `color` varchar(12) NOT NULL DEFAULT '#9BD', - `active` tinyint(1) NOT NULL DEFAULT '1', - `option_reqs` tinyint(1) NOT NULL DEFAULT '0', - `option_priority` tinyint(1) NOT NULL DEFAULT '0', - `option_automation` tinyint(1) NOT NULL DEFAULT '0', - `options` text, - `prefix` varchar(16) NOT NULL, - `tc_counter` int(10) unsigned NOT NULL DEFAULT '0', - `is_public` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - UNIQUE KEY `testprojects_prefix` (`prefix`), - KEY `testprojects_id_active` (`id`,`active`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `testprojects` --- - -LOCK TABLES `testprojects` WRITE; -/*!40000 ALTER TABLE `testprojects` DISABLE KEYS */; -INSERT INTO `testprojects` VALUES (2,'

In accordance with the regulations laid down by the FIA (Fédération Internationale de l\'Automobile) Pirelli will supply two different types of tyre designed for two different types of use.
\r\nThe first type of tyre has been designed for dry surfaces, while the second is for wet surfaces.

','',1,0,0,0,'O:8:\"stdClass\":4:{s:19:\"requirementsEnabled\";i:1;s:19:\"testPriorityEnabled\";i:1;s:17:\"automationEnabled\";i:1;s:16:\"inventoryEnabled\";i:1;}','PDT',66,1),(3,'

in accordance with the regulations laid down by the FIA (Fédération Internationale de l\'Automobile) Pirelli will supply two different types of tyre designed for two different types of use.
\r\nThe first type of tyre has been designed for dry surfaces, while the second is for wet surfaces.

','',1,0,0,0,'O:8:\"stdClass\":4:{s:19:\"requirementsEnabled\";i:1;s:19:\"testPriorityEnabled\";i:1;s:17:\"automationEnabled\";i:1;s:16:\"inventoryEnabled\";i:1;}','PWT',0,1); -/*!40000 ALTER TABLE `testprojects` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `testsuites` --- - -DROP TABLE IF EXISTS `testsuites`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `testsuites` ( - `id` int(10) unsigned NOT NULL, - `details` text, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `testsuites` --- - -LOCK TABLES `testsuites` WRITE; -/*!40000 ALTER TABLE `testsuites` DISABLE KEYS */; -INSERT INTO `testsuites` VALUES (10,''),(11,'


\r\n
\r\n
\r\n

'),(12,''),(13,''),(14,''),(15,'


\r\n

'),(16,''),(17,''),(18,''),(19,''),(20,''),(21,''),(22,''),(23,''),(24,''),(25,''),(26,''),(27,''),(28,''),(29,''),(30,''); -/*!40000 ALTER TABLE `testsuites` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `transactions` --- - -DROP TABLE IF EXISTS `transactions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `transactions` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `entry_point` varchar(45) NOT NULL DEFAULT '', - `start_time` int(10) unsigned NOT NULL DEFAULT '0', - `end_time` int(10) unsigned NOT NULL DEFAULT '0', - `user_id` int(10) unsigned NOT NULL DEFAULT '0', - `session_id` varchar(45) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `transactions` --- - -LOCK TABLES `transactions` WRITE; -/*!40000 ALTER TABLE `transactions` DISABLE KEYS */; -INSERT INTO `transactions` VALUES (1,'/tl193-untouched/lib/general/navBar.php',1335862631,1335862631,210,'95p2svhjqusr1vjbhgj9bird23'),(2,'/tl193-untouched/lib/general/mainPage.php',1335862631,1335862631,210,'95p2svhjqusr1vjbhgj9bird23'),(3,'/tl193-untouched/lib/general/mainPage.php',1335862696,1335862696,210,'95p2svhjqusr1vjbhgj9bird23'),(4,'/tl193-untouched/lib/general/navBar.php',1335862696,1335862696,210,'95p2svhjqusr1vjbhgj9bird23'),(5,'/development/tl-old/tl193-untouched/login.php',1335862713,1335862713,1,'190eh67vq7bsrde26gde2g46c3'),(6,'/tl193-untouched/lib/general/mainPage.php',1335862714,1335862714,1,'190eh67vq7bsrde26gde2g46c3'),(7,'/tl193-untouched/lib/project/projectEdit.php',1335862746,1335862746,1,'190eh67vq7bsrde26gde2g46c3'),(8,'/tl193-untouched/lib/project/projectEdit.php',1335864014,1335864014,1,'190eh67vq7bsrde26gde2g46c3'),(9,'/tl193-untouched/lib/project/projectEdit.php',1335864053,1335864053,1,'190eh67vq7bsrde26gde2g46c3'),(10,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864120,1335864120,1,'190eh67vq7bsrde26gde2g46c3'),(11,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864160,1335864160,1,'190eh67vq7bsrde26gde2g46c3'),(12,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864193,1335864193,1,'190eh67vq7bsrde26gde2g46c3'),(13,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864246,1335864246,1,'190eh67vq7bsrde26gde2g46c3'),(14,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864293,1335864294,1,'190eh67vq7bsrde26gde2g46c3'),(15,'/tl-old/tl193-untouched/lib/plan/planEdit.php',1335864326,1335864326,1,'190eh67vq7bsrde26gde2g46c3'),(16,'/tl193-untouched/lib/project/projectEdit.php',1335864347,1335864347,1,'190eh67vq7bsrde26gde2g46c3'),(17,'/tl193-untouched/lib/plan/buildEdit.php',1335864400,1335864400,1,'190eh67vq7bsrde26gde2g46c3'),(18,'/tl193-untouched/lib/plan/buildEdit.php',1335864414,1335864414,1,'190eh67vq7bsrde26gde2g46c3'),(19,'/tl193-untouched/lib/plan/buildEdit.php',1335864428,1335864429,1,'190eh67vq7bsrde26gde2g46c3'),(20,'/tl193-untouched/lib/plan/buildEdit.php',1335864459,1335864459,1,'190eh67vq7bsrde26gde2g46c3'),(21,'/tl193-untouched/lib/plan/buildEdit.php',1335864466,1335864466,1,'190eh67vq7bsrde26gde2g46c3'),(22,'/tl193-untouched/lib/plan/buildEdit.php',1335864473,1335864473,1,'190eh67vq7bsrde26gde2g46c3'),(23,'/lib/keywords/keywordsEdit.php',1335866991,1335866991,1,'190eh67vq7bsrde26gde2g46c3'),(24,'/lib/keywords/keywordsEdit.php',1335867005,1335867005,1,'190eh67vq7bsrde26gde2g46c3'),(25,'/lib/keywords/keywordsEdit.php',1335867022,1335867022,1,'190eh67vq7bsrde26gde2g46c3'),(26,'/lib/keywords/keywordsEdit.php',1335867034,1335867034,1,'190eh67vq7bsrde26gde2g46c3'),(27,'/lib/keywords/keywordsEdit.php',1335867048,1335867048,1,'190eh67vq7bsrde26gde2g46c3'),(28,'/lib/keywords/keywordsEdit.php',1335867057,1335867057,1,'190eh67vq7bsrde26gde2g46c3'),(29,'/tl193-untouched/lib/testcases/tcEdit.php',1335867108,1335867108,1,'190eh67vq7bsrde26gde2g46c3'),(30,'/tl193-untouched/lib/testcases/tcEdit.php',1335867121,1335867121,1,'190eh67vq7bsrde26gde2g46c3'),(31,'/tl193-untouched/lib/testcases/tcImport.php',1335867626,1335867626,1,'190eh67vq7bsrde26gde2g46c3'),(32,'/tl193-untouched/lib/testcases/tcImport.php',1335867682,1335867682,1,'190eh67vq7bsrde26gde2g46c3'),(33,'/tl193-untouched/lib/testcases/tcImport.php',1335867747,1335867747,1,'190eh67vq7bsrde26gde2g46c3'),(34,'/tl193-untouched/lib/testcases/tcImport.php',1335867792,1335867792,1,'190eh67vq7bsrde26gde2g46c3'),(35,'/tl193-untouched/lib/testcases/tcImport.php',1335867847,1335867847,1,'190eh67vq7bsrde26gde2g46c3'),(36,'/tl193-untouched/lib/testcases/tcImport.php',1335867890,1335867891,1,'190eh67vq7bsrde26gde2g46c3'),(37,'/tl193-untouched/lib/testcases/tcImport.php',1335867956,1335867956,1,'190eh67vq7bsrde26gde2g46c3'),(38,'/tl193-untouched/lib/testcases/tcImport.php',1335868010,1335868011,1,'190eh67vq7bsrde26gde2g46c3'),(39,'/tl193-untouched/lib/testcases/tcImport.php',1335868064,1335868064,1,'190eh67vq7bsrde26gde2g46c3'),(40,'/tl193-untouched/lib/testcases/tcImport.php',1335868106,1335868107,1,'190eh67vq7bsrde26gde2g46c3'),(41,'/tl193-untouched/lib/testcases/tcImport.php',1335868144,1335868144,1,'190eh67vq7bsrde26gde2g46c3'),(42,'/tl193-untouched/lib/testcases/tcImport.php',1335868185,1335868185,1,'190eh67vq7bsrde26gde2g46c3'),(43,'/tl193-untouched/lib/testcases/tcImport.php',1335868220,1335868220,1,'190eh67vq7bsrde26gde2g46c3'),(44,'/tl193-untouched/lib/testcases/tcImport.php',1335868246,1335868247,1,'190eh67vq7bsrde26gde2g46c3'),(45,'/lib/plan/planAddTCNavigator.php',1335868398,1335868399,1,'190eh67vq7bsrde26gde2g46c3'),(46,'/tl193-untouched/lib/plan/planAddTC.php',1335868429,1335868429,1,'190eh67vq7bsrde26gde2g46c3'),(47,'/tl193-untouched/lib/plan/planAddTC.php',1335868446,1335868446,1,'190eh67vq7bsrde26gde2g46c3'),(48,'/tl193-untouched/lib/plan/planAddTC.php',1335868481,1335868481,1,'190eh67vq7bsrde26gde2g46c3'),(49,'/tl193-untouched/lib/plan/planAddTC.php',1335868496,1335868496,1,'190eh67vq7bsrde26gde2g46c3'); -/*!40000 ALTER TABLE `transactions` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_assignments` --- - -DROP TABLE IF EXISTS `user_assignments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_assignments` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `type` int(10) unsigned NOT NULL DEFAULT '1', - `feature_id` int(10) unsigned NOT NULL DEFAULT '0', - `user_id` int(10) unsigned DEFAULT '0', - `build_id` int(10) unsigned DEFAULT '0', - `deadline_ts` datetime DEFAULT NULL, - `assigner_id` int(10) unsigned DEFAULT '0', - `creation_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `status` int(10) unsigned DEFAULT '1', - PRIMARY KEY (`id`), - KEY `user_assignments_feature_id` (`feature_id`) -) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_assignments` --- - -LOCK TABLES `user_assignments` WRITE; -/*!40000 ALTER TABLE `user_assignments` DISABLE KEYS */; -INSERT INTO `user_assignments` VALUES (1,1,1,4,1,NULL,1,'2012-05-01 10:36:33',1),(2,1,2,3,1,NULL,1,'2012-05-01 10:36:33',1),(3,1,4,2,1,NULL,1,'2012-05-01 10:36:33',1),(4,1,3,5,1,NULL,1,'2012-05-01 10:36:33',1),(5,1,5,4,1,NULL,1,'2012-05-01 10:36:33',1),(6,1,6,3,1,NULL,1,'2012-05-01 10:36:33',1),(7,1,8,2,1,NULL,1,'2012-05-01 10:36:33',1),(8,1,7,5,1,NULL,1,'2012-05-01 10:36:33',1),(9,1,9,4,1,NULL,1,'2012-05-01 10:36:58',1),(10,1,10,3,1,NULL,1,'2012-05-01 10:36:58',1),(11,1,11,5,1,NULL,1,'2012-05-01 10:36:58',1),(12,1,12,4,1,NULL,1,'2012-05-01 10:36:58',1),(13,1,13,3,1,NULL,1,'2012-05-01 10:36:58',1),(14,1,14,5,1,NULL,1,'2012-05-01 10:36:58',1),(15,1,22,2,1,NULL,1,'2012-05-01 10:37:46',1),(16,1,24,2,1,NULL,1,'2012-05-01 10:37:46',1),(17,1,23,5,1,NULL,1,'2012-05-01 10:37:46',1),(18,1,26,2,1,NULL,1,'2012-05-01 10:37:46',1),(19,1,25,5,1,NULL,1,'2012-05-01 10:37:46',1),(20,1,28,2,1,NULL,1,'2012-05-01 10:37:46',1),(21,1,27,5,1,NULL,1,'2012-05-01 10:37:46',1),(22,1,30,2,1,NULL,1,'2012-05-01 10:37:46',1); -/*!40000 ALTER TABLE `user_assignments` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_group` --- - -DROP TABLE IF EXISTS `user_group`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_group` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(100) NOT NULL, - `description` text, - PRIMARY KEY (`id`), - UNIQUE KEY `idx_user_group` (`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_group` --- - -LOCK TABLES `user_group` WRITE; -/*!40000 ALTER TABLE `user_group` DISABLE KEYS */; -/*!40000 ALTER TABLE `user_group` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_group_assign` --- - -DROP TABLE IF EXISTS `user_group_assign`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_group_assign` ( - `usergroup_id` int(10) unsigned NOT NULL, - `user_id` int(10) unsigned NOT NULL, - UNIQUE KEY `idx_user_group_assign` (`usergroup_id`,`user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_group_assign` --- - -LOCK TABLES `user_group_assign` WRITE; -/*!40000 ALTER TABLE `user_group_assign` DISABLE KEYS */; -/*!40000 ALTER TABLE `user_group_assign` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_testplan_roles` --- - -DROP TABLE IF EXISTS `user_testplan_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_testplan_roles` ( - `user_id` int(10) NOT NULL DEFAULT '0', - `testplan_id` int(10) NOT NULL DEFAULT '0', - `role_id` int(10) NOT NULL DEFAULT '0', - PRIMARY KEY (`user_id`,`testplan_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_testplan_roles` --- - -LOCK TABLES `user_testplan_roles` WRITE; -/*!40000 ALTER TABLE `user_testplan_roles` DISABLE KEYS */; -/*!40000 ALTER TABLE `user_testplan_roles` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_testproject_roles` --- - -DROP TABLE IF EXISTS `user_testproject_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_testproject_roles` ( - `user_id` int(10) NOT NULL DEFAULT '0', - `testproject_id` int(10) NOT NULL DEFAULT '0', - `role_id` int(10) NOT NULL DEFAULT '0', - PRIMARY KEY (`user_id`,`testproject_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_testproject_roles` --- - -LOCK TABLES `user_testproject_roles` WRITE; -/*!40000 ALTER TABLE `user_testproject_roles` DISABLE KEYS */; -/*!40000 ALTER TABLE `user_testproject_roles` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `users` --- - -DROP TABLE IF EXISTS `users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `users` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `login` varchar(30) NOT NULL DEFAULT '', - `password` varchar(32) NOT NULL DEFAULT '', - `role_id` int(10) unsigned NOT NULL DEFAULT '0', - `email` varchar(100) NOT NULL DEFAULT '', - `first` varchar(30) NOT NULL DEFAULT '', - `last` varchar(30) NOT NULL DEFAULT '', - `locale` varchar(10) NOT NULL DEFAULT 'en_GB', - `default_testproject_id` int(10) DEFAULT NULL, - `active` tinyint(1) NOT NULL DEFAULT '1', - `script_key` varchar(32) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `users_login` (`login`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='User information'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `users` --- - -LOCK TABLES `users` WRITE; -/*!40000 ALTER TABLE `users` DISABLE KEYS */; -INSERT INTO `users` VALUES (1,'admin','21232f297a57a5a743894a0e4a801fc3',8,'','Testlink','Administrator','en_GB',NULL,1,NULL),(2,'Mark.Webber','9651cbc7c0b5fb1a81f2858a07813c82',8,'Mark.Webber@formulaone.com','Mark','Webber','en_GB',NULL,1,'DEVKEY-Webber'),(3,'Lewis.Hamilton','9651cbc7c0b5fb1a81f2858a07813c82',9,'Lewis.Hamilton@formulaone.com','Lewis','Hamilton','it_IT',NULL,1,'DEVKEY-Hamilton'),(4,'Fernando.Alonso','9651cbc7c0b5fb1a81f2858a07813c82',6,'Fernando.Alonso@formulaone.com','Fernando','Alonso','en_GB',NULL,1,'DEVKEY-Alonso'),(5,'Michael.Schumacher','9651cbc7c0b5fb1a81f2858a07813c82',8,'Michael.Schumacher@formulaone.com','Michael','Schumacher','en_GB',NULL,1,'DEVKEY-Schumacher'); -/*!40000 ALTER TABLE `users` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2012-05-01 12:38:54 diff --git a/docs/glosary.html b/docs/glossary.html similarity index 100% rename from docs/glosary.html rename to docs/glossary.html diff --git a/error.php b/error.php index 4a93506362..469a6844da 100644 --- a/error.php +++ b/error.php @@ -1,64 +1,69 @@ -message = 'Rocket Raccoon is watching You'; - $code = isset($_REQUEST['code']) ? $_REQUEST['code'] : 0; - - switch($code) { - case 1: - $args->message = 'No CSRFName found, probable invalid request.'; - break; - - case 2: - $args->message = 'Invalid CSRF token'; - break; - - default: - break; - } - - return $args; -} - -/** - * - */ -function init_gui($args) { - $gui = new stdClass(); - $gui->message = ''; - - if(isset($args->message) { - $gui->message = $args->message; - } - - return $gui; -} - -$templateCfg = templateConfiguration(); -$args = init_args(); -$gui = init_gui($args); - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->default_template); \ No newline at end of file +message = 'Rocket Raccoon is watching You'; + $code = isset($_REQUEST['code']) ? $_REQUEST['code'] : 0; + + switch ($code) { + case 1: + $args->message = 'No CSRFName found, probable invalid request.'; + break; + + case 2: + $args->message = 'Invalid CSRF token'; + break; + + default: + break; + } + + return $args; +} + +/** + * initializes the GUI + * + * @param stdClass $args + * @return stdClass + */ +function init_gui($args) +{ + $gui = new stdClass(); + $gui->message = ''; + + if (isset($args->message)) { + $gui->message = $args->message; + } + + return $gui; +} + +$templateCfg = templateConfiguration(); +$args = init_args(); +$gui = init_gui($args); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->default_template); diff --git a/firstLogin.php b/firstLogin.php index a7febf3842..2edd456f39 100644 --- a/firstLogin.php +++ b/firstLogin.php @@ -1,178 +1,213 @@ -assign('title', lang_get('fatal_page_title')); - $smarty->assign('content', lang_get('error_self_signup_disabled')); - $smarty->assign('link_to_op', "login.php"); - $smarty->assign('hint_text', lang_get('link_back_to_login')); - $smarty->display('workAreaSimple.tpl'); - exit(); +assign('title', lang_get('fatal_page_title')); + $smarty->assign('content', lang_get('error_self_signup_disabled')); + $smarty->assign('link_to_op', "login.php"); + $smarty->assign('hint_text', lang_get('link_back_to_login')); + $smarty->display('workAreaSimple.tpl'); + exit(); +} +doDBConnect($db, database::ONERROREXIT); + +$message = ''; +if (! is_null($args->doEditUser)) { + if (strcmp($args->password, $args->password2)) { + $message = lang_get('passwd_dont_match'); + } else { + $user = new tlUser(); + $rx = $user->checkPasswordQuality($args->password); + if ($rx['status_ok'] >= tl::OK) { + $result = $user->setPassword($args->password); + if ($result >= tl::OK) { + $user->login = $args->login; + $user->emailAddress = $args->email; + $user->firstName = $args->firstName; + $user->lastName = $args->lastName; + $result = $user->writeToDB($db); + + $cfg = config_get('notifications'); + if ($cfg->userSignUp->enabled) { + notifyGlobalAdmins($db, $user); + } + logAuditEvent(TLS("audit_users_self_signup", $args->login), + "CREATE", $user->dbID, "users"); + + $url2go = "login.php?note=first"; + redirect(TL_BASE_HREF . $url2go); + exit(); + } else { + $message = getUserErrorMessage($result); + } + } else { + $message = $rx['msg']; + } + } +} + +$smarty = new TLSmarty(); + +// we get info about THE DEFAULT AUTHENTICATION METHOD +$gui->external_password_mgmt = tlUser::isPasswordMgtExternal(); +$gui->message = $message; +$smarty->assign('gui', $gui); + +$tpl = str_replace('.php', '.tpl', basename($_SERVER['SCRIPT_NAME'])); +if ($args->viewer == 'new') { + $tpl = 'firstLogin-model-marcobiedermann.tpl'; +} +$tpl = 'login/' . $tpl; +$smarty->display($tpl); + +/** + * Get input from user and return it in some sort of namespace + * + * @return stdClass + */ +function init_args() +{ + $args = new stdClass(); + $args->pwdInputSize = config_get('loginPagePasswordSize'); + + $iParams = array( + "doEditUser" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + 1 + ), + "login" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + 30 + ), + "password" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + $args->pwdInputSize + ), + "password2" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + $args->pwdInputSize + ), + "firstName" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + 30 + ), + "lastName" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + 30 + ), + "email" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + 100 + ), + "viewer" => array( + 'GET', + tlInputParameter::STRING_N, + 0, + 3 + ) + ); + I_PARAMS($iParams, $args); + + return $args; +} + +/** + * send mail to administrators (users that have default role = administrator) + * to warn about new user created. + * + * @param database $dbHandler + * @param tlUser $userObj + */ +function notifyGlobalAdmins(&$dbHandler, &$userObj) +{ + // Get email addresses for all users that have default role = administrator + $cfg = config_get('notifications'); + if (! is_null($cfg->userSignUp->to->roles)) { + $opt = array( + 'active' => 1 + ); + foreach ($cfg->userSignUp->to->roles as $roleID) { + $roleMgr = new tlRole($roleID); + $userSet = $roleMgr->getUsersWithGlobalRole($dbHandler, $opt); + $key2loop = array_keys($userSet); + foreach ($key2loop as $userID) { + if (! isset($mail['to'][$userID])) { + $mail['to'][$userID] = $userSet[$userID]->emailAddress; + } + } + } + } + if (! is_null($cfg->userSignUp->to->users)) { + // Brute force query + $tables = tlObject::getDBTables('users'); + $sql = " SELECT id,email FROM {$tables['users']} " . " WHERE login IN('" . + implode("','", $cfg->userSignUp->to->users) . "')"; + echo '
' . __LINE__; + $userSet = $dbHandler->fetchRowsIntoMap($sql, 'id'); + if (! is_null($userSet)) { + foreach ($userSet as $userID => $elem) { + if (! isset($mail['to'][$userID])) { + $mail['to'][$userID] = $elem['email']; + } + } + } + } + + if ($mail['to'] != '') { + $dest = []; + $validator = new Zend_Validate_EmailAddress(); + foreach ($mail['to'] as $mm) { + $ema = trim($mm); + if ($ema == '' || ! $validator->isValid($ema)) { + continue; + } + $dest[] = $ema; + } + + // silence errors + if (! empty($dest)) { + $mail['to'] = implode(',', $dest); // email_api uses ',' as list separator + $mail['subject'] = lang_get('new_account'); + $mail['body'] = lang_get('new_account') . "\n"; + $mail['body'] .= " user:$userObj->login\n"; + $mail['body'] .= " first name:$userObj->firstName surname:$userObj->lastName\n"; + $mail['body'] .= " email:{$userObj->emailAddress}\n"; + @email_send(config_get('from_email'), $mail['to'], $mail['subject'], + $mail['body']); + } + } } -doDBConnect($db,database::ONERROREXIT); - -$message = ''; //lang_get('your_info_please'); -if( !is_null($args->doEditUser) ) { - if(strcmp($args->password,$args->password2)) { - $message = lang_get('passwd_dont_match'); - } else { - $user = new tlUser(); - $rx = $user->checkPasswordQuality($args->password); - if( $rx['status_ok'] >= tl::OK ) { - $result = $user->setPassword($args->password); - if ($result >= tl::OK) { - $user->login = $args->login; - $user->emailAddress = $args->email; - $user->firstName = $args->firstName; - $user->lastName = $args->lastName; - $result = $user->writeToDB($db); - - $cfg = config_get('notifications'); - if($cfg->userSignUp->enabled) { - notifyGlobalAdmins($db,$user); - } - logAuditEvent(TLS("audit_users_self_signup",$args->login),"CREATE",$user->dbID,"users"); - - $url2go = "login.php?viewer={$gui->viewer}¬e=first"; - redirect(TL_BASE_HREF . $url2go); - exit(); - } else { - $message = getUserErrorMessage($result); - } - } else { - $message = $rx['msg']; - } - } -} - -$smarty = new TLSmarty(); - -// we get info about THE DEFAULT AUTHENTICATION METHOD -$gui->external_password_mgmt = tlUser::isPasswordMgtExternal(); -$gui->message = $message; -$smarty->assign('gui',$gui); - -$tpl = str_replace('.php','.tpl',basename($_SERVER['SCRIPT_NAME'])); -if( $args->viewer == 'new' ) { - $tpl='firstLogin-model-marcobiedermann.tpl'; -} -$tpl = 'login/' . $tpl; -$smarty->display($tpl); - - -/** - * get input from user and return it in some sort of namespace - * - */ -function init_args() -{ - $args = new stdClass(); - $args->pwdInputSize = config_get('loginPagePasswordSize'); - - $iParams = array("doEditUser" => array('POST',tlInputParameter::STRING_N,0,1), - "login" => array('POST',tlInputParameter::STRING_N,0,30), - "password" => array('POST',tlInputParameter::STRING_N,0,$args->pwdInputSize), - "password2" => array('POST',tlInputParameter::STRING_N,0,$args->pwdInputSize), - "firstName" => array('POST',tlInputParameter::STRING_N,0,30), - "lastName" => array('POST',tlInputParameter::STRING_N,0,30), - "email" => array('POST',tlInputParameter::STRING_N,0,100), - "viewer" => array('GET',tlInputParameter::STRING_N, 0, 3), - ); - I_PARAMS($iParams,$args); - - return $args; -} - -/** - * send mail to administrators (users that have default role = administrator) - * to warn about new user created. - * - */ -function notifyGlobalAdmins(&$dbHandler,&$userObj) -{ - // Get email addresses for all users that have default role = administrator - - $cfg = config_get('notifications'); - if( !is_null($cfg->userSignUp->to->roles) ) - { - $opt = array('active' => 1); - foreach($cfg->userSignUp->to->roles as $roleID) - { - $roleMgr = new tlRole($roleID); - $userSet = $roleMgr->getUsersWithGlobalRole($dbHandler,$opt); - $key2loop = array_keys($userSet); - foreach($key2loop as $userID) - { - if(!isset($mail['to'][$userID])) - { - $mail['to'][$userID] = $userSet[$userID]->emailAddress; - } - } - } - } - if( !is_null($cfg->userSignUp->to->users) ) - { - // Brute force query - $tables = tlObject::getDBTables('users'); - $sql = " SELECT id,email FROM {$tables['users']} " . - " WHERE login IN('" . implode("','", $cfg->userSignUp->to->users) . "')"; - $userSet = $dbHandler->fetchRowsIntoMap($sql,'id'); - if(!is_null($userSet)) - { - foreach($userSet as $userID => $elem) - { - if(!isset($mail['to'][$userID])) - { - $mail['to'][$userID] = $elem['email']; - } - } - } - } - - if($mail['to'] != '') - { - $dest = null; - $validator = new Zend_Validate_EmailAddress(); - foreach($mail['to'] as $mm) - { - $ema = trim($mm); - if($ema == '' || !$validator->isValid($ema)) - { - continue; - } - $dest[] = $ema; - } - $mail['to'] = implode(',',$dest); // email_api uses ',' as list separator - $mail['subject'] = lang_get('new_account'); - $mail['body'] = lang_get('new_account') . "\n"; - $mail['body'] .= " user:$userObj->login\n"; - $mail['body'] .= " first name:$userObj->firstName surname:$userObj->lastName\n"; - $mail['body'] .= " email:{$userObj->emailAddress}\n"; - - // silence errors - if(!is_null($dest)) - { - @email_send(config_get('from_email'), $mail['to'], $mail['subject'], $mail['body']); - } - } -} \ No newline at end of file diff --git a/gui/javascript/execSetResults.js b/gui/javascript/execSetResults.js index b7a4f55585..10e55301c7 100644 --- a/gui/javascript/execSetResults.js +++ b/gui/javascript/execSetResults.js @@ -6,7 +6,7 @@ * * @package TestLink * @author syji - * @copyright 2010,2019 TestLink community + * @copyright 2010,2023 TestLink community * @filesource execResult.js * @link http://www.testlink.org * @used-by execSetResult.tpl @@ -17,6 +17,7 @@ * */ function doSubmitForHTML5() { + jQuery("#overlay").show(); // Usefult to block user action when saving exec result jQuery("#hidden-submit-button").click(); } @@ -152,4 +153,4 @@ function checkSubmitForStatusCombo(oid,statusCode2block) { saveStepsPartialExecClicked = false; $("#saveStepsPartialExec").click(function() { saveStepsPartialExecClicked = true; -}); \ No newline at end of file +}); diff --git a/gui/javascript/execTree.js b/gui/javascript/execTree.js index cdd664a07e..79a2be32ba 100644 --- a/gui/javascript/execTree.js +++ b/gui/javascript/execTree.js @@ -38,7 +38,7 @@ function TreePanelState(mytree,cookiePrefix) TreePanelState.prototype.init = function() { - this.cp = new Ext.state.CookieProvider(); + this.cp = new Ext.ux.LocalStorageProvider(); // this.state = this.cp.get('TLExecTreePanelState_' + this.mytree.id, new Array() ); this.state = this.cp.get(this.cookiePrefix + this.mytree.id, new Array() ); diff --git a/gui/javascript/execTreeWithMenu.js b/gui/javascript/execTreeWithMenu.js index af25b8e9df..6756a409e7 100644 --- a/gui/javascript/execTreeWithMenu.js +++ b/gui/javascript/execTreeWithMenu.js @@ -39,7 +39,7 @@ function TreePanelState(mytree,cookiePrefix) TreePanelState.prototype.init = function() { - this.cp = new Ext.state.CookieProvider(); + this.cp = new Ext.ux.LocalStorageProvider(); // this.state = this.cp.get('TLExecTreePanelState_' + this.mytree.id, new Array() ); this.state = this.cp.get(this.cookiePrefix + this.mytree.id, new Array() ); diff --git a/gui/javascript/tcTree.js b/gui/javascript/tcTree.js index ccce7d2d82..6562917a28 100644 --- a/gui/javascript/tcTree.js +++ b/gui/javascript/tcTree.js @@ -74,7 +74,7 @@ function TreePanelState(mytree,cookiePrefix) TreePanelState.prototype.init = function() { - this.cp = new Ext.state.CookieProvider(); + this.cp = new Ext.ux.LocalStorageProvider(); // this.state = this.cp.get('TreePanelState_' + this.mytree.id, new Array() ); this.state = this.cp.get(this.cookiePrefix + this.mytree.id, new Array() ); } diff --git a/gui/javascript/tcase_utils.js b/gui/javascript/tcase_utils.js index ae2ae0cc1c..4b63666f99 100644 --- a/gui/javascript/tcase_utils.js +++ b/gui/javascript/tcase_utils.js @@ -39,7 +39,7 @@ * * returns: - */ -function checkTCaseDuplicateName(tcase_id,tcase_name,tsuite_id,warningOID) +function checkTCaseDuplicateName(tcase_id,tcase_name,tsuite_id,tproject_id,warningOID) { Ext.Ajax.request({ url: fRoot+'lib/ajax/checkTCaseDuplicateName.php', @@ -47,7 +47,9 @@ function checkTCaseDuplicateName(tcase_id,tcase_name,tsuite_id,warningOID) params: { name: tcase_name, testcase_id: tcase_id, - testsuite_id: tsuite_id + testsuite_id: tsuite_id, + testproject_id: tproject_id, + }, success: function(result, request) { var obj = Ext.util.JSON.decode(result.responseText); diff --git a/gui/javascript/testlink_library.js b/gui/javascript/testlink_library.js index 1e83f8e6c9..6995dbf6e3 100644 --- a/gui/javascript/testlink_library.js +++ b/gui/javascript/testlink_library.js @@ -1967,3 +1967,19 @@ alert(page); // second parameter(window name) with spaces caused bug on IE window.open(fRoot+feature_url,"Keywords",windowCfg); } + + +/** + * + */ +function showHideByDataEntity(target) +{ + var tg = '[data-entity="' + target + '"]'; + var you = $(tg); + if (you.css('display') == 'block') { + you.hide(); + } else { + you.show(); + } +} + diff --git a/gui/javascript/treebyloader.js b/gui/javascript/treebyloader.js index 3a9b069664..4b75f2719a 100644 --- a/gui/javascript/treebyloader.js +++ b/gui/javascript/treebyloader.js @@ -199,7 +199,7 @@ function TreePanelState(mytree,cookiePrefix) TreePanelState.prototype.init = function() { - this.cp = new Ext.state.CookieProvider(); + this.cp = new Ext.ux.LocalStorageProvider(); this.state = this.cp.get(this.cookiePrefix + this.mytree.id, new Array() ); } diff --git a/gui/templates/conf/input_dimensions.conf b/gui/templates/conf/input_dimensions.conf index 334e842c75..86bb087ba4 100644 --- a/gui/templates/conf/input_dimensions.conf +++ b/gui/templates/conf/input_dimensions.conf @@ -50,6 +50,8 @@ item_view_table="table table-bordered" item_view_thead="thead-dark" pagination_length=20 + +# jQuery DataTables NOT_SORTABLE='data-orderable="false" ' [attachmentupload] @@ -261,8 +263,8 @@ STEP_NUMBER_SIZE=1 STEP_NUMBER_MAXLEN=2 [reqSearchForm] -REQDOCID_SIZE=20 -REQDOCID_MAXLEN=30 +REQDOCID_SIZE=35 +REQDOCID_MAXLEN=64 VERSION_SIZE=3 VERSION_MAXLEN=3 REQNAME_SIZE=35 @@ -275,8 +277,8 @@ CFVALUE_SIZE=20 CFVALUE_MAXLEN=20 [reqSpecSearchForm] -REQSPECDOCID_SIZE=20 -REQSPECDOCID_MAXLEN=30 +REQSPECDOCID_SIZE=35 +REQSPECDOCID_MAXLEN=64 VERSION_SIZE=3 VERSION_MAXLEN=3 REQSPECNAME_SIZE=35 @@ -291,8 +293,8 @@ CFVALUE_MAXLEN=20 [treeFilterForm] TC_TITLE_SIZE=32 TC_TITLE_MAXLEN=32 -REQ_DOCID_SIZE=20 -REQ_DOCID_MAXLEN=30 +REQ_DOCID_SIZE=35 +REQ_DOCID_MAXLEN=64 REQ_NAME_SIZE=35 REQ_NAME_MAXLEN=50 COVERAGE_SIZE=3 diff --git a/gui/templates/dashio/login/login-model-marcobiedermann.tpl b/gui/templates/dashio/login/login-model-marcobiedermann.tpl index b84be92190..7bff19323a 100644 --- a/gui/templates/dashio/login/login-model-marcobiedermann.tpl +++ b/gui/templates/dashio/login/login-model-marcobiedermann.tpl @@ -49,6 +49,7 @@ {/if} + {if ! $gui->authCfg['sso_only']}
@@ -62,6 +63,7 @@
+ {/if} {foreach from=$gui->oauth item=oauth_item}
diff --git a/gui/templates/tl-classic/DataTables.inc.tpl b/gui/templates/tl-classic/DataTables.inc.tpl index f3933c67fa..73f34e1395 100644 --- a/gui/templates/tl-classic/DataTables.inc.tpl +++ b/gui/templates/tl-classic/DataTables.inc.tpl @@ -3,16 +3,27 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ @filesource DataTables.inc.tpl Purpose: smarty template -@internal revisions -@since 1.9.17 + +Template Parameters + DataTablesSelector: + DataTablesLengthMenu: + +Global Coupling + NONE *} - - - - \ No newline at end of file + + + +{if $DataTablesSelector != ''} + {* + To avoid issues due to do initialization multiple times + See projectView.tpl. + *} + +{/if} \ No newline at end of file diff --git a/gui/templates/tl-classic/DataTablesColumnFiltering.inc.tpl b/gui/templates/tl-classic/DataTablesColumnFiltering.inc.tpl new file mode 100644 index 0000000000..a8fb51d1cc --- /dev/null +++ b/gui/templates/tl-classic/DataTablesColumnFiltering.inc.tpl @@ -0,0 +1,83 @@ +{* +TestLink Open Source Project - http://testlink.sourceforge.net/ +@filesource DataTablesColumnFiltering.inc.tpl +@param DataTablesSelector +@param DataTablesLengthMenu + +@see https://datatables.net/extensions/fixedheader/examples/options/columnFiltering.html + + +@since 1.9.20 +*} + diff --git a/gui/templates/tl-classic/cfields/cfieldsTprojectAssign.tpl b/gui/templates/tl-classic/cfields/cfieldsTprojectAssign.tpl index 0ed3cfdfe0..a3ae294858 100644 --- a/gui/templates/tl-classic/cfields/cfieldsTprojectAssign.tpl +++ b/gui/templates/tl-classic/cfields/cfieldsTprojectAssign.tpl @@ -67,7 +67,8 @@ Purpose: management Custom fields assignment to a test project {* location will NOT apply to EXEC only CF *} - {if $cf.node_description == 'testcase' && $cf.enable_on_execution ==0} + {if $cf.node_description == 'testcase' + && $cf.enable_on_design == 1} diff --git a/gui/templates/tl-classic/execute/editExecution.tpl b/gui/templates/tl-classic/execute/editExecution.tpl index 3a1586caf1..ccf19675cb 100644 --- a/gui/templates/tl-classic/execute/editExecution.tpl +++ b/gui/templates/tl-classic/execute/editExecution.tpl @@ -53,5 +53,14 @@ var {$gui->dialogName} = new std_dialog();
+ + + \ No newline at end of file diff --git a/gui/templates/tl-classic/execute/execDashboard.tpl b/gui/templates/tl-classic/execute/execDashboard.tpl index 1a94d2e837..bdb701523e 100644 --- a/gui/templates/tl-classic/execute/execDashboard.tpl +++ b/gui/templates/tl-classic/execute/execDashboard.tpl @@ -39,7 +39,7 @@ TestLink Open Source Project - http://testlink.sourceforge.net/
{$labels.testplan} {$gui->testplan_name|escape}
-
+
{if $gui->testPlanEditorType == 'none'}{$gui->testplan_notes|nl2br}{else}{$gui->testplan_notes}{/if} {if $gui->testplan_cfields neq ''}
{$gui->testplan_cfields}
{/if}
@@ -48,7 +48,7 @@ TestLink Open Source Project - http://testlink.sourceforge.net/
{$labels.platform} {$gui->platform_info.name|escape}
-
+
{if $gui->platformEditorType == 'none'}{$gui->platform_info.notes|nl2br}{else}{$gui->platform_info.notes}{/if}
{/if} @@ -56,7 +56,7 @@ TestLink Open Source Project - http://testlink.sourceforge.net/
{$labels.build} {$gui->build_name|escape}
-
+
{if $gui->buildEditorType == 'none'}{$gui->build_notes|nl2br}{else}{$gui->build_notes}{/if} {if $gui->build_cfields != ''}
{$gui->build_cfields}
{/if}
diff --git a/gui/templates/tl-classic/execute/execNavigator.tpl b/gui/templates/tl-classic/execute/execNavigator.tpl index a50ea05381..9181b810b9 100644 --- a/gui/templates/tl-classic/execute/execNavigator.tpl +++ b/gui/templates/tl-classic/execute/execNavigator.tpl @@ -27,8 +27,11 @@ treeCfg = { tree_div_id:'tree_div',root_name:"",root_id:0,root_href:"", loader:"", enableDD:false, dragDropBackEndUrl:'',children:"" }; Ext.onReady(function() { - Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); - +/* #3714 +Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); +*/ +Ext.state.Manager.setProvider(new Ext.ux.LocalStorageProvider()); + // Use a collapsible panel for filter settings // and place a help icon in ther header var settingsPanel = new Ext.ux.CollapsiblePanel({ diff --git a/gui/templates/tl-classic/execute/execSetResults.tpl b/gui/templates/tl-classic/execute/execSetResults.tpl index 101db7b979..1364f01fe8 100644 --- a/gui/templates/tl-classic/execute/execSetResults.tpl +++ b/gui/templates/tl-classic/execute/execSetResults.tpl @@ -123,7 +123,13 @@ updateLinkToLatestTCVersion, version, warning, warning_delete_execution, -warning_nothing_will_be_saved,file_upload_ko,pleaseOpenTSuite'} +warning_nothing_will_be_saved, +file_upload_ko, +file_upload_tclevel_ok, +file_upload_steplevel_ok, +file_upload_steplevel_ko, +pleaseOpenTSuite, +pleaseWait'} {$cfg_section=$smarty.template|basename|replace:".tpl":""} @@ -132,6 +138,41 @@ warning_nothing_will_be_saved,file_upload_ko,pleaseOpenTSuite'} {$exportAction="lib/execute/execExport.php?tplan_id="} {include file="inc_head.tpl" popup='yes' openHead='yes' jsValidate="yes" editorType=$gui->editorType} + + + + @@ -196,35 +237,6 @@ function validateForm(f) { return status_ok; } - - - - - -function OLDvalidateForm(f) -{ - var status_ok=true; - var cfields_inputs=''; - var cfValidityChecks; - var cfield_container; - var access_key; - cfield_container=document.getElementById('save_button_clicked').value; - access_key='cfields_exec_time_tcversionid_'+cfield_container; - - if( document.getElementById(access_key) != null ) - { - cfields_inputs = document.getElementById(access_key).getElementsByTagName('input'); - cfValidityChecks=validateCustomFields(cfields_inputs); - if( !cfValidityChecks.status_ok ) - { - var warning_msg=cfMessages[cfValidityChecks.msg_id]; - alert_message(alert_box_title,warning_msg.replace(/%s/, cfValidityChecks.cfield_label)); - return false; - } - } - return true; -} - /* function: checkSubmitForStatusCombo $statusCode has been checked, then false is returned to block form submit(). @@ -298,6 +310,8 @@ function jsCallDeleteFile(btn, text, o_id) { {include file="bootstrap.inc.tpl"} + + {* IMPORTANT: if you change value, you need to chang init_args() logic on execSetResults.php @@ -317,7 +331,7 @@ IMPORTANT: if you change value, you need to chang init_args() logic on execSetRe '{$tsd_val_for_hidden_list}'); {/if} - {if $round_enabled}Nifty('div.exec_additional_info');{/if} + {if $round_enabled}Nifty('div.execAdditionalInfo');{/if} {if #ROUND_TC_SPEC#}Nifty('div.exec_test_spec');{/if} {if #ROUND_EXEC_HISTORY#}Nifty('div.exec_history');{/if} {if #ROUND_TC_TITLE#}Nifty('div.exec_tc_title');{/if}" @@ -326,21 +340,21 @@ IMPORTANT: if you change value, you need to chang init_args() logic on execSetRe {if $gui->uploadOp != null } {/if} + +
+
{$labels.pleaseWait} +
+

{$labels.title_t_r_on_build} {$gui->build_name|escape} @@ -375,6 +394,7 @@ IMPORTANT: if you change value, you need to chang init_args() logic on execSetRe {/if}
+ {if $gui->user_feedback != ''}
{$gui->user_feedback}
{/if} @@ -387,184 +407,187 @@ IMPORTANT: if you change value, you need to chang init_args() logic on execSetRe {/if} -
- - - - - - - - - {$bulkExec = $cfg->exec_cfg->show_testsuite_contents && - $gui->can_use_bulk_op } - {$singleExec = !$bulkExec} - - {if $singleExec} -
- - - {$tlImages.toggle_direct_link}   - - - - - - - {if $gui->grants->execute} - - - {/if} - - {if $tlCfg->exec_cfg->enable_test_automation} - - {/if} - - {if $gui->hasNewestVersion && 1==0} - - - {/if} + + + + + + + + + + {$bulkExec = $cfg->exec_cfg->show_testsuite_contents && + $gui->can_use_bulk_op } + {$singleExec = !$bulkExec} + + {if $singleExec} +
+ + + {$tlImages.toggle_direct_link}   + + + + + + {if $gui->grants->execute} + + + {/if} + + {if $tlCfg->exec_cfg->enable_test_automation} + + {/if} -
- {/if} + {if $gui->hasNewestVersion && 1==0} + + + {/if} - {if $gui->plugins.EVENT_TESTRUN_DISPLAY} -
- {foreach from=$gui->plugins.EVENT_TESTRUN_DISPLAY item=testrun_item} - {$testrun_item} -
- {/foreach} -
- {/if} +
+ {/if} - {* ------------------------------------ *} - {* Test Plan notes show/hide management *} - {* ------------------------------------ *} - {$div_id='tplan_notes'} - {$memstatus_id=$tplan_notes_view_memory_id} - {include file="inc_show_hide_mgmt.tpl" - show_hide_container_title=$gui->testplan_div_title - show_hide_container_id=$div_id - show_hide_container_draw=false - show_hide_container_class='exec_additional_info' - show_hide_container_view_status_id=$memstatus_id} - -
- {if $gui->testPlanEditorType == 'none'}{$gui->testplan_notes|nl2br}{else}{$gui->testplan_notes}{/if} - {if $gui->testplan_cfields neq ''}
{$gui->testplan_cfields}
{/if} -
- {* ---------------------------------------------------------------- *} + {if $gui->plugins.EVENT_TESTRUN_DISPLAY} +
+ {foreach from=$gui->plugins.EVENT_TESTRUN_DISPLAY item=testrun_item} + {$testrun_item} +
+ {/foreach} +
+ {/if} - {* ---------------------------------------------------------------- *} - {* Platforms notes show/hide management *} - {* ---------------------------------------------------------------- *} - {if $gui->platform_info.id > 0} - {$div_id='platform_notes'} - {$memstatus_id=$platform_notes_view_memory_id} - {if $gui->platformEditorType == 'none'}{$content=$gui->platform_info.notes|nl2br}{else}{$content=$gui->platform_info.notes}{/if} + {* ------------------------------------ *} + {* Test Plan notes show/hide management *} + {* ------------------------------------ *} + {$div_id='tplan_notes'} + {$memstatus_id=$tplan_notes_view_memory_id} + {include file="inc_show_hide_mgmt.tpl" + show_hide_container_title=$gui->testplan_div_title + show_hide_container_id=$div_id + show_hide_container_draw=false + show_hide_container_class='execAdditionalInfo' + show_hide_container_view_status_id=$memstatus_id} +
+ {if $gui->testPlanEditorType == 'none'}{$gui->testplan_notes|nl2br}{else}{$gui->testplan_notes}{/if} + {if $gui->testplan_cfields neq ''}
{$gui->testplan_cfields}
{/if} +
+ {* ------------------------------------------------------------ *} + + {* ------------------------------------------------------------- *} + {* Platforms notes show/hide management *} + {* ------------------------------------------------------------- *} + {if $gui->platform_info.id > 0} + {$div_id='platform_notes'} + {$memstatus_id=$platform_notes_view_memory_id} + {if $gui->platformEditorType == 'none'}{$content=$gui->platform_info.notes|nl2br}{else}{$content=$gui->platform_info.notes}{/if} + + {include file="inc_show_hide_mgmt.tpl" + show_hide_container_title=$gui->platform_div_title + show_hide_container_id=$div_id + show_hide_container_view_status_id=$memstatus_id + show_hide_container_draw=true + show_hide_container_class='execAdditionalInfo' + show_hide_container_html=$content} + {/if} + {* ------------------------------------------------------- *} + + {* ------------------------------------------------------- *} + {* Build notes show/hide management *} + {* ------------------------------------------------------- *} + {$div_id='build_notes'} + {$memstatus_id=$build_notes_view_memory_id} {include file="inc_show_hide_mgmt.tpl" - show_hide_container_title=$gui->platform_div_title + show_hide_container_title=$gui->build_div_title show_hide_container_id=$div_id show_hide_container_view_status_id=$memstatus_id - show_hide_container_draw=true - show_hide_container_class='exec_additional_info' - show_hide_container_html=$content} - {/if} - {* ------------------------------------------------------- *} - - {* ------------------------------------------------------- *} - {* Build notes show/hide management *} - {* ------------------------------------------------------- *} - {$div_id='build_notes'} - {$memstatus_id=$build_notes_view_memory_id} - {include file="inc_show_hide_mgmt.tpl" - show_hide_container_title=$gui->build_div_title - show_hide_container_id=$div_id - show_hide_container_view_status_id=$memstatus_id - show_hide_container_draw=false - show_hide_container_class='exec_additional_info'} - -
- {if $gui->buildEditorType == 'none'}{$gui->build_notes|nl2br}{else}{$gui->build_notes}{/if} - {if $gui->build_cfields != ''}
{$gui->build_cfields}
{/if} -
+ show_hide_container_draw=false + show_hide_container_class='execAdditionalInfo'} + +
+ {if $gui->buildEditorType == 'none'}{$gui->build_notes|nl2br}{else}{$gui->build_notes}{/if} + {if $gui->build_cfields != ''}
{$gui->build_cfields}
{/if} +
- {* ------------------------------------------------------- *} - {if $gui->map_last_exec eq ""} -
{$labels.no_data_available}
- {else} + {* ------------------------------------------------------- *} + {if $gui->map_last_exec eq ""} +
{$labels.no_data_available}
+ {else} {if $gui->grants->execute == 1 and $gui->build_is_open == 1} - {$input_enabled_disabled=""} - {$att_download_only=false} - {$enable_custom_fields=true} - {$draw_submit_button=true} - - {if $bulkExec} - {$div_id='bulk_controls'} - {$memstatus_id="$bulk_controls_view_memory_id"} - {include file="inc_show_hide_mgmt.tpl" - show_hide_container_title=$labels.bulk_tc_status_management - show_hide_container_id=$div_id - show_hide_container_draw=false - show_hide_container_class='exec_additional_info' - show_hide_container_view_status_id=$memstatus_id} - -
- {include file="execute/{$tplConfig.inc_exec_controls}" - args_save_type='bulk' - args_input_enable_mgmt=$input_enabled_disabled - args_tcversion_id='bulk' - args_webeditor=$gui->bulk_exec_notes_editor - args_execution_time_cfields=$gui->execution_time_cfields - args_draw_save_and_exit=$gui->draw_save_and_exit - args_labels=$labels} -
- {/if} - {/if} - {/if} + {$input_enabled_disabled=""} + {$att_download_only=false} + {$enable_custom_fields=true} + {$draw_submit_button=true} + + {if $bulkExec} + {$div_id='bulk_controls'} + {$memstatus_id="$bulk_controls_view_memory_id"} + {include file="inc_show_hide_mgmt.tpl" + show_hide_container_title=$labels.bulk_tc_status_management + show_hide_container_id=$div_id + show_hide_container_draw=false + show_hide_container_class='execAdditionalInfo' + show_hide_container_view_status_id=$memstatus_id} + +
+ {include file="execute/{$tplConfig.inc_exec_controls}" + args_save_type='bulk' + args_input_enable_mgmt=$input_enabled_disabled + args_tcversion_id='bulk' + args_webeditor=$gui->bulk_exec_notes_editor + args_execution_time_cfields=$gui->execution_time_cfields + args_draw_save_and_exit=$gui->draw_save_and_exit + args_labels=$labels} +
+ {/if} + {/if} + {/if} - {if $bulkExec} - {include file="execute/execSetResultsBulk.inc.tpl"} - {/if} + {if $bulkExec} + {include file="execute/execSetResultsBulk.inc.tpl"} + {/if} - {if $singleExec} - {if $tlCfg->exec_cfg->enable_test_automation && - $gui->remoteExecFeedback != ''} - {include file="execute/execSetResultsRemoteExec.inc.tpl"} - {/if} + {if $singleExec} + {if $tlCfg->exec_cfg->enable_test_automation && + $gui->remoteExecFeedback != ''} + {include file="execute/execSetResultsRemoteExec.inc.tpl"} + {/if} - {include file="execute/inc_exec_show_tc_exec.tpl"} - {if isset($gui->refreshTree) && $gui->refreshTree} - {include file="inc_refreshTreeWithFilters.tpl"} + {include file="execute/inc_exec_show_tc_exec.tpl"} + {if isset($gui->refreshTree) && $gui->refreshTree} + {include file="inc_refreshTreeWithFilters.tpl"} + {/if} {/if} - {/if} - -
+ +
- + \ No newline at end of file diff --git a/gui/templates/tl-classic/execute/execTCaseHeader.inc.tpl b/gui/templates/tl-classic/execute/execTCaseHeader.inc.tpl new file mode 100644 index 0000000000..180d0862ba --- /dev/null +++ b/gui/templates/tl-classic/execute/execTCaseHeader.inc.tpl @@ -0,0 +1,70 @@ +{* +TestLink Open Source Project - http://testlink.sourceforge.net/ +@filesource execTCaseHeader.inc.tpl +*} + + {$theClass = "exec_tc_title"} + {$hasNewestVersionMsg = ''} + {if $gui->hasNewestVersion} + {$theClass = "exec_tc_title_alert"} + {$hasNewestVersionMsg = $labels.hasNewestVersionMsg} + {/if} +
+ {if '' !== $hasNewestVersionMsg} +
{$hasNewestVersionMsg}
+ {if $gui->hasNewestVersion} +
+ + +
+
+ {/if} + {/if} + {if $gui->grants->edit_testcase} + {$tplan=$gui->tplan_id} + {$metaMode="editOnExec&tplan_id=$tplan"} + + + + {/if} + + {$labels.title_test_case} {$gui->tcasePrefix|escape}{$cfg->testcase_cfg->glue_character}{$tc_exec.tc_external_id|escape} :: {$labels.version}: {$tc_exec.version} :: {$tc_exec.name|escape} +
+ + {$cfdtime = $gui->design_time_cfields} + {$cfdtime = $cfdtime[$tc_exec.testcase_id]} + {if $cfdtime.after_title neq ''} +
+
+ {$cfdtime.after_title} +
+
+ {/if} + +
+
+ {$labels.estimated_execution_duration}{$smarty.const.TITLE_SEP} + {$tc_exec.estimated_exec_duration} +
+ +
+ {$labels.execution_type}{$smarty.const.TITLE_SEP} + {$gui->execution_types[$tc_exec.execution_type]} +
+ + +
+
+ {if $tc_exec.assigned_user == ''} +  {$labels.has_no_assignment} + {else} +   + {$labels.assigned_to}{$title_sep}{$tc_exec.assigned_user|escape} + {/if} +
+ + + +
diff --git a/gui/templates/tl-classic/execute/exec_tc_relations.inc.tpl b/gui/templates/tl-classic/execute/exec_tc_relations.inc.tpl index 3034f4b31c..4db70be950 100644 --- a/gui/templates/tl-classic/execute/exec_tc_relations.inc.tpl +++ b/gui/templates/tl-classic/execute/exec_tc_relations.inc.tpl @@ -15,26 +15,30 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ {$rel_labels.relation_id} / {$rel_labels.relation_type_extended} {$rel_labels.test_case} - - {foreach item=rx from=$argsRelSet.relations} - {$rel_status=$rx.related_item.status} - - {$rx.id} / {$rx.type_localized|escape} - - - - - - {$rx.related_tcase.fullExternalID|escape}: - {$rx.related_tcase.name|escape} - - - {/foreach} + + {if $argsRelSet.relations != ''} + {foreach item=rx from=$argsRelSet.relations} + {if $rx != ''} + {$rel_status=$rx.related_item.status} + + {$rx.id} / {$rx.type_localized|escape} + + + + + + {$rx.related_tcase.fullExternalID|escape}: + {$rx.related_tcase.name|escape} + + + {/if} + {/foreach} + {/if} {/if} diff --git a/gui/templates/tl-classic/execute/inc_exec_img_controls.tpl b/gui/templates/tl-classic/execute/inc_exec_img_controls.tpl index 1cf3fb233a..d322bd6bef 100644 --- a/gui/templates/tl-classic/execute/inc_exec_img_controls.tpl +++ b/gui/templates/tl-classic/execute/inc_exec_img_controls.tpl @@ -8,7 +8,7 @@ Purpose: draw execution controls Author : franciscom *} -{* Russian Doll, make name shorter *} +{* Russian Doll, make name shorter *} {$tcvID = $args_tcversion_id} {$ResultsStatusCode=$tlCfg->results.status_code} {if $args_save_type == 'bulk'} @@ -17,228 +17,240 @@ Author : franciscom {$radio_id_prefix = "statusSingle"} {/if} - {if $gui->grants->execute} - - - - + - {if $gui->addIssueOp != '' && !is_null($gui->addIssueOp) && - !is_null($gui->addIssueOp.type) } - {$ak = $gui->addIssueOp.type} -
-
-
{$args_labels.test_exec_notes}
- {$args_webeditor} -
- {include file="attachments_simple.inc.tpl" attach_id=0} -
- {* status of test *} -
- {if $args_save_type == 'bulk'} {$args_labels.test_exec_result} {else}   {/if} -
- -
- {if $args_save_type == 'bulk'} - {foreach key=verbose_status item=locale_status from=$tlCfg->results.status_label_for_exec_ui} - results.default_status} - checked="checked" - {/if} />  {lang_get s=$locale_status}
- {/foreach} - {/if} + {if $gui->grants->execute} + + + + - - {if $args_save_type == 'bulk' && $args_execution_time_cfields != ''} - - {/if} -
+
{$args_labels.test_exec_notes}
+ {$args_webeditor} +
+ +
+ {* status of test *} +
+ {if $args_save_type == 'bulk'} {$args_labels.test_exec_result} {else}   {/if} +
- {if $tlCfg->exec_cfg->features->exec_duration->enabled} -
- - - {/if} - {if $args_save_type == 'single'} -
-
- {$addBR=0} - {if $tc_exec.assigned_user == ''} - - assignTaskChecked} checked {/if}> -   +
+ {if $args_save_type == 'bulk'} + {foreach key=verbose_status item=locale_status from=$tlCfg->results.status_label_for_exec_ui} + results.default_status} + checked="checked" + {/if} />  {lang_get s=$locale_status}
+ {/foreach} {/if} - {if $tlCfg->exec_cfg->exec_mode->new_exec == 'latest'} - {$addBR=1} - - -   - {/if} + {if $tlCfg->exec_cfg->features->exec_duration->enabled} +
+ + + {/if} + {if $args_save_type == 'single'} +
+
+ {$addBR=0} + {if $tc_exec.assigned_user == ''} + + assignTaskChecked} checked {/if}> +   + {/if} + {if $tlCfg->exec_cfg->exec_mode->new_exec == 'latest'} + {$addBR=1} + + +   + {/if} - - {if $gui->tlCanCreateIssue} - {$addBR=1} - - - {/if} - {if $tlCfg->exec_cfg->copyLatestExecIssues->enabled} - {if $addBR}
{/if} - {$args_labels.bug_copy_from_latest_exec}  - exec_cfg->copyLatestExecIssues->default} checked {/if}> -
- {/if} + + {if $gui->tlCanCreateIssue} + {$addBR=1} + + + {/if} - - -
-
- - {foreach key=kode item=ikval from=$gui->execStatusIcons} - {$in = $ikval.img} -   - {/foreach} -
-
+ {if $tlCfg->exec_cfg->copyLatestExecIssues->enabled} + {if $addBR}
{/if} + {$args_labels.bug_copy_from_latest_exec}  + exec_cfg->copyLatestExecIssues->default} checked {/if}> +
+ {/if} - - {foreach key=kode item=ikval from=$gui->execStatusIconsNext} - {$in = $ikval.img} -   - {/foreach} -
-
- - {else} - - {/if} -
-
-
- {$args_labels.testcase_customfields} - {$args_execution_time_cfields.0} {* 0 => bulk *} -
-
- - {else} - - {/if} + + +
+
+ + {foreach key=kode item=ikval from=$gui->execStatusIcons} + {$in = $ikval.img} +   + {/foreach} +
+
+ + {foreach key=kode item=ikval from=$gui->execStatusIconsNext} + {$in = $ikval.img} +   + {/foreach} +
+
+ + {else} + + {/if} +
+
- - + + - - {if $ak == 'createIssue'} + + {if $args_save_type == 'bulk' + && $args_execution_time_cfields != ''} - + - {else} - {foreach key=ik item=ikmsg from=$gui->addIssueOp[$ak]} + {/if} +
{$args_labels.create_issue_feedback}
+ {include file="attachments_simple.inc.tpl" attach_id=0} +
-
{$gui->addIssueOp[$ak].msg}
-
+
+ {$args_labels.testcase_customfields} + {$args_execution_time_cfields.0} {* 0 => bulk *} +
+
+ {else} + + {/if} + + {if $gui->addIssueOp != '' + && !is_null($gui->addIssueOp) + && !is_null($gui->addIssueOp.type) } + {$ak = $gui->addIssueOp.type} +
+ + + + + + {if $ak == 'createIssue'} + + + + {else} + {foreach key=ik item=ikmsg from=$gui->addIssueOp[$ak]} - {/foreach} - {/if} -
{$args_labels.create_issue_feedback}
+
{$gui->addIssueOp[$ak].msg}
+
{$ikmsg.msg}
-
+ {/foreach} {/if} + +
+ {/if} - - - - + + + + - {$itMetaData = $gui->issueTrackerMetaData} - {if '' != $itMetaData && null != $itMetaData} - + {$itMetaData = $gui->issueTrackerMetaData} + {if '' != $itMetaData && null != $itMetaData} + - - {/if} + + {/if} - + - + - + - + - + - + - + -
-
- {$args_labels.exec_not_run_result_note} -
+
+
+ {$args_labels.exec_not_run_result_note} +
+ + {* 20170203 - one-testlink@variant.ch - add localStorage Ext state provider *} + + {/if} {/if} \ No newline at end of file diff --git a/gui/templates/tl-classic/inc_ext_table.tpl b/gui/templates/tl-classic/inc_ext_table.tpl index 41d81ea00b..8e74c7f051 100644 --- a/gui/templates/tl-classic/inc_ext_table.tpl +++ b/gui/templates/tl-classic/inc_ext_table.tpl @@ -28,9 +28,19 @@ var checkedImg = "{$tlImages.checked}"; -{if $gui->bodyOnLoad != ''} +{$bodyOnLoad=''} +{$bodyOnUnload=''} +{if property_exists($gui,'bodyOnLoad')} + {$bodyOnLoad=$gui->bodyOnLoad} +{/if} +{if property_exists($gui,'bodyOnUnload')} + {$bodyOnUnload=$gui->bodyOnUnload} +{/if} + + +{if $bodyOnLoad != ''} + {/if} + - +

{$gui->main_descr|escape}

diff --git a/gui/templates/tl-classic/keywords/keywordsView.tpl b/gui/templates/tl-classic/keywords/keywordsView.tpl index e1bcc8047d..528c86ed8d 100644 --- a/gui/templates/tl-classic/keywords/keywordsView.tpl +++ b/gui/templates/tl-classic/keywords/keywordsView.tpl @@ -25,29 +25,59 @@ var del_action = fRoot+'lib/keywords/keywordsEdit.php'+ '&openByOther={$gui->openByOther}&id='; -{if $gui->bodyOnLoad != ''} +{$bodyOnLoad=''} +{$bodyOnUnload=''} +{if property_exists($gui,'bodyOnLoad')} + {$bodyOnLoad=$gui->bodyOnLoad} +{/if} +{if property_exists($gui,'bodyOnUnload')} + {$bodyOnUnload=$gui->bodyOnUnload} +{/if} + + +{if $bodyOnLoad != ''} + {/if} +{* ------------------------------------------------------------------------------------------------ *} +{* + IMPORTANT DEVELOPMENT NOTICE + Because we are using also DataTablesColumnFiltering + We MUST NOT Initialize the Data Table on DataTables.inc.tpl. + We got this effect with DataTablesOID="" +*} + {* Data Tables Config Area - BEGIN*} + {$gridHTMLID="item_view"} + {* Do not initialize in DataTables.inc.tpl -> DataTablesSelector="" *} + {include file="DataTables.inc.tpl" DataTablesSelector=""} + {include + file="DataTablesColumnFiltering.inc.tpl" + DataTablesSelector="#{$gridHTMLID}" + DataTablesLengthMenu=$tlCfg->gui->{$cfg_section}->pagination->length + } + {* Data Tables Config Area - End*} + -{include file="bootstrap.inc.tpl"} +{* ------------------------------------------------------------------------------------------------ *} + +{include file="bootstrap.inc.tpl"} - + +

{$labels.menu_manage_keywords}

{if $gui->keywords != ''} - +
- - + + {if $gui->canManage != ""} - + {/if} diff --git a/gui/templates/tl-classic/login/login-model-marcobiedermann.tpl b/gui/templates/tl-classic/login/login-model-marcobiedermann.tpl index b84be92190..7bff19323a 100644 --- a/gui/templates/tl-classic/login/login-model-marcobiedermann.tpl +++ b/gui/templates/tl-classic/login/login-model-marcobiedermann.tpl @@ -49,6 +49,7 @@ {/if} + {if ! $gui->authCfg['sso_only']}
@@ -62,6 +63,7 @@
+ {/if} {foreach from=$gui->oauth item=oauth_item}
diff --git a/gui/templates/tl-classic/main.tpl b/gui/templates/tl-classic/main.tpl index 5d5657268c..1648f10c30 100644 --- a/gui/templates/tl-classic/main.tpl +++ b/gui/templates/tl-classic/main.tpl @@ -21,8 +21,8 @@ - - + + diff --git a/gui/templates/tl-classic/mainPageLeft.tpl b/gui/templates/tl-classic/mainPageLeft.tpl index fae03f8846..813acc988a 100644 --- a/gui/templates/tl-classic/mainPageLeft.tpl +++ b/gui/templates/tl-classic/mainPageLeft.tpl @@ -151,7 +151,8 @@ {if $display_left_block_3}
- {if $gui->grants.reqs_view == "yes" || $gui->grants.reqs_edit == "yes" } + {if $gui->grants.reqs_view == "yes" + || $gui->grants.reqs_edit == "yes" } {$labels.href_req_spec} {$labels.href_req_overview} {$labels.href_print_req} diff --git a/gui/templates/tl-classic/mainPageRight.tpl b/gui/templates/tl-classic/mainPageRight.tpl index 15992070dd..6b52cb768c 100644 --- a/gui/templates/tl-classic/mainPageRight.tpl +++ b/gui/templates/tl-classic/mainPageRight.tpl @@ -22,6 +22,8 @@ {$mileView="lib/plan/planMilestonesView.php"} {$platformAssign="lib/platforms/platformsAssign.php?tplan_id="} +{$usersAssign="lib/usermanagement/usersAssign.php?featureType=testplan&featureID="} + {$menuLayout=$tlCfg->gui->layoutMainPageRight} {$display_right_block_1=false} {$display_right_block_2=false} @@ -119,6 +121,10 @@ {if $gui->grants.testplan_milestone_overview == "yes" and $gui->countPlans > 0} {$labels.href_plan_mstones} {/if} + + {if $gui->grants.testplan_user_role_assignment == "yes"} + {$labels.href_assign_user_roles} + {/if}
{/if} diff --git a/gui/templates/tl-classic/navBar.tpl b/gui/templates/tl-classic/navBar.tpl index 87ea2bc372..b5d5dc2985 100644 --- a/gui/templates/tl-classic/navBar.tpl +++ b/gui/templates/tl-classic/navBar.tpl @@ -96,8 +96,16 @@ title bar + menu {/if} {if $gui->updateMainPage == 1} + {$wtg1 = "{$basehref}lib/general/mainPage.php"} + {$wtg2 = "{$basehref}index.php"} {/if} diff --git a/gui/templates/tl-classic/plan/buildView.tpl b/gui/templates/tl-classic/plan/buildView.tpl index 64032d0f26..477e78ef68 100644 --- a/gui/templates/tl-classic/plan/buildView.tpl +++ b/gui/templates/tl-classic/plan/buildView.tpl @@ -36,6 +36,27 @@ Purpose: smarty template - Show existing builds var del_action=fRoot+'{$deleteAction}'; + +{* ------------------------------------------------------------------------------------------------ *} +{* + IMPORTANT DEVELOPMENT NOTICE + Because we are using also DataTablesColumnFiltering + We MUST NOT Initialize the Data Table on DataTables.inc.tpl. + We got this effect with DataTablesOID="" +*} + {* Data Tables Config Area - BEGIN*} + {$gridHTMLID="item_view"} + {* Do not initialize in DataTables.inc.tpl -> DataTablesSelector="" *} + {include file="DataTables.inc.tpl" DataTablesSelector=""} + {include + file="DataTablesColumnFiltering.inc.tpl" + DataTablesSelector="#{$gridHTMLID}" + DataTablesLengthMenu=$tlCfg->gui->{$cfg_section}->pagination->length + } + {* Data Tables Config Area - End*} +{* ------------------------------------------------------------------------------------------------ *} + + {include file="bootstrap.inc.tpl"} @@ -65,31 +86,48 @@ var del_action=fRoot+'{$deleteAction}'; {* table id MUST BE item_view to use show/hide API info *} -
{$tlImages.sort_hint}{$labels.th_keyword}{$tlImages.sort_hint}{$labels.th_notes}{$labels.th_keyword}{$labels.th_notes}{$tlImages.sort_hint}{$labels.th_delete}{$labels.th_delete}
+
- - - - - - + + + + + {* Custom Fields *} + {if $gui->cfieldsColumns != null} + {foreach item=cflbl from=$gui->cfieldsColumns} + + {/foreach} + {/if} + + + + {foreach item=build from=$gui->buildSet} - + {* Custom fields *} + {if $gui->cfieldsColumns != null} + {foreach item=cflbl from=$gui->cfieldsColumns} + + {/foreach} + {/if} + + @@ -91,13 +91,13 @@ function manageBuildRelated(checkBoxOid) @@ -110,21 +110,21 @@ function manageBuildRelated(checkBoxOid) {* always copy platform links *}
{$tlImages.toggle_api_info}{$tlImages.sort_hint}{$labels.th_title}{$labels.th_description}{$labels.release_date}{$labels.th_active}{$labels.th_open}{$labels.th_delete}{$tlImages.toggle_api_info}{$labels.th_title}{$labels.th_description}{$labels.release_date}{$cflbl}{$labels.th_active}{$labels.th_open}{$labels.th_delete}
+ {$build.name|escape} {if $gsmarty_gui->show_icon_edit} {$labels.alt_edit_build} {/if} + {if $gui->editorType == 'none'}{$build.notes|nl2br}{else}{$build.notes}{/if} {if $build.release_date != ''}{localize_date d=$build.release_date}{/if}{$build[$cflbl]['value']|escape} {if $build.active==1}
- {$labels.testplan_copy_user_roles} + {$labels.testplan_copy_user_roles}
- {$labels.testplan_copy_attachments} + {$labels.testplan_copy_attachments}
- + {$labels.testplan_copy_tcases}
         - {$labels.testplan_copy_priorities} + {$labels.testplan_copy_priorities}
- + {$labels.testplan_copy_builds}
- {$labels.testplan_copy_milestones} + {$labels.testplan_copy_milestones}
\ No newline at end of file diff --git a/gui/templates/tl-classic/plan/planAddTCNavigator.tpl b/gui/templates/tl-classic/plan/planAddTCNavigator.tpl index d00625c615..989689dea8 100644 --- a/gui/templates/tl-classic/plan/planAddTCNavigator.tpl +++ b/gui/templates/tl-classic/plan/planAddTCNavigator.tpl @@ -23,8 +23,11 @@ Scope: show test specification tree for Test Plan related features enableDD:false, dragDropBackEndUrl:"",children:"" }; Ext.onReady(function() { - Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); - +/* #3714 +Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); +*/ +Ext.state.Manager.setProvider(new Ext.ux.LocalStorageProvider()); + // Use a collapsible panel for filter settings // and place a help icon in ther header var settingsPanel = new Ext.ux.CollapsiblePanel({ diff --git a/gui/templates/tl-classic/plan/planAddTC_m1.tpl b/gui/templates/tl-classic/plan/planAddTC_m1.tpl index fbeca0830c..47c9972be7 100755 --- a/gui/templates/tl-classic/plan/planAddTC_m1.tpl +++ b/gui/templates/tl-classic/plan/planAddTC_m1.tpl @@ -118,6 +118,15 @@ Ext.onReady(function(){ }); //--> + + +{* +Pace will automatically monitor your ajax requests, event loop lag, +document ready state, and elements on your page to decide the progress. +On ajax navigation it will begin again! +*} + +
- + {* 20200528 - disabled items are not + covered by header-wrap. + *} + + {if $linked_version_id == 0} + + {else} + {$tcase.tcversions[$linked_version_id]} + {/if} diff --git a/gui/templates/tl-classic/plan/planTCNavigator.tpl b/gui/templates/tl-classic/plan/planTCNavigator.tpl index c7f30438c7..94096ccce5 100644 --- a/gui/templates/tl-classic/plan/planTCNavigator.tpl +++ b/gui/templates/tl-classic/plan/planTCNavigator.tpl @@ -22,8 +22,11 @@ Scope: show test plan tree for execution treeCfg = { tree_div_id:'tree_div',root_name:"",root_id:0,root_href:"", loader:"", enableDD:false, dragDropBackEndUrl:'',children:"" }; Ext.onReady(function() { - Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); - +/* #3714 +Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); +*/ +Ext.state.Manager.setProvider(new Ext.ux.LocalStorageProvider()); + // Use a collapsible panel for filter settings // and place a help icon in ther header var settingsPanel = new Ext.ux.CollapsiblePanel({ diff --git a/gui/templates/tl-classic/plan/planView.tpl b/gui/templates/tl-classic/plan/planView.tpl index 39c225cd09..72719bb5c3 100644 --- a/gui/templates/tl-classic/plan/planView.tpl +++ b/gui/templates/tl-classic/plan/planView.tpl @@ -34,11 +34,26 @@ some smarty and javascript variables are created on the inc_*.tpl files. var del_action=fRoot+'{$deleteAction}'; -{if $tlCfg->gui->planView->pagination->enabled} - {$ll = $tlCfg->gui->planView->pagination->length} - {include file="DataTables.inc.tpl" DataTablesOID="item_view" - DataTableslengthMenu=$ll} -{/if} +{* ------------------------------------------------------------------------------------------------ *} +{* + IMPORTANT DEVELOPMENT NOTICE + Because we are using also DataTablesColumnFiltering + We MUST NOT Initialize the Data Table on DataTables.inc.tpl. + We got this effect with DataTablesOID="" +*} + + {* Data Tables Config Area - BEGIN*} + {$gridHTMLID="item_view"} + {* Do not initialize in DataTables.inc.tpl -> DataTablesSelector="" *} + {include file="DataTables.inc.tpl" DataTablesSelector=""} + {include + file="DataTablesColumnFiltering.inc.tpl" + DataTablesSelector="#{$gridHTMLID}" + DataTablesLengthMenu=$tlCfg->gui->{$cfg_section}->pagination->length + } + {* Data Tables Config Area - End*} +{* ------------------------------------------------------------------------------------------------ *} + {include file="bootstrap.inc.tpl"} @@ -73,95 +88,117 @@ var del_action=fRoot+'{$deleteAction}'; - + + + + {* 20210703 + data-orderable="false" -> {#NOT_SORTABLE#} jQuery Datatables + class="{$noSortableColumnClass}" -> sortable.js + *} + +
- - - - + + + + {if $gui->cfieldsColumns != null} + {foreach item=cflbl from=$gui->cfieldsColumns} + + {/foreach} + {/if} + + + {if $gui->drawPlatformQtyColumn} - + {/if} - - - + + + {foreach item=testplan from=$gui->tplans} - - - - - - {if $gui->drawPlatformQtyColumn} - + - {/if} + + + {if $gui->cfieldsColumns != null} + {foreach item=cflbl from=$gui->cfieldsColumns} + + {/foreach} + {/if} + + + + {if $gui->drawPlatformQtyColumn} + + {/if} + + + + - - - + + {/foreach}
{$tlImages.toggle_api_info}{$tlImages.sort_hint}{$labels.testplan_th_name}{$labels.testplan_th_notes}{$tlImages.sort_hint}{$labels.testcase_qty}{$tlImages.sort_hint}{$labels.build_qty}{$tlImages.toggle_api_info}{$labels.testplan_th_name}{$labels.testplan_th_notes}{$cflbl}{$labels.testcase_qty}{$labels.build_qty}{$tlImages.sort_hint}{$labels.platform_qty}{$labels.platform_qty}{$labels.testplan_th_active}{$labels.public} {$labels.testplan_th_active}{$labels.public} 
- {$testplan.name|escape} - - - {if $gsmarty_gui->show_icon_edit} - {$labels.testplan_alt_edit_tp} - {/if} - - {if $gui->editorType == 'none'}{$testplan.notes|nl2br}{else}{$testplan.notes}{/if} - {$testplan.tcase_qty} - - {$testplan.build_qty} - - {$testplan.platform_qty} +
+ {$testplan.name|escape} + + + {if $gsmarty_gui->show_icon_edit} + {$labels.testplan_alt_edit_tp} + {/if} + {if $gui->editorType == 'none'}{$testplan.notes|nl2br}{else}{$testplan.notes}{/if}{$testplan[$cflbl]['value']|escape} + {$testplan.tcase_qty} + + {$testplan.build_qty} + + {$testplan.platform_qty} + + {if $testplan.active==1} + + {else} + + {/if} + + {if $testplan.is_public eq 1} + {$labels.public} + {else} +   + {/if} + + {$labels.testplan_alt_delete_tp} + + {$labels.export_testplan_links} + + + {$labels.import_testplan_links} + - - {if $testplan.active==1} - - {else} - - {/if} - - {if $testplan.is_public eq 1} - {$labels.public} - {else} -   - {/if} - - {$labels.testplan_alt_delete_tp} - - {$labels.export_testplan_links} - - - {$labels.import_testplan_links} - - - {if $testplan.rights.testplan_user_role_assignment} - - {$labels.assign_roles} + {if $testplan.rights.testplan_user_role_assignment} + + {$labels.assign_roles} + + {/if} + + {$labels.execution} - {/if} - - {$labels.execution} - -
diff --git a/gui/templates/tl-classic/platforms/platformsAssign.tpl b/gui/templates/tl-classic/platforms/platformsAssign.tpl index b9b6d97bc0..cafb18011c 100644 --- a/gui/templates/tl-classic/platforms/platformsAssign.tpl +++ b/gui/templates/tl-classic/platforms/platformsAssign.tpl @@ -19,51 +19,55 @@ Purpose: smarty template - assign platforms to testplans {if $gui->can_do} {/if} diff --git a/gui/templates/tl-classic/platforms/platformsEdit.tpl b/gui/templates/tl-classic/platforms/platformsEdit.tpl index 90b833421b..83f37d8902 100644 --- a/gui/templates/tl-classic/platforms/platformsEdit.tpl +++ b/gui/templates/tl-classic/platforms/platformsEdit.tpl @@ -13,7 +13,7 @@ Purpose: smarty template - Edit a platform {lang_get var="labels" s="warning,warning_empty_platform,show_event_history, - th_platform,th_notes,btn_cancel,on_design,on_exec"} + th_platform,th_notes,btn_cancel,on_design,on_exec,platform_open_for_exec"} {include file="inc_head.tpl" jsValidate="yes" openHead="yes"} @@ -30,10 +30,10 @@ Purpose: smarty template - Edit a platform
- {if $gui->mgt_view_events eq "yes" && $gui->platformID > 0} + {if $gui->mgt_view_events eq "yes" && $gui->platform_id > 0} {$labels.show_event_history} {/if} @@ -67,6 +67,14 @@ Purpose: smarty template - Edit a platform {if $gui->enable_on_execution eq 1} checked {/if} /> + + {$labels.platform_open_for_exec} + is_open eq 1} checked {/if} /> + + +
diff --git a/gui/templates/tl-classic/platforms/platformsView.tpl b/gui/templates/tl-classic/platforms/platformsView.tpl index 08a258c16a..f041df7a9c 100644 --- a/gui/templates/tl-classic/platforms/platformsView.tpl +++ b/gui/templates/tl-classic/platforms/platformsView.tpl @@ -20,7 +20,7 @@ Purpose: smarty template - View all platforms menu_manage_platforms,alt_delete_platform,warning_delete_platform, warning_cannot_delete_platform,delete, menu_assign_kw_to_tc,btn_create, - on_design,on_exec'} + on_design,on_exec,active_click_to_change,inactive_click_to_change,platform_open_for_exec'} {lang_get s='warning_delete_platform' var="warning_msg" } {lang_get s='warning_cannot_delete_platform' var="warning_msg_cannot_del" } @@ -59,9 +59,9 @@ var del_action=fRoot+'{$managerURL}'+'&doAction=do_delete&id='; - + @@ -69,6 +69,7 @@ var del_action=fRoot+'{$managerURL}'+'&doAction=do_delete&id='; + {if $gui->canManage != ""} + + + + + {if $gui->canManage != ""} - {if $gui->featureType == 'testproject'} {/if} @@ -160,74 +168,78 @@ during refresh feature, and then we have a bad refresh on page getting a bug.
-
{$tlImages.sort_hint}{$labels.th_notes} {$labels.on_design} {$labels.on_exec}{$labels.platform_open_for_exec}{$labels.th_delete} @@ -82,7 +83,7 @@ var del_action=fRoot+'{$managerURL}'+'&doAction=do_delete&id='; {if $gui->canManage != ""} - + {/if} {$oplat.name|escape} {if $gui->canManage != ""} @@ -123,6 +124,25 @@ var del_action=fRoot+'{$managerURL}'+'&doAction=do_delete&id='; src="{$tlImages.off}"/> {/if} + {if $oplat.is_open==1} + + {else} + + {/if} + {if $oplat.linked_count eq 0} diff --git a/gui/templates/tl-classic/platforms/platformsViewControls.inc.tpl b/gui/templates/tl-classic/platforms/platformsViewControls.inc.tpl index 5e1cf5c038..e527db1097 100644 --- a/gui/templates/tl-classic/platforms/platformsViewControls.inc.tpl +++ b/gui/templates/tl-classic/platforms/platformsViewControls.inc.tpl @@ -4,14 +4,20 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ *}
- {{$labels.btn_create}} - {$labels.btn_export} {if '' != $gui->canManage} - {$labels.btn_import} {/if} diff --git a/gui/templates/tl-classic/project/projectView.tpl b/gui/templates/tl-classic/project/projectView.tpl index 110e0100b2..ab882b1150 100644 --- a/gui/templates/tl-classic/project/projectView.tpl +++ b/gui/templates/tl-classic/project/projectView.tpl @@ -40,9 +40,53 @@ var del_action=fRoot+'{$deleteAction}'; {if $tlCfg->gui->projectView->pagination->enabled} - {$ll = $tlCfg->gui->projectView->pagination->length} - {include file="DataTables.inc.tpl" DataTablesOID="item_view" - DataTableslengthMenu=$ll} + {$menuLen = $tlCfg->gui->projectView->pagination->length} + {* 20220824 + We need to provide + DataTablesOID and she must be empty To avoid issues due to do initialization multiple times + We want to do an special initialization here instead of using + the standard one provided inside DataTables.inc.tpl + *} + {include file="DataTables.inc.tpl" DataTablesOID=""} + + {/if} {include file="bootstrap.inc.tpl"} @@ -88,13 +132,13 @@ var del_action=fRoot+'{$deleteAction}'; - - - - - - + + + + + {if $gui->canManage == "yes"} diff --git a/gui/templates/tl-classic/requirements/reqSpecListTree.tpl b/gui/templates/tl-classic/requirements/reqSpecListTree.tpl index e2000223c6..9a32725209 100644 --- a/gui/templates/tl-classic/requirements/reqSpecListTree.tpl +++ b/gui/templates/tl-classic/requirements/reqSpecListTree.tpl @@ -11,7 +11,12 @@ show requirement specifications tree menu diff --git a/gui/templates/tl-classic/testcases/inc_tcbody.tpl b/gui/templates/tl-classic/testcases/inc_tcbody.tpl index a64962b153..5bd3e763c5 100644 --- a/gui/templates/tl-classic/testcases/inc_tcbody.tpl +++ b/gui/templates/tl-classic/testcases/inc_tcbody.tpl @@ -1,71 +1,108 @@ {* TestLink Open Source Project - http://testlink.sourceforge.net/ +@uses tctitle.inc.tpl +@used-by tcView_viewer.tpl *} +{* variable $tco will be available in included templates *} {$tco = $inc_tcbody_testcase} -
{$tlImages.toggle_api_info} + {$tlImages.toggle_api_info} {$tlImages.sort_hint}{$labels.th_name}{$labels.th_notes}{$tlImages.sort_hint}{$labels.tcase_id_prefix}{$tlImages.sort_hint}{$labels.th_issuetracker}{$tlImages.sort_hint}{$labels.th_codetracker}{$labels.th_requirement_feature}{$labels.th_notes}{$tlImages.sort_hint}{$labels.tcase_id_prefix}{$tlImages.sort_hint}{$labels.th_issuetracker}{$tlImages.sort_hint}{$labels.th_codetracker}{$labels.th_requirement_feature} {$labels.th_active} {$labels.public}
- - +{if $inc_tcbody_cf.after_title neq ''} +
+
+ {$inc_tcbody_cf.after_title} +
+
- - - - - +{* ------------------------------------------------------------------------------------------ *} +{$showSummary = 0} +{if $tco.summary != '' || ($tco.summary == '' && $gui->hideSummary == FALSE)} + {$showSummary = 1} +{/if} - {if $inc_tcbody_author_userinfo != ''} - - - - {/if} - - {if $tco.updater_id != ''} - - - - {/if} - +{$showPreconditions = 0} +{if $tco.preconditions != '' || ($tco.preconditions == '' && $gui->hidePreconditions == FALSE)} + {$showPreconditions = 1} +{/if} +{* ------------------------------------------------------------------------------------------ *} + + +
+
+ {if $inc_tcbody_cf.before_summary neq ''} +
+ {$inc_tcbody_cf.before_summary} +
+
+ {/if} + {if $showSummary} +
{$inc_tcbody_labels.summary}
+
{if $inc_tcbody_editor_type == 'none'}{$tco.summary|nl2br}{else}{$tco.summary}{/if}
+ {/if} + {if $inc_tcbody_cf.after_summary neq ''} +
+
+ {$inc_tcbody_cf.after_summary} +
+ {/if} +
-
- - - - + {if $showPreconditions && $showSummary} +
+ {/if} - - - - - +
+ {if $inc_tcbody_cf.before_preconditions neq ''} +
+ {$inc_tcbody_cf.before_preconditions} +
+
+ {/if} + {if $showPreconditions} + {$spanid="preconditions_{$tco.id}"} + - {if $inc_tcbody_cf.before_steps_results neq ''} -
- - - {/if} -{if $inc_tcbody_close_table} -
- {$tco.tc_external_id}{$smarty.const.TITLE_SEP}{$tco.name|escape} - {$smarty.const.TITLE_SEP_TYPE2}{$inc_tcbody_labels.version|escape}{$tco.version} - - +{include file="testcases/tctitle.inc.tpl"} + - -
{$tco.ghost}
 
{$inc_tcbody_labels.summary} -
{if $inc_tcbody_editor_type == 'none'}{$tco.summary|nl2br}{else}{$tco.summary}{/if}

{$inc_tcbody_labels.preconditions} -
{if $inc_tcbody_editor_type == 'none'}{$tco.preconditions|nl2br}{else}{$tco.preconditions}{/if}

+
{$inc_tcbody_labels.preconditions} + +
+
{if $inc_tcbody_editor_type == 'none'}{$tco.preconditions|nl2br}{else}{$tco.preconditions}{/if}
+ {/if} + {if $inc_tcbody_cf.after_summary neq ''} +
+
+ {$inc_tcbody_cf.after_preconditions} +
+ {/if} + + + {if $inc_tcbody_cf.before_steps_results neq ''} +
+
{$inc_tcbody_cf.before_steps_results} -
-{/if} \ No newline at end of file +
+ + {/if} + +{if $showPreconditions && $showSummary} +
+{/if} + + diff --git a/gui/templates/tl-classic/testcases/keywords.inc.tpl b/gui/templates/tl-classic/testcases/keywords.inc.tpl index d91aef4859..5e3ae896fe 100644 --- a/gui/templates/tl-classic/testcases/keywords.inc.tpl +++ b/gui/templates/tl-classic/testcases/keywords.inc.tpl @@ -51,10 +51,14 @@ var pF_remove_keyword = remove_keyword; -
+ - - + + + {if property_exists($gui,'tproject_id') } + + {/if} {if property_exists($gui,'tplan_id') } @@ -64,71 +68,66 @@ var pF_remove_keyword = remove_keyword; {/if} - {$kwView = - "lib/keywords/keywordsView.php?tproject_id=%s%&openByKWInc=1"|replace:'%s%':$gui->tproject_id} + {$kwView = "lib/keywords/keywordsView.php?tproject_id=%s%&openByKWInc=1"|replace:'%s%':$gui->tproject_id} - {$kwAdd = - "lib/keywords/keywordsEdit.php?doAction=create&tproject_id=%s%&directAccess=1"|replace:'%s%':$gui->tproject_id} + {$kwAdd = "lib/keywords/keywordsEdit.php?doAction=create&tproject_id=%s%&directAccess=1"|replace:'%s%':$gui->tproject_id} - {$kwAL = - "lib/keywords/keywordsEdit.php?doAction=cfl&tproject_id=%s%&directAccess=1&tcversion_id=$args_tcversion_id"|replace:'%s%':$gui->tproject_id} + {$kwAL = "lib/keywords/keywordsEdit.php?doAction=cfl&tproject_id=%s%&directAccess=1&tcversion_id=$args_tcversion_id"|replace:'%s%':$gui->tproject_id} - - - - {if $edit_enabled && $args_frozen_version=="no"} - + diff --git a/gui/templates/tl-classic/testcases/tcAssign2Tplan.tpl b/gui/templates/tl-classic/testcases/tcAssign2Tplan.tpl index eb3e8df4b8..9a405b198d 100644 --- a/gui/templates/tl-classic/testcases/tcAssign2Tplan.tpl +++ b/gui/templates/tl-classic/testcases/tcAssign2Tplan.tpl @@ -31,7 +31,9 @@ function check_action_precondition(container_id,action) {$ll = '[25, 50, 75, -1], [25, 50, 75, "All"]'} -{include file="DataTables.inc.tpl" DataTablesOID="item_view" DataTableslengthMenu=$ll} +{include file="DataTables.inc.tpl" + DataTablesOID="item_view" + DataTablesLengthMenu=$ll} diff --git a/gui/templates/tl-classic/testcases/tcEdit.tpl b/gui/templates/tl-classic/testcases/tcEdit.tpl index 5258133698..ca404b8fb1 100644 --- a/gui/templates/tl-classic/testcases/tcEdit.tpl +++ b/gui/templates/tl-classic/testcases/tcEdit.tpl @@ -2,8 +2,6 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ @filesource tcEdit.tpl Purpose: smarty template - edit test specification: test case - -@internal revisions *} {lang_get var="labels" @@ -12,6 +10,29 @@ Purpose: smarty template - edit test specification: test case {include file="inc_head.tpl" openHead='yes' jsValidate="yes" editorType=$gui->editorType} + + + + + {include file="inc_del_onclick.tpl"} @@ -43,41 +64,49 @@ function validateForm(the_form) { var status_ok = true; - if (isWhitespace(the_form.testcase_name.value)) - { + if (isWhitespace(the_form.testcase_name.value)) { alert_message(alert_box_title,warning_empty_testcase_name); selectField(the_form,'testcase_name'); return false; } var val2check = the_form.estimated_execution_duration.value; - if( isNaN(val2check) || /^\s+$/.test(val2check.trim())) - { + if( isNaN(val2check) || /^\s+$/.test(val2check.trim())) { alert_message(alert_box_title,warning_estimated_execution_duration_format); return false; } - var cf_designTime = document.getElementById('cfields_design_time'); - if (cf_designTime) - { - var cfields_container = cf_designTime.getElementsByTagName('input'); - var cfieldsChecks = validateCustomFields(cfields_container); - if(!cfieldsChecks.status_ok) - { - var warning_msg = cfMessages[cfieldsChecks.msg_id]; - alert_message(alert_box_title,warning_msg.replace(/%s/, cfieldsChecks.cfield_label)); - return false; + var cfSet = [document.getElementById('cf_before_summary'), + document.getElementById('cf_before_steps'), + document.getElementById('cf_standard_location')]; + for (ccx=0; ccx < cfSet.length; ccx++) { + if (typeof cfSet[ccx] == 'undefined') { + continue; } - cfields_container = cf_designTime.getElementsByTagName('textarea'); - cfieldsChecks = validateCustomFields(cfields_container); - if(!cfieldsChecks.status_ok) - { - var warning_msg = cfMessages[cfieldsChecks.msg_id]; - alert_message(alert_box_title,warning_msg.replace(/%s/, cfieldsChecks.cfield_label)); - return false; + var cf_designTime = cfSet[ccx]; + if (cf_designTime) { + var cfields_container = + cf_designTime.getElementsByTagName('input'); + var cfieldsChecks = validateCustomFields(cfields_container); + if (!cfieldsChecks.status_ok) { + var warning_msg = cfMessages[cfieldsChecks.msg_id]; + alert_message(alert_box_title,warning_msg.replace(/%s/, cfieldsChecks.cfield_label)); + return false; + } + + cfields_container = + cf_designTime.getElementsByTagName('textarea'); + cfieldsChecks = validateCustomFields(cfields_container); + + if (!cfieldsChecks.status_ok) { + var warning_msg = cfMessages[cfieldsChecks.msg_id]; + alert_message(alert_box_title,warning_msg.replace(/%s/, cfieldsChecks.cfield_label)); + return false; + } } } + return Ext.ux.requireSessionAndSubmit(the_form); } @@ -103,16 +132,18 @@ function validateForm(the_form)
{$warning_edit_msg}
{/if} - - - + + - + + {* when save or cancel is pressed do not show modification warning *}
diff --git a/gui/templates/tl-classic/testcases/tcEdit_New_viewer.tpl b/gui/templates/tl-classic/testcases/tcEdit_New_viewer.tpl index b792a16e88..b17d7585bd 100644 --- a/gui/templates/tl-classic/testcases/tcEdit_New_viewer.tpl +++ b/gui/templates/tl-classic/testcases/tcEdit_New_viewer.tpl @@ -1,17 +1,15 @@ {* TestLink Open Source Project - http://testlink.sourceforge.net/ -$Id: tcEdit_New_viewer.tpl,v 1.27 2010/10/11 18:11:29 franciscom Exp $ +@filesource tcEdit_New_viewer.tpl Purpose: smarty template - create new testcase - -@internal revisions - *} -{* ---------------------------------------------------------------- *} +{* -------------------------------------------------------------- *} {lang_get var='labels' - s='tc_title,alt_add_tc_name,summary,steps,expected_results, - preconditions,status,estimated_execution_duration,importance, - execution_type,test_importance,tc_keywords,assign_requirements'} + s='tc_title,alt_add_tc_name,summary,steps,expected_results, + preconditions,status,estimated_execution_duration,importance, + execution_type,test_importance,tc_keywords, + assign_requirements'} {* Steps and results Layout management *} {$layout1="
"} @@ -23,7 +21,7 @@ Purpose: smarty template - create new testcase {$layout2='
- {$tcView_viewer_labels.keywords}   + {$removeEnabled = $args_edit_enabled && $gui->assign_keywords && + $args_frozen_version == "no"} - -   + + + - - {$removeEnabled = $args_edit_enabled && $gui->assign_keywords && - $args_frozen_version == "no"} - - + + {foreach item=tckw_link_item from=$args_keywords_map} + + + + {/foreach} + + {$addEnabled = $args_edit_enabled} {if $addEnabled && null != $gui->currentVersionFreeKeywords} - - - + + + {/if}
+ {$tcView_viewer_labels.keywords}   - -   + +   + +   - {foreach item=tckw_link_item from=$args_keywords_map} - {$tckw_link_item.keyword|escape} - {if $removeEnabled} - - - {/if} -
- {foreachelse} - {$tcView_viewer_labels.none} - {/foreach} -
+ {if $removeEnabled} + + + {/if} + {$tckw_link_item.keyword|escape} +
 
-   - - - - + + +
diff --git a/gui/templates/tl-classic/testcases/object_keywords.inc.tpl b/gui/templates/tl-classic/testcases/object_keywords.inc.tpl index 8b9e9b6592..7146bd6dcb 100644 --- a/gui/templates/tl-classic/testcases/object_keywords.inc.tpl +++ b/gui/templates/tl-classic/testcases/object_keywords.inc.tpl @@ -20,12 +20,12 @@ var remove_kw_msgbox_title = '{$remove_kw_msgbox_title|escape:'javascript'}'; * * */ -function keyword_remove_confirmation(item_id, kw_link_id, keyword, title, msg, pFunction) { +function keyword_remove_confirmation(item_id, kw_link_id, keyword, title, msg, pFunction, project_id) { var my_msg = msg.replace('%i',keyword); var safe_title = escapeHTML(title); Ext.Msg.confirm(safe_title, my_msg, function(btn, text) { - pFunction(btn,text,item_id,kw_link_id); + pFunction(btn,text,item_id,kw_link_id,project_id); }); } @@ -34,9 +34,9 @@ function keyword_remove_confirmation(item_id, kw_link_id, keyword, title, msg, p * * */ -function remove_keyword(btn, text, item_id, kw_link_id) { +function remove_keyword(btn, text, item_id, kw_link_id, project_id) { var my_action = fRoot + 'lib/testcases/containerEdit.php?doAction=removeKeyword&item_id=' - + item_id + '&kw_link_id=' + kw_link_id; + + item_id + '&kw_link_id=' + kw_link_id + '&tproject_id=' + project_id; if( btn == 'yes' ) { window.location=my_action; } @@ -51,6 +51,7 @@ var pF_remove_keyword = remove_keyword; + {$kwView = $gsmarty_href_keywordsView|replace:'%s%':$gui->tproject_id} @@ -67,7 +68,7 @@ var pF_remove_keyword = remove_keyword; {$kw_link_item.kw_link}, '{$kw_link_item.keyword|escape:'javascript'}', remove_kw_msgbox_title, remove_kw_msgbox_msg, - pF_remove_keyword);"> + pF_remove_keyword,{$gui->tproject_id});"> {/if}
diff --git a/gui/templates/tl-classic/testcases/platforms.inc.tpl b/gui/templates/tl-classic/testcases/platforms.inc.tpl index 70b067dec4..b2fa2069ac 100644 --- a/gui/templates/tl-classic/testcases/platforms.inc.tpl +++ b/gui/templates/tl-classic/testcases/platforms.inc.tpl @@ -50,10 +50,12 @@ var pF_remove_platform = remove_platform; - - - - + + + + {if property_exists($gui,'tplan_id') } diff --git a/gui/templates/tl-classic/testcases/relations.inc.tpl b/gui/templates/tl-classic/testcases/relations.inc.tpl index 4119a01afe..7888acbf0e 100644 --- a/gui/templates/tl-classic/testcases/relations.inc.tpl +++ b/gui/templates/tl-classic/testcases/relations.inc.tpl @@ -60,11 +60,13 @@ var pF_delete_relation = delete_relation; + {* need to check @20220109 - $gui->tcversion_id is 0, is this OK? means latest? *} - - + + {if property_exists($gui,'tplan_id') } @@ -73,7 +75,8 @@ var pF_delete_relation = delete_relation; {/if} - + {* $tcversion_id inherited from tcView_viewer.tpl *} +
{if $args_edit_enabled} {$canWork = $args_is_latest_tcv == 1 || $tlCfg->testcase_cfg->addTCVRelationsOnlyOnLatestTCVersion == 0} diff --git a/gui/templates/tl-classic/testcases/scriptAdd.tpl b/gui/templates/tl-classic/testcases/scriptAdd.tpl old mode 100755 new mode 100644 index 4c8816a682..6b7feee64a --- a/gui/templates/tl-classic/testcases/scriptAdd.tpl +++ b/gui/templates/tl-classic/testcases/scriptAdd.tpl @@ -1,9 +1,6 @@ {* TestLink Open Source Project - http://testlink.sourceforge.net/ @filesource scriptAdd.tpl -@internal revisions -@since 1.9.15 - *} {include file="inc_head.tpl"} diff --git a/gui/templates/tl-classic/testcases/steps_horizontal.inc.tpl b/gui/templates/tl-classic/testcases/steps_horizontal.inc.tpl index 15cbb13191..d01cbfc5fa 100644 --- a/gui/templates/tl-classic/testcases/steps_horizontal.inc.tpl +++ b/gui/templates/tl-classic/testcases/steps_horizontal.inc.tpl @@ -15,7 +15,17 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ {$inExec = 1} {/if} - +{* + +*} + + + +
+
+ @@ -48,10 +58,8 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ {/if} - - - + {$rowCount=$steps|@count} {$row=0} @@ -59,8 +67,8 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ $tlCfg->exec_cfg->steps_exec_attachments} {foreach from=$steps item=step_info} - - + - - + {if $session['testprojectOptions']->automationEnabled} - + {/if} {if $edit_enabled && $args_frozen_version=="no"} @@ -137,22 +153,53 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ {/if} - - {if $ghost_control} - - {/if} - - {$rCount=$row+$step_info.step_number} - {if ($rCount < $rowCount) && ($rowCount>=1)} - - {if $session['testprojectOptions']->automationEnabled} - - - {/if} - - {/foreach} \ No newline at end of file + {/foreach} +
{if $edit_enabled && $steps != '' && !is_null($steps) && $args_frozen_version=="no"} + onclick="showHideByClass('span','ghost');"> {/if} {$inc_steps_labels.step_number}
+
{if $gui->stepDesignEditorType == 'none'}{$step_info.actions|nl2br}{else}{$step_info.actions}{/if} + {if $gui->stepDesignEditorType == 'none'}{$step_info.actions|nl2br}{else}{$step_info.actions}{/if} {if $gui->stepDesignEditorType == 'none'}{$step_info.expected_results|nl2br}{else}{$step_info.expected_results}{/if}{if $gui->stepDesignEditorType == 'none'}{$step_info.expected_results|nl2br}{else}{$step_info.expected_results}{/if}{$gui->execution_types[$step_info.execution_type]}{$gui->execution_types[$step_info.execution_type]}
- {else} - - {/if} -
-
+ + + + \ No newline at end of file diff --git a/gui/templates/tl-classic/testcases/steps_vertical.inc.tpl b/gui/templates/tl-classic/testcases/steps_vertical.inc.tpl index ebb60dcf6f..e92f06e702 100644 --- a/gui/templates/tl-classic/testcases/steps_vertical.inc.tpl +++ b/gui/templates/tl-classic/testcases/steps_vertical.inc.tpl @@ -41,8 +41,9 @@ TestLink Open Source Project - http://testlink.sourceforge.net/
 {if $gui->stepDesignEditorType == 'none'}{$step_info.actions|nl2br}{else}{$step_info.actions}{/if} @@ -64,8 +65,9 @@ TestLink Open Source Project - http://testlink.sourceforge.net/ {$inc_steps_labels.expected_results}
  {if $gui->stepDesignEditorType == 'none'}{$step_info.expected_results|nl2br}{else}{$step_info.expected_results}{/if}
'} {$layout3="

"} {/if} -{* ---------------------------------------------------------------- *} +{* ----------------------------------------------------------- *}

@@ -32,8 +30,11 @@ Purpose: smarty template - create new testcase maxlength="{#TESTCASE_NAME_MAXLEN#}" onchange="content_modified = true" onkeypress="content_modified = true" - onkeyup="javascript:checkTCaseDuplicateName(Ext.get('testcase_id').getValue(),Ext.get('testcase_name').getValue(), - Ext.get('testsuite_id').getValue(),'testcase_name_warning')" + onkeyup="javascript:checkTCaseDuplicateName($('#testcase_id').val(), + $('#testcase_name').val(), + $('#testsuite_id').val(), + $('#tproject_id').val(), + 'testcase_name_warning')" {if isset($gui->tc.name)} value="{$gui->tc.name|escape}" {else} @@ -44,26 +45,69 @@ Purpose: smarty template - create new testcase

-

{$labels.summary}
-
{$summary}
+ {if $gui->cf.after_title neq ''} +
+
+ {$gui->cf.after_title} +
+
+ + {/if} +
+ +
+
+ {if $gui->cf.before_summary neq ""} +
+ {$gui->cf.before_summary} +
+
+ {/if} + +
{$labels.summary}
+
{$summary}
+ + {if $gui->cf.after_summary neq ""} +
+ {$gui->cf.after_summary} +
+ {/if} + +
+
+
+ {if $gui->cf.before_preconditions neq ""} +
+ {$gui->cf.before_preconditions} +
+
+ {/if} + +
{$labels.preconditions}
+
{$preconditions}
+ {if $gui->cf.after_preconditions neq ""} +
+ {$gui->cf.after_preconditions} +
+ {/if} +
+
+ -
{$labels.preconditions}
-
{$preconditions}
- {* Custom fields - with before steps & results location *}
{if $gui->cf.before_steps_results neq ""}
- {* ID is important because is used on validationForm to get custom field container - that's how have to have SAME ID that other div below on page. - NOTICE that only one of this div are active, if will not be the case we will - have a problem because ID has to be UNIQUE - *} -
- {$gui->cf.before_steps_results} +
+ {$gui->cf.before_steps_results}
- {/if} {$layout1} {include file="testcases/attributesLinear.inc.tpl"} @@ -73,7 +117,7 @@ Purpose: smarty template - create new testcase {* Custom fields - with standard location *} {if $gui->cf.standard_location neq ""}
-
+
{$gui->cf.standard_location}
{/if} diff --git a/gui/templates/tl-classic/testcases/tcNew.tpl b/gui/templates/tl-classic/testcases/tcNew.tpl index 05d3032524..b0fb15135c 100644 --- a/gui/templates/tl-classic/testcases/tcNew.tpl +++ b/gui/templates/tl-classic/testcases/tcNew.tpl @@ -1,7 +1,7 @@ {* TestLink Open Source Project - http://testlink.sourceforge.net/ -$Id: tcNew.tpl,v 1.18.2.3 2011/01/14 14:39:04 asimon83 Exp $ -Purpose: smarty template - create new testcase +$Id: tcNew.tpl +Purpose: smarty template - create a testcase *} {$cfg_section=$smarty.template|basename|replace:".tpl":""} @@ -11,6 +11,31 @@ Purpose: smarty template - create new testcase warning_empty_tc_title,warning_unsaved,stay_here_tc'} {include file="inc_head.tpl" openHead='yes' jsValidate="yes"} + + + + + + + {include file="inc_del_onclick.tpl"} @@ -83,14 +108,18 @@ function validateForm(f)

{$gui->main_descr|escape}

-{* BUGID 4067 *} -{include file="inc_update.tpl" result=$gui->sqlResult item="testcase" name=$gui->name user_feedback=$gui->user_feedback refresh=$smarty.session.setting_refresh_tree_on_action} +{include file="inc_update.tpl" + result=$gui->sqlResult + item="testcase" + name=$gui->name + user_feedback=$gui->user_feedback refresh=$smarty.session.setting_refresh_tree_on_action} + {if $gui->steps != ''} @@ -114,8 +143,6 @@ function validateForm(f)
{/if} - -
editorType} + + + {include file="inc_del_onclick.tpl"} @@ -41,7 +74,8 @@ var warning_step_number_already_exists = "{$labels.warning_step_number_already_e * * */ -function validateForm(the_form,step_set,step_number_on_edit) { +function validateForm(the_form,step_set,step_number_on_edit) +{ var value = ''; var status_ok = true; var feedback = ''; @@ -70,11 +104,11 @@ function validateForm(the_form,step_set,step_number_on_edit) { {if $tlCfg->gui->checkNotSaved} - - + + {/if} @@ -114,7 +148,7 @@ var tc_editor = "{$gui->editorType}"; - {include file="testcases/inc_tcbody.tpl" + {include file="testcases/inc_tcbody.tpl" inc_tcbody_close_table=true inc_tcbody_testcase=$gui->testcase inc_tcbody_show_title="yes" @@ -123,226 +157,229 @@ var tc_editor = "{$gui->editorType}"; inc_tcbody_author_userinfo=$gui->authorObj inc_tcbody_updater_userinfo=$gui->updaterObj inc_tcbody_editor_type=$gui->editorType - inc_tcbody_cf=null} - - + inc_tcbody_cf=$gui->cfieldsDesignTime} {* when save or cancel is pressed do not show modification warning *} -
- - - - - {if $gui->operation == 'doUpdateStep'} - - - - {/if} - -
- -
- {if $gui->steps_results_layout == "horizontal"} - - - {* Julian: added width to show columns step details and expected - * results at approximately same size (step details get 45% - * expected results get the rest) - *} - - - {if $session['testprojectOptions']->automationEnabled} - - {/if} - - - {* this means we have steps to display *} - {if $gui->tcaseSteps != ''} - {$rowCount=$gui->tcaseSteps|@count} - {$row=0} - - {foreach from=$gui->tcaseSteps item=step_info} - - {if $step_info.step_number == $gui->step_number} - - - - {if $session['testprojectOptions']->automationEnabled} - - {/if} - {else} - - - - {if $session['testprojectOptions']->automationEnabled} - - {/if} +
+
+ + + + + {if $gui->operation == 'doUpdateStep'} + + + {/if} - {$rCount=$row+$step_info.step_number} - {if ($rCount < $rowCount) && ($rowCount>=1)} -
- {if $session['testprojectOptions']->automationEnabled} - - - {/if} - - {/foreach} - {/if} - {else} - - {* Vertical layout *} - {foreach from=$gui->tcaseSteps item=step_info} - - - - {if $session['testprojectOptions']->automationEnabled} - {if $step_info.step_number == $gui->step_number} -
{$labels.step_number}{$labels.step_actions}{$labels.expected_results}{$labels.execution_type_short_descr}
{$gui->step_number}{$steps} -
- - - - - - - {if $gui->operation == 'doUpdateStep'} - - {/if} - -
- - -
{$expected_results} - - {$step_info.step_number}{$step_info.actions}{$step_info.expected_results}{$gui->execution_types[$step_info.execution_type]}
- {else} - - {/if} -
-
{$args_labels.step_number} {$step_info.step_number}{$labels.step_actions}{$labels.execution_type_short_descr}: - + + + +
+ + {if $gui->steps_results_layout == "horizontal"} + + + {* Julian: + * added width to show columns step details and expected + * results at approximately same size (step details get 45% + * expected results get the rest) + *} + + + {if $session['testprojectOptions']->automationEnabled} + + {/if} + + {* this means we have steps to display *} + {if $gui->tcaseSteps != ''} + {$rowCount=$gui->tcaseSteps|@count} + {$row=0} + + {foreach from=$gui->tcaseSteps item=step_info} + + {if $step_info.step_number == $gui->step_number} + + + + {if $session['testprojectOptions']->automationEnabled} + + + {/if} + {else} + + + + {if $session['testprojectOptions']->automationEnabled} + + {/if} + {/if} + {$rCount=$row+$step_info.step_number} + {if ($rCount < $rowCount) && ($rowCount>=1)} + + {if $session['testprojectOptions']->automationEnabled} + + {/if} - {else} - - {/if} {* automation *} - {if $edit_enabled} - + + {/foreach} {/if} - - - - {if $step_info.step_number == $gui->step_number} - - {else} - - {/if} - - - - - - - - {if $step_info.step_number == $gui->step_number} - + {else} + {* Vertical layout *} + {foreach from=$gui->tcaseSteps item=step_info} + + + + {if $session['testprojectOptions']->automationEnabled} + {if $step_info.step_number == $gui->step_number} + + {else} + + {/if} + {else} + + {/if} {* automation *} + {if $edit_enabled} + + {/if} + + + + {if $step_info.step_number == $gui->step_number} + + {else} + + {/if} + + + + + + + + {if $step_info.step_number == $gui->step_number} + + {else} + + {/if} + + {/foreach} + {/if} + + {if $gui->action == 'createStep' || $gui->action == 'doCreateStep'} + {* We have forgotten to manage layout here *} + {if $gui->steps_results_layout == "horizontal"} + + + + + {if $session['testprojectOptions']->automationEnabled} + + {/if} + + {else} - - {/if} - - {/foreach} - {/if} - - {if $gui->action == 'createStep' || $gui->action == 'doCreateStep'} - {* We have forgotten to manage layout here *} - {if $gui->steps_results_layout == "horizontal"} - - - - - {if $session['testprojectOptions']->automationEnabled} - - {/if} - - - {else} - - - - {if $session['testprojectOptions']->automationEnabled} - + + + + {if $session['testprojectOptions']->automationEnabled} + + {/if} + + + + + + + + + + + + + {/if} - - - - - - - - - - - - - - {/if} - {/if} -
{$labels.step_number}{$labels.step_actions}{$labels.expected_results}{$labels.execution_type_short_descr}
{$gui->step_number}{$steps} +
+ + + + + + + {if $gui->operation == 'doUpdateStep'} + + {/if} + +
+ + +
{$expected_results} + - - {else} - {$labels.execution_type_short_descr}: - {$gui->execution_types[$step_info.execution_type]}{$step_info.step_number}{$step_info.actions}{$step_info.expected_results}{$gui->execution_types[$step_info.execution_type]}
+ {else} + + {/if} +
+
  
 {$steps}{$step_info.actions}
{$labels.expected_results}
 {$expected_results}
{$args_labels.step_number} {$step_info.step_number}{$labels.step_actions}{$labels.execution_type_short_descr}: + + {$labels.execution_type_short_descr}: + {$gui->execution_types[$step_info.execution_type]}  
 {$steps}{$step_info.actions}
{$labels.expected_results}
 {$expected_results} + {$step_info.expected_results}
{$gui->step_number}{$steps}{$expected_results} + +
- {$step_info.expected_results}
{$gui->step_number}{$steps}{$expected_results} - -
{$args_labels.step_number} {$gui->step_number}{$labels.step_actions}{$labels.execution_type_short_descr}: - -
{$args_labels.step_number} {$gui->step_number}{$labels.step_actions}{$labels.execution_type_short_descr}: + +
 {$steps}
{$labels.expected_results}
  {$expected_results}
 {$steps}
{$labels.expected_results}
  {$expected_results}
-

- {* when save or cancel is pressed do not show modification warning *} -

- - - - - {if $gui->operation == 'doUpdateStep'} - - - - {/if} - - -
+ {/if} +
+
+ {* when save or cancel is pressed do not show + modification warning *} +
+
+ + + + + {if $gui->operation == 'doUpdateStep'} + + + + {/if} + + +
+
diff --git a/gui/templates/tl-classic/testcases/tcTree.tpl b/gui/templates/tl-classic/testcases/tcTree.tpl index fd4c2edd21..a06ecdee10 100644 --- a/gui/templates/tl-classic/testcases/tcTree.tpl +++ b/gui/templates/tl-classic/testcases/tcTree.tpl @@ -19,8 +19,14 @@ Purpose: smarty template - show test specification tree menu treeCfg = { tree_div_id:'tree_div',root_name:"",root_id:0,root_href:"", loader:"", enableDD:false, dragDropBackEndUrl:'',children:"" }; Ext.onReady(function() { + + +/* #3714 Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); - +*/ +Ext.state.Manager.setProvider(new Ext.ux.LocalStorageProvider()); + + // Use a collapsible panel for filter settings // and place a help icon in ther header var settingsPanel = new Ext.ux.CollapsiblePanel({ diff --git a/gui/templates/tl-classic/testcases/tcView.tpl b/gui/templates/tl-classic/testcases/tcView.tpl index 272ba5fd57..d85ca0c5d6 100644 --- a/gui/templates/tl-classic/testcases/tcView.tpl +++ b/gui/templates/tl-classic/testcases/tcView.tpl @@ -7,7 +7,8 @@ Purpose: smarty template - view test case in test specification {config_load file="input_dimensions.conf"} {lang_get var='labels' - s='no_records_found,other_versions,show_hide_reorder,version,title_test_case,match_count,actions, + s='no_records_found,other_versions,show_hide_reorder,version, + title_test_case,match_count,actions, file_upload_ko'} {* Configure Actions *} @@ -19,6 +20,49 @@ Purpose: smarty template - view test case in test specification {$deleteStepAction = "$deleteStepAction&tplan_id=$tplanID&step_id="} {include file="inc_head.tpl" openHead='yes'} + + + + + + + {include file="inc_del_onclick.tpl"} @@ -176,11 +220,16 @@ function jsCallDeleteFile(btn, text, o_id) { {$loadOnCancelURL=""} {/if} - {include file="attachments.inc.tpl" - attach_attachmentInfos=$gui->attachments[$tcVersionID] - attach_downloadOnly=$bDownloadOnly - attach_uploadURL={$gui->fileUploadURL[$tcVersionID]} - attach_loadOnCancelURL=$gui->loadOnCancelURL} + {if $gui->attachments != ''} +
+ {include file="attachments.inc.tpl" + attach_attachmentInfos=$gui->attachments[$tcVersionID] + attach_downloadOnly=$bDownloadOnly + attach_uploadURL={$gui->fileUploadURL[$tcVersionID]} + attach_loadOnCancelURL=$gui->loadOnCancelURL} +
+ {/if} + {* Other Versions *} {if 'editOnExec' != $gui->show_mode && @@ -193,7 +242,7 @@ function jsCallDeleteFile(btn, text, o_id) { show_hide_container_title=$labels.other_versions show_hide_container_id=$div_id show_hide_container_draw=false - show_hide_container_class='exec_additional_info' + show_hide_container_class='execAdditionalInfo' show_hide_container_view_status_id=$memstatus_id}
@@ -225,7 +274,7 @@ function jsCallDeleteFile(btn, text, o_id) { show_hide_container_title=$title show_hide_container_id=$div_id show_hide_container_draw=false - show_hide_container_class='exec_additional_info' + show_hide_container_class='execAdditionalInfo' show_hide_container_view_status_id=$memstatus_id}
diff --git a/gui/templates/tl-classic/testcases/tcView_viewer.tpl b/gui/templates/tl-classic/testcases/tcView_viewer.tpl index 1cc1676a5a..572cbef931 100644 --- a/gui/templates/tl-classic/testcases/tcView_viewer.tpl +++ b/gui/templates/tl-classic/testcases/tcView_viewer.tpl @@ -1,6 +1,8 @@ {* TestLink Open Source Project - http://testlink.sourceforge.net/ @filesource tcView_viewer.tpl +@used-by tcView.tpl + viewer for test case in test specification *} {lang_get var="tcView_viewer_labels" @@ -19,7 +21,8 @@ viewer for test case in test specification estimated_execution_duration,status,btn_save,estimated_execution_duration_short, requirement,btn_show_exec_history,btn_resequence_steps,link_unlink_requirements, code_mgmt,code_link_tl_to_cts,can_not_edit_frozen_tc,testcase_operations, - testcase_version_operations,goto_execute,"} + testcase_version_operations,goto_execute,tc_has_relations, + click_to_copy_ghost_to_clipboard,do_not_execute"} {lang_get s='warning_delete_step' var="warning_msg"} {lang_get s='delete' var="del_msgbox_title"} @@ -31,7 +34,7 @@ viewer for test case in test specification {$module='lib/testcases/'} {$tcase_id=$args_testcase.testcase_id} -{$tcversion_id=$args_testcase.id} +{$tcversion_id=$args_testcase.id} {* @used-by relations.inc.tpl *} {$showMode=$gui->show_mode} {$openC = $gsmarty_gui->role_separator_open} @@ -101,6 +104,16 @@ viewer for test case in test specification {$has_been_executed=1} {/if} +{foreach from=$gui->TCWKFStatusDisplayHintOnTestDesign key=wkfStatusVerbose item=lblKey} + {if $lblKey != '' && $gui->TCWKFStatusVerboseCode[$wkfStatusVerbose] == $args_testcase.status} + {lang_get var="TCWKFMsg" s="$lblKey"} +
{$gui->domainTCStatus[$args_testcase.status]}
{$TCWKFMsg}
+ {/if} +{/foreach} + + + + {if $args_can_do->edit == "yes"} {if $args_status_quo == null || $args_status_quo[$args_testcase.id].executed == null} @@ -109,8 +122,7 @@ viewer for test case in test specification {$warning_edit_msg=""} {$warning_delete_msg=""} {else} - {if isset($args_tcase_cfg) - && $args_tcase_cfg->can_edit_executed == 1} + {if isset($args_tcase_cfg) && $args_tcase_cfg->can_edit_executed == 1} {$edit_enabled=1} {lang_get s='warning_editing_executed_tc' var="warning_edit_msg"} {/if} @@ -218,17 +230,19 @@ viewer for test case in test specification {* START TCV SECTION *}
{$tcView_viewer_labels.testcase_version_operations} -
- + value="{$args_testcase.testcase_id}" /> + - + + value="{$has_been_executed}" /> @@ -258,8 +272,31 @@ viewer for test case in test specification {/if} {/if} + {* activate/desactivate TC version *} + {if $args_can_do->edit == "yes" + && $args_can_do->deactivate=='yes' + && $args_frozen_version=="no"} + + {$act_deact_btn="activate_this_tcversion"} + {$act_deact_value="activate_this_tcversion"} + {$version_title_class="inactivate_version"} + + {if $args_testcase.active eq 1} + {$prex = "de"} + {$act_deact_btn = "$prex$act_deact_btn"} + {$act_deact_value = "$prex$act_deact_value"} + {$version_title_class = "activate_version"} + {/if} + + + {/if} + + + {* freeze/unfreeze TC version *} - {if 'editOnExec' != $gui->show_mode && $args_read_only != "yes" + {if 'editOnExec' != $gui->show_mode + && $args_read_only != "yes" && $args_can_do->freeze=='yes'} {if $args_frozen_version=="yes"} {$freeze_btn="unfreeze"} @@ -357,6 +394,7 @@ viewer for test case in test specification
{$tcView_viewer_labels.can_not_edit_frozen_tc}
{/if} + {if $warning_edit_msg != ""}
{$warning_edit_msg|escape}
@@ -367,7 +405,27 @@ viewer for test case in test specification {$warning_delete_msg|escape}
{/if} - + {if count($gui->additionalMessages) > 0} + {foreach $gui->additionalMessages as $msgCfg} + {lang_get s=$msgCfg->label var="additionalMsg"} + + {$defClass="messages"} + {if property_exists($msgCfg, "class") && $msgCfg->class != ""} + {$defClass=$msgCfg->class} + {/if} + + +
+ {if property_exists($msgCfg, "tlImagesAccessKey") && $msgCfg->tlImagesAccessKey != ""} + {$imgAccessKey = $msgCfg->tlImagesAccessKey} + + {/if} + {$additionalMsg} +
+ {/foreach} + {/if} + + -{if $tlCfg->gui->usersAssign->pagination->enabled} - {$ll = $tlCfg->gui->usersAssign->pagination->length} - {include file="DataTables.inc.tpl" DataTablesOID="item_view" DataTableslengthMenu=$ll} -{/if} +{* ------------------------------------------------------------------------------------------------ *} +{* + IMPORTANT DEVELOPMENT NOTICE + Because we are using also DataTablesColumnFiltering + We MUST NOT Initialize the Data Table on DataTables.inc.tpl. + We got this effect with DataTablesOID="" +*} +{include file="DataTables.inc.tpl" DataTablesOID=""} +{include file="DataTablesColumnFiltering.inc.tpl" + DataTablesLengthMenu=$tlCfg->gui->usersAssign->pagination->length +} +{* ------------------------------------------------------------------------------------------------ *} + +{include file="bootstrap.inc.tpl"} @@ -85,7 +94,11 @@ function toggleRowByClass(oid,className,displayCheckOn,displayCheckOff,displayVa {include file="usermanagement/menu.inc.tpl"}
-{include file="inc_update.tpl" result=$result item="$gui->featureType" action="$action" user_feedback=$gui->user_feedback} +{include file="inc_update.tpl" + result=$result + item=$gui->featureType + action=$action + user_feedback=$gui->user_feedback} {* Because this page can be reloaded due to a test project change done by @@ -130,11 +143,6 @@ during refresh feature, and then we have a bad refresh on page getting a bug. {/foreach}
- {* - - *} -
{$labels.set_roles_to} 
- - - {assign var="featureVerbose" value=$gui->featureType} - - - {foreach from=$gui->users item=user} - {$globalRoleName=$user->globalRole->name} - {$uID=$user->dbID} - - - {* get role name to add to inherited in order to give better information to user *} - {$effective_role_id=$gui->userFeatureRoles[$uID].effective_role_id} - {if $gui->userFeatureRoles[$uID].is_inherited == 1} - {$ikx=$effective_role_id} - {else} - {$ikx=$gui->userFeatureRoles[$uID].uplayer_role_id} - {/if} - {$inherited_role_name=$gui->optRights[$ikx]->name} - - {$user_row_class=''} - {if $effective_role_id == $smarty.const.TL_ROLES_NO_RIGHTS} - {$user_row_class='class="not_authorized_user"'} - {/if} - - - -
{$tlImages.sort_hint}{$labels.User}{$tlImages.sort_hint}{lang_get s="th_roles_$featureVerbose"} ({$my_feature_name|escape})
role_colour != '' && $gui->role_colour[$globalRoleName] != ''} - style="background-color: {$gui->role_colour[$globalRoleName]};" {/if}> - {$user->login|escape} ({$user->firstName|escape} {$user->lastName|escape}) - + + + + {$featureVerbose=$gui->featureType} + + + + + {foreach from=$gui->users item=user} + {$globalRoleName=$user->globalRole->name} + {$uID=$user->dbID} + + + {* get role name to add to inherited in order to give better information to user *} + {$effective_role_id=$gui->userFeatureRoles[$uID].effective_role_id} + {if $gui->userFeatureRoles[$uID].is_inherited == 1} + {$ikx=$effective_role_id} + {else} + {$ikx=$gui->userFeatureRoles[$uID].uplayer_role_id} {/if} + {$inherited_role_name=$gui->optRights[$ikx]->name} - /* For system consistency we need to remove admin role from selection */ - {$removeRole = 0} - {if $role_id == $smarty.const.TL_ROLES_ADMIN && $applySelected == '' } - {$removeRole = 1} - {/if} - - {if !$removeRole } - + {$user_row_class=''} + {if $effective_role_id == $smarty.const.TL_ROLES_NO_RIGHTS} + {$user_row_class='class="not_authorized_user"'} {/if} - {/foreach} - - {if $user->globalRole->dbID == $smarty.const.TL_ROLES_ADMIN} - {$gui->hintImg} - {/if} - - - {/foreach} + + + + + {/foreach} +
{$labels.User}{lang_get s="th_roles_$featureVerbose"} ({$my_feature_name|escape})
role_colour != '' && $gui->role_colour[$globalRoleName] != ''} + style="background-color: {$gui->role_colour[$globalRoleName]};" {/if}> + {$user->login|escape} ({$user->firstName|escape} {$user->lastName|escape}) + + {if $user->globalRole->dbID == $smarty.const.TL_ROLES_ADMIN} + {$gui->hintImg} + {/if} +
diff --git a/gui/templates_c/.gitignore b/gui/templates_c/.gitignore index 127085b569..04a2e64a4a 100644 --- a/gui/templates_c/.gitignore +++ b/gui/templates_c/.gitignore @@ -3,3 +3,4 @@ /*.csv /*.xml /*.tmp* +/*.lock diff --git a/gui/themes/default/css/protection.css b/gui/themes/default/css/protection.css new file mode 100644 index 0000000000..1517e0e9b8 --- /dev/null +++ b/gui/themes/default/css/protection.css @@ -0,0 +1,31 @@ +/* +TestLink Open Source Project - http://testlink.sourceforge.net/ +@filesource protection.css + +manage a blocking/protection overlay + +links +https://www.w3schools.com/howto/howto_css_overlay.asp +https://stackoverflow.com/questions/44543682/disable-page-background-on-button-submit-click +*/ + +#overlay { + background-color: rgba(0, 0, 0, 0.8); + z-index: 999; + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + display: none; +} + +#text-for-overlay{ + position: absolute; + top: 50%; + left: 50%; + font-size: 50px; + color: white; + transform: translate(-50%,-50%); + -ms-transform: translate(-50%,-50%); +} \ No newline at end of file diff --git a/gui/themes/default/css/testlink.css b/gui/themes/default/css/testlink.css index 3445b8a4b6..03216f574c 100644 --- a/gui/themes/default/css/testlink.css +++ b/gui/themes/default/css/testlink.css @@ -200,13 +200,20 @@ present on page (example grid()) On chrome seems not work */ +/* @20200528 I've discovered a regression + the header wrap does not cover what is scrolling + under it. + I need to understand when was introduced + but adding background: white; is enough to fix it + */ #header-wrap { border-bottom: 1px solid #000; height: 200px; position: fixed; top: 0; width: 100%; - z-index: 999; + background: white; + /* z-index: 999;*/ } /* background for working area (tables and forms) */ @@ -361,7 +368,7 @@ fieldset.groupBtn form { /* ***** Help *************************************************************************** * This allows link help from text e.g. instruction - * Using: + * Using: * Test Specification */ img.help { @@ -958,7 +965,7 @@ div.login_form.medium { } -.exec_additional_info{ +.execAdditionalInfo{ background-color: #F0F4F0; /* whitesmoke=F0F4F0, #FFC*/ color: black; margin-bottom: 10px; diff --git a/gui/themes/default/images/asterisk_yellow.png b/gui/themes/default/images/asterisk_yellow.png new file mode 100644 index 0000000000..bab7cc9bcc Binary files /dev/null and b/gui/themes/default/images/asterisk_yellow.png differ diff --git a/gui/themes/default/images/bulkAssignTransparent.png b/gui/themes/default/images/bulkAssignTransparent.png new file mode 100644 index 0000000000..0528dfa24e Binary files /dev/null and b/gui/themes/default/images/bulkAssignTransparent.png differ diff --git a/gui/themes/default/images/gitlab.png b/gui/themes/default/images/gitlab.png new file mode 100644 index 0000000000..b1ca05fe6a Binary files /dev/null and b/gui/themes/default/images/gitlab.png differ diff --git a/gui/themes/default/images/test_status_passed_with_remarks.png b/gui/themes/default/images/test_status_passed_with_remarks.png new file mode 100644 index 0000000000..f54bf736cd Binary files /dev/null and b/gui/themes/default/images/test_status_passed_with_remarks.png differ diff --git a/gui/themes/default/images/test_status_passed_with_remarks_next.png b/gui/themes/default/images/test_status_passed_with_remarks_next.png new file mode 100644 index 0000000000..10137e55ca Binary files /dev/null and b/gui/themes/default/images/test_status_passed_with_remarks_next.png differ diff --git a/index.php b/index.php index 76494b8392..8adb2af836 100644 --- a/index.php +++ b/index.php @@ -1,122 +1,130 @@ -dbID = $_SESSION['currentUser']->dbID; - $user->readFromDB($db); - $dbSecurityCookie = $user->getSecurityCookie(); - $redir2login = ( $securityCookie != $dbSecurityCookie ); - } +dbID = $_SESSION['currentUser']->dbID; + $user->readFromDB($db); + $dbSecurityCookie = $user->getSecurityCookie(); + $redir2login = ($securityCookie != $dbSecurityCookie); + } +} + +if ($redir2login) { + // destroy user in session as security measure + unset($_SESSION['currentUser']); + + // If session does not exists I think is better in order to + // manage other type of authentication method/schemas + // to understand that this is a sort of FIRST Access. + // + // When TL undertand that session exists but has expired + // is OK to call login with expired indication, but is not this case + // + // Dev Notes: + // may be we are going to login.php and it will call us again! + $urlo = TL_BASE_HREF . "login.php" . ($args->ssodisable ? '?ssodisable' : ''); + redirect($urlo); + exit(); +} + +// We arrive to these lines only if we are logged in +// +// Calling testlinkInitPage() I'm doing what we do on navBar.php +// navBar.php is called via main.tpl +// testlinkInitPage($db,('initProject' == 'initProject')); + +$tplEngine = new TLSmarty(); +$tplEngine->assign('gui', $gui); +$tplEngine->display('main.tpl'); + +/** + * Initializes the environment + * + * @return stdClass[] object returns the arguments for the page + */ +function initEnv() +{ + $iParams = array( + "reqURI" => array( + tlInputParameter::STRING_N, + 0, + 4000 + ) + ); + $pParams = G_PARAMS($iParams); + + $args = new stdClass(); + $args->ssodisable = getSSODisable(); + + // CWE-79: + // Improper Neutralization of Input + // During Web Page Generation ('Cross-site Scripting') + // + // https://cxsecurity.com/issue/WLB-2019110139 + $args->reqURI = ''; + if ($pParams["reqURI"] != '') { + $args->reqURI = $pParams["reqURI"]; + + // some sanity checks + // strpos ( string $haystack , mixed $needle + if (stripos($args->reqURI, 'javascript') !== false) { + $args->reqURI = null; + } + } + if (null == $args->reqURI) { + $args->reqURI = 'lib/general/mainPage.php'; + } + $args->reqURI = $_SESSION['basehref'] . $args->reqURI; + + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : 0; + + $gui = new stdClass(); + $gui->title = lang_get('main_page_title'); + $gui->mainframe = $args->reqURI; + $gui->navbar_height = config_get('navbar_height'); + + $sso = ($args->ssodisable ? '&ssodisable' : ''); + $gui->titleframe = "lib/general/navBar.php?" . + "tproject_id={$args->tproject_id}&" . "tplan_id={$args->tplan_id}&" . + "updateMainPage=1" . $sso; + $gui->logout = 'logout.php?viewer=' . $sso; + + return array( + $args, + $gui + ); } - -if($redir2login) { - // destroy user in session as security measure - unset($_SESSION['currentUser']); - - // If session does not exists I think is better in order to - // manage other type of authentication method/schemas - // to understand that this is a sort of FIRST Access. - // - // When TL undertand that session exists but has expired - // is OK to call login with expired indication, but is not this case - // - // Dev Notes: - // may be we are going to login.php and it will call us again! - $urlo = TL_BASE_HREF . "login.php" . ($args->ssodisable ? '?ssodisable' : ''); - redirect($urlo); - exit; -} - - -// We arrive to these lines only if we are logged in -// -// Calling testlinkInitPage() I'm doing what we do on navBar.php -// navBar.php is called via main.tpl -// testlinkInitPage($db,('initProject' == 'initProject')); - -$tplEngine = new TLSmarty(); -$tplEngine->assign('gui', $gui); -$tplEngine->display('main.tpl'); - - -/** - * - * - */ -function initEnv() { - $iParams = array("reqURI" => array(tlInputParameter::STRING_N,0,4000)); - $pParams = G_PARAMS($iParams); - - $args = new stdClass(); - $args->ssodisable = getSSODisable(); - - // CWE-79: - // Improper Neutralization of Input - // During Web Page Generation ('Cross-site Scripting') - // - // https://cxsecurity.com/issue/WLB-2019110139 - $args->reqURI = ''; - if ($pParams["reqURI"] != '') { - $args->reqURI = $pParams["reqURI"]; - - // some sanity checks - // strpos ( string $haystack , mixed $needle - if (stripos($args->reqURI,'javascript') !== false) { - $args->reqURI = null; - } - } - if (null == $args->reqURI) { - $args->reqURI = 'lib/general/mainPage.php'; - } - $args->reqURI = $_SESSION['basehref'] . $args->reqURI; - - - - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : 0; - - $gui = new stdClass(); - $gui->title = lang_get('main_page_title'); - $gui->mainframe = $args->reqURI; - $gui->navbar_height = config_get('navbar_height'); - - $sso = ($args->ssodisable ? '&ssodisable' : ''); - $gui->titleframe = "lib/general/navBar.php?" . - "tproject_id={$args->tproject_id}&" . - "tplan_id={$args->tplan_id}&" . - "updateMainPage=1" . $sso; - $gui->logout = 'logout.php?viewer=' . $sso; - - return array($args,$gui); -} \ No newline at end of file diff --git a/install/index.php b/install/index.php index e8837bd24b..66b6be96db 100644 --- a/install/index.php +++ b/install/index.php @@ -13,9 +13,9 @@ */ if(!isset($tlCfg)) { - $tlCfg = new stdClass(); -} -require_once("../cfg/const.inc.php"); + $tlCfg = new stdClass(); +} +require_once '../cfg/const.inc.php'; session_start(); $_SESSION['session_test'] = 1; @@ -40,8 +40,8 @@

You are installing TestLink

Migration to latest version ( ) requires Database changes that has to be done MANUALLY. - Please read README file provided with installation.

-

For information about Migration from older version ( < 1.9.3 ) please read README file provided with installation.

+ Please read README file provided with installation.

+

For information about Migration from older version ( < 1.9.3 ) please read README file provided with installation.

Please read Section on README file or go to (Forum: TestLink 1.9.4 and greater News,changes, etc)

Open Installation manual for more information or troubleshooting. You could also look at @@ -57,18 +57,18 @@ TestLink Walkthrough
-

+


- TestLink is a complicated piece of software, and has always been released under - an Open Source license, and this will continue into the far future. -
It has cost thousands of hours to develop, test and support TestLink. -
If you find TestLink valuable, we would appreciate if you would consider - buying a support agreement or requesting custom development. + TestLink is a complicated piece of software, and has always been released under + an Open Source license, and this will continue into the far future. +
It has cost thousands of hours to develop, test and support TestLink. +
If you find TestLink valuable, we would appreciate if you would consider + buying a support agreement or requesting custom development.
 
- \ No newline at end of file + diff --git a/install/installCheck.php b/install/installCheck.php index 3dc028dd7b..7bd9f103b4 100644 --- a/install/installCheck.php +++ b/install/installCheck.php @@ -1,35 +1,35 @@ - absolutely wrong use as usual, used on installHead.inc +$inst_phase = 'checking'; // global variable -> absolutely wrong use as usual, used on installHead.inc $msg=''; -include 'installHead.inc'; +include_once 'installHead.inc'; ?>
-

TestLink will carry out a number of checks to see if everything's ready to start +

TestLink will carry out a number of checks to see if everything's ready to start the setup.

@@ -49,10 +49,10 @@ 0) { // Stop process because of error ?> -

Unfortunately, TestLink scripted setup cannot continue at the moment, due to the above - 1 ? $errors." " : "" ; ?>error 1 ? "s" : "" ; ?>. -
Please correct the error 1 ? "s" : "" ; ?>, - and try again (reload page). If you need help figuring out how to fix the +

Unfortunately, TestLink scripted setup cannot continue at the moment, due to the above + 1 ? $errors." " : "" ; ?>error 1 ? "s" : "" ; ?>. +
Please correct the error 1 ? "s" : "" ; ?>, + and try again (reload page). If you need help figuring out how to fix the problem 1 ? "s" : "" ; ?>, please read Installation manual and visit TestLink Forums [click here].

@@ -63,14 +63,14 @@ ?>
- +

Your system is prepared for TestLink configuration (no fatal problem found).

- \ No newline at end of file +include_once 'installFooter.inc'; +?> diff --git a/install/installDbInput.php b/install/installDbInput.php index 81bebdbcd9..b4ed524caf 100644 --- a/install/installDbInput.php +++ b/install/installDbInput.php @@ -1,30 +1,30 @@ - absolutely wrong use as usual, used on installHead.inc -include 'installHead.inc'; +$inst_phase = 'dbaccess'; // global variable -> absolutely wrong use as usual, used on installHead.inc +include_once 'installHead.inc'; ?>
@@ -64,7 +64,7 @@ function validate() { return false; } - if(f.tableprefix.value != "") + if(f.tableprefix.value != "") { if( f.tableprefix.value.search(/^[A-Za-z0-9_]*$/) == -1) { @@ -87,7 +87,7 @@ function validate() {
- '); ?> + '; ?> @@ -108,7 +108,7 @@ function validate() {
-

+

@@ -123,32 +123,36 @@ function validate() { - +

- +

Note: In the case that you DB connection dosn't use STANDARD PORT for , you need to add ':port_number', at the end Database host parameter. - Example: you use MySQL running on port 6606, on server matrix + Example: you use MySQL running on port 6606, on server matrix then Database host will be matrix:6606
-

-

Enter the name of the TestLink database
+

+

Enter the name of the TestLink database

- +

Disallowed characters in Database Name:
- The database name can contains any character that is allowed in + The database name can contains any character that is allowed in a directory name, except '/', '\', or '.'.

@@ -163,10 +167,10 @@ function validate() {
Note: This parameter should be empty for the most of cases.
Using a Database shared with other applications: - Testlink can be installed (using this installer) on a existing database + Testlink can be installed (using this installer) on a existing database used by another application, using a table prefix.
- Warning! PART OF INSTALLATION PROCESS CONSISTS - on dropping all TestLink tables present on the database/schema (if any TestLink table exists). + Warning! PART OF INSTALLATION PROCESS CONSISTS + on dropping all TestLink tables present on the database/schema (if any TestLink table exists). Backup your Database Before installing and load after this process.

@@ -199,23 +203,24 @@ function validate() {

- This user will have permission only to work on TestLink database and will be + This user will have permission only to work on TestLink database and will be stored in TestLink configuration.
All TestLink requests to the Database will be done with this user.

- ' . - 'login name: admin
password : admin'; + ' . 'login name: admin
password : admin'; + } ?>

- +

- + - \ No newline at end of file + diff --git a/install/installFooter.inc b/install/installFooter.inc index 56df987dba..a982a71631 100644 --- a/install/installFooter.inc +++ b/install/installFooter.inc @@ -1,13 +1,13 @@ - \ No newline at end of file + diff --git a/install/installHead.inc b/install/installHead.inc index d215d3306d..14e9f26d01 100644 --- a/install/installHead.inc +++ b/install/installHead.inc @@ -1,13 +1,13 @@ - +

Dot @@ -37,15 +37,15 @@ function getPhaseAttr($inst_phase, $phase)
    >Acceptance of License - >Verification of System and + >Verification of System and configuration requirements >Definition of DB access > + {echo "Create DB, testlink DB user, structures and default data & create configuration file.";} + else {echo "Update DB structures and migrate data.";} ?> - >Verify the procedure result and continue + >Verify the procedure result and continue to TestLink login.
diff --git a/install/installIntro.php b/install/installIntro.php index 0fb87c434c..f5cf6d8954 100644 --- a/install/installIntro.php +++ b/install/installIntro.php @@ -1,13 +1,13 @@

TestLink is developed and shared under GPL license. You are welcome to share your changes @@ -77,18 +77,18 @@

-

@@ -96,4 +96,4 @@ function ableButton() {

- \ No newline at end of file + diff --git a/install/installNewDB.php b/install/installNewDB.php index ec4f54f3d5..2e1a0b72c8 100644 --- a/install/installNewDB.php +++ b/install/installNewDB.php @@ -1,701 +1,663 @@ -isValid($dbHost)) { - // hostname is invalid; print the reasons - foreach ($validator->getMessages() as $message) { - echo "$message\n"; - } - die(); -} - -// Allows only certan kind of letters, numbers, minus, underscore -$san = '/[^A-Za-z0-9\-_]/'; - -$db_name = trim($_SESSION['databasename']); -$db_name = preg_replace($san,'',$db_name); - -$db_table_prefix = trim($_SESSION['tableprefix']); -$db_table_prefix = preg_replace($san,'',$db_table_prefix); - -$db_type = trim($_SESSION['databasetype']); -$db_type = preg_replace($san,'',$db_type); - -$db_admin_pass = trim($_SESSION['databaseloginpassword']); -$tl_db_passwd = trim($_SESSION['tl_loginpassword']); - - - - -// will limit length to avoi some kind of injection -// Choice: 32 -// Allows only certan kind of letters, numbers, minus, underscore -$tl_db_login = trim($_SESSION['tl_loginname']); -$tl_db_login = substr(preg_replace($san,'',$tl_db_login),0,32); - -$db_admin_name = trim($_SESSION['databaseloginname']); -$db_admin_name = substr(preg_replace($san,'',$db_admin_name),0,32); - - - -$sql_create_schema = array(); -$sql_create_schema[] = "sql/{$db_type}/testlink_create_tables.sql"; -$a_sql_schema = array(); -$a_sql_schema[] = $sql_create_schema; - -$sql_default_data = array(); -$sql_default_data [] = "sql/{$db_type}/testlink_create_default_data.sql"; -$a_sql_data = array(); -$a_sql_data[] = $sql_default_data; - - -global $g_tlLogger; -$g_tlLogger->disableLogging('db'); -$inst_type_verbose=" Installation "; - -$install = $_SESSION['isNew']; -$upgrade = !$install; -if ($upgrade) -{ - $inst_type_verbose=" Upgrade "; - $a_sql_data = array(); -} -$the_title = $_SESSION['title']; +isValid($dbHost)) { + // hostname is invalid; print the reasons + foreach ($validator->getMessages() as $message) { + echo "$message\n"; + } + die(); +} + +// Allows only certan kind of letters, numbers, minus, underscore +$san = '/[^A-Za-z0-9\-_]/'; + +$db_name = trim($_SESSION['databasename']); +$db_name = preg_replace($san, '', $db_name); + +$db_table_prefix = trim($_SESSION['tableprefix']); +$db_table_prefix = preg_replace($san, '', $db_table_prefix); + +$db_type = trim($_SESSION['databasetype']); +$db_type = preg_replace($san, '', $db_type); + +$db_admin_pass = trim($_SESSION['databaseloginpassword']); +$tl_db_passwd = trim($_SESSION['tl_loginpassword']); + +// will limit length to avoi some kind of injection +// Choice: 32 +// Allows only certan kind of letters, numbers, minus, underscore +$tl_db_login = trim($_SESSION['tl_loginname']); +$tl_db_login = substr(preg_replace($san, '', $tl_db_login), 0, 32); + +$db_admin_name = trim($_SESSION['databaseloginname']); +$db_admin_name = substr(preg_replace($san, '', $db_admin_name), 0, 32); + +$sql_create_schema = array(); +$sql_create_schema[] = "sql/{$db_type}/testlink_create_tables.sql"; +$a_sql_schema = array(); +$a_sql_schema[] = $sql_create_schema; + +$sql_default_data = array(); +$sql_default_data[] = "sql/{$db_type}/testlink_create_default_data.sql"; +$a_sql_data = array(); +$a_sql_data[] = $sql_default_data; + +global $g_tlLogger; +$g_tlLogger->disableLogging('db'); +$inst_type_verbose = " Installation "; + +$install = $_SESSION['isNew']; +$upgrade = ! $install; +if ($upgrade) { + $inst_type_verbose = " Upgrade "; + $a_sql_data = array(); +} +$the_title = $_SESSION['title']; ?> - <?php echo $the_title; ?> + <?php + +echo $the_title; +?> - + -

+
- - + + - + - - "; + if ($my['opt']['addCheck']) { + $check_name = str_replace('id="', 'name="check_', $dummy[0]); + $cf_smarty .= ""; + } + + $cf_smarty .= "\n"; + } + + $cf_smarty = $openTag . $cf_smarty . $closeTag; + } + return $cf_smarty; + } + + /** + * + * @used-by html_inputs(), html_table_inputs() + */ + private function getValuesFromUserInput($cf_map, $name_suffix = '', + $input_values = null) + { + if (! is_null($input_values)) { + $dateFormatDomain = config_get('locales_date_format'); + + // It will be better remove this coupling + $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; + $date_format = str_replace('%', '', $dateFormatDomain[$locale]); + + foreach ($cf_map as &$cf_info) { + $value = null; + $dtinname = null; + $verbose_type = trim( + $this->custom_field_types[$cf_info['type']]); + $cf_info['html_input_name'] = $this->buildHTMLInputName( + $cf_info, $name_suffix); + + switch ($verbose_type) { + case 'date': + $cf_info['html_input_name'] .= '_input'; + break; + + case 'datetime': + $dtinname = $cf_info['html_input_name']; + $cf_info['html_input_name'] .= '_input'; + break; + } + + if (isset($input_values[$cf_info['html_input_name']])) { + $value = $input_values[$cf_info['html_input_name']]; + } elseif (isset($input_values[$cf_info['id']])) { + $value = $input_values[$cf_info['id']]['value']; + } elseif (isset($cf_info['value'])) { + $value = $cf_info['value']; + } + + switch ($verbose_type) { + case 'date': + if (($value != 0) && ($value != '') && + ! is_numeric($value)) { + $parsed = split_localized_date($value, $date_format); + if ($parsed != null) { + $value = mktime(0, 0, 0, $parsed['month'], + $parsed['day'], $parsed['year']); + } else { + $value = ''; + } + } + break; + + case 'datetime': + if (($value != 0) && ($value != '') && + ! is_numeric($value)) { + $parsed = split_localized_date($value, $date_format); + if ($parsed != null) { + $vtime['hour'] = $input_values[$dtinname . + '_hour']; + $vtime['minute'] = $input_values[$dtinname . + '_minute']; + $vtime['second'] = $input_values[$dtinname . + '_second']; + + if ($vtime['hour'] == - 1 || + $vtime['minute'] == - 1 || + $vtime['second'] == - 1) { + $vtime['hour'] = $vtime['minute'] = $vtime['second'] = 0; + } + $value = mktime($vtime['hour'], $vtime['minute'], + $vtime['second'], $parsed['month'], + $parsed['day'], $parsed['year']); + } else { + $value = ''; + } + } + + break; + } + + if (! is_null($value) && is_array($value)) { + $value = implode("|", $value); + } + + $cf_info['value'] = $value; + } + } + return $cf_map; + } + + /** + */ + public function html_inputs($cfields_map, $name_suffix = '', + $input_values = null) + { + $inputSet = array(); + $getOpt = array( + 'name_suffix' => $name_suffix + ); + + if (! is_null($cfields_map)) { + $cf_map = $this->getValuesFromUserInput($cfields_map, $name_suffix, + $input_values); + + $NO_WARNING_IF_MISSING = true; + foreach ($cf_map as $cf_info) { + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, $NO_WARNING_IF_MISSING)); + + // IMPORTANT NOTICE + // assigning an ID with this format is CRITIC to Javascript logic used + // to validate input data filled by user according to CF type + // extract input html id + // Want to give an html id to "; - if($my['opt']['addCheck']) - { - $check_id = str_replace('id="', 'id="check_', $dummy[0]); - $check_name = str_replace('id="', 'name="check_', $dummy[0]); - $cf_smarty .= ""; - } - - $cf_smarty .= "\n"; - } - - $cf_smarty = $openTag . $cf_smarty . $closeTag; - } - return $cf_smarty; -} - - -/** - * - * @used-by html_inputs(), html_table_inputs() - */ -function getValuesFromUserInput($cf_map,$name_suffix='',$input_values=null) -{ - - if( !is_null($input_values) ) - { - $dateFormatDomain = config_get('locales_date_format'); - - // It will be better remove this coupling - $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; - $date_format = str_replace('%', '', $dateFormatDomain[$locale]); - - foreach($cf_map as &$cf_info) - { - $value = null; - $dtinname = null; - $verbose_type = trim($this->custom_field_types[$cf_info['type']]); - $cf_info['html_input_name'] = $this->buildHTMLInputName($cf_info,$name_suffix); - - switch($verbose_type) - { - case 'date': - $cf_info['html_input_name'] .= '_input'; - break; - - case 'datetime': - $dtinname = $cf_info['html_input_name']; - $cf_info['html_input_name'] .= '_input'; - break; - } - - if (isset($input_values[$cf_info['html_input_name']])) { - $value = $input_values[$cf_info['html_input_name']]; - } else if (isset($input_values[$cf_info['id']])) { - $value = $input_values[$cf_info['id']]['value']; - } else if (isset($cf_info['value'])) { - $value = $cf_info['value']; - } - - switch($verbose_type) - { - case 'date': - if ( ($value != 0) && ($value != '') && !is_numeric($value) ) - { - $parsed = split_localized_date($value, $date_format); - if($parsed != null) - { - $value = mktime(0,0,0,$parsed['month'],$parsed['day'], - $parsed['year']); - } - else - { - $value = ''; - } - } - break; - - case 'datetime': - if ( ($value != 0) && ($value != '') && !is_numeric($value) ) - { - $parsed = split_localized_date($value, $date_format); - if($parsed != null) - { - $vtime['hour'] = $input_values[$dtinname . '_hour']; - $vtime['minute'] = $input_values[$dtinname . '_minute']; - $vtime['second'] = $input_values[$dtinname . '_second']; - - if($vtime['hour'] == -1 || $vtime['minute'] == -1 || - $vtime['second'] == -1) - { - $vtime['hour'] = $vtime['minute'] = $vtime['second'] = 0; - } - $value = mktime($vtime['hour'], $vtime['minute'],$vtime['second'], - $parsed['month'],$parsed['day'],$parsed['year']); - } - else - { - $value = ''; - } - } - - - break; - - } - - if (!is_null($value) && is_array($value)) - { - $value = implode("|", $value); - } - - $cf_info['value'] = $value; - } - } - return $cf_map; -} - - - /** - * - * - */ - function html_inputs($cfields_map,$name_suffix='',$input_values=null) - { - $inputSet = array(); - $getOpt = array('name_suffix' => $name_suffix); - - if(!is_null($cfields_map)) - { - $cf_map = $this->getValuesFromUserInput($cfields_map,$name_suffix,$input_values); - - $NO_WARNING_IF_MISSING=true; - foreach($cf_map as $cf_id => $cf_info) - { - $label=str_replace(TL_LOCALIZE_TAG,'', - lang_get($cf_info['label'],null,$NO_WARNING_IF_MISSING)); - - - // IMPORTANT NOTICE - // assigning an ID with this format is CRITIC to Javascript logic used - // to validate input data filled by user according to CF type - // extract input html id - // Want to give an html id to
     
  
- + + - " . - ' + " . + ' - ' . - " + ' . + "
+
- 0 ) { - echo $check['msg']; - close_html_and_exit(); -} + 0) { + echo $check['msg']; + close_html_and_exit(); +} ?> TestLink setup will now attempt to setup the database:
-Creating connection to Database Server:"; - -// -------------------------------------------------------------------------- -// Connect to DB Server without choosing an specific database -switch($db_type) { - case 'mssql': - $dbDriverName = 'mssqlnative'; - break; - - default: - $dbDriverName = $db_type; - break; -} - -$db = new database($dbDriverName); -define('NO_DSN',FALSE); -@$conn_result = $db->connect(NO_DSN,$db_server, $db_admin_name, $db_admin_pass); - -if( $conn_result['status'] == 0 ) { - echo 'Failed!

Please check the database login details and try again.'; - echo '
Database Error Message: ' . $db->error_msg() . "
"; - - echo '
$db_server:' . $db_server . '
'; - echo '
$db_admin_name:' . $db_admin_name . '
'; - echo '
$db_admin_pass:' . $db_admin_pass . '
'; - - - close_html_and_exit(); -} else { - echo "OK!

"; -} -$db->close(); -$db=null; - - -// ------------------------------------------------------------------------- -// Connect to the Database (if Succesful -> database exists) -$db = new database($dbDriverName); - -@$conn_result = $db->connect(NO_DSN,$db_server, $db_admin_name, $db_admin_pass,$db_name); - -if( $conn_result['status'] == 0 ) { - $db->close(); - echo "
Database $db_name does not exist.
"; - - if( $upgrade ) { - echo "Can't Upgrade"; - close_html_and_exit(); - - $errors += 1; - } - else { - echo "Will attempt to create:"; - $create = true; - } -} -else { - echo "
Connecting to database `" . $db_name . "`:"; - echo "OK!"; -} -// ------------------- - - -// ------------------- -if($create) { - // check database name for invalid characters (now only for MySQL) - $db->close(); - $db = null; - - $db = New database($dbDriverName); - $conn_result=$db->connect(NO_DSN,$db_server, $db_admin_name, $db_admin_pass); - echo "
Creating database `" . $db_name . "`:"; - - // from MySQL Manual - // 9.2. Database, Table, Index, Column, and Alias Names - // - // Identifier : Database - // Maximum Length (bytes): 64 - // Allowed Characters : Any character that is allowed in a directory name, except '/', '\', or '.' - // - // An identifier may be quoted or unquoted. - // If an identifier is a reserved word or contains special characters, you must quote it whenever you refer to it. - // For a list of reserved words, see Section 9.6, �Treatment of Reserved Words in MySQL�. - // Special characters are those outside the set of alphanumeric characters from the current character set, - // '_', and '$'. - // The identifier quote character is the backtick ('`'): - // - // - // Postgres uses as identifier quote character " (double quotes): - $sql_create_db =$db->build_sql_create_db($db_name); - - if(!$db->exec_query($sql_create_db)) { - echo "Failed! - Could not create database: $db! " . - $db->error_msg(); - $errors += 1; - - echo "

TestLink setup could not create the database, " . - "and no existing database with the same name was found.
" . - "Please create a database by different way (e.g. from command line)," . - " or with different DB root account. Run setup again then."; - close_html_and_exit(); - } - else { - echo "OK!"; - } -} - -// in upgrade mode we detect the lenght of user password field -// to identify a version with uncrypted passwords -$tables = tlObject::getDBTables(); -$my_ado = $db->get_dbmgr_object(); -if ($upgrade) { - $user_table=$my_ado->MetaTables('TABLES',false,'user'); - if( count($user_table) == 1 ) { - $the_cols=$my_ado->MetaColumns('user'); - $pwd_field_len =$the_cols['PASSWORD']->max_length; - if ( $pwd_field_len == LEN_PWD_TL_1_0_4 ) { - $update_pwd=1; - echo "

You are trying to upgrade from a pre-release of TestLink 1.5" . - "
this kind of upgrade is supported by this script. Use upgrade to supported version " . - "at first.

"; - close_html_and_exit(); - } - } - // ------------------------------------------------------------- - - $a_sql_upd_dir=array(); - $a_sql_data_dir=array(); - - $the_version_table=$my_ado->MetaTables('TABLES',false,$db_table_prefix . 'db_version'); - if( count($the_version_table) == 0 ) { - echo "

You are trying to upgrade from a pre-release of TestLink 1.7" . - "
this kind of upgrade is supported by this script. Use upgrade to supported version " . - "at first.

"; - close_html_and_exit(); - } - else { - $migration_functions_file = ''; - $migration_process = ''; - - // try to know what db version is installed - // check if we need to use prefix but for some reason tlObjectWithDB::getDBTables - // have not returned prefix. - // - $dbVersionTable = $tables['db_version']; - if($dbVersionTable == 'db_version' && trim($db_table_prefix) != '') { - $dbVersionTable = $db_table_prefix . $dbVersionTable; - } - $sql = "SELECT * FROM {$dbVersionTable} ORDER BY upgrade_ts DESC"; - $res = $db->exec_query($sql); - if (!$res) { - echo "Database ERROR:" . $db->error_msg(); - exit(); - } - - $myrow = $db->fetch_array($res); - $schema_version=trim($myrow['version']); - - switch ($schema_version) { - case 'DB 1.2': - $a_sql_upd_dir[] = "sql/alter_tables/1.9/{$db_type}/DB.1.3/step1/"; - $a_sql_data_dir[] = "sql/alter_tables/1.9/{$db_type}/DB.1.3/stepZ/"; - $migration_process = 'migrate_18_to_19'; - $migration_functions_file = './migration/migrate_18/migrate_18_to_19.php'; - break; - - case 'DB 1.3': - echo "

Your DB Schema {$schema_version} NEED TO BE upgraded, but you have to do "; - echo " this MANUALLY using a SQL client and scripts you will find on "; - echo " directory install/sql/alter_tables/1.9.1 "; - echo "

"; - close_html_and_exit(); - break; - - case 'DB 1.4': - echo "

Your DB Schema {$schema_version} NEED TO BE upgraded, but you have to do "; - echo " this MANUALLY using a SQL client and scripts you will find on "; - echo " directory install/sql/alter_tables/1.9.4 "; - echo "

"; - close_html_and_exit(); - break; - - case 'DB 1.5': - echo "

Your DB Schema {$schema_version} is the last available, then you don't need to do any upgrade."; - echo "
Script is finished.

"; - close_html_and_exit(); - break; - - default: - if( strlen($schema_version) == 0 ) - { - echo "

Information of DB schema version is missing. Don't know how to upgrade.

"; - } - else - { - echo "

This script doesn't recognize your schema version: " . $schema_version . "

"; - } - echo "

Upgrade is not possible. Check your input data (Go back in page history).

"; - close_html_and_exit(); - break; - } - } - - $a_sql_schema = getDirSqlFiles($a_sql_upd_dir,ADD_DIR); - $a_sql_data = getDirSqlFiles($a_sql_data_dir,ADD_DIR); -} - - -// ------------------------------------------------------------------------ -// Now proceed with user checks and user creation (if needed) -// -// Added support for different types of architecture/installations: -// webserver and dbserver on same machines => user will be created as user -// webserver and dbserver on DIFFERENT machines => user must be created as user@webserver -// -// if @ in tl_db_login (username) -> get the hostname using splitting, and use it -// during user creation on db. -$db->close(); -$db=null; -$user_host = explode('@',$tl_db_login); -$msg = create_user_for_db($dbDriverName,$db_name, $db_server, - $db_admin_name, $db_admin_pass, - $tl_db_login, $tl_db_passwd); - -echo "
Creating Testlink DB user `" . $user_host[0] . "`:"; -if ( strpos($msg,'ok -') === FALSE ) { - echo "Failed! - Could not create user: $tl_db_login!"; - $errors += 1; -} -else { - echo "OK! ($msg) "; -} - - -// ------------------------------------------------------------------------ -// Schema Operations (CREATE, ALTER, ecc). -// Important: -// Postgres: do it as tl_login NOT as db_admin -// MySQL : do it as db_admin NOT as tl_login -if( !is_null($db) ) { - $db->close(); - $db=null; -} - -$db = new database($dbDriverName); -switch($db_type) { - case 'mssql': - @$conn_result = $db->connect(NO_DSN, $db_server, $db_admin_name, $db_admin_pass, $db_name); - break; - - case 'mysql': - @$conn_result = $db->connect(NO_DSN, $db_server, $db_admin_name, $db_admin_pass, $db_name); - break; - - case 'postgres': - @$conn_result = $db->connect(NO_DSN, $db_server, $tl_db_login, $tl_db_passwd, $db_name); - break; -} - -// ------------------------------------------------------------------------------------ -if( $install && $conn_result['status'] != 0 ) { - drop_views($db,$db_table_prefix,$db_type); - drop_tables($db,$db_table_prefix,$db_type); -} - - -// ------------------------------------------------------------------------------- -$sqlParser = new SqlParser($db,$db_type,$db_table_prefix); -foreach($a_sql_schema as $sql_schema) { - foreach ($sql_schema as $sql_file) { - echo "
Processing:" . $sql_file; - $sqlParser->process($sql_file); - } - echo "
"; -} - -// Now data migration must be done if needed -if( $migration_process != '' ) { - require_once($migration_functions_file); - $migration_process($db,$tables); -} - -// ------------------------------------------------- -// Data Operations -if ( count($a_sql_data > 0) ) { - foreach($a_sql_data as $sql_data ) { - if ( count($sql_data > 0) ) { - foreach ($sql_data as $sql_file) { - $sqlParser->process($sql_file); - } - } - } -} - - -// ------------------------------------------------- -if ($update_pwd) { - echo "Password Conversion ..."; - // @author Francisco Mancardi - 20050918 - // Found error upgrading from 1.0.4 to 1.6 on RH - // due to case sensitive on table name. (USER) - - $user_pwd = "UPDATE user SET password=MD5(password)"; - $result = $db->exec_query($user_pwd); -} - - -if($sqlParser->install_failed==true) -{ - echo "Failed! - {$inst_type_verbose} failed!"; - $errors += 1; - - echo "

" . - "TestLink setup couldn't install the default site into the selected database. " . - "The last error to occur was " . $sqlParser->sql_errors[count($sqlParser->sql_errors)-1]["error"] . - ' during the execution of SQL statement ' . - strip_tags($sqlParser->sql_errors[count($sqlParser->sql_errors)-1]["sql"]). ""; - - close_html_and_exit(); -} -else -{ - echo "OK!"; -} - -// ----------------------------------------------------------------------------- -echo "
Writing configuration file:"; -$data['db_host']=$db_server; -$data['db_login'] = $user_host[0]; -$data['db_passwd'] = $tl_db_passwd; -$data['db_name'] = $db_name; -$data['db_type'] = $db_type; -$data['db_table_prefix'] = $db_table_prefix; - - -$cfg_file = "../config_db.inc.php"; -$yy = write_config_db($cfg_file,$data); -// ----------------------------------------------------------------------------- - - -if(strcasecmp('ko', $yy['status']) == 0) { - echo "Failed!"; - $errors += 1; - - echo "

" . - "TestLink couldn't write the config file. Please copy the following into the " . - ' ' . $cfg_file . ' file:
' . - '"; - - echo "Once that's been done, you can log into TestLink by pointing your browser at your TestLink site."; - - close_html_and_exit(); -} -else -{ - echo "OK!"; -} - - -manual_operations($db_type); - -important_reminder(); - -// When testlink is updated do not show login data admin/admin -// as they might not exist -$successfull_message = '


'; - -echo $successfull_message; - -$db->close(); -close_html_and_exit(); - - - -/** - * - * - */ -function manual_operations($dbType) { - - echo '

IMPORTANT NOTICE - IMPORTANT NOTICE - IMPORTANT NOTICE - IMPORTANT NOTICE

'; - - echo ''; - //echo 'IMPORTANT NOTICE - IMPORTANT NOTICE - IMPORTANT NOTICE - IMPORTANT NOTICE'; - //echo ''; - - echo '
'; - echo '

YOU NEED TO RUN MANUALLY Following Script on your DB CLIENT Application

'; - echo '

'; - echo '

' . dirname(__FILE__) . '/sql/'. $dbType . '/testlink_create_udf0.sql'; - echo '


THANKS A LOT

'; -} - -// ----------------------------------------------------------- -function write_config_db($filename, $data) -{ - $ret = array('status' => 'ok', 'cfg_string' => ''); - - $db_host = $data['db_host']; - $db_login = $data['db_login']; - // if @ present in db_login, explode an take user name WITHOUT HOST - $user_host = explode('@',$db_login); - - if (count($user_host) > 1 ) - { - $db_login = $user_host[0]; - } - - $db_passwd = $data['db_passwd']; - $db_name = $data['db_name']; - $db_type = $data['db_type']; - $db_table_prefix = $data['db_table_prefix']; - - // write config.inc.php - $configString = "get_dbmgr_object(); - $tablesOnDB =$my_ado->MetaTables('TABLES'); - if( count($tablesOnDB) > 0 && isset($tablesOnDB[0])) - { - echo "
Dropping all TL existent tables:
"; - foreach($schema as $tablePlainName => $tableFullName) - { - $targetTable = $dbTablePrefix . $tablePlainName; - if( in_array($targetTable,$tablesOnDB) ) - { - // Need to add option (CASCADE ?) to delete dependent object - echo "Droping $targetTable" . "
"; - $sql="DROP TABLE $targetTable"; - $sql .= (($dbType != 'mssql') && ($dbType != 'sqlsrv')) ? " CASCADE " : ' '; - $dbHandler->exec_query($sql); - } - } - echo "Done!"; - } -} - -function drop_views(&$dbHandler,$dbItemPrefix,$dbType) -{ - $schema = tlObjectWithDB::getDBViews(); - - // views present on target db - $my_ado = $dbHandler->get_dbmgr_object(); - $itemsOnDB =$my_ado->MetaTables('VIEWS'); - if( count($itemsOnDB) > 0 && isset($itemsOnDB[0])) - { - echo "
Dropping all TL existent views:
"; - foreach($schema as $itemPlainName => $itemFullName) - { - $target = $dbItemPrefix . $itemPlainName; - if( in_array($target,$itemsOnDB) ) - { - // Need to add option (CASCADE ?) to delete dependent object - echo "Droping $target" . "
"; - $sql="DROP VIEW $target"; - $sql .= (($dbType != 'mssql') && ($dbType != 'sqlsrv')) ? " CASCADE " : ' '; - $dbHandler->exec_query($sql); - } - } - echo "Done!"; - } +Creating connection to Database Server:"; + +// Connect to DB Server without choosing an specific database +switch ($db_type) { + case 'mssql': + $dbDriverName = 'mssqlnative'; + break; + + default: + $dbDriverName = $db_type; + break; +} + +$db = new database($dbDriverName); +define('NO_DSN', false); +@$conn_result = $db->connect(NO_DSN, $db_server, $db_admin_name, $db_admin_pass); + +if ($conn_result['status'] == 0) { + echo 'Failed!

Please check the database login details and try again.'; + echo '
Database Error Message: ' . $db->error_msg() . "
"; + + echo '
$db_server:' . $db_server . '
'; + echo '
$db_admin_name:' . $db_admin_name . '
'; + echo '
$db_admin_pass:' . $db_admin_pass . '
'; + + close_html_and_exit(); +} else { + echo "OK!

"; +} +$db->close(); +$db = null; + +// Connect to the Database (if Succesful -> database exists) +$db = new database($dbDriverName); + +@$conn_result = $db->connect(NO_DSN, $db_server, $db_admin_name, $db_admin_pass, + $db_name); + +if ($conn_result['status'] == 0) { + $db->close(); + echo "
Database $db_name does not exist.
"; + + if ($upgrade) { + echo "Can't Upgrade"; + close_html_and_exit(); + + $errors += 1; + } else { + echo "Will attempt to create:"; + $create = true; + } +} else { + echo "
Connecting to database `" . $db_name . "`:"; + echo "OK!"; +} + +if ($create) { + // check database name for invalid characters (now only for MySQL) + $db->close(); + $db = null; + + $db = new database($dbDriverName); + $conn_result = $db->connect(NO_DSN, $db_server, $db_admin_name, + $db_admin_pass); + echo "
Creating database `" . $db_name . "`:"; + + // from MySQL Manual + // 9.2. Database, Table, Index, Column, and Alias Names + // + // Identifier : Database + // Maximum Length (bytes): 64 + // Allowed Characters : Any character that is allowed in a directory name, except '/', '\', or '.' + // + // An identifier may be quoted or unquoted. + // If an identifier is a reserved word or contains special characters, you must quote it whenever you refer to it. + // For a list of reserved words, see Section 9.6, �Treatment of Reserved Words in MySQL�. + // Special characters are those outside the set of alphanumeric characters from the current character set, + // '_', and '$'. + // The identifier quote character is the backtick ('`'): + // + // + // Postgres uses as identifier quote character " (double quotes): + $sql_create_db = $db->build_sql_create_db($db_name); + + if (! $db->exec_query($sql_create_db)) { + echo "Failed! - Could not create database: $db! " . + $db->error_msg(); + $errors += 1; + + echo "

TestLink setup could not create the database, " . + "and no existing database with the same name was found.
" . + "Please create a database by different way (e.g. from command line)," . + " or with different DB root account. Run setup again then."; + close_html_and_exit(); + } else { + echo "OK!"; + } +} + +// in upgrade mode we detect the lenght of user password field +// to identify a version with uncrypted passwords +$tables = tlObject::getDBTables(); +$my_ado = $db->get_dbmgr_object(); +if ($upgrade) { + $user_table = $my_ado->MetaTables('TABLES', false, 'user'); + if (count($user_table) == 1) { + $the_cols = $my_ado->MetaColumns('user'); + $pwd_field_len = $the_cols['PASSWORD']->max_length; + if ($pwd_field_len == LEN_PWD_TL_1_0_4) { + $update_pwd = 1; + echo "

You are trying to upgrade from a pre-release of TestLink 1.5" . + "
this kind of upgrade is supported by this script. Use upgrade to supported version " . + "at first.

"; + close_html_and_exit(); + } + } + + $a_sql_upd_dir = array(); + $a_sql_data_dir = array(); + + $the_version_table = $my_ado->MetaTables('TABLES', false, + $db_table_prefix . 'db_version'); + if (count($the_version_table) == 0) { + echo "

You are trying to upgrade from a pre-release of TestLink 1.7" . + "
this kind of upgrade is supported by this script. Use upgrade to supported version " . + "at first.

"; + close_html_and_exit(); + } else { + $migration_functions_file = ''; + $migration_process = ''; + + // try to know what db version is installed + // check if we need to use prefix but for some reason tlObjectWithDB::getDBTables + // have not returned prefix. + // + $dbVersionTable = $tables['db_version']; + if ($dbVersionTable == 'db_version' && trim($db_table_prefix) != '') { + $dbVersionTable = $db_table_prefix . $dbVersionTable; + } + $sql = "SELECT * FROM {$dbVersionTable} ORDER BY upgrade_ts DESC"; + $res = $db->exec_query($sql); + if (! $res) { + echo "Database ERROR:" . $db->error_msg(); + exit(); + } + + $myrow = $db->fetch_array($res); + $schema_version = trim($myrow['version']); + + switch ($schema_version) { + case 'DB 1.2': + $a_sql_upd_dir[] = "sql/alter_tables/1.9/{$db_type}/DB.1.3/step1/"; + $a_sql_data_dir[] = "sql/alter_tables/1.9/{$db_type}/DB.1.3/stepZ/"; + $migration_process = 'migrate_18_to_19'; + $migration_functions_file = './migration/migrate_18/migrate_18_to_19.php'; + break; + + case 'DB 1.3': + echo "

Your DB Schema {$schema_version} NEED TO BE upgraded, but you have to do "; + echo " this MANUALLY using a SQL client and scripts you will find on "; + echo " directory install/sql/alter_tables/1.9.1 "; + echo "

"; + close_html_and_exit(); + break; + + case 'DB 1.4': + echo "

Your DB Schema {$schema_version} NEED TO BE upgraded, but you have to do "; + echo " this MANUALLY using a SQL client and scripts you will find on "; + echo " directory install/sql/alter_tables/1.9.4 "; + echo "

"; + close_html_and_exit(); + break; + + case 'DB 1.5': + echo "

Your DB Schema {$schema_version} is the last available, then you don't need to do any upgrade."; + echo "
Script is finished.

"; + close_html_and_exit(); + break; + + default: + if (strlen($schema_version) == 0) { + echo "

Information of DB schema version is missing. Don't know how to upgrade.

"; + } else { + echo "

This script doesn't recognize your schema version: " . + $schema_version . "

"; + } + echo "

Upgrade is not possible. Check your input data (Go back in page history).

"; + close_html_and_exit(); + break; + } + } + + $a_sql_schema = getDirSqlFiles($a_sql_upd_dir, ADD_DIR); + $a_sql_data = getDirSqlFiles($a_sql_data_dir, ADD_DIR); +} + +// Now proceed with user checks and user creation (if needed) +// +// Added support for different types of architecture/installations: +// webserver and dbserver on same machines => user will be created as user +// webserver and dbserver on DIFFERENT machines => user must be created as user@webserver +// +// if @ in tl_db_login (username) -> get the hostname using splitting, and use it +// during user creation on db. +$db->close(); +$db = null; +$user_host = explode('@', $tl_db_login); +$msg = create_user_for_db($dbDriverName, $db_name, $db_server, $db_admin_name, + $db_admin_pass, $tl_db_login, $tl_db_passwd); + +echo "
Creating Testlink DB user `" . $user_host[0] . "`:"; +if (strpos($msg, 'ok -') === false) { + echo "Failed! - Could not create user: $tl_db_login!"; + $errors += 1; +} else { + echo "OK! ($msg) "; +} + +// Schema Operations (CREATE, ALTER, ecc). +// Important: +// Postgres: do it as tl_login NOT as db_admin +// MySQL : do it as db_admin NOT as tl_login +if (! is_null($db)) { + $db->close(); + $db = null; +} + +$db = new database($dbDriverName); +switch ($db_type) { + case 'mssql': + @$conn_result = $db->connect(NO_DSN, $db_server, $db_admin_name, + $db_admin_pass, $db_name); + break; + + case 'mysql': + @$conn_result = $db->connect(NO_DSN, $db_server, $db_admin_name, + $db_admin_pass, $db_name); + break; + + case 'postgres': + @$conn_result = $db->connect(NO_DSN, $db_server, $tl_db_login, + $tl_db_passwd, $db_name); + break; +} + +if ($install && $conn_result['status'] != 0) { + drop_views($db, $db_table_prefix, $db_type); + drop_tables($db, $db_table_prefix, $db_type); +} + +$sqlParser = new SqlParser($db, $db_type, $db_table_prefix); +foreach ($a_sql_schema as $sql_schema) { + foreach ($sql_schema as $sql_file) { + echo "
Processing:" . $sql_file; + $sqlParser->process($sql_file); + } + echo "
"; +} + +// Now data migration must be done if needed +if ($migration_process != '') { + require_once $migration_functions_file; + $migration_process($db, $tables); +} + +// Data Operations +if (! empty($a_sql_data)) { + foreach ($a_sql_data as $sql_data) { + if (! empty($sql_data)) { + foreach ($sql_data as $sql_file) { + $sqlParser->process($sql_file); + } + } + } +} + +if ($update_pwd) { + echo "Password Conversion ..."; + // @author Francisco Mancardi - 20050918 + // Found error upgrading from 1.0.4 to 1.6 on RH + // due to case sensitive on table name. (USER) + + $user_pwd = "UPDATE user SET password=MD5(password)"; + $result = $db->exec_query($user_pwd); +} + +if ($sqlParser->install_failed) { + echo "Failed! - {$inst_type_verbose} failed!"; + $errors += 1; + + echo "

" . + "TestLink setup couldn't install the default site into the selected database. " . + "The last error to occur was " . + $sqlParser->sql_errors[count($sqlParser->sql_errors) - 1]["error"] . + ' during the execution of SQL statement ' . + strip_tags( + $sqlParser->sql_errors[count($sqlParser->sql_errors) - 1]["sql"]) . + ""; + + close_html_and_exit(); +} else { + echo "OK!"; +} + +echo "
Writing configuration file:"; +$data['db_host'] = $db_server; +$data['db_login'] = $user_host[0]; +$data['db_passwd'] = $tl_db_passwd; +$data['db_name'] = $db_name; +$data['db_type'] = $db_type; +$data['db_table_prefix'] = $db_table_prefix; + +$cfg_file = "../config_db.inc.php"; +$yy = write_config_db($cfg_file, $data); + +if (strcasecmp('ko', $yy['status']) == 0) { + echo "Failed!"; + $errors += 1; + + echo "

" . + "TestLink couldn't write the config file. Please copy the following into the " . + ' ' . $cfg_file . ' file:
' . + '"; + + echo "Once that's been done, you can log into TestLink by pointing your browser at your TestLink site."; + + close_html_and_exit(); +} else { + echo "OK!"; +} + +manual_operations($db_type); + +important_reminder(); + +// When testlink is updated do not show login data admin/admin +// as they might not exist +$successfull_message = '


'; + +echo $successfull_message; + +$db->close(); +close_html_and_exit(); + +/** + */ +function manual_operations($dbType) +{ + echo '

IMPORTANT NOTICE - IMPORTANT NOTICE - IMPORTANT NOTICE - IMPORTANT NOTICE

'; + echo ''; + echo '
'; + echo '

YOU NEED TO RUN MANUALLY Following Script on your DB CLIENT Application

'; + echo '

'; + echo '

' . dirname(__FILE__) . '/sql/' . $dbType . + '/testlink_create_udf0.sql'; + echo '


THANKS A LOT

'; +} + +function write_config_db($filename, $data) +{ + $ret = array( + 'status' => 'ok', + 'cfg_string' => '' + ); + + $db_host = $data['db_host']; + $db_login = $data['db_login']; + // if @ present in db_login, explode an take user name WITHOUT HOST + $user_host = explode('@', $db_login); + + if (count($user_host) > 1) { + $db_login = $user_host[0]; + } + + $db_passwd = $data['db_passwd']; + $db_name = $data['db_name']; + $db_type = $data['db_type']; + $db_table_prefix = $data['db_table_prefix']; + + // write config.inc.php + $configString = "get_dbmgr_object(); + $tablesOnDB = $my_ado->MetaTables('TABLES'); + if (! empty($tablesOnDB) && isset($tablesOnDB[0])) { + echo "
Dropping all TL existent tables:
"; + foreach ($schema as $tablePlainName => $tableFullName) { + $targetTable = $dbTablePrefix . $tablePlainName; + if (in_array($targetTable, $tablesOnDB)) { + // Need to add option (CASCADE ?) to delete dependent object + echo "Dropping $targetTable" . "
"; + $sql = "DROP TABLE $targetTable"; + $sql .= (($dbType != 'mssql') && ($dbType != 'sqlsrv')) ? " CASCADE " : ' '; + $dbHandler->exec_query($sql); + } + } + echo "Done!"; + } +} + +function drop_views(&$dbHandler, $dbItemPrefix, $dbType) +{ + $schema = tlObjectWithDB::getDBViews(); + + // views present on target db + $my_ado = $dbHandler->get_dbmgr_object(); + $itemsOnDB = $my_ado->MetaTables('VIEWS'); + if (! empty($itemsOnDB) && isset($itemsOnDB[0])) { + echo "
Dropping all TL existent views:
"; + foreach ($schema as $itemPlainName => $itemFullName) { + $target = $dbItemPrefix . $itemPlainName; + if (in_array($target, $itemsOnDB)) { + // Need to add option (CASCADE ?) to delete dependent object + echo "Dropping $target" . "
"; + $sql = "DROP VIEW $target"; + $sql .= (($dbType != 'mssql') && ($dbType != 'sqlsrv')) ? " CASCADE " : ' '; + $dbHandler->exec_query($sql); + } + } + echo "Done!"; + } } diff --git a/install/installStart.php b/install/installStart.php index 0bdbfae1ed..b5400e1d41 100644 --- a/install/installStart.php +++ b/install/installStart.php @@ -13,4 +13,4 @@ echo "No installationType found in \$_GET."; } -?> \ No newline at end of file +?> diff --git a/install/installUtils.php b/install/installUtils.php index 041ee7066b..77026e0248 100644 --- a/install/installUtils.php +++ b/install/installUtils.php @@ -1,982 +1,900 @@ - Eventum - Issue Tracking System -*/ -function getTableList($db) -{ - $my_ado = $db->get_dbmgr_object(); - $tables = $my_ado->MetaTables('TABLES',false,'db_version'); - return($tables); -} - - - -/* - function: getUserList - - args: - - returns: map or null - - rev : - -*/ -function getUserList(&$db,$db_type) -{ - $users=null; - switch($db_type) - { - case 'mysql': - $result = $db->exec_query('SELECT DISTINCT user AS user FROM user'); - break; - - case 'postgres': - $result = $db->exec_query('SELECT DISTINCT usename AS user FROM pg_user'); - break; - - case 'mssql': - case 'mssqlnative': - // info about running store procedures, get form adodb manuals - // Important: - // From ADODB manual - Prepare() documentation - // - // Returns an array containing the original sql statement in the first array element; - // the remaining elements of the array are driver dependent. - // - // Looking into adodb-mssql.inc.php, you will note that array[1] - // is a mssql stm object. - // This info is very important, to use mssql_free_statement() - // - $stmt = $db->db->PrepareSP('SP_HELPLOGINS'); # note that the parameter name does not have @ in front! - $result = $db->db->Execute($stmt); - - // Very important: - // Info from PHP Manual notes - // mssql_free_statement() - // - // mitch at 1800radiator dot kom (23-Mar-2005 06:02) - // Maybe it's unique to my FreeTDS configuration, but if - // I don't call mssql_free_statement() - // after every stored procedure (i.e. mssql_init, mssql_bind, - // mssql_execute, mssql_fetch_array), - // all subsequent stored procedures on the same database - // connection will fail. - // I only mention it because this man-page deprecates - // the use of mssql_free_statement(), - // saying it's only there for run-time memory concerns. - // At least in my case, it's also a crucial step in the - // process of running a stored procedure. - // If anyone else has problems running multiple stored - // procedures on the same connection, - // I hope this helps them out. - // - // Without this was not possible to call other functions - // that use store procedures, - // because I've got: - // a) wrong results - // b) mssql_init() errors - // - if( is_resource($stmt) ) { - if (function_exists('mssql_free_statement')) { - mssql_free_statement($stmt[1]); - } - else { - sqlsrv_free_stmt($stmt[1]); - } - } - break; - - } - - $users = array(); - - // MySQL NOTE: - // if the user cannot select from the mysql.user table, then return an empty list - // - if (!$result) - { - return $users; - } - if( $db_type == 'mssql' ) - { - while (!$result->EOF) - { - $row = $result->GetRowAssoc(); - - // seems that on newer SQL Server Version - // Camel Case is used, or may be ADODB behaviour has changed - // Anyway this check avoid issues - // - if( isset($row['LOGINNAME']) ) { - $uk = 'LOGINNAME'; - } - if( isset($row['LoginName']) ) { - $uk = 'LoginName'; - } - - $users[] = trim($row[$uk]); - $result->MoveNext(); - } - } - else - { - while ($row = $db->fetch_array($result)) - { - $users[] = trim($row['user']); - } - } - return($users); -} - - - -/* -Function: create_user_for_db - - Check for user existence. - - If doesn't exist - Creates a user/passwd with the following GRANTS: SELECT, UPDATE, DELETE, INSERT - for the database - Else - do nothing - - -20051217 - fm -refactoring - cosmetics changes - -20050910 - fm -webserver and dbserver on same machines => user will be created as user -webserver and dbserver on DIFFERENT machines => user must be created as user@webserver - -if @ in login -> get the hostname using splitting, and use it - during user creation on db. - - -*/ -function create_user_for_db($db_type,$db_name,$db_server, $db_admin_name, $db_admin_pass, - $login, $passwd) { -$db = new database($db_type); - -$user_host = explode('@',$login); -$the_host = 'localhost'; - -if ( count($user_host) > 1 ) { - $login = $user_host[0]; - $the_host = trim($user_host[1]); -} - -$try_create_user=0; -switch($db_type) { - - case 'mssql': - @$conn_res = $db->connect(NO_DSN, $db_server, $db_admin_name, $db_admin_pass,$db_name); - $msg="For MSSQL, no attempt is made to check for user existence"; - $try_create_user=1; - break; - - case 'postgres': - @$conn_res = $db->connect(NO_DSN, $db_server, $db_admin_name, $db_admin_pass,$db_name); - $try_create_user=1; - break; - - case 'mysql': - case 'mysqli': - @$conn_res = $db->connect(NO_DSN, $db_server, $db_admin_name, $db_admin_pass, 'mysql'); - $try_create_user=1; - break; - - default: - $try_create_user=0; - break; - -} - -if( $try_create_user==1) -{ - $user_list = getUserList($db,$db_type); - $login_lc = strtolower($login); - $msg = "ko - fatal error - can't get db server user list !!!"; -} - -if ($try_create_user==1 && !is_null($user_list) && count($user_list) > 0) -{ - - $user_list = array_map('strtolower', $user_list); - $user_exists=in_array($login_lc, $user_list); - if (!$user_exists) - { - $msg = ''; - switch($db_type) - { - - case 'mssql': - $op = _mssql_make_user_with_grants($db,$the_host,$db_name,$login,$passwd); - _mssql_set_passwd($db,$login,$passwd); - break; - - case 'postgres': - $op = _postgres_make_user_with_grants($db,$the_host,$db_name,$login,$passwd); - break; - - case 'mysql': - case 'mysqli': - default: - // Starting with MySQL 8 the following sentence is WRONG !! - // for MySQL making the user and assign right is the same operation - // But I've modified _mysql_make_user in order to create user - // and assign rights - $op = _mysql_make_user($db,$the_host,$db_name,$login,$passwd); - break; - - } - } - else - { - // just assign rights on the database - $msg = 'ok - user_exists'; - switch($db_type) { - case 'mysql': - case 'mysqli': - $op = _mysql_assign_grants($db,$the_host,$db_name,$login,$passwd); - break; - - case 'postgres': - $op = _postgres_assign_grants($db,$the_host,$db_name,$login,$passwd); - break; - - case 'mssql': - $op = _mssql_assign_grants($db,$the_host,$db_name,$login,$passwd); - break; - } - - } - if( !$op->status_ok ) - { - $msg .= " but ..."; - } - $msg .= " " . $op->msg; - - -} - -if( !is_null($db) ) -{ - $db->close(); -} - -return($msg); -} - - -/* - function: close_html_and_exit() - - args : - - returns: - -*/ -function close_html_and_exit() -{ -echo " + Eventum - Issue Tracking System + */ +function getTableList($db) +{ + $my_ado = $db->get_dbmgr_object(); + return $my_ado->MetaTables('TABLES', false, 'db_version'); +} + +/* + * function: getUserList + * + * args: + * + * returns: map or null + * + * rev : + * + */ +function getUserList(&$db, $db_type) +{ + $users = null; + switch ($db_type) { + case 'mysql': + $result = $db->exec_query('SELECT DISTINCT user AS user FROM user'); + break; + + case 'postgres': + $result = $db->exec_query( + 'SELECT DISTINCT usename AS user FROM pg_user'); + break; + + case 'mssql': + case 'mssqlnative': + // info about running store procedures, get form adodb manuals + // Important: + // From ADODB manual - Prepare() documentation + // + // Returns an array containing the original sql statement in the first array element; + // the remaining elements of the array are driver dependent. + // + // Looking into adodb-mssql.inc.php, you will note that array[1] + // is a mssql stm object. + // This info is very important, to use mssql_free_statement() + // + $stmt = $db->db->PrepareSP('SP_HELPLOGINS'); # note that the parameter name does not have @ in front! + $result = $db->db->Execute($stmt); + + // Very important: + // Info from PHP Manual notes + // mssql_free_statement() + // + // mitch at 1800radiator dot kom (23-Mar-2005 06:02) + // Maybe it's unique to my FreeTDS configuration, but if + // I don't call mssql_free_statement() + // after every stored procedure (i.e. mssql_init, mssql_bind, + // mssql_execute, mssql_fetch_array), + // all subsequent stored procedures on the same database + // connection will fail. + // I only mention it because this man-page deprecates + // the use of mssql_free_statement(), + // saying it's only there for run-time memory concerns. + // At least in my case, it's also a crucial step in the + // process of running a stored procedure. + // If anyone else has problems running multiple stored + // procedures on the same connection, + // I hope this helps them out. + // + // Without this was not possible to call other functions + // that use store procedures, + // because I've got: + // a) wrong results + // b) mssql_init() errors + // + if (is_resource($stmt)) { + if (function_exists('mssql_free_statement')) { + mssql_free_statement($stmt[1]); + } else { + sqlsrv_free_stmt($stmt[1]); + } + } + break; + } + + $users = array(); + + // MySQL NOTE: + // if the user cannot select from the mysql.user table, then return an empty list + // + if (! $result) { + return $users; + } + if ($db_type == 'mssql') { + while (! $result->EOF) { + $row = $result->GetRowAssoc(); + + // seems that on newer SQL Server Version + // Camel Case is used, or may be ADODB behaviour has changed + // Anyway this check avoid issues + // + if (isset($row['LOGINNAME'])) { + $uk = 'LOGINNAME'; + } + if (isset($row['LoginName'])) { + $uk = 'LoginName'; + } + + $users[] = trim($row[$uk]); + $result->MoveNext(); + } + } else { + while ($row = $db->fetch_array($result)) { + $users[] = trim($row['user']); + } + } + return $users; +} + +/* + * Function: create_user_for_db + * + * Check for user existence. + * + * If doesn't exist + * Creates a user/passwd with the following GRANTS: SELECT, UPDATE, DELETE, INSERT + * for the database + * Else + * do nothing + * + * + * 20051217 - fm + * refactoring - cosmetics changes + * + * 20050910 - fm + * webserver and dbserver on same machines => user will be created as user + * webserver and dbserver on DIFFERENT machines => user must be created as user@webserver + * + * if @ in login -> get the hostname using splitting, and use it + * during user creation on db. + * + * + */ +function create_user_for_db($db_type, $db_name, $db_server, $db_admin_name, + $db_admin_pass, $login, $passwd) +{ + $db = new database($db_type); + + $user_host = explode('@', $login); + $the_host = 'localhost'; + + if (count($user_host) > 1) { + $login = $user_host[0]; + $the_host = trim($user_host[1]); + } + + $try_create_user = 0; + switch ($db_type) { + + case 'mssql': + @$conn_res = $db->connect(NO_DSN, $db_server, $db_admin_name, + $db_admin_pass, $db_name); + $msg = "For MSSQL, no attempt is made to check for user existence"; + $try_create_user = 1; + break; + + case 'postgres': + @$conn_res = $db->connect(NO_DSN, $db_server, $db_admin_name, + $db_admin_pass, $db_name); + $try_create_user = 1; + break; + + case 'mysql': + case 'mysqli': + @$conn_res = $db->connect(NO_DSN, $db_server, $db_admin_name, + $db_admin_pass, 'mysql'); + $try_create_user = 1; + break; + + default: + $try_create_user = 0; + break; + } + + if ($try_create_user == 1) { + $user_list = getUserList($db, $db_type); + $login_lc = strtolower($login); + $msg = "ko - fatal error - can't get db server user list !!!"; + } + + if ($try_create_user == 1 && ! empty($user_list)) { + + $user_list = array_map('strtolower', $user_list); + $user_exists = in_array($login_lc, $user_list); + if (! $user_exists) { + $msg = ''; + switch ($db_type) { + + case 'mssql': + $op = _mssql_make_user_with_grants($db, $the_host, $db_name, + $login, $passwd); + _mssql_set_passwd($db, $login, $passwd); + break; + + case 'postgres': + $op = _postgres_make_user_with_grants($db, $the_host, + $db_name, $login, $passwd); + break; + + case 'mysql': + case 'mysqli': + default: + // Starting with MySQL 8 the following sentence is WRONG !! + // for MySQL making the user and assign right is the same operation + // But I've modified _mysql_make_user in order to create user + // and assign rights + $op = _mysql_make_user($db, $the_host, $db_name, $login, + $passwd); + break; + } + } else { + // just assign rights on the database + $msg = 'ok - user_exists'; + switch ($db_type) { + case 'mysql': + case 'mysqli': + $op = _mysql_assign_grants($db, $the_host, $db_name, $login, + $passwd); + break; + + case 'postgres': + $op = _postgres_assign_grants($db, $the_host, $db_name, + $login, $passwd); + break; + + case 'mssql': + $op = _mssql_assign_grants($db, $the_host, $db_name, $login, + $passwd); + break; + } + } + if (! $op->status_ok) { + $msg .= " but ..."; + } + $msg .= " " . $op->msg; + } + + if (! is_null($db)) { + $db->close(); + } + + return $msg; +} + +/* + * function: close_html_and_exit() + * + * args : + * + * returns: + * + */ +function close_html_and_exit() +{ + echo "
  
 
-"; - -exit; -} - - -// check to see if required PEAR modules are installed -function check_pear_modules() -{ - $errors = 0; - $final_msg = '
Checking if PEAR modules are installed:'; - - // SpreadSheet_Excel_Writer is needed for TestPlanResultsObj that does excel reporting - if(false == include_once('Spreadsheet/Excel/Writer.php')) - { - $final_msg .= 'Failed! - Spreadsheet_Excel_Writer PEAR Module is required.
See' . - '' . - 'http://pear.php.net/package/Spreadsheet_Excel_Writer for additional information'; - $errors += 1; - } - else - { - $final_msg .= "OK!"; - } - - $ret = array('errors' => $errors, 'msg' => $final_msg); - - return $ret; -} - - -/* - function: check_db_loaded_extension - args : - returns: - - rev : - -*/ -function check_db_loaded_extension($db_type) { - $dbType2PhpExtension = array('postgres' => 'pgsql'); - - $isPHPGTE7 = version_compare(phpversion(), "7.0.0", ">="); - - $ext2search = $db_type; - if( $ext2search == 'mysql' && $isPHPGTE7) { - $ext2search = 'mysqli'; - } - - if(PHP_OS == 'WINNT' || $isPHPGTE7 ) { - - // First Time: - // - // Faced this problem when testing XAMPP 1.7.7 on - // Windows 7 with MSSQL 2008 Express - // - // From PHP MANUAL - reganding mssql_* functions - // These functions allow you to access MS SQL Server database. - // This extension is not available anymore on Windows with - // PHP 5.3 or later. - // - // SQLSRV, an alternative driver for MS SQL is available from Microsoft: - // http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx. - // - // - // Second Time: (2018) - // When using PHP 7 or up - // Help from Bitnami - // PHP 7 does not support mssql anymore. - // The PECL extension recommended is to use the "sqlsrv" module - // but you will need to compile it on your own. - // - // - // PHP_VERSION_ID is available as of PHP 5.2.7 - if ( defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50300){ - $dbType2PhpExtension['mssql'] = 'sqlsrv'; - } - - if ( $isPHPGTE7 ){ - $dbType2PhpExtension['mssql'] = 'sqlsrv'; - } - } - - if( isset($dbType2PhpExtension[$db_type]) ) { - $ext2search=$dbType2PhpExtension[$db_type]; - } - - $msg_ko = "Failed!"; - $msg_ok = 'OK!'; - $tt = array_flip(get_loaded_extensions()); - - $errors=0; - $final_msg = "

Checking PHP DB extensions "; - - if( !isset($tt[$ext2search]) ) { - $final_msg .= "Warning!: Your PHP installation don't have the {$db_type} extension {$ext2search} " . - "without it is IMPOSSIBLE to use Testlink."; - $final_msg .= $msg_ko; - $errors += 1; - } else { - $final_msg .= $msg_ok; - } - - $ret = array ('errors' => $errors, 'msg' => $final_msg); - - return $ret; -} - - - - - -/** - * - * - */ -function _mysql_make_user($dbhandler,$db_host,$db_name,$login,$passwd) { - - $op = new stdclass(); - - $op->status_ok = true; - $op->msg = 'ok - new user'; - - // Escaping following rules form: - // - // MySQL Manual - // 9.2. Database, Table, Index, Column, and Alias Names - // - $safeDBHost = $dbhandler->prepare_string($db_host); - $safeDBName = $dbhandler->prepare_string($db_name); - $safeLogin = $dbhandler->prepare_string($login); - - $stmt = " CREATE USER '$safeLogin' "; - if (strlen(trim($db_host)) != 0) { - $stmt .= "@" . "'$safeDBHost'"; - } - - // to guess if we are using MariaDB or MySQL - // does not seems to be a reliable way to do this - // - $sql = "SHOW VARIABLES LIKE 'version%'"; - $vg = array(); - $rh = $dbhandler->exec_query($sql); - if ($rh) { - while($row = $dbhandler->fetch_array($rh)) { - $vg[$row['Variable_name']] = $row['Value']; - } - } - - $isMariaDB = false; - $isMySQL = false; - foreach ($vg as $vn => $vv) { - if (strripos($vv,'MariaDB') !== FALSE) { - $isMariaDB = true; - break; - } - if (strripos($vv,'MySQL') !== FALSE) { - $isMySQL = true; - break; - } - } - - // To have compatibility with MySQL 5.x - // IDENTIFIED WITH mysql_native_password - if ($isMySQL) { - $stmt .= - " IDENTIFIED WITH mysql_native_password BY '$passwd' "; - } - - if ($isMariaDB) { - $stmt .= - " IDENTIFIED BY '$passwd' "; - } - - echo 'Running..' . $stmt; - if (!@$dbhandler->exec_query($stmt)) { - $op->msg = "ko - " . $dbhandler->error_msg(); - $op->status_ok=false; - } else { - // Assign Grants!! - $op = _mysql_assign_grants($dbhandler,$db_host,$db_name,$login,$passwd); - } - - return $op; -} - - -/** - * - */ -function _mysql_assign_grants($dbhandler,$db_host,$db_name,$login,$passwd) { - - $op->status_ok = true; - $op->msg = 'ok - new user'; - - // Escaping following rules form: - // - // MySQL Manual - // 9.2. Database, Table, Index, Column, and Alias Names - // - $safeDBHost = $dbhandler->prepare_string($db_host); - $safeDBName = $dbhandler->prepare_string($db_name); - $safeLogin = $dbhandler->prepare_string($login); - - $stmt = "GRANT SELECT, UPDATE, DELETE, INSERT ON - `$safeDBName`.* TO '$safeLogin'@'$safeDBHost' - WITH GRANT OPTION "; - - if ( !@$dbhandler->exec_query($stmt) ) { - $op->msg = "ko - " . $dbhandler->error_msg(); - $op->status_ok=false; - } - - // found that you get access denied in this situation: - // 1. you have create the user with grant for host. - // 2. you are running your app on host. - // 3. you don't have GRANT for localhost. - // - // Then I've decide to grant always access from localhost - // to avoid this kind of problem. - // I hope this is not a security hole. - // - // - if( strcasecmp('localhost',$db_host) != 0 ) { - $stmt = "GRANT SELECT, UPDATE, DELETE, INSERT ON - `$safeDBName`.* TO '$safeLogin'@'localhost' - WITH GRANT OPTION "; - - if ( !@$dbhandler->exec_query($stmt) ) { - $op->msg = "ko - " . $dbhandler->error_msg(); - $op->status_ok=false; - } - } - - if( $op->status_ok) { - $op->msg = 'ok - grant assignment'; - } - - return ($op); -} - - -/* - function: _postgres_make_user_with_grants - - args : - - returns: - -*/ -function _postgres_make_user_with_grants(&$db,$db_host,$db_name,$login,$passwd) -{ -$op->status_ok=true; -$op->msg=''; - -$int_op = _postgres_make_user($db,$db_host,$db_name,$login,$passwd); - -if( $int_op->status_ok) -{ - $op->msg = $int_op->msg; - $int_op = _postgres_assign_grants($db,$db_host,$db_name,$login,$passwd); - - $op->msg .= " " . $int_op->msg; - $op->status_ok=$int_op->status_ok; -} - -return($op); -} // function end - - -/* - function: _postgres_make_user - - args : - - returns: - -*/ -function _postgres_make_user(&$db,$db_host,$db_name,$login,$passwd) -{ -$op->status_ok=true; -$op->msg = 'ok - new user'; - -$sql = 'CREATE USER "' . $db->prepare_string($login) . '"' . " ENCRYPTED PASSWORD '{$passwd}'"; -if (!@$db->exec_query($sql)) -{ - $op->status_ok=false; - $op->msg = "ko - " . $db->error_msg(); -} -return ($op); -} - - - -/* - function: _postgres_assign_grants - - args : - - returns: - -*/ -function _postgres_assign_grants(&$db,$db_host,$db_name,$login,$passwd) -{ - $op = new stdclass(); - $op->status_ok=true; - $op->msg = 'ok - grant assignment'; - - /* - if( $op->status_ok ) - { - $sql=" REVOKE ALL ON SCHEMA public FROM public "; - if (!@$dbhandler->exec_query($sql)) - { - $op->status_ok=false; - $op->msg = "ko - " . $dbhandler->error_msg(); - } - } - */ - - if( $op->status_ok ) - { - $sql = 'ALTER DATABASE "' . $db->prepare_string($db_name) . '" OWNER TO ' . - '"' . $db->prepare_string($login) . '"'; - if (!@$db->exec_query($sql)) - { - $op->status_ok=false; - $op->msg = "ko - " . $db->error_msg(); - } - } - - if( $op->status_ok ) - { - $sql = 'ALTER SCHEMA public OWNER TO ' . '"' . $db->prepare_string($login) . '"'; - if (!@$db->exec_query($sql)) - { - $op->status_ok=false; - $op->msg = "ko - " . $db->error_msg(); - } - } - - return ($op); -} - - -/* - function: _mssql_make_user_with_grants - - args : - - returns: - -*/ -function _mssql_make_user_with_grants($db,$the_host,$db_name,$login,$passwd) -{ - _mssql_make_user($db,$the_host,$db_name,$login,$passwd); - - $op->status_ok=true; - $op->msg = 'ok - new user'; - - // Check if has been created, because I'm not able to get return code. - $user_list=getUserList($db,'mssql'); - $user_list=array_map('strtolower', $user_list); - $user_exists=in_array(trim($login), $user_list); - if( !$user_exists ) - { - $op->status_ok=false; - $op->msg = "ko - " . $db->error_msg(); - } - else - { - _mssql_assign_grants($db,$the_host,$db_name,$login,$passwd); -} - return $op; - -} // function end - - -function _mssql_make_user($db,$the_host,$db_name,$login,$passwd) -{ - -// Transact-SQL Reference -// -// sp_addlogin -// New Information - SQL Server 2000 SP3. -// -// Creates a new Microsoft® SQL Server™ login that allows a user -// to connect to an instance of SQL Server using SQL Server Authentication. -// -// Security Note When possible, use Windows Authentication. -// -// Syntax -// sp_addlogin [ @loginame = ] 'login' -// [ , [ @passwd = ] 'password' ] -// [ , [ @defdb = ] 'database' ] -// [ , [ @deflanguage = ] 'language' ] -// [ , [ @sid = ] sid ] -// [ , [ @encryptopt = ] 'encryption_option' ] -// -// Arguments -// [@loginame =] 'login' -// Is the name of the login. login is sysname, with no default. -// -// [@passwd =] 'password' -// Is the login password. password is sysname, with a default of NULL. -// After sp_addlogin has been executed, the password is encrypted and stored in the system tables. -// -// [@defdb =] 'database' -// Is the default database of the login (the database the login is connected to after logging in). -// database is sysname, with a default of master. -// -// [@deflanguage =] 'language' -// Is the default language assigned when a user logs on to SQL Server. -// language is sysname, with a default of NULL. -// If language is not specified, language is set to the server's current default language -// (defined by the sp_configure configuration variable default language). -// Changing the server's default language does not change the default language for existing logins. -// language remains the same as the default language used when the login was added. -// -// [@sid =] sid -// Is the security identification number (SID). sid is varbinary(16), with a default of NULL. -// If sid is NULL, the system generates a SID for the new login. -// Despite the use of a varbinary data type, values other than NULL must be -// exactly 16 bytes in length, and must not already exist. -// SID is useful, for example, when you are scripting or moving SQL Server logins -// from one server to another and you want the logins to have the same SID between servers. -// -// [@encryptopt =] 'encryption_option' -// Specifies whether the password is encrypted when stored in the system tables. -// encryption_option is varchar(20), and can be one of these values. -// -// Value Description -// NULL The password is encrypted. This is the default. -// skip_encryption The password is already encrypted. -// SQL Server should store the value without re-encrypting it. -// skip_encryption_old The supplied password was encrypted by a previous version of SQL Server. -// SQL Server should store the value without re-encrypting it. -// This option is provided for upgrade purposes only. -// -// -// Return Code Values -// 0 (success) or 1 (failure) -// -// Permissions -// Only members of the sysadmin and securityadmin fixed server roles can execute sp_addlogin. -// -// Examples -// A. Create a login ID with master default database -// This example creates an SQL Server login for the user Victoria, without specifying a default database. -// -// EXEC sp_addlogin 'Victoria', 'B1r12-36' -// -// B. Create a login ID and default database -// This example creates a SQL Server login for the user Albert, with a password of "B1r12-36" -// and a default database of corporate. -// -// EXEC sp_addlogin 'Albert', 'B1r12-36', 'corporate' -// -// C. Create a login ID with a different default language -// This example creates an SQL Server login for the user Claire Picard, with a password of "B1r12-36", -// a default database of public_db, and a default language of French. -// -// EXEC sp_addlogin 'Claire Picard', 'B1r12-36', 'public_db', 'french' -// -// D. Create a login ID with a specific SID -// This example creates an SQL Server login for the user Michael, with a password of "B1r12-36," -// a default database of pubs, a default language of us_english, -// and an SID of 0x0123456789ABCDEF0123456789ABCDEF. -// -// EXEC sp_addlogin 'Michael', 'B1r12-36', 'pubs', 'us_english', 0x0123456789ABCDEF0123456789ABCDEF -// -// E. Create a login ID and do not encrypt the password -// This example creates an SQL Server login for the user Margaret with a password of "B1r12-36" on Server1, -// extracts the encrypted password, and then adds the login for the user Margaret to Server2 using -// the previously encrypted password but does not further encrypt the password. -// User Margaret can then log on to Server2 using the password Rose. - - - $op->status_ok=true; - $op->msg = 'ok - new user'; - - //sp_addlogin [ @loginame = ] 'login' - // [ , [ @passwd = ] 'password' ] - // [ , [ @defdb = ] 'database' ] - // [ , [ @deflanguage = ] 'language' ] - // [ , [ @sid = ] sid ] - // [ , [ @encryptopt = ] 'encryption_option' ] - // - // Important: - // From ADODB manual - Prepare() documentation - // - // Returns an array containing the original sql statement in the first array element; - // the remaining elements of the array are driver dependent. - // - // 20071104 - franciscom - // Looking into adodb-mssql.inc.php, you will note that array[1] - // is a mssql stm object. - // This info is very important, to use mssql_free_statement() - // - - $sid=null; - $encryptopt=null; - - $stmt = $db->db->PrepareSP('SP_ADDLOGIN'); - $db->db->InParameter($stmt,$login,'loginame'); - // $db->db->InParameter($stmt,$passwd,'passwd'); - $db->db->InParameter($stmt,$db_name,'defdb'); - // $db->db->InParameter($stmt,$sid,'sid'); - // $db->db->InParameter($stmt,$encryptopt,'encryptopt'); - - $db->db->OutParameter($stmt,$retval,'RETVAL'); - $result=$db->db->Execute($stmt); - - // Very important: - // Info from PHP Manual notes - // mssql_free_statement() - // - // mitch at 1800radiator dot kom (23-Mar-2005 06:02) - // Maybe it's unique to my FreeTDS configuration, but if I don't call mssql_free_statement() - // after every stored procedure (i.e. mssql_init, mssql_bind, mssql_execute, mssql_fetch_array), - // all subsequent stored procedures on the same database connection will fail. - // I only mention it because this man-page deprecates the use of mssql_free_statement(), - // saying it's only there for run-time memory concerns. - // At least in my case, it's also a crucial step in the process of running a stored procedure. - // If anyone else has problems running multiple stored procedures on the same connection, - // I hope this helps them out. - // - // franciscom - 20071104 - // Without this was not possible to call other functions that use store procedures, - // because I've got: - // a) wrong results - // b) mssql_init() errors - // - mssql_free_statement($stmt[1]); - - // I've problems trying to set password, - // then I will use as workaround setting a NULL password - // and after do a password change. - $passwd_null=NULL; - $stmt = $db->db->PrepareSP('SP_PASSWORD'); - $db->db->InParameter($stmt,$login,'loginame'); - $db->db->InParameter($stmt,$passwd_null,'old'); - $db->db->InParameter($stmt,$passwd,'new'); - $result=$db->db->Execute($stmt); - mssql_free_statement($stmt[1]); - - -} // function end - - -/* - function: _mssql_assign_grants - - args : - - returns: - -*/ -function _mssql_assign_grants($db,$the_host,$db_name,$login,$passwd) -{ - - // $stmt = $db->db->PrepareSP('SP_GRANTDBACCESS'); - // $db->db->InParameter($stmt,$login,'loginame'); - // $result=$db->db->Execute($stmt); - // mssql_free_statement($stmt[1]); - // - $db_role='db_owner'; - $stmt = $db->db->PrepareSP('SP_ADDUSER'); - $db->db->InParameter($stmt,$login,'loginame'); - $db->db->InParameter($stmt,$login,'name_in_db'); - $db->db->InParameter($stmt,$db_role,'grpname'); - $result=$db->db->Execute($stmt); - mssql_free_statement($stmt[1]); - - - $op = new stdClass(); - $op->status_ok=true; - $op->msg = 'ok - grant assignment'; - - return $op; -} // function end - -/* - function: - - args : - - returns: - -*/ -function _mssql_set_passwd($db,$login,$passwd) -{ - // $passwd_null=NULL; - //$stmt = $db->db->PrepareSP('SP_PASSWORD'); - //$db->db->InParameter($stmt,$login,'loginame'); - //$db->db->InParameter($stmt,$passwd,'old'); - //$db->db->InParameter($stmt,$passwd,'new'); - //$result=$db->db->Execute($stmt); - // - //// echo "
debug 20071104 - \ - " . __FUNCTION__ . " --- "; print_r($result); echo "
"; - //mssql_free_statement($stmt[1]); - - //$sql="EXEC SP_PASSWORD '{$passwd}','{$passwd}',{$login}"; - $sql="EXEC SP_PASSWORD NULL,'{$passwd}',{$login}"; - $db->exec_query($sql); - - -} - -/** - * - */ -function important_reminder() -{ - echo '

YOUR ATTENTION PLEASE:
To have a fully functional installation +"; + + exit(); +} + +// check to see if required PEAR modules are installed +function check_pear_modules() +{ + $errors = 0; + $final_msg = '

Checking if PEAR modules are installed:'; + + // SpreadSheet_Excel_Writer is needed for TestPlanResultsObj that does excel reporting + if (! include_once 'Spreadsheet/Excel/Writer.php') { + $final_msg .= 'Failed! - Spreadsheet_Excel_Writer PEAR Module is required.
See' . + '' . + 'http://pear.php.net/package/Spreadsheet_Excel_Writer for additional information'; + $errors += 1; + } else { + $final_msg .= "OK!"; + } + + return array( + 'errors' => $errors, + 'msg' => $final_msg + ); +} + +/* + * function: check_db_loaded_extension + * args : + * returns: + * + * rev : + * + */ +function check_db_loaded_extension($db_type) +{ + $dbType2PhpExtension = array( + 'postgres' => 'pgsql' + ); + + $isPHPGTE7 = version_compare(phpversion(), "7.0.0", ">="); + + $ext2search = $db_type; + if ($ext2search == 'mysql' && $isPHPGTE7) { + $ext2search = 'mysqli'; + } + + // PHP_OS constant + // keep in mind this constant will contain + // the operating system PHP was built on + // + if (PHP_OS == 'WINNT' || $isPHPGTE7) { + + // First Time: + // + // Faced this problem when testing XAMPP 1.7.7 on + // Windows 7 with MSSQL 2008 Express + // + // From PHP MANUAL - reganding mssql_* functions + // These functions allow you to access MS SQL Server database. + // This extension is not available anymore on Windows with + // PHP 5.3 or later. + // + // SQLSRV, an alternative driver for MS SQL is available from Microsoft: + // http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx. + // + // + // Second Time: (2018) + // When using PHP 7 or up + // Help from Bitnami + // PHP 7 does not support mssql anymore. + // The PECL extension recommended is to use the "sqlsrv" module + // but you will need to compile it on your own. + // + // + // PHP_VERSION_ID is available as of PHP 5.2.7 + if (defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50300) { + $dbType2PhpExtension['mssql'] = 'sqlsrv'; + } + + if ($isPHPGTE7) { + $dbType2PhpExtension['mssql'] = 'sqlsrv'; + } + } + + if (isset($dbType2PhpExtension[$db_type])) { + $ext2search = $dbType2PhpExtension[$db_type]; + } + + $msg_ko = "Failed!"; + $msg_ok = 'OK!'; + $tt = array_flip(get_loaded_extensions()); + + $errors = 0; + $final_msg = "

Checking PHP DB extensions "; + + if (! isset($tt[$ext2search])) { + $final_msg .= "Warning!: Your PHP installation don't have the {$db_type} extension {$ext2search} " . + "without it is IMPOSSIBLE to use Testlink."; + $final_msg .= $msg_ko; + $errors += 1; + } else { + $final_msg .= $msg_ok; + } + + return array( + 'errors' => $errors, + 'msg' => $final_msg + ); +} + +/** + */ +function _mysql_make_user($dbhandler, $db_host, $db_name, $login, $passwd) +{ + $op = new stdclass(); + + $op->status_ok = true; + $op->msg = 'ok - new user'; + + // Escaping following rules form: + // + // MySQL Manual + // 9.2. Database, Table, Index, Column, and Alias Names + // + $safeDBHost = $dbhandler->prepare_string($db_host); + $safeLogin = $dbhandler->prepare_string($login); + + $stmt = " CREATE USER '$safeLogin' "; + if (strlen(trim($db_host)) != 0) { + $stmt .= "@" . "'$safeDBHost'"; + } + + // to guess if we are using MariaDB or MySQL + // does not seems to be a reliable way to do this + // + $sql = "SHOW VARIABLES LIKE 'version%'"; + $vg = array(); + $rh = $dbhandler->exec_query($sql); + if ($rh) { + while ($row = $dbhandler->fetch_array($rh)) { + $vg[$row['Variable_name']] = $row['Value']; + } + } + + $isMariaDB = false; + $isMySQL = false; + foreach ($vg as $vv) { + if (strripos($vv, 'MariaDB') !== false) { + $isMariaDB = true; + break; + } + if (strripos($vv, 'MySQL') !== false) { + $isMySQL = true; + break; + } + } + + // To have compatibility with MySQL 5.x + // IDENTIFIED WITH mysql_native_password + if ($isMySQL) { + $stmt .= " IDENTIFIED WITH mysql_native_password BY '$passwd' "; + } + + if ($isMariaDB) { + $stmt .= " IDENTIFIED BY '$passwd' "; + } + + echo 'Running..' . $stmt; + if (! @$dbhandler->exec_query($stmt)) { + $op->msg = "ko - " . $dbhandler->error_msg(); + $op->status_ok = false; + } else { + // Assign Grants!! + $op = _mysql_assign_grants($dbhandler, $db_host, $db_name, $login, + $passwd); + } + + return $op; +} + +/** + */ +function _mysql_assign_grants($dbhandler, $db_host, $db_name, $login, $passwd) +{ + $op = new stdClass(); + $op->status_ok = true; + $op->msg = 'ok - new user'; + + // Escaping following rules form: + // + // MySQL Manual + // 9.2. Database, Table, Index, Column, and Alias Names + // + $safeDBHost = $dbhandler->prepare_string($db_host); + $safeDBName = $dbhandler->prepare_string($db_name); + $safeLogin = $dbhandler->prepare_string($login); + + $stmt = "GRANT SELECT, UPDATE, DELETE, INSERT ON + `$safeDBName`.* TO '$safeLogin'@'$safeDBHost' + WITH GRANT OPTION "; + + if (! @$dbhandler->exec_query($stmt)) { + $op->msg = "ko - " . $dbhandler->error_msg(); + $op->status_ok = false; + } + + // found that you get access denied in this situation: + // 1. you have create the user with grant for host. + // 2. you are running your app on host. + // 3. you don't have GRANT for localhost. + // + // Then I've decide to grant always access from localhost + // to avoid this kind of problem. + // I hope this is not a security hole. + // + // + if (strcasecmp('localhost', $db_host) != 0) { + $stmt = "GRANT SELECT, UPDATE, DELETE, INSERT ON + `$safeDBName`.* TO '$safeLogin'@'localhost' + WITH GRANT OPTION "; + + if (! @$dbhandler->exec_query($stmt)) { + $op->msg = "ko - " . $dbhandler->error_msg(); + $op->status_ok = false; + } + } + + if ($op->status_ok) { + $op->msg = 'ok - grant assignment'; + } + + return $op; +} + +/* + * function: _postgres_make_user_with_grants + * + * args : + * + * returns: + * + */ +function _postgres_make_user_with_grants(&$db, $db_host, $db_name, $login, + $passwd) +{ + $op->status_ok = true; + $op->msg = ''; + + $int_op = _postgres_make_user($db, $db_host, $db_name, $login, $passwd); + + if ($int_op->status_ok) { + $op->msg = $int_op->msg; + $int_op = _postgres_assign_grants($db, $db_host, $db_name, $login, + $passwd); + + $op->msg .= " " . $int_op->msg; + $op->status_ok = $int_op->status_ok; + } + + return $op; +} + +// function end + +/* + * function: _postgres_make_user + * + * args : + * + * returns: + * + */ +function _postgres_make_user(&$db, $db_host, $db_name, $login, $passwd) +{ + $op->status_ok = true; + $op->msg = 'ok - new user'; + + $sql = 'CREATE USER "' . $db->prepare_string($login) . '"' . + " ENCRYPTED PASSWORD '{$passwd}'"; + if (! @$db->exec_query($sql)) { + $op->status_ok = false; + $op->msg = "ko - " . $db->error_msg(); + } + return $op; +} + +/* + * function: _postgres_assign_grants + * + * args : + * + * returns: + * + */ +function _postgres_assign_grants(&$db, $db_host, $db_name, $login, $passwd) +{ + $op = new stdclass(); + $op->status_ok = true; + $op->msg = 'ok - grant assignment'; + + if ($op->status_ok) { + $sql = 'ALTER DATABASE "' . $db->prepare_string($db_name) . '" OWNER TO ' . + '"' . $db->prepare_string($login) . '"'; + if (! @$db->exec_query($sql)) { + $op->status_ok = false; + $op->msg = "ko - " . $db->error_msg(); + } + } + + if ($op->status_ok) { + $sql = 'ALTER SCHEMA public OWNER TO ' . '"' . + $db->prepare_string($login) . '"'; + if (! @$db->exec_query($sql)) { + $op->status_ok = false; + $op->msg = "ko - " . $db->error_msg(); + } + } + + return $op; +} + +/* + * function: _mssql_make_user_with_grants + * + * args : + * + * returns: + * + */ +function _mssql_make_user_with_grants($db, $the_host, $db_name, $login, $passwd) +{ + _mssql_make_user($db, $the_host, $db_name, $login, $passwd); + + $op->status_ok = true; + $op->msg = 'ok - new user'; + + // Check if has been created, because I'm not able to get return code. + $user_list = getUserList($db, 'mssql'); + $user_list = array_map('strtolower', $user_list); + $user_exists = in_array(trim($login), $user_list); + if (! $user_exists) { + $op->status_ok = false; + $op->msg = "ko - " . $db->error_msg(); + } else { + _mssql_assign_grants($db, $the_host, $db_name, $login, $passwd); + } + return $op; +} + +function _mssql_make_user($db, $the_host, $db_name, $login, $passwd) +{ + + // Transact-SQL Reference + // + // sp_addlogin + // New Information - SQL Server 2000 SP3. + // + // Creates a new Microsoft® SQL Server™ login that allows a user + // to connect to an instance of SQL Server using SQL Server Authentication. + // + // Security Note When possible, use Windows Authentication. + // + // Syntax + // sp_addlogin [ @loginame = ] 'login' + // [ , [ @passwd = ] 'password' ] + // [ , [ @defdb = ] 'database' ] + // [ , [ @deflanguage = ] 'language' ] + // [ , [ @sid = ] sid ] + // [ , [ @encryptopt = ] 'encryption_option' ] + // + // Arguments + // [@loginame =] 'login' + // Is the name of the login. login is sysname, with no default. + // + // [@passwd =] 'password' + // Is the login password. password is sysname, with a default of NULL. + // After sp_addlogin has been executed, the password is encrypted and stored in the system tables. + // + // [@defdb =] 'database' + // Is the default database of the login (the database the login is connected to after logging in). + // database is sysname, with a default of master. + // + // [@deflanguage =] 'language' + // Is the default language assigned when a user logs on to SQL Server. + // language is sysname, with a default of NULL. + // If language is not specified, language is set to the server's current default language + // (defined by the sp_configure configuration variable default language). + // Changing the server's default language does not change the default language for existing logins. + // language remains the same as the default language used when the login was added. + // + // [@sid =] sid + // Is the security identification number (SID). sid is varbinary(16), with a default of NULL. + // If sid is NULL, the system generates a SID for the new login. + // Despite the use of a varbinary data type, values other than NULL must be + // exactly 16 bytes in length, and must not already exist. + // SID is useful, for example, when you are scripting or moving SQL Server logins + // from one server to another and you want the logins to have the same SID between servers. + // + // [@encryptopt =] 'encryption_option' + // Specifies whether the password is encrypted when stored in the system tables. + // encryption_option is varchar(20), and can be one of these values. + // + // Value Description + // NULL The password is encrypted. This is the default. + // skip_encryption The password is already encrypted. + // SQL Server should store the value without re-encrypting it. + // skip_encryption_old The supplied password was encrypted by a previous version of SQL Server. + // SQL Server should store the value without re-encrypting it. + // This option is provided for upgrade purposes only. + // + // + // Return Code Values + // 0 (success) or 1 (failure) + // + // Permissions + // Only members of the sysadmin and securityadmin fixed server roles can execute sp_addlogin. + // + // Examples + // A. Create a login ID with master default database + // This example creates an SQL Server login for the user Victoria, without specifying a default database. + // + // EXEC sp_addlogin 'Victoria', 'B1r12-36' + // + // B. Create a login ID and default database + // This example creates a SQL Server login for the user Albert, with a password of "B1r12-36" + // and a default database of corporate. + // + // EXEC sp_addlogin 'Albert', 'B1r12-36', 'corporate' + // + // C. Create a login ID with a different default language + // This example creates an SQL Server login for the user Claire Picard, with a password of "B1r12-36", + // a default database of public_db, and a default language of French. + // + // EXEC sp_addlogin 'Claire Picard', 'B1r12-36', 'public_db', 'french' + // + // D. Create a login ID with a specific SID + // This example creates an SQL Server login for the user Michael, with a password of "B1r12-36," + // a default database of pubs, a default language of us_english, + // and an SID of 0x0123456789ABCDEF0123456789ABCDEF. + // + // EXEC sp_addlogin 'Michael', 'B1r12-36', 'pubs', 'us_english', 0x0123456789ABCDEF0123456789ABCDEF + // + // E. Create a login ID and do not encrypt the password + // This example creates an SQL Server login for the user Margaret with a password of "B1r12-36" on Server1, + // extracts the encrypted password, and then adds the login for the user Margaret to Server2 using + // the previously encrypted password but does not further encrypt the password. + // User Margaret can then log on to Server2 using the password Rose. + $op->status_ok = true; + $op->msg = 'ok - new user'; + + // sp_addlogin [ @loginame = ] 'login' + // [ , [ @passwd = ] 'password' ] + // [ , [ @defdb = ] 'database' ] + // [ , [ @deflanguage = ] 'language' ] + // [ , [ @sid = ] sid ] + // [ , [ @encryptopt = ] 'encryption_option' ] + // + // Important: + // From ADODB manual - Prepare() documentation + // + // Returns an array containing the original sql statement in the first array element; + // the remaining elements of the array are driver dependent. + // + // 20071104 - franciscom + // Looking into adodb-mssql.inc.php, you will note that array[1] + // is a mssql stm object. + // This info is very important, to use mssql_free_statement() + // + + $stmt = $db->db->PrepareSP('SP_ADDLOGIN'); + $db->db->InParameter($stmt, $login, 'loginame'); + $db->db->InParameter($stmt, $db_name, 'defdb'); + + $db->db->OutParameter($stmt, $retval, 'RETVAL'); + $db->db->Execute($stmt); + + // Very important: + // Info from PHP Manual notes + // mssql_free_statement() + // + // mitch at 1800radiator dot kom (23-Mar-2005 06:02) + // Maybe it's unique to my FreeTDS configuration, but if I don't call mssql_free_statement() + // after every stored procedure (i.e. mssql_init, mssql_bind, mssql_execute, mssql_fetch_array), + // all subsequent stored procedures on the same database connection will fail. + // I only mention it because this man-page deprecates the use of mssql_free_statement(), + // saying it's only there for run-time memory concerns. + // At least in my case, it's also a crucial step in the process of running a stored procedure. + // If anyone else has problems running multiple stored procedures on the same connection, + // I hope this helps them out. + // + // franciscom - 20071104 + // Without this was not possible to call other functions that use store procedures, + // because I've got: + // a) wrong results + // b) mssql_init() errors + // + mssql_free_statement($stmt[1]); + + // I've problems trying to set password, + // then I will use as workaround setting a NULL password + // and after do a password change. + $passwd_null = null; + $stmt = $db->db->PrepareSP('SP_PASSWORD'); + $db->db->InParameter($stmt, $login, 'loginame'); + $db->db->InParameter($stmt, $passwd_null, 'old'); + $db->db->InParameter($stmt, $passwd, 'new'); + $db->db->Execute($stmt); + mssql_free_statement($stmt[1]); +} + +/* + * function: _mssql_assign_grants + * + * args : + * + * returns: + * + */ +function _mssql_assign_grants($db, $the_host, $db_name, $login, $passwd) +{ + $db_role = 'db_owner'; + $stmt = $db->db->PrepareSP('SP_ADDUSER'); + $db->db->InParameter($stmt, $login, 'loginame'); + $db->db->InParameter($stmt, $login, 'name_in_db'); + $db->db->InParameter($stmt, $db_role, 'grpname'); + $db->db->Execute($stmt); + mssql_free_statement($stmt[1]); + + $op = new stdClass(); + $op->status_ok = true; + $op->msg = 'ok - grant assignment'; + + return $op; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function _mssql_set_passwd($db, $login, $passwd) +{ + $sql = "EXEC SP_PASSWORD NULL,'{$passwd}',{$login}"; + $db->exec_query($sql); +} + +/** + */ +function important_reminder() +{ + echo '

YOUR ATTENTION PLEASE:
To have a fully functional installation You need to configure mail server settings, following this steps
  • copy from config.inc.php, [SMTP] Section into custom_config.inc.php.
  • -
  • complete correct data regarding email addresses and mail server.

'; +

  • complete correct data regarding email addresses and mail server.
  • '; } diff --git a/install/sql/alter_tables/1.9.18/mysql/DB.1.9.18/step1/db_schema_update.sql b/install/sql/alter_tables/1.9.18/mysql/DB.1.9.18/step1/db_schema_update.sql index fcde09fa27..4d607b4f8e 100644 --- a/install/sql/alter_tables/1.9.18/mysql/DB.1.9.18/step1/db_schema_update.sql +++ b/install/sql/alter_tables/1.9.18/mysql/DB.1.9.18/step1/db_schema_update.sql @@ -52,10 +52,10 @@ AS SELECT `LRQVN`.`req_id` AS `req_id`, `LRQVN`.`version` AS `version`, `REQV`.`id` AS `req_version_id` -FROM `latest_req_version` `LRQVN` -join `nodes_hierarchy` `NHRQV` +FROM /*prefix*/latest_req_version `LRQVN` +join /*prefix*/nodes_hierarchy `NHRQV` on `NHRQV`.`parent_id` = `LRQVN`.`req_id` -join `req_versions` `REQV` +join /*prefix*/req_versions `REQV` on `REQV`.`id` = `NHRQV`.`id` and `REQV`.`version` = `LRQVN`.`version`; @@ -114,4 +114,4 @@ SET ATT.fk_id = LTCVID.tcversion_id,ATT.fk_table ='tcversions' WHERE ATT.original_fk_id = LTCVID.testcase_id AND ATT.original_fk_table = 'nodes_hierarchy'; -# END \ No newline at end of file +# END diff --git a/install/sql/alter_tables/1.9.20/mysql/DB.1.9.20/step1/db_schema_update.sql b/install/sql/alter_tables/1.9.20/mysql/DB.1.9.20/step1/db_schema_update.sql index 35ae92a287..f66ba0e510 100644 --- a/install/sql/alter_tables/1.9.20/mysql/DB.1.9.20/step1/db_schema_update.sql +++ b/install/sql/alter_tables/1.9.20/mysql/DB.1.9.20/step1/db_schema_update.sql @@ -18,12 +18,35 @@ ALTER TABLE /*prefix*/builds ADD COLUMN branch varchar(64) NULL; ALTER TABLE /*prefix*/builds ADD COLUMN release_candidate varchar(100) NULL; -- -ALTER TABLE /*prefix*/users MODIFY password VARCHAR(255); +ALTER TABLE /*prefix*/users MODIFY password VARCHAR(255) NOT NULL default ''; -- ALTER TABLE /*prefix*/testplan_platforms ADD COLUMN active tinyint(1) NOT NULL default '1'; -ALTER TABLE /*prefix*/platforms ADD COLUMN enable_on_design tinyint(1) NOT NULL default '0', -ALTER TABLE /*prefix*/platforms ADD COLUMN enable_on_execution tinyint(1) NOT NULL default '1', +ALTER TABLE /*prefix*/platforms ADD COLUMN enable_on_design tinyint(1) NOT NULL default '0'; +ALTER TABLE /*prefix*/platforms ADD COLUMN enable_on_execution tinyint(1) NOT NULL default '1'; +ALTER TABLE /*prefix*/platforms ADD COLUMN is_open tinyint(1) NOT NULL default '1'; + +-- +ALTER TABLE /*prefix*/nodes_hierarchy ADD INDEX /*prefix*/nodes_hierarchy_node_type_id (node_type_id); +ALTER TABLE /*prefix*/testcase_keywords ADD INDEX /*prefix*/idx02_testcase_keywords (tcversion_id); + +ALTER TABLE /*prefix*/milestones MODIFY target_date date NOT NULL; +ALTER TABLE /*prefix*/milestones MODIFY start_date date DEFAULT NULL; + +-- +CREATE TABLE /*prefix*/execution_tcsteps_wip ( + id int(10) unsigned NOT NULL auto_increment, + tcstep_id int(10) unsigned NOT NULL default '0', + testplan_id int(10) unsigned NOT NULL default '0', + platform_id int(10) unsigned NOT NULL default '0', + build_id int(10) unsigned NOT NULL default '0', + tester_id int(10) unsigned default NULL, + creation_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + notes text, + status char(1) default NULL, + PRIMARY KEY (id), + UNIQUE KEY /*prefix*/execution_tcsteps_wip_idx1(`tcstep_id`,`testplan_id`,`platform_id`,`build_id`) +) DEFAULT CHARSET=utf8; CREATE TABLE /*prefix*/testcase_platforms ( @@ -41,11 +64,11 @@ CREATE TABLE /*prefix*/baseline_l1l2_context ( id int(10) unsigned NOT NULL AUTO_INCREMENT, testplan_id int(10) unsigned NOT NULL DEFAULT '0', platform_id int(10) unsigned NOT NULL DEFAULT '0', - being_exec_ts timestamp NOT NULL, - end_exec_ts timestamp NOT NULL, + begin_exec_ts timestamp NOT NULL, + end_exec_ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, creation_ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), - UNIQUE KEY udx1 (testplan_id,platform_id,creation_ts), + UNIQUE KEY /*prefix*/udx1_details (testplan_id,platform_id,creation_ts) ) DEFAULT CHARSET=utf8; @@ -58,7 +81,7 @@ CREATE TABLE /*prefix*/baseline_l1l2_details ( qty int(10) unsigned NOT NULL DEFAULT '0', total_tc int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), - UNIQUE KEY udx1 (context_id,top_tsuite_id,child_tsuite_id,status) + UNIQUE KEY /*prefix*/udx1_details (context_id,top_tsuite_id,child_tsuite_id,status) ) DEFAULT CHARSET=utf8; # @@ -73,9 +96,6 @@ AS SELECT tcversion_id, testplan_id,build_id,platform_id,max(id) AS id FROM /*prefix*/executions GROUP BY tcversion_id,testplan_id,build_id,platform_id; -ALTER TABLE /*prefix*/nodes_hierarchy ADD INDEX /*prefix*/nodes_hierarchy_node_type_id (node_type_id); -ALTER TABLE /*prefix*/testcase_keywords ADD INDEX /*prefix*/idx02_testcase_keywords (tcversion_id); - CREATE OR REPLACE VIEW /*prefix*/tcversions_without_platforms AS SELECT diff --git a/install/sql/alter_tables/1.9.20/postgres/DB.1.9.20/step1/db_schema_update.sql b/install/sql/alter_tables/1.9.20/postgres/DB.1.9.20/step1/db_schema_update.sql index 3269465c15..6e0a239586 100644 --- a/install/sql/alter_tables/1.9.20/postgres/DB.1.9.20/step1/db_schema_update.sql +++ b/install/sql/alter_tables/1.9.20/postgres/DB.1.9.20/step1/db_schema_update.sql @@ -19,6 +19,7 @@ ALTER TABLE /*prefix*/users ALTER COLUMN password TYPE VARCHAR(255); ALTER TABLE /*prefix*/testplan_platforms ADD COLUMN active INT2 NOT NULL DEFAULT '1'; ALTER TABLE /*prefix*/platforms ADD COLUMN enable_on_design INT2 NOT NULL DEFAULT '0'; ALTER TABLE /*prefix*/platforms ADD COLUMN enable_on_execution INT2 NOT NULL DEFAULT '1'; +ALTER TABLE /*prefix*/platforms ADD COLUMN is_open INT2 NOT NULL DEFAULT '1'; -- -- Table structure for table "testcase_platforms" @@ -37,12 +38,12 @@ CREATE TABLE /*prefix*/baseline_l1l2_context ( "id" BIGSERIAL NOT NULL , "testplan_id" BIGINT NOT NULL DEFAULT '0' REFERENCES /*prefix*/testplans (id), "platform_id" BIGINT NOT NULL DEFAULT '0' REFERENCES /*prefix*/platforms (id) ON DELETE CASCADE, - "being_exec_ts" timestamp NOT NULL, + "begin_exec_ts" timestamp NOT NULL, "end_exec_ts" timestamp NOT NULL, "creation_ts" timestamp NOT NULL DEFAULT now(), PRIMARY KEY ("id") ); -CREATE UNIQUE INDEX /*prefix*/udx1 ON /*prefix*/baseline_l1l2_context ("testplan_id","platform_id","creation_ts"); +CREATE UNIQUE INDEX /*prefix*/udx1_context ON /*prefix*/baseline_l1l2_context ("testplan_id","platform_id","creation_ts"); CREATE TABLE /*prefix*/baseline_l1l2_details ( @@ -55,7 +56,7 @@ CREATE TABLE /*prefix*/baseline_l1l2_details ( "total_tc" INT NOT NULL DEFAULT '0', PRIMARY KEY ("id") ) ; -CREATE UNIQUE INDEX /*prefix*/udx1 +CREATE UNIQUE INDEX /*prefix*/udx1_details ON /*prefix*/baseline_l1l2_details ("context_id","top_tsuite_id","child_tsuite_id","status"); diff --git a/install/sql/mysql/testlink_create_tables.sql b/install/sql/mysql/testlink_create_tables.sql index 949fe27af8..9315b62702 100644 --- a/install/sql/mysql/testlink_create_tables.sql +++ b/install/sql/mysql/testlink_create_tables.sql @@ -333,6 +333,7 @@ CREATE TABLE /*prefix*/platforms ( notes text NOT NULL, enable_on_design tinyint(1) unsigned NOT NULL default '0', enable_on_execution tinyint(1) unsigned NOT NULL default '1', + is_open tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (id), UNIQUE KEY /*prefix*/idx_platforms (testproject_id,name) ) DEFAULT CHARSET=utf8; @@ -802,11 +803,11 @@ CREATE TABLE /*prefix*/baseline_l1l2_context ( id int(10) unsigned NOT NULL AUTO_INCREMENT, testplan_id int(10) unsigned NOT NULL DEFAULT '0', platform_id int(10) unsigned NOT NULL DEFAULT '0', - being_exec_ts timestamp NOT NULL, - end_exec_ts timestamp NOT NULL, + begin_exec_ts timestamp NOT NULL, + end_exec_ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, creation_ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), - UNIQUE KEY udx1 (testplan_id,platform_id,creation_ts) + UNIQUE KEY udx1_context (testplan_id,platform_id,creation_ts) ) DEFAULT CHARSET=utf8; @@ -819,7 +820,7 @@ CREATE TABLE /*prefix*/baseline_l1l2_details ( qty int(10) unsigned NOT NULL DEFAULT '0', total_tc int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), - UNIQUE KEY udx1 (context_id,top_tsuite_id,child_tsuite_id,status) + UNIQUE KEY udx1_details (context_id,top_tsuite_id,child_tsuite_id,status) ) DEFAULT CHARSET=utf8; diff --git a/install/sql/postgres/testlink_create_tables.sql b/install/sql/postgres/testlink_create_tables.sql index 309f54b5fc..37907e1435 100644 --- a/install/sql/postgres/testlink_create_tables.sql +++ b/install/sql/postgres/testlink_create_tables.sql @@ -701,6 +701,7 @@ CREATE TABLE /*prefix*/platforms ( notes text NOT NULL, enable_on_design INT2 NOT NULL DEFAULT '0', enable_on_execution INT2 NOT NULL DEFAULT '1', + is_open INT2 NOT NULL DEFAULT '1', PRIMARY KEY (id) ); CREATE UNIQUE INDEX /*prefix*/platforms_uidx1 ON /*prefix*/platforms (testproject_id,name); @@ -920,12 +921,12 @@ CREATE TABLE /*prefix*/baseline_l1l2_context ( "id" BIGSERIAL NOT NULL , "testplan_id" BIGINT NOT NULL DEFAULT '0' REFERENCES /*prefix*/testplans (id), "platform_id" BIGINT NOT NULL DEFAULT '0' REFERENCES /*prefix*/platforms (id) ON DELETE CASCADE, - "being_exec_ts" timestamp NOT NULL, + "begin_exec_ts" timestamp NOT NULL, "end_exec_ts" timestamp NOT NULL, "creation_ts" timestamp NOT NULL DEFAULT now(), PRIMARY KEY ("id") ); -CREATE UNIQUE INDEX /*prefix*/udx1 ON /*prefix*/baseline_l1l2_context ("testplan_id","platform_id","creation_ts"); +CREATE UNIQUE INDEX /*prefix*/udx1_context ON /*prefix*/baseline_l1l2_context ("testplan_id","platform_id","creation_ts"); CREATE TABLE /*prefix*/baseline_l1l2_details ( @@ -934,11 +935,11 @@ CREATE TABLE /*prefix*/baseline_l1l2_details ( "top_tsuite_id" BIGINT NOT NULL DEFAULT '0' REFERENCES /*prefix*/testsuites (id), "child_tsuite_id" BIGINT NOT NULL DEFAULT '0' REFERENCES /*prefix*/testsuites (id), "status" char(1) DEFAULT NULL, - "qty" INT unsigned NOT NULL DEFAULT '0', - "total_tc" INT unsigned NOT NULL DEFAULT '0', + "qty" INT NOT NULL DEFAULT '0', + "total_tc" INT NULL DEFAULT '0', PRIMARY KEY ("id") ) ; -CREATE UNIQUE INDEX /*prefix*/udx1 +CREATE UNIQUE INDEX /*prefix*/udx1_details ON /*prefix*/baseline_l1l2_details ("context_id","top_tsuite_id","child_tsuite_id","status"); @@ -1087,6 +1088,15 @@ CREATE OR REPLACE VIEW /*prefix*/tcversions_without_platforms AS WHERE TCPL.tcversion_id = NHTCV.id ) ) ); +-- +-- +CREATE OR REPLACE VIEW /*prefix*/latest_exec_by_testplan_plat AS +( + SELECT tcversion_id, testplan_id,platform_id,max(id) AS id + FROM /*prefix*/executions + GROUP BY tcversion_id,testplan_id,platform_id +); + -- -- CREATE OR REPLACE VIEW /*prefix*/tsuites_tree_depth_2 AS diff --git a/install/sqlParser.class.php b/install/sqlParser.class.php index e6fc6b61e4..a23211b24a 100644 --- a/install/sqlParser.class.php +++ b/install/sqlParser.class.php @@ -1,152 +1,145 @@ -db_conn = $db_conn; - $this->db_type = $db_type; - $this->db_table_prefix = trim($db_table_prefix); - } - - - /* - function: - - args : - - returns: - */ - function process($filename) - { - $new_value=null; - - // ----------------------------------------------------------------- - // part of this logic has been copied from the setup of EVENTUM - $contents = file($filename); - $do_replace = trim($this->db_table_prefix) != ''; - - // From PHP Manual Notes on using a class function as Filter - // This FAILS!!! - // $cfil = array_filter($contents,"only_good_sql"); - // - $do_additional_replace=false; - switch($this->db_type) - { - case 'mysql': - $cfil = array_filter($contents,array($this,"only_good_mysql")); - break; - - case 'postgres': - $target['sequence'] = "SELECT setval('"; - $do_additional_replace=true; - $cfil = array_filter($contents,array($this,"only_good_sql")); - break; - - case 'mssql': - $cfil = array_filter($contents,array($this,"only_good_sql")); - break; - } - - $r2d2 = implode("", $cfil); - // echo "

    debug 20090715 - \ - " . __FUNCTION__ . " --- "; print_r($r2d2); echo "
    "; - - if( $do_replace) - { - $r2d2 = str_replace('/*prefix*/',$this->db_table_prefix,$r2d2); - - // just to solve problem with sequence on PostGres when creating - // start up data (need to find a better way) - if($do_additional_replace) - { - foreach($target as $key => $value) - { - if( !is_null($value) ) - { - $new_value[$key] = $value . $this->db_table_prefix ; - $r2d2 = str_replace($value,$new_value[$key],$r2d2); - } - } - } - } - - $num = 0; - $sql_array = explode(";", $r2d2); - foreach($sql_array as $sql_do) - { - // Needed becuase explode() adds \r\n - $sql_dodo = trim(trim($sql_do, "\r\n ")); - - if( strlen($sql_dodo) > 0 ) - { - $num = $num + 1; - $status_ok=$this->db_conn->exec_query($sql_dodo); - if(!$status_ok) - { - $this->sql_errors[] = array("error" => $this->db_conn->error_msg(), "sql" => $sql_dodo); - $this->install_failed = true; - } - } - } // foreach +db_conn = $db_conn; + $this->db_type = $db_type; + $this->db_table_prefix = trim($db_table_prefix); + } + + /* + * function: + * + * args : + * + * returns: + */ + public function process($filename) + { + $new_value = null; + + // part of this logic has been copied from the setup of EVENTUM + $contents = file($filename); + $do_replace = trim($this->db_table_prefix) != ''; + + // From PHP Manual Notes on using a class function as Filter + // This FAILS!!! + // $cfil = array_filter($contents,"only_good_sql"); + // + $do_additional_replace = false; + switch ($this->db_type) { + case 'mysql': + $cfil = array_filter($contents, + array( + $this, + "only_good_mysql" + )); + break; + + case 'postgres': + $target['sequence'] = "SELECT setval('"; + $do_additional_replace = true; + $cfil = array_filter($contents, array( + $this, + "only_good_sql" + )); + break; + + case 'mssql': + $cfil = array_filter($contents, array( + $this, + "only_good_sql" + )); + break; + } + + $r2d2 = implode("", $cfil); + + if ($do_replace) { + $r2d2 = str_replace('/*prefix*/', $this->db_table_prefix, $r2d2); + + // just to solve problem with sequence on PostGres when creating + // start up data (need to find a better way) + if ($do_additional_replace) { + foreach ($target as $key => $value) { + if (! is_null($value)) { + $new_value[$key] = $value . $this->db_table_prefix; + $r2d2 = str_replace($value, $new_value[$key], $r2d2); + } + } + } + } + + $num = 0; + $sql_array = explode(";", $r2d2); + foreach ($sql_array as $sql_do) { + // Needed becuase explode() adds \r\n + $sql_dodo = trim(trim($sql_do, "\r\n ")); + + if (strlen($sql_dodo) > 0) { + $num += 1; + $status_ok = $this->db_conn->exec_query($sql_dodo); + if (! $status_ok) { + $this->sql_errors[] = array( + "error" => $this->db_conn->error_msg(), + "sql" => $sql_dodo + ); + $this->install_failed = true; + } + } + } + } + + protected function only_good_mysql($v) + { + $comment_char = '#'; + return $this->only_good_sql($v, $comment_char); + } + + protected function only_good_sql($v, $comment_char = '-') + { + $use_v = true; + $findme = $comment_char; + + // Must trim New Line for the strlen check + $v_c = trim($v, "\r\n "); + $pos = strpos($v_c, $findme); + + if ($pos === false) { + $use_v = true; + } else { + if ($pos == 0) { + $use_v = false; + } + } + + // Empty line must not be used + if ($use_v && strlen($v_c) == 0) { + $use_v = false; + } + + return $use_v; + } } - - -function only_good_mysql($v) -{ - $comment_char='#'; - return($this->only_good_sql($v, $comment_char)); -} - - -function only_good_sql($v, $comment_char='-') -{ - - $use_v = true; - $findme=$comment_char; - - // Must trim New Line for the strlen check - $v_c = trim($v, "\r\n "); - $pos = strpos($v_c, $findme); - - - if ($pos === false) - { - $use_v = true; - } - else - { - if ($pos == 0 ) - { - $use_v = false; - } - } - - // Empty line must not be used - if( $use_v == true ) - { - if ( strlen($v_c) == 0) - { - $use_v = false; - } - } - - return ($use_v); -} // Function ends - - -} // class end \ No newline at end of file diff --git a/install/util/sysinfo.php b/install/util/sysinfo.php index 5a2b282567..c449089632 100644 --- a/install/util/sysinfo.php +++ b/install/util/sysinfo.php @@ -1,70 +1,62 @@ -".$msg."
    "; - -return $msg; -} +" . $msg . ""; + + return $msg; +} ?> @@ -84,32 +76,28 @@ function reload() {

    TestLink - System & services checking

    Installation status: - +

    -Error counter = '.$errors.'

    '; +Error counter = ' . $errors . '

    '; ?>
    @@ -120,4 +108,4 @@ function reload() {
    - \ No newline at end of file + diff --git a/lib/ajax/checkDuplicateName.php b/lib/ajax/checkDuplicateName.php index 98145fab78..757ef9e90b 100644 --- a/lib/ajax/checkDuplicateName.php +++ b/lib/ajax/checkDuplicateName.php @@ -16,8 +16,8 @@ * **/ -require_once('../../config.inc.php'); -require_once('common.php'); +require_once '../../config.inc.php'; +require_once 'common.php'; testlinkInitPage($db); $data = array('success' => true, 'message' => ''); diff --git a/lib/ajax/checkNodeDuplicateName.php b/lib/ajax/checkNodeDuplicateName.php index 940aee5d09..765c75cece 100644 --- a/lib/ajax/checkNodeDuplicateName.php +++ b/lib/ajax/checkNodeDuplicateName.php @@ -1,45 +1,62 @@ - true, 'message' => ''); - -$iParams = array("node_name" => array(tlInputParameter::STRING_N,0,100), - "node_id" => array(tlInputParameter::INT), - "parent_id" => array(tlInputParameter::INT), - "node_type" => array(tlInputParameter::STRING_N,0,20)); -$args = G_PARAMS($iParams); - -$tree_manager = new tree($db); -$node_types_descr_id=$tree_manager->get_available_node_types(); - -// To allow name check when creating a NEW NODE => we do not have node id -$args['node_id'] = ($args['node_id'] > 0 )? $args['node_id'] : null; -$args['parent_id'] = ($args['parent_id'] > 0 )? $args['parent_id'] : null; - -$check = $tree_manager->nodeNameExists($args['node_name'], $node_types_descr_id[$args['node_type']], - $args['node_id'],$args['parent_id']); - -$data['success'] = !$check['status']; -$data['message'] = $check['msg']; - -echo json_encode($data); \ No newline at end of file + true, + 'message' => '' +); + +$iParams = array( + "node_name" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "node_id" => array( + tlInputParameter::INT + ), + "parent_id" => array( + tlInputParameter::INT + ), + "node_type" => array( + tlInputParameter::STRING_N, + 0, + 20 + ) +); +$args = G_PARAMS($iParams); + +$tree_manager = new tree($db); +$node_types_descr_id = $tree_manager->get_available_node_types(); + +// To allow name check when creating a NEW NODE => we do not have node id +$args['node_id'] = ($args['node_id'] > 0) ? $args['node_id'] : null; +$args['parent_id'] = ($args['parent_id'] > 0) ? $args['parent_id'] : null; + +$check = $tree_manager->nodeNameExists($args['node_name'], + $node_types_descr_id[$args['node_type']], $args['node_id'], + $args['parent_id']); + +$data['success'] = ! $check['status']; +$data['message'] = $check['msg']; + +echo json_encode($data); diff --git a/lib/ajax/checkTCaseDuplicateName.php b/lib/ajax/checkTCaseDuplicateName.php index 31adfd7ceb..d49532dbdf 100644 --- a/lib/ajax/checkTCaseDuplicateName.php +++ b/lib/ajax/checkTCaseDuplicateName.php @@ -1,50 +1,77 @@ - true, 'message' => ''); - -$iParams = array("name" => array(tlInputParameter::STRING_N,0,100), - "testcase_id" => array(tlInputParameter::INT), - "testsuite_id" => array(tlInputParameter::INT)); -$args = G_PARAMS($iParams); - -if (has_rights($db, 'mgt_view_tc')) -{ - $tree_manager = new tree($db); - $node_types_descr_id=$tree_manager->get_available_node_types(); - - // To allow name check when creating a NEW test case => we do not have test case id - $args['testcase_id'] = ($args['testcase_id'] > 0 )? $args['testcase_id'] : null; - $args['testsuite_id'] = ($args['testsuite_id'] > 0 )? $args['testsuite_id'] : null; - - $check = $tree_manager->nodeNameExists($args['name'], $node_types_descr_id['testcase'], - $args['testcase_id'],$args['testsuite_id']); - - $data['success'] = !$check['status']; - $data['message'] = $check['msg']; -} -else -{ - tLog('User has not right needed to do requested action - checkTCaseDuplicateName.php', 'ERROR'); - $data['success'] = false; - $data['message'] = lang_get('user_has_no_right_for_action'); -} - -echo json_encode($data); \ No newline at end of file + true, + 'message' => '' +); + +$iParams = array( + "name" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "testcase_id" => array( + tlInputParameter::INT + ), + "testsuite_id" => array( + tlInputParameter::INT + ), + "testproject_id" => array( + tlInputParameter::INT + ) +); + +$args = G_PARAMS($iParams); + +// get test project id from session this need to be changed +$rights2check = [ + 'mgt_view_tc', + 'mgt_modify_tc' +]; +if ($_SESSION['currentUser']->hasRight($db, $rights2check[0], + $args['testproject_id']) || + $_SESSION['currentUser']->hasRight($db, $rights2check[1], + $args['testproject_id'])) { + + $tree_manager = new tree($db); + $node_types_descr_id = $tree_manager->get_available_node_types(); + + // To allow name check when creating a NEW test case => we do not have test case id + $args['testcase_id'] = ($args['testcase_id'] > 0) ? $args['testcase_id'] : null; + $args['testsuite_id'] = ($args['testsuite_id'] > 0) ? $args['testsuite_id'] : null; + + $check = $tree_manager->nodeNameExists($args['name'], + $node_types_descr_id['testcase'], $args['testcase_id'], + $args['testsuite_id']); + + $data['success'] = ! $check['status']; + $data['message'] = $check['msg']; +} else { + $tLogMsg = 'User ' . $_SESSION['currentUser']->login . + ' has not right needed () ' . json_encode($rights2check) . + ' to do requested action - checkTCaseDuplicateName.php' . + ' - testproject_id:' . $args['testproject_id']; + tLog($tLogMsg, 'ERROR'); + $data['success'] = false; + $data['message'] = lang_get('user_has_no_right_for_action'); +} + +echo json_encode($data); diff --git a/lib/ajax/dragdroprequirementnodes.php b/lib/ajax/dragdroprequirementnodes.php index 63de9fa080..c250fb02cd 100644 --- a/lib/ajax/dragdroprequirementnodes.php +++ b/lib/ajax/dragdroprequirementnodes.php @@ -1,51 +1,57 @@ -doAction) { - case 'changeParent': - $treeMgr->change_parent($args->nodeid,$args->newparentid); - $sql = " UPDATE " . DB_TABLE_PREFIX . "requirements " . - " SET srs_id=" . intval($args->newparentid) . - " WHERE id=" . intval($args->nodeid); - $db->exec_query($sql); - break; - - case 'doReorder': - $dummy = explode(',',$args->nodelist); - $treeMgr->change_order_bulk($dummy); - break; -} - -/** - * - */ -function init_args() { - $args=new stdClass(); - - $key2loop=array('nodeid','newparentid','nodeorder'); - foreach($key2loop as $key) { - $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : null; - } - - $key2loop = array('doAction','top_or_bottom','nodelist'); - foreach($key2loop as $key) { - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; - } - - return $args; -} \ No newline at end of file +doAction) { + case 'changeParent': + $treeMgr->change_parent($args->nodeid, $args->newparentid); + $sql = " UPDATE " . DB_TABLE_PREFIX . "requirements " . " SET srs_id=" . + intval($args->newparentid) . " WHERE id=" . intval($args->nodeid); + $db->exec_query($sql); + break; + + case 'doReorder': + $dummy = explode(',', $args->nodelist); + $treeMgr->change_order_bulk($dummy); + break; +} + +/** + */ +function initArgs() +{ + $args = new stdClass(); + + $key2loop = array( + 'nodeid', + 'newparentid', + 'nodeorder' + ); + foreach ($key2loop as $key) { + $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : null; + } + + $key2loop = array( + 'doAction', + 'top_or_bottom', + 'nodelist' + ); + foreach ($key2loop as $key) { + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; + } + + return $args; +} diff --git a/lib/ajax/dragdroptprojectnodes.php b/lib/ajax/dragdroptprojectnodes.php index 410be04e3e..7c4fa41995 100644 --- a/lib/ajax/dragdroptprojectnodes.php +++ b/lib/ajax/dragdroptprojectnodes.php @@ -1,57 +1,70 @@ - 1, 'requirement' => 1, 'requirement_spec' => 1); - -$args=init_args(); -$treeMgr = new tree($db); - -switch($args->doAction) -{ - case 'changeParent': - $treeMgr->change_parent($args->nodeid,$args->newparentid); - break; - - case 'doReorder': - $dummy=explode(',',$args->nodelist); - $treeMgr->change_order_bulk($dummy); - break; + 1, + 'requirement' => 1, + 'requirement_spec' => 1 +); + +$args = initArgs(); +$treeMgr = new tree($db); + +switch ($args->doAction) { + case 'changeParent': + $treeMgr->change_parent($args->nodeid, $args->newparentid); + break; + + case 'doReorder': + $dummy = explode(',', $args->nodelist); + $treeMgr->change_order_bulk($dummy); + break; +} + +/** + * Initialize arguments + * + * @return stdClass + */ +function initArgs() +{ + $args = new stdClass(); + + $key2loop = array( + 'nodeid', + 'newparentid', + 'nodeorder' + ); + foreach ($key2loop as $key) { + $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : null; + } + + $key2loop = array( + 'doAction', + 'top_or_bottom', + 'nodelist' + ); + foreach ($key2loop as $key) { + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; + } + + return $args; } - -/** - * - */ -function init_args() { - $args=new stdClass(); - - $key2loop=array('nodeid','newparentid','nodeorder'); - foreach($key2loop as $key) { - $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : null; - } - - $key2loop = array('doAction','top_or_bottom','nodelist'); - foreach($key2loop as $key) { - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; - } - - return $args; -} \ No newline at end of file diff --git a/lib/ajax/dragdroptreenodes.php b/lib/ajax/dragdroptreenodes.php index d695398a9d..882e48e453 100644 --- a/lib/ajax/dragdroptreenodes.php +++ b/lib/ajax/dragdroptreenodes.php @@ -1,42 +1,46 @@ -doAction) -{ - case 'changeParent': - $treeMgr->change_parent($args->nodeid,$args->newparentid); - break; - - case 'doReorder': - $dummy=explode(',',$args->nodelist); - $treeMgr->change_order_bulk($dummy); - break; -} - -function init_args() -{ - $args=new stdClass(); - - $key2loop=array('nodeid','newparentid','doAction','top_or_bottom','nodeorder','nodelist'); - foreach($key2loop as $key) - { - $args->$key=isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; - } - return $args; -} -?> \ No newline at end of file +doAction) { + case 'changeParent': + $treeMgr->change_parent($args->nodeid, $args->newparentid); + break; + + case 'doReorder': + $dummy = explode(',', $args->nodelist); + $treeMgr->change_order_bulk($dummy); + break; +} + +function initArgs() +{ + $args = new stdClass(); + + $key2loop = array( + 'nodeid', + 'newparentid', + 'doAction', + 'top_or_bottom', + 'nodeorder', + 'nodelist' + ); + foreach ($key2loop as $key) { + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; + } + return $args; +} +?> diff --git a/lib/ajax/getUsersWithRight.php b/lib/ajax/getUsersWithRight.php index 62605f8e30..13fb6b9f84 100644 --- a/lib/ajax/getUsersWithRight.php +++ b/lib/ajax/getUsersWithRight.php @@ -1,13 +1,13 @@ array(tlInputParameter::STRING_N,0,100,'/^[a-z0-9_]+$/') - ); + "right" => array(tlInputParameter::STRING_N,0,100,'/^[a-z0-9_]+$/') +); $args = G_PARAMS($iParams); // user must have the same right as requested (security) if (has_rights($db,$args['right'])) { - $tlUser = new tlUser($_SESSION['userID']); - $data['rows'] = $tlUser->getNamesForProjectRight($db,$args['right'],$_SESSION['testprojectID']); - $data['rows'][] = array('id'=>'0','login'=>' ','first'=>' ','last'=>' '); // option for no owner + $tlUser = new tlUser($_SESSION['userID']); + $data['rows'] = $tlUser->getNamesForProjectRight($db,$args['right'],$_SESSION['testprojectID']); + $data['rows'][] = array('id'=>'0','login'=>' ','first'=>' ','last'=>' '); // option for no owner } else { - tLog('Invalid right for the user: '.$args['right'], 'ERROR'); + tLog('Invalid right for the user: '.$args['right'], 'ERROR'); } echo json_encode($data); -?> \ No newline at end of file +?> diff --git a/lib/ajax/getcodetrackercfgtemplate.php b/lib/ajax/getcodetrackercfgtemplate.php index 759f64421f..94b63a6a25 100755 --- a/lib/ajax/getcodetrackercfgtemplate.php +++ b/lib/ajax/getcodetrackercfgtemplate.php @@ -1,44 +1,41 @@ - true, 'cfg' => ''); -$type = intval($_REQUEST['type']); -$itemMgr = new tlCodeTracker($db); -$ctt = $itemMgr->getTypes(); -if( isset($ctt[$type]) ) -{ - unset($ctt); - $cname = $itemMgr->getImplementationForType($type); - $info['cfg'] = stream_resolve_include_path($cname . '.class.php'); - - // Notes for developers - // Trying to use try/catch to manage missing interface file, results on nothing good. - // This way worked. - if( stream_resolve_include_path($cname . '.class.php') !== FALSE ) - { - $info['cfg'] = '
    ' . $cname::getCfgTemplate() . '
    '; - } - else - { - $info['cfg'] = sprintf(lang_get('codetracker_interface_not_implemented'),$cname); - } -} -else -{ - $info['cfg'] = sprintf(lang_get('codetracker_invalid_type'),$type); -} -echo json_encode($info); + true, + 'cfg' => '' +); +$type = intval($_REQUEST['type']); +$itemMgr = new tlCodeTracker($db); +$ctt = $itemMgr->getTypes(); +if (isset($ctt[$type])) { + unset($ctt); + $cname = $itemMgr->getImplementationForType($type); + $info['cfg'] = stream_resolve_include_path($cname . '.class.php'); + + // Notes for developers + // Trying to use try/catch to manage missing interface file, results on nothing good. + // This way worked. + if (stream_resolve_include_path($cname . '.class.php') !== false) { + $info['cfg'] = '
    ' . $cname::getCfgTemplate() . '
    '; + } else { + $info['cfg'] = sprintf( + lang_get('codetracker_interface_not_implemented'), $cname); + } +} else { + $info['cfg'] = sprintf(lang_get('codetracker_invalid_type'), $type); +} +echo json_encode($info); ?> diff --git a/lib/ajax/getissuetrackercfgtemplate.php b/lib/ajax/getissuetrackercfgtemplate.php index ab8d13899e..c1d2cef880 100644 --- a/lib/ajax/getissuetrackercfgtemplate.php +++ b/lib/ajax/getissuetrackercfgtemplate.php @@ -1,49 +1,46 @@ - true, 'cfg' => ''); -$type = intval($_REQUEST['type']); -$itemMgr = new tlIssueTracker($db); -$itt = $itemMgr->getTypes(); -if( isset($itt[$type]) ) -{ - unset($itt); - $iname = $itemMgr->getImplementationForType($type); - $info['cfg'] = stream_resolve_include_path($iname . '.class.php'); - - // Notes for developers - // Trying to use try/catch to manage missing interface file, results on nothing good. - // This way worked. - if( stream_resolve_include_path($iname . '.class.php') !== FALSE ) - { - $info['cfg'] = '
    ' . $iname::getCfgTemplate() . '
    '; - } - else - { - $info['cfg'] = sprintf(lang_get('issuetracker_interface_not_implemented'),$iname); - } -} -else -{ - $info['cfg'] = sprintf(lang_get('issuetracker_invalid_type'),$type); -} -echo json_encode($info); -?> \ No newline at end of file + true, + 'cfg' => '' +); +$type = intval($_REQUEST['type']); +$itemMgr = new tlIssueTracker($db); +$itt = $itemMgr->getTypes(); +if (isset($itt[$type])) { + unset($itt); + $iname = $itemMgr->getImplementationForType($type); + $info['cfg'] = stream_resolve_include_path($iname . '.class.php'); + + // Notes for developers + // Trying to use try/catch to manage missing interface file, results on nothing good. + // This way worked. + if (stream_resolve_include_path($iname . '.class.php') !== false) { + $info['cfg'] = '
    ' . $iname::getCfgTemplate() . '
    '; + } else { + $info['cfg'] = sprintf( + lang_get('issuetracker_interface_not_implemented'), $iname); + } +} else { + $info['cfg'] = sprintf(lang_get('issuetracker_invalid_type'), $type); +} +echo json_encode($info); +?> diff --git a/lib/ajax/getreqcoveragenodes.php b/lib/ajax/getreqcoveragenodes.php index 256cd9d3dd..667905811f 100644 --- a/lib/ajax/getreqcoveragenodes.php +++ b/lib/ajax/getreqcoveragenodes.php @@ -1,144 +1,158 @@ -child_requirements_mgmt) - { - $forbidden_parent['requirement_spec'] = 'none'; - } - - switch($operation) - { - - case 'print': - $js_function = array('testproject' => 'TPROJECT_PTP', - 'requirement_spec' =>'TPROJECT_PRS', 'requirement' => 'TPROJECT_PRS'); - break; - - case 'manage': - default: - $js_function = array('testproject' => 'EP','requirement_spec' =>'ERS', 'requirement' => 'ER'); - break; - } - - $nodes = null; - $filter_node_type = $show_children ? '' : ",'requirement'"; - $sql = " SELECT NHA.*, NT.description AS node_type, RSPEC.doc_id " . - " FROM {$tables['nodes_hierarchy']} NHA JOIN {$tables['node_types']} NT " . - " ON NHA.node_type_id=NT.id " . - " AND NT.description NOT IN " . - " ('testcase','testsuite','testcase_version','testplan','requirement_spec_revision' {$filter_node_type}) " . - " LEFT OUTER JOIN {$tables['req_specs']} RSPEC " . - " ON RSPEC.id = NHA.id " . - " WHERE NHA.parent_id = " . intval($parent); - - // file_put_contents('/tmp/getrequirementnodes.php.txt', $sql); - if(!is_null($filter_node) && $filter_node > 0 && $parent == $root_node) - { - $sql .= " AND NHA.id = " . intval($filter_node); - } - $sql .= " ORDER BY NHA.node_order "; - - $nodeSet = $dbHandler->get_recordset($sql); - if(!is_null($nodeSet)) - { - $sql = " SELECT DISTINCT req_doc_id AS doc_id,NHA.id" . - " FROM {$tables['requirements']} REQ JOIN {$tables['nodes_hierarchy']} NHA ON NHA.id = REQ.id " . - " JOIN {$tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . - " JOIN {$tables['node_types']} NT ON NT.id = NHA.node_type_id " . - " WHERE NHB.id = " . intval($parent) . " AND NT.description = 'requirement'"; - $requirements = $dbHandler->fetchRowsIntoMap($sql,'id'); - - $treeMgr = new tree($dbHandler); - $ntypes = $treeMgr->get_available_node_types(); - $peerTypes = array('target' => $ntypes['requirement'], 'container' => $ntypes['requirement_spec']); - foreach($nodeSet as $key => $row) - { - $path['text'] = htmlspecialchars($row['name']); - $path['id'] = $row['id']; - - // this attribute/property is used on custom code on drag and drop - $path['position'] = $row['node_order']; - $path['leaf'] = false; - $path['cls'] = 'folder'; - - $path['testlink_node_type'] = $row['node_type']; - $path['testlink_node_name'] = $path['text']; // already htmlspecialchars() done - - $path['forbidden_parent'] = 'none'; - switch($row['node_type']) - { - case 'testproject': - $path['href'] = "javascript:EP({$path['id']})"; - $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; - break; - - case 'requirement_spec': - $req_list = array(); - $treeMgr->getAllItemsID($row['id'],$req_list,$peerTypes); - - $path['href'] = "javascript:" . $js_function[$row['node_type']]. "({$path['id']})"; - $path['text'] = htmlspecialchars($row['doc_id'] . ":") . $path['text']; - $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; - if(!is_null($req_list)) - { - $item_qty = count($req_list); - $path['text'] .= " ({$item_qty})"; - } - break; - - case 'requirement': - $path['href'] = "javascript:" . $js_function[$row['node_type']]. "({$path['id']})"; - $path['text'] = htmlspecialchars($requirements[$row['id']]['doc_id'] . ":") . $path['text']; - $path['leaf'] = true; - $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; - break; - } - - $nodes[] = $path; - } // foreach - } - return $nodes; -} \ No newline at end of file +child_requirements_mgmt) { + $forbidden_parent['requirement_spec'] = 'none'; + } + + switch ($operation) { + + case 'print': + $js_function = array( + 'testproject' => 'TPROJECT_PTP', + 'requirement_spec' => 'TPROJECT_PRS', + 'requirement' => 'TPROJECT_PRS' + ); + break; + + case 'manage': + default: + $js_function = array( + 'testproject' => 'EP', + 'requirement_spec' => 'ERS', + 'requirement' => 'ER' + ); + break; + } + + $nodes = null; + $filter_node_type = $show_children ? '' : ",'requirement'"; + $sql = " SELECT NHA.*, NT.description AS node_type, RSPEC.doc_id " . + " FROM {$tables['nodes_hierarchy']} NHA JOIN {$tables['node_types']} NT " . + " ON NHA.node_type_id=NT.id " . " AND NT.description NOT IN " . + " ('testcase','testsuite','testcase_version','testplan','requirement_spec_revision' {$filter_node_type}) " . + " LEFT OUTER JOIN {$tables['req_specs']} RSPEC " . + " ON RSPEC.id = NHA.id " . " WHERE NHA.parent_id = " . intval($parent); + + if (! is_null($filter_node) && $filter_node > 0 && $parent == $root_node) { + $sql .= " AND NHA.id = " . intval($filter_node); + } + $sql .= " ORDER BY NHA.node_order "; + + $nodeSet = $dbHandler->get_recordset($sql); + if (! is_null($nodeSet)) { + $sql = " SELECT DISTINCT req_doc_id AS doc_id,NHA.id" . + " FROM {$tables['requirements']} REQ JOIN {$tables['nodes_hierarchy']} NHA ON NHA.id = REQ.id " . + " JOIN {$tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . + " JOIN {$tables['node_types']} NT ON NT.id = NHA.node_type_id " . + " WHERE NHB.id = " . intval($parent) . + " AND NT.description = 'requirement'"; + $requirements = $dbHandler->fetchRowsIntoMap($sql, 'id'); + + $treeMgr = new tree($dbHandler); + $ntypes = $treeMgr->get_available_node_types(); + $peerTypes = array( + 'target' => $ntypes['requirement'], + 'container' => $ntypes['requirement_spec'] + ); + foreach ($nodeSet as $row) { + $path['text'] = htmlspecialchars($row['name']); + $path['id'] = $row['id']; + + // this attribute/property is used on custom code on drag and drop + $path['position'] = $row['node_order']; + $path['leaf'] = false; + $path['cls'] = 'folder'; + + $path['testlink_node_type'] = $row['node_type']; + $path['testlink_node_name'] = $path['text']; // already htmlspecialchars() done + + $path['forbidden_parent'] = 'none'; + switch ($row['node_type']) { + case 'testproject': + $path['href'] = "javascript:EP({$path['id']})"; + $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; + break; + + case 'requirement_spec': + $req_list = array(); + $treeMgr->getAllItemsID($row['id'], $req_list, $peerTypes); + + $path['href'] = "javascript:" . + $js_function[$row['node_type']] . "({$path['id']})"; + $path['text'] = htmlspecialchars($row['doc_id'] . ":") . + $path['text']; + $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; + if (! is_null($req_list)) { + $item_qty = count($req_list); + $path['text'] .= " ({$item_qty})"; + } + break; + + case 'requirement': + $path['href'] = "javascript:" . + $js_function[$row['node_type']] . "({$path['id']})"; + $path['text'] = htmlspecialchars( + $requirements[$row['id']]['doc_id'] . ":") . + $path['text']; + $path['leaf'] = true; + $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; + break; + } + + $nodes[] = $path; + } + } + return $nodes; +} diff --git a/lib/ajax/getreqlog.php b/lib/ajax/getreqlog.php index f302c3954d..564a71c006 100644 --- a/lib/ajax/getreqlog.php +++ b/lib/ajax/getreqlog.php @@ -1,47 +1,48 @@ -tree_mgr->get_available_node_types(); - $dummy = $reqMgr->tree_mgr->get_node_hierarchy_info($item_id); - - - $target_table = 'req_revisions'; - if($dummy['node_type_id'] == $node_types['requirement_version'] ) - { - $target_table = 'req_versions'; - } - $sql = "SELECT log_message FROM {$tables[$target_table]} WHERE id=" . intval($item_id); - $info = $db->get_recordset($sql); - $info = nl2br($info[0]['log_message']); - - //

    and

    tag at the beginning and the end of summary cause visualization - // errors -> remove them and add
    to get a similar effect - $info = str_replace("

    ","",$info); - $info = str_replace("

    ","
    ",$info); - - // if log message is empty show this information - if ($info == "") { - $info = lang_get("empty_log_message"); - } -} -echo $info; \ No newline at end of file +tree_mgr->get_available_node_types(); + $dummy = $reqMgr->tree_mgr->get_node_hierarchy_info($item_id); + + $target_table = 'req_revisions'; + if ($dummy['node_type_id'] == $node_types['requirement_version']) { + $target_table = 'req_versions'; + } + $sql = "SELECT log_message FROM {$tables[$target_table]} WHERE id=" . + intval($item_id); + $info = $db->get_recordset($sql); + $info = nl2br($info[0]['log_message']); + + //

    and

    tag at the beginning and the end of summary cause visualization + // errors -> remove them and add
    to get a similar effect + $info = str_replace("

    ", "", $info); + $info = str_replace("

    ", "
    ", $info); + + // if log message is empty show this information + if ($info == "") { + $info = lang_get("empty_log_message"); + } +} +echo $info; diff --git a/lib/ajax/getreqmgrsystemcfgtemplate.php b/lib/ajax/getreqmgrsystemcfgtemplate.php index 84818e2fe1..1131f4cc92 100644 --- a/lib/ajax/getreqmgrsystemcfgtemplate.php +++ b/lib/ajax/getreqmgrsystemcfgtemplate.php @@ -1,49 +1,46 @@ - true, 'cfg' => ''); -$type = intval($_REQUEST['type']); -$mgr = new tlReqMgrSystem($db); -$itt = $mgr->getTypes(); -if( isset($itt[$type]) ) -{ - unset($itt); - $iname = $mgr->getImplementationForType($type); - $info['cfg'] = stream_resolve_include_path($iname . '.class.php'); - - // Notes for developers - // Trying to use try/catch to manage missing interface file, results on nothing good. - // This way worked. - if( stream_resolve_include_path($iname . '.class.php') !== FALSE ) - { - $info['cfg'] = '
    ' . $iname::getCfgTemplate() . '
    '; - } - else - { - $info['cfg'] = sprintf(lang_get('reqmgrsystem_interface_not_implemented'),$iname); - } -} -else -{ - $info['cfg'] = sprintf(lang_get('reqmgrsystem_invalid_type'),$type); -} -echo json_encode($info); -?> \ No newline at end of file + true, + 'cfg' => '' +); +$type = intval($_REQUEST['type']); +$mgr = new tlReqMgrSystem($db); +$itt = $mgr->getTypes(); +if (isset($itt[$type])) { + unset($itt); + $iname = $mgr->getImplementationForType($type); + $info['cfg'] = stream_resolve_include_path($iname . '.class.php'); + + // Notes for developers + // Trying to use try/catch to manage missing interface file, results on nothing good. + // This way worked. + if (stream_resolve_include_path($iname . '.class.php') !== false) { + $info['cfg'] = '
    ' . $iname::getCfgTemplate() . '
    '; + } else { + $info['cfg'] = sprintf( + lang_get('reqmgrsystem_interface_not_implemented'), $iname); + } +} else { + $info['cfg'] = sprintf(lang_get('reqmgrsystem_invalid_type'), $type); +} +echo json_encode($info); +?> diff --git a/lib/ajax/getreqspeclog.php b/lib/ajax/getreqspeclog.php index ec26c95507..450aa380ce 100644 --- a/lib/ajax/getreqspeclog.php +++ b/lib/ajax/getreqspeclog.php @@ -1,14 +1,14 @@ child_requirements_mgmt) { $forbidden_parent['requirement_spec'] = 'none'; - } + } $fn = array(); $fn['print']['reqspec'] = array('testproject' => 'TPROJECT_PTP_RS', @@ -73,54 +73,53 @@ function display_children($dbHandler,$root_node,$parent,$filter_node, default: $js_function=$fn['manage'][$mode]; - break; + break; } $nodes = null; $filter_node_type = $show_children ? '' : ",'requirement'"; - $sql = " SELECT NHA.*, NT.description AS node_type, RSPEC.doc_id " . + $sql = " SELECT NHA.*, NT.description AS node_type, RSPEC.doc_id " . " FROM {$tables['nodes_hierarchy']} NHA JOIN {$tables['node_types']} NT " . " ON NHA.node_type_id=NT.id " . - " AND NT.description NOT IN " . + " AND NT.description NOT IN " . " ('testcase','testsuite','testcase_version','testplan','requirement_spec_revision' {$filter_node_type}) " . " LEFT OUTER JOIN {$tables['req_specs']} RSPEC " . - " ON RSPEC.id = NHA.id " . + " ON RSPEC.id = NHA.id " . " WHERE NHA.parent_id = " . intval($parent); if(!is_null($filter_node) && $filter_node > 0 && $parent == $root_node) { - $sql .= " AND NHA.id = " . intval($filter_node); + $sql .= " AND NHA.id = " . intval($filter_node); } - $sql .= " ORDER BY NHA.node_order "; + $sql .= " ORDER BY NHA.node_order "; $nodeSet = $dbHandler->get_recordset($sql); - if(!is_null($nodeSet)) + if(!is_null($nodeSet)) { $sql = " SELECT DISTINCT req_doc_id AS doc_id,NHA.id" . " FROM {$tables['requirements']} REQ JOIN {$tables['nodes_hierarchy']} NHA ON NHA.id = REQ.id " . - " JOIN {$tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . + " JOIN {$tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . " JOIN {$tables['node_types']} NT ON NT.id = NHA.node_type_id " . " WHERE NHB.id = " . intval($parent) . " AND NT.description = 'requirement'"; $requirements = $dbHandler->fetchRowsIntoMap($sql,'id'); $treeMgr = new tree($dbHandler); $ntypes = $treeMgr->get_available_node_types(); - $peerTypes = array('target' => $ntypes['requirement'], 'container' => $ntypes['requirement_spec']); - foreach($nodeSet as $key => $row) + $peerTypes = array('target' => $ntypes['requirement'], 'container' => $ntypes['requirement_spec']); + foreach($nodeSet as $row) { - $path['text'] = htmlspecialchars($row['name']); - $path['id'] = $row['id']; + $path['text'] = htmlspecialchars($row['name']); + $path['id'] = $row['id']; // this attribute/property is used on custom code on drag and drop - $path['position'] = $row['node_order']; + $path['position'] = $row['node_order']; $path['leaf'] = false; $path['cls'] = 'folder'; // Important: // We can add custom keys, and will be able to access it using - // public property 'attributes' of object of Class Ext.tree.TreeNode - // - $path['testlink_node_type'] = $row['node_type']; + // public property 'attributes' of object of Class Ext.tree.TreeNode + $path['testlink_node_type'] = $row['node_type']; $path['testlink_node_name'] = $path['text']; // already htmlspecialchars() done $path['forbidden_parent'] = 'none'; @@ -141,7 +140,7 @@ function display_children($dbHandler,$root_node,$parent,$filter_node, if(!is_null($req_list)) { $item_qty = count($req_list); - $path['text'] .= " ({$item_qty})"; + $path['text'] .= " ({$item_qty})"; } break; @@ -153,8 +152,8 @@ function display_children($dbHandler,$root_node,$parent,$filter_node, break; } - $nodes[] = $path; - } // foreach + $nodes[] = $path; + } } - return $nodes; -} \ No newline at end of file + return $nodes; +} diff --git a/lib/ajax/gettestcasesummary.php b/lib/ajax/gettestcasesummary.php index 965f3e1528..0fa08a619c 100644 --- a/lib/ajax/gettestcasesummary.php +++ b/lib/ajax/gettestcasesummary.php @@ -1,47 +1,47 @@ - 0 ) { - if($tcversion_id > 0 ) { - $tcase = $tcase_mgr->get_by_id($tcase_id,$tcversion_id); - if(!is_null($tcase)) { - $tcase = $tcase[0]; - } - } else { - $tcase = $tcase_mgr->get_last_version_info($tcase_id); - } - $info = $tcase['summary']; - - //

    and

    tag at the beginning and the end of summary cause visualization - // errors -> remove them and add
    to get a similar effect - $info = str_replace("

    ","",$info); - $info = str_replace("

    ","
    ",$info); - - if ($info == "") { - $info = lang_get("empty_tc_summary"); - } else { - $info = '' . lang_get('summary') . '
    ' . $info; - } -} -echo $info; \ No newline at end of file + 0) { + if ($tcversion_id > 0) { + $tcase = $tcaseMgr->get_by_id($tcase_id, $tcversion_id); + if (! is_null($tcase)) { + $tcase = $tcase[0]; + } + } else { + $tcase = $tcaseMgr->getLastVersionInfo($tcase_id); + } + $info = $tcase['summary']; + + //

    and

    tag at the beginning and the end of summary cause visualization + // errors -> remove them and add
    to get a similar effect + $info = str_replace("

    ", "", $info); + $info = str_replace("

    ", "
    ", $info); + + if ($info == "") { + $info = lang_get("empty_tc_summary"); + } else { + $info = '' . lang_get('summary') . '
    ' . $info; + } +} +echo $info; diff --git a/lib/ajax/gettprojectnodes.php b/lib/ajax/gettprojectnodes.php index 1f150318a7..e02daef274 100644 --- a/lib/ajax/gettprojectnodes.php +++ b/lib/ajax/gettprojectnodes.php @@ -1,204 +1,229 @@ - 'none','testcase' => 'testproject', 'testsuite' => 'none'); - $external = ''; - $nodes = null; - $filter_node_type = $show_tcases ? '' : ",'testcase'"; - - switch($operation) - { - case 'print': - $js_function = array('testproject' => 'TPROJECT_PTP', - 'testsuite' =>'TPROJECT_PTS', 'testcase' => 'TPROJECT_PTS'); - break; - - case 'manage': - default: - $js_function = array('testproject' => 'EP','testsuite' =>'ETS', 'testcase' => 'ET'); - break; - } - - $sql = " SELECT NHA.*, NT.description AS node_type " . - " FROM {$tables['nodes_hierarchy']} NHA, {$tables['node_types']} NT " . - " WHERE NHA.node_type_id = NT.id " . - " AND parent_id = " . intval($parent) . - " AND NT.description NOT IN " . - " ('testcase_version','testplan','requirement_spec','requirement'{$filter_node_type}) "; - - if(!is_null($filter_node) && $filter_node > 0 && $parent == $root_node) - { - $sql .=" AND NHA.id = " . intval($filter_node); - } - $sql .= " ORDER BY NHA.node_order "; - - - $nodeSet = $dbHandler->get_recordset($sql); - - if($show_tcases) { - // Get external id, used on test case nodes - $sql = " SELECT DISTINCT tc_external_id,NHA.parent_id " . - " FROM {$tables['tcversions']} TCV " . - " JOIN {$tables['nodes_hierarchy']} NHA ON NHA.id = TCV.id " . - " JOIN {$tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . - " WHERE NHB.parent_id = " . intval($parent) . " AND NHA.node_type_id = 4"; - $external = $dbHandler->fetchRowsIntoMap($sql,'parent_id'); - } - - if(!is_null($nodeSet)) { - foreach($nodeSet as $key => $row) { - $path['text'] = htmlspecialchars($row['name']); - $path['id'] = $row['id']; - - // this attribute/property is used on custom code on drag and drop - $path['position'] = $row['node_order']; - $path['leaf'] = false; - $path['cls'] = 'folder'; - - // customs key will be accessed using node.attributes.[key name] - $path['testlink_node_type'] = $row['node_type']; - $path['testlink_node_name'] = $path['text']; // already htmlspecialchars() done - $path['forbidden_parent'] = 'none'; - - $tcase_qty = null; - switch($row['node_type']) - { - case 'testproject': - // at least on Test Specification seems that we do not execute this piece of code. - $path['href'] = "javascript:EP({$path['id']})"; - $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; - break; - - case 'testsuite': - $items = array(); - getAllTCasesID($row['id'],$items); - $tcase_qty = sizeof($items); - - $path['href'] = "javascript:" . $js_function[$row['node_type']]. "({$path['id']})"; - $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; - break; - - case 'testcase': - $path['href'] = "javascript:" . $js_function[$row['node_type']]. "({$path['id']})"; - $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; - if(is_null($showTestCaseID)) - { - $showTestCaseID = config_get('treemenu_show_testcase_id'); - } - if($showTestCaseID) - { - $path['text'] = htmlspecialchars($tcprefix . $external[$row['id']]['tc_external_id'] . ":") . $path['text']; - } - $path['leaf'] = true; - break; - } - - if(!is_null($tcase_qty)) - { - $path['text'] .= " ({$tcase_qty})"; - } - - switch($row['node_type']) - { - case 'testproject': - case 'testsuite': - if( isset($helpText[$row['node_type']]) ) - { - $path['text'] = '' . $path['text'] . ''; - } - break; - } - - $nodes[] = $path; - } - } - return $nodes; -} - - -/** - * - */ -function getAllTCasesID($idList,&$tcIDs) { - - global $db; // I'm sorry for the global coupling - $tcNodeTypeID = 3; - $tsuiteNodeTypeID = 2; - - $tbl = DB_TABLE_PREFIX . 'nodes_hierarchy'; - $sql = " SELECT id,node_type_id FROM $tbl + 'none', + 'testcase' => 'testproject', + 'testsuite' => 'none' + ); + $external = ''; + $nodes = null; + $filter_node_type = $show_tcases ? '' : ",'testcase'"; + + switch ($operation) { + case 'print': + $js_function = array( + 'testproject' => 'TPROJECT_PTP', + 'testsuite' => 'TPROJECT_PTS', + 'testcase' => 'TPROJECT_PTS' + ); + break; + + case 'manage': + default: + $js_function = array( + 'testproject' => 'EP', + 'testsuite' => 'ETS', + 'testcase' => 'ET' + ); + break; + } + + $sql = " SELECT NHA.*, NT.description AS node_type " . + " FROM {$tables['nodes_hierarchy']} NHA, {$tables['node_types']} NT " . + " WHERE NHA.node_type_id = NT.id " . " AND parent_id = " . + intval($parent) . " AND NT.description NOT IN " . + " ('testcase_version','testplan','requirement_spec','requirement'{$filter_node_type}) "; + + if (! is_null($filter_node) && $filter_node > 0 && $parent == $root_node) { + $sql .= " AND NHA.id = " . intval($filter_node); + } + $sql .= " ORDER BY NHA.node_order "; + + $nodeSet = $dbHandler->get_recordset($sql); + + if ($show_tcases) { + // Get external id, used on test case nodes + $sql = " SELECT DISTINCT tc_external_id,NHA.parent_id " . + " FROM {$tables['tcversions']} TCV " . + " JOIN {$tables['nodes_hierarchy']} NHA ON NHA.id = TCV.id " . + " JOIN {$tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . + " WHERE NHB.parent_id = " . intval($parent) . + " AND NHA.node_type_id = 4"; + $external = $dbHandler->fetchRowsIntoMap($sql, 'parent_id'); + } + + if (! is_null($nodeSet)) { + foreach ($nodeSet as $row) { + $path['text'] = htmlspecialchars($row['name']); + $path['id'] = $row['id']; + + // this attribute/property is used on custom code on drag and drop + $path['position'] = $row['node_order']; + $path['leaf'] = false; + $path['cls'] = 'folder'; + + // customs key will be accessed using node.attributes.[key name] + $path['testlink_node_type'] = $row['node_type']; + $path['testlink_node_name'] = $path['text']; // already htmlspecialchars() done + $path['forbidden_parent'] = 'none'; + + $tcase_qty = null; + switch ($row['node_type']) { + case 'testproject': + // at least on Test Specification seems that we do not execute this piece of code. + $path['href'] = "javascript:EP({$path['id']})"; + $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; + break; + + case 'testsuite': + $items = array(); + getAllTCasesID($row['id'], $items); + $tcase_qty = count($items); + + $path['href'] = "javascript:" . + $js_function[$row['node_type']] . "({$path['id']})"; + $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; + break; + + case 'testcase': + $path['href'] = "javascript:" . + $js_function[$row['node_type']] . "({$path['id']})"; + $path['forbidden_parent'] = $forbidden_parent[$row['node_type']]; + if (is_null($showTestCaseID)) { + $showTestCaseID = config_get( + 'treemenu_show_testcase_id'); + } + if ($showTestCaseID) { + $path['text'] = htmlspecialchars( + $tcprefix . $external[$row['id']]['tc_external_id'] . + ":") . $path['text']; + } + $path['leaf'] = true; + break; + } + + if (! is_null($tcase_qty)) { + $path['text'] .= " ({$tcase_qty})"; + } + + switch ($row['node_type']) { + case 'testproject': + case 'testsuite': + if (isset($helpText[$row['node_type']])) { + $path['text'] = '' . $path['text'] . + ''; + } + break; + } + + $nodes[] = $path; + } + } + return $nodes; +} + +/** + * + * @param string $idList + * @param array $tcIDs + */ +function getAllTCasesID($idList, &$tcIDs) +{ + global $db; // I'm sorry for the global coupling + $tcNodeTypeID = 3; + + $tbl = DB_TABLE_PREFIX . 'nodes_hierarchy'; + $sql = " SELECT id,node_type_id FROM $tbl WHERE parent_id IN ($idList) - AND node_type_id IN (3,2) "; - - $result = $db->exec_query($sql); - if ($result) { - $suiteIDs = array(); - while($row = $db->fetch_array($result)) { - if ($row['node_type_id'] == $tcNodeTypeID) { - $tcIDs[] = $row['id']; - } else { - $suiteIDs[] = $row['id']; - } - } - if (sizeof($suiteIDs)) { - $suiteIDs = implode(",",$suiteIDs); - getAllTCasesID($suiteIDs,$tcIDs); - } - } + AND node_type_id IN (3,2) "; + + $result = $db->exec_query($sql); + if ($result) { + $suiteIDs = array(); + while ($row = $db->fetch_array($result)) { + if ($row['node_type_id'] == $tcNodeTypeID) { + $tcIDs[] = $row['id']; + } else { + $suiteIDs[] = $row['id']; + } + } + if (count($suiteIDs)) { + $suiteIDs = implode(",", $suiteIDs); + getAllTCasesID($suiteIDs, $tcIDs); + } + } } diff --git a/lib/ajax/requirements/getreqmonitors.php b/lib/ajax/requirements/getreqmonitors.php index 41616f911a..b8acc38f3e 100644 --- a/lib/ajax/requirements/getreqmonitors.php +++ b/lib/ajax/requirements/getreqmonitors.php @@ -1,14 +1,14 @@ data = $mon; } -echo json_encode($ou); \ No newline at end of file +echo json_encode($ou); diff --git a/lib/ajax/stepReorder.php b/lib/ajax/stepReorder.php new file mode 100644 index 0000000000..a86e07a17d --- /dev/null +++ b/lib/ajax/stepReorder.php @@ -0,0 +1,53 @@ +stepSeq != '') { + $xx = explode('&', $args->stepSeq); + $point = 1; + $renumbered = []; + foreach ($xx as $step_id) { + $renumbered[$step_id] = $point ++; + } + + // Get test case version id from 1 step + $nt = $tcaseMgr->tree_manager->get_available_node_types(); + $tables = tlObjectWithDB::getDBTables(array( + 'tcsteps', + 'nodes_hierarchy' + )); + $sql = "SELECT NH_STEPS.parent_id + FROM {$tables['nodes_hierarchy']} NH_STEPS + WHERE NH_STEPS.id = {$xx[0]} + AND NH_STEPS.node_type_id = {$nt['testcase_step']}"; + + $tcaseMgr->set_step_number($renumbered); + file_put_contents('/var/testlink/logs/stepReorder.log', + json_encode($renumbered)); + + echo json_encode($renumbered); +} + +/** + */ +function initArgs() +{ + $args = new stdClass(); + $args->stepSeq = isset($_REQUEST["stepSeq"]) ? $_REQUEST["stepSeq"] : ""; + + return $args; +} diff --git a/lib/api/rest/v1/index.php b/lib/api/rest/v1/index.php index 11c56ff419..5d56a2b073 100644 --- a/lib/api/rest/v1/index.php +++ b/lib/api/rest/v1/index.php @@ -1,20 +1,19 @@ -app->run(); diff --git a/lib/api/rest/v1/tlRestApi.class.php b/lib/api/rest/v1/tlRestApi.class.php index ddb42dfcfe..026beefe5b 100644 --- a/lib/api/rest/v1/tlRestApi.class.php +++ b/lib/api/rest/v1/tlRestApi.class.php @@ -1,721 +1,821 @@ - - * @package TestLink - * @since 1.9.7 - * - * References - * http://ericbrandel.com/2013/01/14/quickly-build-restful-apis-in-php-with-slim-part-2/ - * https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+Add+Comment - * http://confluence.jetbrains.com/display/YTD4/Create+New+Work+Item - * http://www.redmine.org/projects/redmine/wiki/Rest_api - * http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/ - * https://github.com/educoder/pest/blob/master/examples/intouch_example.php - * http://stackoverflow.com/questions/9772933/rest-api-request-body-as-json-or-plain-post-data - * - * http://phptrycatch.blogspot.it/ - * http://nitschinger.at/A-primer-on-PHP-exceptions - * - - * - * - * @internal revisions - * @since 1.9.11 - * - */ - -require_once('../../../../config.inc.php'); -require_once('common.php'); -require 'Slim/Slim.php'; -\Slim\Slim::registerAutoloader(); - -/** - * @author Francisco Mancardi - * @package TestLink - */ -class tlRestApi -{ - public static $version = "1.0"; - - - /** - * The DB object used throughout the class - * - * @access protected - */ - protected $db = null; - protected $tables = null; - - protected $tcaseMgr = null; - protected $tprojectMgr = null; - protected $tsuiteMgr = null; - protected $tplanMgr = null; - protected $tplanMetricsMgr = null; - protected $reqSpecMgr = null; - protected $reqMgr = null; - protected $platformMgr = null; - - /** userID associated with the apiKey provided */ - protected $userID = null; - - /** UserObject associated with the userID */ - protected $user = null; - - /** array where all the args are stored for requests */ - protected $args = null; - - /** array where error codes and messages are stored */ - protected $errors = array(); - - /** The api key being used to make a request */ - protected $apiKey = null; - - /** boolean to allow a method to invoke another method and avoid double auth */ - protected $authenticated = false; - - /** The version of a test case that is being used */ - /** This value is setted in following method: */ - /** _checkTCIDAndTPIDValid() */ - protected $tcVersionID = null; - protected $versionNumber = null; - - - - public $statusCode; - public $codeStatus; - - - /** - */ - public function __construct() - { - // We are following Slim naming convention - $this->app = new \Slim\Slim(); - $this->app->contentType('application/json'); - - - // test route with anonymous function - $this->app->get('/who', function () { echo __CLASS__ . ' : Get Route /who';}); - - $this->app->get('/whoAmI', array($this,'authenticate'), array($this,'whoAmI')); - $this->app->get('/testprojects', array($this,'authenticate'), array($this,'getProjects')); - - $this->app->get('/testprojects/:id', array($this,'authenticate'), array($this,'getProjects')); - $this->app->get('/testprojects/:id/testcases', array($this,'authenticate'), array($this,'getProjectTestCases')); - $this->app->get('/testprojects/:id/testplans', array($this,'authenticate'), array($this,'getProjectTestPlans')); - - $this->app->post('/testprojects', array($this,'authenticate'), array($this,'createTestProject')); - $this->app->post('/executions', array($this,'authenticate'), array($this,'createTestCaseExecution')); - $this->app->post('/testplans', array($this,'authenticate'), array($this,'createTestPlan')); - $this->app->post('/testplans/:id', array($this,'authenticate'), array($this,'updateTestPlan')); - - $this->app->post('/testsuites', array($this,'authenticate'), array($this,'createTestSuite')); - $this->app->post('/testcases', array($this,'authenticate'), array($this,'createTestCase')); - - // $this->app->get('/testplans/:id', array($this,'getTestPlan')); - - - $this->db = new database(DB_TYPE); - $this->db->db->SetFetchMode(ADODB_FETCH_ASSOC); - doDBConnect($this->db,database::ONERROREXIT); - - - $this->tcaseMgr=new testcase($this->db); - $this->tprojectMgr=new testproject($this->db); - $this->tsuiteMgr=new testsuite($this->db); - - $this->tplanMgr=new testplan($this->db); - $this->tplanMetricsMgr=new tlTestPlanMetrics($this->db); - $this->reqSpecMgr=new requirement_spec_mgr($this->db); - $this->reqMgr=new requirement_mgr($this->db); - $this->cfieldMgr=$this->tprojectMgr->cfield_mgr; - - $this->tables = $this->tcaseMgr->getDBTables(); - - $resultsCfg = config_get('results'); - foreach($resultsCfg['status_label_for_exec_ui'] as $key => $label ) - { - $this->statusCode[$key]=$resultsCfg['status_code'][$key]; - } - - if( isset($this->statusCode['not_run']) ) - { - unset($this->statusCode['not_run']); - } - $this->codeStatus=array_flip($this->statusCode); - } - - - /** - * - */ - function authenticate(\Slim\Route $route) - { - $apiKey = null; - if(is_null($apiKey)) - { - $request = $this->app->request(); - $apiKey = $request->headers('PHP_AUTH_USER'); - } - - $sql = "SELECT id FROM {$this->tables['users']} " . - "WHERE script_key='" . $this->db->prepare_string($apiKey) . "'"; - - $this->userID = $this->db->fetchFirstRowSingleColumn($sql, "id"); - if( ($ok=!is_null($this->userID)) ) - { - $this->user = tlUser::getByID($this->db,$this->userID); - } - else - { - $this->app->status(400); - echo json_encode(array('status' => 'ko', 'message' => 'authentication error')); - $this->app->stop(); - } - - return $ok; - } - - - - /** - * - */ - public function whoAmI() - { - echo json_encode(array('name' => __CLASS__ . ' : Get Route /whoAmI')); - } - - /** - * - * @param mixed idCard if provided identifies test project - * if intval() > 0 => is considered DBID - * else => is used as PROJECT NAME - */ - public function getProjects($idCard=null, $opt=null) - { - $options = array_merge(array('output' => 'rest'), (array)$opt); - $op = array('status' => 'ok', 'message' => 'ok', 'item' => null); - if(is_null($idCard)) - { - $opOptions = array('output' => 'array_of_map', 'order_by' => " ORDER BY name ", 'add_issuetracker' => true, - 'add_reqmgrsystem' => true); - $op['item'] = $this->tprojectMgr->get_accessible_for_user($this->userID,$opOptions); - } - else - { - $opOptions = array('output' => 'map','field_set' => 'id', 'format' => 'simple'); - $zx = $this->tprojectMgr->get_accessible_for_user($this->userID,$opOptions); - if( ($safeID = intval($idCard)) > 0) - { - if( isset($zx[$safeID]) ) - { - $op['item'] = $this->tprojectMgr->get_by_id($safeID); - } - } - else - { - // Will consider id = name - foreach( $zx as $key => $value ) - { - if( strcmp($value['name'],$idCard) == 0 ) - { - $safeString = $this->db->prepare_string($idCard); - $op['item'] = $this->tprojectMgr->get_by_name($safeString); - break; - } - } - } - } - - // Developer (silly?) information - // json_encode() transforms maps in objects. - switch($options['output']) - { - case 'internal': - return $op['item']; - break; - - case 'rest': - default: - echo json_encode($op); - break; - } - } - - /** - * - * @param mixed idCard if provided identifies test project - * if intval() > 0 => is considered DBID - * else => is used as PROJECT NAME - */ - public function getProjectTestPlans($idCard) - { - $op = array('status' => 'ok', 'message' => 'ok', 'items' => null); - $tproject = $this->getProjects($idCard, array('output' => 'internal')); - - if( !is_null($tproject) ) - { - $items = $this->tprojectMgr->get_all_testplans($tproject[0]['id']); - $op['items'] = (!is_null($items) && count($items) > 0) ? $items : null; - } - else - { - $op['message'] = "No Test Project identified by '" . $idCard . "'!"; - $op['status'] = 'error'; - } - - echo json_encode($op); - } - - /** - * Will return LATEST VERSION of each test case. - * Does return test step info ? - * - * @param mixed idCard if provided identifies test project - * if intval() > 0 => is considered DBID - * else => is used as PROJECT NAME - */ - public function getProjectTestCases($idCard) - { - $op = array('status' => 'ok', 'message' => 'ok', 'items' => null); - $tproject = $this->getProjects($idCard, array('output' => 'internal')); - - if( !is_null($tproject) ) - { - $tcaseIDSet = array(); - $this->tprojectMgr->get_all_testcases_id($tproject[0]['id'],$tcaseIDSet); - if( !is_null($tcaseIDSet) && count($tcaseIDSet) > 0 ) - { - $op['items'] = array(); - foreach( $tcaseIDSet as $key => $tcaseID ) - { - $item = $this->tcaseMgr->get_last_version_info($tcaseID); - $item['keywords'] = $this->tcaseMgr->get_keywords_map($tcaseID); - $item['customfields'] = $this->tcaseMgr->get_linked_cfields_at_design($tcaseID,$item['tcversion_id'], - null,null,$tproject[0]['id']); - $op['items'][] = $item; - } - } - } - else - { - $op['message'] = "No Test Project identified by '" . $idCard . "'!"; - $op['status'] = 'error'; - } - - echo json_encode($op); - } - -// ============================================== - /** - * - * $item->name - * $item->prefix - * $item->notes - * $item->active - * $item->public - * $item->options - * $item->options->requirementsEnabled - * $item->options->testPriorityEnabled - * $item->options->automationEnabled - * $item->options->inventoryEnabled - */ - public function createTestProject() - { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try - { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $op['id'] = $this->tprojectMgr->create($item,array('doChecks' => true)); - $op = array('status' => 'ok', 'message' => 'ok'); - } - catch (Exception $e) - { - $op['message'] = $e->getMessage(); - } - echo json_encode($op); - } - - - - /** - * - * Request Body - * - * $ex->testPlanID - * $ex->buildID - * $ex->platformID - * $ex->testCaseExternalID - * $ex->notes - * $ex->statusCode - * - * - * Checks to be done - * - * A. User right & Test plan existence - * user has right to execute on target Test plan? - * this means also that: Test plan ID exists ? - * - * B. Build - * does Build ID exist on target Test plan ? - * is Build enable to execution ? - * - * C. Platform - * do we need a platform ID in order to execute ? - * is a platform present on provided data ? - * does this platform belong to target Test plan ? - * - * D. Test case identity - * is target Test case part of Test plan ? - * - * - * Z. Other mandatory information - * We are not going to check for other mandatory info - * like: mandatory custom fields. (if we will be able in future to manage it) - * - * - */ - public function createTestCaseExecution() - { - $op = array('status' => ' ko', 'message' => 'ko', 'id' => -1); - try - { - $request = $this->app->request(); - $ex = json_decode($request->getBody()); - $util = $this->checkExecutionEnvironment($ex); - - // If we are here this means we can write execution status!!! - $ex->testerID = $this->userID; - foreach($util as $prop => $value) - { - $ex->$prop = $value; - } - $op = array('status' => 'ok', 'message' => 'ok'); - $op['id'] = $this->tplanMgr->writeExecution($ex); - } - catch (Exception $e) - { - $op['message'] = $e->getMessage(); - } - echo json_encode($op); - } - - - - // - // Support methods - // - private function checkExecutionEnvironment($ex) - { - // throw new Exception($message, $code, $previous); - - // Test plan ID exists and is ACTIVE - $msg = 'invalid Test plan ID'; - $getOpt = array('output' => 'testPlanFields','active' => 1, - 'testPlanFields' => 'id,testproject_id,is_public'); - $status_ok = !is_null($testPlan=$this->tplanMgr->get_by_id($ex->testPlanID,$getOpt)); - - if($status_ok) - { - // user has right to execute on Test plan ID - // hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) - $msg = 'user has no right to execute'; - $status_ok = $this->user->hasRight($this->db,'testplan_execute', - $testPlan['testproject_id'],$ex->testPlanID,true); - } - - if($status_ok) - { - // Check if couple (buildID,testPlanID) is valid - $msg = '(buildID,testPlanID) couple is not valid'; - $getOpt = array('fields' => 'id,active,is_open', 'buildID' => $ex->buildID, 'orderBy' => null); - $status_ok = !is_null($build = $this->tplanMgr->get_builds($ex->testPlanID,null,null,$getOpt)); - - if($status_ok) - { - // now check is execution can be done againts this build - $msg = 'Build is not active and/or closed => execution can not be done'; - $status_ok = $build[$ex->buildID]['active'] && $build[$ex->buildID]['is_open']; - } - } - - if($status_ok) - { - // Get Test plan platforms - $getOpt = array('outputFormat' => 'mapAccessByID' , 'addIfNull' => false); - $platformSet = $this->tplanMgr->getPlatforms($ex->testPlanID,$getOpt); - - if( !($hasPlatforms = !is_null($platformSet)) && $ex->platformID !=0) - { - $status_ok = false; - $msg = 'You can not execute against a platform, because Test plan has no platforms'; - } - - if($status_ok) - { - if($hasPlatforms) - { - if($ex->platformID == 0) - { - $status_ok = false; - $msg = 'Test plan has platforms, you need to provide one in order to execute'; - } - else if (!isset($platformSet[$ex->platformID])) - { - $status_ok = false; - $msg = '(platform,test plan) couple is not valid'; - } - } - } - } - - if($status_ok) - { - // Test case check - $msg = 'Test case does not exist'; - - $tcaseID = $this->tcaseMgr->getInternalID($ex->testCaseExternalID); - $status_ok = ($tcaseID > 0); - if( $status_ok = ($tcaseID > 0) ) - { - $msg = 'Test case doesn not belong to right test project'; - $testCaseTestProject = $this->tcaseMgr->getTestProjectFromTestCase($tcaseID,0); - $status_ok = ($testCaseTestProject == $testPlan['testproject_id']); - } - if($status_ok) - { - // Does this test case is linked to test plan ? - $msg = 'Test case is not linked to (test plan,platform) => can not be executed'; - $getFilters = array('testplan_id' => $ex->testPlanID, 'platform_id' => $ex->platformID); - $getOpt = array('output' => 'simple'); - $links = $this->tcaseMgr->get_linked_versions($tcaseID,$getFilters,$getOpt); - $status_ok = !is_null($links); - } - } - if($status_ok) - { - // status code is OK ? - $msg = 'not run status is not a valid execution status (can not be written to DB)'; - $status_ok = ($ex->statusCode != 'n'); // Sorry for the MAGIC; - - if($status_ok) - { - $msg = 'Requested execution status is not configured on TestLink'; - $status_ok = isset($this->codeStatus[$ex->statusCode]); - } - } - - if($status_ok) - { - $ret = new stdClass(); - $ret->testProjectID = $testPlan['testproject_id']; - $ret->testCaseVersionID = key($links); - $ret->testCaseVersionNumber = $links[$ret->testCaseVersionID][$ex->testPlanID][$ex->platformID]['version']; - } - - if(!$status_ok) - { - throw new Exception($msg); - } - return $ret; - } - - - - /** - * 'name' - * 'testProjectID' - * 'notes' - * 'active' - * 'is_public' - * - */ - public function createTestPlan() - { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try - { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $op = array('status' => 'ok', 'message' => 'ok'); - $op['id'] = $this->tplanMgr->createFromObject($item,array('doChecks' => true)); - } - catch (Exception $e) - { - $op['message'] = $e->getMessage(); - } - echo json_encode($op); - } - - /** - * 'name' - * 'testProjectID' - * 'notes' - * 'active' - * 'is_public' - * - */ - public function updateTestPlan($id) - { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try - { - $op = array('status' => 'ok', 'message' => 'ok'); - - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $item->id = $id; - $op['id'] = $this->tplanMgr->updateFromObject($item); - } - catch (Exception $e) - { - $op['message'] = $e->getMessage(); - } - echo json_encode($op); - } - - - /** - * 'name' - * 'testProjectID' - * 'parentID' - * 'notes' - * 'order' - */ - public function createTestSuite() - { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try - { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $op = array('status' => 'ok', 'message' => 'ok'); - $op['id'] = $this->tsuiteMgr->createFromObject($item,array('doChecks' => true)); - } - catch (Exception $e) - { - $op['message'] = $e->getMessage(); - } - echo json_encode($op); - } - - /** - * "name" - * "testSuiteID" - * "testProjectID" - * "authorLogin" - * "authorID" - * "summary" - * "preconditions" - * "importance" - see const.inc.php for domain - * "executionType" - see ... for domain - * "order" - * - * "estimatedExecutionDuration" // to be implemented - */ - public function createTestCase() - { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try - { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $op = array('status' => 'ok', 'message' => 'ok'); - - // default management - $this->createTestCaseDefaultManagement($item); - - $this->checkRelatives($item->testProjectID,$item->testSuiteID); - - $ou = $this->tcaseMgr->createFromObject($item); - if( ($op['id']=$ou['id']) <= 0) - { - $op['status'] = 'ko'; - $op['message'] = $ou['msg']; - } - - } - catch (Exception $e) - { - $op['message'] = $e->getMessage(); - } - echo json_encode($op); - } - - /** - * - * - */ - private function getUserID($login) - { - - - } - - /** - * - * - */ - private function createTestCaseDefaultManagement(&$obj) - { - if(property_exists($obj, 'authorLogin')) - { - $obj->authorID = $this->getUserID($obj->authorLogin); - if( $obj->authorID <= 0 ) - { - // will use user that do the call ? - } - } - - if(!property_exists($obj, 'steps')) - { - $obj->steps = null; - } - } - - - /** - * - * - */ - private function checkRelatives($testProjectID,$testSuiteID) - { - if($testProjectID <= 0) - { - throw new Exception("Test Project ID is invalid (<=0)"); - } - - if($testSuiteID <= 0) - { - throw new Exception("Test Suite ID is invalid (<=0)"); - } - - $pinfo = $this->tprojectMgr->get_by_id($testProjectID); - if( is_null($pinfo) ) - { - throw new Exception("Test Project ID is invalid (does not exist)"); - } - - $pinfo = $this->tsuiteMgr->get_by_id($testSuiteID); - if( is_null($pinfo) ) - { - throw new Exception("Test Suite ID is invalid (does not exist)"); - } - - - if( $testProjectID != $this->tsuiteMgr->getTestProjectFromTestSuite($testSuiteID,$testSuiteID) ) - { - throw new Exception("Test Suite does not belong to Test Project ID"); - } - } - - - + + * @package TestLink + * @since 1.9.7 + * + * References + * http://ericbrandel.com/2013/01/14/quickly-build-restful-apis-in-php-with-slim-part-2/ + * https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+Add+Comment + * http://confluence.jetbrains.com/display/YTD4/Create+New+Work+Item + * http://www.redmine.org/projects/redmine/wiki/Rest_api + * http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/ + * https://github.com/educoder/pest/blob/master/examples/intouch_example.php + * http://stackoverflow.com/questions/9772933/rest-api-request-body-as-json-or-plain-post-data + * + * http://phptrycatch.blogspot.it/ + * http://nitschinger.at/A-primer-on-PHP-exceptions + * + + * + * + * @internal revisions + * @since 1.9.11 + * + */ +require_once '../../../../config.inc.php'; +require_once 'common.php'; +require_once 'Slim/Slim.php'; +\Slim\Slim::registerAutoloader(); + +/** + * + * @author Francisco Mancardi + * @package TestLink + */ +class tlRestApi +{ + + public static $version = "1.0"; + + /** + * The DB object used throughout the class + * + * @access protected + */ + protected $db = null; + + protected $tables = null; + + protected $tcaseMgr = null; + + protected $tprojectMgr = null; + + protected $tsuiteMgr = null; + + protected $tplanMgr = null; + + protected $tplanMetricsMgr = null; + + protected $reqSpecMgr = null; + + protected $reqMgr = null; + + protected $platformMgr = null; + + /** + * userID associated with the apiKey provided + */ + protected $userID = null; + + /** + * UserObject associated with the userID + */ + protected $user = null; + + /** + * array where all the args are stored for requests + */ + protected $args = null; + + /** + * array where error codes and messages are stored + */ + protected $errors = array(); + + /** + * The api key being used to make a request + */ + protected $apiKey = null; + + /** + * boolean to allow a method to invoke another method and avoid double auth + */ + protected $authenticated = false; + + /** + * The version of a test case that is being used + */ + /** + * This value is setted in following method: + */ + /** + * _checkTCIDAndTPIDValid() + */ + protected $tcVersionID = null; + + protected $versionNumber = null; + + public $statusCode; + + public $codeStatus; + + /** + */ + public function __construct() + { + // We are following Slim naming convention + $this->app = new \Slim\Slim(); + $this->app->contentType('application/json'); + + // test route with anonymous function + $this->app->get('/who', + function (): void { + echo __CLASS__ . ' : Get Route /who'; + }); + + $this->app->get('/whoAmI', array( + $this, + 'authenticate' + ), array( + $this, + 'whoAmI' + )); + $this->app->get('/testprojects', array( + $this, + 'authenticate' + ), array( + $this, + 'getProjects' + )); + + $this->app->get('/testprojects/:id', array( + $this, + 'authenticate' + ), array( + $this, + 'getProjects' + )); + $this->app->get('/testprojects/:id/testcases', + array( + $this, + 'authenticate' + ), array( + $this, + 'getProjectTestCases' + )); + $this->app->get('/testprojects/:id/testplans', + array( + $this, + 'authenticate' + ), array( + $this, + 'getProjectTestPlans' + )); + + $this->app->post('/testprojects', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestProject' + )); + $this->app->post('/executions', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestCaseExecution' + )); + $this->app->post('/testplans', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestPlan' + )); + $this->app->post('/testplans/:id', array( + $this, + 'authenticate' + ), array( + $this, + 'updateTestPlan' + )); + + $this->app->post('/testsuites', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestSuite' + )); + $this->app->post('/testcases', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestCase' + )); + + $this->db = new database(DB_TYPE); + $this->db->db->SetFetchMode(ADODB_FETCH_ASSOC); + doDBConnect($this->db, database::ONERROREXIT); + + $this->tcaseMgr = new testcase($this->db); + $this->tprojectMgr = new testproject($this->db); + $this->tsuiteMgr = new testsuite($this->db); + + $this->tplanMgr = new testplan($this->db); + $this->tplanMetricsMgr = new tlTestPlanMetrics($this->db); + $this->reqSpecMgr = new requirement_spec_mgr($this->db); + $this->reqMgr = new requirement_mgr($this->db); + $this->cfieldMgr = $this->tprojectMgr->cfield_mgr; + + $this->tables = $this->tcaseMgr->getDBTables(); + + $resultsCfg = config_get('results'); + foreach ($resultsCfg['status_label_for_exec_ui'] as $key => $label) { + $this->statusCode[$key] = $resultsCfg['status_code'][$key]; + } + + if (isset($this->statusCode['not_run'])) { + unset($this->statusCode['not_run']); + } + $this->codeStatus = array_flip($this->statusCode); + } + + /** + */ + public function authenticate(\Slim\Route $route) + { + $apiKey = null; + if (is_null($apiKey)) { + $request = $this->app->request(); + $apiKey = $request->headers('PHP_AUTH_USER'); + } + + $sql = "SELECT id FROM {$this->tables['users']} " . "WHERE script_key='" . + $this->db->prepare_string($apiKey) . "'"; + + $this->userID = $this->db->fetchFirstRowSingleColumn($sql, "id"); + if ($ok = ! is_null($this->userID)) { + $this->user = tlUser::getByID($this->db, $this->userID); + } else { + $this->app->status(400); + echo json_encode( + array( + 'status' => 'ko', + 'message' => 'authentication error' + )); + $this->app->stop(); + } + + return $ok; + } + + /** + */ + public function whoAmI() + { + echo json_encode(array( + 'name' => __CLASS__ . ' : Get Route /whoAmI' + )); + } + + /** + * + * @param + * mixed idCard if provided identifies test project + * if intval() > 0 => is considered DBID + * else => is used as PROJECT NAME + */ + public function getProjects($idCard = null, $opt = null) + { + $options = array_merge(array( + 'output' => 'rest' + ), (array) $opt); + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'item' => null + ); + if (is_null($idCard)) { + $opOptions = array( + 'output' => 'array_of_map', + 'order_by' => " ORDER BY name ", + 'add_issuetracker' => true, + 'add_reqmgrsystem' => true + ); + $op['item'] = $this->tprojectMgr->get_accessible_for_user( + $this->userID, $opOptions); + } else { + $opOptions = array( + 'output' => 'map', + 'field_set' => 'id', + 'format' => 'simple' + ); + $zx = $this->tprojectMgr->get_accessible_for_user($this->userID, + $opOptions); + if (($safeID = intval($idCard)) > 0) { + if (isset($zx[$safeID])) { + $op['item'] = $this->tprojectMgr->get_by_id($safeID); + } + } else { + // Will consider id = name + foreach ($zx as $value) { + if (strcmp($value['name'], $idCard) == 0) { + $safeString = $this->db->prepare_string($idCard); + $op['item'] = $this->tprojectMgr->get_by_name( + $safeString); + break; + } + } + } + } + + // Developer (silly?) information + // json_encode() transforms maps in objects. + switch ($options['output']) { + case 'internal': + return $op['item']; + break; + + case 'rest': + default: + echo json_encode($op); + break; + } + } + + /** + * + * @param + * mixed idCard if provided identifies test project + * if intval() > 0 => is considered DBID + * else => is used as PROJECT NAME + */ + public function getProjectTestPlans($idCard) + { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ); + $tproject = $this->getProjects($idCard, array( + 'output' => 'internal' + )); + + if (! is_null($tproject)) { + $items = $this->tprojectMgr->get_all_testplans($tproject[0]['id']); + $op['items'] = (! empty($items)) ? $items : null; + } else { + $op['message'] = "No Test Project identified by '" . $idCard . "'!"; + $op['status'] = 'error'; + } + + echo json_encode($op); + } + + /** + * Will return LATEST VERSION of each test case. + * Does return test step info ? + * + * @param + * mixed idCard if provided identifies test project + * if intval() > 0 => is considered DBID + * else => is used as PROJECT NAME + */ + public function getProjectTestCases($idCard) + { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ); + $tproject = $this->getProjects($idCard, array( + 'output' => 'internal' + )); + + if (! is_null($tproject)) { + $tcaseIDSet = array(); + $this->tprojectMgr->get_all_testcases_id($tproject[0]['id'], + $tcaseIDSet); + if (! empty($tcaseIDSet)) { + $op['items'] = array(); + foreach ($tcaseIDSet as $tcaseID) { + $item = $this->tcaseMgr->getLastVersionInfo($tcaseID); + $item['keywords'] = $this->tcaseMgr->get_keywords_map( + $tcaseID); + $item['customfields'] = $this->tcaseMgr->get_linked_cfields_at_design( + $tcaseID, $item['tcversion_id'], null, null, + $tproject[0]['id']); + $op['items'][] = $item; + } + } + } else { + $op['message'] = "No Test Project identified by '" . $idCard . "'!"; + $op['status'] = 'error'; + } + + echo json_encode($op); + } + + /** + * $item->name + * $item->prefix + * $item->notes + * $item->active + * $item->public + * $item->options + * $item->options->requirementsEnabled + * $item->options->testPriorityEnabled + * $item->options->automationEnabled + * $item->options->inventoryEnabled + */ + public function createTestProject() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $op['id'] = $this->tprojectMgr->create($item, + array( + 'doChecks' => true + )); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + } catch (Exception $e) { + $op['message'] = $e->getMessage(); + } + echo json_encode($op); + } + + /** + * Request Body + * + * $ex->testPlanID + * $ex->buildID + * $ex->platformID + * $ex->testCaseExternalID + * $ex->notes + * $ex->statusCode + * + * + * Checks to be done + * + * A. User right & Test plan existence + * user has right to execute on target Test plan? + * this means also that: Test plan ID exists ? + * + * B. Build + * does Build ID exist on target Test plan ? + * is Build enable to execution ? + * + * C. Platform + * do we need a platform ID in order to execute ? + * is a platform present on provided data ? + * does this platform belong to target Test plan ? + * + * D. Test case identity + * is target Test case part of Test plan ? + * + * + * Z. Other mandatory information + * We are not going to check for other mandatory info + * like: mandatory custom fields. (if we will be able in future to manage it) + */ + public function createTestCaseExecution() + { + $op = array( + 'status' => ' ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $ex = json_decode($request->getBody()); + $util = $this->checkExecutionEnvironment($ex); + + // If we are here this means we can write execution status!!! + $ex->testerID = $this->userID; + foreach ($util as $prop => $value) { + $ex->$prop = $value; + } + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $op['id'] = $this->tplanMgr->writeExecution($ex); + } catch (Exception $e) { + $op['message'] = $e->getMessage(); + } + echo json_encode($op); + } + + // + // Support methods + // + private function checkExecutionEnvironment($ex) + { + // Test plan ID exists and is ACTIVE + $msg = 'invalid Test plan ID'; + $getOpt = array( + 'output' => 'testPlanFields', + 'active' => 1, + 'testPlanFields' => 'id,testproject_id,is_public' + ); + $status_ok = ! is_null( + $testPlan = $this->tplanMgr->get_by_id($ex->testPlanID, $getOpt)); + + if ($status_ok) { + // user has right to execute on Test plan ID + // hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) + $msg = 'user has no right to execute'; + $status_ok = $this->user->hasRight($this->db, 'testplan_execute', + $testPlan['testproject_id'], $ex->testPlanID, true); + } + + if ($status_ok) { + // Check if couple (buildID,testPlanID) is valid + $msg = '(buildID,testPlanID) couple is not valid'; + $getOpt = array( + 'fields' => 'id,active,is_open', + 'buildID' => $ex->buildID, + 'orderBy' => null + ); + $status_ok = ! is_null( + $build = $this->tplanMgr->get_builds($ex->testPlanID, null, null, + $getOpt)); + + if ($status_ok) { + // now check is execution can be done againts this build + $msg = 'Build is not active and/or closed => execution can not be done'; + $status_ok = $build[$ex->buildID]['active'] && + $build[$ex->buildID]['is_open']; + } + } + + if ($status_ok) { + // Get Test plan platforms + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'addIfNull' => false + ); + $platformSet = $this->tplanMgr->getPlatforms($ex->testPlanID, + $getOpt); + + if (! ($hasPlatforms = ! is_null($platformSet)) && + $ex->platformID != 0) { + $status_ok = false; + $msg = 'You can not execute against a platform, because Test plan has no platforms'; + } + + if ($status_ok && $hasPlatforms) { + if ($ex->platformID == 0) { + $status_ok = false; + $msg = 'Test plan has platforms, you need to provide one in order to execute'; + } elseif (! isset($platformSet[$ex->platformID])) { + $status_ok = false; + $msg = '(platform,test plan) couple is not valid'; + } + } + } + + if ($status_ok) { + // Test case check + $msg = 'Test case does not exist'; + + $tcaseID = $this->tcaseMgr->getInternalID($ex->testCaseExternalID); + if ($status_ok = ($tcaseID > 0)) { + $msg = 'Test case doesn not belong to right test project'; + $testCaseTestProject = $this->tcaseMgr->getTestProjectFromTestCase( + $tcaseID, 0); + $status_ok = ($testCaseTestProject == $testPlan['testproject_id']); + } + if ($status_ok) { + // Does this test case is linked to test plan ? + $msg = 'Test case is not linked to (test plan,platform) => can not be executed'; + $getFilters = array( + 'testplan_id' => $ex->testPlanID, + 'platform_id' => $ex->platformID + ); + $getOpt = array( + 'output' => 'simple' + ); + $links = $this->tcaseMgr->get_linked_versions($tcaseID, + $getFilters, $getOpt); + $status_ok = ! is_null($links); + } + } + if ($status_ok) { + // status code is OK ? + $msg = 'not run status is not a valid execution status (can not be written to DB)'; + $status_ok = ($ex->statusCode != 'n'); // Sorry for the MAGIC; + + if ($status_ok) { + $msg = 'Requested execution status is not configured on TestLink'; + $status_ok = isset($this->codeStatus[$ex->statusCode]); + } + } + + if ($status_ok) { + $ret = new stdClass(); + $ret->testProjectID = $testPlan['testproject_id']; + $ret->testCaseVersionID = key($links); + $ret->testCaseVersionNumber = $links[$ret->testCaseVersionID][$ex->testPlanID][$ex->platformID]['version']; + } + + if (! $status_ok) { + throw new Exception($msg); + } + return $ret; + } + + /** + * 'name' + * 'testProjectID' + * 'notes' + * 'active' + * 'is_public' + */ + public function createTestPlan() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $op['id'] = $this->tplanMgr->createFromObject($item, + array( + 'doChecks' => true + )); + } catch (Exception $e) { + $op['message'] = $e->getMessage(); + } + echo json_encode($op); + } + + /** + * 'name' + * 'testProjectID' + * 'notes' + * 'active' + * 'is_public' + */ + public function updateTestPlan($id) + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $item->id = $id; + $op['id'] = $this->tplanMgr->updateFromObject($item); + } catch (Exception $e) { + $op['message'] = $e->getMessage(); + } + echo json_encode($op); + } + + /** + * 'name' + * 'testProjectID' + * 'parentID' + * 'notes' + * 'order' + */ + public function createTestSuite() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $op['id'] = $this->tsuiteMgr->createFromObject($item, + array( + 'doChecks' => true + )); + } catch (Exception $e) { + $op['message'] = $e->getMessage(); + } + echo json_encode($op); + } + + /** + * "name" + * "testSuiteID" + * "testProjectID" + * "authorLogin" + * "authorID" + * "summary" + * "preconditions" + * "importance" - see const.inc.php for domain + * "executionType" - see ... for domain + * "order" + * + * "estimatedExecutionDuration" // to be implemented + */ + public function createTestCase() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + + // default management + $this->createTestCaseDefaultManagement($item); + + $this->checkRelatives($item->testProjectID, $item->testSuiteID); + + $ou = $this->tcaseMgr->createFromObject($item); + if (($op['id'] = $ou['id']) <= 0) { + $op['status'] = 'ko'; + $op['message'] = $ou['msg']; + } + } catch (Exception $e) { + $op['message'] = $e->getMessage(); + } + echo json_encode($op); + } + + /** + */ + private function getUserID($login) + {} + + /** + */ + private function createTestCaseDefaultManagement(&$obj) + { + if (property_exists($obj, 'authorLogin')) { + $obj->authorID = $this->getUserID($obj->authorLogin); + if ($obj->authorID <= 0) { + // will use user that do the call ? + } + } + + if (! property_exists($obj, 'steps')) { + $obj->steps = null; + } + } + + /** + */ + private function checkRelatives($testProjectID, $testSuiteID) + { + if ($testProjectID <= 0) { + throw new Exception("Test Project ID is invalid (<=0)"); + } + + if ($testSuiteID <= 0) { + throw new Exception("Test Suite ID is invalid (<=0)"); + } + + $pinfo = $this->tprojectMgr->get_by_id($testProjectID); + if (is_null($pinfo)) { + throw new Exception("Test Project ID is invalid (does not exist)"); + } + + $pinfo = $this->tsuiteMgr->get_by_id($testSuiteID); + if (is_null($pinfo)) { + throw new Exception("Test Suite ID is invalid (does not exist)"); + } + + if ($testProjectID != + $this->tsuiteMgr->getTestProjectFromTestSuite($testSuiteID, + $testSuiteID)) { + throw new Exception("Test Suite does not belong to Test Project ID"); + } + } } // class end diff --git a/lib/api/rest/v2/index.php b/lib/api/rest/v2/index.php index 11c56ff419..5d56a2b073 100644 --- a/lib/api/rest/v2/index.php +++ b/lib/api/rest/v2/index.php @@ -1,20 +1,19 @@ -app->run(); diff --git a/lib/api/rest/v2/tlRestApi.class.php b/lib/api/rest/v2/tlRestApi.class.php index dbd3af99a7..35940cecfe 100644 --- a/lib/api/rest/v2/tlRestApi.class.php +++ b/lib/api/rest/v2/tlRestApi.class.php @@ -1,1474 +1,1850 @@ - - * @package TestLink - * @since 1.9.7 - * - * Implemented using Slim framework Version 2.2.0 - * - * - * References - * http://ericbrandel.com/2013/01/14/quickly-build-restful-apis-in-php-with-slim-part-2/ - * https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+Add+Comment - * http://confluence.jetbrains.com/display/YTD4/Create+New+Work+Item - * http://www.redmine.org/projects/redmine/wiki/Rest_api - * http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/ - * https://github.com/educoder/pest/blob/master/examples/intouch_example.php - * http://stackoverflow.com/questions/9772933/rest-api-request-body-as-json-or-plain-post-data - * - * http://phptrycatch.blogspot.it/ - * http://nitschinger.at/A-primer-on-PHP-exceptions - * - * - * - */ - -require_once('../../../../config.inc.php'); -require_once('common.php'); -require 'Slim/Slim.php'; -\Slim\Slim::registerAutoloader(); - -/** - * @author Francisco Mancardi - * @package TestLink - */ -class tlRestApi -{ - public static $version = "2.1"; - - - /** - * The DB object used throughout the class - * - * @access protected - */ - protected $db = null; - protected $tables = null; - - protected $tcaseMgr = null; - protected $tprojectMgr = null; - protected $tsuiteMgr = null; - protected $tplanMgr = null; - protected $tplanMetricsMgr = null; - protected $reqSpecMgr = null; - protected $reqMgr = null; - protected $platformMgr = null; - protected $buildMgr = null; - protected $cfieldMgr = null; - - - /** userID associated with the apiKey provided */ - protected $userID = null; - - /** UserObject associated with the userID */ - protected $user = null; - - /** array where all the args are stored for requests */ - protected $args = null; - - /** array where error codes and messages are stored */ - protected $errors = array(); - - /** The api key being used to make a request */ - protected $apiKey = null; - - /** boolean to allow a method to invoke another method and avoid double auth */ - protected $authenticated = false; - - /** The version of a test case that is being used */ - /** This value is setted in following method: */ - protected $tcVersionID = null; - protected $versionNumber = null; - protected $debugMsg; - - protected $cfg; - - protected $apiLogPathName; - - protected $l10n; - - - - /** - */ - public function __construct() { - - // We are following Slim naming convention - $this->app = new \Slim\Slim(); - $this->app->contentType('application/json'); - - $tl = array('API_MISSING_REQUIRED_PROP' => null, - 'API_TESTPLAN_ID_DOES_NOT_EXIST' => null, - 'API_TESTPLAN_APIKEY_DOES_NOT_EXIST' => null, - 'API_BUILDNAME_ALREADY_EXISTS' => null, - 'API_INVALID_BUILDID' => null); - - $this->l10n = init_labels($tl); - - // GET Routes - // test route with anonymous function - $this->app->get('/who', function () { echo __CLASS__ . ' : You have called the Get Route /who';}); - - // using middleware for authentication - // https://docs.slimframework.com/routing/middleware/ - // - $this->app->get('/whoAmI', array($this,'authenticate'), array($this,'whoAmI')); - - $this->app->get('/superman', array($this,'authenticate'), array($this,'superman')); - - $this->app->get('/testprojects', array($this,'authenticate'), array($this,'getProjects')); - - $this->app->get('/testprojects/:id', array($this,'authenticate'), array($this,'getProjects')); - $this->app->get('/testprojects/:id/testcases', array($this,'authenticate'), array($this,'getProjectTestCases')); - $this->app->get('/testprojects/:id/testplans', array($this,'authenticate'), array($this,'getProjectTestPlans')); - - $this->app->get('/testplans/:id/builds', array($this,'authenticate'), array($this,'getPlanBuilds')); - - // POST Routes - $this->app->post('/builds', array($this,'authenticate'), array($this,'createBuild')); - - - $this->app->post('/testprojects', array($this,'authenticate'), array($this,'createTestProject')); - - $this->app->post('/executions', array($this,'authenticate'), array($this,'createTestCaseExecution')); - - $this->app->post('/testplans', array($this,'authenticate'), array($this,'createTestPlan')); - - $this->app->post('/testplans/:id', array($this,'authenticate'), array($this,'updateTestPlan')); - - - $this->app->post('/testplans/:id/platforms', array($this,'authenticate'), array($this,'addPlatformsToTestPlan')); - - $this->app->post('/testsuites', array($this,'authenticate'), array($this,'createTestSuite')); - - $this->app->post('/testcases', array($this,'authenticate'), array($this,'createTestCase')); - - $this->app->post('/keywords', array($this,'authenticate'), array($this,'createKeyword')); - - - // update routes - $this->app->post('/builds/:id', array($this,'authenticate'), array($this,'updateBuild')); - - - // $this->app->get('/testplans/:id', array($this,'getTestPlan')); - $this->apiLogPathName = '/var/testlink/rest-api.log'; - - $this->db = new database(DB_TYPE); - $this->db->db->SetFetchMode(ADODB_FETCH_ASSOC); - doDBConnect($this->db,database::ONERROREXIT); - - - $this->tcaseMgr = new testcase($this->db); - $this->tprojectMgr = new testproject($this->db); - $this->tsuiteMgr = new testsuite($this->db); - - $this->tplanMgr = new testplan($this->db); - $this->tplanMetricsMgr = new tlTestPlanMetrics($this->db); - $this->reqSpecMgr = new requirement_spec_mgr($this->db); - $this->reqMgr = new requirement_mgr($this->db); - $this->cfieldMgr = $this->tprojectMgr->cfield_mgr; - $this->buildMgr = new build_mgr($this->db); - - $this->tables = $this->tcaseMgr->getDBTables(); - - - $this->cfg = array(); - $conf = config_get('results'); - foreach($conf['status_label_for_exec_ui'] as $key => $label ) { - $this->cfg['exec']['statusCode'][$key] = $conf['status_code'][$key]; - } - - $this->cfg['exec']['codeStatus'] = array_flip($this->cfg['exec']['statusCode']); - - $this->cfg['tcase']['defaults']['importance'] = config_get('testcase_importance_default'); - - - $this->cfg['tcase']['status'] = config_get('testCaseStatus'); - - $this->cfg['execType'] = config_get('execution_type'); - - $this->cfg['tcase']['defaults']['executionType'] = - $this->cfg['execType']['manual']; - - $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: '; - } - - - /** - * - */ - function authenticate(\Slim\Route $route) { - $apiKey = null; - if(is_null($apiKey)) { - $request = $this->app->request(); - $hh = $request->headers(); - - if( isset($hh['APIKEY']) ) { - $apiKey = $hh['APIKEY']; - } else { - $apiKey = $hh['PHP_AUTH_USER']; - } - } - $sql = "SELECT id FROM {$this->tables['users']} " . - "WHERE script_key='" . $this->db->prepare_string($apiKey) . "'"; - - $this->userID = $this->db->fetchFirstRowSingleColumn($sql, "id"); - if( ($ok=!is_null($this->userID)) ) { - $this->user = tlUser::getByID($this->db,$this->userID); - } else { - $this->app->status(400); - echo json_encode(array('status' => 'ko', 'message' => 'authentication error')); - $this->app->stop(); - } - - return $ok; - } - - - - /** - * - */ - public function whoAmI() { - echo json_encode(array('name' => __CLASS__ . ' : You have called Get Route /whoAmI')); - } - - /** - * - */ - public function superman() { - echo json_encode( - array('name' => __CLASS__ . ' : You have called the Get Route /superman')); - } - - - /** - * - * @param mixed idCard if provided identifies test project - * if intval() > 0 => is considered DBID - * else => is used as PROJECT NAME - */ - public function getProjects($idCard=null, $opt=null) { - $options = array_merge(array('output' => 'rest'), (array)$opt); - $op = array('status' => 'ok', 'message' => 'ok', 'item' => null); - if(is_null($idCard)) { - $opOptions = array('output' => 'array_of_map', 'order_by' => " ORDER BY name ", 'add_issuetracker' => true, - 'add_reqmgrsystem' => true); - $op['item'] = $this->tprojectMgr->get_accessible_for_user($this->userID,$opOptions); - } else { - $opOptions = array('output' => 'map', - 'field_set' => 'prefix', - 'format' => 'simple'); - $zx = $this->tprojectMgr->get_accessible_for_user($this->userID,$opOptions); - - $targetID = null; - if( ($safeID = intval($idCard)) > 0) { - if( isset($zx[$safeID]) ) { - $targetID = $safeID; - } - } - else { - // Will consider id = name or prefix - foreach( $zx as $itemID => $value ) { - if( strcmp($value['name'],$idCard) == 0 || - strcmp($value['prefix'],$idCard) == 0 ) { - $targetID = $itemID; - break; - } - } - } - - if( null != $targetID ) { - $op['item'] = $this->tprojectMgr->get_by_id($targetID); - } - } - - // Developer (silly?) information - // json_encode() transforms maps in objects. - switch($options['output']) { - case 'internal': - return $op['item']; - break; - - case 'rest': - default: - echo json_encode($op); - break; - } - } - - /** - * - * @param mixed idCard if provided identifies test project - * if intval() > 0 => is considered DBID - * else => is used as PROJECT NAME Or Prefix - */ - public function getProjectTestPlans($idCard) { - $op = array('status' => 'ok', 'message' => 'ok', 'items' => null); - $tproject = $this->getProjects($idCard, array('output' => 'internal')); - - if( !is_null($tproject) ) { - $items = $this->tprojectMgr->get_all_testplans($tproject['id']); - $op['items'] = (!is_null($items) && count($items) > 0) ? $items : null; - } else { - $op['message'] = "No Test Project identified by '" . $idCard . "'!"; - $op['status'] = 'error'; - $this->app->status(500); - } - - echo json_encode($op); - } - - /** - * Will return LATEST VERSION of each test case. - * Does return test step info ? - * - * @param mixed idCard if provided identifies test project - * if intval() > 0 => is considered DBID - * else => is used as PROJECT NAME - */ - public function getProjectTestCases($idCard) { - $op = array('status' => 'ok', 'message' => 'ok', 'items' => null); - $tproject = $this->getProjects($idCard, array('output' => 'internal')); - - if( !is_null($tproject) ) { - $tcaseIDSet = array(); - $this->tprojectMgr->get_all_testcases_id($tproject['id'],$tcaseIDSet); - - if( !is_null($tcaseIDSet) && count($tcaseIDSet) > 0 ) { - $op['items'] = array(); - foreach( $tcaseIDSet as $key => $tcaseID ) { - $item = $this->tcaseMgr->get_last_version_info($tcaseID); - $item['keywords'] = - $this->tcaseMgr->get_keywords_map($tcaseID,$item['tcversion_id']); - $item['customfields'] = - $this->tcaseMgr->get_linked_cfields_at_design($tcaseID,$item['tcversion_id'],null,null,$tproject['id']); - $op['items'][] = $item; - } - } - } else { - $op['message'] = "No Test Project identified by '" . $idCard . "'!"; - $op['status'] = 'error'; - } - - echo json_encode($op); - } - - /** - * - * $item->name - * $item->prefix - * $item->notes - * $item->active - * $item->public - * $item->options - * $item->options->requirementsEnabled - * $item->options->testPriorityEnabled - * $item->options->automationEnabled - * $item->options->inventoryEnabled - */ - public function createTestProject() { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - - try { - // Check user grants for requested operation - // This is a global right - $rightToCheck="mgt_modify_product"; - if( $this->userHasRight($rightToCheck) ) { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $op['id'] = $this->tprojectMgr->create($item,array('doChecks' => true)); - $op = array('status' => 'ok', 'message' => 'ok'); - } else { - $this->app->status(403); - $msg = lang_get('API_INSUFFICIENT_RIGHTS'); - $op['message'] = sprintf($msg,$rightToCheck,0,0); - } - } - catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - - - /** - * - * Request Body - * - * $ex->testPlanID - * $ex->buildID - * $ex->platformID -> optional - * $ex->testCaseExternalID - * $ex->notes - * $ex->statusCode - * - * - * Checks to be done - * - * A. User right & Test plan existence - * user has right to execute on target Test plan? - * this means also that: Test plan ID exists ? - * - * B. Build - * does Build ID exist on target Test plan ? - * is Build enable to execution ? - * - * C. Platform - * do we need a platform ID in order to execute ? - * is a platform present on provided data ? - * does this platform belong to target Test plan ? - * - * D. Test case identity - * is target Test case part of Test plan ? - * - * - * Z. Other mandatory information - * We are not going to check for other mandatory info - * like: mandatory custom fields. (if we will be able in future to manage it) - * - * - */ - public function createTestCaseExecution() { - $op = array('status' => ' ko', 'message' => 'ko', 'id' => -1); - try { - $request = $this->app->request(); - $ex = json_decode($request->getBody()); - $util = $this->checkExecutionEnvironment($ex); - - // Complete missing propertie - if( property_exists($ex, 'platformID') == FALSE ) { - $ex->platformID = 0; - } - - if( property_exists($ex, 'executionType') == FALSE ) { - $ex->executionType = $this->cfg['execType']['auto']; - } - - // If we are here this means we can write execution status!!! - $ex->testerID = $this->userID; - foreach($util as $prop => $value) { - $ex->$prop = $value; - } - $op = array('status' => 'ok', 'message' => 'ok'); - $op['id'] = $this->tplanMgr->writeExecution($ex); - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - - - // - // Support methods - // - private function checkExecutionEnvironment($ex) { - // throw new Exception($message, $code, $previous); - - // no platform - $platform = 0; - - // Test plan ID exists and is ACTIVE - $msg = 'invalid Test plan ID'; - $getOpt = array('output' => 'testPlanFields','active' => 1, - 'testPlanFields' => 'id,testproject_id,is_public'); - $status_ok = !is_null($testPlan=$this->tplanMgr->get_by_id($ex->testPlanID,$getOpt)); - - if($status_ok) { - // user has right to execute on Test plan ID - // hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) - $msg = 'user has no right to execute'; - $status_ok = $this->user->hasRight($this->db,'testplan_execute', - $testPlan['testproject_id'],$ex->testPlanID,true); - } - - if($status_ok) { - // Check if couple (buildID,testPlanID) is valid - $msg = '(buildID,testPlanID) couple is not valid'; - $getOpt = array('fields' => 'id,active,is_open', 'buildID' => $ex->buildID, 'orderBy' => null); - $status_ok = !is_null($build = $this->tplanMgr->get_builds($ex->testPlanID,null,null,$getOpt)); - - if($status_ok) { - // now check is execution can be done againts this build - $msg = 'Build is not active and/or closed => execution can not be done'; - $status_ok = $build[$ex->buildID]['active'] && $build[$ex->buildID]['is_open']; - } - } - - if($status_ok && property_exists($ex, 'platformID')) { - // Get Test plan platforms - $platform = $ex->platformID; - - $getOpt = array('outputFormat' => 'mapAccessByID' , 'addIfNull' => false); - $platformSet = $this->tplanMgr->getPlatforms($ex->testPlanID,$getOpt); - - if( !($hasPlatforms = !is_null($platformSet)) && $platform !=0) { - $status_ok = false; - $msg = 'You can not execute against a platform, because Test plan has no platforms'; - } - - if($status_ok) { - if($hasPlatforms) { - if($platform == 0) { - $status_ok = false; - $msg = 'Test plan has platforms, you need to provide one in order to execute'; - } else if (!isset($platformSet[$platform])) { - $status_ok = false; - $msg = '(platform,test plan) couple is not valid'; - } - } - } - } - - if($status_ok) { - // Test case check - $msg = 'Test case does not exist'; - - $tcaseID = $this->tcaseMgr->getInternalID($ex->testCaseExternalID); - $status_ok = ($tcaseID > 0); - if( $status_ok = ($tcaseID > 0) ) { - $msg = 'Test case doesn not belong to right test project'; - $testCaseTestProject = $this->tcaseMgr->getTestProjectFromTestCase($tcaseID,0); - $status_ok = ($testCaseTestProject == $testPlan['testproject_id']); - } - - if($status_ok) { - // Does this test case is linked to test plan ? - $msg = 'Test case is not linked to (test plan,platform) => can not be executed'; - $getFilters = array('testplan_id' => $ex->testPlanID, - 'platform_id' => $platform); - - $getOpt = array('output' => 'simple'); - $links = $this->tcaseMgr->get_linked_versions($tcaseID,$getFilters,$getOpt); - $status_ok = !is_null($links); - } - } - - if($status_ok) { - // status code is OK ? - $msg = 'not run status is not a valid execution status (can not be written to DB)'; - $status_ok = ($ex->statusCode != $this->cfg['exec']['statusCode']['not_run']); - - if($status_ok) { - $msg = 'Requested execution status is not configured on TestLink'; - $status_ok = isset($this->cfg['exec']['codeStatus'][$ex->statusCode]); - } - } - - if($status_ok) { - $ret = new stdClass(); - $ret->testProjectID = $testPlan['testproject_id']; - $ret->testCaseVersionID = key($links); - $ret->testCaseVersionNumber = - $links[$ret->testCaseVersionID][$ex->testPlanID][$platform]['version']; - } - - if(!$status_ok) { - throw new Exception($msg); - } - - return $ret; - } - - - - /** - * 'name' - * 'testProjectID' - * 'notes' - * 'active' - * 'is_public' - * - */ - public function createTestPlan() { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - - $op = array('status' => 'ok', 'message' => 'ok'); - $opeOpt = array('setSessionProject' => false, - 'doChecks' => true); - $op['id'] = $this->tplanMgr->createFromObject($item,$opeOpt); - - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - /** - * 'name' - * 'testProjectID' - * 'notes' - * 'active' - * 'is_public' - * - */ - public function updateTestPlan($id) { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try { - $op = array('status' => 'ok', 'message' => 'ok'); - - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $item->id = $id; - $op['id'] = $this->tplanMgr->updateFromObject($item); - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - - /** - * 'name' - * 'testProjectID' - * 'parentID' - * 'notes' - * 'order' - */ - public function createTestSuite() { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - $op = array('status' => 'ok', 'message' => 'ok'); - $op['id'] = $this->tsuiteMgr->createFromObject($item,array('doChecks' => true)); - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - /** - * "name" - * "testSuite": {"id": xxx} - * "testProject" : {"id": xxx} or {"prefix": yyy} - * "authorLogin" - * "authorID" - * "summary" - * "preconditions" - * "importance" - see const.inc.php for domain - * "executionType" - see ... for domain - * "order" - * - * "estimatedExecutionDuration" // to be implemented - */ - public function createTestCase() { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - if(is_null($item)) { - throw new Exception("Fatal Error " . __METHOD__ . " json_decode(requesBody) is NULL", 1); - } - - // create obj with standard properties - try { - $tcase = $this->buildTestCaseObj($item); - $this->checkRelatives($tcase->testProjectID,$tcase->testSuiteID); - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = 'After buildTestCaseObj() >> ' . - $e->getMessage(); - echo json_encode($op); - return; - } - - - $ou = $this->tcaseMgr->createFromObject($tcase); - $op = array('status' => 'ok', 'message' => 'ok', 'id' => -1); - if( ($op['id']=$ou['id']) <= 0) { - $op['status'] = 'ko'; - $op['message'] = $ou['msg']; - $this->app->status(409); - } - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - - /** - * - * @param mixed testplan - * - * step 1) testplan is a number ? - * use it as test plan id - * - * step 2) testplan is a string ? - * use it as test plan apikey - * - * Is not possible to consider testplan as name - * becase name can be used in several test projects. - * One option can be request testprojectname/testplanname - * - * @param string name: build name - * @param string [notes] - * @param string [active] - * @param string [open] - * @param string [releasedate]: format YYYY-MM-DD; - * @param int [copytestersfrombuild] - * - * step 1) is a number ? - * will be considered a target build id. - * check will be done to verify that is a valid - * build id inside the test plan. - * - * step 2) is a string ? - * will be used as build name to search inside - * the test plan. - * - * if check is OK, tester assignments will be copied. - * - */ - public function createBuild() { - - $op = array('status' => 'ko', 'message' => 'ko', - 'details' => array(), 'id' => -1); - - $rightToCheck = "testplan_create_build"; - - // need to get input, before doing right checks, - // because right can be tested against in this order - // Test Plan Right - // Test Project Right - // Default Right - $request = $this->app->request(); - $item = json_decode($request->getBody()); - if( null == $item ) { - $this->byeHTTP500(); - } - - $statusOK = true; - $build = new stdClass(); - - $reqProps = array('testplan','name'); - foreach( $reqProps as $prop ) { - if( !property_exists($item, $prop) ) { - $op['details'][] = $this->l10n['API_MISSING_REQUIRED_PROP'] . - $prop; - $statusOK = false; - } - } - - if( $statusOK ) { - $build->name = $item->name; - - if( is_numeric($item->testplan) ) { - // Check if is a valid test plan - // Get it's test project id - $tplan_id = intval($item->testplan); - $tplan = $this->tplanMgr->get_by_id( $tplan_id ); - - if( null == $tplan ) { - $statusOK = false; - $op['details'][] = - sprintf($this->l10n['API_TESTPLAN_ID_DOES_NOT_EXIST'],$item->testplan); - $this->app->status(404); - } - } else { - $tplanAPIKey = trim($item->testplan); - $tplan = $this->tplanMgr->getByAPIKey( $tplanAPIKey ); - if( null == $tplan ) { - $statusOK = false; - $op['details'][] = - sprintf($this->l10n['API_TESTPLAN_APIKEY_DOES_NOT_EXIST'],$item->testplan); - $this->app->status(404); - } - } - } - - if( $statusOK ) { - // Ready to check user permissions - $context = array('tplan_id' => $tplan['id'], - 'tproject_id' => $tplan['testproject_id']); - - if( !$this->userHasRight($rightToCheck,TRUE,$context) ) { - $statusOK = false; - $msg = lang_get('API_INSUFFICIENT_RIGHTS'); - $op['message'] = - sprintf($msg,$rightToCheck,$this->user->login, - $context['tproject_id'],$context['tplan_id']); - $this->app->status(403); - } - } - - // Go ahead, try create build!! - // Step 1 - Check if build name already exists - if( $statusOK ) { - $build->id = - $this->tplanMgr->get_build_id_by_name( $context['tplan_id'], $build->name ); - - if( $build->id > 0 ) { - $statusOK = false; - $op['message'] = - sprintf($this->l10n['API_BUILDNAME_ALREADY_EXISTS'], - $build->name, $build->id); - $this->app->status(409); - } - - $build->tplan_id = $context['tplan_id']; - } - - // Step 2 - Finally Create It!! - if( $statusOK ) { - // key 2 check with default value is parameter is missing - $k2check = array('is_open' => 1, - 'release_candidate' => null, - 'notes' => null, - 'commit_id' => null, 'tag' => null, - 'branch' => null, - 'is_active' => 1,'active' => 1, - 'releasedate' => null,'release_date' => null, - 'copy_testers_from_build' => null, - 'copytestersfrombuild' => null); - - $buildProp = array('tplan_id' => 'tplan_id', - 'release_date' => 'release_date', - 'releasedate' => 'release_date', - 'active' => 'is_active', - 'is_active' => 'is_active', - 'notes' => 'notes', - 'commit_id' => 'commit_id', - 'tag' => 'tag', 'branch' => 'branch', - 'release_candidate' =>'release_candidate', - 'is_open' => 'is_open', - 'copytestersfrombuild' => - 'copytestersfrombuild', - 'copy_testers_from_build' => - 'copytestersfrombuild'); - - $skipKey = array(); - foreach( $k2check as $key => $value ) { - $translate = $buildProp[$key]; - if( !isset($skipKey[$translate]) ) { - $build->$translate = $value; - if( property_exists($item, $key) ) { - $build->$translate = $item->$key; - $skipKey[$translate] = true; - } - } - } - - $itemID = $this->buildMgr->createFromObject($build); - if( $itemID > 0 ) { - $op = array('status' => 'ok', 'message' => 'ok', - 'details' => array(), 'id' => $itemID); - } - } - - echo json_encode($op); - } - - - /** - * - * - */ - private function getUserIDByAttr($user) { - $debugMsg = $this->debugMsg . __FUNCTION__; - $run = false; - $udi = -1; - - $sql = "/* $debugMsg */ SELECT id FROM {$this->tables['users']} "; - if(property_exists($user, 'login')) { - $run = true; - $sql .= " WHERE login='" . $this->db->prepare_string(trim($user->login)) . "'"; - } - - if($run==false && property_exists($user, 'id')) { - $run = true; - $sql .= " WHERE id=" . intval($user->id); - } - - if($run) { - $rs = $this->db->get_recordset($sql); - } - - return ($run && !is_null($rs)) ? $rs[0]['id'] : $uid; - } - - /** - * - * - */ - private function buildTestCaseObj(&$obj) { - if(is_null($obj)) { - throw new Exception("Fatal Error - " . __METHOD__ . " arg is NULL"); - } - - $tcase = new stdClass(); - $tcase->authorID = -1; - $tcase->steps = null; - $tcase->testProjectID = -1; - - $accessKey = array(); - $isOK = true; - - // Knowing author is critic, because rights are related to user. - // Another important thing: - // do we need to check that author when provided, has rights to do - // requested action? - // If we do not do this check, we will find in test cases created - // by people that do not have rights. - // May be is time to add a field that provide info about source of action - // GUI, API - // - if(property_exists($obj, 'author')) { - if(property_exists($obj->author, 'login') || property_exists($obj->author, 'id')) { - $tcase->authorID = $this->getUserIDByAttr($obj->author); - } - } - - // Last resort: get author from credentials use to make the call. - // no error message returned. - if($tcase->authorID <= 0) { - $tcase->authorID = $this->userID; - } - - - // Mandatory attributes - $ma = array('name' => null,'testProject' => array('id','prefix'), - 'testSuite' => array('id')); - - foreach ($ma as $key => $dummy) { - if( !($isOK = $isOK && property_exists($obj, $key)) ) { - throw new Exception("Missing Attribute: {$key} "); - } - } - - foreach ($ma as $key => $attr) { - if( !is_null($attr) ) { - $attrOK = false; - foreach($attr as $ak) { - $accessKey[$key][$ak] = property_exists($obj->$key,$ak); - $attrOK = $attrOK || $accessKey[$key][$ak]; - } - - if(!$attrOK) { - $msg = "Attribute: {$key} mandatory key ("; - if(count($attr) > 1) { - $msg .= "one of set: "; - } - $msg .= implode('/',$attr) . ") is missing"; - throw new Exception($msg); - } - } - } - - $tcase->name = trim($obj->name); - $tcase->testSuiteID = intval($obj->testSuite->id); - - $gOpt = array('output' => 'array_of_map', 'field_set' => 'prefix', - 'add_issuetracker' => false, 'add_reqmgrsystem' => false); - - - $msg = "Test project with "; - if($accessKey['testProject']['id']) { - $safeID = intval($obj->testProject->id); - $gFilters = array('id' => array('op' => '=', 'value' => $safeID)); - $msg .= "id={$safeID} "; - } - - if($accessKey['testProject']['prefix']) { - $gFilters = array('prefix' => - array('op' => '=', 'value' => trim($obj->testProject->prefix)) ); - $msg .= "prefix={$obj->testProject->prefix} "; - } - - $info = $this->tprojectMgr->get_accessible_for_user($this->userID,$gOpt,$gFilters); - - if(is_null($info)) { - $msg .= "does not exist or you have no rights to use it"; - throw new Exception($msg,999); - } - - $tcase->testProjectID = intval($info[0]['id']); - - $sk2d = array('summary' => '','preconditions' => '', - 'order' => 100, 'estimatedExecutionTime' => 0, - 'executionType' => - $this->cfg['tcase']['defaults']['executionType'], - 'importance' => - $this->cfg['tcase']['defaults']['importance'], - 'status' => $this->cfg['tcase']['status']['draft']); - foreach($sk2d as $key => $value) { - $tcase->$key = property_exists($obj, $key) ? $obj->$key : $value; - } - - if(property_exists($obj, 'steps')) { - $tcase->steps = $obj->steps; - } - - return $tcase; - } - - - /** - * - * - */ - private function checkRelatives($testProjectID,$testSuiteID) { - if($testProjectID <= 0) - { - throw new Exception("Test Project ID is invalid (<=0)"); - } - - if($testSuiteID <= 0) { - throw new Exception("Test Suite ID is invalid (<=0)"); - } - - $pinfo = $this->tprojectMgr->get_by_id($testProjectID); - if( is_null($pinfo) ) { - throw new Exception("Test Project ID is invalid (does not exist)"); - } - - $pinfo = $this->tsuiteMgr->get_by_id($testSuiteID); - if( is_null($pinfo) ) { - throw new Exception("Test Suite ID is invalid (does not exist)"); - } - - if( $testProjectID != $this->tsuiteMgr->getTestProjectFromTestSuite($testSuiteID,$testSuiteID) ) { - throw new Exception("Test Suite does not belong to Test Project ID"); - } - } - - - /** - * checks if a user has requested right on test project, test plan pair. - * - * @param string $rightToCheck one of the rights defined in rights table - * @param boolean $checkPublicPrivateAttr (optional) - * @param map $context (optional) - * keys tproject_id,tplan_id (both are also optional) - * - * @return boolean - * @access protected - * - * - */ - protected function userHasRight($rightToCheck,$checkPublicPrivateAttr=false, - $context=null) - { - $status_ok = true; - - // for global rights context is NULL - if( is_null($context) ) { - $tproject_id = 0; - $tplan_id = null; - } else { - $tproject_id = intval(isset($context['tproject_id']) ? - $context['tproject_id'] : 0); - - $tplan_id = null; - if(isset($context['tplan_id'])) { - $tplan_id = intval($context['tplan_id']); - } - - if( $tproject_id <= 0 && !is_null($tplan_id) ) { - // get test project from test plan - $dummy = $this->tplanMgr->get_by_id($tplanid,array('output' => 'minimun')); - $tproject_id = intval($dummy['tproject_id']); - } - } - - // echo $rightToCheck; - if(!$this->user->hasRight($this->db,$rightToCheck, - $tproject_id,$tplan_id,$checkPublicPrivateAttr)) { - $status_ok = false; - } - return $status_ok; - } - - /** - * "keyword" - * "notes" - * "testProject": {"prefix":"APR"} - */ - public function createKeyword() { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try { - $request = $this->app->request(); - $item = json_decode($request->getBody()); - if(is_null($item)) { - throw new Exception("Fatal Error " . __METHOD__ . " json_decode(requestBody) is NULL", 1); - } - - // create obj with standard properties - $pfx = $item->testProject->prefix; - $pid = $this->tprojectMgr->get_by_prefix((string)$pfx); - if( null == $pid ) { - $op['status'] = 'ko'; - $op['message'] = "Can't get test project ID"; - } else { - $pid = $pid['id']; - $ou = $this->tprojectMgr->addKeyword($pid,$item->keyword,$item->notes); - $op = array('status' => 'ok', 'message' => 'ok', 'id' => -1); - if( ($op['id'] = $ou['id']) <= 0) { - $op['status'] = 'ko'; - $op['message'] = $ou['msg']; - } - } - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - - /** - * - * @param mixed idCard identifies test plan via apikey - * - */ - public function getPlanBuilds($idCard) { - $op = array('status' => 'ok', 'message' => 'ok', 'items' => null); - $tplan = $this->tplanMgr->getByAPIKey($idCard); - - if( !is_null($tplan) ) { - $items = $this->tplanMgr->get_builds($tplan['id']); - $op['items'] = (!is_null($items) && count($items) > 0) ? $items : null; - } else { - $op['message'] = "No Test Plan identified by '" . $idCard . "'!"; - $op['status'] = 'error'; - } - - echo json_encode($op); - } - - - /** - * - * @param string id: build id - * @param string [notes] - * @param string [active] - * @param string [open] - * @param string [releasedate]: format YYYY-MM-DD; - * @param int [copytestersfrombuild] - * - * step 1) is a number ? - * will be considered a target build id. - * check will be done to verify that is a valid - * build id inside the test plan. - * - * step 2) is a string ? - * will be used as build name to search inside - * the test plan. - * - * if check is OK, tester assignments will be copied. - * - */ - public function updateBuild($id) { - - $op = array('status' => 'ko', 'message' => 'ko', - 'details' => array(), 'id' => -1); - - $rightToCheck = "testplan_create_build"; - - // need to get input, before doing right checks, - // because right can be tested against in this order - // Test Plan Right - // Test Project Right - // Default Right - $request = $this->app->request(); - $item = json_decode($request->getBody()); - if( null == $item ) { - $this->byeHTTP500(); - } - - - $statusOK = true; - if( intval($id) <= 0 ) { - $op['details'][] = $this->l10n['API_MISSING_REQUIRED_PROP'] . - 'id - the build ID'; - $statusOK = false; - } - - if( $statusOK ) { - $build = $this->buildMgr->get_by_id( $id ); - - if( null == $build ) { - $statusOK = false; - $op['message'] = - sprintf($this->l10n['API_INVALID_BUILDID'],$id); - $this->app->status(404); - } - } - - if( $statusOK ) { - $tplan = $this->tplanMgr->get_by_id( $build['testplan_id'] ); - - // Ready to check user permissions - $context = array('tplan_id' => $tplan['id'], - 'tproject_id' => $tplan['testproject_id']); - - if( !$this->userHasRight($rightToCheck,TRUE,$context) ) { - $statusOK = false; - $msg = lang_get('API_INSUFFICIENT_RIGHTS'); - $op['message'] = - sprintf($msg,$rightToCheck,$this->user->login, - $context['tproject_id'],$context['tplan_id']); - $this->app->status(403); - } - } - - // Go ahead, try to update build!! - if( $statusOK ) { - - // Step 1 - Check if build name already exists - if( property_exists($item,'name') ) { - if( $this->tplanMgr->check_build_name_existence( - $tplan['id'],$item->name,$id) ) { - $statusOK = false; - $op['message'] = - sprintf($this->l10n['API_BUILDNAME_ALREADY_EXISTS'], - $item->name, $id); - $this->app->status(409); - } - } - } - - // Step 2 - Finally Update It!! - if( $statusOK ) { - - $k2check = array('is_open', 'name', - 'release_candidate', - 'notes','commit_id','tag', - 'branch','is_active','active', - 'releasedate','release_date', - 'copy_testers_from_build', - 'copytestersfrombuild'); - - $buildProp = array('name' => 'name', - 'tplan_id' => 'tplan_id', - 'release_date' => 'release_date', - 'releasedate' => 'release_date', - 'active' => 'is_active', - 'is_active' => 'is_active', - 'notes' => 'notes', - 'commit_id' => 'commit_id', - 'tag' => 'tag', 'branch' => 'branch', - 'release_candidate' =>'release_candidate', - 'is_open' => 'is_open', - 'copytestersfrombuild' => - 'copytestersfrombuild', - 'copy_testers_from_build' => - 'copytestersfrombuild'); - - $skipKey = array(); - $buildObj = new stdClass(); - $attr = array(); - foreach( $k2check as $key ) { - $translate = $buildProp[$key]; - if( !isset($skipKey[$translate]) ) { - - // init with value got from DB. - if( isset($build[$translate]) ) { - $buildObj->$translate = $build[$translate]; - } - - if( property_exists($item, $key) ) { - $buildObj->$translate = $item->$key; - $skipKey[$translate] = true; - } - - if( property_exists($buildObj, $translate) ) { - $attr[$translate] = $buildObj->$translate; - } - } - } - - // key 2 check - // $id,$name,$notes,$active=null,$open=null, - // $release_date='',$closed_on_date='') { - - /* - $k2check = array('name','notes','active','is_open', - 'release_date'); - - foreach( $k2check as $key ) { - if( property_exists($item, $key) ) { - $$key = $item->$key; - } else { - $$key = $build[$key]; - } - } - - $ox = $this->buildMgr->update($id,$name, - $notes,$active,$is_open,$release_date); - */ - $ox = $this->buildMgr->update($build['id'], - $buildObj->name,$buildObj->notes,$attr); - - if( $ox ) { - $op = array('status' => 'ok', 'message' => 'ok', - 'details' => array(), 'id' => $id); - - // Special processing Build Closing/Opening - // we need also to manage close on date. - if( property_exists($item,'is_open') ) { - $oio = intval($build['is_open']); - $nio = intval($item->is_open); - if( $oio != $nio ) { - if( $nio ) { - $this->buildMgr->setOpen($id); - } else { - $this->buildMgr->setClosed($id); - } - } - } - - - } - } - - echo json_encode($op); - } - - /** - * body will contain an array of objects - * that can be - * {'name': platform name} - * {'id': platform id} - * - * Check if done to understand if all platforms - * exist before doing any action - * - * - */ - public function addPlatformsToTestPlan($tplan_id) { - $op = array('status' => 'ko', 'message' => 'ko', 'id' => -1); - try { - $request = $this->app->request(); - $plat2link = json_decode($request->getBody()); - - $op = array('status' => 'ok', 'message' => 'ok'); - $statusOK = true; - if (null == $plat2link || !is_array($plat2link)) { - $statusOK = false; - $op['status'] = 'ko'; - $op['message'] = 'Bad Body'; - } - - if ($statusOK) { - // Validate Test plan existence. - // Get Test Project ID before doing anything - $getOpt = array('output' => 'testPlanFields','active' => 1, - 'testPlanFields' => 'id,testproject_id,is_public'); - - $testPlan = $this->tplanMgr->get_by_id($tplan_id,$getOpt); - $statusOK = !is_null($testPlan); - - if ($statusOK) { - $tproject_id = $testPlan['testproject_id']; - } else { - $op['status'] = 'ko'; - $op['message'] = 'Invalid Test Plan ID'; - } - } - - if ($statusOK) { - // Get all test project platforms, then validate - $platMgr = new tlPlatform($this->db,$tproject_id); - $platDomain = $platMgr->getAll(); - $idToLink = []; - $op['message'] = []; - - foreach ($plat2link as $accessObj) { - $checkOK = false; - if (property_exists($accessObj, 'name')) { - $needle = trim($accessObj->name); - foreach ($platDomain as $target) { - if ($target['name'] == $needle) { - $checkOK = true; - $idToLink[$target['id']] = $target['id']; - } - } - $statusOK = $statusOK && $checkOK; - if ($checkOK == false) { - $op['message'][] = "Platform with name:" . - $needle . - " does not exist"; - } - } - - if (property_exists($accessObj, 'id')) { - $needle = intval($accessObj->id); - foreach ($platDomain as $target) { - if ($target['id'] == $needle) { - $checkOK = true; - $idToLink[$target['id']] = $target['id']; - } - } - $statusOK = $statusOK && $checkOK; - if ($checkOK == false) { - $op['message'][] = "Platform with id:" . - $needle . - " does not exist"; - } - } - } - - $op['status'] = $statusOK; - } - - if ($statusOK) { - $p2link = []; - // Finally link platforms, if not linked yet - $gOpt = array('outputFormat' => 'mapAccessByID'); - $linked = (array)$platMgr->getLinkedToTestplan($tplan_id,$gOpt); - foreach ($idToLink as $plat_id) { - if (!isset($linked[$plat_id])) { - $p2link[$plat_id]=$plat_id; - } - } - if (count($p2link) >0){ - $platMgr->linkToTestplan($p2link,$tplan_id); - } - } - - if ($op['status']) { - $op['message'] = 'ok'; - } - } catch (Exception $e) { - $this->app->status(500); - $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); - } - echo json_encode($op); - } - - - - - /** - * - */ - function byeHTTP500($msg=null) { - $op = array(); - if( null == $msg ) { - $msg = 'TestLink Fatal Error - Malformed Request Body - ' . - ' json_decode() issue'; - } - $op['details'][] = sprintf($msg); - $this->app->status(500); - echo json_encode($op); - exit(); // Bye! - } + + * @package TestLink + * @since 1.9.7 + * + * Implemented using Slim framework Version 2.2.0 + * + * + * References + * http://ericbrandel.com/2013/01/14/quickly-build-restful-apis-in-php-with-slim-part-2/ + * https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+Add+Comment + * http://confluence.jetbrains.com/display/YTD4/Create+New+Work+Item + * http://www.redmine.org/projects/redmine/wiki/Rest_api + * http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/ + * https://github.com/educoder/pest/blob/master/examples/intouch_example.php + * http://stackoverflow.com/questions/9772933/rest-api-request-body-as-json-or-plain-post-data + * + * http://phptrycatch.blogspot.it/ + * http://nitschinger.at/A-primer-on-PHP-exceptions + * + * + * + */ +require_once '../../../../config.inc.php'; +require_once 'common.php'; +require_once 'Slim/Slim.php'; +\Slim\Slim::registerAutoloader(); + +/** + * + * @author Francisco Mancardi + * @package TestLink + */ +class tlRestApi +{ + + public static $version = "2.1.1"; + + /** + * The DB object used throughout the class + * + * @access protected + */ + protected $db = null; + + protected $tables = null; + + protected $tcaseMgr = null; + + protected $tprojectMgr = null; + + protected $tsuiteMgr = null; + + protected $tplanMgr = null; + + protected $tplanMetricsMgr = null; + + protected $reqSpecMgr = null; + + protected $reqMgr = null; + + protected $platformMgr = null; + + protected $buildMgr = null; + + protected $cfieldMgr = null; + + /** + * userID associated with the apiKey provided + */ + protected $userID = null; + + /** + * UserObject associated with the userID + */ + protected $user = null; + + /** + * array where all the args are stored for requests + */ + protected $args = null; + + /** + * array where error codes and messages are stored + */ + protected $errors = array(); + + /** + * The api key being used to make a request + */ + protected $apiKey = null; + + /** + * boolean to allow a method to invoke another method and avoid double auth + */ + protected $authenticated = false; + + /** + * The version of a test case that is being used + */ + /** + * This value is setted in following method: + */ + protected $tcVersionID = null; + + protected $versionNumber = null; + + protected $debugMsg; + + protected $cfg; + + protected $apiLogPathName; + + protected $l10n; + + /** + */ + public function __construct() + { + + // We are following Slim naming convention + $this->app = new \Slim\Slim(); + $this->app->contentType('application/json'); + + $tl = array( + 'API_MISSING_REQUIRED_PROP' => null, + 'API_TESTPLAN_ID_DOES_NOT_EXIST' => null, + 'API_TESTPLAN_APIKEY_DOES_NOT_EXIST' => null, + 'API_BUILDNAME_ALREADY_EXISTS' => null, + 'API_INVALID_BUILDID' => null + ); + + $this->l10n = init_labels($tl); + + // GET Routes + // test route with anonymous function + $this->app->get('/who', + function (): void { + echo __CLASS__ . ' : You have called the Get Route /who'; + }); + + // using middleware for authentication + // https://docs.slimframework.com/routing/middleware/ + // + $this->app->get('/whoAmI', array( + $this, + 'authenticate' + ), array( + $this, + 'whoAmI' + )); + + $this->app->get('/superman', array( + $this, + 'authenticate' + ), array( + $this, + 'superman' + )); + + $this->app->get('/testprojects', array( + $this, + 'authenticate' + ), array( + $this, + 'getProjects' + )); + + $this->app->get('/testprojects/:id', array( + $this, + 'authenticate' + ), array( + $this, + 'getProjects' + )); + $this->app->get('/testprojects/:id/testcases', + array( + $this, + 'authenticate' + ), array( + $this, + 'getProjectTestCases' + )); + $this->app->get('/testprojects/:id/testplans', + array( + $this, + 'authenticate' + ), array( + $this, + 'getProjectTestPlans' + )); + + $this->app->get('/testplans/:id/builds', array( + $this, + 'authenticate' + ), array( + $this, + 'getPlanBuilds' + )); + + // POST Routes + $this->app->post('/builds', array( + $this, + 'authenticate' + ), array( + $this, + 'createBuild' + )); + + $this->app->post('/testprojects', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestProject' + )); + + $this->app->post('/executions', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestCaseExecution' + )); + + $this->app->post('/testplans', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestPlan' + )); + + $this->app->post('/testplans/:id', array( + $this, + 'authenticate' + ), array( + $this, + 'updateTestPlan' + )); + + $this->app->post('/testplans/:id/platforms', + array( + $this, + 'authenticate' + ), array( + $this, + 'addPlatformsToTestPlan' + )); + + $this->app->post('/testsuites', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestSuite' + )); + + $this->app->post('/testcases', array( + $this, + 'authenticate' + ), array( + $this, + 'createTestCase' + )); + + $this->app->post('/keywords', array( + $this, + 'authenticate' + ), array( + $this, + 'createKeyword' + )); + + // update routes + $this->app->post('/builds/:id', array( + $this, + 'authenticate' + ), array( + $this, + 'updateBuild' + )); + + $this->apiLogPathName = '/var/testlink/rest-api.log'; + + $this->db = new database(DB_TYPE); + $this->db->db->SetFetchMode(ADODB_FETCH_ASSOC); + doDBConnect($this->db, database::ONERROREXIT); + + $this->tcaseMgr = new testcase($this->db); + $this->tprojectMgr = new testproject($this->db); + $this->tsuiteMgr = new testsuite($this->db); + + $this->tplanMgr = new testplan($this->db); + $this->tplanMetricsMgr = new tlTestPlanMetrics($this->db); + $this->reqSpecMgr = new requirement_spec_mgr($this->db); + $this->reqMgr = new requirement_mgr($this->db); + $this->cfieldMgr = $this->tprojectMgr->cfield_mgr; + $this->buildMgr = new build_mgr($this->db); + + $this->tables = $this->tcaseMgr->getDBTables(); + + $this->cfg = array(); + $conf = config_get('results'); + foreach ($conf['status_label_for_exec_ui'] as $key => $label) { + $this->cfg['exec']['statusCode'][$key] = $conf['status_code'][$key]; + } + + $this->cfg['exec']['codeStatus'] = array_flip( + $this->cfg['exec']['statusCode']); + + $this->cfg['tcase']['executionType'] = config_get('execution_type'); + $this->cfg['tcase']['status'] = config_get('testCaseStatus'); + $this->cfg['tcase']['executionType'] = config_get('execution_type'); + + $x = config_get('importance'); + $this->cfg['tcase']['importance'] = []; + foreach ($x['code_label'] as $code => $label) { + $this->cfg['tcase']['importance'][$label] = $code; + } + + // DEFAULTS + $this->cfg['tcase']['defaults']['executionType'] = $this->cfg['tcase']['executionType']['manual']; + + $this->cfg['tcase']['defaults']['importance'] = config_get( + 'testcase_importance_default'); + + $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: '; + } + + /** + */ + public function authenticate(\Slim\Route $route) + { + $apiKey = null; + if (is_null($apiKey)) { + $request = $this->app->request(); + $hh = $request->headers(); + + if (isset($hh['APIKEY'])) { + $apiKey = $hh['APIKEY']; + } else { + $apiKey = $hh['PHP_AUTH_USER']; + } + } + $sql = "SELECT id FROM {$this->tables['users']} " . "WHERE script_key='" . + $this->db->prepare_string($apiKey) . "'"; + + $this->userID = $this->db->fetchFirstRowSingleColumn($sql, "id"); + if ($ok = ! is_null($this->userID)) { + $this->user = tlUser::getByID($this->db, $this->userID); + } else { + $this->app->status(400); + echo json_encode( + array( + 'status' => 'ko', + 'message' => 'authentication error' + )); + $this->app->stop(); + } + + return $ok; + } + + /** + */ + public function whoAmI() + { + echo json_encode( + array( + 'name' => __CLASS__ . ' : You have called Get Route /whoAmI' + )); + } + + /** + */ + public function superman() + { + echo json_encode( + array( + 'name' => __CLASS__ . + ' : You have called the Get Route /superman' + )); + } + + /** + * + * @param + * mixed idCard if provided identifies test project + * if intval() > 0 => is considered DBID + * else => is used as PROJECT NAME + */ + public function getProjects($idCard = null, $opt = null) + { + $options = array_merge(array( + 'output' => 'rest' + ), (array) $opt); + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'item' => null + ); + if (is_null($idCard)) { + $opOptions = array( + 'output' => 'array_of_map', + 'order_by' => " ORDER BY name ", + 'add_issuetracker' => true, + 'add_reqmgrsystem' => true + ); + $op['item'] = $this->tprojectMgr->get_accessible_for_user( + $this->userID, $opOptions); + } else { + $opOptions = array( + 'output' => 'map', + 'field_set' => 'prefix', + 'format' => 'simple' + ); + $zx = $this->tprojectMgr->get_accessible_for_user($this->userID, + $opOptions); + + $targetID = null; + if (($safeID = intval($idCard)) > 0) { + if (isset($zx[$safeID])) { + $targetID = $safeID; + } + } else { + // Will consider id = name or prefix + foreach ($zx as $itemID => $value) { + if (strcmp($value['name'], $idCard) == 0 || + strcmp($value['prefix'], $idCard) == 0) { + $targetID = $itemID; + break; + } + } + } + + if (null != $targetID) { + $op['item'] = $this->tprojectMgr->get_by_id($targetID); + } + } + + // Developer (silly?) information + // json_encode() transforms maps in objects. + switch ($options['output']) { + case 'internal': + return $op['item']; + break; + + case 'rest': + default: + echo json_encode($op); + break; + } + } + + /** + * + * @param + * mixed idCard if provided identifies test project + * if intval() > 0 => is considered DBID + * else => is used as PROJECT NAME Or Prefix + */ + public function getProjectTestPlans($idCard) + { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ); + $tproject = $this->getProjects($idCard, array( + 'output' => 'internal' + )); + + if (! is_null($tproject)) { + $items = $this->tprojectMgr->get_all_testplans($tproject['id']); + $op['items'] = (! empty($items)) ? $items : null; + } else { + $op['message'] = "No Test Project identified by '" . $idCard . "'!"; + $op['status'] = 'error'; + $this->app->status(500); + } + + echo json_encode($op); + } + + /** + * Will return LATEST VERSION of each test case. + * Does return test step info ? + * + * @param + * mixed idCard if provided identifies test project + * if intval() > 0 => is considered DBID + * else => is used as PROJECT NAME + */ + public function getProjectTestCases($idCard) + { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ); + $tproject = $this->getProjects($idCard, array( + 'output' => 'internal' + )); + + if (! is_null($tproject)) { + $tcaseIDSet = array(); + $this->tprojectMgr->get_all_testcases_id($tproject['id'], + $tcaseIDSet); + + if (! empty($tcaseIDSet)) { + $op['items'] = array(); + foreach ($tcaseIDSet as $tcaseID) { + $item = $this->tcaseMgr->getLastVersionInfo($tcaseID); + $item['keywords'] = $this->tcaseMgr->get_keywords_map( + $tcaseID, $item['tcversion_id']); + $item['customfields'] = $this->tcaseMgr->get_linked_cfields_at_design( + $tcaseID, $item['tcversion_id'], null, null, + $tproject['id']); + $op['items'][] = $item; + } + } + } else { + $op['message'] = "No Test Project identified by '" . $idCard . "'!"; + $op['status'] = 'error'; + } + + echo json_encode($op); + } + + /** + * $item->name + * $item->prefix + * $item->notes + * $item->active + * $item->public + * $item->options + * $item->options->requirementsEnabled + * $item->options->testPriorityEnabled + * $item->options->automationEnabled + * $item->options->inventoryEnabled + */ + public function createTestProject() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + + try { + // Check user grants for requested operation + // This is a global right + $rightToCheck = "mgt_modify_product"; + if ($this->userHasRight($rightToCheck)) { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $op['id'] = $this->tprojectMgr->create($item, + array( + 'doChecks' => true + )); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + } else { + $this->app->status(403); + $msg = lang_get('API_INSUFFICIENT_RIGHTS'); + $op['message'] = sprintf($msg, $rightToCheck, 0, 0); + } + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + echo json_encode($op); + } + + /** + * Request Body + * + * $ex->testPlanID + * $ex->buildID + * $ex->platformID -> optional + * $ex->testCaseExternalID + * $ex->notes + * $ex->statusCode + * + * + * Checks to be done + * + * A. User right & Test plan existence + * user has right to execute on target Test plan? + * this means also that: Test plan ID exists ? + * + * B. Build + * does Build ID exist on target Test plan ? + * is Build enable to execution ? + * + * C. Platform + * do we need a platform ID in order to execute ? + * is a platform present on provided data ? + * does this platform belong to target Test plan ? + * + * D. Test case identity + * is target Test case part of Test plan ? + * + * + * Z. Other mandatory information + * We are not going to check for other mandatory info + * like: mandatory custom fields. (if we will be able in future to manage it) + */ + public function createTestCaseExecution() + { + $op = array( + 'status' => ' ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $ex = json_decode($request->getBody()); + $util = $this->checkExecutionEnvironment($ex); + + // Complete missing propertie + if (! property_exists($ex, 'platformID')) { + $ex->platformID = 0; + } + + if (! property_exists($ex, 'executionType')) { + $ex->executionType = $this->cfg['tcase']['executionType']['auto']; + } + + // If we are here this means we can write execution status!!! + $ex->testerID = $this->userID; + foreach ($util as $prop => $value) { + $ex->$prop = $value; + } + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $op['id'] = $this->tplanMgr->writeExecution($ex); + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); + } + echo json_encode($op); + } + + // + // Support methods + // + private function checkExecutionEnvironment($ex) + { + $platform = 0; + + // Test plan ID exists and is ACTIVE + $msg = 'invalid Test plan ID'; + $getOpt = array( + 'output' => 'testPlanFields', + 'active' => 1, + 'testPlanFields' => 'id,testproject_id,is_public' + ); + $status_ok = ! is_null( + $testPlan = $this->tplanMgr->get_by_id($ex->testPlanID, $getOpt)); + + if ($status_ok) { + // user has right to execute on Test plan ID + // hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) + $msg = 'user has no right to execute'; + $status_ok = $this->user->hasRight($this->db, 'testplan_execute', + $testPlan['testproject_id'], $ex->testPlanID, true); + } + + if ($status_ok) { + // Check if couple (buildID,testPlanID) is valid + $msg = '(buildID,testPlanID) couple is not valid'; + $getOpt = array( + 'fields' => 'id,active,is_open', + 'buildID' => $ex->buildID, + 'orderBy' => null + ); + $status_ok = ! is_null( + $build = $this->tplanMgr->get_builds($ex->testPlanID, null, null, + $getOpt)); + + if ($status_ok) { + // now check is execution can be done againts this build + $msg = 'Build is not active and/or closed => execution can not be done'; + $status_ok = $build[$ex->buildID]['active'] && + $build[$ex->buildID]['is_open']; + } + } + + if ($status_ok && property_exists($ex, 'platformID')) { + // Get Test plan platforms + $platform = $ex->platformID; + + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'addIfNull' => false + ); + $platformSet = $this->tplanMgr->getPlatforms($ex->testPlanID, + $getOpt); + + if (! ($hasPlatforms = ! is_null($platformSet)) && $platform != 0) { + $status_ok = false; + $msg = 'You can not execute against a platform, because Test plan has no platforms'; + } + + if ($status_ok && $hasPlatforms) { + if ($platform == 0) { + $status_ok = false; + $msg = 'Test plan has platforms, you need to provide one in order to execute'; + } elseif (! isset($platformSet[$platform])) { + $status_ok = false; + $msg = '(platform,test plan) couple is not valid'; + } + } + } + + if ($status_ok) { + // Test case check + $msg = 'Test case does not exist'; + + $tcaseID = $this->tcaseMgr->getInternalID($ex->testCaseExternalID); + if ($status_ok = ($tcaseID > 0)) { + $msg = 'Test case doesn not belong to right test project'; + $testCaseTestProject = $this->tcaseMgr->getTestProjectFromTestCase( + $tcaseID, 0); + $status_ok = ($testCaseTestProject == $testPlan['testproject_id']); + } + + if ($status_ok) { + // Does this test case is linked to test plan ? + $msg = 'Test case is not linked to (test plan,platform) => can not be executed'; + $getFilters = array( + 'testplan_id' => $ex->testPlanID, + 'platform_id' => $platform + ); + + $getOpt = array( + 'output' => 'simple' + ); + $links = $this->tcaseMgr->get_linked_versions($tcaseID, + $getFilters, $getOpt); + $status_ok = ! is_null($links); + } + } + + if ($status_ok) { + // status code is OK ? + $msg = 'not run status is not a valid execution status (can not be written to DB)'; + $status_ok = ($ex->statusCode != + $this->cfg['exec']['statusCode']['not_run']); + + if ($status_ok) { + $msg = 'Requested execution status is not configured on TestLink'; + $status_ok = isset( + $this->cfg['exec']['codeStatus'][$ex->statusCode]); + } + } + + if ($status_ok) { + $msg = 'Last Step Get testCaseVersionNumber'; + $ret = new stdClass(); + $ret->testProjectID = $testPlan['testproject_id']; + $ret->testCaseVersionID = key($links); + + $status_ok = false; + if (isset( + $links[$ret->testCaseVersionID][$ex->testPlanID][$platform]['version'])) { + $ret->testCaseVersionNumber = $links[$ret->testCaseVersionID][$ex->testPlanID][$platform]['version']; + $status_ok = true; + } + } + + if (! $status_ok) { + throw new Exception($msg); + } + + return $ret; + } + + /** + * 'name' + * 'testProjectID' + * 'notes' + * 'active' + * 'is_public' + */ + public function createTestPlan() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $opeOpt = array( + 'setSessionProject' => false, + 'doChecks' => true + ); + $op['id'] = $this->tplanMgr->createFromObject($item, $opeOpt); + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); + } + echo json_encode($op); + } + + /** + * 'name' + * 'testProjectID' + * 'notes' + * 'active' + * 'is_public' + */ + public function updateTestPlan($id) + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $item->id = $id; + $op['id'] = $this->tplanMgr->updateFromObject($item); + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . $e->getMessage(); + } + echo json_encode($op); + } + + /** + * 'name' + * 'testProjectID' + * 'parentID' + * 'notes' + * 'order' + */ + public function createTestSuite() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $op['id'] = $this->tsuiteMgr->createFromObject($item, + array( + 'doChecks' => true + )); + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . msgFromException($e); + } + echo json_encode($op); + } + + /** + * "name" + * "testSuite": {"id": xxx} + * "testProject" : {"id": xxx} or {"prefix": yyy} + * "authorLogin" + * "authorID" + * + * "summary" can be a string or an array of strings + * "preconditions" can be a string or an array of strings + * + * "importance" - see const.inc.php for domain + * "executionType" - see ... for domain + * "order" + * + * "estimatedExecutionDuration" // to be implemented + * "steps": array of objects + * IMPORTANT NOTICE: actions and expected_results + * Can be string or array of strings + * [ + * { "step_number":1, + * "actions": "red", + * "expected_results": "#f00", + * "execution_type":1 + * }, + * { "step_number":12, + * "actions": "red12", + * "expected_results": "#f00", + * "execution_type":2 + * } + * ] + */ + public function createTestCase() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + + // It will be important to document WHY!!! + // AFAIK some issues with json_decode() + // https://stackoverflow.com/questions/34486346/new-lines-and-tabs-in-json-decode-php-7 + $body = str_replace("\n", '', $request->getBody()); + $item = json_decode($body); + + if (is_null($item)) { + throw new Exception( + "Fatal Error " . __METHOD__ . + " json_decode(requesBody) is NULL", 1); + } + + // create obj with standard properties + try { + $tcase = $this->buildTestCaseObj($item); + $this->checkRelatives($tcase); + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = 'After buildTestCaseObj() >> ' . + $this->msgFromException($e); + echo json_encode($op); + return; + } + + $ou = $this->tcaseMgr->createFromObject($tcase); + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'id' => - 1 + ); + if (($op['id'] = $ou['id']) <= 0) { + $op['status'] = 'ko'; + $op['message'] = $ou['msg']; + $this->app->status(409); + } + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + echo json_encode($op); + } + + /** + * + * @param + * mixed testplan + * + * step 1) testplan is a number ? + * use it as test plan id + * + * step 2) testplan is a string ? + * use it as test plan apikey + * + * Is not possible to consider testplan as name + * becase name can be used in several test projects. + * One option can be request testprojectname/testplanname + * + * @param + * string name: build name + * @param + * string [notes] + * @param + * string [active] + * @param + * string [open] + * @param + * string [releasedate]: format YYYY-MM-DD; + * @param + * int [copytestersfrombuild] + * + * step 1) is a number ? + * will be considered a target build id. + * check will be done to verify that is a valid + * build id inside the test plan. + * + * step 2) is a string ? + * will be used as build name to search inside + * the test plan. + * + * if check is OK, tester assignments will be copied. + * + */ + public function createBuild() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'details' => array(), + 'id' => - 1 + ); + + $rightToCheck = "testplan_create_build"; + + // need to get input, before doing right checks, + // because right can be tested against in this order + // Test Plan Right + // Test Project Right + // Default Right + $request = $this->app->request(); + $item = json_decode($request->getBody()); + if (null == $item) { + $this->byeHTTP500(); + } + + $statusOK = true; + $build = new stdClass(); + + $reqProps = array( + 'testplan', + 'name' + ); + foreach ($reqProps as $prop) { + if (! property_exists($item, $prop)) { + $op['details'][] = $this->l10n['API_MISSING_REQUIRED_PROP'] . + $prop; + $statusOK = false; + } + } + + if ($statusOK) { + $build->name = $item->name; + + if (is_numeric($item->testplan)) { + // Check if is a valid test plan + // Get it's test project id + $tplan_id = intval($item->testplan); + $tplan = $this->tplanMgr->get_by_id($tplan_id); + + if (null == $tplan) { + $statusOK = false; + $op['details'][] = sprintf( + $this->l10n['API_TESTPLAN_ID_DOES_NOT_EXIST'], + $item->testplan); + $this->app->status(404); + } + } else { + $tplanAPIKey = trim($item->testplan); + $tplan = $this->tplanMgr->getByAPIKey($tplanAPIKey); + if (null == $tplan) { + $statusOK = false; + $op['details'][] = sprintf( + $this->l10n['API_TESTPLAN_APIKEY_DOES_NOT_EXIST'], + $item->testplan); + $this->app->status(404); + } + } + } + + if ($statusOK) { + // Ready to check user permissions + $context = array( + 'tplan_id' => $tplan['id'], + 'tproject_id' => $tplan['testproject_id'] + ); + + if (! $this->userHasRight($rightToCheck, true, $context)) { + $statusOK = false; + $msg = lang_get('API_INSUFFICIENT_RIGHTS'); + $op['message'] = sprintf($msg, $rightToCheck, $this->user->login, + $context['tproject_id'], $context['tplan_id']); + $this->app->status(403); + } + } + + // Go ahead, try create build!! + // Step 1 - Check if build name already exists + if ($statusOK) { + $build->id = $this->tplanMgr->get_build_id_by_name( + $context['tplan_id'], $build->name); + + if ($build->id > 0) { + $statusOK = false; + $op['message'] = sprintf( + $this->l10n['API_BUILDNAME_ALREADY_EXISTS'], $build->name, + $build->id); + $this->app->status(409); + } + + $build->tplan_id = $context['tplan_id']; + } + + // Step 2 - Finally Create It!! + if ($statusOK) { + // key 2 check with default value is parameter is missing + $k2check = array( + 'is_open' => 1, + 'release_candidate' => null, + 'notes' => null, + 'commit_id' => null, + 'tag' => null, + 'branch' => null, + 'is_active' => 1, + 'active' => 1, + 'releasedate' => null, + 'release_date' => null, + 'copy_testers_from_build' => null, + 'copytestersfrombuild' => null + ); + + $buildProp = array( + 'tplan_id' => 'tplan_id', + 'release_date' => 'release_date', + 'releasedate' => 'release_date', + 'active' => 'is_active', + 'is_active' => 'is_active', + 'notes' => 'notes', + 'commit_id' => 'commit_id', + 'tag' => 'tag', + 'branch' => 'branch', + 'release_candidate' => 'release_candidate', + 'is_open' => 'is_open', + 'copytestersfrombuild' => 'copytestersfrombuild', + 'copy_testers_from_build' => 'copytestersfrombuild' + ); + + $skipKey = array(); + foreach ($k2check as $key => $value) { + $translate = $buildProp[$key]; + if (! isset($skipKey[$translate])) { + $build->$translate = $value; + if (property_exists($item, $key)) { + $build->$translate = $item->$key; + $skipKey[$translate] = true; + } + } + } + + $itemID = $this->buildMgr->createFromObject($build); + if ($itemID > 0) { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'details' => array(), + 'id' => $itemID + ); + } + } + + echo json_encode($op); + } + + /** + */ + private function getUserIDByAttr($user) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $run = false; + $uid = - 1; + + $sql = "/* $debugMsg */ SELECT id FROM {$this->tables['users']} "; + if (property_exists($user, 'login')) { + $run = true; + $sql .= " WHERE login='" . + $this->db->prepare_string(trim($user->login)) . "'"; + } + + if (! $run && property_exists($user, 'id')) { + $run = true; + $sql .= " WHERE id=" . intval($user->id); + } + + if ($run) { + $rs = $this->db->get_recordset($sql); + } + + return ($run && ! is_null($rs)) ? $rs[0]['id'] : $uid; + } + + /** + */ + private function buildTestCaseObj(&$obj) + { + if (is_null($obj)) { + throw new Exception("Fatal Error - " . __METHOD__ . " arg is NULL"); + } + + $tcase = new stdClass(); + $tcase->authorID = - 1; + $tcase->steps = null; + $tcase->testProjectID = - 1; + + $accessKey = array(); + $isOK = true; + + // Knowing author is critic, because rights are related to user. + // Another important thing: + // do we need to check that author when provided, has rights to do + // requested action? + // If we do not do this check, we will find in test cases created + // by people that do not have rights. + // May be is time to add a field that provide info about source of action + // GUI, API + if (property_exists($obj, 'author') && + property_exists($obj->author, 'login') || + property_exists($obj->author, 'id')) { + $tcase->authorID = $this->getUserIDByAttr($obj->author); + } + + // Last resort: get author from credentials use to make the call. + // no error message returned. + if ($tcase->authorID <= 0) { + $tcase->authorID = $this->userID; + } + + // Mandatory attributes + $ma = array( + 'name' => null, + 'testProject' => array( + 'id', + 'prefix' + ), + 'testSuite' => array( + 'id' + ) + ); + + foreach ($ma as $key => $dummy) { + if (! ($isOK = $isOK && property_exists($obj, $key))) { + throw new Exception("Missing Attribute: {$key} "); + } + } + + foreach ($ma as $key => $attr) { + if (! is_null($attr)) { + $attrOK = false; + foreach ($attr as $ak) { + $accessKey[$key][$ak] = property_exists($obj->$key, $ak); + $attrOK = $attrOK || $accessKey[$key][$ak]; + } + + if (! $attrOK) { + $msg = "Attribute: {$key} mandatory key ("; + if (count($attr) > 1) { + $msg .= "one of set: "; + } + $msg .= implode('/', $attr) . ") is missing"; + throw new Exception($msg); + } + } + } + + $tcase->name = trim($obj->name); + $tcase->testSuiteID = intval($obj->testSuite->id); + + $gOpt = array( + 'output' => 'array_of_map', + 'field_set' => 'prefix', + 'add_issuetracker' => false, + 'add_reqmgrsystem' => false + ); + + $msg = "Test project with "; + if ($accessKey['testProject']['id']) { + $safeID = intval($obj->testProject->id); + $gFilters = array( + 'id' => array( + 'op' => '=', + 'value' => $safeID + ) + ); + $msg .= "id={$safeID} "; + } + + if ($accessKey['testProject']['prefix']) { + $gFilters = array( + 'prefix' => array( + 'op' => '=', + 'value' => trim($obj->testProject->prefix) + ) + ); + $msg .= "prefix={$obj->testProject->prefix} "; + } + + $info = $this->tprojectMgr->get_accessible_for_user($this->userID, $gOpt, + $gFilters); + + if (is_null($info)) { + $msg .= "does not exist or you have no rights to use it"; + throw new Exception($msg, 999); + } + + $tcase->testProjectID = intval($info[0]['id']); + + $sk2d = array( + 'summary' => '', + 'preconditions' => '', + 'order' => 100, + 'estimatedExecutionTime' => 0 + ); + foreach ($sk2d as $key => $value) { + $tcase->$key = property_exists($obj, $key) ? $obj->$key : $value; + } + + // summary & preconditions + // if type is array -> generate string in this way + // - add
    +        // - concact the elements with "\n"
    +        // - add 
    + $sk2d = array( + 'summary' => '', + 'preconditions' => '' + ); + foreach ($sk2d as $key => $value) { + if (is_array($tcase->$key)) { + $tcase->$key = "
    " . implode("\n", $tcase->$key) . "
    "; + } + } + + // these are objects with name as property. + $tcfg = $this->cfg['tcase']; + $ck2d = array( + 'executionType' => $tcfg['executionType']['manual'], + 'importance' => $tcfg['defaults']['importance'], + 'status' => $tcfg['status']['draft'] + ); + + foreach ($ck2d as $prop => $defa) { + $tcase->$prop = property_exists($obj, $prop) ? $tcfg[$prop][$obj->$prop->name] : $defa; + } + + if (property_exists($obj, 'steps')) { + $tcase->steps = []; + $sk2d = array( + 'actions' => '', + 'expected_results' => '' + ); + foreach ($obj->steps as $stepObj) { + foreach ($sk2d as $key => $value) { + if (is_array($stepObj->$key)) { + $stepObj->$key = "
    " . implode("\n", $stepObj->$key) .
    +                            "
    "; + } + } + $tcase->steps[] = $stepObj; + } + } + + return $tcase; + } + + /** + */ + private function checkRelatives($ctx) + { + $testProjectID = $ctx->testProjectID; + $testSuiteID = $ctx->testSuiteID; + if ($testProjectID <= 0) { + throw new Exception("Test Project ID is invalid (<=0)"); + } + + if ($testSuiteID <= 0) { + throw new Exception("Test Suite ID is invalid (<=0)"); + } + + $pinfo = $this->tprojectMgr->get_by_id($testProjectID); + if (is_null($pinfo)) { + throw new Exception("Test Project ID is invalid (does not exist)"); + } + + $pinfo = $this->tsuiteMgr->get_by_id($testSuiteID); + if (is_null($pinfo)) { + throw new Exception("Test Suite ID is invalid (does not exist)"); + } + + if ($testProjectID != + $this->tsuiteMgr->getTestProjectFromTestSuite($testSuiteID, + $testSuiteID)) { + throw new Exception("Test Suite does not belong to Test Project ID"); + } + } + + /** + * checks if a user has requested right on test project, test plan pair. + * + * @param string $rightToCheck + * one of the rights defined in rights table + * @param boolean $checkPublicPrivateAttr + * (optional) + * @param array $context + * (optional) + * keys tproject_id,tplan_id (both are also optional) + * + * @return boolean + * @access protected + * + * + */ + protected function userHasRight($rightToCheck, + $checkPublicPrivateAttr = false, $context = null) + { + $status_ok = true; + + // for global rights context is NULL + if (is_null($context)) { + $tproject_id = 0; + $tplan_id = null; + } else { + $tproject_id = intval( + isset($context['tproject_id']) ? $context['tproject_id'] : 0); + + $tplan_id = null; + if (isset($context['tplan_id'])) { + $tplan_id = intval($context['tplan_id']); + } + + if ($tproject_id <= 0 && ! is_null($tplan_id)) { + // get test project from test plan + $dummy = $this->tplanMgr->get_by_id($tplanid, + array( + 'output' => 'minimun' + )); + $tproject_id = intval($dummy['tproject_id']); + } + } + + if (! $this->user->hasRight($this->db, $rightToCheck, $tproject_id, + $tplan_id, $checkPublicPrivateAttr)) { + $status_ok = false; + } + return $status_ok; + } + + /** + * "keyword" + * "notes" + * "testProject": {"prefix":"APR"} + */ + public function createKeyword() + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $item = json_decode($request->getBody()); + if (is_null($item)) { + throw new Exception( + "Fatal Error " . __METHOD__ . + " json_decode(requestBody) is NULL", 1); + } + + // create obj with standard properties + $pfx = $item->testProject->prefix; + $pid = $this->tprojectMgr->get_by_prefix((string) $pfx); + if (null == $pid) { + $op['status'] = 'ko'; + $op['message'] = "Can't get test project ID"; + } else { + $pid = $pid['id']; + $ou = $this->tprojectMgr->addKeyword($pid, $item->keyword, + $item->notes); + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'id' => - 1 + ); + if (($op['id'] = $ou['id']) <= 0) { + $op['status'] = 'ko'; + $op['message'] = $ou['msg']; + } + } + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + echo json_encode($op); + } + + /** + * + * @param + * mixed idCard identifies test plan via apikey + * + */ + public function getPlanBuilds($idCard) + { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ); + $tplan = $this->tplanMgr->getByAPIKey($idCard); + + if (! is_null($tplan)) { + $items = $this->tplanMgr->get_builds($tplan['id']); + $op['items'] = (! empty($items)) ? $items : null; + } else { + $op['message'] = "No Test Plan identified by '" . $idCard . "'!"; + $op['status'] = 'error'; + } + + echo json_encode($op); + } + + /** + * + * @param + * string id: build id + * @param + * string [notes] + * @param + * string [active] + * @param + * string [open] + * @param + * string [releasedate]: format YYYY-MM-DD; + * @param + * int [copytestersfrombuild] + * + * step 1) is a number ? + * will be considered a target build id. + * check will be done to verify that is a valid + * build id inside the test plan. + * + * step 2) is a string ? + * will be used as build name to search inside + * the test plan. + * + * if check is OK, tester assignments will be copied. + * + */ + public function updateBuild($id) + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'details' => array(), + 'id' => - 1 + ); + + $rightToCheck = "testplan_create_build"; + + // need to get input, before doing right checks, + // because right can be tested against in this order + // Test Plan Right + // Test Project Right + // Default Right + $request = $this->app->request(); + $item = json_decode($request->getBody()); + if (null == $item) { + $this->byeHTTP500(); + } + + $statusOK = true; + if (intval($id) <= 0) { + $op['details'][] = $this->l10n['API_MISSING_REQUIRED_PROP'] . + 'id - the build ID'; + $statusOK = false; + } + + if ($statusOK) { + $build = $this->buildMgr->get_by_id($id); + + if (null == $build) { + $statusOK = false; + $op['message'] = sprintf($this->l10n['API_INVALID_BUILDID'], $id); + $this->app->status(404); + } + } + + if ($statusOK) { + $tplan = $this->tplanMgr->get_by_id($build['testplan_id']); + + // Ready to check user permissions + $context = array( + 'tplan_id' => $tplan['id'], + 'tproject_id' => $tplan['testproject_id'] + ); + + if (! $this->userHasRight($rightToCheck, true, $context)) { + $statusOK = false; + $msg = lang_get('API_INSUFFICIENT_RIGHTS'); + $op['message'] = sprintf($msg, $rightToCheck, $this->user->login, + $context['tproject_id'], $context['tplan_id']); + $this->app->status(403); + } + } + + // Go ahead, try to update build!! + if ($statusOK && property_exists($item, 'name') && + $this->tplanMgr->check_build_name_existence($tplan['id'], + $item->name, $id)) { + $statusOK = false; + $op['message'] = sprintf( + $this->l10n['API_BUILDNAME_ALREADY_EXISTS'], $item->name, $id); + $this->app->status(409); + } + + // Step 2 - Finally Update It!! + if ($statusOK) { + + $k2check = array( + 'is_open', + 'name', + 'release_candidate', + 'notes', + 'commit_id', + 'tag', + 'branch', + 'is_active', + 'active', + 'releasedate', + 'release_date', + 'copy_testers_from_build', + 'copytestersfrombuild' + ); + + $buildProp = array( + 'name' => 'name', + 'tplan_id' => 'tplan_id', + 'release_date' => 'release_date', + 'releasedate' => 'release_date', + 'active' => 'is_active', + 'is_active' => 'is_active', + 'notes' => 'notes', + 'commit_id' => 'commit_id', + 'tag' => 'tag', + 'branch' => 'branch', + 'release_candidate' => 'release_candidate', + 'is_open' => 'is_open', + 'copytestersfrombuild' => 'copytestersfrombuild', + 'copy_testers_from_build' => 'copytestersfrombuild' + ); + + $skipKey = array(); + $buildObj = new stdClass(); + $attr = array(); + foreach ($k2check as $key) { + $translate = $buildProp[$key]; + if (! isset($skipKey[$translate])) { + + // init with value got from DB. + if (isset($build[$translate])) { + $buildObj->$translate = $build[$translate]; + } + + if (property_exists($item, $key)) { + $buildObj->$translate = $item->$key; + $skipKey[$translate] = true; + } + + if (property_exists($buildObj, $translate)) { + $attr[$translate] = $buildObj->$translate; + } + } + } + + $ox = $this->buildMgr->update($build['id'], $buildObj->name, + $buildObj->notes, $attr); + + if ($ox) { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'details' => array(), + 'id' => $id + ); + + // Special processing Build Closing/Opening + // we need also to manage close on date. + if (property_exists($item, 'is_open')) { + $oio = intval($build['is_open']); + $nio = intval($item->is_open); + if ($oio != $nio) { + if ($nio) { + $this->buildMgr->setOpen($id); + } else { + $this->buildMgr->setClosed($id); + } + } + } + } + } + + echo json_encode($op); + } + + /** + * body will contain an array of objects + * that can be + * {'name': platform name} + * {'id': platform id} + * + * Check if done to understand if all platforms + * exist before doing any action + */ + public function addPlatformsToTestPlan($tplan_id) + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + try { + $request = $this->app->request(); + $plat2link = json_decode($request->getBody()); + + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $statusOK = true; + if (null == $plat2link || ! is_array($plat2link)) { + $statusOK = false; + $op['status'] = 'ko'; + $op['message'] = 'Bad Body'; + } + + if ($statusOK) { + // Validate Test plan existence. + // Get Test Project ID before doing anything + $getOpt = array( + 'output' => 'testPlanFields', + 'active' => 1, + 'testPlanFields' => 'id,testproject_id,is_public' + ); + + $testPlan = $this->tplanMgr->get_by_id($tplan_id, $getOpt); + $statusOK = ! is_null($testPlan); + + if ($statusOK) { + $tproject_id = $testPlan['testproject_id']; + } else { + $op['status'] = 'ko'; + $op['message'] = 'Invalid Test Plan ID'; + } + } + + if ($statusOK) { + // Get all test project platforms, then validate + $platMgr = new tlPlatform($this->db, $tproject_id); + $platDomain = $platMgr->getAll(); + $idToLink = []; + $op['message'] = []; + + foreach ($plat2link as $accessObj) { + $checkOK = false; + if (property_exists($accessObj, 'name')) { + $needle = trim($accessObj->name); + foreach ($platDomain as $target) { + if ($target['name'] == $needle) { + $checkOK = true; + $idToLink[$target['id']] = $target['id']; + } + } + $statusOK = $statusOK && $checkOK; + if (! $checkOK) { + $op['message'][] = "Platform with name:" . $needle . + " does not exist"; + } + } + + if (property_exists($accessObj, 'id')) { + $needle = intval($accessObj->id); + foreach ($platDomain as $target) { + if ($target['id'] == $needle) { + $checkOK = true; + $idToLink[$target['id']] = $target['id']; + } + } + $statusOK = $statusOK && $checkOK; + if (! $checkOK) { + $op['message'][] = "Platform with id:" . $needle . + " does not exist"; + } + } + } + + $op['status'] = $statusOK; + } + + if ($statusOK) { + $p2link = []; + // Finally link platforms, if not linked yet + $gOpt = array( + 'outputFormat' => 'mapAccessByID' + ); + $linked = (array) $platMgr->getLinkedToTestplan($tplan_id, $gOpt); + foreach ($idToLink as $plat_id) { + if (! isset($linked[$plat_id])) { + $p2link[$plat_id] = $plat_id; + } + } + if (! empty($p2link)) { + $platMgr->linkToTestplan($p2link, $tplan_id); + } + } + + if ($op['status']) { + $op['message'] = 'ok'; + } + } catch (Exception $e) { + $this->app->status(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + echo json_encode($op); + } + + /** + */ + private function byeHTTP500($msg = null) + { + $op = array(); + if (null == $msg) { + $msg = 'TestLink Fatal Error - Malformed Request Body - ' . + ' json_decode() issue'; + } + $op['details'][] = sprintf($msg); + $this->app->status(500); + echo json_encode($op); + exit(); // Bye! + } + + /** + */ + private function msgFromException($e) + { + return $e->getMessage() . ' - offending line number: ' . $e->getLine(); + } } // class end diff --git a/lib/api/rest/v3/.htaccess b/lib/api/rest/v3/.htaccess new file mode 100644 index 0000000000..9bcb891c63 --- /dev/null +++ b/lib/api/rest/v3/.htaccess @@ -0,0 +1,11 @@ +RewriteEngine On + +# Some hosts may require you to use the `RewriteBase` directive. +# If you need to use the `RewriteBase` directive, it should be the +# absolute physical path to the directory that contains this htaccess file. +# +# RewriteBase / + +RewriteCond %{REQUEST_FILENAME} !-f +#RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^ index.php [QSA,L] \ No newline at end of file diff --git a/lib/api/rest/v3/RestApi.class.php b/lib/api/rest/v3/RestApi.class.php new file mode 100644 index 0000000000..cfe5bfe9ab --- /dev/null +++ b/lib/api/rest/v3/RestApi.class.php @@ -0,0 +1,1762 @@ + + * @package TestLink + * + * Implemented using + * Slim framework Version 4.3.0 / 4.4.0 + * PHP > 7.4.0 + * + * References + * http://ericbrandel.com/2013/01/14/quickly-build-restful-apis-in-php-with-slim-part-2/ + * https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+Add+Comment + * http://confluence.jetbrains.com/display/YTD4/Create+New+Work+Item + * http://www.redmine.org/projects/redmine/wiki/Rest_api + * http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/ + * https://github.com/educoder/pest/blob/master/examples/intouch_example.php + * http://stackoverflow.com/questions/9772933/rest-api-request-body-as-json-or-plain-post-data + * + * http://phptrycatch.blogspot.it/ + * http://nitschinger.at/A-primer-on-PHP-exceptions + * + * + * + */ +require_once '../../../../config.inc.php'; +require_once 'common.php'; + +use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Server\RequestHandlerInterface as RequestHandler; +use Slim\Psr7\Response; + +/** + * + * @author Francisco Mancardi + * @package TestLink + */ +class RestApi +{ + + public static $version = "3.0"; + + /** + * The DB object used throughout the class + * + * @access protected + */ + protected $db = null; + + protected $tables = null; + + protected $tcaseMgr = null; + + protected $tprojectMgr = null; + + protected $tsuiteMgr = null; + + protected $tplanMgr = null; + + protected $tplanMetricsMgr = null; + + protected $reqSpecMgr = null; + + protected $reqMgr = null; + + protected $platformMgr = null; + + protected $buildMgr = null; + + protected $cfieldMgr = null; + + /** + * userID associated with the apiKey provided + */ + protected $userID = null; + + /** + * UserObject associated with the userID + */ + protected $user = null; + + /** + * The api key being used to make a request + */ + protected $apiKey = null; + + /** + * boolean to allow a method to invoke another method and avoid double auth + */ + protected $authenticated = false; + + /** + * The version of a test case that is being used + */ + /** + * This value is setted in following method: + */ + protected $tcVersionID = null; + + protected $versionNumber = null; + + protected $debugMsg; + + protected $cfg; + + protected $apiLogPathName; + + protected $l10n; + + /** + */ + public function __construct() + { + $tl = array( + 'API_MISSING_REQUIRED_PROP' => null, + 'API_TESTPLAN_ID_DOES_NOT_EXIST' => null, + 'API_TESTPLAN_APIKEY_DOES_NOT_EXIST' => null, + 'API_BUILDNAME_ALREADY_EXISTS' => null, + 'API_INVALID_BUILDID' => null + ); + + $this->l10n = init_labels($tl); + + $this->apiLogPathName = '/var/testlink/rest-api.log'; + + $this->db = new database(DB_TYPE); + $this->db->db->SetFetchMode(ADODB_FETCH_ASSOC); + doDBConnect($this->db, database::ONERROREXIT); + + $this->tcaseMgr = new testcase($this->db); + $this->tprojectMgr = new testproject($this->db); + $this->tsuiteMgr = new testsuite($this->db); + + $this->tplanMgr = new testplan($this->db); + $this->tplanMetricsMgr = new tlTestPlanMetrics($this->db); + $this->reqSpecMgr = new requirement_spec_mgr($this->db); + $this->reqMgr = new requirement_mgr($this->db); + $this->cfieldMgr = $this->tprojectMgr->cfield_mgr; + $this->buildMgr = new build_mgr($this->db); + + $this->tables = $this->tcaseMgr->getDBTables(); + + $this->cfg = array(); + $conf = config_get('results'); + foreach ($conf['status_label_for_exec_ui'] as $key => $label) { + $this->cfg['exec']['statusCode'][$key] = $conf['status_code'][$key]; + } + + $this->cfg['exec']['codeStatus'] = array_flip( + $this->cfg['exec']['statusCode']); + + $this->cfg['tcase']['status'] = config_get('testCaseStatus'); + $this->cfg['tcase']['executionType'] = config_get('execution_type'); + + $this->cfg['tcase']['executionType']['automatic'] = $this->cfg['tcase']['executionType']['auto']; + + $x = config_get('importance'); + $this->cfg['tcase']['importance'] = []; + foreach ($x['code_label'] as $code => $label) { + $this->cfg['tcase']['importance'][$label] = $code; + } + + // DEFAULTS + $this->cfg['tcase']['defaults']['executionType'] = $this->cfg['tcase']['executionType']['manual']; + + $this->cfg['tcase']['defaults']['importance'] = config_get( + 'testcase_importance_default'); + $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: '; + } + + /** + */ + public function authenticate(Request $request, RequestHandler $handler) + { + $hh = $request->getHeaders(); + + $apiKey = null; + + // @20200317 - Not tested + // IMPORTANT NOTICE: 'PHP_AUTH_USER' + // it seems this needs special configuration + // with Apache when you use CGI Module + // http://man.hubwiz.com/docset/PHP.docset/Contents/Resources/ + // Documents/php.net/manual/en/features.http-auth.html + $apiKeySet = [ + 'Apikey', + 'ApiKey', + 'APIKEY', + 'PHP_AUTH_USER' + ]; + foreach ($apiKeySet as $accessKey) { + if (isset($hh[$accessKey])) { + $apiKey = trim($hh[$accessKey][0]); + break; + } + } + + if ($apiKey != null && $apiKey != '') { + $sql = "SELECT id FROM {$this->tables['users']} " . + "WHERE script_key='" . $this->db->prepare_string($apiKey) . "'"; + + $this->userID = $this->db->fetchFirstRowSingleColumn($sql, "id"); + if (! is_null($this->userID)) { + $this->user = tlUser::getByID($this->db, $this->userID); + return $handler->handle($request); + } + } + + // ========================================================= + // Houston we have a problem + $msg = 'Authentication Error'; + if ($apiKey == null) { + $msg .= " (missing authentication key) "; + } + $response = new Response(); + $response->getBody()->write($msg); + $response->withStatus(401); + return $response; + } + + /** + */ + public function setContentTypeJSON(Request $request, RequestHandler $handler) + { + $response = $handler->handle($request); + return $response->withHeader('Content-Type', 'application/json'); + } + + /** + */ + public function whoAmI(Response $response) + { + $msg = json_encode( + array( + 'name' => __CLASS__ . ' : You have called Get Route /whoAmI' + )); + $response->getBody()->write($msg); + return $response; + } + + /** + * + * @param {array} $args + * parameter passed in route + * example + * ../testprojects/12 + * + * array(1) { + * ["id"]=> string(2) "12" + * } + * + */ + public function testprojects(Response $response, $args) + { + $itemSet = $this->getProjects($args); + + $payload = json_encode($itemSet); + $response->getBody()->write($payload); + return $response; + } + + /** + * + * @param + * array idCard if provided identifies test project + * Slim Framework will provided a map with a key + * as defined in the route. + * $app->get('/testprojects/{mixedID}/testplans', ... + * + * + */ + private function getProjects($idCard = null) + { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'item' => null + ); + if (empty($idCard)) { + $opOptions = array( + 'output' => 'array_of_map', + 'order_by' => " ORDER BY name ", + 'add_issuetracker' => true, + 'add_reqmgrsystem' => true + ); + $op['item'] = $this->tprojectMgr->get_accessible_for_user( + $this->userID, $opOptions); + } else { + $opOptions = array( + 'output' => 'map', + 'field_set' => 'prefix', + 'format' => 'simple' + ); + $zx = $this->tprojectMgr->get_accessible_for_user($this->userID, + $opOptions); + + $targetID = null; + $safeID = intval($idCard['mixedID']); + if ($safeID > 0) { + if (isset($zx[$safeID])) { + $targetID = $safeID; + } + } else { + // Will consider id = name or prefix + foreach ($zx as $itemID => $value) { + if (strcmp($value['name'], $idCard['mixedID']) == 0 || + strcmp($value['prefix'], $idCard['mixedID']) == 0) { + $targetID = $itemID; + break; + } + } + } + + if (null != $targetID) { + $op['item'] = $this->tprojectMgr->get_by_id($targetID); + } + } + + return $op['item']; + } + + /** + * Will return LATEST VERSION of each test case. + * Does return test step info ? + * + * @param + * array idCard if provided identifies test project + * 'id' -> DBID + * 'name' -> + * 'prefix' -> + */ + public function getProjectTestCases(Response $response, $idCard) + { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ); + $tproject = $this->getProjects($idCard); + + if (! is_null($tproject)) { + $tcaseIDSet = array(); + $this->tprojectMgr->get_all_testcases_id($tproject['id'], + $tcaseIDSet); + + if (! empty($tcaseIDSet)) { + $op['items'] = array(); + foreach ($tcaseIDSet as $tcaseID) { + $item = $this->tcaseMgr->getLastVersionInfo($tcaseID); + $item['keywords'] = $this->tcaseMgr->get_keywords_map( + $tcaseID, $item['tcversion_id']); + $item['customfields'] = $this->tcaseMgr->get_linked_cfields_at_design( + $tcaseID, $item['tcversion_id'], null, null, + $tproject['id']); + $op['items'][] = $item; + } + } + } else { + $op['message'] = "No Test Project identified by '" . $idCard . "'!"; + $op['status'] = 'error'; + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * $item->name + * $item->prefix + * $item->notes + * $item->active + * $item->public + * $item->options + * $item->options->requirementsEnabled + * $item->options->testPriorityEnabled + * $item->options->automationEnabled + * $item->options->inventoryEnabled + */ + public function createTestProject(Request $request, Response $response) + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + + try { + // Check user grants for requested operation + // This is a global right + $rightToCheck = "mgt_modify_product"; + if ($this->userHasRight($rightToCheck)) { + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $item = json_decode($request->getBody()); + $op['id'] = $this->tprojectMgr->create($item, + array( + 'doChecks' => true + )); + } else { + $response = new Response(); + $response->withStatus(403); + + $msg = lang_get('API_INSUFFICIENT_RIGHTS'); + $op['message'] = sprintf($msg, $rightToCheck, 0, 0); + } + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * + * @param + * array idCard if provided identifies test project + * 'id' -> DBID + * 'name' -> + * 'prefix' -> + */ + public function getProjectTestPlans(Response $response, $idCard) + { + $op = [ + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ]; + + $tproj = $this->getProjects($idCard); + + if (! is_null($tproj)) { + $items = $this->tprojectMgr->get_all_testplans($tproj['id']); + $op['items'] = (! empty($items)) ? $items : null; + } else { + $op['message'] = "No Test Project identified by '" . $idCard . "'!"; + $op['status'] = 'error'; + $response = new Response(); + $response->withStatus(500); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * + * @param + * map idCard[tplanApiKey] + * + */ + public function getPlanBuilds(Response $response, $idCard) + { + $op = $this->getStdOp(); + $tplan = $this->tplanMgr->getByAPIKey($idCard['tplanApiKey']); + + if (! is_null($tplan)) { + $items = $this->tplanMgr->get_builds($tplan['id']); + $op['items'] = (! empty($items)) ? $items : null; + } else { + $op['message'] = "No Test Plan identified by API KEY:" . + $idCard['tplanApiKey'] . ""; + $op['status'] = 'error'; + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * + * @param + * mixed testplan + * + * step 1) testplan is a number ? + * use it as test plan id + * + * step 2) testplan is a string ? + * use it as test plan apikey + * + * Is not possible to consider testplan as name + * becase name can be used in several test projects. + * One option can be request testprojectname/testplanname + * + * @param + * string name: build name + * @param + * string [notes] + * @param + * string [active] + * @param + * string [open] + * @param + * string [releasedate]: format YYYY-MM-DD; + * @param + * int [copytestersfrombuild] + * + * step 1) is a number ? + * will be considered a target build id. + * check will be done to verify that + * is a valid build id inside the test plan. + * + * step 2) is a string ? + * will be used as build name + * to search inside the test plan. + * + * if check is OK, tester assignments will be copied. + * + */ + public function createBuild(Request $request, Response $response) + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'details' => array(), + 'id' => - 1 + ); + + $rightToCheck = "testplan_create_build"; + + // need to get input, before doing right checks, + // because right can be tested against in this order + // Test Plan Right + // Test Project Right + // Default Right + $item = json_decode($request->getBody()); + if (null == $item) { + $this->byeHTTP500(__METHOD__); // No return from it + } + + $statusOK = true; + $build = new stdClass(); + + $reqProps = array( + 'testplan', + 'name' + ); + foreach ($reqProps as $prop) { + if (! property_exists($item, $prop)) { + $op['details'][] = $this->l10n['API_MISSING_REQUIRED_PROP'] . + $prop; + $statusOK = false; + } + } + + if ($statusOK) { + $build->name = $item->name; + + if (is_numeric($item->testplan)) { + // Check if is a valid test plan + // Get it's test project id + $tplan_id = intval($item->testplan); + $tplan = $this->tplanMgr->get_by_id($tplan_id); + + if (null == $tplan) { + $statusOK = false; + $op['details'][] = sprintf( + $this->l10n['API_TESTPLAN_ID_DOES_NOT_EXIST'], + $item->testplan); + + $response = new Response(); + $response->withStatus(404); + } + } else { + $tplanAPIKey = trim($item->testplan); + $tplan = $this->tplanMgr->getByAPIKey($tplanAPIKey); + if (null == $tplan) { + $statusOK = false; + $op['details'][] = sprintf( + $this->l10n['API_TESTPLAN_APIKEY_DOES_NOT_EXIST'], + $item->testplan); + + $response = new Response(); + $response->withStatus(404); + } + } + } + + if ($statusOK) { + // Ready to check user permissions + $context = array( + 'tplan_id' => $tplan['id'], + 'tproject_id' => $tplan['testproject_id'] + ); + + if (! $this->userHasRight($rightToCheck, true, $context)) { + $statusOK = false; + $msg = lang_get('API_INSUFFICIENT_RIGHTS'); + $op['message'] = sprintf($msg, $rightToCheck, $this->user->login, + $context['tproject_id'], $context['tplan_id']); + + $response = new Response(); + $response->withStatus(404); + } + } + + // Go ahead, try create build!! + // Step 1 - Check if build name already exists + if ($statusOK) { + $build->id = $this->tplanMgr->get_build_id_by_name( + $context['tplan_id'], $build->name); + + if ($build->id > 0) { + $statusOK = false; + $op['message'] = sprintf( + $this->l10n['API_BUILDNAME_ALREADY_EXISTS'], $build->name, + $build->id); + + $response = new Response(); + $response->withStatus(409); + } + + $build->tplan_id = $context['tplan_id']; + } + + // Step 2 - Finally Create It!! + if ($statusOK) { + // key 2 check with default value is parameter is missing + $k2check = array( + 'is_open' => 1, + 'release_candidate' => null, + 'notes' => null, + 'commit_id' => null, + 'tag' => null, + 'branch' => null, + 'is_active' => 1, + 'active' => 1, + 'releasedate' => null, + 'release_date' => null, + 'copy_testers_from_build' => null, + 'copytestersfrombuild' => null + ); + + $buildProp = $this->buildPropMapping(); + + $skipKey = array(); + foreach ($k2check as $key => $value) { + $translate = $buildProp[$key]; + if (! isset($skipKey[$translate])) { + $build->$translate = $value; + if (property_exists($item, $key)) { + $build->$translate = $item->$key; + $skipKey[$translate] = true; + } + } + } + + $itemID = $this->buildMgr->createFromObject($build); + if ($itemID > 0) { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'details' => array(), + 'id' => $itemID + ); + } + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * + * @param + * string id: build id + * @param + * string [notes] + * @param + * string [active] + * @param + * string [open] + * @param + * string [releasedate]: format YYYY-MM-DD; + * @param + * int [copytestersfrombuild] + * + * step 1) is a number ? + * will be considered a target build id. + * check will be done to verify that is + * a valid build id inside the test plan. + * + * step 2) is a string ? + * will be used as build name to search + * inside the test plan. + * + * if check is OK, tester assignments will be copied. + * + */ + public function updateBuild(Request $request, Response $response, $args) + { + $op = array( + 'status' => 'ko', + 'message' => 'ko', + 'details' => array(), + 'id' => - 1 + ); + + $id = intval($args['id']); + $rightToCheck = "testplan_create_build"; + + // need to get input, before doing right checks, + // because right can be tested against in this order + // Test Plan Right + // Test Project Right + // Default Right + $item = json_decode($request->getBody()); + if (null == $item) { + $this->byeHTTP500(__METHOD__); // No return from it + } + + $statusOK = true; + if ($id <= 0) { + $op['details'][] = $this->l10n['API_MISSING_REQUIRED_PROP'] . + 'id - the build ID'; + $statusOK = false; + } + + if ($statusOK) { + $build = $this->buildMgr->get_by_id($id); + + if (null == $build) { + $statusOK = false; + $op['message'] = sprintf($this->l10n['API_INVALID_BUILDID'], $id); + + $response = new Response(); + $response->withStatus(404); + } + } + + if ($statusOK) { + $tplan = $this->tplanMgr->get_by_id($build['testplan_id']); + + // Ready to check user permissions + $context = array( + 'tplan_id' => $tplan['id'], + 'tproject_id' => $tplan['testproject_id'] + ); + + if (! $this->userHasRight($rightToCheck, true, $context)) { + $statusOK = false; + $msg = lang_get('API_INSUFFICIENT_RIGHTS'); + $op['message'] = sprintf($msg, $rightToCheck, $this->user->login, + $context['tproject_id'], $context['tplan_id']); + + $response = new Response(); + $response->withStatus(403); + } + } + + // Go ahead, try to update build!! + if ($statusOK && property_exists($item, 'name') && + $this->tplanMgr->check_build_name_existence($tplan['id'], + $item->name, $id)) { + $statusOK = false; + $op['message'] = sprintf( + $this->l10n['API_BUILDNAME_ALREADY_EXISTS'], $item->name, $id); + + $response = new Response(); + $response->withStatus(409); + } + + // Step 2 - Finally Update It!! + if ($statusOK) { + $k2check = array( + 'is_open', + 'name', + 'release_candidate', + 'notes', + 'commit_id', + 'tag', + 'branch', + 'is_active', + 'active', + 'releasedate', + 'release_date', + 'copy_testers_from_build', + 'copytestersfrombuild' + ); + + $buildProp = $this->buildPropMapping(); + + $skipKey = array(); + $buildObj = new stdClass(); + $attr = array(); + foreach ($k2check as $key) { + $translate = $buildProp[$key]; + if (! isset($skipKey[$translate])) { + + // init with value got from DB. + if (isset($build[$translate])) { + $buildObj->$translate = $build[$translate]; + } + + if (property_exists($item, $key)) { + $buildObj->$translate = $item->$key; + $skipKey[$translate] = true; + } + + if (property_exists($buildObj, $translate)) { + $attr[$translate] = $buildObj->$translate; + } + } + } + + // key 2 check + // $id,$name,$notes,$active=null,$open=null, + // $release_date='',$closed_on_date='') { + + $ox = $this->buildMgr->update($build['id'], $buildObj->name, + $buildObj->notes, $attr); + + if ($ox) { + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'details' => array(), + 'id' => $id + ); + + // Special processing Build Closing/Opening + // we need also to manage close on date. + if (property_exists($item, 'is_open')) { + $oio = intval($build['is_open']); + $nio = intval($item->is_open); + if ($oio != $nio) { + if ($nio) { + $this->buildMgr->setOpen($id); + } else { + $this->buildMgr->setClosed($id); + } + } + } + } + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * 'name' + * 'testProjectID' + * 'testProjectPrefix' + * 'notes' + * 'active' + * 'is_public' + */ + public function createTestPlan(Request $request, Response $response) + { + $op = $this->getStdIDKO(); + try { + $item = json_decode($request->getBody()); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $opeOpt = array( + 'setSessionProject' => false, + 'doChecks' => true + ); + + if (property_exists($item, 'testProjectPrefix')) { + $pi = $this->tprojectMgr->get_by_prefix( + trim($item->testProjectPrefix)); + $item->testProjectID = intval($pi[id]); + } + + $op['id'] = $this->tplanMgr->createFromObject($item, $opeOpt); + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * 'name' + * 'testProjectID' + * 'notes' + * 'active' + * 'is_public' + */ + public function updateTestPlan(Request $request, Response $response, $args) + { + $op = $this->getStdIDKO(); + $id = intval($args['id']); + try { + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $item = json_decode($request->getBody()); + $item->id = $id; + var_dump($item); + $op['id'] = $this->tplanMgr->updateFromObject($item); + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + $op['message'] = $this->msgFromException($e); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * Request Body + * + * $ex->testPlanID + * $ex->buildID + * $ex->platformID -> optional + * $ex->testCaseExternalID + * $ex->notes + * $ex->statusCode + * $ex-steps [] + * An example: + * "steps":[ + * { + * "stepNumber":1, + * "notes":"This is an execution created via REST API", + * "statusCode":"b" + * }, + * { + * "stepNumber":12, + * "notes":"This is an execution created via REST API", + * "statusCode":"f" + * } + * ] + * + * Checks to be done + * + * A. User right & Test plan existence + * user has right to execute on target Test plan? + * this means also that: Test plan ID exists ? + * + * B. Build + * does Build ID exist on target Test plan ? + * is Build enable to execution ? + * + * C. Platform + * do we need a platform ID in order to execute ? + * is a platform present on provided data ? + * does this platform belong to target Test plan ? + * + * D. Test case identity + * is target Test case part of Test plan ? + * + * + * Z. Other mandatory information + * We are not going to check for other mandatory info + * like: mandatory custom fields. (if we will be able in future to manage it) + */ + public function createTestCaseExecution(Request $request, Response $response) + { + $op = $this->getStdIDKO(); + + try { + $ex = json_decode($request->getBody()); + $util = $this->checkExecutionEnvironment($ex); + + // Complete missing propertie + if (! property_exists($ex, 'platformID')) { + $ex->platformID = 0; + } + + if (! property_exists($ex, 'executionType')) { + $ex->executionType = $this->cfg['tcase']['executionType']['auto']; + } + + // If we are here this means we can write execution status!!! + $ex->testerID = $this->userID; + foreach ($util as $prop => $value) { + $ex->$prop = $value; + } + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + + // This writes ONLY a test case level, not steps + $op['id'] = $this->tplanMgr->writeExecution($ex); + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * 'name' + * 'testProjectID' + * 'parentID' + * 'notes' + * 'order' + */ + public function createTestSuite(Request $request, Response $response) + { + $op = $this->getStdIDKO(); + try { + $item = json_decode($request->getBody()); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $op['id'] = $this->tsuiteMgr->createFromObject($item, + array( + 'doChecks' => true + )); + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * body will contain an array of objects + * that can be + * {'name': platform name} + * {'id': platform id} + * + * Check if done to understand if all platforms + * exist before doing any action + */ + public function addPlatformsToTestPlan(Request $request, Response $response, + $args) + { + $op = $this->getStdIDKO(); + $tplan_id = intval($args['tplan_id']); + try { + $plat2link = json_decode($request->getBody()); + + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $statusOK = true; + if (null == $plat2link || ! is_array($plat2link)) { + $statusOK = false; + $op['status'] = 'ko'; + $op['message'] = 'Bad Body'; + } + + if ($statusOK) { + // Validate Test plan existence. + // Get Test Project ID before doing anything + $getOpt = array( + 'output' => 'testPlanFields', + 'active' => 1, + 'testPlanFields' => 'id,testproject_id,is_public' + ); + + $testPlan = $this->tplanMgr->get_by_id($tplan_id, $getOpt); + $statusOK = ! is_null($testPlan); + + if ($statusOK) { + $tproject_id = $testPlan['testproject_id']; + } else { + $op['status'] = 'ko'; + $op['message'] = 'Invalid Test Plan ID'; + } + } + + if ($statusOK) { + // Get all test project platforms, + // that can be used on TEST PLAN + // (enabled on execution) + // + // then validate + $platMgr = new tlPlatform($this->db, $tproject_id); + $platDomain = $platMgr->getAll(); + $idToLink = []; + $op['message'] = []; + + foreach ($plat2link as $accessObj) { + $checkOK = false; + if (property_exists($accessObj, 'name')) { + $needle = trim($accessObj->name); + foreach ($platDomain as $target) { + if ($target['name'] == $needle) { + $checkOK = true; + $idToLink[$target['id']] = $target['id']; + } + } + $statusOK = $statusOK && $checkOK; + if (! $checkOK) { + $op['message'][] = " WARNING! - Platform with name:" . + $needle . " Reason: does not exist " . + " or is not enabled for execution"; + } + } + + if (property_exists($accessObj, 'id')) { + $needle = intval($accessObj->id); + foreach ($platDomain as $target) { + if ($target['id'] == $needle) { + $checkOK = true; + $idToLink[$target['id']] = $target['id']; + } + } + $statusOK = $statusOK && $checkOK; + if (! $checkOK) { + $op['message'][] = "Platform with id:" . $needle . + " does not exist"; + } + } + } + + $op['status'] = $statusOK; + } + + if ($statusOK) { + $p2link = []; + // Finally link platforms, if not linked yet + $gOpt = array( + 'outputFormat' => 'mapAccessByID' + ); + $linked = (array) $platMgr->getLinkedToTestplan($tplan_id, $gOpt); + foreach ($idToLink as $plat_id) { + if (! isset($linked[$plat_id])) { + $p2link[$plat_id] = $plat_id; + } + } + if (! empty($p2link)) { + $platMgr->linkToTestplan($p2link, $tplan_id); + } + } + + if ($op['status']) { + $op['message'] = 'ok'; + } + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * "name" + * "testSuite": {"id": xxx} + * "testProject" : {"id": xxx} or {"prefix": yyy} + * + * One of the following + * "authorLogin" + * "authorID" + * ------------------------------------------ + * + * "summary" can be a string or an array of strings + * "preconditions" can be a string or an array of strings + * + * "importance": {"name": "verbose"} + * - see const.inc.php for domain + * "executionType": {"name": "verbose"} + * - see ... for domain + * "order" + * + * "estimatedExecutionDuration" // to be implemented + * + * "steps": array of objects + * IMPORTANT NOTICE: actions and expected_results + * Can be string or array of strings + * [ + * { "step_number":1, + * "actions": "red", + * "expected_results": "#f00", + * "execution_type":1 + * }, + * { "step_number":12, + * "actions": "red12", + * "expected_results": "#f00", + * "execution_type":2 + * } + * ] + */ + public function createTestCase(Request $request, Response $response) + { + $op = $this->getStdIDKO(); + try { + + // It will be important to document WHY!!! + // AFAIK some issues with json_decode() + // https://stackoverflow.com/questions/34486346/new-lines-and-tabs-in-json-decode-php-7 + $body = str_replace("\n", '', $request->getBody()); + $item = json_decode($body); + + if (null == $item) { + $this->byeHTTP500(__METHOD__); + } + + // create obj with standard properties + $op['message'] = 'After buildTestCaseObj() >> ' . + $tcase = $this->buildTestCaseObj($item); + $this->checkRelatives($tcase); + + $ou = $this->tcaseMgr->createFromObject($tcase); + $op = array( + 'status' => 'ok', + 'message' => 'ok', + 'id' => - 1 + ); + if (($op['id'] = $ou['id']) <= 0) { + $op['status'] = 'ko'; + $op['message'] = $ou['msg']; + $response = new Response(); + $response->withStatus(409); + } + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + if ($op['message'] == 'ko') { + $op['message'] = __METHOD__ . ' >> '; + } + $op['message'] .= $this->msgFromException($e); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /** + * "keyword" + * "notes" + * "testProject": {"prefix":"APR"} + */ + public function createKeyword(Request $request, Response $response) + { + $op = $this->getStdIDKO(); + + try { + $body = $request->getBody(); + $bigString = $body->getContents(); + + $ba = explode('",', $bigString); + $needle = '"notes":'; + foreach ($ba as $pa => $ma) { + if (strpos($ma, $needle) !== false) { + $zz = explode($needle, $ma); + $ba[$pa] = $needle . str_replace("\n", "?^§", $zz[1]); + } + $ba[$pa] .= '",'; + } + + $bigString = implode("", $ba); + $bigString = trim($bigString, '",'); + $item = json_decode($bigString); + if (null == $item) { + $this->byeHTTP500(__METHOD__); + } + + if (property_exists($item, 'notes')) { + $item->notes = str_replace("?^§", "\n", $item->notes); + + // try to remove useless spaces + $item->notes = str_replace(" ", "", $item->notes); + } + + // create obj with standard properties + $pfx = $item->testProject->prefix; + $pid = $this->tprojectMgr->get_by_prefix((string) $pfx); + if (null == $pid) { + $op['status'] = 'ko'; + $op['message'] = "Can't get test project ID"; + } else { + $pid = $pid['id']; + $ou = $this->tprojectMgr->addKeyword($pid, $item->keyword, + $item->notes); + $op = array( + 'status' => 'ok', + 'message' => 'ok' + ); + $op['id'] = $ou['id']; + if ($ou['status'] < 0) { + $op['status'] = 'ko'; + $op['message'] = $ou['msg']; + } + } + } catch (Exception $e) { + $response = new Response(); + $response->withStatus(500); + $op['message'] = __METHOD__ . ' >> ' . $this->msgFromException($e); + } + + $payload = json_encode($op); + $response->getBody()->write($payload); + return $response; + } + + /* ************************************ */ + /* Helpers */ + /* ************************************ */ + private function buildPropMapping() + { + return array( + 'name' => 'name', + 'tplan_id' => 'tplan_id', + 'release_date' => 'release_date', + 'releasedate' => 'release_date', + 'active' => 'is_active', + 'is_active' => 'is_active', + 'notes' => 'notes', + 'commit_id' => 'commit_id', + 'tag' => 'tag', + 'branch' => 'branch', + 'release_candidate' => 'release_candidate', + 'is_open' => 'is_open', + 'copytestersfrombuild' => 'copytestersfrombuild', + 'copy_testers_from_build' => 'copytestersfrombuild' + ); + } + + /** + */ + private function buildTestCaseObj(&$obj) + { + if (is_null($obj)) { + throw new Exception("Fatal Error - " . __METHOD__ . " arg is NULL"); + } + + $tcase = new stdClass(); + $tcase->authorID = - 1; + $tcase->steps = null; + $tcase->testProjectID = - 1; + + $accessKey = array(); + $isOK = true; + + // Knowing author is critic, because rights are related to user. + // Another important thing: + // do we need to check that author when provided, has rights to do + // requested action? + // If we do not do this check, we will find in test cases created + // by people that do not have rights. + // May be is time to add a field that provide info about source of action + // GUI, API + if (property_exists($obj, 'author') && + property_exists($obj->author, 'login') || + property_exists($obj->author, 'id')) { + $tcase->authorID = $this->getUserIDByAttr($obj->author); + } + + // Last resort: get author from credentials use to make the call. + // no error message returned. + if ($tcase->authorID <= 0) { + $tcase->authorID = $this->userID; + } + + // Mandatory attributes + $ma = array( + 'name' => null, + 'testProject' => array( + 'id', + 'prefix' + ), + 'testSuite' => array( + 'id' + ) + ); + + foreach ($ma as $key => $dummy) { + if (! ($isOK = $isOK && property_exists($obj, $key))) { + throw new Exception("Missing Attribute: {$key} "); + } + } + + foreach ($ma as $key => $attr) { + if (! is_null($attr)) { + $attrOK = false; + foreach ($attr as $ak) { + $accessKey[$key][$ak] = property_exists($obj->$key, $ak); + $attrOK = $attrOK || $accessKey[$key][$ak]; + } + + if (! $attrOK) { + $msg = "Attribute: {$key} mandatory key ("; + if (count($attr) > 1) { + $msg .= "one of set: "; + } + $msg .= implode('/', $attr) . ") is missing"; + throw new Exception($msg); + } + } + } + + $tcase->name = trim($obj->name); + $tcase->testSuiteID = intval($obj->testSuite->id); + + $gOpt = array( + 'output' => 'array_of_map', + 'field_set' => 'prefix', + 'add_issuetracker' => false, + 'add_reqmgrsystem' => false + ); + + $msg = "Test project with "; + if ($accessKey['testProject']['id']) { + $safeID = intval($obj->testProject->id); + $gFilters = array( + 'id' => array( + 'op' => '=', + 'value' => $safeID + ) + ); + $msg .= "id={$safeID} "; + } + + if ($accessKey['testProject']['prefix']) { + $gFilters = array( + 'prefix' => array( + 'op' => '=', + 'value' => trim($obj->testProject->prefix) + ) + ); + $msg .= "prefix={$obj->testProject->prefix} "; + } + + $info = $this->tprojectMgr->get_accessible_for_user($this->userID, $gOpt, + $gFilters); + + if (is_null($info)) { + $msg .= "does not exist or you have no rights to use it"; + throw new Exception($msg, 999); + } + + $tcase->testProjectID = intval($info[0]['id']); + + // summary & preconditions + // if type is array -> generate string in this way + // - add
    +        // - concact the elements with "\n"
    +        // - add 
    + $sk2d = array( + 'summary' => '', + 'preconditions' => '' + ); + foreach ($sk2d as $key => $value) { + if (is_array($tcase->$key)) { + $tcase->$key = "
    " . implode("\n", $tcase->$key) . "
    "; + } + } + + // these are objects with name as property. + $tcfg = $this->cfg['tcase']; + $ck2d = array( + 'executionType' => $tcfg['executionType']['manual'], + 'importance' => $tcfg['defaults']['importance'], + 'status' => $tcfg['status']['draft'] + ); + + foreach ($ck2d as $prop => $defa) { + $tcase->$prop = property_exists($obj, $prop) ? $tcfg[$prop][$obj->$prop->name] : $defa; + } + + if (property_exists($obj, 'steps')) { + $tcase->steps = []; + $sk2d = array( + 'actions' => '', + 'expected_results' => '' + ); + foreach ($obj->steps as $stepObj) { + foreach ($sk2d as $key => $value) { + if (is_array($stepObj->$key)) { + $stepObj->$key = "
    " . implode("\n", $stepObj->$key) .
    +                            "
    "; + } + } + $tcase->steps[] = $stepObj; + } + } + + return $tcase; + } + + /** + */ + private function checkExecutionEnvironment($ex) + { + + // no platform + $platform = 0; + + // Test plan ID exists and is ACTIVE + $msg = 'invalid Test plan ID'; + $getOpt = array( + 'output' => 'testPlanFields', + 'active' => 1, + 'testPlanFields' => 'id,testproject_id,is_public' + ); + $status_ok = ! is_null( + $testPlan = $this->tplanMgr->get_by_id($ex->testPlanID, $getOpt)); + + if ($status_ok) { + // user has right to execute on Test plan ID + // hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) + $msg = 'user has no right to execute'; + $status_ok = $this->user->hasRight($this->db, 'testplan_execute', + $testPlan['testproject_id'], $ex->testPlanID, true); + } + + if ($status_ok) { + // Check if couple (buildID,testPlanID) is valid + $msg = '(buildID,testPlanID) couple is not valid'; + $getOpt = array( + 'fields' => 'id,active,is_open', + 'buildID' => $ex->buildID, + 'orderBy' => null + ); + $status_ok = ! is_null( + $build = $this->tplanMgr->get_builds($ex->testPlanID, null, null, + $getOpt)); + + if ($status_ok) { + // now check is execution can be done againts this build + $msg = 'Build is not active and/or closed => execution can not be done'; + $status_ok = $build[$ex->buildID]['active'] && + $build[$ex->buildID]['is_open']; + } + } + + if ($status_ok && property_exists($ex, 'platformID')) { + // Get Test plan platforms + $platform = $ex->platformID; + + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'addIfNull' => false + ); + $platformSet = $this->tplanMgr->getPlatforms($ex->testPlanID, + $getOpt); + + if (! ($hasPlatforms = ! is_null($platformSet)) && $platform != 0) { + $status_ok = false; + $msg = 'You can not execute against a platform, because Test plan has no platforms'; + } + + if ($status_ok && $hasPlatforms) { + if ($platform == 0) { + $status_ok = false; + $msg = 'Test plan has platforms, you need to provide one in order to execute'; + } elseif (! isset($platformSet[$platform])) { + $status_ok = false; + $msg = '(platform,test plan) couple is not valid'; + } + } + } + + if ($status_ok) { + // Test case check + $msg = 'Test case does not exist'; + + $tcaseID = $this->tcaseMgr->getInternalID($ex->testCaseExternalID); + if ($status_ok = ($tcaseID > 0)) { + $msg = 'Test case doesn not belong to right test project'; + $testCaseTestProject = $this->tcaseMgr->getTestProjectFromTestCase( + $tcaseID, 0); + $status_ok = ($testCaseTestProject == $testPlan['testproject_id']); + } + + if ($status_ok) { + // Does this test case is linked to test plan ? + $msg = 'Test case is not linked to (test plan,platform) => can not be executed'; + $getFilters = array( + 'testplan_id' => $ex->testPlanID, + 'platform_id' => $platform + ); + + $getOpt = array( + 'output' => 'simple' + ); + $links = $this->tcaseMgr->get_linked_versions($tcaseID, + $getFilters, $getOpt); + $status_ok = ! is_null($links); + } + } + + if ($status_ok) { + // status code is OK ? + $msg = 'not run status is not a valid execution status (can not be written to DB)'; + $status_ok = ($ex->statusCode != + $this->cfg['exec']['statusCode']['not_run']); + + if ($status_ok) { + $msg = 'Requested execution status is not configured on TestLink'; + $status_ok = isset( + $this->cfg['exec']['codeStatus'][$ex->statusCode]); + } + } + + if ($status_ok) { + $ret = new stdClass(); + $ret->testProjectID = $testPlan['testproject_id']; + $ret->testCaseVersionID = key($links); + $ret->testCaseVersionNumber = $links[$ret->testCaseVersionID][$ex->testPlanID][$platform]['version']; + } + + if (! $status_ok) { + throw new Exception($msg); + } + + return $ret; + } + + /** + */ + private function checkRelatives($ctx) + { + $testProjectID = $ctx->testProjectID; + $testSuiteID = $ctx->testSuiteID; + if ($testProjectID <= 0) { + throw new Exception("Test Project ID is invalid (<=0)"); + } + + if ($testSuiteID <= 0) { + throw new Exception("Test Suite ID is invalid (<=0)"); + } + + $pinfo = $this->tprojectMgr->get_by_id($testProjectID); + if (is_null($pinfo)) { + throw new Exception("Test Project ID is invalid (does not exist)"); + } + + $pinfo = $this->tsuiteMgr->get_by_id($testSuiteID); + if (is_null($pinfo)) { + throw new Exception("Test Suite ID is invalid (does not exist)"); + } + + if ($testProjectID != + $this->tsuiteMgr->getTestProjectFromTestSuite($testSuiteID, + $testSuiteID)) { + throw new Exception("Test Suite does not belong to Test Project ID"); + } + } + + /** + * checks if a user has requested right on test project, test plan pair. + * + * @param string $rightToCheck + * one of the rights defined in rights table + * @param boolean $checkPublicPrivateAttr + * (optional) + * @param array $context + * (optional) + * keys tproject_id,tplan_id (both are also optional) + * + * @return boolean + * @access protected + * + * + */ + protected function userHasRight($rightToCheck, + $checkPublicPrivateAttr = false, $context = null) + { + $status_ok = true; + + // for global rights context is NULL + if (is_null($context)) { + $tproject_id = 0; + $tplan_id = null; + } else { + $tproject_id = intval( + isset($context['tproject_id']) ? $context['tproject_id'] : 0); + + $tplan_id = null; + if (isset($context['tplan_id'])) { + $tplan_id = intval($context['tplan_id']); + } + + if ($tproject_id <= 0 && ! is_null($tplan_id)) { + // get test project from test plan + $dummy = $this->tplanMgr->get_by_id($tplanid, + array( + 'output' => 'minimun' + )); + $tproject_id = intval($dummy['tproject_id']); + } + } + + if (! $this->user->hasRight($this->db, $rightToCheck, $tproject_id, + $tplan_id, $checkPublicPrivateAttr)) { + $status_ok = false; + } + return $status_ok; + } + + /** + */ + private function getStdOp() + { + return array( + 'status' => 'ok', + 'message' => 'ok', + 'items' => null + ); + } + + /** + */ + private function getStdIDKO() + { + return array( + 'status' => 'ko', + 'message' => 'ko', + 'id' => - 1 + ); + } + + /** + */ + private function byeHTTP500($msg = null) + { + $op = array(); + if (null == $msg) { + $msg = 'TestLink Fatal Error - Malformed Request Body - ' . + ' json_decode() issue'; + } + $op['details'][] = sprintf($msg); + + $response = new Response(); + $response->getBody()->write('Malformed Request Body'); + $response->withStatus(500); + return $response; + } + + /** + */ + private function msgFromException($e) + { + return $e->getMessage() . ' - offending line number: ' . $e->getLine(); + } +} diff --git a/lib/api/rest/v3/core/routes.php b/lib/api/rest/v3/core/routes.php new file mode 100644 index 0000000000..ccea4dfa04 --- /dev/null +++ b/lib/api/rest/v3/core/routes.php @@ -0,0 +1,98 @@ +get('/',World::class . ':hello'); + + // using array(), was the way in Slim3 and + // still seems valid + $app->get('/whoAmI', array( + $app->restApi, + 'whoAmI' + )); + + $app->get('/testprojects', array( + $app->restApi, + 'testprojects' + )); + $app->get('/testprojects/{id}', array( + $app->restApi, + 'testprojects' + )); + + $app->get('/testprojects/{id}/testcases', + array( + $app->restApi, + 'getProjectTestCases' + )); + $app->get('/testprojects/{mixedID}/testplans', + array( + $app->restApi, + 'getProjectTestPlans' + )); + + $app->get('/testplans/{tplanApiKey}/builds', + array( + $app->restApi, + 'getPlanBuilds' + )); + + $app->post('/executions', array( + $app->restApi, + 'createTestCaseExecution' + )); + + $app->post('/builds', array( + $app->restApi, + 'createBuild' + )); + + $app->post('/keywords', array( + $app->restApi, + 'createKeyword' + )); + + $app->post('/testcases', array( + $app->restApi, + 'createTestCase' + )); + + $app->post('/testplans', array( + $app->restApi, + 'createTestPlan' + )); + + $app->post('/testprojects', array( + $app->restApi, + 'createTestProject' + )); + + $app->post('/testsuites', array( + $app->restApi, + 'createTestSuite' + )); + + // Update Routes + // Following advice from + // https://restfulapi.net/rest-put-vs-post/ + // + $app->put('/builds/{id}', array( + $app->restApi, + 'updateBuild' + )); + + $app->put('/testplans/{id}', array( + $app->restApi, + 'updateTestPlan' + )); + + $app->put('/testplans/{tplan_id}/platforms', + array( + $app->restApi, + 'addPlatformsToTestPlan' + )); +}; diff --git a/lib/api/rest/v3/custom/api/RestApiCustomExample.class.php b/lib/api/rest/v3/custom/api/RestApiCustomExample.class.php new file mode 100644 index 0000000000..ee3e1e5b97 --- /dev/null +++ b/lib/api/rest/v3/custom/api/RestApiCustomExample.class.php @@ -0,0 +1,53 @@ + + * @package TestLink + */ +class RestApiCustomExample extends RestApi +{ + public static $version = "1.0"; + + /** + */ + public function __construct() + { + $this->db = new database(DB_TYPE); + $this->db->db->SetFetchMode(ADODB_FETCH_ASSOC); + doDBConnect($this->db,database::ONERROREXIT); + } + + + + /** + * + */ + public function whoAmI(Request $request, Response $response, $args) + { + $msg = json_encode(array('name' => __CLASS__ . ' : You have called Get Route /whoAmI')); + $response->getBody()->write($msg); + return $response; + } + +} diff --git a/lib/api/rest/v3/custom/routes/routesCustomExample.php b/lib/api/rest/v3/custom/routes/routesCustomExample.php new file mode 100644 index 0000000000..e72936a99f --- /dev/null +++ b/lib/api/rest/v3/custom/routes/routesCustomExample.php @@ -0,0 +1,16 @@ +get('/CustomExample/whoAmI', + array($app->restApiCustomExample,'whoAmI')); + +}; diff --git a/lib/api/rest/v3/index.php b/lib/api/rest/v3/index.php new file mode 100644 index 0000000000..92990102c9 --- /dev/null +++ b/lib/api/rest/v3/index.php @@ -0,0 +1,94 @@ +basePath; +$app->setBasePath($basePath); + +$app->restApi = new RestApi(); + + +/** + * Load Custom API - Begin + * + */ +clearstatcache(); +$APIExtensions = []; +$where = './custom/api/'; +if (is_dir($where)) { + $itera = new DirectoryIterator($where); + foreach ($itera as $fileinfo) { + if ($fileinfo->isFile()) { + $who = $fileinfo->getFilename(); + require_once $where . $who; + + // generate class name + $className = str_replace('.class.php', '', $who); + $APIExtensions[$className] = $className; + } + } + + // Register Custom API + foreach ($APIExtensions as $class) { + $instance = lcfirst($class); + $app->$instance = new $class(); + } +} + +// Register CUSTOM routes +clearstatcache(); +$where = './custom/routes/'; +if (is_dir($where)) { + $itera = new DirectoryIterator($where); + foreach ($itera as $fileinfo) { + if ($fileinfo->isFile()) { + $who = $fileinfo->getFilename(); + $customRoutes = require_once $where . $who; + $customRoutes($app); + } + } +} +// * Load Custom API - END + +// Register Standard routes +$routes = require_once './core/routes.php'; +$routes($app); + +// Middleware +$app->add(array($app->restApi,'authenticate')); + +// https://stackoverflow.com/questions/37255635/ +// php-slim-framework-v3-set- +// global-content-type-for-responses/37255946 +$app->add(array($app->restApi,'setContentTypeJSON')); + +$app->run(); diff --git a/lib/api/rest/v3/test/hippie-createTC-example.sh b/lib/api/rest/v3/test/hippie-createTC-example.sh new file mode 100755 index 0000000000..edfdfff3f8 --- /dev/null +++ b/lib/api/rest/v3/test/hippie-createTC-example.sh @@ -0,0 +1,5 @@ +URI=http://localhost/development/github/testlink-code/lib/api/rest/v2/ +AUTH=c94048220527a3d038db5c19e1156c08:pinkfloyd +ACTION=testcases +JSON=x-with-cr-AL.json +http POST $URI$ACTION --auth $AUTH < $JSON diff --git a/lib/api/rest/v3/test/httpie-addPlatforms.sh b/lib/api/rest/v3/test/httpie-addPlatforms.sh new file mode 100755 index 0000000000..603a015a5d --- /dev/null +++ b/lib/api/rest/v3/test/httpie-addPlatforms.sh @@ -0,0 +1,6 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans/64/platforms + +JSON=./json/addPlatforms.json +http PUT $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-createBuild.sh b/lib/api/rest/v3/test/httpie-createBuild.sh new file mode 100755 index 0000000000..eabb64c8ae --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createBuild.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=builds +JSON=./json/createBuild.json +http POST $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-createKeyword.sh b/lib/api/rest/v3/test/httpie-createKeyword.sh new file mode 100755 index 0000000000..798c1bd07e --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createKeyword.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=keywords +JSON=./json/createKeyword.json +http POST $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-createTestCase.sh b/lib/api/rest/v3/test/httpie-createTestCase.sh new file mode 100755 index 0000000000..9295963e55 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCase.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testcases +JSON=./json/testcases/createTestCase.json +http POST $URI$ACTION $APIKEY < $JSON \ No newline at end of file diff --git a/lib/api/rest/v3/test/httpie-createTestCaseB.sh b/lib/api/rest/v3/test/httpie-createTestCaseB.sh new file mode 100755 index 0000000000..08c36c340e --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCaseB.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testcases +JSON=./json/testcases/createTestCaseB.json +http POST $URI$ACTION $APIKEY < $JSON \ No newline at end of file diff --git a/lib/api/rest/v3/test/httpie-createTestCaseExecution.sh b/lib/api/rest/v3/test/httpie-createTestCaseExecution.sh new file mode 100755 index 0000000000..ffad218a60 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCaseExecution.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=executions +JSON=./json/createTestCaseExecution.json +http POST $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-createTestCaseWithSteps.sh b/lib/api/rest/v3/test/httpie-createTestCaseWithSteps.sh new file mode 100755 index 0000000000..8b28f04508 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCaseWithSteps.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testcases +JSON=./json/testcases/createTestCaseWithSteps.json +http POST $URI$ACTION $APIKEY < $JSON \ No newline at end of file diff --git a/lib/api/rest/v3/test/httpie-createTestCaseXL5.sh b/lib/api/rest/v3/test/httpie-createTestCaseXL5.sh new file mode 100755 index 0000000000..bd2e092845 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCaseXL5.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testcases +JSON=./json/testcases/createTestCaseXL5.json +http POST $URI$ACTION $APIKEY < $JSON \ No newline at end of file diff --git a/lib/api/rest/v3/test/httpie-createTestCaseXL5B.sh b/lib/api/rest/v3/test/httpie-createTestCaseXL5B.sh new file mode 100755 index 0000000000..1658a81930 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCaseXL5B.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testcases +JSON=./json/testcases/createTestCaseXL5B.json +http POST $URI$ACTION $APIKEY < $JSON \ No newline at end of file diff --git a/lib/api/rest/v3/test/httpie-createTestCaseXL5C.sh b/lib/api/rest/v3/test/httpie-createTestCaseXL5C.sh new file mode 100755 index 0000000000..134fa0f783 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCaseXL5C.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testcases +JSON=./json/testcases/createTestCaseXL5C.json +http POST $URI$ACTION $APIKEY < $JSON \ No newline at end of file diff --git a/lib/api/rest/v3/test/httpie-createTestCaseXL5D.sh b/lib/api/rest/v3/test/httpie-createTestCaseXL5D.sh new file mode 100755 index 0000000000..00bbf244bd --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestCaseXL5D.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testcases +JSON=./json/testcases/createTestCaseXL5D.json +http POST $URI$ACTION $APIKEY < $JSON \ No newline at end of file diff --git a/lib/api/rest/v3/test/httpie-createTestPlan.sh b/lib/api/rest/v3/test/httpie-createTestPlan.sh new file mode 100755 index 0000000000..3399ba95d5 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestPlan.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans +JSON=./json/createTestPlan.json +http POST $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-createTestProject.sh b/lib/api/rest/v3/test/httpie-createTestProject.sh new file mode 100755 index 0000000000..a6221d28a8 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestProject.sh @@ -0,0 +1,7 @@ +#URI=http://testlink-2.0.0/lib/api/rest/v3/ +. ./setURI.sh +echo $URI +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testprojects +JSON=./json/createTestProject.json +http POST $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-createTestSuite.sh b/lib/api/rest/v3/test/httpie-createTestSuite.sh new file mode 100755 index 0000000000..fac80b6b72 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTestSuite.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testsuites +JSON=./json/createTestSuite.json +http POST $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-createTopTestSuite.sh b/lib/api/rest/v3/test/httpie-createTopTestSuite.sh new file mode 100755 index 0000000000..ad8abec735 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-createTopTestSuite.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testsuites +JSON=./json/createTopTestSuite.json +http POST $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-getBuildsInTestPlan.sh b/lib/api/rest/v3/test/httpie-getBuildsInTestPlan.sh new file mode 100755 index 0000000000..541f8890c6 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-getBuildsInTestPlan.sh @@ -0,0 +1,4 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans/f6635c2e9153a3b1eb8e8781525ab3277f66d1c847f038167f0ce7779dd908db/builds +http GET $URI$ACTION $APIKEY diff --git a/lib/api/rest/v3/test/httpie-getOneTestProject.sh b/lib/api/rest/v3/test/httpie-getOneTestProject.sh new file mode 100755 index 0000000000..70409ad7b4 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-getOneTestProject.sh @@ -0,0 +1,4 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testprojects/12 +http GET $URI$ACTION $APIKEY diff --git a/lib/api/rest/v3/test/httpie-getTestCasesInTestProject.sh b/lib/api/rest/v3/test/httpie-getTestCasesInTestProject.sh new file mode 100755 index 0000000000..28e1a79fb3 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-getTestCasesInTestProject.sh @@ -0,0 +1,4 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testprojects/12/testcases +http GET $URI$ACTION $APIKEY diff --git a/lib/api/rest/v3/test/httpie-getTestPlansInTestProject.sh b/lib/api/rest/v3/test/httpie-getTestPlansInTestProject.sh new file mode 100755 index 0000000000..c842290de7 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-getTestPlansInTestProject.sh @@ -0,0 +1,4 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testprojects/12/testplans +http GET $URI$ACTION $APIKEY diff --git a/lib/api/rest/v3/test/httpie-getTestProjects.sh b/lib/api/rest/v3/test/httpie-getTestProjects.sh new file mode 100755 index 0000000000..e489262669 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-getTestProjects.sh @@ -0,0 +1,4 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testprojects +http GET $URI$ACTION $APIKEY diff --git a/lib/api/rest/v3/test/httpie-restApiCustomExample-whoAmI.sh b/lib/api/rest/v3/test/httpie-restApiCustomExample-whoAmI.sh new file mode 100755 index 0000000000..ea54c7c351 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-restApiCustomExample-whoAmI.sh @@ -0,0 +1,10 @@ +. ./setURI.sh + +# 43e9294d180949c79da7aa7bf215360a +#APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +#APIKEY="Apikey:43e9294d180949c79da7aa7bf215360a" +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" + +#ACTION=whoCustomExampleAmI +ACTION=CustomExample/whoAmI +http GET $URI$ACTION $APIKEY diff --git a/lib/api/rest/v3/test/httpie-setTestPlanActive.sh b/lib/api/rest/v3/test/httpie-setTestPlanActive.sh new file mode 100755 index 0000000000..568cae95cf --- /dev/null +++ b/lib/api/rest/v3/test/httpie-setTestPlanActive.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans/61 +JSON=./json/setTestPlanActive.json +http PUT $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-setTestPlanInactive.sh b/lib/api/rest/v3/test/httpie-setTestPlanInactive.sh new file mode 100755 index 0000000000..4135e216c3 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-setTestPlanInactive.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans/61 +JSON=./json/setTestPlanInactive.json +http PUT $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-setTestPlanNotes.sh b/lib/api/rest/v3/test/httpie-setTestPlanNotes.sh new file mode 100755 index 0000000000..b5eef3a7f0 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-setTestPlanNotes.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans/61 +JSON=./json/setTestPlanNotes.json +http PUT $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-setTestPlanPrivate.sh b/lib/api/rest/v3/test/httpie-setTestPlanPrivate.sh new file mode 100755 index 0000000000..d906f65a94 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-setTestPlanPrivate.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans/61 +JSON=./json/setTestPlanPrivate.json +http PUT $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-updateBuild.sh b/lib/api/rest/v3/test/httpie-updateBuild.sh new file mode 100755 index 0000000000..2cd7e1df2e --- /dev/null +++ b/lib/api/rest/v3/test/httpie-updateBuild.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=builds/12 +JSON=./json/updateBuild.json +http PUT $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-updateTestPlan.sh b/lib/api/rest/v3/test/httpie-updateTestPlan.sh new file mode 100755 index 0000000000..15bf5a93e0 --- /dev/null +++ b/lib/api/rest/v3/test/httpie-updateTestPlan.sh @@ -0,0 +1,5 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=testplans/61 +JSON=./json/updateTestPlan.json +http PUT $URI$ACTION $APIKEY < $JSON diff --git a/lib/api/rest/v3/test/httpie-whoAmI.sh b/lib/api/rest/v3/test/httpie-whoAmI.sh new file mode 100755 index 0000000000..cfd991a94c --- /dev/null +++ b/lib/api/rest/v3/test/httpie-whoAmI.sh @@ -0,0 +1,4 @@ +. ./setURI.sh +APIKEY="Apikey:c94048220527a3d038db5c19e1156c08" +ACTION=whoAmI +http GET $URI$ACTION $APIKEY diff --git a/lib/api/rest/v3/test/json/README b/lib/api/rest/v3/test/json/README new file mode 100644 index 0000000000..aadca5b36b --- /dev/null +++ b/lib/api/rest/v3/test/json/README @@ -0,0 +1,2 @@ +Files present on this directory can be used to test +TestLink REST API using curl or httpie diff --git a/lib/api/rest/v3/test/json/addPlatforms.json b/lib/api/rest/v3/test/json/addPlatforms.json new file mode 100644 index 0000000000..930cf0be3a --- /dev/null +++ b/lib/api/rest/v3/test/json/addPlatforms.json @@ -0,0 +1,3 @@ +[{"name":"Formazione CF"}, + {"name":"Prima"} +] \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createBuild.json b/lib/api/rest/v3/test/json/createBuild.json new file mode 100644 index 0000000000..372f2492dd --- /dev/null +++ b/lib/api/rest/v3/test/json/createBuild.json @@ -0,0 +1,6 @@ +{"name":"Build me!", + "testplan":7, + "notes":"This is a Good BUILD ", + "active":1, + "is_public":1 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createKeyword.json b/lib/api/rest/v3/test/json/createKeyword.json new file mode 100644 index 0000000000..14cf871e96 --- /dev/null +++ b/lib/api/rest/v3/test/json/createKeyword.json @@ -0,0 +1,8 @@ +{ + "keyword": "DAGAA K-999", + "notes": "This is a Keyword 2 + GAGA IS LOVE + OF + LIFE", + "testProject": {"prefix": "AL"} +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createTestCaseExecution.json b/lib/api/rest/v3/test/json/createTestCaseExecution.json new file mode 100644 index 0000000000..19862af4db --- /dev/null +++ b/lib/api/rest/v3/test/json/createTestCaseExecution.json @@ -0,0 +1,10 @@ +{ + "testPlanID":46, + "buildID":13, + "platformID":6, + "testCaseExternalID":"PWQ-2", + "notes":"This is an execution created via REST API", + "statusCode":"b", + "executionType":"1", + "executionTimeStampISO":"2013-04-27 12:09:00" +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createTestCaseExecutionWithSteps.json b/lib/api/rest/v3/test/json/createTestCaseExecutionWithSteps.json new file mode 100644 index 0000000000..8a07d56189 --- /dev/null +++ b/lib/api/rest/v3/test/json/createTestCaseExecutionWithSteps.json @@ -0,0 +1,22 @@ +{ + "testPlanID":46, + "buildID":13, + "platformID":6, + "testCaseExternalID":"PWQ-2", + "notes":"This is an execution created via REST API", + "statusCode":"b", + "executionType":"1", + "executionTimeStampISO":"2013-04-27 12:09:00", + "steps":[ + { + "stepNumber":1, + "notes":"This is an execution created via REST API", + "statusCode":"b" + }, + { + "stepNumber":12, + "notes":"This is an execution created via REST API", + "statusCode":"f" + } + ] +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createTestPlan.json b/lib/api/rest/v3/test/json/createTestPlan.json new file mode 100644 index 0000000000..08d0a4f09d --- /dev/null +++ b/lib/api/rest/v3/test/json/createTestPlan.json @@ -0,0 +1,7 @@ +{ + "name":"Test PLAN ALFA", + "testProjectID":45, + "notes":"This is notes for Test Case", + "active":1, + "is_public":1 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createTestProject.json b/lib/api/rest/v3/test/json/createTestProject.json new file mode 100644 index 0000000000..4586faefc9 --- /dev/null +++ b/lib/api/rest/v3/test/json/createTestProject.json @@ -0,0 +1,9 @@ +{"name":"FrenchToJD(month, day, year)", + "notes":"This is a FrenchToJD", + "color":"", + "prefix":"FTJD", + "active":1, + "is_public":1, + "options":{"requirementsEnabled":0,"testPriorityEnabled":1, + "automationEnabled":1,"inventoryEnabled":0} +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createTestSuite.json b/lib/api/rest/v3/test/json/createTestSuite.json new file mode 100644 index 0000000000..c9904bc4ad --- /dev/null +++ b/lib/api/rest/v3/test/json/createTestSuite.json @@ -0,0 +1,7 @@ +{ + "name":"TestSuite TS2200-B", + "testProjectID":1, + "parentID":62, + "notes":"This is my first LEVEL TS via REST", + "order":100 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/createTopTestSuite.json b/lib/api/rest/v3/test/json/createTopTestSuite.json new file mode 100644 index 0000000000..f4eb491ea9 --- /dev/null +++ b/lib/api/rest/v3/test/json/createTopTestSuite.json @@ -0,0 +1,7 @@ +{ + "name":"TestSuite TS100-B", + "testProjectID":1, + "parentID":1, + "notes":"This is my first TOP TestSuite via REST", + "order":100 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/setTestPlanActive.json b/lib/api/rest/v3/test/json/setTestPlanActive.json new file mode 100644 index 0000000000..d07a9a5db8 --- /dev/null +++ b/lib/api/rest/v3/test/json/setTestPlanActive.json @@ -0,0 +1 @@ +{"active":1} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/setTestPlanInactive.json b/lib/api/rest/v3/test/json/setTestPlanInactive.json new file mode 100644 index 0000000000..d3c1411b0a --- /dev/null +++ b/lib/api/rest/v3/test/json/setTestPlanInactive.json @@ -0,0 +1 @@ +{"active":0} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/setTestPlanNotes.json b/lib/api/rest/v3/test/json/setTestPlanNotes.json new file mode 100644 index 0000000000..cf7dd89b58 --- /dev/null +++ b/lib/api/rest/v3/test/json/setTestPlanNotes.json @@ -0,0 +1 @@ +{"notes":"What's up doc"} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/setTestPlanPrivate.json b/lib/api/rest/v3/test/json/setTestPlanPrivate.json new file mode 100644 index 0000000000..56fe598cc3 --- /dev/null +++ b/lib/api/rest/v3/test/json/setTestPlanPrivate.json @@ -0,0 +1 @@ +{"is_public":0} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCase.json b/lib/api/rest/v3/test/json/testcases/createTestCase.json new file mode 100644 index 0000000000..ad427f82ff --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCase.json @@ -0,0 +1,11 @@ +{ + "name":"Test Case ALFA", + "testSuite": {"id": 62}, + "testProject": {"id": 1}, + "summary":"This is a summary for Test Case", + "preconditions":"No preconditions", + "order":100, + "authorLogin": "admin", + "importance": {"name":"high"}, + "executionType": {"name":"automatic"} +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseB.json b/lib/api/rest/v3/test/json/testcases/createTestCaseB.json new file mode 100644 index 0000000000..f51bad1ee8 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseB.json @@ -0,0 +1,11 @@ +{ + "name":"Test Case BETA!!!", + "testSuite": {"id": 62}, + "testProject": {"id": 1}, + "summary":"This is a summary for Test Case", + "preconditions":"No preconditions", + "order":100, + "authorLogin": "admin", + "importance": {"name":"high"}, + "executionType": {"name":"automatic"} +} diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseUsingAttrID.json b/lib/api/rest/v3/test/json/testcases/createTestCaseUsingAttrID.json new file mode 100644 index 0000000000..f921bea607 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseUsingAttrID.json @@ -0,0 +1,16 @@ +{ + "name":"Test Case ALFA", + "summary":"This is a summary for Test Case", + "preconditions":"No preconditions", + "order":100, + + + "testProject":{"id":358}, + "testSuite":{"id":360}, + + "author":{"id":1}, + "importance":{"id":2}, + "executionType":{"id":1}, + "status":{"id":1}, + "estimatedExecutionTime":0 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseUsingAttrVerboseKey.json b/lib/api/rest/v3/test/json/testcases/createTestCaseUsingAttrVerboseKey.json new file mode 100644 index 0000000000..234e5c15bc --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseUsingAttrVerboseKey.json @@ -0,0 +1,15 @@ +{ + "name":"Test Case ALFA", + "summary":"This is a summary for Test Case", + "preconditions":"No preconditions", + "order":100, + + "testProject":{"prefix":"APR"}, + "testSuite":{"id":360}, + + "author":{"login":"Mr Spock"}, + "importance":{"name":"high"}, + "executionType":{"name":"manual"}, + "status":{"name":"draft"}, + "estimatedExecutionTime":0 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseWithSteps.json b/lib/api/rest/v3/test/json/testcases/createTestCaseWithSteps.json new file mode 100644 index 0000000000..a9baa2e568 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseWithSteps.json @@ -0,0 +1,23 @@ +{ + "name":"Test Case BETA has STEPS", + "testSuite": {"id": 62}, + "testProject": {"id": 1}, + "summary":"This is a summary for Test Case", + "preconditions":"No preconditions", + "order":100, + "authorLogin": "admin", + "importance": {"name":"high"}, + "executionType": {"name":"automatic"}, + "steps": [ + { "step_number":1, + "actions": "red", + "expected_results": "#f00", + "execution_type":1 + }, + { "step_number":12, + "actions": "red12", + "expected_results": "#f00", + "execution_type":2 + } + ] +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseXL5.json b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5.json new file mode 100644 index 0000000000..5c8f217e30 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5.json @@ -0,0 +1,11 @@ +{ + "name":"Test Case XL5-A", + "testSuite": {"id": 62}, + "testProject": {"id": 1}, + "summary": "This is a summary for Test Case second line line #3", + "preconditions":"No preconditions", + "order":100, + "authorLogin": "admin", + "importance": {"name":"high"}, + "executionType": {"name":"automatic"} +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseXL5B.json b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5B.json new file mode 100644 index 0000000000..2a19af5b41 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5B.json @@ -0,0 +1,14 @@ +{ + "name":"Test Case XL5-B", + "testSuite": {"id": 62}, + "testProject": {"id": 1}, + "summary": + "This is a summary for Test Case + second line + line #3", + "preconditions":"No preconditions", + "order":100, + "authorLogin": "admin", + "importance": {"name":"high"}, + "executionType": {"name":"automatic"} +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseXL5C.json b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5C.json new file mode 100644 index 0000000000..771a5811a4 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5C.json @@ -0,0 +1,14 @@ +{ + "name":"Test Case XL5-C", + "testSuite": {"id": 62}, + "testProject": {"id": 1}, + "summary": + "This is a summary for Test Case + second line + line #3", + "preconditions":"No preconditions", + "order":100, + "authorLogin": "admin", + "importance": {"name":"high"}, + "executionType": {"name":"automatic"} +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/createTestCaseXL5D.json b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5D.json new file mode 100644 index 0000000000..65d402aab0 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/createTestCaseXL5D.json @@ -0,0 +1,16 @@ +{ + "name":"Test Case XL5-D", + "testSuite": {"id": 62}, + "testProject": {"id": 1}, + "summary": + "This is a summary for Test Case
    + second line
    + line #3", + "preconditions":"No preconditions for this testcase. +
    If you use an array [] can be empty +
    If you need to use a object {}", + "order":100, + "authorLogin": "admin", + "importance": {"name":"high"}, + "executionType": {"name":"automatic"} +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/testCaseComplex.json b/lib/api/rest/v3/test/json/testcases/testCaseComplex.json new file mode 100644 index 0000000000..7e0d74e521 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/testCaseComplex.json @@ -0,0 +1,16 @@ +{ + "name":"Test Scenarios for Filter Criteria", + "testSuiteID":378,"testProjectID":358, + "preconditions":"No preconditions for this testcase. +
    If you use an array [] can be empty +
    If you need to use a object {}", + "order":100,"authorID":1,"importance":2,"executionType":1, + "summary": + "
    1. User should be able to filter results using all parameters on the page +
    2. Refine search functionality should load search page with all user selected search parameters +
    3. When there is at least one filter criteria is required to perform search operation, +
    make sure proper error message is displayed when user submits the page without selecting any filter criteria. +
    4. When at least one filter criteria selection is not compulsory user +
    should be able to submit page and default search criteria should get used to query results +
    5. Proper validation messages should be displayed for invalid values for filter criteria" +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/testcases/testCaseComplex2.json b/lib/api/rest/v3/test/json/testcases/testCaseComplex2.json new file mode 100644 index 0000000000..9d014d8724 --- /dev/null +++ b/lib/api/rest/v3/test/json/testcases/testCaseComplex2.json @@ -0,0 +1,18 @@ +{ + "name":"Test Scenarios for a Window", + "testSuiteID":378,"testProjectID":358, + "preconditions":"No preconditions for this testcase. +
    If you use an array [] can be empty +
    If you need to use a object {}", + "order":100,"authorID":1,"importance":2,"executionType":1, + "summary": + "
    1. Check if default window size is correct +
    2. Check if child window size is correct +
    3. Check if there is any field on page with default focus (in general, the focus should be set on first input field of the screen) +
    4. Check if child windows are getting closed on closing parent/opener window +
    5. If child window is opened, user should not be able to use or update any field on background or parent window +
    6. Check window minimize, maximize and close functionality +
    7. Check if window is re-sizable +
    8. Check scroll bar functionality for parent and child windows +
    9. Check cancel button functionality for child window" +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/updateBuild.json b/lib/api/rest/v3/test/json/updateBuild.json new file mode 100644 index 0000000000..2e791684ee --- /dev/null +++ b/lib/api/rest/v3/test/json/updateBuild.json @@ -0,0 +1,6 @@ +{"name":"Build me!", + "testplan":7, + "notes":"I'm changing the notes,This is a Good BUILD ", + "active":1, + "is_public":1 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/json/updateTestPlan.json b/lib/api/rest/v3/test/json/updateTestPlan.json new file mode 100644 index 0000000000..7407559923 --- /dev/null +++ b/lib/api/rest/v3/test/json/updateTestPlan.json @@ -0,0 +1,6 @@ +{ + "testProjectID":45, + "notes":"This is notes for httpie-createTestPlan.sh", + "active":1, + "is_public":1 +} \ No newline at end of file diff --git a/lib/api/rest/v3/test/setURI.sh b/lib/api/rest/v3/test/setURI.sh new file mode 100755 index 0000000000..344a6c82d8 --- /dev/null +++ b/lib/api/rest/v3/test/setURI.sh @@ -0,0 +1 @@ +URI=http://testlink-dev/lib/api/rest/v3/ \ No newline at end of file diff --git a/lib/api/rest/v3/util/sublime_text_rester/README.md b/lib/api/rest/v3/util/sublime_text_rester/README.md new file mode 100644 index 0000000000..e184c56ff2 --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/README.md @@ -0,0 +1,32 @@ +# Using Sublime Text with RESTer plugin to access TestLink REST API +The files present in this folder are examples that can be run inside +the Sublime Text editor using https://github.com/pjdietz/rester-sublime-http-client + +This can be a good approach to involve the developers in test case design +allowing them to write simple tests without leaving the editor. + +I've tried with Pretty JSON plugin to improve the layout of the response text and worked OK!! + +![./media/sublime-rester.png](./media/sublime-rester.png) + + +**Notes if you run Sublime 3 and Sublime 4 side by side** +[side by side](https://www.sublimetext.com/docs/side_by_side.html) +Because I did my tests using the same data directory for both versions and Pretty JSON +is not compatible with Sublime 4 the formatting did not work. +But when using Sublime 3 everything was OK!!. + +# Notes about using Visual Studio Code with REST Client +https://marketplace.visualstudio.com/items?itemName=humao.rest-client +It works also like a charm, response is automatically JSON prettified + +![./media/vscode-rest-client.png](./media/vscode-rest-client.png) + + +# Notes regarding authentication token +You will see in the different scripts +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +the value: YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +is your TestLink API/Script key encoded base64 \ No newline at end of file diff --git a/lib/api/rest/v3/util/sublime_text_rester/addPlatformToTestPlanOnTestLink.rester b/lib/api/rest/v3/util/sublime_text_rester/addPlatformToTestPlanOnTestLink.rester new file mode 100644 index 0000000000..b7e7aa44ac --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/addPlatformToTestPlanOnTestLink.rester @@ -0,0 +1,4 @@ +POST https://testlink.your-organization.com/testlink/tl/lib/api/rest/v2/testplans/71965/platforms +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +[{"name": "6.3-AT-Chrome 20201201"}] \ No newline at end of file diff --git a/lib/api/rest/v3/util/sublime_text_rester/createBuildOnTestPlanOnTestLink.rester b/lib/api/rest/v3/util/sublime_text_rester/createBuildOnTestPlanOnTestLink.rester new file mode 100644 index 0000000000..04d0902729 --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/createBuildOnTestPlanOnTestLink.rester @@ -0,0 +1,9 @@ +POST https://testlink.your-organization.com/testlink/tl/lib/api/rest/v2/builds +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +{ + "testplan":72331, + "name": "6.3.next", + "active": 1, + "is_public": 1 +} \ No newline at end of file diff --git a/lib/api/rest/v3/util/sublime_text_rester/createTestPlanOnTestLink.rester b/lib/api/rest/v3/util/sublime_text_rester/createTestPlanOnTestLink.rester new file mode 100644 index 0000000000..82ccd5257a --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/createTestPlanOnTestLink.rester @@ -0,0 +1,10 @@ +POST https://testlink.your-organization.com/testlink/tl/lib/api/rest/v2/testplans +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +{ + "testProjectID":33945, + "name": "Automation - Test Engineering - PT/08/TMS/Costing 72285 8673", + "active": 1, + "is_public": 1, + "notes": "Automation dei Test Cases disegnati dal Test Engineering" +} diff --git a/lib/api/rest/v3/util/sublime_text_rester/getMyTestProjectsOnTestLink.rester b/lib/api/rest/v3/util/sublime_text_rester/getMyTestProjectsOnTestLink.rester new file mode 100644 index 0000000000..cf96056b1f --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/getMyTestProjectsOnTestLink.rester @@ -0,0 +1,3 @@ +GET https://testlink.your-organization.com/testlink/tl/lib/api/rest/v2/testprojects +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + diff --git a/lib/api/rest/v3/util/sublime_text_rester/media/sublime-rester.png b/lib/api/rest/v3/util/sublime_text_rester/media/sublime-rester.png new file mode 100644 index 0000000000..6f97565076 Binary files /dev/null and b/lib/api/rest/v3/util/sublime_text_rester/media/sublime-rester.png differ diff --git a/lib/api/rest/v3/util/sublime_text_rester/media/vscode-rest-client.png b/lib/api/rest/v3/util/sublime_text_rester/media/vscode-rest-client.png new file mode 100644 index 0000000000..227e67a55d Binary files /dev/null and b/lib/api/rest/v3/util/sublime_text_rester/media/vscode-rest-client.png differ diff --git a/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLink.rester b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLink.rester new file mode 100644 index 0000000000..f3cbf30978 --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLink.rester @@ -0,0 +1,13 @@ +POST https://testlink.your-organization.com/testlink/tl/lib/api/rest/v2/executions +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +{ + "notes": "Automation - Factory - PT/07/TMS/Execution", + "testPlanID":71965, + "buildID":8491, + "platformID":196, + "testCaseExternalID":"PPT-1836", + "statusCode": "f", + "executionType":"2", + "executionTimeStampISO":"2021-04-26 11:26:15" +} \ No newline at end of file diff --git a/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkFactoryPT05TMS.rester b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkFactoryPT05TMS.rester new file mode 100644 index 0000000000..120110dd3e --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkFactoryPT05TMS.rester @@ -0,0 +1,13 @@ +POST https://testlink.your-organization.com/testlink/tl/lib/api/rest/v2/executions +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +{ + "notes": "Automation - Factory - PT/05/TMS/Execution", + "testPlanID":71964, + "buildID":8498, + "platformID":196, + "testCaseExternalID":"PPT-1449", + "statusCode": "p", + "executionType":"2", + "executionTimeStampISO":"2026-04-26 17:44:00" +} \ No newline at end of file diff --git a/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkFactoryPT07TMS.rester b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkFactoryPT07TMS.rester new file mode 100644 index 0000000000..f3cbf30978 --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkFactoryPT07TMS.rester @@ -0,0 +1,13 @@ +POST https://testlink.your-organization.com/testlink/tl/lib/api/rest/v2/executions +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +{ + "notes": "Automation - Factory - PT/07/TMS/Execution", + "testPlanID":71965, + "buildID":8491, + "platformID":196, + "testCaseExternalID":"PPT-1836", + "statusCode": "f", + "executionType":"2", + "executionTimeStampISO":"2021-04-26 11:26:15" +} \ No newline at end of file diff --git a/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkWithSteps.rester b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkWithSteps.rester new file mode 100644 index 0000000000..992878dd8f --- /dev/null +++ b/lib/api/rest/v3/util/sublime_text_rester/writeExecutionOnTestLinkWithSteps.rester @@ -0,0 +1,13 @@ +POST https://testlink.your-organization.com/testlink/lib/api/rest/v2/executions +Authorization: Basic YjgzNTk0OTJjYTIzM2ZkMWNlNTVkNjM2M2NkMDI2Y2Y6dQ== + +{ + "notes": "Automation - Test with steps - delete", + "testPlanID": 71945, + "buildID": 8474, + "platformID": 196, + "testCaseExternalID": "PPT-4105", + "statusCode": "p", + "executionType": "2", + "executionTimeStampISO": "2021-04-26 11:26:15" +} \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/APIErrors.php b/lib/api/xmlrpc/v1/APIErrors.php index 324e1f4fd3..208735afc6 100644 --- a/lib/api/xmlrpc/v1/APIErrors.php +++ b/lib/api/xmlrpc/v1/APIErrors.php @@ -1,365 +1,434 @@ - - * @package TestlinkAPI - * - * - */ - - /** - * general config file gives us lang_get access - */ -require_once 'lang_api.php'; - -/**#@+ - * Constants - */ - - -/** - * a catch all generic error - */ -define('GENERAL_ERROR_CODE', -1); -define('GENERAL_SUCCESS_CODE', 1); - -// IMPORTANT: -// lang_get('API_GENERAL_SUCCESS',null,1) -// null -> use user locale -// 1 -> do not log on audit system if localized string do not exist -// -define('GENERAL_SUCCESS_STR', lang_get('API_GENERAL_SUCCESS',null,1)); - -define('NOT_YET_IMPLEMENTED', 50); -define('NOT_YET_IMPLEMENTED_STR', lang_get('API_NOT_YET_IMPLEMENTED',null,1)); - -/** - * Error codes below 1000 are system level - */ -define('NO_DEV_KEY', 100); -define('NO_DEV_KEY_STR', lang_get('API_NO_DEV_KEY',null,1)); - -define('NO_TCASEID', 110); -define('NO_TCASEID_STR', lang_get('API_NO_TCASEID',null,1)); - -define('NO_TCASEEXTERNALID', 110); -define('NO_TCASEEXTERNALID_STR', lang_get('API_NO_TCASEEXTERNALID',null,1)); - - -define('NO_TPLANID', 120); -define('NO_TPLANID_STR', lang_get('API_NO_TPLANID',null,1)); - -define('NO_BUILDID', 130); -define('NO_BUILDID_STR', lang_get('API_NO_BUILDID',null,1)); - -define('NO_TEST_MODE', 140); -define('NO_TEST_MODE_STR', lang_get('API_NO_TEST_MODE',null,1)); - -define('NO_STATUS', 150); -define('NO_STATUS_STR', lang_get('API_NO_STATUS',null,1)); - -define('NO_TESTPROJECTID', 160); -define('NO_TESTPROJECTID_STR', lang_get('API_NO_TESTPROJECTID',null,1)); - -define('NO_TESTCASENAME', 170); -define('NO_TESTCASENAME_STR', lang_get('API_NO_TESTCASENAME',null,1)); - -define('NO_TESTSUITEID', 180); -define('NO_TESTSUITEID_STR', lang_get('API_NO_TESTSUITEID',null,1)); - -define('MISSING_REQUIRED_PARAMETER', 200); -define('MISSING_REQUIRED_PARAMETER_STR', lang_get('API_MISSING_REQUIRED_PARAMETER',null,1)); - -define('PARAMETER_NOT_INT',210); -define('PARAMETER_NOT_INT_STR', lang_get('API_PARAMETER_NOT_INT',null,1)); - -define('NO_TESTSUITENAME', 220); -define('NO_TESTSUITENAME_STR', lang_get('API_NO_TESTSUITENAME',null,1)); - -define('NODEID_IS_NOT_INTEGER',230); -define('NODEID_IS_NOT_INTEGER_STR',lang_get('API_NODEID_IS_NOT_INTEGER',null,1)); - -define('NODEID_DOESNOT_EXIST',231); -define('NODEID_DOESNOT_EXIST_STR',lang_get('API_NODEID_DOESNOT_EXIST',null,1)); - -define('CFG_DELETE_EXEC_DISABLED',232); -define('CFG_DELETE_EXEC_DISABLED_STR',lang_get('API_CFG_DELETE_EXEC_DISABLED',null,1)); - -define('NO_PLATFORMID', 233); -define('NO_PLATFORMID_STR', lang_get('API_NO_PLATFORMID',null,1)); - - -define('NODEID_INVALID_DATA_TYPE', 234); -define('NODEID_INVALID_DATA_TYPE_STR', lang_get('API_NODEID_INVALID_DATA_TYPE',null,1)); - - -define('PLATFORM_NAME_DOESNOT_EXIST', 235); -define('PLATFORM_NAME_DOESNOT_EXIST_STR', lang_get('API_PLATFORM_NAME_DOESNOT_EXIST',null,1)); - -define('NO_MATCH', 236); -define('NO_MATCH_STR', lang_get('API_NO_MATCH',null,1)); - -define('INVALID_TIMESTAMP', 237); -define('INVALID_TIMESTAMP_STR', lang_get('API_INVALID_TIMESTAMP',null,1)); - -define('TSUITE_NOT_ON_TCASE_TPROJ', 238); -define('TSUITE_NOT_ON_TCASE_TPROJ_STR', - lang_get('API_TSUITE_NOT_ON_TCASE_TPROJ',null,1)); - - -/** - * 2000 level - authentication errors - */ -define('INVALID_AUTH', 2000); -define('INVALID_AUTH_STR', lang_get('API_INVALID_AUTH',null,1)); -define('INSUFFICIENT_RIGHTS', 2010); -define('INSUFFICIENT_RIGHTS_STR', lang_get('API_INSUFFICIENT_RIGHTS',null,1)); -define('UPDATER_INSUFFICIENT_RIGHTS', 2015); -define('UPDATER_INSUFFICIENT_RIGHTS_STR', - lang_get('API_UPDATER_INSUFFICIENT_RIGHTS',null,1)); - - -/** - * 3000 level - Test Plan errors - */ -define('INVALID_TPLANID', 3000); -define('INVALID_TPLANID_STR', lang_get('API_INVALID_TPLANID',null,1)); -define('TPLANID_NOT_INTEGER', 3010); -define('TPLANID_NOT_INTEGER_STR', lang_get('API_TPLANID_NOT_INTEGER',null,1)); -define('NO_BUILD_FOR_TPLANID', 3020); -define('NO_BUILD_FOR_TPLANID_STR', lang_get('API_NO_BUILD_FOR_TPLANID',null,1)); -define('TCASEID_NOT_IN_TPLANID', 3030); -define('TCASEID_NOT_IN_TPLANID_STR', lang_get('API_TCASEID_NOT_IN_TPLANID',null,1)); - -define('TPLAN_HAS_NO_BUILDS',3031); -define('TPLAN_HAS_NO_BUILDS_STR', lang_get('API_TPLAN_HAS_NO_BUILDS',null,1)); - -define('BAD_BUILD_FOR_TPLAN', 3032); -define('BAD_BUILD_FOR_TPLAN_STR', lang_get('API_BAD_BUILD_FOR_TPLAN',null,1)); - -define('TESTPLANNAME_DOESNOT_EXIST', 3033); -define('TESTPLANNAME_DOESNOT_EXIST_STR', lang_get('API_TESTPLANNAME_DOESNOT_EXIST',null,1)); - -define('TESTPLANNAME_ALREADY_EXISTS', 3034); -define('TESTPLANNAME_ALREADY_EXISTS_STR', lang_get('API_TESTPLANNAME_ALREADY_EXISTS',null,1)); - - -define('PLATFORM_NOT_LINKED_TO_TESTPLAN', 3040); -define('PLATFORM_NOT_LINKED_TO_TESTPLAN_STR', lang_get('API_PLATFORM_NOT_LINKED_TO_TESTPLAN',null,1)); - -define('TESTPLAN_HAS_NO_PLATFORMS', 3041); -define('TESTPLAN_HAS_NO_PLATFORMS_STR',lang_get('API_TESTPLAN_HAS_NO_PLATFORMS',null,1)); - -define('TCASEID_NOT_IN_TPLANID_FOR_PLATFORM', 3042); -define('TCASEID_NOT_IN_TPLANID_FOR_PLATFORM_STR', lang_get('API_TCASEID_NOT_IN_TPLANID_FOR_PLATFORM',null,1)); - -define('MISSING_PLATFORMID_BUT_NEEDED', 3043); -define('MISSING_PLATFORMID_BUT_NEEDED_STR', lang_get('API_MISSING_PLATFORMID_BUT_NEEDED',null,1)); - -define('PLATFORM_ID_NOT_LINKED_TO_TESTPLAN', 3044); -define('PLATFORM_ID_NOT_LINKED_TO_TESTPLAN_STR', lang_get('API_PLATFORM_ID_NOT_LINKED_TO_TESTPLAN',null,1)); - -define('LINKED_FEATURE_ALREADY_EXISTS', 3045); -define('LINKED_FEATURE_ALREADY_EXISTS_STR', lang_get('API_LINKED_FEATURE_ALREADY_EXISTS',null,1)); - -define('OTHER_VERSION_IS_ALREADY_LINKED', 3046); -define('OTHER_VERSION_IS_ALREADY_LINKED_STR', lang_get('API_OTHER_VERSION_IS_ALREADY_LINKED',null,1)); - -define('TCVERSIONID_NOT_IN_TPLANID', 3047); -define('TCVERSIONID_NOT_IN_TPLANID_STR', lang_get('API_TCVERSIONID_NOT_IN_TPLANID',null,1)); - - -/** - * 4000 level - Build errors - */ -define('INVALID_BUILDID', 4000); -define('INVALID_BUILDID_STR', lang_get('API_INVALID_BUILDID',null,1)); - -define('BUILDID_NOT_INTEGER', 4010); -define('BUILDID_NOT_INTEGER_STR', lang_get('API_BUILDID_NOT_INTEGER',null,1)); - -define('BUILDID_NOGUESS', 4020); -define('BUILDID_NOGUESS_STR', lang_get('API_BUILDID_NOGUESS',null,1)); - -define('BUILDNAME_ALREADY_EXISTS', 4030); -define('BUILDNAME_ALREADY_EXISTS_STR', lang_get('API_BUILDNAME_ALREADY_EXISTS',null,1)); - -define('BUILDNAME_DOES_NOT_EXIST', 4040); -define('BUILDNAME_DOES_NOT_EXIST_STR', lang_get('API_BUILDNAME_DOES_NOT_EXIST',null,1)); - - - -/** - * 5000 level - Test Case errors - */ -define('INVALID_TCASEID', 5000); -define('INVALID_TCASEID_STR' , lang_get('API_INVALID_TCASEID',null,1)); -define('TCASEID_NOT_INTEGER', 5010); -define('TCASEID_NOT_INTEGER_STR', lang_get('API_TCASEID_NOT_INTEGER',null,1)); -define('TESTCASENAME_NOT_STRING', 5020); -define('TESTCASENAME_NOT_STRING_STR', lang_get('API_TESTCASENAME_NOT_STRING',null,1)); -define('NO_TESTCASE_BY_THIS_NAME', 5030); -define('NO_TESTCASE_BY_THIS_NAME_STR', lang_get('API_NO_TESTCASE_BY_THIS_NAME',null,1)); -define('INVALID_TESTCASE_EXTERNAL_ID', 5040); -define('INVALID_TESTCASE_EXTERNAL_ID_STR', lang_get('API_INVALID_TESTCASE_EXTERNAL_ID',null,1)); -define('INVALID_TESTCASE_VERSION_NUMBER', 5050); -define('INVALID_TESTCASE_VERSION_NUMBER_STR', lang_get('API_INVALID_TESTCASE_VERSION_NUMBER',null,1)); -define('TCASE_VERSION_NUMBER_KO',5051); -define('TCASE_VERSION_NUMBER_KO_STR', lang_get('API_TCASE_VERSION_NUMBER_KO',null,1)); - -define('VERSION_NOT_VALID',5052); -define('VERSION_NOT_VALID_STR', lang_get('API_VERSION_NOT_VALID',null,1)); -define('NO_TESTCASE_FOUND', 5053); -define('NO_TESTCASE_FOUND_STR', lang_get('API_NO_TESTCASE_FOUND',null,1)); - - -/** - * 6000 level - Status errors - */ -define('INVALID_STATUS', 6000); -define('INVALID_STATUS_STR' , lang_get('API_INVALID_STATUS',null,1)); - -define('ATTACH_TEMP_FILE_CREATION_ERROR', 6001); -define('ATTACH_TEMP_FILE_CREATION_ERROR_STR' , lang_get('API_ATTACH_TEMP_FILE_CREATION_ERROR',null,1)); - -define('ATTACH_DB_WRITE_ERROR', 6002); -define('ATTACH_DB_WRITE_ERROR_STR', lang_get('API_ATTACH_DB_WRITE_ERROR',null,1)); - -define('ATTACH_FEATURE_DISABLED', 6003); -define('ATTACH_FEATURE_DISABLED_STR', lang_get('API_ATTACH_FEATURE_DISABLED',null,1)); - -define('ATTACH_INVALID_FK', 6004); -define('ATTACH_INVALID_FK_STR', lang_get('API_ATTACH_INVALID_FK',null,1)); - -define('ATTACH_INVALID_ATTACHMENT', 6005); -define('ATTACH_INVALID_ATTACHMENT_STR', lang_get('API_ATTACH_INVALID_ATTACHMENT',null,1)); - - -/** - * 7000 level - Test Project errors - */ -define('INVALID_TESTPROJECTID', 7000); -define('INVALID_TESTPROJECTID_STR' , lang_get('API_INVALID_TESTPROJECTID',null,1)); - -define('TESTPROJECTNAME_SINTAX_ERROR', 7001); -define('TESTPROJECTNAME_EXISTS', 7002); -define('TESTPROJECT_TESTCASEPREFIX_EXISTS', 7003); -define('TESTPROJECT_TESTCASEPREFIX_IS_EMPTY', 7004); -define('TESTPROJECT_TESTCASEPREFIX_IS_TOO_LONG', 7005); - -define('TPLAN_TPROJECT_KO',7006); -define('TPLAN_TPROJECT_KO_STR',lang_get('API_TPLAN_TPROJECT_KO',null,1)); - -define('TCASE_TPROJECT_KO',7007); -define('TCASE_TPROJECT_KO_STR',lang_get('API_TCASE_TPROJECT_KO',null,1)); - -define('TPROJECT_IS_EMPTY',7008); -define('TPROJECT_IS_EMPTY_STR',lang_get('API_TPROJECT_IS_EMPTY',null,1)); - -define('TPROJECT_PREFIX_ALREADY_EXISTS',7009); -define('TPROJECT_PREFIX_ALREADY_EXISTS_STR', - lang_get('API_TPROJECT_PREFIX_ALREADY_EXISTS',null,1)); - -define('REQSPEC_TPROJECT_KO',7010); -define('REQSPEC_TPROJECT_KO_STR',lang_get('API_REQSPEC_TPROJECT_KO',null,1)); - -define('TESTPROJECTNAME_DOESNOT_EXIST',7011); -define('TESTPROJECTNAME_DOESNOT_EXIST_STR',lang_get('API_TESTPROJECTNAME_DOESNOT_EXIST',null,1)); - -define('TESTPROJECTCOPY_SOURCENAME_DOESNOT_EXIST',7012); -define('TESTPROJECTCOPY_SOURCENAME_DOESNOT_EXIST_STR',lang_get('API_TESTPROJECTCOPY_SOURCENAME_DOESNOT_EXIST',null,1)); - -define('TPROJECT_PREFIX_DOESNOT_EXIST',7013); -define('TPROJECT_PREFIX_DOESNOT_EXIST_STR', - lang_get('API_TPROJECT_PREFIX_DOESNOT_EXIST',null,1)); - -/** - * 8000 level - Test Suite errors - */ -define('INVALID_TESTSUITEID', 8000); -define('INVALID_TESTSUITEID_STR', lang_get('API_INVALID_TESTSUITEID',null,1)); - -define('TESTSUITE_DONOTBELONGTO_TESTPROJECT', 8001); -define('TESTSUITE_DONOTBELONGTO_TESTPROJECT_STR', - lang_get('API_TESTSUITE_DONOTBELONGTO_TESTPROJECT',null,1)); - -define('TESTSUITENAME_NOT_STRING', 8002); -define('TESTSUITENAME_NOT_STRING_STR', lang_get('API_TESTSUITENAME_NOT_STRING',null,1)); - -define('INVALID_PARENT_TESTSUITEID', 8003); -define('INVALID_PARENT_TESTSUITEID_STR', lang_get('API_INVALID_PARENT_TESTSUITEID',null,1)); - - - -/** - * 9000 level - Custom Fields - */ -define('NO_CUSTOMFIELD_BY_THIS_NAME', 9000); -define('NO_CUSTOMFIELD_BY_THIS_NAME_STR', lang_get('API_NO_CUSTOMFIELD_BY_THIS_NAME',null,1)); - -define('CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE',9001); -define('CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE_STR', lang_get('API_CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE',null,1)); - -define('CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE',9002); -define('CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE_STR', lang_get('API_CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE',null,1)); - -define('CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT',9003); -define('CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT_STR', lang_get('API_CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT',null,1)); - -define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES',9004); -define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES_STR', lang_get('API_NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES',null,1)); - -define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES',9005); -define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES_STR', lang_get('API_NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES',null,1)); - -define('NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS',9006); -define('NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS_STR', lang_get('API_NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS',null,1)); - - - -/** - * 10000 level - User - */ -define('NO_USER_BY_THIS_LOGIN', 10000); -define('NO_USER_BY_THIS_LOGIN_STR', lang_get('API_NO_USER_BY_THIS_LOGIN',null,1)); - -define('NO_USER_BY_THIS_ID', 10001); -define('NO_USER_BY_THIS_ID_STR', lang_get('API_NO_USER_BY_THIS_ID',null,1)); - -/** - * 11000 level - Requirements - */ -define('REQSPEC_KO', 11000); -define('REQSPEC_KO_STR', lang_get('API_REQSPEC_KO',null,1)); - -define('REQSPEC_IS_EMPTY', 11001); -define('REQSPEC_IS_EMPTY_STR', lang_get('API_REQSPEC_IS_EMPTY',null,1)); - -define('REQ_REQSPEC_KO', 11002); -define('REQ_REQSPEC_KO_STR', lang_get('API_REQ_REQSPEC_KO',null,1)); - -define('REQ_KO', 11003); -define('REQ_KO_STR', lang_get('API_REQ_KO',null,1)); - -define('NO_REQ_IN_THIS_PROJECT', 11004); -define('NO_REQ_IN_THIS_PROJECT_STR', lang_get('API_NO_REQ_IN_THIS_PROJECT',null,1)); - - -/** - * 12000 level - Platforms - */ -define('PLATFORMNAME_ALREADY_EXISTS',12000); -define('PLATFORMNAME_ALREADY_EXISTS_STR', lang_get('API_PLATFORMNAME_ALREADY_EXISTS',null,1)); - -define('PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK',12001); -define('PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK_STR', lang_get('API_PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK',null,1)); - - -/** - * 13000 level - ITS - */ -define('ITS_NOT_FOUND',13000); -define('ITS_NOT_FOUND_STR', lang_get('API_ITS_NOT_FOUND',null,1)); + + * @package TestlinkAPI + * + * + */ + +/** + * general config file gives us lang_get access + */ +require_once 'lang_api.php'; + +/** + * #@+ + * Constants + */ + +/** + * a catch all generic error + */ +define('GENERAL_ERROR_CODE', - 1); +define('GENERAL_SUCCESS_CODE', 1); + +// IMPORTANT: +// lang_get('API_GENERAL_SUCCESS',null,1) +// null -> use user locale +// 1 -> do not log on audit system if localized string do not exist +// +define('GENERAL_SUCCESS_STR', lang_get('API_GENERAL_SUCCESS', null, 1)); + +define('NOT_YET_IMPLEMENTED', 50); +define('NOT_YET_IMPLEMENTED_STR', lang_get('API_NOT_YET_IMPLEMENTED', null, 1)); + +/** + * Error codes below 1000 are system level + */ +define('NO_DEV_KEY', 100); +define('NO_DEV_KEY_STR', lang_get('API_NO_DEV_KEY', null, 1)); + +define('NO_TCASEID', 110); +define('NO_TCASEID_STR', lang_get('API_NO_TCASEID', null, 1)); + +define('NO_TCASEEXTERNALID', 110); +define('NO_TCASEEXTERNALID_STR', lang_get('API_NO_TCASEEXTERNALID', null, 1)); + +define('NO_TCASEVERSIONID', 111); +define('NO_TCASEVERSIONID_STR', lang_get('API_NO_TCASEVERSIONID', null, 1)); + +define('NO_TPLANID', 120); +define('NO_TPLANID_STR', lang_get('API_NO_TPLANID', null, 1)); + +define('NO_BUILDID', 130); +define('NO_BUILDID_STR', lang_get('API_NO_BUILDID', null, 1)); + +define('NO_TEST_MODE', 140); +define('NO_TEST_MODE_STR', lang_get('API_NO_TEST_MODE', null, 1)); + +define('NO_STATUS', 150); +define('NO_STATUS_STR', lang_get('API_NO_STATUS', null, 1)); + +define('NO_TESTPROJECTID', 160); +define('NO_TESTPROJECTID_STR', lang_get('API_NO_TESTPROJECTID', null, 1)); + +define('NO_TESTCASENAME', 170); +define('NO_TESTCASENAME_STR', lang_get('API_NO_TESTCASENAME', null, 1)); + +define('NO_TESTSUITEID', 180); +define('NO_TESTSUITEID_STR', lang_get('API_NO_TESTSUITEID', null, 1)); + +define('MISSING_REQUIRED_PARAMETER', 200); +define('MISSING_REQUIRED_PARAMETER_STR', + lang_get('API_MISSING_REQUIRED_PARAMETER', null, 1)); + +define('PARAMETER_NOT_INT', 210); +define('PARAMETER_NOT_INT_STR', lang_get('API_PARAMETER_NOT_INT', null, 1)); + +define('NO_TESTSUITENAME', 220); +define('NO_TESTSUITENAME_STR', lang_get('API_NO_TESTSUITENAME', null, 1)); + +define('NODEID_IS_NOT_INTEGER', 230); +define('NODEID_IS_NOT_INTEGER_STR', lang_get('API_NODEID_IS_NOT_INTEGER', null, + 1)); + +define('NODEID_DOESNOT_EXIST', 231); +define('NODEID_DOESNOT_EXIST_STR', lang_get('API_NODEID_DOESNOT_EXIST', null, 1)); + +define('CFG_DELETE_EXEC_DISABLED', 232); +define('CFG_DELETE_EXEC_DISABLED_STR', + lang_get('API_CFG_DELETE_EXEC_DISABLED', null, 1)); + +define('NO_PLATFORMID', 233); +define('NO_PLATFORMID_STR', lang_get('API_NO_PLATFORMID', null, 1)); + +define('NODEID_INVALID_DATA_TYPE', 234); +define('NODEID_INVALID_DATA_TYPE_STR', + lang_get('API_NODEID_INVALID_DATA_TYPE', null, 1)); + +define('PLATFORM_NAME_DOESNOT_EXIST', 235); +define('PLATFORM_NAME_DOESNOT_EXIST_STR', + lang_get('API_PLATFORM_NAME_DOESNOT_EXIST', null, 1)); + +define('NO_MATCH', 236); +define('NO_MATCH_STR', lang_get('API_NO_MATCH', null, 1)); + +define('INVALID_TIMESTAMP', 237); +define('INVALID_TIMESTAMP_STR', lang_get('API_INVALID_TIMESTAMP', null, 1)); + +define('TSUITE_NOT_ON_TCASE_TPROJ', 238); +define('TSUITE_NOT_ON_TCASE_TPROJ_STR', + lang_get('API_TSUITE_NOT_ON_TCASE_TPROJ', null, 1)); + +/** + * 2000 level - authentication errors + */ +define('INVALID_AUTH', 2000); +define('INVALID_AUTH_STR', lang_get('API_INVALID_AUTH', null, 1)); +define('INSUFFICIENT_RIGHTS', 2010); +define('INSUFFICIENT_RIGHTS_STR', lang_get('API_INSUFFICIENT_RIGHTS', null, 1)); +define('UPDATER_INSUFFICIENT_RIGHTS', 2015); +define('UPDATER_INSUFFICIENT_RIGHTS_STR', + lang_get('API_UPDATER_INSUFFICIENT_RIGHTS', null, 1)); +define('MUST_BE_ADMIN', 2016); +define('MUST_BE_ADMIN_STR', lang_get('API_MUST_BE_ADMIN', null, 1)); + +/** + * 3000 level - Test Plan errors + */ +define('INVALID_TPLANID', 3000); +define('INVALID_TPLANID_STR', lang_get('API_INVALID_TPLANID', null, 1)); +define('TPLANID_NOT_INTEGER', 3010); +define('TPLANID_NOT_INTEGER_STR', lang_get('API_TPLANID_NOT_INTEGER', null, 1)); +define('NO_BUILD_FOR_TPLANID', 3020); +define('NO_BUILD_FOR_TPLANID_STR', lang_get('API_NO_BUILD_FOR_TPLANID', null, 1)); +define('TCASEID_NOT_IN_TPLANID', 3030); +define('TCASEID_NOT_IN_TPLANID_STR', + lang_get('API_TCASEID_NOT_IN_TPLANID', null, 1)); + +define('TPLAN_HAS_NO_BUILDS', 3031); +define('TPLAN_HAS_NO_BUILDS_STR', lang_get('API_TPLAN_HAS_NO_BUILDS', null, 1)); + +define('BAD_BUILD_FOR_TPLAN', 3032); +define('BAD_BUILD_FOR_TPLAN_STR', lang_get('API_BAD_BUILD_FOR_TPLAN', null, 1)); + +define('TESTPLANNAME_DOESNOT_EXIST', 3033); +define('TESTPLANNAME_DOESNOT_EXIST_STR', + lang_get('API_TESTPLANNAME_DOESNOT_EXIST', null, 1)); + +define('TESTPLANNAME_ALREADY_EXISTS', 3034); +define('TESTPLANNAME_ALREADY_EXISTS_STR', + lang_get('API_TESTPLANNAME_ALREADY_EXISTS', null, 1)); + +define('PLATFORM_NOT_LINKED_TO_TESTPLAN', 3040); +define('PLATFORM_NOT_LINKED_TO_TESTPLAN_STR', + lang_get('API_PLATFORM_NOT_LINKED_TO_TESTPLAN', null, 1)); + +define('TESTPLAN_HAS_NO_PLATFORMS', 3041); +define('TESTPLAN_HAS_NO_PLATFORMS_STR', + lang_get('API_TESTPLAN_HAS_NO_PLATFORMS', null, 1)); + +define('TCASEID_NOT_IN_TPLANID_FOR_PLATFORM', 3042); +define('TCASEID_NOT_IN_TPLANID_FOR_PLATFORM_STR', + lang_get('API_TCASEID_NOT_IN_TPLANID_FOR_PLATFORM', null, 1)); + +define('MISSING_PLATFORMID_BUT_NEEDED', 3043); +define('MISSING_PLATFORMID_BUT_NEEDED_STR', + lang_get('API_MISSING_PLATFORMID_BUT_NEEDED', null, 1)); + +define('PLATFORM_ID_NOT_LINKED_TO_TESTPLAN', 3044); +define('PLATFORM_ID_NOT_LINKED_TO_TESTPLAN_STR', + lang_get('API_PLATFORM_ID_NOT_LINKED_TO_TESTPLAN', null, 1)); + +define('LINKED_FEATURE_ALREADY_EXISTS', 3045); +define('LINKED_FEATURE_ALREADY_EXISTS_STR', + lang_get('API_LINKED_FEATURE_ALREADY_EXISTS', null, 1)); + +define('OTHER_VERSION_IS_ALREADY_LINKED', 3046); +define('OTHER_VERSION_IS_ALREADY_LINKED_STR', + lang_get('API_OTHER_VERSION_IS_ALREADY_LINKED', null, 1)); + +define('TCVERSIONID_NOT_IN_TPLANID', 3047); +define('TCVERSIONID_NOT_IN_TPLANID_STR', + lang_get('API_TCVERSIONID_NOT_IN_TPLANID', null, 1)); + +/** + * 4000 level - Build errors + */ +define('INVALID_BUILDID', 4000); +define('INVALID_BUILDID_STR', lang_get('API_INVALID_BUILDID', null, 1)); + +define('BUILDID_NOT_INTEGER', 4010); +define('BUILDID_NOT_INTEGER_STR', lang_get('API_BUILDID_NOT_INTEGER', null, 1)); + +define('BUILDID_NOGUESS', 4020); +define('BUILDID_NOGUESS_STR', lang_get('API_BUILDID_NOGUESS', null, 1)); + +define('BUILDNAME_ALREADY_EXISTS', 4030); +define('BUILDNAME_ALREADY_EXISTS_STR', + lang_get('API_BUILDNAME_ALREADY_EXISTS', null, 1)); + +define('BUILDNAME_DOES_NOT_EXIST', 4040); +define('BUILDNAME_DOES_NOT_EXIST_STR', + lang_get('API_BUILDNAME_DOES_NOT_EXIST', null, 1)); + +/** + * 5000 level - Test Case errors + */ +define('INVALID_TCASEID', 5000); +define('INVALID_TCASEID_STR', lang_get('API_INVALID_TCASEID', null, 1)); +define('INVALID_TCASEVERSIONID', 5001); +define('INVALID_TCASEVERSIONID_STR', + lang_get('API_INVALID_TCASEVERSIONID', null, 1)); +define('TCASEID_NOT_INTEGER', 5010); +define('TCASEID_NOT_INTEGER_STR', lang_get('API_TCASEID_NOT_INTEGER', null, 1)); +define('TCASEVERSIONID_NOT_INTEGER', 5011); +define('TCASEVERSIONID_NOT_INTEGER_STR', + lang_get('API_TCASEVERSIONID_NOT_INTEGER', null, 1)); +define('TESTCASENAME_NOT_STRING', 5020); +define('TESTCASENAME_NOT_STRING_STR', + lang_get('API_TESTCASENAME_NOT_STRING', null, 1)); +define('NO_TESTCASE_BY_THIS_NAME', 5030); +define('NO_TESTCASE_BY_THIS_NAME_STR', + lang_get('API_NO_TESTCASE_BY_THIS_NAME', null, 1)); +define('INVALID_TESTCASE_EXTERNAL_ID', 5040); +define('INVALID_TESTCASE_EXTERNAL_ID_STR', + lang_get('API_INVALID_TESTCASE_EXTERNAL_ID', null, 1)); +define('INVALID_TESTCASE_VERSION_NUMBER', 5050); +define('INVALID_TESTCASE_VERSION_NUMBER_STR', + lang_get('API_INVALID_TESTCASE_VERSION_NUMBER', null, 1)); +define('TCASE_VERSION_NUMBER_KO', 5051); +define('TCASE_VERSION_NUMBER_KO_STR', + lang_get('API_TCASE_VERSION_NUMBER_KO', null, 1)); + +define('VERSION_NOT_VALID', 5052); +define('VERSION_NOT_VALID_STR', lang_get('API_VERSION_NOT_VALID', null, 1)); +define('NO_TESTCASE_FOUND', 5053); +define('NO_TESTCASE_FOUND_STR', lang_get('API_NO_TESTCASE_FOUND', null, 1)); + +/** + * 6000 level - Status errors + */ +define('INVALID_STATUS', 6000); +define('INVALID_STATUS_STR', lang_get('API_INVALID_STATUS', null, 1)); + +define('ATTACH_TEMP_FILE_CREATION_ERROR', 6001); +define('ATTACH_TEMP_FILE_CREATION_ERROR_STR', + lang_get('API_ATTACH_TEMP_FILE_CREATION_ERROR', null, 1)); + +define('ATTACH_DB_WRITE_ERROR', 6002); +define('ATTACH_DB_WRITE_ERROR_STR', lang_get('API_ATTACH_DB_WRITE_ERROR', null, + 1)); + +define('ATTACH_FEATURE_DISABLED', 6003); +define('ATTACH_FEATURE_DISABLED_STR', + lang_get('API_ATTACH_FEATURE_DISABLED', null, 1)); + +define('ATTACH_INVALID_FK', 6004); +define('ATTACH_INVALID_FK_STR', lang_get('API_ATTACH_INVALID_FK', null, 1)); + +define('ATTACH_INVALID_ATTACHMENT', 6005); +define('ATTACH_INVALID_ATTACHMENT_STR', + lang_get('API_ATTACH_INVALID_ATTACHMENT', null, 1)); + +/** + * 7000 level - Test Project errors + */ +define('INVALID_TESTPROJECTID', 7000); +define('INVALID_TESTPROJECTID_STR', lang_get('API_INVALID_TESTPROJECTID', null, + 1)); + +define('TESTPROJECTNAME_SINTAX_ERROR', 7001); +define('TESTPROJECTNAME_EXISTS', 7002); +define('TESTPROJECT_TESTCASEPREFIX_EXISTS', 7003); +define('TESTPROJECT_TESTCASEPREFIX_IS_EMPTY', 7004); +define('TESTPROJECT_TESTCASEPREFIX_IS_TOO_LONG', 7005); + +define('TPLAN_TPROJECT_KO', 7006); +define('TPLAN_TPROJECT_KO_STR', lang_get('API_TPLAN_TPROJECT_KO', null, 1)); + +define('TCASE_TPROJECT_KO', 7007); +define('TCASE_TPROJECT_KO_STR', lang_get('API_TCASE_TPROJECT_KO', null, 1)); + +define('TPROJECT_IS_EMPTY', 7008); +define('TPROJECT_IS_EMPTY_STR', lang_get('API_TPROJECT_IS_EMPTY', null, 1)); + +define('TPROJECT_PREFIX_ALREADY_EXISTS', 7009); +define('TPROJECT_PREFIX_ALREADY_EXISTS_STR', + lang_get('API_TPROJECT_PREFIX_ALREADY_EXISTS', null, 1)); + +define('REQSPEC_TPROJECT_KO', 7010); +define('REQSPEC_TPROJECT_KO_STR', lang_get('API_REQSPEC_TPROJECT_KO', null, 1)); + +define('TESTPROJECTNAME_DOESNOT_EXIST', 7011); +define('TESTPROJECTNAME_DOESNOT_EXIST_STR', + lang_get('API_TESTPROJECTNAME_DOESNOT_EXIST', null, 1)); + +define('TESTPROJECTCOPY_SOURCENAME_DOESNOT_EXIST', 7012); +define('TESTPROJECTCOPY_SOURCENAME_DOESNOT_EXIST_STR', + lang_get('API_TESTPROJECTCOPY_SOURCENAME_DOESNOT_EXIST', null, 1)); + +define('TPROJECT_PREFIX_DOESNOT_EXIST', 7013); +define('TPROJECT_PREFIX_DOESNOT_EXIST_STR', + lang_get('API_TPROJECT_PREFIX_DOESNOT_EXIST', null, 1)); + +/** + * 8000 level - Test Suite errors + */ +define('INVALID_TESTSUITEID', 8000); +define('INVALID_TESTSUITEID_STR', lang_get('API_INVALID_TESTSUITEID', null, 1)); + +define('TESTSUITE_DONOTBELONGTO_TESTPROJECT', 8001); +define('TESTSUITE_DONOTBELONGTO_TESTPROJECT_STR', + lang_get('API_TESTSUITE_DONOTBELONGTO_TESTPROJECT', null, 1)); + +define('TESTSUITENAME_NOT_STRING', 8002); +define('TESTSUITENAME_NOT_STRING_STR', + lang_get('API_TESTSUITENAME_NOT_STRING', null, 1)); + +define('INVALID_PARENT_TESTSUITEID', 8003); +define('INVALID_PARENT_TESTSUITEID_STR', + lang_get('API_INVALID_PARENT_TESTSUITEID', null, 1)); + +/** + * 9000 level - Custom Fields + */ +define('NO_CUSTOMFIELD_BY_THIS_NAME', 9000); +define('NO_CUSTOMFIELD_BY_THIS_NAME_STR', + lang_get('API_NO_CUSTOMFIELD_BY_THIS_NAME', null, 1)); + +define('CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE', 9001); +define('CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE_STR', + lang_get('API_CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE', null, 1)); + +define('CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE', 9002); +define('CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE_STR', + lang_get('API_CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE', null, 1)); + +define('CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT', 9003); +define('CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT_STR', + lang_get('API_CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT', null, 1)); + +define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES', 9004); +define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES_STR', + lang_get('API_NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES', null, 1)); + +define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES', 9005); +define('NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES_STR', + lang_get('API_NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES', null, 1)); + +define('NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS', 9006); +define('NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS_STR', + lang_get('API_NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS', null, 1)); + +/** + * 10000 level - User + */ +define('NO_USER_BY_THIS_LOGIN', 10000); +define('NO_USER_BY_THIS_LOGIN_STR', lang_get('API_NO_USER_BY_THIS_LOGIN', null, + 1)); + +define('NO_USER_BY_THIS_ID', 10001); +define('NO_USER_BY_THIS_ID_STR', lang_get('API_NO_USER_BY_THIS_ID', null, 1)); + +/** + * 11000 level - Requirements + */ +define('REQSPEC_KO', 11000); +define('REQSPEC_KO_STR', lang_get('API_REQSPEC_KO', null, 1)); + +define('REQSPEC_IS_EMPTY', 11001); +define('REQSPEC_IS_EMPTY_STR', lang_get('API_REQSPEC_IS_EMPTY', null, 1)); + +define('REQ_REQSPEC_KO', 11002); +define('REQ_REQSPEC_KO_STR', lang_get('API_REQ_REQSPEC_KO', null, 1)); + +define('REQ_KO', 11003); +define('REQ_KO_STR', lang_get('API_REQ_KO', null, 1)); + +define('NO_REQ_IN_THIS_PROJECT', 11004); +define('NO_REQ_IN_THIS_PROJECT_STR', + lang_get('API_NO_REQ_IN_THIS_PROJECT', null, 1)); + +/** + * 12000 level - Platforms + */ +define('PLATFORMNAME_ALREADY_EXISTS', 12000); +define('PLATFORMNAME_ALREADY_EXISTS_STR', + lang_get('API_PLATFORMNAME_ALREADY_EXISTS', null, 1)); + +define('PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK', 12001); +define('PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK_STR', + lang_get('API_PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK', null, 1)); + +/** + * 13000 level - ITS + */ +define('ITS_NOT_FOUND', 13000); +define('ITS_NOT_FOUND_STR', lang_get('API_ITS_NOT_FOUND', null, 1)); + +/** + * 14000 level - Users + */ +define('NO_USERID', 14000); +define('NO_USERID_STR', lang_get('API_NO_USERID', null, 1)); + +define('INVALID_USERID', 14001); +define('INVALID_USERID_STR', lang_get('API_INVALID_USERID', null, 1)); + +define('USER_LOGIN_DOESNOT_EXIST', 14002); +define('USER_LOGIN_DOESNOT_EXIST_STR', + lang_get('API_USER_LOGIN_DOESNOT_EXIST', null, 1)); + +define('USER_CREATION_ERROR', 14003); +// for message see getUserErrorMessage + +/** + * 15000 level - Roles + */ +define('NO_ROLEID', 15000); +define('NO_ROLEID_STR', lang_get('API_NO_ROLEID', null, 1)); + +define('INVALID_ROLEID', 15001); +define('INVALID_ROLEID_STR', lang_get('API_INVALID_ROLEID', null, 1)); + +define('ROLE_NAME_DOESNOT_EXIST', 15002); +define('ROLE_NAME_DOESNOT_EXIST_STR', + lang_get('API_ROLE_NAME_DOESNOT_EXIST', null, 1)); + +define('ROLE_SETTING_ERROR', 15003); +define('ROLE_SETTING_ERROR_STR', lang_get('API_ROLE_SETTING_ERROR', null, 1)); diff --git a/lib/api/xmlrpc/v1/api.const.inc.php b/lib/api/xmlrpc/v1/api.const.inc.php index 1cb5e686a7..685ec4a670 100644 --- a/lib/api/xmlrpc/v1/api.const.inc.php +++ b/lib/api/xmlrpc/v1/api.const.inc.php @@ -1,18 +1,21 @@ - - * @package TestlinkAPI - */ - -/** DB Constants used for testing */ -define('TEST_DSN',FALSE); -define('TEST_DB_TYPE', 'mysql'); -define('TEST_DB_USER', 'root'); -define('TEST_DB_PASS', ''); -define('TEST_DB_HOST', 'localhost'); -define('TEST_DB_NAME', 'testlink_development'); \ No newline at end of file + + * @package TestlinkAPI + */ + +/** + * DB Constants used for testing + */ +define('TEST_DSN', false); +define('TEST_DB_TYPE', 'mysql'); +define('TEST_DB_USER', 'root'); +define('TEST_DB_PASS', ''); +define('TEST_DB_HOST', 'localhost'); +define('TEST_DB_NAME', 'testlink_development'); diff --git a/lib/api/xmlrpc/v1/poc/php/example01/autom01.php b/lib/api/xmlrpc/v1/poc/php/example01/autom01.php index 1535883a30..257e2a77fb 100644 --- a/lib/api/xmlrpc/v1/poc/php/example01/autom01.php +++ b/lib/api/xmlrpc/v1/poc/php/example01/autom01.php @@ -1,77 +1,84 @@ -tlProjectID = -1; -$env->tlSuiteID = -1; -$env->tlPlanID = -1; -$env->tlTestCaseVersion = 1; -// ---------------------------------------------- :) - -$doSetUp = true; -$phpSteps = array(); - -if( $doSetUp ) -{ - $phpSteps[] = array('f2i' => 'stepDeleteTestProject.php', 'id' => 'tlProjectID'); - $phpSteps[] = array('f2i' => 'stepCreateTestProject.php', 'id' => 'tlProjectID'); - $phpSteps[] = array('f2i' => 'stepCreateTestSuite.php', 'id' => 'tlSuiteID'); - $phpSteps[] = array('f2i' => 'stepCreateTestCase.php', 'id' => 'tlJolt'); - $phpSteps[] = array('f2i' => 'stepCreateTestPlan.php', 'id' => 'tlPlanID'); -} -else -{ - // - $env->tlProjectID = 1046; - $env->tlPlanID = 1051; - $env->tlTestCaseVersion = 2; - $tlOverWriteOnAdd = 1; -} - -$phpSteps[] = array('f2i' => 'stepAddTestCaseToTestPlan.php', 'id' => 'tlJolt'); - -// Generate some user feedback -$whatWillBeDone = '

    Steps that will be done (in this order)

    '; -$actions = array(); -foreach($phpSteps as $xx) -{ - foreach($xx as $key => $val) - { - if($key == 'f2i') - { - $actions[] = $val; - } - } -} -$whatWillBeDone .= count($actions) ? implode('
    ',$actions) : 'Nothing!!'; -echo $whatWillBeDone . '
    '; - -foreach( $phpSteps as $m2i) -{ - try - { - $tlIDName = $m2i['id']; - require_once $m2i['f2i']; - } - catch (Exception $e) - { - echo $e->getMessage(); - } -} \ No newline at end of file +tlProjectID = - 1; +$env->tlSuiteID = - 1; +$env->tlPlanID = - 1; +$env->tlTestCaseVersion = 1; + +$doSetUp = true; +$phpSteps = array(); + +if ($doSetUp) { + $phpSteps[] = array( + 'f2i' => 'stepDeleteTestProject.php', + 'id' => 'tlProjectID' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestProject.php', + 'id' => 'tlProjectID' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestSuite.php', + 'id' => 'tlSuiteID' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestCase.php', + 'id' => 'tlJolt' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestPlan.php', + 'id' => 'tlPlanID' + ); +} else { + // + $env->tlProjectID = 1046; + $env->tlPlanID = 1051; + $env->tlTestCaseVersion = 2; + $tlOverWriteOnAdd = 1; +} + +$phpSteps[] = array( + 'f2i' => 'stepAddTestCaseToTestPlan.php', + 'id' => 'tlJolt' +); + +// Generate some user feedback +$whatWillBeDone = '

    Steps that will be done (in this order)

    '; +$actions = array(); +foreach ($phpSteps as $xx) { + foreach ($xx as $key => $val) { + if ($key == 'f2i') { + $actions[] = $val; + } + } +} +$whatWillBeDone .= count($actions) ? implode('
    ', $actions) : 'Nothing!!'; +echo $whatWillBeDone . '
    '; + +foreach ($phpSteps as $m2i) { + try { + $tlIDName = $m2i['id']; + require_once $m2i['f2i']; + } catch (Exception $e) { + echo $e->getMessage(); + } +} diff --git a/lib/api/xmlrpc/v1/poc/php/example01/stepAddTestCaseToTestPlan.php b/lib/api/xmlrpc/v1/poc/php/example01/stepAddTestCaseToTestPlan.php index 8b8006fc33..0d8fb694b9 100644 --- a/lib/api/xmlrpc/v1/poc/php/example01/stepAddTestCaseToTestPlan.php +++ b/lib/api/xmlrpc/v1/poc/php/example01/stepAddTestCaseToTestPlan.php @@ -1,41 +1,41 @@ -tlProjectID; -$args["testplanid"] = $env->tlPlanID; - -$args["testcaseexternalid"] = $tlTestCasePrefix . '-1'; -$args["version"] = $env->tlTestCaseVersion; -$args["overwrite"] = $tlOverWriteOnAdd; - -$tlIdx++; -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$answer = runTest($client,$method,$args,$tlIdx); \ No newline at end of file +tlProjectID; +$args["testplanid"] = $env->tlPlanID; + +$args["testcaseexternalid"] = $tlTestCasePrefix . '-1'; +$args["version"] = $env->tlTestCaseVersion; +$args["overwrite"] = $tlOverWriteOnAdd; + +$tlIdx ++; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$answer = runTest($client, $method, $args, $tlIdx); diff --git a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestCase.php b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestCase.php index 9068b0e55b..9e4c587c38 100644 --- a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestCase.php +++ b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestCase.php @@ -1,32 +1,36 @@ -tlProjectID; -$args["testsuiteid"] = $env->tlSuiteID; - -$args["testcasename"]='ZZ - TEST CASE NAME IS OK'; -$args["summary"]='Test Case created via API'; -$args["preconditions"]='Test Link API Up & Running'; -$args["authorlogin"]='admin'; -$args["checkduplicatedname"]=0; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); - -$unitTestDescription = ""; -echo $unitTestDescription; - -$tlIdx++; -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$ret = runTest($client,$method,$args,$tlIdx); \ No newline at end of file +tlProjectID; +$args["testsuiteid"] = $env->tlSuiteID; + +$args["testcasename"] = 'ZZ - TEST CASE NAME IS OK'; +$args["summary"] = 'Test Case created via API'; +$args["preconditions"] = 'Test Link API Up & Running'; +$args["authorlogin"] = 'admin'; +$args["checkduplicatedname"] = 0; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); + +$unitTestDescription = ""; +echo $unitTestDescription; + +$tlIdx ++; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$ret = runTest($client, $method, $args, $tlIdx); diff --git a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestPlan.php b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestPlan.php index fc754f5629..c31c1b81f0 100644 --- a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestPlan.php +++ b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestPlan.php @@ -1,36 +1,33 @@ -debug = $tlDebug; +$ret = runTest($client, $method, $args, $tlIdx); + +if (isset($ret[0]['id'])) { + $env->$tlIDName = $ret[0]['id']; +} else { + $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . + basename(__FILE__) . ")"; + throw new Exception($msg, 1); } - -$args=array(); -$args["devKey"] =isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $tlDevKey; -$args["prefix"] = $tlTestCasePrefix; -$args["testplanname"]="TPLAN BY API"; -$args["notes"]="test plan created using XML-RPC-API"; - -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$ret = runTest($client,$method,$args,$tlIdx); - -if( isset($ret[0]['id']) ) -{ - $env->$tlIDName = $ret[0]['id']; -} -else -{ - $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . basename(__FILE__) . ")"; - throw new Exception($msg, 1); -} \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestProject.php b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestProject.php index d3f93c983d..8f5c82a00d 100644 --- a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestProject.php +++ b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestProject.php @@ -1,40 +1,38 @@ - {$additionalInfo}"; - -echo $unitTestDescription . ' ' . $additionalInfo; - -$tlIdx++; -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$ret = runTest($client,$method,$args,$tlIdx); - -if( isset($ret[0]['id']) ) -{ - $env->$tlIDName = $ret[0]['id']; -} -else -{ - $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . basename(__FILE__) . ")"; - throw new Exception($msg, 1); -} \ No newline at end of file + {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$tlIdx ++; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$ret = runTest($client, $method, $args, $tlIdx); + +if (isset($ret[0]['id'])) { + $env->$tlIDName = $ret[0]['id']; +} else { + $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . + basename(__FILE__) . ")"; + throw new Exception($msg, 1); +} diff --git a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestSuite.php b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestSuite.php index 321dd09130..684153affb 100644 --- a/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestSuite.php +++ b/lib/api/xmlrpc/v1/poc/php/example01/stepCreateTestSuite.php @@ -1,36 +1,33 @@ -tlProjectID; -$args["testsuitename"] = 'TS API 100'; -$args["details"]='This has been created by XMLRPC API Call'; - -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$tlIdx++; -$ret = runTest($client,$method,$args,$tlIdx); - - -if( isset($ret[0]['id']) ) -{ - $env->$tlIDName = $ret[0]['id']; +tlProjectID; +$args["testsuitename"] = 'TS API 100'; +$args["details"] = 'This has been created by XMLRPC API Call'; + +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$tlIdx ++; +$ret = runTest($client, $method, $args, $tlIdx); + +if (isset($ret[0]['id'])) { + $env->$tlIDName = $ret[0]['id']; +} else { + $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . + basename(__FILE__) . ")"; + throw new Exception($msg, 1); } -else -{ - $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . basename(__FILE__) . ")"; - throw new Exception($msg, 1); -} \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/poc/php/example01/stepDeleteTestProject.php b/lib/api/xmlrpc/v1/poc/php/example01/stepDeleteTestProject.php index aba689ed91..11b24246e7 100644 --- a/lib/api/xmlrpc/v1/poc/php/example01/stepDeleteTestProject.php +++ b/lib/api/xmlrpc/v1/poc/php/example01/stepDeleteTestProject.php @@ -1,26 +1,25 @@ -debug = $tlDebug; - -$tlIdx++; -$answer = runTest($client,$method,$args,$tlIdx); \ No newline at end of file +debug = $tlDebug; + +$tlIdx ++; +$answer = runTest($client, $method, $args, $tlIdx); diff --git a/lib/api/xmlrpc/v1/poc/php/util/setup.inc.php b/lib/api/xmlrpc/v1/poc/php/util/setup.inc.php index 485e4574a9..8a2988915a 100644 --- a/lib/api/xmlrpc/v1/poc/php/util/setup.inc.php +++ b/lib/api/xmlrpc/v1/poc/php/util/setup.inc.php @@ -1,21 +1,23 @@ - TestLink XMLRPC Proof Of Concept @@ -25,43 +27,40 @@ storedDetail : '', toggle : function(id){ - if(this.storedDetail && this.storedDetail != id) + if(this.storedDetail && this.storedDetail != id) { document.getElementById(this.storedDetail).style.display = 'none'; } this.storedDetail = id; var style = document.getElementById(id).style; - if(style.display == 'block') + if(style.display == 'block') { style.display = 'none'; } else { style.display = 'block'; - } + } return false; } }; -TestLink XML-RPC API - POC Samples Runner
    '; -echo "XMLRPC Server URL {$server_url}

    "; \ No newline at end of file +TestLink XML-RPC API - POC Samples Runner
    '; +echo "XMLRPC Server URL {$server_url}

    "; diff --git a/lib/api/xmlrpc/v1/poc/php/util/util.php b/lib/api/xmlrpc/v1/poc/php/util/util.php index 441616a1b4..85030512ea 100644 --- a/lib/api/xmlrpc/v1/poc/php/util/util.php +++ b/lib/api/xmlrpc/v1/poc/php/util/util.php @@ -1,64 +1,62 @@ -

    This sample can be runned without changes against sample database testlinkAPI'; - echo ('
    that you will find on [YOUR TL INSTALLATION DIR]' . '\\docs\\db_sample\\'); - echo '



    '; +

    This sample can be runned without changes against sample database testlinkAPI'; + echo '
    that you will find on [YOUR TL INSTALLATION DIR]' . + '\\docs\\db_sample\\'; + echo '



    '; +} + +function runTest(&$client, $method, $args, $feedback_id = 1) +{ + $html_id = "result_{$feedback_id}"; + $msg_click_to_show = "click to show XML-RPC Client Debug Info"; + + $imgFO = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'img' . + DIRECTORY_SEPARATOR . 'icon-foldout.gif'; + $imgSRC = ' '; + + $onClick = "return DetailController.toggle('{$html_id}')"; + + if ($client->debug) { + echo '
    Debug: Inside function: ' . __FUNCTION__ . '
    '; + new dBug($args); + + echo '
    '; + echo $imgSRC . "{$msg_click_to_show} "; + } + echo '

    "; + + echo "
    Result was: "; + new dBug($response); + echo "
    "; + echo "


    "; + + return $response; } - -function runTest(&$client,$method,$args,$feedback_id=1) -{ - $html_id="result_{$feedback_id}"; - $msg_click_to_show="click to show XML-RPC Client Debug Info"; - - $imgFO = dirname(__FILE__) . DIRECTORY_SEPARATOR . - 'img' . DIRECTORY_SEPARATOR . 'icon-foldout.gif'; - $imgSRC = ' '; - - $onClick = "return DetailController.toggle('{$html_id}')"; - - if($client->debug) - { - echo '
    Debug: Inside function: ' . __FUNCTION__ . '
    '; - new dBug($args); - - echo '
    '; - echo $imgSRC . "{$msg_click_to_show} "; - } - echo '

    "; - - echo "
    Result was: "; - new dBug($response); - echo "
    "; - echo "


    "; - - return $response; -} \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientAddPlatformToTestPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/clientAddPlatformToTestPlan.php index 2d0fc40ae5..1788c574c4 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientAddPlatformToTestPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientAddPlatformToTestPlan.php @@ -1,29 +1,29 @@ -debug=$debug; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- -?> \ No newline at end of file +debug = $debug; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientAddTestCaseKeywords.php b/lib/api/xmlrpc/v1/sample_clients/php/clientAddTestCaseKeywords.php index 4723642db4..308d3b0d45 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientAddTestCaseKeywords.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientAddTestCaseKeywords.php @@ -1,27 +1,35 @@ - array('Barbie','Barbie'), - 'MAB-2' => array('Barbie','Jessie')); - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -$answer = runTest($client,$method,$args); \ No newline at end of file + array( + 'Barbie', + 'Barbie' + ), + 'MAB-2' => array( + 'Barbie', + 'Jessie' + ) +); + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +$answer = runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientAssignRequirements.php b/lib/api/xmlrpc/v1/sample_clients/php/clientAssignRequirements.php index 891fcf9002..49c32a0b10 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientAssignRequirements.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientAssignRequirements.php @@ -1,39 +1,50 @@ - 336,'requirements' => array(340)), - array('req_spec' => 345,'requirements' => array(346,348)) - ); - -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -?> \ No newline at end of file + 336, + 'requirements' => array( + 340 + ) + ), + array( + 'req_spec' => 345, + 'requirements' => array( + 346, + 348 + ) + ) +); + +$additionalInfo = ''; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientAssignTestCaseExecutionTask.php b/lib/api/xmlrpc/v1/sample_clients/php/clientAssignTestCaseExecutionTask.php index ddf8bcc4b9..4158c7c6b3 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientAssignTestCaseExecutionTask.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientAssignTestCaseExecutionTask.php @@ -1,192 +1,168 @@ -debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -new dBug($answer); -die(); - -// --------------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------------- -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - All OK"; - -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 278; -$args["testcaseexternalid"] = 'APX-1'; -$args["platformname"] = 'Informix'; -$args["buildname"] = '2.0'; -$args["user"] = 'giskard'; - - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); - -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Test Plan ID"; - -$args=array(); -$args["devKey"] = $devKey; -// $args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Test Case "; - -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 9; -// $args["testcaseexternalid"] = 'GK-1'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Build Name "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -// $args["buildname"] = '1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Test Plan ID "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 900000; -$args["testcaseexternalid"] = 'GK-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Test Case External ID "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-WRONG-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Build Name "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); +debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +new dBug($answer); +die(); + +// --------------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------------- +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - All OK"; + +$args = array(); +$args["devKey"] = $devKey; +$args["testplanid"] = 278; +$args["testcaseexternalid"] = 'APX-1'; +$args["platformname"] = 'Informix'; +$args["buildname"] = '2.0'; +$args["user"] = 'giskard'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +// --------------------------------------------------------------------------------------- + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Test Plan ID"; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testcaseexternalid"] = 'GK-1'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Test Case "; + +$args = array(); +$args["devKey"] = $devKey; +$args["testplanid"] = 9; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Build Name "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-1'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Test Plan ID "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 900000; +$args["testcaseexternalid"] = 'GK-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Test Case External ID "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-WRONG-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Build Name "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); // --------------------------------------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCheckDevKey.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCheckDevKey.php index 58f0d38e88..2ebde34a20 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCheckDevKey.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCheckDevKey.php @@ -1,58 +1,56 @@ -must finish OK
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY . 'UUUU'; - -$additionalInfo='
    Must Fail
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); +must finish OK
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY . 'UUUU'; + +$additionalInfo = '
    Must Fail
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); // --------------------------------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCloseBuild.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCloseBuild.php index 70f57ea80e..e1d0993c4e 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCloseBuild.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCloseBuild.php @@ -8,7 +8,7 @@ * @Author: francisco.mancardi@gmail.com * */ - + require_once 'util.php'; require_once 'sample.inc.php'; show_api_db_sample_msg(); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateBuild.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateBuild.php index a793f6e2e5..c61d8e27d8 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateBuild.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateBuild.php @@ -1,66 +1,60 @@ -debug = $debug; +runTest($client, $method, $args, $test_num); + +$method = 'createBuild'; +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = $devKey; +$args["testplanid"] = 72; +$args["buildname"] = 'AAASECOND TEST API BUILD'; +$args["buildnotes"] = 'Created via API'; +$args["active"] = 0; +$args["open"] = 0; +$args["releasedate"] = '2016-09-01'; +$additionalInfo = ' active+open+releasedate attributes'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); - -$test_num=0; -$devKey = '985978c915f50e47a4b1a54a943d1b76'; - -// -------------------------------------------------------- -$method='createBuild'; -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=$devKey; -$args["testplanid"]=61; -$args["buildname"]='Abril 230'; -$args["buildnotes"]='Created via API 3'; -$args["copytestersfrombuild"]='3'; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); - -// -------------------------------------------------------- - -// -------------------------------------------------------- -$method='createBuild'; -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=$devKey; -$args["testplanid"]=72; -$args["buildname"]='AAASECOND TEST API BUILD'; -$args["buildnotes"]='Created via API'; -$args["active"]=0; -$args["open"]=0; -$args["releasedate"] = '2016-09-01'; -$additionalInfo=' active+open+releasedate attributes'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); -// -------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateNTestCases.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateNTestCases.php index f7ee574272..2009bdd563 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateNTestCases.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateNTestCases.php @@ -1,44 +1,43 @@ - 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); -$args["authorlogin"]='admin'; - -$client = new IXR_Client($server_url); -$client->debug=true; - -for($idx=1 ; $idx <= $tcaseQty; $idx++) -{ - $args["testcasename"] = "Sample TEST #{$idx}"; - $args["summary"]=$args["testcasename"] . 'created via XML-RPC API'; - runTest($client,$method,$args); -} -echo 'Test Cases Created'; -?> \ No newline at end of file + 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); +$args["authorlogin"] = 'admin'; + +$client = new IXR_Client($server_url); +$client->debug = true; + +for ($idx = 1; $idx <= $tcaseQty; $idx ++) { + $args["testcasename"] = "Sample TEST #{$idx}"; + $args["summary"] = $args["testcasename"] . 'created via XML-RPC API'; + runTest($client, $method, $args); +} +echo 'Test Cases Created'; +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreatePlatform.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreatePlatform.php index 7f3429668c..e872b178ac 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreatePlatform.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreatePlatform.php @@ -1,30 +1,31 @@ -debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; -?> \ No newline at end of file +debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateScenario.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateScenario.php index eeb32acb7c..a032d4902d 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateScenario.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateScenario.php @@ -1,123 +1,122 @@ -debug = false; -$cfg->devKey = 'admin'; -$cfg->prefix = 'AX'; - -// $server_url is GLOBAL created on some previous include -$args4call = array(); - -$ret['createTestProject'] = createTestProject($server_url,$cfg,$args4call); -// $ret = createTestSuite($server_url$cfg,$args4call); -$ret['createTestPlan'] = createTestPlan($server_url,$cfg,$args4call); -$ret['createBuild'] = createBuild($server_url,$cfg,$args4call,$ret['createTestPlan'][0]['id']); -$ret['createPlatform'] = createPlatform($server_url,$cfg,$args4call); - - - -/** - * - */ -function createTestProject($server_url,$cfg,&$args4call) -{ - $method = __FUNCTION__; - - $args4call[$method] = array("devKey" => $cfg->devKey, - "testcaseprefix" => $cfg->prefix, - "testprojectname" => 'TPROJ-01', - "notes" => "test project created using XML-RPC-API"); - - $client = new IXR_Client($server_url); - $client->debug = $cfg->debug; - return runTest($client,$method,$args4call[$method]); -} - - -/** - * - */ -function createTestPlan($server_url,$cfg,&$args4call) -{ - $method = __FUNCTION__; - $args4call[$method] = array("devKey" => $cfg->devKey, - "testprojectname" => $args4call['createTestProject']["testprojectname"], - "testplanname" => "TPLAN A", - "notes" => "Test plan used to test report 'Test cases without tester assignment' "); - - $client = new IXR_Client($server_url); - $client->debug = $cfg->debug; - return runTest($client,$method,$args4call[$method]); -} - - -/** - * - */ -function createBuild($server_url,$cfg,&$args4call,$tplan_id) -{ - - $method = __FUNCTION__; - $args4call[$method] = array("devKey" => $cfg->devKey, - "buildname" => '1.0', - "testplanid" => $tplan_id, - "buildnote" => "Build used to test issue 5451"); - - $client = new IXR_Client($server_url); - $client->debug = $cfg->debug; - return runTest($client,$method,$args4call[$method]); -} - -/** - * - */ -function createPlatform($server_url,$cfg,&$args4call) -{ - $method = __FUNCTION__; - - $platforms = array(); - - $item = new stdClass(); - $item->name = 'PLAT-01'; - $item->notes = 'Notes for' . $item->name; - $platforms[] = $item; - - $item = new stdClass(); - $item->name = 'PLAT-02'; - $item->notes = 'Notes for' . $item->name; - $platforms[] = $item; - - $item = new stdClass(); - $item->name = 'PLAT-03'; - $item->notes = 'Notes for' . $item->name; - $platforms[] = $item; - - $res = array(); - $client = new IXR_Client($server_url); - foreach($platforms as $item) - { - $args4call[$method] = array("devKey" => $cfg->devKey, - "platformname" => $item->name, - "notes" => $item->notes, - "testprojectname" => $args4call['createTestProject']["testprojectname"]); - $client->debug = $cfg->debug; - $res[] = runTest($client,$method,$args4call[$method]); - } - return $res; +debug = false; +$cfg->devKey = 'admin'; +$cfg->prefix = 'AX'; + +// $server_url is GLOBAL created on some previous include +$args4call = array(); + +$ret['createTestProject'] = createTestProject($server_url, $cfg, $args4call); +// $ret = createTestSuite($server_url$cfg,$args4call); +$ret['createTestPlan'] = createTestPlan($server_url, $cfg, $args4call); +$ret['createBuild'] = createBuild($server_url, $cfg, $args4call, + $ret['createTestPlan'][0]['id']); +$ret['createPlatform'] = createPlatform($server_url, $cfg, $args4call); + +/** + */ +function createTestProject($server_url, $cfg, &$args4call) +{ + $method = __FUNCTION__; + + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "testcaseprefix" => $cfg->prefix, + "testprojectname" => 'TPROJ-01', + "notes" => "test project created using XML-RPC-API" + ); + + $client = new IXR_Client($server_url); + $client->debug = $cfg->debug; + return runTest($client, $method, $args4call[$method]); +} + +/** + */ +function createTestPlan($server_url, $cfg, &$args4call) +{ + $method = __FUNCTION__; + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "testprojectname" => $args4call['createTestProject']["testprojectname"], + "testplanname" => "TPLAN A", + "notes" => "Test plan used to test report 'Test cases without tester assignment' " + ); + + $client = new IXR_Client($server_url); + $client->debug = $cfg->debug; + return runTest($client, $method, $args4call[$method]); +} + +/** + */ +function createBuild($server_url, $cfg, &$args4call, $tplan_id) +{ + $method = __FUNCTION__; + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "buildname" => '1.0', + "testplanid" => $tplan_id, + "buildnote" => "Build used to test issue 5451" + ); + + $client = new IXR_Client($server_url); + $client->debug = $cfg->debug; + return runTest($client, $method, $args4call[$method]); +} + +/** + */ +function createPlatform($server_url, $cfg, &$args4call) +{ + $method = __FUNCTION__; + + $platforms = array(); + + $item = new stdClass(); + $item->name = 'PLAT-01'; + $item->notes = 'Notes for' . $item->name; + $platforms[] = $item; + + $item = new stdClass(); + $item->name = 'PLAT-02'; + $item->notes = 'Notes for' . $item->name; + $platforms[] = $item; + + $item = new stdClass(); + $item->name = 'PLAT-03'; + $item->notes = 'Notes for' . $item->name; + $platforms[] = $item; + + $res = array(); + $client = new IXR_Client($server_url); + foreach ($platforms as $item) { + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "platformname" => $item->name, + "notes" => $item->notes, + "testprojectname" => $args4call['createTestProject']["testprojectname"] + ); + $client->debug = $cfg->debug; + $res[] = runTest($client, $method, $args4call[$method]); + } + return $res; } diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCase.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCase.php index 1a56d4aae3..ed02dc3520 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCase.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCase.php @@ -1,135 +1,139 @@ - 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); - -// $wfd = config_get('testCaseStatus'); -$args["status"] = 4; -//$args["estimatedexecduration"] = 4.5; - -$unitTestDescription = "Test #{$tcCounter}- {$method} - With STATUS:{$args['wfstatus']}"; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); - -// --------------------------------------------------------------------------------- -$method='createTestCase'; -$unitTestDescription = "Test #{$tcCounter}- {$method} - With NAME exceeding limit"; -$tcCounter++; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testprojectid"]=280; -$args["testsuiteid"]=297; -$args["testcasename"]= -'TEST CASE NAME IS LONGER THAT ALLOWED SIZE - 100 CHARACTERS - The quick brown fox jumps over the X % lazydog (bye bye dog)'; -$args["summary"]='Test Case created via API'; -$args["preconditions"]='Test Link API Up & Running'; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); -$args["authorlogin"]='admin'; -$args["checkduplicatedname"]=1; - - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); - - -// ---------------------------------------------------------------------------------------------------- -$method='createTestCase'; -$unitTestDescription="Test #{$tcCounter}- {$method}"; -$tcCounter++; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testprojectid"]=620; -$args["testsuiteid"]=621; -$args["testcasename"]='Network Interface Card (NIC) driver update'; -$args["summary"]='Test Case created via API'; -$args["authorlogin"]='admin'; -$args["checkduplicatedname"]=1; -$args["keywordid"]='1,2,3,4'; - - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); - -// ---------------------------------------------------------------------------------------------------- -$method='createTestCase'; -$unitTestDescription="Test #{$tcCounter}- {$method}"; -$tcCounter++; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testprojectid"]=620; -$args["testsuiteid"]=621; -$args["testcasename"]='Volume Manager Increase size'; -$args["summary"]='Test Case created via API - Volume Manager Increase size'; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); -$args["steps"][]=array('step_number' => 2, 'actions' => 'Connect to Server', 'expected_results' => 'beep twice'); -$args["authorlogin"]='admin'; -$args["checkduplicatedname"]=1; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); - -// ---------------------------------------------------------------------------------------------------- -$method='createTestCase'; -$unitTestDescription="Test #{$tcCounter}- {$method}"; -$tcCounter++; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testprojectid"]=620; -$args["testsuiteid"]=621; -$args["testcasename"]='Volume Manager Increase size'; -$args["summary"]='Want to test Action On Duplicate with value create_new_version FOR Volume Manager Increase size'; -$args["authorlogin"]='admin'; -$args["checkduplicatedname"]=1; -$args["actiononduplicatedname"]="create_new_version"; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// ---------------------------------------------------------------------------------------------------- -?> \ No newline at end of file + 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); + +$args["status"] = 4; + +$unitTestDescription = "Test #{$tcCounter}- {$method} - With STATUS:{$args['wfstatus']}"; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +$method = 'createTestCase'; +$unitTestDescription = "Test #{$tcCounter}- {$method} - With NAME exceeding limit"; +$tcCounter ++; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testprojectid"] = 280; +$args["testsuiteid"] = 297; +$args["testcasename"] = 'TEST CASE NAME IS LONGER THAT ALLOWED SIZE - 100 CHARACTERS - The quick brown fox jumps over the X % lazydog (bye bye dog)'; +$args["summary"] = 'Test Case created via API'; +$args["preconditions"] = 'Test Link API Up & Running'; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); +$args["authorlogin"] = 'admin'; +$args["checkduplicatedname"] = 1; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +$method = 'createTestCase'; +$unitTestDescription = "Test #{$tcCounter}- {$method}"; +$tcCounter ++; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testprojectid"] = 620; +$args["testsuiteid"] = 621; +$args["testcasename"] = 'Network Interface Card (NIC) driver update'; +$args["summary"] = 'Test Case created via API'; +$args["authorlogin"] = 'admin'; +$args["checkduplicatedname"] = 1; +$args["keywordid"] = '1,2,3,4'; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +$method = 'createTestCase'; +$unitTestDescription = "Test #{$tcCounter}- {$method}"; +$tcCounter ++; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testprojectid"] = 620; +$args["testsuiteid"] = 621; +$args["testcasename"] = 'Volume Manager Increase size'; +$args["summary"] = 'Test Case created via API - Volume Manager Increase size'; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); +$args["steps"][] = array( + 'step_number' => 2, + 'actions' => 'Connect to Server', + 'expected_results' => 'beep twice' +); +$args["authorlogin"] = 'admin'; +$args["checkduplicatedname"] = 1; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +$method = 'createTestCase'; +$unitTestDescription = "Test #{$tcCounter}- {$method}"; +$tcCounter ++; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testprojectid"] = 620; +$args["testsuiteid"] = 621; +$args["testcasename"] = 'Volume Manager Increase size'; +$args["summary"] = 'Want to test Action On Duplicate with value create_new_version FOR Volume Manager Increase size'; +$args["authorlogin"] = 'admin'; +$args["checkduplicatedname"] = 1; +$args["actiononduplicatedname"] = "create_new_version"; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCaseSteps.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCaseSteps.php index 7895c17544..26c7570471 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCaseSteps.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestCaseSteps.php @@ -1,81 +1,83 @@ - 12, 'actions' => 'SKIP !!!!Start Server Ubuntu 11.04', - 'expected_results' => 'green light' . ' ' . $args["action"]); - -// $args["steps"][]=array('step_number' => 12, 'actions' => 'SKIP !!!! Start Server Fedora 15', -// 'expected_results' => 'green lantern' . ' ' . $args["action"]); -//$args["authorlogin"]='admin'; - - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$tcCounter); -die(); -// ---------------------------------------------------------------------------------------------------- -// -------------------------------------------------------------------------------------------- -$tcCounter++; -$method='createTestCaseSteps'; -$unitTestDescription = "Test #{$tcCounter}- {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testcaseexternalid"]='MKO-1'; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); -$args["authorlogin"]='admin'; - - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$tcCounter); -// ---------------------------------------------------------------------------------------------------- -// -------------------------------------------------------------------------------------------- -$tcCounter++; -$method='createTestCaseSteps'; -$unitTestDescription = "Test #{$tcCounter}- {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testcaseexternalid"]='MKO-1'; -$args["version"]=100; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server VERSION DOES NOT EXIST', - 'expected_results' => 'green light'); -$args["authorlogin"]='admin'; - - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$tcCounter); -// ---------------------------------------------------------------------------------------------------- - -?> \ No newline at end of file + 12, + 'actions' => 'SKIP !!!!Start Server Ubuntu 11.04', + 'expected_results' => 'green light' . ' ' . $args["action"] +); + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $tcCounter); +die(); +// ---------------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------- +$tcCounter ++; +$method = 'createTestCaseSteps'; +$unitTestDescription = "Test #{$tcCounter}- {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testcaseexternalid"] = 'MKO-1'; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); +$args["authorlogin"] = 'admin'; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $tcCounter); +// ---------------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------- +$tcCounter ++; +$method = 'createTestCaseSteps'; +$unitTestDescription = "Test #{$tcCounter}- {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testcaseexternalid"] = 'MKO-1'; +$args["version"] = 100; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server VERSION DOES NOT EXIST', + 'expected_results' => 'green light' +); +$args["authorlogin"] = 'admin'; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $tcCounter); +// ---------------------------------------------------------------------------------------------------- + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestPlan.php index cb56698c5e..7e718f2f1e 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestPlan.php @@ -1,61 +1,61 @@ -debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testprojectname"]='TPROJECT1'; -$args["testplanname"]="TPLAN BY API"; -$args["notes"]="test plan created using XML-RPC-API"; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testprojectname"]='TPROJECT1'; -$args["testplanname"]="TPLAN BY API-2"; -$args["notes"]="test plan 2 created using XML-RPC-API"; -$args["active"]=0; -$args["public"]=1; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; \ No newline at end of file +debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testprojectname"] = 'TPROJECT1'; +$args["testplanname"] = "TPLAN BY API"; +$args["notes"] = "test plan created using XML-RPC-API"; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testprojectname"] = 'TPROJECT1'; +$args["testplanname"] = "TPLAN BY API-2"; +$args["notes"] = "test plan 2 created using XML-RPC-API"; +$args["active"] = 0; +$args["public"] = 1; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProject.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProject.php index 312172deb6..a7553d4d89 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProject.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProject.php @@ -1,248 +1,259 @@ - {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +die(); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}() ::: "; + +$args = array(); +// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled +$args["options"] = array( + 'requirementsEnabled' => 0 +); +$dummy = 'Options['; +foreach ($args["options"] as $key => $value) { + $dummy .= $key . ' -> ' . $value . ' '; +} +$dummy .= "] "; + +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseprefix"] = $prefix . $test_num; +$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; +$args["active"] = 0; +$args["public"] = 0; + +$additionalInfo = $dummy . + " active -> {$args['active']}, public -> {$args['public']}"; +$args["notes"] = "test project created using XML-RPC-API -
    {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}() ::: "; + +$args = array(); +// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled +$args["options"] = array( + 'requirementsEnabled' => 0, + 'testPriorityEnabled' => 0 +); +$dummy = 'Options['; +foreach ($args["options"] as $key => $value) { + $dummy .= $key . ' -> ' . $value . ' '; +} +$dummy .= "] "; + +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseprefix"] = $prefix . $test_num; +$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; +$args["active"] = 0; +$args["public"] = 0; + +$additionalInfo = $dummy . + " active -> {$args['active']}, public -> {$args['public']}"; +$args["notes"] = "test project created using XML-RPC-API -
    {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}() ::: "; + +$args = array(); + +// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled +$args["options"] = array( + 'requirementsEnabled' => 0, + 'testPriorityEnabled' => 0, + 'automationEnabled' => 0, + 'inventoryEnabled' => 0 +); +$dummy = 'Options['; +foreach ($args["options"] as $key => $value) { + $dummy .= $key . ' -> ' . $value . ' '; +} +$dummy .= "] "; + +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseprefix"] = $prefix . $test_num; +$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; +$args["active"] = 0; +$args["public"] = 0; + +$additionalInfo = $dummy . + " active -> {$args['active']}, public -> {$args['public']}"; +$args["notes"] = "test project created using XML-RPC-API -
    {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}() ::: "; + +$args = array(); + +// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled +$args["options"] = array( + 'requirementsEnabled' => 0, + 'testPriorityEnabled' => 0, + 'automationEnabled' => 0, + 'inventoryEnabled' => 0 +); +$dummy = 'Options['; +foreach ($args["options"] as $key => $value) { + $dummy .= $key . ' -> ' . $value . ' '; +} +$dummy .= "] "; + +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseprefix"] = $prefix . $test_num; +$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; +$args["active"] = 0; +$args["public"] = 1; + +$additionalInfo = $dummy . + " active -> {$args['active']}, public -> {$args['public']}"; +$args["notes"] = "test project created using XML-RPC-API -
    {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}() ::: "; + +$args = array(); + +// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled +$args["options"] = array( + 'requirementsEnabled' => 0, + 'testPriorityEnabled' => 0, + 'automationEnabled' => 0, + 'inventoryEnabled' => 0 +); +$dummy = 'Options['; +foreach ($args["options"] as $key => $value) { + $dummy .= $key . ' -> ' . $value . ' '; +} +$dummy .= "] "; + +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseprefix"] = $prefix . $test_num; +$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; +$args["active"] = 1; +$args["public"] = 0; + +$additionalInfo = $dummy . + " active -> {$args['active']}, public -> {$args['public']}"; +$args["notes"] = "test project created using XML-RPC-API -
    {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}() ::: "; + +$args = array(); + +// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled +$args["options"] = array( + 'requirementsEnabled' => 0, + 'testPriorityEnabled' => 0, + 'automationEnabled' => 0, + 'inventoryEnabled' => 0 +); +$dummy = 'Options['; +foreach ($args["options"] as $key => $value) { + $dummy .= $key . ' -> ' . $value . ' '; +} +$dummy .= "] "; + +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseprefix"] = $prefix . $test_num; +$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; +$args["active"] = 1; +$args["public"] = 1; + +$additionalInfo = $dummy . + " active -> {$args['active']}, public -> {$args['public']}"; +$args["notes"] = "test project created using XML-RPC-API -
    {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); // ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; -$prefix = uniqid(); -$devKey = '985978c915f50e47a4b1a54a943d1b76'; -$devKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; - -$args=array(); -$args["devKey"] = $devKey; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; - -$dummy = ''; -$additionalInfo = $dummy; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -die(); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; - - -$args=array(); -// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled -$args["options"] = array('requirementsEnabled' => 0); -$dummy = 'Options['; -foreach($args["options"] as $key => $value) -{ - $dummy .= $key . ' -> ' . $value . ' '; -} -$dummy .= "] "; - -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; -$args["active"] = 0; -$args["public"] = 0; - -$additionalInfo = $dummy . " active -> {$args['active']}, public -> {$args['public']}"; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug = true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; - -$args=array(); -// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled -$args["options"] = array('requirementsEnabled' => 0, 'testPriorityEnabled' => 0); -$dummy = 'Options['; -foreach($args["options"] as $key => $value) -{ - $dummy .= $key . ' -> ' . $value . ' '; -} -$dummy .= "] "; - -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; -$args["active"] = 0; -$args["public"] = 0; - -$additionalInfo = $dummy . " active -> {$args['active']}, public -> {$args['public']}"; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; - -$args=array(); - -// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled -$args["options"] = array('requirementsEnabled' => 0, 'testPriorityEnabled' => 0, - 'automationEnabled' => 0 ,'inventoryEnabled' => 0 ); -$dummy = 'Options['; -foreach($args["options"] as $key => $value) -{ - $dummy .= $key . ' -> ' . $value . ' '; -} -$dummy .= "] "; - - -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; -$args["active"] = 0; -$args["public"] = 0; - -$additionalInfo = $dummy . " active -> {$args['active']}, public -> {$args['public']}"; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; - -$args=array(); - -// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled -$args["options"] = array('requirementsEnabled' => 0, 'testPriorityEnabled' => 0, - 'automationEnabled' => 0 ,'inventoryEnabled' => 0 ); -$dummy = 'Options['; -foreach($args["options"] as $key => $value) -{ - $dummy .= $key . ' -> ' . $value . ' '; -} -$dummy .= "] "; - - -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; -$args["active"] = 0; -$args["public"] = 1; - -$additionalInfo = $dummy . " active -> {$args['active']}, public -> {$args['public']}"; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; - -$args=array(); - -// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled -$args["options"] = array('requirementsEnabled' => 0, 'testPriorityEnabled' => 0, - 'automationEnabled' => 0 ,'inventoryEnabled' => 0 ); -$dummy = 'Options['; -foreach($args["options"] as $key => $value) -{ - $dummy .= $key . ' -> ' . $value . ' '; -} -$dummy .= "] "; - - -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; -$args["active"] = 1; -$args["public"] = 0; - -$additionalInfo = $dummy . " active -> {$args['active']}, public -> {$args['public']}"; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; - -$args=array(); - -// requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled -$args["options"] = array('requirementsEnabled' => 0, 'testPriorityEnabled' => 0, - 'automationEnabled' => 0 ,'inventoryEnabled' => 0 ); -$dummy = 'Options['; -foreach($args["options"] as $key => $value) -{ - $dummy .= $key . ' -> ' . $value . ' '; -} -$dummy .= "] "; - - -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; -$args["active"] = 1; -$args["public"] = 1; - -$additionalInfo = $dummy . " active -> {$args['active']}, public -> {$args['public']}"; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// ------------------------------------------------------------------------------------ \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProjectWithITS.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProjectWithITS.php index e99044472f..2ca44e9124 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProjectWithITS.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestProjectWithITS.php @@ -1,70 +1,66 @@ - {$additionalInfo}"; - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -//runTest($client,$method,$args); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}() ::: "; -$prefix = uniqid(); -$devKey = '985978c915f50e47a4b1a54a943d1b76'; -$devKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; - -$args=array(); -$args["devKey"] = $devKey; -$args["testcaseprefix"] = $prefix . $test_num; -$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; -$args["itsname"] = "jira-testlink.jira"; -$args["itsenabled"] = 1; - -$dummy = ''; -$additionalInfo = $dummy; -$args["notes"]="test project created using XML-RPC-API -
    {$additionalInfo}"; - -echo $unitTestDescription . ' ' . $additionalInfo; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); + {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}() ::: "; +$prefix = uniqid(); +$devKey = '985978c915f50e47a4b1a54a943d1b76'; +$devKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; + +$args = array(); +$args["devKey"] = $devKey; +$args["testcaseprefix"] = $prefix . $test_num; +$args["testprojectname"] = "API Methods Test Project {$args['testcaseprefix']}"; +$args["itsname"] = "jira-testlink.jira"; +$args["itsenabled"] = 1; + +$dummy = ''; +$additionalInfo = $dummy; +$args["notes"] = "test project created using XML-RPC-API -
    {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestSuite.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestSuite.php index 3b108610a7..3ced2bbf51 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestSuite.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateTestSuite.php @@ -1,76 +1,70 @@ - default behaviour BLOCK => will not be created -// use name of existent Test Suite in parentid, request renaming -// - -$method='createTestSuite'; - - -$unitTestDescription="Test - $method"; -$test_num = 0; -$tlDevKey = '985978c915f50e47a4b1a54a943d1b76'; -$tlDevKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $tlDevKey; - -// ------------------------------------------------------------- -$test_num++; -$additionalInfo = 'Using Test Project PREFIX'; -$args=array(); -$args["devKey"]=$tlDevKey; - -$args["prefix"]='ZTZ'; -$args["testsuitename"]='TS API 200.0'; -$args["details"]='This has been created by XMLRPC API Call'; - -// $args["parentid"]=16; -$args["checkduplicatedname"]=1; -$args["actiononduplicatedname"]='generate_new'; -$args["order"]=1; - - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); -// ------------------------------------------------------------- -$test_num++; -$additionalInfo = 'Using Test Project ID'; -$args=array(); -$args["devKey"]=$tlDevKey; - -$args["testprojectid"]=1046; -$args["testsuitename"]='TS API 2'; -$args["details"]='This has been created by XMLRPC API Call'; - -// $args["parentid"]=16; -$args["checkduplicatedname"]=1; -$args["actiononduplicatedname"]='generate_new'; -$args["order"]=1; - - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); \ No newline at end of file + default behaviour BLOCK => will not be created +// use name of existent Test Suite in parentid, request renaming +// + +$method = 'createTestSuite'; + +$unitTestDescription = "Test - $method"; +$test_num = 0; +$tlDevKey = '985978c915f50e47a4b1a54a943d1b76'; +$tlDevKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $tlDevKey; + +// ------------------------------------------------------------- +$test_num ++; +$additionalInfo = 'Using Test Project PREFIX'; +$args = array(); +$args["devKey"] = $tlDevKey; + +$args["prefix"] = 'ZTZ'; +$args["testsuitename"] = 'TS API 200.0'; +$args["details"] = 'This has been created by XMLRPC API Call'; + +$args["checkduplicatedname"] = 1; +$args["actiononduplicatedname"] = 'generate_new'; +$args["order"] = 1; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); +// ------------------------------------------------------------- +$test_num ++; +$additionalInfo = 'Using Test Project ID'; +$args = array(); +$args["devKey"] = $tlDevKey; + +$args["testprojectid"] = 1046; +$args["testsuitename"] = 'TS API 2'; +$args["details"] = 'This has been created by XMLRPC API Call'; + +$args["checkduplicatedname"] = 1; +$args["actiononduplicatedname"] = 'generate_new'; +$args["order"] = 1; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientCreateUser.php b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateUser.php new file mode 100644 index 0000000000..2389e0753e --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientCreateUser.php @@ -0,0 +1,43 @@ +debug=$debug; + +runTest($client,$method,$args,$test_num); + diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteExecution.php b/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteExecution.php index 28678ea480..9bb45f2f07 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteExecution.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteExecution.php @@ -1,32 +1,32 @@ -debug=$debug; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -?> \ No newline at end of file +debug = $debug; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestCaseSteps.php b/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestCaseSteps.php index 2bac34b587..ac01fb9e75 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestCaseSteps.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestCaseSteps.php @@ -1,34 +1,36 @@ -debug=$debug; -runTest($client,$method,$args,$tcCounter); -// ---------------------------------------------------------------------------------------------------- -?> \ No newline at end of file +debug = $debug; +runTest($client, $method, $args, $tcCounter); +// ---------------------------------------------------------------------------------------------------- +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestProject.php b/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestProject.php index 26d42babc6..5c3afc9aa1 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestProject.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientDeleteTestProject.php @@ -1,37 +1,35 @@ -debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); +debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientDoesUserExist.php b/lib/api/xmlrpc/v1/sample_clients/php/clientDoesUserExist.php index 2158c88ad3..faae9ccaa3 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientDoesUserExist.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientDoesUserExist.php @@ -1,60 +1,58 @@ -User does not exist
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["user"]='admin'; - -$additionalInfo='
    User exists
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); +User does not exist
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["user"] = 'admin'; + +$additionalInfo = '
    User exists
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); // --------------------------------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetAllExecutionsResults.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetAllExecutionsResults.php index 8aba0ecba4..51272543bb 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetAllExecutionsResults.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetAllExecutionsResults.php @@ -1,89 +1,63 @@ -debug=$debug; -runTest($client,$method,$args); -*/ -// -------------------------------------------------------------------- - -// -------------------------------------------------------------------- -$args=array(); -$args["devKey"]='developer'; -$args["testplanid"]=133683; -//$args["testcaseexternalid"]='EW-1'; //121690 -$args["testcaseid"]=121690; - -$args["buildid"]=4; -$unitTestDescription="Test - {$method} - ONLY BUILD ID Filter => " . $args["buildid"]; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -die(); -// -------------------------------------------------------------------- - -// -------------------------------------------------------------------- -$args=array(); -$args["devKey"]='DEV_KEY'; -$args["testplanid"]=3; -$args["testcaseexternalid"]='PJH-1'; -$args["buildname"]='1'; -$unitTestDescription="Test - {$method} - ONLY BUILD NAME Filter => " . $args["buildname"]; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// -------------------------------------------------------------------- - -// -------------------------------------------------------------------- -$args=array(); -$args["devKey"]='DEV_KEY'; -$args["testplanid"]=10; -$args["testcaseexternalid"]='PJH-1'; -// $args["buildname"]='1'; -$args["platformname"]='Ferrari'; -$unitTestDescription="Test - {$method} - ONLY PLATFORM NAME Filter => " . $args["platformname"]; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// -------------------------------------------------------------------- + " . + $args["buildid"]; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +die(); + +$args = array(); +$args["devKey"] = 'DEV_KEY'; +$args["testplanid"] = 3; +$args["testcaseexternalid"] = 'PJH-1'; +$args["buildname"] = '1'; +$unitTestDescription = "Test - {$method} - ONLY BUILD NAME Filter => " . + $args["buildname"]; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +$args = array(); +$args["devKey"] = 'DEV_KEY'; +$args["testplanid"] = 10; +$args["testcaseexternalid"] = 'PJH-1'; +$args["platformname"] = 'Ferrari'; +$unitTestDescription = "Test - {$method} - ONLY PLATFORM NAME Filter => " . + $args["platformname"]; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetBuilds.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetBuilds.php index e1104df141..63cf86f995 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetBuilds.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetBuilds.php @@ -1,46 +1,45 @@ -debug=$debug; -runTest($client,$method,$args); - -// ------------------------------------------------------------------------------------- -$method='getLatestBuildForTestPlan'; -$unitTestDescription="Test - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=3; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -?> \ No newline at end of file +debug = $debug; +runTest($client, $method, $args); + +// ------------------------------------------------------------------------------------- +$method = 'getLatestBuildForTestPlan'; +$unitTestDescription = "Test - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 3; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetExecCountersByBuild.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetExecCountersByBuild.php index 44488e28fd..9fb2b40139 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetExecCountersByBuild.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetExecCountersByBuild.php @@ -1,29 +1,27 @@ -Simple client to test method:' . $method . '()'; - - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=337058; -$client = new IXR_Client($server_url); -$client->debug=true; -runTest($client,$method,$args); - -?> \ No newline at end of file +Simple client to test method:' . $method . '()'; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 337058; +$client = new IXR_Client($server_url); +$client->debug = true; +runTest($client, $method, $args); + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetFirstLevelTestSuitesForTestProject.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetFirstLevelTestSuitesForTestProject.php index 8f1d6ea329..9fb47be8b6 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetFirstLevelTestSuitesForTestProject.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetFirstLevelTestSuitesForTestProject.php @@ -1,35 +1,34 @@ -debug=$debug; -runTest($client,$method,$args); -?> \ No newline at end of file +debug = $debug; +runTest($client, $method, $args); +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetFullPath.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetFullPath.php index 3ce56e4601..d5c88eb9ea 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetFullPath.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetFullPath.php @@ -1,109 +1,117 @@ - Array - */ -require_once 'util.php'; -require_once 'sample.inc.php'; -show_api_db_sample_msg(); - -$method='getFullPath'; -$unitTestDescription="Test - {$method}"; -$idx=1; - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["nodeid"]=3312; -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; -// -------------------------------------------------------------------------- - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["nodeid"]=array(3312,3314,3316); -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -// -------------------------------------------------------------------------- - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["nodeid"]=array(3333312,3314,3316); -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; -// -------------------------------------------------------------------------- - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["nodeid"]='A'; -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["nodeid"]=-1; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["nodeid"]=1; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -// -------------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["nodeid"]=419; - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$idx); -$idx++; - -?> \ No newline at end of file + Array + */ +require_once 'util.php'; +require_once 'sample.inc.php'; +show_api_db_sample_msg(); + +$method = 'getFullPath'; +$unitTestDescription = "Test - {$method}"; +$idx = 1; + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["nodeid"] = 3312; +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; +// -------------------------------------------------------------------------- + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["nodeid"] = array( + 3312, + 3314, + 3316 +); +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +// -------------------------------------------------------------------------- + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["nodeid"] = array( + 3333312, + 3314, + 3316 +); +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; +// -------------------------------------------------------------------------- + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["nodeid"] = 'A'; +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["nodeid"] = - 1; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["nodeid"] = 1; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +// -------------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["nodeid"] = 419; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $idx); +$idx ++; + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetLastExecutionResult.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetLastExecutionResult.php index a3977384e6..48c45fb874 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetLastExecutionResult.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetLastExecutionResult.php @@ -1,91 +1,64 @@ -debug=$debug; -runTest($client,$method,$args); -*/ -// -------------------------------------------------------------------- - -// -------------------------------------------------------------------- -$args=array(); -$args["devKey"]='eb6fa75e125944e68739514937d63659'; -$args["testplanid"]=189; -$args["testcaseexternalid"]='AF-1'; -// $args["buildid"]=4; -$unitTestDescription="Test - {$method} - ONLY BUILD ID Filter => " . $args["buildid"]; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -die(); -// -------------------------------------------------------------------- - -// -------------------------------------------------------------------- -$args=array(); -$args["devKey"]='DEV_KEY'; -$args["testplanid"]=3; -$args["testcaseexternalid"]='PJH-1'; -$args["buildname"]='1'; -$unitTestDescription="Test - {$method} - ONLY BUILD NAME Filter => " . $args["buildname"]; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// -------------------------------------------------------------------- - -// -------------------------------------------------------------------- -$args=array(); -$args["devKey"]='DEV_KEY'; -$args["testplanid"]=10; -$args["testcaseexternalid"]='PJH-1'; -// $args["buildname"]='1'; -$args["platformname"]='Ferrari'; -$unitTestDescription="Test - {$method} - ONLY PLATFORM NAME Filter => " . $args["platformname"]; - -$debug=true; -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// -------------------------------------------------------------------- + " . + $args["buildid"]; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +die(); + +$args = array(); +$args["devKey"] = 'DEV_KEY'; +$args["testplanid"] = 3; +$args["testcaseexternalid"] = 'PJH-1'; +$args["buildname"] = '1'; +$unitTestDescription = "Test - {$method} - ONLY BUILD NAME Filter => " . + $args["buildname"]; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +$args = array(); +$args["devKey"] = 'DEV_KEY'; +$args["testplanid"] = 10; +$args["testcaseexternalid"] = 'PJH-1'; +$args["platformname"] = 'Ferrari'; +$unitTestDescription = "Test - {$method} - ONLY PLATFORM NAME Filter => " . + $args["platformname"]; + +$debug = true; +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectKeywords.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectKeywords.php index b90c9fdb49..911e00352a 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectKeywords.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectKeywords.php @@ -1,31 +1,30 @@ -debug=$debug; - -runTest($client,$method,$args); \ No newline at end of file +debug = $debug; + +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectPlatforms.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectPlatforms.php index 3a516b8dc0..3737cdf6ae 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectPlatforms.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectPlatforms.php @@ -1,56 +1,53 @@ -debug=$debug; - -runTest($client,$method,$args,$test_num); - - -// ------------------------------------------------ -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=$tlDevKey; -$args["testprojectid"]=1046; -$additionalInfo='Access By Test Project ID'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args,$test_num); \ No newline at end of file +debug = $debug; + +runTest($client, $method, $args, $test_num); + +// ------------------------------------------------ +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = $tlDevKey; +$args["testprojectid"] = 1046; +$additionalInfo = 'Access By Test Project ID'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args, $test_num); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectTestPlans.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectTestPlans.php index 2abfccd35e..285b4d5e05 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectTestPlans.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjectTestPlans.php @@ -1,36 +1,35 @@ -debug=$debug; - -runTest($client,$method,$args); -?> \ No newline at end of file +debug = $debug; + +runTest($client, $method, $args); +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjects.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjects.php index 007e39cfa2..9bc5d1aa97 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjects.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetProjects.php @@ -1,45 +1,42 @@ -debug=$debug; -$answer = runTest($client,$method,$args); -new dBug($answer); - -$items_qty = count($answer); -foreach($answer as $item) -{ - if( isset($item['name']) ) - { - echo 'name:' . htmlentities($item['name']) . '
    '; - echo 'name:' . htmlentities(utf8_decode($item['name'])) . '
    '; - } -} -?> \ No newline at end of file +debug = $debug; +$answer = runTest($client, $method, $args); +new dBug($answer); + +$items_qty = count($answer); +foreach ($answer as $item) { + if (isset($item['name'])) { + echo 'name:' . htmlentities($item['name']) . '
    '; + echo 'name:' . htmlentities(utf8_decode($item['name'])) . '
    '; + } +} +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetReqCoverage.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetReqCoverage.php new file mode 100644 index 0000000000..421fd30f10 --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetReqCoverage.php @@ -0,0 +1,27 @@ +debug=$debug; +$answer = runTest($client,$method,$args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetRequirement.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetRequirement.php new file mode 100644 index 0000000000..3a31476152 --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetRequirement.php @@ -0,0 +1,65 @@ + latest is returned + +$args=array(); +$args["devKey"]= $devKey; +$args["testprojectid"] = 10; +$args["requirementid"] = 26; + +$client = new IXR_Client($server_url); +$client->debug=true; +$answer = runTest($client, $method, $args); + +// Get a requirement from reqID +// with a version number + +$args=array(); +$args["devKey"]= $devKey; +$args["testprojectid"] = 10; +$args["requirementid"] = 26; +$args["version"] = 2; + +$client = new IXR_Client($server_url); +$client->debug=true; +$answer = runTest($client, $method, $args); + +// Get a requirement from reqID +// with a version ID + +$args=array(); +$args["devKey"]= $devKey; +$args["testprojectid"] = 10; +$args["requirementid"] = 26; +$args["requirementversionid"] = 123; + +$client = new IXR_Client($server_url); +$client->debug=true; +$answer = runTest($client, $method, $args); + +// Get a requirement from reqDocID + +$args=array(); +$args["devKey"]= $devKey; +$args["testprojectid"] = 10; +$args["requirementdocid"] = "R1"; + +$client = new IXR_Client($server_url); +$client->debug=true; +$answer = runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetRequirements.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetRequirements.php new file mode 100644 index 0000000000..919c5db1f3 --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetRequirements.php @@ -0,0 +1,27 @@ +debug=$debug; +$answer = runTest($client,$method,$args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCase.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCase.php index 0d40bb909f..ad61eb96bb 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCase.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCase.php @@ -1,95 +1,93 @@ -debug=$debug; - -runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; -$args["testcaseexternalid"]='API-2'; -$args["version"]=1; -$additionalInfo=''; - -$debug=true; -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; -$args["testcaseid"]='1667'; -$args["version"]=1; -$additionalInfo=''; - -$debug=true; -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; -$args["testcaseexternalid"]='API-2'; -$args["version"]=3; -$additionalInfo=''; - -$debug=true; -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); +debug = $debug; + +runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; +$args["testcaseexternalid"] = 'API-2'; +$args["version"] = 1; +$additionalInfo = ''; + +$debug = true; +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; +$args["testcaseid"] = '1667'; +$args["version"] = 1; +$additionalInfo = ''; + +$debug = true; +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $devKey; +$args["testcaseexternalid"] = 'API-2'; +$args["version"] = 3; +$additionalInfo = ''; + +$debug = true; +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); // --------------------------------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAssignedTester.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAssignedTester.php index d09dd93241..22273a1176 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAssignedTester.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAssignedTester.php @@ -1,154 +1,136 @@ -debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +die(); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Test Plan ID"; + +$args = array(); +$args["devKey"] = $devKey; +$args["testcaseexternalid"] = 'GK-1'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Test Case "; + +$args = array(); +$args["devKey"] = $devKey; +$args["testplanid"] = 9; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Build Name "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-1'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Test Plan ID "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 900000; +$args["testcaseexternalid"] = 'GK-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Test Case External ID "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-WRONG-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Build Name "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); -$method="getTestCaseAssignedTester"; -$utc = 0; -$devKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : 'admin'; - -// --------------------------------------------------------------------------------------- -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - All OK"; - -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 197; -$args["testcaseexternalid"] = 'SK-1'; -// $args["platformname"] = 'P1'; -$args["buildname"] = '1'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -die(); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Test Plan ID"; - -$args=array(); -$args["devKey"] = $devKey; -// $args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Test Case "; - -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 9; -// $args["testcaseexternalid"] = 'GK-1'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Build Name "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -// $args["buildname"] = '1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Test Plan ID "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 900000; -$args["testcaseexternalid"] = 'GK-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Test Case External ID "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-WRONG-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Build Name "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAttachments.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAttachments.php index 5a437cbcc8..4c403fa29b 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAttachments.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseAttachments.php @@ -1,31 +1,29 @@ -debug=$debug; -runTest($client,$method,$args); \ No newline at end of file +debug = $debug; +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseBugs.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseBugs.php index cb64717cf6..4cd238678c 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseBugs.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseBugs.php @@ -1,27 +1,43 @@ -debug=$debug; -runTest($client,$method,$args); \ No newline at end of file +"; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$args["testplanid"] = "21"; +$args["testcaseid"] = "4"; +runTest($client, $method, $args, 1); + +echo "Test adding a filter on a the build 2
    "; +$args["buildid"] = "2"; +runTest($client, $method, $args, 2); + +echo "Test adding a filter on the build 1
    "; +$args["buildid"] = "1"; +runTest($client, $method, $args, 3); + +echo "Test adding a filter on a platform
    "; +$args["platformid"] = "1"; +runTest($client, $method, $args, 4); + +echo "Test adding a filter on an execution ID
    "; +$args["executionid"] = 1; +runTest($client, $method, $args, 5); + +echo "Test adding a filter on another execution ID
    "; +$args["executionid"] = 3; +runTest($client, $method, $args, 6); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldDesignValue.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldDesignValue.php index f853614bed..66509316a3 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldDesignValue.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldDesignValue.php @@ -1,76 +1,75 @@ - Ask for NON EXISTENT VERSION'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); - -// ----------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testcaseexternalid"]='QAZ-1'; -$args["testprojectid"]=455; -$args["customfieldname"]='M LIST'; -$args["details"]='simple'; -$args["version"]= 2; - -$additionalInfo = ' -> Must be GOOD call'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); - -// ----------------------------------------------------------------------- -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testcaseexternalid"]='QAZ-1'; -$args["testprojectid"]=455; -$args["customfieldname"]='M LIST'; -$args["details"]='simple'; -$args["version"]= 1; - -$additionalInfo = ' -> Another GOOD call but for a DIFFERENT Version '; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); - -?> \ No newline at end of file + Ask for NON EXISTENT VERSION'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +// ----------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testcaseexternalid"] = 'QAZ-1'; +$args["testprojectid"] = 455; +$args["customfieldname"] = 'M LIST'; +$args["details"] = 'simple'; +$args["version"] = 2; + +$additionalInfo = ' -> Must be GOOD call'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +// ----------------------------------------------------------------------- +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testcaseexternalid"] = 'QAZ-1'; +$args["testprojectid"] = 455; +$args["customfieldname"] = 'M LIST'; +$args["details"] = 'simple'; +$args["version"] = 1; + +$additionalInfo = ' -> Another GOOD call but for a DIFFERENT Version '; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldExecutionValue.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldExecutionValue.php index 00f9bd0cfc..b0c68766a6 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldExecutionValue.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseCustomFieldExecutionValue.php @@ -1,112 +1,110 @@ -debug = $debug; +runTest($client, $method, $args, $test_num); +// ----------------------------------------------------- + +// ----------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = 'admin'; +$args["devKey"] = 'admin'; +$args["testprojectid"] = 279311; +$args["testplanid"] = 279324; +$args["version"] = 2; +$args["executionid"] = 9230; +$args["customfieldname"] = 'STRING4EXEC'; +$args["details"] = 'simple'; +$additionalInfo = ''; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); +// ----------------------------------------------------- + +// ----------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = 'admin'; +$args["testprojectid"] = 279311; +$args["testplanid"] = 173854; + +$args["customfieldname"] = 'STRING4EXEC'; +$args["details"] = 'simple'; +$additionalInfo = ''; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); +// ----------------------------------------------------- + +// ----------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = 'admin'; +$args["testprojectid"] = 279311; +$args["testplanid"] = 279324; +$args["version"] = 17; +$args["executionid"] = 17; + +$args["customfieldname"] = 'STRING4EXEC'; +$args["details"] = 'simple'; +$additionalInfo = ''; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); // ----------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]='admin'; -$args["testprojectid"]=279311; -$args["testplanid"]=279324; -$args["version"]=1; -$args["executionid"]=9229; -$args["customfieldname"]='STRING4EXEC'; -$args["details"]='simple'; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); -// ----------------------------------------------------- - -// ----------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]='admin'; -$args["devKey"]='admin'; -$args["testprojectid"]=279311; -$args["testplanid"]=279324; -$args["version"]=2; -$args["executionid"]=9230; -$args["customfieldname"]='STRING4EXEC'; -$args["details"]='simple'; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); -// ----------------------------------------------------- - -// ----------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]='admin'; -$args["testprojectid"]=279311; -// $args["testplanid"]=279324; -$args["testplanid"]=173854; - -$args["customfieldname"]='STRING4EXEC'; -$args["details"]='simple'; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); -// ----------------------------------------------------- - -// ----------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]='admin'; -$args["testprojectid"]=279311; -$args["testplanid"]=279324; -$args["version"]=17; -$args["executionid"]=17; - -$args["customfieldname"]='STRING4EXEC'; -$args["details"]='simple'; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); -// ----------------------------------------------------- \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseIDByName.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseIDByName.php index f357ef6f67..da04388413 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseIDByName.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseIDByName.php @@ -1,75 +1,74 @@ -debug=$debug; - -runTest($client,$method,$args); -die(); +debug = $debug; + +runTest($client, $method, $args); +die(); +// --------------------------------------------------------------------------------- + +$test_num = 2; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseexternalid"] = 'API-2'; +$args["version"] = 1; +$additionalInfo = ''; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +$test_num = 2; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testcaseid"] = '1667'; +$args["version"] = 1; +$additionalInfo = ''; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); // --------------------------------------------------------------------------------- - -$test_num=2; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseexternalid"]='API-2'; -$args["version"]=1; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -$test_num=2; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testcaseid"]='1667'; -$args["version"]=1; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseRequirements.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseRequirements.php new file mode 100644 index 0000000000..8c0f8965cf --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCaseRequirements.php @@ -0,0 +1,26 @@ +debug=$debug; +$answer = runTest($client,$method,$args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestPlan.php index 4c9c7927fb..9c99e37447 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestPlan.php @@ -1,173 +1,172 @@ -debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); +debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testplanid"] = $tplan_id; +$args["getstepsinfo"] = false; +$args["details"] = 'simple'; +$additionalInfo = '$args["details"]: ->' . $args["details"] . '
    '; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testplanid"] = $tplan_id; +$args["getstepsinfo"] = false; +$args["details"] = 'full'; +$additionalInfo = '$args["details"]: ->' . $args["details"] . '
    '; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testplanid"] = $tplan_id; +$args["getstepsinfo"] = false; +$args["details"] = 'summary'; +$additionalInfo = '$args["details"]: ->' . $args["details"] . '
    '; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testplanid"] = $tplan_id; +$args["keywords"] = 'Key Feature'; +$additionalInfo = 'Filter by Keyword name - JUST ONE KEYWORD'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testplanid"] = $tplan_id; +$args["keywords"] = 'Key Feature,Must have,Obsolete,Performance,System wide,Usability'; +$additionalInfo = 'Filter by Keyword name - Multiple Keywords - ONLY OR Search'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testplanid"] = $tplan_id; +$args["getstepsinfo"] = false; + +$additionalInfo = 'get steps info: -> false'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testplanid"] = $tplan_id; +$args["getstepsinfo"] = true; + +$additionalInfo = 'get steps info: -> true'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); // --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testplanid"]=$tplan_id; -$args["getstepsinfo"]=false; -$args["details"]='simple'; -$additionalInfo = '$args["details"]: ->' . $args["details"] . '
    '; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testplanid"]=$tplan_id; -$args["getstepsinfo"]=false; -$args["details"]='full'; -$additionalInfo = '$args["details"]: ->' . $args["details"] . '
    '; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testplanid"]=$tplan_id; -$args["getstepsinfo"]=false; -$args["details"]='summary'; -$additionalInfo = '$args["details"]: ->' . $args["details"] . '
    '; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testplanid"]=$tplan_id; -$args["keywords"]='Key Feature'; -$additionalInfo='Filter by Keyword name - JUST ONE KEYWORD'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testplanid"]=$tplan_id; -$args["keywords"]='Key Feature,Must have,Obsolete,Performance,System wide,Usability'; -$additionalInfo='Filter by Keyword name - Multiple Keywords - ONLY OR Search'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - - -// --------------------------------------------------------------------------------- -$test_num++; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testplanid"]=$tplan_id; -$args["getstepsinfo"]=false; - -$additionalInfo='get steps info: -> false'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testplanid"]=$tplan_id; -$args["getstepsinfo"]=true; - -$additionalInfo='get steps info: -> true'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestSuite.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestSuite.php index f3406c642a..13e5df1042 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestSuite.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesForTestSuite.php @@ -1,60 +1,59 @@ -debug=$debug; - -runTest($client,$method,$args); - -// --------------------------------------------------------------------------------- -$method='getTestCasesForTestSuite'; -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; -$args["testprojectid"]=12222; -$args["testsuiteid"]=186; -$args["deep"]=false; -$args["details"]='simple'; - -$additionalInfo=' Parameter deep = ' . $args["deep"]; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -runTest($client,$method,$args); \ No newline at end of file +debug = $debug; + +runTest($client, $method, $args); + +// --------------------------------------------------------------------------------- +$method = 'getTestCasesForTestSuite'; +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : DEV_KEY; +$args["testprojectid"] = 12222; +$args["testsuiteid"] = 186; +$args["deep"] = false; +$args["details"] = 'simple'; + +$additionalInfo = ' Parameter deep = ' . $args["deep"]; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesIDByName.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesIDByName.php index 8e1d54fecb..fd603396d7 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesIDByName.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestCasesIDByName.php @@ -1,62 +1,60 @@ -debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testcasename"]='Full speed unload'; -$args["testcasepathname"]='ZATHURA::Holodeck::Apollo 10 Simulation::Unload::Full speed unload'; - -$additionalInfo=''; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -?> \ No newline at end of file +debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testcasename"] = 'Full speed unload'; +$args["testcasepathname"] = 'ZATHURA::Holodeck::Apollo 10 Simulation::Unload::Full speed unload'; + +$additionalInfo = ''; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanByName.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanByName.php index 3bd7a0837c..1c9ee3d0ea 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanByName.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanByName.php @@ -1,38 +1,37 @@ -debug=$debug; - -runTest($client,$method,$args); -?> \ No newline at end of file +debug = $debug; + +runTest($client, $method, $args); +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanPlatforms.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanPlatforms.php index daec8a2f53..69f02c05a1 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanPlatforms.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestPlanPlatforms.php @@ -1,107 +1,103 @@ -Test PLan ID is < 0 => INEXISTENT
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=380; - -$additionalInfo='
    Test Plan HAS NO PLATFORMS
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=1651; - -$additionalInfo='
    Test Plan Has platforms
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["tesid"]=1651; - -$additionalInfo='
    BAD parameter
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -?> \ No newline at end of file +Test PLan ID is < 0 => INEXISTENT
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 380; + +$additionalInfo = '
    Test Plan HAS NO PLATFORMS
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 1651; + +$additionalInfo = '
    Test Plan Has platforms
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["tesid"] = 1651; + +$additionalInfo = '
    BAD parameter
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestProjectByName.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestProjectByName.php index 883aca3c98..549c535326 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestProjectByName.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestProjectByName.php @@ -1,37 +1,36 @@ -debug=$debug; - -runTest($client,$method,$args); -?> \ No newline at end of file +debug = $debug; + +runTest($client, $method, $args); +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuite.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuite.php index cbc648ebf2..c2022f4cf5 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuite.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuite.php @@ -1,73 +1,72 @@ -debug = $debug; -runTest($client,$method,$args); -// ---- - -// ---- -$UTDescr = "{$method} - Test - Call with just test suite name"; -echo $UTDescr; - -$debug = false; -$args=array(); -$args["devKey"] = 'devkey'; -$args["testsuitename"] = 'QAZ-TS'; - -$client = new IXR_Client($server_url); -$client->debug = $debug; -runTest($client,$method,$args); - -// ---- - -// ---- -$UTDescr = "{$method} - Test - Call with just test project prefix"; - -$debug=false; -echo $UTDescr; -$args=array(); -$args["devKey"] = 'devkey'; -$args["prefix"] = 'QUANTAS'; -$args["details"]='simple'; - -$client = new IXR_Client($server_url); -$client->debug = $debug; -runTest($client,$method,$args); -// ---- - -// ---- -$UTDescr = "{$method} - Test "; - -$debug=false; -echo $UTDescr; -$args=array(); -$args["devKey"] = 'devkey'; -$args["testsuitename"] = 'CANNES'; -$args["prefix"] = 'SRM'; -$args["details"]='simple'; - -$client = new IXR_Client($server_url); -$client->debug = $debug; -runTest($client,$method,$args); +debug = $debug; +runTest($client, $method, $args); +// ---- + +// ---- +$UTDescr = "{$method} - Test - Call with just test suite name"; +echo $UTDescr; + +$debug = false; +$args = array(); +$args["devKey"] = 'devkey'; +$args["testsuitename"] = 'QAZ-TS'; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); + +// ---- + +// ---- +$UTDescr = "{$method} - Test - Call with just test project prefix"; + +$debug = false; +echo $UTDescr; +$args = array(); +$args["devKey"] = 'devkey'; +$args["prefix"] = 'QUANTAS'; +$args["details"] = 'simple'; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); +// ---- + +// ---- +$UTDescr = "{$method} - Test "; + +$debug = false; +echo $UTDescr; +$args = array(); +$args["devKey"] = 'devkey'; +$args["testsuitename"] = 'CANNES'; +$args["prefix"] = 'SRM'; +$args["details"] = 'simple'; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); // ---- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestPlan.php index 0c4a5c787a..eebe797c71 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestPlan.php @@ -1,46 +1,45 @@ - - * @package TestlinkAPI - * @link http://testlink.org/api/ - * - * - * - * rev: 20081013 - franciscom - minor improvements to avoid reconfigure server url - * added test of getTestSuitesForTestPlan() - * 20080818 - franciscom - start work on custom field tests - * 20080306 - franciscom - added dBug to improve diagnostic info. - * 20080305 - franciscom - refactored - */ - -require_once 'util.php'; -require_once 'sample.inc.php'; -show_api_db_sample_msg(); - -$method='getTestSuitesForTestPlan'; -$test_num=1; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=3; - -$debug=true; -echo $unitTestDescription; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -?> \ No newline at end of file + + * @package TestlinkAPI + * @link http://testlink.org/api/ + * + * + * + * rev: 20081013 - franciscom - minor improvements to avoid reconfigure server url + * added test of getTestSuitesForTestPlan() + * 20080818 - franciscom - start work on custom field tests + * 20080306 - franciscom - added dBug to improve diagnostic info. + * 20080305 - franciscom - refactored + */ +require_once 'util.php'; +require_once 'sample.inc.php'; +show_api_db_sample_msg(); + +$method = 'getTestSuitesForTestPlan'; +$test_num = 1; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 3; + +$debug = true; +echo $unitTestDescription; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestSuite.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestSuite.php index 28bede51dc..9253fb034d 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestSuite.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTestSuitesForTestSuite.php @@ -1,107 +1,103 @@ -Test suite ID is INEXISTENT
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testsuiteid"]=689; - -$additionalInfo='

    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testsuiteid"]=193; - -$additionalInfo='
    Test Suite HAS NO CHILDREN
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testsuiteid"]=228; - -$additionalInfo=''; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -$answer = runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -?> \ No newline at end of file +Test suite ID is INEXISTENT
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testsuiteid"] = 689; + +$additionalInfo = '

    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testsuiteid"] = 193; + +$additionalInfo = '
    Test Suite HAS NO CHILDREN
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testsuiteid"] = 228; + +$additionalInfo = ''; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +$answer = runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTotalsForPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTotalsForPlan.php index 4b181fa1ee..bf8288a2a1 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetTotalsForPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetTotalsForPlan.php @@ -1,60 +1,58 @@ -Test plan has no platforms
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args); -// --------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=10872; - -$additionalInfo='
    Test Plan has platforms
    '; -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; -echo 'arguments:
    '; -foreach($args as $key => $value) -{ - echo $key . '=' . $value . '
    '; -} - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args); +Test plan has no platforms
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args); +// --------------------------------------------------------------------------------- + +// --------------------------------------------------------------------------------- +$test_num ++; +$unitTestDescription = "Test {$test_num} - {$method}"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 10872; + +$additionalInfo = '
    Test Plan has platforms
    '; +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; +echo 'arguments:
    '; +foreach ($args as $key => $value) { + echo $key . '=' . $value . '
    '; +} + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args); // --------------------------------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientGetUserByLogin.php b/lib/api/xmlrpc/v1/sample_clients/php/clientGetUserByLogin.php index ce4bf6ae57..018a86e738 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientGetUserByLogin.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientGetUserByLogin.php @@ -1,28 +1,27 @@ -debug=$debug; -runTest($client,$method,$args); \ No newline at end of file +debug = $debug; +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientRemovePlatformFromTestPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/clientRemovePlatformFromTestPlan.php index 41a0a570ee..910a12baec 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientRemovePlatformFromTestPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientRemovePlatformFromTestPlan.php @@ -1,29 +1,29 @@ -debug=$debug; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- -?> \ No newline at end of file +debug = $debug; +$answer = runTest($client, $method, $args); +// --------------------------------------------------------------------------------------- +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientRemoveTestCaseKeywords.php b/lib/api/xmlrpc/v1/sample_clients/php/clientRemoveTestCaseKeywords.php index 9e5d0ce75f..7a478173d8 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientRemoveTestCaseKeywords.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientRemoveTestCaseKeywords.php @@ -1,29 +1,33 @@ - array('Barbie'), - 'MAB-2' => array('Barbie')); - - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; -$answer = runTest($client,$method,$args); \ No newline at end of file + array( + 'Barbie' + ), + 'MAB-2' => array( + 'Barbie' + ) +); + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; +$answer = runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResult.php b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResult.php index c681a4094e..615f1c9ff4 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResult.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResult.php @@ -1,140 +1,122 @@ -testplanid=2; -$context->buildid=1; -$context->buildname=null; -$context->platformname=null; -$context->testcaseexternalid='AF-1'; -$context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['blocked']; -$exec->notes="Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -//$exec->user = 'QQ'; -$exec->overwrite=false; - -$debug=false; -echo '
    ' . $unitTestDescription . ''; -echo '
    '; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - - - -/* - function: - - args: - - returns: - -*/ -function executeTestCase($server_url,$context,$exec,$debug=false) -{ - - echo '
    Context is:'; - new dBug($context); - - - echo '
    Execution data is:'; - new dBug($exec); - - echo '
    '; - - - $client = new IXR_Client($server_url); - $client->debug=$debug; - - $data = array(); - $data["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; - $data["status"] = $exec->status; - - if( property_exists($exec, 'user') && !is_null($exec->user) ) - { - $data["user"]=$exec->user; - } - - if( property_exists($exec, 'notes') && !is_null($exec->notes) ) - { - $data["notes"] = $exec->notes; - } - - if( property_exists($exec, 'bugid') && !is_null($exec->bugid) ) - { - $data["bugid"] = $exec->bugid; - } - - if( property_exists($exec, 'overwrite') && !is_null($exec->overwrite) ) - { - $data["overwrite"]=$exec->overwrite; - } - - if( property_exists($exec, 'customfields') && !is_null($exec->customfields) ) - { - $data["customfields"]=$customfields; - } - - - $data["testplanid"] = $context->testplanid; - if( property_exists($context, 'testcaseid') && !is_null($context->testcaseid) ) - { - $data["testcaseid"] = $context->testcaseid; - } - else if( property_exists($context, 'testcaseexternalid') && !is_null($context->testcaseexternalid) ) - { - $data["testcaseexternalid"] = $context->testcaseexternalid; - } - - if( property_exists($context, 'buildid') && !is_null($context->buildid) ) - { - $data["buildid"] = $context->buildid; - } - else if ( property_exists($context, 'buildname') && !is_null($context->buildname) ) - { - $data["buildname"] = $context->buildname; - } - - if( property_exists($context, 'platformname') && !is_null($context->platformname) ) - { - $data["platformname"]=$context->platformname; - } - - - echo '
    Method will be called with following data:'; - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else { - return $client->getResponse(); - } -} \ No newline at end of file +testplanid = 2; +$context->buildid = 1; +$context->buildname = null; +$context->platformname = null; +$context->testcaseexternalid = 'AF-1'; +$context->testcaseid = null; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['blocked']; +$exec->notes = "Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->overwrite = false; + +$debug = false; +echo '
    ' . $unitTestDescription . ''; +echo '
    '; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function executeTestCase($server_url, $context, $exec, $debug = false) +{ + echo '
    Context is:'; + new dBug($context); + + echo '
    Execution data is:'; + new dBug($exec); + + echo '
    '; + + $client = new IXR_Client($server_url); + $client->debug = $debug; + + $data = array(); + $data["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; + $data["status"] = $exec->status; + + if (property_exists($exec, 'user') && ! is_null($exec->user)) { + $data["user"] = $exec->user; + } + + if (property_exists($exec, 'notes') && ! is_null($exec->notes)) { + $data["notes"] = $exec->notes; + } + + if (property_exists($exec, 'bugid') && ! is_null($exec->bugid)) { + $data["bugid"] = $exec->bugid; + } + + if (property_exists($exec, 'overwrite') && ! is_null($exec->overwrite)) { + $data["overwrite"] = $exec->overwrite; + } + + if (property_exists($exec, 'customfields') && ! is_null($exec->customfields)) { + $data["customfields"] = $customfields; + } + + $data["testplanid"] = $context->testplanid; + if (property_exists($context, 'testcaseid') && + ! is_null($context->testcaseid)) { + $data["testcaseid"] = $context->testcaseid; + } elseif (property_exists($context, 'testcaseexternalid') && + ! is_null($context->testcaseexternalid)) { + $data["testcaseexternalid"] = $context->testcaseexternalid; + } + + if (property_exists($context, 'buildid') && ! is_null($context->buildid)) { + $data["buildid"] = $context->buildid; + } elseif (property_exists($context, 'buildname') && + ! is_null($context->buildname)) { + $data["buildname"] = $context->buildname; + } + + if (property_exists($context, 'platformname') && + ! is_null($context->platformname)) { + $data["platformname"] = $context->platformname; + } + + echo '
    Method will be called with following data:'; + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTester.php b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTester.php index b37505d556..4a26fe7c75 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTester.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTester.php @@ -1,134 +1,116 @@ -testplanid=2; -$context->buildid=1; -$context->buildname=null; -$context->platformname=null; -$context->testcaseexternalid='AF-1'; -$context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['blocked']; -$exec->notes="Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->user = 'QQ'; -$exec->overwrite=false; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - - - -/* - function: - - args: - - returns: - -*/ -function executeTestCase($server_url,$context,$exec,$debug=false) -{ - - new dBug($context); - new dBug($exec); - - $client = new IXR_Client($server_url); - $client->debug=$debug; - - $data = array(); - $data["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; - $data["status"] = $exec->status; - - if( property_exists($exec, 'user') && !is_null($exec->user) ) - { - $data["user"]=$exec->user; - } - - if( property_exists($exec, 'notes') && !is_null($exec->notes) ) - { - $data["notes"] = $exec->notes; - } - - if( property_exists($exec, 'bugid') && !is_null($exec->bugid) ) - { - $data["bugid"] = $exec->bugid; - } - - if( property_exists($exec, 'overwrite') && !is_null($exec->overwrite) ) - { - $data["overwrite"]=$exec->overwrite; - } - - if( property_exists($exec, 'customfields') && !is_null($exec->customfields) ) - { - $data["customfields"]=$customfields; - } - - - $data["testplanid"] = $context->testplanid; - if( property_exists($context, 'testcaseid') && !is_null($context->testcaseid) ) - { - $data["testcaseid"] = $context->testcaseid; - } - else if( property_exists($context, 'testcaseexternalid') && !is_null($context->testcaseexternalid) ) - { - $data["testcaseexternalid"] = $context->testcaseexternalid; - } - - if( property_exists($context, 'buildid') && !is_null($context->buildid) ) - { - $data["buildid"] = $context->buildid; - } - else if ( property_exists($context, 'buildname') && !is_null($context->buildname) ) - { - $data["buildname"] = $context->buildname; - } - - if( property_exists($context, 'platformname') && !is_null($context->platformname) ) - { - $data["platformname"]=$context->platformname; - } - - - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) - { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else - { - return $client->getResponse(); - } -} \ No newline at end of file +testplanid = 2; +$context->buildid = 1; +$context->buildname = null; +$context->platformname = null; +$context->testcaseexternalid = 'AF-1'; +$context->testcaseid = null; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['blocked']; +$exec->notes = "Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->user = 'QQ'; +$exec->overwrite = false; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function executeTestCase($server_url, $context, $exec, $debug = false) +{ + new dBug($context); + new dBug($exec); + + $client = new IXR_Client($server_url); + $client->debug = $debug; + + $data = array(); + $data["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; + $data["status"] = $exec->status; + + if (property_exists($exec, 'user') && ! is_null($exec->user)) { + $data["user"] = $exec->user; + } + + if (property_exists($exec, 'notes') && ! is_null($exec->notes)) { + $data["notes"] = $exec->notes; + } + + if (property_exists($exec, 'bugid') && ! is_null($exec->bugid)) { + $data["bugid"] = $exec->bugid; + } + + if (property_exists($exec, 'overwrite') && ! is_null($exec->overwrite)) { + $data["overwrite"] = $exec->overwrite; + } + + if (property_exists($exec, 'customfields') && ! is_null($exec->customfields)) { + $data["customfields"] = $customfields; + } + + $data["testplanid"] = $context->testplanid; + if (property_exists($context, 'testcaseid') && + ! is_null($context->testcaseid)) { + $data["testcaseid"] = $context->testcaseid; + } elseif (property_exists($context, 'testcaseexternalid') && + ! is_null($context->testcaseexternalid)) { + $data["testcaseexternalid"] = $context->testcaseexternalid; + } + + if (property_exists($context, 'buildid') && ! is_null($context->buildid)) { + $data["buildid"] = $context->buildid; + } elseif (property_exists($context, 'buildname') && + ! is_null($context->buildname)) { + $data["buildname"] = $context->buildname; + } + + if (property_exists($context, 'platformname') && + ! is_null($context->platformname)) { + $data["platformname"] = $context->platformname; + } + + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTimestamp.php b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTimestamp.php index 472beadb87..4a8010158d 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTimestamp.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultForcingTimestamp.php @@ -1,136 +1,118 @@ -testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['blocked']; -$exec->notes="Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-09-07 12:00:00'; -$exec->overwrite=false; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - - - -/* - function: - - args: - - returns: - -*/ -function executeTestCase($server_url,$context,$exec,$debug=false) -{ - - new dBug($context); - new dBug($exec); - - $client = new IXR_Client($server_url); - $client->debug=$debug; - - $data = array(); - $data["devKey"] = 'admin'; - $data["status"] = $exec->status; - - if( property_exists($exec, 'user') && !is_null($exec->user) ) - { - $data["user"]=$exec->user; - } - - if( property_exists($exec, 'notes') && !is_null($exec->notes) ) - { - $data["notes"] = $exec->notes; - } - - if( property_exists($exec, 'bugid') && !is_null($exec->bugid) ) - { - $data["bugid"] = $exec->bugid; - } - - if( property_exists($exec, 'overwrite') && !is_null($exec->overwrite) ) - { - $data["overwrite"]=$exec->overwrite; - } - - if( property_exists($exec, 'customfields') && !is_null($exec->customfields) ) - { - $data["customfields"]=$customfields; - } - - if( property_exists($exec, 'timestamp') && !is_null($exec->timestamp) ) - { - $data["timestamp"]=$exec->timestamp; - } - - $data["testplanid"] = $context->testplanid; - if( property_exists($context, 'testcaseid') && !is_null($context->testcaseid) ) - { - $data["testcaseid"] = $context->testcaseid; - } - else if( property_exists($context, 'testcaseexternalid') && !is_null($context->testcaseexternalid) ) - { - $data["testcaseexternalid"] = $context->testcaseexternalid; - } - - if( property_exists($context, 'buildid') && !is_null($context->buildid) ) - { - $data["buildid"] = $context->buildid; - } - else if ( property_exists($context, 'buildname') && !is_null($context->buildname) ) - { - $data["buildname"] = $context->buildname; - } - - if( property_exists($context, 'platformname') && !is_null($context->platformname) ) - { - $data["platformname"]=$context->platformname; - } - - - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) - { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else - { - return $client->getResponse(); - } -} \ No newline at end of file +testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['blocked']; +$exec->notes = "Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-09-07 12:00:00'; +$exec->overwrite = false; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function executeTestCase($server_url, $context, $exec, $debug = false) +{ + new dBug($context); + new dBug($exec); + + $client = new IXR_Client($server_url); + $client->debug = $debug; + + $data = array(); + $data["devKey"] = 'admin'; + $data["status"] = $exec->status; + + if (property_exists($exec, 'user') && ! is_null($exec->user)) { + $data["user"] = $exec->user; + } + + if (property_exists($exec, 'notes') && ! is_null($exec->notes)) { + $data["notes"] = $exec->notes; + } + + if (property_exists($exec, 'bugid') && ! is_null($exec->bugid)) { + $data["bugid"] = $exec->bugid; + } + + if (property_exists($exec, 'overwrite') && ! is_null($exec->overwrite)) { + $data["overwrite"] = $exec->overwrite; + } + + if (property_exists($exec, 'customfields') && ! is_null($exec->customfields)) { + $data["customfields"] = $customfields; + } + + if (property_exists($exec, 'timestamp') && ! is_null($exec->timestamp)) { + $data["timestamp"] = $exec->timestamp; + } + + $data["testplanid"] = $context->testplanid; + if (property_exists($context, 'testcaseid') && + ! is_null($context->testcaseid)) { + $data["testcaseid"] = $context->testcaseid; + } elseif (property_exists($context, 'testcaseexternalid') && + ! is_null($context->testcaseexternalid)) { + $data["testcaseexternalid"] = $context->testcaseexternalid; + } + + if (property_exists($context, 'buildid') && ! is_null($context->buildid)) { + $data["buildid"] = $context->buildid; + } elseif (property_exists($context, 'buildname') && + ! is_null($context->buildname)) { + $data["buildname"] = $context->buildname; + } + + if (property_exists($context, 'platformname') && + ! is_null($context->platformname)) { + $data["platformname"] = $context->platformname; + } + + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITE.php b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITE.php index c52e7d44a1..a788918127 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITE.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITE.php @@ -1,153 +1,135 @@ - - * @package TestlinkAPI - */ - -require_once 'util.php'; -require_once 'sample.inc.php'; -show_api_db_sample_msg(); - -$tcaseStatusCode['passed']='p'; -$tcaseStatusCode['blocked']='b'; -$tcaseStatusCode['failed']='f'; -$tcaseStatusCode['wrong']='w'; -$tcaseStatusCode['departed']='d'; - -// Substitute for tcid and tpid that apply to your project -$unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseID,buildID"; -$testPlanID=337084; -$testCaseExternalID='URN-1'; -$testCaseID=null; -$buildID=142; -// $status=$tcaseStatusCode['blocked']; -$status=$tcaseStatusCode['passed']; - -date_default_timezone_set('UTC'); - -$exec_notes="Call using all EXTERNAL ID ({$testCaseExternalID}) - status={$status} - " . date(DATE_RFC822); -$platformName='Ferrari'; -$overwrite=true; -$bug_id = null; -$customfields = null; -$debug=false; -echo $unitTestDescription; -$response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID, - $buildID,null,$status,$exec_notes,$bug_id,$customfields, - $platformName,$overwrite,$debug); - -echo "
    Result was: "; -// Typically you'd want to validate the result here and probably do something more useful with it -// print_r($response); -new dBug($response); -echo "
    "; - - -// Substitute for tcid and tpid that apply to your project -// $unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseID,buildID"; -// $testPlanID=446; -// $testCaseExternalID='AA-1'; -// $testCaseID=null; -// $buildID=2; -// // $status=$tcaseStatusCode['departed']; -// $status=$tcaseStatusCode['blocked']; -// // $status=$tcaseStatusCode['wrong']; -// // $exec_notes="Call using all INTERNAL ID's ({$testCaseID}) - status={$status}"; -// $exec_notes="Call using all EXTERNAL ID ({$testCaseExternalID}) - status={$status}"; -// $bug_id='999FF'; -// $customfields=array('CF_EXE1' => 'COMODORE64','CF_DT' => mktime(10,10,0,7,29,2009)); -// -// $debug=false; -// echo $unitTestDescription; -// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID, -// $buildID,null,$status,$exec_notes,$bug_id,$customfields,$debug); -// -// echo "
    Result was: "; -// // Typically you'd want to validate the result here and probably do something more useful with it -// // print_r($response); -// new dBug($response); -// echo "
    "; - - - -/* - function: - - args: - - returns: - -*/ -function reportResult($server_url,$tcaseid=null, $tcaseexternalid=null,$tplanid, $buildid=null, - $buildname=null, $status,$notes=null,$bugid=null,$customfields=null, - $platformname=null,$overwrite=false,$debug=false) -{ - - $client = new IXR_Client($server_url); - - $client->debug=$debug; - - $data = array(); - $data["devKey"] = constant("DEV_KEY"); - $data["testplanid"] = $tplanid; - - if( !is_null($bugid) ) - { - $data["bugid"] = $bugid; - } - - if( !is_null($tcaseid) ) - { - $data["testcaseid"] = $tcaseid; - } - else if( !is_null($tcaseexternalid) ) - { - $data["testcaseexternalid"] = $tcaseexternalid; - } - - if( !is_null($buildid) ) - { - $data["buildid"] = $buildid; - } - else if ( !is_null($buildname) ) - { - $data["buildname"] = $buildname; - } - - if( !is_null($notes) ) - { - $data["notes"] = $notes; - } - $data["status"] = $status; - - if( !is_null($customfields) ) - { - $data["customfields"]=$customfields; - } - - if( !is_null($platformname) ) - { - $data["platformname"]=$platformname; - } - - if( !is_null($overwrite) ) - { - $data["overwrite"]=$overwrite; - } - - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) - { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else - { - return $client->getResponse(); - } -} - - -?> \ No newline at end of file + + * @package TestlinkAPI + */ +require_once 'util.php'; +require_once 'sample.inc.php'; +show_api_db_sample_msg(); + +$tcaseStatusCode['passed'] = 'p'; +$tcaseStatusCode['blocked'] = 'b'; +$tcaseStatusCode['failed'] = 'f'; +$tcaseStatusCode['wrong'] = 'w'; +$tcaseStatusCode['departed'] = 'd'; + +// Substitute for tcid and tpid that apply to your project +$unitTestDescription = "Test - Call with valid parameters: testPlanID,testCaseID,buildID"; +$testPlanID = 337084; +$testCaseExternalID = 'URN-1'; +$testCaseID = null; +$buildID = 142; +$status = $tcaseStatusCode['passed']; + +date_default_timezone_set('UTC'); + +$exec_notes = "Call using all EXTERNAL ID ({$testCaseExternalID}) - status={$status} - " . + date(DATE_RFC822); +$platformName = 'Ferrari'; +$overwrite = true; +$bug_id = null; +$customfields = null; +$debug = false; +echo $unitTestDescription; +$response = reportResult($server_url, $testCaseID, $testCaseExternalID, + $testPlanID, $buildID, null, $status, $exec_notes, $bug_id, $customfields, + $platformName, $overwrite, $debug); + +echo "
    Result was: "; +// Typically you'd want to validate the result here and probably do something more useful with it +// print_r($response); +new dBug($response); +echo "
    "; + +// Substitute for tcid and tpid that apply to your project +// $unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseID,buildID"; +// $testPlanID=446; +// $testCaseExternalID='AA-1'; +// $testCaseID=null; +// $buildID=2; +// // $status=$tcaseStatusCode['departed']; +// $status=$tcaseStatusCode['blocked']; +// // $status=$tcaseStatusCode['wrong']; +// // $exec_notes="Call using all INTERNAL ID's ({$testCaseID}) - status={$status}"; +// $exec_notes="Call using all EXTERNAL ID ({$testCaseExternalID}) - status={$status}"; +// $bug_id='999FF'; +// $customfields=array('CF_EXE1' => 'COMODORE64','CF_DT' => mktime(10,10,0,7,29,2009)); +// +// $debug=false; +// echo $unitTestDescription; +// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID, +// $buildID,null,$status,$exec_notes,$bug_id,$customfields,$debug); +// +// echo "
    Result was: "; +// // Typically you'd want to validate the result here and probably do something more useful with it +// // print_r($response); +// new dBug($response); +// echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function reportResult($server_url, $tcaseid = null, $tcaseexternalid = null, + $tplanid, $buildid = null, $buildname = null, $status, $notes = null, + $bugid = null, $customfields = null, $platformname = null, + $overwrite = false, $debug = false) +{ + $client = new IXR_Client($server_url); + + $client->debug = $debug; + + $data = array(); + $data["devKey"] = constant("DEV_KEY"); + $data["testplanid"] = $tplanid; + + if (! is_null($bugid)) { + $data["bugid"] = $bugid; + } + + if (! is_null($tcaseid)) { + $data["testcaseid"] = $tcaseid; + } elseif (! is_null($tcaseexternalid)) { + $data["testcaseexternalid"] = $tcaseexternalid; + } + + if (! is_null($buildid)) { + $data["buildid"] = $buildid; + } elseif (! is_null($buildname)) { + $data["buildname"] = $buildname; + } + + if (! is_null($notes)) { + $data["notes"] = $notes; + } + $data["status"] = $status; + + if (! is_null($customfields)) { + $data["customfields"] = $customfields; + } + + if (! is_null($platformname)) { + $data["platformname"] = $platformname; + } + + if (! is_null($overwrite)) { + $data["overwrite"] = $overwrite; + } + + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITEForcingTimestamp.php b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITEForcingTimestamp.php index 91cdae2033..de3097f0b6 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITEForcingTimestamp.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultOVERWRITEForcingTimestamp.php @@ -1,217 +1,192 @@ -testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-09-07 12:12:30'; -$exec->overwrite = TRUE; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -// -------------------------------------- -$unitTestDescription="Test - Call with BAD TIMESTAMP"; - -$context = new stdClass(); -$context->testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-09-07 12:'; -$exec->overwrite = TRUE; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -// -------------------------------------- -$unitTestDescription="Test - Call with BAD TIMESTAMP"; - -$context = new stdClass(); -$context->testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-31-10 12:'; -$exec->overwrite = TRUE; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -// -------------------------------------- -$unitTestDescription="Test - Call with BAD TIMESTAMP"; - -$context = new stdClass(); -$context->testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-10-10'; -$exec->overwrite = TRUE; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - - - - -/* - function: - - args: - - returns: - -*/ -function executeTestCase($server_url,$context,$exec,$debug=false) -{ - - new dBug($context); - new dBug($exec); - - $client = new IXR_Client($server_url); - $client->debug=$debug; - - $data = array(); - $data["devKey"] = 'admin'; - $data["status"] = $exec->status; - - if( property_exists($exec, 'user') && !is_null($exec->user) ) - { - $data["user"]=$exec->user; - } - - if( property_exists($exec, 'notes') && !is_null($exec->notes) ) - { - $data["notes"] = $exec->notes; - } - - if( property_exists($exec, 'bugid') && !is_null($exec->bugid) ) - { - $data["bugid"] = $exec->bugid; - } - - if( property_exists($exec, 'overwrite') && !is_null($exec->overwrite) ) - { - $data["overwrite"]=$exec->overwrite; - } - - if( property_exists($exec, 'customfields') && !is_null($exec->customfields) ) - { - $data["customfields"]=$customfields; - } - - if( property_exists($exec, 'timestamp') && !is_null($exec->timestamp) ) - { - $data["timestamp"]=$exec->timestamp; - } - - $data["testplanid"] = $context->testplanid; - if( property_exists($context, 'testcaseid') && !is_null($context->testcaseid) ) - { - $data["testcaseid"] = $context->testcaseid; - } - else if( property_exists($context, 'testcaseexternalid') && !is_null($context->testcaseexternalid) ) - { - $data["testcaseexternalid"] = $context->testcaseexternalid; - } - - if( property_exists($context, 'buildid') && !is_null($context->buildid) ) - { - $data["buildid"] = $context->buildid; - } - else if ( property_exists($context, 'buildname') && !is_null($context->buildname) ) - { - $data["buildname"] = $context->buildname; - } - - if( property_exists($context, 'platformname') && !is_null($context->platformname) ) - { - $data["platformname"]=$context->platformname; - } - - - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) - { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else - { - return $client->getResponse(); - } -} \ No newline at end of file +testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-09-07 12:12:30'; +$exec->overwrite = true; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +// -------------------------------------- +$unitTestDescription = "Test - Call with BAD TIMESTAMP"; + +$context = new stdClass(); +$context->testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-09-07 12:'; +$exec->overwrite = true; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +// -------------------------------------- +$unitTestDescription = "Test - Call with BAD TIMESTAMP"; + +$context = new stdClass(); +$context->testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-31-10 12:'; +$exec->overwrite = true; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +// -------------------------------------- +$unitTestDescription = "Test - Call with BAD TIMESTAMP"; + +$context = new stdClass(); +$context->testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-10-10'; +$exec->overwrite = true; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function executeTestCase($server_url, $context, $exec, $debug = false) +{ + new dBug($context); + new dBug($exec); + + $client = new IXR_Client($server_url); + $client->debug = $debug; + + $data = array(); + $data["devKey"] = 'admin'; + $data["status"] = $exec->status; + + if (property_exists($exec, 'user') && ! is_null($exec->user)) { + $data["user"] = $exec->user; + } + + if (property_exists($exec, 'notes') && ! is_null($exec->notes)) { + $data["notes"] = $exec->notes; + } + + if (property_exists($exec, 'bugid') && ! is_null($exec->bugid)) { + $data["bugid"] = $exec->bugid; + } + + if (property_exists($exec, 'overwrite') && ! is_null($exec->overwrite)) { + $data["overwrite"] = $exec->overwrite; + } + + if (property_exists($exec, 'customfields') && ! is_null($exec->customfields)) { + $data["customfields"] = $customfields; + } + + if (property_exists($exec, 'timestamp') && ! is_null($exec->timestamp)) { + $data["timestamp"] = $exec->timestamp; + } + + $data["testplanid"] = $context->testplanid; + if (property_exists($context, 'testcaseid') && + ! is_null($context->testcaseid)) { + $data["testcaseid"] = $context->testcaseid; + } elseif (property_exists($context, 'testcaseexternalid') && + ! is_null($context->testcaseexternalid)) { + $data["testcaseexternalid"] = $context->testcaseexternalid; + } + + if (property_exists($context, 'buildid') && ! is_null($context->buildid)) { + $data["buildid"] = $context->buildid; + } elseif (property_exists($context, 'buildname') && + ! is_null($context->buildname)) { + $data["buildname"] = $context->buildname; + } + + if (property_exists($context, 'platformname') && + ! is_null($context->platformname)) { + $data["platformname"] = $context->platformname; + } + + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWIthExecDuration.php b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWIthExecDuration.php index 0d91cf5397..a48e15336e 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWIthExecDuration.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWIthExecDuration.php @@ -1,45 +1,43 @@ -debug = $debug; +runTest($client, $method, $args, $test_num); -$method='reportTCResult'; - -$tcaseStatusCode['passed']='p'; -$tcaseStatusCode['blocked']='b'; -$tcaseStatusCode['failed']='f'; -$tcaseStatusCode['wrong']='w'; -$tcaseStatusCode['departed']='d'; - -$test_num=0; - -// ----------------------------------------------------- -$test_num++; -$unitTestDescription="Test {$test_num} - {$method}"; - -$args=array(); -$args["devKey"]='admin'; -$args['testcaseexternalid'] = 'IU7206-1'; -$args["testplanid"]=279324; -$args["buildname"]='1.0'; -$args["execduration"]=2.7; -$args['status'] = $tcaseStatusCode['failed']; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); -// ----------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWithSteps.php b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWithSteps.php index e9ff33115a..11c6446bf7 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWithSteps.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientReportTCResultWithSteps.php @@ -1,228 +1,203 @@ -testplanid=308; -// $context->buildid=1; -$context->buildname='1.0'; -//$context->platformname='PLAT01'; -$context->testcaseexternalid='EO-8'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -// $exec->timestamp = '2015-09-07 12:12:30'; -$exec->overwrite = FALSE; -$exec->steps = array( - array('step_number' => 1, - 'result' => 'n', 'notes' => 'no')); - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; -die(); - -// -------------------------------------- -$unitTestDescription="Test - Call with BAD TIMESTAMP"; - -$context = new stdClass(); -$context->testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-09-07 12:'; -$exec->overwrite = TRUE; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -// -------------------------------------- -$unitTestDescription="Test - Call with BAD TIMESTAMP"; - -$context = new stdClass(); -$context->testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-31-10 12:'; -$exec->overwrite = TRUE; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -// -------------------------------------- -$unitTestDescription="Test - Call with BAD TIMESTAMP"; - -$context = new stdClass(); -$context->testplanid=279348; -// $context->buildid=1; -$context->buildname='2.0'; -$context->platformname='PLAT01'; -$context->testcaseexternalid='ZQ-3'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['passed']; -$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; -$exec->customfields = null; -$exec->bugid = null; -$exec->timestamp = '2015-10-10'; -$exec->overwrite = TRUE; - -$debug=false; -echo $unitTestDescription; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - - - - -/* - function: - - args: - - returns: - -*/ -function executeTestCase($server_url,$context,$exec,$debug=false) -{ - - new dBug($context); - new dBug($exec); - - $client = new IXR_Client($server_url); - $client->debug=$debug; - - $data = array(); - // $data["devKey"] = 'admin'; - $data["devKey"] = 'devkey'; - - $data["status"] = $exec->status; - - if( property_exists($exec, 'steps') ) - { - $data["steps"]=$exec->steps; - } - - if( property_exists($exec, 'user') && !is_null($exec->user) ) - { - $data["user"]=$exec->user; - } - - if( property_exists($exec, 'notes') && !is_null($exec->notes) ) - { - $data["notes"] = $exec->notes; - } - - if( property_exists($exec, 'bugid') && !is_null($exec->bugid) ) - { - $data["bugid"] = $exec->bugid; - } - - if( property_exists($exec, 'overwrite') && !is_null($exec->overwrite) ) - { - $data["overwrite"]=$exec->overwrite; - } - - if( property_exists($exec, 'customfields') && !is_null($exec->customfields) ) - { - $data["customfields"]=$customfields; - } - - if( property_exists($exec, 'timestamp') && !is_null($exec->timestamp) ) - { - $data["timestamp"]=$exec->timestamp; - } - - $data["testplanid"] = $context->testplanid; - if( property_exists($context, 'testcaseid') && !is_null($context->testcaseid) ) - { - $data["testcaseid"] = $context->testcaseid; - } - else if( property_exists($context, 'testcaseexternalid') && !is_null($context->testcaseexternalid) ) - { - $data["testcaseexternalid"] = $context->testcaseexternalid; - } - - if( property_exists($context, 'buildid') && !is_null($context->buildid) ) - { - $data["buildid"] = $context->buildid; - } - else if ( property_exists($context, 'buildname') && !is_null($context->buildname) ) - { - $data["buildname"] = $context->buildname; - } - - if( property_exists($context, 'platformname') && !is_null($context->platformname) ) - { - $data["platformname"]=$context->platformname; - } - - - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) - { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else - { - return $client->getResponse(); - } -} \ No newline at end of file +testplanid = 308; +$context->buildname = '1.0'; +$context->testcaseexternalid = 'EO-8'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->overwrite = false; +$exec->steps = array( + array( + 'step_number' => 1, + 'result' => 'n', + 'notes' => 'no' + ) +); + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; +die(); + +// -------------------------------------- +$unitTestDescription = "Test - Call with BAD TIMESTAMP"; + +$context = new stdClass(); +$context->testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-09-07 12:'; +$exec->overwrite = true; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +// -------------------------------------- +$unitTestDescription = "Test - Call with BAD TIMESTAMP"; + +$context = new stdClass(); +$context->testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-31-10 12:'; +$exec->overwrite = true; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +// -------------------------------------- +$unitTestDescription = "Test - Call with BAD TIMESTAMP"; + +$context = new stdClass(); +$context->testplanid = 279348; +$context->buildname = '2.0'; +$context->platformname = 'PLAT01'; +$context->testcaseexternalid = 'ZQ-3'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['passed']; +$exec->notes = "OVERWRITE - Call using all EXTERNAL ID ({$context->testcaseexternalid}) - status={$exec->status}"; +$exec->customfields = null; +$exec->bugid = null; +$exec->timestamp = '2015-10-10'; +$exec->overwrite = true; + +$debug = false; +echo $unitTestDescription; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function executeTestCase($server_url, $context, $exec, $debug = false) +{ + new dBug($context); + new dBug($exec); + + $client = new IXR_Client($server_url); + $client->debug = $debug; + + $data = array(); + $data["devKey"] = 'devkey'; + + $data["status"] = $exec->status; + + if (property_exists($exec, 'steps')) { + $data["steps"] = $exec->steps; + } + + if (property_exists($exec, 'user') && ! is_null($exec->user)) { + $data["user"] = $exec->user; + } + + if (property_exists($exec, 'notes') && ! is_null($exec->notes)) { + $data["notes"] = $exec->notes; + } + + if (property_exists($exec, 'bugid') && ! is_null($exec->bugid)) { + $data["bugid"] = $exec->bugid; + } + + if (property_exists($exec, 'overwrite') && ! is_null($exec->overwrite)) { + $data["overwrite"] = $exec->overwrite; + } + + if (property_exists($exec, 'customfields') && ! is_null($exec->customfields)) { + $data["customfields"] = $customfields; + } + + if (property_exists($exec, 'timestamp') && ! is_null($exec->timestamp)) { + $data["timestamp"] = $exec->timestamp; + } + + $data["testplanid"] = $context->testplanid; + if (property_exists($context, 'testcaseid') && + ! is_null($context->testcaseid)) { + $data["testcaseid"] = $context->testcaseid; + } elseif (property_exists($context, 'testcaseexternalid') && + ! is_null($context->testcaseexternalid)) { + $data["testcaseexternalid"] = $context->testcaseexternalid; + } + + if (property_exists($context, 'buildid') && ! is_null($context->buildid)) { + $data["buildid"] = $context->buildid; + } elseif (property_exists($context, 'buildname') && + ! is_null($context->buildname)) { + $data["buildname"] = $context->buildname; + } + + if (property_exists($context, 'platformname') && + ! is_null($context->platformname)) { + $data["platformname"] = $context->platformname; + } + + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientSample.php b/lib/api/xmlrpc/v1/sample_clients/php/clientSample.php index 67bcabc71d..7a43fe630d 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientSample.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientSample.php @@ -1,171 +1,153 @@ - - * @package TestlinkAPI - * @link http://testlink.org/api/ - * - * rev: 20080306 - franciscom - added dBug to improve diagnostic info. - * 20080305 - franciscom - refactored - */ - -require_once 'util.php'; -require_once 'sample.inc.php'; - -$tcaseStatusCode['passed']='p'; -$tcaseStatusCode['blocked']='b'; -$tcaseStatusCode['failed']='f'; -$tcaseStatusCode['wrong']='w'; -$tcaseStatusCode['departed']='d'; - - - -// Substitute for tcid and tpid that apply to your project -$unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseID,buildID"; -$testPlanID=222; -// $testCaseID=185; -$testCaseID=58; -$testCaseExternalID=null; -$buildID=15; -// $status=$tcaseStatusCode['departed']; -$status=$tcaseStatusCode['blocked']; -// $status=$tcaseStatusCode['wrong']; -$exec_notes="Call using all INTERNAL ID's ({$testCaseID}) - status={$status}"; -$bug_id='999FF'; - -$debug=false; -echo $unitTestDescription; -$response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID, - $buildID,null,$status,$exec_notes,$bug_id,$debug); - -echo "
    Result was: "; -// Typically you'd want to validate the result here and probably do something more useful with it -// print_r($response); -new dBug($response); -echo "
    "; -// -// -// // Now do a wrong build call -// $unitTestDescription="Test - Call with at least one NON EXISTENT parameters: testPlanID,testCaseID,buildID"; -// $testPlanID=95; -// $testCaseID=86; -// $testCaseExternalID=null; -// $buildID=50; -// $exec_notes=""; -// -// //$debug=true; -// $debug=false; -// echo $unitTestDescription; -// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID, -// $buildID,null,$tcaseStatusCode['passed'],$exec_notes,$bug_id,$debug); -// -// echo "
    Result was: "; -// new dBug($response); -// echo "
    "; -// -// // ---------------------------------------------------------------------------------------- -// // Now do a build name call -// $unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseID,buildName"; -// $testPlanID=95; -// $testCaseID=83; -// $testCaseExternalID=''; -// $buildName="Spock"; -// $exec_notes="Call using all Build by name ({$testCaseID})"; -// -// //$debug=true; -// $debug=false; -// echo $unitTestDescription; -// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID,null, -// $buildName,$tcaseStatusCode['blocked'],$exec_notes,$bug_id,$debug); -// -// echo "
    Result was: "; -// new dBug($response); -// echo "
    "; -// // ---------------------------------------------------------------------------------------- -// -// -// // Now do a build name call -// $unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseExternalID,buildName"; -// $testPlanID=95; -// $testCaseID=null; -// $testCaseExternalID='ESP-3'; -// $buildName="Spock"; -// // $exec_notes="Call using Test Case External ID and Build by Name"; -// $exec_notes=null; -// -// //$debug=true; -// $debug=false; -// echo $unitTestDescription; -// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID,null, -// $buildName,$tcaseStatusCode['failed'],$exec_notes,$bug_id,$debug); -// -// echo "
    Result was: "; -// new dBug($response); -// echo "
    "; - - - -/* - function: - - args: - - returns: - -*/ -function reportResult($server_url,$tcaseid=null, $tcaseexternalid=null,$tplanid, $buildid=null, - $buildname=null, $status,$notes=null,$bugid=null,$debug=false) -{ - - $client = new IXR_Client($server_url); - - $client->debug=$debug; - - $data = array(); - $data["devKey"] = constant("DEV_KEY"); - $data["testplanid"] = $tplanid; - - if( !is_null($bugid) ) - { - $data["bugid"] = $bugid; - } - - if( !is_null($tcaseid) ) - { - $data["testcaseid"] = $tcaseid; - } - else if( !is_null($tcaseexternalid) ) - { - $data["testcaseexternalid"] = $tcaseexternalid; - } - - if( !is_null($buildid) ) - { - $data["buildid"] = $buildid; - } - else if ( !is_null($buildname) ) - { - $data["buildname"] = $buildname; - } - - if( !is_null($notes) ) - { - $data["notes"] = $notes; - } - $data["status"] = $status; - - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) - { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else - { - return $client->getResponse(); - } -} - - -?> \ No newline at end of file + + * @package TestlinkAPI + * @link http://testlink.org/api/ + * + * rev: 20080306 - franciscom - added dBug to improve diagnostic info. + * 20080305 - franciscom - refactored + */ +require_once 'util.php'; +require_once 'sample.inc.php'; + +$tcaseStatusCode['passed'] = 'p'; +$tcaseStatusCode['blocked'] = 'b'; +$tcaseStatusCode['failed'] = 'f'; +$tcaseStatusCode['wrong'] = 'w'; +$tcaseStatusCode['departed'] = 'd'; + +// Substitute for tcid and tpid that apply to your project +$unitTestDescription = "Test - Call with valid parameters: testPlanID,testCaseID,buildID"; +$testPlanID = 222; +$testCaseID = 58; +$testCaseExternalID = null; +$buildID = 15; +$status = $tcaseStatusCode['blocked']; +$exec_notes = "Call using all INTERNAL ID's ({$testCaseID}) - status={$status}"; +$bug_id = '999FF'; + +$debug = false; +echo $unitTestDescription; +$response = reportResult($server_url, $testCaseID, $testCaseExternalID, + $testPlanID, $buildID, null, $status, $exec_notes, $bug_id, $debug); + +echo "
    Result was: "; +// Typically you'd want to validate the result here and probably do something more useful with it +// print_r($response); +new dBug($response); +echo "
    "; + +// +// +// // Now do a wrong build call +// $unitTestDescription="Test - Call with at least one NON EXISTENT parameters: testPlanID,testCaseID,buildID"; +// $testPlanID=95; +// $testCaseID=86; +// $testCaseExternalID=null; +// $buildID=50; +// $exec_notes=""; +// +// //$debug=true; +// $debug=false; +// echo $unitTestDescription; +// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID, +// $buildID,null,$tcaseStatusCode['passed'],$exec_notes,$bug_id,$debug); +// +// echo "
    Result was: "; +// new dBug($response); +// echo "
    "; +// +// // ---------------------------------------------------------------------------------------- +// // Now do a build name call +// $unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseID,buildName"; +// $testPlanID=95; +// $testCaseID=83; +// $testCaseExternalID=''; +// $buildName="Spock"; +// $exec_notes="Call using all Build by name ({$testCaseID})"; +// +// //$debug=true; +// $debug=false; +// echo $unitTestDescription; +// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID,null, +// $buildName,$tcaseStatusCode['blocked'],$exec_notes,$bug_id,$debug); +// +// echo "
    Result was: "; +// new dBug($response); +// echo "
    "; +// // ---------------------------------------------------------------------------------------- +// +// +// // Now do a build name call +// $unitTestDescription="Test - Call with valid parameters: testPlanID,testCaseExternalID,buildName"; +// $testPlanID=95; +// $testCaseID=null; +// $testCaseExternalID='ESP-3'; +// $buildName="Spock"; +// // $exec_notes="Call using Test Case External ID and Build by Name"; +// $exec_notes=null; +// +// //$debug=true; +// $debug=false; +// echo $unitTestDescription; +// $response = reportResult($server_url,$testCaseID,$testCaseExternalID,$testPlanID,null, +// $buildName,$tcaseStatusCode['failed'],$exec_notes,$bug_id,$debug); +// +// echo "
    Result was: "; +// new dBug($response); +// echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function reportResult($server_url, $tcaseid = null, $tcaseexternalid = null, + $tplanid, $buildid = null, $buildname = null, $status, $notes = null, + $bugid = null, $debug = false) +{ + $client = new IXR_Client($server_url); + + $client->debug = $debug; + + $data = array(); + $data["devKey"] = constant("DEV_KEY"); + $data["testplanid"] = $tplanid; + + if (! is_null($bugid)) { + $data["bugid"] = $bugid; + } + + if (! is_null($tcaseid)) { + $data["testcaseid"] = $tcaseid; + } elseif (! is_null($tcaseexternalid)) { + $data["testcaseexternalid"] = $tcaseexternalid; + } + + if (! is_null($buildid)) { + $data["buildid"] = $buildid; + } elseif (! is_null($buildname)) { + $data["buildname"] = $buildname; + } + + if (! is_null($notes)) { + $data["notes"] = $notes; + } + $data["status"] = $status; + + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientSample2.php b/lib/api/xmlrpc/v1/sample_clients/php/clientSample2.php index c1e134b6ec..f49cc5997c 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientSample2.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientSample2.php @@ -1,175 +1,165 @@ - - * @package TestlinkAPI - * @link http://testlink.org/api/ - * - * - * - * rev: 20081013 - franciscom - minor improvements to avoid reconfigure server url - * added test of getTestSuitesForTestPlan() - * 20080818 - franciscom - start work on custom field tests - * 20080306 - franciscom - added dBug to improve diagnostic info. - * 20080305 - franciscom - refactored - */ - -require_once 'util.php'; -require_once 'sample.inc.php'; - -$unitTestDescription="Test - getTestCasesForTestPlan"; - -/** -* getTestCasesForTestPlan -* List test cases linked to a test plan -* -* @param struct $args -* @param string $args["devKey"] -* @param int $args["testplanid"] -* @param int $args["testcaseid"] - optional -* @param int $args["buildid"] - optional -* @param int $args["keywordid"] - optional -* @param boolean $args["executed"] - optional -* @param int $args["$assignedto"] - optional -* @param string $args["executestatus"] - optional -* @return mixed $resultInfo -*/ -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=61579; - -// optional -// $args["testcaseid"] - optional -// $args["buildid"] - optional -// $args["keywordid"] - optional -// $args["executed"] - optional -// $args["$assignedto"] - optional -// $args["executestatus"] - optional - -//$debug=true; -$debug=false; -echo $unitTestDescription; - - -$client = new IXR_Client($server_url); -$client->debug=$debug; - - - -new dBug($args); -if(!$client->query('tl.getTestCasesForTestPlan', $args)) -{ - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - $response=null; -} -else -{ - $response=$client->getResponse(); -} - -echo "
    Result was: "; -// Typically you'd want to validate the result here and probably do something more useful with it -// print_r($response); -new dBug($response); -echo "
    "; - - -// 20080518 - franciscom -$unitTestDescription="Test - createTestProject"; -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testprojectname"]='API TestProject'; - -$debug=true; -//$debug=false; -echo $unitTestDescription; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -new dBug($args); -if(!$client->query('tl.createTestProject', $args)) -{ - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - $response=null; -} -else -{ - $response=$client->getResponse(); -} - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -// ---------------------------------------------------------------------- -$method='getTestCaseCustomFieldDesignValue'; -$client_query='tl.' . $method; -$unitTestDescription="Test - $method"; - -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testcaseexternalid"]='ESP-1'; -// $args["customfieldname"]='TESTER_EXPERIENCE'; -$args["customfieldname"]='SSCRIPT_CF1'; - -$debug=true; - -echo $unitTestDescription; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -new dBug($args); -if(!$client->query($client_query, $args)) -{ - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - $response=null; -} -else -{ - $response=$client->getResponse(); -} - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -// ------------------------------------------------------------------------------------- -$unitTestDescription="Test - getTestSuitesForTestPlan"; -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testplanid"]=61579; - -$debug=true; -echo $unitTestDescription; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -new dBug($args); -if(!$client->query('tl.getTestSuitesForTestPlan', $args)) -{ - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - $response=null; -} -else -{ - $response=$client->getResponse(); -} - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -?> \ No newline at end of file + + * @package TestlinkAPI + * @link http://testlink.org/api/ + * + * + * + * rev: 20081013 - franciscom - minor improvements to avoid reconfigure server url + * added test of getTestSuitesForTestPlan() + * 20080818 - franciscom - start work on custom field tests + * 20080306 - franciscom - added dBug to improve diagnostic info. + * 20080305 - franciscom - refactored + */ +require_once 'util.php'; +require_once 'sample.inc.php'; + +$unitTestDescription = "Test - getTestCasesForTestPlan"; + +/** + * getTestCasesForTestPlan + * List test cases linked to a test plan + * + * @param struct $args + * @param string $args["devKey"] + * @param int $args["testplanid"] + * @param int $args["testcaseid"] + * - optional + * @param int $args["buildid"] + * - optional + * @param int $args["keywordid"] + * - optional + * @param boolean $args["executed"] + * - optional + * @param int $args["$assignedto"] + * - optional + * @param string $args["executestatus"] + * - optional + * @return mixed $resultInfo + */ +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 61579; + +// optional +// $args["testcaseid"] - optional +// $args["buildid"] - optional +// $args["keywordid"] - optional +// $args["executed"] - optional +// $args["$assignedto"] - optional +// $args["executestatus"] - optional + +$debug = false; +echo $unitTestDescription; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +new dBug($args); +if (! $client->query('tl.getTestCasesForTestPlan', $args)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + $response = null; +} else { + $response = $client->getResponse(); +} + +echo "
    Result was: "; +// Typically you'd want to validate the result here and probably do something more useful with it +// print_r($response); +new dBug($response); +echo "
    "; + +// 20080518 - franciscom +$unitTestDescription = "Test - createTestProject"; +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testprojectname"] = 'API TestProject'; + +$debug = true; +echo $unitTestDescription; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +new dBug($args); +if (! $client->query('tl.createTestProject', $args)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + $response = null; +} else { + $response = $client->getResponse(); +} + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +// ---------------------------------------------------------------------- +$method = 'getTestCaseCustomFieldDesignValue'; +$client_query = 'tl.' . $method; +$unitTestDescription = "Test - $method"; + +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testcaseexternalid"] = 'ESP-1'; +$args["customfieldname"] = 'SSCRIPT_CF1'; + +$debug = true; + +echo $unitTestDescription; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +new dBug($args); +if (! $client->query($client_query, $args)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + $response = null; +} else { + $response = $client->getResponse(); +} + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +// ------------------------------------------------------------------------------------- +$unitTestDescription = "Test - getTestSuitesForTestPlan"; +$args = array(); +$args["devKey"] = DEV_KEY; +$args["testplanid"] = 61579; + +$debug = true; +echo $unitTestDescription; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +new dBug($args); +if (! $client->query('tl.getTestSuitesForTestPlan', $args)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + $response = null; +} else { + $response = $client->getResponse(); +} + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientSample20160730.php b/lib/api/xmlrpc/v1/sample_clients/php/clientSample20160730.php index dc57f965d6..e1155adfb1 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientSample20160730.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientSample20160730.php @@ -1,62 +1,72 @@ -tlProjectID = -1; -$env->tlSuiteID = -1; -$env->tlPlanID = -1; -$env->tlTestCaseVersion = 1; -// ---------------------------------------------- :) - -$doSetUp = false; -$phpSteps = array(); - -if( $doSetUp ) -{ - $phpSteps[] = array('f2i' => 'stepDeleteTestProject.php', 'id' => 'tlProjectID'); - $phpSteps[] = array('f2i' => 'stepCreateTestProject.php', 'id' => 'tlProjectID'); - $phpSteps[] = array('f2i' => 'stepCreateTestSuite.php', 'id' => 'tlSuiteID'); - $phpSteps[] = array('f2i' => 'stepCreateTestCase.php', 'id' => 'tlJolt'); - $phpSteps[] = array('f2i' => 'stepCreateTestPlan.php', 'id' => 'tlPlanID'); -} -else -{ - // - $env->tlProjectID = 1046; - $env->tlPlanID = 1051; - $env->tlTestCaseVersion = 2; - $tlOverWriteOnAdd = 1; -} - -$phpSteps[] = array('f2i' => 'stepAddTestCaseToTestPlan.php', 'id' => 'tlJolt'); - -foreach( $phpSteps as $m2i) -{ - try - { - $tlIDName = $m2i['id']; - require_once $m2i['f2i']; - } - catch (Exception $e) - { - echo $e->getMessage(); - } -} \ No newline at end of file +tlProjectID = - 1; +$env->tlSuiteID = - 1; +$env->tlPlanID = - 1; +$env->tlTestCaseVersion = 1; +// ---------------------------------------------- :) + +$doSetUp = false; +$phpSteps = array(); + +if ($doSetUp) { + $phpSteps[] = array( + 'f2i' => 'stepDeleteTestProject.php', + 'id' => 'tlProjectID' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestProject.php', + 'id' => 'tlProjectID' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestSuite.php', + 'id' => 'tlSuiteID' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestCase.php', + 'id' => 'tlJolt' + ); + $phpSteps[] = array( + 'f2i' => 'stepCreateTestPlan.php', + 'id' => 'tlPlanID' + ); +} else { + // + $env->tlProjectID = 1046; + $env->tlPlanID = 1051; + $env->tlTestCaseVersion = 2; + $tlOverWriteOnAdd = 1; +} + +$phpSteps[] = array( + 'f2i' => 'stepAddTestCaseToTestPlan.php', + 'id' => 'tlJolt' +); + +foreach ($phpSteps as $m2i) { + try { + $tlIDName = $m2i['id']; + require_once $m2i['f2i']; + } catch (Exception $e) { + echo $e->getMessage(); + } +} diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseExecutionType.php b/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseExecutionType.php index bf55e04e03..f95cbe347a 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseExecutionType.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseExecutionType.php @@ -1,55 +1,28 @@ -debug=true; -runTest($client,$method,$args); - -/* -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testprojectid"]=32989; -$args["testcaseexternalid"]='APF-1'; -$args["version"]=1; -$args["executiontype"]=2; -$client = new IXR_Client($server_url); -$client->debug=true; -runTest($client,$method,$args); -*/ - -/* -$args=array(); -$args["devKey"]=DEV_KEY; -$args["testprojectid"]=32989; -$args["testcaseexternalid"]='APF-1'; -$args["version"]=1; -$args["executiontype"]=10; -$client = new IXR_Client($server_url); -$client->debug=true; -runTest($client,$method,$args); -*/ -?> \ No newline at end of file +debug = true; +runTest($client, $method, $args); + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseTestSuite.php b/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseTestSuite.php index 013fe2baf6..0d45763333 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseTestSuite.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientSetTestCaseTestSuite.php @@ -1,32 +1,30 @@ -debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); -die(); \ No newline at end of file +debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); +die(); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientSetUserRoleOnProject.php b/lib/api/xmlrpc/v1/sample_clients/php/clientSetUserRoleOnProject.php new file mode 100644 index 0000000000..5d60c26bee --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientSetUserRoleOnProject.php @@ -0,0 +1,42 @@ +debug=$debug; + +runTest($client,$method,$args,$test_num); + diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientSetupTicket-5451.php b/lib/api/xmlrpc/v1/sample_clients/php/clientSetupTicket-5451.php index df89e25bc6..31a59ff8a3 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientSetupTicket-5451.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientSetupTicket-5451.php @@ -1,130 +1,127 @@ -debug = false; -$cfg->devKey = 'Spock (The Tholian Web)'; -$cfg->prefix = 'AXE'; - -$args4call = array(); - -// $server_url is GLOBAL created on some previous include -$op = createTestProject($server_url,$cfg,$args4call); -$op = createTestPlan($server_url,$cfg,$args4call); -if( isset($op[0]['status']) && $op[0]['status']) -{ - $tplan_id = $op[0]['id']; - $op = createBuild($server_url,$cfg,$args4call,$tplan_id); -} - - -$platformSet = array(); -$dummy = new stdClass(); -$dummy->name = 'Ferrari'; -$dummy->notes = 'Italy'; -$platformSet[$dummy->name] = $dummy; - -$dummy = new stdClass(); -$dummy->name = 'Porsche'; -$dummy->notes = 'Germany'; -$platformSet[$dummy->name] = $dummy; - -$dummy = new stdClass(); -$dummy->name = 'Renault'; -$dummy->notes = 'France'; -$platformSet[$dummy->name] = $dummy; - -foreach($platformSet as $name => &$item) -{ - $op = createPlatform($server_url,$cfg,$args4call,$item); - $item->id = $op['id']; -} - - - - -// ------------------------------------------------------------------------------------------------ -// Support functions -// ------------------------------------------------------------------------------------------------ -function createTestProject($server_url,$cfg,&$args4call) -{ - $method = 'createTestProject'; - $args4call[$method] = array("devKey" => $cfg->devKey, - "testcaseprefix" => $cfg->prefix, - "testprojectname" => "TICKET 5451", - "notes" => - "To test TICKET 5451: Test Plan WITH 2 or more PLATFORMS - " . - "Test Cases Without Tester Assignment provide wrong result"); - - $client = new IXR_Client($server_url); - $client->debug = $cfg->debug; - return runTest($client,$method,$args4call[$method]); -} - - -function createTestPlan($server_url,$cfg,&$args4call) -{ - $method = 'createTestPlan'; - $args4call[$method] = array("devKey" => $cfg->devKey, - "testprojectname" => $args4call['createTestProject']["testprojectname"], - "testplanname" => "TPLAN A - 3 Platforms", - "notes" => "Test plan used to test report 'Test cases without tester assignment' "); - - $client = new IXR_Client($server_url); - $client->debug = $cfg->debug; - return runTest($client,$method,$args4call[$method]); -} - - -function createBuild($server_url,$cfg,&$args4call,$tplan_id) -{ - - $method = 'createBuild'; - $args4call[$method] = array("devKey" => $cfg->devKey, - "buildname" => '1.0', - "testplanid" => $tplan_id, - "buildnote" => "Build used to test issue 5451"); - - $client = new IXR_Client($server_url); - $client->debug = $cfg->debug; - return runTest($client,$method,$args4call[$method]); -} - -function createPlatform($server_url,$cfg,&$args4call,$item) -{ - $method = 'createPlatform'; - $args4call[$method] = array("devKey" => $cfg->devKey, - "platformname" => $item->name, - "notes" => $item->notes, - "testprojectname" => $args4call['createTestProject']["testprojectname"]); - - $client = new IXR_Client($server_url); - $client->debug = $cfg->debug; - return runTest($client,$method,$args4call[$method]); -} - - -?> \ No newline at end of file +debug = false; +$cfg->devKey = 'Spock (The Tholian Web)'; +$cfg->prefix = 'AXE'; + +$args4call = array(); + +// $server_url is GLOBAL created on some previous include +$op = createTestProject($server_url, $cfg, $args4call); +$op = createTestPlan($server_url, $cfg, $args4call); +if (isset($op[0]['status']) && $op[0]['status']) { + $tplan_id = $op[0]['id']; + $op = createBuild($server_url, $cfg, $args4call, $tplan_id); +} + +$platformSet = array(); +$dummy = new stdClass(); +$dummy->name = 'Ferrari'; +$dummy->notes = 'Italy'; +$platformSet[$dummy->name] = $dummy; + +$dummy = new stdClass(); +$dummy->name = 'Porsche'; +$dummy->notes = 'Germany'; +$platformSet[$dummy->name] = $dummy; + +$dummy = new stdClass(); +$dummy->name = 'Renault'; +$dummy->notes = 'France'; +$platformSet[$dummy->name] = $dummy; + +foreach ($platformSet as &$item) { + $op = createPlatform($server_url, $cfg, $args4call, $item); + $item->id = $op['id']; +} + +// ------------------------------------------------------------------------------------------------ +// Support functions +// ------------------------------------------------------------------------------------------------ +function createTestProject($server_url, $cfg, &$args4call) +{ + $method = 'createTestProject'; + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "testcaseprefix" => $cfg->prefix, + "testprojectname" => "TICKET 5451", + "notes" => "To test TICKET 5451: Test Plan WITH 2 or more PLATFORMS - " . + "Test Cases Without Tester Assignment provide wrong result" + ); + + $client = new IXR_Client($server_url); + $client->debug = $cfg->debug; + return runTest($client, $method, $args4call[$method]); +} + +function createTestPlan($server_url, $cfg, &$args4call) +{ + $method = 'createTestPlan'; + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "testprojectname" => $args4call['createTestProject']["testprojectname"], + "testplanname" => "TPLAN A - 3 Platforms", + "notes" => "Test plan used to test report 'Test cases without tester assignment' " + ); + + $client = new IXR_Client($server_url); + $client->debug = $cfg->debug; + return runTest($client, $method, $args4call[$method]); +} + +function createBuild($server_url, $cfg, &$args4call, $tplan_id) +{ + $method = 'createBuild'; + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "buildname" => '1.0', + "testplanid" => $tplan_id, + "buildnote" => "Build used to test issue 5451" + ); + + $client = new IXR_Client($server_url); + $client->debug = $cfg->debug; + return runTest($client, $method, $args4call[$method]); +} + +function createPlatform($server_url, $cfg, &$args4call, $item) +{ + $method = 'createPlatform'; + $args4call[$method] = array( + "devKey" => $cfg->devKey, + "platformname" => $item->name, + "notes" => $item->notes, + "testprojectname" => $args4call['createTestProject']["testprojectname"] + ); + + $client = new IXR_Client($server_url); + $client->debug = $cfg->debug; + return runTest($client, $method, $args4call[$method]); +} + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientTestExecStepsIncremental.php b/lib/api/xmlrpc/v1/sample_clients/php/clientTestExecStepsIncremental.php index b2b5d6b1f5..0901a7dd97 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientTestExecStepsIncremental.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientTestExecStepsIncremental.php @@ -1,161 +1,146 @@ -testplanid=434; -// $context->buildid=1; -$context->buildname='1.0'; -// $context->platformname=null; -$context->testcaseexternalid='ZS-1'; -// $context->testcaseid=null; - -$exec = new stdClass(); -$exec->status = $tcaseStatusCode['blocked']; -$exec->notes="Call using all EXTERNAL ID"; -//$exec->customfields = null; -//$exec->bugid = null; -//$exec->user = 'QQ'; -$exec->overwrite=true; - -$exec->steps=array( array('step_number' => 1, - 'result' => 'f', 'notes' => 'no') ); - - -$debug=false; -echo '
    ' . $unitTestDescription . ''; -echo '
    '; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - -$exec->steps=array( array('step_number' => 2, - 'result' => 'p', 'notes' => 'no for 2') ); - -$debug=false; -echo '
    ' . $unitTestDescription . ''; -echo '
    '; -$response = executeTestCase($server_url,$context,$exec,$debug); - -echo "
    Result was: "; -new dBug($response); -echo "
    "; - - - -/* - function: - - args: - - returns: - -*/ -function executeTestCase($server_url,$context,$exec,$debug=false) -{ - - echo '
    Context is:'; - new dBug($context); - - - echo '
    Execution data is:'; - new dBug($exec); - - echo '
    '; - - - $client = new IXR_Client($server_url); - $client->debug=$debug; - - $data = array(); - $data["devKey"] = '3c41cb6d3c39f263b6bade693f8f7fa7'; - $data["status"] = $exec->status; - - if( property_exists($exec, 'steps') && !is_null($exec->steps) ) - { - $data["steps"] = $exec->steps; - } - - if( property_exists($exec, 'user') && !is_null($exec->user) ) - { - $data["user"]=$exec->user; - } - - if( property_exists($exec, 'notes') && !is_null($exec->notes) ) - { - $data["notes"] = $exec->notes; - } - - if( property_exists($exec, 'bugid') && !is_null($exec->bugid) ) - { - $data["bugid"] = $exec->bugid; - } - - if( property_exists($exec, 'overwrite') && !is_null($exec->overwrite) ) - { - $data["overwrite"]=$exec->overwrite; - } - - if( property_exists($exec, 'customfields') && !is_null($exec->customfields) ) - { - $data["customfields"]=$customfields; - } - - - $data["testplanid"] = $context->testplanid; - if( property_exists($context, 'testcaseid') && !is_null($context->testcaseid) ) - { - $data["testcaseid"] = $context->testcaseid; - } - else if( property_exists($context, 'testcaseexternalid') && !is_null($context->testcaseexternalid) ) - { - $data["testcaseexternalid"] = $context->testcaseexternalid; - } - - if( property_exists($context, 'buildid') && !is_null($context->buildid) ) - { - $data["buildid"] = $context->buildid; - } - else if ( property_exists($context, 'buildname') && !is_null($context->buildname) ) - { - $data["buildname"] = $context->buildname; - } - - if( property_exists($context, 'platformname') && !is_null($context->platformname) ) - { - $data["platformname"]=$context->platformname; - } - - - echo '
    Method will be called with following data:'; - new dBug($data); - - if(!$client->query('tl.reportTCResult', $data)) { - echo "something went wrong - " . $client->getErrorCode() . " - " . $client->getErrorMessage(); - } - else { - return $client->getResponse(); - } -} \ No newline at end of file +testplanid = 434; +$context->buildname = '1.0'; +$context->testcaseexternalid = 'ZS-1'; + +$exec = new stdClass(); +$exec->status = $tcaseStatusCode['blocked']; +$exec->notes = "Call using all EXTERNAL ID"; +$exec->overwrite = true; + +$exec->steps = array( + array( + 'step_number' => 1, + 'result' => 'f', + 'notes' => 'no' + ) +); + +$debug = false; +echo '
    ' . $unitTestDescription . ''; +echo '
    '; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +$exec->steps = array( + array( + 'step_number' => 2, + 'result' => 'p', + 'notes' => 'no for 2' + ) +); + +$debug = false; +echo '
    ' . $unitTestDescription . ''; +echo '
    '; +$response = executeTestCase($server_url, $context, $exec, $debug); + +echo "
    Result was: "; +new dBug($response); +echo "
    "; + +/* + * function: + * + * args: + * + * returns: + * + */ +function executeTestCase($server_url, $context, $exec, $debug = false) +{ + echo '
    Context is:'; + new dBug($context); + + echo '
    Execution data is:'; + new dBug($exec); + + echo '
    '; + + $client = new IXR_Client($server_url); + $client->debug = $debug; + + $data = array(); + $data["devKey"] = '3c41cb6d3c39f263b6bade693f8f7fa7'; + $data["status"] = $exec->status; + + if (property_exists($exec, 'steps') && ! is_null($exec->steps)) { + $data["steps"] = $exec->steps; + } + + if (property_exists($exec, 'user') && ! is_null($exec->user)) { + $data["user"] = $exec->user; + } + + if (property_exists($exec, 'notes') && ! is_null($exec->notes)) { + $data["notes"] = $exec->notes; + } + + if (property_exists($exec, 'bugid') && ! is_null($exec->bugid)) { + $data["bugid"] = $exec->bugid; + } + + if (property_exists($exec, 'overwrite') && ! is_null($exec->overwrite)) { + $data["overwrite"] = $exec->overwrite; + } + + if (property_exists($exec, 'customfields') && ! is_null($exec->customfields)) { + $data["customfields"] = $customfields; + } + + $data["testplanid"] = $context->testplanid; + if (property_exists($context, 'testcaseid') && + ! is_null($context->testcaseid)) { + $data["testcaseid"] = $context->testcaseid; + } elseif (property_exists($context, 'testcaseexternalid') && + ! is_null($context->testcaseexternalid)) { + $data["testcaseexternalid"] = $context->testcaseexternalid; + } + + if (property_exists($context, 'buildid') && ! is_null($context->buildid)) { + $data["buildid"] = $context->buildid; + } elseif (property_exists($context, 'buildname') && + ! is_null($context->buildname)) { + $data["buildname"] = $context->buildname; + } + + if (property_exists($context, 'platformname') && + ! is_null($context->platformname)) { + $data["platformname"] = $context->platformname; + } + + echo '
    Method will be called with following data:'; + new dBug($data); + + if (! $client->query('tl.reportTCResult', $data)) { + echo "something went wrong - " . $client->getErrorCode() . " - " . + $client->getErrorMessage(); + } else { + return $client->getResponse(); + } +} diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientTestLinkVersion.php b/lib/api/xmlrpc/v1/sample_clients/php/clientTestLinkVersion.php index caa0bd26c7..bd06619c2a 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientTestLinkVersion.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientTestLinkVersion.php @@ -1,25 +1,25 @@ -debug=$debug; -$answer = runTest($client,$method,$args); \ No newline at end of file +debug = $debug; +$answer = runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientTestSuiteTestCaseStepsManagement.php b/lib/api/xmlrpc/v1/sample_clients/php/clientTestSuiteTestCaseStepsManagement.php index 88feefe74f..09f57e08db 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientTestSuiteTestCaseStepsManagement.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientTestSuiteTestCaseStepsManagement.php @@ -1,169 +1,155 @@ -tcasePrefix = 'MKO'; -$cfg->tcaseVersionNumber = 1; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -$tcCounter = 0; - -$commonArgs = array(); -$commonArgs["devKey"]=DEV_KEY; -$commonArgs["testcaseexternalid"]=$cfg->tcasePrefix . '-1'; - -// Sample data -$qtySteps = 6; -$fakeSteps = null; -for($idx=1; $idx < $qtySteps; $idx++) -{ - $action = 'FULL STOP NOW!!!! - intensity='; - $expected_results = '%s Red Lights ON'; - if( $idx & 1 ) - { - $action = 'Start Server with power='; - $expected_results = 'GREEN Lantern %s ON'; - } - $expected_results = sprintf($expected_results,$idx); - $fakeSteps[] = array('step_number' => $idx, 'actions' => $action . $idx, - 'expected_results' => $expected_results); -} -// --------------------------------------------------------------------------------- - - -// --------------------------------------------------------------------------------- -// Get existent Test Case -$additionalInfo=''; -$tcCounter++; -$args = $commonArgs; -$rr = runTest($client,'getTestCase',$args,$tcCounter); -$ret = $rr[0]; -if(isset($ret['code'])) -{ - new dBug($ret); - exit(); -} - -// Build data to Delete ALL steps -$originalSteps = null; -if( !is_null($ret) && isset($ret['steps']) && !is_null($ret['steps']) && $ret['steps'] != '') -{ - $originalSteps = (array)$ret['steps']; -} - -if( ($loop2do = count($originalSteps)) > 0 ) -{ - $runDelete = true; - $allSteps = null; - for($idx=0; $idx < $loop2do; $idx++) - { - $allSteps[] = $originalSteps[$idx]['step_number']; - } - - // Now request delete - $args=$commonArgs; - $args["version"]=$cfg->tcaseVersionNumber; - $args["steps"] = $allSteps; - $rr = runTest($client,'deleteTestCaseSteps',$args,$tcCounter); - $ret = isset($rr[0]) ? $rr[0] : $rr; - if(isset($ret['code'])) - { - new dBug($ret); - exit(); - } -} - -// Now reinsert original content if any -$steps2insert = !is_null($originalSteps) && count((array)$originalSteps >0) ? $originalSteps : $fakeSteps; -$args=$commonArgs; -$args["version"]=$cfg->tcaseVersionNumber; -$args["action"] = 'create'; -$args['steps'] = $steps2insert; - -// new dBug($steps2insert); -$rr = runTest($client,'createTestCaseSteps',$args,$tcCounter); -$ret = isset($rr[0]) ? $rr[0] : $rr; -if(isset($ret['code'])) -{ - new dBug($ret); - exit(); -} -// ---------------------------------------------------------------------------------------------------- - -// Now Create a Fake Step to PUSH -$alienStartPos = intval($qtySteps/3); -$aliens[] = array('step_number' => $alienStartPos, 'actions' => 'ALIEN ' . $action, - 'expected_results' => 'Ripley Will BE INFECTED'); - -$args=$commonArgs; -$args["version"]=$cfg->tcaseVersionNumber; -$args["action"] = 'push'; -$args['steps'] = $aliens; -$rr = runTest($client,'createTestCaseSteps',$args,$tcCounter); -$ret = isset($rr[0]) ? $rr[0] : $rr; -if(isset($ret['code'])) -{ - new dBug($ret); - exit(); -} -// ---------------------------------------------------------------------------------------------------- - -// ---------------------------------------------------------------------------------------------------- -// Now TRY TO Create EXISTENT STEP -$alienStartPos = intval($qtySteps/3); -$aliens[] = array('step_number' => $alienStartPos, - 'actions' => 'If you see this content => Houston we have a problem' . $action, - 'expected_results' => 'Ripley Will BE INFECTED'); - -$args=$commonArgs; -$args["version"]=$cfg->tcaseVersionNumber; -$args["action"] = 'create'; -$args['steps'] = $aliens; -$rr = runTest($client,'createTestCaseSteps',$args,$tcCounter); -$ret = isset($rr[0]) ? $rr[0] : $rr; -if(isset($ret['code'])) -{ - new dBug($ret); - exit(); -} -// ---------------------------------------------------------------------------------------------------- - - -// ---------------------------------------------------------------------------------------------------- -// Now TRY TO UPDATE a NON EXISTENT STEP -$hint = 'You have requested UPDATE of NON EXISTENT Step => we will CREATE it'; -$alienStartPos = 1000; -$aliens[] = array('step_number' => $alienStartPos, - 'actions' => $hint . $action, - 'expected_results' => 'Ripley Will BE INFECTED'); - -$args=$commonArgs; -$args["version"]=$cfg->tcaseVersionNumber; -$args["action"] = 'update'; -$args['steps'] = $aliens; -$rr = runTest($client,'createTestCaseSteps',$args,$tcCounter); -$ret = isset($rr[0]) ? $rr[0] : $rr; -if(isset($ret['code'])) -{ - new dBug($ret); - exit(); -} -// ---------------------------------------------------------------------------------------------------- - - - -?> \ No newline at end of file +tcasePrefix = 'MKO'; +$cfg->tcaseVersionNumber = 1; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +$tcCounter = 0; + +$commonArgs = array(); +$commonArgs["devKey"] = DEV_KEY; +$commonArgs["testcaseexternalid"] = $cfg->tcasePrefix . '-1'; + +// Sample data +$qtySteps = 6; +$fakeSteps = null; +for ($idx = 1; $idx < $qtySteps; $idx ++) { + $action = 'FULL STOP NOW!!!! - intensity='; + $expected_results = '%s Red Lights ON'; + if ($idx & 1) { + $action = 'Start Server with power='; + $expected_results = 'GREEN Lantern %s ON'; + } + $expected_results = sprintf($expected_results, $idx); + $fakeSteps[] = array( + 'step_number' => $idx, + 'actions' => $action . $idx, + 'expected_results' => $expected_results + ); +} + +// Get existent Test Case +$additionalInfo = ''; +$tcCounter ++; +$args = $commonArgs; +$rr = runTest($client, 'getTestCase', $args, $tcCounter); +$ret = $rr[0]; +if (isset($ret['code'])) { + new dBug($ret); + exit(); +} + +// Build data to Delete ALL steps +$originalSteps = null; +if (! is_null($ret) && isset($ret['steps']) && ! is_null($ret['steps']) && + $ret['steps'] != '') { + $originalSteps = (array) $ret['steps']; +} + +if (($loop2do = count($originalSteps)) > 0) { + $runDelete = true; + $allSteps = null; + for ($idx = 0; $idx < $loop2do; $idx ++) { + $allSteps[] = $originalSteps[$idx]['step_number']; + } + + // Now request delete + $args = $commonArgs; + $args["version"] = $cfg->tcaseVersionNumber; + $args["steps"] = $allSteps; + $rr = runTest($client, 'deleteTestCaseSteps', $args, $tcCounter); + $ret = isset($rr[0]) ? $rr[0] : $rr; + if (isset($ret['code'])) { + new dBug($ret); + exit(); + } +} + +// Now reinsert original content if any +$steps2insert = ! is_null($originalSteps) && count((array) $originalSteps > 0) ? $originalSteps : $fakeSteps; +$args = $commonArgs; +$args["version"] = $cfg->tcaseVersionNumber; +$args["action"] = 'create'; +$args['steps'] = $steps2insert; + +$rr = runTest($client, 'createTestCaseSteps', $args, $tcCounter); +$ret = isset($rr[0]) ? $rr[0] : $rr; +if (isset($ret['code'])) { + new dBug($ret); + exit(); +} + +// Now Create a Fake Step to PUSH +$alienStartPos = intval($qtySteps / 3); +$aliens[] = array( + 'step_number' => $alienStartPos, + 'actions' => 'ALIEN ' . $action, + 'expected_results' => 'Ripley Will BE INFECTED' +); + +$args = $commonArgs; +$args["version"] = $cfg->tcaseVersionNumber; +$args["action"] = 'push'; +$args['steps'] = $aliens; +$rr = runTest($client, 'createTestCaseSteps', $args, $tcCounter); +$ret = isset($rr[0]) ? $rr[0] : $rr; +if (isset($ret['code'])) { + new dBug($ret); + exit(); +} + +// Now TRY TO Create EXISTENT STEP +$alienStartPos = intval($qtySteps / 3); +$aliens[] = array( + 'step_number' => $alienStartPos, + 'actions' => 'If you see this content => Houston we have a problem' . $action, + 'expected_results' => 'Ripley Will BE INFECTED' +); + +$args = $commonArgs; +$args["version"] = $cfg->tcaseVersionNumber; +$args["action"] = 'create'; +$args['steps'] = $aliens; +$rr = runTest($client, 'createTestCaseSteps', $args, $tcCounter); +$ret = isset($rr[0]) ? $rr[0] : $rr; +if (isset($ret['code'])) { + new dBug($ret); + exit(); +} + +// Now TRY TO UPDATE a NON EXISTENT STEP +$hint = 'You have requested UPDATE of NON EXISTENT Step => we will CREATE it'; +$alienStartPos = 1000; +$aliens[] = array( + 'step_number' => $alienStartPos, + 'actions' => $hint . $action, + 'expected_results' => 'Ripley Will BE INFECTED' +); + +$args = $commonArgs; +$args["version"] = $cfg->tcaseVersionNumber; +$args["action"] = 'update'; +$args['steps'] = $aliens; +$rr = runTest($client, 'createTestCaseSteps', $args, $tcCounter); +$ret = isset($rr[0]) ? $rr[0] : $rr; +if (isset($ret['code'])) { + new dBug($ret); + exit(); +} + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientUnassignTestCaseExecutionTask.php b/lib/api/xmlrpc/v1/sample_clients/php/clientUnassignTestCaseExecutionTask.php index 719f5e858f..709caf4134 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientUnassignTestCaseExecutionTask.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientUnassignTestCaseExecutionTask.php @@ -1,194 +1,156 @@ -debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); +new dBug($answer); +die(); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - All OK"; + +$args = array(); +$args["devKey"] = $devKey; +$args["testplanid"] = 278; +$args["testcaseexternalid"] = 'APX-1'; +$args["platformname"] = 'Informix'; +$args["buildname"] = '2.0'; +$args["user"] = 'giskard'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Test Plan ID"; + +$args = array(); +$args["devKey"] = $devKey; +$args["testcaseexternalid"] = 'GK-1'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Test Case "; + +$args = array(); +$args["devKey"] = $devKey; +$args["testplanid"] = 9; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Missing argument - Build Name "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-1'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Test Plan ID "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 900000; +$args["testcaseexternalid"] = 'GK-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Test Case External ID "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-WRONG-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); + +$utc ++; +$unitTestDescription = "Test #{$utc} - {$method} - Wrong argument - Build Name "; + +$args = array(); +$args["devKey"] = $devKey; + +$args["testplanid"] = 9; +$args["testcaseexternalid"] = 'GK-1'; +$args["buildname"] = 'WRONG - 1.0'; +$args["platformname"] = 'P2'; + +$debug = true; +$client = new IXR_Client($server_url); +$client->debug = $debug; + +echo $unitTestDescription; +$answer = runTest($client, $method, $args); -$method="unassignTestCaseExecutionTask"; -$utc = 0; -$devKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : 'admin'; - -// --------------------------------------------------------------------------------------- -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - All OK"; - -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 2808; -$args["testcaseexternalid"] = 'DSM-1'; -// $args["platformname"] = 'Apache Derby'; -// $args["platformname"] = 'Informix'; -$args["buildname"] = '1.0'; -$args["action"] = 'unassignAll'; - -//$args["user"] = 'David.Gilmour'; -// $args["user"] = 'Nick.Mason'; - - -/* -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 278; -$args["testcaseexternalid"] = 'APX-1'; -$args["platformname"] = 'Informix'; -$args["buildname"] = '2.0'; -$args["user"] = 'giskard'; -*/ - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -new dBug($answer); -die(); - -// --------------------------------------------------------------------------------------- - -// --------------------------------------------------------------------------------------- -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - All OK"; - -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 278; -$args["testcaseexternalid"] = 'APX-1'; -$args["platformname"] = 'Informix'; -$args["buildname"] = '2.0'; -$args["user"] = 'giskard'; - - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); - -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Test Plan ID"; - -$args=array(); -$args["devKey"] = $devKey; -// $args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Test Case "; - -$args=array(); -$args["devKey"] = $devKey; -$args["testplanid"] = 9; -// $args["testcaseexternalid"] = 'GK-1'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Missing argument - Build Name "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -// $args["buildname"] = '1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Test Plan ID "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 900000; -$args["testcaseexternalid"] = 'GK-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Test Case External ID "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-WRONG-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- - -$utc++; -$unitTestDescription="Test #{$utc} - {$method} - Wrong argument - Build Name "; - -$args=array(); -$args["devKey"] = $devKey; - -$args["testplanid"] = 9; -$args["testcaseexternalid"] = 'GK-1'; -$args["buildname"] = 'WRONG - 1.0'; -$args["platformname"] = 'P2'; - - -$debug=true; -$client = new IXR_Client($server_url); -$client->debug=$debug; - -echo $unitTestDescription; -$answer = runTest($client,$method,$args); -// --------------------------------------------------------------------------------------- diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCase.php b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCase.php index 6fa2bb3389..e94c79d7f8 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCase.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCase.php @@ -1,154 +1,155 @@ -debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); - - - -// Update Only Summary + Setting updater -$args=array(); -$args["devKey"]='21232f297a57a5a743894a0e4a801fc3'; -$args["testcaseexternalid"]='IU-5844-3'; -$args["version"]=1; -$args["user"]='Iasmin'; -$args["summary"]='Updated via XML-RPC API - by ' . $args["user"]; - - -$client = new IXR_Client($server_url); -$client->debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); - -// Trying to Update AN INEXISTENT Version + Only Summary + Setting updater -$args=array(); -$args["devKey"]='21232f297a57a5a743894a0e4a801fc3'; -$args["testcaseexternalid"]='IU-5844-3'; -$args["version"]=1222; -$args["user"]='Iasmin'; -$args["summary"]='Updated via XML-RPC API - by ' . $args["user"]; - - -$client = new IXR_Client($server_url); -$client->debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); - -// Update Summary + duration + Setting updater -$args=array(); -$args["devKey"]='21232f297a57a5a743894a0e4a801fc3'; -$args["testcaseexternalid"]='IU-5844-3'; -$args["version"]=1; -$args["estimatedexecduration"] = 12.5; -$args["user"]='Iasmin'; -$args["summary"]='Updated via XML-RPC API - by ' . $args["user"]; - - -$client = new IXR_Client($server_url); -$client->debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); - - -// Update Summary + duration + importance + Setting updater -$args=array(); -$args["devKey"]='21232f297a57a5a743894a0e4a801fc3'; -$args["testcaseexternalid"]='IU-5844-3'; -$args["version"]=1; -$args["estimatedexecduration"] = 12.5; -$args["user"]='Iasmin'; -$args["summary"]='Updated via XML-RPC API - by ' . $args["user"]; -$args["importance"] = 3; - - -$client = new IXR_Client($server_url); -$client->debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); - - -// Update creating steps + Setting updater -$args=array(); -$args["devKey"]='21232f297a57a5a743894a0e4a801fc3'; -$args["testcaseexternalid"]='IU-5844-5'; -$args["version"]=1; -$args["user"]='Iasmin'; -$args["summary"]='Updated via XML-RPC API - by ' . $args["user"]; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); - - -$client = new IXR_Client($server_url); -$client->debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); - -// Update creating steps + Setting updater -$args=array(); -$args["devKey"]='21232f297a57a5a743894a0e4a801fc3'; -$args["testcaseexternalid"]='IU-5844-5'; -$args["version"]=1; -$args["user"]='Iasmin'; -$args["summary"]='Updated via XML-RPC API - by ' . $args["user"]; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server Upd', 'expected_results' => 'green light'); -$args["steps"][]=array('step_number' => 10, 'actions' => 'Start Server NEW', 'expected_results' => 'green light'); - - -$client = new IXR_Client($server_url); -$client->debug=true; - -$tcCounter++; -runTest($client,$method,$args,$tcCounter); +debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); + +// Update Only Summary + Setting updater +$args = array(); +$args["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; +$args["testcaseexternalid"] = 'IU-5844-3'; +$args["version"] = 1; +$args["user"] = 'Iasmin'; +$args["summary"] = 'Updated via XML-RPC API - by ' . $args["user"]; + +$client = new IXR_Client($server_url); +$client->debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); + +// Trying to Update AN INEXISTENT Version + Only Summary + Setting updater +$args = array(); +$args["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; +$args["testcaseexternalid"] = 'IU-5844-3'; +$args["version"] = 1222; +$args["user"] = 'Iasmin'; +$args["summary"] = 'Updated via XML-RPC API - by ' . $args["user"]; + +$client = new IXR_Client($server_url); +$client->debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); + +// Update Summary + duration + Setting updater +$args = array(); +$args["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; +$args["testcaseexternalid"] = 'IU-5844-3'; +$args["version"] = 1; +$args["estimatedexecduration"] = 12.5; +$args["user"] = 'Iasmin'; +$args["summary"] = 'Updated via XML-RPC API - by ' . $args["user"]; + +$client = new IXR_Client($server_url); +$client->debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); + +// Update Summary + duration + importance + Setting updater +$args = array(); +$args["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; +$args["testcaseexternalid"] = 'IU-5844-3'; +$args["version"] = 1; +$args["estimatedexecduration"] = 12.5; +$args["user"] = 'Iasmin'; +$args["summary"] = 'Updated via XML-RPC API - by ' . $args["user"]; +$args["importance"] = 3; + +$client = new IXR_Client($server_url); +$client->debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); + +// Update creating steps + Setting updater +$args = array(); +$args["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; +$args["testcaseexternalid"] = 'IU-5844-5'; +$args["version"] = 1; +$args["user"] = 'Iasmin'; +$args["summary"] = 'Updated via XML-RPC API - by ' . $args["user"]; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); + +$client = new IXR_Client($server_url); +$client->debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); + +// Update creating steps + Setting updater +$args = array(); +$args["devKey"] = '21232f297a57a5a743894a0e4a801fc3'; +$args["testcaseexternalid"] = 'IU-5844-5'; +$args["version"] = 1; +$args["user"] = 'Iasmin'; +$args["summary"] = 'Updated via XML-RPC API - by ' . $args["user"]; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server Upd', + 'expected_results' => 'green light' +); +$args["steps"][] = array( + 'step_number' => 10, + 'actions' => 'Start Server NEW', + 'expected_results' => 'green light' +); + +$client = new IXR_Client($server_url); +$client->debug = true; + +$tcCounter ++; +runTest($client, $method, $args, $tcCounter); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCaseCustomFieldDesignValue.php b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCaseCustomFieldDesignValue.php index 95b3849e53..85432e7272 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCaseCustomFieldDesignValue.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestCaseCustomFieldDesignValue.php @@ -1,47 +1,34 @@ - 'COMODORE64','CF_DT' => mktime(10,10,0,7,29,2009)); -// $args["customfields"] = array('TCSTRING' => 'From DUCATI to YAMAHA'); - -$args["customfields"] = - array('L2D' => - 'http://localhost/development/github/testlink-code/' . - 'linkto.php?tprojectPrefix=HA&item=testcase&id=HA-1 '); - - -$client = new IXR_Client($server_url); -$client->debug=true; - -/* -for($idx=1 ; $idx <= $tcaseQty; $idx++) -{ - runTest($client,$method,$args); -} -*/ -runTest($client,$method,$args); + 'http://localhost/development/github/testlink-code/' . + 'linkto.php?tprojectPrefix=HA&item=testcase&id=HA-1 ' +); + +$client = new IXR_Client($server_url); +$client->debug = true; + +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuite.php b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuite.php index 02dee8e5ad..07852f4012 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuite.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuite.php @@ -1,115 +1,102 @@ - default behaviour BLOCK => will not be created -// use name of existent Test Suite in parentid, request renaming -// - -$method='updateTestSuite'; - - -$unitTestDescription="Test - $method"; -$test_num = 0; -$tlDevKey = '985978c915f50e47a4b1a54a943d1b76'; -$tlDevKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $tlDevKey; - - -// ------------------------------------------------------------- -$test_num++; -$additionalInfo = 'Using Test Project PREFIX - Will Change ORDER'; -$args=array(); -$args["devKey"]=$tlDevKey; - -$args["prefix"]='ZTZ'; -$args["testsuiteid"]=1072; -$args["order"]=12000; -// $args["testsuitename"]='TS API 200.0'; -// $args["details"]='MOMO This has been created by XMLRPC API Call'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); - - -// ------------------------------------------------------------- -$test_num++; -$additionalInfo = 'Using Test Project PREFIX - Will UPDATE Name CREATING DUP'; -$args=array(); -$args["devKey"]=$tlDevKey; - -$args["prefix"]='ZTZ'; -$args["testsuiteid"]=1081; -$args["testsuitename"]='TS API 200.0'; -$args["details"]='MOMO This has been created by XMLRPC API Call'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); - - -// ------------------------------------------------------------- -$test_num++; -$additionalInfo = 'Using Test Project PREFIX - Will Update Name QUIET'; -$args=array(); -$args["devKey"]=$tlDevKey; - -$args["prefix"]='ZTZ'; -$args["testsuiteid"]=1080; -$args["testsuitename"]='TS API SUPER 200.0'; - -// $args["details"]='MOMO This has been created by XMLRPC API Call'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); - - - -// ------------------------------------------------------------- -$test_num++; -$additionalInfo = 'Using Test Project PREFIX - Will Update Details'; -$args=array(); -$args["devKey"]=$tlDevKey; - -$args["prefix"]='ZTZ'; -$args["testsuiteid"]=1080; - -// $args["testsuitename"]='TS API 200.0'; - -$args["details"]='MOMO This has been created by XMLRPC API Call'; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; -runTest($client,$method,$args,$test_num); + default behaviour BLOCK => will not be created +// use name of existent Test Suite in parentid, request renaming +// + +$method = 'updateTestSuite'; + +$unitTestDescription = "Test - $method"; +$test_num = 0; +$tlDevKey = '985978c915f50e47a4b1a54a943d1b76'; +$tlDevKey = isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $tlDevKey; + +// ------------------------------------------------------------- +$test_num ++; +$additionalInfo = 'Using Test Project PREFIX - Will Change ORDER'; +$args = array(); +$args["devKey"] = $tlDevKey; + +$args["prefix"] = 'ZTZ'; +$args["testsuiteid"] = 1072; +$args["order"] = 12000; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); + +// ------------------------------------------------------------- +$test_num ++; +$additionalInfo = 'Using Test Project PREFIX - Will UPDATE Name CREATING DUP'; +$args = array(); +$args["devKey"] = $tlDevKey; + +$args["prefix"] = 'ZTZ'; +$args["testsuiteid"] = 1081; +$args["testsuitename"] = 'TS API 200.0'; +$args["details"] = 'MOMO This has been created by XMLRPC API Call'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); + +// ------------------------------------------------------------- +$test_num ++; +$additionalInfo = 'Using Test Project PREFIX - Will Update Name QUIET'; +$args = array(); +$args["devKey"] = $tlDevKey; + +$args["prefix"] = 'ZTZ'; +$args["testsuiteid"] = 1080; +$args["testsuitename"] = 'TS API SUPER 200.0'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); + +// ------------------------------------------------------------- +$test_num ++; +$additionalInfo = 'Using Test Project PREFIX - Will Update Details'; +$args = array(); +$args["devKey"] = $tlDevKey; + +$args["prefix"] = 'ZTZ'; +$args["testsuiteid"] = 1080; + +$args["details"] = 'MOMO This has been created by XMLRPC API Call'; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; +runTest($client, $method, $args, $test_num); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuiteCustomFieldDesignValue.php b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuiteCustomFieldDesignValue.php index 78aba92e80..848f63c80f 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuiteCustomFieldDesignValue.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientUpdateTestSuiteCustomFieldDesignValue.php @@ -1,53 +1,58 @@ -Testing Method: ' . $method . ''; - -$args=array(); -$args["devKey"] = 'admin'; -$args["testprojectid"]=279340; -$args["testsuiteid"]=279341; -$args["customfields"] = array('TCSTRING' => 'From DUCATI to YAMAHA'); - -$client = new IXR_Client($server_url); -$client->debug=true; - -runTest($client,$method,$args); - -// -$args=array(); -$args["devKey"] = 'admin'; -$args["testprojectid"]=279340; -$args["testsuiteid"]=279341; -$args["customfields"] = array('CF_MOTO' => 'From DUCATI to YAMAHA'); - -$client = new IXR_Client($server_url); -$client->debug=true; - -runTest($client,$method,$args); - -// -$args=array(); -$args["devKey"] = 'admin'; -$args["testprojectid"]=279340; -$args["testsuiteid"]=279341; -$args["customfields"] = array('CF_MOTO' => 'From DUCATI to YAMAHA'); - -$client = new IXR_Client($server_url); -$client->debug=true; - -runTest($client,$method,$args); +Testing Method: ' . $method . ''; + +$args = array(); +$args["devKey"] = 'admin'; +$args["testprojectid"] = 279340; +$args["testsuiteid"] = 279341; +$args["customfields"] = array( + 'TCSTRING' => 'From DUCATI to YAMAHA' +); + +$client = new IXR_Client($server_url); +$client->debug = true; + +runTest($client, $method, $args); + +// +$args = array(); +$args["devKey"] = 'admin'; +$args["testprojectid"] = 279340; +$args["testsuiteid"] = 279341; +$args["customfields"] = array( + 'CF_MOTO' => 'From DUCATI to YAMAHA' +); + +$client = new IXR_Client($server_url); +$client->debug = true; + +runTest($client, $method, $args); + +// +$args = array(); +$args["devKey"] = 'admin'; +$args["testprojectid"] = 279340; +$args["testsuiteid"] = 279341; +$args["customfields"] = array( + 'CF_MOTO' => 'From DUCATI to YAMAHA' +); + +$client = new IXR_Client($server_url); +$client->debug = true; + +runTest($client, $method, $args); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/clientUploadTestCaseAttachment.php b/lib/api/xmlrpc/v1/sample_clients/php/clientUploadTestCaseAttachment.php index 26bda95c44..8421bd8b01 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/clientUploadTestCaseAttachment.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/clientUploadTestCaseAttachment.php @@ -1,63 +1,62 @@ -debug=$debug; - -runTest($client,$method,$args,$test_num); -// --------------------------------------------------------------------------------- - -$test_num=2; -$unitTestDescription="Test {$test_num} - {$method}"; -$attach = file_get_contents('./other/marilyn-monroe.jpg'); -$encoded = base64_encode($attach); -$args=array(); -$args["devKey"]='developer'; -$args["testcaseid"]=118951; -$args["version"] = 2; -$args["title"] = 'Marilyn Monroe'; -$args["filename"] = 'marilyn-monroe.jpg'; -$args["content"] = $encoded; -$additionalInfo=''; - -$debug=true; -echo $unitTestDescription; -echo $additionalInfo; - -$client = new IXR_Client($server_url); -$client->debug=$debug; - -runTest($client,$method,$args,$test_num); +debug = $debug; + +runTest($client, $method, $args, $test_num); +// --------------------------------------------------------------------------------- + +$test_num = 2; +$unitTestDescription = "Test {$test_num} - {$method}"; +$attach = file_get_contents('./other/marilyn-monroe.jpg'); +$encoded = base64_encode($attach); +$args = array(); +$args["devKey"] = 'developer'; +$args["testcaseid"] = 118951; +$args["version"] = 2; +$args["title"] = 'Marilyn Monroe'; +$args["filename"] = 'marilyn-monroe.jpg'; +$args["content"] = $encoded; +$additionalInfo = ''; + +$debug = true; +echo $unitTestDescription; +echo $additionalInfo; + +$client = new IXR_Client($server_url); +$client->debug = $debug; + +runTest($client, $method, $args, $test_num); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t1.php b/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t1.php index ed3d6b4f35..a56b4205c4 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t1.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t1.php @@ -1,30 +1,30 @@ -debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); \ No newline at end of file +debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t2.php b/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t2.php index 778731a363..f3ed40fd27 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t2.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/client_issue6159_t2.php @@ -1,32 +1,32 @@ -debug=$debug; - -$answer = runTest($client,$method,$args,$test_num); \ No newline at end of file +debug = $debug; + +$answer = runTest($client, $method, $args, $test_num); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/index.php b/lib/api/xmlrpc/v1/sample_clients/php/index.php index f0f578b9e8..8780792446 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/index.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/index.php @@ -1,62 +1,53 @@ -
    Click on file name to launch sample client

    '; -echo ''; -foreach($examples as $url2launch) -{ - echo ''; -} -echo '
    '; - echo '' . $url2launch . ''; - echo '
    '; - -?> +
    Click on file name to launch sample client

    '; +echo ''; +foreach ($examples as $url2launch) { + echo ''; +} +echo '
    '; + echo '' . $url2launch . ''; + echo '
    '; + +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/sample.inc.php b/lib/api/xmlrpc/v1/sample_clients/php/sample.inc.php index f17a0df76a..c65ff32f96 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/sample.inc.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/sample.inc.php @@ -1,21 +1,23 @@ - -<?php echo $tl_and_version ?> +<?php + +echo $tl_and_version?> @@ -27,54 +29,51 @@ storedDetail : '', toggle : function(id){ - if(this.storedDetail && this.storedDetail != id) + if(this.storedDetail && this.storedDetail != id) { document.getElementById(this.storedDetail).style.display = 'none'; } this.storedDetail = id; var style = document.getElementById(id).style; - if(style.display == 'block') + if(style.display == 'block') { style.display = 'none'; } else { style.display = 'block'; - } + } return false; } }; -Test Link XML-RPC API - PHP Samples
    '; - -// substitute your Dev Key Here -define("DEV_KEY", "dev01"); -if( DEV_KEY == "dev01" ) -{ - echo '

    Attention: DEVKEY is still setted to demo value (' . DEV_KEY . ')

    '; - echo 'Please check if this VALUE is defined for a user on yout DB Installation'; - echo '
    '; -} -?> \ No newline at end of file +Test Link XML-RPC API - PHP Samples
    '; + +// substitute your Dev Key Here +define("DEV_KEY", "dev01"); +if (DEV_KEY == "dev01") { + echo '

    Attention: DEVKEY is still setted to demo value (' . DEV_KEY . + ')

    '; + echo 'Please check if this VALUE is defined for a user on yout DB Installation'; + echo '
    '; +} +?> diff --git a/lib/api/xmlrpc/v1/sample_clients/php/stepAddTestCaseToTestPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/stepAddTestCaseToTestPlan.php index 0c2dbbac4e..6efe455398 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/stepAddTestCaseToTestPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/stepAddTestCaseToTestPlan.php @@ -1,44 +1,44 @@ -tlProjectID; -$args["testplanid"] = $env->tlPlanID; - -$args["testcaseexternalid"] = $tlTestCasePrefix . '-1'; -$args["version"] = $env->tlTestCaseVersion; -$args["overwrite"] = $tlOverWriteOnAdd; - -$tlIdx++; -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$answer = runTest($client,$method,$args,$tlIdx); +tlProjectID; +$args["testplanid"] = $env->tlPlanID; + +$args["testcaseexternalid"] = $tlTestCasePrefix . '-1'; +$args["version"] = $env->tlTestCaseVersion; +$args["overwrite"] = $tlOverWriteOnAdd; + +$tlIdx ++; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$answer = runTest($client, $method, $args, $tlIdx); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestCase.php b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestCase.php index e304c4e6e4..2643f1f98e 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestCase.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestCase.php @@ -1,34 +1,35 @@ -tlProjectID; -$args["testsuiteid"] = $env->tlSuiteID; -$args["testcasename"]='ZZ - TEST CASE NAME IS OK'; -$args["summary"]='Test Case created via API'; -$args["preconditions"]='Test Link API Up & Running'; -$args["authorlogin"]='admin'; -$args["checkduplicatedname"]=0; -$args["steps"][]=array('step_number' => 1, 'actions' => 'Start Server', 'expected_results' => 'green light'); - -$unitTestDescription = ""; -echo $unitTestDescription; - -$tlIdx++; -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$ret = runTest($client,$method,$args,$tlIdx); \ No newline at end of file +tlProjectID; +$args["testsuiteid"] = $env->tlSuiteID; +$args["testcasename"] = 'ZZ - TEST CASE NAME IS OK'; +$args["summary"] = 'Test Case created via API'; +$args["preconditions"] = 'Test Link API Up & Running'; +$args["authorlogin"] = 'admin'; +$args["checkduplicatedname"] = 0; +$args["steps"][] = array( + 'step_number' => 1, + 'actions' => 'Start Server', + 'expected_results' => 'green light' +); + +$unitTestDescription = ""; +echo $unitTestDescription; + +$tlIdx ++; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$ret = runTest($client, $method, $args, $tlIdx); diff --git a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestPlan.php b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestPlan.php index 339f72b814..c31c1b81f0 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestPlan.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestPlan.php @@ -1,38 +1,33 @@ -debug = $tlDebug; +$ret = runTest($client, $method, $args, $tlIdx); + +if (isset($ret[0]['id'])) { + $env->$tlIDName = $ret[0]['id']; +} else { + $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . + basename(__FILE__) . ")"; + throw new Exception($msg, 1); } - -$args=array(); -$args["devKey"] =isset($_REQUEST['apiKey']) ? $_REQUEST['apiKey'] : $tlDevKey; -$args["prefix"] = $tlTestCasePrefix; -$args["testplanname"]="TPLAN BY API"; -$args["notes"]="test plan created using XML-RPC-API"; - -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$ret = runTest($client,$method,$args,$tlIdx); - -if( isset($ret[0]['id']) ) -{ - $env->$tlIDName = $ret[0]['id']; -} -else -{ - $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . basename(__FILE__) . ")"; - throw new Exception($msg, 1); -} \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestProject.php b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestProject.php index c69e217436..fcafffc7b7 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestProject.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestProject.php @@ -1,43 +1,38 @@ - {$additionalInfo}"; - -echo $unitTestDescription . ' ' . $additionalInfo; - -$tlIdx++; -$client = new IXR_Client($server_url); -$client->debug=$tlDebug; -$ret = runTest($client,$method,$args,$tlIdx); - -if( isset($ret[0]['id']) ) -{ - $env->$tlIDName = $ret[0]['id']; + {$additionalInfo}"; + +echo $unitTestDescription . ' ' . $additionalInfo; + +$tlIdx ++; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$ret = runTest($client, $method, $args, $tlIdx); + +if (isset($ret[0]['id'])) { + $env->$tlIDName = $ret[0]['id']; +} else { + $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . + basename(__FILE__) . ")"; + throw new Exception($msg, 1); } -else -{ - $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . basename(__FILE__) . ")"; - throw new Exception($msg, 1); -} \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestSuite.php b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestSuite.php index a256edd6b7..ffa5813d3e 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestSuite.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/stepCreateTestSuite.php @@ -1,39 +1,35 @@ -tlProjectID; -$args["testsuitename"] = 'TS API 100'; -$args["details"]='This has been created by XMLRPC API Call'; - -echo $unitTestDescription; -$client = new IXR_Client($server_url); -$client->debug = $tlDebug; -$ret = runTest($client,$method,$args,$tlIdx); - - -if( isset($ret[0]['id']) ) -{ - $env->$tlIDName = $ret[0]['id']; +tlProjectID; +$args["testsuitename"] = 'TS API 100'; +$args["details"] = 'This has been created by XMLRPC API Call'; + +echo $unitTestDescription; +$client = new IXR_Client($server_url); +$client->debug = $tlDebug; +$ret = runTest($client, $method, $args, $tlIdx); + +if (isset($ret[0]['id'])) { + $env->$tlIDName = $ret[0]['id']; +} else { + $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . + basename(__FILE__) . ")"; + throw new Exception($msg, 1); } -else -{ - $msg = 'Warning! ' . ' :: ' . $ret[0]['message'] . ' (file: ' . basename(__FILE__) . ")"; - throw new Exception($msg, 1); -} \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/php/util.php b/lib/api/xmlrpc/v1/sample_clients/php/util.php index c726286edf..2a7a27e0f6 100644 --- a/lib/api/xmlrpc/v1/sample_clients/php/util.php +++ b/lib/api/xmlrpc/v1/sample_clients/php/util.php @@ -1,54 +1,53 @@ -

    This sample can be runned without changes against sample database testlinkAPI'; - echo ('
    that you will find on [YOUR TL INSTALLATION DIR]' . '\\docs\\db_sample\\'); - echo '



    '; +

    This sample can be runned without changes against sample database testlinkAPI'; + echo '
    that you will find on [YOUR TL INSTALLATION DIR]' . + '\\docs\\db_sample\\'; + echo '



    '; +} + +function runTest(&$client, $method, $args, $feedback_id = 1) +{ + echo __FUNCTION__ . '
    '; + new dBug($args); + $html_id = "result_{$feedback_id}"; + + $msg_click_to_show = "click to show XML-RPC Client Debug Info"; + echo "
    + {$msg_click_to_show}"; + echo '

    "; + + echo "
    Result was: "; + new dBug($response); + echo "
    "; + echo "


    "; + + return $response; } - -function runTest(&$client,$method,$args,$feedback_id=1) -{ - - echo __FUNCTION__ . '
    '; - new dBug($args); - $html_id="result_{$feedback_id}"; - - $msg_click_to_show="click to show XML-RPC Client Debug Info"; - echo "
    - {$msg_click_to_show}"; - echo '

    "; - - echo "
    Result was: "; - new dBug($response); - echo "
    "; - echo "


    "; - - return $response; -} diff --git a/lib/api/xmlrpc/v1/sample_clients/python/clientCreateUser.py b/lib/api/xmlrpc/v1/sample_clients/python/clientCreateUser.py new file mode 100644 index 0000000000..606592cc26 --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/python/clientCreateUser.py @@ -0,0 +1,67 @@ +#!/usr/bin/python + +# for python 2 +import xmlrpclib + +serverName="YOURSERVER" + +SERVER_URL = "https://%s.si.c-s.fr/lib/api/xmlrpc/v1/xmlrpc.php" + +devKey="YOURAPIKEY" + +server = xmlrpclib.Server(SERVER_URL%serverName) + +print(server.tl.about()) + +# check Key +if not (server.tl.checkDevKey({'devKey': devKey})): + raise "Wrong key" + +args = {'devKey': devKey, + 'login': 'uapi', + 'firstname': 'User', + 'lastname': 'API', + 'email': 'user.api@your.domain.org', + 'password': 'yourpwd'} + +print("Create user with password...") +userID = server.tl.createUser(args) + +if isinstance(userID, str): + print("User %s created."%userID) +else: + print("Something's wrong: ") + for err in userID: + print(err['message']) + +args = {'devKey': devKey, + 'login': 'uapi2', + 'firstname': 'User', + 'lastname': 'API2', + 'email': 'user.api2@your.domain.org'} + +print("Create user without password...") +userID = server.tl.createUser(args) + +if isinstance(userID, str): + print("User %s created."%userID) +else: + print("Something's wrong: ") + for err in userID: + print(err['message']) + +args = {'devKey': devKey, + 'login': 'uapi2', + 'firstname': 'User', + 'lastname': 'API2', + 'email': 'user.api2@your.domain.org'} + +print("Create user with existing uid...") +userID = server.tl.createUser(args) + +if isinstance(userID, str): + print("User %s created."%userID) +else: + print("Something's wrong: ") + for err in userID: + print(err['message']) diff --git a/lib/api/xmlrpc/v1/sample_clients/python/clientGetTestCaseForTestSuite.py b/lib/api/xmlrpc/v1/sample_clients/python/clientGetTestCaseForTestSuite.py index c5a04f8bf0..4dfff10e6d 100644 --- a/lib/api/xmlrpc/v1/sample_clients/python/clientGetTestCaseForTestSuite.py +++ b/lib/api/xmlrpc/v1/sample_clients/python/clientGetTestCaseForTestSuite.py @@ -15,4 +15,4 @@ data["details"] = "full" tcinfo = conn.tl.getTestCasesForTestSuite(data) -print tcinfo \ No newline at end of file +print(tcinfo) \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/python/clientSample.py b/lib/api/xmlrpc/v1/sample_clients/python/clientSample.py index 871b80cef8..2da90aae9e 100644 --- a/lib/api/xmlrpc/v1/sample_clients/python/clientSample.py +++ b/lib/api/xmlrpc/v1/sample_clients/python/clientSample.py @@ -22,8 +22,8 @@ def getInfo(self): # substitute your Dev Key Here client = TestlinkAPIClient("f2a979d533cdd9761434bba60a88e4d8") # get info about the server -print client.getInfo() +print(client.getInfo()) # Substitute for tcid and tpid that apply to your project result = client.reportTCResult(1132, 56646, "p") # Typically you'd want to validate the result here and probably do something more useful with it -print "reportTCResult result was: %s" %(result) \ No newline at end of file +print("reportTCResult result was: %s" %(result)) \ No newline at end of file diff --git a/lib/api/xmlrpc/v1/sample_clients/python/clientSetRole.py b/lib/api/xmlrpc/v1/sample_clients/python/clientSetRole.py new file mode 100644 index 0000000000..23fbf748d3 --- /dev/null +++ b/lib/api/xmlrpc/v1/sample_clients/python/clientSetRole.py @@ -0,0 +1,48 @@ +#!/usr/bin/python + +# for python 2 +import xmlrpclib + +serverName="YOURSERVER" + +SERVER_URL = "https://%s.si.c-s.fr/lib/api/xmlrpc/v1/xmlrpc.php" + +devKey="YOURAPIKEY" + +server = xmlrpclib.Server(SERVER_URL%serverName) + +print(server.tl.about()) + +# check Key +if not (server.tl.checkDevKey({'devKey': devKey})): + raise "Wrong key" + +args = {'devKey': devKey, + 'userid': 2, + 'testprojectid': 1, + 'rolename': 'leader'} + +print("Set role leader to a user...") +res = server.tl.setUserRoleOnProject(args) + +if res == True: + print("The role %s is granted to the user %s on the project %s."%(args['rolename'], args['userid'], args['testprojectid'])) +else: + print("Something's wrong: ") + for err in res: + print(err['message']) + +args = {'devKey': devKey, + 'userid': 9999, + 'testprojectid': 1, + 'rolename': 'leader'} + +print("Set role leader to a non existing user...") +res = server.tl.setUserRoleOnProject(args) + +if res == True: + print("The role %s is granted to the user %s on the project %s."%(args['rolename'], args['userid'], args['testprojectid'])) +else: + print("Something's wrong: ") + for err in res: + print(err['message']) diff --git a/lib/api/xmlrpc/v1/sample_extended_server/extended_server.php b/lib/api/xmlrpc/v1/sample_extended_server/extended_server.php index 25ffcfa3d9..f0e5910960 100644 --- a/lib/api/xmlrpc/v1/sample_extended_server/extended_server.php +++ b/lib/api/xmlrpc/v1/sample_extended_server/extended_server.php @@ -1,98 +1,118 @@ - 'this:getTestSuiteIDByName', - 'tl.uploadStats' => 'this:uploadStats' -); - parent::__construct($callbacks); - } - - private function _getTestSuiteByName($args) { - $status_ok=false; - $testSuiteName = $args[self::$testSuiteNameParamName]; - - $result = $this->tsuiteMgr->get_by_name($testSuiteName); - - $num = sizeof($result); - if ($num == 0) { - $msg = $msg_prefix . sprintf("Name %s does not belong to a test suite present on system!", $testSuiteName); - $this->errors[] = new IXR_Error(8004, $msg); - } else { - // Check project id - foreach ($result as $row) { - $projectid = $this->tsuiteMgr->getTestProjectFromTestSuite($row['id']); - if ($projectid == $args[self::$testProjectIDParamName]) { - $result[0] = $row; - $status_ok=true; - break; - } - } - - if (!$status_ok) { - $tprojectInfo=$this->tprojectMgr->get_by_id($args[self::$testProjectIDParamName]); - $msg= sprintf(TESTSUITE_DONOTBELONGTO_TESTPROJECT_STR, $result[0]['id'], - $tprojectInfo['name'], $args[self::$testProjectIDParamName]); - $this->errors[] = new IXR_Error(TESTSUITE_DONOTBELONGTO_TESTPROJECT,$msg_prefix . $msg); - } - } - - return $status_ok ? $result : $this->errors; - } - - public function getTestSuiteIDByName($args) { - $msg_prefix="(" .__FUNCTION__. ") - "; - $status_ok=true; - $this->_setArgs($args); - - $checkFunctions = array('authenticate','checkTestSuiteName'); - $status_ok = $this->_runChecks($checkFunctions,$msg_prefix) && $this->userHasRight("mgt_view_tc"); - - if ($status_ok) { - $keys2check = array(self::$testSuiteNameParamName); - - foreach ($keys2check as $key) { - if (!$this->_isParamPresent($key)) { - $this->errors[] = new IXR_Error(NO_TESTSUITENAME, - $msg_prefix . NO_TESTSUITENAME_STR); - $status_ok=false; - } - } - } - - return $status_ok ? $this->_getTestSuiteByName($this->args) : $this->errors; - } - - public function uploadStats($args) { - foreach ($args as $key => $value) { - switch ($key) { - case devKey: - break; - default: - $stat_msg = sprintf("%s: %s = %s\n", - $_SERVER['REMOTE_ADDR'], $key, $value); - syslog(LOG_INFO, $stat_msg); - break; - } - } - } -} - -$XMLRPCServer = new SampleXMLRPCServer(); - + 'this:getTestSuiteIDByName', + 'tl.uploadStats' => 'this:uploadStats' + ); + parent::__construct($callbacks); + } + + private function _getTestSuiteByName($args) + { + $status_ok = false; + $testSuiteName = $args[self::$testSuiteNameParamName]; + + $result = $this->tsuiteMgr->get_by_name($testSuiteName); + + $num = count($result); + if ($num == 0) { + $msg = $msg_prefix . + sprintf( + "Name %s does not belong to a test suite present on system!", + $testSuiteName); + $this->errors[] = new IXR_Error(8004, $msg); + } else { + // Check project id + foreach ($result as $row) { + $projectid = $this->tsuiteMgr->getTestProjectFromTestSuite( + $row['id']); + if ($projectid == $args[self::$testProjectIDParamName]) { + $result[0] = $row; + $status_ok = true; + break; + } + } + + if (! $status_ok) { + $tprojectInfo = $this->tprojectMgr->get_by_id( + $args[self::$testProjectIDParamName]); + $msg = sprintf(TESTSUITE_DONOTBELONGTO_TESTPROJECT_STR, + $result[0]['id'], $tprojectInfo['name'], + $args[self::$testProjectIDParamName]); + $this->errors[] = new IXR_Error( + TESTSUITE_DONOTBELONGTO_TESTPROJECT, $msg_prefix . $msg); + } + } + + return $status_ok ? $result : $this->errors; + } + + public function getTestSuiteIDByName($args) + { + $msg_prefix = "(" . __FUNCTION__ . ") - "; + $status_ok = true; + $this->_setArgs($args); + + $checkFunctions = array( + 'authenticate', + 'checkTestSuiteName' + ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->userHasRight("mgt_view_tc"); + + if ($status_ok) { + $keys2check = array( + self::$testSuiteNameParamName + ); + + foreach ($keys2check as $key) { + if (! $this->_isParamPresent($key)) { + $this->errors[] = new IXR_Error(NO_TESTSUITENAME, + $msg_prefix . NO_TESTSUITENAME_STR); + $status_ok = false; + } + } + } + + return $status_ok ? $this->_getTestSuiteByName($this->args) : $this->errors; + } + + public function uploadStats($args) + { + foreach ($args as $key => $value) { + switch ($key) { + case devKey: + break; + default: + $stat_msg = sprintf("%s: %s = %s\n", $_SERVER['REMOTE_ADDR'], + $key, $value); + syslog(LOG_INFO, $stat_msg); + break; + } + } + } +} + +$XMLRPCServer = new SampleXMLRPCServer(); + ?> diff --git a/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTest.php b/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTest.php index d7f915f719..2ac3b4d04c 100644 --- a/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTest.php +++ b/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTest.php @@ -1,996 +1,1015 @@ - - * @package TestlinkAPI - * @link http://testlink.org/api/ - */ - -require_once dirname(__FILE__) . '/../../../third_party/xml-rpc/class-IXR.php'; -require_once dirname(__FILE__) . '/../APIErrors.php'; -require_once dirname(__FILE__) . '/TestlinkXMLRPCServerTestData.php'; -require_once 'PHPUnit/Framework.php'; -require_once 'PHPUnit/TextUI/TestRunner.php'; - -/** - * Unit tests for the Testlink API - * - * @author Asiel Brumfield - * @since Class available since Release 1.8.0 - * @version 1.0 - */ -class TestlinkXMLRPCServerTest extends PHPUnit_Framework_TestCase -{ - protected $client; - protected $SERVER_URL = "http://localhost/testlink_trunk/lib/api/xmlrpc.php"; - - function setUp() - { - // This is the path to the server and will vary from machine to machine - $this->client = $client = new IXR_Client($this->SERVER_URL); - // run IXR_Client in debug mode showing verbose output - $this->client->debug = true; - //$this->setupTestMode(); - } - - private function setupTestMode() - { - $data["testmode"] = true; - - if(!$this->client->query('tl.setTestMode', $data)) { - echo "\n" . $this->getName() . " >> problem setting testMode - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $this->assertEquals(true, $this->client->getResponse()); - } - - public static function suite() - { - $suite = new PHPUnit_Framework_TestSuite; - - // Run specific individual tests - $suite->addTest(new TestlinkXMLRPCServerTest('testSayHello')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithInvalidDevKey')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithoutDevKey')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithEmptyDevKey')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithoutTCID')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithInvalidTCID')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithoutNonIntTCID')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithoutTPID')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultRequestWithoutStatus')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultRequestWithInvalidStatus')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultRequestWithBlockedStatus')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultRequestWithPassedStatus')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultValidRequest')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithNoParams')); - - - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultRequestWithValidBuildID')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultNotGuessingBuildID')); - - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithNotes')); - $suite->addTest(new TestlinkXMLRPCServerTest('testCreateBuildWithoutNotes')); - $suite->addTest(new TestlinkXMLRPCServerTest('testCreateBuildWithNotes')); - $suite->addTest(new TestlinkXMLRPCServerTest('testCreateBuildWithInvalidTPID')); - $suite->addTest(new TestlinkXMLRPCServerTest('testValidDevKeyWorks')); - $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjects')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultRequestWithFailedStatus')); - $suite->addTest(new TestlinkXMLRPCServerTest('testCreateBuildWithInsufficientRights')); - $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectTestPlans')); - - - $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuite')); - $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuiteDeepFalse')); - - $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCaseIDByName')); - $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCaseIDByNameWithInvalidName')); - $suite->addTest(new TestlinkXMLRPCServerTest('testRepeat')); - $suite->addTest(new TestlinkXMLRPCServerTest('testAbout')); - $suite->addTest(new TestlinkXMLRPCServerTest('testNonExistantMethod')); - - //NEW TESTS - $suite->addTest(new TestlinkXMLRPCServerTest('testCreateBuildWithInsufficientRights')); - $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithInsufficientRights')); - $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuiteWithInsufficientRights')); - $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCaseIDByNameWithInsufficientRights')); - - $suite->addTest(new TestlinkXMLRPCServerTest('testGetLastTestResult')); - - - - //INCOMPLETE - - - //THERE IS NO RIGHTS ASSOCIATED WITH THIS YET - $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectsWithInsufficientRights')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithInvalidTCIDAndTPIDCombo')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithTimestamp')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectTestPlansWithInvalidID')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectTestPlansWithoutTestProjectID')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testGetTestSuitesForTestPlan')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testGetTestSuitesForTestPlanWithoutTestPlanID')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuiteWithInvalidSuiteID')); - //$suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuiteWithoutSuiteID')); - - - // run all the tests - //$suite->addTestSuite('TestlinkXMLRPCServerTest'); - return $suite; - } - - function testSayHello() - { - if (!$this->client->query('tl.sayHello')) { - die('big time problem ' . $this->client->getErrorCode() . ' : ' . - $this->client->getErrorMessage()); - } - - $this->assertEquals('Hello!', $this->client->getResponse()); - } - - function testReportTCResultWithInvalidDevKey() - { - $data = array(); - $data["devKey"] = "wrongKey"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INVALID_AUTH"); - $expectedResult[0]["message"] = constant("INVALID_AUTH_STR"); - - $this->assertEquals($expectedResult, $this->client->getResponse()); - } - - - function testReportTCResultWithInsufficientRights() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - // set the status to blocked - $data["status"] = "b"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); - $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); - - $result = $this->client->getResponse(); - $this->assertEquals($expectedResult, $result); - } - - - - function testReportTCResultWithoutDevKey() - { - $data = array(); - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("NO_DEV_KEY"); - $expectedResult[0]["message"] = constant("NO_DEV_KEY_STR"); - - $this->assertEquals($expectedResult, $this->client->getResponse()); - } - - function testReportTCResultWithEmptyDevKey() - { - $data = array(); - $data["devKey"] = ""; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INVALID_AUTH"); - $expectedResult[0]["message"] = constant("INVALID_AUTH_STR"); - - $this->assertEquals($expectedResult, $this->client->getResponse()); - } - - function testReportTCResultWithoutTCID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("NO_TCASEID"); - $expectedResult[0]["message"] = constant("NO_TCASEID_STR"); - - var_dump($expectedResult); - - var_dump($this->client->getResponse()); - - $this->assertEquals($expectedResult, $this->client->getResponse()); - } - - function testReportTCResultWithInvalidTCID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["status"] = "f"; - $data["testcaseid"] = -100; // shouldn't have a negative number for TCID in db - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INVALID_TCASEID"); - $expectedResult[0]["message"] = constant("INVALID_TCASEID_STR"); - - $result = $this->client->getResponse(); - //print_r($result); - //print_r($expectedResult); - $this->assertEquals($expectedResult, $result); - } - - function testReportTCResultWithoutNonIntTCID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = "notAnInt"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array containing the errors that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("TCASEID_NOT_INTEGER"); - $expectedResult[0]["message"] = constant("TCASEID_NOT_INTEGER_STR"); - $expectedResult[1]["code"] = constant("INVALID_TCASEID"); - $expectedResult[1]["message"] = constant("INVALID_TCASEID_STR"); - - $result = $this->client->getResponse(); - - $this->assertEquals($expectedResult, $this->client->getResponse()); - } - - // TODO: Implement - function testReportTCResultWithoutTPID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - // dependant on data in the sql file - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("NO_TPLANID"); - $expectedResult[0]["message"] = constant("NO_TPLANID_STR"); - - $response = $this->client->getResponse(); - $this->assertEquals($expectedResult, $response); - } - - function testReportTCResultRequestWithoutStatus() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("NO_STATUS"); - $expectedResult[0]["message"] = constant("NO_STATUS_STR"); - - $response = $this->client->getResponse(); - $this->assertEquals($expectedResult, $response); - } - - function testReportTCResultRequestWithInvalidStatus() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - $data["status"] = "invalidStatus"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INVALID_STATUS"); - $expectedResult[0]["message"] = constant("INVALID_STATUS_STR"); - - $response = $this->client->getResponse(); - $this->assertEquals($expectedResult, $response); - } - - function testReportTCResultRequestWithBlockedStatus() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - // set the status to blocked - $data["status"] = "b"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - var_dump($response); - // Just check the size is good since we don't know the insert id - $this->assertEquals(3, sizeof($response[0])); - } - - function testReportTCResultRequestWithPassedStatus() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - // set the status to passed - $data["status"] = "p"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - // Just check the size is good since we don't know the insert id - $this->assertEquals(3, sizeof($response[0])); - } - - function testReportTCResultRequestWithFailedStatus() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - // set the status to failed - $data["status"] = "f"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - // Just check the size is good since we don't know the insert id - $this->assertEquals(3, sizeof($response[0])); - } - - function testReportTCResultWithNoParams() - { - $data = array(); - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $expectedResult[0]["code"] = constant("NO_DEV_KEY"); - $expectedResult[0]["message"] = constant("NO_DEV_KEY_STR"); - $this->assertEquals($expectedResult, $this->client->getResponse()); - } - - // TODO: Implement - function testReportTCResultWithInvalidTCIDAndTPIDCombo() - { - // TCID_NOT_IN_TPID, TCID_NOT_IN_TPID_STR - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - - function testReportTCResultValidRequest() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - $data["status"] = "p"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - // Just check the size is good since we don't know the insert id - $this->assertEquals(3, sizeof($response[0])); - } - - function testGetLastTestResult() - { - //Setup a Known Response by reporting a block - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - $data["status"] = "b"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - -//Now Building our get last test result - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - if(!$this->client->query('tl.getLastTestResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - $response = $this->client->getResponse(); - // Just check the size is good since we don't know the insert id - print_r($response); - $this->assertEquals(9, sizeof($response[0])); - $this->assertEquals('b', $response[0]['status']); - - } - - function testReportTCResultRequestWithValidBuildID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - $data["guess"] = false; - $data["status"] = "f"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - //$expectedResult[0]["message"] = constant("GENERAL_SUCCESS_STR"); - //$expectedResult[0]["status"] = true; - - $response = $this->client->getResponse(); - // Just check the size is good since we don't know the insert id - $this->assertEquals(3, sizeof($response[0])); - } - - function testReportTCResultNotGuessingBuildID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - // don't allow guessing the build id - $data["guess"] = false; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // build up an array contining the error that should come back - $expectedResult = array(); - $expectedResult[0]["code"] = constant("BUILDID_NOGUESS"); - $expectedResult[0]["message"] = constant("BUILDID_NOGUESS_STR"); - $expectedResult[1]["code"] = constant("NO_BUILDID"); - $expectedResult[1]["message"] = constant("NO_BUILDID_STR"); - - $response = $this->client->getResponse(); - $this->assertEquals($expectedResult, $response); - } - - function testReportTCResultWithTimestamp() - { - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - - function testReportTCResultWithNotes() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["status"] = "p"; - $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; - $data["notes"] = "this is a note about the test"; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - // Just check the size is good since we don't know the insert id - var_dump($response); - - $this->assertEquals(3, sizeof($response[0])); - } - - function testCreateBuildWithInsufficientRights() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; - $data["buildname"] = "Another test build from " . strftime("%c"); - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - - if(!$this->client->query('tl.createBuild', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); - $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); - - $result = $this->client->getResponse(); - $this->assertEquals($expectedResult, $result); - } - - function testCreateBuildWithoutNotes() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["buildname"] = "Another test build from " . strftime("%c"); - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - - if(!$this->client->query('tl.createBuild', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - $response = $this->client->getResponse(); - var_dump($response); - $this->assertEquals(3, sizeof($response[0])); - } - - function testCreateBuildWithNotes() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["buildname"] = "Another notes test build from " . strftime("%c"); - $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; - $data["buildnotes"] = "Some notes from the build created at " . strftime("%c"); - - if(!$this->client->query('tl.createBuild', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - $response = $this->client->getResponse(); - $this->assertEquals(3, sizeof($response[0])); - } - - function testCreateBuildWithInvalidTPID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["buildname"] = "Another test build from " . strftime("%c"); - $data["testplanid"] = -1; - $data["buildnotes"] = "Some notes from the build created at " . strftime("%c"); - - if(!$this->client->query('tl.createBuild', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INVALID_TPLANID"); - $expectedResult[0]["message"] = sprintf(constant("INVALID_TPLANID_STR"), $data["testplanid"]); - - $result = $this->client->getResponse(); - //print_r($expectedResult); - //print_r($result); - - $this->assertEquals($expectedResult, $result); - } - - function testValidDevKeyWorks() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - - if(!$this->client->query('tl.reportTCResult', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - // The response should not have any errors related to the devKey - $response = $this->client->getResponse(); - - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INVALID_AUTH"); - $expectedResult[0]["message"] = constant("INVALID_AUTH_STR"); - $this->assertNotEquals($expectedResult, $this->client->getResponse()); - - $expectedResult = array(); - $expectedResult[0]["code"] = constant("NO_DEV_KEY"); - $expectedResult[0]["message"] = constant("NO_DEV_KEY_STR"); - $this->assertNotEquals($expectedResult, $this->client->getResponse()); - } - - function testGetProjects() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - - if(!$this->client->query('tl.getProjects', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - - $expectedResult[0]["id"] = "1"; - $expectedResult[0]["notes"] = "

    A project for testing

    "; - - $expectedResult[0]["color"] = ""; - - $expectedResult[0]["active"] = "1"; - $expectedResult[0]["option_reqs"] = "1"; - $expectedResult[0]["option_priority"] = "1"; - $expectedResult[0]["prefix"] = ""; - $expectedResult[0]["tc_counter"] = "0"; - $expectedResult[0]["option_automation"] = "0"; - $expectedResult[0]["name"] = "Test Project"; - - $response = $this->client->getResponse(); - - var_dump($response); - var_dump($expectedResult); - - $this->assertEquals($expectedResult, $response); - } - - function testGetProjectsWithInsufficientRights() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; - - if(!$this->client->query('tl.getProjects', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); - $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); - - $result = $this->client->getResponse(); - $this->assertEquals($expectedResult, $result); - - - } - - function testGetProjectTestPlansWithInvalidID() - { - //TODO: Implement - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - function testGetProjectTestPlansWithoutTestProjectID() - { - //TODO: Implement - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - - function testGetProjectTestPlans() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testprojectid"] = 1; - - if(!$this->client->query('tl.getProjectTestPlans', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $testplanID = 2; - $expectedResult[$testplanID]["id"] = $testplanID; - $expectedResult[$testplanID]["name"] = "A test plan for testing"; - // characters like

    are stripped - $expectedResult[$testplanID]["notes"] = "

    A description of a test plan for testing

    "; - $expectedResult[$testplanID]["active"] = "1"; - $expectedResult[$testplanID]["testproject_id"] = "1"; - - $expectedResult = array($expectedResult); - - - $response = $this->client->getResponse(); - //print_r($expectedResult); - //print_r($response); - - $this->assertEquals($expectedResult, $response); - } - - function testGetTestSuitesForTestPlan() - { - //TODO: Implement - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - - function testGetTestSuitesForTestPlanWithoutTestPlanID() - { - //TODO: Implement - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - - function testGetTestCasesForTestSuite() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testsuiteid"] = 3; - - if(!$this->client->query('tl.getTestCasesForTestSuite', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $expectedResult[0]["id"] = 11; - $expectedResult[0]["name"] = "test case in child suite"; - $expectedResult[0]["parent_id"] = 10; - $expectedResult[0]["node_type_id"] = 3; - $expectedResult[0]["node_order"] = 100; - $expectedResult[1]["id"] = 4; - $expectedResult[1]["name"] = "First test case version 3"; - $expectedResult[1]["parent_id"] = 3; - $expectedResult[1]["node_type_id"] = 3; - $expectedResult[1]["node_order"] = 100; - $expectedResult[2]["id"] = 6; - $expectedResult[2]["name"] = "Another test case"; - $expectedResult[2]["parent_id"] = 3; - $expectedResult[2]["node_type_id"] = 3; - $expectedResult[2]["node_order"] = 100; - - $response = $this->client->getResponse(); - - $this->assertEquals($expectedResult, $response, "arrays do not match"); - } - - function testGetTestCasesForTestSuiteWithInsufficientRights() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; - $data["testsuiteid"] = 3; - - if(!$this->client->query('tl.getTestCasesForTestSuite', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); - $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); - - $result = $this->client->getResponse(); - $this->assertEquals($expectedResult, $result); - } - - function testGetTestCasesForTestSuiteDeepFalse() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testsuiteid"] = 3; - $data["deep"] = false; - - if(!$this->client->query('tl.getTestCasesForTestSuite', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $expectedResult[0]["id"] = 4; - $expectedResult[0]["name"] = "First test case version 3"; - $expectedResult[0]["parent_id"] = 3; - $expectedResult[0]["node_type_id"] = 3; - $expectedResult[0]["node_order"] = 100; - $expectedResult[1]["id"] = 6; - $expectedResult[1]["name"] = "Another test case"; - $expectedResult[1]["parent_id"] = 3; - $expectedResult[1]["node_type_id"] = 3; - $expectedResult[1]["node_order"] = 100; - - $response = $this->client->getResponse(); - //print_r($response); - $this->assertEquals($expectedResult, $response, "arrays do not match"); - } - - function testGetTestCasesForTestSuiteWithoutSuiteID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testsuiteid"] = 3; - - if(!$this->client->query('tl.getTestCasesForTestSuite', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - //print_r($response); - - //TODO: Implement - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - - function testGetTestCasesForTestSuiteWithInvalidSuiteID() - { - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testsuiteid"] = 2000; - - if(!$this->client->query('tl.getTestCasesForTestSuite', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $response = $this->client->getResponse(); - //print_r($response); - - //TODO: Implement - throw new PHPUnit_Framework_IncompleteTestError('This test is not yet implemented'); - } - - - function testGetTestCaseIDByName() - { - $tcName = "First test case version 3"; - - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcasename"] = $tcName; - - if(!$this->client->query('tl.getTestCaseIDByName', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $expectedResult[0]["id"] = TestlinkXMLRPCServerTestData::testTCID; - $expectedResult[0]["name"] = $tcName; - $expectedResult[0]["parent_id"] = "1"; - $expectedResult[0]["tsuite_name"] = "Top Level Suite"; - $expectedResult[0]["tc_external_id"] = "0"; - - $response = $this->client->getResponse(); - //print_r($response); - //print_r($expectedResult); - - $this->assertEquals($expectedResult, $response); - } - - function testGetTestCaseIDByNameWithInsufficientRights() - { - $tcName = "First test case version 3"; - - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; - $data["testcasename"] = $tcName; - - if(!$this->client->query('tl.getTestCaseIDByName', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - $expectedResult = array(); - $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); - $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); - - $result = $this->client->getResponse(); - $this->assertEquals($expectedResult, $result); - } - - function testGetTestCaseIDByNameWithInvalidName() - { - $tcName = "A Test case that does not exist"; - - $data = array(); - $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; - $data["testcasename"] = $tcName; - - if(!$this->client->query('tl.getTestCaseIDByName', $data)) { - echo "\n" . $this->getName() . " >> something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $expectedResult = array(); - $expectedResult[0]["code"] = constant("NO_TESTCASE_BY_THIS_NAME"); - $expectedResult[0]["message"] = "(getTestCaseIDByName) - " . constant("NO_TESTCASE_BY_THIS_NAME_STR"); - - $response = $this->client->getResponse(); - //print_r($expectedResult); - //print_r($response); - - $this->assertEquals($expectedResult, $response); - } - - - function testRepeat() - { - $data = array(); - $data["str"] = "I like to talk to myself"; - - if(!$this->client->query('tl.repeat', $data)) - { - echo "\n\n" . $this->getName() . "something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - - $this->assertEquals("You said: " . $data["str"], $this->client->getResponse()); - } - - function testAbout() - { - if(!$this->client->query('tl.about', null)) - { - echo "\n\n" . $this->getName() . "something went really wrong - " . $this->client->getErrorCode() . - $this->client->getErrorMessage(); - } - else - { - echo "success!"; - } - } - - function testNonExistantMethod() - { - $this->assertFalse($this->client->query('tl.noSuchMethodExists')); - } - -} - -// this is only necessary if you want to run "php FileName.php" -// otherwise you can just run "phpunit FileName.php" and it should work -//PHPUnit_TextUI_TestRunner::run(TestlinkXMLRPCServerTest::suite()); - + + * @package TestlinkAPI + * @link http://testlink.org/api/ + */ +require_once dirname(__FILE__) . '/../../../third_party/xml-rpc/class-IXR.php'; +require_once dirname(__FILE__) . '/../APIErrors.php'; +require_once dirname(__FILE__) . '/TestlinkXMLRPCServerTestData.php'; +require_once 'PHPUnit/Framework.php'; +require_once 'PHPUnit/TextUI/TestRunner.php'; + +/** + * Unit tests for the Testlink API + * + * @author Asiel Brumfield + * @since Class available since Release 1.8.0 + * @version 1.0 + */ +class TestlinkXMLRPCServerTest extends PHPUnit_Framework_TestCase +{ + + protected $client; + + protected $SERVER_URL = "http://localhost/testlink_trunk/lib/api/xmlrpc.php"; + + private function setUp() + { + // This is the path to the server and will vary from machine to machine + $this->client = new IXR_Client($this->SERVER_URL); + // run IXR_Client in debug mode showing verbose output + $this->client->debug = true; + } + + private function setupTestMode() + { + $data["testmode"] = true; + + if (! $this->client->query('tl.setTestMode', $data)) { + echo "\n" . $this->getName() . " >> problem setting testMode - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $this->assertEquals(true, $this->client->getResponse()); + } + + public static function suite() + { + $suite = new PHPUnit_Framework_TestSuite(); + + // Run specific individual tests + $suite->addTest(new TestlinkXMLRPCServerTest('testSayHello')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithInvalidDevKey')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithoutDevKey')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithEmptyDevKey')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithoutTCID')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithInvalidTCID')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithoutNonIntTCID')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithoutTPID')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testReportTCResultRequestWithoutStatus')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testReportTCResultRequestWithInvalidStatus')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testReportTCResultRequestWithBlockedStatus')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testReportTCResultRequestWithPassedStatus')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultValidRequest')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithNoParams')); + + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testReportTCResultRequestWithValidBuildID')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultNotGuessingBuildID')); + + $suite->addTest( + new TestlinkXMLRPCServerTest('testReportTCResultWithNotes')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testCreateBuildWithoutNotes')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testCreateBuildWithNotes')); + $suite->addTest( + new TestlinkXMLRPCServerTest('testCreateBuildWithInvalidTPID')); + $suite->addTest(new TestlinkXMLRPCServerTest('testValidDevKeyWorks')); + $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjects')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testReportTCResultRequestWithFailedStatus')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testCreateBuildWithInsufficientRights')); + $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectTestPlans')); + + $suite->addTest( + new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuite')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testGetTestCasesForTestSuiteDeepFalse')); + + $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCaseIDByName')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testGetTestCaseIDByNameWithInvalidName')); + $suite->addTest(new TestlinkXMLRPCServerTest('testRepeat')); + $suite->addTest(new TestlinkXMLRPCServerTest('testAbout')); + $suite->addTest(new TestlinkXMLRPCServerTest('testNonExistantMethod')); + + // NEW TESTS + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testCreateBuildWithInsufficientRights')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testReportTCResultWithInsufficientRights')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testGetTestCasesForTestSuiteWithInsufficientRights')); + $suite->addTest( + new TestlinkXMLRPCServerTest( + 'testGetTestCaseIDByNameWithInsufficientRights')); + + $suite->addTest(new TestlinkXMLRPCServerTest('testGetLastTestResult')); + + // INCOMPLETE + + // THERE IS NO RIGHTS ASSOCIATED WITH THIS YET - $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectsWithInsufficientRights')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithInvalidTCIDAndTPIDCombo')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testReportTCResultWithTimestamp')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectTestPlansWithInvalidID')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testGetProjectTestPlansWithoutTestProjectID')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestSuitesForTestPlan')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestSuitesForTestPlanWithoutTestPlanID')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuiteWithInvalidSuiteID')); + // $suite->addTest(new TestlinkXMLRPCServerTest('testGetTestCasesForTestSuiteWithoutSuiteID')); + + // run all the tests + // $suite->addTestSuite('TestlinkXMLRPCServerTest'); + return $suite; + } + + private function testSayHello() + { + if (! $this->client->query('tl.sayHello')) { + die( + 'big time problem ' . $this->client->getErrorCode() . ' : ' . + $this->client->getErrorMessage()); + } + + $this->assertEquals('Hello!', $this->client->getResponse()); + } + + private function testReportTCResultWithInvalidDevKey() + { + $data = array(); + $data["devKey"] = "wrongKey"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INVALID_AUTH"); + $expectedResult[0]["message"] = constant("INVALID_AUTH_STR"); + + $this->assertEquals($expectedResult, $this->client->getResponse()); + } + + private function testReportTCResultWithInsufficientRights() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + // set the status to blocked + $data["status"] = "b"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); + $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); + + $result = $this->client->getResponse(); + $this->assertEquals($expectedResult, $result); + } + + private function testReportTCResultWithoutDevKey() + { + $data = array(); + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("NO_DEV_KEY"); + $expectedResult[0]["message"] = constant("NO_DEV_KEY_STR"); + + $this->assertEquals($expectedResult, $this->client->getResponse()); + } + + private function testReportTCResultWithEmptyDevKey() + { + $data = array(); + $data["devKey"] = ""; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INVALID_AUTH"); + $expectedResult[0]["message"] = constant("INVALID_AUTH_STR"); + + $this->assertEquals($expectedResult, $this->client->getResponse()); + } + + private function testReportTCResultWithoutTCID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("NO_TCASEID"); + $expectedResult[0]["message"] = constant("NO_TCASEID_STR"); + + var_dump($expectedResult); + + var_dump($this->client->getResponse()); + + $this->assertEquals($expectedResult, $this->client->getResponse()); + } + + private function testReportTCResultWithInvalidTCID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["status"] = "f"; + $data["testcaseid"] = - 100; // shouldn't have a negative number for TCID in db + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INVALID_TCASEID"); + $expectedResult[0]["message"] = constant("INVALID_TCASEID_STR"); + + $result = $this->client->getResponse(); + $this->assertEquals($expectedResult, $result); + } + + private function testReportTCResultWithoutNonIntTCID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = "notAnInt"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array containing the errors that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("TCASEID_NOT_INTEGER"); + $expectedResult[0]["message"] = constant("TCASEID_NOT_INTEGER_STR"); + $expectedResult[1]["code"] = constant("INVALID_TCASEID"); + $expectedResult[1]["message"] = constant("INVALID_TCASEID_STR"); + + $this->client->getResponse(); + + $this->assertEquals($expectedResult, $this->client->getResponse()); + } + + private function testReportTCResultWithoutTPID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + // dependant on data in the sql file + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("NO_TPLANID"); + $expectedResult[0]["message"] = constant("NO_TPLANID_STR"); + + $response = $this->client->getResponse(); + $this->assertEquals($expectedResult, $response); + } + + private function testReportTCResultRequestWithoutStatus() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("NO_STATUS"); + $expectedResult[0]["message"] = constant("NO_STATUS_STR"); + + $response = $this->client->getResponse(); + $this->assertEquals($expectedResult, $response); + } + + private function testReportTCResultRequestWithInvalidStatus() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + $data["status"] = "invalidStatus"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INVALID_STATUS"); + $expectedResult[0]["message"] = constant("INVALID_STATUS_STR"); + + $response = $this->client->getResponse(); + $this->assertEquals($expectedResult, $response); + } + + private function testReportTCResultRequestWithBlockedStatus() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + // set the status to blocked + $data["status"] = "b"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $response = $this->client->getResponse(); + var_dump($response); + // Just check the size is good since we don't know the insert id + $this->assertEquals(3, count($response[0])); + } + + private function testReportTCResultRequestWithPassedStatus() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + // set the status to passed + $data["status"] = "p"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $response = $this->client->getResponse(); + // Just check the size is good since we don't know the insert id + $this->assertEquals(3, count($response[0])); + } + + private function testReportTCResultRequestWithFailedStatus() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + // set the status to failed + $data["status"] = "f"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $response = $this->client->getResponse(); + // Just check the size is good since we don't know the insert id + $this->assertEquals(3, count($response[0])); + } + + private function testReportTCResultWithNoParams() + { + $data = array(); + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $expectedResult[0]["code"] = constant("NO_DEV_KEY"); + $expectedResult[0]["message"] = constant("NO_DEV_KEY_STR"); + $this->assertEquals($expectedResult, $this->client->getResponse()); + } + + // TODO: Implement + private function testReportTCResultWithInvalidTCIDAndTPIDCombo() + { + // TCID_NOT_IN_TPID, TCID_NOT_IN_TPID_STR + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testReportTCResultValidRequest() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + $data["status"] = "p"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $response = $this->client->getResponse(); + // Just check the size is good since we don't know the insert id + $this->assertEquals(3, count($response[0])); + } + + private function testGetLastTestResult() + { + // Setup a Known Response by reporting a block + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + $data["status"] = "b"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // Now Building our get last test result + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + if (! $this->client->query('tl.getLastTestResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + $response = $this->client->getResponse(); + // Just check the size is good since we don't know the insert id + print_r($response); + $this->assertEquals(9, count($response[0])); + $this->assertEquals('b', $response[0]['status']); + } + + private function testReportTCResultRequestWithValidBuildID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + $data["guess"] = false; + $data["status"] = "f"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $response = $this->client->getResponse(); + // Just check the size is good since we don't know the insert id + $this->assertEquals(3, count($response[0])); + } + + private function testReportTCResultNotGuessingBuildID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + // don't allow guessing the build id + $data["guess"] = false; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // build up an array contining the error that should come back + $expectedResult = array(); + $expectedResult[0]["code"] = constant("BUILDID_NOGUESS"); + $expectedResult[0]["message"] = constant("BUILDID_NOGUESS_STR"); + $expectedResult[1]["code"] = constant("NO_BUILDID"); + $expectedResult[1]["message"] = constant("NO_BUILDID_STR"); + + $response = $this->client->getResponse(); + $this->assertEquals($expectedResult, $response); + } + + private function testReportTCResultWithTimestamp() + { + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testReportTCResultWithNotes() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcaseid"] = TestlinkXMLRPCServerTestData::testTCID; + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["status"] = "p"; + $data["buildid"] = TestlinkXMLRPCServerTestData::testBuildID; + $data["notes"] = "this is a note about the test"; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $response = $this->client->getResponse(); + // Just check the size is good since we don't know the insert id + var_dump($response); + + $this->assertEquals(3, count($response[0])); + } + + private function testCreateBuildWithInsufficientRights() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; + $data["buildname"] = "Another test build from " . @strftime("%c"); + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + + if (! $this->client->query('tl.createBuild', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); + $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); + + $result = $this->client->getResponse(); + $this->assertEquals($expectedResult, $result); + } + + private function testCreateBuildWithoutNotes() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["buildname"] = "Another test build from " . @strftime("%c"); + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + + if (! $this->client->query('tl.createBuild', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + $response = $this->client->getResponse(); + var_dump($response); + $this->assertEquals(3, count($response[0])); + } + + private function testCreateBuildWithNotes() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["buildname"] = "Another notes test build from " . @strftime("%c"); + $data["testplanid"] = TestlinkXMLRPCServerTestData::testTPID; + $data["buildnotes"] = "Some notes from the build created at " . + @strftime("%c"); + + if (! $this->client->query('tl.createBuild', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + $response = $this->client->getResponse(); + $this->assertEquals(3, count($response[0])); + } + + private function testCreateBuildWithInvalidTPID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["buildname"] = "Another test build from " . @strftime("%c"); + $data["testplanid"] = - 1; + $data["buildnotes"] = "Some notes from the build created at " . + @strftime("%c"); + + if (! $this->client->query('tl.createBuild', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INVALID_TPLANID"); + $expectedResult[0]["message"] = sprintf(constant("INVALID_TPLANID_STR"), + $data["testplanid"]); + + $result = $this->client->getResponse(); + + $this->assertEquals($expectedResult, $result); + } + + private function testValidDevKeyWorks() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + + if (! $this->client->query('tl.reportTCResult', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + // The response should not have any errors related to the devKey + $this->client->getResponse(); + + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INVALID_AUTH"); + $expectedResult[0]["message"] = constant("INVALID_AUTH_STR"); + $this->assertNotEquals($expectedResult, $this->client->getResponse()); + + $expectedResult = array(); + $expectedResult[0]["code"] = constant("NO_DEV_KEY"); + $expectedResult[0]["message"] = constant("NO_DEV_KEY_STR"); + $this->assertNotEquals($expectedResult, $this->client->getResponse()); + } + + private function testGetProjects() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + + if (! $this->client->query('tl.getProjects', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + + $expectedResult[0]["id"] = "1"; + $expectedResult[0]["notes"] = "

    A project for testing

    "; + + $expectedResult[0]["color"] = ""; + + $expectedResult[0]["active"] = "1"; + $expectedResult[0]["option_reqs"] = "1"; + $expectedResult[0]["option_priority"] = "1"; + $expectedResult[0]["prefix"] = ""; + $expectedResult[0]["tc_counter"] = "0"; + $expectedResult[0]["option_automation"] = "0"; + $expectedResult[0]["name"] = "Test Project"; + + $response = $this->client->getResponse(); + + var_dump($response); + var_dump($expectedResult); + + $this->assertEquals($expectedResult, $response); + } + + private function testGetProjectsWithInsufficientRights() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; + + if (! $this->client->query('tl.getProjects', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); + $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); + + $result = $this->client->getResponse(); + $this->assertEquals($expectedResult, $result); + } + + private function testGetProjectTestPlansWithInvalidID() + { + // TODO: Implement + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testGetProjectTestPlansWithoutTestProjectID() + { + // TODO: Implement + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testGetProjectTestPlans() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testprojectid"] = 1; + + if (! $this->client->query('tl.getProjectTestPlans', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $testplanID = 2; + $expectedResult[$testplanID]["id"] = $testplanID; + $expectedResult[$testplanID]["name"] = "A test plan for testing"; + // characters like

    are stripped + $expectedResult[$testplanID]["notes"] = "

    A description of a test plan for testing

    "; + $expectedResult[$testplanID]["active"] = "1"; + $expectedResult[$testplanID]["testproject_id"] = "1"; + + $expectedResult = array( + $expectedResult + ); + + $response = $this->client->getResponse(); + + $this->assertEquals($expectedResult, $response); + } + + private function testGetTestSuitesForTestPlan() + { + // TODO: Implement + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testGetTestSuitesForTestPlanWithoutTestPlanID() + { + // TODO: Implement + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testGetTestCasesForTestSuite() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testsuiteid"] = 3; + + if (! $this->client->query('tl.getTestCasesForTestSuite', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $expectedResult[0]["id"] = 11; + $expectedResult[0]["name"] = "test case in child suite"; + $expectedResult[0]["parent_id"] = 10; + $expectedResult[0]["node_type_id"] = 3; + $expectedResult[0]["node_order"] = 100; + $expectedResult[1]["id"] = 4; + $expectedResult[1]["name"] = "First test case version 3"; + $expectedResult[1]["parent_id"] = 3; + $expectedResult[1]["node_type_id"] = 3; + $expectedResult[1]["node_order"] = 100; + $expectedResult[2]["id"] = 6; + $expectedResult[2]["name"] = "Another test case"; + $expectedResult[2]["parent_id"] = 3; + $expectedResult[2]["node_type_id"] = 3; + $expectedResult[2]["node_order"] = 100; + + $response = $this->client->getResponse(); + + $this->assertEquals($expectedResult, $response, "arrays do not match"); + } + + private function testGetTestCasesForTestSuiteWithInsufficientRights() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; + $data["testsuiteid"] = 3; + + if (! $this->client->query('tl.getTestCasesForTestSuite', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); + $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); + + $result = $this->client->getResponse(); + $this->assertEquals($expectedResult, $result); + } + + private function testGetTestCasesForTestSuiteDeepFalse() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testsuiteid"] = 3; + $data["deep"] = false; + + if (! $this->client->query('tl.getTestCasesForTestSuite', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $expectedResult[0]["id"] = 4; + $expectedResult[0]["name"] = "First test case version 3"; + $expectedResult[0]["parent_id"] = 3; + $expectedResult[0]["node_type_id"] = 3; + $expectedResult[0]["node_order"] = 100; + $expectedResult[1]["id"] = 6; + $expectedResult[1]["name"] = "Another test case"; + $expectedResult[1]["parent_id"] = 3; + $expectedResult[1]["node_type_id"] = 3; + $expectedResult[1]["node_order"] = 100; + + $response = $this->client->getResponse(); + $this->assertEquals($expectedResult, $response, "arrays do not match"); + } + + private function testGetTestCasesForTestSuiteWithoutSuiteID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testsuiteid"] = 3; + + if (! $this->client->query('tl.getTestCasesForTestSuite', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $this->client->getResponse(); + + // TODO: Implement + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testGetTestCasesForTestSuiteWithInvalidSuiteID() + { + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testsuiteid"] = 2000; + + if (! $this->client->query('tl.getTestCasesForTestSuite', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $this->client->getResponse(); + + // TODO: Implement + throw new PHPUnit_Framework_IncompleteTestError( + 'This test is not yet implemented'); + } + + private function testGetTestCaseIDByName() + { + $tcName = "First test case version 3"; + + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcasename"] = $tcName; + + if (! $this->client->query('tl.getTestCaseIDByName', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $expectedResult[0]["id"] = TestlinkXMLRPCServerTestData::testTCID; + $expectedResult[0]["name"] = $tcName; + $expectedResult[0]["parent_id"] = "1"; + $expectedResult[0]["tsuite_name"] = "Top Level Suite"; + $expectedResult[0]["tc_external_id"] = "0"; + + $response = $this->client->getResponse(); + + $this->assertEquals($expectedResult, $response); + } + + private function testGetTestCaseIDByNameWithInsufficientRights() + { + $tcName = "First test case version 3"; + + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::noRightsDevKey; + $data["testcasename"] = $tcName; + + if (! $this->client->query('tl.getTestCaseIDByName', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + $expectedResult = array(); + $expectedResult[0]["code"] = constant("INSUFFICIENT_RIGHTS"); + $expectedResult[0]["message"] = constant("INSUFFICIENT_RIGHTS_STR"); + + $result = $this->client->getResponse(); + $this->assertEquals($expectedResult, $result); + } + + private function testGetTestCaseIDByNameWithInvalidName() + { + $tcName = "A Test case that does not exist"; + + $data = array(); + $data["devKey"] = TestlinkXMLRPCServerTestData::testDevKey; + $data["testcasename"] = $tcName; + + if (! $this->client->query('tl.getTestCaseIDByName', $data)) { + echo "\n" . $this->getName() . " >> something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $expectedResult = array(); + $expectedResult[0]["code"] = constant("NO_TESTCASE_BY_THIS_NAME"); + $expectedResult[0]["message"] = "(getTestCaseIDByName) - " . + constant("NO_TESTCASE_BY_THIS_NAME_STR"); + + $response = $this->client->getResponse(); + + $this->assertEquals($expectedResult, $response); + } + + private function testRepeat() + { + $data = array(); + $data["str"] = "I like to talk to myself"; + + if (! $this->client->query('tl.repeat', $data)) { + echo "\n\n" . $this->getName() . "something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } + + $this->assertEquals("You said: " . $data["str"], + $this->client->getResponse()); + } + + private function testAbout() + { + if (! $this->client->query('tl.about', null)) { + echo "\n\n" . $this->getName() . "something went really wrong - " . + $this->client->getErrorCode() . $this->client->getErrorMessage(); + } else { + echo "success!"; + } + } + + private function testNonExistantMethod() + { + $this->assertFalse($this->client->query('tl.noSuchMethodExists')); + } +} + +// this is only necessary if you want to run "php FileName.php" +// otherwise you can just run "phpunit FileName.php" and it should work +// PHPUnit_TextUI_TestRunner::run(TestlinkXMLRPCServerTest::suite()); + ?> diff --git a/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTestData.php b/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTestData.php index 509ad8f82a..b7f5d4a383 100644 --- a/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTestData.php +++ b/lib/api/xmlrpc/v1/test/TestlinkXMLRPCServerTestData.php @@ -1,73 +1,77 @@ - - * @package TestlinkAPI - * @link http://testlink.org/testlinkWebServices - */ - - /** - * Alternate class method of generating necessary test data - * @deprecated The SQL file should be used since this isn't maintained - */ -class TestlinkXMLRPCServerTestData -{ - // dependant on data in the sql file - const testDevKey = "validTestDevKey"; - const noRightsDevKey = "devKeyWithNoRights"; - const testTPID = 2; - const testTCID = 4; - const testBuildID = 1; - - - public function __construct() - { - echo $this->_getMessage(); - echo $this->_getTestDeveloperKeys(); - } - - private function _getTestDeveloperKeys() - { - $str = ""; - $str .= "INSERT INTO `api_developer_keys` (`id` ,`developer_key` ,`user_id`) VALUES (NULL , '" . self::testDevKey . "', '1');"; - $str .= "
    "; - return $str; - } - - private function _getNewTCID() - { - $str = ""; - $str .= "@tcid := SELECT MAX()"; - //$str .= "INSERT INTO " - } - - private function _getNewTPID() - { - - } - - private function _getMessage() - { - $str = ""; - $str .= "-- Copy this SQL text and insert it into your testlink database"; - $str .= "
    "; - return $str; - } -} - -// don't output the data when running from cli i.e. gets included in phpUnit test run -if(php_sapi_name() != "cli") -{ - $data = new TestlinkXMLRPCServerTestData(); -} - -?> \ No newline at end of file + + * @package TestlinkAPI + * @link http://testlink.org/testlinkWebServices + */ + +/** + * Alternate class method of generating necessary test data + * + * @deprecated The SQL file should be used since this isn't maintained + */ +class TestlinkXMLRPCServerTestData +{ + + // dependant on data in the sql file + const testDevKey = "validTestDevKey"; + + const noRightsDevKey = "devKeyWithNoRights"; + + const testTPID = 2; + + const testTCID = 4; + + const testBuildID = 1; + + public function __construct() + { + echo $this->_getMessage(); + echo $this->_getTestDeveloperKeys(); + } + + private function _getTestDeveloperKeys() + { + $str = ""; + $str .= "INSERT INTO `api_developer_keys` (`id` ,`developer_key` ,`user_id`) VALUES (NULL , '" . + self::testDevKey . "', '1');"; + $str .= "
    "; + return $str; + } + + private function _getNewTCID() + { + $str = ""; + $str .= "@tcid := SELECT MAX()"; + // $str .= "INSERT INTO " + } + + private function _getNewTPID() + {} + + private function _getMessage() + { + $str = ""; + $str .= "-- Copy this SQL text and insert it into your testlink database"; + $str .= "
    "; + return $str; + } +} + +// don't output the data when running from cli i.e. gets included in phpUnit test run +if (php_sapi_name() != "cli") { + $data = new TestlinkXMLRPCServerTestData(); +} + +?> diff --git a/lib/api/xmlrpc/v1/xmlrpc.class.php b/lib/api/xmlrpc/v1/xmlrpc.class.php index f41a75ea63..c3889ab5a9 100644 --- a/lib/api/xmlrpc/v1/xmlrpc.class.php +++ b/lib/api/xmlrpc/v1/xmlrpc.class.php @@ -23,13 +23,13 @@ /** * IXR is the class used for the XML-RPC server */ -define( "TL_APICALL", 'XML-RPC' ); +define("TL_APICALL", 'XML-RPC'); -require_once("../../../../config.inc.php"); -require_once("common.php"); -require_once("xml-rpc/class-IXR.php"); -require_once("api.const.inc.php"); -require_once("APIErrors.php"); +require_once '../../../../config.inc.php'; +require_once 'common.php'; +require_once 'xml-rpc/class-IXR.php'; +require_once 'api.const.inc.php'; +require_once 'APIErrors.php'; /** * The entry class for serving XML-RPC Requests @@ -45,13 +45,23 @@ * @package TestlinkAPI * @since Class available since Release 1.8.0 */ -class TestlinkXMLRPCServer extends IXR_Server { +class TestlinkXMLRPCServer extends IXR_Server +{ + + public $methods; + public static $version = "1.1"; + const OFF = false; + const ON = true; + const BUILD_GUESS_DEFAULT_MODE = OFF; + const SET_ERROR = true; + const CHECK_PUBLIC_PRIVATE_ATTR = true; + const THROW_ON_ERROR = true; /** @@ -59,16 +69,27 @@ class TestlinkXMLRPCServer extends IXR_Server { * * @access protected */ - protected $dbObj = null; - protected $tables = null; - protected $tcaseMgr = null; - protected $tprojectMgr = null; - protected $tplanMgr = null; - protected $tplanMetricsMgr = null; - protected $reqSpecMgr = null; - protected $reqMgr = null; - protected $platformMgr = null; - protected $itsMgr = null; + protected $dbObj; + + protected $tables; + + protected $tcaseMgr; + + protected $tprojectMgr; + + protected $tplanMgr; + + protected $tplanMetricsMgr; + + protected $reqSpecMgr; + + protected $reqMgr; + + protected $platformMgr; + + protected $itsMgr; + + protected $userMgr; /** * Whether the server will run in a testing mode @@ -78,17 +99,17 @@ class TestlinkXMLRPCServer extends IXR_Server { /** * userID associated with the devKey provided */ - protected $userID = null; + protected $userID; /** * UserObject associated with the userID */ - protected $user = null; + protected $user; /** * array where all the args are stored for requests */ - protected $args = null; + protected $args; /** * array where error codes and messages are stored @@ -98,7 +119,7 @@ class TestlinkXMLRPCServer extends IXR_Server { /** * The api key being used to make a request */ - protected $devKey = null; + protected $devKey; /** * boolean to allow a method to invoke another method and avoid double auth @@ -114,18 +135,19 @@ class TestlinkXMLRPCServer extends IXR_Server { /** * _checkTCIDAndTPIDValid() */ - protected $tcVersionID = null; - protected $versionNumber = null; + protected $tcVersionID; + + protected $versionNumber; /** * Mapping bewteen external & internal test case ID */ - protected $tcaseE2I = null; + protected $tcaseE2I; /** * needed in order to manage logs */ - protected $tlLogger = null; + protected $tlLogger; /** * #@+ @@ -135,87 +157,189 @@ class TestlinkXMLRPCServer extends IXR_Server { * @static */ public static $actionOnDuplicatedNameParamName = "actiononduplicatedname"; + public static $actionParamName = "action"; + public static $activeParamName = "active"; + public static $assignedToParamName = "assignedto"; + public static $automatedParamName = "automated"; + public static $authorLoginParamName = "authorlogin"; + public static $bugIDParamName = "bugid"; + public static $buildIDParamName = "buildid"; + public static $buildNameParamName = "buildname"; + public static $buildNotesParamName = "buildnotes"; + public static $checkDuplicatedNameParamName = "checkduplicatedname"; + public static $contentParamName = "content"; + public static $customFieldNameParamName = "customfieldname"; + public static $customFieldsParamName = "customfields"; + public static $deepParamName = "deep"; + public static $descriptionParamName = "description"; + public static $detailsParamName = "details"; + public static $devKeyParamName = "devKey"; + public static $executionIDParamName = "executionid"; + public static $executionOrderParamName = "executionorder"; + public static $executedParamName = "executed"; + public static $executeStatusParamName = "executestatus"; + public static $executionTypeParamName = "executiontype"; + public static $expectedResultsParamName = "expectedresults"; + public static $fileNameParamName = "filename"; + public static $fileTypeParamName = "filetype"; + public static $foreignKeyIdParamName = "fkid"; + public static $foreignKeyTableNameParamName = "fktable"; + public static $guessParamName = "guess"; + public static $getStepsInfoParamName = "getstepsinfo"; + public static $getKeywordsParamName = "getkeywords"; + public static $importanceParamName = "importance"; + public static $internalIDParamName = "internalid"; + public static $keywordIDParamName = "keywordid"; + public static $keywordNameParamName = "keywords"; + public static $linkIDParamName = "linkid"; + public static $nodeIDParamName = "nodeid"; + public static $nodeTypeParamName = "nodetype"; + public static $noteParamName = "notes"; + public static $openParamName = "open"; + public static $optionsParamName = "options"; + public static $orderParamName = "order"; + public static $overwriteParamName = "overwrite"; + public static $parentIDParamName = "parentid"; + public static $platformNameParamName = "platformname"; + public static $platformIDParamName = "platformid"; + + public static $platformOnDesignParamName = "platformondesign"; + + public static $platformOnExecutionParamName = "platformonexecution"; + public static $preconditionsParamName = "preconditions"; + public static $publicParamName = "public"; + public static $releaseDateParamName = "releasedate"; + + public static $roleIDParamName = "roleid"; + + public static $roleNameParamName = "rolename"; + public static $requirementsParamName = "requirements"; + public static $requirementIDParamName = "requirementid"; + + public static $requirementVersionIDParamName = "requirementversionid"; + public static $requirementDocIDParamName = "requirementdocid"; + public static $reqSpecIDParamName = "reqspecid"; + public static $scopeParamName = "scope"; + public static $summaryParamName = "summary"; + public static $statusParamName = "status"; + public static $stepsParamName = "steps"; + public static $testCaseIDParamName = "testcaseid"; + public static $testCaseExternalIDParamName = "testcaseexternalid"; + public static $testCaseNameParamName = "testcasename"; + public static $testCasePathNameParamName = "testcasepathname"; + public static $testCasePrefixParamName = "testcaseprefix"; - public static $testCaseVersionIDParamName = "testcaseid"; + + public static $testCaseVersionIDParamName = "testcaseversionid"; + public static $testModeParamName = "testmode"; + public static $testPlanIDParamName = "testplanid"; + public static $testPlanNameParamName = "testplanname"; + public static $testProjectIDParamName = "testprojectid"; + public static $testProjectNameParamName = "testprojectname"; + public static $testSuiteIDParamName = "testsuiteid"; + public static $testSuiteNameParamName = "testsuitename"; + public static $timeStampParamName = "timestamp"; + public static $titleParamName = "title"; + public static $urgencyParamName = "urgency"; - public static $userParamName = "user"; + + public static $userEmailParamName = "email"; + + public static $userFirstNameParamName = "firstname"; + public static $userIDParamName = "userid"; + + public static $userLastNameParamName = "lastname"; + + public static $userLoginParamName = "login"; + + public static $userParamName = "user"; + + public static $userPasswordParamName = "password"; + public static $versionNumberParamName = "version"; + public static $estimatedExecDurationParamName = "estimatedexecduration"; + public static $executionDurationParamName = "execduration"; + public static $prefixParamName = "prefix"; + public static $testCaseVersionParamName = "tcversion"; + public static $itsNameParamName = "itsname"; + public static $itsEnabledParamName = "itsenabled"; + public static $copyTestersFromBuildParamName = "copytestersfrombuild"; /** @@ -227,63 +351,70 @@ class TestlinkXMLRPCServer extends IXR_Server { * Will be initialized using user configuration via config_get() */ public $statusCode; + public $codeStatus; /** * Constructor sets up the IXR_Server and db connection */ - public function __construct($callbacks = array()) { - $this->dbObj = new database( DB_TYPE ); - $this->dbObj->db->SetFetchMode( ADODB_FETCH_ASSOC ); + public function __construct($callbacks = array()) + { + $this->dbObj = new database(DB_TYPE); + $this->dbObj->db->SetFetchMode(ADODB_FETCH_ASSOC); $this->_connectToDB(); global $g_tlLogger; $this->tlLogger = &$g_tlLogger; - $this->tlLogger->setDB( $this->dbObj ); + $this->tlLogger->setDB($this->dbObj); // This close the default transaction that is started // when logger.class.php is included. $this->tlLogger->endTransaction(); - $this->tcaseMgr = new testcase( $this->dbObj ); - $this->tprojectMgr = new testproject( $this->dbObj ); - $this->tplanMgr = new testplan( $this->dbObj ); - $this->tplanMetricsMgr = new tlTestPlanMetrics( $this->dbObj ); - $this->reqSpecMgr = new requirement_spec_mgr( $this->dbObj ); - $this->reqMgr = new requirement_mgr( $this->dbObj ); + $this->tcaseMgr = new testcase($this->dbObj); + $this->tprojectMgr = new testproject($this->dbObj); + $this->tplanMgr = new testplan($this->dbObj); + $this->tplanMetricsMgr = new tlTestPlanMetrics($this->dbObj); + $this->reqSpecMgr = new requirement_spec_mgr($this->dbObj); + $this->reqMgr = new requirement_mgr($this->dbObj); + $this->userMgr = new tlUser($this->dbObj); - $this->tprojectMgr->setAuditEventSource( 'API-XMLRPC' ); + $this->tprojectMgr->setAuditEventSource('API-XMLRPC'); $this->tables = $this->tcaseMgr->getDBTables(); - $resultsCfg = config_get( 'results' ); - foreach( $resultsCfg['status_label_for_exec_ui'] as $key => $label ) { + $resultsCfg = config_get('results'); + foreach ($resultsCfg['status_label_for_exec_ui'] as $key => $label) { $this->statusCode[$key] = $resultsCfg['status_code'][$key]; } - if(isset( $this->statusCode['not_run'] )) { - unset( $this->statusCode['not_run'] ); + if (isset($this->statusCode['not_run'])) { + unset($this->statusCode['not_run']); } - $this->codeStatus = array_flip( $this->statusCode ); + $this->codeStatus = array_flip($this->statusCode); $this->initMethodYellowPages(); $this->methods += $callbacks; // after changing IXR_Server() constructor to __contructor() - parent::__construct( $this->methods ); + parent::__construct($this->methods); } /** */ - protected function _setArgs($args, $opt = null) { + protected function _setArgs($args, $opt = null) + { // TODO: should escape args $this->args = $args; - if(isset( $this->args[self::$testProjectNameParamName] ) && ! isset( $this->args[self::$testProjectIDParamName] )) { - $tprojMgr = new testproject( $this->dbObj ); - $name = trim( $this->args[self::$testProjectNameParamName] ); - $info = current( $this->tprojectMgr->get_by_name( $name ) ); - $this->args[self::$testProjectIDParamName] = $info['id']; + if (isset($this->args[self::$testProjectNameParamName]) && + ! isset($this->args[self::$testProjectIDParamName])) { + $name = trim($this->args[self::$testProjectNameParamName]); + $projects = $this->tprojectMgr->get_by_name($name); + if (! is_null($projects)) { + $info = current($projects); + $this->args[self::$testProjectIDParamName] = $info['id']; + } } } @@ -293,12 +424,13 @@ protected function _setArgs($args, $opt = null) { * @param int $buildID * @access protected */ - protected function _setBuildID($buildID) { - if(GENERAL_ERROR_CODE != $buildID) { + protected function _setBuildID($buildID) + { + if (GENERAL_ERROR_CODE != $buildID) { $this->args[self::$buildIDParamName] = $buildID; return true; } else { - $this->errors[] = new IXR_Error( INVALID_BUILDID, INVALID_BUILDID_STR ); + $this->errors[] = new IXR_Error(INVALID_BUILDID, INVALID_BUILDID_STR); return false; } } @@ -309,7 +441,8 @@ protected function _setBuildID($buildID) { * @param int $tcaseID * @access protected */ - protected function _setTestCaseID($tcaseID) { + protected function _setTestCaseID($tcaseID) + { $this->args[self::$testCaseIDParamName] = $tcaseID; } @@ -319,12 +452,13 @@ protected function _setTestCaseID($tcaseID) { * @return boolean * @access protected */ - protected function _setBuildID2Latest() { + protected function _setBuildID2Latest() + { $tplan_id = $this->args[self::$testPlanIDParamName]; - $maxbuildid = $this->tplanMgr->get_max_build_id( $tplan_id ); - $status_ok =($maxbuildid > 0); - if($status_ok) { - $this->_setBuildID( $maxbuildid ); + $maxbuildid = $this->tplanMgr->get_max_build_id($tplan_id); + $status_ok = ($maxbuildid > 0); + if ($status_ok) { + $this->_setBuildID($maxbuildid); } return $status_ok; } @@ -338,17 +472,20 @@ protected function _setBuildID2Latest() { * 20100731 - asimon - BUGID 3644(additional fix for BUGID 2607) * 20100711 - franciscom - BUGID 2607 - UTF8 settings for MySQL */ - protected function _connectToDB() { - if(true == $this->testMode) { - $this->dbObj->connect( TEST_DSN, TEST_DB_HOST, TEST_DB_USER, TEST_DB_PASS, TEST_DB_NAME ); + protected function _connectToDB() + { + if ($this->testMode) { + $this->dbObj->connect(TEST_DSN, TEST_DB_HOST, TEST_DB_USER, + TEST_DB_PASS, TEST_DB_NAME); } else { - $this->dbObj->connect( DSN, DB_HOST, DB_USER, DB_PASS, DB_NAME ); + $this->dbObj->connect(DSN, DB_HOST, DB_USER, DB_PASS, DB_NAME); } // asimon - BUGID 3644 & 2607 - $charSet was undefined here - $charSet = config_get( 'charset' ); - if((DB_TYPE == 'mysql') &&($charSet == 'UTF-8')) { - $this->dbObj->exec_query( "SET CHARACTER SET utf8" ); - $this->dbObj->exec_query( "SET collation_connection = 'utf8_general_ci'" ); + $charSet = config_get('charset'); + if ((DB_TYPE == 'mysql') && ($charSet == 'UTF-8')) { + $this->dbObj->exec_query("SET CHARACTER SET utf8"); + $this->dbObj->exec_query( + "SET collation_connection = 'utf8_general_ci'"); } } @@ -363,10 +500,12 @@ protected function _connectToDB() { * @return boolean * @access protected */ - protected function authenticate($messagePrefix = '') { + protected function authenticate($messagePrefix = '') + { // check that the key was given as part of the args - if(! $this->_isDevKeyPresent()) { - $this->errors[] = new IXR_ERROR( NO_DEV_KEY, $messagePrefix . NO_DEV_KEY_STR ); + if (! $this->_isDevKeyPresent()) { + $this->errors[] = new IXR_ERROR(NO_DEV_KEY, + $messagePrefix . NO_DEV_KEY_STR); $this->authenticated = false; return false; } else { @@ -374,75 +513,142 @@ protected function authenticate($messagePrefix = '') { } // make sure the key we have is valid - if(! $this->_isDevKeyValid( $this->devKey )) { - $this->errors[] = new IXR_Error( INVALID_AUTH, $messagePrefix . INVALID_AUTH_STR ); + if (! $this->_isDevKeyValid($this->devKey)) { + $this->errors[] = new IXR_Error(INVALID_AUTH, + $messagePrefix . INVALID_AUTH_STR); $this->authenticated = false; return false; } else { // Load User - $this->user = tlUser::getByID( $this->dbObj, $this->userID ); + $this->user = tlUser::getByID($this->dbObj, $this->userID); $this->authenticated = true; - $this->tlLogger->startTransaction( 'DEFAULT', null, $this->userID ); + $this->tlLogger->startTransaction('DEFAULT', null, $this->userID); return true; } } + /** + * Check if authenticated user has System Wide Role admin + * + * Useful for services reserved to System Wide Role admin + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkIsSystemWideAdmin($messagePrefix = '') + { + $res = ($this->user->globalRole->dbID == TL_ROLES_ADMIN); + + if (! $res) { + $this->errors[] = new IXR_Error(MUST_BE_ADMIN, + $messagePrefix . MUST_BE_ADMIN_STR); + } + + return $res; + } + /** * checks if a user has requested right on test project, test plan pair. * * @param string $rightToCheck * one of the rights defined in rights table * @param boolean $checkPublicPrivateAttr - * (optional) - * @param map $context - * (optional) + * (optional) + * @param array $context + * (optional) * keys testprojectid,testplanid(both are also optional) * * @return boolean * @access protected * */ - protected function userHasRight($rightToCheck, $checkPublicPrivateAttr = false, $context = null) { + protected function userHasRight($rightToCheck, + $checkPublicPrivateAttr = false, $context = null) + { $status_ok = true; - $tprojectid = intval( isset( $context[self::$testProjectIDParamName] ) ? $context[self::$testProjectIDParamName] : 0 ); - if($tprojectid == 0 && isset( $this->args[self::$testProjectIDParamName] )) { + // Site admin has all rights + if ($this->user->globalRole->dbID == TL_ROLES_ADMIN) { + return true; + } + + $tprojectid = intval( + isset($context[self::$testProjectIDParamName]) ? $context[self::$testProjectIDParamName] : 0); + + if ($tprojectid == 0 && + isset($this->args[self::$testProjectIDParamName])) { $tprojectid = $this->args[self::$testProjectIDParamName]; } - if(isset( $context[self::$testPlanIDParamName] )) { + if (isset($context[self::$testPlanIDParamName])) { $tplanid = $context[self::$testPlanIDParamName]; } else { - $tplanid = isset( $this->args[self::$testPlanIDParamName] ) ? $this->args[self::$testPlanIDParamName] : null; + $tplanid = isset($this->args[self::$testPlanIDParamName]) ? $this->args[self::$testPlanIDParamName] : null; } - $tprojectid = intval( $tprojectid ); - $tplanid = ! is_null( $tplanid ) ? intval( $tplanid ) : - 1; + $tprojectid = intval($tprojectid); + $tplanid = ! is_null($tplanid) ? intval($tplanid) : - 1; - if($tprojectid <= 0 && $tplanid > 0) { + if ($tprojectid <= 0 && $tplanid > 0) { // get test project from test plan $ox = array( - 'output' => 'minimun' + 'output' => 'minimun' ); - $dummy = $this->tplanMgr->get_by_id( $tplanid, $ox ); - $tprojectid = intval( $dummy['tproject_id'] ); + $dummy = $this->tplanMgr->get_by_id($tplanid, $ox); + $tprojectid = intval($dummy['tproject_id']); + } + + // Contribution by frankfal + // Some APIs only provide TestSuiteID or TestCaseID, look up TestProjectID + if ($tprojectid <= 0 && $tplanid == - 1) { + // Try using TestSuiteID to get TestProjectID + $tsuiteid = intval( + isset($context[self::$testSuiteIDParamName]) ? $context[self::$testSuiteIDParamName] : 0); + if ($tsuiteid == 0 && + isset($this->args[self::$testSuiteIDParamName])) { + $tsuiteid = intval($this->args[self::$testSuiteIDParamName]); + } + if ($tsuiteid > 0) { + $dummy = $this->tprojectMgr->tree_manager->get_path($tsuiteid); + $tprojectid = $dummy[0]['parent_id']; + } else { + // Try using TestCaseID to get TestProjectID + $tcaseid = intval( + isset($context[self::$testCaseIDParamName]) ? $context[self::$testCaseIDParamName] : 0); + if ($tcaseid == 0 && + isset($this->args[self::$testCaseIDParamName])) { + $tcaseid = intval($this->args[self::$testCaseIDParamName]); + } + if ($tcaseid > 0) { + $tprojectid = $this->tcaseMgr->get_testproject($tcaseid); + } + } } - if(! $this->user->hasRight( $this->dbObj, $rightToCheck, $tprojectid, $tplanid, $checkPublicPrivateAttr )) { + if (! $this->user->hasRight($this->dbObj, $rightToCheck, $tprojectid, + $tplanid, $checkPublicPrivateAttr)) { $status_ok = false; - $msg = sprintf( INSUFFICIENT_RIGHTS_STR, $this->user->login, $rightToCheck, $tprojectid, $tplanid ); - $this->errors[] = new IXR_Error( INSUFFICIENT_RIGHTS, $msg ); + $msg = sprintf(INSUFFICIENT_RIGHTS_STR, $this->user->login, + $rightToCheck, $tprojectid, $tplanid); + $this->errors[] = new IXR_Error(INSUFFICIENT_RIGHTS, $msg); } - if(isset( $context['updaterID'] )) { - $updUser = tlUser::getByID( $this->dbObj, intval( $context['updaterID'] ) ); + if (isset($context['updaterID'])) { + $updUser = tlUser::getByID($this->dbObj, + intval($context['updaterID'])); - $sk = $updUser->hasRight( $this->dbObj, $rightToCheck, $tprojectid, $tplanid, $checkPublicPrivateAttr ); - if(! $sk) { + $sk = $updUser->hasRight($this->dbObj, $rightToCheck, $tprojectid, + $tplanid, $checkPublicPrivateAttr); + if (! $sk) { $status_ok = false; - $msg = sprintf( UPDATER_INSUFFICIENT_RIGHTS_STR, $updUser->login, $rightToCheck, $tprojectid, $tplanid ); - $this->errors[] = new IXR_Error( UPDATER_INSUFFICIENT_RIGHTS, $msg ); + $msg = sprintf(UPDATER_INSUFFICIENT_RIGHTS_STR, $updUser->login, + $rightToCheck, $tprojectid, $tplanid); + $this->errors[] = new IXR_Error(UPDATER_INSUFFICIENT_RIGHTS, + $msg); } } @@ -457,15 +663,17 @@ protected function userHasRight($rightToCheck, $checkPublicPrivateAttr = false, * @return boolean * @access protected */ - protected function checkTestCaseName() { + protected function checkTestCaseName() + { $status = true; - if(! $this->_isTestCaseNamePresent()) { - $this->errors[] = new IXR_Error( NO_TESTCASENAME, NO_TESTCASENAME_STR ); + if (! $this->_isTestCaseNamePresent()) { + $this->errors[] = new IXR_Error(NO_TESTCASENAME, NO_TESTCASENAME_STR); $status = false; } else { $testCaseName = $this->args[self::$testCaseNameParamName]; - if(! is_string( $testCaseName )) { - $this->errors[] = new IXR_Error( TESTCASENAME_NOT_STRING, TESTCASENAME_NOT_STRING_STR ); + if (! is_string($testCaseName)) { + $this->errors[] = new IXR_Error(TESTCASENAME_NOT_STRING, + TESTCASENAME_NOT_STRING_STR); $status = false; } } @@ -480,14 +688,17 @@ protected function checkTestCaseName() { * @return boolean * @access protected */ - protected function checkStatus() { - if(($status = $this->_isStatusPresent())) { - if(!($status = $this->_isStatusValid( $this->args[self::$statusParamName] ))) { - $msg = sprintf( INVALID_STATUS_STR, $this->args[self::$statusParamName] ); - $this->errors[] = new IXR_Error( INVALID_STATUS, $msg ); + protected function checkStatus() + { + if ($status = $this->_isStatusPresent()) { + if (! ($status = $this->_isStatusValid( + $this->args[self::$statusParamName]))) { + $msg = sprintf(INVALID_STATUS_STR, + $this->args[self::$statusParamName]); + $this->errors[] = new IXR_Error(INVALID_STATUS, $msg); } } else { - $this->errors[] = new IXR_Error( NO_STATUS, NO_STATUS_STR ); + $this->errors[] = new IXR_Error(NO_STATUS, NO_STATUS_STR); } return $status; } @@ -503,17 +714,49 @@ protected function checkStatus() { * @return boolean * @access protected */ - protected function checkTestCaseID($messagePrefix = '') { + protected function checkTestCaseID($messagePrefix = '') + { $msg = $messagePrefix; $status_ok = $this->_isTestCaseIDPresent(); - if($status_ok) { + if ($status_ok) { $tcaseid = $this->args[self::$testCaseIDParamName]; - if(! $this->_isTestCaseIDValid( $tcaseid )) { - $this->errors[] = new IXR_Error( INVALID_TCASEID, $msg . INVALID_TCASEID_STR ); + if (! $this->_isTestCaseIDValid($tcaseid)) { + $this->errors[] = new IXR_Error(INVALID_TCASEID, + $msg . INVALID_TCASEID_STR); + $status_ok = false; + } + } else { + $this->errors[] = new IXR_Error(NO_TCASEID, $msg . NO_TCASEID_STR); + } + return $status_ok; + } + + /** + * Helper method to see if the testcaseversionid provided is valid + * + * This is the only method that should be called directly to check the tcversionid + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkTestCaseVersionID($messagePrefix = '') + { + $msg = $messagePrefix; + $status_ok = $this->_isTestCaseVersionIDPresent(); + if ($status_ok) { + $tcaseversionid = $this->args[self::$testCaseVersionIDParamName]; + if (! $this->_isTestCaseVersionIDValid($tcaseversionid, + "checkTestCaseVersionID", true)) { + $this->errors[] = new IXR_Error(INVALID_TCASEVERSIONID, + sprintf($msg . INVALID_TCASEVERSIONID_STR, $tcaseversionid)); $status_ok = false; } } else { - $this->errors[] = new IXR_Error( NO_TCASEID, $msg . NO_TCASEID_STR ); + $this->errors[] = new IXR_Error(NO_TCASEVERSIONID, + $msg . NO_TCASEVERSIONID_STR); } return $status_ok; } @@ -529,25 +772,30 @@ protected function checkTestCaseID($messagePrefix = '') { * @return boolean * @access protected */ - protected function checkTestPlanID($messagePrefix = '') { + protected function checkTestPlanID($messagePrefix = '') + { $status = true; - if(! $this->_isTestPlanIDPresent()) { + if (! $this->_isTestPlanIDPresent()) { $msg = $messagePrefix . NO_TPLANID_STR; - $this->errors[] = new IXR_Error( NO_TPLANID, $msg ); + $this->errors[] = new IXR_Error(NO_TPLANID, $msg); $status = false; } else { // See if this TPID exists in the db - $tplanid = $this->dbObj->prepare_int( $this->args[self::$testPlanIDParamName] ); + $tplanid = $this->dbObj->prepare_int( + $this->args[self::$testPlanIDParamName]); $query = "SELECT id FROM {$this->tables['testplans']} WHERE id={$tplanid}"; - $result = $this->dbObj->fetchFirstRowSingleColumn( $query, "id" ); - if(null == $result) { - $msg = $messagePrefix . sprintf( INVALID_TPLANID_STR, $tplanid ); - $this->errors[] = new IXR_Error( INVALID_TPLANID, $msg ); + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + if (null == $result) { + $msg = $messagePrefix . sprintf(INVALID_TPLANID_STR, $tplanid); + $this->errors[] = new IXR_Error(INVALID_TPLANID, $msg); $status = false; } else { // tplanid exists and its valid // Do we need to try to guess build id ? - if($this->checkGuess() &&(! $this->_isBuildIDPresent() && ! $this->_isParamPresent( self::$buildNameParamName, $messagePrefix ))) { + if ($this->checkGuess() && + (! $this->_isBuildIDPresent() && + ! $this->_isParamPresent(self::$buildNameParamName, + $messagePrefix))) { $status = $this->_setBuildID2Latest(); } } @@ -566,17 +814,21 @@ protected function checkTestPlanID($messagePrefix = '') { * @return boolean * @access protected */ - protected function checkTestProjectID($messagePrefix = '') { - if(!($status = $this->_isTestProjectIDPresent())) { - $this->errors[] = new IXR_Error( NO_TESTPROJECTID, $messagePrefix . NO_TESTPROJECTID_STR ); + protected function checkTestProjectID($messagePrefix = '') + { + if (! ($status = $this->_isTestProjectIDPresent())) { + $this->errors[] = new IXR_Error(NO_TESTPROJECTID, + $messagePrefix . NO_TESTPROJECTID_STR); } else { // See if this Test Project ID exists in the db - $testprojectid = $this->dbObj->prepare_int( $this->args[self::$testProjectIDParamName] ); + $testprojectid = $this->dbObj->prepare_int( + $this->args[self::$testProjectIDParamName]); $query = "SELECT id FROM {$this->tables['testprojects']} WHERE id={$testprojectid}"; - $result = $this->dbObj->fetchFirstRowSingleColumn( $query, "id" ); - if(null == $result) { - $msg = $messagePrefix . sprintf( INVALID_TESTPROJECTID_STR, $testprojectid ); - $this->errors[] = new IXR_Error( INVALID_TESTPROJECTID, $msg ); + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + if (null == $result) { + $msg = $messagePrefix . + sprintf(INVALID_TESTPROJECTID_STR, $testprojectid); + $this->errors[] = new IXR_Error(INVALID_TESTPROJECTID, $msg); $status = false; } } @@ -599,29 +851,182 @@ protected function checkTestProjectID($messagePrefix = '') { * @return boolean * @access protected */ - protected function checkTestProjectIdentity($messagePrefix = '') { + protected function checkTestProjectIdentity($messagePrefix = '') + { $status = false; - $fromExternal = false; - $fromInternal = false; - if($this->_isTestProjectIDPresent()) { - $fromInternal = true; - $status = $this->checkTestProjectID( $messagePrefix ); - } else if($this->_isParamPresent( self::$prefixParamName, $messagePrefix, true )) { - // Go for the prefix - $fromExternal = true; + if ($this->_isTestProjectIDPresent()) { + $status = $this->checkTestProjectID($messagePrefix); + } elseif ($this->_isParamPresent(self::$prefixParamName, $messagePrefix, + true)) { - $target = $this->dbObj->prepare_string( $this->args[self::$prefixParamName] ); + $target = $this->dbObj->prepare_string( + $this->args[self::$prefixParamName]); $sql = " SELECT id FROM {$this->tables['testprojects']} WHERE prefix='{$target}' "; - $fieldValue = $this->dbObj->fetchFirstRowSingleColumn( $sql, "id" ); - $status =(! is_null( $fieldValue ) &&(intval( $fieldValue ) > 0)); - if($status) { + $fieldValue = $this->dbObj->fetchFirstRowSingleColumn($sql, "id"); + $status = (! is_null($fieldValue) && (intval($fieldValue) > 0)); + if ($status) { $this->args[self::$testProjectIDParamName] = $fieldValue; } else { $status = false; - $msg = $messagePrefix . sprintf( TPROJECT_PREFIX_DOESNOT_EXIST_STR, $target ); - $this->errors[] = new IXR_Error( TPROJECT_PREFIX_DOESNOT_EXIST, $msg ); + $msg = $messagePrefix . + sprintf(TPROJECT_PREFIX_DOESNOT_EXIST_STR, $target); + $this->errors[] = new IXR_Error(TPROJECT_PREFIX_DOESNOT_EXIST, + $msg); + } + } else { + $status = false; + } + + return $status; + } + + /** + * Helper method to see if the UserID provided is valid + * + * This is the only method that should be called directly to check the UserID + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkUserID($messagePrefix = '') + { + if (! ($status = $this->_isUserIDPresent())) { + $this->errors[] = new IXR_Error(NO_USERID, + $messagePrefix . NO_USERID_STR); + } else { + // See if this user ID exists in the DB + $userid = $this->dbObj->prepare_int( + $this->args[self::$userIDParamName]); + $query = "SELECT id FROM {$this->tables['users']} WHERE id={$userid}"; + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + if (null == $result) { + $msg = $messagePrefix . sprintf(INVALID_USERID_STR, $userid); + $this->errors[] = new IXR_Error(INVALID_USERID, $msg); + $status = false; + } + } + return $status; + } + + /** + * Helper method to see if the user identity provided is valid + * Identity can be specified in one of these modes: + * + * - internal id(DB) + * - login + * + * + * If everything OK, user internal ID is setted. + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkUserIdentity($messagePrefix = '') + { + $status = false; + + if ($this->_isUserIDPresent()) { + $status = $this->checkUserID($messagePrefix); + } elseif ($this->_isParamPresent(self::$userLoginParamName, + $messagePrefix, true)) { + + $target = $this->dbObj->prepare_string( + $this->args[self::$userLoginParamName]); + $sql = " SELECT id FROM {$this->tables['users']} WHERE login='{$target}' "; + + $fieldValue = $this->dbObj->fetchFirstRowSingleColumn($sql, "id"); + $status = (! is_null($fieldValue) && (intval($fieldValue) > 0)); + if ($status) { + $this->args[self::$UserIDParamName] = $fieldValue; + } else { + $status = false; + $msg = $messagePrefix . + sprintf(USER_LOGIN_DOESNOT_EXIST_STR, $target); + $this->errors[] = new IXR_Error(USER_LOGIN_DOESNOT_EXIST, $msg); + } + } else { + $status = false; + } + + return $status; + } + + /** + * Helper method to see if the roleID provided is valid + * + * This is the only method that should be called directly to check the roleID + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkRoleID($messagePrefix = '') + { + if (! ($status = $this->_isRoleIDPresent())) { + $this->errors[] = new IXR_Error(NO_ROLEID, + $messagePrefix . NO_ROLEID_STR); + } else { + // See if this role ID exists in the DB + $roleid = $this->dbObj->prepare_int( + $this->args[self::$roleIDParamName]); + $query = "SELECT id FROM {$this->tables['roles']} WHERE id={$roleid}"; + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + if (null == $result) { + $msg = $messagePrefix . sprintf(INVALID_ROLEID_STR, $roleid); + $this->errors[] = new IXR_Error(INVALID_ROLEID, $msg); + $status = false; + } + } + return $status; + } + + /** + * Helper method to see if the role identity provided is valid + * Identity can be specified in one of these modes: + * + * - internal id(DB) + * - description + * + * + * If everything OK, role internal ID is setted. + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkRoleIdentity($messagePrefix = '') + { + $status = false; + + if ($this->_isRoleIDPresent()) { + $status = $this->checkRoleID($messagePrefix); + } elseif ($this->_isParamPresent(self::$roleNameParamName, + $messagePrefix, true)) { + + $target = $this->dbObj->prepare_string( + $this->args[self::$roleNameParamName]); + $sql = " SELECT id FROM {$this->tables['roles']} WHERE description='{$target}' "; + + $fieldValue = $this->dbObj->fetchFirstRowSingleColumn($sql, "id"); + $status = (! is_null($fieldValue) && (intval($fieldValue) > 0)); + if ($status) { + $this->args[self::$roleIDParamName] = $fieldValue; + } else { + $msg = $messagePrefix . + sprintf(ROLE_NAME_DOESNOT_EXIST_STR, $target); + $this->errors[] = new IXR_Error(ROLE_NAME_DOESNOT_EXIST, $msg); } } else { $status = false; @@ -641,16 +1046,21 @@ protected function checkTestProjectIdentity($messagePrefix = '') { * @return boolean * @access protected */ - protected function checkTestSuiteID($messagePrefix = '') { - if(!($status = $this->_isTestSuiteIDPresent())) { - $this->errors[] = new IXR_Error( NO_TESTSUITEID, $messagePrefix . NO_TESTSUITEID_STR ); + protected function checkTestSuiteID($messagePrefix = '') + { + if (! ($status = $this->_isTestSuiteIDPresent())) { + $this->errors[] = new IXR_Error(NO_TESTSUITEID, + $messagePrefix . NO_TESTSUITEID_STR); } else { // See if this Test Suite ID exists in the db - $tsuiteMgr = new testsuite( $this->dbObj ); - $node_info = $tsuiteMgr->get_by_id( $this->args[self::$testSuiteIDParamName] ); - if(!($status = ! is_null( $node_info ))) { - $msg = $messagePrefix . sprintf( INVALID_TESTSUITEID_STR, $this->args[self::$testSuiteIDParamName] ); - $this->errors[] = new IXR_Error( INVALID_TESTSUITEID, $msg ); + $tsuiteMgr = new testsuite($this->dbObj); + $node_info = $tsuiteMgr->get_by_id( + $this->args[self::$testSuiteIDParamName]); + if (! ($status = ! is_null($node_info))) { + $msg = $messagePrefix . + sprintf(INVALID_TESTSUITEID_STR, + $this->args[self::$testSuiteIDParamName]); + $this->errors[] = new IXR_Error(INVALID_TESTSUITEID, $msg); } } return $status; @@ -666,9 +1076,10 @@ protected function checkTestSuiteID($messagePrefix = '') { * @return boolean * @access protected */ - protected function checkGuess() { + protected function checkGuess() + { // if guess is set return its value otherwise return true to guess by default - return($this->_isGuessPresent() ? $this->args[self::$guessParamName] : self::BUILD_GUESS_DEFAULT_MODE); + return $this->_isGuessPresent() ? $this->args[self::$guessParamName] : self::BUILD_GUESS_DEFAULT_MODE; } /** @@ -683,30 +1094,36 @@ protected function checkGuess() { * * @internal revision */ - protected function checkBuildID($msg_prefix) { + protected function checkBuildID($msg_prefix) + { $tplan_id = $this->args[self::$testPlanIDParamName]; $status = true; $try_again = false; // First thing is to know is test plan has any build - $buildQty = $this->tplanMgr->getNumberOfBuilds( $tplan_id ); - if($buildQty == 0) { + $buildQty = $this->tplanMgr->getNumberOfBuilds($tplan_id); + if ($buildQty == 0) { $status = false; - $tplan_info = $this->tplanMgr->get_by_id( $tplan_id ); - $msg = $msg_prefix . sprintf( TPLAN_HAS_NO_BUILDS_STR, $tplan_info['name'], $tplan_info['id'] ); - $this->errors[] = new IXR_Error( TPLAN_HAS_NO_BUILDS, $msg ); + $tplan_info = $this->tplanMgr->get_by_id($tplan_id); + $msg = $msg_prefix . + sprintf(TPLAN_HAS_NO_BUILDS_STR, $tplan_info['name'], + $tplan_info['id']); + $this->errors[] = new IXR_Error(TPLAN_HAS_NO_BUILDS, $msg); } - if($status) { - if(! $this->_isBuildIDPresent()) { + if ($status) { + if (! $this->_isBuildIDPresent()) { $try_again = true; - if($this->_isBuildNamePresent()) { + if ($this->_isBuildNamePresent()) { $try_again = false; - $bname = trim( $this->args[self::$buildNameParamName] ); - $buildInfo = $this->tplanMgr->get_build_by_name( $tplan_id, $bname ); - if(is_null( $buildInfo )) { - $msg = $msg_prefix . sprintf( BUILDNAME_DOES_NOT_EXIST_STR, $bname ); - $this->errors[] = new IXR_Error( BUILDNAME_DOES_NOT_EXIST, $msg ); + $bname = trim($this->args[self::$buildNameParamName]); + $buildInfo = $this->tplanMgr->get_build_by_name($tplan_id, + $bname); + if (is_null($buildInfo)) { + $msg = $msg_prefix . + sprintf(BUILDNAME_DOES_NOT_EXIST_STR, $bname); + $this->errors[] = new IXR_Error( + BUILDNAME_DOES_NOT_EXIST, $msg); $status = false; } else { $this->args[self::$buildIDParamName] = $buildInfo['id']; @@ -714,27 +1131,31 @@ protected function checkBuildID($msg_prefix) { } } - if($try_again) { + if ($try_again) { // this means we aren't supposed to guess the buildid - if(false == $this->checkGuess()) { - $this->errors[] = new IXR_Error( NO_BUILDID, NO_BUILDID_STR ); + if (! $this->checkGuess()) { + $this->errors[] = new IXR_Error(NO_BUILDID, NO_BUILDID_STR); $status = false; } else { $setBuildResult = $this->_setBuildID2Latest(); - if(false == $setBuildResult) { - $this->errors[] = new IXR_Error( NO_BUILD_FOR_TPLANID, NO_BUILD_FOR_TPLANID_STR ); + if (! $setBuildResult) { + $this->errors[] = new IXR_Error(NO_BUILD_FOR_TPLANID, + NO_BUILD_FOR_TPLANID_STR); $status = false; } } } - if($status) { - $buildID = $this->dbObj->prepare_int( $this->args[self::$buildIDParamName] ); - $buildInfo = $this->tplanMgr->get_build_by_id( $tplan_id, $buildID ); - if(is_null( $buildInfo )) { - $tplan_info = $this->tplanMgr->get_by_id( $tplan_id ); - $msg = sprintf( BAD_BUILD_FOR_TPLAN_STR, $buildID, $tplan_info['name'], $tplan_id ); - $this->errors[] = new IXR_Error( BAD_BUILD_FOR_TPLAN, $msg ); + if ($status) { + $buildID = $this->dbObj->prepare_int( + $this->args[self::$buildIDParamName]); + $buildInfo = $this->tplanMgr->get_build_by_id($tplan_id, + $buildID); + if (is_null($buildInfo)) { + $tplan_info = $this->tplanMgr->get_by_id($tplan_id); + $msg = sprintf(BAD_BUILD_FOR_TPLAN_STR, $buildID, + $tplan_info['name'], $tplan_id); + $this->errors[] = new IXR_Error(BAD_BUILD_FOR_TPLAN, $msg); $status = false; } } @@ -758,11 +1179,14 @@ protected function checkBuildID($msg_prefix) { * * */ - protected function _isParamPresent($pname, $messagePrefix = '', $setError = false) { - $status_ok =(isset( $this->args[$pname] ) ? true : false); - if(! $status_ok && $setError) { - $msg = $messagePrefix . sprintf( MISSING_REQUIRED_PARAMETER_STR, $pname ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); + protected function _isParamPresent($pname, $messagePrefix = '', + $setError = false) + { + $status_ok = (isset($this->args[$pname]) ? true : false); + if (! $status_ok && $setError) { + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, $pname); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } return $status_ok; } @@ -773,8 +1197,9 @@ protected function _isParamPresent($pname, $messagePrefix = '', $setError = fals * @return boolean * @access protected */ - protected function _isStatusValid($status) { - return(in_array( $status, $this->statusCode )); + protected function _isStatusValid($status) + { + return in_array($status, $this->statusCode); } /** @@ -783,8 +1208,9 @@ protected function _isStatusValid($status) { * @return boolean * @access protected */ - protected function _isTestCaseNamePresent() { - return(isset( $this->args[self::$testCaseNameParamName] ) ? true : false); + protected function _isTestCaseNamePresent() + { + return isset($this->args[self::$testCaseNameParamName]) ? true : false; } /** @@ -793,9 +1219,9 @@ protected function _isTestCaseNamePresent() { * @return boolean * @access protected */ - protected function _isTestCaseExternalIDPresent() { - $status = isset( $this->args[self::$testCaseExternalIDParamName] ) ? true : false; - return $status; + protected function _isTestCaseExternalIDPresent() + { + return isset($this->args[self::$testCaseExternalIDParamName]) ? true : false; } /** @@ -805,8 +1231,9 @@ protected function _isTestCaseExternalIDPresent() { * @return boolean * @access protected */ - protected function _isTimeStampPresent() { - return(isset( $this->args[self::$timeStampParamName] ) ? true : false); + protected function _isTimeStampPresent() + { + return isset($this->args[self::$timeStampParamName]) ? true : false; } /** @@ -815,8 +1242,9 @@ protected function _isTimeStampPresent() { * @return boolean * @access protected */ - protected function _isBuildIDPresent() { - return(isset( $this->args[self::$buildIDParamName] ) ? true : false); + protected function _isBuildIDPresent() + { + return isset($this->args[self::$buildIDParamName]) ? true : false; } /** @@ -825,9 +1253,9 @@ protected function _isBuildIDPresent() { * @return boolean * @access protected */ - protected function _isBuildNamePresent() { - $status = isset( $this->args[self::$buildNameParamName] ) ? true : false; - return $status; + protected function _isBuildNamePresent() + { + return isset($this->args[self::$buildNameParamName]) ? true : false; } /** @@ -836,8 +1264,9 @@ protected function _isBuildNamePresent() { * @return boolean * @access protected */ - protected function _isBuildNotePresent() { - return(isset( $this->args[self::$buildNotesParamName] ) ? true : false); + protected function _isBuildNotePresent() + { + return isset($this->args[self::$buildNotesParamName]) ? true : false; } /** @@ -846,8 +1275,9 @@ protected function _isBuildNotePresent() { * @return boolean * @access protected */ - protected function _isTestSuiteIDPresent() { - return(isset( $this->args[self::$testSuiteIDParamName] ) ? true : false); + protected function _isTestSuiteIDPresent() + { + return isset($this->args[self::$testSuiteIDParamName]) ? true : false; } /** @@ -856,8 +1286,9 @@ protected function _isTestSuiteIDPresent() { * @return boolean * @access protected */ - protected function _isNotePresent() { - return(isset( $this->args[self::$noteParamName] ) ? true : false); + protected function _isNotePresent() + { + return isset($this->args[self::$noteParamName]) ? true : false; } /** @@ -866,8 +1297,9 @@ protected function _isNotePresent() { * @return boolean * @access protected */ - protected function _isTestPlanIDPresent() { - return(isset( $this->args[self::$testPlanIDParamName] ) ? true : false); + protected function _isTestPlanIDPresent() + { + return isset($this->args[self::$testPlanIDParamName]) ? true : false; } /** @@ -876,8 +1308,64 @@ protected function _isTestPlanIDPresent() { * @return boolean * @access protected */ - protected function _isTestProjectIDPresent() { - return(isset( $this->args[self::$testProjectIDParamName] ) ? true : false); + protected function _isTestProjectIDPresent() + { + return isset($this->args[self::$testProjectIDParamName]) ? true : false; + } + + /** + * Helper method to see if a UserID is given as one of the arguments + * + * @return boolean + * @access protected + */ + protected function _isUserIDPresent() + { + return isset($this->args[self::$userIDParamName]); + } + + /** + * Helper method to see if a RoleID is given as one of the arguments + * + * @return boolean + * @access protected + */ + protected function _isRoleIDPresent() + { + return isset($this->args[self::$roleIDParamName]); + } + + /** + * Helper method to see if a Requirement ID is given as one of the arguments + * + * @return boolean + * @access protected + */ + protected function _isRequirementIDPresent() + { + return isset($this->args[self::$requirementIDParamName]); + } + + /** + * Helper method to see if a Requirement Document ID is given as one of the arguments + * + * @return boolean + * @access protected + */ + protected function _isRequirementDocIDPresent() + { + return isset($this->args[self::$requirementDocIDParamName]); + } + + /** + * Helper method to see if a Requirement Version ID is given as one of the arguments + * + * @return boolean + * @access protected + */ + protected function _isRequirementVersionIDPresent() + { + return isset($this->args[self::$requirementVersionIDParamName]); } /** @@ -886,8 +1374,9 @@ protected function _isTestProjectIDPresent() { * @return boolean * @access protected */ - protected function _isAutomatedPresent() { - return(isset( $this->args[self::$automatedParamName] ) ? true : false); + protected function _isAutomatedPresent() + { + return isset($this->args[self::$automatedParamName]) ? true : false; } /** @@ -896,8 +1385,9 @@ protected function _isAutomatedPresent() { * @return boolean * @access protected */ - protected function _isTestModePresent() { - return(isset( $this->args[self::$testModeParamName] ) ? true : false); + protected function _isTestModePresent() + { + return isset($this->args[self::$testModeParamName]) ? true : false; } /** @@ -906,8 +1396,9 @@ protected function _isTestModePresent() { * @return boolean * @access protected */ - protected function _isDevKeyPresent() { - return(isset( $this->args[self::$devKeyParamName] ) ? true : false); + protected function _isDevKeyPresent() + { + return isset($this->args[self::$devKeyParamName]) ? true : false; } /** @@ -916,8 +1407,20 @@ protected function _isDevKeyPresent() { * @return boolean * @access protected */ - protected function _isTestCaseIDPresent() { - return(isset( $this->args[self::$testCaseIDParamName] ) ? true : false); + protected function _isTestCaseIDPresent() + { + return isset($this->args[self::$testCaseIDParamName]) ? true : false; + } + + /** + * Helper method to see if a testcaseversionid is given as one of the arguments + * + * @return boolean + * @access protected + */ + protected function _isTestCaseVersionIDPresent() + { + return isset($this->args[self::$testCaseVersionIDParamName]) ? true : false; } /** @@ -926,9 +1429,9 @@ protected function _isTestCaseIDPresent() { * @return boolean * @access protected */ - protected function _isGuessPresent() { - $status = isset( $this->args[self::$guessParamName] ) ? true : false; - return $status; + protected function _isGuessPresent() + { + return isset($this->args[self::$guessParamName]) ? true : false; } /** @@ -937,8 +1440,9 @@ protected function _isGuessPresent() { * @return boolean * @access protected */ - protected function _isTestSuiteNamePresent() { - return(isset( $this->args[self::$testSuiteNameParamName] ) ? true : false); + protected function _isTestSuiteNamePresent() + { + return isset($this->args[self::$testSuiteNameParamName]) ? true : false; } /** @@ -947,8 +1451,9 @@ protected function _isTestSuiteNamePresent() { * @return boolean * @access protected */ - protected function _isDeepPresent() { - return(isset( $this->args[self::$deepParamName] ) ? true : false); + protected function _isDeepPresent() + { + return isset($this->args[self::$deepParamName]) ? true : false; } /** @@ -957,8 +1462,9 @@ protected function _isDeepPresent() { * @return boolean * @access protected */ - protected function _isStatusPresent() { - return(isset( $this->args[self::$statusParamName] ) ? true : false); + protected function _isStatusPresent() + { + return isset($this->args[self::$statusParamName]) ? true : false; } /** @@ -973,16 +1479,52 @@ protected function _isStatusPresent() { * @return boolean * @access protected */ - protected function _isTestCaseIDValid($tcaseid, $messagePrefix = '', $setError = false) { - $status_ok = is_numeric( $tcaseid ); - if($status_ok) { + protected function _isTestCaseIDValid($tcaseid, $messagePrefix = '', + $setError = false) + { + $status_ok = is_numeric($tcaseid); + if ($status_ok) { // must be of type 'testcase' and show up in the nodes_hierarchy - $tcaseid = $this->dbObj->prepare_int( $tcaseid ); - $query = " SELECT NH.id AS id " . " FROM {$this->tables['nodes_hierarchy']} NH, " . " {$this->tables['node_types']} NT " . " WHERE NH.id={$tcaseid} AND node_type_id=NT.id " . " AND NT.description='testcase'"; - $result = $this->dbObj->fetchFirstRowSingleColumn( $query, "id" ); - $status_ok = is_null( $result ) ? false : true; - } else if($setError) { - $this->errors[] = new IXR_Error( TCASEID_NOT_INTEGER, $messagePrefix . TCASEID_NOT_INTEGER_STR ); + $tcaseid = $this->dbObj->prepare_int($tcaseid); + $query = " SELECT NH.id AS id " . + " FROM {$this->tables['nodes_hierarchy']} NH, " . + " {$this->tables['node_types']} NT " . + " WHERE NH.id={$tcaseid} AND node_type_id=NT.id " . + " AND NT.description='testcase'"; + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + $status_ok = is_null($result) ? false : true; + } elseif ($setError) { + $this->errors[] = new IXR_Error(TCASEID_NOT_INTEGER, + $messagePrefix . TCASEID_NOT_INTEGER_STR); + } + return $status_ok; + } + + /** + * Helper method to see if the testcaseversionid provided is valid + * + * @param struct $tcaseversionid + * @param string $messagePrefix + * used to be prepended to error message + * @param boolean $setError + * default false + * true: add predefined error code to $this->error[] + * @return boolean + * @access protected + */ + protected function _isTestCaseVersionIDValid($tcaseversionid, + $messagePrefix = '', $setError = false) + { + $status_ok = is_numeric($tcaseversionid); + if ($status_ok) { + // must be of type 'testcaseversion' and show up in the nodes_hierarchy + $tcaseversionid = $this->dbObj->prepare_int($tcaseversionid); + $query = "SELECT NH.id AS id FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['node_types']} NT WHERE NH.id={$tcaseversionid} AND node_type_id=NT.id AND NT.description='testcase_version'"; + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + $status_ok = is_null($result) ? false : true; + } elseif ($setError) { + $this->errors[] = new IXR_Error(TCASEVERSIONID_NOT_INTEGER, + $messagePrefix . TCASEVERSIONID_NOT_INTEGER_STR); } return $status_ok; } @@ -994,20 +1536,20 @@ protected function _isTestCaseIDValid($tcaseid, $messagePrefix = '', $setError = * @return boolean * @access protected */ - protected function _isDevKeyValid($devKey) { - if(null == $devKey || "" == $devKey) { + protected function _isDevKeyValid($devKey) + { + if (null == $devKey || "" == $devKey) { return false; - } else { - $this->userID = null; - $this->devKey = $this->dbObj->prepare_string( $devKey ); - $query = "SELECT id FROM {$this->tables['users']} WHERE script_key='{$this->devKey}'"; - $this->userID = $this->dbObj->fetchFirstRowSingleColumn( $query, "id" ); + } + $this->userID = null; + $this->devKey = $this->dbObj->prepare_string($devKey); + $query = "SELECT id FROM {$this->tables['users']} WHERE script_key='{$this->devKey}'"; + $this->userID = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); - if(null == $this->userID) { - return false; - } else { - return true; - } + if (null == $this->userID) { + return false; + } else { + return true; } } @@ -1018,51 +1560,60 @@ protected function _isDevKeyValid($devKey) { * @return boolean * @access protected */ - protected function _setTCVersion() { + protected function _setTCVersion() + { // TODO: Implement } /** * Helper method to See if the tcid and tplanid are valid together * - * @param map $platformInfo + * @param array $platformInfo * key: platform ID * @param string $messagePrefix * used to be prepended to error message * @return boolean * @access protected */ - protected function _checkTCIDAndTPIDValid($platformInfo = null, $messagePrefix = '') { + protected function _checkTCIDAndTPIDValid($platformInfo = null, + $messagePrefix = '') + { $tplan_id = $this->args[self::$testPlanIDParamName]; $tcase_id = $this->args[self::$testCaseIDParamName]; - $platform_id = ! is_null( $platformInfo ) ? key( $platformInfo ) : null; + $platform_id = ! is_null($platformInfo) ? key($platformInfo) : null; $filters = array( - 'exec_status' => "ALL", - 'active_status' => "ALL", - 'tplan_id' => $tplan_id, - 'platform_id' => $platform_id + 'exec_status' => "ALL", + 'active_status' => "ALL", + 'tplan_id' => $tplan_id, + 'platform_id' => $platform_id ); - $info = $this->tcaseMgr->get_linked_versions( $tcase_id, $filters ); - $status_ok = ! is_null( $info ); + $info = $this->tcaseMgr->get_linked_versions($tcase_id, $filters); + $status_ok = ! is_null($info); - if($status_ok) { - $this->tcVersionID = key( $info ); - $dummy = current( $info ); - $plat = is_null( $platform_id ) ? 0 : $platform_id; + if ($status_ok) { + $this->tcVersionID = key($info); + $dummy = current($info); + $plat = is_null($platform_id) ? 0 : $platform_id; $this->versionNumber = $dummy[$tplan_id][$plat]['version']; } else { - $tplan_info = $this->tplanMgr->get_by_id( $tplan_id ); - $tcase_info = $this->tcaseMgr->get_by_id( $tcase_id, testcase::ALL_VERSIONS, null, array( + $tplan_info = $this->tplanMgr->get_by_id($tplan_id); + $tcase_info = $this->tcaseMgr->get_by_id($tcase_id, + testcase::ALL_VERSIONS, null, array( 'output' => 'essential' - ) ); - if(is_null( $platform_id )) { - $msg = sprintf( TCASEID_NOT_IN_TPLANID_STR, $tcase_info[0]['name'], $tcase_id, $tplan_info['name'], $tplan_id ); - $this->errors[] = new IXR_Error( TCASEID_NOT_IN_TPLANID, $msg ); + )); + if (is_null($platform_id)) { + $msg = sprintf(TCASEID_NOT_IN_TPLANID_STR, + $tcase_info[0]['name'], $tcase_id, $tplan_info['name'], + $tplan_id); + $this->errors[] = new IXR_Error(TCASEID_NOT_IN_TPLANID, $msg); } else { - $msg = sprintf( TCASEID_NOT_IN_TPLANID_FOR_PLATFORM_STR, $tcase_info[0]['name'], $tcase_id, $tplan_info['name'], $tplan_id, $platformInfo[$platform_id], $platform_id ); - $this->errors[] = new IXR_Error( TCASEID_NOT_IN_TPLANID_FOR_PLATFORM, $msg ); + $msg = sprintf(TCASEID_NOT_IN_TPLANID_FOR_PLATFORM_STR, + $tcase_info[0]['name'], $tcase_id, $tplan_info['name'], + $tplan_id, $platformInfo[$platform_id], $platform_id); + $this->errors[] = new IXR_Error( + TCASEID_NOT_IN_TPLANID_FOR_PLATFORM, $msg); } } return $status_ok; @@ -1076,14 +1627,16 @@ protected function _checkTCIDAndTPIDValid($platformInfo = null, $messagePrefix = * @return boolean * @access protected */ - protected function _checkCreateBuildRequest($messagePrefix = '') { + protected function _checkCreateBuildRequest($messagePrefix = '') + { $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); - if($status_ok) { - $status_ok = $this->_isParamPresent( self::$buildNameParamName, $messagePrefix, self::SET_ERROR ); + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); + if ($status_ok) { + $status_ok = $this->_isParamPresent(self::$buildNameParamName, + $messagePrefix, self::SET_ERROR); } return $status_ok; @@ -1095,13 +1648,13 @@ protected function _checkCreateBuildRequest($messagePrefix = '') { * @return boolean * @access protected */ - protected function _checkGetBuildRequest() { + protected function _checkGetBuildRequest() + { $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions ); - return $status_ok; + return $this->_runChecks($checkFunctions); } /** @@ -1115,13 +1668,14 @@ protected function _checkGetBuildRequest() { * @return boolean * @access protected */ - protected function _runChecks($checkFunctions, $messagePrefix = '') { - foreach( $checkFunctions as $pfn ) { - if(!($status_ok = $this->$pfn( $messagePrefix ))) { - break; + protected function _runChecks($checkFunctions, $messagePrefix = '') + { + foreach ($checkFunctions as $pfn) { + if (! ($status_ok = $this->$pfn($messagePrefix))) { + break; + } } - } - return $status_ok; + return $status_ok; } /** @@ -1134,30 +1688,32 @@ protected function _runChecks($checkFunctions, $messagePrefix = '') { * * @access public */ - public function getLatestBuildForTestPlan($args) { + public function getLatestBuildForTestPlan($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $status_ok = true; - $this->_setArgs( $args ); - $resultInfo = array(); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { + if ($status_ok) { $testPlanID = $this->args[self::$testPlanIDParamName]; - $build_id = $this->tplanMgr->get_max_build_id( $testPlanID ); + $build_id = $this->tplanMgr->get_max_build_id($testPlanID); - if(($status_ok = $build_id > 0)) { - $builds = $this->tplanMgr->get_builds( $testPlanID ); + if ($status_ok = $build_id > 0) { + $builds = $this->tplanMgr->get_builds($testPlanID); $build_info = $builds[$build_id]; } else { - $tplan_info = $this->tplanMgr->get_by_id( $testPlanID ); - $msg = $msg_prefix . sprintf( TPLAN_HAS_NO_BUILDS_STR, $tplan_info['name'], $tplan_info['id'] ); - $this->errors[] = new IXR_Error( TPLAN_HAS_NO_BUILDS, $msg ); + $tplan_info = $this->tplanMgr->get_by_id($testPlanID); + $msg = $msg_prefix . + sprintf(TPLAN_HAS_NO_BUILDS_STR, $tplan_info['name'], + $tplan_info['id']); + $this->errors[] = new IXR_Error(TPLAN_HAS_NO_BUILDS, $msg); } } @@ -1170,12 +1726,13 @@ public function getLatestBuildForTestPlan($args) { * @param struct $args * */ - protected function _getLatestBuildForTestPlan($args) { - $builds = $this->_getBuildsForTestPlan( $args ); + protected function _getLatestBuildForTestPlan($args) + { + $builds = $this->_getBuildsForTestPlan($args); $maxid = - 1; $maxkey = - 1; - foreach( $builds as $key => $build ) { - if($build['id'] > $maxid) { + foreach ($builds as $key => $build) { + if ($build['id'] > $maxid) { $maxkey = $key; $maxid = $build['id']; } @@ -1239,11 +1796,12 @@ protected function _getLatestBuildForTestPlan($args) { * * @access public */ - public function getLastExecutionResult($args) { + public function getLastExecutionResult($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); $status_ok = true; @@ -1252,76 +1810,87 @@ public function getLastExecutionResult($args) { // Checks are done in order $checkFunctions = array( - 'authenticate', - 'checkTestPlanID', - 'checkTestCaseIdentity' + 'authenticate', + 'checkTestPlanID', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->_checkTCIDAndTPIDValid( null, $msg_prefix ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->_checkTCIDAndTPIDValid(null, $msg_prefix) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); $execContext = array( - 'tplan_id' => $this->args[self::$testPlanIDParamName], - 'platform_id' => null, - 'build_id' => null + 'tplan_id' => $this->args[self::$testPlanIDParamName], + 'platform_id' => null, + 'build_id' => null ); - if($status_ok) { - if($this->_isParamPresent( self::$optionsParamName, $msg_prefix )) { + if ($status_ok) { + if ($this->_isParamPresent(self::$optionsParamName, $msg_prefix)) { $dummy = $this->args[self::$optionsParamName]; - if(is_array( $dummy )) { - foreach( $dummy as $key => $value ) { - $options->$key =($value > 0) ? 1 : 0; + if (is_array($dummy)) { + foreach ($dummy as $key => $value) { + $options->$key = ($value > 0) ? 1 : 0; } } } // Now we can check for Optional parameters - if($this->_isBuildIDPresent() || $this->_isBuildNamePresent()) { - if(($status_ok = $this->checkBuildID( $msg_prefix ))) { - $execContext['build_id'] = $this->args[self::$buildIDParamName]; - } + if ($this->_isBuildIDPresent() || + $this->_isBuildNamePresent() && + $status_ok = $this->checkBuildID($msg_prefix)) { + $execContext['build_id'] = $this->args[self::$buildIDParamName]; } - if($status_ok) { - if($this->_isParamPresent( self::$platformIDParamName, $msg_prefix ) || $this->_isParamPresent( self::$platformNameParamName, $msg_prefix )) { - $status_ok = $this->checkPlatformIdentity( $this->args[self::$testPlanIDParamName] ); + if ($status_ok && + $this->_isParamPresent(self::$platformIDParamName, $msg_prefix) || + $this->_isParamPresent(self::$platformNameParamName, $msg_prefix)) { + $status_ok = $this->checkPlatformIdentity( + $this->args[self::$testPlanIDParamName]); - if($status_ok) { - $execContext['platform_id'] = $this->args[self::$platformIDParamName]; - } + if ($status_ok) { + $execContext['platform_id'] = $this->args[self::$platformIDParamName]; } } } - if($status_ok) { + if ($status_ok) { - $sql = " SELECT MAX(id) AS exec_id FROM {$this->tables['executions']} " . " WHERE testplan_id = {$this->args[self::$testPlanIDParamName]} " . " AND tcversion_id IN(" . " SELECT id FROM {$this->tables['nodes_hierarchy']} " . " WHERE parent_id = {$this->args[self::$testCaseIDParamName]})"; + $sql = " SELECT MAX(id) AS exec_id FROM {$this->tables['executions']} " . + " WHERE testplan_id = {$this->args[self::$testPlanIDParamName]} " . + " AND tcversion_id IN(" . + " SELECT id FROM {$this->tables['nodes_hierarchy']} " . + " WHERE parent_id = {$this->args[self::$testCaseIDParamName]})"; - if(! is_null( $execContext['build_id'] )) { - $sql .= " AND build_id = " . intval( $execContext['build_id'] ); + if (! is_null($execContext['build_id'])) { + $sql .= " AND build_id = " . intval($execContext['build_id']); } - if(! is_null( $execContext['platform_id'] )) { - $sql .= " AND platform_id = " . intval( $execContext['platform_id'] ); + if (! is_null($execContext['platform_id'])) { + $sql .= " AND platform_id = " . + intval($execContext['platform_id']); } - $rs = $this->dbObj->fetchRowsIntoMap( $sql, 'exec_id' ); - if(is_null( $rs )) { + $rs = $this->dbObj->fetchRowsIntoMap($sql, 'exec_id'); + if (is_null($rs)) { // has not been executed // execution id = -1 => test case has not been runned. $resultInfo[] = array( - 'id' => - 1 + 'id' => - 1 ); } else { // OK Select * is not a good practice but ...(fman) - $targetID = intval( key( $rs ) ); - $sql = "SELECT * FROM {$this->tables['executions']} WHERE id=" . $targetID; - $resultInfo[0] = $this->dbObj->fetchFirstRow( $sql ); + $targetID = intval(key($rs)); + $sql = "SELECT * FROM {$this->tables['executions']} WHERE id=" . + $targetID; + $resultInfo[0] = $this->dbObj->fetchFirstRow($sql); - if($options->getBugs) { + if ($options->getBugs) { $resultInfo[0]['bugs'] = array(); - $sql = " SELECT DISTINCT bug_id FROM {$this->tables['execution_bugs']} " . " WHERE execution_id = " . $targetID; - $resultInfo[0]['bugs'] =( array ) $this->dbObj->get_recordset( $sql ); + $sql = " SELECT DISTINCT bug_id FROM {$this->tables['execution_bugs']} " . + " WHERE execution_id = " . $targetID; + $resultInfo[0]['bugs'] = (array) $this->dbObj->get_recordset( + $sql); } } } @@ -1332,7 +1901,7 @@ public function getLastExecutionResult($args) { /** * Gets ALL EXECUTIONS for a particular testcase on a test plan. * If there are no filter criteria regarding platform and build, - * result will be get WITHOUT checking for a particular platform + * result will be get WITHOUT checking for a particular platform * and build. * * @param struct $args @@ -1340,12 +1909,12 @@ public function getLastExecutionResult($args) { * @param int $args["tplanid"] * @param int $args["testcaseid"]: * Pseudo optional. - * if does not is present then + * if does not is present then * testcaseexternalid MUST BE present * * @param int $args["testcaseexternalid"]: * Pseudo optional. - * if does not is present then testcaseid + * if does not is present then testcaseid * MUST BE present * * @param string $args["platform_id"]: @@ -1371,16 +1940,17 @@ public function getLastExecutionResult($args) { * If user has requested getbugs, then a key bugs(that is an array) * will also exists. * - * if test case has not been execute, the first map + * if test case has not been execute, the first map * will be returned with -1 as 'id' * * @access public */ - public function getAllExecutionsResults($args) { + public function getAllExecutionsResults($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); $status_ok = true; @@ -1388,83 +1958,93 @@ public function getAllExecutionsResults($args) { $options->getBugs = 0; // Checks are done in order - $checkFunctions = array('authenticate', - 'checkTestPlanID', - 'checkTestCaseIdentity'); + $checkFunctions = array( + 'authenticate', + 'checkTestPlanID', + 'checkTestCaseIdentity' + ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->_checkTCIDAndTPIDValid( null, $msg_prefix ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->_checkTCIDAndTPIDValid(null, $msg_prefix) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); $execContext = array( - 'tplan_id' => $this->args[self::$testPlanIDParamName], - 'platform_id' => null, 'build_id' => null); + 'tplan_id' => $this->args[self::$testPlanIDParamName], + 'platform_id' => null, + 'build_id' => null + ); - if($status_ok) { - if($this->_isParamPresent( self::$optionsParamName, $msg_prefix )) { - $dummy = $this->args[self::$optionsParamName]; - if(is_array( $dummy )) { - foreach( $dummy as $key => $value ) { - $options->$key =($value > 0) ? 1 : 0; + if ($status_ok) { + if ($this->_isParamPresent(self::$optionsParamName, $msg_prefix)) { + $dummy = $this->args[self::$optionsParamName]; + if (is_array($dummy)) { + foreach ($dummy as $key => $value) { + $options->$key = ($value > 0) ? 1 : 0; + } + } } - } - } - // Now we can check for Optional parameters - if($this->_isBuildIDPresent() || $this->_isBuildNamePresent()) { - if(($status_ok = $this->checkBuildID( $msg_prefix ))) { - $execContext['build_id'] = $this->args[self::$buildIDParamName]; - } - } + // Now we can check for Optional parameters + if ($this->_isBuildIDPresent() || + $this->_isBuildNamePresent() && + $status_ok = $this->checkBuildID($msg_prefix)) { + $execContext['build_id'] = $this->args[self::$buildIDParamName]; + } - if($status_ok) { - if($this->_isParamPresent( self::$platformIDParamName, $msg_prefix ) || $this->_isParamPresent( self::$platformNameParamName, $msg_prefix )) { - $status_ok = $this->checkPlatformIdentity( $this->args[self::$testPlanIDParamName] ); + if ($status_ok && + $this->_isParamPresent(self::$platformIDParamName, $msg_prefix) || + $this->_isParamPresent(self::$platformNameParamName, $msg_prefix)) { + $status_ok = $this->checkPlatformIdentity( + $this->args[self::$testPlanIDParamName]); - if($status_ok) { + if ($status_ok) { $execContext['platform_id'] = $this->args[self::$platformIDParamName]; } - } + } } - } - if($status_ok) { + if ($status_ok) { - $sql = " SELECT(id) AS exec_id FROM {$this->tables['executions']} - WHERE testplan_id = {$this->args[self::$testPlanIDParamName]} - AND tcversion_id IN(" . - " SELECT id FROM {$this->tables['nodes_hierarchy']} + $sql = " SELECT(id) AS exec_id FROM {$this->tables['executions']} + WHERE testplan_id = {$this->args[self::$testPlanIDParamName]} + AND tcversion_id IN(" . + " SELECT id FROM {$this->tables['nodes_hierarchy']} WHERE parent_id = {$this->args[self::$testCaseIDParamName]})"; - if(!is_null( $execContext['build_id'] )) { - $sql .= " AND build_id = " . intval( $execContext['build_id'] ); - } + if (! is_null($execContext['build_id'])) { + $sql .= " AND build_id = " . intval($execContext['build_id']); + } - if(!is_null( $execContext['platform_id'] )) { - $sql .= " AND platform_id = " . intval( $execContext['platform_id'] ); - } + if (! is_null($execContext['platform_id'])) { + $sql .= " AND platform_id = " . + intval($execContext['platform_id']); + } - $rs = $this->dbObj->fetchRowsIntoMap( $sql, 'exec_id' ); - if(is_null( $rs )) { - // has not been executed - // execution id = -1 => test case has not been runned. - $resultInfo[0] = array('id' => - 1); - } else { - foreach( $rs as $tcExecId => $dummy ) { - $sql = "SELECT * FROM {$this->tables['executions']} WHERE id=" . - $tcExecId; - $resultInfo[$tcExecId] = $this->dbObj->fetchFirstRow( $sql ); - if($options->getBugs) { - $resultInfo[$tcExecId]['bugs'] = array(); - $sql = " SELECT DISTINCT bug_id FROM + $rs = $this->dbObj->fetchRowsIntoMap($sql, 'exec_id'); + if (is_null($rs)) { + // has not been executed + // execution id = -1 => test case has not been runned. + $resultInfo[0] = array( + 'id' => - 1 + ); + } else { + foreach ($rs as $tcExecId => $dummy) { + $sql = "SELECT * FROM {$this->tables['executions']} WHERE id=" . + $tcExecId; + $resultInfo[$tcExecId] = $this->dbObj->fetchFirstRow($sql); + if ($options->getBugs) { + $resultInfo[$tcExecId]['bugs'] = array(); + $sql = " SELECT DISTINCT bug_id FROM {$this->tables['execution_bugs']} WHERE execution_id = " . $tcExecId; - $resultInfo[$tcExecId]['bugs'] = - (array)$this->dbObj->get_recordset( $sql ); + $resultInfo[$tcExecId]['bugs'] = (array) $this->dbObj->get_recordset( + $sql); + } + } } - } } - } - return $status_ok ? $resultInfo : $this->errors; + return $status_ok ? $resultInfo : $this->errors; } /** @@ -1473,21 +2053,20 @@ public function getAllExecutionsResults($args) { * @return int * @access protected */ - protected function _insertResultToDB($user_id = null, $exec_ts = null) { + protected function _insertResultToDB($user_id = null, $exec_ts = null) + { $build_id = $this->args[self::$buildIDParamName]; $status = $this->args[self::$statusParamName]; $testplan_id = $this->args[self::$testPlanIDParamName]; $tcversion_id = $this->tcVersionID; $version_number = $this->versionNumber; - $tester_id = is_null( $user_id ) ? $this->userID : $user_id; - $execTimeStamp = is_null( $exec_ts ) ? $this->dbObj->db_now() : $exec_ts; - - // return $execTimeStamp; + $tester_id = is_null($user_id) ? $this->userID : $user_id; + $execTimeStamp = is_null($exec_ts) ? $this->dbObj->db_now() : $exec_ts; $platform_id = 0; - if(isset( $this->args[self::$platformIDParamName] )) { + if (isset($this->args[self::$platformIDParamName])) { $platform_id = $this->args[self::$platformIDParamName]; } @@ -1495,28 +2074,37 @@ protected function _insertResultToDB($user_id = null, $exec_ts = null) { $notes_field = ""; $notes_value = ""; - if($this->_isNotePresent()) { - $notes = $this->dbObj->prepare_string( $this->args[self::$noteParamName] ); + if ($this->_isNotePresent()) { + $notes = $this->dbObj->prepare_string( + $this->args[self::$noteParamName]); } - if(trim( $notes ) != "") { + if (trim($notes) != "") { $notes_field = ",notes"; $notes_value = ", '{$notes}'"; } $duration_field = ''; $duration_value = ''; - if(isset( $this->args[self::$executionDurationParamName] )) { + if (isset($this->args[self::$executionDurationParamName])) { $duration_field = ',execution_duration'; - $duration_value = ", " . floatval( $this->args[self::$executionDurationParamName] ); + $duration_value = ", " . + floatval($this->args[self::$executionDurationParamName]); } - $execution_type = constant( "TESTCASE_EXECUTION_TYPE_AUTO" ); + $execution_type = constant("TESTCASE_EXECUTION_TYPE_AUTO"); - $query = "INSERT INTO {$this->tables['executions']} " . "(build_id, tester_id, execution_ts, status, testplan_id, tcversion_id, " . " platform_id, tcversion_number," . " execution_type {$notes_field} {$duration_field}) " . " VALUES({$build_id},{$tester_id},{$execTimeStamp}," . " '{$status}',{$testplan_id}," . " {$tcversion_id},{$platform_id}, {$version_number},{$execution_type} " . " {$notes_value} {$duration_value})"; + $query = "INSERT INTO {$this->tables['executions']} " . + "(build_id, tester_id, execution_ts, status, testplan_id, tcversion_id, " . + " platform_id, tcversion_number," . + " execution_type {$notes_field} {$duration_field}) " . + " VALUES({$build_id},{$tester_id},{$execTimeStamp}," . + " '{$status}',{$testplan_id}," . + " {$tcversion_id},{$platform_id}, {$version_number},{$execution_type} " . + " {$notes_value} {$duration_value})"; - $this->dbObj->exec_query( $query ); - return $this->dbObj->insert_id( $this->tables['executions'] ); + $this->dbObj->exec_query($query); + return $this->dbObj->insert_id($this->tables['executions']); } /** @@ -1527,7 +2115,8 @@ protected function _insertResultToDB($user_id = null, $exec_ts = null) { * @return string "Hello!" * @access public */ - public function sayHello($args) { + public function sayHello($args) + { return 'Hello!'; } @@ -1539,10 +2128,10 @@ public function sayHello($args) { * @return string * @access public */ - public function repeat($args) { - $this->_setArgs( $args ); - $str = "You said: " . $this->args['str']; - return $str; + public function repeat($args) + { + $this->_setArgs($args); + return "You said: " . $this->args['str']; } /** @@ -1552,7 +2141,8 @@ public function repeat($args) { * @return string * @access public */ - public function testLinkVersion() { + public function testLinkVersion() + { return TL_VERSION_NUMBER; } @@ -1564,10 +2154,12 @@ public function testLinkVersion() { * @return string * @access public */ - public function about($args) { - $this->_setArgs( $args ); - $str = " Testlink API Version: " . self::$version . " initially written by Asiel Brumfield\n" . " with contributions by TestLink development Team"; - return $str; + public function about($args) + { + $this->_setArgs($args); + return " Testlink API Version: " . self::$version . + " initially written by Asiel Brumfield\n" . + " with contributions by TestLink development Team"; } /** @@ -1590,7 +2182,8 @@ public function about($args) { * * @access public */ - public function createBuild($args) { + public function createBuild($args) + { $operation = __FUNCTION__; $messagePrefix = "({$operation}) - "; $resultInfo = array(); @@ -1599,61 +2192,73 @@ public function createBuild($args) { $insertID = ''; $returnMessage = GENERAL_SUCCESS_STR; - $this->_setArgs( $args ); + $this->_setArgs($args); // check the tpid - if($this->_checkCreateBuildRequest( $messagePrefix ) && $this->userHasRight( "testplan_create_build", self::CHECK_PUBLIC_PRIVATE_ATTR )) { - $testPlanID = intval( $this->args[self::$testPlanIDParamName] ); + if ($this->_checkCreateBuildRequest($messagePrefix) && + $this->userHasRight("testplan_create_build", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { + $testPlanID = intval($this->args[self::$testPlanIDParamName]); $buildName = $this->args[self::$buildNameParamName]; $buildNotes = ""; - if($this->_isBuildNotePresent()) { - $buildNotes = $this->dbObj->prepare_string( $this->args[self::$buildNotesParamName] ); + if ($this->_isBuildNotePresent()) { + $buildNotes = $this->dbObj->prepare_string( + $this->args[self::$buildNotesParamName]); } - if($this->tplanMgr->check_build_name_existence( $testPlanID, $buildName )) { + if ($this->tplanMgr->check_build_name_existence($testPlanID, + $buildName)) { // Build exists so just get the id of the existing build - $insertID = $this->tplanMgr->get_build_id_by_name( $testPlanID, $buildName ); - $returnMessage = sprintf( BUILDNAME_ALREADY_EXISTS_STR, $buildName, $insertID ); + $insertID = $this->tplanMgr->get_build_id_by_name($testPlanID, + $buildName); + $returnMessage = sprintf(BUILDNAME_ALREADY_EXISTS_STR, + $buildName, $insertID); $resultInfo[0]["status"] = false; } else { // Build doesn't exist so create one // ,$active=1,$open=1); - //($tplan_id,$name,$notes = '',$active=1,$open=1,$release_date='') + // ($tplan_id,$name,$notes = '',$active=1,$open=1,$release_date='') // key 2 check with default value is parameter is missing $k2check = array( - self::$activeParamName => 1, - self::$openParamName => 1, - self::$releaseDateParamName => null, - self::$copyTestersFromBuildParamName => 0 + self::$activeParamName => 1, + self::$openParamName => 1, + self::$releaseDateParamName => null, + self::$copyTestersFromBuildParamName => 0 ); - foreach( $k2check as $key => $value ) { - $opt[$key] = $this->_isParamPresent( $key ) ? $this->args[$key] : $value; + foreach ($k2check as $key => $value) { + $opt[$key] = $this->_isParamPresent($key) ? $this->args[$key] : $value; } // check if release date is valid date. // do not check relation with now(), i.e can be <,> or =. // - if(! is_null( $opt[self::$releaseDateParamName] )) { - if(! $this->validateDateISO8601( $opt[self::$releaseDateParamName] )) { - $opt[self::$releaseDateParamName] = null; - } + if (! is_null($opt[self::$releaseDateParamName]) && + ! $this->validateDateISO8601( + $opt[self::$releaseDateParamName])) { + $opt[self::$releaseDateParamName] = null; } - $bm = new build_mgr( $this->dbObj ); - $insertID = $bm->create( $testPlanID, $buildName, $buildNotes, $opt[self::$activeParamName], $opt[self::$openParamName], $opt[self::$releaseDateParamName] ); + $bm = new build_mgr($this->dbObj); + $insertID = $bm->create($testPlanID, $buildName, $buildNotes, + $opt[self::$activeParamName], $opt[self::$openParamName], + $opt[self::$releaseDateParamName]); - if($insertID > 0) { - $sourceBuild = intval( $opt[self::$copyTestersFromBuildParamName] ); + if ($insertID > 0) { + $sourceBuild = intval( + $opt[self::$copyTestersFromBuildParamName]); - if($sourceBuild > 0) { + if ($sourceBuild > 0) { // Check if belongs to test plan, otherwise ignore in silence - $sql = " SELECT id FROM {$this->tables['builds']} " . " WHERE id = " . $sourceBuild . " AND testplan_id = " . $testPlanID; - $rs = $this->dbObj->get_recordset( $sql ); - - if(count( $rs ) == 1) { - $taskMgr = new assignment_mgr( $this->dbObj ); - $taskMgr->copy_assignments( $sourceBuild, $insertID, $this->userID ); + $sql = " SELECT id FROM {$this->tables['builds']} " . + " WHERE id = " . $sourceBuild . " AND testplan_id = " . + $testPlanID; + $rs = $this->dbObj->get_recordset($sql); + + if (count($rs) == 1) { + $taskMgr = new assignment_mgr($this->dbObj); + $taskMgr->copy_assignments($sourceBuild, $insertID, + $this->userID); } } } @@ -1675,11 +2280,14 @@ public function createBuild($args) { * @return mixed $resultInfo * @access public */ - public function getProjects($args) { - $this->_setArgs( $args ); - // TODO: NEED associated RIGHT - if($this->authenticate()) { - return $this->tprojectMgr->get_all(); + public function getProjects($args) + { + $this->_setArgs($args); + if ($this->authenticate()) { + return $this->tprojectMgr->get_accessible_for_user($this->userID, + array( + 'output' => 'array_of_map' + )); } else { return $this->errors; } @@ -1695,23 +2303,24 @@ public function getProjects($args) { * * @access public */ - public function getProjectTestPlans($args) { + public function getProjectTestPlans($args) + { $messagePrefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); // check the tplanid // TODO: NEED associated RIGHT $checkFunctions = array( - 'authenticate', - 'checkTestProjectID' + 'authenticate', + 'checkTestProjectID' ); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); - if($status_ok) { + if ($status_ok) { $testProjectID = $this->args[self::$testProjectIDParamName]; - $info = $this->tprojectMgr->get_all_testplans( $testProjectID ); - if(! is_null( $info ) && count( $info ) > 0) { - $info = array_values( $info ); + $info = $this->tprojectMgr->get_all_testplans($testProjectID); + if (! empty($info)) { + $info = array_values($info); } return $info; } else { @@ -1732,25 +2341,26 @@ public function getProjectTestPlans($args) { * * @access public */ - public function getBuildsForTestPlan($args) { + public function getBuildsForTestPlan($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $builds = null; $status_ok = true; $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { + if ($status_ok) { $testPlanID = $this->args[self::$testPlanIDParamName]; - $dummy = $this->tplanMgr->get_builds( $testPlanID ); + $dummy = $this->tplanMgr->get_builds($testPlanID); - if(! is_null( $dummy )) { - $builds = array_values( $dummy ); + if (! is_null($dummy)) { + $builds = array_values($dummy); } } return $status_ok ? $builds : $this->errors; @@ -1764,20 +2374,20 @@ public function getBuildsForTestPlan($args) { * @param int $args["testplanid"] * @return mixed $resultInfo */ - public function getTestSuitesForTestPlan($args) { + public function getTestSuitesForTestPlan($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); - if($status_ok) { + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + if ($status_ok) { $testPlanID = $this->args[self::$testPlanIDParamName]; - $result = $this->tplanMgr->get_testsuites( $testPlanID ); - return $result; + return $this->tplanMgr->get_testsuites($testPlanID); } else { return $this->errors; } @@ -1792,7 +2402,7 @@ public function getTestSuitesForTestPlan($args) { * @param string $args["testcaseprefix"] * @param string $args["notes"] * OPTIONAL - * @param map $args["options"] + * @param array $args["options"] * OPTIONAL ALL int treated as boolean * keys requirementsEnabled,testPriorityEnabled,automationEnabled,inventoryEnabled * @@ -1808,13 +2418,15 @@ public function getTestSuitesForTestPlan($args) { * * @return mixed $resultInfo */ - public function createTestProject($args) { - $this->_setArgs( $args ); + public function createTestProject($args) + { + $this->_setArgs($args); $msg_prefix = "(" . __FUNCTION__ . ") - "; - $checkRequestMethod = '_check' . ucfirst( __FUNCTION__ ) . 'Request'; + $checkRequestMethod = '_check' . ucfirst(__FUNCTION__) . 'Request'; $status_ok = false; - if($this->$checkRequestMethod( $msg_prefix ) && $this->userHasRight( "mgt_modify_product" )) { + if ($this->$checkRequestMethod($msg_prefix) && + $this->userHasRight("mgt_modify_product")) { $status_ok = true; $item = new stdClass(); @@ -1824,11 +2436,11 @@ public function createTestProject($args) { $item->options->automationEnabled = 1; $item->options->inventoryEnabled = 1; - if($this->_isParamPresent( self::$optionsParamName, $msg_prefix )) { + if ($this->_isParamPresent(self::$optionsParamName, $msg_prefix)) { // has to be an array ? $dummy = $this->args[self::$optionsParamName]; - if(is_array( $dummy )) { - foreach( $dummy as $key => $value ) { + if (is_array($dummy)) { + foreach ($dummy as $key => $value) { $item->options->$key = $value > 0 ? 1 : 0; } } @@ -1837,101 +2449,112 @@ public function createTestProject($args) { // other optional parameters(not of complex type) // key 2 check with default value is parameter is missing $keys2check = array( - self::$activeParamName => 1, - self::$publicParamName => 1, - self::$noteParamName => '', - self::$itsEnabledParamName => 0, - self::$itsNameParamName => '' + self::$activeParamName => 1, + self::$publicParamName => 1, + self::$noteParamName => '', + self::$itsEnabledParamName => 0, + self::$itsNameParamName => '' ); - foreach( $keys2check as $key => $value ) { - $optional[$key] = $this->_isParamPresent( $key ) ? trim( $this->args[$key] ) : $value; + foreach ($keys2check as $key => $value) { + $optional[$key] = $this->_isParamPresent($key) ? trim( + $this->args[$key]) : $value; } - $item->name = htmlspecialchars( $this->args[self::$testProjectNameParamName] ); - $item->prefix = htmlspecialchars( $this->args[self::$testCasePrefixParamName] ); + $item->name = htmlspecialchars( + $this->args[self::$testProjectNameParamName]); + $item->prefix = htmlspecialchars( + $this->args[self::$testCasePrefixParamName]); - $item->notes = htmlspecialchars( $optional[self::$noteParamName] ); - $item->active =($optional[self::$activeParamName] > 0) ? 1 : 0; - $item->is_public =($optional[self::$publicParamName] > 0) ? 1 : 0; + $item->notes = htmlspecialchars($optional[self::$noteParamName]); + $item->active = ($optional[self::$activeParamName] > 0) ? 1 : 0; + $item->is_public = ($optional[self::$publicParamName] > 0) ? 1 : 0; $item->color = ''; $its = null; - if($optional[self::$itsNameParamName] != "") { - $this->itsMgr = new tlIssueTracker( $this->dbObj ); - $its = $this->getIssueTrackerSystem( $this->args, 'internal' ); + if ($optional[self::$itsNameParamName] != "") { + $this->itsMgr = new tlIssueTracker($this->dbObj); + $its = $this->getIssueTrackerSystem($this->args, 'internal'); - $itsOK = ! is_null( $its ); - if(! $itsOK) { + $itsOK = ! is_null($its); + if (! $itsOK) { $status_ok = false; } } } // All checks OK => try to create testproject - if($status_ok) { - $tproject_id = $this->tprojectMgr->create( $item ); + if ($status_ok) { + $tproject_id = $this->tprojectMgr->create($item); // link & enable its? - if($itsOK && $tproject_id > 0) { + if ($itsOK && $tproject_id > 0) { // link - $this->itsMgr->link( $its["id"], $tproject_id ); + $this->itsMgr->link($its["id"], $tproject_id); // enable - if($optional[self::$itsEnabledParamName] > 0) { - $this->tprojectMgr->enableIssueTracker( $tproject_id ); + if ($optional[self::$itsEnabledParamName] > 0) { + $this->tprojectMgr->enableIssueTracker($tproject_id); } } $ret = array(); $ret[] = array( - "operation" => __FUNCTION__, - "additionalInfo" => null, - "status" => true, - "id" => $tproject_id, - "message" => GENERAL_SUCCESS_STR + "operation" => __FUNCTION__, + "additionalInfo" => null, + "status" => true, + "id" => $tproject_id, + "message" => GENERAL_SUCCESS_STR ); return $ret; } - return($status_ok ? $ret : $this->errors); + return $status_ok ? $ret : $this->errors; } /** * _checkCreateTestProjectRequest */ - protected function _checkCreateTestProjectRequest($msg_prefix) { + protected function _checkCreateTestProjectRequest($msg_prefix) + { $status_ok = $this->authenticate(); $name = $this->args[self::$testProjectNameParamName]; $prefix = $this->args[self::$testCasePrefixParamName]; - if($status_ok) { - $check_op = $this->tprojectMgr->checkNameSintax( $name ); + if ($status_ok) { + $check_op = $this->tprojectMgr->checkNameSintax($name); $status_ok = $check_op['status_ok']; - if(! $status_ok) { - $this->errors[] = new IXR_Error( TESTPROJECTNAME_SINTAX_ERROR, $msg_prefix . $check_op['msg'] ); + if (! $status_ok) { + $this->errors[] = new IXR_Error(TESTPROJECTNAME_SINTAX_ERROR, + $msg_prefix . $check_op['msg']); } } - if($status_ok) { - $check_op = $this->tprojectMgr->checkNameExistence( $name ); + if ($status_ok) { + $check_op = $this->tprojectMgr->checkNameExistence($name); $status_ok = $check_op['status_ok']; - if(! $status_ok) { - $this->errors[] = new IXR_Error( TESTPROJECTNAME_EXISTS, $msg_prefix . $check_op['msg'] ); + if (! $status_ok) { + $this->errors[] = new IXR_Error(TESTPROJECTNAME_EXISTS, + $msg_prefix . $check_op['msg']); } } - if($status_ok) { - $status_ok = ! empty( $prefix ); - if(! $status_ok) { - $this->errors[] = new IXR_Error( TESTPROJECT_TESTCASEPREFIX_IS_EMPTY, $msg_prefix . $check_op['msg'] ); + if ($status_ok) { + $status_ok = ! empty($prefix); + if (! $status_ok) { + $this->errors[] = new IXR_Error( + TESTPROJECT_TESTCASEPREFIX_IS_EMPTY, + $msg_prefix . $check_op['msg']); } } - if($status_ok) { - $info = $this->tprojectMgr->get_by_prefix( $prefix ); - if(!($status_ok = is_null( $info ))) { - $msg = $msg_prefix . sprintf( TPROJECT_PREFIX_ALREADY_EXISTS_STR, $prefix, $info['name'] ); - $this->errors[] = new IXR_Error( TPROJECT_PREFIX_ALREADY_EXISTS, $msg ); + if ($status_ok) { + $info = $this->tprojectMgr->get_by_prefix($prefix); + if (! ($status_ok = is_null($info))) { + $msg = $msg_prefix . + sprintf(TPROJECT_PREFIX_ALREADY_EXISTS_STR, $prefix, + $info['name']); + $this->errors[] = new IXR_Error(TPROJECT_PREFIX_ALREADY_EXISTS, + $msg); } } @@ -1962,45 +2585,49 @@ protected function _checkCreateTestProjectRequest($msg_prefix) { * * */ - public function getTestCasesForTestSuite($args) { + public function getTestCasesForTestSuite($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); - $status_ok = $this->_runChecks( array( + $this->_setArgs($args); + $status_ok = $this->_runChecks( + array( 'authenticate', 'checkTestSuiteID' - ), $msg_prefix ); + ), $msg_prefix); $details = 'simple'; $key2search = self::$detailsParamName; - if($this->_isParamPresent( $key2search )) { + if ($this->_isParamPresent($key2search)) { $details = $this->args[$key2search]; } - if($status_ok) { + if ($status_ok) { $testSuiteID = $this->args[self::$testSuiteIDParamName]; - $dummy = $this->tprojectMgr->tree_manager->get_path( $testSuiteID ); + $dummy = $this->tprojectMgr->tree_manager->get_path($testSuiteID); $this->args[self::$testProjectIDParamName] = $dummy[0]['parent_id']; - $status_ok = $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->userHasRight("mgt_view_tc", + self::CHECK_PUBLIC_PRIVATE_ATTR); } - if($status_ok) { - $tsuiteMgr = new testsuite( $this->dbObj ); - if(! $this->_isDeepPresent() || $this->args[self::$deepParamName]) { + if ($status_ok) { + $tsuiteMgr = new testsuite($this->dbObj); + if (! $this->_isDeepPresent() || $this->args[self::$deepParamName]) { $pfn = 'get_testcases_deep'; } else { $pfn = 'get_children_testcases'; } $opt = null; - if(isset( $this->args[self::$getKeywordsParamName] ) && $this->args[self::$getKeywordsParamName]) { + if (isset($this->args[self::$getKeywordsParamName]) && + $this->args[self::$getKeywordsParamName]) { $opt = array( - 'getKeywords' => true + 'getKeywords' => true ); } - return $tsuiteMgr->$pfn( $testSuiteID, $details, $opt ); + return $tsuiteMgr->$pfn($testSuiteID, $details, $opt); } else { return $this->errors; } @@ -2027,51 +2654,57 @@ public function getTestCasesForTestSuite($args) { * pieces separator -> :: -> default value of getByPathName() * @return mixed $resultInfo */ - public function getTestCaseIDByName($args) { + public function getTestCaseIDByName($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $status_ok = true; - $this->_setArgs( $args ); + $this->_setArgs($args); $result = null; $checkFunctions = array( - 'authenticate', - 'checkTestCaseName' + 'authenticate', + 'checkTestCaseName' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { + if ($status_ok) { $testCaseName = $this->args[self::$testCaseNameParamName]; - $testCaseMgr = new testcase( $this->dbObj ); + $testCaseMgr = new testcase($this->dbObj); $keys2check = array( - self::$testSuiteNameParamName, - self::$testCasePathNameParamName, - self::$testProjectNameParamName + self::$testSuiteNameParamName, + self::$testCasePathNameParamName, + self::$testProjectNameParamName ); - foreach( $keys2check as $key ) { - $optional[$key] = $this->_isParamPresent( $key ) ? trim( $this->args[$key] ) : ''; + foreach ($keys2check as $key) { + $optional[$key] = $this->_isParamPresent($key) ? trim( + $this->args[$key]) : ''; } - if($optional[self::$testCasePathNameParamName] != '') { - $dummy = $testCaseMgr->getByPathName( $optional[self::$testCasePathNameParamName] ); - if(! is_null( $dummy )) { + if ($optional[self::$testCasePathNameParamName] != '') { + $dummy = $testCaseMgr->getByPathName( + $optional[self::$testCasePathNameParamName]); + if (! is_null($dummy)) { $result[0] = $dummy; } } else { - $result = $testCaseMgr->get_by_name( $testCaseName, $optional[self::$testSuiteNameParamName], $optional[self::$testProjectNameParamName] ); + $result = $testCaseMgr->get_by_name($testCaseName, + $optional[self::$testSuiteNameParamName], + $optional[self::$testProjectNameParamName]); } - $match_count = count( $result ); - switch($match_count) { - case 0 : + $match_count = count($result); + switch ($match_count) { + case 0: $status_ok = false; - $this->errors[] = new IXR_ERROR( NO_TESTCASE_BY_THIS_NAME, $msg_prefix . NO_TESTCASE_BY_THIS_NAME_STR ); + $this->errors[] = new IXR_ERROR(NO_TESTCASE_BY_THIS_NAME, + $msg_prefix . NO_TESTCASE_BY_THIS_NAME_STR); break; - case 1 : + case 1: $status_ok = true; break; - default : + default: // multiple matches. $status_ok = true; break; @@ -2084,11 +2717,13 @@ public function getTestCaseIDByName($args) { // what to do ? // check access for each result and remove result if user has no access to corresponding // test project. - if($status_ok) { + if ($status_ok) { $out = null; - foreach( $result as $testcase ) { - $this->args[self::$testProjectIDParamName] = $this->tcaseMgr->get_testproject( $testcase['id'] ); - if($this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + foreach ($result as $testcase) { + $this->args[self::$testProjectIDParamName] = $this->tcaseMgr->get_testproject( + $testcase['id']); + if ($this->userHasRight("mgt_view_tc", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { $out[] = $testcase; } } @@ -2145,90 +2780,106 @@ public function getTestCaseIDByName($args) { * @return boolean $resultInfo['additionalInfo']['has_duplicate'] - for debug * @return string $resultInfo['message'] operation message */ - public function createTestCase($args) { + public function createTestCase($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $wfStatusDomain = config_get( 'testCaseStatus' ); + $wfStatusDomain = config_get('testCaseStatus'); $keywordSet = ''; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkTestSuiteID', - 'checkTestCaseName' + 'authenticate', + 'checkTestProjectID', + 'checkTestSuiteID', + 'checkTestCaseName' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->userHasRight( "mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->userHasRight("mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); - if($status_ok) { + if ($status_ok) { $keys2check = array( - self::$authorLoginParamName, - self::$summaryParamName, - self::$stepsParamName + self::$authorLoginParamName, + self::$summaryParamName, + self::$stepsParamName ); - foreach( $keys2check as $key ) { - if(! $this->_isParamPresent( $key )) { + foreach ($keys2check as $key) { + if (! $this->_isParamPresent($key)) { $status_ok = false; - $msg = $msg_prefix . sprintf( MISSING_REQUIRED_PARAMETER_STR, $key ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); + $msg = $msg_prefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, $key); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, + $msg); } } } - if($status_ok) { - $author_id = tlUser::doesUserExist( $this->dbObj, $this->args[self::$authorLoginParamName] ); - if(!($status_ok = ! is_null( $author_id ))) { - $msg = $msg_prefix . sprintf( NO_USER_BY_THIS_LOGIN_STR, $this->args[self::$authorLoginParamName] ); - $this->errors[] = new IXR_Error( NO_USER_BY_THIS_LOGIN, $msg ); + if ($status_ok) { + $author_id = tlUser::doesUserExist($this->dbObj, + $this->args[self::$authorLoginParamName]); + if (! ($status_ok = ! is_null($author_id))) { + $msg = $msg_prefix . + sprintf(NO_USER_BY_THIS_LOGIN_STR, + $this->args[self::$authorLoginParamName]); + $this->errors[] = new IXR_Error(NO_USER_BY_THIS_LOGIN, $msg); } } - if($status_ok) { - $keywordSet = $this->getKeywordSet( $this->args[self::$testProjectIDParamName] ); + if ($status_ok) { + $keywordSet = $this->getKeywordSet( + $this->args[self::$testProjectIDParamName]); } - if($status_ok) { + if ($status_ok) { // Optional parameters $opt = array( - self::$importanceParamName => 2, - self::$executionTypeParamName => TESTCASE_EXECUTION_TYPE_MANUAL, - self::$orderParamName => testcase::DEFAULT_ORDER, - self::$internalIDParamName => testcase::AUTOMATIC_ID, - self::$checkDuplicatedNameParamName => testcase::DONT_CHECK_DUPLICATE_NAME, - self::$actionOnDuplicatedNameParamName => 'generate_new', - self::$preconditionsParamName => '', - self::$statusParamName => $wfStatusDomain['draft'], - self::$estimatedExecDurationParamName => null + self::$importanceParamName => 2, + self::$executionTypeParamName => TESTCASE_EXECUTION_TYPE_MANUAL, + self::$orderParamName => testcase::DEFAULT_ORDER, + self::$internalIDParamName => testcase::AUTOMATIC_ID, + self::$checkDuplicatedNameParamName => testcase::DONT_CHECK_DUPLICATE_NAME, + self::$actionOnDuplicatedNameParamName => 'generate_new', + self::$preconditionsParamName => '', + self::$statusParamName => $wfStatusDomain['draft'], + self::$estimatedExecDurationParamName => null ); - foreach( $opt as $key => $value ) { - if($this->_isParamPresent( $key )) { + foreach ($opt as $key => $value) { + if ($this->_isParamPresent($key)) { $opt[$key] = $this->args[$key]; } } } - if($status_ok) { + if ($status_ok) { $options = array( - 'check_duplicate_name' => $opt[self::$checkDuplicatedNameParamName], - 'action_on_duplicate_name' => $opt[self::$actionOnDuplicatedNameParamName], - 'status' => $opt[self::$statusParamName], - 'estimatedExecDuration' => $opt[self::$estimatedExecDurationParamName] + 'check_duplicate_name' => $opt[self::$checkDuplicatedNameParamName], + 'action_on_duplicate_name' => $opt[self::$actionOnDuplicatedNameParamName], + 'status' => $opt[self::$statusParamName], + 'estimatedExecDuration' => $opt[self::$estimatedExecDurationParamName] ); - $op_result = $this->tcaseMgr->create( $this->args[self::$testSuiteIDParamName], $this->args[self::$testCaseNameParamName], $this->args[self::$summaryParamName], $opt[self::$preconditionsParamName], $this->args[self::$stepsParamName], $author_id, $keywordSet, $opt[self::$orderParamName], $opt[self::$internalIDParamName], $opt[self::$executionTypeParamName], $opt[self::$importanceParamName], $options ); + $op_result = $this->tcaseMgr->create( + $this->args[self::$testSuiteIDParamName], + $this->args[self::$testCaseNameParamName], + $this->args[self::$summaryParamName], + $opt[self::$preconditionsParamName], + $this->args[self::$stepsParamName], $author_id, $keywordSet, + $opt[self::$orderParamName], $opt[self::$internalIDParamName], + $opt[self::$executionTypeParamName], + $opt[self::$importanceParamName], $options); $resultInfo = array(); $resultInfo[] = array( - "operation" => $operation, - "status" => true, - "id" => $op_result['id'], - "additionalInfo" => $op_result, - "message" => GENERAL_SUCCESS_STR + "operation" => $operation, + "status" => true, + "id" => $op_result['id'], + "additionalInfo" => $op_result, + "message" => GENERAL_SUCCESS_STR ); } - return($status_ok ? $resultInfo : $this->errors); + return $status_ok ? $resultInfo : $this->errors; } /** @@ -2290,15 +2941,17 @@ public function createTestCase($args) { * @param string $args["customfields"] * - optional * contains an map with key:Custom Field Name, value: value for CF. - * VERY IMPORTANT: value must be formatted in the way it's written to db, + * VERY IMPORTANT: value must be formatted in the way + * it's written to db, * this is important for types like: * * DATE: strtotime() * DATETIME: mktime() - * MULTISELECTION LIST / CHECKBOX / RADIO: se multipli selezione ! come separatore - * + * MULTISELECTION LIST / CHECKBOX / RADIO: + * character '!'' as separator when multiple values are allowed * - * these custom fields must be configured to be writte during execution. + * these custom fields must be configured to be writte during + * execution. * If custom field do not meet condition value will not be written * * @param boolean $args["overwrite"] @@ -2307,163 +2960,179 @@ public function createTestCase($args) { * * @param boolean $args["user"] * - optional, if present and user is a valid login - * (no other check will be done) it will be used when writting execution. + * (no other check will be done) it will be used when writting execution. * * @param string $args["timestamp"] * - optional, if not present now is used * format YYYY-MM-DD HH:MM:SS * example 2015-05-22 12:15:45 * @return mixed $resultInfo - * [status] => true/false of success - * [id] => result id or error code - * [message] => optional message for error message string + * [status] => true/false of success + * [id] => result id or error code + * [message] => optional message for error message string * @access public * * @internal revisions * */ - public function reportTCResult($args) { + public function reportTCResult($args) + { $resultInfo = array(); $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $this->errors = null; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo[0]["status"] = true; $checkFunctions = array( - 'authenticate', - 'checkTestCaseIdentity', - 'checkTestPlanID', - 'checkBuildID', - 'checkStatus' + 'authenticate', + 'checkTestCaseIdentity', + 'checkTestPlanID', + 'checkBuildID', + 'checkStatus' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { + if ($status_ok) { // This check is needed only if test plan has platforms - $platformSet = $this->tplanMgr->getPlatforms( $this->args[self::$testPlanIDParamName], array( + $platformSet = (array) $this->tplanMgr->getPlatforms( + $this->args[self::$testPlanIDParamName], + array( 'outputFormat' => 'map' - ) ); + )); $targetPlatform = null; - if(! is_null( $platformSet )) { - $status_ok = $this->checkPlatformIdentity( $this->args[self::$testPlanIDParamName], $platformSet, $msg_prefix ); - if($status_ok) { + if (! empty($platformSet)) { + $status_ok = $this->checkPlatformIdentity( + $this->args[self::$testPlanIDParamName], $platformSet, + $msg_prefix); + if ($status_ok) { $targetPlatform[$this->args[self::$platformIDParamName]] = $platformSet[$this->args[self::$platformIDParamName]]; } } - $status_ok = $status_ok && $this->_checkTCIDAndTPIDValid( $targetPlatform, $msg_prefix ); + $status_ok = $status_ok && + $this->_checkTCIDAndTPIDValid($targetPlatform, $msg_prefix); } $tester_id = null; - if($status_ok) { + if ($status_ok) { $this->errors = null; - if($this->_isParamPresent( self::$userParamName )) { - $tester_id = tlUser::doesUserExist( $this->dbObj, $this->args[self::$userParamName] ); - if(!($status_ok = ! is_null( $tester_id ))) { - $msg = $msg_prefix . sprintf( NO_USER_BY_THIS_LOGIN_STR, $this->args[self::$userParamName] ); - $this->errors[] = new IXR_Error( NO_USER_BY_THIS_LOGIN, $msg ); + if ($this->_isParamPresent(self::$userParamName)) { + $tester_id = tlUser::doesUserExist($this->dbObj, + $this->args[self::$userParamName]); + if (! ($status_ok = ! is_null($tester_id))) { + $msg = $msg_prefix . + sprintf(NO_USER_BY_THIS_LOGIN_STR, + $this->args[self::$userParamName]); + $this->errors[] = new IXR_Error(NO_USER_BY_THIS_LOGIN, $msg); } } } $exec_ts = null; - if($status_ok) { - if($this->_isParamPresent( self::$timeStampParamName )) { - // Now check if is a valid one - $exec_ts = $this->args[self::$timeStampParamName]; - - try { - checkTimeStamp( $exec_ts ); - $exec_ts = "'{$exec_ts}'"; - } catch( Exception $e ) { - $status_ok = false; - $this->errors = null; - $msg = $msg_prefix . sprintf( INVALID_TIMESTAMP_STR, $exec_ts ); - $this->errors[] = new IXR_Error( INVALID_TIMESTAMP, $msg ); - } + if ($status_ok && $this->_isParamPresent(self::$timeStampParamName)) { + // Now check if is a valid one + $exec_ts = $this->args[self::$timeStampParamName]; + + try { + checkTimeStamp($exec_ts); + $exec_ts = "'{$exec_ts}'"; + } catch (Exception $e) { + $status_ok = false; + $this->errors = null; + $msg = $msg_prefix . sprintf(INVALID_TIMESTAMP_STR, $exec_ts); + $this->errors[] = new IXR_Error(INVALID_TIMESTAMP, $msg); } } - if($status_ok && $this->userHasRight( "testplan_execute", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + if ($status_ok && + $this->userHasRight("testplan_execute", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { $executionID = 0; $resultInfo[0]["operation"] = $operation; $resultInfo[0]["overwrite"] = false; $resultInfo[0]["status"] = true; $resultInfo[0]["message"] = GENERAL_SUCCESS_STR; - $doOverwrite = $this->_isParamPresent( self::$overwriteParamName ) && $this->args[self::$overwriteParamName]; + $doOverwrite = $this->_isParamPresent(self::$overwriteParamName) && + $this->args[self::$overwriteParamName]; - if($doOverwrite) { + if ($doOverwrite) { $resultInfo[0]["overwrite"] = true; - $executionID = $this->_updateResult( $tester_id, $exec_ts ); + $executionID = $this->_updateResult($tester_id, $exec_ts); - if($executionID != 0 && $this->_isParamPresent( self::$stepsParamName )) { + if ($executionID != 0 && + $this->_isParamPresent(self::$stepsParamName)) { $resultInfo[0]["steps"] = 'yes!'; - $resultInfo[0]["steps_sql"] = $this->_updateStepsResult( $executionID ); + $resultInfo[0]["steps_sql"] = $this->_updateStepsResult( + $executionID); } } - if($executionID == 0) { - $executionID = $this->_insertResultToDB( $tester_id, $exec_ts ); + if ($executionID == 0) { + $executionID = $this->_insertResultToDB($tester_id, $exec_ts); } $resultInfo[0]["id"] = $executionID; // Do we need to insert a bug ? - if($this->_isParamPresent( self::$bugIDParamName )) { + if ($this->_isParamPresent(self::$bugIDParamName)) { $bugID = $this->args[self::$bugIDParamName]; - $resultInfo[0]["bugidstatus"] = $this->_insertExecutionBug( $executionID, $bugID ); + $resultInfo[0]["bugidstatus"] = $this->_insertExecutionBug( + $executionID, $bugID); } - if($this->_isParamPresent( self::$customFieldsParamName )) { - $resultInfo[0]["customfieldstatus"] = $this->_insertCustomFieldExecValues( $executionID ); + if ($this->_isParamPresent(self::$customFieldsParamName)) { + $resultInfo[0]["customfieldstatus"] = $this->_insertCustomFieldExecValues( + $executionID); } - // - if($executionID > 0 && ! $resultInfo[0]["overwrite"]) { - // Get steps info - // step number, result, notes - if($this->_isParamPresent( self::$stepsParamName )) { - $resultInfo[0]["steps"] = 'yes!'; + // Get steps info + // step number, result, notes + if ($executionID > 0 && ! $resultInfo[0]["overwrite"] && + $this->_isParamPresent(self::$stepsParamName)) { + $resultInfo[0]["steps"] = 'yes!'; - $st = &$this->args[self::$stepsParamName]; - foreach( $st as $sp ) { - $nst[$sp['step_number']] = $sp; - } + $st = &$this->args[self::$stepsParamName]; + foreach ($st as $sp) { + $nst[$sp['step_number']] = $sp; + } - $r2d2 = array( - 'fields2get' => 'TCSTEPS.step_number,TCSTEPS.id', - 'accessKey' => 'step_number', - 'renderGhostSteps' => false, - 'renderImageInline' => false - ); + $r2d2 = array( + 'fields2get' => 'TCSTEPS.step_number,TCSTEPS.id', + 'accessKey' => 'step_number', + 'renderGhostSteps' => false, + 'renderImageInline' => false + ); - $steps = $this->tcaseMgr->getStepsSimple( $this->tcVersionID, 0, $r2d2 ); + $steps = $this->tcaseMgr->getStepsSimple($this->tcVersionID, 0, + $r2d2); - $target = DB_TABLE_PREFIX . 'execution_tcsteps'; - $resultsCfg = config_get( 'results' ); + $target = DB_TABLE_PREFIX . 'execution_tcsteps'; + $resultsCfg = config_get('results'); - foreach( $nst as $spnum => $spdata ) { - // check if step exists, if not ignore - if(isset( $steps[$spnum] )) { - // if result is not on domain, write it - // anyway. - $status = strtolower( trim( $spdata['result'] ) ); - $status = $status[0]; + foreach ($nst as $spnum => $spdata) { + // check if step exists, if not ignore + if (isset($steps[$spnum])) { + // if result is not on domain, write it + // anyway. + $status = strtolower(trim($spdata['result'])); + $status = $status[0]; - $sql = " INSERT INTO {$target}(execution_id,tcstep_id,notes"; - $sql .= ",status"; + $sql = " INSERT INTO {$target}(execution_id,tcstep_id,notes"; + $sql .= ",status"; - $values = " VALUES( {$executionID}, {$steps[$spnum]['id']}," . "'" . $this->dbObj->prepare_string( $spdata['notes'] ) . "'"; - $values .= ",'" . $this->dbObj->prepare_string( $status ) . "'"; - $sql .= ") " . $values . ")"; + $values = " VALUES( {$executionID}, {$steps[$spnum]['id']}," . + "'" . $this->dbObj->prepare_string($spdata['notes']) . + "'"; + $values .= ",'" . $this->dbObj->prepare_string($status) . + "'"; + $sql .= ") " . $values . ")"; - if($status != $resultsCfg['status_code']['not_run']) { - $this->dbObj->exec_query( $sql ); - } + if ($status != $resultsCfg['status_code']['not_run']) { + $this->dbObj->exec_query($sql); } } } @@ -2484,11 +3153,12 @@ public function reportTCResult($args) { * @return boolean * @access protected */ - public function setTestMode($args) { - $this->_setArgs( $args ); + public function setTestMode($args) + { + $this->_setArgs($args); - if(! $this->_isTestModePresent()) { - $this->errors[] = new IXR_ERROR( NO_TEST_MODE, NO_TEST_MODE_STR ); + if (! $this->_isTestModePresent()) { + $this->errors[] = new IXR_ERROR(NO_TEST_MODE, NO_TEST_MODE_STR); return false; } else { // TODO: should probably validate that this is a bool or t/f string @@ -2514,42 +3184,51 @@ public function setTestMode($args) { * @return boolean * @access protected */ - protected function checkTestCaseIdentity($messagePrefix = '') { + protected function checkTestCaseIdentity($messagePrefix = '') + { // Three Cases - Internal ID, External ID, No Id $status = false; $tcaseID = 0; - $my_errors = array(); $fromExternal = false; $fromInternal = false; - if($this->_isTestCaseIDPresent()) { + if ($this->_isTestCaseIDPresent()) { $fromInternal = true; - $status =(($tcaseID = intval( $this->args[self::$testCaseIDParamName] )) > 0); + $status = (($tcaseID = intval( + $this->args[self::$testCaseIDParamName])) > 0); - if(! $status) { - $this->errors[] = new IXR_Error( $tcaseID, sprintf( $messagePrefix . INVALID_TCASEID_STR, $tcaseID ) ); + if (! $status) { + $this->errors[] = new IXR_Error($tcaseID, + sprintf($messagePrefix . INVALID_TCASEID_STR, $tcaseID)); } - } elseif($this->_isTestCaseExternalIDPresent()) { + } elseif ($this->_isTestCaseExternalIDPresent()) { $fromExternal = true; $tcaseExternalID = $this->args[self::$testCaseExternalIDParamName]; - $tcaseID = intval( $this->tcaseMgr->getInternalID( $tcaseExternalID ) ); + $tcaseID = intval($this->tcaseMgr->getInternalID($tcaseExternalID)); $status = $tcaseID > 0 ? true : false; // Invalid TestCase ID - if(! $status) { - $this->errors[] = new IXR_Error( INVALID_TESTCASE_EXTERNAL_ID, sprintf( $messagePrefix . INVALID_TESTCASE_EXTERNAL_ID_STR, $tcaseExternalID ) ); + if (! $status) { + $this->errors[] = new IXR_Error(INVALID_TESTCASE_EXTERNAL_ID, + sprintf($messagePrefix . INVALID_TESTCASE_EXTERNAL_ID_STR, + $tcaseExternalID)); } } - if($status) { - if($this->_isTestCaseIDValid( $tcaseID, $messagePrefix )) { - $this->_setTestCaseID( $tcaseID ); + if ($status) { + if ($this->_isTestCaseIDValid($tcaseID, $messagePrefix)) { + $this->_setTestCaseID($tcaseID); } else { $status = false; - if($fromInternal) { - $this->errors[] = new IXR_Error( INVALID_TCASEID, sprintf( $messagePrefix . INVALID_TCASEID_STR, $tcaseID ) ); - } elseif($fromExternal) { - $this->errors[] = new IXR_Error( INVALID_TESTCASE_EXTERNAL_ID, sprintf( $messagePrefix . INVALID_TESTCASE_EXTERNAL_ID_STR, $tcaseExternalID ) ); + if ($fromInternal) { + $this->errors[] = new IXR_Error(INVALID_TCASEID, + sprintf($messagePrefix . INVALID_TCASEID_STR, $tcaseID)); + } elseif ($fromExternal) { + $this->errors[] = new IXR_Error( + INVALID_TESTCASE_EXTERNAL_ID, + sprintf( + $messagePrefix . INVALID_TESTCASE_EXTERNAL_ID_STR, + $tcaseExternalID)); } } } @@ -2591,99 +3270,101 @@ protected function checkTestCaseIdentity($messagePrefix = '') { * 'simple': * 'details': * @param array $args["customfields"] - * - optional can be a boolean or an array with the requested fields + * - optional can be a boolean or an array with the requested fields * @return mixed $resultInfo * * @internal revisions * @since 1.9.13 * 20141230 - franciscom - TICKET 6805: platform parameter */ - public function getTestCasesForTestPlan($args) { + public function getTestCasesForTestPlan($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; // Optional parameters that are not mutual exclusive, // DEFAULT value to use if parameter was not provided $opt = array( - self::$testCaseIDParamName => null, - self::$buildIDParamName => null, - self::$keywordIDParamName => null, - self::$executedParamName => null, - self::$assignedToParamName => null, - self::$executeStatusParamName => null, - self::$executionTypeParamName => null, - self::$getStepsInfoParamName => false, - self::$customFieldsParamName => false, - self::$detailsParamName => 'full', - self::$platformIDParamName => null + self::$testCaseIDParamName => null, + self::$buildIDParamName => null, + self::$keywordIDParamName => null, + self::$executedParamName => null, + self::$assignedToParamName => null, + self::$executeStatusParamName => null, + self::$executionTypeParamName => null, + self::$getStepsInfoParamName => false, + self::$customFieldsParamName => false, + self::$detailsParamName => 'full', + self::$platformIDParamName => null ); - $optMutualExclusive = array( - self::$keywordIDParamName => null, - self::$keywordNameParamName => null - ); - $this->_setArgs( $args ); - if(!($this->_checkGetTestCasesForTestPlanRequest( $msg_prefix ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ))) { + $this->_setArgs($args); + if (! ($this->_checkGetTestCasesForTestPlanRequest($msg_prefix) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR))) { return $this->errors; } $tplanid = $this->args[self::$testPlanIDParamName]; - $tplanInfo = $this->tplanMgr->tree_manager->get_node_hierarchy_info( $tplanid ); + $tplanInfo = $this->tplanMgr->tree_manager->get_node_hierarchy_info( + $tplanid); - foreach( $opt as $key => $value ) { - if($this->_isParamPresent( $key )) { + foreach ($opt as $key => $value) { + if ($this->_isParamPresent($key)) { $opt[$key] = $this->args[$key]; } } $keywordSet = $opt[self::$keywordIDParamName]; - if(is_null( $keywordSet )) { + if (is_null($keywordSet)) { $keywordSet = null; - $keywordList = $this->getKeywordSet( $tplanInfo['parent_id'] ); - if(! is_null( $keywordList )) { - $keywordSet = explode( ",", $keywordList ); + $keywordList = $this->getKeywordSet($tplanInfo['parent_id']); + if (! is_null($keywordList)) { + $keywordSet = explode(",", $keywordList); } } $options = array( - 'executed_only' => $opt[self::$executedParamName], - 'details' => $opt[self::$detailsParamName], - 'output' => 'mapOfMap' + 'executed_only' => $opt[self::$executedParamName], + 'details' => $opt[self::$detailsParamName], + 'output' => 'mapOfMap' ); $filters = array( - 'tcase_id' => $opt[self::$testCaseIDParamName], - 'keyword_id' => $keywordSet, - 'assigned_to' => $opt[self::$assignedToParamName], - 'exec_status' => $opt[self::$executeStatusParamName], - 'build_id' => $opt[self::$buildIDParamName], - 'exec_type' => $opt[self::$executionTypeParamName], - 'platform_id' => $opt[self::$platformIDParamName] + 'tcase_id' => $opt[self::$testCaseIDParamName], + 'keyword_id' => $keywordSet, + 'assigned_to' => $opt[self::$assignedToParamName], + 'exec_status' => $opt[self::$executeStatusParamName], + 'build_id' => $opt[self::$buildIDParamName], + 'exec_type' => $opt[self::$executionTypeParamName], + 'platform_id' => $opt[self::$platformIDParamName] ); - $recordset = $this->tplanMgr->getLTCVNewGeneration( $tplanid, $filters, $options ); + $recordset = $this->tplanMgr->getLTCVNewGeneration($tplanid, $filters, + $options); // Do we need to get Test Case Steps? - if(! is_null( $recordset ) && $opt[self::$getStepsInfoParamName]) { - $itemSet = array_keys( $recordset ); - switch($options['output']) { - case 'mapOfArray' : - case 'mapOfMap' : - foreach( $itemSet as $itemKey ) { - $keySet = array_keys( $recordset[$itemKey] ); + if (! is_null($recordset) && $opt[self::$getStepsInfoParamName]) { + $itemSet = array_keys($recordset); + switch ($options['output']) { + case 'mapOfArray': + case 'mapOfMap': + foreach ($itemSet as $itemKey) { + $keySet = array_keys($recordset[$itemKey]); $target = &$recordset[$itemKey]; - foreach( $keySet as $accessKey ) { - $steps = $this->tcaseMgr->get_steps( $target[$accessKey]['tcversion_id'] ); + foreach ($keySet as $accessKey) { + $steps = $this->tcaseMgr->get_steps( + $target[$accessKey]['tcversion_id']); $target[$accessKey]['steps'] = $steps; } } break; - case 'array' : - case 'map' : - default : - foreach( $itemSet as $accessKey ) { - $sts = $this->tcaseMgr->get_steps( $recordset[$accessKey]['tcversion_id'] ); + case 'array': + case 'map': + default: + foreach ($itemSet as $accessKey) { + $sts = $this->tcaseMgr->get_steps( + $recordset[$accessKey]['tcversion_id']); $recordset[$accessKey]['steps'] = $sts; } break; @@ -2691,27 +3372,31 @@ public function getTestCasesForTestPlan($args) { } // Do we need the custom fields? - if (! is_null( $recordset ) && ($opt[self::$customFieldsParamName] || is_array($opt[self::$customFieldsParamName]))) { - $itemSet = array_keys( $recordset ); - switch($options['output']) { - case 'mapOfArray' : - case 'mapOfMap' : - foreach( $itemSet as $itemKey ) { - $keySet = array_keys( $recordset[$itemKey] ); + if (! is_null($recordset) && + ($opt[self::$customFieldsParamName] || + is_array($opt[self::$customFieldsParamName]))) { + $itemSet = array_keys($recordset); + switch ($options['output']) { + case 'mapOfArray': + case 'mapOfMap': + foreach ($itemSet as $itemKey) { + $keySet = array_keys($recordset[$itemKey]); $target = &$recordset[$itemKey]; - foreach( $keySet as $accessKey ) { + foreach ($keySet as $accessKey) { $target[$accessKey]['customfields'] = $this->_testcaseCustomFieldData( - $target[$accessKey], $tplanInfo['parent_id'], $opt[self::$customFieldsParamName]); + $target[$accessKey], $tplanInfo['parent_id'], + $opt[self::$customFieldsParamName]); } } break; - case 'array' : - case 'map' : - default : - foreach( $itemSet as $accessKey ) { + case 'array': + case 'map': + default: + foreach ($itemSet as $accessKey) { $recordset[$accessKey]['customfields'] = $this->_testcaseCustomFieldData( - $recordset[$accessKey], $tplanInfo['parent_id'], $opt[self::$customFieldsParamName]); + $recordset[$accessKey], $tplanInfo['parent_id'], + $opt[self::$customFieldsParamName]); } break; } @@ -2727,31 +3412,33 @@ public function getTestCasesForTestPlan($args) { * custom field names as $cf_options. * * @param mixed $testcase - * The testcase to query for. + * The testcase to query for. * @param int $project_id - * The project to query for. + * The project to query for. * @param array $cf_options - * The optional name filter. + * The optional name filter. * @return array * @access private */ - private function _testcaseCustomFieldData(&$testcase, $project_id, &$cf_options) { + private function _testcaseCustomFieldData(&$testcase, $project_id, + &$cf_options) + { $cf = $this->tcaseMgr->get_linked_cfields_at_design( - $testcase['tcase_id'], $testcase['tcversion_id'], - null, null, $project_id); + $testcase['tcase_id'], $testcase['tcversion_id'], null, null, + $project_id); $filtered_cf = array(); $cfieds_selected = is_array($cf_options); - foreach ($cf as $cf_id => $cfield) { - if ($cfieds_selected && !in_array($cfield['name'], $cf_options)) { + foreach ($cf as $cfield) { + if ($cfieds_selected && ! in_array($cfield['name'], $cf_options)) { continue; } $filtered_cf[$cfield['name']] = array( - 'name' => $cfield['id']['name'], - 'label' => $cfield['id']['label'], - 'type' => $cfield['id']['type'], - 'value' => $cfield['id']['value'] + 'name' => $cfield['id']['name'], + 'label' => $cfield['id']['label'], + 'type' => $cfield['id']['type'], + 'value' => $cfield['id']['value'] ); } @@ -2768,16 +3455,17 @@ private function _testcaseCustomFieldData(&$testcase, $project_id, &$cf_options) * @return boolean * @access protected */ - protected function _checkGetTestCasesForTestPlanRequest($messagePrefix = '') { + protected function _checkGetTestCasesForTestPlanRequest($messagePrefix = '') + { $status = $this->authenticate(); - if($status) { - $status &= $this->checkTestPlanID( $messagePrefix ); + if ($status) { + $status &= $this->checkTestPlanID($messagePrefix); - if($status && $this->_isTestCaseIDPresent( $messagePrefix )) { - $status &= $this->_checkTCIDAndTPIDValid( null, $messagePrefix ); + if ($status && $this->_isTestCaseIDPresent($messagePrefix)) { + $status &= $this->_checkTCIDAndTPIDValid(null, $messagePrefix); } - if($status && $this->_isBuildIDPresent( $messagePrefix )) { - $status &= $this->checkBuildID( $messagePrefix ); + if ($status && $this->_isBuildIDPresent($messagePrefix)) { + $status &= $this->checkBuildID($messagePrefix); } } return $status; @@ -2808,41 +3496,46 @@ protected function _checkGetTestCasesForTestPlanRequest($messagePrefix = '') { * * @access public */ - public function getTestCaseCustomFieldDesignValue($args) { + public function getTestCaseCustomFieldDesignValue($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkTestCaseIdentity', - 'checkTestCaseVersionNumber' + 'authenticate', + 'checkTestProjectID', + 'checkTestCaseIdentity', + 'checkTestCaseVersionNumber' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { - $status_ok = $this->_isParamPresent( self::$customFieldNameParamName, $msg_prefix, self::SET_ERROR ); + if ($status_ok) { + $status_ok = $this->_isParamPresent(self::$customFieldNameParamName, + $msg_prefix, self::SET_ERROR); } - if($status_ok) { + if ($status_ok) { $ret = $this->checkTestCaseAncestry(); $status_ok = $ret['status_ok']; - if($status_ok) { + if ($status_ok) { // Check if version number exists for Test Case $ret = $this->checkTestCaseVersionNumberAncestry(); $status_ok = $ret['status_ok']; } - if($status_ok) { - $status_ok = $this->_checkGetTestCaseCustomFieldDesignValueRequest( $msg_prefix ); + if ($status_ok) { + $status_ok = $this->_checkGetTestCaseCustomFieldDesignValueRequest( + $msg_prefix); } else { - $this->errors[] = new IXR_Error( $ret['error_code'], $msg_prefix . $ret['error_msg'] ); + $this->errors[] = new IXR_Error($ret['error_code'], + $msg_prefix . $ret['error_msg']); } } - if($status_ok && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + if ($status_ok && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR)) { $details = 'value'; - if($this->_isParamPresent( self::$detailsParamName )) { + if ($this->_isParamPresent(self::$detailsParamName)) { $details = $this->args[self::$detailsParamName]; } @@ -2851,29 +3544,30 @@ public function getTestCaseCustomFieldDesignValue($args) { $tcase_id = $this->args[self::$testCaseIDParamName]; $cfield_mgr = $this->tprojectMgr->cfield_mgr; - $cfinfo = $cfield_mgr->get_by_name( $cf_name ); - $cfield = current( $cfinfo ); + $cfinfo = $cfield_mgr->get_by_name($cf_name); + $cfield = current($cfinfo); $filters = array( - 'cfield_id' => $cfield['id'] + 'cfield_id' => $cfield['id'] ); - $cfieldSpec = $this->tcaseMgr->get_linked_cfields_at_design( $tcase_id, $this->tcVersionID, null, $filters, $tproject_id ); + $cfieldSpec = $this->tcaseMgr->get_linked_cfields_at_design( + $tcase_id, $this->tcVersionID, null, $filters, $tproject_id); - switch($details) { - case 'full' : + switch ($details) { + case 'full': $retval = $cfieldSpec[$cfield['id']]; break; - case 'simple' : + case 'simple': $retval = array( - 'name' => $cf_name, - 'label' => $cfieldSpec[$cfield['id']]['label'], - 'type' => $cfieldSpec[$cfield['id']]['type'], - 'value' => $cfieldSpec[$cfield['id']]['value'] + 'name' => $cf_name, + 'label' => $cfieldSpec[$cfield['id']]['label'], + 'type' => $cfieldSpec[$cfield['id']]['type'], + 'value' => $cfieldSpec[$cfield['id']]['value'] ); break; - case 'value' : - default : + case 'value': + default: $retval = $cfieldSpec[$cfield['id']]['value']; break; } @@ -2898,8 +3592,9 @@ public function getTestCaseCustomFieldDesignValue($args) { * @return boolean * @access protected */ - protected function _checkGetTestCaseCustomFieldDesignValueRequest($messagePrefix = '') { - // $status_ok=$this->authenticate($messagePrefix); + protected function _checkGetTestCaseCustomFieldDesignValueRequest( + $messagePrefix = '') + { $cf_name = $this->args[self::$customFieldNameParamName]; // $testCaseIDParamName = "testcaseid"; @@ -2914,39 +3609,50 @@ protected function _checkGetTestCaseCustomFieldDesignValueRequest($messagePrefix // - Custom Field exists ? $cfield_mgr = $this->tprojectMgr->cfield_mgr; - $cfinfo = $cfield_mgr->get_by_name( $cf_name ); - if(!($status_ok = ! is_null( $cfinfo ))) { - $msg = sprintf( NO_CUSTOMFIELD_BY_THIS_NAME_STR, $cf_name ); - $this->errors[] = new IXR_Error( NO_CUSTOMFIELD_BY_THIS_NAME, $messagePrefix . $msg ); + $cfinfo = $cfield_mgr->get_by_name($cf_name); + if (! ($status_ok = ! is_null($cfinfo))) { + $msg = sprintf(NO_CUSTOMFIELD_BY_THIS_NAME_STR, $cf_name); + $this->errors[] = new IXR_Error(NO_CUSTOMFIELD_BY_THIS_NAME, + $messagePrefix . $msg); } // - Can be used on a test case ? - if($status_ok) { - $cfield = current( $cfinfo ); - $status_ok =(strcasecmp( $cfield['node_type'], 'testcase' ) == 0); - if(! $status_ok) { - $msg = sprintf( CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE_STR, $cf_name, 'testcase', $cfield['node_type'] ); - $this->errors[] = new IXR_Error( CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE, $messagePrefix . $msg ); + if ($status_ok) { + $cfield = current($cfinfo); + $status_ok = (strcasecmp($cfield['node_type'], 'testcase') == 0); + if (! $status_ok) { + $msg = sprintf(CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE_STR, $cf_name, + 'testcase', $cfield['node_type']); + $this->errors[] = new IXR_Error( + CUSTOMFIELD_NOT_APP_FOR_NODE_TYPE, $messagePrefix . $msg); } } // - Custom Field scope includes 'design' ? - if($status_ok) { - $status_ok =($cfield['show_on_design'] || $cfield['enable_on_design']); - if(! $status_ok) { - $msg = sprintf( CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE_STR, $cf_name ); - $this->errors[] = new IXR_Error( CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE, $messagePrefix . $msg ); + if ($status_ok) { + $status_ok = ($cfield['show_on_design'] || + $cfield['enable_on_design']); + if (! $status_ok) { + $msg = sprintf(CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE_STR, $cf_name); + $this->errors[] = new IXR_Error( + CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE, $messagePrefix . $msg); } } // - is linked to testproject that owns test case ? - if($status_ok) { - $allCF = $cfield_mgr->get_linked_to_testproject( $this->args[self::$testProjectIDParamName] ); - $status_ok = ! is_null( $allCF ) && isset( $allCF[$cfield['id']] ); - if(! $status_ok) { - $tproject_info = $this->tprojectMgr->get_by_id( $this->args[self::$testProjectIDParamName] ); - $msg = sprintf( CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT_STR, $cf_name, $tproject_info['name'], $this->args[self::$testProjectIDParamName] ); - $this->errors[] = new IXR_Error( CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT, $messagePrefix . $msg ); + if ($status_ok) { + $allCF = $cfield_mgr->get_linked_to_testproject( + $this->args[self::$testProjectIDParamName]); + $status_ok = ! is_null($allCF) && isset($allCF[$cfield['id']]); + if (! $status_ok) { + $tproject_info = $this->tprojectMgr->get_by_id( + $this->args[self::$testProjectIDParamName]); + $msg = sprintf(CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT_STR, + $cf_name, $tproject_info['name'], + $this->args[self::$testProjectIDParamName]); + $this->errors[] = new IXR_Error( + CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT, + $messagePrefix . $msg); } } @@ -2963,20 +3669,21 @@ protected function _checkGetTestCaseCustomFieldDesignValueRequest($messagePrefix * * @access protected */ - protected function getKeywordSet($tproject_id) { + protected function getKeywordSet($tproject_id) + { $keywordSet = null; $kMethod = null; - if($this->_isParamPresent( self::$keywordNameParamName )) { + if ($this->_isParamPresent(self::$keywordNameParamName)) { $kMethod = 'getValidKeywordSetByName'; $accessKey = self::$keywordNameParamName; - } else if($this->_isParamPresent( self::$keywordIDParamName )) { + } elseif ($this->_isParamPresent(self::$keywordIDParamName)) { $kMethod = 'getValidKeywordSetById'; $accessKey = self::$keywordIDParamName; } - if(! is_null( $kMethod )) { - $keywordSet = $this->$kMethod( $tproject_id, $this->args[$accessKey] ); + if (! is_null($kMethod)) { + $keywordSet = $this->$kMethod($tproject_id, $this->args[$accessKey]); } return $keywordSet; @@ -2993,8 +3700,9 @@ protected function getKeywordSet($tproject_id) { * * @access protected */ - protected function getValidKeywordSetByName($tproject_id, $keywords) { - return $this->getValidKeywordSet( $tproject_id, $keywords, true ); + protected function getValidKeywordSetByName($tproject_id, $keywords) + { + return $this->getValidKeywordSet($tproject_id, $keywords, true); } /** @@ -3007,41 +3715,44 @@ protected function getValidKeywordSetByName($tproject_id, $keywords) { * this to true if $keywords is an array of keywords, false if it's an array of keywordIDs * @return string that represent a list of keyword id(comma is character separator) */ - protected function getValidKeywordSet($tproject_id, $keywords, $byName, $op = null) { + protected function getValidKeywordSet($tproject_id, $keywords, $byName, + $op = null) + { $keywordSet = array(); - $sql = " SELECT keyword,id FROM {$this->tables['keywords']} " . " WHERE testproject_id = {$tproject_id} "; + $sql = " SELECT keyword,id FROM {$this->tables['keywords']} " . + " WHERE testproject_id = {$tproject_id} "; - $keywords = trim( $keywords ); - if($keywords != "") { - $a_keywords = explode( ",", $keywords ); - $items_qty = count( $a_keywords ); - for($idx = 0; $idx < $items_qty; $idx ++) { - $a_keywords[$idx] = trim( $a_keywords[$idx] ); + $keywords = trim($keywords); + if ($keywords != "") { + $a_keywords = explode(",", $keywords); + $items_qty = count($a_keywords); + for ($idx = 0; $idx < $items_qty; $idx ++) { + $a_keywords[$idx] = trim($a_keywords[$idx]); } - $itemSet = implode( "','", $a_keywords ); + $itemSet = implode("','", $a_keywords); - if($byName) { + if ($byName) { $sql .= " AND keyword IN('{$itemSet}')"; } else { $sql .= " AND id IN({$itemSet})"; } } - $keywordMap = $this->dbObj->fetchRowsIntoMap( $sql, 'keyword' ); - if(! is_null( $keywordMap )) { - if(is_null( $op )) { + $keywordMap = $this->dbObj->fetchRowsIntoMap($sql, 'keyword'); + if (! is_null($keywordMap)) { + if (is_null($op)) { $a_items = null; - for($idx = 0; $idx < $items_qty; $idx ++) { - if(isset( $keywordMap[$a_keywords[$idx]] )) { + for ($idx = 0; $idx < $items_qty; $idx ++) { + if (isset($keywordMap[$a_keywords[$idx]])) { $a_items[] = $keywordMap[$a_keywords[$idx]]['id']; } } - if(! is_null( $a_items )) { - $keywordSet = implode( ",", $a_items ); + if (! is_null($a_items)) { + $keywordSet = implode(",", $a_items); } } else { - foreach( $keywordMap as $kw => $elem ) { + foreach ($keywordMap as $elem) { $keywordSet[$elem['id']] = $elem['keyword']; } } @@ -3061,37 +3772,60 @@ protected function getValidKeywordSet($tproject_id, $keywords, $byName, $op = nu * * @access protected */ - protected function getValidKeywordSetById($tproject_id, $keywords) { - return $this->getValidKeywordSet( $tproject_id, $keywords, false ); + protected function getValidKeywordSetById($tproject_id, $keywords) + { + return $this->getValidKeywordSet($tproject_id, $keywords, false); } /** - * checks if test case version number is positive integer + * Checks a version number + * + * - is present + * - is integer + * - is strictly positive * * @return boolean * * @access protected */ - protected function checkTestCaseVersionNumber() { + protected function checkVersionNumber() + { $status = true; - if(!($status = $this->_isParamPresent( self::$versionNumberParamName ))) { - $msg = sprintf( MISSING_REQUIRED_PARAMETER_STR, self::$versionNumberParamName ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); + if (! ($status = $this->_isParamPresent(self::$versionNumberParamName))) { + $msg = sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$versionNumberParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } else { $version = $this->args[self::$versionNumberParamName]; - if(!($status = is_int( $version ))) { - $msg = sprintf( PARAMETER_NOT_INT_STR, self::$versionNumberParamName, $version ); - $this->errors[] = new IXR_Error( PARAMETER_NOT_INT, $msg ); + if (! ($status = is_int($version))) { + $msg = sprintf(PARAMETER_NOT_INT_STR, + self::$versionNumberParamName, $version); + $this->errors[] = new IXR_Error(PARAMETER_NOT_INT, $msg); } else { - if(!($status =($version > 0))) { - $msg = sprintf( VERSION_NOT_VALID_STR, $version ); - $this->errors[] = new IXR_Error( VERSION_NOT_VALID, $msg ); + if (! ($status = ($version > 0))) { + $msg = sprintf(VERSION_NOT_VALID_STR, $version); + $this->errors[] = new IXR_Error(VERSION_NOT_VALID, $msg); } } } return $status; } + /** + * checks the validity of a Test Case version number + * + * This is the same thing as checkVersionNumber. + * Kept for backward compatibility. + * + * @return boolean + * + * @access protected + */ + protected function checkTestCaseVersionNumber() + { + return $this->checkVersionNumber(); + } + /** * Add a test case version to a test plan * @@ -3113,10 +3847,11 @@ protected function checkTestCaseVersionNumber() { * args['overwrite'] - OPTIONAL * */ - public function addTestCaseToTestPlan($args) { + public function addTestCaseToTestPlan($args) + { $operation = __FUNCTION__; $messagePrefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $op_result = null; $additional_fields = ''; @@ -3126,76 +3861,85 @@ public function addTestCaseToTestPlan($args) { $hasPlatformIDArgs = false; $platform_id = 0; $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkTestCaseVersionNumber', - 'checkTestCaseIdentity', - 'checkTestPlanID' + 'authenticate', + 'checkTestProjectID', + 'checkTestCaseVersionNumber', + 'checkTestCaseIdentity', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); // Test Plan belongs to test project ? - if($status_ok) { + if ($status_ok) { $tproject_id = $this->args[self::$testProjectIDParamName]; $tplan_id = $this->args[self::$testPlanIDParamName]; - $tplan_info = $this->tplanMgr->get_by_id( $tplan_id ); + $tplan_info = $this->tplanMgr->get_by_id($tplan_id); - $sql = " SELECT id FROM {$this->tables['testplans']}" . " WHERE testproject_id={$tproject_id} AND id = {$tplan_id}"; + $sql = " SELECT id FROM {$this->tables['testplans']}" . + " WHERE testproject_id={$tproject_id} AND id = {$tplan_id}"; - $rs = $this->dbObj->get_recordset( $sql ); + $rs = $this->dbObj->get_recordset($sql); - if(count( $rs ) != 1) { + if (count($rs) != 1) { $status_ok = false; - $tproject_info = $this->tprojectMgr->get_by_id( $tproject_id ); - $msg = sprintf( TPLAN_TPROJECT_KO_STR, $tplan_info['name'], $tplan_id, $tproject_info['name'], $tproject_id ); - $this->errors[] = new IXR_Error( TPLAN_TPROJECT_KO, $msg_prefix . $msg ); + $tproject_info = $this->tprojectMgr->get_by_id($tproject_id); + $msg = sprintf(TPLAN_TPROJECT_KO_STR, $tplan_info['name'], + $tplan_id, $tproject_info['name'], $tproject_id); + $this->errors[] = new IXR_Error(TPLAN_TPROJECT_KO, + $msg_prefix . $msg); } } // Test Case belongs to test project ? - if($status_ok) { + if ($status_ok) { $ret = $this->checkTestCaseAncestry(); $status_ok = $ret['status_ok']; - if(! $ret['status_ok']) { - $this->errors[] = new IXR_Error( $ret['error_code'], $msg_prefix . $ret['error_msg'] ); + if (! $ret['status_ok']) { + $this->errors[] = new IXR_Error($ret['error_code'], + $msg_prefix . $ret['error_msg']); } } // Does this Version number exist for this test case ? - if($status_ok) { + if ($status_ok) { $tcase_id = $this->args[self::$testCaseIDParamName]; $version_number = $this->args[self::$versionNumberParamName]; - $sql = " SELECT TCV.version,TCV.id " . " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['tcversions']} TCV " . " WHERE NH.parent_id = {$tcase_id} " . " AND TCV.version = {$version_number} " . " AND TCV.id = NH.id "; + $sql = " SELECT TCV.version,TCV.id " . + " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['tcversions']} TCV " . + " WHERE NH.parent_id = {$tcase_id} " . + " AND TCV.version = {$version_number} " . " AND TCV.id = NH.id "; - $target_tcversion = $this->dbObj->fetchRowsIntoMap( $sql, 'version' ); - if(! is_null( $target_tcversion ) && count( $target_tcversion ) != 1) { + $target_tcversion = $this->dbObj->fetchRowsIntoMap($sql, 'version'); + if (is_null($target_tcversion) || count($target_tcversion) != 1) { $status_ok = false; - $tcase_info = $this->tcaseMgr->get_by_id( $tcase_id ); - $msg = sprintf( TCASE_VERSION_NUMBER_KO_STR, $version_number, $tcase_external_id, $tcase_info[0]['name'] ); - $this->errors[] = new IXR_Error( TCASE_VERSION_NUMBER_KO, $msg_prefix . $msg ); + $tcase_info = $this->tcaseMgr->get_by_id($tcase_id); + $msg = sprintf(TCASE_VERSION_NUMBER_KO_STR, $version_number, + $tcase_external_id, $tcase_info[0]['name']); + $this->errors[] = new IXR_Error(TCASE_VERSION_NUMBER_KO, + $msg_prefix . $msg); } } - if($status_ok) { + if ($status_ok) { // Optional parameters $additional_fields = null; $additional_values = null; $opt_fields = array( - self::$urgencyParamName => 'urgency', - self::$executionOrderParamName => 'node_order' + self::$urgencyParamName => 'urgency', + self::$executionOrderParamName => 'node_order' ); $opt_values = array( - self::$urgencyParamName => null, - self::$executionOrderParamName => 1 + self::$urgencyParamName => null, + self::$executionOrderParamName => 1 ); - foreach( $opt_fields as $key => $field_name ) { - if($this->_isParamPresent( $key )) { + foreach ($opt_fields as $key => $field_name) { + if ($this->_isParamPresent($key)) { $additional_values[] = $this->args[$key]; $additional_fields[] = $field_name; } else { - if(! is_null( $opt_values[$key] )) { + if (! is_null($opt_values[$key])) { $additional_values[] = $opt_values[$key]; $additional_fields[] = $field_name; } @@ -3203,34 +3947,41 @@ public function addTestCaseToTestPlan($args) { } } - if($status_ok) { + if ($status_ok) { // 20100705 - work in progress - BUGID 3564 // if test plan has platforms, platformid argument is MANDATORY $opt = array( - 'outputFormat' => 'mapAccessByID' + 'outputFormat' => 'mapAccessByID' ); - $platformSet = $this->tplanMgr->getPlatforms( $tplan_id, $opt ); - $hasPlatforms = ! is_null( $platformSet ); - $hasPlatformIDArgs = $this->_isParamPresent( self::$platformIDParamName ); + $platformSet = (array) $this->tplanMgr->getPlatforms($tplan_id, $opt); + $hasPlatforms = (! empty($platformSet)); + $hasPlatformIDArgs = $this->_isParamPresent( + self::$platformIDParamName); - if($hasPlatforms) { - if($hasPlatformIDArgs) { + if ($hasPlatforms) { + if ($hasPlatformIDArgs) { // Check if platform id belongs to test plan $platform_id = $this->args[self::$platformIDParamName]; - $status_ok = isset( $platformSet[$platform_id] ); - if(! $status_ok) { - $msg = sprintf( PLATFORM_ID_NOT_LINKED_TO_TESTPLAN_STR, $platform_id, $tplan_info['name'] ); - $this->errors[] = new IXR_Error( PLATFORM_ID_NOT_LINKED_TO_TESTPLAN, $msg ); + $status_ok = isset($platformSet[$platform_id]); + if (! $status_ok) { + $msg = sprintf(PLATFORM_ID_NOT_LINKED_TO_TESTPLAN_STR, + $platform_id, $tplan_info['name']); + $this->errors[] = new IXR_Error( + PLATFORM_ID_NOT_LINKED_TO_TESTPLAN, $msg); } } else { - $msg = sprintf( MISSING_PLATFORMID_BUT_NEEDED_STR, $tplan_info['name'], $tplan_id ); - $this->errors[] = new IXR_Error( MISSING_PLATFORMID_BUT_NEEDED, $msg_prefix . $msg ); + $msg = sprintf(MISSING_PLATFORMID_BUT_NEEDED_STR, + $tplan_info['name'], $tplan_id); + $this->errors[] = new IXR_Error( + MISSING_PLATFORMID_BUT_NEEDED, $msg_prefix . $msg); $status_ok = false; } } } - if($status_ok && $this->userHasRight( "testplan_planning", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + if ($status_ok && + $this->userHasRight("testplan_planning", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { // 20100711 - franciscom // Because for TL 1.9 link is done to test plan + platform, logic used // to understand what to unlink has to be changed. @@ -3248,37 +3999,49 @@ public function addTestCaseToTestPlan($args) { // May be is not wise, IMHO this must be refactored, and give user indication that // requested version already is part of Test Plan. // - $sql = " SELECT TCV.version,TCV.id " . " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['tcversions']} TCV " . " WHERE NH.parent_id = " . intval( $tcase_id ) . " AND TCV.id = NH.id "; + $sql = " SELECT TCV.version,TCV.id " . + " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['tcversions']} TCV " . + " WHERE NH.parent_id = " . intval($tcase_id) . + " AND TCV.id = NH.id "; - $all_tcversions = $this->dbObj->fetchRowsIntoMap( $sql, 'id' ); - $id_set = array_keys( $all_tcversions ); + $all_tcversions = $this->dbObj->fetchRowsIntoMap($sql, 'id'); + $id_set = array_keys($all_tcversions); // get records regarding all test case versions linked to test plan - $in_clause = implode( ",", $id_set ); - $sql = " SELECT tcversion_id, platform_id, PLAT.name FROM {$this->tables['testplan_tcversions']} TPTCV " . " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = platform_id " . " WHERE TPTCV.testplan_id={$tplan_id} AND TPTCV.tcversion_id IN({$in_clause}) "; + $in_clause = implode(",", $id_set); + $sql = " SELECT tcversion_id, platform_id, PLAT.name FROM {$this->tables['testplan_tcversions']} TPTCV " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = platform_id " . + " WHERE TPTCV.testplan_id={$tplan_id} AND TPTCV.tcversion_id IN({$in_clause}) "; - if($hasPlatforms) { - $sql .= " AND TPTCV.platform_id=" . intval( $platform_id ); + if ($hasPlatforms) { + $sql .= " AND TPTCV.platform_id=" . intval($platform_id); } - $rs = $this->dbObj->fetchMapRowsIntoMap( $sql, 'tcversion_id', 'platform_id' ); + $rs = $this->dbObj->fetchMapRowsIntoMap($sql, 'tcversion_id', + 'platform_id'); - $doLink = is_null( $rs ); + $doLink = is_null($rs); - if(! $doLink) { + if (! $doLink) { // Are we going to update ? - if(isset( $rs[$target_tcversion[$version_number]['id']] )) { - if($hasPlatforms) { - $plat_keys = array_flip( array_keys( $rs[$target_tcversion[$version_number]['id']] ) ); + if (isset($rs[$target_tcversion[$version_number]['id']])) { + if ($hasPlatforms) { + $plat_keys = array_flip( + array_keys( + $rs[$target_tcversion[$version_number]['id']])); // need to understand what where the linked platforms. $platform_id = $this->args[self::$platformIDParamName]; - $linkExists = isset( $plat_keys[$platform_id] ); + $linkExists = isset($plat_keys[$platform_id]); $doLink = ! $linkExists; - if($linkExists) { + if ($linkExists) { $platform_name = $rs[$target_tcversion[$version_number]['id']][$platform_id]['name']; - $msg = sprintf( LINKED_FEATURE_ALREADY_EXISTS_STR, $tplan_info['name'], $tplan_id, $platform_name, $platform_id ); - $this->errors[] = new IXR_Error( LINKED_FEATURE_ALREADY_EXISTS, $msg_prefix . $msg ); + $msg = sprintf(LINKED_FEATURE_ALREADY_EXISTS_STR, + $tplan_info['name'], $tplan_id, $platform_name, + $platform_id); + $this->errors[] = new IXR_Error( + LINKED_FEATURE_ALREADY_EXISTS, + $msg_prefix . $msg); $status_ok = false; } } else { @@ -3291,61 +4054,68 @@ public function addTestCaseToTestPlan($args) { } else { // Other version than requested done is already linked $doLink = false; - if($this->_isParamPresent( self::$overwriteParamName ) && $this->args[self::$overwriteParamName]) { + if ($this->_isParamPresent(self::$overwriteParamName) && + $this->args[self::$overwriteParamName]) { $doLink = $doDeleteLinks = true; } - reset( $rs ); - $linked_tcversion = key( $rs ); + reset($rs); + $linked_tcversion = key($rs); $other_version = $all_tcversions[$linked_tcversion]['version']; - if(! $doLink) { + if (! $doLink) { $doLink = false; - $msg = sprintf( OTHER_VERSION_IS_ALREADY_LINKED_STR, $other_version, $version_number, $tplan_info['name'], $tplan_id ); - $this->errors[] = new IXR_Error( OTHER_VERSION_IS_ALREADY_LINKED, $msg_prefix . $msg ); + $msg = sprintf(OTHER_VERSION_IS_ALREADY_LINKED_STR, + $other_version, $version_number, $tplan_info['name'], + $tplan_id); + $this->errors[] = new IXR_Error( + OTHER_VERSION_IS_ALREADY_LINKED, $msg_prefix . $msg); $status_ok = false; } } } - if($doLink && $hasPlatforms) { + if ($doLink && $hasPlatforms) { $additional_values[] = $platform_id; $additional_fields[] = 'platform_id'; } - if($doDeleteLinks) { - // $in_clause=implode(",",$id_set); - $sql = " DELETE FROM {$this->tables['testplan_tcversions']} " . " WHERE testplan_id=" . intval( $tplan_id ) . " AND tcversion_id=" . intval( $linked_tcversion ); + if ($doDeleteLinks) { + $sql = " DELETE FROM {$this->tables['testplan_tcversions']} " . + " WHERE testplan_id=" . intval($tplan_id) . + " AND tcversion_id=" . intval($linked_tcversion); - if($hasPlatforms) { - $sql .= " AND platform_id=" . intval( $platform_id ); + if ($hasPlatforms) { + $sql .= " AND platform_id=" . intval($platform_id); } - $this->dbObj->exec_query( $sql ); + $this->dbObj->exec_query($sql); } - if($doLink) { + if ($doLink) { $fields = "testplan_id,tcversion_id,author_id,creation_ts"; - if(! is_null( $additional_fields )) { - $dummy = implode( ",", $additional_fields ); + if (! is_null($additional_fields)) { + $dummy = implode(",", $additional_fields); $fields .= ',' . $dummy; } - $sql_values = "{$tplan_id},{$target_tcversion[$version_number]['id']}," . "{$this->userID},{$this->dbObj->db_now()}"; - if(! is_null( $additional_values )) { - $dummy = implode( ",", $additional_values ); + $sql_values = "{$tplan_id},{$target_tcversion[$version_number]['id']}," . + "{$this->userID},{$this->dbObj->db_now()}"; + if (! is_null($additional_values)) { + $dummy = implode(",", $additional_values); $sql_values .= ',' . $dummy; } $sql = " INSERT INTO {$this->tables['testplan_tcversions']}({$fields}) VALUES({$sql_values})"; - $this->dbObj->exec_query( $sql ); + $this->dbObj->exec_query($sql); - $op_result['feature_id'] = $this->dbObj->insert_id( $this->tables['testplan_tcversions'] ); + $op_result['feature_id'] = $this->dbObj->insert_id( + $this->tables['testplan_tcversions']); } $op_result['operation'] = $operation; $op_result['status'] = true; $op_result['message'] = ''; } - return($status_ok ? $op_result : $this->errors); + return $status_ok ? $op_result : $this->errors; } /** @@ -3357,24 +4127,28 @@ public function addTestCaseToTestPlan($args) { * @return array * */ - public function getFirstLevelTestSuitesForTestProject($args) { + public function getFirstLevelTestSuitesForTestProject($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $status_ok = true; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID' + 'authenticate', + 'checkTestProjectID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { - $result = $this->tprojectMgr->get_first_level_test_suites( $this->args[self::$testProjectIDParamName] ); - if(is_null( $result )) { + if ($status_ok) { + $result = $this->tprojectMgr->get_first_level_test_suites( + $this->args[self::$testProjectIDParamName]); + if (is_null($result)) { $status_ok = false; - $tproject_info = $this->tprojectMgr->get_by_id( $this->args[self::$testProjectIDParamName] ); - $msg = $msg_prefix . sprintf( TPROJECT_IS_EMPTY_STR, $tproject_info['name'] ); - $this->errors[] = new IXR_ERROR( TPROJECT_IS_EMPTY, $msg ); + $tproject_info = $this->tprojectMgr->get_by_id( + $this->args[self::$testProjectIDParamName]); + $msg = $msg_prefix . + sprintf(TPROJECT_IS_EMPTY_STR, $tproject_info['name']); + $this->errors[] = new IXR_ERROR(TPROJECT_IS_EMPTY, $msg); } } return $status_ok ? $result : $this->errors; @@ -3398,56 +4172,60 @@ public function getFirstLevelTestSuitesForTestProject($args) { * array('req_spec' => 3,'requirements' => array(22,42)) * */ - public function assignRequirements($args) { + public function assignRequirements($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $status_ok = true; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkTestCaseIdentity' + 'authenticate', + 'checkTestProjectID', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { + if ($status_ok) { $ret = $this->checkTestCaseAncestry(); $status_ok = $ret['status_ok']; - if(! $status_ok) { - $this->errors[] = new IXR_Error( $ret['error_code'], $msg_prefix . $ret['error_msg'] ); + if (! $status_ok) { + $this->errors[] = new IXR_Error($ret['error_code'], + $msg_prefix . $ret['error_msg']); } } - if($status_ok) { + if ($status_ok) { $ret = $this->checkReqSpecQuality(); $status_ok = $ret['status_ok']; - if(! $status_ok) { - $this->errors[] = new IXR_Error( $ret['error_code'], $msg_prefix . $ret['error_msg'] ); + if (! $status_ok) { + $this->errors[] = new IXR_Error($ret['error_code'], + $msg_prefix . $ret['error_msg']); } } - if($status_ok) { + if ($status_ok) { // assignment // Note: when test case identity is checked this args key is setted // this does not means that this mut be present on method call. // $tcase_id = $this->args[self::$testCaseIDParamName]; - foreach( $this->args[self::$requirementsParamName] as $item ) { - foreach( $item['requirements'] as $req_id ) { - $this->reqMgr->assignToTCaseUsingLatestVersions( $req_id, $tcase_id, $this->userID ); + foreach ($this->args[self::$requirementsParamName] as $item) { + foreach ($item['requirements'] as $req_id) { + $this->reqMgr->assignToTCaseUsingLatestVersions($req_id, + $tcase_id, $this->userID); } } $resultInfo[] = array( - "operation" => $operation, - "status" => true, - "id" => - 1, - "additionalInfo" => '', - "message" => GENERAL_SUCCESS_STR + "operation" => $operation, + "status" => true, + "id" => - 1, + "additionalInfo" => '', + "message" => GENERAL_SUCCESS_STR ); } - return($status_ok ? $resultInfo : $this->errors); + return $status_ok ? $resultInfo : $this->errors; } /** @@ -3456,104 +4234,289 @@ public function assignRequirements($args) { * @param string $messagePrefix * used to be prepended to error message * - * @return map with following keys + * @return array with following keys * boolean map['status_ok'] * string map['error_msg'] * int map['error_code'] */ - protected function checkTestCaseAncestry($messagePrefix = '') { + protected function checkTestCaseAncestry($messagePrefix = '') + { $ret = array( - 'status_ok' => true, - 'error_msg' => '', - 'error_code' => 0 + 'status_ok' => true, + 'error_msg' => '', + 'error_code' => 0 ); $tproject_id = $this->args[self::$testProjectIDParamName]; $tcase_id = $this->args[self::$testCaseIDParamName]; - $tcase_tproject_id = $this->tcaseMgr->get_testproject( $tcase_id ); + $tcase_tproject_id = $this->tcaseMgr->get_testproject($tcase_id); - if($tcase_tproject_id != $tproject_id) { - $status_ok = false; - $tcase_info = $this->tcaseMgr->get_by_id( $tcase_id ); - $dummy = $this->tcaseMgr->getExternalID( $tcase_id ); + if ($tcase_tproject_id != $tproject_id) { + $tcase_info = $this->tcaseMgr->get_by_id($tcase_id); + $dummy = $this->tcaseMgr->getExternalID($tcase_id); $tcase_external_id = $dummy[0]; - $tproject_info = $this->tprojectMgr->get_by_id( $tproject_id ); - $msg = $messagePrefix . sprintf( TCASE_TPROJECT_KO_STR, $tcase_external_id, $tcase_info[0]['name'], $tproject_info['name'], $tproject_id ); + $tproject_info = $this->tprojectMgr->get_by_id($tproject_id); + $msg = $messagePrefix . + sprintf(TCASE_TPROJECT_KO_STR, $tcase_external_id, + $tcase_info[0]['name'], $tproject_info['name'], $tproject_id); $ret = array( - 'status_ok' => false, - 'error_msg' => $msg, - 'error_code' => TCASE_TPROJECT_KO + 'status_ok' => false, + 'error_msg' => $msg, + 'error_code' => TCASE_TPROJECT_KO ); } return $ret; } + /** + * Helper method to check if the requirement ID provided is valid + * + * It is valid when: + * - is present + * - is integer + * - exists in DB + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkReqID($messagePrefix = '') + { + $status = false; + + if ($this->_isRequirementIDPresent()) { + $reqID = $this->args[self::$requirementIDParamName]; + if (is_int($reqID)) { + // See if this req ID exists in the DB + $reqID = $this->dbObj->prepare_int($reqID); + $query = "SELECT NH.id AS id " . + "FROM {$this->tables['nodes_hierarchy']} NH, " . + "{$this->tables['node_types']} NT " . "WHERE NH.id={$reqID} " . + "AND NH.node_type_id=NT.id " . + "AND NT.description='requirement'"; + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + + if ($result) { + $status = true; + } else { + $msg = $messagePrefix . sprintf(REQ_KO_STR, $reqID); + $this->errors[] = new IXR_Error(REQ_KO, $msg); + } + } else { + $msg = $messagePrefix . + sprintf(PARAMETER_NOT_INT_STR, self::$requirementIDParamName, + $reqID); + $this->errors[] = new IXR_Error(PARAMETER_NOT_INT, $msg); + } + } else { + // required argument missing + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$requirementIDParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); + } + + return $status; + } + + /** + * Helper method to check if the requirement version ID provided is valid + * + * It is valid when: + * - is present + * - is integer + * - exists in DB + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkReqVersionID($messagePrefix = '') + { + $status = false; + + if ($this->_isRequirementVersionIDPresent()) { + $reqVersionID = $this->args[self::$requirementVersionIDParamName]; + if (is_int($reqVersionID)) { + // See if this req ID exists in the DB + $reqVersionID = $this->dbObj->prepare_int($reqVersionID); + $query = "SELECT NH.id AS id " . + "FROM {$this->tables['nodes_hierarchy']} NH, " . + "{$this->tables['node_types']} NT " . + "WHERE NH.id={$reqVersionID} " . "AND NH.node_type_id=NT.id " . + "AND NT.description='requirement_version'"; + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); + + if ($result) { + $status = true; + } else { + $msg = $messagePrefix . sprintf(REQ_KO_STR, $reqID); + $this->errors[] = new IXR_Error(REQ_KO, $msg); + } + } else { + $msg = $messagePrefix . + sprintf(PARAMETER_NOT_INT_STR, self::$requirementIDParamName, + $reqVersionID); + $this->errors[] = new IXR_Error(PARAMETER_NOT_INT, $msg); + } + } else { + // required argument missing + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$requirementIDParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); + } + + return $status; + } + + /** + * Helper method to check if the requirement identity provided is valid + * Identity may be specified in on of theses modes: + * + * - requirement Doc ID + * - requirement internal ID + * + * If Doc ID is provided, the project ID must be provided. + * + * If everything OK, requirement internal ID is setted. + * + * @param string $messagePrefix + * used to be prepended to error message + * + * @return boolean + * @access protected + */ + protected function checkReqIdentity($messagePrefix = '') + { + $status = false; + + if ($this->_isRequirementIDPresent()) { + + if (is_int($this->args[self::$requirementIDParamName])) { + $status = true; + } else { + $msg = $messagePrefix . + sprintf(PARAMETER_NOT_INT_STR, self::$requirementIDParamName, + $this->args[self::$requirementIDParamName]); + $this->errors[] = new IXR_Error(PARAMETER_NOT_INT, $msg); + } + } elseif ($this->_isRequirementDocIDPresent()) { + + if ($this->checkTestProjectID()) { + // check req id exists in the project + $reqDocID = $this->args[self::$requirementDocIDParamName]; + $req = $this->reqMgr->getByDocID($reqDocID, + $this->args[self::$testProjectIDParamName], null, + array( + 'access_key' => 'req_doc_id', + 'output' => 'minimun' + )); + if (! is_null($req)) { + // set the requirement ID + $this->args[self::$requirementIDParamName] = (int) $req[$reqDocID]['id']; + $status = true; + } else { + $msg = $messagePrefix . + sprintf(NO_REQ_IN_THIS_PROJECT_STR, $reqDocID, + $this->args[self::$testProjectIDParamName]); + $this->errors[] = new IXR_Error(NO_REQ_IN_THIS_PROJECT, $msg); + } + } + } else { + + // required argument missing + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$requirementIDParamName . ' or ' . + self::$requirementDocIDParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); + } + + if ($status) { + $status = $this->checkReqID($messagePrefix); + } + + return $status; + } + /* * checks Quality of requirements spec * checks done on * Requirements Specification is present on system * Requirements Specification belongs to test project * - * @return map with following keys + * @return array with following keys * boolean map['status_ok'] * string map['error_msg'] * int map['error_code'] */ - protected function checkReqSpecQuality() { + protected function checkReqSpecQuality() + { $ret = array( - 'status_ok' => true, - 'error_msg' => '', - 'error_code' => 0 + 'status_ok' => true, + 'error_msg' => '', + 'error_code' => 0 ); $tproject_id = $this->args[self::$testProjectIDParamName]; $nodes_types = $this->tprojectMgr->tree_manager->get_available_node_types(); - foreach( $this->args[self::$requirementsParamName] as $item ) { + foreach ($this->args[self::$requirementsParamName] as $item) { // does it exist ? $req_spec_id = $item['req_spec']; - $reqspec_info = $this->reqSpecMgr->get_by_id( $req_spec_id ); - if(is_null( $reqspec_info )) { + $reqspec_info = $this->reqSpecMgr->get_by_id($req_spec_id); + if (is_null($reqspec_info)) { $status_ok = false; - $msg = sprintf( REQSPEC_KO_STR, $req_spec_id ); + $msg = sprintf(REQSPEC_KO_STR, $req_spec_id); $error_code = REQSPEC_KO; break; } // does it belongs to test project ? - $a_path = $this->tprojectMgr->tree_manager->get_path( $req_spec_id ); + $a_path = $this->tprojectMgr->tree_manager->get_path($req_spec_id); $req_spec_tproject_id = $a_path[0]['parent_id']; - if($req_spec_tproject_id != $tproject_id) { + if ($req_spec_tproject_id != $tproject_id) { $status_ok = false; - $tproject_info = $this->tprojectMgr->get_by_id( $tproject_id ); - $msg = sprintf( REQSPEC_TPROJECT_KO_STR, $reqspec_info['title'], $req_spec_id, $tproject_info['name'], $tproject_id ); + $tproject_info = $this->tprojectMgr->get_by_id($tproject_id); + $msg = sprintf(REQSPEC_TPROJECT_KO_STR, $reqspec_info['title'], + $req_spec_id, $tproject_info['name'], $tproject_id); $error_code = REQSPEC_TPROJECT_KO; break; } // does this specification have requirements ? - $my_requirements = $this->tprojectMgr->tree_manager->get_subtree_list( $req_spec_id, $nodes_types['requirement'] ); - $status_ok =(trim( $my_requirements ) != ""); - if(! $status_ok) { - $msg = sprintf( REQSPEC_IS_EMPTY_STR, $reqspec_info['title'], $req_spec_id ); + $my_requirements = $this->tprojectMgr->tree_manager->get_subtree_list( + $req_spec_id, $nodes_types['requirement']); + $status_ok = (trim($my_requirements) != ""); + if (! $status_ok) { + $msg = sprintf(REQSPEC_IS_EMPTY_STR, $reqspec_info['title'], + $req_spec_id); $error_code = REQSPEC_IS_EMPTY; break; } // if everything is OK, analise requirements - if($status_ok) { - $dummy = array_flip( explode( ",", $my_requirements ) ); - foreach( $item['requirements'] as $req_id ) { - if(! isset( $dummy[$req_id] )) { + if ($status_ok) { + $dummy = array_flip(explode(",", $my_requirements)); + foreach ($item['requirements'] as $req_id) { + if (! isset($dummy[$req_id])) { $status_ok = false; - $req_info = $this->reqMgr->get_by_id( $req_id, requirement_mgr::LATEST_VERSION ); + $req_info = $this->reqMgr->get_by_id($req_id, + requirement_mgr::LATEST_VERSION); - if(is_null( $req_info )) { - $msg = sprintf( REQ_KO_STR, $req_id ); + if (is_null($req_info)) { + $msg = sprintf(REQ_KO_STR, $req_id); $error_code = REQ_KO; } else { $req_info = $req_inf[0]; - $msg = sprintf( REQ_REQSPEC_KO_STR, $req_info['req_doc_id'], $req_info['title'], $req_id, $reqspec_info['title'], $req_spec_id ); + $msg = sprintf(REQ_REQSPEC_KO_STR, + $req_info['req_doc_id'], $req_info['title'], + $req_id, $reqspec_info['title'], $req_spec_id); $error_code = REQ_REQSPEC_KO; } break; @@ -3561,15 +4524,16 @@ protected function checkReqSpecQuality() { } } - if(! $status_ok) { + if (! $status_ok) { break; } } - if(! $status_ok) { - $ret = array('status_ok' => false, - 'error_msg' => $msg, - 'error_code' => $error_code + if (! $status_ok) { + $ret = array( + 'status_ok' => false, + 'error_msg' => $msg, + 'error_code' => $error_code ); } return $ret; @@ -3584,18 +4548,21 @@ protected function checkReqSpecQuality() { * @access protected * contribution by hnishiyama */ - protected function _insertExecutionBug($executionID, $bugID) { + protected function _insertExecutionBug($executionID, $bugID) + { // Check for existence of executionID $sql = "SELECT id FROM {$this->tables['executions']} WHERE id={$executionID}"; - $rs = $this->dbObj->fetchRowsIntoMap( $sql, 'id' ); - $status_ok = !(is_null( $rs ) || $bugID == ''); - if($status_ok) { - $safeBugID = $this->dbObj->prepare_string( $bugID ); - $sql = "SELECT execution_id FROM {$this->tables['execution_bugs']} " . "WHERE execution_id={$executionID} AND bug_id='{$safeBugID}'"; - - if(is_null( $this->dbObj->fetchRowsIntoMap( $sql, 'execution_id' ) )) { - $sql = "INSERT INTO {$this->tables['execution_bugs']} " . "(execution_id,bug_id) VALUES({$executionID},'{$safeBugID}')"; - $result = $this->dbObj->exec_query( $sql ); + $rs = $this->dbObj->fetchRowsIntoMap($sql, 'id'); + $status_ok = ! (is_null($rs) || $bugID == ''); + if ($status_ok) { + $safeBugID = $this->dbObj->prepare_string($bugID); + $sql = "SELECT execution_id FROM {$this->tables['execution_bugs']} " . + "WHERE execution_id={$executionID} AND bug_id='{$safeBugID}'"; + + if (is_null($this->dbObj->fetchRowsIntoMap($sql, 'execution_id'))) { + $sql = "INSERT INTO {$this->tables['execution_bugs']} " . + "(execution_id,bug_id) VALUES({$executionID},'{$safeBugID}')"; + $result = $this->dbObj->exec_query($sql); $status_ok = $result ? true : false; } } @@ -3607,13 +4574,19 @@ protected function _insertExecutionBug($executionID, $bugID) { * * @param int $execution_id * - * @return map indexed by bug_id + * @return array indexed by bug_id */ - protected function _getBugsForExecutionId($execution_id) { + protected function _getBugsForExecutionId($execution_id) + { $rs = null; - if(! is_null( $execution_id ) && $execution_id != '') { - $sql = "SELECT execution_id,bug_id, B.name AS build_name " . "FROM {$this->tables['execution_bugs']} ," . " {$this->tables['executions']} E, {$this->tables['builds']} B " . "WHERE execution_id={$execution_id} " . "AND execution_id=E.id " . "AND E.build_id=B.id " . "ORDER BY B.name,bug_id"; - $rs = $this->dbObj->fetchRowsIntoMap( $sql, 'bug_id' ); + if (! is_null($execution_id) && $execution_id != '') { + $sql = "SELECT execution_id,bug_id, B.name AS build_name " . + "FROM {$this->tables['execution_bugs']} ," . + " {$this->tables['executions']} E, {$this->tables['builds']} B " . + "WHERE execution_id={$execution_id} " . + "AND execution_id=E.id " . "AND E.build_id=B.id " . + "ORDER BY B.name,bug_id"; + $rs = $this->dbObj->fetchRowsIntoMap($sql, 'bug_id'); } return $rs; } @@ -3625,44 +4598,47 @@ protected function _getBugsForExecutionId($execution_id) { * * @param struct $args * @param string $args["devKey"] - attachm * Developer key + * attachm * Developer key * @param int $args["testsuiteid"]: * id of the testsuite * * @return mixed $resultInfo * @author dennis@etern-it.de */ - public function getTestSuiteAttachments($args) { - $this->_setArgs( $args ); - $box= null; + public function getTestSuiteAttachments($args) + { + $this->_setArgs($args); + $box = null; $checkFunctions = array( - 'authenticate', - 'checkTestSuiteID' + 'authenticate', + 'checkTestSuiteID' ); - $status_ok = $this->_runChecks( $checkFunctions ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); - if($status_ok) { + if ($status_ok) { $tsuite_id = $this->args[self::$testSuiteIDParamName]; - $docRepo = tlAttachmentRepository::create( $this->dbObj ); - $docSet = $docRepo->getAttachmentInfosFor( $tsuite_id, "nodes_hierarchy" ); + $docRepo = tlAttachmentRepository::create($this->dbObj); + $docSet = $docRepo->getAttachmentInfosFor($tsuite_id, + "nodes_hierarchy"); - if($docSet) { - foreach( $docSet as $docu ) { + if ($docSet) { + foreach ($docSet as $docu) { $aID = $docu["id"]; - $content = $docRepo->getAttachmentContent( $aID, $docu ); + $content = $docRepo->getAttachmentContent($aID, $docu); - if($content != null) { + if ($content != null) { $box[$aID]["id"] = $aID; $box[$aID]["name"] = $docu["file_name"]; $box[$aID]["file_type"] = $docu["file_type"]; $box[$aID]["title"] = $docu["title"]; $box[$aID]["date_added"] = $docu["date_added"]; - $box[$aID]["content"] = base64_encode( $content ); + $box[$aID]["content"] = base64_encode($content); } } } } - return $status_ok ? $box: $this->errors; + return $status_ok ? $box : $this->errors; } /** @@ -3686,55 +4662,59 @@ public function getTestSuiteAttachments($args) { * * @return mixed $resultInfo */ - public function getTestCaseAttachments($args) { - $this->_setArgs( $args ); - $box= null; - $checkFunctions = array('authenticate', - 'checkTestCaseIdentity' + public function getTestCaseAttachments($args) + { + $this->_setArgs($args); + $box = null; + $checkFunctions = array( + 'authenticate', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); - if( !$status_ok ) { + if (! $status_ok) { return $this->errors; - } - - if( $status_ok && - !$this->_isParamPresent( self::$versionNumberParamName ) ) { - try { - $tc = $this->getTestCase($args,self::THROW_ON_ERROR); - $this->args[self::$versionNumberParamName] = $tc[0][self::$versionNumberParamName]; - } catch (Exception $e) { - return $this->errors; - } } - + + if ($status_ok && + ! $this->_isParamPresent(self::$versionNumberParamName)) { + try { + $tc = $this->getTestCase($args, self::THROW_ON_ERROR); + $this->args[self::$versionNumberParamName] = $tc[0][self::$versionNumberParamName]; + } catch (Exception $e) { + return $this->errors; + } + } + $map = $this->checkTestCaseVersionNumberAncestry(); $status_ok = $map["status_ok"]; - - if($status_ok) { + + if ($status_ok) { $tcvid = $this->tcVersionID; - $docRepo = tlAttachmentRepository::create( $this->dbObj ); - $docSet = $docRepo->getAttachmentInfosFor( $tcvid, "tcversions" ); + $docRepo = tlAttachmentRepository::create($this->dbObj); + $docSet = $docRepo->getAttachmentInfosFor($tcvid, "tcversions"); - if($docSet) { - foreach( $docSet as $docu ) { + if ($docSet) { + foreach ($docSet as $docu) { $aID = $docu["id"]; - $content = $docRepo->getAttachmentContent( $aID, $docu ); + $content = $docRepo->getAttachmentContent($aID, $docu); - if($content != null) { + if ($content != null) { $box[$aID]["id"] = $aID; $box[$aID]["name"] = $docu["file_name"]; $box[$aID]["file_type"] = $docu["file_type"]; $box[$aID]["title"] = $docu["title"]; $box[$aID]["date_added"] = $docu["date_added"]; - $box[$aID]["content"] = base64_encode( $content ); + $box[$aID]["content"] = base64_encode($content); } } } } else { - $this->errors[] = new IXR_Error( INVALID_TESTCASE_VERSION_NUMBER, $map["error_msg"] ); + $this->errors[] = new IXR_Error(INVALID_TESTCASE_VERSION_NUMBER, + $map["error_msg"]); } - return $status_ok ? $box: $this->errors; + return $status_ok ? $box : $this->errors; } /** @@ -3755,9 +4735,10 @@ public function getTestCaseAttachments($args) { * * @return mixed $resultInfo */ - public function updateTestSuite($args) { + public function updateTestSuite($args) + { $args[self::$actionParamName] = 'update'; - return $this->createTestSuite( $args ); + return $this->createTestSuite($args); } /** @@ -3783,97 +4764,114 @@ public function updateTestSuite($args) { * * @return mixed $resultInfo */ - public function createTestSuite($args) { + public function createTestSuite($args) + { $result = array(); - $this->_setArgs( $args ); - $action = isset( $this->args, self::$actionParamName ) ? $this->args[self::$actionParamName] : 'create'; + $this->_setArgs($args); + $action = isset($this->args, self::$actionParamName) ? $this->args[self::$actionParamName] : 'create'; $checkFunctions = array( - 'authenticate', - 'checkTestProjectIdentity' + 'authenticate', + 'checkTestProjectIdentity' ); - switch($action) { - case 'update' : + switch ($action) { + case 'update': $operation = 'updateTestSuite'; $opt = array( - self::$detailsParamName => null, - self::$testSuiteNameParamName => null, - self::$orderParamName => testsuite::DEFAULT_ORDER, - self::$checkDuplicatedNameParamName => testsuite::CHECK_DUPLICATE_NAME, - self::$actionOnDuplicatedNameParamName => 'block' + self::$detailsParamName => null, + self::$testSuiteNameParamName => null, + self::$orderParamName => testsuite::DEFAULT_ORDER, + self::$checkDuplicatedNameParamName => testsuite::CHECK_DUPLICATE_NAME, + self::$actionOnDuplicatedNameParamName => 'block' ); break; - case 'create' : - default : + case 'create': + default: $operation = __FUNCTION__; $opt = array( - self::$orderParamName => testsuite::DEFAULT_ORDER, - self::$checkDuplicatedNameParamName => testsuite::CHECK_DUPLICATE_NAME, - self::$actionOnDuplicatedNameParamName => 'block' + self::$orderParamName => testsuite::DEFAULT_ORDER, + self::$checkDuplicatedNameParamName => testsuite::CHECK_DUPLICATE_NAME, + self::$actionOnDuplicatedNameParamName => 'block' ); $checkFunctions[] = 'checkTestSuiteName'; break; } $msg_prefix = "({$operation}) - "; - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); // When working on PRIVATE containers, globalRole Admin is ENOUGH // because this is how TestLink works when this action is done on GUI - if($status_ok && $this->user->globalRole->dbID != TL_ROLES_ADMIN) { - $status_ok = FALSE; - if($this->userHasRight( "mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + if ($status_ok && $this->user->globalRole->dbID != TL_ROLES_ADMIN) { + $status_ok = false; + if ($this->userHasRight("mgt_modify_tc", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { $status_ok = true; } } - if($status_ok) { + if ($status_ok) { // Needed After refactoring to use checkTestProjectIdentity() $key = self::$testProjectIDParamName; $args[$key] = $this->args[$key]; // Optional parameters - foreach( $opt as $key => $value ) { - if($this->_isParamPresent( $key )) { + foreach ($opt as $key => $value) { + if ($this->_isParamPresent($key)) { $opt[$key] = $this->args[$key]; } } } - if($status_ok) { + if ($status_ok) { $parent_id = $args[self::$testProjectIDParamName]; - $tprojectInfo = $this->tprojectMgr->get_by_id( $args[self::$testProjectIDParamName] ); + $tprojectInfo = $this->tprojectMgr->get_by_id( + $args[self::$testProjectIDParamName]); - $tsuiteMgr = new testsuite( $this->dbObj ); - if($this->_isParamPresent( self::$parentIDParamName )) { + $tsuiteMgr = new testsuite($this->dbObj); + if ($this->_isParamPresent(self::$parentIDParamName)) { $parent_id = $args[self::$parentIDParamName]; // if parentid exists it must: // be a test suite id - $node_info = $tsuiteMgr->get_by_id( $args[self::$parentIDParamName] ); - if(!($status_ok = ! is_null( $node_info ))) { - $msg = sprintf( INVALID_PARENT_TESTSUITEID_STR, $args[self::$parentIDParamName], $args[self::$testSuiteNameParamName] ); - $this->errors[] = new IXR_Error( INVALID_PARENT_TESTSUITEID, $msg_prefix . $msg ); + $node_info = $tsuiteMgr->get_by_id( + $args[self::$parentIDParamName]); + if (! ($status_ok = ! is_null($node_info))) { + $msg = sprintf(INVALID_PARENT_TESTSUITEID_STR, + $args[self::$parentIDParamName], + $args[self::$testSuiteNameParamName]); + $this->errors[] = new IXR_Error(INVALID_PARENT_TESTSUITEID, + $msg_prefix . $msg); } - if($status_ok) { + if ($status_ok) { // Must belong to target test project - $root_node_id = $tsuiteMgr->getTestProjectFromTestSuite( $args[self::$parentIDParamName], null ); - - if(!($status_ok =($root_node_id == $args[self::$testProjectIDParamName]))) { - $msg = sprintf( TESTSUITE_DONOTBELONGTO_TESTPROJECT_STR, $args[self::$parentIDParamName], $tprojectInfo['name'], $args[self::$testProjectIDParamName] ); - $this->errors[] = new IXR_Error( TESTSUITE_DONOTBELONGTO_TESTPROJECT, $msg_prefix . $msg ); + $root_node_id = $tsuiteMgr->getTestProjectFromTestSuite( + $args[self::$parentIDParamName], null); + + if (! ($status_ok = ($root_node_id == + $args[self::$testProjectIDParamName]))) { + $msg = sprintf(TESTSUITE_DONOTBELONGTO_TESTPROJECT_STR, + $args[self::$parentIDParamName], + $tprojectInfo['name'], + $args[self::$testProjectIDParamName]); + $this->errors[] = new IXR_Error( + TESTSUITE_DONOTBELONGTO_TESTPROJECT, + $msg_prefix . $msg); } } } } - if($status_ok) { - switch($action) { - case 'update' : - $op = $tsuiteMgr->update( $args[self::$testSuiteIDParamName], $args[self::$testSuiteNameParamName], $args[self::$detailsParamName], $parent_id, $opt[self::$orderParamName] ); + if ($status_ok) { + switch ($action) { + case 'update': + $op = $tsuiteMgr->update($args[self::$testSuiteIDParamName], + $args[self::$testSuiteNameParamName], + $args[self::$detailsParamName], $parent_id, + $opt[self::$orderParamName]); /* * $opt[self::$checkDuplicatedNameParamName], @@ -3881,23 +4879,29 @@ public function createTestSuite($args) { */ break; - case 'create' : - default : - $op = $tsuiteMgr->create( $parent_id, $args[self::$testSuiteNameParamName], $args[self::$detailsParamName], $opt[self::$orderParamName], $opt[self::$checkDuplicatedNameParamName], $opt[self::$actionOnDuplicatedNameParamName] ); + case 'create': + default: + $op = $tsuiteMgr->create($parent_id, + $args[self::$testSuiteNameParamName], + $args[self::$detailsParamName], + $opt[self::$orderParamName], + $opt[self::$checkDuplicatedNameParamName], + $opt[self::$actionOnDuplicatedNameParamName]); break; } - if(($status_ok = $op['status_ok'])) { + if ($status_ok = $op['status_ok']) { $op['status'] = $op['status_ok'] ? true : false; $op['operation'] = $operation; $op['additionalInfo'] = ''; $op['message'] = $op['msg']; - unset( $op['msg'] ); - unset( $op['status_ok'] ); + unset($op['msg']); + unset($op['status_ok']); $result[] = $op; } else { // @TODO needs refactoring for UPDATE action - $op['msg'] = sprintf( $op['msg'], $args[self::$testSuiteNameParamName] ); + $op['msg'] = sprintf($op['msg'], + $args[self::$testSuiteNameParamName]); $this->errors = $op; } } @@ -3914,17 +4918,19 @@ public function createTestSuite($args) { * @return boolean * @access protected */ - protected function checkTestSuiteName($messagePrefix = '') { - $status_ok = isset( $this->args[self::$testSuiteNameParamName] ) ? true : false; - if($status_ok) { + protected function checkTestSuiteName($messagePrefix = '') + { + $status_ok = isset($this->args[self::$testSuiteNameParamName]) ? true : false; + if ($status_ok) { $name = $this->args[self::$testSuiteNameParamName]; - if(! is_string( $name )) { + if (! is_string($name)) { $msg = $messagePrefix . TESTSUITENAME_NOT_STRING_STR; - $this->errors[] = new IXR_Error( TESTSUITENAME_NOT_STRING, $msg ); + $this->errors[] = new IXR_Error(TESTSUITENAME_NOT_STRING, $msg); $status_ok = false; } } else { - $this->errors[] = new IXR_Error( NO_TESTSUITENAME, $messagePrefix . NO_TESTSUITENAME_STR ); + $this->errors[] = new IXR_Error(NO_TESTSUITENAME, + $messagePrefix . NO_TESTSUITENAME_STR); } return $status_ok; } @@ -3938,13 +4944,16 @@ protected function checkTestSuiteName($messagePrefix = '') { * @return mixed $resultInfo * @access public */ - public function getTestProjectByName($args) { + public function getTestProjectByName($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $status_ok = false; - $this->_setArgs( $args ); + $this->_setArgs($args); - if($this->authenticate() && $this->_isParamPresent( self::$testProjectNameParamName, $msg_prefix, self::SET_ERROR )) { - $op = $this->helperGetTestProjectByName( $msg_prefix ); + if ($this->authenticate() && + $this->_isParamPresent(self::$testProjectNameParamName, $msg_prefix, + self::SET_ERROR)) { + $op = $this->helperGetTestProjectByName($msg_prefix); $status_ok = $op['status_ok']; } return $status_ok ? $op['info'] : $this->errors; @@ -3960,45 +4969,51 @@ public function getTestProjectByName($args) { * @return mixed $resultInfo * @access public */ - public function getTestPlanByName($args) { + public function getTestPlanByName($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $status_ok = true; - $this->_setArgs( $args ); - if($this->authenticate()) { + $this->_setArgs($args); + if ($this->authenticate()) { $keys2check = array( - self::$testPlanNameParamName, - self::$testProjectNameParamName + self::$testPlanNameParamName, + self::$testProjectNameParamName ); - foreach( $keys2check as $key ) { - $names[$key] = $this->_isParamPresent( $key, $msg_prefix, self::SET_ERROR ) ? trim( $this->args[$key] ) : ''; - if($names[$key] == '') { + foreach ($keys2check as $key) { + $names[$key] = $this->_isParamPresent($key, $msg_prefix, + self::SET_ERROR) ? trim($this->args[$key]) : ''; + if ($names[$key] == '') { $status_ok = false; break; } } } - if($status_ok) { + if ($status_ok) { // need to check name existences $name = $names[self::$testProjectNameParamName]; - $check_op = $this->tprojectMgr->checkNameExistence( $name ); + $check_op = $this->tprojectMgr->checkNameExistence($name); $not_found = $check_op['status_ok']; $status_ok = ! $not_found; - if($not_found) { + if ($not_found) { $status_ok = false; - $msg = $msg_prefix . sprintf( TESTPROJECTNAME_DOESNOT_EXIST_STR, $name ); - $this->errors[] = new IXR_Error( TESTPROJECTNAME_DOESNOT_EXIST, $msg ); + $msg = $msg_prefix . + sprintf(TESTPROJECTNAME_DOESNOT_EXIST_STR, $name); + $this->errors[] = new IXR_Error(TESTPROJECTNAME_DOESNOT_EXIST, + $msg); } else { - $tprojectInfo = current( $this->tprojectMgr->get_by_name( $name ) ); + $tprojectInfo = current($this->tprojectMgr->get_by_name($name)); } } - if($status_ok) { - $name = trim( $names[self::$testPlanNameParamName] ); - $info = $this->tplanMgr->get_by_name( $name, $tprojectInfo['id'] ); - if(!($status_ok = ! is_null( $info ))) { - $msg = $msg_prefix . sprintf( TESTPLANNAME_DOESNOT_EXIST_STR, $name, $tprojectInfo['name'] ); - $this->errors[] = new IXR_Error( TESTPLANNAME_DOESNOT_EXIST, $msg ); + if ($status_ok) { + $name = trim($names[self::$testPlanNameParamName]); + $info = $this->tplanMgr->get_by_name($name, $tprojectInfo['id']); + if (! ($status_ok = ! is_null($info))) { + $msg = $msg_prefix . + sprintf(TESTPLANNAME_DOESNOT_EXIST_STR, $name, + $tprojectInfo['name']); + $this->errors[] = new IXR_Error(TESTPLANNAME_DOESNOT_EXIST, $msg); } } @@ -4023,70 +5038,73 @@ public function getTestPlanByName($args) { * * @return mixed $resultInfo */ - public function getTestCase($args,$throwOnError=false) { + public function getTestCase($args, $throwOnError = false) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $status_ok = true; - $this->_setArgs( $args ); + $this->_setArgs($args); - $checkFunctions = array('authenticate', - 'checkTestCaseIdentity' + $checkFunctions = array( + 'authenticate', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); $version_id = testcase::LATEST_VERSION; $version_number = - 1; - if($status_ok) { - // check optional arguments - if($this->_isParamPresent( self::$versionNumberParamName )) { - if(($status_ok = $this->checkTestCaseVersionNumber())) { - $version_id = null; - $version_number = $this->args[self::$versionNumberParamName]; - } - } + // check optional arguments + if ($status_ok && $this->_isParamPresent(self::$versionNumberParamName) && + $status_ok = $this->checkTestCaseVersionNumber()) { + $version_id = null; + $version_number = $this->args[self::$versionNumberParamName]; } - if($status_ok) { - $testCaseMgr = new testcase( $this->dbObj ); + if ($status_ok) { + $testCaseMgr = new testcase($this->dbObj); $id = $this->args[self::$testCaseIDParamName]; $filters = array( - 'active_status' => 'ALL', - 'open_status' => 'ALL', - 'version_number' => $version_number + 'active_status' => 'ALL', + 'open_status' => 'ALL', + 'version_number' => $version_number ); - $result = $testCaseMgr->get_by_id( $id, $version_id, $filters ); - - if(0 == sizeof( $result )) { + $result = $testCaseMgr->get_by_id($id, $version_id, $filters); + if (empty($result)) { $status_ok = false; - $this->errors[] = new IXR_ERROR( NO_TESTCASE_FOUND, $msg_prefix . NO_TESTCASE_FOUND_STR ); + $this->errors[] = new IXR_ERROR(NO_TESTCASE_FOUND, + $msg_prefix . NO_TESTCASE_FOUND_STR); return $this->errors; } else { - if(isset( $this->args[self::$testCaseExternalIDParamName] )) { + if (isset($this->args[self::$testCaseExternalIDParamName])) { $result[0]['full_tc_external_id'] = $this->args[self::$testCaseExternalIDParamName]; } else { - $dummy = $this->tcaseMgr->getPrefix( $id ); - $result[0]['full_tc_external_id'] = $dummy[0] . config_get( 'testcase_cfg' )->glue_character . $result[0]['tc_external_id']; + $dummy = $this->tcaseMgr->getPrefix($id); + $result[0]['full_tc_external_id'] = $dummy[0] . + config_get('testcase_cfg')->glue_character . + $result[0]['tc_external_id']; } } } - if($status_ok) { + if ($status_ok) { // before returning info need to understand if test case belongs to a test project // accessible to user requesting info // return $result[0]['id']; - $this->args[self::$testProjectIDParamName] = $this->tcaseMgr->get_testproject( $result[0]['id'] ); - $status_ok = $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $this->args[self::$testProjectIDParamName] = $this->tcaseMgr->get_testproject( + $result[0]['id']); + $status_ok = $this->userHasRight("mgt_view_tc", + self::CHECK_PUBLIC_PRIVATE_ATTR); } - if( $status_ok ) { - return $result; + if ($status_ok) { + return $result; } else { - if( $throwOnError ) { - throw new Exception("Test Case Does Not Exist", 1); - } else { - return $this->errors; - } + if ($throwOnError) { + throw new Exception("Test Case Does Not Exist", 1); + } else { + return $this->errors; + } } } @@ -4110,34 +5128,37 @@ public function getTestCase($args,$throwOnError=false) { * @return mixed $resultInfo * @internal revisions */ - public function createTestPlan($args) { - $this->_setArgs( $args ); + public function createTestPlan($args) + { + $this->_setArgs($args); $status_ok = false; $msg_prefix = "(" . __FUNCTION__ . ") - "; - if($this->authenticate()) { + if ($this->authenticate()) { $keys2check = array( - self::$testPlanNameParamName + self::$testPlanNameParamName ); $status_ok = true; - foreach( $keys2check as $key ) { - $dummy[$key] = $this->_isParamPresent( $key, $msg_prefix, self::SET_ERROR ) ? trim( $this->args[$key] ) : ''; - if($dummy[$key] == '') { + foreach ($keys2check as $key) { + $dummy[$key] = $this->_isParamPresent($key, $msg_prefix, + self::SET_ERROR) ? trim($this->args[$key]) : ''; + if ($dummy[$key] == '') { $status_ok = false; break; } } } - if($status_ok) { + if ($status_ok) { $keys2check = array( - self::$testProjectNameParamName, - self::$prefixParamName + self::$testProjectNameParamName, + self::$prefixParamName ); $status_ok = true; - foreach( $keys2check as $key ) { - $target[$key] = $this->_isParamPresent( $key, $msg_prefix ) ? trim( $this->args[$key] ) : ''; - if($target[$key] == '') { + foreach ($keys2check as $key) { + $target[$key] = $this->_isParamPresent($key, $msg_prefix) ? trim( + $this->args[$key]) : ''; + if ($target[$key] == '') { $status_ok = false; } else { // first good match is OK @@ -4146,11 +5167,12 @@ public function createTestPlan($args) { } } - if($status_ok == false) { + if (! $status_ok) { // lazy way to generate error - foreach( $keys2check as $key ) { - $dummy[$key] = $this->_isParamPresent( $key, $msg_prefix ) ? trim( $this->args[$key] ) : ''; - if($dummy[$key] == '') { + foreach ($keys2check as $key) { + $dummy[$key] = $this->_isParamPresent($key, $msg_prefix) ? trim( + $this->args[$key]) : ''; + if ($dummy[$key] == '') { $status_ok = false; break; } @@ -4158,75 +5180,89 @@ public function createTestPlan($args) { } } - if($status_ok) { + if ($status_ok) { $status_ok = false; - if(isset( $target[self::$testProjectNameParamName] ) && $target[self::$testProjectNameParamName] != '') { - $name = trim( $this->args[self::$testProjectNameParamName] ); - $check_op = $this->tprojectMgr->checkNameExistence( $name ); + if (isset($target[self::$testProjectNameParamName]) && + $target[self::$testProjectNameParamName] != '') { + $name = trim($this->args[self::$testProjectNameParamName]); + $check_op = $this->tprojectMgr->checkNameExistence($name); $status_ok = ! $check_op['status_ok']; - if($status_ok) { - $tprojectInfo = current( $this->tprojectMgr->get_by_name( $name ) ); + if ($status_ok) { + $tprojectInfo = current( + $this->tprojectMgr->get_by_name($name)); } else { $status_ok = false; - $msg = $msg_prefix . sprintf( TESTPROJECTNAME_DOESNOT_EXIST_STR, $name ); - $this->errors[] = new IXR_Error( TESTPROJECTNAME_DOESNOT_EXIST, $msg ); + $msg = $msg_prefix . + sprintf(TESTPROJECTNAME_DOESNOT_EXIST_STR, $name); + $this->errors[] = new IXR_Error( + TESTPROJECTNAME_DOESNOT_EXIST, $msg); } } else { - if(isset( $target[self::$prefixParamName] ) && $target[self::$prefixParamName] != '') { - $prefix = trim( $this->args[self::$prefixParamName] ); - $tprojectInfo = $this->tprojectMgr->get_by_prefix( $prefix ); + if (isset($target[self::$prefixParamName]) && + $target[self::$prefixParamName] != '') { + $prefix = trim($this->args[self::$prefixParamName]); + $tprojectInfo = $this->tprojectMgr->get_by_prefix($prefix); - if(($status_ok = ! is_null( $tprojectInfo )) == false) { - $msg = $msg_prefix . sprintf( TPROJECT_PREFIX_DOESNOT_EXIST_STR, $prefix ); - $this->errors[] = new IXR_Error( TPROJECT_PREFIX_DOESNOT_EXIST, $msg ); + if ($status_ok = is_null($tprojectInfo)) { + $msg = $msg_prefix . + sprintf(TPROJECT_PREFIX_DOESNOT_EXIST_STR, $prefix); + $this->errors[] = new IXR_Error( + TPROJECT_PREFIX_DOESNOT_EXIST, $msg); } } } } // Now we need to check if user has rights to do this action - if($status_ok) { + if ($status_ok) { $this->args[self::$testProjectIDParamName] = $tprojectInfo['id']; $this->args[self::$testPlanIDParamName] = null; // When working on PRIVATE containers, globalRole Admin is ENOUGH // because this is how TestLink works when this action is done on GUI - if($this->user->globalRole->dbID != TL_ROLES_ADMIN) { - $status_ok = $this->userHasRight( "mgt_testplan_create", self::CHECK_PUBLIC_PRIVATE_ATTR ); + if ($this->user->globalRole->dbID != TL_ROLES_ADMIN) { + $status_ok = $this->userHasRight("mgt_testplan_create", + self::CHECK_PUBLIC_PRIVATE_ATTR); } } - if($status_ok) { - $name = trim( $this->args[self::$testPlanNameParamName] ); - $info = $this->tplanMgr->get_by_name( $name, $tprojectInfo['id'] ); - $status_ok = is_null( $info ); + if ($status_ok) { + $name = trim($this->args[self::$testPlanNameParamName]); + $info = $this->tplanMgr->get_by_name($name, $tprojectInfo['id']); - if(!($status_ok = is_null( $info ))) { - $msg = $msg_prefix . sprintf( TESTPLANNAME_ALREADY_EXISTS_STR, $name, $tprojectInfo['name'] ); - $this->errors[] = new IXR_Error( TESTPLANNAME_ALREADY_EXISTS, $msg ); + if (! ($status_ok = is_null($info))) { + $msg = $msg_prefix . + sprintf(TESTPLANNAME_ALREADY_EXISTS_STR, $name, + $tprojectInfo['name']); + $this->errors[] = new IXR_Error(TESTPLANNAME_ALREADY_EXISTS, + $msg); } } - if($status_ok) { + if ($status_ok) { $keys2check = array( - self::$activeParamName => 1, - self::$publicParamName => 1, - self::$noteParamName => '' + self::$activeParamName => 1, + self::$publicParamName => 1, + self::$noteParamName => '' ); - foreach( $keys2check as $key => $value ) { - $optional[$key] = $this->_isParamPresent( $key ) ? trim( $this->args[$key] ) : $value; + foreach ($keys2check as $key => $value) { + $optional[$key] = $this->_isParamPresent($key) ? trim( + $this->args[$key]) : $value; } - $retval = $this->tplanMgr->create( htmlspecialchars( $name ), htmlspecialchars( $optional[self::$noteParamName] ), $tprojectInfo['id'], $optional[self::$activeParamName], $optional[self::$publicParamName] ); + $retval = $this->tplanMgr->create(htmlspecialchars($name), + htmlspecialchars($optional[self::$noteParamName]), + $tprojectInfo['id'], $optional[self::$activeParamName], + $optional[self::$publicParamName]); $resultInfo = array(); $resultInfo[] = array( - "operation" => __FUNCTION__, - "additionalInfo" => null, - "status" => true, - "id" => $retval, - "message" => GENERAL_SUCCESS_STR + "operation" => __FUNCTION__, + "additionalInfo" => null, + "status" => true, + "id" => $retval, + "message" => GENERAL_SUCCESS_STR ); } @@ -4250,75 +5286,88 @@ public function createTestPlan($args) { * where all is ok, but content for KEY(nodeID) will be NULL instead of rising ERROR * */ - public function getFullPath($args) { - $this->_setArgs( $args ); + public function getFullPath($args) + { + $this->_setArgs($args); $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $checkFunctions = array( - 'authenticate' + 'authenticate' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->_isParamPresent( self::$nodeIDParamName, $msg_prefix, self::SET_ERROR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->_isParamPresent(self::$nodeIDParamName, $msg_prefix, + self::SET_ERROR); - if($status_ok) { + if ($status_ok) { $nodeIDSet = $this->args[self::$nodeIDParamName]; // if is array => OK - if(!($workOnSet = is_array( $nodeIDSet )) &&(! is_int( $nodeIDSet ) || $nodeIDSet <= 0)) { - $msg = $msg_prefix . sprintf( NODEID_INVALID_DATA_TYPE ); - $this->errors[] = new IXR_Error( NODEID_INVALID_DATA_TYPE, $msg ); + if (! ($workOnSet = is_array($nodeIDSet)) && + (! is_int($nodeIDSet) || $nodeIDSet <= 0)) { + $msg = $msg_prefix . sprintf(NODEID_INVALID_DATA_TYPE); + $this->errors[] = new IXR_Error(NODEID_INVALID_DATA_TYPE, $msg); $status_ok = false; } - if($status_ok && $workOnSet) { + if ($status_ok && $workOnSet) { // do check on each item on set - foreach( $nodeIDSet as $itemID ) { - if(! is_int( $itemID ) || $itemID <= 0) { - $msg = $msg_prefix . sprintf( NODEID_IS_NOT_INTEGER_STR, $itemID ); - $this->errors[] = new IXR_Error( NODEID_IS_NOT_INTEGER, $msg ); + foreach ($nodeIDSet as $itemID) { + if (! is_int($itemID) || $itemID <= 0) { + $msg = $msg_prefix . + sprintf(NODEID_IS_NOT_INTEGER_STR, $itemID); + $this->errors[] = new IXR_Error(NODEID_IS_NOT_INTEGER, + $msg); $status_ok = false; } } } } - if($status_ok) { + if ($status_ok) { // IMPORTANT NOTICE: - //(may be a design problem but ..) + // (may be a design problem but ..) // If $nodeIDSet is an array and for one of items path can not be found // get_full_path_verbose() returns null, no matter if for other items // information is available // - $full_path = $this->tprojectMgr->tree_manager->get_full_path_verbose( $nodeIDSet ); + $full_path = $this->tprojectMgr->tree_manager->get_full_path_verbose( + $nodeIDSet); } return $status_ok ? $full_path : $this->errors; } /** */ - protected function _insertCustomFieldExecValues($executionID) { + protected function _insertCustomFieldExecValues($executionID) + { // // Check for existence of executionID $status_ok = true; $sql = "SELECT id FROM {$this->tables['executions']} WHERE id={$executionID}"; - $rs = $this->dbObj->fetchRowsIntoMap( $sql, 'id' ); + $rs = $this->dbObj->fetchRowsIntoMap($sql, 'id'); // $cfieldSet = $this->args[self::$customFieldsParamName]; - $tprojectID = $this->tcaseMgr->get_testproject( $this->args[self::$testCaseIDParamName] ); + $tprojectID = $this->tcaseMgr->get_testproject( + $this->args[self::$testCaseIDParamName]); $tplanID = $this->args[self::$testPlanIDParamName]; $cfieldMgr = $this->tprojectMgr->cfield_mgr; - $cfieldsMap = $cfieldMgr->get_linked_cfields_at_execution( $tprojectID, 1, 'testcase', null, null, null, 'name' ); - $status_ok = !(is_null( $rs ) || is_null( $cfieldSet ) || count( $cfieldSet ) == 0); + $cfieldsMap = $cfieldMgr->get_linked_cfields_at_execution($tprojectID, 1, + 'testcase', null, null, null, 'name'); + $status_ok = ! (is_null($rs) || is_null($cfieldSet) || + count($cfieldSet) == 0); $cfield4write = null; - if($status_ok && ! is_null( $cfieldsMap )) { - foreach( $cfieldSet as $name => $value ) { - if(isset( $cfieldsMap[$name] )) { + if ($status_ok && ! is_null($cfieldsMap)) { + foreach ($cfieldSet as $name => $value) { + if (isset($cfieldsMap[$name])) { $cfield4write[$cfieldsMap[$name]['id']] = array( - "type_id" => $cfieldsMap[$name]['type'], - "cf_value" => $value + "type_id" => $cfieldsMap[$name]['type'], + "cf_value" => $value ); } } - if(! is_null( $cfield4write )) { - $cfieldMgr->execution_values_to_db( $cfield4write, $this->tcVersionID, $executionID, $tplanID, null, 'write-through' ); + if (! is_null($cfield4write)) { + $cfieldMgr->execution_values_to_db($cfield4write, + $this->tcVersionID, $executionID, $tplanID, null, + 'write-through'); } } return $status_ok; @@ -4332,38 +5381,42 @@ protected function _insertCustomFieldExecValues($executionID) { * @param int $args["executionid"] * * @return mixed $resultInfo - * [status] => true/false of success - * [id] => result id or error code - * [message] => optional message for error message string + * [status] => true/false of success + * [id] => result id or error code + * [message] => optional message for error message string * @access public */ - public function deleteExecution($args) { + public function deleteExecution($args) + { $resultInfo = array(); $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo[0]["status"] = false; $checkFunctions = array( - 'authenticate', - 'checkExecutionID' + 'authenticate', + 'checkExecutionID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); // missing : // we need to get Context => Test plan & Test project to understand if // user has the right to do this operation - if($status_ok) { - if($this->userHasRight( "exec_delete", self::CHECK_PUBLIC_PRIVATE_ATTR )) { - $this->tcaseMgr->deleteExecution( $args[self::$executionIDParamName] ); + if ($status_ok) { + if ($this->userHasRight("exec_delete", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { + $this->tcaseMgr->deleteExecution( + $args[self::$executionIDParamName]); $resultInfo[0]["status"] = true; $resultInfo[0]["id"] = $args[self::$executionIDParamName]; $resultInfo[0]["message"] = GENERAL_SUCCESS_STR; $resultInfo[0]["operation"] = $operation; } else { $status_ok = false; - $this->errors[] = new IXR_Error( CFG_DELETE_EXEC_DISABLED, CFG_DELETE_EXEC_DISABLED_STR ); + $this->errors[] = new IXR_Error(CFG_DELETE_EXEC_DISABLED, + CFG_DELETE_EXEC_DISABLED_STR); } } @@ -4371,26 +5424,32 @@ public function deleteExecution($args) { } /** - * Helper method to see if an execution id exists on DB - * no checks regarding other data like test case , test plam, build, etc are done + * Helper method to see if the provided execution id is valid + * no checks regarding other data like test case, test plan, build, etc are done * * * * @return boolean * @access protected */ - protected function checkExecutionID($messagePrefix = '', $setError = false) { + protected function checkExecutionID($messagePrefix = '', $setError = false) + { $pname = self::$executionIDParamName; - $status_ok = $this->_isParamPresent( $pname, $messagePrefix, $setError ); - if(! $status_ok) { - $msg = $messagePrefix . sprintf( MISSING_REQUIRED_PARAMETER_STR, $pname ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); + $status_ok = $this->_isParamPresent($pname, $messagePrefix, $setError); + if (! $status_ok) { + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, $pname); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } else { - $status_ok = is_int( $this->args[$pname] ) && $this->args[$pname] > 0; - if(! $status_ok) { - $msg = $messagePrefix . sprintf( PARAMETER_NOT_INT_STR, $pname, $this->args[$pname] ); - $this->errors[] = new IXR_Error( PARAMETER_NOT_INT, $msg ); - } else { + if (gettype($this->args[$pname]) == "string" && + intval($this->args[$pname])) { + $this->args[$pname] = intval($this->args[$pname]); + } + $status_ok = is_int($this->args[$pname]) && $this->args[$pname] > 0; + if (! $status_ok) { + $msg = $messagePrefix . + sprintf(PARAMETER_NOT_INT_STR, $pname, $this->args[$pname]); + $this->errors[] = new IXR_Error(PARAMETER_NOT_INT, $msg); } } return $status_ok; @@ -4404,7 +5463,7 @@ protected function checkExecutionID($messagePrefix = '', $setError = false) { * * @param int $tplanID * Test Plan ID - * @param map $platformInfo + * @param array $platformInfo * key: platform ID * @param string $messagePrefix * used to be prepended to error message @@ -4413,61 +5472,71 @@ protected function checkExecutionID($messagePrefix = '', $setError = false) { * @return boolean * @access protected */ - protected function checkPlatformIdentity($tplanID, $platformInfo = null, $messagePrefix = '') { + protected function checkPlatformIdentity($tplanID, $platformInfo = null, + $messagePrefix = '') + { $status = true; - $platformID = 0; - $name_exists = $this->_isParamPresent( self::$platformNameParamName, $messagePrefix ); - $id_exists = $this->_isParamPresent( self::$platformIDParamName, $messagePrefix ); + $name_exists = $this->_isParamPresent(self::$platformNameParamName, + $messagePrefix); + $id_exists = $this->_isParamPresent(self::$platformIDParamName, + $messagePrefix); $status = $name_exists | $id_exists; - if(! $status) { - $pname = self::$platformNameParamName . ' OR ' . self::$platformIDParamName; - $msg = $messagePrefix . sprintf( MISSING_REQUIRED_PARAMETER_STR, $pname ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); + if (! $status) { + $pname = self::$platformNameParamName . ' OR ' . + self::$platformIDParamName; + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, $pname); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } - if($status) { + if ($status) { // get test plan name is useful for error messages - $tplanInfo = $this->tplanMgr->get_by_id( $tplanID ); - if(is_null( $platformInfo )) { - $platformInfo = $this->tplanMgr->getPlatforms( $tplanID, array( + $tplanInfo = $this->tplanMgr->get_by_id($tplanID); + if (is_null($platformInfo)) { + $platformInfo = $this->tplanMgr->getPlatforms($tplanID, + array( 'outputFormat' => 'map' - ) ); + )); } - if(is_null( $platformInfo )) { + if (is_null($platformInfo)) { $status = false; - $msg = sprintf( $messagePrefix . TESTPLAN_HAS_NO_PLATFORMS_STR, $tplanInfo['name'] ); - $this->errors[] = new IXR_Error( TESTPLAN_HAS_NO_PLATFORMS, $msg ); + $msg = sprintf($messagePrefix . TESTPLAN_HAS_NO_PLATFORMS_STR, + $tplanInfo['name']); + $this->errors[] = new IXR_Error(TESTPLAN_HAS_NO_PLATFORMS, $msg); } } - if($status) { + if ($status) { $platform_name = null; $platform_id = null; - if($name_exists) { + if ($name_exists) { $this->errors[] = $platformInfo; $platform_name = $this->args[self::$platformNameParamName]; - $status = in_array( $this->args[self::$platformNameParamName], $platformInfo ); + $status = in_array($this->args[self::$platformNameParamName], + $platformInfo); } else { $platform_id = $this->args[self::$platformIDParamName]; - $status = isset( $platformInfo[$this->args[self::$platformIDParamName]] ); + $status = isset( + $platformInfo[$this->args[self::$platformIDParamName]]); } - if(! $status) { + if (! $status) { // Platform does not exist in target testplan // Can I Try to understand if platform exists on test project ? - $msg = sprintf( $messagePrefix . PLATFORM_NOT_LINKED_TO_TESTPLAN_STR, $platform_name, $platform_id, $tplanInfo['name'] ); - $this->errors[] = new IXR_Error( PLATFORM_NOT_LINKED_TO_TESTPLAN, $msg ); + $msg = sprintf( + $messagePrefix . PLATFORM_NOT_LINKED_TO_TESTPLAN_STR, + $platform_name, $platform_id, $tplanInfo['name']); + $this->errors[] = new IXR_Error(PLATFORM_NOT_LINKED_TO_TESTPLAN, + $msg); } } - if($status) { - if($name_exists) { - $dummy = array_flip( $platformInfo ); - $this->args[self::$platformIDParamName] = $dummy[$this->args[self::$platformNameParamName]]; - } + if ($status && $name_exists) { + $dummy = array_flip($platformInfo); + $this->args[self::$platformIDParamName] = $dummy[$this->args[self::$platformNameParamName]]; } return $status; } @@ -4490,9 +5559,10 @@ protected function checkPlatformIdentity($tplanID, $platformInfo = null, $messag * * @access protected */ - protected function _updateResult($user_id = null, $exec_ts = null) { - $tester_id = is_null( $user_id ) ? $this->userID : $user_id; - $execTimeStamp = is_null( $exec_ts ) ? $this->dbObj->db_now() : $exec_ts; + protected function _updateResult($user_id = null, $exec_ts = null) + { + $tester_id = is_null($user_id) ? $this->userID : $user_id; + $execTimeStamp = is_null($exec_ts) ? $this->dbObj->db_now() : $exec_ts; $exec_id = 0; $status = $this->args[self::$statusParamName]; @@ -4501,51 +5571,50 @@ protected function _updateResult($user_id = null, $exec_ts = null) { // $testplan_id = $this->args[self::$testPlanIDParamName]; // $build_id = $this->args[self::$buildIDParamName]; - $tcversion_id = $this->tcVersionID; $tcase_id = $this->args[self::$testCaseIDParamName]; $execContext = array( - 'tplan_id' => $this->args[self::$testPlanIDParamName], - 'platform_id' => $this->args[self::$platformIDParamName], - 'build_id' => $this->args[self::$buildIDParamName] + 'tplan_id' => $this->args[self::$testPlanIDParamName], + 'platform_id' => $this->args[self::$platformIDParamName], + 'build_id' => $this->args[self::$buildIDParamName] ); - // $db_now=$this->dbObj->db_now(); - - if(isset( $this->args[self::$platformIDParamName] )) { - $platform_id = $this->args[self::$platformIDParamName]; - } - // Here steps and expected results are not needed => do not request => less data on network // $options = array('getSteps' => 0); $opt = array( - 'output' => 'exec_id' + 'output' => 'exec_id' ); - $exec_id = $this->tcaseMgr->getLatestExecSingleContext( array( + $exec_id = $this->tcaseMgr->getLatestExecSingleContext( + array( 'id' => $tcase_id, 'version_id' => null - ), $execContext, $opt ); - if(! is_null( $exec_id )) { - $execution_type = constant( "TESTCASE_EXECUTION_TYPE_AUTO" ); + ), $execContext, $opt); + if (! is_null($exec_id)) { + $execution_type = constant("TESTCASE_EXECUTION_TYPE_AUTO"); $notes = ''; $notes_update = ''; - if($this->_isNotePresent()) { - $notes = $this->dbObj->prepare_string( $this->args[self::$noteParamName] ); + if ($this->_isNotePresent()) { + $notes = $this->dbObj->prepare_string( + $this->args[self::$noteParamName]); } - if(trim( $notes ) != "") { + if (trim($notes) != "") { $notes_update = ",notes='{$notes}'"; } $duration_update = ''; - if(isset( $this->args[self::$executionDurationParamName] )) { - $duration_update = ",execution_duration=" . floatval( $this->args[self::$executionDurationParamName] ); + if (isset($this->args[self::$executionDurationParamName])) { + $duration_update = ",execution_duration=" . + floatval($this->args[self::$executionDurationParamName]); } - $sql = " UPDATE {$this->tables['executions']} " . " SET tester_id={$tester_id}, execution_ts={$execTimeStamp}," . " status='{$status}', execution_type= {$execution_type} " . " {$notes_update} {$duration_update} WHERE id = {$exec_id}"; + $sql = " UPDATE {$this->tables['executions']} " . + " SET tester_id={$tester_id}, execution_ts={$execTimeStamp}," . + " status='{$status}', execution_type= {$execution_type} " . + " {$notes_update} {$duration_update} WHERE id = {$exec_id}"; - $this->dbObj->exec_query( $sql ); + $this->dbObj->exec_query($sql); } return $exec_id; } @@ -4561,26 +5630,23 @@ protected function _updateResult($user_id = null, $exec_ts = null) { * * @access public */ - public function getTestSuiteByID($args) { + public function getTestSuiteByID($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); - $status_ok = $this->_runChecks( array( + $this->_setArgs($args); + $status_ok = $this->_runChecks( + array( 'authenticate', 'checkTestSuiteID' - ), $msg_prefix ); - - $details = 'simple'; - $key2search = self::$detailsParamName; - if($this->_isParamPresent( $key2search )) { - $details = $this->args[$key2search]; - } + ), $msg_prefix); - if($status_ok && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + if ($status_ok && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR)) { $testSuiteID = $this->args[self::$testSuiteIDParamName]; - $tsuiteMgr = new testsuite( $this->dbObj ); - return $tsuiteMgr->get_by_id( $testSuiteID ); + $tsuiteMgr = new testsuite($this->dbObj); + return $tsuiteMgr->get_by_id($testSuiteID); } else { return $this->errors; } @@ -4596,20 +5662,23 @@ public function getTestSuiteByID($args) { * * @access public */ - public function getTestSuitesForTestSuite($args) { + public function getTestSuitesForTestSuite($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $items = null; - $this->_setArgs( $args ); - $status_ok = $this->_runChecks( array( + $this->_setArgs($args); + $status_ok = $this->_runChecks( + array( 'authenticate', 'checkTestSuiteID' - ), $msg_prefix ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); - if($status_ok) { + ), $msg_prefix) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); + if ($status_ok) { $testSuiteID = $this->args[self::$testSuiteIDParamName]; - $tsuiteMgr = new testsuite( $this->dbObj ); - $items = $tsuiteMgr->get_children( $testSuiteID ); + $tsuiteMgr = new testsuite($this->dbObj); + $items = $tsuiteMgr->get_children($testSuiteID); } return $status_ok ? $items : $this->errors; } @@ -4625,31 +5694,35 @@ public function getTestSuitesForTestSuite($args) { * * @access public */ - public function getTestPlanPlatforms($args) { + public function getTestPlanPlatforms($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $status_ok = false; $items = null; // Checks if a test plan id was provided - $status_ok = $this->_isParamPresent( self::$testPlanIDParamName, $msg_prefix, self::SET_ERROR ); + $status_ok = $this->_isParamPresent(self::$testPlanIDParamName, + $msg_prefix, self::SET_ERROR); - if($status_ok) { + if ($status_ok) { // Checks if the provided test plan id is valid - $status_ok = $this->_runChecks( array( + $status_ok = $this->_runChecks( + array( 'authenticate', 'checkTestPlanID' - ), $msg_prefix ); + ), $msg_prefix); } - if($status_ok) { + if ($status_ok) { $tplanID = $this->args[self::$testPlanIDParamName]; // get test plan name is useful for error messages - $tplanInfo = $this->tplanMgr->get_by_id( $tplanID ); - $items = $this->tplanMgr->getPlatforms( $tplanID ); - if(!($status_ok = ! is_null( $items ))) { - $msg = sprintf( $messagePrefix . TESTPLAN_HAS_NO_PLATFORMS_STR, $tplanInfo['name'] ); - $this->errors[] = new IXR_Error( TESTPLAN_HAS_NO_PLATFORMS, $msg ); + $tplanInfo = $this->tplanMgr->get_by_id($tplanID); + $items = $this->tplanMgr->getPlatforms($tplanID); + if (! ($status_ok = ! is_null($items))) { + $msg = sprintf($messagePrefix . TESTPLAN_HAS_NO_PLATFORMS_STR, + $tplanInfo['name']); + $this->errors[] = new IXR_Error(TESTPLAN_HAS_NO_PLATFORMS, $msg); } } return $status_ok ? $items : $this->errors; @@ -4665,7 +5738,7 @@ public function getTestPlanPlatforms($args) { * @param int $args["testplanid"] * test plan id * - * @return map where every element has: + * @return array where every element has: * * 'type' => 'platform' * 'total_tc => ZZ @@ -4676,24 +5749,26 @@ public function getTestPlanPlatforms($args) { * * @access public */ - public function getTotalsForTestPlan($args) { + public function getTotalsForTestPlan($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $total = null; - $this->_setArgs( $args ); + $this->_setArgs($args); $status_ok = true; // Checks are done in order $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); - if($status_ok) { - // $total = $this->tplanMgr->getStatusTotalsByPlatform($this->args[self::$testPlanIDParamName]); - $total = $this->tplanMetricsMgr->getExecCountersByPlatformExecStatus( $this->args[self::$testPlanIDParamName] ); + if ($status_ok) { + $total = $this->tplanMetricsMgr->getExecCountersByPlatformExecStatus( + $this->args[self::$testPlanIDParamName]); } return $status_ok ? $total : $this->errors; @@ -4710,19 +5785,156 @@ public function getTotalsForTestPlan($args) { * * @access public */ - public function doesUserExist($args) { + public function doesUserExist($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); - $user_id = tlUser::doesUserExist( $this->dbObj, $this->args[self::$userParamName] ); - if(!($status_ok = ! is_null( $user_id ))) { - $msg = $msg_prefix . sprintf( NO_USER_BY_THIS_LOGIN_STR, $this->args[self::$userParamName] ); - $this->errors[] = new IXR_Error( NO_USER_BY_THIS_LOGIN, $msg ); + $user_id = tlUser::doesUserExist($this->dbObj, + $this->args[self::$userParamName]); + if (! ($status_ok = ! is_null($user_id))) { + $msg = $msg_prefix . + sprintf(NO_USER_BY_THIS_LOGIN_STR, + $this->args[self::$userParamName]); + $this->errors[] = new IXR_Error(NO_USER_BY_THIS_LOGIN, $msg); } return $status_ok ? $status_ok : $this->errors; } + /** + * Create a new user + * + * Restricted to site admin + * + * @param struct $args + * @param string $args["devKey"] + * @param string $args["login"] + * @param string $args["firstname"] + * @param string $args["lastname"] + * @param string $args["email"] + * @param string $args["password"] + * - OPTIONAL + * + * + * @return ID the new user if OK, otherwise error structure + * + * @access public + */ + public function createUser($args) + { + $msg_prefix = "(" . __FUNCTION__ . ") - "; + $status_ok = true; + $this->_setArgs($args); + + $checkFunctions = array( + 'authenticate', + 'checkIsSystemWideAdmin' + ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + + $password = null; + if ($status_ok && isset($this->args[self::$userPasswordParamName])) { + $password = $this->args[self::$userPasswordParamName]; + $res = $this->userMgr->checkPasswordQuality($password); + if ($res['status_ok'] == tl::ERROR) { + $status_ok = false; + $this->errors[] = new IXR_Error(GENERAL_ERROR_CODE, $res['msg']); + } + } + + if ($status_ok) { + $res = tl::OK; + + if ($password) { + $res = $this->userMgr->setPassword($password); + } + + if ($res == tl::OK) { + $this->userMgr->dbID = null; + $this->userMgr->login = $this->args[self::$userLoginParamName]; + $this->userMgr->firstName = $this->args[self::$userFirstNameParamName]; + $this->userMgr->lastName = $this->args[self::$userLastNameParamName]; + $this->userMgr->emailAddress = $this->args[self::$userEmailParamName]; + $res = $this->userMgr->writeToDB($this->dbObj); + } + + if ($res != tl::OK) { + $status_ok = false; + $msg = getUserErrorMessage($res); + $this->errors[] = new IXR_Error(USER_CREATION_ERROR, $msg); + } else { + logAuditEvent(TLS("audit_user_created", $this->userMgr->login), + "CREATE", $this->userMgr->dbID, "users"); + } + } + + return $status_ok ? $this->userMgr->dbID : $this->errors; + } + + /** + * Set a role to a user at project level + * + * Restricted to users with System Wide Role Admin + * + * @param struct $args + * @param struct $args["userid"] + * @param struct $args["rolename"] + * @param struct $args["testprojectid"] + * + * @return true if OK, otherwise error structure + * + * @access public + */ + public function setUserRoleOnProject($args) + { + $msg_prefix = "(" . __FUNCTION__ . ") - "; + $status_ok = true; + $this->_setArgs($args); + + $checkFunctions = array( + 'authenticate', + 'checkIsSystemWideAdmin', + 'checkTestProjectID', + 'checkUserID', + 'checkRoleIdentity' + ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + + if ($status_ok) { + + $userID = $this->args[self::$userIDParamName]; + $projectID = $this->args[self::$testProjectIDParamName]; + + $role = $this->args[self::$roleNameParamName]; + $roleID = tlRole::doesRoleExist($this->dbObj, $role, - 1); + // role exists. It was checked by checkRoleIdentity + + // delete existing user's role if any before adding the new one + $this->userMgr->dbID = $userID; + $this->userMgr->readTestProjectRoles($this->dbObj, $projectID); + if (isset($this->userMgr->tprojectRoles[$projectID])) { + if ($this->userMgr->tprojectRoles[$projectID]->dbID == $roleID) { + // user already has given role -> OK nothing to do + return true; + } + $this->tprojectMgr->deleteUserRoles($projectID, array( + $userID + )); + } + + $res = $this->tprojectMgr->addUserRole($userID, $projectID, $roleID); + if ($res != tl::OK) { + $status_ok = false; + $msg = $msg_prefix . + sprintf(ROLE_SETTING_ERROR_STR, $role, $userID); + $this->errors[] = new IXR_Error(ROLE_SETTING_ERROR, $msg); + } + } + + return $status_ok ? true : $this->errors; + } + /** * check if Developer Key exists. * @@ -4733,14 +5945,15 @@ public function doesUserExist($args) { * * @access public */ - public function checkDevKey($args) { + public function checkDevKey($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate' + 'authenticate' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); return $status_ok ? $status_ok : $this->errors; } @@ -4755,9 +5968,9 @@ public function checkDevKey($args) { * @param int $args["reqspecid"] * The Requirement Specification ID * @param string $args["title"] - * (Optional) The title of the Attachment + * (Optional) The title of the Attachment * @param string $args["description"] - * (Optional) The description of the Attachment + * (Optional) The description of the Attachment * @param string $args["filename"] * The file name of the Attachment(e.g.:notes.txt) * @param string $args["filetype"] @@ -4770,12 +5983,13 @@ public function checkDevKey($args) { * description, file_name, file_size and file_type. If any errors occur it * returns the error map. */ - public function uploadRequirementSpecificationAttachment($args) { + public function uploadRequirementSpecificationAttachment($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $args[self::$foreignKeyTableNameParamName] = 'req_specs'; $args[self::$foreignKeyIdParamName] = $args['reqspecid']; - $this->_setArgs( $args ); - return $this->uploadAttachment( $args, $msg_prefix, false ); + $this->_setArgs($args); + return $this->uploadAttachment($args, $msg_prefix, false); } /** @@ -4789,9 +6003,9 @@ public function uploadRequirementSpecificationAttachment($args) { * @param int $args["requirementid"] * The Requirement ID * @param string $args["title"] - * (Optional) The title of the Attachment + * (Optional) The title of the Attachment * @param string $args["description"] - * (Optional) The description of the Attachment + * (Optional) The description of the Attachment * @param string $args["filename"] * The file name of the Attachment(e.g.:notes.txt) * @param string $args["filetype"] @@ -4804,12 +6018,13 @@ public function uploadRequirementSpecificationAttachment($args) { * description, file_name, file_size and file_type. If any errors occur it * returns the erros map. */ - public function uploadRequirementAttachment($args) { + public function uploadRequirementAttachment($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $args[self::$foreignKeyTableNameParamName] = 'requirements'; $args[self::$foreignKeyIdParamName] = $args['requirementid']; - $this->_setArgs( $args ); - return $this->uploadAttachment( $args, $msg_prefix, false ); + $this->_setArgs($args); + return $this->uploadAttachment($args, $msg_prefix, false); } /** @@ -4823,9 +6038,9 @@ public function uploadRequirementAttachment($args) { * @param int $args["testprojectid"] * The Test Project ID * @param string $args["title"] - * (Optional) The title of the Attachment + * (Optional) The title of the Attachment * @param string $args["description"] - * (Optional) The description of the Attachment + * (Optional) The description of the Attachment * @param string $args["filename"] * The file name of the Attachment(e.g.:notes.txt) * @param string $args["filetype"] @@ -4838,21 +6053,21 @@ public function uploadRequirementAttachment($args) { * description, file_name, file_size and file_type. If any errors occur it * returns the erros map. */ - public function uploadTestProjectAttachment($args) { + public function uploadTestProjectAttachment($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $ret = null; $args[self::$foreignKeyTableNameParamName] = 'nodes_hierarchy'; $args[self::$foreignKeyIdParamName] = $args[self::$testProjectIDParamName]; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID' + 'authenticate', + 'checkTestProjectID' ); - $statusOK = $this->_runChecks( $checkFunctions ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); - $ret = $statusOK ? $this->uploadAttachment( $args, $msg_prefix, false ) : $this->errors; - return $ret; + $statusOK = $this->_runChecks($checkFunctions) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); + return $statusOK ? $this->uploadAttachment($args, $msg_prefix, false) : $this->errors; } /** @@ -4866,9 +6081,9 @@ public function uploadTestProjectAttachment($args) { * @param int $args["testsuiteid"] * The Test Suite ID * @param string $args["title"] - * (Optional) The title of the Attachment + * (Optional) The title of the Attachment * @param string $args["description"] - * (Optional) The description of the Attachment + * (Optional) The description of the Attachment * @param string $args["filename"] * The file name of the Attachment(e.g.:notes.txt) * @param string $args["filetype"] @@ -4881,19 +6096,20 @@ public function uploadTestProjectAttachment($args) { * description, file_name, file_size and file_type. If any errors occur it * returns the erros map. */ - public function uploadTestSuiteAttachment($args) { + public function uploadTestSuiteAttachment($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $args[self::$foreignKeyTableNameParamName] = 'nodes_hierarchy'; $args[self::$foreignKeyIdParamName] = $args[self::$testSuiteIDParamName]; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestSuiteID' + 'authenticate', + 'checkTestSuiteID' ); - $statusOK = $this->_runChecks( $checkFunctions ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); - $ret = $statusOK ? $this->uploadAttachment( $args, $msg_prefix, false ) : $this->errors; - return $ret; + $statusOK = $this->_runChecks($checkFunctions) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); + return $statusOK ? $this->uploadAttachment($args, $msg_prefix, false) : $this->errors; } /** @@ -4910,9 +6126,9 @@ public function uploadTestSuiteAttachment($args) { * version number * * @param string $args["title"] - * (Optional) The title of the Attachment + * (Optional) The title of the Attachment * @param string $args["description"] - * (Optional) The description of the Attachment + * (Optional) The description of the Attachment * @param string $args["filename"] * The file name of the Attachment(e.g.:notes.txt) * @param string $args["filetype"] @@ -4924,41 +6140,43 @@ public function uploadTestSuiteAttachment($args) { * description, file_name, file_size and file_type. If any errors occur it * returns the erros map. */ - public function uploadTestCaseAttachment($args) { - $ret = null; + public function uploadTestCaseAttachment($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); // Mandatory Parameters $prm = self::$versionNumberParamName; - $statusOK = $this->_isParamPresent( $prm, $msgPrefix, self::SET_ERROR ); + $statusOK = $this->_isParamPresent($prm, $msgPrefix, self::SET_ERROR); - if($statusOK) { - $checkFunctions = array('authenticate', - 'checkTestCaseIdentity', - 'checkTestCaseVersionNumberAncestry'); + if ($statusOK) { + $checkFunctions = array( + 'authenticate', + 'checkTestCaseIdentity', + 'checkTestCaseVersionNumberAncestry' + ); } - if($statusOK = $this->_runChecks( $checkFunctions, $msg_prefix )) { + if ($statusOK = $this->_runChecks($checkFunctions, $msg_prefix)) { - $args[self::$foreignKeyTableNameParamName] = 'tcversions'; - $args[self::$foreignKeyIdParamName] = $this->tcVersionID; - $this->_setArgs( $args ); - // Need to get test project information - // from test case in order to be able - // to do RIGHTS check on $this->userHasRight() - // !!! Important Notice!!!!: - // method checkTestCaseIdentity sets $this->args[self::$testCaseIDParamName] + $args[self::$foreignKeyTableNameParamName] = 'tcversions'; + $args[self::$foreignKeyIdParamName] = $this->tcVersionID; + $this->_setArgs($args); + // Need to get test project information + // from test case in order to be able + // to do RIGHTS check on $this->userHasRight() + // !!! Important Notice!!!!: + // method checkTestCaseIdentity sets $this->args[self::$testCaseIDParamName] - $this->args[self::$testProjectIDParamName] = - $this->tcaseMgr->getTestProjectFromTestCase( $this->args[self::$testCaseIDParamName] ); + $this->args[self::$testProjectIDParamName] = $this->tcaseMgr->getTestProjectFromTestCase( + $this->args[self::$testCaseIDParamName]); - $statusOK = $this->userHasRight( "mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $statusOK = $this->userHasRight("mgt_modify_tc", + self::CHECK_PUBLIC_PRIVATE_ATTR); } - $ret = $statusOK ? $this->uploadAttachment( $args, $msg_prefix, false ) : $this->errors; - return $ret; + return $statusOK ? $this->uploadAttachment($args, $msg_prefix, false) : $this->errors; } /** @@ -4972,9 +6190,9 @@ public function uploadTestCaseAttachment($args) { * @param int $args["executionid"] * execution ID * @param string $args["title"] - * (Optional) The title of the Attachment + * (Optional) The title of the Attachment * @param string $args["description"] - * (Optional) The description of the Attachment + * (Optional) The description of the Attachment * @param string $args["filename"] * The file name of the Attachment(e.g.:notes.txt) * @param string $args["filetype"] @@ -4987,16 +6205,17 @@ public function uploadTestCaseAttachment($args) { * description, file_name, file_size and file_type. If any errors occur it * returns the erros map. */ - public function uploadExecutionAttachment($args) { + public function uploadExecutionAttachment($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; $args[self::$foreignKeyTableNameParamName] = 'executions'; $args[self::$foreignKeyIdParamName] = $args['executionid']; - $this->_setArgs( $args ); + $this->_setArgs($args); // We need to check that user has right to execute in order to allow // him/her to do attachment - return $this->uploadAttachment( $args, $msg_prefix, false ); + return $this->uploadAttachment($args, $msg_prefix, false); } /** @@ -5015,9 +6234,9 @@ public function uploadExecutionAttachment($args) { * @param string $args["fktable"] * The Attachment Foreign Key Table * @param string $args["title"] - * (Optional) The title of the Attachment + * (Optional) The title of the Attachment * @param string $args["description"] - * (Optional) The description of the Attachment + * (Optional) The description of the Attachment * @param string $args["filename"] * The file name of the Attachment(e.g.:notes.txt) * @param string $args["filetype"] @@ -5025,31 +6244,32 @@ public function uploadExecutionAttachment($args) { * @param string $args["content"] * The content(Base64 encoded) of the Attachment * - * @return mixed $resultInfo an array containing + * @return mixed $resultInfo an array containing * the fk_id, fk_table, title, - * description, file_name, file_size and file_type. + * description, file_name, file_size and file_type. * If any errors occur it * returns the errors map. */ - public function uploadAttachment($args, $messagePrefix = '', $setArgs = true) { + public function uploadAttachment($args, $messagePrefix = '', $setArgs = true) + { $resultInfo = array(); - if($setArgs) { - $this->_setArgs( $args ); + if ($setArgs) { + $this->_setArgs($args); } - $msg_prefix =($messagePrefix == '') ?("(" . __FUNCTION__ . ") - ") : $messagePrefix; + $msg_prefix = ($messagePrefix == '') ? ("(" . __FUNCTION__ . ") - ") : $messagePrefix; $checkFunctions = array(); - // TODO: please, somebody review if this is valid. + // TODO: please, somebody review if this is valid. // I added this property // to avoid the upload method of double authenticating the user. - // Otherwise, when uploadTestCaseAttachment was called, + // Otherwise, when uploadTestCaseAttachment was called, // for instante, it // would authenticate, check if the nodes_hierarchy is type TestCase // and then call uploadAttachment that would, authenticate again. // What do you think? - if(!$this->authenticated) { - $checkFunctions[] = 'authenticate'; + if (! $this->authenticated) { + $checkFunctions[] = 'authenticate'; } // check if : @@ -5060,54 +6280,55 @@ public function uploadAttachment($args, $messagePrefix = '', $setArgs = true) { $checkFunctions[] = 'checkForeignKey'; $checkFunctions[] = 'checkUploadAttachmentRequest'; - $statusOK = $this->_runChecks( $checkFunctions, $msg_prefix ); - if($statusOK) { - - $fkId = $this->args[self::$foreignKeyIdParamName]; - $fkTable = $this->args[self::$foreignKeyTableNameParamName]; - $title = $this->args[self::$titleParamName]; - - // creates a temp file and returns an array with size and tmp_name - $fInfo = $this->createAttachmentTempFile(); - if(!$fInfo) { - // Error creating attachment temp file. Ask user to check temp dir - // settings in php.ini and security and rights of this dir. - $msg = $msg_prefix . ATTACH_TEMP_FILE_CREATION_ERROR_STR; - $this->errors[] = new IXR_ERROR( ATTACH_TEMP_FILE_CREATION_ERROR, $msg ); - $statusOK = false; - } else { - // The values have already been validated in the method - // checkUploadAttachmentRequest() - $fInfo['name'] = $args[self::$fileNameParamName]; - $fInfo['type'] = $args[self::$fileTypeParamName]; - if( trim($fInfo['type']) == '' ) { - $fInfo['type'] = mime_content_type($fInfo['tmp_name']); - } - - $docRepo = tlAttachmentRepository::create( $this->dbObj ); - $uploadOp = $docRepo->insertAttachment( $fkId, $fkTable, $title, $fInfo ); - - - if($uploaOp->statusOK == false) { - $msg = $msg_prefix . ATTACH_DB_WRITE_ERROR_STR; - $this->errors[] = new IXR_ERROR( ATTACH_DB_WRITE_ERROR, $msg ); - $statusOK = false; + $statusOK = $this->_runChecks($checkFunctions, $msg_prefix); + if ($statusOK) { + + $fkId = $this->args[self::$foreignKeyIdParamName]; + $fkTable = $this->args[self::$foreignKeyTableNameParamName]; + $title = $this->args[self::$titleParamName]; + + // creates a temp file and returns an array with size and tmp_name + $fInfo = $this->createAttachmentTempFile(); + if (! $fInfo) { + // Error creating attachment temp file. Ask user to check temp dir + // settings in php.ini and security and rights of this dir. + $msg = $msg_prefix . ATTACH_TEMP_FILE_CREATION_ERROR_STR; + $this->errors[] = new IXR_ERROR(ATTACH_TEMP_FILE_CREATION_ERROR, + $msg); + $statusOK = false; } else { - // We are returning some data that the user originally sent. - // Perhaps we could return only new data, like the file size? - $resultInfo['fk_id'] = $args[self::$foreignKeyIdParamName]; - $resultInfo['fk_table'] = $args[self::$foreignKeyTableNameParamName]; - $resultInfo['title'] = $args[self::$titleParamName]; - $resultInfo['description'] = $args[self::$descriptionParamName]; - $resultInfo['file_name'] = $args[self::$fileNameParamName]; + // The values have already been validated in the method + // checkUploadAttachmentRequest() + $fInfo['name'] = $args[self::$fileNameParamName]; + $fInfo['type'] = $args[self::$fileTypeParamName]; + if (trim($fInfo['type']) == '') { + $fInfo['type'] = mime_content_type($fInfo['tmp_name']); + } - // It would be nice have all info available in db - // $resultInfo['file_path'] = $args[""]; - // we could also return the tmp_name, but would it be useful? - $resultInfo['file_size'] = $fInfo['size']; - $resultInfo['file_type'] = $args[self::$fileTypeParamName]; + $docRepo = tlAttachmentRepository::create($this->dbObj); + $uploadOp = $docRepo->insertAttachment($fkId, $fkTable, $title, + $fInfo); + + if (! $uploadOp->statusOK) { + $msg = $msg_prefix . ATTACH_DB_WRITE_ERROR_STR; + $this->errors[] = new IXR_ERROR(ATTACH_DB_WRITE_ERROR, $msg); + $statusOK = false; + } else { + // We are returning some data that the user originally sent. + // Perhaps we could return only new data, like the file size? + $resultInfo['fk_id'] = $args[self::$foreignKeyIdParamName]; + $resultInfo['fk_table'] = $args[self::$foreignKeyTableNameParamName]; + $resultInfo['title'] = $args[self::$titleParamName]; + $resultInfo['description'] = $args[self::$descriptionParamName]; + $resultInfo['file_name'] = $args[self::$fileNameParamName]; + + // It would be nice have all info available in db + // $resultInfo['file_path'] = $args[""]; + // we could also return the tmp_name, but would it be useful? + $resultInfo['file_size'] = $fInfo['size']; + $resultInfo['file_type'] = $args[self::$fileTypeParamName]; + } } - } } return $statusOK ? $resultInfo : $this->errors; @@ -5121,11 +6342,12 @@ public function uploadAttachment($args, $messagePrefix = '', $setArgs = true) { * @return boolean true if attachments feature is enabled in TestLink * configuration, false otherwise. */ - protected function isAttachmentEnabled($msg_prefix = '') { + protected function isAttachmentEnabled($msg_prefix = '') + { $status_ok = true; - if(! config_get( "attachments" )->enabled) { + if (! config_get("attachments")->enabled) { $msg = $msg_prefix . ATTACH_FEATURE_DISABLED_STR; - $this->errors[] = new IXR_ERROR( ATTACH_FEATURE_DISABLED, $msg ); + $this->errors[] = new IXR_ERROR(ATTACH_FEATURE_DISABLED, $msg); $status_ok = false; } return $status_ok; @@ -5140,23 +6362,23 @@ protected function isAttachmentEnabled($msg_prefix = '') { * @since 1.9beta6 * @return boolean true if the given foreign key exists, false otherwise. */ - protected function checkForeignKey($msg_prefix = '') { + protected function checkForeignKey($msg_prefix = '') + { $statusOK = true; - $fkId = $this->args[self::$foreignKeyIdParamName]; $fkTable = $this->args[self::$foreignKeyTableNameParamName]; - - if(isset( $fkId ) && isset( $fkTable )) { - $query = "SELECT id FROM {$this->tables[$fkTable]} + + if (isset($fkId) && isset($fkTable)) { + $query = "SELECT id FROM {$this->tables[$fkTable]} WHERE id={$fkId}"; - $result = $this->dbObj->fetchFirstRowSingleColumn( $query, "id" ); + $result = $this->dbObj->fetchFirstRowSingleColumn($query, "id"); } - if(null == $result) { - $msg = $msg_prefix . sprintf( ATTACH_INVALID_FK_STR, $fkId, $fkTable ); - $this->errors[] = new IXR_ERROR( ATTACH_INVALID_FK, $msg ); + if (null == $result) { + $msg = $msg_prefix . sprintf(ATTACH_INVALID_FK_STR, $fkId, $fkTable); + $this->errors[] = new IXR_ERROR(ATTACH_INVALID_FK, $msg); $statusOK = false; } @@ -5174,24 +6396,26 @@ protected function checkForeignKey($msg_prefix = '') { * @since 1.9beta6 * @return boolean true if the file name and the content are set */ - protected function checkUploadAttachmentRequest($msg_prefix = '') { + protected function checkUploadAttachmentRequest($msg_prefix = '') + { // Did the client set file name? - $status = isset( $this->args[self::$fileNameParamName] ); - if($status) { + $status = isset($this->args[self::$fileNameParamName]); + if ($status) { // Did the client set file content? - $status = isset( $this->args[self::$contentParamName] ); - if($status) { - // Did the client set the file type? If not so use binary as default file type - if(isset( $this->args[self::$fileTypeParamName] )) { - // By default, if no file type is provided, put it as binary - $this->args[self::$fileTypeParamName] = "application/octet-stream"; - } + $status = isset($this->args[self::$contentParamName]); + // Did the client set the file type? If not so use binary as default file type + if ($status && isset($this->args[self::$fileTypeParamName])) { + // By default, if no file type is provided, put it as binary + $this->args[self::$fileTypeParamName] = "application/octet-stream"; } } - if(! $status) { - $msg = $msg_prefix . sprintf( ATTACH_INVALID_ATTACHMENT_STR, $this->args[self::$fileNameParamName], sizeof( $this->args[self::$contentParamName] ) ); - $this->errors[] = new IXR_ERROR( ATTACH_INVALID_ATTACHMENT, $msg ); + if (! $status) { + $msg = $msg_prefix . + sprintf(ATTACH_INVALID_ATTACHMENT_STR, + $this->args[self::$fileNameParamName], + count($this->args[self::$contentParamName])); + $this->errors[] = new IXR_ERROR(ATTACH_INVALID_ATTACHMENT, $msg); } return $status; @@ -5205,16 +6429,17 @@ protected function checkUploadAttachmentRequest($msg_prefix = '') { * @since 1.9beta6 * @return file handler */ - protected function createAttachmentTempFile() { + protected function createAttachmentTempFile() + { $resultInfo = array(); - $filename = tempnam( sys_get_temp_dir(), 'tl-' ); + $filename = tempnam(sys_get_temp_dir(), 'tl-'); $resultInfo["tmp_name"] = $filename; - $handle = fopen( $filename, "w" ); - fwrite( $handle, base64_decode( $this->args[self::$contentParamName] ) ); - fclose( $handle ); + $handle = fopen($filename, "w"); + fwrite($handle, base64_decode($this->args[self::$contentParamName])); + fclose($handle); - $filesize = filesize( $filename ); + $filesize = filesize($filename); $resultInfo["size"] = $filesize; return $resultInfo; @@ -5227,32 +6452,42 @@ protected function createAttachmentTempFile() { * @param string $messagePrefix * used to be prepended to error message * - * @return map with following keys + * @return array with following keys * boolean map['status_ok'] * string map['error_msg'] * int map['error_code'] */ - protected function checkTestCaseVersionNumberAncestry($messagePrefix = '') { - $ret = array('status_ok' => true, - 'error_msg' => '', - 'error_code' => 0 ); + protected function checkTestCaseVersionNumberAncestry($messagePrefix = '') + { + $ret = array( + 'status_ok' => true, + 'error_msg' => '', + 'error_code' => 0 + ); $tcase_id = $this->args[self::$testCaseIDParamName]; $version_number = $this->args[self::$versionNumberParamName]; - $sql = " SELECT TCV.version,TCV.id " . " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['tcversions']} TCV " . " WHERE NH.parent_id = {$tcase_id} " . " AND TCV.version = {$version_number} " . " AND TCV.id = NH.id "; + $sql = " SELECT TCV.version,TCV.id " . + " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['tcversions']} TCV " . + " WHERE NH.parent_id = {$tcase_id} " . + " AND TCV.version = {$version_number} " . " AND TCV.id = NH.id "; - $target_tcversion = $this->dbObj->fetchRowsIntoMap( $sql, 'version' ); - if(! is_null( $target_tcversion ) && count( $target_tcversion ) == 1) { - $dummy = current( $target_tcversion ); + $target_tcversion = $this->dbObj->fetchRowsIntoMap($sql, 'version'); + if (! is_null($target_tcversion) && count($target_tcversion) == 1) { + $dummy = current($target_tcversion); $this->tcVersionID = $dummy['id']; } else { - $status_ok = false; - $tcase_info = $this->tcaseMgr->tree_manager->get_node_hierarchy_info( $tcase_id ); - $msg = sprintf( TCASE_VERSION_NUMBER_KO_STR, $version_number, $this->args[self::$testCaseExternalIDParamName], $tcase_info['name'] ); - $ret = array('status_ok' => false, - 'error_msg' => $msg, - 'error_code' => TCASE_VERSION_NUMBER_KO); + $tcase_info = $this->tcaseMgr->tree_manager->get_node_hierarchy_info( + $tcase_id); + $msg = sprintf(TCASE_VERSION_NUMBER_KO_STR, $version_number, + $this->args[self::$testCaseExternalIDParamName], + $tcase_info['name']); + $ret = array( + 'status_ok' => false, + 'error_msg' => $msg, + 'error_code' => TCASE_VERSION_NUMBER_KO + ); } return $ret; } @@ -5266,8 +6501,9 @@ protected function checkTestCaseVersionNumberAncestry($messagePrefix = '') { * @return boolean * @access protected */ - protected function checkCustomField($messagePrefix = '') { - return(isset( $this->args[self::$customFieldNameParamName] ) ? true : false); + protected function checkCustomField($messagePrefix = '') + { + return isset($this->args[self::$customFieldNameParamName]) ? true : false; } /** @@ -5281,17 +6517,16 @@ protected function checkCustomField($messagePrefix = '') { * @return boolean * @access protected */ - protected function checkCustomFieldScope($messagePrefix = '') { - $status = false; + protected function checkCustomFieldScope($messagePrefix = '') + { $domain = array( - 'design' => true, - 'execution' => true, - 'testplan_design' => true + 'design' => true, + 'execution' => true, + 'testplan_design' => true ); $scope = $this->args[self::$scopeParamName]; - $status = is_null( $scope ) ? false : isset( $domain[$scope] ); - return $status; + return is_null($scope) ? false : isset($domain[$scope]); } /** @@ -5326,47 +6561,54 @@ protected function checkCustomFieldScope($messagePrefix = '') { * * @access protected */ - protected function getCustomFieldValue($args, $msg_prefix = '') { - $this->_setArgs( $args ); + protected function getCustomFieldValue($args, $msg_prefix = '') + { + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkCustomField', - 'checkCustomFieldScope' + 'authenticate', + 'checkTestProjectID', + 'checkCustomField', + 'checkCustomFieldScope' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); $scope = $this->args[self::$scopeParamName]; - switch($scope) { - case 'execution' : + switch ($scope) { + case 'execution': // test plan id is valid ? - if(($status_ok = $this->checkTestPlanID( $msg_prefix ))) { + if ($status_ok = $this->checkTestPlanID($msg_prefix)) { // test plan has to belong to test project - $tplanid = intval( $this->args[self::$testPlanIDParamName] ); - $tprojectid = intval( $this->args[self::$testProjectIDParamName] ); - - $sql = " SELECT id FROM {$this->tables['nodes_hierarchy']} " . " WHERE id = " . $tplanid . " AND parent_id = " . $tprojectid; - - $rs = $this->dbObj->get_recordset( $sql ); - $status_ok = ! is_null( $rs ); - if($status_ok == FALSE) { - $project = $this->tprojectMgr->get_by_id( $tprojectid ); - $plan = $this->tplanMgr->get_by_id( $tplanid ); - $msg = sprintf( TPLAN_TPROJECT_KO_STR, $plan['name'], $tplanid, $project['name'], $tprojectid ); - $this->errors[] = new IXR_Error( TPLAN_TPROJECT_KO, $msg_prefix . $msg ); + $tplanid = intval($this->args[self::$testPlanIDParamName]); + $tprojectid = intval( + $this->args[self::$testProjectIDParamName]); + + $sql = " SELECT id FROM {$this->tables['nodes_hierarchy']} " . + " WHERE id = " . $tplanid . " AND parent_id = " . + $tprojectid; + + $rs = $this->dbObj->get_recordset($sql); + $status_ok = ! is_null($rs); + if (! $status_ok) { + $project = $this->tprojectMgr->get_by_id($tprojectid); + $plan = $this->tplanMgr->get_by_id($tplanid); + $msg = sprintf(TPLAN_TPROJECT_KO_STR, $plan['name'], + $tplanid, $project['name'], $tprojectid); + $this->errors[] = new IXR_Error(TPLAN_TPROJECT_KO, + $msg_prefix . $msg); } } break; - case 'design' : - default : + case 'design': + default: break; } - if($status_ok && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + if ($status_ok && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR)) { $cf_name = $this->args[self::$customFieldNameParamName]; $tproject_id = $this->args[self::$testProjectIDParamName]; $nodetype = $this->args[self::$nodeTypeParamName]; @@ -5378,23 +6620,28 @@ protected function getCustomFieldValue($args, $msg_prefix = '') { $enabled = 1; // returning only enabled custom fields $cfield_mgr = $this->tprojectMgr->cfield_mgr; - $cfinfo = $cfield_mgr->get_by_name( $cf_name ); - $cfield = current( $cfinfo ); + $cfinfo = $cfield_mgr->get_by_name($cf_name); + $cfield = current($cfinfo); - switch($scope) { - case 'design' : + switch ($scope) { + case 'design': $filters = array( - 'cfield_id' => $cfield['id'] + 'cfield_id' => $cfield['id'] ); - $cfieldSpec = $cfield_mgr->get_linked_cfields_at_design( $tproject_id, $enabled, $filters, $nodetype, $nodeid ); + $cfieldSpec = $cfield_mgr->get_linked_cfields_at_design( + $tproject_id, $enabled, $filters, $nodetype, $nodeid); break; - case 'execution' : - $cfieldSpec = $cfield_mgr->get_linked_cfields_at_execution( $tproject_id, $enabled, $nodetype, $nodeid, $executionid, $testplanid ); + case 'execution': + $cfieldSpec = $cfield_mgr->get_linked_cfields_at_execution( + $tproject_id, $enabled, $nodetype, $nodeid, $executionid, + $testplanid); break; - case 'testplan_design' : - $cfieldSpec = $cfield_mgr->get_linked_cfields_at_testplan_design( $tproject_id, $enabled, $nodetype, $nodeid, $linkid, $testplanid ); + case 'testplan_design': + $cfieldSpec = $cfield_mgr->get_linked_cfields_at_testplan_design( + $tproject_id, $enabled, $nodetype, $nodeid, $linkid, + $testplanid); break; } return $cfieldSpec[$cfield['id']]; @@ -5426,44 +6673,49 @@ protected function getCustomFieldValue($args, $msg_prefix = '') { * * @access public */ - public function getTestCaseCustomFieldExecutionValue($args) { + public function getTestCaseCustomFieldExecutionValue($args) + { $msgPrefix = "(" . __FUNCTION__ . ") - "; $args[self::$nodeTypeParamName] = 'testcase'; $args[self::$scopeParamName] = 'execution'; - $this->_setArgs( $args ); + $this->_setArgs($args); $status_ok = true; $p2c = array( - self::$executionIDParamName, - self::$versionNumberParamName + self::$executionIDParamName, + self::$versionNumberParamName ); - foreach( $p2c as $prm ) { - $status_ok = $this->_isParamPresent( $prm, $msgPrefix, self::SET_ERROR ); - if($status_ok == FALSE) { + foreach ($p2c as $prm) { + $status_ok = $this->_isParamPresent($prm, $msgPrefix, + self::SET_ERROR); + if (! $status_ok) { break; } } // version number is related to execution id - if($status_ok) { - $sql = " SELECT id,tcversion_id FROM {$this->tables['executions']} " . " WHERE id = " . intval( $args[self::$executionIDParamName] ) . " AND tcversion_number = " . intval( $args[self::$versionNumberParamName] ); - - $rs = $this->dbObj->get_recordset( $sql ); - - // return $sql; - if(is_null( $rs )) { + if ($status_ok) { + $sql = " SELECT id,tcversion_id FROM {$this->tables['executions']} " . + " WHERE id = " . intval($args[self::$executionIDParamName]) . + " AND tcversion_number = " . + intval($args[self::$versionNumberParamName]); + $rs = $this->dbObj->get_recordset($sql); + + if (is_null($rs)) { $status_ok = false; - $msg = sprintf( NO_MATCH_STR, self::$versionNumberParamName . '/' . self::$executionIDParamName ); - $this->errors[] = new IXR_Error( NO_MATCH, $msg ); + $msg = sprintf(NO_MATCH_STR, + self::$versionNumberParamName . '/' . + self::$executionIDParamName); + $this->errors[] = new IXR_Error(NO_MATCH, $msg); } else { $args[self::$nodeIDParamName] = $rs[0]['tcversion_id']; } } - if($status_ok) { - return $this->getCustomFieldValue( $args ); + if ($status_ok) { + return $this->getCustomFieldValue($args); } return $this->errors; } @@ -5491,12 +6743,13 @@ public function getTestCaseCustomFieldExecutionValue($args) { * * @access public */ - public function getTestCaseCustomFieldTestPlanDesignValue($args) { + public function getTestCaseCustomFieldTestPlanDesignValue($args) + { $args[self::$nodeTypeParamName] = 'testcase'; $args[self::$nodeIDParamName] = $args[self::$versionNumberParamName]; $args[self::$scopeParamName] = 'testplan_design'; - return $this->getCustomFieldValue( $args ); + return $this->getCustomFieldValue($args); } /** @@ -5518,12 +6771,13 @@ public function getTestCaseCustomFieldTestPlanDesignValue($args) { * * @access public */ - public function getTestSuiteCustomFieldDesignValue($args) { + public function getTestSuiteCustomFieldDesignValue($args) + { $args[self::$nodeTypeParamName] = 'testsuite'; $args[self::$nodeIDParamName] = $args[self::$testSuiteIDParamName]; $args[self::$scopeParamName] = 'design'; - return $this->getCustomFieldValue( $args ); + return $this->getCustomFieldValue($args); } /** @@ -5545,12 +6799,13 @@ public function getTestSuiteCustomFieldDesignValue($args) { * * @access public */ - public function getTestPlanCustomFieldDesignValue($args) { + public function getTestPlanCustomFieldDesignValue($args) + { $args[self::$nodeTypeParamName] = 'testplan'; $args[self::$nodeIDParamName] = $args[self::$testPlanIDParamName]; $args[self::$scopeParamName] = 'design'; - return $this->getCustomFieldValue( $args ); + return $this->getCustomFieldValue($args); } /** @@ -5572,12 +6827,13 @@ public function getTestPlanCustomFieldDesignValue($args) { * * @access public */ - public function getReqSpecCustomFieldDesignValue($args) { + public function getReqSpecCustomFieldDesignValue($args) + { $args[self::$nodeTypeParamName] = 'requirement_spec'; $args[self::$nodeIDParamName] = $args[self::$reqSpecIDParamName]; $args[self::$scopeParamName] = 'design'; - return $this->getCustomFieldValue( $args ); + return $this->getCustomFieldValue($args); } /** @@ -5599,12 +6855,13 @@ public function getReqSpecCustomFieldDesignValue($args) { * * @access public */ - public function getRequirementCustomFieldDesignValue($args) { + public function getRequirementCustomFieldDesignValue($args) + { $args['nodetype'] = 'requirement'; $args['nodeid'] = $args[self::$requirementIDParamName]; $args['scope'] = 'design'; - return $this->getCustomFieldValue( $args ); + return $this->getCustomFieldValue($args); } /** @@ -5638,24 +6895,24 @@ public function getRequirementCustomFieldDesignValue($args) { * @internal revisions * 20111018 - franciscom - TICKET 4774: New methods to manage test case steps */ - function createTestCaseSteps($args) { + public function createTestCaseSteps($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $resultInfo = array(); - $useLatestVersion = true; $version = - 1; $item = null; $stepSet = null; - $stepNumbers = null; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestCaseIdentity' + 'authenticate', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->userHasRight( "mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->userHasRight("mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); - if($status_ok) { + if ($status_ok) { // Important Notice: method checkTestCaseIdentity sets // $this->args[self::$testCaseIDParamName] $tcaseID = $this->args[self::$testCaseIDParamName]; @@ -5664,115 +6921,123 @@ function createTestCaseSteps($args) { // if parameter version does not exits or is < 0 // then we will on latest version active or not - if($this->_isParamPresent( self::$versionNumberParamName )) { + if ($this->_isParamPresent(self::$versionNumberParamName)) { $version = $this->args[self::$versionNumberParamName]; } $resultInfo['version'] = 'exists'; - if($version > 0) { - $item = $this->tcaseMgr->get_basic_info( $tcaseID, array( + if ($version > 0) { + $item = $this->tcaseMgr->get_basic_info($tcaseID, + array( 'number' => $version - ) ); + )); } else { $resultInfo['version'] = 'DOES NOT ' . $resultInfo['version']; - $item = $this->tcaseMgr->get_last_active_version( $tcaseID ); - if(is_null( $item )) { + $item = $this->tcaseMgr->get_last_active_version($tcaseID); + if (is_null($item)) { // get last version no matter if is active - $dummy = $this->tcaseMgr->get_last_version_info( $tcaseID ); + $dummy = $this->tcaseMgr->getLastVersionInfo($tcaseID); $dummy['tcversion_id'] = $dummy['id']; $item[0] = $dummy; } } - if(is_null( $item )) { + if (is_null($item)) { $status_ok = false; - $msg = sprintf( VERSION_NOT_VALID_STR, $version ); - $this->errors[] = new IXR_Error( VERSION_NOT_VALID, $msg ); + $msg = sprintf(VERSION_NOT_VALID_STR, $version); + $this->errors[] = new IXR_Error(VERSION_NOT_VALID, $msg); } - if($status_ok) { - $item = current( $item ); + if ($status_ok) { + $item = current($item); $tcversion_id = $item['tcversion_id']; $resultInfo['tcversion_id'] = $tcversion_id; $step_id = 0; $stepSet = null; - $action = isset( $this->args, self::$actionParamName ) ? $this->args[self::$actionParamName] : 'create'; + $action = isset($this->args, self::$actionParamName) ? $this->args[self::$actionParamName] : 'create'; // // id,step_number,actions,expected_results,active,execution_type $opt = array( - 'accessKey' => 'step_number' + 'accessKey' => 'step_number' ); - $stepSet =( array ) $this->tcaseMgr->get_steps( $tcversion_id, 0, $opt ); - $stepNumberIDSet = array_flip( array_keys( $stepSet ) ); - foreach( $stepNumberIDSet as $sn => $dummy ) { + $stepSet = (array) $this->tcaseMgr->get_steps($tcversion_id, 0, + $opt); + $stepNumberIDSet = array_flip(array_keys($stepSet)); + foreach ($stepNumberIDSet as $sn => $dummy) { $stepNumberIDSet[$sn] = $stepSet[$sn]['id']; } $resultInfo['stepSet'] = $stepSet; $resultInfo['stepNumberIDSet'] = $stepNumberIDSet; - foreach( $this->args[self::$stepsParamName] as $si ) { - $execution_type = isset( $si['execution_type'] ) ? $si['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; - $stepExists = isset( $stepSet[$si['step_number']] ); - if($stepExists) { + foreach ($this->args[self::$stepsParamName] as $si) { + $execution_type = isset($si['execution_type']) ? $si['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; + $stepExists = isset($stepSet[$si['step_number']]); + if ($stepExists) { // needed for update op. $step_id = $stepSet[$si['step_number']]['id']; $resultInfo['stepID'][] = array( - $step_id, - $si['step_number'] + $step_id, + $si['step_number'] ); } - switch($action) { - case 'update' : + switch ($action) { + case 'update': $op = $stepExists ? $action : 'create'; break; - case 'push' : + case 'push': $op = $stepExists ? $action : 'skip'; break; - case 'create' : + case 'create': $op = $stepExists ? 'skip' : $action; break; } $resultInfo['feedback'][] = array( - 'operation' => $op, - 'step_number' => $si['step_number'] + 'operation' => $op, + 'step_number' => $si['step_number'] ); - switch($op) { - case 'update' : - $this->tcaseMgr->update_step( $step_id, $si['step_number'], $si['actions'], $si['expected_results'], $execution_type ); + switch ($op) { + case 'update': + $this->tcaseMgr->update_step($step_id, + $si['step_number'], $si['actions'], + $si['expected_results'], $execution_type); break; - case 'create' : - $this->tcaseMgr->create_step( $tcversion_id, $si['step_number'], $si['actions'], $si['expected_results'], $execution_type ); + case 'create': + $this->tcaseMgr->create_step($tcversion_id, + $si['step_number'], $si['actions'], + $si['expected_results'], $execution_type); break; - case 'push' : + case 'push': // First action renumber existent steps $renumberedSet = null; - foreach( $stepNumberIDSet as $tsn => $dim ) { - if($tsn < $si['step_number']) { - unset( $stepNumberIDSet[$tsn] ); + foreach ($stepNumberIDSet as $tsn => $dim) { + if ($tsn < $si['step_number']) { + unset($stepNumberIDSet[$tsn]); } else { $renumberedSet[$dim] = $tsn + 1; } } - $this->tcaseMgr->set_step_number( $renumberedSet ); - $this->tcaseMgr->create_step( $tcversion_id, $si['step_number'], $si['actions'], $si['expected_results'], $execution_type ); + $this->tcaseMgr->set_step_number($renumberedSet); + $this->tcaseMgr->create_step($tcversion_id, + $si['step_number'], $si['actions'], + $si['expected_results'], $execution_type); break; - case 'skip' : - default : + case 'skip': + default: break; } } } } - return($status_ok ? $resultInfo : $this->errors); + return $status_ok ? $resultInfo : $this->errors; } /** @@ -5791,23 +7056,24 @@ function createTestCaseSteps($args) { * @internal revisions * 20111018 - franciscom - TICKET 4774: New methods to manage test case steps */ - function deleteTestCaseSteps($args) { + public function deleteTestCaseSteps($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $resultInfo = array(); $version = - 1; $item = null; $stepSet = null; - $stepNumberIDSet = null; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestCaseIdentity' + 'authenticate', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->userHasRight( "mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->userHasRight("mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); - if($status_ok) { + if ($status_ok) { // Important Notice: method checkTestCaseIdentity sets // $this->args[self::$testCaseIDParamName] $tcaseID = $this->args[self::$testCaseIDParamName]; @@ -5816,50 +7082,48 @@ function deleteTestCaseSteps($args) { // if parameter version does not exits or is < 0 // then we will on latest version active or not - if($this->_isParamPresent( self::$versionNumberParamName )) { + if ($this->_isParamPresent(self::$versionNumberParamName)) { $version = $this->args[self::$versionNumberParamName]; } $resultInfo['version'] = 'exists'; - if($version > 0) { - $item = $this->tcaseMgr->get_basic_info( $tcaseID, array( + if ($version > 0) { + $item = $this->tcaseMgr->get_basic_info($tcaseID, + array( 'number' => $version - ) ); + )); } else { $resultInfo['version'] = 'DOES NOT ' . $resultInfo['version']; - $item = $this->tcaseMgr->get_last_active_version( $tcaseID ); + $item = $this->tcaseMgr->get_last_active_version($tcaseID); } - if(is_null( $item )) { + if (is_null($item)) { $status_ok = false; - $msg = sprintf( VERSION_NOT_VALID_STR, $version ); - $this->errors[] = new IXR_Error( VERSION_NOT_VALID, $msg ); + $msg = sprintf(VERSION_NOT_VALID_STR, $version); + $this->errors[] = new IXR_Error(VERSION_NOT_VALID, $msg); } - // $resultInfo['item'] = is_null($item) ? $msg : $item; - - if($status_ok) { - - // $resultInfo['steps'] = $this->args[self::$stepsParamName]; + if ($status_ok) { $tcversion_id = $item[0]['tcversion_id']; - $step_id = 0; $stepSet = null; // // id,step_number,actions,expected_results,active,execution_type $opt = array( - 'accessKey' => 'step_number' + 'accessKey' => 'step_number' ); - $stepSet =( array ) $this->tcaseMgr->get_steps( $tcversion_id, 0, $opt ); + $stepSet = (array) $this->tcaseMgr->get_steps($tcversion_id, 0, + $opt); $resultInfo['stepSet'] = $stepSet; - foreach( $this->args[self::$stepsParamName] as $step_number ) { - if(isset( $stepSet[$step_number] )) { - $this->tcaseMgr->delete_step_by_id( $stepSet[$step_number]['id'] ); + foreach ($this->args[self::$stepsParamName] as $step_number) { + if (isset($stepSet[$step_number])) { + $this->tcaseMgr->delete_step_by_id( + $stepSet[$step_number]['id']); } } } } - return($status_ok ? $resultInfo : $this->errors); + return $status_ok ? $resultInfo : $this->errors; } /** @@ -5892,53 +7156,58 @@ function deleteTestCaseSteps($args) { * * @access public */ - public function updateTestCaseCustomFieldDesignValue($args) { + public function updateTestCaseCustomFieldDesignValue($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunc = array( - 'authenticate', - 'checkTestProjectID', - 'checkTestCaseIdentity', - 'checkTestCaseVersionNumber' + 'authenticate', + 'checkTestProjectID', + 'checkTestCaseIdentity', + 'checkTestCaseVersionNumber' ); - $status_ok = $this->_runChecks( $checkFunc, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunc, $msg_prefix); - if($status_ok) { - if(! $this->_isParamPresent( self::$customFieldsParamName )) { - $status_ok = false; - $msg = sprintf( MISSING_REQUIRED_PARAMETER_STR, self::$customFieldsParamName ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); - } + if ($status_ok && ! $this->_isParamPresent(self::$customFieldsParamName)) { + $status_ok = false; + $msg = sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$customFieldsParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } - if($status_ok) { + if ($status_ok) { // now check if custom fields are ok // For each custom field need to check if: // 1. is linked to test project // 2. is available for test case at design time - $cfieldMgr = new cfield_mgr( $this->dbObj ); + $cfieldMgr = new cfield_mgr($this->dbObj); // Just ENABLED - $linkedSet = $cfieldMgr->get_linked_cfields_at_design( $this->args[self::$testProjectIDParamName], cfield_mgr::ENABLED, null, 'testcase', null, 'name' ); - if(is_null( $linkedSet )) { + $linkedSet = $cfieldMgr->get_linked_cfields_at_design( + $this->args[self::$testProjectIDParamName], cfield_mgr::ENABLED, + null, 'testcase', null, 'name'); + if (is_null($linkedSet)) { $status_ok = false; $msg = NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES_STR; - $this->errors[] = new IXR_Error( NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES, $msg ); + $this->errors[] = new IXR_Error( + NO_CUSTOMFIELDS_DT_LINKED_TO_TESTCASES, $msg); } } - if($status_ok) { + if ($status_ok) { $accessVersionBy['number'] = $this->args[self::$versionNumberParamName]; - $nodeInfo = $this->tcaseMgr->get_basic_info( $this->args[self::$testCaseIDParamName], $accessVersionBy ); + $nodeInfo = $this->tcaseMgr->get_basic_info( + $this->args[self::$testCaseIDParamName], $accessVersionBy); $cfSet = $args[self::$customFieldsParamName]; - foreach( $cfSet as $cfName => $cfValue ) { + foreach ($cfSet as $cfName => $cfValue) { // $accessKey = "custom_field_" . $item['id'] . _ // design_values_to_db($hash,$node_id,$cf_map=null,$hash_type=null) $item = $linkedSet[$cfName]; $accessKey = "custom_field_" . $item['type'] . '_' . $item['id']; $hash[$accessKey] = $cfValue; - $cfieldMgr->design_values_to_db( $hash, $nodeInfo[0]['tcversion_id'] ); + $cfieldMgr->design_values_to_db($hash, + $nodeInfo[0]['tcversion_id']); } } else { return $this->errors; @@ -5965,33 +7234,37 @@ public function updateTestCaseCustomFieldDesignValue($args) { * * @access public */ - public function setTestCaseExecutionType($args) { + public function setTestCaseExecutionType($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkTestCaseIdentity', - 'checkTestCaseVersionNumber' + 'authenticate', + 'checkTestProjectID', + 'checkTestCaseIdentity', + 'checkTestCaseVersionNumber' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); - if($status_ok) { - if(! $this->_isParamPresent( self::$executionTypeParamName )) { - $status_ok = false; - $msg = sprintf( MISSING_REQUIRED_PARAMETER_STR, self::$customFieldsParamName ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); - } + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + if ($status_ok && ! $this->_isParamPresent( + self::$executionTypeParamName)) { + $status_ok = false; + $msg = sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$customFieldsParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } - if($status_ok) { + if ($status_ok) { // if value not on domain, will use TESTCASE_EXECUTION_TYPE_MANUAL $accessVersionBy['number'] = $this->args[self::$versionNumberParamName]; - $nodeInfo = $this->tcaseMgr->get_basic_info( $this->args[self::$testCaseIDParamName], $accessVersionBy ); - $dbg = $this->tcaseMgr->setExecutionType( $nodeInfo[0]['tcversion_id'], $this->args[self::$executionTypeParamName] ); + $nodeInfo = $this->tcaseMgr->get_basic_info( + $this->args[self::$testCaseIDParamName], $accessVersionBy); + $dbg = $this->tcaseMgr->setExecutionType( + $nodeInfo[0]['tcversion_id'], + $this->args[self::$executionTypeParamName]); return array( - $this->args, - $dbg + $this->args, + $dbg ); } else { return $this->errors; @@ -6000,41 +7273,42 @@ public function setTestCaseExecutionType($args) { /** */ - public function getExecCountersByBuild($args) { + public function getExecCountersByBuild($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $total = null; - $this->_setArgs( $args ); + $this->_setArgs($args); $status_ok = true; // Checks are done in order $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { - $metrics = $this->tplanMetricsMgr->getExecCountersByBuildExecStatus( $this->args[self::$testPlanIDParamName] ); + if ($status_ok) { + $metrics = $this->tplanMetricsMgr->getExecCountersByBuildExecStatus( + $this->args[self::$testPlanIDParamName]); } - if(! is_null( $metrics )) { + if (! is_null($metrics)) { // transform in somethin similar to a simple table $out = array(); - foreach( $metrics['with_tester'] as $build_id => &$elem ) { + foreach ($metrics['with_tester'] as $build_id => &$elem) { $out[$build_id] = array(); $out[$build_id]['name'] = $metrics['active_builds'][$build_id]['name']; $out[$build_id]['notes'] = $metrics['active_builds'][$build_id]['notes']; $out[$build_id]['total'] = $metrics['total'][$build_id]['qty']; - foreach( $elem as $status_code => &$data ) { + foreach ($elem as $status_code => &$data) { $out[$build_id][$status_code] = $data['exec_qty']; } } return array( - 'raw' => $metrics, - 'table' => $out + 'raw' => $metrics, + 'table' => $out ); } else { return $this->errors; @@ -6049,56 +7323,76 @@ public function getExecCountersByBuild($args) { * @param string $args["testprojectname"] * @param string $args["platformname"] * @param string $args["notes"] + * @param boolean $args["platformondesign"] + * @param boolean $args["platformonexecution"] * @return mixed $resultInfo * @internal revisions */ - public function createPlatform($args) { - $this->_setArgs( $args ); + public function createPlatform($args) + { + $this->_setArgs($args); $status_ok = false; $msg_prefix = "(" . __FUNCTION__ . ") - "; - if($this->authenticate() && $this->userHasRight( "platform_management", self::CHECK_PUBLIC_PRIVATE_ATTR )) { + if ($this->authenticate() && + $this->userHasRight("platform_management", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { $status_ok = true; $keys2check = array( - self::$platformNameParamName, - self::$testProjectNameParamName + self::$platformNameParamName, + self::$testProjectNameParamName ); - foreach( $keys2check as $key ) { - $names[$key] = $this->_isParamPresent( $key, $msg_prefix, self::SET_ERROR ) ? trim( $this->args[$key] ) : ''; - if($names[$key] == '') { + foreach ($keys2check as $key) { + $names[$key] = $this->_isParamPresent($key, $msg_prefix, + self::SET_ERROR) ? trim($this->args[$key]) : ''; + if ($names[$key] == '') { $status_ok = false; break; } } } - if($status_ok) { - $op = $this->helperGetTestProjectByName( $msg_prefix ); + if ($status_ok) { + $op = $this->helperGetTestProjectByName($msg_prefix); $status_ok = $op['status_ok']; } - if($status_ok) { + if ($status_ok) { // now check if platform exists - if(is_null( $this->platformMgr )) { - $this->platformMgr = new tlPlatform( $this->dbObj, $op['info']['id'] ); + if (is_null($this->platformMgr)) { + $this->platformMgr = new tlPlatform($this->dbObj, + $op['info']['id']); } // lazy way - $name = trim( $this->args[self::$platformNameParamName] ); + $name = trim($this->args[self::$platformNameParamName]); - $opx = array('accessKey' => 'name', - 'output' => 'allinfo'); + $opx = array( + 'accessKey' => 'name', + 'output' => 'allinfo' + ); $itemSet = $this->platformMgr->getAllAsMap($opx); - if(isset( $itemSet[$name] )) { + if (isset($itemSet[$name])) { $status_ok = false; - $msg = $msg_prefix . sprintf( PLATFORMNAME_ALREADY_EXISTS_STR, $name, $itemSet[$name]['id'] ); - $this->errors[] = new IXR_Error( PLATFORMNAME_ALREADY_EXISTS, $msg ); + $msg = $msg_prefix . + sprintf(PLATFORMNAME_ALREADY_EXISTS_STR, $name, + $itemSet[$name]['id']); + $this->errors[] = new IXR_Error(PLATFORMNAME_ALREADY_EXISTS, + $msg); } } - if($status_ok) { + if ($status_ok) { $notes = $this->_isNotePresent() ? $this->args[self::$noteParamName] : ''; - $op = $this->platformMgr->create( $name, $notes ); + + $plot = new stdClass(); + $plot->name = $name; + $plot->notes = $notes; + $plot->enable_on_design = $this->_isParamPresent( + self::$platformOnDesignParamName) ? $this->args[self::$platformOnDesignParamName] : false; + $plot->enable_on_execution = $this->_isParamPresent( + self::$platformOnExecutionParamName) ? $this->args[self::$platformOnExecutionParamName] : false; + $op = $this->platformMgr->create($plot); $resultInfo = $op; } @@ -6107,23 +7401,32 @@ public function createPlatform($args) { /** */ - public function getProjectPlatforms($args) { + public function getProjectPlatforms($args) + { $messagePrefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectIdentity' + 'authenticate', + 'checkTestProjectIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); - if($status_ok) { + if ($status_ok) { $testProjectID = $this->args[self::$testProjectIDParamName]; - if(is_null( $this->platformMgr )) { - $this->platformMgr = new tlPlatform( $this->dbObj, $testProjectID ); + if (is_null($this->platformMgr)) { + $this->platformMgr = new tlPlatform($this->dbObj, $testProjectID); } - $itemSet = $this->platformMgr->getAllAsMap( 'name', 'allinfo' ); - return $itemSet; + + $optPlat = array( + 'accessKey' => 'name', + 'output' => 'allinfo', + 'orderBy' => ' ORDER BY name ', + 'enable_on_design' => null, + 'enable_on_execution' => null + ); + + return $this->platformMgr->getAllAsMap($optPlat); } else { return $this->errors; } @@ -6135,12 +7438,13 @@ public function getProjectPlatforms($args) { * @param struct $args * @param string $args["devKey"] * @param int $args["testplanid"] - * @param map $args["platformname"] + * @param array $args["platformname"] * @return mixed $resultInfo * @internal revisions */ - public function addPlatformToTestPlan($args) { - return $this->platformLinkOp( $args, 'link', "(" . __FUNCTION__ . ") - " ); + public function addPlatformToTestPlan($args) + { + return $this->platformLinkOp($args, 'link', "(" . __FUNCTION__ . ") - "); } /** @@ -6149,12 +7453,14 @@ public function addPlatformToTestPlan($args) { * @param struct $args * @param string $args["devKey"] * @param int $args["testplanid"] - * @param map $args["platformname"] + * @param array $args["platformname"] * @return mixed $resultInfo * @internal revisions */ - public function removePlatformFromTestPlan($args) { - return $this->platformLinkOp( $args, 'unlink', "(" . __FUNCTION__ . ") - " ); + public function removePlatformFromTestPlan($args) + { + return $this->platformLinkOp($args, 'unlink', + "(" . __FUNCTION__ . ") - "); } /** @@ -6179,25 +7485,32 @@ public function removePlatformFromTestPlan($args) { * @return mixed $ret * */ - public function getUserByLogin($args) { + public function getUserByLogin($args) + { $messagePrefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate' + 'authenticate' ); $ret = array(); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); - if($status_ok) { - $user_id = tlUser::doesUserExist( $this->dbObj, $this->args[self::$userParamName] ); - if(!($status_ok = ! is_null( $user_id ))) { - $msg = $msg_prefix . sprintf( NO_USER_BY_THIS_LOGIN_STR, $this->args[self::$userParamName] ); - $this->errors[] = new IXR_Error( NO_USER_BY_THIS_LOGIN, $msg ); + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); + if ($status_ok) { + if ($this->user->globalRole->dbID == TL_ROLES_ADMIN || + $this->user->login == $this->args[self::$userParamName]) { + $user_id = tlUser::doesUserExist($this->dbObj, + $this->args[self::$userParamName]); + } + if (! ($status_ok = ! is_null($user_id))) { + $msg = $msg_prefix . + sprintf(NO_USER_BY_THIS_LOGIN_STR, + $this->args[self::$userParamName]); + $this->errors[] = new IXR_Error(NO_USER_BY_THIS_LOGIN, $msg); } } - if($status_ok) { - $user = tlUser::getByID( $this->dbObj, $user_id ); + if ($status_ok) { + $user = tlUser::getByID($this->dbObj, $user_id); $user->userApiKey = null; $ret[] = $user; } @@ -6227,21 +7540,28 @@ public function getUserByLogin($args) { * @return mixed $ret * */ - public function getUserByID($args) { + public function getUserByID($args) + { $messagePrefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate' + 'authenticate' ); $ret = array(); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); - if($status_ok) { - $user = tlUser::getByID( $this->dbObj, $this->args[self::$userIDParamName] ); - if(is_null( $user )) { + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); + if ($status_ok) { + if ($this->user->globalRole->dbID == TL_ROLES_ADMIN || + $this->userID == $this->args[self::$userIDParamName]) { + $user = tlUser::getByID($this->dbObj, + $this->args[self::$userIDParamName]); + } + if (is_null($user)) { $status_ok = false; - $msg = $messagePrefix . sprintf( NO_USER_BY_THIS_ID_STR, $this->args[self::$userIDParamName] ); - $this->errors[] = new IXR_Error( NO_USER_BY_ID_LOGIN, $msg ); + $msg = $messagePrefix . + sprintf(NO_USER_BY_THIS_ID_STR, + $this->args[self::$userIDParamName]); + $this->errors[] = new IXR_Error(NO_USER_BY_ID_LOGIN, $msg); } else { $user->userApiKey = null; $ret[] = $user; @@ -6253,73 +7573,86 @@ public function getUserByID($args) { /** */ - private function platformLinkOp($args, $op, $messagePrefix) { - $this->_setArgs( $args ); + private function platformLinkOp($args, $op, $messagePrefix) + { + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ) && $this->_isParamPresent( self::$platformNameParamName, $messagePrefix, self::SET_ERROR ); - if($status_ok) { + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix) && + $this->_isParamPresent(self::$platformNameParamName, $messagePrefix, + self::SET_ERROR); + if ($status_ok) { $testPlanID = $this->args[self::$testPlanIDParamName]; // get Test project ID in order to check that requested Platform // belong to same test project that test plan - $dummy = $this->tplanMgr->get_by_id( $testPlanID ); + $dummy = $this->tplanMgr->get_by_id($testPlanID); $testProjectID = $dummy['testproject_id']; - if(is_null( $this->platformMgr )) { - $this->platformMgr = new tlPlatform( $this->dbObj, $testProjectID ); + if (is_null($this->platformMgr)) { + $this->platformMgr = new tlPlatform($this->dbObj, $testProjectID); } else { // extra protection ??(20131307) - $this->platformMgr->setTestProjectID( $testProjectID ); + $this->platformMgr->setTestProjectID($testProjectID); } $platName = $this->args[self::$platformNameParamName]; - $platform = $this->platformMgr->getByName( $platName ); - if(is_null( $platform )) { + $platform = $this->platformMgr->getByName($platName); + if (is_null($platform)) { $status_ok = false; - $msg = $messagePrefix . sprintf( PLATFORM_NAME_DOESNOT_EXIST_STR, $platName ); - $this->errors[] = new IXR_Error( PLATFORM_NAME_DOESNOT_EXIST, $msg ); + $msg = $messagePrefix . + sprintf(PLATFORM_NAME_DOESNOT_EXIST_STR, $platName); + $this->errors[] = new IXR_Error(PLATFORM_NAME_DOESNOT_EXIST, + $msg); } } - if($status_ok) { - $linkExists = $this->platformMgr->isLinkedToTestplan( $platform['id'], $testPlanID ); + if ($status_ok) { + $linkExists = $this->platformMgr->isLinkedToTestplan( + $platform['id'], $testPlanID); $ret = array( - 'operation' => $op, - 'msg' => 'nothing to do', - 'linkStatus' => $linkExists + 'operation' => $op, + 'msg' => 'nothing to do', + 'linkStatus' => $linkExists ); - switch($op) { - case 'link' : - if(! $linkExists) { - $this->platformMgr->linkToTestplan( $platform['id'], $testPlanID ); + switch ($op) { + case 'link': + if (! $linkExists) { + $this->platformMgr->linkToTestplan($platform['id'], + $testPlanID); $ret['msg'] = 'link done'; } break; - case 'unlink' : - if($linkExists) { + case 'unlink': + if ($linkExists) { // If there are test case versions linked to test plan, that use // this platform, operation(as happens on GUI) can not be done - $hits = $this->tplanMgr->countLinkedTCVersionsByPlatform( $testPlanID,( array ) $platform['id'] ); - if($hits[$platform['id']]['qty'] == 0) { - $this->platformMgr->unlinkFromTestplan( $platform['id'], $testPlanID ); + $hits = $this->tplanMgr->countLinkedTCVersionsByPlatform( + $testPlanID, (array) $platform['id']); + if ($hits[$platform['id']]['qty'] == 0) { + $this->platformMgr->unlinkFromTestplan( + $platform['id'], $testPlanID); $ret['msg'] = 'unlink done'; } else { $status_ok = false; - $msg = $messagePrefix . sprintf( PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK_STR, $platName, $hits[$platform['id']]['qty'] ); - $this->errors[] = new IXR_Error( PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK, $msg ); + $msg = $messagePrefix . + sprintf( + PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK_STR, + $platName, $hits[$platform['id']]['qty']); + $this->errors[] = new IXR_Error( + PLATFORM_REMOVETC_NEEDED_BEFORE_UNLINK, $msg); } } break; } - if($status_ok) { + if ($status_ok) { return $ret; } } - if(! $tatus_ok) { + if (! $tatus_ok) { return $this->errors; } } @@ -6358,7 +7691,8 @@ private function platformLinkOp($args, $op, $messagePrefix) { * - login name used as updater - optional * if not provided will be set to user that request update */ - public function updateTestCase($args) { + public function updateTestCase($args) + { // Check test case identity // Check if user(devkey) has grants to do operation // @@ -6367,80 +7701,82 @@ public function updateTestCase($args) { // // translate args key to column name $updKeys = array( - "summary" => null, - "preconditions" => null, - "importance" => null, - "status" => null, - "executiontype" => "execution_type", - "estimatedexecduration" => "estimated_exec_duration" + "summary" => null, + "preconditions" => null, + "importance" => null, + "status" => null, + "executiontype" => "execution_type", + "estimatedexecduration" => "estimated_exec_duration" ); - $resultInfo = array(); $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $debug_info = null; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestCaseIdentity' + 'authenticate', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); $tprojectID = 0; - if($status_ok) { + if ($status_ok) { $tcaseID = $this->args[self::$testCaseIDParamName]; - $tprojectID = $this->tcaseMgr->getTestProjectFromTestCase( $tcaseID, null ); + $tprojectID = $this->tcaseMgr->getTestProjectFromTestCase($tcaseID, + null); } - if($status_ok) { - $updaterID = $this->updateTestCaseGetUpdater( $msg_prefix ); - $status_ok =($updaterID > 0); + if ($status_ok) { + $updaterID = $this->updateTestCaseGetUpdater($msg_prefix); + $status_ok = ($updaterID > 0); } - if($status_ok) { + if ($status_ok) { // we have got internal test case ID on checkTestCaseIdentity - list( $status_ok, $tcversion_id ) = $this->updateTestCaseGetTCVID( $tcaseID ); + list ($status_ok, $tcversion_id) = $this->updateTestCaseGetTCVID( + $tcaseID); } // We will check that: // updater has right to update // user doing call has also has right to update - if($status_ok) { + if ($status_ok) { $ctx[self::$testProjectIDParamName] = $tprojectID; - if($updaterID != $this->userID) { + if ($updaterID != $this->userID) { $ctx['updaterID'] = $updaterID; } $ck = self::CHECK_PUBLIC_PRIVATE_ATTR; $r2c = array( - 'mgt_modify_tc' + 'mgt_modify_tc' ); - foreach( $r2c as $right ) { - $status_ok = $this->userHasRight( $right, $ck, $ctx ); - if(! $status_ok) { + foreach ($r2c as $right) { + $status_ok = $this->userHasRight($right, $ck, $ctx); + if (! $status_ok) { break; } } } // If test case version has been executed, need to check another right - if($status_ok) { - $xc = $this->tcaseMgr->get_versions_status_quo( $tcaseID, $tcversion_id ); + if ($status_ok) { + $xc = $this->tcaseMgr->getVersionsStatusQuo($tcaseID, $tcversion_id); $checkRight = false; - foreach( $xc as $ele ) { - if($ele['executed']) { + foreach ($xc as $ele) { + if ($ele['executed']) { $checkRight = true; break; } } - if($checkRight) { - $r2c = array('testproject_edit_executed_testcases' ); - foreach( $r2c as $right ) { - $status_ok = $this->userHasRight( $right, $ck, $ctx ); - if(! $status_ok) { + if ($checkRight) { + $r2c = array( + 'testproject_edit_executed_testcases' + ); + foreach ($r2c as $right) { + $status_ok = $this->userHasRight($right, $ck, $ctx); + if (! $status_ok) { break; } } @@ -6449,42 +7785,42 @@ public function updateTestCase($args) { // if name update requested, it will be first thing to be udpated // because if we got duplicate name, we will not do update - if($status_ok) { - if(isset( $this->args[self::$testCaseNameParamName] )) { - $ret = $this->tcaseMgr->updateName( $tcaseID, trim( $this->args[self::$testCaseNameParamName] ) ); - if(!($status_ok = $ret['status_ok'])) { - $this->errors[] = new IXR_Error( constant( $ret['API_error_code'] ), $msg_prefix . $ret['msg'] ); - } + if ($status_ok && isset($this->args[self::$testCaseNameParamName])) { + $ret = $this->tcaseMgr->updateName($tcaseID, + trim($this->args[self::$testCaseNameParamName])); + if (! ($status_ok = $ret['status_ok'])) { + $this->errors[] = new IXR_Error( + constant($ret['API_error_code']), $msg_prefix . $ret['msg']); } } - if($status_ok) { + if ($status_ok) { $fv = null; - foreach( $updKeys as $k2s => $field2update ) { - if(isset( $this->args[$k2s] )) { - $fv[(is_null( $field2update ) ? $k2s : $field2update)] = $this->args[$k2s]; + foreach ($updKeys as $k2s => $field2update) { + if (isset($this->args[$k2s])) { + $fv[(is_null($field2update) ? $k2s : $field2update)] = $this->args[$k2s]; } } - if(! is_null( $fv )) { - $sql = $this->tcaseMgr->updateSimpleFields( $tcversion_id, $fv ); + if (! is_null($fv)) { + $this->tcaseMgr->updateSimpleFields($tcversion_id, $fv); } } // if exist proceed with steps actions / expected results update. - if($status_ok) { - if($this->_isParamPresent( self::$stepsParamName ) && ! is_null( $this->args[self::$stepsParamName] )) { - $this->tcaseMgr->update_tcversion_steps( $tcversion_id, $this->args[self::$stepsParamName] ); - } + if ($status_ok && $this->_isParamPresent(self::$stepsParamName) && + ! is_null($this->args[self::$stepsParamName])) { + $this->tcaseMgr->update_tcversion_steps($tcversion_id, + $this->args[self::$stepsParamName]); } - if($status_ok) { + if ($status_ok) { // update updater and modification time stamp - $this->tcaseMgr->updateChangeAuditTrial( $tcversion_id, $updaterID ); + $this->tcaseMgr->updateChangeAuditTrial($tcversion_id, $updaterID); return array( - 'status_ok' => true, - 'msg' => 'ok', - 'operation' => __FUNCTION__ + 'status_ok' => true, + 'msg' => 'ok', + 'operation' => __FUNCTION__ ); } @@ -6493,14 +7829,18 @@ public function updateTestCase($args) { /** */ - function updateTestCaseGetUpdater($msg_prefix) { + private function updateTestCaseGetUpdater($msg_prefix) + { $status_ok = true; $updaterID = $this->userID; - if($this->_isParamPresent( self::$userParamName )) { - $updaterID = tlUser::doesUserExist( $this->dbObj, $this->args[self::$userParamName] ); - if(!($status_ok = ! is_null( $updaterID ))) { - $msg = $msg_prefix . sprintf( NO_USER_BY_THIS_LOGIN_STR, $this->args[self::$userParamName] ); - $this->errors[] = new IXR_Error( NO_USER_BY_THIS_LOGIN, $msg ); + if ($this->_isParamPresent(self::$userParamName)) { + $updaterID = tlUser::doesUserExist($this->dbObj, + $this->args[self::$userParamName]); + if (! ($status_ok = ! is_null($updaterID))) { + $msg = $msg_prefix . + sprintf(NO_USER_BY_THIS_LOGIN_STR, + $this->args[self::$userParamName]); + $this->errors[] = new IXR_Error(NO_USER_BY_THIS_LOGIN, $msg); } } return $status_ok ? $updaterID : - 1; @@ -6508,57 +7848,61 @@ function updateTestCaseGetUpdater($msg_prefix) { /** */ - function updateTestCaseGetTCVID($tcaseID) { + private function updateTestCaseGetTCVID($tcaseID) + { $status_ok = true; $tcversion_id = - 1; // if user has not provided version number, get last version // no matter if active or not - if(isset( $this->args[self::$versionNumberParamName] )) { - if(($status_ok = $this->checkTestCaseVersionNumber())) { + if (isset($this->args[self::$versionNumberParamName])) { + if ($status_ok = $this->checkTestCaseVersionNumber()) { // Check if version number exists for Test Case $ret = $this->checkTestCaseVersionNumberAncestry(); - if(!($status_ok = $ret['status_ok'])) { - $this->errors[] = new IXR_Error( $ret['error_code'], $msg_prefix . $ret['error_msg'] ); + if (! ($status_ok = $ret['status_ok'])) { + $this->errors[] = new IXR_Error($ret['error_code'], + $msg_prefix . $ret['error_msg']); } } - if($status_ok) { + if ($status_ok) { $opt = array( - 'number' => $this->args[self::$versionNumberParamName] + 'number' => $this->args[self::$versionNumberParamName] ); - $dummy = $this->tcaseMgr->get_basic_info( $tcaseID, $opt ); + $dummy = $this->tcaseMgr->get_basic_info($tcaseID, $opt); $tcversion_id = $dummy[0]['tcversion_id']; } } else { // get latest version info - $dummy = $this->tcaseMgr->get_last_version_info( $tcaseID ); + $dummy = $this->tcaseMgr->getLastVersionInfo($tcaseID); $dummy['tcversion_id'] = $dummy['id']; $tcversion_id = $dummy['tcversion_id']; } return array( - $status_ok, - $tcversion_id + $status_ok, + $tcversion_id ); } /** */ - private function helperGetTestProjectByName($msgPrefix = '') { + private function helperGetTestProjectByName($msgPrefix = '') + { $ret = array( - 'status_ok' => true, - 'info' => null + 'status_ok' => true, + 'info' => null ); - $name = trim( $this->args[self::$testProjectNameParamName] ); - $check_op = $this->tprojectMgr->checkNameExistence( $name ); + $name = trim($this->args[self::$testProjectNameParamName]); + $check_op = $this->tprojectMgr->checkNameExistence($name); $ret['status_ok'] = ! $check_op['status_ok']; - if($ret['status_ok']) { - $ret['info'] = current( $this->tprojectMgr->get_by_name( $name ) ); + if ($ret['status_ok']) { + $ret['info'] = current($this->tprojectMgr->get_by_name($name)); } else { - $msg = $msg_prefix . sprintf( TESTPROJECTNAME_DOESNOT_EXIST_STR, $name ); - $this->errors[] = new IXR_Error( TESTPROJECTNAME_DOESNOT_EXIST, $msg ); + $msg = $msg_prefix . + sprintf(TESTPROJECTNAME_DOESNOT_EXIST_STR, $name); + $this->errors[] = new IXR_Error(TESTPROJECTNAME_DOESNOT_EXIST, $msg); } return $ret; } @@ -6585,10 +7929,11 @@ private function helperGetTestProjectByName($msgPrefix = '') { * - login name => tester * */ - public function assignTestCaseExecutionTask($args) { + public function assignTestCaseExecutionTask($args) + { $msgPrefix = "(" . __FUNCTION__ . ") - "; $args['action'] = 'assignOne'; - return $this->manageTestCaseExecutionTask( $args, $msgPrefix ); + return $this->manageTestCaseExecutionTask($args, $msgPrefix); } /** @@ -6615,12 +7960,13 @@ public function assignTestCaseExecutionTask($args) { * ¸ * */ - public function unassignTestCaseExecutionTask($args) { + public function unassignTestCaseExecutionTask($args) + { $msgPrefix = "(" . __FUNCTION__ . ") - "; - if(! isset( $args['action'] )) { + if (! isset($args['action'])) { $args['action'] = 'unassignOne'; } - return $this->manageTestCaseExecutionTask( $args, $msgPrefix ); + return $this->manageTestCaseExecutionTask($args, $msgPrefix); } /** @@ -6650,93 +7996,101 @@ public function unassignTestCaseExecutionTask($args) { * * @access public */ - public function getTestCaseAssignedTester($args) { + public function getTestCaseAssignedTester($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; $status_ok = true; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); // Checks are done in order $checkFunctions = array( - 'authenticate', - 'checkTestPlanID', - 'checkTestCaseIdentity', - 'checkBuildID' + 'authenticate', + 'checkTestPlanID', + 'checkTestCaseIdentity', + 'checkBuildID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); // Check if requested test case is linke to test plan // if answer is yes, get link info, in order to be able to check if // we need also platform info - if($status_ok) { + if ($status_ok) { $execContext = array( - 'tplan_id' => $this->args[self::$testPlanIDParamName], - 'platform_id' => null, - 'build_id' => $this->args[self::$buildIDParamName] + 'tplan_id' => $this->args[self::$testPlanIDParamName], + 'platform_id' => null, + 'build_id' => $this->args[self::$buildIDParamName] ); $tplan_id = $this->args[self::$testPlanIDParamName]; $tcase_id = $this->args[self::$testCaseIDParamName]; $filters = array( - 'exec_status' => "ALL", - 'active_status' => "ALL", - 'tplan_id' => $tplan_id, - 'platform_id' => null + 'exec_status' => "ALL", + 'active_status' => "ALL", + 'tplan_id' => $tplan_id, + 'platform_id' => null ); - $info = $this->tcaseMgr->get_linked_versions( $tcase_id, $filters, array( + $info = $this->tcaseMgr->get_linked_versions($tcase_id, $filters, + array( 'output' => "feature_id" - ) ); + )); // more than 1 item => we have platforms // access key => tcversion_id, tplan_id, platform_id - $link = current( $info ); - $link = $link[$tplan_id]; - $hits = count( $link ); - $check_platform =(count( $hits ) > 1) || ! isset( $link[0] ); + $link = current($info); + $link = $link[$tplan_id]; // Inside test plan, is indexed by platform + $check_platform = (count($link) > 1) || ! isset($link[0]); } - if($status_ok && $check_platform) { + if ($status_ok && $check_platform) { // this means that platform is MANDATORY - if(! $this->_isParamPresent( self::$platformIDParamName, $msg_prefix ) && ! $this->_isParamPresent( self::$platformNameParamName, $msg_prefix )) { + if (! $this->_isParamPresent(self::$platformIDParamName, $msg_prefix) && + ! $this->_isParamPresent(self::$platformNameParamName, + $msg_prefix)) { $status_ok = false; - $pname = self::$platformNameParamName . ' OR ' . self::$platformIDParamName; - $msg = $messagePrefix . sprintf( MISSING_REQUIRED_PARAMETER_STR, $pname ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); + $pname = self::$platformNameParamName . ' OR ' . + self::$platformIDParamName; + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, $pname); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } else { // get platform_id and check it - if(($status_ok = $this->checkPlatformIdentity( $tplan_id ))) { - $platform_set = $this->tplanMgr->getPlatforms( $tplan_id, array( + if ($status_ok = $this->checkPlatformIdentity($tplan_id)) { + $platform_set = $this->tplanMgr->getPlatforms($tplan_id, + array( 'outputFormat' => 'mapAccessByID', 'outputDetails' => 'name' - ) ); + )); // Now check if link has all 3 components // test plan, test case, platform $platform_id = $this->args[self::$platformIDParamName]; $platform_info = array( - $platform_id => $platform_set[$platform_id] + $platform_id => $platform_set[$platform_id] ); - if(($status_ok = $this->_checkTCIDAndTPIDValid( $platform_info, $msg_prefix ))) { + if ($status_ok = $this->_checkTCIDAndTPIDValid( + $platform_info, $msg_prefix)) { $execContext['platform_id'] = $platform_id; } } } } - if($status_ok) { + if ($status_ok) { $getOpt = array( - 'output' => 'assignment_info', - 'build4assignment' => $execContext['build_id'] + 'output' => 'assignment_info', + 'build4assignment' => $execContext['build_id'] ); - $dummy = $this->tplanMgr->getLinkInfo( $tplan_id, $tcase_id, $platform_id, $getOpt ); + $dummy = $this->tplanMgr->getLinkInfo($tplan_id, $tcase_id, + $platform_id, $getOpt); $resultInfo[0] = array( - 'user_id' => $dummy[0]['user_id'], - 'login' => $dummy[0]['login'], - 'first' => $dummy[0]['first'], - 'last' => $dummy[0]['last'] + 'user_id' => $dummy[0]['user_id'], + 'login' => $dummy[0]['login'], + 'first' => $dummy[0]['first'], + 'last' => $dummy[0]['last'] ); } @@ -6745,8 +8099,8 @@ public function getTestCaseAssignedTester($args) { /** * Returns all bugs linked to a particular testcase on a test plan. - * If there are no filter criteria regarding platform and build, - * result will be get WITHOUT checking for a particular platform and build. + * If there are no filter criteria regarding platform, build and execution, + * result will be get WITHOUT checking for a particular platform, build or execution. * * @param struct $args * @param string $args["devKey"] @@ -6759,7 +8113,7 @@ public function getTestCaseAssignedTester($args) { * Pseudo optional. * if does not is present then testcaseid MUST BE present * - * @param string $args["platformid"]: + * @param int $args["platformid"]: * optional. * ONLY if not present, then $args["platformname"] * will be analized(if exists) @@ -6774,6 +8128,11 @@ public function getTestCaseAssignedTester($args) { * @param int $args["buildname"] * - optional(see $args["buildid"]) * + * @param int $args["executionid"]: + * optional + * Restrict bugs of the given execution. Otherwise, give all + * bugs of all executions of the Test Case (possibly filtered on + * platforms and builds) * * @return mixed $resultInfo * if execution found @@ -6785,70 +8144,88 @@ public function getTestCaseAssignedTester($args) { * * @access public */ - public function getTestCaseBugs($args) { + public function getTestCaseBugs($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); $status_ok = true; - // Checks are done in order - $checkFunctions = array( - 'authenticate', - 'checkTestPlanID', - 'checkTestCaseIdentity' - ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->_checkTCIDAndTPIDValid( null, $msg_prefix ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); - $execContext = array( - 'tplan_id' => $this->args[self::$testPlanIDParamName], - 'platform_id' => null, - 'build_id' => null + 'tplan_id' => $this->args[self::$testPlanIDParamName], + 'platform_id' => null, + 'build_id' => null, + 'execution_id' => null ); // Now we can check for Optional parameters - if($this->_isBuildIDPresent() || $this->_isBuildNamePresent()) { - if(($status_ok = $this->checkBuildID( $msg_prefix ))) { - $execContext['build_id'] = $this->args[self::$buildIDParamName]; + if ($this->_isBuildIDPresent() || + $this->_isBuildNamePresent() && + $status_ok = $this->checkBuildID($msg_prefix)) { + $execContext['build_id'] = $this->args[self::$buildIDParamName]; + } + + if ($status_ok && + $this->_isParamPresent(self::$platformIDParamName, $msg_prefix) || + $this->_isParamPresent(self::$platformNameParamName, $msg_prefix)) { + $status_ok = $this->checkPlatformIdentity( + $this->args[self::$testPlanIDParamName]); + if ($status_ok) { + $execContext['platform_id'] = $this->args[self::$platformIDParamName]; } } - if($status_ok) { - if($this->_isParamPresent( self::$platformIDParamName, $msg_prefix ) || $this->_isParamPresent( self::$platformNameParamName, $msg_prefix )) { - $status_ok = $this->checkPlatformIdentity( $this->args[self::$testPlanIDParamName] ); - if($status_ok) { - $execContext['platform_id'] = $this->args[self::$platformIDParamName]; - } + if ($status_ok && + $this->_isParamPresent(self::$executionIDParamName, $msg_prefix)) { + $status_ok = $this->checkExecutionID($msg_prefix); + if ($status_ok) { + $execContext['execution_id'] = $this->args[self::$executionIDParamName]; } } - if($status_ok) { - $sql = " SELECT id AS exec_id FROM {$this->tables['executions']} " . " WHERE testplan_id = {$this->args[self::$testPlanIDParamName]} " . " AND tcversion_id IN(" . " SELECT id FROM {$this->tables['nodes_hierarchy']} " . " WHERE parent_id = {$this->args[self::$testCaseIDParamName]})"; + if ($status_ok) { + $targetIDs = array(); + if (is_null($execContext['execution_id'])) { + $sql = " SELECT id AS exec_id FROM {$this->tables['executions']} " . + " WHERE testplan_id = {$this->args[self::$testPlanIDParamName]} " . + " AND tcversion_id IN(" . + " SELECT id FROM {$this->tables['nodes_hierarchy']} " . + " WHERE parent_id = {$this->args[self::$testCaseIDParamName]})"; - if(! is_null( $execContext['build_id'] )) { - $sql .= " AND build_id = " . intval( $execContext['build_id'] ); - } + if (! is_null($execContext['build_id'])) { + $sql .= " AND build_id = " . + intval($execContext['build_id']); + } - if(! is_null( $execContext['platform_id'] )) { - $sql .= " AND platform_id = " . intval( $execContext['platform_id'] ); - } + if (! is_null($execContext['platform_id'])) { + $sql .= " AND platform_id = " . + intval($execContext['platform_id']); + } - $rs = $this->dbObj->fetchRowsIntoMap( $sql, 'exec_id' ); - if(is_null( $rs )) { - // has not been executed - // execution id = -1 => test case has not been runned. - $resultInfo[] = array( + $rs = $this->dbObj->fetchRowsIntoMap($sql, 'exec_id'); + if (is_null($rs)) { + // has not been executed + // execution id = -1 => test case has not been runned. + $resultInfo[] = array( 'id' => - 1 - ); - } else { - $targetIDs = array(); - foreach( $rs as $execrun ) { - $targetIDs[] = $execrun['exec_id']; + ); + } else { + foreach ($rs as $execrun) { + $targetIDs[] = $execrun['exec_id']; + } } + } else { + $targetIDs[] = $execContext['execution_id']; + } + + if (! empty($targetIDs)) { $resultInfo[0]['bugs'] = array(); - $sql = " SELECT DISTINCT bug_id FROM {$this->tables['execution_bugs']} " . " WHERE execution_id in(" . implode( ',', $targetIDs ) . ")"; - $resultInfo[0]['bugs'] =( array ) $this->dbObj->get_recordset( $sql ); + $sql = " SELECT DISTINCT bug_id FROM {$this->tables['execution_bugs']} " . + " WHERE execution_id in(" . implode(',', $targetIDs) . ")"; + $resultInfo[0]['bugs'] = (array) $this->dbObj->get_recordset( + $sql); } } @@ -6880,34 +8257,40 @@ public function getTestCaseBugs($args) { * - login name => tester * */ - private function manageTestCaseExecutionTask($args, $msg_prefix) { + private function manageTestCaseExecutionTask($args, $msg_prefix) + { $status_ok = true; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); // Checks are done in order $checkFunctions = array( - 'authenticate', - 'checkTestPlanID', - 'checkTestCaseIdentity', - 'checkBuildID' + 'authenticate', + 'checkTestPlanID', + 'checkTestCaseIdentity', + 'checkBuildID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); - - if($status_ok) { - switch($args['action']) { - case 'assignOne' : - case 'unassignOne' : - if(($status_ok = $this->_isParamPresent( self::$userParamName, $msg_prefix, self::SET_ERROR ))) { - $tester_id = tlUser::doesUserExist( $this->dbObj, $this->args[self::$userParamName] ); - if(!($status_ok = ! is_null( $tester_id ))) { - $msg = $msg_prefix . sprintf( NO_USER_BY_THIS_LOGIN_STR, $this->args[self::$userParamName] ); - $this->errors[] = new IXR_Error( NO_USER_BY_THIS_LOGIN, $msg ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + + if ($status_ok) { + switch ($args['action']) { + case 'assignOne': + case 'unassignOne': + if ($status_ok = $this->_isParamPresent( + self::$userParamName, $msg_prefix, self::SET_ERROR)) { + $tester_id = tlUser::doesUserExist($this->dbObj, + $this->args[self::$userParamName]); + if (! ($status_ok = ! is_null($tester_id))) { + $msg = $msg_prefix . + sprintf(NO_USER_BY_THIS_LOGIN_STR, + $this->args[self::$userParamName]); + $this->errors[] = new IXR_Error( + NO_USER_BY_THIS_LOGIN, $msg); } } break; - case 'unassignAll' : + case 'unassignAll': break; } } @@ -6915,66 +8298,72 @@ private function manageTestCaseExecutionTask($args, $msg_prefix) { // Check if requested test case is linked to test plan // if answer is yes, get link info, in order to be able to check if // we need also platform info - if($status_ok) { + if ($status_ok) { $execContext = array( - 'tplan_id' => $this->args[self::$testPlanIDParamName], - 'platform_id' => null, - 'build_id' => $this->args[self::$buildIDParamName] + 'tplan_id' => $this->args[self::$testPlanIDParamName], + 'platform_id' => null, + 'build_id' => $this->args[self::$buildIDParamName] ); $tplan_id = $this->args[self::$testPlanIDParamName]; $tcase_id = $this->args[self::$testCaseIDParamName]; $filters = array( - 'exec_status' => "ALL", - 'active_status' => "ALL", - 'tplan_id' => $tplan_id, - 'platform_id' => null + 'exec_status' => "ALL", + 'active_status' => "ALL", + 'tplan_id' => $tplan_id, + 'platform_id' => null ); - $info = $this->tcaseMgr->get_linked_versions( $tcase_id, $filters, array( + $info = $this->tcaseMgr->get_linked_versions($tcase_id, $filters, + array( 'output' => "feature_id" - ) ); + )); // more than 1 item => we have platforms // access key => tcversion_id, tplan_id, platform_id - $link = current( $info ); + $link = current($info); $link = $link[$tplan_id]; // Inside test plan, is indexed by platform - $hits = count( $link ); $platform_id = 0; - $check_platform =(count( $hits ) > 1) || ! isset( $link[0] ); + $check_platform = (count($link) > 1) || ! isset($link[0]); } - if($status_ok && $check_platform) { + if ($status_ok && $check_platform) { // this means that platform is MANDATORY - if(! $this->_isParamPresent( self::$platformIDParamName, $msg_prefix ) && ! $this->_isParamPresent( self::$platformNameParamName, $msg_prefix )) { + if (! $this->_isParamPresent(self::$platformIDParamName, $msg_prefix) && + ! $this->_isParamPresent(self::$platformNameParamName, + $msg_prefix)) { $status_ok = false; - $pname = self::$platformNameParamName . ' OR ' . self::$platformIDParamName; - $msg = $messagePrefix . sprintf( MISSING_REQUIRED_PARAMETER_STR, $pname ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); + $pname = self::$platformNameParamName . ' OR ' . + self::$platformIDParamName; + $msg = $messagePrefix . + sprintf(MISSING_REQUIRED_PARAMETER_STR, $pname); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } else { // get platform_id and check it - if(($status_ok = $this->checkPlatformIdentity( $tplan_id ))) { - $platform_set = $this->tplanMgr->getPlatforms( $tplan_id, array( + if ($status_ok = $this->checkPlatformIdentity($tplan_id)) { + $platform_set = $this->tplanMgr->getPlatforms($tplan_id, + array( 'outputFormat' => 'mapAccessByID', 'outputDetails' => 'name' - ) ); + )); // Now check if link has all 3 components // test plan, test case, platform $platform_id = $this->args[self::$platformIDParamName]; $platform_info = array( - $platform_id => $platform_set[$platform_id] + $platform_id => $platform_set[$platform_id] ); - if(($status_ok = $this->_checkTCIDAndTPIDValid( $platform_info, $msg_prefix ))) { + if ($status_ok = $this->_checkTCIDAndTPIDValid( + $platform_info, $msg_prefix)) { $execContext['platform_id'] = $platform_id; } } } } - if($status_ok) { - $assignment_mgr = new assignment_mgr( $this->dbObj ); + if ($status_ok) { + $assignment_mgr = new assignment_mgr($this->dbObj); $types = $assignment_mgr->get_available_types(); // Remove old execution task assignment @@ -6989,33 +8378,28 @@ private function manageTestCaseExecutionTask($args, $msg_prefix) { // `status` int(10) unsigned default '1', // ATTENTION WITH PLATFORMS - $link = is_null( $execContext['platform_id'] ) ? $link[0] : $link[$execContext['platform_id']]; - $feature = array( - $link['feature_id'] => array( - 'build_id' => $execContext['build_id'] - ) - ); + $link = is_null($execContext['platform_id']) ? $link[0] : $link[$execContext['platform_id']]; - switch($args['action']) { - case 'unassignOne' : + switch ($args['action']) { + case 'unassignOne': $signature[] = array( - 'type' => $types['testcase_execution']['id'], - 'user_id' => $tester_id, - 'feature_id' => $link['feature_id'], - 'build_id' => $execContext['build_id'] + 'type' => $types['testcase_execution']['id'], + 'user_id' => $tester_id, + 'feature_id' => $link['feature_id'], + 'build_id' => $execContext['build_id'] ); - $assignment_mgr->deleteBySignature( $signature ); + $assignment_mgr->deleteBySignature($signature); break; - case 'assignOne' : + case 'assignOne': // Step 1 - remove if exists $signature[] = array( - 'type' => $types['testcase_execution']['id'], - 'user_id' => $tester_id, - 'feature_id' => $link['feature_id'], - 'build_id' => $execContext['build_id'] + 'type' => $types['testcase_execution']['id'], + 'user_id' => $tester_id, + 'feature_id' => $link['feature_id'], + 'build_id' => $execContext['build_id'] ); - $assignment_mgr->deleteBySignature( $signature ); + $assignment_mgr->deleteBySignature($signature); // Step 2 - Now assign $assign_status = $assignment_mgr->get_available_status(); @@ -7025,21 +8409,21 @@ private function manageTestCaseExecutionTask($args, $msg_prefix) { $oo[$link['feature_id']]['user_id'] = $tester_id; $oo[$link['feature_id']]['assigner_id'] = $this->userID; $oo[$link['feature_id']]['build_id'] = $execContext['build_id']; - $assignment_mgr->assign( $oo ); + $assignment_mgr->assign($oo); break; - case 'unassignAll' : + case 'unassignAll': $oo[$link['feature_id']]['type'] = $types['testcase_execution']['id']; $oo[$link['feature_id']]['build_id'] = $execContext['build_id']; - $assignment_mgr->delete_by_feature_id_and_build_id( $oo ); + $assignment_mgr->delete_by_feature_id_and_build_id($oo); break; } $resultInfo = array( - "status" => true, - "args" => $this->args + "status" => true, + "args" => $this->args ); - unset( $resultInfo['args']['devKey'] ); + unset($resultInfo['args']['devKey']); } return $status_ok ? $resultInfo : $this->errors; @@ -7047,19 +8431,21 @@ private function manageTestCaseExecutionTask($args, $msg_prefix) { /** */ - public function getProjectKeywords($args) { + public function getProjectKeywords($args) + { $messagePrefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID' + 'authenticate', + 'checkTestProjectID' ); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); - if($status_ok) { - $itemSet = $this->getValidKeywordSet( intval( $this->args[self::$testProjectIDParamName] ), '', true, 'getProjectKeywords' ); - return $itemSet; + if ($status_ok) { + return $this->getValidKeywordSet( + intval($this->args[self::$testProjectIDParamName]), '', true, + 'getProjectKeywords'); } else { return $this->errors; } @@ -7068,66 +8454,117 @@ public function getProjectKeywords($args) { /** * Gets list of keywords for a given Test Case Version * - * @param mixed $testcaseid can be int or array - * mixed $testcaseexternalid can be int or array - * mixed[$version] can be int or array - * if not provided latest version will be used + * @param mixed $testcaseid + * can be int or array + * mixed $testcaseexternalid can be int or array + * mixed[$version] can be int or array + * if not provided latest version will be used * - * @return map indexed by Access Key - * test case internal(DB) ID OR - * test case external ID. + * @return array indexed by Access Key + * test case internal(DB) ID OR + * test case external ID. * * @access public */ - public function getTestCaseKeywords($args) { + public function getTestCaseKeywords($args) + { $msgPrefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); // Prepare material for checkTestCaseSetIdentity() - $a2check = array( self::$testCaseIDParamName, - self::$testCaseExternalIDParamName ); - - foreach( $a2check as $k2c ) { - if(isset( $this->args[$k2c] )) { - $retAsArray = is_array( $this->args[$k2c] ); - $this->args[$k2c] =(array) $this->args[$k2c]; - $outBy = $k2c; - break; - } - } - - $checkFunctions = array('authenticate', - 'checkTestCaseSetIdentity'); - $status_ok = $this->_runChecks( $checkFunctions, $msgPrefix ); - - if($status_ok) { - $hasVersionSet = isset($this->args[self::$versionNumberParamName]); - if( $hasVersionSet ) { - $versionSet = $this->args[self::$versionNumberParamName]; - } - - $kwSet = array(); - $itemSet = $this->args[self::$testCaseIDParamName]; - foreach( $itemSet as $idx => $tcaseID ) { - - $safeTCID = intval($tcaseID); - $accessKey =($outBy == self::$testCaseIDParamName) ? - $tcaseID : $this->args[$outBy][$idx]; - - if( $hasVersionSet && isset($versionSet[$idx]) ) { - $vnum = intval($versionSet[$idx]); - $tcversion_id = getTCVersionIDFromVersion($safeTCID,$vnum); - } else { - $tcversion_id = $this->tcaseMgr->getLatestVersionID($safeTCID); + $a2check = array( + self::$testCaseIDParamName, + self::$testCaseExternalIDParamName + ); + + foreach ($a2check as $k2c) { + if (isset($this->args[$k2c])) { + $this->args[$k2c] = (array) $this->args[$k2c]; + $outBy = $k2c; + break; + } + } + + $checkFunctions = array( + 'authenticate', + 'checkTestCaseSetIdentity' + ); + $status_ok = $this->_runChecks($checkFunctions, $msgPrefix); + + if ($status_ok) { + $hasVersionSet = isset($this->args[self::$versionNumberParamName]); + if ($hasVersionSet) { + $versionSet = $this->args[self::$versionNumberParamName]; } - $kwSet[$accessKey] = - $this->tcaseMgr->get_keywords_map($safeTCID,$tcversion_id); - } - return $kwSet; + $kwSet = array(); + $itemSet = $this->args[self::$testCaseIDParamName]; + foreach ($itemSet as $idx => $tcaseID) { + + $safeTCID = intval($tcaseID); + $accessKey = ($outBy == self::$testCaseIDParamName) ? $tcaseID : $this->args[$outBy][$idx]; + + if ($hasVersionSet && isset($versionSet[$idx])) { + $vnum = intval($versionSet[$idx]); + $tcversion_id = getTCVersionIDFromVersion($safeTCID, $vnum); + } else { + $tcversion_id = $this->tcaseMgr->getLatestVersionID( + $safeTCID); + } + + $kwSet[$accessKey] = $this->tcaseMgr->get_keywords_map( + $safeTCID, $tcversion_id); + } + return $kwSet; } else { - return $this->errors; + return $this->errors; + } + } + + /** + * Gets list of requirements for a given Test case version + * + * The user must have Req view mgt privilege on the project + * containing the given TC + * + * @param struct $args + * @param string $args["devKey"] + * @param int $args["testcaseversionid"] + * + * @return array requirement list, if success + * error info, if failure + * + * @access public + * + * compatible with TL version >= 1.9.18 + * + */ + public function getTestCaseRequirements($args) + { + $msgPrefix = "(" . __FUNCTION__ . ") - "; + $this->_setArgs($args); + $checkFunctions = array( + 'authenticate', + 'checkTestCaseVersionID' + ); + + $status_ok = $this->_runChecks($checkFunctions, $msgPrefix); + + if ($status_ok) { + // set the project as context + $tcaseVersionID = $this->args[self::$testCaseVersionIDParamName]; + $tcase_tprojectID = $this->tcaseMgr->get_testproject( + $tcaseVersionID); + $context[self::$testProjectIDParamName] = $tcase_tprojectID; + + $status_ok = $this->userHasRight("mgt_view_req", false, $context); + + if ($status_ok) { + $itemSet = $this->reqMgr->getGoodForTCVersion($tcaseVersionID); + } } + + return $status_ok ? $itemSet : $this->errors; } /** @@ -7138,35 +8575,38 @@ public function getTestCaseKeywords($args) { * @param int $args["$tplanID"] * * @return mixed $resultInfo - * [status] => true/false of success - * [id] => result id or error code - * [message] => optional message for error message string + * [status] => true/false of success + * [id] => result id or error code + * [message] => optional message for error message string * @access public */ - public function deleteTestPlan($args) { + public function deleteTestPlan($args) + { $resultInfo = array(); $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo[0]["status"] = false; $checkFunctions = array( - 'authenticate', - 'checkTestPlanID' + 'authenticate', + 'checkTestPlanID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { - if($this->userHasRight( "exec_delete", self::CHECK_PUBLIC_PRIVATE_ATTR )) { - $this->tplanMgr->delete( $args[self::$testPlanIDParamName] ); + if ($status_ok) { + if ($this->userHasRight("exec_delete", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { + $this->tplanMgr->delete($args[self::$testPlanIDParamName]); $resultInfo[0]["status"] = true; $resultInfo[0]["id"] = $args[self::$testPlanIDParamName]; $resultInfo[0]["message"] = GENERAL_SUCCESS_STR; $resultInfo[0]["operation"] = $operation; } else { $status_ok = false; - $this->errors[] = new IXR_Error( CFG_DELETE_EXEC_DISABLED, CFG_DELETE_EXEC_DISABLED_STR ); + $this->errors[] = new IXR_Error(CFG_DELETE_EXEC_DISABLED, + CFG_DELETE_EXEC_DISABLED_STR); } } @@ -7187,11 +8627,13 @@ public function deleteTestPlan($args) { * @return mixed $resultInfo * */ - function addTestCaseKeywords($args) { - $ret = $this->checksForManageTestCaseKeywords( $args, 'add' ); - if($ret['status_ok']) { + private function addTestCaseKeywords($args) + { + $ret = $this->checksForManageTestCaseKeywords($args, 'add'); + if ($ret['status_ok']) { $kwSet = $this->args[self::$keywordNameParamName]; - return $this->manageTestCaseKeywords( $kwSet, $ret['tprojectSet'], 'add' ); + return $this->manageTestCaseKeywords($kwSet, $ret['tprojectSet'], + 'add'); } return $this->errors; } @@ -7207,11 +8649,13 @@ function addTestCaseKeywords($args) { * @internal revisions * @since 1.9.14 */ - function removeTestCaseKeywords($args) { - $ret = $this->checksForManageTestCaseKeywords( $args, 'remove' ); - if($ret['status_ok']) { + private function removeTestCaseKeywords($args) + { + $ret = $this->checksForManageTestCaseKeywords($args, 'remove'); + if ($ret['status_ok']) { $kwSet = $this->args[self::$keywordNameParamName]; - return $this->manageTestCaseKeywords( $kwSet, $ret['tprojectSet'], 'remove' ); + return $this->manageTestCaseKeywords($kwSet, $ret['tprojectSet'], + 'remove'); } return $this->errors; } @@ -7220,38 +8664,41 @@ function removeTestCaseKeywords($args) { * * @used by manageTestCaseKeywords */ - protected function checksForManageTestCaseKeywords($args, $action) { - $operation = str_replace( 'checksForManage', $action, __FUNCTION__ ); + protected function checksForManageTestCaseKeywords($args, $action) + { + $operation = str_replace('checksForManage', $action, __FUNCTION__); $msg_prefix = "({$operation}) - "; - $resultInfo = array(); - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate' + 'authenticate' ); // Check on user rights can have some problems if test cases do not belong // to same test project - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); - if($status_ok) { - $items = array_keys( $this->args[self::$keywordNameParamName] ); - $status_ok = $this->checkTestCaseSetIdentity( $msg_prefix, $items ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + if ($status_ok) { + $items = array_keys($this->args[self::$keywordNameParamName]); + $status_ok = $this->checkTestCaseSetIdentity($msg_prefix, $items); } - if($status_ok) { + if ($status_ok) { // Get test projects $idSet = $this->args[self::$testCaseIDParamName]; - foreach( $idSet as $key => $val ) { + foreach ($idSet as $key => $val) { // indexed by same value than keywords - $tprojectSet[$items[$key]] = $this->tcaseMgr->get_testproject( $val ); + $tprojectSet[$items[$key]] = $this->tcaseMgr->get_testproject( + $val); // Do authorization checks, all or nothing // userHasRight() on failure set error to return to caller - $status_ok = $this->userHasRight( "mgt_modify_tc", self::CHECK_PUBLIC_PRIVATE_ATTR, array( + $status_ok = $this->userHasRight("mgt_modify_tc", + self::CHECK_PUBLIC_PRIVATE_ATTR, + array( self::$testProjectIDParamName => $tprojectSet[$items[$key]] - ) ); - if(! $status_ok) { + )); + if (! $status_ok) { break; } } @@ -7274,20 +8721,22 @@ protected function checksForManageTestCaseKeywords($args, $action) { * @return mixed $resultInfo * */ - protected function manageTestCaseKeywords($keywords, $tprojects, $action) { - switch($action) { - case 'add' : + protected function manageTestCaseKeywords($keywords, $tprojects, $action) + { + switch ($action) { + case 'add': $method2call = 'addKeywords'; break; - case 'delete' : - case 'remove' : + case 'delete': + case 'remove': $method2call = 'deleteKeywords'; break; - default : + default: $resultInfo['status_ok'] = false; - $resultInfo['verbose'] = __FUNCTION__ . ' :: Banzai!! - No valida method'; + $resultInfo['verbose'] = __FUNCTION__ . + ' :: Banzai!! - No valida method'; return $resultInfo; break; } @@ -7296,22 +8745,26 @@ protected function manageTestCaseKeywords($keywords, $tprojects, $action) { $resultInfo['validKeywords'] = null; $resultInfo['status_ok'] = true; - foreach( $keywords as $ak => $kwset ) { - $kw[$ak] = $this->getValidKeywordSet( $tprojects[$ak], implode( ",", $kwset ), true, true ); + foreach ($keywords as $ak => $kwset) { + $kw[$ak] = $this->getValidKeywordSet($tprojects[$ak], + implode(",", $kwset), true, true); $resultInfo['validKeywords'][$ak] = $kw[$ak]; - $resultInfo['status_ok'] = $resultInfo['status_ok'] &&($kw[$ak] != ''); + $resultInfo['status_ok'] = $resultInfo['status_ok'] && + ($kw[$ak] != ''); } - if($resultInfo['status_ok']) { + if ($resultInfo['status_ok']) { $cacheLTCV = array(); - foreach( $kw as $ak => $val ) { + foreach ($kw as $ak => $val) { $tcaseID = $this->tcaseE2I[$ak]; - if(! isset( $cacheLTCV[$tcaseID] )) { - $cacheLTCV[$tcaseID] = $this->tcaseMgr->getLatestVersionID( $tcaseID ); + if (! isset($cacheLTCV[$tcaseID])) { + $cacheLTCV[$tcaseID] = $this->tcaseMgr->getLatestVersionID( + $tcaseID); } - $this->tcaseMgr->$method2call( $this->tcaseE2I[$ak], $cacheLTCV[$tcaseID], array_keys( $val ) ); + $this->tcaseMgr->$method2call($this->tcaseE2I[$ak], + $cacheLTCV[$tcaseID], array_keys($val)); } } @@ -7333,10 +8786,11 @@ protected function manageTestCaseKeywords($keywords, $tprojects, $action) { * @return boolean * @access protected */ - protected function checkTestCaseSetIdentity($messagePrefix = '', $itemSet = null) { + protected function checkTestCaseSetIdentity($messagePrefix = '', + $itemSet = null) + { // Three Cases - Internal ID, External ID, No Id $status_ok = false; - $fromExternal = false; $fromInternal = false; $fromItemSet = false; @@ -7344,56 +8798,59 @@ protected function checkTestCaseSetIdentity($messagePrefix = '', $itemSet = null $tcaseIDSet = null; $tcaseE2I = null; // External to Internal - if(! is_null( $itemSet )) { - $fromExternal = true; + if (! is_null($itemSet)) { $fromItemSet = true; $errorCode = INVALID_TESTCASE_EXTERNAL_ID; $msg = $messagePrefix . INVALID_TESTCASE_EXTERNAL_ID_STR; - foreach( $itemSet as $tcaseExternalID ) { - $tcaseE2I[$tcaseExternalID] = $tcaseIDSet[] = intval( $this->tcaseMgr->getInternalID( $tcaseExternalID ) ); + foreach ($itemSet as $tcaseExternalID) { + $tcaseE2I[$tcaseExternalID] = $tcaseIDSet[] = intval( + $this->tcaseMgr->getInternalID($tcaseExternalID)); } } - if($this->_isTestCaseExternalIDPresent()) { - $fromExternal = true; + if ($this->_isTestCaseExternalIDPresent()) { $errorCode = INVALID_TESTCASE_EXTERNAL_ID; $msg = $messagePrefix . INVALID_TESTCASE_EXTERNAL_ID_STR; - foreach( $this->args[self::$testCaseExternalIDParamName] as $tcaseExternalID ) { - $tcaseIDSet[] = intval( $this->tcaseMgr->getInternalID( $tcaseExternalID ) ); + foreach ($this->args[self::$testCaseExternalIDParamName] as $tcaseExternalID) { + $tcaseIDSet[] = intval( + $this->tcaseMgr->getInternalID($tcaseExternalID)); } } - if($this->_isTestCaseIDPresent()) { + if ($this->_isTestCaseIDPresent()) { $fromInternal = true; $errorCode = INVALID_TESTCASE_EXTERNAL_ID; $msg = $messagePrefix . INVALID_TCASEID_STR; $tcaseIDSet = $this->args[self::$testCaseIDParamName]; } - if(! is_null( $tcaseIDSet )) { + if (! is_null($tcaseIDSet)) { $status_ok = true; - foreach( $tcaseIDSet as $idx => $tcaseID ) { - if((($tcaseID = intval( $tcaseID )) <= 0) ||(! $this->_isTestCaseIDValid( $tcaseID, $messagePrefix ))) { + foreach ($tcaseIDSet as $idx => $tcaseID) { + if ((($tcaseID = intval($tcaseID)) <= 0) || + (! $this->_isTestCaseIDValid($tcaseID, $messagePrefix))) { $status_ok = false; - if($fromInternal) { - $this->errors[] = new IXR_Error( $errorCode, sprintf( $msg, $tcaseID ) ); + if ($fromInternal) { + $this->errors[] = new IXR_Error($errorCode, + sprintf($msg, $tcaseID)); } else { - if($fromItemSet) { + if ($fromItemSet) { $tcaseExternalID = $itemSet[$idx]; } else { $tcaseExternalID = $this->args[self::$testCaseExternalIDParamName][$idx]; } - $this->errors[] = new IXR_Error( $errorCode, sprintf( $msg, $tcaseExternalID ) ); + $this->errors[] = new IXR_Error($errorCode, + sprintf($msg, $tcaseExternalID)); } } } } - if($status_ok) { - $this->_setTestCaseID( $tcaseIDSet ); + if ($status_ok) { + $this->_setTestCaseID($tcaseIDSet); $this->tcaseE2I = $tcaseE2I; } @@ -7402,10 +8859,11 @@ protected function checkTestCaseSetIdentity($messagePrefix = '', $itemSet = null /** */ - private function getTcaseDbId($items) { + private function getTcaseDbId($items) + { $tcaseIDSet = null; - foreach( $items as $idx => $eID ) { - $tcaseIDSet[$idx] = intval( $this->tcaseMgr->getInternalID( $eID ) ); + foreach ($items as $idx => $eID) { + $tcaseIDSet[$idx] = intval($this->tcaseMgr->getInternalID($eID)); } return $tcaseIDSet; } @@ -7418,39 +8876,45 @@ private function getTcaseDbId($items) { * @param int $args["prefix"] * * @return mixed $resultInfo - * [status] => true/false of success - * [message] => optional message for error message string + * [status] => true/false of success + * [message] => optional message for error message string * @access public */ - public function deleteTestProject($args) { + public function deleteTestProject($args) + { $resultInfo = array(); $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo[0]["status"] = false; $checkFunctions = array( - 'authenticate' + 'authenticate' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { - $status_ok = $this->userHasRight( "mgt_modify_product" ); + if ($status_ok) { + $status_ok = $this->userHasRight("mgt_modify_product"); } - if($status_ok) { - $status_ok = $this->_isParamPresent( self::$prefixParamName, $msg_prefix, true ); + if ($status_ok) { + $status_ok = $this->_isParamPresent(self::$prefixParamName, + $msg_prefix, true); } - if($status_ok) { - if(($info = $this->tprojectMgr->get_by_prefix( $this->args[self::$prefixParamName] ))) { - $this->tprojectMgr->delete( $info['id'] ); + if ($status_ok) { + if ($info = $this->tprojectMgr->get_by_prefix( + $this->args[self::$prefixParamName])) { + $this->tprojectMgr->delete($info['id']); $resultInfo[0]["status"] = true; } else { $status_ok = false; - $msg = $msg_prefix . sprintf( TPROJECT_PREFIX_DOESNOT_EXIST_STR, $this->args[self::$prefixParamName] ); - $this->errors[] = new IXR_Error( TPROJECT_PREFIX_DOESNOT_EXIST, $msg ); + $msg = $msg_prefix . + sprintf(TPROJECT_PREFIX_DOESNOT_EXIST_STR, + $this->args[self::$prefixParamName]); + $this->errors[] = new IXR_Error(TPROJECT_PREFIX_DOESNOT_EXIST, + $msg); } } @@ -7485,63 +8949,67 @@ public function deleteTestProject($args) { * * @access public */ - public function updateTestSuiteCustomFieldDesignValue($args) { + public function updateTestSuiteCustomFieldDesignValue($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkTestSuiteID' + 'authenticate', + 'checkTestProjectID', + 'checkTestSuiteID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { - if(! $this->_isParamPresent( self::$customFieldsParamName )) { - $status_ok = false; - $msg = sprintf( MISSING_REQUIRED_PARAMETER_STR, self::$customFieldsParamName ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); - } + if ($status_ok && ! $this->_isParamPresent(self::$customFieldsParamName)) { + $status_ok = false; + $msg = sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$customFieldsParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } - if($status_ok) { + if ($status_ok) { // now check if custom fields are ok // For each custom field need to check if: // 1. is linked to test project // 2. is available for Test Suite at design time - $cfieldMgr = new cfield_mgr( $this->dbObj ); + $cfieldMgr = new cfield_mgr($this->dbObj); // Just ENABLED - $linkedSet = $cfieldMgr->get_linked_cfields_at_design( $this->args[self::$testProjectIDParamName], cfield_mgr::ENABLED, null, 'testsuite', null, 'name' ); - if(is_null( $linkedSet )) { + $linkedSet = $cfieldMgr->get_linked_cfields_at_design( + $this->args[self::$testProjectIDParamName], cfield_mgr::ENABLED, + null, 'testsuite', null, 'name'); + if (is_null($linkedSet)) { $status_ok = false; $msg = NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES_STR; - $this->errors[] = new IXR_Error( NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES, $msg ); + $this->errors[] = new IXR_Error( + NO_CUSTOMFIELDS_DT_LINKED_TO_TESTSUITES, $msg); } } - if($status_ok) { + if ($status_ok) { $cfSet = $args[self::$customFieldsParamName]; $itemID = $args[self::$testSuiteIDParamName]; - foreach( $cfSet as $cfName => $cfValue ) { + foreach ($cfSet as $cfName => $cfValue) { // $accessKey = "custom_field_" . $item['id'] . _ // design_values_to_db($hash,$node_id,$cf_map=null,$hash_type=null) // // Simple check: if name is not present on set => ignore - if(isset( $linkedSet[$cfName] )) { + if (isset($linkedSet[$cfName])) { $item = $linkedSet[$cfName]; - $accessKey = "custom_field_" . $item['type'] . '_' . $item['id']; + $accessKey = "custom_field_" . $item['type'] . '_' . + $item['id']; $hash[$accessKey] = $cfValue; - $cfieldMgr->design_values_to_db( $hash, $itemID ); + $cfieldMgr->design_values_to_db($hash, $itemID); $ret[] = array( - 'status' => 'ok', - 'msg' => 'Custom Field:' . $cfName . ' processed ' + 'status' => 'ok', + 'msg' => 'Custom Field:' . $cfName . ' processed ' ); } else { $ret[] = array( - 'status' => 'ko', - 'msg' => 'Custom Field:' . $cfName . ' skipped ' + 'status' => 'ko', + 'msg' => 'Custom Field:' . $cfName . ' skipped ' ); } @@ -7580,64 +9048,71 @@ public function updateTestSuiteCustomFieldDesignValue($args) { * * @access public */ - public function updateBuildCustomFieldsValues($args) { + public function updateBuildCustomFieldsValues($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID', - 'checkBuildID' + 'authenticate', + 'checkTestProjectID', + 'checkBuildID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { - if(! $this->_isParamPresent( self::$customFieldsParamName )) { - $status_ok = false; - $msg = sprintf( MISSING_REQUIRED_PARAMETER_STR, self::$customFieldsParamName ); - $this->errors[] = new IXR_Error( MISSING_REQUIRED_PARAMETER, $msg ); - } + if ($status_ok && ! $this->_isParamPresent(self::$customFieldsParamName)) { + $status_ok = false; + $msg = sprintf(MISSING_REQUIRED_PARAMETER_STR, + self::$customFieldsParamName); + $this->errors[] = new IXR_Error(MISSING_REQUIRED_PARAMETER, $msg); } - if($status_ok) { + if ($status_ok) { // now check if custom fields are ok // For each custom field need to check if: // 1. is linked to test project // 2. is available for Build at design time - $cfieldMgr = new cfield_mgr( $this->dbObj ); + $cfieldMgr = new cfield_mgr($this->dbObj); // Just ENABLED - $linkedSet = $cfieldMgr->get_linked_cfields_at_design( $this->args[self::$testProjectIDParamName], cfield_mgr::ENABLED, null, 'build', null, 'name' ); - if(is_null( $linkedSet )) { + $linkedSet = $cfieldMgr->get_linked_cfields_at_design( + $this->args[self::$testProjectIDParamName], cfield_mgr::ENABLED, + null, 'build', null, 'name'); + if (is_null($linkedSet)) { $status_ok = false; $msg = NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS_STR; - $this->errors[] = new IXR_Error( NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS, $msg ); + $this->errors[] = new IXR_Error( + NO_CUSTOMFIELDS_DT_LINKED_TO_BUILDS, $msg); } } - if($status_ok) { + if ($status_ok) { $cfSet = $args[self::$customFieldsParamName]; $ret = array(); - foreach( $cfSet as $cfName => $cfValue ) { + foreach ($cfSet as $cfName => $cfValue) { // $accessKey = "custom_field_" . $item['id'] . _ // design_values_to_db($hash,$node_id,$cf_map=null,$hash_type=null) // // Simple check: if name is not present on set => ignore - if(isset( $linkedSet[$cfName] )) { + if (isset($linkedSet[$cfName])) { $item = $linkedSet[$cfName]; - $accessKey = "custom_field_" . $item['type'] . '_' . $item['id']; + $accessKey = "custom_field_" . $item['type'] . '_' . + $item['id']; $hash[$accessKey] = $cfValue; - $cfieldMgr->design_values_to_db( $hash, $args[self::$buildIDParamName], null, null, 'build' ); + $cfieldMgr->design_values_to_db($hash, + $args[self::$buildIDParamName], null, null, 'build'); // Add the result for each custom field to the returned array - array_push( $ret, array( + array_push($ret, + array( 'status' => 'ok', 'msg' => 'Custom Field:' . $cfName . ' processed ' - ) ); + )); } else { - array_push( $ret, array( + array_push($ret, + array( 'status' => 'ko', 'msg' => 'Custom Field:' . $cfName . ' skipped ' - ) ); + )); } } // Return the result after all of the fields have been processed @@ -7660,29 +9135,31 @@ public function updateBuildCustomFieldsValues($args) { * * @access public */ - public function getTestSuite($args) { + public function getTestSuite($args) + { $ope = __FUNCTION__; $msg_prefix = "({$ope}) - "; - $this->_setArgs( $args ); - $status_ok = $this->_runChecks( array( - 'authenticate' - ), $msg_prefix ); + $this->_setArgs($args); + $status_ok = $this->_runChecks(array( + 'authenticate' + ), $msg_prefix); - if($status_ok) { + if ($status_ok) { // Check for mandatory parameters $k2s = array( - self::$testSuiteNameParamName, - self::$prefixParamName + self::$testSuiteNameParamName, + self::$prefixParamName ); - foreach( $k2s as $target ) { - $ok = $this->_isParamPresent( $target, $msg_prefix, self::SET_ERROR ); + foreach ($k2s as $target) { + $ok = $this->_isParamPresent($target, $msg_prefix, + self::SET_ERROR); $status_ok = $status_ok && $ok; } } - if($status_ok) { + if ($status_ok) { // optionals // $details='simple'; // $k2s=self::$detailsParamName; @@ -7692,39 +9169,41 @@ public function getTestSuite($args) { // } } - if($status_ok) { - $tprojectMgr = new testproject( $this->dbObj ); + if ($status_ok) { + $tprojectMgr = new testproject($this->dbObj); $pfx = $this->args[self::$prefixParamName]; - $tproj = $tprojectMgr->get_by_prefix( $pfx ); + $tproj = $tprojectMgr->get_by_prefix($pfx); - if(is_null( $tproj )) { + if (is_null($tproj)) { $status_ok = false; - $msg = $msg_prefix . sprintf( TPROJECT_PREFIX_DOESNOT_EXIST_STR, $pfx ); - $this->errors[] = new IXR_Error( TPROJECT_PREFIX_DOESNOT_EXIST, $msg ); + $msg = $msg_prefix . + sprintf(TPROJECT_PREFIX_DOESNOT_EXIST_STR, $pfx); + $this->errors[] = new IXR_Error(TPROJECT_PREFIX_DOESNOT_EXIST, + $msg); } else { $ctx[self::$testProjectIDParamName] = $dummy['id']; } } - if($status_ok && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR, $ctx )) { + if ($status_ok && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR, + $ctx)) { $opt = array( - 'recursive' => false, - 'exclude_testcases' => true + 'recursive' => false, + 'exclude_testcases' => true ); - // $target = $this->dbObj->prepare_string($tg); - // $filters['additionalWhereClause'] = - // " AND name = '{$target}' "; $filters = null; - $items = $tprojectMgr->get_subtree( $tproj['id'], $filters, $opt ); + $items = $tprojectMgr->get_subtree($tproj['id'], $filters, $opt); $ni = array(); - if(! is_null( $items ) &&($l2d = count( $items )) > 0) { + if (! empty($items)) { + $l2d = count($items); $tg = $this->args[self::$testSuiteNameParamName]; - for($ydx = 0; $ydx <= $l2d; $ydx ++) { - if(strcmp( $items[$ydx]['name'], $tg ) == 0) { - unset( $items[$ydx]['tcversion_id'] ); + for ($ydx = 0; $ydx <= $l2d; $ydx ++) { + if (strcmp($items[$ydx]['name'], $tg) == 0) { + unset($items[$ydx]['tcversion_id']); $ni[] = $items[$ydx]; } } @@ -7734,7 +9213,9 @@ public function getTestSuite($args) { } return $status_ok ? $ni : $this->errors; - } // function end + } + + // function end /** * Get Issue Tracker System by name @@ -7746,48 +9227,60 @@ public function getTestSuite($args) { * @return mixed $itsObject * @access public */ - public function getIssueTrackerSystem($args, $call = null) { + public function getIssueTrackerSystem($args, $call = null) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); - $extCall = is_null( $call ); - if($extCall) { - $this->authenticate(); + $extCall = is_null($call); + if ($extCall) { + $status_ok = $this->authenticate(); + if (! $status_ok) { + return $this->errors; + } + if (! $this->userHasRight("issuetracker_view", + self::CHECK_PUBLIC_PRIVATE_ATTR)) { + $msg = sprintf(INSUFFICIENT_RIGHTS_STR); + $this->errors[] = new IXR_Error(INSUFFICIENT_RIGHTS, + $msg_prefix . $msg); + return $this->errors; + } } $ret = null; - if(is_null( $this->itsMgr )) { - $this->itsMgr = new tlIssueTracker( $this->dbObj ); + if (is_null($this->itsMgr)) { + $this->itsMgr = new tlIssueTracker($this->dbObj); } - $ret = $this->itsMgr->getByName( $this->args[self::$itsNameParamName] ); - $status_ok = ! is_null( $ret ); - if(! $status_ok) { - $msg = $msg_prefix . sprintf( ITS_NOT_FOUND_STR, $this->args[self::$itsNameParamName] ); - $this->errors[] = new IXR_Error( ITS_NOT_FOUND, $msg ); + $ret = $this->itsMgr->getByName($this->args[self::$itsNameParamName]); + $status_ok = ! is_null($ret); + if (! $status_ok) { + $msg = $msg_prefix . + sprintf(ITS_NOT_FOUND_STR, $this->args[self::$itsNameParamName]); + $this->errors[] = new IXR_Error(ITS_NOT_FOUND, $msg); } - if($extCall) { - if(! $status_ok) { - $ret = $this->errors; - } + if ($extCall && ! $status_ok) { + $ret = $this->errors; } return $ret; } /** */ - function validateDateISO8601($dateAsString) { - return $this->validateDate( $dateAsString ); + private function validateDateISO8601($dateAsString) + { + return $this->validateDate($dateAsString); } /** */ - function validateDate($dateAsString, $format = 'Y-m-d') { - $d = DateTime::createFromFormat( $format, $dateAsString ); - return $d && $d->format( $format ) == $dateAsString; + private function validateDate($dateAsString, $format = 'Y-m-d') + { + $d = DateTime::createFromFormat($format, $dateAsString); + return $d && $d->format($format) == $dateAsString; } /** @@ -7799,52 +9292,142 @@ function validateDate($dateAsString, $format = 'Y-m-d') { * @param string $args["platformid"] * OPTIONAL * - * @return mixed error if someting's wrong, else array of test cases + * @return mixed error if someting's wrong, else array of requirements * * @access public */ - public function getRequirements($args) { + public function getRequirements($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID' + 'authenticate', + 'checkTestProjectID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); - if($status_ok) { + if ($status_ok) { $context['tproject_id'] = $this->args[self::$testProjectIDParamName]; // check if a context(test plan/platform) is provided - if($this->_isParamPresent( self::$testPlanIDParamName )) { - $status_ok = $this->checkTestPlanID( $msg_prefix ); + if ($this->_isParamPresent(self::$testPlanIDParamName)) { + $status_ok = $this->checkTestPlanID($msg_prefix); $context['tplan_id'] = $this->args[self::$testPlanIDParamName]; - if($status_ok) { - if($this->_isParamPresent( self::$platformIDParamName )) { - $status_ok = $this->checkPlatformIdentity( $this->args[self::$testPlanIDParamName], null, $msg_prefix ); - $context['platform_id'] = $this->args[self::$platformIDParamName]; - } + if ($status_ok && + $this->_isParamPresent(self::$platformIDParamName)) { + $status_ok = $this->checkPlatformIdentity( + $this->args[self::$testPlanIDParamName], null, + $msg_prefix); + $context['platform_id'] = $this->args[self::$platformIDParamName]; } } } - if($status_ok) { - $dummy = $this->reqMgr->getAllByContext( $context ); - if(! is_null( $dummy )) - $req = array_values( $dummy ); - else + if ($status_ok) { + $context_for_rights = array( + self::$testProjectIDParamName => $this->args[self::$testProjectIDParamName], + self::$testPlanIDParamName => $this->args[self::$testPlanIDParamName] + ); + $status_ok = $this->userHasRight('mgt_view_req', + self::CHECK_PUBLIC_PRIVATE_ATTR, $context_for_rights); + } + + if ($status_ok) { + $dummy = $this->reqMgr->getAllByContext($context); + if (! is_null($dummy)) { + $req = array_values($dummy); + } else { $status_ok = false; + } } return $status_ok ? $req : $this->errors; } + /** + * Get requirement + * + * @param struct $args + * @param string $args["devKey"] + * @param string $args["testprojectid"]: + * @param int $args["requirementid"]: + * OPTIONAL, if not present, reqdocid must be present + * @param string $args["requirementdocid"]: + * OPTIONAL, if not present, reqid must be present + * @param string $args["version"] + * OPTIONAL + * @param string $args["requirementversionid"] + * OPTIONAL + * If neither version nor versionid are present, the latest version is used + * + * @return mixed error if someting's wrong, else a requirement version + * + * @access public + */ + public function getRequirement($args) + { + $msg_prefix = "(" . __FUNCTION__ . ") - "; + $this->_setArgs($args); + + $checkFunctions = array( + 'authenticate', + 'checkTestProjectID', + 'checkReqIdentity' + ); + + $status = $this->_runChecks($checkFunctions, $msg_prefix); + + if ($status) { + $context = array( + self::$testProjectIDParamName => $this->args[self::$testProjectIDParamName] + ); + $status = $this->userHasRight('mgt_view_req', + self::CHECK_PUBLIC_PRIVATE_ATTR, $context); + } + + if ($status) { + $latest = true; + $version = false; + + if ($this->_isParamPresent(self::$requirementVersionIDParamName)) { + if (! $this->checkReqVersionID($msg_prefix)) { + return false; + } else { + $reqVersionID = $this->args[self::$requirementVersionIDParamName]; + $latest = false; + } + } elseif ($this->_isParamPresent(self::$versionNumberParamName)) { + if (! $this->checkVersionNumber()) { + return false; + } else { + $reqVersionNumber = $this->args[self::$versionNumberParamName]; + $latest = false; + $version = true; + } + } + + $reqID = $this->args[self::$requirementIDParamName]; + if ($latest) { + $reqVersionID = requirement_mgr::LATEST_VERSION; + } + + if ($version) { + $resultInfo = $this->reqMgr->get_by_id($reqID, null, + $reqVersionNumber); + } else { + $resultInfo = $this->reqMgr->get_by_id($reqID, $reqVersionID); + } + } + + return $status ? $resultInfo : $this->errors; + } + /** * Get requirement coverage * - * Retrieve the test cases associated to a requirement + * Retrieve the test cases versions associated to a requirement version * * @param struct $args * @param string $args["devKey"]: @@ -7852,37 +9435,36 @@ public function getRequirements($args) { * if devKey is not valid => abort. * * @param string $args["testprojectid"] - * @param string $args["requirementdocid"] + * @param string $args["requirementversionid"] * * @return mixed error if someting's wrong, else array of test cases * * @access public */ - public function getReqCoverage($args) { + public function getReqCoverage($args) + { $msg_prefix = "(" . __FUNCTION__ . ") - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); $checkFunctions = array( - 'authenticate', - 'checkTestProjectID' + 'authenticate', + 'checkTestProjectID', + 'checkReqVersionID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->userHasRight( 'mgt_view_req', self::CHECK_PUBLIC_PRIVATE_ATTR ); - - if($status_ok) { - // check req id exists in the project - $reqDocID = $this->args[self::$requirementDocIDParamName]; - $req = $this->reqMgr->getByDocID( $reqDocID, $this->args[self::$testProjectIDParamName], null, array( - 'access_key' => 'req_doc_id', - 'output' => 'minimun' - ) ); - if(! is_null( $req )) { - $resultInfo = $this->reqMgr->get_coverage( $req[$reqDocID]['id'] ); - } else { - $msg = $msg_prefix . sprintf( NO_REQ_IN_THIS_PROJECT_STR, $reqDocID, $this->args[self::$testProjectIDParamName] ); - $this->errors[] = new IXR_Error( NO_REQ_IN_THIS_PROJECT, $msg ); - $status_ok = false; - } + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + + if ($status_ok) { + $context = array( + self::$testProjectIDParamName => $this->args[self::$testProjectIDParamName] + ); + $status_ok = $this->userHasRight('mgt_view_req', + self::CHECK_PUBLIC_PRIVATE_ATTR, $context); + } + + if ($status_ok) { + $reqVersionID = $this->args[self::$requirementVersionIDParamName]; + $resultInfo = $this->reqMgr->getActiveForReqVersion($reqVersionID); } return $status_ok ? $resultInfo : $this->errors; } @@ -7896,61 +9478,66 @@ public function getReqCoverage($args) { * @param int $args["testsuiteid"] * */ - public function setTestCaseTestSuite($args) { + public function setTestCaseTestSuite($args) + { // Check test case identity // Check if user(devkey) has grants to do operation // $ret[] = array( - "operation" => __FUNCTION__, - "status" => true, - "message" => GENERAL_SUCCESS_STR + "operation" => __FUNCTION__, + "status" => true, + "message" => GENERAL_SUCCESS_STR ); $operation = $ret['operation']; $msgPrefix = "({$operation}) - "; - $debug_info = null; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate', - 'checkTestCaseIdentity', - 'checkTestSuiteID' + 'authenticate', + 'checkTestCaseIdentity', + 'checkTestSuiteID' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ); - if($status_ok) { + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix); + if ($status_ok) { // Test Case & Test Suite belongs to same Test Project? - $tcaseTProj = $this->args[self::$testProjectIDParamName] = intval( $this->tcaseMgr->getTestProjectFromTestCase( $this->args[self::$testCaseIDParamName], null ) ); + $tcaseTProj = $this->args[self::$testProjectIDParamName] = intval( + $this->tcaseMgr->getTestProjectFromTestCase( + $this->args[self::$testCaseIDParamName], null)); - $tsuiteMgr = new testsuite( $this->dbObj ); + $tsuiteMgr = new testsuite($this->dbObj); $tsuite_id = $this->args[self::$testSuiteIDParamName]; - $tsuiteTProj = intval( $tsuiteMgr->getTestProjectFromTestSuite( $tsuite_id, null ) ); + $tsuiteTProj = intval( + $tsuiteMgr->getTestProjectFromTestSuite($tsuite_id, null)); - $status_ok =($tcaseTProj == $tsuiteTProj); - if(! $status_ok) { + $status_ok = ($tcaseTProj == $tsuiteTProj); + if (! $status_ok) { $msg = $msgPrefix . TSUITE_NOT_ON_TCASE_TPROJ_STR; - $this->errors[] = new IXR_Error( TSUITE_NOT_ON_TCASE_TPROJ, $msg ); + $this->errors[] = new IXR_Error(TSUITE_NOT_ON_TCASE_TPROJ, $msg); } } - if($status_ok) { + if ($status_ok) { $ctx[self::$testProjectIDParamName] = $tcaseTProj; $ck = self::CHECK_PUBLIC_PRIVATE_ATTR; $r2c = array( - 'mgt_modify_tc' + 'mgt_modify_tc' ); - foreach( $r2c as $right ) { - $status_ok = $this->userHasRight( $right, $ck, $ctx ); - if(! $status_ok) { + foreach ($r2c as $right) { + $status_ok = $this->userHasRight($right, $ck, $ctx); + if (! $status_ok) { break; } } } - if($status_ok) { + if ($status_ok) { - $sql = "/* " . __FUNCTION__ . " */" . " UPDATE " . $this->tables['nodes_hierarchy'] . " SET parent_id=" . $tsuite_id . " WHERE id=" . $this->args['testcaseid']; - $this->dbObj->exec_query( $sql ); + $sql = "/* " . __FUNCTION__ . " */" . " UPDATE " . + $this->tables['nodes_hierarchy'] . " SET parent_id=" . $tsuite_id . + " WHERE id=" . $this->args['testcaseid']; + $this->dbObj->exec_query($sql); } return $status_ok ? $ret : $this->errors; @@ -8000,11 +9587,12 @@ public function setTestCaseTestSuite($args) { * array('id' => -1) * @access public */ - public function getExecutionSet($args) { + public function getExecutionSet($args) + { $operation = __FUNCTION__; $msg_prefix = "({$operation}) - "; - $this->_setArgs( $args ); + $this->_setArgs($args); $resultInfo = array(); $status_ok = true; @@ -8013,71 +9601,79 @@ public function getExecutionSet($args) { // Checks are done in order $checkFunctions = array( - 'authenticate', - 'checkTestPlanID', - 'checkTestCaseIdentity' + 'authenticate', + 'checkTestPlanID', + 'checkTestCaseIdentity' ); - $status_ok = $this->_runChecks( $checkFunctions, $msg_prefix ) && $this->_checkTCIDAndTPIDValid( null, $msg_prefix ) && $this->userHasRight( "mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR ); + $status_ok = $this->_runChecks($checkFunctions, $msg_prefix) && + $this->_checkTCIDAndTPIDValid(null, $msg_prefix) && + $this->userHasRight("mgt_view_tc", self::CHECK_PUBLIC_PRIVATE_ATTR); $tplan_id = $this->args[self::$testPlanIDParamName]; $tcase_id = $this->args[self::$testCaseIDParamName]; $execContext = array( - 'tplan_id' => $tplan_id, - 'platform_id' => null, - 'build_id' => null + 'tplan_id' => $tplan_id, + 'platform_id' => null, + 'build_id' => null ); - if($status_ok) { - if($this->_isParamPresent( self::$optionsParamName, $msg_prefix )) { + if ($status_ok) { + if ($this->_isParamPresent(self::$optionsParamName, $msg_prefix)) { $dummy = $this->args[self::$optionsParamName]; - if(is_array( $dummy )) { - foreach( $dummy as $key => $value ) { - $opt->$key =($value > 0) ? 1 : 0; + if (is_array($dummy)) { + foreach ($dummy as $key => $value) { + $opt->$key = ($value > 0) ? 1 : 0; } } } // Now we can check for Optional parameters - if($this->_isBuildIDPresent() || $this->_isBuildNamePresent()) { - if(($status_ok = $this->checkBuildID( $msg_prefix ))) { - $execContext['build_id'] = $this->args[self::$buildIDParamName]; - } + if ($this->_isBuildIDPresent() || + $this->_isBuildNamePresent() && + $status_ok = $this->checkBuildID($msg_prefix)) { + $execContext['build_id'] = $this->args[self::$buildIDParamName]; } - if($status_ok) { - if($this->_isParamPresent( self::$platformIDParamName, $msg_prefix ) || $this->_isParamPresent( self::$platformNameParamName, $msg_prefix )) { - $status_ok = $this->checkPlatformIdentity( $tplan_id ); - if($status_ok) { - $execContext['platform_id'] = $this->args[self::$platformIDParamName]; - } + if ($status_ok && + $this->_isParamPresent(self::$platformIDParamName, $msg_prefix) || + $this->_isParamPresent(self::$platformNameParamName, $msg_prefix)) { + $status_ok = $this->checkPlatformIdentity($tplan_id); + if ($status_ok) { + $execContext['platform_id'] = $this->args[self::$platformIDParamName]; } } } - if($status_ok) { - $sql = " SELECT * FROM {$this->tables['executions']} WHERE id " . " IN(SELECT id AS exec_id FROM {$this->tables['executions']} " . " WHERE testplan_id = {$tplan_id} " . " AND tcversion_id " . " IN( SELECT id FROM {$this->tables['nodes_hierarchy']} " . " WHERE parent_id = {$tcase_id} )"; + if ($status_ok) { + $sql = " SELECT * FROM {$this->tables['executions']} WHERE id " . + " IN(SELECT id AS exec_id FROM {$this->tables['executions']} " . + " WHERE testplan_id = {$tplan_id} " . + " AND tcversion_id " . + " IN( SELECT id FROM {$this->tables['nodes_hierarchy']} " . + " WHERE parent_id = {$tcase_id} )"; - if(! is_null( $execContext['build_id'] )) { - $sql .= " AND build_id = " . intval( $execContext['build_id'] ); + if (! is_null($execContext['build_id'])) { + $sql .= " AND build_id = " . intval($execContext['build_id']); } - if(! is_null( $execContext['platform_id'] )) { - $sql .= " AND platform_id = " . intval( $execContext['platform_id'] ); + if (! is_null($execContext['platform_id'])) { + $sql .= " AND platform_id = " . + intval($execContext['platform_id']); } // closing bracket for 1st level SELECT $sql .= ")"; $sql .= " ORDER BY id "; - $sql .=($opt->getOrderDescending) ? " DESC" : " ASC"; + $sql .= ($opt->getOrderDescending) ? " DESC" : " ASC"; - $rs = $this->dbObj->fetchRowsIntoMap( $sql, 'id' ); - if(is_null( $rs )) { + $rs = $this->dbObj->fetchRowsIntoMap($sql, 'id'); + if (is_null($rs)) { // has not been executed // execution id = -1 => test case has not been runned. $resultInfo[] = array( - 'id' => - 1 + 'id' => - 1 ); } else { $resultInfo = $rs; @@ -8098,7 +9694,8 @@ public function getExecutionSet($args) { * * @access public */ - public function closeBuild($args) { + public function closeBuild($args) + { $operation = __FUNCTION__; $messagePrefix = "({$operation}) - "; @@ -8109,52 +9706,54 @@ public function closeBuild($args) { $resultInfo[0]["operation"] = $operation; $resultInfo[0]["message"] = GENERAL_SUCCESS_STR; - $this->_setArgs( $args ); + $this->_setArgs($args); $checkFunctions = array( - 'authenticate' + 'authenticate' ); - $status_ok = $this->_runChecks( $checkFunctions, $messagePrefix ); + $status_ok = $this->_runChecks($checkFunctions, $messagePrefix); - if($status_ok) { - $status_ok = $this->_isParamPresent( self::$buildIDParamName, $messagePrefix, self::SET_ERROR ); + if ($status_ok) { + $status_ok = $this->_isParamPresent(self::$buildIDParamName, + $messagePrefix, self::SET_ERROR); } - if($status_ok) { + if ($status_ok) { $buildID = $this->args[self::$buildIDParamName]; - if(!($status_ok = is_int( $buildID ))) { - $msg = sprintf( BUILDID_NOT_INTEGER_STR, $buildID ); - $this->errors[] = new IXR_Error( BUILDID_NOT_INTEGER, $msg ); + if (! ($status_ok = is_int($buildID))) { + $msg = sprintf(BUILDID_NOT_INTEGER_STR, $buildID); + $this->errors[] = new IXR_Error(BUILDID_NOT_INTEGER, $msg); } } - if($status_ok) { + if ($status_ok) { // Get Test Plan ID from Build ID in order to check rights - $bm = new build_mgr( $this->dbObj ); + $bm = new build_mgr($this->dbObj); - $buildID = intval( $this->args[self::$buildIDParamName] ); + $buildID = intval($this->args[self::$buildIDParamName]); $opx = array( - 'output' => 'fields', - 'fields' => 'id,testplan_id' + 'output' => 'fields', + 'fields' => 'id,testplan_id' ); - $buildInfo = $bm->get_by_id( $buildID, $opx ); + $buildInfo = $bm->get_by_id($buildID, $opx); - if($buildInfo == false || count( $buildInfo ) == 0) { + if (! $buildInfo || empty($buildInfo)) { $status_ok = false; - $msg = sprintf( INVALID_BUILDID_STR, $buildID ); - $this->errors[] = new IXR_Error( INVALID_BUILDID, $msg ); + $msg = sprintf(INVALID_BUILDID_STR, $buildID); + $this->errors[] = new IXR_Error(INVALID_BUILDID, $msg); } } - if($status_ok) { + if ($status_ok) { $context = array(); $context[self::$testPlanIDParamName] = $buildInfo['testplan_id']; - $status_ok = $this->userHasRight( "testplan_create_build", self::CHECK_PUBLIC_PRIVATE_ATTR, $context ); + $status_ok = $this->userHasRight("testplan_create_build", + self::CHECK_PUBLIC_PRIVATE_ATTR, $context); } - if($status_ok) { - $bm->setClosed( $buildID ); + if ($status_ok) { + $bm->setClosed($buildID); $resultInfo[0]["id"] = $buildID; } @@ -8169,104 +9768,108 @@ public function closeBuild($args) { * * @access protected */ - protected function _updateStepsResult($execID = null) { + protected function _updateStepsResult($execID = null) + { $tcversion_id = $this->tcVersionID; $tcase_id = $this->args[self::$testCaseIDParamName]; - $stepExecStatus = $this->args[self::$stepsParamName]; - $exec_id = $execID; - if(is_null( $exec_id )) { + if (is_null($exec_id)) { $execContext = array( - 'tplan_id' => $this->args[self::$testPlanIDParamName], - 'platform_id' => $this->args[self::$platformIDParamName], - 'build_id' => $this->args[self::$buildIDParamName] + 'tplan_id' => $this->args[self::$testPlanIDParamName], + 'platform_id' => $this->args[self::$platformIDParamName], + 'build_id' => $this->args[self::$buildIDParamName] ); $opt = array( - 'output' => 'exec_id' + 'output' => 'exec_id' ); $identity = array( - 'id' => $tcase_id, - 'version_id' => $tcversion_id + 'id' => $tcase_id, + 'version_id' => $tcversion_id ); - $exec_id = $this->tcaseMgr->getLatestExecSingleContext( $identity, $execContext, $opt ); + $exec_id = $this->tcaseMgr->getLatestExecSingleContext($identity, + $execContext, $opt); } - if(! is_null( $exec_id )) { - $exec_id = intval( $exec_id ); - $execution_type = constant( "TESTCASE_EXECUTION_TYPE_AUTO" ); + if (! is_null($exec_id)) { + $exec_id = intval($exec_id); $st = &$this->args[self::$stepsParamName]; // needed to get tcstep_id from step number $r2d2 = array( - 'fields2get' => 'TCSTEPS.step_number,TCSTEPS.id', - 'accessKey' => 'step_number', - 'renderGhostSteps' => false, - 'renderImageInline' => false + 'fields2get' => 'TCSTEPS.step_number,TCSTEPS.id', + 'accessKey' => 'step_number', + 'renderGhostSteps' => false, + 'renderImageInline' => false ); - $steps = $this->tcaseMgr->getStepsSimple( $this->tcVersionID, 0, $r2d2 ); + $steps = $this->tcaseMgr->getStepsSimple($this->tcVersionID, 0, + $r2d2); $xx = null; - foreach( $st as $spdata ) { + foreach ($st as $spdata) { $dbField = null; $dbVal = null; - foreach( $spdata as $keyF => $value ) { - switch($keyF) { - case 'result' : - $status = strtolower( trim( $spdata['result'] ) ); + foreach ($spdata as $keyF => $value) { + switch ($keyF) { + case 'result': + $status = strtolower(trim($spdata['result'])); $dbField[] = 'status'; $dbVal[] = "'" . $status[0] . "'"; break; - case 'notes' : + case 'notes': $dbField[] = $keyF; - $dbVal[] = "'" . $this->dbObj->prepare_string( $spdata[$keyF] ) . "'"; + $dbVal[] = "'" . + $this->dbObj->prepare_string($spdata[$keyF]) . + "'"; break; } } $spnum = $spdata['step_number']; - if(! is_null( $dbField ) && isset( $steps[$spnum] )) { + if (! is_null($dbField) && isset($steps[$spnum])) { $target = DB_TABLE_PREFIX . 'execution_tcsteps'; - $where = " WHERE execution_id=" . $exec_id . " AND " . " tcstep_id=" . intval( $steps[$spnum]['id'] ); + $where = " WHERE execution_id=" . $exec_id . " AND " . + " tcstep_id=" . intval($steps[$spnum]['id']); // Manage Insert $sql = "SELECT id FROM $target $where"; - $rs = $this->dbObj->get_recordset( $sql ); + $rs = $this->dbObj->get_recordset($sql); - if(is_null( $rs ) or count( $rs ) != 1) { + if (is_null($rs) || count($rs) != 1) { $sql = " INSERT INTO $target("; $dbField[] = 'tcstep_id'; - $dbVal[] = intval( $steps[$spnum]['id'] ); + $dbVal[] = intval($steps[$spnum]['id']); $dbField[] = 'execution_id'; $dbVal[] = $exec_id; - foreach( $dbField as $idx => $kf ) { - if($idx > 0) { + foreach ($dbField as $idx => $kf) { + if ($idx > 0) { $sql .= ','; } $sql .= $kf; } $sql .= ') VALUES( '; - foreach( $dbVal as $idx => $kv ) { - if($idx > 0) { + foreach ($dbVal as $idx => $kv) { + if ($idx > 0) { $sql .= ','; } $sql .= $kv; } $sql .= ") "; } else { - $sql = " UPDATE " . DB_TABLE_PREFIX . 'execution_tcsteps' . " SET "; - foreach( $dbField as $idx => $kf ) { - if($idx > 0) { + $sql = " UPDATE " . DB_TABLE_PREFIX . 'execution_tcsteps' . + " SET "; + foreach ($dbField as $idx => $kf) { + if ($idx > 0) { $sql .= ','; } $sql .= $kf . '=' . $dbVal[$idx]; @@ -8276,8 +9879,8 @@ protected function _updateStepsResult($execID = null) { try { $xx[] = $sql; - $this->dbObj->exec_query( $sql ); - } catch( Exception $e ) { + $this->dbObj->exec_query($sql); + } catch (Exception $e) { return $e; } } @@ -8288,93 +9891,98 @@ protected function _updateStepsResult($execID = null) { /** */ - function initMethodYellowPages() { + private function initMethodYellowPages() + { $this->methods = array( - 'tl.reportTCResult' => 'this:reportTCResult', - 'tl.setTestCaseExecutionResult' => 'this:reportTCResult', - 'tl.createBuild' => 'this:createBuild', - 'tl.closeBuild' => 'this:closeBuild', - 'tl.createPlatform' => 'this:createPlatform', - 'tl.createTestCase' => 'this:createTestCase', - 'tl.createTestCaseSteps' => 'this:createTestCaseSteps', - 'tl.createTestPlan' => 'this:createTestPlan', - 'tl.createTestProject' => 'this:createTestProject', - 'tl.createTestSuite' => 'this:createTestSuite', - 'tl.deleteTestCaseSteps' => 'this:deleteTestCaseSteps', - 'tl.deleteTestPlan' => 'this:deleteTestPlan', - 'tl.deleteTestProject' => 'this:deleteTestProject', - 'tl.uploadExecutionAttachment' => 'this:uploadExecutionAttachment', - 'tl.uploadRequirementSpecificationAttachment' => 'this:uploadRequirementSpecificationAttachment', - 'tl.uploadRequirementAttachment' => 'this:uploadRequirementAttachment', - 'tl.uploadTestProjectAttachment' => 'this:uploadTestProjectAttachment', - 'tl.uploadTestSuiteAttachment' => 'this:uploadTestSuiteAttachment', - 'tl.uploadTestCaseAttachment' => 'this:uploadTestCaseAttachment', - 'tl.uploadAttachment' => 'this:uploadAttachment', - 'tl.assignRequirements' => 'this:assignRequirements', - 'tl.addTestCaseToTestPlan' => 'this:addTestCaseToTestPlan', - 'tl.addPlatformToTestPlan' => 'this:addPlatformToTestPlan', - 'tl.removePlatformFromTestPlan' => 'this:removePlatformFromTestPlan', - 'tl.getExecCountersByBuild' => 'this:getExecCountersByBuild', - 'tl.getIssueTrackerSystem' => 'this:getIssueTrackerSystem', - 'tl.getProjects' => 'this:getProjects', - 'tl.getProjectKeywords' => 'this:getProjectKeywords', - 'tl.getProjectPlatforms' => 'this:getProjectPlatforms', - 'tl.getProjectTestPlans' => 'this:getProjectTestPlans', - 'tl.getTestCaseAssignedTester' => 'this:getTestCaseAssignedTester', - 'tl.getTestCaseBugs' => 'this:getTestCaseBugs', - 'tl.getTestCaseKeywords' => 'this:getTestCaseKeywords', - 'tl.getTestProjectByName' => 'this:getTestProjectByName', - 'tl.getTestPlanByName' => 'this:getTestPlanByName', - 'tl.getTestPlanPlatforms' => 'this:getTestPlanPlatforms', - 'tl.getTotalsForTestPlan' => 'this:getTotalsForTestPlan', - 'tl.getBuildsForTestPlan' => 'this:getBuildsForTestPlan', - 'tl.getLatestBuildForTestPlan' => 'this:getLatestBuildForTestPlan', - 'tl.getLastExecutionResult' => 'this:getLastExecutionResult', - 'tl.getTestSuitesForTestPlan' => 'this:getTestSuitesForTestPlan', - 'tl.getTestSuitesForTestSuite' => 'this:getTestSuitesForTestSuite', - 'tl.getTestCasesForTestSuite' => 'this:getTestCasesForTestSuite', - 'tl.getTestCasesForTestPlan' => 'this:getTestCasesForTestPlan', - 'tl.getTestCaseIDByName' => 'this:getTestCaseIDByName', - 'tl.getTestCaseCustomFieldDesignValue' => 'this:getTestCaseCustomFieldDesignValue', - 'tl.getTestCaseCustomFieldExecutionValue' => 'this:getTestCaseCustomFieldExecutionValue', - 'tl.getTestCaseCustomFieldTestPlanDesignValue' => 'this:getTestCaseCustomFieldTestPlanDesignValue', - 'tl.getTestSuiteCustomFieldDesignValue' => 'this:getTestSuiteCustomFieldDesignValue', - 'tl.getTestPlanCustomFieldDesignValue' => 'this:getTestPlanCustomFieldDesignValue', - 'tl.getReqSpecCustomFieldDesignValue' => 'this:getReqSpecCustomFieldDesignValue', - 'tl.getRequirementCustomFieldDesignValue' => 'this:getRequirementCustomFieldDesignValue', - 'tl.getFirstLevelTestSuitesForTestProject' => 'this:getFirstLevelTestSuitesForTestProject', - 'tl.getTestCaseAttachments' => 'this:getTestCaseAttachments', - 'tl.getTestSuiteAttachments' => 'this:getTestSuiteAttachments', - 'tl.getTestCase' => 'this:getTestCase', - 'tl.getFullPath' => 'this:getFullPath', - 'tl.getTestSuiteByID' => 'this:getTestSuiteByID', - 'tl.getUserByLogin' => 'this:getUserByLogin', - 'tl.getUserByID' => 'this:getUserByID', - 'tl.deleteExecution' => 'this:deleteExecution', - 'tl.doesUserExist' => 'this:doesUserExist', - 'tl.updateTestCaseCustomFieldDesignValue' => 'this:updateTestCaseCustomFieldDesignValue', - 'tl.updateTestCase' => 'this:updateTestCase', - 'tl.setTestCaseExecutionType' => 'this:setTestCaseExecutionType', - 'tl.assignTestCaseExecutionTask' => 'this:assignTestCaseExecutionTask', - 'tl.unassignTestCaseExecutionTask' => 'this:unassignTestCaseExecutionTask', - 'tl.addTestCaseKeywords' => 'this:addTestCaseKeywords', - 'tl.removeTestCaseKeywords' => 'this:removeTestCaseKeywords', - 'tl.updateTestSuiteCustomFieldDesignValue' => 'this:updateTestSuiteCustomFieldDesignValue', - 'tl.updateBuildCustomFieldsValues' => 'this:updateBuildCustomFieldsValues', - 'tl.getTestSuite' => 'this:getTestSuite', - 'tl.updateTestSuite' => 'this:updateTestSuite', - 'tl.getRequirements' => 'this:getRequirements', - 'tl.getReqCoverage' => 'this:getReqCoverage', - 'tl.setTestCaseTestSuite' => 'this:setTestCaseTestSuite', - 'tl.getExecutionSet' => 'this:getExecutionSet', - 'tl.checkDevKey' => 'this:checkDevKey', - 'tl.about' => 'this:about', - 'tl.testLinkVersion' => 'this:testLinkVersion', - 'tl.setTestMode' => 'this:setTestMode', - 'tl.ping' => 'this:sayHello', - 'tl.sayHello' => 'this:sayHello', - 'tl.repeat' => 'this:repeat', - 'tl.getAllExecutionsResults' => 'this:getAllExecutionsResults' + 'tl.reportTCResult' => 'this:reportTCResult', + 'tl.setTestCaseExecutionResult' => 'this:reportTCResult', + 'tl.createBuild' => 'this:createBuild', + 'tl.closeBuild' => 'this:closeBuild', + 'tl.createPlatform' => 'this:createPlatform', + 'tl.createTestCase' => 'this:createTestCase', + 'tl.createTestCaseSteps' => 'this:createTestCaseSteps', + 'tl.createTestPlan' => 'this:createTestPlan', + 'tl.createTestProject' => 'this:createTestProject', + 'tl.createTestSuite' => 'this:createTestSuite', + 'tl.deleteTestCaseSteps' => 'this:deleteTestCaseSteps', + 'tl.deleteTestPlan' => 'this:deleteTestPlan', + 'tl.deleteTestProject' => 'this:deleteTestProject', + 'tl.uploadExecutionAttachment' => 'this:uploadExecutionAttachment', + 'tl.uploadRequirementSpecificationAttachment' => 'this:uploadRequirementSpecificationAttachment', + 'tl.uploadRequirementAttachment' => 'this:uploadRequirementAttachment', + 'tl.uploadTestProjectAttachment' => 'this:uploadTestProjectAttachment', + 'tl.uploadTestSuiteAttachment' => 'this:uploadTestSuiteAttachment', + 'tl.uploadTestCaseAttachment' => 'this:uploadTestCaseAttachment', + 'tl.uploadAttachment' => 'this:uploadAttachment', + 'tl.assignRequirements' => 'this:assignRequirements', + 'tl.addTestCaseToTestPlan' => 'this:addTestCaseToTestPlan', + 'tl.addPlatformToTestPlan' => 'this:addPlatformToTestPlan', + 'tl.removePlatformFromTestPlan' => 'this:removePlatformFromTestPlan', + 'tl.getExecCountersByBuild' => 'this:getExecCountersByBuild', + 'tl.getIssueTrackerSystem' => 'this:getIssueTrackerSystem', + 'tl.getProjects' => 'this:getProjects', + 'tl.getProjectKeywords' => 'this:getProjectKeywords', + 'tl.getProjectPlatforms' => 'this:getProjectPlatforms', + 'tl.getProjectTestPlans' => 'this:getProjectTestPlans', + 'tl.getTestCaseAssignedTester' => 'this:getTestCaseAssignedTester', + 'tl.getTestCaseBugs' => 'this:getTestCaseBugs', + 'tl.getTestCaseKeywords' => 'this:getTestCaseKeywords', + 'tl.getTestCaseRequirements' => 'this:getTestCaseRequirements', + 'tl.getTestProjectByName' => 'this:getTestProjectByName', + 'tl.getTestPlanByName' => 'this:getTestPlanByName', + 'tl.getTestPlanPlatforms' => 'this:getTestPlanPlatforms', + 'tl.getTotalsForTestPlan' => 'this:getTotalsForTestPlan', + 'tl.getBuildsForTestPlan' => 'this:getBuildsForTestPlan', + 'tl.getLatestBuildForTestPlan' => 'this:getLatestBuildForTestPlan', + 'tl.getLastExecutionResult' => 'this:getLastExecutionResult', + 'tl.getTestSuitesForTestPlan' => 'this:getTestSuitesForTestPlan', + 'tl.getTestSuitesForTestSuite' => 'this:getTestSuitesForTestSuite', + 'tl.getTestCasesForTestSuite' => 'this:getTestCasesForTestSuite', + 'tl.getTestCasesForTestPlan' => 'this:getTestCasesForTestPlan', + 'tl.getTestCaseIDByName' => 'this:getTestCaseIDByName', + 'tl.getTestCaseCustomFieldDesignValue' => 'this:getTestCaseCustomFieldDesignValue', + 'tl.getTestCaseCustomFieldExecutionValue' => 'this:getTestCaseCustomFieldExecutionValue', + 'tl.getTestCaseCustomFieldTestPlanDesignValue' => 'this:getTestCaseCustomFieldTestPlanDesignValue', + 'tl.getTestSuiteCustomFieldDesignValue' => 'this:getTestSuiteCustomFieldDesignValue', + 'tl.getTestPlanCustomFieldDesignValue' => 'this:getTestPlanCustomFieldDesignValue', + 'tl.getReqSpecCustomFieldDesignValue' => 'this:getReqSpecCustomFieldDesignValue', + 'tl.getRequirementCustomFieldDesignValue' => 'this:getRequirementCustomFieldDesignValue', + 'tl.getFirstLevelTestSuitesForTestProject' => 'this:getFirstLevelTestSuitesForTestProject', + 'tl.getTestCaseAttachments' => 'this:getTestCaseAttachments', + 'tl.getTestSuiteAttachments' => 'this:getTestSuiteAttachments', + 'tl.getTestCase' => 'this:getTestCase', + 'tl.getFullPath' => 'this:getFullPath', + 'tl.getTestSuiteByID' => 'this:getTestSuiteByID', + 'tl.getUserByLogin' => 'this:getUserByLogin', + 'tl.getUserByID' => 'this:getUserByID', + 'tl.deleteExecution' => 'this:deleteExecution', + 'tl.doesUserExist' => 'this:doesUserExist', + 'tl.createUser' => 'this:createUser', + 'tl.setUserRoleOnProject' => 'this:setUserRoleOnProject', + 'tl.updateTestCaseCustomFieldDesignValue' => 'this:updateTestCaseCustomFieldDesignValue', + 'tl.updateTestCase' => 'this:updateTestCase', + 'tl.setTestCaseExecutionType' => 'this:setTestCaseExecutionType', + 'tl.assignTestCaseExecutionTask' => 'this:assignTestCaseExecutionTask', + 'tl.unassignTestCaseExecutionTask' => 'this:unassignTestCaseExecutionTask', + 'tl.addTestCaseKeywords' => 'this:addTestCaseKeywords', + 'tl.removeTestCaseKeywords' => 'this:removeTestCaseKeywords', + 'tl.updateTestSuiteCustomFieldDesignValue' => 'this:updateTestSuiteCustomFieldDesignValue', + 'tl.updateBuildCustomFieldsValues' => 'this:updateBuildCustomFieldsValues', + 'tl.getTestSuite' => 'this:getTestSuite', + 'tl.updateTestSuite' => 'this:updateTestSuite', + 'tl.getRequirements' => 'this:getRequirements', + 'tl.getRequirement' => 'this:getRequirement', + 'tl.getReqCoverage' => 'this:getReqCoverage', + 'tl.setTestCaseTestSuite' => 'this:setTestCaseTestSuite', + 'tl.getExecutionSet' => 'this:getExecutionSet', + 'tl.checkDevKey' => 'this:checkDevKey', + 'tl.about' => 'this:about', + 'tl.testLinkVersion' => 'this:testLinkVersion', + 'tl.setTestMode' => 'this:setTestMode', + 'tl.ping' => 'this:sayHello', + 'tl.sayHello' => 'this:sayHello', + 'tl.repeat' => 'this:repeat', + 'tl.getAllExecutionsResults' => 'this:getAllExecutionsResults' ); } -} // class end +} diff --git a/lib/api/xmlrpc/v1/xmlrpc.php b/lib/api/xmlrpc/v1/xmlrpc.php index 5fd5b2a011..7fef92f62a 100644 --- a/lib/api/xmlrpc/v1/xmlrpc.php +++ b/lib/api/xmlrpc/v1/xmlrpc.php @@ -1,17 +1,16 @@ -id) -{ - $attachmentRepository = tlAttachmentRepository::create($db); - $attachmentInfo = $attachmentRepository->getAttachmentInfo($args->id); - if ($attachmentInfo && checkAttachmentID($db,$args->id,$attachmentInfo)) - { - $deleteDone = $attachmentRepository->deleteAttachment($args->id,$attachmentInfo); - if ($deleteDone) - { - logAuditEvent(TLS("audit_attachment_deleted", - $attachmentInfo['title']),"DELETE",$args->id,"attachments"); - } - } -} - -$smarty = new TLSmarty(); -$smarty->assign('bDeleted',$deleteDone); -$smarty->display('attachmentdelete.tpl'); - - -/** - * @return object returns the arguments for the page - */ -function init_args() -{ - //the id (attachments.id) of the attachment to be deleted - $iParams = array( - "id" => array(tlInputParameter::INT_N), - ); - $args = new stdClass(); - G_PARAMS($iParams,$args); - - return $args; -} - - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) -{ - return (config_get("attachments")->enabled); -} -?> \ No newline at end of file +id) { + $attachmentRepository = tlAttachmentRepository::create($db); + $attachmentInfo = $attachmentRepository->getAttachmentInfo($args->id); + if ($attachmentInfo && checkAttachmentID($db, $args->id, $attachmentInfo)) { + $deleteDone = $attachmentRepository->deleteAttachment($args->id, + $attachmentInfo); + if ($deleteDone) { + logAuditEvent( + TLS("audit_attachment_deleted", $attachmentInfo['title']), + "DELETE", $args->id, "attachments"); + } + } +} + +$smarty = new TLSmarty(); +$smarty->assign('bDeleted', $deleteDone); +$smarty->display('attachmentdelete.tpl'); + +/** + * + * @return object returns the arguments for the page + */ +function initArgs() +{ + // the id (attachments.id) of the attachment to be deleted + $iParams = array( + "id" => array( + tlInputParameter::INT_N + ) + ); + $args = new stdClass(); + G_PARAMS($iParams, $args); + + return $args; +} + +/** + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return config_get("attachments")->enabled; +} +?> diff --git a/lib/attachments/attachmentdownload.php b/lib/attachments/attachmentdownload.php index 26f890906b..7b54f6bda3 100644 --- a/lib/attachments/attachmentdownload.php +++ b/lib/attachments/attachmentdownload.php @@ -1,166 +1,186 @@ -id) -{ - $attachmentRepository = tlAttachmentRepository::create($db); - $attachmentInfo = $attachmentRepository->getAttachmentInfo($args->id); - - if ($attachmentInfo) { - switch ($args->opmode) - { - case 'API': - // want to check if apikey provided is right for attachment context - // - test project api key: - // is needed to get attachments for: - // test specifications - // - // - test plan api key: - // is needed to get attacments for: - // test case executions - // test specifications ( access to parent data - OK!) - // - // What kind of attachments I've got ? - $doIt = false; - $attContext = $attachmentInfo['fk_table']; - switch($attContext) - { - case 'executions': - // check apikey - // 1. has to be a test plan key - // 2. execution must belong to the test plan. - $item = getEntityByAPIKey($db,$args->apikey,'testplan'); - if( !is_null($item) ) - { - $tables = tlObjectWithDB::getDBTables(array('executions')); - $sql = "SELECT testplan_id FROM {$tables['executions']} " . - "WHERE id = " . intval($attachmentInfo['fk_id']); - - $rs = $db->get_recordset($sql); - if(!is_null($rs)) - { - if($rs['0']['testplan_id'] == $item['id']) - { - // GOOD ! - $doIt = true; - } - } - } - break; - } - break; - - case 'GUI': - default: - $doIt = true; - break; - } - - - if( $doIt ) - { - $content = ''; - $getContent = true; - if( $args->opmode !== 'API' && $args->skipCheck !== 0 && $args->skipCheck !== false) - { - if( $args->skipCheck != hash('sha256',$attachmentInfo['file_name']) ) - { - $getContent = false; - } - } - - if($getContent) - { - $content = $attachmentRepository->getAttachmentContent($args->id,$attachmentInfo); - } - - if ($content != "" ) - { - @ob_end_clean(); - header('Pragma: public'); - header("Cache-Control: "); - if (!(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on" && preg_match("/MSIE/",$_SERVER["HTTP_USER_AGENT"]))) - { - header('Pragma: no-cache'); - } - header('Content-Type: '.$attachmentInfo['file_type']); - header('Content-Length: '.$attachmentInfo['file_size']); - header("Content-Disposition: inline; filename=\"{$attachmentInfo['file_name']}\""); - header("Content-Description: Download Data"); - global $g_repositoryType; - if($g_repositoryType == TL_REPOSITORY_TYPE_DB) - { - echo base64_decode($content); - } - else { - echo $content; - } - exit(); - } - } - } +id) { + $fileRepo = tlAttachmentRepository::create($db); + $attachInfo = $fileRepo->getAttachmentInfo($args->id); + + if ($attachInfo) { + switch ($args->opmode) { + case 'API': + // want to check if apikey provided is right + // for attachment context + // - test project api key: + // is needed to get attachments for: + // test specifications + // + // - test plan api key: + // is needed to get attacments for: + // test case executions + // test specifications ( access to parent data - OK!) + // + // What kind of attachments I've got ? + $doIt = false; + $attContext = $attachInfo['fk_table']; + switch ($attContext) { + case 'executions': + // check apikey + // 1. has to be a test plan key + // 2. execution must belong to the test plan. + $item = getEntityByAPIKey($db, $args->apikey, 'testplan'); + if (! is_null($item)) { + $tables = tlObjectWithDB::getDBTables( + array( + 'executions' + )); + $sql = "SELECT testplan_id FROM {$tables['executions']} " . + "WHERE id = " . intval($attachInfo['fk_id']); + + $rs = $db->get_recordset($sql); + if (! is_null($rs) && + $rs['0']['testplan_id'] == $item['id']) { + $doIt = true; + } + } + break; + } + break; + + case 'GUI': + default: + $doIt = true; + break; + } + + if ($doIt) { + $content = ''; + $getContent = true; + if ($args->opmode !== 'API' && $args->skipCheck !== 0 && + $args->skipCheck !== false && + $args->skipCheck != hash('sha256', $attachInfo['file_name'])) { + $getContent = false; + } + + if ($getContent) { + $content = $fileRepo->getAttachmentContent($args->id, + $attachInfo); + } + + if ($content != "") { + + // try to fight XSS in SVG + global $g_repositoryType; + $doEncode = ($g_repositoryType == TL_REPOSITORY_TYPE_DB); + if ($doEncode) { + $content = base64_decode($content); + } + + $what2do = "Content-Disposition: inline;"; + // is SVG? + if (strripos($content, "assign('gui', $args); +$smarty->display('attachment404.tpl'); + +/** + * + * @return object returns the arguments for the page + */ +function initArgs(&$dbHandler) +{ + // id (attachments.id) of the attachment to be downloaded + $iParams = array( + 'id' => array( + tlInputParameter::INT_N + ), + 'apikey' => array( + tlInputParameter::STRING_N, + 64 + ), + 'skipCheck' => array( + tlInputParameter::STRING_N, + 1, + 64 + ) + ); + + $args = new stdClass(); + G_PARAMS($iParams, $args); + + $args->light = 'green'; + $args->opmode = 'GUI'; + if (is_null($args->skipCheck) || $args->skipCheck === 0) { + $args->skipCheck = false; + } + + // var_dump($args->skipCheck);die(); + // using apikey lenght to understand apikey type + // 32 => user api key + // other => test project or test plan + $args->apikey = trim($args->apikey); + $apikeyLenght = strlen($args->apikey); + if ($apikeyLenght > 0) { + $args->opmode = 'API'; + $args->skipCheck = true; + } + return $args; +} + +/** + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return config_get("attachments")->enabled; } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$args); -$smarty->display('attachment404.tpl'); - -/** - * @return object returns the arguments for the page - */ -function init_args(&$dbHandler) -{ - // id (attachments.id) of the attachment to be downloaded - $iParams = array('id' => array(tlInputParameter::INT_N), - 'apikey' => array(tlInputParameter::STRING_N,64), - 'skipCheck' => array(tlInputParameter::STRING_N,1,64)); - - $args = new stdClass(); - G_PARAMS($iParams,$args); - - $args->light = 'green'; - $args->opmode = 'GUI'; - if( is_null($args->skipCheck) || $args->skipCheck === 0 ) - { - $args->skipCheck = false; - } - - // var_dump($args->skipCheck);die(); - // using apikey lenght to understand apikey type - // 32 => user api key - // other => test project or test plan - $args->apikey = trim($args->apikey); - $apikeyLenght = strlen($args->apikey); - if($apikeyLenght > 0) - { - $args->opmode = 'API'; - $args->skipCheck = true; - } - return $args; -} - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) -{ - return (config_get("attachments")->enabled); -} \ No newline at end of file diff --git a/lib/attachments/attachmentupload.php b/lib/attachments/attachmentupload.php index 3916ed04f0..21bf6a76cf 100644 --- a/lib/attachments/attachmentupload.php +++ b/lib/attachments/attachmentupload.php @@ -1,105 +1,118 @@ -uploaded = false; -$gui->msg = null; -$gui->tableName = $args->tableName; -$gui->import_limit = TL_REPOSITORY_MAXFILESIZE; -$gui->id = $args->id; - -if ($args->bPostBack) { - $fInfo = isset($_FILES['uploadedFile']) ? $_FILES['uploadedFile'] : null; - $id = $_SESSION['s_upload_id']; - $gui->tableName = $_SESSION['s_upload_tableName']; - - if ($fInfo && $id && $gui->tableName != "") { - $opt = null; - if (trim($gui->tableName) == 'executions') { - $opt['allow_empty_title'] = true; - } - - $l2d = count($fInfo); - for($fdx=0; $fdx <= $l2d; $fdx++) { - $fSize = isset($fInfo['size'][$fdx]) ? $fInfo['size'][$fdx] : 0; - $fTmpName = isset($fInfo['tmp_name'][$fdx]) ? - $fInfo['tmp_name'][$fdx] : ''; - - $fin = array(); - $fin['size'] = $fSize; - $fin['tmp_name'] = $fTmpName; - $fin['type'] = $fInfo['type'][$fdx]; - $fin['name'] = $fInfo['name'][$fdx]; - $fin['error'] = $fInfo['error'][$fdx]; - - if ($fSize && $fTmpName != "") { - $docRepo = tlAttachmentRepository::create($db); - - $uploadOP = $docRepo->insertAttachment($id,$gui->tableName,$args->title,$fin,$opt); - $gui->uploaded = $uploadOP->statusOK; - if ($gui->uploaded) { - logAuditEvent(TLS("audit_attachment_created", - $args->title,$fin['name']), - "CREATE",$id,"attachments"); - } - } else { - $gui->msg = getFileUploadErrorMessage($fin,$uploadOP); - } - } - } +uploaded = false; +$gui->msg = null; +$gui->tableName = $args->tableName; +$gui->import_limit = TL_REPOSITORY_MAXFILESIZE; +$gui->id = $args->id; + +if ($args->bPostBack) { + $fInfo = isset($_FILES['uploadedFile']) ? $_FILES['uploadedFile'] : null; + $id = $_SESSION['s_upload_id']; + $gui->tableName = $_SESSION['s_upload_tableName']; + + if ($fInfo && $id && $gui->tableName != "") { + $opt = null; + if (trim($gui->tableName) == 'executions') { + $opt['allow_empty_title'] = true; + } + + $l2d = count($fInfo); + for ($fdx = 0; $fdx <= $l2d; $fdx ++) { + $fSize = isset($fInfo['size'][$fdx]) ? $fInfo['size'][$fdx] : 0; + $fTmpName = isset($fInfo['tmp_name'][$fdx]) ? $fInfo['tmp_name'][$fdx] : ''; + + $fin = array(); + $fin['size'] = $fSize; + $fin['tmp_name'] = $fTmpName; + $fin['type'] = $fInfo['type'][$fdx]; + $fin['name'] = $fInfo['name'][$fdx]; + $fin['error'] = $fInfo['error'][$fdx]; + + if ($fSize && $fTmpName != "") { + $docRepo = tlAttachmentRepository::create($db); + + $uploadOP = $docRepo->insertAttachment($id, $gui->tableName, + $args->title, $fin, $opt); + $gui->uploaded = $uploadOP->statusOK; + if ($gui->uploaded) { + logAuditEvent( + TLS("audit_attachment_created", $args->title, + $fin['name']), "CREATE", $id, "attachments"); + } + } else { + $gui->msg = getFileUploadErrorMessage($fin, $uploadOP); + } + } + } +} else { + $_SESSION['s_upload_tableName'] = $args->tableName; + $_SESSION['s_upload_id'] = $args->id; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display('attachmentupload.tpl'); + +/** + * + * @return object returns the arguments for the page + */ +function initArgs() +{ + $iParams = array( + // the id (attachments.fk_id) of the object, to which the attachment belongs to + "id" => array( + "GET", + tlInputParameter::INT_N + ), + // the table to which the fk_id refers to (attachments.fk_table) of the attachment + "tableName" => array( + "GET", + tlInputParameter::STRING_N, + 0, + 250 + ), + // the title of the attachment (attachments.title) + "title" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 250 + ) + ); + $args = new stdClass(); + I_PARAMS($iParams, $args); + + $args->bPostBack = count($_POST); + + return $args; +} + +/** + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return config_get("attachments")->enabled; } -else -{ - $_SESSION['s_upload_tableName'] = $args->tableName; - $_SESSION['s_upload_id'] = $args->id; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display('attachmentupload.tpl'); - -/** - * @return object returns the arguments for the page - */ -function init_args() -{ - $iParams = array( - //the id (attachments.fk_id) of the object, to which the attachment belongs to - "id" => array("GET",tlInputParameter::INT_N), - //the table to which the fk_id refers to (attachments.fk_table) of the attachment - "tableName" => array("GET",tlInputParameter::STRING_N,0,250), - //the title of the attachment (attachments.title) - "title" => array("POST",tlInputParameter::STRING_N,0,250), - ); - $args = new stdClass(); - I_PARAMS($iParams,$args); - - $args->bPostBack = sizeof($_POST); - - return $args; -} - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) -{ - return (config_get("attachments")->enabled); -} -?> \ No newline at end of file diff --git a/lib/cfields/cfieldsEdit.php b/lib/cfields/cfieldsEdit.php index 033f3c6cb5..068de570d6 100644 --- a/lib/cfields/cfieldsEdit.php +++ b/lib/cfields/cfieldsEdit.php @@ -1,493 +1,482 @@ -cfieldCfg; -$gui->cfield = $cfMix->emptyCF; - -switch ($args->do_action) { - case 'create': - $templateCfg->template=$templateCfg->default_template; - $user_feedback =''; - $operation_descr = ''; - break; - - case 'edit': - $op = edit($args,$cfield_mgr); - $gui->cfield = $op->cf; - $gui->cfield_is_used = $op->cf_is_used; - $gui->cfield_is_linked = $op->cf_is_linked; - $gui->linked_tprojects = $op->linked_tprojects; - $user_feedback = $op->user_feedback; - $operation_descr=$op->operation_descr; - break; - - case 'do_add': - case 'do_add_and_assign': - $op = doCreate($_REQUEST,$cfield_mgr,$args); - $gui->cfield = $op->cf; - $user_feedback = $op->user_feedback; - $templateCfg->template = $op->template; - $operation_descr = ''; - break; - - case 'do_update': - $op = doUpdate($_REQUEST,$args,$cfield_mgr); - $gui->cfield = $op->cf; - $user_feedback = $op->user_feedback; - $operation_descr=$op->operation_descr; - $templateCfg->template = $op->template; - break; - - case 'do_delete': - $op = doDelete($args,$cfield_mgr); - $user_feedback = $op->user_feedback; - $operation_descr=$op->operation_descr; - $templateCfg->template = $op->template; - $do_control_combo_display = 0; - break; +cfieldCfg; +$gui->cfield = $cfMix->emptyCF; + +switch ($args->do_action) { + case 'create': + $templateCfg->template = $templateCfg->default_template; + $user_feedback = ''; + $operation_descr = ''; + break; + + case 'edit': + $op = edit($args, $cfield_mgr); + $gui->cfield = $op->cf; + $gui->cfield_is_used = $op->cf_is_used; + $gui->cfield_is_linked = $op->cf_is_linked; + $gui->linked_tprojects = $op->linked_tprojects; + $user_feedback = $op->user_feedback; + $operation_descr = $op->operation_descr; + break; + + case 'do_add': + case 'do_add_and_assign': + $op = doCreate($_REQUEST, $cfield_mgr, $args); + $gui->cfield = $op->cf; + $user_feedback = $op->user_feedback; + $templateCfg->template = $op->template; + $operation_descr = ''; + break; + + case 'do_update': + $op = doUpdate($_REQUEST, $args, $cfield_mgr); + $gui->cfield = $op->cf; + $user_feedback = $op->user_feedback; + $operation_descr = $op->operation_descr; + $templateCfg->template = $op->template; + break; + + case 'do_delete': + $op = doDelete($args, $cfield_mgr); + $user_feedback = $op->user_feedback; + $operation_descr = $op->operation_descr; + $templateCfg->template = $op->template; + $do_control_combo_display = 0; + break; +} + +if ($do_control_combo_display) { + $keys2loop = $cfield_mgr->get_application_areas(); + foreach ($keys2loop as $ui_mode) { + $cfieldCfg->cf_enable_on[$ui_mode]['value'] = 0; + $cfieldCfg->cf_show_on[$ui_mode]['disabled'] = ''; + $cfieldCfg->cf_show_on[$ui_mode]['style'] = ''; + + if ($cfieldCfg->enable_on_cfg[$ui_mode][$gui->cfield['node_type_id']]) { + $cfieldCfg->cf_enable_on[$ui_mode]['value'] = 1; + } + + if (! $cfieldCfg->show_on_cfg[$ui_mode][$gui->cfield['node_type_id']]) { + $cfieldCfg->cf_show_on[$ui_mode]['disabled'] = ' disabled="disabled" '; + $cfieldCfg->cf_show_on[$ui_mode]['style'] = ' style="display:none;" '; + } + } +} + +$gui->show_possible_values = 0; +if (isset($gui->cfield['type'])) { + $gui->show_possible_values = $cfieldCfg->possible_values_cfg[$gui->cfield['type']]; +} + +// enable on 'execution' implies show on 'execution' then has nosense to display show_on combo +if ($args->do_action == 'edit' && $gui->cfield['enable_on_execution']) { + $cfieldCfg->cf_show_on['execution']['style'] = ' style="display:none;" '; +} + +$gui->cfieldCfg = $cfieldCfg; + +$smarty = new TLSmarty(); +$smarty->assign('operation_descr', $operation_descr); +$smarty->assign('user_feedback', $user_feedback); +$smarty->assign('user_action', $args->do_action); +renderGui($smarty, $args, $gui, $cfield_mgr, $templateCfg); + +/** + * + * @param stdClass $args + * @param cfield_mgr $cfield_mgr + * @return stdClass + */ +function getCFCfg(&$args, &$cfield_mgr) +{ + $cfg = new stdClass(); + + $cfg->cfieldCfg = cfieldCfgInit($cfield_mgr); + + $cfg->emptyCF = array( + 'id' => $args->cfield_id, + 'name' => '', + 'label' => '', + 'type' => 0, + 'possible_values' => '', + 'show_on_design' => 1, + 'enable_on_design' => 1, + 'show_on_execution' => 0, + 'enable_on_execution' => 0, + 'show_on_testplan_design' => 0, + 'enable_on_testplan_design' => 0 + ); + + $cfg->emptyCF['node_type_id'] = $cfg->cfieldCfg->allowed_nodes['testcase']; + + return $cfg; +} + +/** + * + * @param cfield_mgr $cfield_mgr + * @return stdClass + */ +function initializeGui(&$cfield_mgr) +{ + return $cfield_mgr->initViewGUI(); +} + +/** + * scan a hash looking for a keys with 'cf_' prefix, + * because this keys represents fields of Custom Fields + * tables. + * Is used to get values filled by user on a HTML form. + * This requirement dictated how html inputs must be named. + * If notation is not followed logic will fail. + * + * @param array $hash + * @return array only with related to custom fields, where + * (keys,values) are the original with 'cf_' prefix, but + * in this new hash prefix on key is removed. + * + * rev: + * 20090524 - franciscom - changes due to User Interface changes + * 20080811 - franciscom - added new values on missing_keys + */ +function request2cf($hash) +{ + // design and execution has sense for node types regarding testing + // testplan,testsuite,testcase, but no sense for requirements. + // + // Missing keys are combos that will be disabled and not show at UI. + // For req spec and req, no combo is showed. + // To avoid problems (need to be checked), my choice is set to 1 + // *_on_design keys, that right now will not present only for + // req spec and requirements. + // + $missing_keys = array( + 'show_on_design' => 0, + 'enable_on_design' => 0, + 'show_on_execution' => 0, + 'enable_on_execution' => 0, + 'show_on_testplan_design' => 0, + 'enable_on_testplan_design' => 0, + 'possible_values' => ' ' + ); + + $cf_prefix = 'cf_'; + $len_cfp = tlStringLen($cf_prefix); + $start_pos = $len_cfp; + $cf = array(); + foreach ($hash as $key => $value) { + if (strncmp($key, $cf_prefix, $len_cfp) == 0) { + $dummy = substr($key, $start_pos); + $cf[$dummy] = $value; + } + } + + foreach ($missing_keys as $key => $value) { + if (! isset($cf[$key])) { + $cf[$key] = $value; + } + } + + // After logic refactoring + // if ENABLE_ON_[area] == 1 + // DISPLAY_ON_[area] = 1 + // + // + // IMPORTANT/CRITIC: + // this KEY MUST BE ALIGNED WITH name on User Inteface + // then if is changed on UI must be changed HERE + $setter = array( + 'design' => 0, + 'execution' => 0, + 'testplan_design' => 0 + ); + switch ($cf['enable_on']) { + case 'design': + case 'execution': + case 'testplan_design': + $setter[$cf['enable_on']] = 1; + break; + + default: + $setter['design'] = 1; + break; + } + + foreach ($setter as $key => $value) { + $cf['enable_on_' . $key] = $value; + if ($cf['enable_on_' . $key]) { + $cf['show_on_' . $key] = 1; + } + } + return $cf; +} + +/** + * + * @return stdClass + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + $args->do_action = isset($_REQUEST['do_action']) ? $_REQUEST['do_action'] : null; + $args->cfield_id = isset($_REQUEST['cfield_id']) ? intval( + $_REQUEST['cfield_id']) : 0; + $args->cf_name = isset($_REQUEST['cf_name']) ? $_REQUEST['cf_name'] : null; + + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + if ($args->tproject_id == 0) { + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + return $args; +} + +/** + * + * @param stdClass $argsObj + * @param cfield_mgr $cfieldMgr + * @return stdClass + */ +function edit(&$argsObj, &$cfieldMgr) +{ + $op = new stdClass(); + $op->cf = null; + $op->cf_is_used = 0; + $op->cf_is_linked = 0; + + $op->user_feedback = ''; + $op->template = null; + $op->operation_descr = ''; + $op->linked_tprojects = null; + + $cfinfo = $cfieldMgr->get_by_id($argsObj->cfield_id); + + if ($cfinfo) { + $op->cf = $cfinfo[$argsObj->cfield_id]; + $op->cf_is_used = $cfieldMgr->is_used($argsObj->cfield_id); + + $op->operation_descr = lang_get('title_cfield_edit') . TITLE_SEP_TYPE3 . + $op->cf['name']; + $op->linked_tprojects = $cfieldMgr->get_linked_testprojects( + $argsObj->cfield_id); + $op->cf_is_linked = ! is_null($op->linked_tprojects) && + count($op->linked_tprojects) > 0; + } + return $op; +} + +/** + * + * @param array $hash_request + * @param cfield_mgr $cfieldMgr + * @param stdClass $argsObj + * @return stdClass + */ +function doCreate(&$hash_request, &$cfieldMgr, &$argsObj) +{ + $op = new stdClass(); + $op->template = "cfieldsEdit.tpl"; + $op->user_feedback = ''; + $op->cf = request2cf($hash_request); + + $keys2trim = array( + 'name', + 'label', + 'possible_values' + ); + foreach ($keys2trim as $key) { + $op->cf[$key] = trim($op->cf[$key]); + } + + // Check if name exists + $dupcf = $cfieldMgr->get_by_name($op->cf['name']); + if (is_null($dupcf)) { + $ret = $cfieldMgr->create($op->cf); + if (! $ret['status_ok']) { + $op->user_feedback = lang_get("error_creating_cf"); + } else { + $op->template = null; + logAuditEvent(TLS("audit_cfield_created", $op->cf['name']), "CREATE", + $ret['id'], "custom_fields"); + + if ($hash_request['do_action'] == 'do_add_and_assign') { + $cfieldMgr->link_to_testproject($argsObj->tproject_id, + array( + $ret['id'] + )); + } + } + } else { + $op->user_feedback = lang_get("cf_name_exists"); + } + + return $op; +} + +/** + * + * @param array $hash_request + * @param stdClass $argsObj + * @param cfield_mgr $cfieldMgr + * @return stdClass + */ +function doUpdate(&$hash_request, &$argsObj, &$cfieldMgr) +{ + $op = new stdClass(); + $op->template = "cfieldsEdit.tpl"; + $op->user_feedback = ''; + $op->cf = request2cf($hash_request); + $op->cf['id'] = $argsObj->cfield_id; + + $oldObjData = $cfieldMgr->get_by_id($argsObj->cfield_id); + $oldname = $oldObjData[$argsObj->cfield_id]['name']; + $op->operation_descr = lang_get('title_cfield_edit') . TITLE_SEP_TYPE3 . + $oldname; + + $keys2trim = array( + 'name', + 'label', + 'possible_values' + ); + foreach ($keys2trim as $key) { + $op->cf[$key] = trim($op->cf[$key]); + } + + // Check if name exists + $is_unique = $cfieldMgr->name_is_unique($op->cf['id'], $op->cf['name']); + if ($is_unique) { + $ret = $cfieldMgr->update($op->cf); + if ($ret) { + $op->template = null; + logAuditEvent(TLS("audit_cfield_saved", $op->cf['name']), "SAVE", + $op->cf['id'], "custom_fields"); + } + } else { + $op->user_feedback = lang_get("cf_name_exists"); + } + return $op; +} + +/** + * + * @param stdClass $argsObj + * @param cfield_mgr $cfieldMgr + * @return stdClass + */ +function doDelete(&$argsObj, &$cfieldMgr) +{ + $op = new stdClass(); + $op->user_feedback = ''; + $op->cf = null; + $op->template = null; + $op->operation_descr = ''; + + $cf = $cfieldMgr->get_by_id($argsObj->cfield_id); + if ($cf) { + $cf = $cf[$argsObj->cfield_id]; + if ($cfieldMgr->delete($argsObj->cfield_id)) { + logAuditEvent(TLS("audit_cfield_deleted", $cf['name']), "DELETE", + $argsObj->cfield_id, "custom_fields"); + } + } + return $op; +} + +/** + * + * @param cfield_mgr $cfieldMgr + * @return stdClass + */ +function cfieldCfgInit($cfieldMgr) +{ + $cfg = new stdClass(); + $cfAppAreas = $cfieldMgr->get_application_areas(); + foreach ($cfAppAreas as $area) { + $cfg->disabled_cf_enable_on[$area] = array(); + $cfg->cf_show_on[$area]['disabled'] = ''; + $cfg->cf_show_on[$area]['style'] = ''; + + $cfg->cf_enable_on[$area] = array(); + $cfg->cf_enable_on[$area]['label'] = lang_get($area); + $cfg->cf_enable_on[$area]['value'] = 0; + + $cfg->enable_on_cfg[$area] = $cfieldMgr->get_enable_on_cfg($area); + $cfg->show_on_cfg[$area] = $cfieldMgr->get_show_on_cfg($area); + } + + $cfg->possible_values_cfg = $cfieldMgr->get_possible_values_cfg(); + $cfg->allowed_nodes = $cfieldMgr->get_allowed_nodes(); + $cfg->cf_allowed_nodes = array(); + foreach ($cfg->allowed_nodes as $verbose_type => $type_id) { + $cfg->cf_allowed_nodes[$type_id] = lang_get($verbose_type); + } + + return $cfg; +} + +/** + * set environment and render (if needed) smarty template + * + * @param TLSmarty $smartyObj + * @param stdClass $argsObj + * @param stdClass $guiObj + * @param cfield_mgr $cfieldMgr + * @param stdClass $templateCfg + */ +function renderGui(&$smartyObj, &$argsObj, &$guiObj, &$cfieldMgr, $templateCfg) +{ + $doRender = false; + switch ($argsObj->do_action) { + case "do_add": + case "do_delete": + case "do_update": + case "do_add_and_assign": + $doRender = true; + $tpl = is_null($templateCfg->template) ? 'cfieldsView.tpl' : $templateCfg->template; + break; + + case "edit": + case "create": + $doRender = true; + $tpl = is_null($templateCfg->template) ? $templateCfg->default_template : $templateCfg->template; + break; + } + + if ($doRender) { + $guiObj->cf_map = $cfieldMgr->get_all(null, 'transform'); + $guiObj->cf_types = $cfieldMgr->get_available_types(); + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($templateCfg->template_dir . $tpl); + } +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "cfield_management"); } - -if( $do_control_combo_display ) { - $keys2loop = $cfield_mgr->get_application_areas(); - foreach( $keys2loop as $ui_mode) { - $cfieldCfg->cf_enable_on[$ui_mode]['value']=0; - $cfieldCfg->cf_show_on[$ui_mode]['disabled']=''; - $cfieldCfg->cf_show_on[$ui_mode]['style']=''; - - if($cfieldCfg->enable_on_cfg[$ui_mode][$gui->cfield['node_type_id']]) { - $cfieldCfg->cf_enable_on[$ui_mode]['value']=1; - } - - if(!$cfieldCfg->show_on_cfg[$ui_mode][$gui->cfield['node_type_id']]) { - $cfieldCfg->cf_show_on[$ui_mode]['disabled']=' disabled="disabled" '; - $cfieldCfg->cf_show_on[$ui_mode]['style']=' style="display:none;" '; - } - } -} - -$gui->show_possible_values = 0; -if(isset($gui->cfield['type'])) { - $gui->show_possible_values = $cfieldCfg->possible_values_cfg[$gui->cfield['type']]; -} - -// enable on 'execution' implies show on 'execution' then has nosense to display show_on combo -if($args->do_action == 'edit' && $gui->cfield['enable_on_execution'] ) { - $cfieldCfg->cf_show_on['execution']['style']=' style="display:none;" '; -} - -$gui->cfieldCfg = $cfieldCfg; - -$smarty = new TLSmarty(); -$smarty->assign('operation_descr',$operation_descr); -$smarty->assign('user_feedback',$user_feedback); -$smarty->assign('user_action',$args->do_action); -renderGui($smarty,$args,$gui,$cfield_mgr,$templateCfg); - -/** - * - */ -function getCFCfg(&$args,&$cfield_mgr) { - $cfg = new stdClass(); - - $cfg->cfieldCfg = cfieldCfgInit($cfield_mgr); - - $cfg->emptyCF = array('id' => $args->cfield_id, - 'name' => '','label' => '', - 'type' => 0,'possible_values' => '', - 'show_on_design' => 1,'enable_on_design' => 1, - 'show_on_execution' => 0,'enable_on_execution' => 0, - 'show_on_testplan_design' => 0, - 'enable_on_testplan_design' => 0); - - $cfg->emptyCF['node_type_id'] = $cfg->cfieldCfg->allowed_nodes['testcase']; - - return $cfg; -} - - -/** - * - */ -function initializeGui(&$cfield_mgr) { - $gui = $cfield_mgr->initViewGUI(); - return $gui; -} - - - -/* - function: request2cf - scan a hash looking for a keys with 'cf_' prefix, - because this keys represents fields of Custom Fields - tables. - Is used to get values filled by user on a HTML form. - This requirement dictated how html inputs must be named. - If notation is not followed logic will fail. - - args: hash - - returns: hash only with related to custom fields, where - (keys,values) are the original with 'cf_' prefix, but - in this new hash prefix on key is removed. - - rev: - 20090524 - franciscom - changes due to User Interface changes - 20080811 - franciscom - added new values on missing_keys - -*/ -function request2cf($hash) -{ - // design and execution has sense for node types regarding testing - // testplan,testsuite,testcase, but no sense for requirements. - // - // Missing keys are combos that will be disabled and not show at UI. - // For req spec and req, no combo is showed. - // To avoid problems (need to be checked), my choice is set to 1 - // *_on_design keys, that right now will not present only for - // req spec and requirements. - // - $missing_keys = array('show_on_design' => 0, - 'enable_on_design' => 0, - 'show_on_execution' => 0, - 'enable_on_execution' => 0, - 'show_on_testplan_design' => 0, - 'enable_on_testplan_design' => 0, - 'possible_values' => ' ' ); - - $cf_prefix = 'cf_'; - $len_cfp = tlStringLen($cf_prefix); - $start_pos = $len_cfp; - $cf = array(); - foreach($hash as $key => $value) - { - if(strncmp($key,$cf_prefix,$len_cfp) == 0) - { - $dummy = substr($key,$start_pos); - $cf[$dummy] = $value; - } - } - - foreach($missing_keys as $key => $value) - { - if(!isset($cf[$key])) - { - $cf[$key] = $value; - } - } - - // After logic refactoring - // if ENABLE_ON_[area] == 1 - // DISPLAY_ON_[area] = 1 - // - // - // IMPORTANT/CRITIC: - // this KEY MUST BE ALIGNED WITH name on User Inteface - // then if is changed on UI must be changed HERE - $setter=array('design' => 0, 'execution' => 0, 'testplan_design' => 0); - switch($cf['enable_on']) - { - case 'design': - case 'execution': - case 'testplan_design': - $setter[$cf['enable_on']]=1; - break; - - default: - $setter['design']=1; - break; - } - - foreach($setter as $key => $value) - { - $cf['enable_on_' . $key] = $value; - if( $cf['enable_on_' . $key] ) - { - $cf['show_on_' . $key] = 1; - } - } - return $cf; -} - -/* - function: - - args: - - returns: - -*/ -function init_args() -{ - $_REQUEST=strings_stripSlashes($_REQUEST); - $args = new stdClass(); - $args->do_action = isset($_REQUEST['do_action']) ? $_REQUEST['do_action'] : null; - $args->cfield_id = isset($_REQUEST['cfield_id']) ? intval($_REQUEST['cfield_id']) : 0; - $args->cf_name = isset($_REQUEST['cf_name']) ? $_REQUEST['cf_name'] : null; - - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - if( $args->tproject_id == 0 ) - { - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - return $args; -} - -/* - function: edit - - args: - - returns: - -*/ -function edit(&$argsObj,&$cfieldMgr) -{ - $op = new stdClass(); - $op->cf = null; - $op->cf_is_used = 0; - $op->cf_is_linked = 0; - - $op->user_feedback = ''; - $op->template = null; - $op->operation_descr = ''; - $op->linked_tprojects = null; - - $cfinfo = $cfieldMgr->get_by_id($argsObj->cfield_id); - - if ($cfinfo) - { - $op->cf = $cfinfo[$argsObj->cfield_id]; - $op->cf_is_used = $cfieldMgr->is_used($argsObj->cfield_id); - - $op->operation_descr = lang_get('title_cfield_edit') . TITLE_SEP_TYPE3 . $op->cf['name']; - $op->linked_tprojects = $cfieldMgr->get_linked_testprojects($argsObj->cfield_id); - $op->cf_is_linked = !is_null($op->linked_tprojects) && count($op->linked_tprojects) > 0; - } - return $op; -} - - -/* - function: doCreate - - args: - - returns: - -*/ -function doCreate(&$hash_request,&$cfieldMgr,&$argsObj) -{ - $op = new stdClass(); - $op->template = "cfieldsEdit.tpl"; - $op->user_feedback=''; - $op->cf = request2cf($hash_request); - - $keys2trim=array('name','label','possible_values'); - foreach($keys2trim as $key) - { - $op->cf[$key]=trim($op->cf[$key]); - } - - // Check if name exists - $dupcf = $cfieldMgr->get_by_name($op->cf['name']); - if(is_null($dupcf)) - { - $ret = $cfieldMgr->create($op->cf); - if(!$ret['status_ok']) - { - $op->user_feedback = lang_get("error_creating_cf"); - } - else - { - $op->template = null; - logAuditEvent(TLS("audit_cfield_created",$op->cf['name']),"CREATE",$ret['id'],"custom_fields"); - - if($hash_request['do_action'] == 'do_add_and_assign') - { - $cfieldMgr->link_to_testproject($argsObj->tproject_id,array($ret['id'])); - } - } - } - else - { - $op->user_feedback = lang_get("cf_name_exists"); - } - - return $op; -} - - - -/* - function: doUpdate - - args: - - returns: - -*/ -function doUpdate(&$hash_request,&$argsObj,&$cfieldMgr) -{ - $op = new stdClass(); - $op->template = "cfieldsEdit.tpl"; - $op->user_feedback=''; - $op->cf = request2cf($hash_request); - $op->cf['id'] = $argsObj->cfield_id; - - $oldObjData=$cfieldMgr->get_by_id($argsObj->cfield_id); - $oldname=$oldObjData[$argsObj->cfield_id]['name']; - $op->operation_descr=lang_get('title_cfield_edit') . TITLE_SEP_TYPE3 . $oldname; - - $keys2trim=array('name','label','possible_values'); - foreach($keys2trim as $key) - { - $op->cf[$key]=trim($op->cf[$key]); - } - - // Check if name exists - $is_unique = $cfieldMgr->name_is_unique($op->cf['id'],$op->cf['name']); - if($is_unique) - { - $ret = $cfieldMgr->update($op->cf); - if ($ret) - { - $op->template = null; - logAuditEvent(TLS("audit_cfield_saved",$op->cf['name']),"SAVE",$op->cf['id'],"custom_fields"); - } - } - else - { - $op->user_feedback = lang_get("cf_name_exists"); - } - return $op; -} - - - -/* - function: doDelete - - args: - - returns: - -*/ -function doDelete(&$argsObj,&$cfieldMgr) -{ - $op = new stdClass(); - $op->user_feedback=''; - $op->cf = null; - $op->template = null; - $op->operation_descr = ''; - - $cf = $cfieldMgr->get_by_id($argsObj->cfield_id); - if ($cf) - { - $cf = $cf[$argsObj->cfield_id]; - if ($cfieldMgr->delete($argsObj->cfield_id)) - { - logAuditEvent(TLS("audit_cfield_deleted",$cf['name']),"DELETE",$argsObj->cfield_id,"custom_fields"); - } - } - return $op; -} - - -/* - function: cfieldCfgInit - - args : - - returns: object with configuration options -*/ -function cfieldCfgInit($cfieldMgr) -{ - $cfg = new stdClass(); - $cfAppAreas=$cfieldMgr->get_application_areas(); - foreach($cfAppAreas as $area) - { - $cfg->disabled_cf_enable_on[$area] = array(); - $cfg->cf_show_on[$area]['disabled'] = ''; - $cfg->cf_show_on[$area]['style'] = ''; - - $cfg->cf_enable_on[$area] = array(); - $cfg->cf_enable_on[$area]['label'] = lang_get($area); - $cfg->cf_enable_on[$area]['value'] = 0; - - $cfg->enable_on_cfg[$area] = $cfieldMgr->get_enable_on_cfg($area); - $cfg->show_on_cfg[$area] = $cfieldMgr->get_show_on_cfg($area); - } - - $cfg->possible_values_cfg = $cfieldMgr->get_possible_values_cfg(); - $cfg->allowed_nodes = $cfieldMgr->get_allowed_nodes(); - $cfg->cf_allowed_nodes = array(); - foreach($cfg->allowed_nodes as $verbose_type => $type_id) - { - $cfg->cf_allowed_nodes[$type_id] = lang_get($verbose_type); - } - - return $cfg; -} - - -/* - function: renderGui - set environment and render (if needed) smarty template - - args: - - returns: - - - -*/ -function renderGui(&$smartyObj,&$argsObj,&$guiObj,&$cfieldMgr,$templateCfg) -{ - $doRender=false; - switch($argsObj->do_action) - { - case "do_add": - case "do_delete": - case "do_update": - case "do_add_and_assign": - $doRender=true; - $tpl = is_null($templateCfg->template) ? 'cfieldsView.tpl' : $templateCfg->template; - break; - - case "edit": - case "create": - $doRender=true; - $tpl = is_null($templateCfg->template) ? $templateCfg->default_template : $templateCfg->template; - break; - } - - if($doRender) - { - $guiObj->cf_map = $cfieldMgr->get_all(null,'transform'); - $guiObj->cf_types=$cfieldMgr->get_available_types(); - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($templateCfg->template_dir . $tpl); - } -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"cfield_management"); -} \ No newline at end of file diff --git a/lib/cfields/cfieldsExport.php b/lib/cfields/cfieldsExport.php index 9d0860f26c..5662b829a2 100644 --- a/lib/cfields/cfieldsExport.php +++ b/lib/cfields/cfieldsExport.php @@ -1,112 +1,118 @@ -page_title = lang_get('export_cfields'); -$gui->do_it = 1; -$gui->nothing_todo_msg = ''; -$gui->goback_url = !is_null($args->goback_url) ? $args->goback_url : ''; -$gui->export_filename = is_null($args->export_filename) ? 'customFields.xml' : $args->export_filename; -$gui->exportTypes = array('XML' => 'XML'); - -switch( $args->doAction ) -{ - case 'doExport': - doExport($db,$gui->export_filename); - break; - - default: - break; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: init_args() - - args: - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $iParams = array("doAction" => array(tlInputParameter::STRING_N,0,50), - "export_filename" => array(tlInputParameter::STRING_N,0,100), - "goback_url" => array(tlInputParameter::STRING_N,0,2048)); - - R_PARAMS($iParams,$args); - $args->userID = $_SESSION['userID']; - - return $args; -} - - - -/* - function: doExport() - - args: dbHandler - filename: where to export - - returns: - - -*/ -function doExport(&$dbHandler,$filename) -{ - $tables = tlObjectWithDB::getDBTables(array('custom_fields','cfield_node_types')); - - // To solve issues with MAC OS - $tmp = (PHP_OS == 'Darwin') ? config_get('temp_dir') : null; - - $adodbXML = new ADODB_XML("1.0", "ISO-8859-1",$tmp); - $sql = " SELECT name,label,type,possible_values,default_value,valid_regexp, " . - " length_min,length_max,show_on_design,enable_on_design,show_on_execution," . - " enable_on_execution,show_on_testplan_design,enable_on_testplan_design, " . - " node_type_id " . - " FROM {$tables['custom_fields']} CF,{$tables['cfield_node_types']} " . - " WHERE CF.id=field_id "; - - $adodbXML->setRootTagName('custom_fields'); - $adodbXML->setRowTagName('custom_field'); - - $content = $adodbXML->ConvertToXMLString($dbHandler->db, $sql); - downloadContentsToFile($content,$filename); - exit(); -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"cfield_view"); -} -?> \ No newline at end of file +page_title = lang_get('export_cfields'); +$gui->do_it = 1; +$gui->nothing_todo_msg = ''; +$gui->goback_url = ! is_null($args->goback_url) ? $args->goback_url : ''; +$gui->export_filename = is_null($args->export_filename) ? 'customFields.xml' : $args->export_filename; +$gui->exportTypes = array( + 'XML' => 'XML' +); + +switch ($args->doAction) { + case 'doExport': + doExport($db, $gui->export_filename); + break; + + default: + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "export_filename" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + + R_PARAMS($iParams, $args); + $args->userID = $_SESSION['userID']; + + $args->goback_url = $_SESSION['basehref'] . 'lib/cfields/cfieldsView.php'; + + return $args; +} + +/** + * + * @param database $dbHandler + * @param string $filename + */ +function doExport(&$dbHandler, $filename) +{ + $tables = tlObjectWithDB::getDBTables( + array( + 'custom_fields', + 'cfield_node_types' + )); + + // To solve issues with MAC OS + $tmp = (PHP_OS == 'Darwin') ? config_get('temp_dir') : null; + + $adodbXML = new ADODB_XML("1.0", "ISO-8859-1", $tmp); + $sql = " SELECT name,label,type,possible_values,default_value,valid_regexp, " . + " length_min,length_max,show_on_design,enable_on_design,show_on_execution," . + " enable_on_execution,show_on_testplan_design,enable_on_testplan_design, " . + " node_type_id " . + " FROM {$tables['custom_fields']} CF,{$tables['cfield_node_types']} " . + " WHERE CF.id=field_id "; + + $adodbXML->setRootTagName('custom_fields'); + $adodbXML->setRowTagName('custom_field'); + + $content = $adodbXML->ConvertToXMLString($dbHandler->db, $sql); + downloadContentsToFile($content, $filename); + exit(); +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "cfield_view"); +} +?> diff --git a/lib/cfields/cfieldsImport.php b/lib/cfields/cfieldsImport.php index 21034948e4..724b7dff05 100644 --- a/lib/cfields/cfieldsImport.php +++ b/lib/cfields/cfieldsImport.php @@ -1,146 +1,153 @@ -page_title=lang_get('import_cfields'); -$gui->goback_url = !is_null($args->goback_url) ? $args->goback_url : ''; -$gui->file_check = array('show_results' => 0, 'status_ok' => 1, - 'msg' => 'ok', 'filename' => ''); - -switch( $args->doAction ) -{ - case 'doImport': - $gui->file_check = doImport($db); - break; - - default: - break; -} - -$obj_mgr = new cfield_mgr($db); -$gui->importTypes = array('XML' => 'XML'); -$gui->importLimitKB = (config_get('import_file_max_size_bytes') / 1024); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/* - function: init_args() - - args: - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $iParams = array("doAction" => array(tlInputParameter::STRING_N,0,50), - "export_filename" => array(tlInputParameter::STRING_N,0,100), - "goback_url" => array(tlInputParameter::STRING_N,0,2048)); - - R_PARAMS($iParams,$args); - - // $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; - // $args->export_filename=isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : null; - // $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; - - $args->userID = $_SESSION['userID']; - - return $args; -} - - -/** - * @param object dbHandler reference to db handler - * - */ -function doImport(&$dbHandler) -{ - - $import_msg=array('ok' => array(), 'ko' => array()); - $file_check=array('show_results' => 0, 'status_ok' => 0, 'msg' => '', - 'filename' => '', 'import_msg' => $import_msg); - - $key='targetFilename'; - $dest = TL_TEMP_PATH . session_id(). "-import_cfields.tmp"; - $source = isset($_FILES[$key]['tmp_name']) ? $_FILES[$key]['tmp_name'] : null; - - if (($source != 'none') && ($source != '')) - { - $file_check['filename'] = $_FILES[$key]['name']; - $file_check['status_ok'] = 1; - if (move_uploaded_file($source, $dest)) - { - $file_check['status_ok']=!(($xml=@simplexml_load_file_wrapper($dest)) === FALSE); - } - if( $file_check['status_ok'] ) - { - $file_check['show_results']=1; - $cfield_mgr = new cfield_mgr($dbHandler); - foreach($xml as $cf) - { - if( is_null($cfield_mgr->get_by_name($cf->name)) ) - { - $cfield_mgr->create((array) $cf); - $import_msg['ok'][]=sprintf(lang_get('custom_field_imported'),$cf->name); - } - else - { - $import_msg['ko'][]=sprintf(lang_get('custom_field_already_exists'),$cf->name); - } - } - } - else - { - $file_check['msg']=lang_get('problems_loading_xml_content'); - } - } - else - { - $file_check = array('show_results'=>0, 'status_ok' => 0, - 'msg' => lang_get('please_choose_file_to_import')); - } - - $file_check['import_msg']=$import_msg; - return $file_check; -} - -/** - * - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"cfield_management"); -} -?> \ No newline at end of file +page_title = lang_get('import_cfields'); +$gui->goback_url = ! is_null($args->goback_url) ? $args->goback_url : ''; +$gui->file_check = array( + 'show_results' => 0, + 'status_ok' => 1, + 'msg' => 'ok', + 'filename' => '' +); + +switch ($args->doAction) { + case 'doImport': + $gui->file_check = doImport($db); + break; + + default: + break; +} + +$obj_mgr = new cfield_mgr($db); +$gui->importTypes = array( + 'XML' => 'XML' +); +$gui->importLimitKB = (config_get('import_file_max_size_bytes') / 1024); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "export_filename" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + + R_PARAMS($iParams, $args); + $args->userID = $_SESSION['userID']; + + $args->goback_url = $_SESSION['basehref'] . 'lib/cfields/cfieldsView.php'; + + return $args; +} + +/** + * + * @param database $dbHandler + * reference to db handler + * + */ +function doImport(&$dbHandler) +{ + $import_msg = array( + 'ok' => array(), + 'ko' => array() + ); + $file_check = array( + 'show_results' => 0, + 'status_ok' => 0, + 'msg' => '', + 'filename' => '', + 'import_msg' => $import_msg + ); + + $key = 'targetFilename'; + $dest = TL_TEMP_PATH . session_id() . "-import_cfields.tmp"; + $source = isset($_FILES[$key]['tmp_name']) ? $_FILES[$key]['tmp_name'] : null; + + if (($source != 'none') && ($source != '')) { + $file_check['filename'] = $_FILES[$key]['name']; + $file_check['status_ok'] = 1; + if (move_uploaded_file($source, $dest)) { + $file_check['status_ok'] = (($xml = @simplexml_load_file_wrapper( + $dest)) !== false); + } + if ($file_check['status_ok']) { + $file_check['show_results'] = 1; + $cfield_mgr = new cfield_mgr($dbHandler); + foreach ($xml as $cf) { + if (is_null($cfield_mgr->get_by_name($cf->name))) { + $cfield_mgr->create((array) $cf); + $import_msg['ok'][] = sprintf( + lang_get('custom_field_imported'), $cf->name); + } else { + $import_msg['ko'][] = sprintf( + lang_get('custom_field_already_exists'), $cf->name); + } + } + } else { + $file_check['msg'] = lang_get('problems_loading_xml_content'); + } + } else { + $file_check = array( + 'show_results' => 0, + 'status_ok' => 0, + 'msg' => lang_get('please_choose_file_to_import') + ); + } + + $file_check['import_msg'] = $import_msg; + return $file_check; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return array + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "cfield_management"); +} +?> diff --git a/lib/cfields/cfieldsTprojectAssign.php b/lib/cfields/cfieldsTprojectAssign.php index df83d5c9d7..1d76020354 100644 --- a/lib/cfields/cfieldsTprojectAssign.php +++ b/lib/cfields/cfieldsTprojectAssign.php @@ -1,278 +1,276 @@ -checkedCF); - -switch($args->doAction) -{ - case 'doAssign': - $cfield_mgr->link_to_testproject($args->tproject_id,$checkedIDSet); - break; - - case 'doUnassign': - $cfield_mgr->unlink_from_testproject($args->tproject_id,$checkedIDSet); - break; - - case 'doReorder': - // To make user's life simpler, we work on all linked CF - // and not only on selected. - $cfield_mgr->set_display_order($args->tproject_id,$args->display_order); - if( !is_null($args->location) ) - { - $cfield_mgr->setDisplayLocation($args->tproject_id,$args->location); - } - break; - - case 'doBooleanMgmt': - // To make user's life simpler, we work on all linked CF - // and not only on selected. - $args->attrBefore = $cfield_mgr->getBooleanAttributes($args->tproject_id); - doActiveMgmt($cfield_mgr,$args); - doRequiredMgmt($cfield_mgr,$args); - doMonitorableMgmt($cfield_mgr,$args); - break; - +checkedCF); + +switch ($args->doAction) { + case 'doAssign': + $cfield_mgr->link_to_testproject($args->tproject_id, $checkedIDSet); + break; + + case 'doUnassign': + $cfield_mgr->unlink_from_testproject($args->tproject_id, $checkedIDSet); + break; + + case 'doReorder': + // To make user's life simpler, we work on all linked CF + // and not only on selected. + $cfield_mgr->set_display_order($args->tproject_id, $args->display_order); + if (! is_null($args->location)) { + $cfield_mgr->setDisplayLocation($args->tproject_id, $args->location); + } + break; + + case 'doBooleanMgmt': + // To make user's life simpler, we work on all linked CF + // and not only on selected. + $args->attrBefore = $cfield_mgr->getBooleanAttributes( + $args->tproject_id); + doActiveMgmt($cfield_mgr, $args); + doRequiredMgmt($cfield_mgr, $args); + doMonitorableMgmt($cfield_mgr, $args); + break; +} + +// Get all available custom fields +$cfield_map = $cfield_mgr->get_all(); + +// It's better to get AGAIN CF info AFTER user operations has been applied +// in order to display UPDATED Situation +$gui = initializeGui($args, $cfield_mgr); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * create object with all user inputs + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + + $key2search = array( + 'doAction', + 'checkedCF', + 'display_order', + 'location', + 'hidden_active_cfield', + 'active_cfield', + 'hidden_required_cfield', + 'required_cfield', + 'hidden_monitorable_cfield', + 'monitorable_cfield' + ); + + foreach ($key2search as $key) { + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; + } + + if (is_null($args->checkedCF)) { + $args->checkedCF = array(); + } + + getTproj($dbHandler, $args); + + return $args; +} + +/** + * + * @param database $dbH + * @param stdClass $args + */ +function getTproj(&$dbH, &$args) +{ + $args->tproject_name = ''; + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + + if ($args->tproject_id == 0) { + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + if ($args->tproject_id > 0) { + $mgr = new tree($dbH); + $dummy = $mgr->get_node_hierarchy_info($args->tproject_id, null, + array( + 'nodeType' => 'testproject' + )); + if (is_null($dummy)) { + throw new Exception("Unable to get Test Project ID"); + } + $args->tproject_name = $dummy['name']; + } +} + +/** + * + * @param stdClass $args + * @param cfield_mgr $cfield_mgr + * @return stdClass + */ +function initializeGui(&$args, &$cfield_mgr) +{ + $gui = new stdClass(); + + $gui->locations = createLocationsMenu($cfield_mgr->getLocations()); + $gui->tproject_name = $args->tproject_name; + + $gui->linkedCF = $cfield_mgr->get_linked_to_testproject($args->tproject_id); + $cf2exclude = is_null($gui->linkedCF) ? null : array_keys($gui->linkedCF); + $gui->other_cf = $cfield_mgr->get_all($cf2exclude); + + $gui->cf_available_types = $cfield_mgr->get_available_types(); + $gui->cf_allowed_nodes = array(); + $allowed_nodes = $cfield_mgr->get_allowed_nodes(); + + foreach ($allowed_nodes as $verbose_type => $type_id) { + $gui->cf_allowed_nodes[$type_id] = lang_get($verbose_type); + } + + return $gui; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "cfield_management"); +} + +/** + * + * @param array $locations + * map of maps with locations of CF + * key: item type: 'testcase','testsuite', etc + * @return NULL + */ +function createLocationsMenu($locations) +{ + $menuContents = null; + $items = $locations['testcase']; + foreach ($items as $code => $key4label) { + $menuContents[$code] = lang_get($key4label); + } + return $menuContents; +} + +/** + * + * @param cfield_mgr $cfieldMgr + * @param stdClass $argsObj + */ +function doRequiredMgmt(&$cfieldMgr, $argsObj) +{ + $cfg = array(); + $cfg['attrKey'] = 'required'; + $cfg['dbField'] = 'required'; + $cfg['attr'] = "{$cfg['attrKey']}_cfield"; + $cfg['m2c'] = 'set' . ucfirst($cfg['attrKey']); + $cfg['ha'] = "hidden_{$cfg['attr']}"; + + doSimpleBooleanMgmt($cfieldMgr, $argsObj, $cfg); +} + +/** + * + * @param cfield_mgr $cfieldMgr + * @param stdClass $argsObj + */ +function doActiveMgmt(&$cfieldMgr, $argsObj) +{ + $cfg = array(); + $cfg['attrKey'] = 'active'; + $cfg['dbField'] = 'active'; + $cfg['attr'] = "{$cfg['attrKey']}_cfield"; + $cfg['m2c'] = 'set_active_for_testproject'; + $cfg['ha'] = "hidden_{$cfg['attr']}"; + + doSimpleBooleanMgmt($cfieldMgr, $argsObj, $cfg); +} + +/** + * + * @param cfield_mgr $cfieldMgr + * @param stdClass $argsObj + */ +function doMonitorableMgmt(&$cfieldMgr, $argsObj) +{ + $cfg = array(); + $cfg['attrKey'] = 'monitorable'; + $cfg['dbField'] = 'monitorable'; + $cfg['attr'] = "{$cfg['attrKey']}_cfield"; + $cfg['m2c'] = 'set' . ucfirst($cfg['attrKey']); + $cfg['ha'] = "hidden_{$cfg['attr']}"; + + doSimpleBooleanMgmt($cfieldMgr, $argsObj, $cfg); +} + +/** + * + * @param cfield_mgr $cfieldMgr + * @param stdClass $argsObj + * @param array $cfg + */ +function doSimpleBooleanMgmt(&$cfieldMgr, $argsObj, $cfg) +{ + + // This way user does not need to check cf for this operations + // Think makes life easier + $serviceInput = $cfg['ha']; + $cfSet = array_keys($argsObj->$serviceInput); + + $m2c = $cfg['m2c']; + $operativeInput = $cfg['attr']; + + // we are working with checkboxes, and as we know if is not checked + // nothing will arrive on $_REQUEST + if (is_null($argsObj->$operativeInput)) { + $cfieldMgr->$m2c($argsObj->tproject_id, $cfSet, 0); + } else { + $on = null; + $off = null; + foreach ($cfSet as $id) { + if (isset($argsObj->$operativeInput[$id])) { + if ($argsObj->attrBefore[$id][$cfg['dbField']] == 0) { + $on[] = $id; + } + } else { + if ($argsObj->attrBefore[$id][$cfg['dbField']] == 1) { + $off[] = $id; + } + } + } + + if (! is_null($on)) { + $cfieldMgr->$m2c($argsObj->tproject_id, $on, 1); + } + + if (! is_null($off)) { + $cfieldMgr->$m2c($argsObj->tproject_id, $off, 0); + } + } } - -// Get all available custom fields -$cfield_map = $cfield_mgr->get_all(); - -// It's better to get AGAIN CF info AFTER user operations has been applied -// in order to display UPDATED Situation -$gui = initializeGui($args,$cfield_mgr); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * create object with all user inputs - * - */ -function init_args(&$dbHandler) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - - $key2search = array('doAction','checkedCF','display_order','location', - 'hidden_active_cfield','active_cfield', - 'hidden_required_cfield','required_cfield', - 'hidden_monitorable_cfield','monitorable_cfield'); - - foreach($key2search as $key) - { - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null; - } - - if( is_null($args->checkedCF) ) - { - $args->checkedCF = array(); - } - - getTproj($dbHandler,$args); - - return $args; -} - - -/** - * - */ -function getTproj(&$dbH,&$args) -{ - $args->tproject_name = ''; - $args->tproject_id = isset($_REQUEST['tproject_id']) ? - intval($_REQUEST['tproject_id']) : 0; - - if( $args->tproject_id == 0 ) - { - $args->tproject_id = isset($_SESSION['testprojectID']) ? - intval($_SESSION['testprojectID']) : 0; - } - - if( $args->tproject_id > 0 ) - { - $mgr = new tree($dbH); - $dummy = $mgr->get_node_hierarchy_info($args->tproject_id,null, - array('nodeType' => 'testproject')); - if(is_null($dummy)) - { - throw new Exception("Unable to get Test Project ID"); - } - $args->tproject_name = $dummy['name']; - } -} - -/** - * - */ -function initializeGui(&$args,&$cfield_mgr) -{ - $gui = new stdClass(); - - $gui->locations=createLocationsMenu($cfield_mgr->getLocations()); - $gui->tproject_name = $args->tproject_name; - - $gui->linkedCF = $cfield_mgr->get_linked_to_testproject($args->tproject_id); - $cf2exclude = is_null($gui->linkedCF) ? null :array_keys($gui->linkedCF); - $gui->other_cf = $cfield_mgr->get_all($cf2exclude); - - $gui->cf_available_types = $cfield_mgr->get_available_types(); - $gui->cf_allowed_nodes = array(); - $allowed_nodes = $cfield_mgr->get_allowed_nodes(); - - foreach($allowed_nodes as $verbose_type => $type_id) - { - $gui->cf_allowed_nodes[$type_id] = lang_get($verbose_type); - } - - return $gui; -} - -/** - * - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"cfield_management"); -} - -/** - * @parame map of maps with locations of CF - * key: item type: 'testcase','testsuite', etc - * - */ -function createLocationsMenu($locations) -{ - $menuContents=null; - $items = $locations['testcase']; - - // loop over status for user interface, because these are the statuses - // user can assign while executing test cases - - foreach($items as $code => $key4label) - { - $menuContents[$code] = lang_get($key4label); - } - - return $menuContents; -} - - -/** - * - */ -function doRequiredMgmt(&$cfieldMgr,$argsObj) -{ - $cfg = array(); - $cfg['attrKey'] = 'required'; - $cfg['dbField'] = 'required'; - $cfg['attr'] = "{$cfg['attrKey']}_cfield"; - $cfg['m2c'] = 'set' . ucfirst($cfg['attrKey']); - $cfg['ha'] = "hidden_{$cfg['attr']}"; - - doSimpleBooleanMgmt($cfieldMgr,$argsObj,$cfg); -} - - -/** - * - */ -function doActiveMgmt(&$cfieldMgr,$argsObj) -{ - $cfg = array(); - $cfg['attrKey'] = 'active'; - $cfg['dbField'] = 'active'; - $cfg['attr'] = "{$cfg['attrKey']}_cfield"; - $cfg['m2c'] = 'set_active_for_testproject'; - $cfg['ha'] = "hidden_{$cfg['attr']}"; - - doSimpleBooleanMgmt($cfieldMgr,$argsObj,$cfg); -} - -/** - * - */ -function doMonitorableMgmt(&$cfieldMgr,$argsObj) -{ - $cfg = array(); - $cfg['attrKey'] = 'monitorable'; - $cfg['dbField'] = 'monitorable'; - $cfg['attr'] = "{$cfg['attrKey']}_cfield"; - $cfg['m2c'] = 'set' . ucfirst($cfg['attrKey']); - $cfg['ha'] = "hidden_{$cfg['attr']}"; - - doSimpleBooleanMgmt($cfieldMgr,$argsObj,$cfg); -} - -/** - * - * - */ -function doSimpleBooleanMgmt(&$cfieldMgr,$argsObj,$cfg) -{ - - // This way user does not need to check cf for this operations - // Think makes life easier - $serviceInput = $cfg['ha']; - $cfSet = array_keys($argsObj->$serviceInput); - - $m2c = $cfg['m2c']; - $operativeInput = $cfg['attr']; - - // we are working with checkboxes, and as we know if is not checked - // nothing will arrive on $_REQUEST - if( is_null($argsObj->$operativeInput) ) - { - $cfieldMgr->$m2c($argsObj->tproject_id,$cfSet,0); - } - else - { - $on = null; - $off = null; - foreach($cfSet as $id) - { - if( isset($argsObj->$operativeInput[$id]) ) - { - if($argsObj->attrBefore[$id][$cfg['dbField']] == 0) - { - $on[] = $id; - } - } - else - { - if($argsObj->attrBefore[$id][$cfg['dbField']] == 1) - { - $off[] = $id; - } - } - } - - if(!is_null($on)) - { - $cfieldMgr->$m2c($argsObj->tproject_id,$on,1); - } - - if(!is_null($off)) - { - $cfieldMgr->$m2c($argsObj->tproject_id,$off,0); - } - } - -} - \ No newline at end of file diff --git a/lib/cfields/cfieldsView.php b/lib/cfields/cfieldsView.php index 0c3a8c4469..81767ccf77 100644 --- a/lib/cfields/cfieldsView.php +++ b/lib/cfields/cfieldsView.php @@ -1,30 +1,34 @@ -initViewGUI(); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - */ -function checkRights(&$db,&$user) { - return $user->hasRight($db,"cfield_management") || $user->hasRight($db,"cfield_view"); +initViewGUI(); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @param database $db + * @param tlUser $user + * @return boolean + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "cfield_management") || + $user->hasRight($db, "cfield_view"); } diff --git a/lib/codetrackerintegration/codeTrackerInterface.class.php b/lib/codetrackerintegration/codeTrackerInterface.class.php index 1a89af649e..b128c0f4a0 100755 --- a/lib/codetrackerintegration/codeTrackerInterface.class.php +++ b/lib/codetrackerintegration/codeTrackerInterface.class.php @@ -1,331 +1,325 @@ -tlCharSet = config_get('charset'); - $this->guiCfg = array('use_decoration' => true); // add [] on summary and statusHTMLString - $this->name = $name; - - if( $this->setCfg($config) ) - { - // useful only for integration via DB - if( !property_exists($this->cfg,'dbcharset') ) - { - $this->cfg->dbcharset = $this->tlCharSet; - } - $this->connect(); - } - else - { - $this->connected = false; - } - } - - /** - * - **/ - function getCfg() - { - return $this->cfg; - } - - /** - * - * - **/ - function setCfg($xmlString) - { - $msg = null; - $signature = 'Source:' . __METHOD__; - - // check for empty string - if(strlen(trim($xmlString)) == 0) - { - // Bye,Bye - $msg = " - Code tracker:$this->name - XML Configuration seems to be empty - please check"; - tLog(__METHOD__ . $msg, 'ERROR'); - return false; - } - - $xmlCfg = " " . $xmlString; - libxml_use_internal_errors(true); - try - { - $this->cfg = simplexml_load_string($xmlCfg); - if (!$this->cfg) - { - $msg = $signature . " - Failure loading XML STRING\n"; - foreach(libxml_get_errors() as $error) - { - $msg .= "\t" . $error->message; - } - } - } - catch(Exception $e) - { - $msg = $signature . " - Exception loading XML STRING\n"; - $msg .= 'Message: ' .$e->getMessage(); - } - - if( !($retval = is_null($msg)) ) - { - tLog(__METHOD__ . $msg, 'ERROR'); - } - - // From - // http://php.net/manual/it/function.unserialize.php#112823 - // - // After PHP 5.3 an object made by - // SimpleXML_Load_String() cannot be serialized. - // An attempt to do so will result in a run-time - // failure, throwing an exception. - // - // If you store such an object in $_SESSION, - // you will get a post-execution error that says this: - // Fatal error: Uncaught exception 'Exception' - // with message 'Serialization of 'SimpleXMLElement' - // is not allowed' in [no active file]:0 - // Stack trace: #0 {main} thrown in [no active file] - // on line 0 - // - // !!!!! The entire contents of the session will be lost. - // http://stackoverflow.com/questions/1584725/quickly-convert-simplexmlobject-to-stdclass - $this->cfg = json_decode(json_encode($this->cfg)); - return $retval; - } - - /** - * - **/ - function getMyInterface() - { - return $this->cfg->interfacePHP; - } - - /** - * establishes the database connection to the codetracking system - * - * @return bool returns true if the db connection was established and the - * db could be selected, false else - * - **/ - function connect() - { - if (is_null($this->cfg->dbhost) || is_null($this->cfg->dbuser)) - { - return false; - } - - // cast everything to string in order to avoid issues - // @20140604 someone has been issues trying to connect to JIRA on MSSQL - $this->cfg->dbtype = strtolower((string)$this->cfg->dbtype); - $this->cfg->dbhost = (string)$this->cfg->dbhost; - $this->cfg->dbuser = (string)$this->cfg->dbuser; - $this->cfg->dbpassword = (string)$this->cfg->dbpassword; - $this->cfg->dbname = (string)$this->cfg->dbname; - - $this->dbConnection = new database($this->cfg->dbtype); - $result = $this->dbConnection->connect(false, $this->cfg->dbhost,$this->cfg->dbuser, - $this->cfg->dbpassword, $this->cfg->dbname); - - if (!$result['status']) - { - $this->dbConnection = null; - $connection_args = "(interface: - Host:$this->cfg->dbhost - " . - "DBName: $this->cfg->dbname - User: $this->cfg->dbuser) "; - $msg = sprintf(lang_get('CTS_connect_to_database_fails'),$connection_args); - tLog($msg . $result['dbms_msg'], 'ERROR'); - } - elseif ($this->cfg->dbtype == 'mysql') - { - if ($this->cfg->dbcharset == 'UTF-8') - { - $r = $this->dbConnection->exec_query("SET CHARACTER SET utf8"); - $r = $this->dbConnection->exec_query("SET NAMES utf8"); - $r = $this->dbConnection->exec_query("SET collation_connection = 'utf8_general_ci'"); - } - else - { - $r = $this->dbConnection->exec_query("SET CHARACTER SET " . $this->cfg->dbcharset); - $r = $this->dbConnection->exec_query("SET NAMES ". $this->cfg->dbcharset); - } - } - - $this->connected = $result['status'] ? true : false; - - return $this->connected; - } - - /** - * State of connection to CTS - * - * @return bool returns true if connection with CTS is established, false else - * - **/ - function isConnected() - { - - return ($this->connected && - ((!$this->interfaceViaDB ) || is_object($this->dbConnection)) ? 1 : 0); - } - - /** - * Closes the db connection (if any) - * - **/ - function disconnect() - { - if ($this->isConnected() && $this->interfaceViaDB) - { - $this->dbConnection->close(); - } - $this->connected = false; - $this->dbConnection = null; - } - - /** - * default implementation for generating a link to the codetracking page for viewing - * the code with the given id in a new page - * - * @param mixed project_key - * @param mixed repository_name - * @param mixed code_path - * - * @return string returns a complete HTML HREF to view the code (if found in db) - * - **/ - function buildViewCodeLink($project_key, $repository_name, $code_path, $opt=null) - { - $branch_name = null; - $commit_id = null; - if (isset($opt['branch'])) - { - $branch_name = $opt['branch']; - } - if (isset($opt['commit_id'])) - { - $commit_id = $opt['commit_id']; - } - $link = ""; - - $link .= $code_path; - - $link .= ""; - - $ret = new stdClass(); - $ret->link = $link; - $ret->op = true; - - return $ret; - } - - /** - * returns the URL which should be displayed for entering code links - * - * @return string returns a complete URL - * - **/ - function getEnterCodeURL() - { - return $this->cfg->uricreate; - } - - - /** - * Returns URL to the codetracking page for viewing ticket - * - * @param mixed project_key - * @param mixed repository_name - * @param mixed code_path - * @param mixed branch_name - * - * @return string - **/ - function buildViewCodeURL($project_key, $repository_name, $code_path, $branch_name=null, $commit_id=null) - { - $codeURL = $this->cfg->uriview . $project_key . '/repos/' . $repository_name . - '/browse/' . $code_path; - - //commit_id has priority over branch name - if (!is_null($commit_id)) - { - $codeURL .= '?at=' . $commit_id; - } - else if (!is_null($branch_name)) - { - $codeURL .= '?at=refs%2Fheads%2F' . $branch_name; - } - - return $codeURL; - } - - // How to Force Extending class to define this STATIC method ? - // KO abstract public static function getCfgTemplate(); - public static function getCfgTemplate() - { - throw new RuntimeException("Unimplemented - YOU must implement it in YOUR interface Class"); - } - - /** - * - **/ - public static function checkEnv() - { - $ret = array(); - $ret['status'] = true; - $ret['msg'] = 'OK'; - return $ret; - } - +tlCharSet = config_get('charset'); + $this->guiCfg = array( + 'use_decoration' => true + ); // add [] on summary and statusHTMLString + $this->name = $name; + + if ($this->setCfg($config)) { + // useful only for integration via DB + if (! property_exists($this->cfg, 'dbcharset')) { + $this->cfg->dbcharset = $this->tlCharSet; + } + $this->connect(); + } else { + $this->connected = false; + } + } + + /** + */ + public function getCfg() + { + return $this->cfg; + } + + /** + */ + public function setCfg($xmlString) + { + $msg = null; + $signature = 'Source:' . __METHOD__; + + // check for empty string + if (strlen(trim($xmlString)) == 0) { + // Bye,Bye + $msg = " - Code tracker:$this->name - XML Configuration seems to be empty - please check"; + tLog(__METHOD__ . $msg, 'ERROR'); + return false; + } + + $xmlCfg = " " . $xmlString; + libxml_use_internal_errors(true); + try { + $this->cfg = simplexml_load_string($xmlCfg); + if (! $this->cfg) { + $msg = $signature . " - Failure loading XML STRING\n"; + foreach (libxml_get_errors() as $error) { + $msg .= "\t" . $error->message; + } + } + } catch (Exception $e) { + $msg = $signature . " - Exception loading XML STRING\n"; + $msg .= 'Message: ' . $e->getMessage(); + } + + if (! ($retval = is_null($msg))) { + tLog(__METHOD__ . $msg, 'ERROR'); + } + + // From + // http://php.net/manual/it/function.unserialize.php#112823 + // + // After PHP 5.3 an object made by + // SimpleXML_Load_String() cannot be serialized. + // An attempt to do so will result in a run-time + // failure, throwing an exception. + // + // If you store such an object in $_SESSION, + // you will get a post-execution error that says this: + // Fatal error: Uncaught exception 'Exception' + // with message 'Serialization of 'SimpleXMLElement' + // is not allowed' in [no active file]:0 + // Stack trace: #0 {main} thrown in [no active file] + // on line 0 + // + // !!!!! The entire contents of the session will be lost. + // http://stackoverflow.com/questions/1584725/quickly-convert-simplexmlobject-to-stdclass + $this->cfg = json_decode(json_encode($this->cfg)); + return $retval; + } + + /** + */ + public function getMyInterface() + { + return $this->cfg->interfacePHP; + } + + /** + * establishes the database connection to the codetracking system + * + * @return bool returns true if the db connection was established and the + * db could be selected, false else + * + */ + public function connect() + { + if (is_null($this->cfg->dbhost) || is_null($this->cfg->dbuser)) { + return false; + } + + // cast everything to string in order to avoid issues + // @20140604 someone has been issues trying to connect to JIRA on MSSQL + $this->cfg->dbtype = strtolower((string) $this->cfg->dbtype); + $this->cfg->dbhost = (string) $this->cfg->dbhost; + $this->cfg->dbuser = (string) $this->cfg->dbuser; + $this->cfg->dbpassword = (string) $this->cfg->dbpassword; + $this->cfg->dbname = (string) $this->cfg->dbname; + + $this->dbConnection = new database($this->cfg->dbtype); + $result = $this->dbConnection->connect(false, $this->cfg->dbhost, + $this->cfg->dbuser, $this->cfg->dbpassword, $this->cfg->dbname); + + if (! $result['status']) { + $this->dbConnection = null; + $connection_args = "(interface: - Host:$this->cfg->dbhost - " . + "DBName: $this->cfg->dbname - User: $this->cfg->dbuser) "; + $msg = sprintf(lang_get('CTS_connect_to_database_fails'), + $connection_args); + tLog($msg . $result['dbms_msg'], 'ERROR'); + } elseif ($this->cfg->dbtype == 'mysql') { + if ($this->cfg->dbcharset == 'UTF-8') { + $this->dbConnection->exec_query("SET CHARACTER SET utf8"); + $this->dbConnection->exec_query("SET NAMES utf8"); + $this->dbConnection->exec_query( + "SET collation_connection = 'utf8_general_ci'"); + } else { + $this->dbConnection->exec_query( + "SET CHARACTER SET " . $this->cfg->dbcharset); + $this->dbConnection->exec_query( + "SET NAMES " . $this->cfg->dbcharset); + } + } + + $this->connected = $result['status'] ? true : false; + + return $this->connected; + } + + /** + * State of connection to CTS + * + * @return bool returns true if connection with CTS is established, false else + * + */ + public function isConnected() + { + return $this->connected && + ((! $this->interfaceViaDB) || is_object($this->dbConnection)) ? 1 : 0; + } + + /** + * Closes the db connection (if any) + */ + public function disconnect() + { + if ($this->isConnected() && $this->interfaceViaDB) { + $this->dbConnection->close(); + } + $this->connected = false; + $this->dbConnection = null; + } + + /** + * default implementation for generating a link to the codetracking page for viewing + * the code with the given id in a new page + * + * @param + * mixed project_key + * @param + * mixed repository_name + * @param + * mixed code_path + * + * @return string returns a complete HTML HREF to view the code (if found in db) + * + */ + public function buildViewCodeLink($project_key, $repository_name, $code_path, + $opt = null) + { + $branch_name = null; + $commit_id = null; + if (isset($opt['branch'])) { + $branch_name = $opt['branch']; + } + if (isset($opt['commit_id'])) { + $commit_id = $opt['commit_id']; + } + $link = ""; + + $link .= $code_path; + + $link .= ""; + + $ret = new stdClass(); + $ret->link = $link; + $ret->op = true; + + return $ret; + } + + /** + * returns the URL which should be displayed for entering code links + * + * @return string returns a complete URL + * + */ + public function getEnterCodeURL() + { + return $this->cfg->uricreate; + } + + /** + * Returns URL to the codetracking page for viewing ticket + * + * @param + * mixed project_key + * @param + * mixed repository_name + * @param + * mixed code_path + * @param + * mixed branch_name + * + * @return string + */ + public function buildViewCodeURL($project_key, $repository_name, $code_path, + $branch_name = null, $commit_id = null) + { + $codeURL = $this->cfg->uriview . $project_key . '/repos/' . + $repository_name . '/browse/' . $code_path; + + // commit_id has priority over branch name + if (! is_null($commit_id)) { + $codeURL .= '?at=' . $commit_id; + } elseif (! is_null($branch_name)) { + $codeURL .= '?at=refs%2Fheads%2F' . $branch_name; + } + + return $codeURL; + } + + // How to Force Extending class to define this STATIC method ? + // KO abstract public static function getCfgTemplate(); + public static function getCfgTemplate() + { + throw new RuntimeException( + "Unimplemented - YOU must implement it in YOUR interface Class"); + } + + /** + */ + public static function checkEnv() + { + $ret = array(); + $ret['status'] = true; + $ret['msg'] = 'OK'; + return $ret; + } } diff --git a/lib/codetrackerintegration/code_testing/gitlab/test.php b/lib/codetrackerintegration/code_testing/gitlab/test.php new file mode 100644 index 0000000000..98ba4d47dd --- /dev/null +++ b/lib/codetrackerintegration/code_testing/gitlab/test.php @@ -0,0 +1,75 @@ +\n" . "{$token}\n" . + "{$gitlabUrl}\n" . + "{$gitlabUrl}api/4.0/\n" . + "{$projectID}\n" . + "{$path}\n" . "\n"; + +echo '

    '; +echo "Testing gitlab rest "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$system = new gitlabrestInterface(18, $cfg, 'pandora'); +$cfgObj = $system->getCfg(); + +$params = [ + 'id' => $cfgObj->projectidentifier, + 'path' => $cfgObj->testscriptpath +]; +$client = $system->getAPIClient(); + +echo '
     - getRepoFilesTreeFlat
    '; + +// First Steps First level +$params['recursive'] = false; +$params['itemType'] = 'tree'; +$main = $client->getRepoFilesTreeFlat($params); + +var_dump($main); + +$all = []; +foreach ($main as $path) { + $params['itemType'] = 'blob'; + $params['recursive'] = true; + $params['path'] = $path; + + $all[$path] = $client->getRepoFilesTreeFlat($params); +} + +var_dump($all); + +echo '
    '; diff --git a/lib/codetrackerintegration/stashrestInterface.class.php b/lib/codetrackerintegration/stashrestInterface.class.php index b201e02ece..37bd73a698 100755 --- a/lib/codetrackerintegration/stashrestInterface.class.php +++ b/lib/codetrackerintegration/stashrestInterface.class.php @@ -1,428 +1,366 @@ -name = $name; - $this->interfaceViaDB = false; - - if($this->setCfg($config) && $this->checkCfg()) - { - $this->completeCfg(); - $this->connect(); - $this->guiCfg = array('use_decoration' => true); - } - } - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/'; // be sure no double // at end - - if( !property_exists($this->cfg,'uriapi') ) - { - $this->cfg->uriapi = $base . 'rest/api/1.0/'; - } - - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'projects/'; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base . ''; - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * returns the URL which should be displayed for entering code links - * - * @return string returns a complete URL - * - **/ - function getEnterCodeURL() - { - return $this->cfg->uricreate . 'projects'; - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - try - { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $this->stashCfg = array('username' => (string)trim($this->cfg->username), - 'password' => (string)trim($this->cfg->password), - 'host' => (string)trim($this->cfg->uriapi)); - - $this->stashCfg['proxy'] = config_get('proxy'); - if( !is_null($this->stashCfg['proxy']) ) - { - if( is_null($this->stashCfg['proxy']->host) ) - { - $this->stashCfg['proxy'] = null; - } - } - - - $this->APIClient = new StashApi\Stash($this->stashCfg); - - $this->connected = $this->APIClient->testLogin(); - if($this->connected && ($this->cfg->projectkey != self::NOPROJECTKEY)) - { - // Now check if can get info about the project, to understand - // if at least it exists. - $pk = trim((string)$this->cfg->projectkey); - $this->APIClient->getProject($pk); - } - } - catch(Exception $e) - { - $this->connected = false; - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - /** - * - */ - public function getProject($projectKey) - { - try - { - return $this->APIClient->getProject($projectKey); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getProjects() - { - try - { - return $this->APIClient->getProjects(); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getProjectsForHTMLSelect() - { - $ret = null; - $projList = $this->getProjects(); - if (property_exists($projList, 'values')) - { - $ret = $this->objectAttrToKeyName($projList->values); - } - return $ret; - } - - /** - * - */ - public function getRepos($projectKey) - { - try - { - return $this->APIClient->getRepos($projectKey); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getReposForHTMLSelect($projectKey) - { - $ret = null; - $repoList = $this->getRepos($projectKey); - if (property_exists($repoList, 'values')) - { - $ret = $this->objectAttrToIDName($repoList->values, 'slug','name'); - } - return $ret; - } - - /** - * - */ - public function getRepoContent($projectKey,$repoName,$path='',$branch='',$commit_id='',$type=false) - { - try - { - return $this->APIClient->getRepoContent($projectKey,$repoName,$path,$branch,$commit_id,$type); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getRepoContentForHTMLSelect($projectKey,$repoName,$path='',$branch='',$type=false) - { - $ret = null; - $contentList = $this->getRepoContent($projectKey,$repoName,$path,$branch,'',$type); - if (property_exists($contentList, 'children') && property_exists($contentList->children, 'values')) - { - if ($path != '' && substr($path,-1) != "/") - { - $path .= "/"; - } - foreach($contentList->children->values as $elem) - { - $tmpName = $elem->path->toString; - $slashPos = strpos($elem->path->toString, '/'); - if ($slashPos !== false) - { - $tmpName = substr($tmpName, 0, $slashPos); - } - $ret[$tmpName] = array($elem->type,$path); - } - } - return $ret; - } - - /** - * - */ - public function getBranches($projectKey,$repoName) - { - try - { - return $this->APIClient->getBranches($projectKey,$repoName); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getBranchesForHTMLSelect($projectKey,$repoName) - { - $ret = null; - $branchList = $this->getBranches($projectKey,$repoName); - if (property_exists($branchList, 'values')) - { - $ret = $this->objectAttrToIDName($branchList->values, 'displayId','displayId'); - } - return $ret; - } - - /** - * - */ - public function getCommits($projectKey,$repoName,$branchName=null) - { - try - { - return $this->APIClient->getCommits($projectKey,$repoName,$branchName); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getCommitsForHTMLSelect($projectKey,$repoName,$branchName) - { - $ret = null; - $commitList = $this->getCommits($projectKey,$repoName,$branchName); - if (property_exists($commitList, 'values')) - { - $dateFormats = config_get('locales_date_format'); - - $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; - $localesDateFormat = $dateFormats[$locale]; - - foreach($commitList->values as $elem) - { - $ret[$elem->id] = $elem->displayId . ' (' . strftime($localesDateFormat, - ($elem->authorTimestamp / 1000)) . '): ' . $elem->message; - } - } - return $ret; - } - - /** - * - * - */ - private function objectAttrToKeyName($attrSet) - { - $ret = null; - if(!is_null($attrSet)) - { - $ic = count($attrSet); - for($idx=0; $idx < $ic; $idx++) - { - $ret[$attrSet[$idx]->key] = $attrSet[$idx]->name . " (" . $attrSet[$idx]->key . ")"; - } - } - return $ret; - } - - /** - * - * - */ - private function objectAttrToIDName($attrSet,$id='id',$name='name') - { - $ret = null; - if(!is_null($attrSet)) - { - $ic = count($attrSet); - for($idx=0; $idx < $ic; $idx++) - { - $ret[$attrSet[$idx]->$id] = $attrSet[$idx]->$name; - } - } - return $ret; - } - - /** - * - * - */ - private function objectAttrToIDNameKey($attrSet) - { - $ret = null; - if(!is_null($attrSet)) - { - $ic = count($attrSet); - for($idx=0; $idx < $ic; $idx++) - { - $ret[$attrSet[$idx]->id] = $attrSet[$idx]->name . " (" . $attrSet[$idx]->key . ")"; - } - } - return $ret; - } - - - - - - /** - * - * @author uwe_kirst@mentor.com> - **/ - public static function getCfgTemplate() - { - $tpl = "\n" . - "\n" . - "STASH LOGIN NAME\n" . - "STASH PASSWORD\n" . - "https://testlink.atlassian.net/\n" . - "https://testlink.atlassian.net/rest/api/1.0/\n" . - "https://testlink.atlassian.net/projects/\n" . - "STASH PROJECT KEY\n" . - "\n"; - return $tpl; - } - - /** - * - * - **/ - function checkCfg() - { - $status_ok = true; - if( property_exists($this->cfg, 'projectkey') ) - { - $pk = trim((string)($this->cfg->projectkey)); - if($pk == '') - { - $status_ok = false; - $msg = __CLASS__ . ' - Empty configuration: '; - } - } - else - { - // this is oK if user only wants to LINK issues - $this->cfg->projectkey = self::NOPROJECTKEY; - } - - if(!$status_ok) - { - tLog(__METHOD__ . ' / ' . $msg , 'ERROR'); - } - return $status_ok; - } - - +name = $name; + $this->interfaceViaDB = false; + + if ($this->setCfg($config) && $this->checkCfg()) { + $this->completeCfg(); + $this->connect(); + $this->guiCfg = array( + 'use_decoration' => true + ); + } + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; // be sure no double // at end + + if (! property_exists($this->cfg, 'uriapi')) { + $this->cfg->uriapi = $base . 'rest/api/1.0/'; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'projects/'; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base . ''; + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * returns the URL which should be displayed for entering code links + * + * @return string returns a complete URL + * + */ + public function getEnterCodeURL() + { + return $this->cfg->uricreate . 'projects'; + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + * + */ + public function connect() + { + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $this->stashCfg = array( + 'username' => (string) trim($this->cfg->username), + 'password' => (string) trim($this->cfg->password), + 'host' => (string) trim($this->cfg->uriapi) + ); + + $this->stashCfg['proxy'] = config_get('proxy'); + if (! is_null($this->stashCfg['proxy']) && + is_null($this->stashCfg['proxy']->host)) { + $this->stashCfg['proxy'] = null; + } + + $this->APIClient = new StashApi\Stash($this->stashCfg); + + $this->connected = $this->APIClient->testLogin(); + if ($this->connected && + ($this->cfg->projectkey != self::NOPROJECTKEY)) { + // Now check if can get info about the project, to understand + // if at least it exists. + $pk = trim((string) $this->cfg->projectkey); + $this->APIClient->getProject($pk); + } + } catch (Exception $e) { + $this->connected = false; + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getProject($projectKey) + { + try { + return $this->APIClient->getProject($projectKey); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getProjects() + { + try { + return $this->APIClient->getProjects(); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getProjectsForHTMLSelect() + { + $ret = null; + $projList = $this->getProjects(); + if (property_exists($projList, 'values')) { + $ret = $this->objectAttrToKeyName($projList->values); + } + return $ret; + } + + /** + */ + public function getRepos($projectKey) + { + try { + return $this->APIClient->getRepos($projectKey); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getReposForHTMLSelect($projectKey) + { + $ret = null; + $repoList = $this->getRepos($projectKey); + if (property_exists($repoList, 'values')) { + $ret = $this->objectAttrToIDName($repoList->values, 'slug', 'name'); + } + return $ret; + } + + /** + */ + public function getRepoContent($projectKey, $repoName, $path = '', + $branch = '', $commit_id = '', $type = false) + { + try { + return $this->APIClient->getRepoContent($projectKey, $repoName, + $path, $branch, $commit_id, $type); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getRepoContentForHTMLSelect($projectKey, $repoName, + $path = '', $branch = '', $type = false) + { + $ret = null; + $contentList = $this->getRepoContent($projectKey, $repoName, $path, + $branch, '', $type); + if (property_exists($contentList, 'children') && + property_exists($contentList->children, 'values')) { + if ($path != '' && substr($path, - 1) != "/") { + $path .= "/"; + } + foreach ($contentList->children->values as $elem) { + $tmpName = $elem->path->toString; + $slashPos = strpos($elem->path->toString, '/'); + if ($slashPos !== false) { + $tmpName = substr($tmpName, 0, $slashPos); + } + $ret[$tmpName] = array( + $elem->type, + $path + ); + } + } + return $ret; + } + + /** + */ + public function getBranches($projectKey, $repoName) + { + try { + return $this->APIClient->getBranches($projectKey, $repoName); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getBranchesForHTMLSelect($projectKey, $repoName) + { + $ret = null; + $branchList = $this->getBranches($projectKey, $repoName); + if (property_exists($branchList, 'values')) { + $ret = $this->objectAttrToIDName($branchList->values, 'displayId', + 'displayId'); + } + return $ret; + } + + /** + */ + public function getCommits($projectKey, $repoName, $branchName = null) + { + try { + return $this->APIClient->getCommits($projectKey, $repoName, + $branchName); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getCommitsForHTMLSelect($projectKey, $repoName, $branchName) + { + $ret = null; + $commitList = $this->getCommits($projectKey, $repoName, $branchName); + if (property_exists($commitList, 'values')) { + $dateFormats = config_get('locales_date_format'); + + $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; + $localesDateFormat = $dateFormats[$locale]; + + foreach ($commitList->values as $elem) { + $ret[$elem->id] = $elem->displayId . ' (' . + @strftime($localesDateFormat, + ($elem->authorTimestamp / 1000)) . '): ' . $elem->message; + } + } + return $ret; + } + + /** + */ + private function objectAttrToKeyName($attrSet) + { + $ret = null; + if (! is_null($attrSet)) { + $ic = count($attrSet); + for ($idx = 0; $idx < $ic; $idx ++) { + $ret[$attrSet[$idx]->key] = $attrSet[$idx]->name . " (" . + $attrSet[$idx]->key . ")"; + } + } + return $ret; + } + + /** + */ + private function objectAttrToIDName($attrSet, $id = 'id', $name = 'name') + { + $ret = null; + if (! is_null($attrSet)) { + $ic = count($attrSet); + for ($idx = 0; $idx < $ic; $idx ++) { + $ret[$attrSet[$idx]->$id] = $attrSet[$idx]->$name; + } + } + return $ret; + } + + /** + */ + private function objectAttrToIDNameKey($attrSet) + { + $ret = null; + if (! is_null($attrSet)) { + $ic = count($attrSet); + for ($idx = 0; $idx < $ic; $idx ++) { + $ret[$attrSet[$idx]->id] = $attrSet[$idx]->name . " (" . + $attrSet[$idx]->key . ")"; + } + } + return $ret; + } + + /** + * + * @author uwe_kirst@mentor.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "STASH LOGIN NAME\n" . + "STASH PASSWORD\n" . + "https://testlink.atlassian.net/\n" . + "https://testlink.atlassian.net/rest/api/1.0/\n" . + "https://testlink.atlassian.net/projects/\n" . + "STASH PROJECT KEY\n" . "\n"; + } + + /** + */ + private function checkCfg() + { + $status_ok = true; + if (property_exists($this->cfg, 'projectkey')) { + $pk = trim((string) ($this->cfg->projectkey)); + if ($pk == '') { + $status_ok = false; + $msg = __CLASS__ . ' - Empty configuration: '; + } + } else { + // this is oK if user only wants to LINK issues + $this->cfg->projectkey = self::NOPROJECTKEY; + } + + if (! $status_ok) { + tLog(__METHOD__ . ' / ' . $msg, 'ERROR'); + } + return $status_ok; + } } diff --git a/lib/codetrackers/codeTrackerCommands.class.php b/lib/codetrackers/codeTrackerCommands.class.php index 0cd8d9ec09..84e026e60c 100755 --- a/lib/codetrackers/codeTrackerCommands.class.php +++ b/lib/codetrackers/codeTrackerCommands.class.php @@ -1,274 +1,261 @@ -db=$dbHandler; - $this->codeTrackerMgr = new tlCodeTracker($dbHandler); - $this->entitySpec = $this->codeTrackerMgr->getEntitySpec(); - - $this->grants=new stdClass(); - $this->grants->canManage = false; - - $this->guiOpWhiteList = array_flip(array('checkConnection','create','edit','delete','doCreate', - 'doUpdate','doDelete')); - } - - function setTemplateCfg($cfg) - { - $this->templateCfg = $cfg; - } - - function getGuiOpWhiteList() - { - return $this->guiOpWhiteList; - } - - /** - * - * - */ - function initGuiBean(&$argsObj, $caller) - { - $obj = new stdClass(); - $obj->action = $caller; - $obj->typeDomain = $this->codeTrackerMgr->getTypes(); - $obj->canManage = $argsObj->currentUser->hasRight($this->db,'codetracker_management'); - $obj->user_feedback = array('type' => '', 'message' => ''); - - $obj->l18n = init_labels(array('codetracker_management' => null, - 'btn_save' => null, 'create' => null, - 'edit' => null, - 'checkConnection' => 'btn_check_connection', - 'codetracker_deleted' => null)); - - // we experiment on way to get Action Description for GUI using __FUNCTION__ - $obj->l18n['doUpdate'] = $obj->l18n['edit']; - $obj->l18n['doCreate'] = $obj->l18n['create']; - $obj->l18n['doDelete'] = ''; - $obj->main_descr = $obj->l18n['codetracker_management']; - $obj->action_descr = ucfirst($obj->l18n[$caller]); - $obj->connectionStatus = ''; - - switch($caller) - { - case 'delete': - case 'doDelete': - $obj->submit_button_label = ''; - break; - - default: - $obj->submit_button_label = $obj->l18n['btn_save']; - break; - } - - return $obj; - } - - /** - * - * - */ - function create(&$argsObj,$request,$caller=null) - { - $guiObj = $this->initGuiBean($argsObj,(is_null($caller) ? __FUNCTION__ : $caller)); - $templateCfg = templateConfiguration('codeTrackerEdit'); - $guiObj->template = $templateCfg->default_template; - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'codetracker_management'); - - $guiObj->item = array('id' => 0); - $dummy = ''; - foreach($this->entitySpec as $property => $type) - { - $guiObj->item[$property] = ($type == 'int') ? 0 :''; - } - return $guiObj; - } - - /** - * - * - */ - function doCreate(&$argsObj,$request) - { - $guiObj = $this->create($argsObj,$request,__FUNCTION__); - - // Checks are centralized on create() - $ct = new stdClass(); - foreach($this->entitySpec as $property => $type) - { - $ct->$property = $argsObj->$property; - } - - // Save user input. - // This will be useful if create() will fail, to present values again on GUI - $guiObj->item = (array)$ct; - - $op = $this->codeTrackerMgr->create($ct); - if($op['status_ok']) - { - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->template = "codeTrackerView.php"; - } - else - { - $templateCfg = templateConfiguration('codeTrackerEdit'); - $guiObj->template=$templateCfg->default_template; - $guiObj->user_feedback['message'] = $op['msg']; - } - - return $guiObj; - } - - - - - /* - function: edit - - args: - - returns: - - */ - function edit(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - $templateCfg = templateConfiguration('codeTrackerEdit'); - $guiObj->template = $templateCfg->default_template; - - $guiObj->item = $this->codeTrackerMgr->getByID($argsObj->id); - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'codetracker_management'); - return $guiObj; - } - - - /* - function: doUpdate - - args: - - returns: - - */ - function doUpdate(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - $ct = new stdClass(); - $ct->id = $argsObj->id; - foreach($this->entitySpec as $property => $type) - { - $ct->$property = $argsObj->$property; - } - - // Save user input. - // This will be useful if create() will fail, to present values again on GUI - $guiObj->item = (array)$ct; - - $op = $this->codeTrackerMgr->update($ct); - if( $op['status_ok'] ) - { - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->template = "codeTrackerView.php"; - } - else - { - $guiObj->user_feedback['message'] = $op['msg']; - $guiObj->template = null; - } - - return $guiObj; - } - - /** - * - * - */ - function doDelete(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - // get minimal info for user feedback before deleting - // $ct = $this->codeTrackerMgr->getByID($argsObj->id); - $op = $this->codeTrackerMgr->delete($argsObj->id); - - // http://www.plus2net.com/php_tutorial/variables.php - //if($op['status_ok']) - //{ - // $msg = sprintf($this->guiObj->l18n['codetracker_deleted'],$ct['name']); - //} - //else - //{ - // $msg = $op['msg']; - //} - //$_SESSION['codeTrackerView.user_feedback'] = $msg; - - $guiObj->action = 'doDelete'; - $guiObj->template = "codeTrackerView.php?"; - - return $guiObj; - } - - /** - * - */ - function checkConnection(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'codetracker_management'); - - $tplCfg = templateConfiguration('codeTrackerEdit'); - $guiObj->template = $tplCfg->default_template; - - if( $argsObj->id > 0 ) - { - $cxx = $this->codeTrackerMgr->getByID($argsObj->id); - $guiObj->item['id'] = $cxx['id']; - } - else - { - $guiObj->operation = 'doCreate'; - $guiObj->item['id'] = 0; - } - - $guiObj->item['name'] = $argsObj->name; - $guiObj->item['type'] = $argsObj->type; - $guiObj->item['cfg'] = $argsObj->cfg; - $guiObj->item['implementation'] = - $this->codeTrackerMgr->getImplementationForType($argsObj->type); - - $class2create = $guiObj->item['implementation']; - $cts = new $class2create($argsObj->type,$argsObj->cfg,$argsObj->name); - $guiObj->connectionStatus = $cts->isConnected() ? 'ok' : 'ko'; - - return $guiObj; - } - -} // end class +db = $dbHandler; + $this->codeTrackerMgr = new tlCodeTracker($dbHandler); + $this->entitySpec = $this->codeTrackerMgr->getEntitySpec(); + + $this->grants = new stdClass(); + $this->grants->canManage = false; + + $this->guiOpWhiteList = array_flip( + array( + 'checkConnection', + 'create', + 'edit', + 'delete', + 'doCreate', + 'doUpdate', + 'doDelete' + )); + } + + public function setTemplateCfg($cfg) + { + $this->templateCfg = $cfg; + } + + public function getGuiOpWhiteList() + { + return $this->guiOpWhiteList; + } + + /** + */ + public function initGuiBean(&$argsObj, $caller) + { + $obj = new stdClass(); + $obj->action = $caller; + $obj->typeDomain = $this->codeTrackerMgr->getTypes(); + $obj->canManage = $argsObj->currentUser->hasRight($this->db, + 'codetracker_management'); + $obj->user_feedback = array( + 'type' => '', + 'message' => '' + ); + + $obj->l18n = init_labels( + array( + 'codetracker_management' => null, + 'btn_save' => null, + 'create' => null, + 'edit' => null, + 'checkConnection' => 'btn_check_connection', + 'codetracker_deleted' => null + )); + + // we experiment on way to get Action Description for GUI using __FUNCTION__ + $obj->l18n['doUpdate'] = $obj->l18n['edit']; + $obj->l18n['doCreate'] = $obj->l18n['create']; + $obj->l18n['doDelete'] = ''; + $obj->main_descr = $obj->l18n['codetracker_management']; + $obj->action_descr = ucfirst($obj->l18n[$caller]); + $obj->connectionStatus = ''; + + switch ($caller) { + case 'delete': + case 'doDelete': + $obj->submit_button_label = ''; + break; + + default: + $obj->submit_button_label = $obj->l18n['btn_save']; + break; + } + + return $obj; + } + + /** + */ + public function create(&$argsObj, $request, $caller = null) + { + $guiObj = $this->initGuiBean($argsObj, + (is_null($caller) ? __FUNCTION__ : $caller)); + $templateCfg = templateConfiguration('codeTrackerEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'codetracker_management'); + + $guiObj->item = array( + 'id' => 0 + ); + foreach ($this->entitySpec as $property => $type) { + $guiObj->item[$property] = ($type == 'int') ? 0 : ''; + } + return $guiObj; + } + + /** + */ + public function doCreate(&$argsObj, $request) + { + $guiObj = $this->create($argsObj, $request, __FUNCTION__); + + // Checks are centralized on create() + $ct = new stdClass(); + foreach ($this->entitySpec as $property => $type) { + $ct->$property = $argsObj->$property; + } + + // Save user input. + // This will be useful if create() will fail, to present values again on GUI + $guiObj->item = (array) $ct; + + $op = $this->codeTrackerMgr->create($ct); + if ($op['status_ok']) { + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->template = "codeTrackerView.php"; + } else { + $templateCfg = templateConfiguration('codeTrackerEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->user_feedback['message'] = $op['msg']; + } + + return $guiObj; + } + + /* + * function: edit + * + * args: + * + * returns: + * + */ + public function edit(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $templateCfg = templateConfiguration('codeTrackerEdit'); + $guiObj->template = $templateCfg->default_template; + + $guiObj->item = $this->codeTrackerMgr->getByID($argsObj->id); + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'codetracker_management'); + return $guiObj; + } + + /* + * function: doUpdate + * + * args: + * + * returns: + * + */ + public function doUpdate(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $ct = new stdClass(); + $ct->id = $argsObj->id; + foreach ($this->entitySpec as $property => $type) { + $ct->$property = $argsObj->$property; + } + + // Save user input. + // This will be useful if create() will fail, to present values again on GUI + $guiObj->item = (array) $ct; + + $op = $this->codeTrackerMgr->update($ct); + if ($op['status_ok']) { + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->template = "codeTrackerView.php"; + } else { + $guiObj->user_feedback['message'] = $op['msg']; + $guiObj->template = null; + } + + return $guiObj; + } + + /** + */ + public function doDelete(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $this->codeTrackerMgr->delete($argsObj->id); + + $guiObj->action = 'doDelete'; + $guiObj->template = "codeTrackerView.php?"; + + return $guiObj; + } + + /** + */ + public function checkConnection(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'codetracker_management'); + + $tplCfg = templateConfiguration('codeTrackerEdit'); + $guiObj->template = $tplCfg->default_template; + + if ($argsObj->id > 0) { + $cxx = $this->codeTrackerMgr->getByID($argsObj->id); + $guiObj->item['id'] = $cxx['id']; + } else { + $guiObj->operation = 'doCreate'; + $guiObj->item['id'] = 0; + } + + $guiObj->item['name'] = $argsObj->name; + $guiObj->item['type'] = $argsObj->type; + $guiObj->item['cfg'] = $argsObj->cfg; + $guiObj->item['implementation'] = $this->codeTrackerMgr->getImplementationForType( + $argsObj->type); + + $class2create = $guiObj->item['implementation']; + $cts = new $class2create($argsObj->type, $argsObj->cfg, $argsObj->name); + $guiObj->connectionStatus = $cts->isConnected() ? 'ok' : 'ko'; + + return $guiObj; + } +} diff --git a/lib/codetrackers/codeTrackerEdit.php b/lib/codetrackers/codeTrackerEdit.php index b88a3efa0b..85e0156aca 100755 --- a/lib/codetrackers/codeTrackerEdit.php +++ b/lib/codetrackers/codeTrackerEdit.php @@ -1,185 +1,210 @@ -doAction; -$op = null; -if(method_exists($commandMgr,$pFn)) -{ - $op = $commandMgr->$pFn($args,$_REQUEST); -} - -renderGui($db,$args,$gui,$op,$templateCfg); - - - - -/** - */ -function renderGui(&$dbHandler,&$argsObj,$guiObj,$opObj,$templateCfg) -{ - $smartyObj = new TLSmarty(); - $renderType = 'none'; - - // key: gui action - // value: next gui action (used to set value of action button on gui) - $actionOperation = array('create' => 'doCreate', 'edit' => 'doUpdate', - 'doDelete' => '', 'doCreate' => 'doCreate', - 'doUpdate' => 'doUpdate', - 'checkConnection' => 'doCreate'); - - if($argsObj->id > 0) - { - $actionOperation['checkConnection'] = 'doUpdate'; - } - - // Get rendering type and set variable for template - switch($argsObj->doAction) - { - case "edit": - case "create": - case "doDelete": - case "doCreate": - case "doUpdate": - case "checkConnection": - $key2loop = get_object_vars($opObj); - foreach($key2loop as $key => $value) - { - $guiObj->$key = $value; - } - $guiObj->operation = $actionOperation[$argsObj->doAction]; - - $renderType = 'redirect'; - $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; - $pos = strpos($tpl, '.php'); - if($pos === false) - { - $tplDir = (!isset($opObj->template_dir) || is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; - $tpl = $tplDir . $tpl; - $renderType = 'template'; - } - break; - } - - // execute rendering - // new dBug($tpl); - // new dBug($guiObj); - - switch($renderType) - { - case 'template': - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($tpl); - break; - - case 'redirect': - header("Location: {$tpl}"); - exit(); - break; - - default: - break; - } -} - -/** - * - */ -function initScript(&$dbHandler) { - $mgr = new codeTrackerCommands($dbHandler); - $args = init_args(array('doAction' => $mgr->getGuiOpWhiteList())); - $gui = initializeGui($dbHandler,$args,$mgr); - return array($args,$gui,$mgr); -} - -/** - * @return object returns the arguments for the page - */ -function init_args($whiteList) { - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - - $iParams = array("id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,20), - "name" => array(tlInputParameter::STRING_N,0,100), - "cfg" => array(tlInputParameter::STRING_N,0,2000), - "type" => array(tlInputParameter::INT_N)); - - R_PARAMS($iParams,$args); - - // sanitize via whitelist - foreach($whiteList as $inputKey => $allowedValues) { - if( property_exists($args,$inputKey) ) { - if( !isset($allowedValues[$args->$inputKey]) ) { - $msg = "Input parameter $inputKey - white list validation failure - " . - "Value:" . $args->$inputKey . " - " . - "File: " . basename(__FILE__) . " - Function: " . __FUNCTION__ ; - tLog($msg,'ERROR'); - throw new Exception($msg); - } - } - } - - $args->currentUser = $_SESSION['currentUser']; - - return $args; -} - - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,&$commandMgr) -{ - $gui = new stdClass(); - $gui->main_descr = ''; - $gui->action_descr = ''; - $gui->user_feedback = array('type' => '', 'message' => ''); - $gui->mgt_view_events = $argsObj->currentUser->hasRight($dbHandler,'mgt_view_events'); - - // get affected test projects - $gui->testProjectSet = null; - if($argsObj->id > 0) - { - - // just to fix erroneous test project delete - $dummy = $commandMgr->codeTrackerMgr->getLinks($argsObj->id,array('getDeadLinks' => true)); - if( !is_null($dummy) ) - { - foreach($dummy as $key => $elem) - { - $commandMgr->codeTrackerMgr->unlink($argsObj->id,$key); - } - } - - // Now get good info - $gui->testProjectSet = $commandMgr->codeTrackerMgr->getLinks($argsObj->id); - } - return $gui; -} - - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'codetracker_management'); -} +doAction; +$op = null; +if (method_exists($commandMgr, $pFn)) { + $op = $commandMgr->$pFn($args, $_REQUEST); +} + +renderGui($db, $args, $gui, $op, $templateCfg); + +/** + */ +function renderGui(&$dbHandler, &$argsObj, $guiObj, $opObj, $templateCfg) +{ + $smartyObj = new TLSmarty(); + $renderType = 'none'; + + // key: gui action + // value: next gui action (used to set value of action button on gui) + $actionOperation = array( + 'create' => 'doCreate', + 'edit' => 'doUpdate', + 'doDelete' => '', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate', + 'checkConnection' => 'doCreate' + ); + + if ($argsObj->id > 0) { + $actionOperation['checkConnection'] = 'doUpdate'; + } + + // Get rendering type and set variable for template + switch ($argsObj->doAction) { + case "edit": + case "create": + case "doDelete": + case "doCreate": + case "doUpdate": + case "checkConnection": + $key2loop = get_object_vars($opObj); + foreach ($key2loop as $key => $value) { + $guiObj->$key = $value; + } + $guiObj->operation = $actionOperation[$argsObj->doAction]; + + $renderType = 'redirect'; + $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; + $pos = strpos($tpl, '.php'); + if ($pos === false) { + $tplDir = (! isset($opObj->template_dir) || + is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; + $tpl = $tplDir . $tpl; + $renderType = 'template'; + } + break; + } + + // execute rendering + // new dBug($tpl); + // new dBug($guiObj); + + switch ($renderType) { + case 'template': + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($tpl); + break; + + case 'redirect': + header("Location: {$tpl}"); + exit(); + break; + + default: + break; + } +} + +/** + */ +function initScript(&$dbHandler) +{ + $mgr = new codeTrackerCommands($dbHandler); + $args = initArgs(array( + 'doAction' => $mgr->getGuiOpWhiteList() + )); + $gui = initializeGui($dbHandler, $args, $mgr); + return array( + $args, + $gui, + $mgr + ); +} + +/** + * + * @return object returns the arguments for the page + */ +function initArgs($whiteList) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + + $iParams = array( + "id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + "name" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "cfg" => array( + tlInputParameter::STRING_N, + 0, + 2000 + ), + "type" => array( + tlInputParameter::INT_N + ) + ); + + R_PARAMS($iParams, $args); + + // sanitize via whitelist + foreach ($whiteList as $inputKey => $allowedValues) { + if (property_exists($args, $inputKey) && + ! isset($allowedValues[$args->$inputKey])) { + $msg = "Input parameter $inputKey - white list validation failure - " . + "Value:" . $args->$inputKey . " - " . "File: " . + basename(__FILE__) . " - Function: " . __FUNCTION__; + tLog($msg, 'ERROR'); + throw new Exception($msg); + } + } + + $args->currentUser = $_SESSION['currentUser']; + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj, &$commandMgr) +{ + $gui = new stdClass(); + $gui->main_descr = ''; + $gui->action_descr = ''; + $gui->user_feedback = array( + 'type' => '', + 'message' => '' + ); + $gui->mgt_view_events = $argsObj->currentUser->hasRight($dbHandler, + 'mgt_view_events'); + + // get affected test projects + $gui->testProjectSet = null; + if ($argsObj->id > 0) { + + // just to fix erroneous test project delete + $dummy = $commandMgr->codeTrackerMgr->getLinks($argsObj->id, + array( + 'getDeadLinks' => true + )); + if (! is_null($dummy)) { + foreach ($dummy as $key => $elem) { + $commandMgr->codeTrackerMgr->unlink($argsObj->id, $key); + } + } + + // Now get good info + $gui->testProjectSet = $commandMgr->codeTrackerMgr->getLinks( + $argsObj->id); + } + return $gui; +} + +/** + * + * @param database $db + * the database connection handle + * @param tlUser $user + * the current active user + * + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'codetracker_management'); +} ?> diff --git a/lib/codetrackers/codeTrackerView.php b/lib/codetrackers/codeTrackerView.php index e06cccf170..6f611059cd 100755 --- a/lib/codetrackers/codeTrackerView.php +++ b/lib/codetrackers/codeTrackerView.php @@ -1,71 +1,76 @@ -items = $codeTrackerMgr->getAll(array('output' => 'add_link_count', 'checkEnv' => true)); -$gui->canManage = $args->currentUser->hasRight($db,"codetracker_management"); -$gui->user_feedback = $args->user_feedback; - -if($args->id > 0) -{ - $gui->items[$args->id]['connection_status'] = $codeTrackerMgr->checkConnection($args->id) ? 'ok' : 'ko'; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/** - * @return object returns the arguments for the page - */ -function init_args() -{ - $args = new stdClass(); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - - if( $args->tproject_id == 0 ) - { - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - } - $args->currentUser = $_SESSION['currentUser']; - - $args->user_feedback = array('type' => '', 'message' => ''); - - // only way I've found in order to give feedback for delete - // need to undertand if we really need/want to do all this mess - // $args->user_feedback = array('type' => '', 'message' => ''); - // if( isset($_SESSION['codeTrackerView.user_feedback']) ) - // { - // $args->user_feedback = array('type' => '', 'message' => $_SESSION['codeTrackerView.user_feedback']); - // unset($_SESSION['codeTrackerView.user_feedback']); - // } - - $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; - return $args; -} - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"codetracker_view") || $user->hasRight($db,"codetracker_management"); +items = $codeTrackerMgr->getAll( + array( + 'output' => 'add_link_count', + 'checkEnv' => true + )); +$gui->canManage = $args->currentUser->hasRight($db, "codetracker_management"); +$gui->user_feedback = $args->user_feedback; + +if ($args->id > 0) { + $gui->items[$args->id]['connection_status'] = $codeTrackerMgr->checkConnection( + $args->id) ? 'ok' : 'ko'; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return object returns the arguments for the page + */ +function initArgs() +{ + $args = new stdClass(); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + + if ($args->tproject_id == 0) { + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + } + $args->currentUser = $_SESSION['currentUser']; + + $args->user_feedback = array( + 'type' => '', + 'message' => '' + ); + + // only way I've found in order to give feedback for delete + // need to undertand if we really need/want to do all this mess + // $args->user_feedback = array('type' => '', 'message' => ''); + // if( isset($_SESSION['codeTrackerView.user_feedback']) ) + // { + // $args->user_feedback = array('type' => '', 'message' => $_SESSION['codeTrackerView.user_feedback']); + // unset($_SESSION['codeTrackerView.user_feedback']); + $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; + return $args; +} + +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "codetracker_view") || + $user->hasRight($db, "codetracker_management"); } diff --git a/lib/events/eventinfo.php b/lib/events/eventinfo.php index 90da87153e..b6af0beca0 100644 --- a/lib/events/eventinfo.php +++ b/lib/events/eventinfo.php @@ -1,66 +1,68 @@ -id) -{ - $event = new tlEvent($args->id); - if ($event->readFromDB($db,tlEvent::TLOBJ_O_GET_DETAIL_TRANSACTION) >= tl::OK) - { - $user = new tlUser($event->userID); - if ($user->readFromDB($db) < tl::OK) - { - $user = null; - } - } - else - { - $event = null; - } -} - -$smarty = new TLSmarty(); -$smarty->assign("event",$event); -$smarty->assign("user",$user); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - * @return object returns the arguments of the page - */ -function init_args() -{ - $iParams = array("id" => array(tlInputParameter::INT_N)); - $args = new stdClass(); - P_PARAMS($iParams,$args); - - return $args; -} - -/** - * Checks the user rights for viewing the page - * - * @param $db resource the database connection handle - * @param $user tlUser the object of the current user - * - * @return boolean return true if the page can be viewed, false if not - */ -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,"mgt_view_events")) ? true : false; -} \ No newline at end of file +id) { + $event = new tlEvent($args->id); + if ($event->readFromDB($db, tlEvent::TLOBJ_O_GET_DETAIL_TRANSACTION) >= + tl::OK) { + $user = new tlUser($event->userID); + if ($user->readFromDB($db) < tl::OK) { + $user = null; + } + } else { + $event = null; + } +} + +$smarty = new TLSmarty(); +$smarty->assign("event", $event); +$smarty->assign("user", $user); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return object returns the arguments of the page + */ +function initArgs() +{ + $iParams = array( + "id" => array( + tlInputParameter::INT_N + ) + ); + $args = new stdClass(); + P_PARAMS($iParams, $args); + + return $args; +} + +/** + * Checks the user rights for viewing the page + * + * @param $db resource + * the database connection handle + * @param $user tlUser + * the object of the current user + * + * @return boolean return true if the page can be viewed, false if not + */ +function checkRights(&$db, &$user) +{ + return ($user->hasRight($db, "mgt_view_events")) ? true : false; +} diff --git a/lib/events/eventviewer.php b/lib/events/eventviewer.php index c67b8bf32a..565fbc3736 100644 --- a/lib/events/eventviewer.php +++ b/lib/events/eventviewer.php @@ -1,276 +1,331 @@ -doAction) -{ - case 'clear': - // Ability to delete events from selected class from event logs - $g_tlLogger->deleteEventsFor($args->logLevel); - if( is_null($args->logLevel) ) - { - logAuditEvent(TLS("audit_all_events_deleted",$args->currentUser->login),"DELETE",null,"events"); - } - else - { - $logLevelVerbose = null; - foreach( $args->logLevel as $code ) - { - $logLevelVerbose[] = $gui->logLevels[$code]; - } - $logLevelVerbose = implode(',',$logLevelVerbose); - logAuditEvent(TLS("audit_events_with_level_deleted",$args->currentUser->login,$logLevelVerbose),"DELETE",null,"events"); - } - - // reset filters after clearing events - $args->logLevel = null; - $gui->selectedLogLevels = array(); - $gui->selectedTesters = array(); - $gui->startDate = null; - $gui->endDate = null; - break; - - case 'filter': - default: - $filters = getFilters($args,$date_format_cfg); - break; -} - -$gui->events = $g_tlLogger->getEventsFor($args->logLevel,$args->object_id ? $args->object_id : null, - $args->object_type ? $args->object_type : null,null,500,$filters->startTime, - $filters->endTime,$filters->users); - -if (count($gui->events) > 0) -{ - $table = buildExtTable($gui, $show_icon, $charset); - if (!is_null($table)) - { - $gui->tableSet[] = $table; - } -} -else -{ - $gui->warning_msg = lang_get("no_events"); -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - * @return object returns the arguments of the page - */ -function init_args() -{ - $iParams = array("startDate" => array("POST",tlInputParameter::STRING_N,0,10), - "endDate" => array("POST",tlInputParameter::STRING_N,0,10), - "doAction" => array("POST",tlInputParameter::STRING_N,0,100), - "object_id" => array("REQUEST",tlInputParameter::INT_N), - "object_type" => array("REQUEST",tlInputParameter::STRING_N,0,15), - "logLevel" => array("POST",tlInputParameter::ARRAY_INT), - "testers" => array("REQUEST",tlInputParameter::ARRAY_INT)); - - $args = new stdClass(); - I_PARAMS($iParams,$args); - $args->currentUser = $_SESSION['currentUser']; - return $args; -} - -/** - * Checks the user rights for viewing the page - * - * @param $db resource the database connection handle - * @param $user tlUser the object of the current user - * - * @return boolean return true if the page can be viewed, false if not - */ -function checkRights(&$db,&$user,$action) -{ - $checkStatus = $user->hasRight($db,"mgt_view_events"); - if( !$checkStatus ) - { - $iParams = array("doAction" => array(tlInputParameter::STRING_N,0,100)); - $rParams = R_PARAMS($iParams); - if ($rParams["doAction"] == 'clear') - { - $checkStatus = $user->hasRight($db,'events_mgt'); - } - } - return $checkStatus; -} - - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj) -{ - $gui = new stdClass(); - $gui->logLevels = array(tlLogger::AUDIT => lang_get("log_level_AUDIT"), - tlLogger::ERROR => lang_get("log_level_ERROR"), - tlLogger::WARNING => lang_get("log_level_WARNING"), - tlLogger::INFO => lang_get("log_level_INFO"), - tlLogger::DEBUG => lang_get("log_level_DEBUG"), - tlLogger::L18N => lang_get("log_level_L18N")); - - $gui->allusers = tlUser::getAll($dbHandler); // THIS IS AN OVERKILL because get ALL USER OBJECTS - $gui->testers = getUsersForHtmlOptions($dbHandler,null,null,true,$gui->allusers); - $gui->users = getUsersForHtmlOptions($dbHandler); - $gui->users[0] = false; - - $gui->startDate=$argsObj->startDate; - $gui->endDate=$argsObj->endDate; - $gui->object_id=$argsObj->object_id; - $gui->object_type=$argsObj->object_type; - - $gui->selectedLogLevels = ($argsObj->logLevel ? array_values($argsObj->logLevel) : array()); - $gui->selectedTesters = ($argsObj->testers ? array_values($argsObj->testers) : array()); - - $gui->canDelete = $argsObj->currentUser->hasRight($dbHandler,"events_mgt"); - - $gui->warning_msg = ""; - $gui->tableSet = null; - - return $gui; -} - - -/** - * - * - */ -function getFilters(&$argsObj=null,$dateFormat=null) -{ - $filters = new stdClass(); - $filters->startTime = null; - $filters->endTime = null; - $filters->users = null; - - if( !is_null($argsObj) ) - { - if ($argsObj->startDate != "") - { - $date_array = split_localized_date($argsObj->startDate, $dateFormat); - if ($date_array != null) { - // convert localized date to date that strtotime understands -> en_US: m/d/Y: - $filters->startTime = strToTime($date_array['month'] . "/" . $date_array['day']. "/" .$date_array['year']); - } - if ($filters->startTime == "") - { - $filters->startTime = null; - } - } - - if ($argsObj->endDate != "") - { - $date_array = split_localized_date($argsObj->endDate, $dateFormat); - if ($date_array != null) { - // convert localized date to date that strtotime understands -> en_US: m/d/Y: - // end time must end at selected day at 23:59:59 - $filters->endTime = strToTime($date_array['month'] . "/" . $date_array['day']. "/" . - $date_array['year'] . ", 23:59:59"); - } - if (!$filters->endTime) - { - $filters->endTime = null; - } - } - - if (!is_null($argsObj->testers)) - { - $filters->users = implode(",",$argsObj->testers); - if (!$filters->users) - { - $filters->users = null; - } - } - } - - return $filters; -} - -/** - * - * - */ -function buildExtTable($gui,$show_icon,$charset) -{ - $table = null; - if(count($gui->events) > 0) - { - $columns = array(); - $columns[] = array('title_key' => 'th_timestamp', 'width' => 15); - $columns[] = array('title_key' => 'th_loglevel', 'width' => 15); - $columns[] = array('title_key' => 'th_user', 'width' => 15); - $columns[] = array('title_key' => 'th_event_description','type' => 'text'); - $columns[] = array('title_key' => 'th_transaction', 'width' => 15, 'hidden' => 'true'); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - foreach ($gui->events as $event_key => $event) - { - $transactionID = $event->transactionID; - - $rowData = array(); - - // necessary as localize_dateOrTimeStamp expects 2nd parameter to pass by reference - $dummy = null; - // use html comment to sort properly by timestamp - $rowData[] = "" . - localize_dateOrTimeStamp(null, $dummy, 'timestamp_format',$event->timestamp); - - $rowData[] = $event->getlogLevel(); - - if (isset($event->userID) && $event->userID != false && isset($gui->users[$event->userID])) { - $rowData[] = $gui->users[$event->userID]; - } else { - $rowData[] = lang_get("not_aplicable"); - } - $description = htmlentities($event->description, ENT_QUOTES, $charset); - $rowData[] = "" . - "dbID});\" style=\"cursor: hand; cursor: pointer;\">" . - " " . - $description; - - $rowData[] = $event->transactionID; - - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_eventviewer'); - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - - $table->setGroupByColumnName(lang_get('th_loglevel')); - $table->setSortByColumnName(lang_get('th_timestamp')); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->toolbarExpandCollapseGroupsButton = true; - $table->toolbarShowAllColumnsButton = true; - } - return($table); +doAction) { + case 'clear': + // Ability to delete events from selected class from event logs + $g_tlLogger->deleteEventsFor($args->logLevel); + if (is_null($args->logLevel)) { + logAuditEvent( + TLS("audit_all_events_deleted", $args->currentUser->login), + "DELETE", null, "events"); + } else { + $logLevelVerbose = null; + foreach ($args->logLevel as $code) { + $logLevelVerbose[] = $gui->logLevels[$code]; + } + $logLevelVerbose = implode(',', $logLevelVerbose); + logAuditEvent( + TLS("audit_events_with_level_deleted", $args->currentUser->login, + $logLevelVerbose), "DELETE", null, "events"); + } + + // reset filters after clearing events + $args->logLevel = null; + $gui->selectedLogLevels = array(); + $gui->selectedTesters = array(); + $gui->startDate = null; + $gui->endDate = null; + break; + + case 'filter': + default: + $filters = getFilters($args, $date_format_cfg); + break; +} + +$gui->events = $g_tlLogger->getEventsFor($args->logLevel, + $args->object_id ? $args->object_id : null, + $args->object_type ? $args->object_type : null, null, 500, + $filters->startTime, $filters->endTime, $filters->users); + +if (! empty($gui->events)) { + $table = buildExtTable($gui, $show_icon, $charset); + if (! is_null($table)) { + $gui->tableSet[] = $table; + } +} else { + $gui->warning_msg = lang_get("no_events"); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return object returns the arguments of the page + */ +function initArgs() +{ + $iParams = array( + "startDate" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 10 + ), + "endDate" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 10 + ), + "doAction" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 100 + ), + "object_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "object_type" => array( + "REQUEST", + tlInputParameter::STRING_N, + 0, + 15 + ), + "logLevel" => array( + "POST", + tlInputParameter::ARRAY_INT + ), + "testers" => array( + "REQUEST", + tlInputParameter::ARRAY_INT + ) + ); + + $args = new stdClass(); + I_PARAMS($iParams, $args); + $args->currentUser = $_SESSION['currentUser']; + return $args; +} + +/** + * Checks the user rights for viewing the page + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the object of the current user + * + * @return boolean return true if the page can be viewed, false if not + */ +function checkRights(&$db, &$user) +{ + $checkStatus = $user->hasRight($db, "mgt_view_events"); + if (! $checkStatus) { + $iParams = array( + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + $rParams = R_PARAMS($iParams); + if ($rParams["doAction"] == 'clear') { + $checkStatus = $user->hasRight($db, 'events_mgt'); + } + } + return $checkStatus; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $gui = new stdClass(); + $gui->logLevels = array( + tlLogger::AUDIT => lang_get("log_level_AUDIT"), + tlLogger::ERROR => lang_get("log_level_ERROR"), + tlLogger::WARNING => lang_get("log_level_WARNING"), + tlLogger::INFO => lang_get("log_level_INFO"), + tlLogger::DEBUG => lang_get("log_level_DEBUG"), + tlLogger::L18N => lang_get("log_level_L18N") + ); + + $gui->allusers = tlUser::getAll($dbHandler); // THIS IS AN OVERKILL because get ALL USER OBJECTS + $gui->testers = getUsersForHtmlOptions($dbHandler, null, null, true, + $gui->allusers); + $gui->users = getUsersForHtmlOptions($dbHandler); + $gui->users[0] = false; + + $gui->startDate = $argsObj->startDate; + $gui->endDate = $argsObj->endDate; + $gui->object_id = $argsObj->object_id; + $gui->object_type = $argsObj->object_type; + + $gui->selectedLogLevels = ($argsObj->logLevel ? array_values( + $argsObj->logLevel) : array()); + $gui->selectedTesters = ($argsObj->testers ? array_values($argsObj->testers) : array()); + + $gui->canDelete = $argsObj->currentUser->hasRight($dbHandler, "events_mgt"); + + $gui->warning_msg = ""; + $gui->tableSet = null; + + return $gui; +} + +/** + * + * @param stdClass $argsObj + * @param string $dateFormat + * @return stdClass + */ +function getFilters(&$argsObj = null, $dateFormat = null) +{ + $filters = new stdClass(); + $filters->startTime = null; + $filters->endTime = null; + $filters->users = null; + + if (! is_null($argsObj)) { + if ($argsObj->startDate != "") { + $date_array = split_localized_date($argsObj->startDate, $dateFormat); + if ($date_array != null) { + // convert localized date to date that strtotime understands -> en_US: m/d/Y: + $filters->startTime = strToTime( + $date_array['month'] . "/" . $date_array['day'] . "/" . + $date_array['year']); + } + if ($filters->startTime == "") { + $filters->startTime = null; + } + } + + if ($argsObj->endDate != "") { + $date_array = split_localized_date($argsObj->endDate, $dateFormat); + if ($date_array != null) { + // convert localized date to date that strtotime understands -> en_US: m/d/Y: + // end time must end at selected day at 23:59:59 + $filters->endTime = strToTime( + $date_array['month'] . "/" . $date_array['day'] . "/" . + $date_array['year'] . ", 23:59:59"); + } + if (! $filters->endTime) { + $filters->endTime = null; + } + } + + if (! is_null($argsObj->testers)) { + $filters->users = implode(",", $argsObj->testers); + if (! $filters->users) { + $filters->users = null; + } + } + } + + return $filters; +} + +/** + * + * @param stdClass $gui + * @param string $show_icon + * @param string $charset + * @return tlExtTable + */ +function buildExtTable($gui, $show_icon, $charset) +{ + $table = null; + if (! empty($gui->events)) { + $columns = array(); + $columns[] = array( + 'title_key' => 'th_timestamp', + 'width' => 15 + ); + $columns[] = array( + 'title_key' => 'th_loglevel', + 'width' => 15 + ); + $columns[] = array( + 'title_key' => 'th_user', + 'width' => 15 + ); + $columns[] = array( + 'title_key' => 'th_event_description', + 'type' => 'text' + ); + $columns[] = array( + 'title_key' => 'th_transaction', + 'width' => 15, + 'hidden' => 'true' + ); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + foreach ($gui->events as $event) { + $rowData = array(); + + // necessary as localize_dateOrTimeStamp expects 2nd parameter to pass by reference + $dummy = null; + // use html comment to sort properly by timestamp + $rowData[] = "" . + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', + $event->timestamp); + + $rowData[] = $event->getlogLevel(); + + if (isset($event->userID) && $event->userID && + isset($gui->users[$event->userID])) { + $rowData[] = $gui->users[$event->userID]; + } else { + $rowData[] = lang_get("not_aplicable"); + } + $description = htmlentities($event->description, ENT_QUOTES, + $charset); + $rowData[] = "" . + "dbID});\" style=\"cursor: hand; cursor: pointer;\">" . + " " . $description; + + $rowData[] = $event->transactionID; + + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, 'tl_table_eventviewer'); + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + + $table->setGroupByColumnName(lang_get('th_loglevel')); + $table->setSortByColumnName(lang_get('th_timestamp')); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->toolbarExpandCollapseGroupsButton = true; + $table->toolbarShowAllColumnsButton = true; + } + return $table; } diff --git a/lib/execute/bugAdd.php b/lib/execute/bugAdd.php index 9ba33f811e..f56b3863df 100644 --- a/lib/execute/bugAdd.php +++ b/lib/execute/bugAdd.php @@ -1,342 +1,422 @@ -user_action == 'create' || $args->user_action == 'doCreate') && - $gui->issueTrackerCfg->tlCanCreateIssue) { - // get matadata - $gui->issueTrackerMetaData = getIssueTrackerMetaData($its); - - switch($args->user_action) { - case 'create': - $dummy = generateIssueText($db,$args,$its); - $gui->bug_summary = $dummy->summary; - break; - - case 'doCreate': - $args->direct_link = getDirectLinkToExec($db,$args->exec_id); - - $dummy = generateIssueText($db,$args,$its); - $gui->bug_summary = $dummy->summary; - - $aop = array('addLinkToTL' => $args->addLinkToTL, - 'addLinkToTLPrintView' => $args->addLinkToTLPrintView); - - $ret = addIssue($db,$args,$its,$aop); - $gui->issueTrackerCfg->tlCanCreateIssue = $ret['status_ok']; - $gui->msg = $ret['msg']; - break; - - } -} -else if($args->user_action == 'link' || $args->user_action == 'add_note') { - // Well do not think is very elegant to check for $args->bug_id != "" - // to understand if user has pressed ADD Button - if(!is_null($issueT) && $args->bug_id != "") { - $l18n = init_labels(array("error_wrong_BugID_format" => null,"error_bug_does_not_exist_on_bts" => null)); - - switch($args->user_action) { - case 'link': - $gui->msg = $l18n["error_wrong_BugID_format"]; - if ($its->checkBugIDSyntax($args->bug_id)) { - if ($its->checkBugIDExistence($args->bug_id)) { - if (write_execution_bug($db,$args->exec_id, $args->bug_id,$args->tcstep_id)) { - $gui->msg = lang_get("bug_added"); - logAuditEvent(TLS("audit_executionbug_added",$args->bug_id),"CREATE",$args->exec_id,"executions"); - - // blank notes will not be added :). - if($gui->issueTrackerCfg->tlCanAddIssueNote) { - $hasNotes = (strlen($gui->bug_notes) > 0); - // will do call to update issue Notes - if($args->addLinkToTL || $args->addLinkToTLPrintView) { - $args->direct_link = getDirectLinkToExec($db,$args->exec_id); - - $aop = array('addLinkToTL' => $args->addLinkToTL, - 'addLinkToTLPrintView' => $args->addLinkToTLPrintView); - - $dummy = generateIssueText($db,$args,$its,$aop); - $gui->bug_notes = $dummy->description; - } - - if( $args->addLinkToTL || $args->addLinkToTLPrintView || - $hasNotes ) { - $opt = new stdClass(); - $opt->reporter = $args->user->login; - $opt->reporter_email = trim($args->user->emailAddress); - if( '' == $opt->reporter_email ) { - $opt->reporter_email = $opt->reporter; - } - - $its->addNote($args->bug_id,$gui->bug_notes,$opt); - } - } - } - } else { - $gui->msg = sprintf($l18n["error_bug_does_not_exist_on_bts"],$gui->bug_id); - } - } - break; - - case 'add_note': - // blank notes will not be added :). - $gui->msg = ''; - if($gui->issueTrackerCfg->tlCanAddIssueNote && (strlen($gui->bug_notes) > 0) ) { - $opt = new stdClass(); - $opt->reporter = $args->user->login; - $opt->reporter_email = trim($args->user->emailAddress); - if( '' == $opt->reporter_email ) { - $opt->reporter_email = $opt->reporter; - } - - $ope = $its->addNote($args->bug_id,$gui->bug_notes,$opt); - - if( !$ope['status_ok'] ) { - $gui->msg = $ope['msg']; - } - } - break; - } - } -} -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); - -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/** - * - * - */ -function initEnv(&$dbHandler) -{ - $uaWhiteList = array(); - $uaWhiteList['elements'] = array('link','create','doCreate','add_note'); - $uaWhiteList['lenght'] = array(); - foreach ($uaWhiteList['elements'] as $xmen) { - $uaWhiteList['lenght'][] = strlen($xmen); - } - $user_action['maxLengh'] = max($uaWhiteList['lenght']); - $user_action['minLengh'] = min($uaWhiteList['lenght']); - - $iParams = array("exec_id" => array("GET",tlInputParameter::INT_N), - "bug_id" => array("REQUEST",tlInputParameter::STRING_N), - "tproject_id" => array("REQUEST",tlInputParameter::INT_N), - "tplan_id" => array("REQUEST",tlInputParameter::INT_N), - "tcversion_id" => array("REQUEST",tlInputParameter::INT_N), - "bug_notes" => array("POST",tlInputParameter::STRING_N), - "issueType" => array("POST",tlInputParameter::INT_N), - "issuePriority" => array("POST",tlInputParameter::INT_N), - "artifactComponent" => array("POST",tlInputParameter::ARRAY_INT), - "artifactVersion" => array("POST",tlInputParameter::ARRAY_INT), - "user_action" => array("REQUEST",tlInputParameter::STRING_N, - $user_action['minLengh'],$user_action['maxLengh']), - "addLinkToTL" => array("POST",tlInputParameter::CB_BOOL), - "addLinkToTLPrintView" => array("POST",tlInputParameter::CB_BOOL), - "tcstep_id" => array("REQUEST",tlInputParameter::INT_N),); - - $args = new stdClass(); - I_PARAMS($iParams,$args); - if ($args->exec_id) { - $_SESSION['bugAdd_execID'] = intval($args->exec_id); - } - else { - $args->exec_id = intval(isset($_SESSION['bugAdd_execID']) ? $_SESSION['bugAdd_execID'] : 0); - } - - // it's a checkbox - $args->addLinkToTL = isset($_REQUEST['addLinkToTL']); - $args->addLinkToTLPrintView = isset($_REQUEST['addLinkToTLPrintView']); - - $args->user = $_SESSION['currentUser']; - - $gui = new stdClass(); - $cfg = config_get('exec_cfg'); - $gui->addLinkToTLChecked = $cfg->exec_mode->addLinkToTLChecked; - $gui->addLinkToTLPrintViewChecked = - $cfg->exec_mode->addLinkToTLPrintViewChecked; - - - switch($args->user_action) { - case 'create': - case 'doCreate': - $gui->pageTitle = lang_get('create_issue'); - break; - - case 'add_note': - $gui->pageTitle = lang_get('add_issue_note'); - break; - - case 'link': - default: - $gui->pageTitle = lang_get('title_bug_add'); - break; - } - - $gui->msg = ''; - $gui->bug_summary = ''; - $gui->tproject_id = $args->tproject_id; - $gui->tplan_id = $args->tplan_id; - $gui->tcversion_id = $args->tcversion_id; - $gui->tcstep_id = $args->tcstep_id; - - $gui->user_action = $args->user_action; - $gui->bug_id = $args->bug_id; - - - - // --------------------------------------------------------------- - // Special processing - list($itObj,$itCfg) = getIssueTracker($dbHandler,$args,$gui); - $itsDefaults = $itObj->getCfg(); - - $gui->issueType = $args->issueType; - $gui->issuePriority = $args->issuePriority; - $gui->artifactVersion = $args->artifactVersion; - $gui->artifactComponent = $args->artifactComponent; - $gui->issueTrackerCfg->editIssueAttr = $itsDefaults->userinteraction; - - // This code has been verified with JIRA REST - if ($itsDefaults->userinteraction == 0) { - $singleVal = array('issuetype' => 'issueType', - 'issuepriority' => 'issuePriority'); - foreach ($singleVal as $kj => $attr) { - $gui->$attr = $itsDefaults->$kj; - } - - $multiVal = array('version' => 'artifactVersion', - 'component' => 'artifactComponent'); - foreach ($multiVal as $kj => $attr) { - $gui->$attr = (array)$itsDefaults->$kj; - } - } - $gui->allIssueAttrOnScreen = 1; - - // Second access to user input - $bug_summary['minLengh'] = 1; - $bug_summary['maxLengh'] = $itObj->getBugSummaryMaxLength(); - - $inputCfg = array("bug_summary" => array("POST",tlInputParameter::STRING_N, - $bug_summary['minLengh'],$bug_summary['maxLengh'])); - - I_PARAMS($inputCfg,$args); - - $args->bug_id = trim($args->bug_id); - switch ($args->user_action) { - case 'create': - if( $args->bug_id == '' && $args->exec_id > 0) { - $map = get_execution($dbHandler,$args->exec_id); - $args->bug_notes = $map[0]['notes']; - } - break; - - case 'doCreate': - case 'add_note': - case 'link': - default: - break; - } - - $gui->bug_notes = $args->bug_notes = trim($args->bug_notes); - - $args->basehref = $_SESSION['basehref']; - $tables = tlObjectWithDB::getDBTables(array('testplans')); - $sql = ' SELECT api_key FROM ' . $tables['testplans'] . - ' WHERE id=' . intval($args->tplan_id); - - $rs = $dbHandler->get_recordset($sql); - $args->tplan_apikey = $rs[0]['api_key']; - - return array($args,$gui,$itObj,$itCfg); -} - - -/** - * - */ -function getIssueTracker(&$dbHandler,$argsObj,&$guiObj) -{ - $its = null; - $tprojectMgr = new testproject($dbHandler); - $info = $tprojectMgr->get_by_id($argsObj->tproject_id); - - $guiObj->issueTrackerCfg = new stdClass(); - $guiObj->issueTrackerCfg->createIssueURL = null; - $guiObj->issueTrackerCfg->VerboseID = ''; - $guiObj->issueTrackerCfg->VerboseType = ''; - $guiObj->issueTrackerCfg->bugIDMaxLength = 0; - $guiObj->issueTrackerCfg->bugSummaryMaxLength = 100; // MAGIC - $guiObj->issueTrackerCfg->tlCanCreateIssue = false; - $guiObj->issueTrackerCfg->tlCanAddIssueNote = true; - - if($info['issue_tracker_enabled']) { - $it_mgr = new tlIssueTracker($dbHandler); - $issueTrackerCfg = $it_mgr->getLinkedTo($argsObj->tproject_id); - - if( !is_null($issueTrackerCfg) ) { - $its = $it_mgr->getInterfaceObject($argsObj->tproject_id); - - $guiObj->issueTrackerCfg->VerboseType = $issueTrackerCfg['verboseType']; - $guiObj->issueTrackerCfg->VerboseID = $issueTrackerCfg['issuetracker_name']; - $guiObj->issueTrackerCfg->bugIDMaxLength = $its->getBugIDMaxLength(); - $guiObj->issueTrackerCfg->createIssueURL = $its->getEnterBugURL(); - $guiObj->issueTrackerCfg->bugSummaryMaxLength = $its->getBugSummaryMaxLength(); - - $guiObj->issueTrackerCfg->tlCanCreateIssue = method_exists($its,'addIssue'); - $guiObj->issueTrackerCfg->tlCanAddIssueNote = method_exists($its,'addNote'); - } - } - return array($its,$issueTrackerCfg); -} - -/** - * - */ -function getDirectLinkToExec(&$dbHandler,$execID) -{ - $tbk = array('executions','testplan_tcversions'); - $tbl = tlObjectWithDB::getDBTables($tbk); - $sql = " SELECT EX.id,EX.build_id,EX.testplan_id," . - " EX.tcversion_id,TPTCV.id AS feature_id " . - " FROM {$tbl['executions']} EX " . - " JOIN {$tbl['testplan_tcversions']} TPTCV " . - " ON TPTCV.testplan_id=EX.testplan_id " . - " AND TPTCV.tcversion_id=EX.tcversion_id " . - " AND TPTCV.platform_id=EX.platform_id " . - " WHERE EX.id=" . intval($execID); - - $rs = $dbHandler->get_recordset($sql); - $rs = $rs[0]; - $dlk = trim($_SESSION['basehref'],'/') . - "/ltx.php?item=exec&feature_id=" . $rs['feature_id'] . - "&build_id=" . $rs['build_id']; - - return $dlk; -} - -/** - * Checks the user rights for viewing the page - * - * @param $db resource the database connection handle - * @param $user tlUser the object of the current user - * - * @return boolean return true if the page can be viewed, false if not - */ -function checkRights(&$db,&$user) { - $hasRights = $user->hasRight($db,"testplan_execute"); - return $hasRights; -} \ No newline at end of file +user_action == 'create' || $args->user_action == 'doCreate') && + $gui->issueTrackerCfg->tlCanCreateIssue) { + // get matadata + $gui->issueTrackerMetaData = getIssueTrackerMetaData($its); + + switch ($args->user_action) { + case 'create': + $dummy = generateIssueText($db, $args, $its); + $gui->bug_summary = $dummy->summary; + break; + + case 'doCreate': + $args->direct_link = getDirectLinkToExec($db, $args->exec_id); + + $dummy = generateIssueText($db, $args, $its); + $gui->bug_summary = $dummy->summary; + + $aop = array( + 'addLinkToTL' => $args->addLinkToTL, + 'addLinkToTLPrintView' => $args->addLinkToTLPrintView + ); + + $ret = addIssue($db, $args, $its, $aop); + $gui->issueTrackerCfg->tlCanCreateIssue = $ret['status_ok']; + $gui->msg = $ret['msg']; + break; + } +} elseif ($args->user_action == 'link' || $args->user_action == 'add_note') { + // Well do not think is very elegant to check for $args->bug_id != "" + // to understand if user has pressed ADD Button + if (! is_null($issueT) && $args->bug_id != "") { + $l18n = init_labels( + array( + "error_wrong_BugID_format" => null, + "error_bug_does_not_exist_on_bts" => null + )); + + switch ($args->user_action) { + case 'link': + $gui->msg = $l18n["error_wrong_BugID_format"]; + if ($its->checkBugIDSyntax($args->bug_id)) { + $bugID = $its->normalizeBugID($args->bug_id); + if ($its->checkBugIDExistence($bugID)) { + if (write_execution_bug($db, $args->exec_id, $bugID, + $args->tcstep_id)) { + $gui->msg = lang_get("bug_added"); + logAuditEvent( + TLS("audit_executionbug_added", $args->bug_id), + "CREATE", $args->exec_id, "executions"); + + // blank notes will not be added :). + if ($gui->issueTrackerCfg->tlCanAddIssueNote) { + $hasNotes = (strlen($gui->bug_notes) > 0); + // will do call to update issue Notes + if ($args->addLinkToTL || + $args->addLinkToTLPrintView) { + $args->direct_link = getDirectLinkToExec( + $db, $args->exec_id); + + $aop = array( + 'addLinkToTL' => $args->addLinkToTL, + 'addLinkToTLPrintView' => $args->addLinkToTLPrintView + ); + + $dummy = generateIssueText($db, $args, $its, + $aop); + $gui->bug_notes = $dummy->description; + } + + if ($args->addLinkToTL || + $args->addLinkToTLPrintView || $hasNotes) { + $opt = new stdClass(); + $opt->reporter = $args->user->login; + $opt->reporter_email = trim( + $args->user->emailAddress); + if ('' == $opt->reporter_email) { + $opt->reporter_email = $opt->reporter; + } + + $its->addNote($bugID, $gui->bug_notes, $opt); + } + } + } + } else { + $gui->msg = sprintf( + $l18n["error_bug_does_not_exist_on_bts"], + $gui->bug_id); + } + } + break; + + case 'add_note': + // blank notes will not be added :). + $gui->msg = ''; + if ($gui->issueTrackerCfg->tlCanAddIssueNote && + (strlen($gui->bug_notes) > 0)) { + $opt = new stdClass(); + $opt->reporter = $args->user->login; + $opt->reporter_email = trim($args->user->emailAddress); + if ('' == $opt->reporter_email) { + $opt->reporter_email = $opt->reporter; + } + + $ope = $its->addNote($args->bug_id, $gui->bug_notes, $opt); + + if (! $ope['status_ok']) { + $gui->msg = $ope['msg']; + } + } + break; + } + } +} +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); + +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initEnv(&$dbHandler) +{ + $uaWhiteList = array(); + $uaWhiteList['elements'] = array( + 'link', + 'create', + 'doCreate', + 'add_note' + ); + $uaWhiteList['lenght'] = array(); + foreach ($uaWhiteList['elements'] as $xmen) { + $uaWhiteList['lenght'][] = strlen($xmen); + } + $user_action['maxLengh'] = max($uaWhiteList['lenght']); + $user_action['minLengh'] = min($uaWhiteList['lenght']); + + $iParams = array( + "exec_id" => array( + "GET", + tlInputParameter::INT_N + ), + "bug_id" => array( + "REQUEST", + tlInputParameter::STRING_N + ), + "tproject_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "tplan_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "tcversion_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "bug_notes" => array( + "POST", + tlInputParameter::STRING_N + ), + "issueType" => array( + "POST", + tlInputParameter::INT_N + ), + "issuePriority" => array( + "POST", + tlInputParameter::INT_N + ), + "artifactComponent" => array( + "POST", + tlInputParameter::ARRAY_INT + ), + "artifactVersion" => array( + "POST", + tlInputParameter::ARRAY_INT + ), + "user_action" => array( + "REQUEST", + tlInputParameter::STRING_N, + $user_action['minLengh'], + $user_action['maxLengh'] + ), + "addLinkToTL" => array( + "POST", + tlInputParameter::CB_BOOL + ), + "addLinkToTLPrintView" => array( + "POST", + tlInputParameter::CB_BOOL + ), + "tcstep_id" => array( + "REQUEST", + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + I_PARAMS($iParams, $args); + if ($args->exec_id) { + $_SESSION['bugAdd_execID'] = intval($args->exec_id); + } else { + $args->exec_id = intval( + isset($_SESSION['bugAdd_execID']) ? $_SESSION['bugAdd_execID'] : 0); + } + + // it's a checkbox + $args->addLinkToTL = isset($_REQUEST['addLinkToTL']); + $args->addLinkToTLPrintView = isset($_REQUEST['addLinkToTLPrintView']); + + $args->user = $_SESSION['currentUser']; + + $gui = new stdClass(); + $cfg = config_get('exec_cfg'); + $gui->addLinkToTLChecked = $cfg->exec_mode->addLinkToTLChecked; + $gui->addLinkToTLPrintViewChecked = $cfg->exec_mode->addLinkToTLPrintViewChecked; + + switch ($args->user_action) { + case 'create': + case 'doCreate': + $gui->pageTitle = lang_get('create_issue'); + break; + + case 'add_note': + $gui->pageTitle = lang_get('add_issue_note'); + break; + + case 'link': + default: + $gui->pageTitle = lang_get('title_bug_add'); + break; + } + + $gui->msg = ''; + $gui->bug_summary = ''; + $gui->tproject_id = $args->tproject_id; + $gui->tplan_id = $args->tplan_id; + $gui->tcversion_id = $args->tcversion_id; + $gui->tcstep_id = $args->tcstep_id; + + $gui->user_action = $args->user_action; + $gui->bug_id = $args->bug_id; + + // --------------------------------------------------------------- + // Special processing + list ($itObj, $itCfg) = getIssueTracker($dbHandler, $args, $gui); + $itsDefaults = $itObj->getCfg(); + + $gui->issueType = $args->issueType; + $gui->issuePriority = $args->issuePriority; + $gui->artifactVersion = $args->artifactVersion; + $gui->artifactComponent = $args->artifactComponent; + $gui->issueTrackerCfg->editIssueAttr = $itsDefaults->userinteraction; + + // This code has been verified with JIRA REST + if ($itsDefaults->userinteraction == 0) { + $singleVal = array( + 'issuetype' => 'issueType', + 'issuepriority' => 'issuePriority' + ); + foreach ($singleVal as $kj => $attr) { + $gui->$attr = $itsDefaults->$kj; + } + + $multiVal = array( + 'version' => 'artifactVersion', + 'component' => 'artifactComponent' + ); + foreach ($multiVal as $kj => $attr) { + $gui->$attr = (array) $itsDefaults->$kj; + } + } + $gui->allIssueAttrOnScreen = 1; + + // Second access to user input + $bug_summary['minLengh'] = 1; + $bug_summary['maxLengh'] = $itObj->getBugSummaryMaxLength(); + + $inputCfg = array( + "bug_summary" => array( + "POST", + tlInputParameter::STRING_N, + $bug_summary['minLengh'], + $bug_summary['maxLengh'] + ) + ); + + I_PARAMS($inputCfg, $args); + + $args->bug_id = trim($args->bug_id); + switch ($args->user_action) { + case 'create': + case 'link': + if ($args->bug_id == '' && $args->exec_id > 0) { + $map = get_execution($dbHandler, $args->exec_id); + $args->bug_notes = $map[0]['notes']; + } + break; + + case 'doCreate': + case 'add_note': + default: + break; + } + + $gui->bug_notes = $args->bug_notes = trim($args->bug_notes); + + $args->basehref = $_SESSION['basehref']; + $tables = tlObjectWithDB::getDBTables(array( + 'testplans' + )); + $sql = ' SELECT api_key FROM ' . $tables['testplans'] . ' WHERE id=' . + intval($args->tplan_id); + + $rs = $dbHandler->get_recordset($sql); + $args->tplan_apikey = $rs[0]['api_key']; + + return array( + $args, + $gui, + $itObj, + $itCfg + ); +} + +/** + */ +function getIssueTracker(&$dbHandler, $argsObj, &$guiObj) +{ + $its = null; + $tprojectMgr = new testproject($dbHandler); + $info = $tprojectMgr->get_by_id($argsObj->tproject_id); + + $guiObj->issueTrackerCfg = new stdClass(); + $guiObj->issueTrackerCfg->createIssueURL = null; + $guiObj->issueTrackerCfg->VerboseID = ''; + $guiObj->issueTrackerCfg->VerboseType = ''; + $guiObj->issueTrackerCfg->bugIDMaxLength = 0; + $guiObj->issueTrackerCfg->bugSummaryMaxLength = 100; // MAGIC + $guiObj->issueTrackerCfg->tlCanCreateIssue = false; + $guiObj->issueTrackerCfg->tlCanAddIssueNote = true; + + if ($info['issue_tracker_enabled']) { + $it_mgr = new tlIssueTracker($dbHandler); + $issueTrackerCfg = $it_mgr->getLinkedTo($argsObj->tproject_id); + + if (! is_null($issueTrackerCfg)) { + $its = $it_mgr->getInterfaceObject($argsObj->tproject_id); + + $guiObj->issueTrackerCfg->VerboseType = $issueTrackerCfg['verboseType']; + $guiObj->issueTrackerCfg->VerboseID = $issueTrackerCfg['issuetracker_name']; + $guiObj->issueTrackerCfg->bugIDMaxLength = $its->getBugIDMaxLength(); + $guiObj->issueTrackerCfg->createIssueURL = $its->getEnterBugURL(); + $guiObj->issueTrackerCfg->bugSummaryMaxLength = $its->getBugSummaryMaxLength(); + + $guiObj->issueTrackerCfg->tlCanCreateIssue = method_exists($its, + 'addIssue'); + $guiObj->issueTrackerCfg->tlCanAddIssueNote = method_exists($its, + 'addNote'); + } + } + return array( + $its, + $issueTrackerCfg + ); +} + +/** + */ +function getDirectLinkToExec(&$dbHandler, $execID) +{ + $tbk = array( + 'executions', + 'testplan_tcversions' + ); + $tbl = tlObjectWithDB::getDBTables($tbk); + $sql = " SELECT EX.id,EX.build_id,EX.testplan_id," . + " EX.tcversion_id,TPTCV.id AS feature_id " . + " FROM {$tbl['executions']} EX " . + " JOIN {$tbl['testplan_tcversions']} TPTCV " . + " ON TPTCV.testplan_id=EX.testplan_id " . + " AND TPTCV.tcversion_id=EX.tcversion_id " . + " AND TPTCV.platform_id=EX.platform_id " . " WHERE EX.id=" . + intval($execID); + + $rs = $dbHandler->get_recordset($sql); + $rs = $rs[0]; + return trim($_SESSION['basehref'], '/') . "/ltx.php?item=exec&feature_id=" . + $rs['feature_id'] . "&build_id=" . $rs['build_id']; +} + +/** + * Checks the user rights for viewing the page + * + * @param $db resource + * the database connection handle + * @param $user tlUser + * the object of the current user + * + * @return boolean return true if the page can be viewed, false if not + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, "testplan_execute"); +} diff --git a/lib/execute/bugDelete.php b/lib/execute/bugDelete.php index d970d12a32..8021b2cabf 100644 --- a/lib/execute/bugDelete.php +++ b/lib/execute/bugDelete.php @@ -1,81 +1,91 @@ -exec_id && $args->bug_id != "") -{ - if (write_execution_bug($db,$args->exec_id,$args->bug_id,$args->tcstep_id,true)) - { - // get audit info - $ainfo = get_execution($db,$args->exec_id,array('output' => 'audit')); - $ainfo = $ainfo[0]; - - $msg = lang_get('bugdeleting_was_ok'); - if( $ainfo['platform_name'] == '' ) - { - $auditMsg = TLS('audit_executionbug_deleted_no_platform',$args->bug_id, - $ainfo['exec_id'],$ainfo['testcase_name'], - $ainfo['testproject_name'],$ainfo['testplan_name'], - $ainfo['build_name']); - } - else - { - $auditMsg = TLS('audit_executionbug_deleted',$args->bug_id,$ainfo['exec_id'], - $ainfo['testcase_name'],$ainfo['testproject_name'], - $ainfo['testplan_name'],$ainfo['platform_name'], - $ainfo['build_name']); - } - logAuditEvent($auditMsg,"DELETE",$args->exec_id,"executions"); - } -} - -$smarty = new TLSmarty(); -$smarty->assign('msg',$msg); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - * @return object returns the arguments of the page - */ -function init_args() -{ - $args = new stdClass(); - $iParams = array("exec_id" => array("GET",tlInputParameter::INT_N), - "tcstep_id" => array("GET",tlInputParameter::INT_N), - "bug_id" => array("GET",tlInputParameter::STRING_N,0,config_get('field_size')->bug_id)); - - $pParams = I_PARAMS($iParams,$args); - $args->tproject_id = isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']; - - return $args; -} - - -/** - * Checks the user rights for viewing the page - * - * @param $db resource the database connection handle - * @param $user tlUser the object of the current user - * - * @return boolean return true if the page can be viewed, false if not - */ -function checkRights(&$db,&$user) -{ - $hasRights = $user->hasRight($db,"testplan_execute"); - return $hasRights; -} \ No newline at end of file +exec_id && $args->bug_id != "" && + write_execution_bug($db, $args->exec_id, $args->bug_id, $args->tcstep_id, + true)) { + // get audit info + $ainfo = get_execution($db, $args->exec_id, array( + 'output' => 'audit' + )); + $ainfo = $ainfo[0]; + + $msg = lang_get('bugdeleting_was_ok'); + if ($ainfo['platform_name'] == '') { + $auditMsg = TLS('audit_executionbug_deleted_no_platform', $args->bug_id, + $ainfo['exec_id'], $ainfo['testcase_name'], + $ainfo['testproject_name'], $ainfo['testplan_name'], + $ainfo['build_name']); + } else { + $auditMsg = TLS('audit_executionbug_deleted', $args->bug_id, + $ainfo['exec_id'], $ainfo['testcase_name'], + $ainfo['testproject_name'], $ainfo['testplan_name'], + $ainfo['platform_name'], $ainfo['build_name']); + } + logAuditEvent($auditMsg, "DELETE", $args->exec_id, "executions"); +} + +$smarty = new TLSmarty(); +$smarty->assign('msg', $msg); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return object returns the arguments of the page + */ +function initArgs() +{ + $args = new stdClass(); + $iParams = array( + "exec_id" => array( + "GET", + tlInputParameter::INT_N + ), + "tcstep_id" => array( + "GET", + tlInputParameter::INT_N + ), + "bug_id" => array( + "GET", + tlInputParameter::STRING_N, + 0, + config_get('field_size')->bug_id + ) + ); + + I_PARAMS($iParams, $args); + $args->tproject_id = isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']; + + return $args; +} + +/** + * Checks the user rights for viewing the page + * + * @param $db resource + * the database connection handle + * @param $user tlUser + * the object of the current user + * + * @return boolean return true if the page can be viewed, false if not + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, "testplan_execute"); +} diff --git a/lib/execute/editExecution.php b/lib/execute/editExecution.php index 1ddfc3bbda..91368d437f 100644 --- a/lib/execute/editExecution.php +++ b/lib/execute/editExecution.php @@ -1,115 +1,134 @@ -basehref,$editorCfg); -switch ($args->doAction) { - case 'edit': - break; - - case 'doUpdate': - doUpdate($db,$args,$tcase_mgr,$_REQUEST); - break; +basehref, $editorCfg); +switch ($args->doAction) { + case 'edit': + break; + + case 'doUpdate': + doUpdate($db, $args, $tcaseMgr, $_REQUEST); + break; +} +$map = get_execution($db, $args->exec_id); +$owebeditor->Value = $map[0]['notes']; + +// order on script is critic +$gui = initializeGui($args, $tcaseMgr); +$cols = intval(isset($editorCfg['cols']) ? $editorCfg['cols'] : 60); +$rows = intval(isset($editorCfg['rows']) ? $editorCfg['rows'] : 10); +$gui->notes = $owebeditor->CreateHTML($rows, $cols); +$gui->editorType = $editorCfg['type']; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function doUpdate(&$db, &$args, &$tcaseMgr, &$request) +{ + updateExecutionNotes($db, $args->exec_id, $args->notes); + + $cfield_mgr = new cfield_mgr($db); + $cfield_mgr->execution_values_to_db($request, $args->tcversion_id, + $args->exec_id, $args->tplan_id); +} + +/** + */ +function initArgs() +{ + // Take care of proper escaping when magic_quotes_gpc is enabled + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "exec_id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "notes" => array( + tlInputParameter::STRING_N + ), + "tcversion_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "tproject_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->basehref = $_SESSION['basehref']; + $args->user = $_SESSION['currentUser']; + + return $args; +} + +/** + */ +function initializeGui(&$argsObj, &$tcaseMgr) +{ + $guiObj = new stdClass(); + $guiObj->dialogName = 'editexec_dialog'; + $guiObj->bodyOnLoad = "dialog_onLoad($guiObj->dialogName)"; + $guiObj->bodyOnUnload = "dialog_onUnload($guiObj->dialogName)"; + $guiObj->submitCode = "return dialog_onSubmit($guiObj->dialogName)"; + + $guiObj->exec_id = $argsObj->exec_id; + $guiObj->tcversion_id = $argsObj->tcversion_id; + $guiObj->tplan_id = $argsObj->tplan_id; + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->edit_enabled = $argsObj->user->hasRight($db, "exec_edit_notes") == + 'yes' ? 1 : 0; + $guiObj->cfields_exec = $tcaseMgr->html_table_of_custom_field_inputs( + $argsObj->tcversion_id, null, 'execution', '_cf', $argsObj->exec_id, + $argsObj->tplan_id, $argsObj->tproject_id); + return $guiObj; +} + +/** + * Checks the user rights for viewing the page + * + * @param $db resource + * the database connection handle + * @param $user tlUser + * the object of the current user + * + * @return boolean return true if the page can be viewed, false if not + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "testplan_execute") && + $user->hasRight($db, "exec_edit_notes"); } -$map = get_execution($db,$args->exec_id); -$owebeditor->Value = $map[0]['notes']; - -// order on script is critic -$gui = initializeGui($args,$tcase_mgr); -$cols = intval(isset($editorCfg['cols']) ? $editorCfg['cols'] : 60); -$rows = intval(isset($editorCfg['rows']) ? $editorCfg['rows'] : 10); -$gui->notes = $owebeditor->CreateHTML($rows,$cols); -$gui->editorType = $editorCfg['type']; - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - */ -function doUpdate(&$db,&$args,&$tcaseMgr,&$request) -{ - updateExecutionNotes($db,$args->exec_id,$args->notes); - - $cfield_mgr = new cfield_mgr($db); - $cfield_mgr->execution_values_to_db($request,$args->tcversion_id,$args->exec_id,$args->tplan_id); -} - -/** - * - */ -function init_args() -{ - // Take care of proper escaping when magic_quotes_gpc is enabled - $_REQUEST=strings_stripSlashes($_REQUEST); - - $iParams = array("exec_id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,100), - "notes" => array(tlInputParameter::STRING_N), - "tcversion_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "tproject_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $args->basehref = $_SESSION['basehref']; - $args->user = $_SESSION['currentUser']; - - return $args; -} - -/** - * - */ -function initializeGui(&$argsObj,&$tcaseMgr) -{ - $guiObj = new stdClass(); - $guiObj->dialogName='editexec_dialog'; - $guiObj->bodyOnLoad="dialog_onLoad($guiObj->dialogName)"; - $guiObj->bodyOnUnload="dialog_onUnload($guiObj->dialogName)"; - $guiObj->submitCode="return dialog_onSubmit($guiObj->dialogName)"; - - $guiObj->exec_id = $argsObj->exec_id; - $guiObj->tcversion_id = $argsObj->tcversion_id; - $guiObj->tplan_id = $argsObj->tplan_id; - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->edit_enabled = $argsObj->user->hasRight($db,"exec_edit_notes") == 'yes' ? 1 : 0; - $guiObj->cfields_exec = $tcaseMgr->html_table_of_custom_field_inputs($argsObj->tcversion_id,null,'execution','_cf', - $argsObj->exec_id,$argsObj->tplan_id,$argsObj->tproject_id); - return $guiObj; -} - -/** - * Checks the user rights for viewing the page - * - * @param $db resource the database connection handle - * @param $user tlUser the object of the current user - * - * @return boolean return true if the page can be viewed, false if not - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"testplan_execute") && $user->hasRight($db,"exec_edit_notes"); -} \ No newline at end of file diff --git a/lib/execute/execDashboard.php b/lib/execute/execDashboard.php index f37e311f3b..d2d3e011ee 100644 --- a/lib/execute/execDashboard.php +++ b/lib/execute/execDashboard.php @@ -1,273 +1,275 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: - - args: - - returns: -*/ -function init_args(&$dbHandler,$cfgObj) { - - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - $tplan_mgr = new testplan($dbHandler); - - - // Settings we put on session to create some sort of persistent scope, - // because we have had issues when passing this info using GET mode (size limits) - // - // we get info about build_id, platform_id, etc ... - getContextFromGlobalScope($args); - $args->user = $_SESSION['currentUser']; - $args->user_id = $args->user->dbID; - $args->caller = isset($_REQUEST['caller']) ? $_REQUEST['caller'] : 'exec_feature'; - $args->reload_caller = false; - - $args->tplan_id = intval(isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); - $args->tproject_id = intval(isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']); - - if($args->tproject_id <= 0) { - $tree_mgr = new tree($dbHandler); - $dm = $tree_mgr->get_node_hierarchy_info($args->tplan_id); - $args->tproject_id = $dm['parent_id']; - } - - if(is_null($args->build_id) || ($args->build_id == 0) ) { - // Go for the build - // this info can be present in session, then we will try different ways - // ATTENTION: - // give a look to tlTestCaseFilterControl.class.php method init_setting_build() - // - $key = $args->tplan_id . '_stored_setting_build'; - $args->build_id = isset($_SESSION[$key]) ? intval($_SESSION[$key]) : null; - if( is_null($args->build_id) ) { - $args->build_id = $tplan_mgr->get_max_build_id($args->tplan_id,1,1); - } - } - - if(is_null($args->platform_id) || ($args->platform_id <= 0) ) { - // Go for the platform (if any exists) - // this info can be present in session, then we will try different ways - // ATTENTION: - // give a look to tlTestCaseFilterControl.class.php method init_setting_platform() - // - $itemSet = $tplan_mgr->getPlatforms($args->tplan_id); - if(!is_null($itemSet)) { - $key = $args->tplan_id . '_stored_setting_platform'; - $args->platform_id = isset($_SESSION[$key]) ? intval($_SESSION[$key]) : null; - if( is_null($args->platform_id) || ($args->platform_id <= 0) ) { - $args->platform_id = $itemSet[0]['id']; - } - } - } - return array($args,$tplan_mgr); -} - - - -/* - function: initializeRights - create object with rights useful for this feature - - args: - dbHandler: reference to db object - userObj: reference to current user object - tproject_id: - tplan_id - - Warning: this is right interface for this function, but - has_rights() can works in a mode (that i consider a dirty one) - using SESSION to achieve global coupling. - - returns: - -*/ -function initializeRights(&$dbHandler,&$userObj,$tproject_id,$tplan_id) -{ - $exec_cfg = config_get('exec_cfg'); - $grants = new stdClass(); - - $grants->execute = $userObj->hasRight($dbHandler,"testplan_execute",$tproject_id,$tplan_id); - $grants->execute = $grants->execute=="yes" ? 1 : 0; - - // IMPORTANT NOTICE - TICKET 5128 - // If is TRUE we will need also to analize, test case by test case - // these settings: - // $tlCfg->exec_cfg->exec_mode->tester - // $tlCfg->exec_cfg->simple_tester_roles - // - // Why ? - // Because if a tester can execute ONLY test cases assigned to him, this also - // has to mean that: - // can delete executions ONLY of test cases assigned to him - // can edit exec notes ONLY of test cases assigned to him - // can manage uploads on executions, ONLY of test cases assigned to him - // - // These checks can not be done here - // - // TICKET 5310: Execution Config - convert options into rights - $grants->delete_execution = $userObj->hasRight($dbHandler,"exec_delete",$tproject_id,$tplan_id); - - - // Important: - // Execution right must be present to consider this configuration option. - // $grants->edit_exec_notes = $grants->execute && $exec_cfg->edit_notes; - $grants->edit_exec_notes = $grants->execute && - $userObj->hasRight($dbHandler,"exec_edit_notes",$tproject_id,$tplan_id); - - - $grants->edit_testcase = $userObj->hasRight($dbHandler,"mgt_modify_tc",$tproject_id,$tplan_id); - $grants->edit_testcase = $grants->edit_testcase=="yes" ? 1 : 0; - return $grants; -} - - -/* - function: initializeGui - - args : - - returns: - -*/ -function initializeGui(&$dbHandler,&$argsObj,&$cfgObj,&$tplanMgr) { - - $buildMgr = new build_mgr($dbHandler); - $platformMgr = new tlPlatform($dbHandler,$argsObj->tproject_id); - - $gui = new stdClass(); - $gui->form_token = $argsObj->form_token; - $gui->remoteExecFeedback = $gui->user_feedback = ''; - $gui->tplan_id=$argsObj->tplan_id; - $gui->tproject_id=$argsObj->tproject_id; - $gui->build_id = $argsObj->build_id; - $gui->platform_id = $argsObj->platform_id; - - $gui->attachmentInfos=null; - $gui->refreshTree = 0; - - $cfgTestPlan = getWebEditorCfg('testplan'); - $gui->testPlanEditorType = $cfgTestPlan['type']; - $cfgPlatform = getWebEditorCfg('platform'); - $gui->platformEditorType = $cfgPlatform['type']; - $cfgBuild = getWebEditorCfg('build'); - $gui->buildEditorType = $cfgBuild['type']; - - // Just for the records: - // doing this here, we avoid to do on processTestSuite() and processTestCase(), - // but absolutely this will not improve in ANY WAY perfomance, because we do not loop - // over these two functions. - $tprojectMgr = new testproject($dbHandler); - $gui->tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tproject_id); - $build_info = $buildMgr->get_by_id($argsObj->build_id); - $gui->build_notes=$build_info['notes']; - $gui->build_is_open=($build_info['is_open'] == 1 ? 1 : 0); - - $dummy = $tplanMgr->get_builds_for_html_options($argsObj->tplan_id); - $gui->build_name = isset($dummy[$argsObj->build_id]) ? $dummy[$argsObj->build_id] : ''; - $rs = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->testplan_notes = $rs['notes']; - $gui->testplan_name = $rs['name']; - - // Important note: - // custom fields for test plan can be edited ONLY on design, that's reason why we are using - // scope = 'design' instead of 'execution' - $gui->testplan_cfields = $tplanMgr->html_table_of_custom_field_values($argsObj->tplan_id,'design', - array('show_on_execution' => 1)); - - - $gui->build_cfields = $buildMgr->html_table_of_custom_field_values($argsObj->build_id,$argsObj->tproject_id, - 'design',array('show_on_execution' => 1)); - - $dummy = $platformMgr->getLinkedToTestplan($argsObj->tplan_id); - $gui->has_platforms = !is_null($dummy) ? 1 : 0; - - $gui->platform_info['id']=0; - $gui->platform_info['name']=''; - if(!is_null($argsObj->platform_id) && $argsObj->platform_id > 0 ) { - $gui->platform_info = $platformMgr->getByID($argsObj->platform_id); - } - - $gui->pageTitlePrefix = lang_get('execution_context') . ':'; - - - // JSON for REST API - $gui->restArgs = new stdClass(); - $gui->restArgs->testPlanID = intval($argsObj->tplan_id); - $gui->restArgs->buildID = intval($argsObj->build_id); - $gui->restArgs->platformID = intval($argsObj->platform_id); - - $gui->RESTArgsJSON = json_encode($gui->restArgs); - - return $gui; -} - - -/** - * get info from ... - * - */ -function getContextFromGlobalScope(&$argsObj) -{ - $mode = 'execution_mode'; - $settings = array('build_id' => 'setting_build', 'platform_id' => 'setting_platform'); - $isNumeric = array('build_id' => 0, 'platform_id' => 0); - - $argsObj->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $sf = isset($_SESSION['execution_mode']) && isset($_SESSION['execution_mode'][$argsObj->form_token]) ? - $_SESSION['execution_mode'][$argsObj->form_token] : null; - - if(is_null($sf)) - { - foreach($settings as $key => $sfKey) - { - $argsObj->$key = null; - } - return; - } - - foreach($settings as $key => $sfKey) - { - $argsObj->$key = isset($sf[$sfKey]) ? $sf[$sfKey] : null; - if (is_null($argsObj->$key)) - { - // let's this page be functional withouth a form token too - // (when called from testcases assigned to me) - $argsObj->$key = isset($_REQUEST[$sfKey]) ? $_REQUEST[$sfKey] : null; - } - if(isset($isNumeric[$key])) - { - $argsObj->$key = intval($argsObj->$key); - } - } - +assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: + * + * args: + * + * returns: + */ +function initArgs(&$dbHandler) +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + $tplan_mgr = new testplan($dbHandler); + + // Settings we put on session to create some sort of persistent scope, + // because we have had issues when passing this info using GET mode (size limits) + // + // we get info about build_id, platform_id, etc ... + getContextFromGlobalScope($args); + $args->user = $_SESSION['currentUser']; + $args->user_id = $args->user->dbID; + $args->caller = isset($_REQUEST['caller']) ? $_REQUEST['caller'] : 'exec_feature'; + $args->reload_caller = false; + + $args->tplan_id = intval( + isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); + $args->tproject_id = intval( + isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']); + + if ($args->tproject_id <= 0) { + $tree_mgr = new tree($dbHandler); + $dm = $tree_mgr->get_node_hierarchy_info($args->tplan_id); + $args->tproject_id = $dm['parent_id']; + } + + if (is_null($args->build_id) || ($args->build_id == 0)) { + // Go for the build + // this info can be present in session, then we will try different ways + // ATTENTION: + // give a look to tlTestCaseFilterControl.class.php method init_setting_build() + // + $key = $args->tplan_id . '_stored_setting_build'; + $args->build_id = isset($_SESSION[$key]) ? intval($_SESSION[$key]) : null; + if (is_null($args->build_id)) { + $args->build_id = $tplan_mgr->get_max_build_id($args->tplan_id, 1, 1); + } + } + + if (is_null($args->platform_id) || ($args->platform_id <= 0)) { + // Go for the platform (if any exists) + // this info can be present in session, then we will try different ways + // ATTENTION: + // give a look to tlTestCaseFilterControl.class.php method init_setting_platform() + // + $itemSet = $tplan_mgr->getPlatforms($args->tplan_id); + if (! is_null($itemSet)) { + $key = $args->tplan_id . '_stored_setting_platform'; + $args->platform_id = isset($_SESSION[$key]) ? intval( + $_SESSION[$key]) : null; + if (is_null($args->platform_id) || ($args->platform_id <= 0)) { + $args->platform_id = $itemSet[0]['id']; + } + } + } + return array( + $args, + $tplan_mgr + ); +} + +/* + * function: initializeRights + * create object with rights useful for this feature + * + * args: + * dbHandler: reference to db object + * userObj: reference to current user object + * tproject_id: + * tplan_id + * + * Warning: this is right interface for this function, but + * has_rights() can works in a mode (that i consider a dirty one) + * using SESSION to achieve global coupling. + * + * returns: + * + */ +function initializeRights(&$dbHandler, &$userObj, $tproject_id, $tplan_id) +{ + $grants = new stdClass(); + + $grants->execute = $userObj->hasRight($dbHandler, "testplan_execute", + $tproject_id, $tplan_id); + $grants->execute = $grants->execute == "yes" ? 1 : 0; + + // IMPORTANT NOTICE - TICKET 5128 + // If is TRUE we will need also to analize, test case by test case + // these settings: + // $tlCfg->exec_cfg->exec_mode->tester + // $tlCfg->exec_cfg->simple_tester_roles + // + // Why ? + // Because if a tester can execute ONLY test cases assigned to him, this also + // has to mean that: + // can delete executions ONLY of test cases assigned to him + // can edit exec notes ONLY of test cases assigned to him + // can manage uploads on executions, ONLY of test cases assigned to him + // + // These checks can not be done here + // + // TICKET 5310: Execution Config - convert options into rights + $grants->delete_execution = $userObj->hasRight($dbHandler, "exec_delete", + $tproject_id, $tplan_id); + + // Important: + // Execution right must be present to consider this configuration option. + // $grants->edit_exec_notes = $grants->execute && $exec_cfg->edit_notes; + $grants->edit_exec_notes = $grants->execute && + $userObj->hasRight($dbHandler, "exec_edit_notes", $tproject_id, + $tplan_id); + + $grants->edit_testcase = $userObj->hasRight($dbHandler, "mgt_modify_tc", + $tproject_id, $tplan_id); + $grants->edit_testcase = $grants->edit_testcase == "yes" ? 1 : 0; + return $grants; +} + +/* + * function: initializeGui + * + * args : + * + * returns: + * + */ +function initializeGui(&$dbHandler, &$argsObj, &$cfgObj, &$tplanMgr) +{ + $buildMgr = new build_mgr($dbHandler); + $platformMgr = new tlPlatform($dbHandler, $argsObj->tproject_id); + + $gui = new stdClass(); + $gui->form_token = $argsObj->form_token; + $gui->remoteExecFeedback = $gui->user_feedback = ''; + $gui->tplan_id = $argsObj->tplan_id; + $gui->tproject_id = $argsObj->tproject_id; + $gui->build_id = $argsObj->build_id; + $gui->platform_id = $argsObj->platform_id; + + $gui->attachmentInfos = null; + $gui->refreshTree = 0; + + $cfgTestPlan = getWebEditorCfg('testplan'); + $gui->testPlanEditorType = $cfgTestPlan['type']; + $cfgPlatform = getWebEditorCfg('platform'); + $gui->platformEditorType = $cfgPlatform['type']; + $cfgBuild = getWebEditorCfg('build'); + $gui->buildEditorType = $cfgBuild['type']; + + // Just for the records: + // doing this here, we avoid to do on processTestSuite() and processTestCase(), + // but absolutely this will not improve in ANY WAY perfomance, because we do not loop + // over these two functions. + $tprojectMgr = new testproject($dbHandler); + $gui->tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tproject_id); + $build_info = $buildMgr->get_by_id($argsObj->build_id); + $gui->build_notes = $build_info['notes']; + $gui->build_is_open = ($build_info['is_open'] == 1 ? 1 : 0); + + $dummy = $tplanMgr->get_builds_for_html_options($argsObj->tplan_id); + $gui->build_name = isset($dummy[$argsObj->build_id]) ? $dummy[$argsObj->build_id] : ''; + $rs = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->testplan_notes = $rs['notes']; + $gui->testplan_name = $rs['name']; + + // Important note: + // custom fields for test plan can be edited ONLY on design, that's reason why we are using + // scope = 'design' instead of 'execution' + $gui->testplan_cfields = $tplanMgr->html_table_of_custom_field_values( + $argsObj->tplan_id, 'design', array( + 'show_on_execution' => 1 + )); + + $gui->build_cfields = $buildMgr->html_table_of_custom_field_values( + $argsObj->build_id, $argsObj->tproject_id, 'design', + array( + 'show_on_execution' => 1 + )); + + $dummy = $platformMgr->getLinkedToTestplan($argsObj->tplan_id); + $gui->has_platforms = ! is_null($dummy) ? 1 : 0; + + $gui->platform_info['id'] = 0; + $gui->platform_info['name'] = ''; + if (! is_null($argsObj->platform_id) && $argsObj->platform_id > 0) { + $gui->platform_info = $platformMgr->getByID($argsObj->platform_id); + } + + $gui->pageTitlePrefix = lang_get('execution_context') . ':'; + + // JSON for REST API + $gui->restArgs = new stdClass(); + $gui->restArgs->testPlanID = intval($argsObj->tplan_id); + $gui->restArgs->buildID = intval($argsObj->build_id); + $gui->restArgs->platformID = intval($argsObj->platform_id); + + $gui->RESTArgsJSON = json_encode($gui->restArgs); + + return $gui; +} + +/** + * get info from ... + */ +function getContextFromGlobalScope(&$argsObj) +{ + $settings = array( + 'build_id' => 'setting_build', + 'platform_id' => 'setting_platform' + ); + $isNumeric = array( + 'build_id' => 0, + 'platform_id' => 0 + ); + + $argsObj->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $sf = isset($_SESSION['execution_mode']) && + isset($_SESSION['execution_mode'][$argsObj->form_token]) ? $_SESSION['execution_mode'][$argsObj->form_token] : null; + + if (is_null($sf)) { + foreach ($settings as $key => $sfKey) { + $argsObj->$key = null; + } + return; + } + + foreach ($settings as $key => $sfKey) { + $argsObj->$key = isset($sf[$sfKey]) ? $sf[$sfKey] : null; + if (is_null($argsObj->$key)) { + // let's this page be functional withouth a form token too + // (when called from testcases assigned to me) + $argsObj->$key = isset($_REQUEST[$sfKey]) ? $_REQUEST[$sfKey] : null; + } + if (isset($isNumeric[$key])) { + $argsObj->$key = intval($argsObj->$key); + } + } } diff --git a/lib/execute/execExport.php b/lib/execute/execExport.php index a46b7c0600..a1c8d7d69c 100644 --- a/lib/execute/execExport.php +++ b/lib/execute/execExport.php @@ -1,213 +1,200 @@ -doExport) -{ - $content = contentAsXML($db,$args,$tplan_mgr); - downloadContentsToFile($content,$gui->export_filename); - exit(); -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: init_args - - args: - - returns: - -*/ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - $args->doExport = isset($_REQUEST['export']) ? $_REQUEST['export'] : null; - $args->exportType = isset($_REQUEST['exportType']) ? $_REQUEST['exportType'] : null; - - $key2loop = array('tproject','tplan','platform','build','tsuite'); - foreach($key2loop as $item) - { - $argsKey = $item . '_id'; - $inputKey = $item . 'ID'; - $args->$argsKey = isset($_REQUEST[$inputKey]) ? intval($_REQUEST[$inputKey]) : 0; - } - - $args->export_filename=isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : null; - $args->goback_url=isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; - - $args->tcversionSet=isset($_REQUEST['tcversionSet']) ? $_REQUEST['tcversionSet'] : null; - return $args; -} - - -/** - * - * - */ -function initializeGui(&$argsObj,&$tplanMgr) -{ - $info = $tplanMgr->get_by_id($argsObj->tplan_id); - - $guiObj = new stdClass(); - $guiObj->do_it = 1; - $guiObj->nothing_todo_msg = ''; - $guiObj->export_filename = 'export_execution_set.xml'; - $guiObj->exportTypes = array('XML' => 'XML'); - $guiObj->page_title = lang_get('export_execution_set'); - $guiObj->object_name = ''; - $guiObj->goback_url = !is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; - - $key2loop = array('tproject','tplan','platform','build','tsuite'); - foreach($key2loop as $item) - { - $argsKey = $item . '_id'; - // $inputKey = $item . 'ID'; - $guiObj->$argsKey = intval($argsObj->$argsKey); - } - $guiObj->tcversionSet = $argsObj->tcversionSet; - $guiObj->drawCancelButton = false; - - return $guiObj; -} - - - -/** - * - * - */ -function contentAsXML(&$dbHandler,$contextSet,&$tplanMgr) -{ - $dummy = array(); - $dummy['context'] = contextAsXML($dbHandler,$contextSet,$tplanMgr); - $dummy['tcaseSet'] = tcaseSetAsXML($dbHandler,$contextSet); - - $xmlString = TL_XMLEXPORT_HEADER . - "\n\n\t{$dummy['context']}\n\t{$dummy['tcaseSet']}\n\t"; - return $xmlString; - -} - -/** - * - * - */ -function contextAsXML(&$dbHandler,$contextSet,&$tplanMgr) -{ - $info = array(); - $tprojectMgr = new testproject($dbHandler); - $info['tproject'] = $tprojectMgr->get_by_id($contextSet->tproject_id); - unset($tprojectMgr); - - $info['tplan'] = $tplanMgr->get_by_id($contextSet->tplan_id); - - $buildMgr = new build_mgr($dbHandler); - $info['build'] = $buildMgr->get_by_id($contextSet->build_id); - unset($buildMgr); - - $info['platform'] = null; - $platform_template = ''; - if( $contextSet->platform_id > 0 ) - { - $platformMgr = new tlPlatform($dbHandler, $contextSet->tproject_id); - $info['platform'] = $platformMgr->getByID($contextSet->platform_id); - unset($platformMgr); - - $platform_template = "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . ""; - } - - $key2loop = array_keys($info); - foreach($key2loop as $item_key) - { - if(!is_null($info[$item_key])) - { - $contextInfo[$item_key . '_id'] = $info[$item_key]['id']; - $contextInfo[$item_key . '_name'] = $info[$item_key]['name']; - } - } - $contextInfo['prefix'] = $info['tproject']['prefix']; - - $xml_root = "{{XMLCODE}}\n"; - $xml_template = "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . "" . - "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . "" . $platform_template . - "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . ""; - - $xml_mapping = null; - $xml_mapping = array("||TPROJECTNAME||" => "tproject_name", "||TPROJECTID||" => 'tproject_id', - "||TPROJECTPREFIX||" => "prefix", - "||TPLANNAME||" => "tplan_name", "||TPLANID||" => 'tplan_id', - "||BUILDNAME||" => "build_name", "||BUILDID||" => 'build_id', - "||PLATFORMNAME||" => "platform_name", "||PLATFORMID||" => 'platform_id'); - - - $mm = array($contextInfo); - $contextXML = exportDataToXML($mm,$xml_root,$xml_template,$xml_mapping,('noXMLHeader'=='noXMLHeader')); - // echo '
    ';
    -	// echo $contextXML;
    -	// echo '
    '; - - return $contextXML; -} - -/** - * - * - */ -function tcaseSetAsXML(&$dbHandler,$contextSet) -{ - $tcaseMgr = new testcase($dbHandler); - $tcversionSet = explode(',',$contextSet->tcversionSet); - $xmlTC = "\n\t"; - foreach($tcversionSet as $tcversion_id) - { - $xmlTC .= $tcaseMgr->exportTestCaseDataToXML(0,$tcversion_id,$contextSet->tproject_id,true); - - } - $xmlTC .= "\n\t"; - return $xmlTC; -} +doExport) { + $content = contentAsXML($db, $args, $tplan_mgr); + downloadContentsToFile($content, $gui->export_filename); + exit(); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: init_args + * + * args: + * + * returns: + * + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + $args->doExport = isset($_REQUEST['export']) ? $_REQUEST['export'] : null; + $args->exportType = isset($_REQUEST['exportType']) ? $_REQUEST['exportType'] : null; + + $key2loop = array( + 'tproject', + 'tplan', + 'platform', + 'build', + 'tsuite' + ); + foreach ($key2loop as $item) { + $argsKey = $item . '_id'; + $inputKey = $item . 'ID'; + $args->$argsKey = isset($_REQUEST[$inputKey]) ? intval( + $_REQUEST[$inputKey]) : 0; + } + + $args->export_filename = isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : null; + $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; + + $args->tcversionSet = isset($_REQUEST['tcversionSet']) ? $_REQUEST['tcversionSet'] : null; + return $args; +} + +/** + */ +function initializeGui(&$argsObj, &$tplanMgr) +{ + $guiObj = new stdClass(); + $guiObj->do_it = 1; + $guiObj->nothing_todo_msg = ''; + $guiObj->export_filename = 'export_execution_set.xml'; + $guiObj->exportTypes = array( + 'XML' => 'XML' + ); + $guiObj->page_title = lang_get('export_execution_set'); + $guiObj->object_name = ''; + $guiObj->goback_url = ! is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; + + $key2loop = array( + 'tproject', + 'tplan', + 'platform', + 'build', + 'tsuite' + ); + foreach ($key2loop as $item) { + $argsKey = $item . '_id'; + $guiObj->$argsKey = intval($argsObj->$argsKey); + } + $guiObj->tcversionSet = $argsObj->tcversionSet; + $guiObj->drawCancelButton = false; + + return $guiObj; +} + +/** + */ +function contentAsXML(&$dbHandler, $contextSet, &$tplanMgr) +{ + $dummy = array(); + $dummy['context'] = contextAsXML($dbHandler, $contextSet, $tplanMgr); + $dummy['tcaseSet'] = tcaseSetAsXML($dbHandler, $contextSet); + + return TL_XMLEXPORT_HEADER . + "\n\n\t{$dummy['context']}\n\t{$dummy['tcaseSet']}\n\t"; +} + +/** + */ +function contextAsXML(&$dbHandler, $contextSet, &$tplanMgr) +{ + $info = array(); + $tprojectMgr = new testproject($dbHandler); + $info['tproject'] = $tprojectMgr->get_by_id($contextSet->tproject_id); + unset($tprojectMgr); + + $info['tplan'] = $tplanMgr->get_by_id($contextSet->tplan_id); + + $buildMgr = new build_mgr($dbHandler); + $info['build'] = $buildMgr->get_by_id($contextSet->build_id); + unset($buildMgr); + + $info['platform'] = null; + $platform_template = ''; + if ($contextSet->platform_id > 0) { + $platformMgr = new tlPlatform($dbHandler, $contextSet->tproject_id); + $info['platform'] = $platformMgr->getByID($contextSet->platform_id); + unset($platformMgr); + + $platform_template = "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + ""; + } + + $key2loop = array_keys($info); + foreach ($key2loop as $item_key) { + if (! is_null($info[$item_key])) { + $contextInfo[$item_key . '_id'] = $info[$item_key]['id']; + $contextInfo[$item_key . '_name'] = $info[$item_key]['name']; + } + } + $contextInfo['prefix'] = $info['tproject']['prefix']; + + $xml_root = "{{XMLCODE}}\n"; + $xml_template = "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + "" . "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + "" . $platform_template . "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + ""; + + $xml_mapping = null; + $xml_mapping = array( + "||TPROJECTNAME||" => "tproject_name", + "||TPROJECTID||" => 'tproject_id', + "||TPROJECTPREFIX||" => "prefix", + "||TPLANNAME||" => "tplan_name", + "||TPLANID||" => 'tplan_id', + "||BUILDNAME||" => "build_name", + "||BUILDID||" => 'build_id', + "||PLATFORMNAME||" => "platform_name", + "||PLATFORMID||" => 'platform_id' + ); + + $mm = array( + $contextInfo + ); + return exportDataToXML($mm, $xml_root, $xml_template, $xml_mapping, true); +} + +/** + */ +function tcaseSetAsXML(&$dbHandler, $contextSet) +{ + $tcaseMgr = new testcase($dbHandler); + $tcversionSet = explode(',', $contextSet->tcversionSet); + $xmlTC = "\n\t"; + foreach ($tcversionSet as $tcversion_id) { + $xmlTC .= $tcaseMgr->exportTestCaseDataToXML(0, $tcversion_id, + $contextSet->tproject_id, true); + } + $xmlTC .= "\n\t"; + return $xmlTC; +} ?> diff --git a/lib/execute/execHistory.php b/lib/execute/execHistory.php index 7b892daf5a..d14a7d9a77 100644 --- a/lib/execute/execHistory.php +++ b/lib/execute/execHistory.php @@ -1,198 +1,195 @@ -exec_cfg = config_get('exec_cfg'); - - -$node['basic'] = $tcase_mgr->tree_manager->get_node_hierarchy_info($args->tcase_id); -$node['specific'] = $tcase_mgr->getExternalID($args->tcase_id); -$idCard = $node['specific'][0] . ' : ' . $node['basic']['name']; - - -$gui->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - -// IMPORTANT NOTICE: -// getExecutionSet() consider only executions written to DB. -// we can filter out execution that belongs to test plans / test project current user -// has no right to access -// does this means we need to get also for each test project/test plan present -// in result set it's public/private status -// - -// Need to get all test plans user is able to access. -$testPlanSet = - (array)$args->user->getAccessibleTestPlans($db,$gui->tproject_id,null, - array('active' => $args->onlyActiveTestPlans)); - -$gui->grants = new stdClass(); -$gui->grants->exec_edit_notes = null; -$filters['testplan_id'] = null; -foreach($testPlanSet as $rx) -{ - $filters['testplan_id'][] = $rx['id']; - $gui->grants->exec_edit_notes[$rx['id']] = - $args->user->hasRight($db,'exec_edit_notes',$gui->tproject_id,$rx['id']); +exec_cfg = config_get('exec_cfg'); + +$node['basic'] = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $args->tcase_id); +$node['specific'] = $tcaseMgr->getExternalID($args->tcase_id); +$idCard = $node['specific'][0] . ' : ' . $node['basic']['name']; + +$gui->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + +// IMPORTANT NOTICE: +// getExecutionSet() consider only executions written to DB. +// we can filter out execution that belongs to test plans / test project current user +// has no right to access +// does this means we need to get also for each test project/test plan present +// in result set it's public/private status +// + +// Need to get all test plans user is able to access. +$testPlanSet = (array) $args->user->getAccessibleTestPlans($db, + $gui->tproject_id, null, array( + 'active' => $args->onlyActiveTestPlans + )); + +$gui->grants = new stdClass(); +$gui->grants->exec_edit_notes = null; +$filters['testplan_id'] = null; +foreach ($testPlanSet as $rx) { + $filters['testplan_id'][] = $rx['id']; + $gui->grants->exec_edit_notes[$rx['id']] = $args->user->hasRight($db, + 'exec_edit_notes', $gui->tproject_id, $rx['id']); +} +$gui->execSet = $tcaseMgr->getExecutionSet($args->tcase_id, null, $filters); + +$gui->warning_msg = (! is_null($gui->execSet)) ? '' : lang_get( + 'tcase_never_executed'); +$gui->user_is_admin = ($args->user->globalRole->name == 'admin') ? true : false; + +$gui->execPlatformSet = null; +$gui->cfexec = null; +$gui->attachments = null; + +if (! is_null($gui->execSet)) { + $gui->execPlatformSet = $tcaseMgr->getExecutedPlatforms($args->tcase_id); + + // get issue tracker config and object to manage TestLink - BTS integration + $its = null; + $tproject_mgr = new testproject($db); + $info = $tproject_mgr->get_by_id($gui->tproject_id); + if ($info['issue_tracker_enabled']) { + $gui->bugs = getIssues($db, $gui->execSet, $gui->tproject_id); + } + // get custom fields brute force => do not check if this call is needed + $gui->cfexec = getCustomFields($tcaseMgr, $gui->execSet); + $gui->attachments = getAttachments($db, $gui->execSet); +} + +$gui->displayPlatformCol = ! is_null($gui->execPlatformSet) ? 1 : 0; +$gui->main_descr = lang_get('execution_history'); +$gui->detailed_descr = lang_get('test_case') . ' ' . $idCard; +$gui->tcase_id = intval($args->tcase_id); +$gui->onlyActiveTestPlans = intval($args->onlyActiveTestPlans); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "tcase_id" => array( + tlInputParameter::INT_N + ), + 'onlyActiveTestPlans' => array( + tlInputParameter::INT_N + ) + ); + $pParams = R_PARAMS($iParams); + + $args = new stdClass(); + $args->tcase_id = intval($pParams["tcase_id"]); + + $args->onlyActiveTestPlans = null; + if (intval($pParams["onlyActiveTestPlans"]) > 0 || + $pParams["onlyActiveTestPlans"] == 'on') { + $args->onlyActiveTestPlans = 1; + } + + // not a very good solution but a Quick & Dirty Fix + $args->user = $_SESSION['currentUser']; + + return $args; +} + +/** + * + * @param database $dbHandler + * @param array $execSet + * @param int $tprojectID + * @return array + */ +function getIssues(&$dbHandler, &$execSet, $tprojectID) +{ + $it_mgr = new tlIssueTracker($dbHandler); + $its = $it_mgr->getInterfaceObject($tprojectID); + unset($it_mgr); + + // we will see in future if we can use a better algorithm + $issues = array(); + $tcv2loop = array_keys($execSet); + foreach ($tcv2loop as $tcvid) { + $execQty = count($execSet[$tcvid]); + for ($idx = 0; $idx < $execQty; $idx ++) { + $exec_id = $execSet[$tcvid][$idx]['execution_id']; + $dummy = get_bugs_for_exec($dbHandler, $its, $exec_id); + if (! empty($dummy)) { + $issues[$exec_id] = $dummy; + } + } + } + return $issues; +} + +/** + * + * @param testcase $tcaseMgr + * @param array $execSet + * @return string[]|array[] + */ +function getCustomFields(&$tcaseMgr, &$execSet) +{ + $cf = array(); + $tcv2loop = array_keys($execSet); + foreach ($tcv2loop as $tcvid) { + $execQty = count($execSet[$tcvid]); + for ($idx = 0; $idx < $execQty; $idx ++) { + $exec_id = $execSet[$tcvid][$idx]['execution_id']; + $tplan_id = $execSet[$tcvid][$idx]['testplan_id']; + $dummy = (array) $tcaseMgr->html_table_of_custom_field_values( + $tcvid, 'execution', null, $exec_id, $tplan_id); + $cf[$exec_id] = (! empty($dummy)) ? $dummy : ''; + } + } + return $cf; +} + +/** + * + * @param database $dbHandler + * @param array $execSet + * @return NULL|array + */ +function getAttachments(&$dbHandler, &$execSet) +{ + $attachmentMgr = tlAttachmentRepository::create($dbHandler); + + $att = null; + $tcv2loop = array_keys($execSet); + foreach ($tcv2loop as $tcvid) { + $execQty = count($execSet[$tcvid]); + for ($idx = 0; $idx < $execQty; $idx ++) { + $exec_id = $execSet[$tcvid][$idx]['execution_id']; + $items = getAttachmentInfos($attachmentMgr, $exec_id, 'executions', + true, 1); + if ($items) { + $att[$exec_id] = $items; + } + } + } + return $att; } -$gui->execSet = $tcase_mgr->getExecutionSet($args->tcase_id,null,$filters); - -$gui->warning_msg = (!is_null($gui->execSet)) ? '' : lang_get('tcase_never_executed'); -$gui->user_is_admin = ($args->user->globalRole->name=='admin') ? true : false; - -$gui->execPlatformSet = null; -$gui->cfexec = null; -$gui->attachments = null; - -if(!is_null($gui->execSet) ) -{ - $gui->execPlatformSet = $tcase_mgr->getExecutedPlatforms($args->tcase_id); - - // get issue tracker config and object to manage TestLink - BTS integration - $its = null; - $tproject_mgr = new testproject($db); - $info = $tproject_mgr->get_by_id($gui->tproject_id); - if($info['issue_tracker_enabled']) - { - $gui->bugs = getIssues($db,$gui->execSet,$gui->tproject_id); - } - // get custom fields brute force => do not check if this call is needed - $gui->cfexec = getCustomFields($tcase_mgr,$gui->execSet); - $gui->attachments = getAttachments($db,$gui->execSet); - -} - -$gui->displayPlatformCol = !is_null($gui->execPlatformSet) ? 1 : 0; -$gui->main_descr = lang_get('execution_history'); -$gui->detailed_descr = lang_get('test_case') . ' ' . $idCard; -$gui->tcase_id = intval($args->tcase_id); -$gui->onlyActiveTestPlans = intval($args->onlyActiveTestPlans); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $iParams = array("tcase_id" => array(tlInputParameter::INT_N), - 'onlyActiveTestPlans' => array(tlInputParameter::INT_N)); - $pParams = R_PARAMS($iParams); - - $args = new stdClass(); - $args->tcase_id = intval($pParams["tcase_id"]); - - $args->onlyActiveTestPlans = null; - if(intval($pParams["onlyActiveTestPlans"]) > 0 || - $pParams["onlyActiveTestPlans"] == 'on') - { - $args->onlyActiveTestPlans = 1; - } - - // not a very good solution but a Quick & Dirty Fix - $args->user = $_SESSION['currentUser']; - - return $args; -} - - -/** - * - * - */ -function getIssues(&$dbHandler,&$execSet,$tprojectID) -{ - - $it_mgr = new tlIssueTracker($dbHandler); - $its = $it_mgr->getInterfaceObject($tprojectID); - unset($it_mgr); - - // we will see in future if we can use a better algorithm - $issues = array(); - $tcv2loop = array_keys($execSet); - foreach($tcv2loop as $tcvid) - { - $execQty = count($execSet[$tcvid]); - for($idx=0; $idx < $execQty; $idx++) - { - $exec_id = $execSet[$tcvid][$idx]['execution_id']; - $dummy = get_bugs_for_exec($dbHandler,$its,$exec_id); - if(count($dummy) > 0) - { - $issues[$exec_id] = $dummy; - } - } - } - return $issues; -} - -/** - * - * - */ -function getCustomFields(&$tcaseMgr,&$execSet) -{ - $cf = array(); - $tcv2loop = array_keys($execSet); - foreach($tcv2loop as $tcvid) - { - $execQty = count($execSet[$tcvid]); - for($idx=0; $idx < $execQty; $idx++) - { - $exec_id = $execSet[$tcvid][$idx]['execution_id']; - $tplan_id = $execSet[$tcvid][$idx]['testplan_id']; - $dummy = (array)$tcaseMgr->html_table_of_custom_field_values($tcvid,'execution',null,$exec_id,$tplan_id); - $cf[$exec_id] = (count($dummy) > 0) ? $dummy : ''; - } - } - return $cf; -} - -/** - * - * - */ -function getAttachments(&$dbHandler,&$execSet) -{ - $attachmentMgr = tlAttachmentRepository::create($dbHandler); - - $att = null; - $tcv2loop = array_keys($execSet); - foreach($tcv2loop as $tcvid) - { - $execQty = count($execSet[$tcvid]); - for($idx=0; $idx < $execQty; $idx++) - { - $exec_id = $execSet[$tcvid][$idx]['execution_id']; - $items = getAttachmentInfos($attachmentMgr,$exec_id,'executions',true,1); - if($items) - { - $att[$exec_id] = $items; - } - } - } - return $att; -} \ No newline at end of file diff --git a/lib/execute/execNavigator.php b/lib/execute/execNavigator.php index 345944703c..dd9148b150 100644 --- a/lib/execute/execNavigator.php +++ b/lib/execute/execNavigator.php @@ -1,142 +1,137 @@ -formAction = ''; - -$gui = initializeGui($db,$control); - - -$control->build_tree_menu($gui); - - -$smarty = new TLSmarty(); -if( $gui->execAccess ) { - $smarty->assign('gui',$gui); - $smarty->assign('control', $control); - $smarty->assign('menuUrl',$gui->menuUrl); - $smarty->assign('args', $gui->args); - $tpl = $templateCfg->template_dir . $templateCfg->default_template; -} else { - $tpl = 'noaccesstofeature.tpl'; +formAction = ''; + +$gui = initializeGui($db, $control); + +$control->build_tree_menu($gui); + +$smarty = new TLSmarty(); +if ($gui->execAccess) { + $smarty->assign('gui', $gui); + $smarty->assign('control', $control); + $smarty->assign('menuUrl', $gui->menuUrl); + $smarty->assign('args', $gui->args); + $tpl = $templateCfg->template_dir . $templateCfg->default_template; +} else { + $tpl = 'noaccesstofeature.tpl'; +} + +$smarty->display($tpl); + +/** + */ +function initializeGui(&$dbH, &$control) +{ + $gui = new stdClass(); + + // This logic is managed from execSetResults.php + $gui->loadExecDashboard = true; + if (isset($_SESSION['loadExecDashboard'][$control->form_token]) || + $control->args->loadExecDashboard == 0) { + $gui->loadExecDashboard = false; + unset($_SESSION['loadExecDashboard'][$control->form_token]); + } + + $gui->menuUrl = 'lib/execute/execSetResults.php'; + $gui->args = $control->get_argument_string(); + if (! $control->args->loadExecDashboard) { + $gui->src_workframe = ''; + } else { + $gui->src_workframe = $control->args->basehref . $gui->menuUrl . + "?edit=testproject&id={$control->args->testproject_id}" . $gui->args; + } + + $control->draw_export_testplan_button = true; + $control->draw_import_xml_results_button = true; + + $dummy = config_get('results'); + $gui->not_run = $dummy['status_code']['not_run']; + + $dummy = config_get('execution_filter_methods'); + $gui->lastest_exec_method = $dummy['status_code']['latest_execution']; + $gui->pageTitle = lang_get('href_execute_test'); + + $grants = checkAccessToExec($dbH, $control); + + // feature to enable/disable + $gui->features = array( + 'export' => false, + 'import' => false + ); + $gui->execAccess = false; + if ($grants['testplan_execute']) { + $gui->features['export'] = true; + $gui->features['import'] = true; + $gui->execAccess = true; + } + + if ($grants['exec_ro_access']) { + $gui->execAccess = true; + } + + $control->draw_export_testplan_button = $gui->features['export']; + $control->draw_import_xml_results_button = $gui->features['import']; + + return $gui; +} + +/** + */ +function checkAccessToExec(&$dbH, &$ct) +{ + $tplan_id = intval($ct->args->testplan_id); + $sch = tlObject::getDBTables(array( + 'testplans' + )); + $sql = "SELECT testproject_id FROM {$sch['testplans']} " . "WHERE id=" . + $tplan_id; + $rs = $dbH->get_recordset($sql); + if (is_null($rs)) { + throw new Exception("Can not find Test Project For Test Plan - ABORT", 1); + } + $rs = current($rs); + $tproject_id = $rs['testproject_id']; + + $user = $_SESSION['currentUser']; + $grants = null; + $k2a = array( + 'testplan_execute', + 'exec_ro_access' + ); + foreach ($k2a as $r2c) { + $grants[$r2c] = false; + if ($user->hasRight($dbH, $r2c, $tproject_id, $tplan_id, true) || + $user->globalRoleID == TL_ROLES_ADMIN) { + $grants[$r2c] = true; + } + } + + return $grants; } - -$smarty->display($tpl); - - -/** - * - * - */ -function initializeGui(&$dbH,&$control) { - $gui = new stdClass(); - - // This logic is managed from execSetResults.php - $gui->loadExecDashboard = true; - if( isset($_SESSION['loadExecDashboard'][$control->form_token]) || - $control->args->loadExecDashboard == 0 - ) { - $gui->loadExecDashboard = false; - unset($_SESSION['loadExecDashboard'][$control->form_token]); - } - - $gui->menuUrl = 'lib/execute/execSetResults.php'; - $gui->args = $control->get_argument_string(); - if($control->args->loadExecDashboard == false) { - $gui->src_workframe = ''; - } else { - $gui->src_workframe = $control->args->basehref . $gui->menuUrl . - "?edit=testproject&id={$control->args->testproject_id}" . - $gui->args; - } - - $control->draw_export_testplan_button = true; - $control->draw_import_xml_results_button = true; - - - $dummy = config_get('results'); - $gui->not_run = $dummy['status_code']['not_run']; - - $dummy = config_get('execution_filter_methods'); - $gui->lastest_exec_method = $dummy['status_code']['latest_execution']; - $gui->pageTitle = lang_get('href_execute_test'); - - $grants = checkAccessToExec($dbH,$control); - - // feature to enable/disable - $gui->features = array('export' => false,'import' => false); - $gui->execAccess = false; - if($grants['testplan_execute']) { - $gui->features['export'] = true; - $gui->features['import'] = true; - $gui->execAccess = true; - } - - if($grants['exec_ro_access']) { - $gui->execAccess = true; - } - - - $control->draw_export_testplan_button = $gui->features['export']; - $control->draw_import_xml_results_button = $gui->features['import']; - - return $gui; -} - - -/** - * - */ -function checkAccessToExec(&$dbH,&$ct) { - $tplan_id = intval($ct->args->testplan_id); - $sch = tlObject::getDBTables(array('testplans')); - $sql = "SELECT testproject_id FROM {$sch['testplans']} " . - "WHERE id=" . $tplan_id; - $rs = $dbH->get_recordset($sql); - if(is_null($rs)) - { - throw new Exception("Can not find Test Project For Test Plan - ABORT", 1); - - } - $rs = current($rs); - $tproject_id = $rs['testproject_id']; - - $user = $_SESSION['currentUser']; - $grants = null; - $k2a = array('testplan_execute','exec_ro_access'); - foreach($k2a as $r2c) - { - $grants[$r2c] = false; - if( $user->hasRight($dbH,$r2c,$tproject_id,$tplan_id,true) || $user->globalRoleID == TL_ROLES_ADMIN ) - { - $grants[$r2c] = true; - } - } - - return $grants; -} diff --git a/lib/execute/execNotes.php b/lib/execute/execNotes.php index 0df6e4f277..aada5ba389 100644 --- a/lib/execute/execNotes.php +++ b/lib/execute/execNotes.php @@ -1,85 +1,92 @@ -doAction) -{ - case 'edit': - break; - - case 'doUpdate': - doUpdate($db,$args); - break; -} -$map = get_execution($db,$args->exec_id); -$owebeditor->Value = $map[0]['notes']; - -$smarty = new TLSmarty(); -$smarty->assign('notes',$owebeditor->CreateHTML()); -$smarty->assign('editorType',$editorCfg['type']); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: - - args : - - returns: - -*/ -function doUpdate(&$dbHandler,&$argsObj) -{ - $tables = tlObjectWithDB::getDBTables('executions'); - $sql = "UPDATE {$tables['executions']} " . - " SET notes='" . $dbHandler->prepare_string($argsObj->notes) . "' " . - " WHERE id={$argsObj->exec_id} "; - $dbHandler->exec_query($sql); -} - - -/* - function: - - args : - - returns: - -*/ -function init_args() -{ - $iParams = array("exec_id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,100), - "notes" => array(tlInputParameter::STRING_N)); - $args = new stdClass(); - R_PARAMS($iParams,$args); - return $args; -} - -?> \ No newline at end of file +doAction) { + case 'edit': + break; + + case 'doUpdate': + doUpdate($db, $args); + break; +} +$map = get_execution($db, $args->exec_id); +$owebeditor->Value = $map[0]['notes']; + +$smarty = new TLSmarty(); +$smarty->assign('notes', $owebeditor->CreateHTML()); +$smarty->assign('editorType', $editorCfg['type']); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: + * + * args : + * + * returns: + * + */ +function doUpdate(&$dbHandler, &$argsObj) +{ + $tables = tlObjectWithDB::getDBTables('executions'); + $sql = "UPDATE {$tables['executions']} " . " SET notes='" . + $dbHandler->prepare_string($argsObj->notes) . "' " . + " WHERE id={$argsObj->exec_id} "; + $dbHandler->exec_query($sql); +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs() +{ + $iParams = array( + "exec_id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "notes" => array( + tlInputParameter::STRING_N + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + return $args; +} + +?> diff --git a/lib/execute/execPrint.php b/lib/execute/execPrint.php index 1b742b6458..299a4b2439 100644 --- a/lib/execute/execPrint.php +++ b/lib/execute/execPrint.php @@ -1,71 +1,86 @@ -goback_url = !is_null($args->goback_url) ? $args->goback_url : ''; -$gui->page_title = ''; - -if($args->deleteAttachmentID >0) -{ - deleteAttachment($db,$args->deleteAttachmentID); -} - -// Struture defined in printDocument.php -$printingOptions = array('toc' => 0,'body' => 1,'summary' => 1, 'header' => 0,'headerNumbering' => 0, - 'passfail' => 0, 'author' => 1, 'notes' => 1, 'requirement' => 1, 'keyword' => 1, - 'cfields' => 1, 'displayVersion' => 1, 'displayDates' => 1, - 'docType' => SINGLE_TESTCASE, 'importance' => 1); - -$level = 0; -$tplanID = 0; -$prefix = null; -$text2print = ''; -$text2print .= renderHTMLHeader($gui->page_title,$_SESSION['basehref'],SINGLE_TESTCASE, - array('gui/javascript/testlink_library.js')); - -$text2print .= renderExecutionForPrinting($db,$_SESSION['basehref'],$args->id,$_SESSION['currentUser']); - -echo $text2print; - -/* - function: init_args - - args: - - returns: - -*/ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; - $args->deleteAttachmentID = isset($_REQUEST['deleteAttachmentID']) ? intval($_REQUEST['deleteAttachmentID']) : 0; - - $args->goback_url = null; - return $args; -} \ No newline at end of file +goback_url = ! is_null($args->goback_url) ? $args->goback_url : ''; +$gui->page_title = ''; + +if ($args->deleteAttachmentID > 0) { + deleteAttachment($db, $args->deleteAttachmentID); +} + +// Struture defined in printDocument.php +$printingOptions = array( + 'toc' => 0, + 'body' => 1, + 'summary' => 1, + 'header' => 0, + 'headerNumbering' => 0, + 'passfail' => 0, + 'author' => 1, + 'notes' => 1, + 'requirement' => 1, + 'keyword' => 1, + 'cfields' => 1, + 'displayVersion' => 1, + 'displayDates' => 1, + 'docType' => SINGLE_TESTCASE, + 'importance' => 1 +); + +$level = 0; +$tplanID = 0; +$prefix = null; +$text2print = ''; +$text2print .= renderHTMLHeader($gui->page_title, $_SESSION['basehref'], + SINGLE_TESTCASE, array( + 'gui/javascript/testlink_library.js' + )); + +$text2print .= renderExecutionForPrinting($db, $_SESSION['basehref'], $args->id, + $_SESSION['currentUser']); + +echo $text2print; + +/* + * function: init_args + * + * args: + * + * returns: + * + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; + $args->deleteAttachmentID = isset($_REQUEST['deleteAttachmentID']) ? intval( + $_REQUEST['deleteAttachmentID']) : 0; + + $args->goback_url = null; + return $args; +} diff --git a/lib/execute/execSetResults.php b/lib/execute/execSetResults.php index 0f4f312f60..76c59ef5c7 100644 --- a/lib/execute/execSetResults.php +++ b/lib/execute/execSetResults.php @@ -1,2470 +1,2587 @@ -testcases_to_show - * - * Normally this script is called from the tree. - * Filters and other conditions (example display test cases just assigned to me,etc) - * can be applied, creating a set of test cases that can be used. - * Due to size restrictions on POST variables this info is transfered via $_SESSION. - * - * But because we have choosen to add access to this script from other features - * we have forgot to populate this info. - * This is the reason for several issues. - * The approach will be to understand who is the caller and apply different logics - * instead of recreate the logic to populate $_SESSION - * (I think this approach will be simpler). - * - * - * Note about step info - * is present in gui->map_last_exec - * - * -**/ -require_once('../../config.inc.php'); -require_once('common.php'); -require_once('exec.inc.php'); -require_once("attachments.inc.php"); -require_once("specview.php"); -require_once("web_editor.php"); -require_once('event_api.php'); - -$cfg = getCfg(); -require_once(require_web_editor($cfg->editorCfg['type'])); - -if( $cfg->exec_cfg->enable_test_automation ) { - require_once('remote_exec.php'); -} - -// CRITIC: -// If call to testlinkInitPage() is done AFTER require_once for BTS -// log to event viewer fails, but log to file works ok -testlinkInitPage($db); -$templateCfg = templateConfiguration(); - -$tcversion_id = null; -$submitResult = null; -list($args,$its,$cts) = init_args($db,$cfg); - -$smarty = new TLSmarty(); -$smarty->assign('tsuite_info',null); - -$tree_mgr = new tree($db); -$tplan_mgr = new testplan($db); -$tcase_mgr = new testcase($db); -$exec_cfield_mgr = new exec_cfield_mgr($db,$args->tproject_id); -$fileRepo = tlAttachmentRepository::create($db); -$req_mgr = new requirement_mgr($db); - -$gui = initializeGui($db,$args,$cfg,$tplan_mgr,$tcase_mgr,$its,$cts); - -$_SESSION['history_on'] = $gui->history_on; -$attachmentInfos = null; - -$do_show_instructions = ($args->level == "" || $args->level == 'testproject') ? 1 : 0; -if ($do_show_instructions) { - show_instructions('executeTest'); - exit(); -} - -// Testplan executions and result archiving. -// Checks whether execute cases button was clicked -if($args->doExec == 1 && !is_null($args->tc_versions) && count($args->tc_versions)) { - $gui->remoteExecFeedback = launchRemoteExec($db,$args,$gui->tcasePrefix,$tplan_mgr,$tcase_mgr); -} - - -// link Update will be done on Context -// Context = testplan,platform (if any) -if( $args->linkLatestVersion && $args->level == 'testcase') { - $plat = $args->platform_id > 0 ? $args->platform_id : null; - $args->version_id = - $tcase_mgr->updateTPlanLinkToLatestTCV($args->TCVToUpdate,$args->tplan_id,$plat); -} - - -// LOAD What To Display -list($linked_tcversions,$itemSet) = - getLinkedItems($args,$gui->history_on,$cfg,$tcase_mgr,$tplan_mgr); - -$tcase_id = 0; -$userid_array = null; -if(!is_null($linked_tcversions)) { - - $items_to_exec = array(); - $_SESSION['s_lastAttachmentInfos'] = null; - if($args->level == 'testcase') { - // passed by reference to be updated inside function - // $gui, $args - $tcase = null; - list($tcase_id,$tcversion_id,$latestExecIDInContext,$hasCFOnExec) = - processTestCase($tcase,$gui,$args,$cfg,$linked_tcversions, - $tree_mgr,$tcase_mgr,$fileRepo); - } else { - processTestSuite($db,$gui,$args,$itemSet,$tree_mgr,$tcase_mgr,$fileRepo); - $tcase_id = $itemSet->tcase_id; - $tcversion_id = $itemSet->tcversion_id; - } - - // Send Event for Drawing UI from plugins - $ctx = array('tplan_id' => $args->tplan_id, - 'build_id' => $args->build_id, - 'tcase_id' => $tcase_id, - 'tcversion_id' => $tcversion_id); - - $gui->plugins = array(); - $gui->plugins['EVENT_TESTRUN_DISPLAY'] = - event_signal('EVENT_TESTRUN_DISPLAY', $ctx); - - // check if value is an array before calling implode to avoid warnings in event log - $gui->tcversionSet = is_array($tcversion_id) ? implode(',',$tcversion_id) : $tcversion_id; - - // will create a record even if the testcase version has not been executed (GET_NO_EXEC) - // - // Can be DONE JUST ONCE AFTER write results to DB - // -------------------------------------------------------------------------- - // Results to DB - // - // 20130917 - this implementation regarding save_results is confusing. - // why ? - // because in some situations args->save_results is a number (0) an in other is an array - // with just one element with key => test case version ID executed. - // - if ($args->doSave || $args->doNavigate || $args->saveStepsPartialExec) { - // this has to be done to do not break logic present on write_execution() - $args->save_results = $args->save_and_next ? $args->save_and_next : - ($args->save_results ? $args->save_results : $args->save_and_exit); - - - if( $args->save_results || $args->do_bulk_save) { - // Need to get Latest execution ID before writing - $lexidSysWide = 0; - if($args->copyIssues && $args->level == 'testcase') { - $lexidSysWide = $tcase_mgr->getSystemWideLastestExecutionID($args->version_id); - } - - $_REQUEST['save_results'] = $args->save_results; - - // Steps Partial Execution Feature - if (isset($_REQUEST['step_notes'])) { - $ctx = new stdClass(); - $ctx->testplan_id = $args->tplan_id; - $ctx->platform_id = $args->platform_id; - $ctx->build_id = $args->build_id; - - $tcase_mgr->deleteStepsPartialExec(array_keys($_REQUEST['step_notes']),$ctx); - } - - list($execSet,$gui->addIssueOp,$gui->uploadOp) = - write_execution($db,$args,$_REQUEST,$its); - - // Copy Attachments from latest exec ? - if($args->copyAttFromLEXEC && $cfg->exec_cfg->exec_mode->new_exec && - $args->level == 'testcase') { - - // we have got Latest Execution on Context on processTestCase() - if( $latestExecIDInContext > 0 ) { - - // need to copy : - // attachments at execution level - // attachments at step execution level - - // attachments at execution level - $fileRepo->copyAttachments($latestExecIDInContext, - $execSet[$tcversion_id],'executions'); - - // attachments at step execution level - $tbl = array(); - $tbl['exec_tcsteps'] = DB_TABLE_PREFIX . 'execution_tcsteps'; - $tbl['tcsteps'] = DB_TABLE_PREFIX . 'tcsteps'; - $sql = "SELECT step_number,tcstep_id, - EXTCS.id AS tcsexe_id +testcases_to_show + * + * Normally this script is called from the tree. + * Filters and other conditions (example display test cases just assigned to me,etc) + * can be applied, creating a set of test cases that can be used. + * Due to size restrictions on POST variables this info is transfered via $_SESSION. + * + * But because we have choosen to add access to this script from other features + * we have forgot to populate this info. + * This is the reason for several issues. + * The approach will be to understand who is the caller and apply different logics + * instead of recreate the logic to populate $_SESSION + * (I think this approach will be simpler). + * + * + * Note about step info + * is present in gui->map_last_exec + * + * + **/ +require_once '../../config.inc.php'; +require_once 'common.php'; +require_once 'exec.inc.php'; +require_once 'attachments.inc.php'; +require_once 'specview.php'; +require_once 'web_editor.php'; +require_once 'event_api.php'; + +$cfg = getCfg(); +require_once require_web_editor($cfg->editorCfg['type']); + +if ($cfg->exec_cfg->enable_test_automation) { + require_once 'remote_exec.php'; +} + +// CRITIC: +// If call to testlinkInitPage() is done AFTER require_once for BTS +// log to event viewer fails, but log to file works ok +testlinkInitPage($db); +$templateCfg = templateConfiguration(); + +$tcversion_id = null; +$submitResult = null; +list ($args, $its, $cts) = initArgs($db, $cfg); + +// the default -1 create an out of range error on TC execution without platform +if ($args->platform_id == - 1) { + $args->platform_id = 0; +} + +$smarty = new TLSmarty(); +$smarty->assign('tsuite_info', null); + +$tree_mgr = new tree($db); +$tplan_mgr = new testplan($db); +$tcaseMgr = new testcase($db); +$exec_cfield_mgr = new exec_cfield_mgr($db, $args->tproject_id); +$fileRepo = tlAttachmentRepository::create($db); +$req_mgr = new requirement_mgr($db); + +$gui = initializeGui($db, $args, $cfg, $tplan_mgr, $tcaseMgr, $its, $cts); + +$_SESSION['history_on'] = $gui->history_on; +$attachmentInfos = null; + +$do_show_instructions = ($args->level == "" || $args->level == 'testproject') ? 1 : 0; +if ($do_show_instructions) { + show_instructions('executeTest'); + exit(); +} + +// Testplan executions and result archiving. +// Checks whether execute cases button was clicked +if ($args->doExec == 1 && ! is_null($args->tc_versions) && + count($args->tc_versions)) { + $gui->remoteExecFeedback = launchRemoteExec($db, $args, $gui->tcasePrefix, + $tplan_mgr, $tcaseMgr); +} + +// link Update will be done on Context +// Context = testplan +// +// @20210901 -> CRITIC +// because we do not allow different versions on different platforms +// for same test plan -> platform MUST NOT BE USED +if ($args->linkLatestVersion && $args->level == 'testcase') { + $args->version_id = $tcaseMgr->updateTPlanLinkToLatestTCV( + $args->TCVToUpdate, $args->tplan_id); +} + +// LOAD What To Display +list ($linked_tcversions, $itemSet) = getLinkedItems($args, $gui->history_on, + $cfg, $tcaseMgr, $tplan_mgr); + +$tcase_id = 0; +$userid_array = null; +if (! is_null($linked_tcversions)) { + + $items_to_exec = array(); + $_SESSION['s_lastAttachmentInfos'] = null; + if ($args->level == 'testcase') { + // passed by reference to be updated inside function + // $gui, $args + $tcase = null; + list ($tcase_id, $tcversion_id, $latestExecIDInContext, $hasCFOnExec) = processTestCase( + $tcase, $gui, $args, $cfg, $linked_tcversions, $tree_mgr, $tcaseMgr, + $fileRepo); + } else { + processTestSuite($db, $gui, $args, $itemSet, $tree_mgr, $tcaseMgr, + $fileRepo); + $tcase_id = $itemSet->tcase_id; + $tcversion_id = $itemSet->tcversion_id; + } + + // Send Event for Drawing UI from plugins + $ctx = array( + 'tplan_id' => $args->tplan_id, + 'build_id' => $args->build_id, + 'tcase_id' => $tcase_id, + 'tcversion_id' => $tcversion_id + ); + + $gui->plugins = array(); + $gui->plugins['EVENT_TESTRUN_DISPLAY'] = event_signal( + 'EVENT_TESTRUN_DISPLAY', $ctx); + + // check if value is an array before calling implode to avoid warnings in event log + $gui->tcversionSet = is_array($tcversion_id) ? implode(',', $tcversion_id) : $tcversion_id; + + // will create a record even if the testcase version has not been executed (GET_NO_EXEC) + // + // Can be DONE JUST ONCE AFTER write results to DB + // -------------------------------------------------------------------------- + // Results to DB + // + // 20130917 - this implementation regarding save_results is confusing. + // why ? + // because in some situations args->save_results is a number (0) an in other is an array + // with just one element with key => test case version ID executed. + // + if ($args->doSave || $args->doNavigate || $args->saveStepsPartialExec) { + // this has to be done to do not break logic present on write_execution() + $args->save_results = $args->save_and_next ? $args->save_and_next : ($args->save_results ? $args->save_results : $args->save_and_exit); + + if ($args->save_results || $args->do_bulk_save) { + // Need to get Latest execution ID before writing + $lexidSysWide = 0; + if ($args->copyIssues && $args->level == 'testcase') { + $lexidSysWide = $tcaseMgr->getSystemWideLastestExecutionID( + $args->version_id); + } + + $_REQUEST['save_results'] = $args->save_results; + + // Steps Partial Execution Feature + if (isset($_REQUEST['step_notes'])) { + $ctx = new stdClass(); + $ctx->testplan_id = $args->tplan_id; + $ctx->platform_id = $args->platform_id; + $ctx->build_id = $args->build_id; + + $tcaseMgr->deleteStepsPartialExec( + array_keys($_REQUEST['step_notes']), $ctx); + } + + list ($execSet, $gui->addIssueOp, $gui->uploadOp) = write_execution( + $db, $args, $_REQUEST, $its); + + // Copy Attachments from latest exec ? + // we have got Latest Execution on Context on processTestCase() + if ($args->copyAttFromLEXEC && $cfg->exec_cfg->exec_mode->new_exec && + $args->level == 'testcase' && $latestExecIDInContext > 0) { + // need to copy : + // attachments at execution level + // attachments at step execution level + + // attachments at execution level + $fileRepo->copyAttachments($latestExecIDInContext, + $execSet[$tcversion_id], 'executions'); + + // attachments at step execution level + $tbl = array(); + $tbl['exec_tcsteps'] = DB_TABLE_PREFIX . 'execution_tcsteps'; + $tbl['tcsteps'] = DB_TABLE_PREFIX . 'tcsteps'; + $sql = "SELECT step_number,tcstep_id, + EXTCS.id AS tcsexe_id FROM {$tbl['tcsteps']} TCS JOIN {$tbl['exec_tcsteps']} EXTCS ON - EXTCS.tcstep_id = TCS.id - WHERE EXTCS.execution_id = "; - - $from = (array)$db->fetchRowsIntoMap($sql . $latestExecIDInContext,'step_number'); - - $to = (array)$db->fetchRowsIntoMap($sql . $execSet[$tcversion_id],'step_number'); - - foreach($from as $step_num => $sxelem) { - if( isset($to[$step_num]) ) { - $fileRepo->copyAttachments($sxelem['tcsexe_id'], - $to[$step_num]['tcsexe_id'],'execution_tcsteps'); - } - } - } - } - - - if($args->assignTask) { - $fid = $tplan_mgr->getFeatureID($args->tplan_id,$args->platform_id,$args->version_id); - $taskMgr = new assignment_mgr($db); - $taskDomain = $taskMgr->get_available_types(); - $taskStatusDomain = $taskMgr->get_available_status(); - - $fmap[$fid]['user_id'] = $fmap[$fid]['assigner_id'] = $args->user_id; - $fmap[$fid]['build_id'] = $args->build_id; - $fmap[$fid]['type'] = $taskDomain['testcase_execution']['id']; - $fmap[$fid]['status'] = $taskStatusDomain['open']['id']; - $taskMgr->assign($fmap); - } - - if($lexidSysWide > 0 && $args->copyIssues && $args->level == 'testcase') { - copyIssues($db,$lexidSysWide,$execSet[$args->version_id]); - } - - // Propagate events - $ctx = array('id' => $execSet[$tcversion_id], - 'tplan_id' => $args->tplan_id, - 'build_id' => $args->build_id, - 'tcase_id' => $tcase_id, - 'status' => $args->statusSingle[$args->version_id], - 'directLink' => $args->direct_link); - event_signal('EVENT_EXECUTE_TEST', $ctx); - $tc_info = $tcase_mgr->getExternalID($tcase_id); - $tp_info = $tplan_mgr->get_by_id($args->tplan_id); - $build_info = $tplan_mgr->get_build_by_id($args->tplan_id,$args->build_id); - - logAuditEvent(TLS("audit_exec_saved",$tc_info[0],$build_info['name'],$tp_info['name']),"CREATE",$execSet[$tcversion_id],"execution"); - } - - // Need to re-read to update test case status - if ($args->save_and_next || $args->doMoveNext || $args->doMovePrevious) { - $nextInChain = -1; - if( $cfg->exec_cfg->exec_mode->save_and_move == 'unlimited' ) { - if( $args->caller == 'tcAssignedToMe') { - $optz = array('order_by' => 'ORDER BY TPTCV.node_order'); - $filters['build_id'] = $args->build_id; - - $xx = $tcase_mgr->get_assigned_to_user( - $args->user_id, $args->tproject_id, - array($args->tplan_id), $optz, $filters); - $xx = current($xx); - - // key test case id - // inside an idx array - $args->testcases_to_show = array_keys($xx); - } - - $chainLen = count($args->testcases_to_show); - foreach($args->testcases_to_show as $ix => $val) { - if( $val == $args->tc_id) { - $nextInChain = $ix+1; - if($nextInChain == $chainLen) { - $nextInChain = 0; - } - break; - } - } - } - - // IMPORTANT DEVELOPMENT NOTICE - // Normally this script is called from the tree. - // Filters and other conditions (example display test cases just assigned to me,etc) - // can be applied, creating a set of test cases that can be used. - // Due to size restrictions on POST variables this info is transfered via $_SESSION. - // - // But because we have choosen to add access to this script from other features - // we have forgot to populate this info. - // This is the reason for several issues. - // The approach will be to understand who is the caller and apply different logics - // instead of recreate the logic to populate $_SESSION (I think this approach - // will be simpler). - $doSingleStep = is_null($args->testcases_to_show); - $args->testcases_to_show = (array)$args->testcases_to_show; - - $opt4sibling = array('move' => $args->moveTowards); - switch ($args->caller) { - case 'tcAssignedToMe': - $doSingleStep = true; - $opt4sibling['assigned_to'] = array('user_id' => $args->user_id, 'build_id' => $args->build_id); - break; - - default: - break; - } - - switch($cfg->exec_cfg->exec_mode->save_and_move) { - case 'unlimited': - // get position on chain - $opx = array('tcase_id' => - $args->testcases_to_show[$nextInChain]); - $nextItem = $tplan_mgr->get_linked_tcvid($args->tplan_id,$args->platform_id,$opx); - $nextItem = current($nextItem); - break; - - case 'limited': - $nextItem = $tplan_mgr->getTestCaseNextSibling($args->tplan_id,$tcversion_id,$args->platform_id,$opt4sibling); - if(!$doSingleStep) - { - while (!is_null($nextItem) && !in_array($nextItem['tcase_id'], $args->testcases_to_show)) - { - $nextItem = $tplan_mgr->getTestCaseNextSibling($args->tplan_id,$nextItem['tcversion_id'], - $args->platform_id,$opt4sibling); - } - } - break; - } // cfg - - if( !is_null($nextItem) ) - { - $tcase_id = $nextItem['tcase_id']; - $tcversion_id = $nextItem['tcversion_id']; - - // Save and Next - Issues with display CF for test plan design - always EMPTY - // need info about this test case => need to update linked_tcversions info - $identity = array('id' => $nextItem['tcase_id'], 'version_id' => $nextItem['tcversion_id']); - list($lt,$xdm) = getLinkedItems($args,$gui->history_on,$cfg,$tcase_mgr,$tplan_mgr,$identity); - processTestCase($nextItem,$gui,$args,$cfg,$lt,$tree_mgr,$tcase_mgr,$fileRepo); - } - } - else if($args->save_and_exit) { - $args->reload_caller = true; - } - else if ($args->saveStepsPartialExec) { - $partialExec = array("notes" => $_REQUEST['step_notes'], - "status" => $_REQUEST['step_status'] ); - - $ctx = new stdClass(); - $ctx->testplan_id = $args->tplan_id; - $ctx->platform_id = $args->platform_id; - $ctx->build_id = $args->build_id; - $ctx->tester_id = $args->user_id; - $tcase_mgr->saveStepsPartialExec($partialExec,$ctx); - } - } - - if(!$args->reload_caller) { - if ($args->doDelete) { - $dummy = delete_execution($db,$args->exec_to_delete); - if ($dummy){ - $tc_info = $tcase_mgr->getExternalID($tcase_id); - $tp_info = $tplan_mgr->get_by_id($args->tplan_id); - $build_info = $tplan_mgr->get_build_by_id($args->tplan_id,$args->build_id); - logAuditEvent(TLS("audit_exec_deleted",$tc_info[0],$build_info['name'],$tp_info['name']),"DELETE",$args->exec_to_delete,"execution"); - } - } - - // Important Notice: - // $tcase_id and $tcversions_id, can be ARRAYS - // when user enable bulk execution - if( is_array($tcase_id)) { - $tcase_id = array_intersect($tcase_id, $args->testcases_to_show); - } - - $gui->map_last_exec = - getLatestExec($db,$tcase_id,$tcversion_id,$gui,$args,$tcase_mgr); - - $gui->map_last_exec_any_build = null; - - - // need to get step info from gui - $stepSet = array(); - foreach($gui->map_last_exec as $tcID => $dummy) { - if( null != $gui->map_last_exec[$tcID]['steps'] ) { - foreach ($gui->map_last_exec[$tcID]['steps'] as $step) { - $stepSet[] = $step["id"]; - } - } - } - - if( count($stepSet) > 0 ) { - // test case version under exec has steps - $ctx = new stdClass(); - $ctx->testplan_id = $args->tplan_id; - $ctx->platform_id = $args->platform_id; - $ctx->build_id = $args->build_id; - - $gui->stepsPartialExec = - $tcase_mgr->getStepsPartialExec($stepSet,$ctx); - - if( null != $gui->stepsPartialExec ) { - // will reload it! - $kij = current(array_keys($gui->map_last_exec)); - $cucu = &$gui->map_last_exec[$kij]; - foreach($cucu['steps'] as $ccx => $se) { - $stepID = $se['id']; - if( isset($gui->stepsPartialExec[$stepID]) ) { - $cucu['steps'][$ccx]['execution_notes'] = - $gui->stepsPartialExec[$stepID]['notes']; - - $cucu['steps'][$ccx]['execution_status'] = - $gui->stepsPartialExec[$stepID]['status']; - } - } - } - } - $testerIdKey = 'tester_id'; - - $gui->other_execs=null; - $testerid = null; - - if($args->level == 'testcase') { - // @TODO 20090815 - franciscom check what to do with platform - if( $cfg->exec_cfg->show_last_exec_any_build ) { - $options=array('getNoExecutions' => 1, 'groupByBuild' => 0); - $gui->map_last_exec_any_build = $tcase_mgr->get_last_execution($tcase_id,$tcversion_id,$args->tplan_id,testcase::ANY_BUILD, - $args->platform_id,$options); - - // Get UserID and Updater ID for current Version - $tc_current = $gui->map_last_exec_any_build; - foreach ($tc_current as $key => $value) { - $testerid = $value[$testerIdKey]; - $userid_array[$testerid] = $testerid; - } - } - - $gui->req_details = null; - if( $args->reqEnabled ) { - $gui->req_details = $req_mgr->getActiveForTCVersion($tcversion_id); - } - - $idCard = array('tcase_id' => $tcase_id, 'tcversion_id' => $tcversion_id); - $gui->relations = $tcase_mgr->getTCVersionRelations($idCard); - - $gui->kw = $tcase_mgr->getKeywordsByIdCard($idCard,array('output' => 'kwfull')); - - if(!is_null($cts)) { - $gui->scripts[$tcversion_id]=$tcase_mgr->get_scripts_for_testcase($cts, $tcversion_id); - } - - $gui->other_execs = getOtherExecutions($db,$tcase_id,$tcversion_id,$gui,$args,$cfg,$tcase_mgr); - - // Get attachment,bugs, etc - if(!is_null($gui->other_execs)) { - //Get the Tester ID for all previous executions - foreach ($gui->other_execs as $key => $execution) { - foreach ($execution as $singleExecution) { - $testerid = $singleExecution[$testerIdKey]; - $userid_array[$testerid] = $testerid; - } - } - $other_info = exec_additional_info($db,$fileRepo,$tcase_mgr,$gui->other_execs, - $args->tplan_id,$args->tproject_id, - $args->issue_tracker_enabled,$its); - - $gui->attachments=$other_info['attachment']; - $gui->bugs=$other_info['bugs']; - $gui->other_exec_cfields=$other_info['cfexec_values']; - - // this piece of code is useful to avoid error on smarty template due to undefined value - if( is_array($tcversion_id) && (count($gui->other_execs) != count($gui->map_last_exec)) ) { - foreach($tcversion_id as $version_id) { - if( !isset($gui->other_execs[$version_id]) ) { - $gui->other_execs[$version_id]=null; - } - } - } - - } // if(!is_null($gui->other_execs)) - } - } -} // if(!is_null($linked_tcversions)) - - -if($args->reload_caller) { - windowCloseAndOpenerReload(); - exit(); -} else { - // Removing duplicate and NULL id's - unset($userid_array['']); - $userSet = null; - if ($userid_array) { - foreach($userid_array as $value) { - $userSet[] = $value; - } - } - - $gui->headsUpTSuite = - smarty_assign_tsuite_info($smarty,$tree_mgr,$tcase_id,$args->tproject_id,$cfg); - if ($args->doSave || $args->saveStepsPartialExec) { - $gui->headsUpTSuite = false; - } - - // Bulk is possible when test suite is selected (and is allowed in config) - if( $gui->can_use_bulk_op = ($args->level == 'testsuite') ) { - $xx = null; - if( property_exists($gui, 'execution_time_cfields') ) { - $xx = current((array)$gui->execution_time_cfields); - } - - $gui->execution_time_cfields = null; - if( !is_null($xx) ) { - $gui->execution_time_cfields[0] = $xx; - } - } - - // has sense only if there are cf for execution - // may be can improve check - if( $gui->can_use_bulk_op == false && - $cfg->exec_cfg->exec_mode->new_exec == 'latest' ) { - - list($tcase_id,$tcversion_id,$latestExecIDInContext,$hasCFOnExec) = - processTestCase($tcase,$gui,$args,$cfg,$linked_tcversions, - $tree_mgr,$tcase_mgr,$fileRepo); - - if($latestExecIDInContext > 0) { - $tbl = DB_TABLE_PREFIX . 'executions'; - $sql = "SELECT notes FROM $tbl - WHERE id = $latestExecIDInContext"; - $rs = $db->get_recordset($sql); - $gui->lexNotes = $rs != null ? $rs[0]['notes'] : null; - } - - } - - initWebEditors($gui,$cfg,$_SESSION['basehref']); - - - - // To silence smarty errors - // future must be initialized in a right way - $smarty->assign('test_automation_enabled',0); - $smarty->assign('gui',$gui); - $smarty->assign('cfg',$cfg); - $smarty->assign('users',tlUser::getByIDs($db,$userSet)); - - $smarty->display($templateCfg->template_dir . $templateCfg->default_template); -} - -/** - * - * - */ -function init_args(&$dbHandler,$cfgObj) { - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - // Settings and Filters that we put on session to create some - // sort of persistent scope, because we have had issues when passing this info - // using GET mode (size limits) - // - // we get info about build_id, platform_id, etc ... - getSettingsAndFilters($args); - manageCookies($args,$cfgObj); - - // need to comunicate with left frame, will do via $_SESSION and form_token - if( ($args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0) > 0 ) - { - // do not understand why this do not works OK - // $_SESSION[$args->treeFormToken]['loadExecDashboard'] = false; - $_SESSION['loadExecDashboard'][$args->treeFormToken] = false; - } - - - $args->followTheWhiteRabbit = isset($_REQUEST['followTheWhiteRabbit']) ? 1 : 0; - if(is_null($args->refreshTree)) { - $args->refreshTree = isset($_REQUEST['refresh_tree']) ? intval($_REQUEST['refresh_tree']) : 0; - } - - $args->basehref = $_SESSION['basehref']; - $args->assignTask = isset($_REQUEST['assignTask']) ? 1: 0; - $args->createIssue = isset($_REQUEST['createIssue']) ? 1: 0; - $args->copyIssues = isset($_REQUEST['copyIssues']) ? 1: 0; - $args->copyAttFromLEXEC = isset($_REQUEST['copyAttFromLEXEC']) ? 1: 0; - - - $args->tc_id = null; - $args->tsuite_id = null; - $args->user = $_SESSION['currentUser']; - $args->user_id = intval($args->user->dbID); - $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; - - $args->caller = isset($_REQUEST['caller']) ? $_REQUEST['caller'] : 'exec_feature'; - $args->reload_caller = false; - - $args->doExec = isset($_REQUEST['execute_cases']) ? 1 : 0; - $args->doDelete = isset($_REQUEST['do_delete']) ? $_REQUEST['do_delete'] : 0; - - $args->doMoveNext = isset($_REQUEST['move2next']) ? 1 : 0; - - $args->doMovePrevious = isset($_REQUEST['move2previous']) ? $_REQUEST['move2previous'] : 0; - $args->moveTowards = $args->doMoveNext ? 'forward' : ($args->doMovePrevious ? 'backward' : null); - - // can be a list, will arrive via form POST - $args->tc_versions = isset($_REQUEST['tc_version']) ? $_REQUEST['tc_version'] : null; - - // it's a submit button! - $args->saveStepsPartialExec = isset($_REQUEST['saveStepsPartialExec']); - - $key2loop = array('level' => '','status' => null, 'statusSingle' => null, - 'do_bulk_save' => 0,'save_results' => 0,'save_and_next' => 0, - 'save_and_exit' => 0); - foreach($key2loop as $key => $value) { - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $value; - } - - $args->doSave = $args->save_results || $args->save_and_next || - $args->save_and_exit || $args->do_bulk_save; - - $args->doNavigate = $args->doMoveNext || $args->doMovePrevious; - - - // See details on: "When nullify filter_status - 20080504" in this file - if( $args->level == 'testcase' || is_null($args->filter_status) || - (!is_array($args->filter_status) && trim($args->filter_status)=='') - ) { - $args->filter_status = null; - } - else { - // 20130306 - franciscom - // This (without the strlen() check) generated issue 5541: When "Result" filter is used ... - // at least when result DIFFERENT that NOT RUN is used on filter - // - // 20120616 - franciscom - // some strange thing to investigate, seems that unserialize is invoked - // under the hood when getting data from $_REQUEST, then this piece - // of code not only will be useless BUT WRONG, because will try - // to unserialize something that IS NOT SERIALIZED!!!! - - // After TICKET 6651, may be need to limit size of $args->filter_status - if(is_string($args->filter_status) && strlen($args->filter_status) > 1) { - $args->filter_status = json_decode($args->filter_status); - } - } - - - switch($args->level) { - case 'testcase': - $args->tc_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null; - if( !is_null($args->tc_versions) ) { - $args->tc_id = current($args->tc_versions); - $args->id = $args->tc_id; - $args->version_id = key($args->tc_versions); - } - $args->tsuite_id = null; - break; - - case 'testsuite': - $args->tsuite_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null; - $args->tc_id = null; - break; - } - - // - $args->updateTCVToThis = isset($_REQUEST['updateTCVToThis']) ? $_REQUEST['updateTCVToThis'] : null; - if( null != $args->updateTCVToThis ) { - $args->version_id = intval($args->updateTCVToThis); - } - - - $args->tsuitesInBranch = null; - if( !is_null($args->tsuite_id) ) { - // will get all test suites in this branch, in order to limit amount of data returned - // by functions/method that collect linked tcversions - // THIS COLLECT ONLY FIRST LEVEL UNDER test suite, do not do deep search - // Need to understand is still needed - $tsuite_mgr = new testsuite($dbHandler); - $xx = $tsuite_mgr->get_children($args->tsuite_id,array('details' => 'id')); - $ldx = count($xx); - $xx[$ldx] = $args->tsuite_id; - $args->tsuitesInBranch = $xx; - unset($tsuite_mgr); - } - - - // TICKET 5630: Test Results by direct link ... - $args->tplan_id = intval(isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); - $args->tproject_id = intval(isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']); - - if($args->tproject_id <= 0) { - $tree_mgr = new tree($dbHandler); - $dm = $tree_mgr->get_node_hierarchy_info($args->tplan_id); - $args->tproject_id = $dm['parent_id']; - } - - - $args->addLinkToTL = isset($_REQUEST['addLinkToTL']) ? TRUE : FALSE; - $args->addLinkToTLPrintView = isset($_REQUEST['addLinkToTLPrintView']) ? TRUE : FALSE; - - // Do this only on single execution mode - // get issue tracker config and object to manage TestLink - BTS integration - $args->itsCfg = null; - $its = null; - - $tproject_mgr = new testproject($dbHandler); - $info = $tproject_mgr->get_by_id($args->tproject_id); - $args->reqEnabled = intval($info['option_reqs']); - - unset($tproject_mgr); - $bug_summary['minLengh'] = 1; - $bug_summary['maxLengh'] = 1; - - if( ($args->issue_tracker_enabled = $info['issue_tracker_enabled']) ) { - $it_mgr = new tlIssueTracker($dbHandler); - $args->itsCfg = $it_mgr->getLinkedTo($args->tproject_id); - $its = $it_mgr->getInterfaceObject($args->tproject_id); - - if(!is_null($args->itsCfg) && !is_null($its)) { - $bug_summary['maxLengh'] = $its->getBugSummaryMaxLength(); - } - unset($it_mgr); - } - - initArgsIssueOnTestCase($args,$bug_summary); - - initArgsIssueOnSteps($args,$bug_summary); - - // get code tracker config and object to manage TestLink - CTS integration - $args->ctsCfg = null; - $cts = null; - - if( ($args->codeTrackerEnabled = intval($info['code_tracker_enabled'])) ) { - $ct_mgr = new tlCodeTracker($dbHandler); - $args->ctsCfg = $ct_mgr->getLinkedTo($args->tproject_id); - $cts = $ct_mgr->getInterfaceObject($args->tproject_id); - - unset($ct_mgr); - } - - // is a submit button - $prop = 'linkLatestVersion'; - $args->$prop = isset($_REQUEST[$prop]); - - $prop = 'TCVToUpdate'; - $args->$prop = intval(isset($_REQUEST[$prop]) ? $_REQUEST[$prop] : 0); - - - $prop = 'updateTCVToThis'; - $args->$prop = intval(isset($_REQUEST[$prop]) ? $_REQUEST[$prop] : 0); - - return array($args,$its,$cts); -} - -/** - * - * - */ -function initArgsIssueOnTestCase(&$argsObj,$bugSummaryProp) { - - $inputCfg = array("bug_notes" => array("POST",tlInputParameter::STRING_N), - "issueType" => array("POST",tlInputParameter::INT_N), - "issuePriority" => array("POST",tlInputParameter::INT_N), - "artifactComponent" => array("POST",tlInputParameter::ARRAY_INT), - "artifactVersion" => array("POST",tlInputParameter::ARRAY_INT)); - - $inputCfg["bug_summary"] = array("POST",tlInputParameter::STRING_N); - - // hmm this MAGIC needs to be commented - if(!$argsObj->do_bulk_save) { - $inputCfg["bug_summary"][2] = $bugSummaryProp['minLengh']; - $inputCfg["bug_summary"][3] = $bugSummaryProp['maxLengh']; - } - - I_PARAMS($inputCfg,$argsObj); - -} - -/** - * - * - */ -function initArgsIssueOnSteps(&$argsObj,$bugSummaryProp) { - $arrayOfInt = array("POST",tlInputParameter::ARRAY_INT); - - $cfg = array("issueBodyForStep" => array("POST",tlInputParameter::ARRAY_STRING_N), - "issueTypeForStep" => $arrayOfInt, - "issuePriorityForStep" => $arrayOfInt); - - $cfg["issueSummaryForStep"] = array("POST",tlInputParameter::ARRAY_STRING_N); - - // hmm this MAGIC needs to be commented - if(!$argsObj->do_bulk_save) { - $cfg["issueSummaryForStep"][2] = $bugSummaryProp['minLengh']; - $cfg["issueSummaryForStep"][3] = $bugSummaryProp['maxLengh']; - } - - I_PARAMS($cfg,$argsObj); - - // Special - // Array of Check Boxes: - // 'issueForStep','addLinkToTLForStep' - $sk = array('issueForStep','addLinkToTLForStep', - 'artifactComponentForStep','artifactVersionForStep'); - - foreach($sk as $kt) { - $argsObj->$kt = null; - if(isset($_REQUEST[$kt])) { - $argsObj->$kt = $_REQUEST[$kt]; - } - } - -} - -/* - function: - - args : - - returns: - -*/ -function manage_history_on($hash_REQUEST,$hash_SESSION, - $exec_cfg,$btn_on_name,$btn_off_name,$hidden_on_name) -{ - - - if( isset($hash_REQUEST[$btn_on_name]) ) - { - $history_on = true; - } - elseif(isset($_REQUEST[$btn_off_name])) - { - $history_on = false; - } - elseif (isset($_REQUEST[$hidden_on_name])) - { - $history_on = $_REQUEST[$hidden_on_name]; - } - elseif (isset($_SESSION[$hidden_on_name])) - { - $history_on = $_SESSION[$hidden_on_name]; - } - else - { - $history_on = $exec_cfg->history_on; - } - - return $history_on ? true : false; -} - -/* - function: get_ts_name_details - - args : - - returns: map with key=TCID - values= assoc_array([tsuite_id => 5341 - [details] => my detailas ts1 - [tcid] => 5343 - [tsuite_name] => ts1) -*/ -function get_ts_name_details(&$db,$tcase_id) { - $tables = array(); - $tables['testsuites'] = DB_TABLE_PREFIX . 'testsuites'; - $tables['nodes_hierarchy'] = DB_TABLE_PREFIX . 'nodes_hierarchy'; - - - $rs = ''; - $do_query = true; - $sql = "SELECT TS.id AS tsuite_id, TS.details, - NHA.id AS tc_id, NHB.name AS tsuite_name - FROM {$tables['testsuites']} TS, - {$tables['nodes_hierarchy']} NHA, + EXTCS.tcstep_id = TCS.id + WHERE EXTCS.execution_id = "; + + $from = (array) $db->fetchRowsIntoMap( + $sql . $latestExecIDInContext, 'step_number'); + + $to = (array) $db->fetchRowsIntoMap( + $sql . $execSet[$tcversion_id], 'step_number'); + + foreach ($from as $step_num => $sxelem) { + if (isset($to[$step_num])) { + $fileRepo->copyAttachments($sxelem['tcsexe_id'], + $to[$step_num]['tcsexe_id'], 'execution_tcsteps'); + } + } + } + + if ($args->assignTask) { + $fid = $tplan_mgr->getFeatureID($args->tplan_id, + $args->platform_id, $args->version_id); + $taskMgr = new assignment_mgr($db); + $taskDomain = $taskMgr->get_available_types(); + $taskStatusDomain = $taskMgr->get_available_status(); + + $fmap[$fid]['user_id'] = $fmap[$fid]['assigner_id'] = $args->user_id; + $fmap[$fid]['build_id'] = $args->build_id; + $fmap[$fid]['type'] = $taskDomain['testcase_execution']['id']; + $fmap[$fid]['status'] = $taskStatusDomain['open']['id']; + $taskMgr->assign($fmap); + } + + if ($lexidSysWide > 0 && $args->copyIssues && + $args->level == 'testcase') { + copyIssues($db, $lexidSysWide, $execSet[$args->version_id]); + } + + if ($args->level == 'testcase') { + // Propagate events + $ctx = array( + 'id' => $execSet[$tcversion_id], + 'tplan_id' => $args->tplan_id, + 'build_id' => $args->build_id, + 'tcase_id' => $tcase_id, + 'status' => $args->statusSingle[$args->version_id], + 'directLink' => $args->direct_link + ); + event_signal('EVENT_EXECUTE_TEST', $ctx); + $tc_info = $tcaseMgr->getExternalID($tcase_id); + $tp_info = $tplan_mgr->get_by_id($args->tplan_id); + $build_info = $tplan_mgr->get_build_by_id($args->tplan_id, + $args->build_id); + + logAuditEvent( + TLS("audit_exec_saved", $tc_info[0], $build_info['name'], + $tp_info['name']), "CREATE", $execSet[$tcversion_id], + "execution"); + } + } + + // Need to re-read to update test case status + if ($args->save_and_next || $args->doMoveNext || $args->doMovePrevious) { + $nextInChain = - 1; + if ($cfg->exec_cfg->exec_mode->save_and_move == 'unlimited') { + if ($args->caller == 'tcAssignedToMe') { + $optz = array( + 'order_by' => 'ORDER BY TPTCV.node_order' + ); + $filters['build_id'] = $args->build_id; + + $xx = $tcaseMgr->getAssignedToUser($args->user_id, + $args->tproject_id, array( + $args->tplan_id + ), $optz, $filters); + $xx = current($xx); + + // key test case id + // inside an idx array + $args->testcases_to_show = array_keys($xx); + } + + $chainLen = count($args->testcases_to_show); + foreach ($args->testcases_to_show as $ix => $val) { + if ($val == $args->tc_id) { + $nextInChain = $ix + 1; + if ($nextInChain == $chainLen) { + $nextInChain = 0; + } + break; + } + } + } + + // IMPORTANT DEVELOPMENT NOTICE + // Normally this script is called from the tree. + // Filters and other conditions (example display test cases just assigned to me,etc) + // can be applied, creating a set of test cases that can be used. + // Due to size restrictions on POST variables this info is transfered via $_SESSION. + // + // But because we have choosen to add access to this script from other features + // we have forgot to populate this info. + // This is the reason for several issues. + // The approach will be to understand who is the caller and apply different logics + // instead of recreate the logic to populate $_SESSION (I think this approach + // will be simpler). + $doSingleStep = is_null($args->testcases_to_show); + $args->testcases_to_show = (array) $args->testcases_to_show; + + $opt4sibling = array( + 'move' => $args->moveTowards + ); + switch ($args->caller) { + case 'tcAssignedToMe': + $doSingleStep = true; + $opt4sibling['assigned_to'] = array( + 'user_id' => $args->user_id, + 'build_id' => $args->build_id + ); + break; + + default: + break; + } + + switch ($cfg->exec_cfg->exec_mode->save_and_move) { + case 'unlimited': + // get position on chain + $opx = array( + 'tcase_id' => $args->testcases_to_show[$nextInChain] + ); + $nextItem = $tplan_mgr->get_linked_tcvid($args->tplan_id, + $args->platform_id, $opx); + $nextItem = current($nextItem); + break; + + case 'limited': + $nextItem = $tplan_mgr->getTestCaseNextSibling( + $args->tplan_id, $tcversion_id, $args->platform_id, + $opt4sibling); + if (! $doSingleStep) { + while (! is_null($nextItem) && + ! in_array($nextItem['tcase_id'], + $args->testcases_to_show)) { + $nextItem = $tplan_mgr->getTestCaseNextSibling( + $args->tplan_id, $nextItem['tcversion_id'], + $args->platform_id, $opt4sibling); + } + } + break; + } // cfg + + if (! is_null($nextItem)) { + $tcase_id = $nextItem['tcase_id']; + $tcversion_id = $nextItem['tcversion_id']; + + // Save and Next - Issues with display CF for test plan design - always EMPTY + // need info about this test case => need to update linked_tcversions info + $identity = array( + 'id' => $nextItem['tcase_id'], + 'version_id' => $nextItem['tcversion_id'] + ); + list ($lt, $xdm) = getLinkedItems($args, $gui->history_on, $cfg, + $tcaseMgr, $tplan_mgr, $identity); + processTestCase($nextItem, $gui, $args, $cfg, $lt, $tree_mgr, + $tcaseMgr, $fileRepo); + } + } elseif ($args->save_and_exit) { + $args->reload_caller = true; + } elseif ($args->saveStepsPartialExec) { + $partialExec = array( + "notes" => $_REQUEST['step_notes'], + "status" => $_REQUEST['step_status'] + ); + + $ctx = new stdClass(); + $ctx->testplan_id = $args->tplan_id; + $ctx->platform_id = $args->platform_id; + $ctx->build_id = $args->build_id; + $ctx->tester_id = $args->user_id; + $tcaseMgr->saveStepsPartialExec($partialExec, $ctx); + } + } + + if (! $args->reload_caller) { + if ($args->doDelete) { + $dummy = delete_execution($db, $args->exec_to_delete); + if ($dummy) { + $tc_info = $tcaseMgr->getExternalID($tcase_id); + $tp_info = $tplan_mgr->get_by_id($args->tplan_id); + $build_info = $tplan_mgr->get_build_by_id($args->tplan_id, + $args->build_id); + logAuditEvent( + TLS("audit_exec_deleted", $tc_info[0], $build_info['name'], + $tp_info['name']), "DELETE", $args->exec_to_delete, + "execution"); + } + } + + // Important Notice: + // $tcase_id and $tcversions_id, can be ARRAYS + // when user enable bulk execution + if (is_array($tcase_id)) { + $tcase_id = array_intersect($tcase_id, $args->testcases_to_show); + } + + $gui->map_last_exec = getLatestExec($db, $tcase_id, $tcversion_id, $gui, + $args, $tcaseMgr); + + $gui->map_last_exec_any_build = null; + + // need to get step info from gui + $stepSet = array(); + foreach ($gui->map_last_exec as $tcID => $dummy) { + if (null != $gui->map_last_exec[$tcID]['steps']) { + foreach ($gui->map_last_exec[$tcID]['steps'] as $step) { + $stepSet[] = $step["id"]; + } + } + } + + if (! empty($stepSet)) { + // test case version under exec has steps + $ctx = new stdClass(); + $ctx->testplan_id = $args->tplan_id; + $ctx->platform_id = $args->platform_id; + $ctx->build_id = $args->build_id; + + $gui->stepsPartialExec = $tcaseMgr->getStepsPartialExec($stepSet, + $ctx); + + if (null != $gui->stepsPartialExec) { + // will reload it! + $kij = current(array_keys($gui->map_last_exec)); + $cucu = &$gui->map_last_exec[$kij]; + foreach ($cucu['steps'] as $ccx => $se) { + $stepID = $se['id']; + if (isset($gui->stepsPartialExec[$stepID])) { + $cucu['steps'][$ccx]['execution_notes'] = $gui->stepsPartialExec[$stepID]['notes']; + + $cucu['steps'][$ccx]['execution_status'] = $gui->stepsPartialExec[$stepID]['status']; + } + } + } + } + $testerIdKey = 'tester_id'; + + $gui->other_execs = null; + $testerid = null; + + if ($args->level == 'testcase') { + // @TODO 20090815 - franciscom check what to do with platform + if ($cfg->exec_cfg->show_last_exec_any_build) { + $options = array( + 'getNoExecutions' => 1, + 'groupByBuild' => 0 + ); + $gui->map_last_exec_any_build = $tcaseMgr->getLastExecution( + $tcase_id, $tcversion_id, $args->tplan_id, + testcase::ANY_BUILD, $args->platform_id, $options); + + // Get UserID and Updater ID for current Version + $tc_current = $gui->map_last_exec_any_build; + foreach ($tc_current as $value) { + $testerid = $value[$testerIdKey]; + $userid_array[$testerid] = $testerid; + } + } + + $gui->req_details = null; + if ($args->reqEnabled) { + $gui->req_details = $req_mgr->getActiveForTCVersion( + $tcversion_id); + } + + $idCard = array( + 'tcase_id' => $tcase_id, + 'tcversion_id' => $tcversion_id + ); + $gui->relations = $tcaseMgr->getTCVersionRelations($idCard); + + $gui->kw = $tcaseMgr->getKeywordsByIdCard($idCard, + array( + 'output' => 'kwfull' + )); + + if (! is_null($cts)) { + $gui->scripts[$tcversion_id] = $tcaseMgr->getScriptsForTestCaseVersion( + $cts, $tcversion_id); + } + + $gui->other_execs = getOtherExecutions($db, $tcase_id, $tcversion_id, + $gui, $args, $cfg, $tcaseMgr); + + // Get attachment,bugs, etc + if (! is_null($gui->other_execs)) { + // Get the Tester ID for all previous executions + foreach ($gui->other_execs as $execution) { + foreach ($execution as $singleExecution) { + $testerid = $singleExecution[$testerIdKey]; + $userid_array[$testerid] = $testerid; + } + } + $other_info = execAdditionalInfo($db, $fileRepo, $tcaseMgr, + $gui->other_execs, $args->tplan_id, $args->tproject_id, + $args->issue_tracker_enabled, $its); + + $gui->attachments = $other_info['attachment']; + $gui->bugs = $other_info['bugs']; + $gui->other_exec_cfields = $other_info['cfexec_values']; + + // this piece of code is useful to avoid error on smarty template due to undefined value + if (is_array($tcversion_id) && + (count($gui->other_execs) != count($gui->map_last_exec))) { + foreach ($tcversion_id as $version_id) { + if (! isset($gui->other_execs[$version_id])) { + $gui->other_execs[$version_id] = null; + } + } + } + } + } + } +} + +if ($args->reload_caller) { + windowCloseAndOpenerReload(); + exit(); +} else { + // Removing duplicate and NULL id's + unset($userid_array['']); + $userSet = null; + if ($userid_array) { + foreach ($userid_array as $value) { + $userSet[] = $value; + } + } + + $gui->headsUpTSuite = smartyAssignTestsuiteInfo($smarty, $tree_mgr, + $tcase_id, $args->tproject_id, $cfg); + if ($args->doSave || $args->saveStepsPartialExec) { + $gui->headsUpTSuite = false; + } + + // Bulk is possible when test suite is selected (and is allowed in config) + if ($gui->can_use_bulk_op = ($args->level == 'testsuite')) { + $xx = null; + if (property_exists($gui, 'execution_time_cfields')) { + $xx = current((array) $gui->execution_time_cfields); + } + + $gui->execution_time_cfields = null; + if (! is_null($xx)) { + $gui->execution_time_cfields[0] = $xx; + } + } + + // has sense only if there are cf for execution + // may be can improve check + if (! $gui->can_use_bulk_op && + $cfg->exec_cfg->exec_mode->new_exec == 'latest') { + + list ($tcase_id, $tcversion_id, $latestExecIDInContext, $hasCFOnExec) = processTestCase( + $tcase, $gui, $args, $cfg, $linked_tcversions, $tree_mgr, $tcaseMgr, + $fileRepo); + + if ($latestExecIDInContext > 0) { + $tbl = DB_TABLE_PREFIX . 'executions'; + $sql = "SELECT notes FROM $tbl + WHERE id = $latestExecIDInContext"; + $rs = $db->get_recordset($sql); + $gui->lexNotes = $rs != null ? $rs[0]['notes'] : null; + } + } + + initWebEditors($gui, $cfg, $_SESSION['basehref']); + + // To silence smarty errors + // future must be initialized in a right way + $smarty->assign('test_automation_enabled', 0); + $smarty->assign('gui', $gui); + $smarty->assign('cfg', $cfg); + $smarty->assign('users', tlUser::getByIDs($db, $userSet)); + + $smarty->display( + $templateCfg->template_dir . $templateCfg->default_template); +} + +/** + */ +function initArgs(&$dbHandler, $cfgObj) +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + // Settings and Filters that we put on session to create some + // sort of persistent scope, because we have had issues when passing this info + // using GET mode (size limits) + // + // we get info about build_id, platform_id, etc ... + getSettingsAndFilters($args); + manageCookies($args, $cfgObj); + + // need to comunicate with left frame, will do via $_SESSION and form_token + if (($args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0) > + 0) { + // do not understand why this do not works OK + // $_SESSION[$args->treeFormToken]['loadExecDashboard'] = false; + $_SESSION['loadExecDashboard'][$args->treeFormToken] = false; + } + + $args->followTheWhiteRabbit = isset($_REQUEST['followTheWhiteRabbit']) ? 1 : 0; + if (is_null($args->refreshTree)) { + $args->refreshTree = isset($_REQUEST['refresh_tree']) ? intval( + $_REQUEST['refresh_tree']) : 0; + } + + $args->basehref = $_SESSION['basehref']; + $args->assignTask = isset($_REQUEST['assignTask']) ? 1 : 0; + $args->createIssue = isset($_REQUEST['createIssue']) ? 1 : 0; + $args->copyIssues = isset($_REQUEST['copyIssues']) ? 1 : 0; + $args->copyAttFromLEXEC = isset($_REQUEST['copyAttFromLEXEC']) ? 1 : 0; + + $args->tc_id = null; + $args->tsuite_id = null; + $args->user = $_SESSION['currentUser']; + $args->user_id = intval($args->user->dbID); + $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; + + $args->caller = isset($_REQUEST['caller']) ? $_REQUEST['caller'] : 'exec_feature'; + $args->reload_caller = false; + + $args->doExec = isset($_REQUEST['execute_cases']) ? 1 : 0; + $args->doDelete = isset($_REQUEST['do_delete']) ? $_REQUEST['do_delete'] : 0; + + $args->doMoveNext = isset($_REQUEST['move2next']) ? 1 : 0; + + $args->doMovePrevious = isset($_REQUEST['move2previous']) ? $_REQUEST['move2previous'] : 0; + $args->moveTowards = $args->doMoveNext ? 'forward' : ($args->doMovePrevious ? 'backward' : null); + + // can be a list, will arrive via form POST + $args->tc_versions = isset($_REQUEST['tc_version']) ? $_REQUEST['tc_version'] : null; + + // it's a submit button! + $args->saveStepsPartialExec = isset($_REQUEST['saveStepsPartialExec']); + + $key2loop = array( + 'level' => '', + 'status' => null, + 'statusSingle' => null, + 'do_bulk_save' => 0, + 'save_results' => 0, + 'save_and_next' => 0, + 'save_and_exit' => 0 + ); + foreach ($key2loop as $key => $value) { + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $value; + } + + $args->doSave = $args->save_results || $args->save_and_next || + $args->save_and_exit || $args->do_bulk_save; + + $args->doNavigate = $args->doMoveNext || $args->doMovePrevious; + + // See details on: "When nullify filter_status - 20080504" in this file + if ($args->level == 'testcase' || is_null($args->filter_status) || + (! is_array($args->filter_status) && trim($args->filter_status) == '')) { + $args->filter_status = null; + } else { + // 20130306 - franciscom + // This (without the strlen() check) generated issue 5541: When "Result" filter is used ... + // at least when result DIFFERENT that NOT RUN is used on filter + // + // 20120616 - franciscom + // some strange thing to investigate, seems that unserialize is invoked + // under the hood when getting data from $_REQUEST, then this piece + // of code not only will be useless BUT WRONG, because will try + // to unserialize something that IS NOT SERIALIZED!!!! + + // After TICKET 6651, may be need to limit size of $args->filter_status + if (is_string($args->filter_status) && strlen($args->filter_status) > 1) { + $args->filter_status = json_decode($args->filter_status); + } + } + + switch ($args->level) { + case 'testcase': + $args->tc_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null; + if (! is_null($args->tc_versions)) { + $args->tc_id = current($args->tc_versions); + $args->id = $args->tc_id; + $args->version_id = key($args->tc_versions); + } + $args->tsuite_id = null; + break; + + case 'testsuite': + $args->tsuite_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null; + $args->tc_id = null; + break; + } + + // + $args->updateTCVToThis = isset($_REQUEST['updateTCVToThis']) ? $_REQUEST['updateTCVToThis'] : null; + if (null != $args->updateTCVToThis) { + $args->version_id = intval($args->updateTCVToThis); + } + + $args->tsuitesInBranch = null; + if (! is_null($args->tsuite_id)) { + // will get all test suites in this branch, in order to limit amount of data returned + // by functions/method that collect linked tcversions + // THIS COLLECT ONLY FIRST LEVEL UNDER test suite, do not do deep search + // Need to understand is still needed + $tsuite_mgr = new testsuite($dbHandler); + $xx = $tsuite_mgr->get_children($args->tsuite_id, + array( + 'details' => 'id' + )); + $ldx = count($xx); + $xx[$ldx] = $args->tsuite_id; + $args->tsuitesInBranch = $xx; + unset($tsuite_mgr); + } + + // TICKET 5630: Test Results by direct link ... + $args->tplan_id = intval( + isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); + $args->tproject_id = intval( + isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']); + + if ($args->tproject_id <= 0) { + $tree_mgr = new tree($dbHandler); + $dm = $tree_mgr->get_node_hierarchy_info($args->tplan_id); + $args->tproject_id = $dm['parent_id']; + } + + $args->addLinkToTL = isset($_REQUEST['addLinkToTL']) ? true : false; + $args->addLinkToTLPrintView = isset($_REQUEST['addLinkToTLPrintView']) ? true : false; + + // Do this only on single execution mode + // get issue tracker config and object to manage TestLink - BTS integration + $args->itsCfg = null; + $its = null; + + $tproject_mgr = new testproject($dbHandler); + $info = $tproject_mgr->get_by_id($args->tproject_id); + $args->reqEnabled = intval($info['option_reqs']); + + unset($tproject_mgr); + $bug_summary['minLengh'] = 1; + $bug_summary['maxLengh'] = 1; + + if ($args->issue_tracker_enabled = $info['issue_tracker_enabled']) { + $it_mgr = new tlIssueTracker($dbHandler); + $args->itsCfg = $it_mgr->getLinkedTo($args->tproject_id); + $its = $it_mgr->getInterfaceObject($args->tproject_id); + + if (! is_null($args->itsCfg) && ! is_null($its)) { + $bug_summary['maxLengh'] = $its->getBugSummaryMaxLength(); + } + unset($it_mgr); + } + + initArgsIssueOnTestCase($args, $bug_summary); + + initArgsIssueOnSteps($args, $bug_summary); + + // get code tracker config and object to manage TestLink - CTS integration + $args->ctsCfg = null; + $cts = null; + + if ($args->codeTrackerEnabled = intval($info['code_tracker_enabled'])) { + $ct_mgr = new tlCodeTracker($dbHandler); + $args->ctsCfg = $ct_mgr->getLinkedTo($args->tproject_id); + $cts = $ct_mgr->getInterfaceObject($args->tproject_id); + + unset($ct_mgr); + } + + // is a submit button + $prop = 'linkLatestVersion'; + $args->$prop = isset($_REQUEST[$prop]); + + $prop = 'TCVToUpdate'; + $args->$prop = intval(isset($_REQUEST[$prop]) ? $_REQUEST[$prop] : 0); + + $prop = 'updateTCVToThis'; + $args->$prop = intval(isset($_REQUEST[$prop]) ? $_REQUEST[$prop] : 0); + + return array( + $args, + $its, + $cts + ); +} + +/** + */ +function initArgsIssueOnTestCase(&$argsObj, $bugSummaryProp) +{ + $inputCfg = array( + "bug_notes" => array( + "POST", + tlInputParameter::STRING_N + ), + "issueType" => array( + "POST", + tlInputParameter::INT_N + ), + "issuePriority" => array( + "POST", + tlInputParameter::INT_N + ), + "artifactComponent" => array( + "POST", + tlInputParameter::ARRAY_INT + ), + "artifactVersion" => array( + "POST", + tlInputParameter::ARRAY_INT + ) + ); + + $inputCfg["bug_summary"] = array( + "POST", + tlInputParameter::STRING_N + ); + + // hmm this MAGIC needs to be commented + if (! $argsObj->do_bulk_save) { + $inputCfg["bug_summary"][2] = $bugSummaryProp['minLengh']; + $inputCfg["bug_summary"][3] = $bugSummaryProp['maxLengh']; + } + + I_PARAMS($inputCfg, $argsObj); +} + +/** + */ +function initArgsIssueOnSteps(&$argsObj, $bugSummaryProp) +{ + $arrayOfInt = array( + "POST", + tlInputParameter::ARRAY_INT + ); + + $cfg = array( + "issueBodyForStep" => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + "issueTypeForStep" => $arrayOfInt, + "issuePriorityForStep" => $arrayOfInt + ); + + $cfg["issueSummaryForStep"] = array( + "POST", + tlInputParameter::ARRAY_STRING_N + ); + + // hmm this MAGIC needs to be commented + if (! $argsObj->do_bulk_save) { + $cfg["issueSummaryForStep"][2] = $bugSummaryProp['minLengh']; + $cfg["issueSummaryForStep"][3] = $bugSummaryProp['maxLengh']; + } + + I_PARAMS($cfg, $argsObj); + + // Special + // Array of Check Boxes: + // 'issueForStep','addLinkToTLForStep' + $sk = array( + 'issueForStep', + 'addLinkToTLForStep', + 'artifactComponentForStep', + 'artifactVersionForStep' + ); + + foreach ($sk as $kt) { + $argsObj->$kt = null; + if (isset($_REQUEST[$kt])) { + $argsObj->$kt = $_REQUEST[$kt]; + } + } +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function manageHistoryOn($hash_REQUEST, $hash_SESSION, $exec_cfg, $btn_on_name, + $btn_off_name, $hidden_on_name) +{ + if (isset($hash_REQUEST[$btn_on_name])) { + $history_on = true; + } elseif (isset($_REQUEST[$btn_off_name])) { + $history_on = false; + } elseif (isset($_REQUEST[$hidden_on_name])) { + $history_on = $_REQUEST[$hidden_on_name]; + } elseif (isset($_SESSION[$hidden_on_name])) { + $history_on = $_SESSION[$hidden_on_name]; + } else { + $history_on = $exec_cfg->history_on; + } + + return $history_on ? true : false; +} + +/* + * function: get_ts_name_details + * + * args : + * + * returns: map with key=TCID + * values= assoc_array([tsuite_id => 5341 + * [details] => my detailas ts1 + * [tcid] => 5343 + * [tsuite_name] => ts1) + */ +function getTestsuiteNameDetails(&$db, $tcase_id) +{ + $tables = array(); + $tables['testsuites'] = DB_TABLE_PREFIX . 'testsuites'; + $tables['nodes_hierarchy'] = DB_TABLE_PREFIX . 'nodes_hierarchy'; + + $rs = ''; + $do_query = true; + $sql = "SELECT TS.id AS tsuite_id, TS.details, + NHA.id AS tc_id, NHB.name AS tsuite_name + FROM {$tables['testsuites']} TS, + {$tables['nodes_hierarchy']} NHA, {$tables['nodes_hierarchy']} NHB WHERE TS.id=NHA.parent_id - AND NHB.id=NHA.parent_id "; - if( is_array($tcase_id) && count($tcase_id) > 0) { - $in_list = implode(",",$tcase_id); - $sql .= "AND NHA.id IN (" . $in_list . ")"; - } else if(!is_null($tcase_id)) { - $sql .= "AND NHA.id={$tcase_id}"; - } else { - $do_query = false; - } - - if ($do_query) { - $rs = $db->fetchRowsIntoMap($sql,'tc_id'); - } - return $rs; -} - -/* - function: - - args : - - returns: - -*/ -function smarty_assign_tsuite_info(&$smarty,&$tree_mgr,$tcase_id,$tproject_id,$cfgObj) -{ - - if( ($safeTCaseID = intval($tcase_id)) <= 0) { - // hmm, no good - return; - } - $fpath = $tree_mgr->get_full_path_verbose($tcase_id, array('output_format' => 'id_name')); - - $tsuite_info = get_ts_name_details($tree_mgr->db,$tcase_id); - - foreach($fpath as $key => $value) { - unset($value['name'][0]); // Remove test plan name - unset($value['node_id'][0]); // Remove test plan name - $str=''; - foreach($value['name'] as $jdx => $elem) { - $str .= " "; - $str .= htmlspecialchars($elem,ENT_QUOTES) . '/'; - } - $tsuite_info[$key]['tsuite_name']=$str; - } - $smarty->assign('tsuite_info',$tsuite_info); - - $headsUp = false; - - // -------------------------------------------------------------------------- - if (!is_null($tsuite_info)) { - $ckObj = new stdClass(); - $ckCfg = config_get('cookie'); - $cookieKey = $ckCfg->prefix . 'TL_execSetResults_tsdetails_view_status'; - - $exec_cfg = config_get('exec_cfg'); - $a_tsvw=array(); - $a_ts=array(); - $a_tsval=array(); - - $tsuite_mgr = new testsuite($tree_mgr->db); - $tsid = current($tsuite_info)['tsuite_id']; - if ($cfgObj->kwHeadsUpTSuiteOnExec != '') { - $headsUp = $tsuite_mgr->keywordIsLinked($tsid, - $cfgObj->kwHeadsUpTSuiteOnExec); - } - foreach($tsuite_info as $key => $elem) { - $main_k = 'tsdetails_view_status_' . $key; - $a_tsvw[] = $main_k; - $a_ts[] = 'tsdetails_' . $key; - $expand_collapse = 0; - if( !isset($_REQUEST[$main_k]) ){ - // First time we are entered here => - // we can need to understand how to proceed - switch($exec_cfg->expand_collapse->testsuite_details) { - case LAST_USER_CHOICE: - if (isset($_COOKIE[$cookieKey]) ) { - $expand_collapse = $_COOKIE[$cookieKey]; - } - break; - - default: - $expand_collapse = $exec_cfg->expand_collapse->testsuite_details; - break; - } - } - $a_tsval[] = isset($_REQUEST[$main_k]) ? - $_REQUEST[$main_k] : $expand_collapse; - $tsuite_id = $elem['tsuite_id']; - $tc_id = $elem['tc_id']; - if (!isset($cached_cf[$tsuite_id])) { - $cached_cf[$tsuite_id] = $tsuite_mgr->html_table_of_custom_field_values($tsuite_id,'design',null,$tproject_id); - } - $ts_cf_smarty[$tc_id] = $cached_cf[$tsuite_id]; - } - - if( count($a_tsval) > 0 ) { - $ckObj->value = $a_tsval[0]; - tlSetCookie($ckObj); - } - - $smarty->assign('tsd_div_id_list',implode(",",$a_ts)); - $smarty->assign('tsd_hidden_id_list',implode(",",$a_tsvw)); - $smarty->assign('tsd_val_for_hidden_list',implode(",",$a_tsval)); - - $smarty->assign('ts_cf_smarty',$ts_cf_smarty); - } - return $headsUp; -} -// ---------------------------------------------------------------------------- - - -/* - function: - - args : - - returns: - - @internal revisions: -*/ -function exec_additional_info(&$db, $fileRepo, &$tcase_mgr, $other_execs, - $tplan_id, $tproject_id, $bugInterfaceOn, $bugInterface) -{ - $attachmentInfos = null; - $bugs = null; - $cfexec_values = null; - - - foreach($other_execs as $tcversion_id => $execInfo) { - $num_elem = sizeof($execInfo); - for($idx = 0;$idx < $num_elem;$idx++) { - $exec_id = $execInfo[$idx]['execution_id']; - $aInfo = getAttachmentInfos($fileRepo,$exec_id,'executions',true,1); - if ($aInfo) { - $attachmentInfos[$exec_id] = $aInfo; - } - - if($bugInterfaceOn) { - $the_bugs = get_bugs_for_exec($db,$bugInterface,$exec_id); - if(count($the_bugs) > 0) { - $bugs[$exec_id] = $the_bugs; - } - } - - // Custom fields - $cfexec_values[$exec_id] = - $tcase_mgr->html_table_of_custom_field_values($tcversion_id, - 'execution',null,$exec_id,$tplan_id,$tproject_id); - } - } - - $info = array( 'attachment' => $attachmentInfos, - 'bugs' => $bugs, - 'cfexec_values' => $cfexec_values); - - return $info; -} //function end - - -/* - function: - - args : context hash with following keys - target => array('tc_versions' => array, 'version_id' =>, 'feature_id' => array) - context => array with keys - tproject_id - tplan_id - platform_id - build_id - user_id - - - returns: - -*/ -function do_remote_execution(&$dbHandler,$context) -{ - $debugMsg = "File:" . __FILE__ . " Function: " . __FUNCTION__; - - $tables = array(); - $tables['executions'] = DB_TABLE_PREFIX . 'executions'; - - $resultsCfg = config_get('results'); - $tc_status = $resultsCfg['status_code']; - $tree_mgr = new tree($dbHandler); - $cfield_mgr = new cfield_mgr($dbHandler); - - $ret = null; - $executionResults = array(); - - $myResult = array(); - $sql = " /* $debugMsg */ INSERT INTO {$tables['executions']} " . - " (testplan_id,platform_id,build_id,tester_id,execution_type," . - " tcversion_id,execution_ts,status,notes) " . - " VALUES ({$context['context']['tplan_id']}, " . - " {$context['context']['platform_id']}, " . - " {$context['context']['build_id']}," . - " {$context['context']['user_id']}," . TESTCASE_EXECUTION_TYPE_AUTO . ","; - - // have we got multiple test cases to execute ? - $target = &$context['target']; - foreach($target['tc_versions'] as $version_id => $tcase_id) - { - $ret[$version_id] = array("verboseID" => null, - "status" => null,"notes" => null,"system" => null, - "scheduled" => null, "timestamp" => null); - - $tcaseInfo = $tree_mgr->get_node_hierarchy_info($tcase_id); - $tcaseInfo['version_id'] = $version_id; - - // For each test case version we can have a different server config - $serverCfg = $cfield_mgr->getXMLRPCServerParams($version_id,$target['feature_id'][$version_id]); - $execResult[$version_id] = executeTestCase($tcaseInfo,$serverCfg,$context['context']); // RPC call - - - $tryWrite = false; - switch($execResult[$version_id]['system']['status']) - { - case 'configProblems': - $tryWrite = false; - break; - - case 'connectionFailure': - $tryWrite = false; - break; - - case 'ok'; - $tryWrite = true; - break; - } - - if( $tryWrite ) - { - $trun = &$execResult[$version_id]['execution']; - $ret[$version_id]["status"] = strtolower($trun['result']); - $ret[$version_id]["statusVerbose"] = $trun['resultVerbose']; - $ret[$version_id]["notes"] = trim($trun['notes']); - if( $trun['scheduled'] == 'now' ) - { - $notes = $dbHandler->prepare_string($ret[$version_id]["notes"]); - - if( $ret[$version_id]["status"] != $tc_status['passed'] && - $ret[$version_id]["status"] != $tc_status['failed'] && - $ret[$version_id]["status"] != $tc_status['blocked']) - { - $ret[$version_id]["status"] = $tc_status['blocked']; - } - - // - $sql2exec = $sql . $version_id . "," . $dbHandler->db_now() . - ", '{$ret[$version_id]["status"]}', '{$notes}' )"; - $dbHandler->exec_query($sql2exec); - } - else - { - $ret[$version_id]["scheduled"] = $trun['scheduled']; - $ret[$version_id]["timestamp"]= $trun['timestampISO']; - } - } - else - { - $ret[$version_id]["system"] = $execResult[$version_id]['system']; - } - } - - return $ret; -} - - -/* - function: initializeExecMode - - args: - - returns: - -*/ -function initializeExecMode(&$db,$exec_cfg,$userObj,$tproject_id,$tplan_id) -{ - - $simple_tester_roles=array_flip($exec_cfg->simple_tester_roles); - $effective_role = $userObj->getEffectiveRole($db,$tproject_id,$tplan_id); - - // Role is considered tester if: - // role == TL_ROLES_TESTER OR Role has Test Plan execute but not Test Plan planning - // - // - $can_execute = $effective_role->hasRight('testplan_execute'); - $can_manage = $effective_role->hasRight('testplan_planning'); - - $use_exec_cfg = isset($simple_tester_roles[$effective_role->dbID]) || ($can_execute && !$can_manage); - - return $use_exec_cfg ? $exec_cfg->exec_mode->tester : 'all'; -} // function end - - -/* - function: setTesterAssignment - - args: - - returns: - -*/ -function setTesterAssignment(&$db,$exec_info,&$tcase_mgr,$tplan_id,$platform_id, $build_id) -{ - foreach($exec_info as $version_id => $value) - { - $exec_info[$version_id]['assigned_user'] = null; - $exec_info[$version_id]['assigned_user_id'] = null; - - // map of map: main key version_id, secondary key: platform_id - $p3 = $tcase_mgr->get_version_exec_assignment($version_id,$tplan_id, $build_id); - if(!is_null($p3)) - { - foreach($p3[$version_id][$platform_id] as $uu) - { - $assignedTesterId = intval($uu['user_id']); - if($assignedTesterId) - { - $user = tlUser::getByID($db,$assignedTesterId); - if ($user) - { - $exec_info[$version_id]['assigned_user'][]= $user->getDisplayName(); - - } - $exec_info[$version_id]['assigned_user_id'][] = $assignedTesterId; - } - } - } - $exec_info[$version_id]['assigned_user'] = implode(',',(array)$exec_info[$version_id]['assigned_user']); - $exec_info[$version_id]['assigned_user_id'] = implode(',',(array)$exec_info[$version_id]['assigned_user_id']); - } - return $exec_info; -} //function end - -/* - function: - Reorder executions to mantaing correct visualization order. - - args: - - returns: - -*/ -function reorderExecutions(&$tcversion_id,&$exec_info) -{ - $dummy = array(); - foreach($tcversion_id as $idx => $tcv_id) - { - if(isset($exec_info[$tcv_id])) - { - $dummy[$idx] = $exec_info[$tcv_id]; - } - } - return $dummy; -} - -/* - function: setCanExecute - - args: - - returns: - -*/ -function setCanExecute($exec_info,$execution_mode,$can_execute,$tester_id) -{ - foreach($exec_info as $key => $tc_exec) - { - $execution_enabled = 0; - - if($can_execute == 1 && $tc_exec['active'] == 1) - { - $is_free = $tc_exec['assigned_user_id'] == '' ? 1 : 0; - - $testerSet = array_flip(explode(',',$tc_exec['assigned_user_id'])); - $assigned_to_me = isset($testerSet[$tester_id]) ? 1 : 0; - - switch($execution_mode) - { - case 'assigned_to_me': - $execution_enabled = $assigned_to_me; - break; - - case 'assigned_to_me_or_free': - $execution_enabled = $assigned_to_me || $is_free; - break; - - case 'all': - $execution_enabled = 1; - break; - - default: - $execution_enabled = 0; - break; - } // switch - } - $exec_info[$key]['can_be_executed']=$execution_enabled; - } - return $exec_info; -} - - -/* - function: createExecNotesWebEditor - creates map of html needed to display web editors - for execution notes. - - args: tcversions: array where each element has information - about testcase version that can be executed. - - basehref: URL - editorCfg: - - returns: map - key: testcase id - value: html to display web editor. - -*/ -function createExecNotesWebEditor(&$tcversions,$basehref,$editorCfg,$execCfg,$initValue=null) { - - if(is_null($tcversions) || count($tcversions) == 0 ) { - return null; // nothing todo >>>------> bye! - } - - // Important Notice: - // - // When using tinymce or none as web editor, we need to set rows and cols - // to appropriate values, to avoid an ugly ui. - // null => use default values defined on editor class file - // - // Rows and Cols values are useless for FCKeditor. - // - - if( $execCfg->exec_mode->new_exec == 'latest') { - $itemTemplateValue = $initValue != null ? $initValue : ''; - } else { - $itemTemplateValue = getItemTemplateContents('execution_template', 'notes', null); - } - - foreach($tcversions as $key => $tcv) { - $tcversion_id=$tcv['id']; - $tcase_id=$tcv['testcase_id']; - - $of = web_editor("notes[{$tcversion_id}]",$basehref,$editorCfg) ; - $of->Value = $itemTemplateValue; - - // Magic numbers that can be determined by trial and error - $cols = intval(isset($editorCfg['cols']) ? $editorCfg['cols'] : 60); - $rows = intval(isset($editorCfg['rows']) ? $editorCfg['rows'] : 10); - $editors[$tcase_id]=$of->CreateHTML($rows,$cols); - unset($of); - } - return $editors; -} - - - -/* - function: getCfg - - args: - - returns: - -*/ -function getCfg() { - $cfg = new stdClass(); - $cfg->exec_cfg = config_get('exec_cfg'); - $cfg->gui_cfg = config_get('gui'); - - $results = config_get('results'); - $cfg->tc_status = $results['status_code']; - $cfg->execStatusToExclude = $results['execStatusToExclude']; - - $cfg->testcase_cfg = config_get('testcase_cfg'); - $cfg->editorCfg = getWebEditorCfg('execution'); - - $cfg->cookie = config_get('cookie'); - - $cfg->kwHeadsUpTSuiteOnExec = - trim(config_get('keywords')->headsUpTSuiteOnExec); - - return $cfg; -} - - -/* - function: initializeRights - create object with rights useful for this feature - - args: - dbHandler: reference to db object - userObj: reference to current user object - tproject_id: - tplan_id - - Warning: this is right interface for this function, but - has_rights() can works in a mode (that i consider a dirty one) - using SESSION to achieve global coupling. - - returns: - -*/ -function initializeRights(&$dbHandler,&$userObj,$tproject_id,$tplan_id) { - $exec_cfg = config_get('exec_cfg'); - - - $userERole = $userObj->getEffectiveRole($dbHandler,$tproject_id,$tplan_id); - - $grants = new stdClass(); - $grants->execute = $userERole->hasRight("testplan_execute"); - - // IMPORTANT NOTICE - TICKET 5128 - // If is TRUE we will need also to analize, test case by test case - // these settings: - // $tlCfg->exec_cfg->exec_mode->tester - // $tlCfg->exec_cfg->simple_tester_roles - // - // Why ? - // Because if a tester can execute ONLY test cases assigned to him, this also - // has to mean that: - // can delete executions ONLY of test cases assigned to him - // can edit exec notes ONLY of test cases assigned to him - // can manage uploads on executions, ONLY of test cases assigned to him - // - // These checks can not be done here - // - // TICKET 5310: Execution Config - convert options into rights - $grants->delete_execution = $userERole->hasRight("exec_delete"); - - - // Important: - // Execution right must be present to consider this configuration option. - // $grants->edit_exec_notes = $grants->execute && $exec_cfg->edit_notes; - $grants->edit_exec_notes = $grants->execute && - $userERole->hasRight("exec_edit_notes"); - - $grants->edit_testcase = $userERole->hasRight("mgt_modify_tc"); - - - return $grants; -} - - -/* - function: initializeGui - - args : - - returns: - -*/ -function initializeGui(&$dbHandler,&$argsObj,&$cfgObj,&$tplanMgr,&$tcaseMgr,&$issueTracker,&$codeTracker) -{ - $buildMgr = new build_mgr($dbHandler); - $platformMgr = new tlPlatform($dbHandler,$argsObj->tproject_id); - - $gui = new stdClass(); - $gui->uploadOp = null; - $gui->headsUpTSuite = false; - $gui->direct_link = ''; - $gui->allIssueAttrOnScreen = 0; - $gui->lexNotes = null; - $gui->tcversionSet = null; - $gui->plugins = null; - $gui->hasNewestVersion = 0; - $gui->addLinkToTLChecked = $cfgObj->exec_cfg->exec_mode->addLinkToTLChecked; - $gui->addLinkToTLPrintViewChecked = $cfgObj->exec_cfg->exec_mode->addLinkToTLPrintViewChecked; - - $gui->assignTaskChecked = $cfgObj->exec_cfg->exec_mode->assignTaskChecked; - - - $k2i = array('import','attachments','exec','edit_exec'); - $gui->features = array(); - foreach($k2i as $olh) { - $gui->features[$olh] = false; - } - - if( $argsObj->user->hasRight($dbHandler,'testplan_execute', - $argsObj->tproject_id,$argsObj->tplan_id,true) ) { - foreach($k2i as $olh) { - $gui->features[$olh] = true; - } - } - - $gui->showExternalAccessString = true; - $gui->showImgInlineString = false; - - $gui->issueSummaryForStep = null; - $gui->addIssueOp = null; - $gui->allowStepAttachments = true; - $gui->tlCanCreateIssue = !is_null($issueTracker) && method_exists($issueTracker,'addIssue'); - $gui->remoteExecFeedback = $gui->user_feedback = ''; - $gui->tplan_id=$argsObj->tplan_id; - $gui->tproject_id=$argsObj->tproject_id; - $gui->build_id = $argsObj->build_id; - $gui->platform_id = $argsObj->platform_id; - $gui->loadExecDashboard = false; - $gui->treeFormToken = $argsObj->treeFormToken; - $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; - - - $gui->execStatusIcons = getResultsIcons(); - $gui->execStatusIconsNext = getResultsIconsNext(); - - list($gui->execStatusValues,$gui->execStepStatusValues) = - initExecValuesMenus($cfgObj->tc_status,$cfgObj->execStatusToExclude); - - $gui->can_use_bulk_op=0; - $gui->exec_notes_editors=null; - $gui->bulk_exec_notes_editor=null; - $gui->req_details=null; - $gui->attachmentInfos=null; - $gui->bugs=null; - $gui->scripts=null; - $gui->other_exec_cfields=null; - $gui->ownerDisplayName = null; - - $gui->editorType=$cfgObj->editorCfg['type']; - $cfgTestPlan = getWebEditorCfg('testplan'); - $gui->testPlanEditorType = $cfgTestPlan['type']; - $cfgPlatform = getWebEditorCfg('platform'); - $gui->platformEditorType = $cfgPlatform['type']; - $cfgBuild = getWebEditorCfg('build'); - $gui->buildEditorType = $cfgBuild['type']; - $cfgDesign = getWebEditorCfg('design'); - $gui->testDesignEditorType = $cfgDesign['type']; - $cfgStepsDesign = getWebEditorCfg('design'); - $gui->stepDesignEditorType = $cfgStepsDesign['type']; - - $gui->filter_assigned_to=$argsObj->filter_assigned_to; - $gui->tester_id=$argsObj->user_id; - $gui->include_unassigned=$argsObj->include_unassigned; - $gui->tpn_view_status=$argsObj->tpn_view_status; - $gui->bn_view_status=$argsObj->bn_view_status; - $gui->bc_view_status=$argsObj->bc_view_status; - $gui->platform_notes_view_status=$argsObj->platform_notes_view_status; - - - $gui->refreshTree = $argsObj->refreshTree; - if( !$argsObj->followTheWhiteRabbit ) { - if (!$argsObj->statusSingle || - current($argsObj->statusSingle) == $cfgObj->tc_status['not_run']) { - $gui->refreshTree = 0; - } - } - $gui->map_last_exec_any_build=null; - $gui->map_last_exec=null; - - // 20081122 - franciscom - // Just for the records: - // doing this here, we avoid to do on processTestSuite() and processTestCase(), - // but absolutely this will not improve in ANY WAY perfomance, because we do not loop - // over these two functions. - $tprojectMgr = new testproject($dbHandler); - $gui->tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tproject_id); - - $build_info = $buildMgr->get_by_id($argsObj->build_id); - - $gui->build_name = $build_info['name']; - $gui->build_notes=$build_info['notes']; - $gui->build_is_open=($build_info['is_open'] == 1 ? 1 : 0); - $gui->execution_types=$tcaseMgr->get_execution_types(); - - if ($argsObj->filter_assigned_to) { - $userSet = tlUser::getByIds($dbHandler,array_values($argsObj->filter_assigned_to)); - if ($userSet) { - foreach ($userSet as $key => $userObj) { - $gui->ownerDisplayName[$key] = $userObj->getDisplayName(); - } - } - } - // ------------------------------------------------------------------ - - $dummy = $tplanMgr->get_builds_for_html_options($argsObj->tplan_id); - $gui->build_name = isset($dummy[$argsObj->build_id]) ? $dummy[$argsObj->build_id] : ''; - $gui->build_div_title = lang_get('build') . ' ' . $gui->build_name; - - $gui->exec_mode = initializeExecMode($dbHandler,$cfgObj->exec_cfg, - $argsObj->user,$argsObj->tproject_id,$argsObj->tplan_id); - - $gui->grants = initializeRights($dbHandler,$argsObj->user,$argsObj->tproject_id,$argsObj->tplan_id); - - $rs = $tplanMgr->get_by_id($argsObj->tplan_id); - - $gui->testplan_name = $rs['name']; - $gui->testproject_name = $rs['tproject_name']; - $gui->testplan_notes = $rs['notes']; - $gui->testplan_div_title = lang_get('test_plan') . ' ' . $gui->testplan_name; - - $argsObj->tplan_apikey = $rs['api_key']; - - - // Important note: - // custom fields for test plan can be edited ONLY on design, that's reason why we are using - // scope = 'design' instead of 'execution' - $gui->testplan_cfields = $tplanMgr->html_table_of_custom_field_values( - $argsObj->tplan_id,'design', - array('show_on_execution' => 1)); - - $gui->build_cfields = $buildMgr->html_table_of_custom_field_values( - $argsObj->build_id,$argsObj->tproject_id, - 'design',array('show_on_execution' => 1)); - - - $gui->history_on = manage_history_on($_REQUEST,$_SESSION,$cfgObj->exec_cfg, - 'btn_history_on','btn_history_off','history_on'); - $gui->history_status_btn_name = $gui->history_on ? 'btn_history_off' : 'btn_history_on'; - - - - $dummy = $platformMgr->getLinkedToTestplan($argsObj->tplan_id); - $gui->has_platforms = !is_null($dummy) ? 1 : 0; - - $gui->platform_info['id'] = 0; - $gui->platform_info['name'] = ''; - if(!is_null($argsObj->platform_id) && $argsObj->platform_id > 0 ) - { - $gui->platform_info = $platformMgr->getByID($argsObj->platform_id); - } - $gui->platform_div_title = lang_get('platform') . ' ' . $gui->platform_info['name']; - - - $gui->issueTrackerIntegrationOn = $gui->tlCanCreateIssue = $gui->tlCanAddIssueNote = false; - - $gui->node_id = $argsObj->id; - $gui->draw_save_and_exit = ($argsObj->caller == 'tcAssignedToMe'); - - $gui->bug_summary = ''; - $gui->issueTrackerCfg = new stdClass(); - $gui->issueTrackerCfg->bugSummaryMaxLength = 100; // MAGIC I'm sorry - $gui->issueTrackerCfg->editIssueAttr = false; - - $issueTrackerUpAndRunning = 0; - if(!is_null($issueTracker)) { - if( $issueTracker->isConnected() ) { - $issueTrackerUpAndRunning = 1; - $itsCfg = $issueTracker->getCfg(); - - $gui->issueTrackerCfg->bugSummaryMaxLength = $issueTracker->getBugSummaryMaxLength(); - $gui->issueTrackerCfg->editIssueAttr = intval($itsCfg->userinteraction); - - $gui->issueTrackerIntegrationOn = true; - $gui->accessToIssueTracker = lang_get('link_bts_create_bug') . - "({$argsObj->itsCfg['issuetracker_name']})"; - - $gui->createIssueURL = $issueTracker->getEnterBugURL(); - $gui->tlCanCreateIssue = method_exists($issueTracker,'addIssue') && - $issueTracker->canCreateViaAPI(); - - $gui->tlCanAddIssueNote = method_exists($issueTracker,'addNote'); - } else { - $gui->user_feedback = lang_get('issue_tracker_integration_problems'); - } - } - - // get matadata - - $gui->issueTrackerMetaData = null; - $gui->issueTrackerMetaData = !is_null($issueTracker) ? - getIssueTrackerMetaData($issueTracker) : null; - - if ($gui->issueTrackerCfg->editIssueAttr == 1) { - $k2c = array('issueType','issuePriority','artifactVersion', - 'artifactComponent'); - foreach ($k2c as $kj) { - $gui->$kj = $argsObj->$kj; - - // To manage issue at step level - $kx = $kj . 'ForStep'; - $gui->$kx = $argsObj->$kx; - } - } else { - if( null != $gui->issueTrackerMetaData ) { - $singleVal = array('issuetype' => 'issueType', - 'issuepriority' => 'issuePriority'); - foreach ($singleVal as $kj => $attr) { - $gui->$attr = $itsCfg->$kj; - $forStep = $attr . 'ForStep'; - $gui->$forStep = $gui->$attr; - } - - $multiVal = array('version' => 'artifactVersion', - 'component' => 'artifactComponent'); - foreach ($multiVal as $kj => $attr) { - $gui->$attr = (array)$itsCfg->$kj; - $forStep = $attr . 'ForStep'; - $gui->$forStep = $gui->$attr; - } - - // something similar needs to be done for steps - } - } - - $gui->executionContext = array(); - $gui->executionContext['tproject_name'] = $gui->testproject_name; - $gui->executionContext['tplan_name'] = $gui->testplan_name; - $gui->executionContext['platform_name'] = $gui->platform_info['name']; - $gui->executionContext['build_name'] = $gui->build_name; - - return $gui; -} - - -/** - * processTestCase - * - */ -function processTestCase($tcase,&$guiObj,&$argsObj,&$cfgObj,$tcv,&$treeMgr,&$tcaseMgr,&$docRepository) -{ - - // IMPORTANT due to platform feature - // every element on linked_tcversions will be an array. - $cf_filters = array('show_on_execution' => 1); - $locationFilters = $tcaseMgr->buildCFLocationMap(); - - $guiObj->design_time_cfields = array(); - $guiObj->testplan_design_time_cfields = array(); - - $tcase_id = isset($tcase['tcase_id']) ? $tcase['tcase_id'] : $argsObj->id; - - // Development Notice: - // accessing a FIXED index like in: - // - // $items_to_exec[$tcase_id] = $linked_tcversions[$tcase_id][0]['tcversion_id']; - // $link_id = $linked_tcversions[$tcase_id][0]['feature_id']; - // - // Because we want to access FIRTS element is better to use current. - // - $target = current(current($tcv)); - $items_to_exec[$tcase_id] = $target['tcversion_id']; - $link_id = $target['feature_id']; - $tcversion_id = isset($tcase['tcversion_id']) ? $tcase['tcversion_id'] : $items_to_exec[$tcase_id]; - - // - $ltcvID = $tcaseMgr->getLatestVersionID($tcase_id); - $guiObj->hasNewestVersion = ($ltcvID != $tcversion_id); - - $eid = -1; - if($cfgObj->exec_cfg->exec_mode->new_exec == 'latest' ) { - // Need latest exec id on context - $eid = $tcaseMgr->getLatestExecIDInContext($tcversion_id,$argsObj); - } - - $cf_map = null; - $guiObj->execution_time_cfields[$tcase_id] = null; - if($guiObj->grants->execute) { - if( $eid > 0 ) { - // I'm getting the values saved on latest execution - $cf_map = $tcaseMgr->get_linked_cfields_at_execution( - $tcversion_id,null,null,$eid, - $argsObj->tplan_id,$argsObj->tproject_id); - } - - $guiObj->execution_time_cfields[$tcase_id] = - $tcaseMgr->html_table_of_custom_field_inputs($tcase_id,null, - 'execution',"_{$tcase_id}",null,null, - $argsObj->tproject_id,null,$cf_map); - } - - $guiObj->tcAttachments[$tcase_id] = getAttachmentInfos($docRepository,$tcversion_id,'tcversions',1); - - foreach($locationFilters as $locationKey => $filterValue) { - $finalFilters=$cf_filters+$filterValue; - $guiObj->design_time_cfields[$tcase_id][$locationKey] = - $tcaseMgr->html_table_of_custom_field_values($tcase_id,'design',$finalFilters,null,null, - $argsObj->tproject_id,null,$tcversion_id); - - $guiObj->testplan_design_time_cfields[$tcase_id] = - $tcaseMgr->html_table_of_custom_field_values($tcversion_id, - 'testplan_design',$cf_filters,null,null, - $argsObj->tproject_id,null,$link_id); - } - - - $tc_info = $treeMgr->get_node_hierarchy_info($tcase_id); - $guiObj->tSuiteAttachments[$tc_info['parent_id']] = - getAttachmentInfos($docRepository,$tc_info['parent_id'], - 'nodes_hierarchy',true,1); - // Direct Link - $lk = current($tcv); - $guiObj->direct_link = trim($_SESSION['basehref'],'/') . - "/ltx.php?item=exec&feature_id=" . $lk[0]['feature_id'] . - "&build_id=" . $argsObj->build_id; - - $argsObj->direct_link = $guiObj->direct_link; - - - // Information for Issue Management - // Common Info - $signature = new stdClass(); - $signature->tcname = $tc_info['name']; - $signature->tcpathname = $tcaseMgr->getPathName($tcase_id); - $signature->tcversion_id = $tcversion_id; - - list($guiObj->bug_summary,$guiObj->issueSummaryForStep) = - genIssueSummary($tcaseMgr,$signature,$guiObj->executionContext); - - // return more data eid, has cf on exec - return array($tcase_id,$tcversion_id,$eid,($cf_map != null)); -} - - - - -/* - function: getLatestExec - Important Notice: - $tcase_id and $tcversions_id, can be ARRAYS when user enable bulk execution - - args : - - returns: - -*/ -function getLatestExec(&$dbHandler,$tcase_id,$tcversion_id,$guiObj,$argsObj,&$tcaseMgr) -{ - $options = array('getNoExecutions' => 1, 'groupByBuild' => 0, 'getStepsExecInfo' => 1); - - $last_exec = - $tcaseMgr->get_last_execution($tcase_id,$tcversion_id,$argsObj->tplan_id, - $argsObj->build_id,$argsObj->platform_id,$options); - - if( !is_null($last_exec) ) { - $last_exec = setTesterAssignment($dbHandler,$last_exec,$tcaseMgr, - $argsObj->tplan_id,$argsObj->platform_id, - $argsObj->build_id); - - // Warning: setCanExecute() must be called AFTER setTesterAssignment() - $can_execute = $guiObj->grants->execute && ($guiObj->build_is_open); - $last_exec = setCanExecute($last_exec,$guiObj->exec_mode,$can_execute,$argsObj->user_id); - } - - // Reorder executions to mantaing correct visualization order. - if( is_array($tcversion_id) ) { - $last_exec = reorderExecutions($tcversion_id,$last_exec); - } - - return $last_exec; -} - -/** - * Function retrieve test steps backup - * - * @param testcase $tcaseMgr the testcase Manager - * @param array $guiObj - * @param int $testPlanId - * @param int $platformId - * @param int $buildId - * - * return map - */ -// Has to be moved to testcase class -function getBackupSteps(&$tcaseMgr,$guiObj,$testPlanId,$platformId,$buildId) { - - $stepsIds = array(); - foreach($guiObj->map_last_exec as $tcId => $elements) { - foreach ($guiObj->map_last_exec[$tcId]['steps'] as $step) { - array_push($stepsIds, $step["id"]); - } - } - - return $tcaseMgr->getBackupSteps($stepsIds,$testPlanId,$platformId,$buildId); -} - - - -/* - function: getOtherExecutions - - args : - - returns: - - rev: -*/ -function getOtherExecutions(&$dbHandler,$tcase_id,$tcversion_id,$guiObj,$argsObj,&$cfgObj,&$tcaseMgr) -{ - $other_execs = null; - if($guiObj->history_on) - { - // CRITIC see for key names - testcases.class.php -> getExecutionSet() - $execContext = array('testplan_id' => $argsObj->tplan_id, 'platform_id' => $argsObj->platform_id, - 'build_id' => $argsObj->build_id); - - if($cfgObj->exec_cfg->show_history_all_builds ) - { - $execContext['build_id'] = null; - } - if($cfgObj->exec_cfg->show_history_all_platforms ) - { - $execContext['platform_id'] = null; - } - - $options = array('exec_id_order' => $cfgObj->exec_cfg->history_order); - $other_execs = $tcaseMgr->getExecutionSet($tcase_id,$tcversion_id,$execContext,$options); - } - else - { - // Warning!!!: - // we can't use the data we have got with previous call to get_last_execution() - // because if user have asked to save results last execution data may be has changed - $aux_map = $tcaseMgr->get_last_execution($tcase_id,$tcversion_id,$argsObj->tplan_id, - $argsObj->build_id,$argsObj->platform_id); - if(!is_null($aux_map)) - { - $other_execs = array(); - foreach($aux_map as $key => $value ) - { - $other_execs[$key] = array($value); - } - } - } - return $other_execs; -} - - -/* - function: processTestSuite - - args : - - returns: - -*/ -function processTestSuite(&$dbHandler,&$guiObj,&$argsObj,$testSet,&$treeMgr,&$tcaseMgr,&$docRepository) -{ - $locationFilters = $tcaseMgr->buildCFLocationMap(); - $cf_filters = array('show_on_execution' => 1); - $tsuite_mgr=new testsuite($dbHandler); - $tsuite_data = $tsuite_mgr->get_by_id($argsObj->id); - - // Get the path for every test case, grouping test cases that have same parent. - $testCaseQty = count($testSet->tcase_id); - if( $testCaseQty > 0 ) - { - $dummy = $tcaseMgr->cfield_mgr->getLocations(); - $verboseLocationCode = array_flip($dummy['testcase']); - $filters=null; - foreach($verboseLocationCode as $key => $value) - { - $filters[$key]['location']=$value; - } - - $dummy_id = current($testSet->tcase_id); - $index = $testCaseQty == 1 ? $dummy_id : 0; // 0 => BULK - $suffix = '_' . $index; - $execution_time_cfields = - $tcaseMgr->html_table_of_custom_field_inputs($dummy_id,$argsObj->tproject_id,'execution',$suffix, - null,null,$argsObj->tproject_id); - - $guiObj->execution_time_cfields[$index] = $execution_time_cfields; - $gdx=0; - foreach($testSet->tcase_id as $testcase_id) - { - $path_f = $treeMgr->get_path($testcase_id,null,'full'); - foreach($path_f as $key => $path_elem) - { - if( $path_elem['parent_id'] == $argsObj->id ) - { - // Can be added because is present in the branch the user wants to view - // ID of branch starting node is in $argsObj->id - $guiObj->tcAttachments[$testcase_id] = getAttachmentInfos($docRepository,$testcase_id,'nodes_hierarchy',true,1); - - foreach($locationFilters as $locationKey => $filterValue) { - $finalFilters = $cf_filters+$filterValue; - $guiObj->design_time_cfields[$testcase_id][$locationKey] = - $tcaseMgr->html_table_of_custom_field_values($testcase_id,'design',$finalFilters,null,null, - $argsObj->tproject_id,null,$testSet->tcversion_id[$gdx]); - - $guiObj->testplan_design_time_cfields[$testcase_id] = - $tcaseMgr->html_table_of_custom_field_values($testcase_id,'testplan_design',$cf_filters, - null,null,$argsObj->tproject_id); - - } - - if($guiObj->grants->execute) { - $guiObj->execution_time_cfields[$testcase_id] = - $tcaseMgr->html_table_of_custom_field_inputs($testcase_id, null,'execution', "_".$testcase_id,null,null, - $argsObj->tproject_id); - } - - } // if( $path_elem['parent_id'] == $argsObj->id ) - - // We do this because do not know if some test case not yet analised will be direct - // child of this test suite, then we get this info in advance. - // In situations where only last test suite on branch have test cases, we are colleting - // info we will never use. - if($path_elem['node_table'] == 'testsuites' && !isset($guiObj->tSuiteAttachments[$path_elem['id']])) - { - $guiObj->tSuiteAttachments[$path_elem['id']] = - getAttachmentInfos($docRepository,$path_elem['id'],'nodes_hierarchy',true,1); - } - - } //foreach($path_f as $key => $path_elem) - $gdx++; - } - } - // return array($testSet->tcase_id,$testSet->tcversion_id); -} - - -/** - * - */ -function buildExecContext(&$argsObj,$tcasePrefix,&$tplanMgr,&$tcaseMgr) -{ - $debugMsg = "File:" . __FILE__ . "Function:" . __FUNCTION__; - - $ret = array(); - $ret['target'] = array('tc_versions' => null,'version_id' => null,'feature_id' => null, 'basic_info' => null); - $ret['context'] = array('tproject_id' => null,'tplan_id' => null, 'platform_id' => null, - 'build_id' => null,'user_id' => null); - - - foreach($ret as $area => &$value) - { - foreach($value as $key => $dummy) - { - if( property_exists($argsObj,$key) ) - { - $value[$key] = $argsObj->$key; - } - } - } - - // Now get another important information feature_id on testplan_tcversions - // needed to get remote execution server config if this config has been - // done with Custom Fields at Test Plan Design Time - foreach($ret['target']['tc_versions'] as $tcv_id => $tc_id) - { - $ret['target']['feature_id'][$tcv_id] = $tplanMgr->getFeatureID($ret['context']['tplan_id'], - $ret['context']['platform_id'], - $tcv_id); - - $dummy = $tcaseMgr->get_basic_info($tc_id,array('id' => $tcv_id)); - $dummy[0]['tcasePrefix'] = $tcasePrefix; - $ret['target']['basic_info'][$tcv_id] = $dummy[0]; - - } - return $ret; -} - - - -function launchRemoteExec(&$dbHandler,&$argsObj,$tcasePrefix,&$tplanMgr,&$tcaseMgr) -{ - // IMPORTANT NOTICE - // Remote execution will NOT use ANY of data typed by user, - // - notes - // - custom fields - // - // IMPORTANT NOTICE - // need to understand what to do with feedback provided - // by do_remote_execution(). - // Right now no matter how things go, no feedback is given to user. - // May be this need to be improved in future. - // - // Only drawback i see is when remote exec is done on a test suite - // and amount of feedback can be high, then do not see what can be effect - // on GUI - $execContext = buildExecContext($argsObj,$tcasePrefix,$tplanMgr,$tcaseMgr); - $feedback = do_remote_execution($dbHandler,$execContext); - $feedback = current($feedback); - return $feedback; -} - - - -/** - * @use testplan->filterByOnDesignCustomFields - * - */ -function getLinkedItems($argsObj,$historyOn,$cfgObj,$tcaseMgr,$tplanMgr,$identity=null) -{ - - $ltcv = null; - $idCard = null; - $itemSet = null; - - if( !is_null($identity) ) { - $idCard = $identity; - } - else if(!is_null($argsObj->tc_id) && !is_array($argsObj->tc_id) ) { - $idCard = array('id' => $argsObj->tc_id, 'version_id' => $argsObj->version_id); - } - - $idCard['version_id'] = $tplanMgr->getVersionLinked($argsObj->tplan_id,$idCard['id']); - - - if( !is_null($idCard) ) { - // CRITIC see for key names - testcases.class.php -> getExecutionSet() - $execContext = array('testplan_id' => $argsObj->tplan_id,'platform_id' => $argsObj->platform_id, - 'build_id' => $argsObj->build_id); - - $ltcv = null; - if($historyOn) { - $execContext['testplan_id'] = $argsObj->tplan_id; - $ltcv = $tcaseMgr->getExecutionSet($idCard['id'],null,$execContext); - } - - // lazy implementation: - // getExecutionSet() returns data ONLY for Statuses that are written ON DB, - // then if full history for test case is NOT RUN, we are doomed!! - if(!$historyOn || is_null($ltcv)) { - $opt = null; - $ltcv = $tcaseMgr->getLatestExecSingleContext($idCard,$execContext,$opt); - } - } else { - // ----------------------------------------------------------- - // When nullify filter_status - 20080504 - DO NOT REMOVE - - // - // May be in the following situation we do not HAVE to apply filter status: - // 1. User have filter for Not Run on Tree - // 2. Clicks on TC XXX - // 3. Executes TC - // 4. DO NOT UPDATE TREE. - // we do not update automatically to avoid: - // a) performance problems - // b) delays on operations due to tree redraw - // c) loose tree status due to lack of feature of tree engine - // - // 5. Clicks again on TC XXX - // If we use filter, we will get No Data Available. - // - // When working on show_testsuite_contents mode (OLD MODE) when we show - // all testcases inside a testsuite that verifies a filter criteria WE NEED TO APPLY FILTER - // - // We do not have this problem when this page is called after user have executed, - // probably because filter_status is not send back. - // - // I will add logic to nullify filter_status on init_args() - // - - $options = array('only_executed' => true, 'output' => $historyOn ? 'mapOfArray' : 'mapOfMap', - 'include_unassigned' => $argsObj->include_unassigned, - 'group_by_build' => 'add_build', - 'last_execution' => !$historyOn); - - if(is_null($argsObj->filter_status) || in_array($cfgObj->tc_status['not_run'],(array)$argsObj->filter_status)) { - $options['only_executed'] = false; - } - - // args->tsuites_id: is only used when user click on a test suite. - // probably is used only when bulk execution is enabled. - // - // if args->tc_id is not null, theorically all other filters are useless. - // why ? - // Because will normally call this script, from the execution tree and if we can click - // on a tree node, this means it has passed all filters. - // - // - // $args->platform_id: needed to get execution status info - // $args->build_id: needed to get execution status info - // - $basic_filters = array('tcase_id' => $argsObj->tc_id, 'platform_id' => $argsObj->platform_id,'build_id' => $argsObj->build_id); - - // This filters are useful when bulk execution is enabled, - // and user do click on a test suite on execution tree. - - // seems to be useless => 'cf_hash' => $argsObj->filter_cfields, - // need to review $tplanMgr->getLinkedForExecTree - // - - // $setOfTestSuites = (array)$argsObj->tsuite_id; - $bulk_filters = array('keyword_id' => $argsObj->keyword_id,'assigned_to' => $argsObj->filter_assigned_to, - 'exec_status' => $argsObj->filter_status, - 'tsuites_id' => $argsObj->tsuite_id, - 'assigned_on_build' => $argsObj->build_id, - 'exec_type' => $argsObj->execution_type, - 'urgencyImportance' => $argsObj->priority); - - // CRITIC / IMPORTANT - // With BULK Operation enabled, we prefer to display Test cases tha are ONLY DIRECT CHILDREN - // of test suite id => we do not do deep walk. - // Think is a good choice, to avoid retrieving lot of info. - // May be we need to add a config parameter (or better an option at GUI level) - // in order to allow use how he / she wants to work. - // - $filters = array_merge($basic_filters,$bulk_filters); - - if( !is_null($sql2do = $tplanMgr->getLinkedForExecTree($argsObj->tplan_id,$filters,$options)) ) - { - if( is_array($sql2do) ) - { - if( isset($filters['keyword_filter_type']) && ($filters['keyword_filter_type'] == 'And') ) - { - $kmethod = "fetchRowsIntoMapAddRC"; - $unionClause = " UNION ALL "; - } - else - { - $kmethod = "fetchRowsIntoMap"; - $unionClause = ' UNION '; - } - $sql2run = $sql2do['exec'] . $unionClause . $sql2do['not_run']; - } - else - { - $sql2run = $sql2do; - } - - // Development Notice: - // CUMULATIVE is used only to create same type of datastructe that existed - // before this refactoring - // - // $tex = $tcaseMgr->db->$kmethod($sql2run,'tcase_id',database::CUMULATIVE); - $sql2run .= ' ORDER BY exec_order '; - - $ltcv = $tex = $tcaseMgr->db->$kmethod($sql2run,'tcase_id'); - if(!is_null($tex)) - { - // We need to create: - // one set for Custom fields that apply to DESIGN - // one set for Custom fields that apply to TESTPLAN DESIGN - - if(!is_null($argsObj->filter_cfields)) - { - $tk = array_keys($argsObj->filter_cfields); - $cf = null; - // foreach( array('design','testplan_design') as $l4) - foreach( array('design') as $l4) - { - $cf[$l4] = $tplanMgr->cfield_mgr->getByIDAndEnableOn($tk,array($l4 => true)); - } - if(isset($cf['design']) && !is_null($cf['design'])) - { - foreach($cf['design'] as $yy => $xc) - { - $az[$yy] = $argsObj->filter_cfields[$yy]; - } - $tex = $tplanMgr->filterByOnDesignCustomFields($tex,$az); - } - } - - foreach($tex as $xkey => $xvalue) - { - $itemSet->tcase_id[]=$xkey; - $itemSet->tcversion_id[]=$xvalue['tcversion_id']; - } - } - } - } - return array($ltcv,$itemSet); -} - - -/** - * - * - */ -function initWebEditors(&$guiObj,$cfgObj,$baseHREF) { - if( $guiObj->can_use_bulk_op ) { - $of = web_editor("bulk_exec_notes",$baseHREF,$cfgObj->editorCfg); - $of->Value = getItemTemplateContents('execution_template', $of->InstanceName, null); - - // Magic numbers that can be determined by trial and error - $cols = intval(isset($editorCfg['cols']) ? $cfgObj->editorCfg['cols'] : 60); - $rows = intval(isset($editorCfg['rows']) ? $cfgObj->editorCfg['rows'] : 10); - $guiObj->bulk_exec_notes_editor = $of->CreateHTML($rows,$cols); - unset($of); - } else { - $guiObj->exec_notes_editors = createExecNotesWebEditor($guiObj->map_last_exec,$baseHREF,$cfgObj->editorCfg, - $cfgObj->exec_cfg,$guiObj->lexNotes); - } -} - - - - -/** - * get info from ... - * - */ -function getSettingsAndFilters(&$argsObj) { - - $mode = 'execution_mode'; - $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $sf = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$form_token]) ? $_SESSION[$mode][$form_token] : null; - - $argsObj->testcases_to_show = isset($sf['testcases_to_show']) ? $sf['testcases_to_show'] : null; - - // just for better readability - $filters = array('filter_status' => 'filter_result_result','filter_assigned_to' => 'filter_assigned_user', - 'execution_type' => 'filter_execution_type', 'priority' => 'filter_priority', - 'filter_cfields' => 'filter_custom_fields'); - $settings = array('build_id' => 'setting_build', 'platform_id' => 'setting_platform'); - - $key2null = array_merge($filters,$settings); - $isNumeric = array('build_id' => 0, 'platform_id' => -1); - - foreach($key2null as $key => $sfKey) - { - $argsObj->$key = isset($sf[$sfKey]) ? $sf[$sfKey] : null; - if (is_null($argsObj->$key)) - { - // let's this page be functional withouth a form token too - // (when called from testcases assigned to me) - $argsObj->$key = isset($_REQUEST[$sfKey]) ? $_REQUEST[$sfKey] : null; - } - - if(isset($isNumeric[$key])) - { - $argsObj->$key = intval($argsObj->$key); - } - } - - - // keywords filter - $argsObj->keyword_id = 0; - if (isset($sf['filter_keywords'])) - { - $argsObj->keyword_id = $sf['filter_keywords']; - if (is_array($argsObj->keyword_id) && count($argsObj->keyword_id) == 1) - { - $argsObj->keyword_id = $argsObj->keyword_id[0]; - } - } - - $argsObj->keywordsFilterType = null; - if (isset($sf['filter_keywords_filter_type'])) { - $argsObj->keywordsFilterType = $sf['filter_keywords_filter_type']; - } - - // 20190119 - $argsObj->refreshTree = isset($sf['setting_refresh_tree_on_action']) ? - $sf['setting_refresh_tree_on_action'] : $argsObj->refreshTree; - - // Checkbox - $tgk = 'filter_assigned_user_include_unassigned'; - $argsObj->include_unassigned = isset($sf[$tgk]) && ($sf[$tgk] != 0 ? 1 : 0); -} - - - -/** - * get info from cookies and also set values on cookies - * - */ -function manageCookies(&$argsObj,$cfgObj) -{ - $cookieExecPrefix = 'TL_execSetResults_'; - - // IMPORTANT: logic for test suite notes CAN NOT BE IMPLEMENTED HERE - // see smarty_assign_tsuite_info() in this file. - $key4cookies = array('tpn_view_status' => 'testplan_notes','bn_view_status' => 'build_description', - 'platform_notes_view_status' => 'platform_description'); - - $key2loop = array('id' => 0, 'exec_to_delete' => 0, 'version_id' => 0, 'tpn_view_status' => 0, - 'bn_view_status' => 0, 'bc_view_status' => 1,'platform_notes_view_status' => 0); - - foreach($key4cookies as $key => $cfgKey) - { - $cookieKey = $cookieExecPrefix . $key; - if( !isset($_REQUEST[$key]) ) - { - // First time we are entered here => we can need to understand how to proceed - switch($cfgObj->exec_cfg->expand_collapse->$cfgKey ) - { - case LAST_USER_CHOICE: - if (isset($_COOKIE[$cookieKey]) ) - { - $key2loop[$key] = $_COOKIE[$cookieKey]; - } - break; - - default: - $key2loop[$key] = $cfgObj->exec_cfg->expand_collapse->$cfgKey; - break; - } - } - } - - $ckObj = new stdClass(); - foreach($key2loop as $key => $value) - { - $argsObj->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : $value; - if( isset($key4cookies[$key]) ) - { - $ckObj->name = $cfgObj->cookie->prefix . $cookieExecPrefix . $key; - $ckObj->value = $argsObj->$key; - tlSetCookie($ckObj); - } - } -} - -/** - * - */ -function getResultsIcons() -{ - $resultsCfg = config_get('results'); - // loop over status for user interface, because these are the statuses - // user can assign while executing test cases - foreach ($resultsCfg['status_icons_for_exec_ui'] as $verbose_status => $ele) { - if ($verbose_status != 'not_run') { - $code = $resultsCfg['status_code'][$verbose_status]; - $items[$code] = $ele; - $items[$code]['title'] = lang_get($items[$code]['title']); - } - } - return $items; -} - -/** - * - */ -function getResultsIconsNext() -{ - $resultsCfg = config_get('results'); - // loop over status for user interface, because these are the statuses - // user can assign while executing test cases - foreach($resultsCfg['status_icons_for_exec_next_ui'] as $verbose_status => $ele) - { - if( $verbose_status != 'not_run' ) - { - $code = $resultsCfg['status_code'][$verbose_status]; - $items[$code] = $ele; - $items[$code]['title'] = lang_get($items[$code]['title']); - } - } - return $items; -} - -/** - * - */ -function genIssueSummary(&$tcaseMgr,$signature,$context) { - - $cfg = config_get('exec_cfg'); - - // Work on labels - $text = array(); - $text['tcase'] = helperLabels($cfg->issues->tcase_level->subject); - $text['tcstep'] = helperLabels($cfg->issues->tcstep_level->subject); - - // Work on values - $ecx = &$context; - $searchFor = array('%%TCNAME%%', '%%PROJECTNAME%%', - '%%PLANNAME%%','%%BUILDNAME%%','%%PLATFNAME%%', - '%%TCPATHNAME%%','%%EXECTSISO%%'); - - $replaceWith = array($signature->tcname,$ecx['tproject_name'], - $ecx['tplan_name'],$ecx['build_name'], - $ecx['platform_name'],$signature->tcpathname, - date('Y-m-dTH:i',time())); - - $nu = array(); - $nu['tcase'] = str_replace($searchFor, $replaceWith, $text['tcase']); - $nu['tcstep'] = null; - - $opt = array('fields2get' => 'step_number,id'); - $steps = $tcaseMgr->get_steps($signature->tcversion_id,0,$opt); - if(null != $steps) { - $tstx = str_replace($searchFor, $replaceWith, $text['tcstep']); - foreach($steps as $elem) { - $nu['tcstep'][$elem['id']] = - str_replace('%%STEPNUMBER%%',$elem['step_number'],$tstx); - } - } - - return array($nu['tcase'],$nu['tcstep']); -} - - - -/** - * - */ -function helperLabels($haystack) { - $searchFor = array('$$issue_on_step', - '$$issue_subject_tcname','$$issue_subject_tcpathname', - '$$issue_subject_projectname', - '$$issue_subject_planname','$$issue_subject_buildname', - '$$issue_subject_platfname','$$issue_subject_execon'); - - $replaceWith = array(); - foreach ( $searchFor as $lblKey ) { - $jk = str_replace('$$','',$lblKey); - $replaceWith[] = lang_get($jk); - } - $hy = str_replace($searchFor, $replaceWith, $haystack); - return $hy; -} - -/** - * - */ -function initExecValuesMenus($tcStatusCfg, $execStatusToExclude) { - - $remove = array($tcStatusCfg['not_run']); - $execStatusTestCase = $execStatusTestCaseStep = createResultsMenu($remove); - - foreach($execStatusToExclude['testcase'] as $code) { - if( isset($execStatusTestCase[$code]) ) { - unset($execStatusTestCase[$code]); - } - } - - foreach($execStatusToExclude['step'] as $code) { - if( isset($execStatusTestCaseStep[$code]) ) { - unset($execStatusTestCaseStep[$code]); - } - } - - return array($execStatusTestCase,$execStatusTestCaseStep); + AND NHB.id=NHA.parent_id "; + if (is_array($tcase_id) && ! empty($tcase_id)) { + $in_list = implode(",", $tcase_id); + $sql .= "AND NHA.id IN (" . $in_list . ")"; + } elseif (! is_null($tcase_id)) { + $sql .= "AND NHA.id={$tcase_id}"; + } else { + $do_query = false; + } + + if ($do_query) { + $rs = $db->fetchRowsIntoMap($sql, 'tc_id'); + } + return $rs; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function smartyAssignTestsuiteInfo(&$smarty, &$tree_mgr, $tcase_id, $tproject_id, + $cfgObj) +{ + if ((intval($tcase_id)) <= 0) { + // hmm, no good + return; + } + $fpath = $tree_mgr->get_full_path_verbose($tcase_id, + array( + 'output_format' => 'id_name' + )); + + $tsuite_info = getTestsuiteNameDetails($tree_mgr->db, $tcase_id); + + foreach ($fpath as $key => $value) { + unset($value['name'][0]); // Remove test plan name + unset($value['node_id'][0]); // Remove test plan name + $str = ''; + foreach ($value['name'] as $jdx => $elem) { + $str .= " "; + $str .= htmlspecialchars($elem, ENT_QUOTES) . '/'; + } + $tsuite_info[$key]['tsuite_name'] = $str; + } + $smarty->assign('tsuite_info', $tsuite_info); + + $headsUp = false; + + // -------------------------------------------------------------------------- + if (! is_null($tsuite_info)) { + $ckObj = new stdClass(); + $ckCfg = config_get('cookie'); + $cookieKey = $ckCfg->prefix . 'TL_execSetResults_tsdetails_view_status'; + + $exec_cfg = config_get('exec_cfg'); + $a_tsvw = array(); + $a_ts = array(); + $a_tsval = array(); + + $tsuite_mgr = new testsuite($tree_mgr->db); + $tsid = current($tsuite_info)['tsuite_id']; + if ($cfgObj->kwHeadsUpTSuiteOnExec != '') { + $headsUp = $tsuite_mgr->keywordIsLinked($tsid, + $cfgObj->kwHeadsUpTSuiteOnExec); + } + foreach ($tsuite_info as $key => $elem) { + $main_k = 'tsdetails_view_status_' . $key; + $a_tsvw[] = $main_k; + $a_ts[] = 'tsdetails_' . $key; + $expand_collapse = 0; + if (! isset($_REQUEST[$main_k])) { + // First time we are entered here => + // we can need to understand how to proceed + switch ($exec_cfg->expand_collapse->testsuite_details) { + case LAST_USER_CHOICE: + if (isset($_COOKIE[$cookieKey])) { + $expand_collapse = $_COOKIE[$cookieKey]; + } + break; + + default: + $expand_collapse = $exec_cfg->expand_collapse->testsuite_details; + break; + } + } + $a_tsval[] = isset($_REQUEST[$main_k]) ? $_REQUEST[$main_k] : $expand_collapse; + $tsuite_id = $elem['tsuite_id']; + $tc_id = $elem['tc_id']; + if (! isset($cached_cf[$tsuite_id])) { + $cached_cf[$tsuite_id] = $tsuite_mgr->html_table_of_custom_field_values( + $tsuite_id, 'design', null, $tproject_id); + } + $ts_cf_smarty[$tc_id] = $cached_cf[$tsuite_id]; + } + + if (! empty($a_tsval)) { + $ckObj->value = $a_tsval[0]; + tlSetCookie($ckObj); + } + + $smarty->assign('tsd_div_id_list', implode(",", $a_ts)); + $smarty->assign('tsd_hidden_id_list', implode(",", $a_tsvw)); + $smarty->assign('tsd_val_for_hidden_list', implode(",", $a_tsval)); + + $smarty->assign('ts_cf_smarty', $ts_cf_smarty); + } + return $headsUp; +} + +/* + * function: + * + * args : + * + * returns: + * + * @internal revisions: + */ +function execAdditionalInfo(&$db, $fileRepo, &$tcaseMgr, $other_execs, $tplan_id, + $tproject_id, $bugInterfaceOn, $bugInterface) +{ + $attachmentInfos = null; + $bugs = null; + $cfexec_values = null; + + foreach ($other_execs as $tcversion_id => $execInfo) { + $num_elem = count($execInfo); + for ($idx = 0; $idx < $num_elem; $idx ++) { + $exec_id = $execInfo[$idx]['execution_id']; + $aInfo = getAttachmentInfos($fileRepo, $exec_id, 'executions', true, + 1); + if ($aInfo) { + $attachmentInfos[$exec_id] = $aInfo; + } + + if ($bugInterfaceOn) { + $the_bugs = get_bugs_for_exec($db, $bugInterface, $exec_id); + if (! empty($the_bugs)) { + $bugs[$exec_id] = $the_bugs; + } + } + + // Custom fields + $cfexec_values[$exec_id] = $tcaseMgr->html_table_of_custom_field_values( + $tcversion_id, 'execution', null, $exec_id, $tplan_id, + $tproject_id); + } + } + + return array( + 'attachment' => $attachmentInfos, + 'bugs' => $bugs, + 'cfexec_values' => $cfexec_values + ); +} + +/* + * function: + * + * args : context hash with following keys + * target => array('tc_versions' => array, 'version_id' =>, 'feature_id' => array) + * context => array with keys + * tproject_id + * tplan_id + * platform_id + * build_id + * user_id + * + * + * returns: + * + */ +function doRemoteExecution(&$dbHandler, $context) +{ + $debugMsg = "File:" . __FILE__ . " Function: " . __FUNCTION__; + + $tables = array(); + $tables['executions'] = DB_TABLE_PREFIX . 'executions'; + + $resultsCfg = config_get('results'); + $tc_status = $resultsCfg['status_code']; + $tree_mgr = new tree($dbHandler); + $cfield_mgr = new cfield_mgr($dbHandler); + + $ret = null; + + $sql = " /* $debugMsg */ INSERT INTO {$tables['executions']} " . + " (testplan_id,platform_id,build_id,tester_id,execution_type," . + " tcversion_id,execution_ts,status,notes) " . + " VALUES ({$context['context']['tplan_id']}, " . + " {$context['context']['platform_id']}, " . + " {$context['context']['build_id']}," . + " {$context['context']['user_id']}," . TESTCASE_EXECUTION_TYPE_AUTO . ","; + + // have we got multiple test cases to execute ? + $target = &$context['target']; + foreach ($target['tc_versions'] as $version_id => $tcase_id) { + $ret[$version_id] = array( + "verboseID" => null, + "status" => null, + "notes" => null, + "system" => null, + "scheduled" => null, + "timestamp" => null + ); + + $tcaseInfo = $tree_mgr->get_node_hierarchy_info($tcase_id); + $tcaseInfo['version_id'] = $version_id; + + // For each test case version we can have a different server config + $serverCfg = $cfield_mgr->getXMLRPCServerParams($version_id, + $target['feature_id'][$version_id]); + $execResult[$version_id] = executeTestCase($tcaseInfo, $serverCfg, + $context['context']); // RPC call + + $tryWrite = false; + switch ($execResult[$version_id]['system']['status']) { + case 'configProblems': + $tryWrite = false; + break; + + case 'connectionFailure': + $tryWrite = false; + break; + + case 'ok': + $tryWrite = true; + break; + } + + if ($tryWrite) { + $trun = &$execResult[$version_id]['execution']; + $ret[$version_id]["status"] = strtolower($trun['result']); + $ret[$version_id]["statusVerbose"] = $trun['resultVerbose']; + $ret[$version_id]["notes"] = trim($trun['notes']); + if ($trun['scheduled'] == 'now') { + $notes = $dbHandler->prepare_string($ret[$version_id]["notes"]); + + if ($ret[$version_id]["status"] != $tc_status['passed'] && + $ret[$version_id]["status"] != $tc_status['failed'] && + $ret[$version_id]["status"] != $tc_status['blocked']) { + $ret[$version_id]["status"] = $tc_status['blocked']; + } + + // + $sql2exec = $sql . $version_id . "," . $dbHandler->db_now() . + ", '{$ret[$version_id]["status"]}', '{$notes}' )"; + $dbHandler->exec_query($sql2exec); + } else { + $ret[$version_id]["scheduled"] = $trun['scheduled']; + $ret[$version_id]["timestamp"] = $trun['timestampISO']; + } + } else { + $ret[$version_id]["system"] = $execResult[$version_id]['system']; + } + } + + return $ret; +} + +/* + * function: initializeExecMode + * + * args: + * + * returns: + * + */ +function initializeExecMode(&$db, $exec_cfg, $userObj, $tproject_id, $tplan_id) +{ + $simple_tester_roles = array_flip($exec_cfg->simple_tester_roles); + $effective_role = $userObj->getEffectiveRole($db, $tproject_id, $tplan_id); + + // Role is considered tester if: + // role == TL_ROLES_TESTER OR Role has Test Plan execute but not Test Plan planning + // + // + $can_execute = $effective_role->hasRight('testplan_execute'); + $can_manage = $effective_role->hasRight('testplan_planning'); + + $use_exec_cfg = isset($simple_tester_roles[$effective_role->dbID]) || + ($can_execute && ! $can_manage); + + return $use_exec_cfg ? $exec_cfg->exec_mode->tester : 'all'; +} + +/* + * function: setTesterAssignment + * + * args: + * + * returns: + * + */ +function setTesterAssignment(&$db, $exec_info, &$tcaseMgr, $tplan_id, + $platform_id, $build_id) +{ + foreach ($exec_info as $version_id => $value) { + $exec_info[$version_id]['assigned_user'] = null; + $exec_info[$version_id]['assigned_user_id'] = null; + + // map of map: main key version_id, secondary key: platform_id + $p3 = $tcaseMgr->getVersionExecAssignment($version_id, $tplan_id, + $build_id); + if (! is_null($p3)) { + foreach ($p3[$version_id][$platform_id] as $uu) { + $assignedTesterId = intval($uu['user_id']); + if ($assignedTesterId) { + $user = tlUser::getByID($db, $assignedTesterId); + if ($user) { + $exec_info[$version_id]['assigned_user'][] = $user->getDisplayName(); + } + $exec_info[$version_id]['assigned_user_id'][] = $assignedTesterId; + } + } + } + $exec_info[$version_id]['assigned_user'] = implode(',', + (array) $exec_info[$version_id]['assigned_user']); + $exec_info[$version_id]['assigned_user_id'] = implode(',', + (array) $exec_info[$version_id]['assigned_user_id']); + } + return $exec_info; +} + +/* + * function: + * Reorder executions to mantaing correct visualization order. + * + * args: + * + * returns: + * + */ +function reorderExecutions(&$tcversion_id, &$exec_info) +{ + $dummy = array(); + foreach ($tcversion_id as $idx => $tcv_id) { + if (isset($exec_info[$tcv_id])) { + $dummy[$idx] = $exec_info[$tcv_id]; + } + } + return $dummy; +} + +/* + * function: setCanExecute + * + * args: + * + * returns: + * + */ +function setCanExecute($exec_info, $execution_mode, $can_execute, $tester_id) +{ + foreach ($exec_info as $key => $tc_exec) { + $execution_enabled = 0; + + if ($can_execute == 1 && $tc_exec['active'] == 1) { + $is_free = $tc_exec['assigned_user_id'] == '' ? 1 : 0; + + $testerSet = array_flip(explode(',', $tc_exec['assigned_user_id'])); + $assigned_to_me = isset($testerSet[$tester_id]) ? 1 : 0; + + switch ($execution_mode) { + case 'assigned_to_me': + $execution_enabled = $assigned_to_me; + break; + + case 'assigned_to_me_or_free': + $execution_enabled = $assigned_to_me || $is_free; + break; + + case 'all': + $execution_enabled = 1; + break; + + default: + $execution_enabled = 0; + break; + } // switch + } + $exec_info[$key]['can_be_executed'] = $execution_enabled; + } + return $exec_info; +} + +/* + * function: createExecNotesWebEditor + * creates map of html needed to display web editors + * for execution notes. + * + * args: tcversions: array where each element has information + * about testcase version that can be executed. + * + * basehref: URL + * editorCfg: + * + * returns: map + * key: testcase id + * value: html to display web editor. + * + */ +function createExecNotesWebEditor(&$tcversions, $basehref, $editorCfg, $execCfg, + $initValue = null) +{ + if (is_null($tcversions) || count($tcversions) == 0) { + return null; // nothing todo >>>------> bye! + } + + // Important Notice: + // + // When using tinymce or none as web editor, we need to set rows and cols + // to appropriate values, to avoid an ugly ui. + // null => use default values defined on editor class file + // + // Rows and Cols values are useless for FCKeditor. + // + if ($execCfg->exec_mode->new_exec == 'latest') { + $itemTemplateValue = $initValue != null ? $initValue : ''; + } else { + $itemTemplateValue = getItemTemplateContents('execution_template', + 'notes', null); + } + + foreach ($tcversions as $tcv) { + $tcversion_id = $tcv['id']; + $tcase_id = $tcv['testcase_id']; + + $of = web_editor("notes[{$tcversion_id}]", $basehref, $editorCfg); + $of->Value = $itemTemplateValue; + + // Magic numbers that can be determined by trial and error + $cols = intval(isset($editorCfg['cols']) ? $editorCfg['cols'] : 60); + $rows = intval(isset($editorCfg['rows']) ? $editorCfg['rows'] : 10); + $editors[$tcase_id] = $of->CreateHTML($rows, $cols); + unset($of); + } + return $editors; +} + +/* + * function: getCfg + * + * args: + * + * returns: + * + */ +function getCfg() +{ + $cfg = new stdClass(); + $cfg->exec_cfg = config_get('exec_cfg'); + $cfg->gui_cfg = config_get('gui'); + + $results = config_get('results'); + $cfg->tc_status = $results['status_code']; + $cfg->execStatusToExclude = $results['execStatusToExclude']; + + $cfg->testcase_cfg = config_get('testcase_cfg'); + $cfg->editorCfg = getWebEditorCfg('execution'); + + $cfg->cookie = config_get('cookie'); + + $cfg->kwHeadsUpTSuiteOnExec = trim( + config_get('keywords')->headsUpTSuiteOnExec); + + return $cfg; +} + +/* + * function: initializeRights + * create object with rights useful for this feature + * + * args: + * dbHandler: reference to db object + * userObj: reference to current user object + * tproject_id: + * tplan_id + * + * Warning: this is right interface for this function, but + * has_rights() can works in a mode (that i consider a dirty one) + * using SESSION to achieve global coupling. + * + * returns: + * + */ +function initializeRights(&$dbHandler, &$userObj, $tproject_id, $tplan_id) +{ + $userERole = $userObj->getEffectiveRole($dbHandler, $tproject_id, $tplan_id); + + $grants = new stdClass(); + $grants->execute = $userERole->hasRight("testplan_execute"); + + // IMPORTANT NOTICE - TICKET 5128 + // If is TRUE we will need also to analize, test case by test case + // these settings: + // $tlCfg->exec_cfg->exec_mode->tester + // $tlCfg->exec_cfg->simple_tester_roles + // + // Why ? + // Because if a tester can execute ONLY test cases assigned to him, this also + // has to mean that: + // can delete executions ONLY of test cases assigned to him + // can edit exec notes ONLY of test cases assigned to him + // can manage uploads on executions, ONLY of test cases assigned to him + // + // These checks can not be done here + // + // TICKET 5310: Execution Config - convert options into rights + $grants->delete_execution = $userERole->hasRight("exec_delete"); + + // Important: + // Execution right must be present to consider this configuration option. + // $grants->edit_exec_notes = $grants->execute && $exec_cfg->edit_notes; + $grants->edit_exec_notes = $grants->execute && + $userERole->hasRight("exec_edit_notes"); + + $grants->edit_testcase = $userERole->hasRight("mgt_modify_tc"); + + return $grants; +} + +/* + * function: initializeGui + * + * args : + * + * returns: + * + */ +function initializeGui(&$dbHandler, &$argsObj, &$cfgObj, &$tplanMgr, &$tcaseMgr, + &$issueTracker, &$codeTracker) +{ + $buildMgr = new build_mgr($dbHandler); + $platformMgr = new tlPlatform($dbHandler, $argsObj->tproject_id); + + $gui = new stdClass(); + $gui->uploadOp = null; + $gui->headsUpTSuite = false; + $gui->direct_link = ''; + $gui->allIssueAttrOnScreen = 0; + $gui->lexNotes = null; + $gui->tcversionSet = null; + $gui->plugins = null; + $gui->hasNewestVersion = 0; + $gui->addLinkToTLChecked = $cfgObj->exec_cfg->exec_mode->addLinkToTLChecked; + $gui->addLinkToTLPrintViewChecked = $cfgObj->exec_cfg->exec_mode->addLinkToTLPrintViewChecked; + + $gui->assignTaskChecked = $cfgObj->exec_cfg->exec_mode->assignTaskChecked; + + $k2i = array( + 'import', + 'attachments', + 'exec', + 'edit_exec' + ); + $gui->features = array(); + foreach ($k2i as $olh) { + $gui->features[$olh] = false; + } + + if ($argsObj->user->hasRight($dbHandler, 'testplan_execute', + $argsObj->tproject_id, $argsObj->tplan_id, true)) { + foreach ($k2i as $olh) { + $gui->features[$olh] = true; + } + } + + $gui->showExternalAccessString = true; + $gui->showImgInlineString = false; + + $gui->issueSummaryForStep = null; + $gui->addIssueOp = null; + $gui->allowStepAttachments = true; + + $gui->remoteExecFeedback = $gui->user_feedback = ''; + $gui->tplan_id = $argsObj->tplan_id; + $gui->tproject_id = $argsObj->tproject_id; + $gui->build_id = $argsObj->build_id; + $gui->platform_id = $argsObj->platform_id; + $gui->loadExecDashboard = false; + $gui->treeFormToken = $argsObj->treeFormToken; + $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; + + $gui->execStatusIcons = getResultsIcons(); + $gui->execStatusIconsNext = getResultsIconsNext(); + + list ($gui->execStatusValues, $gui->execStepStatusValues) = initExecValuesMenus( + $cfgObj->tc_status, $cfgObj->execStatusToExclude); + + $gui->can_use_bulk_op = 0; + $gui->exec_notes_editors = null; + $gui->bulk_exec_notes_editor = null; + $gui->req_details = null; + $gui->attachmentInfos = null; + $gui->bugs = null; + $gui->scripts = null; + $gui->other_exec_cfields = null; + $gui->ownerDisplayName = null; + + $gui->editorType = $cfgObj->editorCfg['type']; + $cfgTestPlan = getWebEditorCfg('testplan'); + $gui->testPlanEditorType = $cfgTestPlan['type']; + $cfgPlatform = getWebEditorCfg('platform'); + $gui->platformEditorType = $cfgPlatform['type']; + $cfgBuild = getWebEditorCfg('build'); + $gui->buildEditorType = $cfgBuild['type']; + $cfgDesign = getWebEditorCfg('design'); + $gui->testDesignEditorType = $cfgDesign['type']; + $cfgStepsDesign = getWebEditorCfg('design'); + $gui->stepDesignEditorType = $cfgStepsDesign['type']; + + $gui->filter_assigned_to = $argsObj->filter_assigned_to; + $gui->tester_id = $argsObj->user_id; + $gui->include_unassigned = $argsObj->include_unassigned; + $gui->tpn_view_status = $argsObj->tpn_view_status; + $gui->bn_view_status = $argsObj->bn_view_status; + $gui->bc_view_status = $argsObj->bc_view_status; + $gui->platform_notes_view_status = $argsObj->platform_notes_view_status; + + $gui->refreshTree = $argsObj->refreshTree; + if (! $argsObj->followTheWhiteRabbit && ! $argsObj->statusSingle || + current($argsObj->statusSingle) == $cfgObj->tc_status['not_run']) { + $gui->refreshTree = 0; + } + $gui->map_last_exec_any_build = null; + $gui->map_last_exec = null; + + // 20081122 - franciscom + // Just for the records: + // doing this here, we avoid to do on processTestSuite() and processTestCase(), + // but absolutely this will not improve in ANY WAY perfomance, because we do not loop + // over these two functions. + $tprojectMgr = new testproject($dbHandler); + $gui->tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tproject_id); + + $build_info = $buildMgr->get_by_id($argsObj->build_id); + + $gui->build_name = $build_info['name']; + $gui->build_notes = $build_info['notes']; + $gui->build_is_open = ($build_info['is_open'] == 1 ? 1 : 0); + $gui->execution_types = $tcaseMgr->get_execution_types(); + + if ($argsObj->filter_assigned_to) { + $userSet = tlUser::getByIds($dbHandler, + array_values($argsObj->filter_assigned_to)); + if ($userSet) { + foreach ($userSet as $key => $userObj) { + $gui->ownerDisplayName[$key] = $userObj->getDisplayName(); + } + } + } + + $dummy = $tplanMgr->get_builds_for_html_options($argsObj->tplan_id); + $gui->build_name = isset($dummy[$argsObj->build_id]) ? $dummy[$argsObj->build_id] : ''; + $gui->build_div_title = lang_get('build') . ' ' . $gui->build_name; + + $gui->exec_mode = initializeExecMode($dbHandler, $cfgObj->exec_cfg, + $argsObj->user, $argsObj->tproject_id, $argsObj->tplan_id); + + $gui->grants = initializeRights($dbHandler, $argsObj->user, + $argsObj->tproject_id, $argsObj->tplan_id); + + $rs = $tplanMgr->get_by_id($argsObj->tplan_id); + + $gui->testplan_name = $rs['name']; + $gui->testproject_name = $rs['tproject_name']; + $gui->testplan_notes = $rs['notes']; + $gui->testplan_div_title = lang_get('test_plan') . ' ' . $gui->testplan_name; + + $argsObj->tplan_apikey = $rs['api_key']; + + // Important note: + // custom fields for test plan can be edited ONLY on design, that's reason why we are using + // scope = 'design' instead of 'execution' + $gui->testplan_cfields = $tplanMgr->html_table_of_custom_field_values( + $argsObj->tplan_id, 'design', array( + 'show_on_execution' => 1 + )); + + $gui->build_cfields = $buildMgr->html_table_of_custom_field_values( + $argsObj->build_id, $argsObj->tproject_id, 'design', + array( + 'show_on_execution' => 1 + )); + + $gui->history_on = manageHistoryOn($_REQUEST, $_SESSION, $cfgObj->exec_cfg, + 'btn_history_on', 'btn_history_off', 'history_on'); + $gui->history_status_btn_name = $gui->history_on ? 'btn_history_off' : 'btn_history_on'; + + $dummy = $platformMgr->getLinkedToTestplan($argsObj->tplan_id); + $gui->has_platforms = ! is_null($dummy) ? 1 : 0; + + $gui->platform_info['id'] = 0; + $gui->platform_info['name'] = ''; + if (! is_null($argsObj->platform_id) && $argsObj->platform_id > 0) { + $gui->platform_info = $platformMgr->getByID($argsObj->platform_id); + } + $gui->platform_div_title = lang_get('platform') . ' ' . + $gui->platform_info['name']; + + $gui->node_id = $argsObj->id; + $gui->draw_save_and_exit = ($argsObj->caller == 'tcAssignedToMe'); + + // Issue Tracker Integration + $issueTrackerExists = ! is_null($issueTracker); + $gui->tlCanCreateIssue = false; + $gui->tlCanAddIssueNote = false; + $gui->issueTrackerIntegrationOn = false; + if ($issueTrackerExists) { + $gui->tlCanCreateIssue = method_exists($issueTracker, 'addIssue') && + $issueTracker->canCreateViaAPI(); + $gui->tlCanAddIssueNote = method_exists($issueTracker, 'addNote') && + $issueTracker->canAddNoteViaAPI(); + } + + $gui->bug_summary = ''; + $gui->issueTrackerCfg = new stdClass(); + $gui->issueTrackerCfg->bugSummaryMaxLength = 100; // MAGIC I'm sorry + $gui->issueTrackerCfg->editIssueAttr = false; + $gui->issueTrackerCfg->crudIssueViaAPI = false; + + $gui->issueTrackerMetaData = null; + if ($issueTrackerExists) { + if ($issueTracker->isConnected()) { + + $itsCfg = $issueTracker->getCfg(); + $gui->issueTrackerCfg->bugSummaryMaxLength = $issueTracker->getBugSummaryMaxLength(); + $gui->issueTrackerCfg->editIssueAttr = (intval( + $itsCfg->userinteraction) > 0); + $gui->issueTrackerCfg->crudIssueViaAPI = (intval( + $itsCfg->createissueviaapi) > 0) ?? false; + + $gui->issueTrackerIntegrationOn = true; + $gui->accessToIssueTracker = lang_get('link_bts_create_bug') . + "({$argsObj->itsCfg['issuetracker_name']})"; + $gui->createIssueURL = $issueTracker->getEnterBugURL(); + + if ($gui->issueTrackerCfg->crudIssueViaAPI) { + // get metadata + $gui->issueTrackerMetaData = getIssueTrackerMetaData( + $issueTracker); + $gui->tlCanCreateIssue = method_exists($issueTracker, 'addIssue') && + $issueTracker->canCreateViaAPI(); + + $gui->tlCanAddIssueNote = method_exists($issueTracker, 'addNote') && + $issueTracker->canAddNoteViaAPI(); + } + } else { + $gui->user_feedback = lang_get('issue_tracker_integration_problems'); + } + } + + if ($gui->issueTrackerCfg->editIssueAttr == 1) { + $k2c = array( + 'issueType', + 'issuePriority', + 'artifactVersion', + 'artifactComponent' + ); + foreach ($k2c as $kj) { + $gui->$kj = $argsObj->$kj; + + // To manage issue at step level + $kx = $kj . 'ForStep'; + $gui->$kx = $argsObj->$kx; + } + } else { + if (null != $gui->issueTrackerMetaData) { + $singleVal = array( + 'issuetype' => 'issueType', + 'issuepriority' => 'issuePriority' + ); + foreach ($singleVal as $kj => $attr) { + $gui->$attr = null; + if (property_exists($itsCfg, $kj)) { + $gui->$attr = $itsCfg->$kj; + } else { + /* Provide warning */ + tLog( + "Issue Tracker Config Issue? - Attribute:$kj doesn't exist", + "WARNING"); + } + $forStep = $attr . 'ForStep'; + $gui->$forStep = $gui->$attr; + } + + $multiVal = array( + 'version' => 'artifactVersion', + 'component' => 'artifactComponent' + ); + foreach ($multiVal as $kj => $attr) { + $gui->$attr = null; + if (property_exists($itsCfg, $kj)) { + $gui->$attr = (array) $itsCfg->$kj; + } else { + /* Provide warning */ + tLog( + "Issue Tracker Config Issue? - Attribute:$kj doesn't exist", + "WARNING"); + } + $forStep = $attr . 'ForStep'; + $gui->$forStep = $gui->$attr; + } + } + } + + $gui->executionContext = array(); + $gui->executionContext['tproject_name'] = $gui->testproject_name; + $gui->executionContext['tplan_name'] = $gui->testplan_name; + $gui->executionContext['platform_name'] = $gui->platform_info['name']; + $gui->executionContext['build_name'] = $gui->build_name; + + return $gui; +} + +/** + * processTestCase + */ +function processTestCase($tcase, &$guiObj, &$argsObj, &$cfgObj, $tcv, &$treeMgr, + &$tcaseMgr, &$docRepository) +{ + // IMPORTANT due to platform feature + // every element on linked_tcversions will be an array. + $cf_filters = array( + 'show_on_execution' => 1 + ); + $locationFilters = $tcaseMgr->buildCFLocationMap(); + + $guiObj->design_time_cfields = array(); + $guiObj->testplan_design_time_cfields = array(); + + $tcase_id = isset($tcase['tcase_id']) ? $tcase['tcase_id'] : $argsObj->id; + + // Development Notice: + // accessing a FIXED index like in: + // + // $items_to_exec[$tcase_id] = $linked_tcversions[$tcase_id][0]['tcversion_id']; + // $link_id = $linked_tcversions[$tcase_id][0]['feature_id']; + // + // Because we want to access FIRTS element is better to use current. + $target = current(current($tcv)); + $items_to_exec[$tcase_id] = $target['tcversion_id']; + $link_id = $target['feature_id']; + $tcversion_id = isset($tcase['tcversion_id']) ? $tcase['tcversion_id'] : $items_to_exec[$tcase_id]; + + $ltcvID = $tcaseMgr->getLatestVersionID($tcase_id); + $guiObj->hasNewestVersion = ($ltcvID != $tcversion_id); + + $eid = - 1; + if ($cfgObj->exec_cfg->exec_mode->new_exec == 'latest') { + // Need latest exec id on context + $eid = $tcaseMgr->getLatestExecIDInContext($tcversion_id, $argsObj); + } + + $cf_map = null; + $guiObj->execution_time_cfields[$tcase_id] = null; + if ($guiObj->grants->execute) { + if ($eid > 0) { + // I'm getting the values saved on latest execution + $cf_map = $tcaseMgr->get_linked_cfields_at_execution($tcversion_id, + null, null, $eid, $argsObj->tplan_id, $argsObj->tproject_id); + } + + $guiObj->execution_time_cfields[$tcase_id] = $tcaseMgr->html_table_of_custom_field_inputs( + $tcase_id, null, 'execution', "_{$tcase_id}", null, null, + $argsObj->tproject_id, null, $cf_map); + } + + $guiObj->tcAttachments[$tcase_id] = getAttachmentInfos($docRepository, + $tcversion_id, 'tcversions', 1); + + foreach ($locationFilters as $locationKey => $filterValue) { + $finalFilters = $cf_filters + $filterValue; + $guiObj->design_time_cfields[$tcase_id][$locationKey] = $tcaseMgr->html_table_of_custom_field_values( + $tcase_id, 'design', $finalFilters, null, null, + $argsObj->tproject_id, null, $tcversion_id); + + $guiObj->testplan_design_time_cfields[$tcase_id] = $tcaseMgr->html_table_of_custom_field_values( + $tcversion_id, 'testplan_design', $cf_filters, null, null, + $argsObj->tproject_id, null, $link_id); + } + + $tc_info = $treeMgr->get_node_hierarchy_info($tcase_id); + $guiObj->tSuiteAttachments[$tc_info['parent_id']] = getAttachmentInfos( + $docRepository, $tc_info['parent_id'], 'nodes_hierarchy', true, 1); + // Direct Link + $lk = current($tcv); + $guiObj->direct_link = trim($_SESSION['basehref'], '/') . + "/ltx.php?item=exec&feature_id=" . $lk[0]['feature_id'] . "&build_id=" . + $argsObj->build_id; + + $argsObj->direct_link = $guiObj->direct_link; + + // Information for Issue Management + // Common Info + $signature = new stdClass(); + $signature->tcname = $tc_info['name']; + $signature->tcpathname = $tcaseMgr->getPathName($tcase_id); + $signature->tcversion_id = $tcversion_id; + + list ($guiObj->bug_summary, $guiObj->issueSummaryForStep) = genIssueSummary( + $tcaseMgr, $signature, $guiObj->executionContext); + + // return more data eid, has cf on exec + return array( + $tcase_id, + $tcversion_id, + $eid, + ($cf_map != null) + ); +} + +/* + * function: getLatestExec + * Important Notice: + * $tcase_id and $tcversions_id, can be ARRAYS when user enable bulk execution + * + * args : + * + * returns: + * + */ +function getLatestExec(&$dbHandler, $tcase_id, $tcversion_id, $guiObj, $argsObj, + &$tcaseMgr) +{ + $options = array( + 'getNoExecutions' => 1, + 'groupByBuild' => 0, + 'getStepsExecInfo' => 1 + ); + + $last_exec = $tcaseMgr->getLastExecution($tcase_id, $tcversion_id, + $argsObj->tplan_id, $argsObj->build_id, $argsObj->platform_id, $options); + + if (! is_null($last_exec)) { + $last_exec = setTesterAssignment($dbHandler, $last_exec, $tcaseMgr, + $argsObj->tplan_id, $argsObj->platform_id, $argsObj->build_id); + + // Warning: setCanExecute() must be called AFTER setTesterAssignment() + $can_execute = $guiObj->grants->execute && ($guiObj->build_is_open); + $last_exec = setCanExecute($last_exec, $guiObj->exec_mode, $can_execute, + $argsObj->user_id); + } + + // Reorder executions to mantaing correct visualization order. + if (is_array($tcversion_id)) { + $last_exec = reorderExecutions($tcversion_id, $last_exec); + } + + return $last_exec; +} + +/** + * Function retrieve test steps backup + * + * @param testcase $tcaseMgr + * the testcase Manager + * @param array $guiObj + * @param int $testPlanId + * @param int $platformId + * @param int $buildId + * return map + */ +// Has to be moved to testcase class +function getBackupSteps(&$tcaseMgr, $guiObj, $testPlanId, $platformId, $buildId) +{ + $stepsIds = array(); + foreach ($guiObj->map_last_exec as $tcId => $elements) { + foreach ($guiObj->map_last_exec[$tcId]['steps'] as $step) { + array_push($stepsIds, $step["id"]); + } + } + + return $tcaseMgr->getBackupSteps($stepsIds, $testPlanId, $platformId, + $buildId); +} + +/* + * function: getOtherExecutions + * + * args : + * + * returns: + * + * rev: + */ +function getOtherExecutions(&$dbHandler, $tcase_id, $tcversion_id, $guiObj, + $argsObj, &$cfgObj, &$tcaseMgr) +{ + $other_execs = null; + if ($guiObj->history_on) { + // CRITIC see for key names - testcases.class.php -> getExecutionSet() + $execContext = array( + 'testplan_id' => $argsObj->tplan_id, + 'platform_id' => $argsObj->platform_id, + 'build_id' => $argsObj->build_id + ); + + if ($cfgObj->exec_cfg->show_history_all_builds) { + $execContext['build_id'] = null; + } + if ($cfgObj->exec_cfg->show_history_all_platforms) { + $execContext['platform_id'] = null; + } + + $options = array( + 'exec_id_order' => $cfgObj->exec_cfg->history_order + ); + $other_execs = $tcaseMgr->getExecutionSet($tcase_id, $tcversion_id, + $execContext, $options); + } else { + // Warning!!!: + // we can't use the data we have got with previous call to getLastExecution() + // because if user have asked to save results last execution data may be has changed + $aux_map = $tcaseMgr->getLastExecution($tcase_id, $tcversion_id, + $argsObj->tplan_id, $argsObj->build_id, $argsObj->platform_id); + if (! is_null($aux_map)) { + $other_execs = array(); + foreach ($aux_map as $key => $value) { + $other_execs[$key] = array( + $value + ); + } + } + } + return $other_execs; +} + +/* + * function: processTestSuite + * + * args : + * + * returns: + * + */ +function processTestSuite(&$dbHandler, &$guiObj, &$argsObj, $testSet, &$treeMgr, + &$tcaseMgr, &$docRepository) +{ + $locationFilters = $tcaseMgr->buildCFLocationMap(); + $cf_filters = array( + 'show_on_execution' => 1 + ); + + // Get the path for every test case, grouping test cases that have same parent. + $testCaseQty = count($testSet->tcase_id); + if ($testCaseQty > 0) { + $dummy = $tcaseMgr->cfield_mgr->getLocations(); + $verboseLocationCode = array_flip($dummy['testcase']); + $filters = null; + foreach ($verboseLocationCode as $key => $value) { + $filters[$key]['location'] = $value; + } + + $dummy_id = current($testSet->tcase_id); + $index = $testCaseQty == 1 ? $dummy_id : 0; // 0 => BULK + $suffix = '_' . $index; + $execution_time_cfields = $tcaseMgr->html_table_of_custom_field_inputs( + $dummy_id, $argsObj->tproject_id, 'execution', $suffix, null, null, + $argsObj->tproject_id); + + $guiObj->execution_time_cfields[$index] = $execution_time_cfields; + $gdx = 0; + foreach ($testSet->tcase_id as $testcase_id) { + $path_f = $treeMgr->get_path($testcase_id, null, 'full'); + foreach ($path_f as $path_elem) { + if ($path_elem['parent_id'] == $argsObj->id) { + // Can be added because is present in the branch the user wants to view + // ID of branch starting node is in $argsObj->id + $guiObj->tcAttachments[$testcase_id] = getAttachmentInfos( + $docRepository, $testcase_id, 'nodes_hierarchy', true, 1); + + foreach ($locationFilters as $locationKey => $filterValue) { + $finalFilters = $cf_filters + $filterValue; + $guiObj->design_time_cfields[$testcase_id][$locationKey] = $tcaseMgr->html_table_of_custom_field_values( + $testcase_id, 'design', $finalFilters, null, null, + $argsObj->tproject_id, null, + $testSet->tcversion_id[$gdx]); + + $guiObj->testplan_design_time_cfields[$testcase_id] = $tcaseMgr->html_table_of_custom_field_values( + $testcase_id, 'testplan_design', $cf_filters, null, + null, $argsObj->tproject_id); + } + + if ($guiObj->grants->execute) { + $guiObj->execution_time_cfields[$testcase_id] = $tcaseMgr->html_table_of_custom_field_inputs( + $testcase_id, null, 'execution', "_" . $testcase_id, + null, null, $argsObj->tproject_id); + } + } // if( $path_elem['parent_id'] == $argsObj->id ) + + // We do this because do not know if some test case not yet analised will be direct + // child of this test suite, then we get this info in advance. + // In situations where only last test suite on branch have test cases, we are colleting + // info we will never use. + if ($path_elem['node_table'] == 'testsuites' && + ! isset($guiObj->tSuiteAttachments[$path_elem['id']])) { + $guiObj->tSuiteAttachments[$path_elem['id']] = getAttachmentInfos( + $docRepository, $path_elem['id'], 'nodes_hierarchy', + true, 1); + } + } + $gdx ++; + } + } +} + +/** + */ +function buildExecContext(&$argsObj, $tcasePrefix, &$tplanMgr, &$tcaseMgr) +{ + $ret = array(); + $ret['target'] = array( + 'tc_versions' => null, + 'version_id' => null, + 'feature_id' => null, + 'basic_info' => null + ); + $ret['context'] = array( + 'tproject_id' => null, + 'tplan_id' => null, + 'platform_id' => null, + 'build_id' => null, + 'user_id' => null + ); + + foreach ($ret as &$value) { + foreach ($value as $key => $dummy) { + if (property_exists($argsObj, $key)) { + $value[$key] = $argsObj->$key; + } + } + } + + // Now get another important information feature_id on testplan_tcversions + // needed to get remote execution server config if this config has been + // done with Custom Fields at Test Plan Design Time + foreach ($ret['target']['tc_versions'] as $tcv_id => $tc_id) { + $ret['target']['feature_id'][$tcv_id] = $tplanMgr->getFeatureID( + $ret['context']['tplan_id'], $ret['context']['platform_id'], $tcv_id); + + $dummy = $tcaseMgr->get_basic_info($tc_id, array( + 'id' => $tcv_id + )); + $dummy[0]['tcasePrefix'] = $tcasePrefix; + $ret['target']['basic_info'][$tcv_id] = $dummy[0]; + } + return $ret; +} + +function launchRemoteExec(&$dbHandler, &$argsObj, $tcasePrefix, &$tplanMgr, + &$tcaseMgr) +{ + // IMPORTANT NOTICE + // Remote execution will NOT use ANY of data typed by user, + // - notes + // - custom fields + // + // IMPORTANT NOTICE + // need to understand what to do with feedback provided + // by do_remote_execution(). + // Right now no matter how things go, no feedback is given to user. + // May be this need to be improved in future. + // + // Only drawback i see is when remote exec is done on a test suite + // and amount of feedback can be high, then do not see what can be effect + // on GUI + $execContext = buildExecContext($argsObj, $tcasePrefix, $tplanMgr, $tcaseMgr); + $feedback = doRemoteExecution($dbHandler, $execContext); + $feedback = current($feedback); + return $feedback; +} + +/** + * + * @use testplan->filterByOnDesignCustomFields + * + */ +function getLinkedItems($argsObj, $historyOn, $cfgObj, $tcaseMgr, $tplanMgr, + $identity = null) +{ + $ltcv = null; + $idCard = null; + $itemSet = null; + + if (null == $argsObj->tsuite_id) { + if (! is_null($identity)) { + $idCard = $identity; + } elseif (! is_null($argsObj->tc_id) && ! is_array($argsObj->tc_id)) { + $idCard = array( + 'id' => $argsObj->tc_id, + 'version_id' => $argsObj->version_id + ); + } + + $idCard['version_id'] = $tplanMgr->getVersionLinked($argsObj->tplan_id, + $idCard['id']); + } + + if (! is_null($idCard)) { + // CRITIC see for key names - testcases.class.php -> getExecutionSet() + $execContext = array( + 'testplan_id' => $argsObj->tplan_id, + 'platform_id' => $argsObj->platform_id, + 'build_id' => $argsObj->build_id + ); + + $ltcv = null; + if ($historyOn) { + $execContext['testplan_id'] = $argsObj->tplan_id; + $ltcv = $tcaseMgr->getExecutionSet($idCard['id'], null, $execContext); + } + + // lazy implementation: + // getExecutionSet() returns data ONLY for Statuses that are written ON DB, + // then if full history for test case is NOT RUN, we are doomed!! + if (! $historyOn || is_null($ltcv)) { + $opt = null; + $ltcv = $tcaseMgr->getLatestExecSingleContext($idCard, $execContext, + $opt); + } + } else { + // ----------------------------------------------------------- + // When nullify filter_status - 20080504 - DO NOT REMOVE - + // + // May be in the following situation we do not HAVE to apply filter status: + // 1. User have filter for Not Run on Tree + // 2. Clicks on TC XXX + // 3. Executes TC + // 4. DO NOT UPDATE TREE. + // we do not update automatically to avoid: + // a) performance problems + // b) delays on operations due to tree redraw + // c) loose tree status due to lack of feature of tree engine + // + // 5. Clicks again on TC XXX + // If we use filter, we will get No Data Available. + // + // When working on show_testsuite_contents mode (OLD MODE) + // when we show all testcases inside a testsuite + // that verifies a filter criteria + // WE NEED TO APPLY FILTER + // + // We do not have this problem when this page is called after user have executed, + // probably because filter_status is not send back. + // + // I will add logic to nullify filter_status on init_args() + $options = array( + 'only_executed' => true, + 'output' => $historyOn ? 'mapOfArray' : 'mapOfMap', + 'include_unassigned' => $argsObj->include_unassigned, + 'group_by_build' => 'add_build', + 'last_execution' => ! $historyOn + ); + + if (is_null($argsObj->filter_status) || + in_array($cfgObj->tc_status['not_run'], + (array) $argsObj->filter_status)) { + $options['only_executed'] = false; + } + + // args->tsuites_id: is only used when user click on a test suite. + // probably is used only when bulk execution is enabled. + // + // if args->tc_id is not null, theorically all other filters are useless. + // why ? + // Because will normally call this script, + // from the execution tree and if we can click + // on a tree node, this means it has passed all filters. + // + // + // $args->platform_id: needed to get execution status info + // $args->build_id: needed to get execution status info + // + $basic_filters = array( + 'tcase_id' => $argsObj->tc_id, + 'platform_id' => $argsObj->platform_id, + 'build_id' => $argsObj->build_id + ); + + // This filters are useful when bulk execution is enabled, + // and user do click on a test suite on execution tree. + + // seems to be useless => 'cf_hash' => $argsObj->filter_cfields, + // need to review $tplanMgr->getLinkedForExecTree + $bulk_filters = array( + 'keyword_id' => $argsObj->keyword_id, + 'assigned_to' => $argsObj->filter_assigned_to, + 'exec_status' => $argsObj->filter_status, + 'tsuites_id' => $argsObj->tsuite_id, + 'assigned_on_build' => $argsObj->build_id, + 'exec_type' => $argsObj->execution_type, + 'urgencyImportance' => $argsObj->priority + ); + + // CRITIC / IMPORTANT + // With BULK Operation enabled, we prefer to display Test cases + // that are ONLY DIRECT CHILDREN + // of test suite id => we do not do deep walk. + // Think is a good choice, to avoid retrieving lot of info. + // May be we need to add a config parameter (or better an option at GUI level) + // in order to allow use how he / she wants to work. + // + $filters = array_merge($basic_filters, $bulk_filters); + + if (! is_null( + $sql2do = $tplanMgr->getLinkedForExecTree($argsObj->tplan_id, + $filters, $options))) { + if (is_array($sql2do)) { + if (isset($filters['keyword_filter_type']) && + ($filters['keyword_filter_type'] == 'And')) { + $kmethod = "fetchRowsIntoMapAddRC"; + $unionClause = " UNION ALL "; + } else { + $kmethod = "fetchRowsIntoMap"; + $unionClause = ' UNION '; + } + $sql2run = $sql2do['exec'] . $unionClause . $sql2do['not_run']; + } else { + $sql2run = $sql2do; + } + + // Development Notice: + // CUMULATIVE is used only to create same type of datastructe that existed + // before this refactoring + // + // $tex = $tcaseMgr->db->$kmethod($sql2run,'tcase_id',database::CUMULATIVE); + $sql2run .= ' ORDER BY exec_order '; + + $ltcv = $tex = $tcaseMgr->db->$kmethod($sql2run, 'tcase_id'); + if (! is_null($tex)) { + // We need to create: + // one set for Custom fields that apply to DESIGN + // one set for Custom fields that apply to TESTPLAN DESIGN + + if (! is_null($argsObj->filter_cfields)) { + $tk = array_keys($argsObj->filter_cfields); + $cf = null; + // foreach( array('design','testplan_design') as $l4) + foreach (array( + 'design' + ) as $l4) { + $cf[$l4] = $tplanMgr->cfield_mgr->getByIDAndEnableOn( + $tk, array( + $l4 => true + )); + } + if (isset($cf['design']) && ! is_null($cf['design'])) { + foreach ($cf['design'] as $yy => $xc) { + $az[$yy] = $argsObj->filter_cfields[$yy]; + } + $tex = $tplanMgr->filterByOnDesignCustomFields($tex, $az); + } + } + + foreach ($tex as $xkey => $xvalue) { + $itemSet->tcase_id[] = $xkey; + $itemSet->tcversion_id[] = $xvalue['tcversion_id']; + } + } + } + } + return array( + $ltcv, + $itemSet + ); +} + +/** + */ +function initWebEditors(&$guiObj, $cfgObj, $baseHREF) +{ + if ($guiObj->can_use_bulk_op) { + $of = web_editor("bulk_exec_notes", $baseHREF, $cfgObj->editorCfg); + $of->Value = getItemTemplateContents('execution_template', + $of->InstanceName, null); + + // Magic numbers that can be determined by trial and error + $cols = intval( + isset($editorCfg['cols']) ? $cfgObj->editorCfg['cols'] : 60); + $rows = intval( + isset($editorCfg['rows']) ? $cfgObj->editorCfg['rows'] : 10); + $guiObj->bulk_exec_notes_editor = $of->CreateHTML($rows, $cols); + unset($of); + } else { + $guiObj->exec_notes_editors = createExecNotesWebEditor( + $guiObj->map_last_exec, $baseHREF, $cfgObj->editorCfg, + $cfgObj->exec_cfg, $guiObj->lexNotes); + } +} + +/** + * get info from ... + */ +function getSettingsAndFilters(&$argsObj) +{ + $mode = 'execution_mode'; + $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $sf = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$form_token]) ? $_SESSION[$mode][$form_token] : null; + + $argsObj->testcases_to_show = isset($sf['testcases_to_show']) ? $sf['testcases_to_show'] : null; + + // just for better readability + $filters = [ + 'filter_status' => 'filter_result_result', + 'filter_assigned_to' => 'filter_assigned_user', + 'execution_type' => 'filter_execution_type', + 'priority' => 'filter_priority', + 'filter_cfields' => 'filter_custom_fields' + ]; + $settings = [ + 'build_id' => 'setting_build', + 'platform_id' => 'setting_platform' + ]; + + $key2null = array_merge($filters, $settings); + $isNumeric = [ + 'build_id' => 0, + 'platform_id' => - 1 + ]; + + foreach ($key2null as $key => $sfKey) { + $argsObj->$key = isset($sf[$sfKey]) ? $sf[$sfKey] : null; + if (is_null($argsObj->$key)) { + // let's this page be functional withouth a form token too + // (when called from testcases assigned to me) + $argsObj->$key = isset($_REQUEST[$sfKey]) ? $_REQUEST[$sfKey] : null; + } + + if (isset($isNumeric[$key])) { + $argsObj->$key = intval($argsObj->$key); + } + } + + // keywords filter + $argsObj->keyword_id = 0; + if (isset($sf['filter_keywords'])) { + $argsObj->keyword_id = $sf['filter_keywords']; + if (is_array($argsObj->keyword_id) && count($argsObj->keyword_id) == 1) { + $argsObj->keyword_id = $argsObj->keyword_id[0]; + } + } + + $argsObj->keywordsFilterType = null; + if (isset($sf['filter_keywords_filter_type'])) { + $argsObj->keywordsFilterType = $sf['filter_keywords_filter_type']; + } + + // 20190119 + if (! property_exists($argsObj, 'refreshTree')) { + $argsObj->refreshTree = true; + } + $argsObj->refreshTree = isset($sf['setting_refresh_tree_on_action']) ? $sf['setting_refresh_tree_on_action'] : $argsObj->refreshTree; + + // Checkbox + $tgk = 'filter_assigned_user_include_unassigned'; + $argsObj->include_unassigned = isset($sf[$tgk]) && ($sf[$tgk] != 0 ? 1 : 0); +} + +/** + * get info from cookies and also set values on cookies + */ +function manageCookies(&$argsObj, $cfgObj) +{ + $cookieExecPrefix = 'TL_execSetResults_'; + + // IMPORTANT: logic for test suite notes CAN NOT BE IMPLEMENTED HERE + // see smarty_assign_tsuite_info() in this file. + $key4cookies = array( + 'tpn_view_status' => 'testplan_notes', + 'bn_view_status' => 'build_description', + 'platform_notes_view_status' => 'platform_description' + ); + + $key2loop = array( + 'id' => 0, + 'exec_to_delete' => 0, + 'version_id' => 0, + 'tpn_view_status' => 0, + 'bn_view_status' => 0, + 'bc_view_status' => 1, + 'platform_notes_view_status' => 0 + ); + + foreach ($key4cookies as $key => $cfgKey) { + $cookieKey = $cookieExecPrefix . $key; + if (! isset($_REQUEST[$key])) { + // First time we are entered here => we can need to understand how to proceed + switch ($cfgObj->exec_cfg->expand_collapse->$cfgKey) { + case LAST_USER_CHOICE: + if (isset($_COOKIE[$cookieKey])) { + $key2loop[$key] = $_COOKIE[$cookieKey]; + } + break; + + default: + $key2loop[$key] = $cfgObj->exec_cfg->expand_collapse->$cfgKey; + break; + } + } + } + + $ckObj = new stdClass(); + foreach ($key2loop as $key => $value) { + $argsObj->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : $value; + if (isset($key4cookies[$key])) { + $ckObj->name = $cfgObj->cookie->prefix . $cookieExecPrefix . $key; + $ckObj->value = $argsObj->$key; + tlSetCookie($ckObj); + } + } +} + +/** + */ +function getResultsIcons() +{ + $resultsCfg = config_get('results'); + // loop over status for user interface, because these are the statuses + // user can assign while executing test cases + foreach ($resultsCfg['status_icons_for_exec_ui'] as $verbose_status => $ele) { + if ($verbose_status != 'not_run') { + $code = $resultsCfg['status_code'][$verbose_status]; + $items[$code] = $ele; + $items[$code]['title'] = lang_get($items[$code]['title']); + } + } + return $items; +} + +/** + */ +function getResultsIconsNext() +{ + $resultsCfg = config_get('results'); + // loop over status for user interface, because these are the statuses + // user can assign while executing test cases + foreach ($resultsCfg['status_icons_for_exec_next_ui'] as $verbose_status => $ele) { + if ($verbose_status != 'not_run') { + $code = $resultsCfg['status_code'][$verbose_status]; + $items[$code] = $ele; + $items[$code]['title'] = lang_get($items[$code]['title']); + } + } + return $items; +} + +/** + */ +function genIssueSummary(&$tcaseMgr, $signature, $context) +{ + $cfg = config_get('exec_cfg'); + + // Work on labels + $text = array(); + $text['tcase'] = helperLabels($cfg->issues->tcase_level->subject); + $text['tcstep'] = helperLabels($cfg->issues->tcstep_level->subject); + + // Work on values + $ecx = &$context; + $searchFor = array( + '%%TCNAME%%', + '%%PROJECTNAME%%', + '%%PLANNAME%%', + '%%BUILDNAME%%', + '%%PLATFNAME%%', + '%%TCPATHNAME%%', + '%%EXECTSISO%%' + ); + + $replaceWith = array( + $signature->tcname, + $ecx['tproject_name'], + $ecx['tplan_name'], + $ecx['build_name'], + $ecx['platform_name'], + $signature->tcpathname, + date('Y-m-dTH:i', time()) + ); + + $nu = array(); + $nu['tcase'] = str_replace($searchFor, $replaceWith, $text['tcase']); + $nu['tcstep'] = null; + + $opt = array( + 'fields2get' => 'step_number,id' + ); + $steps = $tcaseMgr->get_steps($signature->tcversion_id, 0, $opt); + if (null != $steps) { + $tstx = str_replace($searchFor, $replaceWith, $text['tcstep']); + foreach ($steps as $elem) { + $nu['tcstep'][$elem['id']] = str_replace('%%STEPNUMBER%%', + $elem['step_number'], $tstx); + } + } + + return array( + $nu['tcase'], + $nu['tcstep'] + ); +} + +/** + */ +function helperLabels($haystack) +{ + $searchFor = array( + '$$issue_on_step', + '$$issue_subject_tcname', + '$$issue_subject_tcpathname', + '$$issue_subject_projectname', + '$$issue_subject_planname', + '$$issue_subject_buildname', + '$$issue_subject_platfname', + '$$issue_subject_execon' + ); + + $replaceWith = array(); + foreach ($searchFor as $lblKey) { + $jk = str_replace('$$', '', $lblKey); + $replaceWith[] = lang_get($jk); + } + return str_replace($searchFor, $replaceWith, $haystack); +} + +/** + */ +function initExecValuesMenus($tcStatusCfg, $execStatusToExclude) +{ + $remove = array( + $tcStatusCfg['not_run'] + ); + $execStatusTestCase = $execStatusTestCaseStep = createResultsMenu($remove); + + foreach ($execStatusToExclude['testcase'] as $code) { + if (isset($execStatusTestCase[$code])) { + unset($execStatusTestCase[$code]); + } + } + + foreach ($execStatusToExclude['step'] as $code) { + if (isset($execStatusTestCaseStep[$code])) { + unset($execStatusTestCaseStep[$code]); + } + } + + return array( + $execStatusTestCase, + $execStatusTestCaseStep + ); } diff --git a/lib/execute/getExecNotes.php b/lib/execute/getExecNotes.php index 44002bed6f..93e481bc74 100644 --- a/lib/execute/getExecNotes.php +++ b/lib/execute/getExecNotes.php @@ -1,70 +1,71 @@ -exec_id); -$notesContent = $map[0]['notes']; - -$readonly = $args->readonly > 0 ? 'readonly="readonly"' : ''; -$smarty = new TLSmarty(); -$smarty->assign('notes',$notesContent); -$smarty->assign('webeditorCfg',$webeditorCfg); -$smarty->assign('webeditorType',$webeditorCfg['type']); -$smarty->assign('readonly',$readonly); -$smarty->assign('editor_instance','exec_notes_' . $args->exec_id); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -function createExecNotesWebEditor($id,$basehref,$editorCfg,$content=null) -{ - // Important Notice: - // - // When using tinymce or none as web editor, we need to set rows and cols - // to appropriate values, to avoid an ugly ui. - // null => use default values defined on editor class file - // - // Rows and Cols values are useless for FCKeditor. - // - $of=web_editor("exec_notes_$id",$basehref,$editorCfg) ; - $of->Value = $content; - $editor=$of->CreateHTML(10,60); - unset($of); - return $editor; -} - - - -function init_args() -{ - $iParams = array("exec_id" => array(tlInputParameter::INT_N), - "readonly" => array(tlInputParameter::INT_N)); - $args = new stdClass(); - R_PARAMS($iParams,$args); - return $args; -} -?> \ No newline at end of file +exec_id); +$notesContent = $map[0]['notes']; + +$readonly = $args->readonly > 0 ? 'readonly="readonly"' : ''; +$smarty = new TLSmarty(); +$smarty->assign('notes', $notesContent); +$smarty->assign('webeditorCfg', $webeditorCfg); +$smarty->assign('webeditorType', $webeditorCfg['type']); +$smarty->assign('readonly', $readonly); +$smarty->assign('editor_instance', 'exec_notes_' . $args->exec_id); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +function createExecNotesWebEditor($id, $basehref, $editorCfg, $content = null) +{ + // Important Notice: + // + // When using tinymce or none as web editor, we need to set rows and cols + // to appropriate values, to avoid an ugly ui. + // null => use default values defined on editor class file + // + // Rows and Cols values are useless for FCKeditor. + // + $of = web_editor("exec_notes_$id", $basehref, $editorCfg); + $of->Value = $content; + $editor = $of->CreateHTML(10, 60); + unset($of); + return $editor; +} + +function initArgs() +{ + $iParams = array( + "exec_id" => array( + tlInputParameter::INT_N + ), + "readonly" => array( + tlInputParameter::INT_N + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + return $args; +} +?> diff --git a/lib/experiments/README.md b/lib/experiments/README.md new file mode 100644 index 0000000000..34f26ec913 --- /dev/null +++ b/lib/experiments/README.md @@ -0,0 +1,3 @@ +# lib/experiments/README.md +Some examples that allows to test different features +Only documentation is any exist is in the code \ No newline at end of file diff --git a/lib/experiments/gitlab.php b/lib/experiments/gitlab.php new file mode 100644 index 0000000000..389b68e7d7 --- /dev/null +++ b/lib/experiments/gitlab.php @@ -0,0 +1,62 @@ + '99b938c71df5dc93ac0c5dc81cdc6e33906d9708e8297d1fe2b99f872279fda7', + + 'clientSecret' => '57d10fc0e6fe9f95ee9faf371dd5d02033fa0b0e63f3db966763c405e5b118ef', + 'redirectUri' => $redu + ]); + +session_start(); + +if (! isset($_GET['code'])) { + // If we don't have an authorization code then get one + $authUrl = $provider->getAuthorizationUrl(); + $_SESSION['oauth2state'] = $provider->getState(); + header('Location: ' . $authUrl); + exit(); + + // Check given state against previously stored one to mitigate CSRF attack +} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { + + var_dump($_GET['state']); + die(); + unset($_SESSION['oauth2state']); + exit('Invalid state'); +} else { + + // Try to get an access token (using the authorization code grant) + $token = $provider->getAccessToken('authorization_code', + [ + 'code' => $_GET['code'] + ]); + + // Optional: Now you have a token you can look up a users profile data + try { + + // We got an access token, let's now get the user's details + $user = $provider->getResourceOwner($token); + + echo '
    ';
    +        var_dump($user->toArray());
    +        echo '
    '; + // Use these details to create a new profile + printf('
    getName %s!', $user->getName()); + printf('
    getEmail %s!', $user->getEmail()); + printf('
    getUserName %s!', $user->getUserName()); + echo '
    '; + } catch (Exception $e) { + + // Failed to get user details + exit('Oh dear...'); + } + + // Use this to interact with an API on the users behalf + echo $token->getToken(); +} diff --git a/lib/experiments/google.php b/lib/experiments/google.php new file mode 100644 index 0000000000..3ed32771bd --- /dev/null +++ b/lib/experiments/google.php @@ -0,0 +1,62 @@ + '860603525614-fscj9cgr2dvks51uh6odl67skec536fd.apps.googleusercontent.com', + 'clientSecret' => '_YOKquNTa4Fux-OMJoxDBuov', + 'redirectUri' => $redu + ]); + +if (! empty($_GET['error'])) { + + // Got an error, probably user denied access + exit('Got error: ' . htmlspecialchars($_GET['error'], ENT_QUOTES, 'UTF-8')); +} elseif (empty($_GET['code'])) { + + // If we don't have an authorization code then get one + $authUrl = $provider->getAuthorizationUrl(); + $_SESSION['oauth2state'] = $provider->getState(); + header('Location: ' . $authUrl); + exit(); +} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { + + // State is invalid, possible CSRF attack in progress + unset($_SESSION['oauth2state']); + exit('Invalid state'); +} else { + + // Try to get an access token (using the authorization code grant) + $token = $provider->getAccessToken('authorization_code', + [ + 'code' => $_GET['code'] + ]); + + // Optional: Now you have a token you can look up a users profile data + try { + // We got an access token, let's now get the owner details + $ownerDetails = $provider->getResourceOwner($token); + + // Use these details to create a new profile + printf('Hello %s!', $ownerDetails->getFirstName()); + } catch (Exception $e) { + + // Failed to get user details + exit('Something went wrong: ' . $e->getMessage()); + } + + // Use this to interact with an API on the users behalf + echo $token->getToken(); + + // Use this to get a new access token if the old one expires + echo $token->getRefreshToken(); + + // Unix timestamp at which the access token expires + echo $token->getExpires(); +} diff --git a/lib/experiments/lea.php b/lib/experiments/lea.php new file mode 100644 index 0000000000..b7a0a77acb --- /dev/null +++ b/lib/experiments/lea.php @@ -0,0 +1,74 @@ + 'demoapp', // The client ID assigned to you by the provider + 'clientSecret' => 'demopass', // The client password assigned to you by the provider + 'redirectUri' => 'http://example.com/your-redirect-url/', + 'urlAuthorize' => 'http://brentertainment.com/oauth2/lockdin/authorize', + 'urlAccessToken' => 'http://brentertainment.com/oauth2/lockdin/token', + 'urlResourceOwnerDetails' => 'http://brentertainment.com/oauth2/lockdin/resource' + ]); + +// If we don't have an authorization code then get one +if (! isset($_GET['code'])) { + + // Fetch the authorization URL from the provider; this returns the + // urlAuthorize option and generates and applies any necessary parameters + // (e.g. state). + $authorizationUrl = $provider->getAuthorizationUrl(); + + // Get the state generated for you and store it to the session. + $_SESSION['oauth2state'] = $provider->getState(); + + // Redirect the user to the authorization URL. + header('Location: ' . $authorizationUrl); + exit(); + + // Check given state against previously stored one to mitigate CSRF attack +} elseif (empty($_GET['state']) || + (isset($_SESSION['oauth2state']) && + $_GET['state'] !== $_SESSION['oauth2state'])) { + + if (isset($_SESSION['oauth2state'])) { + unset($_SESSION['oauth2state']); + } + + exit('Invalid state'); +} else { + + try { + + // Try to get an access token using the authorization code grant. + $accessToken = $provider->getAccessToken('authorization_code', + [ + 'code' => $_GET['code'] + ]); + + // We have an access token, which we may use in authenticated + // requests against the service provider's API. + echo 'Access Token: ' . $accessToken->getToken() . "
    "; + echo 'Refresh Token: ' . $accessToken->getRefreshToken() . "
    "; + echo 'Expired in: ' . $accessToken->getExpires() . "
    "; + echo 'Already expired? ' . + ($accessToken->hasExpired() ? 'expired' : 'not expired') . "
    "; + + // Using the access token, we may look up details about the + // resource owner. + $resourceOwner = $provider->getResourceOwner($accessToken); + + var_export($resourceOwner->toArray()); + + // The provider provides a way to get an authenticated API request for + // the service, using the access token; it returns an object conforming + // to Psr\Http\Message\RequestInterface. + $request = $provider->getAuthenticatedRequest('GET', + 'http://brentertainment.com/oauth2/lockdin/resource', $accessToken); + } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { + + // Failed to get the access token or user details. + exit($e->getMessage()); + } +} diff --git a/lib/functions/APIKey.class.php b/lib/functions/APIKey.class.php index 6d089b79c5..819ad6e6a9 100644 --- a/lib/functions/APIKey.class.php +++ b/lib/functions/APIKey.class.php @@ -1,122 +1,100 @@ -object_table = $this->tables["users"]; - } - - /* - function: addKeyForUser - - args: userid - - returns: tl::OK / tl::ERROR - - */ - public function addKeyForUser($userID) - { - $query = "UPDATE {$this->object_table} " . - " SET script_key='" . $this->generateKey() . "' " . - " WHERE id='".intval($userID)."'"; - $result = $this->db->exec_query($query); - - if ($result) - { - $this->dbID = $this->db->insert_id(); - } - return $result ? tl::OK : tl::ERROR; - } - - /* - function: generateKey - - args: - - - returns: key - - */ - private function generateKey() - { - $key = ''; - - for($i=0; $i<8; $i++) - { - $key .= mt_rand(); - } - - return md5($key); - } - - /* - function: getAPIKey - - args: - - - returns: key - - */ - public function getAPIKey($userID) - { - $key=null; - $key_map=$this->getAPIKeys($userID); - - if( !is_null($key_map) ) - { - $key = $key_map[$userID]; - } - - return $key; - } - - - /* - function: getAPIKeys - - args: [userID]=default null => all APIkeys - - returns: map - associative array[userID]=script_key - - */ - public function getAPIKeys($userID=null) - { - $query = "SELECT id, script_key " . - " FROM {$this->object_table} " ; - - if( is_null($userID) ) - { - $whereClause = " WHERE script_key IS NOT NULL"; - } - else - { - $whereClause = " WHERE id=" . intval($userID); - } - $query .= $whereClause; - - $rs = $this->db->fetchColumnsIntoMap($query, 'id', 'script_key'); - return $rs; - } -} -?> \ No newline at end of file +object_table = $this->tables["users"]; + } + + /** + * + * @param int $userID + * @return mixed tl::OK / tl::ERROR + */ + public function addKeyForUser($userID) + { + $query = "UPDATE {$this->object_table} " . " SET script_key='" . + $this->generateKey() . "' " . " WHERE id='" . intval($userID) . "'"; + $result = $this->db->exec_query($query); + + if ($result) { + $this->dbID = $this->db->insert_id(); + } + return $result ? tl::OK : tl::ERROR; + } + + /** + * + * @return string md5 hash of a string + */ + private function generateKey() + { + $key = ''; + + for ($i = 0; $i < 8; $i ++) { + $key .= mt_rand(); + } + + return md5($key); + } + + /** + * + * @param int $userID + * @return $key + */ + public function getAPIKey($userID) + { + $key = null; + $key_map = $this->getAPIKeys($userID); + + if (! is_null($key_map)) { + $key = $key_map[$userID]; + } + + return $key; + } + + /** + * + * @param int $userID + * [userID]=default null => all APIkeys + * @return array associative array[userID]=script_key + */ + public function getAPIKeys($userID = null) + { + $query = "SELECT id, script_key " . " FROM {$this->object_table} "; + + if (is_null($userID)) { + $whereClause = " WHERE script_key IS NOT NULL"; + } else { + $whereClause = " WHERE id=" . intval($userID); + } + $query .= $whereClause; + + return $this->db->fetchColumnsIntoMap($query, 'id', 'script_key'); + } +} +?> diff --git a/lib/functions/assignment_mgr.class.php b/lib/functions/assignment_mgr.class.php index 77dbe39e8c..fe37d95e6e 100644 --- a/lib/functions/assignment_mgr.class.php +++ b/lib/functions/assignment_mgr.class.php @@ -1,590 +1,568 @@ -tables['assignment_types']}"; - $hash_types = $this->db->fetchRowsIntoMap($sql,$key_field); - } - return $hash_types; - } - - /* - $key_field: contains the name column that has to be used as the key of - the returned hash. - */ - function get_available_status($key_field='description') - { - static $hash_types; - if (!$hash_types) - { - $sql = " SELECT * FROM {$this->tables['assignment_status']} "; - $hash_types = $this->db->fetchRowsIntoMap($sql,$key_field); - } - - return $hash_types; - } - - // $feature_id can be an scalar or an array - function delete_by_feature_id($feature_id) - { - if( is_array($feature_id) ) - { - $feature_id_list = implode(",",$feature_id); - $where_clause = " WHERE feature_id IN ($feature_id_list) "; - } - else - { - $where_clause = " WHERE feature_id={$feature_id}"; - } - $sql = " DELETE FROM {$this->tables['user_assignments']} {$where_clause}"; - $result = $this->db->exec_query($sql); - } - - /** - * Delete the user assignments for a given build. - * - * @author Andreas Simon - * @param int $build_id The ID of the build for which the user assignments shall be deleted. - * @param int $delete_all_types If true, all assignments regardless of type will be deleted, - * else (default) only tester assignments. - */ - function delete_by_build_id($build_id, $delete_all_types = false) - { - $type_sql = ""; - - if (!$delete_all_types) - { - $types = $this->get_available_types(); - $tc_execution_type = $types['testcase_execution']['id']; - $type_sql = " AND type = {$tc_execution_type} "; - } - - $sql = " DELETE FROM {$this->tables['user_assignments']} " . - " WHERE build_id = " . intval($build_id) . " {$type_sql} "; - - $this->db->exec_query($sql); - } - - // delete assignments by feature id and build_id - function delete_by_feature_id_and_build_id($feature_map) - { - $feature_id_list = implode(",",array_keys($feature_map)); - $where_clause = " WHERE feature_id IN ($feature_id_list) "; - - $sql = " DELETE FROM {$this->tables['user_assignments']} {$where_clause} "; - - // build_id is the same for all entries because of assignment form - // -> skip foreach after first iteration - $build_id = 0; - foreach ($feature_map as $key => $feature) - { - $build_id = $feature['build_id']; - break; - } - - $sql .= " AND build_id = {$build_id} "; - $result = $this->db->exec_query($sql); - } - - /** - * $items array of signature - * signature = array('type' => ,'feature_id' =>,'user_id' =>, 'build_id' => ) - * - */ - function deleteBySignature($items) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - foreach($items as $signature) - { - $sql = " DELETE FROM {$this->tables['user_assignments']} WHERE 1=1 "; - foreach($signature as $column => $val) - { - $sql .= " AND $column = " . intval($val); - } - $result = $this->db->exec_query($sql); - } - } - - - /** - * - * @param $feature_map - * $feature_map['feature_id']['user_id'] - * $feature_map['feature_id']['type'] - * $feature_map['feature_id']['status'] - * $feature_map['feature_id']['assigner_id'] - * $feature_map['feature_id']['build_id'] - * - * - * Need to manage situation where user_id = 0 is passed - * I will IGNORE IT - * - * @internal revisions - */ - function assign($feature_map) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array(); - $types = $this->get_available_types(); - $safe = null; - - foreach($feature_map as $feature_id => $elem) - { - $safe['feature_id'] = intval($feature_id); - $safe['build_id'] = intval($elem['build_id']); - $safe['type'] = intval($elem['type']); - - $uSet = (array)$elem['user_id']; - - foreach($uSet as $user_id) - { - $safe['user_id'] = intval($user_id); - - // Check if exists before adding - $check = "/* $debugMsg */ "; - $check .= " SELECT id FROM {$this->tables['user_assignments']} " . - " WHERE feature_id = " . $safe['feature_id'] . - " AND build_id = " . $safe['build_id'] . - " AND type = " . $safe['type'] . - " AND user_id = " . $safe['user_id']; - - $rs = $this->db->get_recordset($check); - if( is_null($rs) || count($rs) == 0 ) - { - if($safe['user_id'] > 0) - { - $sql = "INSERT INTO {$this->tables['user_assignments']} " . - "(feature_id,user_id,assigner_id,type,status,creation_ts"; - - $values = "VALUES({$safe['feature_id']},{$safe['user_id']}," . - "{$elem['assigner_id']}," . - "{$safe['type']},{$elem['status']},"; - $values .= (isset($elem['creation_ts']) ? $elem['creation_ts'] : $this->db->db_now()); - - if(isset($elem['deadline_ts']) ) - { - $sql .=",deadline_ts"; - $values .="," . $elem['deadline_ts']; - } - - if(isset($elem['build_id'])) - { - $sql .= ",build_id"; - $values .= "," . $safe['build_id']; - } - else - { - if($safe['type'] == $types['testcase_execution']['id']) - { - throw new Exception("Error Processing Request - BUILD ID is Mandatory"); - } - } - - $sql .= ") " . $values . ")"; - tLog(__METHOD__ . '::' . $sql,"DEBUG"); - $this->db->exec_query($sql); - $ret[] = $sql; - } - } - } // loop over users - } - return $ret; - } - - - /** - * - * @param $feature_map - * $feature_map: key => feature_id - * value => hash with optional keys - * that have the same name of user_assignment fields - * - * @internal revisions - */ - function update($feature_map) - { - foreach($feature_map as $feature_id => $elem) - { - $sepa = ""; - $sql = "UPDATE {$this->tables['user_assignments']} SET "; - $simple_fields = array('user_id','assigner_id','type','status'); - $date_fields = array('deadline_ts','creation_ts'); - - foreach($simple_fields as $idx => $field) - { - if(isset($elem[$field])) - { - $sql .= $sepa . "$field={$elem[$field]} "; - $sepa=","; - } - } - - foreach($date_fields as $idx => $field) - { - if(isset($elem[$field])) - { - $sql .= $sepa . "$field=" . $elem[$field] . " "; - $sepa = ","; - } - } - - $sql .= "WHERE feature_id={$feature_id} AND build_id={$elem['build_id']}"; - - $this->db->exec_query($sql); - } - } - - /** - * Get the number of assigned users for a given build ID. - * @param int $build_id ID of the build to check - * @param int $count_all_types if true, all assignments will be counted, otherwise - * only tester assignments - * @param int $user_id if given, user ID for which the assignments per build shall be counted - * @return int $count Number of assignments - */ - function get_count_of_assignments_for_build_id($build_id, $count_all_types = false, $user_id = 0) - { - $count = 0; - - $types = $this->get_available_types(); - $tc_execution_type = $types['testcase_execution']['id']; - $type_sql = ($count_all_types) ? "" : " AND type = {$tc_execution_type} "; - - $user_sql = ($user_id && is_numeric($user_id)) ? "AND user_id = {$user_id} " : ""; - - $sql = " SELECT COUNT(id) AS count FROM {$this->tables['user_assignments']} " . - " WHERE build_id = {$build_id} {$user_sql} {$type_sql} "; - - $count = $this->db->fetchOneValue($sql); - - return $count; - } - - /** - * Get count of assigned, but not run testcases per build (and optionally user). - * @param int $build_id - * @param bool $all_types - * @param int $user_id if set and != 0, counts only the assignments for the given user - * - * @internal revisions - */ - function get_not_run_tc_count_per_build($build_id, $all_types = false, $user_id = 0) - { - $count = 0; - - $types = $this->get_available_types(); - $tc_execution_type = $types['testcase_execution']['id']; - $type_sql = ($all_types) ? "" : " AND UA.type = {$tc_execution_type} "; - $user_sql = ($user_id && is_numeric($user_id)) ? "AND UA.user_id = {$user_id} " : ""; - - $sql = " SELECT UA.id as assignment_id,UA.user_id,TPTCV.testplan_id," . - " TPTCV.platform_id,BU.id AS BUILD_ID,E.id AS EXECID, E.status " . - " FROM {$this->tables['user_assignments']} UA " . - " JOIN {$this->tables['builds']} BU ON UA.build_id = BU.id " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.testplan_id = BU.testplan_id " . - " AND TPTCV.id = UA.feature_id " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = UA.build_id " . - " WHERE UA.build_id = {$build_id} AND E.status IS NULL {$type_sql} {$user_sql} "; - - - if (isset($build_id) && is_numeric($build_id)) { - $count = count($this->db->fetchRowsIntoMap($sql, 'assignment_id')); - } - - return $count; - } - - /** - * Copy the test case execution assignments for a test plan - * from one build to another. - * During copying of assignments, the assigner id can be updated if an ID is passed - * and the timestamp will be updated. - * - * @author Andreas Simon - * @param int $source_build_id ID of the build to copy the assignments from - * @param int $target_build_id ID of the target build to which the assignments will be copied - * @param int $assigner_id will be set as assigner ID of the new assignments if != 0, - * otherwise old assigner ID will be copied - * @param array $opt - * key => keep_old_assignments: - * true: existing assignments in target build will be kept, - * otherwise (default) every existing tester assignment will be deleted. - * - * key => copy_all_types - * true: all assignments of any type will be copied. - * false: only tester assignments will be copied (default). - * key => feature_set: array of id - */ - function copy_assignments($source_build_id, $target_build_id, - $assigner_id = 0, $opt = null) - { - - $my = array('opt'); - $my['opt']['keep_old_assignments'] = false; - $my['opt']['copy_all_types'] = false; - $my['opt']['feature_set'] = null; - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $ua = $this->tables['user_assignments']; - $creation_ts = $this->db->db_now(); - $types = $this->get_available_types(); - $tc_execution_type = $types['testcase_execution']['id']; - $delete_all_types = $my['opt']['copy_all_types']; - - $type_sql = ($my['opt']['copy_all_types']) ? "" : " AND type = {$tc_execution_type} "; - $user_sql = (is_numeric($assigner_id) && $assigner_id != 0) ? $assigner_id : "assigner_id"; - - if ($my['opt']['keep_old_assignments'] == false) - { - // delete the old tester assignments in target builds if there are any - $this->delete_by_build_id($target_build_id, $delete_all_types); - } - - $sql = " INSERT INTO {$ua} " . - " (type, feature_id, user_id, deadline_ts, " . - " assigner_id, creation_ts, status, build_id) " . - - " SELECT type, feature_id, user_id, deadline_ts, " . - " {$user_sql}, {$creation_ts}, status, {$target_build_id} " . - " FROM {$ua} " . - " WHERE build_id = " . intval($source_build_id) . $type_sql; - - if(!is_null($my['opt']['feature_set'])) - { - $sql .= " AND feature_id IN (" . implode(',',$my['opt']['feature_set']). ")"; - } - - $this->db->exec_query($sql); - } - - - /** - * get hash with build id and amount of test cases assigned to testers - * - * @author Francisco Mancardi - * @param mixed $buildID can be single value or array of build ID. - */ - function getExecAssignmentsCountByBuild($buildID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $rs = null; - $types = $this->get_available_types(); - $execAssign = $types['testcase_execution']['id']; - - $sql = "/* $debugMsg */ ". - " SELECT COUNT(id) AS qty, build_id " . - " FROM {$this->tables['user_assignments']} " . - " WHERE build_id IN ( " . implode(",",(array)$buildID) . " ) " . - " AND type = {$execAssign} " . - " GROUP BY build_id "; - $rs = $this->db->fetchRowsIntoMap($sql,'build_id'); - - return $rs; - } - - - /** - * get hash with build id and amount of test cases assigned to testers, - * but NOT EXECUTED. - * - * - * @author Francisco Mancardi - * @param mixed $buildID can be single value or array of build ID. - */ - function getNotRunAssignmentsCountByBuild($buildID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $rs = null; - $types = $this->get_available_types(); - $execAssign = $types['testcase_execution']['id']; - - $sql = "/* $debugMsg */ ". - " SELECT count(0) as qty, UA.build_id ". - " FROM {$this->tables['user_assignments']} UA " . - " JOIN {$this->tables['builds']} BU ON UA.build_id = BU.id " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.testplan_id = BU.testplan_id " . - " AND TPTCV.id = UA.feature_id " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = UA.build_id " . - " WHERE UA.build_id IN ( " . implode(",",(array)$buildID) . " ) " . - " AND E.status IS NULL " . - " AND type = {$execAssign} " . - " GROUP BY UA.build_id "; - - $rs = $this->db->fetchRowsIntoMap($sql,'build_id'); - - return $rs; - } - - - /** - * - */ - function getUsersByFeatureBuild($featureSet,$buildID,$assignmentType) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $rs = null; - - if(is_null($assignmentType) || !is_numeric($assignmentType) ) - { - throw new Exception(__METHOD__ . ' assignmentType can not be NULL or not numeric '); - } - $sql = "/* $debugMsg */ ". - " SELECT UA.user_id,UA.feature_id ". - " FROM {$this->tables['user_assignments']} UA " . - " WHERE UA.build_id = " . intval($buildID) . - " AND UA.feature_id IN(" . implode(",",(array)$featureSet) . " )" . - " AND type = " . intval($assignmentType); - - $rs = $this->db->fetchMapRowsIntoMap($sql,'feature_id','user_id'); - - return $rs; - } - - - - /** - * Send link with filters to access (after login) - * to testCaseAssignedToMe feature - * - */ - function emailLinkToExecPlanning($context,$targetUsers=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($targetUsers)) - { - $sql = "/* $debugMsg */ " . - " SELECT id FROM {$this->tables['users']} "; - $targetUsers = $this->db->fetchColumnsIntoArray($sql,'id'); - } - $uSet = (array)$targetUsers; - - - // if user has at least 1 assignment in context - // send link - $atd = $this->get_available_types(); - - $tplan_id = intval($context['tplan_id']); - $build_id = intval($context['build_id']); - $sql = "/* $debugMsg */ ". - " SELECT UA.user_id, U.email ". - " FROM {$this->tables['user_assignments']} UA " . - " JOIN {$this->tables['builds']} B " . - " ON UA.build_id = B.id " . - " LEFT JOIN {$this->tables['users']} U " . - " ON U.id = UA.user_id " . - " WHERE B.testplan_id = " . $tplan_id . - " AND B.id = " . $build_id . - " AND type = " . intval($atd['testcase_execution']['id']); - - $rs = $this->db->fetchRowsIntoMap($sql,'user_id'); - - - $bye = true; - if( !is_null($rs) && count($rs) > 0) - { - $bye = false; - $sql = " SELECT NHTPRJ.name AS tproject, " . - " NHTPL.name AS tplan " . - " FROM {$this->tables['nodes_hierarchy']} NHTPRJ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTPL " . - " ON NHTPRJ.id = NHTPL.parent_id " . - " JOIN {$this->tables['node_types']} NT " . - " ON NHTPRJ.node_type_id = NT.id " . - " WHERE NT.description = 'testproject' " . - " AND NHTPL.id = " . $tplan_id; - $names = $this->db->get_recordset($sql); - $names = $names[0]; - $body_flines = lang_get('testproject') . ': ' . $names['tproject'] . '
    ' . - lang_get('testplan') . ': ' . $names['tplan'] .'

    '; - } - - if($bye) - { - return; // >>>----> Bye,Bye!!! - } - - $email = array(); - $email['from_address'] = config_get('from_email'); - - $isoTS = date(DATE_RFC1123); - $genby = lang_get('generated_by_TestLink_on') . ' ' . $isoTS; - $ll = lang_get('mail_subject_link_to_assigned'); - $email['subject'] = sprintf($ll,$names['tplan'],$isoTS); - - $ln = $_SESSION['basehref'] . 'ltx.php?item=xta2m&tplan_id=' . - $tplan_id . '&user_id='; - - $hint = lang_get('hint_you_need_to_be_logged'); - require_once('email_api.php'); - foreach($uSet as $user_id) - { - if(isset($rs[$user_id])) - { - $email['to_address'] = trim($rs[$user_id]['email']); - if($email['to_address'] != '') - { - $email['body'] = $body_flines; - $email['body'] .= $hint . '

    ' . $ln . $user_id; - $email['body'] .= '

    ' . $genby; - - $email['cc'] = ''; - $email['attachment'] = null; - $email['exit_on_error'] = true; - $email['htmlFormat'] = true; - - $eop = email_send($email['from_address'],$email['to_address'], - $email['subject'], $email['body'], - $email['cc'],$email['attachment'], - $email['exit_on_error'], $email['htmlFormat']); - } - } - } - } - -} // class end +tables['assignment_types']}"; + $hash_types = $this->db->fetchRowsIntoMap($sql, $key_field); + } + return $hash_types; + } + + /** + * + * @param string $key_field + * contains the name column that has to be used as the key of the returned hash. + * @return array + */ + public function get_available_status($key_field = 'description') + { + static $hash_types; + if (! $hash_types) { + $sql = " SELECT * FROM {$this->tables['assignment_status']} "; + $hash_types = $this->db->fetchRowsIntoMap($sql, $key_field); + } + + return $hash_types; + } + + /** + * + * @param + * int or array $feature_id + */ + public function delete_by_feature_id($feature_id) + { + if (is_array($feature_id)) { + $feature_id_list = implode(",", $feature_id); + $where_clause = " WHERE feature_id IN ($feature_id_list) "; + } else { + $where_clause = " WHERE feature_id={$feature_id}"; + } + $sql = " DELETE FROM {$this->tables['user_assignments']} {$where_clause}"; + $this->db->exec_query($sql); + } + + /** + * Delete the user assignments for a given build. + * + * @author Andreas Simon + * @param int $build_id + * The ID of the build for which the user assignments shall be deleted. + * @param boolean $delete_all_types + * If true, all assignments regardless of type will be deleted, + * else (default) only tester assignments. + */ + public function delete_by_build_id($build_id, $delete_all_types = false) + { + $type_sql = ""; + + if (! $delete_all_types) { + $types = $this->get_available_types(); + $tc_execution_type = $types['testcase_execution']['id']; + $type_sql = " AND type = {$tc_execution_type} "; + } + + $sql = " DELETE FROM {$this->tables['user_assignments']} " . + " WHERE build_id = " . intval($build_id) . " {$type_sql} "; + + $this->db->exec_query($sql); + } + + /** + * delete assignments by feature id and build_id + * + * @param array $feature_map + */ + public function delete_by_feature_id_and_build_id($feature_map) + { + $feature_id_list = implode(",", array_keys($feature_map)); + $where_clause = " WHERE feature_id IN ($feature_id_list) "; + + $sql = " DELETE FROM {$this->tables['user_assignments']} {$where_clause} "; + + // build_id is the same for all entries because of assignment form + // -> skip foreach after first iteration + $build_id = 0; + foreach ($feature_map as $feature) { + $build_id = $feature['build_id']; + break; + } + + $sql .= " AND build_id = {$build_id} "; + $this->db->exec_query($sql); + } + + /** + * + * @param array $items + * array of signature + * signature = array('type' => ,'feature_id' =>,'user_id' =>, 'build_id' => ) + */ + public function deleteBySignature($items) + { + foreach ($items as $signature) { + $sql = " DELETE FROM {$this->tables['user_assignments']} WHERE 1=1 "; + foreach ($signature as $column => $val) { + $sql .= " AND $column = " . intval($val); + } + $this->db->exec_query($sql); + } + } + + /** + * + * @param array $feature_map + * $feature_map['feature_id']['user_id'] + * $feature_map['feature_id']['type'] + * $feature_map['feature_id']['status'] + * $feature_map['feature_id']['assigner_id'] + * $feature_map['feature_id']['build_id'] + * + * Need to manage situation where user_id = 0 is passed + * I will IGNORE IT + * @return array + * + * @internal revisions + */ + public function assign($feature_map) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $ret = array(); + $types = $this->get_available_types(); + $safe = null; + + foreach ($feature_map as $feature_id => $elem) { + $safe['feature_id'] = intval($feature_id); + $safe['build_id'] = intval($elem['build_id']); + $safe['type'] = intval($elem['type']); + + $uSet = (array) $elem['user_id']; + + foreach ($uSet as $user_id) { + $safe['user_id'] = intval($user_id); + + // Check if exists before adding + $check = "/* $debugMsg */ "; + $check .= " SELECT id FROM {$this->tables['user_assignments']} " . + " WHERE feature_id = " . $safe['feature_id'] . + " AND build_id = " . $safe['build_id'] . " AND type = " . + $safe['type'] . " AND user_id = " . $safe['user_id']; + + $rs = $this->db->get_recordset($check); + if ((is_null($rs) || count($rs) == 0) && ($safe['user_id'] > 0)) { + $sql = "INSERT INTO {$this->tables['user_assignments']} " . + "(feature_id,user_id,assigner_id,type,status,creation_ts"; + + $values = "VALUES({$safe['feature_id']},{$safe['user_id']}," . + "{$elem['assigner_id']}," . + "{$safe['type']},{$elem['status']},"; + $values .= (isset($elem['creation_ts']) ? $elem['creation_ts'] : $this->db->db_now()); + + if (isset($elem['deadline_ts'])) { + $sql .= ",deadline_ts"; + $values .= "," . $elem['deadline_ts']; + } + + if (isset($elem['build_id'])) { + $sql .= ",build_id"; + $values .= "," . $safe['build_id']; + } else { + if ($safe['type'] == $types['testcase_execution']['id']) { + throw new Exception( + "Error Processing Request - BUILD ID is Mandatory"); + } + } + + $sql .= ") " . $values . ")"; + tLog(__METHOD__ . '::' . $sql, "DEBUG"); + $this->db->exec_query($sql); + $ret[] = $sql; + } + } + } + return $ret; + } + + /** + * + * @param array $feature_map + * $feature_map: key => feature_id + * value => hash with optional keys + * that have the same name of user_assignment fields + * + * @internal revisions + */ + public function update($feature_map) + { + foreach ($feature_map as $feature_id => $elem) { + $sepa = ""; + $sql = "UPDATE {$this->tables['user_assignments']} SET "; + $simple_fields = array( + 'user_id', + 'assigner_id', + 'type', + 'status' + ); + $date_fields = array( + 'deadline_ts', + 'creation_ts' + ); + + foreach ($simple_fields as $idx => $field) { + if (isset($elem[$field])) { + $sql .= $sepa . "$field={$elem[$field]} "; + $sepa = ","; + } + } + + foreach ($date_fields as $field) { + if (isset($elem[$field])) { + $sql .= $sepa . "$field=" . $elem[$field] . " "; + $sepa = ","; + } + } + + $sql .= "WHERE feature_id={$feature_id} AND build_id={$elem['build_id']}"; + + $this->db->exec_query($sql); + } + } + + /** + * Get the number of assigned users for a given build ID. + * + * @param int $build_id + * ID of the build to check + * @param int $count_all_types + * if true, all assignments will be counted, otherwise + * only tester assignments + * @param int $user_id + * if given, user ID for which the assignments per build shall be counted + * @return int $count Number of assignments + */ + public function get_count_of_assignments_for_build_id($build_id, + $count_all_types = false, $user_id = 0) + { + $types = $this->get_available_types(); + $tc_execution_type = $types['testcase_execution']['id']; + $type_sql = ($count_all_types) ? "" : " AND type = {$tc_execution_type} "; + + $user_sql = ($user_id && is_numeric($user_id)) ? "AND user_id = {$user_id} " : ""; + + $sql = " SELECT COUNT(id) AS count FROM {$this->tables['user_assignments']} " . + " WHERE build_id = {$build_id} {$user_sql} {$type_sql} "; + + return $this->db->fetchOneValue($sql); + } + + /** + * Get count of assigned, but not run testcases per build (and optionally user). + * + * @param int $build_id + * @param bool $all_types + * @param int $user_id + * if set and != 0, counts only the assignments for the given user + * + * @internal revisions + */ + public function get_not_run_tc_count_per_build($build_id, $all_types = false, + $user_id = 0) + { + $count = 0; + + $types = $this->get_available_types(); + $tc_execution_type = $types['testcase_execution']['id']; + $type_sql = ($all_types) ? "" : " AND UA.type = {$tc_execution_type} "; + $user_sql = ($user_id && is_numeric($user_id)) ? "AND UA.user_id = {$user_id} " : ""; + + $sql = " SELECT UA.id as assignment_id,UA.user_id,TPTCV.testplan_id," . + " TPTCV.platform_id,BU.id AS BUILD_ID,E.id AS EXECID, E.status " . + " FROM {$this->tables['user_assignments']} UA " . + " JOIN {$this->tables['builds']} BU ON UA.build_id = BU.id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.testplan_id = BU.testplan_id " . + " AND TPTCV.id = UA.feature_id " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.build_id = UA.build_id " . + " WHERE UA.build_id = {$build_id} AND E.status IS NULL {$type_sql} {$user_sql} "; + + if (isset($build_id) && is_numeric($build_id)) { + $count = count($this->db->fetchRowsIntoMap($sql, 'assignment_id')); + } + + return $count; + } + + /** + * Copy the test case execution assignments for a test plan + * from one build to another. + * During copying of assignments, the assigner id can be updated if an ID is passed + * and the timestamp will be updated. + * + * @author Andreas Simon + * @param int $source_build_id + * ID of the build to copy the assignments from + * @param int $target_build_id + * ID of the target build to which the assignments will be copied + * @param int $assigner_id + * will be set as assigner ID of the new assignments if != 0, + * otherwise old assigner ID will be copied + * @param array $opt + * key => keep_old_assignments: + * true: existing assignments in target build will be kept, + * otherwise (default) every existing tester assignment will be deleted. + * + * key => copy_all_types + * true: all assignments of any type will be copied. + * false: only tester assignments will be copied (default). + * key => feature_set: array of id + */ + public function copy_assignments($source_build_id, $target_build_id, + $assigner_id = 0, $opt = null) + { + $my = array( + 'opt' + ); + $my['opt']['keep_old_assignments'] = false; + $my['opt']['copy_all_types'] = false; + $my['opt']['feature_set'] = null; + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $ua = $this->tables['user_assignments']; + $creation_ts = $this->db->db_now(); + $types = $this->get_available_types(); + $tc_execution_type = $types['testcase_execution']['id']; + $delete_all_types = $my['opt']['copy_all_types']; + + $type_sql = ($my['opt']['copy_all_types']) ? "" : " AND type = {$tc_execution_type} "; + $user_sql = (is_numeric($assigner_id) && $assigner_id != 0) ? $assigner_id : "assigner_id"; + + if (! $my['opt']['keep_old_assignments']) { + // delete the old tester assignments in target builds if there are any + $this->delete_by_build_id($target_build_id, $delete_all_types); + } + + $sql = " INSERT INTO {$ua} " . + " (type, feature_id, user_id, deadline_ts, " . + " assigner_id, creation_ts, status, build_id) " . + + " SELECT type, feature_id, user_id, deadline_ts, " . + " {$user_sql}, {$creation_ts}, status, {$target_build_id} " . + " FROM {$ua} " . " WHERE build_id = " . intval($source_build_id) . + $type_sql; + + if (! is_null($my['opt']['feature_set'])) { + $sql .= " AND feature_id IN (" . + implode(',', $my['opt']['feature_set']) . ")"; + } + + $this->db->exec_query($sql); + } + + /** + * get hash with build id and amount of test cases assigned to testers + * + * @author Francisco Mancardi + * @param mixed $buildID + * can be single value or array of build ID. + * @return array + */ + private function getExecAssignmentsCountByBuild($buildID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $types = $this->get_available_types(); + $execAssign = $types['testcase_execution']['id']; + + $sql = "/* $debugMsg */ " . " SELECT COUNT(id) AS qty, build_id " . + " FROM {$this->tables['user_assignments']} " . + " WHERE build_id IN ( " . implode(",", (array) $buildID) . " ) " . + " AND type = {$execAssign} " . " GROUP BY build_id "; + return $this->db->fetchRowsIntoMap($sql, 'build_id'); + } + + /** + * get hash with build id and amount of test cases assigned to testers, + * but NOT EXECUTED. + * + * @author Francisco Mancardi + * @param mixed $buildID + * can be single value or array of build ID. + * @return array + */ + private function getNotRunAssignmentsCountByBuild($buildID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $types = $this->get_available_types(); + $execAssign = $types['testcase_execution']['id']; + + $sql = "/* $debugMsg */ " . " SELECT count(0) as qty, UA.build_id " . + " FROM {$this->tables['user_assignments']} UA " . + " JOIN {$this->tables['builds']} BU ON UA.build_id = BU.id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.testplan_id = BU.testplan_id " . + " AND TPTCV.id = UA.feature_id " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.build_id = UA.build_id " . " WHERE UA.build_id IN ( " . + implode(",", (array) $buildID) . " ) " . " AND E.status IS NULL " . + " AND type = {$execAssign} " . " GROUP BY UA.build_id "; + + return $this->db->fetchRowsIntoMap($sql, 'build_id'); + } + + /** + * + * @param array $featureSet + * @param int $buildID + * @param int $assignmentType + * @return array + */ + public function getUsersByFeatureBuild($featureSet, $buildID, + $assignmentType) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + if (is_null($assignmentType) || ! is_numeric($assignmentType)) { + throw new Exception( + __METHOD__ . ' assignmentType can not be NULL or not numeric '); + } + $sql = "/* $debugMsg */ " . " SELECT UA.user_id,UA.feature_id " . + " FROM {$this->tables['user_assignments']} UA " . + " WHERE UA.build_id = " . intval($buildID) . " AND UA.feature_id IN(" . + implode(",", (array) $featureSet) . " )" . " AND type = " . + intval($assignmentType); + + return $this->db->fetchMapRowsIntoMap($sql, 'feature_id', 'user_id'); + } + + /** + * Send link with filters to access (after login) + * to testCaseAssignedToMe feature + * + * @param array $context + * @param array $targetUsers + */ + public function emailLinkToExecPlanning($context, $targetUsers = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($targetUsers)) { + $sql = "/* $debugMsg */ " . + " SELECT id FROM {$this->tables['users']} "; + $targetUsers = $this->db->fetchColumnsIntoArray($sql, 'id'); + } + $uSet = (array) $targetUsers; + + // if user has at least 1 assignment in context + // send link + $atd = $this->get_available_types(); + + $tplan_id = intval($context['tplan_id']); + $build_id = intval($context['build_id']); + $sql = "/* $debugMsg */ " . " SELECT UA.user_id, U.email " . + " FROM {$this->tables['user_assignments']} UA " . + " JOIN {$this->tables['builds']} B " . " ON UA.build_id = B.id " . + " LEFT JOIN {$this->tables['users']} U " . " ON U.id = UA.user_id " . + " WHERE B.testplan_id = " . $tplan_id . " AND B.id = " . $build_id . + " AND type = " . intval($atd['testcase_execution']['id']); + + $rs = $this->db->fetchRowsIntoMap($sql, 'user_id'); + + $bye = true; + if (! is_null($rs) && ! empty($rs)) { + $bye = false; + $sql = " SELECT NHTPRJ.name AS tproject, " . " NHTPL.name AS tplan " . + " FROM {$this->tables['nodes_hierarchy']} NHTPRJ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTPL " . + " ON NHTPRJ.id = NHTPL.parent_id " . + " JOIN {$this->tables['node_types']} NT " . + " ON NHTPRJ.node_type_id = NT.id " . + " WHERE NT.description = 'testproject' " . " AND NHTPL.id = " . + $tplan_id; + $names = $this->db->get_recordset($sql); + $names = $names[0]; + $body_flines = lang_get('testproject') . ': ' . $names['tproject'] . + '
    ' . lang_get('testplan') . ': ' . $names['tplan'] . + '

    '; + } + + if ($bye) { + return; + } + + $email = array(); + $email['from_address'] = config_get('from_email'); + + $isoTS = date(DATE_RFC1123); + $genby = lang_get('generated_by_TestLink_on') . ' ' . $isoTS; + $ll = lang_get('mail_subject_link_to_assigned'); + $email['subject'] = sprintf($ll, $names['tplan'], $isoTS); + + $ln = $_SESSION['basehref'] . 'ltx.php?item=xta2m&tplan_id=' . $tplan_id . + '&user_id='; + + $hint = lang_get('hint_you_need_to_be_logged'); + require_once 'email_api.php'; + foreach ($uSet as $user_id) { + if (isset($rs[$user_id])) { + $email['to_address'] = trim($rs[$user_id]['email']); + if ($email['to_address'] != '') { + $email['body'] = $body_flines; + $email['body'] .= $hint . '

    ' . $ln . $user_id; + $email['body'] .= '

    ' . $genby; + + $email['cc'] = ''; + $email['attachment'] = null; + $email['exit_on_error'] = true; + $email['htmlFormat'] = true; + + email_send($email['from_address'], $email['to_address'], + $email['subject'], $email['body'], $email['cc'], + $email['attachment'], $email['exit_on_error'], + $email['htmlFormat']); + } + } + } + } +} diff --git a/lib/functions/attachments.inc.php b/lib/functions/attachments.inc.php index 28cc8b2f22..9b6a3074c1 100644 --- a/lib/functions/attachments.inc.php +++ b/lib/functions/attachments.inc.php @@ -1,169 +1,178 @@ - 0 the attachments are appended to existing attachments within the session - * - * @return array infos about the attachment on success, NULL else -*/ -function getAttachmentInfos(&$attachmentRepository,$fkid,$fkTableName,$storeListInSession = true,$counter = 0) -{ - $attachmentInfos = $attachmentRepository->getAttachmentInfosFor($fkid,$fkTableName); - if ($storeListInSession) - { - storeAttachmentsInSession($attachmentInfos,$counter); - } - return $attachmentInfos; -} - -/** - * Get infos about the attachments of a given object - * - * @param tlObjectWithAttachments $object The object whose attachment should be fetched - * @param int $fkid the id of the object (attachments.fk_id); - * @param bool $storeListInSession if true, the attachment list will be stored within the session - * @param int $counter if $counter > 0 the attachments are appended to existing attachments within the session - * - * @return array returns infos about the attachment on success, NULL else - */ -function getAttachmentInfosFrom(&$object,$fkid,$storeListInSession = true,$counter = 0) -{ - $attachmentInfos = $object->getAttachmentInfos($fkid); - if ($storeListInSession) - { - storeAttachmentsInSession($attachmentInfos,$counter); - } - return $attachmentInfos; -} - -/** - * Stores the attachment infos into the session for referencing it later - * - * @param array $attachmentInfos infos about attachment - * @param $counter counter for the attachments in the session - */ -function storeAttachmentsInSession($attachmentInfos,$counter = 0) -{ - if (!$attachmentInfos) - { - $attachmentInfos = array(); - } - - if (!isset($_SESSION['s_lastAttachmentInfos']) || !$_SESSION['s_lastAttachmentInfos']) - { - $_SESSION['s_lastAttachmentInfos'] = array(); - } - - if ($counter == 0) - { - $_SESSION['s_lastAttachmentInfos'] = $attachmentInfos; - } - else - { - $_SESSION['s_lastAttachmentInfos'] = array_merge($_SESSION['s_lastAttachmentInfos'],$attachmentInfos); - } - -} - -/** - * Checks the id of an attachment and the corresponding attachment info for validity - * - * @param resource $db [ref] the database connection - * @param integer $id the database identifier of the attachment - * @param $attachmentInfo - * @return boolean return true if the id is valid, false else - */ -function checkAttachmentID(&$db,$id,$attachmentInfo) -{ - $isValid = false; - if ($attachmentInfo) - { - $sLastAttachmentInfos = isset($_SESSION['s_lastAttachmentInfos']) ? $_SESSION['s_lastAttachmentInfos'] : null; - for($i = 0;$i < sizeof($sLastAttachmentInfos);$i++) - { - $info = $sLastAttachmentInfos[$i]; - if ($info['id'] == $id) - { - $isValid = true; - break; - } - } - } - return $isValid; -} - - -/** - * - */ -function fileUploadManagement(&$dbHandler,$id,$title,$table) -{ - $uploadOp = new stdClass(); - $uploadOp->statusOK = false; - $uploadOp->statusCode = 0; - $uploadOp->msg = null; - - $fInfo = isset($_FILES['uploadedFile']) ? $_FILES['uploadedFile'] : null; - if ($fInfo && $id) { - $fSize = isset($fInfo['size']) ? $fInfo['size'] : 0; - $fTmpName = isset($fInfo['tmp_name']) ? $fInfo['tmp_name'] : ''; - - if ($fSize && $fTmpName != "") { - $repo = tlAttachmentRepository::create($dbHandler); - $uploadOp = $repo->insertAttachment($id,$table,$title,$fInfo); - $uploadOp->uploaded = $uploadOp->statusOK; - - if ($uploadOp->statusOK) { - logAuditEvent(TLS("audit_attachment_created",$title,$fInfo['name']),"CREATE",$id,"attachments"); - } else { - $uploadOp->msg = getFileUploadErrorMessage($fInfo,$uploadOp); - } - } else { - $uploadOp->msg = getFileUploadErrorMessage($fInfo); - } - } - return $uploadOp; -} - -/** - * - */ -function deleteAttachment(&$dbHandler,$fileID,$checkOnSession=true) { - $repo = tlAttachmentRepository::create($dbHandler); - $info = $repo->getAttachmentInfo($fileID); - if( $info ) { - $doIt = true; - if( $checkOnSession ) { - $doIt = checkAttachmentID($dbHandler,$fileID,$info); - } - - if( $doIt ) { - if($repo->deleteAttachment($fileID,$info)) { - logAuditEvent(TLS("audit_attachment_deleted",$info['title']),"DELETE",$fileID,"attachments"); - } - } - } - return $info; + 0 the attachments are appended to existing attachments within the session + * + * @return array infos about the attachment on success, NULL else + */ +function getAttachmentInfos(&$attachmentRepository, $fkid, $fkTableName, + $storeListInSession = true, $counter = 0) +{ + $attachmentInfos = $attachmentRepository->getAttachmentInfosFor($fkid, + $fkTableName); + if ($storeListInSession) { + storeAttachmentsInSession($attachmentInfos, $counter); + } + return $attachmentInfos; +} + +/** + * Get infos about the attachments of a given object + * + * @param tlObjectWithAttachments $object + * The object whose attachment should be fetched + * @param int $fkid + * the id of the object (attachments.fk_id); + * @param bool $storeListInSession + * if true, the attachment list will be stored within the session + * @param int $counter + * if $counter > 0 the attachments are appended to existing attachments within the session + * + * @return array returns infos about the attachment on success, NULL else + */ +function getAttachmentInfosFrom(&$object, $fkid, $storeListInSession = true, + $counter = 0) +{ + $attachmentInfos = $object->getAttachmentInfos($fkid); + if ($storeListInSession) { + storeAttachmentsInSession($attachmentInfos, $counter); + } + return $attachmentInfos; +} + +/** + * Stores the attachment infos into the session for referencing it later + * + * @param array $attachmentInfos + * infos about attachment + * @param int $counter + * counter for the attachments in the session + */ +function storeAttachmentsInSession($attachmentInfos, $counter = 0) +{ + if (! $attachmentInfos) { + $attachmentInfos = array(); + } + + if (! isset($_SESSION['s_lastAttachmentInfos']) || + ! $_SESSION['s_lastAttachmentInfos']) { + $_SESSION['s_lastAttachmentInfos'] = array(); + } + + if ($counter == 0) { + $_SESSION['s_lastAttachmentInfos'] = $attachmentInfos; + } else { + $_SESSION['s_lastAttachmentInfos'] = array_merge( + $_SESSION['s_lastAttachmentInfos'], $attachmentInfos); + } +} + +/** + * Checks the id of an attachment and the corresponding attachment info for validity + * + * @param resource $db + * [ref] the database connection + * @param integer $id + * the database identifier of the attachment + * @param + * $attachmentInfo + * @return boolean return true if the id is valid, false else + */ +function checkAttachmentID(&$db, $id, $attachmentInfo) +{ + $isValid = false; + if ($attachmentInfo) { + $sLastAttachmentInfos = isset($_SESSION['s_lastAttachmentInfos']) ? $_SESSION['s_lastAttachmentInfos'] : null; + for ($i = 0; $i < count($sLastAttachmentInfos); $i ++) { + $info = $sLastAttachmentInfos[$i]; + if ($info['id'] == $id) { + $isValid = true; + break; + } + } + } + return $isValid; +} + +/** + */ +function fileUploadManagement(&$dbHandler, $id, $title, $table) +{ + $uploadOp = new stdClass(); + $uploadOp->statusOK = false; + $uploadOp->statusCode = 0; + $uploadOp->msg = null; + + $fInfo = isset($_FILES['uploadedFile']) ? $_FILES['uploadedFile'] : null; + if ($fInfo && $id) { + $fSize = isset($fInfo['size']) ? $fInfo['size'] : 0; + $fTmpName = isset($fInfo['tmp_name']) ? $fInfo['tmp_name'] : ''; + + if ($fSize && $fTmpName != "") { + $repo = tlAttachmentRepository::create($dbHandler); + $uploadOp = $repo->insertAttachment($id, $table, $title, $fInfo); + $uploadOp->uploaded = $uploadOp->statusOK; + + if ($uploadOp->statusOK) { + logAuditEvent( + TLS("audit_attachment_created", $title, $fInfo['name']), + "CREATE", $id, "attachments"); + } else { + $uploadOp->msg = getFileUploadErrorMessage($fInfo, $uploadOp); + } + } else { + $uploadOp->msg = getFileUploadErrorMessage($fInfo); + } + } + return $uploadOp; +} + +/** + */ +function deleteAttachment(&$dbHandler, $fileID, $checkOnSession = true) +{ + $repo = tlAttachmentRepository::create($dbHandler); + $info = $repo->getAttachmentInfo($fileID); + if ($info) { + $doIt = true; + if ($checkOnSession) { + $doIt = checkAttachmentID($dbHandler, $fileID, $info); + } + + if ($doIt && $repo->deleteAttachment($fileID, $info)) { + logAuditEvent(TLS("audit_attachment_deleted", $info['title']), + "DELETE", $fileID, "attachments"); + } + } + return $info; } diff --git a/lib/functions/bareBonesRestAPI.class.php b/lib/functions/bareBonesRestAPI.class.php new file mode 100644 index 0000000000..7d229864f0 --- /dev/null +++ b/lib/functions/bareBonesRestAPI.class.php @@ -0,0 +1,262 @@ + + * + * @author Francisco Mancardi + * + */ + +/** + */ +class bareBonesRestAPI +{ + + /** + * + * @var string Some systems i.e. trello need both + */ + public $apikey = ''; + + public $apitoken = ''; + + /** + * Curl interface with specific settings + * + * @var string + */ + public $curl = ''; + + /** + * Curl Header + * changes according the system + * + * @var [] + */ + public $curlHeader = []; + + /** + * properties + * host + * port + * login + * password + */ + public $proxy = null; + + public $cfg; + + /** + * Constructor + * + * + * @return void + */ + public function __construct() + {} + + /** + */ + public function initCurl($cfg = null) + { + $agent = "TestLink " . TL_VERSION_NUMBER; + try { + $this->curl = curl_init(); + } catch (Exception $e) { + var_dump($e); + } + + // set the agent, forwarding, and turn off ssl checking + // Timeout in Seconds + $curlCfg = [ + CURLOPT_USERAGENT => $agent, + CURLOPT_VERBOSE => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_AUTOREFERER => true, + CURLOPT_TIMEOUT => 60, + CURLOPT_SSL_VERIFYPEER => false + ]; + + if (! is_null($this->proxy)) { + $doProxyAuth = false; + $curlCfg[CURLOPT_PROXYTYPE] = 'HTTP'; + + foreach ($this->proxy as $prop => $value) { + switch ($prop) { + case 'host': + $curlCfg[CURLOPT_PROXY] = $value; + break; + + case 'port': + $curlCfg[CURLOPT_PROXYPORT] = $value; + break; + + case 'login': + case 'password': + $doProxyAuth = true; + break; + } + } + + if ($doProxyAuth && ! is_null($this->proxy->login) && + ! is_null($this->proxy->password)) { + $curlCfg[CURLOPT_PROXYUSERPWD] = $this->proxy->login . ':' . + $this->proxy->password; + } + } + + curl_setopt_array($this->curl, $curlCfg); + } + + /** + * + * @internal notice + * copied and adpated from work on YouTrack API interface by Jens Jahnke + */ + protected function _get($cmd) + { + // GET must returns a JSON object ALWAYS + return $this->_request_json('GET', $cmd); + } + + /** + * Use it when the API called will return + * - response + * - JSON content + */ + protected function _postWithContent($cmd, $body = null) + { + return $this->_request_json('POST', $cmd, $body); + } + + /** + * Use it when the API called will return + * - response + */ + protected function _post($cmd, $body = null) + { + return $this->_request('POST', $cmd, $body); + } + + /** + * + * @param unknown $method + * @param unknown $cmd + * @param unknown $body + * @param number $ignore_status + * @param unknown $reporter + * @return mixed + * @internal notice + * copied and adpated from work on YouTrack API interface by Jens Jahnke + */ + protected function _request_json($method, $cmd, $body = null, + $ignore_status = 0, $reporter = null) + { + $r = $this->_request($method, $cmd, $body, $ignore_status, $reporter); + $response = $r['response']; + + $content = json_decode($r['content']); + if (json_last_error() == JSON_ERROR_NONE) { + return $content; + } + + // Oh no!!! + $msg = 'Bad Response!!'; + if (null != $response && isset($response['http_code'])) { + $msg = "http_code:" . $response['http_code']; + } + $msg = "Error Parsing JSON In TESTLINK -> " . $msg . + " -> Give a look to TestLink Event Viewer"; + + throw new Exception($msg, 1); + } + + /** + * + * @param unknown $method + * @param unknown $cmd + * @param unknown $body + * @param number $ignoreStatusCode + * @param unknown $reporter + * @return array + * + * @internal notice + * copied and adpated from work on YouTrack API interface by Jens Jahnke + */ + protected function _request($method, $cmd, $body = null, + $ignoreStatusCode = 0, $reporter = null) + { + + // this is the minimal test + if (empty($this->apikey)) { + throw new exception(__METHOD__ . " Can not work without apikey"); + } + + // this can happens because if I save object on _SESSION PHP is not able to + // save resources. + if (! is_resource($this->curl)) { + $this->initCurl(); + } + + $additional = ''; + if (property_exists($this, 'api')) { + $additional = trim($this->api); + } + $url = $this->url . $additional . $cmd; + + curl_setopt($this->curl, CURLOPT_URL, $url); + curl_setopt($this->curl, CURLOPT_DNS_USE_GLOBAL_CACHE, false); + curl_setopt($this->curl, CURLOPT_DNS_CACHE_TIMEOUT, 2); + curl_setopt($this->curl, CURLOPT_HEADER, 0); + + if (! empty($this->curlHeader)) { + curl_setopt($this->curl, CURLOPT_HTTPHEADER, $this->curlHeader); + } + + switch ($method) { + case 'GET': + curl_setopt($this->curl, CURLOPT_HTTPGET, true); + break; + + case 'POST': + case 'PATCH': + curl_setopt($this->curl, CURLOPT_POST, true); + if (! empty($body)) { + curl_setopt($this->curl, CURLOPT_POSTFIELDS, + json_encode($body)); + } + break; + + default: + throw new exception("Unknown method $method!"); + break; + } + + $content = curl_exec($this->curl); + $response = curl_getinfo($this->curl); + $curlError = curl_error($this->curl); + $httpCode = (int) $response['http_code']; + if ($httpCode != 200 && $httpCode != 201 && + $httpCode != $ignoreStatusCode) { + throw new exception( + __METHOD__ . "url:$this->url - response:" . + json_encode($response) . ' - content: ' . json_encode($content)); + } + + return [ + 'content' => $content, + 'response' => $response, + 'curlError' => $curlError + ]; + } + + /** + */ + public function __destruct() + {} +} diff --git a/lib/functions/cfield_mgr.class.php b/lib/functions/cfield_mgr.class.php index 581bd1800d..16eacd5eb9 100644 --- a/lib/functions/cfield_mgr.class.php +++ b/lib/functions/cfield_mgr.class.php @@ -1,3021 +1,2940 @@ - 0 ) -{ - foreach($cf_files as $inc) - { - require_once($inc); - } + 240 chars <= 255 chars table field size + */ + const TEXTAREA_DEFAULT_COLS = 70; + + const TEXTAREA_DEFAULT_ROWS = 4; + + const CF_ENABLED = 1; + + const ENABLED = 1; + + const DISABLED = 0; + + /** @var resource the database handler */ + private $db; + + /** @var object tree class */ + private $tree_manager; + + /** + * + * @var array $application_areas Holds string keys used on this object and pages that manages CF, + * identifying in what areas/features something will be done + * 'execution' => mainly on test execution pages, + * identifies TL features/pages to record test results + * 'design' => test suites, test cases creation + * identifies TL features/pages to create test specification + * 'testplan_design' => link test cases to test plan (assign testcase option) + * + * IMPORTANT: this values are used as access keys in several properties of this object. + * then if you add one here, remember to update other properties. + */ + private $application_areas = array( + 'execution', + 'design', + 'testplan_design' + ); + + /** + * + * @var array Define type of custom fields managed. + * Values will be displayed in "Custom Field Type" combo box when + * users create custom fields. No localization is applied + * + * + * Added specific type for test automation related custom fields. + * Start at code 500 + */ + public $custom_field_types = array( + 0 => 'string', + 1 => 'numeric', + 2 => 'float', + 4 => 'email', + 5 => 'checkbox', + 6 => 'list', + 7 => 'multiselection list', + 8 => 'date', + 9 => 'radio', + 10 => 'datetime', + 20 => 'text area', + 500 => 'script', + 501 => 'server' + ); + + /** + * + * @var array Configures for what type of CF "POSSIBLE_VALUES" field need to be manage at GUI level + * Keys of this map must be the values present in: + * $this->custom_field_types + */ + public $possible_values_cfg = array( + 'string' => 0, + 'numeric' => 0, + 'float' => 0, + 'email' => 0, + 'checkbox' => 1, + 'list' => 1, + 'multiselection list' => 1, + 'date' => 0, + 'radio' => 1, + 'datetime' => 0, + 'text area' => 0, + 'script' => 0, + 'server' => 0 + ); + + /** @var array only the types listed here can have custom fields */ + private $node_types = array( + 'build', + 'testsuite', + 'testplan', + 'testcase', + 'requirement_spec', + 'requirement' + ); + + /** + * + * @var array of maps $locations + * + * Location is place on page where to display custom field. + * This concept has been created to implement a user contribution, that allows for + * test cases, display custom fields in a different location (standard location is after + * all test case definition), to implemente Prerequisites using CF. + * + * First map key: node type: 'testcase','testsuite', etc. + * Each element will be a map with following structure: + * key:Holds string keys used on this object and pages that manages CF. + * current options: 1 -> standard location, i.e. work as done before this implementation. + * 2 -> before steps and results, => between summary and steps/results. + * + * value: used to get translated label to use on User Interface. + * + * IMPORTANT: if you add a new key, this values are used as access keys in several properties of this object. + * then if you add one here, remember to update other properties. + */ + private $locations = [ + 'testcase' => [ + 1 => 'standard_location', + 2 => 'before_steps_results', + 3 => 'before_summary', + 4 => 'before_preconditions', + 5 => 'after_title', + 6 => 'after_summary', + 7 => 'after_preconditions', + 8 => 'hide_because_is_used_as_variable' /* use when you will use the custom field as a variable [tlVar][/tlvar] */ + ] + ]; + + // changes in configuration + // + // Needed to manage user interface, when creating Custom Fields. + // When user choose a item type (test case, etc), a javascript logic + // uses this information to hide/show enable_on, and show_on combos. + // + // 0 => combo will not displayed + // + // May be need a review, because after the changes, seems a little bit silly. + private $enable_on_cfg = array( + 'execution' => array( + 'build' => 0, + 'testsuite' => 0, + 'testplan' => 0, + 'testcase' => 1, + 'requirement_spec' => 0, + 'requirement' => 0 + ), + 'design' => array( + 'build' => 0, + 'testsuite' => 0, + 'testplan' => 0, + 'testcase' => 1, + 'requirement_spec' => 0, + 'requirement' => 0 + ), + 'testplan_design' => array( + 'build' => 0, + 'testsuite' => 0, + 'testplan' => 0, + 'testcase' => 1, + 'requirement_spec' => 0, + 'requirement' => 0 + ) + ); + + // 0 => combo will not displayed + private $show_on_cfg = array( + 'execution' => array( + 'testsuite' => 1, + 'testplan' => 1, + 'testcase' => 1, + 'build' => 1, + 'requirement_spec' => 0, + 'requirement' => 0 + ), + 'design' => array( + 'testsuite' => 1, + 'testplan' => 1, + 'testcase' => 1, + 'build' => 0, + 'requirement_spec' => 0, + 'requirement' => 0 + ), + 'testplan_design' => array( + 'testsuite' => 1, + 'testplan' => 1, + 'testcase' => 1, + 'build' => 0, + 'requirement_spec' => 0, + 'requirement' => 0 + ) + ); + + // the name of html input will have the following format + // __ + // + public $name_prefix = 'custom_field_'; + + private $sizes = null; + + // must be equal to the lenght of: + // value column on cfield_*_values tables + // default_value column on custom_fields table + // 0 -> no limit + // Is used on text area types + private $max_length_value; + + // must be equal to the lenght of: + // possible_values column on custom_fields table + // 0 -> no limit + private $max_length_possible_values; + + private $decode; + + private $html_date_input_suffix = array( + 'input' => true, + 'hour' => true, + 'minute' => true, + 'second' => true + ); + + /** + * Class constructor + * + * @param + * database &$db reference to the database handler + */ + public function __construct(&$db) + { + parent::__construct(); + + $this->db = &$db; + $this->tree_manager = new tree($this->db); + + $cfConfig = config_get('custom_fields'); + $this->sizes = $cfConfig->sizes; + + if (property_exists($cfConfig, 'types') && ! is_null($cfConfig->types)) { + $this->custom_field_types += $cfConfig->types; + ksort($this->custom_field_types); + } + + if (property_exists($cfConfig, 'possible_values_cfg') && + ! is_null($cfConfig->possible_values_cfg)) { + $this->possible_values_cfg += $cfConfig->possible_values_cfg; + } + $this->object_table = $this->tables["custom_fields"]; + + $this->max_length_value = $cfConfig->max_length; + $this->max_length_possible_values = $this->max_length_value; + + $this->decode['nodes'] = $this->tree_manager->get_available_node_types(); + + $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: '; + } + + /** + * + * @return array + */ + private function getSizeLimit() + { + return $this->max_length_value; + } + + /** + * + * @return array + */ + public function get_application_areas() + { + return $this->application_areas; + } + + /** + * + * @return array with available locatipons + */ + public function getLocations() + { + return $this->locations; + } + + /** + * + * @return array with custom field available types + * key: numeric id + * value: short description + */ + public function get_available_types() + { + return $this->custom_field_types; + } + + /** + * + * @return string + */ + public function get_name_prefix() + { + return $this->name_prefix; + } + + /** + * + * @return array with node types id, that can have custom fields. + * key: short description (node_types.description) + * value: node_type_id (node_types.id) + */ + public function get_allowed_nodes() + { + $allowed_nodes = array(); + foreach ($this->node_types as $verbose_type) { + $allowed_nodes[$verbose_type] = $this->decode['nodes'][$verbose_type]; + } + return $allowed_nodes; + } + + /** + * + * @return array with node types id, that can have custom fields with enabled_on_$ui_mode. + * key : node_type_id (node_types.id) + * value: 1 -> enable on exec can be configured by user + */ + public function get_enable_on_cfg($ui_mode) + { + return $this->_get_ui_mgtm_cfg_for_node_type( + $this->enable_on_cfg[$ui_mode]); + } + + /** + * + * @param string $ui_mode + * @return array + */ + public function get_show_on_cfg($ui_mode) + { + return $this->_get_ui_mgtm_cfg_for_node_type( + $this->show_on_cfg[$ui_mode]); + } + + /* + * function: _get_ui_mgtm_cfg_for_node_type + * utility method + * + * returns: hash with node types id. + * key : node_type_id (node_types.id) + * value: 1 -> enable on exec can be configured by user + * + * + */ + private function _get_ui_mgtm_cfg_for_node_type($map_node_id_cfg) + { + $enabled_mgmt = array(); + $tl_node_types = $this->decode['nodes']; + foreach ($this->node_types as $verbose_type) { + $type_id = $tl_node_types[$verbose_type]; + if (isset($map_node_id_cfg[$verbose_type])) { + $enabled_mgmt[$type_id] = $map_node_id_cfg[$verbose_type]; + } + } + return $enabled_mgmt; + } + + /* + * function: get_possible_values_cfg + * + * returns: hash + * key : cf_type_id (see $custom_field_types) + * value: 1 -> possible values can be managed on UI. + * + * + */ + public function get_possible_values_cfg() + { + $pv_cfg = array(); + $custom_field_types_id = array_flip($this->custom_field_types); + + foreach ($this->possible_values_cfg as $verbose_cf_type => $use_on_ui) { + $cf_type_id = $custom_field_types_id[$verbose_cf_type]; + $pv_cfg[$cf_type_id] = $use_on_ui; + } + return $pv_cfg; + } + + /** + * + * @param array $context + * @param array $filters + * @param string $access_key + * @return array + */ + public function getLinkedCfieldsAtDesign($context, $filters = null, + $access_key = 'id') + { + $ctx = array( + 'tproject_id' => null, + 'enabled' => true, + 'node_type' => null, + 'node_id' => null + ); + $ctx = array_merge($ctx, $context); + if (null == $ctx['tproject_id']) { + throw new Exception( + __METHOD__ . ' EXCEPTION: test project ID, is mandatory'); + } + + extract($ctx); + + return $this->get_linked_cfields_at_design($tproject_id, $enabled, + $filters, $node_type, $node_id, $access_key); + } + + /* + * function: get_linked_cfields_at_design + * returns information about custom fields that can be used + * at least at design time, with the value assigned (is any has been assigned). + * + * + * $tproject_id: needed because is possible to associate/link + * a different set of custom field for every test project + * + * $enabled : 1 -> get custom fields that are has been configured + * to be shown during specification design AND are enabled. + * + * Remember that also exist custom fields + * that can be only used during TEST CASE EXECUTION. + * + * [$filters]:default: null + * map with keys: + * [show_on_execution]: 1 -> filter on field show_on_execution=1 + * 0 or null or not exists -> don't filter + * + * [show_on_testplan_design]: 1 -> filter on field show_on_execution=1 + * 0 or null or not exists -> don't filter + * + * [cfield_id]: if exists use it's value to filter on custom field id + * null or not exists -> don't filter + * + * [location]: new concept used to define on what location on screen + * custom field will be designed. + * Initally used with CF available for Test cases, to + * implement pre-requisites. + * null => no filtering + * + * [$node_type]: default: null + * verbose id ('testcase', 'testsuite', etc) of a node type. + * custom fields are linked also to different node types. + * Example: + * I can define a custom field "Aspect" with values + * Performace, Usability and wnat use it only for test suites. + * + * [$node_id]: default: null + * identification of a node/element on node hierarchy. + * Needed when I want to get the value of custom fields + * linked to a node. + * Example: + * Have two test cases (ID:9999, ID:89800), and want to get + * the value assigned to custom field "Operating System". + * I will do two calls to this method. + * + * [$access_key]: default id, field name to use as access key in returned hash + * + * returns: hash + * key: custom field id + * + * + * rev : + * + */ + public function get_linked_cfields_at_design($tproject_id, $enabled, + $filters = null, $node_type = null, $node_id = null, $access_key = 'id') + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $additional_join = ""; + $additional_values = ""; + $additional_filter = ""; + + switch ($access_key) { + case 'id': + case 'node_id': + case 'name': + break; + + default: + $access_key = 'id'; + break; + } + + switch ($node_type) { + case 'build': + $table_key = 'cfield_build_design_values'; + break; + + default: + $table_key = 'cfield_design_values'; + break; + } + + if (! is_null($node_type)) { + $additional_join .= " JOIN {$this->tables['cfield_node_types']} CFNT + ON CFNT.field_id=CF.id AND CFNT.node_type_id=" . + $this->db->prepare_int($this->decode['nodes'][$node_type]); + } + + $targetIsArray = false; + if (! is_null($node_id)) { + $additional_values .= ",CFDV.value AS value,CFDV.node_id AS node_id"; + + if (is_array($node_id)) { + $targetIsArray = true; + $sane = array_map('intval', $node_id); + $inClause = implode(',', $sane); + } else { + $inClause = $this->db->prepare_int($node_id); + } + + $additional_join .= " LEFT OUTER JOIN {$this->tables[$table_key]} CFDV ON CFDV.field_id=CF.id + AND CFDV.node_id IN ($inClause) "; + } + + $locFilter = []; + $replaceLocation = false; + $targetLocationCode = 0; + + if (! is_null($filters)) { + if (isset($filters['show_on_execution']) && + ! is_null($filters['show_on_execution'])) { + $additional_filter .= " AND CF.show_on_execution=1 "; + } + + // Probably this piece need to be changed to act on enable_on_ attribute + // due to CF display logic refactoring + // if( isset($filters['show_on_testplan_design']) && !is_null($filters['show_on_testplan_design']) ) + // { + // $additional_filter .= " AND CF.show_on_testplan_design=1 "; + // } + if (isset($filters['show_on_testplan_design']) && + ! is_null($filters['show_on_testplan_design'])) { + $additional_filter .= " AND CF.enable_on_testplan_design=1 "; + } + + if (isset($filters['cfield_id']) && ! is_null($filters['cfield_id'])) { + $additional_filter .= " AND CF.id = {$filters['cfield_id']} "; + } + + $filterKey = 'location'; + if (isset($filters[$filterKey]) && ! is_null($filters[$filterKey])) { + $locFilter = (array) $filters[$filterKey]; + $additional_filter .= " AND CFTP.$filterKey IN(" . + implode(",", $locFilter) . ") "; + + if ($replaceLocation = (count($locFilter) > 1)) { + $locMap = $this->buildLocationMap('testcase'); + $targetLocationCode = " {$locMap['standard_location']['location']} AS location "; + } + } + } + $sql = "/* $debugMsg */ SELECT CF.*,CFTP.display_order,CFTP.location,CFTP.required "; + if ($replaceLocation) { + $sql = str_replace('CFTP.location', $targetLocationCode, $sql); + } + + $sql .= $additional_values . " FROM {$this->object_table} CF " . + " JOIN {$this->tables['cfield_testprojects']} CFTP ON CFTP.field_id=CF.id " . + $additional_join . " WHERE CFTP.testproject_id=" . + intval($tproject_id) . + " AND CFTP.active=1 AND CF.show_on_design=1 " . + " AND CF.enable_on_design={$enabled} " . $additional_filter . + " ORDER BY display_order,CF.id "; + + if ($targetIsArray) { + // # 0008792: Tl 1.9.20 (dev) >> Requirement overview >> Custom field content displayed in wrong column + // $map = $this->db->fetchArrayRowsIntoMap($sql,$access_key); + $map = $this->db->fetchMapRowsIntoMap($sql, $access_key, 'id'); + } else { + $map = $this->db->fetchRowsIntoMap($sql, $access_key); + } + + return $map; + } + + /* + * ==================================================================== + * * Very Imporant ** + * This code is based on Mantis code. + * Initial development was based on 1.x.x versions. + * file:custom_field_api.php - function:print_custom_field_input() + * + * 20080815: some changes are done to add more flexibility, and idea + * was compared with 1.2.0a1 Mantis implementation. + * ==================================================================== + * + * function: string_custom_field_input + * returns an string with the html needed to display the custom field. + * + * If no specific code is found to manage a custom field type, + * it will be used code that manage string type. + * + * args: $p_field_def: contains the definition of the custom field + * (including it's field id) + * + * [$name_suffix]: if used must start with _. + * example _TCID017 + * + * returns: html string + * + * rev : + */ + public function string_custom_field_input($p_field_def, $opt = null) + { + $options = array( + 'name_suffix' => '', + 'field_size' => 0, + 'show_on_filters' => false, + 'remove_required' => false + ); + $options = array_merge($options, (array) $opt); + extract($options); + + $str_out = ''; + + $cfValue = $p_field_def['default_value']; + if (isset($p_field_def['value'])) { + $cfValue = $p_field_def['value']; + } + + $verbose_type = trim($this->custom_field_types[$p_field_def['type']]); + $cfValue = htmlspecialchars($cfValue); + $input_name = $this->buildHTMLInputName($p_field_def, $name_suffix); + $size = isset($this->sizes[$verbose_type]) ? intval( + $this->sizes[$verbose_type]) : 0; + + if ($options['remove_required']) { + $required = ' class="" '; + } else { + $required = $p_field_def['required'] ? ' class="required" required ' : ' class="" '; + } + + $dateOpt = array( + 'default_disable' => false, + 'allow_blank' => true, + 'required' => $required, + 'show_on_filters' => $show_on_filters + ); + + if ($field_size > 0) { + $size = $field_size; + } + + switch ($verbose_type) { + case 'list': + case 'multiselection list': + $t_values = explode('|', $p_field_def['possible_values']); + + if ($verbose_type == 'list') { + // get maximum allowed window size for lists + // $window_size = intval($size) > 1 ? $size : self::LISTBOX_WINDOW_SIZE; + $t_multiple = ' '; + $t_name_suffix = ''; + } else { + $t_name_suffix = '[]'; + $t_multiple = ' multiple="multiple" '; + } + + $html_identity = $input_name . $t_name_suffix; + $str_out .= ''; + break; + + case 'checkbox': + $t_values = explode('|', $p_field_def['possible_values']); + $t_checked_values = explode('|', $cfValue); + foreach ($t_values as $t_option) { + $str_out .= ' ' . $t_option . + '  '; + } else { + $str_out .= ' value="' . $t_option . '"> ' . + $t_option . '  '; + } + } + break; + + case 'string': + case 'email': + case 'float': + case 'numeric': + $str_out .= $this->string_input_string($p_field_def, $input_name, + $cfValue, $size, $options); + break; + + case 'text area': + $cols = intval($this->sizes['text area']['cols']); + $rows = intval($this->sizes['text area']['rows']); + if ($cols <= 0) { + $cols = self::TEXTAREA_DEFAULT_COLS; + } + if ($rows <= 0) { + $rows = self::TEXTAREA_DEFAULT_ROWS; + } + + if ($this->max_length_value > 0) { + $counterId = $input_name . '_counter'; + $cf_current_size = $this->max_length_value - + tlStringLen($cfValue); + + // call JS function for check max. size from validate.js + $js_function = '"textCounter(this.form.' . $input_name . + ',document.getElementById(\'' . $counterId . '\'),' . + $this->max_length_value . ');" '; + + $str_out .= '\n"; + + // show character counter + $str_out .= '
    ' . + sprintf(lang_get('text_counter_feedback'), + $this->max_length_value) . ' ' . $cf_current_size . '.
    '; + } else { + // unlimited + $str_out .= '\n"; + } + break; + + case 'date': + $str_out .= create_date_selection_set($input_name, + config_get('date_format'), $cfValue, $dateOpt); + break; + + case 'datetime': + $cfg = config_get('gui'); + + // Important + // We can do this mix (get date format configuration from standard variable + // and time format from an specific custom field config) because string used + // for date_format on strftime() has no problem + // on date() calls (that are used in create_date_selection_set() ). + $format = config_get('date_format') . " " . + $cfg->custom_fields->time_format; + $str_out .= create_date_selection_set($input_name, $format, + $cfValue, $dateOpt); + break; + + default: + $dynamic_call = 'string_input_' . + str_replace(' ', '_', $verbose_type); + if (function_exists($dynamic_call)) { + $str_out .= $dynamic_call($p_field_def, $input_name, + $cfValue); + } elseif (method_exists($this, $dynamic_call)) { + $str_out .= $this->$dynamic_call($p_field_def, $input_name, + $cfValue); + } else { + // treat it as an simple string + $str_out .= $this->string_input_string($p_field_def, + $input_name, $cfValue, $size, $options); + } + break; + } + return $str_out; + } + + /* + * function: design_values_to_db + * write values of custom fields that are used at design time. + * + * args: $hash: contains info about CF gathered at user interface. + * (normally $_REQUEST variable) + * key: custom_field__. + * Example custom_field_0_67 -> 0=> string field + * + * $node_id: + * [$cf_map]: hash -> all the custom fields linked and enabled + * that are applicable to the node type of $node_id. + * + * For the keys not present in $hash, we will write + * an appropriate value according to custom field + * type. + * + * This is needed because when trying to udpate + * with hash being $_REQUEST, $_POST or $_GET + * some kind of custom fields (checkbox, list, multiple list) + * when has been deselected by user. + * + * + * rev: + */ + public function design_values_to_db($hash, $node_id, $cf_map = null, + $hash_type = null, $node_type = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + if (is_null($hash) && is_null($cf_map)) { + return; + } + + $cfield = $hash; + if (is_null($hash_type)) { + $cfield = $this->_build_cfield($hash, $cf_map); + } + + if (! is_null($cfield)) { + switch ($node_type) { + case 'build': + $table_key = 'cfield_build_design_values'; + break; + + default: + $table_key = 'cfield_design_values'; + break; + } + + $safeNodeID = intval($node_id); + foreach ($cfield as $field_id => $type_and_value) { + $value = $type_and_value['cf_value']; + + // do I need to update or insert this value? + $sql = "/* $debugMsg */ SELECT value FROM {$this->tables[$table_key]} " . + " WHERE field_id=" . intval($field_id) . " AND node_id=" . + $safeNodeID; + + $result = $this->db->exec_query($sql); + + // max_length_value = 0 => no limit + if ($this->max_length_value > 0 && + tlStringLen($value) > $this->max_length_value) { + $value = substr($value, 0, $this->max_length_value); + } + + $safe_value = $this->db->prepare_string($value); + $rowCount = $this->db->num_rows($result); + if ($rowCount > 0) { + if ($value != "") { + $sql = "/* $debugMsg */ UPDATE {$this->tables[$table_key]} " . + " SET value='{$safe_value}' "; + } else { + // bye, bye record + $sql = "/* $debugMsg */ DELETE FROM {$this->tables[$table_key]} "; + } + $sql .= " WHERE field_id=" . intval($field_id) . + " AND node_id=" . $safeNodeID; + $this->db->exec_query($sql); + } elseif ($rowCount == 0 && $value != "") { + # Remark got from Mantis code: + # Always store the value, even if it's the dafault value + # This is important, as the definitions might change but the + # values stored with a bug must not change + $sql = "/* $debugMsg */ INSERT INTO {$this->tables[$table_key]} " . + " ( field_id, node_id, value ) " . " VALUES ( " . + intval($field_id) . ", {$safeNodeID}, '{$safe_value}' )"; + $this->db->exec_query($sql); + } + } + } + } + + /* + * function: remove_all_design_values_from_node + * remove the values of ALL custom fields linked to + * a node. (example test case 5555) + * + * args: $node_id: single value or array + * + * returns: - + * + * rev : + * 20070102 - franciscom - $node_id can be an array + * + */ + public function remove_all_design_values_from_node($node_id, + $node_type = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + switch ($node_type) { + case 'build': + $table_key = 'cfield_build_design_values'; + break; + + default: + $table_key = 'cfield_design_values'; + break; + } + + $sql = "/* $debugMsg */ DELETE FROM {$this->tables[$table_key]} "; + if (is_array($node_id)) { + $sql .= " WHERE node_id IN(" . implode(",", $node_id) . ") "; + } else { + $sql .= " WHERE node_id=" . intval($node_id); + } + + $this->db->exec_query($sql); + } + + /* + * function: get_all + * get the definition of all custom field defined in the system, + * or all custom fields with id not included in $id2exclude. + * + * args: [$id2exclude]: array with custom field ids + * + * returns: hash: + * key: custom field id + * + */ + public function get_all($id2exclude = null, $opt = null) + { + static $lbl; + + if (! $lbl) { + $lbl = init_labels( + array( + 'context_design' => null, + 'context_exec' => null, + 'context_testplan_design' => null + )); + } + + $not_in_clause = ""; + if (! is_null($id2exclude)) { + $not_in_clause = " AND CF.id NOT IN (" . implode(',', $id2exclude) . + ") "; + } + $sql = "SELECT CF.*,NT.description AS node_description,NT.id AS node_type_id " . + " FROM {$this->object_table} CF, " . + " {$this->tables['cfield_node_types']} CFNT, " . + " {$this->tables['node_types']} NT " . + " WHERE CF.id=CFNT.field_id " . " AND NT.id=CFNT.node_type_id " . + $not_in_clause . " ORDER BY CF.name"; + + $map = $this->db->fetchRowsIntoMap($sql, 'id'); + if (! is_null($map) && ! is_null($opt)) { + $k2l = array_keys($map); + foreach ($k2l as $key) { + $map[$key]['enabled_on_context'] = ''; + if ($map[$key]['enable_on_design']) { + $map[$key]['enabled_on_context'] = $lbl['context_design']; + } elseif ($map[$key]['enable_on_execution']) { + $map[$key]['enabled_on_context'] = $lbl['context_exec']; + } elseif ($map[$key]['enable_on_testplan_design']) { + $map[$key]['enabled_on_context'] = $lbl['context_testplan_design']; + } + } + } + + return $map; + } + + /* + * function: get_linked_to_testproject + * get definition of all custom fields linked to a test project. + * + * + * args: $tproject_id + * [$active]: if not null will add the following filter " AND CFTP.active={$active}" + * + * returns: hash: + * key: custom field id + * + * internal revision: + */ + public function get_linked_to_testproject($tproject_id, $active = null, + $opt = null) + { + $options = array( + 'name' => null + ); + $options = array_merge($options, (array) $opt); + + $sql = "SELECT CF.*,NT.description AS node_description,NT.id AS node_type_id, " . + " CFTP.display_order, CFTP.active, CFTP.location,CFTP.required,CFTP.monitorable " . + " FROM {$this->object_table} CF, " . + " {$this->tables['cfield_testprojects']} CFTP, " . + " {$this->tables['cfield_node_types']} CFNT, " . + " {$this->tables['node_types']} NT " . + " WHERE CF.id=CFNT.field_id " . " AND CF.id=CFTP.field_id " . + " AND NT.id=CFNT.node_type_id " . " AND CFTP.testproject_id=" . + $this->db->prepare_int($tproject_id); + + if (! is_null($active)) { + $sql .= " AND CFTP.active={$active} "; + } + + if (! is_null($options['name'])) { + $sql .= " AND CF.name='" . + $this->db->prepare_string($options['name']) . "'"; + } + + $sql .= " ORDER BY NT.description,CF.enable_on_design desc, " . + " CF.enable_on_execution desc, " . + " CF.enable_on_testplan_design desc," . + " CFTP.display_order, CF.name"; + + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /* + * function: link_to_testproject + * + * + * + * args: $tproject_id + * $cfields_id: array() + * + * returns: - + */ + public function link_to_testproject($tproject_id, $cfield_ids) + { + if (is_null($cfield_ids)) { + return; + } + + $debugMsg = $this->debugMsg . __FUNCTION__; + $safeID = intval($tproject_id); + $tproject_info = $this->tree_manager->get_node_hierarchy_info($safeID); + foreach ($cfield_ids as $field_id) { + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['cfield_testprojects']} " . + " (testproject_id,field_id) " . " VALUES({$safeID},{$field_id})"; + + if ($this->db->exec_query($sql)) { + $cf = $this->get_by_id($field_id); + if ($cf) { + logAuditEvent( + TLS("audit_cfield_assigned", $cf[$field_id]['name'], + $tproject_info['name']), "ASSIGN", $tproject_id, + "testprojects"); + } + } + } + } + + /* + * function: set_active_for_testproject + * set the value of active field + * + * + * args: $tproject_id + * $cfields_id: array() + * $active_val: 1/0 + * + * returns: - + */ + public function set_active_for_testproject($tproject_id, $cfield_ids, + $active_val) + { + if (is_null($cfield_ids)) { + return; + } + + $debugMsg = $this->debugMsg . __FUNCTION__; + $tproject_info = $this->tree_manager->get_node_hierarchy_info( + $tproject_id); + $auditMsg = $active_val ? "audit_cfield_activated" : "audit_cfield_deactivated"; + foreach ($cfield_ids as $field_id) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['cfield_testprojects']} " . + " SET active={$active_val} " . " WHERE testproject_id=" . + $this->db->prepare_int($tproject_id) . " AND field_id=" . + $this->db->prepare_int($field_id); + + if ($this->db->exec_query($sql)) { + $cf = $this->get_by_id($field_id); + if ($cf) { + logAuditEvent( + TLS($auditMsg, $cf[$field_id]['name'], + $tproject_info['name']), "SAVE", $tproject_id, + "testprojects"); + } + } + } + } + + /** + */ + public function setRequired($tproject_id, $cfieldSet, $val) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + if (is_null($cfieldSet)) { + return; + } + + $safe = new stdClass(); + $safe->tproject_id = intval($tproject_id); + $safe->val = (intval($val) > 0) ? 1 : 0; + + $info = $this->tree_manager->get_node_hierarchy_info($safe->tproject_id); + $auditMsg = $val ? "audit_cfield_required_on" : "audit_cfield_required_off"; + foreach ($cfieldSet as $field_id) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['cfield_testprojects']} " . + " SET required=" . $safe->val . " WHERE testproject_id=" . + $safe->tproject_id . " AND field_id=" . + $this->db->prepare_int($field_id); + + if ($this->db->exec_query($sql)) { + $cf = $this->get_by_id($field_id); + if ($cf) { + logAuditEvent( + TLS($auditMsg, $cf[$field_id]['name'], $info['name']), + "SAVE", $safe->tproject_id, "testprojects"); + } + } + } + } + + // function end + + /** + * unlink_from_testproject + * remove custom field links from target test project + * N.B.: following Mantis Bugtracking System model, + * this operation will NOR remove all values assigned to + * these custom fields . + * + * @param int $tproject_id + * @param array $cfield_ids + * + */ + public function unlink_from_testproject($tproject_id, $cfield_ids) + { + if (is_null($cfield_ids)) { + return; + } + + $debugMsg = $this->debugMsg . __FUNCTION__; + $tproject_info = $this->tree_manager->get_node_hierarchy_info( + $tproject_id); + foreach ($cfield_ids as $field_id) { + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_testprojects']} " . + " WHERE field_id = " . $this->db->prepare_int($field_id) . + " AND testproject_id = " . $this->db->prepare_int($tproject_id); + if ($this->db->exec_query($sql)) { + $cf = $this->get_by_id($field_id); + if ($cf) { + logAuditEvent( + TLS("audit_cfield_unassigned", $cf[$field_id]['name'], + $tproject_info['name']), "ASSIGN", $tproject_id, + "testprojects"); + } + } + } + } + + /* + * function: get_by_name + * get custom field definition + * + * args: $name: custom field name + * + * returns: hash + */ + public function get_by_name($name) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my_name = $this->db->prepare_string(trim($name)); + + $sql = "/* $debugMsg */ SELECT CF.*, CFNT.node_type_id,NT.description AS node_type" . + " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_node_types']} CFNT," . + " {$this->tables['node_types']} NT" . " WHERE CF.id=CFNT.field_id " . + " AND CFNT.node_type_id=NT.id " . " AND name='{$my_name}' "; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /* + * function: get_by_id + * get custom field definition + * + * args: $id: custom field id + * + * returns: hash + * + */ + public function get_by_id($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT CF.*, CFNT.node_type_id" . + " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_node_types']} CFNT" . + " WHERE CF.id=CFNT.field_id " . " AND CF.id IN (" . + implode(',', (array) $id) . ")"; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /* + * function: get_available_item_type + * get information about what item type (testcase,testplan, etc) + * can use this custom field + * + * args: $id: custom field id + * + * returns: + */ + private function get_available_item_type($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT CFNT.field_id,CFNT.node_type_id " . + " FROM {$this->tables['cfield_node_types']} CFNT, " . + " {$this->tables['nodes_types']} NT " . + " WHERE NT.id=CFNT.node_type_id " . " CFNt.field_id=" . + $this->db->prepare_int($id); + + return $this->db->fetchRowsIntoMap($sql, 'field_id'); + } + + /* + * + * keys name -> trim will be applied + * label -> trim will be applied + * type -> intval() wil be applied + * possible_values + * show_on_design -> trasformation on 1/0 using intval() [*] + * enable_on_design -> [*] + * show_on_execute -> [*] + * enable_on_execute -> [*] + * show_on_testplan_design -> [*] + * enable_on_testplan_design -> [*] + * + */ + private function sanitize($cf) + { + $safe = $cf; + + // remove the standard set of characters considered harmful + // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab + // "\r" - carriage return + // and spaces + // fortunatelly this is trim standard behaviour + $k2san = array( + 'name', + 'label' + ); + foreach ($k2san as $key) { + $safe[$key] = $this->db->prepare_string(trim($cf[$key])); + } + + // seems here is better do not touch. + $safe['possible_values'] = $this->db->prepare_string( + $cf['possible_values']); + + $onezero = array( + 'show_on_design', + 'enable_on_design', + 'show_on_testplan_design', + 'enable_on_testplan_design', + 'show_on_execution', + 'enable_on_execution' + ); + + foreach ($onezero as $key) { + $safe[$key] = intval($cf[$key]) > 0 ? 1 : 0; + } + + $safe['type'] = intval((int) $cf['type']); + $safe['node_type_id'] = intval((int) $cf['node_type_id']); + return $safe; + } + + /** + * create a custom field + * + * @param array $cf + * keys: name -> trim will be applied + * label -> trim will be applied + * type -> intval() wil be applied + * possible_values + * show_on_design -> trasformation on 1/0 using intval() [*] + * enable_on_design -> [*] + * show_on_execute -> [*] + * enable_on_execute -> [*] + * show_on_testplan_design -> [*] + * enable_on_testplan_design -> [*] + * node_type_id + * @return array + * @internal revision + */ + public function create($cf) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $ret = array( + 'status_ok' => 0, + 'id' => 0, + 'msg' => 'ko' + ); + + $safecf = $this->sanitize($cf); + + // if CF is for BUILD force enable_on_execution ALWAYS FALSE + // Node Verbose Code / Node Code Verbose + $nvc = $this->tree_manager->get_available_node_types(); + $ncv = array_flip($nvc); + if ($ncv[$safecf['node_type_id']] == 'build') { + $safecf['enable_on_design'] = 1; + $safecf['enable_on_execution'] = 0; + } + + $sql = "/* $debugMsg */ INSERT INTO {$this->object_table} " . + " (name,label,type,possible_values, " . + " show_on_design,enable_on_design, " . + " show_on_testplan_design,enable_on_testplan_design, " . + " show_on_execution,enable_on_execution) " . " VALUES('" . + $safecf['name'] . "','" . $safecf['label'] . "'," . + intval($safecf['type']) . ",'" . $safecf['possible_values'] . "', " . + " {$safecf['show_on_design']},{$safecf['enable_on_design']}," . + " {$safecf['show_on_testplan_design']},{$safecf['enable_on_testplan_design']}," . + " {$safecf['show_on_execution']},{$safecf['enable_on_execution']})"; + $result = $this->db->exec_query($sql); + + if ($result) { + // at least for Postgres DBMS table name is needed. + $field_id = $this->db->insert_id($this->object_table); + + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['cfield_node_types']} " . + " (field_id,node_type_id) " . + " VALUES({$field_id},{$safecf['node_type_id']}) "; + $result = $this->db->exec_query($sql); + } + + if ($result) { + $ret = array( + 'status_ok' => 1, + 'id' => $field_id, + 'msg' => 'ok' + ); + } + return $ret; + } + + /** + * update a custom field + * + * @param array $cf + * keys: name + * label + * type + * possible_values + * show_on_design + * enable_on_design + * show_on_execute + * enable_on_execute + * show_on_testplan_design + * enable_on_testplan_design + * node_type_id + * @return number + */ + public function update($cf) + { + $safecf = $this->sanitize($cf); + + // if CF is for BUILD force enable_on_execution ALWAYS FALSE + // Node Verbose Code / Node Code Verbose + $nvc = $this->tree_manager->get_available_node_types(); + $ncv = array_flip($nvc); + if ($ncv[$safecf['node_type_id']] == 'build') { + $safecf['enable_on_design'] = 1; + $safecf['enable_on_execution'] = 0; + } + + $sql = "UPDATE {$this->tables['custom_fields']} " . " SET name='" . + $safecf['name'] . "'," . " label='" . $safecf['label'] . "'," . + " type={$safecf['type']}," . " possible_values='" . + $safecf['possible_values'] . "'," . + " show_on_design={$safecf['show_on_design']}," . + " enable_on_design={$safecf['enable_on_design']}," . + " show_on_testplan_design={$safecf['show_on_testplan_design']}," . + " enable_on_testplan_design={$safecf['enable_on_testplan_design']}," . + " show_on_execution={$safecf['show_on_execution']}," . + " enable_on_execution={$safecf['enable_on_execution']}" . + " WHERE id={$safecf['id']}"; + $result = $this->db->exec_query($sql); + + if ($result) { + $sql = "UPDATE {$this->tables['cfield_node_types']} " . + " SET node_type_id={$safecf['node_type_id']}" . + " WHERE field_id={$safecf['id']}"; + $result = $this->db->exec_query($sql); + } + return $result ? 1 : 0; + } + + /** + * Will delete custom field definition and also ALL assigned values + * If custom field is linked to test projects, these links must be removed + * + * @param int $id + * @return number + */ + public function delete($id) + { + // Before deleting definition I need to remove values + if ($this->is_used($id)) { + $this->remove_all_scopes_values($id); + } + $linked_tprojects = $this->get_linked_testprojects($id); + if (! empty($linked_tprojects)) { + $target = array_keys($linked_tprojects); + foreach ($target as $tproject_id) { + $this->unlink_from_testproject($tproject_id, (array) $id); + } + } + + $sql = "DELETE FROM {$this->tables['cfield_node_types']} WHERE field_id={$id}"; + $result = $this->db->exec_query($sql); + if ($result) { + $sql = "DELETE FROM {$this->tables['custom_fields']} WHERE id={$id}"; + $result = $this->db->exec_query($sql); + } + return $result ? 1 : 0; + } + + /* + * function: is_used + * + * args: $id: custom field id + * + * returns: 1/0 + * + * @used by cfieldsEdit.php, cfieldsEdit.tpl + * + */ + public function is_used($id) + { + $sql = "SELECT field_id FROM {$this->tables['cfield_design_values']} " . + "WHERE field_id={$id} " . "UNION " . + "SELECT field_id FROM {$this->tables['cfield_build_design_values']} " . + "WHERE field_id={$id} " . "UNION " . + "SELECT field_id FROM {$this->tables['cfield_testplan_design_values']} " . + "WHERE field_id={$id} " . "UNION " . + "SELECT field_id FROM {$this->tables['cfield_execution_values']} " . + "WHERE field_id={$id} "; + $result = $this->db->exec_query($sql); + return $this->db->num_rows($result) > 0 ? 1 : 0; + } + + // function end + + /** + * + * @param int $id + * @param string $name + * @return number 1 => name is unique + */ + public function name_is_unique($id, $name) + { + $cf = $this->get_by_name($name); + $status = 0; + if (is_null($cf) || isset($cf[$id])) { + $status = 1; + } + return $status; + } + + # -------------------- + # Adapted from Mantis code + # Prepare a string containing a custom field value for display + # $p_field_def definition of the custom field + # $p_node_id bug id to display the custom field value for + # + # [$p_value_field]: field id, to point to the field value in $p_field_def + public function string_custom_field_value($p_field_def, $p_node_id, + $p_value_field = 'value') + { + $t_value = isset($p_field_def[$p_value_field]) ? $p_field_def[$p_value_field] : null; + $cfValue = htmlspecialchars($t_value); + + switch ($this->custom_field_types[intval($p_field_def['type'])]) { + case 'email': + return "$cfValue"; + break; + + case 'enum': + case 'list': + case 'multiselection list': + case 'checkbox': + return str_replace('|', ', ', $cfValue); + break; + + case 'date': + if ($cfValue != null) { + // must remove % + $t_date_format = str_replace("%", "", + config_get('date_format')); + return date($t_date_format, $cfValue); + } + break; + + case 'datetime': + if ($cfValue != null) { + // must remove % + // $t_date_format=str_replace("%","",config_get( 'timestamp_format')); + // $datetime_format=$t_date_format; + $t_date_format = str_replace("%", "", + config_get('date_format')); + $cfg = config_get('gui'); + $datetime_format = $t_date_format . " " . + $cfg->custom_fields->time_format; + return date($datetime_format, $cfValue); + } + break; + + case 'text area': + if ($cfValue != null) { + return nl2br($cfValue); + } + break; + + case 'string': + return string_display_links($cfValue); + break; + + default: + // done this way in order to be able to debug if needed + return string_display_links($cfValue); + break; + } + } + + /* + * function: get_linked_cfields_at_execution + * returns information about custom fields that can be used + * at least at executed, with the value assigned (is any has been assigned). + * + * + * $tproject_id: needed because is possible to associate/link + * a different set of custom field for every test project + * + * $enabled : 1 -> get custom fields that are has been configured + * to be shown during test case execution AND are enabled. + * + * [$node_type]: default: null + * verbose id ('testcase', 'testsuite', etc) of a node type. + * custom fields are linked also to different node types. + * Example: + * I can define a custom field "Aspect" with values + * Performace, Usability and wnat use it only for test suites. + * + * [$node_id]: default: null + * identification of a node/element on node hierarchy. + * Needed when I want to get the value of custom fields + * linked to a node. + * Example: + * Have two test cases (ID:9999, ID:89800), and want to get + * the value assigned to custom field "Operating System". + * I will do two calls to this method. + * + * + * [execution_id] + * [testplan_id] + * [access_key] + * [location] + * + * returns: hash + * key: custom field id + * + */ + public function get_linked_cfields_at_execution($tproject_id, $enabled, + $node_type = null, $node_id = null, $execution_id = null, + $testplan_id = null, $access_key = 'id', $location = null) + { + $base_values = "CF.*,"; + $additional_join = ""; + $additional_values = ""; + $additional_filter = ""; + $order_clause = " ORDER BY display_order,CF.id "; + + $fetchMethod = 'fetchRowsIntoMap'; + + if (! is_null($node_type)) { + $additional_join .= " JOIN {$this->tables['cfield_node_types']} CFNT ON CFNT.field_id=CF.id " . + " AND CFNT.node_type_id=" . $this->decode['nodes'][$node_type]; + } + + if (! is_null($node_id) && ! is_null($execution_id) && + ! is_null($testplan_id)) { + $additional_values .= ",CFEV.value AS value,CFEV.tcversion_id AS node_id"; + $additional_join .= " LEFT OUTER JOIN {$this->tables['cfield_execution_values']} CFEV ON CFEV.field_id=CF.id " . + " AND CFEV.tcversion_id=" . intval($node_id) . " " . + " AND CFEV.execution_id=" . intval($execution_id) . " " . + " AND CFEV.testplan_id=" . intval($testplan_id) . " "; + } elseif (! is_null($execution_id)) { + $access_key = 'execution_id'; + $fetchMethod = 'fetchMapRowsIntoMap'; + $additional_values .= ',CFEV.value AS value, CFEV.execution_id '; + $additional_join .= " LEFT OUTER JOIN {$this->tables['cfield_execution_values']} CFEV ON CFEV.field_id=CF.id " . + " AND CFEV.execution_id IN (" . implode(',', $execution_id) . + ") "; + } else { + if (! is_null($testplan_id)) { + $base_values = ''; + + // MSSQL BLOCKING error on Report "Test Cases with Execution Details" due to reserved word EXEC + $additional_values .= ",CF.type,CF.name,CF.label,CF.id,CFEV.value AS value,CFEV.tcversion_id AS node_id," . + "EXECU.id AS exec_id, EXECU.tcversion_id,EXECU.tcversion_number," . + "EXECU.execution_ts,EXECU.status AS exec_status,EXECU.notes AS exec_notes, " . + "NHB.id AS tcase_id, NHB.name AS tcase_name, TCV.tc_external_id, " . + "B.id AS builds_id,B.name AS build_name, U.login AS tester, " . + "PLAT.name AS platform_name, COALESCE(PLAT.id,0) AS platform_id"; + + $additional_join .= " JOIN {$this->tables['cfield_execution_values']} CFEV ON CFEV.field_id=CF.id " . + " AND CFEV.testplan_id={$testplan_id} " . + " JOIN {$this->tables['executions']} EXECU ON CFEV.tcversion_id = EXECU.tcversion_id " . + " AND CFEV.execution_id = EXECU.id "; + + $additional_join .= " JOIN {$this->tables['builds']} B ON B.id = EXECU.build_id " . + " AND B.testplan_id = EXECU.testplan_id "; + + $additional_join .= " JOIN {$this->tables['tcversions']} TCV ON TCV.version = EXECU.tcversion_number " . + " AND TCV.id = EXECU.tcversion_id "; + + $additional_join .= " JOIN {$this->tables['users']} U ON U.id = EXECU.tester_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHA ON NHA.id = EXECU.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHB.id = NHA.parent_id "; + + // Use left join, if platforms is not used platform_name will become null + $additional_join .= " LEFT JOIN {$this->tables['platforms']} PLAT ON EXECU.platform_id = PLAT.id"; + $order_clause = "ORDER BY EXECU.tcversion_id,exec_status,exec_id"; + + $fetchMethod = 'fetchArrayRowsIntoMap'; + } + } + + if (! is_null($location)) { + $additional_filter .= " AND CF.id= " . intval($location) . " "; + } + + $sql = "SELECT {$base_values} CFTP.display_order,CFTP.location,CFTP.required" . + $additional_values . " FROM {$this->tables['custom_fields']} CF " . + " JOIN {$this->tables['cfield_testprojects']} CFTP ON CFTP.field_id=CF.id " . + $additional_join . " WHERE CFTP.testproject_id={$tproject_id} " . + " AND CFTP.active=1 " . " AND CF.enable_on_execution={$enabled} " . + " AND CF.show_on_execution=1 {$additional_filter} {$order_clause} "; + + switch ($fetchMethod) { + case 'fetchArrayRowsIntoMap': + case 'fetchRowsIntoMap': + $map = $this->db->$fetchMethod($sql, $access_key); + break; + + case 'fetchMapRowsIntoMap': + $map = $this->db->$fetchMethod($sql, $access_key, 'id'); + break; + } + return $map; + } + + /* + * function: execution_values_to_db + * write values of custom fields that are used at execution time. + * if record exists => UPDATE + * + * args: $hash: contains info about CF gathered at user interface. + * (normally $_REQUEST variable) + * key: custom_field__. + * Example custom_field_0_67 -> 0=> string field + * + * $node_id: + * $execution_id: + * $testplan_id: + * + * [$cf_map]: hash -> all the custom fields linked and enabled + * that are applicable to the node type of $node_id. + * + * For the keys not present in $hash, we will write + * an appropriate value according to custom field + * type. + * + * This is needed because when trying to udpate + * with hash being $_REQUEST, $_POST or $_GET + * some kind of custom fields (checkbox, list, multiple list) + * when has been deselected by user. + * + * + * [hash_type]: default null, string that can be used to change how hash + * is processed. + * + * rev: + * 20090727 - franciscom - added [hash_type], to reuse this method on API + * 20070501 - franciscom - limiting lenght of value before writting + */ + public function execution_values_to_db($hash, $node_id, $execution_id, + $testplan_id, $cf_map = null, $hash_type = null) + { + if (is_null($hash) && is_null($cf_map)) { + return; + } + + $cfield = $hash; + if (is_null($hash_type)) { + $cfield = $this->_build_cfield($hash, $cf_map); + } + + if (! is_null($cfield)) { + foreach ($cfield as $field_id => $type_and_value) { + $value = $type_and_value['cf_value']; + + $where_clause = " WHERE field_id=" . + $this->db->prepare_int($field_id) . " AND tcversion_id=" . + $this->db->prepare_int($node_id) . " AND execution_id=" . + $this->db->prepare_int($execution_id) . " AND testplan_id=" . + $this->db->prepare_int($testplan_id); + + $debugMsg = $this->debugMsg . __FUNCTION__; + + // do I need to update or insert this value? + $sql = " SELECT value,field_id,execution_id " . + " FROM {$this->tables['cfield_execution_values']} " . + $where_clause; + + $rs = (array) $this->db->get_recordset($sql); + + // max_length_value = 0 => no limit + if ($this->max_length_value > 0 && + tlStringLen($value) > $this->max_length_value) { + $value = substr($value, 0, $this->max_length_value); + } + $safe_value = $this->db->prepare_string($value); + + $howMany = count($rs); + if ($howMany > 0 && $value != "") { + $sql = " UPDATE {$this->tables['cfield_execution_values']} " . + " SET value='{$safe_value}' " . $where_clause; + $this->db->exec_query($sql); + } elseif ($howMany == 0 && $value != "") { + + # Remark got from Mantis code: + # Always store the value, even if it's the default value + # This is important, as the definitions might change but the + # values stored with a bug must not change + $sql = "INSERT INTO {$this->tables['cfield_execution_values']} " . + " ( field_id, tcversion_id, execution_id,testplan_id,value ) " . + " VALUES ( {$field_id}, {$node_id}, {$execution_id}, {$testplan_id}, '{$safe_value}' )"; + $this->db->exec_query($sql); + } elseif ($howMany > 0 && $value == "") { + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_execution_values']} " . + $where_clause; + $this->db->exec_query($sql); + } + } + } + } + + /* + * function: _build_cfield + * support function useful for method used to write CF values to db: + * - design_values_to_db() + * - execution_values_to_db() + * - testplan_design_values_to_db() + * + * args: $hash: + * key: custom_field__[_][_]. + * Example custom_field_0_67 -> 0=> string field + * + * In certain situation we can get: + * custom_field_0_67_234 + * 0 => string field + * 234 => item owner of CF. + * this happens when you can have multiple times same CF on a page, as happens + * on execution page if configure TL to work on all test cases in test suite, + * or when you use CF on testplan_design. + * + * To understand [<_date_part>] read below on "Notes on DATE PART - _build_cfield" + * + * value: can be an array, or a string depending the + * + * $cf_map: hash + * key: cfield_id + * value: custom field definition data + * + * + * returns: hash or null. + * + * key: cfield_id + * value: hash ('type_id' => field_type_id, + * 'cf_value' => value) + * + * rev: + */ + public function _build_cfield($hash, $cf_map) + { + $localesDateFormat = config_get('locales_date_format'); + $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; + $date_format = str_replace('%', '', $localesDateFormat[$locale]); + + // carved in the stone + $cf_prefix = $this->name_prefix; + $len_cfp = tlStringLen($cf_prefix); + $cftype_pos = 2; + $cfid_pos = 3; + $cfield = null; + + if (! is_null($cf_map)) { + foreach ($cf_map as $key => $value) { + $cfield[$key] = array( + "type_id" => $value['type'], + "cf_value" => '' + ); + } + } + + // Overwrite with values if custom field id exist + if (! is_null($hash)) { + foreach ($hash as $key => $value) { + if (strncmp($key, $cf_prefix, $len_cfp) == 0) { + // Notes on DATE PART - _build_cfield + // + // When using Custom Fields on Test Spec: + // key has this format (for every type except date ) + // custom_field_0_10 for every type except for type date & datetime. + // + // For date custom fields: + // custom_field_8_10_input + // + // For datetime custom fields + // custom_field_8_10_input + // custom_field_8_10_hour, custom_field_8_10_minute, ..._second + // + // After explode() + // Position 2: CF type + // Position 3: CF id + // Position 4: only available for date CF, is date part indicator + // + // When using Custom Fields on Execution + // another piece is added (TC id) then for a date CF, + // date part indicator is Position 5, instead of 4 + // + // When using Custom Fields on Testplan Design + // another piece is added (testplan_tcversion.id) then for a date CF, + // date part indicator is Position 5, instead of 4 + $dummy = explode('_', $key); + $last_idx = count($dummy) - 1; + + $the_value = null; // without this #0008347 :( + if (isset($this->html_date_input_suffix[$dummy[$last_idx]])) { + $the_value[$dummy[$last_idx]] = $value; + } else { + $the_value = $value; + } + + $cfield[$dummy[$cfid_pos]] = array( + "type_id" => $dummy[$cftype_pos], + "cf_value" => $the_value + ); + } + } + } + + if (! is_null($cfield)) { + foreach ($cfield as $field_id => $type_and_value) { + $value = $type_and_value['cf_value']; + $verbose_type = trim( + $this->custom_field_types[$type_and_value['type_id']]); + switch ($verbose_type) { + case 'multiselection list': + case 'checkbox': + $valueIsArray = is_array($value); + if ($valueIsArray && count($value) > 1) { + $value = implode('|', $value); + } else { + $value = $valueIsArray ? $value[0] : $value; + } + $cfield[$field_id]['cf_value'] = $value; + break; + + case 'date': + if (($value['input'] == 0) || ($value['input'] == '')) { + $cfield[$field_id]['cf_value'] = ''; + } else { + $cfield[$field_id]['cf_value'] = ''; + $pvalue = split_localized_date($value['input'], + $date_format); + if ($pvalue != null) { + $cfield[$field_id]['cf_value'] = mktime(0, 0, 0, + $pvalue['month'], $pvalue['day'], + $pvalue['year']); + } + } + break; + + case 'datetime': + if ($value['input'] == '') { + $cfield[$field_id]['cf_value'] = ''; + } else { + $cfield[$field_id]['cf_value'] = ''; + $pvalue = split_localized_date($value['input'], + $date_format); + if ($pvalue != null) { + if ($value['hour'] == - 1 || + $value['minute'] == - 1 || + $value['second'] == - 1) { + $value['hour'] = $value['minute'] = $value['second'] = 0; + } + $cfield[$field_id]['cf_value'] = mktime( + $value['hour'], $value['minute'], + $value['second'], $pvalue['month'], + $pvalue['day'], $pvalue['year']); + } + } + break; + + default: + $dynamic_call = 'build_cfield_' . + str_replace(' ', '_', $verbose_type); + if (function_exists($dynamic_call)) { + $cfield[$field_id]['cf_value'] = $dynamic_call( + $value); + } elseif (method_exists($this, $dynamic_call)) { + $cfield[$field_id]['cf_value'] = $this->$dynamic_call( + $value); + } else { + $cfield[$field_id]['cf_value'] = $value; + } + break; + } + } + } + return $cfield; + } + + /* + * function: set_display_order + * + * args : $tproject_id: needed because is possible to associate/link + * a different set of custom field for every test project + * $map_field_id_display_order + * + * + * + * returns: + * + */ + public function set_display_order($tproject_id, $map_field_id_display_order) + { + $tproject_info = $this->tree_manager->get_node_hierarchy_info( + $tproject_id); + foreach ($map_field_id_display_order as $field_id => $display_order) { + $sql = "UPDATE {$this->tables['cfield_testprojects']} " . + " SET display_order=" . intval($display_order) . + " WHERE testproject_id={$tproject_id} AND field_id={$field_id} "; + $this->db->exec_query($sql); + } + if ($tproject_info) { + logAuditEvent( + TLS("audit_cfield_display_order_changed", $tproject_info['name']), + "SAVE", $tproject_id, "testprojects"); + } + } + + /** + * set value of location attribute for one or multiple custom fields. + */ + public function setDisplayLocation($tproject_id, $field_id_location) + { + $tproject_info = $this->tree_manager->get_node_hierarchy_info( + $tproject_id); + foreach ($field_id_location as $field_id => $location) { + $sql = "UPDATE {$this->tables['cfield_testprojects']} " . + " SET location=" . intval($location) . + " WHERE testproject_id={$tproject_id} AND field_id={$field_id} "; + $this->db->exec_query($sql); + } + if ($tproject_info) { + logAuditEvent( + TLS("audit_cfield_location_changed", $tproject_info['name']), + "SAVE", $tproject_id, "testprojects"); + } + } + + # code from mantis helper_api.php + # -------------------- + # returns a tab index value and increments it by one. This is used to give sequential tab index on + # a form. + private function helper_get_tab_index_value() + { + static $tab_index = 0; + return ++ $tab_index; + } + + # -------------------- + # returns a tab index and increments internal state by 1. This is used to give sequential tab index on + # a form. For example, this function returns: tabindex="1" + private function helper_get_tab_index() + { + return 'tabindex="' . helper_get_tab_index_value() . '"'; + } + + /** + * Retrieves the XML-RPC Server Parameters specified through custom fields. + * + * Done searching CARVED in the stone Custom Field Names on different + * (AGAIN CARVED in the stone) CF value tables in this way: + * + * CF name will have 3 pieces separated by _ (underscore) + * + * RE-XMLRPC_url_tsuite + * RE-XMLRPC_url_tcase + * RE-XMLRPC_url_link + * + * Part 1: RE-XMLRPC_ FIXED value, used as search key to get automatically + * CF to be analised. + * + * Part 2: url will be key on returned hash, and is part of 'contract' with caller, + * i.e. caller will use this key. + * This key is a FREE choice of developer of Remote Execute modules to use + * with TL. + * + * Part 3: this part is domain (link,tcase,tsuite) + * work this way: + * To specify Remote Execution server parameters we have provided 3 choices + * a. on test case version LINKED to Test Plan + Platform (Test Plan Design time) + * b. on test case version BUT at Test Spec Design time. + * In this way if is OK to have always same parameters no matter + * test plan + platform where test case version has been linked, we configure + * this just ONCE. + * c. on test suite (can be done ONLY at Test Spec Design time), all test case versions + * contained on this test suite branch (and children Test suites) will share this + * configuration. + * + * + * + * @param integer $node_id + * Accepts current node id from nodes hierarchy level + * @return mixed An array of config params if found, else returns null + * + * @internal rev: + * + * 20110123 - franciscom - need refactoring after we have choose to link custom field + * values to test case version not to test case + * + */ + public function getXMLRPCServerParams($nodeID, $tplanLinkID = null) + { + static $node_type; + static $likeTarget; + static $CFGKEY_IDX; + + $debugMsg = $this->debugMsg . __FUNCTION__; + + if (is_null($node_type)) { + $node_type = $this->tree_manager->get_available_node_types(); + $likeTarget = 'RE-XMLRPC_%'; + $CFGKEY_IDX = 1; + } + + $node_info = $this->tree_manager->get_node_hierarchy_info($nodeID); + $ret = null; + + if (! is_null($node_info)) { + $server_info = null; + + // First Search at test plan design time + if (! is_null($tplanLinkID)) { + $sql = " /* $debugMsg */ SELECT cf.name, cfv.value " . + " FROM {$this->tables['cfield_testplan_design_values']} cfv " . + " JOIN {$this->tables['custom_fields']} cf ON " . + " cfv.field_id = cf.id " . + " WHERE cf.name LIKE '{$likeTarget}' " . + " AND cfv.link_id = " . intval($tplanLinkID); + + $server_info = $this->db->fetchRowsIntoMap($sql, 'name'); + } + + if (is_null($server_info)) { + + $sql = " /* $debugMsg */ SELECT cf.name, cfv.value " . + " FROM {$this->tables['cfield_design_values']} cfv " . + " JOIN {$this->tables['custom_fields']} cf ON " . + " cfv.field_id = cf.id " . + " WHERE cf.name LIKE '{$likeTarget}' " . + " AND cfv.node_id = " . intval($nodeID); + + $server_info = $this->db->fetchRowsIntoMap($sql, 'name'); + } + + if (is_null($server_info)) { + // Recurse + // 20110123 - franciscom + // At time of initial development this was thinked to try to get + // server info from Test Suite. + // Because with TL 1.9.x when working with test case we will receive + // Test Case Version ID, instead of Test Case ID (1.8.x), we will do + // a call to reach Test Case and then another to reach Test Suite + if ($node_info['parent_id'] != "") { + $ret = $this->getXMLRPCServerParams($node_info['parent_id']); + } + } else { + $key2loop = array_keys($server_info); + foreach ($key2loop as $target) { + $dummy = explode('_', $target); + $ret[$dummy[$CFGKEY_IDX]] = $server_info[$target]['value']; + } + } + } + + return $ret; + } + + /* + * function: testplan_design_values_to_db + * write values of custom fields that are used at testplan design time. + * + * args: $hash: contains info about CF gathered at user interface. + * (normally $_REQUEST variable) + * key: custom_field__. + * Example custom_field_0_67 -> 0=> string field + * + * $node_id: Remember that this CF are used to extend information + * on test cases (tcversions) linked to test plans. + * Then node_id can not point to other type of node than test case version, + * then node_id will contain a tcversion_id. + * + * I have leave this argument to + * + * + * + * $link_id: Remember that this CF are used to extend information + * on test cases (tcversions) linked to test plans. + * Link information is store in testplan_tcversions table, + * $link_id points to this link (testplan_tcversions.id field) + * + * [$cf_map]: hash -> all the custom fields linked and enabled + * that are applicable to the node type of $node_id. + * + * For the keys not present in $hash, we will write + * an appropriate value according to custom field + * type. + * This is needed because when trying to udpate + * with hash being $_REQUEST, $_POST or $_GET + * some kind of custom fields (checkbox, list, multiple list) + * when has been deselected by user. + * + * [$hash_type]: NEED TO BE COMMENTED + * + * + * rev: + */ + public function testplan_design_values_to_db($hash, $node_id, $link_id, + $cf_map = null, $hash_type = null) + { + if (is_null($hash) && is_null($cf_map)) { + return; + } + $debugMsg = $this->debugMsg . __FUNCTION__; + $cfield = is_null($hash_type) ? $this->_build_cfield($hash, $cf_map) : $hash; + if (! is_null($cfield)) { + foreach ($cfield as $field_id => $type_and_value) { + $value = $type_and_value['cf_value']; + + // do I need to update or insert this value? + $sql = "SELECT value FROM {$this->tables['cfield_testplan_design_values']} " . + " WHERE field_id={$field_id} AND link_id={$link_id}"; + + $result = $this->db->exec_query($sql); + + // max_length_value = 0 => no limit + if ($this->max_length_value > 0 && + tlStringLen($value) > $this->max_length_value) { + $value = substr($value, 0, $this->max_length_value); + } + + $safe_value = $this->db->prepare_string($value); + if ($this->db->num_rows($result) > 0 && $value != "") { + $sql = "UPDATE {$this->tables['cfield_testplan_design_values']} " . + " SET value='{$safe_value}' " . + " WHERE field_id={$field_id} AND link_id={$link_id}"; + $this->db->exec_query($sql); + } // BUGID 3989 + elseif ($this->db->num_rows($result) == 0 && $value != "") { + # Remark got from Mantis code: + # Always store the value, even if it's the dafault value + # This is important, as the definitions might change but the + # values stored with a bug must not change + $sql = "INSERT INTO {$this->tables['cfield_testplan_design_values']} " . + " ( field_id, link_id, value ) " . + " VALUES ( {$field_id}, {$link_id}, '{$safe_value}' )"; + $this->db->exec_query($sql); + // BUGID 3989 + } elseif ($this->db->num_rows($result) > 0 && $value == "") { + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_testplan_design_values']} " . + " WHERE field_id={$field_id} AND link_id={$link_id}"; + $this->db->exec_query($sql); + } + } + } + } + + /* + * function: get_linked_cfields_at_testplan_design + * returns information about custom fields that can be used + * at least at testplan design time (test case assignment), + * with the value assigned (is any has been assigned). + * + * + * $tproject_id: needed because is possible to associate/link + * a different set of custom field for every test project + * + * $enabled : 1 -> get custom fields that are has been configured + * to be shown during test case execution AND are enabled. + * + * [$node_type]: default: null + * verbose id ('testcase', 'testsuite', etc) of a node type. + * custom fields are linked also to different node types. + * Example: + * I can define a custom field "Aspect" with values + * Performace, Usability and wnat use it only for test suites. + * + * [$node_id]: default: null + * identification of a node/element on node hierarchy. + * Needed when I want to get the value of custom fields + * linked to a node. + * Example: + * Have two test cases (ID:9999, ID:89800), and want to get + * the value assigned to custom field "Operating System". + * I will do two calls to this method. + * + * IMPORTANT: + * Fot testplan_design Custom Field this will be a TCVERSION_ID, + * not a TESTCASE_ID + * + * + * [link_id]: points to testplan_tcversions.id field + * [testplan_id] + * + * + * returns: hash + * key: custom field id + * + * + * + */ + public function get_linked_cfields_at_testplan_design($tproject_id, $enabled, + $node_type = null, $node_id = null, $link_id = null, $testplan_id = null, + $access_key = 'id') + { + $additional_join = ""; + $additional_values = ""; + + $order_by_clause = " ORDER BY display_order,CF.id "; + $fetchMethod = 'fetchRowsIntoMap'; + + if (! is_null($node_type)) { + $hash_descr_id = $this->tree_manager->get_available_node_types(); + $node_type_id = $hash_descr_id[$node_type]; + + $additional_join .= " JOIN {$this->tables['cfield_node_types']} CFNT ON CFNT.field_id=CF.id " . + " AND CFNT.node_type_id={$node_type_id} "; + } + + if (is_null($link_id) && ! is_null($testplan_id)) { + $additional_values .= ",CFTDV.value AS value, CFTDV.link_id AS node_id, " . + "NHB.id AS tcase_id, NHB.name AS tcase_name, " . + "TCV.tc_external_id "; + + $additional_join .= "JOIN {$this->tables['testplan_tcversions']} TPTC" . + " ON TPTC.testplan_id = {$testplan_id}" . + " JOIN {$this->tables['cfield_testplan_design_values']} CFTDV " . + " ON CFTDV.field_id=CF.id " . " AND CFTDV.link_id = TPTC.id "; + + $additional_join .= " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTC.tcversion_id " . + " AND TCV.id = TPTC.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHA ON NHA.id = TPTC.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHB.id = NHA.parent_id "; + + $order_by_clause = " ORDER BY node_id,display_order,CF.id "; + $fetchMethod = 'fetchArrayRowsIntoMap'; + $access_key = 'node_id'; + } elseif (! is_null($link_id)) { + $additional_values .= ",CFTDV.value AS value, CFTDV.link_id AS node_id"; + $additional_join .= " LEFT OUTER JOIN {$this->tables['cfield_testplan_design_values']} CFTDV " . + " ON CFTDV.field_id=CF.id " . " AND CFTDV.link_id={$link_id} "; + } + + $sql = "SELECT CF.*,CFTP.display_order,CFTP.required" . + $additional_values . " FROM {$this->tables['custom_fields']} CF " . + " JOIN {$this->tables['cfield_testprojects']} CFTP ON CFTP.field_id=CF.id " . + $additional_join . " WHERE CFTP.testproject_id={$tproject_id} " . + " AND CFTP.active=1 " . + " AND CF.enable_on_testplan_design={$enabled} " . $order_by_clause; + + return $this->db->$fetchMethod($sql, $access_key); + } + + /* + * function: string_input_radio + * returns an string with the html needed to display radio custom field. + * Is normally called by string_custom_field_input() + * + * args: p_field_def: contains the definition of the custom field + * (including it's field id) + * + * p_input_name: html input name + * + * p_custom_field_value: html input value + * htmlspecialchars() must be applied to this + * argument by caller. + * + * returns: html string + * + * rev: 20080816 - franciscom + * based on Mantis 1.2.0a1 code + * + */ + private function string_input_radio($p_field_def, $p_input_name, + $p_custom_field_value, $opt = null) + { + $options = array( + 'remove_required' => false + ); + $options = array_merge($options, (array) $opt); + + $str_out = ''; + $t_values = explode('|', $p_field_def['possible_values']); + $t_checked_values = explode('|', $p_custom_field_value); + + if ($options['remove_required']) { + $required = ' class="" '; + } else { + $required = $p_field_def['required'] ? ' class="required" required ' : ' class="" '; + } + + foreach ($t_values as $t_option) { + $str_out .= ' ' . + $t_option . '  '; + } else { + $str_out .= ' value="' . $t_option . '"> ' . $t_option . + '  '; + } + } + return $str_out; + } + + /* + * function: build_cfield_radio + * support function useful for method used to write radio CF values to db. + * Is normally called by _build_cfield() + * + * args: custom_field_value: value to be converted to be written to db. + * + * returns: value converted + * + * rev: 20080816 - franciscom + * + */ + private function build_cfield_radio($custom_field_value) + { + if (count($custom_field_value) > 1) { + $value = implode('|', $custom_field_value); + } else { + $value = is_array($custom_field_value) ? $custom_field_value[0] : $custom_field_value; + } + return $value; + } + + /* + * function: string_input_string + * returns an string with the html needed to display custom field of type: + * string, email, numeric, float + * + * Is normally called by string_custom_field_input() + * + * args: p_field_def: contains the definition of the custom field + * (including it's field id) + * + * p_input_name: html input name + * + * p_custom_field_value: html input value + * htmlspecialchars() must be applied to this + * argument by caller. + * + * p_size: html input size + * + * returns: html string + * + * + */ + private function string_input_string($p_field_def, $p_input_name, + $p_custom_field_value, $p_size, $opt = null) + { + $options = array( + 'remove_required' => false + ); + $options = array_merge($options, (array) $opt); + if ($options['remove_required']) { + $required = ' class="" '; + } else { + $required = $p_field_def['required'] ? ' class="required" required ' : ' class="" '; + } + + $str_out = ''; + $size = intval($p_size) > 0 ? $p_size : self::DEFAULT_INPUT_SIZE; + $str_out .= "'; + return $str_out; + } + + /** + * exportValueAsXML + * generate XML with custom field name, and custom field value + * useful on export to XML method for items that can have custom fields, + * example: test cases, test suites, req specification, etc. + * + * @param array $cfMap: + * key: custom file ID, value: map with at least keys 'name', 'value' + * + */ + public function exportValueAsXML($cfMap) + { + $cfRootElem = "\n{{XMLCODE}}\t\t\n"; + $cfElemTemplate = "\t\t\t" . + "\n\t\t\t\n\t\t\t" . + "\n" . "\t\t\t" . + "\n"; + $cfDecode = array( + "||NAME||" => "name", + "||VALUE||" => "value" + ); + return exportDataToXML($cfMap, $cfRootElem, $cfElemTemplate, $cfDecode, + true); + } + + /** + * remove_all_scopes_values + * For a given custom field id remove all assigned values in any scope + * + * @param int $id: + * custom field id + * + */ + private function remove_all_scopes_values($id) + { + // some sort of blind delete + $tables = array( + 'cfield_design_values', + 'cfield_build_design_values', + 'cfield_execution_values', + 'cfield_testplan_design_values' + ); + $safe_id = intval($id); + foreach ($tables as $tt) { + $sql = "DELETE FROM {$this->tables[$tt]} WHERE field_id={$safe_id} "; + $this->db->exec_query($sql); + } + } + + /** + * get_linked_testprojects + * For a given custom field id return all test projects where is linked. + * + * @param int $id: + * custom field id + * @return array + */ + public function get_linked_testprojects($id) + { + $sql = " SELECT NH.id, NH.name " . + " FROM {$this->tables['cfield_testprojects']} CFTP, {$this->tables['nodes_hierarchy']} NH " . + " WHERE CFTP.testproject_id=NH.id " . + " AND CFTP.field_id = {$id} ORDER BY NH.name "; + + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + * + * @param + * string node type in verbose form. Example 'testcase' + * + * returns map with key: verbose location (see custom field class $locations) + * value: array with fixed key 'location' + * value: location code + * + */ + public function buildLocationMap($nodeType) + { + $locationMap = null; + $dummy = $this->getLocations(); + $verboseLocationCode = array_flip($dummy[$nodeType]); + if (! empty($verboseLocationCode)) { + foreach ($verboseLocationCode as $key => $value) { + $locationMap[$key]['location'] = $value; + } + } + return $locationMap; + } + + /** + * + * @param + * int linkID: how is used depends on $options['scope'] + * $options['scope']=design => node_id + * $options['scope']=testplan_design => feature_id (see testplan_tcversions table) + * $options['scope']=execution => execution_id + * + * @used by testsuite.class.php => copy_cfields_values + */ + public function getByLinkID($linkID, $options = null) + { + $my['options'] = array( + 'scope' => 'design', + 'output' => 'field_id' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + switch ($my['options']['output']) { + case 'field_id': + $sql = "/* debugMsg */ SELECT field_id FROM "; + break; + + case 'full': + $sql = "/* debugMsg */ SELECT * FROM "; + break; + } + + switch ($my['options']['scope']) { + case 'design': + $sql .= " {$this->tables['cfield_design_values']} " . + " WHERE node_id = {$linkID} "; + break; + + case 'testplan_design': + $sql .= " {$this->tables['cfield_testplan_design_values']} " . + " WHERE feature_id = {$linkID} "; + break; + + case 'execution': + $sql .= " {$this->tables['cfield_execution_values']} " . + " WHERE execution_id = {$linkID} "; + break; + } + return $this->db->get_recordset($sql); + } + + /** + * buildHTMLInputName + */ + private function buildHTMLInputName($cf, $name_suffix) + { + return "{$this->name_prefix}{$cf['type']}_{$cf['id']}{$name_suffix}"; + } + + /** + */ + public function html_table_inputs($cfields_map, $name_suffix = '', + $input_values = null, $opt = null) + { + $cf_smarty = ''; + $getOpt = array( + 'name_suffix' => $name_suffix + ); + + $my['opt'] = array( + 'addCheck' => false, + 'addTable' => true, + 'forceOptional' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + if (! is_null($cfields_map)) { + $lbl_upd = lang_get('update_hint'); + $cf_map = $this->getValuesFromUserInput($cfields_map, $name_suffix, + $input_values); + + $NO_WARNING_IF_MISSING = true; + $openTag = $my['opt']['addTable'] ? "" : ''; + $closeTag = $my['opt']['addTable'] ? "
    " : ''; + + $add_img = "'; + + $cf_smarty = ''; + foreach ($cf_map as $cf_info) { + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, $NO_WARNING_IF_MISSING)); + + // IMPORTANT NOTICE + // assigning an ID with this format is CRITIC to Javascript logic used + // to validate input data filled by user according to CF type + // extract input html id + // Want to give an html id to
    used as labelHolder, to use it in Javascript + // logic to validate CF content + if ($my['opt']['forceOptional']) { + $cf_info['required'] = 0; + } + + $cf_html_string = $this->string_custom_field_input($cf_info, + $getOpt); + + $dummy = explode(' ', + strstr($cf_html_string, 'id="custom_field_')); + $td_label_id = str_replace('id="', 'id="label_', $dummy[0]); + + $cf_smarty .= "
    {$add_img}" . + " " . + htmlspecialchars($label) . ":" . + $this->string_custom_field_input($cf_info, $getOpt) . + "
    used as labelHolder, to use it in Javascript + // logic to validate CF content + $cf_html_string = $this->string_custom_field_input($cf_info, + $getOpt); + + $dummy = explode(' ', + strstr($cf_html_string, 'id="custom_field_')); + $label_id = str_replace('id="', 'id="label_', $dummy[0]); + + $inputSet[] = array( + 'label' => htmlspecialchars($label), + 'label_id' => $label_id, + 'input' => $this->string_custom_field_input($cf_info, + $getOpt) + ); + } + } + return $inputSet; + } + + /** + */ + public function getByIDAndEnableOn($id, $enableOn = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT CF.*, CFNT.node_type_id" . + " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_node_types']} CFNT" . + " WHERE CF.id=CFNT.field_id " . " AND CF.id IN (" . + implode(',', (array) $id) . ")"; + + if (! is_null($enableOn) && is_array($enableOn)) { + foreach ($this->application_areas as $key) { + if (isset($enableOn[$key]) && $enableOn[$key]) { + $sql .= " AND CF.enable_on_{$key}=1 "; + } + } + } + + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + */ + public function setMonitorable($tproject_id, $cfieldSet, $val) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + if (is_null($cfieldSet)) { + return; + } + + $safe = new stdClass(); + $safe->tproject_id = intval($tproject_id); + $safe->val = (intval($val) > 0) ? 1 : 0; + + $field = 'monitorable'; + + $info = $this->tree_manager->get_node_hierarchy_info($safe->tproject_id); + $auditMsg = $val ? "audit_cfield_{$field}_on" : "audit_cfield_{$field}_off"; + foreach ($cfieldSet as $field_id) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['cfield_testprojects']} " . + " SET {$field}=" . $safe->val . " WHERE testproject_id=" . + $safe->tproject_id . " AND field_id=" . + $this->db->prepare_int($field_id); + + if ($this->db->exec_query($sql)) { + $cf = $this->get_by_id($field_id); + if ($cf) { + logAuditEvent( + TLS($auditMsg, $cf[$field_id]['name'], $info['name']), + "SAVE", $safe->tproject_id, "testprojects"); + } + } + } + } + + /** + */ + public function cfdate2mktime($value) + { + if (($value == 0) || ($value == '')) { + return ''; + } else { + $localesDateFormat = config_get('locales_date_format'); + $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; + $date_format = str_replace('%', '', $localesDateFormat[$locale]); + + $pvalue = split_localized_date($value, $date_format); + if ($pvalue != null) { + $pvalue = mktime(0, 0, 0, $pvalue['month'], $pvalue['day'], + $pvalue['year']); + return $pvalue; + } else { + return ''; + } + } + } + + /** + */ + public function getBooleanAttributes($tproject_id, $cfSet = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $sql = " /* $debugMsg */ " . + " SELECT field_id,active,required,monitorable " . + " FROM {$this->tables['cfield_testprojects']} CFTP " . + " WHERE testproject_id =" . intval($tproject_id); + + if (! is_null($cfSet)) { + $sql .= " AND field_id IN(" . implode(',', $cfSet) . ")"; + } + + return $this->db->fetchRowsIntoMap($sql, 'field_id'); + } + + /** + * + * @return stdClass + */ + public function initViewGUI() + { + $gogo = new stdClass(); + + $gogo->cfield = null; + $gogo->cfield_is_used = 0; + $gogo->cfield_is_linked = 0; + $gogo->linked_tprojects = null; + + $gogo->cf_map = $this->get_all(null, 'transform'); + $gogo->cf_types = $gogo->cfield_types = $this->get_available_types(); + + // MAGIC 10 + $gogo->drawControlsOnTop = (null != $gogo->cf_map && + count($gogo->cf_map) > 10); + + return $gogo; + } } - - -/** - * class is responsible for logic and store Custom Fields functionality - * @package TestLink - */ -class cfield_mgr extends tlObject -{ - const DEFAULT_INPUT_SIZE = 50; - const TEXTAREA_MAX_SIZE = 255; - - // EDIT HERE IF YOU CUSTOMIZE YOUR DB - /** for text area custom field 40 x 6 -> 240 chars <= 255 chars table field size */ - const TEXTAREA_DEFAULT_COLS = 70; - const TEXTAREA_DEFAULT_ROWS = 4; - - const CF_ENABLED = 1; - const ENABLED = 1; - const DISABLED = 0; - - /** @var resource the database handler */ - var $db; - - /** @var object tree class */ - var $tree_manager; - - /** - * @var array $application_areas - * Holds string keys used on this object and pages that manages CF, - * identifying in what areas/features something will be done - * 'execution' => mainly on test execution pages, - * identifies TL features/pages to record test results - * 'design' => test suites, test cases creation - * identifies TL features/pages to create test specification - * 'testplan_design' => link test cases to test plan (assign testcase option) - * - * IMPORTANT: this values are used as access keys in several properties of this object. - * then if you add one here, remember to update other properties. - */ - var $application_areas = array('execution','design','testplan_design'); - - /** - * @var array Define type of custom fields managed. - * Values will be displayed in "Custom Field Type" combo box when - * users create custom fields. No localization is applied - * - * - * Added specific type for test automation related custom fields. - * Start at code 500 - */ - var $custom_field_types = array(0=>'string', - 1=>'numeric', - 2=>'float', - 4=>'email', - 5=>'checkbox', - 6=>'list', - 7=>'multiselection list', - 8=>'date', - 9=>'radio', - 10=>'datetime', - 20=>'text area', - 500=>'script', - 501=>'server'); - - /** - * @var array Configures for what type of CF "POSSIBLE_VALUES" field need to be manage at GUI level - * Keys of this map must be the values present in: - * $this->custom_field_types - */ - var $possible_values_cfg = array('string' => 0, - 'numeric'=> 0, - 'float'=> 0, - 'email'=> 0, - 'checkbox' => 1, - 'list' => 1, - 'multiselection list' => 1, - 'date' => 0, - 'radio' => 1, - 'datetime' =>0, - 'text area' => 0, - 'script'=> 0, - 'server' => 0); - - /** @var array only the types listed here can have custom fields */ - var $node_types = array('build','testsuite','testplan','testcase','requirement_spec','requirement'); - - /** - * @var map of maps $locations - * - * Location is place on page where to display custom field. - * This concept has been created to implement a user contribution, that allows for - * test cases, display custom fields in a different location (standard location is after - * all test case definition), to implemente Prerequisites using CF. - * - * First map key: node type: 'testcase','testsuite', etc. - * Each element will be a map with following structure: - * key:Holds string keys used on this object and pages that manages CF. - * current options: 1 -> standard location, i.e. work as done before this implementation. - * 2 -> before steps and results, => between summary and steps/results. - * - * value: used to get translated label to use on User Interface. - * - * IMPORTANT: if you add a new key, this values are used as access keys in several properties of this object. - * then if you add one here, remember to update other properties. - */ - var $locations = array( 'testcase' => - array( 1 => 'standard_location', 2 => 'before_steps_results')); - - // changes in configuration - // - // Needed to manage user interface, when creating Custom Fields. - // When user choose a item type (test case, etc), a javascript logic - // uses this information to hide/show enable_on, and show_on combos. - // - // 0 => combo will not displayed - // - // May be need a review, because after the changes, seems a little bit silly. - var $enable_on_cfg = array('execution' => array('build' => 0, 'testsuite' => 0, - 'testplan' => 0,'testcase' => 1, - 'requirement_spec' => 0,'requirement' => 0), - 'design' => array('build' => 0,'testsuite' => 0, - 'testplan' => 0,'testcase' => 1, - 'requirement_spec' => 0,'requirement' => 0), - 'testplan_design' => array('build' => 0,'testsuite' => 0, - 'testplan' => 0,'testcase' => 1, - 'requirement_spec' => 0,'requirement' => 0)); - - // 0 => combo will not displayed - var $show_on_cfg=array('execution'=>array('testsuite' => 1, - 'testplan' => 1, - 'testcase' => 1, - 'build' => 1, - 'requirement_spec' => 0, - 'requirement' => 0 ), - 'design' => array('testsuite' => 1, - 'testplan' => 1, - 'testcase' => 1, - 'build' => 0, - 'requirement_spec' => 0, - 'requirement' => 0 ), - 'testplan_design' => array('testsuite' => 1, - 'testplan' => 1, - 'testcase' => 1, - 'build' => 0, - 'requirement_spec' => 0, - 'requirement' => 0 ) - ); - - // the name of html input will have the following format - // __ - // - var $name_prefix='custom_field_'; - var $sizes = null; - - // must be equal to the lenght of: - // value column on cfield_*_values tables - // default_value column on custom_fields table - // 0 -> no limit - // Is used on text area types - var $max_length_value; - - // must be equal to the lenght of: - // possible_values column on custom_fields table - // 0 -> no limit - var $max_length_possible_values; - - var $decode; - var $html_date_input_suffix = array('input' => true,'hour' => true, - 'minute' => true,'second' => true); - - /** - * Class constructor - * - * @param resource &$db reference to the database handler - */ - function __construct(&$db) - { - parent::__construct(); - - $this->db = &$db; - $this->tree_manager = new tree($this->db); - - $cfConfig = config_get('custom_fields'); - $this->sizes = $cfConfig->sizes; - - - - if( property_exists($cfConfig,'types') && !is_null($cfConfig->types) ) - { - $this->custom_field_types +=$cfConfig->types; - ksort($this->custom_field_types); - } - - if( property_exists($cfConfig,'possible_values_cfg') && - !is_null($cfConfig->possible_values_cfg) ) - { - $this->possible_values_cfg +=$cfConfig->possible_values_cfg; - } - $this->object_table=$this->tables["custom_fields"]; - - $this->max_length_value = $cfConfig->max_length; - $this->max_length_possible_values = $this->max_length_value; - - $this->decode['nodes'] = $this->tree_manager->get_available_node_types(); - - - } - - - function getSizeLimit() - { - return $this->max_length_value; - } - - - function get_application_areas() - { - return($this->application_areas); - } - - /** - * @return hash with available locatipons - * - * - */ - function getLocations() - { - return($this->locations); - } - - - /** - * @return hash with custom field available types - * key: numeric id - * value: short description - */ - function get_available_types() - { - return($this->custom_field_types); - } - - /** - * @return string - */ - function get_name_prefix() { - return $this->name_prefix ; - } - - /** - * @return hash with node types id, that can have custom fields. - * key: short description (node_types.description) - * value: node_type_id (node_types.id) - */ - function get_allowed_nodes() - { - $allowed_nodes = array(); - foreach($this->node_types as $verbose_type ) - { - $allowed_nodes[$verbose_type] = $this->decode['nodes'][$verbose_type]; - } - return $allowed_nodes; - } - - /** - * @return hash with node types id, that can have custom fields with enabled_on_$ui_mode. - * key : node_type_id (node_types.id) - * value: 1 -> enable on exec can be configured by user - */ - function get_enable_on_cfg($ui_mode) - { - $mgmt_cfg=array(); - $mgmt_cfg=$this->_get_ui_mgtm_cfg_for_node_type($this->enable_on_cfg[$ui_mode]); - return($mgmt_cfg); - } - - - function get_show_on_cfg($ui_mode) - { - $mgmt_cfg=array(); - $mgmt_cfg=$this->_get_ui_mgtm_cfg_for_node_type($this->show_on_cfg[$ui_mode]); - return($mgmt_cfg); - } - - - /* - function: _get_ui_mgtm_cfg_for_node_type - utility method - - returns: hash with node types id. - key : node_type_id (node_types.id) - value: 1 -> enable on exec can be configured by user - - - */ - function _get_ui_mgtm_cfg_for_node_type($map_node_id_cfg) - { - $enabled_mgmt = array(); - $tl_node_types = $this->decode['nodes']; - foreach($this->node_types as $verbose_type) - { - $type_id = $tl_node_types[$verbose_type]; - if( isset($map_node_id_cfg[$verbose_type]) ) - { - $enabled_mgmt[$type_id]=$map_node_id_cfg[$verbose_type]; - } - } - return $enabled_mgmt; - } - - - - /* - function: get_possible_values_cfg - - returns: hash - key : cf_type_id (see $custom_field_types) - value: 1 -> possible values can be managed on UI. - - - */ - function get_possible_values_cfg() - { - $pv_cfg=array(); - $custom_field_types_id=array_flip($this->custom_field_types); - - foreach($this->possible_values_cfg as $verbose_cf_type => $use_on_ui) - { - $cf_type_id=$custom_field_types_id[$verbose_cf_type]; - $pv_cfg[$cf_type_id]=$use_on_ui; - } - return($pv_cfg); - } - - /** - * - */ - function getLinkedCfieldsAtDesign($context,$filters=null,$access_key='id') { - - // $context - $ctx = array('tproject_id' => null, 'enabled' => true, 'node_type' => null, - 'node_id' => null); - $ctx = array_merge($ctx,$context); - if( null == $ctx['tproject_id'] ) { - throw new Exception(__METHOD__ . ' EXCEPTION: test project ID, is mandatory'); - } - - extract($ctx); - - return $this->get_linked_cfields_at_design($tproject_id,$enabled,$filters, - $node_type,$node_id,$access_key); - - } - - - - - /* - function: get_linked_cfields_at_design - returns information about custom fields that can be used - at least at design time, with the value assigned (is any has been assigned). - - - $tproject_id: needed because is possible to associate/link - a different set of custom field for every test project - - $enabled : 1 -> get custom fields that are has been configured - to be shown during specification design AND are enabled. - - Remember that also exist custom fields - that can be only used during TEST CASE EXECUTION. - - [$filters]:default: null - map with keys: - [show_on_execution]: 1 -> filter on field show_on_execution=1 - 0 or null or not exists -> don't filter - - [show_on_testplan_design]: 1 -> filter on field show_on_execution=1 - 0 or null or not exists -> don't filter - - [cfield_id]: if exists use it's value to filter on custom field id - null or not exists -> don't filter - - [location]: new concept used to define on what location on screen - custom field will be designed. - Initally used with CF available for Test cases, to - implement pre-requisites. - null => no filtering - - [$node_type]: default: null - verbose id ('testcase', 'testsuite', etc) of a node type. - custom fields are linked also to different node types. - Example: - I can define a custom field "Aspect" with values - Performace, Usability and wnat use it only for test suites. - - [$node_id]: default: null - identification of a node/element on node hierarchy. - Needed when I want to get the value of custom fields - linked to a node. - Example: - Have two test cases (ID:9999, ID:89800), and want to get - the value assigned to custom field "Operating System". - I will do two calls to this method. - - [$access_key]: default id, field name to use as access key in returned hash - - returns: hash - key: custom field id - - - rev : - - */ - function get_linked_cfields_at_design($tproject_id,$enabled,$filters=null, - $node_type=null,$node_id=null, - $access_key='id') - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $additional_join=""; - $additional_values=""; - $additional_filter=""; - - switch ($access_key) { - case 'id': - case 'node_id': - break; - - default: - $access_key = 'id'; - break; - } - - switch($node_type) { - case 'build': - $table_key = 'cfield_build_design_values'; - break; - - default: - $table_key = 'cfield_design_values'; - break; - } - - if( !is_null($node_type) ) { - $additional_join .= - " JOIN {$this->tables['cfield_node_types']} CFNT - ON CFNT.field_id=CF.id AND CFNT.node_type_id=" . - $this->db->prepare_int($this->decode['nodes'][$node_type]); - } - - $targetIsArray = false; - if( !is_null($node_id) ) { - $additional_values .= ",CFDV.value AS value,CFDV.node_id AS node_id"; - - if( is_array($node_id) ) { - $targetIsArray = true; - $sane = array_map('intval', $node_id); - $inClause = implode(',',$sane); - } else { - $inClause = $this->db->prepare_int($node_id); - } - - $additional_join .= - " LEFT OUTER JOIN {$this->tables[$table_key]} CFDV ON CFDV.field_id=CF.id - AND CFDV.node_id IN ($inClause) "; - } - - if (!is_null($filters)) { - if (isset($filters['show_on_execution']) && - !is_null($filters['show_on_execution']) ) { - $additional_filter .= " AND CF.show_on_execution=1 "; - } - - // Probably this piece need to be changed to act on enable_on_ attribute - // due to CF display logic refactoring - // if( isset($filters['show_on_testplan_design']) && !is_null($filters['show_on_testplan_design']) ) - // { - // $additional_filter .= " AND CF.show_on_testplan_design=1 "; - // } - if (isset($filters['show_on_testplan_design']) && - !is_null($filters['show_on_testplan_design'])) { - $additional_filter .= " AND CF.enable_on_testplan_design=1 "; - } - - if( isset($filters['cfield_id']) && !is_null($filters['cfield_id']) ) - { - $additional_filter .= " AND CF.id = {$filters['cfield_id']} "; - } - - $filterKey='location'; - if( isset($filters[$filterKey]) && !is_null($filters[$filterKey]) ) { - $additional_filter .= " AND CFTP.$filterKey={$filters[$filterKey]} "; - } - } - - $sql="/* $debugMsg */ SELECT CF.*,CFTP.display_order,CFTP.location,CFTP.required " . - $additional_values . - " FROM {$this->object_table} CF " . - " JOIN {$this->tables['cfield_testprojects']} CFTP ON CFTP.field_id=CF.id " . - $additional_join . - " WHERE CFTP.testproject_id=" . intval($tproject_id) . - " AND CFTP.active=1 AND CF.show_on_design=1 " . - " AND CF.enable_on_design={$enabled} " . - $additional_filter . - " ORDER BY display_order,CF.id "; - - if ( $targetIsArray ) { - // # 0008792: Tl 1.9.20 (dev) >> Requirement overview >> Custom field content displayed in wrong column - // - // $map = $this->db->fetchArrayRowsIntoMap($sql,$access_key); - $map = $this->db->fetchMapRowsIntoMap($sql,$access_key,'id'); - } else { - $map = $this->db->fetchRowsIntoMap($sql,$access_key); - } - - return $map; - } - - - /* - ==================================================================== - ** Very Imporant ** - This code is based on Mantis code. - Initial development was based on 1.x.x versions. - file:custom_field_api.php - function:print_custom_field_input() - - 20080815: some changes are done to add more flexibility, and idea - was compared with 1.2.0a1 Mantis implementation. - ==================================================================== - - function: string_custom_field_input - returns an string with the html needed to display the custom field. - - If no specific code is found to manage a custom field type, - it will be used code that manage string type. - - args: $p_field_def: contains the definition of the custom field - (including it's field id) - - [$name_suffix]: if used must start with _. - example _TCID017 - - returns: html string - - rev : - */ - function string_custom_field_input($p_field_def,$opt = null) - { - $options = array('name_suffix' => '', 'field_size' => 0, 'show_on_filters' => false, 'remove_required' => false); - $options = array_merge($options,(array)$opt); - extract($options); - - $str_out=''; - - $cfValue = $p_field_def['default_value']; - if( isset($p_field_def['value']) ) - { - $cfValue = $p_field_def['value']; - } - - $verbose_type=trim($this->custom_field_types[$p_field_def['type']]); - $cfValue = htmlspecialchars($cfValue); - $input_name = $this->buildHTMLInputName($p_field_def,$name_suffix); - $size = isset($this->sizes[$verbose_type]) ? intval($this->sizes[$verbose_type]) : 0; - - if($options['remove_required']) - { - $required = ' class="" '; - } - else - { - $required = $p_field_def['required'] ? ' class="required" required ' : ' class="" '; - } - - $dateOpt = array('default_disable' => false, 'allow_blank' => true, 'required' => $required, - 'show_on_filters' => $show_on_filters); - - if( $field_size > 0) - { - $size=$field_size; - } - - switch ($verbose_type) - { - case 'list': - case 'multiselection list': - $t_values = explode( '|', $p_field_def['possible_values']); - $t_values_count = count($t_values); - $window_size = intval($size); - if($t_values_count < $window_size) - { - $window_size = $t_values_count; - } - - if( $verbose_type == 'list' ) - { - // get maximum allowed window size for lists - // $window_size = intval($size) > 1 ? $size : self::LISTBOX_WINDOW_SIZE; - $t_multiple=' '; - $t_name_suffix=''; - } - else - { - $t_name_suffix='[]'; - $t_multiple=' multiple="multiple" '; - } - - // set the list size to the number of possible values of custom field - // but respect the maximum window size - $t_list_size = $t_values_count; - if($t_list_size > $window_size) - { - $t_list_size=$window_size; - } - - $html_identity=$input_name . $t_name_suffix; - $str_out .= ''; - break; - - case 'checkbox': - $t_values = explode( '|', $p_field_def['possible_values']); - $t_checked_values = explode( '|', $cfValue); - foreach( $t_values as $t_option ) - { - $str_out .= ' ' . $t_option . '  '; - } - else - { - $str_out .= ' value="' . $t_option . '"> ' . $t_option . '  '; - } - - } - break; - - case 'string': - case 'email': - case 'float': - case 'numeric': - $str_out .= $this->string_input_string($p_field_def,$input_name,$cfValue,$size,$options); - break ; - - case 'text area': - $cols = intval($this->sizes['text area']['cols']); - $rows = intval($this->sizes['text area']['rows']); - if($cols <= 0) - { - $cols = self::TEXTAREA_DEFAULT_COLS; - } - if($rows <= 0) - { - $rows = self::TEXTAREA_DEFAULT_ROWS; - } - - if( $this->max_length_value > 0 ) - { - $counterId = $input_name . '_counter'; - $cf_current_size = $this->max_length_value - tlStringLen($cfValue); - - // call JS function for check max. size from validate.js - $js_function = '"textCounter(this.form.' . $input_name . - ',document.getElementById(\''. $counterId.'\'),' . $this->max_length_value .');" '; - - $str_out .= '\n"; - - // show character counter - $str_out .= '
    ' . - sprintf(lang_get('text_counter_feedback'), $this->max_length_value) . - ' '.$cf_current_size.'.
    '; - } - else - { - // unlimited - $str_out .= '\n"; - - } - break; - - case 'date': - $str_out .= create_date_selection_set($input_name,config_get('date_format'),$cfValue,$dateOpt); - break; - - case 'datetime': - $cfg=config_get('gui'); - - // Important - // We can do this mix (get date format configuration from standard variable - // and time format from an specific custom field config) because string used - // for date_format on strftime() has no problem - // on date() calls (that are used in create_date_selection_set() ). - $format = config_get('date_format') . " " . $cfg->custom_fields->time_format; - $str_out .=create_date_selection_set($input_name,$format,$cfValue,$dateOpt); - break; - - - default: - $dynamic_call='string_input_' . str_replace(' ', '_', $verbose_type); - if( function_exists($dynamic_call) ) - { - $str_out .= $dynamic_call($p_field_def, $input_name, $cfValue); - } - else if( method_exists($this, $dynamic_call) ) - { - $str_out .= $this->$dynamic_call($p_field_def, $input_name, $cfValue); - } - else - { - // treat it as an simple string - $str_out .= $this->string_input_string($p_field_def,$input_name,$cfValue,$size,$options); - } - break; - - - } - return ($str_out); - } //function end - - - /* - function: design_values_to_db - write values of custom fields that are used at design time. - - args: $hash: contains info about CF gathered at user interface. - (normally $_REQUEST variable) - key: custom_field__. - Example custom_field_0_67 -> 0=> string field - - $node_id: - [$cf_map]: hash -> all the custom fields linked and enabled - that are applicable to the node type of $node_id. - - For the keys not present in $hash, we will write - an appropriate value according to custom field - type. - - This is needed because when trying to udpate - with hash being $_REQUEST, $_POST or $_GET - some kind of custom fields (checkbox, list, multiple list) - when has been deselected by user. - - - rev: - */ - function design_values_to_db($hash,$node_id,$cf_map=null,$hash_type=null,$node_type=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - if( is_null($hash) && is_null($cf_map) ) { - return; - } - if( is_null($hash_type) ) { - $cfield=$this->_build_cfield($hash,$cf_map); - } - else { - $cfield=$hash; - } - - if( !is_null($cfield) ) { - switch($node_type) { - case 'build': - $table_key = 'cfield_build_design_values'; - break; - - default: - $table_key = 'cfield_design_values'; - break; - } - - $safeNodeID = intval($node_id); - foreach($cfield as $field_id => $type_and_value) { - $value = $type_and_value['cf_value']; - - // do I need to update or insert this value? - $sql = "/* $debugMsg */ SELECT value FROM {$this->tables[$table_key]} " . - " WHERE field_id=" . intval($field_id) . " AND node_id=" . $safeNodeID; - - $result = $this->db->exec_query($sql); - - // max_length_value = 0 => no limit - if( $this->max_length_value > 0 && tlStringLen($value) > $this->max_length_value) { - $value = substr($value,0,$this->max_length_value); - } - - $safe_value = $this->db->prepare_string($value); - $rowCount = $this->db->num_rows($result); - if( $rowCount > 0 ) { - if( $value != "" ) { - $sql = "/* $debugMsg */ UPDATE {$this->tables[$table_key]} " . - " SET value='{$safe_value}' "; - } - else { - // bye, bye record - $sql = "/* $debugMsg */ DELETE FROM {$this->tables[$table_key]} "; - } - $sql .= " WHERE field_id=" . intval($field_id) . " AND node_id=" . $safeNodeID; - $this->db->exec_query($sql); - } - else if ($rowCount == 0 && $value != "") { - # Remark got from Mantis code: - # Always store the value, even if it's the dafault value - # This is important, as the definitions might change but the - # values stored with a bug must not change - $sql = "/* $debugMsg */ INSERT INTO {$this->tables[$table_key]} " . - " ( field_id, node_id, value ) " . - " VALUES ( " . intval($field_id) . ", {$safeNodeID}, '{$safe_value}' )"; - $this->db->exec_query($sql); - } - } //foreach($cfield - } //if( !is_null($cfield) ) - } //function end - - - - /* - function: remove_all_design_values_from_node - remove the values of ALL custom fields linked to - a node. (example test case 5555) - - args: $node_id: single value or array - - returns: - - - rev : - 20070102 - franciscom - $node_id can be an array - - */ - function remove_all_design_values_from_node($node_id,$node_type=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - switch($node_type) - { - case 'build': - $table_key = 'cfield_build_design_values'; - break; - - default: - $table_key = 'cfield_design_values'; - break; - } - - $sql = "/* $debugMsg */ DELETE FROM {$this->tables[$table_key]} "; - if( is_array($node_id) ) - { - $sql .= " WHERE node_id IN(" . implode(",",$node_id) . ") "; - } - else - { - $sql .= " WHERE node_id=" . intval($node_id); - } - - $this->db->exec_query($sql); - } - - - /* - function: get_all - get the definition of all custom field defined in the system, - or all custom fields with id not included in $id2exclude. - - args: [$id2exclude]: array with custom field ids - - returns: hash: - key: custom field id - - */ - function get_all($id2exclude=null,$opt=null) - { - static $lbl; - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(!$lbl) - { - $lbl = init_labels(array('context_design' => null,'context_exec' => null, - 'context_testplan_design' => null)); - } - - $not_in_clause=""; - if( !is_null($id2exclude) ) - { - $not_in_clause=" AND CF.id NOT IN (" .implode(',',$id2exclude) .") "; - } - $sql="SELECT CF.*,NT.description AS node_description,NT.id AS node_type_id " . - " FROM {$this->object_table} CF, " . - " {$this->tables['cfield_node_types']} CFNT, " . - " {$this->tables['node_types']} NT " . - " WHERE CF.id=CFNT.field_id " . - " AND NT.id=CFNT.node_type_id " . - $not_in_clause . - " ORDER BY CF.name"; - - $map = $this->db->fetchRowsIntoMap($sql,'id'); - if(!is_null($map) && !is_null($opt)) - { - $k2l = array_keys($map); - foreach($k2l as $key) - { - $map[$key]['enabled_on_context'] = ''; - if($map[$key]['enable_on_design']) - { - $map[$key]['enabled_on_context'] = $lbl['context_design']; - } - else if($map[$key]['enable_on_execution']) - { - $map[$key]['enabled_on_context'] = $lbl['context_exec']; - } - else if($map[$key]['enable_on_testplan_design']) - { - $map[$key]['enabled_on_context'] = $lbl['context_testplan_design']; - } - } - } - - return $map; - } - - /* - function: get_linked_to_testproject - get definition of all custom fields linked to a test project. - - - args: $tproject_id - [$active]: if not null will add the following filter " AND CFTP.active={$active}" - - returns: hash: - key: custom field id - - internal revision: - */ - function get_linked_to_testproject($tproject_id,$active=null,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $options = array('name' => null); - $options = array_merge($options,(array)$opt); - - - $sql="SELECT CF.*,NT.description AS node_description,NT.id AS node_type_id, " . - " CFTP.display_order, CFTP.active, CFTP.location,CFTP.required,CFTP.monitorable " . - " FROM {$this->object_table} CF, " . - " {$this->tables['cfield_testprojects']} CFTP, " . - " {$this->tables['cfield_node_types']} CFNT, " . - " {$this->tables['node_types']} NT " . - " WHERE CF.id=CFNT.field_id " . - " AND CF.id=CFTP.field_id " . - " AND NT.id=CFNT.node_type_id " . - " AND CFTP.testproject_id=" . $this->db->prepare_int($tproject_id); - - if( !is_null($active) ) - { - $sql .= " AND CFTP.active={$active} "; - } - - if( !is_null($options['name']) ) - { - $sql .= " AND CF.name='" . $this->db->prepare_string($options['name']) . "'"; - } - - $sql .= " ORDER BY NT.description,CF.enable_on_design desc, " . - " CF.enable_on_execution desc, " . - " CF.enable_on_testplan_design desc,". - " CFTP.display_order, CF.name"; - - $map = $this->db->fetchRowsIntoMap($sql,'id'); - return $map; - } - - - /* - function: link_to_testproject - - - - args: $tproject_id - $cfields_id: array() - - returns: - - */ - function link_to_testproject($tproject_id,$cfield_ids) - { - if(is_null($cfield_ids)) - { - return; - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safeID = intval($tproject_id); - $tproject_info = $this->tree_manager->get_node_hierarchy_info($safeID); - foreach($cfield_ids as $field_id) - { - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['cfield_testprojects']} " . - " (testproject_id,field_id) " . - " VALUES({$safeID},{$field_id})"; - - if ($this->db->exec_query($sql)) - { - $cf = $this->get_by_id($field_id); - if ($cf) - { - logAuditEvent(TLS("audit_cfield_assigned",$cf[$field_id]['name'],$tproject_info['name']), - "ASSIGN",$tproject_id,"testprojects"); - } - } - } - } - - - /* - function: set_active_for_testproject - set the value of active field - - - args: $tproject_id - $cfields_id: array() - $active_val: 1/0 - - returns: - - */ - function set_active_for_testproject($tproject_id,$cfield_ids,$active_val) - { - if(is_null($cfield_ids)) - { - return; - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $tproject_info = $this->tree_manager->get_node_hierarchy_info($tproject_id); - $auditMsg = $active_val ? "audit_cfield_activated" : "audit_cfield_deactivated"; - foreach($cfield_ids as $field_id) - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['cfield_testprojects']} " . - " SET active={$active_val} " . - " WHERE testproject_id=" . $this->db->prepare_int($tproject_id) . - " AND field_id=" . $this->db->prepare_int($field_id); - - if ($this->db->exec_query($sql)) - { - $cf = $this->get_by_id($field_id); - if ($cf) - { - logAuditEvent(TLS($auditMsg,$cf[$field_id]['name'],$tproject_info['name']), - "SAVE",$tproject_id,"testprojects"); - } - } - } - } //function end - - - /** - * - */ - function setRequired($tproject_id,$cfieldSet,$val) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($cfieldSet)) - { - return; - } - - $safe = new stdClass(); - $safe->tproject_id = intval($tproject_id); - $safe->val = (intval($val) > 0) ? 1 : 0; - - $info = $this->tree_manager->get_node_hierarchy_info($safe->tproject_id); - $auditMsg = $val ? "audit_cfield_required_on" : "audit_cfield_required_off"; - foreach($cfieldSet as $field_id) - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['cfield_testprojects']} " . - " SET required=" . $safe->val . - " WHERE testproject_id=" . $safe->tproject_id . - " AND field_id=" . $this->db->prepare_int($field_id); - - if ($this->db->exec_query($sql)) - { - $cf = $this->get_by_id($field_id); - if($cf) - { - logAuditEvent(TLS($auditMsg,$cf[$field_id]['name'],$info['name']), - "SAVE",$safe->tproject_id,"testprojects"); - } - } - } - } //function end - - - - /** - * unlink_from_testproject - * remove custom field links from target test project - * N.B.: following Mantis Bugtracking System model, - * this operation will NOR remove all values assigned to - * these custom fields . - * - * @param int $tproject_id - * @param array $cfield_ids - * - */ - function unlink_from_testproject($tproject_id,$cfield_ids) - { - if(is_null($cfield_ids)) - { - return; - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $tproject_info = $this->tree_manager->get_node_hierarchy_info($tproject_id); - foreach($cfield_ids as $field_id) - { - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_testprojects']} " . - " WHERE field_id = " . $this->db->prepare_int($field_id) . - " AND testproject_id = " . $this->db->prepare_int($tproject_id); - if ($this->db->exec_query($sql)) - { - $cf = $this->get_by_id($field_id); - if ($cf) - { - logAuditEvent(TLS("audit_cfield_unassigned",$cf[$field_id]['name'],$tproject_info['name']), - "ASSIGN",$tproject_id,"testprojects"); - } - } - } - } //function end - - - - /* - function: get_by_name - get custom field definition - - args: $name: custom field name - - returns: hash - */ - function get_by_name($name) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my_name=$this->db->prepare_string(trim($name)); - - $sql="/* $debugMsg */ SELECT CF.*, CFNT.node_type_id,NT.description AS node_type" . - " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_node_types']} CFNT," . - " {$this->tables['node_types']} NT" . - " WHERE CF.id=CFNT.field_id " . - " AND CFNT.node_type_id=NT.id " . - " AND name='{$my_name}' "; - return $this->db->fetchRowsIntoMap($sql,'id'); - } - - /* - function: get_by_id - get custom field definition - - args: $id: custom field id - - returns: hash - - */ - function get_by_id($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT CF.*, CFNT.node_type_id" . - " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_node_types']} CFNT" . - " WHERE CF.id=CFNT.field_id " . - " AND CF.id IN (" . implode(',',(array)$id) . ")"; - return($this->db->fetchRowsIntoMap($sql,'id')); - } - - /* - function: get_available_item_type - get information about what item type (testcase,testplan, etc) - can use this custom field - - args: $id: custom field id - - returns: - - */ - function get_available_item_type($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT CFNT.field_id,CFNT.node_type_id ". - " FROM {$this->tables['cfield_node_types']} CFNT, " . - " {$this->tables['nodes_types']} NT " . - " WHERE NT.id=CFNT.node_type_id " . - " CFNt.field_id=" . $this->db->prepare_int($id); - - return($this->db->fetchRowsIntoMap($sql,'field_id')); - } - - - /* - * - * keys name -> trim will be applied - * label -> trim will be applied - * type -> intval() wil be applied - * possible_values - * show_on_design -> trasformation on 1/0 using intval() [*] - * enable_on_design -> [*] - * show_on_execute -> [*] - * enable_on_execute -> [*] - * show_on_testplan_design -> [*] - * enable_on_testplan_design -> [*] - * - */ - function sanitize($cf) - { - $safe = $cf; - - // remove the standard set of characters considered harmful - // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab - // "\r" - carriage return - // and spaces - // fortunatelly this is trim standard behaviour - $k2san = array('name','label'); - foreach($k2san as $key) - { - $safe[$key] = $this->db->prepare_string(trim($cf[$key])); - } - - // seems here is better do not touch. - $safe['possible_values'] = $this->db->prepare_string($cf['possible_values']); - - $onezero = array('show_on_design','enable_on_design','show_on_testplan_design', - 'enable_on_testplan_design','show_on_execution','enable_on_execution'); - - foreach($onezero as $key) - { - $safe[$key] = intval($cf[$key]) > 0 ? 1 : 0; - } - - $safe['type'] = intval((int)$cf['type']); - $safe['node_type_id'] = intval((int)$cf['node_type_id']); - return $safe; - } - - /* - function: create a custom field - - args: $hash: - keys name -> trim will be applied - label -> trim will be applied - type -> intval() wil be applied - possible_values - show_on_design -> trasformation on 1/0 using intval() [*] - enable_on_design -> [*] - show_on_execute -> [*] - enable_on_execute -> [*] - show_on_testplan_design -> [*] - enable_on_testplan_design -> [*] - node_type_id - - returns: - - - rev: - @internal revision - */ - function create($cf) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array('status_ok' => 0, 'id' => 0, 'msg' => 'ko'); - - $safecf = $this->sanitize($cf); - - // if CF is for BUILD force enable_on_execution ALWAYS FALSE - // Node Verbose Code / Node Code Verbose - $nvc = $this->tree_manager->get_available_node_types(); - $ncv = array_flip($nvc); - if($ncv[$safecf['node_type_id']] == 'build') - { - $safecf['enable_on_design'] = 1; - $safecf['enable_on_execution'] = 0; - } - - $sql="/* $debugMsg */ INSERT INTO {$this->object_table} " . - " (name,label,type,possible_values, " . - " show_on_design,enable_on_design, " . - " show_on_testplan_design,enable_on_testplan_design, " . - " show_on_execution,enable_on_execution) " . - " VALUES('" . $safecf['name'] . "','" . $safecf['label'] . "'," . - intval($safecf['type']) . ",'" . $safecf['possible_values'] . "', " . - " {$safecf['show_on_design']},{$safecf['enable_on_design']}," . - " {$safecf['show_on_testplan_design']},{$safecf['enable_on_testplan_design']}," . - " {$safecf['show_on_execution']},{$safecf['enable_on_execution']})"; - $result=$this->db->exec_query($sql); - - if ($result) - { - // at least for Postgres DBMS table name is needed. - $field_id=$this->db->insert_id($this->object_table); - - $sql="/* $debugMsg */ INSERT INTO {$this->tables['cfield_node_types']} " . - " (field_id,node_type_id) " . - " VALUES({$field_id},{$safecf['node_type_id']}) "; - $result=$this->db->exec_query($sql); - } - - if ($result) - { - $ret = array('status_ok' => 1, 'id' => $field_id, 'msg' => 'ok'); - } - return $ret; - } - - - - - /* - function: update a custom field - - args: $hash: - keys name - label - type - possible_values - show_on_design - enable_on_design - show_on_execute - enable_on_execute - show_on_testplan_design - enable_on_testplan_design - node_type_id - - returns: - - */ - function update($cf) - { - $safecf = $this->sanitize($cf); - - // if CF is for BUILD force enable_on_execution ALWAYS FALSE - // Node Verbose Code / Node Code Verbose - $nvc = $this->tree_manager->get_available_node_types(); - $ncv = array_flip($nvc); - if($ncv[$safecf['node_type_id']] == 'build') - { - $safecf['enable_on_design'] = 1; - $safecf['enable_on_execution'] = 0; - } - - $sql = "UPDATE {$this->tables['custom_fields']} " . - " SET name='" . $safecf['name'] . "'," . - " label='" . $safecf['label'] . "'," . - " type={$safecf['type']}," . - " possible_values='" . $safecf['possible_values'] . "'," . - " show_on_design={$safecf['show_on_design']}," . - " enable_on_design={$safecf['enable_on_design']}," . - " show_on_testplan_design={$safecf['show_on_testplan_design']}," . - " enable_on_testplan_design={$safecf['enable_on_testplan_design']}," . - " show_on_execution={$safecf['show_on_execution']}," . - " enable_on_execution={$safecf['enable_on_execution']}" . - " WHERE id={$safecf['id']}"; - $result = $this->db->exec_query($sql); - - if ($result) - { - $sql = "UPDATE {$this->tables['cfield_node_types']} " . - " SET node_type_id={$safecf['node_type_id']}" . - " WHERE field_id={$safecf['id']}"; - $result = $this->db->exec_query($sql); - } - return $result ? 1 : 0; - } - - - /** - * delete() - * Will delete custom field definition and also ALL assigned values - * If custom field is linked to test projects, these links must be removed - * - */ - function delete($id) - { - // Before deleting definition I need to remove values - if( $this->is_used($id) ) - { - $this->remove_all_scopes_values($id); - } - $linked_tprojects = $this->get_linked_testprojects($id); - if( !is_null($linked_tprojects) && count($linked_tprojects) > 0 ) - { - $target=array_keys($linked_tprojects); - foreach($target as $tproject_id) - { - $this->unlink_from_testproject($tproject_id,(array)$id); - } - } - - $sql="DELETE FROM {$this->tables['cfield_node_types']} WHERE field_id={$id}"; - $result=$this->db->exec_query($sql); - if($result) - { - $sql="DELETE FROM {$this->tables['custom_fields']} WHERE id={$id}"; - $result=$this->db->exec_query($sql); - } - return $result ? 1 : 0; - } - - - /* - function: is_used - - args: $id: custom field id - - returns: 1/0 - - @used by cfieldsEdit.php, cfieldsEdit.tpl - - */ - function is_used($id) - { - $sql="SELECT field_id FROM {$this->tables['cfield_design_values']} " . - "WHERE field_id={$id} " . - "UNION " . - "SELECT field_id FROM {$this->tables['cfield_build_design_values']} " . - "WHERE field_id={$id} " . - "UNION " . - "SELECT field_id FROM {$this->tables['cfield_testplan_design_values']} " . - "WHERE field_id={$id} " . - "UNION " . - "SELECT field_id FROM {$this->tables['cfield_execution_values']} " . - "WHERE field_id={$id} "; - $result=$this->db->exec_query($sql); - return($this->db->num_rows( $result ) > 0 ? 1 : 0); - } //function end - - -/* - function: name_is_unique - - args: $id - $name - - returns: 1 => name is unique -*/ -function name_is_unique($id,$name) -{ - $cf=$this->get_by_name($name); - $status=0; - if( is_null($cf) || isset($cf[$id]) ) - { - $status=1; - } - return($status); -} //function end - - - - # -------------------- - # Adapted from Mantis code - # Prepare a string containing a custom field value for display - # $p_field_def definition of the custom field - # $p_node_id bug id to display the custom field value for - # - # [$p_value_field]: field id, to point to the field value in $p_field_def - function string_custom_field_value( $p_field_def, $p_node_id,$p_value_field='value') - { - $t_value = isset($p_field_def[$p_value_field]) ? $p_field_def[$p_value_field] : null; - $cfValue = htmlspecialchars($t_value); - - switch ($this->custom_field_types[intval($p_field_def['type'])]) - { - case 'email': - return "$cfValue"; - break; - - case 'enum': - case 'list': - case 'multiselection list': - case 'checkbox': - return str_replace( '|', ', ', $cfValue ); - break; - - case 'date': - if ($cfValue != null) - { - // must remove % - $t_date_format=str_replace("%","",config_get( 'date_format')); - $xdate=date( $t_date_format, $cfValue); - return $xdate; - } - break ; - - case 'datetime': - if ($cfValue != null) - { - // must remove % - // $t_date_format=str_replace("%","",config_get( 'timestamp_format')); - // $datetime_format=$t_date_format; - $t_date_format=str_replace("%","",config_get( 'date_format')); - $cfg=config_get('gui'); - $datetime_format=$t_date_format . " " . $cfg->custom_fields->time_format; - $xdate=date($datetime_format, $cfValue); - return $xdate; - } - break ; - - - case 'text area': - if ($cfValue != null) - { - return nl2br($cfValue); - } - break; - - case 'string': - return string_display_links($cfValue); - break; - - default: - // done this way in order to be able to debug if needed - return string_display_links($cfValue); - break; - } - } - - - - /* - function: get_linked_cfields_at_execution - returns information about custom fields that can be used - at least at executed, with the value assigned (is any has been assigned). - - - $tproject_id: needed because is possible to associate/link - a different set of custom field for every test project - - $enabled : 1 -> get custom fields that are has been configured - to be shown during test case execution AND are enabled. - - [$node_type]: default: null - verbose id ('testcase', 'testsuite', etc) of a node type. - custom fields are linked also to different node types. - Example: - I can define a custom field "Aspect" with values - Performace, Usability and wnat use it only for test suites. - - [$node_id]: default: null - identification of a node/element on node hierarchy. - Needed when I want to get the value of custom fields - linked to a node. - Example: - Have two test cases (ID:9999, ID:89800), and want to get - the value assigned to custom field "Operating System". - I will do two calls to this method. - - - [execution_id] - [testplan_id] - [access_key] - [location] - - returns: hash - key: custom field id - - */ - function get_linked_cfields_at_execution($tproject_id,$enabled, - $node_type=null,$node_id=null, - $execution_id=null,$testplan_id=null, - $access_key='id',$location=null) - { - $base_values="CF.*,"; - $additional_join=""; - $additional_values=""; - $additional_filter=""; - $order_clause=" ORDER BY display_order,CF.id "; - - $fetchMethod='fetchRowsIntoMap'; - - if( !is_null($node_type) ) - { - $additional_join .= " JOIN {$this->tables['cfield_node_types']} CFNT ON CFNT.field_id=CF.id " . - " AND CFNT.node_type_id=" . $this->decode['nodes'][$node_type]; - } - - if( !is_null($node_id) && !is_null($execution_id) && !is_null($testplan_id) ) - { - $additional_values .= ",CFEV.value AS value,CFEV.tcversion_id AS node_id"; - $additional_join .= " LEFT OUTER JOIN {$this->tables['cfield_execution_values']} CFEV ON CFEV.field_id=CF.id " . - " AND CFEV.tcversion_id=" . intval($node_id) . " " . - " AND CFEV.execution_id=" . intval($execution_id) . " " . - " AND CFEV.testplan_id=" . intval($testplan_id) . " "; - } - else if(!is_null($execution_id)) - { - $access_key = 'execution_id'; - $fetchMethod='fetchMapRowsIntoMap'; - $additional_values .= ',CFEV.value AS value, CFEV.execution_id '; - $additional_join .= " LEFT OUTER JOIN {$this->tables['cfield_execution_values']} CFEV ON CFEV.field_id=CF.id " . - " AND CFEV.execution_id IN (" . implode(',', $execution_id) . ") "; - } - else - { - if( !is_null($testplan_id) ) - { - $base_values = ''; - - // MSSQL BLOCKING error on Report "Test Cases with Execution Details" due to reserved word EXEC - $additional_values .= ",CF.type,CF.name,CF.label,CF.id,CFEV.value AS value,CFEV.tcversion_id AS node_id," . - "EXECU.id AS exec_id, EXECU.tcversion_id,EXECU.tcversion_number," . - "EXECU.execution_ts,EXECU.status AS exec_status,EXECU.notes AS exec_notes, " . - "NHB.id AS tcase_id, NHB.name AS tcase_name, TCV.tc_external_id, " . - "B.id AS builds_id,B.name AS build_name, U.login AS tester, " . - "PLAT.name AS platform_name, COALESCE(PLAT.id,0) AS platform_id"; - - $additional_join .= " JOIN {$this->tables['cfield_execution_values']} CFEV ON CFEV.field_id=CF.id " . - " AND CFEV.testplan_id={$testplan_id} " . - " JOIN {$this->tables['executions']} EXECU ON CFEV.tcversion_id = EXECU.tcversion_id " . - " AND CFEV.execution_id = EXECU.id " ; - - $additional_join .= " JOIN {$this->tables['builds']} B ON B.id = EXECU.build_id " . - " AND B.testplan_id = EXECU.testplan_id " ; - - $additional_join .= " JOIN {$this->tables['tcversions']} TCV ON TCV.version = EXECU.tcversion_number " . - " AND TCV.id = EXECU.tcversion_id " ; - - $additional_join .= " JOIN {$this->tables['users']} U ON U.id = EXECU.tester_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHA ON NHA.id = EXECU.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHB.id = NHA.parent_id " ; - - // Use left join, if platforms is not used platform_name will become null - $additional_join .= " LEFT JOIN {$this->tables['platforms']} PLAT ON EXECU.platform_id = PLAT.id"; - $order_clause="ORDER BY EXECU.tcversion_id,exec_status,exec_id"; - - $fetchMethod='fetchArrayRowsIntoMap'; - } - } - - if( !is_null($location) ) - { - $additional_filter .= " AND CF.id= " . intval($location) . " "; - } - - - $sql = "SELECT {$base_values} CFTP.display_order,CFTP.location,CFTP.required" . - $additional_values . - " FROM {$this->tables['custom_fields']} CF " . - " JOIN {$this->tables['cfield_testprojects']} CFTP ON CFTP.field_id=CF.id " . - $additional_join . - " WHERE CFTP.testproject_id={$tproject_id} " . - " AND CFTP.active=1 " . - " AND CF.enable_on_execution={$enabled} " . - " AND CF.show_on_execution=1 {$additional_filter} {$order_clause} "; - - switch ($fetchMethod) - { - case 'fetchArrayRowsIntoMap': - case 'fetchRowsIntoMap': - $map = $this->db->$fetchMethod($sql,$access_key); - break; - - case 'fetchMapRowsIntoMap': - $map = $this->db->$fetchMethod($sql,$access_key,'id'); - break; - } - return $map; - } - - - - - /* - function: execution_values_to_db - write values of custom fields that are used at execution time. - if record exists => UPDATE - - args: $hash: contains info about CF gathered at user interface. - (normally $_REQUEST variable) - key: custom_field__. - Example custom_field_0_67 -> 0=> string field - - $node_id: - $execution_id: - $testplan_id: - - [$cf_map]: hash -> all the custom fields linked and enabled - that are applicable to the node type of $node_id. - - For the keys not present in $hash, we will write - an appropriate value according to custom field - type. - - This is needed because when trying to udpate - with hash being $_REQUEST, $_POST or $_GET - some kind of custom fields (checkbox, list, multiple list) - when has been deselected by user. - - - [hash_type]: default null, string that can be used to change how hash - is processed. - - rev: - 20090727 - franciscom - added [hash_type], to reuse this method on API - 20070501 - franciscom - limiting lenght of value before writting - */ - function execution_values_to_db($hash,$node_id,$execution_id,$testplan_id, - $cf_map=null,$hash_type=null) - { - if( is_null($hash) && is_null($cf_map) ) - { - return; - } - - if( is_null($hash_type) ) - { - $cfield=$this->_build_cfield($hash,$cf_map); - } - else - { - $cfield=$hash; - } - - if( !is_null($cfield) ) - { - foreach($cfield as $field_id => $type_and_value) - { - $value = $type_and_value['cf_value']; - - $where_clause = " WHERE field_id=" . $this->db->prepare_int($field_id) . - " AND tcversion_id=" . $this->db->prepare_int($node_id) . - " AND execution_id=" .$this->db->prepare_int($execution_id) . - " AND testplan_id=" . $this->db->prepare_int($testplan_id); - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // do I need to update or insert this value? - $sql = " SELECT value,field_id,execution_id " . - " FROM {$this->tables['cfield_execution_values']} " . $where_clause; - - $rs = (array)$this->db->get_recordset($sql); - - // max_length_value = 0 => no limit - if( $this->max_length_value > 0 && tlStringLen($value) > $this->max_length_value) { - $value = substr($value,0,$this->max_length_value); - } - $safe_value=$this->db->prepare_string($value); - - $howMany = count($rs); - if( $howMany > 0 && $value != "" ) { - $sql = " UPDATE {$this->tables['cfield_execution_values']} " . - " SET value='{$safe_value}' " . $where_clause; - $this->db->exec_query($sql); - } else if( $howMany == 0 && $value != "" ) { - - # Remark got from Mantis code: - # Always store the value, even if it's the default value - # This is important, as the definitions might change but the - # values stored with a bug must not change - $sql = "INSERT INTO {$this->tables['cfield_execution_values']} " . - " ( field_id, tcversion_id, execution_id,testplan_id,value ) " . - " VALUES ( {$field_id}, {$node_id}, {$execution_id}, {$testplan_id}, '{$safe_value}' )"; - $this->db->exec_query($sql); - - } else if( $howMany > 0 && $value == "") { - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_execution_values']} " . $where_clause; - $this->db->exec_query($sql); - } - - } //foreach($cfield - } //if( !is_null($cfield) ) - } //function end - - - - /* - function: _build_cfield - support function useful for method used to write CF values to db: - - design_values_to_db() - - execution_values_to_db() - - testplan_design_values_to_db() - - args: $hash: - key: custom_field__[_][_]. - Example custom_field_0_67 -> 0=> string field - - In certain situation we can get: - custom_field_0_67_234 - 0 => string field - 234 => item owner of CF. - this happens when you can have multiple times same CF on a page, as happens - on execution page if configure TL to work on all test cases in test suite, - or when you use CF on testplan_design. - - To understand [<_date_part>] read below on "Notes on DATE PART - _build_cfield" - - value: can be an array, or a string depending the - - $cf_map: hash - key: cfield_id - value: custom field definition data - - - returns: hash or null. - - key: cfield_id - value: hash ('type_id' => field_type_id, - 'cf_value' => value) - - rev: - */ - function _build_cfield($hash,$cf_map) { - $localesDateFormat = config_get('locales_date_format'); - $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; - $date_format = str_replace('%', '', $localesDateFormat[$locale]); - - // carved in the stone - $cf_prefix = $this->name_prefix; - $len_cfp = tlStringLen($cf_prefix); - $cftype_pos=2; - $cfid_pos=3; - $cfield=null; - - // --------------------------------------------------------------------- - if( !is_null($cf_map) ) { - foreach($cf_map as $key => $value) { - $cfield[$key]=array("type_id" => $value['type'], "cf_value" => ''); - } - } - // --------------------------------------------------------------------- - - // --------------------------------------------------------------------- - // Overwrite with values if custom field id exist - if( !is_null($hash) ) { - foreach($hash as $key => $value) { - if( strncmp($key,$cf_prefix,$len_cfp) == 0 ) { - // Notes on DATE PART - _build_cfield - // - // When using Custom Fields on Test Spec: - // key has this format (for every type except date ) - // custom_field_0_10 for every type except for type date & datetime. - // - // For date custom fields: - // custom_field_8_10_input - // - // For datetime custom fields - // custom_field_8_10_input - // custom_field_8_10_hour, custom_field_8_10_minute, ..._second - // - // After explode() - // Position 2: CF type - // Position 3: CF id - // Position 4: only available for date CF, is date part indicator - // - // When using Custom Fields on Execution - // another piece is added (TC id) then for a date CF, - // date part indicator is Position 5, instead of 4 - // - // When using Custom Fields on Testplan Design - // another piece is added (testplan_tcversion.id) then for a date CF, - // date part indicator is Position 5, instead of 4 - // - $dummy = explode('_',$key); - $last_idx = count($dummy)-1; - - $the_value = null; // without this #0008347 :( - if( isset($this->html_date_input_suffix[$dummy[$last_idx]]) ) { - $the_value[$dummy[$last_idx]] = $value; - } - else { - $the_value = $value; - } - - $cfield[$dummy[$cfid_pos]]=array("type_id" => $dummy[$cftype_pos], - "cf_value" => $the_value); - } - } - } //if( !is_null($hash) ) - - if( !is_null($cfield) ) { - foreach($cfield as $field_id => $type_and_value) { - $value = $type_and_value['cf_value']; - $verbose_type=trim($this->custom_field_types[$type_and_value['type_id']]); - switch ($verbose_type) { - case 'multiselection list': - case 'checkbox': - if( count($value) > 1) { - $value=implode('|',$value); - } - else { - $value=is_array($value) ? $value[0] : $value; - } - $cfield[$field_id]['cf_value']=$value; - break; - - case 'date': - if (($value['input'] == 0) || ($value['input'] == '')) { - $cfield[$field_id]['cf_value']=''; - } - else { - $cfield[$field_id]['cf_value']=''; - $pvalue = split_localized_date($value['input'], $date_format); - if($pvalue != null) { - $cfield[$field_id]['cf_value'] = - mktime(0,0,0,$pvalue['month'],$pvalue['day'],$pvalue['year']); - } - } - break; - - case 'datetime': - if ($value['input'] == '') { - $cfield[$field_id]['cf_value']=''; - } - else { - $cfield[$field_id]['cf_value']=''; - $pvalue = split_localized_date($value['input'], $date_format); - if($pvalue != null) { - if($value['hour'] == -1 || $value['minute'] == -1 || - $value['second'] == -1) { - $value['hour'] = $value['minute'] = $value['second'] = 0; - } - $cfield[$field_id]['cf_value'] = - mktime($value['hour'], $value['minute'], $value['second'], - $pvalue['month'], $pvalue['day'], $pvalue['year']); - } - } - break; - - default: - $dynamic_call='build_cfield_' . str_replace(' ', '_', $verbose_type); - if( function_exists($dynamic_call) ) { - $cfield[$field_id]['cf_value'] = $dynamic_call($value); - } - else if( method_exists($this,$dynamic_call) ) { - $cfield[$field_id]['cf_value'] = $this->$dynamic_call($value); - } - else { - $cfield[$field_id]['cf_value']=$value; - } - break; - - } - } // foreach - } - return $cfield; - } // function end - - - - - - - /* - function: set_display_order - - args : $tproject_id: needed because is possible to associate/link - a different set of custom field for every test project - $map_field_id_display_order - - - - returns: - - */ - function set_display_order($tproject_id, $map_field_id_display_order) - { - $tproject_info = $this->tree_manager->get_node_hierarchy_info($tproject_id); - foreach($map_field_id_display_order as $field_id => $display_order) - { - $sql = "UPDATE {$this->tables['cfield_testprojects']} " . - " SET display_order=" . intval($display_order) . - " WHERE testproject_id={$tproject_id} AND field_id={$field_id} "; - $this->db->exec_query($sql); - } - if ($tproject_info) - { - logAuditEvent(TLS("audit_cfield_display_order_changed",$tproject_info['name']), - "SAVE",$tproject_id,"testprojects"); - } - } // function end - - -/** - * set value of location attribute for one or multiple custom fields. - * - * - */ - function setDisplayLocation($tproject_id, $field_id_location) - { - $tproject_info = $this->tree_manager->get_node_hierarchy_info($tproject_id); - foreach($field_id_location as $field_id => $location) - { - $sql = "UPDATE {$this->tables['cfield_testprojects']} " . - " SET location=" . intval($location) . - " WHERE testproject_id={$tproject_id} AND field_id={$field_id} "; - $this->db->exec_query($sql); - } - if ($tproject_info) - { - logAuditEvent(TLS("audit_cfield_location_changed",$tproject_info['name']), - "SAVE",$tproject_id,"testprojects"); - } - } // function end - - - # code from mantis helper_api.php - # -------------------- - # returns a tab index value and increments it by one. This is used to give sequential tab index on - # a form. - function helper_get_tab_index_value() { - static $tab_index = 0; - return ++$tab_index; - } - - # -------------------- - # returns a tab index and increments internal state by 1. This is used to give sequential tab index on - # a form. For example, this function returns: tabindex="1" - function helper_get_tab_index() { - return 'tabindex="' . helper_get_tab_index_value() . '"'; - } - - - -/** - * Retrieves the XML-RPC Server Parameters specified through custom fields. - * - * Done searching CARVED in the stone Custom Field Names on different - * (AGAIN CARVED in the stone) CF value tables in this way: - * - * CF name will have 3 pieces separated by _ (underscore) - * - * RE-XMLRPC_url_tsuite - * RE-XMLRPC_url_tcase - * RE-XMLRPC_url_link - * - * Part 1: RE-XMLRPC_ FIXED value, used as search key to get automatically - * CF to be analised. - * - * Part 2: url will be key on returned hash, and is part of 'contract' with caller, - * i.e. caller will use this key. - * This key is a FREE choice of developer of Remote Execute modules to use - * with TL. - * - * Part 3: this part is domain (link,tcase,tsuite) - * work this way: - * To specify Remote Execution server parameters we have provided 3 choices - * a. on test case version LINKED to Test Plan + Platform (Test Plan Design time) - * b. on test case version BUT at Test Spec Design time. - * In this way if is OK to have always same parameters no matter - * test plan + platform where test case version has been linked, we configure - * this just ONCE. - * c. on test suite (can be done ONLY at Test Spec Design time), all test case versions - * contained on this test suite branch (and children Test suites) will share this - * configuration. - * - * - * - * @param integer $node_id Accepts current node id from nodes hierarchy level - * @return mixed An array of config params if found, else returns null - * - * @internal rev: - * - * 20110123 - franciscom - need refactoring after we have choose to link custom field - * values to test case version not to test case - * - **/ -function getXMLRPCServerParams($nodeID,$tplanLinkID=null) -{ - static $node_type; - static $likeTarget; - static $CFGKEY_IDX; - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $srv_cfg = new stdClass(); - - if( is_null($node_type) ) - { - $node_type=$this->tree_manager->get_available_node_types(); - $likeTarget = 'RE-XMLRPC_%'; - $CFGKEY_IDX = 1; - } - - $node_info = $this->tree_manager->get_node_hierarchy_info($nodeID); - $ret = null; - - if( !is_null($node_info) ) - { - $server_info = null; - - // First Search at test plan design time - if( !is_null($tplanLinkID) ) - { - $sql = " /* $debugMsg */ SELECT cf.name, cfv.value " . - " FROM {$this->tables['cfield_testplan_design_values']} cfv " . - " JOIN {$this->tables['custom_fields']} cf ON " . - " cfv.field_id = cf.id " . - " WHERE cf.name LIKE '{$likeTarget}' " . - " AND cfv.link_id = " . intval($tplanLinkID); - - $server_info = $this->db->fetchRowsIntoMap($sql,'name'); - } - - if( is_null($server_info) ) - { - - $sql = " /* $debugMsg */ SELECT cf.name, cfv.value " . - " FROM {$this->tables['cfield_design_values']} cfv " . - " JOIN {$this->tables['custom_fields']} cf ON " . - " cfv.field_id = cf.id " . - " WHERE cf.name LIKE '{$likeTarget}' " . - " AND cfv.node_id = " . intval($nodeID); - - $server_info = $this->db->fetchRowsIntoMap($sql,'name'); - } - - if( is_null($server_info) ) - { - // Recurse - // 20110123 - franciscom - // At time of initial development this was thinked to try to get - // server info from Test Suite. - // Because with TL 1.9.x when working with test case we will receive - // Test Case Version ID, instead of Test Case ID (1.8.x), we will do - // a call to reach Test Case and then another to reach Test Suite - // - // - if($node_info['parent_id'] != "") - { - $ret = $this->getXMLRPCServerParams($node_info['parent_id']); - } - } - else - { - $key2loop = array_keys($server_info); - foreach($key2loop as $target) - { - $dummy = explode('_',$target); - $ret[$dummy[$CFGKEY_IDX]] = $server_info[$target]['value']; - } - } - } // if( !is_null($node_info) ) - - return $ret; -} //function end - - - /* - function: testplan_design_values_to_db - write values of custom fields that are used at testplan design time. - - args: $hash: contains info about CF gathered at user interface. - (normally $_REQUEST variable) - key: custom_field__. - Example custom_field_0_67 -> 0=> string field - - $node_id: Remember that this CF are used to extend information - on test cases (tcversions) linked to test plans. - Then node_id can not point to other type of node than test case version, - then node_id will contain a tcversion_id. - - I have leave this argument to - - - - $link_id: Remember that this CF are used to extend information - on test cases (tcversions) linked to test plans. - Link information is store in testplan_tcversions table, - $link_id points to this link (testplan_tcversions.id field) - - [$cf_map]: hash -> all the custom fields linked and enabled - that are applicable to the node type of $node_id. - - For the keys not present in $hash, we will write - an appropriate value according to custom field - type. - This is needed because when trying to udpate - with hash being $_REQUEST, $_POST or $_GET - some kind of custom fields (checkbox, list, multiple list) - when has been deselected by user. - - [$hash_type]: NEED TO BE COMMENTED - - - rev: - */ - function testplan_design_values_to_db($hash,$node_id,$link_id,$cf_map=null,$hash_type=null) - { - if( is_null($hash) && is_null($cf_map) ) - { - return; - } - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $cfield = is_null($hash_type) ? $this->_build_cfield($hash,$cf_map) : $hash; - if( !is_null($cfield) ) - { - foreach($cfield as $field_id => $type_and_value) - { - $value = $type_and_value['cf_value']; - - // do I need to update or insert this value? - $sql = "SELECT value FROM {$this->tables['cfield_testplan_design_values']} " . - " WHERE field_id={$field_id} AND link_id={$link_id}"; - - $result = $this->db->exec_query($sql); - - // max_length_value = 0 => no limit - if( $this->max_length_value > 0 && tlStringLen($value) > $this->max_length_value) - { - $value = substr($value,0,$this->max_length_value); - } - - $safe_value=$this->db->prepare_string($value); - if($this->db->num_rows( $result ) > 0 && $value != "") - { - $sql = "UPDATE {$this->tables['cfield_testplan_design_values']} " . - " SET value='{$safe_value}' " . - " WHERE field_id={$field_id} AND link_id={$link_id}"; - $this->db->exec_query($sql); - } - // BUGID 3989 - else if ($this->db->num_rows( $result ) == 0 && $value != "") - { - # Remark got from Mantis code: - # Always store the value, even if it's the dafault value - # This is important, as the definitions might change but the - # values stored with a bug must not change - $sql = "INSERT INTO {$this->tables['cfield_testplan_design_values']} " . - " ( field_id, link_id, value ) " . - " VALUES ( {$field_id}, {$link_id}, '{$safe_value}' )"; - $this->db->exec_query($sql); - // BUGID 3989 - } else if ($this->db->num_rows( $result ) > 0 && $value == "") { - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_testplan_design_values']} " . - " WHERE field_id={$field_id} AND link_id={$link_id}"; - $this->db->exec_query($sql); - } - - } //foreach($cfield - } //if( !is_null($cfield) ) - - } //function end - - - - /* - function: get_linked_cfields_at_testplan_design - returns information about custom fields that can be used - at least at testplan design time (test case assignment), - with the value assigned (is any has been assigned). - - - $tproject_id: needed because is possible to associate/link - a different set of custom field for every test project - - $enabled : 1 -> get custom fields that are has been configured - to be shown during test case execution AND are enabled. - - [$node_type]: default: null - verbose id ('testcase', 'testsuite', etc) of a node type. - custom fields are linked also to different node types. - Example: - I can define a custom field "Aspect" with values - Performace, Usability and wnat use it only for test suites. - - [$node_id]: default: null - identification of a node/element on node hierarchy. - Needed when I want to get the value of custom fields - linked to a node. - Example: - Have two test cases (ID:9999, ID:89800), and want to get - the value assigned to custom field "Operating System". - I will do two calls to this method. - - IMPORTANT: - Fot testplan_design Custom Field this will be a TCVERSION_ID, - not a TESTCASE_ID - - - [link_id]: points to testplan_tcversions.id field - [testplan_id] - - - returns: hash - key: custom field id - - - - */ - function get_linked_cfields_at_testplan_design($tproject_id,$enabled, - $node_type=null,$node_id=null, - $link_id=null,$testplan_id=null,$access_key = 'id') - { - $additional_join=""; - $additional_values=""; - $additional_filter=""; - - $order_by_clause = " ORDER BY display_order,CF.id "; - $fetchMethod = 'fetchRowsIntoMap'; - - if( !is_null($node_type) ) - { - $hash_descr_id = $this->tree_manager->get_available_node_types(); - $node_type_id=$hash_descr_id[$node_type]; - - $additional_join .= " JOIN {$this->tables['cfield_node_types']} CFNT ON CFNT.field_id=CF.id " . - " AND CFNT.node_type_id={$node_type_id} "; - } - - if( is_null($link_id) && !is_null($testplan_id)) - { - $additional_values .= ",CFTDV.value AS value, CFTDV.link_id AS node_id, " . - "NHB.id AS tcase_id, NHB.name AS tcase_name, " . - "TCV.tc_external_id "; - - $additional_join .= "JOIN {$this->tables['testplan_tcversions']} TPTC" . - " ON TPTC.testplan_id = {$testplan_id}" . - " JOIN {$this->tables['cfield_testplan_design_values']} CFTDV " . - " ON CFTDV.field_id=CF.id " . - " AND CFTDV.link_id = TPTC.id "; - - $additional_join .= " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTC.tcversion_id " . - " AND TCV.id = TPTC.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHA ON NHA.id = TPTC.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHB.id = NHA.parent_id " ; - - $order_by_clause = " ORDER BY node_id,display_order,CF.id "; - $fetchMethod = 'fetchArrayRowsIntoMap'; - $access_key = 'node_id'; - - } - elseif( !is_null($link_id) ) - { - $additional_values .= ",CFTDV.value AS value, CFTDV.link_id AS node_id"; - $additional_join .= " LEFT OUTER JOIN {$this->tables['cfield_testplan_design_values']} CFTDV " . - " ON CFTDV.field_id=CF.id " . - " AND CFTDV.link_id={$link_id} "; - } - - $sql="SELECT CF.*,CFTP.display_order,CFTP.required" . - $additional_values . - " FROM {$this->tables['custom_fields']} CF " . - " JOIN {$this->tables['cfield_testprojects']} CFTP ON CFTP.field_id=CF.id " . - $additional_join . - " WHERE CFTP.testproject_id={$tproject_id} " . - " AND CFTP.active=1 " . - " AND CF.enable_on_testplan_design={$enabled} " . - $order_by_clause; - - $map = $this->db->$fetchMethod($sql,$access_key); - return $map; - } - - /* - function: string_input_radio - returns an string with the html needed to display radio custom field. - Is normally called by string_custom_field_input() - - args: p_field_def: contains the definition of the custom field - (including it's field id) - - p_input_name: html input name - - p_custom_field_value: html input value - htmlspecialchars() must be applied to this - argument by caller. - - returns: html string - - rev: 20080816 - franciscom - based on Mantis 1.2.0a1 code - - */ - private function string_input_radio($p_field_def, $p_input_name, $p_custom_field_value,$opt=null) - { - $options = array('remove_required' => false); - $options = array_merge($options,(array)$opt); - - $str_out=''; - $t_values = explode( '|', $p_field_def['possible_values']); - $t_checked_values = explode( '|', $p_custom_field_value ); - - if($options['remove_required']) - { - $required = ' class="" '; - } - else - { - $required = $p_field_def['required'] ? ' class="required" required ' : ' class="" '; - } - - foreach( $t_values as $t_option ) - { - // $str_out .= ' ' . $t_option . '  '; - } - else - { - $str_out .= ' value="' . $t_option . '"> ' . $t_option . '  '; - } - } - return $str_out; - } - - /* - function: build_cfield_radio - support function useful for method used to write radio CF values to db. - Is normally called by _build_cfield() - - args: custom_field_value: value to be converted to be written to db. - - returns: value converted - - rev: 20080816 - franciscom - - */ - function build_cfield_radio($custom_field_value) - { - if( count($custom_field_value) > 1) - { - $value=implode('|',$custom_field_value); - } - else - { - $value=is_array($custom_field_value) ? $custom_field_value[0] :$custom_field_value; - } - return $value; - } - - -/* - function: string_input_string - returns an string with the html needed to display custom field of type: - string, email, numeric, float - - Is normally called by string_custom_field_input() - - args: p_field_def: contains the definition of the custom field - (including it's field id) - - p_input_name: html input name - - p_custom_field_value: html input value - htmlspecialchars() must be applied to this - argument by caller. - - p_size: html input size - - returns: html string - - - */ - private function string_input_string($p_field_def, $p_input_name, $p_custom_field_value, $p_size,$opt=null) - { - $options = array('remove_required' => false); - $options = array_merge($options,(array)$opt); - if($options['remove_required']) - { - $required = ' class="" '; - } - else - { - $required = $p_field_def['required'] ? ' class="required" required ' : ' class="" '; - } - - $str_out=''; - $size = intval($p_size) > 0 ? $p_size : self::DEFAULT_INPUT_SIZE; - $str_out .= "'; - return $str_out; - } - - - -/** - * exportValueAsXML - * generate XML with custom field name, and custom field value - * useful on export to XML method for items that can have custom fields, - * example: test cases, test suites, req specification, etc. - * - * @param map $cfMap: key: custom file ID, value: map with at least keys 'name', 'value' - * - */ - function exportValueAsXML($cfMap) - { - $cfRootElem = "\n{{XMLCODE}}\t\t\n"; - $cfElemTemplate = "\t\t\t" . "\n\t\t\t\n\t\t\t" . - "\n" . - "\t\t\t" . "\n"; - $cfDecode = array ("||NAME||" => "name","||VALUE||" => "value"); - $cfXML = exportDataToXML($cfMap,$cfRootElem,$cfElemTemplate,$cfDecode,true); - return $cfXML; - } - - -/** - * remove_all_scopes_values - * For a given custom field id remove all assigned values in any scope - * - * @param int $id: custom field id - * - * - * - * - */ -function remove_all_scopes_values($id) -{ - // some sort of blind delete - $tables = array('cfield_design_values','cfield_build_design_values', - 'cfield_execution_values','cfield_testplan_design_values'); - $safe_id = intval($id); - foreach($tables as $tt) - { - $sql = "DELETE FROM {$this->tables[$tt]} WHERE field_id={$safe_id} "; - $this->db->exec_query($sql); - } -} - -/** - * get_linked_testprojects - * For a given custom field id return all test projects where is linked. - * - * @param int $id: custom field id - * - */ -function get_linked_testprojects($id) -{ - $sql=" SELECT NH.id, NH.name " . - " FROM {$this->tables['cfield_testprojects']} CFTP, {$this->tables['nodes_hierarchy']} NH " . - " WHERE CFTP.testproject_id=NH.id " . - " AND CFTP.field_id = {$id} ORDER BY NH.name "; - - $rs=$this->db->fetchRowsIntoMap($sql,'id'); - return $rs; -} - - -/** - * @param string node type in verbose form. Example 'testcase' - * - * returns map with key: verbose location (see custom field class $locations) - * value: array with fixed key 'location' - * value: location code - * - */ -function buildLocationMap($nodeType) -{ - $locationMap=null; - $dummy = $this->getLocations(); - $verboseLocationCode = array_flip($dummy[$nodeType]); - if( !is_null($verboseLocationCode) && count($verboseLocationCode) > 0 ) - { - foreach($verboseLocationCode as $key => $value) - { - $locationMap[$key]['location']=$value; - } - } - return $locationMap; -} - - -/** - * @param int linkID: how is used depends on $options['scope'] - * $options['scope']=design => node_id - * $options['scope']=testplan_design => feature_id (see testplan_tcversions table) - * $options['scope']=execution => execution_id - * - * @used by testsuite.class.php => copy_cfields_values - */ -function getByLinkID($linkID, $options=null) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('scope' => 'design', 'output' => 'field_id'); - $my['options'] = array_merge($my['options'], (array)$options); - - switch($my['options']['output']) - { - case 'field_id': - $sql = "/* debugMsg */ SELECT field_id FROM "; - break; - - case 'full': - $sql = "/* debugMsg */ SELECT * FROM "; - break; - - } - - switch($my['options']['scope']) - { - case 'design': - $sql .= " {$this->tables['cfield_design_values']} " . - " WHERE node_id = {$linkID} "; - break; - - case 'testplan_design': - $sql .= " {$this->tables['cfield_testplan_design_values']} " . - " WHERE feature_id = {$linkID} "; - break; - - case 'execution': - $sql .= " {$this->tables['cfield_execution_values']} " . - " WHERE execution_id = {$linkID} "; - break; - - } - $rs = $this->db->get_recordset($sql); - - - return $rs; -} - - - -/** - * buildHTMLInputName - * - */ -function buildHTMLInputName($cf,$name_suffix) { - return "{$this->name_prefix}{$cf['type']}_{$cf['id']}{$name_suffix}"; -} - - - -/** - * - * - */ -function html_table_inputs($cfields_map,$name_suffix='',$input_values=null,$opt=null) -{ - $cf_smarty = ''; - $getOpt = array('name_suffix' => $name_suffix); - - $my['opt'] = array('addCheck' => false, 'addTable' => true, 'forceOptional' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - if(!is_null($cfields_map)) - { - $lbl_upd = lang_get('update_hint'); - $cf_map = $this->getValuesFromUserInput($cfields_map,$name_suffix,$input_values); - - $NO_WARNING_IF_MISSING=true; - $openTag = $my['opt']['addTable'] ? "" : ''; - $closeTag = $my['opt']['addTable'] ? "
    " : ''; - - $add_img = "'; - - $cf_smarty = ''; - foreach($cf_map as $cf_id => $cf_info) - { - $label=str_replace(TL_LOCALIZE_TAG,'', - lang_get($cf_info['label'],null,$NO_WARNING_IF_MISSING)); - - - // IMPORTANT NOTICE - // assigning an ID with this format is CRITIC to Javascript logic used - // to validate input data filled by user according to CF type - // extract input html id - // Want to give an html id to
    used as labelHolder, to use it in Javascript - // logic to validate CF content - if($my['opt']['forceOptional']) - { - $cf_info['required'] = 0; - } - - $cf_html_string = $this->string_custom_field_input($cf_info,$getOpt); - - $dummy = explode(' ', strstr($cf_html_string,'id="custom_field_')); - $td_label_id = str_replace('id="', 'id="label_', $dummy[0]); - - $cf_smarty .= "
    {$add_img}" . - " " . htmlspecialchars($label) . ":" . - $this->string_custom_field_input($cf_info,$getOpt) . "
    used as labelHolder, to use it in Javascript - // logic to validate CF content - $cf_html_string = $this->string_custom_field_input($cf_info,$getOpt); - - $dummy = explode(' ', strstr($cf_html_string,'id="custom_field_')); - $label_id = str_replace('id="', 'id="label_', $dummy[0]); - - $inputSet[] = array('label' => htmlspecialchars($label) , - 'label_id' => $label_id, - 'input' => $this->string_custom_field_input($cf_info,$getOpt)); - } - } - return $inputSet; - } - - /** - * - * - */ - function getByIDAndEnableOn($id,$enableOn=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT CF.*, CFNT.node_type_id" . - " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_node_types']} CFNT" . - " WHERE CF.id=CFNT.field_id " . - " AND CF.id IN (" . implode(',',(array)$id) . ")"; - - if(!is_null($enableOn) && is_array($enableOn)) - { - foreach($this->application_areas as $key) - { - if(isset($enableOn[$key]) && $enableOn[$key]) - { - $sql .= " AND CF.enable_on_{$key}=1 "; - } - } - } - - return($this->db->fetchRowsIntoMap($sql,'id')); - } - - /** - * - */ - function setMonitorable($tproject_id,$cfieldSet,$val) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($cfieldSet)) - { - return; - } - - $safe = new stdClass(); - $safe->tproject_id = intval($tproject_id); - $safe->val = (intval($val) > 0) ? 1 : 0; - - $field = 'monitorable'; - - $info = $this->tree_manager->get_node_hierarchy_info($safe->tproject_id); - $auditMsg = $val ? "audit_cfield_{$field}_on" : "audit_cfield_{$field}_off"; - foreach($cfieldSet as $field_id) - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['cfield_testprojects']} " . - " SET {$field}=" . $safe->val . - " WHERE testproject_id=" . $safe->tproject_id . - " AND field_id=" . $this->db->prepare_int($field_id); - - if ($this->db->exec_query($sql)) - { - $cf = $this->get_by_id($field_id); - if($cf) - { - logAuditEvent(TLS($auditMsg,$cf[$field_id]['name'],$info['name']), - "SAVE",$safe->tproject_id,"testprojects"); - } - } - } - } - - /** - * - * - */ - function cfdate2mktime($value) - { - if (($value == 0) || ($value == '')) - { - return ''; - } - else - { - $localesDateFormat = config_get('locales_date_format'); - $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; - $date_format = str_replace('%', '', $localesDateFormat[$locale]); - - $pvalue = split_localized_date($value, $date_format); - if($pvalue != null) - { - $pvalue = mktime(0, 0, 0, $pvalue['month'], $pvalue['day'], $pvalue['year']); - return $pvalue; - } - else - { - return ''; - } - } - } - - /** - * - */ - function getBooleanAttributes($tproject_id,$cfSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ " . - " SELECT field_id,active,required,monitorable " . - " FROM {$this->tables['cfield_testprojects']} CFTP " . - " WHERE testproject_id =" . intval($tproject_id); - - if(!is_null($cfSet)) - { - $sql .= " AND field_id IN(" . implode(',', $cfSet) . ")"; - } - - $rs = $this->db->fetchRowsIntoMap($sql,'field_id'); - - return $rs; - } - - /** - * - * - */ - function initViewGUI() { - $gogo = new stdClass(); - - $gogo->cfield = null; - $gogo->cfield_is_used = 0; - $gogo->cfield_is_linked = 0; - $gogo->linked_tprojects = null; - - $gogo->cf_map = $this->get_all(null,'transform'); - $gogo->cf_types = $gogo->cfield_types = $this->get_available_types(); - - // MAGIC 10 - $gogo->drawControlsOnTop = (null != $gogo->cf_map && count($gogo->cf_map) > 10); - - return $gogo; - } - -} // end class \ No newline at end of file diff --git a/lib/functions/ckeditor.class.php b/lib/functions/ckeditor.class.php index 03d2072745..cda0ef36bd 100644 --- a/lib/functions/ckeditor.class.php +++ b/lib/functions/ckeditor.class.php @@ -1,44 +1,45 @@ -InstanceName = $instanceName; - $this->Value = ''; - $this->Editor = new CKEditor(); - $this->Editor->returnOutput = true; - } - - /** - * - */ - function Create() { - echo $this->CreateHtml($rows,$cols); - } - - /** - * - */ - function CreateHtml() { - $Html = $this->Editor->editor($this->InstanceName, $this->Value, $config = array()); - - return $Html ; - } - -} // class end \ No newline at end of file +InstanceName = $instanceName; + $this->Value = ''; + $this->Editor = new CKEditor(); + $this->Editor->returnOutput = true; + } + + /** + */ + public function Create() + { + echo $this->CreateHtml($rows, $cols); + } + + /** + */ + public function CreateHtml($config = []) + { + return $this->Editor->editor($this->InstanceName, $this->Value, $config); + } +} diff --git a/lib/functions/code_testing/cfield_mgr.class.test.php b/lib/functions/code_testing/cfield_mgr.class.test.php index 356a18fc75..8058e12d84 100644 --- a/lib/functions/code_testing/cfield_mgr.class.test.php +++ b/lib/functions/code_testing/cfield_mgr.class.test.php @@ -1,51 +1,51 @@ -Poor's Man - $object_item - code inspection tool
    "; -echo "
    Scope of this page is allow you to understand with live
    "; -echo "examples how to use object: $object_item (implemented in file $object_class.class.php)
    "; -echo "Important:"; -echo "You are using your testlink DB to do all operations"; -echo "
    "; -echo "
    "; -echo "
     $object_item - constructor - $object_class(&\$db)";echo "
    "; -$obj_mgr=new $object_class($db); -new dBug($obj_mgr); - -$tproject_id=2714; -$enabled=1; +Poor's Man - $object_item - code inspection tool
    "; +echo "
    Scope of this page is allow you to understand with live
    "; +echo "examples how to use object: $object_item (implemented in file $object_class.class.php)
    "; +echo "Important:"; +echo "You are using your testlink DB to do all operations"; +echo "
    "; +echo "
    "; +echo "
     $object_item - constructor - $object_class(&\$db)";
    +echo "
    "; +$obj_mgr = new $object_class($db); +new dBug($obj_mgr); + +$tproject_id = 2714; +$enabled = 1; echo " function get_linked_cfields_at_testplan_design(\$tproject_id,\$enabled, \$node_type=null,\$node_id=null, \$link_id=null,\$testplan_id=null) -"; +"; echo " function get_linked_cfields_at_testplan_design($tproject_id,$enabled); -"; -$cf=$obj_mgr->get_linked_cfields_at_testplan_design($tproject_id,$enabled); -new dBug($cf); - - -?> \ No newline at end of file +"; +$cf = $obj_mgr->get_linked_cfields_at_testplan_design($tproject_id, $enabled); +new dBug($cf); + +?> diff --git a/lib/functions/code_testing/dBug.php b/lib/functions/code_testing/dBug.php index 9e498ea90e..dd574823d3 100644 --- a/lib/functions/code_testing/dBug.php +++ b/lib/functions/code_testing/dBug.php @@ -1,526 +1,606 @@ -initJSandCSS(); - } - $arrAccept=array("array","object","xml"); //array of variable types that can be "forced" - if(in_array($forceType,$arrAccept)) - $this->{"varIs".ucfirst($forceType)}($var); - else - $this->checkType($var); - } - - //get variable name - function getVariableName() { - $arrBacktrace = debug_backtrace(); - - //possible 'included' functions - $arrInclude = array("include","include_once","require","require_once"); - - //check for any included/required files. if found, get array of the last included file (they contain the right line numbers) - for($i=count($arrBacktrace)-1; $i>=0; $i--) { - $arrCurrent = $arrBacktrace[$i]; - if(array_key_exists("function", $arrCurrent) && - (in_array($arrCurrent["function"], $arrInclude) || (0 != strcasecmp($arrCurrent["function"], "dbug")))) - continue; - - $arrFile = $arrCurrent; - - break; - } - - if(isset($arrFile)) { - $arrLines = file($arrFile["file"]); - $code = $arrLines[($arrFile["line"]-1)]; - - //find call to dBug class - preg_match('/\bnew dBug\s*\(\s*(.+)\s*\);/i', $code, $arrMatches); - - return $arrMatches[1]; - } - return ""; - } - - //create the main table header - function makeTableHeader($type,$header,$colspan=2) { - if(!$this->bInitialized) { - $header = $this->getVariableName() . " (" . $header . ")"; - $this->bInitialized = true; - } - echo " +initJSandCSS(); + } + $arrAccept = array( + "array", + "object", + "xml" + ); // array of variable types that can be "forced" + if (in_array($forceType, $arrAccept)) + $this->{"varIs" . ucfirst($forceType)}($var); + else + $this->checkType($var); + } + + private function getVariableName() + { + $arrBacktrace = debug_backtrace(); + + // possible 'included' functions + $arrInclude = array( + "include", + "include_once", + "require", + "require_once" + ); + + // check for any included/required files. if found, get array of the last included file (they contain the right line numbers) + for ($i = count($arrBacktrace) - 1; $i >= 0; $i --) { + $arrCurrent = $arrBacktrace[$i]; + if (array_key_exists("function", $arrCurrent) && + (in_array($arrCurrent["function"], $arrInclude) || + (0 != strcasecmp($arrCurrent["function"], "dbug")))) + continue; + + $arrFile = $arrCurrent; + + break; + } + + if (isset($arrFile)) { + $arrLines = file($arrFile["file"]); + $code = $arrLines[($arrFile["line"] - 1)]; + + // find call to dBug class + preg_match('/\bnew dBug\s*\(\s*(.+)\s*\);/i', $code, $arrMatches); + + return $arrMatches[1]; + } + return ""; + } + + // create the main table header + function makeTableHeader($type, $header, $colspan = 2) + { + if (! $this->bInitialized) { + $header = $this->getVariableName() . " (" . $header . ")"; + $this->bInitialized = true; + } + echo "
    - - "; - } - - //create the table row header - function makeTDHeader($type,$header) { - echo " - - \n"; - } - - //error - function error($type) { - $error="Error: Variable cannot be a"; - // this just checks if the type starts with a vowel or "x" and displays either "a" or "an" - if(in_array(substr($type,0,1),array("a","e","i","o","u","x"))) - $error.="n"; - return ($error." ".$type." type"); - } - - //check variable type - function checkType($var) { - switch(gettype($var)) { - case "resource": - $this->varIsResource($var); - break; - case "object": - $this->varIsObject($var); - break; - case "array": - $this->varIsArray($var); - break; - case "NULL": - $this->varIsNULL(); - break; - case "boolean": - $this->varIsBoolean($var); - break; - default: - $var=($var=="") ? "[empty string]" : $var; - echo "
    ".$header."
    ".$header.""; - } - - //close table row - function closeTDRow() { - return "
    \n\n\n
    ".$var."
    \n"; - break; - } - } - - //if variable is a NULL type - function varIsNULL() { - echo "NULL"; - } - - //if variable is a boolean type - function varIsBoolean($var) { - $var=($var==1) ? "TRUE" : "FALSE"; - echo $var; - } - - //if variable is an array type - function varIsArray($var) { - $var_ser = serialize($var); - array_push($this->arrHistory, $var_ser); - - $this->makeTableHeader("array","array"); - if(is_array($var)) { - foreach($var as $key=>$value) { - $this->makeTDHeader("array",$key); - - //check for recursion - if(is_array($value)) { - $var_ser = serialize($value); - if(in_array($var_ser, $this->arrHistory, TRUE)) - $value = "*RECURSION*"; - } - - if(in_array(gettype($value),$this->arrType)) - $this->checkType($value); - else { - $value=(trim($value)=="") ? "[empty string]" : $value; - echo $value; - } - echo $this->closeTDRow(); - } - } - else echo "
    ".$this->error("array").$this->closeTDRow(); - array_pop($this->arrHistory); - echo "
    "; - } - - //if variable is an object type - function varIsObject($var) { - $var_ser = serialize($var); - array_push($this->arrHistory, $var_ser); - $this->makeTableHeader("object","object"); - - if(is_object($var)) { - $arrObjVars=get_object_vars($var); - foreach($arrObjVars as $key=>$value) { - - $value=(!is_object($value) && !is_array($value) && trim($value)=="") ? "[empty string]" : $value; - $this->makeTDHeader("object",$key); - - //check for recursion - if(is_object($value)||is_array($value)) { - $var_ser = serialize($value); - if(in_array($var_ser, $this->arrHistory, TRUE)) { - $value = (is_object($value)) ? "*RECURSION* -> $".get_class($value) : "*RECURSION*"; - - } - } - if(in_array(gettype($value),$this->arrType)) - $this->checkType($value); - else echo $value; - echo $this->closeTDRow(); - } - $arrObjMethods=get_class_methods(get_class($var)); - foreach($arrObjMethods as $key=>$value) { - $this->makeTDHeader("object",$value); - echo "[function]".$this->closeTDRow(); - } - } - else echo "
    ".$this->error("object").$this->closeTDRow(); - array_pop($this->arrHistory); - echo "
    "; - } - - //if variable is a resource type - function varIsResource($var) { - $this->makeTableHeader("resourceC","resource",1); - echo "\n\n"; - switch(get_resource_type($var)) { - case "fbsql result": - case "mssql result": - case "msql query": - case "pgsql result": - case "sybase-db result": - case "sybase-ct result": - case "mysql result": - $db=current(explode(" ",get_resource_type($var))); - $this->varIsDBResource($var,$db); - break; - case "gd": - $this->varIsGDResource($var); - break; - case "xml": - $this->varIsXmlResource($var); - break; - default: - echo get_resource_type($var).$this->closeTDRow(); - break; - } - echo $this->closeTDRow()."\n"; - } - - //if variable is a database resource type - function varIsDBResource($var,$db="mysql") { - if($db == "pgsql") - $db = "pg"; - if($db == "sybase-db" || $db == "sybase-ct") - $db = "sybase"; - $arrFields = array("name","type","flags"); - $numrows=call_user_func($db."_num_rows",$var); - $numfields=call_user_func($db."_num_fields",$var); - $this->makeTableHeader("resource",$db." result",$numfields+1); - echo " "; - for($i=0;$i<$numfields;$i++) { - $field_header = ""; - for($j=0; $j".$field_name.""; - } - echo ""; - for($i=0;$i<$numrows;$i++) { - $row=call_user_func($db."_fetch_array",$var,constant(strtoupper($db)."_ASSOC")); - echo "\n"; - echo "".($i+1).""; - for($k=0;$k<$numfields;$k++) { - $tempField=$field[$k]->name; - $fieldrow=$row[($field[$k]->name)]; - $fieldrow=($fieldrow=="") ? "[empty string]" : $fieldrow; - echo "".$fieldrow."\n"; - } - echo "\n"; - } - echo ""; - if($numrows>0) - call_user_func($db."_data_seek",$var,0); - } - - //if variable is an image/gd resource type - function varIsGDResource($var) { - $this->makeTableHeader("resource","gd",2); - $this->makeTDHeader("resource","Width"); - echo imagesx($var).$this->closeTDRow(); - $this->makeTDHeader("resource","Height"); - echo imagesy($var).$this->closeTDRow(); - $this->makeTDHeader("resource","Colors"); - echo imagecolorstotal($var).$this->closeTDRow(); - echo ""; - } - - //if variable is an xml type - function varIsXml($var) { - $this->varIsXmlResource($var); - } - - //if variable is an xml resource type - function varIsXmlResource($var) { - $xml_parser=xml_parser_create(); - xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0); - xml_set_element_handler($xml_parser,array(&$this,"xmlStartElement"),array(&$this,"xmlEndElement")); - xml_set_character_data_handler($xml_parser,array(&$this,"xmlCharacterData")); - xml_set_default_handler($xml_parser,array(&$this,"xmlDefaultHandler")); - - $this->makeTableHeader("xml","xml document",2); - $this->makeTDHeader("xml","xmlRoot"); - - //attempt to open xml file - $bFile=(!($fp=@fopen($var,"r"))) ? false : true; - - //read xml file - if($bFile) { - while($data=str_replace("\n","",fread($fp,4096))) - $this->xmlParse($xml_parser,$data,feof($fp)); - } - //if xml is not a file, attempt to read it as a string - else { - if(!is_string($var)) { - echo $this->error("xml").$this->closeTDRow()."\n"; - return; - } - $data=$var; - $this->xmlParse($xml_parser,$data,1); - } - - echo $this->closeTDRow()."\n"; - - } - - //parse xml - function xmlParse($xml_parser,$data,$bFinal) { - if (!xml_parse($xml_parser,$data,$bFinal)) { - die(sprintf("XML error: %s at line %d\n", - xml_error_string(xml_get_error_code($xml_parser)), - xml_get_current_line_number($xml_parser))); - } - } - - //xml: inititiated when a start tag is encountered - function xmlStartElement($parser,$name,$attribs) { - $this->xmlAttrib[$this->xmlCount]=$attribs; - $this->xmlName[$this->xmlCount]=$name; - $this->xmlSData[$this->xmlCount]='$this->makeTableHeader("xml","xml element",2);'; - $this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlName");'; - $this->xmlSData[$this->xmlCount].='echo "'.$this->xmlName[$this->xmlCount].'".$this->closeTDRow();'; - $this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlAttributes");'; - if(count($attribs)>0) - $this->xmlSData[$this->xmlCount].='$this->varIsArray($this->xmlAttrib['.$this->xmlCount.']);'; - else - $this->xmlSData[$this->xmlCount].='echo " ";'; - $this->xmlSData[$this->xmlCount].='echo $this->closeTDRow();'; - $this->xmlCount++; - } - - //xml: initiated when an end tag is encountered - function xmlEndElement($parser,$name) { - for($i=0;$i<$this->xmlCount;$i++) { - eval($this->xmlSData[$i]); - $this->makeTDHeader("xml","xmlText"); - echo (!empty($this->xmlCData[$i])) ? $this->xmlCData[$i] : " "; - echo $this->closeTDRow(); - $this->makeTDHeader("xml","xmlComment"); - echo (!empty($this->xmlDData[$i])) ? $this->xmlDData[$i] : " "; - echo $this->closeTDRow(); - $this->makeTDHeader("xml","xmlChildren"); - unset($this->xmlCData[$i],$this->xmlDData[$i]); - } - echo $this->closeTDRow(); - echo ""; - $this->xmlCount=0; - } - - //xml: initiated when text between tags is encountered - function xmlCharacterData($parser,$data) { - $count=$this->xmlCount-1; - if(!empty($this->xmlCData[$count])) - $this->xmlCData[$count].=$data; - else - $this->xmlCData[$count]=$data; - } - - //xml: initiated when a comment or other miscellaneous texts is encountered - function xmlDefaultHandler($parser,$data) { - //strip '' off comments - $data=str_replace(array("<!--","-->"),"",htmlspecialchars($data)); - $count=$this->xmlCount-1; - if(!empty($this->xmlDData[$count])) - $this->xmlDData[$count].=$data; - else - $this->xmlDData[$count]=$data; - } - - function initJSandCSS() { - echo << - /* code modified from ColdFusion's cfdump code */ - function dBug_toggleRow(source) { - var target = (document.all) ? source.parentElement.cells[1] : source.parentNode.lastChild; - dBug_toggleTarget(target,dBug_toggleSource(source)); - } - - function dBug_toggleSource(source) { - if (source.style.fontStyle=='italic') { - source.style.fontStyle='normal'; - source.title='click to collapse'; - return 'open'; - } else { - source.style.fontStyle='italic'; - source.title='click to expand'; - return 'closed'; - } - } - - function dBug_toggleTarget(target,switchToState) { - target.style.display = (switchToState=='open') ? '' : 'none'; - } - - function dBug_toggleTable(source) { - var switchToState=dBug_toggleSource(source); - if(document.all) { - var table=source.parentElement.parentElement; - for(var i=1;i - - -SCRIPTS; - } - -} -?> \ No newline at end of file + " . $header . " + "; + } + + // create the table row header + function makeTDHeader($type, $header) + { + echo " + " . $header . " + "; + } + + // close table row + function closeTDRow() + { + return "\n"; + } + + // error + function error($type) + { + $error = "Error: Variable cannot be a"; + // this just checks if the type starts with a vowel or "x" and displays either "a" or "an" + if (in_array(substr($type, 0, 1), array( + "a", + "e", + "i", + "o", + "u", + "x" + ))) + $error .= "n"; + return $error . " " . $type . " type"; + } + + // check variable type + function checkType($var) + { + switch (gettype($var)) { + case "resource": + $this->varIsResource($var); + break; + case "object": + $this->varIsObject($var); + break; + case "array": + $this->varIsArray($var); + break; + case "NULL": + $this->varIsNULL(); + break; + case "boolean": + $this->varIsBoolean($var); + break; + default: + $var = ($var == "") ? "[empty string]" : $var; + echo "\n\n\n
    " . $var . + "
    \n"; + break; + } + } + + // if variable is a NULL type + function varIsNULL() + { + echo "NULL"; + } + + // if variable is a boolean type + function varIsBoolean($var) + { + $var = ($var == 1) ? "TRUE" : "FALSE"; + echo $var; + } + + // if variable is an array type + function varIsArray($var) + { + $var_ser = serialize($var); + array_push($this->arrHistory, $var_ser); + + $this->makeTableHeader("array", "array"); + if (is_array($var)) { + foreach ($var as $key => $value) { + $this->makeTDHeader("array", $key); + + // check for recursion + if (is_array($value)) { + $var_ser = serialize($value); + if (in_array($var_ser, $this->arrHistory, true)) + $value = "*RECURSION*"; + } + + if (in_array(gettype($value), $this->arrType)) + $this->checkType($value); + else { + $value = (trim($value) == "") ? "[empty string]" : $value; + echo $value; + } + echo $this->closeTDRow(); + } + } else + echo "" . $this->error("array") . $this->closeTDRow(); + array_pop($this->arrHistory); + echo ""; + } + + // if variable is an object type + function varIsObject($var) + { + $var_ser = serialize($var); + array_push($this->arrHistory, $var_ser); + $this->makeTableHeader("object", "object"); + + if (is_object($var)) { + $arrObjVars = get_object_vars($var); + foreach ($arrObjVars as $key => $value) { + + $value = (! is_object($value) && ! is_array($value) && + trim($value) == "") ? "[empty string]" : $value; + $this->makeTDHeader("object", $key); + + // check for recursion + if (is_object($value) || is_array($value)) { + $var_ser = serialize($value); + if (in_array($var_ser, $this->arrHistory, true)) { + $value = (is_object($value)) ? "*RECURSION* -> $" . + get_class($value) : "*RECURSION*"; + } + } + if (in_array(gettype($value), $this->arrType)) + $this->checkType($value); + else + echo $value; + echo $this->closeTDRow(); + } + $arrObjMethods = get_class_methods(get_class($var)); + foreach ($arrObjMethods as $value) { + $this->makeTDHeader("object", $value); + echo "[function]" . $this->closeTDRow(); + } + } else + echo "" . $this->error("object") . $this->closeTDRow(); + array_pop($this->arrHistory); + echo ""; + } + + // if variable is a resource type + function varIsResource($var) + { + $this->makeTableHeader("resourceC", "resource", 1); + echo "\n\n"; + switch (get_resource_type($var)) { + case "fbsql result": + case "mssql result": + case "msql query": + case "pgsql result": + case "sybase-db result": + case "sybase-ct result": + case "mysql result": + $db = current(explode(" ", get_resource_type($var))); + $this->varIsDBResource($var, $db); + break; + case "gd": + $this->varIsGDResource($var); + break; + case "xml": + $this->varIsXmlResource($var); + break; + default: + echo get_resource_type($var) . $this->closeTDRow(); + break; + } + echo $this->closeTDRow() . "\n"; + } + + // if variable is a database resource type + function varIsDBResource($var, $db = "mysql") + { + if ($db == "pgsql") + $db = "pg"; + if ($db == "sybase-db" || $db == "sybase-ct") + $db = "sybase"; + $arrFields = array( + "name", + "type", + "flags" + ); + $numrows = call_user_func($db . "_num_rows", $var); + $numfields = call_user_func($db . "_num_fields", $var); + $this->makeTableHeader("resource", $db . " result", $numfields + 1); + echo " "; + for ($i = 0; $i < $numfields; $i ++) { + $field_header = ""; + for ($j = 0; $j < count($arrFields); $j ++) { + $db_func = $db . "_field_" . $arrFields[$j]; + if (function_exists($db_func)) { + $fheader = call_user_func($db_func, $var, $i) . " "; + if ($j == 0) + $field_name = $fheader; + else + $field_header .= $fheader; + } + } + $field[$i] = call_user_func($db . "_fetch_field", $var, $i); + echo "" . $field_name . ""; + } + echo ""; + for ($i = 0; $i < $numrows; $i ++) { + $row = call_user_func($db . "_fetch_array", $var, + constant(strtoupper($db) . "_ASSOC")); + echo "\n"; + echo "" . ($i + 1) . ""; + for ($k = 0; $k < $numfields; $k ++) { + $fieldrow = $row[($field[$k]->name)]; + $fieldrow = ($fieldrow == "") ? "[empty string]" : $fieldrow; + echo "" . $fieldrow . "\n"; + } + echo "\n"; + } + echo ""; + if ($numrows > 0) + call_user_func($db . "_data_seek", $var, 0); + } + + // if variable is an image/gd resource type + function varIsGDResource($var) + { + $this->makeTableHeader("resource", "gd", 2); + $this->makeTDHeader("resource", "Width"); + echo imagesx($var) . $this->closeTDRow(); + $this->makeTDHeader("resource", "Height"); + echo imagesy($var) . $this->closeTDRow(); + $this->makeTDHeader("resource", "Colors"); + echo imagecolorstotal($var) . $this->closeTDRow(); + echo ""; + } + + // if variable is an xml type + function varIsXml($var) + { + $this->varIsXmlResource($var); + } + + // if variable is an xml resource type + function varIsXmlResource($var) + { + $xml_parser = xml_parser_create(); + xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0); + xml_set_element_handler($xml_parser, array( + &$this, + "xmlStartElement" + ), array( + &$this, + "xmlEndElement" + )); + xml_set_character_data_handler($xml_parser, + array( + &$this, + "xmlCharacterData" + )); + xml_set_default_handler($xml_parser, + array( + &$this, + "xmlDefaultHandler" + )); + + $this->makeTableHeader("xml", "xml document", 2); + $this->makeTDHeader("xml", "xmlRoot"); + + // attempt to open xml file + $bFile = (! ($fp = @fopen($var, "r"))) ? false : true; + + // read xml file + if ($bFile) { + while ($data = str_replace("\n", "", fread($fp, 4096))) + $this->xmlParse($xml_parser, $data, feof($fp)); + } // if xml is not a file, attempt to read it as a string + else { + if (! is_string($var)) { + echo $this->error("xml") . $this->closeTDRow() . "\n"; + return; + } + $data = $var; + $this->xmlParse($xml_parser, $data, 1); + } + + echo $this->closeTDRow() . "\n"; + } + + // parse xml + function xmlParse($xml_parser, $data, $bFinal) + { + if (! xml_parse($xml_parser, $data, $bFinal)) { + die( + sprintf("XML error: %s at line %d\n", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser))); + } + } + + // xml: inititiated when a start tag is encountered + function xmlStartElement($parser, $name, $attribs) + { + $this->xmlAttrib[$this->xmlCount] = $attribs; + $this->xmlName[$this->xmlCount] = $name; + $this->xmlSData[$this->xmlCount] = '$this->makeTableHeader("xml","xml element",2);'; + $this->xmlSData[$this->xmlCount] .= '$this->makeTDHeader("xml","xmlName");'; + $this->xmlSData[$this->xmlCount] .= 'echo "' . + $this->xmlName[$this->xmlCount] . '".$this->closeTDRow();'; + $this->xmlSData[$this->xmlCount] .= '$this->makeTDHeader("xml","xmlAttributes");'; + if (! empty($attribs)) + $this->xmlSData[$this->xmlCount] .= '$this->varIsArray($this->xmlAttrib[' . + $this->xmlCount . ']);'; + else + $this->xmlSData[$this->xmlCount] .= 'echo " ";'; + $this->xmlSData[$this->xmlCount] .= 'echo $this->closeTDRow();'; + $this->xmlCount ++; + } + + // xml: initiated when an end tag is encountered + function xmlEndElement($parser, $name) + { + for ($i = 0; $i < $this->xmlCount; $i ++) { + eval($this->xmlSData[$i]); + $this->makeTDHeader("xml", "xmlText"); + echo (! empty($this->xmlCData[$i])) ? $this->xmlCData[$i] : " "; + echo $this->closeTDRow(); + $this->makeTDHeader("xml", "xmlComment"); + echo (! empty($this->xmlDData[$i])) ? $this->xmlDData[$i] : " "; + echo $this->closeTDRow(); + $this->makeTDHeader("xml", "xmlChildren"); + unset($this->xmlCData[$i], $this->xmlDData[$i]); + } + echo $this->closeTDRow(); + echo ""; + $this->xmlCount = 0; + } + + // xml: initiated when text between tags is encountered + function xmlCharacterData($parser, $data) + { + $count = $this->xmlCount - 1; + if (! empty($this->xmlCData[$count])) + $this->xmlCData[$count] .= $data; + else + $this->xmlCData[$count] = $data; + } + + // xml: initiated when a comment or other miscellaneous texts is encountered + function xmlDefaultHandler($parser, $data) + { + // strip '' off comments + $data = str_replace(array( + "<!--", + "-->" + ), "", htmlspecialchars($data)); + $count = $this->xmlCount - 1; + if (! empty($this->xmlDData[$count])) + $this->xmlDData[$count] .= $data; + else + $this->xmlDData[$count] = $data; + } + + function initJSandCSS() + { + echo << + /* code modified from ColdFusion's cfdump code */ + function dBug_toggleRow(source) { + var target = (document.all) ? source.parentElement.cells[1] : source.parentNode.lastChild; + dBug_toggleTarget(target,dBug_toggleSource(source)); + } + + function dBug_toggleSource(source) { + if (source.style.fontStyle=='italic') { + source.style.fontStyle='normal'; + source.title='click to collapse'; + return 'open'; + } else { + source.style.fontStyle='italic'; + source.title='click to expand'; + return 'closed'; + } + } + + function dBug_toggleTarget(target,switchToState) { + target.style.display = (switchToState=='open') ? '' : 'none'; + } + + function dBug_toggleTable(source) { + var switchToState=dBug_toggleSource(source); + if(document.all) { + var table=source.parentElement.parentElement; + for(var i=1;i + + + SCRIPTS; + } +} +?> diff --git a/lib/functions/code_testing/get_linked_tcversions.testplan.class.test.php b/lib/functions/code_testing/get_linked_tcversions.testplan.class.test.php index 91d823f67d..1752504406 100644 --- a/lib/functions/code_testing/get_linked_tcversions.testplan.class.test.php +++ b/lib/functions/code_testing/get_linked_tcversions.testplan.class.test.php @@ -1,159 +1,176 @@ -get_linked_tcversions($tplan_id); -echo '
    '; -echo $descr . '
    '; -echo 'Qta records:' . count($lt) . '
    '; -new dBug($lt); -*/ -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$descr='Get all linked test case versions'; - -$filters = array('tcase_id' => 32614); -$opt = array('exclude_info' => array('exec_info','assigned_on_build','priority')); -$lt=$obj_mgr->get_linked_tcversions($tplan_id,$filters,$opt); -echo '
    '; -echo $descr . '
    '; - -if( !is_null($filters) ) -{ - echo 'Filters:'; - new dBug($filters); -} - -if( !is_null($opt) ) -{ - echo 'Options:'; - new dBug($opt); -} -echo 'Qta records:' . count($lt) . '
    '; -new dBug($lt); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$descr='Get all linked test case versions'; - -$filters = array('tcase_id' => 32614, 'assigned_on_build' => 5); -$opt = array('exclude_info' => array('exec_info','priority')); -$lt=$obj_mgr->get_linked_tcversions($tplan_id,$filters,$opt); -echo '
    ';echo $descr . '
    '; - -if( !is_null($filters) ) -{ - echo 'Filters:'; - new dBug($filters); -} - -if( !is_null($opt) ) -{ - echo 'Options:'; - new dBug($opt); -} -echo 'Qta records:' . count($lt) . '
    '; -new dBug($lt); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$descr='Get all linked test case versions'; - -$filters = array('tcase_id' => 32614, 'assigned_on_build' => 5555); -$opt = array('exclude_info' => array('exec_info','priority')); -$lt=$obj_mgr->get_linked_tcversions($tplan_id,$filters,$opt); -echo '
    ';echo $descr . '
    '; - -if( !is_null($filters) ) -{ - echo 'Filters:'; - new dBug($filters); -} - -if( !is_null($opt) ) -{ - echo 'Options:'; - new dBug($opt); -} -echo 'Qta records:' . count($lt) . '
    '; -new dBug($lt); -// ------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------ -$descr='Get all linked test case versions'; - -$filters = array('platform_id' => 2, 'assigned_on_build' => 5555); -$opt = array('exclude_info' => array('exec_info','priority')); -$lt=$obj_mgr->get_linked_tcversions($tplan_id,$filters,$opt); -echo '
    ';echo $descr . '
    '; - -if( !is_null($filters) ) -{ - echo 'Filters:'; - new dBug($filters); -} - -if( !is_null($opt) ) -{ - echo 'Options:'; - new dBug($opt); -} -echo 'Qta records:' . count($lt) . '
    '; -new dBug($lt); -// ------------------------------------------------------------------------------------ - - -// ------------------------------------------------------------------------------------ -$tplan_id = 32676; -$descr='Get all linked test case versions'; - -$filters = array('platform_id' => 9, 'assigned_on_build' => 5555); -$opt = array('exclude_info' => array('exec_info','priority')); -$lt=$obj_mgr->get_linked_tcversions($tplan_id,$filters,$opt); -echo '
    ';echo $descr . '
    '; - -if( !is_null($filters) ) -{ - echo 'Filters:'; - new dBug($filters); -} - -if( !is_null($opt) ) -{ - echo 'Options:'; - new dBug($opt); -} -echo 'Qta records:' . count($lt) . '
    '; -new dBug($lt); -// ------------------------------------------------------------------------------------ - - - -?> \ No newline at end of file + 32614 +); +$opt = array( + 'exclude_info' => array( + 'exec_info', + 'assigned_on_build', + 'priority' + ) +); +$lt = $obj_mgr->get_linked_tcversions($tplan_id, $filters, $opt); +echo '
    '; +echo $descr . '
    '; + +if (! is_null($filters)) { + echo 'Filters:'; + new dBug($filters); +} + +if (! is_null($opt)) { + echo 'Options:'; + new dBug($opt); +} +echo 'Qta records:' . count($lt) . '
    '; +new dBug($lt); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$descr = 'Get all linked test case versions'; + +$filters = array( + 'tcase_id' => 32614, + 'assigned_on_build' => 5 +); +$opt = array( + 'exclude_info' => array( + 'exec_info', + 'priority' + ) +); +$lt = $obj_mgr->get_linked_tcversions($tplan_id, $filters, $opt); +echo '
    '; +echo $descr . '
    '; + +if (! is_null($filters)) { + echo 'Filters:'; + new dBug($filters); +} + +if (! is_null($opt)) { + echo 'Options:'; + new dBug($opt); +} +echo 'Qta records:' . count($lt) . '
    '; +new dBug($lt); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$descr = 'Get all linked test case versions'; + +$filters = array( + 'tcase_id' => 32614, + 'assigned_on_build' => 5555 +); +$opt = array( + 'exclude_info' => array( + 'exec_info', + 'priority' + ) +); +$lt = $obj_mgr->get_linked_tcversions($tplan_id, $filters, $opt); +echo '
    '; +echo $descr . '
    '; + +if (! is_null($filters)) { + echo 'Filters:'; + new dBug($filters); +} + +if (! is_null($opt)) { + echo 'Options:'; + new dBug($opt); +} +echo 'Qta records:' . count($lt) . '
    '; +new dBug($lt); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$descr = 'Get all linked test case versions'; + +$filters = array( + 'platform_id' => 2, + 'assigned_on_build' => 5555 +); +$opt = array( + 'exclude_info' => array( + 'exec_info', + 'priority' + ) +); +$lt = $obj_mgr->get_linked_tcversions($tplan_id, $filters, $opt); +echo '
    '; +echo $descr . '
    '; + +if (! is_null($filters)) { + echo 'Filters:'; + new dBug($filters); +} + +if (! is_null($opt)) { + echo 'Options:'; + new dBug($opt); +} +echo 'Qta records:' . count($lt) . '
    '; +new dBug($lt); +// ------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------ +$tplan_id = 32676; +$descr = 'Get all linked test case versions'; + +$filters = array( + 'platform_id' => 9, + 'assigned_on_build' => 5555 +); +$opt = array( + 'exclude_info' => array( + 'exec_info', + 'priority' + ) +); +$lt = $obj_mgr->get_linked_tcversions($tplan_id, $filters, $opt); +echo '
    '; +echo $descr . '
    '; + +if (! is_null($filters)) { + echo 'Filters:'; + new dBug($filters); +} + +if (! is_null($opt)) { + echo 'Options:'; + new dBug($opt); +} +echo 'Qta records:' . count($lt) . '
    '; +new dBug($lt); +// ------------------------------------------------------------------------------------ + +?> diff --git a/lib/functions/code_testing/requirement_mgr.class.test.php b/lib/functions/code_testing/requirement_mgr.class.test.php index 6726f26cf0..8f8624c5de 100644 --- a/lib/functions/code_testing/requirement_mgr.class.test.php +++ b/lib/functions/code_testing/requirement_mgr.class.test.php @@ -1,134 +1,145 @@ - Class Under Test : {$classUnderTest} "; -echo "
     {$classUnderTest}.class - constructor - $classUnderTest(&\$db)";echo "
    "; -$obj_mgr=new $classUnderTest($db); -new dBug($obj_mgr); - -// $method2test = "create"; -// echo "
     {$method2test} - $$method2test(&\$db)";echo "
    "; -// $obj_mgr=new $classUnderTest($db); -// new dBug($obj_mgr); - -$method2test = "updateOpen"; -$reqID = 18; -$reqVersionID = 19; -$value = 0; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - - -$method2test = "updateOpen"; -$reqID = 18; -$reqVersionID = 19; -$value = 1; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - -$method2test = "updateActive"; -$reqID = 18; -$reqVersionID = 19; -$value = 0; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - -$method2test = "updateActive"; -$reqID = 18; -$reqVersionID = 19; -$value = 1000; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - -$method2test = "updateActive"; -$reqID = 18; -$reqVersionID = 19; -$value = null; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - -$method2test = "updateActive"; -$reqID = 18; -$reqVersionID = 19; -$value = "one"; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - -$method2test = "updateActive"; -$reqID = 18; -$reqVersionID = 19; -$value = array(); -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - -$method2test = "updateActive"; -$reqID = 18; -$reqVersionID = 19; -$value = -18; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - -$method2test = "updateActive"; -$reqID = 18; -$reqVersionID = 19; -$value = false; -echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";echo "
    "; -echo "
     {$method2test} - $$method2test($reqVersionID,$value)";echo "
    "; - -$obj_mgr->$method2test($reqVersionID,$value); -$req_version = $obj_mgr->get_by_id($reqID); -new dBug($req_version); - - -?> \ No newline at end of file + Class Under Test : {$classUnderTest} "; +echo "
     {$classUnderTest}.class - constructor - $classUnderTest(&\$db)";
    +echo "
    "; +$obj_mgr = new $classUnderTest($db); +new dBug($obj_mgr); + +$method2test = "updateOpen"; +$reqID = 18; +$reqVersionID = 19; +$value = 0; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateOpen"; +$reqID = 18; +$reqVersionID = 19; +$value = 1; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateActive"; +$reqID = 18; +$reqVersionID = 19; +$value = 0; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateActive"; +$reqID = 18; +$reqVersionID = 19; +$value = 1000; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateActive"; +$reqID = 18; +$reqVersionID = 19; +$value = null; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateActive"; +$reqID = 18; +$reqVersionID = 19; +$value = "one"; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateActive"; +$reqID = 18; +$reqVersionID = 19; +$value = array(); +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateActive"; +$reqID = 18; +$reqVersionID = 19; +$value = - 18; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +$method2test = "updateActive"; +$reqID = 18; +$reqVersionID = 19; +$value = false; +echo "
     {$method2test} - $$method2test(&\$reqVersionID,\$value)";
    +echo "
    "; +echo "
     {$method2test} - $$method2test($reqVersionID,$value)";
    +echo "
    "; + +$obj_mgr->$method2test($reqVersionID, $value); +$req_version = $obj_mgr->get_by_id($reqID); +new dBug($req_version); + +?> diff --git a/lib/functions/code_testing/requirement_spec_mgr.class.test.php b/lib/functions/code_testing/requirement_spec_mgr.class.test.php index d3ecc8fc90..3cef1fa906 100644 --- a/lib/functions/code_testing/requirement_spec_mgr.class.test.php +++ b/lib/functions/code_testing/requirement_spec_mgr.class.test.php @@ -1,48 +1,40 @@ - Class Under Test : {$classUnderTest} "; -echo "
     {$classUnderTest}.class - constructor - $classUnderTest(&\$db)";echo "
    "; -$obj_mgr=new $classUnderTest($db); -new dBug($obj_mgr); - -// $method2test = "create"; -// echo "
     {$method2test} - $$method2test(&\$db)";echo "
    "; -// $obj_mgr=new $classUnderTest($db); -// new dBug($obj_mgr); - -$method2test = "create_revision"; -$rspecID='12'; -$item = array(); -$item['revision'] = 2; -$item['doc_id'] = 'DOCO'; -$item['name']='NIKO'; -$item['scope'] = ' Scoppppp'; -$item['status']=3; -$item['type'] ='K'; -$item['log_message']='This is a log message'; -$item['author_id']=1; -// $item['creation_ts']='This is a log message'; -// $item['modifier_id']='This is a log message'; -// $item['modification_ts']='This is a log message'; - -echo "
     {$method2test} - $$method2test(&\$rspecID,&\$item,)";echo "
    "; - - -new dBug($obj_mgr->$method2test($rspecID,$item)); - -?> \ No newline at end of file + Class Under Test : {$classUnderTest} "; +echo "
     {$classUnderTest}.class - constructor - $classUnderTest(&\$db)";
    +echo "
    "; +$obj_mgr = new $classUnderTest($db); +new dBug($obj_mgr); + +$method2test = "create_revision"; +$rspecID = '12'; +$item = array(); +$item['revision'] = 2; +$item['doc_id'] = 'DOCO'; +$item['name'] = 'NIKO'; +$item['scope'] = ' Scoppppp'; +$item['status'] = 3; +$item['type'] = 'K'; +$item['log_message'] = 'This is a log message'; +$item['author_id'] = 1; + +echo "
     {$method2test} - $$method2test(&\$rspecID,&\$item,)";
    +echo "
    "; + +new dBug($obj_mgr->$method2test($rspecID, $item)); + +?> diff --git a/lib/functions/code_testing/testcase.class.test.php b/lib/functions/code_testing/testcase.class.test.php index 28669208b2..fac3d15434 100644 --- a/lib/functions/code_testing/testcase.class.test.php +++ b/lib/functions/code_testing/testcase.class.test.php @@ -1,365 +1,367 @@ - testcase - constructor - testcase(&\$db)";echo ""; -$tcase_mgr = new testcase($db); - -$steps = array('notes' => array(1 => 'Lone Ranger',23 => 'Kimosawi'), - 'status' => array(1 => 'f', 23 => 'p')); - - -$context = new stdClass(); -$context->testplan_id = 2; -$context->platform_id = 0; -$context->build_id = 20; -$context->tester_id = 10; - - -$stepsID = array_keys($steps['notes']); -//$tcase_mgr->deleteStepsPartialExec($stepsID,$context); - -$m2r = 'saveStepsPartialExec'; -echo '
    Testing:' . $m2r . '
    '; -var_dump($steps,$context); -$tcase_mgr->$m2r($steps,$context); - -$m2r = 'getStepsPartialExec'; -echo '
    Testing:' . $m2r . '
    '; -var_dump($tcase_mgr->$m2r($stepsID,$context) ); - -die(); - - - -die(); - - -// new dBug($tcase_mgr); - -try -{ - $fullEID = 'PTJ09-1'; - echo '
    Testing getInternalID with fullEID
    '; - $va = $tcase_mgr->getInternalID($fullEID); - new dBug($va); -} -catch (Exception $e) -{ - echo 'Message: ' .$e->getMessage(); -} - -try -{ - $EID = 1; - echo '
    Testing getInternalID with ONLY NUMERIC EID
    '; - $va = $tcase_mgr->getInternalID($EID); - new dBug($va); -} -catch (Exception $e) -{ - echo 'Message: ' .$e->getMessage(); -} - -try -{ - $EID = 1; - echo '
    Testing getInternalID with ONLY NUMERIC EID
    '; - $va = $tcase_mgr->getInternalID($EID, array('tproject_id' => 282)); - new dBug($va); -} -catch (Exception $e) -{ - echo 'Message: ' .$e->getMessage(); -} - - -die(); - - -$items = array(1628,1626,1616,392,531); -$va = $tcase_mgr->get_last_active_version($items); -new dBug($va); - -$va = $tcase_mgr->get_last_active_version($items[0]); -new dBug($va); - -$options = array('access_key' => 'id', 'max_field' => 'version'); -$va = $tcase_mgr->get_last_active_version($items,$options); -new dBug($options); -new dBug($items); -new dBug($va); - - -die(); - -// SELECT MAX(version) AS version, NH_TCVERSION.parent_id AS id FROM tcversions TCV -// JOIN nodes_hierarchy NH_TCVERSION ON NH_TCVERSION.id = TCV.id AND TCV.active=1 -// AND NH_TCVERSION.parent_id IN () GROUP BY NH_TCVERSION.parent_id ORDER BY NH_TCVERSION.parent_id , -1) called at [C:\usr\local\xampp-1.7.2\xampp\htdocs\head-20100315\lib\functions\database.class.php:593] -// #1 database->fetchRowsIntoMap(/* Class:testcase - Method: get_last_active_version - -// $old_article = file_get_contents('./old_article.txt'); -// $new_article = $_REQUEST['article']; /* Let's say that someone pasted a new article to html form */ -// -// $diff = xdiff_string_diff($old_article, $new_article, 1); -// if (is_string($diff)) { -// echo "Differences between two articles:\n"; -// echo $diff; -// } - -$version_a=1; -$version_b=2; - -$tcase_id=88; -$va = $tcase_mgr->get_by_id($tcase_id,null,'ALL','ALL',$version_a); -$vb = $tcase_mgr->get_by_id($tcase_id,null,'ALL','ALL',$version_b); - -new dBug($va); -new dBug($vb); -$diff = xdiff_string_diff($va[0]['summary'], $vb[0]['summary'], 1); -echo "Differences between two articles:\n"; -echo $diff; -die(); - - - -// getByPathName -// function getByPathName($pathName,$pathSeparator='::') -$pathName='ZATHURA::Holodeck::Apollo 10 Simulation::Unload::Full speed unload'; -$fname = 'getByPathName'; -echo "
     testcase - $fname(\$pathName,\$pathSeparator='::')";echo "
    "; -echo "
                $fname($pathName)";echo "
    "; -$result=$tcase_mgr->$fname($pathName); -new dBug($result); -die(); - - -$tcase_id=318; -$tplan_id=389; -$build_id=21; -$platform_id=5; -$version_id=testcase::ALL_VERSIONS; -// $options = array('getNoExecutions' => true); -$options = null; - -// function get_last_execution($id,$version_id,$tplan_id,$build_id,$platform_id,$options=null) -echo "
     testcase - get_last_execution(\$id,\$version_id,\$tplan_id,\$build_id,\$platform_id\$options=null)";echo "
    "; -echo "
                get_last_execution($tcase_id,$version_id,$tplan_id,$build_id,$platform_id,$options)";echo "
    "; -$last_execution=$tcase_mgr->get_last_execution($tcase_id,$version_id,$tplan_id,$build_id,$platform_id,$options); -new dBug($last_execution); -die(); - -$tcase_id=4; -echo "
     testcase - get_by_id(\$id,\$version_id = TC_ALL_VERSIONS, \$active_status='ALL',\$open_status='ALL')";echo "
    "; -echo "
                get_by_id($tcase_id)";echo "
    "; -$tcase_info=$tcase_mgr->get_by_id($tcase_id); -new dBug($tcase_info); - - -$set_of_tcase_id=array(4,6); -echo "
                get_by_id($set_of_tcase_id)";echo "
    "; -$set_of_tcase_info=$tcase_mgr->get_by_id($set_of_tcase_id); -new dBug($set_of_tcase_info); - -$tcase_name='Configuration'; -$method='get_by_name'; -$tsuite_name=''; -$tproject_name=''; -echo "
                $method('{$tcase_name}')";echo "
    "; -$info=$tcase_mgr->$method($tcase_name); -new dBug($info); - -$tcase_name='Configuration'; -$tsuite_name='Bugzilla'; -$tproject_name=''; -$method='get_by_name'; -echo "
                $method('{$tcase_name}','{$tsuite_name}')";echo "
    "; -$info=$tcase_mgr->$method($tcase_name,$tsuite_name); -new dBug($info); - -$tcase_name='Configuration'; -$tsuite_name='Bugzilla'; -$tproject_name='IMPORT_TEST'; -$method='get_by_name'; -echo "
                $method('{$tcase_name}','{$tsuite_name}','{$tproject_name}')";echo "
    "; -$info=$tcase_mgr->$method($tcase_name,$tsuite_name,$tproject_name); -new dBug($info); - - -die(); - - - -$tcase_id=4; -echo "
     testcase - check_link_and_exec_status(\$id)";echo "
    "; -echo "
                check_link_and_exec_status($tcase_id)";echo "
    "; -$link_and_exec_status=$tcase_mgr->check_link_and_exec_status($tcase_id); -new dBug($link_and_exec_status); - - -echo "
     testcase - get_linked_versions(\$id,\$exec_status='ALL',\$active_status='ALL')";
    -echo "
                get_linked_versions($tcase_id)";
    -$linked_versions=$tcase_mgr->get_linked_versions($tcase_id);
    -new dBug($linked_versions);
    -
    -$tcase_id=4;
    -echo "
     testcase - get_testproject(\$id)";
    -echo "
                get_testproject($tcase_id)";
    -$testproject_id=$tcase_mgr->get_testproject($tcase_id);
    -new dBug("testproject id=" . $testproject_id);
    -
    -
    -$tcase_id=4;
    -echo "
     testcase - get_last_version_info(\$id)";
    -echo "
                get_last_version_info($tcase_id)";
    -$last_version_info=$tcase_mgr->get_last_version_info($tcase_id);
    -new dBug($last_version_info);
    -
    -
    -echo "
     testcase - get_versions_status_quo(\$id,\$tcversion_id=null, \$testplan_id=null)";
    -echo "
                get_versions_status_quo($tcase_id)";
    -$status_quo=$tcase_mgr->get_versions_status_quo($tcase_id);
    -new dBug($status_quo);
    -
    -
    -echo "
     testcase - get_exec_status(\$id)";
    -echo "
                get_exec_status($tcase_id)";
    -$testcase_exec_status=$tcase_mgr->get_exec_status($tcase_id);
    -new dBug($testcase_exec_status);
    -
    -
    -echo "
     testcase - getKeywords(\$tcID,\$kwID = null)";echo "
    "; -echo "
                getKeywords($tcase_id)";echo "
    "; -$keywords=$tcase_mgr->getKeywords($tcase_id); -new dBug($keywords); - - -echo "
     testcase - get_keywords_map(\$id,\$order_by_clause='')";echo "
    "; -$tcase_id=4; -echo "
                   get_keywords_map($tcase_id)";echo "
    "; -$keywords_map=$tcase_mgr->get_keywords_map($tcase_id); -new dBug($keywords_map); - - -$tcase_id=4; -$version_id=5; -$tplan_id=8; -$build_id=1; + testcase - constructor - testcase(&\$db)"; +echo "
    "; +$tcaseMgr = new testcase($db); + +$steps = array( + 'notes' => array( + 1 => 'Lone Ranger', + 23 => 'Kimosawi' + ), + 'status' => array( + 1 => 'f', + 23 => 'p' + ) +); + +$context = new stdClass(); +$context->testplan_id = 2; +$context->platform_id = 0; +$context->build_id = 20; +$context->tester_id = 10; + +$stepsID = array_keys($steps['notes']); + +$m2r = 'saveStepsPartialExec'; +echo '
    Testing:' . $m2r . '
    '; +var_dump($steps, $context); +$tcaseMgr->$m2r($steps, $context); + +$m2r = 'getStepsPartialExec'; +echo '
    Testing:' . $m2r . '
    '; +var_dump($tcaseMgr->$m2r($stepsID, $context)); + +die(); + +die(); + +try { + $fullEID = 'PTJ09-1'; + echo '
    Testing getInternalID with fullEID
    '; + $va = $tcaseMgr->getInternalID($fullEID); + new dBug($va); +} catch (Exception $e) { + echo 'Message: ' . $e->getMessage(); +} + +try { + $EID = 1; + echo '
    Testing getInternalID with ONLY NUMERIC EID
    '; + $va = $tcaseMgr->getInternalID($EID); + new dBug($va); +} catch (Exception $e) { + echo 'Message: ' . $e->getMessage(); +} + +try { + $EID = 1; + echo '
    Testing getInternalID with ONLY NUMERIC EID
    '; + $va = $tcaseMgr->getInternalID($EID, array( + 'tproject_id' => 282 + )); + new dBug($va); +} catch (Exception $e) { + echo 'Message: ' . $e->getMessage(); +} + +die(); + +$items = array( + 1628, + 1626, + 1616, + 392, + 531 +); +$va = $tcaseMgr->get_last_active_version($items); +new dBug($va); + +$va = $tcaseMgr->get_last_active_version($items[0]); +new dBug($va); + +$options = array( + 'access_key' => 'id', + 'max_field' => 'version' +); +$va = $tcaseMgr->get_last_active_version($items, $options); +new dBug($options); +new dBug($items); +new dBug($va); + +die(); + +// SELECT MAX(version) AS version, NH_TCVERSION.parent_id AS id FROM tcversions TCV +// JOIN nodes_hierarchy NH_TCVERSION ON NH_TCVERSION.id = TCV.id AND TCV.active=1 +// AND NH_TCVERSION.parent_id IN () GROUP BY NH_TCVERSION.parent_id ORDER BY NH_TCVERSION.parent_id , -1) called at [C:\usr\local\xampp-1.7.2\xampp\htdocs\head-20100315\lib\functions\database.class.php:593] +// #1 database->fetchRowsIntoMap(/* Class:testcase - Method: get_last_active_version + +$version_a = 1; +$version_b = 2; + +$tcase_id = 88; +$va = $tcaseMgr->get_by_id($tcase_id, null, 'ALL', 'ALL', $version_a); +$vb = $tcaseMgr->get_by_id($tcase_id, null, 'ALL', 'ALL', $version_b); + +new dBug($va); +new dBug($vb); +$diff = xdiff_string_diff($va[0]['summary'], $vb[0]['summary'], 1); +echo "Differences between two articles:\n"; +echo $diff; +die(); + +// getByPathName +// function getByPathName($pathName,$pathSeparator='::') +$pathName = 'ZATHURA::Holodeck::Apollo 10 Simulation::Unload::Full speed unload'; +$fname = 'getByPathName'; +echo "
     testcase - $fname(\$pathName,\$pathSeparator='::')";
    +echo "
    "; +echo "
                $fname($pathName)";
    +echo "
    "; +$result = $tcaseMgr->$fname($pathName); +new dBug($result); +die(); + +$tcase_id = 318; +$tplan_id = 389; +$build_id = 21; +$platform_id = 5; +$version_id = testcase::ALL_VERSIONS; +$options = null; + +// function getLastExecution($id,$version_id,$tplan_id,$build_id,$platform_id,$options=null) +echo "
     testcase - getLastExecution(\$id,\$version_id,\$tplan_id,\$build_id,\$platform_id\$options=null)";
    +echo "
    "; +echo "
                getLastExecution($tcase_id,$version_id,$tplan_id,$build_id,$platform_id,$options)";
    +echo "
    "; +$last_execution = $tcaseMgr->getLastExecution($tcase_id, $version_id, $tplan_id, + $build_id, $platform_id, $options); +new dBug($last_execution); +die(); + +$tcase_id = 4; +echo "
     testcase - get_by_id(\$id,\$version_id = TC_ALL_VERSIONS, \$active_status='ALL',\$open_status='ALL')";
    +echo "
    "; +echo "
                get_by_id($tcase_id)";
    +echo "
    "; +$tcase_info = $tcaseMgr->get_by_id($tcase_id); +new dBug($tcase_info); + +$set_of_tcase_id = array( + 4, + 6 +); +echo "
                get_by_id($set_of_tcase_id)";
    +echo "
    "; +$set_of_tcase_info = $tcaseMgr->get_by_id($set_of_tcase_id); +new dBug($set_of_tcase_info); + +$tcase_name = 'Configuration'; +$method = 'get_by_name'; +$tsuite_name = ''; +$tproject_name = ''; +echo "
                $method('{$tcase_name}')";
    +echo "
    "; +$info = $tcaseMgr->$method($tcase_name); +new dBug($info); + +$tcase_name = 'Configuration'; +$tsuite_name = 'Bugzilla'; +$tproject_name = ''; +$method = 'get_by_name'; +echo "
                $method('{$tcase_name}','{$tsuite_name}')";
    +echo "
    "; +$info = $tcaseMgr->$method($tcase_name, $tsuite_name); +new dBug($info); + +$tcase_name = 'Configuration'; +$tsuite_name = 'Bugzilla'; +$tproject_name = 'IMPORT_TEST'; +$method = 'get_by_name'; +echo "
                $method('{$tcase_name}','{$tsuite_name}','{$tproject_name}')";
    +echo "
    "; +$info = $tcaseMgr->$method($tcase_name, $tsuite_name, $tproject_name); +new dBug($info); + +die(); + +$tcase_id = 4; +echo "
     testcase - check_link_and_exec_status(\$id)";
    +echo "
    "; +echo "
                check_link_and_exec_status($tcase_id)";
    +echo "
    "; +$link_and_exec_status = $tcaseMgr->checkLinkAndExecStatus($tcase_id); +new dBug($link_and_exec_status); + +echo "
     testcase - get_linked_versions(\$id,\$exec_status='ALL',\$active_status='ALL')";
    +echo "
                get_linked_versions($tcase_id)";
    +$linked_versions = $tcaseMgr->get_linked_versions($tcase_id);
    +new dBug($linked_versions);
    +
    +$tcase_id = 4;
    +echo "
     testcase - get_testproject(\$id)";
    +echo "
                get_testproject($tcase_id)";
    +$testproject_id = $tcaseMgr->get_testproject($tcase_id);
    +new dBug("testproject id=" . $testproject_id);
    +
    +$tcase_id = 4;
    +echo "
     testcase - getLastVersionInfo(\$id)";
    +echo "
                getLastVersionInfo($tcase_id)";
    +$last_version_info = $tcaseMgr->getLastVersionInfo($tcase_id);
    +new dBug($last_version_info);
    +
    +echo "
     testcase - get_versions_status_quo(\$id,\$tcversion_id=null, \$testplan_id=null)";
    +echo "
                get_versions_status_quo($tcase_id)";
    +$status_quo = $tcaseMgr->getVersionsStatusQuo($tcase_id);
    +new dBug($status_quo);
    +
    +echo "
     testcase - get_exec_status(\$id)";
    +echo "
                get_exec_status($tcase_id)";
    +$testcase_exec_status = $tcaseMgr->getExecStatus($tcase_id);
    +new dBug($testcase_exec_status);
    +
    +echo "
     testcase - getKeywords(\$tcID,\$kwID = null)";
    +echo "
    "; +echo "
                getKeywords($tcase_id)";
    +echo "
    "; +$keywords = $tcaseMgr->getKeywords($tcase_id); +new dBug($keywords); + +echo "
     testcase - get_keywords_map(\$id,\$order_by_clause='')";
    +echo "
    "; +$tcase_id = 4; +echo "
                   get_keywords_map($tcase_id)";
    +echo "
    "; +$keywords_map = $tcaseMgr->get_keywords_map($tcase_id); +new dBug($keywords_map); + +$tcase_id = 4; +$version_id = 5; +$tplan_id = 8; +$build_id = 1; echo "
     testcase - get_executions(\$id,\$version_id,\$tplan_id,\$build_id,
    - \$exec_id_order='DESC',\$exec_to_exclude=null)";echo "
    "; - -echo "
                get_executions($tcase_id,$version_id,$tplan_id,$build_id)";echo "
    "; -$executions=$tcase_mgr->get_executions($tcase_id,$version_id,$tplan_id,$build_id); -new dBug($executions); - - -echo "
     testcase - get_last_execution(\$id,\$version_id,\$tplan_id,\$build_id,\$get_no_executions=0)";echo "
    "; -echo "
                get_last_execution($tcase_id,$version_id,$tplan_id,$build_id)";echo "
    "; -$last_execution=$tcase_mgr->get_last_execution($tcase_id,$version_id,$tplan_id,$build_id); -new dBug($last_execution); - - - - -$tcversion_id=5; -$tplan_id=8; -echo "
     testcase - get_version_exec_assignment(\$tcversion_id,\$tplan_id)";echo "
    "; -echo "
                get_version_exec_assignment($tcversion_id,$tplan_id)";echo "
    "; -$version_exec_assignment=$tcase_mgr->get_version_exec_assignment($tcversion_id,$tplan_id); -new dBug($version_exec_assignment); - - -echo "
     testcase - get_linked_cfields_at_design(\$id,\$parent_id=null,\$show_on_execution=null)";echo "
    "; -echo "
                get_linked_cfields_at_design($tcase_id)";echo "
    "; -$linked_cfields_at_design=$tcase_mgr->get_linked_cfields_at_design($tcase_id); -new dBug($linked_cfields_at_design); - - - + \$exec_id_order='DESC',\$exec_to_exclude=null)"; +echo "
    "; + +echo "
                get_executions($tcase_id,$version_id,$tplan_id,$build_id)";
    +echo "
    "; +$executions = $tcaseMgr->get_executions($tcase_id, $version_id, $tplan_id, + $build_id); +new dBug($executions); + +echo "
     testcase - getLastExecution(\$id,\$version_id,\$tplan_id,\$build_id,\$get_no_executions=0)";
    +echo "
    "; +echo "
                getLastExecution($tcase_id,$version_id,$tplan_id,$build_id)";
    +echo "
    "; +$last_execution = $tcaseMgr->getLastExecution($tcase_id, $version_id, $tplan_id, + $build_id); +new dBug($last_execution); + +$tcversion_id = 5; +$tplan_id = 8; +echo "
     testcase - getVersionExecAssignment(\$tcversion_id,\$tplan_id)";
    +echo "
    "; +echo "
                getVersionExecAssignment($tcversion_id,$tplan_id)";
    +echo "
    "; +$version_exec_assignment = $tcaseMgr->getVersionExecAssignment($tcversion_id, + $tplan_id); +new dBug($version_exec_assignment); + +echo "
     testcase - get_linked_cfields_at_design(\$id,\$parent_id=null,\$show_on_execution=null)";
    +echo "
    "; +echo "
                get_linked_cfields_at_design($tcase_id)";
    +echo "
    "; +$linked_cfields_at_design = $tcaseMgr->get_linked_cfields_at_design($tcase_id); +new dBug($linked_cfields_at_design); + echo "
     testcase - get_linked_cfields_at_execution(\$id,\$parent_id=null,
    \$show_on_execution=null,
    - \$execution_id=null,\$testplan_id=null)";echo "
    "; -echo "
                get_linked_cfields_at_execution($tcase_id)";echo "
    "; -$linked_cfields_at_execution=$tcase_mgr->get_linked_cfields_at_execution($tcase_id); -new dBug($linked_cfields_at_execution); - - - -echo "
     testcase - html_table_of_custom_field_inputs(\$id,\$parent_id=null,\$scope='design',\$name_suffix='')";echo "
    "; -echo "
                html_table_of_custom_field_inputs($tcase_id)";echo "
    "; -$table_of_custom_field_inputs=$tcase_mgr->html_table_of_custom_field_inputs($tcase_id); -echo "
    "; echo $table_of_custom_field_inputs; echo "
    "; - - + \$execution_id=null,\$testplan_id=null)"; +echo "
    "; +echo "
                get_linked_cfields_at_execution($tcase_id)";
    +echo "
    "; +$linked_cfields_at_execution = $tcaseMgr->get_linked_cfields_at_execution( + $tcase_id); +new dBug($linked_cfields_at_execution); + +echo "
     testcase - html_table_of_custom_field_inputs(\$id,\$parent_id=null,\$scope='design',\$name_suffix='')";
    +echo "
    "; +echo "
                html_table_of_custom_field_inputs($tcase_id)";
    +echo "
    "; +$table_of_custom_field_inputs = $tcaseMgr->html_table_of_custom_field_inputs( + $tcase_id); +echo "
    ";
    +echo $table_of_custom_field_inputs;
    +echo "
    "; + echo "
     testcase - html_table_of_custom_field_values(\$id,\$scope='design',
    \$show_on_execution=null,
    - \$execution_id=null,\$testplan_id=null) ";echo "
    "; - -echo "
     testcase - html_table_of_custom_field_values($tcase_id)";echo "
    "; -$table_of_custom_field_values=$tcase_mgr->html_table_of_custom_field_values($tcase_id); -echo "
    "; echo $table_of_custom_field_values; echo "
    "; - - - - - - - - -/* - function testcase(&$db) -function get_by_name($name) -function get_all() -function show(&$smarty,$id, $user_id, $version_id=TC_ALL_VERSIONS, $action='', -function update($id,$tcversion_id,$name,$summary,$steps, -function check_link_and_exec_status($id) -function delete($id,$version_id = TC_ALL_VERSIONS) -function get_linked_versions($id,$exec_status="ALL",$active_status='ALL') -function _blind_delete($id,$version_id=TC_ALL_VERSIONS,$children=null) -function _execution_delete($id,$version_id=TC_ALL_VERSIONS,$children=null) -function get_testproject($id) -function copy_to($id,$parent_id,$user_id, -function create_new_version($id,$user_id) -function get_last_version_info($id) -function copy_tcversion($from_tcversion_id,$to_tcversion_id,$as_version_number,$user_id) -function get_by_id_bulk($id,$version_id=TC_ALL_VERSIONS, $get_active=0, $get_open=0) -function get_by_id($id,$version_id = TC_ALL_VERSIONS, $active_status='ALL',$open_status='ALL') -function get_versions_status_quo($id, $tcversion_id=null, $testplan_id=null) -function get_exec_status($id) -function getKeywords($tcID,$kwID = null) -function get_keywords_map($id,$order_by_clause='') -function addKeyword($id,$kw_id) -function addKeywords($id,$kw_ids) -function copyKeywordsTo($id,$destID) -function deleteKeywords($tcID,$kwID = null) -function get_executions($id,$version_id,$tplan_id,$build_id,$exec_id_order='DESC',$exec_to_exclude=null) -function get_last_execution($id,$version_id,$tplan_id,$build_id,$get_no_executions=0) -function exportTestCaseDataToXML($tcase_id,$tcversion_id,$bNoXMLHeader = false,$optExport = array()) -function get_version_exec_assignment($tcversion_id,$tplan_id) -function update_active_status($id,$tcversion_id,$active_status) -function copy_attachments($source_id,$target_id) -function deleteAttachments($id) -function get_linked_cfields_at_design($id,$parent_id=null,$show_on_execution=null) -function html_table_of_custom_field_inputs($id,$parent_id=null,$scope='design',$name_suffix='') -function html_table_of_custom_field_values($id,$scope='design',$show_on_execution=null, -function get_linked_cfields_at_execution($id,$parent_id=null,$show_on_execution=null, -function copy_cfields_design_values($from_id,$to_id) -*/?> + \$execution_id=null,\$testplan_id=null) "; +echo "
    "; + +echo "
     testcase - html_table_of_custom_field_values($tcase_id)";
    +echo "
    "; +$table_of_custom_field_values = $tcaseMgr->html_table_of_custom_field_values( + $tcase_id); +echo "
    ";
    +echo $table_of_custom_field_values;
    +echo "
    "; + +/* + * function testcase(&$db) + * function get_by_name($name) + * function get_all() + * function show(&$smarty,$id, $user_id, $version_id=TC_ALL_VERSIONS, $action='', + * function update($id,$tcversion_id,$name,$summary,$steps, + * function check_link_and_exec_status($id) + * function delete($id,$version_id = TC_ALL_VERSIONS) + * function get_linked_versions($id,$exec_status="ALL",$active_status='ALL') + * function _blind_delete($id,$version_id=TC_ALL_VERSIONS,$children=null) + * function _execution_delete($id,$version_id=TC_ALL_VERSIONS,$children=null) + * function get_testproject($id) + * function copy_to($id,$parent_id,$user_id, + * function create_new_version($id,$user_id) + * function getLastVersionInfo($id) + * function copy_tcversion($from_tcversion_id,$to_tcversion_id,$as_version_number,$user_id) + * function get_by_id_bulk($id,$version_id=TC_ALL_VERSIONS, $get_active=0, $get_open=0) + * function get_by_id($id,$version_id = TC_ALL_VERSIONS, $active_status='ALL',$open_status='ALL') + * function get_versions_status_quo($id, $tcversion_id=null, $testplan_id=null) + * function get_exec_status($id) + * function getKeywords($tcID,$kwID = null) + * function get_keywords_map($id,$order_by_clause='') + * function addKeyword($id,$kw_id) + * function addKeywords($id,$kw_ids) + * function copyKeywordsTo($id,$destID) + * function deleteKeywords($tcID,$kwID = null) + * function get_executions($id,$version_id,$tplan_id,$build_id,$exec_id_order='DESC',$exec_to_exclude=null) + * function getLastExecution($id,$version_id,$tplan_id,$build_id,$get_no_executions=0) + * function exportTestCaseDataToXML($tcase_id,$tcversion_id,$bNoXMLHeader = false,$optExport = array()) + * function getVersionExecAssignment($tcversion_id,$tplan_id) + * function update_active_status($id,$tcversion_id,$active_status) + * function copy_attachments($source_id,$target_id) + * function deleteAttachments($id) + * function get_linked_cfields_at_design($id,$parent_id=null,$show_on_execution=null) + * function html_table_of_custom_field_inputs($id,$parent_id=null,$scope='design',$name_suffix='') + * function html_table_of_custom_field_values($id,$scope='design',$show_on_execution=null, + * function get_linked_cfields_at_execution($id,$parent_id=null,$show_on_execution=null, + * function copyCfieldsDesignValues($from_id,$to_id) + */ +?> diff --git a/lib/functions/code_testing/testplan.class.test.php b/lib/functions/code_testing/testplan.class.test.php index 89d94b2713..45edc30451 100644 --- a/lib/functions/code_testing/testplan.class.test.php +++ b/lib/functions/code_testing/testplan.class.test.php @@ -1,339 +1,269 @@ -Poor's Man - $object_item - code inspection tool
    "; -echo "
    Scope of this page is allow you to understand with live
    "; -echo "examples how to use object: $object_item (implemented in file $object_class_file.class.php)
    "; -echo "Important:"; -echo "You are using your testlink DB to do all operations"; -echo "
    "; -echo "
    "; -echo "
     $object_item - constructor - $object_class(&\$db)";echo "
    "; -$obj_mgr=new $object_class($db); -new dBug($obj_mgr); - -// echo "
     testplan - get_linked_tcversions(\$tplan_id,\$tcase_id=null,\$keyword_id=0,\$executed=null,
    -//                                              \$assigned_to=null,\$exec_status=null,\$build_id=0,
    -//                                              \$cf_hash = null)";echo "
    "; -// - -$tplan_id = 1212; -echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";echo "
    "; -echo "
     get_linked_tcversions($tplan_id)";echo "
    "; -$linked_tcversions=$obj_mgr->get_linked_tcversions($tplan_id); -new dBug($linked_tcversions); - -$options = array('output' => 'mapOfMap'); -echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";echo "
    "; - -echo "
     get_linked_tcversions($tplan_id,null,$options)";echo "
    "; -new dBug($options); -$linked_tcversions=$obj_mgr->get_linked_tcversions($tplan_id,null,$options); -new dBug($linked_tcversions); - -$options = array('output' => 'array'); -echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";echo "
    "; - -echo "
     get_linked_tcversions($tplan_id,null,$options)";echo "
    "; -new dBug($options); -$linked_tcversions=$obj_mgr->get_linked_tcversions($tplan_id,null,$options); -new dBug($linked_tcversions); - -die(); - - - - - - - - - -$target_testproject=new stdClass(); -$target_testproject->name='Testplan Class Unit Test'; -$target_testproject->color=''; - -$target_testproject->options=new stdClass(); -$target_testproject->options->requirement_mgmt=1; -$target_testproject->options->priority_mgmt=1; -$target_testproject->options->automated_execution=1; - -$target_testproject->notes='Created to run testplan unit tests on '; -$target_testproject->active=1; -$target_testproject->tcasePrefix='TPlanUnitTest'; - -// Create a test project that will be Test plan parent -$tproject_mgr=new testproject($db); -$info=$tproject_mgr->get_by_name($target_testproject->name); -if( !is_null($info) ) -{ - $name=$info[0]['name']; - echo "TestProject with name {$name} exists!
    Will be deleted and re-created"; - $tproject_mgr->delete($info[0]['id']); -} -$testproject_id=$tproject_mgr->create($target_testproject->name, - $target_testproject->color, - $target_testproject->options, - $target_testproject->notes,$target_testproject->active, - $target_testproject->tcasePrefix); - - -$testplan = new stdClass(); -$testplan->name='Test Plan Code Testing'; -$testplan->notes='Test Plan created running Code Testing code by TestLink Development Team'; -echo "
     {$object_class} - create(\$name,\$notes,\$testproject_id)";echo "
    "; -echo "
     {$object_class} - create('$testplan->name','$testplan->notes',$testproject_id)";echo "
    "; -$testplan->id=$obj_mgr->create($testplan->name,$testplan->notes,$testproject_id); -$info=$obj_mgr->get_by_id($testplan->id); -new dBug($info); - - - - -// --------------------------------------------------------------------------------------------------------- -// Build Manager -// --------------------------------------------------------------------------------------------------------- -// Support Object -$tplan_mgr=new testplan($db); - -$object_item="Build Manager"; -$object_class="build_mgr"; -$object_class_file="testplan"; - -echo "
    Poor's Man - $object_item - code inspection tool
    "; -echo "
    Scope of this page is allow you to understand with live
    "; -echo "examples how to use object: $object_item (implemented in file $object_class_file.class.php)
    "; -echo "Important:"; -echo "You are using your testlink DB to do all operations"; -echo "
    "; -echo "
    "; -echo "
     $object_item - constructor - $object_class(&\$db)";echo "
    "; -$obj_mgr=new $object_class($db); -new dBug($obj_mgr); - - - - - - -$build = new stdClass(); -$build->name='Build Code Testing'; -$build->notes='Build created running Code Testing code by TestLink Development Team'; - -echo "
     {$object_class} - create(\$tplan_id,\$name,\$notes = '',\$active=1,\$open=1)";echo "
    "; -echo "
     {$object_class} - create($testplan->id,'$build->name','$build->notes')";echo "
    "; -$build->id=$obj_mgr->create($testplan->id,$build->name,$build->notes); -$info=$obj_mgr->get_by_id($build->id); -new dBug($info); - -echo "
     Check Build existence using testplan manager method 'get_builds()'";echo "
    "; -echo "
     testplan - get_builds(\$testplan_id,\$active=null,\$open=null)";echo "
    "; -echo "
                get_builds($testplan->id)";echo "
    "; -$all_builds=$tplan_mgr->get_builds($testplan->id); -new dBug($all_builds); - -// Final Clean-Up -$tproject_mgr->delete($testproject_id); -die(); - - -// OLD CODE MUST BE REFACTORED -echo "
     testplan - get_all()";echo "
    "; -$all_testplans_on_tl=$tplan_mgr->get_all(); -new dBug($all_testplans_on_tl); - -$tplan_id=-1; -if( !is_null($all_testplans_on_tl) ) -{ - $tplan_id=$all_testplans_on_tl[0]['id']; -} - -echo "
     testplan - get_by_id(\$id)";echo "
    "; -echo "
                get_by_id($tplan_id)";echo "
    "; -$tplan_info=$tplan_mgr->get_by_id($tplan_id); -new dBug($tplan_info); - - -$tplan_name="TEST_TESTPLAN"; -echo "
     testplan - get_by_name(\$name,\$tproject_id = 0)";echo "
    "; -echo "
                get_by_name($tplan_name)";echo "
    "; -$tplan_info=$tplan_mgr->get_by_name($tplan_name); -new dBug($tplan_info); - - -echo "
     testplan - get_builds(\$tplan_id,\$active=null,\$open=null)";echo "
    "; -echo "
                get_builds($tplan_id)";echo "
    "; -$all_builds=$tplan_mgr->get_builds($tplan_id); -new dBug($all_builds); - - -echo "
     testplan - count_testcases(\$tplan_id)";echo "
    "; -echo "
                count_testcases($tplan_id)";echo "
    "; -$count_testcases=$tplan_mgr->count_testcases($tplan_id); -new dBug("Number of testcase linked to test plan=" . $count_testcases); - -// echo "
     testplan - get_linked_tcversions(\$tplan_id,\$tcase_id=null,\$keyword_id=0,\$executed=null,
    -//                                              \$assigned_to=null,\$exec_status=null,\$build_id=0,
    -//                                              \$cf_hash = null)";echo "
    "; -// - - -echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";echo "
    "; - -echo "
                get_linked_tcversions($tplan_id)";echo "
    "; -$linked_tcversions=$tplan_mgr->get_linked_tcversions($tplan_id); -new dBug($linked_tcversions); - - - -// ------------------------------------------------------------------------------------------- -echo "

    Build Manager Class

    "; -echo "
     build manager - constructor - build_mgr(&\$db)";echo "
    "; -$build_mgr=new build_mgr($db); -new dBug($build_mgr); - - -$all_builds=$tplan_mgr->get_builds($tplan_id); -$dummy=array_keys($all_builds); -$build_id=$dummy[0]; - -echo "
     build manager - get_by_id(\$id)";echo "
    "; -echo "
                     get_by_id($build_id)";echo "
    "; -$build_info=$build_mgr->get_by_id($build_id); -new dBug($build_info); - - - - - - -/* - -// getKeywords($testproject_id,$keywordID = null) -$tplan_id=1; -echo "
     testplan - getKeywords(\$testproject_id,\$keywordID = null)";echo "
    "; -echo "
                   getKeywords($tplan_id)";echo "
    "; -$keywords=$tplan_mgr->getKeywords($tplan_id); -new dBug($keywords); - - -echo "
     testplan - get_keywords_map(\$testproject_id)";echo "
    "; -$tplan_id=1; -echo "
                   get_keywords_map($tplan_id)";echo "
    "; -$keywords_map=$tplan_mgr->get_keywords_map($tplan_id); -new dBug($keywords_map); - - -echo "
     testplan - get_keywords_tcases(\$testproject_id, \$keyword_id=0)";echo "
    "; -echo "
                   get_keywords_tcases($tplan_id)";echo "
    "; -$keywords_tcases=$tplan_mgr->get_keywords_tcases($tplan_id); -new dBug($keywords_tcases); - - -echo "
     testplan - get_linked_custom_fields(\$id,\$node_type=null)";echo "
    "; -echo "
                   get_linked_custom_fields($tplan_id)";echo "
    "; -$linked_custom_fields=$tplan_mgr->get_linked_custom_fields($tplan_id); -new dBug($linked_custom_fields); - - -echo "
     testplan - gen_combo_test_suites(\$id,\$exclude_branches=null,\$mode='dotted')";echo "
    "; -echo "
                   gen_combo_test_suites($tplan_id,null,'dotted')";echo "
    "; -$combo_test_suites=$tplan_mgr->gen_combo_test_suites($tplan_id,null,'dotted'); -new dBug($combo_test_suites); - -echo "
                   gen_combo_test_suites($tplan_id,null,'dotted')";echo "
    "; -$combo_test_suites=$tplan_mgr->gen_combo_test_suites($tplan_id,null,'array'); -new dBug($combo_test_suites); - - -echo "
     testplan - getReqSpec(\$testproject_id, \$id = null)";echo "
    "; -echo "
                   getReqSpec($tplan_id)";echo "
    "; -$requirement_spec=$tplan_mgr->getReqSpec($tplan_id); -new dBug($requirement_spec); - -$srs_id=2; -echo "
                   getReqSpec(\$tplan_id,\$srs_id)";echo "
    "; -echo "
                   getReqSpec($tplan_id,$srs_id)";echo "
    "; -$requirement_spec=$tplan_mgr->getReqSpec($tplan_id,$srs_id); -new dBug($requirement_spec); - - -$srs_title='SRS2'; -echo "
     testplan - get_srs_by_title(\$testproject_id,\$title,\$ignore_case=0)";echo "
    "; -echo "
                   get_srs_by_title($tplan_id,$srs_title)";echo "
    "; -$srs_by_title=$tplan_mgr->get_srs_by_title($tplan_id,$srs_title); -new dBug($srs_by_title); - -// function get_srs_by_title($testproject_id,$title,$ignore_case=0) -*/ - -/* -function count_testcases($id) - -function link_tcversions($id,&$items_to_link) -function get_linked_tcversions($id,$tcase_id=null,$keyword_id=0,$executed=null, -function get_linked_and_newest_tcversions($id,$tcase_id=null) -function unlink_tcversions($id,&$items) -function get_keywords_map($id,$order_by_clause='') -function get_keywords_tcases($id,$keyword_id=0) -function copy_as($id,$new_tplan_id,$tplan_name=null,$tproject_id=null) -function copy_builds($id,$new_tplan_id) -function copy_linked_tcversions($id,$new_tplan_id) -function copy_milestones($id,$new_tplan_id) -function get_milestones($id) -function copy_user_roles($id,$new_tplan_id) -function copy_priorities($id,$new_tplan_id) -function delete($id) -function get_builds_for_html_options($id,$active=null,$open=null) -function get_max_build_id($id,$active = null,$open = null) -function get_builds($id,$active=null,$open=null) -function _natsort_builds($builds_map) -function check_build_name_existence($tplan_id,$build_name,$case_sensitive=0) -function create_build($tplan_id,$name,$notes = '',$active=1,$open=1) -function get_linked_cfields_at_design($id,$parent_id=null,$show_on_execution=null) -function get_linked_cfields_at_execution($id,$parent_id=null,$show_on_execution=null) -function html_table_of_custom_field_inputs($id,$parent_id=null,$scope='design') -function html_table_of_custom_field_values($id,$scope='design',$show_on_execution=null) -} // function end -function insert_default_priorities($tplan_id) -function get_priority_rules($tplan_id,$do_lang_get=0) -function set_priority_rules($tplan_id,$priority_hash) -function filter_cf_selection ($tp_tcs, $cf_hash) - -function build_mgr(&$db) -function create($tplan_id,$name,$notes = '',$active=1,$open=1) -function update($id,$name,$notes,$active=null,$open=null) -function delete($id) -function get_by_id($id) -function milestone_mgr(&$db) -function create($tplan_id,$name,$date,$A,$B,$C) -function update($id,$name,$date,$A,$B,$C) -function delete($id) -function get_by_id($id) -function get_all_by_testplan($tplan_id) - - -*/ +Poor's Man - $object_item - code inspection tool
    "; +echo "
    Scope of this page is allow you to understand with live
    "; +echo "examples how to use object: $object_item (implemented in file $object_class_file.class.php)
    "; +echo "Important:"; +echo "You are using your testlink DB to do all operations"; +echo "
    "; +echo "
    "; +echo "
     $object_item - constructor - $object_class(&\$db)";
    +echo "
    "; +$obj_mgr = new $object_class($db); +new dBug($obj_mgr); + +$tplan_id = 1212; +echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";
    +echo "
    "; +echo "
     get_linked_tcversions($tplan_id)";
    +echo "
    "; +$linked_tcversions = $obj_mgr->get_linked_tcversions($tplan_id); +new dBug($linked_tcversions); + +$options = array( + 'output' => 'mapOfMap' +); +echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";
    +echo "
    "; + +echo "
     get_linked_tcversions($tplan_id,null,$options)";
    +echo "
    "; +new dBug($options); +$linked_tcversions = $obj_mgr->get_linked_tcversions($tplan_id, null, $options); +new dBug($linked_tcversions); + +$options = array( + 'output' => 'array' +); +echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";
    +echo "
    "; + +echo "
     get_linked_tcversions($tplan_id,null,$options)";
    +echo "
    "; +new dBug($options); +$linked_tcversions = $obj_mgr->get_linked_tcversions($tplan_id, null, $options); +new dBug($linked_tcversions); + +die(); + +$target_testproject = new stdClass(); +$target_testproject->name = 'Testplan Class Unit Test'; +$target_testproject->color = ''; + +$target_testproject->options = new stdClass(); +$target_testproject->options->requirement_mgmt = 1; +$target_testproject->options->priority_mgmt = 1; +$target_testproject->options->automated_execution = 1; + +$target_testproject->notes = 'Created to run testplan unit tests on '; +$target_testproject->active = 1; +$target_testproject->tcasePrefix = 'TPlanUnitTest'; + +// Create a test project that will be Test plan parent +$tproject_mgr = new testproject($db); +$info = $tproject_mgr->get_by_name($target_testproject->name); +if (! is_null($info)) { + $name = $info[0]['name']; + echo "TestProject with name {$name} exists!
    Will be deleted and re-created"; + $tproject_mgr->delete($info[0]['id']); +} +$testproject_id = $tproject_mgr->create($target_testproject->name, + $target_testproject->color, $target_testproject->options, + $target_testproject->notes, $target_testproject->active, + $target_testproject->tcasePrefix); + +$testplan = new stdClass(); +$testplan->name = 'Test Plan Code Testing'; +$testplan->notes = 'Test Plan created running Code Testing code by TestLink Development Team'; +echo "
     {$object_class} - create(\$name,\$notes,\$testproject_id)";
    +echo "
    "; +echo "
     {$object_class} - create('$testplan->name','$testplan->notes',$testproject_id)";
    +echo "
    "; +$testplan->id = $obj_mgr->create($testplan->name, $testplan->notes, + $testproject_id); +$info = $obj_mgr->get_by_id($testplan->id); +new dBug($info); + +// --------------------------------------------------------------------------------------------------------- +// Build Manager +// --------------------------------------------------------------------------------------------------------- +// Support Object +$tplan_mgr = new testplan($db); + +$object_item = "Build Manager"; +$object_class = "build_mgr"; +$object_class_file = "testplan"; + +echo "
    Poor's Man - $object_item - code inspection tool
    "; +echo "
    Scope of this page is allow you to understand with live
    "; +echo "examples how to use object: $object_item (implemented in file $object_class_file.class.php)
    "; +echo "Important:"; +echo "You are using your testlink DB to do all operations"; +echo "
    "; +echo "
    "; +echo "
     $object_item - constructor - $object_class(&\$db)";
    +echo "
    "; +$obj_mgr = new $object_class($db); +new dBug($obj_mgr); + +$build = new stdClass(); +$build->name = 'Build Code Testing'; +$build->notes = 'Build created running Code Testing code by TestLink Development Team'; + +echo "
     {$object_class} - create(\$tplan_id,\$name,\$notes = '',\$active=1,\$open=1)";
    +echo "
    "; +echo "
     {$object_class} - create($testplan->id,'$build->name','$build->notes')";
    +echo "
    "; +$build->id = $obj_mgr->create($testplan->id, $build->name, $build->notes); +$info = $obj_mgr->get_by_id($build->id); +new dBug($info); + +echo "
     Check Build existence using testplan manager method 'get_builds()'";
    +echo "
    "; +echo "
     testplan - get_builds(\$testplan_id,\$active=null,\$open=null)";
    +echo "
    "; +echo "
                get_builds($testplan->id)";
    +echo "
    "; +$all_builds = $tplan_mgr->get_builds($testplan->id); +new dBug($all_builds); + +// Final Clean-Up +$tproject_mgr->delete($testproject_id); +die(); + +// OLD CODE MUST BE REFACTORED +echo "
     testplan - get_all()";
    +echo "
    "; +$all_testplans_on_tl = $tplan_mgr->getAll(); +new dBug($all_testplans_on_tl); + +$tplan_id = - 1; +if (! is_null($all_testplans_on_tl)) { + $tplan_id = $all_testplans_on_tl[0]['id']; +} + +echo "
     testplan - get_by_id(\$id)";
    +echo "
    "; +echo "
                get_by_id($tplan_id)";
    +echo "
    "; +$tplan_info = $tplan_mgr->get_by_id($tplan_id); +new dBug($tplan_info); + +$tplan_name = "TEST_TESTPLAN"; +echo "
     testplan - get_by_name(\$name,\$tproject_id = 0)";
    +echo "
    "; +echo "
                get_by_name($tplan_name)";
    +echo "
    "; +$tplan_info = $tplan_mgr->get_by_name($tplan_name); +new dBug($tplan_info); + +echo "
     testplan - get_builds(\$tplan_id,\$active=null,\$open=null)";
    +echo "
    "; +echo "
                get_builds($tplan_id)";
    +echo "
    "; +$all_builds = $tplan_mgr->get_builds($tplan_id); +new dBug($all_builds); + +echo "
     testplan - count_testcases(\$tplan_id)";
    +echo "
    "; +echo "
                count_testcases($tplan_id)";
    +echo "
    "; +$count_testcases = $tplan_mgr->count_testcases($tplan_id); +new dBug("Number of testcase linked to test plan=" . $count_testcases); + +echo "
     testplan - get_linked_tcversions(\$id,\$filters=null,\$options=null)";
    +echo "
    "; + +echo "
                get_linked_tcversions($tplan_id)";
    +echo "
    "; +$linked_tcversions = $tplan_mgr->get_linked_tcversions($tplan_id); +new dBug($linked_tcversions); + +// ------------------------------------------------------------------------------------------- +echo "

    Build Manager Class

    "; +echo "
     build manager - constructor - build_mgr(&\$db)";
    +echo "
    "; +$build_mgr = new build_mgr($db); +new dBug($build_mgr); + +$all_builds = $tplan_mgr->get_builds($tplan_id); +$dummy = array_keys($all_builds); +$build_id = $dummy[0]; + +echo "
     build manager - get_by_id(\$id)";
    +echo "
    "; +echo "
                     get_by_id($build_id)";
    +echo "
    "; +$build_info = $build_mgr->get_by_id($build_id); +new dBug($build_info); + +/* + * function count_testcases($id) + * + * function link_tcversions($id,&$items_to_link) + * function get_linked_tcversions($id,$tcase_id=null,$keyword_id=0,$executed=null, + * function get_linked_and_newest_tcversions($id,$tcase_id=null) + * function unlink_tcversions($id,&$items) + * function get_keywords_map($id,$order_by_clause='') + * function get_keywords_tcases($id,$keyword_id=0) + * function copy_as($id,$new_tplan_id,$tplan_name=null,$tproject_id=null) + * function copy_builds($id,$new_tplan_id) + * function copy_linked_tcversions($id,$new_tplan_id) + * function copy_milestones($id,$new_tplan_id) + * function get_milestones($id) + * function copy_user_roles($id,$new_tplan_id) + * function copy_priorities($id,$new_tplan_id) + * function delete($id) + * function get_builds_for_html_options($id,$active=null,$open=null) + * function get_max_build_id($id,$active = null,$open = null) + * function get_builds($id,$active=null,$open=null) + * function _natsort_builds($builds_map) + * function check_build_name_existence($tplan_id,$build_name,$case_sensitive=0) + * function create_build($tplan_id,$name,$notes = '',$active=1,$open=1) + * function get_linked_cfields_at_design($id,$parent_id=null,$show_on_execution=null) + * function get_linked_cfields_at_execution($id,$parent_id=null,$show_on_execution=null) + * function html_table_of_custom_field_inputs($id,$parent_id=null,$scope='design') + * function html_table_of_custom_field_values($id,$scope='design',$show_on_execution=null) + * } // function end + * function insert_default_priorities($tplan_id) + * function get_priority_rules($tplan_id,$do_lang_get=0) + * function set_priority_rules($tplan_id,$priority_hash) + * function filter_cf_selection ($tp_tcs, $cf_hash) + * + * function build_mgr(&$db) + * function create($tplan_id,$name,$notes = '',$active=1,$open=1) + * function update($id,$name,$notes,$active=null,$open=null) + * function delete($id) + * function get_by_id($id) + * function milestone_mgr(&$db) + * function create($tplan_id,$name,$date,$A,$B,$C) + * function update($id,$name,$date,$A,$B,$C) + * function delete($id) + * function get_by_id($id) + * function get_all_by_testplan($tplan_id) + * + * + */ ?> diff --git a/lib/functions/code_testing/testplan.getHits.test.php b/lib/functions/code_testing/testplan.getHits.test.php index 53210f28c6..96451a4edb 100644 --- a/lib/functions/code_testing/testplan.getHits.test.php +++ b/lib/functions/code_testing/testplan.getHits.test.php @@ -1,38 +1,38 @@ -Database:' . DB_NAME . '
    '; -$object_item="Testplan Manager"; -$object_class="testplan"; - -echo "
    Poor's Man - $object_item - code inspection tool
    "; -echo "
    Scope of this page is allow you to understand with live
    "; -echo "examples how to use object: $object_item (implemented in file $object_class_file.class.php)
    "; -echo "Important:"; -echo "You are using your testlink DB to do all operations"; -echo "
    "; -echo "
    "; -echo "
     $object_item - constructor - $object_class(&\$db)";echo "
    "; - -echo "
    "; +Database:' . DB_NAME . '
    '; +$object_item = "Testplan Manager"; +$object_class = "testplan"; + +echo "
    Poor's Man - $object_item - code inspection tool
    "; +echo "
    Scope of this page is allow you to understand with live
    "; +echo "examples how to use object: $object_item (implemented in file $object_class_file.class.php)
    "; +echo "Important:"; +echo "You are using your testlink DB to do all operations"; +echo "
    "; +echo "
    "; +echo "
     $object_item - constructor - $object_class(&\$db)";
    +echo "
    "; + +echo "
    "; echo <<TC-200 B1 1/FAILED TC-200 B2 1/FAILED TC-200 B3 1/BLOCKED - + TC-300 B1 3/Passed TC-300 B1 2/Passed TC-300 B1 1/Passed @@ -57,7 +57,7 @@ TC-300 B3 3/BLOCKED TC-300 B3 2/Passed TC-300 B3 1/FAILED - + TC-400 B1 2/FAILED = TC-400 B1 1/BLOCKED TC-400 B2 1/FAILED = @@ -69,273 +69,276 @@ TC-500 B2 0 / NOT RUN TC-500 B3 0 / NOT RUN -STUFF; -// ================================================================================ - - -$obj_mgr=new $object_class($db); -// new dBug($obj_mgr); - - -$tplan_id = 33123; -$platform_id = 0; - -echo '

    Test group conditions

    '; -echo 'Test Plan ID:' . $tplan_id . '
    '; -echo 'Platform ID:' . $platform_id . '
    '; -echo '
    '; - - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsStatusSetOnLatestExecution'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- - -$statusMixed = 'n'; -try -{ - $$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusMixed); -} -catch (Exception $e) -{ - echo $e->getMessage(); -} -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - - -$statusMixed = array('n','p'); -try -{ - $$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusMixed); -} -catch (Exception $e) -{ - echo $e->getMessage(); -} -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - -$statusMixed = array('b','p'); -try -{ - $$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusMixed); -} -catch (Exception $e) -{ - echo $e->getMessage(); -} -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - - -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsStatusSetOnBuild'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- -$build_id = 26; // 26,27,28 -echo 'Build ID:' . $build_id . '
    '; - -$statusMixed = 'p'; -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$build_id,$statusMixed); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - -$statusMixed = array('p','b'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$build_id,$statusMixed); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - -$statusMixed = array('f','b'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$build_id,$statusMixed); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - -// ----------------------------------------------------------------------------- - - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsNotRunOnBuild'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- - -$build_id = 26; // 26,27,28 -echo 'Build ID:' . $build_id . '
    '; - -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$build_id); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($$method2call); -echo '
    '; - -$build_id = 27; // 26,27,28 -echo 'Build ID:' . $build_id . '
    '; - -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$build_id); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($$method2call); -echo '
    '; - -$build_id = 28; // 26,27,28 -echo 'Build ID:' . $build_id . '
    '; - -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$build_id); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($$method2call); -echo '
    '; -// ----------------------------------------------------------------------------- - - - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsSameStatusFull'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- - -$statusMixed = 'n'; -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusMixed); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - -$statusMixed = array('n','p'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusMixed); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - -$statusMixed = array('b','p'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusMixed); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; - -$statusMixed = array('b','f'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusMixed); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($statusMixed); -new dBug($$method2call); -echo '
    '; -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsNotRunFull'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- - -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($$method2call); -echo '
    '; -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsSingleStatusFull'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- - -$status = 'p'; -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$status); -echo '
    ' . $method2call . '()' . '
    '; -echo '
    ' . "status=$status" . '
    '; -new dBug($$method2call); -echo '
    '; - -$status = 'b'; -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$status); -echo '
    ' . $method2call . '()' . '
    '; -echo '
    ' . "status=$status" . '
    '; -new dBug($$method2call); -echo '
    '; - -$status = 'f'; -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$status); -echo '
    ' . "status=$status" . '
    '; -echo '
    ' . $method2call . '()' . '
    '; -new dBug($$method2call); -echo '
    '; -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsStatusSetFull'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- - -$statusSet = array('b','p'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusSet); -echo '
    ' . $method2call . '()' . '
    '; -var_dump($statusSet); -new dBug($$method2call); -echo '
    '; - -$statusSet = array('b','f'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusSet); -echo '
    ' . $method2call . '()' . '
    '; -var_dump($statusSet); -new dBug($$method2call); -echo '
    '; -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsNotRunPartial'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- - -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id); -echo '
    ' . $method2call . '()' . '
    '; -new dBug($$method2call); -echo '
    '; -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -$method2call = 'getHitsStatusSetPartial'; -echo "

    TESTING:$method2call

    "; -// ----------------------------------------------------------------------------- -$statusSet = array('b','p'); -$$method2call = $obj_mgr->$method2call($tplan_id,$platform_id,$statusSet); -echo '
    ' . $method2call . '()' . '
    '; -var_dump($statusSet); -new dBug($$method2call); -echo '
    '; - - - -// WITH PLATFORMS -/* -$tplan_id = 33111; -$platform_id = 0; -echo '

    Test group conditions

    '; -echo 'Test Plan ID:' . $tplan_id . '
    '; -echo 'Platform ID:' . $platform_id . '
    '; -*/ - - -/* -new view -last_executions -SELECT tcversion_id,testplan_id,platform_id,build_id,MAX(status) AS status, id AS MAX(E.id) AS id -FROM executions -GROUP BY tcversion_id,testsplan_id,platform_id,build_id - -CREATE VIEW tk_last_executions AS -SELECT tcversion_id,testplan_id,platform_id,build_id,max(status) AS status,max(id) AS id -from tk_executions -group by tcversion_id,testplan_id,platform_id,build_id -*/ +STUFF; +// ================================================================================ + +$obj_mgr = new $object_class($db); + +$tplan_id = 33123; +$platform_id = 0; + +echo '

    Test group conditions

    '; +echo 'Test Plan ID:' . $tplan_id . '
    '; +echo 'Platform ID:' . $platform_id . '
    '; +echo '
    '; + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsStatusSetOnLatestExecution'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- + +$statusMixed = 'n'; +try { + $$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusMixed); +} catch (Exception $e) { + echo $e->getMessage(); +} +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +$statusMixed = array( + 'n', + 'p' +); +try { + $$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusMixed); +} catch (Exception $e) { + echo $e->getMessage(); +} +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +$statusMixed = array( + 'b', + 'p' +); +try { + $$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusMixed); +} catch (Exception $e) { + echo $e->getMessage(); +} +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsStatusSetOnBuild'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- +$build_id = 26; // 26,27,28 +echo 'Build ID:' . $build_id . '
    '; + +$statusMixed = 'p'; +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $build_id, + $statusMixed); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +$statusMixed = array( + 'p', + 'b' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $build_id, + $statusMixed); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +$statusMixed = array( + 'f', + 'b' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $build_id, + $statusMixed); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsNotRunOnBuild'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- + +$build_id = 26; // 26,27,28 +echo 'Build ID:' . $build_id . '
    '; + +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $build_id); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($$method2call); +echo '
    '; + +$build_id = 27; // 26,27,28 +echo 'Build ID:' . $build_id . '
    '; + +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $build_id); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($$method2call); +echo '
    '; + +$build_id = 28; // 26,27,28 +echo 'Build ID:' . $build_id . '
    '; + +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $build_id); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($$method2call); +echo '
    '; +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsSameStatusFull'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- + +$statusMixed = 'n'; +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusMixed); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +$statusMixed = array( + 'n', + 'p' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusMixed); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +$statusMixed = array( + 'b', + 'p' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusMixed); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; + +$statusMixed = array( + 'b', + 'f' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusMixed); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($statusMixed); +new dBug($$method2call); +echo '
    '; +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsNotRunFull'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- + +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($$method2call); +echo '
    '; +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsSingleStatusFull'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- + +$status = 'p'; +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $status); +echo '
    ' . $method2call . '()' . '
    '; +echo '
    ' . "status=$status" . '
    '; +new dBug($$method2call); +echo '
    '; + +$status = 'b'; +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $status); +echo '
    ' . $method2call . '()' . '
    '; +echo '
    ' . "status=$status" . '
    '; +new dBug($$method2call); +echo '
    '; + +$status = 'f'; +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $status); +echo '
    ' . "status=$status" . '
    '; +echo '
    ' . $method2call . '()' . '
    '; +new dBug($$method2call); +echo '
    '; +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsStatusSetFull'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- + +$statusSet = array( + 'b', + 'p' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusSet); +echo '
    ' . $method2call . '()' . '
    '; +var_dump($statusSet); +new dBug($$method2call); +echo '
    '; + +$statusSet = array( + 'b', + 'f' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusSet); +echo '
    ' . $method2call . '()' . '
    '; +var_dump($statusSet); +new dBug($$method2call); +echo '
    '; +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsNotRunPartial'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- + +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id); +echo '
    ' . $method2call . '()' . '
    '; +new dBug($$method2call); +echo '
    '; +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +$method2call = 'getHitsStatusSetPartial'; +echo "

    TESTING:$method2call

    "; +// ----------------------------------------------------------------------------- +$statusSet = array( + 'b', + 'p' +); +$$method2call = $obj_mgr->$method2call($tplan_id, $platform_id, $statusSet); +echo '
    ' . $method2call . '()' . '
    '; +var_dump($statusSet); +new dBug($$method2call); +echo '
    '; + +/* + * new view + * last_executions + * SELECT tcversion_id,testplan_id,platform_id,build_id,MAX(status) AS status, id AS MAX(E.id) AS id + * FROM executions + * GROUP BY tcversion_id,testsplan_id,platform_id,build_id + * + * CREATE VIEW tk_last_executions AS + * SELECT tcversion_id,testplan_id,platform_id,build_id,max(status) AS status,max(id) AS id + * from tk_executions + * group by tcversion_id,testplan_id,platform_id,build_id + */ ?> diff --git a/lib/functions/code_testing/testproject.class.test.php b/lib/functions/code_testing/testproject.class.test.php index b120dc1a1c..fbdc29244c 100644 --- a/lib/functions/code_testing/testproject.class.test.php +++ b/lib/functions/code_testing/testproject.class.test.php @@ -1,167 +1,167 @@ - testproject - constructor - testproject(&\$db)";echo "
    "; -$tproject_mgr=new testproject($db); -new dBug($tproject_mgr); - -$item = new stdClass(); -$item->name = 'CRASH'; -$item->notes = " Created doing test "; -$item->color = ''; -$item->options = new stdClass(); -//$item->options->requirement_mgmt = 1; -//$item->options->priority_mgmt = 1; -//$item->options->automated_execution = 1; -$item->active=1; -$item->is_public=1; -$item->prefix = 'TPX :: '; - -try -{ - $id = $tproject_mgr->create($item, array('doChecks' => true)); -} -catch (Exception $e) -{ - echo 'Caught exception: ', $e->getMessage(), "\n"; -} -die(); - - -// new dBug($_SESSION); - -$xx=$tproject_mgr->get_accessible_for_user(1, - array('output' => 'map','field_set' => 'id', 'format' => 'simple')); -new dBug($xx); -die(); - -// create() -// function create($name,$color,$options,$notes,$active=1,$tcasePrefix='',$is_public=1) -$notes = " Created doing test "; -$color = ''; -$options = new stdClass(); -$options->requirement_mgmt = 1; -$options->priority_mgmt = 1; -$options->automated_execution = 1; - -$active=1; -$is_public=1; - -$namePrefix = 'TPX :: '; -$name = uniqid($namePrefix,true); -$tcasePrefix = uniqid('',false); -//$new_id = $tproject_mgr->create($name,$color,$options,$notes,$active,$tcasePrefix,$is_public); -// -//$name = $namePrefix . $new_id; -//$tcasePrefix = $namePrefix . $new_id; -// -//$tproject_mgr->update($new_id, $name, $color, $options->requirement_mgmt, -// $options->priority_mgmt, $options->automated_execution, -// $notes,$active,$tcasePrefix,$is_public); -// -//new dBug($tproject_mgr->get_by_id($new_id)); -//die(); - -$new_id = 1157; -$tproject_mgr->copy_as(9,$new_id,1); -die(); - - -// getKeywords($testproject_id,$keywordID = null) -$tproject_id=1; -echo "
     testproject - getKeywords(\$testproject_id,\$keywordID = null)";echo "
    "; -echo "
                   getKeywords($tproject_id)";echo "
    "; -$keywords=$tproject_mgr->getKeywords($tproject_id); -new dBug($keywords); - -$tproject_id=1; -echo "
     testproject - get_first_level_test_suites($tproject_id,$mode='simple')";echo "
    "; -echo "
                   get_first_level_test_suites($tproject_id,$mode='simple')";echo "
    "; -$info=$tproject_mgr->get_first_level_test_suites($tproject_id,$mode='simple'); -new dBug($info); -die(); - -echo "
     testproject - get_keywords_map(\$testproject_id)";echo "
    "; -$tproject_id=1; -echo "
                   get_keywords_map($tproject_id)";echo "
    "; -$keywords_map=$tproject_mgr->get_keywords_map($tproject_id); -new dBug($keywords_map); - - -echo "
     testproject - get_keywords_tcases(\$testproject_id, \$keyword_id=0)";echo "
    "; -echo "
                   get_keywords_tcases($tproject_id)";echo "
    "; -$keywords_tcases=$tproject_mgr->get_keywords_tcases($tproject_id); -new dBug($keywords_tcases); - - -echo "
     testproject - get_linked_custom_fields(\$id,\$node_type=null)";echo "
    "; -echo "
                   get_linked_custom_fields($tproject_id)";echo "
    "; -$linked_custom_fields=$tproject_mgr->get_linked_custom_fields($tproject_id); -new dBug($linked_custom_fields); - - -echo "
     testproject - gen_combo_test_suites(\$id,\$exclude_branches=null,\$mode='dotted')";echo "
    "; -echo "
                   gen_combo_test_suites($tproject_id,null,'dotted')";echo "
    "; -$combo_test_suites=$tproject_mgr->gen_combo_test_suites($tproject_id,null,'dotted'); -new dBug($combo_test_suites); - -echo "
                   gen_combo_test_suites($tproject_id,null,'dotted')";echo "
    "; -$combo_test_suites=$tproject_mgr->gen_combo_test_suites($tproject_id,null,'array'); -new dBug($combo_test_suites); - - -echo "
     testproject - getReqSpec(\$testproject_id, \$id = null)";echo "
    "; -echo "
                   getReqSpec($tproject_id)";echo "
    "; -$requirement_spec=$tproject_mgr->getReqSpec($tproject_id); -new dBug($requirement_spec); - -$srs_id=2; -echo "
                   getReqSpec(\$tproject_id,\$srs_id)";echo "
    "; -echo "
                   getReqSpec($tproject_id,$srs_id)";echo "
    "; -$requirement_spec=$tproject_mgr->getReqSpec($tproject_id,$srs_id); -new dBug($requirement_spec); - - -$srs_title='SRS2'; -echo "
     testproject - get_srs_by_title(\$testproject_id,\$title,\$ignore_case=0)";echo "
    "; -echo "
                   get_srs_by_title($tproject_id,$srs_title)";echo "
    "; -$srs_by_title=$tproject_mgr->get_srs_by_title($tproject_id,$srs_title); -new dBug($srs_by_title); - -// function get_srs_by_title($testproject_id,$title,$ignore_case=0) - - - -/* -function getReqSpec($testproject_id, $id = null) -function createReqSpec($testproject_id,$title, $scope, $countReq,$user_id,$type = 'n') -function get_srs_by_title($testproject_id,$title,$ignore_case=0) -function check_srs_title($testproject_id,$title,$ignore_case=0) -function delete($id,&$error) -function get_keywords_map($testproject_id) -function get_all_testcases_id($id) -function get_keywords_tcases($testproject_id, $keyword_id=0) -function get_all_testplans($testproject_id,$get_tp_without_tproject_id=0,$plan_status=null) -function check_tplan_name_existence($tproject_id,$tplan_name,$case_sensitive=0) -function get_first_level_test_suites($tproject_id,$mode='simple') -function get_linked_custom_fields($id,$node_type=null,$node_id=null) -*/ - + testproject - constructor - testproject(&\$db)"; +echo "
    "; +$tproject_mgr = new testproject($db); +new dBug($tproject_mgr); + +$item = new stdClass(); +$item->name = 'CRASH'; +$item->notes = " Created doing test "; +$item->color = ''; +$item->options = new stdClass(); +$item->active = 1; +$item->is_public = 1; +$item->prefix = 'TPX :: '; + +try { + $id = $tproject_mgr->create($item, array( + 'doChecks' => true + )); +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} +die(); + +$xx = $tproject_mgr->get_accessible_for_user(1, + array( + 'output' => 'map', + 'field_set' => 'id', + 'format' => 'simple' + )); +new dBug($xx); +die(); + +// create() +// function create($name,$color,$options,$notes,$active=1,$tcasePrefix='',$is_public=1) +$notes = " Created doing test "; +$color = ''; +$options = new stdClass(); +$options->requirement_mgmt = 1; +$options->priority_mgmt = 1; +$options->automated_execution = 1; + +$active = 1; +$is_public = 1; + +$namePrefix = 'TPX :: '; +$name = uniqid($namePrefix, true); +$tcasePrefix = uniqid('', false); + +$new_id = 1157; +$tproject_mgr->copy_as(9, $new_id, 1); +die(); + +// getKeywords($testproject_id,$keywordID = null) +$tproject_id = 1; +echo "
     testproject - getKeywords(\$testproject_id,\$keywordID = null)";
    +echo "
    "; +echo "
                   getKeywords($tproject_id)";
    +echo "
    "; +$keywords = $tproject_mgr->getKeywords($tproject_id); +new dBug($keywords); + +$tproject_id = 1; +echo "
     testproject - get_first_level_test_suites($tproject_id,$mode='simple')";
    +echo "
    "; +echo "
                   get_first_level_test_suites($tproject_id,$mode='simple')";
    +echo "
    "; +$info = $tproject_mgr->get_first_level_test_suites($tproject_id, + $mode = 'simple'); +new dBug($info); +die(); + +echo "
     testproject - get_keywords_map(\$testproject_id)";
    +echo "
    "; +$tproject_id = 1; +echo "
                   get_keywords_map($tproject_id)";
    +echo "
    "; +$keywords_map = $tproject_mgr->get_keywords_map($tproject_id); +new dBug($keywords_map); + +echo "
     testproject - get_keywords_tcases(\$testproject_id, \$keyword_id=0)";
    +echo "
    "; +echo "
                   get_keywords_tcases($tproject_id)";
    +echo "
    "; +$keywords_tcases = $tproject_mgr->get_keywords_tcases($tproject_id); +new dBug($keywords_tcases); + +echo "
     testproject - get_linked_custom_fields(\$id,\$node_type=null)";
    +echo "
    "; +echo "
                   get_linked_custom_fields($tproject_id)";
    +echo "
    "; +$linked_custom_fields = $tproject_mgr->get_linked_custom_fields($tproject_id); +new dBug($linked_custom_fields); + +echo "
     testproject - gen_combo_test_suites(\$id,\$exclude_branches=null,\$mode='dotted')";
    +echo "
    "; +echo "
                   gen_combo_test_suites($tproject_id,null,'dotted')";
    +echo "
    "; +$combo_test_suites = $tproject_mgr->gen_combo_test_suites($tproject_id, null, + 'dotted'); +new dBug($combo_test_suites); + +echo "
                   gen_combo_test_suites($tproject_id,null,'dotted')";
    +echo "
    "; +$combo_test_suites = $tproject_mgr->gen_combo_test_suites($tproject_id, null, + 'array'); +new dBug($combo_test_suites); + +echo "
     testproject - getReqSpec(\$testproject_id, \$id = null)";
    +echo "
    "; +echo "
                   getReqSpec($tproject_id)";
    +echo "
    "; +$requirement_spec = $tproject_mgr->getReqSpec($tproject_id); +new dBug($requirement_spec); + +$srs_id = 2; +echo "
                   getReqSpec(\$tproject_id,\$srs_id)";
    +echo "
    "; +echo "
                   getReqSpec($tproject_id,$srs_id)";
    +echo "
    "; +$requirement_spec = $tproject_mgr->getReqSpec($tproject_id, $srs_id); +new dBug($requirement_spec); + +$srs_title = 'SRS2'; +echo "
     testproject - get_srs_by_title(\$testproject_id,\$title,\$ignore_case=0)";
    +echo "
    "; +echo "
                   get_srs_by_title($tproject_id,$srs_title)";
    +echo "
    "; +$srs_by_title = $tproject_mgr->get_srs_by_title($tproject_id, $srs_title); +new dBug($srs_by_title); + +// function get_srs_by_title($testproject_id,$title,$ignore_case=0) + +/* + * function getReqSpec($testproject_id, $id = null) + * function createReqSpec($testproject_id,$title, $scope, $countReq,$user_id,$type = 'n') + * function get_srs_by_title($testproject_id,$title,$ignore_case=0) + * function check_srs_title($testproject_id,$title,$ignore_case=0) + * function delete($id,&$error) + * function get_keywords_map($testproject_id) + * function get_all_testcases_id($id) + * function get_keywords_tcases($testproject_id, $keyword_id=0) + * function get_all_testplans($testproject_id,$get_tp_without_tproject_id=0,$plan_status=null) + * function check_tplan_name_existence($tproject_id,$tplan_name,$case_sensitive=0) + * function get_first_level_test_suites($tproject_id,$mode='simple') + * function get_linked_custom_fields($id,$node_type=null,$node_id=null) + */ + ?> diff --git a/lib/functions/code_testing/testsuite.class.test.php b/lib/functions/code_testing/testsuite.class.test.php index 03e38939d8..100c3963c7 100644 --- a/lib/functions/code_testing/testsuite.class.test.php +++ b/lib/functions/code_testing/testsuite.class.test.php @@ -1,127 +1,149 @@ - testsuite - constructor - testsuite(&\$db)";echo "
    "; -$tsuite_mgr=new testsuite($db); -new dBug($tsuite_mgr); - -$tsuite_name = 'Build Management'; -echo "
     testsuite - get_by_name(\$name)";echo "
    "; -echo "
                 get_by_name($tsuite_name)";echo "
    "; -$tsuite_info = $tsuite_mgr->get_by_name($tsuite_name); -new dBug($tsuite_info); -die(); - -$tsuite_id=689; -echo "
     testsuite - get_children(\$id)";echo "
    "; -echo "
                 get_children($tsuite_id)";echo "
    "; -$tsuite_info=$tsuite_mgr->get_children($tsuite_id); -new dBug($tsuite_info); - - -$tsuite_id=676; -echo "
     testsuite - get_by_id(\$id)";echo "
    "; -echo "
                 get_by_id($tsuite_id)";echo "
    "; -$tsuite_info=$tsuite_mgr->get_by_id($tsuite_id); -new dBug($tsuite_info); - -$tsuite_name=$tsuite_info['name']; - -$tsuite_id = array(); -$tsuite_id[]=676; -$tsuite_id[]=804; -$tsuite_id[]=826; - - -echo "
     testsuite - get_by_id(\$id)";echo "
    "; -echo "
                 get_by_id($tsuite_id)";echo "
    "; -$tsuite_info=$tsuite_mgr->get_by_id($tsuite_id); -new dBug($tsuite_info); -die(); - - - -echo "
     testsuite - get_all()";echo "
    "; -echo "
                 get_all()";echo "
    "; -$all_tsuites_in_my_tl=$tsuite_mgr->get_all(); -new dBug($all_tsuites_in_my_tl); - -echo "
     testsuite - get_by_name(\$name)";echo "
    "; -echo "
                 get_by_name($tsuite_name)";echo "
    "; -$tsuite_info=$tsuite_mgr->get_by_name($tsuite_name); -new dBug($tsuite_info); - -echo "
     testsuite - get_testcases_deep(\$id,\$details='simple')";echo "
    "; -echo "
                 get_testcases_deep($tsuite_id,'simple')";echo "
    "; -$testcases_deep=$tsuite_mgr->get_testcases_deep($tsuite_id); -new dBug($testcases_deep); - -define("GET_ONLY_TESTCASE_ID",1); -echo "
                 get_testcases_deep(\$tsuite_id,\$details='full')";echo "
    "; -$testcases_deep=$tsuite_mgr->get_testcases_deep($tsuite_id,'full'); -new dBug($testcases_deep); - -echo "
     testsuite - getKeywords(\$tcID,\$kwID = null)";echo "
    "; -echo "
                getKeywords($tsuite_id)";echo "
    "; -$keywords=$tsuite_mgr->getKeywords($tsuite_id); -new dBug($keywords); - - -echo "
     testsuite - get_keywords_map(\$id,\$order_by_clause='')";echo "
    "; -$tsuite_id=4; -echo "
                   get_keywords_map($tsuite_id)";echo "
    "; -$keywords_map=$tsuite_mgr->get_keywords_map($tsuite_id); -new dBug($keywords_map); - - - - -echo "
     testsuite - get_linked_cfields_at_design(\$id,\$parent_id=null,\$show_on_execution=null)";echo "
    "; -echo "
                get_linked_cfields_at_design($tsuite_id)";echo "
    "; -$linked_cfields_at_design=$tsuite_mgr->get_linked_cfields_at_design($tsuite_id); -new dBug($linked_cfields_at_design); - - - + testsuite - constructor - testsuite(&\$db)"; +echo "
    "; +$tsuite_mgr = new testsuite($db); +new dBug($tsuite_mgr); + +$tsuite_name = 'Build Management'; +echo "
     testsuite - get_by_name(\$name)";
    +echo "
    "; +echo "
                 get_by_name($tsuite_name)";
    +echo "
    "; +$tsuite_info = $tsuite_mgr->get_by_name($tsuite_name); +new dBug($tsuite_info); +die(); + +$tsuite_id = 689; +echo "
     testsuite - get_children(\$id)";
    +echo "
    "; +echo "
                 get_children($tsuite_id)";
    +echo "
    "; +$tsuite_info = $tsuite_mgr->get_children($tsuite_id); +new dBug($tsuite_info); + +$tsuite_id = 676; +echo "
     testsuite - get_by_id(\$id)";
    +echo "
    "; +echo "
                 get_by_id($tsuite_id)";
    +echo "
    "; +$tsuite_info = $tsuite_mgr->get_by_id($tsuite_id); +new dBug($tsuite_info); + +$tsuite_name = $tsuite_info['name']; + +$tsuite_id = array(); +$tsuite_id[] = 676; +$tsuite_id[] = 804; +$tsuite_id[] = 826; + +echo "
     testsuite - get_by_id(\$id)";
    +echo "
    "; +echo "
                 get_by_id($tsuite_id)";
    +echo "
    "; +$tsuite_info = $tsuite_mgr->get_by_id($tsuite_id); +new dBug($tsuite_info); +die(); + +echo "
     testsuite - get_all()";
    +echo "
    "; +echo "
                 get_all()";
    +echo "
    "; +$all_tsuites_in_my_tl = $tsuite_mgr->get_all(); +new dBug($all_tsuites_in_my_tl); + +echo "
     testsuite - get_by_name(\$name)";
    +echo "
    "; +echo "
                 get_by_name($tsuite_name)";
    +echo "
    "; +$tsuite_info = $tsuite_mgr->get_by_name($tsuite_name); +new dBug($tsuite_info); + +echo "
     testsuite - get_testcases_deep(\$id,\$details='simple')";
    +echo "
    "; +echo "
                 get_testcases_deep($tsuite_id,'simple')";
    +echo "
    "; +$testcases_deep = $tsuite_mgr->get_testcases_deep($tsuite_id); +new dBug($testcases_deep); + +define("GET_ONLY_TESTCASE_ID", 1); +echo "
                 get_testcases_deep(\$tsuite_id,\$details='full')";
    +echo "
    "; +$testcases_deep = $tsuite_mgr->get_testcases_deep($tsuite_id, 'full'); +new dBug($testcases_deep); + +echo "
     testsuite - getKeywords(\$tcID,\$kwID = null)";
    +echo "
    "; +echo "
                getKeywords($tsuite_id)";
    +echo "
    "; +$keywords = $tsuite_mgr->getKeywords($tsuite_id); +new dBug($keywords); + +echo "
     testsuite - get_keywords_map(\$id,\$order_by_clause='')";
    +echo "
    "; +$tsuite_id = 4; +echo "
                   get_keywords_map($tsuite_id)";
    +echo "
    "; +$keywords_map = $tsuite_mgr->get_keywords_map($tsuite_id); +new dBug($keywords_map); + +echo "
     testsuite - get_linked_cfields_at_design(\$id,\$parent_id=null,\$show_on_execution=null)";
    +echo "
    "; +echo "
                get_linked_cfields_at_design($tsuite_id)";
    +echo "
    "; +$linked_cfields_at_design = $tsuite_mgr->get_linked_cfields_at_design( + $tsuite_id); +new dBug($linked_cfields_at_design); + echo "
     testsuite - get_linked_cfields_at_execution(\$id,\$parent_id=null,
    \$show_on_execution=null,
    - \$execution_id=null,\$testplan_id=null)";echo "
    "; -echo "
                get_linked_cfields_at_execution($tsuite_id)";echo "
    "; -$linked_cfields_at_execution=$tsuite_mgr->get_linked_cfields_at_execution($tsuite_id); -new dBug($linked_cfields_at_execution); - - - -echo "
     testsuite - html_table_of_custom_field_inputs(\$id,\$parent_id=null,\$scope='design',\$name_suffix='')";echo "
    "; -echo "
                html_table_of_custom_field_inputs($tsuite_id)";echo "
    "; -$table_of_custom_field_inputs=$tsuite_mgr->html_table_of_custom_field_inputs($tsuite_id); -echo "
    "; echo $table_of_custom_field_inputs; echo "
    "; - - + \$execution_id=null,\$testplan_id=null)"; +echo "
    "; +echo "
                get_linked_cfields_at_execution($tsuite_id)";
    +echo "
    "; +$linked_cfields_at_execution = $tsuite_mgr->get_linked_cfields_at_execution( + $tsuite_id); +new dBug($linked_cfields_at_execution); + +echo "
     testsuite - html_table_of_custom_field_inputs(\$id,\$parent_id=null,\$scope='design',\$name_suffix='')";
    +echo "
    "; +echo "
                html_table_of_custom_field_inputs($tsuite_id)";
    +echo "
    "; +$table_of_custom_field_inputs = $tsuite_mgr->html_table_of_custom_field_inputs( + $tsuite_id); +echo "
    ";
    +echo $table_of_custom_field_inputs;
    +echo "
    "; + echo "
     testsuite - html_table_of_custom_field_values(\$id,\$scope='design',
    \$show_on_execution=null,
    - \$execution_id=null,\$testplan_id=null) ";echo "
    "; - -echo "
     testsuite - html_table_of_custom_field_values($tsuite_id)";echo "
    "; -$table_of_custom_field_values=$tsuite_mgr->html_table_of_custom_field_values($tsuite_id); -echo "
    "; echo $table_of_custom_field_values; echo "
    "; + \$execution_id=null,\$testplan_id=null) "; +echo "
    "; + +echo "
     testsuite - html_table_of_custom_field_values($tsuite_id)";
    +echo "
    "; +$table_of_custom_field_values = $tsuite_mgr->html_table_of_custom_field_values( + $tsuite_id); +echo "
    ";
    +echo $table_of_custom_field_values;
    +echo "
    "; ?> diff --git a/lib/functions/code_testing/tlIssueTracker.test.php b/lib/functions/code_testing/tlIssueTracker.test.php index 19758bbd1e..624ba51cb4 100644 --- a/lib/functions/code_testing/tlIssueTracker.test.php +++ b/lib/functions/code_testing/tlIssueTracker.test.php @@ -1,134 +1,87 @@ -getTypes()); - -$tprojectSet = array(32674,2,27); - -/* -$dx = new stdClass(); -$dx->name = 'Francisco2'; -$dx->type = $issueTrackerDomain['MANTIS']; -$dx->cfg = " I'm Mantis "; -$info = $it->create($dx); -new dBug($info); -*/ - -$str = ""; -$str = ''; -$str .= "" . - "localhost" . - "mantis_tlorg" . - "mysql" . - "root" . - "mysqlroot" . - "http://localhost:8080/development/mantisbt-1.2.5/my_view_page.php?id=" . - "http://localhost:8080/development/mantisbt-1.2.5/" . - ""; - -$dx = new stdClass(); -$dx->name = 'Francisco3'; -$dx->type = $issueTrackerDomain['MANTIS']; -$dx->cfg = $str; -$info = $it->create($dx); -new dBug($info); - -$info = $it->getByName('Francisco3'); -new dBug($info); - - -die(); - -$links = $it->getLinks(4); -new dBug($links); - -// $it->link(4,2); - -$links = $it->getLinks(4); -new dBug($links); - - -$linkSet = $it->getLinkSet(); -new dBug($linkSet); - -$info = $it->delete(0); -new dBug($info); - -$info = $it->delete('papap'); -new dBug($info); - -$info = $it->delete(-1); -new dBug($info); - - -$info = $it->delete(4); -new dBug($info); - -$info = $it->delete(5); -new dBug($info); - -die(); - - -/* -$opt = array(null,array('output' => 'id')); -foreach($opt as $o) -{ - $info = $it->getByName('FMAN',$o); - new dBug($info); -} - -$opt = array(null,array('output' => 'id')); -foreach($opt as $o) -{ - $info = $it->getByID(2,$o); - new dBug($info); -} - - -$dx = new stdClass(); -$dx->name = 'FEFE'; -$dx->type = $issueTrackerDomain['MANTIS']; -$dx->cfg = " I'm Mantis "; -$info = $it->create($dx); -new dBug($info); -*/ - -/* -$it->link(22,27); -$it->unlink(2,27); -$it->unlink(22,27); -*/ - -$dx = new stdClass(); -$dx->id = 1; -$dx->name = 'FEFE'; -$dx->type = $issueTrackerDomain['MANTIS']; -$dx->cfg = " I'm Mantis "; -$xx = $it->update($dx); - -new dBug($xx); - -$dx = new stdClass(); -$dx->id = 1; -$dx->name = 'FARFANS'; -$dx->type = $issueTrackerDomain['MANTIS']; -$dx->cfg = " I'm Mantis "; -$xx = $it->update($dx); - -new dBug($xx); - - -$links = $it->getLinks(1); -new dBug($links); - - -?> \ No newline at end of file +getTypes()); + +$tprojectSet = array( + 32674, + 2, + 27 +); + +$str = ""; +$str = ''; +$str .= "" . "localhost" . + "mantis_tlorg" . "mysql" . + "root" . "mysqlroot" . + "http://localhost:8080/development/mantisbt-1.2.5/my_view_page.php?id=" . + "http://localhost:8080/development/mantisbt-1.2.5/" . + ""; + +$dx = new stdClass(); +$dx->name = 'Francisco3'; +$dx->type = $issueTrackerDomain['MANTIS']; +$dx->cfg = $str; +$info = $it->create($dx); +new dBug($info); + +$info = $it->getByName('Francisco3'); +new dBug($info); + +die(); + +$links = $it->getLinks(4); +new dBug($links); + +$links = $it->getLinks(4); +new dBug($links); + +$linkSet = $it->getLinkSet(); +new dBug($linkSet); + +$info = $it->delete(0); +new dBug($info); + +$info = $it->delete('papap'); +new dBug($info); + +$info = $it->delete(- 1); +new dBug($info); + +$info = $it->delete(4); +new dBug($info); + +$info = $it->delete(5); +new dBug($info); + +die(); + +$dx = new stdClass(); +$dx->id = 1; +$dx->name = 'FEFE'; +$dx->type = $issueTrackerDomain['MANTIS']; +$dx->cfg = " I'm Mantis "; +$xx = $it->update($dx); + +new dBug($xx); + +$dx = new stdClass(); +$dx->id = 1; +$dx->name = 'FARFANS'; +$dx->type = $issueTrackerDomain['MANTIS']; +$dx->cfg = " I'm Mantis "; +$xx = $it->update($dx); + +new dBug($xx); + +$links = $it->getLinks(1); +new dBug($links); + +?> diff --git a/lib/functions/code_testing/tree.class.test.php b/lib/functions/code_testing/tree.class.test.php index 6d74da793d..db6c799f5c 100644 --- a/lib/functions/code_testing/tree.class.test.php +++ b/lib/functions/code_testing/tree.class.test.php @@ -1,105 +1,129 @@ - tree - constructor - tree(&\$db)";echo "
    "; -$tree_mgr=new tree($db); -new dBug($tree_mgr); - -echo "
     tree - getNodeByAttributes()";echo "
    "; -$xx = $tree_mgr->getNodeByAttributes(array('type' => 'testproject','name' => 'ISSUE-5429')); -new dBug($xx); - -$xx = $tree_mgr->getNodeByAttributes(array('type' => 'testplan','name' => 'AKA','parent_id' => 5675)); -new dBug($xx); - -echo "
     tree - get_available_node_types()";echo "
    "; -$available_node_types = $tree_mgr->get_available_node_types(); -new dBug($available_node_types); - -echo "
     tree - get_node_hierarchy_info(\$node_id)";echo "
    "; -$node_id=1; -echo "
     get_node_hierarchy_info($node_id)";echo "
    "; -$node_hierachy_info = $tree_mgr->get_node_hierarchy_info($node_id); -new dBug($node_hierachy_info); - -echo "
     tree - get_subtree(\$node_id)";echo "
    "; -echo "
     get_subtree($node_id)";echo "
    "; -$subtree = $tree_mgr->get_subtree($node_id); -new dBug($subtree); - - -echo "
     tree - get_subtree(\$node_id,\$exclude_node_types=null," . "
    " . -" \$exclude_children_of=null,\$exclude_branches=null," . "
    " . -" \$and_not_in_clause='',\$bRecursive = false)";echo "
    "; - -echo "
     get_subtree($node_id,null,null,null,'',false)";echo "
    "; -$subtree = $tree_mgr->get_subtree($node_id,null,null,null,'',false); -new dBug($subtree); - - -echo "
     get_subtree($node_id,null,null,null,'',true)";echo "
    "; -$subtree = $tree_mgr->get_subtree($node_id,null,null,null,'',true); -new dBug($subtree); - - -echo "
     tree - get_subtree_list(\$node_id)";echo "
    "; -echo "
     get_subtree_list($node_id)";echo "
    "; -$subtree_list = $tree_mgr->get_subtree_list($node_id); -new dBug($subtree_list); - -$path_begin_node_id=285; -$path_end_node_id=2; -define('TREE_ROOT',null); -define('FORMAT_FULL','full'); -define('FORMAT_SIMPLE','simple'); - -echo "
     tree - get_path(\$node_id,\$to_node_id = null,\$format = 'full') ";echo "
    "; -echo "
     tree - get_path($path_begin_node_id) ";echo "
    "; -$path=$tree_mgr->get_path($path_begin_node_id); -new dBug($path); - - -echo "
     tree - get_path(\$node_id,\$to_node_id = null,\$format = 'full') ";echo "
    "; -echo "
     tree - get_path($path_begin_node_id,TREE_ROOT,FORMAT_FULL) ";echo "
    "; -$path=$tree_mgr->get_path($path_begin_node_id,TREE_ROOT,FORMAT_FULL); -new dBug($path); - -echo "
     tree - get_path($path_begin_node_id,TREE_ROOT,FORMAT_SIMPLE) ";echo "
    "; -$path=$tree_mgr->get_path($path_begin_node_id,TREE_ROOT,FORMAT_SIMPLE); -new dBug($path); - - -echo "
     tree - get_path($path_begin_node_id,$path_end_node_id,FORMAT_FULL) ";echo "
    "; -$path=$tree_mgr->get_path($path_begin_node_id,$path_end_node_id,FORMAT_FULL); -new dBug($path); - -$node_id=1; -echo "
     tree - get_children(\$node_id)";echo "
    "; -echo "
     get_children($node_id)";echo "
    "; -$children = $tree_mgr->get_children($node_id); -new dBug($children); - - -echo "
     tree - get_node_hierarchy_info(\$node_id) ";echo "
    "; -echo "
     get_node_hierarchy_info($node_id) ";echo "
    "; -$node_hierachy_info=$tree_mgr->get_node_hierarchy_info($node_id) ; -new dBug($node_hierachy_info); -?> \ No newline at end of file + tree - constructor - tree(&\$db)"; +echo "
    "; +$tree_mgr = new tree($db); +new dBug($tree_mgr); + +echo "
     tree - getNodeByAttributes()";
    +echo "
    "; +$xx = $tree_mgr->getNodeByAttributes( + array( + 'type' => 'testproject', + 'name' => 'ISSUE-5429' + )); +new dBug($xx); + +$xx = $tree_mgr->getNodeByAttributes( + array( + 'type' => 'testplan', + 'name' => 'AKA', + 'parent_id' => 5675 + )); +new dBug($xx); + +echo "
     tree - get_available_node_types()";
    +echo "
    "; +$available_node_types = $tree_mgr->get_available_node_types(); +new dBug($available_node_types); + +echo "
     tree - get_node_hierarchy_info(\$node_id)";
    +echo "
    "; +$node_id = 1; +echo "
     get_node_hierarchy_info($node_id)";
    +echo "
    "; +$node_hierachy_info = $tree_mgr->get_node_hierarchy_info($node_id); +new dBug($node_hierachy_info); + +echo "
     tree - get_subtree(\$node_id)";
    +echo "
    "; +echo "
     get_subtree($node_id)";
    +echo "
    "; +$subtree = $tree_mgr->get_subtree($node_id); +new dBug($subtree); + +echo "
     tree - get_subtree(\$node_id,\$exclude_node_types=null," . "
    " . + " \$exclude_children_of=null,\$exclude_branches=null," . + "
    " . + " \$and_not_in_clause='',\$bRecursive = false)"; +echo "
    "; + +echo "
     get_subtree($node_id,null,null,null,'',false)";
    +echo "
    "; +$subtree = $tree_mgr->get_subtree($node_id, null, null, null, '', false); +new dBug($subtree); + +echo "
     get_subtree($node_id,null,null,null,'',true)";
    +echo "
    "; +$subtree = $tree_mgr->get_subtree($node_id, null, null, null, '', true); +new dBug($subtree); + +echo "
     tree - get_subtree_list(\$node_id)";
    +echo "
    "; +echo "
     get_subtree_list($node_id)";
    +echo "
    "; +$subtree_list = $tree_mgr->get_subtree_list($node_id); +new dBug($subtree_list); + +$path_begin_node_id = 285; +$path_end_node_id = 2; +define('TREE_ROOT', null); +define('FORMAT_FULL', 'full'); +define('FORMAT_SIMPLE', 'simple'); + +echo "
     tree - get_path(\$node_id,\$to_node_id = null,\$format = 'full') ";
    +echo "
    "; +echo "
     tree - get_path($path_begin_node_id) ";
    +echo "
    "; +$path = $tree_mgr->get_path($path_begin_node_id); +new dBug($path); + +echo "
     tree - get_path(\$node_id,\$to_node_id = null,\$format = 'full') ";
    +echo "
    "; +echo "
     tree - get_path($path_begin_node_id,TREE_ROOT,FORMAT_FULL) ";
    +echo "
    "; +$path = $tree_mgr->get_path($path_begin_node_id, TREE_ROOT, FORMAT_FULL); +new dBug($path); + +echo "
     tree - get_path($path_begin_node_id,TREE_ROOT,FORMAT_SIMPLE) ";
    +echo "
    "; +$path = $tree_mgr->get_path($path_begin_node_id, TREE_ROOT, FORMAT_SIMPLE); +new dBug($path); + +echo "
     tree - get_path($path_begin_node_id,$path_end_node_id,FORMAT_FULL) ";
    +echo "
    "; +$path = $tree_mgr->get_path($path_begin_node_id, $path_end_node_id, FORMAT_FULL); +new dBug($path); + +$node_id = 1; +echo "
     tree - get_children(\$node_id)";
    +echo "
    "; +echo "
     get_children($node_id)";
    +echo "
    "; +$children = $tree_mgr->get_children($node_id); +new dBug($children); + +echo "
     tree - get_node_hierarchy_info(\$node_id) ";
    +echo "
    "; +echo "
     get_node_hierarchy_info($node_id) ";
    +echo "
    "; +$node_hierachy_info = $tree_mgr->get_node_hierarchy_info($node_id); +new dBug($node_hierachy_info); +?> diff --git a/lib/functions/common.php b/lib/functions/common.php index adf618618c..a220e866ae 100644 --- a/lib/functions/common.php +++ b/lib/functions/common.php @@ -1,2109 +1,2184 @@ - TICKET 0007190 */ -if( !defined('TL_APICALL') ) -{ - require_once("csrf.php"); -} - -/** Input data validation */ -require_once("inputparameter.inc.php"); - -/** @TODO use the next include only if it is used -> must be removed */ -// require_once("testproject.class.php"); -require_once("treeMenu.inc.php"); - - -// 20130526 checks need to be done in order to understand if this class is really needed -require_once("exec_cfield_mgr.class.php"); - -/** - * Automatic loader for PHP classes - * See PHP Manual for details - */ -function tlAutoload($class_name) { - - // exceptions - // 1. remove prefix and convert lower case - $tlClasses = null; - $tlClassPrefixLen = 2; - $classFileName = $class_name; - - - // 2. add a lower case directory - $addDirToInclude = array('Kint' => true); - - // this way Zend_Loader_Autoloader will take care of these classes. - // Needed in order to make work bugzillaxmlrpc interface - if( strstr($class_name,'Zend_') !== FALSE ) { - return false; - } - - // Workaround - // https://github.com/smarty-php/smarty/issues/344 - // https://github.com/smarty-php/smarty/pull/345 - if( strpos($class_name,'Smarty_Internal_Compile_') !== FALSE ) { - return false; - } - - if (isset($tlClasses[$classFileName])) { - $len = tlStringLen($classFileName) - $tlClassPrefixLen; - $classFileName = strtolower(tlSubstr($classFileName,$tlClassPrefixLen,$len)); - } - - if (isset($addDirToInclude[$class_name])) { - $classFileName = strtolower($class_name) . "/" . $class_name; - } - - // Plugin special processing, class name ends with Plugin (see plugin_register()) - // Does not use autoload - if( preg_match('/Plugin$/', $class_name) == 1 ) { - return; - } - - - // fix provided by BitNami for: - // Reason: We had a problem integrating TestLink with other apps. - // You can reproduce it installing ThinkUp and TestLink applications in the same stack. - - try { - include_once $classFileName . '.class.php'; - } - catch (Exception $e) { - } - -} - - -// ----- End of loading and begin functions --------------------------------------------- - -/** @var integer global main DB connection identifier */ -$db = 0; - - -/** - * TestLink connects to the database - * - * @param &$db reference to resource, here resource pointer will be returned. - * @param $onErrorExit default false, true standard page will be displayed - * - * @return array - * aa['status'] = 1 -> OK , 0 -> KO - * aa['dbms_msg''] = 'ok', or $db->error_msg(). - */ -function doDBConnect(&$db,$onErrorExit=false) { - global $g_tlLogger; - - $charSet = config_get('charset'); - $result = array('status' => 1, 'dbms_msg' => 'ok'); - - switch(DB_TYPE) { - case 'mssql': - $dbDriverName = 'mssqlnative'; - break; - - default: - $dbDriverName = DB_TYPE; - break; - } - - $db = new database($dbDriverName); - $result = $db->connect(DSN, DB_HOST, DB_USER, DB_PASS, DB_NAME); - - if (!$result['status']) { - echo $result['dbms_msg']; - $result['status'] = 0; - $search = array('','','
    '); - $replace = array('',''," :: "); - $logtext = ' Connect to database ' . DB_NAME . ' on Host ' . DB_HOST . ' fails
    '; - $logtext .= 'DBMS Error Message: ' . $result['dbms_msg']; - - $logmsg = $logtext . ($onErrorExit ? '
    Redirection to connection fail screen.' : ''); - tLog(str_replace($search,$replace,$logmsg), 'ERROR'); - if( $onErrorExit ) { - $smarty = new TLSmarty(); - $smarty->assign('title', lang_get('fatal_page_title')); - $smarty->assign('content', $logtext); - $smarty->assign('link_to_op', null); - $smarty->display('workAreaSimple.tpl'); - exit(); - } - } - else { - if((DB_TYPE == 'mysql') && ($charSet == 'UTF-8')) { - $db->exec_query("SET CHARACTER SET utf8"); - $db->exec_query("SET collation_connection = 'utf8_general_ci'"); - } - } - - // if we establish a DB connection, we reopen the session, - // to attach the db connection - $g_tlLogger->endTransaction(); - $g_tlLogger->startTransaction(); - - return $result; -} - - -/** - * Set session data related to the current test plan - * and saves a cookie with current testplan id - * - * @param array $tplan_info result of DB query - */ -function setSessionTestPlan($tplan_info) { - if ($tplan_info) { - $_SESSION['testplanID'] = $tplan_info['id']; - $_SESSION['testplanName'] = $tplan_info['name']; - - // Save testplan id for next session - $ckObj = new stdClass(); - - $ckCfg = config_get('cookie'); - $ckObj->name = $ckCfg->prefix . 'TL_lastTestPlanForUserID_' . - intval($_SESSION['userID']); - $ckObj->value = $tplan_info['id']; - - tlSetCookie($ckObj); - - tLog("Test Plan was adjusted to '" . $tplan_info['name'] . "' ID(" . $tplan_info['id'] . ')', 'INFO'); - } - else { - unset($_SESSION['testplanID']); - unset($_SESSION['testplanName']); - } -} - - -/** - * Set home URL path - * @internal revisions - */ -function setPaths() -{ - if (!isset($_SESSION['basehref'])) - { - $_SESSION['basehref'] = get_home_url(array('force_https' => config_get('force_https'))); - } -} - - -/** - * Verify if user is log in. Redirect to login page if not. - * - * @param integer $db DB identifier - * @param boolean $redirect if true (default) redirects user to login page, otherwise returns true/false as login status - **/ -function checkSessionValid(&$db, $redirect=true) -{ - $isValidSession = false; - if (isset($_SESSION['userID']) && $_SESSION['userID'] > 0) - { - $now = time(); - if (($now - $_SESSION['lastActivity']) <= (config_get("sessionInactivityTimeout") * 60)) - { - $_SESSION['lastActivity'] = $now; - $user = new tlUser($_SESSION['userID']); - $user->readFromDB($db); - $_SESSION['currentUser'] = $user; - $isValidSession = true; - } - } - if (!$isValidSession && $redirect) - { - tLog('Invalid session from ' . $_SERVER["REMOTE_ADDR"] . '. Redirected to login page.', 'INFO'); - - $fName = "login.php"; - $baseDir = dirname($_SERVER['SCRIPT_FILENAME']); - - while(!file_exists($baseDir . DIRECTORY_SEPARATOR . $fName)) - { - $fName = "../" . $fName; - } - $destination = "&destination=" . urlencode($_SERVER['REQUEST_URI']); - redirect($fName . "?note=expired" . $destination,"top.location"); - exit(); - } - return $isValidSession; -} - - -/** - * Start session - */ -function doSessionStart($setPaths=false) { - - if( PHP_SESSION_NONE == session_status() ) { - session_set_cookie_params(99999); - } - - if(!isset($_SESSION)) { - session_start(); - if(defined('KINT_ON') && KINT_ON) { - Kint::enabled(true); - } else { - Kint::enabled(false); - } - } - - if($setPaths) { - unset($_SESSION['basehref']); - setPaths(); - } -} - - -/** - * Initialize structure of top menu for the user and the project. - * - * @param integer $db DB connection identifier - * @uses $_SESSION Requires initialized project, test plan and user data. - * @since 1.9 - * - * @internal revisions - */ -function initTopMenu(&$db) -{ - $_SESSION['testprojectTopMenu'] = ''; - $guiTopMenu = config_get('guiTopMenu'); - - $imageSet = TLSmarty::getImageSet(); - - // check if Project is available - if (isset($_SESSION['testprojectID']) && $_SESSION['testprojectID'] > 0) - { - $idx = 1; - foreach ($guiTopMenu as $element) - { - // check if Test Plan is available - if ((!isset($element['condition'])) || ($element['condition'] == '') || - (($element['condition'] == 'TestPlanAvailable') && - isset($_SESSION['testplanID']) && $_SESSION['testplanID'] > 0) || - (($element['condition'] == 'ReqMgmtEnabled') && - isset($_SESSION['testprojectOptions']->requirementsEnabled) && - $_SESSION['testprojectOptions']->requirementsEnabled)) - { - // (is_null($element['right']) => no right needed => display always - - $addItem = is_null($element['right']); - if(!$addItem) - { - if( is_array($element['right'])) - { - foreach($element['right'] as $rg) - { - if( $addItem = (has_rights($db,$rg) == "yes") ) - { - break; - } - } - } - else - { - $addItem = (has_rights($db,$element['right']) == "yes"); - } - } - - if( $addItem ) - { - $_SESSION['testprojectTopMenu'] .= ""; - - if( isset($element['imgKey']) ) - { - $_SESSION['testprojectTopMenu'] .= ''; - } - else - { - $_SESSION['testprojectTopMenu'] .= lang_get($element['label']); - } - - $_SESSION['testprojectTopMenu'] .= "   "; - } - } - } - $_SESSION['testprojectTopMenu'] .= "      "; - } -} - - -/** - * Update Project and Test Plan data on Project change or startup - * Data are stored in $_SESSION array - * - * If we receive TestPlan ID in the _SESSION then do some checks and if everything OK - * Update this value at Session Level, to set it available in other pieces of the application - * - * @param integer $db DB connection identifier - * @param array $hash_user_sel input data for the page ($_REQUEST) - * - * @uses initMenu() - * @internal revisions - **/ -function initProject(&$db,$hash_user_sel) { - - $ckObj = new stdClass(); - $ckCfg = config_get('cookie'); - - $tproject = new testproject($db); - $user_sel = array("tplan_id" => 0, "tproject_id" => 0 ); - $user_sel["tproject_id"] = isset($hash_user_sel['testproject']) ? intval($hash_user_sel['testproject']) : 0; - $user_sel["tplan_id"] = isset($hash_user_sel['testplan']) ? intval($hash_user_sel['testplan']) : 0; - - $tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - // test project is Test Plan container, then we start checking the container - if( $user_sel["tproject_id"] != 0 ) { - $tproject_id = $user_sel["tproject_id"]; - } - - // We need to do checks before updating the SESSION to cover the case that not defined but exists - if (!$tproject_id) { - $all_tprojects = $tproject->get_all(); - if ($all_tprojects) { - $tproject_data = $all_tprojects[0]; - $tproject_id = $tproject_data['id']; - } - } - $tproject->setSessionProject($tproject_id); - - // set a Test Plan - // Refresh test project id after call to setSessionProject - $tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : null; - - // Now we need to validate the TestPlan - $ckObj->name = $ckCfg->prefix . "TL_user${_SESSION['userID']}_proj${tproject_id}_testPlanId"; - - if($user_sel["tplan_id"] != 0) - { - $ckObj->value = $user_sel["tplan_id"]; - $ckObj->expire = time()+60*60*24*90; - tlSetCookie($ckObj); - } - elseif (isset($_COOKIE[$ckObj->name])) - { - $tplan_id = intval($_COOKIE[$ckObj->name]); - } - - // check if the specific combination of testprojectid and testplanid is valid - $tplan_data = $_SESSION['currentUser']->getAccessibleTestPlans($db,$tproject_id,$tplan_id); - if(is_null($tplan_data)) - { - // Need to get first accessible test plan for user, if any exists. - $tplan_data = $_SESSION['currentUser']->getAccessibleTestPlans($db,$tproject_id); - } - - if(!is_null($tplan_data) && is_array($tplan_data)) - { - $tplan_data = $tplan_data[0]; - setSessionTestPlan($tplan_data); - } - - // initialize structure of top menu for the user and the project - initTopMenu($db); -} - - -/** - * General GUI page initialization procedure - * - init session - * - init database - * - check rights - * - initialize project data (if requested) - * - * @param integer $db DB connection identifier - * @param boolean $initProject (optional) Set true if adjustment of Test Project or - * Test Plan is required; default is FALSE - * @param boolean $dontCheckSession (optional) Set to true if no session should be started - * @param string $userRightsCheckFunction (optional) name of function used to check user right needed - * to execute the page - */ -function testlinkInitPage(&$db, $initProject = FALSE, $dontCheckSession = false, - $userRightsCheckFunction = null, $onFailureGoToLogin = false) -{ - static $pageStatistics = null; - - doSessionStart(); - setPaths(); - if( isset($_SESSION['locale']) && !is_null($_SESSION['locale']) ) { - setDateTimeFormats($_SESSION['locale']); - } - doDBConnect($db); - - if (!$pageStatistics && (config_get('log_level') == 'EXTENDED')) { - $pageStatistics = new tlPageStatistics($db); - } - - if (!$dontCheckSession) { - checkSessionValid($db); - } - - if ($userRightsCheckFunction) { - checkUserRightsFor($db,$userRightsCheckFunction,$onFailureGoToLogin); - } - - // Init plugins - plugin_init_installed(); - - // adjust Product and Test Plan to $_SESSION - if ($initProject) { - initProject($db,$_REQUEST); - } - - // used to disable the attachment feature if there are problems with repository path - /** @TODO this check should not be done anytime but on login and using */ - global $g_repositoryPath; - global $g_repositoryType; - global $tlCfg; - $tlCfg->attachments->disabled_msg = ""; - if($g_repositoryType == TL_REPOSITORY_TYPE_FS) { - $ret = checkForRepositoryDir($g_repositoryPath); - if(!$ret['status_ok']) { - $tlCfg->attachments->enabled = FALSE; - $tlCfg->attachments->disabled_msg = $ret['msg']; - } - } -} - - -/** - * Redirect page to another one - * - * @param string URL of required page - * @param string Browser location - use for redirection or refresh of another frame - * Default: 'location' - */ -function redirect($url, $level = 'location') -{ - // XSS Attack - 06486: Cross-Site Scripting on login page - $safeUrl = addslashes($url); - echo ""; - echo ""; - - exit; -} - - -/** - * Security parser for input strings - * - * @param string $parameter - * @return string cleaned parameter - */ -function strings_stripSlashes($parameter,$bGPC = true) -{ - if ($bGPC && !ini_get('magic_quotes_gpc')) - { - return $parameter; - } - - if (is_array($parameter)) - { - $retParameter = null; - if (sizeof($parameter)) - { - foreach($parameter as $key=>$value) - { - if (is_array($value)) - { - $retParameter[$key] = strings_stripSlashes($value,$bGPC); - } - else - { - $retParameter[$key] = stripslashes($value); - } - } - } - return $retParameter; - } - else - { - return stripslashes($parameter); - } -} - - -function to_boolean($alt_boolean) -{ - $the_val = 1; - - if (is_numeric($alt_boolean) && !intval($alt_boolean)) - { - $the_val = 0; - } - else - { - $a_bool = array ("on" => 1, "y" => 1, "off" => 0, "n" => 0); - $alt_boolean = strtolower($alt_boolean); - if(isset($a_bool[$alt_boolean])) - { - $the_val = $a_bool[$alt_boolean]; - } - } - - return $the_val; -} - - -/** - * Validate string by relular expression - * - * @param string $str2check - * @param string $regexp_forbidden_chars Regular expression (perl format) - * - * @return boolean 1: check ok, 0:check KO - * - * @todo havlatm: remove as obsolete or move to inputparam.inc.php - */ -function check_string($str2check, $regexp_forbidden_chars) -{ - $status_ok = 1; - - if( $regexp_forbidden_chars != '' && !is_null($regexp_forbidden_chars)) - { - if (preg_match($regexp_forbidden_chars, $str2check)) - { - $status_ok=0; - } - } - return $status_ok; -} - - -/** - * Load global configuration to function - * - * @param string $config_id key for identification of configuration parameter - * @return mixed the configuration parameter(s) - * - * @internal Revisions - */ -function config_get($config_id, $default=null) { - $t_value = (null == $default) ? '' : $default; - $t_found = false; - $logInfo = array('msg' => "config option not available: {$config_id}", 'level' => 'WARNING'); - if(!$t_found) { - $my = "g_" . $config_id; - if( ($t_found = isset($GLOBALS[$my])) ) - { - $t_value = $GLOBALS[$my]; - } - else - { - $cfg = $GLOBALS['tlCfg']; - if( ($t_found = property_exists($cfg,$config_id)) ) - { - $t_value = $cfg->$config_id; - } - } - - if( $t_found ) - { - $logInfo['msg'] = "config option: {$config_id} is " . - ((is_object($t_value) || is_array($t_value)) ? serialize($t_value) : $t_value); - $logInfo['level'] = 'INFO'; - } - } - - tLog($logInfo['msg'],$logInfo['level']); - return $t_value; -} - - -/** - * @return boolean Return true if the parameter is an empty string or a string - * containing only whitespace, false otherwise - * @author Copyright (C) 2000 - 2004 Mantis Team, Kenzaburo Ito - */ -function is_blank( $p_var ) -{ - $p_var = trim( $p_var ); - $str_len = strlen( $p_var ); - if ( 0 == $str_len ) { - return true; - } - return false; -} - - -/** - * Builds the header needed to make the content available for downloading - * - * @param string $content the content which should be downloaded - * @param string $fileName the filename - **/ -function downloadContentsToFile($content,$fileName,$opt=null) -{ - $my = array(); - $my['opt'] = array('Content-Type' => 'text/plain'); - $my['opt'] = array_merge($my['opt'], (array)$opt); - $charSet = config_get('charset'); - - ob_get_clean(); - header('Pragma: public' ); - header('Content-Type: ' . $my['opt']['Content-Type'] . "; charset={$charSet}; name={$fileName}" ); - header('Content-Transfer-Encoding: BASE64;' ); - header('Content-Disposition: attachment; filename="' . $fileName .'"'); - echo $content; -} - - -/** - * helper function for performance timing - * - * @TODO havlatm: Andreas, move to logger? - * returns: ? - */ -function microtime_float() -{ - list($usec, $sec) = explode(" ", microtime()); - return ((float)$usec + (float)$sec); -} - - -/** - * Converts a priority weight (urgency * importance) to HIGH, MEDUIM or LOW - * - * @return integer HIGH, MEDUIM or LOW - */ -function priority_to_level($priority) { - $urgencyImportance = config_get('urgencyImportance'); - - if ($priority >= $urgencyImportance->threshold['high']) { - return HIGH; - } else if ($priority < $urgencyImportance->threshold['low']) { - return LOW; - } else { - return MEDIUM; - } -} - - -/** - * Get the named php ini variable but return it as a bool - * - * @author Copyright (C) 2000 - 2004 Mantis Team, Kenzaburo Ito - */ -function ini_get_bool( $p_name ) { - $result = ini_get( $p_name ); - - if ( is_string( $result ) ) { - switch ( $result ) { - case 'off': - case 'false': - case 'no': - case 'none': - case '': - case '0': - return false; - break; - case 'on': - case 'true': - case 'yes': - case '1': - return true; - break; - } - } else { - return (bool)$result; - } -} - - -/** - * Trim string and limit to N chars - * - * @param string - * @param int [len]: how many chars return - * - * @return string trimmed string - * - * @author Francisco Mancardi - 20050905 - refactoring - */ -function trim_and_limit($s, $len = 100) -{ - $s = trim($s); - if (tlStringLen($s) > $len) { - $s = tlSubStr($s, 0, $len); - } - - return $s; -} - - -/** @todo havlatm - 20100207 - what's that? and why here. Remove' */ -// nodes_order format: NODE_ID-?,NODE_ID-? -// 2-0,10-0,3-0 -function transform_nodes_order($nodes_order,$node_to_exclude=null) -{ - $fa = explode(',',$nodes_order); - - foreach($fa as $key => $value) - { - // $value= X-Y - $fb = explode('-',$value); - - if( is_null($node_to_exclude) || $fb[0] != $node_to_exclude) - { - $nodes_id[]=$fb[0]; - } - } - - return $nodes_id; -} - - -/** - * Checks $_FILES for errors while uploading - * - * @param array $fInfo an array used by uploading files ($_FILES) - * @return string containing an error message (if any) - */ -function getFileUploadErrorMessage($fInfo,$tlInfo=null) -{ - $msg = null; - if (isset($fInfo['error'])) { - switch($fInfo['error']) { - case UPLOAD_ERR_INI_SIZE: - $msg = lang_get('error_file_size_larger_than_maximum_size_check_php_ini'); - break; - - case UPLOAD_ERR_FORM_SIZE: - $msg = lang_get('error_file_size_larger_than_maximum_size'); - break; - - case UPLOAD_ERR_PARTIAL: - case UPLOAD_ERR_NO_FILE: - $msg = lang_get('error_file_upload'); - break; - } - } - - if (null == $msg && null != $tlInfo && $tlInfo->statusOK == false) { - $msg = lang_get('FILE_UPLOAD_' . $tlInfo->statusCode); - } - return $msg; -} - - -/** - * Redirect to a page with static html defined in locale/en_GB/texts.php - * - * @param string $key keyword for finding exact html text in definition array - */ -function show_instructions($key, $refreshTree=0) -{ - $myURL = $_SESSION['basehref'] . "lib/general/staticPage.php?key={$key}"; - - if( $refreshTree ) - { - $myURL .= "&refreshTree=1"; - } - redirect($myURL); -} - - -/** - * @TODO: franciscom - 20091003 - document return value - */ -function templateConfiguration($template2get=null) -{ - $custom_templates = config_get('tpl'); - $access_key = $template2get; - if( is_null($access_key) ) - { - $access_key = str_replace('.php','',basename($_SERVER['SCRIPT_NAME'])); - } - - $path_parts=explode("/",dirname($_SERVER['SCRIPT_NAME'])); - $last_part=array_pop($path_parts); - $tcfg = new stdClass(); - $tcfg->template_dir = "{$last_part}/"; - $tcfg->default_template = isset($custom_templates[$access_key]) ? $custom_templates[$access_key] : ($access_key . '.tpl'); - $tcfg->template = null; - $tcfg->tpl = $tcfg->template_dir . $tcfg->default_template; - return $tcfg; -} - - -/** - * Check if an string is a valid ISO date/time - * accepted format: YYYY-MM-DD HH:MM:SS - * - * @param string $ISODateTime datetime to check - * @return boolean True if string has correct format - * - * @internal - * rev: 20080907 - franciscom - Code taked form PHP manual - */ -function isValidISODateTime($ISODateTime) -{ - $dateParts=array('YEAR' => 1, 'MONTH' => 2 , 'DAY' => 3); - - $matches=null; - $status_ok=false; - if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $ISODateTime, $matches)) - { - $status_ok=checkdate($matches[$dateParts['MONTH']],$matches[$dateParts['DAY']],$matches[$dateParts['YEAR']]); - } - return $status_ok; -} - -/** - * Check if a localized timestamp is valid - * uses split_localized_date() - * - */ -function is_valid_date($timestamp, $dateFormat) { - $date_array = split_localized_date($timestamp,$dateFormat); - - $status_ok = false; - if ($date_array != null) { - $status_ok = checkdate($date_array['month'],$date_array['day'],$date_array['year']); - } - - return $status_ok; -} - -/** - * Returns array containing date pieces for a given timestamp according to dateFormat - */ - -function split_localized_date($timestamp,$dateFormat) -{ - if(strlen(trim($timestamp)) == 0) - { - return null; - } - - $splitChar = null; - $needle = array(".","-","/","%"); - foreach($needle as $target) - { - if (strpos($timestamp,$target) !== false) - { - $splitChar = $target; - break; - } - } - // put each char of strippedDateFormat into an Array Element - $strippedDateFormat = str_replace($needle,"",$dateFormat); - $format = preg_split('//', $strippedDateFormat, -1, PREG_SPLIT_NO_EMPTY); - $pieces = explode($splitChar,$timestamp); - $result = array(); - if( count($pieces) == 3 ) // MAGIC ALLOWED - { - $k2t = array('Y' => 'year', 'm' => 'month', 'd' => 'day'); - foreach ($format as $idx => $access) - { - $result[$k2t[$access]] = $pieces[$idx]; - } - } - return $result; -} - - -/** - * - * - */ -function checkUserRightsFor(&$db,$pfn,$onFailureGoToLogin=false) -{ - $script = basename($_SERVER['PHP_SELF']); - $currentUser = $_SESSION['currentUser']; - $doExit = false; - $action = null; - - $m2call = $pfn; - $arguments = null; - if( is_object($pfn) ) - { - $m2call = $pfn->method; - $arguments = $pfn->args; - } - - - if (!$m2call($db,$currentUser,$arguments,$action)) - { - if (!$action) - { - $action = "any"; - } - logAuditEvent(TLS("audit_security_user_right_missing",$currentUser->login,$script,$action), - $action,$currentUser->dbID,"users"); - $doExit = true; - } - - if($doExit) - { - $myURL = $_SESSION['basehref']; - if($onFailureGoToLogin) - { - unset($_SESSION['currentUser']); - redirect($myURL ."login.php"); - } - else - { - redirect($myURL,"top.location"); - } - exit(); - } -} - - -function tlStringLen($str) -{ - $charset = config_get('charset'); - $nLen = iconv_strlen($str,$charset); - if ($nLen === false) - { - throw new Exception("Invalid UTF-8 Data detected!"); - } - return $nLen; -} - - -function tlSubStr($str,$start,$length = null) -{ - $charset = config_get('charset'); - if ($length === null) - { - $length = iconv_strlen($str,$charset); - } - // BUGID 3951: replaced iconv_substr() by mb_substr() - $function_call = "mb_substr"; - if (function_exists('iconv_substr') && version_compare(PHP_VERSION, '5.2.0') >= 0) { - $function_call = "iconv_substr"; - } - return $function_call($str,$start,$length,$charset); -} - -/** - * Get text from a configured item template for editor objects - * - * @param $itemTemplate identifies a TestLink item that can have - * templates that can be loaded when creating an item to semplify - * or guide user's work. - * $itemTemplate is a property (of type stdClass) of $tlCfg configuration object. - * - * supported values: - * testcase_template - * - * @param $webEditorName webeditor name, that identifies a propety of $tlCfg->$itemTemplate - * that holds input tenmplate configuration - * - * @param $defaultText text to use if: - * $tlCfg->itemTemplate OR $tlCfg->itemTemplate->$webEditorName - * does not exists. - * - */ -function getItemTemplateContents($itemTemplate, $webEditorName, $defaultText='') { - $editorTemplate = config_get($itemTemplate); - $value=$defaultText; - if( !is_null($editorTemplate) ) { - if (property_exists($editorTemplate, $webEditorName)) { - switch($editorTemplate->$webEditorName->type) { - case 'string': - $value = $editorTemplate->$webEditorName->value; - break; - - case 'string_id': - $value = lang_get($editorTemplate->$webEditorName->value); - break; - - case 'file': - $value = getFileContents($editorTemplate->$webEditorName->value); - if (is_null($value)) { - $value = lang_get('problems_trying_to_access_template') . - " {$editorTemplate->$webEditorName->value} "; - } - break; - - default: - $value = ''; - break; - } - } - } - return $value; -} - - -/** - * Builds a string $testCasePrefix . $glueChar . $external_id - * - * @param string $testCasePrefix prefix for the project without glue character - * @param mixed $external_id - */ -function buildExternalIdString($testCasePrefix, $external_id) -{ - static $glueChar; - if (!$glueChar) { - $glueChar = config_get('testcase_cfg')->glue_character; - } - return $testCasePrefix . $glueChar . $external_id; - -} - -/** - * - * - */ -function displayMemUsage($msg='') -{ - $dx = date('l jS \of F Y h:i:s A'); - echo "
    {$msg} :: {$dx}
    "; - ob_flush();flush(); - echo "memory:" . memory_get_usage() . " - PEAK -> " . memory_get_peak_usage() .'
    '; - ob_flush();flush(); -} - -/** - * - */ -function setUpEnvForRemoteAccess(&$dbHandler,$apikey,$rightsCheck=null,$opt=null) -{ - $my = array('opt' => array('setPaths' => false,'clearSession' => false)); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - if($my['opt']['clearSession']) - { - $_SESSION = null; - } - - doSessionStart($my['opt']['setPaths']); - if( isset($_SESSION['locale']) && !is_null($_SESSION['locale']) ) - { - setDateTimeFormats($_SESSION['locale']); - } - doDBConnect($dbHandler); - - $user = tlUser::getByAPIKey($dbHandler,$apikey); - if( count($user) == 1 ) - { - $_SESSION['lastActivity'] = time(); - $userObj = new tlUser(key($user)); - $userObj->readFromDB($dbHandler); - $_SESSION['currentUser'] = $userObj; - $_SESSION['userID'] = $userObj->dbID; - $_SESSION['locale'] = $userObj->locale; - - // if user do this: - // 1. login to test link - // 2. get direct link and open in new tab or new window while still logged - // 3. logout - // If user refresh tab / window open on (2), because on (3) we destroyed - // session we have loose basehref, and we are not able to recreate it. - // Without basehref we are not able to get CSS, JS, etc. - // In this situation we destroy session, this way user is forced to login - // again in one of two ways - // a. using the direct link - // b. using traditional login - // In both way we assure that behaivour will be OK. - // - if(!isset($_SESSION['basehref'])) - { - session_unset(); - session_destroy(); - if(property_exists($rightsCheck, 'redirect_target') && !is_null($rightsCheck->redirect_target)) - { - redirect($rightsCheck->redirect_target); - } - else - { - // best guess for all features that live on ./lib/results/ - redirect("../../login.php?note=logout"); - } - - exit(); - } - - - - if(!is_null($rightsCheck)) - { - checkUserRightsFor($dbHandler,$rightsCheck,true); - } - } -} - - - -/* - returns map with config values and strings translated (using lang_get()) - to be used on user interface for a Test link configuration option that - is structure in this way: - config_option = array( string_value => any_value, ...) - - All this works if TL_ strings defined on strings.txt follows this naming standard. - - For a config option like: - $tlCfg->workflowStatus=array('draft' => 1, 'review' => 2); - - - will exists: $TL_workflowStatus_draft='...'; - $TL_workflowStatus_review='...'; - - @param string configKey: valus used on call to standard test link - method to get configuration option - - @param string accessMode: two values allowed 'key', 'code' - indicates how the returned map must be indexed. - - 'key' => will be indexed by string - value that is key of config option - - 'code' => will be indexed by value of config option - - @example - - $tlCfg->workflowStatus=array('draft' => 1, 'review' => 2); - $i18nlabels = getLabels('workflowStatus','key'); - array_keys($i18nlabels) will return array('draft','review'); - - - $tlCfg->workflowStatus=array('draft' => 1, 'review' => 2); - $i18nlabels = getLabels('workflowStatus','code'); - array_keys($i18nlabels) will return array(1,2); - - @internal revisions - @since 1.9.7 -*/ - -function getConfigAndLabels($configKey,$accessMode='key') -{ - $stringKeyCode = config_get($configKey); - $labels=null; - foreach( $stringKeyCode as $accessKey => $code ) - { - $index = ($accessMode == 'key') ? $accessKey : $code; - $labels[$index] = lang_get($configKey . '_' . $accessKey); - } - return array('cfg' => $stringKeyCode, 'lbl' => $labels); -} - - -function setDateTimeFormats($locale) -{ - global $tlCfg; - - if($tlCfg->locales_date_format[$locale]) - { - $tlCfg->date_format = $tlCfg->locales_date_format[$locale]; - } - - if($tlCfg->locales_timestamp_format[$locale]) - { - $tlCfg->timestamp_format = $tlCfg->locales_timestamp_format[$locale]; - } -} - -/** - * windowCloseAndOpenerReload() - * will close a popup window and reload caller contents. - */ -function windowCloseAndOpenerReload() -{ - echo ""; - echo ""; - exit; -} - - -/** - * - */ -function setUpEnvForAnonymousAccess(&$dbHandler,$apikey,$rightsCheck=null,$opt=null) -{ - $my = array('opt' => array('setPaths' => false,'clearSession' => false)); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - if($my['opt']['clearSession']) - { - $_SESSION = null; - } - - doSessionStart($my['opt']['setPaths']); - if( isset($_SESSION['locale']) && !is_null($_SESSION['locale']) ) - { - setDateTimeFormats($_SESSION['locale']); - } - doDBConnect($dbHandler); - - // @since 1.9.14 - $checkMode = 'paranoic'; - if(property_exists($rightsCheck->args, 'envCheckMode')) - { - $checkMode = $rightsCheck->args->envCheckMode; - } - - switch($checkMode) - { - case 'hippie': - $tk = array('testplan','testproject'); - break; - - default: - $tk[] = (intval($rightsCheck->args->tplan_id) != 0) ? 'testplan' : 'testproject'; - break; - } - - foreach($tk as $ak) - { - $item = getEntityByAPIKey($dbHandler,$apikey,$ak); - if(!is_null($item)) - { - break; - } - } - - $status_ok = false; - if( !is_null($item) ) - { - $_SESSION['lastActivity'] = time(); - $userObj = new tlUser(); - $_SESSION['currentUser'] = $userObj; - $_SESSION['userID'] = -1; - $_SESSION['locale'] = config_get('default_language'); - - // if user do this: - // 1. login to test link - // 2. get direct link and open in new tab or new window while still logged - // 3. logout - // If user refresh tab / window open on (2), because on (3) we destroyed - // session we have loose basehref, and we are not able to recreate it. - // Without basehref we are not able to get CSS, JS, etc. - // In this situation we destroy session, this way user is forced to login - // again in one of two ways - // a. using the direct link - // b. using traditional login - // In both way we assure that behaivour will be OK. - // - if(!isset($_SESSION['basehref'])) - { - // echo $rightsCheck->redirect_target; - session_unset(); - session_destroy(); - if(property_exists($rightsCheck, 'redirect_target') && !is_null($rightsCheck->redirect_target)) - { - redirect($rightsCheck->redirect_target); - } - else - { - // best guess for all features that live on ./lib/results/ - redirect("../../login.php?note=logout"); - } - exit(); - } - - if(!is_null($rightsCheck->method)) - { - checkUserRightsFor($dbHandler,$rightsCheck->method,true); - } - $status_ok = true; - } - - return $status_ok; -} - -/** - * - */ -function getEntityByAPIKey(&$dbHandler,$apiKey,$type) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $tables = tlObjectWithDB::getDBTables(array('testprojects','testplans')); - switch ($type) - { - case 'testproject': - $target = $tables['testprojects']; - break; - - case 'testplan': - $target = $tables['testplans']; - break; - - default: - throw new Exception("Aborting - Bad type", 1); - break; - } - - $sql = "/* $debugMsg */ " . - " SELECT id FROM {$target} " . - " WHERE api_key = '" . - $dbHandler->prepare_string($apiKey) . "'"; - - $rs = $dbHandler->get_recordset($sql); - return ($rs ? $rs[0] : null); -} - -/** - * - * - */ -function checkAccess(&$dbHandler,&$userObj,$context,$rightsToCheck) -{ - // name of caller script - $script = basename($_SERVER['PHP_SELF']); - $doExit = false; - $action = 'any'; - $env = array('tproject_id' => 0, 'tplan_id' => 0); - $env = array_merge($env, $context); - foreach($env as $key => $val) - { - $env[$key] = intval($val); - } - - if( $doExit = (is_null($env) || $env['tproject_id'] == 0) ) - { - logAuditEvent(TLS("audit_security_no_environment",$script), $action,$userObj->dbID,"users"); - } - - if( !$doExit ) - { - foreach($rightsToCheck->items as $verboseRight) - { - $status = $userObj->hasRight($dbHandler,$verboseRight, - $env['tproject_id'],$env['tplan_id'],true); - if( ($doExit = !$status) && ($rightsToCheck->mode == 'and')) - { - $action = 'any'; - logAuditEvent(TLS("audit_security_user_right_missing",$userObj->login,$script,$action), - $action,$userObj->dbID,"users"); - break; - } - } - } - - if ($doExit) - { - redirect($_SESSION['basehref'],"top.location"); - exit(); - } -} - -/* - function: getWebEditorCfg - - args:- - - returns: - -*/ -function getWebEditorCfg($feature='all') -{ - $cfg = config_get('gui'); - $defaultCfg = $cfg->text_editor['all']; - $webEditorCfg = isset($cfg->text_editor[$feature]) ? $cfg->text_editor[$feature] : $defaultCfg; - - foreach($defaultCfg as $key => $value) - { - if(!isset($webEditorCfg[$key])) - { - $webEditorCfg[$key] = $defaultCfg[$key]; - } - } - return $webEditorCfg; -} - -/** - * - */ -function downloadXls($fname,$xlsType,$gui,$filePrefix) -{ - $sets = array(); - $sets['Excel2007'] = array('ext' => '.xlsx', - 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); - $sets['Excel5'] = array('ext' => '.xls', - 'Content-Type' => 'application/vnd.ms-excel'); - - - $dct = array('Content-Type' => $sets[$xlsType]['Content-Type']); - $content = file_get_contents($fname); - $f2d = $filePrefix . $gui->tproject_name . '_' . $gui->tplan_name . - $sets[$xlsType]['ext']; - - downloadContentsToFile($content,$f2d,$dct); - unlink($fname); - exit(); -} - -/** - * POC on papertrailapp.com - */ -function syslogOnCloud($message, $component = "web", $program = "TestLink") -{ - $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); - foreach(explode("\n", $message) as $line) - { - $syslog_message = "<22>" . date('M d H:i:s ') . $program . ' ' . - $component . ': ' . $line; - socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, - 'logs5.papertrailapp.com', 11613); - } - socket_close($sock); -} - -/** - * - */ -function getSSODisable() -{ - return isset($_REQUEST['ssodisable']) ? 1 : 0; -} - -/** - * - */ -function tlSetCookie($ckObj) { - $stdCk = config_get('cookie'); - - foreach($ckObj as $prop => $value) { - $stdCk->$prop = $value; - } - - setcookie($stdCk->name, $stdCk->value, $stdCk->expire,$stdCk->path, - $stdCk->domain,$stdCk->secure,$stdCk->httponly); -} - - -/** - * - * $opt: skip map each element can be a map - * tplanForInit - * tplanToGetEffectiveRole - */ -function initUserEnv(&$dbH, $context, $opt=null) { - $args = new stdClass(); - $gui = new stdClass(); - - $optDeep = array('skip' => array('tplanForInit' => false, - 'tplanToGetEffectiveRole' => false)); - $options = array('forceCreateProj' => false, - 'initNavBarMenu' => false, - 'caller' => 'not provided'); - if (null != $opt) { - if( isset($opt['skip']) ) { - $optDeep['skip'] = array_merge($optDeep['skip'],$opt['skip']); - } - - foreach ($options as $key => $defa) { - if (isset($opt[$key])) { - $options[$key] = $opt[$key]; - } - } - } - - $args->user = $_SESSION['currentUser']; - $k2l = array( 'tproject_id' => 0, - 'current_tproject_id' => 0, - 'tplan_id' => 0); - - foreach($k2l as $pp => $vv) { - $args->$pp = $vv; - if( isset($_REQUEST[$pp]) ) { - $args->$pp = intval($_REQUEST[$pp]); - } else if (null != $context && property_exists($context, $pp)) { - $args->$pp = intval($context->$pp); - } - } - $tprjMgr = new testproject($dbH); - $guiCfg = config_get("gui"); - $opx = array('output' => 'map_name_with_inactive_mark', - 'field_set' => $guiCfg->tprojects_combo_format, - 'order_by' => $guiCfg->tprojects_combo_order_by); - - $gui->prjSet = $tprjMgr->get_accessible_for_user($args->user->dbID, $opx); - $gui->prjQtyWholeSystem = $tprjMgr->getItemCount(); - $gui->zeroTestProjects = ($gui->prjQtyWholeSystem == 0); - $args->zeroTestProjects = $gui->zeroTestProjects; - - $args->userIsBlindFolded = - (is_null($gui->prjSet) || count($gui->prjSet) == 0) - && $gui->prjQtyWholeSystem > 0; - if( $args->userIsBlindFolded ) { - $args->current_tproject_id = 0; - $args->tproject_id = 0; - $args->tplan_id = 0; - } - - - // It's ok to get testproject context if - // we have the testplan id? - // Can this be a potential security issue? - // Can this create a non coherent situation on GUI? - if( $args->tproject_id == 0 ) { - $args->tproject_id = key($gui->prjSet); - } - - if( $args->current_tproject_id == 0 ) { - $args->current_tproject_id = $args->tproject_id; - } - - $gui->caller = isset($_REQUEST['caller']) ? trim($_REQUEST['caller']) : ''; - $gui->tproject_id = intval($args->tproject_id); - $gui->current_tproject_id = intval($args->current_tproject_id); - $gui->tplan_id = intval($args->tplan_id); - - if( $gui->tproject_id > 0 ) { - // Force to avoid lot of processing - $gui->hasTestCases = $gui->hasKeywords = true; - - $gui->num_active_tplans = $tprjMgr->getActiveTestPlansCount($args->tproject_id); - - // get Test Plans available for the user - // $gpOpt = array('output' => 'map'); - $gpOpt = null; - $gui->tplanSet = (array)$args->user->getAccessibleTestPlans($dbH,$args->tproject_id,$gpOpt); - $gui->countPlans = count($gui->tplanSet); - - /* 20191212 - will remove because have created issues - with IVU, and I not sure anymore of usefulness - if (false == $optDeep['skip']['tplanForInit'] || $args->tplan_id <= 0) { - $gui->tplan_id = $args->tplan_id = (int)doTestPlanSetup($gui); - } - */ - if ($args->tplan_id <= 0) { - $gui->tplan_id = $args->tplan_id = (int)doTestPlanSetup($gui); - } - } - - $doInitUX = ($args->tproject_id > 0) || $options['forceCreateProj']; - $gui->grants = null; - $gui->access = null; - $gui->showMenu = null; - $gui->activeMenu = setSystemWideActiveMenuOFF(); - - /* - if ($options['caller'] != 'not provided') { - echo '
    1509 - caller => ' . $options['caller'] . '
    '; - } - */ - - if( $doInitUX ) { - // echo 'doInitUX
    '; - $gui->grants = getGrantSetWithExit($dbH,$args,$tprjMgr,$options); - $gui->access = getAccess($gui); - $gui->showMenu = getMenuVisibility($gui); - - /* - if ($options['caller'] != 'not provided') { - echo '
    1509 - caller => ' . $options['caller'] . '
    '; - } - */ - } - - // Get Role Description to display. - // This means get Effective Role that has to be calculated - // using current test project & current test plan - // - // SKIP is useful if you want to consider role only - // at test project level. - // - $tplan_id = $gui->tplan_id; - if( $optDeep['skip']['tplanToGetEffectiveRole'] ) { - $tplan_id = null; - } - - $eRoleObj = $args->user->getEffectiveRole($dbH,$gui->tproject_id,$tplan_id); - - $cfg = config_get('gui'); - $gui->whoami = $args->user->getDisplayName() . ' ' . - $cfg->role_separator_open . - $eRoleObj->getDisplayName() . - $cfg->role_separator_close; - - $gui->launcher = $_SESSION['basehref'] . - 'lib/general/frmWorkArea.php'; - - $gui->docs = config_get('userDocOnDesktop') ? getUserDocumentation() : null; - - $secCfg = config_get('config_check_warning_frequence'); - $gui->securityNotes = ''; - if( (strcmp($secCfg, 'ALWAYS') == 0) || - (strcmp($secCfg, 'ONCE_FOR_SESSION') == 0 && !isset($_SESSION['getSecurityNotesOnMainPageDone'])) ) { - $_SESSION['getSecurityNotesOnMainPageDone'] = 1; - $gui->securityNotes = getSecurityNotes($dbH); - } - - $gui->tprojOpt = $tprjMgr->getOptions($args->tproject_id); - $gui->opt_requirements = - isset($gui->tprojOpt->requirementsEnabled) ? - $gui->tprojOpt->requirementsEnabled : null; - - getActions($gui,$_SESSION['basehref']); - - if( $gui->current_tproject_id == null || - trim($gui->current_tproject_id) == '' ) { - } - - $gui->logo = $_SESSION['basehref'] . TL_THEME_IMG_DIR . - config_get('logo_navbar'); - - - $ft = 'form_token'; - $gui->$ft = isset($args->$ft) ? $args->$ft : 0; - if ($gui->$ft == 0 && isset($_REQUEST[$ft])) { - $gui->$ft = $_REQUEST[$ft]; - } - $gui->treeFormToken = $gui->form_token; - - return array($args,$gui,$tprjMgr); -} - -/** - * - * Actions for left side menu - * - */ -function getActions(&$gui,$baseURL) { - $bb = "{$baseURL}lib"; - - $tproject_id = 0; - if( property_exists($gui,'tproject_id')) { - $tproject_id = intval($gui->tproject_id); - } - $ctx = "tproject_id={$tproject_id}"; - - $tplan_id = 0; - if( property_exists($gui,'tplan_id')) { - $tplan_id = intval($gui->tplan_id); - } - $ctx .= "&tplan_id={$tplan_id}"; - - $actions = new stdClass(); - - $actions->events = "$bb/events/eventviewer.php?{$ctx}"; - $actions->usersAssign = "$bb/usermanagement/usersAssign.php?{$ctx}&featureType=testproject&featureID=" . intval($gui->tproject_id); - - $actions->userMgmt = "$bb/usermanagement/usersView.php?{$ctx}" . - intval($gui->tproject_id); - - $actions->userInfo = "$bb/usermanagement/userInfo.php?{$ctx}"; - $actions->projectView = "$bb/project/projectView.php?{$ctx}"; - - $actions->cfAssignment = "$bb/cfields/cfieldsTprojectAssign.php?{$ctx}"; - $actions->cfieldsView = "$bb/cfields/cfieldsView.php?{$ctx}"; - - $actions->keywordsView = "$bb/keywords/keywordsView.php?{$ctx}"; - $actions->platformsView = "$bb/platforms/platformsView.php?{$ctx}"; - $actions->issueTrackerView = "$bb/issuetrackers/issueTrackerView.php?{$ctx}"; - $actions->codeTrackerView = "$bb/codetrackers/codeTrackerView.php?{$ctx}"; - $actions->reqOverView = "$bb/requirements/reqOverview.php?{$ctx}"; - $actions->reqMonOverView = "$bb/requirements/reqMonitorOverview.php?{$ctx}"; - $actions->tcSearch = "$bb/testcases/tcSearch.php?doAction=userInput&{$ctx}"; - $actions->tcCreatedUser = "$bb/results/tcCreatedPerUserOnTestProject.php?do_action=uinput&{$ctx}"; - $actions->assignReq = "$bb/general/frmWorkArea.php?feature=assignReqs&{$ctx}"; - $actions->inventoryView = "$bb/inventory/inventoryView.php?{$ctx}"; - - $actions->fullTextSearch = "$bb/search/searchMgmt.php?{$ctx}"; - - $actions->metrics_dashboard = - "$bb/results/metricsDashboard.php?{$ctx}"; - - - $pp = $bb . '/plan'; - $actions->planView = "$pp/planView.php?{$ctx}"; - - $actions->buildView = null; - $actions->mileView = null; - $actions->platformAssign = null; - $actions->milestonesView = null; - $actions->testcase_assignments = null; - if ($tplan_id >0) { - $actions->buildView = "$pp/buildView.php?{$ctx}"; - $actions->mileView = "$pp/planMilestonesView.php?{$ctx}"; - $actions->platformAssign = "$bb/platforms/platformsAssign.php?{$ctx}"; - $actions->milestonesView = "$bb/plan/planMilestonesView.php?{$ctx}"; - $actions->testcase_assignments = - "$bb/testcases/tcAssignedToUser.php?{$ctx}"; - } - - $launcher = $_SESSION['basehref'] . - "lib/general/frmWorkArea.php?feature="; - - $gui->workArea = new stdClass(); - $gui->workArea->testSpec = "editTc&{$ctx}"; - $gui->workArea->keywordsAssign = "keywordsAssign&{$ctx}"; - - $gui->workArea->planAddTC = null; - $gui->workArea->executeTest = null; - $gui->workArea->setTestUrgency = null; - $gui->workArea->planUpdateTC = null; - $gui->workArea->showNewestTCV = null; - $gui->workArea->assignTCVExecution = null; - $gui->workArea->showMetrics = null; - - if ($tplan_id >0) { - $gui->workArea->planAddTC = "planAddTC&{$ctx}"; - $gui->workArea->executeTest = "executeTest&{$ctx}"; - $gui->workArea->setTestUrgency = "test_urgency&{$ctx}"; - $gui->workArea->planUpdateTC = "planUpdateTC&{$ctx}"; - $gui->workArea->showNewestTCV = "newest_tcversions&{$ctx}"; - $gui->workArea->assignTCVExecution = "tc_exec_assignment&{$ctx}"; - $gui->workArea->showMetrics = "showMetrics&{$ctx}"; - } - - $gui->workArea->reqSpecMgmt = "reqSpecMgmt&{$ctx}"; - $gui->workArea->printReqSpec = "printReqSpec&{$ctx}"; - $gui->workArea->searchReq = "searchReq&{$ctx}"; - $gui->workArea->searchReqSpec = "searchReqSpec&{$ctx}"; - - $wprop = get_object_vars($gui->workArea); - foreach ($wprop as $wp => $wv) { - if (null != $gui->workArea->$wp) { - $gui->workArea->$wp = $launcher . $gui->workArea->$wp; - } - $actions->$wp = $gui->workArea->$wp; - } - - $gui->uri = $actions; - $p2l = get_object_vars($actions); - foreach( $p2l as $pp => $val) { - $gui->$pp = $actions->$pp; - } -} - - -/** - * - */ -function getGrantSetWithExit(&$dbHandler,&$argsObj,&$tprojMgr,$opt=null) { - - /** redirect admin to create testproject if not found */ - $options = array('forceCreateProj' => true); - $options = array_merge($options,(array)$opt); - - if ($options['forceCreateProj'] && $argsObj->zeroTestProjects) { - if ($argsObj->user->hasRight($dbHandler,'mgt_modify_product')) { - redirect($_SESSION['basehref'] . - 'lib/project/projectEdit.php?doAction=create'); - exit(); - } - } - - // User has test project rights - // This talks about Default/Global - // - // key: more or less verbose - // value: string present on rights table - - $systemWideRights = - array( - 'project_edit' => 'mgt_modify_product', - 'configuration' => "system_configuraton", - 'usergroups' => "mgt_view_usergroups", - 'event_viewer' => "events_mgt", - 'user_mgmt' => "mgt_users" - ); - - $r2cTranslate = - array( - 'reqs_view' => "mgt_view_req", - 'monitor_req' => "monitor_requirement", - 'reqs_edit' => "mgt_modify_req", - 'keywords_view' => "mgt_view_key", - 'keywords_edit' => "mgt_modify_key", - 'view_tc' => "mgt_view_tc", - 'view_testcase_spec' => "mgt_view_tc", - 'modify_tc' => 'mgt_modify_tc', - 'testplan_create' => 'mgt_testplan_create'); - - $r2cSame = array ( - 'req_tcase_link_management','keyword_assignment', - 'issuetracker_management','issuetracker_view', - 'codetracker_management','codetracker_view', - 'platform_management','platform_view', - 'cfield_management', - 'cfield_view','cfield_assignment', - 'project_inventory_view','project_inventory_management', - 'testplan_unlink_executed_testcases', - 'testproject_delete_executed_testcases', - 'mgt_testplan_create', - 'testplan_execute','testplan_create_build', - 'testplan_metrics','testplan_planning', - 'testplan_user_role_assignment', - 'testplan_add_remove_platforms', - 'testplan_update_linked_testcase_versions', - 'testplan_set_urgent_testcases', - 'testplan_show_testcases_newest_versions', - 'testplan_milestone_overview', - 'exec_edit_notes','exec_delete','exec_ro_access', - 'exec_testcases_assigned_to_me','exec_assign_testcases'); - - if( ($forceToNo = $argsObj->userIsBlindFolded) ) { - $tr = array_merge($systemWideRights, $r2cTranslate); - $grants = array_fill_keys(array_keys($tr), 'no'); - - foreach($r2cSame as $rr) { - $grants[$rr] = 'no'; - } - return (object)$grants; - } - - // Go ahead, continue with the analysis - // First get system wide rights - foreach ($systemWideRights as $humankey => $right) { - $grants[$humankey] = $argsObj->user->hasRight($dbHandler,$right); - } - - foreach ($r2cTranslate as $humankey => $right) { - $grants[$humankey] = - $argsObj->user->hasRight($dbHandler,$right,$argsObj->tproject_id,$argsObj->tplan_id); - } - - - foreach ($r2cSame as $right) { - $grants[$right] = - $argsObj->user->hasRight($dbHandler,$right,$argsObj->tproject_id,$argsObj->tplan_id); - } - - - // check right ONLY if option is enabled - $tprojOpt = $tprojMgr->getOptions($argsObj->tproject_id); - if($tprojOpt->inventoryEnabled) { - $invr = array('project_inventory_view','project_inventory_management'); - foreach($invr as $r){ - $grants[$r] = ($user->hasRight($dbHandler,$r) == 'yes') ? 1 : 0; - } - } - - $grants['tproject_user_role_assignment'] = "no"; - if( $argsObj->user->hasRight($dbH,"testproject_user_role_assignment", - $argsObj->tproject_id,-1) == "yes" || - $argsObj->user->hasRight($db,"user_role_assignment",null,-1) == "yes" ) { - $grants['tproject_user_role_assignment'] = "yes"; - } - return (object)$grants; -} - -/** - * - */ -function getAccess(&$gui) { - $k2l = array('codetracker','issuetracker','platform'); - foreach($k2l as $ak) { - $access[$ak] = 'no'; - $p_m = $ak . '_management'; - $p_v = $ak . '_view'; - if( 'yes' == $gui->grants->$p_m || - 'yes' == $gui->grants->$p_v ) { - $access[$ak] = 'yes'; - } - } - return $access; -} - - -/** - * - * - */ -function getMenuVisibility(&$gui) -{ - $showMenu = getFirstLevelMenuStructure(); - - if($gui->tproject_id > 0 && - ( $gui->grants->view_tc == "yes" - || $gui->grants->reqs_view == "yes" - || $gui->grants->reqs_edit == "yes") ) { - $showMenu['search'] = true; - } - - //var_dump(__FUNCTION__,$gui->tproject_id,$gui->tplan_id); - if($gui->tproject_id > 0 && - ($gui->grants->cfield_assignment == "yes" || - $gui->grants->cfield_management == "yes" || - $gui->grants->issuetracker_management == "yes" || - $gui->grants->codetracker_management == "yes" || - $gui->grants->issuetracker_view == "yes" || - $gui->grants->codetracker_view == "yes") ) { - $showMenu['system'] = true; - } - - if($gui->tproject_id > 0 && - ($gui->grants->project_edit == "yes" || - $gui->grants->tproject_user_role_assignment == "yes" || - $gui->grants->cfield_management == "yes" || - $gui->grants->platform_management == "yes" || - $gui->grants->keywords_view == "yes") ) { - $showMenu['projects'] = true; - } - - if ( $gui->tproject_id > 0 && - //$gui->opt_requirements == true && TO REACTIVATE - ($gui->grants->reqs_view == "yes" || - $gui->grants->reqs_edit == "yes" || - $gui->grants->monitor_req == "yes" || - $gui->grants->req_tcase_link_management == "yes") ) { - $showMenu['requirements_design'] = true; - } - - if($gui->tproject_id > 0 && - ($gui->grants->view_tc == "yes") ) { - $showMenu['tests_design'] = true; - } - - if($gui->tproject_id > 0 && - ($gui->grants->testplan_planning == "yes" || - $gui->grants->mgt_testplan_create == "yes" || - $gui->grants->testplan_user_role_assignment == "yes" || - $gui->grants->testplan_create_build == "yes") ) { - $showMenu['plans'] = true; - } - - if ($gui->tproject_id > 0 - && $gui->tplan_id > 0 - && ($gui->grants->testplan_execute == "yes" || - $gui->grants->exec_ro_access == "yes") ) { - $showMenu['execution'] = true; - } - - if($gui->tproject_id > 0 && $gui->tplan_id > 0) { - $showMenu['reports'] = true; - } - - return $showMenu; -} - -/** - * - */ -function setSystemWideActiveMenuOFF() -{ - $items = getFirstLevelMenuStructure(); - foreach( $items as $ky => $dm) { - $items[$ky] = ''; - } - return $items; -} - -/** - * - */ -function getFirstLevelMenuStructure() -{ - return array('dashboard' => false, - 'system'=> false, - 'projects' => false, - 'requirements_design' => false, - 'tests_design' => false, - 'plans' => false, - 'execution' => false, - 'reports' => false, - ); -} - - -/** - * - * - */ -function doTestPlanSetup(&$gui) { - $loop2do = count($gui->tplanSet); - if( $loop2do == 0 ) { - return $gui->tplan_id; - } - - $index = 0; - $found = 0; - for($idx = 0; $idx < $loop2do; $idx++) { - if( $gui->tplanSet[$idx]['id'] == $gui->tplan_id ) { - $found = 1; - $index = $idx; - break; - } - } - - if( $found == 0 ) { - $index = 0; - $gui->tplan_id = $gui->tplanSet[$index]['id']; - } - - $gui->tplanSet[$index]['selected']=1; - - return $gui->tplan_id; -} - -/** - * - */ -function initContext() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $context = new stdClass(); - $env = ''; - $k2ctx = array('tproject_id' => 0, - 'tplan_id' => 0, - 'form_token' => 0); - foreach ($k2ctx as $prop => $defa) { - $context->$prop = isset($_REQUEST[$prop]) ? $_REQUEST[$prop] : $defa; - if( is_numeric($defa) ) { - $context->$prop = intval($context->$prop); - } - if ($env != '') { - $env .= "&"; - } - $env .= "$prop=" . $context->$prop; - } - - return array($context,$env); + TICKET 0007190 + */ +if (! defined('TL_APICALL')) { + require_once 'csrf.php'; +} + +/** + * Input data validation + */ +require_once 'inputparameter.inc.php'; + +/** + */ +require_once 'treeMenu.inc.php'; + +// 20130526 checks need to be done in order to understand if this class is really needed +require_once 'exec_cfield_mgr.class.php'; + +/** + * Automatic loader for PHP classes + * See PHP Manual for details + */ +function tlAutoload($class_name) +{ + + // exceptions + // 1. remove prefix and convert lower case + $tlClasses = null; + $tlClassPrefixLen = 2; + $classFileName = $class_name; + + // 2. add a lower case directory + $addDirToInclude = array( + 'Kint' => true + ); + + // this way Zend_Loader_Autoloader will take care of these classes. + // Needed in order to make work bugzillaxmlrpc interface + if (strstr($class_name, 'Zend_') !== false) { + return false; + } + + // Workaround + // https://github.com/smarty-php/smarty/issues/344 + // https://github.com/smarty-php/smarty/pull/345 + if (strpos($class_name, 'Smarty_Internal_Compile_') !== false) { + return false; + } + + if (isset($tlClasses[$classFileName])) { + $len = tlStringLen($classFileName) - $tlClassPrefixLen; + $classFileName = strtolower( + tlSubstr($classFileName, $tlClassPrefixLen, $len)); + } + + if (isset($addDirToInclude[$class_name])) { + $classFileName = strtolower($class_name) . "/" . $class_name; + } + + // Plugin special processing, class name ends with Plugin (see plugin_register()) + // Does not use autoload + if (preg_match('/Plugin$/', $class_name) == 1) { + return; + } + + // fix provided by BitNami for: + // Reason: We had a problem integrating TestLink with other apps. + // You can reproduce it installing ThinkUp and TestLink applications in the same stack. + + try { + include_once $classFileName . '.class.php'; + } catch (Exception $e) {} +} + +/** @var integer global main DB connection identifier */ +$db = 0; + +/** + * TestLink connects to the database + * + * @param + * database &$db reference to resource, here resource pointer will be returned. + * @param boolean $onErrorExit + * default + * false, true standard page will be displayed + * + * @return array aa['status'] = 1 -> OK , 0 -> KO + * aa['dbms_msg''] = 'ok', or $db->error_msg(). + */ +function doDBConnect(&$db, $onErrorExit = false) +{ + global $g_tlLogger; + + $charSet = config_get('charset'); + + switch (DB_TYPE) { + case 'mssql': + $dbDriverName = 'mssqlnative'; + break; + + default: + $dbDriverName = DB_TYPE; + break; + } + + $db = new database($dbDriverName); + $result = $db->connect(DSN, DB_HOST, DB_USER, DB_PASS, DB_NAME); + + if (! $result['status']) { + echo $result['dbms_msg']; + $result['status'] = 0; + $search = array( + '', + '', + '
    ' + ); + $replace = array( + '', + '', + " :: " + ); + $logtext = ' Connect to database ' . DB_NAME . ' on Host ' . + DB_HOST . ' fails
    '; + $logtext .= 'DBMS Error Message: ' . $result['dbms_msg']; + + $logmsg = $logtext . + ($onErrorExit ? '
    Redirection to connection fail screen.' : ''); + tLog(str_replace($search, $replace, $logmsg), 'ERROR'); + if ($onErrorExit) { + $smarty = new TLSmarty(); + $smarty->assign('title', lang_get('fatal_page_title')); + $smarty->assign('content', $logtext); + $smarty->assign('link_to_op', null); + $smarty->display('workAreaSimple.tpl'); + exit(); + } + } else { + if ((DB_TYPE == 'mysql') && ($charSet == 'UTF-8')) { + $db->exec_query("SET CHARACTER SET utf8"); + $db->exec_query("SET collation_connection = 'utf8_general_ci'"); + } + } + + // if we establish a DB connection, we reopen the session, + // to attach the db connection + $g_tlLogger->endTransaction(); + $g_tlLogger->startTransaction(); + + return $result; +} + +/** + * Set session data related to the current test plan + * and saves a cookie with current testplan id + * + * @param array $tplan_info + * result of DB query + */ +function setSessionTestPlan($tplan_info) +{ + if ($tplan_info) { + $_SESSION['testplanID'] = $tplan_info['id']; + $_SESSION['testplanName'] = $tplan_info['name']; + + // Save testplan id for next session + $ckObj = new stdClass(); + + $ckCfg = config_get('cookie'); + $ckObj->name = $ckCfg->prefix . 'TL_lastTestPlanForUserID_' . + intval($_SESSION['userID']); + $ckObj->value = $tplan_info['id']; + + tlSetCookie($ckObj); + + tLog( + "Test Plan was adjusted to '" . $tplan_info['name'] . "' ID(" . + $tplan_info['id'] . ')', 'INFO'); + } else { + unset($_SESSION['testplanID']); + unset($_SESSION['testplanName']); + } +} + +/** + * Set home URL path + * + * @internal revisions + */ +function setPaths() +{ + if (! isset($_SESSION['basehref'])) { + $_SESSION['basehref'] = get_home_url( + array( + 'force_https' => config_get('force_https') + )); + } +} + +/** + * Verify if user is log in. + * Redirect to login page if not. + * + * @param integer $db + * DB identifier + * @param boolean $redirect + * if true (default) redirects user to login page, otherwise returns true/false as login status + */ +function checkSessionValid(&$db, $redirect = true) +{ + $isValidSession = false; + if (isset($_SESSION['userID']) && $_SESSION['userID'] > 0) { + $now = time(); + if (($now - $_SESSION['lastActivity']) <= + (config_get("sessionInactivityTimeout") * 60)) { + $_SESSION['lastActivity'] = $now; + $user = new tlUser($_SESSION['userID']); + $user->readFromDB($db); + $_SESSION['currentUser'] = $user; + $isValidSession = true; + } + } + + if (! $isValidSession && $redirect) { + tLog( + 'Invalid session from ' . $_SERVER["REMOTE_ADDR"] . + '. Redirected to login page.', 'INFO'); + + $fName = "login.php"; + $baseDir = dirname($_SERVER['SCRIPT_FILENAME']); + + while (! file_exists($baseDir . DIRECTORY_SEPARATOR . $fName)) { + $fName = "../" . $fName; + } + $destination = "&destination=" . urlencode($_SERVER['REQUEST_URI']); + redirect($fName . "?note=expired" . $destination, "top.location"); + exit(); + } + return $isValidSession; +} + +/** + * Start session + */ +function doSessionStart($setPaths = false) +{ + if (PHP_SESSION_NONE == session_status()) { + session_set_cookie_params(99999); + } + + if (! isset($_SESSION)) { + session_start(); + if (defined('KINT_ON') && KINT_ON) { + Kint::enabled(true); + } else { + Kint::enabled(false); + } + } + + if ($setPaths) { + unset($_SESSION['basehref']); + setPaths(); + } +} + +/** + * Initialize structure of top menu for the user and the project. + * + * @param integer $db + * DB connection identifier + * @uses $_SESSION Requires initialized project, test plan and user data. + * @since 1.9 + * + * @internal revisions + */ +function initTopMenu(&$db) +{ + $_SESSION['testprojectTopMenu'] = ''; + $guiTopMenu = config_get('guiTopMenu'); + + $imageSet = TLSmarty::getImageSet(); + + // check if Project is available + if (isset($_SESSION['testprojectID']) && $_SESSION['testprojectID'] > 0) { + $idx = 1; + foreach ($guiTopMenu as $element) { + // check if Test Plan is available + $testPlanID = (isset($_SESSION['testplanID']) && + $_SESSION['testplanID'] > 0) ? $_SESSION['testplanID'] : null; + if ((! isset($element['condition'])) || ($element['condition'] == '') || + (($element['condition'] == 'TestPlanAvailable') && + ! is_null($testPlanID)) || + (($element['condition'] == 'ReqMgmtEnabled') && + isset($_SESSION['testprojectOptions']->requirementsEnabled) && + $_SESSION['testprojectOptions']->requirementsEnabled)) { + // (is_null($element['right']) => no right needed => display always + + $addItem = is_null($element['right']); + if (! $addItem) { + if (is_array($element['right'])) { + foreach ($element['right'] as $rg) { + if ($addItem = (has_rights($db, $rg, + $_SESSION['testprojectID'], $testPlanID) == "yes")) { + break; + } + } + } else { + $addItem = (has_rights($db, $element['right'], + $_SESSION['testprojectID'], $testPlanID) == "yes"); + } + } + + if ($addItem) { + $_SESSION['testprojectTopMenu'] .= ""; + + if (isset($element['imgKey'])) { + $_SESSION['testprojectTopMenu'] .= ''; + } else { + $_SESSION['testprojectTopMenu'] .= lang_get( + $element['label']); + } + + $_SESSION['testprojectTopMenu'] .= "   "; + } + } + } + $_SESSION['testprojectTopMenu'] .= "      "; + } +} + +/** + * Update Project and Test Plan data on Project change or startup + * Data are stored in $_SESSION array + * + * If we receive TestPlan ID in the _SESSION then do some checks and if everything OK + * Update this value at Session Level, to set it available in other pieces of the application + * + * @param integer $db + * DB connection identifier + * @param array $hash_user_sel + * input data for the page ($_REQUEST) + * + * @uses initMenu() + * @internal revisions + */ +function initProject(&$db, $hash_user_sel) +{ + $ckObj = new stdClass(); + $ckCfg = config_get('cookie'); + + $tproject = new testproject($db); + $user_sel = array( + "tplan_id" => 0, + "tproject_id" => 0 + ); + $user_sel["tproject_id"] = isset($hash_user_sel['testproject']) ? intval( + $hash_user_sel['testproject']) : 0; + $user_sel["tplan_id"] = isset($hash_user_sel['testplan']) ? intval( + $hash_user_sel['testplan']) : 0; + + $tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + // test project is Test Plan container, then we start checking the container + if ($user_sel["tproject_id"] != 0) { + $tproject_id = $user_sel["tproject_id"]; + } + + // We need to do checks before updating the SESSION to cover the case that not defined but exists + if (! $tproject_id) { + $all_tprojects = $tproject->get_all(); + if ($all_tprojects) { + $tproject_data = $all_tprojects[0]; + $tproject_id = $tproject_data['id']; + } + } + $tproject->setSessionProject($tproject_id); + + // set a Test Plan + // Refresh test project id after call to setSessionProject + $tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : null; + + // Now we need to validate the TestPlan + $ckObj->name = $ckCfg->prefix . + "TL_user{$_SESSION['userID']}_proj{$tproject_id}_testPlanId"; + + if ($user_sel["tplan_id"] != 0) { + $ckObj->value = $user_sel["tplan_id"]; + $ckObj->expire = time() + 60 * 60 * 24 * 90; + tlSetCookie($ckObj); + } elseif (isset($_COOKIE[$ckObj->name])) { + $tplan_id = intval($_COOKIE[$ckObj->name]); + } + + // check if the specific combination of testprojectid and testplanid is valid + $tplan_data = $_SESSION['currentUser']->getAccessibleTestPlans($db, + $tproject_id, $tplan_id); + if (is_null($tplan_data)) { + // Need to get first accessible test plan for user, if any exists. + $tplan_data = $_SESSION['currentUser']->getAccessibleTestPlans($db, + $tproject_id); + } + + if (! is_null($tplan_data) && is_array($tplan_data)) { + $tplan_data = $tplan_data[0]; + setSessionTestPlan($tplan_data); + } + + // initialize structure of top menu for the user and the project + initTopMenu($db); +} + +/** + * General GUI page initialization procedure + * - init session + * - init database + * - check rights + * - initialize project data (if requested) + * + * @param integer $db + * DB connection identifier + * @param boolean $initProject + * (optional) Set true if adjustment of Test Project or + * Test Plan is required; default is FALSE + * @param boolean $dontCheckSession + * (optional) Set to true if no session should be started + * @param string $userRightsCheckFunction + * (optional) name of function used to check user right needed + * to execute the page + */ +function testlinkInitPage(&$db, $initProject = false, $dontCheckSession = false, + $userRightsCheckFunction = null, $onFailureGoToLogin = false) +{ + static $pageStatistics = null; + + doSessionStart(); + setPaths(); + if (isset($_SESSION['locale']) && ! is_null($_SESSION['locale'])) { + setDateTimeFormats($_SESSION['locale']); + } + doDBConnect($db); + + if (! $pageStatistics && (config_get('log_level') == 'EXTENDED')) { + $pageStatistics = new tlPageStatistics($db); + } + + if (! $dontCheckSession) { + checkSessionValid($db); + } + + if ($userRightsCheckFunction !== null) { + checkUserRightsFor($db, $userRightsCheckFunction, $onFailureGoToLogin); + } + + // Init plugins + plugin_init_installed(); + + // adjust Product and Test Plan to $_SESSION + if ($initProject) { + initProject($db, $_REQUEST); + } + + // used to disable the attachment feature if there are problems with repository path + /** + * + * @todo this check should not be done anytime but on login and using + */ + global $g_repositoryPath; + global $g_repositoryType; + global $tlCfg; + $tlCfg->attachments->disabled_msg = ""; + if ($g_repositoryType == TL_REPOSITORY_TYPE_FS) { + $ret = checkForRepositoryDir($g_repositoryPath); + if (! $ret['status_ok']) { + $tlCfg->attachments->enabled = false; + $tlCfg->attachments->disabled_msg = $ret['msg']; + } + } +} + +/** + * Redirect page to another one + * + * @param + * string URL of required page + * @param + * string Browser location - use for redirection or refresh of another frame + * Default: 'location' + */ +function redirect($url, $level = 'location') +{ + // XSS Attack - 06486: Cross-Site Scripting on login page + $safeUrl = addslashes($url); + echo ""; + echo ""; + + exit(); +} + +/** + * Security parser for input strings + * + * @param string $parameter + * @return string cleaned parameter + */ +function strings_stripSlashes($parameter, $bGPC = true) +{ + if ($bGPC && ! ini_get('magic_quotes_gpc')) { + return $parameter; + } + + if (is_array($parameter)) { + $retParameter = null; + if (count($parameter)) { + foreach ($parameter as $key => $value) { + if (is_array($value)) { + $retParameter[$key] = strings_stripSlashes($value, $bGPC); + } else { + $retParameter[$key] = stripslashes($value); + } + } + } + return $retParameter; + } else { + return stripslashes($parameter); + } +} + +function to_boolean($alt_boolean) +{ + $the_val = 1; + + if (is_numeric($alt_boolean) && ! intval($alt_boolean)) { + $the_val = 0; + } else { + $a_bool = array( + "on" => 1, + "y" => 1, + "off" => 0, + "n" => 0 + ); + $alt_boolean = strtolower($alt_boolean); + if (isset($a_bool[$alt_boolean])) { + $the_val = $a_bool[$alt_boolean]; + } + } + + return $the_val; +} + +/** + * Validate string by relular expression + * + * @param string $str2check + * @param string $regexp_forbidden_chars + * Regular expression (perl format) + * @return boolean 1: check ok, 0:check KO + * + * @todo havlatm: remove as obsolete or move to inputparam.inc.php + */ +function checkString($str2check, $regexp_forbidden_chars) +{ + $status_ok = 1; + + if (! empty($regexp_forbidden_chars) && + preg_match($regexp_forbidden_chars, $str2check)) { + $status_ok = 0; + } + return $status_ok; +} + +/** + * Load global configuration to function + * + * @param string $config_id + * key for identification of configuration parameter + * @return mixed the configuration parameter(s) + * + * @internal Revisions + */ +function config_get($config_id, $default = null) +{ + $t_value = (null == $default) ? '' : $default; + $t_found = false; + $logInfo = array( + 'msg' => "config option not available: {$config_id}", + 'level' => 'WARNING' + ); + if (! $t_found) { + $my = "g_" . $config_id; + if ($t_found = isset($GLOBALS[$my])) { + $t_value = $GLOBALS[$my]; + } else { + $cfg = $GLOBALS['tlCfg']; + if ($t_found = property_exists($cfg, $config_id)) { + $t_value = $cfg->$config_id; + } + } + + if ($t_found) { + $logInfo['msg'] = "config option: {$config_id} is " . + ((is_object($t_value) || is_array($t_value)) ? serialize( + $t_value) : $t_value); + $logInfo['level'] = 'INFO'; + } + } + + tLog($logInfo['msg'], $logInfo['level']); + return $t_value; +} + +/** + * + * @return boolean Return true if the parameter is an empty string or a string + * containing only whitespace, false otherwise + * @author Copyright (C) 2000 - 2004 Mantis Team, Kenzaburo Ito + */ +function isBlank($p_var) +{ + $p_var = trim($p_var); + $str_len = strlen($p_var); + if (0 == $str_len) { + return true; + } + return false; +} + +/** + * Builds the header needed to make the content available for downloading + * + * @param string $content + * the content which should be downloaded + * @param string $fileName + * the filename + */ +function downloadContentsToFile($content, $fileName, $opt = null) +{ + $my = array(); + $my['opt'] = array( + 'Content-Type' => 'text/plain' + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + $charSet = config_get('charset'); + + ob_get_clean(); + header('Pragma: public'); + header( + 'Content-Type: ' . $my['opt']['Content-Type'] . + "; charset={$charSet}; name={$fileName}"); + header('Content-Transfer-Encoding: BASE64;'); + header('Content-Disposition: attachment; filename="' . $fileName . '"'); + echo $content; +} + +/** + * helper function for performance timing + * + * @todo havlatm: Andreas, move to logger? + * returns: ? + */ +function microtime_float() +{ + list ($usec, $sec) = explode(" ", microtime()); + return (float) $usec + (float) $sec; +} + +/** + * Converts a priority weight (urgency * importance) to HIGH, MEDUIM or LOW + * + * @return integer HIGH, MEDUIM or LOW + */ +function priority_to_level($priority) +{ + $urgencyImportance = config_get('urgencyImportance'); + + if ($priority >= $urgencyImportance->threshold['high']) { + return HIGH; + } elseif ($priority < $urgencyImportance->threshold['low']) { + return LOW; + } else { + return MEDIUM; + } +} + +/** + * Get the named php ini variable but return it as a bool + * + * @author Copyright (C) 2000 - 2004 Mantis Team, Kenzaburo Ito + */ +function ini_get_bool($p_name) +{ + $result = ini_get($p_name); + + if (is_string($result)) { + switch ($result) { + case 'off': + case 'false': + case 'no': + case 'none': + case '': + case '0': + return false; + break; + case 'on': + case 'true': + case 'yes': + case '1': + return true; + break; + } + } else { + return (bool) $result; + } +} + +/** + * Trim string and limit to N chars + * + * @param + * string + * @param + * int [len]: how many chars return + * @return string trimmed string + * + * @author Francisco Mancardi - 20050905 - refactoring + */ +function trimAndLimit($s, $len = 100) +{ + $s = trim($s); + if (tlStringLen($s) > $len) { + $s = tlSubStr($s, 0, $len); + } + + return $s; +} + +/** + * + * @todo havlatm - 20100207 - what's that? and why here. Remove' + */ +// nodes_order format: NODE_ID-?,NODE_ID-? +// 2-0,10-0,3-0 +function transform_nodes_order($nodes_order, $node_to_exclude = null) +{ + $fa = explode(',', $nodes_order); + + foreach ($fa as $value) { + // $value= X-Y + $fb = explode('-', $value); + + if (is_null($node_to_exclude) || $fb[0] != $node_to_exclude) { + $nodes_id[] = $fb[0]; + } + } + + return $nodes_id; +} + +/** + * Checks $_FILES for errors while uploading + * + * @param array $fInfo + * an array used by uploading files ($_FILES) + * @return string containing an error message (if any) + */ +function getFileUploadErrorMessage($fInfo, $tlInfo = null) +{ + $msg = null; + if (isset($fInfo['error'])) { + switch ($fInfo['error']) { + case UPLOAD_ERR_INI_SIZE: + $msg = lang_get( + 'error_file_size_larger_than_maximum_size_check_php_ini'); + break; + + case UPLOAD_ERR_FORM_SIZE: + $msg = lang_get('error_file_size_larger_than_maximum_size'); + break; + + case UPLOAD_ERR_PARTIAL: + case UPLOAD_ERR_NO_FILE: + $msg = lang_get('error_file_upload'); + break; + } + } + + if (null == $msg && null != $tlInfo && ! $tlInfo->statusOK) { + $msg = lang_get('FILE_UPLOAD_' . $tlInfo->statusCode); + if (property_exists($tlInfo, 'msg')) { + $msg = $tlInfo->msg; + } + } + return $msg; +} + +/** + * Redirect to a page with static html defined in locale/en_GB/texts.php + * + * @param string $key + * keyword for finding exact html text in definition array + */ +function show_instructions($key, $refreshTree = 0) +{ + $myURL = $_SESSION['basehref'] . "lib/general/staticPage.php?key={$key}"; + + if ($refreshTree) { + $myURL .= "&refreshTree=1"; + } + redirect($myURL); +} + +/** + * + * @param string $template2get + * @return stdClass with the template configuration + */ +function templateConfiguration($template2get = null) +{ + $custom_templates = config_get('tpl'); + $access_key = $template2get; + if (is_null($access_key)) { + $access_key = str_replace('.php', '', basename($_SERVER['SCRIPT_NAME'])); + } + + $path_parts = explode("/", dirname($_SERVER['SCRIPT_NAME'])); + $last_part = array_pop($path_parts); + $tcfg = new stdClass(); + $tcfg->template_dir = "{$last_part}/"; + $tcfg->default_template = isset($custom_templates[$access_key]) ? $custom_templates[$access_key] : ($access_key . + '.tpl'); + $tcfg->template = null; + $tcfg->tpl = $tcfg->template_dir . $tcfg->default_template; + return $tcfg; +} + +/** + * Check if an string is a valid ISO date/time + * accepted format: YYYY-MM-DD HH:MM:SS + * + * @param string $isoDateTime + * datetime to check + * @return boolean True if string has correct format + * + * @internal rev: 20080907 - franciscom - Code taked form PHP manual + */ +function isValidISODateTime($isoDateTime) +{ + $dateParts = array( + 'YEAR' => 1, + 'MONTH' => 2, + 'DAY' => 3 + ); + + $matches = null; + $status_ok = false; + if (preg_match( + "/^(\d{4})-(\d{2})-(\d{2}) ([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/", + $isoDateTime, $matches)) { + $status_ok = checkdate($matches[$dateParts['MONTH']], + $matches[$dateParts['DAY']], $matches[$dateParts['YEAR']]); + } + return $status_ok; +} + +/** + * Check if a localized timestamp is valid + * uses split_localized_date() + */ +function is_valid_date($timestamp, $dateFormat) +{ + $date_array = split_localized_date($timestamp, $dateFormat); + + $status_ok = false; + if ($date_array != null) { + $status_ok = checkdate($date_array['month'], $date_array['day'], + $date_array['year']); + } + + return $status_ok; +} + +/** + * Returns array containing date pieces for a given timestamp according to dateFormat + */ +function split_localized_date($timestamp, $dateFormat) +{ + if (strlen(trim($timestamp)) == 0) { + return null; + } + + $splitChar = null; + $needle = array( + ".", + "-", + "/", + "%" + ); + foreach ($needle as $target) { + if (strpos($timestamp, $target) !== false) { + $splitChar = $target; + break; + } + } + // put each char of strippedDateFormat into an Array Element + $strippedDateFormat = str_replace($needle, "", $dateFormat); + $format = preg_split('//', $strippedDateFormat, - 1, PREG_SPLIT_NO_EMPTY); + $pieces = explode($splitChar, $timestamp); + $result = array(); + if (count($pieces) == 3) // MAGIC ALLOWED + { + $k2t = array( + 'Y' => 'year', + 'm' => 'month', + 'd' => 'day' + ); + foreach ($format as $idx => $access) { + $result[$k2t[$access]] = $pieces[$idx]; + } + } + return $result; +} + +/** + */ +function checkUserRightsFor(&$db, $pfn, $onFailureGoToLogin = false) +{ + $script = basename($_SERVER['PHP_SELF']); + $currentUser = $_SESSION['currentUser']; + $doExit = false; + $action = null; + + $m2call = $pfn; + $arguments = null; + if (is_object($pfn)) { + $m2call = $pfn->method; + $arguments = $pfn->args; + } + + if (! $m2call($db, $currentUser, $arguments, $action)) { + if (! $action) { + $action = "any"; + } + logAuditEvent( + TLS("audit_security_user_right_missing", $currentUser->login, + $script, $action), $action, $currentUser->dbID, "users"); + $doExit = true; + } + + if ($doExit) { + $myURL = $_SESSION['basehref']; + if ($onFailureGoToLogin) { + unset($_SESSION['currentUser']); + redirect($myURL . "login.php"); + } else { + redirect($myURL, "top.location"); + } + exit(); + } +} + +function tlStringLen($str) +{ + $charset = config_get('charset'); + $nLen = iconv_strlen($str, $charset); + if ($nLen === false) { + throw new Exception("Invalid UTF-8 Data detected!"); + } + return $nLen; +} + +function tlSubStr($str, $start, $length = null) +{ + $charset = config_get('charset'); + if ($length === null) { + $length = iconv_strlen($str, $charset); + } + // BUGID 3951: replaced iconv_substr() by mb_substr() + $function_call = "mb_substr"; + if (function_exists('iconv_substr') && + version_compare(PHP_VERSION, '5.2.0') >= 0) { + $function_call = "iconv_substr"; + } + return $function_call($str, $start, $length, $charset); +} + +/** + * Get text from a configured item template for editor objects + * + * @param string $itemTemplate + * identifies + * a TestLink item that can have + * templates that can be loaded when creating an item to semplify + * or guide user's work. + * $itemTemplate is a property (of type stdClass) of $tlCfg configuration object. + * + * supported values: + * testcase_template + * + * @param string $webEditorName + * webeditor + * name, that identifies a propety of $tlCfg->$itemTemplate + * that holds input tenmplate configuration + * + * @param string $defaultText + * text + * to use if: + * $tlCfg->itemTemplate OR $tlCfg->itemTemplate->$webEditorName + * does not exists. + * + */ +function getItemTemplateContents($itemTemplate, $webEditorName, + $defaultText = '') +{ + $editorTemplate = config_get($itemTemplate); + $value = $defaultText; + if (! is_null($editorTemplate) && + property_exists($editorTemplate, $webEditorName)) { + switch ($editorTemplate->$webEditorName->type) { + case 'string': + $value = $editorTemplate->$webEditorName->value; + break; + + case 'string_id': + $value = lang_get($editorTemplate->$webEditorName->value); + break; + + case 'file': + $value = getFileContents($editorTemplate->$webEditorName->value); + if (is_null($value)) { + $value = lang_get('problems_trying_to_access_template') . + " {$editorTemplate->$webEditorName->value} "; + } + break; + + case 'none': + default: + break; + } + } + return $value; +} + +/** + * Builds a string $testCasePrefix . + * $glueChar . $external_id + * + * @param string $testCasePrefix + * prefix for the project without glue character + * @param mixed $external_id + */ +function buildExternalIdString($testCasePrefix, $external_id) +{ + static $glueChar; + if (! $glueChar) { + $glueChar = config_get('testcase_cfg')->glue_character; + } + return $testCasePrefix . $glueChar . $external_id; +} + +/** + */ +function displayMemUsage($msg = '') +{ + $dx = date('l jS \of F Y h:i:s A'); + echo "
    {$msg} :: {$dx}
    "; + ob_flush(); + flush(); + echo "memory:" . memory_get_usage() . " - PEAK -> " . memory_get_peak_usage() . + '
    '; + ob_flush(); + flush(); +} + +/** + */ +function setUpEnvForRemoteAccess(&$dbHandler, $apikey, $rightsCheck = null, + $opt = null) +{ + $my = array( + 'opt' => array( + 'setPaths' => false, + 'clearSession' => false + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + if ($my['opt']['clearSession']) { + $_SESSION = null; + } + + doSessionStart($my['opt']['setPaths']); + if (isset($_SESSION['locale']) && ! is_null($_SESSION['locale'])) { + setDateTimeFormats($_SESSION['locale']); + } + doDBConnect($dbHandler); + + $user = tlUser::getByAPIKey($dbHandler, $apikey); + if (count($user) == 1) { + $_SESSION['lastActivity'] = time(); + $userObj = new tlUser(key($user)); + $userObj->readFromDB($dbHandler); + $_SESSION['currentUser'] = $userObj; + $_SESSION['userID'] = $userObj->dbID; + $_SESSION['locale'] = $userObj->locale; + + // if user do this: + // 1. login to test link + // 2. get direct link and open in new tab or new window while still logged + // 3. logout + // If user refresh tab / window open on (2), because on (3) we destroyed + // session we have loose basehref, and we are not able to recreate it. + // Without basehref we are not able to get CSS, JS, etc. + // In this situation we destroy session, this way user is forced to login + // again in one of two ways + // a. using the direct link + // b. using traditional login + // In both way we assure that behaivour will be OK. + // + if (! isset($_SESSION['basehref'])) { + session_unset(); + session_destroy(); + if (property_exists($rightsCheck, 'redirect_target') && + ! is_null($rightsCheck->redirect_target)) { + redirect($rightsCheck->redirect_target); + } else { + // best guess for all features that live on ./lib/results/ + redirect("../../login.php?note=logout"); + } + + exit(); + } + + if (! is_null($rightsCheck)) { + checkUserRightsFor($dbHandler, $rightsCheck, true); + } + } +} + +/* + * returns map with config values and strings translated (using lang_get()) + * to be used on user interface for a Test link configuration option that + * is structure in this way: + * config_option = array( string_value => any_value, ...) + * + * All this works if TL_ strings defined on strings.txt follows this naming standard. + * + * For a config option like: + * $tlCfg->workflowStatus=array('draft' => 1, 'review' => 2); + * + * + * will exists: $TL_workflowStatus_draft='...'; + * $TL_workflowStatus_review='...'; + * + * @param string configKey: valus used on call to standard test link + * method to get configuration option + * + * @param string accessMode: two values allowed 'key', 'code' + * indicates how the returned map must be indexed. + * + * 'key' => will be indexed by string + * value that is key of config option + * + * 'code' => will be indexed by value of config option + * + * @example + * + * $tlCfg->workflowStatus=array('draft' => 1, 'review' => 2); + * $i18nlabels = getLabels('workflowStatus','key'); + * array_keys($i18nlabels) will return array('draft','review'); + * + * + * $tlCfg->workflowStatus=array('draft' => 1, 'review' => 2); + * $i18nlabels = getLabels('workflowStatus','code'); + * array_keys($i18nlabels) will return array(1,2); + * + * @internal revisions + * @since 1.9.7 + */ +function getConfigAndLabels($configKey, $accessMode = 'key') +{ + $stringKeyCode = config_get($configKey); + $labels = null; + foreach ($stringKeyCode as $accessKey => $code) { + $index = ($accessMode == 'key') ? $accessKey : $code; + $labels[$index] = lang_get($configKey . '_' . $accessKey); + } + return array( + 'cfg' => $stringKeyCode, + 'lbl' => $labels + ); +} + +function setDateTimeFormats($locale) +{ + global $tlCfg; + + if ($tlCfg->locales_date_format[$locale]) { + $tlCfg->date_format = $tlCfg->locales_date_format[$locale]; + } + + if ($tlCfg->locales_timestamp_format[$locale]) { + $tlCfg->timestamp_format = $tlCfg->locales_timestamp_format[$locale]; + } +} + +/** + * windowCloseAndOpenerReload() + * will close a popup window and reload caller contents. + */ +function windowCloseAndOpenerReload() +{ + echo ""; + echo ""; + exit(); +} + +/** + */ +function setUpEnvForAnonymousAccess(&$dbHandler, $apikey, $rightsCheck = null, + $opt = null) +{ + $my = array( + 'opt' => array( + 'setPaths' => false, + 'clearSession' => false + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + if ($my['opt']['clearSession']) { + $_SESSION = null; + } + + doSessionStart($my['opt']['setPaths']); + if (isset($_SESSION['locale']) && ! is_null($_SESSION['locale'])) { + setDateTimeFormats($_SESSION['locale']); + } + doDBConnect($dbHandler); + + // @since 1.9.14 + $checkMode = 'paranoic'; + if (property_exists($rightsCheck->args, 'envCheckMode')) { + $checkMode = $rightsCheck->args->envCheckMode; + } + + switch ($checkMode) { + case 'hippie': + $tk = array( + 'testplan', + 'testproject' + ); + break; + + default: + $tk[] = (intval($rightsCheck->args->tplan_id) != 0) ? 'testplan' : 'testproject'; + break; + } + + foreach ($tk as $ak) { + $item = getEntityByAPIKey($dbHandler, $apikey, $ak); + if (! is_null($item)) { + break; + } + } + + $status_ok = false; + if (! is_null($item)) { + $_SESSION['lastActivity'] = time(); + $userObj = new tlUser(); + $_SESSION['currentUser'] = $userObj; + $_SESSION['userID'] = - 1; + $_SESSION['locale'] = config_get('default_language'); + + // if user do this: + // 1. login to test link + // 2. get direct link and open in new tab or new window while still logged + // 3. logout + // If user refresh tab / window open on (2), because on (3) we destroyed + // session we have loose basehref, and we are not able to recreate it. + // Without basehref we are not able to get CSS, JS, etc. + // In this situation we destroy session, this way user is forced to login + // again in one of two ways + // a. using the direct link + // b. using traditional login + // In both way we assure that behaivour will be OK. + // + if (! isset($_SESSION['basehref'])) { + session_unset(); + session_destroy(); + if (property_exists($rightsCheck, 'redirect_target') && + ! is_null($rightsCheck->redirect_target)) { + redirect($rightsCheck->redirect_target); + } else { + // best guess for all features that live on ./lib/results/ + redirect("../../login.php?note=logout"); + } + exit(); + } + + if (! is_null($rightsCheck->method)) { + checkUserRightsFor($dbHandler, $rightsCheck->method, true); + } + $status_ok = true; + } + + return $status_ok; +} + +/** + */ +function getEntityByAPIKey(&$dbHandler, $apiKey, $type) +{ + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $tables = tlObjectWithDB::getDBTables(array( + 'testprojects', + 'testplans' + )); + switch ($type) { + case 'testproject': + $target = $tables['testprojects']; + break; + + case 'testplan': + $target = $tables['testplans']; + break; + + default: + throw new Exception("Aborting - Bad type", 1); + break; + } + + $sql = "/* $debugMsg */ " . " SELECT id FROM {$target} " . + " WHERE api_key = '" . $dbHandler->prepare_string($apiKey) . "'"; + + $rs = $dbHandler->get_recordset($sql); + return $rs ? $rs[0] : null; +} + +/** + */ +function checkAccess(&$dbHandler, &$userObj, $context, $rightsToCheck) +{ + // name of caller script + $script = basename($_SERVER['PHP_SELF']); + $doExit = false; + $action = 'any'; + $env = array( + 'tproject_id' => 0, + 'tplan_id' => 0 + ); + $env = array_merge($env, $context); + foreach ($env as $key => $val) { + $env[$key] = intval($val); + } + + if ($doExit = (is_null($env) || $env['tproject_id'] == 0)) { + logAuditEvent(TLS("audit_security_no_environment", $script), $action, + $userObj->dbID, "users"); + } + + if (! $doExit) { + foreach ($rightsToCheck->items as $verboseRight) { + $status = $userObj->hasRight($dbHandler, $verboseRight, + $env['tproject_id'], $env['tplan_id'], true); + if (($doExit = ! $status) && ($rightsToCheck->mode == 'and')) { + $action = 'any'; + logAuditEvent( + TLS("audit_security_user_right_missing", $userObj->login, + $script, $action), $action, $userObj->dbID, "users"); + break; + } + } + } + + if ($doExit) { + redirect($_SESSION['basehref'], "top.location"); + exit(); + } +} + +/* + * function: getWebEditorCfg + * + * args:- + * + * returns: + * + */ +function getWebEditorCfg($feature = 'all') +{ + $cfg = config_get('gui'); + $defaultCfg = $cfg->text_editor['all']; + $webEditorCfg = isset($cfg->text_editor[$feature]) ? $cfg->text_editor[$feature] : $defaultCfg; + + foreach ($defaultCfg as $key => $value) { + if (! isset($webEditorCfg[$key])) { + $webEditorCfg[$key] = $defaultCfg[$key]; + } + } + return $webEditorCfg; +} + +/** + */ +function downloadXls($fname, $xlsType, $gui, $filePrefix) +{ + $sets = array(); + $sets['Excel2007'] = array( + 'ext' => '.xlsx', + 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ); + $sets['Excel5'] = array( + 'ext' => '.xls', + 'Content-Type' => 'application/vnd.ms-excel' + ); + + $dct = array( + 'Content-Type' => $sets[$xlsType]['Content-Type'] + ); + $content = file_get_contents($fname); + $f2d = $filePrefix . $gui->tproject_name . '_' . $gui->tplan_name . + $sets[$xlsType]['ext']; + + downloadContentsToFile($content, $f2d, $dct); + unlink($fname); + exit(); +} + +/** + * POC on papertrailapp.com + */ +function syslogOnCloud($message, $component = "web", $program = "TestLink") +{ + $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + foreach (explode("\n", $message) as $line) { + $syslog_message = "<22>" . date('M d H:i:s ') . $program . ' ' . + $component . ': ' . $line; + socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, + 'logs5.papertrailapp.com', 11613); + } + socket_close($sock); +} + +/** + */ +function getSSODisable() +{ + return isset($_REQUEST['ssodisable']) ? 1 : 0; +} + +/** + */ +function tlSetCookie($ckObj) +{ + $stdCk = config_get('cookie'); + + foreach ($ckObj as $prop => $value) { + $stdCk->$prop = $value; + } + + setcookie($stdCk->name, $stdCk->value, $stdCk->expire, $stdCk->path, + $stdCk->domain, $stdCk->secure, $stdCk->httponly); +} + +/** + * $opt: skip map each element can be a map + * tplanForInit + * tplanToGetEffectiveRole + */ +function initUserEnv(&$dbH, $context, $opt = null) +{ + $args = new stdClass(); + $gui = new stdClass(); + + $optDeep = array( + 'skip' => array( + 'tplanForInit' => false, + 'tplanToGetEffectiveRole' => false + ) + ); + $options = array( + 'forceCreateProj' => false, + 'initNavBarMenu' => false, + 'caller' => 'not provided' + ); + if (null != $opt) { + if (isset($opt['skip'])) { + $optDeep['skip'] = array_merge($optDeep['skip'], $opt['skip']); + } + + foreach ($options as $key => $defa) { + if (isset($opt[$key])) { + $options[$key] = $opt[$key]; + } + } + } + + $args->user = $_SESSION['currentUser']; + $k2l = array( + 'tproject_id' => 0, + 'current_tproject_id' => 0, + 'tplan_id' => 0 + ); + + foreach ($k2l as $pp => $vv) { + $args->$pp = $vv; + if (isset($_REQUEST[$pp])) { + $args->$pp = intval($_REQUEST[$pp]); + } elseif (null != $context && property_exists($context, $pp)) { + $args->$pp = intval($context->$pp); + } + } + $tprjMgr = new testproject($dbH); + $guiCfg = config_get("gui"); + $opx = array( + 'output' => 'map_name_with_inactive_mark', + 'field_set' => $guiCfg->tprojects_combo_format, + 'order_by' => $guiCfg->tprojects_combo_order_by + ); + + $gui->prjSet = $tprjMgr->get_accessible_for_user($args->user->dbID, $opx); + $gui->prjQtyWholeSystem = $tprjMgr->getItemCount(); + $gui->zeroTestProjects = ($gui->prjQtyWholeSystem == 0); + $args->zeroTestProjects = $gui->zeroTestProjects; + + $args->userIsBlindFolded = (is_null($gui->prjSet) || count($gui->prjSet) == 0) && + $gui->prjQtyWholeSystem > 0; + if ($args->userIsBlindFolded) { + $args->current_tproject_id = 0; + $args->tproject_id = 0; + $args->tplan_id = 0; + } + + // It's ok to get testproject context if + // we have the testplan id? + // Can this be a potential security issue? + // Can this create a non coherent situation on GUI? + if ($args->tproject_id == 0) { + $args->tproject_id = key($gui->prjSet); + } + + if ($args->current_tproject_id == 0) { + $args->current_tproject_id = $args->tproject_id; + } + + $gui->caller = isset($_REQUEST['caller']) ? trim($_REQUEST['caller']) : ''; + $gui->tproject_id = intval($args->tproject_id); + $gui->current_tproject_id = intval($args->current_tproject_id); + $gui->tplan_id = intval($args->tplan_id); + + if ($gui->tproject_id > 0) { + // Force to avoid lot of processing + $gui->hasTestCases = $gui->hasKeywords = true; + + $gui->num_active_tplans = $tprjMgr->getActiveTestPlansCount( + $args->tproject_id); + + // get Test Plans available for the user + // $gpOpt = array('output' => 'map'); + $gpOpt = null; + $gui->tplanSet = (array) $args->user->getAccessibleTestPlans($dbH, + $args->tproject_id, $gpOpt); + $gui->countPlans = count($gui->tplanSet); + + /* + * 20191212 - will remove because have created issues + * with IVU, and I not sure anymore of usefulness + * if (false == $optDeep['skip']['tplanForInit'] || $args->tplan_id <= 0) { + * $gui->tplan_id = $args->tplan_id = (int)doTestPlanSetup($gui); + * } + */ + if ($args->tplan_id <= 0) { + $gui->tplan_id = $args->tplan_id = (int) doTestPlanSetup($gui); + } + } + + $doInitUX = ($args->tproject_id > 0) || $options['forceCreateProj']; + $gui->grants = null; + $gui->access = null; + $gui->showMenu = null; + $gui->activeMenu = setSystemWideActiveMenuOFF(); + + if ($doInitUX) { + $gui->grants = getGrantSetWithExit($dbH, $args, $tprjMgr, $options); + $gui->access = getAccess($gui); + $gui->showMenu = getMenuVisibility($gui); + } + + // Get Role Description to display. + // This means get Effective Role that has to be calculated + // using current test project & current test plan + // + // SKIP is useful if you want to consider role only + // at test project level. + // + $tplan_id = $gui->tplan_id; + if ($optDeep['skip']['tplanToGetEffectiveRole']) { + $tplan_id = null; + } + + $eRoleObj = $args->user->getEffectiveRole($dbH, $gui->tproject_id, $tplan_id); + + $cfg = config_get('gui'); + $gui->whoami = $args->user->getDisplayName() . ' ' . + $cfg->role_separator_open . $eRoleObj->getDisplayName() . + $cfg->role_separator_close; + + $gui->launcher = $_SESSION['basehref'] . 'lib/general/frmWorkArea.php'; + + $gui->docs = config_get('userDocOnDesktop') ? getUserDocumentation() : null; + + $secCfg = config_get('config_check_warning_frequence'); + $gui->securityNotes = ''; + if ((strcmp($secCfg, 'ALWAYS') == 0) || + (strcmp($secCfg, 'ONCE_FOR_SESSION') == 0 && + ! isset($_SESSION['getSecurityNotesOnMainPageDone']))) { + $_SESSION['getSecurityNotesOnMainPageDone'] = 1; + $gui->securityNotes = getSecurityNotes($dbH); + } + + $gui->tprojOpt = $tprjMgr->getOptions($args->tproject_id); + $gui->opt_requirements = isset($gui->tprojOpt->requirementsEnabled) ? $gui->tprojOpt->requirementsEnabled : null; + + getActions($gui, $_SESSION['basehref']); + + $gui->logo = $_SESSION['basehref'] . TL_THEME_IMG_DIR . + config_get('logo_navbar'); + + $ft = 'form_token'; + $gui->$ft = isset($args->$ft) ? $args->$ft : 0; + if ($gui->$ft == 0 && isset($_REQUEST[$ft])) { + $gui->$ft = $_REQUEST[$ft]; + } + $gui->treeFormToken = $gui->form_token; + + return array( + $args, + $gui, + $tprjMgr + ); +} + +/** + * Actions for left side menu + */ +function getActions(&$gui, $baseURL) +{ + $bb = "{$baseURL}lib"; + + $tproject_id = 0; + if (property_exists($gui, 'tproject_id')) { + $tproject_id = intval($gui->tproject_id); + } + $ctx = "tproject_id={$tproject_id}"; + + $tplan_id = 0; + if (property_exists($gui, 'tplan_id')) { + $tplan_id = intval($gui->tplan_id); + } + $ctx .= "&tplan_id={$tplan_id}"; + + $actions = new stdClass(); + + $actions->events = "$bb/events/eventviewer.php?{$ctx}"; + $actions->usersAssign = "$bb/usermanagement/usersAssign.php?{$ctx}&featureType=testproject&featureID=" . + intval($gui->tproject_id); + + $actions->userMgmt = "$bb/usermanagement/usersView.php?{$ctx}" . + intval($gui->tproject_id); + + $actions->userInfo = "$bb/usermanagement/userInfo.php?{$ctx}"; + $actions->projectView = "$bb/project/projectView.php?{$ctx}"; + + $actions->cfAssignment = "$bb/cfields/cfieldsTprojectAssign.php?{$ctx}"; + $actions->cfieldsView = "$bb/cfields/cfieldsView.php?{$ctx}"; + + $actions->keywordsView = "$bb/keywords/keywordsView.php?{$ctx}"; + $actions->platformsView = "$bb/platforms/platformsView.php?{$ctx}"; + $actions->issueTrackerView = "$bb/issuetrackers/issueTrackerView.php?{$ctx}"; + $actions->codeTrackerView = "$bb/codetrackers/codeTrackerView.php?{$ctx}"; + $actions->reqOverView = "$bb/requirements/reqOverview.php?{$ctx}"; + $actions->reqMonOverView = "$bb/requirements/reqMonitorOverview.php?{$ctx}"; + $actions->tcSearch = "$bb/testcases/tcSearch.php?doAction=userInput&{$ctx}"; + $actions->tcCreatedUser = "$bb/results/tcCreatedPerUserOnTestProject.php?do_action=uinput&{$ctx}"; + $actions->assignReq = "$bb/general/frmWorkArea.php?feature=assignReqs&{$ctx}"; + $actions->inventoryView = "$bb/inventory/inventoryView.php?{$ctx}"; + + $actions->fullTextSearch = "$bb/search/searchMgmt.php?{$ctx}"; + + $actions->metrics_dashboard = "$bb/results/metricsDashboard.php?{$ctx}"; + + $pp = $bb . '/plan'; + $actions->planView = "$pp/planView.php?{$ctx}"; + + $actions->buildView = null; + $actions->mileView = null; + $actions->platformAssign = null; + $actions->milestonesView = null; + $actions->testcase_assignments = null; + if ($tplan_id > 0) { + $actions->buildView = "$pp/buildView.php?{$ctx}"; + $actions->mileView = "$pp/planMilestonesView.php?{$ctx}"; + $actions->platformAssign = "$bb/platforms/platformsAssign.php?{$ctx}"; + $actions->milestonesView = "$bb/plan/planMilestonesView.php?{$ctx}"; + $actions->testcase_assignments = "$bb/testcases/tcAssignedToUser.php?{$ctx}"; + } + + $launcher = $_SESSION['basehref'] . "lib/general/frmWorkArea.php?feature="; + + $gui->workArea = new stdClass(); + $gui->workArea->testSpec = "editTc&{$ctx}"; + $gui->workArea->keywordsAssign = "keywordsAssign&{$ctx}"; + + $gui->workArea->planAddTC = null; + $gui->workArea->executeTest = null; + $gui->workArea->setTestUrgency = null; + $gui->workArea->planUpdateTC = null; + $gui->workArea->showNewestTCV = null; + $gui->workArea->assignTCVExecution = null; + $gui->workArea->showMetrics = null; + + if ($tplan_id > 0) { + $gui->workArea->planAddTC = "planAddTC&{$ctx}"; + $gui->workArea->executeTest = "executeTest&{$ctx}"; + $gui->workArea->setTestUrgency = "test_urgency&{$ctx}"; + $gui->workArea->planUpdateTC = "planUpdateTC&{$ctx}"; + $gui->workArea->showNewestTCV = "newest_tcversions&{$ctx}"; + $gui->workArea->assignTCVExecution = "tc_exec_assignment&{$ctx}"; + $gui->workArea->showMetrics = "showMetrics&{$ctx}"; + } + + $gui->workArea->reqSpecMgmt = "reqSpecMgmt&{$ctx}"; + $gui->workArea->printReqSpec = "printReqSpec&{$ctx}"; + $gui->workArea->searchReq = "searchReq&{$ctx}"; + $gui->workArea->searchReqSpec = "searchReqSpec&{$ctx}"; + + $wprop = get_object_vars($gui->workArea); + foreach ($wprop as $wp => $wv) { + if (null != $gui->workArea->$wp) { + $gui->workArea->$wp = $launcher . $gui->workArea->$wp; + } + $actions->$wp = $gui->workArea->$wp; + } + + $gui->uri = $actions; + $p2l = get_object_vars($actions); + foreach ($p2l as $pp => $val) { + $gui->$pp = $actions->$pp; + } +} + +/** + */ +function getGrantSetWithExit(&$dbHandler, &$argsObj, &$tprojMgr, $opt = null) +{ + + /** + * redirect admin to create testproject if not found + */ + $options = array( + 'forceCreateProj' => true + ); + $options = array_merge($options, (array) $opt); + + if ($options['forceCreateProj'] && $argsObj->zeroTestProjects && + $argsObj->user->hasRight($dbHandler, 'mgt_modify_product')) { + redirect( + $_SESSION['basehref'] . 'lib/project/projectEdit.php?doAction=create'); + exit(); + } + + // User has test project rights + // This talks about Default/Global + // + // key: more or less verbose + // value: string present on rights table + + $systemWideRights = array( + 'project_edit' => 'mgt_modify_product', + 'configuration' => "system_configuraton", + 'usergroups' => "mgt_view_usergroups", + 'event_viewer' => "events_mgt", + 'user_mgmt' => "mgt_users" + ); + + $r2cTranslate = array( + 'reqs_view' => "mgt_view_req", + 'monitor_req' => "monitor_requirement", + 'reqs_edit' => "mgt_modify_req", + 'keywords_view' => "mgt_view_key", + 'keywords_edit' => "mgt_modify_key", + 'view_tc' => "mgt_view_tc", + 'view_testcase_spec' => "mgt_view_tc", + 'modify_tc' => 'mgt_modify_tc', + 'testplan_create' => 'mgt_testplan_create' + ); + + $r2cSame = array( + 'req_tcase_link_management', + 'keyword_assignment', + 'issuetracker_management', + 'issuetracker_view', + 'codetracker_management', + 'codetracker_view', + 'platform_management', + 'platform_view', + 'cfield_management', + 'cfield_view', + 'cfield_assignment', + 'project_inventory_view', + 'project_inventory_management', + 'testplan_unlink_executed_testcases', + 'testproject_delete_executed_testcases', + 'mgt_testplan_create', + 'testplan_execute', + 'testplan_create_build', + 'testplan_metrics', + 'testplan_planning', + 'testplan_user_role_assignment', + 'testplan_add_remove_platforms', + 'testplan_update_linked_testcase_versions', + 'testplan_set_urgent_testcases', + 'testplan_show_testcases_newest_versions', + 'testplan_milestone_overview', + 'exec_edit_notes', + 'exec_delete', + 'exec_ro_access', + 'exec_testcases_assigned_to_me', + 'exec_assign_testcases' + ); + + if ($argsObj->userIsBlindFolded) { + $tr = array_merge($systemWideRights, $r2cTranslate); + $grants = array_fill_keys(array_keys($tr), 'no'); + + foreach ($r2cSame as $rr) { + $grants[$rr] = 'no'; + } + return (object) $grants; + } + + // Go ahead, continue with the analysis + // First get system wide rights + foreach ($systemWideRights as $humankey => $right) { + $grants[$humankey] = $argsObj->user->hasRight($dbHandler, $right); + } + + foreach ($r2cTranslate as $humankey => $right) { + $grants[$humankey] = $argsObj->user->hasRight($dbHandler, $right, + $argsObj->tproject_id, $argsObj->tplan_id); + } + + foreach ($r2cSame as $right) { + $grants[$right] = $argsObj->user->hasRight($dbHandler, $right, + $argsObj->tproject_id, $argsObj->tplan_id); + } + + // check right ONLY if option is enabled + $tprojOpt = $tprojMgr->getOptions($argsObj->tproject_id); + if ($tprojOpt->inventoryEnabled) { + $invr = array( + 'project_inventory_view', + 'project_inventory_management' + ); + foreach ($invr as $r) { + $grants[$r] = ($argsObj->user->hasRight($dbHandler, $r) == 'yes') ? 1 : 0; + } + } + + $grants['tproject_user_role_assignment'] = "no"; + if ($argsObj->user->hasRight($dbH, "testproject_user_role_assignment", + $argsObj->tproject_id, - 1) == "yes" || + $argsObj->user->hasRight($db, "user_role_assignment", null, - 1) == "yes") { + $grants['tproject_user_role_assignment'] = "yes"; + } + return (object) $grants; +} + +/** + */ +function getAccess(&$gui) +{ + $k2l = array( + 'codetracker', + 'issuetracker', + 'platform' + ); + foreach ($k2l as $ak) { + $access[$ak] = 'no'; + $p_m = $ak . '_management'; + $p_v = $ak . '_view'; + if ('yes' == $gui->grants->$p_m || 'yes' == $gui->grants->$p_v) { + $access[$ak] = 'yes'; + } + } + return $access; +} + +/** + */ +function getMenuVisibility(&$gui) +{ + $showMenu = getFirstLevelMenuStructure(); + + if ($gui->tproject_id > 0 && + ($gui->grants->view_tc == "yes" || $gui->grants->reqs_view == "yes" || + $gui->grants->reqs_edit == "yes")) { + $showMenu['search'] = true; + } + + if ($gui->tproject_id > 0 && + ($gui->grants->cfield_assignment == "yes" || + $gui->grants->cfield_management == "yes" || + $gui->grants->issuetracker_management == "yes" || + $gui->grants->codetracker_management == "yes" || + $gui->grants->issuetracker_view == "yes" || + $gui->grants->codetracker_view == "yes")) { + $showMenu['system'] = true; + } + + if ($gui->tproject_id > 0 && + ($gui->grants->project_edit == "yes" || + $gui->grants->tproject_user_role_assignment == "yes" || + $gui->grants->cfield_management == "yes" || + $gui->grants->platform_management == "yes" || + $gui->grants->keywords_view == "yes")) { + $showMenu['projects'] = true; + } + + if ($gui->tproject_id > 0 && + // $gui->opt_requirements == true && TO REACTIVATE + ($gui->grants->reqs_view == "yes" || $gui->grants->reqs_edit == "yes" || + $gui->grants->monitor_req == "yes" || + $gui->grants->req_tcase_link_management == "yes")) { + $showMenu['requirements_design'] = true; + } + + if ($gui->tproject_id > 0 && ($gui->grants->view_tc == "yes")) { + $showMenu['tests_design'] = true; + } + + if ($gui->tproject_id > 0 && + ($gui->grants->testplan_planning == "yes" || + $gui->grants->mgt_testplan_create == "yes" || + $gui->grants->testplan_user_role_assignment == "yes" || + $gui->grants->testplan_create_build == "yes")) { + $showMenu['plans'] = true; + } + + if ($gui->tproject_id > 0 && $gui->tplan_id > 0 && + ($gui->grants->testplan_execute == "yes" || + $gui->grants->exec_ro_access == "yes")) { + $showMenu['execution'] = true; + } + + if ($gui->tproject_id > 0 && $gui->tplan_id > 0) { + $showMenu['reports'] = true; + } + + return $showMenu; +} + +/** + */ +function setSystemWideActiveMenuOFF() +{ + $items = getFirstLevelMenuStructure(); + foreach ($items as $ky => $dm) { + $items[$ky] = ''; + } + return $items; +} + +/** + */ +function getFirstLevelMenuStructure() +{ + return array( + 'dashboard' => false, + 'system' => false, + 'projects' => false, + 'requirements_design' => false, + 'tests_design' => false, + 'plans' => false, + 'execution' => false, + 'reports' => false + ); +} + +/** + */ +function doTestPlanSetup(&$gui) +{ + $loop2do = count($gui->tplanSet); + if ($loop2do == 0) { + return $gui->tplan_id; + } + + $index = 0; + $found = 0; + for ($idx = 0; $idx < $loop2do; $idx ++) { + if ($gui->tplanSet[$idx]['id'] == $gui->tplan_id) { + $found = 1; + $index = $idx; + break; + } + } + + if ($found == 0) { + $index = 0; + $gui->tplan_id = $gui->tplanSet[$index]['id']; + } + + $gui->tplanSet[$index]['selected'] = 1; + + return $gui->tplan_id; +} + +/** + */ +function initContext() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $context = new stdClass(); + $env = ''; + $k2ctx = array( + 'tproject_id' => 0, + 'tplan_id' => 0, + 'form_token' => 0 + ); + foreach ($k2ctx as $prop => $defa) { + $context->$prop = isset($_REQUEST[$prop]) ? $_REQUEST[$prop] : $defa; + if (is_numeric($defa)) { + $context->$prop = intval($context->$prop); + } + if ($env != '') { + $env .= "&"; + } + $env .= "$prop=" . $context->$prop; + } + + return array( + $context, + $env + ); +} + +/* + * rights check + */ +function pageAccessCheck(&$db, &$user, $context) +{ + $tplan_id = 0; + if (property_exists($context, 'tplan_id')) { + $tplan_id = $context->tplan_id; + } + + $checkAnd = true; + foreach ($context->rightsAnd as $ri) { + // $user->hasRight() needs refactoring to return ALWAYS boolean + // right now it seems will return + // false or null -> for FALSE + // 'yes' -> for TRUE !!! + $boolCheck = ($user->hasRight($db, $ri, $context->tproject_id, $tplan_id, + true) == 'yes'); + $checkAnd &= $boolCheck; + } + + $checkOr = true; + if ($checkAnd) { + $checkOr = false; + foreach ($context->rightsAnd as $ri) { + $checkOr = $user->hasRight($db, $ri, $context->tproject_id, + $tplan_id, true); + if ($checkOr) { + break; + } + } + } + + if (! $checkAnd && ! $checkOr) { + $script = basename($_SERVER['PHP_SELF']); + $action = 'Access Req Feature'; + $msg = TLS("audit_security_user_right_missing", $user->login, $script, + $action); + logAuditEvent($msg, $action, $user->dbID, "users"); + throw new Exception($msg, 1); + } +} + +/** + */ +function xssStringScriptSafe($content) +{ + $needle = []; + $needle[] = "'; + exit(); + } +} + +/** + * checks if installation is done + * + * @return bool returns true if the installation was already executed, false else + * @author Martin Havlat + */ +function checkInstallStatus() +{ + return defined('DB_TYPE') ? true : false; +} + +/** + * Checks if charts are supported (GD and PNG library) + * + * @return string resulted message ('OK' means pass) + * @author Martin Havlat + */ +function checkLibGd() +{ + if (extension_loaded('gd')) { + $arrLibConf = gd_info(); + $msg = lang_get("error_gd_png_support_disabled"); + if ($arrLibConf["PNG Support"]) { + $msg = 'OK'; + } + } else { + $msg = lang_get("error_gd_missing"); + } + return $msg; +} + +/** + * Checks if needed functions and extensions are defined + * + * @param + * array [ref] msgs will be appended + * @return bool returns true if all extension or functions ar present or defined + * + */ +function checkForExtensions(&$msg) +{ + // without this pChart do not work + if (! extension_loaded('gd')) { + $msg[] = lang_get("error_gd_missing"); + } + return true; +} + +/** + * checks if the install dir is present + * + * @return bool returns true if the install dir is present, false else + */ +function checkForInstallDir() +{ + $installerDir = TL_ABS_PATH . DIRECTORY_SEPARATOR . "install" . + DIRECTORY_SEPARATOR; + clearstatcache(); + return (is_dir($installerDir)) ? true : false; +} + +/** + * checks if the default password for the admin accout is still set + * + * @return boolean returns true if the default password for the admin account is set, + * false else + */ +function checkForAdminDefaultPwd(&$db) +{ + $passwordHasDefaultValue = false; + + $user = new tlUser(); + $user->login = "admin"; + if ($user->readFromDB($db, tlUser::USER_O_SEARCH_BYLOGIN) >= tl::OK && + $user->comparePassword($db, "admin") >= tl::OK) { + $passwordHasDefaultValue = true; + } + return $passwordHasDefaultValue; +} + +/* + * function: checkForLDAPExtension + */ +function checkForLDAPExtension() +{ + return extension_loaded("ldap"); +} + +/** + * builds the security notes while checking some security issues + * these notes should be displayed! + * + * @return array returns the security issues, or null if none found! + * + */ +function getSecurityNotes(&$db) +{ + $repository['type'] = config_get('repositoryType'); + $repository['path'] = config_get('repositoryPath'); + + $securityNotes = null; + if (checkForInstallDir()) { + $securityNotes[] = lang_get("sec_note_remove_install_dir"); + } + + $authCfg = config_get('authentication'); + if ('LDAP' == $authCfg['method']) { + if (! checkForLDAPExtension()) { + $securityNotes[] = lang_get("ldap_extension_not_loaded"); + } + } else { + if (checkForAdminDefaultPwd($db)) { + $securityNotes[] = lang_get("sec_note_admin_default_pwd"); + } + } + + if (! checkForBTSConnection()) { + $securityNotes[] = lang_get("bts_connection_problems"); + } + + if ($repository['type'] == TL_REPOSITORY_TYPE_FS) { + $ret = checkForRepositoryDir($repository['path']); + if (! $ret['status_ok']) { + $securityNotes[] = $ret['msg']; + } + } + + // Needed when schemas change has been done. + // This call can be removed when release is stable + $res = checkSchemaVersion($db); + $msg = $res['msg']; + + if ($msg != "") { + $securityNotes[] = $msg; + } + + $msg = checkEmailConfig(); + if (! is_null($msg)) { + foreach ($msg as $detail) { + $securityNotes[] = $detail; + } + } + checkForExtensions($securityNotes); + + if (! is_null($securityNotes)) { + $user_feedback = config_get('config_check_warning_mode'); + + switch ($user_feedback) { + case 'SCREEN': + break; + + case 'FILE': + case 'SILENT': + $warnings = ''; + $filename = config_get('log_path') . 'config_check.txt'; + if (@$handle = fopen($filename, 'w')) { + $warnings = implode("\n", $securityNotes); + @fwrite($handle, $warnings); + @fclose($handle); + } + $securityNotes = null; + if ($user_feedback == 'FILE') { + $securityNotes[] = sprintf( + lang_get('config_check_warnings'), $filename); + } + break; + } + } + return $securityNotes; +} + +/** + * checks if the connection to the Bug Tracking System database is working + * + * @return boolean returns true if ok + * false else + * @author franciscom + */ +function checkForBTSConnection() +{ + global $g_bugInterface; + $status_ok = true; + if ($g_bugInterface && ! $g_bugInterface->connect()) { + $status_ok = false; + } + return $status_ok; +} + +/** + * Check if server OS is microsoft Windows flavour + * + * @return boolean TRUE if microsoft + * @author havlatm + */ +function isMSWindowsServer() +{ + $osID = strtoupper(substr(PHP_OS, 0, 3)); + return (strcmp('WIN', $osID) == 0) ? true : false; +} + +/* + * function: checkForRepositoryDir + */ +function checkForRepositoryDir($the_dir) +{ + clearstatcache(); + + $ret['msg'] = lang_get('attachments_dir') . " " . $the_dir . " "; + $ret['status_ok'] = false; + + if (is_dir($the_dir)) { + $ret['msg'] .= lang_get('exists') . ' '; + $ret['status_ok'] = (is_writable($the_dir)) ? true : false; + + if ($ret['status_ok']) { + $ret['msg'] .= lang_get('directory_is_writable'); + } else { + $ret['msg'] .= lang_get('but_directory_is_not_writable'); + } + } else { + $ret['msg'] .= lang_get('does_not_exist'); + } + + return $ret; +} + +/** + * Check if DB schema is valid + * + * @param database $db + * Database class + * @return string message + * @todo Update list of versions + */ +function checkSchemaVersion(&$db) +{ + $result = array( + 'status' => tl::ERROR, + 'msg' => null, + 'kill_session' => true + ); + $latest_version = TL_LATEST_DB_VERSION; + $db_version_table = DB_TABLE_PREFIX . 'db_version'; + + $sql = "SELECT * FROM {$db_version_table} ORDER BY upgrade_ts DESC"; + $res = $db->exec_query($sql, 1); + if (! $res) { + return $result['msg'] = "Failed to get Schema version from DB"; + } + + $myrow = $db->fetch_array($res); + + $upgrade_msg = "You need to upgrade your Testlink Database to {$latest_version} -
    " . + 'click here access install and upgrade page
    '; + + $manualop_msg = "You need to proceed with Manual upgrade of your DB scheme to {$latest_version} - Read README file!"; + + switch (trim($myrow['version'])) { + + case '1.7.0 Alpha': + case '1.7.0 Beta 1': + case '1.7.0 Beta 2': + case '1.7.0 Beta 3': + case '1.7.0 Beta 4': + case '1.7.0 Beta 5': + case '1.7.0 RC 2': + case '1.7.0 RC 3': + case 'DB 1.1': + case 'DB 1.2': + $result['msg'] = $upgrade_msg; + break; + + case 'DB 1.3': + case 'DB 1.4': + case 'DB 1.5': + case 'DB 1.6': + case 'DB 1.9.8': + case 'DB 1.9.10': + case 'DB 1.9.11': + case 'DB 1.9.12': + case 'DB 1.9.13': + case 'DB 1.9.14': + case 'DB 1.9.15': + case 'DB 1.9.16': + case 'DB 1.9.17': + case 'DB 1.9.18': + case 'DB 1.9.19': + $result['msg'] = $manualop_msg; + break; + + case 'DB 1.9.20': + // check critic DB schema change because + // blocks login + $m = $db->db->metaColumns(DB_TABLE_PREFIX . 'users'); + if ($m['PASSWORD']->max_length == 32) { + $result['msg'] = "It seems that you have migrated to 1.9.20" . + "
    But migration does not changed users table structure" . + "
    the password field is not able to contain " . + " a bcrypt password"; + $result['status'] = tl::ERROR; + } else { + $result['status'] = tl::OK; + $result['kill_session'] = 'false'; + } + break; + + case $latest_version: + $result['status'] = tl::OK; + $result['kill_session'] = 'false'; + break; + + default: + $result['msg'] = "Unknown Schema version " . trim($myrow['version']) . + ", please upgrade your Testlink Database to " . $latest_version; + break; + } + + /* + * It will be better for debug if this message will be written to a log file + * if($result['status'] != tl::OK) + * { + * + * } + */ + return $result; +} + +/* + * function: checkEmailConfig + * args : + * returns: + */ +function checkEmailConfig() +{ + $common[] = lang_get('check_email_config'); + $msg = null; + $idx = 1; + $key2get = array( + 'tl_admin_email', + 'from_email', + 'return_path_email', + 'smtp_host' + ); + + foreach ($key2get as $cfg_key) { + $cfg_param = config_get($cfg_key); + if (trim($cfg_param) == "" || strpos($cfg_param, 'not_configured') > 0) { + $msg[$idx ++] = $cfg_key; + } + } + return is_null($msg) ? null : $common + $msg; +} + +/** + * checking register global = OFF (doesn't cause error') + * + * @param + * integer &$errCounter reference to error counter + * @return string html table row + */ +function check_php_settings(&$errCounter) +{ + $max_execution_time_recommended = 120; + $max_execution_time = ini_get('max_execution_time'); + $memory_limit_recommended = 64; + $memory_limit = intval(str_ireplace('M', '', ini_get('memory_limit'))); + + $final_msg = 'Checking max. execution time (Parameter max_execution_time)'; + if ($max_execution_time < $max_execution_time_recommended) { + $final_msg .= "{$max_execution_time} seconds - " . + "We suggest {$max_execution_time_recommended} " . + "seconds in order to manage hundred of test cases (edit php.ini)"; + } else { + $final_msg .= 'OK (' . $max_execution_time . + ' seconds)'; + } + $final_msg .= "Checking maximal allowed memory (Parameter memory_limit)"; + if ($memory_limit < $memory_limit_recommended) { + $final_msg .= "$memory_limit MegaBytes - " . + "We suggest {$memory_limit_recommended} MB" . + " in order to manage hundred of test cases"; + } else { + $final_msg .= 'OK (' . $memory_limit . + ' MegaBytes)'; + } + $final_msg .= "Checking if Register Globals is disabled"; + if (ini_get('register_globals')) { + $final_msg .= "Failed! is enabled - " . + "Please change the setting in your php.ini file"; + } else { + $final_msg .= "OK\n"; + } + return $final_msg; +} + +/** + * Check availability of PHP extensions + * + * @param + * integer &$errCounter pointer to error counter + * @return string html table rows + * @author Martin Havlat + * @todo martin: Do we require "Checking DOM XML support"? It seems that we use internal library. + * if (function_exists('domxml_open_file')) + */ +function checkPhpExtensions(&$errCounter) +{ + $td_ok = "OK\n"; + $td_failed = 'Failed! %s %s.'; + + $msg_support = 'Checking %s '; + $checks = array(); + + // Database extensions + $checks[] = array( + 'extension' => 'pgsql', + 'msg' => array( + 'feedback' => 'Postgres Database', + 'ok' => $td_ok, + 'ko' => 'cannot be used' + ) + ); + + $mysqlExt = 'mysql'; + if (version_compare(phpversion(), "7.4.2", ">=")) { + $mysqlExt = 'mysqli'; + } + $checks[] = array( + 'extension' => $mysqlExt, + 'msg' => array( + 'feedback' => 'MySQL Database', + 'ok' => $td_ok, + 'ko' => 'cannot be used' + ) + ); + + // ---------------------------------------------------------------------------- + // special check for MSSQL + $isPHPGTE7 = version_compare(phpversion(), "7.0.0", ">="); + + $extid = 'mssql'; + if (PHP_OS == 'WINNT' || $isPHPGTE7) { + // Faced this problem when testing XAMPP 1.7.7 on Windows 7 with MSSQL 2008 Express + // From PHP MANUAL - reganding mssql_* functions + // These functions allow you to access MS SQL Server database. + // This extension is not available anymore on Windows with PHP 5.3 or later. + // SQLSRV, an alternative driver for MS SQL is available from Microsoft: + // http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx. + // + // Second Time: (2018) + // When using PHP 7 or up + // Help from Bitnami + // PHP 7 does not support mssql anymore. + // The PECL extension recommended is to use the "sqlsrv" module + // but you will need to compile it on your own. + // + // + // PHP_VERSION_ID is available as of PHP 5.2.7 + if (defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50300) { + $extid = 'sqlsrv'; + } + + if ($isPHPGTE7) { + $extid = 'sqlsrv'; + } + } + $checks[] = array( + 'extension' => $extid, + 'msg' => array( + 'feedback' => 'MSSQL Database', + 'ok' => $td_ok, + 'ko' => 'cannot be used' + ) + ); + // --------------------------------------------------------------------------------------------------------- + + $checks[] = array( + 'extension' => 'gd', + 'msg' => array( + 'feedback' => 'GD Graphic library', + 'ok' => $td_ok, + 'ko' => " not enabled.
    Graph rendering requires it. This feature will be disabled." . + " It's recommended to install it." + ) + ); + + $checks[] = array( + 'extension' => 'ldap', + 'msg' => array( + 'feedback' => 'LDAP library', + 'ok' => $td_ok, + 'ko' => " not enabled. LDAP authentication cannot be used. " . + "(default internal authentication will works)" + ) + ); + + $checks[] = array( + 'extension' => 'json', + 'msg' => array( + 'feedback' => 'JSON library', + 'ok' => $td_ok, + 'ko' => " not enabled. You MUST install it to use EXT-JS tree component. " + ) + ); + + $checks[] = array( + 'extension' => 'curl', + 'msg' => array( + 'feedback' => 'cURL library', + 'ok' => $td_ok, + 'ko' => " not enabled. You MUST install it to use REST Integration with issue trackers. " + ) + ); + + $out = ''; + foreach ($checks as $test) { + $out .= sprintf($msg_support, $test['msg']['feedback']); + if (extension_loaded($test['extension'])) { + $msg = $test['msg']['ok']; + } else { + $msg = sprintf($td_failed, $test['msg']['feedback'], + $test['msg']['ko']); + } + $out .= $msg; + } + + return $out; +} + +/** + * Check if web server support session data + * + * @param + * integer &$errCounter reference to error counter + * @return string html row with result + */ +function check_session(&$errCounter) +{ + $out = "Checking if sessions are properly configured"; + + if (! isset($_SESSION)) { + session_start(); + } + + if ($_SESSION['session_test'] != 1) { + $color = 'success'; + $msg = 'OK'; + } else { + $color = 'error'; + $msg = 'Failed!'; + $errCounter ++; + } + + $out .= "$msg\n"; + return $out; +} + +// function end + +/** + * check PHP defined timeout + * + * @param + * integer &$errCounter reference to error counter + * @return string html row with result + */ +function check_timeout(&$errCounter) +{ + $out = 'Maximum Session Idle Time before Timeout'; + + $timeout = ini_get("session.gc_maxlifetime"); + $gc_maxlifetime_min = floor($timeout / 60); + $gc_maxlifetime_sec = $timeout % 60; + + if ($gc_maxlifetime_min > 30) { + $color = 'success'; + $res = 'OK'; + } elseif ($gc_maxlifetime_min > 10) { + $color = 'warning'; + $res = 'Short. Consider to extend.'; + } else { + $color = 'error'; + $res = 'Too short. It must be extended!'; + $errCounter ++; + } + $out .= "" . $gc_maxlifetime_min . + " minutes and $gc_maxlifetime_sec seconds - ($res)\n"; + + return $out; +} + +/** + * check Database type + * + * @param + * integer &$errCounter reference to error counter + * @param string $type + * valid PHP database type label + * + * @return string html row with result + */ +function checkDbType(&$errCounter, $type) +{ + $out = 'Database type'; + + switch ($type) { + case 'mysql': + case 'mysqli': + case 'mssql': + case 'postgres': + $out .= '' . $type . + ''; + break; + + default: + $out .= 'Unsupported type: ' . $type . + '. MySQL,Postgres and MSSQL are supported DB types. Of course' . + ' you can use also other ones without migration support.'; + break; + } + + return $out; +} + +/** + * Display Operating System + * + * @return string html table row + */ +function checkServerOs() +{ + $final_msg = 'Server Operating System (no constrains)'; + $final_msg .= '' . PHP_OS . ''; + + return $final_msg; +} + +/** + * check minimal required PHP version + * + * @param + * integer &$errCounter pointer to error counter + * @return string html row with result + */ +function checkPhpVersion(&$errCounter) +{ + $min_version = '7.4.2'; + $my_version = phpversion(); + + // version_compare: + // -1 if left is less, 0 if equal, +1 if left is higher + $php_ver_comp = version_compare($my_version, $min_version); + + $final_msg = 'PHP version'; + + if ($php_ver_comp < 0) { + $final_msg .= "Failed! - You are running on PHP " . + $my_version . ", and TestLink requires PHP " . $min_version . + ' or greater. ' . 'This is fatal problem. You must upgrade it.'; + $errCounter += 1; + } else { + $final_msg .= "OK ( {$min_version} [minimum version] "; + $final_msg .= ($php_ver_comp == 0 ? " = " : " <= "); + $final_msg .= $my_version . " [your version] "; + $final_msg .= " ) "; + } + + return $final_msg; +} + +/** + * verify that files are writable/readable + * OK result is for state: + * a) installation - writable + * b) installed - readable + * + * @param + * integer &$errCounter pointer to error counter + * @return string html row with result + * @author Martin Havlat + */ +function check_file_permissions(&$errCounter, $inst_type, $checked_filename, + $isCritical = false) +{ + $checked_path = realpath( + dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . + '..'); + $checked_file = $checked_path . DIRECTORY_SEPARATOR . $checked_filename; + $out = 'Access to file (' . $checked_file . ')'; + + if ($inst_type == 'new') { + if (file_exists($checked_file)) { + if (is_writable($checked_file)) { + $out .= "OK (writable)\n"; + } else { + if ($isCritical) { + $out .= "Failed! Please fix the file " . + $checked_file . + " permissions and reload the page."; + $errCounter += 1; + } else { + $out .= "Not writable! Please fix the file " . + $checked_file . " permissions."; + } + } + } else { + if (is_writable($checked_path)) { + $out .= "OK\n"; + } else { + if ($isCritical) { + $out .= "Directory is not writable! Please fix " . + $checked_path . + " permissions and reload the page."; + $errCounter += 1; + } else { + $out .= "Directory is not writable! Please fix " . + $checked_path . " permissions."; + } + } + } + } else { + if (file_exists($checked_file)) { + if (! is_writable($checked_file)) { + $out .= "OK (read only)\n"; + } else { + $out .= "It's recommended to have read only permission for security reason."; + } + } else { + if ($isCritical) { + $out .= "Failed! The file is not on place."; + $errCounter += 1; + } else { + $out .= "The file is not on place."; + } + } + } + + return $out; +} + +/** + * Check read/write permissions for directories + * based on check_with_feedback($dirs_to_check); + * + * @param + * integer &$errCounter pointer to error counter + * @return string html row with result + * @author Martin Havlat + */ +function check_dir_permissions(&$errCounter) +{ + $dirs_to_check = [ + 'templates_c' => [ + 'config' => 'temp_dir', + 'needsLock' => '' + ], + 'logs' => [ + 'config' => 'log_path', + 'needsLock' => '[S] ' + ], + 'upload_area' => [ + 'config' => 'repositoryPath', + 'needsLock' => '[S] ' + ] + ]; + + $final_msg = ''; + $msg_ko = "Failed!"; + $msg_ok = "OK"; + + $final_msg .= "For security reasons we suggest that directories tagged with [S]" . + " on following messages, will be made UNREACHEABLE from browser.
    " . + "Give a look to README file, section 'Installation & SECURITY' " . + " to understand how to change the defaults."; + + $os = strtolower(PHP_OS); + if ($os == 'linux') { + $final_msg .= '
    Give a look to SELINUX section in README.md'; + } + $final_msg .= ""; + + foreach ($dirs_to_check as $the_d => $settings) { + + $the_d = config_get($settings['config']); + $needsLock = $settings['needsLock']; + + $final_msg .= "Checking if {$the_d} directory exists {$needsLock}"; + if (! file_exists($the_d)) { + $errCounter += 1; + $final_msg .= $msg_ko; + } else { + $final_msg .= $msg_ok; + $final_msg .= "Checking if {$the_d} directory is writable (by user used to run webserver process) "; + if (! is_writable($the_d)) { + $errCounter += 1; + $final_msg .= $msg_ko; + } else { + $final_msg .= $msg_ok; + } + } + } + + return $final_msg; +} + +/** + * Print table with checking www browser support + * + * @param + * integer &$errCounter pointer to error counter + * @author Martin Havlat + */ +function reportCheckingBrowser(&$errCounter) +{ + $browser = strtolower($_SERVER['HTTP_USER_AGENT']); + + echo "\n" . + '

    Browser compliance

    ' . + "\n"; + + echo '

    ' . $browser . '

    '; + echo ''; + + if (strpos($browser, 'firefox') === false || + strpos($browser, 'msie') === false) { + echo ""; + } else { + echo ""; + } + + echo ''; + + echo '
    Browser supportedOK
    Unsupported: {$_SERVER['HTTP_USER_AGENT']}
    Javascript availability' . + '' . + '' . + '
    '; +} + +/** + * print table with system checking results + * + * @param + * integer &$errCounter reference to error counter + * @author Martin Havlat + */ +function reportCheckingSystem(&$errCounter) +{ + echo '

    System requirements

    '; + echo checkServerOs(); + echo checkPhpVersion($errCounter); + echo '
    '; +} + +/** + * print table with database checking + * + * @param + * integer &$errCounter reference to error counter + * @author Martin Havlat + */ +function reportCheckingDatabase(&$errCounter, $type = null) +{ + if (checkInstallStatus()) { + $type = DB_TYPE; + } + + if (! is_null($type)) { + echo '

    Database checking

    '; + echo checkDbType($errCounter, $type); + echo "
    \n"; + } +} + +/** + * print table with system checking results + * + * @param + * integer &$errCounter reference to error counter + * @author Martin Havlat + */ +function reportCheckingWeb(&$errCounter) +{ + echo '

    Web and PHP configuration

    '; + echo check_timeout($errCounter); + echo check_php_settings($errCounter); + echo checkPhpExtensions($errCounter); + echo '
    '; +} + +/** + * print table with system checking results + * + * @param + * integer &$errCounter pointer to error counter + * @param + * string installationType: useful when this function is used on installer + * + * @author Martin Havlat + */ +function reportCheckingPermissions(&$errCounter, $installationType = 'none') +{ + echo '

    Read/write permissions

    '; + echo check_dir_permissions($errCounter); + + // for $installationType='upgrade' existence of config_db.inc.php is not needed + $blockingCheck = $installationType == 'upgrade' ? false : true; + if ($installationType == 'new') { + echo check_file_permissions($errCounter, $installationType, + 'config_db.inc.php', $blockingCheck); + } + echo '
    '; } - - -/** check language acceptance by web client */ -function checkServerLanguageSettings($defaultLanguage) { - $language = $defaultLanguage; - - // check for !== false because getenv() returns false on error - $serverLanguage = getenv($_SERVER['HTTP_ACCEPT_LANGUAGE']); - if(false !== $serverLanguage) { - $localeSet = config_get('locales'); - if (array_key_exists($serverLanguage,$localeSet)) - { - $language = $serverLanguage; - } - } - - return $language; -} - - -/** Check if we need to run the install program. Used on login.php and index.php */ -function checkConfiguration() -{ - clearstatcache(); - $file_to_check = "config_db.inc.php"; - if(!is_file($file_to_check)) { - echo ''; - exit(); - } -} - - -/** - * checks if installation is done - * - * @return bool returns true if the installation was already executed, false else - * @author Martin Havlat - **/ -function checkInstallStatus() -{ - $status=defined('DB_TYPE') ? true : false; - return $status; -} - - -/** - * Checks if charts are supported (GD and PNG library) - * - * @return string resulted message ('OK' means pass) - * @author Martin Havlat - **/ -function checkLibGd() -{ - if( extension_loaded('gd') ) { - $arrLibConf = gd_info(); - $msg = lang_get("error_gd_png_support_disabled"); - if ($arrLibConf["PNG Support"]) { - $msg = 'OK'; - } - } else { - $msg = lang_get("error_gd_missing"); - } - return $msg; -} - - -/** - * Checks if needed functions and extensions are defined - * - * @param array [ref] msgs will be appended - * @return bool returns true if all extension or functions ar present or defined - * - **/ -function checkForExtensions(&$msg) -{ - // without this pChart do not work - if( !extension_loaded('gd') ) - { - $msg[] = lang_get("error_gd_missing"); - } - return true; -} - -/** - * checks if the install dir is present - * - * @return bool returns true if the install dir is present, false else - **/ -function checkForInstallDir() -{ - $installerDir = TL_ABS_PATH. DIRECTORY_SEPARATOR . "install" . DIRECTORY_SEPARATOR; - clearstatcache(); - $dirExists= (is_dir($installerDir)) ? true : false; - return $dirExists; -} - - -/** - * checks if the default password for the admin accout is still set - * - * @return boolean returns true if the default password for the admin account is set, - * false else - **/ -function checkForAdminDefaultPwd(&$db) -{ - $passwordHasDefaultValue = false; - - $user = new tlUser(); - $user->login = "admin"; - if ($user->readFromDB($db,tlUser::USER_O_SEARCH_BYLOGIN) >= tl::OK && - $user->comparePassword($db,"admin") >= tl::OK) { - $passwordHasDefaultValue = true; - } - return $passwordHasDefaultValue; -} - -/* - function: checkForLDAPExtension -*/ -function checkForLDAPExtension() -{ - return extension_loaded("ldap"); -} - -/** - * builds the security notes while checking some security issues - * these notes should be displayed! - * - * @return array returns the security issues, or null if none found! - * - **/ -function getSecurityNotes(&$db) -{ - $repository['type'] = config_get('repositoryType'); - $repository['path'] = config_get('repositoryPath'); - - $securityNotes = null; - if (checkForInstallDir()) { - $securityNotes[] = lang_get("sec_note_remove_install_dir"); - } - - $authCfg = config_get('authentication'); - if( 'LDAP' == $authCfg['method'] ) { - if( !checkForLDAPExtension() ) { - $securityNotes[] = lang_get("ldap_extension_not_loaded"); - } - } else { - if( checkForAdminDefaultPwd($db) ) { - $securityNotes[] = lang_get("sec_note_admin_default_pwd"); - } - } - - - if (!checkForBTSConnection()) { - $securityNotes[] = lang_get("bts_connection_problems"); - } - - if($repository['type'] == TL_REPOSITORY_TYPE_FS) { - $ret = checkForRepositoryDir($repository['path']); - if(!$ret['status_ok']) { - $securityNotes[] = $ret['msg']; - } - } - - // Needed when schemas change has been done. - // This call can be removed when release is stable - $res = checkSchemaVersion($db); - $msg = $res['msg']; - - if($msg != "") { - $securityNotes[] = $msg; - } - - $msg = checkEmailConfig(); - if(!is_null($msg)) { - foreach($msg as $detail) - { - $securityNotes[] = $detail; - } - } - checkForExtensions($securityNotes); - - if(!is_null($securityNotes)) - { - $user_feedback=config_get('config_check_warning_mode'); - - switch($user_feedback) - { - case 'SCREEN': - break; - - case 'FILE': - case 'SILENT': - $warnings=''; - $filename = config_get('log_path') . 'config_check.txt'; - if (@$handle = fopen($filename, 'w')) - { - $warnings=implode("\n",$securityNotes); - @fwrite($handle, $warnings); - @fclose($handle); - } - $securityNotes=null; - if($user_feedback=='FILE') - { - $securityNotes[] = sprintf(lang_get('config_check_warnings'),$filename); - } - break; - } - } - return $securityNotes; -} - - -/** - * checks if the connection to the Bug Tracking System database is working - * - * @return boolean returns true if ok - * false else - * @author franciscom - **/ -function checkForBTSConnection() -{ - - global $g_bugInterface; - $status_ok = true; - if($g_bugInterface && !$g_bugInterface->connect()) - { - $status_ok = false; - } - return $status_ok; -} - -/** - * Check if server OS is microsoft Windows flavour - * - * @return boolean TRUE if microsoft - * @author havlatm - */ -function isMSWindowsServer() -{ - $osID = strtoupper(substr(PHP_OS, 0, 3)); - $isWindows = (strcmp('WIN',$osID) == 0) ? true: false; - return $isWindows; -} - -/* - function: checkForRepositoryDir -*/ -function checkForRepositoryDir($the_dir) -{ - clearstatcache(); - - $ret['msg']=lang_get('attachments_dir') . " " . $the_dir . " "; - $ret['status_ok']=false; - - if(is_dir($the_dir)) - { - $ret['msg'] .= lang_get('exists') . ' '; - $ret['status_ok'] = true; - $ret['status_ok'] = (is_writable($the_dir)) ? true : false; - - if($ret['status_ok']) - { - $ret['msg'] .= lang_get('directory_is_writable'); - } - else - { - $ret['msg'] .= lang_get('but_directory_is_not_writable'); - } - } - else - { - $ret['msg'] .= lang_get('does_not_exist'); - } - - return $ret; -} - - -/** - * Check if DB schema is valid - * - * @param pointer $db Database class - * @return string message - * @todo Update list of versions - */ -function checkSchemaVersion(&$db) -{ - $result = array('status' => tl::ERROR, 'msg' => null, 'kill_session' => true); - $latest_version = TL_LATEST_DB_VERSION; - $db_version_table = DB_TABLE_PREFIX . 'db_version'; - - $sql = "SELECT * FROM {$db_version_table} ORDER BY upgrade_ts DESC"; - $res = $db->exec_query($sql,1); - if (!$res) { - return $result['msg'] = "Failed to get Schema version from DB"; - } - - $myrow = $db->fetch_array($res); - - $upgrade_msg = "You need to upgrade your Testlink Database to {$latest_version} -
    " . - 'click here access install and upgrade page
    '; - - $manualop_msg = "You need to proceed with Manual upgrade of your DB scheme to {$latest_version} - Read README file!"; - - switch (trim($myrow['version'])) { - - case '1.7.0 Alpha': - case '1.7.0 Beta 1': - case '1.7.0 Beta 2': - case '1.7.0 Beta 3': - case '1.7.0 Beta 4': - case '1.7.0 Beta 5': - case '1.7.0 RC 2': - case '1.7.0 RC 3': - case 'DB 1.1': - case 'DB 1.2': - $result['msg'] = $upgrade_msg; - break; - - case 'DB 1.3': - case 'DB 1.4': - case 'DB 1.5': - case 'DB 1.6': - case 'DB 1.9.8': - case 'DB 1.9.10': - case 'DB 1.9.11': - case 'DB 1.9.12': - case 'DB 1.9.13': - case 'DB 1.9.14': - case 'DB 1.9.15': - case 'DB 1.9.16': - case 'DB 1.9.17': - case 'DB 1.9.18': - case 'DB 1.9.19': - $result['msg'] = $manualop_msg; - break; - - case 'DB 1.9.20': - // check critic DB schema change because - // blocks login - $m = $db->db->metaColumns(DB_TABLE_PREFIX . 'users'); - if ($m['PASSWORD']->max_length == 32) { - $result['msg'] = - "It seems that you have migrated to 1.9.20" . - "
    But migration does not changed users table structure" . - "
    the password field is not able to contain " . - " a bcrypt password"; - $result['status'] = tl::ERROR; - } else { - $result['status'] = tl::OK; - $result['kill_session'] = 'false'; - } - break; - - case $latest_version: - $result['status'] = tl::OK; - $result['kill_session'] = 'false'; - break; - - default: - $result['msg'] = "Unknown Schema version " . trim($myrow['version']) . - ", please upgrade your Testlink Database to " . $latest_version; - break; - } - - /* It will be better for debug if this message will be written to a log file - if($result['status'] != tl::OK) - { - - } - */ - return $result; -} - -/* - function: checkEmailConfig - args : - returns: -*/ -function checkEmailConfig() -{ - $common[] = lang_get('check_email_config'); - $msg = null; - $idx = 1; - $key2get = array('tl_admin_email','from_email','return_path_email','smtp_host'); - - foreach($key2get as $cfg_key) - { - $cfg_param = config_get($cfg_key); - if(trim($cfg_param) == "" || strpos($cfg_param,'not_configured') > 0 ) - { - $msg[$idx++] = $cfg_key; - } - } - return is_null($msg) ? null : $common+$msg; -} - -/** - * checking register global = OFF (doesn't cause error') - * @param integer &$errCounter reference to error counter - * @return string html table row - */ -function check_php_settings(&$errCounter) -{ - $max_execution_time_recommended = 120; - $max_execution_time = ini_get('max_execution_time'); - $memory_limit_recommended = 64; - $memory_limit = intval(str_ireplace('M','',ini_get('memory_limit'))); - - $final_msg = 'Checking max. execution time (Parameter max_execution_time)'; - if($max_execution_time < $max_execution_time_recommended) - { - $final_msg .= "{$max_execution_time} seconds - " . - "We suggest {$max_execution_time_recommended} " . - "seconds in order to manage hundred of test cases (edit php.ini)"; - } - else - { - $final_msg .= 'OK ('.$max_execution_time.' seconds)'; - } - $final_msg .= "Checking maximal allowed memory (Parameter memory_limit)"; - if($memory_limit < $memory_limit_recommended) - { - $final_msg .= "$memory_limit MegaBytes - " . - "We suggest {$memory_limit_recommended} MB" . - " in order to manage hundred of test cases"; - } - else - { - $final_msg .= 'OK ('.$memory_limit.' MegaBytes)'; - } - $final_msg .= "Checking if Register Globals is disabled"; - if(ini_get('register_globals')) - { - $final_msg .= "Failed! is enabled - " . - "Please change the setting in your php.ini file"; - } - else - { - $final_msg .= "OK\n"; - } - return ($final_msg); -} - - -/** - * Check availability of PHP extensions - * - * @param integer &$errCounter pointer to error counter - * @return string html table rows - * @author Martin Havlat - * @todo martin: Do we require "Checking DOM XML support"? It seems that we use internal library. - * if (function_exists('domxml_open_file')) - */ -function checkPhpExtensions(&$errCounter) { - - $cannot_use='cannot be used'; - $td_ok = "OK\n"; - $td_failed = 'Failed! %s %s.'; - - $msg_support='Checking %s '; - $checks=array(); - - // Database extensions - $checks[]=array('extension' => 'pgsql', - 'msg' => array('feedback' => 'Postgres Database', 'ok' => $td_ok, 'ko' => 'cannot be used') ); - - $mysqlExt = 'mysql'; - if( version_compare(phpversion(), "5.5.0", ">=") ) { - $mysqlExt = 'mysqli'; - } - $checks[]=array('extension' => $mysqlExt, - 'msg' => array('feedback' => 'MySQL Database', 'ok' => $td_ok, 'ko' => 'cannot be used') ); - - // ---------------------------------------------------------------------------- - // special check for MSSQL - $isPHPGTE7 = version_compare(phpversion(), "7.0.0", ">="); - - $extid = 'mssql'; - if(PHP_OS == 'WINNT' || $isPHPGTE7 ) { - // Faced this problem when testing XAMPP 1.7.7 on Windows 7 with MSSQL 2008 Express - // From PHP MANUAL - reganding mssql_* functions - // These functions allow you to access MS SQL Server database. - // This extension is not available anymore on Windows with PHP 5.3 or later. - // SQLSRV, an alternative driver for MS SQL is available from Microsoft: - // http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx. - // - // Second Time: (2018) - // When using PHP 7 or up - // Help from Bitnami - // PHP 7 does not support mssql anymore. - // The PECL extension recommended is to use the "sqlsrv" module - // but you will need to compile it on your own. - // - // - // PHP_VERSION_ID is available as of PHP 5.2.7 - if ( defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50300 ) { - $extid = 'sqlsrv'; - } - - if ( $isPHPGTE7 ) { - $extid = 'sqlsrv'; - } - - } - $checks[] = array('extension' => $extid, - 'msg' => array('feedback' => 'MSSQL Database', 'ok' => $td_ok, 'ko' => 'cannot be used') ); - // --------------------------------------------------------------------------------------------------------- - - - $checks[]=array('extension' => 'gd', - 'msg' => array('feedback' => 'GD Graphic library', 'ok' => $td_ok, - 'ko' => " not enabled.
    Graph rendering requires it. This feature will be disabled." . - " It's recommended to install it.") ); - - $checks[]=array('extension' => 'ldap', - 'msg' => array('feedback' => 'LDAP library', 'ok' => $td_ok, - 'ko' => " not enabled. LDAP authentication cannot be used. " . - "(default internal authentication will works)")); - - $checks[]=array('extension' => 'json', - 'msg' => array('feedback' => 'JSON library', 'ok' => $td_ok, - 'ko' => " not enabled. You MUST install it to use EXT-JS tree component. ")); - - $checks[]=array('extension' => 'curl', - 'msg' => array('feedback' => 'cURL library', 'ok' => $td_ok, - 'ko' => " not enabled. You MUST install it to use REST Integration with issue trackers. ")); - - $out=''; - foreach($checks as $test) - { - $out .= sprintf($msg_support,$test['msg']['feedback']); - if( extension_loaded($test['extension']) ) - { - $msg=$test['msg']['ok']; - } - else - { - $msg=sprintf($td_failed,$test['msg']['feedback'],$test['msg']['ko']); - } - $out .= $msg; - } - - return $out; -} - - - -/** - * Check if web server support session data - * - * @param integer &$errCounter reference to error counter - * @return string html row with result - */ -function check_session(&$errCounter) { - $out = "Checking if sessions are properly configured"; - - if( !isset($_SESSION) ) - { - session_start(); - } - - if( $_SESSION['session_test'] != 1 ) - { - $color = 'success'; - $msg = 'OK'; - } - else - { - $color = 'error'; - $msg = 'Failed!'; - $errCounter++; - } - - $out .= "$msg\n"; - return ($out); -} //function end - - -/** - * check PHP defined timeout - * - * @param integer &$errCounter reference to error counter - * @return string html row with result - */ -function check_timeout(&$errCounter) -{ - $out = 'Maximum Session Idle Time before Timeout'; - - $timeout = ini_get("session.gc_maxlifetime"); - $gc_maxlifetime_min = floor($timeout/60); - $gc_maxlifetime_sec = $timeout % 60; - - if ($gc_maxlifetime_min > 30) { - $color = 'success'; - $res = 'OK'; - } else if ($gc_maxlifetime_min > 10){ - $color = 'warning'; - $res = 'Short. Consider to extend.'; - } else { - $color = 'error'; - $res = 'Too short. It must be extended!'; - $errCounter++; - } - $out .= "".$gc_maxlifetime_min . - " minutes and $gc_maxlifetime_sec seconds - ($res)\n"; - - return $out; -} - - -/** - * check Database type - * - * @param integer &$errCounter reference to error counter - * @param string $type valid PHP database type label - * - * @return string html row with result - */ -function checkDbType(&$errCounter, $type) -{ - $out = 'Database type'; - - switch ($type) - { - case 'mysql': - case 'mysqli': - case 'mssql': - case 'postgres': - $out .= ''.$type.''; - break; - - default: - $out .= 'Unsupported type: '.$type. - '. MySQL,Postgres and MSSQL are supported DB types. Of course' . - ' you can use also other ones without migration support.'; - break; - } - - return $out; -} - - -/** - * Display Operating System - * - * @return string html table row - */ -function checkServerOs() -{ - $final_msg = 'Server Operating System (no constrains)'; - $final_msg .= ''.PHP_OS.''; - - return $final_msg; -} - - -/** - * check minimal required PHP version - * - * @param integer &$errCounter pointer to error counter - * @return string html row with result - */ -function checkPhpVersion(&$errCounter) -{ - $min_version = '5.5.0'; - $my_version = phpversion(); - - // version_compare: - // -1 if left is less, 0 if equal, +1 if left is higher - $php_ver_comp = version_compare($my_version, $min_version); - - $final_msg = 'PHP version'; - - if($php_ver_comp < 0) - { - $final_msg .= "Failed! - You are running on PHP " . $my_version . - ", and TestLink requires PHP " . $min_version . ' or greater. ' . - 'This is fatal problem. You must upgrade it.'; - $errCounter += 1; - } - else - { - $final_msg .= "OK ( {$min_version} [minimum version] "; - $final_msg .= ($php_ver_comp == 0 ? " = " : " <= "); - $final_msg .= $my_version . " [your version] " ; - $final_msg .= " ) "; - } - - return $final_msg; -} - - -/** - * verify that files are writable/readable - * OK result is for state: - * a) installation - writable - * b) installed - readable - * - * @param integer &$errCounter pointer to error counter - * @return string html row with result - * @author Martin Havlat - */ -function check_file_permissions(&$errCounter, $inst_type, $checked_filename, $isCritical=FALSE) -{ - $checked_path = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..'); - $checked_file = $checked_path.DIRECTORY_SEPARATOR.$checked_filename; - $out = 'Access to file ('.$checked_file.')'; - - if ($inst_type == 'new') - { - if(file_exists($checked_file)) - { - if (is_writable($checked_file)) - { - $out .= "OK (writable)\n"; - } - else - { - if ($isCritical) - { - $out .= "Failed! Please fix the file " . - $checked_file . " permissions and reload the page."; - $errCounter += 1; - } - else - { - $out .= "Not writable! Please fix the file " . - $checked_file . " permissions."; - } - } - } - else - { - if (is_writable($checked_path)) - { - $out .= "OK\n"; - } - else - { - if ($isCritical) - { - $out .= "Directory is not writable! Please fix " . - $checked_path . " permissions and reload the page."; - $errCounter += 1; - } - else - { - $out .= "Directory is not writable! Please fix " . - $checked_path . " permissions."; - } - } - } - } - else - { - if(file_exists($checked_file)) - { - if (!is_writable($checked_file)) - { - $out .= "OK (read only)\n"; - } - else - { - $out .= "It's recommended to have read only permission for security reason."; - } - } - else - { - if ($isCritical) - { - $out .= "Failed! The file is not on place."; - $errCounter += 1; - } - else - { - $out .= "The file is not on place."; - } - } - } - - return($out); -} - - -/** - * Check read/write permissions for directories - * based on check_with_feedback($dirs_to_check); - * - * @param integer &$errCounter pointer to error counter - * @return string html row with result - * @author Martin Havlat - */ -function check_dir_permissions(&$errCounter) -{ - $dirs_to_check = array('gui' . DIRECTORY_SEPARATOR . 'templates_c' => null, - 'logs' => 'log_path','upload_area' => 'repositoryPath'); - - $final_msg = ''; - $msg_ko = "Failed!"; - $msg_ok = "OK"; - $checked_path_base = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..'); - - $final_msg .= "For security reasons we suggest that directories tagged with [S]" . - " on following messages, will be made UNREACHEABLE from browser.
    " . - "Give a look to README file, section 'Installation & SECURITY' " . - " to understand how to change the defaults." . - ""; - - foreach ($dirs_to_check as $the_d => $how) - { - if( is_null($how) ) - { - // Correct relative path for installer. - $needsLock = ''; - $the_d = $checked_path_base . DIRECTORY_SEPARATOR . $the_d; - } - else - { - $needsLock = '[S] '; - $the_d = config_get($how); - } - - $final_msg .= "Checking if {$the_d} directory exists {$needsLock}"; - - if(!file_exists($the_d)) - { - $errCounter += 1; - $final_msg .= $msg_ko; - } - else - { - $final_msg .= $msg_ok; - $final_msg .= "Checking if {$the_d} directory is writable (by user used to run webserver process) "; - if(!is_writable($the_d)) - { - $errCounter += 1; - $final_msg .= $msg_ko; - } - else - { - $final_msg .= $msg_ok; - } - } - } - - return($final_msg); -} - - -/** - * Print table with checking www browser support - * - * @param integer &$errCounter pointer to error counter - * @author Martin Havlat - **/ -function reportCheckingBrowser(&$errCounter) -{ - $browser = strtolower($_SERVER['HTTP_USER_AGENT']); - - echo "\n".'

    Browser compliance

    '."\n"; - - echo '

    '.$browser.'

    '; - echo ''; - - if (strpos($browser, 'firefox') === false || strpos($browser, 'msie') === false) - { - echo ""; - } - else - { - echo ""; - } - - echo ''; - - echo '
    Browser supportedOK
    Unsupported: {$_SERVER['HTTP_USER_AGENT']}
    Javascript availability' . - ''. - '' . - '
    '; -} - - -/** - * print table with system checking results - * - * @param integer &$errCounter reference to error counter - * @author Martin Havlat - **/ -function reportCheckingSystem(&$errCounter) -{ - echo '

    System requirements

    '; - echo checkServerOs(); - echo checkPhpVersion($errCounter); - echo '
    '; -} - - -/** - * print table with database checking - * - * @param integer &$errCounter reference to error counter - * @author Martin Havlat - **/ -function reportCheckingDatabase(&$errCounter, $type = null) -{ - if (checkInstallStatus()) - { - $type = DB_TYPE; - } - - if (!is_null($type)) - { - echo '

    Database checking

    '; - echo checkDbType($errCounter, $type); - echo "
    \n"; - } - -} - - -/** - * print table with system checking results - * - * @param integer &$errCounter reference to error counter - * @author Martin Havlat - **/ -function reportCheckingWeb(&$errCounter) { - echo '

    Web and PHP configuration

    '; - echo check_timeout($errCounter); - echo check_php_settings($errCounter); - echo checkPhpExtensions($errCounter); - echo '
    '; -} - - -/** - * print table with system checking results - * - * @param integer &$errCounter pointer to error counter - * @param string installationType: useful when this function is used on installer - * - * @author Martin Havlat - **/ -function reportCheckingPermissions(&$errCounter,$installationType='none') -{ - echo '

    Read/write permissions

    '; - echo check_dir_permissions($errCounter); - - // for $installationType='upgrade' existence of config_db.inc.php is not needed - $blockingCheck=$installationType=='upgrade' ? FALSE : TRUE; - if($installationType=='new') - { - echo check_file_permissions($errCounter,$installationType,'config_db.inc.php', $blockingCheck); - } - echo '
    '; -} \ No newline at end of file diff --git a/lib/functions/csrf.php b/lib/functions/csrf.php index 02e86ec180..b474a156c5 100644 --- a/lib/functions/csrf.php +++ b/lib/functions/csrf.php @@ -1,212 +1,197 @@ - - * In case you would like to skip this, you can add a nocsrf field. - * - * @param unknown_type $form_data_html HTML content - * @return html content with modified forms that include CSRF hidden tokens - */ -function csrfguard_replace_forms($form_data_html) -{ - $count=preg_match_all("/(.*?)<\\/form>/is",$form_data_html,$matches,PREG_SET_ORDER); - if (is_array($matches)) - { - foreach ($matches as $m) - { - if (strpos($m[1],"nocsrf")!==false) - { - continue; - } - $name="CSRFGuard_".mt_rand(0,mt_getrandmax()); - $token= csrfguard_generate_token($name); - - // because you can have multiple forms in a HTML page - // is not possible to add a fixed ID. - // - $form_data_html=str_replace($m[0], - " + + * In case you would like to skip this, you can add a nocsrf field. + * + * @param string $form_data_html + * HTML content + * @return string html content with modified forms that include CSRF hidden tokens + */ +function csrfguard_replace_forms($form_data_html) +{ + preg_match_all("/(.*?)<\\/form>/is", $form_data_html, $matches, + PREG_SET_ORDER); + if (is_array($matches)) { + foreach ($matches as $m) { + if (strpos($m[1], "nocsrf") !== false) { + continue; + } + $name = "CSRFGuard_" . mt_rand(0, mt_getrandmax()); + $token = csrfguard_generate_token($name); + + // because you can have multiple forms in a HTML page + // is not possible to add a fixed ID. + // + $form_data_html = str_replace($m[0], + " - {$m[2]}",$form_data_html); - } - } - return $form_data_html; -} - -/** - * Applies CSRF filter on Smarty template content. Can be - * used as a output filter. - * - * @param string $source - * @param Smarty $smarty - * @return CSRF filtered content - */ -function smarty_csrf_filter($source, $smarty) { - return csrfguard_replace_forms($source); -} - -/** - * Validates the CSRF tokens found in $_POST variable. Raoses user - * errors if the token is not found or invalid. - * - * @return true if validated correctly, otherwise false - */ -function csrfguard_start() -{ - if (count($_POST)) - { - if (!isset($_POST['CSRFName'])) - { - //trigger_error("No CSRFName found, probable invalid request.",E_USER_ERROR); - //return false; - redirect($_SESSION['basehref'] . 'error.php?code=1'); - exit(); - } - - $name = trim($_POST['CSRFName']); - $token = trim($_POST['CSRFToken']); - $good = (strlen($name) > 0 && strlen($token) > 0); - - if (!$good || !csrfguard_validate_token($name, $token)) - { - //trigger_error("Invalid CSRF token.",E_USER_ERROR); - //return false; - redirect($_SESSION['basehref'] . 'error.php?code=2'); - exit(); - } - } -} - -// this way is runned always -// Need to understand if this is needed -// + {$m[2]}", + $form_data_html); + } + } + return $form_data_html; +} + +/** + * Applies CSRF filter on Smarty template content. + * Can be + * used as a output filter. + * + * @param string $source + * @param Smarty $smarty + * @return string CSRF filtered content + */ +function smarty_csrf_filter($source, $smarty) +{ + return csrfguard_replace_forms($source); +} + +/** + * Validates the CSRF tokens found in $_POST variable. + * Raoses user + * errors if the token is not found or invalid. + * + * @return true if validated correctly, otherwise false + */ +function csrfguard_start() +{ + if (count($_POST)) { + if (! isset($_POST['CSRFName'])) { + redirect($_SESSION['basehref'] . 'error.php?code=1'); + exit(); + } + + $name = trim($_POST['CSRFName']); + $token = trim($_POST['CSRFToken']); + $good = (strlen($name) > 0 && strlen($token) > 0); + + if (! $good || ! csrfguard_validate_token($name, $token)) { + redirect($_SESSION['basehref'] . 'error.php?code=2'); + exit(); + } + } +} + +// this way is runned always +// Need to understand if this is needed doSessionStart(false); -// csrfguard_start(); \ No newline at end of file + diff --git a/lib/functions/csv.inc.php b/lib/functions/csv.inc.php index 6f30ea0a98..85ad2e3e32 100644 --- a/lib/functions/csv.inc.php +++ b/lib/functions/csv.inc.php @@ -1,165 +1,177 @@ - ';', + 'fieldQty' => 0, + 'processHeader' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $handle = fopen($fileName, "r"); + $check_syntax = $my['options']['fieldQty'] > 0; + $do_import = 1; + + // parsedCounter: count lines on file that has been parsed => exclude comment lines + // syntaxError: array: index -> line number on file that has been skipped due to syntax + // check problems. + // info: map with lines that can be processed by caller + // + $retVal = array( + 'userFeedback' => array( + 'parsedCounter' => 0, + 'syntaxError' => array() + ), + 'info' => null + ); + + if ($handle) { + $lineNumber = 0; + $idx = 0; + $isHeaderLine = true; + $keyMappings = $fieldMappings; + + $debugMsg = 'DEBUG::' . basename(__FILE__); + + if ($debugMe) { + echo $debugMsg . ' OPTIONS: processHeader=' . + (($my['options']['processHeader']) ? 'true' : 'false') . '
    '; + } + + while ($data = fgetcsv($handle, TL_IMPORT_ROW_MAX, + $my['options']['delimiter'])) { + $lineNumber ++; + + // ignore line that start with comment char, leading blanks are ignored + $firstChunk = trim($data[0]); + $positionCheck = strpos($firstChunk, '#'); + $processLine = ($positionCheck === false || $positionCheck != 0); + + if ($debugMe) { + echo $debugMsg . ':: Line: ' . $lineNumber . '=>' . + (($processLine) ? 'OK to process' : 'Skipped') . '
    '; + } + + if ($processLine) { + $retVal['userFeedback']['parsedCounter'] ++; + + if ($isHeaderLine && $my['options']['processHeader']) { + // Get format information from first line, and rebuild with this + // information the keyMappings, using fieldMappings + $isHeaderLine = false; + $keyMappings = null; + foreach ($fieldMapping as $k => $targetKey) { + if (is_int($k)) { + $needle = $targetKey; + $dest = $needle; + } else { + $needle = $k; + $dest = $targetKey; + } + $t = array_search($needle, $data); + $keyMappings[$t] = $dest; + } + } else { + if ($check_syntax) { + $fieldsQty = count($data); + if (! ($do_import = ($fieldsQty == + $my['options']['fieldQty']))) { + $msg = 'Field count:' . $fieldsQty . + ' Required Field count: ' . + $my['options']['fieldQty']; + + $retVal['userFeedback']['syntaxError'][$lineNumber] = $msg; + + if ($debugMe) { + echo $debugMsg . 'Syntax Check Failure - Line ' . + $lineNumber . $msg . ' - SKIPPED ' . '
    '; + } + } + } + + if ($do_import) { + foreach ($keyMappings as $fieldPos => $fieldKey) { + $retVal['info'][$idx][$fieldKey] = $data[$fieldPos]; + } + $idx ++; + } + } + } + } + } + return $retVal; } - -// added [$num_fields] number of fields a line must have to be valid -// if the number is not verified the line is discarded. -// -/** @uses requirements.inc.php */ -function importCSVData($fileName,$fieldMappings, $options = null) { - $debugMe = false; //true; - $my['options'] = array( 'delimiter' => ';', 'fieldQty' => 0, 'processHeader' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - $handle = fopen ($fileName,"r"); - $check_syntax = $my['options']['fieldQty'] > 0; - $do_import = 1; - - // parsedCounter: count lines on file that has been parsed => exclude comment lines - // syntaxError: array: index -> line number on file that has been skipped due to syntax - // check problems. - // info: map with lines that can be processed by caller - // - $retVal = array('userFeedback' => array('parsedCounter' => 0, 'syntaxError' => array()), - 'info' => null); - - if ($handle) - { - $lineNumber = 0; - $idx = 0; - $isHeaderLine = true; - $keyMappings = $fieldMappings; - - $debugMsg = 'DEBUG::' . basename(__FILE__); - - if( $debugMe ) - { - echo $debugMsg . ' OPTIONS: processHeader=' . - (($my['options']['processHeader']) ? 'true' : 'false' ) . '
    '; - } - - while( $data = fgetcsv($handle, TL_IMPORT_ROW_MAX, $my['options']['delimiter']) ) - { - $lineNumber++; - - // ignore line that start with comment char, leading blanks are ignored - $firstChunk = trim($data[0]); - $positionCheck = strpos($firstChunk,'#'); - $processLine = ($positionCheck === false || $positionCheck != 0); - - if( $debugMe ) - { - echo $debugMsg . ':: Line: ' . $lineNumber . '=>' . - (($processLine) ? 'OK to process' : 'Skipped') .'
    '; - } - - if( $processLine ) - { - $retVal['userFeedback']['parsedCounter']++; - - if( $isHeaderLine && $my['options']['processHeader'] ) - { - // Get format information from first line, and rebuild with this - // information the keyMappings, using fieldMappings - // - $isHeaderLine = false; - $keyMappings = null; - foreach($fieldMapping as $k => $targetKey) - { - if (is_int($k)) - { - $needle = $targetKey; - $dest = $needle; - } - else - { - $needle = $k; - $dest = $targetKey; - } - $t = array_search($needle, $data); - $keyMappings[$t] = $dest; - } - } - else - { - if( $check_syntax) - { - $fieldsQty = count($data); - if( !($do_import = ($fieldsQty == $my['options']['fieldQty']))) - { - $msg = 'Field count:' . $fieldsQty . ' Required Field count: ' . - $my['options']['fieldQty']; - - $retVal['userFeedback']['syntaxError'][$lineNumber] = $msg; - - if($debugMe) - { - echo $debugMsg . 'Syntax Check Failure - Line ' . $lineNumber . - $msg . ' - SKIPPED ' . '
    '; - } - } - } - - if( $do_import ) - { - foreach($keyMappings as $fieldPos => $fieldKey) - { - $retVal['info'][$idx][$fieldKey] = $data[$fieldPos]; - } - $idx++; - } - } - } - } // end while - } - return $retVal; -} - - diff --git a/lib/functions/database.class.php b/lib/functions/database.class.php index 3563cc7155..f9ff4ef89a 100644 --- a/lib/functions/database.class.php +++ b/lib/functions/database.class.php @@ -1,1058 +1,1066 @@ -logEnabled=$value ? 1 : 0; - } - - function getLogEnabled($value) - { - return $this->logEnabled; - } - - function setLogQueries($value) - { - $this->logQueries = $value ? 1 : 0; - } - - function getLogQueries($value) - { - return $this->logQueries; - } - - // TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher - function __construct($db_type) - { - $fetch_mode = ADODB_FETCH_ASSOC; - - $this->dbType = $db_type; - if( $this->dbType == 'mysql' && version_compare(phpversion(), "5.5.0", ">=") ) - { - $this->dbType = 'mysqli'; - } - $adodb_driver = $this->dbType; - - // added to reduce memory usage (before this setting we used ADODB_FETCH_BOTH) - if($this->dbType == 'mssql') - { - $fetch_mode = ADODB_FETCH_BOTH; - if(PHP_OS == 'WINNT') - { - // Faced this problem when testing XAMPP 1.7.7 on Windows 7 with MSSQL 2008 Express - // From PHP MANUAL - reganding mssql_* functions - // These functions allow you to access MS SQL Server database. - // This extension is not available anymore on Windows with PHP 5.3 or later. - // SQLSRV, an alternative driver for MS SQL is available from Microsoft: - // http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx. - // - // PHP_VERSION_ID is available as of PHP 5.2.7 - if ( defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50300) - { - $adodb_driver = 'mssqlnative'; - } - } - } - $this->db = NewADOConnection($adodb_driver); - $this->db->SetFetchMode($fetch_mode); - } - - - // access to the ADODB object - function get_dbmgr_object() - { - return($this->db); - } - - - - /** Make a connection to the database */ - # changed Connect() to NConnect() see ADODB Manuals - function connect( $p_dsn, $p_hostname = null, $p_username = null, - $p_password = null, $p_database_name = null ) - { - $result = array('status' => 1, 'dbms_msg' => 'ok'); - - if( $p_dsn === false ) { - $t_result = $this->db->NConnect($p_hostname, $p_username, $p_password, $p_database_name ); - } else { - $t_result = $this->db->IsConnected(); - } - - if ( $t_result ) { - $this->is_connected = true; - } else { - $result['status'] = 0; - $result['dbms_msg']=$this->error(); - } - return ($result); - } - - - /** - * execute SQL query, - * requires connection to be opened - * - * @param string $p_query SQL request - * @param integer $p_limit (optional) number of rows - * @param integer $p_offset (optional) begining row number - * - * @return boolean result of request - **/ - function exec_query( $p_query, $p_limit = -1, $p_offset = -1 ) - { - $ec = 0; - $emsg = null; - $logLevel = 'DEBUG'; - $message = ''; - - if($this->logQueries) - { - $this->nQuery++; - $t_start = $this->microtime_float(); - } - - if ( ( $p_limit != -1 ) || ( $p_offset != -1 ) ) { - $t_result = $this->db->SelectLimit( $p_query, $p_limit, $p_offset ); - } else { - $t_result = $this->db->Execute( $p_query ); - } - - if($this->logQueries) - { - $t_elapsed = number_format( $this->microtime_float() - $t_start, 4); - $this->overallDuration += $t_elapsed; - $message = "SQL [". $this->nQuery . "] executed [took {$t_elapsed} secs]" . - "[all took {$this->overallDuration} secs]:\n\t\t"; - } - $message .= $p_query; - - if (!$t_result) - { - $ec = $this->error_num(); - $emsg = $this->error_msg(); - $message .= "\nQuery failed: errorcode[" . $ec . "]". "\n\terrormsg:".$emsg; - $logLevel = 'ERROR'; - - - tLog("ERROR ON exec_query() - database.class.php
    " . $this->error(htmlspecialchars($p_query)) . - "
    THE MESSAGE : $message ", 'ERROR', "DATABASE"); - echo "
     ============================================================================== 
    "; - echo "
     DB Access Error - debug_print_backtrace() OUTPUT START 
    "; - echo "
     ATTENTION: Enabling more debug info will produce path disclosure weakness (CWE-200) 
    "; - echo "
                Having this additional Information could be useful for reporting 
    "; - echo "
                issue to development TEAM. 
    "; - echo "
     ============================================================================== 
    "; - - if(defined('DBUG_ON') && DBUG_ON == 1) - { - echo "
    "; debug_print_backtrace(); echo "
    "; - die(); - } - echo "
    "; debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); echo "
    "; - die(); - - //else - //{ - // echo "
    "; debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); echo "
    "; - //} - echo "
     ============================================================================== 
    "; - $t_result = false; - } - - if($this->logEnabled) - { - tLog($message,$logLevel,"DATABASE"); - } - - if($this->logQueries) - { - array_push ($this->queries_array, array( $p_query, $t_elapsed, $ec, $emsg ) ); - } - - return $t_result; - - } - - - // TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher - function fetch_array( &$p_result ) - { - if ( $p_result->EOF ) { - return false; - } - - // mysql obeys FETCH_MODE_BOTH, hence ->fields works, other drivers do not support this - switch ($this->db->databaseType) - { - case "mysql": - case "oci8po": - case "mssql": - case "mssqlnative": - $t_array = $p_result->fields; - break; - - default: - $t_array = $p_result->GetRowAssoc(false); - break; - } - - $p_result->MoveNext(); - return $t_array; - } - - - // 20080315 - franciscom - Got new code from Mantis, that manages FETCH_MODE_ASSOC - function db_result( $p_result, $p_index1=0, $p_index2=0 ) { - if ( $p_result && ( $this->num_rows( $p_result ) > 0 ) ) - { - $p_result->Move( $p_index1 ); - $t_result = $p_result->GetArray(); - - if ( isset( $t_result[0][$p_index2] ) ) { - return $t_result[0][$p_index2]; - } - - // The numeric index doesn't exist. FETCH_MODE_ASSOC may have been used. - // Get 2nd dimension and make it numerically indexed - $t_result = array_values( $t_result[0] ); - return $t_result[$p_index2]; - } - return false; - } - - - /** @return integer the last inserted id */ - function insert_id($p_table = null) - { - if ( isset($p_table) && ($this->db_is_pgsql() || $this->db_is_oracle())) - { - if ( $this->db_is_pgsql() ) - { - $sql = "SELECT currval('".$p_table."_id_seq')"; - } - elseif ($this->db_is_oracle()) - { - $sql = "SELECT ".$p_table."_id_seq.currval from dual"; - } - $result = $this->exec_query( $sql ); - return $this->db_result($result); - } - return $this->db->Insert_ID( ); - } - - - /** Check is the database is PostgreSQL */ - function db_is_pgsql() - { - $status_ok = false; - switch( $this->dbType ) - { - case 'postgres': - case 'postgres7': - case 'pgsql': - $status_ok = true; - break; - } - return $status_ok; - } - - - /** - * Check is the database is ORACLE - * @return boolean TRUE = Oracle type - **/ - function db_is_oracle() - { - $status_ok = false; - switch( $this->dbType ) - { - case 'oci8': - case 'oci8po': - $status_ok = true; - break; - } - return $status_ok; - } - - - function db_table_exists( $p_table_name ) { - return in_array ( $p_table_name , $this->db->MetaTables( "TABLE" ) ) ; - } - - - function db_field_exists( $p_field_name, $p_table_name ) { - return in_array ( $p_field_name , $this->db->MetaColumnNames( $p_table_name ) ) ; - } - - - /** - * Check if there is an index defined on the specified table/field and with - * the specified type. - * Warning: only works with MySQL - * - * @param string $p_table Name of table to check - * @param string $p_field Name of field to check - * @param string $p_key key type to check for (eg: PRI, MUL, ...etc) - * - * @return boolean - */ - function key_exists_on_field( $p_table, $p_field, $p_key ) { - $c_table = $this->db->prepare_string( $p_table ); - $c_field = $this->db->prepare_string( $p_field ); - $c_key = $this->db->prepare_string( $p_key ); - - $sql = "DESCRIBE $c_table"; - $result = $this->exec_query( $sql ); - - $count = $this->num_rows( $result ); - for ( $i=0 ; $i < $count ; $i++ ) { - $row = $this->db->fetch_array( $result ); - - if ( $row['Field'] == $c_field ) { - return ( $row['Key'] == $c_key ); - } - } - return false; - } - - - # prepare a string before DB insertion - # 20051226 - fm - function prepare_string( $p_string ) - { - if (is_null($p_string)) - return ''; - - $t_escaped = $this->db->qstr( $p_string, false ); - // from second char(1) to one before last(-1) - return(substr($t_escaped,1,-1)); - } - - - # prepare an integer before DB insertion - function prepare_int( $p_int ) { - return (int)$p_int; - } - - - # prepare a boolean before DB insertion - function prepare_bool( $p_bool ) { - return (int)(bool)$p_bool; - } - - # return current timestamp for DB - function db_now() - { - switch($this->db->databaseType) - { - /* @todo: maybe we should use this? - case 'odbc_mssql': - return "GETDATE()"; - */ - default: - return $this->db->DBTimeStamp(time()); - } - } - - - # generate a unixtimestamp of a date - # > SELECT UNIX_TIMESTAMP(); - # -> 882226357 - # > SELECT UNIX_TIMESTAMP('1997-10-04 22:23:00'); - # -> 875996580 - function db_timestamp( $p_date=null ) { - - if ( null !== $p_date ) { - $p_timestamp = $this->db->UnixTimeStamp($p_date); - } else { - $p_timestamp = time(); - } - return $this->db->DBTimeStamp($p_timestamp) ; - } - - - function db_unixtimestamp( $p_date=null ) { - - if ( null !== $p_date ) { - $p_timestamp = $this->db->UnixTimeStamp($p_date); - } else { - $p_timestamp = time(); - } - return $p_timestamp ; - } - - - /** @return integer count queries */ - function count_queries () { - return count( $this->queries_array ); - } - - - /** @return integer count unique queries */ - function count_unique_queries () { - - $t_unique_queries = 0; - $t_shown_queries = array(); - foreach ($this->queries_array as $t_val_array) { - if ( ! in_array( $t_val_array[0], $t_shown_queries ) ) { - $t_unique_queries++; - array_push( $t_shown_queries, $t_val_array[0] ); - } - } - return $t_unique_queries; - } - - - /** get total time for queries */ - function time_queries () { - $t_count = count( $this->queries_array ); - $t_total = 0; - for ( $i = 0; $i < $t_count; $i++ ) { - $t_total += $this->queries_array[$i][1]; - } - return $t_total; - } - - - /** - * close the connection. - * Not really necessary most of the time since a connection is - * automatically closed when a page finishes loading. - */ - function close() { - $t_result = $this->db->Close(); - } - - - function error_num() { - return $this->db->ErrorNo(); - } - - - function error_msg() { - return $this->db->ErrorMsg(); - } - - - /** - * returns a message string with: error num, error msg and query. - * - * @return string the message - */ - function error( $p_query=null ) { - $msg= $this->error_num() . " - " . $this->error_msg(); - - if ( null !== $p_query ) - { - $msg .= " - " . $p_query ; - } - return $msg; - } - - - function num_rows( $p_result ) { - return $p_result->RecordCount( ); - } - - - function affected_rows() { - return $this->db->Affected_Rows( ); - } - - - /** - * Fetches the first column first row - * - * @param string $sql the query to be executed - * @param string $column the name of the column which shall be returned - * - * @return mixed the value of the column - **/ - function fetchFirstRowSingleColumn($sql,$column) - { - $value = null; - $row = $this->fetchFirstRow($sql); - - // BUGID 1318 - if ($row && array_key_exists($column, $row)) - { - $value = $row[$column]; - } - return $value; - } - - /** - * Fetches the first row (in a assoc-array) - * - * @param string $sql the query to be executed - * @return array the first row - **/ - function fetchFirstRow($sql) - { - $result = $this->exec_query($sql); - $row = null; - if ($result) - { - $row = $this->fetch_array($result); - } - unset($result); - return $row; - } - - - /** - * Get one value (no array) - * for example: SELECT COUNT(*) FROM table - * - * @param string $sql the query to be executed - * @return string of one value || null - **/ - public function fetchOneValue($sql) - { - $row = $this->fetchFirstRow($sql); - if ($row) - { - $fieldName = array_keys($row); - return $row[$fieldName[0]]; - } - return null; - } - - - /** - * Fetches all values for a given column of all returned rows - * - * @param string $sql the query to be executed - * @param string $column the name of the column - * @param integer $limit (optional) number of rows - * - * @return array an enumerated array, which contains all the values - **/ - function fetchColumnsIntoArray($sql,$column,$limit = -1) - { - $items = null; - $result = $this->exec_query($sql,$limit); - if ($result) - { - while($row = $this->fetch_array($result)) - { - $items[] = $row[$column]; - } - } - - unset($result); - return $items; - } - - - /** - * Fetches all rows into a map whose keys are the values of columns - * - * @param string $sql the query to be executed - * @param string $column the name of the column - * @param booleam $cumulative default 0 - * useful in situations with results set with multiple - * rows with same value on key column like this: - * - * col1 col2 col3 ... - * X A C - * X B Z - * Y B 0 - * - * cumulative=0 -> - * return items= array('X' => array('A','C'), 'Y' => array('B','0') ) - * - * cumulative=1 -> - * return items= - * array('X' => - * array( 0 => array('A','C'), - * 1 => array('B','Z')), - * 'Y' => array( 0 => array('B','0') ) - * - * @param integer $limit (optional) number of rows - * - * @return array an assoc array whose keys are the values from the columns - * of the rows - **/ - function fetchRowsIntoMap($sql,$column,$cumulative = 0,$limit = -1,$col2implode='') - { - $items = null; - $result = $this->exec_query($sql,$limit); - if ($result) - { - // ----------------------------------------------- - // Error management Code - $errorMsg=__CLASS__ . '/' . __FUNCTION__ . ' - '; - if( ($empty_column = (trim($column)=='') ) ) - { - $errorMsg .= 'empty column - SQL:' . $sql; - trigger_error($errorMsg,E_USER_NOTICE); - return null; - } - - while($row = $this->fetch_array($result)) - { - // ----------------------------------------------- - // Error management Code - if( !isset($row[$column]) ) - { - $errorMsg .= 'missing column:' . $column; - $errorMsg .= ' - SQL:' . $sql; - trigger_error($errorMsg,E_USER_NOTICE); - return null; - } - // ----------------------------------------------- - - if ($cumulative) - { - $items[$row[$column]][] = $row; - } - else if($col2implode != '') - { - if(isset($items[$row[$column]])) - { - $items[$row[$column]][$col2implode] .= ',' . $row[$col2implode]; - } - else - { - $items[$row[$column]] = $row; - } - } - else - { - $items[$row[$column]] = $row; - } - } - } - - unset($result); - unset($row); - return $items; - } - - - /** - * Fetches the values of two columns from all rows into a map - * - * @param string $sql the query to be executed - * @param string $column1 the name of the column (keys for the map) - * @param string $column2 the name of the second column (values of the map) - * @param boolean $cumulative - * useful in situations with results set like - * col1 col2 - * X A - * X B - * Y B - * - * cumulative=0 -> return items= array('X' => 'B', 'Y' => 'B') - * - * cumulative=1 -> return items= array('X' => array('A','B'), 'Y' => array('B') ) - * - * @param integer $limit (optional) number of rows - * - * @return assoc array whose keys are the values of column1 and the values are: - * - * cumulative=0 => the values of column2 - * cumulative=1 => array with the values of column2 - * - **/ - function fetchColumnsIntoMap($sql,$column1,$column2,$cumulative=0,$limit = -1) - { - $result = $this->exec_query($sql,$limit); - $items = null; - if ($result) - { - while ($myrow = $this->fetch_array($result)) - { - if($cumulative) - { - $items[$myrow[$column1]][] = $myrow[$column2]; - } - else - { - $items[$myrow[$column1]] = $myrow[$column2]; - } - } - } - - unset($result); - return $items; - } - - - /** - * database server information - * wrapper for adodb method ServerInfo - * - * @return assoc array members 'version' and 'description' - **/ - function get_version_info() - { - $version = $this->db->ServerInfo(); - return $version; - } - - - /** - **/ - function get_recordset($sql,$fetch_mode = null,$limit = -1, $start = -1) - { - $output = null; - - $result = $this->exec_query($sql,$limit,$start); - if ($result) - { - while($row = $this->fetch_array($result)) - { - $output[] = $row; - } - } - - unset($result); - return $output; - } - - - /** - * Fetches all rows into a map whose keys are the values of columns - * - * @param string $sql the query to be executed - * @param string $column the name of the column - * @param integer $limit (optional) number of rows - * - * @return array an assoc array whose keys are the values from the columns - * of the rows - **/ - function fetchArrayRowsIntoMap($sql,$column,$limit = -1) - { - $items = null; - $result = $this->exec_query($sql,$limit); - if ($result) - { - while($row = $this->fetch_array($result)) - { - $items[$row[$column]][] = $row; - } - } - - unset($result); - return $items; - } - - - /** - * Fetches all rows into a map whose keys are the values of columns - * - * @param string $sql the query to be executed - * @param string $column_main_key the name of the column - * @param string $column_sec_key the name of the column - * @param boolean $cumulative - * @param integer $limit (optional) number of rows - * - * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] - * - **/ - function fetchMapRowsIntoMap($sql,$main_key,$sec_key, - $cumulative = 0,$limit = -1, $col2implode ='') - { - $items = null; - $result = $this->exec_query($sql,$limit); - if ($result) { - while($row = $this->fetch_array($result)) { - if($cumulative) { - $items[$row[$main_key]][$row[$sec_key]][] = $row; - } else if($col2implode !='') { - if(isset($items[$row[$main_key]][$row[$sec_key]])) { - $items[$row[$main_key]][$row[$sec_key]][$col2implode] .= - ',' . $row[$col2implode]; - } else { - $items[$row[$main_key]][$row[$sec_key]] = $row; - } - } - else { - $items[$row[$main_key]][$row[$sec_key]] = $row; - } - } - } - - unset($result); - return $items; - } - - /** - * TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher - **/ - function build_sql_create_db($db_name) - { - $sql=''; - - switch($this->db->databaseType) - { - case 'postgres7': - case 'postgres8': - $sql = 'CREATE DATABASE "' . $this->prepare_string($db_name) . '" ' . "WITH ENCODING='UNICODE' "; - break; - - case 'mssql': - case 'mssqlnative': - $sql = 'CREATE DATABASE [' . $this->prepare_string($db_name) . '] '; - break; - - case 'mysql': - default: - $sql = "CREATE DATABASE `" . $this->prepare_string($db_name) . "` CHARACTER SET utf8 "; - break; - } - return ($sql); - } - - - function db_null_timestamp() - { - $db_type = $this->db->databaseType; - $nullValue = NULL; - - switch($db_type) - { - case 'mysql': - // is not an error i put single quote on value - $nullValue = " '0000-00-00 00:00:00' "; - break; - } - return $nullValue; - } - - /** - * Fetches all rows into a map of 2 levels - * - * @param string $sql the query to be executed - * @param array $keyCols, columns to used as access key - * @param boolean $cumulative - * @param integer $limit (optional) number of rows - * - * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] - * - **/ - function fetchRowsIntoMap2l($sql,$keyCols,$cumulative = 0,$limit = -1) { - $items = null; - $result = $this->exec_query($sql,$limit); - - // new dBug($result); - if ($result) { - while($row = $this->fetch_array($result)) { - if($cumulative) { - $items[$row[$keyCols[0]]][$row[$keyCols[1]]][] = $row; - } else { - $items[$row[$keyCols[0]]][$row[$keyCols[1]]] = $row; - } - } - } - - unset($result); - return $items; - } - - /** - * Fetches all rows into a map of 3 levels - * - * @param string $sql the query to be executed - * @param array $keyCols, columns to used as access key - * @param boolean $cumulative - * @param integer $limit (optional) number of rows - * - * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] - * - **/ - function fetchRowsIntoMap3l($sql,$keyCols,$cumulative = 0,$limit = -1) { - $items = null; - $result = $this->exec_query($sql,$limit); - - // new dBug($result); - if ($result) { - while($row = $this->fetch_array($result)) { - if($cumulative) { - $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][] = $row; - } else { - $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]] = $row; - } - } - } - - unset($result); - return $items; - } - - - /** - * Fetches all rows into a map of 4 levels - * - * @param string $sql the query to be executed - * @param array $keyCols, columns to used as access key - * @param boolean $cumulative - * @param integer $limit (optional) number of rows - * - * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] - * - **/ - function fetchRowsIntoMap4l($sql,$keyCols,$cumulative = 0,$limit = -1) - { - $items = null; - $result = $this->exec_query($sql,$limit); - - // displayMemUsage(__FUNCTION__); - - // new dBug($result); - if ($result) - { - while($row = $this->fetch_array($result)) - { - if($cumulative) - { - $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][$row[$keyCols[3]]][] = $row; - } - else - { - $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][$row[$keyCols[3]]] = $row; - } - } - } - // displayMemUsage(__FUNCTION__); - unset($result); - // displayMemUsage(__FUNCTION__); - return $items; - } - - - - - /** - * Fetches all rows into a map whose keys are the values of columns - * - * @param string $sql the query to be executed - * @param string $column the name of the column - * - * @return array an assoc array - **/ - function fetchRowsIntoMapAddRC($sql,$column,$limit = -1) - { - $items = null; - $result = $this->exec_query($sql,$limit); - if ($result) - { - $errorMsg=__CLASS__ . '/' . __FUNCTION__ . ' - '; - if( ($empty_column = (trim($column)=='') ) ) - { - $errorMsg .= 'empty column - SQL:' . $sql; - trigger_error($errorMsg,E_USER_NOTICE); - return null; - } - - while($row = $this->fetch_array($result)) - { - if( !isset($row[$column]) ) - { - $errorMsg .= 'missing column:' . $column; - $errorMsg .= ' - SQL:' . $sql; - trigger_error($errorMsg,E_USER_NOTICE); - return null; - } - if(!isset($items[$row[$column]]) ) - { - $row['recordcount'] = 0; - } - else - { - $row['recordcount'] = $items[$row[$column]]['recordcount']; - } - $row['recordcount']++; - $items[$row[$column]] = $row; - } - } - - unset($result); - unset($row); - return $items; - } - - /** - * @used-by testplan.class.php - */ - function fetchMapRowsIntoMapStackOnCol($sql,$column_main_key,$column_sec_key,$stackOnCol) { - $items = null; - $result = $this->exec_query($sql); - if ($result) { - while($row = $this->fetch_array($result)) { - if( !isset($items[$row[$column_main_key]][$row[$column_sec_key]]) ) { - $items[$row[$column_main_key]][$row[$column_sec_key]] = $row; - $items[$row[$column_main_key]][$row[$column_sec_key]][$stackOnCol] = array(); - } - $items[$row[$column_main_key]][$row[$column_sec_key]][$stackOnCol][]=$row[$stackOnCol]; - } - } - unset($result); - return $items; - } - - -} // end of database class \ No newline at end of file +logEnabled = $value ? 1 : 0; + } + + private function getLogEnabled($value) + { + return $this->logEnabled; + } + + private function setLogQueries($value) + { + $this->logQueries = $value ? 1 : 0; + } + + private function getLogQueries($value) + { + return $this->logQueries; + } + + // TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher + public function __construct($db_type) + { + $fetch_mode = ADODB_FETCH_ASSOC; + + $this->dbType = $db_type; + if ($this->dbType == 'mysql' && + version_compare(phpversion(), "5.5.0", ">=")) { + $this->dbType = 'mysqli'; + } + $adodb_driver = $this->dbType; + + // added to reduce memory usage (before this setting we used ADODB_FETCH_BOTH) + if ($this->dbType == 'mssql') { + $fetch_mode = ADODB_FETCH_BOTH; + + // Faced this problem when testing XAMPP 1.7.7 on Windows 7 with MSSQL 2008 Express + // From PHP MANUAL - reganding mssql_* functions + // These functions allow you to access MS SQL Server database. + // This extension is not available anymore on Windows with PHP 5.3 or later. + // SQLSRV, an alternative driver for MS SQL is available from Microsoft: + // http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx. + // + // PHP_VERSION_ID is available as of PHP 5.2.7 + if (PHP_OS == 'WINNT' && defined('PHP_VERSION_ID') && + PHP_VERSION_ID >= 50300) { + $adodb_driver = 'mssqlnative'; + } + } + $this->db = NewADOConnection($adodb_driver); + $this->db->SetFetchMode($fetch_mode); + } + + // access to the ADODB object + public function get_dbmgr_object() + { + return $this->db; + } + + /** + * Make a connection to the database + */ + # changed Connect() to NConnect() see ADODB Manuals + public function connect($p_dsn, $p_hostname = null, $p_username = null, + $p_password = null, $p_database_name = null) + { + $result = array( + 'status' => 1, + 'dbms_msg' => 'ok' + ); + + if ($p_dsn === false) { + $t_result = $this->db->NConnect($p_hostname, $p_username, + $p_password, $p_database_name); + } else { + $t_result = $this->db->IsConnected(); + } + + if ($t_result) { + $this->is_connected = true; + } else { + $result['status'] = 0; + $result['dbms_msg'] = $this->error(); + } + return $result; + } + + /** + * execute SQL query, + * requires connection to be opened + * + * @param string $p_query + * SQL request + * @param integer $p_limit + * (optional) number of rows + * @param integer $p_offset + * (optional) begining row number + * + * @return boolean result of request + */ + public function exec_query($p_query, $p_limit = - 1, $p_offset = - 1) + { + $ec = 0; + $emsg = null; + $logLevel = 'DEBUG'; + $message = ''; + + if ($this->logQueries) { + $this->nQuery ++; + $t_start = $this->microtime_float(); + } + + if (($p_limit != - 1) || ($p_offset != - 1)) { + $t_result = $this->db->SelectLimit($p_query, $p_limit, $p_offset); + } else { + $t_result = $this->db->Execute($p_query); + } + + if ($this->logQueries) { + $t_elapsed = number_format($this->microtime_float() - $t_start, 4); + $this->overallDuration += $t_elapsed; + $message = "SQL [" . $this->nQuery . + "] executed [took {$t_elapsed} secs]" . + "[all took {$this->overallDuration} secs]:\n\t\t"; + } + $message .= $p_query; + + if (! $t_result) { + $ec = $this->error_num(); + $emsg = $this->error_msg(); + $message .= "\nQuery failed: errorcode[" . $ec . "]" . + "\n\terrormsg:" . $emsg; + $logLevel = 'ERROR'; + + tLog( + "ERROR ON exec_query() - database.class.php
    " . + $this->error(htmlspecialchars($p_query)) . + "
    THE MESSAGE : $message ", 'ERROR', "DATABASE"); + echo "
     ============================================================================== 
    "; + echo "
     DB Access Error - debug_print_backtrace() OUTPUT START 
    "; + echo "
     ATTENTION: Enabling more debug info will produce path disclosure weakness (CWE-200) 
    "; + echo "
                Having this additional Information could be useful for reporting 
    "; + echo "
                issue to development TEAM. 
    "; + echo "
     ============================================================================== 
    "; + + if (defined('DBUG_ON') && DBUG_ON == 1) { + echo "
    ";
    +                debug_print_backtrace();
    +                echo "
    "; + die(); + } + echo "
    ";
    +            debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
    +            echo "
    "; + die(); + + echo "
     ============================================================================== 
    "; + $t_result = false; + } + + if ($this->logEnabled) { + tLog($message, $logLevel, "DATABASE"); + } + + if ($this->logQueries) { + array_push($this->queries_array, + array( + $p_query, + $t_elapsed, + $ec, + $emsg + )); + } + + return $t_result; + } + + // TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher + public function fetch_array(&$p_result) + { + if ($p_result->EOF) { + return false; + } + + // mysql obeys FETCH_MODE_BOTH, hence ->fields works, other drivers do not support this + switch ($this->db->databaseType) { + case "mysql": + case "oci8po": + case "mssql": + case "mssqlnative": + $t_array = $p_result->fields; + break; + + default: + $t_array = $p_result->GetRowAssoc(false); + break; + } + + $p_result->MoveNext(); + return $t_array; + } + + // 20080315 - franciscom - Got new code from Mantis, that manages FETCH_MODE_ASSOC + private function db_result($p_result, $p_index1 = 0, $p_index2 = 0) + { + if ($p_result && ($this->num_rows($p_result) > 0)) { + $p_result->Move($p_index1); + $t_result = $p_result->GetArray(); + + if (isset($t_result[0][$p_index2])) { + return $t_result[0][$p_index2]; + } + + // The numeric index doesn't exist. FETCH_MODE_ASSOC may have been used. + // Get 2nd dimension and make it numerically indexed + $t_result = array_values($t_result[0]); + return $t_result[$p_index2]; + } + return false; + } + + /** + * + * @return integer the last inserted id + */ + public function insert_id($p_table = null) + { + if (isset($p_table) && ($this->db_is_pgsql() || $this->db_is_oracle())) { + if ($this->db_is_pgsql()) { + $sql = "SELECT currval('" . $p_table . "_id_seq')"; + } elseif ($this->db_is_oracle()) { + $sql = "SELECT " . $p_table . "_id_seq.currval from dual"; + } + $result = $this->exec_query($sql); + return $this->db_result($result); + } + return $this->db->Insert_ID(); + } + + /** + * Check is the database is PostgreSQL + */ + private function db_is_pgsql() + { + $status_ok = false; + $dbType = $this->dbType; + if (strpos($dbType, 'postgres') === 0) { + $dbType = 'postgres'; + } + + switch ($dbType) { + case 'postgres': + case 'pgsql': + $status_ok = true; + break; + } + return $status_ok; + } + + /** + * Check is the database is ORACLE + * + * @return boolean TRUE = Oracle type + */ + private function db_is_oracle() + { + $status_ok = false; + switch ($this->dbType) { + case 'oci8': + case 'oci8po': + $status_ok = true; + break; + } + return $status_ok; + } + + private function db_table_exists($p_table_name) + { + return in_array($p_table_name, $this->db->MetaTables("TABLE")); + } + + private function db_field_exists($p_field_name, $p_table_name) + { + return in_array($p_field_name, $this->db->MetaColumnNames($p_table_name)); + } + + /** + * Check if there is an index defined on the specified table/field and with + * the specified type. + * Warning: only works with MySQL + * + * @param string $p_table + * Name of table to check + * @param string $p_field + * Name of field to check + * @param string $p_key + * key type to check for (eg: PRI, MUL, ...etc) + * + * @return boolean + */ + private function key_exists_on_field($p_table, $p_field, $p_key) + { + $c_table = $this->db->prepare_string($p_table); + $c_field = $this->db->prepare_string($p_field); + $c_key = $this->db->prepare_string($p_key); + + $sql = "DESCRIBE $c_table"; + $result = $this->exec_query($sql); + + $count = $this->num_rows($result); + for ($i = 0; $i < $count; $i ++) { + $row = $this->db->fetch_array($result); + + if ($row['Field'] == $c_field) { + return $row['Key'] == $c_key; + } + } + return false; + } + + # prepare a string before DB insertion + # 20051226 - fm + public function prepare_string($p_string) + { + if (is_null($p_string)) { + return ''; + } + + $t_escaped = $this->db->qstr($p_string, false); + // from second char(1) to one before last(-1) + return substr($t_escaped, 1, - 1); + } + + # prepare an integer before DB insertion + public function prepare_int($p_int) + { + return (int) $p_int; + } + + # prepare a boolean before DB insertion + private function prepare_bool($p_bool) + { + return (int) (bool) $p_bool; + } + + # return current timestamp for DB + public function db_now() + { + switch ($this->db->databaseType) { + /* + * @todo: maybe we should use this? + * case 'odbc_mssql': + * return "GETDATE()"; + */ + default: + return $this->db->DBTimeStamp(time()); + } + } + + # generate a unixtimestamp of a date + # > SELECT UNIX_TIMESTAMP(); + # -> 882226357 + # > SELECT UNIX_TIMESTAMP('1997-10-04 22:23:00'); + # -> 875996580 + private function db_timestamp($p_date = null) + { + if (null !== $p_date) { + $p_timestamp = $this->db->UnixTimeStamp($p_date); + } else { + $p_timestamp = time(); + } + return $this->db->DBTimeStamp($p_timestamp); + } + + public function db_unixtimestamp($p_date = null) + { + if (null !== $p_date) { + $p_timestamp = $this->db->UnixTimeStamp($p_date); + } else { + $p_timestamp = time(); + } + return $p_timestamp; + } + + /** + * + * @return integer count queries + */ + private function count_queries() + { + return count($this->queries_array); + } + + /** + * + * @return integer count unique queries + */ + private function count_unique_queries() + { + $t_unique_queries = 0; + $t_shown_queries = array(); + foreach ($this->queries_array as $t_val_array) { + if (! in_array($t_val_array[0], $t_shown_queries)) { + $t_unique_queries ++; + array_push($t_shown_queries, $t_val_array[0]); + } + } + return $t_unique_queries; + } + + /** + * get total time for queries + */ + private function time_queries() + { + $t_count = count($this->queries_array); + $t_total = 0; + for ($i = 0; $i < $t_count; $i ++) { + $t_total += $this->queries_array[$i][1]; + } + return $t_total; + } + + /** + * close the connection. + * Not really necessary most of the time since a connection is + * automatically closed when a page finishes loading. + */ + public function close() + { + $this->db->Close(); + } + + private function error_num() + { + return $this->db->ErrorNo(); + } + + public function error_msg() + { + return $this->db->ErrorMsg(); + } + + /** + * returns a message string with: error num, error msg and query. + * + * @return string the message + */ + private function error($p_query = null) + { + $msg = $this->error_num() . " - " . $this->error_msg(); + + if (null !== $p_query) { + $msg .= " - " . $p_query; + } + return $msg; + } + + public function num_rows($p_result) + { + return $p_result->RecordCount(); + } + + public function affected_rows() + { + return $this->db->Affected_Rows(); + } + + /** + * Fetches the first column first row + * + * @param string $sql + * the query to be executed + * @param string $column + * the name of the column which shall be returned + * + * @return mixed the value of the column + */ + public function fetchFirstRowSingleColumn($sql, $column) + { + $value = null; + $row = $this->fetchFirstRow($sql); + + // BUGID 1318 + if ($row && array_key_exists($column, $row)) { + $value = $row[$column]; + } + return $value; + } + + /** + * Fetches the first row (in a assoc-array) + * + * @param string $sql + * the query to be executed + * @return array the first row + */ + public function fetchFirstRow($sql) + { + $result = $this->exec_query($sql); + $row = null; + if ($result) { + $row = $this->fetch_array($result); + } + unset($result); + return $row; + } + + /** + * Get one value (no array) + * for example: SELECT COUNT(*) FROM table + * + * @param string $sql + * the query to be executed + * @return string of one value || null + */ + public function fetchOneValue($sql) + { + $row = $this->fetchFirstRow($sql); + if ($row) { + $fieldName = array_keys($row); + return $row[$fieldName[0]]; + } + return null; + } + + /** + * Fetches all values for a given column of all returned rows + * + * @param string $sql + * the query to be executed + * @param string $column + * the name of the column + * @param integer $limit + * (optional) number of rows + * + * @return array an enumerated array, which contains all the values + */ + public function fetchColumnsIntoArray($sql, $column, $limit = - 1) + { + $items = null; + $result = $this->exec_query($sql, $limit); + if ($result) { + while ($row = $this->fetch_array($result)) { + $items[] = $row[$column]; + } + } + + unset($result); + return $items; + } + + /** + * Fetches all rows into a map whose keys are the values of columns + * + * @param string $sql + * the query to be executed + * @param string $column + * the name of the column + * @param boolean $cumulative + * default 0 + * useful in situations with results set with multiple + * rows with same value on key column like this: + * + * col1 col2 col3 ... + * X A C + * X B Z + * Y B 0 + * + * cumulative=0 -> + * return items= array('X' => array('A','C'), 'Y' => array('B','0') ) + * + * cumulative=1 -> + * return items= + * array('X' => + * array( 0 => array('A','C'), + * 1 => array('B','Z')), + * 'Y' => array( 0 => array('B','0') ) + * + * @param integer $limit + * (optional) number of rows + * + * @return array an assoc array whose keys are the values from the columns + * of the rows + */ + public function fetchRowsIntoMap($sql, $column, $cumulative = 0, + $limit = - 1, $col2implode = '') + { + $items = null; + $result = $this->exec_query($sql, $limit); + if ($result) { + // Error management Code + $errorMsg = __CLASS__ . '/' . __FUNCTION__ . ' - '; + if (trim($column) == '') { + $errorMsg .= 'empty column - SQL:' . $sql; + trigger_error($errorMsg, E_USER_NOTICE); + return null; + } + + while ($row = $this->fetch_array($result)) { + // Error management Code + if (! isset($row[$column])) { + $errorMsg .= 'missing column:' . $column; + $errorMsg .= ' - SQL:' . $sql; + trigger_error($errorMsg, E_USER_NOTICE); + return null; + } + + if ($cumulative) { + $items[$row[$column]][] = $row; + } elseif ($col2implode != '') { + if (isset($items[$row[$column]])) { + $items[$row[$column]][$col2implode] .= ',' . + $row[$col2implode]; + } else { + $items[$row[$column]] = $row; + } + } else { + $items[$row[$column]] = $row; + } + } + } + + unset($result); + unset($row); + return $items; + } + + /** + * Fetches the values of two columns from all rows into a map + * + * @param string $sql + * the query to be executed + * @param string $column1 + * the name of the column (keys for the map) + * @param string $column2 + * the name of the second column (values of the map) + * @param boolean $cumulative + * useful in situations with results set like + * col1 col2 + * X A + * X B + * Y B + * + * cumulative=0 -> return items= array('X' => 'B', 'Y' => 'B') + * + * cumulative=1 -> return items= array('X' => array('A','B'), 'Y' => array('B') ) + * + * @param integer $limit + * (optional) number of rows + * + * @return array whose keys are the values of column1 and the values are: + * + * cumulative=0 => the values of column2 + * cumulative=1 => array with the values of column2 + * + */ + public function fetchColumnsIntoMap($sql, $column1, $column2, + $cumulative = 0, $limit = - 1) + { + $result = $this->exec_query($sql, $limit); + $items = null; + if ($result) { + while ($myrow = $this->fetch_array($result)) { + if ($cumulative) { + $items[$myrow[$column1]][] = $myrow[$column2]; + } else { + $items[$myrow[$column1]] = $myrow[$column2]; + } + } + } + + unset($result); + return $items; + } + + /** + * database server information + * wrapper for adodb method ServerInfo + * + * @return array members 'version' and 'description' + */ + private function get_version_info() + { + return $this->db->ServerInfo(); + } + + /** + */ + public function get_recordset($sql, $fetch_mode = null, $limit = - 1, + $start = - 1) + { + $output = null; + + $result = $this->exec_query($sql, $limit, $start); + if ($result) { + while ($row = $this->fetch_array($result)) { + $output[] = $row; + } + } + + unset($result); + return $output; + } + + /** + * Fetches all rows into a map whose keys are the values of columns + * + * @param string $sql + * the query to be executed + * @param string $column + * the name of the column + * @param integer $limit + * (optional) number of rows + * + * @return array an assoc array whose keys are the values from the columns + * of the rows + */ + public function fetchArrayRowsIntoMap($sql, $column, $limit = - 1) + { + $items = null; + $result = $this->exec_query($sql, $limit); + if ($result) { + while ($row = $this->fetch_array($result)) { + $items[$row[$column]][] = $row; + } + } + + unset($result); + return $items; + } + + /** + * Fetches all rows into a map whose keys are the values of columns + * + * @param string $sql + * the query to be executed + * @param string $column_main_key + * the name of the column + * @param string $column_sec_key + * the name of the column + * @param boolean $cumulative + * @param integer $limit + * (optional) number of rows + * + * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] + * + */ + public function fetchMapRowsIntoMap($sql, $main_key, $sec_key, + $cumulative = 0, $limit = - 1, $col2implode = '') + { + $items = null; + $result = $this->exec_query($sql, $limit); + if ($result) { + while ($row = $this->fetch_array($result)) { + if ($cumulative) { + $items[$row[$main_key]][$row[$sec_key]][] = $row; + } elseif ($col2implode != '') { + if (isset($items[$row[$main_key]][$row[$sec_key]])) { + $items[$row[$main_key]][$row[$sec_key]][$col2implode] .= ',' . + $row[$col2implode]; + } else { + $items[$row[$main_key]][$row[$sec_key]] = $row; + } + } else { + $items[$row[$main_key]][$row[$sec_key]] = $row; + } + } + } + + unset($result); + return $items; + } + + /** + * TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher + */ + public function build_sql_create_db($db_name) + { + $sql = ''; + $dbType = $this->db->databaseType; + + // @user contribution + if (strpos($dbType, 'postgres') === 0) { + $dbType = 'postgres'; + } + + switch ($dbType) { + case 'postgres': + $sql = 'CREATE DATABASE "' . $this->prepare_string($db_name) . + '" ' . "WITH ENCODING='UNICODE' "; + break; + + case 'mssql': + case 'mssqlnative': + $sql = 'CREATE DATABASE [' . $this->prepare_string($db_name) . + '] '; + break; + + case 'mysql': + default: + $sql = "CREATE DATABASE `" . $this->prepare_string($db_name) . + "` CHARACTER SET utf8 "; + break; + } + return $sql; + } + + /** + * + * @return NULL|string + */ + public function db_null_timestamp() + { + $db_type = $this->db->databaseType; + $nullValue = null; + + switch ($db_type) { + case 'mysql': + // is not an error i put single quote on value + $nullValue = " '0000-00-00 00:00:00' "; + break; + } + return $nullValue; + } + + /** + * Fetches all rows into a map of 2 levels + * + * @param string $sql + * the query to be executed + * @param array $keyCols, + * columns to used as access key + * @param boolean $cumulative + * @param integer $limit + * (optional) number of rows + * + * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] + * + */ + public function fetchRowsIntoMap2l($sql, $keyCols, $cumulative = 0, + $limit = - 1) + { + $items = null; + $result = $this->exec_query($sql, $limit); + + if ($result) { + while ($row = $this->fetch_array($result)) { + if ($cumulative) { + $items[$row[$keyCols[0]]][$row[$keyCols[1]]][] = $row; + } else { + $items[$row[$keyCols[0]]][$row[$keyCols[1]]] = $row; + } + } + } + + unset($result); + return $items; + } + + /** + * Fetches all rows into a map of 3 levels + * + * @param string $sql + * the query to be executed + * @param array $keyCols, + * columns to used as access key + * @param boolean $cumulative + * @param integer $limit + * (optional) number of rows + * + * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] + * + */ + public function fetchRowsIntoMap3l($sql, $keyCols, $cumulative = 0, + $limit = - 1) + { + $items = null; + $result = $this->exec_query($sql, $limit); + + if ($result) { + while ($row = $this->fetch_array($result)) { + if ($cumulative) { + $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][] = $row; + } else { + $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]] = $row; + } + } + } + + unset($result); + return $items; + } + + /** + * Fetches all rows into a map of 4 levels + * + * @param string $sql + * the query to be executed + * @param array $keyCols, + * columns to used as access key + * @param boolean $cumulative + * @param integer $limit + * (optional) number of rows + * + * @return array $items[$row[$column_main_key]][$row[$column_sec_key]] + * + */ + public function fetchRowsIntoMap4l($sql, $keyCols, $cumulative = 0, + $limit = - 1) + { + $items = null; + $result = $this->exec_query($sql, $limit); + + if ($result) { + while ($row = $this->fetch_array($result)) { + if ($cumulative) { + $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][$row[$keyCols[3]]][] = $row; + } else { + $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][$row[$keyCols[3]]] = $row; + } + } + } + unset($result); + + return $items; + } + + /** + * Fetches all rows into a map whose keys are the values of columns + * + * @param string $sql + * the query to be executed + * @param string $column + * the name of the column + * + * @return array an assoc array + */ + public function fetchRowsIntoMapAddRC($sql, $column, $limit = - 1) + { + $items = null; + $result = $this->exec_query($sql, $limit); + if ($result) { + $errorMsg = __CLASS__ . '/' . __FUNCTION__ . ' - '; + if (trim($column) == '') { + $errorMsg .= 'empty column - SQL:' . $sql; + trigger_error($errorMsg, E_USER_NOTICE); + return null; + } + + while ($row = $this->fetch_array($result)) { + if (! isset($row[$column])) { + $errorMsg .= 'missing column:' . $column; + $errorMsg .= ' - SQL:' . $sql; + trigger_error($errorMsg, E_USER_NOTICE); + return null; + } + if (! isset($items[$row[$column]])) { + $row['recordcount'] = 0; + } else { + $row['recordcount'] = $items[$row[$column]]['recordcount']; + } + $row['recordcount'] ++; + $items[$row[$column]] = $row; + } + } + + unset($result); + unset($row); + return $items; + } + + /** + * + * @used-by testplan.class.php + */ + public function fetchMapRowsIntoMapStackOnCol($sql, $column_main_key, + $column_sec_key, $stackOnCol) + { + $items = null; + $result = $this->exec_query($sql); + if ($result) { + while ($row = $this->fetch_array($result)) { + if (! isset( + $items[$row[$column_main_key]][$row[$column_sec_key]])) { + $items[$row[$column_main_key]][$row[$column_sec_key]] = $row; + $items[$row[$column_main_key]][$row[$column_sec_key]][$stackOnCol] = array(); + } + $items[$row[$column_main_key]][$row[$column_sec_key]][$stackOnCol][] = $row[$stackOnCol]; + } + } + unset($result); + return $items; + } +} diff --git a/lib/functions/date_api.php b/lib/functions/date_api.php index 3b52b6d7f5..ff2d2f8ec3 100644 --- a/lib/functions/date_api.php +++ b/lib/functions/date_api.php @@ -1,251 +1,293 @@ -$month_name"; - } else { - $month_option .= ""; - } - } - return $month_option; +$month_name"; + } else { + $month_option .= ""; + } + } + return $month_option; +} + +function create_numeric_month_option_list($p_month = 0) +{ + $month_option = ''; + for ($i = 1; $i <= 12; $i ++) { + if ($i == $p_month) { + $month_option .= ""; + } else { + $month_option .= ""; + } + } + return $month_option; +} + +function create_day_option_list($p_day = 0) +{ + $day_option = ''; + for ($i = 1; $i <= 31; $i ++) { + if ($i == $p_day) { + $day_option .= ""; + } else { + $day_option .= ""; + } + } + return $day_option; +} + +function create_year_option_list($p_year = 0) +{ + $year_option = ''; + $current_year = date("Y"); + + for ($i = $current_year; $i > 1999; $i --) { + if ($i == $p_year) { + $year_option .= ""; + } else { + $year_option .= ""; + } + } + return $year_option; +} + +// Added contribution (done on mantis) to manage datetime +/** + * used in cfield_mgr.class.php + */ +function create_date_selection_set($p_name, $p_format, $p_date = 0, + $options = null) +{ + $opt = array( + 'default_disable' => false, + 'allow_blank' => false, + 'show_on_filters' => false, + 'required' => '' + ); + + $opt = array_merge($opt, (array) $options); + + $localeDateFormat = config_get('locales_date_format'); + $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; + $date_format = $localeDateFormat[$locale]; + $date_format_without_percent = str_replace('%', '', $date_format); + + // if calender shall be shown on filter position has to be fixed to fully display + $calender_div_position = $opt['show_on_filters'] ? "fixed" : "absolute"; + + $str_out = ''; + $t_chars = preg_split('//', $p_format, - 1, PREG_SPLIT_NO_EMPTY); + if ($p_date != 0) { + $t_date = preg_split('/-| |:/', date('Y-m-d H:i:s', $p_date), - 1, + PREG_SPLIT_NO_EMPTY); + } else { + $t_date = array( + - 1, + - 1, + - 1, + - 1, + - 1, + - 1 + ); + } + + $t_disable = ''; + $t_blank_line_time = ''; + if ($opt['default_disable']) { + $t_disable = 'disabled'; + } + if ($opt['allow_blank']) { + $t_blank_line_time = ""; + } + + $m = $t_date[1]; + $d = $t_date[2]; + $y = $t_date[0]; + + // PHP on 32bit systems, when passing mktime(0,0,0,-1,-1,-1) returns false. + // PHP on 64bit systems it returns a long negative value which causes the error. + if ($m < 0 || $d < 0 || $y < 0) { + $time = 0; + } else { + $time = mktime(0, 0, 0, $m, $d, $y); + } + + $formatted_date = $time != 0 ? strftime($date_format, $time) : ''; + + $str_out .= '' . ' ' . ' ' . '
    '; + + // Here we work with the TIME PART, that exists only when we require TIMESTAMP + foreach ($t_chars as $t_char) { + $common = $opt['required'] . " $t_disable>"; + if (strcasecmp($t_char, "H") == 0) { + $mask = '' . - ' ' . - ' ' . - '
    '; - - - // Here we work with the TIME PART, that exists only when we require TIMESTAMP - foreach( $t_chars as $t_char ) - { - $common = $opt['required'] . " $t_disable>" ; - if (strcasecmp( $t_char, "H") == 0) - { - $mask = '" ; - - return $Html ; - } - -} // class end \ No newline at end of file +InstanceName = $instanceName; + $this->Value = ''; + } + + public function Create($rows = null, $cols = null) + { + echo $this->CreateHtml($rows, $cols); + } + + public function CreateHtml($rows = null, $cols = null) + { + $htmlValue = htmlspecialchars($this->Value); + + $my_rows = $rows; + $my_cols = $cols; + + if (is_null($my_rows) || $my_rows <= 0) { + $my_rows = $this->rows; + } + + if (is_null($my_cols) || $my_cols <= 0) { + $my_cols = $this->cols; + } + + return ' "; + } +} diff --git a/lib/functions/oauth_api.php b/lib/functions/oauth_api.php index 5d575c996f..9e2d3fc31c 100644 --- a/lib/functions/oauth_api.php +++ b/lib/functions/oauth_api.php @@ -1,47 +1,54 @@ - $cfg['oauth_client_id'], + 'clientSecret' => $cfg['oauth_client_secret'], + 'redirectUri' => $cfg['redirect_uri'] + ]; + break; + } + + session_start(); + switch ($oauth2Name) { + case 'gitlab': + $provider = new Omines\OAuth2\Client\Provider\Gitlab($providerCfg); + $urlOpt = []; + break; + + case 'github': + $provider = new \League\OAuth2\Client\Provider\Github($providerCfg); + $urlOpt = [ + 'scope' => [ + 'user', + 'user:email', + 'public_profile' + ] + ]; + break; + + case 'google': + $provider = new League\OAuth2\Client\Provider\Google($providerCfg); + $urlOpt = []; + break; + + case 'microsoft': + case 'azuread': + $clientType = 'testLink'; + $_SESSION['oauth2state'] = $oauth2Name . '$$$' . + bin2hex(random_bytes(32)); + + // see https://docs.microsoft.com/en-us/azure/ + // active-directory/develop/v1-protocols-oauth-code + // for details + $oap = []; + $oap['state'] = $_SESSION['oauth2state']; + $oap['redirect_uri'] = $cfg['redirect_uri']; + $oap['client_id'] = $cfg['oauth_client_id']; + $oap['scope'] = $cfg['oauth_scope']; + + $oap['response_type'] = 'code'; + + if ($oauth2Name == 'azuread') { + if (! is_null($oauthCfg['oauth_domain'])) { + $oap['domain_hint'] = $oauthCfg['oauth_domain']; + } + } else { + if ($oauthCfg['oauth_force_single']) { + $oap['prompt'] = 'consent'; + } + } + + // http_build_query — Generate URL-encoded query string + $authUrl = $cfg['oauth_url'] . '?' . http_build_query($oap); + break; + + default: + $clientType = 'testLink'; + break; + } + + switch ($clientType) { + case 'ThePHPLeague': + // Give a look to + // https://github.com/omines/oauth2-gitlab#managing-scopes + // + $authUrl = $provider->getAuthorizationUrl($urlOpt); + + // We are setting this to be able to check given state + // against previously stored one (this one!!) + // to mitigate CSRF attack + // This check will be done in method oauth_get_token() + // + $_SESSION['oauth2state'] = $provider->getState(); + header('Location: ' . $authUrl); + exit(); + break; + + default: + header('Location: ' . $authUrl); + break; + } +} diff --git a/lib/functions/oauth_providers/azuread.php b/lib/functions/oauth_providers/azuread.php index 4412a54772..109224638f 100644 --- a/lib/functions/oauth_providers/azuread.php +++ b/lib/functions/oauth_providers/azuread.php @@ -1,94 +1,98 @@ -status = array('status' => tl::OK, 'msg' => null); - - // Params to get token - $oauthParams = array( - 'code' => $code, - 'grant_type' => $authCfg['oauth_grant_type'], - 'client_id' => $authCfg['oauth_client_id'], - 'client_secret' => $authCfg['oauth_client_secret'] - ); - - $oauthParams['redirect_uri'] = trim($authCfg['redirect_uri']); - if( isset($_SERVER['HTTPS']) ) { - $oauthParams['redirect_uri'] = - str_replace('http://', 'https://', $oauthParams['redirect_uri']); - } - - $token_curl = curl_init(); - curl_setopt($token_curl, CURLOPT_URL, $authCfg['token_url']); - curl_setopt($token_curl, CURLOPT_POST, 1); - curl_setopt($token_curl, CURLOPT_POSTFIELDS, http_build_query($oauthParams)); - curl_setopt($token_curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($token_curl, CURLOPT_SSL_VERIFYPEER, false); - $result_token_curl = curl_exec($token_curl); - curl_close($token_curl); - $tokenInfo = json_decode($result_token_curl, true); - - // To get user principal name, given name, and surname from token endpoint, Directory.ReadWriteAll permission is required. - // However, it is too much to give Write permission, so if you get them via the graph API, only User.Read and Email.Read will be sufficient. - $graph_curl = curl_init(); - $graph_api_header = [ - 'Content-Type: application/json', - 'Authorization: Bearer ' . $tokenInfo['access_token'] - ]; - curl_setopt($graph_curl, CURLOPT_URL, 'https://graph.microsoft.com/v1.0/me'); - curl_setopt($graph_curl, CURLOPT_HTTPHEADER, $graph_api_header); - curl_setopt($graph_curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($graph_curl, CURLOPT_SSL_VERIFYPEER, false); - $result_graph_curl = curl_exec($graph_curl); - curl_close($graph_curl); - $userInfo = json_decode($result_graph_curl, true); - - // At this point we may turn to the user_info endpoint for additional information - // but for now, we are going to ignore it, as all neccessary information is available - // in the id_token - if (isset($tokenInfo['id_token'])){ - list($header, $payload, $signature) = explode(".", $tokenInfo['id_token']); - $jwtInfo = json_decode(base64_decode ($payload), true); - - if (isset($jwtInfo['oid'])){ - if (isset($authCfg['oauth_domain'])) { - $domain = substr(strrchr($userInfo['userPrincipalName'], "@"), 1); - if ($domain !== $authCfg['oauth_domain']){ - $result->status['msg'] = - "TestLink Oauth policy - User email domain:$domain does not - match \$authCfg['oauth_domain']:{$authCfg['oauth_domain']} "; - $result->status['status'] = tl::ERROR; - } - } - } else { - $result->status['msg'] = 'TestLink - User ID is empty'; - $result->status['status'] = tl::ERROR; - } - - $options = new stdClass(); - $options->givenName = $userInfo['givenName']; - $options->familyName = $userInfo['surname']; - $options->user = $userInfo['userPrincipalName']; - $options->auth = 'oauth'; - - $result->options = $options; - } else { - $result->status['msg'] = 'TestLink - An error occurred during get token e'.$result_curl.'e'; - $result->status['status'] = tl::ERROR; - } - - return $result; -} \ No newline at end of file +status = array( + 'status' => tl::OK, + 'msg' => null + ); + + // Params to get token + $oauthParams = array( + 'code' => $code, + 'grant_type' => $authCfg['oauth_grant_type'], + 'client_id' => $authCfg['oauth_client_id'], + 'client_secret' => $authCfg['oauth_client_secret'] + ); + + $oauthParams['redirect_uri'] = trim($authCfg['redirect_uri']); + if (isset($_SERVER['HTTPS'])) { + $oauthParams['redirect_uri'] = str_replace('http://', 'https://', + $oauthParams['redirect_uri']); + } + + $token_curl = curl_init(); + curl_setopt($token_curl, CURLOPT_URL, $authCfg['token_url']); + curl_setopt($token_curl, CURLOPT_POST, 1); + curl_setopt($token_curl, CURLOPT_POSTFIELDS, http_build_query($oauthParams)); + curl_setopt($token_curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($token_curl, CURLOPT_SSL_VERIFYPEER, false); + $result_token_curl = curl_exec($token_curl); + curl_close($token_curl); + $tokenInfo = json_decode($result_token_curl, true); + + // To get user principal name, given name, and surname from token endpoint, Directory.ReadWriteAll permission is required. + // However, it is too much to give Write permission, so if you get them via the graph API, only User.Read and Email.Read will be sufficient. + $graph_curl = curl_init(); + $graph_api_header = [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $tokenInfo['access_token'] + ]; + curl_setopt($graph_curl, CURLOPT_URL, $authCfg['oauth_profile']); + curl_setopt($graph_curl, CURLOPT_HTTPHEADER, $graph_api_header); + curl_setopt($graph_curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($graph_curl, CURLOPT_SSL_VERIFYPEER, false); + $result_graph_curl = curl_exec($graph_curl); + curl_close($graph_curl); + $userInfo = json_decode($result_graph_curl, true); + + // At this point we may turn to the user_info endpoint for additional information + // but for now, we are going to ignore it, as all neccessary information is available + // in the id_token + if (isset($tokenInfo['id_token'])) { + list (, $payload,) = explode(".", $tokenInfo['id_token']); + $jwtInfo = json_decode(base64_decode($payload), true); + + if (isset($jwtInfo['oid'])) { + if (isset($authCfg['oauth_domain'])) { + $domain = substr(strrchr($userInfo['email'], "@"), 1); + if ($domain !== $authCfg['oauth_domain']) { + $result->status['msg'] = "TestLink Oauth policy - User email domain:$domain does not + match \$authCfg['oauth_domain']:{$authCfg['oauth_domain']} "; + $result->status['status'] = tl::ERROR; + } + } + } else { + $result->status['msg'] = 'TestLink - User ID is empty'; + $result->status['status'] = tl::ERROR; + } + + $options = new stdClass(); + $options->givenName = $userInfo['given_name']; + $options->familyName = $userInfo['family_name']; + $options->user = $userInfo['email']; + $options->auth = 'oauth'; + + $result->options = $options; + } else { + $result->status['msg'] = 'TestLink - An error occurred during get token e' . + $result_curl . 'e'; + $result->status['status'] = tl::ERROR; + } + + return $result; +} diff --git a/lib/functions/oauth_providers/github.php b/lib/functions/oauth_providers/github.php index 4137c2f7bb..f2d2fd28a1 100644 --- a/lib/functions/oauth_providers/github.php +++ b/lib/functions/oauth_providers/github.php @@ -1,105 +1,90 @@ -status = array('status' => tl::OK, 'msg' => null); - - // Params to get token - $oauthParams = array( - 'code' => $code, - 'client_id' => $authCfg['oauth_client_id'], - 'client_secret' => $authCfg['oauth_client_secret'] - ); - - $oauthParams['redirect_uri'] = $oauthCfg['redirect_uri']; - if( isset($_SERVER['HTTPS']) ) { - $oauthParams['redirect_uri'] = - str_replace('http://', 'https://', $oauthParams['redirect_uri']); - } - - $curlAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1'; - $curlContentType = array('Content-Type: application/xml','Accept: application/json'); - - // Step #1 - Get the token - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $authCfg['token_url']); - curl_setopt($curl, CURLOPT_POST, 1); - curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/json')); - curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($oauthParams)); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_COOKIESESSION, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - $result_curl = curl_exec($curl); - - if( $result_curl === false ) { - echo 'Curl error: ' . curl_error($curl); - echo '
    ';
    -    var_dump(curl_getinfo($curl));
    -    echo '
    '; - die(); - } - curl_close($curl); - $tokenInfo = json_decode($result_curl); - - // If token is received start session - if (isset($tokenInfo->access_token)) { - $oauthParams['access_token'] = $tokenInfo->access_token; - - $queryString = http_build_query($tokenInfo); - $targetURL = array(); - $targetURL['user'] = $authCfg['oauth_profile'] . '?' . $queryString; - $targetURL['email'] = $authCfg['oauth_profile'] . '/emails?'. $queryString; - - // Get User - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $targetURL['user']); - curl_setopt($curl, CURLOPT_USERAGENT, $curlAgent); - curl_setopt($curl, CURLOPT_HTTPHEADER, $curlContentType); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - $result_curl = curl_exec($curl); - $userInfo = json_decode($result_curl, true); - curl_close($curl); - - if (!isset($userInfo['login'])) { - $result->status['msg'] = 'User ID is empty'; - $result->status['status'] = tl::ERROR; - } - - // Get email - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $targetURL['email'] ); - curl_setopt($curl, CURLOPT_USERAGENT, $curlAgent); - curl_setopt($curl, CURLOPT_HTTPHEADER, $curlContentType); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - $result_curl = curl_exec($curl); - $emailInfo = json_decode($result_curl, true); - curl_close($curl); - - - $result->options = new stdClass(); - $result->options->givenName = $userInfo['login']; - $result->options->familyName = $userInfo['id']; - $result->options->user = $emailInfo[0]['email']; - $result->options->auth = 'oauth'; - - } else { - $result->status['msg'] = 'An error occurred during getting token'; - $result->status['status'] = tl::ERROR; - } - - return $result; -} +status = array( + 'status' => tl::OK, + 'msg' => null + ); + + $oauthParams['redirect_uri'] = $authCfg['redirect_uri']; + if (isset($_SERVER['HTTPS'])) { + $oauthParams['redirect_uri'] = str_replace('http://', 'https://', + $oauthParams['redirect_uri']); + } + + $providerCfg = [ + 'clientId' => $authCfg['oauth_client_id'], + 'clientSecret' => $authCfg['oauth_client_secret'], + 'redirectUri' => $oauthParams['redirect_uri'] + ]; + + $provider = new \League\OAuth2\Client\Provider\Github($providerCfg); + + // CRITICAL + // Suggested in https://github.com/thephpleague/oauth2-client + // + // Check given state ($_GET) against previously stored one + // ($_SESSION) to mitigate CSRF attack + if (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { + throw new Exception("OAuth CSRF Check using ", 1); + } + + // Try to get an access token (using the authorization code grant) + $token = $provider->getAccessToken('authorization_code', + [ + 'code' => $_GET['code'] + ]); + + // Now you have a token you can look up a users profile data + try { + // We got an access token, let's now get the user's details + $user = $provider->getResourceOwner($token); + + $firstLast = $user->getName(); + $result->options = new stdClass(); + $result->options->givenName = $firstLast; + $result->options->familyName = $firstLast; + + // Github does not provide the email in the getResourceOwner() + // response if the user has configured the mail as Private. + // Solution from: + // https://github.com/thephpleague/oauth2-github/issues/3 + // + $email = $user->getEmail(); + if ($email == null) { + $request = $provider->getAuthenticatedRequest('GET', + 'https://api.github.com/user/emails', $token // Your access token + ); + $array = $provider->getParsedResponse($request); + foreach ($array as $item) { + if ($item['primary'] === true) { + $email = $item['email']; + break; + } + } + } + $result->options->user = $email; + $result->options->email = $email; + $result->options->auth = 'oauth'; + + return $result; + } catch (Exception $e) { + // Failed to get user details + exit('Oh dear...'); + } +} diff --git a/lib/functions/oauth_providers/gitlab.php b/lib/functions/oauth_providers/gitlab.php new file mode 100644 index 0000000000..5b0cfb41ac --- /dev/null +++ b/lib/functions/oauth_providers/gitlab.php @@ -0,0 +1,72 @@ +status = array( + 'status' => tl::OK, + 'msg' => null + ); + + $oauthParams['redirect_uri'] = $authCfg['redirect_uri']; + if (isset($_SERVER['HTTPS'])) { + $oauthParams['redirect_uri'] = str_replace('http://', 'https://', + $oauthParams['redirect_uri']); + } + + $providerCfg = [ + 'clientId' => $authCfg['oauth_client_id'], + 'clientSecret' => $authCfg['oauth_client_secret'], + 'redirectUri' => $oauthParams['redirect_uri'] + ]; + + $provider = new Omines\OAuth2\Client\Provider\Gitlab($providerCfg); + + // CRITICAL + // Suggested in https://github.com/thephpleague/oauth2-client + // + // Check given state ($_GET) against previously stored one + // ($_SESSION) to mitigate CSRF attack + if (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { + throw new Exception("OAuth CSRF Check using ", 1); + } + + // Try to get an access token (using the authorization code grant) + $token = $provider->getAccessToken('authorization_code', + [ + 'code' => $_GET['code'] + ]); + + // Now you have a token you can look up a users profile data + try { + // We got an access token, let's now get the user's details + $user = $provider->getResourceOwner($token); + + $firstLast = $user->getName(); + $result->options = new stdClass(); + $result->options->givenName = $firstLast; + $result->options->familyName = $firstLast; + $result->options->user = $user->getEmail(); + $result->options->email = $user->getEmail(); + $result->options->login = $user->getUserName(); + $result->options->auth = 'oauth'; + + return $result; + } catch (Exception $e) { + // Failed to get user details + exit('Oh dear...'); + } +} diff --git a/lib/functions/oauth_providers/google.php b/lib/functions/oauth_providers/google.php index 240cf70257..1c669f7036 100644 --- a/lib/functions/oauth_providers/google.php +++ b/lib/functions/oauth_providers/google.php @@ -1,80 +1,72 @@ -status = array('status' => tl::OK, 'msg' => null); - - //Params to get token - $oauthParams = array( - 'code' => $code, - 'grant_type' => $authCfg['oauth_grant_type'], - 'client_id' => $authCfg['oauth_client_id'], - 'client_secret' => $authCfg['oauth_client_secret'] - ); - - $oauthParams['redirect_uri'] = trim($authCfg['redirect_uri']); - if( isset($_SERVER['HTTPS']) ) { - $oauthParams['redirect_uri'] = - str_replace('http://', 'https://', $oauthParams['redirect_uri']); - } - - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $authCfg['token_url']); - curl_setopt($curl, CURLOPT_POST, 1); - curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($oauthParams)); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - $result_curl = curl_exec($curl); - curl_close($curl); - $tokenInfo = json_decode($result_curl, true); - - //If token is received start session - if (isset($tokenInfo['access_token'])){ - $oauthParams['access_token'] = $tokenInfo['access_token']; - $userInfo = json_decode(file_get_contents($authCfg['oauth_profile'] . '?' . - http_build_query($oauthParams)), true); - - if (isset($userInfo['id'])){ - if (isset($authCfg['oauth_domain'])) { - $domain = substr(strrchr($userInfo['email'], "@"), 1); - if ($domain !== $authCfg['oauth_domain']){ - $result->status['msg'] = - "TestLink Oauth policy - User email domain:$domain does not - match \$authCfg['oauth_domain']:{$authCfg['oauth_domain']} "; - $result->status['status'] = tl::ERROR; - } - } - } else { - $result->status['msg'] = 'TestLink - User ID is empty'; - $result->status['status'] = tl::ERROR; - } - - $options = new stdClass(); - $options->givenName = $userInfo['given_name']; - $options->familyName = $userInfo['family_name']; - $options->user = $userInfo['email']; - $options->auth = 'oauth'; - - $result->options = $options; - } else { - $result->status['msg'] = 'TestLink - An error occurred during get token'; - $result->status['status'] = tl::ERROR; - } - - return $result; - -} +status = array( + 'status' => tl::OK, + 'msg' => null + ); + + $oauthParams['redirect_uri'] = $authCfg['redirect_uri']; + if (isset($_SERVER['HTTPS'])) { + $oauthParams['redirect_uri'] = str_replace('http://', 'https://', + $oauthParams['redirect_uri']); + } + + $providerCfg = [ + 'clientId' => $authCfg['oauth_client_id'], + 'clientSecret' => $authCfg['oauth_client_secret'], + 'redirectUri' => $oauthParams['redirect_uri'] + ]; + + $provider = new League\OAuth2\Client\Provider\Google($providerCfg); + + // + // CRITICAL + // Suggested in https://github.com/thephpleague/oauth2-client + // + // Check given state ($_GET) against previously stored one + // ($_SESSION) to mitigate CSRF attack + if (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { + throw new Exception("OAuth CSRF Check using ", 1); + } + + // Try to get an access token (using the authorization code grant) + $token = $provider->getAccessToken('authorization_code', + [ + 'code' => $_GET['code'] + ]); + + // Now you have a token you can look up a users profile data + try { + // We got an access token, let's now get the user's details + $user = $provider->getResourceOwner($token); + + $result->options = new stdClass(); + $result->options->givenName = $user->getFirstName(); + $result->options->familyName = $user->getLastName(); + $result->options->user = $user->getEmail(); + $result->options->email = $user->getEmail(); + $result->options->login = $user->getEmail(); + $result->options->auth = 'oauth'; + + return $result; + } catch (Exception $e) { + // Failed to get user details + exit('Oh dear...'); + } +} diff --git a/lib/functions/oauth_providers/microsoft.php b/lib/functions/oauth_providers/microsoft.php index 17e0e5481b..8635484b48 100644 --- a/lib/functions/oauth_providers/microsoft.php +++ b/lib/functions/oauth_providers/microsoft.php @@ -1,96 +1,104 @@ -status = array('status' => tl::OK, 'msg' => null); - - // Params to get token - $oauthParams = array( - 'code' => $code, - 'client_id' => $authCfg['oauth_client_id'], - 'client_secret' => $authCfg['oauth_client_secret'], - 'scope' => $authCfg['oauth_scope'], - 'grant_type' => $authCfg['oauth_grant_type'] - ); - - $oauthParams['redirect_uri'] = $authCfg['redirect_uri']; - if( isset($_SERVER['HTTPS']) ) { - $oauthParams['redirect_uri'] = - str_replace('http://', 'https://', $oauthParams['redirect_uri']); - } - - $curlAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1'; - $curlContentType = array('Content-Type: application/xml','Accept: application/json'); - - // Step #1 - Get the token - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $authCfg['token_url']); - curl_setopt($curl, CURLOPT_POST, 1); - curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/json')); - curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($oauthParams)); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_COOKIESESSION, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - $result_curl = curl_exec($curl); - - if( $result_curl === false ) { - echo 'Curl error: ' . curl_error($curl); - echo '
    ';
    -    var_dump(curl_getinfo($curl));
    -    echo '
    '; - die(); - } - curl_close($curl); - $tokenInfo = json_decode($result_curl); - - // If token is received start session - if (isset($tokenInfo->access_token)) { - $oauthParams['access_token'] = $tokenInfo->access_token; - - $targetURL = array(); - $targetURL['user'] = $authCfg['oauth_profile']; - $curlContentType[] = 'Authorization: Bearer '.$oauthParams['access_token']; - - // Get User - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $targetURL['user']); - curl_setopt($curl, CURLOPT_USERAGENT, $curlAgent); - curl_setopt($curl, CURLOPT_HTTPHEADER, $curlContentType); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - - $result_curl = curl_exec($curl); - $userInfo = json_decode($result_curl, true); - curl_close($curl); - - if (!isset($userInfo['userPrincipalName'])) { - $result->status['msg'] = 'User ID is empty'; - $result->status['status'] = tl::ERROR; - } - - $result->options = new stdClass(); - $result->options->givenName = $userInfo['givenName']; - $result->options->familyName = $userInfo['surname']; - $result->options->user = $userInfo['userPrincipalName']; - $result->options->auth = 'oauth'; - - } else { - $result->status['msg'] = 'An error occurred during getting token'; - $result->status['status'] = tl::ERROR; - } - - return $result; +status = array( + 'status' => tl::OK, + 'msg' => null + ); + + // Params to get token + $oauthParams = array( + 'code' => $code, + 'client_id' => $authCfg['oauth_client_id'], + 'client_secret' => $authCfg['oauth_client_secret'], + 'scope' => $authCfg['oauth_scope'], + 'grant_type' => $authCfg['oauth_grant_type'] + ); + + $oauthParams['redirect_uri'] = $authCfg['redirect_uri']; + if (isset($_SERVER['HTTPS'])) { + $oauthParams['redirect_uri'] = str_replace('http://', 'https://', + $oauthParams['redirect_uri']); + } + + $curlAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:7.0.1) Gecko/20100101 Firefox/7.0.1'; + $curlContentType = array( + 'Content-Type: application/xml', + 'Accept: application/json' + ); + + // Step #1 - Get the token + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $authCfg['token_url']); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Accept: application/json' + )); + curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($oauthParams)); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_COOKIESESSION, true); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + $result_curl = curl_exec($curl); + + if ($result_curl === false) { + echo 'Curl error: ' . curl_error($curl); + echo '
    ';
    +        var_dump(curl_getinfo($curl));
    +        echo '
    '; + die(); + } + curl_close($curl); + $tokenInfo = json_decode($result_curl); + + // If token is received start session + if (isset($tokenInfo->access_token)) { + $oauthParams['access_token'] = $tokenInfo->access_token; + + $targetURL = array(); + $targetURL['user'] = $authCfg['oauth_profile']; + $curlContentType[] = 'Authorization: Bearer ' . + $oauthParams['access_token']; + + // Get User + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $targetURL['user']); + curl_setopt($curl, CURLOPT_USERAGENT, $curlAgent); + curl_setopt($curl, CURLOPT_HTTPHEADER, $curlContentType); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + + $result_curl = curl_exec($curl); + $userInfo = json_decode($result_curl, true); + curl_close($curl); + + if (! isset($userInfo['userPrincipalName'])) { + $result->status['msg'] = 'User ID is empty'; + $result->status['status'] = tl::ERROR; + } + + $result->options = new stdClass(); + $result->options->givenName = $userInfo['givenName']; + $result->options->familyName = $userInfo['surname']; + $result->options->user = $userInfo['userPrincipalName']; + $result->options->auth = 'oauth'; + } else { + $result->status['msg'] = 'An error occurred during getting token'; + $result->status['status'] = tl::ERROR; + } + + return $result; } diff --git a/lib/functions/oauth_providers/test.txt b/lib/functions/oauth_providers/test.txt new file mode 100644 index 0000000000..dc07e09f75 --- /dev/null +++ b/lib/functions/oauth_providers/test.txt @@ -0,0 +1,29 @@ +https://api.github.com/user +?access_token=cbc290aeeb7983dc113891b9380262634f9bcda8 +&token_type=bearer +&scope=user%3Aemail" + +https://api.github.com/user/emails +?access_token=cbc290aeeb7983dc113891b9380262634f9bcda8 +&token_type=bearer&scope=user%3Aemail" + + + +http://fman.hopto.org/login.php?oauth=github +&code=bce42ebb4213420894f6&state=0a677485080ae288dc750cea76135ea5 + +http://fman.hopto.org/login.php?oauth=github +&code=bce42ebb4213420894f6&state=0a677485080ae288dc750cea76135ea5 + + +https://github.com/login/oauth/authorize +?state=d824d74cda4983ec86e420d02f22a402 +&scope=&response_type=code +&approval_prompt=auto +&redirect_uri=http%3A%2F%2Ffman.hopto.org%2Flogin.php%3Foauth%3Dgithub +&client_id=aa5f70a8de342fb95043 + +https://github.com/login/oauth/authorize?state=015bd0b0bc8cb9b5abbcbc4983b4b831&scope=&response_type=code&approval_prompt=auto&redirect_uri=http%3A%2F%2Ffman.hopto.org%2Flogin.php%3Foauth%3Dgithub&client_id=aa5f70a8de342fb95043 + + +http://fman.hopto.org/login.php?redirect_uri=http%3A%2F%2Ffman.hopto.org%2Flogin.php%3Foauth%3Dgithub&prompt=none&response_type=code&client_id=aa5f70a8de342fb95043&state=github \ No newline at end of file diff --git a/lib/functions/object.class.php b/lib/functions/object.class.php index 6d298716b8..e84f9893e6 100644 --- a/lib/functions/object.class.php +++ b/lib/functions/object.class.php @@ -1,723 +1,782 @@ -= tl::OK, and for ERROR with < tl::OK - */ - const OK = 1; - - /** - * error and status codes: - * all ERROR error codes and ERROR status codes should be lesser than tl::ERROR - * so we can check for ERRORS with <= tl::ERROR, and for SUCCESS with > tl::ERROR - */ - const ERROR = 0; - - /** - * error and status codes: - * return code for not implemented interface functions - */ - const E_NOT_IMPLEMENTED = -0xFFFFFFFF; -}; - - -require_once('int_serialization.php'); - -/** - * Base class for all managed TestLink objects, all tl-managed objects should extend this base class - * - * @package TestLink - * @abstract - */ -abstract class tlObject implements iSerialization -{ - /** @var string the unique object id */ - protected $objectID; - - /** - * @var string a message for user feedback - */ - protected $userFeedback; - - /** - * @var array supported serialization interfaces - */ - protected $serializationInterfaces; - - /** - * @var array of format descriptors for the interfaces - */ - protected $serializationFormatDescriptors; - - /** - * @var array useful to manage DB where TL table names must have a prefix. - * key: table name WITHOUT prefix - * value: table name WITH PREFIX - * @see getDBTables() - */ - protected $tables = null; - - /** - * @var array useful to manage DB where TL view names must have a prefix. - * key: view name WITHOUT prefix - * value: view name WITH PREFIX - * @see getDBViews() - */ - protected $views = null; - - protected $auditCfg; - - - /** class constructor */ - public function __construct() - { - if (!isset($this->tables)) - { - $this->tables = self::getDBTables(); - $this->views = self::getDBViews(); - } - - $this->objectID = str_replace(".","",uniqid("", true)); - - $this->auditCfg = new stdClass(); - $this->auditCfg->eventSource = 'GUI'; - $this->auditCfg->logEnabled = true; - - /* - Any supported import/Export Serialization Interface must be prefixed with iSerializationTo - so we can automatically detected the interfaces - */ - $prefix = "iSerializationTo"; - $prefixLen = strlen($prefix); - $o = new ReflectionObject($this); - $interfaces = $o->getInterfaces(); - $this->serializationInterfaces = null; - $this->serializationFormatDescriptors = null; - if ($interfaces) - { - foreach($interfaces as $name => $info) - { - $iPos = strpos($name,$prefix); - if ($iPos === 0) - { - $format = substr($name,$prefixLen); - $this->serializationInterfaces[$name] = $format; - $pfn = "getFormatDescriptionFor".$format; - $this->serializationFormatDescriptors[$format] = $this->$pfn(); - } - } - } - $this->getSupportedSerializationFormatDescriptions(); - } - - /** - * - */ - function setAuditLogOn() - { - $this->auditCfg->logEnabled = true; - } - - /** - * - */ - function setAuditLogOff() - { - $this->auditCfg->logEnabled = false; - } - - /** - * - */ - function setAuditEventSource($val) - { - $this->auditCfg->eventSource = $val; - } - - /** - * get a message for user - */ - public function getUserFeedback() - { - return $this->userFeedback; - } - - /** - * Set a message for user - * @param string $message a localized message as user feedback - */ - public function setUserFeedback($message) - { - $this->userFeedback = $message; - } - - /** - * Getter for the unique objectID - * @return string the ID of the object - */ - public function getObjectID() - { - return $this->objectID; - } - - /** class destructor */ - public function __destruct() - { - $this->_clean(); - } - - - /** - * magic method for usage with print() or echo() , dumps out the object - * - * @return string a dump of the object - */ - public function __toString() - { - return __CLASS__.", ".print_r($this,true); - } - - /** function used for resetting the object's internal data */ - protected function _clean() - { - } - - /** - * Gets all serializationInterfaces the object supports - * - * @return all supported Import/Export Interfaces - **/ - function getSupportedSerializationInterfaces() - { - return $this->serializationInterfaces; - } - - /** - * @return all supported Import/Export Interfaces - Format Descriptors - **/ - function getSupportedSerializationFormatDescriptions() - { - return $this->serializationFormatDescriptors; - } - - /** - * should be called whenever a not implemented method is called - * - * @param string name of method - * @return integer error code "not implemented" - **/ - protected function handleNotImplementedMethod($fName = "") - { - trigger_error("Method ".$fName." called which is not implemented",E_USER_WARNING); - return tl::E_NOT_IMPLEMENTED; - } - - - /** - * useful to manage DB where TL table names must have a prefix. - * - * @param $tableNames array of tablenames, to get only some of the tables - * @return map key=table name without prefix, value=table name on db - * - */ - static public function getDBTables($tableNames = null) - { - $items = array( 'assignment_status', - 'assignment_types', - 'attachments', - 'baseline_l1l2_context', - 'baseline_l1l2_details', - 'builds', - 'cfield_build_design_values', - 'cfield_design_values', - 'cfield_execution_values', - 'cfield_node_types', - 'cfield_testplan_design_values', - 'cfield_testprojects', - 'custom_fields', - 'db_version', - 'events', - 'execution_bugs', - 'execution_tcsteps', - 'executions', - 'inventory', - 'issuetrackers', - 'testproject_issuetracker', - 'codetrackers', - 'testproject_codetracker', - 'keywords', - 'milestones', - 'node_types', - 'nodes_hierarchy', - 'object_keywords', - 'platforms', - 'plugins', - 'plugins_configuration', - 'req_coverage', - 'req_relations', - 'req_specs', - 'req_specs_revisions', - 'reqmgrsystems', - 'testproject_reqmgrsystem', - 'requirements', - 'req_versions', - 'req_revisions', - 'req_notify_assignments', - 'req_monitor', - 'rights', - 'risk_assignments', - 'role_rights', - 'roles', - 'testcase_relations', - 'tcversions', - 'tcsteps', - 'testcase_keywords', - 'testcase_platforms', - 'testplan_platforms' , - 'testcase_script_links', - 'testplan_tcversions' , - 'testplans' , - 'testprojects', - 'testsuites', - 'text_templates', - 'transactions', - 'user_assignments', - 'user_group', - 'user_group_assign', - 'user_testplan_roles', - 'user_testproject_roles', - 'users', - 'execution_tcsteps_wip' - ); - - $tables = array(); - foreach($items as $tblKey) { - $tables[$tblKey] = DB_TABLE_PREFIX . $tblKey; - } - - if ($tableNames != null) { - $tableNames = (array)$tableNames; - $tableNames = array_flip($tableNames); - $tables = array_intersect_key($tables,$tableNames); - if (sizeof($tables) != sizeof($tableNames)) { - throw new Exception("Wrong table name(s) for getDBTables() detected!"); - } - } - - return $tables; - } - - /** - * - */ - static public function getDBViews($itemNames = null) - { - $items = array('tcversions_last_active' => null, - 'tcversions_without_keywords' => null, - 'tcversions_without_platforms' => null, - 'latest_exec_by_context' => null, - 'latest_exec_by_testplan' => null, - 'latest_exec_by_testplan_plat' => null, - 'latest_tcase_version_id' => null, - 'latest_tcase_version_number' => null, - 'latest_req_version' => null, - 'latest_req_version_id' => null, - 'latest_rspec_revision' => null, - 'tsuites_tree_depth_2' => null, - 'exec_by_date_time' => null, - 'exec_daily_stats' => null); - - foreach($items as $key => $value) { - $items[$key] = DB_TABLE_PREFIX . $key; - } - - if ($itemNames != null) { - $itemNames = (array)$itemNames; - $itemNames = array_flip($itemNames); - $items = array_intersect_key($items,$itemNames); - if (sizeof($items) != sizeof($itemNames)) { - $msg = "Wrong view name(s) for " . __FUNCTION__ . " detected!"; - throw new Exception($msg); - } - } - - return $items; - } - -} - - -/** - * The base class for all managed TestLink objects which need a db connection - * - * @package TestLink - * @abstract - */ -abstract class tlObjectWithDB extends tlObject -{ - /** @var resource the db connection to the testlink database */ - protected $db; - - /** - * Class contructor - * @param object [ref] $db the database connection - */ - function __construct(&$db) - { - tlObject::__construct(); - $this->db = &$db; - } - - function setDB(&$db) - { - $this->db = &$db; - } - -} - -/** - * The base class for all managed TestLink objects which support attachments - * - * @package TestLink - * @abstract - */ -abstract class tlObjectWithAttachments extends tlObjectWithDB -{ - /** @var object the attachment repository object */ - protected $attachmentRepository; - - /** @var string the foreign key table name to store the attachements */ - protected $attachmentTableName; - - /** - * Class constructor - * - * @param object [ref] $db the database connection - * @param string $attachmentTableName the foreign key table name to store the attachments - */ - function __construct(&$db,$attachmentTableName) - { - tlObjectWithDB::__construct($db); - $this->attachmentRepository = tlAttachmentRepository::create($this->db); - $this->attachmentTableName = $attachmentTableName; - } - - /** - * gets all infos about the attachments of the object specified by $id - * - * @param integer $id this is the fkid of the attachments table - * @return array returns map with the infos of the attachment, - * keys are the column names of the attachments table - * - * @TODO schlundus: legacy function to keep existing code, should be replaced by a - * function which returns objects - */ - function getAttachmentInfos($id) - { - return $this->attachmentRepository->getAttachmentInfosFor($id,$this->attachmentTableName); - } - - /** - * deletes all attachments of the object specified by $id - * - * @param int $id this is the fkid of the attachments table - * @return returns tl::OK on success, else error code - */ - function deleteAttachments($id) - { - return $this->attachmentRepository->deleteAttachmentsFor($id,$this->attachmentTableName); - } - - /** - * function used for resetting the object's internal data - **/ - protected function _clean() - { - $this->attachmentRepository = null; - $this->attachmentTableName = null; - } - - function getAttachmentTableName() - { - return $this->attachmentTableName; - } - -} - - -/** - * class implement basic support for work with DB - * - * @package TestLink - * @abstract - */ -abstract class tlDBObject extends tlObject implements iDBSerialization -{ - /** - * @var array this is the static object cache for all tlDBObject. objects are stored like this - * [classname][detailLevel][databaseID] - */ - static protected $objectCache = null; - - /** - * @var boolean activate Caching or not, default is set to false, because it all brings performance to certain - * objects - */ - protected $activateCaching = false; - - /** - * @var integer the database id of the object - */ - public $dbID; - - /** - * @var int the detail level, used to configure how much information - * about the object is read from the database - */ - protected $detailLevel; - - /** standard get option, all other get options must be greater than this */ - const TLOBJ_O_SEARCH_BY_ID = 1; - - //standard detail levels, can be used to get only some specific details when reading an object - //to avoid unneccessary DB queries (when the info is actual not used and not needed) - const TLOBJ_O_GET_DETAIL_MINIMUM = 0; - - //get all information - const TLOBJ_O_GET_DETAIL_FULL = 0xFFFFFFFF; - - /** - * Class constructor - * - * @param integer $dbID (optional) the database identifier - */ - function __construct($dbID = null) - { - parent::__construct(); - - $this->dbID = $dbID; - $this->detailLevel = self::TLOBJ_O_GET_DETAIL_FULL; - } - - /** - * if we fetch an object, we can set here different details levels for the objects, because we - * don't always need all nested data - - * @param $level integer any combination of TLOBJ_O_GET_DETAIL_? constancts - */ - public function setDetailLevel($level = self::TLOBJ_O_GET_DETAIL_FULL) - { - $this->detailLevel = $level; - } - - /* some factory functions to be used to create tl managed objects */ - /** - * creates any tl-managed objects - * - * @param object [ref] $db the database connection - * @param int $id the id of the object to be created (must exist in the database) - * @param string $className the class name of the object - * @param int $options some additional options for creating the options (these are class specific) - * @param int $detailLevel the detail level of the object - * - * @return the newly created object on success, or null else - */ - static public function createObjectFromDB(&$db,$id,$className, - $options = self::TLOBJ_O_SEARCH_BY_ID, - $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) { - if ($id) { - $item = new $className($id); - $item->setDetailLevel($detailLevel); - if ($item->readFromDB($db,$options) >= tl::OK) { - return $item; - } - } - return null; - } - - /** - * used to create any tl-managed objects - * - * @param object [ref] $db the database connection - * @param string $query the ids of the objects to be created are obtained by this query - * @param string $column the name of the column which delivers the ids - * @param string $className the class name of the objects - * @param boolean $returnAsMap if set to true, to objects are returned in a - * map whose keys are the ids, else they are returned in a normal array. - - * @param int $detailLevel the detail level of the object - * - * @return the newly created objects on success, or null else - */ - static public function createObjectsFromDBbySQL(&$db,$query,$column,$className,$returnAsMap = false, - $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL,$limit = -1) - { - $ids = $db->fetchColumnsIntoArray($query,$column,$limit); - return self::createObjectsFromDB($db,$ids,$className,$returnAsMap,$detailLevel); - } - - /** - * used to create any tl-managed objects - * - * @param object [ref] $db the database connection - * @param array $ids the ids of the objects to be created - * @param string $className the class name of the objects - * @param boolean $returnAsMap if set to true, to objects are returned in a - * map whose keys are the ids, else they are returned in a normal array. - * @param integer $detailLevel the detail level of the object - * - * @return mixed the newly created objects on success, or null else - */ - static public function createObjectsFromDB(&$db,$ids,$className,$returnAsMap = false, - $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - $items = null; - - if (in_array("iDBBulkReadSerialization",class_implements($className))) - $items = self::bulkCreateObjectsFromDB($db,$ids,$className,$returnAsMap,$detailLevel); - else - { - for($i = 0;$i < sizeof($ids);$i++) - { - $id = $ids[$i]; - $item = self::createObjectFromDB($db,$id,$className,self::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - if ($item) - { - if ($returnAsMap) - { - $items[$id] = $item; - } - else - { - $items[] = $item; - } - } - } - } - return $items; - } - - /** - * used to bulk-create tl-managed objects which support the "iDBBulkReadSerialization"-Interface - * - * @param object [ref] $db the database connection - * @param array $ids the ids of the objects to be created - * @param string $className the class name of the objects - * @param boolean $returnAsMap if set to true, to objects are returned in a - * map whose keys are the ids, else they are returned in a normal array. - * @param integer $detailLevel the detail level of the object - * - * @return mixed the newly created objects on success, or null else - */ - static public function bulkCreateObjectsFromDB(&$db,$ids,$className,$returnAsMap = false, - $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - $items = null; - if (null != $ids && sizeof($ids)) { - $dummyItem = new $className(); - $query = $dummyItem->getReadFromDBQuery($ids,self::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - $result = $db->exec_query($query); - if ($result) { - while($row = $db->fetch_array($result)) { - $item = new $className(); - $item->readFromDBRow($row); - - if ($returnAsMap) { - $items[$item->dbID] = $item; - } else { - $items[] = $item; - } - } - } - } - return $items; - } - - /** - * deletes an tl-Managed object from the DB - * - * @param object [rerf] $db the database connection - * @param int $id the database-id of the object which should be deleted - * @param string $className the class name of the object - * - * @return integer result code - */ - static public function deleteObjectFromDB(&$db,$id,$className) { - if ($id) { - $item = new $className($id); - return $item->deleteFromDB($db); - } - return tl::ERROR; - } - - /** - * Adds the object to the cache if caching is activated - * - * @return integer returns always tl::OK - */ - protected function addToCache() { - if ($this->activateCaching) { - self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID] = $this; - } - return tl::OK; - } - - /** - * Remove the object from the cache - * - * @return integer returns always tl::OK - */ - protected function removeFromCache() { - if ($this->activateCaching) { - unset(self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID]); - } - return tl::OK; - } - - /** - * Dummy implementation, each cachable object needs only to implement this function - * The function must read the members (but not the internal ones) from object and copy it to itself - * - * @param $object the object to read from - * @return integer returns always tl::OK - */ - protected function copyFromCache($object) { - return tl::OK; - } - - /** - * @return integer returns tl::ERROR if caching is not activated or a cache miss happens - * else it returns the result of copyFromCache - */ - public function readFromCache() { - if (!$this->activateCaching) { - return tl::ERROR; - } - - if (isset(self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID])) { - $object = self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID]; - return $this->copyFromCache($object); - } - return tl::ERROR; - } += tl::OK, and for ERROR with < tl::OK + */ + const OK = 1; + + /** + * error and status codes: + * all ERROR error codes and ERROR status codes should be lesser than tl::ERROR + * so we can check for ERRORS with <= tl::ERROR, and for SUCCESS with > tl::ERROR + */ + const ERROR = 0; + + /** + * error and status codes: + * return code for not implemented interface functions + */ + const E_NOT_IMPLEMENTED = - 0xFFFFFFFF; +} + +require_once 'int_serialization.php'; + +/** + * Base class for all managed TestLink objects, all tl-managed objects should extend this base class + * + * @package TestLink + * @abstract + */ +abstract class tlObject implements iSerialization +{ + + /** @var string the unique object id */ + protected $objectID; + + /** + * + * @var string a message for user feedback + */ + protected $userFeedback; + + /** + * + * @var array supported serialization interfaces + */ + protected $serializationInterfaces; + + /** + * + * @var array of format descriptors for the interfaces + */ + protected $serializationFormatDescriptors; + + /** + * + * @var array useful to manage DB where TL table names must have a prefix. + * key: table name WITHOUT prefix + * value: table name WITH PREFIX + * @see getDBTables() + */ + protected $tables = null; + + /** + * + * @var array useful to manage DB where TL view names must have a prefix. + * key: view name WITHOUT prefix + * value: view name WITH PREFIX + * @see getDBViews() + */ + protected $views = null; + + protected $auditCfg; + + /** + * class constructor + */ + public function __construct() + { + if (! isset($this->tables)) { + $this->tables = self::getDBTables(); + $this->views = self::getDBViews(); + } + + $this->objectID = str_replace(".", "", uniqid("", true)); + + $this->auditCfg = new stdClass(); + $this->auditCfg->eventSource = 'GUI'; + $this->auditCfg->logEnabled = true; + + /* + * Any supported import/Export Serialization Interface must be prefixed with iSerializationTo + * so we can automatically detected the interfaces + */ + $prefix = "iSerializationTo"; + $prefixLen = strlen($prefix); + $o = new ReflectionObject($this); + $interfaces = $o->getInterfaces(); + $this->serializationInterfaces = null; + $this->serializationFormatDescriptors = null; + if ($interfaces) { + foreach ($interfaces as $name => $info) { + $iPos = strpos($name, $prefix); + if ($iPos === 0) { + $format = substr($name, $prefixLen); + $this->serializationInterfaces[$name] = $format; + $pfn = "getFormatDescriptionFor" . $format; + $this->serializationFormatDescriptors[$format] = $this->$pfn(); + } + } + } + $this->getSupportedSerializationFormatDescriptions(); + } + + /** + */ + public function setAuditLogOn() + { + $this->auditCfg->logEnabled = true; + } + + /** + */ + public function setAuditLogOff() + { + $this->auditCfg->logEnabled = false; + } + + /** + */ + public function setAuditEventSource($val) + { + $this->auditCfg->eventSource = $val; + } + + /** + * get a message for user + */ + public function getUserFeedback() + { + return $this->userFeedback; + } + + /** + * Set a message for user + * + * @param string $message + * a localized message as user feedback + */ + public function setUserFeedback($message) + { + $this->userFeedback = $message; + } + + /** + * Getter for the unique objectID + * + * @return string the ID of the object + */ + public function getObjectID() + { + return $this->objectID; + } + + /** + * class destructor + */ + public function __destruct() + { + $this->_clean(); + } + + /** + * magic method for usage with print() or echo() , dumps out the object + * + * @return string a dump of the object + */ + public function __toString() + { + return __CLASS__ . ", " . print_r($this, true); + } + + /** + * function used for resetting the object's internal data + */ + protected function _clean() + {} + + /** + * Gets all serializationInterfaces the object supports + * + * @return all supported Import/Export Interfaces + */ + public function getSupportedSerializationInterfaces() + { + return $this->serializationInterfaces; + } + + /** + * + * @return all supported Import/Export Interfaces - Format Descriptors + */ + public function getSupportedSerializationFormatDescriptions() + { + return $this->serializationFormatDescriptors; + } + + /** + * should be called whenever a not implemented method is called + * + * @param + * string name of method + * @return integer error code "not implemented" + */ + protected function handleNotImplementedMethod($fName = "") + { + trigger_error("Method " . $fName . " called which is not implemented", + E_USER_WARNING); + return tl::E_NOT_IMPLEMENTED; + } + + /** + * useful to manage DB where TL table names must have a prefix. + * + * @param $tableNames array + * of tablenames, to get only some of the tables + * @return map key=table name without prefix, value=table name on db + * + */ + public static function getDBTables($tableNames = null) + { + $items = array( + 'assignment_status', + 'assignment_types', + 'attachments', + 'baseline_l1l2_context', + 'baseline_l1l2_details', + 'builds', + 'cfield_build_design_values', + 'cfield_design_values', + 'cfield_execution_values', + 'cfield_node_types', + 'cfield_testplan_design_values', + 'cfield_testprojects', + 'custom_fields', + 'db_version', + 'events', + 'execution_bugs', + 'execution_tcsteps', + 'executions', + 'inventory', + 'issuetrackers', + 'testproject_issuetracker', + 'codetrackers', + 'testproject_codetracker', + 'keywords', + 'milestones', + 'node_types', + 'nodes_hierarchy', + 'object_keywords', + 'platforms', + 'plugins', + 'plugins_configuration', + 'req_coverage', + 'req_relations', + 'req_specs', + 'req_specs_revisions', + 'reqmgrsystems', + 'testproject_reqmgrsystem', + 'requirements', + 'req_versions', + 'req_revisions', + 'req_notify_assignments', + 'req_monitor', + 'rights', + 'risk_assignments', + 'role_rights', + 'roles', + 'testcase_relations', + 'tcversions', + 'tcsteps', + 'testcase_keywords', + 'testcase_platforms', + 'testplan_platforms', + 'testcase_script_links', + 'testplan_tcversions', + 'testplans', + 'testprojects', + 'testsuites', + 'text_templates', + 'transactions', + 'user_assignments', + 'user_group', + 'user_group_assign', + 'user_testplan_roles', + 'user_testproject_roles', + 'users', + 'execution_tcsteps_wip' + ); + + $tables = array(); + foreach ($items as $tblKey) { + $tables[$tblKey] = DB_TABLE_PREFIX . $tblKey; + } + + if ($tableNames != null) { + $tableNames = (array) $tableNames; + $tableNames = array_flip($tableNames); + $tables = array_intersect_key($tables, $tableNames); + if (count($tables) != count($tableNames)) { + throw new Exception( + "Wrong table name(s) for getDBTables() detected!"); + } + } + + return $tables; + } + + /** + */ + public static function getDBViews($itemNames = null) + { + $items = array( + 'tcversions_last_active' => null, + 'tcversions_without_keywords' => null, + 'tcversions_without_platforms' => null, + 'latest_exec_by_context' => null, + 'latest_exec_by_testplan' => null, + 'latest_exec_by_testplan_plat' => null, + 'latest_tcase_version_id' => null, + 'latest_tcase_version_number' => null, + 'latest_req_version' => null, + 'latest_req_version_id' => null, + 'latest_rspec_revision' => null, + 'tsuites_tree_depth_2' => null, + 'exec_by_date_time' => null, + 'exec_daily_stats' => null + ); + + foreach ($items as $key => $value) { + $items[$key] = DB_TABLE_PREFIX . $key; + } + + if ($itemNames != null) { + $itemNames = (array) $itemNames; + $itemNames = array_flip($itemNames); + $items = array_intersect_key($items, $itemNames); + if (count($items) != count($itemNames)) { + $msg = "Wrong view name(s) for " . __FUNCTION__ . " detected!"; + throw new Exception($msg); + } + } + + return $items; + } +} + +/** + * The base class for all managed TestLink objects which need a db connection + * + * @package TestLink + * @abstract + */ +abstract class tlObjectWithDB extends tlObject +{ + + /** @var resource the db connection to the testlink database */ + protected $db; + + /** + * Class contructor + * + * @param + * object [ref] $db the database connection + */ + public function __construct(&$db) + { + tlObject::__construct(); + $this->db = &$db; + } + + public function setDB(&$db) + { + $this->db = &$db; + } +} + +/** + * The base class for all managed TestLink objects which support attachments + * + * @package TestLink + * @abstract + */ +abstract class tlObjectWithAttachments extends tlObjectWithDB +{ + + /** @var object the attachment repository object */ + protected $attachmentRepository; + + /** @var string the foreign key table name to store the attachements */ + protected $attachmentTableName; + + /** + * Class constructor + * + * @param + * object [ref] $db the database connection + * @param string $attachmentTableName + * the foreign key table name to store the attachments + */ + public function __construct(&$db, $attachmentTableName) + { + tlObjectWithDB::__construct($db); + $this->attachmentRepository = tlAttachmentRepository::create($this->db); + $this->attachmentTableName = $attachmentTableName; + } + + /** + * gets all infos about the attachments of the object specified by $id + * + * @param integer $id + * this is the fkid of the attachments table + * @return array returns map with the infos of the attachment, + * keys are the column names of the attachments table + * + * @todo schlundus: legacy function to keep existing code, should be replaced by a + * function which returns objects + */ + public function getAttachmentInfos($id) + { + return $this->attachmentRepository->getAttachmentInfosFor($id, + $this->attachmentTableName); + } + + /** + * deletes all attachments of the object specified by $id + * + * @param int $id + * this is the fkid of the attachments table + * @return returns tl::OK on success, else error code + */ + public function deleteAttachments($id) + { + return $this->attachmentRepository->deleteAttachmentsFor($id, + $this->attachmentTableName); + } + + /** + * function used for resetting the object's internal data + */ + protected function _clean() + { + $this->attachmentRepository = null; + $this->attachmentTableName = null; + } + + public function getAttachmentTableName() + { + return $this->attachmentTableName; + } +} + +/** + * class implement basic support for work with DB + * + * @package TestLink + * @abstract + */ +abstract class tlDBObject extends tlObject implements iDBSerialization +{ + + /** + * + * @var array this is the static object cache for all tlDBObject. objects are stored like this + * [classname][detailLevel][databaseID] + */ + protected static $objectCache = null; + + /** + * + * @var boolean activate Caching or not, default is set to false, because it all brings performance to certain + * objects + */ + protected $activateCaching = false; + + /** + * + * @var integer the database id of the object + */ + public $dbID; + + /** + * + * @var int the detail level, used to configure how much information + * about the object is read from the database + */ + protected $detailLevel; + + /** + * standard get option, all other get options must be greater than this + */ + const TLOBJ_O_SEARCH_BY_ID = 1; + + // standard detail levels, can be used to get only some specific details when reading an object + // to avoid unneccessary DB queries (when the info is actual not used and not needed) + const TLOBJ_O_GET_DETAIL_MINIMUM = 0; + + // get all information + const TLOBJ_O_GET_DETAIL_FULL = 0xFFFFFFFF; + + /** + * Class constructor + * + * @param integer $dbID + * (optional) the database identifier + */ + public function __construct($dbID = null) + { + parent::__construct(); + + $this->dbID = $dbID; + $this->detailLevel = self::TLOBJ_O_GET_DETAIL_FULL; + } + + /** + * if we fetch an object, we can set here different details levels for the objects, because we + * don't always need all nested data + * + * @param $level integer + * any combination of TLOBJ_O_GET_DETAIL_? constancts + */ + public function setDetailLevel($level = self::TLOBJ_O_GET_DETAIL_FULL) + { + $this->detailLevel = $level; + } + + /* some factory functions to be used to create tl managed objects */ + /** + * creates any tl-managed objects + * + * @param + * object [ref] $db the database connection + * @param int $id + * the id of the object to be created (must exist in the database) + * @param string $className + * the class name of the object + * @param int $options + * some additional options for creating the options (these are class specific) + * @param int $detailLevel + * the detail level of the object + * + * @return the newly created object on success, or null else + */ + public static function createObjectFromDB(&$db, $id, $className, + $options = self::TLOBJ_O_SEARCH_BY_ID, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + if ($id) { + $item = new $className($id); + $item->setDetailLevel($detailLevel); + if ($item->readFromDB($db, $options) >= tl::OK) { + return $item; + } + } + return null; + } + + /** + * used to create any tl-managed objects + * + * @param + * object [ref] $db the database connection + * @param string $query + * the ids of the objects to be created are obtained by this query + * @param string $column + * the name of the column which delivers the ids + * @param string $className + * the class name of the objects + * @param boolean $returnAsMap + * if set to true, to objects are returned in a + * map whose keys are the ids, else they are returned in a normal array. + * + * @param int $detailLevel + * the detail level of the object + * + * @return the newly created objects on success, or null else + */ + public static function createObjectsFromDBbySQL(&$db, $query, $column, + $className, $returnAsMap = false, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL, $limit = - 1) + { + $ids = $db->fetchColumnsIntoArray($query, $column, $limit); + return self::createObjectsFromDB($db, $ids, $className, $returnAsMap, + $detailLevel); + } + + /** + * used to create any tl-managed objects + * + * @param + * object [ref] $db the database connection + * @param array $ids + * the ids of the objects to be created + * @param string $className + * the class name of the objects + * @param boolean $returnAsMap + * if set to true, to objects are returned in a + * map whose keys are the ids, else they are returned in a normal array. + * @param integer $detailLevel + * the detail level of the object + * + * @return mixed the newly created objects on success, or null else + */ + public static function createObjectsFromDB(&$db, $ids, $className, + $returnAsMap = false, $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + $items = null; + + if (in_array("iDBBulkReadSerialization", class_implements($className))) { + $items = self::bulkCreateObjectsFromDB($db, $ids, $className, + $returnAsMap, $detailLevel); + } else { + for ($i = 0; $i < count((array) $ids); $i ++) { + $id = $ids[$i]; + $item = self::createObjectFromDB($db, $id, $className, + self::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + if ($item) { + if ($returnAsMap) { + $items[$id] = $item; + } else { + $items[] = $item; + } + } + } + } + return $items; + } + + /** + * used to bulk-create tl-managed objects which support the "iDBBulkReadSerialization"-Interface + * + * @param + * object [ref] $db the database connection + * @param array $ids + * the ids of the objects to be created + * @param string $className + * the class name of the objects + * @param boolean $returnAsMap + * if set to true, to objects are returned in a + * map whose keys are the ids, else they are returned in a normal array. + * @param integer $detailLevel + * the detail level of the object + * + * @return mixed the newly created objects on success, or null else + */ + public static function bulkCreateObjectsFromDB(&$db, $ids, $className, + $returnAsMap = false, $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + $items = null; + if (null != $ids && count($ids)) { + $dummyItem = new $className(); + $query = $dummyItem->getReadFromDBQuery($ids, + self::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + $result = $db->exec_query($query); + if ($result) { + while ($row = $db->fetch_array($result)) { + $item = new $className(); + $item->readFromDBRow($row); + + if ($returnAsMap) { + $items[$item->dbID] = $item; + } else { + $items[] = $item; + } + } + } + } + return $items; + } + + /** + * deletes an tl-Managed object from the DB + * + * @param + * object [rerf] $db the database connection + * @param int $id + * the database-id of the object which should be deleted + * @param string $className + * the class name of the object + * + * @return integer result code + */ + public static function deleteObjectFromDB(&$db, $id, $className) + { + if ($id) { + $item = new $className($id); + return $item->deleteFromDB($db); + } + return tl::ERROR; + } + + /** + * Adds the object to the cache if caching is activated + * + * @return integer returns always tl::OK + */ + protected function addToCache() + { + if ($this->activateCaching) { + self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID] = $this; + } + return tl::OK; + } + + /** + * Remove the object from the cache + * + * @return integer returns always tl::OK + */ + protected function removeFromCache() + { + if ($this->activateCaching) { + unset( + self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID]); + } + return tl::OK; + } + + /** + * Dummy implementation, each cachable object needs only to implement this function + * The function must read the members (but not the internal ones) from object and copy it to itself + * + * @param $object the + * object to read from + * @return integer returns always tl::OK + */ + protected function copyFromCache($object) + { + return tl::OK; + } + + /** + * + * @return integer returns tl::ERROR if caching is not activated or a cache miss happens + * else it returns the result of copyFromCache + */ + public function readFromCache() + { + if (! $this->activateCaching) { + return tl::ERROR; + } + + if (isset( + self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID])) { + $object = self::$objectCache[get_class($this)][$this->detailLevel][$this->dbID]; + return $this->copyFromCache($object); + } + return tl::ERROR; + } } diff --git a/lib/functions/opt_transfer.php b/lib/functions/opt_transfer.php index af71da4af3..72a60aa629 100644 --- a/lib/functions/opt_transfer.php +++ b/lib/functions/opt_transfer.php @@ -1,172 +1,159 @@ -js_events->all_right_click="window.setTimeout('$js_ot_name.transferAllRight()',20);"; - $opt_cfg->js_events->left2right_click="window.setTimeout('$js_ot_name.transferRight()',20);"; - $opt_cfg->js_events->right2left_click="window.setTimeout('$js_ot_name.transferLeft()',20);"; - $opt_cfg->js_events->all_left_click="window.setTimeout('$js_ot_name.transferAllLeft()',20);"; - - - $a_right = array(); - $a_left = array(); - - if(trim($right_list) == "") - { - if(!is_null($opt_cfg->to->map)) - { - $a_right = $opt_cfg->to->map; - } - } - else - { - $a_k = explode(",",trim($right_list)); - foreach($a_k as $key => $code) - { - $a_right[$code] = $opt_cfg->from->map[$code]; - } - } - - if(!is_null($opt_cfg->from->map)) - { - $a_left = array_diff_assoc($opt_cfg->from->map,$a_right); - } - - $opt_cfg->from->map = $a_left; - $opt_cfg->to->map = $a_right; -} - - -function keywords_opt_transf_cfg(&$opt_cfg, $right_list) -{ - $opt_cfg->size = 8; - $opt_cfg->style = "width: 98%;"; - - $opt_cfg->js_events = new stdClass(); - $opt_cfg->js_events->all_right_click = ""; - $opt_cfg->js_events->left2right_click = ""; - $opt_cfg->js_events->right2left_click = ""; - $opt_cfg->js_events->all_left_click = ""; - - if( is_null($opt_cfg->from)) - { - $opt_cfg->from = new stdClass(); - } - $opt_cfg->from->name = "from_select_box"; - $opt_cfg->from->id_field = 'id'; - $opt_cfg->from->desc_field = 'keyword'; - $opt_cfg->from->desc_glue = " "; - $opt_cfg->from->desc_html_content = true; - $opt_cfg->from->required = false; - $opt_cfg->from->show_id_in_desc = true; - $opt_cfg->from->js_events->ondblclick = ""; - - if( is_null($opt_cfg->to)) - { - $opt_cfg->to = new stdClass(); - } - $opt_cfg->to->name = "to_select_box"; - $opt_cfg->to->show_id_in_desc = true; - $opt_cfg->to->id_field = 'id'; - $opt_cfg->to->desc_field = 'keyword'; - $opt_cfg->to->desc_glue = " "; - $opt_cfg->to->desc_html_content = true; - $opt_cfg->to->required = false; - $opt_cfg->to->show_id_in_desc = true; - $opt_cfg->to->js_events->ondblclick = ""; - - opt_transf_cfg($opt_cfg, $right_list,$opt_cfg->js_ot_name); -} - -function opt_transf_empty_cfg() -{ - $opt_cfg = new stdClass(); - $opt_cfg->js_ot_name = ""; - $opt_cfg->size = 8; - $opt_cfg->style = "width: 300px;"; - - $opt_cfg->js_events = new stdClass(); - $opt_cfg->js_events->all_right_click = ""; - $opt_cfg->js_events->left2right_click = ""; - $opt_cfg->js_events->right2left_click = ""; - $opt_cfg->js_events->all_left_click = ""; - - $opt_cfg->global_lbl = 'Option Transfer'; - $opt_cfg->from = new stdClass(); - $opt_cfg->from->lbl = 'from'; - $opt_cfg->from->name = "from_select_box"; - $opt_cfg->from->map = array(); - - $opt_cfg->from->id_field = ''; - $opt_cfg->from->desc_field = ''; - $opt_cfg->from->desc_glue = " "; - $opt_cfg->from->desc_html_content = true; - $opt_cfg->from->required = false; - $opt_cfg->from->show_id_in_desc = true; - $opt_cfg->from->js_events = new stdClass; - $opt_cfg->from->js_events->ondblclick = ""; - - $opt_cfg->to = new stdClass(); - $opt_cfg->to->lbl = 'to'; - $opt_cfg->to->name = "to_select_box"; - $opt_cfg->to->map = array(); - $opt_cfg->to->show_id_in_desc = true; - $opt_cfg->to->id_field = ''; - $opt_cfg->to->desc_field = ''; - $opt_cfg->to->desc_glue = " "; - $opt_cfg->to->desc_html_content = true; - $opt_cfg->to->required = false; - $opt_cfg->to->show_id_in_desc = true; - $opt_cfg->to->js_events = new stdClass(); - $opt_cfg->to->js_events->ondblclick = ""; - - return $opt_cfg; -} - -/** - * - * - */ -function item_opt_transf_cfg(&$opt_cfg, $right_list) -{ - $opt_cfg->size = 8; - $opt_cfg->style = "width: 98%;"; - - $opt_cfg->js_events->all_right_click = ""; - $opt_cfg->js_events->left2right_click = ""; - $opt_cfg->js_events->right2left_click = ""; - $opt_cfg->js_events->all_left_click = ""; - $opt_cfg->from->name = "from_select_box"; - - $opt_cfg->from->id_field = 'id'; - // $opt_cfg->from->desc_field = 'keyword'; - $opt_cfg->from->desc_glue = " "; - $opt_cfg->from->desc_html_content = true; - $opt_cfg->from->required = false; - $opt_cfg->from->show_id_in_desc = true; - $opt_cfg->from->js_events->ondblclick = ""; - - $opt_cfg->to->name = "to_select_box"; - $opt_cfg->to->show_id_in_desc = true; - $opt_cfg->to->id_field = 'id'; - //$opt_cfg->to->desc_field = 'keyword'; - $opt_cfg->to->desc_glue = " "; - $opt_cfg->to->desc_html_content = true; - $opt_cfg->to->required = false; - $opt_cfg->to->show_id_in_desc = true; - $opt_cfg->to->js_events->ondblclick = ""; - - opt_transf_cfg($opt_cfg, $right_list,$opt_cfg->js_ot_name); +js_events->all_right_click = "window.setTimeout('$js_ot_name.transferAllRight()',20);"; + $opt_cfg->js_events->left2right_click = "window.setTimeout('$js_ot_name.transferRight()',20);"; + $opt_cfg->js_events->right2left_click = "window.setTimeout('$js_ot_name.transferLeft()',20);"; + $opt_cfg->js_events->all_left_click = "window.setTimeout('$js_ot_name.transferAllLeft()',20);"; + + $a_right = array(); + $a_left = array(); + + if (trim($right_list) == "") { + if (! is_null($opt_cfg->to->map)) { + $a_right = $opt_cfg->to->map; + } + } else { + $a_k = explode(",", trim($right_list)); + foreach ($a_k as $code) { + $a_right[$code] = $opt_cfg->from->map[$code]; + } + } + + if (! is_null($opt_cfg->from->map)) { + $a_left = array_diff_assoc($opt_cfg->from->map, $a_right); + } + + $opt_cfg->from->map = $a_left; + $opt_cfg->to->map = $a_right; +} + +function keywords_opt_transf_cfg(&$opt_cfg, $right_list) +{ + $opt_cfg->size = 8; + $opt_cfg->style = "width: 98%;"; + + $opt_cfg->js_events = new stdClass(); + $opt_cfg->js_events->all_right_click = ""; + $opt_cfg->js_events->left2right_click = ""; + $opt_cfg->js_events->right2left_click = ""; + $opt_cfg->js_events->all_left_click = ""; + + if (is_null($opt_cfg->from)) { + $opt_cfg->from = new stdClass(); + } + $opt_cfg->from->name = "from_select_box"; + $opt_cfg->from->id_field = 'id'; + $opt_cfg->from->desc_field = 'keyword'; + $opt_cfg->from->desc_glue = " "; + $opt_cfg->from->desc_html_content = true; + $opt_cfg->from->required = false; + $opt_cfg->from->show_id_in_desc = true; + $opt_cfg->from->js_events->ondblclick = ""; + + if (is_null($opt_cfg->to)) { + $opt_cfg->to = new stdClass(); + } + $opt_cfg->to->name = "to_select_box"; + $opt_cfg->to->show_id_in_desc = true; + $opt_cfg->to->id_field = 'id'; + $opt_cfg->to->desc_field = 'keyword'; + $opt_cfg->to->desc_glue = " "; + $opt_cfg->to->desc_html_content = true; + $opt_cfg->to->required = false; + $opt_cfg->to->show_id_in_desc = true; + $opt_cfg->to->js_events->ondblclick = ""; + + opt_transf_cfg($opt_cfg, $right_list, $opt_cfg->js_ot_name); +} + +function opt_transf_empty_cfg() +{ + $opt_cfg = new stdClass(); + $opt_cfg->js_ot_name = ""; + $opt_cfg->size = 8; + $opt_cfg->style = "width: 300px;"; + + $opt_cfg->js_events = new stdClass(); + $opt_cfg->js_events->all_right_click = ""; + $opt_cfg->js_events->left2right_click = ""; + $opt_cfg->js_events->right2left_click = ""; + $opt_cfg->js_events->all_left_click = ""; + + $opt_cfg->global_lbl = 'Option Transfer'; + $opt_cfg->from = new stdClass(); + $opt_cfg->from->lbl = 'from'; + $opt_cfg->from->name = "from_select_box"; + $opt_cfg->from->map = array(); + + $opt_cfg->from->id_field = ''; + $opt_cfg->from->desc_field = ''; + $opt_cfg->from->desc_glue = " "; + $opt_cfg->from->desc_html_content = true; + $opt_cfg->from->required = false; + $opt_cfg->from->show_id_in_desc = true; + $opt_cfg->from->js_events = new stdClass(); + $opt_cfg->from->js_events->ondblclick = ""; + + $opt_cfg->to = new stdClass(); + $opt_cfg->to->lbl = 'to'; + $opt_cfg->to->name = "to_select_box"; + $opt_cfg->to->map = array(); + $opt_cfg->to->show_id_in_desc = true; + $opt_cfg->to->id_field = ''; + $opt_cfg->to->desc_field = ''; + $opt_cfg->to->desc_glue = " "; + $opt_cfg->to->desc_html_content = true; + $opt_cfg->to->required = false; + $opt_cfg->to->show_id_in_desc = true; + $opt_cfg->to->js_events = new stdClass(); + $opt_cfg->to->js_events->ondblclick = ""; + + return $opt_cfg; +} + +/** + */ +function item_opt_transf_cfg(&$opt_cfg, $right_list) +{ + $opt_cfg->size = 8; + $opt_cfg->style = "width: 98%;"; + + $opt_cfg->js_events->all_right_click = ""; + $opt_cfg->js_events->left2right_click = ""; + $opt_cfg->js_events->right2left_click = ""; + $opt_cfg->js_events->all_left_click = ""; + $opt_cfg->from->name = "from_select_box"; + + $opt_cfg->from->id_field = 'id'; + $opt_cfg->from->desc_glue = " "; + $opt_cfg->from->desc_html_content = true; + $opt_cfg->from->required = false; + $opt_cfg->from->show_id_in_desc = true; + $opt_cfg->from->js_events->ondblclick = ""; + + $opt_cfg->to->name = "to_select_box"; + $opt_cfg->to->show_id_in_desc = true; + $opt_cfg->to->id_field = 'id'; + $opt_cfg->to->desc_glue = " "; + $opt_cfg->to->desc_html_content = true; + $opt_cfg->to->required = false; + $opt_cfg->to->show_id_in_desc = true; + $opt_cfg->to->js_events->ondblclick = ""; + + opt_transf_cfg($opt_cfg, $right_list, $opt_cfg->js_ot_name); } diff --git a/lib/functions/pagestatistics.class.php b/lib/functions/pagestatistics.class.php index 352e2a130b..bbd3b9f0ba 100644 --- a/lib/functions/pagestatistics.class.php +++ b/lib/functions/pagestatistics.class.php @@ -1,237 +1,247 @@ -initialize(); - } - - /** - * initializes the page statistics, by starting the "OVERALL" Counter - */ - protected function initialize() - { - $this->startPerformanceCounter("OVERALL",tlPerformanceCounter::TYPE_ALL); - } - - /** - * starts a new performance counter with the given title and type - * - * @param string $title the title of the performance counter - * @param integer $type the type of the performance Counter, any combination of - * tlPerformanceCounter::TYPE_ Flags - */ - public function startPerformanceCounter($title,$type) - { - $this->performanceCounters[$title] = new tlPerformanceCounter($this->db,$type); - } - - /** - * Class destructor, echoes the contents of the counter - */ - public function __destruct() - { - echo (string) $this; - } - - /** - * Magic function called by php whenever a tlPageStatistics should be used as string - * - * @return string returns a string representation of the counter - */ - public function __toString() - { - $output = "
    "; - $output .= "Performance counters: \n
    "; - foreach($this->performanceCounters as $title => $counter) - { - $output .= "{$title}\n
    "; - $output .= "    {$counter}"; - } - $output .= "
    "; - - return $output; - - } -} - - -/** - * @package TestLink - * @author Andreas Morsing - * @since 1.9 - Jun, 2009 - */ -class tlPerformanceCounter extends tlObjectWithDB -{ - const TYPE_MEMORY = 1; - const TYPE_TIME = 2; - const TYPE_SQL = 4; - const TYPE_ALL = 0xFFFF; - - private $counterType = self::TYPE_ALL; - private $memoryPeak = 0; - private $memoryStart = 0; - private $memoryEnd = 0; - private $echoOnDestruct = false; - private $initialStart = 0; - private $duration = 0; - private $initialQueries = 0; - private $initialOverall = 0; - private $sqlQueries = 0; - private $sqlOverall = 0; - - function __construct(&$db,$type,$echoOnDestruct = false) - { - parent::__construct($db); - $this->counterType = $type; - $this->reset(); - $this->echoOnDestruct = $echoOnDestruct; - } - public function __destruct() - { - if ($this->echoOnDestruct) - { - $this->stop(); - echo $this; - } - } - - public function __toString() - { - $output = null; - if ($this->counterType & self::TYPE_MEMORY) - { - $this->updateMemory(); - $output .= "MEMORY: {$this->memoryStart} to {$this->memoryEnd} (max. Peak {$this->memoryPeak});\n"; - } - if ($this->counterType & self::TYPE_TIME) - { - $duration = $this->getDuration(); - $output .= "DURATION: {$duration} secs;\n"; - } - if ($this->counterType & self::TYPE_SQL) - { - $this->updateSQL(); - $output .= "SQL queries: ".($this->sqlQueries).";\n"; - $output .= "took ".$this->sqlOverall." secs;\n"; - } - return $output; - } - - public function getDuration() - { - $current = $this->getmicrotime(); - return ($current - $this->initialStart); - } - - public function reset() - { - $this->resetTimer(); - $this->resetMemory(); - $this->resetSQL(); - } - - public function resetTimer() - { - if ($this->counterType & self::TYPE_TIME) - { - $this->initialStart = $this->getmicrotime(); - $this->duration = 0; - } - } - - public function resetMemory() - { - if ($this->counterType & self::TYPE_MEMORY) - { - $this->memoryStart = memory_get_usage(true); - $this->memoryEnd = 0; - $this->memoryPeak = memory_get_peak_usage(true); - } - } - - public function resetSQL() - { - if ($this->counterType & self::TYPE_SQL) - { - $this->initialOverall = $this->db->overallDuration; - $this->initialQueries = $this->db->nQuery; - } - } - - public function stop() - { - $this->stopTimer(); - $this->updateMemory(); - $this->updateSQL(); - } - - protected function updateMemory() - { - if ($this->counterType & self::TYPE_MEMORY) - { - $this->memoryEnd = memory_get_usage(true); - $this->memoryPeak = memory_get_peak_usage(true); - } - } - - protected function updateSQL() - { - if ($this->counterType & self::TYPE_SQL) - { - $this->sqlOverall = $this->db->overallDuration - $this->initialOverall; - $this->sqlQueries = $this->db->nQuery - $this->initialQueries; - } - } - - public function stopTimer() - { - if ($this->counterType & self::TYPE_TIME) - { - $current = $this->getmicrotime(); - $this->duration = ($current - $this->initialStart); - } - } - - protected function getmicrotime() - { - $t = microtime(); - $t = explode(' ',$t); - return (float)$t[1]+ (float)$t[0]; - } -} -?> \ No newline at end of file +initialize(); + } + + /** + * initializes the page statistics, by starting the "OVERALL" Counter + */ + protected function initialize() + { + $this->startPerformanceCounter("OVERALL", tlPerformanceCounter::TYPE_ALL); + } + + /** + * starts a new performance counter with the given title and type + * + * @param string $title + * the title of the performance counter + * @param integer $type + * the type of the performance Counter, any combination of + * tlPerformanceCounter::TYPE_ Flags + */ + public function startPerformanceCounter($title, $type) + { + $this->performanceCounters[$title] = new tlPerformanceCounter($this->db, + $type); + } + + /** + * Class destructor, echoes the contents of the counter + */ + public function __destruct() + { + echo (string) $this; + } + + /** + * Magic function called by php whenever a tlPageStatistics should be used as string + * + * @return string returns a string representation of the counter + */ + public function __toString() + { + $output = "
    "; + $output .= "Performance counters: \n
    "; + foreach ($this->performanceCounters as $title => $counter) { + $output .= "{$title}\n
    "; + $output .= "    {$counter}"; + } + $output .= "
    "; + + return $output; + } +} + +/** + * + * @package TestLink + * @author Andreas Morsing + * @since 1.9 - Jun, 2009 + */ +class tlPerformanceCounter extends tlObjectWithDB +{ + + const TYPE_MEMORY = 1; + + const TYPE_TIME = 2; + + const TYPE_SQL = 4; + + const TYPE_ALL = 0xFFFF; + + private $counterType = self::TYPE_ALL; + + private $memoryPeak = 0; + + private $memoryStart = 0; + + private $memoryEnd = 0; + + private $echoOnDestruct = false; + + private $initialStart = 0; + + private $duration = 0; + + private $initialQueries = 0; + + private $initialOverall = 0; + + private $sqlQueries = 0; + + private $sqlOverall = 0; + + private function __construct(&$db, $type, $echoOnDestruct = false) + { + parent::__construct($db); + $this->counterType = $type; + $this->reset(); + $this->echoOnDestruct = $echoOnDestruct; + } + + public function __destruct() + { + if ($this->echoOnDestruct) { + $this->stop(); + echo $this; + } + } + + public function __toString() + { + $output = null; + if ($this->counterType & self::TYPE_MEMORY) { + $this->updateMemory(); + $output .= "MEMORY: {$this->memoryStart} to {$this->memoryEnd} (max. Peak {$this->memoryPeak});\n"; + } + if ($this->counterType & self::TYPE_TIME) { + $duration = $this->getDuration(); + $output .= "DURATION: {$duration} secs;\n"; + } + if ($this->counterType & self::TYPE_SQL) { + $this->updateSQL(); + $output .= "SQL queries: " . ($this->sqlQueries) . ";\n"; + $output .= "took " . $this->sqlOverall . " secs;\n"; + } + return $output; + } + + public function getDuration() + { + $current = $this->getmicrotime(); + return $current - $this->initialStart; + } + + public function reset() + { + $this->resetTimer(); + $this->resetMemory(); + $this->resetSQL(); + } + + public function resetTimer() + { + if ($this->counterType & self::TYPE_TIME) { + $this->initialStart = $this->getmicrotime(); + $this->duration = 0; + } + } + + public function resetMemory() + { + if ($this->counterType & self::TYPE_MEMORY) { + $this->memoryStart = memory_get_usage(true); + $this->memoryEnd = 0; + $this->memoryPeak = memory_get_peak_usage(true); + } + } + + public function resetSQL() + { + if ($this->counterType & self::TYPE_SQL) { + $this->initialOverall = $this->db->overallDuration; + $this->initialQueries = $this->db->nQuery; + } + } + + public function stop() + { + $this->stopTimer(); + $this->updateMemory(); + $this->updateSQL(); + } + + protected function updateMemory() + { + if ($this->counterType & self::TYPE_MEMORY) { + $this->memoryEnd = memory_get_usage(true); + $this->memoryPeak = memory_get_peak_usage(true); + } + } + + protected function updateSQL() + { + if ($this->counterType & self::TYPE_SQL) { + $this->sqlOverall = $this->db->overallDuration - + $this->initialOverall; + $this->sqlQueries = $this->db->nQuery - $this->initialQueries; + } + } + + public function stopTimer() + { + if ($this->counterType & self::TYPE_TIME) { + $current = $this->getmicrotime(); + $this->duration = ($current - $this->initialStart); + } + } + + protected function getmicrotime() + { + $t = microtime(); + $t = explode(' ', $t); + return (float) $t[1] + (float) $t[0]; + } +} +?> diff --git a/lib/functions/plugin_api.php b/lib/functions/plugin_api.php index fe66616126..6d21c7c812 100644 --- a/lib/functions/plugin_api.php +++ b/lib/functions/plugin_api.php @@ -1,625 +1,637 @@ -prepare_string($full_option); - - $sql = "/* $debugMsg */ " . - " SELECT config_value FROM " . $tables['plugins_configuration'] . - " where config_key = '" . $full_option . "' AND testproject_id = "; - - $value = $dbHandler->fetchOneValue($sql . intval($project)); - - if (is_null($value) && $project != TL_ANY_PROJECT) - { - // Check if its in the Global Project - $value = $dbHandler->fetchOneValue($sql . TL_ANY_PROJECT); - } - - if (is_null($value)) - { - // Fetch from the Global list, and if not, fetch from default value - global $g_plugin_config_cache; - $value = array_key_exists($full_option, $g_plugin_config_cache) ? $g_plugin_config_cache[$full_option] : $default; - } - return $value; -} - -/** - * Set a plugin configuration option in the database. - * @param string Configuration option name - * @param multi Option value - * @param int User ID - * @param int Project ID - * @param int Access threshold - */ -function plugin_config_set($option, $value, $project = TL_ANY_PROJECT) -{ - doDBConnect($dbHandler); - $tables = tlObjectWithDB::getDBTables(array('plugins_configuration')); - $plugin_config_table = $tables['plugins_configuration']; - - $basename = plugin_get_current(); - $full_option = 'plugin_' . $basename . '_' . $option; - - if (is_array($value) || is_object($value)) - { - $config_type = CONFIG_TYPE_COMPLEX; - $value = serialize($value); - } - else if (is_float($value)) - { - $config_type = CONFIG_TYPE_FLOAT; - $value = (float)$value; - } - else if (is_int($value) || is_numeric($value)) - { - $config_type = CONFIG_TYPE_INT; - $value = $dbHandler->prepare_int($value); - } - else - { - $config_type = CONFIG_TYPE_STRING; - } - - - $safe_id = intval($project); - $sql = " SELECT COUNT(*) from $plugin_config_table " . - " WHERE config_key = '" . $dbHandler->prepare_string($full_option) . "' " . - " AND testproject_id = {$safe_id} "; - $rows_exist = $dbHandler->fetchOneValue($sql); - - if ($rows_exist > 0) - { - // Update the existing record - $sql = " UPDATE $plugin_config_table " . - " SET config_value = '" . $dbHandler->prepare_string($value) . "'," . - " config_type = " . $config_type . - " WHERE config_key = '" . $dbHandler->prepare_string($full_option) . "' " . - " AND testproject_id = {$safe_id} "; - } - else - { - // Insert new config value - $sql = " INSERT INTO $plugin_config_table " . - " (config_key, config_type, config_value, testproject_id, author_id) " . - " VALUES (" . - "'" . $dbHandler->prepare_string($full_option) . "', " . - $config_type . "," . - "'" . $dbHandler->prepare_string($value) . "', " . - $safe_id . ", " . $_SESSION['currentUser']->dbID . ")"; - } - $dbHandler->exec_query($sql); -} - -/** - * Set plugin default values to global values without overriding anything. - * @param array Array of configuration option name/value pairs. - */ -function plugin_config_defaults($options) -{ - global $g_plugin_config_cache; - if (!is_array($options)) - { - return; - } - - $basename = plugin_get_current(); - $option_base = 'plugin_' . $basename . '_'; - - foreach ($options as $option => $value) - { - $full_option = $option_base . $option; - $g_plugin_config_cache[$full_option] = $value; - } -} - -/** - * Get a language string for the plugin. - * Automatically prepends plugin_ to the string requested. - * @param string Language string name - * @param string Plugin basename - * @return string Language string - */ -function plugin_lang_get($p_name, $p_basename = null) -{ - if (!is_null($p_basename)) - { - plugin_push_current($p_basename); - } - - $t_basename = plugin_get_current(); - $t_name = 'plugin_' . $t_basename . '_' . $p_name; - $t_string = lang_get($t_name); - - if (!is_null($p_basename)) - { - plugin_pop_current(); - } - return $t_string; -} - -/** - * Hook a plugin's callback function to an event. - * @param string Event name - * @param string Callback function - */ -function plugin_event_hook($p_name, $p_callback) -{ - $t_basename = plugin_get_current(); - event_hook($p_name, $p_callback, $t_basename); -} - -/** - * Hook multiple plugin callbacks at once. - * @param array Array of event name/callback key/value pairs - */ -function plugin_event_hook_many($p_hooks) -{ - if (!is_array($p_hooks)) - { - return; - } - - $t_basename = plugin_get_current(); - - foreach ($p_hooks as $t_event => $t_callbacks) - { - if (!is_array($t_callbacks)) - { - event_hook($t_event, $t_callbacks, $t_basename); - continue; - } - - foreach ($t_callbacks as $t_callback) - { - event_hook($t_event, $t_callback, $t_basename); - } - } -} - -# ## Plugin Management Helpers - -/** - * Checks if a given plugin has been registered and initialized, - * and returns a boolean value representing the "loaded" state. - * @param string Plugin basename - * @return boolean Plugin loaded - */ -function plugin_is_loaded($p_basename) -{ - global $g_plugin_cache_init; - - return (isset($g_plugin_cache_init[$p_basename]) && $g_plugin_cache_init[$p_basename]); -} - -# ## Plugin management functions -/** - * Determine if a given plugin is installed. - * @param string Plugin basename - * @return boolean True if plugin is installed - */ -function plugin_is_installed($p_basename) -{ - doDBConnect($dbHandler); - $tables = tlObjectWithDB::getDBTables(array('plugins')); - - $sql = " SELECT COUNT(*) count FROM {$tables['plugins']} " . - " WHERE basename='" . $dbHandler->prepare_string($p_basename) . "'"; - - $t_result = $dbHandler->fetchFirstRow($sql); - return (0 < $t_result['count']); -} - -/** - * Install a plugin to the database. - * @param string Plugin basename - */ -function plugin_install($p_plugin) -{ - $debugMsg = "Function: " . __FUNCTION__; - - if (plugin_is_installed($p_plugin->basename)) - { - trigger_error('Plugin ' . $p_plugin->basename . ' already installed', E_USER_WARNING); - return null; - } - - plugin_push_current($p_plugin->basename); - - if (!$p_plugin->install()) - { - plugin_pop_current($p_plugin->basename); - return null; - } - - doDBConnect($dbHandler); - $tables = tlObjectWithDB::getDBTables(array('plugins')); - $sql = "/* $debugMsg */ INSERT INTO {$tables['plugins']} (basename,enabled) " . - " VALUES ('" . $dbHandler->prepare_string($p_plugin->basename) . "',1)"; - $dbHandler->exec_query($sql); - - plugin_pop_current(); -} - -/** - * Uninstall a plugin from the database. - * @param string Plugin basename - */ -function plugin_uninstall($plugin_id) -{ - global $g_plugin_cache; - $debugMsg = "Function: " . __FUNCTION__; - - doDBConnect($dbHandler); - $tables = tlObjectWithDB::getDBTables(array('plugins')); - $sql = "/* debugMsg */ " . - " SELECT basename FROM {$tables['plugins']} WHERE id=" . $plugin_id; - - $t_row = $dbHandler->fetchFirstRow($sql); - - // Check that teh plugin is actually available and loaded - if (!$t_row) - { - return; - } - $t_basename = $t_row['basename']; - - $sql = "/* $debugMsg */ DELETE FROM {$tables['plugins']} " . - " WHERE id=" . $plugin_id; - $dbHandler->exec_query($sql); - - $p_plugin = $g_plugin_cache[$t_basename]; - $p_plugin->uninstall(); - return $t_basename; -} - -# ## Core usage only. -/** - * Search the plugins directory for plugins. - * @return array Plugin basename/info key/value pairs. - */ -function plugin_find_all() -{ - $t_plugin_path = TL_PLUGIN_PATH; - - if ($t_dir = opendir($t_plugin_path)) - { - while (($t_file = readdir($t_dir)) !== false) - { - if ('.' == $t_file || '..' == $t_file) - { - continue; - } - if (is_dir($t_plugin_path . $t_file)) - { - $t_plugin = plugin_register($t_file, true); - - if (!is_null($t_plugin)) - { - $t_plugins[$t_file] = $t_plugin; - } - } - } - closedir($t_dir); - } - return $t_plugins; -} - -/** - * Load a plugin's core class file. - * @param string Plugin basename - */ -function plugin_include($p_basename) -{ - $t_plugin_file = TL_PLUGIN_PATH . $p_basename . DIRECTORY_SEPARATOR . $p_basename . '.php'; - - $t_included = false; - if (is_file($t_plugin_file)) - { - include_once($t_plugin_file); - $t_included = true; - } - - return $t_included; -} - -/** - * Register a plugin with TestLink. - * The plugin class must already be loaded before calling. - * @param string Plugin classname without 'Plugin' postfix - */ -function plugin_register($p_basename, $p_return = false) -{ - global $g_plugin_cache; - - if (!isset($g_plugin_cache[$p_basename])) - { - $t_classname = $p_basename . 'Plugin'; - - # Include the plugin script if the class is not already declared. - if (!class_exists($t_classname)) - { - if (!plugin_include($p_basename)) - { - return null; - } - } - - # Make sure the class exists and that it's of the right type. - if (class_exists($t_classname) && is_subclass_of($t_classname, 'TestlinkPlugin')) - { - plugin_push_current($p_basename); - - doDBConnect($dbHandler); - $t_plugin = new $t_classname($dbHandler, $p_basename); - - plugin_pop_current(); - - # Final check on the class - if (is_null($t_plugin->name) || is_null($t_plugin->version)) - { - return null; - } - - if ($p_return) - { - return $t_plugin; - } - else - { - $g_plugin_cache[$p_basename] = $t_plugin; - } - } - } - - return $g_plugin_cache[$p_basename]; -} - -/** - * Find and register all installed plugins. - */ -function plugin_register_installed() -{ - doDBConnect($dbHandler); - $tables = tlObjectWithDB::getDBTables(array('plugins')); - $sql = "/* debugMsg */ " . - " SELECT basename FROM {$tables['plugins']} WHERE enabled=1 "; - - $t_result = $dbHandler->exec_query($sql); - while ($t_row = $dbHandler->fetch_array($t_result)) - { - $t_basename = $t_row['basename']; - plugin_register($t_basename); - } -} - -/** - * Initialize all installed plugins. - */ -function plugin_init_installed() -{ - - global $g_plugin_cache, $g_plugin_current, $g_plugin_cache_init; - $g_plugin_cache = array(); - $g_plugin_current = array(); - $g_plugin_cache_init = array(); - - plugin_register_installed(); - - $t_plugins = array_keys($g_plugin_cache); - - foreach ($t_plugins as $t_basename) - { - plugin_init($t_basename); - } - -} - -/** - * Initialize a single plugin. - * @param string Plugin basename - * @return boolean True if plugin initialized, false otherwise. - */ -function plugin_init($p_basename) -{ - global $g_plugin_cache, $g_plugin_cache_init; - - $ret = false; - if (isset($g_plugin_cache[$p_basename])) - { - $t_plugin = $g_plugin_cache[$p_basename]; - - plugin_push_current($p_basename); - - # finish initializing the plugin - $t_plugin->__init(); - $g_plugin_cache_init[$p_basename] = true; - - plugin_pop_current(); - $ret = true; - } - return $ret; -} - -function get_all_installed_plugins() -{ - doDBConnect($dbHandler); - - // Store all the available plugins (Enabled + Disabled + Just Available) - $installed_plugins = array(); - - $tables = tlObjectWithDB::getDBTables(array('plugins')); - $sql = "/* debugMsg */ " . - " SELECT id, basename, enabled FROM {$tables['plugins']}"; - - $t_result = $dbHandler->exec_query($sql); - while ($t_row = $dbHandler->fetch_array($t_result)) { - $t_basename = $t_row['basename']; - $t_enabled = $t_row['enabled']; - $t_pluginid = $t_row['id']; - - if (plugin_include($t_basename)) { - $t_classname = $t_basename . 'Plugin'; - $t_plugin = new $t_classname($dbHandler, $t_basename); - - $installed_plugins[] = array( - 'id' => $t_pluginid, - 'name' => $t_basename, - 'enabled' => $t_enabled, - 'description' => $t_plugin->description, - 'version' => $t_plugin->version); - } - } - - return $installed_plugins; -} - -function get_plugin_name($arr) -{ - if (array_key_exists('name', $arr)) - { - return $arr['name']; - } - return false; -} - -function get_all_available_plugins($existing_plugins) -{ - $registered_plugin_names = array_map("get_plugin_name", $existing_plugins); - $available_plugins = array(); - // Find all plugins that are newly available (And not already registered) - if ($t_dir = opendir(TL_PLUGIN_PATH)) - { - while (($t_file = readdir($t_dir)) !== false) - { - if ('.' == $t_file || '..' == $t_file) - { - continue; - } - if (!in_array($t_file, $registered_plugin_names) && - is_dir(TL_PLUGIN_PATH. $t_file) && plugin_include($t_file)) - { - $t_classname = $t_file . 'Plugin'; - if (class_exists($t_classname) && is_subclass_of($t_classname, 'TestlinkPlugin')) - { - $t_plugin = new $t_classname($dbHandler, $t_file); - - $available_plugins[] = array('name' => $t_plugin->name, - 'enabled' => 0, - 'description' => $t_plugin->description, - 'version' => $t_plugin->version); - } - } - } - closedir($t_dir); - } - - return $available_plugins; +prepare_string($full_option); + + $sql = "/* $debugMsg */ " . " SELECT config_value FROM " . + $tables['plugins_configuration'] . " where config_key = '" . $full_option . + "' AND testproject_id = "; + + $value = $dbHandler->fetchOneValue($sql . intval($project)); + + if (is_null($value) && $project != TL_ANY_PROJECT) { + // Check if its in the Global Project + $value = $dbHandler->fetchOneValue($sql . TL_ANY_PROJECT); + } + + if (is_null($value)) { + // Fetch from the Global list, and if not, fetch from default value + global $g_plugin_config_cache; + $value = array_key_exists($full_option, $g_plugin_config_cache) ? $g_plugin_config_cache[$full_option] : $default; + } + return $value; +} + +/** + * Set a plugin configuration option in the database. + * + * @param + * string Configuration option name + * @param + * multi Option value + * @param + * int User ID + * @param + * int Project ID + * @param + * int Access threshold + */ +function plugin_config_set($option, $value, $project = TL_ANY_PROJECT) +{ + doDBConnect($dbHandler); + $tables = tlObjectWithDB::getDBTables(array( + 'plugins_configuration' + )); + $plugin_config_table = $tables['plugins_configuration']; + + $basename = plugin_get_current(); + $full_option = 'plugin_' . $basename . '_' . $option; + + if (is_array($value) || is_object($value)) { + $config_type = CONFIG_TYPE_COMPLEX; + $value = serialize($value); + } elseif (is_float($value)) { + $config_type = CONFIG_TYPE_FLOAT; + $value = (float) $value; + } elseif (is_int($value) || is_numeric($value)) { + $config_type = CONFIG_TYPE_INT; + $value = $dbHandler->prepare_int($value); + } else { + $config_type = CONFIG_TYPE_STRING; + } + + $safe_id = intval($project); + $sql = " SELECT COUNT(*) from $plugin_config_table " . + " WHERE config_key = '" . $dbHandler->prepare_string($full_option) . "' " . + " AND testproject_id = {$safe_id} "; + $rows_exist = $dbHandler->fetchOneValue($sql); + + if ($rows_exist > 0) { + // Update the existing record + $sql = " UPDATE $plugin_config_table " . " SET config_value = '" . + $dbHandler->prepare_string($value) . "'," . " config_type = " . + $config_type . " WHERE config_key = '" . + $dbHandler->prepare_string($full_option) . "' " . + " AND testproject_id = {$safe_id} "; + } else { + // Insert new config value + $sql = " INSERT INTO $plugin_config_table " . + " (config_key, config_type, config_value, testproject_id, author_id) " . + " VALUES (" . "'" . $dbHandler->prepare_string($full_option) . "', " . + $config_type . "," . "'" . $dbHandler->prepare_string($value) . "', " . + $safe_id . ", " . $_SESSION['currentUser']->dbID . ")"; + } + $dbHandler->exec_query($sql); +} + +/** + * Set plugin default values to global values without overriding anything. + * + * @param + * array Array of configuration option name/value pairs. + */ +function plugin_config_defaults($options) +{ + global $g_plugin_config_cache; + if (! is_array($options)) { + return; + } + + $basename = plugin_get_current(); + $option_base = 'plugin_' . $basename . '_'; + + foreach ($options as $option => $value) { + $full_option = $option_base . $option; + $g_plugin_config_cache[$full_option] = $value; + } +} + +/** + * Get a language string for the plugin. + * Automatically prepends plugin_ to the string requested. + * + * @param + * string Language string name + * @param + * string Plugin basename + * @return string Language string + */ +function plugin_lang_get($p_name, $p_basename = null) +{ + if (! is_null($p_basename)) { + plugin_push_current($p_basename); + } + + $t_basename = plugin_get_current(); + $t_name = 'plugin_' . $t_basename . '_' . $p_name; + $t_string = lang_get($t_name); + + if (! is_null($p_basename)) { + plugin_pop_current(); + } + return $t_string; +} + +/** + * Hook a plugin's callback function to an event. + * + * @param + * string Event name + * @param + * string Callback function + */ +function plugin_event_hook($p_name, $p_callback) +{ + $t_basename = plugin_get_current(); + event_hook($p_name, $p_callback, $t_basename); +} + +/** + * Hook multiple plugin callbacks at once. + * + * @param + * array Array of event name/callback key/value pairs + */ +function plugin_event_hook_many($p_hooks) +{ + if (! is_array($p_hooks)) { + return; + } + + $t_basename = plugin_get_current(); + + foreach ($p_hooks as $t_event => $t_callbacks) { + if (! is_array($t_callbacks)) { + event_hook($t_event, $t_callbacks, $t_basename); + continue; + } + + foreach ($t_callbacks as $t_callback) { + event_hook($t_event, $t_callback, $t_basename); + } + } +} + +# ## Plugin Management Helpers + +/** + * Checks if a given plugin has been registered and initialized, + * and returns a boolean value representing the "loaded" state. + * + * @param + * string Plugin basename + * @return boolean Plugin loaded + */ +function plugin_is_loaded($p_basename) +{ + global $g_plugin_cache_init; + + return isset($g_plugin_cache_init[$p_basename]) && + $g_plugin_cache_init[$p_basename]; +} + +# ## Plugin management functions +/** + * Determine if a given plugin is installed. + * + * @param + * string Plugin basename + * @return boolean True if plugin is installed + */ +function plugin_is_installed($p_basename) +{ + doDBConnect($dbHandler); + $tables = tlObjectWithDB::getDBTables(array( + 'plugins' + )); + + $sql = " SELECT COUNT(*) count FROM {$tables['plugins']} " . + " WHERE basename='" . $dbHandler->prepare_string($p_basename) . "'"; + + $t_result = $dbHandler->fetchFirstRow($sql); + return 0 < $t_result['count']; +} + +/** + * Install a plugin to the database. + * + * @param + * string Plugin basename + */ +function plugin_install($p_plugin) +{ + $debugMsg = "Function: " . __FUNCTION__; + + if (plugin_is_installed($p_plugin->basename)) { + trigger_error('Plugin ' . $p_plugin->basename . ' already installed', + E_USER_WARNING); + return null; + } + + plugin_push_current($p_plugin->basename); + + if (! $p_plugin->install()) { + plugin_pop_current($p_plugin->basename); + return null; + } + + doDBConnect($dbHandler); + $tables = tlObjectWithDB::getDBTables(array( + 'plugins' + )); + $sql = "/* $debugMsg */ INSERT INTO {$tables['plugins']} (basename,enabled) " . + " VALUES ('" . $dbHandler->prepare_string($p_plugin->basename) . "',1)"; + $dbHandler->exec_query($sql); + + plugin_pop_current(); +} + +/** + * Uninstall a plugin from the database. + * + * @param + * string Plugin basename + */ +function plugin_uninstall($plugin_id) +{ + global $g_plugin_cache; + $debugMsg = "Function: " . __FUNCTION__; + + doDBConnect($dbHandler); + $tables = tlObjectWithDB::getDBTables(array( + 'plugins' + )); + $sql = "/* debugMsg */ " . + " SELECT basename FROM {$tables['plugins']} WHERE id=" . $plugin_id; + + $t_row = $dbHandler->fetchFirstRow($sql); + + // Check that teh plugin is actually available and loaded + if (! $t_row) { + return; + } + $t_basename = $t_row['basename']; + + $sql = "/* $debugMsg */ DELETE FROM {$tables['plugins']} " . " WHERE id=" . + $plugin_id; + $dbHandler->exec_query($sql); + + $p_plugin = $g_plugin_cache[$t_basename]; + $p_plugin->uninstall(); + return $t_basename; +} + +# ## Core usage only. +/** + * Search the plugins directory for plugins. + * + * @return array Plugin basename/info key/value pairs. + */ +function plugin_find_all() +{ + $t_plugin_path = TL_PLUGIN_PATH; + + if ($t_dir = opendir($t_plugin_path)) { + while (($t_file = readdir($t_dir)) !== false) { + if ('.' == $t_file || '..' == $t_file) { + continue; + } + if (is_dir($t_plugin_path . $t_file)) { + $t_plugin = plugin_register($t_file, true); + + if (! is_null($t_plugin)) { + $t_plugins[$t_file] = $t_plugin; + } + } + } + closedir($t_dir); + } + return $t_plugins; +} + +/** + * Load a plugin's core class file. + * + * @param + * string Plugin basename + */ +function plugin_include($p_basename) +{ + $t_plugin_file = TL_PLUGIN_PATH . $p_basename . DIRECTORY_SEPARATOR . + $p_basename . '.php'; + + $t_included = false; + if (is_file($t_plugin_file)) { + include_once $t_plugin_file; + $t_included = true; + } + + return $t_included; +} + +/** + * Register a plugin with TestLink. + * The plugin class must already be loaded before calling. + * + * @param + * string Plugin classname without 'Plugin' postfix + */ +function plugin_register($p_basename, $p_return = false) +{ + global $g_plugin_cache; + + if (! isset($g_plugin_cache[$p_basename])) { + $t_classname = $p_basename . 'Plugin'; + + # Include the plugin script if the class is not already declared. + if (! class_exists($t_classname) && ! plugin_include($p_basename)) { + return null; + } + + # Make sure the class exists and that it's of the right type. + if (class_exists($t_classname) && + is_subclass_of($t_classname, 'TestlinkPlugin')) { + plugin_push_current($p_basename); + + doDBConnect($dbHandler); + $t_plugin = new $t_classname($dbHandler, $p_basename); + + plugin_pop_current(); + + # Final check on the class + if (is_null($t_plugin->name) || is_null($t_plugin->version)) { + return null; + } + + if ($p_return) { + return $t_plugin; + } else { + $g_plugin_cache[$p_basename] = $t_plugin; + } + } + } + + return $g_plugin_cache[$p_basename]; +} + +/** + * Find and register all installed plugins. + */ +function plugin_register_installed() +{ + doDBConnect($dbHandler); + $tables = tlObjectWithDB::getDBTables(array( + 'plugins' + )); + $sql = "/* debugMsg */ " . + " SELECT basename FROM {$tables['plugins']} WHERE enabled=1 "; + + $t_result = $dbHandler->exec_query($sql); + while ($t_row = $dbHandler->fetch_array($t_result)) { + $t_basename = $t_row['basename']; + plugin_register($t_basename); + } +} + +/** + * Initialize all installed plugins. + */ +function plugin_init_installed() +{ + global $g_plugin_cache, $g_plugin_current, $g_plugin_cache_init; + $g_plugin_cache = array(); + $g_plugin_current = array(); + $g_plugin_cache_init = array(); + + plugin_register_installed(); + + $t_plugins = array_keys($g_plugin_cache); + + foreach ($t_plugins as $t_basename) { + plugin_init($t_basename); + } +} + +/** + * Initialize a single plugin. + * + * @param + * string Plugin basename + * @return boolean True if plugin initialized, false otherwise. + */ +function plugin_init($p_basename) +{ + global $g_plugin_cache, $g_plugin_cache_init; + + $ret = false; + if (isset($g_plugin_cache[$p_basename])) { + $t_plugin = $g_plugin_cache[$p_basename]; + + plugin_push_current($p_basename); + + # finish initializing the plugin + $t_plugin->__init(); + $g_plugin_cache_init[$p_basename] = true; + + plugin_pop_current(); + $ret = true; + } + return $ret; +} + +function get_all_installed_plugins() +{ + doDBConnect($dbHandler); + + // Store all the available plugins (Enabled + Disabled + Just Available) + $installed_plugins = array(); + + $tables = tlObjectWithDB::getDBTables(array( + 'plugins' + )); + $sql = "/* debugMsg */ " . + " SELECT id, basename, enabled FROM {$tables['plugins']}"; + + $t_result = $dbHandler->exec_query($sql); + while ($t_row = $dbHandler->fetch_array($t_result)) { + $t_basename = $t_row['basename']; + $t_enabled = $t_row['enabled']; + $t_pluginid = $t_row['id']; + + if (plugin_include($t_basename)) { + $t_classname = $t_basename . 'Plugin'; + $t_plugin = new $t_classname($dbHandler, $t_basename); + + $installed_plugins[] = array( + 'id' => $t_pluginid, + 'name' => $t_basename, + 'enabled' => $t_enabled, + 'description' => $t_plugin->description, + 'version' => $t_plugin->version + ); + } + } + + return $installed_plugins; +} + +function get_plugin_name($arr) +{ + if (array_key_exists('name', $arr)) { + return $arr['name']; + } + return false; +} + +function get_all_available_plugins($existing_plugins) +{ + $registered_plugin_names = array_map("get_plugin_name", $existing_plugins); + $available_plugins = array(); + // Find all plugins that are newly available (And not already registered) + if ($t_dir = opendir(TL_PLUGIN_PATH)) { + while (($t_file = readdir($t_dir)) !== false) { + if ('.' == $t_file || '..' == $t_file) { + continue; + } + if (! in_array($t_file, $registered_plugin_names) && + is_dir(TL_PLUGIN_PATH . $t_file) && plugin_include($t_file)) { + $t_classname = $t_file . 'Plugin'; + if (class_exists($t_classname) && + is_subclass_of($t_classname, 'TestlinkPlugin')) { + $t_plugin = new $t_classname($dbHandler, $t_file); + + $available_plugins[] = array( + 'name' => $t_plugin->name, + 'enabled' => 0, + 'description' => $t_plugin->description, + 'version' => $t_plugin->version + ); + } + } + } + closedir($t_dir); + } + + return $available_plugins; } diff --git a/lib/functions/print.inc.php b/lib/functions/print.inc.php index a07813395a..c8689e6ac2 100644 --- a/lib/functions/print.inc.php +++ b/lib/functions/print.inc.php @@ -1,2287 +1,2417 @@ - 'requirement', 'status' => 'status', - 'scope' => 'scope', 'type' => 'type', 'author' => 'author', - 'relations' => 'relations','not_aplicable' => 'not_aplicable', - 'coverage' => 'coverage','last_edit' => 'last_edit', - 'custom_field' => 'custom_field', 'relation_project' => 'relation_project', - 'related_tcs' => 'related_tcs', 'version' => 'version', - 'revision' => 'revision', 'attached_files' => 'attached_files'); - - $labels = init_labels($labels); - - $decodeReq = array(); - $decodeReq['status'] = init_labels($req_cfg->status_labels); - $decodeReq['type'] = init_labels($req_cfg->type_labels); - - - $force['displayVersion'] = isset($options['displayVersion']) ? $options['displayVersion'] : false; - $force['displayLastEdit'] = isset($options['displayLastEdit']) ? $options['displayLastEdit'] : false; - - - $title_separator = config_get('gui_title_separator_1'); - $req_mgr = new requirement_mgr($db); - $tplan_mgr = new testplan($db); - - $repoDir = config_get('repositoryPath'); - } - - $versionID = isset($node['version_id']) ? intval($node['version_id']) : requirement_mgr::LATEST_VERSION; - $revision = isset($node['revision']) ? intval($node['revision']) : null; - - $getOpt = array('renderImageInline' => true); - if( is_null($revision) ) { - // will get last revision of requested req version - $dummy = $req_mgr->get_by_id($node['id'],$versionID,1,$getOpt); - } - else { - $dummy = $req_mgr->get_version_revision($versionID,array('number' => $revision),$getOpt); - if(!is_null($dummy)) { - // do this way instead of using SQL alias on get_version_revision(), in order - // to avoid issues (potential not confirmed)on different DBMS. - $dummy[0]['id'] = $dummy[0]['req_id']; - } - } - - $req = $dummy[0]; - - // update with values got from req, this is needed if user did not provide it - $versionID = $req['version_id']; - $revision = $req['revision']; - - $name = htmlspecialchars($req["req_doc_id"] . $title_separator . $req['title']); - - // change table style in case of single req printing to not be indented - $table_style = ""; - if (isset($options['docType']) && $options['docType'] == SINGLE_REQ) - { - $table_style = "style=\"margin-left: 0;\""; - } - - $output = "\n"; - - if( $force['displayVersion'] ) { - foreach(array('version','revision') as $key) { - $output .= '' . - '\n"; - } - } - - - if ($options['toc']) { - $options['tocCode'] .= '

    ' . - $name . '

    '; - $output .= ''; - } - - if ($options['req_author']) - { - $output .= '' . - '\n"; - - if ($req['modifier_id'] > 0) - { - // add updater if available and differs from author OR forced - if ($force['displayLastEdit'] || ($req['modifier_id'] != $req['modifier_id']) ) - { - $output .= '' . - '\n"; - } - } - } - - foreach(array('status','type') as $key) - { - if($options['req_' . $key]) - { - $output .= '" . - ""; - } - } - - if ($options['req_coverage']) { - - // @since 1.9.18 - // Coverage Link REQV to TCV - // $current = count($req_mgr->get_coverage($req['id'])); - $current = count((array)$req_mgr->getGoodForReqVersion($req['version_id'])); - - $expected = $req['expected_coverage']; - $coverage = $labels['not_aplicable'] . " ($current/0)"; - if ($expected) { - $percentage = round(100 / $expected * $current, 2); - $coverage = "{$percentage}% ({$current}/{$expected})"; - } - - $output .= "" . ""; - } - - if ($options['req_scope']) - { - $output .= ""; - } - - if ($options['req_relations']) { - - // REQ relations are managed AT REQ level NOT REQV - $relations = $req_mgr->get_relations($req['id']); - - if ($relations['num_relations']) { - $output .= ""; - } - } - - if ($options['req_linked_tcs']) { - - // @since 1.9.18 - // Coverage links REQV to TCV - // $req_coverage = $req_mgr->get_coverage($req['id']); - $req_coverage = - (array)$req_mgr->getGoodForReqVersion($req['version_id'], - array('verbose' => true, 'tproject_id' => $tprojectID)); - - if (count($req_coverage) > 0) { - $output .= "" . ""; - } - } - - if ($options['req_cf']) - { - $childID = (is_null($revision) || $req['revision_id'] < 0) ? $req['version_id'] : $req['revision_id']; - $linked_cf = $req_mgr->get_linked_cfields($req['id'], $childID); - if ($linked_cf) - { - foreach ($linked_cf as $key => $cf) - { - $cflabel = htmlspecialchars($cf['label']); - $value = htmlspecialchars($cf['value']); - - $output .= "" . ""; - } - } - } - - // Display Images Inline (Always) - // since 1.9.18 => we need to use req version - $attachSet = (array)$req_mgr->getAttachmentInfos($req['revision_id']); - - if (count($attachSet)) { - $output .= ""; - } - - - $output .= "
    " . - "{$labels['requirement']}: " . $name . "
    ' . - ''.$labels[$key].':' . $req[$key]. "
    ' . - ''.$labels['author'].':' . htmlspecialchars(gendocGetUserName($db, $req['author_id'])); - - if(isset($options['displayDates']) && $options['displayDates']) - { - $dummy = null; - $output .= ' - ' . localize_dateOrTimeStamp(null,$dummy,'timestamp_format',$req['creation_ts']); - } - $output .= "
    ' . - ''. $labels['last_edit'] . ':' . htmlspecialchars(gendocGetUserName($db, $req['modifier_id'])); - - if(isset($options['displayDates']) && $options['displayDates']) - { - $dummy = null; - $output .= ' - ' . localize_dateOrTimeStamp(null,$dummy,'timestamp_format', - $req['modification_ts']); - } - $output .= "
    ' . - $labels[$key] . "" . $decodeReq[$key][$req[$key]] . "
    " . $labels['coverage'] . - "$coverage

    " . $req['scope'] . "
    " . $labels['relations'] . - ""; - - $filler = str_repeat(' ',5); // MAGIC allowed - foreach ($relations['relations'] as $rel) - { - $output .= "{$rel['type_localized']}:
    {$filler}" . - htmlspecialchars($rel['related_req']['req_doc_id']) . $title_separator . - htmlspecialchars($rel['related_req']['title']) . "
    " . - "{$filler}{$labels['status']}: " . - "{$decodeReq['status'][$rel['related_req']['status']]}
    "; - - if ($req_cfg->relations->interproject_linking) - { - $output .= "{$filler}{$labels['relation_project']}: " . - htmlspecialchars($rel['related_req']['testproject_name']) . "
    "; - } - } - - $output .= "
    " . $labels['related_tcs'] . - ""; - foreach ($req_coverage[$req['version_id']] as $tc) { - $output .= htmlspecialchars($tc['tc_external_id'] . $title_separator . $tc['testcase_name']) . "  [{$labels['version']}:" . $tc['version'] . "]"; - } - - $output .= "
    " . - $cflabel . "$value
    " . - $labels['attached_files'] . ""; - - foreach($attachSet as $fitem) { - $sec = hash('sha256',$fitem['file_name']); - $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . $sec . - '&id=' . $fitem['id']; - - $safeFileName = htmlspecialchars($fitem['file_name']); - if($fitem['is_image']) { - $output .= "
  • " . $safeFileName . "
  • "; - - $pathname = $repoDir . $item['file_path']; - list($iWidth, $iHeight, $iT, $iA) = getimagesize($pathname); - $iDim = ' width=' . $iWidth . ' height=' . $iHeight; - $output .= '
  • ' . ''; - } else { - $output .= '
  • ' . ' ' . $safeFileName . ''; - } - } - $output .="

  • "; - - return $output; -} - - -/** - * render a requirement specification node as HTML code for printing - * - * @author Andreas Simon - * - * @param resource $db - * @param array $node the node to be printed - * @param array $options - * @param string $tocPrefix Prefix to be printed in TOC before title of node - * @param int $level - * @param int $tprojectID - * - * @return string $output HTML Code - */ -function renderReqSpecNodeForPrinting(&$db, &$node, &$options, $tocPrefix, $rsLevel, $tprojectID) -{ - static $tableColspan; - static $firstColWidth; - static $labels; - static $title_separator; - static $req_spec_mgr; - static $tplan_mgr; - static $req_spec_cfg; - static $reqSpecTypeLabels; - static $nodeTypes; - static $basehref; - static $repoDir; - - $output = ''; - $reLevel = ($rsLevel > 0) ? $rsLevel : 1; - - if (!$req_spec_mgr) { - $repoDir = config_get('repositoryPath'); - - $basehref = $_SESSION['basehref']; - $req_spec_cfg = config_get('req_spec_cfg'); - $firstColWidth = '20%'; - $tableColspan = 2; - $labels = array('requirements_spec' => 'requirements_spec', - 'scope' => 'scope', 'type' => 'type', 'author' => 'author', - 'relations' => 'relations', 'overwritten_count' => 'req_total', - 'coverage' => 'coverage','revision' => 'revision','attached_files' => 'attached_files', - 'undefined_req_spec_type' => 'undefined_req_spec_type', - 'custom_field' => 'custom_field', 'not_aplicable' => 'not_aplicable'); - - $labels = init_labels($labels); - $reqSpecTypeLabels = init_labels($req_spec_cfg->type_labels); - $title_separator = config_get('gui_title_separator_1'); - $req_spec_mgr = new requirement_spec_mgr($db); - $tplan_mgr = new testplan($db); - $nodeTypes = array_flip($tplan_mgr->tree_manager->get_available_node_types()); - } - - switch($nodeTypes[$node['node_type_id']]) - { - case 'requirement_spec_revision': - $spec = $req_spec_mgr->getRevisionByID($node['id']); - $spec_id = $spec['parent_id']; - $who = array('parent_id' => $spec['parent_id'],'item_id' => $spec['id'], - 'tproject_id' => $spec['testproject_id']); - break; - - case 'requirement_spec': - $spec = $req_spec_mgr->get_by_id($node['id']); - $spec_id = $spec['id']; - $who = array('parent_id' => $spec['id'],'item_id' => $spec['revision_id'], - 'tproject_id' => $spec['testproject_id']); - break; - } - $name = htmlspecialchars($spec['doc_id'] . $title_separator . $spec['title']); - - $docHeadingNumbering = ''; - if ($options['headerNumbering']) { - $docHeadingNumbering = "$tocPrefix. "; - } - - if($options['docType'] != SINGLE_REQSPEC) - { - $output = '

    '; - } - - // Remember that only H1 to H6 exists - $reLevel = ($reLevel > 6) ? 6 : $reLevel; - $reLevel = ($reLevel < 1) ? 1 : $reLevel; - - $output .= "\n"; - - if ($options['toc']) - { - $spacing = ($reLevel == 2) ? "
    " : ""; - $options['tocCode'] .= $spacing.'

    ' . - '' . $docHeadingNumbering . $name . "

    \n"; - $output .= "\n"; - } - $output .= '\n"; - - if ($options['req_spec_author']) - { - // get author name for node - $author = tlUser::getById($db, $spec['author_id']); - $whois = (is_null($author)) ? lang_get('undefined') : $author->getDisplayName(); - $output .= '\n"; - } - - if ($options['req_spec_type']) - { - $output .= '" . ""; - } - - if ($options['req_spec_overwritten_count_reqs']) { - $current = $req_spec_mgr->get_requirements_count($spec_id); // NEEDS REFACTOR - $expected = $spec['total_req']; - $coverage = $labels['not_aplicable'] . " ($current/0)"; - if ($expected) { - $percentage = round(100 / $expected * $current, 2); - $coverage = "{$percentage}% ({$current}/{$expected})"; - } - - $output .= '" . - ""; - } - - if ($options['req_spec_scope']) - { - $output .= ""; - } - - if ($options['req_spec_cf']) - { - - $linked_cf = $req_spec_mgr->get_linked_cfields($who); - if ($linked_cf) - { - foreach ($linked_cf as $key => $cf) - { - $cflabel = htmlspecialchars($cf['label']); - $value = htmlspecialchars($cf['value']); - - $output .= "" . - ""; - } - } - } - - $attachSet = (array)$req_spec_mgr->getAttachmentInfos($spec_id); - if (count($attachSet)) { - $output .= ""; - } - - $output .= "
    " . - " {$docHeadingNumbering}{$labels['requirements_spec']}: " . - $name . "
    ' . - $labels['revision'] . " " . - $spec['revision'] . "
    ' . - $labels['author'] . " " . - htmlspecialchars($whois) . "
    ' . - $labels['type'] . ""; - - if( isset($reqSpecTypeLabels[$spec['type']]) ) - { - $output .= $reqSpecTypeLabels[$spec['type']]; - } - else - { - $output .= sprintf($labels['undefined_req_spec_type'],$spec['type']); - } - $output .= "
    ' . - $labels['overwritten_count'] . " (" . $labels['coverage'] . ")" . $coverage . "
    " . $spec['scope'] . "
    " . - $cflabel . "$value
    " . - $labels['attached_files'] . "
      "; - - foreach($attachSet as $item) { - $fname = ""; - if ($item['title']) { - $fname .= htmlspecialchars($item['title']) . " : "; - } - $fname .= htmlspecialchars($item['file_name']); - $output .= "
    • $fname
    • "; - } - $output .="

    \n"; - - return $output; -} - - -/** - * render a complete tree, consisting of mixed requirement and req spec nodes, - * as HTML code for printing - * - * @author Andreas Simon - * - * @param resource $db - * @param array $node the node to be printed - * @param array $options - * @param string $tocPrefix Prefix to be printed in TOC before title of each node - * @param int $level - * @param int $tprojectID - * @param int $user_id ID of user which shall be printed as author of the document - * - * @return string $output HTML Code - */ -function renderReqSpecTreeForPrinting(&$db, &$node, &$options,$tocPrefix, $rsCnt, $rstLevel, $user_id, - $tplan_id = 0, $tprojectID = 0) -{ - - static $tree_mgr; - static $map_id_descr; - static $tplan_mgr; - static $repoDir; - $code = null; - - if(!$tree_mgr) { - $tplan_mgr = new testplan($db); - $tree_mgr = new tree($db); - $map_id_descr = $tree_mgr->node_types; - $repoDir = config_get('repositoryPath'); - } - $verbose_node_type = $map_id_descr[$node['node_type_id']]; - - switch($verbose_node_type) - { - case 'testproject': - - break; - - case 'requirement_spec': - $tocPrefix .= (!is_null($tocPrefix) ? "." : '') . $rsCnt; - $code .= renderReqSpecNodeForPrinting($db,$node,$options, - $tocPrefix, $rstLevel, $tprojectID); - break; - - case 'requirement': - $tocPrefix .= (!is_null($tocPrefix) ? "." : '') . $rsCnt; - $code .= renderReqForPrinting($db, $node, $options, - $tocPrefix, $rstLevel, $tprojectID); - break; - } - - if (isset($node['childNodes']) && $node['childNodes']) - { - - $childNodes = $node['childNodes']; - $rsCnt = 0; - $children_qty = sizeof($childNodes); - for($i = 0;$i < $children_qty ;$i++) - { - $current = $childNodes[$i]; - if(is_null($current)) - { - continue; - } - - if (isset($current['node_type_id']) && - $map_id_descr[$current['node_type_id']] == 'requirement_spec') - { - $rsCnt++; - } - - $code .= renderReqSpecTreeForPrinting($db, $current, $options,$tocPrefix, $rsCnt, - $rstLevel+1, $user_id, $tplan_id, $tprojectID); - } - } - - if ($verbose_node_type == 'testproject') - { - if ($options['toc']) - { - $code = str_replace("{{INSERT_TOC}}",$options['tocCode'],$code); - } - } - - return $code; -} - - -/** - * render HTML header - * Standard: HTML 4.01 trans (because is more flexible to bugs in user data) - * - * @param string $title - * @param string $base_href Base URL - * - * @return string html data - */ -function renderHTMLHeader($title,$base_href,$doc_type,$jsSet=null) -{ - $themeDir = config_get('theme_dir'); - $docCfg = config_get('document_generator'); - - $cssFile = $base_href . $themeDir; - switch ($doc_type) - { - case DOC_REQ_SPEC: - case SINGLE_REQ: - case SINGLE_REQSPEC: - $cssFile .= $docCfg->requirement_css_template; - break; - - case DOC_TEST_SPEC: - case DOC_TEST_PLAN_DESIGN: - case DOC_TEST_PLAN_EXECUTION: - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - case SINGLE_TESTCASE: - default: - $cssFile .= $docCfg->css_template; - break; - } - - $output = "\n"; - - $output .= "\n\n"; - $output .= ''; - - $output .= '' . htmlspecialchars($title). "\n"; - $output .= '\n"; - - // way to add CSS directly to the exported file (not used - test required) - // $docCss = file_get_contents(TL_ABS_PATH . $docCfg->css_template); - // $output .= '\n"; - $output .= ''; - - if(!is_null($jsSet)) - { - foreach($jsSet as $js) - { - $output .= "\n" . '' . "\n"; - } - } - - $output .= "\n\n"; - return $output; -} - - -/** - * Generate initial page of document - * - * @param object $doc_info data with the next string values: - * title - * type_name: what does this means ??? - * author, tproject_name, testplan_name - * @return string html - * @author havlatm - */ -function renderFirstPage($doc_info) -{ - $docCfg = config_get('document_generator'); - $date_format_cfg = config_get('date_format'); - $output = "\n
    \n"; - - // Print header - if ($docCfg->company_name != '' ) - { - $output .= '
    ' . htmlspecialchars($docCfg->company_name) ."
    \n"; - } - $output .= "
     

    \n"; - - if ($docCfg->company_logo != '' ) - { - // allow to configure height via config file - $height = ''; - if (isset($docCfg->company_logo_height) && $docCfg->company_logo_height != '') - { - $height = "height=\"{$docCfg->company_logo_height}\""; - } - - $safePName = $_SESSION['basehref'] . TL_THEME_IMG_DIR . $docCfg->company_logo; - list($iWidth, $iHeight, $iType, $iAttr) = getimagesize($safePName); - $output .= '

    TestLink logo

    '; - } - $output .= "
    \n"; - - // Print context - // Report Minimal Description - // Test Project Name - // Test Plan Name - // Build Name (if applicable) - // Test Suite Name (if applicable) - // - $output .= '
    ' . '

    ' . $doc_info->type_name . '

    '; - if($doc_info->additional_info != '') - { - $output .= '

    ' . $doc_info->additional_info . '

    '; - } - $output .= "
    \n"; - $output .= '
    ' . '

    ' . - lang_get('testproject') . ": " . $doc_info->tproject_name; - - if($doc_info->type == DOC_TEST_PLAN_DESIGN || $doc_info->type == DOC_TEST_PLAN_EXECUTION || - $doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) - { - $output .= '
    ' . lang_get('testplan') . ": " . $doc_info->testplan_name; - } - - if($doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) - { - $output .= '
    ' . lang_get('build') . ": " . $doc_info->build_name; - } - - - if($doc_info->content_range == 'testsuite') - { - $output .= '
    ' . lang_get('testsuite') . ": " . $doc_info->title; - } - $output .= '

    ' . "
    \n"; - - - $output .= '
    ' . '

    ' . lang_get('printed_by_TestLink_on')." ". - strftime($date_format_cfg, time()) . "

    \n"; - - // Print legal notes - if ($docCfg->company_copyright != '') - { - $output .= '\n"; - } - - if ($docCfg->confidential_msg != '') - { - $output .= '\n"; - } - - return $output; -} - - -/** - * Generate a chapter to a document - * - * @param string $title - * @param string $content - * - * @return string html - * @author havlatm - */ -function renderSimpleChapter($title, $content, $addToStyle=null) -{ - $output = ''; - if ($content != "") - { - $sAdd = !is_null($addToStyle) ? " style=\"{$addToStyle}\" " : ''; - $output .= '

    ' . $title . "

    \n"; - $output .= '
    ' .$content . "
    \n
    "; - } - return $output; -} - - -/* - function: renderTestSpecTreeForPrinting - args : - returns: - - -env->base_href -env->item_type -env->tocPrefix -env->testCounter => env->tocCounter -env->user_id - -context['tproject_id'] -context['tplan_id'] -context['platform_id'] -context['build_id'] -context['level'] >>>>> WRONG -context['prefix'] - -*/ - -function renderTestSpecTreeForPrinting(&$db,&$node,&$options,$env,$context,$tocPrefix,$indentLevel) -{ - static $tree_mgr; - static $id_descr; - static $tplan_mgr; - static $repoDir; - - $code = null; - - if(!$tree_mgr) { - $tplan_mgr = new testplan($db); - $tree_mgr = new tree($db); - $id_descr = $tree_mgr->node_types; - $repoDir = config_get('repositoryPath'); - - $k2i = array('tproject_id' => 0, 'tplan_id' => 0, 'platform_id' => 0, 'build_id' => 0, 'prefix' => null); - $context = array_merge($k2i,$context); - } - - $node_type = $id_descr[intval($node['node_type_id'])]; - switch($node_type) { - case 'testproject': - break; - - case 'testsuite': - $tocPrefix .= (!is_null($tocPrefix) ? "." : '') . $env->tocCounter; - $code .= renderTestSuiteNodeForPrinting($db,$node,$env,$options,$context,$tocPrefix,$indentLevel); - break; - - case 'testcase': - $code .= renderTestCaseForPrinting($db,$node,$options,$env,$context,$indentLevel); - break; - } - - if (isset($node['childNodes']) && $node['childNodes']) { - // Need to be a LOCAL COUNTER for each PARENT - $TOCCounter = 0; - $childNodes = $node['childNodes']; - $children_qty = sizeof($childNodes); - for($idx = 0;$idx < $children_qty ;$idx++) - { - $current = $childNodes[$idx]; - if(is_null($current) || $current == REMOVEME) - { - continue; - } - - if (isset($current['node_type_id']) && $id_descr[$current['node_type_id']] == 'testsuite') - { - // Each time I found a contained Test Suite need to add a .x.x. to TOC - $TOCCounter++; - } - $env->tocCounter = $TOCCounter; - $code .= renderTestSpecTreeForPrinting($db,$current,$options,$env,$context,$tocPrefix,$indentLevel+1); - } - } - - if ($node_type == 'testproject' && $options['toc']) - { - $code = str_replace("{{INSERT_TOC}}",$options['tocCode'],$code); - } - - return $code; -} - - -/** - * get user name from pool (save used names in session to improve performance) - * - * @param integer $db DB connection identifier - * @param integer $userId - * - * @return string readable user name - * @author havlatm - */ -function gendocGetUserName(&$db, $userId) -{ - $authorName = null; - - if(isset($_SESSION['userNamePool'][$userId])) - { - $authorName = $_SESSION['userNamePool'][$userId]; - } - else - { - $user = tlUser::getByID($db,$userId); - if ($user) { - $authorName = $user->getDisplayName(); - $authorName = htmlspecialchars($authorName); - $_SESSION['userNamePool'][$userId] = $authorName; - } - else { - $authorName = lang_get('undefined'); - tLog('tlUser::getByID($db,$userId) failed', 'ERROR'); - } - } - - return $authorName; -} - - -/** - * render Test Case content for generated documents - * - * @param $integer db DB connection identifier - * @return string generated html code - * - * @internal revisions - */ -function renderTestCaseForPrinting(&$db,&$node,&$options,$env,$context,$indentLevel) { - - - static $st; - static $statusL10N; - static $labels; - static $tcase_prefix; - static $userMap = array(); - static $cfg; - static $force = null; - - $code = null; - $tcInfo = null; - $tcResultInfo = null; - $tcase_pieces = null; - - $id = $node['id']; - $tcversion_id = isset($node['tcversion_id']) ? $node['tcversion_id'] : null; - - $level = $indentLevel; - $prefix = isset($context['prefix']) ? $context['prefix'] : null; - $tplan_id = isset($context['tplan_id']) ? $context['tplan_id'] : 0; - $tprojectID = isset($context['tproject_id']) ? $context['tproject_id'] : 0; - $platform_id = isset($context['platform_id']) ? $context['platform_id'] : 0; - $build_id = isset($context['build_id']) ? $context['build_id'] : 0; - - $designCfg = getWebEditorCfg('design'); - $designType = $designCfg['type']; - $stepDesignCfg = getWebEditorCfg('steps_design'); - $stepDesignType = $stepDesignCfg['type']; - - // init static elements - if (!$st) { - - $st = new stdClass(); - $statusL10N = array(); - list($cfg,$labels,$statusL10N) = initRenderTestCaseCfg($options); - $st = initStaticRenderTestCaseForPrinting($db,$node['id'],$context,$cfg); - $st->statusL10N = $statusL10N; - - $tcase_prefix = $st->tcase_prefix; - - $force['displayVersion'] = isset($options['displayVersion']) ? $options['displayVersion'] : false; - $force['displayLastEdit'] = isset($options['displayLastEdit']) ? $options['displayLastEdit'] : false; - - // change table style in case of single TC printing to not be indented - $st->table_style = ""; - if (isset($options['docType']) && $options['docType'] == SINGLE_TESTCASE) { - $st->table_style = 'style="margin-left: 0;"'; - } - } - - - /** - * @TODO THIS IS NOT THE WAY TO DO THIS IS ABSOLUTELY WRONG AND MUST BE REFACTORED, - * using existent methods - franciscom - 20090329 - * Need to get CF with execution scope - */ - $exec_info = null; - $getByID['filters'] = null; - - $opt = array(); - $opt['step_exec_notes'] = isset($options['step_exec_notes']) && $options['step_exec_notes']; - $opt['step_exec_status'] = isset($options['step_exec_status']) && $options['step_exec_status']; - - switch($options["docType"]) { - case DOC_TEST_SPEC: - $getByID['tcversion_id'] = testcase::LATEST_VERSION; - $getExecutions = false; - break; - - case SINGLE_TESTCASE: - $getByID['tcversion_id'] = $node['tcversion_id']; - $getExecutions = ($options['passfail'] || $options['notes'] || - $opt['step_exec_notes'] || $opt['step_exec_status']); - break; - - default: - $getByID['tcversion_id'] = $node['tcversion_id']; - $getExecutions = ($options['cfields'] || $options['passfail'] || - $options['notes'] || - $opt['step_exec_notes'] || $opt['step_exec_status']); - break; - } - - if ($getExecutions) { - // Thanks to Evelyn from Cortado, have found a very old issue never reported. - // 1. create TC-1A VERSION 1 - // 2. add to test plan and execute FAILED ON BUILD 1 - // 3. Request Test Report (Test Plan EXECUTION REPORT). - // You will get spec for VERSION 1 and result for VERSION 1 - OK cool! - // 4. create VERSION 2 - // 5. update linked Test Case Versions - // 6. do nothing more than repeat step 3 - // without this fix you will get - // You will get spec for VERSION 2 and result for VERSION 1 - Hmmm - // and in addition is not clear that execution was on VERSION 1 . No GOOD!! - // - // HOW has been fixed ? - // Getting info about THE CURRENT LINKED test case version and looking for - // exec info for this. - // - // ATTENTION: THIS IS OK ONLY WHEN BUILD ID is not provided - // - // - // Get Linked test case version - $linkedItem = $st->tplan_mgr->getLinkInfo($tplan_id,$id,$platform_id); - - $sql = " SELECT E.id AS execution_id, E.status, E.execution_ts, + 'requirement', + 'status' => 'status', + 'scope' => 'scope', + 'type' => 'type', + 'author' => 'author', + 'relations' => 'relations', + 'not_aplicable' => 'not_aplicable', + 'coverage' => 'coverage', + 'last_edit' => 'last_edit', + 'custom_field' => 'custom_field', + 'relation_project' => 'relation_project', + 'related_tcs' => 'related_tcs', + 'version' => 'version', + 'revision' => 'revision', + 'attached_files' => 'attached_files' + ); + + $labels = init_labels($labels); + + $decodeReq = array(); + $decodeReq['status'] = init_labels($req_cfg->status_labels); + $decodeReq['type'] = init_labels($req_cfg->type_labels); + + $force['displayVersion'] = isset($options['displayVersion']) ? $options['displayVersion'] : false; + $force['displayLastEdit'] = isset($options['displayLastEdit']) ? $options['displayLastEdit'] : false; + + $title_separator = config_get('gui_title_separator_1'); + $req_mgr = new requirement_mgr($db); + + $repoDir = config_get('repositoryPath'); + } + + $versionID = isset($node['version_id']) ? intval($node['version_id']) : requirement_mgr::LATEST_VERSION; + $revision = isset($node['revision']) ? intval($node['revision']) : null; + + $getOpt = array( + 'renderImageInline' => true + ); + if (is_null($revision)) { + // will get last revision of requested req version + $dummy = $req_mgr->get_by_id($node['id'], $versionID, 1, $getOpt); + } else { + $dummy = $req_mgr->get_version_revision($versionID, + array( + 'number' => $revision + ), $getOpt); + if (! is_null($dummy)) { + // do this way instead of using SQL alias on get_version_revision(), in order + // to avoid issues (potential not confirmed)on different DBMS. + $dummy[0]['id'] = $dummy[0]['req_id']; + } + } + + $req = $dummy[0]; + + // update with values got from req, this is needed if user did not provide it + $revision = $req['revision']; + + $name = htmlspecialchars( + $req["req_doc_id"] . $title_separator . $req['title']); + + // change table style in case of single req printing to not be indented + $table_style = ""; + if (isset($options['docType']) && $options['docType'] == SINGLE_REQ) { + $table_style = "style=\"margin-left: 0;\""; + } + + $output = "\n"; + + if ($force['displayVersion']) { + foreach (array( + 'version', + 'revision' + ) as $key) { + $output .= '' . '\n"; + } + } + + if ($options['toc']) { + $options['tocCode'] .= '

    ' . + $name . '

    '; + $output .= ''; + } + + if ($options['req_author']) { + $output .= '' . '\n"; + + // add updater if available and differs from author OR forced + if ($req['modifier_id'] > 0 && $force['displayLastEdit'] || + ($req['modifier_id'] != $req['modifier_id'])) { + $output .= '' . '\n"; + } + } + + foreach (array( + 'status', + 'type' + ) as $key) { + if ($options['req_' . $key]) { + $output .= '" . + ""; + } + } + + if ($options['req_coverage']) { + + // @since 1.9.18 + // Coverage Link REQV to TCV + // $current = count($req_mgr->get_coverage($req['id'])); + $current = count( + (array) $req_mgr->getGoodForReqVersion($req['version_id'])); + + $expected = $req['expected_coverage']; + $coverage = $labels['not_aplicable'] . " ($current/0)"; + if ($expected) { + $percentage = round(100 / $expected * $current, 2); + $coverage = "{$percentage}% ({$current}/{$expected})"; + } + + $output .= "" . ""; + } + + if ($options['req_scope']) { + $output .= ""; + } + + if ($options['req_relations']) { + + // REQ relations are managed AT REQ level NOT REQV + $relations = $req_mgr->get_relations($req['id']); + + if ($relations['num_relations']) { + $output .= ""; + } + } + + if ($options['req_linked_tcs']) { + + // @since 1.9.18 + // Coverage links REQV to TCV + // $req_coverage = $req_mgr->get_coverage($req['id']); + $req_coverage = (array) $req_mgr->getGoodForReqVersion( + $req['version_id'], + array( + 'verbose' => true, + 'tproject_id' => $tprojectID + )); + + if (! empty($req_coverage)) { + $output .= "" . ""; + } + } + + if ($options['req_cf']) { + $childID = (is_null($revision) || $req['revision_id'] < 0) ? $req['version_id'] : $req['revision_id']; + $linked_cf = $req_mgr->get_linked_cfields($req['id'], $childID); + if ($linked_cf) { + foreach ($linked_cf as $cf) { + $cflabel = htmlspecialchars($cf['label']); + $value = htmlspecialchars($cf['value']); + + $output .= "" . ""; + } + } + } + + // Display Images Inline (Always) + // since 1.9.18 => we need to use req version + $attachSet = (array) $req_mgr->getAttachmentInfos($req['revision_id']); + + if (count($attachSet)) { + $output .= ""; + } + + $output .= "
    " . + "{$labels['requirement']}: " . $name . + "
    ' . '' . + $labels[$key] . ':' . $req[$key] . + "
    ' . '' . + $labels['author'] . ':' . + htmlspecialchars(gendocGetUserName($db, $req['author_id'])); + + if (isset($options['displayDates']) && $options['displayDates']) { + $dummy = null; + $output .= ' - ' . + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', + $req['creation_ts']); + } + $output .= "
    ' . '' . + $labels['last_edit'] . ':' . + htmlspecialchars(gendocGetUserName($db, $req['modifier_id'])); + + if (isset($options['displayDates']) && $options['displayDates']) { + $dummy = null; + $output .= ' - ' . + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', + $req['modification_ts']); + } + $output .= "
    ' . $labels[$key] . "" . $decodeReq[$key][$req[$key]] . "
    " . + $labels['coverage'] . "$coverage

    " . $req['scope'] . + "
    " . + $labels['relations'] . ""; + + $filler = str_repeat(' ', 5); // MAGIC allowed + foreach ($relations['relations'] as $rel) { + $output .= "{$rel['type_localized']}:
    {$filler}" . + htmlspecialchars($rel['related_req']['req_doc_id']) . + $title_separator . + htmlspecialchars($rel['related_req']['title']) . "
    " . + "{$filler}{$labels['status']}: " . + "{$decodeReq['status'][$rel['related_req']['status']]}
    "; + + if ($req_cfg->relations->interproject_linking) { + $output .= "{$filler}{$labels['relation_project']}: " . + htmlspecialchars( + $rel['related_req']['testproject_name']) . "
    "; + } + } + + $output .= "
    " . + $labels['related_tcs'] . ""; + foreach ($req_coverage[$req['version_id']] as $tc) { + $output .= htmlspecialchars( + $tc['tc_external_id'] . $title_separator . + $tc['testcase_name']) . "  [{$labels['version']}:" . + $tc['version'] . "]"; + } + + $output .= "
    " . + $cflabel . "$value
    " . + $labels['attached_files'] . ""; + + foreach ($attachSet as $fitem) { + $sec = hash('sha256', $fitem['file_name']); + $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . $sec . + '&id=' . $fitem['id']; + + $safeFileName = htmlspecialchars($fitem['file_name']); + if ($fitem['is_image']) { + $output .= "
  • " . $safeFileName . "
  • "; + + $pathname = $repoDir . $item['file_path']; + list ($iWidth, $iHeight, ,) = getimagesize($pathname); + + // Sorry by MAGIC Numbers + if ($iWidth > 900 || $iHeight > 700) { + if ($iWidth > $iHeight) { + $imgDiff = round($iWidth / 600); + } else { + $imgDiff = round($iHeight / 450); + } + $iWidth /= $imgDiff; + $iHeight /= $imgDiff; + } + + $iDim = ' width=' . $iWidth . ' height=' . $iHeight; + $output .= '
  • ' . ''; + } else { + $output .= '
  • ' . ' ' . $safeFileName . ''; + } + } + $output .= "

  • "; + + return $output; +} + +/** + * render a requirement specification node as HTML code for printing + * + * @author Andreas Simon + * + * @param resource $db + * @param array $node + * the node to be printed + * @param array $options + * @param string $tocPrefix + * Prefix to be printed in TOC before title of node + * @param int $level + * @param int $tprojectID + * + * @return string $output HTML Code + */ +function renderReqSpecNodeForPrinting(&$db, &$node, &$options, $tocPrefix, + $rsLevel) +{ + static $tableColspan; + static $firstColWidth; + static $labels; + static $title_separator; + static $req_spec_mgr; + static $tplan_mgr; + static $req_spec_cfg; + static $reqSpecTypeLabels; + static $nodeTypes; + + $output = ''; + $reLevel = ($rsLevel > 0) ? $rsLevel : 1; + + if (! $req_spec_mgr) { + $req_spec_cfg = config_get('req_spec_cfg'); + $firstColWidth = '20%'; + $tableColspan = 2; + $labels = array( + 'requirements_spec' => 'requirements_spec', + 'scope' => 'scope', + 'type' => 'type', + 'author' => 'author', + 'relations' => 'relations', + 'overwritten_count' => 'req_total', + 'coverage' => 'coverage', + 'revision' => 'revision', + 'attached_files' => 'attached_files', + 'undefined_req_spec_type' => 'undefined_req_spec_type', + 'custom_field' => 'custom_field', + 'not_aplicable' => 'not_aplicable' + ); + + $labels = init_labels($labels); + $reqSpecTypeLabels = init_labels($req_spec_cfg->type_labels); + $title_separator = config_get('gui_title_separator_1'); + $req_spec_mgr = new requirement_spec_mgr($db); + $tplan_mgr = new testplan($db); + $nodeTypes = array_flip( + $tplan_mgr->tree_manager->get_available_node_types()); + } + + switch ($nodeTypes[$node['node_type_id']]) { + case 'requirement_spec_revision': + $spec = $req_spec_mgr->getRevisionByID($node['id']); + $spec_id = $spec['parent_id']; + $who = array( + 'parent_id' => $spec['parent_id'], + 'item_id' => $spec['id'], + 'tproject_id' => $spec['testproject_id'] + ); + break; + + case 'requirement_spec': + $spec = $req_spec_mgr->get_by_id($node['id']); + $spec_id = $spec['id']; + $who = array( + 'parent_id' => $spec['id'], + 'item_id' => $spec['revision_id'], + 'tproject_id' => $spec['testproject_id'] + ); + break; + } + $name = htmlspecialchars( + $spec['doc_id'] . $title_separator . $spec['title']); + + $docHeadingNumbering = ''; + if ($options['headerNumbering']) { + $docHeadingNumbering = "$tocPrefix. "; + } + + if ($options['docType'] != SINGLE_REQSPEC) { + $output = '

    '; + } + + // Remember that only H1 to H6 exists + $reLevel = ($reLevel > 6) ? 6 : $reLevel; + $reLevel = ($reLevel < 1) ? 1 : $reLevel; + + $output .= "\n"; + + if ($options['toc']) { + $spacing = ($reLevel == 2) ? "
    " : ""; + $options['tocCode'] .= $spacing . '

    ' . '' . $docHeadingNumbering . $name . + "

    \n"; + $output .= "\n"; + } + $output .= '\n"; + + if ($options['req_spec_author']) { + // get author name for node + $author = tlUser::getById($db, $spec['author_id']); + $whois = (is_null($author)) ? lang_get('undefined') : $author->getDisplayName(); + $output .= '\n"; + } + + if ($options['req_spec_type']) { + $output .= '" . ""; + } + + if ($options['req_spec_overwritten_count_reqs']) { + $current = $req_spec_mgr->get_requirements_count($spec_id); // NEEDS REFACTOR + $expected = $spec['total_req']; + $coverage = $labels['not_aplicable'] . " ($current/0)"; + if ($expected) { + $percentage = round(100 / $expected * $current, 2); + $coverage = "{$percentage}% ({$current}/{$expected})"; + } + + $output .= '" . ""; + } + + if ($options['req_spec_scope']) { + $output .= ""; + } + + if ($options['req_spec_cf']) { + + $linked_cf = $req_spec_mgr->get_linked_cfields($who); + if ($linked_cf) { + foreach ($linked_cf as $cf) { + $cflabel = htmlspecialchars($cf['label']); + $value = htmlspecialchars($cf['value']); + + $output .= "" . ""; + } + } + } + + $attachSet = (array) $req_spec_mgr->getAttachmentInfos($spec_id); + if (count($attachSet)) { + $output .= ""; + } + + $output .= "
    " . + " {$docHeadingNumbering}{$labels['requirements_spec']}: " . + $name . "
    ' . + $labels['revision'] . " " . $spec['revision'] . + "
    ' . + $labels['author'] . " " . htmlspecialchars($whois) . + "
    ' . + $labels['type'] . ""; + + if (isset($reqSpecTypeLabels[$spec['type']])) { + $output .= $reqSpecTypeLabels[$spec['type']]; + } else { + $output .= sprintf($labels['undefined_req_spec_type'], $spec['type']); + } + $output .= "
    ' . + $labels['overwritten_count'] . " (" . $labels['coverage'] . + ")" . $coverage . "
    " . $spec['scope'] . + "
    " . + $cflabel . "$value
    " . + $labels['attached_files'] . "
      "; + + foreach ($attachSet as $item) { + $fname = ""; + if ($item['title']) { + $fname .= htmlspecialchars($item['title']) . " : "; + } + $fname .= htmlspecialchars($item['file_name']); + $output .= "
    • $fname
    • "; + } + $output .= "

    \n"; + + return $output; +} + +/** + * render a complete tree, consisting of mixed requirement and req spec nodes, + * as HTML code for printing + * + * @author Andreas Simon + * + * @param resource $db + * @param array $node + * the node to be printed + * @param array $options + * @param string $tocPrefix + * Prefix to be printed in TOC before title of each node + * @param int $level + * @param int $tprojectID + * @param int $user_id + * ID of user which shall be printed as author of the document + * + * @return string $output HTML Code + */ +function renderReqSpecTreeForPrinting(&$db, &$node, &$options, $tocPrefix, + $rsCnt, $rstLevel, $user_id, $tplan_id = 0, $tprojectID = 0) +{ + static $tree_mgr; + static $map_id_descr; + $code = null; + + if (! $tree_mgr) { + $tree_mgr = new tree($db); + $map_id_descr = $tree_mgr->node_types; + } + $verbose_node_type = $map_id_descr[$node['node_type_id']]; + + switch ($verbose_node_type) { + case 'testproject': + + break; + + case 'requirement_spec': + $tocPrefix .= (! is_null($tocPrefix) ? "." : '') . $rsCnt; + $code .= renderReqSpecNodeForPrinting($db, $node, $options, + $tocPrefix, $rstLevel, $tprojectID); + break; + + case 'requirement': + $tocPrefix .= (! is_null($tocPrefix) ? "." : '') . $rsCnt; + $code .= renderReqForPrinting($db, $node, $options, $rstLevel, + $tprojectID); + break; + } + + if (isset($node['childNodes']) && $node['childNodes']) { + + $childNodes = $node['childNodes']; + $rsCnt = 0; + $children_qty = count($childNodes); + for ($i = 0; $i < $children_qty; $i ++) { + $current = $childNodes[$i]; + if (is_null($current)) { + continue; + } + + if (isset($current['node_type_id']) && + $map_id_descr[$current['node_type_id']] == 'requirement_spec') { + $rsCnt ++; + } + + $code .= renderReqSpecTreeForPrinting($db, $current, $options, + $tocPrefix, $rsCnt, $rstLevel + 1, $user_id, $tplan_id, + $tprojectID); + } + } + + if ($verbose_node_type == 'testproject' && $options['toc']) { + $code = str_replace("{{INSERT_TOC}}", $options['tocCode'], $code); + } + + return $code; +} + +/** + * render HTML header + * Standard: HTML 4.01 trans (because is more flexible to bugs in user data) + * + * @param string $title + * @param string $base_href + * Base URL + * + * @return string html data + */ +function renderHTMLHeader($title, $base_href, $doc_type, $jsSet = null) +{ + $themeDir = config_get('theme_dir'); + $docCfg = config_get('document_generator'); + + $cssFile = $base_href . $themeDir; + switch ($doc_type) { + case DOC_REQ_SPEC: + case SINGLE_REQ: + case SINGLE_REQSPEC: + $cssFile .= $docCfg->requirement_css_template; + break; + + case DOC_TEST_SPEC: + case DOC_TEST_PLAN_DESIGN: + case DOC_TEST_PLAN_EXECUTION: + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + case SINGLE_TESTCASE: + default: + $cssFile .= $docCfg->css_template; + break; + } + + $output = "\n"; + + $output .= "\n\n"; + $output .= ''; + + $output .= '' . htmlspecialchars($title) . "\n"; + $output .= '\n"; + + // way to add CSS directly to the exported file (not used - test required) + // $docCss = file_get_contents(TL_ABS_PATH . $docCfg->css_template); + // $output .= '\n"; + $output .= ''; + + if (! is_null($jsSet)) { + foreach ($jsSet as $js) { + $output .= "\n" . '' . "\n"; + } + } + + $output .= "\n\n"; + return $output; +} + +/** + * Generate initial page of document + * + * @param object $doc_info + * data with the next string values: + * title + * type_name: what does this means ??? + * author, tproject_name, testplan_name + * @return string html + * @author havlatm + */ +function renderFirstPage($doc_info) +{ + $docCfg = config_get('document_generator'); + $date_format_cfg = config_get('date_format'); + $output = "\n
    \n"; + + // Print header + if ($docCfg->company_name != '') { + $output .= '
    ' . + htmlspecialchars($docCfg->company_name) . "
    \n"; + } + $output .= "
     

    \n"; + + if ($docCfg->company_logo != '') { + // allow to configure height via config file + $safePName = $_SESSION['basehref'] . TL_THEME_IMG_DIR . + $docCfg->company_logo; + list ($iWidth, $iHeight, ,) = getimagesize($safePName); + $output .= '

    TestLink logo

    '; + } + $output .= "
    \n"; + + // Print context + // Report Minimal Description + // Test Project Name + // Test Plan Name + // Build Name (if applicable) + // Test Suite Name (if applicable) + // + $output .= '
    ' . '

    ' . $doc_info->type_name . '

    '; + if ($doc_info->additional_info != '') { + $output .= '

    ' . $doc_info->additional_info . '

    '; + } + $output .= "
    \n"; + $output .= '
    ' . + '

    ' . lang_get('testproject') . ": " . $doc_info->tproject_name; + + if ($doc_info->type == DOC_TEST_PLAN_DESIGN || + $doc_info->type == DOC_TEST_PLAN_EXECUTION || + $doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { + $output .= '
    ' . lang_get('testplan') . ": " . + $doc_info->testplan_name; + } + + if ($doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { + $output .= '
    ' . lang_get('build') . ": " . $doc_info->build_name; + } + + if ($doc_info->content_range == 'testsuite') { + $output .= '
    ' . lang_get('testsuite') . ": " . $doc_info->title; + } + $output .= '

    ' . "
    \n"; + + $output .= '
    ' . '

    ' . + lang_get('printed_by_TestLink_on') . " " . + @strftime($date_format_cfg, time()) . "

    \n"; + + // Print legal notes + if ($docCfg->company_copyright != '') { + $output .= '\n"; + } + + if ($docCfg->confidential_msg != '') { + $output .= '\n"; + } + + return $output; +} + +/** + * Generate a chapter to a document + * + * @param string $title + * @param string $content + * + * @return string html + * @author havlatm + */ +function renderSimpleChapter($title, $content, $addToStyle = null) +{ + $output = ''; + if ($content != "") { + $sAdd = ! is_null($addToStyle) ? " style=\"{$addToStyle}\" " : ''; + $output .= '

    ' . $title . "

    \n"; + $output .= '
    ' . $content . "
    \n
    "; + } + return $output; +} + +/* + * function: renderTestSpecTreeForPrinting + * args : + * returns: + * + * + * env->base_href + * env->item_type + * env->tocPrefix + * env->testCounter => env->tocCounter + * env->user_id + * + * context['tproject_id'] + * context['tplan_id'] + * context['platform_id'] + * context['build_id'] + * context['level'] >>>>> WRONG + * context['prefix'] + * + */ +function renderTestSpecTreeForPrinting(&$db, &$node, &$options, $env, $context, + $tocPrefix, $indentLevel) +{ + static $tree_mgr; + static $id_descr; + + $code = null; + + if (! $tree_mgr) { + $tree_mgr = new tree($db); + $id_descr = $tree_mgr->node_types; + + $k2i = array( + 'tproject_id' => 0, + 'tplan_id' => 0, + 'platform_id' => 0, + 'build_id' => 0, + 'prefix' => null + ); + $context = array_merge($k2i, $context); + } + + $node_type = $id_descr[intval($node['node_type_id'])]; + switch ($node_type) { + case 'testproject': + break; + + case 'testsuite': + $tocPrefix .= (! is_null($tocPrefix) ? "." : '') . $env->tocCounter; + $code .= renderTestSuiteNodeForPrinting($db, $node, $env, $options, + $context, $tocPrefix, $indentLevel); + break; + + case 'testcase': + $code .= renderTestCaseForPrinting($db, $node, $options, $env, + $context, $indentLevel); + break; + } + + if (isset($node['childNodes']) && $node['childNodes']) { + // Need to be a LOCAL COUNTER for each PARENT + $tocCounter = 0; + $childNodes = $node['childNodes']; + $children_qty = count($childNodes); + for ($idx = 0; $idx < $children_qty; $idx ++) { + $current = $childNodes[$idx]; + if (is_null($current) || $current == REMOVEME) { + continue; + } + + if (isset($current['node_type_id']) && + $id_descr[$current['node_type_id']] == 'testsuite') { + // Each time I found a contained Test Suite need to add a .x.x. to TOC + $tocCounter ++; + } + $env->tocCounter = $tocCounter; + $code .= renderTestSpecTreeForPrinting($db, $current, $options, $env, + $context, $tocPrefix, $indentLevel + 1); + } + } + + if ($node_type == 'testproject' && $options['toc']) { + $code = str_replace("{{INSERT_TOC}}", $options['tocCode'], $code); + } + + return $code; +} + +/** + * get user name from pool (save used names in session to improve performance) + * + * @param integer $db + * DB connection identifier + * @param integer $userId + * + * @return string readable user name + * @author havlatm + */ +function gendocGetUserName(&$db, $userId) +{ + $authorName = null; + + if (isset($_SESSION['userNamePool'][$userId])) { + $authorName = $_SESSION['userNamePool'][$userId]; + } else { + $user = tlUser::getByID($db, $userId); + if ($user) { + $authorName = $user->getDisplayName(); + $authorName = htmlspecialchars($authorName); + $_SESSION['userNamePool'][$userId] = $authorName; + } else { + $authorName = lang_get('undefined'); + tLog('tlUser::getByID($db,$userId) failed', 'ERROR'); + } + } + + return $authorName; +} + +/** + * render Test Case content for generated documents + * + * @param $integer db + * DB connection identifier + * @return string generated html code + * + * @internal revisions + */ +function renderTestCaseForPrinting(&$db, &$node, &$options, $env, $context, + $indentLevel) +{ + static $st; + static $statusL10N; + static $labels; + static $tcase_prefix; + static $cfg; + static $force = null; + + $code = null; + $tcInfo = null; + $tcase_pieces = null; + + $id = $node['id']; + + $level = $indentLevel; + $tplan_id = isset($context['tplan_id']) ? $context['tplan_id'] : 0; + $tprojectID = isset($context['tproject_id']) ? $context['tproject_id'] : 0; + $platform_id = isset($context['platform_id']) ? $context['platform_id'] : 0; + $build_id = isset($context['build_id']) ? $context['build_id'] : 0; + + $designCfg = getWebEditorCfg('design'); + $designType = $designCfg['type']; + $stepDesignCfg = getWebEditorCfg('steps_design'); + $stepDesignType = $stepDesignCfg['type']; + + // init static elements + if (! $st) { + + $st = new stdClass(); + $statusL10N = array(); + list ($cfg, $labels, $statusL10N) = initRenderTestCaseCfg($options); + $st = initStaticRenderTestCaseForPrinting($db, $node['id'], $context, + $cfg); + $st->statusL10N = $statusL10N; + + $tcase_prefix = $st->tcase_prefix; + + $force['displayVersion'] = isset($options['displayVersion']) ? $options['displayVersion'] : false; + $force['displayLastEdit'] = isset($options['displayLastEdit']) ? $options['displayLastEdit'] : false; + + // change table style in case of single TC printing to not be indented + $st->table_style = ""; + if (isset($options['docType']) && $options['docType'] == SINGLE_TESTCASE) { + $st->table_style = 'style="margin-left: 0;"'; + } + } + + /** + * + * @todo THIS IS NOT THE WAY TO DO THIS IS ABSOLUTELY WRONG AND MUST BE REFACTORED, + * using existent methods - franciscom - 20090329 + * Need to get CF with execution scope + */ + $exec_info = null; + $getByID['filters'] = null; + + $opt = array(); + $opt['step_exec_notes'] = isset($options['step_exec_notes']) && + $options['step_exec_notes']; + $opt['step_exec_status'] = isset($options['step_exec_status']) && + $options['step_exec_status']; + + switch ($options["docType"]) { + case DOC_TEST_SPEC: + $getByID['tcversion_id'] = testcase::LATEST_VERSION; + $getExecutions = false; + break; + + case SINGLE_TESTCASE: + $getByID['tcversion_id'] = $node['tcversion_id']; + $getExecutions = ($options['passfail'] || $options['notes'] || + $opt['step_exec_notes'] || $opt['step_exec_status']); + break; + + default: + $getByID['tcversion_id'] = $node['tcversion_id']; + $getExecutions = ($options['cfields'] || $options['passfail'] || + $options['notes'] || $opt['step_exec_notes'] || + $opt['step_exec_status']); + break; + } + + if ($getExecutions) { + // Thanks to Evelyn from Cortado, have found a very old issue never reported. + // 1. create TC-1A VERSION 1 + // 2. add to test plan and execute FAILED ON BUILD 1 + // 3. Request Test Report (Test Plan EXECUTION REPORT). + // You will get spec for VERSION 1 and result for VERSION 1 - OK cool! + // 4. create VERSION 2 + // 5. update linked Test Case Versions + // 6. do nothing more than repeat step 3 + // without this fix you will get + // You will get spec for VERSION 2 and result for VERSION 1 - Hmmm + // and in addition is not clear that execution was on VERSION 1 . No GOOD!! + // + // HOW has been fixed ? + // Getting info about THE CURRENT LINKED test case version and looking for + // exec info for this. + // + // ATTENTION: THIS IS OK ONLY WHEN BUILD ID is not provided + // + // Get Linked test case version + $linkedItem = $st->tplan_mgr->getLinkInfo($tplan_id, $id, $platform_id); + + $sql = " SELECT E.id AS execution_id, E.status, E.execution_ts, E.tester_id, E.notes, E.build_id, E.tcversion_id, - E.tcversion_number,E.testplan_id," . - " E.execution_type, E.execution_duration, " . - " B.name AS build_name " . - " FROM {$st->tables['executions']} E " . - " JOIN {$st->tables['builds']} B ON B.id = E.build_id " . - " WHERE 1 = 1 "; - - //Bugfix to show only active builds in Test Report view - $sql .= "AND B.active = 1"; - - if(isset($context['exec_id'])) { - $sql .= " AND E.id=" . intval($context['exec_id']); - } else { - $sql .= " AND E.testplan_id = " . intval($tplan_id) . - " AND E.platform_id = " . intval($platform_id) . - " AND E.tcversion_id = " . intval($linkedItem[0]['tcversion_id']); - if($build_id > 0) { - $sql .= " AND E.build_id = " . intval($build_id); - } else { - // We are looking for LATEST EXECUTION of CURRENT LINKED test case version - $sql .= " AND E.tcversion_number=" . intval($linkedItem[0]['version']); - } - $sql .= " ORDER BY execution_id DESC"; - } - - $exec_info = $db->get_recordset($sql,null,1); - - $getByID['tcversion_id'] = $linkedItem[0]['tcversion_id']; - $getByID['filters'] = null; - $linkedItem = null; - - if( !is_null($exec_info) ) { - $getByID['tcversion_id'] = null; - $getByID['filters'] = array('version_number' => $exec_info[0]['tcversion_number']); - $tbuild_id = $exec_info[0]['build_id']; - if( isset($options['build_cfields']) && $options['build_cfields'] ) { - if( !isset($st->buildCfields[$tbuild_id]) ) { - $st->buildCfields[$tbuild_id] = - $st->build_mgr->html_table_of_custom_field_values($tbuild_id,$tprojectID); - } - } - } - } - - $tcInfo = (array)$st->tc_mgr->get_by_id($id,$getByID['tcversion_id'], - $getByID['filters'], - array('renderGhost' => true, - 'renderImageInline' => true)); - - - if( null != $tcInfo && count($tcInfo) > 0) { - $tcInfo = $tcInfo[0]; - } else { - $msg = basename(__FILE__) . ' >' . - 'Line: ' . __LINE__ . ' > ' . - 'Function: ' . __FUNCTION__ . ' > ' . - "Failed to get Test Case Info for ID=" . $id; - - if( $getByID['tcversion_id'] == testcase::ALL_VERSIONS ) { - $msg .= " ALL VERSIONS "; - } else if ( $getByID['tcversion_id'] == testcase::LATEST_VERSION ) { - $msg .= " LATEST VERSION "; - } else { - $msg .= " tcversion id:" . $getByID['tcversion_id']; - } - - tLog( $msg , 'ERROR'); - - throw new Exception($msg, 1); - } - - $tcVersionID = $tcInfo['id']; - - $external_id = $tcase_prefix . $tcInfo['tc_external_id']; - $name = htmlspecialchars($node['name']); - - $cfields = array('specScope' => null, 'execScope' => null); - if ($options['cfields']) { - // Get custom fields that has specification scope - // Custom Field values at Test Case VERSION Level - foreach($st->locationFilters as $fkey => $fvalue) { - $cfields['specScope'][$fkey] = - $st->tc_mgr->html_table_of_custom_field_values($id,'design',$fvalue,null, - $tplan_id,$tprojectID, - $st->cfieldFormatting,$tcInfo['id']); - } - - if (!is_null($exec_info)) { - $cfields['execScope'] = $st->tc_mgr->html_table_of_custom_field_values( - $tcInfo['id'],'execution',null, - $exec_info[0]['execution_id'], $tplan_id, - $tprojectID,$st->cfieldFormatting); - } - } - - if ($options['toc']) { - // EXTERNAL ID added - $options['tocCode'] .= '

    ' . - htmlspecialchars($external_id) . ": ". $name . '

    '; - $code .= ''; - } - - - $code .= '

     

    table_style . '>'; - $code .= '\n"; - - - if ($options['author']) { - $code .= '' . - '\n"; - - if ($tcInfo['updater_id'] > 0) { - // add updater if available and differs from author OR forced - if ($force['displayLastEdit'] > 0 || ($tcInfo['updater_id'] != $tcInfo['author_id']) ) - { - $code .= '' . - '\n"; - } - } - } - - if ($options['body'] || $options['summary']) { - $tcase_pieces = array('summary'); - } - - if ($options['body']) { - $tcase_pieces[] = 'preconditions'; - } - - if( $options['body'] || $options['step_exec_notes'] || $options['step_exec_status'] ){ - $tcase_pieces[] = 'steps'; - } - - if(!is_null($tcase_pieces)) { - // Check user rights in order to understand if can delete attachments here - // function hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) - // $tplan_id = isset($context['tplan_id']) ? $context['tplan_id'] : 0; - // $tprojectID = isset($context['tproject_id']) ? $context['tproject_id'] : 0; - $canManageAttachments = false; - if(isset($context['user']) && !is_null($context['user'])) { - $canManageAttachments = $context['user']->hasRight($db,'testplan_execute',$tprojectID,$tplan_id); - } - - // Multiple Test Case Steps Feature - foreach($tcase_pieces as $key) { - if( $key == 'steps' ) { - if( isset($cfields['specScope']['before_steps_results']) ) { - $code .= $cfields['specScope']['before_steps_results']; - } - - if (!is_null($tcInfo[$key]) && $tcInfo[$key] != '') { - $td_colspan = 3; - $code .= '' . - '' . - '' . - ''; - - $sxni = null; - if($opt['step_exec_notes'] || $opt['step_exec_status']) { - $sxni = $st->tc_mgr->getStepsExecInfo($exec_info[0]['execution_id']); - - if($opt['step_exec_notes']) { - $td_colspan++; - $code .= ''; - } - - if($opt['step_exec_status']) { - $td_colspan++; - $code .= ''; - } - } - - $code .= ''; - - $loop2do = count($tcInfo[$key]); - for($ydx=0 ; $ydx < $loop2do; $ydx++) { - $code .= '' . - '' . - '' . - ''; - - $nike = !is_null($sxni) && isset($sxni[$tcInfo[$key][$ydx]['id']]) && - !is_null($sxni[$tcInfo[$key][$ydx]['id']]); - if( $opt['step_exec_notes'] ) { - $code .= ''; - } - - if( $opt['step_exec_status'] ) { - $code .= ''; - } - $code .= ''; - - // Attachment management - if($getExecutions) { - if( isset($sxni[$tcInfo[$key][$ydx]['id']])) { - $attachInfo = getAttachmentInfos($st->docRepo, - $sxni[$tcInfo[$key][$ydx]['id']]['id'], - $st->tables['execution_tcsteps'],true,1); - - if( !is_null($attachInfo) ) { - $code .= ''; - } - } - } // $getExecutions - - } - } - } - else - { - // disable the field if it's empty - if ($tcInfo[$key] != '') { - $code .= '"; - } - } - } - } - - - $code .= '' . - '\n"; - - // estimated execution time - $code .= '' . - '\n"; - - if( isset($options['importance']) && $options['importance'] ) { - $code .= '' . - '\n"; - } - - - // print priority when printing test plan - if (isset($options['priority']) && $options['priority']) { - // Get priority of this tc version for this test plan by using testplanUrgency class. - // Is there maybe a better method than this one? - $filters = array('tcversion_id' => $tcInfo['id']); - $opt = array('details' => 'tcversion'); - $prio_info = $st->tplan_urgency->getPriority($tplan_id, $filters, $opt); - $prio = $prio_info[$tcInfo['id']]['priority_level']; - - $code .= '' . - '\n"; - } - - // Spacer - $code .= '"; - $code .= $cfields['specScope']['standard_location'] . $cfields['execScope']; - - // - $cfields = null; - $prio_info = null; - - // since 1.9.18 - // TC relations has been migrated to TCV relations - $greenCard = array('tcase_id' => $id, 'tcversion_id' => $tcVersionID); - $relSet = $st->tc_mgr->getTCVersionRelations($greenCard); - - if(!is_null($relSet['relations'])) { - // $fx = str_repeat(' ',5); // MAGIC allowed - $code .= ''; - - $code .= ''; - } - $relSet = null; - - - // collect REQ for Test Case Version - if ($options['requirement']) { - // Coverage Links REQV to TCV - $requirements = (array)$st->req_mgr->getActiveForTCVersion($tcVersionID); - $code .= '\n"; - } - $requirements = null; - - // collect keywords for TC VERSION - if ($options['keyword']) { - $code .= ''; - $code .= '\n"; - } - $kwSet = null; - - // collect platforms for TC VERSION - if ($options['platform']) { - $code .= ''; - $code .= '\n"; - } - $itSet = null; - - - // Attachments - $attachSet = (array)$st->tc_mgr->getAttachmentInfos($tcVersionID); - if (count($attachSet) > 0) { - $code .= ''; - $code .= '"; - } - $attachSet = null; - - - // generate test results data for test report - if ($options['passfail']) { - $tsp = ($cfg['tableColspan']-1); - $code .= '' . '' . - '\n"; - - - $bn = ''; - switch($env->reportType) { - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - $ib = $st->build_mgr->get_by_id($build_id); - $bn = htmlspecialchars($ib['name']); - break; - - case DOC_TEST_PLAN_EXECUTION: - if ($exec_info) { - $bn = htmlspecialchars($exec_info[0]['build_name']); - } - break; - } - - /* Build name */ - if( $bn != '' ) { - $code .= '' . - '\n"; - - if(is_null($exec_info)) { - if(!is_null($buildCfields) && - isset($st->buildCfields[$build_id]) && - $st->buildCfields[$build_id] != '') { - $code .= '' . '\n"; - } - } - } - - if( isset($node['assigned_to']) ) { - $crew = explode(',',$node['assigned_to']); - $code .= '' . - '\n"; - } - - if ($exec_info) { - $settings['cfg'] = $cfg; - $settings['lbl'] = $labels; - $settings['opt'] = array('show_notes' => $options['notes']); - $settings['colspan'] = $cfg['tableColspan']-1; - - $code .= - buildTestExecResults($db,$its,$exec_info,$settings,$st->buildCfields); - - // Get Execution Attachments - // Need to fixed in a better way - // Seems that when creating attachment I use 'executions' - // instead of real table name. - // Name will be different is TABLE PREFIX is configured - // - $execAttachInfo = - getAttachmentInfos($st->docRepo,$exec_info[0]['execution_id'],'executions', - true,1); - - if( !is_null($execAttachInfo) ) { - $code .= ''; - } - - } - else - { - $code .= '' . - '\n"; - } - $execAttachInfo = null; - $exec_info = null; - } - - $code .= "
    ' . - $labels['test_case'] . " " . - htmlspecialchars($external_id) . ": " . $name; - - // add test case version - switch($env->reportType) { - case DOC_TEST_PLAN_DESIGN: - $version_number = isset($node['version']) ? $node['version'] : $tcInfo['version']; - break; - - case DOC_TEST_PLAN_EXECUTION: - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - $version_number = $tcInfo['version']; - break; - - default: - $version_number = $tcInfo['version']; - break; - } - - if($cfg['doc']->tc_version_enabled || $force['displayVersion'] ) { - $code .= ' ' . - $cfg['gui']->version_separator_open . - $labels['version'] . $cfg['gui']->title_separator_1 . $version_number . - $cfg['gui']->version_separator_close . ''; - } - $code .= "
    ' . - ''.$labels['author'].':' . - gendocGetUserName($db, $tcInfo['author_id']); - - if(isset($options['displayDates']) && $options['displayDates']) { - $dummy = null; - $code .= ' - ' . localize_dateOrTimeStamp(null,$dummy,'timestamp_format',$tcInfo['creation_ts']); - } - $code .= "
    ' . - ''. $labels['last_edit'] . ':' . - gendocGetUserName($db, $tcInfo['updater_id']); - - if(isset($options['displayDates']) && $options['displayDates']) { - $dummy = null; - $code .= ' - ' . localize_dateOrTimeStamp(null,$dummy,'timestamp_format',$tcInfo['modification_ts']); - } - $code .= "
    ' . $labels['step_number'] .':' . $labels['step_actions'] .':' . $labels['expected_results'] .':' . $labels['step_exec_notes'] .':' . $labels['step_exec_status'] .':
    ' . $tcInfo[$key][$ydx]['step_number'] . - '' . ($stepDesignType == 'none' ? nl2br($tcInfo[$key][$ydx]['actions']) : $tcInfo[$key][$ydx]['actions'] ) . '' . ($stepDesignType == 'none' ? nl2br($tcInfo[$key][$ydx]['expected_results']) : $tcInfo[$key][$ydx]['expected_results'] ) . ''; - if( $nike ) { - $code .= nl2br($sxni[$tcInfo[$key][$ydx]['id']]['notes']); - } - $code .= ''; - if( $nike ) { - $sk = $sxni[$tcInfo[$key][$ydx]['id']]; - if(isset($statusL10N[$sk['status']])) - { - $code .= $statusL10N[$sk['status']]; - } - } - $code .= '
    '; - $code .= '' . $labels['exec_attachments'] . '
    '; - - foreach($attachInfo as $fitem) { - $code .= '
    '; - - $code .= ''; - - $safeItemID = intval($fitem['id']); - $code .= ''; - - $safeFileName = htmlspecialchars($fitem['file_name']); - $sec = hash('sha256',$fitem['file_name']); - $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . - $sec . '&id=' . $safeItemID; - - - if($fitem['is_image']) { - $code .= "
  • {$safeFileName}
  • "; - $pathname = $st->repoDir . $fitem['file_path']; - list($iWidth, $iHeight, $iT, $iA) = getimagesize($pathname); - $iDim = ' width=' . $iWidth . ' height=' . $iHeight; - $code .= '
  • '; - } else { - $code .= '
  • ' . $safeFileName . ''; - } - $code .= '
  • '; - } - $code .= '
    ' . $labels[$key] . - ':
    ' . ($designType == 'none' ? nl2br($tcInfo[$key]) : $tcInfo[$key] ) . "
    ' . - ''.$labels['execution_type'].':'; - - - // This is what have been choosen DURING DESIGN, but may be we can choose at DESIGN - // manual and the execute AUTO, or may be choose AUTO and execute MANUAL. - // After report on MANTIS, seems that we need to provide in output two values: - // DESIGN execution type - // EXECUTION execution type - switch ($tcInfo['execution_type']) { - case TESTCASE_EXECUTION_TYPE_AUTO: - $code .= $labels['execution_type_auto']; - break; - - case TESTCASE_EXECUTION_TYPE_MANUAL: - default: - $code .= $labels['execution_type_manual']; - break; - } - $code .= "
    ' . - ''. $labels['estimated_execution_duration'].':' . $tcInfo['estimated_exec_duration']; - $code .= "
    ' . - ''.$labels['importance'].':' . - $cfg['importance'][$tcInfo['importance']]; - $code .= "
    ' . - ''.$labels['priority'].':' . $cfg['priority'][$prio]; - $code .= "
    ' . "
    ' . $labels['relations'] . ''; - for($rdx=0; $rdx < $relSet['num_relations']; $rdx++) { - if($relSet['relations'][$rdx]['source_id'] == $id) { - $ak = 'source_localized'; - } - else { - $ak = 'destination_localized'; - } - - $code .= htmlspecialchars($relSet['relations'][$rdx][$ak]) . ' - ' . - htmlspecialchars($relSet['relations'][$rdx]['related_tcase']['fullExternalID']) . ':' . - htmlspecialchars($relSet['relations'][$rdx]['related_tcase']['name']) . "  [{$labels['version']}:" . - $relSet['relations'][$rdx]['related_tcase']['version'] . "]"; - } - $code .= '
    '. - $labels['reqs'].''; - $code .= ''; - - if (sizeof($requirements)) { - foreach ($requirements as $req) { - $code .= htmlspecialchars($req['req_doc_id'] . ": " . $req['title']) . - " " . - $cfg['gui']->version_separator_open . - "{$labels['version']}: {$req['version']}" . - $cfg['gui']->version_separator_close . - "
    "; - } - } else { - $code .= ' ' . $labels['none'] . '
    '; - } - $code .= "
    '. - $labels['keywords'].':'; - - $kwSet = (array)$st->tc_mgr->getKeywords($id,$tcVersionID,null,array('fields' => 'keyword_id,KW.keyword')); - if (sizeof($kwSet)) { - foreach ($kwSet as $kw) { - $code .= htmlspecialchars($kw['keyword']) . "
    "; - } - } else { - $code .= ' ' . $labels['none'] . '
    '; - } - $code .= "
    '. - $labels['platforms'].':'; - - $itSet = (array)$st->tc_mgr->getPlatforms($id,$tcVersionID,null,array('fields' => 'platform_id,PL.name')); - if (sizeof($itSet)) { - foreach ($itSet as $it) { - $code .= htmlspecialchars($it['name']) . "
    "; - } - } else { - $code .= ' ' . $labels['none'] . '
    '; - } - $code .= "
    ' . $labels['attached_files'] . '
      '; - - foreach($attachSet as $item) { - $fname = ""; - if ($item['title']) { - $fname .= htmlspecialchars($item['title']) . " : "; - } - $fname .= htmlspecialchars($item['file_name']); - $code .= "
    • $fname
    • "; - - $sec = hash('sha256',$item['file_name']); - - $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . $sec . - '&id=' . $item['id']; - - if($item['is_image']) { - $pathname = $st->repoDir . $item['file_path']; - list($iWidth, $iHeight, $iT, $iA) = getimagesize($pathname); - - $iDim = ' width=' . $iWidth . ' height=' . $iHeight; - $code .= '
    • ' . '
    • '; - } else { - $code .= '
    • ' . ' ' . htmlspecialchars($item['file_name']) . '
    • '; - } - } - $code .="
    ' . - $labels['execution_details'] .'' . " " . "
    ' . - $labels['build'] .'' . $bn . "
    ' . - $st->buildCfields[$build_id] . "
    ' . - $labels['assigned_to'] . ''; - - $xdx = 0; - foreach($crew as $mm) { - if ($xdx != 0) { - $code .= ','; - } - $xdx = -1; - echo $mm .'
    '; - $code .= gendocGetUserName($db, $mm); - } - $code .= "
    '; - $code .= '' . $labels['exec_attachments'] . '
    '; - foreach($execAttachInfo as $fitem) { - $sec = hash('sha256',$fitem['file_name']); - - $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . $sec . - '&id=' . $fitem['id']; - - $safeFileName = htmlspecialchars($fitem['file_name']); - if($fitem['is_image']) { - $code .= "
  • {$safeFileName}
  • "; - - $pathname = $st->repoDir . $item['file_path']; - list($iWidth, $iHeight, $iT, $iA) = getimagesize($pathname); - $iDim = ' width=' . $iWidth . ' height=' . $iHeight; - $code .= '
  • ' . '
  • '; - } else { - $code .= '
  • ' . ' ' . $safeFileName . '
  • '; - } - } - $code .= '
    ' . - '' . $labels['report_exec_result'] . '' . $labels["test_status_not_run"] . - "
    \n
    \n"; - return $code; -} - - -/** - * - * - * - */ -function renderTOC(&$options) -{ - $code = ''; - $options['toc_numbers'][1] = 0; - if ($options['toc']) - { - $options['tocCode'] = '

    ' . - lang_get('title_toc').'

    '; - $code .= "{{INSERT_TOC}}"; - } - - return $code; -} - - -/* - function: renderTestSuiteNodeForPrinting - args : - returns: - - ATTENTION: This variables: $tocPrefix,$indentLevel - - can not be passed on a data type that pass by reference - because need to have LOCAL life during recursion. - Having added it as members of $env and $context has generated a BUG -*/ -function renderTestSuiteNodeForPrinting(&$db,&$node,$env,&$options,$context,$tocPrefix,$indentLevel) -{ - static $tsuite_mgr; - static $l10n; - static $title_separator; - static $cfieldFormatting; - static $getOpt; - static $reporDir; - - $designCfg = getWebEditorCfg('design'); - $designType = $designCfg['type']; - - if(is_null($l10n)) { - $repoDir = config_get('repositoryPath'); - $tsuite_mgr = new testsuite($db); - - $l10n = array('test_suite' => 'test_suite', 'details' => 'details', - 'attached_files' => 'attached_files'); - - $l10n = init_labels($l10n); - - $title_separator = config_get('gui_title_separator_1'); - $cfieldFormatting = array('table_css_style' => 'class="cf"'); - - $getOpt['getByID'] = array('fields' => ' TS.id,TS.details ', - 'renderImageInline' => true); - } - - $code = null; - $name = isset($node['name']) ? htmlspecialchars($node['name']) : ''; - $cfields = array('design' => ''); - - $docHeadingNumbering = $options['headerNumbering'] ? ($tocPrefix . ".") : ''; - - if ($options['toc']) { - $spacing = ($indentLevel == 2 && $tocPrefix != 1) ? "
    " : ""; - $options['tocCode'] .= $spacing.'

    ' . - '' . $docHeadingNumbering . - $name . "

    \n"; - $code .= "\n"; - } - - // we would like to have html top heading H1 - H6 - $docHeadingLevel = ($indentLevel-1); - - // Remember that only H1 to H6 exists - $docHeadingLevel = ($docHeadingLevel > 6) ? 6 : $docHeadingLevel; - $docHeadingLevel = ($docHeadingLevel < 1) ? 1 : $docHeadingLevel; - - $code .= "" . $docHeadingNumbering . $l10n['test_suite'] . - $title_separator . $name . "\n"; - - - // ----- get Test Suite text ----------------- - if ($options['header']) { - - $tInfo = $tsuite_mgr->get_by_id($node['id'],$getOpt['getByID']); - if ($tInfo['details'] != '') { - $code .= '
    ' . ($designType == 'none' ? nl2br($tInfo['details']) : $tInfo['details'] ) . '
    '; - } - $tInfo = null; - - $attachSet = (array)$tsuite_mgr->getAttachmentInfos($node['id']); - if (count($attachSet) > 0) { - $code .= ''; - $code .= ''; - $code .= '"; - $code .= "
    ' . $l10n['attached_files'] . '
     
      '; - foreach($attachSet as $item) { - $fname = ""; - if ($item['title']) { - $fname .= htmlspecialchars($item['title']) . " : "; - } - $fname .= htmlspecialchars($item['file_name']); - $code .= "
    • $fname
    • "; - - - $sec = hash('sha256',$item['file_name']); - $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . - $sec . '&id=' . $item['id']; - - if($item['is_image']) { - $pathname = $repoDir . $item['file_path']; - list($iWidth, $iHeight, $iT, $iA) = getimagesize($pathname); - $iDim = ' width=' . $iWidth . ' height=' . $iHeight; - $code .= '
    • ' . '
    • '; - } else { - $code .= '
    • ' . ' ' . htmlspecialchars($item['file_name']) . '
    • '; - } - } - $code .="
    "; - } - $attachSet = null; - - // get Custom fields - // Attention: for test suites custom fields can not be edited during execution, - // then we need to get just custom fields with scope 'design' - foreach($cfields as $key => $value) { - $cfields[$key] = - $tsuite_mgr->html_table_of_custom_field_values($node['id'],$key,null, - $context['tproject_id'], - $cfieldFormatting); - if($cfields[$key] != "") { - $add_br = true; - $code .= '

    ' . $cfields[$key] . '

    '; - } - } - $cfields = null; - } - return $code; -} - - - - -/* - function: renderTestPlanForPrinting - args: - returns: - - @internal revisions: -*/ -function renderTestPlanForPrinting(&$db,&$node,&$options,$env,$context) { - $tProjectMgr = new testproject($db); - $context['prefix'] = $tProjectMgr->getTestCasePrefix($context['tproject_id']); - $code = - renderTestSpecTreeForPrinting($db,$node,$options,$env,$context,$env->tocPrefix,$context['level']); - return $code; -} - - -/** - * Render HTML for estimated and real execute duration based on contribution - * - * @param array_of_strings $statistics - * @return string HTML code - */ -function renderTestDuration($statistics,$platform_id=0) { - static $ecfg; - - $output = ''; - $hasOutput = false; - - if(!$ecfg) { - $ecfg = config_get('exec_cfg'); - } - $estimatedTimeAvailable = isset($statistics['estimated_execution']) && !is_null($statistics['estimated_execution']); - - if($ecfg->features->exec_duration->enabled) { - $realTimeAvailable = isset($statistics['real_execution']) && - !is_null($statistics['real_execution']['platform'][$platform_id]); - } else { - $realTimeAvailable = null; - } - - - if( $estimatedTimeAvailable || $realTimeAvailable) { - if($estimatedTimeAvailable) { - $estimated_minutes = $statistics['estimated_execution']['platform'][$platform_id]['minutes']; - $tcase_qty = $statistics['estimated_execution']['platform'][$platform_id]['tcase_qty']; - if($estimated_minutes > 0) { - if($estimated_minutes > 60) { - $estimated_string = lang_get('estimated_time_hours') . round($estimated_minutes/60,2) ; - } - else - { - $estimated_string = lang_get('estimated_time_min') . $estimated_minutes; - } - $estimated_string = sprintf($estimated_string,$tcase_qty); - $output .= '

    ' . $estimated_string . "

    \n"; - } - } - - if($realTimeAvailable) { - $real_minutes = $statistics['real_execution']['platform'][$platform_id]['minutes']; - $tcase_qty = $statistics['real_execution']['platform'][$platform_id]['tcase_qty']; - if( $real_minutes > 0 ) { - if($real_minutes > 60) { - $real_string = lang_get('real_time_hours') . round($real_minutes/60,2) ; - } else { - $real_string = lang_get('real_time_min') . $real_minutes; - } - $real_string = sprintf($real_string,$tcase_qty); - $output .= '

    ' . $real_string . "

    \n"; - } - } - } - - if($output != '') { - $output = "
    \n" . $output . "
    \n"; - } - - return $output; -} - - -/** - * get final markup for HTML - * - * @return string HTML - **/ -function renderEOF() { - return "\n\n"; -} - - -/** - * compose html text for metrics (meantime estimated time only) - * - * @return string html - */ -function buildTestPlanMetrics($statistics,$platform_id = 0) { - static $lbl; - if(!$lbl) { - $lbl = lang_get('execution_time_metrics'); - } - - $output =''; - $dummy = renderTestDuration($statistics,$platform_id); - if($dummy != '') - { - $output = '

    ' . $lbl . "

    \n" . $dummy; - } - return $output; -} - - -/** - * utility function to allow easy reading of code - * on renderTestCaseForPrinting() - * - * @return map with configuration and labels - * - */ -function initRenderTestCaseCfg($options) { - $config = null; - $config['firstColWidth'] = '20%'; - $config['doc'] = config_get('document_generator'); - $config['gui'] = config_get('gui'); - $config['testcase'] = config_get('testcase_cfg'); - $config['results'] = config_get('results'); - $config['exec_cfg'] = config_get('exec_cfg'); - - $config['tableColspan'] = 4; - if( (isset($options['step_exec_notes']) && $options['step_exec_notes']) ) { - $config['tableColspan']++; - } - if( (isset($options['step_exec_status']) && $options['step_exec_status']) ) { - $config['tableColspan']++; - } - - - foreach($config['results']['code_status'] as $key => $value) { - $config['status_labels'][$key] = - "check your \$tlCfg->results['status_label'] configuration "; - if( isset($config['results']['status_label'][$value]) ) { - $config['status_labels'][$key] = lang_get($config['results']['status_label'][$value]); - } - } - - $labelsKeys=array('last_exec_result', 'report_exec_result','execution_details', - 'execution_mode','version', 'bugs','tester', - 'title_execution_notes', 'none', 'reqs','author', 'summary', - 'steps', 'expected_results','build', 'test_case', 'keywords', - 'test_status_not_run', 'not_aplicable', 'preconditions','step', - 'step_number', 'step_actions', 'last_edit', 'created_on', - 'execution_type', - 'execution_type_manual','execution_type_auto','importance', - 'relations', - 'estimated_execution_duration','step_exec_notes', - 'step_exec_status', - 'exec_attachments','alt_delete_attachment','assigned_to', - 'high_importance','medium_importance','low_importance', - 'execution_duration', - 'priority', 'high_priority','medium_priority','low_priority', - 'attached_files','platforms'); - - $labelsQty=count($labelsKeys); - for($idx=0; $idx < $labelsQty; $idx++) { - $labels[$labelsKeys[$idx]] = lang_get($labelsKeys[$idx]); - } - - $config['importance'] = array(HIGH => $labels['high_importance'], - MEDIUM => $labels['medium_importance'], - LOW => $labels['low_importance']); - - $config['priority'] = array(HIGH => $labels['high_priority'], - MEDIUM => $labels['medium_priority'], - LOW => $labels['low_priority']); - - - $statusL10N = null; - foreach($config['results']['code_status'] as $vc => $vstat) { - if(isset($config['results']['status_label_for_exec_ui'][$vstat])) { - $statusL10N[$vc] = - lang_get($config['results']['status_label_for_exec_ui'][$vstat]); - } - } - - - return array($config,$labels,$statusL10N); -} - - -/** - * - * @internal revisions - * @since 1.9.12 - * - * - */ -function buildTestExecResults(&$dbHandler,&$its,$exec_info,$opt,$buildCF=null) -{ - static $testerNameCache; - $out=''; - - $my['opt'] = array('show_notes' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $cfg = &$opt['cfg']; - - $labels = &$opt['lbl']; - $testStatus = $cfg['status_labels'][$exec_info[0]['status']]; - - if(!isset($testerNameCache[$exec_info[0]['tester_id']])) - { - $testerNameCache[$exec_info[0]['tester_id']] = - gendocGetUserName($dbHandler, $exec_info[0]['tester_id']); - } - - $executionNotes = $my['opt']['show_notes'] ? $exec_info[0]['notes'] : ''; - - switch($exec_info[0]['execution_type']) - { - case TESTCASE_EXECUTION_TYPE_AUTO: - $etk = 'execution_type_auto'; - break; - - case TESTCASE_EXECUTION_TYPE_MANUAL: - default: - $etk = 'execution_type_manual'; - break; - } - - $td_colspan = ''; - if( !is_null($opt['colspan']) ) - { - $td_colspan .= ' colspan="' . $opt['colspan'] . '" '; - } - - // Check if CF exits for this BUILD - if(!is_null($buildCF) && isset($buildCF[$exec_info[0]['build_id']]) && - $buildCF[$exec_info[0]['build_id']] != '') - { - $out .= '' . - '' . $buildCF[$exec_info[0]['build_id']] . "\n"; - } - $out .= '' . $labels['tester'] .'' . - '' . $testerNameCache[$exec_info[0]['tester_id']] . "\n"; - - - $out .= '' . - '' . $labels['report_exec_result'] . ':' . - '' . $testStatus . "\n" . - - '' . - '' . $labels['execution_mode'] . ':' . - '' . $labels[$etk] . "\n"; - - if($cfg['exec_cfg']->features->exec_duration->enabled) - { - $out .= '' . - '' . $labels['execution_duration'] . ':' . - - '' . - (isset($exec_info[0]['execution_duration']) ? $exec_info[0]['execution_duration'] : " ") . - "\n"; - } - - if ($executionNotes != '') // show execution notes is not empty - { - $out .= ''.$labels['title_execution_notes'] . '' . - '' . nl2br($executionNotes) . "\n"; - } - - if( !is_null($its) ) - { - $bugs = get_bugs_for_exec($dbHandler,$its,$exec_info[0]['execution_id']); - - if ($bugs) - { - $bugString = ''; - foreach($bugs as $bugID => $bugInfo) - { - if($bugInfo['step_number'] != '') - { - $bugString .= $labels['step'] . ' ' . $bugInfo['step_number'] . ' - '; - } - $bugString .= $bugInfo['link_to_bts']."
    "; - } - $out .= '' . - $labels['bugs'] . '' . $bugString ."\n"; - - } - } - - return $out; -} - - -/** - * Render HTML header for a given platform. - * Also adds code to $options['tocCode'] - */ -function renderPlatformHeading($tocPrefix, $platform,&$options) -{ - $platformCfg = getWebEditorCfg('platform'); - $platformType = $platformCfg['type']; - $lbl = lang_get('platform'); - $name = htmlspecialchars($platform['name']); - $options['tocCode'] .= '

     

    ' . "$tocPrefix. $lbl" . ':' . $name . '

    '; - - $out = '

    $tocPrefix. $lbl: $name

    "; - // platform description is enabled with test plan description option settings - if ($options['showPlatformNotes']) - { - $out .= '
    ' . ( $platformType == 'none' ? nl2br($platform['notes']) : $platform['notes'] ) . "
    \n
    "; - } - return $out; -} - - -/** - * simple utility function, to avoid lot of copy and paste - * given an string, return an string useful to jump to an anchor on document - */ -function prefixToHTMLID($string2convert,$anchor_prefix='toc_') { - return $anchor_prefix . str_replace('.', '_', $string2convert); -} - -function renderTestProjectItem($info) { - $testProjectCfg = getWebEditorCfg('testproject'); - $testProjectType = $testProjectCfg['type']; - $lbl = init_labels(array('testproject' => null, 'context' => null, 'scope' => null)); - $out = ''; - $out .= renderSimpleChapter($lbl['testproject'] . ': ' . htmlspecialchars($info->tproject_name), - ($testProjectType == 'none' ? nl2br($info->tproject_scope) : $info->tproject_scope ) ); - return $out; -} - -/** - * - */ -function renderTestPlanItem($info) { - $testPlanCfg = getWebEditorCfg('testplan'); - $testPlanType = $testPlanCfg['type']; - $lbl = init_labels(array('testplan' => null, 'scope' => null)); - $out = ''; - $out .= renderSimpleChapter($lbl['testplan'] . ': ' . htmlspecialchars($info->testplan_name), - ($testPlanType == 'none' ? nl2br($info->testplan_scope) : $info->testplan_scope ), 'page-break-before: avoid;'); - return $out; -} - - - -/** - * - */ -function renderExecutionForPrinting(&$dbHandler, $baseHref, $id, $userObj = null) -{ - static $tprojectMgr; - static $tcaseMgr; - static $st; - - $out = ''; - - if(!$st) - { - $st = new stdClass(); - $st->tables = tlDBObject::getDBTables(array('executions','builds')); - - $tprojectMgr = new testproject($dbHandler); - $tcaseMgr = new testcase($dbHandler); - $tplanMgr = new testplan($dbHandler); - } - - $sql = " SELECT E.id AS execution_id, E.status, E.execution_ts, E.tester_id," . - " E.notes, E.build_id, E.tcversion_id,E.tcversion_number,E.testplan_id," . - " E.platform_id,E.execution_duration, " . - " B.name AS build_name, B.id AS build_id " . - " FROM {$st->tables['executions']} E " . - " JOIN {$st->tables['builds']} B ON B.id = E.build_id " . - " WHERE E.id = " . intval($id); - - $exec_info = $dbHandler->get_recordset($sql); - if( !is_null($exec_info) ) - { - $exec_info = $exec_info[0]; - - - $context['exec_id'] = intval($id); - - $context['tplan_id'] = $exec_info['testplan_id']; - $context['platform_id'] = $exec_info['platform_id']; - $context['build_id'] = $exec_info['build_id']; - $context['level'] = '??'; // ??? - - $node = $tprojectMgr->tree_manager->get_node_hierarchy_info($context['tplan_id']); - $context['prefix'] = $tprojectMgr->getTestCasePrefix($node['parent_id']); - $context['tproject_id'] = $node['parent_id']; - unset($tprojectMgr); - - // IMPORTANT DEVELOPMENT NOTICE - // Remember that on executions table we have following fields - // - // testplan_id - // tcversion_id - // tcversion_number - // - // a. (testplan_id ,tcversion_id) ARE LINK To testplan_tcversions table - // b. if user creates a new version of a LINKED AND EXECUTED test case - // when he/she updates test plan, ONLY tcversion_id is updated, - // while tcversion_number HAS ALWAYS the VERSION HUMAN READABLE NUMBER - // of executed version. - // - // Then if you want to access specification of executed test case version - // you need to proceed this way - // 1. with tcversion_id => get test case id - // 2. using test case id AND tcversion_number you access the data. - // - // Why is important to remember this? - // Because here we need to get data for renderTestCaseForPrinting - // - // The Cinematic Orchestra: To build a home Incubus: Wish you were here Mau Mau: La ola - $node = $tcaseMgr->tree_manager->get_node_hierarchy_info($exec_info['tcversion_id']); - - // get_by_id($id,$version_id = self::ALL_VERSIONS, $filters = null, $options=null) - $tcase = $tcaseMgr->get_by_id($node['parent_id'],null,array('version_number' => $exec_info['tcversion_number'])); - - - $renderOptions = array('toc' => 0,'body' => 1,'summary' => 1, 'header' => 0,'headerNumbering' => 0, - 'passfail' => 1, 'author' => 1, 'notes' => 1, 'requirement' => 1, 'keyword' => 1, - 'cfields' => 1, 'displayVersion' => 1, 'displayDates' => 1, - 'docType' => SINGLE_TESTCASE, 'importance' => 1, - 'step_exec_notes' => 1, 'step_exec_status' => 1); - - // need to change keys - $tcase = $tcase[0]; - $tcase['tcversion_id'] = $tcase['id']; - $tcase['id'] = $node['parent_id']; - - $env = new stdClass(); - $env->base_href = $baseHref; - $env->reportType = $renderOptions['docType']; - - $indentLevel = 100000; - - $context['user'] = $userObj; - $out .= renderTestCaseForPrinting($dbHandler,$tcase,$renderOptions,$env,$context,$indentLevel); - - $tplanInfo = $tplanMgr->get_by_id($context['tplan_id']); - $out .= '
    ' . lang_get('direct_link') . ':' . - $env->base_href . 'lnl.php?type=exec&id=' . intval($id) . - '&apikey=' . $tplanInfo['api_key'] . '
    '; - $exec_info = null; - } - - return $out; -} - -/** - * - */ -function renderBuildItem($info) { - $cfg = getWebEditorCfg('build'); - $buildType = $cfg['type']; - $lbl = init_labels(array('build' => null, 'notes' => null)); - $out = ''; - - $title = $lbl['build'] . ': ' . htmlspecialchars($info->build_name); - $out .= renderSimpleChapter($title, - ($buildType == 'none' ? nl2br($info->build_notes) : $info->build_notes), - 'page-break-before: avoid;'); - - return $out; + E.tcversion_number,E.testplan_id," . + " E.execution_type, E.execution_duration, " . + " B.name AS build_name " . " FROM {$st->tables['executions']} E " . + " JOIN {$st->tables['builds']} B ON B.id = E.build_id " . + " WHERE 1 = 1 "; + + // Bugfix to show only active builds in Test Report view + $sql .= "AND B.active = 1"; + + if (isset($context['exec_id'])) { + $sql .= " AND E.id=" . intval($context['exec_id']); + } else { + $sql .= " AND E.testplan_id = " . intval($tplan_id) . + " AND E.platform_id = " . intval($platform_id) . + " AND E.tcversion_id = " . intval( + $linkedItem[0]['tcversion_id']); + if ($build_id > 0) { + $sql .= " AND E.build_id = " . intval($build_id); + } else { + // We are looking for LATEST EXECUTION of CURRENT LINKED test case version + $sql .= " AND E.tcversion_number=" . + intval($linkedItem[0]['version']); + } + $sql .= " ORDER BY execution_id DESC"; + } + + $exec_info = $db->get_recordset($sql, null, 1); + + $getByID['tcversion_id'] = $linkedItem[0]['tcversion_id']; + $getByID['filters'] = null; + $linkedItem = null; + + if (! is_null($exec_info)) { + $getByID['tcversion_id'] = null; + $getByID['filters'] = array( + 'version_number' => $exec_info[0]['tcversion_number'] + ); + $tbuild_id = $exec_info[0]['build_id']; + if (isset($options['build_cfields']) && $options['build_cfields'] && + ! isset($st->buildCfields[$tbuild_id])) { + $st->buildCfields[$tbuild_id] = $st->build_mgr->html_table_of_custom_field_values( + $tbuild_id, $tprojectID); + } + } + } + + $tcInfo = (array) $st->tc_mgr->get_by_id($id, $getByID['tcversion_id'], + $getByID['filters'], + array( + 'renderGhost' => true, + 'renderImageInline' => true + )); + + if (! empty($tcInfo)) { + $tcInfo = $tcInfo[0]; + } else { + $msg = basename(__FILE__) . ' >' . 'Line: ' . __LINE__ . ' > ' . + 'Function: ' . __FUNCTION__ . ' > ' . + "Failed to get Test Case Info for ID=" . $id; + + if ($getByID['tcversion_id'] == testcase::ALL_VERSIONS) { + $msg .= " ALL VERSIONS "; + } elseif ($getByID['tcversion_id'] == testcase::LATEST_VERSION) { + $msg .= " LATEST VERSION "; + } else { + $msg .= " tcversion id:" . $getByID['tcversion_id']; + } + + tLog($msg, 'ERROR'); + + throw new Exception($msg, 1); + } + + $tcVersionID = $tcInfo['id']; + + $external_id = $tcase_prefix . $tcInfo['tc_external_id']; + $name = htmlspecialchars($node['name']); + + $cfields = array( + 'specScope' => null, + 'execScope' => null + ); + if ($options['cfields']) { + // Get custom fields that has specification scope + // Custom Field values at Test Case VERSION Level + foreach ($st->locationFilters as $fkey => $fvalue) { + $cfields['specScope'][$fkey] = $st->tc_mgr->html_table_of_custom_field_values( + $id, 'design', $fvalue, null, $tplan_id, $tprojectID, + $st->cfieldFormatting, $tcInfo['id']); + } + + if (! is_null($exec_info)) { + $cfields['execScope'] = $st->tc_mgr->html_table_of_custom_field_values( + $tcInfo['id'], 'execution', null, $exec_info[0]['execution_id'], + $tplan_id, $tprojectID, $st->cfieldFormatting); + } + } + + if ($options['toc']) { + // EXTERNAL ID added + $options['tocCode'] .= '

    ' . + htmlspecialchars($external_id) . ": " . $name . '

    '; + $code .= ''; + } + + $code .= '

     

    table_style . '>'; + $code .= '\n"; + + if ($options['author']) { + $code .= '' . + '\n"; + + // add updater if available and differs from author OR forced + if ($tcInfo['updater_id'] > 0 && $force['displayLastEdit'] > 0 || + ($tcInfo['updater_id'] != $tcInfo['author_id'])) { + $code .= '' . '\n"; + } + } + + if ($options['body'] || $options['summary']) { + $tcase_pieces = array( + 'summary' + ); + } + + if ($options['body']) { + $tcase_pieces[] = 'preconditions'; + } + + if ($options['body'] || $options['step_exec_notes'] || + $options['step_exec_status']) { + $tcase_pieces[] = 'steps'; + } + + if (! is_null($tcase_pieces)) { + // Check user rights in order to understand if can delete attachments here + // function hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) + // $tplan_id = isset($context['tplan_id']) ? $context['tplan_id'] : 0; + // $tprojectID = isset($context['tproject_id']) ? $context['tproject_id'] : 0; + // $canManageAttachments = false; + // if(isset($context['user']) && !is_null($context['user'])) { + // $canManageAttachments = $context['user']->hasRight($db,'testplan_execute',$tprojectID,$tplan_id); + // } + + // Multiple Test Case Steps Feature + foreach ($tcase_pieces as $key) { + if ($key == 'steps') { + if (isset($cfields['specScope']['before_steps_results'])) { + $code .= $cfields['specScope']['before_steps_results']; + } + + if (! is_null($tcInfo[$key]) && $tcInfo[$key] != '') { + $td_colspan = 3; + $code .= '' . '' . + '' . ''; + + $sxni = null; + if ($opt['step_exec_notes'] || $opt['step_exec_status']) { + $sxni = $st->tc_mgr->getStepsExecInfo( + $exec_info[0]['execution_id']); + + if ($opt['step_exec_notes']) { + $td_colspan ++; + $code .= ''; + } + + if ($opt['step_exec_status']) { + $td_colspan ++; + $code .= ''; + } + } + + $code .= ''; + + $loop2do = count($tcInfo[$key]); + for ($ydx = 0; $ydx < $loop2do; $ydx ++) { + $code .= '' . '' . '' . ''; + + $nike = ! is_null($sxni) && + isset($sxni[$tcInfo[$key][$ydx]['id']]) && + ! is_null($sxni[$tcInfo[$key][$ydx]['id']]); + if ($opt['step_exec_notes']) { + $code .= ''; + } + + if ($opt['step_exec_status']) { + $code .= ''; + } + $code .= ''; + + // Attachment management + if ($getExecutions && + isset($sxni[$tcInfo[$key][$ydx]['id']])) { + $attachInfo = getAttachmentInfos($st->docRepo, + $sxni[$tcInfo[$key][$ydx]['id']]['id'], + $st->tables['execution_tcsteps'], true, 1); + + if (! is_null($attachInfo)) { + $code .= ''; + } + } + } + } + } else { + // disable the field if it's empty + if ($tcInfo[$key] != '') { + $code .= '"; + } + } + } + } + + $code .= '' . + '\n"; + + // estimated execution time + $code .= '' . '\n"; + + if (isset($options['importance']) && $options['importance']) { + $code .= '' . + '\n"; + } + + // print priority when printing test plan + if (isset($options['priority']) && $options['priority']) { + // Get priority of this tc version for this test plan by using testplanUrgency class. + // Is there maybe a better method than this one? + $filters = array( + 'tcversion_id' => $tcInfo['id'] + ); + $opt = array( + 'details' => 'tcversion' + ); + $prio_info = $st->tplan_urgency->getPriority($tplan_id, $filters, $opt); + $prio = $prio_info[$tcInfo['id']]['priority_level']; + + $code .= '' . + '\n"; + } + + // Spacer + $code .= '"; + $code .= $cfields['specScope']['standard_location'] . $cfields['execScope']; + + $cfields = null; + $prio_info = null; + + // since 1.9.18 + // TC relations has been migrated to TCV relations + $greenCard = array( + 'tcase_id' => $id, + 'tcversion_id' => $tcVersionID + ); + $relSet = $st->tc_mgr->getTCVersionRelations($greenCard); + + if (! is_null($relSet['relations'])) { + // $fx = str_repeat(' ',5); // MAGIC allowed + $code .= ''; + + $code .= ''; + } + $relSet = null; + + // collect REQ for Test Case Version + if (isset($options['requirement'])) { + // Coverage Links REQV to TCV + $requirements = (array) $st->req_mgr->getActiveForTCVersion( + $tcVersionID); + $code .= '\n"; + } + $requirements = null; + + // collect keywords for TC VERSION + if (isset($options['keyword'])) { + $code .= ''; + $code .= '\n"; + } + $kwSet = null; + + // collect platforms for TC VERSION + if (isset($options['platform'])) { + $code .= ''; + $code .= '\n"; + } + $itSet = null; + + // Attachments + $attachSet = (array) $st->tc_mgr->getAttachmentInfos($tcVersionID); + if (! empty($attachSet)) { + $code .= ''; + $code .= '"; + } + $attachSet = null; + + // generate test results data for test report + if ($options['passfail']) { + $tsp = ($cfg['tableColspan'] - 1); + $code .= '' . + '' . '\n"; + + $bn = ''; + switch ($env->reportType) { + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + $ib = $st->build_mgr->get_by_id($build_id); + $bn = htmlspecialchars($ib['name']); + break; + + case DOC_TEST_PLAN_EXECUTION: + if ($exec_info) { + $bn = htmlspecialchars($exec_info[0]['build_name']); + } + break; + } + + /* Build name */ + if ($bn != '') { + $code .= '' . '\n"; + + if (is_null($exec_info) && ! is_null($buildCfields) && + isset($st->buildCfields[$build_id]) && + $st->buildCfields[$build_id] != '') { + $code .= '' . '\n"; + } + } + + if (isset($node['assigned_to'])) { + $crew = explode(',', $node['assigned_to']); + $code .= '' . + '\n"; + } + + if ($exec_info) { + $settings['cfg'] = $cfg; + $settings['lbl'] = $labels; + $settings['opt'] = array( + 'show_notes' => $options['notes'] + ); + $settings['colspan'] = $cfg['tableColspan'] - 1; + + $code .= buildTestExecResults($db, $its, $exec_info, $settings, + $st->buildCfields); + + // Get Execution Attachments + // Need to fixed in a better way + // Seems that when creating attachment I use 'executions' + // instead of real table name. + // Name will be different is TABLE PREFIX is configured + // + $execAttachInfo = getAttachmentInfos($st->docRepo, + $exec_info[0]['execution_id'], 'executions', true, 1); + + if (! is_null($execAttachInfo)) { + $code .= ''; + } + } else { + $code .= '' . '\n"; + } + $execAttachInfo = null; + $exec_info = null; + } + + $code .= "
    ' . + $labels['test_case'] . " " . htmlspecialchars($external_id) . ": " . + $name; + + // add test case version + switch ($env->reportType) { + case DOC_TEST_PLAN_DESIGN: + $version_number = isset($node['version']) ? $node['version'] : $tcInfo['version']; + break; + + case DOC_TEST_PLAN_EXECUTION: + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + $version_number = $tcInfo['version']; + break; + + default: + $version_number = $tcInfo['version']; + break; + } + + if ($cfg['doc']->tc_version_enabled || $force['displayVersion']) { + $code .= ' ' . + $cfg['gui']->version_separator_open . $labels['version'] . + $cfg['gui']->title_separator_1 . $version_number . + $cfg['gui']->version_separator_close . ''; + } + $code .= "
    ' . + '' . $labels['author'] . ':' . + gendocGetUserName($db, $tcInfo['author_id']); + + if (isset($options['displayDates']) && $options['displayDates']) { + $dummy = null; + $code .= ' - ' . + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', + $tcInfo['creation_ts']); + } + $code .= "
    ' . '' . $labels['last_edit'] . + ':' . gendocGetUserName($db, $tcInfo['updater_id']); + + if (isset($options['displayDates']) && $options['displayDates']) { + $dummy = null; + $code .= ' - ' . + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', + $tcInfo['modification_ts']); + } + $code .= "
    ' . + $labels['step_number'] . ':' . $labels['step_actions'] . + ':' . + $labels['expected_results'] . ':' . + $labels['step_exec_notes'] . ':' . + $labels['step_exec_status'] . ':
    ' . + $tcInfo[$key][$ydx]['step_number'] . '' . + ($stepDesignType == 'none' ? nl2br( + $tcInfo[$key][$ydx]['actions']) : $tcInfo[$key][$ydx]['actions']) . + '' . + ($stepDesignType == 'none' ? nl2br( + $tcInfo[$key][$ydx]['expected_results']) : $tcInfo[$key][$ydx]['expected_results']) . + ''; + if ($nike) { + $code .= nl2br( + $sxni[$tcInfo[$key][$ydx]['id']]['notes']); + } + $code .= ''; + if ($nike) { + $sk = $sxni[$tcInfo[$key][$ydx]['id']]; + if (isset($statusL10N[$sk['status']])) { + $code .= $statusL10N[$sk['status']]; + } + } + $code .= '
    '; + $code .= '' . $labels['exec_attachments'] . + '
    '; + + foreach ($attachInfo as $fitem) { + $code .= '
    '; + + $code .= ''; + + $safeItemID = intval($fitem['id']); + $code .= ''; + + $safeFileName = htmlspecialchars( + $fitem['file_name']); + $sec = hash('sha256', $fitem['file_name']); + $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . + $sec . '&id=' . $safeItemID; + + if ($fitem['is_image']) { + $code .= "
  • {$safeFileName}
  • "; + $pathname = $st->repoDir . + $fitem['file_path']; + list ($iWidth, $iHeight, ,) = getimagesize( + $pathname); + + // Sorry by MAGIC Numbers + if ($iWidth > 900 || $iHeight > 700) { + if ($iWidth > $iHeight) { + $imgDiff = round($iWidth / 600); + } else { + $imgDiff = round($iHeight / 450); + } + $iWidth /= $imgDiff; + $iHeight /= $imgDiff; + } + + $iDim = ' width=' . $iWidth . ' height=' . + $iHeight; + $code .= '
  • '; + } else { + $code .= '
  • ' . + $safeFileName . ''; + } + $code .= '
  • '; + } + $code .= '
    ' . $labels[$key] . + ':
    ' . + ($designType == 'none' ? nl2br($tcInfo[$key]) : $tcInfo[$key]) . + "
    ' . + '' . $labels['execution_type'] . ':'; + + // This is what have been choosen DURING DESIGN, but may be we can choose at DESIGN + // manual and the execute AUTO, or may be choose AUTO and execute MANUAL. + // After report on MANTIS, seems that we need to provide in output two values: + // DESIGN execution type + // EXECUTION execution type + switch ($tcInfo['execution_type']) { + case TESTCASE_EXECUTION_TYPE_AUTO: + $code .= $labels['execution_type_auto']; + break; + + case TESTCASE_EXECUTION_TYPE_MANUAL: + default: + $code .= $labels['execution_type_manual']; + break; + } + $code .= "
    ' . + '' . $labels['estimated_execution_duration'] . + ':' . + $tcInfo['estimated_exec_duration']; + $code .= "
    ' . + '' . $labels['importance'] . ':' . + $cfg['importance'][$tcInfo['importance']]; + $code .= "
    ' . + '' . $labels['priority'] . ':' . + $cfg['priority'][$prio]; + $code .= "
    ' . "
    ' . $labels['relations'] . + ''; + for ($rdx = 0; $rdx < $relSet['num_relations']; $rdx ++) { + if ($relSet['relations'][$rdx]['source_id'] == $id) { + $ak = 'source_localized'; + } else { + $ak = 'destination_localized'; + } + + $code .= htmlspecialchars($relSet['relations'][$rdx][$ak]) . ' - ' . + htmlspecialchars( + $relSet['relations'][$rdx]['related_tcase']['fullExternalID']) . + ':' . + htmlspecialchars( + $relSet['relations'][$rdx]['related_tcase']['name']) . + "  [{$labels['version']}:" . + $relSet['relations'][$rdx]['related_tcase']['version'] . "]"; + } + $code .= '
    ' . $labels['reqs'] . ''; + $code .= ''; + + if (count($requirements)) { + foreach ($requirements as $req) { + $code .= htmlspecialchars( + $req['req_doc_id'] . ": " . $req['title']) . " " . + $cfg['gui']->version_separator_open . + "{$labels['version']}: {$req['version']}" . + $cfg['gui']->version_separator_close . "
    "; + } + } else { + $code .= ' ' . $labels['none'] . '
    '; + } + $code .= "
    ' . $labels['keywords'] . + ':'; + + $kwSet = (array) $st->tc_mgr->getKeywords($id, $tcVersionID, null, + array( + 'fields' => 'keyword_id,KW.keyword' + )); + if (count($kwSet)) { + foreach ($kwSet as $kw) { + $code .= htmlspecialchars($kw['keyword']) . "
    "; + } + } else { + $code .= ' ' . $labels['none'] . '
    '; + } + $code .= "
    ' . $labels['platforms'] . + ':'; + + $itSet = (array) $st->tc_mgr->getPlatforms($id, $tcVersionID, null, + array( + 'fields' => 'platform_id,PL.name' + )); + if (count($itSet)) { + foreach ($itSet as $it) { + $code .= htmlspecialchars($it['name']) . "
    "; + } + } else { + $code .= ' ' . $labels['none'] . '
    '; + } + $code .= "
    ' . $labels['attached_files'] . + '
      '; + + foreach ($attachSet as $item) { + $fname = ""; + if ($item['title']) { + $fname .= htmlspecialchars($item['title']) . " : "; + } + $fname .= htmlspecialchars($item['file_name']); + $code .= "
    • $fname
    • "; + + $sec = hash('sha256', $item['file_name']); + + $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . $sec . + '&id=' . $item['id']; + + if ($item['is_image']) { + $pathname = $st->repoDir . $item['file_path']; + list ($iWidth, $iHeight, ,) = getimagesize($pathname); + + $iDim = ' width=' . $iWidth . ' height=' . $iHeight; + $code .= '
    • ' . '
    • '; + } else { + $code .= '
    • ' . ' ' . htmlspecialchars($item['file_name']) . + '
    • '; + } + } + $code .= "
    ' . + $labels['execution_details'] . '' . " " . "
    ' . $labels['build'] . '' . $bn . "
    ' . + $st->buildCfields[$build_id] . "
    ' . $labels['assigned_to'] . ''; + + $xdx = 0; + foreach ($crew as $mm) { + if ($xdx != 0) { + $code .= ','; + } + $xdx = - 1; + echo $mm . '
    '; + $code .= gendocGetUserName($db, $mm); + } + $code .= "
    '; + $code .= '' . $labels['exec_attachments'] . '
    '; + foreach ($execAttachInfo as $fitem) { + $sec = hash('sha256', $fitem['file_name']); + + $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . + $sec . '&id=' . $fitem['id']; + + $safeFileName = htmlspecialchars($fitem['file_name']); + if ($fitem['is_image']) { + $code .= "
  • {$safeFileName}
  • "; + + $pathname = $st->repoDir . $item['file_path']; + list ($iWidth, $iHeight, ,) = getimagesize($pathname); + + // Sorry by MAGIC Numbers + if ($iWidth > 900 || $iHeight > 700) { + if ($iWidth > $iHeight) { + $imgDiff = round($iWidth / 600); + } else { + $imgDiff = round($iHeight / 450); + } + $iWidth /= $imgDiff; + $iHeight /= $imgDiff; + } + + $iDim = ' width=' . $iWidth . ' height=' . $iHeight; + $code .= '
  • ' . '
  • '; + } else { + $code .= '
  • ' . ' ' . $safeFileName . '
  • '; + } + } + $code .= '
    ' . '' . + $labels['report_exec_result'] . '' . + $labels["test_status_not_run"] . "
    \n
    \n"; + return $code; +} + +/** + */ +function renderTOC(&$options) +{ + $code = ''; + $options['toc_numbers'][1] = 0; + if ($options['toc']) { + $options['tocCode'] = '

    ' . + lang_get('title_toc') . '

    '; + $code .= "{{INSERT_TOC}}"; + } + + return $code; +} + +/* + * function: renderTestSuiteNodeForPrinting + * args : + * returns: + * + * ATTENTION: This variables: $tocPrefix,$indentLevel + * + * can not be passed on a data type that pass by reference + * because need to have LOCAL life during recursion. + * Having added it as members of $env and $context has generated a BUG + */ +function renderTestSuiteNodeForPrinting(&$db, &$node, $env, &$options, $context, + $tocPrefix, $indentLevel) +{ + static $tsuite_mgr; + static $l10n; + static $title_separator; + static $cfieldFormatting; + static $getOpt; + + $designCfg = getWebEditorCfg('design'); + $designType = $designCfg['type']; + + if (is_null($l10n)) { + $repoDir = config_get('repositoryPath'); + $tsuite_mgr = new testsuite($db); + + $l10n = array( + 'test_suite' => 'test_suite', + 'details' => 'details', + 'attached_files' => 'attached_files' + ); + + $l10n = init_labels($l10n); + + $title_separator = config_get('gui_title_separator_1'); + $cfieldFormatting = array( + 'table_css_style' => 'class="cf"' + ); + + $getOpt['getByID'] = array( + 'fields' => ' TS.id,TS.details ', + 'renderImageInline' => true + ); + } + + $code = null; + $name = isset($node['name']) ? htmlspecialchars($node['name']) : ''; + $cfields = array( + 'design' => '' + ); + + $docHeadingNumbering = $options['headerNumbering'] ? ($tocPrefix . ".") : ''; + + if ($options['toc']) { + $spacing = ($indentLevel == 2 && $tocPrefix != 1) ? "
    " : ""; + $options['tocCode'] .= $spacing . '

    ' . '' . $docHeadingNumbering . $name . + "

    \n"; + $code .= "\n"; + } + + // we would like to have html top heading H1 - H6 + $docHeadingLevel = ($indentLevel - 1); + + // Remember that only H1 to H6 exists + $docHeadingLevel = ($docHeadingLevel > 6) ? 6 : $docHeadingLevel; + $docHeadingLevel = ($docHeadingLevel < 1) ? 1 : $docHeadingLevel; + + $code .= "" . $docHeadingNumbering . + $l10n['test_suite'] . $title_separator . $name . + "\n"; + + // ----- get Test Suite text ----------------- + if ($options['header']) { + + $tInfo = $tsuite_mgr->get_by_id($node['id'], $getOpt['getByID']); + if ($tInfo['details'] != '') { + $code .= '
    ' . + ($designType == 'none' ? nl2br($tInfo['details']) : $tInfo['details']) . + '
    '; + } + $tInfo = null; + + $attachSet = (array) $tsuite_mgr->getAttachmentInfos($node['id']); + if (! empty($attachSet)) { + $code .= ''; + $code .= ''; + $code .= '"; + $code .= "
    ' . + $l10n['attached_files'] . '
     
      '; + foreach ($attachSet as $item) { + $fname = ""; + if ($item['title']) { + $fname .= htmlspecialchars($item['title']) . " : "; + } + $fname .= htmlspecialchars($item['file_name']); + $code .= "
    • $fname
    • "; + + $sec = hash('sha256', $item['file_name']); + $cmout = 'lib/attachments/attachmentdownload.php?skipCheck=' . + $sec . '&id=' . $item['id']; + + if ($item['is_image']) { + $pathname = $repoDir . $item['file_path']; + list ($iWidth, $iHeight, ,) = getimagesize($pathname); + $iDim = ' width=' . $iWidth . ' height=' . $iHeight; + $code .= '
    • ' . '
    • '; + } else { + $code .= '
    • ' . ' ' . + htmlspecialchars($item['file_name']) . '
    • '; + } + } + $code .= "
    "; + } + $attachSet = null; + + // get Custom fields + // Attention: for test suites custom fields can not be edited during execution, + // then we need to get just custom fields with scope 'design' + foreach ($cfields as $key => $value) { + $cfields[$key] = $tsuite_mgr->html_table_of_custom_field_values( + $node['id'], $key, null, $context['tproject_id'], + $cfieldFormatting); + if ($cfields[$key] != "") { + $code .= '

    ' . $cfields[$key] . '

    '; + } + } + $cfields = null; + } + return $code; +} + +/* + * function: renderTestPlanForPrinting + * args: + * returns: + * + * @internal revisions: + */ +function renderTestPlanForPrinting(&$db, &$node, &$options, $env, $context) +{ + $tProjectMgr = new testproject($db); + $context['prefix'] = $tProjectMgr->getTestCasePrefix( + $context['tproject_id']); + return renderTestSpecTreeForPrinting($db, $node, $options, $env, $context, + $env->tocPrefix, $context['level']); +} + +/** + * Render HTML for estimated and real execute duration based on contribution + * + * @param array $statistics + * @return string HTML code + */ +function renderTestDuration($statistics, $platform_id = 0) +{ + static $ecfg; + + $output = ''; + + if (! $ecfg) { + $ecfg = config_get('exec_cfg'); + } + $estimatedTimeAvailable = isset($statistics['estimated_execution']) && + ! is_null($statistics['estimated_execution']); + + if ($ecfg->features->exec_duration->enabled) { + $realTimeAvailable = isset($statistics['real_execution']) && + ! is_null($statistics['real_execution']['platform'][$platform_id]); + } else { + $realTimeAvailable = null; + } + + if ($estimatedTimeAvailable || $realTimeAvailable) { + if ($estimatedTimeAvailable) { + $estimated_minutes = $statistics['estimated_execution']['platform'][$platform_id]['minutes']; + $tcase_qty = $statistics['estimated_execution']['platform'][$platform_id]['tcase_qty']; + if ($estimated_minutes > 0) { + if ($estimated_minutes > 60) { + $estimated_string = lang_get('estimated_time_hours') . + round($estimated_minutes / 60, 2); + } else { + $estimated_string = lang_get('estimated_time_min') . + $estimated_minutes; + } + $estimated_string = sprintf($estimated_string, $tcase_qty); + $output .= '

    ' . $estimated_string . "

    \n"; + } + } + + if ($realTimeAvailable) { + $real_minutes = $statistics['real_execution']['platform'][$platform_id]['minutes']; + $tcase_qty = $statistics['real_execution']['platform'][$platform_id]['tcase_qty']; + if ($real_minutes > 0) { + if ($real_minutes > 60) { + $real_string = lang_get('real_time_hours') . + round($real_minutes / 60, 2); + } else { + $real_string = lang_get('real_time_min') . $real_minutes; + } + $real_string = sprintf($real_string, $tcase_qty); + $output .= '

    ' . $real_string . "

    \n"; + } + } + } + + if ($output != '') { + $output = "
    \n" . $output . "
    \n"; + } + + return $output; +} + +/** + * get final markup for HTML + * + * @return string HTML + */ +function renderEOF() +{ + return "\n\n"; +} + +/** + * compose html text for metrics (meantime estimated time only) + * + * @return string html + */ +function buildTestPlanMetrics($statistics, $platform_id = 0) +{ + static $lbl; + if (! $lbl) { + $lbl = lang_get('execution_time_metrics'); + } + + $output = ''; + $dummy = renderTestDuration($statistics, $platform_id); + if ($dummy != '') { + $output = '

    ' . $lbl . "

    \n" . $dummy; + } + return $output; +} + +/** + * utility function to allow easy reading of code + * on renderTestCaseForPrinting() + * + * @return array with configuration and labels + * + */ +function initRenderTestCaseCfg($options) +{ + $config = null; + $config['firstColWidth'] = '20%'; + $config['doc'] = config_get('document_generator'); + $config['gui'] = config_get('gui'); + $config['testcase'] = config_get('testcase_cfg'); + $config['results'] = config_get('results'); + $config['exec_cfg'] = config_get('exec_cfg'); + + $config['tableColspan'] = 4; + if (isset($options['step_exec_notes']) && $options['step_exec_notes']) { + $config['tableColspan'] ++; + } + if (isset($options['step_exec_status']) && $options['step_exec_status']) { + $config['tableColspan'] ++; + } + + foreach ($config['results']['code_status'] as $key => $value) { + $config['status_labels'][$key] = "check your \$tlCfg->results['status_label'] configuration "; + if (isset($config['results']['status_label'][$value])) { + $config['status_labels'][$key] = lang_get( + $config['results']['status_label'][$value]); + } + } + + $labelsKeys = array( + 'last_exec_result', + 'report_exec_result', + 'execution_details', + 'execution_mode', + 'version', + 'bugs', + 'tester', + 'title_execution_notes', + 'none', + 'reqs', + 'author', + 'summary', + 'steps', + 'expected_results', + 'build', + 'test_case', + 'keywords', + 'test_status_not_run', + 'not_aplicable', + 'preconditions', + 'step', + 'step_number', + 'step_actions', + 'last_edit', + 'created_on', + 'execution_type', + 'execution_type_manual', + 'execution_type_auto', + 'importance', + 'relations', + 'estimated_execution_duration', + 'step_exec_notes', + 'step_exec_status', + 'exec_attachments', + 'alt_delete_attachment', + 'assigned_to', + 'high_importance', + 'medium_importance', + 'low_importance', + 'execution_duration', + 'priority', + 'high_priority', + 'medium_priority', + 'low_priority', + 'attached_files', + 'platforms' + ); + + $labelsQty = count($labelsKeys); + for ($idx = 0; $idx < $labelsQty; $idx ++) { + $labels[$labelsKeys[$idx]] = lang_get($labelsKeys[$idx]); + } + + $config['importance'] = array( + HIGH => $labels['high_importance'], + MEDIUM => $labels['medium_importance'], + LOW => $labels['low_importance'] + ); + + $config['priority'] = array( + HIGH => $labels['high_priority'], + MEDIUM => $labels['medium_priority'], + LOW => $labels['low_priority'] + ); + + $statusL10N = null; + foreach ($config['results']['code_status'] as $vc => $vstat) { + if (isset($config['results']['status_label_for_exec_ui'][$vstat])) { + $statusL10N[$vc] = lang_get( + $config['results']['status_label_for_exec_ui'][$vstat]); + } + } + + return array( + $config, + $labels, + $statusL10N + ); +} + +/** + * + * @internal revisions + * @since 1.9.12 + * + * + */ +function buildTestExecResults(&$dbHandler, &$its, $exec_info, $opt, + $buildCF = null) +{ + static $testerNameCache; + $out = ''; + + $my['opt'] = array( + 'show_notes' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $cfg = &$opt['cfg']; + + $labels = &$opt['lbl']; + $testStatus = $cfg['status_labels'][$exec_info[0]['status']]; + + if (! isset($testerNameCache[$exec_info[0]['tester_id']])) { + $testerNameCache[$exec_info[0]['tester_id']] = gendocGetUserName( + $dbHandler, $exec_info[0]['tester_id']); + } + + $executionNotes = $my['opt']['show_notes'] ? $exec_info[0]['notes'] : ''; + + switch ($exec_info[0]['execution_type']) { + case TESTCASE_EXECUTION_TYPE_AUTO: + $etk = 'execution_type_auto'; + break; + + case TESTCASE_EXECUTION_TYPE_MANUAL: + default: + $etk = 'execution_type_manual'; + break; + } + + $td_colspan = ''; + if (! is_null($opt['colspan'])) { + $td_colspan .= ' colspan="' . $opt['colspan'] . '" '; + } + + // Check if CF exits for this BUILD + if (! is_null($buildCF) && isset($buildCF[$exec_info[0]['build_id']]) && + $buildCF[$exec_info[0]['build_id']] != '') { + $out .= '' . '' . + $buildCF[$exec_info[0]['build_id']] . "\n"; + } + $out .= '' . + $labels['tester'] . '' . '' . + $testerNameCache[$exec_info[0]['tester_id']] . "\n"; + + $out .= '' . '' . + $labels['report_exec_result'] . ':' . '' . $testStatus . "\n" . '' . + '' . $labels['execution_mode'] . ':' . + '' . $labels[$etk] . "\n"; + + if ($cfg['exec_cfg']->features->exec_duration->enabled) { + $out .= '' . '' . + $labels['execution_duration'] . ':' . '' . + (isset($exec_info[0]['execution_duration']) ? $exec_info[0]['execution_duration'] : " ") . + "\n"; + } + + if ($executionNotes != '') // show execution notes is not empty + { + $out .= '' . + $labels['title_execution_notes'] . '' . '' . nl2br($executionNotes) . "\n"; + } + + if (! is_null($its)) { + $bugs = get_bugs_for_exec($dbHandler, $its, + $exec_info[0]['execution_id']); + + if ($bugs) { + $bugString = ''; + foreach ($bugs as $bugInfo) { + if ($bugInfo['step_number'] != '') { + $bugString .= $labels['step'] . ' ' . $bugInfo['step_number'] . + ' - '; + } + $bugString .= $bugInfo['link_to_bts'] . "
    "; + } + $out .= '' . + $labels['bugs'] . '' . $bugString . + "\n"; + } + } + + return $out; +} + +/** + * Render HTML header for a given platform. + * Also adds code to $options['tocCode'] + */ +function renderPlatformHeading($tocPrefix, $platform, &$options) +{ + $platformCfg = getWebEditorCfg('platform'); + $platformType = $platformCfg['type']; + $lbl = lang_get('platform'); + $name = htmlspecialchars($platform['name']); + $options['tocCode'] .= '

     

    ' . "$tocPrefix. $lbl" . ':' . $name . + '

    '; + + $out = '

    $tocPrefix. $lbl: $name

    "; + // platform description is enabled with test plan description option settings + if ($options['showPlatformNotes']) { + $out .= '
    ' . + ($platformType == 'none' ? nl2br($platform['notes']) : $platform['notes']) . + "
    \n
    "; + } + return $out; +} + +/** + * simple utility function, to avoid lot of copy and paste + * given an string, return an string useful to jump to an anchor on document + */ +function prefixToHTMLID($string2convert, $anchor_prefix = 'toc_') +{ + return $anchor_prefix . str_replace('.', '_', $string2convert); +} + +function renderTestProjectItem($info) +{ + $testProjectCfg = getWebEditorCfg('testproject'); + $testProjectType = $testProjectCfg['type']; + $lbl = init_labels( + array( + 'testproject' => null, + 'context' => null, + 'scope' => null + )); + $out = ''; + $out .= renderSimpleChapter( + $lbl['testproject'] . ': ' . htmlspecialchars($info->tproject_name), + ($testProjectType == 'none' ? nl2br($info->tproject_scope) : $info->tproject_scope)); + return $out; +} + +/** + */ +function renderTestPlanItem($info) +{ + $testPlanCfg = getWebEditorCfg('testplan'); + $testPlanType = $testPlanCfg['type']; + $lbl = init_labels(array( + 'testplan' => null, + 'scope' => null + )); + $out = ''; + $out .= renderSimpleChapter( + $lbl['testplan'] . ': ' . htmlspecialchars($info->testplan_name), + ($testPlanType == 'none' ? nl2br($info->testplan_scope) : $info->testplan_scope), + 'page-break-before: avoid;'); + return $out; +} + +/** + */ +function renderExecutionForPrinting(&$dbHandler, $baseHref, $id, $userObj = null) +{ + static $tprojectMgr; + static $tcaseMgr; + static $st; + + $out = ''; + + if (! $st) { + $st = new stdClass(); + $st->tables = tlDBObject::getDBTables(array( + 'executions', + 'builds' + )); + + $tprojectMgr = new testproject($dbHandler); + $tcaseMgr = new testcase($dbHandler); + $tplanMgr = new testplan($dbHandler); + } + + $sql = " SELECT E.id AS execution_id, E.status, E.execution_ts, E.tester_id," . + " E.notes, E.build_id, E.tcversion_id,E.tcversion_number,E.testplan_id," . + " E.platform_id,E.execution_duration, " . + " B.name AS build_name, B.id AS build_id " . + " FROM {$st->tables['executions']} E " . + " JOIN {$st->tables['builds']} B ON B.id = E.build_id " . + " WHERE E.id = " . intval($id); + + $exec_info = $dbHandler->get_recordset($sql); + if (! is_null($exec_info)) { + $exec_info = $exec_info[0]; + + $context['exec_id'] = intval($id); + + $context['tplan_id'] = $exec_info['testplan_id']; + $context['platform_id'] = $exec_info['platform_id']; + $context['build_id'] = $exec_info['build_id']; + $context['level'] = '??'; // ??? + + $node = $tprojectMgr->tree_manager->get_node_hierarchy_info( + $context['tplan_id']); + $context['prefix'] = $tprojectMgr->getTestCasePrefix($node['parent_id']); + $context['tproject_id'] = $node['parent_id']; + unset($tprojectMgr); + + // IMPORTANT DEVELOPMENT NOTICE + // Remember that on executions table we have following fields + // + // testplan_id + // tcversion_id + // tcversion_number + // + // a. (testplan_id ,tcversion_id) ARE LINK To testplan_tcversions table + // b. if user creates a new version of a LINKED AND EXECUTED test case + // when he/she updates test plan, ONLY tcversion_id is updated, + // while tcversion_number HAS ALWAYS the VERSION HUMAN READABLE NUMBER + // of executed version. + // + // Then if you want to access specification of executed test case version + // you need to proceed this way + // 1. with tcversion_id => get test case id + // 2. using test case id AND tcversion_number you access the data. + // + // Why is important to remember this? + // Because here we need to get data for renderTestCaseForPrinting + // + // The Cinematic Orchestra: To build a home Incubus: Wish you were here Mau Mau: La ola + $node = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $exec_info['tcversion_id']); + + // get_by_id($id,$version_id = self::ALL_VERSIONS, $filters = null, $options=null) + $tcase = $tcaseMgr->get_by_id($node['parent_id'], null, + array( + 'version_number' => $exec_info['tcversion_number'] + )); + + $renderOptions = array( + 'toc' => 0, + 'body' => 1, + 'summary' => 1, + 'header' => 0, + 'headerNumbering' => 0, + 'passfail' => 1, + 'author' => 1, + 'notes' => 1, + 'requirement' => 1, + 'keyword' => 1, + 'cfields' => 1, + 'displayVersion' => 1, + 'displayDates' => 1, + 'docType' => SINGLE_TESTCASE, + 'importance' => 1, + 'step_exec_notes' => 1, + 'step_exec_status' => 1 + ); + + // need to change keys + $tcase = $tcase[0]; + $tcase['tcversion_id'] = $tcase['id']; + $tcase['id'] = $node['parent_id']; + + $env = new stdClass(); + $env->base_href = $baseHref; + $env->reportType = $renderOptions['docType']; + + $indentLevel = 100000; + + $context['user'] = $userObj; + $out .= renderTestCaseForPrinting($dbHandler, $tcase, $renderOptions, + $env, $context, $indentLevel); + + $tplanInfo = $tplanMgr->get_by_id($context['tplan_id']); + $out .= '
    ' . lang_get('direct_link') . ':' . $env->base_href . + 'lnl.php?type=exec&id=' . intval($id) . '&apikey=' . + $tplanInfo['api_key'] . '
    '; + $exec_info = null; + } + + return $out; +} + +/** + */ +function renderBuildItem($info) +{ + $cfg = getWebEditorCfg('build'); + $buildType = $cfg['type']; + $lbl = init_labels(array( + 'build' => null, + 'notes' => null + )); + $out = ''; + + $title = $lbl['build'] . ': ' . htmlspecialchars($info->build_name); + $out .= renderSimpleChapter($title, + ($buildType == 'none' ? nl2br($info->build_notes) : $info->build_notes), + 'page-break-before: avoid;'); + + return $out; +} + +/** + */ +function initStaticRenderTestCaseForPrinting(&$dbH, $tcaseID, $ctx, $cfg) +{ + $things = new stdClass(); + $things->repoDir = config_get('repositoryPath'); + $things->tables = tlDBObject::getDBTables( + array( + 'executions', + 'builds', + 'execution_tcsteps' + )); + + $things->tc_mgr = new testcase($dbH); + $things->tplan_urgency = new testPlanUrgency($dbH); + $things->build_mgr = new build_mgr($dbH); + $things->tplan_mgr = new testplan($dbH); + $things->req_mgr = new requirement_mgr($dbH); + $things->tproject_mgr = new testproject($dbH); + $things->docRepo = tlAttachmentRepository::create($dbH); + + $things->locationFilters = $things->tc_mgr->buildCFLocationMap(); + + $things->buildCfields = array(); + + $prefix = isset($ctx['prefix']) ? $ctx['prefix'] : null; + if (! is_null($prefix)) { + $things->tcase_prefix = $prefix; + } else { + list ($things->tcase_prefix,) = $things->tc_mgr->getPrefix($tcaseID); + } + $things->tcase_prefix .= $cfg['testcase']->glue_character; + + $things->its = null; + $tprojectID = isset($ctx['tproject_id']) ? $ctx['tproject_id'] : 0; + $info = $things->tproject_mgr->get_by_id($tprojectID); + if ($info['issue_tracker_enabled']) { + $it_mgr = new tlIssueTracker($dbH); + $things->its = $it_mgr->getInterfaceObject($tprojectID); + unset($it_mgr); + } + + $things->cfieldFormatting = array( + 'label_css_style' => '', + 'add_table' => false, + 'value_css_style' => ' colspan = "' . ($cfg['tableColspan'] - 1) . '" ' + ); + + return $things; } - - -/** - * - */ -function initStaticRenderTestCaseForPrinting(&$dbH,$tcaseID,$ctx,$cfg) { - - $things = new stdClass(); - $things->repoDir = config_get('repositoryPath'); - $things->tables = - tlDBObject::getDBTables(array('executions','builds','execution_tcsteps')); - - - $things->tc_mgr = new testcase($dbH); - $things->tplan_urgency = new testPlanUrgency($dbH); - $things->build_mgr = new build_mgr($dbH); - $things->tplan_mgr = new testplan($dbH); - $things->req_mgr = new requirement_mgr($dbH); - $things->tproject_mgr = new testproject($dbH); - $things->docRepo = tlAttachmentRepository::create($dbH); - - $things->locationFilters = $things->tc_mgr->buildCFLocationMap(); - - - $things->buildCfields = array(); - - $prefix = isset($ctx['prefix']) ? $ctx['prefix'] : null; - if(!is_null($prefix)) { - $things->tcase_prefix = $prefix; - } else { - list($things->tcase_prefix,$dummy) = $things->tc_mgr->getPrefix($tcaseID); - } - $things->tcase_prefix .= $cfg['testcase']->glue_character; - - - $things->its = null; - $tprojectID = isset($ctx['tproject_id']) ? $ctx['tproject_id'] : 0; - $info = $things->tproject_mgr->get_by_id($tprojectID); - if($info['issue_tracker_enabled']) { - $it_mgr = new tlIssueTracker($dbH); - $things->its = $it_mgr->getInterfaceObject($tprojectID); - unset($it_mgr); - } - - $things->cfieldFormatting = - array('label_css_style' => '', 'add_table' => false, - 'value_css_style' => - ' colspan = "' . ($cfg['tableColspan']-1) . '" ' ); - - - return $things; -} \ No newline at end of file diff --git a/lib/functions/printDocOptions.class.php b/lib/functions/printDocOptions.class.php index 36f67f007e..26f6399d2f 100644 --- a/lib/functions/printDocOptions.class.php +++ b/lib/functions/printDocOptions.class.php @@ -1,140 +1,206 @@ -doc = array(); - - // element format - // - // 'value' => 'toc','description' => 'opt_show_toc','checked' => 'n' - // 'value': will be used to get the value - // 'description': label id, to be used for localization - // - // if checked is not present => 'checked' => 'n' - // - $this->doc[] = array( 'value' => 'toc','description' => 'opt_show_toc'); - $this->doc[] = array( 'value' => 'headerNumbering','description' => 'opt_show_hdrNumbering'); - - // Specific for Documents regarding Requirement Specifications - $this->reqSpec = array(); - $key2init = array('req_spec_scope','req_spec_author', - 'req_spec_overwritten_count_reqs', - 'req_spec_type','req_spec_cf','req_scope', - 'req_author','req_status', - 'req_type','req_cf','req_relations', - 'req_linked_tcs','req_coverage','displayVersion'); - - $yes = array('req_spec_scope' => 'y','req_scope' => 'y'); - foreach($key2init as $key) { - $yn = isset($key2init2yes[$key]) ? $key2init2yes[$key] : 'n'; - $this->reqSpec[] = array('value' => $key,'checked' => $yn, - 'description' => 'opt_' . $key); - } - - $this->testSpec = array(); - $this->testSpec[] = array('value' => 'header','description' => 'opt_show_suite_txt'); - $this->testSpec[] = array('value' => 'summary','description' => 'opt_show_tc_summary','checked' => 'y'); - $this->testSpec[] = array('value' => 'body','description' => 'opt_show_tc_body'); - $this->testSpec[] = array('value' => 'author','description' => 'opt_show_tc_author'); - $this->testSpec[] = array('value' => 'keyword','description' => 'opt_show_tc_keys'); - $this->testSpec[] = array('value' => 'cfields','description' => 'opt_show_cfields'); - $this->testSpec[] = array( 'value' => 'requirement','description' => 'opt_show_tc_reqs'); - - $this->exec = array(); - $this->exec[] = array( 'value' => 'execResultsByCFOnExecCombination','description' => 'opt_cfexec_comb'); - - $this->exec[] = array('value' => 'notes', 'description' => 'opt_show_tc_notes'); - - $this->exec[] = array('value' => 'step_exec_notes', 'description' => 'opt_show_tcstep_exec_notes'); - - $this->exec[] = array('value' => 'passfail','description' => 'opt_show_passfail','checked' => 'y'); - - $this->exec[] = array('value' => 'step_exec_status','description' => 'opt_show_tcstep_exec_status','checked' => 'y'); - - $this->exec[] = array('value' => 'build_cfields','description' => 'opt_show_build_cfields','checked' => 'n'); - $this->exec[] = array('value' => 'metrics','description' => 'opt_show_metrics'); - - } - - /** - * - */ - function getDocOpt() { - return $this->doc; - } - - /** - * - */ - function getTestSpecOpt() { - return $this->testSpec; - } - - /** - * - */ - function getReqSpecOpt() { - return $this->reqSpec; - } - - - /** - * - */ - function getExecOpt() { - return $this->exec; - } - - - /** - * - */ - function getAllOptVars() { - - $ov = array(); - $prop = array('doc','testSpec','reqSpec','exec'); - foreach($prop as $pp) { - foreach($this->$pp as $ele) { - $ov[$ele['value']] = isset($ele['checked']) ? $ele['checked'] : 'n'; - $ov[$ele['value']] = ($ov[$ele['value']] == 'y') ? 1 : 0; - } - } - - return $ov; - } - - /** - * - */ - function getJSPrintPreferences() { - - $ov = array(); - $prop = array("doc","testSpec","reqSpec","exec"); - foreach($prop as $pp) { - foreach($this->$pp as $ele) { - $ov[] = $ele['value']; - } - } - return implode(',',$ov); - } - - - - - - +doc = array(); + + // element format + // + // 'value' => 'toc','description' => 'opt_show_toc','checked' => 'n' + // 'value': will be used to get the value + // 'description': label id, to be used for localization + // + // if checked is not present => 'checked' => 'n' + $this->doc[] = array( + 'value' => 'toc', + 'description' => 'opt_show_toc' + ); + $this->doc[] = array( + 'value' => 'headerNumbering', + 'description' => 'opt_show_hdrNumbering' + ); + + // Specific for Documents regarding Requirement Specifications + $this->reqSpec = array(); + $key2init = array( + 'req_spec_scope', + 'req_spec_author', + 'req_spec_overwritten_count_reqs', + 'req_spec_type', + 'req_spec_cf', + 'req_scope', + 'req_author', + 'req_status', + 'req_type', + 'req_cf', + 'req_relations', + 'req_linked_tcs', + 'req_coverage', + 'displayVersion' + ); + + foreach ($key2init as $key) { + $yn = isset($key2init2yes[$key]) ? $key2init2yes[$key] : 'n'; + $this->reqSpec[] = array( + 'value' => $key, + 'checked' => $yn, + 'description' => 'opt_' . $key + ); + } + + $this->testSpec = array(); + $this->testSpec[] = array( + 'value' => 'header', + 'description' => 'opt_show_suite_txt' + ); + $this->testSpec[] = array( + 'value' => 'summary', + 'description' => 'opt_show_tc_summary', + 'checked' => 'y' + ); + $this->testSpec[] = array( + 'value' => 'body', + 'description' => 'opt_show_tc_body' + ); + $this->testSpec[] = array( + 'value' => 'author', + 'description' => 'opt_show_tc_author' + ); + $this->testSpec[] = array( + 'value' => 'keyword', + 'description' => 'opt_show_tc_keys' + ); + $this->testSpec[] = array( + 'value' => 'cfields', + 'description' => 'opt_show_cfields' + ); + $this->testSpec[] = array( + 'value' => 'requirement', + 'description' => 'opt_show_tc_reqs' + ); + + $this->exec = array(); + $this->exec[] = array( + 'value' => 'execResultsByCFOnExecCombination', + 'description' => 'opt_cfexec_comb' + ); + + $this->exec[] = array( + 'value' => 'notes', + 'description' => 'opt_show_tc_notes' + ); + + $this->exec[] = array( + 'value' => 'step_exec_notes', + 'description' => 'opt_show_tcstep_exec_notes' + ); + + $this->exec[] = array( + 'value' => 'passfail', + 'description' => 'opt_show_passfail', + 'checked' => 'y' + ); + + $this->exec[] = array( + 'value' => 'step_exec_status', + 'description' => 'opt_show_tcstep_exec_status', + 'checked' => 'y' + ); + + $this->exec[] = array( + 'value' => 'build_cfields', + 'description' => 'opt_show_build_cfields', + 'checked' => 'n' + ); + $this->exec[] = array( + 'value' => 'metrics', + 'description' => 'opt_show_metrics' + ); + } + + /** + */ + public function getDocOpt() + { + return $this->doc; + } + + /** + */ + public function getTestSpecOpt() + { + return $this->testSpec; + } + + /** + */ + public function getReqSpecOpt() + { + return $this->reqSpec; + } + + /** + */ + public function getExecOpt() + { + return $this->exec; + } + + /** + */ + public function getAllOptVars() + { + $ov = array(); + $prop = array( + 'doc', + 'testSpec', + 'reqSpec', + 'exec' + ); + foreach ($prop as $pp) { + foreach ($this->$pp as $ele) { + $ov[$ele['value']] = isset($ele['checked']) ? $ele['checked'] : 'n'; + $ov[$ele['value']] = ($ov[$ele['value']] == 'y') ? 1 : 0; + } + } + + return $ov; + } + + /** + */ + public function getJSPrintPreferences() + { + $ov = array(); + $prop = array( + "doc", + "testSpec", + "reqSpec", + "exec" + ); + foreach ($prop as $pp) { + foreach ($this->$pp as $ele) { + $ov[] = $ele['value']; + } + } + return implode(',', $ov); + } } diff --git a/lib/functions/remote_exec.php b/lib/functions/remote_exec.php index 5057c3173c..a1d016c2f2 100644 --- a/lib/functions/remote_exec.php +++ b/lib/functions/remote_exec.php @@ -1,133 +1,134 @@ - - * - * @internal revisions - * 20110308 - franciscom - refactoring - */ -require_once("../../config.inc.php"); -require_once (TL_ABS_PATH . 'third_party'. DIRECTORY_SEPARATOR . 'xml-rpc/class-IXR.php'); - -/** -* Initiate the execution of a testcase through XML Server RPCs. -* All the object instantiations are done here. -* XML-RPC Server Settings need to be configured using the custom fields feature. -* Three fields each for testcase level and testsuite level are required. -* The fields are: server_host, server_port and server_path. -* Precede 'tc_' for custom fields assigned to testcase level. -* -* @param $tcaseInfo: -* @param $serverCfg: -* @param $context -* -* @return map: -* keys: 'result','notes','message' -* values: 'result' -> (Pass, Fail or Blocked) -* 'notes' -> Notes text -* 'message' -> Message from server -*/ -function executeTestCase($tcaseInfo,$serverCfg,$context) -{ - // system: to give info about conection to remote execution server - // execution: - // scheduled: domain 'now', 'future' - // caller will use this attribute to write exec result (only if now) - // timestampISO: can be used by server to say the scheduled time. - // To be used only if scheduled = 'future' - // - // Complete date plus hours, minutes and seconds: - // YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00) - // - // where: - // - // YYYY = four-digit year - // MM = two-digit month (01=January, etc.) - // DD = two-digit day of month (01 through 31) - // hh = two digits of hour (00 through 23) (am/pm NOT allowed) - // mm = two digits of minute (00 through 59) - // ss = two digits of second (00 through 59) - // TZD = time zone designator (Z or +hh:mm or -hh:mm) - - - $ret = array('system' => array('status' => 'ok', 'msg' => 'ok'), - 'execution' => array('scheduled' => '', - 'result' => '', - 'resultVerbose' => '', - 'notes' => '', - 'timestampISO' => '') ); - - - $labels = init_labels(array('remoteExecServerConfigProblems' => null, - 'remoteExecServerConnectionFailure' => null)); - - - $do_it = (!is_null($serverCfg) && !is_null($serverCfg["url"]) ); - if(!$do_it) - { - $ret['system']['status'] = 'configProblems'; - $ret['system']['msg'] = $labels['remoteExecServerConfigProblems']; - } - - if($do_it) - { - $xmlrpcClient = new IXR_Client($serverCfg["url"]); - if( is_null($xmlrpcClient) ) - { - $do_it = false; - $ret['system']['status'] = 'connectionFailure'; - $ret['system']['msg'] = $labels['remoteExecServerConnectionFailure']; - } - } - - if($do_it) - { - $args4call = array(); - - // Execution Target - $args4call['testCaseName'] = $tcaseInfo['name']; - $args4call['testCaseID'] = $tcaseInfo['id']; - $args4call['testCaseVersionID'] = $tcaseInfo['version_id']; - - // Context - $args4call['testProjectID'] = $context['tproject_id']; - $args4call['testPlanID'] = $context['tplan_id']; - $args4call['platformID'] = $context['platform_id']; - $args4call['buildID'] = $context['build_id']; - $args4call['executionMode'] = 'now'; // domain: deferred,now - - $xmlrpcClient->query('executeTestCase',$args4call); - $response = $xmlrpcClient->getResponse(); - - if( is_null($response) ) - { - // Houston we have a problem!!! (Apollo 13) - $ret['system']['status'] = 'connectionFailure'; - $ret['system']['msg'] = $labels['remoteExecServerConnectionFailure']; - $ret['execution'] = null; - } - else - { - $ret['execution'] = $response; - $ret['execution']['resultVerbose'] = ''; - - if(!is_null($response['result'])) - { - $code = trim($response['result']); - if( $code != '') - { - $resultsCfg = config_get('results'); - $codeStatus = array_flip($resultsCfg['status_code']); - $dummy = trim($codeStatus[$code]); - $ret['execution']['resultVerbose'] = lang_get($resultsCfg['status_label'][$dummy]); - } - } - } - } - - return $ret; -} // function end -?> \ No newline at end of file + + * + * @internal revisions + * 20110308 - franciscom - refactoring + */ +require_once '../../config.inc.php'; +require_once TL_ABS_PATH . 'third_party' . DIRECTORY_SEPARATOR . + 'xml-rpc/class-IXR.php'; + +/** + * Initiate the execution of a testcase through XML Server RPCs. + * All the object instantiations are done here. + * XML-RPC Server Settings need to be configured using the custom fields feature. + * Three fields each for testcase level and testsuite level are required. + * The fields are: server_host, server_port and server_path. + * Precede 'tc_' for custom fields assigned to testcase level. + * + * @param + * $tcaseInfo: + * @param + * $serverCfg: + * @param + * $context + * + * @return map: keys: 'result','notes','message' + * values: 'result' -> (Pass, Fail or Blocked) + * 'notes' -> Notes text + * 'message' -> Message from server + */ +function executeTestCase($tcaseInfo, $serverCfg, $context) +{ + // system: to give info about conection to remote execution server + // execution: + // scheduled: domain 'now', 'future' + // caller will use this attribute to write exec result (only if now) + // timestampISO: can be used by server to say the scheduled time. + // To be used only if scheduled = 'future' + // + // Complete date plus hours, minutes and seconds: + // YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00) + // + // where: + // + // YYYY = four-digit year + // MM = two-digit month (01=January, etc.) + // DD = two-digit day of month (01 through 31) + // hh = two digits of hour (00 through 23) (am/pm NOT allowed) + // mm = two digits of minute (00 through 59) + // ss = two digits of second (00 through 59) + // TZD = time zone designator (Z or +hh:mm or -hh:mm) + $ret = array( + 'system' => array( + 'status' => 'ok', + 'msg' => 'ok' + ), + 'execution' => array( + 'scheduled' => '', + 'result' => '', + 'resultVerbose' => '', + 'notes' => '', + 'timestampISO' => '' + ) + ); + + $labels = init_labels( + array( + 'remoteExecServerConfigProblems' => null, + 'remoteExecServerConnectionFailure' => null + )); + + $do_it = (! is_null($serverCfg) && ! is_null($serverCfg["url"])); + if (! $do_it) { + $ret['system']['status'] = 'configProblems'; + $ret['system']['msg'] = $labels['remoteExecServerConfigProblems']; + } + + if ($do_it) { + $xmlrpcClient = new IXR_Client($serverCfg["url"]); + if (is_null($xmlrpcClient)) { + $do_it = false; + $ret['system']['status'] = 'connectionFailure'; + $ret['system']['msg'] = $labels['remoteExecServerConnectionFailure']; + } + } + + if ($do_it) { + $args4call = array(); + + // Execution Target + $args4call['testCaseName'] = $tcaseInfo['name']; + $args4call['testCaseID'] = $tcaseInfo['id']; + $args4call['testCaseVersionID'] = $tcaseInfo['version_id']; + + // Context + $args4call['testProjectID'] = $context['tproject_id']; + $args4call['testPlanID'] = $context['tplan_id']; + $args4call['platformID'] = $context['platform_id']; + $args4call['buildID'] = $context['build_id']; + $args4call['executionMode'] = 'now'; // domain: deferred,now + + $xmlrpcClient->query('executeTestCase', $args4call); + $response = $xmlrpcClient->getResponse(); + + if (is_null($response)) { + // Houston we have a problem!!! (Apollo 13) + $ret['system']['status'] = 'connectionFailure'; + $ret['system']['msg'] = $labels['remoteExecServerConnectionFailure']; + $ret['execution'] = null; + } else { + $ret['execution'] = $response; + $ret['execution']['resultVerbose'] = ''; + + if (! is_null($response['result'])) { + $code = trim($response['result']); + if ($code != '') { + $resultsCfg = config_get('results'); + $codeStatus = array_flip($resultsCfg['status_code']); + $dummy = trim($codeStatus[$code]); + $ret['execution']['resultVerbose'] = lang_get( + $resultsCfg['status_label'][$dummy]); + } + } + } + } + + return $ret; +} +?> diff --git a/lib/functions/reports.class.php b/lib/functions/reports.class.php index e0d9bc20c1..b7b79dcb8f 100644 --- a/lib/functions/reports.class.php +++ b/lib/functions/reports.class.php @@ -1,162 +1,168 @@ -db = $db; - $this->testPlanID = $tplanId; - // tlObjectWithDB::__construct($db); - parent::__construct($this->db); - } - - - /** - * Function returns array with input for reports navigator - * - * @param object $context - * @param boolean $bug_interface_enabled - * @param boolean $req_mgmt_enabled - * @param integer $format format identifier - * - * @return array of array - described for array $g_reports_list in const.inc.php - **/ - public function get_list_reports($context,$bug_interface_enabled, $req_mgmt_enabled, $format) { - - $reportList = config_get('reports_list'); - $items = array(); - - $toggleMsg = lang_get('show_hide_direct_link'); - $canNotCreateDirectLink = lang_get('can_not_create_direct_link'); - - $apiKeyLen = strlen(trim($context->apikey)); - $apiKeyIsValid = ($apiKeyLen == 32 || $apiKeyLen == 64); // I'm sorry for MAGIC - - $xdx = 0; - - foreach ($reportList as &$rptItem) { - // check validity of report - if (($rptItem['enabled'] == 'all') || - (($rptItem['enabled'] == 'req') && $req_mgmt_enabled) || - (($rptItem['enabled'] == 'bts') && $bug_interface_enabled)) { - - if (strpos(",".$rptItem['format'],$format) > 0) { - $reportUrl = $rptItem['url'] . ( stristr($rptItem['url'], "?") ? '&' : '?'); - $items[$xdx] = - array('name' => lang_get($rptItem['title']), - 'href' => $reportUrl, 'directLink' => ''); - - if(isset($rptItem['directLink']) && - trim($rptItem['directLink']) != '') { - if($apiKeyIsValid) { - $items[$xdx]['directLink'] = - sprintf($rptItem['directLink'],$_SESSION['basehref'], - $context->apikey,$context->tproject_id,$context->tplan_id); - } else { - $items[$xdx]['directLink'] = $canNotCreateDirectLink; - } - } - - $dl = $items[$xdx]['directLink']; - $mask = '%s'; - - $divClass = 'direct_link_' . $xdx; - $items[$xdx]['toggle'] = sprintf($mask,$toggleMsg,$toggleMsg,$divClass); - $items[$xdx]['directLinkDiv'] = ''; - $xdx++; - } - } - } - return $items; - } - - - /** - * get count of builds - * - * @param boolean $active (optional) query open builds [0,1] - * @param boolean $open (optional) query active builds [0,1] - * - * @return integer count of builds - */ - public function get_count_builds($active=1, $open=0) { - $sql = " SELECT COUNT(0) FROM {$this->tables['builds']} builds " . - " WHERE builds.testplan_id = {$this->testPlanID} "; - - if( $active ) - { - $sql .= " AND active=" . intval($active) . " "; - } - - if( $open ) - { - $sql .= " AND is_open=" . intval($open) . " "; - } - - return $this->db->fetchOneValue($sql); - } - - - /** - * get count of testcase linked to a testplan - * @return integer count - */ - public function get_count_testcase4testplan() - { - $sql = " SELECT COUNT(0) FROM {$this->tables['testplan_tcversions']} testplan_tcversions " . - " WHERE testplan_id = {$this->testPlanID} "; - return $this->db->fetchOneValue($sql); - } - -} // end class result - -?> \ No newline at end of file +db = $db; + $this->testPlanID = $tplanId; + parent::__construct($this->db); + } + + /** + * Function returns array with input for reports navigator + * + * @param object $context + * @param boolean $bug_interface_enabled + * @param boolean $req_mgmt_enabled + * @param integer $format + * format identifier + * + * @return array of array - described for array $g_reports_list in const.inc.php + */ + public function get_list_reports($context, $bug_interface_enabled, + $req_mgmt_enabled, $format) + { + $reportList = config_get('reports_list'); + $items = array(); + + $toggleMsg = lang_get('show_hide_direct_link'); + $canNotCreateDirectLink = lang_get('can_not_create_direct_link'); + + $apiKeyLen = strlen(trim($context->apikey)); + $apiKeyIsValid = ($apiKeyLen == 32 || $apiKeyLen == 64); // I'm sorry for MAGIC + + $xdx = 0; + foreach ($reportList as &$rptItem) { + // check validity of report + if (($rptItem['enabled'] == 'all') || + (($rptItem['enabled'] == 'req') && $req_mgmt_enabled) || + (($rptItem['enabled'] == 'bts') && $bug_interface_enabled) && + strpos("," . $rptItem['format'], $format) > 0) { + $reportUrl = $rptItem['url'] . + (stristr($rptItem['url'], "?") ? '&' : '?'); + $items[$xdx] = array( + 'name' => lang_get($rptItem['title']), + 'href' => $reportUrl, + 'directLink' => '' + ); + + if (isset($rptItem['directLink']) && + trim($rptItem['directLink']) != '') { + if ($apiKeyIsValid) { + $items[$xdx]['directLink'] = sprintf( + $rptItem['directLink'], $_SESSION['basehref'], + $context->apikey, $context->tproject_id, + $context->tplan_id); + } else { + $items[$xdx]['directLink'] = $canNotCreateDirectLink; + } + } + + $dl = $items[$xdx]['directLink']; + $mask = '%s'; + + $divClass = 'direct_link_' . $xdx; + $items[$xdx]['toggle'] = sprintf($mask, $toggleMsg, $toggleMsg, + $divClass); + $items[$xdx]['directLinkDiv'] = ''; + $xdx ++; + } + } + return $items; + } + + /** + * get count of builds + * + * @param boolean $active + * (optional) query open builds [0,1] + * @param boolean $open + * (optional) query active builds [0,1] + * + * @return integer count of builds + */ + public function get_count_builds($active = 1, $open = 0) + { + $sql = " SELECT COUNT(0) FROM {$this->tables['builds']} builds " . + " WHERE builds.testplan_id = {$this->testPlanID} "; + + if ($active) { + $sql .= " AND active=" . intval($active) . " "; + } + + if ($open) { + $sql .= " AND is_open=" . intval($open) . " "; + } + + return $this->db->fetchOneValue($sql); + } + + /** + * get count of testcase linked to a testplan + * + * @return integer count + */ + public function get_count_testcase4testplan() + { + $sql = " SELECT COUNT(0) FROM {$this->tables['testplan_tcversions']} testplan_tcversions " . + " WHERE testplan_id = {$this->testPlanID} "; + return $this->db->fetchOneValue($sql); + } +} + +?> diff --git a/lib/functions/requirement_mgr.class.php b/lib/functions/requirement_mgr.class.php index e60ed72e3e..44cfef18d5 100644 --- a/lib/functions/requirement_mgr.class.php +++ b/lib/functions/requirement_mgr.class.php @@ -1,4916 +1,4885 @@ - - * @copyright 2007-2020, TestLink community - * - * Manager for requirements. - * Requirements are children of a requirement specification (requirements container) - * - * - */ - -// Needed to use extends tlObjectWithAttachments, If not present autoload fails. -require_once( dirname(__FILE__) . '/attachments.inc.php'); -class requirement_mgr extends tlObjectWithAttachments { - var $db; - var $cfield_mgr; - var $my_node_type; - var $tree_mgr; - var $node_types_descr_id; - var $node_types_id_descr; - var $attachmentTableName; - - // 20100220 - franciscom - I'm will work only on XML - // then remove other formats till other dev do refactor - var $import_file_types = array("csv" => "CSV", - "csv_doors" => "CSV (Doors)", - "XML" => "XML", - "DocBook" => "DocBook"); - - var $export_file_types = array("XML" => "XML"); - - var $fieldSize; - var $reqCfg; - var $internal_links; - var $relationsCfg; - var $notifyOn; - var $reqTCLinkCfg; - - - - const AUTOMATIC_ID=0; - const ALL_VERSIONS=0; - const LATEST_VERSION=-1; - const NO_REVISION=-1; - - - - /* - function: requirement_mgr - contructor - - args: db: reference to db object - - returns: instance of requirement_mgr - - */ - function __construct(&$db) { - - $this->db = &$db; - $this->cfield_mgr=new cfield_mgr($this->db); - $this->tree_mgr = new tree($this->db); - - $this->attachmentTableName = 'req_versions'; - - tlObjectWithAttachments::__construct($this->db,$this->attachmentTableName); - - $this->node_types_descr_id= $this->tree_mgr->get_available_node_types(); - $this->node_types_id_descr=array_flip($this->node_types_descr_id); - $this->my_node_type=$this->node_types_descr_id['requirement']; - $this->object_table=$this->tables['requirements']; - - $this->fieldSize = config_get('field_size'); - $this->reqCfg = config_get('req_cfg'); - $this->reqTCLinkCfg = config_get('reqTCLinks'); - - $this->relationsCfg = new stdClass(); - $this->relationsCfg->interProjectLinking = $this->reqCfg->relations->interproject_linking; - - $this->internal_links = config_get('internal_links'); - - $this->notifyOn = null; - } - - /* - function: get_export_file_types - getter - - args: - - - returns: map - key: export file type code - value: export file type verbose description - - */ - function get_export_file_types() - { - return $this->export_file_types; - } - - /* - function: get_impor_file_types - getter - - args: - - - returns: map - key: import file type code - value: import file type verbose description - - */ - function get_import_file_types() - { - return $this->import_file_types; - } - - - - - -/* - function: get_by_id - - - args: id: requirement id (can be an array) - [version_id]: requirement version id (can be an array) - [version_number]: - [options] - - - returns: null if query fails - map with requirement info - - -*/ -function get_by_id($id,$version_id=self::ALL_VERSIONS,$version_number=1,$options=null,$filters=null) -{ - static $debugMsg; - static $userCache; // key: user id, value: display name - static $lables; - static $user_keys; - - if(!$debugMsg) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $labels['undefined'] = lang_get('undefined'); - $user_keys = array('author' => 'author_id', 'modifier' => 'modifier_id'); - } - - - $my['options'] = array('order_by' => " ORDER BY REQV.version DESC ", - 'output_format' => 'array', 'renderImageInline' => false, - 'decodeUsers' => true, 'outputLevel' => 'std'); - - $my['options'] = array_merge($my['options'], (array)$options); - - // null => do not filter - $my['filters'] = array('status' => null, 'type' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $filter_clause = ''; - $dummy[]=''; // trick to make implode() work - foreach( $my['filters'] as $field2filter => $value) { - if( !is_null($value) ) { - $dummy[] = " {$field2filter} = '{$value}' "; - } - } - - if( count($dummy) > 1) { - $filter_clause = implode(" AND ",$dummy); - } - - $where_clause = " WHERE NH_REQV.parent_id "; - if( ($id_is_array=is_array($id)) ) { - $where_clause .= "IN (" . implode(",",$id) . ") "; - } else { - $where_clause .= " = {$id} "; - } - - if(is_array($version_id)) { - $versionid_list = implode(",",$version_id); - $where_clause .= " AND REQV.id IN ({$versionid_list}) "; - } else { - if( is_null($version_id) ) { - // search by "human" version number - $where_clause .= " AND REQV.version = {$version_number} "; - } else { - if($version_id != self::ALL_VERSIONS && $version_id != self::LATEST_VERSION) { - $where_clause .= " AND REQV.id = {$version_id} "; - } - } - } - - // added -1 AS revision_id to make some process easier - switch($my['options']['outputLevel']) { - case 'minimal': - $outf = " /* $debugMsg */ - SELECT REQ.id,REQ.req_doc_id,REQV.id AS version_id, - NH_REQ.name AS title "; - break; - - case 'std': - default: - $outf = " /* $debugMsg */ SELECT REQ.id,REQ.srs_id,REQ.req_doc_id," . - " REQV.scope,REQV.status,REQV.type,REQV.active," . - " REQV.is_open,REQV.is_open AS reqver_is_open,REQV.author_id,REQV.version,REQV.id AS version_id," . - " REQV.expected_coverage,REQV.creation_ts,REQV.modifier_id," . - " REQV.modification_ts,REQV.revision, -1 AS revision_id, " . - " NH_REQ.name AS title, REQ_SPEC.testproject_id, " . - " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order "; - break; - } - - // added -1 AS revision_id to make some process easier - $sql = $outf . - " FROM {$this->object_table} REQ " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id ". - " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . - " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . - $where_clause . $filter_clause . $my['options']['order_by']; - - $decodeUserMode = 'simple'; - if ($version_id != self::LATEST_VERSION) { - switch($my['options']['output_format']) { - case 'mapOfArray': - $recordset = $this->db->fetchRowsIntoMap($sql,'id',database::CUMULATIVE); - $decodeUserMode = 'complex'; - break; - - case 'array': - default: - $recordset = $this->db->get_recordset($sql); - break; - - } - } else { - // But, how performance wise can be do this, - // instead of using MAX(version) and a group by? - // - // if $id was a list then this will return something USELESS - // - if( !$id_is_array ) { - $recordset = array($this->db->fetchFirstRow($sql)); - } else { - // Write to event viewer ??? - // Developer Needs to user - die('use getByIDBulkLatestVersionRevision()'); - } - } - - $rs = null; - if(!is_null($recordset) && $my['options']['renderImageInline']) { - $k2l = array_keys($recordset); - foreach($k2l as $akx) { - $this->renderImageAttachments($id,$recordset[$akx]); - } - reset($recordset); - } - - $rs = $recordset; - if(!is_null($recordset) && $my['options']['decodeUsers']) { - switch ($decodeUserMode) { - case 'complex': - // output[REQID][0] = array('id' =>, 'xx' => ...) - $flevel = array_keys($recordset); - foreach($flevel as $flk) { - $key2loop = array_keys($recordset[$flk]); - foreach( $key2loop as $key ) { - foreach( $user_keys as $ukey => $userid_field) { - $rs[$flk][$key][$ukey] = ''; - if(trim($rs[$flk][$key][$userid_field]) != "") { - if( !isset($userCache[$rs[$flk][$key][$userid_field]]) ) { - $user = tlUser::getByID($this->db,$rs[$flk][$key][$userid_field]); - $rs[$flk][$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; - $userCache[$rs[$flk][$key][$userid_field]] = $rs[$flk][$key][$ukey]; - unset($user); - } else { - $rs[$flk][$key][$ukey] = $userCache[$rs[$flk][$key][$userid_field]]; - } - } - } - } - } - break; - - case 'simple': - default: - $key2loop = array_keys($recordset); - foreach( $key2loop as $key ) { - foreach( $user_keys as $ukey => $userid_field) { - $rs[$key][$ukey] = ''; - if(trim($rs[$key][$userid_field]) != "") { - if( !isset($userCache[$rs[$key][$userid_field]]) ) { - $user = tlUser::getByID($this->db,$rs[$key][$userid_field]); - $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; - $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; - unset($user); - } else { - $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; - } - } - } - } - break; - } - } - - unset($recordset); - unset($my); - unset($dummy); - - return $rs; -} - - /* - function: create - - args: srs_id: req spec id, parent of requirement to be created - reqdoc_id - title - scope - user_id: author - [status] - [type] - [expected_coverage] - [node_order] - - returns: map with following keys: - status_ok -> 1/0 - msg -> some simple message, useful when status_ok ==0 - id -> id of new requirement. - - @internal revision - */ -function create($srs_id,$reqdoc_id,$title, $scope, $user_id, - $status = TL_REQ_STATUS_VALID, $type = TL_REQ_TYPE_INFO, - $expected_coverage=1,$node_order=0,$tproject_id=null, $options=null) -{ - // This kind of saving is important when called in a loop in situations like - // copy test project - static $debugMsg; - static $log_message; - - if(!$log_message) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $log_message = lang_get('req_created_automatic_log'); - } - - $tproject_id = is_null($tproject_id) ? $this->tree_mgr->getTreeRoot($srs_id) : $tproject_id; - - $result = array( 'id' => 0, 'status_ok' => 0, 'msg' => 'ko'); - $my['options'] = array('quickAndDirty' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - if(!$my['options']['quickAndDirty']) - { - $reqdoc_id = trim_and_limit($reqdoc_id,$this->fieldSize->req_docid); - $title = trim_and_limit($title,$this->fieldSize->req_title); - $op = $this->check_basic_data($srs_id,$tproject_id,$title,$reqdoc_id); - } - else - { - $op['status_ok'] = true; - } - - $result['msg'] = $op['status_ok'] ? $result['msg'] : $op['msg']; - if( $op['status_ok'] ) - { - $result = $this->create_req_only($srs_id,$reqdoc_id,$title,$user_id,$node_order); - if($result["status_ok"]) - { - if ($this->internal_links->enable ) - { - $scope = req_link_replace($this->db, $scope, $tproject_id); - } - - $op = $this->create_version($result['id'],1,$scope,$user_id, - $status,$type,intval($expected_coverage)); - $result['msg'] = $op['status_ok'] ? $result['msg'] : $op['msg']; - $result['version_id'] = $op['status_ok'] ? $op['id'] : -1; - - if( $op['status_ok'] ) - { - $sql = "/* $debugMsg */ " . - "UPDATE {$this->tables['req_versions']} " . - " SET log_message='" . $this->db->prepare_string($log_message) . "'" . - " WHERE id = " . intval($op['id']) ; - $this->db->exec_query($sql); - } - - } - } - $ctx = array('id' => $result['id']); - event_signal('EVENT_TEST_REQUIREMENT_CREATE', $ctx); - - return $result; - -} // function end - - - /* - function: update - - - args: id: requirement id - version_id - reqdoc_id - title - scope - user_id: author - status - type - $expected_coverage - [skip_controls] - - - returns: map: keys : status_ok, msg - - @internal revision - 20091202 - franciscom - - - */ - -function update($id,$version_id,$reqdoc_id,$title, $scope, $user_id, $status, $type, - $expected_coverage,$node_order=null,$tproject_id=null,$skip_controls=0, - $create_revision=false,$log_msg=null) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $result['status_ok'] = 1; - $result['msg'] = 'ok'; - - $db_now = $this->db->db_now(); - - // get SRSid, needed to do controls - $rs=$this->get_by_id($id,$version_id); - $req = $rs[0]; - $srs_id=$req['srs_id']; - - // try to avoid function calls when data is available on caller - $tproject_id = is_null($tproject_id) ? $this->tree_mgr->getTreeRoot($srs_id): $tproject_id; - - if ($this->internal_links->enable ) - { - $scope = req_link_replace($this->db, $scope, $tproject_id); - } - - $reqdoc_id=trim_and_limit($reqdoc_id,$this->fieldSize->req_docid); - $title=trim_and_limit($title,$this->fieldSize->req_title); - $chk=$this->check_basic_data($srs_id,$tproject_id,$title,$reqdoc_id,$id); - - if($chk['status_ok'] || $skip_controls) - { - if( $create_revision ) - { - $this->create_new_revision($version_id,$user_id,$tproject_id,$req,$log_msg); - } - - $sql = array(); - - $q = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . - " SET name='" . $this->db->prepare_string($title) . "'"; - if( !is_null($node_order) ) - { - $q .= ', node_order= ' . abs(intval($node_order)); - } - $sql[] = $q . " WHERE id={$id}"; - - - $sql[] = "/* $debugMsg */ UPDATE {$this->tables['requirements']} " . - " SET req_doc_id='" . $this->db->prepare_string($reqdoc_id) . "'" . - " WHERE id={$id}"; - - $sql_temp = "/* $debugMsg */ UPDATE {$this->tables['req_versions']} " . - " SET scope='" . $this->db->prepare_string($scope) . "', " . - " status='" . $this->db->prepare_string($status) . "', " . - " expected_coverage={$expected_coverage}, " . - " type='" . $this->db->prepare_string($type) . "' "; - - // only if no new revision is created set modifier and modification ts - // otherwise those values are handled by function create_new_revision() - if (!$create_revision) - { - $sql_temp .= ", modifier_id={$user_id}, modification_ts={$db_now} "; - } - - $sql[] = $sql_temp . " WHERE id=" . intval($version_id); - - foreach($sql as $stm) - { - $qres = $this->db->exec_query($stm); - if( !$qres ) - { - $result['status_ok'] = 0; - $result['msg'] = $this->db->error_msg; - $result['sql'] = $stm; - break; - } - } - - } // if($chk['status_ok'] || $skip_controls) - else - { - $result['status_ok']=$chk['status_ok']; - $result['msg']=$chk['msg']; - } - - $ctx = array('id' => $id); - event_signal('EVENT_TEST_REQUIREMENT_UPDATE', $ctx); - return $result; - } //function end - - - - /* - function: delete - Requirement - Requirement link to testcases - Requirement relations - Requirement custom fields values - Attachments - - check if we are deleting the only existent version, in this case - we need to delete the requirement. - - args: id: can be one id, or an array of id - - returns: - - - */ - function delete($id,$version_id = self::ALL_VERSIONS,$user_id=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $children = null; - - $where = array('coverage' => '','this' => '', 'iam_parent' => ''); - $deleteAll = false; - $result = null; - $doIt = true; - $kaboom = false; - - if(is_array($id)) { - $id_list = implode(',',$id); - $where['coverage'] = " WHERE req_id IN ({$id_list})"; - $where['this'] = " WHERE id IN ({$id_list})"; - $where['iam_parent'] = " WHERE parent_id IN ({$id_list})"; - } - else { - $safeID = intval($id); - $where['coverage'] = " WHERE req_id = " . $safeID; - $where['this'] = " WHERE id = " . $safeID; - $where['iam_parent'] = " WHERE parent_id = " . $safeID; - } - - $set2del = null; - - // if we are trying to delete ONE SPECIFIC VERSION - // of ONE REQ, and is the ONLY VERSION on DB - // then we are going to delete the req. - $checkNotify = true; - $action4notify = 'delete'; - if( $version_id != self::ALL_VERSIONS ) { - // we use version id when working on ONE REQ, - // then I'm going to trust this. - // From GUI if only one version exists, - // the operation available is DELETE REQ, - // not delete version - $sql = "SELECT COUNT(0) AS VQTY, parent_id FROM " . - " {$this->tables['nodes_hierarchy']} " . - $where['iam_parent'] . ' GROUP BY parent_id'; - - $rs = $this->db->fetchRowsIntoMap($sql,'parent_id'); - $rs = current($rs); - if(isset($rs['VQTY']) && $rs['VQTY'] > 1) { - $action4notify = 'delete_version'; - } - } - - if( $checkNotify && $this->notifyOn[__FUNCTION__] ) { - // Need to save data before delete - $set2del = $this->getByIDBulkLatestVersionRevision($id); - if( !is_null($set2del) ) { - foreach($set2del as $rk => $r2d) { - $this->notifyMonitors($rk,$action4notify,$user_id); - if($action4notify == 'delete') { - $this->monitorOff($rk); - } - } - } - } - - // When deleting only one version, we need to check - // if we need to delete requirement also. - $children[] = $version_id; - if( $version_id == self::ALL_VERSIONS) { - $deleteAll = true; - - // I'm trying to speedup the next deletes - $sql = "/* $debugMsg */ " . - "SELECT NH.id FROM {$this->tables['nodes_hierarchy']} NH " . - "WHERE NH.parent_id "; - - if( is_array($id) ) { - $sql .= " IN (" .implode(',',$id) . ") "; - } - else { - $sql .= " = {$id} "; - } - - $sql .= " AND node_type_id=" . $this->node_types_descr_id['requirement_version']; - - $children_rs=$this->db->fetchRowsIntoMap($sql,'id'); - $children = array_keys($children_rs); - - // delete dependencies with test specification - $sql = "DELETE FROM {$this->tables['req_coverage']} " . - $where['coverage']; - $result = $this->db->exec_query($sql); - - // also delete relations to other requirements - // Issue due to FK - // - if ($result) { - $this->delete_all_relations($id); - } - - if ($result) { - // Need to get all versions for all requirements - $doIt = true; - $reqIDSet = (array)$id; - $reqVerSet = $this->getAllReqVersionIDForReq($reqIDSet); - - foreach($reqVerSet as $reqID2Del => $reqVerElem) { - foreach($reqVerElem as $ydx => $reqVID2Del) { - $result = $this->attachmentRepository->deleteAttachmentsFor( - $reqVID2Del,$this->attachmentTableName); - } - } - } - } - - // Delete version info - $target = null; - if( $doIt ) { - // As usual working with MySQL makes easier to be lazy and forget that - // agregate functions need GROUP BY - // How many versions are there? - // we will delete req also for all with COUNT(0) == 1 - $sql = "SELECT COUNT(0) AS VQTY, parent_id " . - " FROM {$this->tables['nodes_hierarchy']} " . - $where['iam_parent'] . ' GROUP BY parent_id'; - - $rs = $this->db->fetchRowsIntoMap($sql,'parent_id'); - foreach($rs as $el) { - if(isset($el['VQTY']) && $el['VQTY'] == 1) { - $target[] = $el['parent_id']; - } - } - - if( ($kaboom = !is_null($target)) ) { - $where['this'] = " WHERE id IN (" . implode(',',$target) . ")"; - } - - // Attachments are related to VERSION - foreach($children as $key => $reqVID) { - $result = $this->attachmentRepository->deleteAttachmentsFor($reqVID,$this->attachmentTableName); - } - - - // Going to work on REVISIONS - $implosion = implode(',',$children); - $sql = "/* $debugMsg */ " . - " SELECT id from {$this->tables['nodes_hierarchy']} " . - " WHERE parent_id IN ( {$implosion} ) " . - " AND node_type_id=" . - $this->node_types_descr_id['requirement_revision']; - - $revisionSet = $this->db->fetchRowsIntoMap($sql,'id'); - if( !is_null($revisionSet) ) { - $this->cfield_mgr->remove_all_design_values_from_node(array_keys($revisionSet)); - - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['req_revisions']} - WHERE parent_id IN ( {$implosion} ) "; - $result = $this->db->exec_query($sql); - - $sql = "/* $debugMsg */ - DELETE FROM {$this->tables['nodes_hierarchy']} - WHERE parent_id IN ( {$implosion} ) - AND node_type_id=" . $this->node_types_descr_id['requirement_revision']; - $result = $this->db->exec_query($sql); - } - $this->cfield_mgr->remove_all_design_values_from_node((array)$children); - - $where['children'] = " WHERE id IN ( {$implosion} ) "; - - $sql = "DELETE FROM {$this->tables['req_versions']} " . $where['children']; - $result = $this->db->exec_query($sql); - - $sql = "DELETE FROM {$this->tables['nodes_hierarchy']} " . - $where['children'] . - " AND node_type_id=" . $this->node_types_descr_id['requirement_version']; - $result = $this->db->exec_query($sql); - } - - $kaboom = $kaboom || ($deleteAll && $result); - if( $kaboom ) { - $sql = "DELETE FROM {$this->object_table} " . $where['this']; - $result = $this->db->exec_query($sql); - - $sql = "DELETE FROM {$this->tables['nodes_hierarchy']} " . $where['this'] . - " AND node_type_id=" . $this->node_types_descr_id['requirement']; - - $result = $this->db->exec_query($sql); - } - - $result = (!$result) ? lang_get('error_deleting_req') : 'ok'; - - $ctx = array('id' => $id); - event_signal('EVENT_TEST_REQUIREMENT_DELETE', $ctx); - - return $result; - } - - - - -/** collect coverage of Requirement - * @param string $req_id ID of req. - * @return assoc_array list of test cases [id, title] - * - * Notice regarding platforms: - * When doing Requirements Based Reports, we analize report situation - * on a Context composed by: - * Test project AND Test plan. - * - * We do this because we want to have a dynamic view (i.e. want to add exec info). - * - * When a Test plan has platforms defined, user get at GUI possibility to choose - * one platform. - * IMHO (franciscom) this has to change how coverage (dynamic) is computed. - * - * Static coverage: - * depicts relation bewteen Req and test cases spec, and platforms are not considered - * - * DYNAMIC coverage: - * depicts relation bewteen Req and test cases spec and exec status of these test case, - * and platforms have to be considered - * - */ -function get_coverage($id,$context=null,$options=null) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('options' => array('accessKey' => 'idx')); - $my['options'] = array_merge($my['options'], (array)$options); - - - $safe_id = intval($id); - $common = array(); - - $common['join'] = " FROM {$this->tables['nodes_hierarchy']} NH_TC " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id=NH_TC.id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id=NH_TCV.id " . - " JOIN {$this->tables['req_coverage']} RC ON RC.testcase_id = NH_TC.id "; - $common['where'] = " WHERE RC.req_id={$safe_id} "; - - if(is_null($context)) - { - $sql = "/* $debugMsg - Static Coverage */ " . - " SELECT DISTINCT NH_TC.id,NH_TC.name,TCV.tc_external_id,U.login,RC.creation_ts" . - $common['join'] . - " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = RC.author_id " . - $common['where']; - } - else - { - - $sql = "/* $debugMsg - Dynamic Coverage */ " . - " SELECT DISTINCT NH_TC.id,NH_TC.name,TCV.tc_external_id" . - $common['join'] . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NH_TCV.id " . - $common['where'] . - " AND TPTCV.testplan_id = " . intval($context['tplan_id']) . - " AND TPTCV.platform_id = " . intval($context['platform_id']); - } - $sql .= " ORDER BY tc_external_id "; - - - switch($my['options']['accessKey']) - { - case 'tcase_id': - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - break; - - case 'idx': - default: - $rs = $this->db->get_recordset($sql); - break; - } - return $rs; -} - - - /* - function: check_basic_data - do checks on title and reqdoc id, for a requirement - - Checks: - empty title - empty docid - docid already exists inside test project (DOCID is Test Project WIDE) - title alreday exists under same REQ SPEC (req. parent) - - - args: srs_id: req spec id (req parent) - title - reqdoc_id - [id]: default null - - - returns: map - keys: status_ok - msg - failure_reason - - @internal revision - 20110206 - franciscom - add new key on retval 'failure_reason' - 20110108 - franciscom - check on duplicate title under same parent - */ - function check_basic_data($srs_id,$tproject_id,$title,$reqdoc_id,$id = null) - { - - $ret['status_ok'] = 1; - $ret['msg'] = ''; - $ret['failure_reason'] = ''; - - $title = trim($title); - $reqdoc_id = trim($reqdoc_id); - - if ($title == "") - { - $ret['status_ok'] = 0; - $ret['msg'] = lang_get("warning_empty_req_title"); - $ret['failure_reason'] = 'empty_req_title'; - } - - if ($reqdoc_id == "") - { - $ret['status_ok'] = 0; - $ret['msg'] .= " " . lang_get("warning_empty_reqdoc_id"); - $ret['failure_reason'] = 'empty_reqdoc_id'; - } - - if($ret['status_ok']) - { - $ret['msg'] = 'ok'; - $rs = $this->getByDocID($reqdoc_id,$tproject_id); - if(!is_null($rs) && (is_null($id) || !isset($rs[$id]))) - { - $ret['msg'] = sprintf(lang_get("warning_duplicate_reqdoc_id"),$reqdoc_id); - $ret['status_ok'] = 0; - $ret['failure_reason'] = 'duplicate_reqdoc_id'; - } - } - - // check for duplicate title - // BUGID 4150 - if($ret['status_ok']) - { - $ret['msg'] = 'ok'; - $target = array('key' => 'title', 'value' => $title); - $getOptions = array('output' => 'id'); - $rs = $this->getByAttribute($target,$tproject_id,$srs_id,$getOptions); - if(!is_null($rs) && (is_null($id) || !isset($rs[$id]))) - { - $ret['failure_reason'] = 'sibling_req_with_same_title'; - $ret['msg'] = sprintf(lang_get("warning_sibling_req_with_same_title"),$title); - $ret['status_ok'] = 0; - } - } - - return $ret; - } - - - /* - function: create_tc_from_requirement - create testcases using requirements as input - - - args: - - returns: - - */ -function create_tc_from_requirement($mixIdReq,$srs_id, $user_id, $tproject_id = null, $tc_count=null) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $tcase_mgr = new testcase($this->db); - $tsuite_mgr = new testsuite($this->db); - - $auto_testsuite_name = $this->reqCfg->default_testsuite_name; - $node_descr_type = $this->tree_mgr->get_available_node_types(); - $empty_steps = null; - $empty_preconditions = ''; // fix for BUGID 2995 - - $labels['tc_created'] = lang_get('tc_created'); - - $output = null; - $reqSet = is_array($mixIdReq) ? $mixIdReq : array($mixIdReq); - - if( is_null($tproject_id) || $tproject_id == 0 ) { - $tproject_id = $this->tree_mgr->getTreeRoot($srs_id); - } - - if ( $this->reqCfg->use_req_spec_as_testsuite_name ) { - $full_path = $this->tree_mgr->get_path($srs_id); - $addition = " (" . lang_get("testsuite_title_addition") . ")"; - $truncate_limit = $this->fieldSize->testsuite_name - strlen($addition); - - // REQ_SPEC_A - // |-- REQ_SPEC_A1 - // |-- REQ_SPEC_A2 - // |- REQ100 - // |- REQ101 - // - // We will try to check if a test suite has already been created for - // top REQ_SPEC_A (we do search using automatic generated name as search criteria). - // If not => we need to create all path till leaves (REQ100 and REQ200) - // - // - // First search: we use test project - $parent_id = $tproject_id; - $deep_create = false; - foreach($full_path as $key => $node) { - // follow hierarchy of test suites to create - $tsuiteInfo = null; - - // deal with UTF-8 - // $testsuite_name = substr($node['name'],0,$truncate_limit). $addition; - $testsuite_name = mb_substr($node['name'],0,$truncate_limit,mb_detect_encoding($node['name'])) . $addition; - - if( !$deep_create ) { - // child test suite with this name, already exists on current parent ? - // At first a failure we will not check anymore an proceed with deep create - $sql = "/* $debugMsg */ SELECT id,name FROM {$this->tables['nodes_hierarchy']} NH " . - " WHERE name='" . $this->db->prepare_string($testsuite_name) . "' " . - " AND node_type_id=" . $node_descr_type['testsuite'] . - " AND parent_id = {$parent_id} "; - - // If returns more that one record use ALWAYS first - $tsuiteInfo = $this->db->fetchRowsIntoMap($sql,'id'); - } - - if( is_null($tsuiteInfo) ) { - $tsuiteInfo = $tsuite_mgr->create($parent_id,$testsuite_name,$this->reqCfg->testsuite_details); - $output[] = sprintf(lang_get('testsuite_name_created'), $testsuite_name); - $deep_create = true; - } - else { - $tsuiteInfo = current($tsuiteInfo); - $tsuite_id = $tsuiteInfo['id']; - } - $tsuite_id = $tsuiteInfo['id']; // last value here will be used as parent for test cases - $parent_id = $tsuite_id; - } - $output[]=sprintf(lang_get('created_on_testsuite'), $testsuite_name); - } else { - // don't use req_spec as testsuite name - // Warning: - // We are not maintaining hierarchy !!! - $sql=" SELECT id FROM {$this->tables['nodes_hierarchy']} NH " . - " WHERE name='" . $this->db->prepare_string($auto_testsuite_name) . "' " . - " AND parent_id=" . $tproject_id . " " . - " AND node_type_id=" . $node_descr_type['testsuite']; - - $result = $this->db->exec_query($sql); - if ($this->db->num_rows($result) == 1) { - $row = $this->db->fetch_array($result); - $tsuite_id = $row['id']; - $label = lang_get('created_on_testsuite'); - } else { - // not found -> create - tLog('test suite:' . $auto_testsuite_name . ' was not found.'); - $new_tsuite=$tsuite_mgr->create($tproject_id,$auto_testsuite_name,$this->reqCfg->testsuite_details); - $tsuite_id=$new_tsuite['id']; - $label = lang_get('testsuite_name_created'); - } - $output[]=sprintf($label, $auto_testsuite_name); - } - /* end contribution */ - - // create TC - $createOptions = array(); - $createOptions['check_names_for_duplicates'] = config_get('check_names_for_duplicates'); - $createOptions['action_on_duplicate_name'] = config_get('action_on_duplicate_name'); - - $testcase_importance_default = config_get('testcase_importance_default'); - - // compute test case order - $testcase_order = config_get('treemenu_default_testcase_order'); - $nt2exclude=array('testplan' => 'exclude_me','requirement_spec'=> 'exclude_me','requirement'=> 'exclude_me'); - - $siblings = $this->tree_mgr->get_children($tsuite_id,$nt2exclude); - if( !is_null($siblings) ) { - $dummy = end($siblings); - $testcase_order = $dummy['node_order']; - } - - foreach ($reqSet as $reqID) { - $reqData = $this->get_by_id($reqID,requirement_mgr::LATEST_VERSION); - $count = (!is_null($tc_count)) ? $tc_count[$reqID] : 1; - $reqData = $reqData[0]; - - // Generate name with progessive - $instance=1; - $getOptions = array('check_criteria' => 'like','access_key' => 'name'); - $itemSet = $tcase_mgr->getDuplicatesByName($reqData['title'],$tsuite_id,$getOptions); - - $nameSet = null; - if( !is_null($itemSet) ){ - $nameSet = array_flip(array_keys($itemSet)); - } - - for ($idx = 0; $idx < $count; $idx++) { - $testcase_order++; - - // We have a little problem to work on: - // suppose you have created: - // TC [1] - // TC [2] - // TC [3] - // If we delete TC [2] - // When I got siblings il will got 2, if I create new progressive using next, - // it will be 3 => I will get duplicated name. - // - // Seems better option can be: - // Get all siblings names, put on array, create name an check if exists, if true - // generate a new name. - // This may be at performance level is better than create name then check on db, - // because this approach will need more queries to DB - // - $tcase_name = $reqData['title'] . " [{$instance}]"; - if( !is_null($nameSet) ) { - while( isset($nameSet[$tcase_name]) ) { - $instance++; - $tcase_name = $reqData['title'] . " [{$instance}]"; - } - } - $nameSet[$tcase_name]=$tcase_name; - - $prefix = ($this->reqCfg->use_testcase_summary_prefix_with_title_and_version) - ? sprintf($this->reqCfg->testcase_summary_prefix_with_title_and_version, - $reqID, $reqData['version_id'], $reqData['title'], $reqData['version']) - : $this->reqCfg->testcase_summary_prefix; - $content = ($this->reqCfg->copy_req_scope_to_tc_summary) ? $prefix . $reqData['scope'] : $prefix; - - $tcase = $tcase_mgr->create($tsuite_id,$tcase_name,$content, - $empty_preconditions, $empty_steps,$user_id,null, - $testcase_order,testcase::AUTOMATIC_ID,TESTCASE_EXECUTION_TYPE_MANUAL, - $testcase_importance_default,$createOptions); - - $tcase_name = $tcase['new_name'] == '' ? $tcase_name : $tcase['new_name']; - $output[] = sprintf($labels['tc_created'], $tcase_name); - - // create coverage dependency - $rrv = array('id' => $reqData['id'], 'version_id' => $reqData['version_id']); - $ttcv = array('id' => $tcase['id'], 'version_id' => $tcase['tcversion_id']); - - if (!$this->assignReqVerToTCVer($rrv,$ttcv,$user_id) ) { - $output[] = 'Test case: ' . $tcase_name . " was not created"; - } - } - } - return $output; -} - - - /* - function: assign_to_tcase - assign requirement(s) to test case - Will use always latest ACTIVE Versions - - args: req_id: can be an array of requirement id - testcase_id - - returns: 1/0 - */ - function assign_to_tcase($req_id,$testcase_id,$author_id) { - - static $tcMgr; - - if(!$tcMgr) { - $tcMgr = new testcase($this->db); - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $output = 0; - $now = $this->db->db_now(); - if($testcase_id && $req_id) { - - // Need to get latest version for each requirement - $reqIDSet = (array)$req_id; - $gopt = array('output' => 'id,version'); - - $reqLatestVersionIDSet = array(); - $reqLatestVersionNumberSet = array(); - foreach( $reqIDSet as $req ) { - $isofix = $this->get_last_version_info($req, $gopt); - $reqLatestVersionIDSet[] = $isofix['id']; - $reqLatestVersionNumberSet[] = $isofix['version']; - } - - // Get Latest Active Test Case Version - $tcv = current($tcMgr->get_last_active_version($testcase_id)); - $ltcv = $tcv['tcversion_id']; - $ltcvNum = $tcv['version']; - $in_clause = implode(",",$reqLatestVersionIDSet); - - // - $sql = " /* $debugMsg */ " . - " SELECT req_id,testcase_id,req_version_id,tcversion_id " . - " FROM {$this->tables['req_coverage']} " . - " WHERE req_version_id IN ({$in_clause}) " . - " AND tcversion_id = {$ltcv}"; - - $coverage = $this->db->fetchRowsIntoMap($sql,'req_version_id'); - - // Useful for audit - $tcInfo = $this->tree_mgr->get_node_hierarchy_info($testcase_id); - - $loop2do = count($reqLatestVersionIDSet); - for($idx=0; $idx < $loop2do; $idx++) { - if( is_null($coverage) || - !isset($coverage[$reqLatestVersionIDSet[$idx]]) ) { - $sql = " INSERT INTO {$this->tables['req_coverage']} " . - " (req_id,testcase_id,req_version_id, tcversion_id," . - " author_id,creation_ts) " . - " VALUES ({$reqIDSet[$idx]},{$testcase_id}," . - " $reqLatestVersionIDSet[$idx],{$ltcv}," . - " {$author_id},{$now})"; - $result = $this->db->exec_query($sql); - if ($this->db->affected_rows() == 1) { - $output = 1; - - // For audit - $reqInfo = $this->tree_mgr->get_node_hierarchy_info($reqIDSet[$idx]); - if($tcInfo && $reqInfo) { - logAuditEvent(TLS("audit_reqv_assigned_tcv", - $reqInfo['name'], - $reqLatestVersionNumberSet[$idx], - $tcInfo['name'], - $ltcvNum), - "ASSIGN",$this->object_table); - } - } - } - else - { - $output = 1; - } - } - } - return $output; - } - - - /** - * - */ - function assignToTCaseUsingLatestVersions($req_id,$testcase_id,$author_id) { - return $this->assign_to_tcase($req_id,$testcase_id,$author_id); - } - - - - /* - function: get_relationships - - args : - - returns: - - */ - function get_relationships($req_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT nodes_hierarchy.id,nodes_hierarchy.name " . - " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy, " . - " {$this->tables['req_coverage']} req_coverage " . - " WHERE req_coverage.testcase_id = nodes_hierarchy.id " . - " AND req_coverage.req_id={$req_id}"; - - return ($this->db->get_recordset($sql)); - } - - - /* - function: get_all_for_tcase - get all requirements assigned to a test case - A filter can be applied to do search on all req spec, - or only on one. - - - args: testcase_id - [srs_id]: default 'all' - - returns: - - - - */ -function get_all_for_tcase($testcase_id, $srs_id = 'all') -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT REQ.id,REQ.req_doc_id,NHA.name AS title, " . - " NHB.name AS req_spec_title,REQ_COVERAGE.testcase_id " . - " FROM {$this->object_table} REQ, " . - " {$this->tables['req_coverage']} REQ_COVERAGE," . - " {$this->tables['nodes_hierarchy']} NHA," . - " {$this->tables['nodes_hierarchy']} NHB," . - " {$this->tables['req_specs']} RSPEC " ; - - $idList = implode(",",(array)$testcase_id); - $sql .= " WHERE REQ_COVERAGE.testcase_id IN (" . $idList . ")"; - $sql .= " AND REQ.srs_id=RSPEC.id AND REQ_COVERAGE.req_id=REQ.id " . - " AND NHA.id=REQ.id AND NHB.id=RSPEC.id " ; - - // if only for one specification is required - if ($srs_id != 'all') - { - $sql .= " AND REQ.srs_id=" . $srs_id; - } - if (is_array($testcase_id)) - { - return $this->db->fetchRowsIntoMap($sql,'testcase_id',true); - } - else - { - return $this->db->get_recordset($sql); - } -} - - - - - /* - function: - - args : - - returns: - - */ - function check_title($title) - { - $ret = array('status_ok' => 1, 'msg' => 'ok'); - - if ($title == "") - { - $ret['status_ok'] = 0; - $ret['msg'] = lang_get("warning_empty_req_title"); - } - - return $ret; - } - -/* - function: - - args : - $nodes: array with req_id in order - returns: - -*/ -function set_order($map_id_order) -{ - $this->tree_mgr->change_order_bulk($map_id_order); -} - - -/** - * exportReqToXML - * - * @param int $id requirement id - * @param int $tproject_id: optional default null. - * useful to get custom fields (when this feature will be developed). - * - * @return string with XML code - * - */ -function exportReqToXML($id,$tproject_id=null, $inc_attachments=false) -{ - $req = $this->get_by_id($id,requirement_mgr::LATEST_VERSION); - $reqData[] = $req[0]; - - $elemTpl = "\t" . "" . - "\n\t\t" . "" . - "\n\t\t" . "<![CDATA[||TITLE||]]>" . - "\n\t\t" . "||VERSION||" . - "\n\t\t" . "||REVISION||" . - "\n\t\t" . "||NODE_ORDER||". - "\n\t\t" . "". - "\n\t\t" . "" . - "\n\t\t" . "" . - "\n\t\t" . "" . - "\n\t\t" . $this->customFieldValuesAsXML($id,$req[0]['version_id'],$tproject_id); - - // add req attachment content if checked in GUI - if ($inc_attachments) - { - $attachments=null; - // retrieve all attachments linked to req - $attachmentInfos = $this->attachmentRepository->getAttachmentInfosFor($id,$this->attachmentTableName,'id'); - - // get all attachments content and encode it in base64 - if ($attachmentInfos) - { - foreach ($attachmentInfos as $attachmentInfo) - { - $aID = $attachmentInfo["id"]; - $content = $this->attachmentRepository->getAttachmentContent($aID, $attachmentInfo); - - if ($content != null) - { - $attachments[$aID]["id"] = $aID; - $attachments[$aID]["name"] = $attachmentInfo["file_name"]; - $attachments[$aID]["file_type"] = $attachmentInfo["file_type"]; - $attachments[$aID]["title"] = $attachmentInfo["title"]; - $attachments[$aID]["date_added"] = $attachmentInfo["date_added"]; - $attachments[$aID]["content"] = base64_encode($content); - } - } - - if( !is_null($attachments) && count($attachments) > 0 ) - { - $attchRootElem = "\n{{XMLCODE}}\t\t\n"; - $attchElemTemplate = "\t\t\t\n" . - "\t\t\t\t\n" . - "\t\t\t\t\n" . - "\t\t\t\t\n" . - "\t\t\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . - "\t\t\t\t\n" . - "\t\t\t\t\n" . - "\t\t\t\n"; - - $attchDecode = array ("||ATTACHMENT_ID||" => "id", "||ATTACHMENT_NAME||" => "name", - "||ATTACHMENT_FILE_TYPE||" => "file_type", "||ATTACHMENT_TITLE||" => "title", - "||ATTACHMENT_DATE_ADDED||" => "date_added", "||ATTACHMENT_CONTENT||" => "content"); - $elemTpl .= exportDataToXML($attachments,$attchRootElem,$attchElemTemplate,$attchDecode,true); - } - } - } - $elemTpl .= "\n\t" . "" . "\n"; - - $info = array("||DOCID||" => "req_doc_id","||TITLE||" => "title", - "||DESCRIPTION||" => "scope","||STATUS||" => "status", - "||TYPE||" => "type","||NODE_ORDER||" => "node_order", - "||EXPECTED_COVERAGE||" => "expected_coverage", - "||VERSION||" => "version","||REVISION||" => "revision"); - - $xmlStr = exportDataToXML($reqData,"{{XMLCODE}}",$elemTpl,$info,true); - return $xmlStr; - -} - - -/** - * xmlToMapRequirement - * - */ -function xmlToMapRequirement($xml_item) -{ - // Attention: following PHP Manual SimpleXML documentation, Please remember to cast - // before using data from $xml, - if( is_null($xml_item) ) - { - return null; - } - - $dummy=array(); - foreach($xml_item->attributes() as $key => $value) - { - $dummy[$key] = (string)$value; // See PHP Manual SimpleXML documentation. - } - - $dummy['node_order'] = (int)$xml_item->node_order; - $dummy['title'] = (string)$xml_item->title; - $dummy['docid'] = (string)$xml_item->docid; - $dummy['description'] = (string)$xml_item->description; - $dummy['status'] = (string)$xml_item->status; - $dummy['type'] = (string)$xml_item->type; - $dummy['expected_coverage'] = (int)$xml_item->expected_coverage; - - if( property_exists($xml_item,'custom_fields') ) - { - $dummy['custom_fields']=array(); - foreach($xml_item->custom_fields->children() as $key) - { - $dummy['custom_fields'][(string)$key->name]= (string)$key->value; - } - } - if( property_exists($xml_item,'attachments') ) - { - $dummy['attachments'] = array(); - foreach($xml_item->attachments->children() as $attachment) - { - $attach_id = (int)$attachment->id; - $dummy['attachments'][$attach_id]['id'] = (int)$attachment->id; - $dummy['attachments'][$attach_id]['name'] = (string)$attachment->name; - $dummy['attachments'][$attach_id]['file_type'] = (string)$attachment->file_type; - $dummy['attachments'][$attach_id]['title'] = (string)$attachment->title; - $dummy['attachments'][$attach_id]['date_added'] = (string)$attachment->date_added; - $dummy['attachments'][$attach_id]['content'] = (string)$attachment->content; - } - } - return $dummy; -} - - - - - - -/** - * createFromXML - * - * @internal revisions - */ -function createFromXML($xml,$tproject_id,$parent_id,$author_id,$filters = null,$options=null) -{ - $reqAsMap = $this->xmlToMapRequirement($xml); - - // Map structure - // node_order => 0 - // title => Breaks - // docid => MAZDA3-0001 - // description => Heavy Rain Conditions - // status => [empty string] - // type => [empty string] - // expected_coverage => 0 - - return $this->createFromMap($reqAsMap,$tproject_id,$parent_id,$author_id,$filters,$options); -} - - -/** - * createFromMap - * - * Map structure - * node_order => 0 - * title => Breaks - * docid => MAZDA3-0001 - * description => Heavy Rain Conditions - * status => [empty string] - * type => [empty string] - * expected_coverage => 0 - * - * @internal revisions - */ -function createFromMap($req,$tproject_id,$parent_id,$author_id,$filters = null,$options=null) -{ - static $missingCfMsg; - static $linkedCF; - static $messages; - static $labels; - static $fieldSize; - static $doProcessCF = false; - static $debugMsg; - static $getByAttributeOpt; - static $getLastChildInfoOpt; - - if(is_null($linkedCF) ) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $fieldSize = config_get('field_size'); - - $linkedCF = $this->cfield_mgr->get_linked_cfields_at_design( - $tproject_id,cfield_mgr::CF_ENABLED,null,'requirement',null,'name'); - $doProcessCF = true; - - $messages = array(); - $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); - $messages['cfield'] = lang_get('cf_value_not_imported_missing_cf_on_testproject'); - - $labels = array('import_req_created' => '', - 'import_req_skipped' =>'', 'import_req_updated' => '', - 'frozen_req_unable_to_import' => '', 'requirement' => '', - 'import_req_new_version_created' => '', - 'import_req_update_last_version_failed' => '', - 'import_req_new_version_failed' => '', - 'import_req_skipped_plain' => '', - 'req_title_lenght_exceeded' => '', - 'req_docid_lenght_exceeded' => ''); - foreach($labels as $key => $dummy) { - $labels[$key] = lang_get($key); - } - $getByAttributeOpt = array('output' => 'id'); - $getLastChildInfoOpt = array('child_type' => 'version', 'output' => ' CHILD.is_open, CHILD.id '); - } - - $cf2insert=null; - $status_ok = true; - $user_feedback = null; - $dummy = ''; - $result = null; - - $newReq = null; - $copy_req = null; - $getOptions = array('output' => 'minimun'); - $has_filters = !is_null($filters); - $my['options'] = array( 'hitCriteria' => 'docid' , 'actionOnHit' => "update", 'skipFrozenReq' => true); - $my['options'] = array_merge($my['options'], (array)$options); - - // 20160314 - // Check data than can create issue when writting to DB due to lenght - // - $req['title'] = trim($req['title']); - $req['docid'] = trim($req['docid']); - - $checkLengthOK = true; - $what2add = ''; - if( strlen($req['title']) > $fieldSize->req_title ) - { - $checkLengthOK = false; - $what2add = $labels['req_title_lenght_exceeded'] . '/'; - } - - if( strlen($req['docid']) > $fieldSize->req_docid ) - { - $checkLengthOK = false; - $what2add .= $labels['req_docid_lenght_exceeded']; - } - - if( $checkLengthOK == FALSE ) - { - $msgID = 'import_req_skipped_plain'; - $user_feedback[] = array('doc_id' => $req['docid'],'title' => $req['title'], - 'import_status' => sprintf($labels[$msgID],$what2add)); - - return $user_feedback; - } - - // Check: - // If item with SAME DOCID exists inside container - // If there is a hit - // We will follow user option: update,create new version - // - // If do not exist check must be repeated, but on WHOLE test project - // If there is a hit -> we can not create - // else => create - // - // $getOptions = array('output' => 'minimun'); - $msgID = 'import_req_skipped'; - - $target = array('key' => $my['options']['hitCriteria'], 'value' => $req[$my['options']['hitCriteria']]); - - // IMPORTANT NOTICE - // When get is done by attribute that can not be unique (like seems to happen now 20110108 with - // title), we can get more than one hit and then on this context we can not continue - // with requested operation - $check_in_reqspec = $this->getByAttribute($target,$tproject_id,$parent_id,$getByAttributeOpt); - - // while working on BUGID 4210, new details came to light. - // In addition to hit criteria there are also the criteria that we use - // when creating/update item using GUI, and these criteria have to be - // checked abd fullfilled. - // - if(is_null($check_in_reqspec)) - { - $check_in_tproject = $this->getByAttribute($target,$tproject_id,null,$getByAttributeOpt); - - if (is_null($check_in_tproject)) { - $importMode = 'creation'; - $newReq = $this->create($parent_id,$req['docid'],$req['title'],$req['description'], - $author_id,$req['status'],$req['type'],$req['expected_coverage'], - $req['node_order'],$tproject_id,array('quickAndDirty' => true)); - $reqID = $newReq['id']; - $fk_id = $newReq['version_id']; // for attachments - if( ($status_ok = ($newReq['status_ok'] == 1)) ){ - $msgID = 'import_req_created'; - } else { - $msgID = 'import_req_skipped_plain'; - $result['msg'] = $newReq['msg']; // done to use what2add logic far below - } - - } - else - { - // Can not have req with same req doc id on another branch => BLOCK - // What to do if is Frozen ??? -> now ignore and update anyway - $msgID = 'import_req_skipped'; - $status_ok = false; - } - } - else - { - // IMPORTANT NOTICE - // When you - // Need to get Last Version no matter active or not. - $reqID = key($check_in_reqspec); - $last_version = $this->get_last_child_info($reqID,$getLastChildInfoOpt); - $msgID = 'frozen_req_unable_to_import'; - $status_ok = false; - - if( $last_version['is_open'] == 1 || !$my['options']['skipFrozenReq']) { - switch ($my['options']['actionOnHit']) { - case 'update_last_version': - $importMode = 'update'; - $result = $this->update($reqID,$last_version['id'],$req['docid'],$req['title'],$req['description'], - $author_id,$req['status'],$req['type'],$req['expected_coverage'], - $req['node_order']); - $fk_id = $last_version['id']; // for attachment management - $status_ok = ($result['status_ok'] == 1); - if( $status_ok) { - $msgID = 'import_req_updated'; - } else { - $msgID = 'import_req_update_last_version_failed'; - } - break; - - case 'create_new_version': - $newItem = $this->create_new_version($reqID,$author_id,array('notify' => true)); - - // Set always new version to NOT Frozen - $this->updateOpen($newItem['id'],1); - - // hmm wrong implementation - // Need to update ALL fields on new version then why do not use - // update() ? - $newReq['version_id'] = $newItem['id']; - $fk_id = $newReq['version_id']; // for attachment management - - // IMPORTANT NOTICE: - // We have to DO NOT UPDATE REQDOCID with info received from USER - // Because ALL VERSION HAS TO HAVE docid, or we need to improve our checks - // and if update fails => we need to delete new created version. - $title = trim_and_limit($req['title'],$fieldSize->req_title); - $importMode = 'update'; - $result = $this->update($reqID,$newItem['id'],$req['docid'],$title,$req['description'], - $author_id,$req['status'],$req['type'],$req['expected_coverage'], - $req['node_order']); - - $status_ok = ($result['status_ok'] == 1); - if( $status_ok) - { - $msgID = 'import_req_new_version_created'; - } - else - { - // failed -> removed just created version - $this->delete($reqID,$newItem['id']); - $msgID = 'import_req_new_version_failed'; - } - break; - } - } - } - $what2add = is_null($result) ? $req['docid'] : $req['docid'] . ':' . $result['msg']; - - $user_feedback[] = array('doc_id' => $req['docid'],'title' => $req['title'], - 'import_status' => sprintf($labels[$msgID],$what2add)); - - $hasAttachments = array_key_exists('attachments',$req); - // process attachements for creation and update - if ($status_ok && $hasAttachments) { - $addAttachResp = $this->processAttachments( $importMode, $fk_id, $req['attachments'], $feedbackMsg ); - } - - // display only problems during attachments import - if( isset($addAttachResp) && !is_null($addAttachResp) ) { - foreach($addAttachResp as $att_name){ - $user_feedback[] = - array('doc_id' => $req['docid'], - 'title' => $req['title'], - 'import_status' => sprintf(lang_get('import_req_attachment_skipped'),$att_name)); - } - } - - if( $status_ok && $doProcessCF && isset($req['custom_fields']) && !is_null($req['custom_fields']) ) - { - $req_version_id = !is_null($newReq) ? $newReq['version_id'] : $last_version['id']; - $cf2insert = null; - foreach($req['custom_fields'] as $cfname => $cfvalue) - { - $cfname = trim($cfname); - if( isset($linkedCF[$cfname]) ) - { - $cf2insert[$linkedCF[$cfname]['id']]=array('type_id' => $linkedCF[$cfname]['type'], - 'cf_value' => $cfvalue); - } - else - { - if( !isset($missingCfMsg[$cfname]) ) - { - $missingCfMsg[$cfname] = sprintf($messages['cfield'],$cfname,$labels['requirement']); - } - $user_feedback[] = array('doc_id' => $req['docid'],'title' => $req['title'], - 'import_status' => $missingCfMsg[$cfname]); - } - } - if( !is_null($cf2insert) ) - { - $this->cfield_mgr->design_values_to_db($cf2insert,$req_version_id,null,'simple'); - } - } - - return $user_feedback; -} - - /** - * processAttachments - * - * Analyze attachments info related to requirement to define if the the attachment has to be added. - * attachments are ignored only if a attachment with the same ID is already linked to the target requirement. - * - * return an array of all attachments names of IDs already linked to target requirement (to display warning messages). - * - */ - - function processAttachments($importMode, $srs_id, $attachments, $feedbackMsg ) - { - $tables = tlObjectWithDB::getDBTables(array('req_versions','attachments')); - - $knownAttachments = array(); - foreach( $attachments as $attachment ) - { - $addAttachment = true; - if($importMode == 'update'){ - // try to bypass the importation of already known attachments. - // Check in database if the attachment with the same ID is linked to the rspec with the same internal ID - // The couple attachment ID + InternalID is used as a kind of signature to avoid duplicates. - // If signature is not precise enough, could add the use of attachment timestamp (date_added in XML file). - $sql = " SELECT ATT.id from {$tables['attachments']} ATT " . - " WHERE ATT.id='{$this->db->prepare_string($attachment[id])}' " . - " AND ATT.fk_id={$srs_id} "; - $rsx=$this->db->get_recordset($sql); - $addAttachment = ( is_null($rsx) || count($rsx) < 1 ); - if( $addAttachment === false ){ // inform user that the attachment has been skipped - $knownAttachments[] = $attachment['name']; - } - } - if($addAttachment){ - $attachRepo = tlAttachmentRepository::create($this->db); - $fileInfo = $attachRepo->createAttachmentTempFile( $attachment['content'] ); - $fileInfo['name'] = $attachment['name']; - $fileInfo['type'] = $attachment['file_type']; - $attachRepo->insertAttachment( $srs_id, - $tables['req_versions'], $attachment['title'], $fileInfo); - } - } - return $knownAttachments; - } - -// ---------------------------------------------------------------------------- -// Custom field related functions -// ---------------------------------------------------------------------------- - -/* - function: get_linked_cfields - Get all linked custom fields. - Remember that custom fields are defined at system wide level, and - has to be linked to a testproject, in order to be used. - - - args: id: requirement id - $child_id: requirement version id or requirement revision id - [parent_id]: this information is vital, - to get the linked custom fields. - null -> use requirement_id as starting point. - !is_null -> use this value as testproject id - - returns: map/hash - key: custom field id - value: map with custom field definition and value assigned for choosen requirement, - with following keys: - - id: custom field id - name - label - type: custom field type - possible_values: for custom field - default_value - valid_regexp - length_min - length_max - show_on_design - enable_on_design - show_on_execution - enable_on_execution - display_order - value: value assigned to custom field for this requirement - null if for this requirement custom field was never edited. - - node_id: requirement id - null if for this requirement, custom field was never edited. -*/ -function get_linked_cfields($id,$child_id,$parent_id=null,$opt=null) -{ - $options = array('access_key' => null); - $options = array_merge($options,(array)$opt); - - if( !is_null($parent_id) ) { - $tproject_id = $parent_id; - } else { - $req_info = $this->get_by_id($id); - $tproject_id = $req_info[0]['testproject_id']; - unset($req_info); - } - - $cf_map = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id,cfield_mgr::ENABLED,null,'requirement', - $child_id,$options['access_key']); - return $cf_map; -} - - -/* - function: html_table_of_custom_field_inputs - Return html code, implementing a table with custom fields labels - and html inputs, for choosen requirement. - Used to manage user actions on custom fields values. - - - args: $id - $version_id --- BUGID - NEEDS CHANGES - [parent_id]: need to undertad to which testproject the requirement belongs. - this information is vital, to get the linked custom fields. - null -> use requirement_id as starting point. - !is_null -> use this value as starting point. - - - [$name_suffix]: must start with '_' (underscore). - Used when we display in a page several items - (example during test case execution, several test cases) - that have the same custom fields. - In this kind of situation we can use the item id as name suffix. - - - returns: html string -*/ -function html_table_of_custom_field_inputs($id,$version_id,$parent_id=null,$name_suffix='', $input_values = null) -{ - $cf_map = $this->get_linked_cfields($id,$version_id,$parent_id,$name_suffix); - $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map,$name_suffix,$input_values); - return $cf_smarty; -} - - -/* - function: html_table_of_custom_field_values - Return html code, implementing a table with custom fields labels - and custom fields values, for choosen requirement. - You can think of this function as some sort of read only version - of html_table_of_custom_field_inputs. - - - args: $id - $child_id: req version or req revision ID - - returns: html string - -*/ -function html_table_of_custom_field_values($id,$child_id,$tproject_id=null) -{ - $NO_WARNING_IF_MISSING=true; - $cf_smarty = ''; - - $root_id = is_null($id) ? $tproject_id : null; - $cf_map = $this->get_linked_cfields($id,$child_id,$root_id); - - $show_cf = config_get('custom_fields')->show_custom_fields_without_value; - - if(!is_null($cf_map)) { - foreach($cf_map as $cf_id => $cf_info) { - // if user has assigned a value, then node_id is not null - if($cf_info['node_id'] || $show_cf) { - $label = str_replace(TL_LOCALIZE_TAG,'', - lang_get($cf_info['label'],null,$NO_WARNING_IF_MISSING)); - - $cf_smarty .= '' . - htmlspecialchars($label) . ":" . - $this->cfield_mgr->string_custom_field_value($cf_info,$child_id) . - "\n"; - } - } - - if(trim($cf_smarty) != "") - { - $cf_smarty = "" . $cf_smarty . "
    "; - } - } - return $cf_smarty; -} // function end - - - /* - function: values_to_db - write values of custom fields. - - args: $hash: - key: custom_field__. - Example custom_field_0_67 -> 0=> string field - - $node_id: - - [$cf_map]: hash -> all the custom fields linked and enabled - that are applicable to the node type of $node_id. - - For the keys not present in $hash, we will write - an appropriate value according to custom field - type. - - This is needed because when trying to udpate - with hash being $_REQUEST, $_POST or $_GET - some kind of custom fields (checkbox, list, multiple list) - when has been deselected by user. - - - rev: - */ - function values_to_db($hash,$node_id,$cf_map=null,$hash_type=null) - { - $this->cfield_mgr->design_values_to_db($hash,$node_id,$cf_map,$hash_type); - } - - - /** - * customFieldValuesAsXML - * - * @param $id: requirement spec id - * @param $tproject_id: test project id - * - * - */ - function customFieldValuesAsXML($id,$version_id,$tproject_id) - { - $xml = null; - $cfMap=$this->get_linked_cfields($id,$version_id,$tproject_id); - if( !is_null($cfMap) && count($cfMap) > 0 ) { - $xml = $this->cfield_mgr->exportValueAsXML($cfMap); - } - return $xml; - } - - - - - - /* - function: getByDocID - get req information using document ID as access key. - - args : doc_id: - [tproject_id] - [parent_id] -> req spec parent of requirement searched - default 0 -> case sensivite search - - returns: map. - key: req spec id - value: req info, map with following keys: - id - doc_id - testproject_id - title - scope - */ - function getByDocID($doc_id,$tproject_id=null,$parent_id=null, $options = null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('check_criteria' => '=', 'access_key' => 'id', - 'case' => 'sensitive', 'output' => 'standard'); - $my['options'] = array_merge($my['options'], (array)$options); - - - $output=null; - $the_doc_id = $this->db->prepare_string(trim($doc_id)); - switch($my['options']['check_criteria']) - { - case '=': - default: - $check_criteria = " = '{$the_doc_id}' "; - break; - - case 'like': - $check_criteria = " LIKE '{$the_doc_id}%' "; - break; - } - - $sql = " /* $debugMsg */ SELECT "; - switch($my['options']['output']) - { - case 'standard': - $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id, " . - " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order "; - break; - - case 'minimun': - $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id"; - break; - } - - $sql .= " FROM {$this->object_table} REQ " . - " /* Get Req info from NH */ " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . - " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . - " WHERE REQ.req_doc_id {$check_criteria} "; - - if( !is_null($tproject_id) ) - { - $sql .= " AND REQ_SPEC.testproject_id={$tproject_id}"; - } - - if( !is_null($parent_id) ) - { - $sql .= " AND REQ.srs_id={$parent_id}"; - } - - $out = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']); - - return $out; - } - - /** - * Copy a requirement to a new requirement specification - * requirement DOC ID will be changed because must be unique inside - * MASTER CONTAINER (test project) - * - * @param integer $id: requirement ID - * @param integer $parent_id: target req spec id (where we want to copy) - * @param integer $user_id: who is requesting copy - * @param integer $tproject_id: FOR SOURCE ($id), optional, - * is null will be computed here - * @param array $options: map - * - */ - function copy_to($id,$parent_id,$user_id,$tproject_id=null,$options=null) { - $new_item = array('id' => -1, 'status_ok' => 0, 'msg' => 'ok', 'mappings' => null); - $my['options'] = array('copy_also' => null, 'caller' => ''); - $my['options'] = array_merge($my['options'], (array)$options); - - if( is_null($my['options']['copy_also']) ) { - $my['options']['copy_also'] = array('testcase_assignment' => true); - } - - $copyReqVTCVLinks = isset($my['options']['copy_also']['testcase_assignment']) && - $my['options']['copy_also']['testcase_assignment']; - - $root = $tproject_id; - if( is_null($root) ) { - $reqSpecMgr = new requirement_spec_mgr($this->db); - $target = $reqSpecMgr->get_by_id($parent_id); - $root = $target['testproject_id']; - } - - // NEED INLINE REFACTORING - $item_versions = $this->get_by_id($id); - if($item_versions) { - if($my['options']['caller'] == 'copy_testproject') { - $target_doc = $item_versions[0]['req_doc_id']; - $title = $item_versions[0]['title']; - } else { - // REQ DOCID is test project wide => can not exist duplicates inside - // a test project => we need to generate a new one using target as - // starting point - $target_doc = $this->generateDocID($id,$root); - - // If a sibling exists with same title => need to generate automatically - // a new one. - $title = $this->generateUniqueTitle($item_versions[0]['title'], - $parent_id,$root); - } - - $new_item = $this->create_req_only($parent_id,$target_doc,$title, - $item_versions[0]['author_id'], - $item_versions[0]['node_order']); - - if ($new_item['status_ok']) { - $ret['status_ok']=1; - $new_item['mappings']['req'][$id] = $new_item['id']; - - foreach($item_versions as &$req_version) { - $op = $this->create_version($new_item['id'],$req_version['version'], - $req_version['scope'],$req_version['author_id'], - $req_version['status'],$req_version['type'], - $req_version['expected_coverage']); - - // need to explain how this mappings are used outside this method - // first thing that can see here, we are mixing req id and - // req version id on same hash. - // - $new_item['mappings']['req_version'][$req_version['version_id']] = $op['id']; - - // 2018 - $new_item['mappings']['req_tree'][$id][$req_version['version_id']] = - $op['id']; - - - // here we have made a mistake, that help to show that - // we have some memory issue - // with copy_cfields(). - // ALWAYS when we have tproject_id we have to use it!!! - // - $this->copy_cfields(array('id' => $req_version['id'], - 'version_id' => $req_version['version_id']), - array('id' => $new_item['id'], 'version_id' => $op['id']), - $tproject_id); - - - $source = $req_version['version_id']; - $dest = $op['id']; - $this->attachmentRepository->copyAttachments($source,$dest, - $this->attachmentTableName); - - // Seems that when we call this function during Test Project Copy - // we DO NOT USE this piece - - if( $copyReqVTCVLinks ) { - $lnk = $this->getGoodForReqVersion($req_version['version_id']); - if( !is_null($lnk) ) { - $reqAndVer = array('id' => $new_item['id'], - 'version_id' => $op['id']); - foreach($lnk as $links) { - foreach($links as $value) { - $tcAndVer = array('id' => $value['testcase_id'], - 'version_id' => $value['tcversion_id']); - $this->assignReqVerToTCVer($reqAndVer,$tcAndVer,$user_id); - } - } - } - } - - unset($op); - } - - } - } - - unset($item_versions); // does this help to release memory ? - - return $new_item; - } - - - /** - * - * - */ - function copy_attachments($source_id,$target_id) { - $this->attachmentRepository->copyAttachments($source_id,$target_id,$this->attachmentTableName); - } - - - /* - function: copy_cfields - Get all cfields linked to any testcase of this testproject - with the values presents for $from_id, testcase we are using as - source for our copy. - - args: from_id: source item id - to_id: target item id - - returns: - - - */ - function copy_cfields($source,$destination,$tproject_id=null) - { - $cfmap_from = $this->get_linked_cfields($source['id'],$source['version_id'],$tproject_id); - $cfield=null; - if( !is_null($cfmap_from) ) { - foreach($cfmap_from as $key => $value) { - $cfield[$key]=array("type_id" => $value['type'], "cf_value" => $value['value']); - } - $this->cfield_mgr->design_values_to_db($cfield,$destination['version_id'],null,'reqversion_copy_cfields'); - } - } - - - - /** - * - * - */ - function generateDocID($id, $tproject_id) - { - $item_info = $this->get_by_id($id); - $item_info = $item_info[0]; - - // Check if another req with same DOC ID exists on test project (MASTER CONTAINER), - // If yes generate a new DOC ID - $getOptions = array('check_criteria' => 'like', 'access_key' => 'req_doc_id'); - $itemSet = $this->getByDocID($item_info['req_doc_id'],$tproject_id,null,$getOptions); - - $target_doc = $item_info['req_doc_id']; - $instance = 1; - if( !is_null($itemSet) ) - { - $safety_len = 2; // use t - $mask = $this->reqCfg->duplicated_docid_algorithm->text; - - // req_doc_id has limited size then we need to be sure that generated id will - // not exceed DB size - $nameSet = array_flip(array_keys($itemSet)); - $prefix = trim_and_limit($item_info['req_doc_id'], - $this->fieldSize->req_docid-strlen($mask)-$safety_len); - - // $target_doc = $prefix . " [{$instance}]"; - $target_doc = $prefix . sprintf($mask,$instance); - while( isset($nameSet[$target_doc]) ) - { - $instance++; - $target_doc = $prefix . sprintf($mask,$instance); - } - } - return $target_doc; - } - - /** - * - * - */ - function create_req_only($srs_id,$reqdoc_id,$title,$user_id,$node_order=0) - { - static $debugMsg; - - if(!$debugMsg) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - } - - $req_id = $this->tree_mgr->new_node($srs_id,$this->node_types_descr_id['requirement'], - $title,$node_order); - $sql = "/* $debugMsg */ INSERT INTO {$this->object_table} " . - " (id, srs_id, req_doc_id)" . - " VALUES ({$req_id}, {$srs_id},'" . $this->db->prepare_string($reqdoc_id) . "')"; - - if (!$this->db->exec_query($sql)) - { - $result = array( 'id' => -1, 'status_ok' => 0); - $result['msg'] = lang_get('error_inserting_req'); - } - else - { - $result = array( 'id' => $req_id, 'status_ok' => 1, 'msg' => 'ok'); - } - - unset($sql); - unset($req_id); - - return $result; - } - - /* - function: create_version - - args: - - returns: - - - */ - function create_version($id,$version,$scope, $user_id, $status = TL_REQ_STATUS_VALID, - $type = TL_REQ_TYPE_INFO, $expected_coverage=1) - { - static $debugMsg; - - if(!$debugMsg) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - } - - $item_id = $this->tree_mgr->new_node($id,$this->node_types_descr_id['requirement_version']); - - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['req_versions']} " . - " (id,version,scope,status,type,expected_coverage,author_id,creation_ts) " . - " VALUES({$item_id},{$version},'" . trim($this->db->prepare_string($scope)) . "','" . - $this->db->prepare_string($status) . "','" . $this->db->prepare_string($type) . "'," . - "{$expected_coverage}," . intval($user_id) . "," . $this->db->db_now() . ")"; - - $result = $this->db->exec_query($sql); - $ret = array( 'msg' => 'ok', 'id' => $item_id, 'status_ok' => 1); - if (!$result) - { - $ret['msg'] = $this->db->error_msg(); - $ret['status_ok']=0; - $ret['id']=-1; - } - unset($sql); - unset($result); - unset($item_id); - return $ret; - } - - /* - function: create_new_version() - create a new version, doing BY DEFAULT a copy of last version. - If reqVersionID is passed, then this version will be used as source data. - - args : $id: requirement id - $user_id: who is doing this operation. - $reqVersionID = default null => use last version as source - - returns: - map: id: node id of created tcversion - version: version number (i.e. 5) - msg - - */ - function create_new_version($id,$user_id,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('reqVersionID' => null,'log_msg' => null, - 'notify' => false, - 'freezeSourceVersion' => true); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $reqVersionID = $my['opt']['reqVersionID']; - $log_msg = $my['opt']['log_msg']; - $notify = $my['opt']['notify']; - - // get a new id - $version_id = $this->tree_mgr->new_node($id,$this->node_types_descr_id['requirement_version']); - - // Needed to get higher version NUMBER, to generata new VERSION NUMBER - $sourceVersionInfo = $this->get_last_child_info($id, array('child_type' => 'version')); - if( is_null($sourceVersionInfo) ) { - throw new Exception($debugMsg . ' $this->get_last_child_info() RETURNED NULL !!! - WRONG - Open Issue on mantis.testlink.org'); - } - - // Till now everything is OK - if( $notify ) { - // be optimistic send email before doing nothing - $this->notifyMonitors($id,__FUNCTION__,$user_id,$log_msg); - } - - $newVersionNumber = $sourceVersionInfo['version']+1; - - $ret = array(); - $ret['id'] = $version_id; - $ret['version'] = $newVersionNumber; - $ret['msg'] = 'ok'; - - $sourceVersionID = is_null($reqVersionID) ? $sourceVersionInfo['id'] : $reqVersionID; - - // Update Link Status To Test Case Versions for Source Version - // is done on copy_version() - $this->copy_version($id,$sourceVersionID,$version_id,$newVersionNumber,$user_id); - - // need to update log message in new created version - $sql = "/* $debugMsg */ " . - " UPDATE {$this->tables['req_versions']} " . - " SET log_message = '" . trim($this->db->prepare_string($log_msg)) . "'" . - " WHERE id={$version_id}"; - $this->db->exec_query($sql); - - if( $my['opt']['freezeSourceVersion'] ) { - $this->updateOpen($sourceVersionInfo['id'],0); - } - - return $ret; - } - - - /** - * - * - */ - function get_last_version_info($id, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $info = null; - - $sql = " /* $debugMsg */ SELECT MAX(version) AS version " . - " FROM {$this->tables['req_versions']} REQV," . - " {$this->tables['nodes_hierarchy']} NH WHERE ". - " NH.id = REQV.id ". - " AND NH.parent_id = {$id} "; - - $max_version = $this->db->fetchFirstRowSingleColumn($sql,'version'); - if ($max_version) { - $my['opt'] = array('output' => 'default'); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - switch($my['opt']['output']) { - case 'id': - $fields = ' REQV.id '; - break; - - case 'id,version': - $fields = ' REQV.id, REQV.version '; - break; - - default: - $fields = ' REQV.*'; - break; - } - $sql = "/* $debugMsg */ SELECT {$fields} " . - " FROM {$this->tables['req_versions']} REQV," . - " {$this->tables['nodes_hierarchy']} NH ". - " WHERE version = {$max_version} AND NH.id = REQV.id AND NH.parent_id = {$id}"; - - $info = $this->db->fetchFirstRow($sql); - } - return $info; - } - - /** - * get last defined req doc id for specific test project - * - * @author Julian Krien - * - * @param int $tproj_id test project id - * - * @return string last defned req doc id - */ - - function get_last_doc_id_for_testproject($tproj_id) - { - $info = null; - $tproject_mgr = new testproject($this->db); - $all_reqs = $tproject_mgr->get_all_requirement_ids($tproj_id); - - if(count($all_reqs) > 0) - { - //only use maximum value of all reqs array - $last_req = max($all_reqs); - $last_req = $this->get_by_id($last_req); - $info = $last_req[0]['req_doc_id']; - } - return $info; - } - - /** - * - * - */ - function copy_version($id,$from_version_id,$to_version_id,$as_version_number,$user_id) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $now = $this->db->db_now(); - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['req_versions']} " . - " (id,version,author_id,creation_ts,scope,status,type,expected_coverage) " . - " SELECT {$to_version_id} AS id, {$as_version_number} AS version, " . - " {$user_id} AS author_id, {$now} AS creation_ts," . - " scope,status,type,expected_coverage " . - " FROM {$this->tables['req_versions']} " . - " WHERE id=" . intval($from_version_id); - $result = $this->db->exec_query($sql); - - $this->copy_cfields(array('id' => $id, 'version_id' => $from_version_id), - array('id' => $id, 'version_id' => $to_version_id)); - - - $this->copy_attachments($from_version_id,$to_version_id); - - - $reqTCLinksCfg = config_get('reqTCLinks'); - $freezeLinkOnNewReqVersion = $reqTCLinksCfg->freezeLinkOnNewReqVersion; + + * @copyright 2007-2020, TestLink community + * + * Manager for requirements. + * Requirements are children of a requirement specification (requirements container) + * + * + */ + +// Needed to use extends tlObjectWithAttachments, If not present autoload fails. +require_once dirname(__FILE__) . '/attachments.inc.php'; + +class requirement_mgr extends tlObjectWithAttachments +{ + + protected $db; + + public $cfield_mgr; + + private $my_node_type; + + public $tree_mgr; + + private $node_types_descr_id; + + private $node_types_id_descr; + + public $attachmentTableName; + + // 20100220 - franciscom - I'm will work only on XML + // then remove other formats till other dev do refactor + private $import_file_types = array( + "csv" => "CSV", + "csv_doors" => "CSV (Doors)", + "XML" => "XML", + "DocBook" => "DocBook" + ); + + private $export_file_types = array( + "XML" => "XML" + ); + + private $fieldSize; + + private $reqCfg; + + private $internal_links; + + private $relationsCfg; + + private $notifyOn; + + private $reqTCLinkCfg; + + protected $debugMsg; + + const AUTOMATIC_ID = 0; + + const ALL_VERSIONS = 0; + + const LATEST_VERSION = - 1; + + const NO_REVISION = - 1; + + /* + * function: requirement_mgr + * contructor + * + * args: db: reference to db object + * + * returns: instance of requirement_mgr + * + */ + public function __construct(&$db) + { + $this->db = &$db; + $this->cfield_mgr = new cfield_mgr($this->db); + $this->tree_mgr = new tree($this->db); + + $this->attachmentTableName = 'req_versions'; + + tlObjectWithAttachments::__construct($this->db, + $this->attachmentTableName); + + $this->node_types_descr_id = $this->tree_mgr->get_available_node_types(); + $this->node_types_id_descr = array_flip($this->node_types_descr_id); + $this->my_node_type = $this->node_types_descr_id['requirement']; + $this->object_table = $this->tables['requirements']; + + $this->fieldSize = config_get('field_size'); + $this->reqCfg = config_get('req_cfg'); + $this->reqTCLinkCfg = config_get('reqTCLinks'); + + $this->relationsCfg = new stdClass(); + $this->relationsCfg->interProjectLinking = $this->reqCfg->relations->interproject_linking; + + $this->internal_links = config_get('internal_links'); + + $this->notifyOn = null; + + $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: '; + } + + /* + * function: get_export_file_types + * getter + * + * args: - + * + * returns: map + * key: export file type code + * value: export file type verbose description + * + */ + public function get_export_file_types() + { + return $this->export_file_types; + } + + /* + * function: get_impor_file_types + * getter + * + * args: - + * + * returns: map + * key: import file type code + * value: import file type verbose description + * + */ + public function get_import_file_types() + { + return $this->import_file_types; + } + + /* + * function: get_by_id + * + * + * args: id: requirement id (can be an array) + * [version_id]: requirement version id (can be an array) + * [version_number]: + * [options] + * + * + * returns: null if query fails + * map with requirement info + * + * + */ + public function get_by_id($id, $version_id = self::ALL_VERSIONS, + $version_number = 1, $options = null, $filters = null) + { + static $userCache; // key: user id, value: display name + static $user_keys; + + $debugMsg = $this->debugMsg . __FUNCTION__; + $labels['undefined'] = lang_get('undefined'); + $user_keys = array( + 'author' => 'author_id', + 'modifier' => 'modifier_id' + ); + + $my['options'] = array( + 'order_by' => " ORDER BY REQV.version DESC ", + 'output_format' => 'array', + 'renderImageInline' => false, + 'decodeUsers' => true, + 'outputLevel' => 'std' + ); + + $my['options'] = array_merge($my['options'], (array) $options); + + // null => do not filter + $my['filters'] = array( + 'status' => null, + 'type' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $filter_clause = ''; + $dummy[] = ''; // trick to make implode() work + foreach ($my['filters'] as $field2filter => $value) { + if (! is_null($value)) { + $dummy[] = " {$field2filter} = '{$value}' "; + } + } + + if (count($dummy) > 1) { + $filter_clause = implode(" AND ", $dummy); + } + + $where_clause = " WHERE NH_REQV.parent_id "; + if ($id_is_array = is_array($id)) { + $where_clause .= "IN (" . implode(",", $id) . ") "; + } else { + $where_clause .= " = {$id} "; + } + + if (is_array($version_id)) { + $versionid_list = implode(",", $version_id); + $where_clause .= " AND REQV.id IN ({$versionid_list}) "; + } else { + if (is_null($version_id)) { + // search by "human" version number + $where_clause .= " AND REQV.version = {$version_number} "; + } else { + if ($version_id != self::ALL_VERSIONS && + $version_id != self::LATEST_VERSION) { + $where_clause .= " AND REQV.id = {$version_id} "; + } + } + } + + // added -1 AS revision_id to make some process easier + switch ($my['options']['outputLevel']) { + case 'minimal': + $outf = " /* $debugMsg */ SELECT REQ.id,REQ.req_doc_id,REQV.id AS version_id," . + " NH_REQ.name AS title "; + break; + + case 'std': + default: + $outf = " /* $debugMsg */ SELECT REQ.id,REQ.srs_id,REQ.req_doc_id," . + " REQV.scope,REQV.status,REQV.type,REQV.active," . + " REQV.is_open,REQV.is_open AS reqver_is_open,REQV.author_id,REQV.version,REQV.id AS version_id," . + " REQV.expected_coverage,REQV.creation_ts,REQV.modifier_id," . + " REQV.modification_ts,REQV.revision, -1 AS revision_id, " . + " NH_REQ.name AS title, REQ_SPEC.testproject_id, " . + " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order "; + break; + } + + // added -1 AS revision_id to make some process easier + $sql = $outf . " FROM {$this->object_table} REQ " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id " . + " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . + " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . + $where_clause . $filter_clause . $my['options']['order_by']; + + $decodeUserMode = 'simple'; + if ($version_id != self::LATEST_VERSION) { + switch ($my['options']['output_format']) { + case 'mapOfArray': + $recordset = $this->db->fetchRowsIntoMap($sql, 'id', + database::CUMULATIVE); + $decodeUserMode = 'complex'; + break; + + case 'array': + default: + $recordset = $this->db->get_recordset($sql); + break; + } + } else { + // But, how performance wise can be do this, + // instead of using MAX(version) and a group by? + // + // if $id was a list then this will return something USELESS + // + if (! $id_is_array) { + $recordset = array( + $this->db->fetchFirstRow($sql) + ); + } else { + // Write to event viewer ??? + // Developer Needs to user + die('use getByIDBulkLatestVersionRevision()'); + } + } + + $rs = null; + if (! is_null($recordset) && $my['options']['renderImageInline']) { + $k2l = array_keys($recordset); + foreach ($k2l as $akx) { + $this->renderImageAttachments($id, $recordset[$akx]); + } + reset($recordset); + } + + $rs = $recordset; + if (! is_null($recordset) && $my['options']['decodeUsers']) { + switch ($decodeUserMode) { + case 'complex': + // output[REQID][0] = array('id' =>, 'xx' => ...) + $flevel = array_keys($recordset); + foreach ($flevel as $flk) { + $key2loop = array_keys($recordset[$flk]); + foreach ($key2loop as $key) { + foreach ($user_keys as $ukey => $userid_field) { + $rs[$flk][$key][$ukey] = ''; + if (trim($rs[$flk][$key][$userid_field]) != "") { + if (! isset( + $userCache[$rs[$flk][$key][$userid_field]])) { + $user = tlUser::getByID($this->db, + $rs[$flk][$key][$userid_field]); + $rs[$flk][$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; + $userCache[$rs[$flk][$key][$userid_field]] = $rs[$flk][$key][$ukey]; + unset($user); + } else { + $rs[$flk][$key][$ukey] = $userCache[$rs[$flk][$key][$userid_field]]; + } + } + } + } + } + break; + + case 'simple': + default: + $key2loop = array_keys($recordset); + foreach ($key2loop as $key) { + foreach ($user_keys as $ukey => $userid_field) { + $rs[$key][$ukey] = ''; + if (trim($rs[$key][$userid_field]) != "") { + if (! isset( + $userCache[$rs[$key][$userid_field]])) { + $user = tlUser::getByID($this->db, + $rs[$key][$userid_field]); + $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; + $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; + unset($user); + } else { + $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; + } + } + } + } + break; + } + } + + unset($recordset); + unset($my); + unset($dummy); + + return $rs; + } + + /* + * function: create + * + * args: srs_id: req spec id, parent of requirement to be created + * reqdoc_id + * title + * scope + * user_id: author + * [status] + * [type] + * [expected_coverage] + * [node_order] + * + * returns: map with following keys: + * status_ok -> 1/0 + * msg -> some simple message, useful when status_ok ==0 + * id -> id of new requirement. + * + * @internal revision + */ + public function create($srs_id, $reqdoc_id, $title, $scope, $user_id, + $status = TL_REQ_STATUS_VALID, $type = TL_REQ_TYPE_INFO, + $expected_coverage = 1, $node_order = 0, $tproject_id = null, + $options = null) + { + // This kind of saving is important when called in a loop in situations like + // copy test project + static $debugMsg; + static $log_message; + + if (! $log_message) { + $debugMsg = $this->debugMsg . __FUNCTION__; + $log_message = lang_get('req_created_automatic_log'); + } + + $tproject_id = is_null($tproject_id) ? $this->tree_mgr->getTreeRoot( + $srs_id) : $tproject_id; + + $result = array( + 'id' => 0, + 'status_ok' => 0, + 'msg' => 'ko' + ); + $my['options'] = array( + 'quickAndDirty' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + if (! $my['options']['quickAndDirty']) { + $reqdoc_id = trimAndLimit($reqdoc_id, $this->fieldSize->req_docid); + $title = trimAndLimit($title, $this->fieldSize->req_title); + $op = $this->check_basic_data($srs_id, $tproject_id, $title, + $reqdoc_id); + } else { + $op['status_ok'] = true; + } + + $result['msg'] = $op['status_ok'] ? $result['msg'] : $op['msg']; + if ($op['status_ok']) { + $result = $this->create_req_only($srs_id, $reqdoc_id, $title, + $node_order); + if ($result["status_ok"]) { + if ($this->internal_links->enable) { + $scope = req_link_replace($this->db, $scope, $tproject_id); + } + + $op = $this->create_version($result['id'], 1, $scope, $user_id, + $status, $type, intval($expected_coverage)); + $result['msg'] = $op['status_ok'] ? $result['msg'] : $op['msg']; + $result['version_id'] = $op['status_ok'] ? $op['id'] : - 1; + + if ($op['status_ok']) { + $sql = "/* $debugMsg */ " . + "UPDATE {$this->tables['req_versions']} " . + " SET log_message='" . + $this->db->prepare_string($log_message) . "'" . + " WHERE id = " . intval($op['id']); + $this->db->exec_query($sql); + } + } + } + $ctx = array( + 'id' => $result['id'] + ); + event_signal('EVENT_TEST_REQUIREMENT_CREATE', $ctx); + + return $result; + } + + // function end + + /* + * function: update + * + * + * args: id: requirement id + * version_id + * reqdoc_id + * title + * scope + * user_id: author + * status + * type + * $expected_coverage + * [skip_controls] + * + * + * returns: map: keys : status_ok, msg + * + * @internal revision + * 20091202 - franciscom - + * + */ + public function update($id, $version_id, $reqdoc_id, $title, $scope, + $user_id, $status, $type, $expected_coverage, $node_order = null, + $tproject_id = null, $skip_controls = 0, $create_revision = false, + $log_msg = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $result['status_ok'] = 1; + $result['msg'] = 'ok'; + + $db_now = $this->db->db_now(); + + // get SRSid, needed to do controls + $rs = $this->get_by_id($id, $version_id); + $req = $rs[0]; + $srs_id = $req['srs_id']; + + // try to avoid function calls when data is available on caller + $tproject_id = is_null($tproject_id) ? $this->tree_mgr->getTreeRoot( + $srs_id) : $tproject_id; + + if ($this->internal_links->enable) { + $scope = req_link_replace($this->db, $scope, $tproject_id); + } + + $reqdoc_id = trimAndLimit($reqdoc_id, $this->fieldSize->req_docid); + $title = trimAndLimit($title, $this->fieldSize->req_title); + $chk = $this->check_basic_data($srs_id, $tproject_id, $title, $reqdoc_id, + $id); + + if ($chk['status_ok'] || $skip_controls) { + if ($create_revision) { + $this->create_new_revision($version_id, $user_id, $tproject_id, + $req, $log_msg); + } + + $sql = array(); + + $q = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . + " SET name='" . $this->db->prepare_string($title) . "'"; + if (! is_null($node_order)) { + $q .= ', node_order= ' . abs(intval($node_order)); + } + $sql[] = $q . " WHERE id={$id}"; + + $sql[] = "/* $debugMsg */ UPDATE {$this->tables['requirements']} " . + " SET req_doc_id='" . $this->db->prepare_string($reqdoc_id) . "'" . + " WHERE id={$id}"; + + $sql_temp = "/* $debugMsg */ UPDATE {$this->tables['req_versions']} " . + " SET scope='" . $this->db->prepare_string($scope) . "', " . + " status='" . $this->db->prepare_string($status) . "', " . + " expected_coverage={$expected_coverage}, " . " type='" . + $this->db->prepare_string($type) . "' "; + + // only if no new revision is created set modifier and modification ts + // otherwise those values are handled by function create_new_revision() + if (! $create_revision) { + $sql_temp .= ", modifier_id={$user_id}, modification_ts={$db_now} "; + } + + $sql[] = $sql_temp . " WHERE id=" . intval($version_id); + + foreach ($sql as $stm) { + $qres = $this->db->exec_query($stm); + if (! $qres) { + $result['status_ok'] = 0; + $result['msg'] = $this->db->error_msg; + $result['sql'] = $stm; + break; + } + } + } // if($chk['status_ok'] || $skip_controls) + else { + $result['status_ok'] = $chk['status_ok']; + $result['msg'] = $chk['msg']; + } + + $ctx = array( + 'id' => $id + ); + event_signal('EVENT_TEST_REQUIREMENT_UPDATE', $ctx); + return $result; + } + + // function end + + /* + * function: delete + * Requirement + * Requirement link to testcases + * Requirement relations + * Requirement custom fields values + * Attachments + * + * check if we are deleting the only existent version, in this case + * we need to delete the requirement. + * + * args: id: can be one id, or an array of id + * + * returns: + */ + public function delete($id, $version_id = self::ALL_VERSIONS, + $user_id = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $children = null; + + $where = array( + 'coverage' => '', + 'this' => '', + 'iam_parent' => '' + ); + $deleteAll = false; + $result = null; + $doIt = true; + $kaboom = false; + + if (is_array($id)) { + $id_list = implode(',', $id); + $where['coverage'] = " WHERE req_id IN ({$id_list})"; + $where['this'] = " WHERE id IN ({$id_list})"; + $where['iam_parent'] = " WHERE parent_id IN ({$id_list})"; + } else { + $safeID = intval($id); + $where['coverage'] = " WHERE req_id = " . $safeID; + $where['this'] = " WHERE id = " . $safeID; + $where['iam_parent'] = " WHERE parent_id = " . $safeID; + } + + $set2del = null; + + // if we are trying to delete ONE SPECIFIC VERSION + // of ONE REQ, and is the ONLY VERSION on DB + // then we are going to delete the req. + $checkNotify = true; + $action4notify = 'delete'; + if ($version_id != self::ALL_VERSIONS) { + // we use version id when working on ONE REQ, + // then I'm going to trust this. + // From GUI if only one version exists, + // the operation available is DELETE REQ, + // not delete version + $sql = "SELECT COUNT(0) AS VQTY, parent_id FROM " . + " {$this->tables['nodes_hierarchy']} " . $where['iam_parent'] . + ' GROUP BY parent_id'; + + $rs = $this->db->fetchRowsIntoMap($sql, 'parent_id'); + $rs = current($rs); + if (isset($rs['VQTY']) && $rs['VQTY'] > 1) { + $action4notify = 'delete_version'; + } + } + + if ($checkNotify && $this->notifyOn[__FUNCTION__]) { + // Need to save data before delete + $set2del = $this->getByIDBulkLatestVersionRevision($id); + if (! is_null($set2del)) { + foreach ($set2del as $rk => $r2d) { + $this->notifyMonitors($rk, $action4notify, $user_id); + if ($action4notify == 'delete') { + $this->monitorOff($rk); + } + } + } + } + + // When deleting only one version, we need to check + // if we need to delete requirement also. + $children[] = $version_id; + if ($version_id == self::ALL_VERSIONS) { + $deleteAll = true; + + // I'm trying to speedup the next deletes + $sql = "/* $debugMsg */ " . + "SELECT NH.id FROM {$this->tables['nodes_hierarchy']} NH " . + "WHERE NH.parent_id "; + + if (is_array($id)) { + $sql .= " IN (" . implode(',', $id) . ") "; + } else { + $sql .= " = {$id} "; + } + + $sql .= " AND node_type_id=" . + $this->node_types_descr_id['requirement_version']; + + $children_rs = $this->db->fetchRowsIntoMap($sql, 'id'); + $children = array_keys($children_rs); + + // delete dependencies with test specification + $sql = "DELETE FROM {$this->tables['req_coverage']} " . + $where['coverage']; + $result = $this->db->exec_query($sql); + + // also delete relations to other requirements + // Issue due to FK + if ($result) { + $this->delete_all_relations($id); + } + + if ($result) { + // Need to get all versions for all requirements + $doIt = true; + $reqIDSet = (array) $id; + $reqVerSet = $this->getAllReqVersionIDForReq($reqIDSet); + + foreach ($reqVerSet as $reqVerElem) { + foreach ($reqVerElem as $reqVID2Del) { + $result = $this->attachmentRepository->deleteAttachmentsFor( + $reqVID2Del, $this->attachmentTableName); + } + } + } + } + + // Delete version info + $target = null; + if ($doIt) { + // As usual working with MySQL makes easier to be lazy and forget that + // agregate functions need GROUP BY + // How many versions are there? + // we will delete req also for all with COUNT(0) == 1 + $sql = "SELECT COUNT(0) AS VQTY, parent_id " . + " FROM {$this->tables['nodes_hierarchy']} " . + $where['iam_parent'] . ' GROUP BY parent_id'; + + $rs = $this->db->fetchRowsIntoMap($sql, 'parent_id'); + foreach ($rs as $el) { + if (isset($el['VQTY']) && $el['VQTY'] == 1) { + $target[] = $el['parent_id']; + } + } + + if ($kaboom = ! is_null($target)) { + $where['this'] = " WHERE id IN (" . implode(',', $target) . ")"; + } + + // Attachments are related to VERSION + foreach ($children as $reqVID) { + $this->attachmentRepository->deleteAttachmentsFor($reqVID, + $this->attachmentTableName); + } + + // Going to work on REVISIONS + $implosion = implode(',', $children); + $sql = "/* $debugMsg */ " . + " SELECT id from {$this->tables['nodes_hierarchy']} " . + " WHERE parent_id IN ( {$implosion} ) " . " AND node_type_id=" . + $this->node_types_descr_id['requirement_revision']; + + $revisionSet = $this->db->fetchRowsIntoMap($sql, 'id'); + if (! is_null($revisionSet)) { + $this->cfield_mgr->remove_all_design_values_from_node( + array_keys($revisionSet)); + + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['req_revisions']} + WHERE parent_id IN ( {$implosion} ) "; + $this->db->exec_query($sql); + + $sql = "/* $debugMsg */ + DELETE FROM {$this->tables['nodes_hierarchy']} + WHERE parent_id IN ( {$implosion} ) + AND node_type_id=" . + $this->node_types_descr_id['requirement_revision']; + $this->db->exec_query($sql); + } + $this->cfield_mgr->remove_all_design_values_from_node( + (array) $children); + + $where['children'] = " WHERE id IN ( {$implosion} ) "; + + $sql = "DELETE FROM {$this->tables['req_versions']} " . + $where['children']; + $this->db->exec_query($sql); + + $sql = "DELETE FROM {$this->tables['nodes_hierarchy']} " . + $where['children'] . " AND node_type_id=" . + $this->node_types_descr_id['requirement_version']; + $result = $this->db->exec_query($sql); + } + + $kaboom = $kaboom || ($deleteAll && $result); + if ($kaboom) { + $sql = "DELETE FROM {$this->object_table} " . $where['this']; + $this->db->exec_query($sql); + + $sql = "DELETE FROM {$this->tables['nodes_hierarchy']} " . + $where['this'] . " AND node_type_id=" . + $this->node_types_descr_id['requirement']; + + $result = $this->db->exec_query($sql); + } + + $result = (! $result) ? lang_get('error_deleting_req') : 'ok'; + + $ctx = array( + 'id' => $id + ); + event_signal('EVENT_TEST_REQUIREMENT_DELETE', $ctx); + + return $result; + } + + /** + * collect coverage of Requirement + * + * @param string $req_id + * ID of req. + * @return array list of test cases [id, title] + * + * Notice regarding platforms: + * When doing Requirements Based Reports, we analize report situation + * on a Context composed by: + * Test project AND Test plan. + * + * We do this because we want to have a dynamic view (i.e. want to add exec info). + * + * When a Test plan has platforms defined, user get at GUI possibility to choose + * one platform. + * IMHO (franciscom) this has to change how coverage (dynamic) is computed. + * + * Static coverage: + * depicts relation bewteen Req and test cases spec, and platforms are not considered + * + * DYNAMIC coverage: + * depicts relation bewteen Req and test cases spec and exec status of these test case, + * and platforms have to be considered + * + */ + public function get_coverage($id, $context = null, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my = array( + 'options' => array( + 'accessKey' => 'idx' + ) + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $safe_id = intval($id); + $common = array(); + + $common['join'] = " FROM {$this->tables['nodes_hierarchy']} NH_TC " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id=NH_TC.id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id=NH_TCV.id " . + " JOIN {$this->tables['req_coverage']} RC ON RC.testcase_id = NH_TC.id "; + $common['where'] = " WHERE RC.req_id={$safe_id} "; + + if (is_null($context)) { + $sql = "/* $debugMsg - Static Coverage */ " . + " SELECT DISTINCT NH_TC.id,NH_TC.name,TCV.tc_external_id,U.login,RC.creation_ts" . + $common['join'] . + " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = RC.author_id " . + $common['where']; + } else { + + $sql = "/* $debugMsg - Dynamic Coverage */ " . + " SELECT DISTINCT NH_TC.id,NH_TC.name,TCV.tc_external_id" . + $common['join'] . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NH_TCV.id " . + $common['where'] . " AND TPTCV.testplan_id = " . + intval($context['tplan_id']) . " AND TPTCV.platform_id = " . + intval($context['platform_id']); + } + $sql .= " ORDER BY tc_external_id "; + + switch ($my['options']['accessKey']) { + case 'tcase_id': + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + break; + + case 'idx': + default: + $rs = $this->db->get_recordset($sql); + break; + } + return $rs; + } + + /* + * function: check_basic_data + * do checks on title and reqdoc id, for a requirement + * + * Checks: + * empty title + * empty docid + * docid already exists inside test project (DOCID is Test Project WIDE) + * title alreday exists under same REQ SPEC (req. parent) + * + * + * args: srs_id: req spec id (req parent) + * title + * reqdoc_id + * [id]: default null + * + * + * returns: map + * keys: status_ok + * msg + * failure_reason + * + * @internal revision + * 20110206 - franciscom - add new key on retval 'failure_reason' + * 20110108 - franciscom - check on duplicate title under same parent + */ + private function check_basic_data($srs_id, $tproject_id, $title, $reqdoc_id, + $id = null) + { + $ret['status_ok'] = 1; + $ret['msg'] = ''; + $ret['failure_reason'] = ''; + + $title = trim($title); + $reqdoc_id = trim($reqdoc_id); + + if ($title == "") { + $ret['status_ok'] = 0; + $ret['msg'] = lang_get("warning_empty_req_title"); + $ret['failure_reason'] = 'empty_req_title'; + } + + if ($reqdoc_id == "") { + $ret['status_ok'] = 0; + $ret['msg'] .= " " . lang_get("warning_empty_reqdoc_id"); + $ret['failure_reason'] = 'empty_reqdoc_id'; + } + + if ($ret['status_ok']) { + $ret['msg'] = 'ok'; + $rs = $this->getByDocID($reqdoc_id, $tproject_id); + if (! is_null($rs) && (is_null($id) || ! isset($rs[$id]))) { + $ret['msg'] = sprintf(lang_get("warning_duplicate_reqdoc_id"), + $reqdoc_id); + $ret['status_ok'] = 0; + $ret['failure_reason'] = 'duplicate_reqdoc_id'; + } + } + + // check for duplicate title + // BUGID 4150 + if ($ret['status_ok']) { + $ret['msg'] = 'ok'; + $target = array( + 'key' => 'title', + 'value' => $title + ); + $getOptions = array( + 'output' => 'id' + ); + $rs = $this->getByAttribute($target, $tproject_id, $srs_id, + $getOptions); + if (! is_null($rs) && (is_null($id) || ! isset($rs[$id]))) { + $ret['failure_reason'] = 'sibling_req_with_same_title'; + $ret['msg'] = sprintf( + lang_get("warning_sibling_req_with_same_title"), $title); + $ret['status_ok'] = 0; + } + } + + return $ret; + } + + /* + * function: create_tc_from_requirement + * create testcases using requirements as input + * + * + * args: + * + * returns: + */ + public function create_tc_from_requirement($mixIdReq, $srs_id, $user_id, + $tproject_id = null, $tc_count = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $tcaseMgr = new testcase($this->db); + $tsuite_mgr = new testsuite($this->db); + + $auto_testsuite_name = $this->reqCfg->default_testsuite_name; + $node_descr_type = $this->tree_mgr->get_available_node_types(); + $empty_steps = null; + $empty_preconditions = ''; // fix for BUGID 2995 + + $labels['tc_created'] = lang_get('tc_created'); + + $output = null; + $reqSet = is_array($mixIdReq) ? $mixIdReq : array( + $mixIdReq + ); + + if (is_null($tproject_id) || $tproject_id == 0) { + $tproject_id = $this->tree_mgr->getTreeRoot($srs_id); + } + + if ($this->reqCfg->use_req_spec_as_testsuite_name) { + $full_path = $this->tree_mgr->get_path($srs_id); + $addition = " (" . lang_get("testsuite_title_addition") . ")"; + $truncate_limit = $this->fieldSize->testsuite_name - + strlen($addition); + + // REQ_SPEC_A + // |-- REQ_SPEC_A1 + // |-- REQ_SPEC_A2 + // |- REQ100 + // |- REQ101 + // + // We will try to check if a test suite has already been created for + // top REQ_SPEC_A (we do search using automatic generated name as search criteria). + // If not => we need to create all path till leaves (REQ100 and REQ200) + // + // + // First search: we use test project + $parent_id = $tproject_id; + $deep_create = false; + foreach ($full_path as $node) { + // follow hierarchy of test suites to create + $tsuiteInfo = null; + + // deal with UTF-8 + $testsuite_name = mb_substr($node['name'], 0, $truncate_limit, + mb_detect_encoding($node['name'])) . $addition; + + if (! $deep_create) { + // child test suite with this name, already exists on current parent ? + // At first a failure we will not check anymore an proceed with deep create + $sql = "/* $debugMsg */ SELECT id,name FROM {$this->tables['nodes_hierarchy']} NH " . + " WHERE name='" . + $this->db->prepare_string($testsuite_name) . "' " . + " AND node_type_id=" . $node_descr_type['testsuite'] . + " AND parent_id = {$parent_id} "; + + // If returns more that one record use ALWAYS first + $tsuiteInfo = $this->db->fetchRowsIntoMap($sql, 'id'); + } + + if (is_null($tsuiteInfo)) { + $tsuiteInfo = $tsuite_mgr->create($parent_id, + $testsuite_name, $this->reqCfg->testsuite_details); + $output[] = sprintf(lang_get('testsuite_name_created'), + $testsuite_name); + $deep_create = true; + } else { + $tsuiteInfo = current($tsuiteInfo); + } + $tsuite_id = $tsuiteInfo['id']; // last value here will be used as parent for test cases + $parent_id = $tsuite_id; + } + $output[] = sprintf(lang_get('created_on_testsuite'), + $testsuite_name); + } else { + // don't use req_spec as testsuite name + // Warning: + // We are not maintaining hierarchy !!! + $sql = " SELECT id FROM {$this->tables['nodes_hierarchy']} NH " . + " WHERE name='" . $this->db->prepare_string( + $auto_testsuite_name) . "' " . " AND parent_id=" . + $tproject_id . " " . " AND node_type_id=" . + $node_descr_type['testsuite']; + + $result = $this->db->exec_query($sql); + if ($this->db->num_rows($result) == 1) { + $row = $this->db->fetch_array($result); + $tsuite_id = $row['id']; + $label = lang_get('created_on_testsuite'); + } else { + // not found -> create + tLog('test suite:' . $auto_testsuite_name . ' was not found.'); + $new_tsuite = $tsuite_mgr->create($tproject_id, + $auto_testsuite_name, $this->reqCfg->testsuite_details); + $tsuite_id = $new_tsuite['id']; + $label = lang_get('testsuite_name_created'); + } + $output[] = sprintf($label, $auto_testsuite_name); + } + + // create TC + $createOptions = array(); + $createOptions['check_names_for_duplicates'] = config_get( + 'check_names_for_duplicates'); + $createOptions['action_on_duplicate_name'] = config_get( + 'action_on_duplicate_name'); + + $testcase_importance_default = config_get('testcase_importance_default'); + + // compute test case order + $testcase_order = config_get('treemenu_default_testcase_order'); + $nt2exclude = array( + 'testplan' => 'exclude_me', + 'requirement_spec' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + + $siblings = $this->tree_mgr->get_children($tsuite_id, $nt2exclude); + if (! is_null($siblings)) { + $dummy = end($siblings); + $testcase_order = $dummy['node_order']; + } + + foreach ($reqSet as $reqID) { + $reqData = $this->get_by_id($reqID, requirement_mgr::LATEST_VERSION); + $count = (! is_null($tc_count)) ? $tc_count[$reqID] : 1; + $reqData = $reqData[0]; + + // Generate name with progessive + $instance = 1; + $getOptions = array( + 'check_criteria' => 'like', + 'access_key' => 'name' + ); + $itemSet = $tcaseMgr->getDuplicatesByName($reqData['title'], + $tsuite_id, $getOptions); + + $nameSet = null; + if (! is_null($itemSet)) { + $nameSet = array_flip(array_keys($itemSet)); + } + + for ($idx = 0; $idx < $count; $idx ++) { + $testcase_order ++; + + // We have a little problem to work on: + // suppose you have created: + // TC [1] + // TC [2] + // TC [3] + // If we delete TC [2] + // When I got siblings il will got 2, if I create new progressive using next, + // it will be 3 => I will get duplicated name. + // + // Seems better option can be: + // Get all siblings names, put on array, create name an check if exists, if true + // generate a new name. + // This may be at performance level is better than create name then check on db, + // because this approach will need more queries to DB + // + $tcase_name = $reqData['title'] . " [{$instance}]"; + if (! is_null($nameSet)) { + while (isset($nameSet[$tcase_name])) { + $instance ++; + $tcase_name = $reqData['title'] . " [{$instance}]"; + } + } + $nameSet[$tcase_name] = $tcase_name; + + $prefix = ($this->reqCfg->use_testcase_summary_prefix_with_title_and_version) ? sprintf( + $this->reqCfg->testcase_summary_prefix_with_title_and_version, + $reqID, $reqData['version_id'], $reqData['title'], + $reqData['version']) : $this->reqCfg->testcase_summary_prefix; + $content = ($this->reqCfg->copy_req_scope_to_tc_summary) ? $prefix . + $reqData['scope'] : $prefix; + + $tcase = $tcaseMgr->create($tsuite_id, $tcase_name, $content, + $empty_preconditions, $empty_steps, $user_id, null, + $testcase_order, testcase::AUTOMATIC_ID, + TESTCASE_EXECUTION_TYPE_MANUAL, $testcase_importance_default, + $createOptions); + + $tcase_name = $tcase['new_name'] == '' ? $tcase_name : $tcase['new_name']; + $output[] = sprintf($labels['tc_created'], $tcase_name); + + // create coverage dependency + $rrv = array( + 'id' => $reqData['id'], + 'version_id' => $reqData['version_id'] + ); + $ttcv = array( + 'id' => $tcase['id'], + 'version_id' => $tcase['tcversion_id'] + ); + + if (! $this->assignReqVerToTCVer($rrv, $ttcv, $user_id)) { + $output[] = 'Test case: ' . $tcase_name . " was not created"; + } + } + } + return $output; + } + + /* + * function: assign_to_tcase + * assign requirement(s) to test case + * Will use always latest ACTIVE Versions + * + * args: req_id: can be an array of requirement id + * testcase_id + * + * returns: 1/0 + */ + public function assign_to_tcase($req_id, $testcase_id, $author_id) + { + static $tcMgr; + + if (! $tcMgr) { + $tcMgr = new testcase($this->db); + } + + $debugMsg = $this->debugMsg . __FUNCTION__; + + $output = 0; + $now = $this->db->db_now(); + if ($testcase_id && $req_id) { + // Get Latest Active Test Case Version + $tcv = current($tcMgr->get_last_active_version($testcase_id)); + if ($tcv == null) { + return $output; + } + } + + // Go ahead + if ($testcase_id && $req_id) { + // Need to get latest version for each requirement + $reqIDSet = (array) $req_id; + $gopt = array( + 'output' => 'id,version' + ); + + $reqLatestVersionIDSet = array(); + $reqLatestVersionNumberSet = array(); + foreach ($reqIDSet as $req) { + $isofix = $this->getLastVersionInfo($req, $gopt); + $reqLatestVersionIDSet[] = $isofix['id']; + $reqLatestVersionNumberSet[] = $isofix['version']; + } + + $ltcv = $tcv['tcversion_id']; + $ltcvNum = $tcv['version']; + $in_clause = implode(",", $reqLatestVersionIDSet); + + // + $sql = " /* $debugMsg */ " . + " SELECT req_id,testcase_id,req_version_id,tcversion_id " . + " FROM {$this->tables['req_coverage']} " . + " WHERE req_version_id IN ({$in_clause}) " . + " AND tcversion_id = {$ltcv}"; + + $coverage = $this->db->fetchRowsIntoMap($sql, 'req_version_id'); + + // Useful for audit + $tcInfo = $this->tree_mgr->get_node_hierarchy_info($testcase_id); + + $loop2do = count($reqLatestVersionIDSet); + for ($idx = 0; $idx < $loop2do; $idx ++) { + if (is_null($coverage) || + ! isset($coverage[$reqLatestVersionIDSet[$idx]])) { + $sql = " INSERT INTO {$this->tables['req_coverage']} " . + " (req_id,testcase_id,req_version_id, tcversion_id," . + " author_id,creation_ts) " . + " VALUES ({$reqIDSet[$idx]},{$testcase_id}," . + " $reqLatestVersionIDSet[$idx],{$ltcv}," . + " {$author_id},{$now})"; + $this->db->exec_query($sql); + if ($this->db->affected_rows() == 1) { + $output = 1; + + // For audit + $reqInfo = $this->tree_mgr->get_node_hierarchy_info( + $reqIDSet[$idx]); + if ($tcInfo && $reqInfo) { + logAuditEvent( + TLS("audit_reqv_assigned_tcv", $reqInfo['name'], + $reqLatestVersionNumberSet[$idx], + $tcInfo['name'], $ltcvNum), "ASSIGN", + $this->object_table); + } + } + } else { + $output = 1; + } + } + } + return $output; + } + + /** + */ + public function assignToTCaseUsingLatestVersions($req_id, $testcase_id, + $author_id) + { + return $this->assign_to_tcase($req_id, $testcase_id, $author_id); + } + + /** + * + * @todo delete the unused function if necessary + */ + private function get_relationships($req_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = " /* $debugMsg */ SELECT nodes_hierarchy.id,nodes_hierarchy.name " . + " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy, " . + " {$this->tables['req_coverage']} req_coverage " . + " WHERE req_coverage.testcase_id = nodes_hierarchy.id " . + " AND req_coverage.req_id={$req_id}"; + + return $this->db->get_recordset($sql); + } + + /* + * function: get_all_for_tcase + * get all requirements assigned to a test case + * A filter can be applied to do search on all req spec, + * or only on one. + * + * args: testcase_id + * [srs_id]: default 'all' + * + * returns: + */ + public function get_all_for_tcase($testcase_id, $srs_id = 'all') + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = " /* $debugMsg */ SELECT REQ.id,REQ.req_doc_id,NHA.name AS title, " . + " NHB.name AS req_spec_title,REQ_COVERAGE.testcase_id " . + " FROM {$this->object_table} REQ, " . + " {$this->tables['req_coverage']} REQ_COVERAGE," . + " {$this->tables['nodes_hierarchy']} NHA," . + " {$this->tables['nodes_hierarchy']} NHB," . + " {$this->tables['req_specs']} RSPEC "; + + $idList = implode(",", (array) $testcase_id); + $sql .= " WHERE REQ_COVERAGE.testcase_id IN (" . $idList . ")"; + $sql .= " AND REQ.srs_id=RSPEC.id AND REQ_COVERAGE.req_id=REQ.id " . + " AND NHA.id=REQ.id AND NHB.id=RSPEC.id "; + + // if only for one specification is required + if ($srs_id != 'all') { + $sql .= " AND REQ.srs_id=" . $srs_id; + } + if (is_array($testcase_id)) { + return $this->db->fetchRowsIntoMap($sql, 'testcase_id', true); + } else { + return $this->db->get_recordset($sql); + } + } + + /** + * + * @todo delete the unused function if necessary + */ + private function check_title($title) + { + $ret = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + + if ($title == "") { + $ret['status_ok'] = 0; + $ret['msg'] = lang_get("warning_empty_req_title"); + } + + return $ret; + } + + /* + * function: + * + * args : + * $nodes: array with req_id in order + * returns: + */ + public function set_order($map_id_order) + { + $this->tree_mgr->change_order_bulk($map_id_order); + } + + /** + * exportReqToXML + * + * @param int $id + * requirement id + * @param int $tproject_id: + * optional default null. + * useful to get custom fields + * (when this feature will be developed). + * + * @return string with XML code + * + */ + public function exportReqToXML($id, $tproject_id = null, + $inc_attachments = false) + { + $req = $this->get_by_id($id, requirement_mgr::LATEST_VERSION); + $reqData[] = $req[0]; + $req_version_id = $req[0]['version_id']; + + $elemTpl = "\t" . "" . "\n\t\t" . + "" . "\n\t\t" . + "<![CDATA[||TITLE||]]>" . "\n\t\t" . + "||VERSION||" . "\n\t\t" . + "||REVISION||" . "\n\t\t" . + "||NODE_ORDER||" . "\n\t\t" . + "" . "\n\t\t" . + "" . "\n\t\t" . + "" . "\n\t\t" . + "" . + "\n\t\t" . + $this->customFieldValuesAsXML($id, $req[0]['version_id'], + $tproject_id); + + // add req attachment content if checked in GUI + if ($inc_attachments) { + $attachments = null; + + // id -> req_id, but I need latest req_versionid + $attachSet = $this->attachmentRepository->getAttachmentInfosFor( + $req_version_id, $this->attachmentTableName, 'id'); + // get all attachments content and encode it in base64 + if ($attachSet) { + foreach ($attachSet as $attachmentInfo) { + $aID = $attachmentInfo["id"]; + $content = $this->attachmentRepository->getAttachmentContent( + $aID, $attachmentInfo); + + if ($content != null) { + $attachments[$aID]["id"] = $aID; + $attachments[$aID]["name"] = $attachmentInfo["file_name"]; + $attachments[$aID]["file_type"] = $attachmentInfo["file_type"]; + $attachments[$aID]["title"] = $attachmentInfo["title"]; + $attachments[$aID]["date_added"] = $attachmentInfo["date_added"]; + $attachments[$aID]["content"] = base64_encode($content); + } + } + + if (! empty($attachments)) { + $attchRootElem = "\n{{XMLCODE}}\t\t\n"; + $attchElemTemplate = "\t\t\t\n" . + "\t\t\t\t\n" . + "\t\t\t\t\n" . + "\t\t\t\t\n" . + "\t\t\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . + "\t\t\t\t\n" . + "\t\t\t\t\n" . + "\t\t\t\n"; + + $attchDecode = array( + "||ATTACHMENT_ID||" => "id", + "||ATTACHMENT_NAME||" => "name", + "||ATTACHMENT_FILE_TYPE||" => "file_type", + "||ATTACHMENT_TITLE||" => "title", + "||ATTACHMENT_DATE_ADDED||" => "date_added", + "||ATTACHMENT_CONTENT||" => "content" + ); + $elemTpl .= exportDataToXML($attachments, $attchRootElem, + $attchElemTemplate, $attchDecode, true); + } + } + } + $elemTpl .= "\n\t" . "" . "\n"; + + $info = array( + "||DOCID||" => "req_doc_id", + "||TITLE||" => "title", + "||DESCRIPTION||" => "scope", + "||STATUS||" => "status", + "||TYPE||" => "type", + "||NODE_ORDER||" => "node_order", + "||EXPECTED_COVERAGE||" => "expected_coverage", + "||VERSION||" => "version", + "||REVISION||" => "revision" + ); + + return exportDataToXML($reqData, "{{XMLCODE}}", $elemTpl, $info, true); + } + + /** + * xmlToMapRequirement + */ + public function xmlToMapRequirement($xml_item) + { + // Attention: following PHP Manual SimpleXML documentation, Please remember to cast + // before using data from $xml, + if (is_null($xml_item)) { + return null; + } + + $dummy = array(); + foreach ($xml_item->attributes() as $key => $value) { + $dummy[$key] = (string) $value; // See PHP Manual SimpleXML documentation. + } + + $dummy['node_order'] = (int) $xml_item->node_order; + $dummy['title'] = (string) $xml_item->title; + $dummy['docid'] = (string) $xml_item->docid; + $dummy['description'] = (string) $xml_item->description; + $dummy['status'] = (string) $xml_item->status; + $dummy['type'] = (string) $xml_item->type; + $dummy['expected_coverage'] = (int) $xml_item->expected_coverage; + + if (property_exists($xml_item, 'custom_fields')) { + $dummy['custom_fields'] = array(); + foreach ($xml_item->custom_fields->children() as $key) { + $dummy['custom_fields'][(string) $key->name] = (string) $key->value; + } + } + if (property_exists($xml_item, 'attachments')) { + $dummy['attachments'] = array(); + foreach ($xml_item->attachments->children() as $attachment) { + $attach_id = (int) $attachment->id; + $dummy['attachments'][$attach_id]['id'] = (int) $attachment->id; + $dummy['attachments'][$attach_id]['name'] = (string) $attachment->name; + $dummy['attachments'][$attach_id]['file_type'] = (string) $attachment->file_type; + $dummy['attachments'][$attach_id]['title'] = (string) $attachment->title; + $dummy['attachments'][$attach_id]['date_added'] = (string) $attachment->date_added; + $dummy['attachments'][$attach_id]['content'] = (string) $attachment->content; + } + } + return $dummy; + } + + /** + * createFromXML + * + * @internal revisions + */ + public function createFromXML($xml, $tproject_id, $parent_id, $author_id, + $filters = null, $options = null) + { + $reqAsMap = $this->xmlToMapRequirement($xml); + + // Map structure + // node_order => 0 + // title => Breaks + // docid => MAZDA3-0001 + // description => Heavy Rain Conditions + // status => [empty string] + // type => [empty string] + // expected_coverage => 0 + + return $this->createFromMap($reqAsMap, $tproject_id, $parent_id, + $author_id, $filters, $options); + } + + /** + * createFromMap + * + * Map structure + * node_order => 0 + * title => Breaks + * docid => MAZDA3-0001 + * description => Heavy Rain Conditions + * status => [empty string] + * type => [empty string] + * expected_coverage => 0 + * + * @internal revisions + */ + public function createFromMap($req, $tproject_id, $parent_id, $author_id, + $filters = null, $options = null) + { + static $missingCfMsg; + static $linkedCF; + static $messages; + static $labels; + static $fieldSize; + static $doProcessCF = false; + static $getByAttributeOpt; + static $getLastChildInfoOpt; + + if (is_null($linkedCF)) { + $fieldSize = config_get('field_size'); + + $linkedCF = $this->cfield_mgr->get_linked_cfields_at_design( + $tproject_id, cfield_mgr::CF_ENABLED, null, 'requirement', null, + 'name'); + $doProcessCF = true; + + $messages = array(); + $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); + $messages['cfield'] = lang_get( + 'cf_value_not_imported_missing_cf_on_testproject'); + + $labels = array( + 'import_req_created' => '', + 'import_req_skipped' => '', + 'import_req_updated' => '', + 'frozen_req_unable_to_import' => '', + 'requirement' => '', + 'import_req_new_version_created' => '', + 'import_req_update_last_version_failed' => '', + 'import_req_new_version_failed' => '', + 'import_req_skipped_plain' => '', + 'req_title_lenght_exceeded' => '', + 'req_docid_lenght_exceeded' => '' + ); + foreach ($labels as $key => $dummy) { + $labels[$key] = lang_get($key); + } + $getByAttributeOpt = array( + 'output' => 'id' + ); + $getLastChildInfoOpt = array( + 'child_type' => 'version', + 'output' => ' CHILD.is_open, CHILD.id ' + ); + } + + $cf2insert = null; + $status_ok = true; + $user_feedback = null; + $dummy = ''; + $result = null; + $newReq = null; + + $my['options'] = array( + 'hitCriteria' => 'docid', + 'actionOnHit' => "update", + 'skipFrozenReq' => true + ); + $my['options'] = array_merge($my['options'], (array) $options); + + // Check data than can create issue when writting to DB due to lenght + $req['title'] = trim($req['title']); + $req['docid'] = trim($req['docid']); + + $checkLengthOK = true; + $what2add = ''; + if (mb_strlen($req['title'], $tlCfg->charset) > $fieldSize->req_title) { + $checkLengthOK = false; + $what2add = $labels['req_title_lenght_exceeded'] . '/'; + } + + if (mb_strlen($req['docid'], $tlCfg->charset) > $fieldSize->req_docid) { + $checkLengthOK = false; + $what2add .= $labels['req_docid_lenght_exceeded']; + } + + if (! $checkLengthOK) { + $msgID = 'import_req_skipped_plain'; + $user_feedback[] = array( + 'doc_id' => $req['docid'], + 'title' => $req['title'], + 'import_status' => sprintf($labels[$msgID], $what2add) + ); + + return $user_feedback; + } + + // Check: + // If item with SAME DOCID exists inside container + // If there is a hit + // We will follow user option: update,create new version + // + // If do not exist check must be repeated, but on WHOLE test project + // If there is a hit -> we can not create + // else => create + $target = array( + 'key' => $my['options']['hitCriteria'], + 'value' => $req[$my['options']['hitCriteria']] + ); + + // IMPORTANT NOTICE + // When get is done by attribute that can not be unique (like seems to happen now 20110108 with + // title), we can get more than one hit and then on this context we can not continue + // with requested operation + $check_in_reqspec = $this->getByAttribute($target, $tproject_id, + $parent_id, $getByAttributeOpt); + + // while working on BUGID 4210, new details came to light. + // In addition to hit criteria there are also the criteria that we use + // when creating/update item using GUI, and these criteria have to be + // checked abd fullfilled. + // + if (is_null($check_in_reqspec)) { + $check_in_tproject = $this->getByAttribute($target, $tproject_id, + null, $getByAttributeOpt); + + if (is_null($check_in_tproject)) { + $importMode = 'creation'; + $newReq = $this->create($parent_id, $req['docid'], $req['title'], + $req['description'], $author_id, $req['status'], + $req['type'], $req['expected_coverage'], $req['node_order'], + $tproject_id, array( + 'quickAndDirty' => true + )); + // $reqID = $newReq['id']; + $fk_id = $newReq['version_id']; // for attachments + if ($status_ok = ($newReq['status_ok'] == 1)) { + $msgID = 'import_req_created'; + } else { + $msgID = 'import_req_skipped_plain'; + $result['msg'] = $newReq['msg']; // done to use what2add logic far below + } + } else { + // Can not have req with same req doc id + // on another branch => BLOCK + // What to do if is Frozen ??? -> now ignore and update anyway + $msgID = 'import_req_skipped'; + $status_ok = false; + } + } else { + // IMPORTANT NOTICE + // When you + // Need to get Last Version no matter active or not. + $reqID = key($check_in_reqspec); + $last_version = $this->get_last_child_info($reqID, + $getLastChildInfoOpt); + $msgID = 'frozen_req_unable_to_import'; + $status_ok = false; + + if ($last_version['is_open'] == 1 || + ! $my['options']['skipFrozenReq']) { + switch ($my['options']['actionOnHit']) { + case 'update_last_version': + $importMode = 'update'; + $result = $this->update($reqID, $last_version['id'], + $req['docid'], $req['title'], $req['description'], + $author_id, $req['status'], $req['type'], + $req['expected_coverage'], $req['node_order']); + $fk_id = $last_version['id']; // for attachment management + $status_ok = ($result['status_ok'] == 1); + if ($status_ok) { + $msgID = 'import_req_updated'; + } else { + $msgID = 'import_req_update_last_version_failed'; + } + break; + + case 'create_new_version': + $newItem = $this->create_new_version($reqID, $author_id, + array( + 'notify' => true + )); + + // Set always new version to NOT Frozen + $this->updateOpen($newItem['id'], 1); + + // hmm wrong implementation + // Need to update ALL fields on new version then why do not use + // update() ? + $newReq['version_id'] = $newItem['id']; + $fk_id = $newReq['version_id']; // for attachment management + + // IMPORTANT NOTICE: + // We have to DO NOT UPDATE REQDOCID with info received from USER + // Because ALL VERSION HAS TO HAVE docid, or we need to improve our checks + // and if update fails => we need to delete new created version. + $title = trimAndLimit($req['title'], + $fieldSize->req_title); + $importMode = 'update'; + $result = $this->update($reqID, $newItem['id'], + $req['docid'], $title, $req['description'], + $author_id, $req['status'], $req['type'], + $req['expected_coverage'], $req['node_order']); + + $status_ok = ($result['status_ok'] == 1); + if ($status_ok) { + $msgID = 'import_req_new_version_created'; + } else { + // failed -> removed just created version + $this->delete($reqID, $newItem['id']); + $msgID = 'import_req_new_version_failed'; + } + break; + } + } + } + $what2add = is_null($result) ? $req['docid'] : $req['docid'] . ':' . + $result['msg']; + + $user_feedback[] = array( + 'doc_id' => $req['docid'], + 'title' => $req['title'], + 'import_status' => sprintf($labels[$msgID], $what2add) + ); + + $hasAttachments = array_key_exists('attachments', $req); + // process attachements for creation and update + if ($status_ok && $hasAttachments) { + $addAttachResp = $this->processAttachments($importMode, $fk_id, + $req['attachments']); + } + + // display only problems during attachments import + if (isset($addAttachResp) && ! is_null($addAttachResp)) { + foreach ($addAttachResp as $att_name) { + $user_feedback[] = array( + 'doc_id' => $req['docid'], + 'title' => $req['title'], + 'import_status' => sprintf( + lang_get('import_req_attachment_skipped'), $att_name) + ); + } + } + + if ($status_ok && $doProcessCF && isset($req['custom_fields']) && + ! is_null($req['custom_fields'])) { + $req_version_id = ! is_null($newReq) ? $newReq['version_id'] : $last_version['id']; + $cf2insert = null; + + foreach ($req['custom_fields'] as $cfname => $cfvalue) { + $cfname = trim($cfname); + if (isset($linkedCF[$cfname])) { + $cf2insert[$linkedCF[$cfname]['id']] = array( + 'type_id' => $linkedCF[$cfname]['type'], + 'cf_value' => $cfvalue + ); + } else { + if (! isset($missingCfMsg[$cfname])) { + $missingCfMsg[$cfname] = sprintf($messages['cfield'], + $cfname, $labels['requirement']); + } + $user_feedback[] = array( + 'doc_id' => $req['docid'], + 'title' => $req['title'], + 'import_status' => $missingCfMsg[$cfname] + ); + } + } + if (! is_null($cf2insert)) { + $this->cfield_mgr->design_values_to_db($cf2insert, + $req_version_id, null, 'simple'); + } + } + + return $user_feedback; + } + + /** + * processAttachments + * + * Analyze attachments info related to requirement to define if the the attachment has to be added. + * attachments are ignored only if a attachment with the same ID is already linked to the target requirement. + * + * return an array of all attachments names of IDs already linked to target requirement (to display warning messages). + */ + private function processAttachments($importMode, $srs_id, $attachments) + { + $tables = tlObjectWithDB::getDBTables( + array( + 'req_versions', + 'attachments' + )); + + $knownAttachments = array(); + foreach ($attachments as $attachment) { + $addAttachment = true; + if ($importMode == 'update') { + // try to bypass the importation of already known attachments. + // Check in database if the attachment with the same ID is linked to the rspec with the same internal ID + // The couple attachment ID + InternalID is used as a kind of signature to avoid duplicates. + // If signature is not precise enough, could add the use of attachment timestamp (date_added in XML file). + $sql = " SELECT ATT.id from {$tables['attachments']} ATT " . + " WHERE ATT.id='{$this->db->prepare_string($attachment[id])}' " . + " AND ATT.fk_id={$srs_id} "; + $rsx = $this->db->get_recordset($sql); + $addAttachment = (is_null($rsx) || count($rsx) < 1); + if ($addAttachment === false) { // inform user that the attachment has been skipped + $knownAttachments[] = $attachment['name']; + } + } + if ($addAttachment) { + $attachRepo = tlAttachmentRepository::create($this->db); + $fileInfo = $attachRepo->createAttachmentTempFile( + $attachment['content']); + $fileInfo['name'] = $attachment['name']; + $fileInfo['type'] = $attachment['file_type']; + $attachRepo->insertAttachment($srs_id, $tables['req_versions'], + $attachment['title'], $fileInfo); + } + } + return $knownAttachments; + } + + // ---------------------------------------------------------------------------- + // Custom field related functions + // ---------------------------------------------------------------------------- + + /* + * function: get_linked_cfields + * Get all linked custom fields. + * Remember that custom fields are defined at system wide level, and + * has to be linked to a testproject, in order to be used. + * + * + * args: id: requirement id + * $child_id: requirement version id or requirement revision id + * [parent_id]: this information is vital, + * to get the linked custom fields. + * null -> use requirement_id as starting point. + * !is_null -> use this value as testproject id + * + * returns: map/hash + * key: custom field id + * value: map with custom field definition and value assigned for choosen requirement, + * with following keys: + * + * id: custom field id + * name + * label + * type: custom field type + * possible_values: for custom field + * default_value + * valid_regexp + * length_min + * length_max + * show_on_design + * enable_on_design + * show_on_execution + * enable_on_execution + * display_order + * value: value assigned to custom field for this requirement + * null if for this requirement custom field was never edited. + * + * node_id: requirement id + * null if for this requirement, custom field was never edited. + */ + public function get_linked_cfields($id, $child_id, $parent_id = null, + $opt = null) + { + $options = array( + 'access_key' => null + ); + $options = array_merge($options, (array) $opt); + + if (! is_null($parent_id)) { + $tproject_id = $parent_id; + } else { + $req_info = $this->get_by_id($id); + $tproject_id = $req_info[0]['testproject_id']; + unset($req_info); + } + + return $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, + cfield_mgr::ENABLED, null, 'requirement', $child_id, + $options['access_key']); + } + + /* + * function: html_table_of_custom_field_inputs + * Return html code, implementing a table with custom fields labels + * and html inputs, for choosen requirement. + * Used to manage user actions on custom fields values. + * + * + * args: $id + * $version_id --- BUGID - NEEDS CHANGES + * [parent_id]: need to undertad to which testproject the requirement belongs. + * this information is vital, to get the linked custom fields. + * null -> use requirement_id as starting point. + * !is_null -> use this value as starting point. + * + * + * [$name_suffix]: must start with '_' (underscore). + * Used when we display in a page several items + * (example during test case execution, several test cases) + * that have the same custom fields. + * In this kind of situation we can use the item id as name suffix. + * + * returns: html string + */ + public function html_table_of_custom_field_inputs($id, $version_id, + $parent_id = null, $name_suffix = '', $input_values = null) + { + $cf_map = $this->get_linked_cfields($id, $version_id, $parent_id, + $name_suffix); + return $this->cfield_mgr->html_table_inputs($cf_map, $name_suffix, + $input_values); + } + + /* + * function: html_table_of_custom_field_values + * Return html code, implementing a table with custom fields labels + * and custom fields values, for choosen requirement. + * You can think of this function as some sort of read only version + * of html_table_of_custom_field_inputs. + * + * + * args: $id + * $child_id: req version or req revision ID + * + * returns: html string + */ + public function html_table_of_custom_field_values($id, $child_id, + $tproject_id = null) + { + $NO_WARNING_IF_MISSING = true; + $cf_smarty = ''; + + $root_id = is_null($id) ? $tproject_id : null; + $cf_map = $this->get_linked_cfields($id, $child_id, $root_id); + + $show_cf = config_get('custom_fields')->show_custom_fields_without_value; + + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + // if user has assigned a value, then node_id is not null + if ($cf_info['node_id'] || $show_cf) { + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, $NO_WARNING_IF_MISSING)); + + $cf_smarty .= '' . + htmlspecialchars($label) . ":" . + $this->cfield_mgr->string_custom_field_value($cf_info, + $child_id) . "\n"; + } + } + + if (trim($cf_smarty) != "") { + $cf_smarty = "" . $cf_smarty . "
    "; + } + } + return $cf_smarty; + } + + /* + * function: values_to_db + * write values of custom fields. + * + * args: $hash: + * key: custom_field__. + * Example custom_field_0_67 -> 0=> string field + * + * $node_id: + * + * [$cf_map]: hash -> all the custom fields linked and enabled + * that are applicable to the node type of $node_id. + * + * For the keys not present in $hash, we will write + * an appropriate value according to custom field + * type. + * + * This is needed because when trying to udpate + * with hash being $_REQUEST, $_POST or $_GET + * some kind of custom fields (checkbox, list, multiple list) + * when has been deselected by user. + * + * + * rev: + */ + public function values_to_db($hash, $node_id, $cf_map = null, + $hash_type = null) + { + $this->cfield_mgr->design_values_to_db($hash, $node_id, $cf_map, + $hash_type); + } + + /** + * customFieldValuesAsXML + * + * @param int $id: + * requirement spec id + * @param int $version_id + * @param int $tproject_id: + * test project id + * @return array + * + */ + private function customFieldValuesAsXML($id, $version_id, $tproject_id) + { + $xml = null; + $cfMap = $this->get_linked_cfields($id, $version_id, $tproject_id); + if (! empty($cfMap)) { + $xml = $this->cfield_mgr->exportValueAsXML($cfMap); + } + return $xml; + } + + /* + * function: getByDocID + * get req information using document ID as access key. + * + * args : doc_id: + * [tproject_id] + * [parent_id] -> req spec parent of requirement searched + * default 0 -> case sensivite search + * + * returns: map. + * key: req spec id + * value: req info, map with following keys: + * id + * doc_id + * testproject_id + * title + * scope + */ + public function getByDocID($doc_id, $tproject_id = null, $parent_id = null, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'check_criteria' => '=', + 'access_key' => 'id', + 'case' => 'sensitive', + 'output' => 'standard' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $the_doc_id = $this->db->prepare_string(trim($doc_id)); + switch ($my['options']['check_criteria']) { + case 'like': + $check_criteria = " LIKE '{$the_doc_id}%' "; + break; + + case '=': + default: + $check_criteria = " = '{$the_doc_id}' "; + break; + } + + $sql = " /* $debugMsg */ SELECT "; + switch ($my['options']['output']) { + case 'standard': + $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id, " . + " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order "; + break; + + case 'minimun': + $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id"; + break; + } + + $sql .= " FROM {$this->object_table} REQ " . + " /* Get Req info from NH */ " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . + " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . + " WHERE REQ.req_doc_id {$check_criteria} "; + + if (! is_null($tproject_id)) { + $sql .= " AND REQ_SPEC.testproject_id={$tproject_id}"; + } + + if (! is_null($parent_id)) { + $sql .= " AND REQ.srs_id={$parent_id}"; + } + + return $this->db->fetchRowsIntoMap($sql, $my['options']['access_key']); + } + + /** + * Copy a requirement to a new requirement specification + * requirement DOC ID will be changed because must be unique inside + * MASTER CONTAINER (test project) + * + * @param integer $id: + * requirement ID + * @param integer $parent_id: + * target req spec id (where we want to copy) + * @param integer $user_id: + * who is requesting copy + * @param integer $tproject_id: + * FOR SOURCE ($id), optional, + * is null will be computed here + * @param array $options: + * map + * + */ + public function copy_to($id, $parent_id, $user_id, $tproject_id = null, + $options = null) + { + $new_item = array( + 'id' => - 1, + 'status_ok' => 0, + 'msg' => 'ok', + 'mappings' => null + ); + $my['options'] = array( + 'copy_also' => null, + 'caller' => '' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + if (is_null($my['options']['copy_also'])) { + $my['options']['copy_also'] = array( + 'testcase_assignment' => true + ); + } + + $copyReqVTCVLinks = isset( + $my['options']['copy_also']['testcase_assignment']) && + $my['options']['copy_also']['testcase_assignment']; + + $root = $tproject_id; + if (is_null($root)) { + $reqSpecMgr = new requirement_spec_mgr($this->db); + $target = $reqSpecMgr->get_by_id($parent_id); + $root = $target['testproject_id']; + } + + // NEED INLINE REFACTORING + $item_versions = $this->get_by_id($id); + if ($item_versions) { + if ($my['options']['caller'] == 'copy_testproject') { + $target_doc = $item_versions[0]['req_doc_id']; + $title = $item_versions[0]['title']; + } else { + // REQ DOCID is test project wide => can not exist duplicates inside + // a test project => we need to generate a new one using target as + // starting point + $target_doc = $this->generateDocID($id, $root); + + // If a sibling exists with same title => need to generate automatically + // a new one. + $title = $this->generateUniqueTitle($item_versions[0]['title'], + $parent_id, $root); + } + + $new_item = $this->create_req_only($parent_id, $target_doc, $title, + $item_versions[0]['node_order']); + + if ($new_item['status_ok']) { + $ret['status_ok'] = 1; + $new_item['mappings']['req'][$id] = $new_item['id']; + + foreach ($item_versions as &$req_version) { + $op = $this->create_version($new_item['id'], + $req_version['version'], $req_version['scope'], + $req_version['author_id'], $req_version['status'], + $req_version['type'], $req_version['expected_coverage']); + + // need to explain how this mappings are used outside this method + // first thing that can see here, we are mixing req id and + // req version id on same hash. + $new_item['mappings']['req_version'][$req_version['version_id']] = $op['id']; + $new_item['mappings']['req_tree'][$id][$req_version['version_id']] = $op['id']; + + // here we have made a mistake, that help to show that + // we have some memory issue + // with copy_cfields(). + // ALWAYS when we have tproject_id we have to use it!!! + $this->copy_cfields( + array( + 'id' => $req_version['id'], + 'version_id' => $req_version['version_id'] + ), + array( + 'id' => $new_item['id'], + 'version_id' => $op['id'] + ), $tproject_id); + + $source = $req_version['version_id']; + $dest = $op['id']; + $this->attachmentRepository->copyAttachments($source, $dest, + $this->attachmentTableName); + + // Seems that when we call this function during Test Project Copy + // we DO NOT USE this piece + + if ($copyReqVTCVLinks) { + $lnk = $this->getGoodForReqVersion( + $req_version['version_id']); + if (! is_null($lnk)) { + $reqAndVer = array( + 'id' => $new_item['id'], + 'version_id' => $op['id'] + ); + foreach ($lnk as $links) { + foreach ($links as $value) { + $tcAndVer = array( + 'id' => $value['testcase_id'], + 'version_id' => $value['tcversion_id'] + ); + $this->assignReqVerToTCVer($reqAndVer, + $tcAndVer, $user_id); + } + } + } + } + + unset($op); + } + } + } + + unset($item_versions); // does this help to release memory ? + + return $new_item; + } + + /** + * Copy attachments from source to target + */ + private function copyAttachments($source_id, $target_id) + { + $this->attachmentRepository->copyAttachments($source_id, $target_id, + $this->attachmentTableName); + } + + /* + * function: copy_cfields + * Get all cfields linked to any testcase of this testproject + * with the values presents for $from_id, testcase we are using as + * source for our copy. + * + * args: from_id: source item id + * to_id: target item id + * + * returns: - + */ + private function copy_cfields($source, $destination, $tproject_id = null) + { + $cfmap_from = $this->get_linked_cfields($source['id'], + $source['version_id'], $tproject_id); + $cfield = null; + if (! is_null($cfmap_from)) { + foreach ($cfmap_from as $key => $value) { + $cfield[$key] = array( + "type_id" => $value['type'], + "cf_value" => $value['value'] + ); + } + $this->cfield_mgr->design_values_to_db($cfield, + $destination['version_id'], null, 'reqversion_copy_cfields'); + } + } + + /** + */ + private function generateDocID($id, $tproject_id) + { + $item_info = $this->get_by_id($id); + $item_info = $item_info[0]; + + // Check if another req with same DOC ID exists on test project (MASTER CONTAINER), + // If yes generate a new DOC ID + $getOptions = array( + 'check_criteria' => 'like', + 'access_key' => 'req_doc_id' + ); + $itemSet = $this->getByDocID($item_info['req_doc_id'], $tproject_id, + null, $getOptions); + + $target_doc = $item_info['req_doc_id']; + $instance = 1; + if (! is_null($itemSet)) { + $safety_len = 2; // use t + $mask = $this->reqCfg->duplicated_docid_algorithm->text; + + // req_doc_id has limited size then we need to be sure that generated id will + // not exceed DB size + $nameSet = array_flip(array_keys($itemSet)); + $prefix = trimAndLimit($item_info['req_doc_id'], + $this->fieldSize->req_docid - strlen($mask) - $safety_len); + + $target_doc = $prefix . sprintf($mask, $instance); + while (isset($nameSet[$target_doc])) { + $instance ++; + $target_doc = $prefix . sprintf($mask, $instance); + } + } + return $target_doc; + } + + /** + */ + private function create_req_only($srs_id, $reqdoc_id, $title, + $node_order = 0) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $req_id = $this->tree_mgr->new_node($srs_id, + $this->node_types_descr_id['requirement'], $title, $node_order); + $sql = "/* $debugMsg */ INSERT INTO {$this->object_table} " . + " (id, srs_id, req_doc_id)" . " VALUES ({$req_id}, {$srs_id},'" . + $this->db->prepare_string($reqdoc_id) . "')"; + + if (! $this->db->exec_query($sql)) { + $result = array( + 'id' => - 1, + 'status_ok' => 0 + ); + $result['msg'] = lang_get('error_inserting_req'); + } else { + $result = array( + 'id' => $req_id, + 'status_ok' => 1, + 'msg' => 'ok' + ); + } + + unset($sql); + unset($req_id); + + return $result; + } + + /* + * function: create_version + * + * args: + * + * returns: + */ + private function create_version($id, $version, $scope, $user_id, + $status = TL_REQ_STATUS_VALID, $type = TL_REQ_TYPE_INFO, + $expected_coverage = 1) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $item_id = $this->tree_mgr->new_node($id, + $this->node_types_descr_id['requirement_version']); + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['req_versions']} " . + " (id,version,scope,status,type,expected_coverage,author_id,creation_ts) " . + " VALUES({$item_id},{$version},'" . + trim($this->db->prepare_string($scope)) . "','" . + $this->db->prepare_string($status) . "','" . + $this->db->prepare_string($type) . "'," . "{$expected_coverage}," . + intval($user_id) . "," . $this->db->db_now() . ")"; + + $result = $this->db->exec_query($sql); + $ret = array( + 'msg' => 'ok', + 'id' => $item_id, + 'status_ok' => 1 + ); + if (! $result) { + $ret['msg'] = $this->db->error_msg(); + $ret['status_ok'] = 0; + $ret['id'] = - 1; + } + unset($sql); + unset($result); + unset($item_id); + return $ret; + } + + /* + * function: create_new_version() + * create a new version, doing BY DEFAULT a copy of last version. + * If reqVersionID is passed, then this version will be used as source data. + * + * args : $id: requirement id + * $user_id: who is doing this operation. + * $reqVersionID = default null => use last version as source + * + * returns: + * map: id: node id of created tcversion + * version: version number (i.e. 5) + * msg + * + */ + public function create_new_version($id, $user_id, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['opt'] = array( + 'reqVersionID' => null, + 'log_msg' => null, + 'notify' => false, + 'freezeSourceVersion' => true + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $reqVersionID = $my['opt']['reqVersionID']; + $log_msg = $my['opt']['log_msg']; + $notify = $my['opt']['notify']; + + // get a new id + $version_id = $this->tree_mgr->new_node($id, + $this->node_types_descr_id['requirement_version']); + + // Needed to get higher version NUMBER, to generata new VERSION NUMBER + $sourceVersionInfo = $this->get_last_child_info($id, + array( + 'child_type' => 'version' + )); + if (is_null($sourceVersionInfo)) { + throw new Exception( + $debugMsg . + ' $this->get_last_child_info() RETURNED NULL !!! - WRONG - Open Issue on mantis.testlink.org'); + } + + // Till now everything is OK + if ($notify) { + // be optimistic send email before doing nothing + $this->notifyMonitors($id, __FUNCTION__, $user_id, $log_msg); + } + + $newVersionNumber = $sourceVersionInfo['version'] + 1; + + $ret = array(); + $ret['id'] = $version_id; + $ret['version'] = $newVersionNumber; + $ret['msg'] = 'ok'; + + $sourceVersionID = is_null($reqVersionID) ? $sourceVersionInfo['id'] : $reqVersionID; + + // Update Link Status To Test Case Versions for Source Version + // is done on copy_version() + $this->copy_version($id, $sourceVersionID, $version_id, + $newVersionNumber, $user_id); + + // need to update log message in new created version + $sql = "/* $debugMsg */ " . " UPDATE {$this->tables['req_versions']} " . + " SET log_message = '" . trim($this->db->prepare_string($log_msg)) . + "'" . " WHERE id={$version_id}"; + $this->db->exec_query($sql); + + if ($my['opt']['freezeSourceVersion']) { + $this->updateOpen($sourceVersionInfo['id'], 0); + } + + return $ret; + } + + /** + */ + public function getLastVersionInfo($id, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $info = null; + $sql = " /* $debugMsg */ SELECT MAX(version) AS version " . + " FROM {$this->tables['req_versions']} REQV," . + " {$this->tables['nodes_hierarchy']} NH WHERE " . " NH.id = REQV.id " . + " AND NH.parent_id = {$id} "; + + $max_version = $this->db->fetchFirstRowSingleColumn($sql, 'version'); + if ($max_version) { + $my['opt'] = array( + 'output' => 'default' + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + switch ($my['opt']['output']) { + case 'id': + $fields = ' REQV.id '; + break; + + case 'id,version': + $fields = ' REQV.id, REQV.version '; + break; + + default: + $fields = ' REQV.*'; + break; + } + $sql = "/* $debugMsg */ SELECT {$fields} " . + " FROM {$this->tables['req_versions']} REQV," . + " {$this->tables['nodes_hierarchy']} NH " . + " WHERE version = {$max_version} AND NH.id = REQV.id AND NH.parent_id = {$id}"; + + $info = $this->db->fetchFirstRow($sql); + } + return $info; + } + + /** + * get last defined req doc id for specific test project + * + * @author Julian Krien + * + * @param int $tproj_id + * test project id + * + * @return string last defned req doc id + */ + public function get_last_doc_id_for_testproject($tproj_id) + { + $info = null; + $tproject_mgr = new testproject($this->db); + $all_reqs = $tproject_mgr->get_all_requirement_ids($tproj_id); + + if (! empty($all_reqs)) { + // only use maximum value of all reqs array + $last_req = max($all_reqs); + $last_req = $this->get_by_id($last_req); + $info = $last_req[0]['req_doc_id']; + } + return $info; + } + + /** + */ + private function copy_version($id, $from_version_id, $to_version_id, + $as_version_number, $user_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $now = $this->db->db_now(); + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['req_versions']} " . + " (id,version,author_id,creation_ts,scope,status,type,expected_coverage) " . + " SELECT {$to_version_id} AS id, {$as_version_number} AS version, " . + " {$user_id} AS author_id, {$now} AS creation_ts," . + " scope,status,type,expected_coverage " . + " FROM {$this->tables['req_versions']} " . " WHERE id=" . + intval($from_version_id); + $this->db->exec_query($sql); + + $this->copy_cfields( + array( + 'id' => $id, + 'version_id' => $from_version_id + ), array( + 'id' => $id, + 'version_id' => $to_version_id + )); + + $this->copyAttachments($from_version_id, $to_version_id); + + $reqTCLinksCfg = config_get('reqTCLinks'); + $freezeLinkOnNewReqVersion = $reqTCLinksCfg->freezeLinkOnNewReqVersion; $freezeLinkedTCases = $freezeLinkOnNewReqVersion & - $reqTCLinksCfg->freezeBothEndsOnNewREQVersion; - - if( $freezeLinkedTCases ) { - $this->closeOpenTCVersionOnOpenLinks( $from_version_id ); - } - - $signature = array('user_id' => $user_id, 'when' => $now); - - if( $freezeLinkOnNewReqVersion ) { - $this->updateTCVLinkStatus($from_version_id,LINK_TC_REQ_CLOSED_BY_NEW_REQVERSION); - } - - } - - /** - * - */ - function closeOpenTCVersionOnOpenLinks( $reqVersionID ) { - - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; - - $commonWhere = " WHERE req_version_id = " . intval($reqVersionID) . - " AND link_status = " . LINK_TC_REQ_OPEN; - - $sql = " $debugMsg UPDATE {$this->tables['tcversions']} - SET is_open = 0 - WHERE id IN ( - SELECT tcversion_id - FROM {$this->tables['req_coverage']} - $commonWhere - ) AND is_open = 1"; - - $this->db->exec_query($sql); - } - - /** - * - * - */ - function updateOpen($reqVersionID,$value) { - $this->updateBoolean($reqVersionID,'is_open',$value); - } - - - /** - * - * - */ - function updateActive($reqVersionID,$value) { - $this->updateBoolean($reqVersionID,'active',$value); - } - - /** - * - * - */ - private function updateBoolean($reqVersionID,$field,$value) - { - $booleanValue = $value; - if( is_bool($booleanValue) ) { - $booleanValue = $booleanValue ? 1 : 0; - } else if( !is_numeric($booleanValue) || is_null($booleanValue)) { - $booleanValue = 1; - } - $booleanValue = $booleanValue > 0 ? 1 : 0; - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ UPDATE {$this->tables['req_versions']} " . - " SET {$field}={$booleanValue} WHERE id={$reqVersionID}"; - - $result = $this->db->exec_query($sql); - - } - - - /** - * get relations for a given requirement ID - * - * @author Andreas Simon - * - * @param int $id Requirement ID - * - * @return array $relations in which this req is either source or destination - */ - public function get_relations($id) - { - - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $relations = array(); - $relations['num_relations'] = 0; - $relations['req'] = current($this->get_by_id($id)); - $relations['relations'] = array(); - - $tproject_mgr = new testproject($this->db); - - $sql = " $debugMsg SELECT id, source_id, destination_id, relation_type, author_id, creation_ts " . - " FROM {$this->tables['req_relations']} " . - " WHERE source_id=$id OR destination_id=$id " . - " ORDER BY id ASC "; - - $relations['relations']= $this->db->get_recordset($sql); - if( !is_null($relations['relations']) && count($relations['relations']) > 0 ) - { - $labels = $this->get_all_relation_labels(); - $label_keys = array_keys($labels); - foreach($relations['relations'] as $key => $rel) - { - - // is this relation type is configured? - if( ($relTypeAllowed = in_array($rel['relation_type'],$label_keys)) ) - { - $relations['relations'][$key]['source_localized'] = $labels[$rel['relation_type']]['source']; - $relations['relations'][$key]['destination_localized'] = $labels[$rel['relation_type']]['destination']; - - if ($id == $rel['source_id']) - { - $type_localized = 'source_localized'; - $other_key = 'destination_id'; - } - else - { - $type_localized = 'destination_localized'; - $other_key = 'source_id'; - } - $relations['relations'][$key]['type_localized'] = $relations['relations'][$key][$type_localized]; - $other_req = $this->get_by_id($rel[$other_key]); - - // only add it, if either interproject linking is on or if it is in the same project - $relTypeAllowed = false; - if ($this->relationsCfg->interProjectLinking || ($other_req[0]['testproject_id'] == $relations['req']['testproject_id'])) - { - $relTypeAllowed = true; - $relations['relations'][$key]['related_req'] = $other_req[0]; - $other_tproject = $tproject_mgr->get_by_id($other_req[0]['testproject_id']); - $relations['relations'][$key]['related_req']['testproject_name'] = $other_tproject['name']; - - $user = tlUser::getByID($this->db,$rel['author_id']); - $relations['relations'][$key]['author'] = $user->getDisplayName(); - } - } - - if( !$relTypeAllowed ) - { - unset($relations['relations'][$key]); - } - - } // end foreach - - $relations['num_relations'] = count($relations['relations']); - } - return $relations; - } - - - /** - * checks if there is a relation of a given type between two requirements - * - * @author Andreas Simon - * - * @param integer $first_id requirement ID to check - * @param integer $second_id another requirement ID to check - * @param integer $rel_type_id relation type ID to check - * - * @return true, if relation already exists, false if not - */ - public function check_if_relation_exists($first_id, $second_id, $rel_type_id) { - - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $sql = " $debugMsg SELECT COUNT(0) AS qty " . - " FROM {$this->tables['req_relations']} " . - " WHERE ((source_id=$first_id AND destination_id=$second_id) " . - " OR (source_id=$second_id AND destination_id=$first_id)) " . - " AND relation_type=$rel_type_id"; - $rs = $this->db->get_recordset($sql); - return($rs[0]['qty'] > 0); - } - - - /** - * Get count of all relations for a requirement, no matter if it is source or destination - * or what type of relation it is. - * - * @author Andreas Simon - * - * @param integer $id requirement ID to check - * - * @return integer $count - */ - public function count_relations($id) - { - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $safeID = intval($id); - $sql = " $debugMsg SELECT COUNT(*) AS qty " . - " FROM {$this->tables['req_relations']} " . - " WHERE source_id={$safeID} OR destination_id={$safeID} "; - $rs = $this->db->get_recordset($sql); - return($rs[0]['qty']); - } - - - /** - * add a relation of a given type between two requirements - * - * @author Andreas Simon - * - * @param integer $source_id ID of source requirement - * @param integer $destination_id ID of destination requirement - * @param integer $type_id relation type ID to set - * @param integer $author_id user's ID - */ - public function add_relation($source_id, $destination_id, $type_id, $author_id) { - - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $time = $this->db->db_now(); - $sql = " $debugMsg INSERT INTO {$this->tables['req_relations']} " . - " (source_id, destination_id, relation_type, author_id, creation_ts) " . - " values ($source_id, $destination_id, $type_id, $author_id, $time)"; - $this->db->exec_query($sql); - } - - - /** - * delete an existing relation with between two requirements - * - * @author Andreas Simon - * - * @param int $id requirement relation id - */ - public function delete_relation($id) { - - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $sql = " $debugMsg DELETE FROM {$this->tables['req_relations']} WHERE id=$id "; - $this->db->exec_query($sql); - } - - - /** - * delete all existing relations for (from or to) a given req id, no matter which project - * they belong to or which other requirement they are related to - * - * @author Andreas Simon - * - * @param int $id requirement ID (can be array of IDs) - */ - public function delete_all_relations($id) { - - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $id_list = implode(",", (array)$id); - $sql = " $debugMsg DELETE FROM {$this->tables['req_relations']} " . - " WHERE source_id IN ($id_list) OR destination_id IN ($id_list) "; - $this->db->exec_query($sql); - } - - - /** - * initialize the requirement relation labels - * - * @author Andreas Simon - * - * @return array $labels a map with all labels in following form: - * Array - * ( - * [1] => Array - * ( - * [source] => parent of - * [destination] => child of - * ) - * [2] => Array - * ( - * [source] => blocks - * [destination] => depends on - * ) - * [3] => Array - * ( - * [source] => related to - * [destination] => related to - * ) - * ) - */ - public static function get_all_relation_labels() - { - - $labels = config_get('req_cfg')->rel_type_labels; - - foreach ($labels as $key => $label) { - $labels[$key] = init_labels($label); - } - - return $labels; - } - - - /** - * Initializes the select field for the localized requirement relation types. - * - * @author Andreas Simon - * - * @return array $htmlSelect info needed to create select box on multiple templates - */ - function init_relation_type_select() - { - - $htmlSelect = array('items' => array(), 'selected' => null, 'equal_relations' => array()); - $labels = $this->get_all_relation_labels(); - - foreach ($labels as $key => $lab) - { - $htmlSelect['items'][$key . "_source"] = $lab['source']; - if ($lab['source'] != $lab['destination']) - { - // relation is not equal as labels for source and dest are different - $htmlSelect['items'][$key . "_destination"] = $lab['destination']; - } - else - { - // mark this as equal relation - no parent/child, makes searching simpler - $htmlSelect['equal_relations'][] = $key . "_source"; - } - } - - // set "related to" as default preselected value in forms - if (defined('TL_REQ_REL_TYPE_RELATED') && isset($htmlSelect[TL_REQ_REL_TYPE_RELATED . "_source"])) - { - $selected_key = TL_REQ_REL_TYPE_RELATED . "_source"; - } - else - { - // "related to" is not configured, so take last element as selected one - $keys = array_keys($htmlSelect['items']); - $selected_key = end($keys); - } - $htmlSelect['selected'] = $selected_key; - - return $htmlSelect; - } - - - - /** - * getByAttribute - * allows search (on this version) by one of following attributes - * - title - * - docid - * - */ - function getByAttribute($attr,$tproject_id=null,$parent_id=null, $options = null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array( 'check_criteria' => '=', 'access_key' => 'id', - 'case' => 'sensitive', 'output' => 'standard'); - $my['options'] = array_merge($my['options'], (array)$options); - - $output=null; - $target = $this->db->prepare_string(trim($attr['value'])); - - $where_clause = $attr['key'] == 'title' ? " NH_REQ.name " : " REQ.req_doc_id "; - - switch($my['options']['check_criteria']) - { - case '=': - default: - $check_criteria = " = '{$target}' "; - break; - - case 'like': - case 'likeLeft': - $check_criteria = " LIKE '{$target}%' "; - break; - } - - $sql = " /* $debugMsg */ SELECT "; - switch($my['options']['output']) - { - case 'standard': - $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id, " . - " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order "; - break; - - case 'minimun': - $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id"; - break; - - case 'id': - $sql .= " REQ.id"; - break; - - - } - $sql .= " FROM {$this->object_table} REQ " . - " /* Get Req info from NH */ " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . - " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . - " WHERE {$where_clause} {$check_criteria} "; - - if( !is_null($tproject_id) ) - { - $sql .= " AND REQ_SPEC.testproject_id={$tproject_id}"; - } - - if( !is_null($parent_id) ) - { - $sql .= " AND REQ.srs_id={$parent_id}"; - } - - $output = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']); - return $output; - } - - /** - * @param id: parent id: can be REQ ID or REQ VERSION ID depending of $child_type - * @param child_type: 'req_versions', 'req_revisions' - * - * @return - */ - function get_last_child_info($id, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('child_type' => 'revision', 'output' => 'full'); - $my['options'] = array_merge($my['options'], (array)$options); - - - $info = null; - $target_cfg = array('version' => array('table' => 'req_versions', 'field' => 'version'), - 'revision' => array('table'=> 'req_revisions', 'field' => 'revision')); - - $child_type = $my['options']['child_type']; // just for readability - $table = $target_cfg[$child_type]['table']; - $field = $target_cfg[$child_type]['field']; - - $sql = " /* $debugMsg */ SELECT COALESCE(MAX($field),-1) AS $field " . - " FROM {$this->tables[$table]} CHILD," . - " {$this->tables['nodes_hierarchy']} NH WHERE ". - " NH.id = CHILD.id ". - " AND NH.parent_id = {$id} "; - - $max_verbose = $this->db->fetchFirstRowSingleColumn($sql,$field); - - if ($max_verbose >= 0) - { - $sql = "/* $debugMsg */ SELECT "; - - switch($my['options']['output']) - { - case 'credentials': - $sql .= " CHILD.parent_id,CHILD.id,CHILD.revision,CHILD.doc_id "; - break; - - case 'full': - $sql .= " CHILD.* "; - break; - - default: - $sql .= $my['options']['output']; - break; - } - - $sql .= " FROM {$this->tables[$table]} CHILD," . - " {$this->tables['nodes_hierarchy']} NH ". - " WHERE $field = {$max_verbose} AND NH.id = CHILD.id AND NH.parent_id = {$id}"; - - $info = $this->db->fetchFirstRow($sql); - } - return $info; - } - - - - /** - * - * - * @internal revision - * 20110115 - franciscom - fixed insert of null on timestamp field - */ - function create_new_revision($parent_id,$user_id,$tproject_id,$req = null,$log_msg = null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $item_id = $this->tree_mgr->new_node($parent_id,$this->node_types_descr_id['requirement_revision']); - - // Needed to get higher revision NUMBER, to generata new NUMBER - $source_info = $this->get_last_child_info($parent_id,array('child_type' => 'revision')); - $current_rev = 0; - if( !is_null($source_info) ) - { - $current_rev = $source_info['revision']; - } - $current_rev++; - - // Info regarding new record created on req_revisions table - $ret = array(); - $ret['id'] = $item_id; - $ret['revision'] = $current_rev; - $ret['msg'] = 'ok'; - - $this->copy_version_as_revision($parent_id,$item_id,$current_rev,$user_id,$tproject_id); - $sql = "/* $debugMsg */ " . - " UPDATE {$this->tables['req_revisions']} " . - " SET name ='" . $this->db->prepare_string($req['title']) . "'," . - " req_doc_id ='" . $this->db->prepare_string($req['req_doc_id']) . "'" . - " WHERE id = {$item_id} "; - $this->db->exec_query($sql); - - $new_rev = $current_rev+1; - $db_now = $this->db->db_now(); - $sql = " /* $debugMsg */ " . - " UPDATE {$this->tables['req_versions']} " . - " SET revision = {$new_rev}, log_message=' " . $this->db->prepare_string($log_msg) . "'," . - " creation_ts = {$db_now} ,author_id = {$user_id}, modifier_id = NULL"; - - $nullTS = $this->db->db_null_timestamp(); - if(!is_null($nullTS)) - { - $sql .= ",modification_ts = {$nullTS} "; - } - - $sql .= " WHERE id = {$parent_id} "; - $this->db->exec_query($sql); - return $ret; - } - - - /** - * - * - */ - function copy_version_as_revision($parent_id,$item_id,$revision,$user_id,$tproject_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = '/* $debugMsg */' . - " INSERT INTO {$this->tables['req_revisions']} " . - " (parent_id,id,revision,scope,status,type,active,is_open, " . - " expected_coverage,author_id,creation_ts,modifier_id,modification_ts,log_message) " . - " SELECT REQV.id, {$item_id}, {$revision}, " . - " REQV.scope,REQV.status,REQV.type,REQV.active,REQV.is_open, " . - " REQV.expected_coverage,REQV.author_id,REQV.creation_ts,REQV.modifier_id," . - " REQV.modification_ts,REQV.log_message" . - " FROM {$this->tables['req_versions']} REQV " . - " WHERE REQV.id = {$parent_id} "; - $this->db->exec_query($sql); - - // need to copy Custom Fields ALSO - // BAD NAME -> version_id is REALLY NODE ID - $source = array('id' => 0, 'version_id' => $parent_id); - $dest = array('id' => 0, 'version_id' => $item_id); - $this->copy_cfields($source,$dest,$tproject_id); - - } - - - /** - * used to create overwiew of changes between revisions - * 20110116 - franciscom - BUGID 4172 - MSSQL UNION text field issue - */ - function get_history($id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('output' => "map", 'decode_user' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - // - // Why can I use these common fields ? - // explain better - $common_fields = " REQV.id AS version_id, REQV.version,REQV.creation_ts, REQV.author_id, " . - " REQV.modification_ts, REQV.modifier_id "; - - // needs a double coalesce not too elegant but... - - // Two steps algorithm - // First understand is we already have a revision - $sql = " /* $debugMsg */" . - " SELECT COUNT(0) AS qta_rev " . - " FROM {$this->tables['req_revisions']} REQRV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQRV.parent_id " . - " WHERE NH_REQV.parent_id = {$id} "; - - $rs = $this->db->get_recordset($sql); - - $sql = "/* $debugMsg */" . - " SELECT REQV.id AS version_id, REQV.version," . - " REQV.creation_ts, REQV.author_id, " . - " REQV.modification_ts, REQV.modifier_id, " . - self::NO_REVISION . " AS revision_id, " . - " REQV.revision, REQV.scope, " . - " REQV.status,REQV.type,REQV.expected_coverage,NH_REQ.name, REQ.req_doc_id, " . - " COALESCE(REQV.log_message,'') AS log_message" . - " FROM {$this->tables['req_versions']} REQV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . - " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . - " WHERE NH_REQV.parent_id = {$id} "; - - if( $rs[0]['qta_rev'] > 0 ) - { - // - // Important NOTICE - MSSQL - // - // text fields can be used on union ONLY IF YOU USE UNION ALL - // - // UNION ALL returns ALSO duplicated rows. - // In this situation this is NOT A PROBLEM (because we will not have dups) - // - $sql .= " UNION ALL ( " . - " SELECT REQV.id AS version_id, REQV.version, " . - " REQRV.creation_ts, REQRV.author_id, " . - " REQRV.modification_ts, REQRV.modifier_id, " . - " REQRV.id AS revision_id, " . - " REQRV.revision,REQRV.scope,REQRV.status,REQRV.type, " . - " REQRV.expected_coverage,REQRV.name,REQRV.req_doc_id, " . - " COALESCE(REQRV.log_message,'') as log_message" . - " FROM {$this->tables['req_versions']} REQV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . - " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . - " JOIN {$this->tables['req_revisions']} REQRV " . - " ON REQRV.parent_id = REQV.id " . - " WHERE NH_REQV.parent_id = {$id} " . - " ) " . - " ORDER BY version_id DESC,version DESC,revision DESC "; - } - - switch($my['options']['output']) - { - case 'map': - $rs = $this->db->fetchRowsIntoMap($sql,'version_id'); - break; - - case 'array': - $rs = $this->db->get_recordset($sql); - break; - } - - if( !is_null($rs) ) - { - $lbl = init_labels(array('undefined' => 'undefined')); - $key2loop = array_keys($rs); - foreach($key2loop as $ap) - { - $rs[$ap]['item_id'] = ($rs[$ap]['revision_id'] > 0) ? $rs[$ap]['revision_id'] : $rs[$ap]['version_id']; - - // IMPORTANT NOTICE - // each DBMS uses a different (unfortunatelly) way to signal NULL DATE - // - // We need to Check with ALL DB types - // MySQL NULL DATE -> "0000-00-00 00:00:00" - // Postgres NULL DATE -> NULL - // MSSQL NULL DATE - ??? - $key4date = 'creation_ts'; - $key4user = 'author_id'; - if( ($rs[$ap]['modification_ts'] != '0000-00-00 00:00:00') && !is_null($rs[$ap]['modification_ts']) ) - { - $key4date = 'modification_ts'; - $key4user = 'modifier_id'; - } - $rs[$ap]['timestamp'] = $rs[$ap][$key4date]; - $rs[$ap]['last_editor'] = $rs[$ap][$key4user]; - // decode user_id for last_editor - $user = tlUser::getByID($this->db,$rs[$ap]['last_editor']); - $rs[$ap]['last_editor'] = $user ? $user->getDisplayName() : $lbl['undefined']; - } - } - - $history = $rs; - if( $my['options']['decode_user'] && !is_null($history) ) - { - $this->decode_users($history); - } - - return $history; - } - - - /** - * - * - */ - function get_version($version_id,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('renderImageInline' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = " /* $debugMsg */ SELECT REQ.id,REQ.srs_id,REQ.req_doc_id," . - " REQV.scope,REQV.status,REQV.type,REQV.active," . - " REQV.is_open,REQV.author_id,REQV.version,REQV.revision,REQV.id AS version_id," . - " REQV.expected_coverage,REQV.creation_ts,REQV.modifier_id," . - " REQV.modification_ts,REQV.revision,NH_REQ.name AS title, REQ_SPEC.testproject_id, " . - " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order " . - " FROM {$this->object_table} REQ " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id ". - " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . - " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . - " WHERE REQV.id = " . intval($version_id); - - $dummy = $this->db->get_recordset($sql); - - if( !is_null($dummy) ) - { - $this->decode_users($dummy); - $dummy = $dummy[0]; - } - - if(!is_null($dummy) && $my['opt']['renderImageInline']) - { - $this->renderImageAttachments($dummy['id'],$dummy); - } - - return $dummy; - } - - - - /** - * - * - * @internal revision - * - */ - function get_revision($revision_id,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('renderImageInline' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = " /* $debugMsg */ " . - " SELECT REQV.id AS req_version_id,REQ.id,REQ.srs_id, + $reqTCLinksCfg->freezeBothEndsOnNewREQVersion; + + if ($freezeLinkedTCases) { + $this->closeOpenTCVersionOnOpenLinks($from_version_id); + } + + if ($freezeLinkOnNewReqVersion) { + $this->updateTCVLinkStatus($from_version_id, + LINK_TC_REQ_CLOSED_BY_NEW_REQVERSION); + } + } + + /** + */ + private function closeOpenTCVersionOnOpenLinks($reqVersionID) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $commonWhere = " WHERE req_version_id = " . intval($reqVersionID) . + " AND link_status = " . LINK_TC_REQ_OPEN; + $sql = "/* $debugMsg */ UPDATE {$this->tables['tcversions']} " . + " SET is_open = 0" . " WHERE id IN ( SELECT tcversion_id " . + " FROM {$this->tables['req_coverage']} $commonWhere " . + " ) AND is_open = 1"; + + $this->db->exec_query($sql); + } + + /** + */ + public function updateOpen($reqVersionID, $value) + { + $this->updateBoolean($reqVersionID, 'is_open', $value); + } + + /** + * + * @todo delete the unused function if necessary + */ + private function updateActive($reqVersionID, $value) + { + $this->updateBoolean($reqVersionID, 'active', $value); + } + + /** + */ + private function updateBoolean($reqVersionID, $field, $value) + { + $booleanValue = $value; + if (is_bool($booleanValue)) { + $booleanValue = $booleanValue ? 1 : 0; + } elseif (! is_numeric($booleanValue) || is_null($booleanValue)) { + $booleanValue = 1; + } + $booleanValue = $booleanValue > 0 ? 1 : 0; + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ UPDATE {$this->tables['req_versions']} " . + " SET {$field}={$booleanValue} WHERE id={$reqVersionID}"; + + $this->db->exec_query($sql); + } + + /** + * get relations for a given requirement ID + * + * @author Andreas Simon + * + * @param int $id + * Requirement ID + * + * @return array $relations in which this req is either source or destination + */ + public function get_relations($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $relations = array(); + $relations['num_relations'] = 0; + $relations['req'] = current($this->get_by_id($id)); + + $tproject_mgr = new testproject($this->db); + + $sql = "/* $debugMsg */ SELECT id, source_id, destination_id, relation_type, author_id, creation_ts " . + " FROM {$this->tables['req_relations']} " . + " WHERE source_id=$id OR destination_id=$id " . " ORDER BY id ASC "; + + $relations['relations'] = $this->db->get_recordset($sql); + if (! empty($relations['relations'])) { + $labels = $this->get_all_relation_labels(); + $label_keys = array_keys($labels); + foreach ($relations['relations'] as $key => $rel) { + + // is this relation type is configured? + if ($relTypeAllowed = in_array($rel['relation_type'], + $label_keys)) { + $relations['relations'][$key]['source_localized'] = $labels[$rel['relation_type']]['source']; + $relations['relations'][$key]['destination_localized'] = $labels[$rel['relation_type']]['destination']; + + if ($id == $rel['source_id']) { + $type_localized = 'source_localized'; + $other_key = 'destination_id'; + } else { + $type_localized = 'destination_localized'; + $other_key = 'source_id'; + } + $relations['relations'][$key]['type_localized'] = $relations['relations'][$key][$type_localized]; + $other_req = $this->get_by_id($rel[$other_key]); + + // only add it, if either interproject linking is on or if it is in the same project + $relTypeAllowed = false; + if ($this->relationsCfg->interProjectLinking || + ($other_req[0]['testproject_id'] == + $relations['req']['testproject_id'])) { + $relTypeAllowed = true; + $relations['relations'][$key]['related_req'] = $other_req[0]; + $other_tproject = $tproject_mgr->get_by_id( + $other_req[0]['testproject_id']); + $relations['relations'][$key]['related_req']['testproject_name'] = $other_tproject['name']; + + $user = tlUser::getByID($this->db, $rel['author_id']); + $relations['relations'][$key]['author'] = $user->getDisplayName(); + } + } + + if (! $relTypeAllowed) { + unset($relations['relations'][$key]); + } + } + + $relations['num_relations'] = count($relations['relations']); + } + return $relations; + } + + /** + * checks if there is a relation of a given type between two requirements + * + * @author Andreas Simon + * + * @param integer $first_id + * requirement ID to check + * @param integer $second_id + * another requirement ID to check + * @param integer $rel_type_id + * relation type ID to check + * + * @return true, if relation already exists, false if not + */ + public function check_if_relation_exists($first_id, $second_id, $rel_type_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT COUNT(0) AS qty " . + " FROM {$this->tables['req_relations']} " . + " WHERE ((source_id=$first_id AND destination_id=$second_id) " . + " OR (source_id=$second_id AND destination_id=$first_id)) " . + " AND relation_type=$rel_type_id"; + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty'] > 0; + } + + /** + * Get count of all relations for a requirement, no matter if it is source or destination + * or what type of relation it is. + * + * @author Andreas Simon + * + * @param integer $id + * requirement ID to check + * + * @return integer $count + */ + public function count_relations($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $safeID = intval($id); + $sql = "/* $debugMsg */ SELECT COUNT(*) AS qty " . + " FROM {$this->tables['req_relations']} " . + " WHERE source_id={$safeID} OR destination_id={$safeID} "; + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty']; + } + + /** + * add a relation of a given type between two requirements + * + * @author Andreas Simon + * + * @param integer $source_id + * ID of source requirement + * @param integer $destination_id + * ID of destination requirement + * @param integer $type_id + * relation type ID to set + * @param integer $author_id + * user's ID + */ + public function add_relation($source_id, $destination_id, $type_id, + $author_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $time = $this->db->db_now(); + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['req_relations']} " . + " (source_id, destination_id, relation_type, author_id, creation_ts) " . + " values ($source_id, $destination_id, $type_id, $author_id, $time)"; + $this->db->exec_query($sql); + } + + /** + * delete an existing relation with between two requirements + * + * @author Andreas Simon + * + * @param int $id + * requirement relation id + */ + public function delete_relation($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['req_relations']} WHERE id=$id "; + $this->db->exec_query($sql); + } + + /** + * delete all existing relations for (from or to) a given req id, no matter which project + * they belong to or which other requirement they are related to + * + * @author Andreas Simon + * + * @param int $id + * requirement ID (can be array of IDs) + */ + public function delete_all_relations($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $id_list = implode(",", (array) $id); + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['req_relations']} " . + " WHERE source_id IN ($id_list) OR destination_id IN ($id_list) "; + $this->db->exec_query($sql); + } + + /** + * initialize the requirement relation labels + * + * @author Andreas Simon + * + * @return array $labels a map with all labels in following form: + * Array + * ( + * [1] => Array + * ( + * [source] => parent of + * [destination] => child of + * ) + * [2] => Array + * ( + * [source] => blocks + * [destination] => depends on + * ) + * [3] => Array + * ( + * [source] => related to + * [destination] => related to + * ) + * ) + */ + public static function get_all_relation_labels() + { + $labels = config_get('req_cfg')->rel_type_labels; + foreach ($labels as $key => $label) { + $labels[$key] = init_labels($label); + } + + return $labels; + } + + /** + * Initializes the select field for the localized requirement relation types. + * + * @author Andreas Simon + * + * @return array $htmlSelect info needed to create select box on multiple templates + */ + public function init_relation_type_select() + { + $htmlSelect = array( + 'items' => array(), + 'selected' => null, + 'equal_relations' => array() + ); + $labels = $this->get_all_relation_labels(); + + foreach ($labels as $key => $lab) { + $htmlSelect['items'][$key . "_source"] = $lab['source']; + if ($lab['source'] != $lab['destination']) { + // relation is not equal as labels for source and dest are different + $htmlSelect['items'][$key . "_destination"] = $lab['destination']; + } else { + // mark this as equal relation - no parent/child, makes searching simpler + $htmlSelect['equal_relations'][] = $key . "_source"; + } + } + + // set "related to" as default preselected value in forms + if (defined('TL_REQ_REL_TYPE_RELATED') && + isset($htmlSelect[TL_REQ_REL_TYPE_RELATED . "_source"])) { + $selected_key = TL_REQ_REL_TYPE_RELATED . "_source"; + } else { + // "related to" is not configured, so take last element as selected one + $keys = array_keys($htmlSelect['items']); + $selected_key = end($keys); + } + $htmlSelect['selected'] = $selected_key; + + return $htmlSelect; + } + + /** + * getByAttribute + * allows search (on this version) by one of following attributes + * - title + * - docid + */ + private function getByAttribute($attr, $tproject_id = null, + $parent_id = null, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'check_criteria' => '=', + 'access_key' => 'id', + 'case' => 'sensitive', + 'output' => 'standard' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $target = $this->db->prepare_string(trim($attr['value'])); + + $where_clause = $attr['key'] == 'title' ? " NH_REQ.name " : " REQ.req_doc_id "; + + switch ($my['options']['check_criteria']) { + case 'like': + case 'likeLeft': + $check_criteria = " LIKE '{$target}%' "; + break; + + case '=': + default: + $check_criteria = " = '{$target}' "; + break; + } + + $sql = " /* $debugMsg */ SELECT "; + switch ($my['options']['output']) { + case 'standard': + $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id, " . + " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order "; + break; + + case 'minimun': + $sql .= " REQ.id,REQ.srs_id,REQ.req_doc_id,NH_REQ.name AS title, REQ_SPEC.testproject_id"; + break; + + case 'id': + $sql .= " REQ.id"; + break; + } + $sql .= " FROM {$this->object_table} REQ " . + " /* Get Req info from NH */ " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . + " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . + " WHERE {$where_clause} {$check_criteria} "; + + if (! is_null($tproject_id)) { + $sql .= " AND REQ_SPEC.testproject_id={$tproject_id}"; + } + + if (! is_null($parent_id)) { + $sql .= " AND REQ.srs_id={$parent_id}"; + } + + return $this->db->fetchRowsIntoMap($sql, $my['options']['access_key']); + } + + /** + * + * @param + * id: parent id: can be REQ ID or REQ VERSION ID depending of $child_type + * @param + * child_type: 'req_versions', 'req_revisions' + * + * @return + */ + private function get_last_child_info($id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'child_type' => 'revision', + 'output' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $info = null; + $target_cfg = array( + 'version' => array( + 'table' => 'req_versions', + 'field' => 'version' + ), + 'revision' => array( + 'table' => 'req_revisions', + 'field' => 'revision' + ) + ); + + $child_type = $my['options']['child_type']; // just for readability + $table = $target_cfg[$child_type]['table']; + $field = $target_cfg[$child_type]['field']; + + $sql = " /* $debugMsg */ SELECT COALESCE(MAX($field),-1) AS $field " . + " FROM {$this->tables[$table]} CHILD," . + " {$this->tables['nodes_hierarchy']} NH WHERE " . + " NH.id = CHILD.id " . " AND NH.parent_id = {$id} "; + + $max_verbose = $this->db->fetchFirstRowSingleColumn($sql, $field); + + if ($max_verbose >= 0) { + $sql = "/* $debugMsg */ SELECT "; + + switch ($my['options']['output']) { + case 'credentials': + $sql .= " CHILD.parent_id,CHILD.id,CHILD.revision,CHILD.doc_id "; + break; + + case 'full': + $sql .= " CHILD.* "; + break; + + default: + $sql .= $my['options']['output']; + break; + } + + $sql .= " FROM {$this->tables[$table]} CHILD," . + " {$this->tables['nodes_hierarchy']} NH " . + " WHERE $field = {$max_verbose} AND NH.id = CHILD.id AND NH.parent_id = {$id}"; + + $info = $this->db->fetchFirstRow($sql); + } + return $info; + } + + /** + * + * @internal revision + * 20110115 - franciscom - fixed insert of null on timestamp field + */ + public function create_new_revision($parent_id, $user_id, $tproject_id, + $req = null, $log_msg = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $item_id = $this->tree_mgr->new_node($parent_id, + $this->node_types_descr_id['requirement_revision']); + + // Needed to get higher revision NUMBER, to generata new NUMBER + $source_info = $this->get_last_child_info($parent_id, + array( + 'child_type' => 'revision' + )); + $current_rev = 0; + if (! is_null($source_info)) { + $current_rev = $source_info['revision']; + } + $current_rev ++; + + // Info regarding new record created on req_revisions table + $ret = array(); + $ret['id'] = $item_id; + $ret['revision'] = $current_rev; + $ret['msg'] = 'ok'; + + $this->copy_version_as_revision($parent_id, $item_id, $current_rev, + $tproject_id); + $sql = "/* $debugMsg */ " . " UPDATE {$this->tables['req_revisions']} " . + " SET name ='" . $this->db->prepare_string($req['title']) . "'," . + " req_doc_id ='" . $this->db->prepare_string($req['req_doc_id']) . + "'" . " WHERE id = {$item_id} "; + $this->db->exec_query($sql); + + $new_rev = $current_rev + 1; + $db_now = $this->db->db_now(); + $sql = " /* $debugMsg */ " . " UPDATE {$this->tables['req_versions']} " . + " SET revision = {$new_rev}, log_message=' " . + $this->db->prepare_string($log_msg) . "'," . + " creation_ts = {$db_now} ,author_id = {$user_id}, modifier_id = NULL"; + + $nullTS = $this->db->db_null_timestamp(); + if (! is_null($nullTS)) { + $sql .= ",modification_ts = {$nullTS} "; + } + + $sql .= " WHERE id = {$parent_id} "; + $this->db->exec_query($sql); + return $ret; + } + + /** + */ + private function copy_version_as_revision($parent_id, $item_id, $revision, + $tproject_id) + { + $sql = '/* $debugMsg */' . + " INSERT INTO {$this->tables['req_revisions']} " . + " (parent_id,id,revision,scope,status,type,active,is_open, " . + " expected_coverage,author_id,creation_ts,modifier_id,modification_ts,log_message) " . + " SELECT REQV.id, {$item_id}, {$revision}, " . + " REQV.scope,REQV.status,REQV.type,REQV.active,REQV.is_open, " . + " REQV.expected_coverage,REQV.author_id,REQV.creation_ts,REQV.modifier_id," . + " REQV.modification_ts,REQV.log_message" . + " FROM {$this->tables['req_versions']} REQV " . + " WHERE REQV.id = {$parent_id} "; + $this->db->exec_query($sql); + + // need to copy Custom Fields ALSO + // BAD NAME -> version_id is REALLY NODE ID + $source = array( + 'id' => 0, + 'version_id' => $parent_id + ); + $dest = array( + 'id' => 0, + 'version_id' => $item_id + ); + $this->copy_cfields($source, $dest, $tproject_id); + } + + /** + * used to create overwiew of changes between revisions + * 20110116 - franciscom - BUGID 4172 - MSSQL UNION text field issue + */ + public function get_history($id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'output' => "map", + 'decode_user' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + // Why can I use these common fields ? + // explain better + // $common_fields = " REQV.id AS version_id, REQV.version,REQV.creation_ts, REQV.author_id, " . + // " REQV.modification_ts, REQV.modifier_id "; + + // needs a double coalesce not too elegant but... + + // Two steps algorithm + // First understand is we already have a revision + $sql = " /* $debugMsg */" . " SELECT COUNT(0) AS qta_rev " . + " FROM {$this->tables['req_revisions']} REQRV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQRV.parent_id " . + " WHERE NH_REQV.parent_id = {$id} "; + + $rs = $this->db->get_recordset($sql); + + $sql = "/* $debugMsg */" . " SELECT REQV.id AS version_id, REQV.version," . + " REQV.creation_ts, REQV.author_id, " . + " REQV.modification_ts, REQV.modifier_id, " . self::NO_REVISION . + " AS revision_id, " . " REQV.revision, REQV.scope, " . + " REQV.status,REQV.type,REQV.expected_coverage,NH_REQ.name, REQ.req_doc_id, " . + " COALESCE(REQV.log_message,'') AS log_message" . + " FROM {$this->tables['req_versions']} REQV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . + " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . + " WHERE NH_REQV.parent_id = {$id} "; + + if ($rs[0]['qta_rev'] > 0) { + // Important NOTICE - MSSQL + // + // text fields can be used on union ONLY IF YOU USE UNION ALL + // + // UNION ALL returns ALSO duplicated rows. + // In this situation this is NOT A PROBLEM (because we will not have dups) + // + $sql .= " UNION ALL ( " . + " SELECT REQV.id AS version_id, REQV.version, " . + " REQRV.creation_ts, REQRV.author_id, " . + " REQRV.modification_ts, REQRV.modifier_id, " . + " REQRV.id AS revision_id, " . + " REQRV.revision,REQRV.scope,REQRV.status,REQRV.type, " . + " REQRV.expected_coverage,REQRV.name,REQRV.req_doc_id, " . + " COALESCE(REQRV.log_message,'') as log_message" . + " FROM {$this->tables['req_versions']} REQV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . + " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . + " JOIN {$this->tables['req_revisions']} REQRV " . + " ON REQRV.parent_id = REQV.id " . + " WHERE NH_REQV.parent_id = {$id} " . " ) " . + " ORDER BY version_id DESC,version DESC,revision DESC "; + } + + switch ($my['options']['output']) { + case 'map': + $rs = $this->db->fetchRowsIntoMap($sql, 'version_id'); + break; + + case 'array': + $rs = $this->db->get_recordset($sql); + break; + } + + if (! is_null($rs)) { + $lbl = init_labels(array( + 'undefined' => 'undefined' + )); + $key2loop = array_keys($rs); + foreach ($key2loop as $ap) { + $rs[$ap]['item_id'] = ($rs[$ap]['revision_id'] > 0) ? $rs[$ap]['revision_id'] : $rs[$ap]['version_id']; + + // IMPORTANT NOTICE + // each DBMS uses a different (unfortunatelly) way to signal NULL DATE + // + // We need to Check with ALL DB types + // MySQL NULL DATE -> "0000-00-00 00:00:00" + // Postgres NULL DATE -> NULL + // MSSQL NULL DATE - ??? + $key4date = 'creation_ts'; + $key4user = 'author_id'; + $nullTS = $this->db->db_null_timestamp(); + if (($rs[$ap]['modification_ts'] != $nullTS) && + ! is_null($rs[$ap]['modification_ts']) && + ! is_null($rs[$ap]['modifier_id'])) { + $key4date = 'modification_ts'; + $key4user = 'modifier_id'; + } + $rs[$ap]['timestamp'] = $rs[$ap][$key4date]; + $rs[$ap]['last_editor'] = $rs[$ap][$key4user]; + // decode user_id for last_editor + $user = tlUser::getByID($this->db, $rs[$ap]['last_editor']); + $rs[$ap]['last_editor'] = $user ? $user->getDisplayName() : $lbl['undefined']; + } + } + + $history = $rs; + if ($my['options']['decode_user'] && ! is_null($history)) { + $this->decode_users($history); + } + + return $history; + } + + /** + */ + public function get_version($version_id, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['opt'] = array( + 'renderImageInline' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = " /* $debugMsg */ SELECT REQ.id,REQ.srs_id,REQ.req_doc_id," . + " REQV.scope,REQV.status,REQV.type,REQV.active," . + " REQV.is_open,REQV.author_id,REQV.version,REQV.revision,REQV.id AS version_id," . + " REQV.expected_coverage,REQV.creation_ts,REQV.modifier_id," . + " REQV.modification_ts,REQV.revision,NH_REQ.name AS title, REQ_SPEC.testproject_id, " . + " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order " . + " FROM {$this->object_table} REQ " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id " . + " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . + " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . + " WHERE REQV.id = " . intval($version_id); + + $dummy = $this->db->get_recordset($sql); + + if (! is_null($dummy)) { + $this->decode_users($dummy); + $dummy = $dummy[0]; + } + + if (! is_null($dummy) && $my['opt']['renderImageInline']) { + $this->renderImageAttachments($dummy['id'], $dummy); + } + + return $dummy; + } + + /** + * + * @internal revision + * + */ + public function get_revision($revision_id, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['opt'] = array( + 'renderImageInline' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = " /* $debugMsg */ " . + " SELECT REQV.id AS req_version_id,REQ.id,REQ.srs_id, REQ.req_doc_id,REQRV.scope,REQRV.status,REQRV.type, - REQRV.active," . - " REQRV.is_open,REQRV.author_id,REQV.version,REQRV.parent_id AS version_id," . - " REQRV.expected_coverage,REQRV.creation_ts,REQRV.modifier_id," . - " REQRV.modification_ts,REQRV.revision, REQRV.id AS revision_id," . - " NH_REQ.name AS title, REQ_SPEC.testproject_id, " . - " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order " . - " FROM {$this->tables['req_revisions']} REQRV " . - " JOIN {$this->tables['req_versions']} REQV ON REQV.id = REQRV.parent_id ". - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQRV.parent_id ". - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . - " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . - " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . - " WHERE REQRV.id = " . intval($revision_id); - $dummy = $this->db->get_recordset($sql); - - if( !is_null($dummy) ) - { - $this->decode_users($dummy); - $dummy = $dummy[0]; - } - - if(!is_null($dummy) && $my['opt']['renderImageInline']) - { - $this->renderImageAttachments($dummy['id'],$dummy); - } - - return $dummy; - } - - - /** - * get info regarding a req version, using also revision as access criteria. - * - * @int version_id - * @array revision_access possible keys 'id', 'number' - * - * @uses print.inc.php - * @uses renderReqForPrinting() - * - * - */ - function get_version_revision($version_id,$revision_access,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('renderImageInline' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = "/* $debugMsg */"; - - if( isset($revision_access['number']) ) { - $rev_number = intval($revision_access['number']); - - // we have to tables to search on - // Req Versions -> holds LATEST revision - // Req Revisions -> holds other revisions - $sql .= " SELECT NH_REQV.parent_id AS req_id, REQV.id AS version_id, REQV.version," . - " REQV.creation_ts, REQV.author_id, " . - " REQV.modification_ts, REQV.modifier_id, " . - self::NO_REVISION . " AS revision_id, " . - " REQV.revision, REQV.scope, " . - " REQV.status,REQV.type,REQV.expected_coverage,NH_REQ.name, REQ.req_doc_id, " . - " COALESCE(REQV.log_message,'') AS log_message, NH_REQ.name AS title " . - " FROM {$this->tables['req_versions']} REQV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . - " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . - " WHERE NH_REQV.id = {$version_id} AND REQV.revision = {$rev_number} "; - - $sql .= " UNION ALL ( " . - " SELECT NH_REQV.parent_id AS req_id, REQV.id AS version_id, REQV.version, " . - " REQRV.creation_ts, REQRV.author_id, " . - " REQRV.modification_ts, REQRV.modifier_id, " . - " REQRV.id AS revision_id, " . - " REQRV.revision,REQRV.scope,REQRV.status,REQRV.type, " . - " REQRV.expected_coverage,REQRV.name,REQRV.req_doc_id, " . - " COALESCE(REQRV.log_message,'') as log_message, NH_REQ.name AS title " . - " FROM {$this->tables['req_versions']} REQV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . - " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . - " JOIN {$this->tables['req_revisions']} REQRV " . - " ON REQRV.parent_id = REQV.id " . - " WHERE NH_REQV.id = {$version_id} AND REQRV.revision = {$rev_number} ) "; - - } else { - // revision_id is present ONLY on req revisions table, then we do not need UNION - $sql .= " SELECT NH_REQV.parent_id AS req_id, REQV.id AS version_id, REQV.version, " . - " REQRV.creation_ts, REQRV.author_id, " . - " REQRV.modification_ts, REQRV.modifier_id, " . - " REQRV.id AS revision_id, " . - " REQRV.revision,REQRV.scope,REQRV.status,REQRV.type, " . - " REQRV.expected_coverage,REQRV.name,REQRV.req_doc_id, " . - " COALESCE(REQRV.log_message,'') as log_message, NH_REQ.name AS title " . - " FROM {$this->tables['req_versions']} REQV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . - " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . - " JOIN {$this->tables['req_revisions']} REQRV " . - " ON REQRV.parent_id = REQV.id " . - " WHERE NH_REQV.id = {$version_id} AND REQRV.revision_id = " . intval($revision_access['id']); - } - - $rs = $this->db->get_recordset($sql); - - if(!is_null($rs) && $my['opt']['renderImageInline']) { - $k2l = array_keys($rs); - foreach($k2l as $akx) { - $this->renderImageAttachments($rs[$akx]['req_id'],$rs[$akx]); - } - reset($rs); - } - return $rs; - } - - - - /** - * - * - */ - function decode_users(&$rs) - { - $userCache = null; // key: user id, value: display name - $key2loop = array_keys($rs); - $labels['undefined'] = lang_get('undefined'); - $user_keys = array('author' => 'author_id', 'modifier' => 'modifier_id'); - foreach( $key2loop as $key ) - { - foreach( $user_keys as $ukey => $userid_field) - { - $rs[$key][$ukey] = ''; - if(trim($rs[$key][$userid_field]) != "") - { - if( !isset($userCache[$rs[$key][$userid_field]]) ) - { - $user = tlUser::getByID($this->db,$rs[$key][$userid_field]); - $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; - $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; - } - else - { - $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; - } - } - } - } - } - - - - /** - * - * - */ - function generateUniqueTitle($title2check, $parent_id, $tproject_id) - { - - static $fieldSize; - static $getOptions; - static $reqCfg; - static $mask; - static $title_max_len; - - if( !$fieldSize ) - { - $fieldSize = config_get('field_size'); - $reqCfg = config_get('req_cfg'); - - $mask = $reqCfg->duplicated_name_algorithm->text; - $title_max_len = $fieldSize->requirement_title; - - $getOptions = array('output' => 'minimun', 'check_criteria' => 'likeLeft'); - } - - $generated = $title2check; - $attr = array('key' => 'title', 'value' => $title2check); - - // search need to be done in like to the left - $itemSet = $this->getByAttribute($attr,$tproject_id,$parent_id,$getOptions); - - // we borrow logic (may be one day we can put it on a central place) from - // testcase class create_tcase_only() - if( !is_null($itemSet) && ($siblingQty=count($itemSet)) > 0 ) - { - $nameSet = array_flip(array_keys($itemSet)); - $target = $title2check . ($suffix = sprintf($mask,++$siblingQty)); - $final_len = strlen($target); - if( $final_len > $title_max_len) - { - $target = substr($target,strlen($suffix),$title_max_len); - } - // Need to recheck if new generated name does not crash with existent name - // why? Suppose you have created: - // REQ [1] - // REQ [2] - // REQ [3] - // Then you delete REQ [2]. - // When I got siblings il will got 2 siblings, if I create new progressive using next, - // it will be 3 => I will get duplicated name. - while( isset($nameSet[$target]) ) - { - $target = $title2check . ($suffix = sprintf($mask,++$siblingQty)); - $final_len = strlen($target); - if( $final_len > $title_max_len) - { - $target = substr($target,strlen($suffix),$title_max_len); - } - } - $generated = $target; - } - - return $generated; - } - - - /** - * - * - */ - function getTestProjectID($id, $reqSpecID=null) - { - $reqSpecMgr = new requirement_spec_mgr($this->db); - $parent = $reqSpecID; - if( is_null($parent) ) - { - $dummy = $this->tree_mgr->get_node_hierarchy_info($id); - $parent = $dummy['parent_id']; - } - $target = $reqSpecMgr->get_by_id($parent); - return $target['testproject_id']; - } - - - /** - * @param $context map with following keys - * tproject_id => REQUIRED - * tplan_id => OPTIONAL - * platform_id => OPTIONAL, will be used ONLY - * if tplan_id is provided. - * - */ - function getAllByContext($context,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if( !isset($context['tproject_id']) ) - { - throw new Exception($debugMsg . ' : $context[\'tproject_id\'] is needed'); - } - - $where = "WHERE RSPEC.testproject_id = " . intval($context['tproject_id']); - $sql = "/* $debugMsg */ " . - "SELECT DISTINCT REQ.id,REQ.req_doc_id FROM {$this->tables['requirements']} REQ " . - "JOIN {$this->tables['req_specs']} RSPEC ON RSPEC.id = REQ.srs_id "; - - - if( isset($context['tplan_id']) ) - { - - $sql .= "JOIN {$this->tables['req_coverage']} REQCOV ON REQCOV.req_id = REQ.id " . - "JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = REQCOV.testcase_id " . - "JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NH_TCV.id "; - - $where .= " AND TPTCV.testplan_id = " . intval($context['tplan_id']); - if( isset($context['platform_id']) && intval($context['platform_id']) > 0 ) - { - $where .= " AND TPTCV.platform_id = " . intval($context['platform_id']); - } - } - - - $sql .= $where; - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - - return $rs; - } - - /** - * - * @used-by - */ - function getFileUploadRelativeURL($req_id,$req_version_id) { - $sfReqID = intval($req_id); - $sfVersion = intval($req_version_id); - - $url = "lib/requirements/reqEdit.php" . - "?doAction=fileUpload&requirement_id=" . $sfReqID . - "&req_id=" . $sfReqID ."&req_version_id=" . $sfVersion; - - return $url; - } - - /** - * - * @used-by - */ - function getDeleteAttachmentRelativeURL($req_id,$req_version_id) { - $url = "lib/requirements/reqEdit.php?doAction=deleteFile" . - "&requirement_id=" . intval($req_id) . - "&req_version_id=" . intval($req_version_id) . - "&file_id=" ; - return $url; - } - - - - /** - * exportRelationToXML - * - * Function to export a requirement relation to XML. - * - * @param int $relation relation data array - * @param string $troject_id - * @param boolean check_for_req_project - * - * @return string with XML code - * - * doc_id - * prj - * doc2_id - * prj2 - * 0 - * - * - * @internal revisions - * - */ - function exportRelationToXML( $relation, $tproject_id = null, $check_for_req_project = false) - { - $xmlStr = ''; - $source_docid = null; $destination_docid = null; - $source_project = null; $destination_project = null; - - if( !is_null($relation) ) - { - // FRL : interproject linking support - $tproject_mgr = new testproject($this->db); - $reqs = $this->get_by_id($relation['source_id'],requirement_mgr::LATEST_VERSION); - if ( ! is_null ( $reqs ) && count($reqs) > 0 ) - { - $source_docid = $reqs[0]['req_doc_id']; - if ($check_for_req_project) - { - $tproject = $tproject_mgr->get_by_id($reqs[0]['testproject_id']); - if ($tproject['id'] != $tproject_id) - { - $source_project = $tproject['name']; - } - } - } - - $reqs = $this->get_by_id($relation['destination_id'],requirement_mgr::LATEST_VERSION); - if( !is_null($reqs) && count($reqs) > 0 ) - { - $destination_docid = $reqs[0]['req_doc_id']; - if ($check_for_req_project) - { - $tproject = $tproject_mgr->get_by_id($reqs[0]['testproject_id']); - if ($tproject['id'] != $tproject_id) - { - $destination_project = $tproject['name']; - } - } - - } - - if ( !is_null($source_docid) && !is_null($destination_docid) ) - { - $relation['source_doc_id'] = $source_docid; - $relation['destination_doc_id'] = $destination_docid; - - $info = array("||SOURCE||" => "source_doc_id","||DESTINATION||" => "destination_doc_id", - "||TYPE||" => "relation_type"); - - $elemTpl = "\t" . "" . "\n\t\t" . "||SOURCE||" ; - if (!is_null($source_project)) - { - $elemTpl .= "\n\t\t" . "||SRC_PROJECT||"; - $relation['source_project'] = $source_project; - $info["||SRC_PROJECT||"] = "source_project"; - } - - $elemTpl .= "\n\t\t" . "||DESTINATION||"; - if (!is_null($destination_project)) - { - $elemTpl .= "\n\t\t" . "||DST_PROJECT||"; - $relation['destination_project'] = $destination_project; - $info["||DST_PROJECT||"] = "destination_project"; - } - $elemTpl .= "\n\t\t" . "||TYPE||" . "\n\t" . "" . "\n"; - - $relations[] = $relation; - $xmlStr = exportDataToXML($relations,"{{XMLCODE}}",$elemTpl,$info,true); - } - } - - return $xmlStr; - } - - /** - * Converts a XML relation into a Map that represents relation. - * - * The XML should be in the following format: - * - * - * doc_id - * prj - * doc2_id - * prj2 - * 0 - * - * - * And here is an example of an output map of this function. - * - * [ - * 'source_doc_id' => 'doc_id', - * 'destination_doc_id' => 'doc2_id', - * 'type'=> 0, - * 'source_id' => 100, - * 'destination_id' => 101 - * ] - * - * The source_id and the destination_id are set here to null but are used in - * other parts of the system. When you add a relation to the database you - * have to provide the source_id and destination_id. - * - * @internal revisions - * 20120110 - frl - add project info if interproject_linking is set - * 20110314 - kinow - Created function. - */ - function convertRelationXmlToRelationMap($xml_item) - { - // Attention: following PHP Manual SimpleXML documentation, Please remember to cast - // before using data from $xml, - if( is_null($xml_item) ) - { - return null; - } - - $dummy=array(); - foreach($xml_item->attributes() as $key => $value) - { - $dummy[$key] = (string)$value; // See PHP Manual SimpleXML documentation. - } - - $dummy['source_doc_id'] = (string)$xml_item->source; - $dummy['destination_doc_id'] = (string)$xml_item->destination; - $dummy['type'] = (string)$xml_item->type; - $dummy['source_id'] = null; - $dummy['destination_id'] = null; - // FRL : interproject linking support - $dummy['source_tproject'] = property_exists($xml_item,'source_project') ? (string)$xml_item->source_project : null; - $dummy['destination_tproject'] = property_exists($xml_item,'destination_project') ? (string)$xml_item->destination_project : null; - - return $dummy; - } - - /** - * This function receives a relation XML node, converts it into a map and - * then adds this relation to database if it doesn't exist yet. - * - * @internal revisions - * 20110314 - kinow - Created function. - */ - function createRelationFromXML($xml,$tproject_id,$author_id) - { - $relationAsMap = $this->convertRelationXmlToRelationMap($xml); - $user_feedback = $this->createRelationFromMap($relationAsMap, $tproject_id, $author_id); - return $user_feedback; - } - - /** - * Adds a relation into database. Before adding it checks whether it - * exists or not. If it exists than the relation is not added, otherwise - * it is. - * - * Map structure - * source_doc_id => doc_id - * destination_doc_id => doc_id - * type => 10 - * source_id => 0 - * destination_id = 0 - * - * @internal revisions - * 20110314 - kinow - Created function. - */ - function createRelationFromMap($rel, $tproject_id, $authorId) - { - $status_ok = true; - - // get internal source id / destination id - $options = array('access_key' => 'req_doc_id', 'output' =>'minimun'); - $reqs = null; - $source_doc_id = $rel['source_doc_id']; - - // FRL : interproject linking support (look for req in defined project and req must be found - // in current project if interproject_linking is not set) - $reqs = $this->getByDocIDInProject($source_doc_id, $rel['source_tproject'], $tproject_id, null, $options); - $source = ( ! is_null($reqs) ) ? $reqs[$source_doc_id] : null; - if( !is_null($source) && ($this->relationsCfg->interProjectLinking || $source['testproject_id'] == $tproject_id) ) - { - $rel['source_id'] = $source['id']; - } - - $destination_doc_id = $rel['destination_doc_id']; - $reqs = $this->getByDocIDInProject($destination_doc_id, $rel['destination_tproject'], $tproject_id, null, $options); - $destination = ( ! is_null($reqs) ) ? $reqs[$destination_doc_id] : null; - if( !is_null($destination) && - ($this->relationsCfg->interProjectLinking || $destination['testproject_id'] == $tproject_id) ) - { - $rel['destination_id'] = $destination['id']; - } - - // 2 - check if relation is valid - $source_id = $rel['source_id']; - $destination_id = $rel['destination_id']; - $source_doc_id .= (is_null($rel['source_tproject']) ? '' : (' [' . $rel['source_tproject'] . ']')); - $destination_doc_id .= (is_null($rel['destination_tproject']) ? '' : (' [' . $rel['destination_tproject'] . ']')); - $rel_types_desc = config_get('req_cfg')->rel_type_description; - - // check if given type is a valid one for rel_type_description defined in config - $type_desc = array_key_exists(intval($rel['type']), $rel_types_desc) ? $rel_types_desc[intval($rel['type'])] : null; - $user_feedback = array('doc_id' => $source_doc_id . ' - ' . $destination_doc_id, - 'title' => lang_get('relation_type') . ' : ' . (is_null($type_desc) ? $rel['type'] : $type_desc)); - - if ( is_null($source_id ) ) - { - $user_feedback['import_status'] = lang_get('rel_add_error_src_id') ." [".$source_doc_id."]."; - } - else if ( is_null($destination_id ) ) - { - $user_feedback['import_status'] = lang_get('rel_add_error_dest_id') ." [".$destination_doc_id."]."; - } - else if ($source_id == $destination_id) - { - $user_feedback['import_status'] = lang_get('rel_add_error_self'); - } - else if ( ($source['testproject_id'] != $tproject_id) && ($destination['testproject_id'] != $tproject_id) ) - { - $user_feedback['import_status'] = lang_get('rel_add_not_in_project'); - } - else if (is_null($type_desc)) - { - $user_feedback['import_status'] = lang_get('rel_add_invalid_type'); - } - else if ($this->check_if_relation_exists($source_id, $destination_id, $rel['type'])) - { - $user_feedback['import_status'] = sprintf(lang_get('rel_add_error_exists_already'), $type_desc); - } - else // all checks are ok => create it - { - $this->add_relation($source_id, $destination_id, $rel['type'], $authorId); - $user_feedback['import_status'] = lang_get('new_rel_add_success'); - } - - return array ($user_feedback); - } - - /** - * This function retrieves a requirement by doc_id with a specifed project - * @param string $doc_id - * @param string $req_project name of req's project - * @param string $tproject_id used only if $req_project is null - * @param string $parent_id - * @param array $options (same as original $options getByDocID method) - * - * @internal revisions - * 20110314 - kinow - Created function. - */ - function getByDocIDInProject($doc_id, $req_project=null, $tproject_id=null,$parent_id=null, $options = null) - { - $reqs = null; - if ( !is_null($req_project) ) - { - $tproject_mgr = new testproject($this->db); - $info=$tproject_mgr->get_by_name($req_project); - if ( !is_null($info) ) // is project found ? - { - $tproject_id = $info[0]['id']; - $reqs = $this->getByDocID($doc_id, $tproject_id, $parent_id, $options); - } - //else $req = null; // project not found => no req - } - else - { - $reqs = $this->getByDocID($doc_id, $tproject_id, $parent_id, $options); - } - return $reqs; - } - - -/* - function: getByIDBulkLatestVersionRevision - - @used by reqOverView - - args: id: requirement id (can be an array) - [version_id]: requirement version id (can be an array) - [version_number]: - [options] - - - returns: null if query fails - map with requirement info - - @internal revisions - @since 1.9.12 - -*/ -function getByIDBulkLatestVersionRevision($id,$opt=null) -{ - static $debugMsg; - static $userCache; // key: user id, value: display name - static $lables; - static $user_keys; - - if(!$debugMsg) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $labels['undefined'] = lang_get('undefined'); - $user_keys = array('author' => 'author_id', 'modifier' => 'modifier_id'); - } - - $my['opt'] = array('outputFormat' => 'map'); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $in_clause = "IN (" . implode(",",(array)$id) . ") "; - $where_clause = " WHERE NH_REQV.parent_id " . $in_clause; - - // added -1 AS revision_id to make some process easier - $sql = " /* $debugMsg */ SELECT REQ.id,REQ.srs_id,REQ.req_doc_id," . - " REQV.scope,REQV.status,REQV.type,REQV.active," . - " REQV.is_open,REQV.author_id,REQV.version,REQV.id AS version_id," . - " REQV.expected_coverage,REQV.creation_ts,REQV.modifier_id," . - " REQV.modification_ts,REQV.revision, -1 AS revision_id, " . - " NH_REQ.name AS title, REQ_SPEC.testproject_id, " . - " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order " . - - " FROM {$this->tables['nodes_hierarchy']} NH_REQV JOIN " . - "( SELECT XNH_REQV.parent_id,MAX(XNH_REQV.id) AS LATEST_VERSION_ID " . - " FROM {$this->tables['nodes_hierarchy']} XNH_REQV " . - " WHERE XNH_REQV.parent_id {$in_clause} " . - " GROUP BY XNH_REQV.parent_id ) ZAZA ON NH_REQV.id = ZAZA.LATEST_VERSION_ID " . - - " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . - " JOIN {$this->object_table} REQ ON REQ.id = NH_REQV.parent_id " . - - " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . - $where_clause; - - $sqlOpt = ($my['opt']['outputFormat'] == 'map' ? 0 : database::CUMULATIVE); - $recordset = $this->db->fetchRowsIntoMap($sql,'id',$sqlOpt); - - - $rs = null; - - if(!is_null($recordset)) { - // Decode users - $rs = $recordset; - - // try to guess output structure - $x = array_keys(current($rs)); - if( is_int($x[0]) ) - { - // output[REQID][0] = array('id' =>, 'xx' => ...) - $flevel = array_keys($recordset); - foreach($flevel as $flk) - { - $key2loop = array_keys($recordset[$flk]); - foreach( $key2loop as $key ) - { - foreach( $user_keys as $ukey => $userid_field) - { - $rs[$flk][$key][$ukey] = ''; - if(trim($rs[$flk][$key][$userid_field]) != "") - { - if( !isset($userCache[$rs[$flk][$key][$userid_field]]) ) - { - $user = tlUser::getByID($this->db,$rs[$flk][$key][$userid_field]); - $rs[$flk][$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; - $userCache[$rs[$flk][$key][$userid_field]] = $rs[$flk][$key][$ukey]; - unset($user); - } - else - { - $rs[$flk][$key][$ukey] = $userCache[$rs[$flk][$key][$userid_field]]; - } - } - } - } - } - } - else - { - // output[REQID] = array('id' =>, 'xx' => ...) - $key2loop = array_keys($recordset); - foreach( $key2loop as $key ) - { - foreach( $user_keys as $ukey => $userid_field) - { - $rs[$key][$ukey] = ''; - if(trim($rs[$key][$userid_field]) != "") - { - if( !isset($userCache[$rs[$key][$userid_field]]) ) - { - $user = tlUser::getByID($this->db,$rs[$key][$userid_field]); - $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; - $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; - unset($user); - } - else - { - $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; - } - } - } - } - } - - } - - unset($recordset); - unset($my); - unset($dummy); - - return $rs; -} - -/** - * - * @internal revisions - * @since 1.9.12 - */ -function getCoverageCounter($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe_id = intval($id); - $sql = "/* $debugMsg */ " . - " SELECT COUNT(0) AS qty " . - " FROM {$this->tables['req_coverage']} " . - " WHERE req_id = " . $safe_id; - - - $rs = $this->db->get_recordset($sql); - return $rs[0]['qty']; -} - - - /** - * - * @internal revisions - * @since 1.9.12 - */ - function getCoverageCounterSet($itemSet) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT req_id, COUNT(0) AS qty " . - " FROM {$this->tables['req_coverage']} " . - " WHERE req_id IN (" . implode(',', $itemSet) . ")" . - " GROUP BY req_id "; - - $rs = $this->db->fetchRowsIntoMap($sql,'req_id'); - return $rs; - } - - - /** - * - * @internal revisions - * @since 1.9.12 - */ - public function getRelationsCounters($itemSet) { - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $inSet = implode(',',$itemSet); - - $sqlS = " $debugMsg SELECT COUNT(*) AS qty, source_id AS req_id " . - " FROM {$this->tables['req_relations']} " . - " WHERE source_id IN ({$inSet}) "; - $sqlS .= (DB_TYPE == 'mssql') ? ' GROUP BY source_id ' : ' GROUP BY req_id '; - - $sqlD = " $debugMsg SELECT COUNT(*) AS qty, destination_id AS req_id " . - " FROM {$this->tables['req_relations']} " . - " WHERE destination_id IN ({$inSet}) "; - $sqlD .= (DB_TYPE == 'mssql') ? ' GROUP BY destination_id ' : ' GROUP BY req_id '; - - $sqlT = " SELECT SUM(qty) AS qty, req_id " . - " FROM ($sqlS UNION ALL $sqlD) D ". - ' GROUP BY req_id '; - - $rs = $this->db->fetchColumnsIntoMap($sqlT,'req_id','qty'); - return $rs; - } - - - /** - * - * - */ - function updateScope($reqVersionID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ UPDATE {$this->tables['req_versions']} " . - " SET scope='" . $this->db->prepare_string($scope) . "'" . - " WHERE id=" . intval($reqVersionID); - $this->db->exec_query($sql); - } - - - /** - * render Image Attachments INLINE - * - */ - function renderImageAttachments($id,&$item2render,$basehref=null) { - static $attSet; - static $targetTag; - - $version_id = intval($item2render['version_id']); - if(!$attSet || !isset($attSet[$id])) { - $attSet[$id] = $this->attachmentRepository->getAttachmentInfosFor($version_id,$this->attachmentTableName,'id'); - $beginTag = '[tlInlineImage]'; - $endTag = '[/tlInlineImage]'; - } - - if(is_null($attSet[$id])) { - return; - } - - // $href = '%s:%s' . " $versionTag (link)

    "; - // second \'%s\' needed if I want to use Latest as indication, need to understand - // Javascript instead of javascript, because CKeditor sometimes complains - $bhref = is_null($basehref) ? $_SESSION['basehref'] : $basehref; - $img = '

    '; - - $key2check = array('scope'); - $rse = &$item2render; - foreach($key2check as $item_key) { - $start = strpos($rse[$item_key],$beginTag); - $ghost = $rse[$item_key]; - - // There is at least one request to replace ? - if($start !== FALSE) { - $xx = explode($beginTag,$rse[$item_key]); - - // How many requests to replace ? - $xx2do = count($xx); - $ghost = ''; - for($xdx=0; $xdx < $xx2do; $xdx++) { - // Hope was not a false request. - if( strpos($xx[$xdx],$endTag) !== FALSE) { - // Separate command string from other text - // Theorically can be just ONE, but it depends - // is user had not messed things. - $yy = explode($endTag,$xx[$xdx]); - if( ($elc = count($yy)) > 0) { - $atx = $yy[0]; - try { - if(isset($attSet[$id][$atx]) && $attSet[$id][$atx]['is_image']) { - $ghost .= str_replace('%id%',$atx,$img); - } - $lim = $elc-1; - for($cpx=1; $cpx <= $lim; $cpx++) { - $ghost .= $yy[$cpx]; - } - } catch (Exception $e) { - $ghost .= $rse[$item_key]; - } - } - } else { - // nothing to do - $ghost .= $xx[$xdx]; - } - } - } - - // reconstruct field contents - if($ghost != '') { - $rse[$item_key] = $ghost; - } - } - } - - - - /** - * scope is managed at revision and version level - * @since 1.9.13 - */ - function inlineImageProcessing($idCard,$scope,$rosettaStone) { - // get all attachments, then check is there are images - $att = $this->attachmentRepository->getAttachmentInfosFor($idCard->id,$this->attachmentTableName,'id'); - foreach($rosettaStone as $oid => $nid) - { - if($att[$nid]['is_image']) - { - $needle = str_replace($nid,$oid,$att[$nid]['inlineString']); - $inlineImg[] = array('needle' => $needle, 'rep' => $att[$nid]['inlineString']); - } - } - - if( !is_null($inlineImg) ) - { - $dex = $scope; - foreach($inlineImg as $elem) - { - $dex = str_replace($elem['needle'],$elem['rep'],$dex); - } - $this->updateScope($idCard->versionID,$dex); - } - } - - /** - * - */ - function monitorOn($req_id,$user_id,$tproject_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // simple checks - // key = column name!! - $safe = array(); - $safe['req_id'] = intval($req_id); - $safe['user_id'] = intval($user_id); - $safe['testproject_id'] = intval($tproject_id); - - $fields = implode(',',array_keys($safe)); - - foreach($safe as $key => $val) - { - if( $val <= 0 ) - { - throw new Exception("$key invalid value", 1); - } - } - - try - { - // check before insert - $sql = "/* $debugMsg */ " . - " SELECT req_id FROM {$this->tables['req_monitor']} " . - " WHERE req_id = {$safe['req_id']} " . - " AND user_id = {$safe['user_id']} " . - " AND testproject_id = {$safe['testproject_id']}"; - $rs = $this->db->get_recordset($sql); - - if( is_null($rs) ) - { - $sql = "/* $debugMsg */ " . - " INSERT INTO {$this->tables['req_monitor']} ($fields) " . - " VALUES ({$safe['req_id']},{$safe['user_id']},{$safe['testproject_id']})"; - $this->db->exec_query($sql); - } - } - catch (Exception $e) - { - echo $e->getMessage(); - } - } - - /** - * - */ - function monitorOff($req_id,$user_id=null,$tproject_id=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // simple checks - $safe = array(); - $safe['req_id'] = intval($req_id); - $safe['user_id'] = intval($user_id); - $safe['tproject_id'] = intval($tproject_id); - - $key2check = array('req_id'); - foreach($key2check as $key) - { - $val = $safe[$key]; - if( $val <= 0 ) - { - throw new Exception("$key invalid value", 1); - } - } - - // Blind delete - try - { - $sql = "/* $debugMsg */ " . - " DELETE FROM {$this->tables['req_monitor']} " . - " WHERE req_id = {$safe['req_id']} "; - - if($safe['user_id'] >0) - { - $sql .= " AND user_id = {$safe['user_id']} "; - } - - if($safe['tproject_id'] >0) - { - $sql .= " AND testproject_id = {$safe['tproject_id']}"; - } - $this->db->exec_query($sql); - } - catch (Exception $e) - { - echo $e->getMessage(); - } - } - - /** - * - */ - function getMonitoredByUser($user_id,$tproject_id,$opt=null,$filters=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('reqSpecID' => null); - $my['filters'] = array(); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - $my['filters'] = array_merge($my['opt'],(array)$filters); - - // simple checks - $safe = array(); - $safe['user_id'] = intval($user_id); - $safe['tproject_id'] = intval($tproject_id); - - foreach($safe as $key => $val) - { - if( $val <= 0 ) - { - throw new Exception("$key invalid value", 1); - } - } - - $rs = null; - - if( is_null($my['opt']['reqSpecID']) ) - { - $sql = "/* $debugMsg */ " . - " SELECT RQM.* FROM {$this->tables['req_monitor']} RQM " . - " WHERE RQM.user_id = {$safe['user_id']} " . - " AND RQM.testproject_id = {$safe['tproject_id']}"; - } - else - { - $sql = "/* $debugMsg */ " . - " SELECT RQM.* FROM {$this->tables['req_monitor']} RQM " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ " . - " ON NH_REQ.id = RQM.req_id " . - " WHERE RQM.user_id = {$safe['user_id']} " . - " AND RQM.testproject_id = {$safe['tproject_id']} " . - " AND NH_REQ.parent_id = " . intval($my['opt']['reqSpecID']); - } - - try - { - $rs = $this->db->fetchRowsIntoMap($sql,'req_id'); - } - catch (Exception $e) - { - echo $e->getMessage(); - } - - - - - return $rs; - } - - /** - * - * - */ - function getReqMonitors($req_id,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $options = array('tproject_id' => 0, 'output' => 'map'); - $options = array_merge($options,(array)$opt); - - // simple checks - $safe = array(); - $safe['req_id'] = intval($req_id); - $safe['tproject_id'] = intval($options['tproject_id']); - - $sql = "/* $debugMsg */ " . - " SELECT RMON.user_id,U.login,U.email,U.locale " . - " FROM {$this->tables['req_monitor']} RMON " . - " JOIN {$this->tables['users']} U " . - " ON U.id = RMON.user_id ". - " WHERE req_id = {$safe['req_id']} "; - - if($safe['tproject_id'] > 0) - { - $sql .= " AND testproject_id = {$safe['tproject_id']}"; - } - - switch($options['output']) - { - case 'array': - $rs = $this->db->get_recordset($sql); - break; - - case 'map': - default: - $rs = $this->db->fetchRowsIntoMap($sql,'user_id'); - break; - } - return $rs; - } - - /** - * - */ - function notifyMonitors($req_id,$action,$user_id,$log_msg=null) - { - static $user; - $mailBodyCache = ''; - $mailSubjectCache = ''; - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe = array(); - $safe['req_id'] = intval($req_id); - - // who is monitoring? - $iuSet = $this->getReqMonitors($safe['req_id']); - if( is_null($iuSet) ) - { - return; - } - - if( !$user ) - { - $user = new tlUser($this->db); - } - - $author = $user->getNames($this->db,$user_id); - $author = $author[$user_id]; - $idCard = $author['login'] . - " ({$author['first']} {$author['last']})"; - - // use specific query because made things simpler - $sql = "/* $debugMsg */ " . - " SELECT REQ.id,REQ.req_doc_id,REQV.scope," . - " NH_REQ.name AS title, REQV.version " . - " FROM {$this->object_table} REQ " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id ". - " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . - " WHERE REQ.id = {$safe['req_id']} " . - " ORDER BY REQV.version DESC "; - - if( !is_null($rs = $this->db->get_recordset($sql)) ) - { - $req = $rs[0]; - } - - $mailCfg = $this->getMonitorMailCfg($action); - - $from = config_get('from_email'); - $trf = array("%docid%" => $req['req_doc_id'], - "%title%" => $req['title'], - "%scope%" => $req['scope'], - "%user%" => $idCard, - "%logmsg%" => $log_msg, - "%version%" => $req['version'], - "%timestamp%" => date("D M j G:i:s T Y")); - $body['target'] = array_keys($trf); - $body['values'] = array_values($trf); - $subj['target'] = array_keys($trf); - $subj['values'] = array_values($trf); - - foreach($iuSet as $ue) - { - if( !isset($mailBodyCache[$ue['locale']]) ) - { - $lang = $ue['locale']; - $mailBodyCache[$lang] = mailBodyGet($mailCfg['bodyAccessKey']); - - // set values - $mailBodyCache[$lang] = - str_replace($body['target'],$body['values'],$mailBodyCache[$lang]); - - $mailSubjectCache[$lang] = - lang_get($mailCfg['subjectAccessKey'],$lang); - - $mailSubjectCache[$lang] = - str_replace($subj['target'],$subj['values'],$mailSubjectCache[$lang]); - - } - - // send mail - $auditMsg = 'Requirement - ' . $action . ' - mail to user: ' . $ue["login"] . - ' using address:' . $ue["email"]; - try - { - $xmail = array(); - $xmail['cc'] = ''; - $xmail['attachment'] = null; - $xmail['exit_on_error'] = false; - $xmail['htmlFormat'] = true; - - - $rmx = @email_send($from,$ue["email"], - $mailSubjectCache[$ue['locale']],$mailBodyCache[$ue['locale']], - $xmail['cc'],$xmail['attachment'],$xmail['exit_on_error'], - $xmail['htmlFormat'],null); - $apx = $rmx->status_ok ? 'Succesful - ' : 'ERROR -'; - } - catch (Exception $e) - { - $apx = 'ERROR - '; - } - $auditMsg = $apx . $auditMsg; - logAuditEvent($auditMsg); - } - } - - /** - * - */ - function getMonitorMailCfg($action) { - $cfg = null; - switch( $action ) { - case 'create_new_version': - $cfg['subjectAccessKey'] = 'mail_subject_req_new_version'; - $cfg['bodyAccessKey'] = 'requirements/req_create_new_version.txt'; - break; - - case 'delete': - $cfg['subjectAccessKey'] = 'mail_subject_req_delete'; - $cfg['bodyAccessKey'] = 'requirements/req_delete.txt'; - break; - - case 'delete_version': - $cfg['subjectAccessKey'] = 'mail_subject_req_delete_version'; - $cfg['bodyAccessKey'] = 'requirements/req_delete_version.txt'; - break; - } - return $cfg; - } - - /** - * - */ - function setNotifyOn($cfg) - { - foreach($cfg as $key => $val) - { - $this->notifyOn[$key] = $val; - } - } - - /** - * - */ - function getNotifyOn($key=null) { - if( !is_null($key) && isset($this->notifyOn['key']) ) { - return $this->notifyOn['key']; - } - return $this->notifyOn; - } - - /** - * - */ - function updateCoverage($link,$whoWhen,$opt=null) { - - // Set coverage for previous version to FROZEN & INACTIVE ? - // Create coverage for NEW Version - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $options = array('freezePrevious' => true); - $options = array_merge($options,(array)$opt); - - $safeF = intval($link['source']); - $safeT = intval($link['dest']); - - // Set coverage for previous version to FROZEN & INACTIVE ? - if( $options['freezePrevious'] ) { - $sql = " /* $debugMsg */ " . - " UPDATE {$this->tables['req_coverage']} " . - " SET link_status = " . LINK_TC_REQ_CLOSED_BY_NEW_REQVERSION . "," . - " is_active=0 " . - " WHERE req_version_id=" . $safeF; - $this->db->exec_query($sql); - } - - // Create coverage for NEW Version - $sql = "/* $debugMsg */ " . - " INSERT INTO {$this->tables['req_coverage']} " . - " (testcase_id,tcversion_id,req_id," . - " req_version_id,author_id,creation_ts) " . - - " SELECT testcase_id,tcversion_id,req_id, " . - " {$safeT} AS req_version_id," . - " {$whoWhen['user_id']} AS author_id, " . - " {$whoWhen['when']} AS creation_ts" . - " FROM {$this->tables['req_coverage']} " . - " WHERE req_version_id=" . $safeF; - $this->db->exec_query($sql); - - } - - - /** - * - */ - function updateTCVLinkStatus($from_version_id,$reason) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safeF = intval($from_version_id); - - $sql = " /* $debugMsg */ " . - " UPDATE {$this->tables['req_coverage']} " . - " SET link_status = " . $reason . "," . - " is_active=0 " . - " WHERE req_version_id=" . $safeF; - $this->db->exec_query($sql); - - } - - - /** - * - */ - function getAllReqVersionIDForReq( $idSet ) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $idList = implode(",", (array)$idSet); - $sql = " /* $debugMsg */ - SELECT REQ.id AS req_id, NHREQVER.id AS req_version_id - FROM {$this->object_table} REQ - JOIN {$this->tables['nodes_hierarchy']} NHREQVER - ON NHREQVER.parent_id = REQ.id "; - $sql .= " WHERE REQ.id IN ($idList)"; - - return $this->db->fetchColumnsIntoMap( - $sql,'req_id','req_version_id',database::CUMULATIVE); - - - } - - - /** - * - */ - function getActiveForTCVersion($tcversion_id, $srs_id = 'all') { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ " . - " SELECT REQ.id,REQ.id AS req_id,REQ.req_doc_id,NHREQ.name AS title, RCOV.is_active," . - " NHRS.name AS req_spec_title,RCOV.testcase_id," . - " REQV.id AS req_version_id, REQV.version " . - " FROM {$this->object_table} REQ " . - " JOIN {$this->tables['req_specs']} RSPEC " . - " ON REQ.srs_id = RSPEC.id " . - " JOIN {$this->tables['req_coverage']} RCOV " . - " ON RCOV.req_id = REQ.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHRS " . - " ON NHRS.id=RSPEC.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHREQ " . - " ON NHREQ.id=REQ.id " . - " JOIN {$this->tables['req_versions']} REQV " . - " ON RCOV.req_version_id=REQV.id "; - - $idList = implode(",",(array)$tcversion_id); - - $sql .= " WHERE RCOV.tcversion_id IN (" . $idList . ")" . - " AND RCOV.is_active=1 "; - - // if only for one specification is required - if ($srs_id != 'all') { - $sql .= " AND REQ.srs_id=" . intval($srs_id); - } - - if ( is_array($tcversion_id) ) { - return $this->db->fetchRowsIntoMap($sql,'tcversion_id',true); - } - else { - return $this->db->get_recordset($sql); - } - } - - /** - * what is meaning of Good? - * - */ - function getGoodForTCVersion($tcversion_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ " . - " SELECT REQ.id,REQ.id AS req_id,REQ.req_doc_id, + REQRV.active," . + " REQRV.is_open,REQRV.author_id,REQV.version,REQRV.parent_id AS version_id," . + " REQRV.expected_coverage,REQRV.creation_ts,REQRV.modifier_id," . + " REQRV.modification_ts,REQRV.revision, REQRV.id AS revision_id," . + " NH_REQ.name AS title, REQ_SPEC.testproject_id, " . + " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order " . + " FROM {$this->tables['req_revisions']} REQRV " . + " JOIN {$this->tables['req_versions']} REQV ON REQV.id = REQRV.parent_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQRV.parent_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . + " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . + " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . + " WHERE REQRV.id = " . intval($revision_id); + $dummy = $this->db->get_recordset($sql); + + if (! is_null($dummy)) { + $this->decode_users($dummy); + $dummy = $dummy[0]; + } + + if (! is_null($dummy) && $my['opt']['renderImageInline']) { + $this->renderImageAttachments($dummy['id'], $dummy); + } + + return $dummy; + } + + /** + * get info regarding a req version, using also revision as access criteria. + * + * @int version_id + * @array revision_access possible keys 'id', 'number' + * + * @uses print.inc.php + * @uses renderReqForPrinting() + * + * + */ + public function get_version_revision($version_id, $revision_access, + $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['opt'] = array( + 'renderImageInline' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = "/* $debugMsg */"; + + if (isset($revision_access['number'])) { + $rev_number = intval($revision_access['number']); + + // we have to tables to search on + // Req Versions -> holds LATEST revision + // Req Revisions -> holds other revisions + $sql .= " SELECT NH_REQV.parent_id AS req_id, REQV.id AS version_id, REQV.version," . + " REQV.creation_ts, REQV.author_id, " . + " REQV.modification_ts, REQV.modifier_id, " . + self::NO_REVISION . " AS revision_id, " . + " REQV.revision, REQV.scope, " . + " REQV.status,REQV.type,REQV.expected_coverage,NH_REQ.name, REQ.req_doc_id, " . + " COALESCE(REQV.log_message,'') AS log_message, NH_REQ.name AS title " . + " FROM {$this->tables['req_versions']} REQV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . + " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . + " WHERE NH_REQV.id = {$version_id} AND REQV.revision = {$rev_number} "; + + $sql .= " UNION ALL ( " . + " SELECT NH_REQV.parent_id AS req_id, REQV.id AS version_id, REQV.version, " . + " REQRV.creation_ts, REQRV.author_id, " . + " REQRV.modification_ts, REQRV.modifier_id, " . + " REQRV.id AS revision_id, " . + " REQRV.revision,REQRV.scope,REQRV.status,REQRV.type, " . + " REQRV.expected_coverage,REQRV.name,REQRV.req_doc_id, " . + " COALESCE(REQRV.log_message,'') as log_message, NH_REQ.name AS title " . + " FROM {$this->tables['req_versions']} REQV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . + " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . + " JOIN {$this->tables['req_revisions']} REQRV " . + " ON REQRV.parent_id = REQV.id " . + " WHERE NH_REQV.id = {$version_id} AND REQRV.revision = {$rev_number} ) "; + } else { + // revision_id is present ONLY on req revisions table, then we do not need UNION + $sql .= " SELECT NH_REQV.parent_id AS req_id, REQV.id AS version_id, REQV.version, " . + " REQRV.creation_ts, REQRV.author_id, " . + " REQRV.modification_ts, REQRV.modifier_id, " . + " REQRV.id AS revision_id, " . + " REQRV.revision,REQRV.scope,REQRV.status,REQRV.type, " . + " REQRV.expected_coverage,REQRV.name,REQRV.req_doc_id, " . + " COALESCE(REQRV.log_message,'') as log_message, NH_REQ.name AS title " . + " FROM {$this->tables['req_versions']} REQV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.id = REQV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = NH_REQV.parent_id " . + " JOIN {$this->tables['requirements']} REQ ON REQ.id = NH_REQ.id " . + " JOIN {$this->tables['req_revisions']} REQRV " . + " ON REQRV.parent_id = REQV.id " . + " WHERE NH_REQV.id = {$version_id} AND REQRV.revision_id = " . + intval($revision_access['id']); + } + + $rs = $this->db->get_recordset($sql); + + if (! is_null($rs) && $my['opt']['renderImageInline']) { + $k2l = array_keys($rs); + foreach ($k2l as $akx) { + $this->renderImageAttachments($rs[$akx]['req_id'], $rs[$akx]); + } + reset($rs); + } + return $rs; + } + + /** + */ + private function decode_users(&$rs) + { + $userCache = null; // key: user id, value: display name + $key2loop = array_keys($rs); + $labels['undefined'] = lang_get('undefined'); + $user_keys = array( + 'author' => 'author_id', + 'modifier' => 'modifier_id' + ); + foreach ($key2loop as $key) { + foreach ($user_keys as $ukey => $userid_field) { + $rs[$key][$ukey] = ''; + if (trim($rs[$key][$userid_field]) != "") { + if (! isset($userCache[$rs[$key][$userid_field]])) { + $user = tlUser::getByID($this->db, + $rs[$key][$userid_field]); + $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; + $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; + } else { + $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; + } + } + } + } + } + + /** + */ + private function generateUniqueTitle($title2check, $parent_id, $tproject_id) + { + static $fieldSize; + static $getOptions; + static $reqCfg; + static $mask; + static $title_max_len; + + if (! $fieldSize) { + $fieldSize = config_get('field_size'); + $reqCfg = config_get('req_cfg'); + + $mask = $reqCfg->duplicated_name_algorithm->text; + $title_max_len = $fieldSize->requirement_title; + + $getOptions = array( + 'output' => 'minimun', + 'check_criteria' => 'likeLeft' + ); + } + + $generated = $title2check; + $attr = array( + 'key' => 'title', + 'value' => $title2check + ); + + // search need to be done in like to the left + $itemSet = $this->getByAttribute($attr, $tproject_id, $parent_id, + $getOptions); + + // we borrow logic (may be one day we can put it on a central place) from + // testcase class create_tcase_only() + if (! is_null($itemSet) && ($siblingQty = count($itemSet)) > 0) { + $nameSet = array_flip(array_keys($itemSet)); + $target = $title2check . ($suffix = sprintf($mask, ++ $siblingQty)); + $final_len = strlen($target); + if ($final_len > $title_max_len) { + $target = substr($target, strlen($suffix), $title_max_len); + } + // Need to recheck if new generated name does not crash with existent name + // why? Suppose you have created: + // REQ [1] + // REQ [2] + // REQ [3] + // Then you delete REQ [2]. + // When I got siblings il will got 2 siblings, if I create new progressive using next, + // it will be 3 => I will get duplicated name. + while (isset($nameSet[$target])) { + $target = $title2check . + ($suffix = sprintf($mask, ++ $siblingQty)); + $final_len = strlen($target); + if ($final_len > $title_max_len) { + $target = substr($target, strlen($suffix), $title_max_len); + } + } + $generated = $target; + } + + return $generated; + } + + /** + */ + public function getTestProjectID($id, $reqSpecID = null) + { + $reqSpecMgr = new requirement_spec_mgr($this->db); + $parent = $reqSpecID; + if (is_null($parent)) { + $dummy = $this->tree_mgr->get_node_hierarchy_info($id); + $parent = $dummy['parent_id']; + } + $target = $reqSpecMgr->get_by_id($parent); + return $target['testproject_id']; + } + + /** + * + * @param array $context + * with following keys + * tproject_id => REQUIRED + * tplan_id => OPTIONAL + * platform_id => OPTIONAL, will be used ONLY if tplan_id is provided. + * @param unknown $opt + * @return array + */ + public function getAllByContext($context, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + if (! isset($context['tproject_id'])) { + throw new Exception( + $debugMsg . ' : $context[\'tproject_id\'] is needed'); + } + + $where = "WHERE RSPEC.testproject_id = " . + intval($context['tproject_id']); + $sql = "/* $debugMsg */ " . + "SELECT DISTINCT REQ.id,REQ.req_doc_id FROM {$this->tables['requirements']} REQ " . + "JOIN {$this->tables['req_specs']} RSPEC ON RSPEC.id = REQ.srs_id "; + + if (isset($context['tplan_id'])) { + + $sql .= "JOIN {$this->tables['req_coverage']} REQCOV ON REQCOV.req_id = REQ.id " . + "JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = REQCOV.testcase_id " . + "JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NH_TCV.id "; + + $where .= " AND TPTCV.testplan_id = " . intval($context['tplan_id']); + if (isset($context['platform_id']) && + intval($context['platform_id']) > 0) { + $where .= " AND TPTCV.platform_id = " . + intval($context['platform_id']); + } + } + + $sql .= $where; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + * + * @used-by + */ + public function getFileUploadRelativeURL($req_id, $req_version_id) + { + $sfReqID = intval($req_id); + $sfVersion = intval($req_version_id); + + return "lib/requirements/reqEdit.php" . + "?doAction=fileUpload&requirement_id=" . $sfReqID . "&req_id=" . + $sfReqID . "&req_version_id=" . $sfVersion; + } + + /** + * + * @used-by + */ + public function getDeleteAttachmentRelativeURL($req_id, $req_version_id) + { + return "lib/requirements/reqEdit.php?doAction=deleteFile" . + "&requirement_id=" . intval($req_id) . "&req_version_id=" . + intval($req_version_id) . "&file_id="; + } + + /** + * exportRelationToXML + * + * Function to export a requirement relation to XML. + * + * @param int $relation + * relation data array + * @param string $troject_id + * @param + * boolean check_for_req_project + * + * @return string with XML code + * + * doc_id + * prj + * doc2_id + * prj2 + * 0 + * + * + * @internal revisions + * + */ + public function exportRelationToXML($relation, $tproject_id = null, + $check_for_req_project = false) + { + $xmlStr = ''; + $source_docid = null; + $destination_docid = null; + $source_project = null; + $destination_project = null; + + if (! is_null($relation)) { + // FRL : interproject linking support + $tproject_mgr = new testproject($this->db); + $reqs = $this->get_by_id($relation['source_id'], + requirement_mgr::LATEST_VERSION); + if (! empty($reqs)) { + $source_docid = $reqs[0]['req_doc_id']; + if ($check_for_req_project) { + $tproject = $tproject_mgr->get_by_id( + $reqs[0]['testproject_id']); + if ($tproject['id'] != $tproject_id) { + $source_project = $tproject['name']; + } + } + } + + $reqs = $this->get_by_id($relation['destination_id'], + requirement_mgr::LATEST_VERSION); + if (! empty($reqs)) { + $destination_docid = $reqs[0]['req_doc_id']; + if ($check_for_req_project) { + $tproject = $tproject_mgr->get_by_id( + $reqs[0]['testproject_id']); + if ($tproject['id'] != $tproject_id) { + $destination_project = $tproject['name']; + } + } + } + + if (! is_null($source_docid) && ! is_null($destination_docid)) { + $relation['source_doc_id'] = $source_docid; + $relation['destination_doc_id'] = $destination_docid; + + $info = array( + "||SOURCE||" => "source_doc_id", + "||DESTINATION||" => "destination_doc_id", + "||TYPE||" => "relation_type" + ); + + $elemTpl = "\t" . "" . "\n\t\t" . + "||SOURCE||"; + if (! is_null($source_project)) { + $elemTpl .= "\n\t\t" . + "||SRC_PROJECT||"; + $relation['source_project'] = $source_project; + $info["||SRC_PROJECT||"] = "source_project"; + } + + $elemTpl .= "\n\t\t" . + "||DESTINATION||"; + if (! is_null($destination_project)) { + $elemTpl .= "\n\t\t" . + "||DST_PROJECT||"; + $relation['destination_project'] = $destination_project; + $info["||DST_PROJECT||"] = "destination_project"; + } + $elemTpl .= "\n\t\t" . "||TYPE||" . "\n\t" . + "" . "\n"; + + $relations[] = $relation; + $xmlStr = exportDataToXML($relations, "{{XMLCODE}}", $elemTpl, + $info, true); + } + } + + return $xmlStr; + } + + /** + * Converts a XML relation into a Map that represents relation. + * + * The XML should be in the following format: + * + * + * doc_id + * prj + * doc2_id + * prj2 + * 0 + * + * + * And here is an example of an output map of this function. + * + * [ + * 'source_doc_id' => 'doc_id', + * 'destination_doc_id' => 'doc2_id', + * 'type'=> 0, + * 'source_id' => 100, + * 'destination_id' => 101 + * ] + * + * The source_id and the destination_id are set here to null but are used in + * other parts of the system. When you add a relation to the database you + * have to provide the source_id and destination_id. + * + * @internal revisions + * 20120110 - frl - add project info if interproject_linking is set + * 20110314 - kinow - Created function. + */ + public function convertRelationXmlToRelationMap($xml_item) + { + // Attention: following PHP Manual SimpleXML documentation, Please remember to cast + // before using data from $xml, + if (is_null($xml_item)) { + return null; + } + + $dummy = array(); + foreach ($xml_item->attributes() as $key => $value) { + $dummy[$key] = (string) $value; // See PHP Manual SimpleXML documentation. + } + + $dummy['source_doc_id'] = (string) $xml_item->source; + $dummy['destination_doc_id'] = (string) $xml_item->destination; + $dummy['type'] = (string) $xml_item->type; + $dummy['source_id'] = null; + $dummy['destination_id'] = null; + // FRL : interproject linking support + $dummy['source_tproject'] = property_exists($xml_item, 'source_project') ? (string) $xml_item->source_project : null; + $dummy['destination_tproject'] = property_exists($xml_item, + 'destination_project') ? (string) $xml_item->destination_project : null; + + return $dummy; + } + + /** + * This function receives a relation XML node, converts it into a map and + * then adds this relation to database if it doesn't exist yet. + * + * @internal revisions + * 20110314 - kinow - Created function. + * @todo delete the unused function if necessary + */ + private function createRelationFromXML($xml, $tproject_id, $author_id) + { + $relationAsMap = $this->convertRelationXmlToRelationMap($xml); + return $this->createRelationFromMap($relationAsMap, $tproject_id, + $author_id); + } + + /** + * Adds a relation into database. + * Before adding it checks whether it + * exists or not. If it exists than the relation is not added, otherwise + * it is. + * + * Map structure + * source_doc_id => doc_id + * destination_doc_id => doc_id + * type => 10 + * source_id => 0 + * destination_id = 0 + * + * @internal revisions + * 20110314 - kinow - Created function. + */ + public function createRelationFromMap($rel, $tproject_id, $authorId) + { + // get internal source id / destination id + $options = array( + 'access_key' => 'req_doc_id', + 'output' => 'minimun' + ); + $reqs = null; + $source_doc_id = $rel['source_doc_id']; + + // FRL : interproject linking support (look for req in defined project and req must be found + // in current project if interproject_linking is not set) + $reqs = $this->getByDocIDInProject($source_doc_id, + $rel['source_tproject'], $tproject_id, null, $options); + $source = (! is_null($reqs)) ? $reqs[$source_doc_id] : null; + if (! is_null($source) && + ($this->relationsCfg->interProjectLinking || + $source['testproject_id'] == $tproject_id)) { + $rel['source_id'] = $source['id']; + } + + $destination_doc_id = $rel['destination_doc_id']; + $reqs = $this->getByDocIDInProject($destination_doc_id, + $rel['destination_tproject'], $tproject_id, null, $options); + $destination = (! is_null($reqs)) ? $reqs[$destination_doc_id] : null; + if (! is_null($destination) && + ($this->relationsCfg->interProjectLinking || + $destination['testproject_id'] == $tproject_id)) { + $rel['destination_id'] = $destination['id']; + } + + // 2 - check if relation is valid + $source_id = $rel['source_id']; + $destination_id = $rel['destination_id']; + $source_doc_id .= (is_null($rel['source_tproject']) ? '' : (' [' . + $rel['source_tproject'] . ']')); + $destination_doc_id .= (is_null($rel['destination_tproject']) ? '' : (' [' . + $rel['destination_tproject'] . ']')); + $rel_types_desc = config_get('req_cfg')->rel_type_description; + + // check if given type is a valid one for rel_type_description defined in config + $type_desc = array_key_exists(intval($rel['type']), $rel_types_desc) ? $rel_types_desc[intval( + $rel['type'])] : null; + $user_feedback = array( + 'doc_id' => $source_doc_id . ' - ' . $destination_doc_id, + 'title' => lang_get('relation_type') . ' : ' . + (is_null($type_desc) ? $rel['type'] : $type_desc) + ); + + if (is_null($source_id)) { + $user_feedback['import_status'] = lang_get('rel_add_error_src_id') . + " [" . $source_doc_id . "]."; + } elseif (is_null($destination_id)) { + $user_feedback['import_status'] = lang_get('rel_add_error_dest_id') . + " [" . $destination_doc_id . "]."; + } elseif ($source_id == $destination_id) { + $user_feedback['import_status'] = lang_get('rel_add_error_self'); + } elseif (($source['testproject_id'] != $tproject_id) && + ($destination['testproject_id'] != $tproject_id)) { + $user_feedback['import_status'] = lang_get('rel_add_not_in_project'); + } elseif (is_null($type_desc)) { + $user_feedback['import_status'] = lang_get('rel_add_invalid_type'); + } elseif ($this->check_if_relation_exists($source_id, $destination_id, + $rel['type'])) { + $user_feedback['import_status'] = sprintf( + lang_get('rel_add_error_exists_already'), $type_desc); + } else // all checks are ok => create it + { + $this->add_relation($source_id, $destination_id, $rel['type'], + $authorId); + $user_feedback['import_status'] = lang_get('new_rel_add_success'); + } + + return array( + $user_feedback + ); + } + + /** + * This function retrieves a requirement by doc_id with a specifed project + * + * @param string $doc_id + * @param string $req_project + * name of req's project + * @param string $tproject_id + * used only if $req_project is null + * @param string $parent_id + * @param array $options + * (same as original $options getByDocID method) + * + * @internal revisions + * 20110314 - kinow - Created function. + */ + private function getByDocIDInProject($doc_id, $req_project = null, + $tproject_id = null, $parent_id = null, $options = null) + { + $reqs = null; + if (! is_null($req_project)) { + $tproject_mgr = new testproject($this->db); + $info = $tproject_mgr->get_by_name($req_project); + if (! is_null($info)) // is project found ? + { + $tproject_id = $info[0]['id']; + $reqs = $this->getByDocID($doc_id, $tproject_id, $parent_id, + $options); + } + // else $req = null; // project not found => no req + } else { + $reqs = $this->getByDocID($doc_id, $tproject_id, $parent_id, + $options); + } + return $reqs; + } + + /* + * function: getByIDBulkLatestVersionRevision + * + * @used by reqOverView + * + * args: id: requirement id (can be an array) + * [version_id]: requirement version id (can be an array) + * [version_number]: + * [options] + * + * + * returns: null if query fails + * map with requirement info + * + * @internal revisions + * @since 1.9.12 + */ + public function getByIDBulkLatestVersionRevision($id, $opt = null) + { + static $debugMsg; + static $userCache; // key: user id, value: display name + static $user_keys; + + if (! $debugMsg) { + $debugMsg = $this->debugMsg . __FUNCTION__; + $labels['undefined'] = lang_get('undefined'); + $user_keys = array( + 'author' => 'author_id', + 'modifier' => 'modifier_id' + ); + } + + $my['opt'] = array( + 'outputFormat' => 'map' + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $in_clause = "IN (" . implode(",", (array) $id) . ") "; + $where_clause = " WHERE NH_REQV.parent_id " . $in_clause; + + // added -1 AS revision_id to make some process easier + $sql = " /* $debugMsg */ SELECT REQ.id,REQ.srs_id,REQ.req_doc_id," . + " REQV.scope,REQV.status,REQV.type,REQV.active," . + " REQV.is_open,REQV.author_id,REQV.version,REQV.id AS version_id," . + " REQV.expected_coverage,REQV.creation_ts,REQV.modifier_id," . + " REQV.modification_ts,REQV.revision, -1 AS revision_id, " . + " NH_REQ.name AS title, REQ_SPEC.testproject_id, " . + " NH_RSPEC.name AS req_spec_title, REQ_SPEC.doc_id AS req_spec_doc_id, NH_REQ.node_order " . + " FROM {$this->tables['nodes_hierarchy']} NH_REQV JOIN " . + "( SELECT XNH_REQV.parent_id,MAX(XNH_REQV.id) AS LATEST_VERSION_ID " . + " FROM {$this->tables['nodes_hierarchy']} XNH_REQV " . + " WHERE XNH_REQV.parent_id {$in_clause} " . + " GROUP BY XNH_REQV.parent_id ) ZAZA ON NH_REQV.id = ZAZA.LATEST_VERSION_ID " . + " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . + " JOIN {$this->object_table} REQ ON REQ.id = NH_REQV.parent_id " . + " JOIN {$this->tables['req_specs']} REQ_SPEC ON REQ_SPEC.id = REQ.srs_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC ON NH_RSPEC.id = REQ_SPEC.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . + $where_clause; + + $sqlOpt = ($my['opt']['outputFormat'] == 'map' ? 0 : database::CUMULATIVE); + $recordset = $this->db->fetchRowsIntoMap($sql, 'id', $sqlOpt); + + $rs = null; + + if (! is_null($recordset)) { + // Decode users + $rs = $recordset; + + // try to guess output structure + $x = array_keys(current($rs)); + if (is_int($x[0])) { + // output[REQID][0] = array('id' =>, 'xx' => ...) + $flevel = array_keys($recordset); + foreach ($flevel as $flk) { + $key2loop = array_keys($recordset[$flk]); + foreach ($key2loop as $key) { + foreach ($user_keys as $ukey => $userid_field) { + $rs[$flk][$key][$ukey] = ''; + if (trim($rs[$flk][$key][$userid_field]) != "") { + if (! isset( + $userCache[$rs[$flk][$key][$userid_field]])) { + $user = tlUser::getByID($this->db, + $rs[$flk][$key][$userid_field]); + $rs[$flk][$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; + $userCache[$rs[$flk][$key][$userid_field]] = $rs[$flk][$key][$ukey]; + unset($user); + } else { + $rs[$flk][$key][$ukey] = $userCache[$rs[$flk][$key][$userid_field]]; + } + } + } + } + } + } else { + // output[REQID] = array('id' =>, 'xx' => ...) + $key2loop = array_keys($recordset); + foreach ($key2loop as $key) { + foreach ($user_keys as $ukey => $userid_field) { + $rs[$key][$ukey] = ''; + if (trim($rs[$key][$userid_field]) != "") { + if (! isset($userCache[$rs[$key][$userid_field]])) { + $user = tlUser::getByID($this->db, + $rs[$key][$userid_field]); + $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; + $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; + unset($user); + } else { + $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; + } + } + } + } + } + } + + unset($recordset); + unset($my); + unset($dummy); + + return $rs; + } + + /** + * + * @internal revisions + * @since 1.9.12 + * @todo delete the unused function if necessary + */ + private function getCoverageCounter($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $safe_id = intval($id); + $sql = "/* $debugMsg */ " . " SELECT COUNT(0) AS qty " . + " FROM {$this->tables['req_coverage']} " . " WHERE req_id = " . + $safe_id; + + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty']; + } + + /** + * + * @internal revisions + * @since 1.9.12 + * @todo delete the unused function if necessary + */ + private function getCoverageCounterSet($itemSet) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT req_id, COUNT(0) AS qty " . + " FROM {$this->tables['req_coverage']} " . " WHERE req_id IN (" . + implode(',', $itemSet) . ")" . " GROUP BY req_id "; + + return $this->db->fetchRowsIntoMap($sql, 'req_id'); + } + + /** + * + * @internal revisions + * @since 1.9.12 + */ + public function getRelationsCounters($itemSet) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $inSet = implode(',', $itemSet); + $sqlS = "/* $debugMsg */ SELECT COUNT(*) AS qty, source_id AS req_id " . + " FROM {$this->tables['req_relations']} " . + " WHERE source_id IN ({$inSet}) "; + $sqlS .= (DB_TYPE == 'mssql') ? ' GROUP BY source_id ' : ' GROUP BY req_id '; + + $sqlD = "/* $debugMsg */ SELECT COUNT(*) AS qty, destination_id AS req_id " . + " FROM {$this->tables['req_relations']} " . + " WHERE destination_id IN ({$inSet}) "; + $sqlD .= (DB_TYPE == 'mssql') ? ' GROUP BY destination_id ' : ' GROUP BY req_id '; + + $sqlT = " SELECT SUM(qty) AS qty, req_id " . + " FROM ($sqlS UNION ALL $sqlD) D " . ' GROUP BY req_id '; + + return $this->db->fetchColumnsIntoMap($sqlT, 'req_id', 'qty'); + } + + /** + */ + private function updateScope($reqVersionID) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $sql = "/* $debugMsg */ UPDATE {$this->tables['req_versions']} " . + " SET scope='" . $this->db->prepare_string($scope) . "'" . + " WHERE id=" . intval($reqVersionID); + $this->db->exec_query($sql); + } + + /** + * render Image Attachments INLINE + */ + public function renderImageAttachments($id, &$item2render, $basehref = null) + { + static $attSet; + + $version_id = intval($item2render['version_id']); + if (! $attSet || ! isset($attSet[$id])) { + $attSet[$id] = $this->attachmentRepository->getAttachmentInfosFor( + $version_id, $this->attachmentTableName, 'id'); + $beginTag = '[tlInlineImage]'; + $endTag = '[/tlInlineImage]'; + } + + if (is_null($attSet[$id])) { + return; + } + + // $href = '%s:%s' . " $versionTag (link)

    "; + // second \'%s\' needed if I want to use Latest as indication, need to understand + // Javascript instead of javascript, because CKeditor sometimes complains + $bhref = is_null($basehref) ? $_SESSION['basehref'] : $basehref; + $img = '

    '; + + $key2check = array( + 'scope' + ); + $rse = &$item2render; + foreach ($key2check as $item_key) { + $start = strpos($rse[$item_key], $beginTag); + $ghost = $rse[$item_key]; + + // There is at least one request to replace ? + if (! $start && ! empty($beginTag)) { + $xx = explode($beginTag, $rse[$item_key]); + + // How many requests to replace ? + $xx2do = count($xx); + $ghost = ''; + for ($xdx = 0; $xdx < $xx2do; $xdx ++) { + // Hope was not a false request. + if (strpos($xx[$xdx], $endTag) !== false) { + // Separate command string from other text + // Theorically can be just ONE, but it depends + // is user had not messed things. + $yy = explode($endTag, $xx[$xdx]); + if (! empty($yy)) { + $atx = $yy[0]; + try { + if (isset($attSet[$id][$atx]) && + $attSet[$id][$atx]['is_image']) { + $ghost .= str_replace('%id%', $atx, $img); + } + $lim = count($yy) - 1; + for ($cpx = 1; $cpx <= $lim; $cpx ++) { + $ghost .= $yy[$cpx]; + } + } catch (Exception $e) { + $ghost .= $rse[$item_key]; + } + } + } else { + // nothing to do + $ghost .= $xx[$xdx]; + } + } + } + + // reconstruct field contents + if ($ghost != '') { + $rse[$item_key] = $ghost; + } + } + } + + /** + * scope is managed at revision and version level + * + * @since 1.9.13 + * @todo delete the unused function if necessary + */ + private function inlineImageProcessing($idCard, $scope, $rosettaStone) + { + // get all attachments, then check is there are images + $att = $this->attachmentRepository->getAttachmentInfosFor($idCard->id, + $this->attachmentTableName, 'id'); + foreach ($rosettaStone as $oid => $nid) { + if ($att[$nid]['is_image']) { + $needle = str_replace($nid, $oid, $att[$nid]['inlineString']); + $inlineImg[] = array( + 'needle' => $needle, + 'rep' => $att[$nid]['inlineString'] + ); + } + } + + if (! is_null($inlineImg)) { + $dex = $scope; + foreach ($inlineImg as $elem) { + $dex = str_replace($elem['needle'], $elem['rep'], $dex); + } + $this->updateScope($idCard->versionID, $dex); + } + } + + /** + */ + public function monitorOn($req_id, $user_id, $tproject_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + // simple checks + // key = column name!! + $safe = array(); + $safe['req_id'] = intval($req_id); + $safe['user_id'] = intval($user_id); + $safe['testproject_id'] = intval($tproject_id); + + $fields = implode(',', array_keys($safe)); + + foreach ($safe as $key => $val) { + if ($val <= 0) { + throw new Exception("$key invalid value", 1); + } + } + + try { + // check before insert + $sql = "/* $debugMsg */ " . + " SELECT req_id FROM {$this->tables['req_monitor']} " . + " WHERE req_id = {$safe['req_id']} " . + " AND user_id = {$safe['user_id']} " . + " AND testproject_id = {$safe['testproject_id']}"; + $rs = $this->db->get_recordset($sql); + + if (is_null($rs)) { + $sql = "/* $debugMsg */ " . + " INSERT INTO {$this->tables['req_monitor']} ($fields) " . + " VALUES ({$safe['req_id']},{$safe['user_id']},{$safe['testproject_id']})"; + $this->db->exec_query($sql); + } + } catch (Exception $e) { + echo $e->getMessage(); + } + } + + /** + */ + public function monitorOff($req_id, $user_id = null, $tproject_id = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + // simple checks + $safe = array(); + $safe['req_id'] = intval($req_id); + $safe['user_id'] = intval($user_id); + $safe['tproject_id'] = intval($tproject_id); + + $key2check = array( + 'req_id' + ); + foreach ($key2check as $key) { + $val = $safe[$key]; + if ($val <= 0) { + throw new Exception("$key invalid value", 1); + } + } + + // Blind delete + try { + $sql = "/* $debugMsg */ " . + " DELETE FROM {$this->tables['req_monitor']} " . + " WHERE req_id = {$safe['req_id']} "; + + if ($safe['user_id'] > 0) { + $sql .= " AND user_id = {$safe['user_id']} "; + } + + if ($safe['tproject_id'] > 0) { + $sql .= " AND testproject_id = {$safe['tproject_id']}"; + } + $this->db->exec_query($sql); + } catch (Exception $e) { + echo $e->getMessage(); + } + } + + /** + */ + public function getMonitoredByUser($user_id, $tproject_id, $opt = null, + $filters = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['opt'] = array( + 'reqSpecID' => null + ); + $my['filters'] = array(); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + $my['filters'] = array_merge($my['opt'], (array) $filters); + + // simple checks + $safe = array(); + $safe['user_id'] = intval($user_id); + $safe['tproject_id'] = intval($tproject_id); + + foreach ($safe as $key => $val) { + if ($val <= 0) { + throw new Exception("$key invalid value", 1); + } + } + + $rs = null; + + if (is_null($my['opt']['reqSpecID'])) { + $sql = "/* $debugMsg */ " . + " SELECT RQM.* FROM {$this->tables['req_monitor']} RQM " . + " WHERE RQM.user_id = {$safe['user_id']} " . + " AND RQM.testproject_id = {$safe['tproject_id']}"; + } else { + $sql = "/* $debugMsg */ " . + " SELECT RQM.* FROM {$this->tables['req_monitor']} RQM " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ " . + " ON NH_REQ.id = RQM.req_id " . + " WHERE RQM.user_id = {$safe['user_id']} " . + " AND RQM.testproject_id = {$safe['tproject_id']} " . + " AND NH_REQ.parent_id = " . intval($my['opt']['reqSpecID']); + } + + try { + $rs = $this->db->fetchRowsIntoMap($sql, 'req_id'); + } catch (Exception $e) { + echo $e->getMessage(); + } + + return $rs; + } + + /** + */ + public function getReqMonitors($req_id, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $options = array( + 'tproject_id' => 0, + 'output' => 'map' + ); + $options = array_merge($options, (array) $opt); + + // simple checks + $safe = array(); + $safe['req_id'] = intval($req_id); + $safe['tproject_id'] = intval($options['tproject_id']); + + $sql = "/* $debugMsg */ " . + " SELECT RMON.user_id,U.login,U.email,U.locale " . + " FROM {$this->tables['req_monitor']} RMON " . + " JOIN {$this->tables['users']} U " . " ON U.id = RMON.user_id " . + " WHERE req_id = {$safe['req_id']} "; + + if ($safe['tproject_id'] > 0) { + $sql .= " AND testproject_id = {$safe['tproject_id']}"; + } + + switch ($options['output']) { + case 'array': + $rs = $this->db->get_recordset($sql); + break; + + case 'map': + default: + $rs = $this->db->fetchRowsIntoMap($sql, 'user_id'); + break; + } + return $rs; + } + + /** + */ + private function notifyMonitors($req_id, $action, $user_id, $log_msg = null) + { + static $user; + $mailBodyCache = ''; + $mailSubjectCache = ''; + $debugMsg = $this->debugMsg . __FUNCTION__; + + $safe = array(); + $safe['req_id'] = intval($req_id); + + // who is monitoring? + $iuSet = $this->getReqMonitors($safe['req_id']); + if (is_null($iuSet)) { + return; + } + + if (! $user) { + $user = new tlUser($this->db); + } + + $author = $user->getNames($this->db, $user_id); + $author = $author[$user_id]; + $idCard = $author['login'] . " ({$author['first']} {$author['last']})"; + + // use specific query because made things simpler + $sql = "/* $debugMsg */ " . " SELECT REQ.id,REQ.req_doc_id,REQV.scope," . + " NH_REQ.name AS title, REQV.version " . + " FROM {$this->object_table} REQ " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id " . + " JOIN {$this->tables['req_versions']} REQV ON REQV.id = NH_REQV.id " . + " WHERE REQ.id = {$safe['req_id']} " . " ORDER BY REQV.version DESC "; + + if (! is_null($rs = $this->db->get_recordset($sql))) { + $req = $rs[0]; + } + + $mailCfg = $this->getMonitorMailCfg($action); + + $from = config_get('from_email'); + $trf = array( + "%docid%" => $req['req_doc_id'], + "%title%" => $req['title'], + "%scope%" => $req['scope'], + "%user%" => $idCard, + "%logmsg%" => $log_msg, + "%version%" => $req['version'], + "%timestamp%" => date("D M j G:i:s T Y") + ); + $body['target'] = array_keys($trf); + $body['values'] = array_values($trf); + $subj['target'] = array_keys($trf); + $subj['values'] = array_values($trf); + + foreach ($iuSet as $ue) { + if (! isset($mailBodyCache[$ue['locale']])) { + $lang = $ue['locale']; + $mailBodyCache[$lang] = mailBodyGet($mailCfg['bodyAccessKey']); + + // set values + $mailBodyCache[$lang] = str_replace($body['target'], + $body['values'], $mailBodyCache[$lang]); + + $mailSubjectCache[$lang] = lang_get( + $mailCfg['subjectAccessKey'], $lang); + + $mailSubjectCache[$lang] = str_replace($subj['target'], + $subj['values'], $mailSubjectCache[$lang]); + } + + // send mail + $auditMsg = 'Requirement - ' . $action . ' - mail to user: ' . + $ue["login"] . ' using address:' . $ue["email"]; + try { + $xmail = array(); + $xmail['cc'] = ''; + $xmail['attachment'] = null; + $xmail['exit_on_error'] = false; + $xmail['htmlFormat'] = true; + + $rmx = @email_send($from, $ue["email"], + $mailSubjectCache[$ue['locale']], + $mailBodyCache[$ue['locale']], $xmail['cc'], + $xmail['attachment'], $xmail['exit_on_error'], + $xmail['htmlFormat'], null); + $apx = $rmx->status_ok ? 'Succesful - ' : 'ERROR -'; + } catch (Exception $e) { + $apx = 'ERROR - '; + } + $auditMsg = $apx . $auditMsg; + logAuditEvent($auditMsg); + } + } + + /** + */ + private function getMonitorMailCfg($action) + { + $cfg = null; + switch ($action) { + case 'create_new_version': + $cfg['subjectAccessKey'] = 'mail_subject_req_new_version'; + $cfg['bodyAccessKey'] = 'requirements/req_create_new_version.txt'; + break; + + case 'delete': + $cfg['subjectAccessKey'] = 'mail_subject_req_delete'; + $cfg['bodyAccessKey'] = 'requirements/req_delete.txt'; + break; + + case 'delete_version': + $cfg['subjectAccessKey'] = 'mail_subject_req_delete_version'; + $cfg['bodyAccessKey'] = 'requirements/req_delete_version.txt'; + break; + } + return $cfg; + } + + /** + */ + public function setNotifyOn($cfg) + { + foreach ($cfg as $key => $val) { + $this->notifyOn[$key] = $val; + } + } + + /** + * + * @todo delete the unused function if necessary + */ + private function getNotifyOn($key = null) + { + if (! is_null($key) && isset($this->notifyOn['key'])) { + return $this->notifyOn['key']; + } + return $this->notifyOn; + } + + /** + * + * @todo delete the unused function if necessary + */ + private function updateCoverage($link, $whoWhen, $opt = null) + { + + // Set coverage for previous version to FROZEN & INACTIVE ? + // Create coverage for NEW Version + $debugMsg = $this->debugMsg . __FUNCTION__; + + $options = array( + 'freezePrevious' => true + ); + $options = array_merge($options, (array) $opt); + + $safeF = intval($link['source']); + $safeT = intval($link['dest']); + + // Set coverage for previous version to FROZEN & INACTIVE ? + if ($options['freezePrevious']) { + $sql = " /* $debugMsg */ " . + " UPDATE {$this->tables['req_coverage']} " . + " SET link_status = " . LINK_TC_REQ_CLOSED_BY_NEW_REQVERSION . + "," . " is_active=0 " . " WHERE req_version_id=" . $safeF; + $this->db->exec_query($sql); + } + + // Create coverage for NEW Version + $sql = "/* $debugMsg */ " . + " INSERT INTO {$this->tables['req_coverage']} " . + " (testcase_id,tcversion_id,req_id," . + " req_version_id,author_id,creation_ts) " . + " SELECT testcase_id,tcversion_id,req_id, " . + " {$safeT} AS req_version_id," . + " {$whoWhen['user_id']} AS author_id, " . + " {$whoWhen['when']} AS creation_ts" . + " FROM {$this->tables['req_coverage']} " . " WHERE req_version_id=" . + $safeF; + $this->db->exec_query($sql); + } + + /** + */ + private function updateTCVLinkStatus($from_version_id, $reason) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $safeF = intval($from_version_id); + + $sql = " /* $debugMsg */ " . " UPDATE {$this->tables['req_coverage']} " . + " SET link_status = " . $reason . "," . " is_active=0 " . + " WHERE req_version_id=" . $safeF; + $this->db->exec_query($sql); + } + + /** + */ + private function getAllReqVersionIDForReq($idSet) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $idList = implode(",", (array) $idSet); + $sql = " /* $debugMsg */ + SELECT REQ.id AS req_id, NHREQVER.id AS req_version_id + FROM {$this->object_table} REQ + JOIN {$this->tables['nodes_hierarchy']} NHREQVER + ON NHREQVER.parent_id = REQ.id "; + $sql .= " WHERE REQ.id IN ($idList)"; + + return $this->db->fetchColumnsIntoMap($sql, 'req_id', 'req_version_id', + database::CUMULATIVE); + } + + /** + */ + public function getActiveForTCVersion($tcversion_id, $srs_id = 'all') + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $sql = " /* $debugMsg */ " . + " SELECT REQ.id,REQ.id AS req_id,REQ.req_doc_id,NHREQ.name AS title, RCOV.is_active," . + " NHRS.name AS req_spec_title,RCOV.testcase_id," . + " REQV.id AS req_version_id, REQV.version " . + " FROM {$this->object_table} REQ " . + " JOIN {$this->tables['req_specs']} RSPEC " . + " ON REQ.srs_id = RSPEC.id " . + " JOIN {$this->tables['req_coverage']} RCOV " . + " ON RCOV.req_id = REQ.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHRS " . + " ON NHRS.id=RSPEC.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHREQ " . + " ON NHREQ.id=REQ.id " . + " JOIN {$this->tables['req_versions']} REQV " . + " ON RCOV.req_version_id=REQV.id "; + + $idList = implode(",", (array) $tcversion_id); + + $sql .= " WHERE RCOV.tcversion_id IN (" . $idList . ")" . + " AND RCOV.is_active=1 "; + + // if only for one specification is required + if ($srs_id != 'all') { + $sql .= " AND REQ.srs_id=" . intval($srs_id); + } + + if (is_array($tcversion_id)) { + return $this->db->fetchRowsIntoMap($sql, 'tcversion_id', true); + } else { + return $this->db->get_recordset($sql); + } + } + + /** + * what is meaning of Good? + */ + public function getGoodForTCVersion($tcversion_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $sql = " /* $debugMsg */ " . + " SELECT REQ.id,REQ.id AS req_id,REQ.req_doc_id, NHREQ.name AS title, RCOV.is_active, RCOV.testcase_id,RCOV.tcversion_id, - NHRS.name AS req_spec_title," . - " REQV.id AS req_version_id, REQV.version " . - - " FROM {$this->object_table} REQ " . - " JOIN {$this->tables['req_specs']} RSPEC " . - " ON REQ.srs_id = RSPEC.id " . - " JOIN {$this->tables['req_coverage']} RCOV " . - " ON RCOV.req_id = REQ.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHRS " . - " ON NHRS.id=RSPEC.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHREQ " . - " ON NHREQ.id=REQ.id " . - " JOIN {$this->tables['req_versions']} REQV " . - " ON RCOV.req_version_id=REQV.id "; - - $idList = implode(",",(array)$tcversion_id); - - $sql .= " WHERE RCOV.tcversion_id IN (" . $idList . ")"; - //" AND RCOV.is_active=1 "; - - if ( is_array($tcversion_id) ) { - return $this->db->fetchRowsIntoMap($sql,'tcversion_id',true); - } - else { - return $this->db->get_recordset($sql); - } - } - - /** - * - */ - function getActiveForReqVersion($req_version_id) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe_id = intval($req_version_id); - - $sql = " /* $debugMsg */ " . - " SELECT NH_TC.id, NH_TC.id AS tcase_id,NH_TC.name,NH_TC.name AS tcase_name," . - " TCV.tc_external_id,TCV.version,TCV.id AS tcversion_id, " . - " /* Seems to be compatible with MySQL,MSSQL,POSTGRES */ " . - " (CASE WHEN RC.link_status = " . LINK_TC_REQ_CLOSED_BY_EXEC . - " THEN 0 ELSE is_active END) AS can_be_deleted, " . - " (CASE WHEN RC.link_status = " . LINK_TC_REQ_CLOSED_BY_NEW_TCVERSION . - " THEN 1 ELSE 0 END) AS is_obsolete " . - " FROM {$this->tables['nodes_hierarchy']} NH_TC " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV " . - " ON NH_TCV.parent_id=NH_TC.id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH_TCV.id " . - " JOIN {$this->tables['req_coverage']} RC ON RC.tcversion_id = NH_TCV.id "; - - $sql .= " WHERE RC.req_version_id={$safe_id} "; - - return $this->db->get_recordset($sql); - } - - /** - * - */ - function delReqVersionTCVersionLink($bond,$caller=null) { - - $safeID = array( 'req' => intval($bond['req']), - 'tc' => intval($bond['tc']) ); - $output = 0; - $sql = " DELETE FROM {$this->tables['req_coverage']} " . - " WHERE req_version_id=" . $safeID['req'] . - " AND tcversion_id=" . $safeID['tc']; - - $result = $this->db->exec_query($sql); - - if ($result && $this->db->affected_rows() == 1) { - - // Going to audit - $sql = "SELECT NHP.name,NHC.id " . - " FROM {$this->tables['nodes_hierarchy']} NHP " . - " JOIN {$this->tables['nodes_hierarchy']} NHC " . - " ON NHP.id = NHC.parent_id " . - " WHERE NHC.id IN(" . - $safeID['req'] . "," . $safeID['tc'] . ")"; - - $mx = $this->db->fetchRowsIntoMap($sql,'id'); - - $sql = " SELECT TCV.version " . - " FROM {$this->tables['tcversions']} TCV " . - " WHERE TCV.id = " . $safeID['tc']; - $tcv = current($this->db->fetchRowsIntoMap($sql,'version')); - - $sql = " SELECT RQV.version " . - " FROM {$this->tables['req_versions']} RQV " . - " WHERE RQV.id = " . $safeID['req']; - $rqv = current($this->db->fetchRowsIntoMap($sql,'version')); - - logAuditEvent(TLS("audit_reqv_assignment_removed_tcv", - $mx[$safeID['req']]['name'],$rqv['version'], - $mx[$safeID['tc']]['name'],$tcv['version']), - "ASSIGN",$this->object_table); - $output = 1; - } - return $output; - } - - - /** - * - */ - function delReqVersionTCVersionLinkByID($link_id) { - - $safeID = intval($link_id); - - // First get audit info - $sql = " SELECT TCV.version AS tcv_vernum, NHTC.name AS tcname, " . - " RQV.version AS req_vernum, NHRQ.name AS reqname " . - " FROM {$this->tables['req_coverage']} RCOV " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = RCOV.tcversion_id " . - " JOIN {$this->tables['req_versions']} RQV " . - " ON RQV.id = RCOV.req_version_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = RCOV.testcase_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHRQ " . - " ON NHRQ.id = RCOV.req_id " . - " WHERE RCOV.id = $safeID "; - - $audit = current($this->db->get_recordset($sql)); - - $sql = " DELETE FROM {$this->tables['req_coverage']} " . - " WHERE id = $safeID "; - - $result = $this->db->exec_query($sql); - if ($result && $this->db->affected_rows() == 1) { - logAuditEvent(TLS("audit_reqv_assignment_removed_tcv", - $audit['reqname'],$audit['req_vernum'], - $audit['tcname'],$audit['tcv_vernum']), - "ASSIGN",$this->object_table); - $output = 1; - } - return $output; - } - - - /** - * - */ - function getLatestReqVersionCoverageCounterSet($itemSet) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT RCOV.req_id, COUNT(0) AS qty " . - " FROM {$this->tables['req_coverage']} RCOV " . - " JOIN {$this->views['latest_req_version_id']} LRQV " . - " ON LRQV.req_version_id = RCOV.req_version_id " . - " WHERE LRQV.req_version_id IN (" . - implode(',', $itemSet) . ")" . - " AND is_active = 1" . - " GROUP BY RCOV.req_id "; - - $rs = $this->db->fetchRowsIntoMap($sql,'req_id'); - return $rs; - } - - /* - function: bulkAssignLatestREQVTCV - assign N requirements to M test cases - Do not write audit info - - args: req_id: can be an array - testcase_id: can be an array - - returns: number of assignments done - - - */ - function bulkAssignLatestREQVTCV($req_id,$testcase_id,$author_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $insertCounter=0; // just for debug - - $reqSet = (array)$req_id; - $tcaseSet = (array)$testcase_id; - - $inReqID = implode(",",$reqSet); - $inTCaseID = implode(",",$tcaseSet); - - // Get coverage for this set of requirements and testcase - // to be used to understand if insert if needed - $sql = " /* $debugMsg */ - SELECT RCOV.req_id,RCOV.testcase_id, - RCOV.req_version_id,RCOV.tcversion_id - FROM {$this->tables['req_coverage']} RCOV - JOIN {$this->views['latest_req_version_id']} LRQV - ON LRQV.req_version_id = RCOV.req_version_id - JOIN {$this->views['latest_tcase_version_id']} LTCV - ON LTCV.tcversion_id = RCOV.tcversion_id - WHERE LRQV.req_id IN ({$inReqID}) - AND LTCV.testcase_id IN ({$inTCaseID}) "; - - // $coverage = $this->db->get_recordset($sql); - $coverage = (array) $this->db->fetchMapRowsIntoMap($sql, - 'req_version_id','tcversion_id'); - $sql = " /* $debugMsg */ - SELECT * FROM {$this->views['latest_tcase_version_id']} - WHERE testcase_id IN ({$inTCaseID}) "; - $ltcvSet = $this->db->fetchRowsIntoMap($sql,'tcversion_id'); - - $sql = " /* $debugMsg */ - SELECT * FROM {$this->views['latest_req_version_id']} - WHERE req_id IN ({$inReqID}) "; - $lrqvSet = $this->db->fetchRowsIntoMap($sql,'req_version_id'); - - $now = $this->db->db_now(); - $ins = " INSERT INTO {$this->tables['req_coverage']} + NHRS.name AS req_spec_title," . + " REQV.id AS req_version_id, REQV.version " . + " FROM {$this->object_table} REQ " . + " JOIN {$this->tables['req_specs']} RSPEC " . + " ON REQ.srs_id = RSPEC.id " . + " JOIN {$this->tables['req_coverage']} RCOV " . + " ON RCOV.req_id = REQ.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHRS " . + " ON NHRS.id=RSPEC.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHREQ " . + " ON NHREQ.id=REQ.id " . + " JOIN {$this->tables['req_versions']} REQV " . + " ON RCOV.req_version_id=REQV.id "; + + $idList = implode(",", (array) $tcversion_id); + + $sql .= " WHERE RCOV.tcversion_id IN (" . $idList . ")"; + + if (is_array($tcversion_id)) { + return $this->db->fetchRowsIntoMap($sql, 'tcversion_id', true); + } else { + return $this->db->get_recordset($sql); + } + } + + /** + */ + public function getActiveForReqVersion($req_version_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $safe_id = intval($req_version_id); + + $sql = " /* $debugMsg */ " . + " SELECT NH_TC.id, NH_TC.id AS tcase_id,NH_TC.name,NH_TC.name AS tcase_name," . + " TCV.tc_external_id,TCV.version,TCV.id AS tcversion_id, " . + " /* Seems to be compatible with MySQL,MSSQL,POSTGRES */ " . + " (CASE WHEN RC.link_status = " . LINK_TC_REQ_CLOSED_BY_EXEC . + " THEN 0 ELSE is_active END) AS can_be_deleted, " . + " (CASE WHEN RC.link_status = " . LINK_TC_REQ_CLOSED_BY_NEW_TCVERSION . + " THEN 1 ELSE 0 END) AS is_obsolete " . + " FROM {$this->tables['nodes_hierarchy']} NH_TC " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV " . + " ON NH_TCV.parent_id=NH_TC.id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH_TCV.id " . + " JOIN {$this->tables['req_coverage']} RC ON RC.tcversion_id = NH_TCV.id "; + + $sql .= " WHERE RC.req_version_id={$safe_id} "; + + return $this->db->get_recordset($sql); + } + + /** + */ + public function delReqVersionTCVersionLink($bond, $caller = null) + { + $safeID = array( + 'req' => intval($bond['req']), + 'tc' => intval($bond['tc']) + ); + $output = 0; + $sql = " DELETE FROM {$this->tables['req_coverage']} " . + " WHERE req_version_id=" . $safeID['req'] . " AND tcversion_id=" . + $safeID['tc']; + + $result = $this->db->exec_query($sql); + + if ($result && $this->db->affected_rows() == 1) { + + // Going to audit + $sql = "SELECT NHP.name,NHC.id " . + " FROM {$this->tables['nodes_hierarchy']} NHP " . + " JOIN {$this->tables['nodes_hierarchy']} NHC " . + " ON NHP.id = NHC.parent_id " . " WHERE NHC.id IN(" . + $safeID['req'] . "," . $safeID['tc'] . ")"; + + $mx = $this->db->fetchRowsIntoMap($sql, 'id'); + + $sql = " SELECT TCV.version " . + " FROM {$this->tables['tcversions']} TCV " . " WHERE TCV.id = " . + $safeID['tc']; + $tcv = current($this->db->fetchRowsIntoMap($sql, 'version')); + + $sql = " SELECT RQV.version " . + " FROM {$this->tables['req_versions']} RQV " . " WHERE RQV.id = " . + $safeID['req']; + $rqv = current($this->db->fetchRowsIntoMap($sql, 'version')); + + logAuditEvent( + TLS("audit_reqv_assignment_removed_tcv", + $mx[$safeID['req']]['name'], $rqv['version'], + $mx[$safeID['tc']]['name'], $tcv['version']), "ASSIGN", + $this->object_table); + $output = 1; + } + return $output; + } + + /** + */ + public function delReqVersionTCVersionLinkByID($link_id) + { + $safeID = intval($link_id); + + // First get audit info + $sql = " SELECT TCV.version AS tcv_vernum, NHTC.name AS tcname, " . + " RQV.version AS req_vernum, NHRQ.name AS reqname " . + " FROM {$this->tables['req_coverage']} RCOV " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = RCOV.tcversion_id " . + " JOIN {$this->tables['req_versions']} RQV " . + " ON RQV.id = RCOV.req_version_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = RCOV.testcase_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHRQ " . + " ON NHRQ.id = RCOV.req_id " . " WHERE RCOV.id = $safeID "; + + $audit = current($this->db->get_recordset($sql)); + + $sql = " DELETE FROM {$this->tables['req_coverage']} " . + " WHERE id = $safeID "; + + $result = $this->db->exec_query($sql); + if ($result && $this->db->affected_rows() == 1) { + logAuditEvent( + TLS("audit_reqv_assignment_removed_tcv", $audit['reqname'], + $audit['req_vernum'], $audit['tcname'], $audit['tcv_vernum']), + "ASSIGN", $this->object_table); + $output = 1; + } + return $output; + } + + /** + */ + public function getLatestReqVersionCoverageCounterSet($itemSet) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . " SELECT RCOV.req_id, COUNT(0) AS qty " . + " FROM {$this->tables['req_coverage']} RCOV " . + " JOIN {$this->views['latest_req_version_id']} LRQV " . + " ON LRQV.req_version_id = RCOV.req_version_id " . + " WHERE LRQV.req_version_id IN (" . implode(',', $itemSet) . ")" . + " AND is_active = 1" . " GROUP BY RCOV.req_id "; + + return $this->db->fetchRowsIntoMap($sql, 'req_id'); + } + + /* + * function: bulkAssignLatestREQVTCV + * assign N requirements to M test cases + * Do not write audit info + * + * args: req_id: can be an array + * testcase_id: can be an array + * + * returns: number of assignments done + * + * + */ + public function bulkAssignLatestREQVTCV($req_id, $testcase_id, $author_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $insertCounter = 0; // just for debug + + $reqSet = (array) $req_id; + $tcaseSet = (array) $testcase_id; + + $inReqID = implode(",", $reqSet); + $inTCaseID = implode(",", $tcaseSet); + + // Get coverage for this set of requirements and testcase + // to be used to understand if insert if needed + $sql = " /* $debugMsg */ SELECT RCOV.req_id,RCOV.testcase_id, " . + " RCOV.req_version_id,RCOV.tcversion_id " . + " FROM {$this->tables['req_coverage']} RCOV " . + " JOIN {$this->views['latest_req_version_id']} LRQV " . + " ON LRQV.req_version_id = RCOV.req_version_id " . + " JOIN {$this->views['latest_tcase_version_id']} LTCV " . + " ON LTCV.tcversion_id = RCOV.tcversion_id " . + " WHERE LRQV.req_id IN ({$inReqID}) " . + " AND LTCV.testcase_id IN ({$inTCaseID}) "; + + $coverage = (array) $this->db->fetchMapRowsIntoMap($sql, + 'req_version_id', 'tcversion_id'); + $sql = " /* $debugMsg */ SELECT * FROM {$this->views['latest_tcase_version_id']} " . + " WHERE testcase_id IN ({$inTCaseID}) "; + $ltcvSet = $this->db->fetchRowsIntoMap($sql, 'tcversion_id'); + + $sql = " /* $debugMsg */ SELECT * FROM {$this->views['latest_req_version_id']} " . + " WHERE req_id IN ({$inReqID}) "; + $lrqvSet = $this->db->fetchRowsIntoMap($sql, 'req_version_id'); + + $now = $this->db->db_now(); + $ins = " INSERT INTO {$this->tables['req_coverage']} (req_id,testcase_id,req_version_id, - tcversion_id,author_id,creation_ts) "; - - foreach( $ltcvSet as $tcversion_id => $tc ) { - $sql = $ins; - $values = array(); - foreach( $lrqvSet as $req_version_id => $req ) { - if( !isset($coverage[$req_version_id][$tcversion_id]) ) { - $insertCounter++; - $values[] = " ({$req['req_id']},{$tc['testcase_id']}, + tcversion_id,author_id,creation_ts) "; + + foreach ($ltcvSet as $tcversion_id => $tc) { + $sql = $ins; + $values = array(); + foreach ($lrqvSet as $req_version_id => $req) { + if (! isset($coverage[$req_version_id][$tcversion_id])) { + $insertCounter ++; + $values[] = " ({$req['req_id']},{$tc['testcase_id']}, $req_version_id,$tcversion_id, - {$author_id},{$now}) "; - } - } - - if( count($values) > 0 ) { - $sql .= " VALUES " . implode(',',$values); - $this->db->exec_query($sql); - } - } - - return $insertCounter; - } - - - /** - * - * reqIdentity array('id' =>,'version_id' =>); - * tcIdentity array('id' =>,'version_id' =>); - * - */ - function assignReqVerToTCVer($reqIdentity,$tcIdentity,$authorID) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $now = $this->db->db_now(); - $sql = " /* $debugMsg */ - INSERT INTO {$this->tables['req_coverage']} - (req_id,testcase_id,req_version_id, tcversion_id, - author_id,creation_ts) - VALUES ({$reqIdentity['id']},{$tcIdentity['id']}, - {$reqIdentity['version_id']}, - {$tcIdentity['version_id']}, - {$authorID},{$now})"; - - $result = $this->db->exec_query($sql); - - return 1; - } - - /** - * what is meaning of Good? - * - */ - function getGoodForReqVersion($reqVersionID, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $options = array('verbose' => false, 'tproject_id' => null); - $options = array_merge($options,(array)$opt); - - $sql = " /* $debugMsg */ " . - " SELECT REQ.id,REQ.id AS req_id,REQ.req_doc_id, - NHREQ.name AS title, RCOV.is_active, - RCOV.testcase_id,RCOV.tcversion_id, - NHRS.name AS req_spec_title," . - " REQV.id AS req_version_id, REQV.version "; - - $addJoin = ''; - if($options['verbose']) { - $addFP = " TCV.tc_external_id AS external_id"; - if( ($tprj = intval($options['tproject_id'])) > 0 ) { - $sqlP = " SELECT prefix FROM {$this->tables['testprojects']} - WHERE id=$tprj"; - $dummy = $this->db->get_recordset($sqlP); - - if( count($dummy) == 1 ) { - $prefix = $dummy[0]['prefix']; - } - $glue = config_get('testcase_cfg'); - $glue = $glue->glue_character; - $addFP = " CONCAT('$prefix','$glue',TCV.tc_external_id) AS tc_external_id "; - } - - $sql .= ",NH_TC.name AS testcase_name,$addFP"; - $addJoin = " JOIN {$this->tables['nodes_hierarchy']} NH_TC + {$author_id},{$now}) "; + } + } + + if (! empty($values)) { + $sql .= " VALUES " . implode(',', $values); + $this->db->exec_query($sql); + } + } + + return $insertCounter; + } + + /** + * reqIdentity array('id' =>,'version_id' =>); + * tcIdentity array('id' =>,'version_id' =>); + */ + public function assignReqVerToTCVer($reqIdentity, $tcIdentity, $authorID) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $now = $this->db->db_now(); + $sql = " /* $debugMsg */ INSERT INTO {$this->tables['req_coverage']} " . + " (req_id,testcase_id,req_version_id, tcversion_id, " . + " author_id,creation_ts) VALUES ({$reqIdentity['id']},{$tcIdentity['id']}, " . + " {$reqIdentity['version_id']}, {$tcIdentity['version_id']}, {$authorID},{$now})"; + $this->db->exec_query($sql); + + return 1; + } + + /** + * what is meaning of Good? + */ + public function getGoodForReqVersion($reqVersionID, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $options = array( + 'verbose' => false, + 'tproject_id' => null + ); + $options = array_merge($options, (array) $opt); + + $sql = " /* $debugMsg */ SELECT REQ.id,REQ.id AS req_id,REQ.req_doc_id, " . + " NHREQ.name AS title, RCOV.is_active, RCOV.testcase_id,RCOV.tcversion_id, " . + " NHRS.name AS req_spec_title, REQV.id AS req_version_id, REQV.version "; + + $addJoin = ''; + if ($options['verbose']) { + $addFP = " TCV.tc_external_id AS external_id"; + if (($tprj = intval($options['tproject_id'])) > 0) { + $sqlP = " SELECT prefix FROM {$this->tables['testprojects']} + WHERE id=$tprj"; + $dummy = $this->db->get_recordset($sqlP); + + if (count($dummy) == 1) { + $prefix = $dummy[0]['prefix']; + } + $glue = config_get('testcase_cfg'); + $glue = $glue->glue_character; + $addFP = " CONCAT('$prefix','$glue',TCV.tc_external_id) AS tc_external_id "; + } + + $sql .= ",NH_TC.name AS testcase_name,$addFP"; + $addJoin = " JOIN {$this->tables['nodes_hierarchy']} NH_TC ON NH_TC.id = RCOV.testcase_id JOIN {$this->tables['tcversions']} TCV - ON TCV.id = RCOV.tcversion_id "; - } - - $sql .= " FROM {$this->object_table} REQ - JOIN {$this->tables['req_specs']} RSPEC - ON REQ.srs_id = RSPEC.id - JOIN {$this->tables['req_coverage']} RCOV - ON RCOV.req_id = REQ.id - JOIN {$this->tables['nodes_hierarchy']} NHRS - ON NHRS.id=RSPEC.id - JOIN {$this->tables['nodes_hierarchy']} NHREQ - ON NHREQ.id=REQ.id - JOIN {$this->tables['req_versions']} REQV - ON RCOV.req_version_id=REQV.id $addJoin "; - - - $idList = implode(",",(array)$reqVersionID); - - $sql .= " WHERE RCOV.req_version_id IN (" . $idList . ")"; - - return $this->db->fetchRowsIntoMap($sql,'req_version_id',true); - } - - -} // class end + ON TCV.id = RCOV.tcversion_id "; + } + + $sql .= " FROM {$this->object_table} REQ + JOIN {$this->tables['req_specs']} RSPEC + ON REQ.srs_id = RSPEC.id + JOIN {$this->tables['req_coverage']} RCOV + ON RCOV.req_id = REQ.id + JOIN {$this->tables['nodes_hierarchy']} NHRS + ON NHRS.id=RSPEC.id + JOIN {$this->tables['nodes_hierarchy']} NHREQ + ON NHREQ.id=REQ.id + JOIN {$this->tables['req_versions']} REQV + ON RCOV.req_version_id=REQV.id $addJoin "; + + $idList = implode(",", (array) $reqVersionID); + + $sql .= " WHERE RCOV.req_version_id IN (" . $idList . ")"; + + return $this->db->fetchRowsIntoMap($sql, 'req_version_id', true); + } +} diff --git a/lib/functions/requirement_spec_mgr.class.php b/lib/functions/requirement_spec_mgr.class.php index 92fc43db1c..17df07285a 100644 --- a/lib/functions/requirement_spec_mgr.class.php +++ b/lib/functions/requirement_spec_mgr.class.php @@ -1,2852 +1,2822 @@ - "XML"); - var $export_file_types = array("XML" => "XML"); - var $my_node_type; - var $node_types_descr_id; - var $node_types_id_descr; - var $attachmentTableName; - var $field_size; - var $req_mgr; - var $relationsCfg; - var $requirement_child_ids = array(); - - /* - contructor - - args: db: reference to db object - - returns: instance of requirement_spec_mgr - - */ - function __construct(&$db) - { - $this->db = &$db; - $this->cfield_mgr = new cfield_mgr($this->db); - $this->tree_mgr = new tree($this->db); - $this->req_mgr = new requirement_mgr($this->db); - - $this->node_types_descr_id = $this->tree_mgr->get_available_node_types(); - $this->node_types_id_descr = array_flip($this->node_types_descr_id); - $this->my_node_type = $this->node_types_descr_id['requirement_spec']; - - $this->attachmentTableName = 'req_specs'; - tlObjectWithAttachments::__construct($this->db,$this->attachmentTableName); - $this->object_table=$this->tables['req_specs']; - - $this->field_size = config_get('field_size'); - - $this->relationsCfg = new stdClass(); - $this->relationsCfg->interProjectLinking = config_get('req_cfg')->relations->interproject_linking; - } - - /* - function: get_export_file_types - getter - - args: - - - returns: map - key: export file type code - value: export file type verbose description - - */ - function get_export_file_types() - { - return $this->export_file_types; - } - - /* - function: get_impor_file_types - getter - - args: - - - returns: map - key: import file type code - value: import file type verbose description - - */ - function get_import_file_types() - { - return $this->import_file_types; - } - - - /* - function: create - - args: - tproject_id: requirement spec parent (till we will manage unlimited tree depth) - parent_id: - doc_id - title - scope - countReq - user_id: requirement spec author - [type] - [node_order] - [options] - - returns: map with following keys: - status_ok -> 1/0 - msg -> some simple message, useful when status_ok ==0 - id -> id of requirement specification - - */ - function create($tproject_id,$parent_id,$doc_id,$title, $scope, - $countReq,$user_id, $type = TL_REQ_SPEC_TYPE_FEATURE, - $node_order=null, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $result=array('status_ok' => 0, 'msg' => 'ko', 'id' => -1, 'revision_id' => -1); - $title=trim($title); - $chk=$this->check_main_data($title,$doc_id,$tproject_id,$parent_id); - $result['msg']=$chk['msg']; - - $my['options'] = array( 'actionOnDuplicate' => "block"); - $my['options'] = array_merge($my['options'], (array)$options); - - if ($chk['status_ok']) - { - if( config_get('internal_links')->enable ) - { - $scope = req_link_replace($this->db, $scope, $tproject_id); - } - $req_spec_id = $this->tree_mgr->new_node($parent_id,$this->my_node_type,$title,$node_order); - - $sql = "/* $debugMsg */ INSERT INTO {$this->object_table} " . - " (id, testproject_id, doc_id) " . - " VALUES (" . $req_spec_id . "," . $tproject_id . ",'" . $this->db->prepare_string($doc_id) . "')"; - - if (!$this->db->exec_query($sql)) - { - $result['msg']=lang_get('error_creating_req_spec'); - } - else - { - $revItem = array('revision' => 1, 'doc_id' => $doc_id, 'name' => $title, - 'scope' => $scope, 'type' => $type, 'status' => 1, - 'total_req' => $countReq,'author_id' => $user_id, - 'log_message' => lang_get('reqspec_created_automatic_log')); - - $op = $this->create_revision($req_spec_id,$revItem); - $result['status_ok'] = $op['status_ok']; - $result['msg'] = $op['status_ok'] ? $result['msg'] : $op['msg']; - $result['revision_id'] = $op['status_ok'] ? $op['id'] : -1; - $result['id'] = $op['status_ok'] ? $req_spec_id : -1; - } - } - return $result; - } - - /* - function: get_by_id - - - args : id: requirement spec id - options: - key: output - values: 'full','credentials' - - returns: null if query fails - map with requirement spec info - */ - function get_by_id($id,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - - $my['options'] = array('output' => 'full'); - $my['options'] = array_merge($my['options'], (array)$options); - - - // First Step get ID of LATEST revision - $info = $this->get_last_child_info($id,array('output' => 'credentials') ); - $childID = $info['id']; - - - $sql = "/* $debugMsg */ SELECT RSPEC.id,RSPEC.doc_id, RSPEC.testproject_id, " . - " RSPEC_REV.id AS revision_id, RSPEC_REV.revision "; - - switch($my['options']['output']) - { - case 'credentials': - $doUserDecode = false; - break; - - case 'full': - default: - $sql .= " , '' AS author, '' AS modifier, NH_RSPEC.node_order, " . - " RSPEC_REV.scope,RSPEC_REV.total_req,RSPEC_REV.type," . - " RSPEC_REV.author_id,RSPEC_REV.creation_ts,RSPEC_REV.modifier_id," . - " RSPEC_REV.modification_ts,NH_RSPEC.name AS title "; - $doUserDecode = true; - break; - } - $sql .= " FROM {$this->object_table} RSPEC " . - " JOIN {$this->tables['req_specs_revisions']} RSPEC_REV " . - " ON RSPEC_REV.parent_id = RSPEC.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC " . - " ON RSPEC.id = NH_RSPEC.id " . - " WHERE RSPEC.id = NH_RSPEC.id " . - " AND RSPEC_REV.id = {$childID} " . - " AND RSPEC.id = {$id} "; - - - // echo $sql; - $recordset = $this->db->get_recordset($sql); - $rs = null; - if(!is_null($recordset)) - { - // Decode users - $rs = $recordset[0]; - - if($doUserDecode) - { - $lbl_undef = lang_get('undefined'); - if(trim($rs['author_id']) != "") - { - $user = tlUser::getByID($this->db,$rs['author_id']); - // need to manage deleted users - $rs['author'] = $lbl_undef; - if($user) - { - $rs['author'] = $user->getDisplayName(); - } - } - - if(trim($rs['modifier_id']) != "") - { - $user = tlUser::getByID($this->db,$rs['modifier_id']); - // need to manage deleted users - $rs['modifier'] = $lbl_undef; - if($user) - { - $rs['modifier'] = $user->getDisplayName(); - } - } - } - } - return $rs; - } - - - - /** - * get analyse based on requirements and test specification - * - * @param integer $id: Req Spec id - * @return array Coverage in three internal arrays: covered, uncovered, nottestable REQ - * @author martin havlat - */ - function get_coverage($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $output = array( 'covered' => array(), 'uncovered' => array(),'nottestable' => array()); - - // function get_requirements($id, $range = 'all', $testcase_id = null, $options=null, $filters = null) - $getOptions = array('order_by' => " ORDER BY req_doc_id,title"); - $getFilters = array('status' => VALID_REQ); - $validReq = $this->get_requirements($id,'all',null,$getOptions,$getFilters); - - // get not-testable requirements - $getFilters = array('status' => NON_TESTABLE_REQ); - $output['nottestable'] = $this->get_requirements($id,'all',null,$getOptions,$getFilters); - - // get coverage - if (sizeof($validReq)) - { - foreach ($validReq as $req) - { - // collect TC for REQ - $arrCoverage = $this->req_mgr->get_coverage($req['id']); - - if (count($arrCoverage) > 0) - { - // add information about coverage - $req['coverage'] = $arrCoverage; - $output['covered'][] = $req; - } - else - { - $output['uncovered'][] = $req; - } - } - } - return $output; - } - - - /** - * get requirement coverage metrics - * - * @param integer $srs_id - * @return array results - * @author havlatm - */ - function get_metrics($id) - { - $output = array('notTestable' => 0, 'total' => 0, 'covered' => 0, 'uncovered' => 0); - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $getFilters = array('status' => NON_TESTABLE_REQ); - $output['notTestable'] = $this->get_requirements_count($id,'all',null,$getFilters); - - $sql = "/* $debugMsg */ SELECT count(0) AS cnt FROM {$this->tables['requirements']} WHERE srs_id={$id}"; - $output['total'] = $this->db->fetchFirstRowSingleColumn($sql,'cnt'); - - $sql = "/* $debugMsg */ SELECT total_req FROM {$this->object_table} WHERE id={$id}"; - $output['expectedTotal'] = $this->db->fetchFirstRowSingleColumn($sql,'total_req'); - if ($output['expectedTotal'] == 0) - { - $output['expectedTotal'] = $output['total']; - } - - $sql = "/* $debugMsg */ SELECT DISTINCT REQ.id " . - " FROM {$this->tables['requirements']} REQ " . - " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ.id=REQ_COV.req_id" . - " WHERE REQ.srs_id={$id} " ; - $rs = $this->db->get_recordset($sql); - if (!is_null($rs)) - { - $output['covered'] = count($rs); - } - $output['uncovered'] = $output['expectedTotal'] - $output['total']; - - return $output; - } - - /* - function: get_all_in_testproject - get info about all req spec defined for a testproject - - - args: tproject_id - [order_by] - - returns: null if no srs exits, or no srs exists for id - array, where each element is a map with req spec data. - - map keys: - id - testproject_id - title - scope - total_req - type - author_id - creation_ts - modifier_id - modification_ts - */ - function get_all_in_testproject($tproject_id,$order_by=" ORDER BY title") - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT RSPEC.id,testproject_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," . - " RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," . - " RSPEC.modification_ts,NH.name AS title,NH.node_order " . - " FROM {$this->object_table} RSPEC, {$this->tables['nodes_hierarchy']} NH " . - " WHERE NH.id=RSPEC.id" . - " AND testproject_id={$tproject_id}"; - - if (!is_null($order_by)) - { - $sql .= $order_by; - } - return $this->db->get_recordset($sql); - } - - - /* - function: update - - args: item => map with following keys - id,doc_id,name,scope,countReq,user_id,type,node_order - - returns: map with following keys: - status_ok -> 1/0 - msg -> some simple message, useful when status_ok ==0 - revision_id -> useful when user request create new revision on update - - */ - function update($item,$options=null) - { - - $result = array('status_ok' => 1, 'msg' => 'ok', 'revision_id' => -1); - $my['options'] = array('skip_controls' => false, 'create_rev' => false, 'log_message' => ''); - $my['options'] = array_merge($my['options'], (array)$options); - - $title=trim_and_limit($item['name']); - $doc_id=trim_and_limit($item['doc_id']); - - $path=$this->tree_mgr->get_path($item['id']); - $tproject_id = $path[0]['parent_id']; - $last_idx=count($path)-1; - $parent_id = $last_idx==0 ? null : $path[$last_idx]['parent_id']; - $chk=$this->check_main_data($title,$doc_id,$path[0]['parent_id'],$parent_id,$item['id']); - - - if ($chk['status_ok'] || $my['options']['skip_controls']) - { - if( config_get('internal_links')->enable ) - { - $item['scope'] = req_link_replace($this->db, $item['scope'], $tproject_id); - } - - $cnr = null; - if( $my['options']['create_rev']) - { - $cnr = $this->create_new_revision($item['id'],$item+$my['options']); - } - else - { - // missing piece, need to update all fields on last revision - $cnr = $this->update_revision($item); - } - - $db_now = $this->db->db_now(); - $sql = " UPDATE {$this->object_table} " . - " SET doc_id='" . $this->db->prepare_string($doc_id) . "' " . - " WHERE id={$item['id']}"; - - if (!$this->db->exec_query($sql)) - { - $result['msg']=lang_get('error_updating_reqspec'); - $result['status_ok'] = 0; - } - - if( $result['status_ok'] ) - { - // need to update node on tree - $sql = " UPDATE {$this->tables['nodes_hierarchy']} " . - " SET name='" . $this->db->prepare_string($title) . "'"; - if(isset($item['node_order']) && !is_null($item['node_order']) ) - { - $sql .= ",node_order=" . intval($item['node_order']); - } - $sql .= " WHERE id={$item['id']}"; - - if (!$this->db->exec_query($sql)) - { - $result['msg']=lang_get('error_updating_reqspec'); - $result['status_ok'] = 0; - } - } - - if( $result['status_ok'] && !is_null($cnr)) - { - $result['revision_id'] = $cnr['id']; - } - } - else - { - $result['status_ok']=$chk['status_ok']; - $result['msg']=$chk['msg']; - } - return $result; - } - - - - /* - function: delete - deletes: - Requirements spec - Requirements spec custom fields values - Requirements ( Requirements spec children ) - Requirements custom fields values - - IMPORTANT/CRITIC: - This function can used to delete a Req Specification that contains ONLY Requirements. - This function is needed by tree class method: delete_subtree_objects() - To delete a Req Specification that contains other Req Specification delete_deep() must be used. - - args: id: requirement spec id - - returns: message string - ok if everything is ok - - */ - function delete($unsafe_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $id = intval($unsafe_id); - - // ATTENTION: CF linked to REVISION - $this->cfield_mgr->remove_all_design_values_from_node($id); - $result = $this->attachmentRepository->deleteAttachmentsFor($id,"req_specs"); - - // delete requirements (one type req spec children) with all related data - // coverage, attachments, custom fields, etc - $requirements_info = $this->get_requirements($id); - if(!is_null($requirements_info)) - { - $items = null; - foreach($requirements_info as $req) - { - $items[] = $req["id"]; - } - $this->req_mgr->delete($items); - } - - // delete revisions - $sqlx = array(); - $sqlx[] = "DELETE FROM {$this->tables['req_specs_revisions']} " . - "WHERE parent_id = {$id}"; - - $sqlx[] = "DELETE FROM {$this->tables['nodes_hierarchy']} " . - "WHERE parent_id = {$id} " . - "AND node_type_id=" . - $this->node_types_descr_id['requirement_spec_revision']; - - foreach($sqlx as $sql) - { - $result = $this->db->exec_query("/* $debugMsg */" . $sql); - } - - // delete specification itself - $sqlx = array(); - $sqlx[] = "DELETE FROM {$this->object_table} WHERE id = {$id}"; - $sqlx[] = "DELETE FROM {$this->tables['nodes_hierarchy']} " . - "WHERE id = {$id} AND node_type_id=" . - $this->node_types_descr_id['requirement_spec']; - - foreach($sqlx as $sql) - { - $result = $this->db->exec_query("/* $debugMsg */" .$sql); - } - - // This is a poor implementation - if($result) - { - $result = 'ok'; - } - else - { - $result = 'The DELETE SRS request fails.'; - } - - return $result; - } - - - /** - * delete_deep() - * - * Delete Req Specification, removing all children (other Req. Spec and Requirements) - */ - function delete_deep($id) - { - $exclusion = ' AND NH.node_type_id <> ' . - intval($this->node_types_descr_id['requirement_spec_revision']); - $this->tree_mgr->delete_subtree_objects($id,$id,$exclusion,array('requirement' => 'exclude_my_children')); - $this->delete($id); - } - - - - /* - function: get_requirements - get LATEST VERSION OF requirements contained in a req spec - - - args: id: req spec id - [range]: default 'all' - [testcase_id]: default null - if !is_null, is used as filter - [order_by] - - returns: array of rows - */ - function get_requirements($id, $range = 'all', $testcase_id = null, $options=null, $filters = null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array( 'order_by' => - " ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id", - 'output' => 'standard', - 'outputLevel' => 'std', 'decodeUsers' => true); - - $my['options'] = array_merge($my['options'], (array)$options); - - // null => do not filter - $my['filters'] = array('status' => null, 'type' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - switch($my['options']['output']) { - case 'count': - $rs = 0; - break; - - case 'standard': - default: - $rs = null; - break; - } - - - $tcase_filter = ''; - - // First Step - get only req info - $sql = "/* $debugMsg */ SELECT NH_REQ.id FROM {$this->tables['nodes_hierarchy']} NH_REQ "; - $addFields = ''; - switch($range) { - case 'all'; - break; - - case 'assigned': - $sql .= " JOIN {$this->tables['req_coverage']} - REQ_COV ON REQ_COV.req_id = NH_REQ.id "; - - if(!is_null($testcase_id)) { - $tcase_filter = " AND REQ_COV.testcase_id = {$testcase_id}"; - } - break; - } - - $sql = sprintf($sql,$addFields); - - $sql .= " WHERE NH_REQ.parent_id={$id} " . - " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} {$tcase_filter}"; - $itemSet = $this->db->fetchRowsIntoMap($sql,'id'); - - if( !is_null($itemSet) ) { - $reqSet = array_keys($itemSet); - $sql = "/* $debugMsg */ SELECT MAX(NH_REQV.id) AS version_id" . - " FROM {$this->tables['nodes_hierarchy']} NH_REQV " . - " WHERE NH_REQV.parent_id IN (" . implode(",",$reqSet) . ") " . - " GROUP BY NH_REQV.parent_id "; - - $latestVersionSet = $this->db->fetchRowsIntoMap($sql,'version_id'); - $reqVersionSet = array_keys($latestVersionSet); - - $getOptions['order_by'] = $my['options']['order_by']; - $getOptions['outputLevel'] = $my['options']['outputLevel']; - $getOptions['decodeUsers'] = $my['options']['decodeUsers']; - - - $rs = $this->req_mgr->get_by_id($reqSet,$reqVersionSet,null, - $getOptions,$my['filters']); - - switch($my['options']['output']) { - case 'standard': - break; - - case 'count': - $rs = !is_null($rs) ? count($rs) : 0; - break; - } - } - - // get child requirements - $reqSql = "SELECT NH_REQ.id FROM {$this->tables['nodes_hierarchy']} NH_REQ WHERE NH_REQ.parent_id={$id}"; - - $itemSetAllFolder = $this->db->fetchRowsIntoMap($reqSql,'id'); - - if(!is_null($itemSetAllFolder)){ - - foreach($itemSetAllFolder as $key => $value){ - - $sql= ''; - $tcase_filter = ''; - - // First Step - get only req info - $sql = "/* $debugMsg */ SELECT NH_REQ.id FROM {$this->tables['nodes_hierarchy']} NH_REQ "; - $addFields = ''; - switch($range) - { - case 'all'; - break; - - case 'assigned': - $sql .= " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ_COV.req_id=NH_REQ.id "; - if(!is_null($testcase_id)) - { - $tcase_filter = " AND REQ_COV.testcase_id={$testcase_id}"; - } - break; - } - - $sql = sprintf($sql,$addFields); - - $sql .= " WHERE NH_REQ.parent_id=" . $value['id'] . - " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} {$tcase_filter}"; - $itemSet = $this->db->fetchRowsIntoMap($sql,'id'); - - // var_dump($sql); - - if( !is_null($itemSet) ) - { - $reqSet = array_keys($itemSet); - $sql = "/* $debugMsg */ SELECT MAX(NH_REQV.id) AS version_id" . - " FROM {$this->tables['nodes_hierarchy']} NH_REQV " . - " WHERE NH_REQV.parent_id IN (" . implode(",",$reqSet) . ") " . - " GROUP BY NH_REQV.parent_id "; - - $latestVersionSet = $this->db->fetchRowsIntoMap($sql,'version_id'); - $reqVersionSet = array_keys($latestVersionSet); - - $getOptions['order_by'] = $my['options']['order_by']; - $getOptions['outputLevel'] = $my['options']['outputLevel']; - $getOptions['decodeUsers'] = $my['options']['decodeUsers']; - - if(is_null($rs)){ - $rs = $this->req_mgr->get_by_id($reqSet,$reqVersionSet,null,$getOptions,$my['filters']); - } else { - $rs = array_merge($rs, $this->req_mgr->get_by_id($reqSet,$reqVersionSet,null,$getOptions,$my['filters'])); - } - - - switch($my['options']['output']) - { - case 'standard': - break; - - case 'count': - $rs = !is_null($rs) ? count($rs) : 0; - break; - } - } - } - } - - return $rs; -} - - -/** get child requirements for get all testcase associate. - * args: id: requirement id - * - * returns: array of rows - */ -function get_requirement_child_by_id($id){ - - $children = $this->get_requirement_child_by_id_req($id); - foreach($children as $key => $child){ - array_push($this->requirement_child_ids, $child); - $this->get_requirement_child_by_id($child["destination_id"]); - } - return $this->requirement_child_ids; -} - -/** - * get child requirements by id. - * args: id: requirement spec id - * - * returns: array of rows - */ -function get_requirement_child_by_id_req($id){ - $sql = "/* $debugMsg */ SELECT REQ_REL.destination_id, REQ.req_doc_id, NH.name FROM req_relations REQ_REL INNER + "XML" + ); + + private $export_file_types = array( + "XML" => "XML" + ); + + private $my_node_type; + + private $node_types_descr_id; + + private $node_types_id_descr; + + protected $attachmentTableName; + + private $field_size; + + private $req_mgr; + + private $relationsCfg; + + private $requirement_child_ids = array(); + + protected $debugMsg; + + /* + * contructor + * + * args: db: reference to db object + * + * returns: instance of requirement_spec_mgr + * + */ + public function __construct(&$db) + { + $this->db = &$db; + $this->cfield_mgr = new cfield_mgr($this->db); + $this->tree_mgr = new tree($this->db); + $this->req_mgr = new requirement_mgr($this->db); + + $this->node_types_descr_id = $this->tree_mgr->get_available_node_types(); + $this->node_types_id_descr = array_flip($this->node_types_descr_id); + $this->my_node_type = $this->node_types_descr_id['requirement_spec']; + + $this->attachmentTableName = 'req_specs'; + tlObjectWithAttachments::__construct($this->db, + $this->attachmentTableName); + $this->object_table = $this->tables['req_specs']; + + $this->field_size = config_get('field_size'); + + $this->relationsCfg = new stdClass(); + $this->relationsCfg->interProjectLinking = config_get('req_cfg')->relations->interproject_linking; + + $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: '; + } + + /* + * function: get_export_file_types + * getter + * + * args: - + * + * returns: map + * key: export file type code + * value: export file type verbose description + * + */ + public function get_export_file_types() + { + return $this->export_file_types; + } + + /* + * function: get_impor_file_types + * getter + * + * args: - + * + * returns: map + * key: import file type code + * value: import file type verbose description + * + */ + public function get_import_file_types() + { + return $this->import_file_types; + } + + /* + * function: create + * + * args: + * tproject_id: requirement spec parent (till we will manage unlimited tree depth) + * parent_id: + * doc_id + * title + * scope + * countReq + * user_id: requirement spec author + * [type] + * [node_order] + * [options] + * + * returns: map with following keys: + * status_ok -> 1/0 + * msg -> some simple message, useful when status_ok ==0 + * id -> id of requirement specification + * + */ + public function create($tproject_id, $parent_id, $doc_id, $title, $scope, + $countReq, $user_id, $type = TL_REQ_SPEC_TYPE_FEATURE, + $node_order = null, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $result = array( + 'status_ok' => 0, + 'msg' => 'ko', + 'id' => - 1, + 'revision_id' => - 1 + ); + $title = trim($title); + $chk = $this->check_main_data($title, $doc_id, $tproject_id, $parent_id); + $result['msg'] = $chk['msg']; + + $my['options'] = array( + 'actionOnDuplicate' => "block" + ); + $my['options'] = array_merge($my['options'], (array) $options); + + if ($chk['status_ok']) { + if (config_get('internal_links')->enable) { + $scope = req_link_replace($this->db, $scope, $tproject_id); + } + $req_spec_id = $this->tree_mgr->new_node($parent_id, + $this->my_node_type, $title, $node_order); + + $sql = "/* $debugMsg */ INSERT INTO {$this->object_table} " . + " (id, testproject_id, doc_id) " . " VALUES (" . $req_spec_id . + "," . $tproject_id . ",'" . $this->db->prepare_string($doc_id) . + "')"; + + if (! $this->db->exec_query($sql)) { + $result['msg'] = lang_get('error_creating_req_spec'); + } else { + $revItem = array( + 'revision' => 1, + 'doc_id' => $doc_id, + 'name' => $title, + 'scope' => $scope, + 'type' => $type, + 'status' => 1, + 'total_req' => $countReq, + 'author_id' => $user_id, + 'log_message' => lang_get('reqspec_created_automatic_log') + ); + + $op = $this->create_revision($req_spec_id, $revItem); + $result['status_ok'] = $op['status_ok']; + $result['msg'] = $op['status_ok'] ? $result['msg'] : $op['msg']; + $result['revision_id'] = $op['status_ok'] ? $op['id'] : - 1; + $result['id'] = $op['status_ok'] ? $req_spec_id : - 1; + } + } + return $result; + } + + /* + * function: get_by_id + * + * + * args : id: requirement spec id + * options: + * key: output + * values: 'full','credentials' + * + * returns: null if query fails + * map with requirement spec info + */ + public function get_by_id($id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['options'] = array( + 'output' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + // First Step get ID of LATEST revision + $info = $this->get_last_child_info($id, + array( + 'output' => 'credentials' + )); + $childID = $info['id']; + + $sql = "/* $debugMsg */ SELECT RSPEC.id,RSPEC.doc_id, RSPEC.testproject_id, " . + " RSPEC_REV.id AS revision_id, RSPEC_REV.revision "; + + switch ($my['options']['output']) { + case 'credentials': + $doUserDecode = false; + break; + + case 'full': + default: + $sql .= " , '' AS author, '' AS modifier, NH_RSPEC.node_order, " . + " RSPEC_REV.scope,RSPEC_REV.total_req,RSPEC_REV.type," . + " RSPEC_REV.author_id,RSPEC_REV.creation_ts,RSPEC_REV.modifier_id," . + " RSPEC_REV.modification_ts,NH_RSPEC.name AS title "; + $doUserDecode = true; + break; + } + $sql .= " FROM {$this->object_table} RSPEC " . + " JOIN {$this->tables['req_specs_revisions']} RSPEC_REV " . + " ON RSPEC_REV.parent_id = RSPEC.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC " . + " ON RSPEC.id = NH_RSPEC.id " . " WHERE RSPEC.id = NH_RSPEC.id " . + " AND RSPEC_REV.id = {$childID} " . " AND RSPEC.id = {$id} "; + + $recordset = $this->db->get_recordset($sql); + $rs = null; + if (! is_null($recordset)) { + // Decode users + $rs = $recordset[0]; + + if ($doUserDecode) { + $lbl_undef = lang_get('undefined'); + if (trim($rs['author_id']) != "") { + $user = tlUser::getByID($this->db, $rs['author_id']); + // need to manage deleted users + $rs['author'] = $lbl_undef; + if ($user) { + $rs['author'] = $user->getDisplayName(); + } + } + + if (trim($rs['modifier_id']) != "") { + $user = tlUser::getByID($this->db, $rs['modifier_id']); + // need to manage deleted users + $rs['modifier'] = $lbl_undef; + if ($user) { + $rs['modifier'] = $user->getDisplayName(); + } + } + } + } + return $rs; + } + + /** + * get analyse based on requirements and test specification + * + * @param integer $id: + * Req Spec id + * @return array Coverage in three internal arrays: covered, uncovered, nottestable REQ + * @author martin havlat + */ + private function get_coverage($id) + { + $output = array( + 'covered' => array(), + 'uncovered' => array(), + 'nottestable' => array() + ); + + // function get_requirements($id, $range = 'all', $testcase_id = null, $options=null, $filters = null) + $getOptions = array( + 'order_by' => " ORDER BY req_doc_id,title" + ); + $getFilters = array( + 'status' => VALID_REQ + ); + $validReq = $this->get_requirements($id, 'all', null, $getOptions, + $getFilters); + + // get not-testable requirements + $getFilters = array( + 'status' => NON_TESTABLE_REQ + ); + $output['nottestable'] = $this->get_requirements($id, 'all', null, + $getOptions, $getFilters); + + // get coverage + if (count($validReq)) { + foreach ($validReq as $req) { + // collect TC for REQ + $arrCoverage = $this->req_mgr->get_coverage($req['id']); + + if (! empty($arrCoverage)) { + // add information about coverage + $req['coverage'] = $arrCoverage; + $output['covered'][] = $req; + } else { + $output['uncovered'][] = $req; + } + } + } + return $output; + } + + /** + * get requirement coverage metrics + * + * @param integer $srs_id + * @return array results + * @author havlatm + */ + private function get_metrics($id) + { + $output = array( + 'notTestable' => 0, + 'total' => 0, + 'covered' => 0, + 'uncovered' => 0 + ); + $debugMsg = $this->debugMsg . __FUNCTION__; + $getFilters = array( + 'status' => NON_TESTABLE_REQ + ); + $output['notTestable'] = $this->get_requirements_count($id, 'all', null, + $getFilters); + + $sql = "/* $debugMsg */ SELECT count(0) AS cnt FROM {$this->tables['requirements']} WHERE srs_id={$id}"; + $output['total'] = $this->db->fetchFirstRowSingleColumn($sql, 'cnt'); + + $sql = "/* $debugMsg */ SELECT total_req FROM {$this->object_table} WHERE id={$id}"; + $output['expectedTotal'] = $this->db->fetchFirstRowSingleColumn($sql, + 'total_req'); + if ($output['expectedTotal'] == 0) { + $output['expectedTotal'] = $output['total']; + } + + $sql = "/* $debugMsg */ SELECT DISTINCT REQ.id " . + " FROM {$this->tables['requirements']} REQ " . + " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ.id=REQ_COV.req_id" . + " WHERE REQ.srs_id={$id} "; + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + $output['covered'] = count($rs); + } + $output['uncovered'] = $output['expectedTotal'] - $output['total']; + + return $output; + } + + /* + * function: get_all_in_testproject + * get info about all req spec defined for a testproject + * + * + * args: tproject_id + * [order_by] + * + * returns: null if no srs exits, or no srs exists for id + * array, where each element is a map with req spec data. + * + * map keys: + * id + * testproject_id + * title + * scope + * total_req + * type + * author_id + * creation_ts + * modifier_id + * modification_ts + */ + public function get_all_in_testproject($tproject_id, + $order_by = " ORDER BY title") + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT RSPEC.id,testproject_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," . + " RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," . + " RSPEC.modification_ts,NH.name AS title,NH.node_order " . + " FROM {$this->object_table} RSPEC, {$this->tables['nodes_hierarchy']} NH " . + " WHERE NH.id=RSPEC.id" . " AND testproject_id={$tproject_id}"; + + if (! is_null($order_by)) { + $sql .= $order_by; + } + return $this->db->get_recordset($sql); + } + + /* + * function: update + * + * args: item => map with following keys + * id,doc_id,name,scope,countReq,user_id,type,node_order + * + * returns: map with following keys: + * status_ok -> 1/0 + * msg -> some simple message, useful when status_ok ==0 + * revision_id -> useful when user request create new revision on update + * + */ + public function update($item, $options = null) + { + $result = array( + 'status_ok' => 1, + 'msg' => 'ok', + 'revision_id' => - 1 + ); + $my['options'] = array( + 'skip_controls' => false, + 'create_rev' => false, + 'log_message' => '' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $title = trimAndLimit($item['name']); + $doc_id = trimAndLimit($item['doc_id']); + + $path = $this->tree_mgr->get_path($item['id']); + $tproject_id = $path[0]['parent_id']; + $last_idx = count($path) - 1; + $parent_id = $last_idx == 0 ? null : $path[$last_idx]['parent_id']; + $chk = $this->check_main_data($title, $doc_id, $path[0]['parent_id'], + $parent_id, $item['id']); + + if ($chk['status_ok'] || $my['options']['skip_controls']) { + if (config_get('internal_links')->enable) { + $item['scope'] = req_link_replace($this->db, $item['scope'], + $tproject_id); + } + + $cnr = null; + if ($my['options']['create_rev']) { + $cnr = $this->create_new_revision($item['id'], + $item + $my['options']); + } else { + // missing piece, need to update all fields on last revision + $cnr = $this->update_revision($item); + } + + $this->db->db_now(); + $sql = " UPDATE {$this->object_table} " . " SET doc_id='" . + $this->db->prepare_string($doc_id) . "' " . + " WHERE id={$item['id']}"; + + if (! $this->db->exec_query($sql)) { + $result['msg'] = lang_get('error_updating_reqspec'); + $result['status_ok'] = 0; + } + + if ($result['status_ok']) { + // need to update node on tree + $sql = " UPDATE {$this->tables['nodes_hierarchy']} " . + " SET name='" . $this->db->prepare_string($title) . "'"; + if (isset($item['node_order']) && ! is_null($item['node_order'])) { + $sql .= ",node_order=" . intval($item['node_order']); + } + $sql .= " WHERE id={$item['id']}"; + + if (! $this->db->exec_query($sql)) { + $result['msg'] = lang_get('error_updating_reqspec'); + $result['status_ok'] = 0; + } + } + + if ($result['status_ok'] && ! is_null($cnr)) { + $result['revision_id'] = $cnr['id']; + } + } else { + $result['status_ok'] = $chk['status_ok']; + $result['msg'] = $chk['msg']; + } + return $result; + } + + /* + * function: delete + * deletes: + * Requirements spec + * Requirements spec custom fields values + * Requirements ( Requirements spec children ) + * Requirements custom fields values + * + * IMPORTANT/CRITIC: + * This function can used to delete a Req Specification that contains ONLY Requirements. + * This function is needed by tree class method: delete_subtree_objects() + * To delete a Req Specification that contains other Req Specification delete_deep() must be used. + * + * args: id: requirement spec id + * + * returns: message string + * ok if everything is ok + * + */ + public function delete($unsafe_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $id = intval($unsafe_id); + + // ATTENTION: CF linked to REVISION + $this->cfield_mgr->remove_all_design_values_from_node($id); + $result = $this->attachmentRepository->deleteAttachmentsFor($id, + "req_specs"); + + // delete requirements (one type req spec children) with all related data + // coverage, attachments, custom fields, etc + $requirements_info = $this->get_requirements($id); + if (! is_null($requirements_info)) { + $items = null; + foreach ($requirements_info as $req) { + $items[] = $req["id"]; + } + $this->req_mgr->delete($items); + } + + // delete revisions + $sqlx = array(); + $sqlx[] = "DELETE FROM {$this->tables['req_specs_revisions']} " . + "WHERE parent_id = {$id}"; + + $sqlx[] = "DELETE FROM {$this->tables['nodes_hierarchy']} " . + "WHERE parent_id = {$id} " . "AND node_type_id=" . + $this->node_types_descr_id['requirement_spec_revision']; + + foreach ($sqlx as $sql) { + $result = $this->db->exec_query("/* $debugMsg */" . $sql); + } + + // delete specification itself + $sqlx = array(); + $sqlx[] = "DELETE FROM {$this->object_table} WHERE id = {$id}"; + $sqlx[] = "DELETE FROM {$this->tables['nodes_hierarchy']} " . + "WHERE id = {$id} AND node_type_id=" . + $this->node_types_descr_id['requirement_spec']; + + foreach ($sqlx as $sql) { + $result = $this->db->exec_query("/* $debugMsg */" . $sql); + } + + // This is a poor implementation + if ($result) { + $result = 'ok'; + } else { + $result = 'The DELETE SRS request fails.'; + } + + return $result; + } + + /** + * delete_deep() + * + * Delete Req Specification, removing all children (other Req. Spec and Requirements) + */ + public function delete_deep($id) + { + $exclusion = ' AND NH.node_type_id <> ' . + intval($this->node_types_descr_id['requirement_spec_revision']); + $this->tree_mgr->delete_subtree_objects($id, $id, $exclusion, + array( + 'requirement' => 'exclude_my_children' + )); + $this->delete($id); + } + + /* + * function: get_requirements + * get LATEST VERSION OF requirements contained in a req spec + * + * + * args: id: req spec id + * [range]: default 'all' + * [testcase_id]: default null + * if !is_null, is used as filter + * [order_by] + * + * returns: array of rows + */ + public function get_requirements($id, $range = 'all', $testcase_id = null, + $options = null, $filters = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'order_by' => " ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id", + 'output' => 'standard', + 'outputLevel' => 'std', + 'decodeUsers' => true + ); + + $my['options'] = array_merge($my['options'], (array) $options); + + // null => do not filter + $my['filters'] = array( + 'status' => null, + 'type' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + switch ($my['options']['output']) { + case 'count': + $rs = 0; + break; + + case 'standard': + default: + $rs = null; + break; + } + + $tcase_filter = ''; + + // First Step - get only req info + $sql = "/* $debugMsg */ SELECT NH_REQ.id FROM {$this->tables['nodes_hierarchy']} NH_REQ "; + $addFields = ''; + switch ($range) { + case 'all': + break; + + case 'assigned': + $sql .= " JOIN {$this->tables['req_coverage']} + REQ_COV ON REQ_COV.req_id = NH_REQ.id "; + + if (! is_null($testcase_id)) { + $tcase_filter = " AND REQ_COV.testcase_id = {$testcase_id}"; + } + break; + } + + $sql = sprintf($sql, $addFields); + + $sql .= " WHERE NH_REQ.parent_id={$id} " . + " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} {$tcase_filter}"; + $itemSet = $this->db->fetchRowsIntoMap($sql, 'id'); + + if (! is_null($itemSet)) { + $reqSet = array_keys($itemSet); + $sql = "/* $debugMsg */ SELECT MAX(NH_REQV.id) AS version_id" . + " FROM {$this->tables['nodes_hierarchy']} NH_REQV " . + " WHERE NH_REQV.parent_id IN (" . implode(",", $reqSet) . ") " . + " GROUP BY NH_REQV.parent_id "; + + $latestVersionSet = $this->db->fetchRowsIntoMap($sql, 'version_id'); + $reqVersionSet = array_keys($latestVersionSet); + + $getOptions['order_by'] = $my['options']['order_by']; + $getOptions['outputLevel'] = $my['options']['outputLevel']; + $getOptions['decodeUsers'] = $my['options']['decodeUsers']; + + $rs = $this->req_mgr->get_by_id($reqSet, $reqVersionSet, null, + $getOptions, $my['filters']); + + switch ($my['options']['output']) { + case 'standard': + break; + + case 'count': + return ! is_null($rs) ? count($rs) : 0; + break; + } + } + + // get child requirements + $reqSql = "SELECT NH_REQ.id FROM {$this->tables['nodes_hierarchy']} NH_REQ WHERE NH_REQ.parent_id={$id}"; + + $itemSetAllFolder = $this->db->fetchRowsIntoMap($reqSql, 'id'); + + if (! is_null($itemSetAllFolder)) { + + foreach ($itemSetAllFolder as $value) { + + $sql = ''; + $tcase_filter = ''; + + // First Step - get only req info + $sql = "/* $debugMsg */ SELECT NH_REQ.id FROM {$this->tables['nodes_hierarchy']} NH_REQ "; + $addFields = ''; + switch ($range) { + case 'all': + break; + + case 'assigned': + $sql .= " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ_COV.req_id=NH_REQ.id "; + if (! is_null($testcase_id)) { + $tcase_filter = " AND REQ_COV.testcase_id={$testcase_id}"; + } + break; + } + + $sql = sprintf($sql, $addFields); + + $sql .= " WHERE NH_REQ.parent_id=" . $value['id'] . + " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} {$tcase_filter}"; + $itemSet = $this->db->fetchRowsIntoMap($sql, 'id'); + + if (! is_null($itemSet)) { + $reqSet = array_keys($itemSet); + $sql = "/* $debugMsg */ SELECT MAX(NH_REQV.id) AS version_id" . + " FROM {$this->tables['nodes_hierarchy']} NH_REQV " . + " WHERE NH_REQV.parent_id IN (" . implode(",", $reqSet) . + ") " . " GROUP BY NH_REQV.parent_id "; + + $latestVersionSet = $this->db->fetchRowsIntoMap($sql, + 'version_id'); + $reqVersionSet = array_keys($latestVersionSet); + + $getOptions['order_by'] = $my['options']['order_by']; + $getOptions['outputLevel'] = $my['options']['outputLevel']; + $getOptions['decodeUsers'] = $my['options']['decodeUsers']; + + if (is_null($rs)) { + $rs = $this->req_mgr->get_by_id($reqSet, $reqVersionSet, + null, $getOptions, $my['filters']); + } else { + $rs = array_merge($rs, + $this->req_mgr->get_by_id($reqSet, $reqVersionSet, + null, $getOptions, $my['filters'])); + } + + switch ($my['options']['output']) { + case 'standard': + break; + + case 'count': + $rs = ! is_null($rs) ? count($rs) : 0; + break; + } + } + } + } + + return $rs; + } + + /** + * get child requirements for get all testcase associate. + * args: id: requirement id + * + * returns: array of rows + */ + public function get_requirement_child_by_id($id) + { + $children = $this->get_requirement_child_by_id_req($id); + foreach ($children as $child) { + array_push($this->requirement_child_ids, $child); + $this->get_requirement_child_by_id($child["destination_id"]); + } + return $this->requirement_child_ids; + } + + /** + * get child requirements by id. + * args: id: requirement spec id + * + * returns: array of rows + */ + private function get_requirement_child_by_id_req($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT REQ_REL.destination_id, REQ.req_doc_id, NH.name FROM req_relations REQ_REL INNER JOIN nodes_hierarchy NH ON REQ_REL.destination_id = NH.id - JOIN {$this->tables['requirements']} REQ ON REQ_REL.destination_id = REQ.id where REQ_REL.source_id={$id}"; - $child = $this->db->get_recordset($sql); - return $child; -} - - /* - function: get_by_title - get req spec information using title as access key. - - args : title: req spec title - [tproject_id] - [parent_id] - [case_analysis]: control case sensitive search. - default 0 -> case sensivite search - - returns: map. - key: req spec id - value: srs info, map with folowing keys: - id - testproject_id - doc_id - title - scope - total_req - type - author_id - creation_ts - modifier_id - modification_ts - */ - function get_by_title($title,$tproject_id=null,$parent_id=null,$case_analysis=self::CASE_SENSITIVE) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $output=null; - $title=trim($title); - $the_title=$this->db->prepare_string($title); - $sql = "/* $debugMsg */ " . - " SELECT RSPEC.id,testproject_id,RSPEC.doc_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," . - " RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," . - " RSPEC.modification_ts,NH.name AS title " . - " FROM {$this->object_table} RSPEC, {$this->tables['nodes_hierarchy']} NH"; - - switch ($case_analysis) - { - case self::CASE_SENSITIVE: - $sql .= " WHERE NH.name='{$the_title}'"; - break; - - case self::CASE_INSENSITIVE: - $sql .= " WHERE UPPER(NH.name)='" . strtoupper($the_title) . "'"; - break; - } - $sql .= " AND RSPEC.id=NH.id "; - - - if( !is_null($tproject_id) ) - { - $sql .= " AND RSPEC.testproject_id={$tproject_id}"; - } - - if( !is_null($parent_id) ) - { - $sql .= " AND NH.parent_id={$parent_id}"; - } - - $sql .= " AND RSPEC.id=NH.id "; - $output = $this->db->fetchRowsIntoMap($sql,'id'); - - return $output; - } - - /* - function: check_title - Do checks on req spec title, to understand if can be used. - - Checks: - 1. title is empty ? - 2. does already exist a req spec with this title? - - args : title: req spec title - [parent_id]: default null -> do check for tile uniqueness system wide. - valid id: only inside parent_id with this id. - - [id]: req spec id. - [case_analysis]: control case sensitive search. - default 0 -> case sensivite search - - returns: - - */ - function check_title($title,$tproject_id=null,$parent_id=null,$id=null, - $case_analysis=self::CASE_SENSITIVE) - { - $ret['status_ok'] = 1; - $ret['msg'] = ''; - - $title = trim($title); - - if ($title == "") - { - $ret['status_ok'] = 0; - $ret['msg'] = lang_get("warning_empty_req_title"); - } - - if($ret['status_ok']) - { - $ret['msg']='ok'; - $rs = $this->get_by_title($title,$tproject_id,$parent_id,$case_analysis); - if(!is_null($rs) && (is_null($id) || !isset($rs[$id]))) - { - $ret['msg'] = sprintf(lang_get("warning_duplicate_req_title"),$title); - $ret['status_ok'] = 0; - } - } - return $ret; - } //function end - - - /* - function: check_main_data - Do checks on req spec title and doc id, to understand if can be used. - - Checks: - 1. title is empty ? - 2. doc is is empty ? - 3. does already exist a req spec with this title? - 4. does already exist a req spec with this doc id? - - VERY IMPORTANT: - $tlCfg->req_cfg->child_requirements_mgmt has effects on check on already - existent title or doc id. - - $tlCfg->req_cfg->child_requirements_mgmt == ENABLED => N level tree - title and doc id can not repited on ANY level of tree - - This is important due to unique index present on Database - ATTENTION: - Must be rethinked!!!! - - - args : title: req spec title - doc_id: req spec document id / code / short title - [parent_id]: default null -> do check for tile uniqueness system wide. - valid id: only inside parent_id with this id. - - [id]: req spec id. - [case_analysis]: control case sensitive search. - default 0 -> case sensivite search - - returns: - - */ - function check_main_data($title,$doc_id,$tproject_id=null,$parent_id=null,$id=null, - $case_analysis=self::CASE_SENSITIVE) - { - $cfg = config_get('req_cfg'); - - $my_parent_id = $parent_id; - - $ret['status_ok'] = 1; - $ret['msg'] = ''; - - $title = trim($title); - $doc_id = trim($doc_id); - - if ($title == "") - { - $ret['status_ok'] = 0; - $ret['msg'] = lang_get("warning_empty_req_title"); - } - - if ($doc_id == "") - { - $ret['status_ok'] = 0; - $ret['msg'] = lang_get("warning_empty_doc_id"); - } - - - if($ret['status_ok']) - { - $ret['msg']='ok'; - $rs = $this->getByDocID($doc_id,$tproject_id); - if(!is_null($rs) && (is_null($id) || !isset($rs[$id]))) - { - $info = current($rs); - $ret['msg'] = sprintf(lang_get("warning_duplicated_req_spec_doc_id"),$info['title'],$doc_id); - $ret['status_ok'] = 0; - } - } - - return $ret; - } //function end - - - - - - - - /* - function: - - args : - $nodes: array with req_spec in order - returns: - - */ - function set_order($map_id_order) - { - $this->tree_mgr->change_order_bulk($map_id_order); - } // set_order($map_id_order) - - - /* - function: - - args: - - returns: - - */ - function get_requirements_count($id, $range = 'all', $testcase_id = null,$filters=null) - { - // filters => array('status' => NON_TESTABLE_REQ, 'type' => 'X'); - $options = array('output' => 'count'); - $count = $this->get_requirements($id,$range,$testcase_id,$options,$filters); - return $count; - } - - - /** - * getReqTree - * - * Example of returned value ( is a recursive one ) - * ( - * [childNodes] => Array - * ([0] => Array - * ( [id] => 216 - * [parent_id] => 179 - * [node_type_id] => 6 - * [node_order] => 0 - * [node_table] => req_specs - * [name] => SUB-R - * [childNodes] => Array - * ([0] => Array - * ( [id] => 181 - * [parent_id] => 216 - * [node_type_id] => 7 - * [node_order] => 0 - * [node_table] => requirements - * [name] => Gamma Ray Emissions - * [childNodes] => - * ) - * [1] => Array - * ( [id] => 182 - * [parent_id] => 216 - * [node_type_id] => 7 - * [node_order] => 0 - * [node_table] => requirements - * [name] => Coriolis Effet - * [childNodes] => - * ) - * ) - * ) - * [1] => Array - * ( [id] => 217 - * [parent_id] => 179 - * [node_type_id] => 6 - * [node_order] => 0 - * [node_table] => req_specs - * [name] => SUB-R2 - * [childNodes] => Array - * ... - * - * - */ - function getReqTree($id) - { - $filters=null; - $options=array('recursive' => true); - $map = $this->tree_mgr->get_subtree($id,$filters,$options); - return $map; - } - - - /** - * exportReqSpecToXML - * create XML string with following req spec data - * - basic data (title, scope) - * - custom fields values - * - children: can be other req spec or requirements (tree leaves) - * - * Developed using exportTestSuiteDataToXML() as model - * - * @internal revision - */ - function exportReqSpecToXML($id,$tproject_id,$optionsForExport=array()) - { - // manage missing keys; recursive export by default - if( !array_key_exists('RECURSIVE',$optionsForExport) ){ - $optionsForExport['RECURSIVE'] = true; - } - - $relXmlData = ''; - $relationsCache = array(); - - $cfXML=null; - $xmlData = null; - if($optionsForExport['RECURSIVE']) - { - $cfXML = $this->customFieldValuesAsXML($id,$tproject_id); - $containerData = $this->get_by_id($id); - $xmlData = "' . "\n". - "\t\n" . - "\t\n" . - "\t\n" . - "\t\n" . - "\t\n\t\t{$cfXML}"; - } - - // Add attachments info - if (isset($optionsForExport['ATTACHMENTS']) && $optionsForExport['ATTACHMENTS']) - { - $attachments=null; - // get all attachments - $attachmentInfos = $this->attachmentRepository->getAttachmentInfosFor($id,$this->attachmentTableName,'id'); - - // get all attachments content and encode it in base64 - if ($attachmentInfos) - { - foreach ($attachmentInfos as $attachmentInfo) - { - $aID = $attachmentInfo["id"]; - $content = $this->attachmentRepository->getAttachmentContent($aID, $attachmentInfo); - - if ($content != null) - { - $attachments[$aID]["id"] = $aID; - $attachments[$aID]["name"] = $attachmentInfo["file_name"]; - $attachments[$aID]["file_type"] = $attachmentInfo["file_type"]; - $attachments[$aID]["title"] = $attachmentInfo["title"]; - $attachments[$aID]["date_added"] = $attachmentInfo["date_added"]; - $attachments[$aID]["content"] = base64_encode($content); - } - } - } - - if( !is_null($attachments) && count($attachments) > 0 ) - { - $attchRootElem = "\t\n{{XMLCODE}}\t\n"; - $attchElemTemplate = "\t\t\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\n"; - - $attchDecode = array ("||ATTACHMENT_ID||" => "id", "||ATTACHMENT_NAME||" => "name", - "||ATTACHMENT_FILE_TYPE||" => "file_type", "||ATTACHMENT_TITLE||" => "title", - "||ATTACHMENT_DATE_ADDED||" => "date_added", "||ATTACHMENT_CONTENT||" => "content"); - $xmlData .= exportDataToXML($attachments,$attchRootElem,$attchElemTemplate,$attchDecode,true); - } - } - - $req_spec = $this->getReqTree($id); - $childNodes = isset($req_spec['childNodes']) ? $req_spec['childNodes'] : null ; - if( !is_null($childNodes) ) - { - $loop_qty=sizeof($childNodes); - for($idx = 0;$idx < $loop_qty;$idx++) - { - $cNode = $childNodes[$idx]; - $nTable = $cNode['node_table']; - if($optionsForExport['RECURSIVE'] && $cNode['node_table'] == 'req_specs') - { - $xmlData .= $this->exportReqSpecToXML($cNode['id'],$tproject_id,$optionsForExport); - } - else if ($cNode['node_table'] == 'requirements') - { - $xmlData .= $this->req_mgr->exportReqToXML($cNode['id'],$tproject_id,$optionsForExport['ATTACHMENTS']); - - $relations = $this->req_mgr->get_relations($cNode['id']); - if( !is_null($relations['relations']) && count($relations['relations']) > 0 ) - { - foreach($relations['relations'] as $key => $rel) - { - // If we have already found this relation, skip it. - if ( !in_array($rel['id'], $relationsCache) ) - { - // otherwise export it to XML. - $testproject_id = $this->relationsCfg->interProjectLinking ? $tproject_id : null; - $relXmlData .= $this->req_mgr->exportRelationToXML($rel,$tproject_id, - $this->relationsCfg->interProjectLinking); - $relationsCache[] = $rel['id']; - } - } - } - } - } - - // after we scanned all relations and exported all relations to xml, let's output it to the XML buffer - $xmlData .= $relXmlData; - } - - if ($optionsForExport['RECURSIVE']) - { - $xmlData .= "\n"; - } - return $xmlData; - } - - /** - * xmlToReqSpec - * - * @param object $source: - * $source->type: possible values 'string', 'file' - * $source->value: depends of $source->type - * 'string' => xml string - * 'file' => path name of XML file - * - */ - function xmlToReqSpec($source) - { - $status_ok=true; - $xml_string=null; - $req_spec=null; - switch( $source->type ) - { - case 'string': - $xml_string = $source->value; - break; - - case 'file': - $xml_file = $source->value; - $status_ok=!(($xml_object=@$this->simplexml_load_file_helper($xml_file)) === FALSE); - break; - } - - if( $status_ok ) - { - - } - - return $req_spec; - } - - /** - * xmlToMapReqSpec - * - */ - function xmlToMapReqSpec($xml_item,$level=0) - { - static $iterations=0; - static $mapped; - - // Attention: following PHP Manual SimpleXML documentation, Please remember to cast - // before using data from $xml, - if( is_null($xml_item) ) - { - return null; - } - - // used to reset static structures if calling this in loop - if($level == 0) - { - $iterations = 0; - $mapped = null; - } - - $dummy=array(); - $dummy['node_order'] = (int)$xml_item->node_order; - $dummy['scope'] = (string)$xml_item->scope; - $dummy['type'] = (int)$xml_item->type; - $dummy['total_req'] = (int)$xml_item->total_req; - $dummy['level'] = $level; - $depth=$level+1; - - foreach($xml_item->attributes() as $key => $value) - { - $dummy[$key] = (string)$value; // See PHP Manual SimpleXML documentation. - } - - - if( property_exists($xml_item,'custom_fields') ) - { - $dummy['custom_fields']=array(); - foreach($xml_item->custom_fields->children() as $key) - { - $dummy['custom_fields'][(string)$key->name]= (string)$key->value; - } - } - - if( property_exists($xml_item,'attachments') ) - { - $dummy['attachments'] = array(); - foreach($xml_item->attachments->children() as $attachment) - { - $attach_id = (int)$attachment->id; - $dummy['attachments'][$attach_id]['id'] = (int)$attachment->id; - $dummy['attachments'][$attach_id]['name'] = (string)$attachment->name; - $dummy['attachments'][$attach_id]['file_type'] = (string)$attachment->file_type; - $dummy['attachments'][$attach_id]['title'] = (string)$attachment->title; - $dummy['attachments'][$attach_id]['date_added'] = (string)$attachment->date_added; - $dummy['attachments'][$attach_id]['content'] = (string)$attachment->content; - } - } - $mapped[]=array('req_spec' => $dummy, 'requirements' => null, - 'level' => $dummy['level']); - - // Process children - if( property_exists($xml_item,'requirement') ) - { - $loop2do=count($xml_item->requirement); - for($idx=0; $idx <= $loop2do; $idx++) - { - $xml_req=$this->req_mgr->xmlToMapRequirement($xml_item->requirement[$idx]); - if(!is_null($xml_req)) - { - $fdx=count($mapped)-1; - $mapped[$fdx]['requirements'][]=$xml_req; - } - } - } - - if( property_exists($xml_item,'relation') ) - { - $loop3do=count($xml_item->relation); - for($idx=0; $idx <= $loop3do; $idx++) - { - $rel=$this->req_mgr->convertRelationXmlToRelationMap($xml_item->relation[$idx]); - if(!is_null($rel)) - { - $fdx=count($mapped)-1; - $mapped[$fdx]['relations'][]=$rel; - } - } - } - - if( property_exists($xml_item,'req_spec') ) - { - $loop2do=count($xml_item->req_spec); - for($idx=0; $idx <= $loop2do; $idx++) - { - $this->xmlToMapReqSpec($xml_item->req_spec[$idx],$depth); - } - } - - return $mapped; - } - - - // Custom field related functions - - /* - function: get_linked_cfields - Get all linked custom fields. - Remember that custom fields are defined at system wide level, and - has to be linked to a testproject, in order to be used. - - - args: credentials, map with following keys - item_id: Req. Spec REVISION ID (can be NULL if parent_id IS NOT NULL) - parent_id: Req. Spec ID (can be NULL if item_id IS NOT NULL) - tproject_id:node id of parent testproject of requirement spec. - need to understand to which testproject requirement spec belongs. - this information is vital, to get the linked custom fields. - Presence /absence of this value changes starting point - on procedure to build tree path to get testproject id. - - null -> use requirement spec id as starting point. - !is_null -> use this value as starting point. - - returns: map/hash - key: custom field id - value: map with custom field definition and value assigned for choosen req spec, - with following keys: - - id: custom field id - name - label - type: custom field type - possible_values: for custom field - default_value - valid_regexp - length_min - length_max - show_on_design - enable_on_design - show_on_execution - enable_on_execution - display_order - value: value assigned to custom field for this req spec - null if for this req spec custom field was never edited. - - node_id: req spec id - null if for this req spec, custom field was never edited. - - - @internal revisions - - */ - function get_linked_cfields($credentials) - { - $who = array('item_id' => null, 'parent_id' => null, 'tproject_id' => null); - $who = array_merge($who, (array)$credentials); - - $tproject_id = $who['tproject_id']; - $hasParentInfo = !is_null($who['parent_id']) && ($who['parent_id'] > 0); - - if($hasParentInfo && (is_null($tproject_id) || is_null($who['item_id']) )) - { - // will get info for LAST revision - $info = $this->get_by_id($who['parent_id'],array('output' => 'credentials')); - $tproject_id = $info['testproject_id']; - $who['item_id'] = $info['revision_id']; - } - $cf_map = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id,cfield_mgr::CF_ENABLED,null, - 'requirement_spec',$who['item_id']); - return $cf_map; - } - - - /* - function: html_table_of_custom_field_inputs - Return html code, implementing a table with custom fields labels - and html inputs, for choosen req spec. - Used to manage user actions on custom fields values. - - - args: $id - [tproject_id]: node id of testproject (req spec parent). - this information is vital, to get the linked custom fields, - because custom fields are system wide, but to be used are - assigned to a test project. - is null this method or other called will use get_path() - method to get test project id. - - [parent_id]: Need to e rethinked, may be remove (20090111 - franciscom) - - [$name_suffix]: must start with '_' (underscore). - Used when we display in a page several items - (example during test case execution, several test cases) - that have the same custom fields. - In this kind of situation we can use the item id as name suffix. - - - returns: html string - - */ - function html_table_of_custom_field_inputs( $id,$child_id,$tproject_id=null,$parent_id=null, - $name_suffix='',$input_values = null) - { - $NO_WARNING_IF_MISSING=true; - $cf_smarty = ''; - - $idCard = array('parent_id' => $id, 'item_id' => $child_id, 'tproject_id' => $tproject_id); - $cf_map = $this->get_linked_cfields($idCard); - $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map,$name_suffix,$input_values); - - return $cf_smarty; - } - - - - /* - function: html_table_of_custom_field_values - Return html code, implementing a table with custom fields labels - and custom fields values, for choosen req spec. - You can think of this function as some sort of read only version - of html_table_of_custom_field_inputs. - - - args: $id - - returns: html string - - */ - function html_table_of_custom_field_values($id,$child_id,$tproject_id) - { - $NO_WARNING_IF_MISSING=true; - $cf_smarty = ''; - - // $cf_map = $this->get_linked_cfields($id,$child_id,$tproject_id); - $idCard = array('parent_id' => $id, 'item_id' => $child_id, 'tproject_id' => $tproject_id); - $cf_map = $this->get_linked_cfields($idCard); - $show_cf = config_get('custom_fields')->show_custom_fields_without_value; - - if(!is_null($cf_map)) - { - foreach($cf_map as $cf_id => $cf_info) - { - // if user has assigned a value, then node_id is not null - if($cf_info['node_id'] || $show_cf) - { - $label = str_replace(TL_LOCALIZE_TAG,'', - lang_get($cf_info['label'],null,$NO_WARNING_IF_MISSING)); - - $cf_smarty .= '' . - htmlspecialchars($label) . ":" . - $this->cfield_mgr->string_custom_field_value($cf_info,$id) . - "\n"; - } - } - - if(trim($cf_smarty) != "") - { - $cf_smarty = "" . $cf_smarty . "
    "; - } - } - return $cf_smarty; - } // function end - - - /* - function: values_to_db - write values of custom fields to db - - args: hash: - key: custom_field__. - Example custom_field_0_67 -> 0=> string field - - node_id: req spec id - - [cf_map]: hash -> all the custom fields linked and enabled - that are applicable to the node type of $node_id. - - For the keys not present in $hash, we will write - an appropriate value according to custom field - type. - - This is needed because when trying to udpate - with hash being $_REQUEST, $_POST or $_GET - some kind of custom fields (checkbox, list, multiple list) - when has been deselected by user. - - [hash_type] - - rev: - */ - function values_to_db($hash,$node_id,$cf_map=null,$hash_type=null) - { - $this->cfield_mgr->design_values_to_db($hash,$node_id,$cf_map,$hash_type); - } - - - /** - * customFieldValuesAsXML - * - * @param $id: requirement spec id - * @param $tproject_id: test project id - * - * - */ - function customFieldValuesAsXML($id,$tproject_id) - { - $xml = null; - - $idCard = array('parent_id' => $id, 'item_id' => null, 'tproject_id' => $tproject_id); - $cfMap = $this->get_linked_cfields($idCard); - if( !is_null($cfMap) && count($cfMap) > 0 ) - { - $xml = $this->cfield_mgr->exportValueAsXML($cfMap); - } - return $xml; - } - - - /** - * create a req spec tree on system from $xml data - * - * - * @internal revisions - */ - function createFromXML($xml,$tproject_id,$parent_id,$author_id,$filters = null,$options=null) - { - static $labels; - static $missingCfMsg; - static $linkedCF; - static $messages; - static $doProcessCF = false; - - // init static items - if( is_null($labels) ) - { - $labels = array('import_req_spec_created' => '', 'import_req_spec_skipped' => '', - 'import_req_spec_updated' => '', 'import_req_spec_ancestor_skipped' => '', - 'import_req_created' => '','import_req_skipped' =>'', 'import_req_updated' => ''); - foreach($labels as $key => $dummy) - { - $labels[$key] = lang_get($key); - } - - $messages = array(); - $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); - $messages['cfield'] = lang_get('cf_value_not_imported_missing_cf_on_testproject'); - - $linkedCF = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id,cfield_mgr::CF_ENABLED,null, - 'requirement_spec',null,'name'); - $doProcessCF = true; - } - - $user_feedback = null; - $copy_reqspec = null; - $copy_req = null; - $getOptions = array('output' => 'minimun'); - $my['options'] = array('skipFrozenReq' => true); - $my['options'] = array_merge($my['options'], (array)$options); - - $items = $this->xmlToMapReqSpec($xml); - - $has_filters = !is_null($filters); - if($has_filters) - { - if(!is_null($filters['requirements'])) - { - foreach($filters['requirements'] as $reqspec_pos => $requirements_pos) - { - $copy_req[$reqspec_pos] = is_null($requirements_pos) ? null : array_keys($requirements_pos); - } - } - } - - $loop2do = count($items); - $container_id[0] = (is_null($parent_id) || $parent_id == 0) ? $tproject_id : $parent_id; - - // items is an array of req. specs - $skip_level = -1; - for($idx = 0;$idx < $loop2do; $idx++) - { - $rspec = $items[$idx]['req_spec']; - $depth = $rspec['level']; - if( $skip_level > 0 && $depth >= $skip_level) - { - $msgID = 'import_req_spec_ancestor_skipped'; - $user_feedback[] = array('doc_id' => $rspec['doc_id'],'title' => $rspec['title'], - 'import_status' => sprintf($labels[$msgID],$rspec['doc_id'])); - continue; - } - $req_spec_order = isset($rspec['node_order']) ? $rspec['node_order'] : 0; - - // Check if req spec with same DOCID exists, inside container_id - // If there is a hit - // We will go in update - // If Check fails, need to repeat check on WHOLE Testproject. - // If now there is a HIT we can not import this branch - // If Check fails => we can import creating a new one. - // - // Important thing: - // Working in this way, i.e. doing check while walking the structure to import - // we can end importing struct with 'holes'. - // - $check_in_container = $this->getByDocID($rspec['doc_id'],$tproject_id,$container_id[$depth],$getOptions); - $hasAttachments = array_key_exists('attachments',$rspec); - - $skip_level = $depth + 1; - $result['status_ok'] = 0; - $msgID = 'import_req_spec_skipped'; - - if(is_null($check_in_container)) - { - $check_in_tproject = $this->getByDocID($rspec['doc_id'],$tproject_id,null,$getOptions); - if(is_null($check_in_tproject)) - { - $importMode = 'creation'; - $msgID = 'import_req_spec_created'; - $result = $this->create($tproject_id,$container_id[$depth],$rspec['doc_id'],$rspec['title'], - $rspec['scope'],$rspec['total_req'],$author_id,$rspec['type'],$req_spec_order); - } - } - else - { - $importMode = 'update'; - $msgID = 'import_req_spec_updated'; - $reqSpecID = key($check_in_container); - $item = array('id' => $reqSpecID, 'name' => $rspec['title'],'doc_id' => $rspec['doc_id'], - 'scope' => $rspec['scope'],'total_req' => $rspec['total_req'],'modifier_id' => $author_id, - 'type' => $rspec['type'],'node_order' => $req_spec_order); - - // ATTENTION update return key => revision_id, because CF values are saved at REVISION LEVEL - $result = $this->update($item); - $result['id'] = $reqSpecID; - } - $user_feedback[] = array('doc_id' => $rspec['doc_id'],'title' => $rspec['title'], - 'import_status' => sprintf($labels[$msgID],$rspec['doc_id'])); - - // process attachements for creation and update - if($result['status_ok'] && $hasAttachments) - { - $addAttachmentsResponse = $this->processAttachments( $importMode, $result['id'], $rspec['attachments'], $feedbackMsg ); - } - // display only problems during attachments import - if( isset($addAttachmentsResponse) && !is_null($addAttachmentsResponse) ) - { - foreach($addAttachmentsResponse as $att_name){ - $user_feedback[] = array('doc_id' => $rspec['doc_id'],'title' => $rspec['title'], - 'import_status' => sprintf(lang_get('import_req_spec_attachment_skipped'),$att_name)); - } - } - if( $result['status_ok'] && $doProcessCF && isset($rspec['custom_fields']) && !is_null($rspec['custom_fields']) ) - { - $cf2insert = null; - foreach($rspec['custom_fields'] as $cfname => $cfvalue) - { - $cfname = trim($cfname); - if( isset($linkedCF[$cfname]) ) - { - $cf2insert[$linkedCF[$cfname]['id']]=array('type_id' => $linkedCF[$cfname]['type'],'cf_value' => $cfvalue); - } - else - { - if( !isset($missingCfMsg[$cfname]) ) - { - $missingCfMsg[$cfname] = sprintf($messages['cfield'],$cfname,$labels['requirement']); - } - $user_feedback[] = array('doc_id' => $rspec['docid'],'title' => $rspec['title'], - 'import_status' => $missingCfMsg[$cfname]); - } - } - if( !is_null($cf2insert) ) - { - $this->cfield_mgr->design_values_to_db($cf2insert,$result['revision_id'],null,'simple'); - } - } - - - if($result['status_ok']) - { - $skip_level = -1; - $container_id[$depth+1] = ($reqSpecID = $result['id']); - $reqSet = $items[$idx]['requirements']; - $create_req = (!$has_filters || isset($copy_req[$idx])) && !is_null($reqSet); - if($create_req) - { - $items_qty = isset($copy_req[$idx]) ? count($copy_req[$idx]) : count($reqSet); - $keys2insert = isset($copy_req[$idx]) ? $copy_req[$idx] : array_keys($reqSet); - for($jdx = 0;$jdx < $items_qty; $jdx++) - { - $req = $reqSet[$keys2insert[$jdx]]; - $dummy = $this->req_mgr->createFromMap($req,$tproject_id,$reqSpecID,$author_id, null,$my['options']); - $user_feedback = array_merge($user_feedback,$dummy); - } - } // if($create_req) - - if(isset($items[$idx]['relations'])) - { - $relationsMap = $items[$idx]['relations']; - $numberOfRelations = count($relationsMap); - for($jdx=0; $jdx < $numberOfRelations; $jdx++) - { - $rel = $relationsMap[$jdx]; - $dummy = $this->req_mgr->createRelationFromMap($rel, $tproject_id, $author_id); - $user_feedback = array_merge($user_feedback,$dummy); - } - } - } // if($result['status_ok']) - } - return $user_feedback; - } - - - - /* - function: getByDocID - get req spec information using document ID as access key. - - args : doc_id: - [tproject_id] - [parent_id] - [options]: - [case]: control case sensitive search. - default 0 -> case sensivite search - [access_key]: - [check_criteria]: - [output]: - - returns: map. - key: req spec id - value: srs info, map with folowing keys: - id - testproject_id - title - scope - total_req - type - author_id - creation_ts - modifier_id - modification_ts - - */ - function getByDocID($doc_id,$tproject_id=null,$parent_id=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array( 'check_criteria' => '=', 'access_key' => 'id', - 'case' => 'sensitive', 'output' => 'standard'); - $my['options'] = array_merge($my['options'], (array)$options); - - - $output=null; - $the_doc_id=$this->db->prepare_string(trim($doc_id)); - - switch($my['options']['check_criteria']) { - case '=': - default: - $check_criteria = " = '{$the_doc_id}' "; - break; - - case 'like': - $check_criteria = " LIKE '{$the_doc_id}%' "; - break; - } - - $where = " WHERE RSPEC.doc_id {$check_criteria} "; - if( !is_null($tproject_id) ) - { - $where .= " AND RSPEC.testproject_id={$tproject_id}"; - } - if( !is_null($parent_id) ) - { - $where .= " AND NH_RSPEC.parent_id={$parent_id}"; - } - - // Developer Note: - // a mix of SQL ignorance and MySQL relaxed SQL on GROUP BY - // Fortunatelly Postgres do the right job - // - // - // First step get MAX revision - // will trust in this that max(revision) has also always max(revision_id) - // ( but really can be on a differente way ? ), in order to use a simple logic. - // - $sql_max = " /* $debugMsg */ SELECT MAX(RSPEC_REV.id) AS rev_id" . - " FROM {$this->tables['req_specs']} RSPEC " . - " JOIN {$this->tables['req_specs_revisions']} RSPEC_REV " . - " ON RSPEC_REV.parent_id = RSPEC.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC " . - " ON NH_RSPEC.id = RSPEC.id " . - $where . ' GROUP BY RSPEC_REV.parent_id '; - - $maxi = (array)$this->db->fetchRowsIntoMap($sql_max,'rev_id');; - if( count($maxi) > 0) - { - $sql = " /* $debugMsg */ SELECT RSPEC.id,RSPEC.testproject_id,RSPEC.doc_id,NH_RSPEC.name AS title, " . - " RSPEC_REV.revision "; - - switch($my['options']['output']) - { - case 'standard': - $sql .= " ,RSPEC_REV.total_req, RSPEC_REV.scope,RSPEC_REV.type," . - " RSPEC_REV.author_id,RSPEC_REV.creation_ts, " . - " RSPEC_REV.modifier_id,RSPEC_REV.modification_ts"; - break; - - case 'minimun': - break; - } - - $sql .= " FROM {$this->tables['req_specs']} RSPEC " . - " JOIN {$this->tables['req_specs_revisions']} RSPEC_REV " . - " ON RSPEC_REV.parent_id = RSPEC.id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC " . - " ON NH_RSPEC.id = RSPEC.id "; - - $sql .= $where . ' AND RSPEC_REV.id IN (' . implode(",",array_keys($maxi)) . ') '; - $output = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']); - } - - return $output; - } - - - /* - function: copy_to - deep copy one req spec to another parent (req spec or testproject). - - - args : id: req spec id (source or copy) - parent_id: - user_id: who is requesting copy operation - [options] - - returns: map with following keys: - status_ok: 0 / 1 - msg: 'ok' if status_ok == 1 - id: new created if everything OK, -1 if problems. - - rev : - */ - function copy_to($id, $parent_id, $tproject_id, $user_id,$options = null) { - - static $get_tree_nt2exclude; - if(!$get_tree_nt2exclude) { - $get_tree_nt2exclude = - array('req_version' => 'exclude_me','req_revision' => 'exclude_me', - 'requirement_spec_revision' => 'exclude_me'); - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $op = array('status_ok' => 1, 'msg' => 'ok', 'id' => -1 , 'mappings' => null); - $my['options'] = array('copy_also' => null); - $my['options'] = array_merge($my['options'], (array)$options); - - - $item_info = $this->get_by_id($id); - $target_doc = $this->generateDocID($id,$tproject_id); - $new_item = $this->create($tproject_id,$parent_id,$target_doc,$item_info['title'], - $item_info['scope'],$item_info['total_req'], - $item_info['author_id'],$item_info['type'],$item_info['node_order']); - - $op = $new_item; - if( $new_item['status_ok'] ) { - $op['mappings'][$id] = $new_item['id']; - $op['mappings']['req_spec'] = array(); - $op['mappings']['req'] = array(); - $op['mappings']['req_version'] = array(); - $op['mappings']['req_tree'] = array(); - - - $idCard = array('parent_id' => $id, 'tproject_id' => $tproject_id); - $this->copy_cfields($idCard,$new_item['id']); - - $this->copy_attachments($id,$new_item['id']); - - // Now loop to copy all items inside it - // null is OK, because $id is a req spec, there is no risk - // to copy/traverse wrong node types. - // Hmmm may be req_revi ??? - $my['filters']['exclude_node_types'] = $get_tree_nt2exclude; - $subtree = $this->tree_mgr->get_subtree($id,$my['filters'],array('output' => 'essential')); - - if (!is_null($subtree)) { - $reqMgr = new requirement_mgr($this->db); - $parent_decode=array(); - $parent_decode[$id]=$new_item['id']; - - // using reference has to avoid duplicate => memory consumption - // (at least this is info found on Internet) - // Few test indicates that it's true, but that using a counter - // is still better. - // - $loop2do = count($subtree); - for($sdx=0; $sdx <= $loop2do; $sdx++) { - $elem = &$subtree[$sdx]; - $the_parent_id = isset($parent_decode[$elem['parent_id']]) ? $parent_decode[$elem['parent_id']] : null; - - switch ($elem['node_type_id']) { - case $this->node_types_descr_id['requirement']: - $ret = $reqMgr->copy_to($elem['id'],$the_parent_id,$user_id, - $tproject_id,$my['options']); - - $op['status_ok'] = $ret['status_ok']; - $op['mappings']['req'] += $ret['mappings']['req']; - $op['mappings']['req_version'] += $ret['mappings']['req_version']; - $op['mappings']['req_tree'] += $ret['mappings']['req_tree']; - break; - - case $this->node_types_descr_id['requirement_spec']: - $item_info = $this->get_by_id($elem['id']); - - // hmm, when copy_to() is called because we are duplicating - // a test project, call to generateDocID(), can be avoided. - // we have IMHO an absolute inexistent risk. - $target_doc = $this->generateDocID($elem['id'],$tproject_id); - - $ret = $this->create($tproject_id,$the_parent_id,$target_doc, - $item_info['title'], - $item_info['scope'],$item_info['total_req'], - $item_info['author_id'],$item_info['type'], - $item_info['node_order']); - - $parent_decode[$elem['id']]=$ret['id']; - $op['mappings']['req_spec'][$elem['id']] = $ret['id']; - - if( ($op['status_ok'] = $ret['status_ok']) ) { - // try to reduce memory usage - // $idCard = array('parent_id' => $elem['id'], - // 'tproject_id' => $tproject_id); - $this->copy_cfields(array('parent_id' => $elem['id'], - 'tproject_id' => $tproject_id), - $ret['id']); - } - break; - } - - if( $op['status_ok'] == 0 ) { - break; - } - } - } - } - return $op; - } - - - /* - function: copy_cfields - Get all cfields linked to item with the values presents for $from_id, - item we are using as source for our copy. - - args: from_identity: source credentianls (complex type) - array('parent_id' => , 'item_id' => , 'tproject_id' => ); - - to_id: target item id (simple type) - - returns: - - - */ - function copy_cfields($from_identity,$to_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $cfmap_from=$this->get_linked_cfields($from_identity); - - $cfield=null; - if( !is_null($cfmap_from) ) { - foreach($cfmap_from as $key => $value) { - $cfield[$key]=array("type_id" => $value['type'], - "cf_value" => $value['value']); - } - } - $this->cfield_mgr->design_values_to_db($cfield,$to_id,null,'tcase_copy_cfields'); - } - - - - /** - * processAttachments - * - * Analyze attachments info related to req spec to define if the the attachment has to be added. - * attachments are ignored only if a attachment with the same ID is already linked to the target ReqSpec. - * - * return an array of all attachments names of IDs already linked to target ReqSpec. - * - */ - - function processAttachments($importMode, $rs_id, $attachments, $feedbackMsg ) - { - $tables = tlObjectWithDB::getDBTables(array('req_specs','attachments')); - - $knownAttachments = array(); - foreach( $attachments as $attachment ) - { - $addAttachment = true; - if($importMode == 'update'){ - // try to bypass the importation of already known attachments. - // Check in database if the attachment with the same ID is linked to the rspec with the same internal ID - // The couple attachment ID + InternalID is used as a kind of signature to avoid duplicates. - // If signature is not precise enough, could add the use of attachment timestamp (date_added in XML file). - $sql = " SELECT ATT.id from {$tables['attachments']} ATT " . - " WHERE ATT.id='{$this->db->prepare_string($attachment[id])}' " . - " AND ATT.fk_id={$rs_id} "; - $rsx=$this->db->get_recordset($sql); - $addAttachment = ( is_null($rsx) || count($rsx) < 1 ); - if( $addAttachment === false ){ // inform user that the attachment has been skipped - $knownAttachments[] = $attachment['name']; - } - } - if($addAttachment){ - $attachRepo = tlAttachmentRepository::create($this->db); - $fileInfo = $attachRepo->createAttachmentTempFile( $attachment['content'] ); - $fileInfo['name'] = $attachment['name']; - $fileInfo['type'] = $attachment['file_type']; - $attachRepo->insertAttachment( $rs_id, $tables['req_specs'], $attachment['title'], $fileInfo); - } - } - return $knownAttachments; - } - - - /** - * - * - */ - function generateDocID($id, $tproject_id) - { - $item_info = $this->get_by_id($id); - - // Check if another req with same DOC ID exists on target container, - // If yes generate a new DOC ID - $getOptions = array('check_criteria' => 'like', 'access_key' => 'doc_id'); - $itemSet = $this->getByDocID($item_info['doc_id'],$tproject_id,null,$getOptions); - $target_doc = $item_info['doc_id']; - $instance = 1; - if( !is_null($itemSet) ) - { - // doc_id has limited size => we need to be sure that generated id - // will not exceed DB size - $nameSet = array_flip(array_keys($itemSet)); - - // 6 magic from " [xxx]" - $prefix = trim_and_limit($item_info['doc_id'],$this->field_size->docid-6); - $target_doc = $prefix . " [{$instance}]"; - while( isset($nameSet[$target_doc]) ) - { - $instance++; - $target_doc = $prefix . " [{$instance}]"; - } - } - return $target_doc; - } - - /** - * - * - */ - function getFirstLevelInTestProject($tproject_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT * from {$this->tables['nodes_hierarchy']} " . - " WHERE parent_id = {$tproject_id} " . - " AND node_type_id = {$this->node_types_descr_id['requirement_spec']} " . - " ORDER BY node_order,id"; - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - return $rs; - } - - - /** - * IMPORTANT NOTICE - * Only information regarding basic tables is created. - * This means THAT NOTHING is done (example) on custom fields, or other - * items that are related/linked to revisions. - * - */ - function create_revision($rspecID,$item) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array('msg' => 'ok','status_ok' => 1,'id' => -1); - $ret['id'] = $this->tree_mgr->new_node($rspecID,$this->node_types_descr_id['requirement_spec_revision']); - - $optActorPairs = array('author_id' => 'creation_ts', 'modifier_id' => 'modification_ts'); - $val2add = ''; - $fields2insert = 'parent_id,id,revision,status,doc_id,name,scope,type,log_message'; - - - foreach($optActorPairs as $main => $sec) - { - if( isset($item[$main]) && is_numeric($item[$main]) ) - { - $fields2insert .= ',' . $main . ',' . $sec; - $ts = isset($item[$sec]) ? $item[$sec] : $this->db->db_now(); - $val2add .= ',' . intval($item[$main]) . ',' . $ts; - } - } - $optIntKeys = array('status' => 1); - foreach($optIntKeys as $field => $default) - { - $item[$field] = isset($item[$field]) ? $item[$field] : $default; - } - - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['req_specs_revisions']} " . - " ($fields2insert) " . - " VALUES({$rspecID}" . "," . $ret['id'] . "," . intval($item['revision']) . "," . - intval($item['status']) . ",'" . - $this->db->prepare_string($item['doc_id']) . "','" . - $this->db->prepare_string($item['name']) . "','" . - $this->db->prepare_string($item['scope']) . "','" . - $this->db->prepare_string($item['type']) . "','" . - $this->db->prepare_string($item['log_message']) . "'" . $val2add . ")"; - // echo $sql . '
    '; die(); - - $result = $this->db->exec_query($sql); - if ($result) - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . - " SET name='" . $this->db->prepare_string($item['name']) . "' " . - " WHERE id={$ret['id']} "; - // echo $sql . '
    '; - $this->db->exec_query($sql); - } - else - { - $ret['msg'] = $this->db->error_msg(); - $ret['status_ok'] = 0; - $ret['id'] = -1; - } - - return $ret; - } - - - /** - * - */ - function create_new_revision($rspecID,$item) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array('msg' => 'ok','status_ok' => 1,'id' => -1); - - // Needed to get higher revision NUMBER, to generata new NUMBER - $source_info = $this->get_last_child_info($rspecID); - $current_rev = 0; - if( !is_null($source_info) ) - { - $current_rev = $source_info['revision']; - } - $current_rev++; - $item['revision'] = $current_rev++; - - $ret = $this->create_revision($rspecID,$item); - return $ret; - } - - /** - * @param id: parent id - * @param child_type: 'revision' - * - * @return - */ - function get_last_child_info($id, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('child_type' => 'revision', 'output' => 'full'); - $my['options'] = array_merge($my['options'], (array)$options); - - $info = null; - $target_cfg = array('revision' => array('table'=> 'req_specs_revisions', 'field' => 'revision')); - - $child_type = $my['options']['child_type']; // just for readability - $table = $target_cfg[$child_type]['table']; - $field = $target_cfg[$child_type]['field']; - - $sql = " /* $debugMsg */ SELECT COALESCE(MAX($field),-1) AS $field " . - " FROM {$this->tables[$table]} CHILD," . - " {$this->tables['nodes_hierarchy']} NH WHERE ". - " NH.id = CHILD.id ". - " AND NH.parent_id = {$id} "; - - $max_verbose = $this->db->fetchFirstRowSingleColumn($sql,$field); - if ($max_verbose >= 0) - { - $sql = "/* $debugMsg */ SELECT "; - - switch($my['options']['output']) - { - case 'credentials': - $sql .= " CHILD.parent_id,CHILD.id,CHILD.revision,CHILD.doc_id "; - break; - - case 'full': - default: - $sql .= " CHILD.* "; - break; - } - - $sql .= " FROM {$this->tables[$table]} CHILD," . - " {$this->tables['nodes_hierarchy']} NH ". - " WHERE $field = {$max_verbose} AND NH.id = CHILD.id AND NH.parent_id = {$id}"; - - $info = $this->db->fetchFirstRow($sql); - } - return $info; - } - - /** - * @param id: parent id - * @param child_type: 'revision' - * - * @return - */ - function getRevisionsCount($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $qty = 0; - - $sql = " /* $debugMsg */ SELECT COUNT(0) AS qty" . - " FROM {$this->tables['req_specs_revisions']} RSPEC_REV" . - " WHERE RSPEC_REV.parent_id = {$id} "; - - $dummy = $this->db->get_recordset($sql); - return $dummy[0]['qty']; - } - - - /** - * used to create overwiew of changes between revisions - */ - function get_history($id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('output' => "map", 'decode_user' => false, 'order_by_dir' => 'DESC'); - $my['options'] = array_merge($my['options'], (array)$options); - - $labels['undefined'] = lang_get('undefined'); - $sql = "/* $debugMsg */" . - " SELECT RSREV.id AS revision_id, RSREV.revision," . - " RSREV.creation_ts, RSREV.author_id, " . - " RSREV.modification_ts, RSREV.modifier_id, " . - " RSREV.revision, RSREV.scope, " . - " RSREV.status,RSREV.type,RSREV.name, RSREV.doc_id, " . - " COALESCE(RSREV.log_message,'') AS log_message" . - " FROM {$this->tables['req_specs_revisions']} RSREV " . - " WHERE RSREV.parent_id = {$id} " . - " ORDER BY RSREV.revision {$my['options']['order_by_dir']} "; - - - switch($my['options']['output']) - { - case 'map': - $rs = $this->db->fetchRowsIntoMap($sql,'revision_id'); - break; - - case 'array': - $rs = $this->db->get_recordset($sql); - break; - } - - if( !is_null($rs) ) - { - $key2loop = array_keys($rs); - foreach($key2loop as $ap) - { - $rs[$ap]['item_id'] = $rs[$ap]['revision_id']; - - // IMPORTANT NOTICE - // each DBMS uses a different (unfortunatelly) way to signal NULL DATE - // - // We need to Check with ALL DB types - // MySQL NULL DATE -> "0000-00-00 00:00:00" - // Postgres NULL DATE -> NULL - // MSSQL NULL DATE - ??? - $key4date = 'creation_ts'; - $key4user = 'author_id'; - if( ($rs[$ap]['modification_ts'] != '0000-00-00 00:00:00') && !is_null($rs[$ap]['modification_ts']) ) - { - $key4date = 'modification_ts'; - $key4user = 'modifier_id'; - } - $rs[$ap]['timestamp'] = $rs[$ap][$key4date]; - $rs[$ap]['last_editor'] = $rs[$ap][$key4user]; - - // decode user_id for last_editor - $user = tlUser::getByID($this->db,$rs[$ap]['last_editor']); - $rs[$ap]['last_editor'] = $user ? $user->getDisplayName() : $labels['undefined']; - } - } - - $history = $rs; - if( $my['options']['decode_user'] && !is_null($history) ) - { - $this->decode_users($history); - } - - return $history; - } - - /** - * - * - */ - function decode_users(&$rs) - { - $userCache = null; // key: user id, value: display name - $key2loop = array_keys($rs); - $labels['undefined'] = lang_get('undefined'); - $user_keys = array('author' => 'author_id', 'modifier' => 'modifier_id'); - foreach( $key2loop as $key ) - { - foreach( $user_keys as $ukey => $userid_field) - { - $rs[$key][$ukey] = ''; - if(trim($rs[$key][$userid_field]) != "") - { - if( !isset($userCache[$rs[$key][$userid_field]]) ) - { - $user = tlUser::getByID($this->db,$rs[$key][$userid_field]); - $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; - $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; - } - else - { - $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; - } - } - } - } - } - - /** - * - */ - function getRevisionTemplate() - { - $tpl = array('revision' => 1, 'doc_id' => null, 'name' => null, - 'scope' => null, 'type' => null, 'status' => 1, - 'total_req' => 0, 'log_message' => '', 'author_id' => -1); - return $tpl; - } - - - /** - * - */ - function clone_revision($rspecID,$item) - { - $fields2copy = "parent_id,id,revision,doc_id,name,scope,total_req,status,type,log_message"; - - // Create a new revision node on db - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array('msg' => 'ok','status_ok' => 1,'id' => -1); - $ret['id'] = $this->tree_mgr->new_node($rspecID,$this->node_types_descr_id['requirement_spec_revision']); - - if( !isset($item['source_id']) || ($item['source_id'] < 0) ) - { - $dummy = $this->get_last_child_info($rspecID); - $source_id = $dummy['id']; - } - else - { - $source_id = $item['source_id']; - } - - // get data to clone - $sourceItem = $this->getRevisionByID($source_id); - $sourceItem['log_message'] = $item['log_message']; - $sourceItem['author_id'] = $item['author_id']; - $sourceItem['revision']++; - - unset($sourceItem['modifier_id']); - unset($sourceItem['modification_ts']); - unset($sourceItem['creation_ts']); - - $ret = $this->create_revision($rspecID,$sourceItem); - if( $ret['status_ok'] ) - { - $source = array('parent_id' => $rspecID, 'item_id' => $source_id, - 'tproject_id' => $sourceItem['testproject_id']); - $dest_id = $ret['id']; - $this->copy_cfields($source,$ret['id']); - } - - return $ret; - - } - - - /** - * - * - */ - function getRevisionByID($id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('decode_user' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - $sql = '/* $debugMsg */' . - " SELECT RSPEC_REV.*, RSPEC.testproject_id " . - " FROM {$this->tables['req_specs_revisions']} RSPEC_REV " . - " JOIN {$this->tables['req_specs']} RSPEC " . - " ON RSPEC.id = RSPEC_REV.parent_id " . - " WHERE RSPEC_REV.id={$id} "; - - $ret = $this->db->get_recordset($sql); - if( !is_null($ret) && $my['options']['decode_user']) - { - $this->decode_users($ret); - } - return (!is_null($ret) ? $ret[0] : null); - } - - - /** - * - * - */ - function update_revision($item) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - if( !isset($item['revision_id']) || is_null($item['revision_id']) ) - { - // will go to update LATEST - $info = $this->get_last_child_info($item['id'],array('output' => 'credentials')); - $targetID = $info['id']; - } - else - { - $targetID = $item['revision_id']; - } - - $sql = '/* $debugMsg */' . - " UPDATE {$this->tables['req_specs_revisions']} " . - " SET scope = '" . $this->db->prepare_string($item['scope']) . "', " . - " modifier_id = " . $item['modifier_id'] . ", " . - " modification_ts = " . $this->db->db_now() . - " WHERE id={$targetID} "; - $stat = $this->db->exec_query($sql); - return array('id' => $targetID); - } - - - /** - * - */ - function get_all_id_in_testproject($tproject_id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('output' => 'classic'); - $my['options'] = array_merge($my['options'], (array)$options); - - $sql = "/* $debugMsg */ " . - " SELECT RSPEC.id FROM {$this->object_table} RSPEC WHERE testproject_id={$tproject_id}"; - - $rs = $this->db->get_recordset($sql); - switch($my['options']['output']) - { - case 'id': - $rx = array(); - foreach($rs as $elem) - { - $rx[] = $elem['id']; - } - return $rx; - break; - - default: - return $rs; - break; - } - } - - - /** - * - */ - function getAssignedCoverage($id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array( 'order_by' => " ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id", - 'output' => 'standard'); - $my['options'] = array_merge($my['options'], (array)$options); - - - $sql = "/* $debugMsg */ SELECT NH_REQ.id,U.login, REQ_COV.creation_ts " . - " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . - " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ_COV.req_id=NH_REQ.id " . - " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = REQ_COV.author_id "; - $sql .= " WHERE NH_REQ.parent_id={$id} " . - " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']}"; - $itemSet = $this->db->fetchRowsIntoMap($sql,'id'); - return $itemSet; - } - - /** - * - */ - function simplexml_load_file_helper($filename) - { - // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html - libxml_disable_entity_loader(true); - $zebra = file_get_contents($filename); - $xml = @simplexml_load_string($zebra); - return $xml; - } - - /** - * - * @used-by - */ - function getFileUploadRelativeURL($id) - { - $url = "lib/requirements/reqSpecEdit.php?doAction=fileUpload&req_spec_id=" . intval($id); - return $url; - } - - /** - * - * @used-by - */ - function getDeleteAttachmentRelativeURL($id) - { - $url = "lib/requirements/reqSpecEdit.php?doAction=deleteFile&req_spec_id=" . intval($id) . "&file_id=" ; - return $url; - } - - /** - * Copy attachments from source to target - * - **/ - function copy_attachments($source_id,$target_id) - { - return $this->attachmentRepository->copyAttachments($source_id,$target_id,$this->attachmentTableName); - } - - - /** - * - * - * - */ - function getReqsOnSpecForLatestTCV($id, $tcase_id=null, $options=null, $filters = null) { - - static $tcMgr; - - if( !$tcMgr ) { - $tcMgr = new testcase( $this->db ); - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = - array('order_by' => ' ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id ', - 'output' => 'standard', 'outputLevel' => 'std', 'decodeUsers' => true, - 'version_string' => lang_get('version_short')); - - $my['options'] = array_merge($my['options'], (array)$options); - - // null => do not filter - $my['filters'] = array('link_status' => 1, 'type' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - - // - $ltcv = null; - if( null == $tcase_id ) { - $tcversionJoin = - " JOIN {$this->views['latest_tcase_version_id']} LTCV " . - " ON LTCV.tcversion_id = RCOV.tcversion_id "; - } else { - $tcInfo = current($tcMgr->get_last_active_version($tcase_id)); - $ltcv = intval($tcInfo['tcversion_id']); - $tcversionJoin = " AND RCOV.tcversion_id = " . $ltcv; - } - - - // Step 1 - - // get all req inside the Req Spec Folder ONLY DIRECT CHILDREN - // - // Step 2 - - // Need to get only the Req Versions That are Assigned - // to Latest Active Test Case Version - // I'm doing this because I'm calling this function from - // the Test Spec Tree and in this context I CAN NOT choose - // test case version - // - $filters = ''; - if( null != $my['filters']['link_status'] ) { - $nu = (array)$my['filters']['link_status']; - $filters .= ' AND link_status IN(' . implode(',',$nu) . ')'; - } - - // Postgres => USER is reserved keyword !! - $lblVersion = $my['options']['version_string']; - $sql = "/* $debugMsg */ " . - " SELECT RCOV.id as link_id, NH_REQ.id,RCOV.req_version_id," . - " REQVER.scope, " . - " CONCAT(NH_REQ.name,' [{$lblVersion}',REQVER.version ,'] ' ) AS title," . - " REQ.req_doc_id, REQVER.version,TLUSER.login AS coverage_author, + JOIN {$this->tables['requirements']} REQ ON REQ_REL.destination_id = REQ.id where REQ_REL.source_id={$id}"; + return $this->db->get_recordset($sql); + } + + /* + * function: get_by_title + * get req spec information using title as access key. + * + * args : title: req spec title + * [tproject_id] + * [parent_id] + * [case_analysis]: control case sensitive search. + * default 0 -> case sensivite search + * + * returns: map. + * key: req spec id + * value: srs info, map with folowing keys: + * id + * testproject_id + * doc_id + * title + * scope + * total_req + * type + * author_id + * creation_ts + * modifier_id + * modification_ts + */ + private function get_by_title($title, $tproject_id = null, $parent_id = null, + $case_analysis = self::CASE_SENSITIVE) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $title = trim($title); + $the_title = $this->db->prepare_string($title); + $sql = "/* $debugMsg */ " . + " SELECT RSPEC.id,testproject_id,RSPEC.doc_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," . + " RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," . + " RSPEC.modification_ts,NH.name AS title " . + " FROM {$this->object_table} RSPEC, {$this->tables['nodes_hierarchy']} NH"; + + switch ($case_analysis) { + case self::CASE_SENSITIVE: + $sql .= " WHERE NH.name='{$the_title}'"; + break; + + case self::CASE_INSENSITIVE: + $sql .= " WHERE UPPER(NH.name)='" . strtoupper($the_title) . "'"; + break; + } + $sql .= " AND RSPEC.id=NH.id "; + + if (! is_null($tproject_id)) { + $sql .= " AND RSPEC.testproject_id={$tproject_id}"; + } + + if (! is_null($parent_id)) { + $sql .= " AND NH.parent_id={$parent_id}"; + } + + $sql .= " AND RSPEC.id=NH.id "; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /* + * function: check_title + * Do checks on req spec title, to understand if can be used. + * + * Checks: + * 1. title is empty ? + * 2. does already exist a req spec with this title? + * + * args : title: req spec title + * [parent_id]: default null -> do check for tile uniqueness system wide. + * valid id: only inside parent_id with this id. + * + * [id]: req spec id. + * [case_analysis]: control case sensitive search. + * default 0 -> case sensivite search + * + * returns: + * + */ + private function check_title($title, $tproject_id = null, $parent_id = null, + $id = null, $case_analysis = self::CASE_SENSITIVE) + { + $ret['status_ok'] = 1; + $ret['msg'] = ''; + + $title = trim($title); + + if ($title == "") { + $ret['status_ok'] = 0; + $ret['msg'] = lang_get("warning_empty_req_title"); + } + + if ($ret['status_ok']) { + $ret['msg'] = 'ok'; + $rs = $this->get_by_title($title, $tproject_id, $parent_id, + $case_analysis); + if (! is_null($rs) && (is_null($id) || ! isset($rs[$id]))) { + $ret['msg'] = sprintf(lang_get("warning_duplicate_req_title"), + $title); + $ret['status_ok'] = 0; + } + } + return $ret; + } + + // function end + + /* + * function: check_main_data + * Do checks on req spec title and doc id, to understand if can be used. + * + * Checks: + * 1. title is empty ? + * 2. doc is is empty ? + * 3. does already exist a req spec with this title? + * 4. does already exist a req spec with this doc id? + * + * VERY IMPORTANT: + * $tlCfg->req_cfg->child_requirements_mgmt has effects on check on already + * existent title or doc id. + * + * $tlCfg->req_cfg->child_requirements_mgmt == ENABLED => N level tree + * title and doc id can not repited on ANY level of tree + * + * This is important due to unique index present on Database + * ATTENTION: + * Must be rethinked!!!! + * + * + * args : title: req spec title + * doc_id: req spec document id / code / short title + * [parent_id]: default null -> do check for tile uniqueness system wide. + * valid id: only inside parent_id with this id. + * + * [id]: req spec id. + * [case_analysis]: control case sensitive search. + * default 0 -> case sensivite search + * + * returns: + * + */ + public function check_main_data($title, $doc_id, $tproject_id = null, + $parent_id = null, $id = null, $case_analysis = self::CASE_SENSITIVE) + { + $ret['status_ok'] = 1; + $ret['msg'] = ''; + + $title = trim($title); + $doc_id = trim($doc_id); + + if ($title == "") { + $ret['status_ok'] = 0; + $ret['msg'] = lang_get("warning_empty_req_title"); + } + + if ($doc_id == "") { + $ret['status_ok'] = 0; + $ret['msg'] = lang_get("warning_empty_doc_id"); + } + + if ($ret['status_ok']) { + $ret['msg'] = 'ok'; + $rs = $this->getByDocID($doc_id, $tproject_id); + if (! is_null($rs) && (is_null($id) || ! isset($rs[$id]))) { + $info = current($rs); + $ret['msg'] = sprintf( + lang_get("warning_duplicated_req_spec_doc_id"), + $info['title'], $doc_id); + $ret['status_ok'] = 0; + } + } + + return $ret; + } + + /* + * function: + * + * args : + * $nodes: array with req_spec in order + * returns: + * + */ + public function set_order($map_id_order) + { + $this->tree_mgr->change_order_bulk($map_id_order); + } + + // set_order($map_id_order) + + /* + * function: + * + * args: + * + * returns: + * + */ + public function get_requirements_count($id, $range = 'all', + $testcase_id = null, $filters = null) + { + // filters => array('status' => NON_TESTABLE_REQ, 'type' => 'X'); + $options = array( + 'output' => 'count' + ); + return $this->get_requirements($id, $range, $testcase_id, $options, + $filters); + } + + /** + * getReqTree + * + * Example of returned value ( is a recursive one ) + * ( + * [childNodes] => Array + * ([0] => Array + * ( [id] => 216 + * [parent_id] => 179 + * [node_type_id] => 6 + * [node_order] => 0 + * [node_table] => req_specs + * [name] => SUB-R + * [childNodes] => Array + * ([0] => Array + * ( [id] => 181 + * [parent_id] => 216 + * [node_type_id] => 7 + * [node_order] => 0 + * [node_table] => requirements + * [name] => Gamma Ray Emissions + * [childNodes] => + * ) + * [1] => Array + * ( [id] => 182 + * [parent_id] => 216 + * [node_type_id] => 7 + * [node_order] => 0 + * [node_table] => requirements + * [name] => Coriolis Effet + * [childNodes] => + * ) + * ) + * ) + * [1] => Array + * ( [id] => 217 + * [parent_id] => 179 + * [node_type_id] => 6 + * [node_order] => 0 + * [node_table] => req_specs + * [name] => SUB-R2 + * [childNodes] => Array + * ... + */ + public function getReqTree($id) + { + $filters = null; + $options = array( + 'recursive' => true + ); + return $this->tree_mgr->get_subtree($id, $filters, $options); + } + + /** + * exportReqSpecToXML + * create XML string with following req spec data + * - basic data (title, scope) + * - custom fields values + * - children: can be other req spec or requirements + * (tree leaves) + * + * Developed using exportTestSuiteDataToXML() as model + */ + public function exportReqSpecToXML($id, $tproject_id, + $optForExport = array()) + { + // manage missing keys; recursive export by default + if (! array_key_exists('RECURSIVE', $optForExport)) { + $optForExport['RECURSIVE'] = true; + } + + $relXmlData = ''; + $relationsCache = array(); + + $cfXML = null; + $xmlData = null; + if ($optForExport['RECURSIVE']) { + $cfXML = $this->customFieldValuesAsXML($id, $tproject_id); + $containerData = $this->get_by_id($id); + $xmlData = "' . "\n" . + "\t\n" . + "\t\n" . + "\t\n" . + "\t\n" . + "\t\n\t\t{$cfXML}"; + } + + // Add attachments info + if (isset($optForExport['ATTACHMENTS']) && $optForExport['ATTACHMENTS']) { + + $attachments = null; + $attachSet = $this->attachmentRepository->getAttachmentInfosFor($id, + $this->attachmentTableName, 'id'); + + // get all attachments content and encode it in base64 + if ($attachSet) { + foreach ($attachSet as $attInfo) { + $aID = $attInfo["id"]; + $content = $this->attachmentRepository->getAttachmentContent( + $aID, $attInfo); + + if ($content != null) { + $attachments[$aID]["id"] = $aID; + $attachments[$aID]["name"] = $attInfo["file_name"]; + $attachments[$aID]["file_type"] = $attInfo["file_type"]; + $attachments[$aID]["title"] = $attInfo["title"]; + $attachments[$aID]["date_added"] = $attInfo["date_added"]; + $attachments[$aID]["content"] = base64_encode($content); + } + } + } + + if (! empty($attachments)) { + $attchRootElem = "\t\n{{XMLCODE}}\t\n"; + $attchElemTemplate = "\t\t\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\n"; + + $attchDecode = array( + "||ATTACHMENT_ID||" => "id", + "||ATTACHMENT_NAME||" => "name", + "||ATTACHMENT_FILE_TYPE||" => "file_type", + "||ATTACHMENT_TITLE||" => "title", + "||ATTACHMENT_DATE_ADDED||" => "date_added", + "||ATTACHMENT_CONTENT||" => "content" + ); + $xmlData .= exportDataToXML($attachments, $attchRootElem, + $attchElemTemplate, $attchDecode, true); + } + } + + $req_spec = $this->getReqTree($id); + $childNodes = isset($req_spec['childNodes']) ? $req_spec['childNodes'] : null; + if (! is_null($childNodes)) { + $loop_qty = count($childNodes); + for ($idx = 0; $idx < $loop_qty; $idx ++) { + $cNode = $childNodes[$idx]; + if ($optForExport['RECURSIVE'] && + $cNode['node_table'] == 'req_specs') { + $xmlData .= $this->exportReqSpecToXML($cNode['id'], + $tproject_id, $optForExport); + } elseif ($cNode['node_table'] == 'requirements') { + $xmlData .= $this->req_mgr->exportReqToXML($cNode['id'], + $tproject_id, $optForExport['ATTACHMENTS']); + + $relations = $this->req_mgr->get_relations($cNode['id']); + if (! empty($relations['relations'])) { + foreach ($relations['relations'] as $rel) { + // If we have already found this relation, skip it. + if (! in_array($rel['id'], $relationsCache)) { + // otherwise export it to XML. + // $testproject_id = $this->relationsCfg->interProjectLinking ? $tproject_id : null; + $relXmlData .= $this->req_mgr->exportRelationToXML( + $rel, $tproject_id, + $this->relationsCfg->interProjectLinking); + $relationsCache[] = $rel['id']; + } + } + } + } + } + + // after we scanned all relations and exported all relations to xml, let's output it to the XML buffer + $xmlData .= $relXmlData; + } + + if ($optForExport['RECURSIVE']) { + $xmlData .= "\n"; + } + return $xmlData; + } + + /** + * xmlToReqSpec + * + * @param object $source: + * $source->type: possible values 'string', 'file' + * $source->value: depends of $source->type + * 'string' => xml string + * 'file' => path name of XML file + * + */ + private function xmlToReqSpec($source) + { + $req_spec = null; + switch ($source->type) { + case 'string': + break; + + case 'file': + $xml_file = $source->value; + (@$this->simplexml_load_file_helper($xml_file)) !== false; + break; + } + + return $req_spec; + } + + /** + * xmlToMapReqSpec + */ + private function xmlToMapReqSpec($xml_item, $level = 0) + { + static $mapped; + + // Attention: following PHP Manual SimpleXML documentation, Please remember to cast + // before using data from $xml, + if (is_null($xml_item)) { + return null; + } + + // used to reset static structures if calling this in loop + if ($level == 0) { + $mapped = null; + } + + $dummy = array(); + $dummy['node_order'] = (int) $xml_item->node_order; + $dummy['scope'] = (string) $xml_item->scope; + $dummy['type'] = (int) $xml_item->type; + $dummy['total_req'] = (int) $xml_item->total_req; + $dummy['level'] = $level; + $depth = $level + 1; + + foreach ($xml_item->attributes() as $key => $value) { + $dummy[$key] = (string) $value; // See PHP Manual SimpleXML documentation. + } + + if (property_exists($xml_item, 'custom_fields')) { + $dummy['custom_fields'] = array(); + foreach ($xml_item->custom_fields->children() as $key) { + $dummy['custom_fields'][(string) $key->name] = (string) $key->value; + } + } + + if (property_exists($xml_item, 'attachments')) { + $dummy['attachments'] = array(); + foreach ($xml_item->attachments->children() as $attachment) { + $attach_id = (int) $attachment->id; + $dummy['attachments'][$attach_id]['id'] = (int) $attachment->id; + $dummy['attachments'][$attach_id]['name'] = (string) $attachment->name; + $dummy['attachments'][$attach_id]['file_type'] = (string) $attachment->file_type; + $dummy['attachments'][$attach_id]['title'] = (string) $attachment->title; + $dummy['attachments'][$attach_id]['date_added'] = (string) $attachment->date_added; + $dummy['attachments'][$attach_id]['content'] = (string) $attachment->content; + } + } + $mapped[] = array( + 'req_spec' => $dummy, + 'requirements' => null, + 'level' => $dummy['level'] + ); + + // Process children + if (property_exists($xml_item, 'requirement')) { + $loop2do = count($xml_item->requirement); + for ($idx = 0; $idx <= $loop2do; $idx ++) { + $xml_req = $this->req_mgr->xmlToMapRequirement( + $xml_item->requirement[$idx]); + if (! is_null($xml_req)) { + $fdx = count($mapped) - 1; + $mapped[$fdx]['requirements'][] = $xml_req; + } + } + } + + if (property_exists($xml_item, 'relation')) { + $loop3do = count($xml_item->relation); + for ($idx = 0; $idx <= $loop3do; $idx ++) { + $rel = $this->req_mgr->convertRelationXmlToRelationMap( + $xml_item->relation[$idx]); + if (! is_null($rel)) { + $fdx = count($mapped) - 1; + $mapped[$fdx]['relations'][] = $rel; + } + } + } + + if (property_exists($xml_item, 'req_spec')) { + $loop2do = count($xml_item->req_spec); + for ($idx = 0; $idx <= $loop2do; $idx ++) { + $this->xmlToMapReqSpec($xml_item->req_spec[$idx], $depth); + } + } + + return $mapped; + } + + // Custom field related functions + + /* + * function: get_linked_cfields + * Get all linked custom fields. + * Remember that custom fields are defined at system wide level, and + * has to be linked to a testproject, in order to be used. + * + * + * args: credentials, map with following keys + * item_id: Req. Spec REVISION ID (can be NULL if parent_id IS NOT NULL) + * parent_id: Req. Spec ID (can be NULL if item_id IS NOT NULL) + * tproject_id:node id of parent testproject of requirement spec. + * need to understand to which testproject requirement spec belongs. + * this information is vital, to get the linked custom fields. + * Presence /absence of this value changes starting point + * on procedure to build tree path to get testproject id. + * + * null -> use requirement spec id as starting point. + * !is_null -> use this value as starting point. + * + * returns: map/hash + * key: custom field id + * value: map with custom field definition and value assigned for choosen req spec, + * with following keys: + * + * id: custom field id + * name + * label + * type: custom field type + * possible_values: for custom field + * default_value + * valid_regexp + * length_min + * length_max + * show_on_design + * enable_on_design + * show_on_execution + * enable_on_execution + * display_order + * value: value assigned to custom field for this req spec + * null if for this req spec custom field was never edited. + * + * node_id: req spec id + * null if for this req spec, custom field was never edited. + * + * + * @internal revisions + * + */ + public function get_linked_cfields($credentials) + { + $who = array( + 'item_id' => null, + 'parent_id' => null, + 'tproject_id' => null + ); + $who = array_merge($who, (array) $credentials); + + $tproject_id = $who['tproject_id']; + $hasParentInfo = ! is_null($who['parent_id']) && ($who['parent_id'] > 0); + + if ($hasParentInfo && (is_null($tproject_id) || is_null($who['item_id']))) { + // will get info for LAST revision + $info = $this->get_by_id($who['parent_id'], + array( + 'output' => 'credentials' + )); + $tproject_id = $info['testproject_id']; + $who['item_id'] = $info['revision_id']; + } + return $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, + cfield_mgr::CF_ENABLED, null, 'requirement_spec', $who['item_id']); + } + + /* + * function: html_table_of_custom_field_inputs + * Return html code, implementing a table with custom fields labels + * and html inputs, for choosen req spec. + * Used to manage user actions on custom fields values. + * + * + * args: $id + * [tproject_id]: node id of testproject (req spec parent). + * this information is vital, to get the linked custom fields, + * because custom fields are system wide, but to be used are + * assigned to a test project. + * is null this method or other called will use get_path() + * method to get test project id. + * + * [parent_id]: Need to e rethinked, may be remove (20090111 - franciscom) + * + * [$name_suffix]: must start with '_' (underscore). + * Used when we display in a page several items + * (example during test case execution, several test cases) + * that have the same custom fields. + * In this kind of situation we can use the item id as name suffix. + * + * + * returns: html string + * + */ + public function html_table_of_custom_field_inputs($id, $child_id, + $tproject_id = null, $parent_id = null, $name_suffix = '', + $input_values = null) + { + $idCard = array( + 'parent_id' => $id, + 'item_id' => $child_id, + 'tproject_id' => $tproject_id + ); + $cf_map = $this->get_linked_cfields($idCard); + return $this->cfield_mgr->html_table_inputs($cf_map, $name_suffix, + $input_values); + } + + /* + * function: html_table_of_custom_field_values + * Return html code, implementing a table with custom fields labels + * and custom fields values, for choosen req spec. + * You can think of this function as some sort of read only version + * of html_table_of_custom_field_inputs. + * + * + * args: $id + * + * returns: html string + * + */ + public function html_table_of_custom_field_values($id, $child_id, + $tproject_id) + { + $NO_WARNING_IF_MISSING = true; + $cf_smarty = ''; + + $idCard = array( + 'parent_id' => $id, + 'item_id' => $child_id, + 'tproject_id' => $tproject_id + ); + $cf_map = $this->get_linked_cfields($idCard); + $show_cf = config_get('custom_fields')->show_custom_fields_without_value; + + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + // if user has assigned a value, then node_id is not null + if ($cf_info['node_id'] || $show_cf) { + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, $NO_WARNING_IF_MISSING)); + + $cf_smarty .= '' . + htmlspecialchars($label) . ":" . + $this->cfield_mgr->string_custom_field_value($cf_info, + $id) . "\n"; + } + } + + if (trim($cf_smarty) != "") { + $cf_smarty = "" . $cf_smarty . "
    "; + } + } + return $cf_smarty; + } + + /* + * function: values_to_db + * write values of custom fields to db + * + * args: hash: + * key: custom_field__. + * Example custom_field_0_67 -> 0=> string field + * + * node_id: req spec id + * + * [cf_map]: hash -> all the custom fields linked and enabled + * that are applicable to the node type of $node_id. + * + * For the keys not present in $hash, we will write + * an appropriate value according to custom field + * type. + * + * This is needed because when trying to udpate + * with hash being $_REQUEST, $_POST or $_GET + * some kind of custom fields (checkbox, list, multiple list) + * when has been deselected by user. + * + * [hash_type] + * + * rev: + */ + public function values_to_db($hash, $node_id, $cf_map = null, + $hash_type = null) + { + $this->cfield_mgr->design_values_to_db($hash, $node_id, $cf_map, + $hash_type); + } + + /** + * customFieldValuesAsXML + * + * @param int $id: + * requirement + * spec id + * @param int $tproject_id: + * testproject id + */ + private function customFieldValuesAsXML($id, $tproject_id) + { + $xml = null; + + $idCard = array( + 'parent_id' => $id, + 'item_id' => null, + 'tproject_id' => $tproject_id + ); + $cfMap = $this->get_linked_cfields($idCard); + if (! empty($cfMap)) { + $xml = $this->cfield_mgr->exportValueAsXML($cfMap); + } + return $xml; + } + + /** + * create a req spec tree on system from $xml data + * + * + * @internal revisions + */ + public function createFromXML($xml, $tproject_id, $parent_id, $author_id, + $filters = null, $options = null) + { + static $labels; + static $missingCfMsg; + static $linkedCF; + static $messages; + static $doProcessCF = false; + + // init static items + if (is_null($labels)) { + $labels = array( + 'import_req_spec_created' => '', + 'import_req_spec_skipped' => '', + 'import_req_spec_updated' => '', + 'import_req_spec_ancestor_skipped' => '', + 'import_req_created' => '', + 'import_req_skipped' => '', + 'import_req_updated' => '' + ); + foreach ($labels as $key => $dummy) { + $labels[$key] = lang_get($key); + } + + $messages = array(); + $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); + $messages['cfield'] = lang_get( + 'cf_value_not_imported_missing_cf_on_testproject'); + + $linkedCF = $this->cfield_mgr->get_linked_cfields_at_design( + $tproject_id, cfield_mgr::CF_ENABLED, null, 'requirement_spec', + null, 'name'); + $doProcessCF = true; + } + + $user_feedback = null; + $copy_req = null; + $getOptions = array( + 'output' => 'minimun' + ); + $my['options'] = array( + 'skipFrozenReq' => true + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $items = $this->xmlToMapReqSpec($xml); + + $has_filters = ! is_null($filters); + if ($has_filters && ! is_null($filters['requirements'])) { + foreach ($filters['requirements'] as $reqspec_pos => $requirements_pos) { + $copy_req[$reqspec_pos] = is_null($requirements_pos) ? null : array_keys( + $requirements_pos); + } + } + + $loop2do = count($items); + $container_id[0] = (is_null($parent_id) || $parent_id == 0) ? $tproject_id : $parent_id; + + // items is an array of req. specs + $skip_level = - 1; + for ($idx = 0; $idx < $loop2do; $idx ++) { + $rspec = $items[$idx]['req_spec']; + $depth = $rspec['level']; + if ($skip_level > 0 && $depth >= $skip_level) { + $msgID = 'import_req_spec_ancestor_skipped'; + $user_feedback[] = array( + 'doc_id' => $rspec['doc_id'], + 'title' => $rspec['title'], + 'import_status' => sprintf($labels[$msgID], $rspec['doc_id']) + ); + continue; + } + $req_spec_order = isset($rspec['node_order']) ? $rspec['node_order'] : 0; + + // Check if req spec with same DOCID exists, inside container_id + // If there is a hit + // We will go in update + // If Check fails, need to repeat check on WHOLE Testproject. + // If now there is a HIT we can not import this branch + // If Check fails => we can import creating a new one. + // + // Important thing: + // Working in this way, i.e. doing check while walking the structure to import + // we can end importing struct with 'holes'. + // + $check_in_container = $this->getByDocID($rspec['doc_id'], + $tproject_id, $container_id[$depth], $getOptions); + $hasAttachments = array_key_exists('attachments', $rspec); + + $skip_level = $depth + 1; + $result['status_ok'] = 0; + $msgID = 'import_req_spec_skipped'; + + if (is_null($check_in_container)) { + $check_in_tproject = $this->getByDocID($rspec['doc_id'], + $tproject_id, null, $getOptions); + if (is_null($check_in_tproject)) { + $importMode = 'creation'; + $msgID = 'import_req_spec_created'; + $result = $this->create($tproject_id, $container_id[$depth], + $rspec['doc_id'], $rspec['title'], $rspec['scope'], + $rspec['total_req'], $author_id, $rspec['type'], + $req_spec_order); + } + } else { + $importMode = 'update'; + $msgID = 'import_req_spec_updated'; + $reqSpecID = key($check_in_container); + $item = array( + 'id' => $reqSpecID, + 'name' => $rspec['title'], + 'doc_id' => $rspec['doc_id'], + 'scope' => $rspec['scope'], + 'total_req' => $rspec['total_req'], + 'modifier_id' => $author_id, + 'type' => $rspec['type'], + 'node_order' => $req_spec_order + ); + + // ATTENTION update return key => revision_id, because CF values are saved at REVISION LEVEL + $result = $this->update($item); + $result['id'] = $reqSpecID; + } + $user_feedback[] = array( + 'doc_id' => $rspec['doc_id'], + 'title' => $rspec['title'], + 'import_status' => sprintf($labels[$msgID], $rspec['doc_id']) + ); + + // process attachements for creation and update + if ($result['status_ok'] && $hasAttachments) { + $addAttachmentsResponse = $this->processAttachments($importMode, + $result['id'], $rspec['attachments'], $feedbackMsg); + } + // display only problems during attachments import + if (isset($addAttachmentsResponse) && + ! is_null($addAttachmentsResponse)) { + foreach ($addAttachmentsResponse as $att_name) { + $user_feedback[] = array( + 'doc_id' => $rspec['doc_id'], + 'title' => $rspec['title'], + 'import_status' => sprintf( + lang_get('import_req_spec_attachment_skipped'), + $att_name) + ); + } + } + if ($result['status_ok'] && $doProcessCF && + isset($rspec['custom_fields']) && + ! is_null($rspec['custom_fields'])) { + $cf2insert = null; + foreach ($rspec['custom_fields'] as $cfname => $cfvalue) { + $cfname = trim($cfname); + if (isset($linkedCF[$cfname])) { + $cf2insert[$linkedCF[$cfname]['id']] = array( + 'type_id' => $linkedCF[$cfname]['type'], + 'cf_value' => $cfvalue + ); + } else { + if (! isset($missingCfMsg[$cfname])) { + $missingCfMsg[$cfname] = sprintf( + $messages['cfield'], $cfname, + $labels['requirement']); + } + $user_feedback[] = array( + 'doc_id' => $rspec['docid'], + 'title' => $rspec['title'], + 'import_status' => $missingCfMsg[$cfname] + ); + } + } + if (! is_null($cf2insert)) { + $this->cfield_mgr->design_values_to_db($cf2insert, + $result['revision_id'], null, 'simple'); + } + } + + if ($result['status_ok']) { + $skip_level = - 1; + $container_id[$depth + 1] = ($reqSpecID = $result['id']); + $reqSet = $items[$idx]['requirements']; + $create_req = (! $has_filters || isset($copy_req[$idx])) && + ! is_null($reqSet); + if ($create_req) { + $items_qty = isset($copy_req[$idx]) ? count($copy_req[$idx]) : count( + $reqSet); + $keys2insert = isset($copy_req[$idx]) ? $copy_req[$idx] : array_keys( + $reqSet); + for ($jdx = 0; $jdx < $items_qty; $jdx ++) { + $req = $reqSet[$keys2insert[$jdx]]; + $dummy = $this->req_mgr->createFromMap($req, + $tproject_id, $reqSpecID, $author_id, null, + $my['options']); + $user_feedback = array_merge($user_feedback, $dummy); + } + } + + if (isset($items[$idx]['relations'])) { + $relationsMap = $items[$idx]['relations']; + $numberOfRelations = count($relationsMap); + for ($jdx = 0; $jdx < $numberOfRelations; $jdx ++) { + $rel = $relationsMap[$jdx]; + $dummy = $this->req_mgr->createRelationFromMap($rel, + $tproject_id, $author_id); + $user_feedback = array_merge($user_feedback, $dummy); + } + } + } + } + return $user_feedback; + } + + /* + * function: getByDocID + * get req spec information using document ID as access key. + * + * args : doc_id: + * [tproject_id] + * [parent_id] + * [options]: + * [case]: control case sensitive search. + * default 0 -> case sensivite search + * [access_key]: + * [check_criteria]: + * [output]: + * + * returns: map. + * key: req spec id + * value: srs info, map with folowing keys: + * id + * testproject_id + * title + * scope + * total_req + * type + * author_id + * creation_ts + * modifier_id + * modification_ts + * + */ + public function getByDocID($doc_id, $tproject_id = null, $parent_id = null, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'check_criteria' => '=', + 'access_key' => 'id', + 'case' => 'sensitive', + 'output' => 'standard' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $output = null; + $the_doc_id = $this->db->prepare_string(trim($doc_id)); + + switch ($my['options']['check_criteria']) { + case 'like': + $check_criteria = " LIKE '{$the_doc_id}%' "; + break; + + case '=': + default: + $check_criteria = " = '{$the_doc_id}' "; + break; + } + + $where = " WHERE RSPEC.doc_id {$check_criteria} "; + if (! is_null($tproject_id)) { + $where .= " AND RSPEC.testproject_id={$tproject_id}"; + } + if (! is_null($parent_id)) { + $where .= " AND NH_RSPEC.parent_id={$parent_id}"; + } + + // Developer Note: + // a mix of SQL ignorance and MySQL relaxed SQL on GROUP BY + // Fortunatelly Postgres do the right job + // + // + // First step get MAX revision + // will trust in this that max(revision) has also always max(revision_id) + // ( but really can be on a differente way ? ), in order to use a simple logic. + // + $sql_max = " /* $debugMsg */ SELECT MAX(RSPEC_REV.id) AS rev_id" . + " FROM {$this->tables['req_specs']} RSPEC " . + " JOIN {$this->tables['req_specs_revisions']} RSPEC_REV " . + " ON RSPEC_REV.parent_id = RSPEC.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC " . + " ON NH_RSPEC.id = RSPEC.id " . $where . + ' GROUP BY RSPEC_REV.parent_id '; + + $maxi = (array) $this->db->fetchRowsIntoMap($sql_max, 'rev_id'); + if (count($maxi) > 0) { + $sql = " /* $debugMsg */ SELECT RSPEC.id,RSPEC.testproject_id,RSPEC.doc_id,NH_RSPEC.name AS title, " . + " RSPEC_REV.revision "; + + switch ($my['options']['output']) { + case 'standard': + $sql .= " ,RSPEC_REV.total_req, RSPEC_REV.scope,RSPEC_REV.type," . + " RSPEC_REV.author_id,RSPEC_REV.creation_ts, " . + " RSPEC_REV.modifier_id,RSPEC_REV.modification_ts"; + break; + + case 'minimun': + break; + } + + $sql .= " FROM {$this->tables['req_specs']} RSPEC " . + " JOIN {$this->tables['req_specs_revisions']} RSPEC_REV " . + " ON RSPEC_REV.parent_id = RSPEC.id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_RSPEC " . + " ON NH_RSPEC.id = RSPEC.id "; + + $sql .= $where . ' AND RSPEC_REV.id IN (' . + implode(",", array_keys($maxi)) . ') '; + $output = $this->db->fetchRowsIntoMap($sql, + $my['options']['access_key']); + } + + return $output; + } + + /* + * function: copy_to + * deep copy one req spec to another parent (req spec or testproject). + * + * + * args : id: req spec id (source or copy) + * parent_id: + * user_id: who is requesting copy operation + * [options] + * + * returns: map with following keys: + * status_ok: 0 / 1 + * msg: 'ok' if status_ok == 1 + * id: new created if everything OK, -1 if problems. + * + * rev : + */ + public function copy_to($id, $parent_id, $tproject_id, $user_id, + $options = null) + { + static $get_tree_nt2exclude; + if (! $get_tree_nt2exclude) { + $get_tree_nt2exclude = array( + 'req_version' => 'exclude_me', + 'req_revision' => 'exclude_me', + 'requirement_spec_revision' => 'exclude_me' + ); + } + + $my['options'] = array( + 'copy_also' => null + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $item_info = $this->get_by_id($id); + $target_doc = $this->generateDocID($id, $tproject_id); + $new_item = $this->create($tproject_id, $parent_id, $target_doc, + $item_info['title'], $item_info['scope'], $item_info['total_req'], + $item_info['author_id'], $item_info['type'], + $item_info['node_order']); + + $op = $new_item; + if ($new_item['status_ok']) { + $op['mappings'][$id] = $new_item['id']; + $op['mappings']['req_spec'] = array(); + $op['mappings']['req'] = array(); + $op['mappings']['req_version'] = array(); + $op['mappings']['req_tree'] = array(); + + $idCard = array( + 'parent_id' => $id, + 'tproject_id' => $tproject_id + ); + $this->copy_cfields($idCard, $new_item['id']); + + $this->copyAttachments($id, $new_item['id']); + + // Now loop to copy all items inside it + // null is OK, because $id is a req spec, there is no risk + // to copy/traverse wrong node types. + // Hmmm may be req_revi ??? + $my['filters']['exclude_node_types'] = $get_tree_nt2exclude; + $subtree = $this->tree_mgr->get_subtree($id, $my['filters'], + array( + 'output' => 'essential' + )); + + if (! is_null($subtree)) { + $reqMgr = new requirement_mgr($this->db); + $parent_decode = array(); + $parent_decode[$id] = $new_item['id']; + + // using reference has to avoid duplicate => memory consumption + // (at least this is info found on Internet) + // Few test indicates that it's true, but that using a counter + // is still better. + // + $loop2do = count($subtree); + for ($sdx = 0; $sdx <= $loop2do; $sdx ++) { + $elem = &$subtree[$sdx]; + $the_parent_id = isset($parent_decode[$elem['parent_id']]) ? $parent_decode[$elem['parent_id']] : null; + + switch ($elem['node_type_id']) { + case $this->node_types_descr_id['requirement']: + $ret = $reqMgr->copy_to($elem['id'], $the_parent_id, + $user_id, $tproject_id, $my['options']); + + $op['status_ok'] = $ret['status_ok']; + $op['mappings']['req'] += $ret['mappings']['req']; + $op['mappings']['req_version'] += $ret['mappings']['req_version']; + $op['mappings']['req_tree'] += $ret['mappings']['req_tree']; + break; + + case $this->node_types_descr_id['requirement_spec']: + $item_info = $this->get_by_id($elem['id']); + + // hmm, when copy_to() is called because we are duplicating + // a test project, call to generateDocID(), can be avoided. + // we have IMHO an absolute inexistent risk. + $target_doc = $this->generateDocID($elem['id'], + $tproject_id); + + $ret = $this->create($tproject_id, $the_parent_id, + $target_doc, $item_info['title'], + $item_info['scope'], $item_info['total_req'], + $item_info['author_id'], $item_info['type'], + $item_info['node_order']); + + $parent_decode[$elem['id']] = $ret['id']; + $op['mappings']['req_spec'][$elem['id']] = $ret['id']; + + if ($op['status_ok'] = $ret['status_ok']) { + $this->copy_cfields( + array( + 'parent_id' => $elem['id'], + 'tproject_id' => $tproject_id + ), $ret['id']); + } + break; + } + + if ($op['status_ok'] == 0) { + break; + } + } + } + } + return $op; + } + + /* + * function: copy_cfields + * Get all cfields linked to item with the values presents for $from_id, + * item we are using as source for our copy. + * + * args: from_identity: source credentianls (complex type) + * array('parent_id' => , 'item_id' => , 'tproject_id' => ); + * + * to_id: target item id (simple type) + * + * returns: - + * + */ + private function copy_cfields($from_identity, $to_id) + { + $cfmap_from = $this->get_linked_cfields($from_identity); + + $cfield = null; + if (! is_null($cfmap_from)) { + foreach ($cfmap_from as $key => $value) { + $cfield[$key] = array( + "type_id" => $value['type'], + "cf_value" => $value['value'] + ); + } + } + $this->cfield_mgr->design_values_to_db($cfield, $to_id, null, + 'tcase_copy_cfields'); + } + + /** + * processAttachments + * + * Analyze attachments info related to req spec to define if the the attachment has to be added. + * attachments are ignored only if a attachment with the same ID is already linked to the target ReqSpec. + * + * return an array of all attachments names of IDs already linked to target ReqSpec. + */ + private function processAttachments($importMode, $rs_id, $attachments, + $feedbackMsg) + { + $tables = tlObjectWithDB::getDBTables( + array( + 'req_specs', + 'attachments' + )); + + $knownAttachments = array(); + foreach ($attachments as $attachment) { + $addAttachment = true; + if ($importMode == 'update') { + // try to bypass the importation of already known attachments. + // Check in database if the attachment with the same ID is linked to the rspec with the same internal ID + // The couple attachment ID + InternalID is used as a kind of signature to avoid duplicates. + // If signature is not precise enough, could add the use of attachment timestamp (date_added in XML file). + $sql = " SELECT ATT.id from {$tables['attachments']} ATT " . + " WHERE ATT.id='{$this->db->prepare_string($attachment[id])}' " . + " AND ATT.fk_id={$rs_id} "; + $rsx = $this->db->get_recordset($sql); + $addAttachment = (is_null($rsx) || count($rsx) < 1); + if ($addAttachment === false) { // inform user that the attachment has been skipped + $knownAttachments[] = $attachment['name']; + } + } + if ($addAttachment) { + $attachRepo = tlAttachmentRepository::create($this->db); + $fileInfo = $attachRepo->createAttachmentTempFile( + $attachment['content']); + $fileInfo['name'] = $attachment['name']; + $fileInfo['type'] = $attachment['file_type']; + $attachRepo->insertAttachment($rs_id, $tables['req_specs'], + $attachment['title'], $fileInfo); + } + } + return $knownAttachments; + } + + /** + */ + private function generateDocID($id, $tproject_id) + { + $item_info = $this->get_by_id($id); + + // Check if another req with same DOC ID exists on target container, + // If yes generate a new DOC ID + $getOptions = array( + 'check_criteria' => 'like', + 'access_key' => 'doc_id' + ); + $itemSet = $this->getByDocID($item_info['doc_id'], $tproject_id, null, + $getOptions); + $target_doc = $item_info['doc_id']; + $instance = 1; + if (! is_null($itemSet)) { + // doc_id has limited size => we need to be sure that generated id + // will not exceed DB size + $nameSet = array_flip(array_keys($itemSet)); + + // 6 magic from " [xxx]" + $prefix = trimAndLimit($item_info['doc_id'], + $this->field_size->docid - 6); + $target_doc = $prefix . " [{$instance}]"; + while (isset($nameSet[$target_doc])) { + $instance ++; + $target_doc = $prefix . " [{$instance}]"; + } + } + return $target_doc; + } + + /** + */ + public function getFirstLevelInTestProject($tproject_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT * from {$this->tables['nodes_hierarchy']} " . + " WHERE parent_id = {$tproject_id} " . + " AND node_type_id = {$this->node_types_descr_id['requirement_spec']} " . + " ORDER BY node_order,id"; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + * IMPORTANT NOTICE + * Only information regarding basic tables is created. + * This means THAT NOTHING is done (example) on custom fields, or other + * items that are related/linked to revisions. + */ + private function create_revision($rspecID, $item) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $ret = array( + 'msg' => 'ok', + 'status_ok' => 1, + 'id' => - 1 + ); + $ret['id'] = $this->tree_mgr->new_node($rspecID, + $this->node_types_descr_id['requirement_spec_revision']); + + $optActorPairs = array( + 'author_id' => 'creation_ts', + 'modifier_id' => 'modification_ts' + ); + $val2add = ''; + $fields2insert = 'parent_id,id,revision,status,doc_id,name,scope,type,log_message'; + + foreach ($optActorPairs as $main => $sec) { + if (isset($item[$main]) && is_numeric($item[$main])) { + $fields2insert .= ',' . $main . ',' . $sec; + $ts = isset($item[$sec]) ? $item[$sec] : $this->db->db_now(); + $val2add .= ',' . intval($item[$main]) . ',' . $ts; + } + } + $optIntKeys = array( + 'status' => 1 + ); + foreach ($optIntKeys as $field => $default) { + $item[$field] = isset($item[$field]) ? $item[$field] : $default; + } + + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['req_specs_revisions']} " . + " ($fields2insert) " . " VALUES({$rspecID}" . "," . $ret['id'] . "," . + intval($item['revision']) . "," . intval($item['status']) . ",'" . + $this->db->prepare_string($item['doc_id']) . "','" . + $this->db->prepare_string($item['name']) . "','" . + $this->db->prepare_string($item['scope']) . "','" . + $this->db->prepare_string($item['type']) . "','" . + $this->db->prepare_string($item['log_message']) . "'" . $val2add . + ")"; + + $result = $this->db->exec_query($sql); + if ($result) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . + " SET name='" . $this->db->prepare_string($item['name']) . "' " . + " WHERE id={$ret['id']} "; + $this->db->exec_query($sql); + } else { + $ret['msg'] = $this->db->error_msg(); + $ret['status_ok'] = 0; + $ret['id'] = - 1; + } + + return $ret; + } + + /** + */ + private function create_new_revision($rspecID, $item) + { + // Needed to get higher revision NUMBER, to generata new NUMBER + $source_info = $this->get_last_child_info($rspecID); + $current_rev = 0; + if (! is_null($source_info)) { + $current_rev = $source_info['revision']; + } + $current_rev ++; + $item['revision'] = $current_rev ++; + + return $this->create_revision($rspecID, $item); + } + + /** + * + * @param + * id: parent id + * @param + * child_type: 'revision' + * + * @return + */ + private function get_last_child_info($id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['options'] = array( + 'child_type' => 'revision', + 'output' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $info = null; + $target_cfg = array( + 'revision' => array( + 'table' => 'req_specs_revisions', + 'field' => 'revision' + ) + ); + + $child_type = $my['options']['child_type']; // just for readability + $table = $target_cfg[$child_type]['table']; + $field = $target_cfg[$child_type]['field']; + + $sql = " /* $debugMsg */ SELECT COALESCE(MAX($field),-1) AS $field " . + " FROM {$this->tables[$table]} CHILD," . + " {$this->tables['nodes_hierarchy']} NH WHERE " . + " NH.id = CHILD.id " . " AND NH.parent_id = {$id} "; + + $max_verbose = $this->db->fetchFirstRowSingleColumn($sql, $field); + if ($max_verbose >= 0) { + $sql = "/* $debugMsg */ SELECT "; + + switch ($my['options']['output']) { + case 'credentials': + $sql .= " CHILD.parent_id,CHILD.id,CHILD.revision,CHILD.doc_id "; + break; + + case 'full': + default: + $sql .= " CHILD.* "; + break; + } + + $sql .= " FROM {$this->tables[$table]} CHILD," . + " {$this->tables['nodes_hierarchy']} NH " . + " WHERE $field = {$max_verbose} AND NH.id = CHILD.id AND NH.parent_id = {$id}"; + + $info = $this->db->fetchFirstRow($sql); + } + return $info; + } + + /** + * + * @param + * id: parent id + * @param + * child_type: 'revision' + * + * @return + */ + public function getRevisionsCount($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT COUNT(0) AS qty" . + " FROM {$this->tables['req_specs_revisions']} RSPEC_REV" . + " WHERE RSPEC_REV.parent_id = {$id} "; + + $dummy = $this->db->get_recordset($sql); + return $dummy[0]['qty']; + } + + /** + * used to create overwiew of changes between revisions + */ + private function get_history($id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'output' => "map", + 'decode_user' => false, + 'order_by_dir' => 'DESC' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $labels['undefined'] = lang_get('undefined'); + $sql = "/* $debugMsg */" . + " SELECT RSREV.id AS revision_id, RSREV.revision," . + " RSREV.creation_ts, RSREV.author_id, " . + " RSREV.modification_ts, RSREV.modifier_id, " . + " RSREV.revision, RSREV.scope, " . + " RSREV.status,RSREV.type,RSREV.name, RSREV.doc_id, " . + " COALESCE(RSREV.log_message,'') AS log_message" . + " FROM {$this->tables['req_specs_revisions']} RSREV " . + " WHERE RSREV.parent_id = {$id} " . + " ORDER BY RSREV.revision {$my['options']['order_by_dir']} "; + + switch ($my['options']['output']) { + case 'map': + $rs = $this->db->fetchRowsIntoMap($sql, 'revision_id'); + break; + + case 'array': + $rs = $this->db->get_recordset($sql); + break; + } + + if (! is_null($rs)) { + $key2loop = array_keys($rs); + foreach ($key2loop as $ap) { + $rs[$ap]['item_id'] = $rs[$ap]['revision_id']; + + // IMPORTANT NOTICE + // each DBMS uses a different (unfortunatelly) way to signal NULL DATE + // + // We need to Check with ALL DB types + // MySQL NULL DATE -> "0000-00-00 00:00:00" + // Postgres NULL DATE -> NULL + // MSSQL NULL DATE - ??? + $key4date = 'creation_ts'; + $key4user = 'author_id'; + + $nullTS = $this->db->db_null_timestamp(); + if (($rs[$ap]['modification_ts'] != $nullTS) && + ! is_null($rs[$ap]['modification_ts']) && + ! is_null($rs[$ap]['modifier_id'])) { + $key4date = 'modification_ts'; + $key4user = 'modifier_id'; + } + $rs[$ap]['timestamp'] = $rs[$ap][$key4date]; + $rs[$ap]['last_editor'] = $rs[$ap][$key4user]; + + // decode user_id for last_editor + $user = tlUser::getByID($this->db, $rs[$ap]['last_editor']); + $rs[$ap]['last_editor'] = $user ? $user->getDisplayName() : $labels['undefined']; + } + } + + $history = $rs; + if ($my['options']['decode_user'] && ! is_null($history)) { + $this->decode_users($history); + } + + return $history; + } + + /** + */ + private function decode_users(&$rs) + { + $userCache = null; // key: user id, value: display name + $key2loop = array_keys($rs); + $labels['undefined'] = lang_get('undefined'); + $user_keys = array( + 'author' => 'author_id', + 'modifier' => 'modifier_id' + ); + foreach ($key2loop as $key) { + foreach ($user_keys as $ukey => $userid_field) { + $rs[$key][$ukey] = ''; + if (trim($rs[$key][$userid_field]) != "") { + if (! isset($userCache[$rs[$key][$userid_field]])) { + $user = tlUser::getByID($this->db, + $rs[$key][$userid_field]); + $rs[$key][$ukey] = $user ? $user->getDisplayName() : $labels['undefined']; + $userCache[$rs[$key][$userid_field]] = $rs[$key][$ukey]; + } else { + $rs[$key][$ukey] = $userCache[$rs[$key][$userid_field]]; + } + } + } + } + } + + /** + */ + private function getRevisionTemplate() + { + return array( + 'revision' => 1, + 'doc_id' => null, + 'name' => null, + 'scope' => null, + 'type' => null, + 'status' => 1, + 'total_req' => 0, + 'log_message' => '', + 'author_id' => - 1 + ); + } + + /** + */ + public function clone_revision($rspecID, $item) + { + // Create a new revision node on db + $ret = array( + 'msg' => 'ok', + 'status_ok' => 1, + 'id' => - 1 + ); + $ret['id'] = $this->tree_mgr->new_node($rspecID, + $this->node_types_descr_id['requirement_spec_revision']); + + if (! isset($item['source_id']) || ($item['source_id'] < 0)) { + $dummy = $this->get_last_child_info($rspecID); + $source_id = $dummy['id']; + } else { + $source_id = $item['source_id']; + } + + // get data to clone + $sourceItem = $this->getRevisionByID($source_id); + $sourceItem['log_message'] = $item['log_message']; + $sourceItem['author_id'] = $item['author_id']; + $sourceItem['revision'] ++; + + unset($sourceItem['modifier_id']); + unset($sourceItem['modification_ts']); + unset($sourceItem['creation_ts']); + + $ret = $this->create_revision($rspecID, $sourceItem); + if ($ret['status_ok']) { + $source = array( + 'parent_id' => $rspecID, + 'item_id' => $source_id, + 'tproject_id' => $sourceItem['testproject_id'] + ); + $this->copy_cfields($source, $ret['id']); + } + + return $ret; + } + + /** + */ + public function getRevisionByID($id, $options = null) + { + $my['options'] = array( + 'decode_user' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = '/* $debugMsg */' . " SELECT RSPEC_REV.*, RSPEC.testproject_id " . + " FROM {$this->tables['req_specs_revisions']} RSPEC_REV " . + " JOIN {$this->tables['req_specs']} RSPEC " . + " ON RSPEC.id = RSPEC_REV.parent_id " . " WHERE RSPEC_REV.id={$id} "; + + $ret = $this->db->get_recordset($sql); + if (! is_null($ret) && $my['options']['decode_user']) { + $this->decode_users($ret); + } + return ! is_null($ret) ? $ret[0] : null; + } + + /** + */ + private function update_revision($item) + { + if (! isset($item['revision_id']) || is_null($item['revision_id'])) { + // will go to update LATEST + $info = $this->get_last_child_info($item['id'], + array( + 'output' => 'credentials' + )); + $targetID = $info['id']; + } else { + $targetID = $item['revision_id']; + } + + $sql = '/* $debugMsg */' . + " UPDATE {$this->tables['req_specs_revisions']} " . " SET scope = '" . + $this->db->prepare_string($item['scope']) . "', " . + " modifier_id = " . $item['modifier_id'] . ", " . + " modification_ts = " . $this->db->db_now() . + " WHERE id={$targetID} "; + $this->db->exec_query($sql); + return array( + 'id' => $targetID + ); + } + + /** + */ + public function get_all_id_in_testproject($tproject_id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'output' => 'classic' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = "/* $debugMsg */ " . + " SELECT RSPEC.id FROM {$this->object_table} RSPEC WHERE testproject_id={$tproject_id}"; + + $rs = $this->db->get_recordset($sql); + switch ($my['options']['output']) { + case 'id': + $rx = array(); + foreach ($rs as $elem) { + $rx[] = $elem['id']; + } + return $rx; + break; + + default: + return $rs; + break; + } + } + + /** + */ + private function getAssignedCoverage($id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'order_by' => " ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id", + 'output' => 'standard' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = "/* $debugMsg */ SELECT NH_REQ.id,U.login, REQ_COV.creation_ts " . + " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . + " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ_COV.req_id=NH_REQ.id " . + " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = REQ_COV.author_id "; + $sql .= " WHERE NH_REQ.parent_id={$id} " . + " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']}"; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + */ + private function simplexml_load_file_helper($filename) + { + // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + libxml_disable_entity_loader(true); + $zebra = file_get_contents($filename); + return @simplexml_load_string($zebra); + } + + /** + * + * @used-by + */ + public function getFileUploadRelativeURL($id) + { + return "lib/requirements/reqSpecEdit.php?doAction=fileUpload&req_spec_id=" . + intval($id); + } + + /** + * + * @used-by + */ + public function getDeleteAttachmentRelativeURL($id) + { + return "lib/requirements/reqSpecEdit.php?doAction=deleteFile&req_spec_id=" . + intval($id) . "&file_id="; + } + + /** + * Copy attachments from source to target + */ + private function copyAttachments($source_id, $target_id) + { + return $this->attachmentRepository->copyAttachments($source_id, + $target_id, $this->attachmentTableName); + } + + /** + */ + public function getReqsOnSpecForLatestTCV($id, $tcase_id = null, + $options = null, $filters = null) + { + static $tcMgr; + + if (! $tcMgr) { + $tcMgr = new testcase($this->db); + } + + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'order_by' => ' ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id ', + 'output' => 'standard', + 'outputLevel' => 'std', + 'decodeUsers' => true, + 'version_string' => lang_get('version_short') + ); + + $my['options'] = array_merge($my['options'], (array) $options); + + // null => do not filter + $my['filters'] = array( + 'link_status' => 1, + 'type' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $ltcv = null; + if (null == $tcase_id) { + $tcversionJoin = " JOIN {$this->views['latest_tcase_version_id']} LTCV " . + " ON LTCV.tcversion_id = RCOV.tcversion_id "; + } else { + $tcInfo = current($tcMgr->get_last_active_version($tcase_id)); + $ltcv = intval($tcInfo['tcversion_id']); + $tcversionJoin = " AND RCOV.tcversion_id = " . $ltcv; + } + + // Step 1 - + // get all req inside the Req Spec Folder ONLY DIRECT CHILDREN + // + // Step 2 - + // Need to get only the Req Versions That are Assigned + // to Latest Active Test Case Version + // I'm doing this because I'm calling this function from + // the Test Spec Tree and in this context I CAN NOT choose + // test case version + $filters = ''; + if (null != $my['filters']['link_status']) { + $nu = (array) $my['filters']['link_status']; + $filters .= ' AND link_status IN(' . implode(',', $nu) . ')'; + } + + // Postgres => USER is reserved keyword !! + $lblVersion = $my['options']['version_string']; + $sql = "/* $debugMsg */ " . + " SELECT RCOV.id as link_id, NH_REQ.id,RCOV.req_version_id," . + " REQVER.scope, " . + " CONCAT(NH_REQ.name,' [{$lblVersion}',REQVER.version ,'] ' ) AS title," . + " REQ.req_doc_id, REQVER.version,TLUSER.login AS coverage_author, RCOV.creation_ts AS coverage_ts, REQVER.is_open AS reqver_is_open, - TCVER.is_open AS tcversion_is_open," . - " CASE " . - " WHEN RCOV.link_status = " . LINK_TC_REQ_OPEN . - " THEN 1 " . - " ELSE 0 " . - " END AS can_be_removed " . - " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . - - " JOIN {$this->tables['req_coverage']} RCOV " . - - " ON RCOV.req_id = NH_REQ.id " . $tcversionJoin . - - " JOIN {$this->tables['req_versions']} REQVER " . - " ON REQVER.id = RCOV.req_version_id " . - - " JOIN {$this->tables['tcversions']} TCVER " . - " ON TCVER.id = RCOV.tcversion_id " . - - " JOIN {$this->tables['requirements']} REQ " . - " ON REQ.id = NH_REQ.id " . - - " LEFT OUTER JOIN {$this->tables['users']} TLUSER " . - " ON TLUSER.id = RCOV.author_id " . - - " WHERE NH_REQ.parent_id=" . intval($id) . - " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} AND RCOV.is_active = 1 {$filters} "; - - $itemSet = $this->db->get_recordset($sql); - return $itemSet; - } - - - /** - * - * - */ - function getReqsOnSpecNotLinkedToLatestTCV($id, $tcase_id=null, $opt=null, $filters = null) { - - static $tcMgr; - - if( !$tcMgr ) { - $tcMgr = new testcase( $this->db ); - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array( 'order_by' => - ' ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id ', - 'output' => 'standard', - 'outputLevel' => 'std', 'decodeUsers' => true); - - $my['options'] = array_merge($my['options'], (array)$options); - - // null => do not filter - $my['filters'] = array('status' => null, 'type' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - // - $ltcv = null; - if( null == $tcase_id ) { - $tcversionJoin = - " LEFT JOIN {$this->views['latest_tcase_version_id']} LTCV " . - " ON LTCV.tcversion_id = RCOV.tcversion_id "; - } else { - $tcInfo = current($tcMgr->get_last_active_version($tcase_id)); - $ltcv = intval($tcInfo['tcversion_id']); - $tcversionJoin = " AND RCOV.tcversion_id = " . $ltcv; - } - - // Step 1 - - // get all req inside the Req Spec Folder ONLY DIRECT CHILDREN - // - // Step 2 - - // Need to get only the Req Versions That are Assigned - // to Latest Active Test Case Version - // I'm doing this because I'm calling this function from - // the Test Spec Tree and in this context I CAN NOT choose - // test case version - // - $sql = "/* $debugMsg */ " . - " SELECT NH_REQ.id,REQVER.scope, " . - " CONCAT(NH_REQ.name,' [v', REQVER.version ,'] ' ) AS title," . - " REQ.req_doc_id, REQVER.version," . - " (CASE WHEN REQVER.version IS NULL " . - " THEN 1 ELSE 0 END) AS can_be_deleted " . - - " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . - " JOIN {$this->tables['requirements']} REQ " . - " ON REQ.id = NH_REQ.id " . - - - " LEFT JOIN {$this->tables['req_coverage']} RCOV " . - " ON RCOV.req_id = NH_REQ.id " . - $tcversionJoin . - - " LEFT JOIN {$this->tables['req_versions']} REQVER " . - " ON REQVER.id = RCOV.req_version_id " . - - - " WHERE NH_REQ.parent_id=" . intval($id) . - " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']}"; - - $itemSet = $this->db->get_recordset($sql); - return $itemSet; - } - - - /** - * - */ - function getReqsOnRSpecForLTCVOnTSuite($id, $tsuite_id, $options=null, $filters = null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array( 'order_by' => - ' ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id ', - 'output' => 'standard', - 'outputLevel' => 'std', 'decodeUsers' => true); - - $my['options'] = array_merge($my['options'], (array)$options); - - // null => do not filter - $my['filters'] = array('link_status' => 1, 'type' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - - // Step 1 - - // get all req inside the Req Spec Folder ONLY DIRECT CHILDREN - // - // Step 2 - - // Need to get only the Req Versions That are Assigned - // to Latest Active Test Case Version - // I'm doing this because I'm calling this function from - // the Test Spec Tree and in this context I CAN NOT choose - // test case version - // - $filters = ''; - if( null != $my['filters']['link_status'] ) { - $nu = (array)$my['filters']['link_status']; - $filters .= ' AND link_status IN(' . implode(',',$nu) . ')'; - } - - $getLatestTCVersion = - " SELECT LTCV.tcversion_id AS tcversion_id + TCVER.is_open AS tcversion_is_open," . " CASE " . + " WHEN RCOV.link_status = " . LINK_TC_REQ_OPEN . + " THEN 1 " . " ELSE 0 " . " END AS can_be_removed " . + " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . + " JOIN {$this->tables['req_coverage']} RCOV " . + " ON RCOV.req_id = NH_REQ.id " . $tcversionJoin . + " JOIN {$this->tables['req_versions']} REQVER " . + " ON REQVER.id = RCOV.req_version_id " . + " JOIN {$this->tables['tcversions']} TCVER " . + " ON TCVER.id = RCOV.tcversion_id " . + " JOIN {$this->tables['requirements']} REQ " . + " ON REQ.id = NH_REQ.id " . + " LEFT OUTER JOIN {$this->tables['users']} TLUSER " . + " ON TLUSER.id = RCOV.author_id " . " WHERE NH_REQ.parent_id=" . + intval($id) . + " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} AND RCOV.is_active = 1 {$filters} "; + + return $this->db->get_recordset($sql); + } + + /** + */ + private function getReqsOnSpecNotLinkedToLatestTCV($id, $tcase_id = null, + $opt = null, $filters = null) + { + static $tcMgr; + + if (! $tcMgr) { + $tcMgr = new testcase($this->db); + } + + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'order_by' => ' ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id ', + 'output' => 'standard', + 'outputLevel' => 'std', + 'decodeUsers' => true + ); + + $my['options'] = array_merge($my['options'], (array) $options); + + // null => do not filter + $my['filters'] = array( + 'status' => null, + 'type' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $ltcv = null; + if (null == $tcase_id) { + $tcversionJoin = " LEFT JOIN {$this->views['latest_tcase_version_id']} LTCV " . + " ON LTCV.tcversion_id = RCOV.tcversion_id "; + } else { + $tcInfo = current($tcMgr->get_last_active_version($tcase_id)); + $ltcv = intval($tcInfo['tcversion_id']); + $tcversionJoin = " AND RCOV.tcversion_id = " . $ltcv; + } + + // Step 1 - + // get all req inside the Req Spec Folder ONLY DIRECT CHILDREN + // + // Step 2 - + // Need to get only the Req Versions That are Assigned + // to Latest Active Test Case Version + // I'm doing this because I'm calling this function from + // the Test Spec Tree and in this context I CAN NOT choose + // test case version + $sql = "/* $debugMsg */ " . " SELECT NH_REQ.id,REQVER.scope, " . + " CONCAT(NH_REQ.name,' [v', REQVER.version ,'] ' ) AS title," . + " REQ.req_doc_id, REQVER.version," . + " (CASE WHEN REQVER.version IS NULL " . + " THEN 1 ELSE 0 END) AS can_be_deleted " . + " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . + " JOIN {$this->tables['requirements']} REQ " . + " ON REQ.id = NH_REQ.id " . + " LEFT JOIN {$this->tables['req_coverage']} RCOV " . + " ON RCOV.req_id = NH_REQ.id " . $tcversionJoin . + " LEFT JOIN {$this->tables['req_versions']} REQVER " . + " ON REQVER.id = RCOV.req_version_id " . " WHERE NH_REQ.parent_id=" . + intval($id) . + " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']}"; + + return $this->db->get_recordset($sql); + } + + /** + */ + private function getReqsOnRSpecForLTCVOnTSuite($id, $tsuite_id, + $options = null, $filters = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'order_by' => ' ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id ', + 'output' => 'standard', + 'outputLevel' => 'std', + 'decodeUsers' => true + ); + + $my['options'] = array_merge($my['options'], (array) $options); + + // null => do not filter + $my['filters'] = array( + 'link_status' => 1, + 'type' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + // Step 1 - + // get all req inside the Req Spec Folder ONLY DIRECT CHILDREN + // + // Step 2 - + // Need to get only the Req Versions That are Assigned + // to Latest Active Test Case Version + // I'm doing this because I'm calling this function from + // the Test Spec Tree and in this context I CAN NOT choose + // test case version + $filters = ''; + if (null != $my['filters']['link_status']) { + $nu = (array) $my['filters']['link_status']; + $filters .= ' AND link_status IN(' . implode(',', $nu) . ')'; + } + + $getLatestTCVersion = " SELECT LTCV.tcversion_id AS tcversion_id FROM {$this->tables['nodes_hierarchy']} NHX_TC - JOIN {$this->tables['nodes_hierarchy']} NHX_TCV + JOIN {$this->tables['nodes_hierarchy']} NHX_TCV ON NHX_TCV.parent_id = NHX_TC.id - JOIN {$this->views['latest_tcase_version_id']} LTCV + JOIN {$this->views['latest_tcase_version_id']} LTCV ON LTCV.tcversion_id = NHX_TCV.id - WHERE NHX_TC.parent_id = $tsuite_id "; - - // Postgres => USER is reserved keyword !! - $sql = "/* $debugMsg */ " . - " SELECT RCOV.id as link_id, NH_REQ.id,RCOV.req_version_id," . - " REQVER.scope, " . - " CONCAT(NH_REQ.name,' [v', REQVER.version ,'] ' ) AS title," . - " REQ.req_doc_id, REQVER.version," . - " TLUSER.login AS coverage_author," . - " RCOV.creation_ts AS coverage_ts,REQVER.is_open, - REQVER.is_open AS reqver_is_open," . - " CASE " . - " WHEN RCOV.link_status = " . LINK_TC_REQ_OPEN . - " THEN 1 " . - " ELSE 0 " . - " END AS can_be_removed " . - " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . - - " JOIN {$this->tables['req_coverage']} RCOV " . - - " ON RCOV.req_id = NH_REQ.id " . - " AND RCOV.tcversion_id IN ( $getLatestTCVersion ) " . - - " JOIN {$this->tables['req_versions']} REQVER " . - " ON REQVER.id = RCOV.req_version_id " . - - " JOIN {$this->tables['requirements']} REQ " . - " ON REQ.id = NH_REQ.id " . - - " LEFT OUTER JOIN {$this->tables['users']} TLUSER " . - " ON TLUSER.id = RCOV.author_id " . - - " WHERE NH_REQ.parent_id=" . intval($id) . - " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} AND RCOV.is_active = 1 {$filters} "; - - //echo $sql; - $itemSet = $this->db->get_recordset($sql); - //var_dump($itemSet); - - return $itemSet; - } - - - - /* - function: getAllLatestRQVOnReqSpec - get LATEST VERSION OF requirements contained in a req spec - ONLY direct children - - args: id: req spec id - - returns: array of rows - */ - function getAllLatestRQVOnReqSpec($reqSpecID, $opt=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $options = array('output' => 'mapOnReqID'); - $options = array_merge($options,(array)$opt); - - $reqNode = $this->node_types_descr_id['requirement']; - $sql = "/* $debugMsg */ + WHERE NHX_TC.parent_id = $tsuite_id "; + + // Postgres => USER is reserved keyword !! + $sql = "/* $debugMsg */ " . + " SELECT RCOV.id as link_id, NH_REQ.id,RCOV.req_version_id," . + " REQVER.scope, " . + " CONCAT(NH_REQ.name,' [v', REQVER.version ,'] ' ) AS title," . + " REQ.req_doc_id, REQVER.version," . + " TLUSER.login AS coverage_author," . + " RCOV.creation_ts AS coverage_ts,REQVER.is_open, + REQVER.is_open AS reqver_is_open," . " CASE " . + " WHEN RCOV.link_status = " . LINK_TC_REQ_OPEN . + " THEN 1 " . " ELSE 0 " . " END AS can_be_removed " . + " FROM {$this->tables['nodes_hierarchy']} NH_REQ " . + " JOIN {$this->tables['req_coverage']} RCOV " . + " ON RCOV.req_id = NH_REQ.id " . + " AND RCOV.tcversion_id IN ( $getLatestTCVersion ) " . + " JOIN {$this->tables['req_versions']} REQVER " . + " ON REQVER.id = RCOV.req_version_id " . + " JOIN {$this->tables['requirements']} REQ " . + " ON REQ.id = NH_REQ.id " . + " LEFT OUTER JOIN {$this->tables['users']} TLUSER " . + " ON TLUSER.id = RCOV.author_id " . " WHERE NH_REQ.parent_id=" . + intval($id) . + " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} AND RCOV.is_active = 1 {$filters} "; + + return $this->db->get_recordset($sql); + } + + /* + * function: getAllLatestRQVOnReqSpec + * get LATEST VERSION OF requirements contained in a req spec + * ONLY direct children + * + * args: id: req spec id + * + * returns: array of rows + */ + public function getAllLatestRQVOnReqSpec($reqSpecID, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $options = array( + 'output' => 'mapOnReqID' + ); + $options = array_merge($options, (array) $opt); + + $reqNode = $this->node_types_descr_id['requirement']; + $sql = "/* $debugMsg */ SELECT NH_REQ.id,REQV.id AS req_version_id, REQV.version, REQV.scope, NH_REQ.name AS title, CONCAT(REQ.req_doc_id,' [', REQV.version, '] ') AS req_doc_id FROM {$this->tables['nodes_hierarchy']} NH_REQ JOIN {$this->views['latest_req_version_id']} LRQV - ON LRQV.req_id = NH_REQ.id + ON LRQV.req_id = NH_REQ.id JOIN {$this->tables['requirements']} REQ - ON REQ.id = NH_REQ.id - + ON REQ.id = NH_REQ.id + JOIN {$this->tables['req_versions']} REQV ON REQV.id = LRQV.req_version_id - - WHERE NH_REQ.parent_id = {$reqSpecID} - AND NH_REQ.node_type_id = $reqNode "; - - switch($options['output']) { - case 'array': - $rs = $this->db->get_recordset($sql); - break; - case 'mapOnId': - default: - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - break; - } - return $rs; - } - -} // class end \ No newline at end of file + WHERE NH_REQ.parent_id = {$reqSpecID} + AND NH_REQ.node_type_id = $reqNode "; + + switch ($options['output']) { + case 'array': + $rs = $this->db->get_recordset($sql); + break; + + case 'mapOnId': + default: + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + break; + } + return $rs; + } +} diff --git a/lib/functions/requirements.inc.php b/lib/functions/requirements.inc.php index c95ff4418c..ce1b156e1f 100644 --- a/lib/functions/requirements.inc.php +++ b/lib/functions/requirements.inc.php @@ -1,1005 +1,998 @@ -{{XMLCODE}}"; - $elemTpl = "\t".'". - '<![CDATA['."\n||TITLE||\n]]>".''. - '".''. - ''."\n"; - $info = array("||DOCID||" => "req_doc_id","||TITLE||" => "title", - "||DESCRIPTION||" => "scope"); - return exportDataToXML($reqData,$rootElem,$elemTpl,$info); -} - - -/** Process CVS file contents with requirements into TL - * and creates an array with reports - * @return array_of_strings list of particular REQ data with resolution comment - * - * - **/ -function executeImportedReqs(&$db,$arrImportSource, $map_cur_reqdoc_id,$conflictSolution, - $emptyScope, $idSRS, $tprojectID, $userID) -{ - define('SKIP_CONTROLS',1); - - $req_mgr = new requirement_mgr($db); - $import_status = null; - $field_size = config_get('field_size'); - - foreach ($arrImportSource as $data) - { - $docID = trim_and_limit($data['docid'],$field_size->req_docid); - $title = trim_and_limit($data['title'],$field_size->req_title); - $scope = $data['description']; - $type = $data['type']; - $status = $data['status']; - $expected_coverage = $data['expected_coverage']; - $node_order = $data['node_order']; - - if (($emptyScope == 'on') && empty($scope)) - { - // skip rows with empty scope - $import_status = lang_get('req_import_result_skipped'); - } - else - { - $crash = $map_cur_reqdoc_id && array_search($docID, $map_cur_reqdoc_id); - if($crash) - { - // process conflict according to choosen solution - tLog('Conflict found. solution: ' . $conflictSolution); - $import_status['msg'] = 'Error'; - if ($conflictSolution == 'overwrite') - { - $item = current($req_mgr->getByDocID($docID,$tprojectID)); - $last_version = $req_mgr->get_last_version_info($item['id']); - - // BUGID 0003745: CSV Requirements Import Updates Frozen Requirement - if( $last_version['is_open'] == 1 ) - { - $op = $req_mgr->update($item['id'],$last_version['id'],$docID,$title,$scope,$userID, - $status,$type,$expected_coverage,$node_order,SKIP_CONTROLS); - if( $op['status_ok']) - { - $import_status['msg'] = lang_get('req_import_result_overwritten'); - } - } - else - { - $import_status['msg'] = lang_get('req_import_result_skipped_is_frozen'); - } - } - elseif ($conflictSolution == 'skip') - { - // no work - $import_status['msg'] = lang_get('req_import_result_skipped'); - } - } - else - { - // no conflict - just add requirement - $import_status = $req_mgr->create($idSRS,$docID,$title,$scope,$userID,$status,$type, - $expected_coverage,$node_order); - } - $arrImport[] = array('doc_id' => $docID, 'title' => $title, 'import_status' => $import_status['msg']); - } - } - return $arrImport; -} - -/* - On version 1.9 is NOT USED when importing from XML format -*/ -function compareImportedReqs(&$dbHandler,$arrImportSource,$tprojectID,$reqSpecID) -{ - $reqCfg = config_get('req_cfg'); - $labels = array('type' => $reqCfg->type_labels, 'status' => $reqCfg->status_labels); - $verbose = array('type' => null, 'status' => null); - $cache = array('type' => null, 'status' => null); - $cacheKeys = array_keys($cache); - - $unknown_code = lang_get('unknown_code'); - $reqMgr = new requirement_mgr($dbHandler); - $arrImport = null; - if( ($loop2do=count($arrImportSource)) ) - { - $getOptions = array('output' => 'minimun'); - $messages = array('ok' => '', 'import_req_conflicts_other_branch' => '','import_req_exists_here' => ''); - foreach($messages as $key => $dummy) - { - $messages[$key] = lang_get($key); - } - - for($idx=0 ; $idx < $loop2do; $idx++) - { - $msgID = 'ok'; - $req = $arrImportSource[$idx]; - - // Check: - // If item with SAME DOCID exists inside container - // If there is a hit - // We will follow user option: update,create new version - // - // If do not exist check must be repeated, but on WHOLE test project - // If there is a hit -> we can not create - // else => create - // - // - // 20100321 - we do not manage yet user option - $check_in_reqspec = $reqMgr->getByDocID($req['docid'],$tprojectID,$reqSpecID,$getOptions); - if(is_null($check_in_reqspec)) - { - $check_in_tproject = $reqMgr->getByDocID($req['docid'],$tprojectID,null,$getOptions); - if(!is_null($check_in_tproject)) - { - $msgID = 'import_req_conflicts_other_branch'; - } - } - else - { - $msgID = 'import_req_exists_here'; - } - - foreach($cacheKeys as $attr) - { - if( isset($labels[$attr][$req[$attr]]) ) - { - if( !isset($cache[$attr][$req[$attr]]) ) - { - $cache[$attr][$req[$attr]] = lang_get($labels[$attr][$req[$attr]]); - } - $verbose[$attr] = $cache[$attr][$req[$attr]]; - } - else - { - $verbose[$attr] = sprintf($unknown_code,$req[$attr]); - } - } - - $arrImport[] = array('req_doc_id' => $req['docid'], 'title' => trim($req['title']), - 'scope' => $req['description'], 'type' => $verbose['type'], - 'status' => $verbose['status'], 'expected_coverage' => $req['expected_coverage'], - 'node_order' => $req['order'], 'check_status' => $messages[$msgID]); - } - } - return $arrImport; -} - -function getReqDocIDs(&$db,$srs_id) -{ - $req_spec_mgr = new requirement_spec_mgr($db); - $arrCurrentReq = $req_spec_mgr->get_requirements($srs_id); - - $result = null; - if (count($arrCurrentReq)) - { - // only if some reqs exist - foreach ($arrCurrentReq as $data) - { - $result[$data['id']] = $data['req_doc_id']; - } - } - - return($result); -} - - - -/** - * load imported data from file and parse it to array - * @return array_of_array each inner array include fields title and scope (and more) - */ -function loadImportedReq($fileName, $importType) -{ - $retVal = null; - switch($importType) - { - case 'csv': - $pfn = "importReqDataFromCSV"; - break; - - case 'csv_doors': - $pfn = "importReqDataFromCSVDoors"; - break; - - case 'DocBook': - $pfn = "importReqDataFromDocBook"; - break; - } - - if ($pfn) - { - $retVal = $pfn($fileName); - if($importType == 'DocBook') - { - // this structure if useful when importing from CSV - // $retVal = array('userFeedback' => arra(),'info' => null); - // - // But we need to return same data structure ALWAYS - // for DocBook we do not use 'parsedCounter' and 'syntaxError' - // - $dummy = array('userFeedback' => null, 'info' => $retVal); - $retVal = $dummy; - } - } - - return $retVal; -} - -/** - * importReqDataFromCSV - * - */ -function importReqDataFromCSV($fileName) -{ - // CSV line format - $fieldMappings = array("docid","title","description","type","status","expected_coverage","node_order"); - - - $options = array('delimiter' => ',' , 'fieldQty' => count($fieldMappings)); - $impData = importCSVData($fileName,$fieldMappings,$options); - - $reqData = &$impData['info']; - if($reqData) - { - // lenght will be adjusted to these values - $field_size=config_get('field_size'); - $fieldLength = array("docid" => $field_size->req_docid, "title" => $field_size->req_title); - - $reqCfg = config_get('req_cfg'); - $fieldDefault = array("type" => array('check' => 'type_labels', 'value' => TL_REQ_TYPE_FEATURE), - "status" => array('check' => 'status_labels' , 'value' => TL_REQ_STATUS_VALID)); - - $loop2do = count($reqData); - for($ddx=0; $ddx < $loop2do; $ddx++) - { - foreach($reqData[$ddx] as $fieldKey => &$fieldValue) - { - // Adjust Lenght - if( isset($fieldLength[$fieldKey]) ) - { - $fieldValue = trim_and_limit($fieldValue,$fieldLength[$fieldKey]); - } - else if(isset($fieldDefault[$fieldKey])) - { - // Assign default value - $checkKey = $fieldDefault[$fieldKey]['check']; - $checkObj = &$reqCfg->$checkKey; - if( !isset($checkObj[$fieldValue]) ) - { - $fieldValue = $fieldDefault[$fieldKey]['value']; - } - } - } - } - } - return $impData; -} - -/** - * importReqDataFromCSVDoors - * - * @internal revision - * - */ -function importReqDataFromCSVDoors($fileName) -{ - // Some keys are strings, other numeric - $fieldMappings = array("Object Identifier" => "title","Object Text" => "description", - "Created By","Created On","Last Modified By","Last Modified On"); - - $options = array('delimiter' => ',', 'fieldQty' => count($fieldMappings), 'processHeader' => true); - $impData = importCSVData($fileName,$fieldMappings,$options); - - return $impData; -} - -/** - * Parses one 'informaltable' XML entry and produces HTML table as string. - * - * XML relationship: - * informaltable -> tgroup -> thead -> row -> entry - * -> tbody -> row -> entry - * - * 20081103 - sisajr - */ -function getDocBookTableAsHtmlString($docTable,$parseCfg) -{ - $resultTable = ""; - foreach ($docTable->children() as $tgroup) - { - if ($tgroup->getName() != $parseCfg->table_group) - { - continue; - } - - $table = ""; - foreach ($tgroup->children() as $tbody) - { - // get table head - $tbodyName = $tbody->getName() ; - $doIt = false; - if( $tbodyName == $parseCfg->table_head) - { - $cellTag = array('open' => '', 'close' => ''); - $doIt = true; - } - else if( $tbodyName == $parseCfg->table_body) - { - $cellTag = array('open' => '', 'close' => ''); - $doIt = true; - } - - if( $doIt ) - { - foreach ($tbody->children() as $row) - { - if( $row->getName() == $parseCfg->table_row ) - { - $table_row = ""; - foreach ($row->children() as $entry) - { - if ( ($ename = $entry->getName()) == $parseCfg->table_entry) - { - if( $entry->count() == 0 ) - { - $table_row .= $cellTag['open'] . (string)$entry . $cellTag['close']; - } - else - { - $table_row .= $cellTag['open']; - foreach($parseCfg->table_entry_children as $ck) - { - if( property_exists($entry,$ck) ) - { - $table_row .= (string)$entry->$ck; - } - } - $table_row .= $cellTag['close']; - } - } - } - - $table_row .= ""; - $table .= $table_row; - } - } - } - } - - $resultTable .= "" . $table . "
    "; - } - - return $resultTable; -} - -/** - * Imports data from DocBook XML - * - * @return array of map - * - */ -function importReqDataFromDocBook($fileName) -{ - $req_cfg = config_get('req_cfg'); - $docbookCfg = $req_cfg->importDocBook; - $xmlReqs = null; - $xmlData = null; - $field_size=config_get('field_size'); - - $simpleXMLObj = simplexml_load_file($fileName); - $num_elem = count($simpleXMLObj->{$docbookCfg->requirement}); - - $idx=0; - foreach($simpleXMLObj->{$docbookCfg->requirement} as $xmlReq) - { - // get all child elements of this requirement - $title = ""; - $description = ""; - $children = $xmlReq->children(); - foreach ($children as $child) - { - $nodeName = $child->getName(); - if ($nodeName == $docbookCfg->title ) - { - $title = (string)$child; - } - else if ($nodeName == $docbookCfg->ordered_list) - { - $list = ""; - foreach( $child->children() as $item ) - { - if( $item->getName() == $docbookCfg->list_item ) - { - if( $item->count() == 0 ) - { - $list .= "
  • " . (string)$item . "
  • "; - } - else - { - foreach($docbookCfg->list_item_children as $ck) - { - if( property_exists($item,$ck) ) - { - $list .= "
  • " . (string)$item->$ck . "
  • "; - } - } - } - } - } - $description .= "
      " . $list . "
    "; - } - else if ($nodeName == $docbookCfg->table) - { - $description .= getDocBookTableAsHtmlString($child,$docbookCfg); - } - else if ($nodeName == $docbookCfg->paragraph) - { - $description .= "

    " . (string)$child . "

    "; - } - else - { - $description .= "

    " . (string)$child . "

    "; - } - - - } - $xmlData[$idx]['description'] = $description; - $xmlData[$idx]['title'] = trim_and_limit($title,$field_size->req_title); - - // parse Doc ID from requirement title - // first remove any weird characters before the title. This could be probably omitted - $xmlData[$idx]['title'] = preg_replace("/^[^a-zA-Z_0-9]*/","",$xmlData[$idx]['title']); - - // get Doc ID - // - // this will create Doc ID as words ended with number - // Example: Req BL 20 Business Logic - // Doc ID: Req BL 20 - //if (preg_match("/[ a-zA-Z_]*[0-9]*/", $xmlData[$i]['title'], $matches)) - //{ - // $xmlData[$i]['req_doc_id'] = $matches[0]; - //} - - // this matches first two words in Title and adds counter started from 1 - // Doc ID is grouped (case insensitive), so different groups have their own counter running - // Example: Req BL Business Logic - // Doc ID: Req BL 1 - // Note: Doc ID doesn't need trim_and_limit since it is parsed from Title - // new dBug($xmlData[$idx]['title']); - - if (preg_match("/[ ]*[a-zA-Z_0-9]*[ ][a-zA-Z_0-9]*/", $xmlData[$idx]['title'], $matches)) - { - $index = strtolower($matches[0]); - if( !isset($counter[$index]) ) - { - $counter[$index] = 0; - } - $counter[$index]++; - $xmlData[$idx]['docid'] = $matches[0] . " " . $counter[$index]; - } - else - { - $xmlData[$idx]['docid'] = uniqid('REQ-'); - } - - $xmlData[$idx]['node_order'] = $idx; - $xmlData[$idx]['expected_coverage'] = 0; - $xmlData[$idx]['type'] = TL_REQ_TYPE_FEATURE; - $xmlData[$idx]['status'] = TL_REQ_STATUS_VALID; - - $idx++; - } - - return $xmlData; -} - - -/* - function: - - args : - - returns: - -*/ -function doReqImport(&$dbHandler,$tprojectID,$userID,$reqSpecID,$fileName,$importType,$emptyScope, - $conflictSolution,$doImport) -{ - $arrImportSource = loadImportedReq($fileName, $importType); - $arrImport = null; - - if (count($arrImportSource)) - { - $map_cur_reqdoc_id = getReqDocIDs($dbHandler,$reqSpecID); - if ($doImport) - { - $arrImport = executeImportedReqs($dbHandler,$arrImportSource, $map_cur_reqdoc_id, - $conflictSolution, $emptyScope, $reqSpecID, $tprojectID, $userID); - } - else - { - $arrImport = compareImportedReqs($dbHandler,$arrImportSource,$tprojectID,$reqSpecID); - } - } - return $arrImport; -} - -/* - function: - - args : - - returns: - -*/ -function exportReqDataToCSV($reqData) -{ - $sKeys = array("req_doc_id","title","scope"); - return exportDataToCSV($reqData,$sKeys,$sKeys,0,','); -} - - -/** - * getReqCoverage - * - */ -function getReqCoverage(&$dbHandler,$reqs,&$execMap) -{ - $tree_mgr = new tree($dbHandler); - - $coverageAlgorithm=config_get('req_cfg')->coverageStatusAlgorithm; - $resultsCfg=config_get('results'); - $status2check=array_keys($resultsCfg['status_label_for_exec_ui']); - - // $coverage['byStatus']=null; - $coverage['withTestCase']=null; - $coverage['withoutTestCase']=null; - $coverage['byStatus']=$resultsCfg['status_label_for_exec_ui']; - $status_counters=array(); - foreach($coverage['byStatus'] as $status_code => $value) - { - $coverage['byStatus'][$status_code]=array(); - $status_counters[$resultsCfg['status_code'][$status_code]]=0; - } - - $reqs_qty=count($reqs); - if($reqs_qty > 0) - { - foreach($reqs as $requirement_id => $req_tcase_set) - { - $first_key=key($req_tcase_set); - $item_qty = count($req_tcase_set); - $req = array("id" => $requirement_id, "title" => $req_tcase_set[$first_key]['req_title'], - "req_doc_id" => $req_tcase_set[$first_key]["req_doc_id"]); - - foreach($status_counters as $key => $value) - { - $status_counters[$key]=0; - } - if( $req_tcase_set[$first_key]['testcase_id'] > 0 ) - { - $coverage['withTestCase'][$requirement_id] = 1; - } - else - { - $coverage['withoutTestCase'][$requirement_id] = $req; - } - - for($idx = 0; $idx < $item_qty; $idx++) - { - $item_info=$req_tcase_set[$idx]; - if( $idx==0 ) // just to avoid useless assignments - { - $req['title']=$item_info['req_title']; - } - - // BUGID 1063 - if( $item_info['testcase_id'] > 0 ) - { - $exec_status = $resultsCfg['status_code']['not_run']; - $tcase_path=''; - if (isset($execMap[$item_info['testcase_id']]) && sizeof($execMap[$item_info['testcase_id']])) - { - $execInfo = end($execMap[$item_info['testcase_id']]); - $tcase_path=$execInfo['tcase_path']; - if( isset($execInfo['status']) && trim($execInfo['status']) !='') - { - $exec_status = $execInfo['status']; - } - } - else - { - $path_info=$tree_mgr->get_full_path_verbose($item_info['testcase_id']); - unset($path_info[$item_info['testcase_id']][0]); // remove test project name - $path_info[$item_info['testcase_id']][]=''; - $tcase_path=implode(' / ',$path_info[$item_info['testcase_id']]); - } - $status_counters[$exec_status]++; - $req['tcList'][] = array("tcID" => $item_info['testcase_id'], - "title" => $item_info['testcase_name'], - "tcaseExternalID" => $item_info['testcase_external_id'], - "version" => $item_info['testcase_version'], - "tcase_path" => $tcase_path, - "status" => $exec_status, - "status_label" => $resultsCfg['status_label'][$resultsCfg['code_status'][$exec_status]]); - } - } // for($idx = 0; $idx < $item_qty; $idx++) - - - // We analyse counters - $go_away=0; - foreach( $coverageAlgorithm['checkOrder'] as $checkKey ) - { - foreach( $coverageAlgorithm['checkType'][$checkKey] as $tcase_status ) - { - if($checkKey == 'atLeastOne') - { - if($status_counters[$resultsCfg['status_code'][$tcase_status]] > 0 ) - { - $coverage['byStatus'][$tcase_status][] = $req; - $go_away=1; - break; - } - } - if($checkKey == 'all') - { - if($status_counters[$resultsCfg['status_code'][$tcase_status]] == $item_qty ) - { - $coverage['byStatus'][$tcase_status][] = $req; - $go_away=1; - break; - } - //-amitkhullar - 20090331 - BUGFIX 2292 - elseif ($status_counters[$resultsCfg['status_code'][$tcase_status]] > 0 ) - { - $coverage['byStatus'][$tcase_status][] = $req; - $go_away=1; - break; - } - elseif ( isset($coverageAlgorithm['checkFail']) && - isset($coverageAlgorithm['checkFail'][$checkKey]) && - isset($req['tcList']) ) - { - - // BUGID 2171 - // ($coverageAlgorithm['checkFail'][$checkKey]==$tcase_status) - // If particular requirement has assigned more than one test cases, and: - // - at least one of assigned test cases was not yet executed - // - the rest of assigned test cases was executed and passed - // then on the "Requirements based report" this particular requirement - // is not shown at all (in any section). - $coverage['byStatus'][$coverageAlgorithm['checkFail'][$checkKey]][] = $req; - $go_away=1; - break; - } - } - } - if($go_away) - { - break; - } - } - } // foreach($reqs as $requirement_id => $req_tcase_set) - } - return $coverage; -} - - -/* - function: - - args : - - returns: - - rev: 20090716 - franciscom - get_last_execution() interface changes -*/ -function getLastExecutions(&$db,$tcaseSet,$tplanId) -{ - $execMap = array(); - if (sizeof($tcaseSet)) - { - $tcase_mgr = new testcase($db); - $items=array_keys($tcaseSet); - $path_info=$tcase_mgr->tree_manager->get_full_path_verbose($items); - $options=array('getNoExecutions' => 1, 'groupByBuild' => 0); - foreach($tcaseSet as $tcaseId => $tcInfo) - { - $execMap[$tcaseId] = $tcase_mgr->get_last_execution($tcaseId,$tcInfo['tcversion_id'], - $tplanId,testcase::ANY_BUILD, - testcase::ANY_PLATFORM,$options); - - unset($path_info[$tcaseId][0]); // remove test project name - $path_info[$tcaseId][]=''; - $execMap[$tcaseId][$tcInfo['tcversion_id']]['tcase_path']=implode(' / ',$path_info[$tcaseId]); - } - - unset($tcase_mgr); - } - return $execMap; -} - - -// 20061014 - franciscom -function check_syntax($fileName,$importType) -{ - switch($importType) - { - case 'csv': - $pfn = "check_syntax_csv"; - break; - - case 'csv_doors': - $pfn = "check_syntax_csv_doors"; - break; - - case 'XML': - $pfn = "check_syntax_xml"; - break; - - // 20081103 - sisajr - case 'DocBook': - $pfn = "check_syntax_xml"; - break; - } - if ($pfn) - { - $data = $pfn($fileName); - return $data; - } - return; -} - -/* - function: - - args: - - returns: - -*/ -function check_syntax_xml($fileName) -{ - $ret=array(); - $ret['status_ok']=1; - $ret['msg']='ok'; - return($ret); -} - - -function check_syntax_csv($fileName) -{ - $ret=array(); - $ret['status_ok']=1; - $ret['msg']='ok'; - return($ret); -} - -// Must be implemented !!! -function check_syntax_csv_doors($fileName) -{ - $ret=array(); - $ret['status_ok']=1; - $ret['msg']='ok'; - - return($ret); -} - -/** - * replace BBCode-link tagged links in req/reqspec scope with actual links - * - * @internal revisions: - * 20110525 - Julian - BUGID 4487 - allow to specify requirement version for internal links - * 20100301 - asimon - added anchor and tproj parameters to tags - * - * @param resource $dbHandler database handle - * @param string $scope text in which to replace tags with links - * @param integer $tprojectID ID of testproject to which req/reqspec belongs - * @return string $scope text with generated links - */ -function req_link_replace($dbHandler, $scope, $tprojectID) -{ - - // Use this to improve performance when is called in loops - static $tree_mgr; - static $tproject_mgr; - static $req_mgr; - static $cfg; - static $l18n; - static $title; - static $tables; - - if(!$tproject_mgr) - { - $tproject_mgr = new testproject($dbHandler); - $tree_mgr = new tree($dbHandler); - $req_mgr = new requirement_mgr($dbHandler); - - $tables = tlObjectWithDB::getDBTables(array('requirements', 'req_specs')); - - $cfg = config_get('internal_links'); - $l18n['version'] = lang_get('tcversion_indicator'); - - $prop2loop = array('req' => array('prop' => 'req_link_title', 'default_lbl' => 'requirement'), - 'req_spec' => array('prop' => 'req_spec_link_title','default_lbl' => 'req_spec_short')); - - - // configure link title (first part of the generated link) - $title = array(); - foreach($prop2loop as $key => $elem) - { - $prop = $elem['prop']; - if ($cfg->$prop->type == 'string' && $cfg->$prop->value != '') - { - $title[$key] = lang_get($cfg->$prop->value); - } - else if ($cfg->$prop->type == 'none') - { - $title[$key] = ''; - } - else - { - $title[$key] = lang_get($elem['default_lbl']) . ": "; - } - } - - } - - $prefix = $tproject_mgr->getTestCasePrefix($tprojectID); - $string2replace = array(); - - // configure target in which link shall open - // use a reasonable default value if nothing is set in config - $cfg->target = isset($cfg->target) ? $cfg->target :'popup'; - - switch($cfg->target) - { - case 'popup': - // use javascript to open popup window - $string2replace['req'] = '%s%s%s'; - $string2replace['req_spec'] = '%s%s'; - break; - - case 'window': - case 'frame':// open in same frame - $target = ($cfg->target == 'window') ? 'target="_blank"' : 'target="_self"'; - $string2replace['req'] = '%s%s%s'; - $string2replace['req_spec'] = '%s%s'; - break; - } - - // now the actual replacing - $patterns2search = array(); - $patterns2search['req'] = "#\[req(.*)\](.*)\[/req\]#iU"; - $patterns2search['req_spec'] = "#\[req_spec(.*)\](.*)\[/req_spec\]#iU"; - $patternPositions = array('complete_string' => 0,'attributes' => 1,'doc_id' => 2); - - $items2search['req'] = array('tproj','anchor','version'); - $items2search['req_spec'] = array('tproj','anchor'); - $itemPositions = array ('item' => 0,'item_value' => 1); - - $sql2exec = array(); - $sql2exec['req'] = " SELECT id, req_doc_id AS doc_id " . - " FROM {$tables['requirements']} WHERE req_doc_id="; - - $sql2exec['req_spec'] = " SELECT id, doc_id FROM {$tables['req_specs']} " . - " WHERE doc_id=" ; - - foreach($patterns2search as $accessKey => $pattern ) - { - - $matches = array(); - preg_match_all($pattern, $scope, $matches); - - // if no req_doc_id is set skip loop - if( count($matches[$patternPositions['doc_id']]) == 0 ) - { - continue; - } - - foreach ($matches[$patternPositions['complete_string']] as $key => $matched_string) - { - - $matched = array (); - $matched['tproj'] = ''; - $matched['anchor'] = ''; - $matched['version'] = ''; - - // only look for attributes if any found - if ($matches[$patternPositions['attributes']][$key] != '') { - foreach ($items2search[$accessKey] as $item) { - $matched_item = array(); - preg_match('/'.$item.'=([\w]+)/',$matched_string,$matched_item); - $matched[$item] = (isset($matched_item[$itemPositions['item_value']])) ? - $matched_item[$itemPositions['item_value']] : ''; - } - } - // set tproj to current project if tproj is not specified in attributes - if (!isset($matched['tproj']) || $matched['tproj'] == '') - { - $matched['tproj'] = $prefix; - } - - // get all reqs / req specs with the specified doc_id - $sql = $sql2exec[$accessKey] . "'{$matches[$patternPositions['doc_id']][$key]}'"; - $rs = $dbHandler->get_recordset($sql); - - if (count($rs) > 0) - { - - foreach($rs as $key => $value) - { - // get root of linked node and check - $real_root = $tree_mgr->getTreeRoot($value['id']); - $matched_root_info = $tproject_mgr->get_by_prefix($matched['tproj']); - - // do only continue if project with the specified project exists and - // if the requirement really belongs to the specified project (requirements - // with the same doc_id may exist within different projects) - if ($real_root == $matched_root_info['id']) - { - if($accessKey == 'req') - { - // add version to link title if set - $version = ''; - $req_version_id = 'null'; - if ($matched['version'] != '') - { - // get requirement version_id of the specified version - $req_version = $req_mgr->get_by_id($value['id'],null,$matched['version']); - - // if version is not set or wrong version was set - // -> show latest version by setting version_id to null - $req_version_id = isset($req_version[0]['version_id']) ? $req_version[0]['version_id'] :'null'; - - // if req_version_id exists set the version to show on hyperlink text - if ($req_version_id != 'null') - { - $version = sprintf($l18n['version'],$matched['version']); - } - } - $urlString = sprintf($string2replace[$accessKey], $value['id'], $req_version_id, - $matched['anchor'], $title[$accessKey], $value['doc_id'], $version); - } - else - { - // build urlString for req specs which do not have a version - $urlString = sprintf($string2replace[$accessKey], $value['id'], - $matched['anchor'], $title[$accessKey], $value['doc_id']); - } - $scope = str_replace($matched_string,$urlString,$scope); - } - } - } - } - } - - return $scope; -} - -?> \ No newline at end of file +{{XMLCODE}}"; + $elemTpl = "\t" . '" . + '<![CDATA[' . "\n||TITLE||\n]]>" . '' . + '" . '' . + '' . "\n"; + $info = array( + "||DOCID||" => "req_doc_id", + "||TITLE||" => "title", + "||DESCRIPTION||" => "scope" + ); + return exportDataToXML($reqData, $rootElem, $elemTpl, $info); +} + +/** + * Process CVS file contents with requirements into TL + * and creates an array with reports + * + * @return array of strings list of particular REQ data with resolution comment + * + * + */ +function executeImportedReqs(&$db, $arrImportSource, $map_cur_reqdoc_id, + $conflictSolution, $emptyScope, $idSRS, $tprojectID, $userID) +{ + define('SKIP_CONTROLS', 1); + + $req_mgr = new requirement_mgr($db); + $import_status = null; + $field_size = config_get('field_size'); + + foreach ($arrImportSource as $data) { + $docID = trimAndLimit($data['docid'], $field_size->req_docid); + $title = trimAndLimit($data['title'], $field_size->req_title); + $scope = $data['description']; + $type = $data['type']; + $status = $data['status']; + $expected_coverage = $data['expected_coverage']; + $node_order = $data['node_order']; + + if (($emptyScope == 'on') && empty($scope)) { + // skip rows with empty scope + $import_status = lang_get('req_import_result_skipped'); + } else { + $crash = $map_cur_reqdoc_id && + array_search($docID, $map_cur_reqdoc_id); + if ($crash) { + // process conflict according to choosen solution + tLog('Conflict found. solution: ' . $conflictSolution); + $import_status['msg'] = 'Error'; + if ($conflictSolution == 'overwrite') { + $item = current($req_mgr->getByDocID($docID, $tprojectID)); + $last_version = $req_mgr->getLastVersionInfo($item['id']); + + // BUGID 0003745: CSV Requirements Import Updates Frozen Requirement + if ($last_version['is_open'] == 1) { + $op = $req_mgr->update($item['id'], $last_version['id'], + $docID, $title, $scope, $userID, $status, $type, + $expected_coverage, $node_order, SKIP_CONTROLS); + if ($op['status_ok']) { + $import_status['msg'] = lang_get( + 'req_import_result_overwritten'); + } + } else { + $import_status['msg'] = lang_get( + 'req_import_result_skipped_is_frozen'); + } + } elseif ($conflictSolution == 'skip') { + // no work + $import_status['msg'] = lang_get( + 'req_import_result_skipped'); + } + } else { + // no conflict - just add requirement + $import_status = $req_mgr->create($idSRS, $docID, $title, $scope, + $userID, $status, $type, $expected_coverage, $node_order); + } + $arrImport[] = array( + 'doc_id' => $docID, + 'title' => $title, + 'import_status' => $import_status['msg'] + ); + } + } + return $arrImport; +} + +/* + * On version 1.9 is NOT USED when importing from XML format + */ +function compareImportedReqs(&$dbHandler, $arrImportSource, $tprojectID, + $reqSpecID) +{ + $reqCfg = config_get('req_cfg'); + $labels = array( + 'type' => $reqCfg->type_labels, + 'status' => $reqCfg->status_labels + ); + $verbose = array( + 'type' => null, + 'status' => null + ); + $cache = array( + 'type' => null, + 'status' => null + ); + $cacheKeys = array_keys($cache); + + $unknown_code = lang_get('unknown_code'); + $reqMgr = new requirement_mgr($dbHandler); + $arrImport = null; + if ($loop2do = count($arrImportSource)) { + $getOptions = array( + 'output' => 'minimun' + ); + $messages = array( + 'ok' => '', + 'import_req_conflicts_other_branch' => '', + 'import_req_exists_here' => '' + ); + foreach ($messages as $key => $dummy) { + $messages[$key] = lang_get($key); + } + + for ($idx = 0; $idx < $loop2do; $idx ++) { + $msgID = 'ok'; + $req = $arrImportSource[$idx]; + + // Check: + // If item with SAME DOCID exists inside container + // If there is a hit + // We will follow user option: update,create new version + // + // If do not exist check must be repeated, but on WHOLE test project + // If there is a hit -> we can not create + // else => create + // + // + // 20100321 - we do not manage yet user option + $check_in_reqspec = $reqMgr->getByDocID($req['docid'], $tprojectID, + $reqSpecID, $getOptions); + if (is_null($check_in_reqspec)) { + $check_in_tproject = $reqMgr->getByDocID($req['docid'], + $tprojectID, null, $getOptions); + if (! is_null($check_in_tproject)) { + $msgID = 'import_req_conflicts_other_branch'; + } + } else { + $msgID = 'import_req_exists_here'; + } + + foreach ($cacheKeys as $attr) { + if (isset($labels[$attr][$req[$attr]])) { + if (! isset($cache[$attr][$req[$attr]])) { + $cache[$attr][$req[$attr]] = lang_get( + $labels[$attr][$req[$attr]]); + } + $verbose[$attr] = $cache[$attr][$req[$attr]]; + } else { + $verbose[$attr] = sprintf($unknown_code, $req[$attr]); + } + } + + $arrImport[] = array( + 'req_doc_id' => $req['docid'], + 'title' => trim($req['title']), + 'scope' => $req['description'], + 'type' => $verbose['type'], + 'status' => $verbose['status'], + 'expected_coverage' => $req['expected_coverage'], + 'node_order' => $req['order'], + 'check_status' => $messages[$msgID] + ); + } + } + return $arrImport; +} + +function getReqDocIDs(&$db, $srs_id) +{ + $req_spec_mgr = new requirement_spec_mgr($db); + $arrCurrentReq = $req_spec_mgr->get_requirements($srs_id); + + $result = null; + if (count($arrCurrentReq)) { + // only if some reqs exist + foreach ($arrCurrentReq as $data) { + $result[$data['id']] = $data['req_doc_id']; + } + } + + return $result; +} + +/** + * load imported data from file and parse it to array + * + * @return array of array each inner array include fields title and scope (and more) + */ +function loadImportedReq($fileName, $importType) +{ + $retVal = null; + switch ($importType) { + case 'csv': + $pfn = "importReqDataFromCSV"; + break; + + case 'csv_doors': + $pfn = "importReqDataFromCSVDoors"; + break; + + case 'DocBook': + $pfn = "importReqDataFromDocBook"; + break; + } + + if ($pfn) { + $retVal = $pfn($fileName); + if ($importType == 'DocBook') { + // this structure if useful when importing from CSV + // $retVal = array('userFeedback' => arra(),'info' => null); + // + // But we need to return same data structure ALWAYS + // for DocBook we do not use 'parsedCounter' and 'syntaxError' + // + $dummy = array( + 'userFeedback' => null, + 'info' => $retVal + ); + $retVal = $dummy; + } + } + + return $retVal; +} + +/** + * importReqDataFromCSV + */ +function importReqDataFromCSV($fileName) +{ + // CSV line format + $fieldMappings = array( + "docid", + "title", + "description", + "type", + "status", + "expected_coverage", + "node_order" + ); + + $options = array( + 'delimiter' => ',', + 'fieldQty' => count($fieldMappings) + ); + $impData = importCSVData($fileName, $fieldMappings, $options); + + $reqData = &$impData['info']; + if ($reqData) { + // lenght will be adjusted to these values + $field_size = config_get('field_size'); + $fieldLength = array( + "docid" => $field_size->req_docid, + "title" => $field_size->req_title + ); + + $reqCfg = config_get('req_cfg'); + $fieldDefault = array( + "type" => array( + 'check' => 'type_labels', + 'value' => TL_REQ_TYPE_FEATURE + ), + "status" => array( + 'check' => 'status_labels', + 'value' => TL_REQ_STATUS_VALID + ) + ); + + $loop2do = count($reqData); + for ($ddx = 0; $ddx < $loop2do; $ddx ++) { + foreach ($reqData[$ddx] as $fieldKey => &$fieldValue) { + // Adjust Lenght + if (isset($fieldLength[$fieldKey])) { + $fieldValue = trimAndLimit($fieldValue, + $fieldLength[$fieldKey]); + } elseif (isset($fieldDefault[$fieldKey])) { + // Assign default value + $checkKey = $fieldDefault[$fieldKey]['check']; + $checkObj = &$reqCfg->$checkKey; + if (! isset($checkObj[$fieldValue])) { + $fieldValue = $fieldDefault[$fieldKey]['value']; + } + } + } + } + } + return $impData; +} + +/** + * importReqDataFromCSVDoors + * + * @internal revision + * + */ +function importReqDataFromCSVDoors($fileName) +{ + // Some keys are strings, other numeric + $fieldMappings = array( + "Object Identifier" => "title", + "Object Text" => "description", + "Created By", + "Created On", + "Last Modified By", + "Last Modified On" + ); + + $options = array( + 'delimiter' => ',', + 'fieldQty' => count($fieldMappings), + 'processHeader' => true + ); + return importCSVData($fileName, $fieldMappings, $options); +} + +/** + * Parses one 'informaltable' XML entry and produces HTML table as string. + * + * XML relationship: + * informaltable -> tgroup -> thead -> row -> entry + * -> tbody -> row -> entry + * + * 20081103 - sisajr + */ +function getDocBookTableAsHtmlString($docTable, $parseCfg) +{ + $resultTable = ""; + foreach ($docTable->children() as $tgroup) { + if ($tgroup->getName() != $parseCfg->table_group) { + continue; + } + + $table = ""; + foreach ($tgroup->children() as $tbody) { + // get table head + $tbodyName = $tbody->getName(); + $doIt = false; + if ($tbodyName == $parseCfg->table_head) { + $cellTag = array( + 'open' => '', + 'close' => '' + ); + $doIt = true; + } elseif ($tbodyName == $parseCfg->table_body) { + $cellTag = array( + 'open' => '', + 'close' => '' + ); + $doIt = true; + } + + if ($doIt) { + foreach ($tbody->children() as $row) { + if ($row->getName() == $parseCfg->table_row) { + $table_row = ""; + foreach ($row->children() as $entry) { + if (($entry->getName()) == $parseCfg->table_entry) { + if ($entry->count() == 0) { + $table_row .= $cellTag['open'] . + (string) $entry . $cellTag['close']; + } else { + $table_row .= $cellTag['open']; + foreach ($parseCfg->table_entry_children as $ck) { + if (property_exists($entry, $ck)) { + $table_row .= (string) $entry->$ck; + } + } + $table_row .= $cellTag['close']; + } + } + } + + $table_row .= ""; + $table .= $table_row; + } + } + } + } + + $resultTable .= "" . $table . "
    "; + } + + return $resultTable; +} + +/** + * Imports data from DocBook XML + * + * @return array of map + * + */ +function importReqDataFromDocBook($fileName) +{ + $req_cfg = config_get('req_cfg'); + $docbookCfg = $req_cfg->importDocBook; + $xmlData = null; + $field_size = config_get('field_size'); + + $simpleXMLObj = simplexml_load_file($fileName); + + $idx = 0; + foreach ($simpleXMLObj->{$docbookCfg->requirement} as $xmlReq) { + // get all child elements of this requirement + $title = ""; + $description = ""; + $children = $xmlReq->children(); + foreach ($children as $child) { + $nodeName = $child->getName(); + if ($nodeName == $docbookCfg->title) { + $title = (string) $child; + } elseif ($nodeName == $docbookCfg->ordered_list) { + $list = ""; + foreach ($child->children() as $item) { + if ($item->getName() == $docbookCfg->list_item) { + if ($item->count() == 0) { + $list .= "
  • " . (string) $item . "
  • "; + } else { + foreach ($docbookCfg->list_item_children as $ck) { + if (property_exists($item, $ck)) { + $list .= "
  • " . (string) $item->$ck . + "
  • "; + } + } + } + } + } + $description .= "
      " . $list . "
    "; + } elseif ($nodeName == $docbookCfg->table) { + $description .= getDocBookTableAsHtmlString($child, $docbookCfg); + } elseif ($nodeName == $docbookCfg->paragraph) { + $description .= "

    " . (string) $child . "

    "; + } else { + $description .= "

    " . (string) $child . "

    "; + } + } + $xmlData[$idx]['description'] = $description; + $xmlData[$idx]['title'] = trimAndLimit($title, $field_size->req_title); + + // parse Doc ID from requirement title + // first remove any weird characters before the title. This could be probably omitted + $xmlData[$idx]['title'] = preg_replace("/^[^a-zA-Z_0-9]*/", "", + $xmlData[$idx]['title']); + + // get Doc ID + // + // this will create Doc ID as words ended with number + // Example: Req BL 20 Business Logic + // Doc ID: Req BL 20 + // if (preg_match("/[ a-zA-Z_]*[0-9]*/", $xmlData[$i]['title'], $matches)) + // { + // $xmlData[$i]['req_doc_id'] = $matches[0]; + // } + + // this matches first two words in Title and adds counter started from 1 + // Doc ID is grouped (case insensitive), so different groups have their own counter running + // Example: Req BL Business Logic + // Doc ID: Req BL 1 + // Note: Doc ID doesn't need trim_and_limit since it is parsed from Title + // new dBug($xmlData[$idx]['title']); + + if (preg_match("/[ ]*[a-zA-Z_0-9]*[ ][a-zA-Z_0-9]*/", + $xmlData[$idx]['title'], $matches)) { + $index = strtolower($matches[0]); + if (! isset($counter[$index])) { + $counter[$index] = 0; + } + $counter[$index] ++; + $xmlData[$idx]['docid'] = $matches[0] . " " . $counter[$index]; + } else { + $xmlData[$idx]['docid'] = uniqid('REQ-'); + } + + $xmlData[$idx]['node_order'] = $idx; + $xmlData[$idx]['expected_coverage'] = 0; + $xmlData[$idx]['type'] = TL_REQ_TYPE_FEATURE; + $xmlData[$idx]['status'] = TL_REQ_STATUS_VALID; + + $idx ++; + } + + return $xmlData; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function doReqImport(&$dbHandler, $tprojectID, $userID, $reqSpecID, $fileName, + $importType, $emptyScope, $conflictSolution, $doImport) +{ + $arrImportSource = loadImportedReq($fileName, $importType); + $arrImport = null; + + if (count($arrImportSource)) { + $map_cur_reqdoc_id = getReqDocIDs($dbHandler, $reqSpecID); + if ($doImport) { + $arrImport = executeImportedReqs($dbHandler, $arrImportSource, + $map_cur_reqdoc_id, $conflictSolution, $emptyScope, $reqSpecID, + $tprojectID, $userID); + } else { + $arrImport = compareImportedReqs($dbHandler, $arrImportSource, + $tprojectID, $reqSpecID); + } + } + return $arrImport; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function exportReqDataToCSV($reqData) +{ + $sKeys = array( + "req_doc_id", + "title", + "scope" + ); + return exportDataToCSV($reqData, $sKeys, $sKeys, 0, ','); +} + +/** + * getReqCoverage + */ +function getReqCoverage(&$dbHandler, $reqs, &$execMap) +{ + $tree_mgr = new tree($dbHandler); + + $coverageAlgorithm = config_get('req_cfg')->coverageStatusAlgorithm; + $resultsCfg = config_get('results'); + + $coverage['withTestCase'] = null; + $coverage['withoutTestCase'] = null; + $coverage['byStatus'] = $resultsCfg['status_label_for_exec_ui']; + $status_counters = array(); + foreach ($coverage['byStatus'] as $status_code => $value) { + $coverage['byStatus'][$status_code] = array(); + $status_counters[$resultsCfg['status_code'][$status_code]] = 0; + } + + $reqs_qty = count($reqs); + if ($reqs_qty > 0) { + foreach ($reqs as $requirement_id => $req_tcase_set) { + $first_key = key($req_tcase_set); + $item_qty = count($req_tcase_set); + $req = array( + "id" => $requirement_id, + "title" => $req_tcase_set[$first_key]['req_title'], + "req_doc_id" => $req_tcase_set[$first_key]["req_doc_id"] + ); + + foreach ($status_counters as $key => $value) { + $status_counters[$key] = 0; + } + if ($req_tcase_set[$first_key]['testcase_id'] > 0) { + $coverage['withTestCase'][$requirement_id] = 1; + } else { + $coverage['withoutTestCase'][$requirement_id] = $req; + } + + for ($idx = 0; $idx < $item_qty; $idx ++) { + $item_info = $req_tcase_set[$idx]; + if ($idx == 0) // just to avoid useless assignments + { + $req['title'] = $item_info['req_title']; + } + + // BUGID 1063 + if ($item_info['testcase_id'] > 0) { + $exec_status = $resultsCfg['status_code']['not_run']; + $tcase_path = ''; + if (isset($execMap[$item_info['testcase_id']]) && + count($execMap[$item_info['testcase_id']])) { + $execInfo = end($execMap[$item_info['testcase_id']]); + $tcase_path = $execInfo['tcase_path']; + if (isset($execInfo['status']) && + trim($execInfo['status']) != '') { + $exec_status = $execInfo['status']; + } + } else { + $path_info = $tree_mgr->get_full_path_verbose( + $item_info['testcase_id']); + unset($path_info[$item_info['testcase_id']][0]); // remove test project name + $path_info[$item_info['testcase_id']][] = ''; + $tcase_path = implode(' / ', + $path_info[$item_info['testcase_id']]); + } + $status_counters[$exec_status] ++; + $req['tcList'][] = array( + "tcID" => $item_info['testcase_id'], + "title" => $item_info['testcase_name'], + "tcaseExternalID" => $item_info['testcase_external_id'], + "version" => $item_info['testcase_version'], + "tcase_path" => $tcase_path, + "status" => $exec_status, + "status_label" => $resultsCfg['status_label'][$resultsCfg['code_status'][$exec_status]] + ); + } + } // for($idx = 0; $idx < $item_qty; $idx++) + + // We analyse counters + $go_away = 0; + foreach ($coverageAlgorithm['checkOrder'] as $checkKey) { + foreach ($coverageAlgorithm['checkType'][$checkKey] as $tcase_status) { + if ($checkKey == 'atLeastOne' && + $status_counters[$resultsCfg['status_code'][$tcase_status]] > + 0) { + $coverage['byStatus'][$tcase_status][] = $req; + $go_away = 1; + break; + } + if ($checkKey == 'all') { + if ($status_counters[$resultsCfg['status_code'][$tcase_status]] == + $item_qty) { + $coverage['byStatus'][$tcase_status][] = $req; + $go_away = 1; + break; + } // -amitkhullar - 20090331 - BUGFIX 2292 + elseif ($status_counters[$resultsCfg['status_code'][$tcase_status]] > + 0) { + $coverage['byStatus'][$tcase_status][] = $req; + $go_away = 1; + break; + } elseif (isset($coverageAlgorithm['checkFail']) && + isset($coverageAlgorithm['checkFail'][$checkKey]) && + isset($req['tcList'])) { + + // BUGID 2171 + // ($coverageAlgorithm['checkFail'][$checkKey]==$tcase_status) + // If particular requirement has assigned more than one test cases, and: + // - at least one of assigned test cases was not yet executed + // - the rest of assigned test cases was executed and passed + // then on the "Requirements based report" this particular requirement + // is not shown at all (in any section). + $coverage['byStatus'][$coverageAlgorithm['checkFail'][$checkKey]][] = $req; + $go_away = 1; + break; + } + } + } + if ($go_away) { + break; + } + } + } // foreach($reqs as $requirement_id => $req_tcase_set) + } + return $coverage; +} + +/* + * function: + * + * args : + * + * returns: + * + * rev: 20090716 - franciscom - getLastExecution() interface changes + */ +function getLastExecutions(&$db, $tcaseSet, $tplanId) +{ + $execMap = array(); + if (count($tcaseSet)) { + $tcaseMgr = new testcase($db); + $items = array_keys($tcaseSet); + $path_info = $tcaseMgr->tree_manager->get_full_path_verbose($items); + $options = array( + 'getNoExecutions' => 1, + 'groupByBuild' => 0 + ); + foreach ($tcaseSet as $tcaseId => $tcInfo) { + $execMap[$tcaseId] = $tcaseMgr->getLastExecution($tcaseId, + $tcInfo['tcversion_id'], $tplanId, testcase::ANY_BUILD, + testcase::ANY_PLATFORM, $options); + + unset($path_info[$tcaseId][0]); // remove test project name + $path_info[$tcaseId][] = ''; + $execMap[$tcaseId][$tcInfo['tcversion_id']]['tcase_path'] = implode( + ' / ', $path_info[$tcaseId]); + } + + unset($tcaseMgr); + } + return $execMap; +} + +// 20061014 - franciscom +function check_syntax($fileName, $importType) +{ + switch ($importType) { + case 'csv': + $pfn = "check_syntax_csv"; + break; + + case 'csv_doors': + $pfn = "check_syntax_csv_doors"; + break; + + case 'XML': + $pfn = "check_syntax_xml"; + break; + + case 'DocBook': + $pfn = "check_syntax_xml"; + break; + } + if ($pfn) { + return $pfn($fileName); + } +} + +/* + * function: + * + * args: + * + * returns: + * + */ +function check_syntax_xml($fileName) +{ + $ret = array(); + $ret['status_ok'] = 1; + $ret['msg'] = 'ok'; + return $ret; +} + +function check_syntax_csv($fileName) +{ + $ret = array(); + $ret['status_ok'] = 1; + $ret['msg'] = 'ok'; + return $ret; +} + +// Must be implemented !!! +function check_syntax_csv_doors($fileName) +{ + $ret = array(); + $ret['status_ok'] = 1; + $ret['msg'] = 'ok'; + + return $ret; +} + +/** + * replace BBCode-link tagged links in req/reqspec scope with actual links + * + * @internal revisions: + * 20110525 - Julian - BUGID 4487 - allow to specify requirement version for internal links + * 20100301 - asimon - added anchor and tproj parameters to tags + * + * @param resource $dbHandler + * database handle + * @param string $scope + * text in which to replace tags with links + * @param integer $tprojectID + * ID of testproject to which req/reqspec belongs + * @return string $scope text with generated links + */ +function req_link_replace($dbHandler, $scope, $tprojectID) +{ + + // Use this to improve performance when is called in loops + static $tree_mgr; + static $tproject_mgr; + static $req_mgr; + static $cfg; + static $l18n; + static $title; + static $tables; + + if (! $tproject_mgr) { + $tproject_mgr = new testproject($dbHandler); + $tree_mgr = new tree($dbHandler); + $req_mgr = new requirement_mgr($dbHandler); + + $tables = tlObjectWithDB::getDBTables( + array( + 'requirements', + 'req_specs' + )); + + $cfg = config_get('internal_links'); + $l18n['version'] = lang_get('tcversion_indicator'); + + $prop2loop = array( + 'req' => array( + 'prop' => 'req_link_title', + 'default_lbl' => 'requirement' + ), + 'req_spec' => array( + 'prop' => 'req_spec_link_title', + 'default_lbl' => 'req_spec_short' + ) + ); + + // configure link title (first part of the generated link) + $title = array(); + foreach ($prop2loop as $key => $elem) { + $prop = $elem['prop']; + if ($cfg->$prop->type == 'string' && $cfg->$prop->value != '') { + $title[$key] = lang_get($cfg->$prop->value); + } elseif ($cfg->$prop->type == 'none') { + $title[$key] = ''; + } else { + $title[$key] = lang_get($elem['default_lbl']) . ": "; + } + } + } + + $prefix = $tproject_mgr->getTestCasePrefix($tprojectID); + $string2replace = array(); + + // configure target in which link shall open + // use a reasonable default value if nothing is set in config + $cfg->target = isset($cfg->target) ? $cfg->target : 'popup'; + + switch ($cfg->target) { + case 'popup': + // use javascript to open popup window + $string2replace['req'] = '%s%s%s'; + $string2replace['req_spec'] = '%s%s'; + break; + + case 'window': + case 'frame': // open in same frame + $target = ($cfg->target == 'window') ? 'target="_blank"' : 'target="_self"'; + $string2replace['req'] = '%s%s%s'; + $string2replace['req_spec'] = '%s%s'; + break; + } + + // now the actual replacing + $patterns2search = array(); + $patterns2search['req'] = "#\[req(.*)\](.*)\[/req\]#iU"; + $patterns2search['req_spec'] = "#\[req_spec(.*)\](.*)\[/req_spec\]#iU"; + $patternPositions = array( + 'complete_string' => 0, + 'attributes' => 1, + 'doc_id' => 2 + ); + + $items2search['req'] = array( + 'tproj', + 'anchor', + 'version' + ); + $items2search['req_spec'] = array( + 'tproj', + 'anchor' + ); + $itemPositions = array( + 'item' => 0, + 'item_value' => 1 + ); + + $sql2exec = array(); + $sql2exec['req'] = " SELECT id, req_doc_id AS doc_id " . + " FROM {$tables['requirements']} WHERE req_doc_id="; + + $sql2exec['req_spec'] = " SELECT id, doc_id FROM {$tables['req_specs']} " . + " WHERE doc_id="; + + foreach ($patterns2search as $accessKey => $pattern) { + + $matches = array(); + preg_match_all($pattern, $scope, $matches); + + // if no req_doc_id is set skip loop + if (count($matches[$patternPositions['doc_id']]) == 0) { + continue; + } + + foreach ($matches[$patternPositions['complete_string']] as $key => $matched_string) { + + $matched = array(); + $matched['tproj'] = ''; + $matched['anchor'] = ''; + $matched['version'] = ''; + + // only look for attributes if any found + if ($matches[$patternPositions['attributes']][$key] != '') { + foreach ($items2search[$accessKey] as $item) { + $matched_item = array(); + preg_match('/' . $item . '=([\w]+)/', $matched_string, + $matched_item); + $matched[$item] = (isset( + $matched_item[$itemPositions['item_value']])) ? $matched_item[$itemPositions['item_value']] : ''; + } + } + // set tproj to current project if tproj is not specified in attributes + if (! isset($matched['tproj']) || $matched['tproj'] == '') { + $matched['tproj'] = $prefix; + } + + // get all reqs / req specs with the specified doc_id + $sql = $sql2exec[$accessKey] . + "'{$matches[$patternPositions['doc_id']][$key]}'"; + $rs = $dbHandler->get_recordset($sql); + + if (count($rs) > 0) { + + foreach ($rs as $value) { + // get root of linked node and check + $real_root = $tree_mgr->getTreeRoot($value['id']); + $matched_root_info = $tproject_mgr->get_by_prefix( + $matched['tproj']); + + // do only continue if project with the specified project exists and + // if the requirement really belongs to the specified project (requirements + // with the same doc_id may exist within different projects) + if ($real_root == $matched_root_info['id']) { + if ($accessKey == 'req') { + // add version to link title if set + $version = ''; + $req_version_id = 'null'; + if ($matched['version'] != '') { + // get requirement version_id of the specified version + $req_version = $req_mgr->get_by_id($value['id'], + null, $matched['version']); + + // if version is not set or wrong version was set + // -> show latest version by setting version_id to null + $req_version_id = isset( + $req_version[0]['version_id']) ? $req_version[0]['version_id'] : 'null'; + + // if req_version_id exists set the version to show on hyperlink text + if ($req_version_id != 'null') { + $version = sprintf($l18n['version'], + $matched['version']); + } + } + $urlString = sprintf($string2replace[$accessKey], + $value['id'], $req_version_id, + $matched['anchor'], $title[$accessKey], + $value['doc_id'], $version); + } else { + // build urlString for req specs which do not have a version + $urlString = sprintf($string2replace[$accessKey], + $value['id'], $matched['anchor'], + $title[$accessKey], $value['doc_id']); + } + $scope = str_replace($matched_string, $urlString, $scope); + } + } + } + } + } + + return $scope; +} + +?> diff --git a/lib/functions/roles.inc.php b/lib/functions/roles.inc.php index 005df81625..dac37649bf 100644 --- a/lib/functions/roles.inc.php +++ b/lib/functions/roles.inc.php @@ -1,468 +1,491 @@ - HAS EFFECTS ONLY ON LAYOUT - global $g_rights_tp; - global $g_rights_mgttc; - global $g_rights_kw; - global $g_rights_req; - global $g_rights_product; - global $g_rights_cf; - global $g_rights_users_global; - global $g_rights_users; - global $g_rights_system; - global $g_rights_platforms; - global $g_rights_issuetrackers; - global $g_rights_codetrackers; - global $g_rights_executions; - - // global $g_rights_reqmgrsystems; - - global $g_propRights_global; - global $g_propRights_product; - - - // @since 1.9.7 - $l18nCfg = - array('desc_testplan_execute' => null,'desc_testplan_create_build' => null, - 'desc_testplan_metrics' => null,'desc_testplan_planning' => null, - 'desc_user_role_assignment' => null,'desc_mgt_view_tc' => null, - 'desc_mgt_modify_tc' => null,'mgt_testplan_create' => null, - 'desc_mgt_view_key' => null,'desc_mgt_modify_key' => null, - 'desc_keyword_assignment' => null,'desc_mgt_view_req' => null, - 'desc_monitor_requirement' => null,'desc_mgt_modify_req' => null, - 'desc_req_tcase_link_management' => null, - 'desc_mgt_modify_product' => null, - 'desc_project_inventory_management' => null, - 'desc_project_inventory_view' => null, - 'desc_cfield_view' => null,'desc_cfield_management' => null, - 'desc_cfield_assignment' => null, - 'desc_exec_assign_testcases' => null, - 'desc_platforms_view' => null,'desc_platforms_management' => null, - 'desc_issuetrackers_view' => null, - 'desc_issuetrackers_management' => null, - 'desc_codetrackers_view' => null, - 'desc_codetrackers_management' => null, - 'desc_mgt_modify_users' => null,'desc_role_management' => null, - 'desc_user_role_assignment' => null, - 'desc_mgt_view_events' => null, 'desc_events_mgt' => null, - 'desc_mgt_unfreeze_req' => null,'desc_mgt_plugins' => null, - 'right_exec_edit_notes' => null, 'right_exec_delete' => null, - 'right_testplan_unlink_executed_testcases' => null, - 'right_testproject_delete_executed_testcases' => null, - 'right_testproject_edit_executed_testcases' => null, - 'right_testplan_milestone_overview' => null, - 'right_exec_testcases_assigned_to_me' => null, - 'right_testproject_metrics_dashboard' => null, - 'right_testplan_add_remove_platforms' => null, - 'right_testplan_update_linked_testcase_versions' => null, - 'right_testplan_set_urgent_testcases' => null, - 'right_testplan_show_testcases_newest_versions' => null, - 'right_testcase_freeze' => null, - 'right_exec_ro_access' => null, - 'right_testproject_add_remove_keywords_executed_tcversions' => null, - 'right_delete_frozen_tcversion' => null); - - - - $l18n = init_labels($l18nCfg); - - $g_rights_executions = - array('exec_edit_notes' => $l18n['right_exec_edit_notes'], - 'exec_delete' => $l18n['right_exec_delete'], - 'exec_ro_access' => $l18n['right_exec_ro_access']); - - // order is important ? - $g_rights_tp = - array("mgt_testplan_create" => $l18n['mgt_testplan_create'], - "testplan_create_build" => $l18n['desc_testplan_create_build'], - "testplan_planning" => $l18n['desc_testplan_planning'], - "testplan_execute" => $l18n['desc_testplan_execute'], - "testplan_metrics" => $l18n['desc_testplan_metrics'], - "testplan_user_role_assignment" => $l18n['desc_user_role_assignment'], - "exec_assign_testcases" => $l18n['desc_exec_assign_testcases'], - "testplan_unlink_executed_testcases" => $l18n['right_testplan_unlink_executed_testcases'], - "testplan_milestone_overview" => $l18n['right_testplan_milestone_overview'], - "exec_testcases_assigned_to_me" => $l18n['right_exec_testcases_assigned_to_me'], - 'testplan_add_remove_platforms' => $l18n['right_testplan_add_remove_platforms'], - 'testplan_update_linked_testcase_versions' => $l18n['right_testplan_update_linked_testcase_versions'], - 'testplan_set_urgent_testcases' => $l18n['right_testplan_set_urgent_testcases'], - 'testplan_show_testcases_newest_versions' => $l18n['right_testplan_show_testcases_newest_versions']); - - - $g_rights_mgttc = - array("mgt_view_tc" => $l18n['desc_mgt_view_tc'], - "mgt_modify_tc" => $l18n['desc_mgt_modify_tc'], - "testproject_delete_executed_testcases" => null, - "testproject_edit_executed_testcases" => null , - "testproject_add_remove_keywords_executed_tcversions" => null, - "testcase_freeze" => null, - "delete_frozen_tcversion" => null); - - foreach( $g_rights_mgttc as $tr => $lbl ) { - if( null == $lbl ) { - $g_rights_mgttc[$tr] = $l18n['right_' . $tr]; - } - } - - - $g_rights_kw = array("mgt_view_key" => $l18n['desc_mgt_view_key'], - "keyword_assignment" => $l18n['desc_keyword_assignment'], - "mgt_modify_key" => $l18n['desc_mgt_modify_key']); - - $g_rights_req = array("mgt_view_req" => $l18n['desc_mgt_view_req'], - "monitor_requirement" => $l18n['desc_monitor_requirement'], - "mgt_modify_req" => $l18n['desc_mgt_modify_req'], - "mgt_unfreeze_req" => $l18n['desc_mgt_unfreeze_req'], - "req_tcase_link_management" => $l18n['desc_req_tcase_link_management']); - - $g_rights_product = - array("mgt_modify_product" => $l18n['desc_mgt_modify_product'], - "cfield_assignment" => $l18n['desc_cfield_assignment'], - "project_inventory_management" => $l18n['desc_project_inventory_management'], - "project_inventory_view" => $l18n['desc_project_inventory_view'] ); - - $g_rights_cf = array("cfield_view" => $l18n['desc_cfield_view'], - "cfield_management" => $l18n['desc_cfield_management']); - - - $g_rights_platforms = array("platform_view" => $l18n['desc_platforms_view'], - "platform_management" => $l18n['desc_platforms_management']); - - $g_rights_issuetrackers = array("issuetracker_view" => $l18n['desc_issuetrackers_view'], - "issuetracker_management" => $l18n['desc_issuetrackers_management']); - - $g_rights_codetrackers = array("codetracker_view" => $l18n['desc_codetrackers_view'], - "codetracker_management" => $l18n['desc_codetrackers_management']); - - - // $g_rights_reqmgrsystems = array("reqmgrsystem_view" => $l18n['desc_reqmgrsystems_view'], - // "reqmgrsystem_management" => $l18n['desc_reqmgrsystems_management']); - - - // Global means test project independent. - // - // $g_rights_users_global = array("mgt_users" => $l18n['desc_mgt_modify_users'], - // "role_management" => $l18n['desc_role_management'], - // "user_role_assignment" => $l18n['desc_user_role_assignment']); - - $g_rights_users_global = array("mgt_users" => $l18n['desc_mgt_modify_users'], - "role_management" => $l18n['desc_role_management']); - - $g_rights_users = $g_rights_users_global; - - $g_rights_system = array ("mgt_view_events" => $l18n['desc_mgt_view_events'], - "events_mgt" => $l18n['desc_events_mgt'], - "mgt_plugins" => $l18n['desc_mgt_plugins']); - - - - $g_propRights_global = array_merge($g_rights_users_global,$g_rights_system,$g_rights_product); - unset($g_propRights_global["testproject_user_role_assignment"]); - - $g_propRights_product = array_merge($g_propRights_global,$g_rights_mgttc,$g_rights_kw,$g_rights_req); -} - - -/** - * function takes a roleQuestion from a specified link and returns whether - * the user has rights to view it - * - * @param resource &$db reference to database handler - * @param string $roleQuestion a right identifier - * @param integer $tprojectID (optional) - * @param integer $tplanID (optional) - * - * @see tlUser - */ -function has_rights(&$db,$roleQuestion,$tprojectID = null,$tplanID = null) { - return $_SESSION['currentUser']->hasRight($db,$roleQuestion,$tprojectID,$tplanID); -} - - -/** - * - */ -function propagateRights($fromRights,$propRights,&$toRights) { - // the mgt_users right isn't test project related so this right is inherited from - // the global role (if set) - foreach($propRights as $right => $desc) { - if (in_array($right,$fromRights) && !in_array($right,$toRights)) { - $toRights[] = $right; - } - } -} - - -/** - * TBD - * - * @param string $rights - * @param mixed $roleQuestion - * @param boolean $bAND [default = 1] - * @return mixed 'yes' or null - * - * @author Andreas Morsing - * @since 20.02.2006, 20:30:07 - * - **/ -function checkForRights($rights,$roleQuestion,$bAND = 1) { - $ret = null; - //check to see if the $roleQuestion variable appears in the $roles variable - if (is_array($roleQuestion)) { - $r = array_intersect($roleQuestion,$rights); - if ($bAND) { - //for AND all rights must be present - if (sizeof($r) == sizeof($roleQuestion)) { - $ret = 'yes'; - } - } else { - //for OR one of all must be present - if (sizeof($r)) { - $ret = 'yes'; - } - } - } else { - $ret = (in_array($roleQuestion,$rights) ? 'yes' : null); - } - return $ret; -} - -/** - * Get info about user(s) role at test project level, - * with indication about the nature of role: inherited or assigned. - * - * To get a user role we consider a 3 layer model: - * layer 1 - user <--- uplayer - * layer 2 - test project <--- in this fuction we are interested in this level. - * layer 3 - test plan - * - * args : $tproject_id - * [$user_id] - * - * @return array map with effetive_role in context ($tproject_id) - * key: user_id - * value: map with keys: - * login (from users table - useful for debug) - * user_role_id (from users table - useful for debug) - * uplayer_role_id (always = user_role_id) - * uplayer_is_inherited - * effective_role_id user role for test project - * is_inherited - */ -function get_tproject_effective_role(&$db,$tproject,$user_id = null,$users = null) { - $effective_role = array(); - $tproject_id = $tproject['id']; - if (!is_null($user_id)) - { - $users = tlUser::getByIDs($db,(array)$user_id); - } - else if (is_null($users)) - { - $users = tlUser::getAll($db); - } - - if ($users) - { - foreach($users as $id => $user) - { - // manage admin exception - $isInherited = 1; - $effectiveRoleID = $user->globalRoleID; - $effectiveRole = $user->globalRole; - if( ($user->globalRoleID != TL_ROLES_ADMIN) && !$tproject['is_public']) - { - $isInherited = $tproject['is_public']; - $effectiveRoleID = TL_ROLES_NO_RIGHTS; - $effectiveRole = ''; - } - - if(isset($user->tprojectRoles[$tproject_id])) - { - $isInherited = 0; - $effectiveRoleID = $user->tprojectRoles[$tproject_id]->dbID; - $effectiveRole = $user->tprojectRoles[$tproject_id]; - } - - $effective_role[$id] = array('login' => $user->login, - 'user' => $user, - 'user_role_id' => $user->globalRoleID, - 'uplayer_role_id' => $user->globalRoleID, - 'uplayer_is_inherited' => 0, - 'effective_role_id' => $effectiveRoleID, - 'effective_role' => $effectiveRole, - 'is_inherited' => $isInherited); - } - } - return $effective_role; -} - - -/** - * Get info about user(s) role at test plan level, - * with indication about the nature of role: inherited or assigned. - * - * To get a user role we consider a 3 layer model: - * layer 1 - user <--- uplayer - * layer 2 - test project <--- in this fuction we are interested in this level. - * layer 3 - test plan - - args : $tplan_id - $tproject_id - [$user_id] - - * @return array map with effetive_role in context ($tplan_id) - key: user_id - value: map with keys: - login (from users table - useful for debug) - user_role_id (from users table - useful for debug) - uplayer_role_id user role for test project - uplayer_is_inherited 1 -> uplayer role is inherited - 0 -> uplayer role is written in table - effective_role_id user role for test plan - is_inherited - - @internal revisions - 20101111 - franciscom - BUGID 4006: test plan is_public - */ -function get_tplan_effective_role(&$db,$tplan_id,$tproject,$user_id = null,$users = null,$inheritanceMode = null) -{ - $tplan_mgr = new testplan($db); - $tplan = $tplan_mgr->get_by_id($tplan_id); - unset($tplan_mgr); - - $roleInhMode = !is_null($inheritanceMode) ? $inheritanceMode : - config_get('testplan_role_inheritance_mode'); - - /** - * key: user_id - * value: map with keys: - * login (from users table - useful for debug) - * user_role_id (from users table - useful for debug) - * uplayer_role_id (always = user_role_id) - * uplayer_is_inherited - * effective_role_id user role for test project - * is_inherited - */ - $effective_role = get_tproject_effective_role($db,$tproject,$user_id,$users); - - foreach($effective_role as $user_id => $row) { - - $doNextStep = true; - - // Step 1 - If I've role specified for Test Plan, get and skip - if( isset($row['user']->tplanRoles[$tplan_id]) ) { - $isInherited = 0; - $doNextStep = false; - - $effective_role[$user_id]['effective_role_id'] = $row['user']->tplanRoles[$tplan_id]->dbID; - $effective_role[$user_id]['effective_role'] = $row['user']->tplanRoles[$tplan_id]; - } - - // For Private Test Plans specific role is NEEDED for users with - // global role !? ADMIN - if( $doNextStep && - ($row['user']->globalRoleID != TL_ROLES_ADMIN) && !$tplan['is_public']) { - $isInherited = 0; - $doNextStep = false; - - $effective_role[$user_id]['effective_role_id'] = TL_ROLES_NO_RIGHTS; - $effective_role[$user_id]['effective_role'] = ''; - } - - if( $doNextStep ) { - $isInherited = 1; - - switch($roleInhMode) { - case 'testproject': - $effective_role[$user_id]['uplayer_role_id'] = $effective_role[$user_id]['effective_role_id']; - $effective_role[$user_id]['uplayer_is_inherited'] = $effective_role[$user_id]['is_inherited']; - break; - - case 'global': - $effective_role[$user_id]['effective_role_id'] = $row['user']->globalRoleID; - $effective_role[$user_id]['effective_role'] = $row['user']->globalRole; - break; - } - } - - $effective_role[$user_id]['is_inherited'] = $isInherited; - } - return $effective_role; -} - - -function getRoleErrorMessage($code) -{ - $msg = 'ok'; - switch($code) - { - case tlRole::E_NAMEALREADYEXISTS: - $msg = lang_get('error_duplicate_rolename'); - break; - - case tlRole::E_NAMELENGTH: - $msg = lang_get('error_role_no_rolename'); - break; - - case tlRole::E_EMPTYROLE: - $msg = lang_get('error_role_no_rights'); - break; - - case tl::OK: - break; - - case ERROR: - case tlRole::E_DBERROR: - default: - $msg = lang_get('error_role_not_updated'); - } - return $msg; -} - - -function deleteRole(&$db,$roleID) -{ - $userFeedback = ''; - $role = new tlRole($roleID); - $role->readFromDb($db); - if ($role->deleteFromDB($db) < tl::OK) - $userFeedback = lang_get("error_role_deletion"); - else - logAuditEvent(TLS("audit_role_deleted",$role->getDisplayName()),"DELETE",$roleID,"roles"); - - return $userFeedback; + HAS EFFECTS ONLY ON LAYOUT + global $g_rights_tp; + global $g_rights_mgttc; + global $g_rights_kw; + global $g_rights_req; + global $g_rights_product; + global $g_rights_cf; + global $g_rights_users_global; + global $g_rights_users; + global $g_rights_system; + global $g_rights_platforms; + global $g_rights_issuetrackers; + global $g_rights_codetrackers; + global $g_rights_executions; + global $g_propRights_global; + global $g_propRights_product; + + // @since 1.9.7 + $l18nCfg = array( + 'desc_testplan_execute' => null, + 'desc_testplan_create_build' => null, + 'desc_testplan_metrics' => null, + 'desc_testplan_planning' => null, + 'desc_mgt_view_tc' => null, + 'desc_mgt_modify_tc' => null, + 'mgt_testplan_create' => null, + 'desc_mgt_view_key' => null, + 'desc_mgt_modify_key' => null, + 'desc_keyword_assignment' => null, + 'desc_mgt_view_req' => null, + 'desc_monitor_requirement' => null, + 'desc_mgt_modify_req' => null, + 'desc_req_tcase_link_management' => null, + 'desc_mgt_modify_product' => null, + 'desc_project_inventory_management' => null, + 'desc_project_inventory_view' => null, + 'desc_cfield_view' => null, + 'desc_cfield_management' => null, + 'desc_cfield_assignment' => null, + 'desc_exec_assign_testcases' => null, + 'desc_platforms_view' => null, + 'desc_platforms_management' => null, + 'desc_issuetrackers_view' => null, + 'desc_issuetrackers_management' => null, + 'desc_codetrackers_view' => null, + 'desc_codetrackers_management' => null, + 'desc_mgt_modify_users' => null, + 'desc_role_management' => null, + 'desc_user_role_assignment' => null, + 'desc_testproject_user_role_assignment' => null, + 'desc_testplan_user_role_assignment' => null, + 'desc_mgt_view_events' => null, + 'desc_events_mgt' => null, + 'desc_mgt_unfreeze_req' => null, + 'desc_mgt_plugins' => null, + 'right_exec_edit_notes' => null, + 'right_exec_delete' => null, + 'right_testplan_unlink_executed_testcases' => null, + 'right_testproject_delete_executed_testcases' => null, + 'right_testproject_edit_executed_testcases' => null, + 'right_testplan_milestone_overview' => null, + 'right_exec_testcases_assigned_to_me' => null, + 'right_testproject_metrics_dashboard' => null, + 'right_testplan_add_remove_platforms' => null, + 'right_testplan_update_linked_testcase_versions' => null, + 'right_testplan_set_urgent_testcases' => null, + 'right_testplan_show_testcases_newest_versions' => null, + 'right_testcase_freeze' => null, + 'right_exec_ro_access' => null, + 'right_testproject_add_remove_keywords_executed_tcversions' => null, + 'right_delete_frozen_tcversion' => null + ); + + $l18n = init_labels($l18nCfg); + + $g_rights_executions = array( + 'exec_edit_notes' => $l18n['right_exec_edit_notes'], + 'exec_delete' => $l18n['right_exec_delete'], + 'exec_ro_access' => $l18n['right_exec_ro_access'] + ); + + // order is important ? + $g_rights_tp = array( + "mgt_testplan_create" => $l18n['mgt_testplan_create'], + "testplan_create_build" => $l18n['desc_testplan_create_build'], + "testplan_planning" => $l18n['desc_testplan_planning'], + "testplan_execute" => $l18n['desc_testplan_execute'], + "testplan_metrics" => $l18n['desc_testplan_metrics'], + // "testplan_user_role_assignment" => $l18n['desc_user_role_assignment'], + "exec_assign_testcases" => $l18n['desc_exec_assign_testcases'], + "testplan_unlink_executed_testcases" => $l18n['right_testplan_unlink_executed_testcases'], + "testplan_milestone_overview" => $l18n['right_testplan_milestone_overview'], + "exec_testcases_assigned_to_me" => $l18n['right_exec_testcases_assigned_to_me'], + 'testplan_add_remove_platforms' => $l18n['right_testplan_add_remove_platforms'], + 'testplan_update_linked_testcase_versions' => $l18n['right_testplan_update_linked_testcase_versions'], + 'testplan_set_urgent_testcases' => $l18n['right_testplan_set_urgent_testcases'], + 'testplan_show_testcases_newest_versions' => $l18n['right_testplan_show_testcases_newest_versions'] + ); + + $g_rights_mgttc = array( + "mgt_view_tc" => $l18n['desc_mgt_view_tc'], + "mgt_modify_tc" => $l18n['desc_mgt_modify_tc'], + "testproject_delete_executed_testcases" => null, + "testproject_edit_executed_testcases" => null, + "testproject_add_remove_keywords_executed_tcversions" => null, + "testcase_freeze" => null, + "delete_frozen_tcversion" => null + ); + + foreach ($g_rights_mgttc as $tr => $lbl) { + if (null == $lbl) { + $g_rights_mgttc[$tr] = $l18n['right_' . $tr]; + } + } + + $g_rights_kw = array( + "mgt_view_key" => $l18n['desc_mgt_view_key'], + "keyword_assignment" => $l18n['desc_keyword_assignment'], + "mgt_modify_key" => $l18n['desc_mgt_modify_key'] + ); + + $g_rights_req = array( + "mgt_view_req" => $l18n['desc_mgt_view_req'], + "monitor_requirement" => $l18n['desc_monitor_requirement'], + "mgt_modify_req" => $l18n['desc_mgt_modify_req'], + "mgt_unfreeze_req" => $l18n['desc_mgt_unfreeze_req'], + "req_tcase_link_management" => $l18n['desc_req_tcase_link_management'] + ); + + $g_rights_product = array( + "mgt_modify_product" => $l18n['desc_mgt_modify_product'], + "cfield_assignment" => $l18n['desc_cfield_assignment'], + "project_inventory_management" => $l18n['desc_project_inventory_management'], + "project_inventory_view" => $l18n['desc_project_inventory_view'] + ); + + $g_rights_cf = array( + "cfield_view" => $l18n['desc_cfield_view'], + "cfield_management" => $l18n['desc_cfield_management'] + ); + + $g_rights_platforms = array( + "platform_view" => $l18n['desc_platforms_view'], + "platform_management" => $l18n['desc_platforms_management'] + ); + + $g_rights_issuetrackers = array( + "issuetracker_view" => $l18n['desc_issuetrackers_view'], + "issuetracker_management" => $l18n['desc_issuetrackers_management'] + ); + + $g_rights_codetrackers = array( + "codetracker_view" => $l18n['desc_codetrackers_view'], + "codetracker_management" => $l18n['desc_codetrackers_management'] + ); + + // Global means test project independent. + $g_rights_users_global = array( + "mgt_users" => $l18n['desc_mgt_modify_users'], + "role_management" => $l18n['desc_role_management'], + "user_role_assignment" => $l18n['desc_testproject_user_role_assignment'], + "testplan_user_role_assignment" => $l18n['desc_testplan_user_role_assignment'] + ); + + $g_rights_users = $g_rights_users_global; + + $g_rights_system = array( + "mgt_view_events" => $l18n['desc_mgt_view_events'], + "events_mgt" => $l18n['desc_events_mgt'], + "mgt_plugins" => $l18n['desc_mgt_plugins'] + ); + + $g_propRights_global = array_merge($g_rights_users_global, $g_rights_system, + $g_rights_product); + unset($g_propRights_global["testproject_user_role_assignment"]); + + $g_propRights_product = array_merge($g_propRights_global, $g_rights_mgttc, + $g_rights_kw, $g_rights_req); +} + +/** + * function takes a roleQuestion from a specified link and returns whether + * the user has rights to view it + * + * @param + * resource &$db reference to database handler + * @param string $roleQuestion + * a right identifier + * @param integer $tprojectID + * (optional) + * @param integer $tplanID + * (optional) + * + * @see tlUser + */ +function has_rights(&$db, $roleQuestion, $tprojectID = null, $tplanID = null) +{ + return $_SESSION['currentUser']->hasRight($db, $roleQuestion, $tprojectID, + $tplanID); +} + +/** + */ +function propagateRights($fromRights, $propRights, &$toRights) +{ + // the mgt_users right isn't test project related so this right is inherited from + // the global role (if set) + foreach ($propRights as $right => $desc) { + if (in_array($right, $fromRights) && ! in_array($right, $toRights)) { + $toRights[] = $right; + } + } +} + +/** + * TBD + * + * @param string $rights + * @param mixed $roleQuestion + * @param boolean $bAND + * [default = 1] + * @return mixed 'yes' or null + * + * @author Andreas Morsing + * @since 20.02.2006, 20:30:07 + * + */ +function checkForRights($rights, $roleQuestion, $bAND = 1) +{ + $ret = null; + // check to see if the $roleQuestion variable appears in the $roles variable + if (is_array($roleQuestion)) { + $r = array_intersect($roleQuestion, $rights); + if ($bAND) { + // for AND all rights must be present + if (count($r) == count($roleQuestion)) { + $ret = 'yes'; + } + } else { + // for OR one of all must be present + if (count($r)) { + $ret = 'yes'; + } + } + } else { + $ret = (in_array($roleQuestion, $rights) ? 'yes' : null); + } + return $ret; +} + +/** + * Get info about user(s) role at test project level, + * with indication about the nature of role: inherited or assigned. + * + * To get a user role we consider a 3 layer model: + * layer 1 - user <--- uplayer + * layer 2 - test project <--- in this fuction we are interested in this level. + * layer 3 - test plan + * + * args : $tproject_id + * [$user_id] + * + * @return array map with effetive_role in context ($tproject_id) + * key: user_id + * value: map with keys: + * login (from users table - useful for debug) + * user_role_id (from users table - useful for debug) + * uplayer_role_id (always = user_role_id) + * uplayer_is_inherited + * effective_role_id user role for test project + * is_inherited + */ +function get_tproject_effective_role(&$db, $tproject, $user_id = null, + $users = null) +{ + $effective_role = array(); + $tproject_id = $tproject['id']; + if (! is_null($user_id)) { + $users = tlUser::getByIDs($db, (array) $user_id); + } elseif (is_null($users)) { + $users = tlUser::getAll($db); + } + + if ($users) { + foreach ($users as $id => $user) { + // manage admin exception + $isInherited = 1; + $effectiveRoleID = $user->globalRoleID; + $effectiveRole = $user->globalRole; + if (($user->globalRoleID != TL_ROLES_ADMIN) && + ! $tproject['is_public']) { + $isInherited = $tproject['is_public']; + $effectiveRoleID = TL_ROLES_NO_RIGHTS; + $effectiveRole = ''; + } + + if (isset($user->tprojectRoles[$tproject_id])) { + $isInherited = 0; + $effectiveRoleID = $user->tprojectRoles[$tproject_id]->dbID; + $effectiveRole = $user->tprojectRoles[$tproject_id]; + } + + $effective_role[$id] = array( + 'login' => $user->login, + 'user' => $user, + 'user_role_id' => $user->globalRoleID, + 'uplayer_role_id' => $user->globalRoleID, + 'uplayer_is_inherited' => 0, + 'effective_role_id' => $effectiveRoleID, + 'effective_role' => $effectiveRole, + 'is_inherited' => $isInherited + ); + } + } + return $effective_role; +} + +/** + * Get info about user(s) role at test plan level, + * with indication about the nature of role: inherited or assigned. + * + * To get a user role we consider a 3 layer model: + * layer 1 - user <--- uplayer + * layer 2 - test project <--- in this fuction we are interested in this level. + * layer 3 - test plan + * + * args : $tplan_id + * $tproject_id + * [$user_id] + * + * @return array map with effetive_role in context ($tplan_id) + * key: user_id + * value: map with keys: + * login (from users table - useful for debug) + * user_role_id (from users table - useful for debug) + * uplayer_role_id user role for test project + * uplayer_is_inherited 1 -> uplayer role is inherited + * 0 -> uplayer role is written in table + * effective_role_id user role for test plan + * is_inherited + * + * @internal revisions + * 20101111 - franciscom - BUGID 4006: test plan is_public + */ +function get_tplan_effective_role(&$db, $tplan_id, $tproject, $user_id = null, + $users = null, $inheritanceMode = null) +{ + $tplan_mgr = new testplan($db); + $tplan = $tplan_mgr->get_by_id($tplan_id); + unset($tplan_mgr); + + $roleInhMode = ! is_null($inheritanceMode) ? $inheritanceMode : config_get( + 'testplan_role_inheritance_mode'); + + /** + * key: user_id + * value: map with keys: + * login (from users table - useful for debug) + * user_role_id (from users table - useful for debug) + * uplayer_role_id (always = user_role_id) + * uplayer_is_inherited + * effective_role_id user role for test project + * is_inherited + */ + $effective_role = get_tproject_effective_role($db, $tproject, $user_id, + $users); + + foreach ($effective_role as $user_id => $row) { + + $doNextStep = true; + + // Step 1 - If I've role specified for Test Plan, get and skip + if (isset($row['user']->tplanRoles[$tplan_id])) { + $isInherited = 0; + $doNextStep = false; + + $effective_role[$user_id]['effective_role_id'] = $row['user']->tplanRoles[$tplan_id]->dbID; + $effective_role[$user_id]['effective_role'] = $row['user']->tplanRoles[$tplan_id]; + } + + // For Private Test Plans specific role is NEEDED for users with + // global role !? ADMIN + if ($doNextStep && ($row['user']->globalRoleID != TL_ROLES_ADMIN) && + ! $tplan['is_public']) { + $isInherited = 0; + $doNextStep = false; + + $effective_role[$user_id]['effective_role_id'] = TL_ROLES_NO_RIGHTS; + $effective_role[$user_id]['effective_role'] = ''; + } + + if ($doNextStep) { + $isInherited = 1; + + switch ($roleInhMode) { + case 'testproject': + $effective_role[$user_id]['uplayer_role_id'] = $effective_role[$user_id]['effective_role_id']; + $effective_role[$user_id]['uplayer_is_inherited'] = $effective_role[$user_id]['is_inherited']; + break; + + case 'global': + $effective_role[$user_id]['effective_role_id'] = $row['user']->globalRoleID; + $effective_role[$user_id]['effective_role'] = $row['user']->globalRole; + break; + } + } + + $effective_role[$user_id]['is_inherited'] = $isInherited; + } + return $effective_role; +} + +function getRoleErrorMessage($code) +{ + $msg = 'ok'; + switch ($code) { + case tlRole::E_NAMEALREADYEXISTS: + $msg = lang_get('error_duplicate_rolename'); + break; + + case tlRole::E_NAMELENGTH: + $msg = lang_get('error_role_no_rolename'); + break; + + case tlRole::E_EMPTYROLE: + $msg = lang_get('error_role_no_rights'); + break; + + case tl::OK: + break; + + case tl::ERROR: + case tlRole::E_DBERROR: + default: + $msg = lang_get('error_role_not_updated'); + } + return $msg; +} + +function deleteRole(&$db, $roleID) +{ + $userFeedback = ''; + $role = new tlRole($roleID); + $role->readFromDb($db); + if ($role->deleteFromDB($db) < tl::OK) { + $userFeedback = lang_get("error_role_deletion"); + } else { + logAuditEvent(TLS("audit_role_deleted", $role->getDisplayName()), + "DELETE", $roleID, "roles"); + } + + return $userFeedback; } diff --git a/lib/functions/specview.php b/lib/functions/specview.php index b66c1ac479..f01900d7a9 100644 --- a/lib/functions/specview.php +++ b/lib/functions/specview.php @@ -1,1788 +1,1851 @@ - 2732 - * [tc_id] => 2733 - * [z] => 100 ---> nodes_hierarchy.order - * [name] => TC1 - * [tcversion_id] => 2734 - * [feature_id] => 9 --->> testplan_tcversions.ID - * [execution_order] => 10 - * [version] => 1 - * [active] => 1 - * [external_id] => 1 - * [exec_id] => 1 - * [tcversion_number] => 1 - * [executed] => 2734 - * [exec_on_tplan] => 2735 - * [user_id] => - * [type] => - * [status] => - * [assigner_id] => - * [urgency] => 2 IMPORTANT: exists ONLY FOR LINKED TEST CASES - * [exec_status] => b - * [priority] => 4 // urgency*importance IMPORTANT: exists ONLY FOR LINKED TEST CASES - * - * @param array $map_node_tccount - * @TODO probably this argument ($map_node_tccount) is not needed, but it will depend - * of how this feature (gen_spec_view) will be used on other TL areas. - * - * @param map $filters keys - * [keyword_id] default 0 - * [tcase_id] default null, can be an array - * - * @param map $options keys - * [write_button_only_if_linked] default 0 - * [prune_unlinked_tcversions]: default 0. - * Useful when working on spec_view_type='testplan'. - * 1 -> will return only linked tcversion - * 0 -> returns all test cases specs. - * [add_custom_fields]: default=0 - * useful when working on spec_view_type='testproject' - * when doing test case assign to test plans. - * 1 -> for every test case cfields of area 'testplan_design' - * will be fetched and displayed. - * 0 -> do nothing - * - * [$tproject_id]: default = null - * useful to improve performance in custom field method calls - * when add_custom_fields=1. - * - * - * @return array every element is an associative array with the following - * structure: (to get last updated info add debug code and print_r returned value) - * [testsuite] => Array( [id] => 28 - * [name] => TS1 ) - * [testcases] => Array( [2736] => Array - * ( - * [id] => 2736 - * [name] => TC2 - * [tcversions] => Array - * ( - * [2738] => 2 // key=tcversion id,value=version - * [2737] => 1 - * ) - * [tcversions_active_status] => Array - * ( - * [2738] => 1 // key=tcversion id,value=active status - * [2737] => 1 - * ) - * [tcversions_execution_type] => Array - * ( - * [2738] => 1 - * [2737] => 2 - * ) - * [tcversions_qty] => 2 - * [linked_version_id] => 2737 - * [executed] => no - * [user_id] => 0 ---> !=0 if execution has been assigned - * [feature_id] => 12 ---> testplan_tcversions.id - * [execution_order] => 20 - * [external_id] => 2 - * [linked_ts] => 2009-06-10 23:00 - * [linked_by] => 2 - * [priority] => HIGH, MEDIUM or LOW - * ) - * [81] => Array( [id] => 81 - * [name] => TC88) - * ... - * ) - * [level] = - * [write_buttons] => yes or no - * level and write_buttons are used to generate the user interface - * - * Warning: - * if the root element of the spec_view, has 0 test => then the default - * structure is returned ( $result = array('spec_view'=>array(), 'num_tc' => 0)) - * - * - */ - -function gen_spec_view(&$db, $spec_view_type='testproject', $tobj_id, $id, $name, &$linked_items, - $map_node_tccount, $filters=null, $options = null, $tproject_id = null) -{ - - $out = array(); - $result = array('spec_view'=>array(), 'num_tc' => 0, 'has_linked_items' => 0); - - $my = array(); - $my['options'] = array('write_button_only_if_linked' => 0, - 'prune_unlinked_tcversions' => 0, - 'add_custom_fields' => 0) + (array)$options; - - $my['filters'] = array('keywords' => 0, 'testcases' => null , - 'exec_type' => null, - 'importance' => null, 'cfields' => null, - 'platforms' => null); - foreach( $my as $key => $settings) { - if( !is_null($$key) && is_array($$key) ) { - $my[$key] = array_merge($my[$key],$$key); - } - } - - $write_status = $my['options']['write_button_only_if_linked'] ? 'no' : 'yes'; - $is_tplan_view_type=$spec_view_type == 'testplan' ? 1 : 0; - $is_uncovered_view_type = ($spec_view_type == 'uncoveredtestcases') ? 1 : 0; - - if (!$is_tplan_view_type && is_null($tproject_id)) { - $tproject_id = $tobj_id; - } - - $testplan_id = $is_tplan_view_type ? $tobj_id : null; - - $tcase_mgr = new testcase($db); - $hash_descr_id = $tcase_mgr->tree_manager->get_available_node_types(); - $hash_id_descr = array_flip($hash_descr_id); - - $key2map = array('keyword_id' => 'keywords', 'tcase_id' => 'testcases', - 'execution_type' => 'exec_type', - 'importance' => 'importance', - 'cfields' => 'cfields', - 'tcase_name' => 'tcase_name', - 'status' => 'workflow_status', - 'platform_id' => 'platforms'); - - $pfFilters = array('tcase_node_type_id' => $hash_descr_id['testcase']); - foreach($key2map as $tk => $fk) { - $pfFilters[$tk] = isset($my['filters'][$fk]) ? $my['filters'][$fk] : null; - } - - // transform in array to be gentle with getTestSpecFromNode() - $t2a = array('importance','status'); - foreach($t2a as $tortuga) { - if(!is_null($pfFilters[$tortuga])) { - $pfFilters[$tortuga] = (array)$pfFilters[$tortuga]; - } - } - - //var_dump(__LINE__,$pfFilters); - //die(); - - $test_spec = getTestSpecFromNode($db,$tcase_mgr,$linked_items,$tobj_id,$id,$spec_view_type,$pfFilters); - - $platforms = getPlatforms($db,$tproject_id,$testplan_id); - $idx = 0; - $a_tcid = array(); - $a_tsuite_idx = array(); - if (count($test_spec)) { - $cfg = array('node_types' => $hash_id_descr, - 'write_status' => $write_status, - 'is_uncovered_view_type' => $is_uncovered_view_type); - - // $a_tsuite_idx - // key: test case version id - // value: index inside $out, where parent test suite of test case version id is located. - // - list($a_tcid,$a_tsuite_idx,$tsuite_tcqty,$out) = buildSkeleton($id,$name,$cfg,$test_spec,$platforms); - } - - // This code has been replace (see below on Remove empty branches) - // Once we have created array with testsuite and children testsuites - // we are trying to remove nodes that has 0 test case count. - // May be this can be done (as noted by schlundus during performance - // analisys done on october 2008) in a better way, or better can be absolutely avoided. - // - // This process is needed to prune whole branches that are empty - // Need to look for every call in TL and understand if this can be removed - // - if (!is_null($map_node_tccount)) { - foreach($out as $key => $elem) { - if (isset($map_node_tccount[$elem['testsuite']['id']]) - && $map_node_tccount[$elem['testsuite']['id']]['testcount'] == 0) { - // why not unset ? - $out[$key]=null; - } - } - } - - // Collect information related to linked testcase versions - if(!is_null($out) && count($out) > 0 && !is_null($out[0]) && count($a_tcid)) - { - $optGBI = array('output' => 'full_without_users', - 'order_by' => " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC "); - - $tcaseVersionSet = $tcase_mgr->get_by_id($a_tcid,testcase::ALL_VERSIONS,null,$optGBI); - $result = addLinkedVersionsInfo($tcaseVersionSet,$a_tsuite_idx,$out,$linked_items,$options); - } - - // Try to prune empty test suites, to reduce memory usage and - // to remove elements - // that do not need to be displayed on user interface. - if (count($result['spec_view']) > 0) { - removeEmptyTestSuites($result['spec_view'],$tcase_mgr->tree_manager, - ($my['options']['prune_unlinked_tcversions'] && $is_tplan_view_type),$hash_descr_id); - } - - // Remove empty branches - // Loop to compute test case qty ($tsuite_tcqty) on every level and prune test suite branchs that are empty - if (count($result['spec_view']) > 0) { - removeEmptyBranches($result['spec_view'],$tsuite_tcqty); - } - - /** @TODO: maybe we can integrate this into already present loops above? */ - // This is not right condition for identifing an empty test suite for the porpouse - // of gen_spec_view(), because for following structure - // TS1 - // \--- TS2 - // \--TC1 - // \--TC2 - // - // \--- TS3 - // \-- TXX - // - // When we are displaying a Test Specification we want to see previous structure - // But if we apply this criteria for empty test suite, TS1 results empty and will - // be removed -> WRONG - // - // Need to understand when this feature will be needed and then reimplement - // - // if ($prune_empty_tsuites) - // { - // foreach($result['spec_view'] as $key => $value) - // { - // if(is_null($value) || !isset($value['testcases']) || !count($value['testcases'])) - // unset($result['spec_view'][$key]); - // } - // } - - // #1650 We want to manage custom fields when user is doing test case execution assigment - if( count($result['spec_view']) > 0 && $my['options']['add_custom_fields']) - { - addCustomFieldsToView($result['spec_view'],$tproject_id,$tcase_mgr); - } - // ------------------------------------------------------------------------ - unset($tcase_mgr); - - // with array_values() we reindex array to avoid "holes" - $result['spec_view']= array_values($result['spec_view']); - return $result; -} - -/** -* -*/ -function gen_coverage_view(&$db, $spec_view_type='testproject', $tobj_id, $id, $name, &$linked_items, -$map_node_tccount, $filters=null, $options = null, $tproject_id = null) -{ - $out = array(); - $result = array('spec_view'=>array(), 'num_tc' => 0, 'has_linked_items' => 0); - - $my = array(); - $my['options'] = array('write_button_only_if_linked' => 0,'prune_unlinked_tcversions' => 0, - 'add_custom_fields' => 0) + (array)$options; - - $my['filters'] = array('keywords' => 0, 'testcases' => null ,'exec_type' => null, - 'importance' => null, 'cfields' => null); - foreach( $my as $key => $settings) - { - if( !is_null($$key) && is_array($$key) ) - { - $my[$key] = array_merge($my[$key],$$key); - } - } - - $write_status = $my['options']['write_button_only_if_linked'] ? 'no' : 'yes'; - $is_tplan_view_type=$spec_view_type == 'testplan' ? 1 : 0; - $is_uncovered_view_type = ($spec_view_type == 'uncoveredtestcases') ? 1 : 0; - - if( !$is_tplan_view_type && is_null($tproject_id) ) - { - $tproject_id = $tobj_id; - } - - $testplan_id = $is_tplan_view_type ? $tobj_id : null; - - - $tcase_mgr = new testcase($db); - $hash_descr_id = $tcase_mgr->tree_manager->get_available_node_types(); - $hash_id_descr = array_flip($hash_descr_id); - - $key2map = array('keyword_id' => 'keywords', 'tcase_id' => 'testcases', - 'execution_type' => 'exec_type', 'importance' => 'importance', - 'cfields' => 'cfields','tcase_name' => 'tcase_name' ); - - $pfFilters = array('tcase_node_type_id' => $hash_descr_id['testcase']); - foreach($key2map as $tk => $fk) - { - $pfFilters[$tk] = isset($my['filters'][$fk]) ? $my['filters'][$fk] : null; - } - - $test_spec = getTestSpecFromNode($db,$tcase_mgr,$linked_items,$tobj_id,$id,$spec_view_type,$pfFilters,'req_order'); - - $platforms = getPlatforms($db,$tproject_id,$testplan_id); - $idx = 0; - $a_tcid = array(); - $a_tsuite_idx = array(); - if (count($test_spec)) { - $cfg = array('node_types' => $hash_id_descr, - 'write_status' => $write_status, - 'is_uncovered_view_type' => $is_uncovered_view_type); - - // $a_tsuite_idx - // key: test case version id - // value: index inside $out, where parent test suite of test case version id is located. - // - list($a_tcid,$a_tsuite_idx,$tsuite_tcqty,$out) = buildSkeleton($id,$name,$cfg,$test_spec,$platforms); - } - - // This code has been replace (see below on Remove empty branches) - // Once we have created array with testsuite and children testsuites - // we are trying to remove nodes that has 0 test case count. - // May be this can be done (as noted by schlundus during performance - // analisys done on october 2008) in a better way, or better can be absolutely avoided. - // - // This process is needed to prune whole branches that are empty - // Need to look for every call in TL and understand if this can be removed - // - if(!is_null($map_node_tccount)) - { - foreach($out as $key => $elem) - { - if(isset($map_node_tccount[$elem['testsuite']['id']]) && - $map_node_tccount[$elem['testsuite']['id']]['testcount'] == 0) - { - $out[$key]=null; - } - } - } - - // Collect information related to linked testcase versions - if(!is_null($out) && count($out) > 0 && !is_null($out[0]) && count($a_tcid)) - { - $optGBI = array('output' => 'full_without_users', - 'order_by' => " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC "); - - $tcaseVersionSet = $tcase_mgr->get_by_id($a_tcid,testcase::ALL_VERSIONS,null,$optGBI); - $result = addLinkedVersionsInfo($tcaseVersionSet,$a_tsuite_idx,$out,$linked_items); - - } - - // Try to prune empty test suites, to reduce memory usage and to remove elements - // that do not need to be displayed on user interface. - if( count($result['spec_view']) > 0) - { - //removeEmptyTestSuites($result['spec_view'],$tcase_mgr->tree_manager, - //($my['options']['prune_unlinked_tcversions'] && $is_tplan_view_type),$hash_descr_id); - } - - // Remove empty branches - // Loop to compute test case qty ($tsuite_tcqty) on every level and prune test suite branchs that are empty - if( count($result['spec_view']) > 0) - { - //removeEmptyBranches($result['spec_view'],$tsuite_tcqty); -} - - - - /** @TODO: maybe we can integrate this into already present loops above? */ - // This is not right condition for identifing an empty test suite for the porpouse - // of gen_spec_view(), because for following structure - // TS1 - // \--- TS2 - // \--TC1 - // \--TC2 - // - // \--- TS3 - // \-- TXX - // - // When we are displaying a Test Specification we want to see previous structure - // But if we apply this criteria for empty test suite, TS1 results empty and will - // be removed -> WRONG - // - // Need to understand when this feature will be needed and then reimplement - // - // if ($prune_empty_tsuites) - // { - // foreach($result['spec_view'] as $key => $value) - // { - // if(is_null($value) || !isset($value['testcases']) || !count($value['testcases'])) - // unset($result['spec_view'][$key]); - // } - // } - - // #1650 We want to manage custom fields when user is doing test case execution assigment - if( count($result['spec_view']) > 0 && $my['options']['add_custom_fields']) - { - addCustomFieldsToView($result['spec_view'],$tproject_id,$tcase_mgr); -} -// -------------------------------------------------------------------------------------------- -unset($tcase_mgr); - - // with array_values() we reindex array to avoid "holes" -$result['spec_view']= array_values($result['spec_view']); -return $result; -} - -/** - * get linked versions filtered by Keyword ID - * Filter is done ONLY on attributes THAT ARE COMMON to ALL test case versions, - * because (till now) while adding/removing test cases user works on Test Spec Tree - * and filter applied to this tree acts on: - * - * 1. attributes COMMON to all versions - * 2. attributes present ON LAST ACTIVE version. - * - * But do no make considerations regarding versions linked to test plan - * DEV NOTE: may be this has to be changed in future ? - * - * @param ref $dbHandler: - * @param ref $argsObj: stdClass object with information about filters - * @param ref $tplanMgr: test plan manager object - * @param ref $tcaseMgr: test case manager object - * @param map $options: default null (at today 20110820 seems not be used). - * @param boolean $isTestSuite: filter testGroupBy, default true - * - */ -function getFilteredLinkedVersions(&$dbHandler,&$argsObj, &$tplanMgr, - &$tcaseMgr, - $options = null, $isTestSuite=true) -{ - static $tsuite_mgr; - $doFilterByKeyword = (!is_null($argsObj->keyword_id) - && $argsObj->keyword_id > 0) ? true : false; - - // Multiple step algoritm to apply keyword filter on type=AND - // get_*_tcversions filters by keyword ALWAYS in OR mode. - // - $filters = array('keyword_id' => $argsObj->keyword_id, - 'platform_id' => null); - if( property_exists($argsObj,'control_panel') && - isset($argsObj->control_panel['setting_platform']) && - intval($argsObj->control_panel['setting_platform']) > 0 ) { - $filters['platform_id'] = intval($argsObj->control_panel['setting_platform']); - } - - if( isset($options['assigned_on_build']) - && $options['assigned_on_build'] > 0) { - $filters['assigned_on_build'] = $options['assigned_on_build']; - } - - // get test suites in branch to limit search - if($isTestSuite) { - $itemID = property_exists($argsObj,'object_id') ? $argsObj->object_id : $argsObj->id; - if( !is_null($itemID) ) { - // will get all test suites in this branch, in order to limit amount of data returned by - // get_*_tcversions - if (!$tsuite_mgr) { - $tsuite_mgr = new testsuite($dbHandler); - } - $xx = $tsuite_mgr->get_branch($itemID); - $xx .= ($xx == '') ? $itemID : ',' . $itemID; - $filters['tsuites_id'] = explode(',',$xx); - } - } - - // $opx = array('addExecInfo' => true, 'specViewFields' => true) + (array)$options; - $opx = array_merge( array('addExecInfo' => true, 'specViewFields' => true, - 'tlFeature' => 'none'), - (array)$options ); - - switch ($opx['tlFeature']) { - case 'testCaseExecTaskAssignment': - $method2call = 'getLinkedTCVXmen'; - break; - - case 'testCaseTestPlanAssignment': - default: - $method2call = 'getLTCVNewGeneration'; - break; - } - - if (isset($argsObj->testcases_to_show) - && !is_null($argsObj->testcases_to_show)) { - $filters['tcase_id'] = $argsObj->testcases_to_show; - } - - if (isset($argsObj->platform_id) - && $argsObj->platform_id > 0) { - $filters['platform_id'] = $argsObj->platform_id; - } - - $tplan_tcases = $tplanMgr->$method2call($argsObj->tplan_id, $filters, $opx); - - if( !is_null($tplan_tcases) && $doFilterByKeyword && $argsObj->keywordsFilterType == 'AND') - { - $filteredSet = $tcaseMgr->filterByKeyword(array_keys($tplan_tcases), - $argsObj->keyword_id,$argsObj->keywordsFilterType); - - $filters = array('tcase_id' => array_keys($filteredSet)); - - // HERE WE CAN HAVE AN ISSUE - $tplan_tcases = $tplanMgr->getLTCVNewGeneration($argsObj->tplan_id, $filters, $opx); - } - return $tplan_tcases; -} - - -/** - * - * @param obj $dbHandler - * @param obj $argsObj: user input - * @param obj $argsObj: user input - * @param obj $tplanMgr: test plan manager - * @param obj $tcaseMgr: test case manager - * @param map $filters: keys keywordsFilter, testcaseFilter,assignedToFilter, - * executionTypeFilter, cfieldsFilter - * - * IMPORTANT NOTICE: not all filters are here, - * other arrive via argsObj - * @param map $options: keys ?? - * USED TO PASS options to other method called here - * -> see these method docs. - * - */ -function getFilteredSpecView(&$dbHandler, &$argsObj, &$tplanMgr, &$tcaseMgr, $filters=null, $options=null) -{ - $tprojectMgr = new testproject($dbHandler); - $tsuite_data = $tcaseMgr->tree_manager->get_node_hierarchy_info($argsObj->id); - - $my = array(); // some sort of local scope - $my['filters'] = array('keywordsFilter' => null, 'testcaseFilter' => null, - 'assignedToFilter' => null,'executionTypeFilter' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $my['options'] = array('write_button_only_if_linked' => 1, 'prune_unlinked_tcversions' => 1); - $my['options'] = array_merge($my['options'],(array)$options); - - // This does filter on keywords ALWAYS in OR mode. - $tplan_linked_tcversions = - getFilteredLinkedVersions($dbHandler,$argsObj, $tplanMgr, $tcaseMgr, $options); - - // With these pieces we implement the AND type of keyword filter. - $testCaseSet = null; - $tryNextFilter = true; - $filterApplied = false; - if(!is_null($my['filters']['keywordsFilter']) && !is_null($my['filters']['keywordsFilter']->items)) { - - /* - $keywordsTestCases = $tprojectMgr->get_keywords_tcases($argsObj->tproject_id, - $my['filters']['keywordsFilter']->items, - $my['filters']['keywordsFilter']->type); - - */ - $keywordsTestCases = $tprojectMgr->getKeywordsLatestTCV($argsObj->tproject_id, - $my['filters']['keywordsFilter']->items, - $my['filters']['keywordsFilter']->type); - - $testCaseSet = array_keys((array)$keywordsTestCases); - $tryNextFilter = !is_null($testCaseSet); - $filterApplied = true; - } - - if( $tryNextFilter && !is_null($my['filters']['testcaseFilter'])) - { - $filterApplied = true; - if (is_null($testCaseSet)) { - $testCaseSet = $my['filters']['testcaseFilter']; - } else { - // wrong use of array() instead of (array) - $testCaseSet = array_intersect($testCaseSet, (array)$my['filters']['testcaseFilter']); - } - } - - // when $testCaseSet is null because we have - // applied filters => we do not need to call other - // method because we know we are going to get NOTHING - $testCaseSet = !is_null($testCaseSet) ? - array_combine($testCaseSet, $testCaseSet) : null; - if ($filterApplied && is_null($testCaseSet)) { - return null; - } - - $genSpecFilters = array('keywords' => $argsObj->keyword_id, - 'testcases' => $testCaseSet, - 'exec_type' => $my['filters']['executionTypeFilter'], - 'cfields' => null); - - - if( isset($my['filters']['cfieldsFilter']) ) { - $genSpecFilters['cfields'] = $my['filters']['cfieldsFilter']; - } - - $out = gen_spec_view($dbHandler, 'testplan', - $argsObj->tplan_id, $argsObj->id, $tsuite_data['name'], - $tplan_linked_tcversions, null, $genSpecFilters, $my['options']); - return $out; -} - - -/** - * get Test Specification data within a Node - * - * using nodeId (that normally is a test suite id) as starting point - * will return subtree that start at nodeId. - * If filters are given, the subtree returned is filtered. - * - * Important Notice regaring keyword filtering - * Keyword filter logic inside this function seems to work ONLY on OR mode. - * Then how the AND mode is implemented ? - * Filter for test case id is used, and the test case set has been generated - * applying AND or OR logic (following user's choice). - * Then seems that logic regarding keywords here, may be can be removed - * - * @param integer $masterContainerId can be a Test Project Id, or a Test Plan id. - * is used only if keyword id filter has been specified - * to get all keyword defined on masterContainer. - * - * @param integer $nodeId node that will be root of the view we want to build. - * - * @param string $specViewType: type of view requested - * - * @param array $filters - * filters['keyword_id']: array of keywords - * filters['tcase_id']: - * filters['execution_type']: - * filters['importance']: - * filters['cfields']: - * filters['tcase_name']: - * - * @param string $type: request selected for create tree - * - * @return array map with view (test cases subtree) - * - * - */ -function getTestSpecFromNode(&$dbHandler,&$tcaseMgr,&$linkedItems,$masterContainerId,$nodeId,$specViewType,$filters, $type='spec_order') -{ - $applyFilters = false; - $testCaseSet = null; - $tck_map = null; - $tobj_mgr = new testproject($dbHandler); - - $opt = array('order_cfg' => array("type" =>$type)); - if($specViewType =='testplan') { - $opt['order_cfg']=array("type" =>'exec_order', 'tplan_id' => $masterContainerId); - } - $test_spec = $tobj_mgr->get_subtree($nodeId,null,$opt); - - $key2loop = null; - $useAllowed = false; - - $nullCheckFilter = array('tcase_id' => false, - 'importance' => false, - 'tcase_name' => false, - 'cfields' => false, 'status' => false); - - $zeroNullCheckFilter = array('execution_type' => false); - $useFilter = array('keyword_id' => false, 'platform_id' => false) - + $nullCheckFilter + $zeroNullCheckFilter; - - $applyFilters = false; - - foreach($nullCheckFilter as $key => $value) { - $useFilter[$key] = !is_null($filters[$key]); - $applyFilters = $applyFilters || $useFilter[$key]; - } - - // more specif analisys - if( ($useFilter['status']=($filters['status'][0] > 0)) ) { - $applyFilters = true; - $filtersByValue['status'] = array_flip((array)$filters['status']); - } - - if( ($useFilter['importance']=($filters['importance'][0] > 0)) ) { - $applyFilters = true; - $filtersByValue['importance'] = array_flip((array)$filters['importance']); - } - - - foreach($zeroNullCheckFilter as $key => $value) { - // need to check for > 0, because for some items 0 has same meaning that null -> no filter - $useFilter[$key] = (!is_null($filters[$key]) && ($filters[$key] > 0)); - $applyFilters = $applyFilters || $useFilter[$key]; - } - - if( $useFilter['tcase_id'] ) { - $testCaseSet = is_array($filters['tcase_id']) ? $filters['tcase_id'] : array($filters['tcase_id']); - } - - if(!is_array($filters['keyword_id']) ) { - $filters['keyword_id'] = array($filters['keyword_id']); - } - - if(($useFilter['keyword_id']=$filters['keyword_id'][0] > 0)) { - $applyFilters = true; - switch ($specViewType) { - case 'testplan': - $tobj_mgr = new testplan($dbHandler); - $tck_map = $tobj_mgr->getKeywordsLinkedTCVersions($masterContainerId, - $filters['keyword_id']); - break; - - default: - $tck_map = $tobj_mgr->getKeywordsLatestTCV($masterContainerId, - $filters['keyword_id']); - break; - } - } - - $tcpl_map = null; - if(($useFilter['platforms']=$filters['platform_id'][0] > 0)) { - $applyFilters = true; - switch ($specViewType) { - case 'testplan': - $tobj_mgr = new testplan($dbHandler); - $tcpl_map = $tobj_mgr->getPlatformsLinkedTCVersions($masterContainerId, - $filters['platforms']); - break; - - default: - $tcpl_map = $tobj_mgr->getPlatformsLatestTCV($masterContainerId, - $filters['platforms']); - break; - } - } - - - - if( $applyFilters ) { - $key2loop = array_keys($test_spec); - - // first step: generate list of TEST CASE NODES - $itemSet = null ; - foreach($key2loop as $key) { - if( ($test_spec[$key]['node_type_id'] == $filters['tcase_node_type_id']) ) { - $itemSet[$test_spec[$key]['id']] = $key; - } - } - $itemKeys = $itemSet; - - foreach($itemKeys as $key => $tspecKey) { - // case insensitive search - if( ($useFilter['keyword_id'] - && !isset($tck_map[$test_spec[$tspecKey]['id']]) ) - - || ($useFilter['platforms'] - && !isset($tcpl_map[$test_spec[$tspecKey]['id']]) ) - - || ($useFilter['tcase_id'] - && !in_array($test_spec[$tspecKey]['id'],$testCaseSet)) - || ($useFilter['tcase_name'] - && (stripos($test_spec[$tspecKey]['name'],$filters['tcase_name']) === false)) - ) { - $test_spec[$tspecKey]=null; - unset($itemSet[$key]); - } - } - - if( count($itemSet) > 0 && - ($useFilter['execution_type'] || $useFilter['importance'] || $useFilter['cfields'] || - $useFilter['status']) - ) { - // This logic can have some Potential Performance ISSUE - 20120619 - fman - $targetSet = array_keys($itemSet); - $options = ($specViewType == 'testPlanLinking') ? array( 'access_key' => 'testcase_id') : null; - - $getFilters = $useFilter['cfields'] ? array('cfields' => $filters['cfields']) : null; - $s2h = config_get('tplanDesign')->hideTestCaseWithStatusIn; - if( !is_null($s2h) ) { - $getFilters['status'] = array('not_in' => array_keys($s2h)); - } - - //var_dump($getFilters); - $tcversionSet = $tcaseMgr->get_last_active_version($targetSet,$getFilters,$options); - - switch($specViewType) { - case 'testPlanLinking': - // We need to analise linked items and spec - foreach($targetSet as $idx => $key) { - $targetTestCase = isset($tcversionSet[$key]) ? $tcversionSet[$key]['testcase_id'] : null; - - if( is_null($targetTestCase) ) { - $test_spec[$itemSet[$key]]=null; - $item = null; - } else { - if( isset($linkedItems[$targetTestCase]) ) { - $item = current($linkedItems[$targetTestCase]); - } else { - // hmmm, does not understand this logic. - $item = null; - if( isset($test_spec[$itemSet[$targetTestCase]]) ) { - $item = $tcversionSet[$targetTestCase]; - } - } - } - - if( !is_null($item) ) { - if( $useFilter['execution_type'] && - ($item['execution_type'] != $filters['execution_type']) || - $useFilter['importance'] && - (!isset($filtersByValue['importance'][$item['importance']])) || - $useFilter['status'] && - (!isset($filtersByValue['status'][$item['status']])) - ) { - $tspecKey = $itemSet[$targetTestCase]; - $test_spec[$tspecKey]=null; - } - } - } - break; - - default: - $tcvidSet = array_keys($tcversionSet); - foreach($tcvidSet as $zx) { - $tcidSet[$tcversionSet[$zx]['testcase_id']] = $zx; - } - - $options = null; - $doFilter = true; - $allowedSet = null; - - // a first clean will not be bad, ok may be we are going to do more - // loops that needed, but think logic will be more clear - // (at least @20130426 is a little bit confusing ;) ) - foreach($targetSet as $idx => $key) { - if( !isset($tcidSet[$key]) ) { - $test_spec[$itemSet[$key]]=null; - } - } - - if( $useFilter['execution_type'] ) { - // Potential Performance ISSUE - $allowedSet = $tcaseMgr->filter_tcversions_by_exec_type($tcvidSet,$filters['execution_type'],$options); - $doFilter = (!is_null($allowedSet) && count($allowedSet) > 0); - } - - if( $doFilter ) { - // Add another filter on cascade mode - // @20130426 - seems we are applying TWICE the Custom Fields Filter - // because we have applied it before on: - // $tcversionSet = $tcaseMgr->get_last_active_version() - if( $useFilter['cfields'] ) { - $filteredSet = (!is_null($allowedSet) && count($allowedSet) > 0) ? array_keys($allowedSet) : $tcvidSet; - $dummySet = $tcaseMgr->filter_tcversions_by_cfields($filteredSet,$filters['cfields'],$options); - - // transform to make compatible with filter_tcversions_by_exec_type() return type - if( !is_null($dummySet) && count($dummySet) > 0 ) { - $allowedSet = null; - $work2do = array_keys($dummySet); - foreach($work2do as $wkey) { - $allowedSet[$wkey] = $dummySet[$wkey][0]; - } - unset($dummySet); - } - } - } - - if( !is_null($allowedSet) && count($allowedSet) > 0 ) { - $useAllowed = true; - foreach($allowedSet as $key => $value) { - $tspecKey = $itemSet[$value['testcase_id']]; - $test_spec[$tspecKey]['version']=$value['version']; - } - reset($allowedSet); - } - - $setToRemove = array_diff_key($tcversionSet,$allowedSet); - if( !is_null($setToRemove) && count($setToRemove) > 0 ) { - foreach($setToRemove as $key => $value) { - $tspecKey = $itemSet[$value['testcase_id']]; - $test_spec[$tspecKey]=null; - } - } - break; - } // end switch - } - } // if apply filters - unset($tobj_mgr); - return $test_spec; -} - - -/** - * remove empty Test Suites - * - * @param array $testSuiteSet reference to set to analyse and clean. - * @param object $treeMgr reference to object - * @param TBD $pruneUnlinkedTcversions useful when working on test plans - * @param TBD $nodeTypes hash key: node type description, value: code - */ -function removeEmptyTestSuites(&$testSuiteSet,&$treeMgr,$pruneUnlinkedTcversions,$nodeTypes) -{ - foreach($testSuiteSet as $key => $value) - { - // We will remove test suites that meet the empty conditions: - // - do not contain other test suites OR - // - do not contain test cases - if( is_null($value) ) - { - unset($testSuiteSet[$key]); - } - - else if ($pruneUnlinkedTcversions && - (isset($value['testcase_qty']) && $value['testcase_qty'] > 0) ) - { - // only linked tcversion must be returned, but this analisys must be done - // for test suites that has test cases. - if( isset($value['linked_testcase_qty']) && $value['linked_testcase_qty']== 0) - { - unset($testSuiteSet[$key]); - } - else - { - // Only if test suite has children test cases we need to understand - // if they are linked or not - if( isset($value['testcases']) && count($value['testcases']) > 0 ) - { - foreach($value['testcases'] as $skey => $svalue) - { - if( $svalue['linked_version_id'] == 0) - { - unset($testSuiteSet[$key]['testcases'][$skey]); - } - } - } - } // is_null($value) - } - - else - { - // list of children test suites if useful on smarty template, in order - // to draw nested div. - $tsuite_id=$value['testsuite']['id']; - $testSuiteSet[$key]['children_testsuites']= - $treeMgr->get_subtree_list($tsuite_id,$nodeTypes['testsuite']); - - if( $value['testcase_qty'] == 0 && $testSuiteSet[$key]['children_testsuites']=='' ) - { - unset($testSuiteSet[$key]); - } - } - } - -} - - -/** - * - * - */ -function removeEmptyBranches(&$testSuiteSet,&$tsuiteTestCaseQty) -{ - foreach($testSuiteSet as $key => $elem) - { - $tsuite_id=$elem['testsuite']['id']; - - if( !isset($tsuiteTestCaseQty[$tsuite_id]) ) - { - $tsuiteTestCaseQty[$tsuite_id]=0; - } - - if( isset($elem['children_testsuites']) && $elem['children_testsuites'] != '' ) - { - $children=explode(',',$elem['children_testsuites']); - foreach($children as $access_id) - { - if( isset($tsuiteTestCaseQty[$access_id]) ) - { - $tsuiteTestCaseQty[$tsuite_id] += $tsuiteTestCaseQty[$access_id]; - } - } - } - - if( $tsuiteTestCaseQty[$tsuite_id]== 0 ) - { - unset($testSuiteSet[$key]); - } - } -} // function end - - -/** - * @param array &$testSuiteSet: changes will be done to this array - * to add custom fields info. - * Custom field info will be indexed by platform id - * - * @param integer $tprojectId - * @param object &$tcaseMgr reference to testCase class instance - * - * - * @internal revisions - * 20100119 - franciscom - start fixing missing platform refactoring - * - */ -function addCustomFieldsToView(&$testSuiteSet,$tprojectId,&$tcaseMgr) -{ - // Important: - // testplan_tcversions.id value, that is used to link to manage custom fields that are used - // during testplan_design is present on key 'feature_id' (only is linked_version_id != 0) - foreach($testSuiteSet as $key => $value) - { - if( !is_null($value) ) - { - if( isset($value['testcases']) && count($value['testcases']) > 0 ) - { - foreach($value['testcases'] as $skey => $svalue) - { - if( ($linked_version_id=$svalue['linked_version_id']) > 0 ) - { - $platformSet = array_keys($svalue['feature_id']); - foreach($platformSet as $platform_id) - { - $testSuiteSet[$key]['testcases'][$skey]['custom_fields'][$platform_id]=''; - if( $linked_version_id != 0 ) - { - $cf_name_suffix = "_" . $svalue['feature_id'][$platform_id]; - $cf_map = $tcaseMgr->html_table_of_custom_field_inputs($linked_version_id,null,'testplan_design', - $cf_name_suffix,$svalue['feature_id'][$platform_id], - null,$tprojectId); - $testSuiteSet[$key]['testcases'][$skey]['custom_fields'][$platform_id] = $cf_map; - } - } - } - } - } - - } // is_null($value) - } -} // function end - - -/** - * - * Developer Notice - * key 'user_id' is JUST initialized - */ -function buildSkeleton($id,$name,$config,&$test_spec,&$platforms) -{ - $parent_idx=-1; - $pivot_tsuite = $test_spec[0]; - $level = array(); - $tcase_memory = null; - - $node_types = $config['node_types']; - $write_status = $config['write_status']; - $is_uncovered_view_type = $config['is_uncovered_view_type']; - - $out=array(); - $idx = 0; - $a_tcid = array(); - $a_tsuite_idx = array(); - $hash_id_pos[$id] = $idx; - $out[$idx]['testsuite'] = array('id' => $id, 'name' => $name); - $out[$idx]['testcases'] = array(); - $out[$idx]['write_buttons'] = 'no'; - $out[$idx]['testcase_qty'] = 0; - $out[$idx]['level'] = 1; - $out[$idx]['linked_testcase_qty'] = 0; - $out[$idx]['linked_ts'] = null; - $out[$idx]['linked_by'] = 0; - $out[$idx]['priority'] = 0; - - $the_level = $out[0]['level']+1; - $idx++; - $tsuite_tcqty=array($id => 0); - - foreach ($test_spec as $current) - { - if(is_null($current)) - { - continue; - } - // In some situations during processing of testcase, a change of parent can - // exists, then we need to update $tsuite_tcqty - if($node_types[$current['node_type_id']] == "testcase") - { - $tc_id = $current['id']; - $parent_idx = $hash_id_pos[$current['parent_id']]; - $a_tsuite_idx[$tc_id] = $parent_idx; - $out[$parent_idx]['testcases'][$tc_id] = array('id' => $tc_id,'name' => $current['name']); - - // Reference to make code reading more human friendly - $outRef = &$out[$parent_idx]['testcases'][$tc_id]; - - if($is_uncovered_view_type) - { - // @TODO understand impacts of platforms - $outRef['external_id'] = $test_spec[$tc_id]['external_id']; - } - else - { - $out[$parent_idx]['write_buttons'] = $write_status; - $out[$parent_idx]['linked_testcase_qty'] = 0; - - $outRef['tcversions'] = array(); - $outRef['tcversions_active_status'] = array(); - $outRef['tcversions_execution_type'] = array(); - $outRef['tcversions_qty'] = 0; - $outRef['linked_version_id'] = 0; - $outRef['executed'] = null; // 'no'; - - // useful for tc_exec_assignment.php - $outRef['platforms'] = $platforms; - $outRef['feature_id'] = null; //0; - $outRef['linked_by'] = null; //0; - $outRef['linked_ts'] = null; - $outRef['priority'] = 0; - $outRef['user_id'] = array(); - } - $out[$parent_idx]['testcase_qty']++; - $a_tcid[] = $current['id']; - - // This piece is needed initialize in right way $tsuite_tcqty - // in this kind of situation, for SubSuite2 - // - // Tsuite 1 - // |__ SubSuite1 - // | |__TCX1 - // | |__TCX2 - // | - // |__ SubSuite2 - // | |__TCY1 - // | |__TCY2 - // | - // |__ TCZ1 - // - // - if( $tcase_memory['parent_id'] != $current['parent_id'] ) - { - if( !is_null($tcase_memory) ) - { - $pidx = $hash_id_pos[$tcase_memory['parent_id']]; - $xdx=$out[$pidx]['testsuite']['id']; - $tsuite_tcqty[$xdx]=$out[$pidx]['testcase_qty']; - } - $tcase_memory=$current; - } - } - else - { - // This node is a Test Suite - $the_level = 0; - if($parent_idx >= 0) - { - $xdx=$out[$parent_idx]['testsuite']['id']; - $tsuite_tcqty[$xdx]=$out[$parent_idx]['testcase_qty']; - } - - if($pivot_tsuite['parent_id'] != $current['parent_id']) - { - if ($pivot_tsuite['id'] == $current['parent_id']) - { - $the_level++; - $level[$current['parent_id']] = $the_level; - } - else - { - if( isset($level[$current['parent_id']]) ) - { - $the_level = $level[$current['parent_id']]; - } - } - } - $out[$idx]['testsuite']=array('id' => $current['id'], 'name' => $current['name']); - $out[$idx]['testcases'] = array(); - $out[$idx]['testcase_qty'] = 0; - $out[$idx]['linked_testcase_qty'] = 0; - $out[$idx]['level'] = $the_level; - $out[$idx]['write_buttons'] = 'no'; - $hash_id_pos[$current['id']] = $idx; - $idx++; - - // update pivot. - $level[$current['parent_id']] = $the_level; - $pivot_tsuite = $current; - } - } // foreach - - // Update after finished loop - if($parent_idx >= 0) - { - $xdx=$out[$parent_idx]['testsuite']['id']; - $tsuite_tcqty[$xdx]=$out[$parent_idx]['testcase_qty']; - } - - unset($tcase_memory); - $tsuite_tcqty[$id] = $out[$hash_id_pos[$id]]['testcase_qty']; - return array($a_tcid,$a_tsuite_idx,$tsuite_tcqty,$out); -} - - -/** - * VERY IMPORTANT NOTICE - * - * You can be a little bit confused regarding What will be returned on 'testcases' =>[]['tcversions'] - * You will see JUST ON tcversion with active status = 0, ONLY if the version is LINKED to test plan. - * Otherwise you will get ONLY ACTIVE test case versions. - * - * - * @internal revisions: - */ -function addLinkedVersionsInfo($testCaseVersionSet,$a_tsuite_idx,&$out,&$linked_items,$opt=null) -{ - $my['opt'] = array('useOptionalArrayFields' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $tcStatus2exclude = config_get('tplanDesign')->hideTestCaseWithStatusIn; - $optionalIntegerFields = array('feature_id','linked_by'); - $optionalArrayFields = array('user_id'); - - $result = array('spec_view'=>array(), 'num_tc' => 0, 'has_linked_items' => 0); - $pivot_id=-1; - $firstElemIDX = key($out); - - foreach($testCaseVersionSet as $the_k => $testCase) - { - $tc_id = $testCase['testcase_id']; - - // Needed when having multiple platforms - if($pivot_id != $tc_id ) - { - $pivot_id = $tc_id; - $result['num_tc']++; - } - $parent_idx = $a_tsuite_idx[$tc_id]; - - // Reference to make code reading more human friendly - $outRef = &$out[$parent_idx]['testcases'][$tc_id]; - - // Is not clear (need explanation) why we process in this part ONLY ACTIVE - // also we need to explain !is_null($out[$parent_idx]) - // - if($testCase['active'] == 1 && !isset($tcStatus2exclude[$testCase['status']]) && - !is_null($out[$parent_idx]) ) - { - if( !isset($outRef['execution_order']) ) - { - // Doing this I will set order for test cases that still are not linked. - // But Because I loop over all versions (linked and not) if I always write, - // will overwrite right execution order of linked tcversion. - // - // N.B.: - // As suggested by Martin Havlat order will be set to external_id * 10 - $outRef['execution_order'] = $testCase['node_order'] * 10; - } - $outRef['tcversions'][$testCase['id']] = $testCase['version']; - $outRef['tcversions_active_status'][$testCase['id']] = 1; - $outRef['external_id'] = $testCase['tc_external_id']; - $outRef['tcversions_execution_type'][$testCase['id']] = $testCase['execution_type']; - $outRef['importance'][$testCase['id']] = $testCase['importance']; - $outRef['status'][$testCase['id']] = $testCase['status']; - - if (!isset($outRef['tcversions_qty'])) - { - $outRef['tcversions_qty']=0; - } - $outRef['tcversions_qty']++; - } - - if(!is_null($linked_items)) - { - foreach($linked_items as $linked_testcase) - { - $target = current($linked_testcase); - if(($target['tc_id'] == $testCase['testcase_id']) && - ($target['tcversion_id'] == $testCase['id']) ) - { - // This can be written only once no matter platform qty - if( !isset($outRef['tcversions'][$testCase['id']]) ) - { - $outRef['tcversions'][$testCase['id']] = $testCase['version']; - $outRef['tcversions_active_status'][$testCase['id']] = 0; - $outRef['external_id'] = $testCase['tc_external_id']; - $outRef['tcversions_execution_type'][$testCase['id']] = $testCase['execution_type']; - $outRef['importance'][$testCase['id']] = $testCase['importance']; - } - $outRef['execution_order'] = isset($target['execution_order'])? $target['execution_order'] : 0; - if( isset($target['priority']) ) - { - $outRef['priority'] = priority_to_level($target['priority']); - } - $outRef['linked_version_id']= $testCase['id']; - $out[$parent_idx]['write_buttons'] = 'yes'; - $out[$parent_idx]['linked_testcase_qty']++; - $result['has_linked_items'] = 1; - - foreach($linked_testcase as $item) - { - // 20120714 - franciscom - need t check if this info is needed. - if(isset($item['executed']) && (intval($item['executed']) >0) || - isset($item['exec_id']) && (intval($item['exec_id']) >0)) - { - $outRef['executed'][$item['platform_id']]='yes'; - } - - if( isset($item['linked_ts'])) - { - $outRef['linked_ts'][$item['platform_id']]=$item['linked_ts']; - } - - foreach ($optionalIntegerFields as $fieldKey ) - { - if( isset($item[$fieldKey])) - { - $outRef[$fieldKey][$item['platform_id']]=intval($item[$fieldKey]); - } - } - - // this logic has been created to cope with multiple tester assignment - if($my['opt']['useOptionalArrayFields']) - { - - foreach ($optionalArrayFields as $fieldKey ) - { - // We have issues when no user is assigned because is - if(is_array($item[$fieldKey])) - { - // this seems to be the path we follow when trying to work on test suite - $outRef[$fieldKey][$item['platform_id']]=$item[$fieldKey]; - } - else - { - // this seems to be the path we follow when trying to work on SINGLE test case - $outRef[$fieldKey][$item['platform_id']][]=intval($item[$fieldKey]); - } - } - } - } - break; - } - } - } - } //foreach - - // Again DAMM 0!! - if( !is_null($out[$firstElemIDX]) ) - { - $result['spec_view'] = $out; - } - return $result; -} - -/** - * - * changed return type when there are no platforms - */ -function getPlatforms($db,$tproject_id,$testplan_id) -{ - $platform_mgr = new tlPlatform($db, $tproject_id); - - if (is_null($testplan_id)) { - $opx = array('enable_on_design' => null, - 'enable_on_execution' => true); - $platforms = $platform_mgr->getAll($opx); - } else { - $platforms = $platform_mgr->getLinkedToTestplan($testplan_id); - } - - if (is_null($platforms)) { - // need to create fake data for platform 0 in order - // to have only simple logic - // $platforms= array( 'id' => 0, 'name' => ''); - $platforms[0] = array( 'id' => 0, 'name' => ''); - } - return $platforms; -} - -/** - * - */ -function getFilteredSpecViewFlat(&$dbHandler, &$argsObj, &$tplanMgr, &$tcaseMgr, $filters=null, $options=null) -{ - $tprojectMgr = new testproject($dbHandler); - $tsuite_data = $tcaseMgr->tree_manager->get_node_hierarchy_info($argsObj->id); - - $my = array(); // some sort of local scope - $my['filters'] = array('keywordsFilter' => null, - 'testcaseFilter' => null, - 'assignedToFilter' => null, - 'executionTypeFilter' => null, - 'platformsFilter' => null,); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $my['options'] = array('write_button_only_if_linked' => 1, - 'prune_unlinked_tcversions' => 1); - $my['options'] = array_merge($my['options'],(array)$options); - - - // This does filter on keywords ALWAYS in OR mode. - $tplan_linked_tcversions = - getFilteredLinkedVersions($dbHandler,$argsObj,$tplanMgr, $tcaseMgr, $options); - - // With these pieces we implement the AND type of keyword filter. - $testCaseSet = null; - $tryNextFilter = true; - $filterApplied = false; - if(!is_null($my['filters']['keywordsFilter']) && !is_null($my['filters']['keywordsFilter']->items)) { - $keywordsTestCases = $tprojectMgr->getKeywordsLatestTCV( - $argsObj->tproject_id, - $my['filters']['keywordsFilter']->items, - $my['filters']['keywordsFilter']->type); - - $testCaseSet = array_keys((array)$keywordsTestCases); - $tryNextFilter = !is_null($testCaseSet); - $filterApplied = true; - } - - if( $tryNextFilter && !is_null($my['filters']['testcaseFilter'])) { - $filterApplied = true; - if( is_null($testCaseSet) ) { - $testCaseSet = $my['filters']['testcaseFilter']; - } else { - // wrong use of array() instead of (array) - $testCaseSet = array_intersect($testCaseSet, (array)$my['filters']['testcaseFilter']); - } - } - - // when $testCaseSet is null because we have applied filters - // => we do not need to call other - // method because we know we are going to get NOTHING - $testCaseSet = !is_null($testCaseSet) ? array_combine($testCaseSet, $testCaseSet) : null; - if ($filterApplied && is_null($testCaseSet)) { - return null; - } - - $genSpecFilters = array('keywords' => $argsObj->keyword_id, - 'testcases' => $testCaseSet, - 'exec_type' => $my['filters']['executionTypeFilter'], - 'cfields' => null, - 'platforms' => $argsObj->platform_id); - - echo '
    ';
    -  var_dump($genSpecFilters);
    -  echo '
    '; - - if (isset($my['filters']['cfieldsFilter'])) { - $genSpecFilters['cfields'] = $my['filters']['cfieldsFilter']; - } - - $out = genSpecViewFlat($dbHandler, 'testplan', - $argsObj->tplan_id, $argsObj->id, - $tsuite_data['name'], - $tplan_linked_tcversions, null, - $genSpecFilters, $my['options']); - return $out; -} - -/** - * - */ -function genSpecViewFlat(&$db, $spec_view_type='testproject', $tobj_id, $id, $name, &$linked_items, - $map_node_tccount, $filters=null, $options = null, $tproject_id = null) -{ - - $out = array(); - $result = array('spec_view'=>array(), 'num_tc' => 0, 'has_linked_items' => 0); - - $my = array(); - $my['options'] = array('write_button_only_if_linked' => 0,'prune_unlinked_tcversions' => 0, - 'add_custom_fields' => 0) + (array)$options; - - $my['filters'] = array('keywords' => 0, 'testcases' => null ,'exec_type' => null, - 'importance' => null, 'cfields' => null); - foreach( $my as $key => $settings) - { - if( !is_null($$key) && is_array($$key) ) - { - $my[$key] = array_merge($my[$key],$$key); - } - } - - $write_status = $my['options']['write_button_only_if_linked'] ? 'no' : 'yes'; - $is_tplan_view_type=$spec_view_type == 'testplan' ? 1 : 0; - $is_uncovered_view_type = ($spec_view_type == 'uncoveredtestcases') ? 1 : 0; - - if( !$is_tplan_view_type && is_null($tproject_id) ) - { - $tproject_id = $tobj_id; - } - - $testplan_id = $is_tplan_view_type ? $tobj_id : null; - - - $tcase_mgr = new testcase($db); - $hash_descr_id = $tcase_mgr->tree_manager->get_available_node_types(); - $hash_id_descr = array_flip($hash_descr_id); - - $key2map = array('keyword_id' => 'keywords', 'tcase_id' => 'testcases', - 'execution_type' => 'exec_type', 'importance' => 'importance', - 'cfields' => 'cfields','tcase_name' => 'tcase_name', - 'status' => 'workflow_status'); - - $pfFilters = array('tcase_node_type_id' => $hash_descr_id['testcase']); - foreach($key2map as $tk => $fk) - { - $pfFilters[$tk] = isset($my['filters'][$fk]) ? $my['filters'][$fk] : null; - } - - $test_spec = getTestSpecFromNode($db,$tcase_mgr,$linked_items,$tobj_id,$id,$spec_view_type,$pfFilters); - - $platforms = getPlatforms($db,$tproject_id,$testplan_id); - $idx = 0; - $a_tcid = array(); - $a_tsuite_idx = array(); - if(count($test_spec)) - { - $cfg = array('node_types' => $hash_id_descr, 'write_status' => $write_status, - 'is_uncovered_view_type' => $is_uncovered_view_type); - - // $a_tsuite_idx - // key: test case version id - // value: index inside $out, where parent test suite of test case version id is located. - // - list($a_tcid,$a_tsuite_idx,$tsuite_tcqty,$out) = buildSkeletonFlat($id,$name,$cfg,$test_spec,$platforms); - } - - // Collect information related to linked testcase versions - // DAMMED 0!!!! - $firtsElemIDX = key($out); - if(!is_null($out) && count($out) > 0 && !is_null($out[$firtsElemIDX]) && count($a_tcid)) - { - $optGBI = array('output' => 'full_without_users', - 'order_by' => " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC "); - - $tcaseVersionSet = $tcase_mgr->get_by_id($a_tcid,testcase::ALL_VERSIONS,null,$optGBI); - $result = addLinkedVersionsInfo($tcaseVersionSet,$a_tsuite_idx,$out,$linked_items,$options); - } - - if( count($result['spec_view']) > 0 && $my['options']['add_custom_fields']) - { - addCustomFieldsToView($result['spec_view'],$tproject_id,$tcase_mgr); - } - // -------------------------------------------------------------------------------------------- - unset($tcase_mgr); - - // with array_values() we reindex array to avoid "holes" - $result['spec_view']= array_values($result['spec_view']); - return $result; -} - - -/** - * - * Developer Notice - * key 'user_id' is JUST initialized - */ -function buildSkeletonFlat($branchRootID,$name,$config,&$test_spec,&$platforms) -{ - $parent_idx=-1; - $pivot_tsuite = $test_spec[0]; - $levelSet = array(); - $tcase_memory = null; - - $node_types = $config['node_types']; - $write_status = $config['write_status']; - $is_uncovered_view_type = $config['is_uncovered_view_type']; - - $out=array(); - $a_tcid = array(); - $a_tsuite_idx = array(); - - $rootIDX = 0; - $hash_id_pos[$branchRootID] = $rootIDX; - $out[$rootIDX]['testsuite'] = array('id' => $branchRootID, 'name' => $name); - $out[$rootIDX]['testcases'] = array(); - $out[$rootIDX]['write_buttons'] = 'no'; - $out[$rootIDX]['testcase_qty'] = 0; - $out[$rootIDX]['level'] = 1; - $out[$rootIDX]['linked_testcase_qty'] = 0; - $out[$rootIDX]['linked_ts'] = null; - $out[$rootIDX]['linked_by'] = 0; - $out[$rootIDX]['priority'] = 0; - - // $familyNames[$branchRootID] = $name; - $nameAtLevel[$out[0]['level']] = $name; - - - $level = $out[0]['level']+1; - $idx = 0; - $idx++; - $tsuite_tcqty=array($branchRootID => 0); - - $rdx = 0; - foreach ($test_spec as $current) - { - // it will be interesting to understand if this can happen due to filtering - if(is_null($current)) - { - continue; - } - - // pivot is updated each time I find a Test Suite. - switch($node_types[$current['node_type_id']]) - { - case 'testsuite': - // $familyNames[$current['id']] = $current['name']; - - - // parent_idx is setted ONLY when a test case is found - // this logic is used just to have test case count inside test suite. - if($parent_idx >= 0) - { - $xdx=$out[$parent_idx]['testsuite']['id']; - $tsuite_tcqty[$xdx]=$out[$parent_idx]['testcase_qty']; - } - - if($pivot_tsuite['parent_id'] != $current['parent_id']) - { - // echo 'May be we are doing one Step Down Walking on Tree? Let\'s check - ' . __LINE__ . '
    '; - if ($pivot_tsuite['id'] == $current['parent_id']) - { - // echo 'Yes!, we are stepping down and ...
    '; - // echo 'Luke I\'m Your FATHER
    '; - $level++; - $levelSet[$current['parent_id']] = $level; - } - else - { - // echo 'Oops!. What will be next level ? UP or Down?'; - $level = $levelSet[$current['parent_id']]; - } - $nameAtLevel[$level] = $current['name']; - } - else - { - $nameAtLevel[$level] = $current['name']; - } - - - $whoiam = ''; - for($ldx=$out[$rootIDX]['level']; $ldx <= $level; $ldx++) - { - $whoiam .= $nameAtLevel[$ldx] . '/'; - } - // echo 'What is my name NOW? -> ' . $whoiam .'
    '; - - $out[$idx]['testsuite']=array('id' => $current['id'], 'name' => $whoiam); - $out[$idx]['testcases'] = array(); - $out[$idx]['testcase_qty'] = 0; - $out[$idx]['linked_testcase_qty'] = 0; - $out[$idx]['level'] = $level; - $out[$idx]['write_buttons'] = 'no'; - $hash_id_pos[$current['id']] = $idx; - $idx++; - - // update pivot. - $levelSet[$current['parent_id']] = $level; - $pivot_tsuite = $current; - break; - - case 'testcase': - break; - - } - - // In some situations during processing of testcase, a change of parent can - // exists, then we need to update $tsuite_tcqty - if($node_types[$current['node_type_id']] == "testcase") - { - $tc_id = $current['id']; - $parent_idx = $hash_id_pos[$current['parent_id']]; - $a_tsuite_idx[$tc_id] = $parent_idx; - $out[$parent_idx]['testcases'][$tc_id] = array('id' => $tc_id,'name' => $current['name']); - - // Reference to make code reading more human friendly - $outRef = &$out[$parent_idx]['testcases'][$tc_id]; - - if($is_uncovered_view_type) - { - // @TODO understand impacts of platforms - $outRef['external_id'] = $test_spec[$tc_id]['external_id']; - } - else - { - $out[$parent_idx]['write_buttons'] = $write_status; - $out[$parent_idx]['linked_testcase_qty'] = 0; - - $outRef['tcversions'] = array(); - $outRef['tcversions_active_status'] = array(); - $outRef['tcversions_execution_type'] = array(); - $outRef['tcversions_qty'] = 0; - $outRef['linked_version_id'] = 0; - $outRef['executed'] = null; // 'no'; - - // useful for tc_exec_assignment.php - $outRef['platforms'] = $platforms; - $outRef['feature_id'] = null; //0; - $outRef['linked_by'] = null; //0; - $outRef['linked_ts'] = null; - $outRef['priority'] = 0; - $outRef['user_id'] = array(); - } - $out[$parent_idx]['testcase_qty']++; - $a_tcid[] = $current['id']; - - // This piece is needed initialize in right way $tsuite_tcqty - // in this kind of situation, for SubSuite2 - // - // Tsuite 1 - // |__ SubSuite1 - // | |__TCX1 - // | |__TCX2 - // | - // |__ SubSuite2 - // | |__TCY1 - // | |__TCY2 - // | - // |__ TCZ1 - // - // - if( $tcase_memory['parent_id'] != $current['parent_id'] ) - { - if( !is_null($tcase_memory) ) - { - $pidx = $hash_id_pos[$tcase_memory['parent_id']]; - $xdx=$out[$pidx]['testsuite']['id']; - $tsuite_tcqty[$xdx]=$out[$pidx]['testcase_qty']; - } - $tcase_memory=$current; - } - } - } // foreach - - // Update after finished loop - if($parent_idx >= 0) - { - $xdx=$out[$parent_idx]['testsuite']['id']; - $tsuite_tcqty[$xdx]=$out[$parent_idx]['testcase_qty']; - } - - unset($tcase_memory); - $tsuite_tcqty[$branchRootID] = $out[$hash_id_pos[$branchRootID]]['testcase_qty']; - - // Clean up - $loop2do = count($out); - $toUnset = null; - for($lzx=0; $lzx < $loop2do; $lzx++) - { - if(count($out[$lzx]['testcases']) == 0) - { - $toUnset[$lzx]=$lzx; - } - } - if(!is_null($toUnset)) - { - foreach($toUnset as $kill) - { - unset($out[$kill]); - } - } - return array($a_tcid,$a_tsuite_idx,$tsuite_tcqty,$out); + 2732 + * [tc_id] => 2733 + * [z] => 100 ---> nodes_hierarchy.order + * [name] => TC1 + * [tcversion_id] => 2734 + * [feature_id] => 9 --->> testplan_tcversions.ID + * [execution_order] => 10 + * [version] => 1 + * [active] => 1 + * [external_id] => 1 + * [exec_id] => 1 + * [tcversion_number] => 1 + * [executed] => 2734 + * [exec_on_tplan] => 2735 + * [user_id] => + * [type] => + * [status] => + * [assigner_id] => + * [urgency] => 2 IMPORTANT: exists ONLY FOR LINKED TEST CASES + * [exec_status] => b + * [priority] => 4 // urgency*importance IMPORTANT: exists ONLY FOR LINKED TEST CASES + * + * @param array $map_node_tccount + * @todo probably this argument ($map_node_tccount) is not needed, but it will depend + * of how this feature (gen_spec_view) will be used on other TL areas. + * + * @param array $filters + * keys + * [keyword_id] default 0 + * [tcase_id] default null, can be an array + * + * @param array $options + * keys + * [write_button_only_if_linked] default 0 + * [prune_unlinked_tcversions]: default 0. + * Useful when working on spec_view_type='testplan'. + * 1 -> will return only linked tcversion + * 0 -> returns all test cases specs. + * [add_custom_fields]: default=0 + * useful when working on spec_view_type='testproject' + * when doing test case assign to test plans. + * 1 -> for every test case cfields of area 'testplan_design' + * will be fetched and displayed. + * 0 -> do nothing + * + * [$tproject_id]: default = null + * useful to improve performance in custom field method calls + * when add_custom_fields=1. + * + * + * @return array every element is an associative array with the following + * structure: (to get last updated info add debug code and print_r returned value) + * [testsuite] => Array( [id] => 28 + * [name] => TS1 ) + * [testcases] => Array( [2736] => Array + * ( + * [id] => 2736 + * [name] => TC2 + * [tcversions] => Array + * ( + * [2738] => 2 // key=tcversion id,value=version + * [2737] => 1 + * ) + * [tcversions_active_status] => Array + * ( + * [2738] => 1 // key=tcversion id,value=active status + * [2737] => 1 + * ) + * [tcversions_execution_type] => Array + * ( + * [2738] => 1 + * [2737] => 2 + * ) + * [tcversions_qty] => 2 + * [linked_version_id] => 2737 + * [executed] => no + * [user_id] => 0 ---> !=0 if execution has been assigned + * [feature_id] => 12 ---> testplan_tcversions.id + * [execution_order] => 20 + * [external_id] => 2 + * [linked_ts] => 2009-06-10 23:00 + * [linked_by] => 2 + * [priority] => HIGH, MEDIUM or LOW + * ) + * [81] => Array( [id] => 81 + * [name] => TC88) + * ... + * ) + * [level] = + * [write_buttons] => yes or no + * level and write_buttons are used to generate the user interface + * + * Warning: + * if the root element of the spec_view, has 0 test => then the default + * structure is returned ( $result = array('spec_view'=>array(), 'num_tc' => 0)) + * + * + */ +function gen_spec_view(&$db, $specViewType, $tobj_id, $id, $name, &$linked_items, + $map_node_tccount, $filters = null, $options = null, $tproject_id = null) +{ + $spec_view_type = is_null($specViewType) ? 'testproject' : $specViewType; + $out = array(); + $result = array( + 'spec_view' => array(), + 'num_tc' => 0, + 'has_linked_items' => 0 + ); + + $my = array(); + $my['options'] = array( + 'write_button_only_if_linked' => 0, + 'prune_unlinked_tcversions' => 0, + 'add_custom_fields' => 0 + ) + (array) $options; + + $my['filters'] = array( + 'keywords' => 0, + 'testcases' => null, + 'exec_type' => null, + 'importance' => null, + 'cfields' => null, + 'platforms' => null + ); + + foreach ($my as $key => $settings) { + if (! is_null($$key) && is_array($$key)) { + $my[$key] = array_merge($my[$key], $$key); + } + } + + $write_status = $my['options']['write_button_only_if_linked'] ? 'no' : 'yes'; + $is_tplan_view_type = $spec_view_type == 'testplan' ? 1 : 0; + $is_uncovered_view_type = ($spec_view_type == 'uncoveredtestcases') ? 1 : 0; + + if (! $is_tplan_view_type && is_null($tproject_id)) { + $tproject_id = $tobj_id; + } + + $testplan_id = $is_tplan_view_type ? $tobj_id : null; + + $tcaseMgr = new testcase($db); + $hash_descr_id = $tcaseMgr->tree_manager->get_available_node_types(); + $hash_id_descr = array_flip($hash_descr_id); + + $key2map = array( + 'keyword_id' => 'keywords', + 'tcase_id' => 'testcases', + 'execution_type' => 'exec_type', + 'importance' => 'importance', + 'cfields' => 'cfields', + 'tcase_name' => 'tcase_name', + 'status' => 'workflow_status', + 'platform_id' => 'platforms' + ); + + $pfFilters = array( + 'tcase_node_type_id' => $hash_descr_id['testcase'] + ); + foreach ($key2map as $tk => $fk) { + $pfFilters[$tk] = isset($my['filters'][$fk]) ? $my['filters'][$fk] : null; + } + + // transform in array to be gentle with getTestSpecFromNode() + $t2a = array( + 'importance', + 'status' + ); + foreach ($t2a as $tortuga) { + if (! is_null($pfFilters[$tortuga])) { + $pfFilters[$tortuga] = (array) $pfFilters[$tortuga]; + } + } + + $test_spec = getTestSpecFromNode($db, $tcaseMgr, $linked_items, $tobj_id, + $id, $spec_view_type, $pfFilters); + + $platforms = getPlatforms($db, $tproject_id, $testplan_id); + $a_tcid = array(); + $a_tsuite_idx = array(); + if (count($test_spec)) { + $cfg = array( + 'node_types' => $hash_id_descr, + 'write_status' => $write_status, + 'is_uncovered_view_type' => $is_uncovered_view_type + ); + + // $a_tsuite_idx + // key: test case version id + // value: index inside $out, where parent test suite of test case version id is located. + // + list ($a_tcid, $a_tsuite_idx, $tsuite_tcqty, $out) = buildSkeleton($id, + $name, $cfg, $test_spec, $platforms); + } + + // This code has been replace (see below on Remove empty branches) + // Once we have created array with testsuite and children testsuites + // we are trying to remove nodes that has 0 test case count. + // May be this can be done (as noted by schlundus during performance + // analisys done on october 2008) in a better way, or better can be absolutely avoided. + // + // This process is needed to prune whole branches that are empty + // Need to look for every call in TL and understand if this can be removed + // + if (! is_null($map_node_tccount)) { + foreach ($out as $key => $elem) { + if (isset($map_node_tccount[$elem['testsuite']['id']]) && + $map_node_tccount[$elem['testsuite']['id']]['testcount'] == 0) { + // why not unset ? + $out[$key] = null; + } + } + } + + // Collect information related to linked testcase versions + if (! empty($out) && ! is_null($out[0]) && count($a_tcid)) { + $optGBI = array( + 'output' => 'full_without_users', + 'order_by' => " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC " + ); + + $tcaseVersionSet = $tcaseMgr->get_by_id($a_tcid, testcase::ALL_VERSIONS, + null, $optGBI); + $result = addLinkedVersionsInfo($tcaseVersionSet, $a_tsuite_idx, $out, + $linked_items, $options); + } + + // Try to prune empty test suites, to reduce memory usage and + // to remove elements + // that do not need to be displayed on user interface. + if (! empty($result['spec_view'])) { + removeEmptyTestSuites($result['spec_view'], $tcaseMgr->tree_manager, + ($my['options']['prune_unlinked_tcversions'] && $is_tplan_view_type), + $hash_descr_id); + } + + // Remove empty branches + // Loop to compute test case qty ($tsuite_tcqty) on every level and prune test suite branchs that are empty + if (! empty($result['spec_view'])) { + removeEmptyBranches($result['spec_view'], $tsuite_tcqty); + } + + /** + * + * @TODO: maybe we can integrate this into already present loops above? + */ + // This is not right condition for identifing an empty test suite for the porpouse + // of gen_spec_view(), because for following structure + // TS1 + // \--- TS2 + // \--TC1 + // \--TC2 + // + // \--- TS3 + // \-- TXX + // + // When we are displaying a Test Specification we want to see previous structure + // But if we apply this criteria for empty test suite, TS1 results empty and will + // be removed -> WRONG + // + // Need to understand when this feature will be needed and then reimplement + // + // if ($prune_empty_tsuites) + // { + // foreach($result['spec_view'] as $key => $value) + // { + // if(is_null($value) || !isset($value['testcases']) || !count($value['testcases'])) + // unset($result['spec_view'][$key]); + // } + // } + + // #1650 We want to manage custom fields when user is doing test case execution assigment + if (! empty($result['spec_view']) && $my['options']['add_custom_fields']) { + addCustomFieldsToView($result['spec_view'], $tproject_id, $tcaseMgr); + } + unset($tcaseMgr); + + // with array_values() we reindex array to avoid "holes" + $result['spec_view'] = array_values($result['spec_view']); + return $result; +} + +/** + */ +function gen_coverage_view(&$db, $specViewType, $tobj_id, $id, $name, + &$linked_items, $map_node_tccount, $filters = null, $options = null, + $tproject_id = null) +{ + $spec_view_type = is_null($specViewType) ? 'testproject' : $specViewType; + + $out = array(); + $result = array( + 'spec_view' => array(), + 'num_tc' => 0, + 'has_linked_items' => 0 + ); + + $my = array(); + $my['options'] = array( + 'write_button_only_if_linked' => 0, + 'prune_unlinked_tcversions' => 0, + 'add_custom_fields' => 0 + ) + (array) $options; + + $my['filters'] = array( + 'keywords' => 0, + 'testcases' => null, + 'exec_type' => null, + 'importance' => null, + 'cfields' => null + ); + foreach ($my as $key => $settings) { + if (! is_null($$key) && is_array($$key)) { + $my[$key] = array_merge($my[$key], $$key); + } + } + + $write_status = $my['options']['write_button_only_if_linked'] ? 'no' : 'yes'; + $is_tplan_view_type = $spec_view_type == 'testplan' ? 1 : 0; + $is_uncovered_view_type = ($spec_view_type == 'uncoveredtestcases') ? 1 : 0; + + if (! $is_tplan_view_type && is_null($tproject_id)) { + $tproject_id = $tobj_id; + } + + $testplan_id = $is_tplan_view_type ? $tobj_id : null; + + $tcaseMgr = new testcase($db); + $hash_descr_id = $tcaseMgr->tree_manager->get_available_node_types(); + $hash_id_descr = array_flip($hash_descr_id); + + $key2map = array( + 'keyword_id' => 'keywords', + 'tcase_id' => 'testcases', + 'execution_type' => 'exec_type', + 'importance' => 'importance', + 'cfields' => 'cfields', + 'tcase_name' => 'tcase_name' + ); + + $pfFilters = array( + 'tcase_node_type_id' => $hash_descr_id['testcase'] + ); + foreach ($key2map as $tk => $fk) { + $pfFilters[$tk] = isset($my['filters'][$fk]) ? $my['filters'][$fk] : null; + } + + $test_spec = getTestSpecFromNode($db, $tcaseMgr, $linked_items, $tobj_id, + $id, $spec_view_type, $pfFilters, 'req_order'); + + $platforms = getPlatforms($db, $tproject_id, $testplan_id); + $a_tcid = array(); + $a_tsuite_idx = array(); + if (count($test_spec)) { + $cfg = array( + 'node_types' => $hash_id_descr, + 'write_status' => $write_status, + 'is_uncovered_view_type' => $is_uncovered_view_type + ); + + // $a_tsuite_idx + // key: test case version id + // value: index inside $out, where parent test suite of test case version id is located. + // + list ($a_tcid, $a_tsuite_idx, , $out) = buildSkeleton($id, $name, $cfg, + $test_spec, $platforms); + } + + // This code has been replace (see below on Remove empty branches) + // Once we have created array with testsuite and children testsuites + // we are trying to remove nodes that has 0 test case count. + // May be this can be done (as noted by schlundus during performance + // analisys done on october 2008) in a better way, or better can be absolutely avoided. + // + // This process is needed to prune whole branches that are empty + // Need to look for every call in TL and understand if this can be removed + // + if (! is_null($map_node_tccount)) { + foreach ($out as $key => $elem) { + if (isset($map_node_tccount[$elem['testsuite']['id']]) && + $map_node_tccount[$elem['testsuite']['id']]['testcount'] == 0) { + $out[$key] = null; + } + } + } + + // Collect information related to linked testcase versions + if (! empty($out) && ! is_null($out[0]) && count($a_tcid)) { + $optGBI = array( + 'output' => 'full_without_users', + 'order_by' => " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC " + ); + + $tcaseVersionSet = $tcaseMgr->get_by_id($a_tcid, testcase::ALL_VERSIONS, + null, $optGBI); + $result = addLinkedVersionsInfo($tcaseVersionSet, $a_tsuite_idx, $out, + $linked_items); + } + + /** + * + * @TODO: maybe we can integrate this into already present loops above? + */ + // This is not right condition for identifing an empty test suite for the porpouse + // of gen_spec_view(), because for following structure + // TS1 + // \--- TS2 + // \--TC1 + // \--TC2 + // + // \--- TS3 + // \-- TXX + // + // When we are displaying a Test Specification we want to see previous structure + // But if we apply this criteria for empty test suite, TS1 results empty and will + // be removed -> WRONG + // + // Need to understand when this feature will be needed and then reimplement + // + // if ($prune_empty_tsuites) + // { + // foreach($result['spec_view'] as $key => $value) + // { + // if(is_null($value) || !isset($value['testcases']) || !count($value['testcases'])) + // unset($result['spec_view'][$key]); + // } + // } + + // #1650 We want to manage custom fields when user is doing test case execution assigment + if (count($result['spec_view']) > 0 && $my['options']['add_custom_fields']) { + addCustomFieldsToView($result['spec_view'], $tproject_id, $tcaseMgr); + } + // -------------------------------------------------------------------------------------------- + unset($tcaseMgr); + + // with array_values() we reindex array to avoid "holes" + $result['spec_view'] = array_values($result['spec_view']); + return $result; +} + +/** + * get linked versions filtered by Keyword ID + * Filter is done ONLY on attributes THAT ARE COMMON to ALL test case versions, + * because (till now) while adding/removing test cases user works on Test Spec Tree + * and filter applied to this tree acts on: + * + * 1. attributes COMMON to all versions + * 2. attributes present ON LAST ACTIVE version. + * + * But do no make considerations regarding versions linked to test plan + * DEV NOTE: may be this has to be changed in future ? + * + * @param database $dbHandler: + * @param stdClass $argsObj: + * stdClass object with information about filters + * @param testplan $tplanMgr: + * test plan manager object + * @param testcase $tcaseMgr: + * test case manager object + * @param array $options: + * default null (at today 20110820 seems not be used). + * @param boolean $isTestSuite: + * filter testGroupBy, default true + * + */ +function getFilteredLinkedVersions(&$dbHandler, &$argsObj, &$tplanMgr, + &$tcaseMgr, $options = null, $isTestSuite = true) +{ + static $tsuite_mgr; + $doFilterByKeyword = (! is_null($argsObj->keyword_id) && + $argsObj->keyword_id > 0) ? true : false; + + // Multiple step algoritm to apply keyword filter on type=AND + // get_*_tcversions filters by keyword ALWAYS in OR mode. + // + $filters = array( + 'keyword_id' => $argsObj->keyword_id, + 'platform_id' => null + ); + if (property_exists($argsObj, 'control_panel') && + isset($argsObj->control_panel['setting_platform']) && + intval($argsObj->control_panel['setting_platform']) > 0) { + $filters['platform_id'] = intval( + $argsObj->control_panel['setting_platform']); + } + + if (isset($options['assigned_on_build']) && $options['assigned_on_build'] > 0) { + $filters['assigned_on_build'] = $options['assigned_on_build']; + } + + // get test suites in branch to limit search + if ($isTestSuite) { + $itemID = property_exists($argsObj, 'object_id') ? $argsObj->object_id : $argsObj->id; + if (! is_null($itemID)) { + // will get all test suites in this branch, in order to limit amount of data returned by + // get_*_tcversions + if (! $tsuite_mgr) { + $tsuite_mgr = new testsuite($dbHandler); + } + $xx = $tsuite_mgr->get_branch($itemID); + $xx .= ($xx == '') ? $itemID : ',' . $itemID; + $filters['tsuites_id'] = explode(',', $xx); + } + } + + $opx = array_merge( + array( + 'addExecInfo' => true, + 'specViewFields' => true, + 'tlFeature' => 'none' + ), (array) $options); + + switch ($opx['tlFeature']) { + case 'testCaseExecTaskAssignment': + $method2call = 'getLinkedTCVXmen'; + break; + + case 'testCaseTestPlanAssignment': + default: + $method2call = 'getLTCVNewGeneration'; + break; + } + + if (isset($argsObj->testcases_to_show) && + ! is_null($argsObj->testcases_to_show)) { + $filters['tcase_id'] = $argsObj->testcases_to_show; + } + + if (isset($argsObj->platform_id) && $argsObj->platform_id > 0) { + $filters['platform_id'] = $argsObj->platform_id; + } + + $tplan_tcases = $tplanMgr->$method2call($argsObj->tplan_id, $filters, $opx); + + if (! is_null($tplan_tcases) && $doFilterByKeyword && + $argsObj->keywordsFilterType == 'AND') { + $filteredSet = $tcaseMgr->filterByKeyword(array_keys($tplan_tcases), + $argsObj->keyword_id, $argsObj->keywordsFilterType); + + $filters = array( + 'tcase_id' => array_keys($filteredSet) + ); + + // HERE WE CAN HAVE AN ISSUE + $tplan_tcases = $tplanMgr->getLTCVNewGeneration($argsObj->tplan_id, + $filters, $opx); + } + return $tplan_tcases; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj: + * user input + * @param testplan $tplanMgr: + * test plan manager + * @param testcase $tcaseMgr: + * test case manager + * @param array $filters: + * keys keywordsFilter, testcaseFilter,assignedToFilter, + * executionTypeFilter, cfieldsFilter + * + * IMPORTANT NOTICE: not all filters are here, + * other arrive via argsObj + * @param array $options: + * keys ?? + * USED TO PASS options to other method called here + * -> see these method docs. + */ +function getFilteredSpecView(&$dbHandler, &$argsObj, &$tplanMgr, &$tcaseMgr, + $filters = null, $options = null) +{ + $tprojectMgr = new testproject($dbHandler); + $tsuite_data = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $argsObj->id); + + $my = array(); // some sort of local scope + $my['filters'] = array( + 'keywordsFilter' => null, + 'testcaseFilter' => null, + 'assignedToFilter' => null, + 'executionTypeFilter' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $my['options'] = array( + 'write_button_only_if_linked' => 1, + 'prune_unlinked_tcversions' => 1 + ); + $my['options'] = array_merge($my['options'], (array) $options); + + // This does filter on keywords ALWAYS in OR mode. + $tplan_linked_tcversions = getFilteredLinkedVersions($dbHandler, $argsObj, + $tplanMgr, $tcaseMgr, $options); + + // With these pieces we implement the AND type of keyword filter. + $testCaseSet = null; + $tryNextFilter = true; + $filterApplied = false; + if (! is_null($my['filters']['keywordsFilter']) && + ! is_null($my['filters']['keywordsFilter']->items)) { + + $keywordsTestCases = $tprojectMgr->getKeywordsLatestTCV( + $argsObj->tproject_id, $my['filters']['keywordsFilter']->items, + $my['filters']['keywordsFilter']->type); + + $testCaseSet = array_keys((array) $keywordsTestCases); + $tryNextFilter = ! is_null($testCaseSet); + $filterApplied = true; + } + + if ($tryNextFilter && ! is_null($my['filters']['testcaseFilter'])) { + $filterApplied = true; + if (is_null($testCaseSet)) { + $testCaseSet = $my['filters']['testcaseFilter']; + } else { + // wrong use of array() instead of (array) + $testCaseSet = array_intersect($testCaseSet, + (array) $my['filters']['testcaseFilter']); + } + } + + // when $testCaseSet is null because we have + // applied filters => we do not need to call other + // method because we know we are going to get NOTHING + $testCaseSet = ! is_null($testCaseSet) ? array_combine($testCaseSet, + $testCaseSet) : null; + if ($filterApplied && is_null($testCaseSet)) { + return null; + } + + $genSpecFilters = array( + 'keywords' => $argsObj->keyword_id, + 'testcases' => $testCaseSet, + 'exec_type' => $my['filters']['executionTypeFilter'], + 'cfields' => null + ); + + if (isset($my['filters']['cfieldsFilter'])) { + $genSpecFilters['cfields'] = $my['filters']['cfieldsFilter']; + } + + return gen_spec_view($dbHandler, 'testplan', $argsObj->tplan_id, + $argsObj->id, $tsuite_data['name'], $tplan_linked_tcversions, null, + $genSpecFilters, $my['options']); +} + +/** + * get Test Specification data within a Node + * + * using nodeId (that normally is a test suite id) as starting point + * will return subtree that start at nodeId. + * If filters are given, the subtree returned is filtered. + * + * Important Notice regaring keyword filtering + * Keyword filter logic inside this function seems to work ONLY on OR mode. + * Then how the AND mode is implemented ? + * Filter for test case id is used, and the test case set has been generated + * applying AND or OR logic (following user's choice). + * Then seems that logic regarding keywords here, may be can be removed + * + * @param integer $masterContainerId + * can be a Test Project Id, or a Test Plan id. + * is used only if keyword id filter has been specified + * to get all keyword defined on masterContainer. + * + * @param integer $nodeId + * node that will be root of the view we want to build. + * + * @param string $specViewType: + * type of view requested + * + * @param array $filters + * filters['keyword_id']: array of keywords + * filters['tcase_id']: + * filters['execution_type']: + * filters['importance']: + * filters['cfields']: + * filters['tcase_name']: + * + * @param string $type: + * request selected for create tree + * + * @return array map with view (test cases subtree) + * + * + */ +function getTestSpecFromNode(&$dbHandler, &$tcaseMgr, &$linkedItems, + $masterContainerId, $nodeId, $specViewType, $filters, $type = 'spec_order') +{ + $applyFilters = false; + $testCaseSet = null; + $tck_map = null; + $tobj_mgr = new testproject($dbHandler); + + $opt = array( + 'order_cfg' => array( + "type" => $type + ) + ); + if ($specViewType == 'testplan') { + $opt['order_cfg'] = array( + "type" => 'exec_order', + 'tplan_id' => $masterContainerId + ); + } + $test_spec = $tobj_mgr->get_subtree($nodeId, null, $opt); + + $key2loop = null; + + $nullCheckFilter = array( + 'tcase_id' => false, + 'importance' => false, + 'tcase_name' => false, + 'cfields' => false, + 'status' => false + ); + + $zeroNullCheckFilter = array( + 'execution_type' => false + ); + $useFilter = array( + 'keyword_id' => false, + 'platform_id' => false + ) + $nullCheckFilter + $zeroNullCheckFilter; + + $applyFilters = false; + + foreach ($nullCheckFilter as $key => $value) { + $useFilter[$key] = ! is_null($filters[$key]); + $applyFilters = $applyFilters || $useFilter[$key]; + } + + // more specif analisys + if (! empty($filters['status'][0])) { + $useFilter['status'] = $filters['status'][0]; + $applyFilters = true; + $filtersByValue['status'] = array_flip((array) $filters['status']); + } + + if (! empty($filters['importance'][0])) { + $useFilter['importance'] = $filters['importance'][0]; + $applyFilters = true; + $filtersByValue['importance'] = array_flip( + (array) $filters['importance']); + } + + foreach ($zeroNullCheckFilter as $key => $value) { + // need to check for > 0, because for some items 0 has same meaning that null -> no filter + $useFilter[$key] = (! is_null($filters[$key]) && ($filters[$key] > 0)); + $applyFilters = $applyFilters || $useFilter[$key]; + } + + if ($useFilter['tcase_id']) { + $testCaseSet = is_array($filters['tcase_id']) ? $filters['tcase_id'] : array( + $filters['tcase_id'] + ); + } + + if (! is_array($filters['keyword_id'])) { + $filters['keyword_id'] = array( + $filters['keyword_id'] + ); + } + + if (! empty($filters['keyword_id'][0])) { + $useFilter['keyword_id'] = $filters['keyword_id'][0]; + $applyFilters = true; + switch ($specViewType) { + case 'testplan': + $tobj_mgr = new testplan($dbHandler); + $tck_map = $tobj_mgr->getKeywordsLinkedTCVersions( + $masterContainerId, $filters['keyword_id']); + break; + + default: + $tck_map = $tobj_mgr->getKeywordsLatestTCV($masterContainerId, + $filters['keyword_id']); + break; + } + } + + $tcpl_map = null; + if (! empty($filters['platform_id'][0])) { + $useFilter['platforms'] = $filters['platform_id'][0]; + $applyFilters = true; + switch ($specViewType) { + case 'testplan': + $tobj_mgr = new testplan($dbHandler); + $tcpl_map = $tobj_mgr->getPlatformsLinkedTCVersions( + $masterContainerId, $filters['platforms']); + break; + + default: + $tcpl_map = $tobj_mgr->getPlatformsLatestTCV($masterContainerId, + $filters['platforms']); + break; + } + } + + if ($applyFilters) { + $key2loop = array_keys($test_spec); + + // first step: generate list of TEST CASE NODES + $itemSet = null; + foreach ($key2loop as $key) { + if ($test_spec[$key]['node_type_id'] == + $filters['tcase_node_type_id']) { + $itemSet[$test_spec[$key]['id']] = $key; + } + } + $itemKeys = $itemSet; + + foreach ($itemKeys as $key => $tspecKey) { + // case insensitive search + if (($useFilter['keyword_id'] && + ! isset($tck_map[$test_spec[$tspecKey]['id']])) || + ($useFilter['platforms'] && + ! isset($tcpl_map[$test_spec[$tspecKey]['id']])) || + ($useFilter['tcase_id'] && + ! in_array($test_spec[$tspecKey]['id'], $testCaseSet)) || + ($useFilter['tcase_name'] && + (stripos($test_spec[$tspecKey]['name'], $filters['tcase_name']) === + false))) { + $test_spec[$tspecKey] = null; + unset($itemSet[$key]); + } + } + + if (! empty($itemSet) && + ($useFilter['execution_type'] || $useFilter['importance'] || + $useFilter['cfields'] || $useFilter['status'])) { + // This logic can have some Potential Performance ISSUE - 20120619 - fman + $targetSet = array_keys($itemSet); + $options = ($specViewType == 'testPlanLinking') ? array( + 'access_key' => 'testcase_id' + ) : null; + + $getFilters = $useFilter['cfields'] ? array( + 'cfields' => $filters['cfields'] + ) : null; + $s2h = config_get('tplanDesign')->hideTestCaseWithStatusIn; + if (! is_null($s2h)) { + $getFilters['status'] = array( + 'not_in' => array_keys($s2h) + ); + } + + $tcversionSet = $tcaseMgr->get_last_active_version($targetSet, + $getFilters, $options); + + switch ($specViewType) { + case 'testPlanLinking': + // We need to analise linked items and spec + foreach ($targetSet as $key) { + $targetTestCase = isset($tcversionSet[$key]) ? $tcversionSet[$key]['testcase_id'] : null; + + if (is_null($targetTestCase)) { + $test_spec[$itemSet[$key]] = null; + $item = null; + } else { + if (isset($linkedItems[$targetTestCase])) { + $item = current($linkedItems[$targetTestCase]); + } else { + // hmmm, does not understand this logic. + $item = null; + if (isset($test_spec[$itemSet[$targetTestCase]])) { + $item = $tcversionSet[$targetTestCase]; + } + } + } + + if (! is_null($item) && $useFilter['execution_type'] && + ($item['execution_type'] != + $filters['execution_type']) || + $useFilter['importance'] && + (! isset( + $filtersByValue['importance'][$item['importance']])) || + $useFilter['status'] && + (! isset($filtersByValue['status'][$item['status']]))) { + $tspecKey = $itemSet[$targetTestCase]; + $test_spec[$tspecKey] = null; + } + } + break; + + default: + $tcvidSet = array_keys($tcversionSet); + foreach ($tcvidSet as $zx) { + $tcidSet[$tcversionSet[$zx]['testcase_id']] = $zx; + } + + $options = null; + $doFilter = true; + $allowedSet = null; + $emptySet = false; + + // a first clean will not be bad, ok may be we are going to do more + // loops that needed, but think logic will be more clear + // (at least @20130426 is a little bit confusing ;) ) + foreach ($targetSet as $key) { + if (! isset($tcidSet[$key])) { + $test_spec[$itemSet[$key]] = null; + } + } + + if ($useFilter['execution_type']) { + // Potential Performance ISSUE + $allowedSet = $tcaseMgr->filter_tcversions_by_exec_type( + $tcvidSet, $filters['execution_type'], $options); + + $doFilter = (! empty($allowedSet)); + $emptySet = ! $doFilter; + } + + if ((! $emptySet) && $doFilter) { + // Add another filter on cascade mode + // @20130426 - seems we are applying TWICE the Custom Fields Filter + // because we have applied it before on: + // $tcversionSet = $tcaseMgr->get_last_active_version() + if ($useFilter['cfields']) { + $filteredSet = (! empty($allowedSet)) ? array_keys( + $allowedSet) : $tcvidSet; + $dummySet = $tcaseMgr->filter_tcversions_by_cfields( + $filteredSet, $filters['cfields'], $options); + + // transform to make compatible with filter_tcversions_by_exec_type() return type + if (! empty($dummySet)) { + $allowedSet = null; + $work2do = array_keys($dummySet); + foreach ($work2do as $wkey) { + $allowedSet[$wkey] = $dummySet[$wkey][0]; + } + unset($dummySet); + } + } + $doFilter = (! empty($allowedSet)); + $emptySet = ! $doFilter; + } + + if ($doFilter && ! empty($allowedSet)) { + foreach ($allowedSet as $value) { + $tspecKey = $itemSet[$value['testcase_id']]; + $test_spec[$tspecKey]['version'] = $value['version']; + } + reset($allowedSet); + } + + if ($emptySet) { + $test_spec = null; + } + + $setToRemove = array_diff_key($tcversionSet, $allowedSet); + if (! empty($setToRemove)) { + foreach ($setToRemove as $value) { + $tspecKey = $itemSet[$value['testcase_id']]; + $test_spec[$tspecKey] = null; + } + } + break; + } + } + } + return $test_spec; +} + +/** + * remove empty Test Suites + * + * @param array $testSuiteSet + * reference to set to analyse and clean. + * @param tree $treeMgr + * reference to object + * @param array $pruneUnlinkedTcversions + * useful when working on test plans + * @param array $nodeTypes + * hash key: node type description, value: code + */ +function removeEmptyTestSuites(&$testSuiteSet, &$treeMgr, + $pruneUnlinkedTcversions, $nodeTypes) +{ + foreach ($testSuiteSet as $key => $value) { + // We will remove test suites that meet the empty conditions: + // - do not contain other test suites OR + // - do not contain test cases + if (is_null($value)) { + unset($testSuiteSet[$key]); + } elseif ($pruneUnlinkedTcversions && + (isset($value['testcase_qty']) && $value['testcase_qty'] > 0)) { + // only linked tcversion must be returned, but this analisys must be done + // for test suites that has test cases. + if (isset($value['linked_testcase_qty']) && + $value['linked_testcase_qty'] == 0) { + unset($testSuiteSet[$key]); + } else { + // Only if test suite has children test cases we need to understand + // if they are linked or not + if (! empty($value['testcases'])) { + foreach ($value['testcases'] as $skey => $svalue) { + if ($svalue['linked_version_id'] == 0) { + unset($testSuiteSet[$key]['testcases'][$skey]); + } + } + } + } + } else { + // list of children test suites if useful on smarty template, in order + // to draw nested div. + $tsuite_id = $value['testsuite']['id']; + $testSuiteSet[$key]['children_testsuites'] = $treeMgr->get_subtree_list( + $tsuite_id, $nodeTypes['testsuite']); + + if ($value['testcase_qty'] == 0 && + $testSuiteSet[$key]['children_testsuites'] == '') { + unset($testSuiteSet[$key]); + } + } + } +} + +/** + */ +function removeEmptyBranches(&$testSuiteSet, &$tsuiteTestCaseQty) +{ + foreach ($testSuiteSet as $key => $elem) { + $tsuite_id = $elem['testsuite']['id']; + + if (! isset($tsuiteTestCaseQty[$tsuite_id])) { + $tsuiteTestCaseQty[$tsuite_id] = 0; + } + + if (isset($elem['children_testsuites']) && + $elem['children_testsuites'] != '') { + $children = explode(',', $elem['children_testsuites']); + foreach ($children as $access_id) { + if (isset($tsuiteTestCaseQty[$access_id])) { + $tsuiteTestCaseQty[$tsuite_id] += $tsuiteTestCaseQty[$access_id]; + } + } + } + + if ($tsuiteTestCaseQty[$tsuite_id] == 0) { + unset($testSuiteSet[$key]); + } + } +} + +// function end + +/** + * + * @param + * array &$testSuiteSet: changes will be done to this array to add custom fields info. + * Custom field info will be indexed by platform id + * @param integer $tprojectId + * @param + * testcase &$tcaseMgr reference to testCase class instance + * @internal revisions + * 20100119 - franciscom - start fixing missing platform refactoring + * + */ +function addCustomFieldsToView(&$testSuiteSet, $tprojectId, &$tcaseMgr) +{ + // Important: + // testplan_tcversions.id value, that is used to link to manage custom fields that are used + // during testplan_design is present on key 'feature_id' (only is linked_version_id != 0) + foreach ($testSuiteSet as $key => $value) { + if (! is_null($value) && isset($value['testcases']) && + ! empty($value['testcases'])) { + foreach ($value['testcases'] as $skey => $svalue) { + if (($linked_version_id = $svalue['linked_version_id']) > 0) { + $platformSet = array_keys($svalue['feature_id']); + foreach ($platformSet as $platform_id) { + $testSuiteSet[$key]['testcases'][$skey]['custom_fields'][$platform_id] = ''; + if ($linked_version_id != 0) { + $cf_name_suffix = "_" . + $svalue['feature_id'][$platform_id]; + $cf_map = $tcaseMgr->html_table_of_custom_field_inputs( + $linked_version_id, null, 'testplan_design', + $cf_name_suffix, + $svalue['feature_id'][$platform_id], null, + $tprojectId); + $testSuiteSet[$key]['testcases'][$skey]['custom_fields'][$platform_id] = $cf_map; + } + } + } + } + } + } +} + +/** + * Developer Notice + * key 'user_id' is JUST initialized + */ +function buildSkeleton($id, $name, $config, &$test_spec, &$platforms) +{ + $parent_idx = - 1; + $pivot_tsuite = $test_spec[0]; + $level = array(); + $tcase_memory = null; + + $node_types = $config['node_types']; + $write_status = $config['write_status']; + $is_uncovered_view_type = $config['is_uncovered_view_type']; + + $out = array(); + $idx = 0; + $a_tcid = array(); + $a_tsuite_idx = array(); + $hash_id_pos[$id] = $idx; + $out[$idx]['testsuite'] = array( + 'id' => $id, + 'name' => $name + ); + $out[$idx]['testcases'] = array(); + $out[$idx]['write_buttons'] = 'no'; + $out[$idx]['testcase_qty'] = 0; + $out[$idx]['level'] = 1; + $out[$idx]['linked_testcase_qty'] = 0; + $out[$idx]['linked_ts'] = null; + $out[$idx]['linked_by'] = 0; + $out[$idx]['priority'] = 0; + + $idx ++; + $tsuite_tcqty = array( + $id => 0 + ); + + foreach ($test_spec as $current) { + if (is_null($current)) { + continue; + } + // In some situations during processing of testcase, a change of parent can + // exists, then we need to update $tsuite_tcqty + if ($node_types[$current['node_type_id']] == "testcase") { + $tc_id = $current['id']; + $parent_idx = $hash_id_pos[$current['parent_id']]; + $a_tsuite_idx[$tc_id] = $parent_idx; + $out[$parent_idx]['testcases'][$tc_id] = array( + 'id' => $tc_id, + 'name' => $current['name'] + ); + + // Reference to make code reading more human friendly + $outRef = &$out[$parent_idx]['testcases'][$tc_id]; + + if ($is_uncovered_view_type) { + // @TODO understand impacts of platforms + $outRef['external_id'] = $test_spec[$tc_id]['external_id']; + } else { + $out[$parent_idx]['write_buttons'] = $write_status; + $out[$parent_idx]['linked_testcase_qty'] = 0; + + $outRef['tcversions'] = array(); + $outRef['tcversions_active_status'] = array(); + $outRef['tcversions_execution_type'] = array(); + $outRef['tcversions_qty'] = 0; + $outRef['linked_version_id'] = 0; + $outRef['executed'] = null; + + // useful for tc_exec_assignment.php + $outRef['platforms'] = $platforms; + $outRef['feature_id'] = null; + $outRef['linked_by'] = null; + $outRef['linked_ts'] = null; + $outRef['priority'] = 0; + $outRef['user_id'] = array(); + } + $out[$parent_idx]['testcase_qty'] ++; + $a_tcid[] = $current['id']; + + // This piece is needed initialize in right way $tsuite_tcqty + // in this kind of situation, for SubSuite2 + // + // Tsuite 1 + // |__ SubSuite1 + // | |__TCX1 + // | |__TCX2 + // | + // |__ SubSuite2 + // | |__TCY1 + // | |__TCY2 + // | + // |__ TCZ1 + // + if ($tcase_memory['parent_id'] != $current['parent_id']) { + if (! is_null($tcase_memory)) { + $pidx = $hash_id_pos[$tcase_memory['parent_id']]; + $xdx = $out[$pidx]['testsuite']['id']; + $tsuite_tcqty[$xdx] = $out[$pidx]['testcase_qty']; + } + $tcase_memory = $current; + } + } else { + // This node is a Test Suite + $the_level = 0; + if ($parent_idx >= 0) { + $xdx = $out[$parent_idx]['testsuite']['id']; + $tsuite_tcqty[$xdx] = $out[$parent_idx]['testcase_qty']; + } + + if ($pivot_tsuite['parent_id'] != $current['parent_id']) { + if ($pivot_tsuite['id'] == $current['parent_id']) { + $the_level ++; + $level[$current['parent_id']] = $the_level; + } else { + if (isset($level[$current['parent_id']])) { + $the_level = $level[$current['parent_id']]; + } + } + } + $out[$idx]['testsuite'] = array( + 'id' => $current['id'], + 'name' => $current['name'] + ); + $out[$idx]['testcases'] = array(); + $out[$idx]['testcase_qty'] = 0; + $out[$idx]['linked_testcase_qty'] = 0; + $out[$idx]['level'] = $the_level; + $out[$idx]['write_buttons'] = 'no'; + $hash_id_pos[$current['id']] = $idx; + $idx ++; + + // update pivot. + $level[$current['parent_id']] = $the_level; + $pivot_tsuite = $current; + } + } // foreach + + // Update after finished loop + if ($parent_idx >= 0) { + $xdx = $out[$parent_idx]['testsuite']['id']; + $tsuite_tcqty[$xdx] = $out[$parent_idx]['testcase_qty']; + } + + unset($tcase_memory); + $tsuite_tcqty[$id] = $out[$hash_id_pos[$id]]['testcase_qty']; + return array( + $a_tcid, + $a_tsuite_idx, + $tsuite_tcqty, + $out + ); +} + +/** + * VERY IMPORTANT NOTICE + * + * You can be a little bit confused regarding What will be returned on 'testcases' =>[]['tcversions'] + * You will see JUST ON tcversion with active status = 0, ONLY if the version is LINKED to test plan. + * Otherwise you will get ONLY ACTIVE test case versions. + * + * + * @internal revisions: + */ +function addLinkedVersionsInfo($testCaseVersionSet, $a_tsuite_idx, &$out, + &$linked_items, $opt = null) +{ + $my['opt'] = array( + 'useOptionalArrayFields' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $tcStatus2exclude = config_get('tplanDesign')->hideTestCaseWithStatusIn; + $optionalIntegerFields = array( + 'feature_id', + 'linked_by' + ); + $optionalArrayFields = array( + 'user_id' + ); + + $result = array( + 'spec_view' => array(), + 'num_tc' => 0, + 'has_linked_items' => 0 + ); + $pivot_id = - 1; + $firstElemIDX = key($out); + + foreach ($testCaseVersionSet as $testCase) { + $tc_id = $testCase['testcase_id']; + + // Needed when having multiple platforms + if ($pivot_id != $tc_id) { + $pivot_id = $tc_id; + $result['num_tc'] ++; + } + $parent_idx = $a_tsuite_idx[$tc_id]; + + // Reference to make code reading more human friendly + $outRef = &$out[$parent_idx]['testcases'][$tc_id]; + + // Is not clear (need explanation) why we process in this part ONLY ACTIVE + // also we need to explain !is_null($out[$parent_idx]) + // + if ($testCase['active'] == 1 && + ! isset($tcStatus2exclude[$testCase['status']]) && + ! is_null($out[$parent_idx])) { + + if (! isset($outRef['execution_order'])) { + // Doing this I will set order for test cases that still are not linked. + // But Because I loop over all versions (linked and not) if I always write, + // will overwrite right execution order of linked tcversion. + // + // N.B.: + // As suggested by Martin Havlat order will be set to external_id * 10 + $outRef['execution_order'] = $testCase['node_order'] * 10; + } + $outRef['tcversions'][$testCase['id']] = $testCase['version']; + $outRef['tcversions_active_status'][$testCase['id']] = 1; + $outRef['external_id'] = $testCase['tc_external_id']; + $outRef['tcversions_execution_type'][$testCase['id']] = $testCase['execution_type']; + $outRef['importance'][$testCase['id']] = $testCase['importance']; + $outRef['status'][$testCase['id']] = $testCase['status']; + + if (! isset($outRef['tcversions_qty'])) { + $outRef['tcversions_qty'] = 0; + } + $outRef['tcversions_qty'] ++; + } + + if (! is_null($linked_items)) { + foreach ($linked_items as $linked_testcase) { + $target = current($linked_testcase); + if (($target['tc_id'] == $testCase['testcase_id']) && + ($target['tcversion_id'] == $testCase['id'])) { + // This can be written only once no matter platform qty + if (! isset($outRef['tcversions'][$testCase['id']])) { + $outRef['tcversions'][$testCase['id']] = $testCase['version']; + $outRef['tcversions_active_status'][$testCase['id']] = 0; + $outRef['external_id'] = $testCase['tc_external_id']; + $outRef['tcversions_execution_type'][$testCase['id']] = $testCase['execution_type']; + $outRef['importance'][$testCase['id']] = $testCase['importance']; + } + $outRef['execution_order'] = isset( + $target['execution_order']) ? $target['execution_order'] : 0; + if (isset($target['priority'])) { + $outRef['priority'] = priority_to_level( + $target['priority']); + } + $outRef['linked_version_id'] = $testCase['id']; + $out[$parent_idx]['write_buttons'] = 'yes'; + $out[$parent_idx]['linked_testcase_qty'] ++; + $result['has_linked_items'] = 1; + + foreach ($linked_testcase as $item) { + // 20120714 - franciscom - need t check if this info is needed. + if (isset($item['executed']) && + (intval($item['executed']) > 0) || + isset($item['exec_id']) && + (intval($item['exec_id']) > 0)) { + $outRef['executed'][$item['platform_id']] = 'yes'; + } + + if (isset($item['linked_ts'])) { + $outRef['linked_ts'][$item['platform_id']] = $item['linked_ts']; + } + + foreach ($optionalIntegerFields as $fieldKey) { + if (isset($item[$fieldKey])) { + $outRef[$fieldKey][$item['platform_id']] = intval( + $item[$fieldKey]); + } + } + + // this logic has been created to cope with multiple tester assignment + if ($my['opt']['useOptionalArrayFields']) { + + foreach ($optionalArrayFields as $fieldKey) { + // We have issues when no user is assigned because is + if (is_array($item[$fieldKey])) { + // this seems to be the path we follow when trying to work on test suite + $outRef[$fieldKey][$item['platform_id']] = $item[$fieldKey]; + } else { + // this seems to be the path we follow when trying to work on SINGLE test case + $outRef[$fieldKey][$item['platform_id']][] = intval( + $item[$fieldKey]); + } + } + } + } + break; + } + } + } + } // foreach + + // Again DAMM 0!! + if (! is_null($out[$firstElemIDX])) { + $result['spec_view'] = $out; + } + return $result; +} + +/** + * changed return type when there are no platforms + */ +function getPlatforms($db, $tproject_id, $testplan_id) +{ + $platform_mgr = new tlPlatform($db, $tproject_id); + + if (is_null($testplan_id)) { + $opx = array( + 'enable_on_design' => null, + 'enable_on_execution' => true + ); + $platforms = $platform_mgr->getAll($opx); + } else { + $platforms = $platform_mgr->getLinkedToTestplan($testplan_id); + } + + if (is_null($platforms)) { + // need to create fake data for platform 0 in order + // to have only simple logic + // $platforms= array( 'id' => 0, 'name' => ''); + $platforms[0] = array( + 'id' => 0, + 'name' => '' + ); + } + return $platforms; +} + +/** + */ +function getFilteredSpecViewFlat(&$dbHandler, &$argsObj, &$tplanMgr, &$tcaseMgr, + $filters = null, $options = null) +{ + $tprojectMgr = new testproject($dbHandler); + $tsuite_data = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $argsObj->id); + + $my = array(); // some sort of local scope + $my['filters'] = array( + 'keywordsFilter' => null, + 'testcaseFilter' => null, + 'assignedToFilter' => null, + 'executionTypeFilter' => null, + 'platformsFilter' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $my['options'] = array( + 'write_button_only_if_linked' => 1, + 'prune_unlinked_tcversions' => 1 + ); + $my['options'] = array_merge($my['options'], (array) $options); + + // This does filter on keywords ALWAYS in OR mode. + $tplan_linked_tcversions = getFilteredLinkedVersions($dbHandler, $argsObj, + $tplanMgr, $tcaseMgr, $options); + + // With these pieces we implement the AND type of keyword filter. + $testCaseSet = null; + $tryNextFilter = true; + $filterApplied = false; + if (! is_null($my['filters']['keywordsFilter']) && + ! is_null($my['filters']['keywordsFilter']->items)) { + $keywordsTestCases = $tprojectMgr->getKeywordsLatestTCV( + $argsObj->tproject_id, $my['filters']['keywordsFilter']->items, + $my['filters']['keywordsFilter']->type); + + $testCaseSet = array_keys((array) $keywordsTestCases); + $tryNextFilter = ! is_null($testCaseSet); + $filterApplied = true; + } + + if ($tryNextFilter && ! is_null($my['filters']['testcaseFilter'])) { + $filterApplied = true; + if (is_null($testCaseSet)) { + $testCaseSet = $my['filters']['testcaseFilter']; + } else { + // wrong use of array() instead of (array) + $testCaseSet = array_intersect($testCaseSet, + (array) $my['filters']['testcaseFilter']); + } + } + + // when $testCaseSet is null because we have applied filters + // => we do not need to call other + // method because we know we are going to get NOTHING + $testCaseSet = ! is_null($testCaseSet) ? array_combine($testCaseSet, + $testCaseSet) : null; + if ($filterApplied && is_null($testCaseSet)) { + return null; + } + + $genSpecFilters = array( + 'keywords' => $argsObj->keyword_id, + 'testcases' => $testCaseSet, + 'exec_type' => $my['filters']['executionTypeFilter'], + 'cfields' => null, + 'platforms' => $argsObj->platform_id + ); + + if (isset($my['filters']['cfieldsFilter'])) { + $genSpecFilters['cfields'] = $my['filters']['cfieldsFilter']; + } + + return genSpecViewFlat($dbHandler, 'testplan', $argsObj->tplan_id, + $argsObj->id, $tsuite_data['name'], $tplan_linked_tcversions, null, + $genSpecFilters, $my['options']); +} + +/** + */ +function genSpecViewFlat(&$db, $specViewType, $tobj_id, $id, $name, + &$linked_items, $map_node_tccount, $filters = null, $options = null, + $tproject_id = null) +{ + $spec_view_type = is_null($specViewType) ? 'testproject' : $specViewType; + + $out = array(); + $result = array( + 'spec_view' => array(), + 'num_tc' => 0, + 'has_linked_items' => 0 + ); + + $my = array(); + $my['options'] = array( + 'write_button_only_if_linked' => 0, + 'prune_unlinked_tcversions' => 0, + 'add_custom_fields' => 0 + ) + (array) $options; + + $my['filters'] = array( + 'keywords' => 0, + 'testcases' => null, + 'exec_type' => null, + 'importance' => null, + 'cfields' => null + ); + + foreach ($my as $key => $settings) { + if (! is_null($$key) && is_array($$key)) { + $my[$key] = array_merge($my[$key], $$key); + } + } + + $write_status = $my['options']['write_button_only_if_linked'] ? 'no' : 'yes'; + $is_tplan_view_type = $spec_view_type == 'testplan' ? 1 : 0; + $is_uncovered_view_type = ($spec_view_type == 'uncoveredtestcases') ? 1 : 0; + + if (! $is_tplan_view_type && is_null($tproject_id)) { + $tproject_id = $tobj_id; + } + + $testplan_id = $is_tplan_view_type ? $tobj_id : null; + + $tcaseMgr = new testcase($db); + $hash_descr_id = $tcaseMgr->tree_manager->get_available_node_types(); + $hash_id_descr = array_flip($hash_descr_id); + + $key2map = array( + 'keyword_id' => 'keywords', + 'tcase_id' => 'testcases', + 'execution_type' => 'exec_type', + 'importance' => 'importance', + 'cfields' => 'cfields', + 'tcase_name' => 'tcase_name', + 'status' => 'workflow_status' + ); + + $pfFilters = array( + 'tcase_node_type_id' => $hash_descr_id['testcase'] + ); + foreach ($key2map as $tk => $fk) { + $pfFilters[$tk] = isset($my['filters'][$fk]) ? $my['filters'][$fk] : null; + } + + $test_spec = getTestSpecFromNode($db, $tcaseMgr, $linked_items, $tobj_id, + $id, $spec_view_type, $pfFilters); + + $platforms = getPlatforms($db, $tproject_id, $testplan_id); + $a_tcid = array(); + $a_tsuite_idx = array(); + if (count($test_spec)) { + $cfg = array( + 'node_types' => $hash_id_descr, + 'write_status' => $write_status, + 'is_uncovered_view_type' => $is_uncovered_view_type + ); + + // $a_tsuite_idx + // key: test case version id + // value: index inside $out, where parent test suite of test case version id is located. + // + list ($a_tcid, $a_tsuite_idx, , $out) = buildSkeletonFlat($id, $name, + $cfg, $test_spec, $platforms); + } + + // Collect information related to linked testcase versions + // DAMMED 0!!!! + $firtsElemIDX = key($out); + if (! empty($out) && ! is_null($out[$firtsElemIDX]) && count($a_tcid)) { + $optGBI = array( + 'output' => 'full_without_users', + 'order_by' => " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC " + ); + + if (isset($options['onlyLatestTCV']) && $options['onlyLatestTCV']) { + $tcaseVersionSet = $tcaseMgr->getLTCVInfo($a_tcid); + } else { + $whatSet = testcase::ALL_VERSIONS; + $tcaseVersionSet = $tcaseMgr->get_by_id($a_tcid, $whatSet, null, + $optGBI); + } + $result = addLinkedVersionsInfo($tcaseVersionSet, $a_tsuite_idx, $out, + $linked_items, $options); + } + + if (count($result['spec_view']) > 0 && $my['options']['add_custom_fields']) { + addCustomFieldsToView($result['spec_view'], $tproject_id, $tcaseMgr); + } + + // with array_values() we reindex array to avoid "holes" + $result['spec_view'] = array_values($result['spec_view']); + return $result; +} + +/** + * Developer Notice + * key 'user_id' is JUST initialized + */ +function buildSkeletonFlat($branchRootID, $name, $config, &$test_spec, + &$platforms) +{ + $parent_idx = - 1; + $pivot_tsuite = $test_spec[0]; + $levelSet = array(); + $tcase_memory = null; + + $node_types = $config['node_types']; + $write_status = $config['write_status']; + $is_uncovered_view_type = $config['is_uncovered_view_type']; + + $out = array(); + $a_tcid = array(); + $a_tsuite_idx = array(); + + $rootIDX = 0; + $hash_id_pos[$branchRootID] = $rootIDX; + $out[$rootIDX]['testsuite'] = array( + 'id' => $branchRootID, + 'name' => $name + ); + $out[$rootIDX]['testcases'] = array(); + $out[$rootIDX]['write_buttons'] = 'no'; + $out[$rootIDX]['testcase_qty'] = 0; + $out[$rootIDX]['level'] = 1; + $out[$rootIDX]['linked_testcase_qty'] = 0; + $out[$rootIDX]['linked_ts'] = null; + $out[$rootIDX]['linked_by'] = 0; + $out[$rootIDX]['priority'] = 0; + + $nameAtLevel[$out[0]['level']] = $name; + + $level = $out[0]['level'] + 1; + $idx = 0; + $idx ++; + $tsuite_tcqty = array( + $branchRootID => 0 + ); + + foreach ($test_spec as $current) { + // it will be interesting to understand if this can happen due to filtering + if (is_null($current)) { + continue; + } + + // pivot is updated each time I find a Test Suite. + switch ($node_types[$current['node_type_id']]) { + case 'testsuite': + // parent_idx is setted ONLY when a test case is found + // this logic is used just to have test case count inside test suite. + if ($parent_idx >= 0) { + $xdx = $out[$parent_idx]['testsuite']['id']; + $tsuite_tcqty[$xdx] = $out[$parent_idx]['testcase_qty']; + } + + if ($pivot_tsuite['parent_id'] != $current['parent_id']) { + if ($pivot_tsuite['id'] == $current['parent_id']) { + $level ++; + $levelSet[$current['parent_id']] = $level; + } else { + $level = $levelSet[$current['parent_id']]; + } + $nameAtLevel[$level] = $current['name']; + } else { + $nameAtLevel[$level] = $current['name']; + } + + $whoiam = ''; + for ($ldx = $out[$rootIDX]['level']; $ldx <= $level; $ldx ++) { + $whoiam .= $nameAtLevel[$ldx] . '/'; + } + + $out[$idx]['testsuite'] = array( + 'id' => $current['id'], + 'name' => $whoiam + ); + $out[$idx]['testcases'] = array(); + $out[$idx]['testcase_qty'] = 0; + $out[$idx]['linked_testcase_qty'] = 0; + $out[$idx]['level'] = $level; + $out[$idx]['write_buttons'] = 'no'; + $hash_id_pos[$current['id']] = $idx; + $idx ++; + + // update pivot. + $levelSet[$current['parent_id']] = $level; + $pivot_tsuite = $current; + break; + + case 'testcase': + break; + } + + // In some situations during processing of testcase, a change of parent can + // exists, then we need to update $tsuite_tcqty + if ($node_types[$current['node_type_id']] == "testcase") { + $tc_id = $current['id']; + $parent_idx = $hash_id_pos[$current['parent_id']]; + $a_tsuite_idx[$tc_id] = $parent_idx; + $out[$parent_idx]['testcases'][$tc_id] = array( + 'id' => $tc_id, + 'name' => $current['name'] + ); + + // Reference to make code reading more human friendly + $outRef = &$out[$parent_idx]['testcases'][$tc_id]; + + if ($is_uncovered_view_type) { + // @TODO understand impacts of platforms + $outRef['external_id'] = $test_spec[$tc_id]['external_id']; + } else { + $out[$parent_idx]['write_buttons'] = $write_status; + $out[$parent_idx]['linked_testcase_qty'] = 0; + + $outRef['tcversions'] = array(); + $outRef['tcversions_active_status'] = array(); + $outRef['tcversions_execution_type'] = array(); + $outRef['tcversions_qty'] = 0; + $outRef['linked_version_id'] = 0; + $outRef['executed'] = null; + + // useful for tc_exec_assignment.php + $outRef['platforms'] = $platforms; + $outRef['feature_id'] = null; + $outRef['linked_by'] = null; + $outRef['linked_ts'] = null; + $outRef['priority'] = 0; + $outRef['user_id'] = array(); + } + $out[$parent_idx]['testcase_qty'] ++; + $a_tcid[] = $current['id']; + + // This piece is needed initialize in right way $tsuite_tcqty + // in this kind of situation, for SubSuite2 + // + // Tsuite 1 + // |__ SubSuite1 + // | |__TCX1 + // | |__TCX2 + // | + // |__ SubSuite2 + // | |__TCY1 + // | |__TCY2 + // | + // |__ TCZ1 + // + // + if ($tcase_memory['parent_id'] != $current['parent_id']) { + if (! is_null($tcase_memory)) { + $pidx = $hash_id_pos[$tcase_memory['parent_id']]; + $xdx = $out[$pidx]['testsuite']['id']; + $tsuite_tcqty[$xdx] = $out[$pidx]['testcase_qty']; + } + $tcase_memory = $current; + } + } + } // foreach + + // Update after finished loop + if ($parent_idx >= 0) { + $xdx = $out[$parent_idx]['testsuite']['id']; + $tsuite_tcqty[$xdx] = $out[$parent_idx]['testcase_qty']; + } + + unset($tcase_memory); + $tsuite_tcqty[$branchRootID] = $out[$hash_id_pos[$branchRootID]]['testcase_qty']; + + // Clean up + $loop2do = count($out); + $toUnset = null; + for ($lzx = 0; $lzx < $loop2do; $lzx ++) { + if (count($out[$lzx]['testcases']) == 0) { + $toUnset[$lzx] = $lzx; + } + } + if (! is_null($toUnset)) { + foreach ($toUnset as $kill) { + unset($out[$kill]); + } + } + return array( + $a_tcid, + $a_tsuite_idx, + $tsuite_tcqty, + $out + ); } diff --git a/lib/functions/string_api.php b/lib/functions/string_api.php index ee08328700..9ce43cd6f2 100644 --- a/lib/functions/string_api.php +++ b/lib/functions/string_api.php @@ -1,522 +1,528 @@ - - **/ -function string_preserve_spaces_at_bol( $p_string ) -{ - $lines = explode( "\n", $p_string ); - $line_count = count( $lines ); - for ( $i = 0; $i < $line_count; $i++ ) { - $count = 0; - $prefix = ''; - - $t_char = substr( $lines[$i], $count, 1 ); - $spaces = 0; - while ( ( $t_char == ' ' ) || ( $t_char == "\t" ) ) { - if ( $t_char == ' ' ) - $spaces++; - else - $spaces += 4; // 1 tab = 4 spaces, can be configurable. - - $count++; - $t_char = substr( $lines[$i], $count, 1 ); - } - - for ( $j = 0; $j < $spaces; $j++ ) { - $prefix .= ' '; - } - - $lines[$i] = $prefix . substr( $lines[$i], $count ); - } - return implode( "\n", $lines ); -} - - -/** - * Prepare a string to be printed without being broken into multiple lines - **/ -function string_no_break( $p_string ) { - if ( strpos( $p_string, ' ' ) !== false ) { - return '' . $p_string . ""; - } else { - return $p_string; - } -} - -/** - * Similar to nl2br, but fixes up a problem where new lines are doubled between < pre > tags. - * additionally, wrap the text an $p_wrap character intervals if the config is set - * - * @author Mantis BT team - */ -function string_nl2br( $p_string, $p_wrap = 100 ) -{ - $p_string = nl2br( $p_string ); - - // fix up eols within
     tags
    -		$pre2 = array();
    -		preg_match_all("/]*?>(.|\n)*?<\/pre>/", $p_string, $pre1);
    -		for ( $x = 0; $x < count($pre1[0]); $x++ ) 
    -		{
    -			$pre2[$x] = preg_replace("/]*?>/", "", $pre1[0][$x]);
    -			// this may want to be replaced by html_entity_decode (or equivalent)
    -			//     if other encoded characters are a problem
    -			$pre2[$x] = preg_replace("/ /", " ", $pre2[$x]);
    -			if ( ON == config_get( 'wrap_in_preformatted_text' ) ) 
    -			{
    -				$pre2[$x] = preg_replace("/([^\n]{".$p_wrap."})(?!<\/pre>)/", "$1\n", $pre2[$x]);
    -			}
    -			$pre1[0][$x] = "/" . preg_quote($pre1[0][$x], "/") . "/";
    -		}
    -
    -		return preg_replace( $pre1[0], $pre2, $p_string );
    -}
    -
    -
    -/** 
    - * Prepare a multiple line string for display to HTML 
    - **/
    -function string_display( $p_string ) 
    -{	
    -	$p_string = string_strip_hrefs( $p_string );
    -	$p_string = string_html_specialchars( $p_string );
    -	$p_string = string_restore_valid_html_tags( $p_string, /* multiline = */ true );
    -	$p_string = string_preserve_spaces_at_bol( $p_string );
    -	$p_string = string_nl2br( $p_string );
    -
    -	return $p_string;
    -}
    -
    -
    -/** Prepare a single line string for display to HTML */
    -function string_display_line( $p_string ) 
    -{
    -	$p_string = string_strip_hrefs( $p_string );
    -	$p_string = string_html_specialchars( $p_string );
    -	$p_string = string_restore_valid_html_tags( $p_string, /* multiline = */ false );
    -	
    -	return $p_string;
    -}
    -
    -
    -/** 
    - * Prepare a string for display to HTML and add href anchors for URLs, emails,
    - * bug references, and cvs references
    - */
    -function string_display_links( $p_string ) 
    -{
    -	$p_string = string_display( $p_string );
    -	$p_string = string_insert_hrefs( $p_string );
    -	return $p_string;
    -}
    -
    -
    -/** 
    - * Prepare a single line string for display to HTML and add href anchors for
    - * URLs, emails, bug references, and cvs references
    - */ 
    -function string_display_line_links( $p_string ) 
    -{
    -	$p_string = string_display_line( $p_string );
    -	$p_string = string_insert_hrefs( $p_string );
    -
    -	return $p_string;
    -}
    -
    -
    -/** Prepare a string for display in rss */
    -function string_rss_links( $p_string ) 
    -{
    -	// rss can not start with   which spaces will be replaced into by string_display().
    -	$t_string = trim( $p_string );
    -
    -	// same steps as string_display_links() without the preservation of spaces since   is undefined in XML.
    -	$t_string = string_strip_hrefs( $t_string );
    -	$t_string = string_html_specialchars( $t_string );
    -	$t_string = string_restore_valid_html_tags( $t_string );
    -	$t_string = string_nl2br( $t_string );
    -	$t_string = string_insert_hrefs( $t_string );
    -	$t_string = string_process_bug_link( $t_string, /* anchor */ true, /* detailInfo */ false, /* fqdn */ true );
    -	$t_string = string_process_bugnote_link( $t_string, /* anchor */ true, /* detailInfo */ false, /* fqdn */ true );
    -	$t_string = string_process_cvs_link( $t_string );
    -	# another escaping to escape the special characters created by the generated links
    -	$t_string = string_html_specialchars( $t_string );
    -
    -	return $t_string;
    -}
    -
    -   
    -/** 
    - * Prepare a string for plain text display in email 
    - **/
    -function string_email( $p_string ) 
    -{
    -	$p_string = string_strip_hrefs( $p_string );
    -	return $p_string;
    -}
    - 
    -  
    -/**  
    - * Prepare a string for plain text display in email and add URLs for bug
    - * links and cvs links
    - */     
    -function string_email_links( $p_string ) {
    -	$p_string = string_email( $p_string );
    -  return $p_string;
    -}
    -
    -
    -/** 
    - * Process a string for display in a textarea box 
    - **/
    -function string_textarea( $p_string ) 
    -{
    -	$p_string = string_html_specialchars( $p_string );
    -	return $p_string;
    -}
    -
    -
    -/** 
    - * Process a string for display in a text box
    - */
    -function string_attribute( $p_string ) 
    -{
    -	$p_string = string_html_specialchars( $p_string );
    -
    -	return $p_string;
    -}
    -
    -
    -/** 
    - * Process a string for inclusion in a URL as a GET parameter 
    - */
    -function string_url( $p_string ) 
    -{
    -	$p_string = rawurlencode( $p_string );
    -
    -	return $p_string;
    -}
    -
    -
    -/** 
    - * validate the url as part of this site before continuing 
    - **/
    -function string_sanitize_url( $p_url ) {
    -
    -	$t_url = strip_tags( urldecode( $p_url ) );
    -	if ( preg_match( '?http(s)*://?', $t_url ) > 0 ) { 
    -		// no embedded addresses
    -		if ( preg_match( '?^' . config_get( 'path' ) . '?', $t_url ) == 0 ) { 
    -			// url is ok if it begins with our path, if not, replace it
    -			$t_url = 'index.php';
    -		}
    -	}
    -	if ( $t_url == '' ) {
    -		$t_url = 'index.php';
    -	}
    -	
    -	// split and encode parameters
    -	if ( strpos( $t_url, '?' ) !== FALSE ) {
    -		list( $t_path, $t_param ) = split( '\?', $t_url, 2 );
    -		if ( $t_param !== "" ) {
    -			$t_vals = array();
    -			parse_str( $t_param, $t_vals );
    -			$t_param = '';
    -			foreach($t_vals as $k => $v) {
    -				if ($t_param != '') {
    -					$t_param .= '&'; 
    -				}
    -				$t_param .= "$k=" . urlencode( strip_tags( urldecode( $v ) ) );
    -			}
    -			return $t_path . '?' . $t_param;
    -		} else {
    -			return $t_path;
    -		}
    -	} else {
    -		return $t_url;
    -	}
    -}
    -	
    -
    -// ----- Tag Processing -------------------------------------------------------
    -
    -/**
    - * Search email addresses and URLs for a few common protocols in the given
    - * string, and replace occurences with href anchors.
    - * @param string $p_string
    - * @return string
    - */
    -function string_insert_hrefs( $p_string ) {
    -	static $s_url_regex = null;
    -	static $s_email_regex = null;
    -	static $s_anchor_regex = '/(]*>.*?<\/a>)/is';
    -
    -	if( !config_get( 'html_make_links' ) ) {
    -		return $p_string;
    -	}
    -
    -	$t_change_quotes = false;
    -	if( ini_get_bool( 'magic_quotes_sybase' ) && function_exists( 'ini_set' ) ) {
    -		$t_change_quotes = true;
    -		ini_set( 'magic_quotes_sybase', false );
    -	}
    -
    -	# Initialize static variables
    -	if ( is_null( $s_url_regex ) ) {
    -		# URL protocol. The regex accepts a small subset from the list of valid
    -		# IANA permanent and provisional schemes defined in
    -		# http://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
    -		$t_url_protocol = '(?:https?|s?ftp|file|irc[6s]?|ssh|telnet|nntp|git|svn(?:\+ssh)?|cvs):\/\/';
    -
    -		# %2A notation in url's
    -		$t_url_hex = '%[[:digit:]A-Fa-f]{2}';
    -
    -		# valid set of characters that may occur in url scheme. Note: - should be first (A-F != -AF).
    -		$t_url_valid_chars       = '-_.,!~*\';\/?%^\\\\:@&={\|}+$#[:alnum:]\pL';
    -		$t_url_chars             = "(?:${t_url_hex}|[${t_url_valid_chars}\(\)\[\]])";
    -		$t_url_chars2            = "(?:${t_url_hex}|[${t_url_valid_chars}])";
    -		$t_url_chars_in_brackets = "(?:${t_url_hex}|[${t_url_valid_chars}\(\)])";
    -		$t_url_chars_in_parens   = "(?:${t_url_hex}|[${t_url_valid_chars}\[\]])";
    -
    -		$t_url_part1 = "${t_url_chars}";
    -		$t_url_part2 = "(?:\(${t_url_chars_in_parens}*\)|\[${t_url_chars_in_brackets}*\]|${t_url_chars2})";
    -
    -		$s_url_regex = "/(${t_url_protocol}(${t_url_part1}*?${t_url_part2}+))/su";
    -
    -		# e-mail regex
    -		$s_email_regex = substr_replace( email_regex_simple(), '(?:mailto:)?', 1, 0 );
    -	}
    -
    -	# Find any URL in a string and replace it by a clickable link
    -	$t_function = create_function( '$p_match', '
    -		$t_url_href = \'href="\' . rtrim( $p_match[1], \'.\' ) . \'"\';
    -		return "${p_match[1]} [^]";
    -	' );
    -	$p_string = preg_replace_callback( $s_url_regex, $t_function, $p_string );
    -	if( $t_change_quotes ) {
    -		ini_set( 'magic_quotes_sybase', true );
    -	}
    -
    -	# Find any email addresses in the string and replace them with a clickable
    -	# mailto: link, making sure that we skip processing of any existing anchor
    -	# tags, to avoid parts of URLs such as https://user@example.com/ or
    -	# http://user:password@example.com/ to be not treated as an email.
    -	$t_pieces = preg_split( $s_anchor_regex, $p_string, null, PREG_SPLIT_DELIM_CAPTURE );
    -	$p_string = '';
    -	foreach( $t_pieces as $piece ) {
    -		if( preg_match( $s_anchor_regex, $piece ) ) {
    -			$p_string .= $piece;
    -		} else {
    -			$p_string .= preg_replace( $s_email_regex, '\0', $piece );
    -		}
    -	}
    -
    -	return $p_string;
    -}
    -
    -
    -/** 
    - * Detect href anchors in the string and replace them with URLs and email addresses 
    - **/
    -function string_strip_hrefs( $p_string ) 
    -{
    -	# First grab mailto: hrefs.  We don't care whether the URL is actually
    -	# correct - just that it's inside an href attribute.
    -	$p_string = preg_replace( '/]*href="mailto:([^\"]+)"[^\>]*>[^\<]*<\/a>/si',
    -								'\1', $p_string);
    -
    -	# Then grab any other href
    -	$p_string = preg_replace( '/]*href="([^\"]+)"[^\>]*>[^\<]*<\/a>/si',
    -								'\1', $p_string);
    -	return $p_string;
    -}
    -
    -
    -/**
    - * This function looks for text with htmlentities
    - * like <b> and converts is into corresponding
    - * html <b> based on the configuration presets
    - */
    -function string_restore_valid_html_tags( $p_string, $p_multiline = true ) 
    -{
    -	$t_html_valid_tags = config_get( $p_multiline ? 'html_valid_tags' : 'html_valid_tags_single_line' );
    -
    -	if ( OFF === $t_html_valid_tags || is_blank( $t_html_valid_tags ) ) {
    -		return $p_string;
    -	}
    -
    -	$tags = explode( ',', $t_html_valid_tags );
    -	foreach ($tags as $key => $value) 
    -	{ 
    -    	if ( !is_blank( $value ) ) {
    -        	$tags[$key] = trim($value); 
    -        }
    -    }
    -    $tags = implode( '|', $tags);
    -
    -	$p_string = preg_replace( '/<(' . $tags . ')\s*>/ui', '<\\1>', $p_string );
    -	$p_string = preg_replace( '/<\/(' . $tags . ')\s*>/ui', '', $p_string );
    -	$p_string = preg_replace( '/<(' . $tags . ')\s*\/>/ui', '<\\1 />', $p_string );
    -
    -
    -	return $p_string;
    -}
    -
    -
    -/**	
    - * Return a string with the $p_character pattern repeated N times.
    - * 
    - * @param string $p_character - pattern to repeat
    - * @param integer $p_repeats - number of times to repeat.
    - */
    -function string_repeat_char( $p_character, $p_repeats ) {
    -	return str_pad( '', $p_repeats, $p_character );
    -}
    -
    -
    -/**
    - * Format date for display
    - */ 
    -function string_format_complete_date( $p_date ) {
    -	$t_timestamp = db_unixtimestamp( $p_date );
    -	return date( config_get( 'complete_date_format' ), $t_timestamp );
    -}
    -
    -
    -/** 
    - * Shorten a string for display on a dropdown to prevent the page rendering too wide
    - */
    -function string_shorten( $p_string ) {
    -	$t_max = config_get( 'max_dropdown_length' );
    -	if ( ( tlStrLen($p_string ) > $t_max ) && ( $t_max > 0 ) ){
    -		$t_pattern = '/([\s|.|,|\-|_|\/|\?]+)/';
    -		$t_bits = preg_split( $t_pattern, $p_string, -1, PREG_SPLIT_DELIM_CAPTURE );
    -
    -		$t_string = '';
    -		$t_last = $t_bits[ count( $t_bits ) - 1 ];
    -		$t_last_len = tlStrLen( $t_last );
    -
    -		foreach ( $t_bits as $t_bit ) {
    -			if ( ( tlStrLen( $t_string ) + tlStrLen( $t_bit ) + $t_last_len + 3 <= $t_max )
    -				|| ( strpos( $t_bit, '.,-/?' ) > 0 ) ) {
    -				$t_string .= $t_bit;
    -			} else {
    -				break;
    -			}
    -		}
    -		$t_string .= '...' . $t_last;
    -		return $t_string;
    -	} else {
    -		return $p_string;
    -	}
    -}
    -
    -
    -/**
    - * remap a field name to a string name (for sort filter)
    - */
    -function string_get_field_name( $p_string ) {
    -
    -	$t_map = array(
    -			'last_updated' => 'last_update',
    -			'id' => 'email_bug'
    -			);
    -
    -	$t_string = $p_string;
    -	if ( isset( $t_map[ $p_string ] ) ) {
    -		$t_string = $t_map[ $p_string ];
    -	}
    -	return lang_get_defaulted( $t_string );
    -}
    -
    -
    -/** 
    - * Calls htmlentities on the specified string, passing along
    - * the current charset.
    - */
    -function string_html_entities( $p_string ) {
    -	return htmlentities( $p_string, ENT_COMPAT, config_get('charset') );
    -}
    -
    -
    -/** 
    - * Calls htmlspecialchars on the specified string, passing along
    - * the current charset, if the current PHP version supports it.
    - */
    -function string_html_specialchars( $p_string ) {
    -	# achumakov: @ added to avoid warning output in unsupported codepages
    -	# e.g. 8859-2, windows-1257, Korean, which are treated as 8859-1.
    -	# This is VERY important for Eastern European, Baltic and Korean languages
    -	return preg_replace("/&(#[0-9]+|[a-z]+);/i", "&$1;", @htmlspecialchars( $p_string, ENT_COMPAT, config_get('charset') ) );
    -}
    -
    -
    -/** 
    - * Prepares a string to be used as part of header().
    - */
    -function string_prepare_header( $p_string ) {
    -	$t_string = $p_string;
    -
    -	$t_truncate_pos = strpos($p_string, "\n");
    -	if ($t_truncate_pos !== false ) {
    -		$t_string = substr($t_string, 0, $t_truncate_pos);
    -	}
    -
    -	$t_truncate_pos = strpos($p_string, "\r");
    -	if ($t_truncate_pos !== false ) {
    -		$t_string = substr($t_string, 0, $t_truncate_pos);
    -	}
    -
    -	return $t_string;
    -}
    -
    -
    -/** 
    - * Checks the supplied string for scripting characters, if it contains any, then return true, otherwise return false.
    - * 
    - * @param string $p_string
    - * @return boolean
    - */
    -function string_contains_scripting_chars( $p_string ) {
    -	if ( ( strstr( $p_string, '<' ) !== false ) || ( strstr( $p_string, '>' ) !== false ) ) {
    -		return true;
    -	}
    -
    -	return false;
    -}
    -
    -/**
    - * Use a simple perl regex for valid email addresses.  This is not a complete regex,
    - * as it does not cover quoted addresses or domain literals, but it is simple and
    - * covers the vast majority of all email addresses without being overly complex.
    - * @return string
    - */
    -function email_regex_simple() {
    -	static $s_email_regex = null;
    -
    -	if( is_null( $s_email_regex ) ) {
    -		$t_recipient = "([a-z0-9!#*+\/=?^_{|}~-]+(?:\.[a-z0-9!#*+\/=?^_{|}~-]+)*)";
    -
    -		# a domain is one or more subdomains
    -		$t_subdomain = "(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)";
    -		$t_domain    = "(${t_subdomain}(?:\.${t_subdomain})*)";
    -
    -		$s_email_regex = "/${t_recipient}\@${t_domain}/i";
    -	}
    -	return $s_email_regex;
    +
    + */
    +use function database\db_unixtimestamp;
    +
    +require_once 'common.php';
    +
    +function string_preserve_spaces_at_bol($p_string)
    +{
    +    $lines = explode("\n", $p_string);
    +    $line_count = count($lines);
    +    for ($i = 0; $i < $line_count; $i ++) {
    +        $count = 0;
    +        $prefix = '';
    +
    +        $t_char = substr($lines[$i], $count, 1);
    +        $spaces = 0;
    +        while (($t_char == ' ') || ($t_char == "\t")) {
    +            if ($t_char == ' ') {
    +                $spaces ++;
    +            } else {
    +                $spaces += 4;
    +            } // 1 tab = 4 spaces, can be configurable.
    +
    +            $count ++;
    +            $t_char = substr($lines[$i], $count, 1);
    +        }
    +
    +        for ($j = 0; $j < $spaces; $j ++) {
    +            $prefix .= ' ';
    +        }
    +
    +        $lines[$i] = $prefix . substr($lines[$i], $count);
    +    }
    +    return implode("\n", $lines);
    +}
    +
    +/**
    + * Prepare a string to be printed without being broken into multiple lines
    + */
    +function string_no_break($p_string)
    +{
    +    if (strpos($p_string, ' ') !== false) {
    +        return '' . $p_string . "";
    +    } else {
    +        return $p_string;
    +    }
    +}
    +
    +/**
    + * Similar to nl2br, but fixes up a problem where new lines are doubled between < pre > tags.
    + * additionally, wrap the text an $p_wrap character intervals if the config is set
    + *
    + * @author Mantis BT team
    + */
    +function string_nl2br($p_string, $p_wrap = 100)
    +{
    +    $p_string = nl2br($p_string);
    +
    +    // fix up eols within 
     tags
    +    $pre2 = array();
    +    preg_match_all("/]*?>(.|\n)*?<\/pre>/", $p_string, $pre1);
    +    for ($x = 0; $x < count($pre1[0]); $x ++) {
    +        $pre2[$x] = preg_replace("/]*?>/", "", $pre1[0][$x]);
    +        // this may want to be replaced by html_entity_decode (or equivalent)
    +        // if other encoded characters are a problem
    +        $pre2[$x] = str_replace("/ /", " ", $pre2[$x]);
    +        if (tl::ON == config_get('wrap_in_preformatted_text')) {
    +            $pre2[$x] = preg_replace("/([^\n]{" . $p_wrap . "})(?!<\/pre>)/",
    +                "$1\n", $pre2[$x]);
    +        }
    +        $pre1[0][$x] = "/" . preg_quote($pre1[0][$x], "/") . "/";
    +    }
    +
    +    return preg_replace($pre1[0], $pre2, $p_string);
    +}
    +
    +/**
    + * Prepare a multiple line string for display to HTML
    + */
    +function string_display($p_string)
    +{
    +    $p_string = string_strip_hrefs($p_string);
    +    $p_string = string_html_specialchars($p_string);
    +    $p_string = string_restore_valid_html_tags($p_string, /* multiline = */ true);
    +    $p_string = string_preserve_spaces_at_bol($p_string);
    +    $p_string = string_nl2br($p_string);
    +
    +    return $p_string;
    +}
    +
    +/**
    + * Prepare a single line string for display to HTML
    + */
    +function string_display_line($p_string)
    +{
    +    $p_string = string_strip_hrefs($p_string);
    +    $p_string = string_html_specialchars($p_string);
    +    $p_string = string_restore_valid_html_tags($p_string, /* multiline = */ false);
    +
    +    return $p_string;
    +}
    +
    +/**
    + * Prepare a string for display to HTML and add href anchors for URLs, emails,
    + * bug references, and cvs references
    + */
    +function string_display_links($p_string)
    +{
    +    $p_string = string_display($p_string);
    +    $p_string = string_insert_hrefs($p_string);
    +    return $p_string;
    +}
    +
    +/**
    + * Prepare a single line string for display to HTML and add href anchors for
    + * URLs, emails, bug references, and cvs references
    + */
    +function string_display_line_links($p_string)
    +{
    +    $p_string = string_display_line($p_string);
    +    $p_string = string_insert_hrefs($p_string);
    +
    +    return $p_string;
    +}
    +
    +/**
    + * Prepare a string for display in rss
    + */
    +function string_rss_links($p_string)
    +{
    +    // rss can not start with   which spaces will be replaced into by string_display().
    +    $t_string = trim($p_string);
    +
    +    // same steps as string_display_links() without the preservation of spaces since   is undefined in XML.
    +    $t_string = string_strip_hrefs($t_string);
    +    $t_string = string_html_specialchars($t_string);
    +    $t_string = string_restore_valid_html_tags($t_string);
    +    $t_string = string_nl2br($t_string);
    +    $t_string = string_insert_hrefs($t_string);
    +    $t_string = string_process_bug_link($t_string, /* anchor */ true, /* detailInfo */ false, /* fqdn */ true);
    +    $t_string = string_process_bugnote_link($t_string, /* anchor */ true, /* detailInfo */ false, /* fqdn */ true);
    +    $t_string = string_process_cvs_link($t_string);
    +    # another escaping to escape the special characters created by the generated links
    +    $t_string = string_html_specialchars($t_string);
    +
    +    return $t_string;
    +}
    +
    +/**
    + * Prepare a string for plain text display in email
    + */
    +function string_email($p_string)
    +{
    +    $p_string = string_strip_hrefs($p_string);
    +    return $p_string;
    +}
    +
    +/**
    + * Prepare a string for plain text display in email and add URLs for bug
    + * links and cvs links
    + */
    +function string_email_links($p_string)
    +{
    +    $p_string = string_email($p_string);
    +    return $p_string;
    +}
    +
    +/**
    + * Process a string for display in a textarea box
    + */
    +function string_textarea($p_string)
    +{
    +    $p_string = string_html_specialchars($p_string);
    +    return $p_string;
    +}
    +
    +/**
    + * Process a string for display in a text box
    + */
    +function string_attribute($p_string)
    +{
    +    $p_string = string_html_specialchars($p_string);
    +
    +    return $p_string;
    +}
    +
    +/**
    + * Process a string for inclusion in a URL as a GET parameter
    + */
    +function string_url($p_string)
    +{
    +    $p_string = rawurlencode($p_string);
    +
    +    return $p_string;
    +}
    +
    +/**
    + * validate the url as part of this site before continuing
    + */
    +function string_sanitize_url($p_url)
    +{
    +    $t_url = strip_tags(urldecode($p_url));
    +    if (preg_match('?http(s)*://?', $t_url) > 0 &&
    +        preg_match('?^' . config_get('path') . '?', $t_url) == 0) {
    +        // url is ok if it begins with our path, if not, replace it
    +        $t_url = 'index.php';
    +    }
    +    if ($t_url == '') {
    +        $t_url = 'index.php';
    +    }
    +
    +    // explode and encode parameters
    +    if (strpos($t_url, '?') !== false) {
    +        list ($t_path, $t_param) = explode('\?', $t_url, 2);
    +        if ($t_param !== "") {
    +            $t_vals = array();
    +            parse_str($t_param, $t_vals);
    +            $t_param = '';
    +            foreach ($t_vals as $k => $v) {
    +                if ($t_param != '') {
    +                    $t_param .= '&';
    +                }
    +                $t_param .= "$k=" . urlencode(strip_tags(urldecode($v)));
    +            }
    +            return $t_path . '?' . $t_param;
    +        } else {
    +            return $t_path;
    +        }
    +    } else {
    +        return $t_url;
    +    }
    +}
    +
    +/**
    + * Search email addresses and URLs for a few common protocols in the given
    + * string, and replace occurences with href anchors.
    + *
    + * @param string $p_string
    + * @return string
    + */
    +function string_insert_hrefs($p_string)
    +{
    +    static $s_url_regex = null;
    +    static $s_email_regex = null;
    +    static $s_anchor_regex = '/(]*>.*?<\/a>)/is';
    +
    +    if (! config_get('html_make_links')) {
    +        return $p_string;
    +    }
    +
    +    if (ini_get_bool('magic_quotes_sybase') && function_exists('ini_set')) {
    +        ini_set('magic_quotes_sybase', false);
    +    }
    +
    +    # Initialize static variables
    +    if (is_null($s_url_regex)) {
    +        # URL protocol. The regex accepts a small subset from the list of valid
    +        # IANA permanent and provisional schemes defined in
    +        # http://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
    +        $t_url_protocol = '(?:https?|s?ftp|file|irc[6s]?|ssh|telnet|nntp|git|svn(?:\+ssh)?|cvs):\/\/';
    +
    +        # %2A notation in url's
    +        $t_url_hex = '%[[:digit:]A-Fa-f]{2}';
    +
    +        # valid set of characters that may occur in url scheme. Note: - should be first (A-F != -AF).
    +        $t_url_valid_chars = '-_.,!~*\';\/?%^\\\\:@&={\|}+$#[:alnum:]\pL';
    +        $t_url_chars = "(?:{$t_url_hex}|[{$t_url_valid_chars}\(\)\[\]])";
    +        $t_url_chars2 = "(?:{$t_url_hex}|[{$t_url_valid_chars}])";
    +        $t_url_chars_in_brackets = "(?:{$t_url_hex}|[{$t_url_valid_chars}\(\)])";
    +        $t_url_chars_in_parens = "(?:{$t_url_hex}|[{$t_url_valid_chars}\[\]])";
    +
    +        $t_url_part1 = "{$t_url_chars}";
    +        $t_url_part2 = "(?:\({$t_url_chars_in_parens}*\)|\[{$t_url_chars_in_brackets}*\]|{$t_url_chars2})";
    +
    +        $s_url_regex = "/({$t_url_protocol}({$t_url_part1}*?{$t_url_part2}+))/su";
    +
    +        # e-mail regex
    +        $s_email_regex = substr_replace(email_regex_simple(), '(?:mailto:)?', 1,
    +            0);
    +    }
    +
    +    # Find any URL in a string and replace it with a clickable link
    +    # From MantisBT 2.25.2
    +    $p_string = preg_replace_callback($s_url_regex,
    +        function ($p_match) {
    +            $t_url_href = 'href="' . rtrim($p_match[1], '.') . '"';
    +            if (config_get('html_make_links') == LINKS_NEW_WINDOW) {
    +                $t_url_target = ' target="_blank"';
    +            } else {
    +                $t_url_target = '';
    +            }
    +            return "{$p_match[1]}";
    +        }, $p_string);
    +
    +    # Find any email addresses in the string and replace them with a clickable
    +    # mailto: link, making sure that we skip processing of any existing anchor
    +    # tags, to avoid parts of URLs such as https://user@example.com/ or
    +    # http://user:password@example.com/ to be not treated as an email.
    +    $t_pieces = preg_split($s_anchor_regex, $p_string, null,
    +        PREG_SPLIT_DELIM_CAPTURE);
    +    $p_string = '';
    +    foreach ($t_pieces as $piece) {
    +        if (preg_match($s_anchor_regex, $piece)) {
    +            $p_string .= $piece;
    +        } else {
    +            $p_string .= preg_replace($s_email_regex,
    +                '\0', $piece);
    +        }
    +    }
    +
    +    return $p_string;
    +}
    +
    +/**
    + * Detect href anchors in the string and replace them with URLs and email addresses
    + */
    +function string_strip_hrefs($p_string)
    +{
    +    # First grab mailto: hrefs. We don't care whether the URL is actually
    +    # correct - just that it's inside an href attribute.
    +    $p_string = preg_replace(
    +        '/]*href="mailto:([^\"]+)"[^\>]*>[^\<]*<\/a>/si', '\1',
    +        $p_string);
    +
    +    # Then grab any other href
    +    $p_string = preg_replace('/]*href="([^\"]+)"[^\>]*>[^\<]*<\/a>/si',
    +        '\1', $p_string);
    +    return $p_string;
    +}
    +
    +/**
    + * This function looks for text with htmlentities
    + * like <b> and converts is into corresponding
    + * html <b> based on the configuration presets
    + */
    +function string_restore_valid_html_tags($p_string, $p_multiline = true)
    +{
    +    $t_html_valid_tags = config_get(
    +        $p_multiline ? 'html_valid_tags' : 'html_valid_tags_single_line');
    +
    +    if (0 === $t_html_valid_tags || isBlank($t_html_valid_tags)) {
    +        return $p_string;
    +    }
    +
    +    $tags = explode(',', $t_html_valid_tags);
    +    foreach ($tags as $key => $value) {
    +        if (! isBlank($value)) {
    +            $tags[$key] = trim($value);
    +        }
    +    }
    +    $tags = implode('|', $tags);
    +
    +    $p_string = preg_replace('/<(' . $tags . ')\s*>/ui', '<\\1>',
    +        $p_string);
    +    $p_string = preg_replace('/<\/(' . $tags . ')\s*>/ui', '',
    +        $p_string);
    +    $p_string = preg_replace('/<(' . $tags . ')\s*\/>/ui', '<\\1 />',
    +        $p_string);
    +
    +    return $p_string;
    +}
    +
    +/**
    + * Return a string with the $p_character pattern repeated N times.
    + *
    + * @param string $p_character
    + *            - pattern to repeat
    + * @param integer $p_repeats
    + *            - number of times to repeat.
    + */
    +function string_repeat_char($p_character, $p_repeats)
    +{
    +    return str_pad('', $p_repeats, $p_character);
    +}
    +
    +/**
    + * Format date for display
    + */
    +function string_format_complete_date($p_date)
    +{
    +    $t_timestamp = db_unixtimestamp($p_date);
    +    return date(config_get('complete_date_format'), $t_timestamp);
    +}
    +
    +/**
    + * Shorten a string for display on a dropdown to prevent the page rendering too wide
    + */
    +function string_shorten($p_string)
    +{
    +    $t_max = config_get('max_dropdown_length');
    +    if ((tlStrLen($p_string) > $t_max) && ($t_max > 0)) {
    +        $t_pattern = '/([\s|.|,|\-|_|\/|\?]+)/';
    +        $t_bits = preg_split($t_pattern, $p_string, - 1,
    +            PREG_SPLIT_DELIM_CAPTURE);
    +
    +        $t_string = '';
    +        $t_last = $t_bits[count($t_bits) - 1];
    +        $t_last_len = tlStrLen($t_last);
    +
    +        foreach ($t_bits as $t_bit) {
    +            if ((tlStrLen($t_string) + tlStrLen($t_bit) + $t_last_len + 3 <=
    +                $t_max) || (strpos($t_bit, '.,-/?') > 0)) {
    +                $t_string .= $t_bit;
    +            } else {
    +                break;
    +            }
    +        }
    +        $t_string .= '...' . $t_last;
    +        return $t_string;
    +    } else {
    +        return $p_string;
    +    }
    +}
    +
    +/**
    + * remap a field name to a string name (for sort filter)
    + */
    +function string_get_field_name($p_string)
    +{
    +    $t_map = array(
    +        'last_updated' => 'last_update',
    +        'id' => 'email_bug'
    +    );
    +
    +    $t_string = $p_string;
    +    if (isset($t_map[$p_string])) {
    +        $t_string = $t_map[$p_string];
    +    }
    +    return lang_get_defaulted($t_string);
    +}
    +
    +/**
    + * Calls htmlentities on the specified string, passing along
    + * the current charset.
    + */
    +function string_html_entities($p_string)
    +{
    +    return htmlentities($p_string, ENT_COMPAT, config_get('charset'));
    +}
    +
    +/**
    + * Calls htmlspecialchars on the specified string, passing along
    + * the current charset, if the current PHP version supports it.
    + */
    +function string_html_specialchars($p_string)
    +{
    +    # achumakov: @ added to avoid warning output in unsupported codepages
    +    # e.g. 8859-2, windows-1257, Korean, which are treated as 8859-1.
    +    # This is VERY important for Eastern European, Baltic and Korean languages
    +    return preg_replace("/&(#[\d]+|[a-z]+);/i", "&$1;",
    +        @htmlspecialchars($p_string, ENT_COMPAT, config_get('charset')));
    +}
    +
    +/**
    + * Prepares a string to be used as part of header().
    + */
    +function string_prepare_header($p_string)
    +{
    +    $t_string = $p_string;
    +
    +    $t_truncate_pos = strpos($p_string, "\n");
    +    if ($t_truncate_pos !== false) {
    +        $t_string = substr($t_string, 0, $t_truncate_pos);
    +    }
    +
    +    $t_truncate_pos = strpos($p_string, "\r");
    +    if ($t_truncate_pos !== false) {
    +        $t_string = substr($t_string, 0, $t_truncate_pos);
    +    }
    +
    +    return $t_string;
    +}
    +
    +/**
    + * Checks the supplied string for scripting characters, if it contains any, then return true, otherwise return false.
    + *
    + * @param string $p_string
    + * @return boolean
    + */
    +function string_contains_scripting_chars($p_string)
    +{
    +    if ((strstr($p_string, '<') !== false) || (strstr($p_string, '>') !== false)) {
    +        return true;
    +    }
    +
    +    return false;
    +}
    +
    +/**
    + * Use a simple perl regex for valid email addresses.
    + * This is not a complete regex,
    + * as it does not cover quoted addresses or domain literals, but it is simple and
    + * covers the vast majority of all email addresses without being overly complex.
    + *
    + * @return string
    + */
    +function email_regex_simple()
    +{
    +    static $s_email_regex = null;
    +
    +    if (is_null($s_email_regex)) {
    +        $t_recipient = "([a-z0-9!#*+\/=?^_{|}~-]+(?:\.[a-z0-9!#*+\/=?^_{|}~-]+)*)";
    +
    +        # a domain is one or more subdomains
    +        $t_subdomain = "(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)";
    +        $t_domain = "({$t_subdomain}(?:\.{$t_subdomain})*)";
    +
    +        $s_email_regex = "/{$t_recipient}\@{$t_domain}/i";
    +    }
    +    return $s_email_regex;
     }
    diff --git a/lib/functions/table.class.php b/lib/functions/table.class.php
    index 7d6585c779..5c5c498ef8 100644
    --- a/lib/functions/table.class.php
    +++ b/lib/functions/table.class.php
    @@ -1,187 +1,194 @@
    - 'My column',
    -   *              'col_id' => 'id_Mycolumn',
    -   *              'width' => 150,
    -   *              'type' => 'status'
    -   *            );
    -   *            It is up to the derived class to use this information on
    -   *            rendering. The col_id key is used to identify the data
    -   *            field when using ext js.
    -   *            @see tlTable::titleToColumnName()
    -   */
    -  protected $columns;
    -
    -  /**
    -   * @var array that holds the row data to be displayed. Every row is
    -   *      an array with the column data as describled in $columns.
    -   *      If the data type is status the value should be an array like
    -   *      array('value' => 'f', 'text' => 'Failed', 'cssClass' => 'failed_text')
    -   *      to allow coloring and sorting in table.
    -   */
    -  protected $data;
    -
    -  /**
    -   * A unique id that is used to render the table and to remember state via
    -   * cookie (requires CookieProvider to be set in Ext.onReady);
    -   */
    -  public $tableID = null;
    -
    -  /** @var The title header for the whole table. Default: null (no title) */
    -  public $title = null;
    -
    -  /** @var Width of the table. Default: null (full width) */
    -  public $width = null;
    -
    -  /** @var Height of the table. Default: null
    -   * @see $autoHeight
    -   */
    -  public $height = null;
    -
    -  /** @var autoHeight determines if the table should have a fixed height, or
    -   *       if the height depends on the content.
    -   *       Default: true (height = height of content)
    -   */
    -  public $autoHeight = true;
    -
    -  /*
    -   * Used by titleToColumnName() to create unique column identifiers.
    -   */
    -  protected $usedNames = array();
    -
    -  /**
    -   * @param $columns is either an array of column titles
    -   *        (i.e. array('Title 1', 'Title 2')) or an array where each value
    -   *        is an array('title' => 'Column title 1',
    -   *                    'type' => 'string',
    -   *                    'width' => 150);
    -   *
    -   *        Let the constructor do localization:
    -   *        array('title_key' => 'title_test_case_title', 'width' => 150)
    -   *
    -   *        Explicitly set column id:
    -   *        array('title' => '[%]', 'col_id' => 'passed_percent')
    -   *
    -   *        It is possible to set title_key instead of title: this will mean
    -   *        the localization is done within the constructor and that title_key
    -   *        can be used as column id. If title key is not given then create a
    -   *        column id based on the localized title.
    -   *
    -   *        It is possible to override the generated column id by passing a
    -   *        value as col_id.
    -   *
    -   *        Internally the columns will always be saved in the full format
    -   *        (array-of-arrays).
    -   *
    -   *        @see tlTable::$columns
    -   *        @see tlTable::$data
    -   */
    -  public function __construct($columns, $data, $tableID)
    -  {
    -    // Expand the simple column format (array-of-titles) to full
    -    // array-of-arrays and compute js friendly column names.
    -    $this->columns = array();
    -    foreach ($columns as $column) {
    -      if (is_array($column)) {
    -        if (isset($column['title_key'])) {
    -          if (isset($column['title'])) {
    -            throw new Exception("Both title and title_key are set: use only one of them");
    -          }
    -          $column['title'] = lang_get($column['title_key']);
    -        }
    -
    -        // If $title_key was given, use that for col_id, otherwise use $title
    -        if (!isset($column['col_id'])) {
    -          $key = $column['title'];
    -          if (isset($column['title_key'])) {
    -            $key = $column['title_key'];
    -          }
    -          $column['col_id'] = $this->titleToColumnName($key);
    -        }
    -        $this->columns[] = $column;
    -      }
    -      else if (is_string($column)) {
    -        $this->columns[] = array(
    -          'title' => $column,
    -          'col_id' => $this->titleToColumnName($column)
    -        );
    -      }
    -      else {
    -        throw new Exception("Invalid column header: " . $column);
    -      }
    -    }
    -    $this->data = $data;
    -    $this->tableID = $tableID;
    -  }
    -
    -  /**
    -   * Outputs the code that all tables shares
    -   */
    -  public abstract function renderCommonGlobals();
    -
    -  /**
    -   * Outputs the code that should be in 
    -   */
    -  public abstract function renderHeadSection();
    -
    -  /**
    -   * Outputs the code that should be in 
    -   */
    -  public abstract function renderBodySection();
    -
    -
    -  /**
    -   * Transforms a column title (localized string) to a unique valid
    -   * js identifier by removing all invalid chars.
    -   *
    -   * Note: The result is unique so passing the same $title twice will
    -   * return different column ids. Only meant to be called from constructor.
    -   */
    -  private function titleToColumnName($title) {
    -    static $allowedChars = "_abcdefghijklmnopqrstuvwxyz0123456789";
    -    // always start with this to avoid number in beginning
    -    $js_safe = 'id_';
    -    $chars = str_split($title);
    -    foreach ($chars as $char) {
    -      if (stripos($allowedChars, $char) !== FALSE) {
    -        $js_safe .= $char;
    -      }
    -    }
    -    // If the name is already used append a number
    -    if (in_array($js_safe, $this->usedNames)) {
    -      $i = 1;
    -      // Find next available number
    -      while (in_array($js_safe . $i, $this->usedNames)) {
    -        $i++;
    -      }
    -      $js_safe .= $i;
    -    }
    -    $this->usedNames[] = $js_safe;
    -    return $js_safe;
    -  }
    + 'My column',
    +     *      'col_id' => 'id_Mycolumn',
    +     *      'width' => 150,
    +     *      'type' => 'status'
    +     *      );
    +     *      It is up to the derived class to use this information on
    +     *      rendering. The col_id key is used to identify the data
    +     *      field when using ext js.
    +     * @see tlTable::titleToColumnName()
    +     */
    +    protected $columns;
    +
    +    /**
    +     *
    +     * @var array that holds the row data to be displayed. Every row is
    +     *      an array with the column data as describled in $columns.
    +     *      If the data type is status the value should be an array like
    +     *      array('value' => 'f', 'text' => 'Failed', 'cssClass' => 'failed_text')
    +     *      to allow coloring and sorting in table.
    +     */
    +    protected $data;
    +
    +    /**
    +     * A unique id that is used to render the table and to remember state via
    +     * cookie (requires CookieProvider to be set in Ext.onReady);
    +     */
    +    public $tableID = null;
    +
    +    /** @var The title header for the whole table. Default: null (no title) */
    +    public $title = null;
    +
    +    /** @var Width of the table. Default: null (full width) */
    +    public $width = null;
    +
    +    /**
    +     *
    +     * @var Height of the table. Default: null
    +     * @see $autoHeight
    +     */
    +    public $height = null;
    +
    +    /**
    +     *
    +     * @var autoHeight determines if the table should have a fixed height, or
    +     *      if the height depends on the content.
    +     *      Default: true (height = height of content)
    +     */
    +    public $autoHeight = true;
    +
    +    /*
    +     * Used by titleToColumnName() to create unique column identifiers.
    +     */
    +    protected $usedNames = array();
    +
    +    /**
    +     *
    +     * @param array $columns
    +     *            is either an array of column titles
    +     *            (i.e. array('Title 1', 'Title 2')) or an array where each value
    +     *            is an array('title' => 'Column title 1',
    +     *            'type' => 'string',
    +     *            'width' => 150);
    +     *
    +     *            Let the constructor do localization:
    +     *            array('title_key' => 'title_test_case_title', 'width' => 150)
    +     *
    +     *            Explicitly set column id:
    +     *            array('title' => '[%]', 'col_id' => 'passed_percent')
    +     *
    +     *            It is possible to set title_key instead of title: this will mean
    +     *            the localization is done within the constructor and that title_key
    +     *            can be used as column id. If title key is not given then create a
    +     *            column id based on the localized title.
    +     *
    +     *            It is possible to override the generated column id by passing a
    +     *            value as col_id.
    +     *
    +     *            Internally the columns will always be saved in the full format
    +     *            (array-of-arrays).
    +     *
    +     * @see tlTable::$columns
    +     * @see tlTable::$data
    +     */
    +    public function __construct($columns, $data, $tableID)
    +    {
    +        // Expand the simple column format (array-of-titles) to full
    +        // array-of-arrays and compute js friendly column names.
    +        $this->columns = array();
    +        foreach ($columns as $column) {
    +            if (is_array($column)) {
    +                if (isset($column['title_key'])) {
    +                    if (isset($column['title'])) {
    +                        throw new Exception(
    +                            "Both title and title_key are set: use only one of them");
    +                    }
    +                    $column['title'] = lang_get($column['title_key']);
    +                }
    +
    +                // If $title_key was given, use that for col_id, otherwise use $title
    +                if (! isset($column['col_id'])) {
    +                    $key = $column['title'];
    +                    if (isset($column['title_key'])) {
    +                        $key = $column['title_key'];
    +                    }
    +                    $column['col_id'] = $this->titleToColumnName($key);
    +                }
    +                $this->columns[] = $column;
    +            } elseif (is_string($column)) {
    +                $this->columns[] = array(
    +                    'title' => $column,
    +                    'col_id' => $this->titleToColumnName($column)
    +                );
    +            } else {
    +                throw new Exception("Invalid column header: " . $column);
    +            }
    +        }
    +        $this->data = $data;
    +        $this->tableID = $tableID;
    +    }
    +
    +    /**
    +     * Outputs the code that all tables shares
    +     */
    +    abstract public function renderCommonGlobals();
    +
    +    /**
    +     * Outputs the code that should be in 
    +     */
    +    abstract public function renderHeadSection();
    +
    +    /**
    +     * Outputs the code that should be in 
    +     */
    +    abstract public function renderBodySection();
    +
    +    /**
    +     * Transforms a column title (localized string) to a unique valid
    +     * js identifier by removing all invalid chars.
    +     *
    +     * Note: The result is unique so passing the same $title twice will
    +     * return different column ids. Only meant to be called from constructor.
    +     */
    +    private function titleToColumnName($title)
    +    {
    +        static $allowedChars = "_abcdefghijklmnopqrstuvwxyz0123456789";
    +        // always start with this to avoid number in beginning
    +        $js_safe = 'id_';
    +        $chars = str_split($title);
    +        foreach ($chars as $char) {
    +            if (stripos($allowedChars, $char) !== false) {
    +                $js_safe .= $char;
    +            }
    +        }
    +        // If the name is already used append a number
    +        if (in_array($js_safe, $this->usedNames)) {
    +            $i = 1;
    +            // Find next available number
    +            while (in_array($js_safe . $i, $this->usedNames)) {
    +                $i ++;
    +            }
    +            $js_safe .= $i;
    +        }
    +        $this->usedNames[] = $js_safe;
    +        return $js_safe;
    +    }
     }
    diff --git a/lib/functions/testPlanUrgency.class.php b/lib/functions/testPlanUrgency.class.php
    index 11f675f20d..c3c712756b 100644
    --- a/lib/functions/testPlanUrgency.class.php
    +++ b/lib/functions/testPlanUrgency.class.php
    @@ -1,264 +1,272 @@
    -tables['testplan_tcversions']} SET urgency={$urgency} " .
    -           " WHERE testplan_id=" . $this->db->prepare_int($testplan_id) .
    -           " AND tcversion_id=" . $this->db->prepare_int($tc_id);
    -    $result = $this->db->exec_query($sql);
    -
    -    return $result ? tl::OK : tl::ERROR;
    -  }
    -
    -
    -
    -  /**
    -   * Set urgency for TCs (direct child only) within a Test Suite and Test Plan
    -   * 
    -   * @param integer $testplan_id Test Plan ID
    -   * @param integer $node_id Test Suite to set Urgency
    -   * @param integer $urgency
    -   * 
    -   * @return integer result code
    -   * 
    -   * @internal 
    -   * 20081212 - franciscom - Postgres do not like SQL syntax with JOIN
    -   *  $sql = 'UPDATE testplan_tcversions ' .
    -   *  ' JOIN nodes_hierarchy NHA ON testplan_tcversions.tcversion_id = NHA.id '.
    -   *  ' JOIN nodes_hierarchy NHB ON NHA.parent_id = NHB.id' .
    -   *  ' SET urgency=' . $urgency .
    -   *  ' WHERE testplan_tcversions.testplan_id=' . $testplan_id .
    -   *  ' AND NHB.parent_id=' . $node_id; 
    -   */ 
    -  public function setSuiteUrgency($testplan_id, $node_id, $urgency)
    -  {
    -    $sql = " UPDATE {$this->tables['testplan_tcversions']} " . 
    -           " SET urgency=" . $this->db->prepare_int($urgency) .
    -           " WHERE testplan_id= " . $this->db->prepare_int($testplan_id) .
    -           " AND tcversion_id IN (" .
    -           " SELECT NHB.id " . 
    -           " FROM {$this->tables['nodes_hierarchy']}  NHA, " .
    -           " {$this->tables['nodes_hierarchy']} NHB, {$this->tables['node_types']} NT " .
    -           " WHERE NHA.node_type_id = NT.id " .
    -           " AND NT.description='testcase' " . 
    -           " AND NHB.parent_id = NHA.id " . 
    -           " AND NHA.parent_id = " . $this->db->prepare_int($node_id) . " )";
    -
    -    $result = $this->db->exec_query($sql);
    -    return $result ? OK : ERROR;;
    -  }
    -  
    -  /**
    -   * Collect urgency for a Test Suite within a Test Plan
    -   * 
    -   * @used-by planUrgency.php
    -   *
    -   *
    -   * @param integer $testplan_id Test Plan ID
    -   * @param integer $node_id Test Suite 
    -   * @param integer $testproject_id
    -   *
    -   * @return array of array testcase_id, name, urgency, tcprefix, tc_external_id 
    -   * 
    -   * @internal revisions
    -   */
    -  public function getSuiteUrgency($context,$options=null,$filters=null)
    -  {
    -
    -    $node_id = intval($context->tsuite_id); 
    -    $testplan_id = intval($context->tplan_id);
    -    $platform_id = property_exists($context, 'platform_id') ? intval($context->platform_id) : 0;
    -    $testproject_id = property_exists($context, 'tproject_id') ? intval($context->tproject_id) : null;
    -
    -    $testcase_cfg = config_get('testcase_cfg');  
    -    $moreFields = '';
    -    $moreJoins = '';
    -
    -    $my['options'] = array('build4testers' => 0);
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -    $my['filters'] = array('testcases' => null);
    -    $my['filters'] = array_merge($my['filters'], (array)$filters);
    -
    -    if( $my['options']['build4testers'] != 0 )
    -    {
    -      $tasks = $this->assignment_types;
    -
    -      // ATTENTION:
    -      // Remember that test case execution task can be assigned to MULTIPLE USERS
    -      $moreFields = ',USERS.login AS assigned_to, USERS.first, USERS.last ';
    -
    -      $moreJoins = " LEFT JOIN {$this->tables['user_assignments']} UA " .
    -                    " ON UA.feature_id = TPTCV.id " .
    -                    " AND UA.type = " . $tasks['testcase_execution']['id'] .
    -                    " AND UA.build_id = " . $my['options']['build4testers'] .
    -                    " LEFT JOIN {$this->tables['users']} USERS " .
    -                    " ON USERS.id = UA.user_id ";
    -    }     
    -
    -
    -
    -    $sql = " SELECT testprojects.prefix  FROM {$this->tables['testprojects']} testprojects " .
    -           " WHERE testprojects.id = ";
    -    
    -    if( !is_null($testproject_id) )
    -    {
    -      $sql .= intval($testproject_id);  
    -    }      
    -    else
    -    {
    -      $sql .= "( SELECT parent_id AS testproject_id FROM {$this->tables['nodes_hierarchy']} " .
    -              "  WHERE id=" . intval($testplan_id) . " ) ";
    -    }
    -    
    -    $tcprefix = $this->db->fetchOneValue($sql) . $testcase_cfg->glue_character;
    -    $tcprefix = $this->db->prepare_string($tcprefix);
    -    
    -    $sql = " SELECT DISTINCT '{$tcprefix}' AS tcprefix, NHB.name, NHB.node_order," .
    -           " NHA.parent_id AS testcase_id, TCV.tc_external_id, TPTCV.tcversion_id,".
    -           " TPTCV.urgency, TCV.importance, (TCV.importance * TPTCV.urgency) AS priority" .
    -           $moreFields .
    -           " FROM {$this->tables['nodes_hierarchy']} NHA " .
    -           " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " .
    -           " JOIN {$this->tables['testplan_tcversions']} TPTCV " .
    -           " ON TPTCV.tcversion_id=NHA.id " .
    -           " JOIN {$this->tables['tcversions']}  TCV ON TCV.id = TPTCV.tcversion_id " .
    -           $moreJoins;
    -
    -    $sql .= " WHERE TPTCV.testplan_id=" . $this->db->prepare_int($testplan_id) .
    -            " AND NHB.parent_id=" . $this->db->prepare_int($node_id);
    -
    -    if($platform_id > 0)
    -    {
    -      $sql .= " AND TPTCV.platform_id=" . $this->db->prepare_int($platform_id);
    -    }        
    -
    -    if( !is_null($my['filters']['testcases']) )
    -    {
    -      // sanitize
    -      $loop2do = count($my['filters']['testcases']);
    -      for($gdx=0; $gdx < $loop2do; $gdx++)
    -      {
    -        $my['filters']['testcases'][$gdx] = intval($my['filters']['testcases'][$gdx]);
    -      }  
    -      $sql .= " AND NHB.id IN (" . implode(",", $my['filters']['testcases']) . ") ";
    -    }  
    -
    -    $sql .= " ORDER BY NHB.node_order";
    -
    -    return $this->db->fetchRowsIntoMap($sql,'tcversion_id',database::CUMULATIVE);
    -  }
    -  
    -  /**
    -   * Returns priority (urgency * importance) as HIGH, MEDUIM or LOW depending on value
    -   * 
    -   *
    -   * @param integer $testplan_id Test Plan ID
    -   * @param  $filters: optional, map with following keys
    -   * @param  $options: optional, map with following keys
    -   *
    -   * @return 
    -   */
    -  public function getPriority($testplan_id, $filters=null, $options=null)
    -  {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -    $rs = null;
    -    $my = array ('filters' => array('platform_id' => null, 'tcversion_id' =>null), 
    -                 'options' => array('details' => 'tcversion'));
    -    $my['filters'] = array_merge($my['filters'], (array)$filters);
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -    $sqlFilter = '';
    -    if( !is_null($my['filters']['platform_id']) )
    -    {
    -      $sqlFilter .= " AND TPTCV.platform_id = {$my['filters']['platform_id']} ";
    -    }
    -
    -    if( !is_null($my['filters']['tcversion_id']) )
    -    {
    -      $dummy = implode(',',(array)$my['filters']['tcversion_id']);
    -      $sqlFilter .= " AND TPTCV.tcversion_id IN ({$dummy}) ";
    -    }
    -        
    -    $sql = "/* $debugMsg */ ";
    -    $sql .= " SELECT (urgency * importance) AS priority,  " .
    -            " urgency,importance, " .
    -            LOW . " AS priority_level, TPTCV.tcversion_id %CLAUSE%" .
    -            " FROM {$this->tables['testplan_tcversions']} TPTCV " .
    -            " JOIN {$this->tables['tcversions']} TCV ON TPTCV.tcversion_id = TCV.id " .
    -            " WHERE TPTCV.testplan_id = {$testplan_id} {$sqlFilter}";
    -          
    -    switch($my['options']['details'])
    -    {
    -      case 'tcversion':
    -        $sql = str_ireplace("%CLAUSE%", "", $sql);
    -        $rs = $this->db->fetchRowsIntoMap($sql,'tcversion_id');
    -      break;
    -
    -      case 'platform':
    -        $sql = str_ireplace("%CLAUSE%", ", TPTCV.platform_id", $sql);
    -        $rs = $this->db->fetchMapRowsIntoMap($sql,'tcversion_id','platform_id');
    -      break;
    -    }       
    -    
    -    if( !is_null($rs) )
    -    {
    -      $key2loop = array_keys($rs);
    -      switch($my['options']['details'])
    -      {
    -        case 'tcversion':
    -          foreach($key2loop as $key)
    -          {
    -            $rs[$key]['priority_level'] = priority_to_level($rs[$key]['priority']);
    -          }
    -        break;
    -
    -        case 'platform':
    -          foreach($key2loop as  $key)
    -          {
    -            $platformSet = array_keys($rs[$key]);
    -            foreach($platformSet as $platform_id) 
    -            {
    -              $rs[$key][$platform_id]['priority_level'] = priority_to_level($rs[$key][$platform_id]['priority']);
    -            }
    -          }
    -        break;
    -      } // switch
    -    } // !is_null
    -
    -    return $rs;
    -  } 
    -  
    -} // end of class
    \ No newline at end of file
    +tables['testplan_tcversions']}
    +             SET urgency=" . $this->db->prepare_int($urgency) .
    +            " WHERE testplan_id=" . $this->db->prepare_int($testplan_id) .
    +            " AND tcversion_id=" . $this->db->prepare_int($tc_id);
    +
    +        $result = $this->db->exec_query($sql);
    +
    +        return $result ? tl::OK : tl::ERROR;
    +    }
    +
    +    /**
    +     * Set urgency for TCs (direct child only) within a Test Suite and Test Plan
    +     *
    +     * @param integer $testplan_id
    +     *            Test Plan ID
    +     * @param integer $node_id
    +     *            Test Suite to set Urgency
    +     * @param integer $urgency
    +     *
    +     * @return integer result code
    +     *
    +     * @internal 20081212 - franciscom - Postgres do not like SQL syntax with JOIN
    +     *           $sql = 'UPDATE testplan_tcversions ' .
    +     *           ' JOIN nodes_hierarchy NHA ON testplan_tcversions.tcversion_id = NHA.id '.
    +     *           ' JOIN nodes_hierarchy NHB ON NHA.parent_id = NHB.id' .
    +     *           ' SET urgency=' . $urgency .
    +     *           ' WHERE testplan_tcversions.testplan_id=' . $testplan_id .
    +     *           ' AND NHB.parent_id=' . $node_id;
    +     */
    +    public function setSuiteUrgency($testplan_id, $node_id, $urgency)
    +    {
    +        $sql = " UPDATE {$this->tables['testplan_tcversions']} " .
    +            " SET urgency=" . $this->db->prepare_int($urgency) .
    +            " WHERE testplan_id= " . $this->db->prepare_int($testplan_id) .
    +            " AND tcversion_id IN (" . " SELECT NHB.id " .
    +            " FROM {$this->tables['nodes_hierarchy']}  NHA, " .
    +            " {$this->tables['nodes_hierarchy']} NHB, {$this->tables['node_types']} NT " .
    +            " WHERE NHA.node_type_id = NT.id " .
    +            " AND NT.description='testcase' " . " AND NHB.parent_id = NHA.id " .
    +            " AND NHA.parent_id = " . $this->db->prepare_int($node_id) . " )";
    +
    +        $result = $this->db->exec_query($sql);
    +        return $result ? OK : ERROR;
    +    }
    +
    +    /**
    +     * Collect urgency for a Test Suite within a Test Plan
    +     *
    +     * @used-by planUrgency.php
    +     *
    +     *
    +     * @param integer $testplan_id
    +     *            Test Plan ID
    +     * @param integer $node_id
    +     *            Test Suite
    +     * @param integer $testproject_id
    +     *
    +     * @return array of array testcase_id, name, urgency, tcprefix, tc_external_id
    +     *
    +     * @internal revisions
    +     */
    +    public function getSuiteUrgency($context, $options = null, $filters = null)
    +    {
    +        $node_id = intval($context->tsuite_id);
    +        $testplan_id = intval($context->tplan_id);
    +        $platform_id = property_exists($context, 'platform_id') ? intval(
    +            $context->platform_id) : 0;
    +        $testproject_id = property_exists($context, 'tproject_id') ? intval(
    +            $context->tproject_id) : null;
    +
    +        $testcase_cfg = config_get('testcase_cfg');
    +        $moreFields = '';
    +        $moreJoins = '';
    +
    +        $my['options'] = array(
    +            'build4testers' => 0
    +        );
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        $my['filters'] = array(
    +            'testcases' => null
    +        );
    +        $my['filters'] = array_merge($my['filters'], (array) $filters);
    +
    +        if ($my['options']['build4testers'] != 0) {
    +            $tasks = $this->assignment_types;
    +
    +            // ATTENTION:
    +            // Remember that test case execution task can be assigned to MULTIPLE USERS
    +            $moreFields = ',USERS.login AS assigned_to, USERS.first, USERS.last ';
    +
    +            $moreJoins = " LEFT JOIN {$this->tables['user_assignments']} UA " .
    +                " ON UA.feature_id = TPTCV.id " . " AND UA.type = " .
    +                $tasks['testcase_execution']['id'] . " AND UA.build_id = " .
    +                $my['options']['build4testers'] .
    +                " LEFT JOIN {$this->tables['users']} USERS " .
    +                " ON USERS.id = UA.user_id ";
    +        }
    +
    +        $sql = " SELECT testprojects.prefix  FROM {$this->tables['testprojects']} testprojects " .
    +            " WHERE testprojects.id = ";
    +
    +        if (! is_null($testproject_id)) {
    +            $sql .= intval($testproject_id);
    +        } else {
    +            $sql .= "( SELECT parent_id AS testproject_id FROM {$this->tables['nodes_hierarchy']} " .
    +                "  WHERE id=" . intval($testplan_id) . " ) ";
    +        }
    +
    +        $tcprefix = $this->db->fetchOneValue($sql) .
    +            $testcase_cfg->glue_character;
    +        $tcprefix = $this->db->prepare_string($tcprefix);
    +
    +        $sql = " SELECT DISTINCT '{$tcprefix}' AS tcprefix, NHB.name, NHB.node_order," .
    +            " NHA.parent_id AS testcase_id, TCV.tc_external_id, TPTCV.tcversion_id," .
    +            " TPTCV.urgency, TCV.importance, (TCV.importance * TPTCV.urgency) AS priority" .
    +            $moreFields . " FROM {$this->tables['nodes_hierarchy']} NHA " .
    +            " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " .
    +            " JOIN {$this->tables['testplan_tcversions']} TPTCV " .
    +            " ON TPTCV.tcversion_id=NHA.id " .
    +            " JOIN {$this->tables['tcversions']}  TCV ON TCV.id = TPTCV.tcversion_id " .
    +            $moreJoins;
    +
    +        $sql .= " WHERE TPTCV.testplan_id=" .
    +            $this->db->prepare_int($testplan_id) . " AND NHB.parent_id=" .
    +            $this->db->prepare_int($node_id);
    +
    +        if ($platform_id > 0) {
    +            $sql .= " AND TPTCV.platform_id=" .
    +                $this->db->prepare_int($platform_id);
    +        }
    +
    +        if (! is_null($my['filters']['testcases'])) {
    +            // sanitize
    +            $loop2do = count($my['filters']['testcases']);
    +            for ($gdx = 0; $gdx < $loop2do; $gdx ++) {
    +                $my['filters']['testcases'][$gdx] = intval(
    +                    $my['filters']['testcases'][$gdx]);
    +            }
    +            $sql .= " AND NHB.id IN (" .
    +                implode(",", $my['filters']['testcases']) . ") ";
    +        }
    +
    +        $sql .= " ORDER BY NHB.node_order";
    +
    +        return $this->db->fetchRowsIntoMap($sql, 'tcversion_id',
    +            database::CUMULATIVE);
    +    }
    +
    +    /**
    +     * Returns priority (urgency * importance) as HIGH, MEDUIM or LOW depending on value
    +     *
    +     *
    +     * @param integer $testplan_id
    +     *            Test Plan ID
    +     * @param $filters: optional,
    +     *            map with following keys
    +     * @param $options: optional,
    +     *            map with following keys
    +     *
    +     * @return
    +     */
    +    public function getPriority($testplan_id, $filters = null, $options = null)
    +    {
    +        $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    +        $rs = null;
    +        $my = array(
    +            'filters' => array(
    +                'platform_id' => null,
    +                'tcversion_id' => null
    +            ),
    +            'options' => array(
    +                'details' => 'tcversion'
    +            )
    +        );
    +        $my['filters'] = array_merge($my['filters'], (array) $filters);
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        $sqlFilter = '';
    +        if (! is_null($my['filters']['platform_id'])) {
    +            $sqlFilter .= " AND TPTCV.platform_id = {$my['filters']['platform_id']} ";
    +        }
    +
    +        if (! is_null($my['filters']['tcversion_id'])) {
    +            $dummy = implode(',', (array) $my['filters']['tcversion_id']);
    +            $sqlFilter .= " AND TPTCV.tcversion_id IN ({$dummy}) ";
    +        }
    +
    +        $sql = "/* $debugMsg */ ";
    +        $sql .= " SELECT (urgency * importance) AS priority,  " .
    +            " urgency,importance, " . LOW .
    +            " AS priority_level, TPTCV.tcversion_id %CLAUSE%" .
    +            " FROM {$this->tables['testplan_tcversions']} TPTCV " .
    +            " JOIN {$this->tables['tcversions']} TCV ON TPTCV.tcversion_id = TCV.id " .
    +            " WHERE TPTCV.testplan_id = {$testplan_id} {$sqlFilter}";
    +
    +        switch ($my['options']['details']) {
    +            case 'tcversion':
    +                $sql = str_ireplace("%CLAUSE%", "", $sql);
    +                $rs = $this->db->fetchRowsIntoMap($sql, 'tcversion_id');
    +                break;
    +
    +            case 'platform':
    +                $sql = str_ireplace("%CLAUSE%", ", TPTCV.platform_id", $sql);
    +                $rs = $this->db->fetchMapRowsIntoMap($sql, 'tcversion_id',
    +                    'platform_id');
    +                break;
    +        }
    +
    +        if (! is_null($rs)) {
    +            $key2loop = array_keys($rs);
    +            switch ($my['options']['details']) {
    +                case 'tcversion':
    +                    foreach ($key2loop as $key) {
    +                        $rs[$key]['priority_level'] = priority_to_level(
    +                            $rs[$key]['priority']);
    +                    }
    +                    break;
    +
    +                case 'platform':
    +                    foreach ($key2loop as $key) {
    +                        $platformSet = array_keys($rs[$key]);
    +                        foreach ($platformSet as $platform_id) {
    +                            $rs[$key][$platform_id]['priority_level'] = priority_to_level(
    +                                $rs[$key][$platform_id]['priority']);
    +                        }
    +                    }
    +                    break;
    +            }
    +        }
    +
    +        return $rs;
    +    }
    +}
    diff --git a/lib/functions/testcase.class.php b/lib/functions/testcase.class.php
    index 61f3b5dd8b..522fa82081 100644
    --- a/lib/functions/testcase.class.php
    +++ b/lib/functions/testcase.class.php
    @@ -1,2492 +1,2850 @@
    - lang_get('the_format_tc_xml_import'));
    -
    -/**
    - * class for Test case CRUD
    - * @package   TestLink
    - */
    -class testcase extends tlObjectWithAttachments {
    -  const AUTOMATIC_ID=0;
    -  const DEFAULT_ORDER=0;
    -  const ALL_VERSIONS=0;
    -  const LATEST_VERSION=-1;
    -  const AUDIT_OFF=0;
    -  const AUDIT_ON=1;
    -  const CHECK_DUPLICATE_NAME=1;
    -  const DONT_CHECK_DUPLICATE_NAME=0;
    -  const ENABLED=1;
    -  const ALL_TESTPLANS=null;
    -  const ANY_BUILD=null;
    -  const GET_NO_EXEC=1;
    -  const ANY_PLATFORM=null;
    -  const NOXMLHEADER=true;
    -  const EXECUTION_TYPE_MANUAL = 1;
    -  const EXECUTION_TYPE_AUTO = 2;
    -
    -  const NAME_PHOPEN = '[[';
    -  const NAME_PHCLOSE = ']]';
    -  const NAME_DIVIDE = '::';
    -  const GHOSTBEGIN = '[ghost]';
    -  const GHOSTEND = '[/ghost]';
    -  const GHOSTMASK = self::GHOSTBEGIN . 
    -    '"TestCase":"%s","Version":"%s"' . self::GHOSTEND;
    -  const GHOSTSTEPMASK = self::GHOSTBEGIN .
    -    '"Step":"%s","TestCase":"%s","Version":"%s"' . self::GHOSTEND;
    -
    -  /** @var database handler */
    -  var $db;
    -  var $tree_manager;
    -  var $tproject_mgr;
    -
    -  var $node_types_descr_id;
    -  var $node_types_id_descr;
    -  var $my_node_type;
    -
    -  var $assignment_mgr;
    -  var $assignment_types;
    -  var $assignment_status;
    -
    -  var $cfield_mgr;
    -
    -  var $import_file_types = array("XML" => "XML");
    -  var $export_file_types = array("XML" => "XML");
    -  var $execution_types = array();
    -  var $cfg;
    -  var $debugMsg;
    -  var $layout;
    -  var $XMLCfg;
    -  var $tproject_id;
    -
    -  /**
    -   * testcase class constructor
    -   *
    -   * @param resource &$db reference to database handler
    -   */
    -  function __construct(&$db) {
    -    $this->db = &$db;
    -    $this->tproject_mgr = new testproject($this->db);
    -    $this->tree_manager = &$this->tproject_mgr->tree_manager;
    -
    -    $this->node_types_descr_id=$this->tree_manager->get_available_node_types();
    -    $this->node_types_id_descr=array_flip($this->node_types_descr_id);
    -    $this->my_node_type=$this->node_types_descr_id['testcase'];
    -
    -    $this->assignment_mgr=New assignment_mgr($this->db);
    -    $this->assignment_types=$this->assignment_mgr->get_available_types();
    -    $this->assignment_status=$this->assignment_mgr->get_available_status();
    -
    -    $this->cfield_mgr = new cfield_mgr($this->db);
    -
    -    $this->execution_types = $this->getExecutionTypes();
    -
    -    $this->layout = $this->getLayout();
    -
    -    $this->cfg = new stdClass();
    -    $this->cfg->testcase = config_get('testcase_cfg');
    -    $this->cfg->execution = config_get('exec_cfg');
    -    $this->cfg->cfield = config_get('custom_fields');
    -
    -    $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: ';
    -
    -
    -    $this->XMLCfg = new stdClass();
    -    $this->XMLCfg->att = $this->getAttXMLCfg();
    -    $this->XMLCfg->req = $this->getReqXMLCfg();
    -
    -    // ATTENTION:
    -    // second argument is used to set $this->attachmentTableName,property that this calls
    -    // get from his parent
    -    // ORIGINAL
    -    // parent::__construct($this->db,"nodes_hierarchy");
    -    parent::__construct($this->db,"tcversions");
    -  }
    -
    -  /**
    -   *
    -   */
    -  function setTestProject($tproject_id) {
    -    $this->tproject_id = intval($tproject_id);
    -  }
    -
    -  /**
    -   *
    -   */
    -  static function getExecutionTypes() {
    -    $stdSet = array(self::EXECUTION_TYPE_MANUAL => lang_get('manual'),
    -                    self::EXECUTION_TYPE_AUTO => lang_get('automated'));
    -
    -    if( !is_null($customSet = config_get('custom_execution_types')) )
    -    {
    -      foreach($customSet as $code => $lbl)
    -      {
    -        $stdSet[$code] = lang_get($lbl);
    -      }
    -    }
    -    return $stdSet;
    -  }
    -
    -
    -  /**
    -   *
    -   */
    -  function getName($tcase_id) {
    -    $info  = $this->tree_manager->get_node_hierarchy_info($tcase_id);
    -    return $info['name'];
    -  }
    -
    -  /**
    -   *
    -   */
    -  function getFileUploadRelativeURL($identity) {
    -    $url = "lib/testcases/tcEdit.php?doAction=fileUpload&" . 
    -           "&tcase_id=" . intval($identity->tcase_id) .
    -           "&tcversion_id=" . intval($identity->tcversion_id) .
    -           "&tproject_id=" . intval($identity->tproject_id);
    -    return $url;
    -  }
    -
    -  /**
    -   *
    -   */
    -  function getDeleteAttachmentRelativeURL($identity) {
    -    $url = "lib/testcases/tcEdit.php?doAction=deleteFile&tcase_id=" . intval($identity->tcase_id) .
    -           "&tcversion_id=" . intval($identity->tcversion_id) .
    -           "&tproject_id=" . intval($identity->tproject_id) . "&file_id=" ;
    -
    -    return $url;
    -  }
    -
    -
    -  /**
    -   *
    -   */
    -  function getDeleteAttachmentByIDRelativeURL($identity,&$guiObj=null) {
    -    $url = "lib/testcases/tcEdit.php?doAction=deleteFile&tcase_id=" . 
    -           intval($identity->tcase_id) .
    -           "&tproject_id=" . intval($identity->tproject_id) . "&file_id=" ;
    -
    -    // needed for IVU 2019 implementation
    -    if( null != $guiObj ) {
    -      $p2l = array('show_mode','tplan_id');
    -      foreach($p2l as $pr) {
    -        if( property_exists($guiObj, $pr) ) {
    -          $url .= "&$pr=" . $guiObj->$pr;
    -        }        
    -      }
    -    }        
    -
    -    $url .= "&file_id=" ;
    -    return $url;
    -  }
    -
    -  /**
    -   *
    -   */
    -  function getDeleteTCVRelationRelativeURL($identity,&$guiObj=null) {
    -    $url = "lib/testcases/tcEdit.php?doAction=doDeleteRelation";
    -
    -    // needed for IVU 2019 implementation
    -    if( null != $guiObj ) {
    -      $p2l = array('show_mode','tplan_id');
    -      foreach($p2l as $pr) {
    -        if( property_exists($guiObj, $pr) ) {
    -          $url .= "&$pr=" . $guiObj->$pr;
    -        }        
    -      }
    -    }        
    -           
    -    $url .= '&tcase_id=%1&relation_id=%2';
    -
    -    return $url;
    -  }
    -
    -  /**
    -   *
    -   */
    -  function getDeleteTCVKeywordRelativeURL($identity,&$guiObj=null) {
    -    $url = "lib/testcases/tcEdit.php?doAction=removeKeyword";
    -
    -    // needed for IVU 2019 implementation
    -    if( null != $guiObj ) {
    -      $p2l = array('show_mode','tplan_id');
    -      foreach($p2l as $pr) {
    -        if( property_exists($guiObj, $pr) ) {
    -          $url .= "&$pr=" . $guiObj->$pr;
    -        }        
    -      }
    -    }        
    -           
    -    $url .= '&tcase_id=%1&tckw_link_id=%2';
    -    return $url;
    -  }
    -
    -
    -  /*
    -    function: get_export_file_types
    -              getter
    -
    -    args: -
    -
    -    returns: map
    -             key: export file type code
    -             value: export file type verbose description
    -
    -  */
    -  function get_export_file_types()
    -  {
    -    return $this->export_file_types;
    -  }
    -
    -  /*
    -    function: get_impor_file_types
    -              getter
    -
    -    args: -
    -
    -    returns: map
    -             key: import file type code
    -             value: import file type verbose description
    -
    -  */
    -  function get_import_file_types()
    -  {
    -    return $this->import_file_types;
    -  }
    -
    -  /*
    -     function: get_execution_types
    -               getter
    -
    -     args: -
    -
    -     returns: map
    -              key: execution type code
    -              value: execution type verbose description
    -
    -  */
    -  function get_execution_types()
    -  {
    -    return $this->execution_types;
    -  }
    -
    -
    -  /**
    -   *  just a wrapper
    -   *
    -   */
    -  function createFromObject($item) {
    -    static $wkfstatus;
    -
    -    if(is_null($wkfstatus)) {
    -      $wkfstatus = config_get('testCaseStatus');
    -    }
    -    $options = array('check_duplicate_name' => self::CHECK_DUPLICATE_NAME,
    -                     'action_on_duplicate_name' => 'block',
    -                     'estimatedExecDuration' => 0,
    -                     'status' => $wkfstatus['draft'], 'importLogic' => null);
    -
    -    if(property_exists($item, 'estimatedExecDuration')) {
    -      $options['estimatedExecDuration'] = floatval($item->estimatedExecDuration);
    -    }
    -
    -    if(property_exists($item, 'status')) {
    -      $options['status'] = intval($item->status);
    -    }
    -
    -
    -    if(property_exists($item, 'importLogic')) {
    -      $options['importLogic'] = $item->importLogic;
    -    }
    -
    -    $ret = $this->create($item->testSuiteID,$item->name,$item->summary,$item->preconditions,
    -                         $item->steps,$item->authorID,'',$item->order,self::AUTOMATIC_ID,
    -                         $item->executionType,$item->importance,$options);
    -    return $ret;
    -  }
    -
    -  /**
    -   * create a test case
    -   *
    -   */
    -  function create($parent_id,$name,$summary,$preconditions,$steps,$author_id,
    -                  $keywords_id='',$tc_order=self::DEFAULT_ORDER,$id=self::AUTOMATIC_ID,
    -                  $execution_type = TESTCASE_EXECUTION_TYPE_MANUAL,
    -                  $importance=2,$options=null) {
    -    $status_ok = 1;
    -
    -    $my['options'] = array( 'check_duplicate_name' => self::DONT_CHECK_DUPLICATE_NAME,
    -                            'action_on_duplicate_name' => 'generate_new',
    -                            'estimatedExecDuration' => null,'status' => null,'active' => null,'is_open' => null,
    -                            'importLogic' => null);
    -
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -    if( trim($summary) != '' ) {
    -      if( strpos($summary,self::NAME_PHOPEN) !== FALSE && strpos($summary,self::NAME_PHCLOSE) !== FALSE ) {
    -        $name = $this->buildTCName($name,$summary);
    -      }
    -    }
    -
    -    if( trim($preconditions) != '' ) {
    -      if( strpos($preconditions,self::NAME_PHOPEN) !== FALSE && 
    -          strpos($preconditions,self::NAME_PHCLOSE) !== FALSE ) {
    -        $name = $this->buildTCName($name,$preconditions);
    -      }
    -    }
    -
    -    $ret = $this->create_tcase_only($parent_id,$name,$tc_order,$id,$my['options']);
    -
    -
    -    $tcase_id = $ret['id'];
    -    $ix = new stdClass();
    -
    -    if($ret["status_ok"]) {
    -      $ix->version = 1;
    -      if(isset($ret['version_number']) && $ret['version_number'] < 0) {
    -        // We are in the special situation we are only creating a new version,
    -        // useful when importing test cases. Need to get last version number.
    -        // I do not use create_new_version() because it does a copy ot last version
    -        // and do not allow to set new values in different fields while doing this operation.
    -        $last_version_info = $this->get_last_version_info($ret['id'],array('output' => 'minimun'));
    -
    -        $ix->version = $last_version_info['version']+1;
    -        $ret['msg'] = sprintf($ret['msg'],$ix->version);
    -        $ret['version_number'] = $ix->version;
    -      }
    -
    -      // Multiple Test Case Steps Feature
    -      $version_number = $ret['version_number'];
    -
    -      $ix->id = $tcase_id;
    -      $ix->externalID = $ret['external_id'];
    -      $ix->summary = $summary;
    -      $ix->preconditions = $preconditions;
    -      $ix->steps = $steps;
    -      $ix->authorID = $author_id;
    -      $ix->executionType = $execution_type;
    -      $ix->importance = $importance;
    -      $ix->status = $my['options']['status'];
    -      $ix->active = $my['options']['active'];
    -      $ix->is_open = $my['options']['is_open'];
    -      $ix->estimatedExecDuration = $my['options']['estimatedExecDuration'];
    -
    -
    -      $op = $this->createVersion($ix);
    -      if(trim($keywords_id) != "") {
    -        $a_keywords = explode(",",$keywords_id);
    -        $auditContext = array('on' => self::AUDIT_ON, 
    -                              'version' => $version_number);
    -        $this->addKeywords($tcase_id,$op['id'],$a_keywords,$auditContext);
    -      }
    -
    -      if($ret['update_name']) {
    -        $sql = " UPDATE {$this->tables['nodes_hierarchy']} SET name='" .
    -               $this->db->prepare_string($name) . "' WHERE id= " . intval($ret['id']);
    -        $this->db->exec_query($sql);
    -      }
    -
    -      $ret['msg'] = $op['status_ok'] ? $ret['msg'] : $op['msg'];
    -      $ret['tcversion_id'] = $op['status_ok'] ? $op['id'] : -1;
    -
    -      $ctx = array('test_suite_id' => $parent_id,'id' => $id,'name' => $name,
    -                   'summary' => $summary,'preconditions' => $preconditions,
    -                   'steps' => $steps,'author_id' => $author_id,
    -                   'keywords_id' => $keywords_id,
    -                   'order' => $tc_order, 'exec_type' => $execution_type,
    -                   'importance' => $importance,'options' => $options);
    -      event_signal('EVENT_TEST_CASE_CREATE', $ctx);
    -    }
    -    return $ret;
    -  }
    -
    -  /*
    -    [$check_duplicate_name]
    -    [$action_on_duplicate_name]
    -    [$order]
    -
    -    [$id]
    -         0 -> the id will be assigned by dbms
    -         x -> this will be the id
    -              Warning: no check is done before insert => can got error.
    -
    -  return:
    -         $ret['id']
    -         $ret['external_id']
    -         $ret['status_ok']
    -         $ret['msg'] = 'ok';
    -         $ret['new_name']
    -
    -  rev:
    -
    -  */
    -  function create_tcase_only($parent_id,$name,$order=self::DEFAULT_ORDER,$id=self::AUTOMATIC_ID,
    -                             $options=null) {
    -    $dummy = config_get('field_size');
    -    $name_max_len = $dummy->testcase_name;
    -    $name = trim($name);
    -    $originalNameLen = tlStringLen($name);
    -
    -    $getOptions = array();
    -    $ret = array('id' => -1,'external_id' => 0, 'status_ok' => 1,'msg' => 'ok',
    -                 'new_name' => '', 'version_number' => 1, 'has_duplicate' => false,
    -                 'external_id_already_exists' => false, 'update_name' => false);
    -
    -    $my['options'] = array('check_duplicate_name' => self::DONT_CHECK_DUPLICATE_NAME,
    -                           'action_on_duplicate_name' => 'generate_new',
    -                           'external_id' => null, 'importLogic' => null);
    -
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -    $doCreate=true;
    -    $forceGenerateExternalID = false;
    -
    -    $algo_cfg = config_get('testcase_cfg')->duplicated_name_algorithm;
    -    $getDupOptions['check_criteria'] = ($algo_cfg->type == 'counterSuffix') ? 'like' : '=';
    -    $getDupOptions['access_key'] = ($algo_cfg->type == 'counterSuffix') ? 'name' : 'id';
    -
    -
    -
    -    // If external ID has been provided, check if exists.
    -    // If answer is yes, then
    -    // 1. collect current info
    -    // 2. if $my['options']['check_duplicate_name'] is create new version
    -    //    change to BLOCK
    -    //
    -    if( !is_null($my['options']['importLogic']) ) {
    -      $doQuickReturn = false;
    -      switch($my['options']['importLogic']['hitCriteria']) {
    -        case 'externalID':
    -          if( ($sf = intval($my['options']['external_id'])) > 0 ) {
    -            // check if already exists a test case with this external id
    -            $info = $this->get_by_external($sf, $parent_id);
    -            if( !is_null($info)) {
    -              if( count($info) > 1) {
    -                // abort
    -                throw new Exception("More than one test case with same external ID");
    -              }
    -
    -              $doQuickReturn = true;
    -              $ret['id'] = key($info);
    -              $ret['external_id'] = $sf;
    -              $ret['version_number'] = -1;
    -              $ret['external_id_already_exists'] = true;
    -            }
    -          }
    -
    -          switch($my['options']['importLogic']['actionOnHit']) {
    -            case 'create_new_version':
    -              if($doQuickReturn) {
    -                // I this situation we will need to also update test case name, if user
    -                // has provided one on import file.
    -                // Then we need to check that new name will not conflict with an existing one
    -                $doCreate = false;
    -                if( strcmp($info[key($info)]['name'],$name) != 0) {
    -                  $itemSet = $this->getDuplicatesByName($name,$parent_id,$getDupOptions);
    -                  if( is_null($itemSet) ) {
    -                    $ret['name'] = $name;
    -                    $ret['update_name'] = true;
    -                  }
    -                }
    -                return $ret;
    -              }
    -            break;
    -
    -            case 'generate_new':
    -              // on GUI => create a new test case with a different title
    -              // IMPORTANT:
    -              // if name provided on import file does not hit an existent one
    -              // then I'm going to use it, instead of generating a NEW NAME
    -              $forceGenerateExternalID = true;
    -            break;
    -          }
    -        break;
    -      }
    -    }
    -
    -
    -    if ($my['options']['check_duplicate_name']) {
    -      $itemSet = $this->getDuplicatesByName($name,$parent_id,$getDupOptions);
    -
    -      if( !is_null($itemSet) && ($siblingQty=count($itemSet)) > 0 ) {
    -        $ret['has_duplicate'] = true;
    -
    -        switch($my['options']['action_on_duplicate_name']) {
    -            case 'block':
    -              $doCreate=false;
    -              $ret['status_ok'] = 0;
    -              $ret['msg'] = sprintf(lang_get('testcase_name_already_exists'),$name);
    -            break;
    -
    -            case 'generate_new':
    -              $doCreate=true;
    -
    -              // TICKET 5159: importing duplicate test suites
    -              // Need to force use of generated External ID
    -              // (this seems the best alternative)
    -              $my['options']['external_id'] = null;
    -
    -              switch($algo_cfg->type) {
    -                case 'stringPrefix':
    -                  $doIt = true;
    -                  while($doIt) {
    -                    if( $doIt = !is_null($itemSet) ) {
    -                      $prefix = strftime($algo_cfg->text,time());
    -                      $target = $prefix . " " . $name ;
    -                      $final_len = strlen($target);
    -                      if( $final_len > $name_max_len)
    -                      {
    -                        $target = substr($target,0,$name_max_len);
    -                      }
    -
    -                      // Check new generated name
    -                      $itemSet = $this->getDuplicatesByName($target,$parent_id,$getDupOptions);
    -                    }
    -                  }
    -                  $name = $target;
    -                break;
    -
    -                case 'counterSuffix':
    -                  $mask =  !is_null($algo_cfg->text) ? $algo_cfg->text : '#%s';
    -                  $nameSet = array_flip(array_keys($itemSet));
    -
    -                  // 20110109 - franciscom
    -                  // does not understand why I've choosen time ago
    -                  // to increment $siblingQty before using it
    -                  // This way if TC X exists on target parent
    -                  // I will create TC X [2] insteand of TC X [1]
    -                  // Anyway right now I will not change.
    -                  $target = $name . ($suffix = sprintf($mask,++$siblingQty));
    -                  $final_len = strlen($target);
    -                  if( $final_len > $name_max_len) {
    -                    $target = substr($target,strlen($suffix),$name_max_len);
    -                  }
    -
    -                  // Need to recheck if new generated name does not crash with existent name
    -                  // why? Suppose you have created:
    -                  // TC [1]
    -                  // TC [2]
    -                  // TC [3]
    -                  // Then you delete TC [2].
    -                  // When I got siblings  il will got 2 siblings, if I create new progressive using next,
    -                  // it will be 3 => I will get duplicated name.
    -                  while( isset($nameSet[$target]) )
    -                  {
    -                    $target = $name . ($suffix = sprintf($mask,++$siblingQty));
    -                    $final_len = strlen($target);
    -                    if( $final_len > $name_max_len) {
    -                      $target = substr($target,strlen($suffix),$name_max_len);
    -                    }
    -                  }
    -                  $name = $target;
    -                break;
    -              }
    -
    -              $ret['status_ok'] = 1;
    -              $ret['new_name'] = $name;
    -              $ret['msg'] = sprintf(lang_get('created_with_title'),$name);
    -            break;
    -
    -            case 'create_new_version':
    -              $doCreate = false;
    -
    -              // If we found more that one with same name and same parent,
    -              // will take the first one.
    -              $xx = current($itemSet);
    -              $ret['id'] = $xx['id'];
    -              $ret['external_id']=$xx['tc_external_id'];
    -              $ret['status_ok'] = 1;
    -              $ret['new_name'] = $name;
    -              $ret['version_number'] = -1;
    -              $ret['msg'] = lang_get('create_new_version');
    -            break;
    -
    -            default:
    -            break;
    -        }
    -      }
    -    }
    -
    -    // 20120822 - think we have potencial issue, because we never check if
    -    // duplicated EXTERNAL ID exists.
    -    // Right now there is no time to try a fix
    -    if( $ret['status_ok'] && $doCreate)
    -    {
    -
    -      $safeLenName = tlSubStr($name, 0, $name_max_len);
    -
    -      // Get tproject id
    -      $path2root = $this->tree_manager->get_path($parent_id);
    -      $tproject_id = $path2root[0]['parent_id'];
    -
    -      $tcase_id = $this->tree_manager->new_node($parent_id,$this->my_node_type,$safeLenName,$order,$id);
    -      $ret['id'] = $tcase_id;
    -
    -      $generateExtID = false;
    -      if( $forceGenerateExternalID || is_null($my['options']['external_id']) )
    -      {
    -        $generateExtID = true;
    -      }
    -      else
    -      {
    -        // this need more work and checks (20140209)
    -        $sf = intval($my['options']['external_id']);
    -        if( is_null($this->get_by_external($sf, $parent_id)) )
    -        {
    -          $ret['external_id'] = $sf;
    -
    -          // CRITIC: setTestCaseCounter() will update only if new provided value > current value
    -          $this->tproject_mgr->setTestCaseCounter($tproject_id,$ret['external_id']);
    -
    -        }
    -        else
    -        {
    -          $generateExtID = true;
    -        }
    -
    -      }
    -      if( $generateExtID )
    -      {
    -        $ret['external_id'] = $this->tproject_mgr->generateTestCaseNumber($tproject_id);
    -      }
    -
    -      if( !$ret['has_duplicate'] && ($originalNameLen > $name_max_len) )
    -      {
    -        $ret['new_name'] = $safeLenName;
    -        $ret['msg'] = sprintf(lang_get('testcase_name_length_exceeded'),$originalNameLen,$name_max_len);
    -      }
    -    }
    -
    -    return $ret;
    -  }
    -
    -
    -  /**
    -   *  trying to solve tree_manager->new_node($item->id,
    -                        $this->node_types_descr_id['testcase_version']);
    -
    -    $this->CKEditorCopyAndPasteCleanUp($item,array('summary','preconditions')); 
    -
    -    $sql = "/* $debugMsg */ INSERT INTO {$this->tables['tcversions']} " .
    -           " (id,tc_external_id,version,summary,preconditions," .
    -           "  author_id,creation_ts,execution_type,importance ";
    -
    -    $sqlValues = " VALUES({$tcase_version_id},{$item->externalID},{$item->version},'" .
    -                 $this->db->prepare_string($item->summary) . "','" .
    -                 $this->db->prepare_string($item->preconditions) . "'," .
    -                 $this->db->prepare_int($item->authorID) . "," . $this->db->db_now() .
    -                 ", {$item->executionType},{$item->importance} ";
    -
    -
    -    if( !is_null($item->status) ) {
    -      $wf = intval($item->status);
    -      $sql .= ',status';
    -      $sqlValues .= ",{$wf}";
    -    }
    -
    -    if( !is_null($item->estimatedExecDuration) ) {
    -      $v = trim($item->estimatedExecDuration);
    -      if($v != '') {
    -        $sql .= ", estimated_exec_duration";
    -        $sqlValues .= "," . floatval($v);
    -      }
    -    }
    -
    -    if( property_exists($item,'active') && !is_null($item->active) ) {
    -      $v = intval($item->active) > 0 ? 1 : 0;
    -      $sql .= ", active";
    -      $sqlValues .= "," . $v;
    -    }
    -	  
    -    if( property_exists($item,'is_open') && !is_null($item->is_open) ) {
    -      $v = intval($item->is_open) > 0 ? 1 : 0;
    -      $sql .= ", is_open";
    -      $sqlValues .= "," . $v;
    -    }
    -
    -    $sql .= " )" . $sqlValues . " )";
    -
    -
    -
    -    $result = $this->db->exec_query($sql);
    -
    -    $ret['msg']='ok';
    -    $ret['id'] = $tcase_version_id;
    -    $ret['status_ok']=1;
    -
    -    if ($result && ( !is_null($item->steps) && is_array($item->steps) ) ) {
    -      $steps2create = count($item->steps);
    -      $op['status_ok'] = 1;
    -
    -      // need to this to manage call to this method for REST API.
    -      $stepIsObject =  is_object($item->steps[0]);
    -      for($jdx=0 ; ($jdx < $steps2create && $op['status_ok']); $jdx++) {
    -        if($stepIsObject) {
    -          $item->steps[$jdx] = (array)$item->steps[$jdx];
    -        }
    -
    -        $this->create_step($tcase_version_id,
    -                           $item->steps[$jdx]['step_number'],
    -                           $item->steps[$jdx]['actions'],
    -                           $item->steps[$jdx]['expected_results'],
    -                           $item->steps[$jdx]['execution_type']);
    -      }
    -    }
    -
    -    if (!$result) {
    -      $ret['msg'] = $this->db->error_msg();
    -      $ret['status_ok']=0;
    -      $ret['id']=-1;
    -    }
    -
    -    return $ret;
    -  }
    -
    -
    -  /*
    -    function: getDuplicatesByname
    -
    -    args: $name
    -          $parent_id
    -
    -    returns: hash
    -  */
    -  function getDuplicatesByName($name, $parent_id, $options=null) {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -
    -    $my['options'] = array( 'check_criteria' => '=', 'access_key' => 'id', 'id2exclude' => null);
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -    $target = $this->db->prepare_string($name);
    -    switch($my['options']['check_criteria']) {
    -      case '=':
    -      default:
    -        $check_criteria = " AND NHA.name = '{$target}' ";
    -      break;
    -
    -      case 'like':
    -        // % and _ need to be escaped, but method is different
    -        // according DBMS
    -        $check_criteria = " AND NHA.name LIKE '{$target}%' ";
    -      break;
    -
    -    }
    -
    -    $sql = " SELECT DISTINCT NHA.id,NHA.name,TCV.tc_external_id" .
    -           " FROM {$this->tables['nodes_hierarchy']} NHA, " .
    -           " {$this->tables['nodes_hierarchy']} NHB, {$this->tables['tcversions']} TCV  " .
    -           " WHERE NHA.node_type_id = {$this->my_node_type} " .
    -           " AND NHB.parent_id=NHA.id " .
    -           " AND TCV.id=NHB.id " .
    -           " AND NHB.node_type_id = {$this->node_types_descr_id['testcase_version']} " .
    -           " AND NHA.parent_id=" . $this->db->prepare_int($parent_id) . " {$check_criteria}";
    -
    -    if( !is_null($my['options']['id2exclude']) ) {
    -      $sql .= " AND NHA.id <> " . intval($my['options']['id2exclude']);
    -    }
    -
    -    $rs = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']);
    -
    -    if( is_null($rs) || count($rs) == 0 ) {
    -      $rs=null;
    -    }
    -    return $rs;
    -  }
    -
    -
    -
    -
    -  /*
    -    function: get_by_name
    -
    -    args: $name
    -          [$tsuite_name]: name of parent test suite
    -          [$tproject_name]
    -
    -    returns: hash
    -
    -    @internal revisions
    -    20100831 - franciscom - BUGID 3729
    -
    -  */
    -  function get_by_name($name, $tsuite_name = '', $tproject_name = '')
    -  {
    -
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -
    -    $recordset = null;
    -    $filters_on = array('tsuite_name' => false, 'tproject_name' => false);
    -    $field_size = config_get('field_size');
    -    $tsuite_name = tlSubStr(trim($tsuite_name),0, $field_size->testsuite_name);
    -    $tproject_name = tlSubStr(trim($tproject_name),0,$field_size->testproject_name);
    -    $name = tlSubStr(trim($name), 0, $field_size->testcase_name);
    -
    -    $sql = "/* $debugMsg */ " .
    -           " SELECT DISTINCT NH_TCASE.id,NH_TCASE.name,NH_TCASE_PARENT.id AS parent_id," .
    -           " NH_TCASE_PARENT.name AS tsuite_name, TCV.tc_external_id " .
    -           " FROM {$this->tables['nodes_hierarchy']} NH_TCASE, " .
    -           " {$this->tables['nodes_hierarchy']} NH_TCASE_PARENT, " .
    -           " {$this->tables['nodes_hierarchy']} NH_TCVERSIONS," .
    -           " {$this->tables['tcversions']}  TCV  " .
    -           " WHERE NH_TCASE.node_type_id = {$this->my_node_type} " .
    -           " AND NH_TCASE.name = '{$this->db->prepare_string($name)}' " .
    -           " AND TCV.id=NH_TCVERSIONS.id " .
    -           " AND NH_TCVERSIONS.parent_id=NH_TCASE.id " .
    -           " AND NH_TCASE_PARENT.id=NH_TCASE.parent_id ";
    -
    -    if($tsuite_name != "")
    -    {
    -      $sql .= " AND NH_TCASE_PARENT.name = '{$this->db->prepare_string($tsuite_name)}' " .
    -              " AND NH_TCASE_PARENT.node_type_id = {$this->node_types_descr_id['testsuite']} ";
    -    }
    -    $recordset = $this->db->get_recordset($sql);
    -    if(count($recordset) && $tproject_name != "")
    -    {
    -      list($tproject_info)=$this->tproject_mgr->get_by_name($tproject_name);
    -      foreach($recordset as $idx => $tcase_info)
    -      {
    -        if( $this->get_testproject($tcase_info['id']) != $tproject_info['id'] )
    -        {
    -          unset($recordset[$idx]);
    -        }
    -      }
    -    }
    -    return $recordset;
    -  }
    -
    -
    -  /*
    -  get array of info for every test case
    -  without any kind of filter.
    -  Every array element contains an assoc array with testcase info
    -
    -  */
    -  function get_all() {
    -    $sql = " SELECT nodes_hierarchy.name, nodes_hierarchy.id
    + lang_get('the_format_tc_xml_import')
    +);
    +
    +/**
    + * class for Test case CRUD
    + *
    + * @package TestLink
    + */
    +class testcase extends tlObjectWithAttachments
    +{
    +
    +    const AUTOMATIC_ID = 0;
    +
    +    const DEFAULT_ORDER = 0;
    +
    +    const ALL_VERSIONS = 0;
    +
    +    const LATEST_VERSION = - 1;
    +
    +    const AUDIT_OFF = 0;
    +
    +    const AUDIT_ON = 1;
    +
    +    const CHECK_DUPLICATE_NAME = 1;
    +
    +    const DONT_CHECK_DUPLICATE_NAME = 0;
    +
    +    const ENABLED = 1;
    +
    +    const ALL_TESTPLANS = null;
    +
    +    const ANY_BUILD = null;
    +
    +    const GET_NO_EXEC = 1;
    +
    +    const ANY_PLATFORM = null;
    +
    +    const NOXMLHEADER = true;
    +
    +    const EXECUTION_TYPE_MANUAL = 1;
    +
    +    const EXECUTION_TYPE_AUTO = 2;
    +
    +    const NAME_PHOPEN = '[[';
    +
    +    const NAME_PHCLOSE = ']]';
    +
    +    const NAME_DIVIDE = '::';
    +
    +    const GHOSTBEGIN = '[ghost]';
    +
    +    const GHOSTEND = '[/ghost]';
    +
    +    const GHOST_TC_VERSION = '"TestCase":"%s","Version":"%s"';
    +
    +    const GHOSTMASK = self::GHOSTBEGIN . self::GHOST_TC_VERSION . self::GHOSTEND;
    +
    +    const GHOSTSTEPMASK = self::GHOSTBEGIN . '"Step":"%s",' .
    +        self::GHOST_TC_VERSION . self::GHOSTEND;
    +
    +    const GHOSTPRECONDITIONSMASK = self::GHOSTBEGIN . '"Preconditions":"x",' .
    +        self::GHOST_TC_VERSION . self::GHOSTEND;
    +
    +    /**
    +     *
    +     * @var database handler
    +     */
    +    protected $db;
    +
    +    public $tree_manager;
    +
    +    /**
    +     *
    +     * @var \testproject
    +     */
    +    public $tproject_mgr;
    +
    +    private array $node_types_descr_id;
    +
    +    private array $node_types_id_descr;
    +
    +    public $my_node_type;
    +
    +    private \assignment_mgr $assignment_mgr;
    +
    +    private array $assignment_types;
    +
    +    private array $assignment_status;
    +
    +    /**
    +     *
    +     * @var \cfield_mgr
    +     */
    +    public $cfield_mgr;
    +
    +    private array $import_file_types = array(
    +        "XML" => "XML"
    +    );
    +
    +    private array $export_file_types = array(
    +        "XML" => "XML"
    +    );
    +
    +    private $execution_types = array();
    +
    +    private \stdClass $cfg;
    +
    +    private string $debugMsg;
    +
    +    private $layout;
    +
    +    private \stdClass $XMLCfg;
    +
    +    private int $tproject_id;
    +
    +    private $keywordAnnotations = [];
    +
    +    /**
    +     * testcase class constructor
    +     *
    +     * @param
    +     *            resource &$db reference to database handler
    +     */
    +    public function __construct(&$db)
    +    {
    +        $this->db = &$db;
    +        $this->tproject_mgr = new testproject($this->db);
    +        $this->tree_manager = &$this->tproject_mgr->tree_manager;
    +
    +        $this->node_types_descr_id = $this->tree_manager->get_available_node_types();
    +        $this->node_types_id_descr = array_flip($this->node_types_descr_id);
    +        $this->my_node_type = $this->node_types_descr_id['testcase'];
    +
    +        $this->assignment_mgr = new assignment_mgr($this->db);
    +        $this->assignment_types = $this->assignment_mgr->get_available_types();
    +        $this->assignment_status = $this->assignment_mgr->get_available_status();
    +
    +        $this->cfield_mgr = new cfield_mgr($this->db);
    +
    +        $this->execution_types = $this->getExecutionTypes();
    +
    +        $this->layout = $this->getLayout();
    +
    +        $this->cfg = new stdClass();
    +        $this->cfg->testcase = config_get('testcase_cfg');
    +        $this->cfg->execution = config_get('exec_cfg');
    +        $this->cfg->cfield = config_get('custom_fields');
    +
    +        $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: ';
    +
    +        $this->XMLCfg = new stdClass();
    +        $this->XMLCfg->att = $this->getAttXMLCfg();
    +        $this->XMLCfg->req = $this->getReqXMLCfg();
    +
    +        $this->keywordAnnotations = config_get("keywords")->annotations;
    +
    +        // ATTENTION:
    +        // second argument is used to set $this->attachmentTableName,property that this calls
    +        // get from his parent
    +        // ORIGINAL
    +        // parent::__construct($this->db,"nodes_hierarchy");
    +        parent::__construct($this->db, "tcversions");
    +    }
    +
    +    /**
    +     *
    +     * @param int $tproject_id
    +     */
    +    public function setTestProject($tproject_id): void
    +    {
    +        $this->tproject_id = intval($tproject_id);
    +    }
    +
    +    /**
    +     *
    +     * @return array
    +     */
    +    private static function getExecutionTypes(): array
    +    {
    +        $stdSet = array(
    +            self::EXECUTION_TYPE_MANUAL => lang_get('manual'),
    +            self::EXECUTION_TYPE_AUTO => lang_get('automated')
    +        );
    +
    +        if (! is_null($customSet = config_get('custom_execution_types'))) {
    +            foreach ($customSet as $code => $lbl) {
    +                $stdSet[$code] = lang_get($lbl);
    +            }
    +        }
    +        return $stdSet;
    +    }
    +
    +    /**
    +     */
    +    public function getName($tcase_id)
    +    {
    +        $info = $this->tree_manager->get_node_hierarchy_info($tcase_id);
    +        return $info['name'];
    +    }
    +
    +    /**
    +     */
    +    public function getFileUploadRelativeURL($identity)
    +    {
    +        return "lib/testcases/tcEdit.php?doAction=fileUpload&" . "&tcase_id=" .
    +            intval($identity->tcase_id) . "&tcversion_id=" .
    +            intval($identity->tcversion_id) . "&tproject_id=" .
    +            intval($identity->tproject_id);
    +    }
    +
    +    /**
    +     */
    +    private function getDeleteAttachmentRelativeURL($identity)
    +    {
    +        return "lib/testcases/tcEdit.php?doAction=deleteFile&tcase_id=" .
    +            intval($identity->tcase_id) . "&tcversion_id=" .
    +            intval($identity->tcversion_id) . "&tproject_id=" .
    +            intval($identity->tproject_id) . "&file_id=";
    +    }
    +
    +    /**
    +     */
    +    private function getDeleteAttachmentByIDRelativeURL($identity,
    +        &$guiObj = null): string
    +    {
    +        $url = "lib/testcases/tcEdit.php?doAction=deleteFile&tcase_id=" .
    +            intval($identity->tcase_id) . "&tproject_id=" .
    +            intval($identity->tproject_id) . "&file_id=";
    +
    +        // needed for IVU 2019 implementation
    +        if (null != $guiObj) {
    +            $p2l = array(
    +                'show_mode',
    +                'tplan_id'
    +            );
    +            foreach ($p2l as $pr) {
    +                if (property_exists($guiObj, $pr)) {
    +                    $url .= "&$pr=" . $guiObj->$pr;
    +                }
    +            }
    +        }
    +
    +        $url .= "&file_id=";
    +        return $url;
    +    }
    +
    +    /**
    +     */
    +    private function getDeleteTCVRelationRelativeURL(&$guiObj = null): string
    +    {
    +        $url = "lib/testcases/tcEdit.php?doAction=doDeleteRelation";
    +
    +        // needed for IVU 2019 implementation
    +        if (null != $guiObj) {
    +            $p2l = array(
    +                'show_mode',
    +                'tplan_id'
    +            );
    +            foreach ($p2l as $pr) {
    +                if (property_exists($guiObj, $pr)) {
    +                    $url .= "&$pr=" . $guiObj->$pr;
    +                }
    +            }
    +        }
    +
    +        $url .= '&tcase_id=%1&relation_id=%2';
    +
    +        return $url;
    +    }
    +
    +    /**
    +     */
    +    private function getDeleteTCVKeywordRelativeURL(&$guiObj = null): string
    +    {
    +        $url = "lib/testcases/tcEdit.php?doAction=removeKeyword";
    +
    +        // needed for IVU 2019 implementation
    +        if (null != $guiObj) {
    +            $p2l = array(
    +                'show_mode',
    +                'tplan_id'
    +            );
    +            foreach ($p2l as $pr) {
    +                if (property_exists($guiObj, $pr)) {
    +                    $url .= "&$pr=" . $guiObj->$pr;
    +                }
    +            }
    +        }
    +
    +        $url .= '&tcase_id=%1&tckw_link_id=%2';
    +        return $url;
    +    }
    +
    +    /*
    +     * function: get_export_file_types
    +     * getter
    +     *
    +     * args: -
    +     *
    +     * returns: map
    +     * key: export file type code
    +     * value: export file type verbose description
    +     *
    +     */
    +    public function get_export_file_types()
    +    {
    +        return $this->export_file_types;
    +    }
    +
    +    /*
    +     * function: get_impor_file_types
    +     * getter
    +     *
    +     * args: -
    +     *
    +     * returns: map
    +     * key: import file type code
    +     * value: import file type verbose description
    +     *
    +     */
    +    public function get_import_file_types()
    +    {
    +        return $this->import_file_types;
    +    }
    +
    +    /*
    +     * function: get_execution_types
    +     * getter
    +     *
    +     * args: -
    +     *
    +     * returns: map
    +     * key: execution type code
    +     * value: execution type verbose description
    +     *
    +     */
    +    public function get_execution_types()
    +    {
    +        return $this->execution_types;
    +    }
    +
    +    /**
    +     * just a wrapper
    +     */
    +    public function createFromObject($item)
    +    {
    +        static $wkfstatus;
    +
    +        if (is_null($wkfstatus)) {
    +            $wkfstatus = config_get('testCaseStatus');
    +        }
    +        $options = array(
    +            'check_duplicate_name' => self::CHECK_DUPLICATE_NAME,
    +            'action_on_duplicate_name' => 'block',
    +            'estimatedExecDuration' => 0,
    +            'status' => $wkfstatus['draft'],
    +            'importLogic' => null
    +        );
    +
    +        if (property_exists($item, 'estimatedExecDuration')) {
    +            $options['estimatedExecDuration'] = floatval(
    +                $item->estimatedExecDuration);
    +        }
    +
    +        if (property_exists($item, 'status')) {
    +            $options['status'] = intval($item->status);
    +        }
    +
    +        if (property_exists($item, 'importLogic')) {
    +            $options['importLogic'] = $item->importLogic;
    +        }
    +
    +        return $this->create($item->testSuiteID, $item->name, $item->summary,
    +            $item->preconditions, $item->steps, $item->authorID, '',
    +            $item->order, self::AUTOMATIC_ID, $item->executionType,
    +            $item->importance, $options);
    +    }
    +
    +    /**
    +     * create a test case
    +     */
    +    public function create($parent_id, $name, $summary, $preconditions, $steps,
    +        $author_id, $keywords_id = '', $tc_order = self::DEFAULT_ORDER,
    +        $id = self::AUTOMATIC_ID,
    +        $execution_type = TESTCASE_EXECUTION_TYPE_MANUAL, $importance = 2,
    +        $options = null)
    +    {
    +        $my['options'] = array(
    +            'check_duplicate_name' => self::DONT_CHECK_DUPLICATE_NAME,
    +            'action_on_duplicate_name' => 'generate_new',
    +            'estimatedExecDuration' => null,
    +            'status' => null,
    +            'active' => null,
    +            'is_open' => null,
    +            'importLogic' => null
    +        );
    +
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        if (trim($summary) != '' && strpos($summary, self::NAME_PHOPEN) !== false &&
    +            strpos($summary, self::NAME_PHCLOSE) !== false) {
    +            $name = $this->buildTCName($name, $summary);
    +        }
    +
    +        if (trim($preconditions) != '' &&
    +            strpos($preconditions, self::NAME_PHOPEN) !== false &&
    +            strpos($preconditions, self::NAME_PHCLOSE) !== false) {
    +            $name = $this->buildTCName($name, $preconditions);
    +        }
    +
    +        $ret = $this->create_tcase_only($parent_id, $name, $tc_order, $id,
    +            $my['options']);
    +
    +        $tcase_id = $ret['id'];
    +        $ix = new stdClass();
    +
    +        if ($ret["status_ok"]) {
    +            $ix->version = 1;
    +            if (isset($ret['version_number']) && $ret['version_number'] < 0) {
    +                // We are in the special situation we are only creating a new version,
    +                // useful when importing test cases. Need to get last version number.
    +                // I do not use create_new_version() because it does a copy ot last version
    +                // and do not allow to set new values in different fields while doing this operation.
    +                $last_version_info = $this->getLastVersionInfo($ret['id'],
    +                    array(
    +                        'output' => 'minimun'
    +                    ));
    +
    +                $ix->version = $last_version_info['version'] + 1;
    +                $ret['msg'] = sprintf($ret['msg'], $ix->version);
    +                $ret['version_number'] = $ix->version;
    +            }
    +
    +            // Multiple Test Case Steps Feature
    +            $version_number = $ret['version_number'];
    +
    +            $ix->id = $tcase_id;
    +            $ix->externalID = $ret['external_id'];
    +            $ix->summary = $summary;
    +            $ix->preconditions = $preconditions;
    +            $ix->steps = $steps;
    +            $ix->authorID = $author_id;
    +            $ix->executionType = $execution_type;
    +            $ix->importance = $importance;
    +            $ix->status = $my['options']['status'];
    +            $ix->active = $my['options']['active'];
    +            $ix->is_open = $my['options']['is_open'];
    +            $ix->estimatedExecDuration = $my['options']['estimatedExecDuration'];
    +
    +            $op = $this->createVersion($ix);
    +            if (trim($keywords_id) != "") {
    +                $a_keywords = explode(",", $keywords_id);
    +                $auditContext = array(
    +                    'on' => self::AUDIT_ON,
    +                    'version' => $version_number
    +                );
    +                $this->addKeywords($tcase_id, $op['id'], $a_keywords,
    +                    $auditContext);
    +            }
    +
    +            if ($ret['update_name']) {
    +                $sql = " UPDATE {$this->tables['nodes_hierarchy']} SET name='" .
    +                    $this->db->prepare_string($name) . "' WHERE id= " .
    +                    intval($ret['id']);
    +                $this->db->exec_query($sql);
    +            }
    +
    +            $ret['msg'] = $op['status_ok'] ? $ret['msg'] : $op['msg'];
    +            $ret['tcversion_id'] = $op['status_ok'] ? $op['id'] : - 1;
    +
    +            $ctx = array(
    +                'test_suite_id' => $parent_id,
    +                'id' => $id,
    +                'name' => $name,
    +                'summary' => $summary,
    +                'preconditions' => $preconditions,
    +                'steps' => $steps,
    +                'author_id' => $author_id,
    +                'keywords_id' => $keywords_id,
    +                'order' => $tc_order,
    +                'exec_type' => $execution_type,
    +                'importance' => $importance,
    +                'options' => $options
    +            );
    +            event_signal('EVENT_TEST_CASE_CREATE', $ctx);
    +        }
    +        return $ret;
    +    }
    +
    +    /*
    +     * [$check_duplicate_name]
    +     * [$action_on_duplicate_name]
    +     * [$order]
    +     *
    +     * [$id]
    +     * 0 -> the id will be assigned by dbms
    +     * x -> this will be the id
    +     * Warning: no check is done before insert => can got error.
    +     *
    +     * return:
    +     * $ret['id']
    +     * $ret['external_id']
    +     * $ret['status_ok']
    +     * $ret['msg'] = 'ok';
    +     * $ret['new_name']
    +     *
    +     * rev:
    +     *
    +     */
    +    public function create_tcase_only($parent_id, $name,
    +        $order = self::DEFAULT_ORDER, $id = self::AUTOMATIC_ID, $options = null)
    +    {
    +        $dummy = config_get('field_size');
    +        $name_max_len = $dummy->testcase_name;
    +        $name = trim($name);
    +        $originalNameLen = tlStringLen($name);
    +
    +        $ret = array(
    +            'id' => - 1,
    +            'external_id' => 0,
    +            'status_ok' => 1,
    +            'msg' => 'ok',
    +            'new_name' => '',
    +            'version_number' => 1,
    +            'has_duplicate' => false,
    +            'external_id_already_exists' => false,
    +            'update_name' => false
    +        );
    +
    +        $my['options'] = array(
    +            'check_duplicate_name' => self::DONT_CHECK_DUPLICATE_NAME,
    +            'action_on_duplicate_name' => 'generate_new',
    +            'external_id' => null,
    +            'importLogic' => null
    +        );
    +
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        $doCreate = true;
    +        $forceGenerateExternalID = false;
    +
    +        $algo_cfg = config_get('testcase_cfg')->duplicated_name_algorithm;
    +        $getDupOptions['check_criteria'] = ($algo_cfg->type == 'counterSuffix') ? 'like' : '=';
    +        $getDupOptions['access_key'] = ($algo_cfg->type == 'counterSuffix') ? 'name' : 'id';
    +
    +        // If external ID has been provided, check if exists.
    +        // If answer is yes, then
    +        // 1. collect current info
    +        // 2. if $my['options']['check_duplicate_name'] is create new version
    +        // change to BLOCK
    +        //
    +        if (! is_null($my['options']['importLogic'])) {
    +            $doQuickReturn = false;
    +            switch ($my['options']['importLogic']['hitCriteria']) {
    +                case 'externalID':
    +                    if (($sf = intval($my['options']['external_id'])) > 0) {
    +                        // check if already exists a test case with this external id
    +                        $info = $this->get_by_external($sf, $parent_id);
    +                        if (! is_null($info)) {
    +                            if (count($info) > 1) {
    +                                // abort
    +                                throw new Exception(
    +                                    "More than one test case with same external ID");
    +                            }
    +
    +                            $doQuickReturn = true;
    +                            $ret['id'] = key($info);
    +                            $ret['external_id'] = $sf;
    +                            $ret['version_number'] = - 1;
    +                            $ret['external_id_already_exists'] = true;
    +                        }
    +                    }
    +
    +                    switch ($my['options']['importLogic']['actionOnHit']) {
    +                        case 'create_new_version':
    +                            if ($doQuickReturn) {
    +                                // I this situation we will need to also update test case name, if user
    +                                // has provided one on import file.
    +                                // Then we need to check that new name will not conflict with an existing one
    +                                $doCreate = false;
    +                                if (strcmp($info[key($info)]['name'], $name) != 0) {
    +                                    $itemSet = $this->getDuplicatesByName($name,
    +                                        $parent_id, $getDupOptions);
    +                                    if (is_null($itemSet)) {
    +                                        $ret['name'] = $name;
    +                                        $ret['update_name'] = true;
    +                                    }
    +                                }
    +                                return $ret;
    +                            }
    +                            break;
    +
    +                        case 'generate_new':
    +                            // on GUI => create a new test case with a different title
    +                            // IMPORTANT:
    +                            // if name provided on import file does not hit an existent one
    +                            // then I'm going to use it, instead of generating a NEW NAME
    +                            $forceGenerateExternalID = true;
    +                            break;
    +                    }
    +                    break;
    +            }
    +        }
    +
    +        if ($my['options']['check_duplicate_name']) {
    +            $itemSet = $this->getDuplicatesByName($name, $parent_id,
    +                $getDupOptions);
    +
    +            if (! is_null($itemSet) && ($siblingQty = count($itemSet)) > 0) {
    +                $ret['has_duplicate'] = true;
    +
    +                switch ($my['options']['action_on_duplicate_name']) {
    +                    case 'block':
    +                        $doCreate = false;
    +                        $ret['status_ok'] = 0;
    +                        $ret['msg'] = sprintf(
    +                            lang_get('testcase_name_already_exists'), $name);
    +                        break;
    +
    +                    case 'generate_new':
    +                        $doCreate = true;
    +
    +                        // TICKET 5159: importing duplicate test suites
    +                        // Need to force use of generated External ID
    +                        // (this seems the best alternative)
    +                        $my['options']['external_id'] = null;
    +
    +                        switch ($algo_cfg->type) {
    +                            case 'stringPrefix':
    +                                $doIt = true;
    +                                while ($doIt) {
    +                                    if ($doIt = ! is_null($itemSet)) {
    +                                        $prefix = @strftime($algo_cfg->text,
    +                                            time());
    +                                        $target = $prefix . " " . $name;
    +                                        $final_len = strlen($target);
    +                                        if ($final_len > $name_max_len) {
    +                                            $target = substr($target, 0,
    +                                                $name_max_len);
    +                                        }
    +
    +                                        // Check new generated name
    +                                        $itemSet = $this->getDuplicatesByName(
    +                                            $target, $parent_id, $getDupOptions);
    +                                    }
    +                                }
    +                                $name = $target;
    +                                break;
    +
    +                            case 'counterSuffix':
    +                                $mask = ! is_null($algo_cfg->text) ? $algo_cfg->text : '#%s';
    +                                $nameSet = array_flip(array_keys($itemSet));
    +
    +                                // 20110109 - franciscom
    +                                // does not understand why I've choosen time ago
    +                                // to increment $siblingQty before using it
    +                                // This way if TC X exists on target parent
    +                                // I will create TC X [2] insteand of TC X [1]
    +                                // Anyway right now I will not change.
    +                                $target = $name .
    +                                    ($suffix = sprintf($mask, ++ $siblingQty));
    +                                $final_len = strlen($target);
    +                                if ($final_len > $name_max_len) {
    +                                    $target = substr($target, strlen($suffix),
    +                                        $name_max_len);
    +                                }
    +
    +                                // Need to recheck if new generated name does not crash with existent name
    +                                // why? Suppose you have created:
    +                                // TC [1]
    +                                // TC [2]
    +                                // TC [3]
    +                                // Then you delete TC [2].
    +                                // When I got siblings il will got 2 siblings, if I create new progressive using next,
    +                                // it will be 3 => I will get duplicated name.
    +                                while (isset($nameSet[$target])) {
    +                                    $target = $name .
    +                                        ($suffix = sprintf($mask, ++ $siblingQty));
    +                                    $final_len = strlen($target);
    +                                    if ($final_len > $name_max_len) {
    +                                        $target = substr($target,
    +                                            strlen($suffix), $name_max_len);
    +                                    }
    +                                }
    +                                $name = $target;
    +                                break;
    +                        }
    +
    +                        $ret['status_ok'] = 1;
    +                        $ret['new_name'] = $name;
    +                        $ret['msg'] = sprintf(lang_get('created_with_title'),
    +                            $name);
    +                        break;
    +
    +                    case 'create_new_version':
    +                        $doCreate = false;
    +
    +                        // If we found more that one with same name and same parent,
    +                        // will take the first one.
    +                        $xx = current($itemSet);
    +                        $ret['id'] = $xx['id'];
    +                        $ret['external_id'] = $xx['tc_external_id'];
    +                        $ret['status_ok'] = 1;
    +                        $ret['new_name'] = $name;
    +                        $ret['version_number'] = - 1;
    +                        $ret['msg'] = lang_get('create_new_version');
    +                        break;
    +
    +                    default:
    +                        break;
    +                }
    +            }
    +        }
    +
    +        // 20120822 - think we have potencial issue, because we never check if
    +        // duplicated EXTERNAL ID exists.
    +        // Right now there is no time to try a fix
    +        if ($ret['status_ok'] && $doCreate) {
    +
    +            $safeLenName = tlSubStr($name, 0, $name_max_len);
    +
    +            // Get tproject id
    +            $path2root = $this->tree_manager->get_path($parent_id);
    +            $tproject_id = $path2root[0]['parent_id'];
    +
    +            $tcase_id = $this->tree_manager->new_node($parent_id,
    +                $this->my_node_type, $safeLenName, $order, $id);
    +            $ret['id'] = $tcase_id;
    +
    +            $generateExtID = false;
    +            if ($forceGenerateExternalID ||
    +                is_null($my['options']['external_id'])) {
    +                $generateExtID = true;
    +            } else {
    +                // this need more work and checks (20140209)
    +                $sf = intval($my['options']['external_id']);
    +                if (is_null($this->get_by_external($sf, $parent_id))) {
    +                    $ret['external_id'] = $sf;
    +
    +                    // CRITIC: setTestCaseCounter() will update only if new provided value > current value
    +                    $this->tproject_mgr->setTestCaseCounter($tproject_id,
    +                        $ret['external_id']);
    +                } else {
    +                    $generateExtID = true;
    +                }
    +            }
    +            if ($generateExtID) {
    +                $ret['external_id'] = $this->tproject_mgr->generateTestCaseNumber(
    +                    $tproject_id);
    +            }
    +
    +            if (! $ret['has_duplicate'] && ($originalNameLen > $name_max_len)) {
    +                $ret['new_name'] = $safeLenName;
    +                $ret['msg'] = sprintf(lang_get('testcase_name_length_exceeded'),
    +                    $originalNameLen, $name_max_len);
    +            }
    +        }
    +
    +        return $ret;
    +    }
    +
    +    /**
    +     * trying to solve debugMsg . __FUNCTION__;
    +        $tcase_version_id = $this->tree_manager->new_node($item->id,
    +            $this->node_types_descr_id['testcase_version']);
    +
    +        $this->ckEditorCopyAndPasteCleanUp($item,
    +            array(
    +                'summary',
    +                'preconditions'
    +            ));
    +
    +        $sql = "/* $debugMsg */ INSERT INTO {$this->tables['tcversions']} " .
    +            " (id,tc_external_id,version,summary,preconditions," .
    +            "  author_id,creation_ts,execution_type,importance ";
    +
    +        $sqlValues = " VALUES({$tcase_version_id},{$item->externalID},{$item->version},'" .
    +            $this->db->prepare_string($item->summary) . "','" .
    +            $this->db->prepare_string($item->preconditions) . "'," .
    +            $this->db->prepare_int($item->authorID) . "," . $this->db->db_now() .
    +            ", {$item->executionType},{$item->importance} ";
    +
    +        if (! is_null($item->status)) {
    +            $wf = intval($item->status);
    +            $sql .= ',status';
    +            $sqlValues .= ",{$wf}";
    +        }
    +
    +        if (! is_null($item->estimatedExecDuration)) {
    +            $v = trim($item->estimatedExecDuration);
    +            if ($v != '') {
    +                $sql .= ", estimated_exec_duration";
    +                $sqlValues .= "," . floatval($v);
    +            }
    +        }
    +
    +        if (property_exists($item, 'active') && ! is_null($item->active)) {
    +            $v = intval($item->active) > 0 ? 1 : 0;
    +            $sql .= ", active";
    +            $sqlValues .= "," . $v;
    +        }
    +
    +        if (property_exists($item, 'is_open') && ! is_null($item->is_open)) {
    +            $v = intval($item->is_open) > 0 ? 1 : 0;
    +            $sql .= ", is_open";
    +            $sqlValues .= "," . $v;
    +        }
    +
    +        $sql .= " )" . $sqlValues . " )";
    +
    +        $result = $this->db->exec_query($sql);
    +
    +        $ret['msg'] = 'ok';
    +        $ret['id'] = $tcase_version_id;
    +        $ret['status_ok'] = 1;
    +
    +        if ($result && (! is_null($item->steps) && is_array($item->steps))) {
    +            $steps2create = count($item->steps);
    +            $op['status_ok'] = 1;
    +
    +            // need to this to manage call to this method for REST API.
    +            $stepIsObject = is_object($item->steps[0]);
    +            for ($jdx = 0; ($jdx < $steps2create && $op['status_ok']); $jdx ++) {
    +                if ($stepIsObject) {
    +                    $item->steps[$jdx] = (array) $item->steps[$jdx];
    +                }
    +
    +                $this->create_step($tcase_version_id,
    +                    $item->steps[$jdx]['step_number'],
    +                    $item->steps[$jdx]['actions'],
    +                    $item->steps[$jdx]['expected_results'],
    +                    $item->steps[$jdx]['execution_type']);
    +            }
    +        }
    +
    +        if (! $result) {
    +            $ret['msg'] = $this->db->error_msg();
    +            $ret['status_ok'] = 0;
    +            $ret['id'] = - 1;
    +        }
    +
    +        return $ret;
    +    }
    +
    +    /*
    +     * function: getDuplicatesByname
    +     *
    +     * args: $name
    +     * $parent_id
    +     *
    +     * returns: hash
    +     */
    +    public function getDuplicatesByName($name, $parent_id, $options = null)
    +    {
    +        $my['options'] = array(
    +            'check_criteria' => '=',
    +            'access_key' => 'id',
    +            'id2exclude' => null
    +        );
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        $target = $this->db->prepare_string($name);
    +        switch ($my['options']['check_criteria']) {
    +            case 'like':
    +                // % and _ need to be escaped, but method is different
    +                // according DBMS
    +                $check_criteria = " AND NHA.name LIKE '{$target}%' ";
    +                break;
    +
    +            case '=':
    +            default:
    +                $check_criteria = " AND NHA.name = '{$target}' ";
    +                break;
    +        }
    +
    +        $sql = " SELECT DISTINCT NHA.id,NHA.name,TCV.tc_external_id" .
    +            " FROM {$this->tables['nodes_hierarchy']} NHA, " .
    +            " {$this->tables['nodes_hierarchy']} NHB, {$this->tables['tcversions']} TCV  " .
    +            " WHERE NHA.node_type_id = {$this->my_node_type} " .
    +            " AND NHB.parent_id=NHA.id " . " AND TCV.id=NHB.id " .
    +            " AND NHB.node_type_id = {$this->node_types_descr_id['testcase_version']} " .
    +            " AND NHA.parent_id=" . $this->db->prepare_int($parent_id) .
    +            " {$check_criteria}";
    +
    +        if (! is_null($my['options']['id2exclude'])) {
    +            $sql .= " AND NHA.id <> " . intval($my['options']['id2exclude']);
    +        }
    +
    +        $rs = $this->db->fetchRowsIntoMap($sql, $my['options']['access_key']);
    +
    +        if (is_null($rs) || count($rs) == 0) {
    +            $rs = null;
    +        }
    +        return $rs;
    +    }
    +
    +    /*
    +     * function: get_by_name
    +     *
    +     * args: $name
    +     * [$tsuite_name]: name of parent test suite
    +     * [$tproject_name]
    +     *
    +     * returns: hash
    +     *
    +     * @internal revisions
    +     * 20100831 - franciscom - BUGID 3729
    +     *
    +     */
    +    public function get_by_name($name, $tsuite_name = '', $tproject_name = '')
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +
    +        $recordset = null;
    +        $field_size = config_get('field_size');
    +        $tsuite_name = tlSubStr(trim($tsuite_name), 0,
    +            $field_size->testsuite_name);
    +        $tproject_name = tlSubStr(trim($tproject_name), 0,
    +            $field_size->testproject_name);
    +        $name = tlSubStr(trim($name), 0, $field_size->testcase_name);
    +
    +        $sql = "/* $debugMsg */ " .
    +            " SELECT DISTINCT NH_TCASE.id,NH_TCASE.name,NH_TCASE_PARENT.id AS parent_id," .
    +            " NH_TCASE_PARENT.name AS tsuite_name, TCV.tc_external_id " .
    +            " FROM {$this->tables['nodes_hierarchy']} NH_TCASE, " .
    +            " {$this->tables['nodes_hierarchy']} NH_TCASE_PARENT, " .
    +            " {$this->tables['nodes_hierarchy']} NH_TCVERSIONS," .
    +            " {$this->tables['tcversions']}  TCV  " .
    +            " WHERE NH_TCASE.node_type_id = {$this->my_node_type} " .
    +            " AND NH_TCASE.name = '{$this->db->prepare_string($name)}' " .
    +            " AND TCV.id=NH_TCVERSIONS.id " .
    +            " AND NH_TCVERSIONS.parent_id=NH_TCASE.id " .
    +            " AND NH_TCASE_PARENT.id=NH_TCASE.parent_id ";
    +
    +        if ($tsuite_name != "") {
    +            $sql .= " AND NH_TCASE_PARENT.name = '{$this->db->prepare_string($tsuite_name)}' " .
    +                " AND NH_TCASE_PARENT.node_type_id = {$this->node_types_descr_id['testsuite']} ";
    +        }
    +        $recordset = $this->db->get_recordset($sql);
    +        if (count($recordset) && $tproject_name != "") {
    +            list ($tproject_info) = $this->tproject_mgr->get_by_name(
    +                $tproject_name);
    +            foreach ($recordset as $idx => $tcase_info) {
    +                if ($this->get_testproject($tcase_info['id']) !=
    +                    $tproject_info['id']) {
    +                    unset($recordset[$idx]);
    +                }
    +            }
    +        }
    +        return $recordset;
    +    }
    +
    +    /*
    +     * get array of info for every test case
    +     * without any kind of filter.
    +     * Every array element contains an assoc array with testcase info
    +     *
    +     */
    +    public function get_all()
    +    {
    +        $sql = " SELECT nodes_hierarchy.name, nodes_hierarchy.id
                  FROM  {$this->tables['nodes_hierarchy']} nodes_hierarchy
    -             WHERE nodes_hierarchy.node_type_id={$my_node_type}";
    -    $recordset = $this->db->get_recordset($sql);
    -
    -    return $recordset;
    -  }
    -
    -
    -  /**
    -   * Show Test Case
    -   *
    -   *
    -   */
    -  function show(&$smarty,$guiObj,$identity,$grants,$opt=null) {
    -    static $cfg;
    -    static $reqMgr;
    -
    -    if(!$cfg) {
    -      $cfg = config_get('spec_cfg');
    -      $reqMgr = new requirement_mgr($this->db);
    -    }
    -
    -    $status_ok = ($identity->id > 0);
    -    if( !$status_ok ) {
    -      throw new Exception(__METHOD__ . ' EXCEPTION: Test Case ID is invalid ( <= 0)' );
    -    }
    -    
    -    $my = array('opt' => array('getAttachments' => false));
    -    $my['opt'] = array_merge($my['opt'],(array)$opt);
    -
    -    $id = $identity->id;
    -    $idSet = (array)$id;
    -
    -    $idCard = new stdClass();
    -    $idCard->tcase_id = intval($id);
    -    $idCard->tproject_id = intval($identity->tproject_id);
    -
    -    $idCard->tcversion_id = isset($identity->version_id) ? $identity->version_id : self::ALL_VERSIONS;
    -
    -    $getVersionID = $idCard->tcversion_id;
    -
    -    $gui = $this->initShowGui($guiObj,$grants,$idCard);
    -
    -    // When editing on execution, it's important to understand
    -    // is current displayed version is LINKED to Test Plan 
    -    // to add or remove some features
    -    //
    -    $gui->candidateToUpd = 0;
    -    switch( $gui->show_mode ) {
    -      case 'editOnExec':
    -        $gui->candidateToUpd = 
    -          !$this->isLinkedTCVersion($idCard->tcversion_id,$gui->tplan_id);
    -        $gui->new_version_source = 'latest'; 
    -      break;
    -
    -      default:
    -      break;
    -    }
    -
    -    
    -    $userIDSet = array();
    -
    -    if($status_ok && sizeof($idSet)) {
    -
    -      $cfPlaces = $this->buildCFLocationMap();
    -
    -      $gui->linked_versions = null;
    -
    -      $gopt = array('renderGhost' => true, 'withGhostString' => true,
    -                    'renderImageInline' => true, 'renderVariables' => true,
    -                    'renderSpecialKW' => true,
    -                    'caller' => 'show()');
    -
    -      $cfx = 0;
    -      $gui->otherVersionsKeywords = array();
    -
    -      $gui->fileUploadURL = array();
    -      foreach($idSet as $key => $tc_id) {
    -
    -        // IMPORTANT NOTICE
    -        // Deep Analysis is need to understand if there is an use case
    -        // where this method really receive an array of test case ID.
    -        // 
    -        // using an specific value for test case version id has sense 
    -        // only when we are working on ONE SPECIFIC Test Case.
    -        // 
    -        // if we are working on a set of test cases, because this method 
    -        // does not manage in input couple of (test case, versio id),
    -        // the only chance is to get ALL VERSIONS
    -        //
    -        if( !$tcvSet = $this->get_by_id($tc_id,$getVersionID,null,$gopt) ) {
    -          continue;
    -        }
    -
    -        if($cfg->show_tplan_usage) {
    -          $gui->linked_versions[] = $this->get_linked_versions($tc_id);
    -        }
    -
    -        // Position 0 is latest active version
    -        $tcvSet[0]['tc_external_id'] = 
    -          $gui->tcasePrefix . $tcvSet[0]['tc_external_id'];
    -        $tcvSet[0]['ghost'] = 
    -          sprintf(self::GHOSTMASK,$tcvSet[0]['tc_external_id'],$tcvSet[0]['version']);
    -
    -        // status quo of execution and links of tc versions
    -        $gui->status_quo[] = $this->get_versions_status_quo($tc_id);
    -
    -        // Logic on Current/Latest Test Case Version
    -        $tc_current = $tcvSet[0];
    -        $tc_current['isTheLatest'] = 1;
    -        $currentVersionID = $tc_current['id'];
    -
    -        $io = $idCard;
    -        $io->tcversion_id = $currentVersionID;
    -
    -        
    -        $gui->delAttachmentURL = $_SESSION['basehref'] . 
    -          $this->getDeleteAttachmentByIDRelativeURL($io,$gui);
    -
    -
    -        $gui->delTCVRelationURL = $_SESSION['basehref'] . 
    -          $this->getDeleteTCVRelationRelativeURL($io,$gui);
    -
    -        $gui->delTCVKeywordURL = $_SESSION['basehref'] . 
    -          $this->getDeleteTCVKeywordRelativeURL($io,$gui);
    -
    -        $gui->delTCVPlatformURL = $_SESSION['basehref'] . 
    -          $this->getDeleteTCVPlatformRelativeURL($io,$gui);
    -
    -
    -        // Impacted for version management
    -        $gui->fileUploadURL[$currentVersionID] = 
    -          $_SESSION['basehref'] . $this->getFileUploadRelativeURL($io);
    -
    -
    -
    -        $gui->tc_current_version[] = array($tc_current);
    -
    -        //  
    -        // REFACTORING - Following code uses tc_current!!!
    -        //
    -
    -        // Get UserID and Updater ID for current Version
    -        $userIDSet[$tc_current['author_id']] = null;
    -        $userIDSet[$tc_current['updater_id']] = null;
    -
    -        $gui->req4current_version = 
    -          $reqMgr->getGoodForTCVersion($currentVersionID);
    -
    -        
    -        $gui->currentVersionKeywords = 
    -          $this->getKeywords($tc_id,$currentVersionID);
    -
    -        $gui->currentVersionPlatforms = 
    -          $this->getPlatforms($tc_id,$currentVersionID);
    -
    -
    -        $whoami = array('tcase_id' => $tc_id, 
    -                        'tcversion_id' => $currentVersionID);
    -
    -        $of = array('output' => 'html_options','add_blank' => true);
    -        $gui->currentVersionFreeKeywords = $this->getFreeKeywords($whoami,$of);
    -
    -
    -        $gui->currentVersionFreePlatforms = 
    -          $this->getFreePlatforms($whoami,$of);
    -
    -
    -        if( $my['opt']['getAttachments'] ) {
    -          $gui->attachments[$currentVersionID] = 
    -            getAttachmentInfosFrom($this,$currentVersionID);
    -        }
    -
    -        // get linked testcase scripts
    -        if($gui->codeTrackerEnabled) {
    -          $scripts = $this->getScriptsForTestCaseVersion($gui->cts,$currentVersionID);
    -          if(!is_null($scripts)) {
    -            $gui->scripts[$currentVersionID] = $scripts;
    -          }
    -        }
    -
    -        if($this->cfg->testcase->relations->enable) {
    -          $xm = array('tcase_id' => $tc_id, 'tcversion_id' => $currentVersionID);
    -          $gui->relationSet[] = $this->getTCVersionRelations($xm);
    -        }
    -
    -        $cfCtx = array('scope' => 'design','tproject_id' => $gui->tproject_id,'link_id' => $tc_current['id']);
    -        foreach($cfPlaces as $cfpKey => $cfpFilter) {
    -          $gui->cf_current_version[$cfx][$cfpKey] =
    -            $this->htmlTableOfCFValues($tc_id,$cfCtx,$cfpFilter);
    -        }
    -
    -        // Other versions (if exists)
    -        if(count($tcvSet) > 1) {
    -          $gui->testcase_other_versions[] = array_slice($tcvSet,1);
    -
    -          $target_idx = count($gui->testcase_other_versions) - 1;
    -          $loop2do = count($gui->testcase_other_versions[$target_idx]);
    -
    -          $cfCtx = array('scope' => 'design','tproject_id' => $gui->tproject_id);
    -
    -          $ref = &$gui->testcase_other_versions[$target_idx];
    -          for($qdx=0; $qdx < $loop2do; $qdx++) {
    -
    -            $gui->testcase_other_versions[$target_idx][$qdx]['isTheLatest'] = 0;
    -            
    -            $ref[$qdx]['ghost'] = 
    -              sprintf(self::GHOSTMASK,$tcvSet[0]['tc_external_id'],
    -                                      $ref[$qdx]['version']);
    -
    -            $cfCtx['link_id'] = $gui->testcase_other_versions[$target_idx][$qdx]['id'];
    -            foreach($cfPlaces as $locKey => $locFilter) {
    -              $gui->cf_other_versions[$cfx][$qdx][$locKey] =
    -                  $this->htmlTableOfCFValues($tc_id,$cfCtx,$locFilter);
    -            }
    -          }
    -        }
    -        else {
    -          $gui->testcase_other_versions[] = null;
    -          $gui->otherVersionsRelations[] = null;
    -          $gui->cf_other_versions[$cfx]=null;
    -        }
    -
    -        $cfx++;
    -
    -        if ($gui->testcase_other_versions[0]) {
    -          
    -          // Get author and updater id for each version
    -          foreach($gui->testcase_other_versions[0] as $key => $version) {
    -
    -            $userIDSet[$version['author_id']] = null;
    -            $userIDSet[$version['updater_id']] = null;
    -
    -            if($this->cfg->testcase->relations->enable) {
    -              $xm = array('tcase_id' => $version['testcase_id'], 
    -                          'tcversion_id' => $version['id'],
    -                          'other' => 'other');
    -              $gui->otherVersionsRelations[] = $this->getTCVersionRelations($xm);
    -            }
    -
    -            // get linked testcase scripts
    -            if($gui->codeTrackerEnabled) {
    -              $scripts = $this->getScriptsForTestCaseVersion($gui->cts,$version['id']);
    -              if(!is_null($scripts)) {
    -                $gui->scripts[$version['id']] = $scripts;
    -              }
    -            }
    -
    -            if( $my['opt']['getAttachments'] ) {
    -              $gui->attachments[$version['id']] = 
    -                getAttachmentInfosFrom($this,$version['id']);
    -            }
    -
    -
    -            $io = $idCard;
    -            $io->tcversion_id = $version['id'];
    -
    -            $gui->fileUploadURL[$version['id']] = 
    -              $_SESSION['basehref'] . $this->getFileUploadRelativeURL($io);
    -
    -            // Requirements
    -            $gui->req4OtherVersions[] = 
    -              $reqMgr->getGoodForTCVersion($version['id']);
    -            
    -
    -            $gui->otherVersionsKeywords[] = 
    -              $this->getKeywords($version['testcase_id'],$version['id']);
    -
    -            $gui->otherVersionsPlatforms[] = 
    -              $this->getPlatforms($version['testcase_id'],$version['id']);
    -
    -          }
    -        } // Other versions exist
    -      }
    -    }
    -
    -    $gui->relations = $gui->relationSet;
    -    $gui->relation_domain = '';
    -    if($this->cfg->testcase->relations->enable) {
    -      $gui->relation_domain = $this->getRelationTypeDomainForHTMLSelect();
    -    }
    -
    -    // Removing duplicate and NULL id's
    -    unset($userIDSet['']);
    -    $gui->users = tlUser::getByIDs($this->db,array_keys($userIDSet));
    -    $gui->cf = null;
    -
    -    $this->initShowGuiActions($gui);
    -    $tplCfg = templateConfiguration('tcView');
    -
    -    $smarty->assign('gui',$gui);
    -    $smarty->display($tplCfg->template_dir . $tplCfg->default_template);
    -  }
    -
    -
    -
    -  /**
    -   * update test case specification
    -   *
    -   * @param integer $id Test case unique identifier (node_hierarchy table)
    -   * @param integer $tcversion_id Test Case Version unique ID (node_hierarchy table)
    -   * @param string $name name/title
    -   * @param string $summary
    -   * @param string $preconditions
    -   * @param array $steps steps + expected results
    -   * @param integer $user_id who is doing the update
    -   * @param string $keywords_id optional list of keyword id to be linked to test case
    -   *         this list will override previous keyword links (delete + insert).
    -   *
    -   * @param integer $tc_order optional order inside parent test suite
    -   * @param integer $execution_type optional
    -   * @param integer $importance optional
    -   *
    -   *
    -   *
    -   */
    -  function update($id,$tcversion_id,$name,$summary,$preconditions,$steps,
    -                  $user_id,$keywords_id='',$tc_order=self::DEFAULT_ORDER,
    -                  $execution_type=TESTCASE_EXECUTION_TYPE_MANUAL,$importance=2,
    -                  $attr=null,$opt=null)
    -  {
    -    $ret['status_ok'] = 1;
    -    $ret['msg'] = '';
    -    $ret['reason'] = '';
    -
    -    $my['opt'] = array('blockIfExecuted' => false);
    -    $my['opt'] = array_merge($my['opt'],(array)$opt);
    -
    -
    -    $attrib = array('status' => null, 'is_open' => null, 'active' => null, 'estimatedExecDuration' => null);
    -    $attrib = array_merge($attrib,(array)$attr);
    -
    -
    -    tLog("TC UPDATE ID=($id): exec_type=$execution_type importance=$importance");
    -
    -    if( trim($summary) != '' ) {
    -      if( strpos($summary,self::NAME_PHOPEN) !== FALSE && strpos($summary,self::NAME_PHCLOSE) !== FALSE ) {
    -        $name = $this->buildTCName($name,$summary);
    -      }
    -    }
    -    
    -    // Check if new name will be create a duplicate testcase under same parent
    -    if( ($checkDuplicates = config_get('check_names_for_duplicates')) )
    -    {
    -      // get my parent
    -      $mi = $this->tree_manager->get_node_hierarchy_info($id);
    -      $itemSet = $this->getDuplicatesByName($name,$mi['parent_id'],array('id2exclude' => $id));
    -
    -      if(!is_null($itemSet)) {
    -        $ret['status_ok'] = false;
    -        $ret['msg'] = sprintf(lang_get('name_already_exists'),$name);
    -        $ret['reason'] = 'already_exists';
    -        $ret['hit_on'] = current($itemSet);
    -      }
    -
    -      if( $ret['status_ok'] == false ) {
    -        // get more info for feedback
    -      }
    -    }
    -
    -    if($ret['status_ok']) {
    -      if($my['opt']['blockIfExecuted']) {
    -        // When tcversion is updated on test plan after an executio exists
    -        // execution tcversion_number keeps the version of test case executed
    -        // will EX.tcversion_id is updated with id requested by user.
    -        // That's why when importing we need to check HUMAN READEABLE version numbers.
    -        $sql = " SELECT EX.id, EX.tcversion_number,TCV.version " .  
    -               " FROM {$this->tables['executions']} EX " .
    -               " JOIN {$this->tables['tcversions']} TCV " .
    -               " ON TCV.id = EX.tcversion_id " .
    -               " WHERE tcversion_id=" . $this->db->prepare_int($tcversion_id);
    -
    -        $rs = $this->db->get_recordset($sql);
    -        if(!is_null($rs))
    -        {
    -          foreach($rs as $rwx)
    -          {
    -            if( $rwx['tcversion_number'] == $rwx['version'] )
    -            {
    -              $ret['status_ok'] = false;
    -              $ret['msg'] = lang_get('block_ltcv_hasbeenexecuted');
    -              $ret['reason'] = 'blockIfExecuted';
    -              return $ret;
    -            }
    -          }  
    -        }
    -      }
    -
    -      $sql=array();
    -      $sql[] = " UPDATE {$this->tables['nodes_hierarchy']} SET name='" .
    -               $this->db->prepare_string($name) . "' WHERE id= {$id}";
    -
    -
    -      $k2e = array('summary','preconditions');
    -      $item = new stdClass();
    -      $item->summary = $summary;
    -      $item->preconditions = $preconditions;
    -      $this->CKEditorCopyAndPasteCleanUp($item,$k2e); 
    -      
    -      $dummy = " UPDATE {$this->tables['tcversions']} " .
    -               " SET summary='" . 
    -                 $this->db->prepare_string($item->summary) . "'," .
    -               " updater_id=" . $this->db->prepare_int($user_id) . ", " .
    -               " modification_ts = " . $this->db->db_now() . "," .
    -               " execution_type=" . $this->db->prepare_int($execution_type) . ", " .
    -               " importance=" . $this->db->prepare_int($importance) . "," .
    -               " preconditions='" . 
    -                 $this->db->prepare_string($item->preconditions) . "' ";
    -
    -
    -      if( !is_null($attrib['status']) )
    -      {
    -        $dummy .= ", status=" . intval($attrib['status']);
    -      }
    -
    -	    if( !is_null($attrib['is_open']) )    
    -      {
    -        $dummy .= ", is_open=" . intval($attrib['is_open']); 
    -      }
    -	  
    -	    if( !is_null($attrib['active']) )    
    -      {
    -        $dummy .= ", active=" . intval($attrib['active']); 
    -      }
    -
    -      if( !is_null($attrib['estimatedExecDuration']) )
    -      {
    -        $dummy .= ", estimated_exec_duration=";
    -        $v = trim($attrib['estimatedExecDuration']);
    -
    -        $dummy .= ($v == '') ? "NULL" : floatval($v);
    -      }
    -
    -      $dummy .= " WHERE id = " . $this->db->prepare_int($tcversion_id);
    -      $sql[] = $dummy;
    -
    -
    -      foreach($sql as $stm)
    -      {
    -        $result = $this->db->exec_query($stm);
    -        if( !$result )
    -        {
    -          $ret['status_ok'] = 0;
    -          $ret['msg'] = $this->db->error_msg;
    -          break;
    -        }
    -      }
    -
    -      if( $ret['status_ok'] && !is_null($steps) )
    -      {
    -        $this->update_tcversion_steps($tcversion_id,$steps);
    -      }
    -
    -      if( $ret['status_ok'] )
    -      {
    -        
    -        $idCard = array('id' => $id, 'version_id' => $tcversion_id,
    -                        'version' => $this->getVersionNumber($tcversion_id));
    -
    -        $this->updateKeywordAssignment($idCard,$keywords_id);
    -      }
    -
    -      $ctx = array('id' => $id,'version_id' => $tcversion_id,'name' => $name,
    -                   'summary' => $summary,'preconditions' => $preconditions,
    -                   'steps' => $steps,'user_id' => $user_id,
    -                   'keywords_id' => $keywords_id,'order' => $tc_order,
    -                   'exec_type' => $execution_type, 'importance' => $importance,
    -                   'attr' => $attr,'options' => $opt);
    -      event_signal('EVENT_TEST_CASE_UPDATE', $ctx);
    -    }
    -
    -    return $ret;
    -  }
    -
    -
    -  /**
    -   * used when updating a test case
    -   *
    -   */
    -  private function updateKeywordAssignment($idCard,$keywords_id) {
    -    // To avoid false loggings, check is delete is needed
    -    $id = intval($idCard['id']);
    -    $version_id = intval($idCard['version_id']);
    -    $version = intval($idCard['version']);
    -
    -    $items = array();
    -    $items['stored'] = $this->get_keywords_map($id,$version_id);
    -    if (is_null($items['stored'])) {
    -      $items['stored'] = array();
    -    }
    -
    -    $items['requested'] = array();
    -    if(trim($keywords_id) != "")
    -    {
    -      $a_keywords = explode(",",trim($keywords_id));
    -      $sql = " SELECT id,keyword " .
    -             " FROM {$this->tables['keywords']} " .
    -             " WHERE id IN (" . implode(',',$a_keywords) . ")";
    -
    -      $items['requested'] = $this->db->fetchColumnsIntoMap($sql,'id','keyword');
    -    }
    -
    -    $items['common'] = array_intersect_assoc($items['stored'],$items['requested']);
    -    $items['new'] = array_diff_assoc($items['requested'],$items['common']);
    -    $items['todelete'] = array_diff_assoc($items['stored'],$items['common']);
    -
    -    $auditContext = array('on' => self::AUDIT_ON, 'version' => $version);
    -    
    -    if(!is_null($items['todelete']) && count($items['todelete'])) {
    -      $this->deleteKeywords($id,$version_id,array_keys($items['todelete']),$auditContext);
    -    }
    -
    -    if(!is_null($items['new']) && count($items['new']))
    -    {
    -      $this->addKeywords($id,$version_id,array_keys($items['new']),$auditContext);
    -    }
    -  }
    -
    -
    -  /*
    -    function: logKeywordChanges
    -
    -    args:
    -
    -    returns:
    -
    -  */
    -  function logKeywordChanges($old,$new)
    -  {
    -
    -     // try to understand the really new
    -
    -  }
    -
    -
    -
    -
    -
    -
    -
    -  /*
    -    function: check_link_and_exec_status
    -              Fore every version of testcase (id), do following checks:
    -
    -              1. testcase is linked to one of more test plans ?
    -              2. if anwser is yes then,check if has been executed => has records on executions table
    -
    -    args : id: testcase id
    -
    -    returns: string with following values:
    -             no_links: testcase is not linked to any testplan
    -             linked_but_not_executed: testcase is linked at least to a testplan
    -                                      but has not been executed.
    -
    -             linked_and_executed: testcase is linked at least to a testplan and
    -                                  has been executed => has records on executions table.
    -
    -
    -  */
    -  function check_link_and_exec_status($id)
    -  {
    -    $status = 'no_links';
    -
    -    // get linked versions
    -    // ATTENTION TO PLATFORMS
    -    $linked_tcversions = $this->get_linked_versions($id);
    -    $has_links_to_testplans = is_null($linked_tcversions) ? 0 : 1;
    -
    -    if($has_links_to_testplans)
    -    {
    -      // check if executed
    -      $linked_not_exec = $this->get_linked_versions($id,array('exec_status' => 'NOT_EXECUTED'));
    -
    -      $status='linked_and_executed';
    -      if(count($linked_tcversions) == count($linked_not_exec))
    -      {
    -        $status = 'linked_but_not_executed';
    -      }
    -    }
    -    return $status;
    -  }
    -
    -
    -  /**
    -   *
    -   */
    -  function delete($id,$version_id = self::ALL_VERSIONS) {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -    $children=null;
    -    $do_it=true;
    -
    -    // I'm trying to speedup the next deletes
    -    $sql = "/* $debugMsg */ " .
    -           " SELECT NH_TCV.id AS tcversion_id, NH_TCSTEPS.id AS step_id " .
    -           " FROM {$this->tables['nodes_hierarchy']} NH_TCV " .
    -           " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NH_TCSTEPS " .
    -           " ON NH_TCSTEPS.parent_id = NH_TCV.id ";
    -
    -    $parent = (array)($id);
    -    $sql .= " WHERE NH_TCV.parent_id IN (" .implode(',',$parent) . ") ";
    -    if($version_id != self::ALL_VERSIONS) {
    -      $sql .= " AND NH_TCV.id = {$version_id}";
    -    }
    -
    -    $children_rs = $this->db->get_recordset($sql);
    -    $do_it = !is_null($children_rs);
    -    if($do_it) {
    -      foreach($children_rs as $value) {
    -        $children['tcversion'][]=$value['tcversion_id'];
    -        $children['step'][]=$value['step_id'];
    -      }
    -      $this->_execution_delete($id,$version_id,$children);
    -      $this->deleteAllTestCaseRelations($id);
    -      $this->_blind_delete($id,$version_id,$children);
    -    }
    -
    -    $ctx = array('id' => $id);
    -    event_signal('EVENT_TEST_CASE_DELETE', $ctx);
    -
    -    return 1;
    -  }
    -
    -  /*
    -    function: get_linked_versions
    -              For a test case get information about versions linked to testplans.
    -              Filters can be applied on:
    -                                        execution status
    -                                        active status
    -
    -    args : id: testcase id
    -           [filters]
    -            [exec_status]: default: ALL, range: ALL,EXECUTED,NOT_EXECUTED
    -            [active_status]: default: ALL, range: ALL,ACTIVE,INACTIVE
    -            [tplan_id]
    -            [platform_id]
    -
    -           [options]
    -            [output] 'full', 'nosteps', 'simple' (no info about steps)
    -
    -      returns: map.
    -             key: version id
    -             value: map with following structure:
    -                    key: testplan id
    -                    value: map with following structure:
    -
    -                    testcase_id
    -                    tcversion_id
    -                    id -> tcversion_id (node id)
    -                    version
    -                    summary
    -                    importance
    -                    author_id
    -                    creation_ts
    -                    updater_id
    -                    modification_ts
    -                    active
    -                    is_open
    -                    testplan_id
    -                    tplan_name
    -  */
    -  function get_linked_versions($id,$filters=null,$options=null)
    -  {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -
    -    $my['filters'] = array( 'exec_status' => "ALL", 'active_status' => 'ALL',
    -                            'tplan_id' => null, 'platform_id' => null);
    -    $my['filters'] = array_merge($my['filters'], (array)$filters);
    -
    -    // 'output' => 'full', 'nosteps', 'simple' (no info about steps)
    -    //
    -    $my['options'] = array('output' => "full");
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -
    -    $exec_status = strtoupper($my['filters']['exec_status']);
    -    $active_status = strtoupper($my['filters']['active_status']);
    -    $tplan_id = $my['filters']['tplan_id'];
    -    $platform_id = $my['filters']['platform_id'];
    -
    -    $active_filter='';
    -    if($active_status !='ALL')
    -    {
    -      $active_filter=' AND tcversions.active=' . $active_status=='ACTIVE' ? 1 : 0;
    -    }
    -
    -    $fields2get = 'tc_external_id,version,status,importance,active, is_open,execution_type,';
    -
    -    switch($my['options']['output'])
    -    {
    -      case 'full':
    -      case 'nosteps':
    -      $fields2get .=  'layout,summary,preconditions,tcversions.author_id,tcversions.creation_ts,' .
    -                      'tcversions.updater_id,tcversions.modification_ts,';
    -      break;
    -
    -      case 'simple':
    -      break;
    -
    -      case 'feature_id':
    -        $fields2get .=  'TTC.id AS feature_id,';
    -      break;
    -
    -    }
    -
    -    switch ($exec_status)
    -    {
    -      case "ALL":
    -            $sql = "/* $debugMsg */ " .
    -               " SELECT NH.parent_id AS testcase_id, TTC.tcversion_id, TTC.testplan_id,  TTC.platform_id," .
    -               " tcversions.id, {$fields2get} " .
    -             " NHB.name AS tplan_name " .
    -             " FROM   {$this->tables['nodes_hierarchy']} NH," .
    -             " {$this->tables['tcversions']} tcversions," .
    -             " {$this->tables['testplan_tcversions']} TTC, " .
    -             " {$this->tables['nodes_hierarchy']} NHB    " .
    -             " WHERE  TTC.tcversion_id = tcversions.id {$active_filter} " .
    -             " AND    tcversions.id = NH.id " .
    -             " AND    NHB.id = TTC.testplan_id " .
    -             " AND    NH.parent_id = {$id}";
    -
    -            if (!is_null($tplan_id)) {
    -              $sql .= " AND TTC.testplan_id = {$tplan_id} ";
    -            }
    -
    -            if (!is_null($platform_id)) {
    -              $sql .= " AND TTC.platform_id = {$platform_id} ";
    -            }
    -
    -            $recordset = $this->db->fetchMapRowsIntoMap($sql,'tcversion_id','testplan_id',database::CUMULATIVE);
    -
    -        if (!is_null($recordset)) {
    -          // changes third access key from sequential index to platform_id
    -          foreach ($recordset as $accessKey => $testplan) {
    -            foreach ($testplan as $tplanKey => $testcases) {
    -              // Use a temporary array to avoid key collisions
    -              $newArray = array();
    -              foreach ($testcases as $elemKey => $element) {
    -                $platform_id = $element['platform_id'];
    -                $newArray[$platform_id] = $element;
    -              }
    -              $recordset[$accessKey][$tplanKey] = $newArray;
    -            }
    -          }
    -        }
    -      break;
    -
    -      case "EXECUTED":
    -      case "NOT_EXECUTED":
    -        $getFilters = array('exec_status' => $exec_status,
    -                            'active_status' => $active_status,
    -                            'tplan_id' => $tplan_id, 
    -                            'platform_id' => $platform_id);
    -        $recordset=$this->get_exec_status($id,$getFilters);
    -      break;
    -    }
    -
    -    // Multiple Test Case Steps
    -    if( !is_null($recordset) && ($my['options']['output'] == 'full') ) {
    -      $version2loop = array_keys($recordset);
    -      foreach( $version2loop as $accessKey) {
    -        // no options => will renderd Ghost Steps
    -        $step_set = $this->get_steps($accessKey);
    -        $tplan2loop = array_keys($recordset[$accessKey]);
    -        foreach( $tplan2loop as $tplanKey) {
    -          $elem2loop = array_keys($recordset[$accessKey][$tplanKey]);
    -          foreach( $elem2loop as $elemKey) {
    -            $recordset[$accessKey][$tplanKey][$elemKey]['steps'] = $step_set;
    -          }
    -        }
    -
    -      }
    -    }
    -
    -    return $recordset;
    -  }
    -
    -  /*
    -    Delete the following info:
    -    req_coverage
    -    risk_assignment
    -    custom fields
    -    keywords
    -    links to test plans
    -    tcversions
    -    nodes from hierarchy
    -    testcase_script_links
    -
    -  */
    -  function _blind_delete($id,$version_id=self::ALL_VERSIONS,$children=null) {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -    $sql = array();
    -
    -    $destroyTC = false;
    -    $item_id = $version_id;
    -    $tcversion_list = $version_id;
    -    $target_nodes = $version_id;
    -    if( $version_id == self::ALL_VERSIONS) {
    -      $destroyTC = true;
    -      $item_id = $id;
    -      $tcversion_list = implode(',',$children['tcversion']);
    -      $target_nodes = $children['tcversion'];
    -    }
    -
    -    $this->cfield_mgr->remove_all_design_values_from_node($target_nodes);
    -
    -    $sql[] = "/* $debugMsg */ 
    -              DELETE FROM {$this->tables['user_assignments']} 
    -              WHERE feature_id in (" .
    -             " SELECT id FROM {$this->tables['testplan_tcversions']}  " .
    -             " WHERE tcversion_id IN ({$tcversion_list}))";
    -
    -    $sql[]="/* $debugMsg */ 
    -            DELETE FROM {$this->tables['testplan_tcversions']} 
    -            WHERE tcversion_id IN ({$tcversion_list})";
    -
    -    // Multiple Test Case Steps Feature
    -    if( !is_null($children['step']) ) {
    -      // remove null elements
    -      foreach($children['step'] as $key => $value) {
    -        if(is_null($value)) {
    -          unset($children['step'][$key]);
    -        }
    -      }
    -
    -      if( count($children['step']) > 0) {
    -        $step_list=trim(implode(',',$children['step']));
    -        $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['tcsteps']}  " .
    -               " WHERE id IN ({$step_list})";
    -      }
    -    }
    -    $sql[]="/* $debugMsg */  
    -            DELETE FROM {$this->tables['testcase_script_links']} 
    -            WHERE tcversion_id IN ({$tcversion_list})";
    -
    -    $sql[]="/* $debugMsg */ 
    -             DELETE FROM {$this->tables['testcase_keywords']} 
    -             WHERE testcase_id = {$id} 
    -             AND tcversion_id IN ({$tcversion_list})";
    -
    -    $sql[]="/* $debugMsg */ 
    -            DELETE FROM {$this->tables['req_coverage']}  
    +             WHERE nodes_hierarchy.node_type_id={$my_node_type}";
    +        return $this->db->get_recordset($sql);
    +    }
    +
    +    /**
    +     * Show Test Case
    +     */
    +    public function show(&$smarty, $guiObj, $identity, $grants, $opt = null)
    +    {
    +        static $cfg;
    +        static $reqMgr;
    +        static $hidePreconditions;
    +        static $hideSummary;
    +
    +        if (! $cfg) {
    +            $cfg = config_get('spec_cfg');
    +            $reqMgr = new requirement_mgr($this->db);
    +
    +            // Investigate if special keywords are defined in the test project
    +            $tproject_id = intval($identity->tproject_id);
    +            $hidePreconditions = tlKeyword::doesKeywordExist($this->db,
    +                '@#HIDE_PRECONDITIONS_IF_EMPTY', $tproject_id);
    +            $hideSummary = tlKeyword::doesKeywordExist($this->db,
    +                '@#HIDE_SUMMARY_IF_EMPTY', $tproject_id);
    +            $hidePreconditions = ($hidePreconditions['kwID'] != null);
    +            $hideSummary = ($hideSummary['kwID'] != null);
    +        }
    +
    +        $status_ok = ($identity->id > 0);
    +        if (! $status_ok) {
    +            throw new Exception(
    +                __METHOD__ . ' EXCEPTION: Test Case ID is invalid ( <= 0)');
    +        }
    +
    +        $my = array(
    +            'opt' => array(
    +                'getAttachments' => false
    +            )
    +        );
    +        $my['opt'] = array_merge($my['opt'], (array) $opt);
    +
    +        $id = $identity->id;
    +        $idSet = (array) $id;
    +
    +        $idCard = new stdClass();
    +        $idCard->tcase_id = intval($id);
    +        $idCard->tproject_id = intval($identity->tproject_id);
    +
    +        $idCard->tcversion_id = isset($identity->version_id) ? $identity->version_id : self::ALL_VERSIONS;
    +
    +        $getVersionID = $idCard->tcversion_id;
    +
    +        $gui = $this->initShowGui($guiObj, $grants, $idCard);
    +
    +        $gui->hidePreconditions = $hidePreconditions;
    +        $gui->hideSummary = $hideSummary;
    +
    +        // When editing on execution, it's important to understand
    +        // is current displayed version is LINKED to Test Plan
    +        // to add or remove some features
    +        //
    +        $gui->candidateToUpd = 0;
    +        switch ($gui->show_mode) {
    +            case 'editOnExec':
    +                $gui->candidateToUpd = ! $this->isLinkedTCVersion(
    +                    $idCard->tcversion_id, $gui->tplan_id);
    +                $gui->new_version_source = 'latest';
    +                break;
    +
    +            default:
    +                break;
    +        }
    +
    +        $userIDSet = array();
    +
    +        if ($status_ok && count($idSet)) {
    +
    +            $cfPlaces = $this->buildCFLocationMap();
    +            $gui->linked_versions = null;
    +            $gopt = [
    +                'withGhostString' => true,
    +                'renderGhost' => true,
    +                'renderImageInline' => true,
    +                'renderVariables' => true,
    +                'renderSpecialKW' => true,
    +                'caller' => 'show()',
    +                'tproject_id' => $idCard->tproject_id
    +            ];
    +
    +            $cfx = 0;
    +            $gui->otherVersionsKeywords = array();
    +
    +            $gui->fileUploadURL = array();
    +            foreach ($idSet as $tc_id) {
    +                // IMPORTANT NOTICE
    +                // Deep Analysis is need to understand if there is an use case
    +                // where this method really receive an array of test case ID.
    +                //
    +                // using an specific value for test case version id has sense
    +                // only when we are working on ONE SPECIFIC Test Case.
    +                //
    +                // if we are working on a set of test cases, because this method
    +                // does not manage in input couple of (test case, versio id),
    +                // the only chance is to get ALL VERSIONS
    +                //
    +                if (! $tcvSet = $this->get_by_id($tc_id, $getVersionID, null,
    +                    $gopt)) {
    +                    continue;
    +                }
    +
    +                if ($cfg->show_tplan_usage) {
    +                    $gui->linked_versions[] = $this->get_linked_versions($tc_id);
    +                }
    +
    +                // Position 0 is latest active version
    +                $tcvSet[0]['tc_external_id'] = $gui->tcasePrefix .
    +                    $tcvSet[0]['tc_external_id'];
    +                $tcvSet[0]['ghost'] = sprintf(self::GHOSTMASK,
    +                    $tcvSet[0]['tc_external_id'], $tcvSet[0]['version']);
    +
    +                $tcvSet[0]['ghost_preconditions'] = sprintf(
    +                    self::GHOSTPRECONDITIONSMASK, $tcvSet[0]['tc_external_id'],
    +                    $tcvSet[0]['version']);
    +
    +                // status quo of execution and links of tc versions
    +                $gui->status_quo[] = $this->getVersionsStatusQuo($tc_id);
    +
    +                // Logic on Current/Latest Test Case Version
    +                $tc_current = $tcvSet[0];
    +                $tc_current['isTheLatest'] = 1;
    +                $currentVersionID = $tc_current['id'];
    +
    +                $io = $idCard;
    +                $io->tcversion_id = $currentVersionID;
    +
    +                $gui->delAttachmentURL = $_SESSION['basehref'] .
    +                    $this->getDeleteAttachmentByIDRelativeURL($io, $gui);
    +
    +                $gui->delTCVRelationURL = $_SESSION['basehref'] .
    +                    $this->getDeleteTCVRelationRelativeURL($gui);
    +
    +                $gui->delTCVKeywordURL = $_SESSION['basehref'] .
    +                    $this->getDeleteTCVKeywordRelativeURL($gui);
    +
    +                $gui->delTCVPlatformURL = $_SESSION['basehref'] .
    +                    $this->getDeleteTCVPlatformRelativeURL($gui);
    +
    +                // Impacted for version management
    +                $gui->fileUploadURL[$currentVersionID] = $_SESSION['basehref'] .
    +                    $this->getFileUploadRelativeURL($io);
    +
    +                $gui->tc_current_version[] = array(
    +                    $tc_current
    +                );
    +
    +                //
    +                // REFACTORING - Following code uses tc_current!!!
    +                //
    +
    +                // Get UserID and Updater ID for current Version
    +                $userIDSet[$tc_current['author_id']] = null;
    +                $userIDSet[$tc_current['updater_id']] = null;
    +
    +                $gui->req4current_version = $reqMgr->getGoodForTCVersion(
    +                    $currentVersionID);
    +
    +                $gui->currentVersionKeywords = $this->getKeywords($tc_id,
    +                    $currentVersionID);
    +
    +                $gui->currentVersionPlatforms = $this->getPlatforms($tc_id,
    +                    $currentVersionID);
    +
    +                $whoami = array(
    +                    'tcase_id' => $tc_id,
    +                    'tcversion_id' => $currentVersionID
    +                );
    +
    +                $of = array(
    +                    'output' => 'html_options',
    +                    'add_blank' => true
    +                );
    +                $gui->currentVersionFreeKeywords = $this->getFreeKeywords(
    +                    $whoami, $of);
    +
    +                $gui->currentVersionFreePlatforms = $this->getFreePlatforms(
    +                    $whoami, $of);
    +
    +                if ($my['opt']['getAttachments']) {
    +                    $gui->attachments[$currentVersionID] = getAttachmentInfosFrom(
    +                        $this, $currentVersionID);
    +                }
    +
    +                // get linked testcase scripts
    +                if ($gui->codeTrackerEnabled) {
    +                    $scripts = $this->getScriptsForTestCaseVersion($gui->cts,
    +                        $currentVersionID);
    +                    if (! is_null($scripts)) {
    +                        $gui->scripts[$currentVersionID] = $scripts;
    +                    }
    +                }
    +
    +                if ($this->cfg->testcase->relations->enable) {
    +                    $xm = array(
    +                        'tcase_id' => $tc_id,
    +                        'tcversion_id' => $currentVersionID
    +                    );
    +                    $gui->relationSet[] = $this->getTCVersionRelations($xm);
    +                }
    +
    +                $cfCtx = [
    +                    'scope' => 'design',
    +                    'tproject_id' => $gui->tproject_id,
    +                    'link_id' => $tc_current['id']
    +                ];
    +
    +                foreach ($cfPlaces as $cfpKey => $cfpFilter) {
    +                    // we need to do this when in display mode
    +                    switch ($cfpKey) {
    +                        case 'hide_because_is_used_as_variable':
    +                            break;
    +
    +                        default:
    +                            $gui->cf_current_version[$cfx][$cfpKey] = $this->htmlTableOfCFValues(
    +                                $tc_id, $cfCtx, $cfpFilter);
    +                            break;
    +                    }
    +                }
    +
    +                // Other versions (if exists)
    +                if (count($tcvSet) > 1) {
    +                    $gui->testcase_other_versions[] = array_slice($tcvSet, 1);
    +
    +                    $target_idx = count($gui->testcase_other_versions) - 1;
    +                    $loop2do = count($gui->testcase_other_versions[$target_idx]);
    +
    +                    $cfCtx = array(
    +                        'scope' => 'design',
    +                        'tproject_id' => $gui->tproject_id
    +                    );
    +
    +                    $ref = &$gui->testcase_other_versions[$target_idx];
    +                    for ($qdx = 0; $qdx < $loop2do; $qdx ++) {
    +
    +                        $gui->testcase_other_versions[$target_idx][$qdx]['isTheLatest'] = 0;
    +
    +                        $ref[$qdx]['ghost'] = sprintf(self::GHOSTMASK,
    +                            $tcvSet[0]['tc_external_id'], $ref[$qdx]['version']);
    +
    +                        $cfCtx['link_id'] = $gui->testcase_other_versions[$target_idx][$qdx]['id'];
    +                        foreach ($cfPlaces as $locKey => $locFilter) {
    +                            switch ($cfpKey) {
    +                                case 'hide_because_is_used_as_variable':
    +                                    break;
    +
    +                                default:
    +                                    $gui->cf_other_versions[$cfx][$qdx][$locKey] = $this->htmlTableOfCFValues(
    +                                        $tc_id, $cfCtx, $locFilter);
    +                                    break;
    +                            }
    +                        }
    +                    }
    +                } else {
    +                    $gui->testcase_other_versions[] = null;
    +                    $gui->otherVersionsRelations[] = null;
    +                    $gui->cf_other_versions[$cfx] = null;
    +                }
    +
    +                $cfx ++;
    +
    +                if ($gui->testcase_other_versions[0]) {
    +
    +                    // Get author and updater id for each version
    +                    foreach ($gui->testcase_other_versions[0] as $version) {
    +
    +                        $userIDSet[$version['author_id']] = null;
    +                        $userIDSet[$version['updater_id']] = null;
    +
    +                        if ($this->cfg->testcase->relations->enable) {
    +                            $xm = array(
    +                                'tcase_id' => $version['testcase_id'],
    +                                'tcversion_id' => $version['id'],
    +                                'other' => 'other'
    +                            );
    +                            $gui->otherVersionsRelations[] = $this->getTCVersionRelations(
    +                                $xm);
    +                        }
    +
    +                        // get linked testcase scripts
    +                        if ($gui->codeTrackerEnabled) {
    +                            $scripts = $this->getScriptsForTestCaseVersion(
    +                                $gui->cts, $version['id']);
    +                            if (! is_null($scripts)) {
    +                                $gui->scripts[$version['id']] = $scripts;
    +                            }
    +                        }
    +
    +                        if ($my['opt']['getAttachments']) {
    +                            $gui->attachments[$version['id']] = getAttachmentInfosFrom(
    +                                $this, $version['id']);
    +                        }
    +
    +                        $io = $idCard;
    +                        $io->tcversion_id = $version['id'];
    +
    +                        $gui->fileUploadURL[$version['id']] = $_SESSION['basehref'] .
    +                            $this->getFileUploadRelativeURL($io);
    +
    +                        // Requirements
    +                        $gui->req4OtherVersions[] = $reqMgr->getGoodForTCVersion(
    +                            $version['id']);
    +
    +                        $gui->otherVersionsKeywords[] = $this->getKeywords(
    +                            $version['testcase_id'], $version['id']);
    +
    +                        $gui->otherVersionsPlatforms[] = $this->getPlatforms(
    +                            $version['testcase_id'], $version['id']);
    +                    }
    +                } // Other versions exist
    +            }
    +        }
    +
    +        $gui->relations = $gui->relationSet;
    +        $gui->relation_domain = '';
    +        if ($this->cfg->testcase->relations->enable) {
    +            $gui->relation_domain = $this->getRelationTypeDomainForHTMLSelect();
    +        }
    +
    +        // Removing duplicate and NULL id's
    +        unset($userIDSet['']);
    +        $gui->users = tlUser::getByIDs($this->db, array_keys($userIDSet));
    +        $gui->cf = null;
    +
    +        $this->initShowGuiActions($gui);
    +        $tplCfg = templateConfiguration('tcView');
    +
    +        $gui->additionalMessages = [];
    +        if ($gui->currentVersionKeywords != null &&
    +            count($gui->currentVersionKeywords) > 0) {
    +            // look for annotations in notes
    +            foreach ($gui->currentVersionKeywords as $kwEntity) {
    +                foreach ($this->keywordAnnotations as $kwAnnot) {
    +                    if (strpos($kwEntity['notes'], $kwAnnot) !== false) {
    +                        $gui->additionalMessages[] = json_decode(
    +                            str_replace($kwAnnot, '',
    +                                explode("/@", $kwEntity['notes'])[0]));
    +                        break;
    +                    }
    +                }
    +            }
    +        }
    +        $smarty->assign('gui', $gui);
    +        $smarty->display($tplCfg->template_dir . $tplCfg->default_template);
    +    }
    +
    +    /**
    +     * update test case specification
    +     *
    +     * @param integer $id
    +     *            Test case unique identifier (node_hierarchy table)
    +     * @param integer $tcversion_id
    +     *            Test Case Version unique ID (node_hierarchy table)
    +     * @param string $name
    +     *            name/title
    +     * @param string $summary
    +     * @param string $preconditions
    +     * @param array $steps
    +     *            steps + expected results
    +     * @param integer $user_id
    +     *            who is doing the update
    +     * @param string $keywords_id
    +     *            optional list of keyword id to be linked to test case
    +     *            this list will override previous keyword links (delete + insert).
    +     *
    +     * @param integer $tc_order
    +     *            optional order inside parent test suite
    +     * @param integer $execution_type
    +     *            optional
    +     * @param integer $importance
    +     *            optional
    +     *
    +     *
    +     *
    +     */
    +    public function update($id, $tcversion_id, $name, $summary, $preconditions,
    +        $steps, $user_id, $keywords_id = '', $tc_order = self::DEFAULT_ORDER,
    +        $execution_type = TESTCASE_EXECUTION_TYPE_MANUAL, $importance = 2,
    +        $attr = null, $opt = null)
    +    {
    +        $ret['status_ok'] = 1;
    +        $ret['msg'] = '';
    +        $ret['reason'] = '';
    +
    +        $my['opt'] = array(
    +            'blockIfExecuted' => false
    +        );
    +        $my['opt'] = array_merge($my['opt'], (array) $opt);
    +
    +        $attrib = array(
    +            'status' => null,
    +            'is_open' => null,
    +            'active' => null,
    +            'estimatedExecDuration' => null
    +        );
    +        $attrib = array_merge($attrib, (array) $attr);
    +
    +        tLog(
    +            "TC UPDATE ID=($id): exec_type=$execution_type importance=$importance");
    +
    +        if (trim($summary) != '' && strpos($summary, self::NAME_PHOPEN) !== false &&
    +            strpos($summary, self::NAME_PHCLOSE) !== false) {
    +            $name = $this->buildTCName($name, $summary);
    +        }
    +
    +        // Check if new name will be create a duplicate testcase under same parent
    +        if (config_get('check_names_for_duplicates')) {
    +            // get my parent
    +            $mi = $this->tree_manager->get_node_hierarchy_info($id);
    +            $itemSet = $this->getDuplicatesByName($name, $mi['parent_id'],
    +                array(
    +                    'id2exclude' => $id
    +                ));
    +
    +            if (! is_null($itemSet)) {
    +                $ret['status_ok'] = false;
    +                $ret['msg'] = sprintf(lang_get('name_already_exists'), $name);
    +                $ret['reason'] = 'already_exists';
    +                $ret['hit_on'] = current($itemSet);
    +            }
    +
    +            if (! $ret['status_ok']) {
    +                // get more info for feedback
    +            }
    +        }
    +
    +        if ($ret['status_ok']) {
    +            if ($my['opt']['blockIfExecuted']) {
    +                // When tcversion is updated on test plan after an executio exists
    +                // execution tcversion_number keeps the version of test case executed
    +                // will EX.tcversion_id is updated with id requested by user.
    +                // That's why when importing we need to check HUMAN READEABLE version numbers.
    +                $sql = " SELECT EX.id, EX.tcversion_number,TCV.version " .
    +                    " FROM {$this->tables['executions']} EX " .
    +                    " JOIN {$this->tables['tcversions']} TCV " .
    +                    " ON TCV.id = EX.tcversion_id " . " WHERE tcversion_id=" .
    +                    $this->db->prepare_int($tcversion_id);
    +
    +                $rs = $this->db->get_recordset($sql);
    +                if (! is_null($rs)) {
    +                    foreach ($rs as $rwx) {
    +                        if ($rwx['tcversion_number'] == $rwx['version']) {
    +                            $ret['status_ok'] = false;
    +                            $ret['msg'] = lang_get('block_ltcv_hasbeenexecuted');
    +                            $ret['reason'] = 'blockIfExecuted';
    +                            return $ret;
    +                        }
    +                    }
    +                }
    +            }
    +
    +            $sql = array();
    +            $sql[] = " UPDATE {$this->tables['nodes_hierarchy']} SET name='" .
    +                $this->db->prepare_string($name) . "' WHERE id= {$id}";
    +
    +            $k2e = array(
    +                'summary',
    +                'preconditions'
    +            );
    +            $item = new stdClass();
    +            $item->summary = $summary;
    +            $item->preconditions = $preconditions;
    +            $this->ckEditorCopyAndPasteCleanUp($item, $k2e);
    +
    +            $dummy = " UPDATE {$this->tables['tcversions']} " . " SET summary='" .
    +                $this->db->prepare_string($item->summary) . "'," . " updater_id=" .
    +                $this->db->prepare_int($user_id) . ", " . " modification_ts = " .
    +                $this->db->db_now() . "," . " execution_type=" .
    +                $this->db->prepare_int($execution_type) . ", " . " importance=" .
    +                $this->db->prepare_int($importance) . "," . " preconditions='" .
    +                $this->db->prepare_string($item->preconditions) . "' ";
    +
    +            if (! is_null($attrib['status'])) {
    +                $dummy .= ", status=" . intval($attrib['status']);
    +            }
    +
    +            if (! is_null($attrib['is_open'])) {
    +                $dummy .= ", is_open=" . intval($attrib['is_open']);
    +            }
    +
    +            if (! is_null($attrib['active'])) {
    +                $dummy .= ", active=" . intval($attrib['active']);
    +            }
    +
    +            if (! is_null($attrib['estimatedExecDuration'])) {
    +                $dummy .= ", estimated_exec_duration=";
    +                $v = trim($attrib['estimatedExecDuration']);
    +
    +                $dummy .= ($v == '') ? "NULL" : floatval($v);
    +            }
    +
    +            $dummy .= " WHERE id = " . $this->db->prepare_int($tcversion_id);
    +            $sql[] = $dummy;
    +
    +            foreach ($sql as $stm) {
    +                $result = $this->db->exec_query($stm);
    +                if (! $result) {
    +                    $ret['status_ok'] = 0;
    +                    $ret['msg'] = $this->db->error_msg;
    +                    break;
    +                }
    +            }
    +
    +            if ($ret['status_ok'] && ! is_null($steps)) {
    +                $this->update_tcversion_steps($tcversion_id, $steps);
    +            }
    +
    +            if ($ret['status_ok']) {
    +
    +                $idCard = array(
    +                    'id' => $id,
    +                    'version_id' => $tcversion_id,
    +                    'version' => $this->getVersionNumber($tcversion_id)
    +                );
    +
    +                $this->updateKeywordAssignment($idCard, $keywords_id);
    +            }
    +
    +            $ctx = array(
    +                'id' => $id,
    +                'version_id' => $tcversion_id,
    +                'name' => $name,
    +                'summary' => $summary,
    +                'preconditions' => $preconditions,
    +                'steps' => $steps,
    +                'user_id' => $user_id,
    +                'keywords_id' => $keywords_id,
    +                'order' => $tc_order,
    +                'exec_type' => $execution_type,
    +                'importance' => $importance,
    +                'attr' => $attr,
    +                'options' => $opt
    +            );
    +            event_signal('EVENT_TEST_CASE_UPDATE', $ctx);
    +        }
    +
    +        return $ret;
    +    }
    +
    +    /**
    +     * used when updating a test case
    +     */
    +    private function updateKeywordAssignment($idCard, $keywords_id)
    +    {
    +        // To avoid false loggings, check is delete is needed
    +        $id = intval($idCard['id']);
    +        $version_id = intval($idCard['version_id']);
    +        $version = intval($idCard['version']);
    +
    +        $items = array();
    +        $items['stored'] = $this->get_keywords_map($id, $version_id);
    +        if (is_null($items['stored'])) {
    +            $items['stored'] = array();
    +        }
    +
    +        $items['requested'] = array();
    +        if (trim($keywords_id) != "") {
    +            $a_keywords = explode(",", trim($keywords_id));
    +            $sql = " SELECT id,keyword " . " FROM {$this->tables['keywords']} " .
    +                " WHERE id IN (" . implode(',', $a_keywords) . ")";
    +
    +            $items['requested'] = $this->db->fetchColumnsIntoMap($sql, 'id',
    +                'keyword');
    +        }
    +
    +        $items['common'] = array_intersect_assoc($items['stored'],
    +            $items['requested']);
    +        $items['new'] = array_diff_assoc($items['requested'], $items['common']);
    +        $items['todelete'] = array_diff_assoc($items['stored'], $items['common']);
    +
    +        $auditContext = array(
    +            'on' => self::AUDIT_ON,
    +            'version' => $version
    +        );
    +
    +        if (! is_null($items['todelete']) && count($items['todelete'])) {
    +            $this->deleteKeywords($id, $version_id,
    +                array_keys($items['todelete']), $auditContext);
    +        }
    +
    +        if (! is_null($items['new']) && count($items['new'])) {
    +            $this->addKeywords($id, $version_id, array_keys($items['new']),
    +                $auditContext);
    +        }
    +    }
    +
    +    /*
    +     * function: check_link_and_exec_status
    +     * Fore every version of testcase (id), do following checks:
    +     *
    +     * 1. testcase is linked to one of more test plans ?
    +     * 2. if anwser is yes then,check if has been executed => has records on executions table
    +     *
    +     * args : id: testcase id
    +     *
    +     * returns: string with following values:
    +     * no_links: testcase is not linked to any testplan
    +     * linked_but_not_executed: testcase is linked at least to a testplan
    +     * but has not been executed.
    +     *
    +     * linked_and_executed: testcase is linked at least to a testplan and
    +     * has been executed => has records on executions table.
    +     *
    +     *
    +     */
    +    private function checkLinkAndExecStatus($id)
    +    {
    +        $status = 'no_links';
    +
    +        // get linked versions
    +        // ATTENTION TO PLATFORMS
    +        $linked_tcversions = $this->get_linked_versions($id);
    +        $has_links_to_testplans = is_null($linked_tcversions) ? 0 : 1;
    +
    +        if ($has_links_to_testplans) {
    +            // check if executed
    +            $linked_not_exec = $this->get_linked_versions($id,
    +                array(
    +                    'exec_status' => 'NOT_EXECUTED'
    +                ));
    +
    +            $status = 'linked_and_executed';
    +            if (count($linked_tcversions) == count($linked_not_exec)) {
    +                $status = 'linked_but_not_executed';
    +            }
    +        }
    +        return $status;
    +    }
    +
    +    /**
    +     */
    +    public function delete($id, $version_id = self::ALL_VERSIONS)
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +        $children = null;
    +        $do_it = true;
    +
    +        // I'm trying to speedup the next deletes
    +        $sql = "/* $debugMsg */ " .
    +            " SELECT NH_TCV.id AS tcversion_id, NH_TCSTEPS.id AS step_id " .
    +            " FROM {$this->tables['nodes_hierarchy']} NH_TCV " .
    +            " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NH_TCSTEPS " .
    +            " ON NH_TCSTEPS.parent_id = NH_TCV.id ";
    +
    +        $parent = (array) ($id);
    +        $sql .= " WHERE NH_TCV.parent_id IN (" . implode(',', $parent) . ") ";
    +        if ($version_id != self::ALL_VERSIONS) {
    +            $sql .= " AND NH_TCV.id = {$version_id}";
    +        }
    +
    +        $children_rs = $this->db->get_recordset($sql);
    +        $do_it = ! is_null($children_rs);
    +        if ($do_it) {
    +            foreach ($children_rs as $value) {
    +                $children['tcversion'][] = $value['tcversion_id'];
    +                $children['step'][] = $value['step_id'];
    +            }
    +            $this->_execution_delete($id, $version_id, $children);
    +            $this->deleteAllTestCaseRelations($id);
    +            $this->_blind_delete($id, $version_id, $children);
    +        }
    +
    +        $ctx = array(
    +            'id' => $id
    +        );
    +        event_signal('EVENT_TEST_CASE_DELETE', $ctx);
    +
    +        return 1;
    +    }
    +
    +    /*
    +     * function: get_linked_versions
    +     * For a test case get information about versions linked to testplans.
    +     * Filters can be applied on:
    +     * execution status
    +     * active status
    +     *
    +     * args : id: testcase id
    +     * [filters]
    +     * [exec_status]: default: ALL, range: ALL,EXECUTED,NOT_EXECUTED
    +     * [active_status]: default: ALL, range: ALL,ACTIVE,INACTIVE
    +     * [tplan_id]
    +     * [platform_id]
    +     *
    +     * [options]
    +     * [output] 'full', 'nosteps', 'simple' (no info about steps)
    +     *
    +     * returns: map.
    +     * key: version id
    +     * value: map with following structure:
    +     * key: testplan id
    +     * value: map with following structure:
    +     *
    +     * testcase_id
    +     * tcversion_id
    +     * id -> tcversion_id (node id)
    +     * version
    +     * summary
    +     * importance
    +     * author_id
    +     * creation_ts
    +     * updater_id
    +     * modification_ts
    +     * active
    +     * is_open
    +     * testplan_id
    +     * tplan_name
    +     */
    +    public function get_linked_versions($id, $filters = null, $options = null)
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +
    +        $my['filters'] = array(
    +            'exec_status' => "ALL",
    +            'active_status' => 'ALL',
    +            'tplan_id' => null,
    +            'platform_id' => null
    +        );
    +        $my['filters'] = array_merge($my['filters'], (array) $filters);
    +
    +        // 'output' => 'full', 'nosteps', 'simple' (no info about steps)
    +        //
    +        $my['options'] = array(
    +            'output' => "full"
    +        );
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        $exec_status = strtoupper($my['filters']['exec_status']);
    +        $active_status = strtoupper($my['filters']['active_status']);
    +        $tplan_id = $my['filters']['tplan_id'];
    +        $platform_id = $my['filters']['platform_id'];
    +
    +        $active_filter = '';
    +        if ($active_status != 'ALL') {
    +            $active_filter = ' AND tcversions.active=' . $active_status ==
    +                'ACTIVE' ? 1 : 0;
    +        }
    +
    +        $fields2get = 'tc_external_id,version,status,importance,active, is_open,execution_type,';
    +
    +        switch ($my['options']['output']) {
    +            case 'full':
    +            case 'nosteps':
    +                $fields2get .= 'layout,summary,preconditions,tcversions.author_id,tcversions.creation_ts,' .
    +                    'tcversions.updater_id,tcversions.modification_ts,';
    +                break;
    +
    +            case 'simple':
    +                break;
    +
    +            case 'feature_id':
    +                $fields2get .= 'TTC.id AS feature_id,';
    +                break;
    +        }
    +
    +        switch ($exec_status) {
    +            case "ALL":
    +                $sql = "/* $debugMsg */ " .
    +                    " SELECT NH.parent_id AS testcase_id, TTC.tcversion_id, TTC.testplan_id,  TTC.platform_id," .
    +                    " tcversions.id, {$fields2get} " . " NHB.name AS tplan_name " .
    +                    " FROM   {$this->tables['nodes_hierarchy']} NH," .
    +                    " {$this->tables['tcversions']} tcversions," .
    +                    " {$this->tables['testplan_tcversions']} TTC, " .
    +                    " {$this->tables['nodes_hierarchy']} NHB    " .
    +                    " WHERE  TTC.tcversion_id = tcversions.id {$active_filter} " .
    +                    " AND    tcversions.id = NH.id " .
    +                    " AND    NHB.id = TTC.testplan_id " .
    +                    " AND    NH.parent_id = {$id}";
    +
    +                if (! is_null($tplan_id)) {
    +                    $sql .= " AND TTC.testplan_id = {$tplan_id} ";
    +                }
    +
    +                if (! is_null($platform_id)) {
    +                    $sql .= " AND TTC.platform_id = {$platform_id} ";
    +                }
    +
    +                $recordset = $this->db->fetchMapRowsIntoMap($sql, 'tcversion_id',
    +                    'testplan_id', database::CUMULATIVE);
    +
    +                if (! is_null($recordset)) {
    +                    // changes third access key from sequential index to platform_id
    +                    foreach ($recordset as $accessKey => $testplan) {
    +                        foreach ($testplan as $tplanKey => $testcases) {
    +                            // Use a temporary array to avoid key collisions
    +                            $newArray = array();
    +                            foreach ($testcases as $element) {
    +                                $platform_id = $element['platform_id'];
    +                                $newArray[$platform_id] = $element;
    +                            }
    +                            $recordset[$accessKey][$tplanKey] = $newArray;
    +                        }
    +                    }
    +                }
    +                break;
    +
    +            case "EXECUTED":
    +            case "NOT_EXECUTED":
    +                $getFilters = array(
    +                    'exec_status' => $exec_status,
    +                    'active_status' => $active_status,
    +                    'tplan_id' => $tplan_id,
    +                    'platform_id' => $platform_id
    +                );
    +                $recordset = $this->getExecStatus($id, $getFilters);
    +                break;
    +        }
    +
    +        // Multiple Test Case Steps
    +        if (! is_null($recordset) && ($my['options']['output'] == 'full')) {
    +            $version2loop = array_keys($recordset);
    +            foreach ($version2loop as $accessKey) {
    +                // no options => will renderd Ghost Steps
    +                $step_set = $this->get_steps($accessKey);
    +                $tplan2loop = array_keys($recordset[$accessKey]);
    +                foreach ($tplan2loop as $tplanKey) {
    +                    $elem2loop = array_keys($recordset[$accessKey][$tplanKey]);
    +                    foreach ($elem2loop as $elemKey) {
    +                        $recordset[$accessKey][$tplanKey][$elemKey]['steps'] = $step_set;
    +                    }
    +                }
    +            }
    +        }
    +
    +        return $recordset;
    +    }
    +
    +    /*
    +     * Delete the following info:
    +     * req_coverage
    +     * risk_assignment
    +     * custom fields
    +     * keywords
    +     * links to test plans
    +     * tcversions
    +     * nodes from hierarchy
    +     * testcase_script_links
    +     *
    +     */
    +    private function _blind_delete($id, $version_id = self::ALL_VERSIONS,
    +        $children = null)
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +        $sql = array();
    +
    +        $destroyTC = false;
    +        $item_id = $version_id;
    +        $tcversion_list = $version_id;
    +        $target_nodes = $version_id;
    +        if ($version_id == self::ALL_VERSIONS) {
    +            $destroyTC = true;
    +            $item_id = $id;
    +            $tcversion_list = implode(',', $children['tcversion']);
    +            $target_nodes = $children['tcversion'];
    +        }
    +
    +        $this->cfield_mgr->remove_all_design_values_from_node($target_nodes);
    +
    +        $sql[] = "/* $debugMsg */
    +              DELETE FROM {$this->tables['user_assignments']}
    +              WHERE feature_id in (" .
    +            " SELECT id FROM {$this->tables['testplan_tcversions']}  " .
    +            " WHERE tcversion_id IN ({$tcversion_list}))";
    +
    +        $sql[] = "/* $debugMsg */
    +            DELETE FROM {$this->tables['testplan_tcversions']}
    +            WHERE tcversion_id IN ({$tcversion_list})";
    +
    +        // Multiple Test Case Steps Feature
    +        if (! is_null($children['step'])) {
    +            // remove null elements
    +            foreach ($children['step'] as $key => $value) {
    +                if (is_null($value)) {
    +                    unset($children['step'][$key]);
    +                }
    +            }
    +
    +            if (count($children['step']) > 0) {
    +                $step_list = trim(implode(',', $children['step']));
    +                $sql[] = "/* $debugMsg */ DELETE FROM {$this->tables['tcsteps']}  " .
    +                    " WHERE id IN ({$step_list})";
    +            }
    +        }
    +
    +        $sql[] = "/* $debugMsg */
    +            DELETE FROM {$this->tables['testcase_script_links']}
    +            WHERE tcversion_id IN ({$tcversion_list})";
    +
    +        $sql[] = "/* $debugMsg */
    +             DELETE FROM {$this->tables['testcase_keywords']}
    +             WHERE testcase_id = {$id}
    +             AND tcversion_id IN ({$tcversion_list})";
    +
    +        $sql[] = "/* $debugMsg */
    +            DELETE FROM {$this->tables['req_coverage']}
                 WHERE testcase_id = {$id}
    -            AND tcversion_id IN ({$tcversion_list})";
    -
    -
    -    // This has to be the last, to avoid FK issues
    -    $sql[]="/* $debugMsg */ 
    -            DELETE FROM {$this->tables['tcversions']} 
    -            WHERE id IN ({$tcversion_list})";
    -
    -
    -    foreach ($sql as $the_stm) {
    -      $result = $this->db->exec_query($the_stm);
    -    }
    -
    -    if( !$destroyTC ) {
    -      $toloop = array( $version_id );
    -      foreach( $toloop as $nu ) {
    -        $this->deleteAttachments($nu);
    -      }
    -    }
    -
    -    if($destroyTC) {
    -      // Remove data that is related to Test Case => must be deleted when there is no more trace
    -      // of test case => when all version are deleted
    -      $sql = null;
    -      $sql[]="/* $debugMsg */ 
    -             DELETE FROM {$this->tables['testcase_keywords']} 
    -             WHERE testcase_id = {$id}";
    -
    -      $sql[]="/* $debugMsg */ 
    -              DELETE FROM {$this->tables['req_coverage']}  
    -              WHERE testcase_id = {$id}";
    -
    -      foreach ($sql as $the_stm) {
    -        $result = $this->db->exec_query($the_stm);
    -      }
    -
    -      // $this->deleteAttachments($id);
    -      if( $version_id == self::ALL_VERSIONS ) {
    -        $toloop = explode(',',$tcversion_list);
    -      } 
    -      foreach( $toloop as $nu ) {
    -        $this->deleteAttachments($nu);
    -      }
    -    }
    -
    -    // Attention:
    -    // After addition of test case steps feature, a test case version can be root of
    -    // a subtree that contains the steps.
    -    $this->tree_manager->delete_subtree($item_id);
    -  }
    -
    -
    -  /*
    -    Delete the following info:
    -    bugs
    -    executions
    -    cfield_execution_values
    -  */
    -  function _execution_delete($id,$version_id=self::ALL_VERSIONS,$children=null) {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -    $sql = array();
    -
    -    if( $version_id == self::ALL_VERSIONS ) {
    -      $tcversion_list = implode(',',$children['tcversion']);
    -
    -      $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['execution_tcsteps']} " .
    -             " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
    -             " WHERE tcversion_id IN ({$tcversion_list}))";
    -
    -      $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['execution_bugs']} " .
    -             " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
    -             " WHERE tcversion_id IN ({$tcversion_list}))";
    -
    -      $sql[] = "/* $debugMsg */ 
    +            AND tcversion_id IN ({$tcversion_list})";
    +
    +        // This has to be the last, to avoid FK issues
    +        $sql[] = "/* $debugMsg */
    +            DELETE FROM {$this->tables['tcversions']}
    +            WHERE id IN ({$tcversion_list})";
    +
    +        foreach ($sql as $the_stm) {
    +            $this->db->exec_query($the_stm);
    +        }
    +
    +        if (! $destroyTC) {
    +            $toloop = array(
    +                $version_id
    +            );
    +            foreach ($toloop as $nu) {
    +                $this->deleteAttachments($nu);
    +            }
    +        }
    +
    +        if ($destroyTC) {
    +            // Remove data that is related to Test Case => must be deleted when there is no more trace
    +            // of test case => when all version are deleted
    +            $sql = null;
    +            $sql[] = "/* $debugMsg */
    +             DELETE FROM {$this->tables['testcase_keywords']}
    +             WHERE testcase_id = {$id}";
    +
    +            $sql[] = "/* $debugMsg */
    +              DELETE FROM {$this->tables['req_coverage']}
    +              WHERE testcase_id = {$id}";
    +
    +            foreach ($sql as $the_stm) {
    +                $this->db->exec_query($the_stm);
    +            }
    +
    +            if ($version_id == self::ALL_VERSIONS) {
    +                $toloop = explode(',', $tcversion_list);
    +            }
    +            foreach ($toloop as $nu) {
    +                $this->deleteAttachments($nu);
    +            }
    +        }
    +
    +        // Attention:
    +        // After addition of test case steps feature, a test case version can be root of
    +        // a subtree that contains the steps.
    +        $this->tree_manager->delete_subtree($item_id);
    +    }
    +
    +    /*
    +     * Delete the following info:
    +     * bugs
    +     * executions
    +     * cfield_execution_values
    +     */
    +    private function _execution_delete($id, $version_id = self::ALL_VERSIONS,
    +        $children = null)
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +        $sql = array();
    +
    +        $step_list = '';
    +        if (! is_null($children['step'])) {
    +            // remove null elements
    +            foreach ($children['step'] as $key => $value) {
    +                if (is_null($value)) {
    +                    unset($children['step'][$key]);
    +                }
    +            }
    +
    +            if (count($children['step']) > 0) {
    +                $step_list = trim(implode(',', $children['step']));
    +            }
    +        }
    +
    +        if ($version_id == self::ALL_VERSIONS) {
    +
    +            if ($step_list != '') {
    +                $sql[] = "/* $debugMsg */
    +                  DELETE FROM {$this->tables['execution_tcsteps_wip']}
    +                  WHERE tcstep_id IN ({$step_list})";
    +            }
    +
    +            $tcversion_list = implode(',', $children['tcversion']);
    +
    +            $sql[] = "/* $debugMsg */ DELETE FROM {$this->tables['execution_tcsteps']} " .
    +                " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
    +                " WHERE tcversion_id IN ({$tcversion_list}))";
    +
    +            $sql[] = "/* $debugMsg */ DELETE FROM {$this->tables['execution_bugs']} " .
    +                " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
    +                " WHERE tcversion_id IN ({$tcversion_list}))";
    +
    +            $sql[] = "/* $debugMsg */
                     DELETE FROM {$this->tables['cfield_execution_values']}
    -                WHERE tcversion_id IN ({$tcversion_list}) ";
    -
    -      $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['executions']}  " .
    -             " WHERE tcversion_id IN ({$tcversion_list})";
    -    } else {
    -
    -      $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['execution_tcsteps']} " .
    -             " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
    -             " WHERE tcversion_id = {$version_id})";
    -
    -      $sql[]="/* $debugMsg */  DELETE FROM {$this->tables['execution_bugs']} " .
    -             " WHERE execution_id IN (SELECT id FROM {$this->tables['executions']} " .
    -             " WHERE tcversion_id = {$version_id})";
    -
    -      $sql[]="/* $debugMsg */ 
    -              DELETE FROM {$this->tables['cfield_execution_values']}
    -              WHERE tcversion_id = {$version_id}";
    -
    -      $sql[]="/* $debugMsg */ DELETE FROM {$this->tables['executions']} " .
    -             " WHERE tcversion_id = {$version_id}";
    -    }
    -
    -    foreach ($sql as $the_stm) {
    -      $result = $this->db->exec_query($the_stm);
    -    }
    -  }
    -
    -
    -  /*
    -    function: formatTestCaseIdentity
    -
    -    args: id: testcase id
    -          external_id
    -
    -    returns: testproject id
    -
    -  */
    -  function formatTestCaseIdentity($id,$external_id=null)
    -  {
    -    $path2root = $this->tree_manager->get_path($tc_id);
    -    $tproject_id = $path2root[0]['parent_id'];
    -    $tcasePrefix = $this->tproject_mgr->getTestCasePrefix($tproject_id);
    -  }
    -
    -
    -  /*
    -    function: getPrefix
    -
    -    args: id: testcase id
    -          [$tproject_id]
    -
    -    returns: array(prefix,testproject id)
    -
    -  */
    -  function getPrefix($id, $tproject_id=null)
    -  {
    -    $root = $tproject_id;
    -    if( is_null($root) )
    -    {
    -      $path2root=$this->tree_manager->get_path($id);
    -      $root=$path2root[0]['parent_id'];
    -    }
    -    $tcasePrefix = $this->tproject_mgr->getTestCasePrefix($root);
    -    return array($tcasePrefix,$root);
    -  }
    -
    -
    -
    -
    -
    -  /*
    -    @internal revisions
    -  */
    -  function copy_to($id,$parent_id,$user_id,$options=null,$mappings=null) 
    -  {
    -    $newTCObj = array('id' => -1, 'status_ok' => 0, 
    -                      'msg' => 'ok', 'mappings' => null);
    -    $my['options'] = array('check_duplicate_name' => 
    -                             self::DONT_CHECK_DUPLICATE_NAME,
    -                           'action_on_duplicate_name' => 'generate_new',
    -                           'use_this_name' => null,
    -                           'copy_also' => null, 
    -                           'preserve_external_id' => false,
    -                           'renderGhostSteps' => false, 
    -                           'stepAsGhost' => false,
    -                           'copyOnlyLatest' => false);
    -
    -    // needed when Test Case is copied to a DIFFERENT Test Project,
    -    // added during Test Project COPY Feature implementation
    -    $my['mappings']['keywords'] = null;
    -    $my['mappings']['requirements'] = null;
    -
    -    $my['mappings'] = array_merge($my['mappings'], (array)$mappings);
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -
    -    if( is_null($my['options']['copy_also']) ) {
    -      $my['options']['copy_also'] = array('keyword_assignments' => true,'requirement_assignments' => true);
    -    }
    -
    -    $copyKW = ( isset($my['options']['copy_also']['keyword_assignments']) &&
    -                $my['options']['copy_also']['keyword_assignments'] );
    -
    -    $copyPL = ( isset($my['options']['copy_also']['platform_assignments']) &&
    -                $my['options']['copy_also']['platform_assignments'] );
    -
    -    $uglyKey = 'requirement_assignments';
    -    $copyReqLinks = ( isset($my['options']['copy_also'][$uglyKey]) &&
    -                      $my['options']['copy_also'][$uglyKey]);
    -    // ==================================================================
    -
    -    $useLatest = $my['options']['stepAsGhost'] 
    -                 || $my['options']['copyOnlyLatest'];
    -
    -    $tcVersionID = $useLatest ? self::LATEST_VERSION : self::ALL_VERSIONS;
    -    $tcase_info = $this->get_by_id($id,$tcVersionID);
    -    if ($tcase_info) {
    -      $callme = !is_null($my['options']['use_this_name']) ? 
    -                $my['options']['use_this_name'] : $tcase_info[0]['name'];
    -      $callme = $this->trim_and_limit($callme);
    -
    -      $newTCObj = $this->create_tcase_only($parent_id,
    -                    $callme,$tcase_info[0]['node_order'],
    -                    self::AUTOMATIC_ID,$my['options']);
    -
    -      $ix = new stdClass();
    -      $ix->authorID = $user_id;
    -      $ix->status = null;
    -      $ix->steps = null;
    -
    -      if($newTCObj['status_ok']) {
    -
    -        $ret['status_ok']=1;
    -        $newTCObj['mappings'][$id] = $newTCObj['id'];
    -
    -        $ix->id = $newTCObj['id'];
    -        $ix->externalID = $newTCObj['external_id'];
    -        if( $my['options']['preserve_external_id'] ) {
    -          $ix->externalID = $tcase_info[0]['tc_external_id'];
    -        }
    -
    -        foreach($tcase_info as $tcversion) {
    -
    -          // IMPORTANT NOTICE:
    -          // In order to implement COPY to another test project, WE CAN NOT ASK
    -          // to method create_tcversion() to create inside itself THE STEPS.
    -          // Passing NULL as steps we instruct create_tcversion() TO DO NOT CREATE STEPS
    -
    -          $ix->executionType = $tcversion['execution_type'];
    -          $ix->importance = $tcversion['importance'];
    -          
    -          $ix->version = $tcversion['version'];
    -          if ($my['options']['copyOnlyLatest']) {
    -            $ix->version = 1;
    -          }
    -
    -          $ix->status = $tcversion['status'];
    -          $ix->estimatedExecDuration = $tcversion['estimated_exec_duration'];
    -          $ix->is_open = $tcversion['is_open'];
    -
    -          // Further processing will be needed to manage inline 
    -          // image attachments
    -          // updateSimpleFields() will be used.
    -          $ix->summary = $tcversion['summary'];
    -          $ix->preconditions = $tcversion['preconditions'];
    -
    -          $opCV = $this->createVersion($ix);
    -          if( $opCV['status_ok'] ) {
    -              $alienTCV = $newTCObj['mappings'][$tcversion['id']] = $opCV['id'];
    -
    -              $inlineImg = null;
    -              $attNewRef = $this->copy_attachments($tcversion['id'],$alienTCV);      
    -              if(!is_null($attNewRef)) {
    -                // get all attachments, then check is there are images
    -                $att = $this->attachmentRepository->getAttachmentInfosFor(
    -                         $alienTCV,$this->attachmentTableName,'id');
    -                foreach($attNewRef as $oid => $nid) {
    -                  if($att[$nid]['is_image']) {
    -                    $needle = str_replace($nid,$oid,$att[$nid]['inlineString']);
    -                    $inlineImg[] = 
    -                      array('needle' => $needle, 'rep' => $att[$nid]['inlineString']);
    -                  }
    -                }
    -              }
    -
    -              $doInline = !is_null($inlineImg);
    -              if($doInline) {
    -                foreach($inlineImg as $elem) {
    -                  $ix->summary = str_replace($elem['needle'],$elem['rep'],
    -                                             $ix->summary);
    -                  $ix->preconditions = str_replace($elem['needle'],$elem['rep'],
    -                                                   $ix->preconditions);
    -                }
    -                // updateSimpleFields() will be used.
    -                $usf = array('summary' => $ix->summary,
    -                             'preconditions' => $ix->preconditions);
    -
    -                $this->updateSimpleFields($alienTCV,$usf);
    -              }        
    -
    -              // ATTENTION:  NEED TO UNDERSTAND HOW TO MANAGE COPY TO OTHER TEST PROJECTS
    -              $this->copy_cfields_design_values(
    -                array('id' => $id, 'tcversion_id' => $tcversion['id']),
    -                array('id' => $newTCObj['id'], 'tcversion_id' => $opCV['id']));
    -
    -              // Need to get all steps
    -              $stepsSet = $this->get_steps($tcversion['id'],0,$my['options']);
    -
    -              $to_tcversion_id = $opCV['id'];
    -              if( !is_null($stepsSet) ) {
    -
    -                // not elegant but ...
    -                if($my['options']['stepAsGhost']) {
    -                  $pfx = $this->getPrefix($id);
    -                  $pfx = $pfx[0] . $this->cfg->testcase->glue_character . $tcversion['tc_external_id'];
    -
    -                  foreach($stepsSet as $key => $step) {
    -                    $act = sprintf(self::GHOSTSTEPMASK,$step['step_number'],
    -                                   $pfx,$tcversion['version']); 
    -
    -                    $this->create_step($to_tcversion_id,
    -                                       $step['step_number'],$act,$act,
    -                                       $step['execution_type']);
    -                  }
    -                } else {
    -                  foreach($stepsSet as $key => $step) {
    -                    $this->create_step($to_tcversion_id,
    -                                       $step['step_number'],
    -                                       $step['actions'],
    -                                       $step['expected_results'],
    -                                       $step['execution_type']);
    -                  }
    -                }
    -              }
    -          }
    -
    -          // Conditional copies
    -          if( $opCV['status_ok'] ) {
    -            $source = array('id' => $id, 'version_id' => $tcversion['id']);
    -            $dest = array('id' => $newTCObj['id'], 'version_id' => $opCV['id'] ,
    -                          'version' => $tcversion['version']);
    -          }
    -
    -          if( $opCV['status_ok'] && $copyKW ) {
    -            $this->copyKeywordsTo($source,$dest,$my['mappings']['keywords']);
    -          }
    -
    -          if( $opCV['status_ok'] && $copyPL ) {
    -            $this->copyPlatformsTo($source,$dest,$my['mappings']['platforms']);
    -          }
    -
    -          if( $opCV['status_ok'] && $copyReqLinks ) {
    -            $this->copyReqVersionLinksTo($source,$dest,
    -              $my['mappings']['requirements'],$ix->authorID);
    -          }
    -        }  // foreach($tcase_info ...
    -      } // $newTCObj['status_ok']
    -    }
    -
    -    return $newTCObj ;
    -  }
    -
    -
    -  /*
    -    function: create_new_version()
    -              create a new test case version,
    -              doing a copy of source test case version
    -
    -
    -    args : $id: testcase id
    -           $user_id: who is doing this operation.
    -           [$source_version_id]: default null -> source is LATEST TCVERSION
    -
    -    returns:
    -            map:  id: node id of created tcversion
    -                  version: version number (i.e. 5)
    -                  msg
    -
    -  */
    -  function create_new_version($id,$user_id,$source_version_id=null, $options=null) {
    -
    -    // Before working on requirements it will be useful
    -    // to understand if req management is enabled
    -    // for the Test Project
    -    //
    -    $freezeLinkOnNewTCVersion = false;
    -    $freezeLinkedRequirements = false;
    -    $freezeTCVRelationsOnNewTCVersion =false;
    -    $reqTCLinksCfg = config_get('reqTCLinks'); 
    -
    -    if( $this->tproject_id > 0 ) {
    -
    -      $po = $this->tproject_mgr->getOptions($this->tproject_id);
    -      if($po->requirementsEnabled) {
    -        $freezeLinkOnNewTCVersion = $reqTCLinksCfg->freezeLinkOnNewTCVersion;
    -        $freezeLinkedRequirements = $freezeLinkOnNewTCVersion && 
    -          $reqTCLinksCfg->freezeBothEndsOnNewTCVersion;
    -
    -        $freezeTCVRelationsOnNewTCVersion = 
    -          $this->cfg->testcase->freezeTCVRelationsOnNewTCVersion;
    -      }
    -    }
    -    
    -    $now = $this->db->db_now();
    -    $opt = array('is_open' => 1, 
    -                 'freezeLinkedRequirements' => $freezeLinkedRequirements,
    -                 'freezeLinkOnNewTCVersion' => $freezeLinkOnNewTCVersion,
    -                 'freezeTCVRelationsOnNewTCVersion' =>
    -                   $freezeTCVRelationsOnNewTCVersion);
    -
    -    $opt = array_merge($opt,(array)$options);
    -
    -    $tcversion_id = $this->tree_manager->new_node($id,$this->node_types_descr_id['testcase_version']);
    -
    -    // get last version for this test case (need to get new version number)
    -    $last_version_info =  $this->get_last_version_info($id, array('output' => 'minimun'));
    -
    -    $from = $source_version_id;
    -    if( is_null($source_version_id) || $source_version_id <= 0) {
    -      $from = $last_version_info['id'];
    -    }
    -    $this->copy_tcversion($id,$from,$tcversion_id,$last_version_info['version']+1,$user_id);
    -
    -    $ret['id'] = $tcversion_id;
    -    $ret['version'] = $last_version_info['version']+1;
    -    $ret['msg'] = 'ok';
    -
    -    $this->setIsOpen(null,$tcversion_id,$opt['is_open']);
    -
    -    // Keywords managed @version level.
    -    $source = array('id' => $id, 'version_id' => $from);
    -    $dest = array('id' => $id, 'version_id' => $tcversion_id);
    -    $auditContext = array('on' => self::AUDIT_OFF);
    -    
    -    $this->copyKeywordsTo($source,$dest,null,$auditContext,array('delete' => false));
    -    $this->copy_attachments($source['version_id'],$dest['version_id']);
    -    $this->copyTCVRelations($source['version_id'],$dest['version_id']);
    -
    -    $this->copyPlatformsTo($source,$dest,null,$auditContext,array('delete' => false));
    -
    -
    -    if( $this->cfg->testcase->relations->enable && 
    -        $freezeTCVRelationsOnNewTCVersion ) {
    -      $oldVerRel = $this->getTCVRelationsRaw($source['version_id']);
    -      if( null != $oldVerRel && count($oldVerRel) > 0 ) {
    -        $i2c = array_keys($oldVerRel);
    -        $this->closeOpenTCVRelation($i2c,LINK_TC_RELATION_CLOSED_BY_NEW_TCVERSION);
    -      }
    -    }
    -
    -
    -    if( $opt['freezeLinkedRequirements'] ) {
    -       $this->closeOpenReqVersionOnOpenLinks($source['version_id']);
    -    }
    -
    -    $signature = array('user_id' => $user_id, 'when' => $now);
    -    $link = array('source' => $source['version_id'],
    -                  'dest' => $dest['version_id']);
    -    $optUC = array('freezePrevious' => $opt['freezeLinkOnNewTCVersion']);
    -    $this->updateCoverage($link,$signature,$optUC);
    -
    -
    -    $ret['id'] = $tcversion_id;
    -    $ret['version'] = $last_version_info['version']+1;
    -    $ret['msg'] = 'ok';
    -    
    -    return $ret;
    -  }
    -
    -
    -
    -  /*
    -    function: get_last_version_info
    -              Get information about last version (greater number) of a testcase.
    -
    -    args : id: testcase id
    -           [options]
    -
    -    returns: map with keys  that depends of options['output']:
    -
    -             id -> tcversion_id
    -             version
    -             summary
    -             importance
    -             author_id
    -             creation_ts
    -             updater_id
    -             modification_ts
    -             active
    -             is_open
    -    @internal revisions
    -    @since 1.9.9
    -    'active' => values 1,0, null => do not apply filter
    -  */
    -  function get_last_version_info($id,$options=null) {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -    $my['options'] = array( 'get_steps' => false, 'output' => 'full','active' => null);
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -    $tcInfo = null;
    -    switch($my['options']['output']) {
    -
    -      case 'thin':
    -        $fields2get = " TCV.id AS tcversion_id";
    -      break;
    -
    -      case 'minimun':
    -        $fields2get = " TCV.id, TCV.id AS tcversion_id, TCV.version, TCV.tc_external_id,NH_TC.name ";
    -      break;
    -
    -      case 'full':
    -      default:
    -        $fields2get = " TCV.*,TCV.id AS tcversion_id, NH_TC.name ";
    -      break;
    -    }
    -
    -
    -    $sql = "/* $debugMsg */ SELECT MAX(version) AS version " .
    -           " FROM {$this->tables['tcversions']} TCV " .
    -           " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TCV.id ".
    -           " WHERE NH_TCV.parent_id = {$id} ";
    -
    -    if( !is_null($my['options']['active']) ) {
    -      $sql .= " AND TCV.active=" . (intval($my['options']['active']) > 0 ? 1 : 0);
    -    }
    -
    -    $max_version = $this->db->fetchFirstRowSingleColumn($sql,'version');
    -
    -    $tcInfo = null;
    -    if ($max_version) {
    -      $sql = " SELECT {$fields2get} FROM {$this->tables['tcversions']} TCV " .
    -             " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TCV.id ".
    -             " JOIN {$this->tables['nodes_hierarchy']} NH_TC ON NH_TC.id = NH_TCV.parent_id ".
    -             " WHERE TCV.version = {$max_version} ".
    -             " AND NH_TCV.parent_id = {$id}";
    -
    -      $tcInfo = $this->db->fetchFirstRow($sql);
    -    }
    -
    -    // Multiple Test Case Steps Feature
    -    if( !is_null($tcInfo) && $my['options']['get_steps'] ) {
    -      $step_set = $this->get_steps($tcInfo['id']);
    -      $tcInfo['steps'] = $step_set;
    -    }
    -
    -    return $tcInfo;
    -  }
    -
    -
    -  /*
    -    function: copy_tcversion
    -
    -    args:
    -
    -    returns:
    -
    -    rev:
    -
    -  */
    -  function copy_tcversion($id,$from_tcversion_id,$to_tcversion_id,$as_version_number,$user_id)
    -  {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -    $now = $this->db->db_now();
    -    $sql = "/* $debugMsg */ " .
    -           " INSERT INTO {$this->tables['tcversions']} " .
    -           " (id,version,tc_external_id,author_id,creation_ts,summary, " .
    -           "  importance,execution_type,preconditions,estimated_exec_duration) " .
    -           " SELECT {$to_tcversion_id} AS id, {$as_version_number} AS version, " .
    -           "        tc_external_id, " .
    -           "        {$user_id} AS author_id, {$now} AS creation_ts," .
    -           "        summary,importance,execution_type, preconditions,estimated_exec_duration " .
    -           " FROM {$this->tables['tcversions']} " .
    -           " WHERE id={$from_tcversion_id} ";
    -    $result = $this->db->exec_query($sql);
    -
    -    // copy custom fields values JUST DESIGN AREA
    -    $this->copy_cfields_design_values(array('id' => $id, 'tcversion_id' => $from_tcversion_id),
    -                                      array('id' => $id, 'tcversion_id' => $to_tcversion_id));
    -
    -
    -    // Need to get all steps
    -    $gso = array('renderGhostSteps' => false, 'renderImageInline' => false);
    -    $stepsSet = $this->get_steps($from_tcversion_id,0,$gso);
    -    if( !is_null($stepsSet) && count($stepsSet) > 0) {
    -      foreach($stepsSet as $key => $step) {
    -        $op = $this->create_step($to_tcversion_id,$step['step_number'],
    -                                 $step['actions'],$step['expected_results'],
    -                                 $step['execution_type']);
    -      }
    -    }
    -  }
    -
    -
    -  /*
    -    function: get_by_id_bulk
    -
    -              IMPORTANT CONSIDERATION:
    -              how may elements can be used in an SQL IN CLAUSE?
    -              Think there is a limit ( on MSSQL 1000 ?)
    -
    -    args :
    -
    -    returns:
    -
    -  */
    -  function get_by_id_bulk($id,$version_id=self::ALL_VERSIONS, $get_active=0, $get_open=0)
    -  {
    -    $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
    -    $where_clause="";
    -    $where_clause_names="";
    -    $tcid_list ="";
    -    $tcversion_id_filter="";
    -    $sql = "";
    -    $the_names = null;
    -    if( is_array($id) ) {
    -      $tcid_list = implode(",",$id);
    -      $where_clause = " WHERE nodes_hierarchy.parent_id IN ($tcid_list) ";
    -      $where_clause_names = " WHERE nodes_hierarchy.id IN ($tcid_list) ";
    -    }
    -    else
    -    {
    -      $where_clause = " WHERE nodes_hierarchy.parent_id = {$id} ";
    -      $where_clause_names = " WHERE nodes_hierarchy.id = {$id} ";
    -    }
    -      if( $version_id != self::ALL_VERSIONS )
    -      {
    -          $tcversion_id_filter=" AND tcversions.id IN (" . implode(",",(array)$version_id) . ") ";
    -      }
    -
    -    $sql = " /* $debugMsg */ SELECT nodes_hierarchy.parent_id AS testcase_id, ".
    -           " tcversions.*, users.first AS author_first_name, users.last AS author_last_name, " .
    -           " '' AS updater_first_name, '' AS updater_last_name " .
    -           " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy " .
    -           " JOIN {$this->tables['tcversions']} tcversions ON nodes_hierarchy.id = tcversions.id " .
    -             " LEFT OUTER JOIN {$this->tables['users']} users ON tcversions.author_id = users.id " .
    -             " {$where_clause} {$tcversion_id_filter} ORDER BY tcversions.version DESC";
    -    $recordset = $this->db->get_recordset($sql);
    -
    -    if($recordset)
    -    {
    -       // get the names
    -     $sql = " /* $debugMsg */ " .
    -            " SELECT nodes_hierarchy.id AS testcase_id, nodes_hierarchy.name " .
    -            " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy {$where_clause_names} ";
    -
    -     $the_names = $this->db->get_recordset($sql);
    -       if($the_names)
    -       {
    -          foreach ($recordset as  $the_key => $row )
    -          {
    -              reset($the_names);
    -              foreach($the_names as $row_n)
    -              {
    -                  if( $row['testcase_id'] == $row_n['testcase_id'])
    -                  {
    -                    $recordset[$the_key]['name']= $row_n['name'];
    -                    break;
    -                  }
    -              }
    -          }
    -       }
    -
    -
    -     $sql = " /* $debugMsg */ " .
    -            " SELECT updater_id, users.first AS updater_first_name, users.last  AS updater_last_name " .
    -            " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy " .
    -            " JOIN {$this->tables['tcversions']} tcversions ON nodes_hierarchy.id = tcversions.id " .
    -              " LEFT OUTER JOIN {$this->tables['users']} users ON tcversions.updater_id = users.id " .
    -              " {$where_clause} and tcversions.updater_id IS NOT NULL ";
    -
    -      $updaters = $this->db->get_recordset($sql);
    -
    -      if($updaters)
    -      {
    -        reset($recordset);
    -        foreach ($recordset as  $the_key => $row )
    -        {
    -          if ( !is_null($row['updater_id']) )
    -          {
    -            foreach ($updaters as $row_upd)
    -            {
    -              if ( $row['updater_id'] == $row_upd['updater_id'] )
    -              {
    -                $recordset[$the_key]['updater_last_name'] = $row_upd['updater_last_name'];
    -                $recordset[$the_key]['updater_first_name'] = $row_upd['updater_first_name'];
    -                break;
    -              }
    -            }
    -          }
    -        }
    -      }
    -
    -    }
    -
    -
    -    return($recordset ? $recordset : null);
    -  }
    -
    -
    -
    -
    -  /*
    -    function: get_by_id
    -
    -    args : id: can be a single testcase id or an array od testcase id.
    -
    -           [version_id]: default self::ALL_VERSIONS => all versions
    -                         can be an array.
    -                         Useful to retrieve only a subset of versions.
    -                         null => means use version_number argument
    -
    -       [filters]:
    -                [active_status]: default 'ALL', range: 'ALL','ACTIVE','INACTIVE'
    -                                 has effect for the following version_id values:
    -                                 self::ALL_VERSIONS,TC_LAST_VERSION, version_id is NOT an array
    -
    -                [open_status]: default 'ALL'
    -                               currently not used.
    -
    -                [version_number]: default 1, version number displayed at User Interface
    -
    -       [options]:
    -                [output]: default 'full'
    -          domain 'full','essential','full_without_steps','full_without_users'
    -
    -    returns: array
    -
    -  */
    -  function get_by_id($id,$version_id = self::ALL_VERSIONS, $filters = null, $options=null) {
    -    $my['filters'] = array('active_status' => 'ALL', 'open_status' => 'ALL', 'version_number' => 1);
    -    $my['filters'] = array_merge($my['filters'], (array)$filters);
    -
    -    $my['options'] = array('output' => 'full', 'access_key' => 'tcversion_id', 
    -                           'getPrefix' => false,
    -                           'order_by' => null, 'renderGhost' => false, 
    -                           'withGhostString' => false,
    -                           'renderImageInline' => false, 
    -                           'renderVariables' => false,
    -                           'renderSpecialKW' => false);
    -
    -    $my['options'] = array_merge($my['options'], (array)$options);
    -
    -    $tcid_list = null;
    -    $where_clause = '';
    -    $active_filter = '';
    -    $versionSQLOp = ' AND ';
    -
    -    if( ($accessByVersionID = is_null($id) && !is_null($version_id)) ) {
    -      $versionSQLOp = ' WHERE ';
    -    }
    -    else if(is_array($id)) {
    -      $tcid_list = implode(",",$id);
    -      $where_clause = " WHERE NHTCV.parent_id IN ({$tcid_list}) ";
    -    }
    -    else {
    -      $where_clause = " WHERE NHTCV.parent_id = {$id} ";
    -    }
    -
    -    if( ($version_id_is_array=is_array($version_id)) ) {
    -        $versionid_list = implode(",",$version_id);
    -        $where_clause .= $versionSQLOp . " TCV.id IN ({$versionid_list}) ";
    -    }
    -    else {
    -      if( is_null($version_id) ) {
    -          // when tcase ID has not been provided this can not be used
    -          // will not do any check => leave it CRASH
    -            $where_clause .= " AND TCV.version = {$my['filters']['version_number']} ";
    -      }
    -      else {
    -        if($version_id != self::ALL_VERSIONS && $version_id != self::LATEST_VERSION) {
    -          $where_clause .= $versionSQLOp .  " TCV.id = {$version_id} ";
    -        }
    -      }
    -
    -      $active_status = strtoupper($my['filters']['active_status']);
    -      if($active_status != 'ALL') {
    -        $active_filter =' AND TCV.active=' . ($active_status=='ACTIVE' ? 1 : 0) . ' ';
    -      }
    -    }
    -
    -    switch($my['options']['output']) {
    -      case 'full':
    -      case 'full_without_steps':
    -        $sql = "SELECT UA.login AS updater_login,UB.login AS author_login,
    +                WHERE tcversion_id IN ({$tcversion_list}) ";
    +
    +            $sql[] = "/* $debugMsg */ DELETE FROM {$this->tables['executions']}  " .
    +                " WHERE tcversion_id IN ({$tcversion_list})";
    +
    +            foreach ($sql as $the_stm) {
    +                $this->db->exec_query($the_stm);
    +            }
    +        } else {
    +
    +            // Long explanation
    +            // executions table has following fields
    +            // tcversion_id
    +            // tcversion_number
    +            //
    +            // 1) why?
    +            // 2) how are used?
    +            //
    +            // Detailed original analisys is not available anymore, but:
    +            // probably the right thing to do is to use here the field
    +            // testplan_tcversions.id, because we can have ONLY ONE tcversion
    +            // linked to a testplan.
    +            // What to do when a new tcversion is created and LINKED to a testplan ?
    +            // How to get information about all executions in every tcversion ?
    +            //
    +            // The method used is explained with this example:
    +            // 1. create testcase TC1
    +            // 2. tcversion with number 1 will be created (internal ID 77755)
    +            // 3. add to testplan + platform
    +            // 4. execute on build X
    +            // 5. executions table -> exec_id=9543, tcversion_id=77755, tcversion_number=1
    +            // 6. create new version for TC1 -> numer=2 , internal ID 78888
    +            // 7. update link for TC1 version in testplan to version 2
    +            // this generates this effect in executions table:
    +            // exec_id=9787, tcversion_id=78888, tcversion_number=1
    +            // 8. execute on build X
    +            // 9. in executions table ->
    +            // exec_id=9543, tcversion_id=78888, tcversion_number=1
    +            // exec_id=9787, tcversion_id=78888, tcversion_number=2
    +            //
    +            //
    +            // Then after user report on forum.testlink.org on 20210810
    +            // this logic need to be changed.
    +            //
    +            // - get the tcversion_number (VNUM) for the tcversion_id (TARGET_TCVID)
    +            // - analize executions table to understand if we have executions
    +            // for other versions inspecting the tcversion_number field
    +            // NO:
    +            // no more checks are needed.
    +            //
    +            // YES:
    +            // we need to delete ONLY the records with:
    +            // tcversion_number = VNUM && tcversion_id = TARGET_TCVID
    +            //
    +            // - get the tcversion_number (VNUM) for the tcversion_id (TARGET_TCVID)
    +            $myVersionNum = $this->getVersionNumber($version_id);
    +
    +            if ($step_list != '') {
    +
    +                $sql[] = "/* $debugMsg */
    +                  DELETE FROM {$this->tables['execution_tcsteps_wip']}
    +                  WHERE tcstep_id IN ({$step_list}) ";
    +            }
    +
    +            $execSQL = " SELECT id FROM {$this->tables['executions']}
    +                   WHERE tcversion_id = {$version_id}
    +                   AND tcversion_number = {$myVersionNum} ";
    +
    +            $sql[] = "/* $debugMsg */
    +                DELETE FROM {$this->tables['execution_tcsteps']}
    +                WHERE execution_id IN ($execSQL)";
    +
    +            $sql[] = "/* $debugMsg */
    +                DELETE FROM {$this->tables['execution_bugs']}
    +                WHERE execution_id IN ($execSQL)";
    +
    +            $sql[] = "/* $debugMsg */
    +                DELETE FROM {$this->tables['cfield_execution_values']}
    +                WHERE execution_id IN ($execSQL)";
    +
    +            $sql[] = "/* $debugMsg */
    +                   DELETE FROM {$this->tables['executions']}
    +                   WHERE tcversion_id = {$version_id}
    +                   AND tcversion_number = {$myVersionNum} ";
    +
    +            foreach ($sql as $the_stm) {
    +                $this->db->exec_query($the_stm);
    +            }
    +
    +            $sqlCheckExec = "/* $debugMsg */
    +                       SELECT tcversion_number, tcversion_id
    +                       FROM {$this->tables['executions']}
    +                       WHERE tcversion_id = {$version_id}
    +                       AND tcversion_number <> {$myVersionNum}";
    +            $rs = (array) $this->db->get_recordset($sqlCheckExec);
    +
    +            if (count($rs) != 0) {
    +                // Get latest execution to get the version number and then tcversion_id
    +                // to update the testplan_tcversions.
    +                // We need to get version number for EACH TEST PLAN!!
    +
    +                // If platforms exists on testplan, anyway same testcase version
    +                // MUST BE used in each platform.
    +                $sqlLE = "/* $debugMsg */
    +                  SELECT latest_exec FROM (
    +                    SELECT MAX(id) AS latest_exec,testplan_id
    +                    FROM {$this->tables['executions']}
    +                    WHERE tcversion_id = {$version_id}
    +                    AND tcversion_number <> {$myVersionNum}
    +                    GROUP BY testplan_id
    +                  ) SQLLE ";
    +
    +                $sqlExecForUpd = "/* $debugMsg */
    +                         SELECT id AS execution_id,testplan_id,tcversion_id,tcversion_number
    +                         FROM {$this->tables['executions']}
    +                         WHERE id IN ($sqlLE) ";
    +                $rs = (array) $this->db->get_recordset($sqlExecForUpd);
    +
    +                //
    +                $execContext = new stdClass();
    +                $execContext->target = new stdClass();
    +                $execContext->update = new stdClass();
    +                foreach ($rs as $elem) {
    +                    // - update executions
    +                    $nvrs = $this->get_basic_info($id,
    +                        array(
    +                            'number' => $elem['tcversion_number']
    +                        ));
    +                    $execContext->update->tcversionID = $nvrs[0]['tcversion_id'];
    +                    $execContext->target->tcversionID = $elem['tcversion_id'];
    +                    $execContext->target->tplanID = $elem['testplan_id'];
    +                    $this->updateTPlanLinkTCV($execContext);
    +                }
    +            }
    +        }
    +    }
    +
    +    /*
    +     * function: formatTestCaseIdentity
    +     *
    +     * args: id: testcase id
    +     * external_id
    +     *
    +     * returns: testproject id
    +     *
    +     */
    +    private function formatTestCaseIdentity($tc_id)
    +    {
    +        $path2root = $this->tree_manager->get_path($tc_id);
    +        $tproject_id = $path2root[0]['parent_id'];
    +        $this->tproject_mgr->getTestCasePrefix($tproject_id);
    +    }
    +
    +    /*
    +     * function: getPrefix
    +     *
    +     * args: id: testcase id
    +     * [$tproject_id]
    +     *
    +     * returns: array(prefix,testproject id)
    +     *
    +     */
    +    public function getPrefix($id, $tproject_id = null)
    +    {
    +        $root = $tproject_id;
    +        if (is_null($root)) {
    +            $path2root = $this->tree_manager->get_path($id);
    +            $root = $path2root[0]['parent_id'];
    +        }
    +        $tcasePrefix = $this->tproject_mgr->getTestCasePrefix($root);
    +        return array(
    +            $tcasePrefix,
    +            $root
    +        );
    +    }
    +
    +    /*
    +     * @internal revisions
    +     */
    +    public function copy_to($id, $parent_id, $user_id, $options = null,
    +        $mappings = null)
    +    {
    +        $newTCObj = array(
    +            'id' => - 1,
    +            'status_ok' => 0,
    +            'msg' => 'ok',
    +            'mappings' => null
    +        );
    +        $my['options'] = array(
    +            'check_duplicate_name' => self::DONT_CHECK_DUPLICATE_NAME,
    +            'action_on_duplicate_name' => 'generate_new',
    +            'use_this_name' => null,
    +            'copy_also' => null,
    +            'preserve_external_id' => false,
    +            'renderGhostSteps' => false,
    +            'stepAsGhost' => false,
    +            'copyOnlyLatest' => false
    +        );
    +
    +        // needed when Test Case is copied to a DIFFERENT Test Project,
    +        // added during Test Project COPY Feature implementation
    +        $my['mappings']['keywords'] = null;
    +        $my['mappings']['requirements'] = null;
    +
    +        $my['mappings'] = array_merge($my['mappings'], (array) $mappings);
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        if (is_null($my['options']['copy_also'])) {
    +            $my['options']['copy_also'] = array(
    +                'keyword_assignments' => true,
    +                'requirement_assignments' => true
    +            );
    +        }
    +
    +        $copyKW = (isset($my['options']['copy_also']['keyword_assignments']) &&
    +            $my['options']['copy_also']['keyword_assignments']);
    +
    +        $copyPL = (isset($my['options']['copy_also']['platform_assignments']) &&
    +            $my['options']['copy_also']['platform_assignments']);
    +
    +        $uglyKey = 'requirement_assignments';
    +        $copyReqLinks = (isset($my['options']['copy_also'][$uglyKey]) &&
    +            $my['options']['copy_also'][$uglyKey]);
    +
    +        $useLatest = $my['options']['stepAsGhost'] ||
    +            $my['options']['copyOnlyLatest'];
    +
    +        $tcVersionID = $useLatest ? self::LATEST_VERSION : self::ALL_VERSIONS;
    +        $tcase_info = $this->get_by_id($id, $tcVersionID);
    +        if ($tcase_info) {
    +            $callme = ! is_null($my['options']['use_this_name']) ? $my['options']['use_this_name'] : $tcase_info[0]['name'];
    +            $callme = $this->trimAndLimit($callme);
    +
    +            $newTCObj = $this->create_tcase_only($parent_id, $callme,
    +                $tcase_info[0]['node_order'], self::AUTOMATIC_ID, $my['options']);
    +
    +            $ix = new stdClass();
    +            $ix->authorID = $user_id;
    +            $ix->status = null;
    +            $ix->steps = null;
    +
    +            if ($newTCObj['status_ok']) {
    +
    +                $ret['status_ok'] = 1;
    +                $newTCObj['mappings'][$id] = $newTCObj['id'];
    +
    +                $ix->id = $newTCObj['id'];
    +                $ix->externalID = $newTCObj['external_id'];
    +                if ($my['options']['preserve_external_id']) {
    +                    $ix->externalID = $tcase_info[0]['tc_external_id'];
    +                }
    +
    +                foreach ($tcase_info as $tcversion) {
    +
    +                    // IMPORTANT NOTICE:
    +                    // In order to implement COPY to another test project, WE CAN NOT ASK
    +                    // to method create_tcversion() to create inside itself THE STEPS.
    +                    // Passing NULL as steps we instruct create_tcversion() TO DO NOT CREATE STEPS
    +
    +                    $ix->executionType = $tcversion['execution_type'];
    +                    $ix->importance = $tcversion['importance'];
    +
    +                    $ix->version = $tcversion['version'];
    +                    if ($my['options']['copyOnlyLatest']) {
    +                        $ix->version = 1;
    +                    }
    +
    +                    $ix->status = $tcversion['status'];
    +                    $ix->estimatedExecDuration = $tcversion['estimated_exec_duration'];
    +                    $ix->is_open = $tcversion['is_open'];
    +
    +                    // Further processing will be needed to manage inline
    +                    // image attachments
    +                    // updateSimpleFields() will be used.
    +                    $ix->summary = $tcversion['summary'];
    +                    $ix->preconditions = $tcversion['preconditions'];
    +
    +                    $opCV = $this->createVersion($ix);
    +                    if ($opCV['status_ok']) {
    +                        $alienTCV = $newTCObj['mappings'][$tcversion['id']] = $opCV['id'];
    +
    +                        $inlineImg = null;
    +                        $attNewRef = $this->copyAttachments($tcversion['id'],
    +                            $alienTCV);
    +                        if (! is_null($attNewRef)) {
    +                            // get all attachments, then check is there are images
    +                            $att = $this->attachmentRepository->getAttachmentInfosFor(
    +                                $alienTCV, $this->attachmentTableName, 'id');
    +                            foreach ($attNewRef as $oid => $nid) {
    +                                if ($att[$nid]['is_image']) {
    +                                    $needle = str_replace($nid, $oid,
    +                                        $att[$nid]['inlineString']);
    +                                    $inlineImg[] = array(
    +                                        'needle' => $needle,
    +                                        'rep' => $att[$nid]['inlineString']
    +                                    );
    +                                }
    +                            }
    +                        }
    +
    +                        $doInline = ! is_null($inlineImg);
    +                        if ($doInline) {
    +                            foreach ($inlineImg as $elem) {
    +                                $ix->summary = str_replace($elem['needle'],
    +                                    $elem['rep'], $ix->summary);
    +                                $ix->preconditions = str_replace(
    +                                    $elem['needle'], $elem['rep'],
    +                                    $ix->preconditions);
    +                            }
    +                            // updateSimpleFields() will be used.
    +                            $usf = array(
    +                                'summary' => $ix->summary,
    +                                'preconditions' => $ix->preconditions
    +                            );
    +
    +                            $this->updateSimpleFields($alienTCV, $usf);
    +                        }
    +
    +                        // ATTENTION: NEED TO UNDERSTAND HOW TO MANAGE COPY TO OTHER TEST PROJECTS
    +                        $this->copyCfieldsDesignValues(
    +                            array(
    +                                'id' => $id,
    +                                'tcversion_id' => $tcversion['id']
    +                            ),
    +                            array(
    +                                'id' => $newTCObj['id'],
    +                                'tcversion_id' => $opCV['id']
    +                            ));
    +
    +                        // Need to get all steps
    +                        $steps_options = $my['options'];
    +                        // Add the option renderImageInline to keep Inline Images
    +                        $steps_options['renderImageInline'] = false;
    +                        $stepsSet = $this->get_steps($tcversion['id'], 0,
    +                            $steps_options);
    +
    +                        $to_tcversion_id = $opCV['id'];
    +                        if (! is_null($stepsSet)) {
    +
    +                            // not elegant but ...
    +                            if ($my['options']['stepAsGhost']) {
    +                                $pfx = $this->getPrefix($id);
    +                                $pfx = $pfx[0] .
    +                                    $this->cfg->testcase->glue_character .
    +                                    $tcversion['tc_external_id'];
    +
    +                                foreach ($stepsSet as $step) {
    +                                    $act = sprintf(self::GHOSTSTEPMASK,
    +                                        $step['step_number'], $pfx,
    +                                        $tcversion['version']);
    +
    +                                    $this->create_step($to_tcversion_id,
    +                                        $step['step_number'], $act, $act,
    +                                        $step['execution_type']);
    +                                }
    +                            } else {
    +                                foreach ($stepsSet as $step) {
    +                                    // update inline references
    +                                    if ($doInline) {
    +                                        foreach ($inlineImg as $elem) {
    +                                            $step['actions'] = str_replace(
    +                                                $elem['needle'], $elem['rep'],
    +                                                $step['actions']);
    +                                            $step['expected_results'] = str_replace(
    +                                                $elem['needle'], $elem['rep'],
    +                                                $step['expected_results']);
    +                                        }
    +                                    }
    +
    +                                    $this->create_step($to_tcversion_id,
    +                                        $step['step_number'], $step['actions'],
    +                                        $step['expected_results'],
    +                                        $step['execution_type']);
    +                                }
    +                            }
    +                        }
    +                    }
    +
    +                    // Conditional copies
    +                    if ($opCV['status_ok']) {
    +                        $source = array(
    +                            'id' => $id,
    +                            'version_id' => $tcversion['id']
    +                        );
    +                        $dest = array(
    +                            'id' => $newTCObj['id'],
    +                            'version_id' => $opCV['id'],
    +                            'version' => $tcversion['version']
    +                        );
    +                    }
    +
    +                    if ($opCV['status_ok'] && $copyKW) {
    +                        $this->copyKeywordsTo($source, $dest,
    +                            $my['mappings']['keywords']);
    +                    }
    +
    +                    if ($opCV['status_ok'] && $copyPL) {
    +                        $this->copyPlatformsTo($source, $dest,
    +                            $my['mappings']['platforms']);
    +                    }
    +
    +                    if ($opCV['status_ok'] && $copyReqLinks) {
    +                        $this->copyReqVersionLinksTo($source, $dest,
    +                            $my['mappings']['requirements'], $ix->authorID);
    +                    }
    +                } // foreach($tcase_info ...
    +            } // $newTCObj['status_ok']
    +        }
    +
    +        return $newTCObj;
    +    }
    +
    +    /*
    +     * function: create_new_version()
    +     * create a new test case version,
    +     * doing a copy of source test case version
    +     *
    +     *
    +     * args : $id: testcase id
    +     * $user_id: who is doing this operation.
    +     * [$source_version_id]: default null -> source is LATEST TCVERSION
    +     *
    +     * returns:
    +     * map: id: node id of created tcversion
    +     * version: version number (i.e. 5)
    +     * msg
    +     *
    +     */
    +    public function create_new_version($id, $user_id, $source_version_id = null,
    +        $options = null)
    +    {
    +
    +        // Before working on requirements it will be useful
    +        // to understand if req management is enabled
    +        // for the Test Project
    +        //
    +        $freezeLinkOnNewTCVersion = false;
    +        $freezeLinkedRequirements = false;
    +        $freezeTCVRelationsOnNewTCVersion = false;
    +        $reqTCLinksCfg = config_get('reqTCLinks');
    +
    +        if ($this->tproject_id > 0) {
    +
    +            $po = $this->tproject_mgr->getOptions($this->tproject_id);
    +            if ($po->requirementsEnabled) {
    +                $freezeLinkOnNewTCVersion = $reqTCLinksCfg->freezeLinkOnNewTCVersion;
    +                $freezeLinkedRequirements = $freezeLinkOnNewTCVersion &&
    +                    $reqTCLinksCfg->freezeBothEndsOnNewTCVersion;
    +
    +                $freezeTCVRelationsOnNewTCVersion = $this->cfg->testcase->freezeTCVRelationsOnNewTCVersion;
    +            }
    +        }
    +
    +        $now = $this->db->db_now();
    +        $opt = array(
    +            'is_open' => 1,
    +            'freezeLinkedRequirements' => $freezeLinkedRequirements,
    +            'freezeLinkOnNewTCVersion' => $freezeLinkOnNewTCVersion,
    +            'freezeTCVRelationsOnNewTCVersion' => $freezeTCVRelationsOnNewTCVersion
    +        );
    +
    +        $opt = array_merge($opt, (array) $options);
    +
    +        $tcversion_id = $this->tree_manager->new_node($id,
    +            $this->node_types_descr_id['testcase_version']);
    +
    +        // get last version for this test case (need to get new version number)
    +        $last_version_info = $this->getLastVersionInfo($id,
    +            array(
    +                'output' => 'minimun'
    +            ));
    +
    +        $from = $source_version_id;
    +        if (is_null($source_version_id) || $source_version_id <= 0) {
    +            $from = $last_version_info['id'];
    +        }
    +        $this->copyTestcaseVersion($id, $from, $tcversion_id,
    +            $last_version_info['version'] + 1, $user_id);
    +
    +        $this->setIsOpen(null, $tcversion_id, $opt['is_open']);
    +
    +        // Keywords managed @version level.
    +        $source = array(
    +            'id' => $id,
    +            'version_id' => $from
    +        );
    +        $dest = array(
    +            'id' => $id,
    +            'version_id' => $tcversion_id
    +        );
    +        $auditContext = array(
    +            'on' => self::AUDIT_OFF
    +        );
    +
    +        $this->copyKeywordsTo($source, $dest, null, $auditContext,
    +            array(
    +                'delete' => false
    +            ));
    +        $this->copyAttachments($source['version_id'], $dest['version_id']);
    +        $this->copyTCVRelations($source['version_id'], $dest['version_id']);
    +
    +        $this->copyPlatformsTo($source, $dest, null, $auditContext,
    +            array(
    +                'delete' => false
    +            ));
    +
    +        if ($this->cfg->testcase->relations->enable &&
    +            $freezeTCVRelationsOnNewTCVersion) {
    +            $oldVerRel = $this->getTCVRelationsRaw($source['version_id']);
    +            if (! empty($oldVerRel)) {
    +                $i2c = array_keys($oldVerRel);
    +                $this->closeOpenTCVRelation($i2c,
    +                    LINK_TC_RELATION_CLOSED_BY_NEW_TCVERSION);
    +            }
    +        }
    +
    +        if ($opt['freezeLinkedRequirements']) {
    +            $this->closeOpenReqVersionOnOpenLinks($source['version_id']);
    +        }
    +
    +        $signature = array(
    +            'user_id' => $user_id,
    +            'when' => $now
    +        );
    +        $link = array(
    +            'source' => $source['version_id'],
    +            'dest' => $dest['version_id']
    +        );
    +        $optUC = array(
    +            'freezePrevious' => $opt['freezeLinkOnNewTCVersion']
    +        );
    +        $this->updateCoverage($link, $signature, $optUC);
    +
    +        $ret['id'] = $tcversion_id;
    +        $ret['version'] = $last_version_info['version'] + 1;
    +        $ret['msg'] = 'ok';
    +
    +        return $ret;
    +    }
    +
    +    /*
    +     * function: getLastVersionInfo
    +     * Get information about last version (greater number) of a testcase.
    +     *
    +     * args : id: testcase id
    +     * [options]
    +     *
    +     * returns: map with keys that depends of options['output']:
    +     *
    +     * id -> tcversion_id
    +     * version
    +     * summary
    +     * importance
    +     * author_id
    +     * creation_ts
    +     * updater_id
    +     * modification_ts
    +     * active
    +     * is_open
    +     * @internal revisions
    +     * @since 1.9.9
    +     * 'active' => values 1,0, null => do not apply filter
    +     */
    +    public function getLastVersionInfo($id, $options = null)
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +        $my['options'] = array(
    +            'get_steps' => false,
    +            'output' => 'full',
    +            'active' => null
    +        );
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        $tcInfo = null;
    +        switch ($my['options']['output']) {
    +
    +            case 'thin':
    +                $fields2get = " TCV.id AS tcversion_id";
    +                break;
    +
    +            case 'minimun':
    +                $fields2get = " TCV.id, TCV.id AS tcversion_id, TCV.version, TCV.tc_external_id,NH_TC.name ";
    +                break;
    +
    +            case 'full':
    +            default:
    +                $fields2get = " TCV.*,TCV.id AS tcversion_id, NH_TC.name ";
    +                break;
    +        }
    +
    +        $sql = "/* $debugMsg */ SELECT MAX(version) AS version " .
    +            " FROM {$this->tables['tcversions']} TCV " .
    +            " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TCV.id " .
    +            " WHERE NH_TCV.parent_id = {$id} ";
    +
    +        if (! is_null($my['options']['active'])) {
    +            $sql .= " AND TCV.active=" .
    +                (intval($my['options']['active']) > 0 ? 1 : 0);
    +        }
    +
    +        $max_version = $this->db->fetchFirstRowSingleColumn($sql, 'version');
    +
    +        $tcInfo = null;
    +        if ($max_version) {
    +            $sql = " SELECT {$fields2get} FROM {$this->tables['tcversions']} TCV " .
    +                " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TCV.id " .
    +                " JOIN {$this->tables['nodes_hierarchy']} NH_TC ON NH_TC.id = NH_TCV.parent_id " .
    +                " WHERE TCV.version = {$max_version} " .
    +                " AND NH_TCV.parent_id = {$id}";
    +
    +            $tcInfo = $this->db->fetchFirstRow($sql);
    +        }
    +
    +        // Multiple Test Case Steps Feature
    +        if (! is_null($tcInfo) && $my['options']['get_steps']) {
    +            $step_set = $this->get_steps($tcInfo['id']);
    +            $tcInfo['steps'] = $step_set;
    +        }
    +
    +        return $tcInfo;
    +    }
    +
    +    /*
    +     * function: copy_tcversion
    +     *
    +     * args:
    +     *
    +     * returns:
    +     *
    +     * rev:
    +     *
    +     */
    +    public function copyTestcaseVersion($id, $from_tcversion_id,
    +        $to_tcversion_id, $as_version_number, $user_id)
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +        $now = $this->db->db_now();
    +        $sql = "/* $debugMsg */ " . " INSERT INTO {$this->tables['tcversions']} " .
    +            " (id,version,tc_external_id,author_id,creation_ts,summary, " .
    +            "  importance,execution_type,preconditions,estimated_exec_duration) " .
    +            " SELECT {$to_tcversion_id} AS id, {$as_version_number} AS version, " .
    +            "        tc_external_id, " .
    +            "        {$user_id} AS author_id, {$now} AS creation_ts," .
    +            "        summary,importance,execution_type, preconditions,estimated_exec_duration " .
    +            " FROM {$this->tables['tcversions']} " .
    +            " WHERE id={$from_tcversion_id} ";
    +        $this->db->exec_query($sql);
    +
    +        // copy custom fields values JUST DESIGN AREA
    +        $this->copyCfieldsDesignValues(
    +            array(
    +                'id' => $id,
    +                'tcversion_id' => $from_tcversion_id
    +            ), array(
    +                'id' => $id,
    +                'tcversion_id' => $to_tcversion_id
    +            ));
    +
    +        // Need to get all steps
    +        $gso = array(
    +            'renderGhostSteps' => false,
    +            'renderImageInline' => false
    +        );
    +        $stepsSet = $this->get_steps($from_tcversion_id, 0, $gso);
    +        if (! empty($stepsSet)) {
    +            foreach ($stepsSet as $step) {
    +                $this->create_step($to_tcversion_id, $step['step_number'],
    +                    $step['actions'], $step['expected_results'],
    +                    $step['execution_type']);
    +            }
    +        }
    +    }
    +
    +    /*
    +     * function: get_by_id_bulk
    +     *
    +     * IMPORTANT CONSIDERATION:
    +     * how may elements can be used in an SQL IN CLAUSE?
    +     * Think there is a limit ( on MSSQL 1000 ?)
    +     *
    +     * args :
    +     *
    +     * returns:
    +     *
    +     */
    +    public function get_by_id_bulk($id, $version_id = self::ALL_VERSIONS,
    +        $get_active = 0, $get_open = 0)
    +    {
    +        $debugMsg = $this->debugMsg . __FUNCTION__;
    +        $where_clause = "";
    +        $where_clause_names = "";
    +        $tcid_list = "";
    +        $tcversion_id_filter = "";
    +        $sql = "";
    +        $the_names = null;
    +        if (is_array($id)) {
    +            $tcid_list = implode(",", $id);
    +            $where_clause = " WHERE nodes_hierarchy.parent_id IN ($tcid_list) ";
    +            $where_clause_names = " WHERE nodes_hierarchy.id IN ($tcid_list) ";
    +        } else {
    +            $where_clause = " WHERE nodes_hierarchy.parent_id = {$id} ";
    +            $where_clause_names = " WHERE nodes_hierarchy.id = {$id} ";
    +        }
    +        if ($version_id != self::ALL_VERSIONS) {
    +            $tcversion_id_filter = " AND tcversions.id IN (" .
    +                implode(",", (array) $version_id) . ") ";
    +        }
    +
    +        $sql = " /* $debugMsg */ SELECT nodes_hierarchy.parent_id AS testcase_id, " .
    +            " tcversions.*, users.first AS author_first_name, users.last AS author_last_name, " .
    +            " '' AS updater_first_name, '' AS updater_last_name " .
    +            " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy " .
    +            " JOIN {$this->tables['tcversions']} tcversions ON nodes_hierarchy.id = tcversions.id " .
    +            " LEFT OUTER JOIN {$this->tables['users']} users ON tcversions.author_id = users.id " .
    +            " {$where_clause} {$tcversion_id_filter} ORDER BY tcversions.version DESC";
    +        $recordset = $this->db->get_recordset($sql);
    +
    +        if ($recordset) {
    +            // get the names
    +            $sql = " /* $debugMsg */ " .
    +                " SELECT nodes_hierarchy.id AS testcase_id, nodes_hierarchy.name " .
    +                " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy {$where_clause_names} ";
    +
    +            $the_names = $this->db->get_recordset($sql);
    +            if ($the_names) {
    +                foreach ($recordset as $the_key => $row) {
    +                    reset($the_names);
    +                    foreach ($the_names as $row_n) {
    +                        if ($row['testcase_id'] == $row_n['testcase_id']) {
    +                            $recordset[$the_key]['name'] = $row_n['name'];
    +                            break;
    +                        }
    +                    }
    +                }
    +            }
    +
    +            $sql = " /* $debugMsg */ " .
    +                " SELECT updater_id, users.first AS updater_first_name, users.last  AS updater_last_name " .
    +                " FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy " .
    +                " JOIN {$this->tables['tcversions']} tcversions ON nodes_hierarchy.id = tcversions.id " .
    +                " LEFT OUTER JOIN {$this->tables['users']} users ON tcversions.updater_id = users.id " .
    +                " {$where_clause} and tcversions.updater_id IS NOT NULL ";
    +
    +            $updaters = $this->db->get_recordset($sql);
    +
    +            if ($updaters) {
    +                reset($recordset);
    +                foreach ($recordset as $the_key => $row) {
    +                    if (! is_null($row['updater_id'])) {
    +                        foreach ($updaters as $row_upd) {
    +                            if ($row['updater_id'] == $row_upd['updater_id']) {
    +                                $recordset[$the_key]['updater_last_name'] = $row_upd['updater_last_name'];
    +                                $recordset[$the_key]['updater_first_name'] = $row_upd['updater_first_name'];
    +                                break;
    +                            }
    +                        }
    +                    }
    +                }
    +            }
    +        }
    +
    +        return $recordset ? $recordset : null;
    +    }
    +
    +    /*
    +     * function: get_by_id
    +     *
    +     * args : id: can be a single testcase id or an array od testcase id.
    +     *
    +     * [version_id]: default self::ALL_VERSIONS => all versions
    +     * can be an array.
    +     * Useful to retrieve only a subset of versions.
    +     * null => means use version_number argument
    +     *
    +     * [filters]:
    +     * [active_status]: default 'ALL', range: 'ALL','ACTIVE','INACTIVE'
    +     * has effect for the following version_id values:
    +     * self::ALL_VERSIONS,TC_LAST_VERSION, version_id is NOT an array
    +     *
    +     * [open_status]: default 'ALL'
    +     * currently not used.
    +     *
    +     * [version_number]: default 1, version number displayed at User Interface
    +     *
    +     * [options]:
    +     * [output]: default 'full'
    +     * domain 'full','essential','full_without_steps','full_without_users'
    +     *
    +     * returns: array
    +     *
    +     */
    +    public function get_by_id($id, $version_id = self::ALL_VERSIONS,
    +        $filters = null, $options = null)
    +    {
    +        $my['filters'] = array(
    +            'active_status' => 'ALL',
    +            'open_status' => 'ALL',
    +            'version_number' => 1
    +        );
    +        $my['filters'] = array_merge($my['filters'], (array) $filters);
    +
    +        $my['options'] = [
    +            'output' => 'full',
    +            'access_key' => 'tcversion_id',
    +            'getPrefix' => false,
    +            'order_by' => null,
    +            'withGhostString' => false,
    +            'renderGhost' => false,
    +            'renderImageInline' => false,
    +            'renderVariables' => false,
    +            'renderSpecialKW' => false
    +        ];
    +
    +        $my['options'] = array_merge($my['options'], (array) $options);
    +
    +        $tcid_list = null;
    +        $where_clause = '';
    +        $active_filter = '';
    +        $versionSQLOp = ' AND ';
    +
    +        if (is_null($id) && ! is_null($version_id)) {
    +            $versionSQLOp = ' WHERE ';
    +        } elseif (is_array($id)) {
    +            $tcid_list = implode(",", $id);
    +            $where_clause = " WHERE NHTCV.parent_id IN ({$tcid_list}) ";
    +        } else {
    +            $where_clause = " WHERE NHTCV.parent_id = {$id} ";
    +        }
    +
    +        if ($version_id_is_array = is_array($version_id)) {
    +            $versionid_list = implode(",", $version_id);
    +            $where_clause .= $versionSQLOp . " TCV.id IN ({$versionid_list}) ";
    +        } else {
    +            if (is_null($version_id)) {
    +                // when tcase ID has not been provided this can not be used
    +                // will not do any check => leave it CRASH
    +                $where_clause .= " AND TCV.version = {$my['filters']['version_number']} ";
    +            } else {
    +                if ($version_id != self::ALL_VERSIONS &&
    +                    $version_id != self::LATEST_VERSION) {
    +                    $where_clause .= $versionSQLOp . " TCV.id = {$version_id} ";
    +                }
    +            }
    +
    +            $active_status = strtoupper($my['filters']['active_status']);
    +            if ($active_status != 'ALL') {
    +                $active_filter = ' AND TCV.active=' .
    +                    ($active_status == 'ACTIVE' ? 1 : 0) . ' ';
    +            }
    +        }
    +
    +        switch ($my['options']['output']) {
    +            case 'full':
    +            case 'full_without_steps':
    +                $sql = "SELECT UA.login AS updater_login,UB.login AS author_login,
                     NHTC.name,NHTC.node_order,NHTC.parent_id AS testsuite_id,
                     NHTCV.parent_id AS testcase_id, TCV.*,
                     UB.first AS author_first_name,UB.last AS author_last_name,
    @@ -2496,1196 +2854,1171 @@ function get_by_id($id,$version_id = self::ALL_VERSIONS, $filters = null, $optio
                     JOIN {$this->tables['tcversions']} TCV ON NHTCV.id = TCV.id
                     LEFT OUTER JOIN {$this->tables['users']} UB ON TCV.author_id = UB.id
                     LEFT OUTER JOIN {$this->tables['users']} UA ON TCV.updater_id = UA.id
    -                $where_clause $active_filter";
    -
    -            if(is_null($my['options']['order_by'])) {
    -              $sql .= " ORDER BY TCV.version DESC";
    -            }
    -            else {
    -              $sql .= $my['options']['order_by'];
    -            }
    -            break;
    -
    -      case 'full_without_users':
    -        $tcversionFields = 'TCV.id,TCV.tc_external_id,TCV.version,TCV.status,TCV.active,TCV.is_open,' .
    -                           'TCV.execution_type,TCV.importance';
    -
    -        // ATTENTION:
    -        // Order is critical for functions that use this recordset
    -        // (see specview.php).
    -        //
    -        $sql = "SELECT NHTC.name,NHTC.node_order,NHTC.parent_id AS testsuite_id,
    +                $where_clause $active_filter";
    +
    +                if (is_null($my['options']['order_by'])) {
    +                    $sql .= " ORDER BY TCV.version DESC";
    +                } else {
    +                    $sql .= $my['options']['order_by'];
    +                }
    +                break;
    +
    +            case 'full_without_users':
    +                $tcversionFields = 'TCV.id,TCV.tc_external_id,TCV.version,TCV.status,TCV.active,TCV.is_open,' .
    +                    'TCV.execution_type,TCV.importance';
    +
    +                // ATTENTION:
    +                // Order is critical for functions that use this recordset
    +                // (see specview.php).
    +                $sql = "SELECT NHTC.name,NHTC.node_order,NHTC.parent_id AS testsuite_id,
                     NHTCV.parent_id AS testcase_id, {$tcversionFields}
                     FROM {$this->tables['nodes_hierarchy']} NHTCV
                     JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTCV.parent_id = NHTC.id
                     JOIN {$this->tables['tcversions']} TCV ON NHTCV.id = TCV.id
    -                $where_clause $active_filter";
    -
    -            if(is_null($my['options']['order_by'])) {
    -              $sql .= " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC ";
    -            }
    -            else {
    -              $sql .= $my['options']['order_by'];
    -            }
    -            break;
    -
    -      case 'essential':
    -        $sql = " SELECT NHTC.name,NHTC.node_order,NHTCV.parent_id AS testcase_id, " .
    -               " NHTC.parent_id AS testsuite_id, " .
    -               " TCV.version, TCV.id, TCV.tc_external_id " .
    -               " FROM {$this->tables['nodes_hierarchy']} NHTCV " .
    -               " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTCV.parent_id = NHTC.id " .
    -               " JOIN {$this->tables['tcversions']} TCV ON NHTCV.id = TCV.id " .
    -               " {$where_clause} {$active_filter} ";
    -
    -        if(is_null($my['options']['order_by'])) {
    -          $sql .= " ORDER BY TCV.version DESC ";
    -        } else {
    -          $sql .= $my['options']['order_by'];
    -        }
    -      break;
    -    }
    -
    -    $render = array();
    -    $render['ghost'] = false;
    -    $render['ghostSteps'] = false;
    -    $render['imageInline'] = $my['options']['renderImageInline'];
    -    $render['variables'] = $my['options']['renderVariables'];
    -    $render['specialKW'] = $my['options']['renderSpecialKW'];
    -
    -    switch($my['options']['output']) {
    -      case 'full':
    -      case 'full_without_users':
    -        $render['ghost'] = $my['options']['renderGhost'];
    -        $render['ghostSteps'] = true;
    -      break;
    -
    -      case 'full_without_steps':
    -        $render['ghost'] = $my['options']['renderGhost'];
    -        $render['ghostSteps'] = false;
    -      break;
    -
    -      case 'essential':
    -        $render['imageInline'] = false;
    -        $render['variables'] = false;
    -      break;
    -    }
    -
    -    $recordset = null;
    -
    -    // Control improvements
    -    if( !$version_id_is_array && $version_id == self::LATEST_VERSION) {
    -      // But, how performance wise can be do this, instead of using MAX(version)
    -      // and a group by?
    -      //
    -      // if $id was a list then this will return something USELESS
    -      //
    -      if( is_null($tcid_list) ) {
    -        $recordset = array($this->db->fetchFirstRow($sql));
    -      }
    -      else {
    -        // Write to event viewer ???
    -        // throw exception ??
    -      }
    -    }
    -    else {
    -      $recordset = $this->db->get_recordset($sql);
    -    }
    -
    -    $canProcess = !is_null($recordset);
    -
    -    if( $canProcess && $render['variables'] ) {
    -      $key2loop = array_keys($recordset);
    -      foreach( $key2loop as $accessKey) {
    -        $this->renderVariables($recordset[$accessKey]);
    -      }
    -      reset($recordset);
    -    }
    -
    -    if( $canProcess && $render['specialKW'] ) {
    -      $key2loop = array_keys($recordset);
    -      foreach( $key2loop as $accessKey) {
    -        $this->renderSpecialTSuiteKeywords($recordset[$accessKey]);
    -      }
    -      reset($recordset);
    -    }
    -
    -
    -    // ghost on preconditions and summary
    -    if( $canProcess && $my['options']['renderGhost'] ) {
    -      $key2loop = array_keys($recordset);
    -      foreach( $key2loop as $accessKey) {
    -        $this->renderGhost($recordset[$accessKey]);
    -      }
    -      reset($recordset);
    -    }
    -
    -    if( $canProcess && $render['imageInline']) {
    -      $key2loop = array_keys($recordset);
    -      foreach( $key2loop as $accessKey) {
    -        $pVersion = $recordset[$accessKey]['id'];
    -        $this->renderImageAttachments($pVersion,$recordset[$accessKey]);
    -      }
    -      reset($recordset);
    -    }
    -
    -
    -    // Multiple Test Case Steps
    -    if( $canProcess && $my['options']['output'] == 'full') {
    -      $gsOpt['renderGhostSteps'] = $my['options']['renderGhost'];
    -
    -      $key2loop = array_keys($recordset);
    -      foreach( $key2loop as $accessKey) {
    -        $step_set = $this->get_steps($recordset[$accessKey]['id'],0,$gsOpt);
    -        if($my['options']['withGhostString']) {
    -          // need to get test case prefix test project info
    -          $pfx = $this->getPrefix($id);
    -          $pfx = $pfx[0] . $this->cfg->testcase->glue_character . $recordset[$accessKey]['tc_external_id'];
    -
    -          $k2l = array_keys((array)$step_set);
    -          foreach($k2l as $kx) {
    -            $step_set[$kx]['ghost_action'] =
    -              sprintf(self::GHOSTSTEPMASK,$step_set[$kx]['step_number'],
    -                      $pfx,$recordset[$accessKey]['version']);
    -
    -            $step_set[$kx]['ghost_result'] = $step_set[$kx]['ghost_action'];
    -          }
    -        }
    -        $recordset[$accessKey]['steps'] = $step_set;
    -      }
    -    }
    -
    -    if( $canProcess && $my['options']['getPrefix'] ) {
    -      $pfx = $this->getPrefix($id);
    -      $key2loop = array_keys($recordset);
    -      foreach( $key2loop as $accessKey) {
    -        $recordset[$accessKey]['fullExternalID'] =  $pfx[0] . $this->cfg->testcase->glue_character .
    -                                                    $recordset[$accessKey]['tc_external_id'];
    -      }
    -    }
    -
    -    return ($recordset ? $recordset : null);
    -  }
    -
    -
    -  /*
    -    function: get_versions_status_quo
    -              Get linked and executed status quo.
    -
    -              IMPORTANT:
    -              NO INFO SPECIFIC TO TESTPLAN ITEMS where testacase can be linked to
    -              is returned.
    -
    -
    -    args : id: test case id
    -           [tcversion_id]: default: null -> get info about all versions.
    -                           can be a single value or an array.
    -
    -
    -           [testplan_id]: default: null -> all testplans where testcase is linked,
    -                                           are analised to generate results.
    -
    -                          when not null, filter for testplan_id, to analise for
    -                          generating results.
    -
    -
    -
    -    returns: map.
    -             key: tcversion_id.
    -             value: map with the following keys:
    -
    -             tcversion_id, linked , executed
    -
    -             linked field: will take the following values
    -                           if $testplan_id == null
    -                              NULL if the tc version is not linked to ANY TEST PLAN
    -                              tcversion_id if linked
    -
    -                           if $testplan_id != null
    -                              NULL if the tc version is not linked to $testplan_id
    -
    -
    -             executed field: will take the following values
    -                             if $testplan_id == null
    -                                NULL if the tc version has not been executed in ANY TEST PLAN
    -                                tcversion_id if has executions.
    -
    -                             if $testplan_id != null
    -                                NULL if the tc version has not been executed in $testplan_id
    -
    -  rev :
    -
    -  */
    -  function get_versions_status_quo($id, $tcversion_id=null, $testplan_id=null) {
    -    $testplan_filter='';
    -    $tcversion_filter='';
    -    if(!is_null($tcversion_id)) {
    -      if(is_array($tcversion_id)) {
    -         $tcversion_filter=" AND NH.id IN (" . implode(",",$tcversion_id) . ") ";
    -      } else {
    -         $tcversion_filter=" AND NH.id={$tcversion_id} ";
    -      }
    -    }
    -
    -    $testplan_filter='';
    -    if(!is_null($testplan_id)){
    -      $testplan_filter=" AND E.testplan_id = {$testplan_id} ";
    -    }
    -    $execution_join=" LEFT OUTER JOIN {$this->tables['executions']} E " .
    -                    " ON (E.tcversion_id = NH.id {$testplan_filter})";
    -
    -    $sqlx=  " SELECT TCV.id,TCV.version  
    +                $where_clause $active_filter";
    +
    +                if (is_null($my['options']['order_by'])) {
    +                    $sql .= " ORDER BY NHTC.node_order, NHTC.name, TCV.version DESC ";
    +                } else {
    +                    $sql .= $my['options']['order_by'];
    +                }
    +                break;
    +
    +            case 'essential':
    +                $sql = " SELECT NHTC.name,NHTC.node_order,NHTCV.parent_id AS testcase_id, " .
    +                    " NHTC.parent_id AS testsuite_id, " .
    +                    " TCV.version, TCV.id, TCV.tc_external_id " .
    +                    " FROM {$this->tables['nodes_hierarchy']} NHTCV " .
    +                    " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTCV.parent_id = NHTC.id " .
    +                    " JOIN {$this->tables['tcversions']} TCV ON NHTCV.id = TCV.id " .
    +                    " {$where_clause} {$active_filter} ";
    +
    +                if (is_null($my['options']['order_by'])) {
    +                    $sql .= " ORDER BY TCV.version DESC ";
    +                } else {
    +                    $sql .= $my['options']['order_by'];
    +                }
    +                break;
    +        }
    +
    +        $render = array();
    +        $render['ghost'] = false;
    +        $render['ghostSteps'] = false;
    +        $render['imageInline'] = $my['options']['renderImageInline'];
    +        $render['variables'] = $my['options']['renderVariables'];
    +        $render['specialKW'] = $my['options']['renderSpecialKW'];
    +
    +        switch ($my['options']['output']) {
    +            case 'full':
    +            case 'full_without_users':
    +                $render['ghost'] = $my['options']['renderGhost'];
    +                $render['ghostSteps'] = true;
    +                break;
    +
    +            case 'full_without_steps':
    +                $render['ghost'] = $my['options']['renderGhost'];
    +                $render['ghostSteps'] = false;
    +                break;
    +
    +            case 'essential':
    +                $render['imageInline'] = false;
    +                $render['variables'] = false;
    +                break;
    +        }
    +
    +        $recordset = null;
    +
    +        // Control improvements
    +        if (! $version_id_is_array && $version_id == self::LATEST_VERSION) {
    +            // But, how performance wise can be do this, instead of using MAX(version)
    +            // and a group by?
    +            //
    +            // if $id was a list then this will return something USELESS
    +            if (is_null($tcid_list)) {
    +                $recordset = array(
    +                    $this->db->fetchFirstRow($sql)
    +                );
    +            }
    +        } else {
    +            $recordset = $this->db->get_recordset($sql);
    +        }
    +
    +        $canProcess = ! is_null($recordset);
    +
    +        if ($canProcess && $render['variables']) {
    +            $key2loop = array_keys($recordset);
    +            foreach ($key2loop as $accessKey) {
    +                try {
    +                    $this->renderVariables($recordset[$accessKey],
    +                        $my['options']['tproject_id']);
    +                } catch (Exception $e) {
    +                    echo '
    ';
    +                    debug_print_backtrace();
    +                    echo '
    '; + die(); + } + } + reset($recordset); + } + + if ($canProcess && $render['specialKW']) { + $key2loop = array_keys($recordset); + foreach ($key2loop as $accessKey) { + $this->renderSpecialTSuiteKeywords($recordset[$accessKey]); + } + reset($recordset); + } + + // ghost on preconditions and summary + if ($canProcess && $my['options']['renderGhost']) { + $key2loop = array_keys($recordset); + foreach ($key2loop as $accessKey) { + $this->renderGhost($recordset[$accessKey]); + } + reset($recordset); + } + + if ($canProcess && $render['imageInline']) { + $key2loop = array_keys($recordset); + foreach ($key2loop as $accessKey) { + $pVersion = $recordset[$accessKey]['id']; + $this->renderImageAttachments($pVersion, $recordset[$accessKey]); + } + reset($recordset); + } + + // Multiple Test Case Steps + if ($canProcess && $my['options']['output'] == 'full') { + $gsOpt['renderGhostSteps'] = $my['options']['renderGhost']; + + $key2loop = array_keys($recordset); + foreach ($key2loop as $accessKey) { + $step_set = $this->get_steps($recordset[$accessKey]['id'], 0, + $gsOpt); + if ($my['options']['withGhostString']) { + // need to get test case prefix test project info + $pfx = $this->getPrefix( + $recordset[$accessKey]['testcase_id']); + $pfx = $pfx[0] . $this->cfg->testcase->glue_character . + $recordset[$accessKey]['tc_external_id']; + + $k2l = array_keys((array) $step_set); + foreach ($k2l as $kx) { + $step_set[$kx]['ghost_action'] = sprintf( + self::GHOSTSTEPMASK, $step_set[$kx]['step_number'], + $pfx, $recordset[$accessKey]['version']); + + $step_set[$kx]['ghost_result'] = $step_set[$kx]['ghost_action']; + } + } + $recordset[$accessKey]['steps'] = $step_set; + } + } + + if ($canProcess && $my['options']['getPrefix']) { + $key2loop = array_keys($recordset); + foreach ($key2loop as $accessKey) { + $pfx = $this->getPrefix($recordset[$accessKey]['testcase_id']); + $recordset[$accessKey]['fullExternalID'] = $pfx[0] . + $this->cfg->testcase->glue_character . + $recordset[$accessKey]['tc_external_id']; + } + } + + return $recordset ? $recordset : null; + } + + /* + * function: get_versions_status_quo + * Get linked and executed status quo. + * + * IMPORTANT: + * NO INFO SPECIFIC TO TESTPLAN ITEMS where testacase can be linked to + * is returned. + * + * + * args : id: test case id + * [tcversion_id]: default: null -> get info about all versions. + * can be a single value or an array. + * + * + * [testplan_id]: default: null -> all testplans where testcase is linked, + * are analised to generate results. + * + * when not null, filter for testplan_id, to analise for + * generating results. + * + * + * + * returns: map. + * key: tcversion_id. + * value: map with the following keys: + * + * tcversion_id, linked , executed + * + * linked field: will take the following values + * if $testplan_id == null + * NULL if the tc version is not linked to ANY TEST PLAN + * tcversion_id if linked + * + * if $testplan_id != null + * NULL if the tc version is not linked to $testplan_id + * + * + * executed field: will take the following values + * if $testplan_id == null + * NULL if the tc version has not been executed in ANY TEST PLAN + * tcversion_id if has executions. + * + * if $testplan_id != null + * NULL if the tc version has not been executed in $testplan_id + * + * rev : + * + */ + public function getVersionsStatusQuo($id, $tcversion_id = null, + $testplan_id = null) + { + $testplan_filter = ''; + $tcversion_filter = ''; + if (! is_null($tcversion_id)) { + if (is_array($tcversion_id)) { + $tcversion_filter = " AND NH.id IN (" . + implode(",", $tcversion_id) . ") "; + } else { + $tcversion_filter = " AND NH.id={$tcversion_id} "; + } + } + + $testplan_filter = ''; + if (! is_null($testplan_id)) { + $testplan_filter = " AND E.testplan_id = {$testplan_id} "; + } + $execution_join = " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON (E.tcversion_id = NH.id {$testplan_filter})"; + + $sqlx = " SELECT TCV.id,TCV.version FROM {$this->tables['nodes_hierarchy']} NHA - JOIN {$this->tables['nodes_hierarchy']} NHB + JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id - JOIN {$this->tables['tcversions']} TCV ON NHA.id = TCV.id - WHERE NHA.parent_id = " . intval($id); - - $version_id = $this->db->fetchRowsIntoMap($sqlx,'version'); - - $sql="SELECT DISTINCT NH.id AS tcversion_id,T.tcversion_id AS linked, " . - " E.tcversion_id AS executed,E.tcversion_number,TCV.version " . - " FROM {$this->tables['nodes_hierarchy']} NH " . - " JOIN {$this->tables['tcversions']} TCV ON (TCV.id = NH.id ) " . - " LEFT OUTER JOIN {$this->tables['testplan_tcversions']} T ON T.tcversion_id = NH.id " . - " {$execution_join} WHERE NH.parent_id = {$id} {$tcversion_filter} ORDER BY executed DESC"; - - $rs = $this->db->get_recordset($sql); - - $recordset=array(); - $template=array('tcversion_id' => '','linked' => '','executed' => ''); - foreach($rs as $elem) { - $recordset[$elem['tcversion_id']]=$template; - $recordset[$elem['tcversion_id']]['tcversion_id']=$elem['tcversion_id']; - $recordset[$elem['tcversion_id']]['linked']=$elem['linked']; - $recordset[$elem['tcversion_id']]['version']=$elem['version']; - } - - foreach($rs as $elem) { - $tcvid=null; - if( $elem['tcversion_number'] != $elem['version']) { - if( !is_null($elem['tcversion_number']) ) { - $tcvid=$version_id[$elem['tcversion_number']]['id']; - } - } else { - $tcvid=$elem['tcversion_id']; - } - - if( !is_null($tcvid) ) { - $recordset[$tcvid]['executed']=$tcvid; - $recordset[$tcvid]['version']=$elem['tcversion_number']; - } - } - - return $recordset; - } - - - - /* - function: get_exec_status - Get information about executed and linked status in - every testplan, a testcase is linked to. - - args : id : testcase id - [exec_status]: default: ALL, range: ALL,EXECUTED,NOT_EXECUTED - [active_status]: default: ALL, range: ALL,ACTIVE,INACTIVE - - - returns: map - key: tcversion_id - value: map: - key: testplan_id - value: map with following keys: - - tcase_id - tcversion_id - version - testplan_id - tplan_name - linked if linked to testplan -> tcversion_id - executed if executed in testplan -> tcversion_id - exec_on_tplan if executed in testplan -> testplan_id - - - rev: - 20100908 - franciscom - added platform name in output recordset - - 20080531 - franciscom - Because we allow people to update test case version linked to test plan, - and to do this we update tcversion_id on executions to new version - maintaining the really executed version in tcversion_number (version number displayed - on User Interface) field we need to change algorithm. - */ - function get_exec_status($id,$filters=null, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array(); - $my['filters'] = array( 'exec_status' => "ALL", 'active_status' => 'ALL', - 'tplan_id' => null, 'platform_id' => null); - $my['options'] = array('addExecIndicator' => false); - - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - - $active_status = strtoupper($my['filters']['active_status']); - $exec_status = strtoupper($my['filters']['exec_status']); - $tplan_id = $my['filters']['tplan_id']; - $platform_id = $my['filters']['platform_id']; - - // Get info about tcversions of this test case - $sqlx = "/* $debugMsg */ " . - " SELECT TCV.id,TCV.version,TCV.active" . - " FROM {$this->tables['nodes_hierarchy']} NHA " . - " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . - " JOIN {$this->tables['tcversions']} TCV ON NHA.id = TCV.id "; - - $where_clause = " WHERE NHA.parent_id = " . $this->db->prepare_int($id); - - if(!is_null($tplan_id)) - { - $sqlx .= " JOIN {$this->tables['testplan_tcversions']} TTCV ON TTCV.tcversion_id = TCV.id "; - $where_clause .= " AND TTCV.tplan_id = " . $this->db->prepare_int($tplan_id); - } - $sqlx .= $where_clause; - $version_id = $this->db->fetchRowsIntoMap($sqlx,'version'); - - $sql = "/* $debugMsg */ " . - " SELECT DISTINCT NH.parent_id AS tcase_id, NH.id AS tcversion_id, " . - " T.tcversion_id AS linked, T.platform_id, TCV.active, E.tcversion_id AS executed, " . - " E.testplan_id AS exec_on_tplan, E.tcversion_number, " . - " T.testplan_id, NHB.name AS tplan_name, TCV.version, PLAT.name AS platform_name " . - " FROM {$this->tables['nodes_hierarchy']} NH " . - " JOIN {$this->tables['testplan_tcversions']} T ON T.tcversion_id = NH.id " . - " JOIN {$this->tables['tcversions']} TCV ON T.tcversion_id = TCV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHB ON T.testplan_id = NHB.id " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT " . - " ON T.platform_id = PLAT.id " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON (E.tcversion_id = NH.id AND E.testplan_id=T.testplan_id AND E.platform_id=T.platform_id ) " . - " WHERE NH.parent_id = " . $this->db->prepare_int($id); - - if(!is_null($tplan_id)) - { - $sql .= " AND T.tplan_id = " . $this->db->prepare_int($tplan_id); - } - if(!is_null($platform_id)) - { - $sql .= " AND T.platform_id = " . $this->db->prepare_int($platform_id); - } - - $sql .= " ORDER BY version,tplan_name"; - $rs = $this->db->get_recordset($sql); - - // set right tcversion_id, based on tcversion_number,version comparison - $item_not_executed = null; - $item_executed = null; - $link_info = null; - $in_set = null; - - if (sizeof($rs)) - { - foreach($rs as $idx => $elem) - { - if( $elem['tcversion_number'] != $elem['version']) - { - // Save to generate record for linked but not executed if needed - // (see below fix not executed section) - // access key => (version,test plan, platform) - $link_info[$elem['tcversion_id']][$elem['testplan_id']][$elem['platform_id']]=$elem; - - // We are working with a test case version, that was used in a previous life of this test plan - // information about his tcversion_id is not anymore present in tables: - // - // testplan_tcversions - // executions - // cfield_execution_values. - // - // if has been executed, but after this operation User has choosen to upgrade tcversion - // linked to testplan to a different (may be a newest) test case version. - // - // We can get this information using table tcversions using tcase id and version number - // (value displayed at User Interface) as search key. - // - // Important: - // executions.tcversion_number: maintain info about RIGHT TEST case version executed - // executions.tcversion_id : test case version linked to test plan. - // - // - if( is_null($elem['tcversion_number']) ) - { - // Not Executed - $rs[$idx]['executed']=null; - $rs[$idx]['tcversion_id']=$elem['tcversion_id']; - $rs[$idx]['version']=$elem['version']; - $rs[$idx]['linked']=$elem['tcversion_id']; - $item_not_executed[]=$idx; - } - else - { - // Get right tcversion_id - $rs[$idx]['executed']=$version_id[$elem['tcversion_number']]['id']; - $rs[$idx]['tcversion_id']=$rs[$idx]['executed']; - $rs[$idx]['version']=$elem['tcversion_number']; - $rs[$idx]['linked']=$rs[$idx]['executed']; - $item_executed[]=$idx; - } - $version=$rs[$idx]['version']; - $rs[$idx]['active']=$version_id[$version]['active']; - } - else - { - $item_executed[]=$idx; - } - - // needed for logic to avoid miss not executed (see below fix not executed) - $in_set[$rs[$idx]['tcversion_id']][$rs[$idx]['testplan_id']][$rs[$idx]['platform_id']]=$rs[$idx]['tcversion_id']; - } - } - else - { - $rs = array(); - } - - // fix not executed - // - // need to add record for linked but not executed, that due to new - // logic to upate testplan-tcversions link can be absent - if(!is_null($link_info)) - { - foreach($link_info as $tcversion_id => $elem) - { - foreach($elem as $testplan_id => $platform_link) - { - foreach($platform_link as $platform_id => $value) - { - if( !isset($in_set[$tcversion_id][$testplan_id][$platform_id]) ) - { - // missing record - $value['executed']=null; - $value['exec_on_tplan']=null; - $value['tcversion_number']=null; - $rs[]=$value; - - // Must Update list of not executed - $kix=count($rs); - $item_not_executed[]=$kix > 0 ? $kix-1 : $kix; - } - - } - } - } - } - - // Convert to result map. - switch ($exec_status) - { - case 'NOT_EXECUTED': - $target=$item_not_executed; - break; - - case 'EXECUTED': - $target=$item_executed; - break; - - default: - $target = array_keys($rs); - break; - } - - $recordset = null; - - if( !is_null($target) ) - { - foreach($target as $idx) - { - $wkitem=$rs[$idx]; - if( $active_status=='ALL' || - $active_status='ACTIVE' && $wkitem['active'] || - $active_status='INACTIVE' && $wkitem['active']==0 ) - { - $recordset[$wkitem['tcversion_id']][$wkitem['testplan_id']][$wkitem['platform_id']]=$wkitem; - - if( $my['options']['addExecIndicator'] ) - { - if( !isset($recordset['executed']) ) - { - $recordset['executed'] = 0; - } - - if( $recordset['executed'] == 0 ) - { - if( !is_null($wkitem['executed']) ) - { - $recordset['executed'] = 1; - } - } - } - } - } - } - - if( !is_null($recordset) ) - { - // Natural name sort - ksort($recordset); - } - return $recordset; - } - // ------------------------------------------------------------------------------- - - - /** - * @param string stringID external test case ID - * a string on the form XXXXXGNN where: - * XXXXX: test case prefix, exists one for each test project - * G: glue character - * NN: test case number (generated using testprojects.tc_counter field) - * - * @return internal id (node id in nodes_hierarchy) - * 0 -> test case prefix OK, but external id does not exists - * 1 -> test case prefix KO - * - * 20080818 - franciscom - Dev Note - * I'm a feeling regarding performance of this function. - * Surelly adding a new column to tcversions (prefix) will simplify a lot this function. - * Other choice (that I refuse to implement time ago) is to add prefix field - * as a new nodes_hierarchy column. - * This must be discussed with dev team if we got performance bottleneck trying - * to get internal id from external one. - * - * @internal revisions - */ - function getInternalID($stringID,$opt = null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $internalID = 0; - $my['opt'] = array('glue' => $this->cfg->testcase->glue_character, - 'tproject_id' => null, 'output' => null); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - $status_ok = false; - $tproject_info = null; - - // When using this method on a context where caller certifies that - // test project is OK, we will skip this check. - $tproject_id = $my['opt']['tproject_id']; - if( !is_null($tproject_id) && !is_null($my['opt']['output']) ) { - $sql = " SELECT id,is_public FROM {$this->tables['testprojects']} " . - " WHERE id = " . intval($tproject_id); - - $tproject_info = $this->db->get_recordset($sql); - if( !is_null($tproject_info) ) { - $tproject_info = current($tproject_info); - } - } - - - // Find the last glue char - $gluePos = strrpos($stringID, $my['opt']['glue']); - $isFullExternal = ($gluePos !== false); - if($isFullExternal) { - $rawTestCasePrefix = substr($stringID, 0, $gluePos); - $rawExternalID = substr($stringID, $gluePos+1); - $status_ok = ($externalID = is_numeric($rawExternalID) ? intval($rawExternalID) : 0) > 0; - } else { - $status_ok = (($externalID = intval($stringID)) > 0); - } - - if( $status_ok && is_null($tproject_id) ) { - $status_ok = false; - if($isFullExternal) { - // Check first if Test Project prefix is valid, if not abort - $testCasePrefix = $this->db->prepare_string($rawTestCasePrefix); - $sql = "SELECT id,is_public FROM {$this->tables['testprojects']} " . - "WHERE prefix = '" . $this->db->prepare_string($testCasePrefix) . "'"; - - $tproject_info = $this->db->get_recordset($sql); - if( $status_ok = !is_null($tproject_info) ) { - $tproject_info = current($tproject_info); - $tproject_id = $tproject_info['id']; - // $tproject_id = $tproject_info[0]['id']; - } - } else { - throw new Exception(__METHOD__ . - ' EXCEPTION: When using just numeric part of External ID, test project ID, is mandatory'); - } - } - - if( $status_ok ) { - $internalID = 0; - - // get all test cases with requested external ID on all test projects. - // we do not have way to work only on one test project. - $sql = " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['tcversions']} TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON TCV.id = NHTCV.id " . - " WHERE TCV.tc_external_id = " . intval($externalID); - - $testCases = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - if(!is_null($testCases)) { - foreach($testCases as $tcaseID => $value) { - $path2root = $this->tree_manager->get_path($tcaseID); - if($tproject_id == $path2root[0]['parent_id']) { - $internalID = $tcaseID; - break; - } - } - } - } - return is_null($my['opt']['output']) ? $internalID : - array('id' => $internalID,'tproject' => $tproject_info); - } - - /* - function: filterByKeyword - given a test case id (or an array of test case id) - and a keyword filter, returns for the test cases given in input - only which pass the keyword filter criteria. - - - args : - - returns: - - */ - function filterByKeyword($id,$keyword_id=0, $keyword_filter_type='OR') - { - $keyword_filter= '' ; - $subquery=''; - - // test case filter - if( is_array($id) ) - { - $testcase_filter = " AND testcase_id IN (" . implode(',',$id) . ")"; - } - else - { - $testcase_filter = " AND testcase_id = {$id} "; - } - - if( is_array($keyword_id) ) - { - $keyword_filter = " AND keyword_id IN (" . implode(',',$keyword_id) . ")"; - - if($keyword_filter_type == 'AND') - { - $subquery = "AND testcase_id IN (" . - " SELECT MAFALDA.testcase_id FROM + JOIN {$this->tables['tcversions']} TCV ON NHA.id = TCV.id + WHERE NHA.parent_id = " . intval($id); + + $version_id = $this->db->fetchRowsIntoMap($sqlx, 'version'); + + $sql = "SELECT DISTINCT NH.id AS tcversion_id,T.tcversion_id AS linked, " . + " E.tcversion_id AS executed,E.tcversion_number,TCV.version " . + " FROM {$this->tables['nodes_hierarchy']} NH " . + " JOIN {$this->tables['tcversions']} TCV ON (TCV.id = NH.id ) " . + " LEFT OUTER JOIN {$this->tables['testplan_tcversions']} T ON T.tcversion_id = NH.id " . + " {$execution_join} WHERE NH.parent_id = {$id} {$tcversion_filter} ORDER BY executed DESC"; + + $rs = $this->db->get_recordset($sql); + + $recordset = array(); + $template = array( + 'tcversion_id' => '', + 'linked' => '', + 'executed' => '' + ); + foreach ($rs as $elem) { + $recordset[$elem['tcversion_id']] = $template; + $recordset[$elem['tcversion_id']]['tcversion_id'] = $elem['tcversion_id']; + $recordset[$elem['tcversion_id']]['linked'] = $elem['linked']; + $recordset[$elem['tcversion_id']]['version'] = $elem['version']; + } + + foreach ($rs as $elem) { + $tcvid = null; + if ($elem['tcversion_number'] != $elem['version']) { + if (! is_null($elem['tcversion_number'])) { + $tcvid = $version_id[$elem['tcversion_number']]['id']; + } + } else { + $tcvid = $elem['tcversion_id']; + } + + if (! is_null($tcvid)) { + $recordset[$tcvid]['executed'] = $tcvid; + $recordset[$tcvid]['version'] = $elem['tcversion_number']; + } + } + + return $recordset; + } + + /* + * function: get_exec_status + * Get information about executed and linked status in + * every testplan, a testcase is linked to. + * + * args : id : testcase id + * [exec_status]: default: ALL, range: ALL,EXECUTED,NOT_EXECUTED + * [active_status]: default: ALL, range: ALL,ACTIVE,INACTIVE + * + * + * returns: map + * key: tcversion_id + * value: map: + * key: testplan_id + * value: map with following keys: + * + * tcase_id + * tcversion_id + * version + * testplan_id + * tplan_name + * linked if linked to testplan -> tcversion_id + * executed if executed in testplan -> tcversion_id + * exec_on_tplan if executed in testplan -> testplan_id + * + * + * rev: + * 20100908 - franciscom - added platform name in output recordset + * + * 20080531 - franciscom + * Because we allow people to update test case version linked to test plan, + * and to do this we update tcversion_id on executions to new version + * maintaining the really executed version in tcversion_number (version number displayed + * on User Interface) field we need to change algorithm. + */ + public function getExecStatus($id, $filters = null, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my = array(); + $my['filters'] = array( + 'exec_status' => "ALL", + 'active_status' => 'ALL', + 'tplan_id' => null, + 'platform_id' => null + ); + $my['options'] = array( + 'addExecIndicator' => false + ); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + $active_status = strtoupper($my['filters']['active_status']); + $exec_status = strtoupper($my['filters']['exec_status']); + $tplan_id = $my['filters']['tplan_id']; + $platform_id = $my['filters']['platform_id']; + + // Get info about tcversions of this test case + $sqlx = "/* $debugMsg */ " . " SELECT TCV.id,TCV.version,TCV.active" . + " FROM {$this->tables['nodes_hierarchy']} NHA " . + " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id " . + " JOIN {$this->tables['tcversions']} TCV ON NHA.id = TCV.id "; + + $where_clause = " WHERE NHA.parent_id = " . $this->db->prepare_int($id); + + if (! is_null($tplan_id)) { + $sqlx .= " JOIN {$this->tables['testplan_tcversions']} TTCV ON TTCV.tcversion_id = TCV.id "; + $where_clause .= " AND TTCV.tplan_id = " . + $this->db->prepare_int($tplan_id); + } + $sqlx .= $where_clause; + $version_id = $this->db->fetchRowsIntoMap($sqlx, 'version'); + + $sql = "/* $debugMsg */ " . + " SELECT DISTINCT NH.parent_id AS tcase_id, NH.id AS tcversion_id, " . + " T.tcversion_id AS linked, T.platform_id, TCV.active, E.tcversion_id AS executed, " . + " E.testplan_id AS exec_on_tplan, E.tcversion_number, " . + " T.testplan_id, NHB.name AS tplan_name, TCV.version, PLAT.name AS platform_name " . + " FROM {$this->tables['nodes_hierarchy']} NH " . + " JOIN {$this->tables['testplan_tcversions']} T ON T.tcversion_id = NH.id " . + " JOIN {$this->tables['tcversions']} TCV ON T.tcversion_id = TCV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHB ON T.testplan_id = NHB.id " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT " . + " ON T.platform_id = PLAT.id " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON (E.tcversion_id = NH.id AND E.testplan_id=T.testplan_id AND E.platform_id=T.platform_id ) " . + " WHERE NH.parent_id = " . $this->db->prepare_int($id); + + if (! is_null($tplan_id)) { + $sql .= " AND T.tplan_id = " . $this->db->prepare_int($tplan_id); + } + if (! is_null($platform_id)) { + $sql .= " AND T.platform_id = " . + $this->db->prepare_int($platform_id); + } + + $sql .= " ORDER BY version,tplan_name"; + $rs = (array) $this->db->get_recordset($sql); + + // set right tcversion_id, based on tcversion_number,version comparison + $item_not_executed = null; + $item_executed = null; + $link_info = null; + $in_set = null; + + if (count($rs)) { + foreach ($rs as $idx => $elem) { + if ($elem['tcversion_number'] != $elem['version']) { + // Save to generate record for linked but not executed if needed + // (see below fix not executed section) + // access key => (version,test plan, platform) + $link_info[$elem['tcversion_id']][$elem['testplan_id']][$elem['platform_id']] = $elem; + + // We are working with a test case version, that was used in a previous life of this test plan + // information about his tcversion_id is not anymore present in tables: + // + // testplan_tcversions + // executions + // cfield_execution_values. + // + // if has been executed, but after this operation User has choosen to upgrade tcversion + // linked to testplan to a different (may be a newest) test case version. + // + // We can get this information using table tcversions using tcase id and version number + // (value displayed at User Interface) as search key. + // + // Important: + // executions.tcversion_number: maintain info about RIGHT TEST case version executed + // executions.tcversion_id : test case version linked to test plan. + if (is_null($elem['tcversion_number'])) { + // Not Executed + $rs[$idx]['executed'] = null; + $rs[$idx]['tcversion_id'] = $elem['tcversion_id']; + $rs[$idx]['version'] = $elem['version']; + $rs[$idx]['linked'] = $elem['tcversion_id']; + $item_not_executed[] = $idx; + } else { + // Get right tcversion_id + $rs[$idx]['executed'] = $version_id[$elem['tcversion_number']]['id']; + $rs[$idx]['tcversion_id'] = $rs[$idx]['executed']; + $rs[$idx]['version'] = $elem['tcversion_number']; + $rs[$idx]['linked'] = $rs[$idx]['executed']; + $item_executed[] = $idx; + } + $version = $rs[$idx]['version']; + $rs[$idx]['active'] = $version_id[$version]['active']; + } else { + $item_executed[] = $idx; + } + + // needed for logic to avoid miss not executed (see below fix not executed) + $in_set[$rs[$idx]['tcversion_id']][$rs[$idx]['testplan_id']][$rs[$idx]['platform_id']] = $rs[$idx]['tcversion_id']; + } + } else { + $rs = array(); + } + + // fix not executed + // + // need to add record for linked but not executed, that due to new + // logic to upate testplan-tcversions link can be absent + if (! is_null($link_info)) { + foreach ($link_info as $tcversion_id => $elem) { + foreach ($elem as $testplan_id => $platform_link) { + foreach ($platform_link as $platform_id => $value) { + if (! isset( + $in_set[$tcversion_id][$testplan_id][$platform_id])) { + // missing record + $value['executed'] = null; + $value['exec_on_tplan'] = null; + $value['tcversion_number'] = null; + $rs[] = $value; + + // Must Update list of not executed + $kix = count($rs); + $item_not_executed[] = $kix > 0 ? $kix - 1 : $kix; + } + } + } + } + } + + // Convert to result map. + switch ($exec_status) { + case 'NOT_EXECUTED': + $target = $item_not_executed; + break; + + case 'EXECUTED': + $target = $item_executed; + break; + + default: + $target = array_keys($rs); + break; + } + + $recordset = null; + + if (! is_null($target)) { + foreach ($target as $idx) { + $wkitem = $rs[$idx]; + if ($active_status == 'ALL' || + $active_status = 'ACTIVE' && $wkitem['active'] || + $active_status = 'INACTIVE' && $wkitem['active'] == 0) { + $recordset[$wkitem['tcversion_id']][$wkitem['testplan_id']][$wkitem['platform_id']] = $wkitem; + + if ($my['options']['addExecIndicator']) { + if (! isset($recordset['executed'])) { + $recordset['executed'] = 0; + } + + if ($recordset['executed'] == 0 && + ! is_null($wkitem['executed'])) { + $recordset['executed'] = 1; + } + } + } + } + } + + if (! is_null($recordset)) { + ksort($recordset); + } + return $recordset; + } + + /** + * + * @param + * string stringID external test case ID + * a string on the form XXXXXGNN where: + * XXXXX: test case prefix, exists one for each test project + * G: glue character + * NN: test case number (generated using testprojects.tc_counter field) + * + * @return int id (node id in nodes_hierarchy) + * 0 -> test case prefix OK, but external id does not exists + * 1 -> test case prefix KO + * + * 20080818 - franciscom - Dev Note + * I'm a feeling regarding performance of this function. + * Surelly adding a new column to tcversions (prefix) will simplify a lot this function. + * Other choice (that I refuse to implement time ago) is to add prefix field + * as a new nodes_hierarchy column. + * This must be discussed with dev team if we got performance bottleneck trying + * to get internal id from external one. + * + * @internal revisions + */ + public function getInternalID($stringID, $opt = null) + { + $internalID = 0; + $my['opt'] = array( + 'glue' => $this->cfg->testcase->glue_character, + 'tproject_id' => null, + 'output' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $status_ok = false; + $tproject_info = null; + + // When using this method on a context where caller certifies that + // test project is OK, we will skip this check. + $tproject_id = $my['opt']['tproject_id']; + if (! is_null($tproject_id) && ! is_null($my['opt']['output'])) { + $sql = " SELECT id,is_public FROM {$this->tables['testprojects']} " . + " WHERE id = " . intval($tproject_id); + + $tproject_info = $this->db->get_recordset($sql); + if (! is_null($tproject_info)) { + $tproject_info = current($tproject_info); + } + } + + // Find the last glue char + $gluePos = strrpos($stringID, $my['opt']['glue']); + $isFullExternal = ($gluePos !== false); + if ($isFullExternal) { + $rawTestCasePrefix = substr($stringID, 0, $gluePos); + $rawExternalID = substr($stringID, $gluePos + 1); + $status_ok = ($externalID = is_numeric($rawExternalID) ? intval( + $rawExternalID) : 0) > 0; + } else { + $status_ok = (($externalID = intval($stringID)) > 0); + } + + if ($status_ok && is_null($tproject_id)) { + $status_ok = false; + if ($isFullExternal) { + // Check first if Test Project prefix is valid, if not abort + $testCasePrefix = $this->db->prepare_string($rawTestCasePrefix); + $sql = "SELECT id,is_public FROM {$this->tables['testprojects']} " . + "WHERE prefix = '" . + $this->db->prepare_string($testCasePrefix) . "'"; + + $tproject_info = $this->db->get_recordset($sql); + if ($status_ok = ! is_null($tproject_info)) { + $tproject_info = current($tproject_info); + $tproject_id = $tproject_info['id']; + } + } else { + throw new Exception( + __METHOD__ . + ' EXCEPTION: When using just numeric part of External ID, test project ID, is mandatory'); + } + } + + if ($status_ok) { + $internalID = 0; + + // get all test cases with requested external ID on all test projects. + // we do not have way to work only on one test project. + $sql = " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['tcversions']} TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON TCV.id = NHTCV.id " . " WHERE TCV.tc_external_id = " . + intval($externalID); + + $testCases = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + if (! is_null($testCases)) { + foreach ($testCases as $tcaseID => $value) { + $path2root = $this->tree_manager->get_path($tcaseID); + if ($tproject_id == $path2root[0]['parent_id']) { + $internalID = $tcaseID; + break; + } + } + } + } + return is_null($my['opt']['output']) ? $internalID : array( + 'id' => $internalID, + 'tproject' => $tproject_info + ); + } + + /* + * function: filterByKeyword + * given a test case id (or an array of test case id) + * and a keyword filter, returns for the test cases given in input + * only which pass the keyword filter criteria. + * + * + * args : + * + * returns: + * + */ + public function filterByKeyword($id, $keyword_id = 0, + $keyword_filter_type = 'OR') + { + $keyword_filter = ''; + $subquery = ''; + + if (is_array($id)) { + $testcase_filter = " AND testcase_id IN (" . implode(',', $id) . ")"; + } else { + $testcase_filter = " AND testcase_id = {$id} "; + } + + if (is_array($keyword_id)) { + $keyword_filter = " AND keyword_id IN (" . implode(',', $keyword_id) . + ")"; + + if ($keyword_filter_type == 'AND') { + $subquery = "AND testcase_id IN (" . + " SELECT MAFALDA.testcase_id FROM ( SELECT COUNT(testcase_id) AS HITS,testcase_id FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']} WHERE keyword_id = K.id {$keyword_filter} - GROUP BY testcase_id ) AS MAFALDA " . - " WHERE MAFALDA.HITS=" . count($keyword_id) . ")"; - - $keyword_filter =''; - } - } - else if( $keyword_id > 0 ) - { - $keyword_filter = " AND keyword_id = {$keyword_id} "; - } - - $map_keywords = null; - $sql = " SELECT testcase_id,keyword_id,keyword + GROUP BY testcase_id ) AS MAFALDA " . + " WHERE MAFALDA.HITS=" . count($keyword_id) . ")"; + + $keyword_filter = ''; + } + } elseif ($keyword_id > 0) { + $keyword_filter = " AND keyword_id = {$keyword_id} "; + } + + $sql = " SELECT testcase_id,keyword_id,keyword FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']} WHERE keyword_id = K.id {$testcase_filter} {$keyword_filter} {$subquery} - ORDER BY keyword ASC "; - - // $map_keywords = $this->db->fetchRowsIntoMap($sql,'testcase_id'); - $map_keywords = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','keyword_id'); - - return($map_keywords); - } //end function - - - - // ------------------------------------------------------------------------ - // Keyword related methods - // ------------------------------------------------------------------------ - /* - function: getKeywords - - args : - - returns: - - */ - function getKeywords($tcID,$versionID,$kwID = null,$opt = null) { - $my['opt'] = array('accessKey' => 'keyword_id', 'fields' => null, - 'orderBy' => null); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $f2g = is_null($my['opt']['fields']) ? - ' TCKW.id AS tckw_link,keyword_id,KW.keyword,KW.notes, - testcase_id,tcversion_id ' : - $my['opt']['fields']; - - $sql = " SELECT {$f2g} + ORDER BY keyword ASC "; + + return $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', 'keyword_id'); + } + + /* + * function: getKeywords + * + * args : + * + * returns: + * + */ + public function getKeywords($tcID, $versionID, $kwID = null, $opt = null) + { + $my['opt'] = array( + 'accessKey' => 'keyword_id', + 'fields' => null, + 'orderBy' => null + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $f2g = is_null($my['opt']['fields']) ? ' TCKW.id AS tckw_link,keyword_id,KW.keyword,KW.notes, + testcase_id,tcversion_id ' : $my['opt']['fields']; + + $sql = " SELECT {$f2g} FROM {$this->tables['testcase_keywords']} TCKW JOIN {$this->tables['keywords']} KW - ON keyword_id = KW.id "; - - $sql .= " WHERE testcase_id = " . intval($tcID) . - " AND tcversion_id=" . intval($versionID); - - if (!is_null($kwID)) { - $sql .= " AND keyword_id = " . intval($kwID); - } - - if (!is_null($my['opt']['orderBy'])) { - $sql .= ' ' . $my['opt']['orderBy']; - } - - switch( $my['opt']['accessKey'] ) { - case 'testcase_id,tcversion_id'; - $items = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','tcversion_id',database::CUMULATIVE); - break; - - default: - $items = $this->db->fetchRowsIntoMap($sql,$my['opt']['accessKey']); - break; - } - - return $items; - } - - /** - * - */ - function getKeywordsByIdCard($idCard,$opt=null) { - return $this->get_keywords_map($idCard['tcase_id'],$idCard['tcversion_id'],$opt); - } - - - - /* - function: get_keywords_map - - args: id: testcase id - version_id - opt: 'orderByClause' => '' -> no order choosen - must be an string with complete clause, - i.e. 'ORDER BY keyword' - - 'output' => null => array[keyword_id] = keyword - 'kwfull' => - array[keyword_id] = array('keyword_id' => value, - 'keyword' => value, - 'notes' => value) - - returns: map with keywords information - - - */ - function get_keywords_map($id,$version_id,$opt=null) { - $my['opt'] = array('orderByClause' => '', 'output' => null); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - - switch($my['opt']['output']) { - case 'kwfull': - $sql = "SELECT TCKW.keyword_id,KW.keyword,KW.notes"; - break; - - default: - $sql = "SELECT TCKW.keyword_id,KW.keyword"; - break; - } - $sql .= " FROM {$this->tables['testcase_keywords']} TCKW, " . - " {$this->tables['keywords']} KW WHERE keyword_id = KW.id "; - - $sql .= " AND TCKW.testcase_id = " . intval($id) . - " AND TCKW.tcversion_id = " . intval($version_id); - - $sql .= $my['opt']['orderByClause']; - - - switch($my['opt']['output']) { - case 'kwfull': - $map_keywords = $this->db->fetchRowsIntoMap($sql,'keyword_id'); - break; - - default: - $map_keywords = $this->db->fetchColumnsIntoMap($sql,'keyword_id','keyword'); - break; - } - - return $map_keywords; - } - - /** - * add keywords without checking if exist. - * - */ - function addKeywords($id,$version_id,$kw_ids,$audit=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $adt = array('on' => self::AUDIT_ON, 'version' => null); - $adt = array_merge($adt, (array)$audit); - - if( count($kw_ids) == 0 ) { - return true; - } - - $safeID = array('tc' => intval($id), 'tcv' => intval($version_id)); - foreach($safeID as $key => $val ) { - if($val <= 0) { - throw new Exception(__METHOD__ . " $key cannot be $val ", 1); - } - } - - // Firts check if records exist - $sql = "/* $debugMsg */ - SELECT keyword_id FROM {$this->tables['testcase_keywords']} - WHERE testcase_id = {$safeID['tc']} - AND tcversion_id = {$safeID['tcv']} - AND keyword_id IN (" . implode(',',$kw_ids) . ")"; - - $kwCheck = $this->db->fetchRowsIntoMap($sql,'keyword_id'); - - $sql = "/* $debugMsg */" . - " INSERT INTO {$this->tables['testcase_keywords']} " . - " (testcase_id,tcversion_id,keyword_id) VALUES "; - - $dummy = array(); - foreach( $kw_ids as $kiwi ) { - if( !isset($kwCheck[$kiwi]) ) { - $dummy[] = "($id,$version_id,$kiwi)"; - } - } - - if( count($dummy) <= 0 ) { - return; - } - - // Go ahead - $sql .= implode(',', $dummy); - $this->db->exec_query($sql); - - // Now AUDIT - if ( $adt['on'] == self::AUDIT_ON ) { - - // Audit Context - $tcPath = $this->getPathName( $id ); - $kwOpt = array('cols' => 'id,keyword', - 'accessKey' => 'id', 'kwSet' => $kw_ids); - $keywordSet = tlKeyword::getSimpleSet($this->db,$kwOpt); - - foreach($keywordSet as $elem ) { - logAuditEvent(TLS("audit_keyword_assigned_tc",$elem['keyword'], - $tcPath,$adt['version']), - "ASSIGN",$version_id,"nodes_hierarchy"); - } - } - - return true; - } - - - /* - function: set's the keywords of the given testcase to the passed keywords - - args : - - returns: - - */ - function setKeywords($id,$version_id,$kw_ids,$audit = self::AUDIT_ON) { - - if( null == $version_id) { - - } - - $result = $this->deleteKeywords($id,$version_id); - if ($result && sizeof((array)$kw_ids)) { - $result = $this->addKeywords($id,$version_id,$kw_ids); - } - return $result; - } - - /** - * - * mappings is only useful when source_id and target_id do not belong - * to same Test Project. - * Because keywords are defined INSIDE a Test Project, - * ID will be different for same keyword - * in a different Test Project. - * - */ - function copyKeywordsTo($source,$dest,$kwMappings,$auditContext=null,$opt=null) { - - $adt = array('on' => self::AUDIT_ON); - if( isset($dest['version']) ) { - $adt['version'] = $dest['version']; - } - $adt = array_merge($adt,(array)$auditContext); - - $what = array('delete' => true); - $what = array_merge($what,(array)$opt); - - // Not sure that this delete is needed (@20180610) - if( $what['delete'] ) { - $this->deleteKeywords($dest['id'],$dest['version_id'],null,$auditContext); - } - - $sourceKW = $this->getKeywords($source['id'],$source['version_id']); - - if( !is_null($sourceKW) ) { - - // build item id list - $keySet = array_keys($sourceKW); - if( null != $kwMappings ) { - foreach($keySet as $itemPos => $itemID) { - if( isset($mappings[$itemID]) ) { - $keySet[$itemPos] = $mappings[$itemID]; - } - } - } - - $this->addKeywords($dest['id'],$dest['version_id'],$keySet,$adt); - } - - return true; - } - - /* - function: - - args : - - returns: - - */ - function deleteKeywords($tcID,$versionID,$kwID = null,$audit=null) { - - $sql = " DELETE FROM {$this->tables['testcase_keywords']} " . - " WHERE testcase_id = " . intval($tcID) . - " AND tcversion_id = " . intval($versionID); - - $adt = array('on' => self::AUDIT_ON); - $adt = array_merge($adt,(array)$audit); - - if (!is_null($kwID)) { - if(is_array($kwID)) { - $sql .= " AND keyword_id IN (" . implode(',',$kwID) . ")"; - $key4log=$kwID; - } - else { - $sql .= " AND keyword_id = {$kwID}"; - $key4log = array($kwID); - } - } - else { - $key4log = array_keys((array)$this->get_keywords_map($tcID,$versionID)); - } - - $result = $this->db->exec_query($sql); - if ($result) { - $tcInfo = $this->tree_manager->get_node_hierarchy_info($tcID); - if ($tcInfo && $key4log) { - foreach($key4log as $key2get) { - - $keyword = tlKeyword::getByID($this->db,$key2get); - if ($keyword && $adt['on']==self::AUDIT_ON) { - logAuditEvent(TLS("audit_keyword_assignment_removed_tc",$keyword->name,$tcInfo['name']), - "ASSIGN",$tcID,"nodes_hierarchy"); - } - } - } - } - - return $result; - } - - - /** - * - */ - function deleteKeywordsByLink($tcID, $tckwLinkID, $audit=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safeTCID = intval($tcID); - - $links = (array)$tckwLinkID; - $inClause = implode(',',$links); - - $sql = " /* $debugMsg */ + ON keyword_id = KW.id "; + + $sql .= " WHERE testcase_id = " . intval($tcID) . " AND tcversion_id=" . + intval($versionID); + + if (! is_null($kwID)) { + $sql .= " AND keyword_id = " . intval($kwID); + } + + if (! is_null($my['opt']['orderBy'])) { + $sql .= ' ' . $my['opt']['orderBy']; + } + + switch ($my['opt']['accessKey']) { + case 'testcase_id,tcversion_id': + $items = $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', + 'tcversion_id', database::CUMULATIVE); + break; + + default: + $items = $this->db->fetchRowsIntoMap($sql, + $my['opt']['accessKey']); + break; + } + + return $items; + } + + /** + */ + public function getKeywordsByIdCard($idCard, $opt = null) + { + return $this->get_keywords_map($idCard['tcase_id'], + $idCard['tcversion_id'], $opt); + } + + /* + * function: get_keywords_map + * + * args: id: testcase id + * version_id + * opt: 'orderByClause' => '' -> no order choosen + * must be an string with complete clause, + * i.e. 'ORDER BY keyword' + * + * 'output' => null => array[keyword_id] = keyword + * 'kwfull' => + * array[keyword_id] = array('keyword_id' => value, + * 'keyword' => value, + * 'notes' => value) + * + * returns: map with keywords information + * + * + */ + public function get_keywords_map($id, $version_id, $opt = null) + { + $my['opt'] = array( + 'orderByClause' => '', + 'output' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + switch ($my['opt']['output']) { + case 'kwfull': + $sql = "SELECT TCKW.keyword_id,KW.keyword,KW.notes"; + break; + + default: + $sql = "SELECT TCKW.keyword_id,KW.keyword"; + break; + } + $sql .= " FROM {$this->tables['testcase_keywords']} TCKW, " . + " {$this->tables['keywords']} KW WHERE keyword_id = KW.id "; + + $sql .= " AND TCKW.testcase_id = " . intval($id) . + " AND TCKW.tcversion_id = " . intval($version_id); + + $sql .= $my['opt']['orderByClause']; + + switch ($my['opt']['output']) { + case 'kwfull': + $map_keywords = $this->db->fetchRowsIntoMap($sql, 'keyword_id'); + break; + + default: + $map_keywords = $this->db->fetchColumnsIntoMap($sql, + 'keyword_id', 'keyword'); + break; + } + + return $map_keywords; + } + + /** + * add keywords without checking if exist. + */ + public function addKeywords($id, $version_id, $kw_ids, $audit = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $adt = array( + 'on' => self::AUDIT_ON, + 'version' => null + ); + $adt = array_merge($adt, (array) $audit); + + if (count($kw_ids) == 0) { + return true; + } + + $safeID = array( + 'tc' => intval($id), + 'tcv' => intval($version_id) + ); + foreach ($safeID as $key => $val) { + if ($val <= 0) { + throw new Exception(__METHOD__ . " $key cannot be $val ", 1); + } + } + + // Firts check if records exist + $sql = "/* $debugMsg */ + SELECT keyword_id FROM {$this->tables['testcase_keywords']} + WHERE testcase_id = {$safeID['tc']} + AND tcversion_id = {$safeID['tcv']} + AND keyword_id IN (" . implode(',', $kw_ids) . ")"; + + $kwCheck = $this->db->fetchRowsIntoMap($sql, 'keyword_id'); + + $sql = "/* $debugMsg */" . + " INSERT INTO {$this->tables['testcase_keywords']} " . + " (testcase_id,tcversion_id,keyword_id) VALUES "; + + $dummy = array(); + foreach ($kw_ids as $kiwi) { + if (! isset($kwCheck[$kiwi])) { + $dummy[] = "($id,$version_id,$kiwi)"; + } + } + + if (empty($dummy)) { + return; + } + + // Go ahead + $sql .= implode(',', $dummy); + $this->db->exec_query($sql); + + // Now AUDIT + if ($adt['on'] == self::AUDIT_ON) { + + // Audit Context + $tcPath = $this->getPathName($id); + $kwOpt = array( + 'cols' => 'id,keyword', + 'accessKey' => 'id', + 'kwSet' => $kw_ids + ); + $keywordSet = tlKeyword::getSimpleSet($this->db, $kwOpt); + + foreach ($keywordSet as $elem) { + logAuditEvent( + TLS("audit_keyword_assigned_tc", $elem['keyword'], $tcPath, + $adt['version']), "ASSIGN", $version_id, + "nodes_hierarchy"); + } + } + + return true; + } + + /* + * function: set's the keywords of the given testcase to the passed keywords + * + * args : + * + * returns: + * + */ + public function setKeywords($id, $version_id, $kw_ids, + $audit = self::AUDIT_ON) + { + $result = $this->deleteKeywords($id, $version_id); + if ($result && count((array) $kw_ids)) { + $result = $this->addKeywords($id, $version_id, $kw_ids); + } + return $result; + } + + /** + * mappings is only useful when source_id and target_id do not belong + * to same Test Project. + * Because keywords are defined INSIDE a Test Project, + * ID will be different for same keyword + * in a different Test Project. + */ + private function copyKeywordsTo($source, $dest, $kwMappings, + $auditContext = null, $opt = null) + { + $adt = array( + 'on' => self::AUDIT_ON + ); + if (isset($dest['version'])) { + $adt['version'] = $dest['version']; + } + $adt = array_merge($adt, (array) $auditContext); + + $what = array( + 'delete' => true + ); + $what = array_merge($what, (array) $opt); + + // Not sure that this delete is needed (@20180610) + if ($what['delete']) { + $this->deleteKeywords($dest['id'], $dest['version_id'], null, + $auditContext); + } + + $sourceKW = $this->getKeywords($source['id'], $source['version_id']); + + if (! is_null($sourceKW)) { + + // build item id list + $keySet = array_keys($sourceKW); + if (null != $kwMappings) { + foreach ($keySet as $itemPos => $itemID) { + if (isset($mappings[$itemID])) { + $keySet[$itemPos] = $mappings[$itemID]; + } + } + } + + $this->addKeywords($dest['id'], $dest['version_id'], $keySet, $adt); + } + + return true; + } + + /* + * function: + * + * args : + * + * returns: + * + */ + public function deleteKeywords($tcID, $versionID, $kwID = null, + $audit = null) + { + $sql = " DELETE FROM {$this->tables['testcase_keywords']} " . + " WHERE testcase_id = " . intval($tcID) . " AND tcversion_id = " . + intval($versionID); + + $adt = array( + 'on' => self::AUDIT_ON + ); + $adt = array_merge($adt, (array) $audit); + + if (! is_null($kwID)) { + if (is_array($kwID)) { + $sql .= " AND keyword_id IN (" . implode(',', $kwID) . ")"; + $key4log = $kwID; + } else { + $sql .= " AND keyword_id = {$kwID}"; + $key4log = array( + $kwID + ); + } + } else { + $key4log = array_keys( + (array) $this->get_keywords_map($tcID, $versionID)); + } + + $result = $this->db->exec_query($sql); + if ($result) { + $tcInfo = $this->tree_manager->get_node_hierarchy_info($tcID); + if ($tcInfo && $key4log) { + foreach ($key4log as $key2get) { + + $keyword = tlKeyword::getByID($this->db, $key2get); + if ($keyword && $adt['on'] == self::AUDIT_ON) { + logAuditEvent( + TLS("audit_keyword_assignment_removed_tc", + $keyword->name, $tcInfo['name']), "ASSIGN", + $tcID, "nodes_hierarchy"); + } + } + } + } + + return $result; + } + + /** + */ + public function deleteKeywordsByLink($tcID, $tckwLinkID, $audit = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $safeTCID = intval($tcID); + + $links = (array) $tckwLinkID; + $inClause = implode(',', $links); + + $sql = " /* $debugMsg */ SELECT TCKW.tcversion_id, TCKW.keyword_id FROM {$this->tables['testcase_keywords']} TCKW WHERE TCKW.testcase_id = {$safeTCID} - AND TCKW.id IN ($inClause) "; - - - $rs = $this->db->get_recordset($sql); - - foreach($rs as $link) { - $this->deleteKeywords($safeTCID, $link['tcversion_id'], $link['keyword_id'],$audit); - } - - } - - - /** - * - */ - function getKeywordsAllTCVersions($id,$opt=null) { - $my['opt'] = array('orderBy' => null); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $f2g = ' keyword_id,KW.keyword,KW.notes,' . - ' testcase_id,tcversion_id '; - - $sql = " SELECT {$f2g} + AND TCKW.id IN ($inClause) "; + + $rs = $this->db->get_recordset($sql); + + foreach ($rs as $link) { + $this->deleteKeywords($safeTCID, $link['tcversion_id'], + $link['keyword_id'], $audit); + } + } + + /** + */ + private function getKeywordsAllTCVersions($id, $opt = null) + { + $my['opt'] = array( + 'orderBy' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $f2g = ' keyword_id,KW.keyword,KW.notes,' . ' testcase_id,tcversion_id '; + + $sql = " SELECT {$f2g} FROM {$this->tables['testcase_keywords']} TCKW JOIN {$this->tables['keywords']} KW - ON keyword_id = KW.id "; - - $sql .= " WHERE testcase_id = " . intval($id); - - if (!is_null($my['opt']['orderBy'])) - { - $sql .= ' ' . $my['opt']['orderBy']; - } - - $items = $this->db->fetchMapRowsIntoMap($sql, - 'testcase_id','tcversion_id',database::CUMULATIVE); - return $items; - } - - - // ------------------------------------------------------------------------------- - // END Keyword related methods - // ------------------------------------------------------------------------------- - - /* - function: get_executions - get information about all executions for a testcase version, - on a testplan, platform, build. - Execution results are ordered by execution timestamp. - - Is possible to filter certain executions - Is possible to choose Ascending/Descending order of results. (order by exec timestamp). - - @used-by execSetResults.php - - args : id: testcase (node id) - can be single value or array. - version_id: tcversion id (node id) - can be single value or array. - tplan_id: testplan id - build_id: if null -> do not filter by build_id - platform_id: if null -> do not filter by platform_id - options: default null, map with options. - [exec_id_order] default: 'DESC' - range: ASC,DESC - [exec_to_exclude]: default: null -> no filter - can be single value or array, this exec id will be EXCLUDED. - - - returns: map - key: tcversion id - value: array where every element is a map with following keys - - name: testcase name - testcase_id - id: tcversion_id - version - summary: testcase spec. summary - steps: testcase spec. steps - expected_results: testcase spec. expected results - execution_type: see const.inc.php TESTCASE_EXECUTION_TYPE_ constants - importance - author_id: tcversion author - creation_ts: timestamp of creation - updater_id: last updater of specification - modification_ts: - active: tcversion active status - is_open: tcversion open status - tester_login - tester_first_name - tester_last_name - tester_id - execution_id - status: execution status - execution_notes - execution_ts - execution_run_type: see const.inc.php TESTCASE_EXECUTION_TYPE_ constants - build_id - build_name - build_is_active - build_is_open - platform_id - platform_name - - */ - function get_executions($id,$version_id,$tplan_id,$build_id,$platform_id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('exec_id_order' => 'DESC', 'exec_to_exclude' => null); - $my['options'] = array_merge($my['options'], (array)$options); - - $filterKeys = array('build_id','platform_id'); - foreach($filterKeys as $key) - { - $filterBy[$key] = ''; - if( !is_null($$key) ) - { - $itemSet = implode(',', (array)$$key); - $filterBy[$key] = " AND e.{$key} IN ({$itemSet}) "; - } - } - - // -------------------------------------------------------------------- - if( is_array($id) ) - { - $tcid_list = implode(",",$id); - $where_clause = " WHERE NHA.parent_id IN ({$tcid_list}) "; - } - else - { - $where_clause = " WHERE NHA.parent_id = {$id} "; - } - - if( is_array($version_id) ) - { - $versionid_list = implode(",",$version_id); - $where_clause .= " AND tcversions.id IN ({$versionid_list}) "; - } - else - { - if($version_id != self::ALL_VERSIONS) - { - $where_clause .= " AND tcversions.id = {$version_id} "; - } - } - - if( !is_null($my['options']['exec_to_exclude']) ) - { - - if( is_array($my['options']['exec_to_exclude'])) - { - if(count($my['options']['exec_to_exclude']) > 0 ) - { - $exec_id_list = implode(",",$my['options']['exec_to_exclude']); - $where_clause .= " AND e.id NOT IN ({$exec_id_list}) "; - } - } - else - { - $where_clause .= " AND e.id <> {$exec_id_list} "; - } - } - // -------------------------------------------------------------------- - // 20090517 - to manage deleted users i need to change: - // users.id AS tester_id => e.tester_id AS tester_id - // 20090214 - franciscom - e.execution_type -> e.execution_run_type - // - $sql="/* $debugMsg */ SELECT NHB.name,NHA.parent_id AS testcase_id, tcversions.*, + ON keyword_id = KW.id "; + + $sql .= " WHERE testcase_id = " . intval($id); + + if (! is_null($my['opt']['orderBy'])) { + $sql .= ' ' . $my['opt']['orderBy']; + } + + return $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', + 'tcversion_id', database::CUMULATIVE); + } + + /* + * function: get_executions + * get information about all executions for a testcase version, + * on a testplan, platform, build. + * Execution results are ordered by execution timestamp. + * + * Is possible to filter certain executions + * Is possible to choose Ascending/Descending order of results. (order by exec timestamp). + * + * @used-by execSetResults.php + * + * args : id: testcase (node id) - can be single value or array. + * version_id: tcversion id (node id) - can be single value or array. + * tplan_id: testplan id + * build_id: if null -> do not filter by build_id + * platform_id: if null -> do not filter by platform_id + * options: default null, map with options. + * [exec_id_order] default: 'DESC' - range: ASC,DESC + * [exec_to_exclude]: default: null -> no filter + * can be single value or array, this exec id will be EXCLUDED. + * + * + * returns: map + * key: tcversion id + * value: array where every element is a map with following keys + * + * name: testcase name + * testcase_id + * id: tcversion_id + * version + * summary: testcase spec. summary + * steps: testcase spec. steps + * expected_results: testcase spec. expected results + * execution_type: see const.inc.php TESTCASE_EXECUTION_TYPE_ constants + * importance + * author_id: tcversion author + * creation_ts: timestamp of creation + * updater_id: last updater of specification + * modification_ts: + * active: tcversion active status + * is_open: tcversion open status + * tester_login + * tester_first_name + * tester_last_name + * tester_id + * execution_id + * status: execution status + * execution_notes + * execution_ts + * execution_run_type: see const.inc.php TESTCASE_EXECUTION_TYPE_ constants + * build_id + * build_name + * build_is_active + * build_is_open + * platform_id + * platform_name + * + */ + public function get_executions($id, $version_id, $tplan_id, $build_id, + $platform_id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['options'] = array( + 'exec_id_order' => 'DESC', + 'exec_to_exclude' => null + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $filterKeys = array( + 'build_id', + 'platform_id' + ); + foreach ($filterKeys as $key) { + $filterBy[$key] = ''; + if (! is_null($$key)) { + $itemSet = implode(',', (array) $$key); + $filterBy[$key] = " AND e.{$key} IN ({$itemSet}) "; + } + } + + if (is_array($id)) { + $tcid_list = implode(",", $id); + $where_clause = " WHERE NHA.parent_id IN ({$tcid_list}) "; + } else { + $where_clause = " WHERE NHA.parent_id = {$id} "; + } + + if (is_array($version_id)) { + $versionid_list = implode(",", $version_id); + $where_clause .= " AND tcversions.id IN ({$versionid_list}) "; + } else { + if ($version_id != self::ALL_VERSIONS) { + $where_clause .= " AND tcversions.id = {$version_id} "; + } + } + + if (! is_null($my['options']['exec_to_exclude'])) { + + if (is_array($my['options']['exec_to_exclude'])) { + if (! empty($my['options']['exec_to_exclude'])) { + $exec_id_list = implode(",", + $my['options']['exec_to_exclude']); + $where_clause .= " AND e.id NOT IN ({$exec_id_list}) "; + } + } else { + $where_clause .= " AND e.id <> {$exec_id_list} "; + } + } + // 20090517 - to manage deleted users i need to change: + // users.id AS tester_id => e.tester_id AS tester_id + // 20090214 - franciscom - e.execution_type -> e.execution_run_type + $sql = "/* $debugMsg */ SELECT NHB.name,NHA.parent_id AS testcase_id, tcversions.*, users.login AS tester_login, users.first AS tester_first_name, users.last AS tester_last_name, @@ -3705,5922 +4038,6075 @@ function get_executions($id,$version_id,$tplan_id,$build_id,$platform_id,$option LEFT OUTER JOIN {$this->tables['users']} users ON users.id = e.tester_id LEFT OUTER JOIN {$this->tables['platforms']} p ON p.id = e.platform_id $where_clause - ORDER BY NHA.node_order ASC, NHA.parent_id ASC, execution_id {$my['options']['exec_id_order']}"; - - - $recordset = $this->db->fetchArrayRowsIntoMap($sql,'id'); - return($recordset ? $recordset : null); - } - - - /* - function: get_last_execution - - args : - - - returns: map: - key: tcversions.id - value: map with following keys: - execution_id - status: execution status - execution_type: see const.inc.php TESTCASE_EXECUTION_TYPE_ constants - name: testcase name - testcase_id - tsuite_id: parent testsuite of testcase (node id) - id: tcversion id (node id) - version - summary: testcase spec. summary - steps: testcase spec. steps - expected_results: testcase spec. expected results - execution_type: type of execution desired - importance - author_id: tcversion author - creation_ts: timestamp of creation - updater_id: last updater of specification. - modification_ts - active: tcversion active status - is_open: tcversion open status - tester_login - tester_first_name - tester_last_name - tester_id - execution_notes - execution_ts - execution_run_type: how the execution was really done - build_id - build_name - build_is_active - build_is_open - - */ - function get_last_execution($id,$version_id,$tplan_id,$build_id,$platform_id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $resultsCfg = config_get('results'); - $status_not_run = $resultsCfg['status_code']['not_run']; - - $filterKeys = array('build_id','platform_id'); - foreach($filterKeys as $key) { - $filterBy[$key] = ''; - if( !is_null($$key) ) { - $itemSet = implode(',', (array)$$key); - $filterBy[$key] = " AND e.{$key} IN ({$itemSet}) "; - } - } - - $where_clause_1 = ''; - $where_clause_2 = ''; - $add_columns=''; - $add_groupby=''; - $cumulativeMode=0; - $group_by = ''; - - // getNoExecutions: 1 -> if testcase/version_id has not been executed return anyway - // standard return structure. - // 0 -> default - // - // groupByBuild: 0 -> default, get last execution on ANY BUILD, then for a testcase/version_id - // only a record will be present on return struture. - // GROUP BY must be done ONLY BY tcversion_id - // - // 1 -> get last execution on EACH BUILD. - // GROUP BY must be done BY tcversion_id,build_id - // - $localOptions=array('getNoExecutions' => 0, 'groupByBuild' => 0, 'getSteps' => 1, - 'getStepsExecInfo' => 0, 'output' => 'std'); - if(!is_null($options) && is_array($options)) { - $localOptions=array_merge($localOptions,$options); - } - - if( is_array($id) ) { - $tcid_list = implode(",",$id); - $where_clause = " WHERE NHA.parent_id IN ({$tcid_list}) "; - } else { - $where_clause = " WHERE NHA.parent_id = {$id} "; - } - - if( is_array($version_id) ) { - $versionid_list = implode(",",$version_id); - $where_clause_1 = $where_clause . " AND NHA.id IN ({$versionid_list}) "; - $where_clause_2 = $where_clause . " AND tcversions.id IN ({$versionid_list}) "; - } else { - if($version_id != self::ALL_VERSIONS) { - $where_clause_1 = $where_clause . " AND NHA.id = {$version_id} "; - $where_clause_2 = $where_clause . " AND tcversions.id = {$version_id} "; - } - } - - // This logic (is mine - franciscom) must be detailed better!!!!! - $group_by = ' GROUP BY tcversion_id '; - $add_fields = ', e.tcversion_id AS tcversion_id'; - if( $localOptions['groupByBuild'] ) { - $add_fields .= ', e.build_id'; - $group_by .= ', e.build_id'; - $cumulativeMode = 1; - - // Hummm!!! I do not understand why this can be needed - $where_clause_1 = $where_clause; - $where_clause_2 = $where_clause; - } - - // we may be need to remove tcversion filter ($set_group_by==false) - // $add_field = $set_group_by ? ', e.tcversion_id AS tcversion_id' : ''; - // $add_field = $localOptions['groupByBuild'] ? '' : ', e.tcversion_id AS tcversion_id'; - // $where_clause_1 = $localOptions['groupByBuild'] ? $where_clause : $where_clause_1; - // $where_clause_2 = $localOptions['groupByBuild'] ? $where_clause : $where_clause_2; - - // get list of max exec id, to be used filter in next query - // Here we can get: - // a) one record for each tcversion_id (ignoring build) - // b) one record for each tcversion_id,build - // - - // 20101212 - franciscom - may be not the best logic but ... - $where_clause_1 = ($where_clause_1 == '') ? $where_clause : $where_clause_1; - $where_clause_2 = ($where_clause_2 == '') ? $where_clause : $where_clause_2; - - $sql="/* $debugMsg */ " . - " SELECT COALESCE(MAX(e.id),0) AS execution_id {$add_fields}" . - " FROM {$this->tables['nodes_hierarchy']} NHA " . - " JOIN {$this->tables['executions']} e ON NHA.id = e.tcversion_id AND e.testplan_id = {$tplan_id} " . - " {$filterBy['build_id']} {$filterBy['platform_id']}" . - " AND e.status IS NOT NULL " . - " $where_clause_1 {$group_by}"; - - $recordset = $this->db->fetchColumnsIntoMap($sql,'execution_id','tcversion_id'); - $and_exec_id=''; - if( !is_null($recordset) && count($recordset) > 0) { - $the_list = implode(",", array_keys($recordset)); - if($the_list != '') { - if( count($recordset) > 1 ) { - $and_exec_id = " AND e.id IN ($the_list) "; - } else { - $and_exec_id = " AND e.id = $the_list "; - } - } - } - - $executions_join = - " JOIN {$this->tables['executions']} e ON NHA.id = e.tcversion_id " . - " AND e.testplan_id = {$tplan_id} {$and_exec_id} {$filterBy['build_id']} " . - " {$filterBy['platform_id']} "; - - if( $localOptions['getNoExecutions'] ) { - $executions_join = " LEFT OUTER " . $executions_join; - } else { - // @TODO understand if this condition is really needed - 20090716 - franciscom - $executions_join .= " AND e.status IS NOT NULL "; - } - - // - switch ($localOptions['output']) { - case 'timestamp': - $sql= "/* $debugMsg */ SELECT e.id AS execution_id, " . - " COALESCE(e.status,'{$status_not_run}') AS status, " . - " e.execution_ts, e.build_id,e.tcversion_number," . - " FROM {$this->tables['nodes_hierarchy']} NHA" . - " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id" . - " JOIN {$this->tables['tcversions']} tcversions ON NHA.id = tcversions.id" . - " {$executions_join}" . - " $where_clause_2" . - " ORDER BY NHB.parent_id ASC, NHA.parent_id ASC, execution_id DESC"; - break; - - case 'std': - default: - $sql= "/* $debugMsg */ SELECT e.id AS execution_id, " . - " COALESCE(e.status,'{$status_not_run}') AS status, " . - " e.execution_type AS execution_run_type,e.execution_duration, " . - " NHB.name,NHA.parent_id AS testcase_id, NHB.parent_id AS tsuite_id," . - " tcversions.id,tcversions.tc_external_id,tcversions.version,tcversions.summary," . - " tcversions.preconditions," . - " tcversions.importance,tcversions.author_id," . - " tcversions.creation_ts,tcversions.updater_id,tcversions.modification_ts,tcversions.active," . - " tcversions.is_open,tcversions.execution_type," . - " tcversions.estimated_exec_duration,tcversions.status AS wkfstatus," . - " users.login AS tester_login,users.first AS tester_first_name," . - " users.last AS tester_last_name, e.tester_id AS tester_id," . - " e.notes AS execution_notes, e.execution_ts, e.build_id,e.tcversion_number," . - " builds.name AS build_name, builds.active AS build_is_active, builds.is_open AS build_is_open," . - " e.platform_id,p.name AS platform_name" . - " FROM {$this->tables['nodes_hierarchy']} NHA" . - " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id" . - " JOIN {$this->tables['tcversions']} tcversions ON NHA.id = tcversions.id" . - " {$executions_join}" . - " LEFT OUTER JOIN {$this->tables['builds']} builds ON builds.id = e.build_id" . - " AND builds.testplan_id = {$tplan_id}" . - " LEFT OUTER JOIN {$this->tables['users']} users ON users.id = e.tester_id " . - " LEFT OUTER JOIN {$this->tables['platforms']} p ON p.id = e.platform_id" . - " $where_clause_2" . - " ORDER BY NHB.parent_id ASC, NHA.node_order ASC, NHA.parent_id ASC, execution_id DESC"; - break; - } - - $recordset = $this->db->fetchRowsIntoMap($sql,'id',$cumulativeMode); - - // Multiple Test Case Steps Feature - if( !is_null($recordset) && $localOptions['getSteps'] ) { - $xx = null; - if( $localOptions['getStepsExecInfo'] && - ($this->cfg->execution->steps_exec_notes_default == 'latest' || - $this->cfg->execution->steps_exec_status_default == 'latest') - ) { - $tg = current($recordset); - $xx = $this->getStepsExecInfo($tg['execution_id']); - } - - $itemSet = array_keys($recordset); - foreach( $itemSet as $sdx) { - $step_set = $this->get_steps($recordset[$sdx]['id']); - if($localOptions['getStepsExecInfo']) { - if(!is_null($step_set)) { - $key_set = array_keys($step_set); - foreach($key_set as $kyx) { - $step_set[$kyx]['execution_notes'] = ''; - $step_set[$kyx]['execution_status'] = ''; - - if( isset($xx[$step_set[$kyx]['id']]) ) { - if($this->cfg->execution->steps_exec_notes_default == 'latest') { - $step_set[$kyx]['execution_notes'] = - $xx[$step_set[$kyx]['id']]['notes']; - } - - if($this->cfg->execution->steps_exec_status_default == 'latest') { - $step_set[$kyx]['execution_status'] = - $xx[$step_set[$kyx]['id']]['status']; - } - } - } - } - } - $recordset[$sdx]['steps'] = $step_set; - } - - } - - // ghost Test Case processing in summary & preconditions - if( !is_array($id) ) { - if(!is_null($recordset)) { - $key2loop = array_keys($recordset); - foreach( $key2loop as $accessKey) { - $this->renderGhost($recordset[$accessKey]); - $this->renderVariables($recordset[$accessKey]); - $this->renderSpecialTSuiteKeywords($recordset[$accessKey]); - $this->renderImageAttachments($id,$recordset[$accessKey]); - - // render exec variables only if we have just one build - if( intval($build_id) > 0 && intval($tplan_id) >0 ) { - $context = array('tplan_id' => $tplan_id, 'build_id' => $build_id); - $this->renderBuildExecVars($context,$recordset[$accessKey]); - } - } - reset($recordset); - } - } - - return($recordset ? $recordset : null); - } - - - - /* - function: exportTestCaseDataToXML - - args : - - $tcversion_id: can be testcase::LATEST_VERSION - - returns: - - - */ - function exportTestCaseDataToXML($tcase_id,$tcversion_id, - $tproject_id=null,$bNoXMLHeader = false,$optExport = array()) { - - static $reqMgr; - static $keywordMgr; - static $cfieldMgr; - if( is_null($reqMgr) ) { - $reqMgr = new requirement_mgr($this->db); - $keywordMgr = new tlKeyword(); - $cfieldMgr = new cfield_mgr($this->db); - } - - // Useful when you need to get info but do not have tcase id - $tcase_id = intval((int)($tcase_id)); - $tcversion_id = intval((int)($tcversion_id)); - if( $tcase_id <= 0 && $tcversion_id > 0) { - $info = $this->tree_manager->get_node_hierarchy_info($tcversion_id); - $tcase_id = $info['parent_id']; - } - - - $opt = array('getPrefix' => false); - if(!isset($optExport['EXTERNALID']) || $optExport['EXTERNALID']) { - $opt = array('getPrefix' => (isset($optExport['ADDPREFIX']) && $optExport['ADDPREFIX'])); - } - $tc_data = $this->get_by_id($tcase_id,$tcversion_id,null,$opt); - $testCaseVersionID = $tc_data[0]['id']; - - if (!$tproject_id) { - $tproject_id = $this->getTestProjectFromTestCase($tcase_id); - } - - if (isset($optExport['CFIELDS']) && $optExport['CFIELDS']) { - $cfMap = $this->get_linked_cfields_at_design($tcase_id,$testCaseVersionID,null,null,$tproject_id); - - // ||yyy||-> tags, {{xxx}} -> attribute - // tags and attributes receive different treatment on exportDataToXML() - // - // each UPPER CASE word in this map KEY, MUST HAVE AN OCCURENCE on $elemTpl - // value is a key inside $tc_data[0] - // - if( !is_null($cfMap) && count($cfMap) > 0 ) { - $tc_data[0]['xmlcustomfields'] = $cfieldMgr->exportValueAsXML($cfMap); - } - } - - if (isset($optExport['KEYWORDS']) && $optExport['KEYWORDS']) { - // 20180610 - Will export Only for latest version? - $keywords = $this->getKeywords($tcase_id,$testCaseVersionID); - if(!is_null($keywords)) { - $xmlKW = "" . $keywordMgr->toXMLString($keywords,true) . - ""; - $tc_data[0]['xmlkeywords'] = $xmlKW; - } - } - - if (isset($optExport['REQS']) && $optExport['REQS']) { - - // $requirements = $reqMgr->get_all_for_tcase($tcase_id); - // Need to get only for test case version - - $req4version = $reqMgr->getGoodForTCVersion($testCaseVersionID); - - if( !is_null($req4version) && count($req4version) > 0 ) { - $tc_data[0]['xmlrequirements'] = - exportDataToXML($req4version,$this->XMLCfg->req->root, - $this->XMLCfg->req->elemTPL,$this->XMLCfg->req->decode,true); - } - } - - if (isset($optExport['ATTACHMENTS']) && $optExport['ATTACHMENTS']) { - - $attachments=null; - - $library = $this->attachmentRepository->getAttachmentInfosFor($tcversion_id,$this->attachmentTableName,'id'); - - // get all attachments content and encode it in base64 - if ($library) { - foreach ($library as $file) { - $aID = $file["id"]; - $content = $this->attachmentRepository->getAttachmentContent($aID, $file); - - if ($content != null) { - $attachments[$aID]["id"] = $aID; - $attachments[$aID]["name"] = $file["file_name"]; - $attachments[$aID]["file_type"] = $file["file_type"]; - $attachments[$aID]["file_size"] = $file["file_size"]; - $attachments[$aID]["title"] = $file["title"]; - $attachments[$aID]["date_added"] = $file["date_added"]; - $attachments[$aID]["content"] = base64_encode($content); - } - } - } - - if( !is_null($attachments) && count($attachments) > 0 ) { - $tc_data[0]['xmlattachments'] = - exportDataToXML($attachments,$this->XMLCfg->att->root, - $this->XMLCfg->att->elemTPL,$this->XMLCfg->att->decode,true); - } - } - - // ----------------------------------------------------------------------------- - if(!isset($optExport['TCSTEPS']) || $optExport['TCSTEPS']) { - $stepRootElem = "{{XMLCODE}}"; - $stepTemplate = "\n" . '' . "\n" . - "\t\n" . - "\t\n" . - "\t\n" . - "\t\n" . - "\n"; - $stepInfo = array("||STEP_NUMBER||" => "step_number", "||ACTIONS||" => "actions", - "||EXPECTEDRESULTS||" => "expected_results","||EXECUTIONTYPE||" => "execution_type" ); - - $stepSet = $tc_data[0]['steps']; - $xmlsteps = exportDataToXML($stepSet,$stepRootElem,$stepTemplate,$stepInfo,true); - $tc_data[0]['xmlsteps'] = $xmlsteps; - } - // -------------------------------------------------------------------------------- - - - $tc_data[0]['xmlrelations'] = null; - $addElemTpl = ''; - - // When exporting JUST a test case, exporting relations can be used - // as documentation. - // When exporting a Test Suite, format can be different as has been done - // with requirements. - // While ideas become clear , i prefer to add this option for testing - if( isset($optExport['RELATIONS']) && $optExport['RELATIONS'] ) { - $xmlRel = null; - $addElemTpl .= "||RELATIONS||"; - $relSet = $this->getRelations($tcase_id); - if($relSet['num_relations'] > 0 ) { - foreach($relSet['relations'] as $rk => $rv) { - $xmlRel .= $this->exportRelationToXML($rv,$relSet['item']); - } - $tc_data[0]['xmlrelations'] = $xmlRel; - } - } - - $rootElem = "{{XMLCODE}}"; - if (isset($optExport['ROOTELEM'])) { - $rootElem = $optExport['ROOTELEM']; - } - $elemTpl = "\n".'' . "\n" . - "\t\n"; - - - // Export the Execution Order in a TestPlan for each testcase - if(isset($optExport['EXEC_ORDER'])) { - $elemTpl .= "\t\n"; - - $tc_data[0]['exec_order'] = $optExport['EXEC_ORDER']; - } - - // Export assigned_users into "Export Test Plan" XML content - // table with all users assigned to an execution - if(isset($optExport['ASSIGNED_USER'])) { - $elemTpl .= "\t\n"; - foreach ($optExport['ASSIGNED_USER'] as $key => $username){ - $elemTpl .= "\t\t\n"; - } - $elemTpl .= "\t\n"; - } - - if(!isset($optExport['EXTERNALID']) || $optExport['EXTERNALID']) { - $elemTpl .= "\t\n"; - } - - if(!isset($optExport['ADDPREFIX']) || $optExport['ADDPREFIX']) { - $elemTpl .= "\t\n"; - } - - $optElem = ''; - if( !isset($optExport['TCSUMMARY']) || $optExport['TCSUMMARY'] ) { - $optElem .= "\t\n"; - } - if( !isset($optExport['TCPRECONDITIONS']) || $optExport['TCPRECONDITIONS'] ) { - $optElem .= "\t\n"; - } - - $elemTpl .= "\t\n" . - $optElem . - "\t\n" . - "\t\n" . - "\t||ESTIMATED_EXEC_DURATION||\n" . - "\t||STATUS||\n" . - "\t||ISOPEN||\n" . - "\t||ACTIVE||\n" . - "||STEPS||\n" . - "||KEYWORDS||||CUSTOMFIELDS||||REQUIREMENTS||||ATTACHMENTS||{$addElemTpl}\n"; - - - // ||yyy||-> tags, {{xxx}} -> attribute - // tags and attributes receive different treatment on exportDataToXML() - // - // each UPPER CASE word in this map KEY, MUST HAVE AN OCCURENCE on $elemTpl - // value is a key inside $tc_data[0] - // - $info = array("{{TESTCASE_ID}}" => "testcase_id", - "{{NAME}}" => "name", - "||NODE_ORDER||" => "node_order", - "||EXEC_ORDER||" => "exec_order", - "||EXTERNALID||" => "tc_external_id", - "||FULLEXTERNALID||" => "fullExternalID", - "||VERSION||" => "version", - "||SUMMARY||" => "summary", - "||PRECONDITIONS||" => "preconditions", - "||EXECUTIONTYPE||" => "execution_type", - "||IMPORTANCE||" => "importance", - "||ESTIMATED_EXEC_DURATION||" => "estimated_exec_duration", - "||STATUS||" => "status", - "||ISOPEN||" => "is_open", - "||ACTIVE||" => "active", - "||STEPS||" => "xmlsteps", - "||KEYWORDS||" => "xmlkeywords", - "||CUSTOMFIELDS||" => "xmlcustomfields", - "||REQUIREMENTS||" => "xmlrequirements", - "||ATTACHMENTS||" => "xmlattachments", - "||RELATIONS||" => "xmlrelations"); - - - $xmlTC = exportDataToXML($tc_data,$rootElem,$elemTpl,$info,$bNoXMLHeader); - return $xmlTC; - } - - - /* - function: get_version_exec_assignment - get information about user that has been assigned - test case version for execution on a testplan - - args : tcversion_id: test case version id - tplan_id - - - - returns: map - key: tcversion_id - value: map with following keys: - tcversion_id - feature_id: identifies row on table testplan_tcversions. - - - user_id: user that has reponsibility to execute this tcversion_id. - null/empty string is nodoby has been assigned - - type type of assignment. - 1 -> testcase_execution. - See assignment_types tables for updated information - about other types of assignemt available. - - status assignment status - See assignment_status tables for updated information. - 1 -> open - 2 -> closed - 3 -> completed - 4 -> todo_urgent - 5 -> todo - - assigner_id: who has assigned execution to user_id. - - - - */ - function get_version_exec_assignment($tcversion_id, $tplan_id, $build_id) - { - $sql = "SELECT T.tcversion_id AS tcversion_id,T.id AS feature_id,T.platform_id, " . - " UA.user_id,UA.type,UA.status,UA.assigner_id ". - " FROM {$this->tables['testplan_tcversions']} T " . - " LEFT OUTER JOIN {$this->tables['user_assignments']} UA ON UA.feature_id = T.id " . - " WHERE T.testplan_id={$tplan_id} AND UA.build_id = {$build_id} " . - " AND T.tcversion_id = {$tcversion_id} " . - " AND (UA.type=" . $this->assignment_types['testcase_execution']['id'] . - " OR UA.type IS NULL) "; - - - // $recordset = $this->db->fetchRowsIntoMap($sql,'tcversion_id'); - $recordset = $this->db->fetchMapRowsIntoMap($sql,'tcversion_id','platform_id',database::CUMULATIVE); - - return $recordset; - } - - - /** - * get_assigned_to_user() - * Given a user and a tesplan id, get all test case version id linked to - * test plan, that has been assigned for execution to user. - * - * @param int user_id - * - * @param mixed tproject_id list of test project id to search. - * int or array - * - * @param array [tplan_id] list of test plan id to search. - * null => all test plans - * - * @param map [options] mode='full_path' - * testcase name full path will be returned - * Only available when acces_keys ='testplan_testcase' - * - * access_keys - * possible values: 'testplan_testcase','testcase_testplan' - * changes access key in result map of maps. - * if not defined or null -> 'testplan_testcase' - * - * @param map [filters] 'tplan_status' => 'active','inactive','all' - * - * - * @return map key: (test plan id or test case id depending on options->access_keys, - * default is test plan). - * - * value: map key: (test case id or test plan id depending on options->access_keys, - * default is test case). - * value: - * - * @internal revision - */ - function get_assigned_to_user($user_id,$tproject_id,$tplan_id=null,$options=null, $filters=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('mode' => null, 'order_by' => '', - 'access_keys' => 'testplan_testcase'); - $my['opt'] = array_merge($my['opt'],(array)$options); - - $my['filters'] = array( 'tplan_status' => 'all'); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - // to load assignments for all users OR one given user - $user_sql = ($user_id != TL_USER_ANYBODY) ? " AND UA.user_id = {$user_id} " : ""; - - $filters = ""; - $access_key=array('testplan_id','testcase_id'); - - $sql="/* $debugMsg */ SELECT TPROJ.id as testproject_id,TPTCV.testplan_id,TPTCV.tcversion_id, " . - " TCV.version,TCV.tc_external_id, NHTC.id AS testcase_id, NHTC.name, TPROJ.prefix, " . - " UA.creation_ts ,UA.deadline_ts, UA.user_id as user_id, " . - " COALESCE(PLAT.name,'') AS platform_name, COALESCE(PLAT.id,0) AS platform_id, " . - " (TPTCV.urgency * TCV.importance) AS priority, BUILDS.name as build_name, " . - " BUILDS.id as build_id " . - " FROM {$this->tables['user_assignments']} UA " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.id = UA.feature_id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id=TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TCV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN ON NHTPLAN.id=TPTCV.testplan_id " . - " JOIN {$this->tables['testprojects']} TPROJ ON TPROJ.id = NHTPLAN.parent_id " . - " JOIN {$this->tables['testplans']} TPLAN ON TPLAN.id = TPTCV.testplan_id " . - " JOIN {$this->tables['builds']} BUILDS ON BUILDS.id = UA.build_id " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . - " WHERE UA.type={$this->assignment_types['testcase_execution']['id']} " . - " {$user_sql} " . - " AND TPROJ.id IN (" . implode(',', array($tproject_id)) .") " ; - - if( !is_null($tplan_id) ) - { - $filters .= " AND TPTCV.testplan_id IN (" . implode(',',$tplan_id) . ") "; - } - - if (isset($my['filters']['build_id'])) - { - $filters .= " AND UA.build_id = {$my['filters']['build_id']} "; - } - - switch($my['filters']['tplan_status']) - { - case 'all': - break; - - case 'active': - $filters .= " AND TPLAN.active = 1 "; - break; - - case 'inactive': - $filters .= " AND TPLAN.active = 0 "; - break; - } - - if(isset($my['filters']['build_status'])) - { - switch($my['filters']['build_status']) - { - case 'open': - $filters .= " AND BUILDS.is_open = 1 "; - break; - - case 'closed': - $filters .= " AND BUILDS.is_open = 0 "; - break; - - case 'all': - default: - break; - } - } - - $sql .= $filters; - - if( isset($my['opt']['access_keys']) ) - { - switch($my['opt']['access_keys']) - { - case 'testplan_testcase': - break; - - case 'testcase_testplan': - $access_key=array('testcase_id','testplan_id'); - break; - } - } - - $sql .= $my['opt']['order_by']; - - $rs = $this->db->fetchMapRowsIntoMap($sql,$access_key[0],$access_key[1],database::CUMULATIVE); - - if( !is_null($rs) ) - { - if( !is_null($my['opt']['mode']) ) - { - switch($my['opt']['mode']) - { - case 'full_path': - if($my['opt']['access_keys'] == 'testplan_testcase') - { - $tcaseSet=null; - $main_keys = array_keys($rs); - - foreach($main_keys as $maccess_key) - { - $sec_keys = array_keys($rs[$maccess_key]); - foreach($sec_keys as $saccess_key) - { - // is enough I process first element - $item = $rs[$maccess_key][$saccess_key][0]; - if(!isset($tcaseSet[$item['testcase_id']])) - { - $tcaseSet[$item['testcase_id']]=$item['testcase_id']; - } - } - } - - $path_info = $this->tree_manager->get_full_path_verbose($tcaseSet); - - // Remove test project piece and convert to string - $flat_path=null; - foreach($path_info as $tcase_id => $pieces) - { - unset($pieces[0]); - // 20100813 - asimon - deactivated last slash on path - // to remove it from test suite name in "tc assigned to user" tables - $flat_path[$tcase_id]=implode('/',$pieces); - } - $main_keys = array_keys($rs); - - foreach($main_keys as $idx) - { - $sec_keys = array_keys($rs[$idx]); - foreach($sec_keys as $jdx) - { - $third_keys = array_keys($rs[$idx][$jdx]); - foreach($third_keys as $tdx) - { - $fdx = $rs[$idx][$jdx][$tdx]['testcase_id']; - $rs[$idx][$jdx][$tdx]['tcase_full_path']=$flat_path[$fdx]; - } - } - } - } - break; - } - } - } - - return $rs; - } - - - - /* - function: update_active_status - - args : id: testcase id - tcversion_id - active_status: 1 -> active / 0 -> inactive - - returns: 1 -> everything ok. - 0 -> some error - rev: - */ - function update_active_status($id,$tcversion_id,$active_status) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " /* $debugMsg */ UPDATE {$this->tables['tcversions']} + ORDER BY NHA.node_order ASC, NHA.parent_id ASC, execution_id {$my['options']['exec_id_order']}"; + + $recordset = $this->db->fetchArrayRowsIntoMap($sql, 'id'); + return $recordset ? $recordset : null; + } + + /* + * function: getLastExecution + * + * args : + * + * + * returns: map: + * key: tcversions.id + * value: map with following keys: + * execution_id + * status: execution status + * execution_type: see const.inc.php TESTCASE_EXECUTION_TYPE_ constants + * name: testcase name + * testcase_id + * tsuite_id: parent testsuite of testcase (node id) + * id: tcversion id (node id) + * version + * summary: testcase spec. summary + * steps: testcase spec. steps + * expected_results: testcase spec. expected results + * execution_type: type of execution desired + * importance + * author_id: tcversion author + * creation_ts: timestamp of creation + * updater_id: last updater of specification. + * modification_ts + * active: tcversion active status + * is_open: tcversion open status + * tester_login + * tester_first_name + * tester_last_name + * tester_id + * execution_notes + * execution_ts + * execution_run_type: how the execution was really done + * build_id + * build_name + * build_is_active + * build_is_open + * + */ + public function getLastExecution($id, $version_id, $tplan_id, $build_id, + $platform_id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $resultsCfg = config_get('results'); + $status_not_run = $resultsCfg['status_code']['not_run']; + + $filterKeys = array( + 'build_id', + 'platform_id' + ); + foreach ($filterKeys as $key) { + $filterBy[$key] = ''; + if (! is_null($$key) && intval($$key) > 0) { // 20230826 + $itemSet = implode(',', (array) $$key); + $filterBy[$key] = " AND e.{$key} IN ({$itemSet}) "; + } + } + + $where_clause_1 = ''; + $where_clause_2 = ''; + $cumulativeMode = 0; + $group_by = ''; + + // getNoExecutions: 1 -> if testcase/version_id has not been executed return anyway + // standard return structure. + // 0 -> default + // + // groupByBuild: 0 -> default, get last execution on ANY BUILD, then for a testcase/version_id + // only a record will be present on return struture. + // GROUP BY must be done ONLY BY tcversion_id + // + // 1 -> get last execution on EACH BUILD. + // GROUP BY must be done BY tcversion_id,build_id + $localOptions = array( + 'getNoExecutions' => 0, + 'groupByBuild' => 0, + 'getSteps' => 1, + 'getStepsExecInfo' => 0, + 'output' => 'std' + ); + if (! is_null($options) && is_array($options)) { + $localOptions = array_merge($localOptions, $options); + } + + if (is_array($id)) { + $tcid_list = implode(",", $id); + $where_clause = " WHERE NHA.parent_id IN ({$tcid_list}) "; + } else { + $where_clause = " WHERE NHA.parent_id = {$id} "; + } + + if (is_array($version_id)) { + $versionid_list = implode(",", $version_id); + $where_clause_1 = $where_clause . + " AND NHA.id IN ({$versionid_list}) "; + $where_clause_2 = $where_clause . + " AND tcversions.id IN ({$versionid_list}) "; + } else { + if ($version_id != self::ALL_VERSIONS) { + $where_clause_1 = $where_clause . " AND NHA.id = {$version_id} "; + $where_clause_2 = $where_clause . + " AND tcversions.id = {$version_id} "; + } + } + + // This logic (is mine - franciscom) must be detailed better!!!!! + $group_by = ' GROUP BY tcversion_id '; + $add_fields = ', e.tcversion_id AS tcversion_id'; + if ($localOptions['groupByBuild']) { + $add_fields .= ', e.build_id'; + $group_by .= ', e.build_id'; + $cumulativeMode = 1; + + // Hummm!!! I do not understand why this can be needed + $where_clause_1 = $where_clause; + $where_clause_2 = $where_clause; + } + + // we may be need to remove tcversion filter ($set_group_by==false) + // $add_field = $set_group_by ? ', e.tcversion_id AS tcversion_id' : ''; + // $add_field = $localOptions['groupByBuild'] ? '' : ', e.tcversion_id AS tcversion_id'; + // $where_clause_1 = $localOptions['groupByBuild'] ? $where_clause : $where_clause_1; + // $where_clause_2 = $localOptions['groupByBuild'] ? $where_clause : $where_clause_2; + + // get list of max exec id, to be used filter in next query + // Here we can get: + // a) one record for each tcversion_id (ignoring build) + // b) one record for each tcversion_id,build + + // 20101212 - franciscom - may be not the best logic but ... + $where_clause_1 = ($where_clause_1 == '') ? $where_clause : $where_clause_1; + $where_clause_2 = ($where_clause_2 == '') ? $where_clause : $where_clause_2; + + $sql = "/* $debugMsg */ " . + " SELECT COALESCE(MAX(e.id),0) AS execution_id {$add_fields}" . + " FROM {$this->tables['nodes_hierarchy']} NHA " . + " JOIN {$this->tables['executions']} e ON NHA.id = e.tcversion_id AND e.testplan_id = {$tplan_id} " . + " {$filterBy['build_id']} {$filterBy['platform_id']}" . + " AND e.status IS NOT NULL " . " $where_clause_1 {$group_by}"; + + $recordset = $this->db->fetchColumnsIntoMap($sql, 'execution_id', + 'tcversion_id'); + $and_exec_id = ''; + if (! empty($recordset)) { + $the_list = implode(",", array_keys($recordset)); + if ($the_list != '') { + if (count($recordset) > 1) { + $and_exec_id = " AND e.id IN ($the_list) "; + } else { + $and_exec_id = " AND e.id = $the_list "; + } + } + } + + $executions_join = " JOIN {$this->tables['executions']} e ON NHA.id = e.tcversion_id " . + " AND e.testplan_id = {$tplan_id} {$and_exec_id} {$filterBy['build_id']} " . + " {$filterBy['platform_id']} "; + + if ($localOptions['getNoExecutions']) { + $executions_join = " LEFT OUTER " . $executions_join; + } else { + // @TODO understand if this condition is really needed - 20090716 - franciscom + $executions_join .= " AND e.status IS NOT NULL "; + } + + switch ($localOptions['output']) { + case 'timestamp': + $sql = "/* $debugMsg */ SELECT e.id AS execution_id, " . + " COALESCE(e.status,'{$status_not_run}') AS status, " . + " e.execution_ts, e.build_id,e.tcversion_number," . + " FROM {$this->tables['nodes_hierarchy']} NHA" . + " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id" . + " JOIN {$this->tables['tcversions']} tcversions ON NHA.id = tcversions.id" . + " {$executions_join}" . " $where_clause_2" . + " ORDER BY NHB.parent_id ASC, NHA.parent_id ASC, execution_id DESC"; + break; + + case 'std': + default: + $sql = "/* $debugMsg */ SELECT e.id AS execution_id, " . + " COALESCE(e.status,'{$status_not_run}') AS status, " . + " e.execution_type AS execution_run_type,e.execution_duration, " . + " NHB.name,NHA.parent_id AS testcase_id, NHB.parent_id AS tsuite_id," . + " tcversions.id,tcversions.tc_external_id,tcversions.version,tcversions.summary," . + " tcversions.preconditions," . + " tcversions.importance,tcversions.author_id," . + " tcversions.creation_ts,tcversions.updater_id,tcversions.modification_ts,tcversions.active," . + " tcversions.is_open,tcversions.execution_type," . + " tcversions.estimated_exec_duration,tcversions.status AS wkfstatus," . + " users.login AS tester_login,users.first AS tester_first_name," . + " users.last AS tester_last_name, e.tester_id AS tester_id," . + " e.notes AS execution_notes, e.execution_ts, e.build_id,e.tcversion_number," . + " builds.name AS build_name, builds.active AS build_is_active, builds.is_open AS build_is_open," . + " e.platform_id,p.name AS platform_name" . + " FROM {$this->tables['nodes_hierarchy']} NHA" . + " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHA.parent_id = NHB.id" . + " JOIN {$this->tables['tcversions']} tcversions ON NHA.id = tcversions.id" . + " {$executions_join}" . + " LEFT OUTER JOIN {$this->tables['builds']} builds ON builds.id = e.build_id" . + " AND builds.testplan_id = {$tplan_id}" . + " LEFT OUTER JOIN {$this->tables['users']} users ON users.id = e.tester_id " . + " LEFT OUTER JOIN {$this->tables['platforms']} p ON p.id = e.platform_id" . + " $where_clause_2" . + " ORDER BY NHB.parent_id ASC, NHA.node_order ASC, NHA.parent_id ASC, execution_id DESC"; + break; + } + + $recordset = $this->db->fetchRowsIntoMap($sql, 'id', $cumulativeMode); + + // Multiple Test Case Steps Feature + if (! is_null($recordset) && $localOptions['getSteps']) { + $xx = null; + if ($localOptions['getStepsExecInfo'] && + ($this->cfg->execution->steps_exec_notes_default == 'latest' || + $this->cfg->execution->steps_exec_status_default == 'latest')) { + $tg = current($recordset); + $xx = $this->getStepsExecInfo($tg['execution_id']); + } + + $itemSet = array_keys($recordset); + foreach ($itemSet as $sdx) { + $step_set = $this->get_steps($recordset[$sdx]['id']); + if ($localOptions['getStepsExecInfo'] && ! is_null($step_set)) { + $key_set = array_keys($step_set); + foreach ($key_set as $kyx) { + $step_set[$kyx]['execution_notes'] = ''; + $step_set[$kyx]['execution_status'] = ''; + + if (isset($xx[$step_set[$kyx]['id']])) { + if ($this->cfg->execution->steps_exec_notes_default == + 'latest') { + $step_set[$kyx]['execution_notes'] = $xx[$step_set[$kyx]['id']]['notes']; + } + + if ($this->cfg->execution->steps_exec_status_default == + 'latest') { + $step_set[$kyx]['execution_status'] = $xx[$step_set[$kyx]['id']]['status']; + } + } + } + } + $recordset[$sdx]['steps'] = $step_set; + } + } + + // ghost Test Case processing in summary & preconditions + if (! is_array($id) && ! is_null($recordset)) { + $key2loop = array_keys($recordset); + + // get test project from test plan + $tplanInfo = $this->tree_manager->get_node_hierarchy_info($tplan_id); + $tproj_id = intval($tplanInfo['parent_id']); + + foreach ($key2loop as $accessKey) { + $this->renderGhost($recordset[$accessKey]); + $this->renderVariables($recordset[$accessKey], $tproj_id); + $this->renderSpecialTSuiteKeywords($recordset[$accessKey]); + $this->renderImageAttachments($id, $recordset[$accessKey]); + + // render exec variables only if we have just one build + if (intval($build_id) > 0 && intval($tplan_id) > 0) { + $context = array( + 'tplan_id' => $tplan_id, + 'build_id' => $build_id + ); + $this->renderBuildExecVars($context, $recordset[$accessKey]); + } + } + reset($recordset); + } + + return $recordset ? $recordset : null; + } + + /* + * function: exportTestCaseDataToXML + * + * args : + * + * $tcversion_id: can be testcase::LATEST_VERSION + * + * returns: + * + * + */ + public function exportTestCaseDataToXML($tcase_id, $tcversion_id, + $tproject_id = null, $bNoXMLHeader = false, $optExport = array()) + { + static $reqMgr; + static $keywordMgr; + static $cfieldMgr; + if (is_null($reqMgr)) { + $reqMgr = new requirement_mgr($this->db); + $keywordMgr = new tlKeyword(); + $cfieldMgr = new cfield_mgr($this->db); + } + + // Useful when you need to get info but do not have tcase id + $tcase_id = intval((int) ($tcase_id)); + $tcversion_id = intval((int) ($tcversion_id)); + if ($tcase_id <= 0 && $tcversion_id > 0) { + $info = $this->tree_manager->get_node_hierarchy_info($tcversion_id); + $tcase_id = $info['parent_id']; + } + + $opt = array( + 'getPrefix' => false + ); + if (! isset($optExport['EXTERNALID']) || $optExport['EXTERNALID']) { + $opt = array( + 'getPrefix' => (isset($optExport['ADDPREFIX']) && + $optExport['ADDPREFIX']) + ); + } + $tc_data = $this->get_by_id($tcase_id, $tcversion_id, null, $opt); + $testCaseVersionID = intval($tc_data[0]['id']); + + if (! $tproject_id) { + $tproject_id = $this->getTestProjectFromTestCase($tcase_id); + } + + $tc_data[0]['xmlplatforms_on_design'] = $this->getPlatformsAsXMLString( + $tcase_id, $testCaseVersionID); + + if (isset($optExport['CFIELDS']) && $optExport['CFIELDS']) { + $cfMap = $this->get_linked_cfields_at_design($tcase_id, + $testCaseVersionID, null, null, $tproject_id); + + // ||yyy||-> tags, {{xxx}} -> attribute + // tags and attributes receive different treatment on exportDataToXML() + // + // each UPPER CASE word in this map KEY, MUST HAVE AN OCCURENCE on $elemTpl + // value is a key inside $tc_data[0] + if (! is_null($cfMap) && count($cfMap) > 0) { + $tc_data[0]['xmlcustomfields'] = $cfieldMgr->exportValueAsXML( + $cfMap); + } + } + + if (isset($optExport['KEYWORDS']) && $optExport['KEYWORDS']) { + // 20180610 - Will export Only for latest version? + $keywords = $this->getKeywords($tcase_id, $testCaseVersionID); + if (! is_null($keywords)) { + $xmlKW = "" . $keywordMgr->toXMLString($keywords, true) . + ""; + $tc_data[0]['xmlkeywords'] = $xmlKW; + } + } + + if (isset($optExport['REQS']) && $optExport['REQS']) { + // Need to get only for test case version + $req4version = $reqMgr->getGoodForTCVersion($testCaseVersionID); + + if (! empty($req4version)) { + $tc_data[0]['xmlrequirements'] = exportDataToXML($req4version, + $this->XMLCfg->req->root, $this->XMLCfg->req->elemTPL, + $this->XMLCfg->req->decode, true); + } + } + + if (isset($optExport['ATTACHMENTS']) && $optExport['ATTACHMENTS']) { + + $attachments = null; + + $library = $this->attachmentRepository->getAttachmentInfosFor( + $tcversion_id, $this->attachmentTableName, 'id'); + + // get all attachments content and encode it in base64 + if ($library) { + foreach ($library as $file) { + $aID = $file["id"]; + $content = $this->attachmentRepository->getAttachmentContent( + $aID, $file); + + if ($content != null) { + $attachments[$aID]["id"] = $aID; + $attachments[$aID]["name"] = $file["file_name"]; + $attachments[$aID]["file_type"] = $file["file_type"]; + $attachments[$aID]["file_size"] = $file["file_size"]; + $attachments[$aID]["title"] = $file["title"]; + $attachments[$aID]["date_added"] = $file["date_added"]; + $attachments[$aID]["content"] = base64_encode($content); + } + } + } + + if (! empty($attachments)) { + $tc_data[0]['xmlattachments'] = exportDataToXML($attachments, + $this->XMLCfg->att->root, $this->XMLCfg->att->elemTPL, + $this->XMLCfg->att->decode, true); + } + } + + if (! isset($optExport['TCSTEPS']) || $optExport['TCSTEPS']) { + $stepRootElem = "{{XMLCODE}}"; + $stepTemplate = "\n" . '' . "\n" . + "\t\n" . + "\t\n" . + "\t\n" . + "\t\n" . + "\n"; + $stepInfo = array( + "||STEP_NUMBER||" => "step_number", + "||ACTIONS||" => "actions", + "||EXPECTEDRESULTS||" => "expected_results", + "||EXECUTIONTYPE||" => "execution_type" + ); + + $stepSet = $tc_data[0]['steps']; + $xmlsteps = exportDataToXML($stepSet, $stepRootElem, $stepTemplate, + $stepInfo, true); + $tc_data[0]['xmlsteps'] = $xmlsteps; + } + + $tc_data[0]['xmlrelations'] = null; + $addElemTpl = ''; + + // When exporting JUST a test case, exporting relations can be used + // as documentation. + // When exporting a Test Suite, format can be different as has been done + // with requirements. + // While ideas become clear , i prefer to add this option for testing + if (isset($optExport['RELATIONS']) && $optExport['RELATIONS']) { + $xmlRel = null; + $addElemTpl .= "||RELATIONS||"; + $relSet = $this->getRelations($tcase_id); + if ($relSet['num_relations'] > 0) { + foreach ($relSet['relations'] as $rv) { + $xmlRel .= $this->exportRelationToXML($rv, $relSet['item']); + } + $tc_data[0]['xmlrelations'] = $xmlRel; + } + } + + $rootElem = "{{XMLCODE}}"; + if (isset($optExport['ROOTELEM'])) { + $rootElem = $optExport['ROOTELEM']; + } + $elemTpl = "\n" . + '' . "\n" . + "\t\n"; + + // Export the Execution Order in a TestPlan for each testcase + if (isset($optExport['EXEC_ORDER'])) { + $elemTpl .= "\t\n"; + + $tc_data[0]['exec_order'] = $optExport['EXEC_ORDER']; + } + + // Export assigned_users into "Export Test Plan" XML content + // table with all users assigned to an execution + if (isset($optExport['ASSIGNED_USER'])) { + $elemTpl .= "\t\n"; + foreach ($optExport['ASSIGNED_USER'] as $username) { + $elemTpl .= "\t\t\n"; + } + $elemTpl .= "\t\n"; + } + + if (! isset($optExport['EXTERNALID']) || $optExport['EXTERNALID']) { + $elemTpl .= "\t\n"; + } + + if (! isset($optExport['ADDPREFIX']) || $optExport['ADDPREFIX']) { + $elemTpl .= "\t\n"; + } + + $optElem = ''; + if (! isset($optExport['TCSUMMARY']) || $optExport['TCSUMMARY']) { + $optElem .= "\t\n"; + } + if (! isset($optExport['TCPRECONDITIONS']) || + $optExport['TCPRECONDITIONS']) { + $optElem .= "\t\n"; + } + + $elemTpl .= "\t\n" . $optElem . + "\t\n" . + "\t\n" . + "\t||ESTIMATED_EXEC_DURATION||\n" . + "\t||STATUS||\n" . + "\t||ISOPEN||\n" . + "\t||ACTIVE||\n" . "||STEPS||\n" . + "||KEYWORDS||||CUSTOMFIELDS||||PLATFORMS_ON_DESIGN||\n" . + "||REQUIREMENTS||||ATTACHMENTS||{$addElemTpl}\n"; + + // ||yyy||-> tags, {{xxx}} -> attribute + // tags and attributes receive different treatment on exportDataToXML() + // + // each UPPER CASE word in this map KEY, MUST HAVE AN OCCURENCE on $elemTpl + // value is a key inside $tc_data[0] + // + $info = array( + "{{TESTCASE_ID}}" => "testcase_id", + "{{NAME}}" => "name", + "||NODE_ORDER||" => "node_order", + "||EXEC_ORDER||" => "exec_order", + "||EXTERNALID||" => "tc_external_id", + "||FULLEXTERNALID||" => "fullExternalID", + "||VERSION||" => "version", + "||SUMMARY||" => "summary", + "||PRECONDITIONS||" => "preconditions", + "||EXECUTIONTYPE||" => "execution_type", + "||IMPORTANCE||" => "importance", + "||ESTIMATED_EXEC_DURATION||" => "estimated_exec_duration", + "||STATUS||" => "status", + "||ISOPEN||" => "is_open", + "||ACTIVE||" => "active", + "||STEPS||" => "xmlsteps", + "||KEYWORDS||" => "xmlkeywords", + "||CUSTOMFIELDS||" => "xmlcustomfields", + "||PLATFORMS_ON_DESIGN||" => "xmlplatforms_on_design", + "||REQUIREMENTS||" => "xmlrequirements", + "||ATTACHMENTS||" => "xmlattachments", + "||RELATIONS||" => "xmlrelations" + ); + + return exportDataToXML($tc_data, $rootElem, $elemTpl, $info, + $bNoXMLHeader); + } + + /* + * function: get_version_exec_assignment + * get information about user that has been assigned + * test case version for execution on a testplan + * + * args : tcversion_id: test case version id + * tplan_id + * + * + * + * returns: map + * key: tcversion_id + * value: map with following keys: + * tcversion_id + * feature_id: identifies row on table testplan_tcversions. + * + * + * user_id: user that has reponsibility to execute this tcversion_id. + * null/empty string is nodoby has been assigned + * + * type type of assignment. + * 1 -> testcase_execution. + * See assignment_types tables for updated information + * about other types of assignemt available. + * + * status assignment status + * See assignment_status tables for updated information. + * 1 -> open + * 2 -> closed + * 3 -> completed + * 4 -> todo_urgent + * 5 -> todo + * + * assigner_id: who has assigned execution to user_id. + * + * + * + */ + public function getVersionExecAssignment($tcversion_id, $tplan_id, $build_id) + { + $sql = "SELECT T.tcversion_id AS tcversion_id,T.id AS feature_id,T.platform_id, " . + " UA.user_id,UA.type,UA.status,UA.assigner_id " . + " FROM {$this->tables['testplan_tcversions']} T " . + " LEFT OUTER JOIN {$this->tables['user_assignments']} UA ON UA.feature_id = T.id " . + " WHERE T.testplan_id={$tplan_id} AND UA.build_id = {$build_id} " . + " AND T.tcversion_id = {$tcversion_id} " . " AND (UA.type=" . + $this->assignment_types['testcase_execution']['id'] . + " OR UA.type IS NULL) "; + + return $this->db->fetchMapRowsIntoMap($sql, 'tcversion_id', + 'platform_id', database::CUMULATIVE); + } + + /** + * get_assigned_to_user() + * Given a user and a tesplan id, get all test case version id linked to + * test plan, that has been assigned for execution to user. + * + * @param + * int user_id + * + * @param + * mixed tproject_id list of test project id to search. + * int or array + * + * @param + * array [tplan_id] list of test plan id to search. + * null => all test plans + * + * @param + * map [options] mode='full_path' + * testcase name full path will be returned + * Only available when acces_keys ='testplan_testcase' + * + * access_keys + * possible values: 'testplan_testcase','testcase_testplan' + * changes access key in result map of maps. + * if not defined or null -> 'testplan_testcase' + * + * @param + * map [filters] 'tplan_status' => 'active','inactive','all' + * + * + * @return array key: (test plan id or test case id depending on options->access_keys, + * default is test plan). + * + * value: map key: (test case id or test plan id depending on options->access_keys, + * default is test case). + * value: + * + * @internal revision + */ + public function getAssignedToUser($user_id, $tproject_id, $tplan_id = null, + $options = null, $filters = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['opt'] = array( + 'mode' => null, + 'order_by' => '', + 'access_keys' => 'testplan_testcase' + ); + $my['opt'] = array_merge($my['opt'], (array) $options); + + $my['filters'] = array( + 'tplan_status' => 'all' + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + // to load assignments for all users OR one given user + $user_sql = ($user_id != TL_USER_ANYBODY) ? " AND UA.user_id = {$user_id} " : ""; + + $filters = ""; + $access_key = array( + 'testplan_id', + 'testcase_id' + ); + + $sql = "/* $debugMsg */ SELECT TPROJ.id as testproject_id,TPTCV.testplan_id,TPTCV.tcversion_id, " . + " TCV.version,TCV.tc_external_id, NHTC.id AS testcase_id, NHTC.name, TPROJ.prefix, " . + " UA.creation_ts ,UA.deadline_ts, UA.user_id as user_id, " . + " COALESCE(PLAT.name,'') AS platform_name, COALESCE(PLAT.id,0) AS platform_id, " . + " (TPTCV.urgency * TCV.importance) AS priority, BUILDS.name as build_name, " . + " BUILDS.id as build_id " . + " FROM {$this->tables['user_assignments']} UA " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.id = UA.feature_id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id=TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TCV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN ON NHTPLAN.id=TPTCV.testplan_id " . + " JOIN {$this->tables['testprojects']} TPROJ ON TPROJ.id = NHTPLAN.parent_id " . + " JOIN {$this->tables['testplans']} TPLAN ON TPLAN.id = TPTCV.testplan_id " . + " JOIN {$this->tables['builds']} BUILDS ON BUILDS.id = UA.build_id " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . + " WHERE UA.type={$this->assignment_types['testcase_execution']['id']} " . + " {$user_sql} " . " AND TPROJ.id IN (" . + implode(',', array( + $tproject_id + )) . ") "; + + if (! is_null($tplan_id)) { + $filters .= " AND TPTCV.testplan_id IN (" . implode(',', $tplan_id) . + ") "; + } + + if (isset($my['filters']['build_id'])) { + $filters .= " AND UA.build_id = {$my['filters']['build_id']} "; + } + + switch ($my['filters']['tplan_status']) { + case 'all': + break; + + case 'active': + $filters .= " AND TPLAN.active = 1 "; + break; + + case 'inactive': + $filters .= " AND TPLAN.active = 0 "; + break; + } + + if (isset($my['filters']['build_status'])) { + switch ($my['filters']['build_status']) { + case 'open': + $filters .= " AND BUILDS.is_open = 1 "; + break; + + case 'closed': + $filters .= " AND BUILDS.is_open = 0 "; + break; + + case 'all': + default: + break; + } + } + + $sql .= $filters; + + if (isset($my['opt']['access_keys'])) { + switch ($my['opt']['access_keys']) { + case 'testplan_testcase': + break; + + case 'testcase_testplan': + $access_key = array( + 'testcase_id', + 'testplan_id' + ); + break; + } + } + + $sql .= $my['opt']['order_by']; + + $rs = $this->db->fetchMapRowsIntoMap($sql, $access_key[0], + $access_key[1], database::CUMULATIVE); + + if (! is_null($rs) && ! is_null($my['opt']['mode'])) { + switch ($my['opt']['mode']) { + case 'full_path': + if ($my['opt']['access_keys'] == 'testplan_testcase') { + $tcaseSet = null; + $main_keys = array_keys($rs); + + foreach ($main_keys as $maccess_key) { + $sec_keys = array_keys($rs[$maccess_key]); + foreach ($sec_keys as $saccess_key) { + // is enough I process first element + $item = $rs[$maccess_key][$saccess_key][0]; + if (! isset($tcaseSet[$item['testcase_id']])) { + $tcaseSet[$item['testcase_id']] = $item['testcase_id']; + } + } + } + + $path_info = $this->tree_manager->get_full_path_verbose( + $tcaseSet); + + // Remove test project piece and convert to string + $flat_path = null; + foreach ($path_info as $tcase_id => $pieces) { + unset($pieces[0]); + // 20100813 - asimon - deactivated last slash on path + // to remove it from test suite name in "tc assigned to user" tables + $flat_path[$tcase_id] = implode('/', $pieces); + } + $main_keys = array_keys($rs); + + foreach ($main_keys as $idx) { + $sec_keys = array_keys($rs[$idx]); + foreach ($sec_keys as $jdx) { + $third_keys = array_keys($rs[$idx][$jdx]); + foreach ($third_keys as $tdx) { + $fdx = $rs[$idx][$jdx][$tdx]['testcase_id']; + $rs[$idx][$jdx][$tdx]['tcase_full_path'] = $flat_path[$fdx]; + } + } + } + } + break; + } + } + + return $rs; + } + + /* + * function: update_active_status + * + * args : id: testcase id + * tcversion_id + * active_status: 1 -> active / 0 -> inactive + * + * returns: 1 -> everything ok. + * 0 -> some error + * rev: + */ + public function updateActiveStatus($id, $tcversion_id, $active_status) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = " /* $debugMsg */ UPDATE {$this->tables['tcversions']} SET active={$active_status} - WHERE id = {$tcversion_id} "; - - $result = $this->db->exec_query($sql); - return $result ? 1: 0; - } - - /* - function: update_order - - args : id: testcase id - order - - returns: - - - */ - function update_order($id,$order) - { - $result=$this->tree_manager->change_order_bulk(array($order => $id)); - return $result ? 1: 0; - } - - - /* - function: update_external_id - - args : id: testcase id - external_id - - returns: - - - */ - function update_external_id($id,$external_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ UPDATE {$this->tables['tcversions']} " . - " SET tc_external_id={$external_id} " . - " WHERE id IN (" . - " SELECT id FROM {$this->tables['nodes_hierarchy']} WHERE parent_id={$id} ) "; - - $result=$this->db->exec_query($sql); - return $result ? 1: 0; - } - - - /** - * Copy attachments from source testcase to target testcase - * - **/ - function copy_attachments($source_id,$target_id) { - return $this->attachmentRepository->copyAttachments($source_id,$target_id,$this->attachmentTableName); - } - - - /** - * copyReqAssignmentTo - * copy requirement assignments for $from test case id to $to test case id - * - * mappings is only useful when source_id and target_id do not belong to same Test Project. - * - * - */ - function copyReqAssignmentTo($from,$to,$mappings,$userID) { - static $req_mgr; - if( is_null($req_mgr) ) { - $req_mgr=new requirement_mgr($this->db); - } - - $itemSet=$req_mgr->get_all_for_tcase($from); - if( !is_null($itemSet) ) { - $loop2do=count($itemSet); - for($idx=0; $idx < $loop2do; $idx++) { - if( isset($mappings[$itemSet[$idx]['id']]) ) { - $items[$idx]=$mappings[$itemSet[$idx]['id']]; - } else { - $items[$idx]=$itemSet[$idx]['id']; - } - } - $req_mgr->assign_to_tcase($items,$to,$userID); - } - } - - /** - * - * - */ - private function getShowViewerActions($mode) { - // fine grain control of operations - $viewerActions= new stdClass(); - $viewerActions->edit='no'; - $viewerActions->delete_testcase='no'; - $viewerActions->delete_version='no'; - $viewerActions->deactivate='no'; - $viewerActions->create_new_version='no'; - $viewerActions->export='no'; - $viewerActions->move='no'; - $viewerActions->copy='no'; - $viewerActions->add2tplan='no'; - $viewerActions->freeze='no'; - $viewerActions->updTplanTCV='no'; - - switch ($mode) { - case 'editOnExec': - $viewerActions->edit='yes'; - $viewerActions->create_new_version='yes'; - $viewerActions->updTplanTCV='yes'; - break; - - case 'editDisabled': - break; - - default: - foreach($viewerActions as $key => $value) { - $viewerActions->$key='yes'; - } - break; - } - return $viewerActions; - } - - /** - * given an executio id delete execution and related data. - * - */ - function deleteExecution($executionID) - { - $whereClause = " WHERE execution_id = {$executionID} "; - $sql = array("DELETE FROM {$this->tables['execution_bugs']} {$whereClause} ", - "DELETE FROM {$this->tables['cfield_execution_values']} {$whereClause} ", - "DELETE FROM {$this->tables['executions']} WHERE id = {$executionID}" ); - - foreach ($sql as $the_stm) - { - $result = $this->db->exec_query($the_stm); - if (!$result) - { - break; - } - } - } - - - - - // --------------------------------------------------------------------------------------- - // Custom field related functions - // --------------------------------------------------------------------------------------- - - /* - function: get_linked_cfields_at_design - Get all linked custom fields that must be available at design time. - Remember that custom fields are defined at system wide level, and - has to be linked to a testproject, in order to be used. - - args: id: testcase id - tcversion_id: testcase version id ---- BUGID 3431 - [parent_id]: node id of parent testsuite of testcase. - need to understand to which testproject the testcase belongs. - this information is vital, to get the linked custom fields. - Presence /absence of this value changes starting point - on procedure to build tree path to get testproject id. - - null -> use testcase_id as starting point. - !is_null -> use this value as starting point. - - [$filters]:default: null - - map with keys: - - [show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - include ONLY custom fields that can be viewed - while user is execution testcases. - - 0 or null -> don't filter - - [show_on_testplan_design]: default: null - 1 -> filter on field show_on_testplan_design=1 - include ONLY custom fields that can be viewed - while user is designing test plan. - - 0 or null -> don't filter - - [location] new concept used to define on what location on screen - custom field will be designed. - Initally used with CF available for Test cases, to - implement pre-requisites. - null => no filtering - - - More comments/instructions on cfield_mgr->get_linked_cfields_at_design() - - returns: map/hash - key: custom field id - value: map with custom field definition and value assigned for choosen testcase, - with following keys: - - id: custom field id - name - label - type: custom field type - possible_values: for custom field - default_value - valid_regexp - length_min - length_max - show_on_design - enable_on_design - show_on_execution - enable_on_execution - display_order - value: value assigned to custom field for this testcase - null if for this testcase custom field was never edited. - - node_id: testcase id - null if for this testcase, custom field was never edited. - - - rev : - 20070302 - check for $id not null, is not enough, need to check is > 0 - - */ - function get_linked_cfields_at_design($id,$tcversion_id,$parent_id=null,$filters=null,$tproject_id = null) - { - if (!$tproject_id) - { - $tproject_id = $this->getTestProjectFromTestCase($id,$parent_id); - } - - $cf_map = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, - self::ENABLED,$filters,'testcase',$tcversion_id); - return $cf_map; - } - - - - /* - function: getTestProjectFromTestCase - - args: id: testcase id - [parent_id]: node id of parent testsuite of testcase. - need to understand to which testproject the testcase belongs. - this information is vital, to get the linked custom fields. - Presence /absence of this value changes starting point - on procedure to build tree path to get testproject id. - - null -> use testcase_id as starting point. - !is_null -> use this value as starting point. - */ - function getTestProjectFromTestCase($id,$parent_id=null) - { - $the_path = $this->tree_manager->get_path( (!is_null($id) && $id > 0) ? $id : $parent_id); - $path_len = count($the_path); - $tproject_id = ($path_len > 0)? $the_path[0]['parent_id'] : $parent_id; - - return $tproject_id; - } - - /* - function: get_testproject - Given a testcase id get node id of testproject to which testcase belongs. - args :id: testcase id - - returns: testproject id - */ - function get_testproject($id) { - $a_path = $this->tree_manager->get_path($id); - return $a_path[0]['parent_id']; - } - - - /* - function: html_table_of_custom_field_inputs - Return html code, implementing a table with custom fields labels - and html inputs, for choosen testcase. - Used to manage user actions on custom fields values. - - - args: $id: IMPORTANT: - we can receive 0 in this arguments and THERE IS NOT A problem - if parent_id arguments has a value. - Because argument id or parent_id are used to understand what is - testproject where test case belong, in order to get custom fields - assigned/linked to test project. - - - [parent_id]: node id of parent testsuite of testcase. - need to undertad to which testproject the testcase belongs. - this information is vital, to get the linked custom fields. - Presence /absence of this value changes starting point - on procedure to build tree path to get testproject id. - - null -> use testcase_id as starting point. - !is_null -> use this value as starting point. - - [$scope]: 'design' -> use custom fields that can be used at design time (specification) - 'execution' -> use custom fields that can be used at execution time. - - [$name_suffix]: must start with '_' (underscore). - Used when we display in a page several items - example: - during test case execution, several test cases - during testplan design (assign test case to testplan). - - that have the same custom fields. - In this kind of situation we can use the item id as name suffix. - - [link_id]: default null - scope='testplan_design'. - link_id=testplan_tcversions.id this value is also part of key - to access CF values on new table that hold values assigned - to CF used on the 'tesplan_design' scope. - - scope='execution' - link_id=execution id - - BUGID 3431 - scope='design' - link_id=tcversion id - - - [tplan_id]: default null - used when scope='execution' and YOU NEED to get input with value - related to link_id - - [tproject_id]: default null - used to speedup feature when this value is available. - - - returns: html string - - rev: 20080811 - franciscom - BUGID 1650 (REQ) - - BUGID 3431 - - - */ - function html_table_of_custom_field_inputs($id,$parent_id=null, - $scope='design',$name_suffix='',$link_id=null,$tplan_id=null, - $tproject_id = null,$filters=null, $input_values = null) { - $cf_smarty = ''; - $cf_scope=trim($scope); - $method_name='get_linked_cfields_at_' . $cf_scope; - - switch($cf_scope) - { - case 'testplan_design': - $cf_map = $this->$method_name($id,$parent_id,null,$link_id,null,$tproject_id); - break; - - case 'design': - $cf_map = $this->$method_name($id,$link_id,$parent_id,$filters,$tproject_id); - break; - - case 'execution': - $cf_map = $this->$method_name($id,$parent_id,null,$link_id,$tplan_id,$tproject_id); - break; - - } - - if(!is_null($cf_map)) { - $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map,$name_suffix,$input_values); - } - return $cf_smarty; - } - - /** - * Just a Wrapper to improve, sometimes code layout - */ - function htmlTableOfCFValues($id,$context,$filters=null,$formatOptions=null) { - - // $context - $ctx = array('scope' => 'design', 'execution_id' => null, - 'testplan_id' => null,'tproject_id' => null,'link_id' => null); - - $ctx = array_merge($ctx,$context); - extract($ctx); - - return $this->html_table_of_custom_field_values($id,$scope,$filters,$execution_id, - $testplan_id,$tproject_id,$formatOptions,$link_id); - - } - - - /* - function: html_table_of_custom_field_values - Return html code, implementing a table with custom fields labels - and custom fields values, for choosen testcase. - You can think of this function as some sort of read only version - of html_table_of_custom_field_inputs. - - - args: $id: Very Important!!! - scope='design' -> this is a testcase id - scope='execution' -> this is a testcase VERSION id - scope='testplan_design' -> this is a testcase VERSION id - - [$scope]: 'design' -> use custom fields that can be used at design time (specification) - 'execution' -> use custom fields that can be used at execution time. - 'testplan_design' - - [$filters]:default: null - - map with keys: - - [show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - include ONLY custom fields that can be viewed - while user is execution testcases. - - 0 or null -> don't filter - - [show_on_testplan_design]: default: null - 1 -> filter on field show_on_testplan_design=1 - include ONLY custom fields that can be viewed - while user is designing test plan. - - 0 or null -> don't filter - - [location] new concept used to define on what location on screen - custom field will be designed. - Initally used with CF available for Test cases, to - implement pre-requisites. - null => no filtering - - More comments/instructions on cfield_mgr->get_linked_cfields_at_design() - - - [$execution_id]: null -> get values for all executions availables for testcase - !is_null -> only get values or this execution_id - - [$testplan_id]: null -> get values for any tesplan to with testcase is linked - !is_null -> get values only for this testplan. - - [$tproject_id] - [$formatOptions] - [$link_id]: default null - scope='testplan_design'. - link_id=testplan_tcversions.id this value is also part of key - to access CF values on new table that hold values assigned - to CF used on the 'tesplan_design' scope. - - BUGID 3431 - scope='design'. - link_id=tcversion_id - - - - - returns: html string - - */ - function html_table_of_custom_field_values($id,$scope='design', - $filters=null,$execution_id=null, - $testplan_id=null,$tproject_id = null, - $formatOptions=null,$link_id=null) - { - $label_css_style = ' class="labelHolder" '; - $value_css_style = ' '; - - $add_table=true; - $table_style=''; - if( !is_null($formatOptions) ) - { - $label_css_style = isset($formatOptions['label_css_style']) ? - $formatOptions['label_css_style'] : $label_css_style; - $value_css_style = isset($formatOptions['value_css_style']) ? - $formatOptions['value_css_style'] : $value_css_style; - - $add_table = isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; - $table_style = isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; - } - - $cf_smarty = ''; - - $location=null; // no filter - $filterKey='location'; - if( isset($filters[$filterKey]) && !is_null($filters[$filterKey]) ) - { - $location = $filters[$filterKey]; - } - - switch($scope) - { - case 'design': - $cf_map = $this->get_linked_cfields_at_design($id,$link_id,null,$filters,$tproject_id); - break; - - case 'testplan_design': - $cf_map = $this->get_linked_cfields_at_testplan_design($id,null,$filters,$link_id, - $testplan_id,$tproject_id); - break; - - case 'execution': - $cf_map = $this->get_linked_cfields_at_execution($id,null,$filters,$execution_id, - $testplan_id,$tproject_id,$location); - break; - } - - if(!is_null($cf_map)) - { - foreach($cf_map as $cf_id => $cf_info) - { - // if user has assigned a value, then node_id is not null - if(isset($cf_info['node_id']) || - $this->cfg->cfield->show_custom_fields_without_value) - { - // true => do not create input in audit log - $label = str_replace(TL_LOCALIZE_TAG,'',lang_get($cf_info['label'],null,true)); - - $cf_smarty .= " " . htmlspecialchars($label) . ":" . - "" . - $this->cfield_mgr->string_custom_field_value($cf_info,$id) . - "\n"; - } - } - - if( (trim($cf_smarty) != "") && $add_table) - { - $cf_smarty = "" . $cf_smarty . "
    "; - } - } - return $cf_smarty; - } // function end - - - /* - function: get_linked_cfields_at_execution - - - args: $id - [$parent_id] - [$show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - 0 or null -> don't filter - //@TODO - 20090718 - franciscom - // this filter has any sense ? - // review and remove if needed - - - [$execution_id]: null -> get values for all executions availables for testcase - !is_null -> only get values or this execution_id - - [$testplan_id]: null -> get values for any tesplan to with testcase is linked - !is_null -> get values only for this testplan. - - [$tproject_id]: - - returns: hash - key: custom field id - value: map with custom field definition, with keys: - - id: custom field id - name - label - type - possible_values - default_value - valid_regexp - length_min - length_max - show_on_design - enable_on_design - show_on_execution - enable_on_execution - display_order - - */ - function get_linked_cfields_at_execution($id,$parent_id=null,$show_on_execution=null, - $execution_id=null,$testplan_id=null, - $tproject_id = null, $location=null) - { - $thisMethod=__FUNCTION__; - if (!$tproject_id) - { - $tproject_id = $this->getTestProjectFromTestCase($id,$parent_id); - } - - // VERY IMPORTANT WARNING: - // I'm setting node type to test case, but $id is the tcversion_id, because - // execution data is related to tcversion NO testcase - // - $cf_map = $this->cfield_mgr->$thisMethod($tproject_id,self::ENABLED,'testcase', - $id,$execution_id,$testplan_id,'id',$location); - return $cf_map; - } - - - /* - function: copy_cfields_design_values - Get all cfields linked to any testcase of this testproject - with the values presents for $from_id, testcase we are using as - source for our copy. - - args: source: map('id' => testcase id, 'tcversion_id' => testcase id) - destination: map('id' => testcase id, 'tcversion_id' => testcase id) - - returns: - - - - */ - function copy_cfields_design_values($source,$destination) { - // Get all cfields linked to any testcase of this test project - // with the values presents for $from_id, testcase we are using as - // source for our copy - $cfmap_from = $this->get_linked_cfields_at_design($source['id'],$source['tcversion_id']); - - $cfield=null; - if( !is_null($cfmap_from) ) { - foreach($cfmap_from as $key => $value) { - $cfield[$key]=array("type_id" => $value['type'], "cf_value" => $value['value']); - } - } - $this->cfield_mgr->design_values_to_db($cfield,$destination['tcversion_id'],null,'tcase_copy_cfields'); - } - - - /* - function: get_linked_cfields_at_testplan_design - - - args: $id - [$parent_id] - - [$filters]:default: null - - map with keys: - - [show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - include ONLY custom fields that can be viewed - while user is execution testcases. - - 0 or null -> don't filter - - [show_on_testplan_design]: default: null - 1 -> filter on field show_on_testplan_design=1 - include ONLY custom fields that can be viewed - while user is designing test plan. - - 0 or null -> don't filter - - More comments/instructions on cfield_mgr->get_linked_cfields_at_design() - - [$link_id]: - - [$testplan_id]: null -> get values for any tesplan to with testcase is linked - !is_null -> get values only for this testplan. - - returns: hash - key: custom field id - value: map with custom field definition, with keys: - - id: custom field id - name - label - type - possible_values - default_value - valid_regexp - length_min - length_max - show_on_design - enable_on_design - show_on_execution - enable_on_execution - display_order - - - */ - function get_linked_cfields_at_testplan_design($id,$parent_id=null,$filters=null, - $link_id=null,$testplan_id=null,$tproject_id = null) - { - if (!$tproject_id) - { - $tproject_id = $this->getTestProjectFromTestCase($id,$parent_id); - } - - // Warning: - // I'm setting node type to test case, but $id is the tcversion_id, because - // link data is related to tcversion NO testcase - // - $cf_map = $this->cfield_mgr->get_linked_cfields_at_testplan_design($tproject_id,self::ENABLED,'testcase', - $id,$link_id,$testplan_id); - return $cf_map; - } - - - /** - * returns map with key: verbose location (see custom field class $locations - * value: array with fixed key 'location' - * value: location code - * - */ - function buildCFLocationMap() - { - $ret = $this->cfield_mgr->buildLocationMap('testcase'); - return $ret; - } - - - /** - * given a set of test cases, will return a map with - * test suites name that form test case path to root test suite. - * - * example: - * - * communication devices [ID 4] - * |__ Subspace channels [ID 20] - * | - * |__ TestCase100 - * | - * |__ short range devices [ID 21] - * |__ TestCase1 - * |__ TestCase2 - * - * if test case set: TestCase100,TestCase1 - * - * 4 Communications - * 20 Communications/Subspace channels - * 21 Communications/Subspace channels/short range devices - * - * - * returns map with key: test suite id - * value: test suite path to root - * - * - */ - function getPathLayered($tcaseSet, $opt=null) { - - static $tsuiteMgr; - if( !$tsuiteMgr ) { - $tsuiteMgr = new testsuite($this->db); - } - - $xtree=null; - - $options = array('getTSuiteKeywords' => false); - $options = array_merge($options, (array)$opt); - - $idSet = (array)$tcaseSet; - foreach($idSet as $item) { - $path_info = $this->tree_manager->get_path($item); - $testcase = end($path_info); - - // This check is useful when you have several test cases with same parent test suite - if( !isset($xtree[$testcase['parent_id']]['value']) ) { - $level=0; - - foreach($path_info as $elem) { - $level++; - $prefix = isset($xtree[$elem['parent_id']]['value']) ? ($xtree[$elem['parent_id']]['value'] . '/') : ''; - if( $elem['node_table'] == 'testsuites' ) { - $xtree[$elem['id']]['value'] = $prefix . $elem['name']; - $xtree[$elem['id']]['level']=$level; - $xtree[$elem['id']]['data_management'] = null; - } - } - } - - if( null != $xtree && $options['getTSuiteKeywords'] ) { - $tsSet = array_keys($xtree); - $opkw = array('output' => 'kwname'); - $fkw = array('keywordsLikeStart' => '@#'); - $iset = (array) $tsuiteMgr->getTSuitesFilteredByKWSet($tsSet,$opkw,$fkw); - - foreach( $iset as $tsuite_id => $elem ) { - foreach( $elem as $e ) { - if( null != $e ) { - $xtree[$tsuite_id]['data_management'][$e['keyword']] = $e['dyn_string']; - } - } - } - } - } - return $xtree; - } // getPathLayered($tcaseSet) - - - - /** - * - * - */ - function getPathTopSuite($tcaseSet) - { - $xtmas=null; - foreach($tcaseSet as $item) - { - $path_info = $this->tree_manager->get_path($item); - $top = current($path_info); - $xtmas[$item] = array( 'name' => $top['name'], 'id' => $top['id']); - } - return $xtmas; - } // getPathTopSuite($tcaseSet) - - - - /* - function: getByPathName - pathname format - Test Project Name::SuiteName::SuiteName::...::Test case name - - args: $pathname - returns: hash - */ - function getByPathName($pathName,$pathSeparator='::') - { - $recordset = null; - $retval=null; - - // First get root -> test project name and leaf => test case name - $parts = explode($pathSeparator,$pathName); - $partsQty = count($parts); - $tprojectName = $parts[0]; - $tsuiteName = $parts[$partsQty-2]; - $tcaseName = end($parts); - - // get all testcases on test project with this name and parent test suite - $recordset = $this->get_by_name($tcaseName, $tsuiteName ,$tprojectName); - if( !is_null($recordset) && count($recordset) > 0 ) - { - foreach($recordset as $value) - { - $dummy = $this->tree_manager->get_full_path_verbose($value['id']); - $sx = implode($pathSeparator,current($dummy)) . $pathSeparator . $tcaseName; - if( strcmp($pathName,$sx ) == 0 ) - { - $retval = $value; - break; - } - } - } - return $retval; - } - - /** - * - * - */ - function buildDirectWebLink($base_href,$id,$tproject_id=null) - { - list($external_id,$prefix,$glue,$tc_number) = $this->getExternalID($id,$tproject_id); - - $dl = $base_href . 'linkto.php?tprojectPrefix=' . urlencode($prefix) . - '&item=testcase&id=' . urlencode($external_id); - return $dl; - } - - /** - * - * - */ - function getExternalID($id,$tproject_id=null,$prefix=null) - { - static $root; - static $tcase_prefix; - - if( is_null($prefix) ) - { - if( is_null($root) || ($root != $tproject_id) ) - { - list($tcase_prefix,$root) = $this->getPrefix($id,$tproject_id); - } - } - else - { - $tcase_prefix = $prefix; - } - $info = $this->get_last_version_info($id, array('output' => 'minimun')); - $external = $info['tc_external_id']; - $identity = $tcase_prefix . $this->cfg->testcase->glue_character . $external; - return array($identity,$tcase_prefix,$this->cfg->testcase->glue_character,$external); - } - - - /** - * returns just name, tc_external_id, version. - * this info is normally enough for user feednack. - * - * @param int $id test case id - * @param array $accessVersionBy 'number' => contains test case version number - * 'id' => contains test case version ID - * - * @return array with one element with keys: name,version,tc_external_id - */ - function get_basic_info($id,$accessVersionBy) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT NH_TCASE.id, NH_TCASE.name, TCV.version, TCV.tc_external_id, " . - " TCV.id AS tcversion_id, TCV.status " . - " FROM {$this->tables['nodes_hierarchy']} NH_TCASE " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = NH_TCASE.id" . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH_TCV.id "; - - $accessBy = array('number' => 'version', 'id' => 'id'); - $where_clause = ''; - foreach( $accessBy as $key => $field) - { - if( isset($accessVersionBy[$key]) ) - { - $where_clause = " WHERE TCV.{$field} = " . intval($accessVersionBy[$key]) ; - break; - } - } - $where_clause .= " AND NH_TCASE .id = {$id} "; - $sql .= $where_clause; - $result = $this->db->get_recordset($sql); - return $result; - } - - - - /** - * - * - */ - function create_step($tcversion_id,$step_number,$actions,$expected_results, - $execution_type=TESTCASE_EXECUTION_TYPE_MANUAL) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array(); - - // defensive programming - $dummy = $this->db->prepare_int($execution_type); - $dummy = (isset($this->execution_types[$dummy])) ? $dummy : TESTCASE_EXECUTION_TYPE_MANUAL; - - $item_id = $this->tree_manager->new_node($tcversion_id,$this->node_types_descr_id['testcase_step']); - - $k2e = array('actions','expected_results'); - $item = new stdClass(); - $item->actions = $actions; - $item->expected_results = $expected_results; - $this->CKEditorCopyAndPasteCleanUp($item,$k2e); - - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['tcsteps']} " . - " (id,step_number,actions,expected_results,execution_type) " . - " VALUES({$item_id},{$step_number},'" . - $this->db->prepare_string($item->actions) . "','" . - $this->db->prepare_string($item->expected_results) . "', " . - $this->db->prepare_int($dummy) . ")"; - - $result = $this->db->exec_query($sql); - $ret = array('msg' => 'ok', 'id' => $item_id, 'status_ok' => 1, - 'sql' => $sql); - if (!$result) - { - $ret['msg'] = $this->db->error_msg(); - $ret['status_ok']=0; - $ret['id']=-1; - } - return $ret; - } - - /** - * - * - */ - function get_steps($tcversion_id,$step_number=0,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array( 'fields2get' => '*', 'accessKey' => null, - 'renderGhostSteps' => true, 'renderImageInline' => true); - - $my['options'] = array_merge($my['options'], (array)$options); - - $step_filter = $step_number > 0 ? " AND step_number = {$step_number} " : ""; - $safe_tcversion_id = $this->db->prepare_int($tcversion_id); - - // build - $f2g = "TCSTEPS.{$my['options']['fields2get']}"; - if($my['options']['fields2get'] != '*') { - $sof = explode(',',$my['options']['fields2get']); - foreach($sof as &$ele) { - $ele = 'TCSTEPS.' . $ele; - } - $f2g = implode(',',$sof); - } - $sql = "/* $debugMsg */ " . - " SELECT {$f2g} " . - " FROM {$this->tables['tcsteps']} TCSTEPS " . - " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . - " ON NH_STEPS.id = TCSTEPS.id " . - " WHERE NH_STEPS.parent_id = {$safe_tcversion_id} {$step_filter} ORDER BY step_number"; - - if( is_null($my['options']['accessKey']) ) { - $result = $this->db->get_recordset($sql); - } else { - $result = $this->db->fetchRowsIntoMap($sql,$my['options']['accessKey']); - } - - if(!is_null($result) && $my['options']['renderGhostSteps']) { - $sql = "/* $debugMsg */ - SELECT summary,preconditions - FROM {$this->tables['tcversions']} TCV - WHERE TCV.id = $safe_tcversion_id "; - $scan = current($this->db->get_recordset($sql)); - - $xrayScan = null; - foreach($scan as $fn => $vf) { - if( trim($vf) != '' ) { - if( strpos($vf,self::NAME_PHOPEN) !== FALSE && - strpos($vf,self::NAME_PHCLOSE) !== FALSE ) { - $xrayScan[$fn] = $vf; - } - } - } - $this->renderGhostSteps($result, $xrayScan); - } - - if(!is_null($result) && $my['options']['renderImageInline']) { - // for attachments we need main entity => Test case - $tcvnode = $this->tree_manager->get_node_hierarchy_info($tcversion_id); - $k2l = count($result); - $gaga = array('actions','expected_results'); - for($idx=0; $idx < $k2l; $idx++) { - $this->renderImageAttachments($tcvnode['parent_id'],$result[$idx],$gaga); - } - } - - return $result; - } - - /** - * - */ - function getStepsSimple($tcversion_id,$step_number=0,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('fields2get' => 'TCSTEPS.*', 'accessKey' => null, - 'renderGhostSteps' => true, 'renderImageInline' => true); - $my['options'] = array_merge($my['options'], (array)$options); - - $step_filter = $step_number > 0 ? " AND step_number = {$step_number} " : ""; - $safe_tcversion_id = $this->db->prepare_int($tcversion_id); - - $sql = "/* $debugMsg */ " . - " SELECT {$my['options']['fields2get']} " . - " FROM {$this->tables['tcsteps']} TCSTEPS " . - " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . - " ON NH_STEPS.id = TCSTEPS.id " . - " WHERE NH_STEPS.parent_id = {$safe_tcversion_id} {$step_filter} ORDER BY step_number"; - - if( is_null($my['options']['accessKey']) ) - { - $result = $this->db->get_recordset($sql); - } - else - { - $result = $this->db->fetchRowsIntoMap($sql,$my['options']['accessKey']); - } - - return $result; - } - - - - /** - * - * - */ - function get_step_by_id($step_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT TCSTEPS.* FROM {$this->tables['tcsteps']} TCSTEPS " . - " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . - " ON NH_STEPS.id = TCSTEPS.id " . - " WHERE TCSTEPS.id = {$step_id} "; - $result = $this->db->get_recordset($sql); - - return is_null($result) ? $result : $result[0]; - } - - - function get_step_numbers($tcversion_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT TCSTEPS.id, TCSTEPS.step_number FROM {$this->tables['tcsteps']} TCSTEPS " . - " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . - " ON NH_STEPS.id = TCSTEPS.id " . - " WHERE NH_STEPS.parent_id = {$tcversion_id} ORDER BY step_number"; - - $result = $this->db->fetchRowsIntoMap($sql,'step_number'); - return $result; - } - - - - /** - * - * - */ - function get_latest_step_number($tcversion_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT MAX(TCSTEPS.step_number) AS max_step FROM {$this->tables['tcsteps']} TCSTEPS " . - " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . - " ON NH_STEPS.id = TCSTEPS.id " . - " WHERE NH_STEPS.parent_id = {$tcversion_id} "; - - $result = $this->db->get_recordset($sql); - $max_step = (!is_null($result) && isset($result[0]['max_step']) )? $result[0]['max_step'] : 0; - return $max_step; - } - - - /** - * - * $step_id can be an array - */ - function delete_step_by_id($step_id) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $idSet = implode(',',(array)$step_id); - - // Try to delete any children entity - // Execution Attachment - // Execution result - // - $dummy = " /* $debugMsg */ SELECT id FROM - {$this->tables['attachments']} - WHERE fk_table = 'execution_tcsteps' + WHERE id = {$tcversion_id} "; + + $result = $this->db->exec_query($sql); + return $result ? 1 : 0; + } + + /* + * function: update_order + * + * args : id: testcase id + * order + * + * returns: - + * + */ + private function updateOrder($id, $order) + { + $result = $this->tree_manager->change_order_bulk(array( + $order => $id + )); + return $result ? 1 : 0; + } + + /* + * function: update_external_id + * + * args : id: testcase id + * external_id + * + * returns: - + * + */ + private function updateExternalID($id, $external_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ UPDATE {$this->tables['tcversions']} " . + " SET tc_external_id={$external_id} " . " WHERE id IN (" . + " SELECT id FROM {$this->tables['nodes_hierarchy']} WHERE parent_id={$id} ) "; + + $result = $this->db->exec_query($sql); + return $result ? 1 : 0; + } + + /** + * Copy attachments from source testcase to target testcase + */ + private function copyAttachments($source_id, $target_id) + { + return $this->attachmentRepository->copyAttachments($source_id, + $target_id, $this->attachmentTableName); + } + + /** + * copyReqAssignmentTo + * copy requirement assignments for $from test case id to $to test case id + * + * mappings is only useful when source_id and target_id do not belong to same Test Project. + */ + private function copyReqAssignmentTo($from, $to, $mappings, $userID) + { + static $req_mgr; + if (is_null($req_mgr)) { + $req_mgr = new requirement_mgr($this->db); + } + + $itemSet = $req_mgr->get_all_for_tcase($from); + if (! is_null($itemSet)) { + $loop2do = count($itemSet); + for ($idx = 0; $idx < $loop2do; $idx ++) { + if (isset($mappings[$itemSet[$idx]['id']])) { + $items[$idx] = $mappings[$itemSet[$idx]['id']]; + } else { + $items[$idx] = $itemSet[$idx]['id']; + } + } + $req_mgr->assign_to_tcase($items, $to, $userID); + } + } + + /** + */ + private function getShowViewerActions($mode) + { + // fine grain control of operations + $viewerActions = new stdClass(); + $viewerActions->edit = 'no'; + $viewerActions->delete_testcase = 'no'; + $viewerActions->delete_version = 'no'; + $viewerActions->deactivate = 'no'; + $viewerActions->create_new_version = 'no'; + $viewerActions->export = 'no'; + $viewerActions->move = 'no'; + $viewerActions->copy = 'no'; + $viewerActions->add2tplan = 'no'; + $viewerActions->freeze = 'no'; + $viewerActions->updTplanTCV = 'no'; + + switch ($mode) { + case 'editOnExec': + $viewerActions->edit = 'yes'; + $viewerActions->create_new_version = 'yes'; + $viewerActions->updTplanTCV = 'yes'; + break; + + case 'editDisabled': + break; + + default: + foreach ($viewerActions as $key => $value) { + $viewerActions->$key = 'yes'; + } + break; + } + return $viewerActions; + } + + /** + * given an executio id delete execution and related data. + */ + public function deleteExecution($executionID) + { + $whereClause = " WHERE execution_id = {$executionID} "; + $sql = array( + "DELETE FROM {$this->tables['execution_bugs']} {$whereClause} ", + "DELETE FROM {$this->tables['cfield_execution_values']} {$whereClause} ", + "DELETE FROM {$this->tables['executions']} WHERE id = {$executionID}" + ); + + foreach ($sql as $the_stm) { + $result = $this->db->exec_query($the_stm); + if (! $result) { + break; + } + } + } + + // --------------------------------------------------------------------------------------- + // Custom field related functions + // --------------------------------------------------------------------------------------- + + /* + * function: get_linked_cfields_at_design + * Get all linked custom fields that must be available at design time. + * Remember that custom fields are defined at system wide level, and + * has to be linked to a testproject, in order to be used. + * + * args: id: testcase id + * tcversion_id: testcase version id ---- BUGID 3431 + * [parent_id]: node id of parent testsuite of testcase. + * need to understand to which testproject the testcase belongs. + * this information is vital, to get the linked custom fields. + * Presence /absence of this value changes starting point + * on procedure to build tree path to get testproject id. + * + * null -> use testcase_id as starting point. + * !is_null -> use this value as starting point. + * + * [$filters]:default: null + * + * map with keys: + * + * [show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * include ONLY custom fields that can be viewed + * while user is execution testcases. + * + * 0 or null -> don't filter + * + * [show_on_testplan_design]: default: null + * 1 -> filter on field show_on_testplan_design=1 + * include ONLY custom fields that can be viewed + * while user is designing test plan. + * + * 0 or null -> don't filter + * + * [location] new concept used to define on what location on screen + * custom field will be designed. + * Initally used with CF available for Test cases, to + * implement pre-requisites. + * null => no filtering + * + * + * More comments/instructions on cfield_mgr->get_linked_cfields_at_design() + * + * returns: map/hash + * key: custom field id + * value: map with custom field definition and value assigned for choosen testcase, + * with following keys: + * + * id: custom field id + * name + * label + * type: custom field type + * possible_values: for custom field + * default_value + * valid_regexp + * length_min + * length_max + * show_on_design + * enable_on_design + * show_on_execution + * enable_on_execution + * display_order + * value: value assigned to custom field for this testcase + * null if for this testcase custom field was never edited. + * + * node_id: testcase id + * null if for this testcase, custom field was never edited. + * + * + * rev : + * 20070302 - check for $id not null, is not enough, need to check is > 0 + * + */ + public function get_linked_cfields_at_design($id, $tcversion_id, + $parent_id = null, $filters = null, $tproject_id = null) + { + if (! $tproject_id) { + $tproject_id = $this->getTestProjectFromTestCase($id, $parent_id); + } + + return $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, + self::ENABLED, $filters, 'testcase', $tcversion_id); + } + + /* + * function: getTestProjectFromTestCase + * + * args: id: testcase id + * [parent_id]: node id of parent testsuite of testcase. + * need to understand to which testproject the testcase belongs. + * this information is vital, to get the linked custom fields. + * Presence /absence of this value changes starting point + * on procedure to build tree path to get testproject id. + * + * null -> use testcase_id as starting point. + * !is_null -> use this value as starting point. + */ + public function getTestProjectFromTestCase($id, $parent_id = null) + { + $the_path = $this->tree_manager->get_path( + (! empty($id)) ? $id : $parent_id); + return (! empty($the_path)) ? $the_path[0]['parent_id'] : $parent_id; + } + + /* + * function: get_testproject + * Given a testcase id get node id of testproject to which testcase belongs. + * args :id: testcase id + * + * returns: testproject id + */ + public function get_testproject($id) + { + $a_path = $this->tree_manager->get_path($id); + return $a_path[0]['parent_id']; + } + + /* + * function: html_table_of_custom_field_inputs + * Return html code, implementing a table with custom fields labels + * and html inputs, for choosen testcase. + * Used to manage user actions on custom fields values. + * + * + * args: $id: IMPORTANT: + * we can receive 0 in this arguments and THERE IS NOT A problem + * if parent_id arguments has a value. + * Because argument id or parent_id are used to understand what is + * testproject where test case belong, in order to get custom fields + * assigned/linked to test project. + * + * + * [parent_id]: node id of parent testsuite of testcase. + * need to undertad to which testproject the testcase belongs. + * this information is vital, to get the linked custom fields. + * Presence /absence of this value changes starting point + * on procedure to build tree path to get testproject id. + * + * null -> use testcase_id as starting point. + * !is_null -> use this value as starting point. + * + * [$scope]: 'design' -> use custom fields that can be used at design time (specification) + * 'execution' -> use custom fields that can be used at execution time. + * + * [$name_suffix]: must start with '_' (underscore). + * Used when we display in a page several items + * example: + * during test case execution, several test cases + * during testplan design (assign test case to testplan). + * + * that have the same custom fields. + * In this kind of situation we can use the item id as name suffix. + * + * [link_id]: default null + * scope='testplan_design'. + * link_id=testplan_tcversions.id this value is also part of key + * to access CF values on new table that hold values assigned + * to CF used on the 'tesplan_design' scope. + * + * scope='execution' + * link_id=execution id + * + * BUGID 3431 + * scope='design' + * link_id=tcversion id + * + * + * [tplan_id]: default null + * used when scope='execution' and YOU NEED to get input with value + * related to link_id + * + * [tproject_id]: default null + * used to speedup feature when this value is available. + * + * + * returns: html string + * + * rev: 20080811 - franciscom - BUGID 1650 (REQ) + * + * BUGID 3431 - + * + */ + public function html_table_of_custom_field_inputs($id, $parent_id = null, + $scope = 'design', $name_suffix = '', $link_id = null, $tplan_id = null, + $tproject_id = null, $filters = null, $input_values = null) + { + $cf_smarty = ''; + $cf_scope = trim($scope); + $method_name = 'get_linked_cfields_at_' . $cf_scope; + + switch ($cf_scope) { + case 'testplan_design': + $cf_map = $this->$method_name($id, $parent_id, null, $link_id, + null, $tproject_id); + break; + + case 'design': + $cf_map = $this->$method_name($id, $link_id, $parent_id, + $filters, $tproject_id); + break; + + case 'execution': + $cf_map = $this->$method_name($id, $parent_id, null, $link_id, + $tplan_id, $tproject_id); + break; + } + + if (! is_null($cf_map)) { + $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map, + $name_suffix, $input_values); + } + return $cf_smarty; + } + + /** + * Just a Wrapper to improve, sometimes code layout + */ + public function htmlTableOfCFValues($id, $context, $filters = null, + $formatOptions = null) + { + + // $context + $ctx = array( + 'scope' => 'design', + 'execution_id' => null, + 'testplan_id' => null, + 'tproject_id' => null, + 'link_id' => null + ); + + $ctx = array_merge($ctx, $context); + extract($ctx); + + return $this->html_table_of_custom_field_values($id, $scope, $filters, + $execution_id, $testplan_id, $tproject_id, $formatOptions, $link_id); + } + + /* + * function: html_table_of_custom_field_values + * Return html code, implementing a table with custom fields labels + * and custom fields values, for choosen testcase. + * You can think of this function as some sort of read only version + * of html_table_of_custom_field_inputs. + * + * + * args: $id: Very Important!!! + * scope='design' -> this is a testcase id + * scope='execution' -> this is a testcase VERSION id + * scope='testplan_design' -> this is a testcase VERSION id + * + * [$scope]: 'design' -> use custom fields that can be used at design time (specification) + * 'execution' -> use custom fields that can be used at execution time. + * 'testplan_design' + * + * [$filters]:default: null + * + * map with keys: + * + * [show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * include ONLY custom fields that can be viewed + * while user is execution testcases. + * + * 0 or null -> don't filter + * + * [show_on_testplan_design]: default: null + * 1 -> filter on field show_on_testplan_design=1 + * include ONLY custom fields that can be viewed + * while user is designing test plan. + * + * 0 or null -> don't filter + * + * [location] new concept used to define on what location on screen + * custom field will be designed. + * Initally used with CF available for Test cases, to + * implement pre-requisites. + * null => no filtering + * + * More comments/instructions on cfield_mgr->get_linked_cfields_at_design() + * + * + * [$execution_id]: null -> get values for all executions availables for testcase + * !is_null -> only get values or this execution_id + * + * [$testplan_id]: null -> get values for any tesplan to with testcase is linked + * !is_null -> get values only for this testplan. + * + * [$tproject_id] + * [$formatOptions] + * [$link_id]: default null + * scope='testplan_design'. + * link_id=testplan_tcversions.id this value is also part of key + * to access CF values on new table that hold values assigned + * to CF used on the 'tesplan_design' scope. + * + * BUGID 3431 + * scope='design'. + * link_id=tcversion_id + * + * + * + * + * returns: html string + * + */ + public function html_table_of_custom_field_values($id, $scope = 'design', + $filters = null, $execution_id = null, $testplan_id = null, + $tproject_id = null, $formatOptions = null, $link_id = null) + { + $label_css_style = ' class="labelHolder" '; + $value_css_style = ' '; + + $add_table = true; + $table_style = ''; + if (! is_null($formatOptions)) { + $label_css_style = isset($formatOptions['label_css_style']) ? $formatOptions['label_css_style'] : $label_css_style; + + $value_css_style = isset($formatOptions['value_css_style']) ? $formatOptions['value_css_style'] : $value_css_style; + + $add_table = isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; + $table_style = isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; + } + + $cf_smarty = ''; + + $location = null; // no filter + $filterKey = 'location'; + if (isset($filters[$filterKey]) && ! is_null($filters[$filterKey])) { + $location = $filters[$filterKey]; + } + + switch ($scope) { + case 'design': + $cf_map = $this->get_linked_cfields_at_design($id, $link_id, + null, $filters, $tproject_id); + break; + + case 'testplan_design': + $cf_map = $this->get_linked_cfields_at_testplan_design($id, null, + $filters, $link_id, $testplan_id, $tproject_id); + break; + + case 'execution': + $cf_map = $this->get_linked_cfields_at_execution($id, null, + $filters, $execution_id, $testplan_id, $tproject_id, + $location); + break; + } + + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + // if user has assigned a value, then node_id is not null + if (isset($cf_info['node_id']) || + $this->cfg->cfield->show_custom_fields_without_value) { + // true => do not create input in audit log + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, true)); + + $cf_smarty .= " " . + htmlspecialchars($label) . ":" . + "" . + $this->cfield_mgr->string_custom_field_value($cf_info, + $id) . "\n"; + } + } + + if ((trim($cf_smarty) != "") && $add_table) { + $cf_smarty = "" . $cf_smarty . "
    "; + } + } + return $cf_smarty; + } + + // function end + + /* + * function: get_linked_cfields_at_execution + * + * + * args: $id + * [$parent_id] + * [$show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * 0 or null -> don't filter + * //@TODO - 20090718 - franciscom + * // this filter has any sense ? + * // review and remove if needed + * + * + * [$execution_id]: null -> get values for all executions availables for testcase + * !is_null -> only get values or this execution_id + * + * [$testplan_id]: null -> get values for any tesplan to with testcase is linked + * !is_null -> get values only for this testplan. + * + * [$tproject_id]: + * + * returns: hash + * key: custom field id + * value: map with custom field definition, with keys: + * + * id: custom field id + * name + * label + * type + * possible_values + * default_value + * valid_regexp + * length_min + * length_max + * show_on_design + * enable_on_design + * show_on_execution + * enable_on_execution + * display_order + * + */ + public function get_linked_cfields_at_execution($id, $parent_id = null, + $show_on_execution = null, $execution_id = null, $testplan_id = null, + $tproject_id = null, $location = null) + { + $thisMethod = __FUNCTION__; + if (! $tproject_id) { + $tproject_id = $this->getTestProjectFromTestCase($id, $parent_id); + } + + // VERY IMPORTANT WARNING: + // I'm setting node type to test case, but $id is the tcversion_id, because + // execution data is related to tcversion NO testcase + return $this->cfield_mgr->$thisMethod($tproject_id, self::ENABLED, + 'testcase', $id, $execution_id, $testplan_id, 'id', $location); + } + + /* + * function: copyCfieldsDesignValues + * Get all cfields linked to any testcase of this testproject + * with the values presents for $from_id, testcase we are using as + * source for our copy. + * + * args: source: map('id' => testcase id, 'tcversion_id' => testcase id) + * destination: map('id' => testcase id, 'tcversion_id' => testcase id) + * + */ + public function copyCfieldsDesignValues($source, $destination) + { + // Get all cfields linked to any testcase of this test project + // with the values presents for $from_id, testcase we are using as + // source for our copy + $cfmap_from = $this->get_linked_cfields_at_design($source['id'], + $source['tcversion_id']); + + $cfield = null; + if (! is_null($cfmap_from)) { + foreach ($cfmap_from as $key => $value) { + $cfield[$key] = array( + "type_id" => $value['type'], + "cf_value" => $value['value'] + ); + } + } + $this->cfield_mgr->design_values_to_db($cfield, + $destination['tcversion_id'], null, 'tcase_copy_cfields'); + } + + /* + * function: get_linked_cfields_at_testplan_design + * + * + * args: $id + * [$parent_id] + * + * [$filters]:default: null + * + * map with keys: + * + * [show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * include ONLY custom fields that can be viewed + * while user is execution testcases. + * + * 0 or null -> don't filter + * + * [show_on_testplan_design]: default: null + * 1 -> filter on field show_on_testplan_design=1 + * include ONLY custom fields that can be viewed + * while user is designing test plan. + * + * 0 or null -> don't filter + * + * More comments/instructions on cfield_mgr->get_linked_cfields_at_design() + * + * [$link_id]: + * + * [$testplan_id]: null -> get values for any tesplan to with testcase is linked + * !is_null -> get values only for this testplan. + * + * returns: hash + * key: custom field id + * value: map with custom field definition, with keys: + * + * id: custom field id + * name + * label + * type + * possible_values + * default_value + * valid_regexp + * length_min + * length_max + * show_on_design + * enable_on_design + * show_on_execution + * enable_on_execution + * display_order + * + * + */ + public function get_linked_cfields_at_testplan_design($id, $parent_id = null, + $filters = null, $link_id = null, $testplan_id = null, + $tproject_id = null) + { + if (! $tproject_id) { + $tproject_id = $this->getTestProjectFromTestCase($id, $parent_id); + } + + // Warning: + // I'm setting node type to test case, but $id is the tcversion_id, because + // link data is related to tcversion NO testcase + return $this->cfield_mgr->get_linked_cfields_at_testplan_design( + $tproject_id, self::ENABLED, 'testcase', $id, $link_id, $testplan_id); + } + + /** + * returns map with key: + * verbose location (see custom field class $locations) + * value: array with fixed key 'location' + * value: location code + */ + public function buildCFLocationMap() + { + return $this->cfield_mgr->buildLocationMap('testcase'); + } + + /** + * given a set of test cases, will return a map with + * test suites name that form test case path to root test suite. + * + * example: + * + * communication devices [ID 4] + * |__ Subspace channels [ID 20] + * | + * |__ TestCase100 + * | + * |__ short range devices [ID 21] + * |__ TestCase1 + * |__ TestCase2 + * + * if test case set: TestCase100,TestCase1 + * + * 4 Communications + * 20 Communications/Subspace channels + * 21 Communications/Subspace channels/short range devices + * + * + * returns map with key: test suite id + * value: test suite path to root + */ + public function getPathLayered($tcaseSet, $opt = null) + { + static $tsuiteMgr; + if (! $tsuiteMgr) { + $tsuiteMgr = new testsuite($this->db); + } + + $xtree = null; + + $options = array( + 'getTSuiteKeywords' => false + ); + $options = array_merge($options, (array) $opt); + + $idSet = (array) $tcaseSet; + foreach ($idSet as $item) { + $path_info = $this->tree_manager->get_path($item); + $testcase = end($path_info); + + // This check is useful when you have several test cases with same parent test suite + if (! isset($xtree[$testcase['parent_id']]['value'])) { + $level = 0; + + foreach ($path_info as $elem) { + $level ++; + $prefix = isset($xtree[$elem['parent_id']]['value']) ? ($xtree[$elem['parent_id']]['value'] . + '/') : ''; + if ($elem['node_table'] == 'testsuites') { + $xtree[$elem['id']]['value'] = $prefix . $elem['name']; + $xtree[$elem['id']]['level'] = $level; + $xtree[$elem['id']]['data_management'] = null; + } + } + } + + if (null != $xtree && $options['getTSuiteKeywords']) { + $tsSet = array_keys($xtree); + $opkw = array( + 'output' => 'kwname' + ); + $fkw = array( + 'keywordsLikeStart' => '@#' + ); + $iset = (array) $tsuiteMgr->getTSuitesFilteredByKWSet($tsSet, + $opkw, $fkw); + + foreach ($iset as $tsuite_id => $elem) { + foreach ($elem as $e) { + if (null != $e) { + $xtree[$tsuite_id]['data_management'][$e['keyword']] = $e['dyn_string']; + } + } + } + } + } + return $xtree; + } + + // getPathLayered($tcaseSet) + + /** + */ + private function getPathTopSuite($tcaseSet) + { + $xtmas = null; + foreach ($tcaseSet as $item) { + $path_info = $this->tree_manager->get_path($item); + $top = current($path_info); + $xtmas[$item] = array( + 'name' => $top['name'], + 'id' => $top['id'] + ); + } + return $xtmas; + } + + // getPathTopSuite($tcaseSet) + + /* + * function: getByPathName + * pathname format + * Test Project Name::SuiteName::SuiteName::...::Test case name + * + * args: $pathname + * returns: hash + */ + public function getByPathName($pathName, $pathSeparator = '::') + { + $recordset = null; + $retval = null; + + // First get root -> test project name and leaf => test case name + $parts = explode($pathSeparator, $pathName); + $partsQty = count($parts); + $tprojectName = $parts[0]; + $tsuiteName = $parts[$partsQty - 2]; + $tcaseName = end($parts); + + // get all testcases on test project with this name and parent test suite + $recordset = $this->get_by_name($tcaseName, $tsuiteName, $tprojectName); + if (! empty($recordset)) { + foreach ($recordset as $value) { + $dummy = $this->tree_manager->get_full_path_verbose( + $value['id']); + $sx = implode($pathSeparator, current($dummy)) . $pathSeparator . + $tcaseName; + if (strcmp($pathName, $sx) == 0) { + $retval = $value; + break; + } + } + } + return $retval; + } + + /** + */ + public function buildDirectWebLink($base_href, $id, $tproject_id = null) + { + list ($external_id, $prefix, ,) = $this->getExternalID($id, $tproject_id); + + return $base_href . 'linkto.php?tprojectPrefix=' . urlencode($prefix) . + '&item=testcase&id=' . urlencode($external_id); + } + + /** + */ + public function getExternalID($id, $tproject_id = null, $prefix = null) + { + static $root; + static $tcase_prefix; + + if (is_null($prefix)) { + if (is_null($root) || ($root != $tproject_id)) { + list ($tcase_prefix, $root) = $this->getPrefix($id, $tproject_id); + } + } else { + $tcase_prefix = $prefix; + } + $info = $this->getLastVersionInfo($id, array( + 'output' => 'minimun' + )); + if (is_null($info)) { + return []; + } + + $external = $info['tc_external_id']; + $identity = $tcase_prefix . $this->cfg->testcase->glue_character . + $external; + return array( + $identity, + $tcase_prefix, + $this->cfg->testcase->glue_character, + $external + ); + } + + /** + * returns just name, tc_external_id, version. + * this info is normally enough for user feednack. + * + * @param int $id + * test case id + * @param array $accessVersionBy + * 'number' => contains test case version number + * 'id' => contains test case version ID + * + * @param + * array moreFields -> fields from tcversions table + * + * @return array with one element with keys: name,version,tc_external_id + */ + public function get_basic_info($id, $accessVersionBy, $moreFields = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $additionalFields = ''; + if ($moreFields != null) { + $additionalFields = "," . implode(",", $moreFields); + } + + $sql = "/* $debugMsg */ " . + " SELECT NH_TCASE.id, NH_TCASE.name, TCV.version, TCV.tc_external_id, " . + " TCV.id AS tcversion_id, TCV.status $additionalFields" . + " FROM {$this->tables['nodes_hierarchy']} NH_TCASE " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = NH_TCASE.id" . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH_TCV.id "; + + $accessBy = array( + 'number' => 'version', + 'id' => 'id' + ); + $where_clause = ''; + foreach ($accessBy as $key => $field) { + if (isset($accessVersionBy[$key])) { + $where_clause = " WHERE TCV.{$field} = " . + intval($accessVersionBy[$key]); + break; + } + } + $where_clause .= " AND NH_TCASE .id = {$id} "; + $sql .= $where_clause; + return $this->db->get_recordset($sql); + } + + /** + */ + public function create_step($tcversion_id, $step_number, $actions, + $expected_results, $execution_type = TESTCASE_EXECUTION_TYPE_MANUAL) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $ret = array(); + + // defensive programming + $dummy = $this->db->prepare_int($execution_type); + $dummy = (isset($this->execution_types[$dummy])) ? $dummy : TESTCASE_EXECUTION_TYPE_MANUAL; + + $item_id = $this->tree_manager->new_node($tcversion_id, + $this->node_types_descr_id['testcase_step']); + + $k2e = array( + 'actions', + 'expected_results' + ); + $item = new stdClass(); + $item->actions = $actions; + $item->expected_results = $expected_results; + $this->ckEditorCopyAndPasteCleanUp($item, $k2e); + + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['tcsteps']} " . + " (id,step_number,actions,expected_results,execution_type) " . + " VALUES({$item_id},{$step_number},'" . + $this->db->prepare_string($item->actions) . "','" . + $this->db->prepare_string($item->expected_results) . "', " . + $this->db->prepare_int($dummy) . ")"; + + $result = $this->db->exec_query($sql); + $ret = array( + 'msg' => 'ok', + 'id' => $item_id, + 'status_ok' => 1, + 'sql' => $sql + ); + if (! $result) { + $ret['msg'] = $this->db->error_msg(); + $ret['status_ok'] = 0; + $ret['id'] = - 1; + } + return $ret; + } + + /** + */ + public function get_steps($tcversion_id, $step_number = 0, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['options'] = array( + 'fields2get' => '*', + 'accessKey' => null, + 'renderGhostSteps' => true, + 'renderImageInline' => true + ); + + $my['options'] = array_merge($my['options'], (array) $options); + + $step_filter = $step_number > 0 ? " AND step_number = {$step_number} " : ""; + $safe_tcversion_id = $this->db->prepare_int($tcversion_id); + + // build + $f2g = "TCSTEPS.{$my['options']['fields2get']}"; + if ($my['options']['fields2get'] != '*') { + $sof = explode(',', $my['options']['fields2get']); + foreach ($sof as &$ele) { + $ele = 'TCSTEPS.' . $ele; + } + $f2g = implode(',', $sof); + } + $sql = "/* $debugMsg */ " . " SELECT {$f2g} " . + " FROM {$this->tables['tcsteps']} TCSTEPS " . + " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . + " ON NH_STEPS.id = TCSTEPS.id " . + " WHERE NH_STEPS.parent_id = {$safe_tcversion_id} {$step_filter} ORDER BY step_number"; + + if (is_null($my['options']['accessKey'])) { + $result = $this->db->get_recordset($sql); + } else { + $result = $this->db->fetchRowsIntoMap($sql, + $my['options']['accessKey']); + } + + if (! is_null($result) && $my['options']['renderGhostSteps']) { + $sql = "/* $debugMsg */ + SELECT summary,preconditions + FROM {$this->tables['tcversions']} TCV + WHERE TCV.id = $safe_tcversion_id "; + $scan = current($this->db->get_recordset($sql)); + + $xrayScan = null; + foreach ($scan as $fn => $vf) { + if (trim($vf) != '' && strpos($vf, self::NAME_PHOPEN) !== false && + strpos($vf, self::NAME_PHCLOSE) !== false) { + $xrayScan[$fn] = $vf; + } + } + $this->renderGhostSteps($result, $xrayScan); + } + + if (! is_null($result) && $my['options']['renderImageInline']) { + // for attachments we need the Test Case Version ID + // (time ago we used the Test Case ID) + $k2l = count($result); + $gaga = array( + 'actions', + 'expected_results' + ); + for ($idx = 0; $idx < $k2l; $idx ++) { + $this->renderImageAttachments($tcversion_id, $result[$idx], + $gaga); + } + } + + return $result; + } + + /** + */ + public function getStepsSimple($tcversion_id, $step_number = 0, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['options'] = array( + 'fields2get' => 'TCSTEPS.*', + 'accessKey' => null, + 'renderGhostSteps' => true, + 'renderImageInline' => true + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $step_filter = $step_number > 0 ? " AND step_number = {$step_number} " : ""; + $safe_tcversion_id = $this->db->prepare_int($tcversion_id); + + $sql = "/* $debugMsg */ " . " SELECT {$my['options']['fields2get']} " . + " FROM {$this->tables['tcsteps']} TCSTEPS " . + " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . + " ON NH_STEPS.id = TCSTEPS.id " . + " WHERE NH_STEPS.parent_id = {$safe_tcversion_id} {$step_filter} ORDER BY step_number"; + + if (is_null($my['options']['accessKey'])) { + $result = $this->db->get_recordset($sql); + } else { + $result = $this->db->fetchRowsIntoMap($sql, + $my['options']['accessKey']); + } + + return $result; + } + + /** + */ + public function get_step_by_id($step_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT TCSTEPS.* FROM {$this->tables['tcsteps']} TCSTEPS " . + " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . + " ON NH_STEPS.id = TCSTEPS.id " . " WHERE TCSTEPS.id = {$step_id} "; + $result = $this->db->get_recordset($sql); + + return is_null($result) ? $result : $result[0]; + } + + public function get_step_numbers($tcversion_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT TCSTEPS.id, TCSTEPS.step_number FROM {$this->tables['tcsteps']} TCSTEPS " . + " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . + " ON NH_STEPS.id = TCSTEPS.id " . + " WHERE NH_STEPS.parent_id = {$tcversion_id} ORDER BY step_number"; + + return $this->db->fetchRowsIntoMap($sql, 'step_number'); + } + + /** + */ + public function get_latest_step_number($tcversion_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT MAX(TCSTEPS.step_number) AS max_step FROM {$this->tables['tcsteps']} TCSTEPS " . + " JOIN {$this->tables['nodes_hierarchy']} NH_STEPS " . + " ON NH_STEPS.id = TCSTEPS.id " . + " WHERE NH_STEPS.parent_id = {$tcversion_id} "; + + $result = $this->db->get_recordset($sql); + return (! is_null($result) && isset($result[0]['max_step'])) ? $result[0]['max_step'] : 0; + } + + /** + * $step_id can be an array + */ + public function delete_step_by_id($step_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $idSet = implode(',', (array) $step_id); + + // Try to delete any children entity + // Execution Attachment + // Execution result + // + $dummy = " /* $debugMsg */ SELECT id FROM + {$this->tables['attachments']} + WHERE fk_table = 'execution_tcsteps' AND fk_id IN ( SELECT id FROM {$this->tables['execution_tcsteps']} - WHERE tcstep_id IN ($idSet) )"; - - $rs = $this->db->fetchRowsIntoMap($dummy,'id'); - if(!is_null($rs)) { - foreach($rs as $fik => $v) { - deleteAttachment($this->db,$fik,false); - } - } - - // Order is CRITIC due to Foreing Keys - $sqlSet = array(); - $sqlSet[] = "/* $debugMsg */ - DELETE FROM {$this->tables['execution_tcsteps']} - WHERE tcstep_id IN ($idSet)"; - - $whereClause = " WHERE id IN ($idSet) "; - $sqlSet[] = "/* $debugMsg */ DELETE FROM {$this->tables['tcsteps']} {$whereClause} "; - $sqlSet[] = "/* $debugMsg */ DELETE FROM {$this->tables['nodes_hierarchy']} " . - " {$whereClause} AND node_type_id = " . - $this->node_types_descr_id['testcase_step']; - - foreach($sqlSet as $sql) { - $this->db->exec_query($sql); - } - } - - - /** - * - * - * @internal revision - * BUGID 4207 - MSSQL - */ - function set_step_number($step_number) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - foreach($step_number as $step_id => $value) - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['tcsteps']} " . - " SET step_number = {$value} WHERE id = {$step_id} "; - $this->db->exec_query($sql); - } - - } - - /** - * - * - */ - function update_step($step_id,$step_number,$actions,$expected_results,$execution_type) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array(); - - $k2e = array('actions','expected_results'); - $item = new stdClass(); - $item->actions = $actions; - $item->expected_results = $expected_results; - $this->CKEditorCopyAndPasteCleanUp($item,$k2e); - - $sql = "/* $debugMsg */ UPDATE {$this->tables['tcsteps']} " . - " SET step_number=" . $this->db->prepare_int($step_number) . "," . - " actions='" . $this->db->prepare_string($item->actions) . "', " . - " expected_results='" . - $this->db->prepare_string($item->expected_results) . "', " . - " execution_type = " . $this->db->prepare_int($execution_type) . - " WHERE id = " . $this->db->prepare_int($step_id); - - $result = $this->db->exec_query($sql); - $ret = array('msg' => 'ok', 'status_ok' => 1, 'sql' => $sql); - if (!$result) - { - $ret['msg'] = $this->db->error_msg(); - $ret['status_ok']=0; - } - return $ret; - } - - /** - * get by external id - * - * @param mixed filters: - */ - function get_by_external($external_id, $parent_id,$filters=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $recordset = null; - - $my = array(); - $my['filters'] = array('version' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $sql = "/* $debugMsg */ " . - " SELECT DISTINCT NH_TCASE.id,NH_TCASE.name,NH_TCASE_PARENT.id AS parent_id," . - " NH_TCASE_PARENT.name AS tsuite_name, TCV.tc_external_id " . - " FROM {$this->tables['nodes_hierarchy']} NH_TCASE, " . - " {$this->tables['nodes_hierarchy']} NH_TCASE_PARENT, " . - " {$this->tables['nodes_hierarchy']} NH_TCVERSIONS," . - " {$this->tables['tcversions']} TCV " . - " WHERE NH_TCVERSIONS.id=TCV.id " . - " AND NH_TCVERSIONS.parent_id=NH_TCASE.id " . - " AND NH_TCASE_PARENT.id=NH_TCASE.parent_id " . - " AND NH_TCASE.node_type_id = {$this->my_node_type} " . - " AND TCV.tc_external_id=$external_id "; - - $add_filters = ' '; - foreach($my['filters'] as $field => $value) - { - switch($my['filters']) - { - case 'version': - if( !is_null($value) ) - { - $add_filters .= ' AND TCV.version = intval($value) '; - } - } - } - - $sql .= $add_filters; - $sql .= " AND NH_TCASE_PARENT.id = {$parent_id}" ; - $recordset = $this->db->fetchRowsIntoMap($sql,'id'); - return $recordset; - } - - - /** - * for a given set of test cases, search on the ACTIVE version set, - * and returns for each test case, - * a map with: the corresponding MAX(version number), other info - * - * @param mixed $id: test case id can be an array - * @param map $filters OPTIONAL - now only 'cfields' key is supported - * @param map $options OPTIONAL - * - */ - function get_last_active_version($id,$filters=null,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $recordset = null; - $itemSet = implode(',',(array)$id); - - $my = array(); - $my['filters'] = array( 'cfields' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $my['options'] = array( 'max_field' => 'tcversion_id', 'access_key' => 'tcversion_id'); - $my['options'] = array_merge($my['options'], (array)$options); - - - - switch($my['options']['max_field']) { - case 'version': - $maxClause = " SELECT MAX(TCV.version) AS version "; - $selectClause = " SELECT TCV.version AS version "; - break; - - case 'tcversion_id': - $maxClause = " SELECT MAX(TCV.id) AS tcversion_id "; - $selectClause = " SELECT TCV.id AS tcversion_id "; - break; - } - - $sql = "/* $debugMsg */ " . - " {$maxClause}, NH_TCVERSION.parent_id AS testcase_id " . - " FROM {$this->tables['tcversions']} TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . - " ON NH_TCVERSION.id = TCV.id AND TCV.active=1 " . - " AND NH_TCVERSION.parent_id IN ({$itemSet}) " . - " GROUP BY NH_TCVERSION.parent_id " . - " ORDER BY NH_TCVERSION.parent_id "; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcversion_id'); - - $cfSelect = ''; - $cfJoin = ''; - $cfQuery = ''; - $cfQty = 0; - - if( !is_null($recordset) ) { - $or_clause = ''; - $cf_query = ''; - - if( !is_null($my['filters']['cfields']) ) { - $cf_hash = &$my['filters']['cfields']; - $cfQty = count($cf_hash); - $countmain = 1; - - // Build custom fields filter - // do not worry!! it seems that filter criteria is OR, - // but really is an AND, - // OR is needed to do a simple query. - // with processing on recordset becomes an AND - foreach ($cf_hash as $cf_id => $cf_value) { - if ( $countmain != 1 ) { - $cfQuery .= " OR "; - } - if (is_array($cf_value)) { - $count = 1; - - foreach ($cf_value as $value) { - if ($count > 1) { - $cfQuery .= " AND "; - } - $cfQuery .= " ( CFDV.value LIKE '%{$value}%' AND CFDV.field_id = {$cf_id} )"; - $count++; - } - } - else { - $cfQuery .= " ( CFDV.value LIKE '%{$cf_value}%' AND CFDV.field_id = {$cf_id} )"; - } - $countmain++; - } - $cfSelect = ", CFDV.field_id, CFDV.value "; - $cfJoin = " JOIN {$this->tables['cfield_design_values']} CFDV ON CFDV.node_id = TCV.id "; - $cfQuery = " AND ({$cfQuery}) "; - } - - $keySet = implode(',',array_keys($recordset)); - $sql = "/* $debugMsg */ " . - " {$selectClause}, NH_TCVERSION.parent_id AS testcase_id, " . - " TCV.version,TCV.execution_type,TCV.importance,TCV.status {$cfSelect} " . - " FROM {$this->tables['tcversions']} TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . - " ON NH_TCVERSION.id = TCV.id {$cfJoin} " . - " AND NH_TCVERSION.id IN ({$keySet}) {$cfQuery}"; - - $recordset = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key'],database::CUMULATIVE); - - // now loop over result, - // Processing has to be done no matter value of cfQty - // (not doing this has produced in part TICKET 4704,4708) - // entries whose count() < number of custom fields has to be removed - if( !is_null($recordset) ) { - $key2loop = array_keys($recordset); - if($cfQty > 0) { - foreach($key2loop as $key) { - if( count($recordset[$key]) < $cfQty) { - unset($recordset[$key]); - } - else { - $recordset[$key] = $recordset[$key][0]; - unset($recordset[$key]['value']); - unset($recordset[$key]['field_id']); - } - } - } - else { - foreach($key2loop as $key) { - $recordset[$key] = $recordset[$key][0]; - } - } - - if( count($recordset) <= 0 ) { - $recordset = null; - } - } - } - - return $recordset; - } - - - /** - * - */ - function filter_tcversions_by_exec_type($tcversion_id,$exec_type,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $recordset = null; - $itemSet = implode(',',(array)$tcversion_id); - - $my['options'] = array( 'access_key' => 'tcversion_id'); - $my['options'] = array_merge($my['options'], (array)$options); - - - - $sql = "/* $debugMsg */ " . - " SELECT TCV.id AS tcversion_id, NH_TCVERSION.parent_id AS testcase_id, TCV.version " . - " FROM {$this->tables['tcversions']} TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . - " ON NH_TCVERSION.id = TCV.id AND TCV.execution_type={$exec_type}" . - " AND NH_TCVERSION.id IN ({$itemSet}) "; - - $recordset = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']); - return $recordset; - } - - /** - * - * - */ - function filter_tcversions($tcversion_id,$filters,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $recordset = null; - $itemSet = implode(',',(array)$tcversion_id); - - $my['options'] = array( 'access_key' => 'tcversion_id'); - $my['options'] = array_merge($my['options'], (array)$options); - - $sql = "/* $debugMsg */ " . - " SELECT TCV.id AS tcversion_id, NH_TCVERSION.parent_id AS testcase_id, TCV.version " . - " FROM {$this->tables['tcversions']} TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . - " ON NH_TCVERSION.id = TCV.id "; - - if ( !is_null($filters) ) - { - foreach($filters as $key => $value) - { - if( !is_null($value) ) - { - $sql .= " AND TCV.{$key}={$value} "; // Hmmm some problems coming with strings - } - } - } - $sql .= " AND NH_TCVERSION.id IN ({$itemSet}) "; - - $recordset = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']); - return $recordset; - } - - - - /** - * given a test case version id, the provided steps will be analized in order - * to update whole steps/expected results structure for test case version. - * This can result in some step removed, other updated and other new created. - * - */ - function update_tcversion_steps($tcversion_id,$steps) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // delete all current steps (if any exists) - // Attention: - // After addition of test case steps feature, a test case version - // can be root of a subtree that contains the steps. - // Remember we are using (at least on Postgres FK => we need to delete - // in a precise order. - - $stepSet = $this->get_steps($tcversion_id,0, - array('fields2get' => 'id', 'accessKey' => 'id')); - if( count($stepSet) > 0 ) - { - $this->delete_step_by_id(array_keys($stepSet)); - } - - // Now insert steps - $loop2do = count($steps); - for($idx=0; $idx < $loop2do; $idx++) - { - $this->create_step($tcversion_id,$steps[$idx]['step_number'], - $steps[$idx]['actions'], - $steps[$idx]['expected_results'], - $steps[$idx]['execution_type']); - } - } - - /** - * update_last_modified - * - * @internal revision - * 20101016 - franciscom - refixing of BUGID 3849 - */ - function update_last_modified($tcversion_id,$user_id,$time_stamp=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $changed_ts = !is_null($time_stamp) ? $time_stamp : $this->db->db_now(); - $sql = " UPDATE {$this->tables['tcversions']} " . - " SET updater_id=" . $this->db->prepare_int($user_id) . ", " . - " modification_ts = " . $changed_ts . - " WHERE id = " . $this->db->prepare_int($tcversion_id); - $this->db->exec_query($sql); - } - - - /** - * Given a tcversion set, returns a modified set, where only tcversion id - * that has requested values on Custom fields are returned. - * - * @param mixed tcversion_id: can be a single value or an array - * @param map cf_hash: custom fields id plus values - * @param map options: OPTIONAL - * - * @return map key: tcversion_id , - * element: array numerical index with as much element as custom fields - * - * @20170325: Ay! this search on EXACT VALUE not LIKE! - * changed! - */ - function filter_tcversions_by_cfields($tcversion_id,$cf_hash,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $recordset = null; - $itemSet = implode(',',(array)$tcversion_id); - - $my['options'] = array( 'access_key' => 'tcversion_id'); - $my['options'] = array_merge($my['options'], (array)$options); - - $or_clause = ''; - $cf_query = ''; - $cf_qty = count($cf_hash); - - // do not worry!! it seems that filter criteria is OR, but really is an AND, - // OR is needed to do a simple query. - // with processing on recordset becomes an AND - foreach ($cf_hash as $cf_id => $cf_value) - { - $cf_query .= $or_clause . " (CFDV.field_id=" . $cf_id . - " AND CFDV.value LIKE '%{$cf_value}%') "; - $or_clause = ' OR '; - } - - $sql = "/* $debugMsg */ " . - " SELECT TCV.id AS tcversion_id, " . - " NH_TCVERSION.parent_id AS testcase_id, TCV.version," . - " CFDV.field_id,CFDV.value " . - " FROM {$this->tables['tcversions']} TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . - " ON NH_TCVERSION.id = TCV.id " . - " JOIN {$this->tables['cfield_design_values']} CFDV " . - " ON CFDV.node_id = TCV.id " . - " AND NH_TCVERSION.id IN ({$itemSet}) AND ({$cf_query}) "; - - $recordset = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key'],database::CUMULATIVE); - - // now loop over result, entries whose count() < number of custom fields has to be removed - if( !is_null($recordset) ) - { - $key2loop = array_keys($recordset); - foreach($key2loop as $key) - { - if( count($recordset[$key]) < $cf_qty) - { - unset($recordset[$key]); - } - } - if( count($recordset) <= 0 ) - { - $recordset = null; - } - } - return $recordset; - } - - /** - * - * @used-by execSetResults.php - */ - function getExecutionSet($id,$version_id=null,$filters=null,$options=null) - { - // need to understand if possibility of choosing order by - // allow us to replace completely code that seems duplicate - // get_executions. - // - // NHA.node_order ASC, NHA.parent_id ASC, execution_id DESC - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // IMPORTANT NOTICE: keys are field names of executions tables - $my['filters'] = array('tcversion_id' => null,'testplan_id' => null, - 'platform_id' => null, 'build_id' => null); - - - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $my['options'] = array('exec_id_order' => 'DESC'); - $my['options'] = array_merge($my['options'], (array)$options); - - $filterBy = array(); - $filterKeys = array('build_id','platform_id','testplan_id','tcversion_id'); - foreach($filterKeys as $fieldName) - { - $filterBy[$fieldName] = ''; - if( !is_null($my['filters'][$fieldName]) ) - { - $itemSet = implode(',', (array)($my['filters'][$fieldName])); - $filterBy[$fieldName] = " AND E.{$fieldName} IN ({$itemSet}) "; - } - } - - - // -------------------------------------------------------------------- - if( is_array($id) ) - { - $tcid_list = implode(",",$id); - $where_clause = " WHERE NHTCV.parent_id IN ({$tcid_list}) "; - } - else - { - $where_clause = " WHERE NHTCV.parent_id = {$id} "; - } - - if(!is_null($version_id)) - { - if( is_array($version_id) ) - { - foreach($version_id as &$elem) - { - $elem = intval($elem); - } - $where_clause .= ' AND TCV.id IN (' . implode(",",$version_id) . ') '; - } - else - { - if($version_id != self::ALL_VERSIONS) - { - $where_clause .= ' AND TCV.id = ' .intval($version_id); - } - } - } - - - - - $sql = "/* $debugMsg */ SELECT NHTC.name,NHTCV.parent_id AS testcase_id, NHTCV.id AS tcversion_id, " . - " TCV.*, " . - " U.login AS tester_login, U.first AS tester_first_name, U.last AS tester_last_name," . - " E.tester_id AS tester_id,E.id AS execution_id, E.status,E.tcversion_number," . - " E.notes AS execution_notes, E.execution_ts, E.execution_type AS execution_run_type," . - " E.execution_duration," . - " E.build_id AS build_id, B.name AS build_name, B.active AS build_is_active, " . - " B.is_open AS build_is_open,E.platform_id, PLATF.name AS platform_name," . - " E.testplan_id,NHTPLAN.name AS testplan_name,TPTCV.id AS feature_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - - " JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = NHTCV.id " . - $filterBy['testplan_id'] . $filterBy['build_id'] . - $filterBy['platform_id'] . $filterBy['tcversion_id'] . - - " /* To get build name */ " . - " JOIN {$this->tables['builds']} B ON B.id=E.build_id " . - - " /* To get test plan name */ " . - // " JOIN {$this->tables['testplans']} TPLAN ON TPLAN.id = E.testplan_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN ON NHTPLAN.id = E.testplan_id " . - - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.testplan_id = E.testplan_id " . - " AND TPTCV.tcversion_id = E.tcversion_id " . - " AND TPTCV.platform_id = E.platform_id " . - " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = E.tester_id " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLATF ON PLATF.id = E.platform_id " . - $where_clause . - " ORDER BY execution_id {$my['options']['exec_id_order']} "; - - $recordset = $this->db->fetchArrayRowsIntoMap($sql,'id'); - return($recordset ? $recordset : null); - } - - - - /** - * for test case id and filter criteria return set with platforms - * where test case has a version that has been executed. - * - */ - function getExecutedPlatforms($id,$filters=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['filters'] = array( 'version_id' => null,'tplan_id' => null, - 'platform_id' => null, 'build_id' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $my['options'] = array('exec_id_order' => 'DESC'); - $my['options'] = array_merge($my['options'], (array)$options); - - $filterKeys = array('build_id','platform_id','tplan_id'); - foreach($filterKeys as $key) - { - $filterBy[$key] = ''; - if( !is_null($my['filters'][$key]) ) - { - $itemSet = implode(',', (array)$$key); - $filterBy[$key] = " AND e.{$key} IN ({$itemSet}) "; - } - } - - // -------------------------------------------------------------------- - if( is_array($id) ) - { - $tcid_list = implode(",",$id); - $where_clause = " WHERE NHTCV.parent_id IN ({$tcid_list}) "; - } - else - { - $where_clause = " WHERE NHTCV.parent_id = {$id} "; - } - - // if( is_array($version_id) ) - // { - // $versionid_list = implode(",",$version_id); - // $where_clause .= " AND tcversions.id IN ({$versionid_list}) "; - // } - // else - // { - // if($version_id != self::ALL_VERSIONS) - // { - // $where_clause .= " AND tcversions.id = {$version_id} "; - // } - // } - - $sql = "/* $debugMsg */ SELECT DISTINCT e.platform_id,p.name " . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['tcversions']} tcversions ON NHTCV.id = tcversions.id " . - " JOIN {$this->tables['executions']} e ON NHTCV.id = e.tcversion_id " . - " {$filterBy['tplan_id']} {$filterBy['build_id']} {$filterBy['platform_id']} " . - " JOIN {$this->tables['builds']} b ON e.build_id=b.id " . - " LEFT OUTER JOIN {$this->tables['platforms']} p ON p.id = e.platform_id " . - $where_clause; - - $recordset = $this->db->fetchRowsIntoMap($sql,'platform_id'); - return($recordset ? $recordset : null); - } - - - - /** - * - * Solve point to my self - * - *

    added by web rich editor create some layout issues - */ - function renderGhostSteps(&$steps2render, $scan = null) { - $warningRenderException = lang_get('unable_to_render_ghost'); - $loop2do = count($steps2render); - - $tlBeginMark = self::GHOSTBEGIN; - $tlEndMark = self::GHOSTEND; - $tlEndMarkLen = strlen($tlEndMark); - - $key2check = array('actions','expected_results'); - - // I've discovered that working with Web Rich Editor generates - // some additional not wanted entities, that disturb a lot - // when trying to use json_decode(). - // Hope this set is enough. - $replaceSet = array($tlEndMark, '

    ', '

    ',' '); - $replaceSetWebRichEditor = array('

    ', '

    ',' '); - - $rse = &$steps2render; - for($gdx=0; $gdx < $loop2do; $gdx++) { - foreach($key2check as $item_key) { - $deghosted = false; - $start = FALSE; - - if(isset($rse[$gdx][$item_key])) { - $start = strpos($rse[$gdx][$item_key],$tlBeginMark); - $ghost = $rse[$gdx][$item_key]; - } - - if($start !== FALSE) { - $xx = explode($tlBeginMark,$rse[$gdx][$item_key]); - $xx2do = count($xx); - $ghost = ''; - $deghosted = false; - for($xdx=0; $xdx < $xx2do; $xdx++) { - try { - if( ($cutting_point = strpos($xx[$xdx],$tlEndMark)) !== FALSE) { - // here I've made a mistake - // Look at this situation: - // - // ** Original String - // [ghost]"Step":1,"TestCase":"BABA-1","Version":1[/ghost] RIGHT - // - // ** $xx[$xdx] - // "Step":1,"TestCase":"BABA-1","Version":1[/ghost] RIGHT - // Then $ydx = trim(str_replace($replaceSet,'',$xx[$xdx])); - // - // WRONG!!! => "Step":1,"TestCase":"BABA-1","Version":1 - // - // Need to CUT WHERE I have found $tlEndMark - // - $leftside = trim(substr($xx[$xdx],0,$cutting_point)); - $rightside = trim(substr($xx[$xdx],$cutting_point+$tlEndMarkLen)); - $dx = '{' . html_entity_decode(trim($leftside,'\n')) . '}'; - $dx = json_decode($dx,true); - - if(isset($dx['Step'])) { - if( ($xid = $this->getInternalID($dx['TestCase'])) > 0 ) { - // Start looking initially just for ACTIVE Test Case Versions - $vn = isset($dx['Version']) ? intval($dx['Version']) : 0; - if($vn == 0) { - // User wants to follow latest ACTIVE VERSION - $yy = $this->get_last_version_info($xid,array('output' => 'full','active' => 1)); - if(is_null($yy)) { - // seems all versions are inactive, in this situation will get latest - $yy = $this->get_last_version_info($xid,array('output' => 'full')); - } - $vn = intval($yy['version']); - } - - $fi = $this->get_basic_info($xid,array('number' => $vn)); - if(!is_null($fi)) { - if(intval($dx['Step']) > 0) { - $deghosted = true; - $stx = $this->get_steps($fi[0]['tcversion_id'],$dx['Step']); - $ghost .= str_replace($replaceSetWebRichEditor,'',$stx[0][$item_key]) . $rightside; - } - } - } - } else { - // seems we have found a ghost test case INSTEAD OF a GHOST test case STEP - // Then I do a trick creating an artificial 'summary' member - $zorro = array('summary' => $tlBeginMark . $leftside . $tlEndMark); - $this->renderGhost($zorro); - $deghosted = true; - $ghost .= $zorro['summary'] . $rightside; - } - } else { - $ghost = $xx[$xdx]; // 20131022 - } - } catch (Exception $e) { - $deghosted = true; - $ghost .= $warningRenderException . $rse[$gdx][$item_key]; - } - } - } // $start - - if($deghosted) { - $rse[$gdx][$item_key] = $ghost; - } - - if(null != $scan) { - $gaga = implode(',',$scan); - $rse[$gdx][$item_key] = - $this->replaceTextBTWTags($rse[$gdx][$item_key],$gaga); - } - - } - } - } - - /** - * Gets test cases created per user. The test cases are restricted to a - * test plan of a test project. This method performs a query to database - * using the given arguments. - * - * Optional values may be passed in the options array. These optional - * values include tplan_id - Test plan ID. - * - * @param integer $user_id User ID - * @param integer $tproject_id Test Project ID - * @param mixed $options Optional array of options - * @return mixed Array of test cases created per user - */ - function get_created_per_user($user_id, $tproject_id, $options) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $has_options=!is_null($options); - - $sql = "/* $debugMsg */ SELECT ". - "TPROJ.id AS testproject_id, TPTCV.testplan_id, TCV.id AS tcversion_id," . - "TCV.version, TCV.tc_external_id, NHTC.id AS testcase_id, NHTC.name, ". - "TCV.creation_ts, TCV.modification_ts, TPROJ.prefix, U.first AS first_name,". - "U.last AS last_name, U.login, (TPTCV.urgency * TCV.importance) AS priority " . - "FROM testprojects TPROJ, users U JOIN tcversions TCV ON U.id = TCV.author_id " . - "JOIN nodes_hierarchy NHTCV ON TCV.id = NHTCV.id " . - "JOIN nodes_hierarchy NHTC ON NHTCV.parent_id = NHTC.id " . - "LEFT OUTER JOIN testplan_tcversions TPTCV ON TCV.id = TPTCV.tcversion_id " . - "LEFT OUTER JOIN testplans TPLAN ON TPTCV.testplan_id = TPLAN.id " . - "LEFT OUTER JOIN testprojects TPROJ_TPLAN ON TPLAN.testproject_id = TPROJ_TPLAN.id " . - "WHERE TPROJ.id = {$tproject_id}"; - - if($user_id !== 0) { - $sql .= " AND U.id = {$user_id}"; - } - - if( $has_options && isset($options->tplan_id)) { - $sql .= " AND TPTCV.testplan_id = {$options->tplan_id}"; - } - - if( $has_options && isset($options->startTime) ) { - $sql .= " AND TCV.creation_ts >= '{$options->startTime}'"; - } - - if( $has_options && isset($options->endTime) ) { - $sql .= " AND TCV.creation_ts <= '{$options->endTime}'"; - } - - $access_key=array('testplan_id','testcase_id'); - if( $has_options && isset($options->access_keys) ) - { - switch($options->access_keys) - { - case 'testplan_testcase': - $access_key=array('testplan_id','testcase_id'); - break; - - case 'testcase_testplan': - $access_key=array('testcase_id','testplan_id'); - break; - - default: - $access_key=array('testplan_id','testcase_id'); - break; - } - } - - $rs=$this->db->fetchMapRowsIntoMap($sql,$access_key[0],$access_key[1],database::CUMULATIVE); - - if( $has_options && !is_null($rs)) // TBD: Check if we can remove it - { - if( !isset($options->access_keys) || - (is_null($options->access_keys) || $options->access_keys='testplan_testcase') ) - { - $tcaseSet=null; - $main_keys = array_keys($rs); - foreach($main_keys as $maccess_key) - { - $sec_keys = array_keys($rs[$maccess_key]); - foreach($sec_keys as $saccess_key) - { - // is enough I process first element - $item = $rs[$maccess_key][$saccess_key][0]; - if(!isset($tcaseSet[$item['testcase_id']])) - { - $tcaseSet[$item['testcase_id']]=$item['testcase_id']; - } - } - } - - $path_info = $this->tree_manager->get_full_path_verbose($tcaseSet); - - // Remove test project piece and convert to string - $flat_path=null; - foreach($path_info as $tcase_id => $pieces) - { - unset($pieces[0]); - $flat_path[$tcase_id]=implode('/',$pieces); - } - $main_keys = array_keys($rs); - - foreach($main_keys as $idx) - { - $sec_keys = array_keys($rs[$idx]); - foreach($sec_keys as $jdx) - { - $third_keys = array_keys($rs[$idx][$jdx]); - foreach($third_keys as $tdx) - { - $fdx = $rs[$idx][$jdx][$tdx]['testcase_id']; - $rs[$idx][$jdx][$tdx]['tcase_full_path']=$flat_path[$fdx]; - } - } - break; - } - } - } - - return $rs; - } - - /** - * - * - */ - function setExecutionType($tcversionID,$value,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('updSteps' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $execType = $this->db->prepare_int(intval($value)); - $safeTCVID = $this->db->prepare_int($tcversionID); - - $sql = "/* $debugMsg */ " . - " UPDATE {$this->tables['tcversions']} " . - " SET execution_type={$execType} WHERE id = {$safeTCVID} "; - $this->db->exec_query($sql); - - if( $my['opt']['updSteps'] ) - { - $opx = array('fields2get' => 'id'); - $stepIDSet = $this->get_steps($safeTCVID,null,$opx); - - if( !is_null($stepIDSet) ) - { - $target = array(); - foreach($stepIDSet as $elem ) - { - $target[] = $elem['id']; - } - $inClause = implode(',',$target); - $sqlX = " UPDATE {$this->tables['tcsteps']} " . - " SET execution_type={$execType} WHERE id IN (" . $inClause . ")"; - $this->db->exec_query($sqlX); - } - } - - return array($value,$execType,$sql); - } - - - /** - * - * - */ - function setEstimatedExecDuration($tcversionID,$value) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe = trim($value); - $safe = is_numeric($safe) ? $safe : null; - - $sql = "/* $debugMsg */ " . - " UPDATE {$this->tables['tcversions']} " . - " SET estimated_exec_duration=" . ((is_null($safe) || $safe == '') ? 'NULL' : $safe) . - " WHERE id = " . $this->db->prepare_int($tcversionID); - $this->db->exec_query($sql); - return array($value,$safe,$sql); - } - - - - /** - * @param map $identity: id, version_id - * @param map $execContext: tplan_id, platform_id,build_id - * @internal revisions - * - * @since 1.9.4 - **/ - function getLatestExecSingleContext($identity,$execContext,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $cfg = config_get('results'); - $status_not_run = $cfg['status_code']['not_run']; - - $my = array('opt' => array('output' => 'full')); - $my['opt'] = array_merge($my['opt'],(array)$options); - $safeContext = $execContext; - $safeIdentity = $identity; - foreach($safeContext as &$ele) - { - $ele = intval($ele); - } - foreach($safeIdentity as &$ele) - { - $ele = intval($ele); - } - - // dammed names!!! - $safeContext['tplan_id'] = isset($safeContext['tplan_id']) ? $safeContext['tplan_id'] : $safeContext['testplan_id']; - - - // we have to manage following situations - // 1. we do not know test case version id. - if($safeIdentity['version_id'] > 0) - { - $addJoinLEX = ''; - $addWhereLEX = " AND EE.tcversion_id = " . $safeIdentity['version_id']; - $addWhere = " AND TPTCV.tcversion_id = " . $safeIdentity['version_id']; - } - else - { - $addJoinLEX = " JOIN {$this->tables['nodes_hierarchy']} H2O " . - " ON H2O.id = EE.tcversion_id "; - $addWhereLEX = " AND H2O.parent_id = " . $safeIdentity['id']; - $addWhere = " AND NHTC.id = " . $safeIdentity['id']; - } - - $sqlLEX = ' SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id,' . - ' MAX(EE.id) AS id ' . - " FROM {$this->tables['executions']} EE " . - $addJoinLEX . - ' WHERE EE.testplan_id = ' . $safeContext['tplan_id'] . - ' AND EE.platform_id = ' . $safeContext['platform_id'] . - ' AND EE.build_id = ' . $safeContext['build_id'] . - $addWhereLEX . - ' GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id ,EE.build_id '; - - $out = null; - switch($my['opt']['output']) - { - case 'exec_id': - $dummy = $this->db->get_recordset($sqlLEX); - $out = (!is_null($dummy) ? $dummy[0]['id'] : null); - break; - - case 'timestamp': - $sql= "/* $debugMsg */ SELECT E.id AS execution_id, " . - " COALESCE(E.status,'{$status_not_run}') AS status," . - " NHTC.id AS testcase_id, TCV.id AS tcversion_id, E.execution_ts" . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id" . - " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - - " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.platform_id = TPTCV.platform_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.build_id = {$safeContext['build_id']} " . - - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.id = LEX.id " . - - " WHERE TPTCV.testplan_id = {$safeContext['tplan_id']} " . - " AND TPTCV.platform_id = {$safeContext['platform_id']} " . - $addWhere . - " AND (E.build_id = {$safeContext['build_id']} OR E.build_id IS NULL)"; - - // using database::CUMULATIVE is just a trick to return data structure - // that will be liked on execSetResults.php - $out = $this->db->fetchRowsIntoMap($sql,'testcase_id',database::CUMULATIVE); - break; - - - case 'full': - default: - $sql= "/* $debugMsg */ SELECT E.id AS execution_id, " . - " COALESCE(E.status,'{$status_not_run}') AS status, E.execution_type AS execution_run_type," . - " NHTC.name, NHTC.id AS testcase_id, NHTC.parent_id AS tsuite_id," . - " TCV.id AS tcversion_id,TCV.tc_external_id,TCV.version,TCV.summary," . - " TCV.preconditions,TCV.importance,TCV.author_id," . - " TCV.creation_ts,TCV.updater_id,TCV.modification_ts,TCV.active," . - " TCV.is_open,TCV.execution_type," . - " U.login AS tester_login,U.first AS tester_first_name," . - " U.last AS tester_last_name, E.tester_id AS tester_id," . - " E.notes AS execution_notes, E.execution_ts, E.build_id,E.tcversion_number," . - " B.name AS build_name, B.active AS build_is_active, B.is_open AS build_is_open," . - " COALESCE(PLATF.id,0) AS platform_id,PLATF.name AS platform_name, TPTCV.id AS feature_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id" . - " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - - " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.platform_id = TPTCV.platform_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.build_id = {$safeContext['build_id']} " . - - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.id = LEX.id " . - - " JOIN {$this->tables['builds']} B ON B.id = {$safeContext['build_id']} " . - " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = E.tester_id " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLATF ON PLATF.id = {$safeContext['platform_id']} " . - " WHERE TPTCV.testplan_id = {$safeContext['tplan_id']} " . - " AND TPTCV.platform_id = {$safeContext['platform_id']} " . - $addWhere . - " AND (E.build_id = {$safeContext['build_id']} OR E.build_id IS NULL)"; - - // using database::CUMULATIVE is just a trick to return data structure - // that will be liked on execSetResults.php - $out = $this->db->fetchRowsIntoMap($sql,'testcase_id',database::CUMULATIVE); - break; - } - return $out; - } - - /** - * - * DBExec means we do not considered NOT RUN, because are not written to DB. - * @param map $identity: id, version_id - * @param map $execContext: tplan_id, platform_id - * @internal revisions - * - * @since 1.9.4 - **/ - function getLatestDBExecPlatformContext($identity,$execContext,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $cfg = config_get('results'); - $status_not_run = $cfg['status_code']['not_run']; - - $my = array('opt' => array('output' => 'full')); - $my['opt'] = array_merge($my['opt'],(array)$options); - $safeContext = $execContext; - $safeIdentity = $identity; - foreach($safeContext as &$ele) - { - $ele = intval($ele); - } - foreach($safeIdentity as &$ele) - { - $ele = intval($ele); - } - - // we have to manage following situations - // 1. we do not know test case version id. - if($safeIdentity['version_id'] > 0) - { - $addJoinLEX = ''; - $addWhereLEX = " AND EE.tcversion_id = " . $safeIdentity['version_id']; - $addWhere = " AND TPTCV.tcversion_id = " . $safeIdentity['version_id']; - } - else - { - $addJoinLEX = " JOIN {$this->tables['nodes_hierarchy']} H2O " . - " ON H2O.id = EE.tcversion_id "; - $addWhereLEX = " AND H2O.parent_id = " . $safeIdentity['id']; - $addWhere = " AND NHTC.id = " . $safeIdentity['id']; - } - - $sqlLEX = ' SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,' . - ' MAX(EE.id) AS id ' . - " FROM {$this->tables['executions']} EE " . - $addJoinLEX . - ' WHERE EE.testplan_id = ' . $safeContext['tplan_id'] . - ' AND EE.platform_id = ' . $safeContext['platform_id'] . - $addWhereLEX . - ' GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id'; - - $out = null; - switch($my['opt']['output']) - { - case 'exec_id': - $dummy = $this->db->get_recordset($sqlLEX); - $out = (!is_null($dummy) ? $dummy[0]['id'] : null); - break; - - case 'full': - default: - $sql= "/* $debugMsg */ SELECT E.id AS execution_id, " . - " COALESCE(E.status,'{$status_not_run}') AS status, E.execution_type AS execution_run_type," . - " NHTC.name, NHTC.id AS testcase_id, NHTC.parent_id AS tsuite_id," . - " TCV.id AS tcversion_id,TCV.tc_external_id,TCV.version,TCV.summary," . - " TCV.preconditions,TCV.importance,TCV.author_id," . - " TCV.creation_ts,TCV.updater_id,TCV.modification_ts,TCV.active," . - " TCV.is_open,TCV.execution_type," . - " U.login AS tester_login,U.first AS tester_first_name," . - " U.last AS tester_last_name, E.tester_id AS tester_id," . - " E.notes AS execution_notes, E.execution_ts, E.build_id,E.tcversion_number," . - " B.name AS build_name, B.active AS build_is_active, B.is_open AS build_is_open," . - " COALESCE(PLATF.id,0) AS platform_id,PLATF.name AS platform_name, TPTCV.id AS feature_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id" . - " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - - " JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.platform_id = TPTCV.platform_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEX.id " . - - " JOIN {$this->tables['builds']} B ON B.id = E.build_id " . - " JOIN {$this->tables['users']} U ON U.id = E.tester_id " . - - " /* Left outer on Platforms because Test plan can have NO PLATFORMS */ " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLATF " . - " ON PLATF.id = {$safeContext['platform_id']} " . - " WHERE TPTCV.testplan_id = {$safeContext['tplan_id']} " . - " AND TPTCV.platform_id = {$safeContext['platform_id']} " . - $addWhere; - - // using database::CUMULATIVE is just a trick to return data structure - // that will be liked on execSetResults.php - $out = $this->db->fetchRowsIntoMap($sql,'testcase_id',database::CUMULATIVE); - break; - } - - return $out; - } - - /** - * - */ - function getExecution($execID,$tcversionID) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT NHTC.name,NHTCV.parent_id AS testcase_id, NHTCV.id AS tcversion_id, " . - " TCV.*, " . - " U.login AS tester_login, U.first AS tester_first_name, U.last AS tester_last_name," . - " E.tester_id AS tester_id,E.id AS execution_id, E.status,E.tcversion_number," . - " E.notes AS execution_notes, E.execution_ts, E.execution_type AS execution_run_type," . - " E.build_id AS build_id, B.name AS build_name, B.active AS build_is_active, " . - " B.is_open AS build_is_open,E.platform_id, PLATF.name AS platform_name," . - " E.testplan_id,NHTPLAN.name AS testplan_name,TPTCV.id AS feature_id, TPLAN.api_key AS testplan_api_key" . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - " JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = NHTCV.id " . - " /* To get build name */ " . - " JOIN {$this->tables['builds']} B ON B.id=E.build_id " . - " /* To get test plan name */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN ON NHTPLAN.id = E.testplan_id " . - - " JOIN {$this->tables['testplans']} TPLAN ON TPLAN.id = E.testplan_id " . - - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.testplan_id = E.testplan_id " . - " AND TPTCV.tcversion_id = E.tcversion_id " . - " AND TPTCV.platform_id = E.platform_id " . - " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = E.tester_id " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLATF ON PLATF.id = E.platform_id " . - " WHERE E.id = " . intval($execID) . " AND E.tcversion_id = " . intval($tcversionID); - $rs = $this->db->get_recordset($sql); - return ($rs ? $rs : null); - } - - - /** - * - */ - public function getAuditSignature($context,$options = null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $key2check = array('tcversion_id','id'); - $safeID = array(); - foreach($key2check as $idx => $key ) { - if( property_exists($context, $key) ) { - $safeID[$key] = intval($context->$key); - } - else { - $safeID[$key] = -1; - } - } - - if( $safeID['id'] <= 0 && $safeID['tcversion_id'] > 0 ) { - $node = $this->tree_manager->get_node_hierarchy_info($safeID['tcversion_id']); - $safeID['id'] = $node['parent_id']; - } - - // we need: - // Test Case External ID - // Test Case Name - // Test Case Path - // What about test case version ID ? => only if argument provided - // - $pathInfo = $this->tree_manager->get_full_path_verbose($safeID['id'],array('output_format' => 'id_name')); - $pathInfo = current($pathInfo); - $path = '/' . implode('/',$pathInfo['name']) . '/'; - $tcase_prefix = $this->getPrefix($safeID['id'], $pathInfo['node_id'][0]); - $info = $this->get_last_version_info($safeID['id'], array('output' => 'medium')); - $signature = $path . $tcase_prefix[0] . $this->cfg->testcase->glue_character . - $info['tc_external_id'] . ':' . $info['name']; - - return $signature; - } - - /** - * - */ - public function getTestSuite($id) - { - $dummy = $this->tree_manager->get_node_hierarchy_info($id); - return $dummy['parent_id']; - } - - - function getIdCardByStepID($step_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT NH_TCV.parent_id AS tcase_id, NH_STEPS.parent_id AS tcversion_id" . - " FROM {$this->tables['nodes_hierarchy']} NH_STEPS " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = NH_STEPS.parent_id " . - " WHERE NH_STEPS.id = " . intval($step_id); - $rs = $this->db->get_recordset($sql); - return is_null($rs) ? $rs : $rs[0]; - } - - - /** - * - */ - private function initShowGui($guiObj,$grantsObj,$idCard) { - $id = $idCard->tcase_id; - $goo = is_null($guiObj) ? new stdClass() : $guiObj; - - if( !property_exists($goo, 'closeMyWindow') ) { - $goo->closeMyWindow = 0; - } - - if( !property_exists($goo, 'uploadOp') ) { - $goo->uploadOp = null; - } - - $goo->new_version_source = 'this'; - - $goo->execution_types = $this->execution_types; - $goo->tcase_cfg = $this->cfg->testcase; - $goo->import_limit = TL_REPOSITORY_MAXFILESIZE; - $goo->msg = ''; - $goo->fileUploadMsg = ''; - - $goo->requirement_mgmt = property_exists($grantsObj, 'mgt_modify_req' ) ? $grantsObj->mgt_modify_req : null; - if( is_null($goo->requirement_mgmt)) { - $goo->requirement_mgmt = property_exists($grantsObj, 'requirement_mgmt' ) ? $grantsObj->requirement_mgmt : 0; - } - - // some config options have been migrated to rights - // In order to refactor less code, we will remap to OLD config options present on config file. - $goo->tcase_cfg->can_edit_executed = $grantsObj->testproject_edit_executed_testcases == 'yes' ? 1 : 0; - $goo->tcase_cfg->can_delete_executed = $grantsObj->testproject_delete_executed_testcases == 'yes' ? 1 : 0; - - - $goo->tcase_cfg->can_add_remove_kw_on_executed = 0; - $g2c = 'testproject_add_remove_keywords_executed_tcversions'; - if( property_exists($grantsObj,$g2c) ) { - $goo->tcase_cfg->can_add_remove_kw_on_executed = - ($grantsObj->$g2c == 'yes' ? 1 : 0); - } - - - $goo->view_req_rights = property_exists($grantsObj, 'mgt_view_req') ? $grantsObj->mgt_view_req : 0; - $goo->assign_keywords = property_exists($grantsObj, 'keyword_assignment') ? $grantsObj->keyword_assignment : 0; - $goo->req_tcase_link_management = property_exists($grantsObj, 'req_tcase_link_management') ? $grantsObj->req_tcase_link_management : 0; - - $goo->parentTestSuiteName = ''; - $goo->tprojectName = ''; - $goo->submitCode = ""; - $goo->dialogName = ''; - $goo->bodyOnLoad = ""; - $goo->bodyOnUnload = "storeWindowSize('TCEditPopup')"; - - - $goo->tableColspan = $this->layout->tableToDisplayTestCaseSteps->colspan; - - $goo->tc_current_version = array(); - $goo->status_quo = array(); - $goo->keywords_map = array(); - $goo->arrReqs = array(); - - $goo->cf_current_version = null; - $goo->cf_other_versions = null; - $goo->linked_versions=null; - $goo->platforms = null; - - // add_relation_feedback_msg @used-by testcaseCommands.class.php:doAddRelation() - $viewer_defaults = array('title' => lang_get('title_test_case'),'show_title' => 'no', - 'action' => '', 'msg_result' => '','user_feedback' => '', - 'refreshTree' => 1, 'disable_edit' => 0, - 'display_testproject' => 0,'display_parent_testsuite' => 0, - 'hilite_testcase_name' => 0,'show_match_count' => 0, - 'add_relation_feedback_msg' => ''); - - $viewer_defaults = array_merge($viewer_defaults, (array)$guiObj->viewerArgs); - - $goo->display_testproject = $viewer_defaults['display_testproject']; - $goo->display_parent_testsuite = $viewer_defaults['display_parent_testsuite']; - $goo->show_title = $viewer_defaults['show_title']; - $goo->hilite_testcase_name = $viewer_defaults['hilite_testcase_name']; - $goo->action = $viewer_defaults['action']; - $goo->user_feedback = $viewer_defaults['user_feedback']; - $goo->add_relation_feedback_msg = $viewer_defaults['add_relation_feedback_msg']; - - - $goo->pageTitle = $viewer_defaults['title']; - $goo->display_testcase_path = !is_null($goo->path_info); - $goo->show_match_count = $viewer_defaults['show_match_count']; - if($goo->show_match_count && $goo->display_testcase_path ) { - $goo->pageTitle .= '-' . lang_get('match_count') . ':' . ($goo->match_count = count($goo->path_info)); - } - - $goo->refreshTree = isset($goo->refreshTree) ? $goo->refreshTree : $viewer_defaults['refreshTree']; - $goo->sqlResult = $viewer_defaults['msg_result']; - - // fine grain control of operations - if( $viewer_defaults['disable_edit'] == 1 || ($grantsObj->mgt_modify_tc == false) ) - { - $goo->show_mode = 'editDisabled'; - } - else if( !is_null($goo->show_mode) && $goo->show_mode == 'editOnExec' ) - { - // refers to two javascript functions present in testlink_library.js - // and logic used to refresh both frames when user call this - // method to edit a test case while executing it. - $goo->dialogName='tcview_dialog'; - $goo->bodyOnLoad="dialog_onLoad($guiObj->dialogName)"; - $goo->bodyOnUnload="dialog_onUnload($guiObj->dialogName)"; - $goo->submitCode="return dialog_onSubmit($guiObj->dialogName)"; - - if( !property_exists($goo, 'additionalURLPar') ) { - $goo->additionalURLPar = ''; - } - } - - $dummy = getConfigAndLabels('testCaseStatus','code'); - $goo->domainTCStatus = $dummy['lbl']; - - // editOnExec is part of show_mode Domain - $goo->can_do = $this->getShowViewerActions($goo->show_mode); - $key = 'testcase_freeze'; - if(property_exists($grantsObj, $key)) { - $goo->can_do->freeze = $grantsObj->$key; - } - - $key = 'delete_frozen_tcversion'; - if(property_exists($grantsObj, $key)) { - $goo->can_do->delete_frozen_tcversion = $grantsObj->$key; - } - - $path2root = $this->tree_manager->get_path($id); - $goo->tproject_id = $path2root[0]['parent_id']; - $info = $this->tproject_mgr->get_by_id($goo->tproject_id); - $goo->requirementsEnabled = $info['opt']->requirementsEnabled; - - if( $goo->display_testproject ) { - $goo->tprojectName = $info['name']; - } - - if( $goo->display_parent_testsuite ) { - $parent = count($path2root)-2; - $goo->parentTestSuiteName = $path2root[$parent]['name']; - } - - - $testplans = $this->tproject_mgr->get_all_testplans($goo->tproject_id,array('plan_status' =>1) ); - $goo->has_testplans = !is_null($testplans) && count($testplans) > 0 ? 1 : 0; - - - $platformMgr = new tlPlatform($this->db,$goo->tproject_id); - - $opx = array('enable_on_design' => true, - 'enable_on_execution' => false); - $goo->platforms = $platformMgr->getAllAsMap($opx); - - $goo->tcasePrefix = $this->tproject_mgr->getTestCasePrefix($goo->tproject_id) . $this->cfg->testcase->glue_character; - - $goo->scripts = null; - $goo->tcase_id = $idCard->tcase_id; - $goo->tcversion_id = $idCard->tcversion_id; - $goo->allowStepAttachments = false; - $designEditorCfg = getWebEditorCfg('design'); - $goo->designEditorType = $designEditorCfg['type']; - $stepDesignEditorCfg = getWebEditorCfg('steps_design'); - $goo->stepDesignEditorType = $stepDesignEditorCfg['type']; - - // Add To Testplan button will be disabled if - // the testcase doesn't belong to the current selected testproject - if($idCard->tproject_id == $goo->tproject_id) { - $goo->can_do->add2tplan = ($goo->can_do->add2tplan == 'yes') ? $grantsObj->testplan_planning : 'no'; - } else { - $goo->can_do->add2tplan = 'no'; - } - - return $goo; - } - - /** - * - */ - private function initShowGuiActions(&$gui) - { - - $gui->deleteStepAction = "lib/testcases/tcEdit.php?tproject_id=$gui->tproject_id&show_mode=$gui->show_mode" . - "&doAction=doDeleteStep&step_id="; - - $gui->tcExportAction = "lib/testcases/tcExport.php?tproject_id=$gui->tproject_id&show_mode=$gui->show_mode"; - $gui->tcViewAction = "lib/testcases/archiveData.php?tproject_id={$gui->tproject_id}" . - "&show_mode=$gui->show_mode&tcase_id="; - - $gui->printTestCaseAction = "lib/testcases/tcPrint.php?tproject_id=$gui->tproject_id&show_mode=$gui->show_mode"; - - - $gui->keywordsViewHREF = "lib/keywords/keywordsView.php?tproject_id={$gui->tproject_id} " . - ' target="mainframe" class="bold" title="' . lang_get('menu_manage_keywords') . '"'; - - - $gui->reqSpecMgmtHREF = "lib/general/frmWorkArea.php?tproject_id={$gui->tproject_id}&feature=reqSpecMgmt"; - $gui->reqMgmtHREF = "lib/requirements/reqView.php?tproject_id={$gui->tproject_id}" . - "&showReqSpecTitle=1&requirement_id="; - - $gui->addTc2TplanHREF = "lib/testcases/tcAssign2Tplan.php?tproject_id={$gui->tproject_id}"; - - - } - - - /** - * render Ghost Test Case - */ - function renderGhost(&$item2render) { - $versionTag = '[version:%s]'; - $hint = "(link%s"; - - // $href = '%s:%s' . " $versionTag (link)

    "; - // second \'%s\' needed if I want to use Latest as indication, need to understand - // Javascript instead of javascript, because CKeditor sometimes complains - $href = '%s:%s' . " $versionTag $hint

    "; - $tlBeginMark = '[ghost]'; - $tlEndMark = '[/ghost]'; - $tlEndMarkLen = strlen($tlEndMark); - - $key2check = array('summary','preconditions'); - - // I've discovered that working with Web Rich Editor generates - // some additional not wanted entities, that disturb a lot - // when trying to use json_decode(). - // Hope this set is enough. - // 20130605 - after algorithm change, this seems useless - //$replaceSet = array($tlEndMark, '

    ', '

    ',' '); - // $replaceSetWebRichEditor = array('

    ', '

    ',' '); - - - $rse = &$item2render; - foreach($key2check as $item_key) - { - $start = strpos($rse[$item_key],$tlBeginMark); - $ghost = $rse[$item_key]; - - // There is at least one request to replace ? - if($start !== FALSE) - { - $xx = explode($tlBeginMark,$rse[$item_key]); - - // How many requests to replace ? - $xx2do = count($xx); - $ghost = ''; - for($xdx=0; $xdx < $xx2do; $xdx++) - { - $isTestCaseGhost = true; - - // Hope was not a false request. - // if( strpos($xx[$xdx],$tlEndMark) !== FALSE) - if( ($cutting_point = strpos($xx[$xdx],$tlEndMark)) !== FALSE) - { - // Separate command string from other text - // Theorically can be just ONE, but it depends - // is user had not messed things. - $yy = explode($tlEndMark,$xx[$xdx]); - - if( ($elc = count($yy)) > 0) - { - $dx = $yy[0]; - - // trick to convert to array - $dx = '{' . html_entity_decode(trim($dx,'\n')) . '}'; - $dx = json_decode($dx,true); - - try - { - $xid = $this->getInternalID($dx['TestCase']); - if( $xid > 0 ) - { - $linkFeedback=")"; - $addInfo=""; - $vn = isset($dx['Version']) ? intval($dx['Version']) : 0; - if($vn == 0) - { - // User wants to follow latest ACTIVE VERSION - $zorro = $this->get_last_version_info($xid,array('output' => 'full','active' => 1)); - $linkFeedback=" to Latest ACTIVE Version)"; - if(is_null($zorro)) - { - // seems all versions are inactive, in this situation will get latest - $zorro = $this->get_last_version_info($xid,array('output' => 'full')); - $addInfo = " - All versions are inactive!!"; - $linkFeedback=" to Latest Version{$addInfo})"; - } - $vn = intval($zorro['version']); - } - - $fi = $this->get_basic_info($xid,array('number' => $vn)); - if(!is_null($fi)) - { - if( isset($dx['Step']) ) - { - $isTestCaseGhost = false; - - // ghost for rendering Test Case Step (string display) - // [ghost]"Step":1,"TestCase":"MOK-2","Version":1[/ghost] - // - // ATTENTION REMEMBER THAT ALSO CAN BE: - // [ghost]"Step":1,"TestCase":"MOK-2","Version":""[/ghost] - // [ghost]"Step":1,"TestCase":"MOK-2"[/ghost] - // - if(intval($dx['Step']) > 0) - { - $deghosted = true; - $rightside = trim(substr($xx[$xdx],$cutting_point+$tlEndMarkLen)); - $stx = $this->get_steps($fi[0]['tcversion_id'],$dx['Step']); - - $ghost .= $stx[0]['actions'] . $rightside; - } - } - else - { - // ghost for rendering Test Case (create link) - $ghost .= sprintf($href,$dx['TestCase'],$vn,$dx['TestCase'],$fi[0]['name'],$vn,$linkFeedback); - } - } - } - - if($isTestCaseGhost) - { - $lim = $elc-1; - for($cpx=1; $cpx <= $lim; $cpx++) - { - $ghost .= $yy[$cpx]; - } - } - } - catch (Exception $e) - { - $ghost .= $rse[$item_key]; - } - } - - } - else - { - $ghost .= $xx[$xdx]; - } - } - } - - if($ghost != '') - { - $rse[$item_key] = $ghost; - } - } - } - - /** - * - * - * - */ - function setImportance($tcversionID,$value) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " UPDATE {$this->tables['tcversions']} " . - " SET importance=" . $this->db->prepare_int($value) . - " WHERE id = " . $this->db->prepare_int($tcversionID); - $this->db->exec_query($sql); - } - - /** - * - * - * - */ - function setStatus($tcversionID,$value) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " UPDATE {$this->tables['tcversions']} " . - " SET status=" . $this->db->prepare_int($value) . - " WHERE id = " . $this->db->prepare_int($tcversionID); - $this->db->exec_query($sql); - } - - - /** - * updateSimpleFields - * used to update fields of type int, string on test case version - * - * @param int $tcversionID item ID to update - * @param hash $fieldsValues key DB field to update - * supported fields: - * summary,preconditions,execution_type,importance,status, - * updater_id,estimated_exec_duration - * - * @internal revisions - * - */ - function updateSimpleFields($tcversionID,$fieldsValues) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $fieldsConvertions = array('summary' => 'prepare_string','preconditions' => 'prepare_string', - 'execution_type' => 'prepare_int', 'importance' => 'prepare_int', - 'status' => 'prepare_int', 'estimated_exec_duration' => null, - 'updater_id' => null); - $dummy = null; - $sql = null; - $ddx = 0; - foreach($fieldsConvertions as $fkey => $fmethod) - { - if( isset($fieldsValues[$fkey]) ) - { - $dummy[$ddx] = $fkey . " = "; - if( !is_null($fmethod) ) - { - $sep = ($fmethod == 'prepare_string') ? "'" : ""; - $dummy[$ddx] .= $sep . $this->db->$fmethod($fieldsValues[$fkey]) . $sep; - } - else - { - $dummy[$ddx] .= $fieldsValues[$fkey]; - } - $ddx++; - } - } - if( !is_null($dummy) ) - { - $sqlSET = implode(",",$dummy); - $sql = "/* {$debugMsg} */ UPDATE {$this->tables['tcversions']} " . - "SET {$sqlSET} WHERE id={$tcversionID}"; - - $this->db->exec_query($sql); - } - return $sql; - } - - - /** - * updateName - * check for duplicate name under same parent - * - * @param int $id test case id - * @param string $name - * - * @used-by XML-RPC API - */ - function updateName($id,$name) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret['status_ok'] = true; - $ret['msg'] = 'ok'; - $ret['debug'] = ''; - $ret['API_error_code'] = 0; - - $field_size = config_get('field_size'); - $new_name = trim($name); - - if( ($nl = mb_strlen($new_name)) <= 0 ) { - $ret['status_ok'] = false; - $ret['API_error_code'] = 'TESTCASE_EMPTY_NAME'; - $ret['msg'] = lang_get('API_' . $ret['API_error_code']); - } - - if( $ret['status_ok'] && $nl > $field_size->testcase_name) { - $ret['status_ok'] = false; - $ret['API_error_code'] = 'TESTCASE_NAME_LEN_EXCEEDED'; - $ret['msg'] = sprintf(lang_get('API_' . $ret['API_error_code']),$nl,$field_size->testcase_name); - } - - if( $ret['status_ok'] ) { - // Go ahead - $check = $this->tree_manager->nodeNameExists($name,$this->my_node_type,$id); - $ret['status_ok'] = !$check['status']; - $ret['API_error_code'] = 'TESTCASE_SIBLING_WITH_SAME_NAME_EXISTS'; - $ret['msg'] = sprintf(lang_get('API_' . $ret['API_error_code']),$name); - $ret['debug'] = ''; - } - - if($ret['status_ok']) { - $rs = $this->tree_manager->get_node_hierarchy_info($id); - if( !is_null($rs) && $rs['node_type_id'] == $this->my_node_type) { - $sql = "/* {$debugMsg} */ UPDATE {$this->tables['nodes_hierarchy']} " . - " SET name='" . $this->db->prepare_string($name) . "' " . - " WHERE id= {$id}"; - $this->db->exec_query($sql); - $ret['debug'] = "Old name:{$rs['name']} - new name:{$name}"; - } - } - return $ret; - } - - function getAttachmentTable() { - return $this->attachmentTableName; - } - - /** - * - */ - function updateChangeAuditTrial($tcversion_id,$user_id) - { - $sql = " UPDATE {$this->tables['tcversions']} " . - " SET updater_id=" . $this->db->prepare_int($user_id) . ", " . - " modification_ts = " . $this->db->db_now() . - " WHERE id = " . $this->db->prepare_int(intval($tcversion_id)); - $this->db->exec_query($sql); - } - - /** - * - */ - function getStepsExecInfo($execution_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* {$debugMsg} */ " . - " SELECT id, execution_id,tcstep_id,notes,status FROM {$this->tables['execution_tcsteps']} " . - " WHERE execution_id = " . intval($execution_id); - - $rs = $this->db->fetchRowsIntoMap($sql,'tcstep_id'); - return $rs; - } - - /** - * - */ - function getWorkFlowStatusDomain() { - $dummy = getConfigAndLabels('testCaseStatus','code'); - return $dummy['lbl']; - } - - - /** - * - */ - public function getRelations($id) { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - - $safeID = intval($id); - - $relSet = array(); - $relSet['num_relations'] = 0; - - $dummy = $this->get_by_id($id,self::LATEST_VERSION,null, - array('output' => 'essential','getPrefix' => true, - 'caller' => __FUNCTION__)); - - $relSet['item'] = (null != $dummy) ? current($dummy) : null; - $relSet['relations'] = array(); - - $tproject_mgr = new testproject($this->db); - - $sql = " $debugMsg SELECT id, source_id, destination_id, relation_type, author_id, creation_ts " . - " FROM {$this->tables['testcase_relations']} " . - " WHERE source_id=$safeID OR destination_id=$safeID " . - " ORDER BY id ASC "; - - $relSet['relations']= $this->db->get_recordset($sql); - - if( !is_null($relSet['relations']) && count($relSet['relations']) > 0 ) { - $labels = $this->getRelationLabels(); - $label_keys = array_keys($labels); - foreach($relSet['relations'] as $key => $rel) - { - // is this relation type is configured? - if( ($relTypeAllowed = in_array($rel['relation_type'],$label_keys)) ) - { - $relSet['relations'][$key]['source_localized'] = $labels[$rel['relation_type']]['source']; - $relSet['relations'][$key]['destination_localized'] = $labels[$rel['relation_type']]['destination']; - - $type_localized = 'destination_localized'; - $other_key = 'source_id'; - if ($id == $rel['source_id']) - { - $type_localized = 'source_localized'; - $other_key = 'destination_id'; - } - $relSet['relations'][$key]['type_localized'] = $relSet['relations'][$key][$type_localized]; - $otherItem = $this->get_by_id($rel[$other_key],self::LATEST_VERSION,null, - array('output' => 'full_without_users','getPrefix' => true)); - - - // only add it, if either interproject linking is on or if it is in the same project - $relTypeAllowed = true; - $relSet['relations'][$key]['related_tcase'] = $otherItem[0]; - - $user = tlUser::getByID($this->db,$rel['author_id']); - $relSet['relations'][$key]['author'] = $user->getDisplayName(); - } - - if( !$relTypeAllowed ) - { - unset($relSet['relations'][$key]); - } - - } // end foreach - - $relSet['num_relations'] = count($relSet['relations']); - } - - return $relSet; - } - - - /** - * idCard['tcase_id'] - * idCard['tcversion_id'] - */ - public function getTCVersionRelations($idCard) { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - - $safeID = $idCard; - foreach($safeID as $prop => $val) { - $safeID[$prop] = intval($val); - } - - $getOpt = array('output' => 'essential','getPrefix' => true, - 'caller' => __FUNCTION__); - $relSet = array('num_relations' => 0, 'relations' => array()); - - - $relSet['item'] = current($this->get_by_id($safeID['tcase_id'], - $safeID['tcversion_id'],null,$getOpt)); - - $sql = " $debugMsg " . - " SELECT TR.id, source_id, destination_id, relation_type, " . - " TR.author_id, TR.creation_ts,TR.link_status, " . - " NHTCV_S.parent_id AS tcase_source, " . - " NHTCV_D.parent_id AS tcase_destination " . - " FROM {$this->tables['testcase_relations']} TR " . - " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_D " . - " ON NHTCV_D.id = destination_id " . - " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_S " . - " ON NHTCV_S.id = source_id " . - " WHERE source_id = {$safeID['tcversion_id']} OR " . - " destination_id = {$safeID['tcversion_id']} " . - " ORDER BY id ASC "; - - $relSet['relations']= $this->db->get_recordset($sql); - - if( !is_null($relSet['relations']) && count($relSet['relations']) > 0 ) { - $labels = $this->getRelationLabels(); - $label_keys = array_keys($labels); - - foreach($relSet['relations'] as $key => $rel) { - // is this relation type is configured? - if( ($relTypeAllowed = in_array($rel['relation_type'],$label_keys)) ) { - $relSet['relations'][$key]['source_localized'] = $labels[$rel['relation_type']]['source']; - $relSet['relations'][$key]['destination_localized'] = $labels[$rel['relation_type']]['destination']; - - $type_localized = 'destination_localized'; - $oKeyTCVID = 'source_id'; - $oKeyTCID = 'tcase_source'; - if( $safeID['tcversion_id'] == $rel['source_id'] ) { - $type_localized = 'source_localized'; - $oKeyTCVID = 'destination_id'; - $oKeyTCID = 'tcase_destination'; - } - $otherTCID = $rel[$oKeyTCID]; - $otherTCVID = $rel[$oKeyTCVID]; - - $relSet['relations'][$key]['type_localized'] = $relSet['relations'][$key][$type_localized]; - - $otherItem = $this->get_by_id($otherTCID,$otherTCVID,null, - array('output' => 'full_without_users','getPrefix' => true)); - - - // only add it to output set, if either interproject linking is on - // or if it is in the same project - $relTypeAllowed = true; - $relSet['relations'][$key]['related_tcase'] = $otherItem[0]; - - $user = tlUser::getByID($this->db,$rel['author_id']); - $relSet['relations'][$key]['author'] = $user->getDisplayName(); - } - - if( !$relTypeAllowed ) { - unset($relSet['relations'][$key]); - } - - } // end foreach - - $relSet['num_relations'] = count($relSet['relations']); - } - - return $relSet; - } - - /** - * - */ - public function getTCVRelationsRaw($tcversionID, $opt=null) { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - - $safeID['tcversion_id'] = intval($tcversionID); - - $my = array('opt' => array('side' => null)); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = " $debugMsg " . - " SELECT TR.id, source_id, destination_id, relation_type, " . - " TR.author_id, TR.creation_ts,TR.link_status, " . - " NHTCV_S.parent_id AS tcase_source, " . - " NHTCV_D.parent_id AS tcase_destination " . - " FROM {$this->tables['testcase_relations']} TR " . - " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_D " . - " ON NHTCV_D.id = destination_id " . - " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_S " . - " ON NHTCV_S.id = source_id "; - - switch($my['opt']['side']) { - case 'source': - $where = " WHERE source_id = {$safeID['tcversion_id']} "; - break; - - case 'destination': - case 'dest': - $where = " WHERE destination_id = {$safeID['tcversion_id']} "; - break; - - default: - $where = " WHERE source_id = {$safeID['tcversion_id']} OR " . - " destination_id = {$safeID['tcversion_id']} "; - break; - } - - $sql .= $where; - $relSet= $this->db->fetchRowsIntoMap($sql,'id'); - - return $relSet; - } - - /** - * - */ - public static function getRelationLabels() { - $cfg = config_get('testcase_cfg'); - $labels = $cfg->relations->type_labels; - foreach ($labels as $key => $label) { - $labels[$key] = init_labels($label); - } - return $labels; - } - - - /** - * - */ - public function deleteAllTestCaseRelations($id) { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - - $tcaseSet = (array)$id; - array_walk($tcaseSet,'intval'); - - // @since 1.9.18 - // Relations on test case versions - $tcVIDSet = $this->getAllVersionsID($tcaseSet); - $inValues = implode(',', $tcVIDSet); - $sql = " $debugMsg DELETE FROM {$this->tables['testcase_relations']} " . - " WHERE source_id IN ($inValues) OR " . - " destination_id IN ($inValues) "; - $this->db->exec_query($sql); - } - - - - /** - * checks if there is a relation of a given type between two requirements - * - * @author Andreas Simon - * - * @param integer $first_id ID to check - * @param integer $second_id ID to check - * @param integer $rel_type_id relation type ID to check - * - * @return true, if relation already exists, false if not - */ - public function relationExits($first_id, $second_id, $rel_type_id) - { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - - $safe_first_id = intval($first_id); - $safe_second_id = intval($second_id); - - $sql = " $debugMsg SELECT COUNT(0) AS qty " . - " FROM {$this->tables['testcase_relations']} " . - " WHERE ((source_id=" . $safe_first_id . " AND destination_id=" . $safe_second_id . ") " . - " OR (source_id=" . $safe_second_id . " AND destination_id=" . $safe_first_id . ")) " . - " AND relation_type=" . intval($rel_type_id); - - $rs = $this->db->get_recordset($sql); - return($rs[0]['qty'] > 0); - } - - /** - * Get count of all relations, no matter if it is source or destination - * or what type of relation it is. - * - * @param integer $id requirement ID to check - * - * @return integer $count - */ - public function getRelationsCount($id) { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - $safeID = intval($id); - $sql = " $debugMsg SELECT COUNT(*) AS qty " . - " FROM {$this->tables['testcase_relations']} " . - " WHERE source_id=$safeID OR destination_id=$safeID "; - $rs = $this->db->get_recordset($sql); - return($rs[0]['qty']); - } - - /** - * add a relation of a given type between Test Case Versions - * - * @param integer $source_id: - * ID of source test case version or test case. - * If test case is provided, latest active version - * will be used. - * - * @param integer $destination_id: - * ID of destination test case version or test case - * If test case is provided, latest active version - * will be used. - * - * @param integer $type_id relation type ID to set - * @param integer $author_id user's ID - */ - public function addRelation($source_id, $destination_id, $type_id, $author_id, $ts=null) { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - - // Check if items are test cases or test case versions - // Will check only source - $safeID = array('s' => intval($source_id), - 'd' => intval($destination_id)); - - $extr = array($safeID['s']); - $sql = " SELECT node_type_id,id " . - " FROM {$this->tables['nodes_hierarchy']} " . - " WHERE id IN(" . implode(',', $extr) . ")"; - - $nu = current($this->db->get_recordset($sql)); - - if( $nu['node_type_id'] == $this->node_types_descr_id['testcase'] ) { - // Need to get latest active version for source and dest - $tcvSet = $this->get_last_active_version( - array($safeID['s'],$safeID['d']), null, - array('access_key' => 'testcase_id')); - - // Overwrite - $safeID['s'] = intval($tcvSet[$safeID['s']]['tcversion_id']); - $safeID['d'] = intval($tcvSet[$safeID['d']]['tcversion_id']); - } - - // check if exists before trying to add - if( !$this->relationExits($source_id, $destination_id, $type_id) ) { - // check if related testcase is open - $dummy = $this->get_by_id($destination_id,self::LATEST_VERSION); - if(($dummy[0]['is_open']) == 1){ - $time = is_null($ts) ? $this->db->db_now() : $ts; - $sql = " $debugMsg INSERT INTO {$this->tables['testcase_relations']} " . - " (source_id, destination_id, relation_type, author_id, creation_ts) " . - " values ({$safeID['s']},{$safeID['d']}, $type_id, $author_id, $time)"; - $this->db->exec_query($sql); - $ret = array('status_ok' => true, 'msg' => 'relation_added'); - } else { - $ret = array('status_ok' => false, 'msg' => 'related_tcase_not_open'); - } - } else { - $ret = array('status_ok' => false, 'msg' => 'relation_already_exists'); - } - - return $ret; - } - - - - - - /** - * delete an existing relation - * - * @author Andreas Simon - * - * @param int $id relation id - */ - public function deleteRelationByID($relID) { - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; - $sql = " $debugMsg DELETE FROM {$this->tables['testcase_relations']} WHERE id=" . intval($relID); - $this->db->exec_query($sql); - } - - /** - * - * @return array $htmlSelect info needed to create select box on multiple templates - */ - function getRelationTypeDomainForHTMLSelect() { - $htmlSelect = array('items' => array(), 'selected' => null, 'equal_relations' => array()); - $labels = $this->getRelationLabels(); - - foreach ($labels as $key => $lab) - { - $htmlSelect['items'][$key . "_source"] = $lab['source']; - if ($lab['source'] != $lab['destination']) - { - // relation is not equal as labels for source and dest are different - $htmlSelect['items'][$key . "_destination"] = $lab['destination']; - } - else - { - // mark this as equal relation - no parent/child, makes searching simpler - $htmlSelect['equal_relations'][] = $key . "_source"; - } - } - - // set "related to" as default preselected value in forms - if (defined('TL_REL_TYPE_RELATED') && isset($htmlSelect[TL_REL_TYPE_RELATED . "_source"])) - { - $selected_key = TL_REL_TYPE_RELATED . "_source"; - } - else - { - // "related to" is not configured, so take last element as selected one - $keys = array_keys($htmlSelect['items']); - $selected_key = end($keys); - } - $htmlSelect['selected'] = $selected_key; - - return $htmlSelect; - } - - /** - * exportRelationToXML - * - * Function to export a test case relation to XML. - * - * @param int $relation relation data array - * @param string $troject_id - * - * @return string with XML code - * - * - * testcase external id - * prj - * doc2_id - * testcase external id - * 0 - * - * - * @internal revisions - * - */ - function exportRelationToXML($relation,$item) - { - $xmlStr = ''; - - if(!is_null($relation)) - { - // need to understand if swap is needed, this happens when - // relation type is - // - child_of - // - depends_on - // where item is DESTINATION and NOT SOURCE - if( $relation['source_id'] == $item['testcase_id']) - { - $ele['source_ext_id'] = $item['fullExternalID']; - $ele['destination_ext_id'] = $relation['related_tcase']['fullExternalID']; - } - else - { - // SWAP - $ele['source_ext_id'] = $relation['related_tcase']['fullExternalID']; - $ele['destination_ext_id'] = $item['fullExternalID']; - } - $ele['relation_type'] = $relation['relation_type']; - - $info = array("||SOURCE||" => "source_ext_id","||DESTINATION||" => "destination_ext_id", - "||TYPE||" => "relation_type"); - - $elemTpl = "\t" . "" . "\n\t\t" . "||SOURCE||" ; - $elemTpl .= "\n\t\t" . "||DESTINATION||"; - $elemTpl .= "\n\t\t" . "||TYPE||" . "\n\t" . "" . "\n"; - - $work[] = $ele; - $xmlStr = exportDataToXML($work,"{{XMLCODE}}",$elemTpl,$info,true); - } - - return $xmlStr; - } - - - /** - * Will do analisys IGNORING test plan, platform and build - * get info of execution WRITTEN to DB. - * - */ - function getSystemWideLastestExecutionID($tcversion_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT MAX(e.id) AS execution_id " . - " FROM {$this->tables['executions']} e " . - " WHERE e.tcversion_id = " . intval($tcversion_id); - - - $rs = $this->db->get_recordset($sql); - return intval($rs[0]['execution_id']); - } - - - /** - * render Image Attachments INLINE - * - */ - private function renderImageAttachments($id,&$item2render,$key2check=array('summary','preconditions'),$basehref=null) { - - static $attSet; - static $beginTag; - static $endTag; - static $repoDir; - - if(!$attSet || !isset($attSet[$id])) { - $attSet[$id] = $this->attachmentRepository->getAttachmentInfosFor($id,$this->attachmentTableName,'id'); - $beginTag = '[tlInlineImage]'; - $endTag = '[/tlInlineImage]'; - $repoDir = config_get('repositoryPath'); - } - - if(is_null($attSet[$id])) { - return; - } - - // $href = '%s:%s' . " $versionTag (link)

    "; - // second \'%s\' needed if I want to use Latest as indication, need to understand - // Javascript instead of javascript, because CKeditor sometimes complains - // - // CRITIC: skipCheck is needed to render OK when creating report on Pseudo-Word format. - $bhref = is_null($basehref) ? $_SESSION['basehref'] : $basehref; - - $src = ' src="' . $bhref . - '/lib/attachments/attachmentdownload.php?skipCheck=%sec%&id=%id%">

    '; - $img = '

    0) { - - $atx = $yy[0]; - if( intval($atx) == 0 ) { - $atx = - $this->getTCVersionAttachIDFromTitle($id,$atx); - } - - try { - if(isset($attSet[$id][$atx]) && $attSet[$id][$atx]['is_image']) { - $sec = hash('sha256', $attSet[$id][$atx]['file_name']); - - // Need file dimension!!! - $pathname = $repoDir . $attSet[$id][$atx]['file_path']; - list($iWidth, $iHeight, $iT, $iA) = - getimagesize($pathname); - - $iDim = ' width=' . $iWidth . ' height=' . $iHeight; - $icarus = str_replace(array('%id%','%sec%'),array($atx,$sec), $img); - $ghost .= sprintf($icarus,$iDim); - } - $lim = $elc-1; - for($cpx=1; $cpx <= $lim; $cpx++) { - $ghost .= $yy[$cpx]; - } - } - catch (Exception $e) { - $ghost .= $rse[$item_key]; - } - } - } - else { - // nothing to do - $ghost .= $xx[$xdx]; - } - } - } - - // reconstruct field contents - if($ghost != '') { - $rse[$item_key] = $ghost; - } - } - } - - - /** - * - */ - function trim_and_limit($s, $len = 100) - { - $s = trim($s); - if (tlStringLen($s) > $len) - { - $s = tlSubStr($s, 0, $len); - } - - return $s; - } - - /** - * - */ - function generateTimeStampName($name) - { - return strftime("%Y%m%d-%H:%M:%S", time()) . ' ' . $name; - } - - /** - * - */ - static function getLayout() { - $ly = new stdClass(); - $ly->tableToDisplayTestCaseSteps = new stdClass(); - - // MAGIC: columns are: - //column for reorder, action, expected results, exec type, delete, insert - $ly->tableToDisplayTestCaseSteps->colspan = 6; - - return $ly; - } - - /** - * - */ - function setIntAttrForAllVersions($id,$attr,$value,$forceFrozenVersions=false) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; -// $children = - - - $sql = " UPDATE {$this->tables['tcversions']} " . - " SET {$attr} = " . $this->db->prepare_int($value) ; - - if($forceFrozenVersions==false){ - $sql .= " WHERE is_open=1 AND "; - } - else{ - $sql .= " WHERE "; - } - $sql .= " id IN (" . - " SELECT NHTCV.id FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " WHERE NHTCV.parent_id = " . intval($id) . ")"; - $this->db->exec_query($sql); - } - - - - /** - * - */ - function getTcSearchSkeleton($userInput=null) { - $sk = new stdClass(); - - $sk->creation_date_from = null; - $sk->creation_date_to = null; - $sk->modification_date_from = null; - $sk->modification_date_to = null; - $sk->search_important_notice = ''; - $sk->design_cf = ''; - $sk->keywords = ''; - $sk->filter_by['design_scope_custom_fields'] = false; - $sk->filter_by['keyword'] = false; - $sk->filter_by['requirement_doc_id'] = false; - $sk->option_importance = array(0 => '',HIGH => lang_get('high_importance'),MEDIUM => lang_get('medium_importance'), - LOW => lang_get('low_importance')); - - $dummy = getConfigAndLabels('testCaseStatus','code'); - $sk->domainTCStatus = array(0 => '') + $dummy['lbl']; - $sk->importance = null; - $sk->status = null; - $sk->tcversion = null; - $sk->tcasePrefix = ''; - $sk->targetTestCase = ''; - - $txtin = array("created_by","edited_by","jolly"); - $jollyKilled = array("summary","steps","expected_results","preconditions","name"); - $txtin = array_merge($txtin, $jollyKilled); - - foreach($txtin as $key ) - { - $sk->$key = !is_null($userInput) ? $userInput->$key : ''; - } - - if(!is_null($userInput) && $userInput->jolly != '') - { - foreach($jollyKilled as $key) - { - $sk->$key = ''; - } - } - - return $sk; - } - - /** - * - * - */ - function setIsOpen($id,$tcversion_id,$value) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $bv = (intval($value) > 0) ? 1 : 0; - $sql = " /* $debugMsg */ UPDATE {$this->tables['tcversions']} " . - " SET is_open = {$bv}" . - " WHERE id = " . intval($tcversion_id) ; - - $this->db->exec_query($sql); - } - - /** - * render CF values - * - *

    added by web rich editor create some layout issues - */ - function renderVariables(&$item2render) { - $tcase_id = $item2render['testcase_id']; - $tcversion_id = $item2render['id']; - $cfSet = $this->get_linked_cfields_at_design($tcase_id,$tcversion_id); - - if( is_null($cfSet) ) { - return; - } - - $key2check = array('summary','preconditions'); - $tlBeginTag = '[tlVar]'; - $tlEndTag = '[/tlVar]'; - $tlEndTagLen = strlen($tlEndTag); - - // I've discovered that working with Web Rich Editor generates - // some additional not wanted entities, that disturb a lot - // when trying to use json_decode(). - // Hope this set is enough. - // $replaceSet = array($tlEndTag, '

    ', '

    ',' '); - // $replaceSetWebRichEditor = array('

    ', '

    ',' '); - - - $rse = &$item2render; - foreach($key2check as $item_key) { - $start = strpos($rse[$item_key],$tlBeginTag); - $ghost = $rse[$item_key]; - - // There is at least one request to replace ? - if($start !== FALSE) { - // This way remove may be the

    that webrich editor adds - $play = substr($rse[$item_key],$start); - $xx = explode($tlBeginTag,$play); - - // How many requests to replace ? - $xx2do = count($xx); - $ghost = ''; - for($xdx=0; $xdx < $xx2do; $xdx++) { - - // Hope was not a false request. - if( ($es=strpos($xx[$xdx],$tlEndTag)) !== FALSE) { - // Separate command string from other text - // Theorically can be just ONE, but it depends - // is user had not messed things. - $yy = explode($tlEndTag,$xx[$xdx]); - if( ($elc = count($yy)) > 0) { - $cfname = trim($yy[0]); - try { - // look for the custom field - foreach ($cfSet as $cfID => $cfDef) { - if( $cfDef['name'] === $cfname ) { - $ghost .= - $this->cfield_mgr->string_custom_field_value($cfDef,$tcversion_id); - } - } - - // reconstruct the contect with the other pieces - $lim = $elc-1; - for($cpx=1; $cpx <= $lim; $cpx++) { - $ghost .= $yy[$cpx]; - } - } catch (Exception $e) { - $ghost .= $rse[$item_key]; - } - } - } else { - $ghost .= $xx[$xdx]; - } - } - } - - // reconstruct field contents - if($ghost != '') { - $rse[$item_key] = $ghost; - } - } - } - - /** - * get data about code links from external tool - * - * @param resource &$db reference to database handler - * @param object &$code_interface reference to instance of bugTracker class - * @param integer $tcversion_id Identifier of test case version - * - * @return array list of 'script_name' with values: link_to_cts, - * project_key, repository_name, code_path, branch_name - */ - function getScriptsForTestCaseVersion(&$code_interface,$tcversion_id) { - $tables = tlObjectWithDB::getDBTables(array('tcversions','testcase_script_links')); - $script_list=array(); - - $debugMsg = 'FILE:: ' . __FILE__ . ' :: FUNCTION:: ' . __FUNCTION__; - if( is_object($code_interface) ) - { - - $sql = "/* $debugMsg */ SELECT TSL.*,TCV.version " . - " FROM {$tables['testcase_script_links']} TSL, {$tables['tcversions']} TCV " . - " WHERE TSL.tcversion_id = " . intval($tcversion_id) . - " AND TSL.tcversion_id = TCV.id " . - " ORDER BY TSL.code_path"; - - $map = $this->db->get_recordset($sql); - if( !is_null($map) ) - { - $opt = array(); - foreach($map as $elem) - { - $script_id = $elem['project_key'].'&&'.$elem['repository_name'].'&&'.$elem['code_path']; - if(!isset($script_list[$script_id])) - { - $opt['branch'] = $elem['branch_name']; - $opt['commit_id'] = $elem['commit_id']; - $dummy = $code_interface->buildViewCodeLink($elem['project_key'], - $elem['repository_name'],$elem['code_path'],$opt); - $script_list[$script_id]['link_to_cts'] = $dummy->link; - $script_list[$script_id]['project_key'] = $elem['project_key']; - $script_list[$script_id]['repository_name'] = $elem['repository_name']; - $script_list[$script_id]['code_path'] = $elem['code_path']; - $script_list[$script_id]['branch_name'] = $elem['branch_name']; - $script_list[$script_id]['commit_id'] = $elem['commit_id']; - $script_list[$script_id]['tcversion_id'] = $elem['tcversion_id']; - } - unset($dummy); - } - } - } - - if(count($script_list) === 0) - { - $script_list = null; - } - return($script_list); - } - - - /** - * - */ - function CKEditorCopyAndPasteCleanUp(&$items,$keys) - { - $offending = array(''); - $good = array('<body id="cke_pastebin"','</body>'); - foreach($keys as $fi) - { - $items->$fi = str_ireplace($offending,$good,$items->$fi); - } - } - - /** - * - * - */ - function getPathName($tcase_id) { - - $pathInfo = $this->tree_manager->get_full_path_verbose($tcase_id,array('output_format' => 'id_name')); - $pathInfo = current($pathInfo); - $path = '/' . implode('/',$pathInfo['name']) . '/'; - - $pfx = $this->tproject_mgr->getTestCasePrefix($pathInfo['node_id'][0]); - - $info = $this->get_last_version_info($tcase_id, array('output' => 'medium')); - - $path .= $pfx . $this->cfg->testcase->glue_character . - $info['tc_external_id'] . ':' . $info['name']; - - return $path; - } - - - - /** - * build Test Case Name getting information - * from special marked text inside string - * - * string can be test case summary or test case precondition - */ - function buildTCName($name, $text2scan) { - - $taglen = strlen(self::NAME_PHOPEN); - $whomai = array('l' => '','r' => ''); - - $where['open'] = strpos($name, self::NAME_PHOPEN); - $where['close'] = strpos($name, self::NAME_PHCLOSE); - - if( FALSE !== $where['open'] ) { - $whoami['l'] = substr($name, 0, $where['open']); - $meat = substr($name,$where['open']+$taglen, ($where['close'] - $where['open']-$taglen) ); - - - $dummy = strstr($name,self::NAME_PHCLOSE); - $whoami['r'] = ''; - if( $dummy !== FALSE ) { - $whoami['r'] = ltrim($dummy,self::NAME_PHCLOSE); - } - - $dm = explode(self::NAME_DIVIDE, $meat); - $name = $whoami['l'] . self::NAME_PHOPEN; - - $juice = $this->orangeJuice($text2scan); - $name .= ( count($dm) > 0 ) ? $dm[0] : $meat; - $name .= self::NAME_DIVIDE . $juice . self::NAME_PHCLOSE . $whoami['r']; - } - return $name; - } - - /** - * target => [[All you need]] - * scan4values => [[is Love]] - * - * returned text => All you need::is Love - * - */ - function replaceTextBTWTags($target, $scan4values) { - - $taglen = strlen(self::NAME_PHOPEN); - $side = array('l' => '','r' => ''); - - $where['open'] = strpos($target,self::NAME_PHOPEN); - $where['close'] = strpos($target,self::NAME_PHCLOSE); - - // Both tags present or ... skip - if( FALSE !== $where['open'] && FALSE !== $where['close']) { - - // the needle will NOT BE replaced. - $needle = substr($target, - $where['open'] + $taglen, - ($where['close'] - $where['open'] - $taglen) ); - - // start disecting the target - // first to the left - $side['l'] = substr($target, 0, $where['open']); - - // haystack = $target - // needle = self::NAME_PHCLOSE - $dummy = strstr($target,self::NAME_PHCLOSE); - $whoami['r'] = ''; - - if( $dummy !== FALSE ) { - // dummy => ]]xxxxxxxxxxx - $side['r'] = ltrim($dummy,self::NAME_PHCLOSE); - } - - $dm = explode(self::NAME_DIVIDE, $needle); - $target = $side['l'] . ((count($dm) > 0) ? $dm[0] : $needle); - - $juice = $this->orangeJuice($scan4values); - $target .= self::NAME_DIVIDE . $juice . $side['r']; - } - return $target; - } - - - - /** - * - */ - function orangeJuice($str) { - - $juice = ''; - $taglen = strlen(self::NAME_PHOPEN); - - $where['open'] = strpos($str, self::NAME_PHOPEN); - $where['close'] = strpos($str, self::NAME_PHCLOSE); - - if( FALSE !== $where['open'] ) { - $juice = substr($str,$where['open']+$taglen, ($where['close'] - $where['open']-$taglen) ); - } - return $juice; - } - - /** - * - */ - function getVersionNumber($version_id) { - - $sql = " SELECT version FROM {$this->tables['tcversions']} " . - " WHERE id=" . intval($version_id); - - $rs = $this->db->get_recordset($sql); - - return $rs[0]['version']; - } - - /** - * - */ - function getAllVersionsID( $id ) { - - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; - - $target = (array)$id; - array_walk($target,'intval'); - - $sql = $debugMsg . - " SELECT id AS tcversion_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " WHERE NHTCV.parent_id =" . implode(',',$target) . - " AND NHTCV.node_type_id = " . - $this->node_types_descr_id['testcase_version']; - - $xx = $this->db->fetchRowsIntoMap( $sql, 'tcversion_id' ); - - if( null != $xx && count($xx) > 0 ) { - return array_keys($xx); - } - - return null; - } - - - /** - * - */ - function getAttXMLCfg() { - $attXML = new stdClass(); - - $attXML->root = "\t\n{{XMLCODE}}\t\n"; - $attXML->elemTPL = "\t\t\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\n"; - - $attXML->decode = array ("||ATTACHMENT_ID||" => "id", - "||ATTACHMENT_NAME||" => "name", - "||ATTACHMENT_FILE_TYPE||" => "file_type", - "||ATTACHMENT_FILE_SIZE||" => "file_size", - "||ATTACHMENT_TITLE||" => "title", - "||ATTACHMENT_DATE_ADDED||" => "date_added", - "||ATTACHMENT_CONTENT||" => "content"); - - return $attXML; - } - - - /** - * - */ - function closeOpenTCVRelation( $relationID, $reason ) { - - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; - $sql = " $debugMsg UPDATE {$this->tables['testcase_relations']} " . - " SET link_status = " . intval($reason) . - " WHERE id IN(" . implode(',',$relationID) . ")" . - " AND link_status = " . LINK_TC_RELATION_OPEN; - - $this->db->exec_query($sql); - - // No audit yet - } - - - /** - * - **/ - function copyTCVRelations($source_id,$dest_id) { - - // Step 1 - get existent relations - $relSource = $this->getTCVRelationsRaw($source_id,array('side' => 'source')); - $relDest = $this->getTCVRelationsRaw($source_id,array('side' => 'dest')); - - $ins = "(source_id,destination_id,relation_type," . - " link_status,author_id) "; - - $values = array(); - if( null != $relSource && count($relSource) > 0) { - foreach ($relSource as $key => $elem) { - $stm = "($dest_id,{$elem['destination_id']}," . - "{$elem['relation_type']},{$elem['link_status']}," . - "{$elem['author_id']})"; - $values[] = $stm; - } - } - - if( null != $relDest && count($relDest) > 0) { - foreach ($relDest as $key => $elem) { - $stm = "({$elem['source_id']},$dest_id," . - "{$elem['relation_type']},{$elem['link_status']}," . - "{$elem['author_id']})"; - $values[] = $stm; - } - } - - if( count($values) > 0 ) { - $sql = 'INSERT INTO ' . $this->tables['testcase_relations'] . - $ins . ' VALUES ' . implode(',',$values); - - $this->db->exec_query($sql); - } - - - // public function addRelation($source_id, $destination_id, $type_id, $author_id, $ts=null) { - - } - - - /** - * - */ - function updateCoverage($link,$whoWhen,$opt=null) { - - // Set coverage for previous version to FROZEN & INACTIVE - // Create coverage for NEW Version - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $options = array('freezePrevious' => true); - $options = array_merge($options,(array)$opt); - $safeF = intval($link['source']); - $safeT = intval($link['dest']); - - // Set coverage for previous version to FROZEN & INACTIVE ? - if( $options['freezePrevious'] ) { - $sql = " /* $debugMsg */ " . - " UPDATE {$this->tables['req_coverage']} " . - " SET link_status = " . LINK_TC_REQ_CLOSED_BY_NEW_TCVERSION . "," . - " is_active=0 " . - " WHERE tcversion_id=" . $safeF; - $this->db->exec_query($sql); - } - - // Create coverage for NEW Version - $sql = "/* $debugMsg */ " . - " INSERT INTO {$this->tables['req_coverage']} " . - " (req_id,req_version_id,testcase_id,tcversion_id," . - " author_id,creation_ts) " . - - " SELECT req_id,req_version_id,testcase_id, " . - " {$safeT} AS tcversion_id," . - " {$whoWhen['user_id']} AS author_id, " . - " {$whoWhen['when']} AS creation_ts" . - " FROM {$this->tables['req_coverage']} " . - " WHERE tcversion_id=" . $safeF; - - $this->db->exec_query($sql); - - } - - /** - * - */ - function closeOpenReqLinks( $tcversion_id, $reason, $opt=null ) { - - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; - - $options = array('freeze_req_version' => false); - $options = array_merge($options,(array)$opt); - - $commonWhere = " WHERE tcversion_id = " . intval($tcversion_id) . - " AND link_status = " . LINK_TC_REQ_OPEN; - - - // This has to be done BEFORE changing link_status - if( $options['freeze_req_version'] ) { - - /* execution time issues - $sql = " $debugMsg UPDATE {$this->tables['req_versions']} - SET is_open = 0 - WHERE id IN ( - SELECT req_version_id - FROM {$this->tables['req_coverage']} - $commonWhere - ) "; - */ - switch( DB_TYPE ) { - case 'mysql': - $sql = " $debugMsg + WHERE tcstep_id IN ($idSet) )"; + + $rs = $this->db->fetchRowsIntoMap($dummy, 'id'); + if (! is_null($rs)) { + foreach ($rs as $fik => $v) { + deleteAttachment($this->db, $fik, false); + } + } + + // Order is CRITIC due to Foreing Keys + $sqlSet = array(); + $sqlSet[] = "/* $debugMsg */ + DELETE FROM {$this->tables['execution_tcsteps']} + WHERE tcstep_id IN ($idSet)"; + + $whereClause = " WHERE id IN ($idSet) "; + $sqlSet[] = "/* $debugMsg */ DELETE FROM {$this->tables['tcsteps']} {$whereClause} "; + $sqlSet[] = "/* $debugMsg */ DELETE FROM {$this->tables['nodes_hierarchy']} " . + " {$whereClause} AND node_type_id = " . + $this->node_types_descr_id['testcase_step']; + + foreach ($sqlSet as $sql) { + $this->db->exec_query($sql); + } + } + + /** + * + * @internal revision + * BUGID 4207 - MSSQL + */ + public function set_step_number($step_number) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + foreach ($step_number as $step_id => $value) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['tcsteps']} " . + " SET step_number = {$value} WHERE id = {$step_id} "; + $this->db->exec_query($sql); + } + } + + /** + */ + public function update_step($step_id, $step_number, $actions, + $expected_results, $execution_type) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $ret = array(); + + $k2e = array( + 'actions', + 'expected_results' + ); + $item = new stdClass(); + $item->actions = $actions; + $item->expected_results = $expected_results; + $this->ckEditorCopyAndPasteCleanUp($item, $k2e); + + $sql = "/* $debugMsg */ UPDATE {$this->tables['tcsteps']} " . + " SET step_number=" . $this->db->prepare_int($step_number) . "," . + " actions='" . $this->db->prepare_string($item->actions) . "', " . + " expected_results='" . + $this->db->prepare_string($item->expected_results) . "', " . + " execution_type = " . $this->db->prepare_int($execution_type) . + " WHERE id = " . $this->db->prepare_int($step_id); + + $result = $this->db->exec_query($sql); + $ret = array( + 'msg' => 'ok', + 'status_ok' => 1, + 'sql' => $sql + ); + if (! $result) { + $ret['msg'] = $this->db->error_msg(); + $ret['status_ok'] = 0; + } + return $ret; + } + + /** + * get by external id + * + * @param + * mixed filters: + */ + public function get_by_external($external_id, $parent_id, $filters = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my = array(); + $my['filters'] = array( + 'version' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $sql = "/* $debugMsg */ " . + " SELECT DISTINCT NH_TCASE.id,NH_TCASE.name,NH_TCASE_PARENT.id AS parent_id," . + " NH_TCASE_PARENT.name AS tsuite_name, TCV.tc_external_id " . + " FROM {$this->tables['nodes_hierarchy']} NH_TCASE, " . + " {$this->tables['nodes_hierarchy']} NH_TCASE_PARENT, " . + " {$this->tables['nodes_hierarchy']} NH_TCVERSIONS," . + " {$this->tables['tcversions']} TCV " . + " WHERE NH_TCVERSIONS.id=TCV.id " . + " AND NH_TCVERSIONS.parent_id=NH_TCASE.id " . + " AND NH_TCASE_PARENT.id=NH_TCASE.parent_id " . + " AND NH_TCASE.node_type_id = {$this->my_node_type} " . + " AND TCV.tc_external_id=$external_id "; + + $add_filters = ' '; + foreach ($my['filters'] as $value) { + switch ($my['filters']) { + case 'version': + if (! is_null($value)) { + $add_filters .= ' AND TCV.version = intval($value) '; + } + } + } + + $sql .= $add_filters; + $sql .= " AND NH_TCASE_PARENT.id = {$parent_id}"; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + * for a given set of test cases, search on the ACTIVE version set, + * and returns for each test case, + * a map with: the corresponding MAX(version number), other info + * + * @param mixed $id: + * test case id can be an array + * @param array $filters + * OPTIONAL - now only 'cfields' key is supported + * @param array $options + * OPTIONAL + * + */ + public function get_last_active_version($id, $filters = null, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $recordset = null; + $itemSet = implode(',', (array) $id); + + $my = array(); + $my['filters'] = array( + 'cfields' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $my['options'] = array( + 'max_field' => 'tcversion_id', + 'access_key' => 'tcversion_id' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + switch ($my['options']['max_field']) { + case 'version': + $maxClause = " SELECT MAX(TCV.version) AS version "; + $selectClause = " SELECT TCV.version AS version "; + break; + + case 'tcversion_id': + $maxClause = " SELECT MAX(TCV.id) AS tcversion_id "; + $selectClause = " SELECT TCV.id AS tcversion_id "; + break; + } + + $sql = "/* $debugMsg */ " . + " {$maxClause}, NH_TCVERSION.parent_id AS testcase_id " . + " FROM {$this->tables['tcversions']} TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . + " ON NH_TCVERSION.id = TCV.id AND TCV.active=1 " . + " AND NH_TCVERSION.parent_id IN ({$itemSet}) " . + " GROUP BY NH_TCVERSION.parent_id " . + " ORDER BY NH_TCVERSION.parent_id "; + + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcversion_id'); + + $cfSelect = ''; + $cfJoin = ''; + $cfQuery = ''; + $cfQty = 0; + + if (! is_null($recordset)) { + if (! is_null($my['filters']['cfields'])) { + $cf_hash = &$my['filters']['cfields']; + $cfQty = count($cf_hash); + $countmain = 1; + + // Build custom fields filter + // do not worry!! it seems that filter criteria is OR, + // but really is an AND, + // OR is needed to do a simple query. + // with processing on recordset becomes an AND + foreach ($cf_hash as $cf_id => $cf_value) { + if ($countmain != 1) { + $cfQuery .= " OR "; + } + if (is_array($cf_value)) { + $count = 1; + + foreach ($cf_value as $value) { + if ($count > 1) { + $cfQuery .= " AND "; + } + $cfQuery .= " ( CFDV.value LIKE '%{$value}%' AND CFDV.field_id = {$cf_id} )"; + $count ++; + } + } else { + $cfQuery .= " ( CFDV.value LIKE '%{$cf_value}%' AND CFDV.field_id = {$cf_id} )"; + } + $countmain ++; + } + $cfSelect = ", CFDV.field_id, CFDV.value "; + $cfJoin = " JOIN {$this->tables['cfield_design_values']} CFDV ON CFDV.node_id = TCV.id "; + $cfQuery = " AND ({$cfQuery}) "; + } + + $keySet = implode(',', array_keys($recordset)); + $sql = "/* $debugMsg */ " . + " {$selectClause}, NH_TCVERSION.parent_id AS testcase_id, " . + " TCV.version,TCV.execution_type,TCV.importance,TCV.status {$cfSelect} " . + " FROM {$this->tables['tcversions']} TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . + " ON NH_TCVERSION.id = TCV.id {$cfJoin} " . + " AND NH_TCVERSION.id IN ({$keySet}) {$cfQuery}"; + + $recordset = $this->db->fetchRowsIntoMap($sql, + $my['options']['access_key'], database::CUMULATIVE); + + // now loop over result, + // Processing has to be done no matter value of cfQty + // (not doing this has produced in part TICKET 4704,4708) + // entries whose count() < number of custom fields has to be removed + if (! is_null($recordset)) { + $key2loop = array_keys($recordset); + if ($cfQty > 0) { + foreach ($key2loop as $key) { + if (count($recordset[$key]) < $cfQty) { + unset($recordset[$key]); + } else { + $recordset[$key] = $recordset[$key][0]; + unset($recordset[$key]['value']); + unset($recordset[$key]['field_id']); + } + } + } else { + foreach ($key2loop as $key) { + $recordset[$key] = $recordset[$key][0]; + } + } + + if (empty($recordset)) { + $recordset = null; + } + } + } + + return $recordset; + } + + /** + */ + public function filter_tcversions_by_exec_type($tcversion_id, $exec_type, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $itemSet = implode(',', (array) $tcversion_id); + + $my['options'] = array( + 'access_key' => 'tcversion_id' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = "/* $debugMsg */ " . + " SELECT TCV.id AS tcversion_id, NH_TCVERSION.parent_id AS testcase_id, TCV.version " . + " FROM {$this->tables['tcversions']} TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . + " ON NH_TCVERSION.id = TCV.id AND TCV.execution_type={$exec_type}" . + " AND NH_TCVERSION.id IN ({$itemSet}) "; + + return $this->db->fetchRowsIntoMap($sql, $my['options']['access_key']); + } + + /** + */ + public function filter_tcversions($tcversion_id, $filters, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $itemSet = implode(',', (array) $tcversion_id); + + $my['options'] = array( + 'access_key' => 'tcversion_id' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = "/* $debugMsg */ " . + " SELECT TCV.id AS tcversion_id, NH_TCVERSION.parent_id AS testcase_id, TCV.version " . + " FROM {$this->tables['tcversions']} TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . + " ON NH_TCVERSION.id = TCV.id "; + + if (! is_null($filters)) { + foreach ($filters as $key => $value) { + if (! is_null($value)) { + $sql .= " AND TCV.{$key}={$value} "; // Hmmm some problems coming with strings + } + } + } + $sql .= " AND NH_TCVERSION.id IN ({$itemSet}) "; + + return $this->db->fetchRowsIntoMap($sql, $my['options']['access_key']); + } + + /** + * given a test case version id, the provided steps will be analized in order + * to update whole steps/expected results structure for test case version. + * This can result in some step removed, other updated and other new created. + */ + public function update_tcversion_steps($tcversion_id, $steps) + { + // delete all current steps (if any exists) + // Attention: + // After addition of test case steps feature, a test case version + // can be root of a subtree that contains the steps. + // Remember we are using (at least on Postgres FK => we need to delete + // in a precise order. + $stepSet = (array) $this->get_steps($tcversion_id, 0, + array( + 'fields2get' => 'id', + 'accessKey' => 'id' + )); + if (! empty($stepSet)) { + $this->delete_step_by_id(array_keys($stepSet)); + } + + // Now insert steps + $loop2do = count($steps); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $this->create_step($tcversion_id, $steps[$idx]['step_number'], + $steps[$idx]['actions'], $steps[$idx]['expected_results'], + $steps[$idx]['execution_type']); + } + } + + /** + * update_last_modified + * + * @internal revision + * 20101016 - franciscom - refixing of BUGID 3849 + */ + public function update_last_modified($tcversion_id, $user_id, + $time_stamp = null) + { + $changed_ts = ! is_null($time_stamp) ? $time_stamp : $this->db->db_now(); + $sql = " UPDATE {$this->tables['tcversions']} " . " SET updater_id=" . + $this->db->prepare_int($user_id) . ", " . " modification_ts = " . + $changed_ts . " WHERE id = " . $this->db->prepare_int($tcversion_id); + $this->db->exec_query($sql); + } + + /** + * Given a tcversion set, returns a modified set, where only tcversion id + * that has requested values on Custom fields are returned. + * + * @param + * mixed tcversion_id: can be a single value or an array + * @param + * map cf_hash: custom fields id plus values + * @param + * map options: OPTIONAL + * + * @return array key: tcversion_id , + * element: array numerical index with as much element as custom fields + * + * @20170325: Ay! this search on EXACT VALUE not LIKE! + * changed! + */ + public function filter_tcversions_by_cfields($tcversion_id, $cf_hash, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $recordset = null; + $itemSet = implode(',', (array) $tcversion_id); + + $my['options'] = array( + 'access_key' => 'tcversion_id' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $or_clause = ''; + $cf_query = ''; + $cf_qty = count($cf_hash); + + // do not worry!! it seems that filter criteria is OR, but really is an AND, + // OR is needed to do a simple query. + // with processing on recordset becomes an AND + foreach ($cf_hash as $cf_id => $cf_value) { + $cf_query .= $or_clause . " (CFDV.field_id=" . $cf_id . + " AND CFDV.value LIKE '%{$cf_value}%') "; + $or_clause = ' OR '; + } + + $sql = "/* $debugMsg */ " . " SELECT TCV.id AS tcversion_id, " . + " NH_TCVERSION.parent_id AS testcase_id, TCV.version," . + " CFDV.field_id,CFDV.value " . + " FROM {$this->tables['tcversions']} TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCVERSION " . + " ON NH_TCVERSION.id = TCV.id " . + " JOIN {$this->tables['cfield_design_values']} CFDV " . + " ON CFDV.node_id = TCV.id " . + " AND NH_TCVERSION.id IN ({$itemSet}) AND ({$cf_query}) "; + + $recordset = $this->db->fetchRowsIntoMap($sql, + $my['options']['access_key'], database::CUMULATIVE); + + // now loop over result, entries whose count() < number of custom fields has to be removed + if (! is_null($recordset)) { + $key2loop = array_keys($recordset); + foreach ($key2loop as $key) { + if (count($recordset[$key]) < $cf_qty) { + unset($recordset[$key]); + } + } + if (empty($recordset)) { + $recordset = null; + } + } + return $recordset; + } + + /** + * + * @used-by execSetResults.php + */ + public function getExecutionSet($id, $version_id = null, $filters = null, + $options = null) + { + // need to understand if possibility of choosing order by + // allow us to replace completely code that seems duplicate + // get_executions. + // + // NHA.node_order ASC, NHA.parent_id ASC, execution_id DESC + $debugMsg = $this->debugMsg . __FUNCTION__; + + // IMPORTANT NOTICE: keys are field names of executions tables + $my['filters'] = [ + 'tcversion_id' => null, + 'testplan_id' => null, + 'platform_id' => null, + 'build_id' => null + ]; + + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $my['options'] = array( + 'exec_id_order' => 'DESC' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $filterBy = array(); + $filterKeys = [ + 'build_id', + 'platform_id', + 'testplan_id', + 'tcversion_id' + ]; + + foreach ($filterKeys as $fieldName) { + $filterBy[$fieldName] = ''; // default -> no filter + + if ($fieldName == 'platform_id' && $my['filters'][$fieldName] == - 1) { + continue; + } + + if (! is_null($my['filters'][$fieldName])) { + $itemSet = implode(',', (array) ($my['filters'][$fieldName])); + $filterBy[$fieldName] = " AND E.{$fieldName} IN ({$itemSet}) "; + } + } + + // -------------------------------------------------------------------- + if (is_array($id)) { + $tcid_list = implode(",", $id); + $where_clause = " WHERE NHTCV.parent_id IN ({$tcid_list}) "; + } else { + $where_clause = " WHERE NHTCV.parent_id = {$id} "; + } + + if (! is_null($version_id)) { + if (is_array($version_id)) { + foreach ($version_id as &$elem) { + $elem = intval($elem); + } + $where_clause .= ' AND TCV.id IN (' . implode(",", $version_id) . + ') '; + } else { + if ($version_id != self::ALL_VERSIONS) { + $where_clause .= ' AND TCV.id = ' . intval($version_id); + } + } + } + + $sql = "/* $debugMsg */ SELECT NHTC.name,NHTCV.parent_id AS testcase_id, NHTCV.id AS tcversion_id, " . + " TCV.*, " . + " U.login AS tester_login, U.first AS tester_first_name, U.last AS tester_last_name," . + " E.tester_id AS tester_id,E.id AS execution_id, E.status,E.tcversion_number," . + " E.notes AS execution_notes, E.execution_ts, E.execution_type AS execution_run_type," . + " E.execution_duration," . + " E.build_id AS build_id, B.name AS build_name, B.active AS build_is_active, " . + " B.is_open AS build_is_open,E.platform_id, PLATF.name AS platform_name," . + " E.testplan_id,NHTPLAN.name AS testplan_name,TPTCV.id AS feature_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = NHTCV.id " . $filterBy['testplan_id'] . + $filterBy['build_id'] . $filterBy['platform_id'] . + $filterBy['tcversion_id'] . " /* To get build name */ " . + " JOIN {$this->tables['builds']} B ON B.id=E.build_id " . + " /* To get test plan name */ " . + // " JOIN {$this->tables['testplans']} TPLAN ON TPLAN.id = E.testplan_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN ON NHTPLAN.id = E.testplan_id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.testplan_id = E.testplan_id " . + " AND TPTCV.tcversion_id = E.tcversion_id " . + " AND TPTCV.platform_id = E.platform_id " . + " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = E.tester_id " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLATF ON PLATF.id = E.platform_id " . + $where_clause . + " ORDER BY execution_id {$my['options']['exec_id_order']} "; + + $recordset = $this->db->fetchArrayRowsIntoMap($sql, 'id'); + return $recordset ? $recordset : null; + } + + /** + * for test case id and filter criteria return set with platforms + * where test case has a version that has been executed. + */ + public function getExecutedPlatforms($id, $filters = null, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['filters'] = array( + 'version_id' => null, + 'tplan_id' => null, + 'platform_id' => null, + 'build_id' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $my['options'] = array( + 'exec_id_order' => 'DESC' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $filterKeys = array( + 'build_id', + 'platform_id', + 'tplan_id' + ); + foreach ($filterKeys as $key) { + $filterBy[$key] = ''; + if (! is_null($my['filters'][$key])) { + $itemSet = implode(',', (array) $$key); + $filterBy[$key] = " AND e.{$key} IN ({$itemSet}) "; + } + } + + // -------------------------------------------------------------------- + if (is_array($id)) { + $tcid_list = implode(",", $id); + $where_clause = " WHERE NHTCV.parent_id IN ({$tcid_list}) "; + } else { + $where_clause = " WHERE NHTCV.parent_id = {$id} "; + } + + $sql = "/* $debugMsg */ SELECT DISTINCT e.platform_id,p.name " . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['tcversions']} tcversions ON NHTCV.id = tcversions.id " . + " JOIN {$this->tables['executions']} e ON NHTCV.id = e.tcversion_id " . + " {$filterBy['tplan_id']} {$filterBy['build_id']} {$filterBy['platform_id']} " . + " JOIN {$this->tables['builds']} b ON e.build_id=b.id " . + " LEFT OUTER JOIN {$this->tables['platforms']} p ON p.id = e.platform_id " . + $where_clause; + + $recordset = $this->db->fetchRowsIntoMap($sql, 'platform_id'); + return $recordset ? $recordset : null; + } + + /** + * Solve point to my self + * + *

    added by web rich editor create some layout issues + */ + public function renderGhostSteps(&$steps2render, $scan = null) + { + $warningRenderException = lang_get('unable_to_render_ghost'); + $loop2do = count($steps2render); + + $tlBeginMark = self::GHOSTBEGIN; + $tlEndMark = self::GHOSTEND; + $tlEndMarkLen = strlen($tlEndMark); + + $key2check = array( + 'actions', + 'expected_results' + ); + + // I've discovered that working with Web Rich Editor generates + // some additional not wanted entities, that disturb a lot + // when trying to use json_decode(). + // Hope this set is enough. + // $replaceSet = array($tlEndMark, '

    ', '

    ',' '); + $replaceSetWebRichEditor = array( + '

    ', + '

    ', + ' ' + ); + + $rse = &$steps2render; + for ($gdx = 0; $gdx < $loop2do; $gdx ++) { + foreach ($key2check as $item_key) { + $deghosted = false; + $start = false; + + if (isset($rse[$gdx][$item_key])) { + $start = strpos($rse[$gdx][$item_key], $tlBeginMark); + $ghost = $rse[$gdx][$item_key]; + } + + if ($start !== false) { + $xx = explode($tlBeginMark, $rse[$gdx][$item_key]); + $xx2do = count($xx); + $ghost = ''; + $deghosted = false; + for ($xdx = 0; $xdx < $xx2do; $xdx ++) { + try { + if (($cutting_point = strpos($xx[$xdx], $tlEndMark)) !== + false) { + // here I've made a mistake + // Look at this situation: + // + // ** Original String + // [ghost]"Step":1,"TestCase":"BABA-1","Version":1[/ghost] RIGHT + // + // ** $xx[$xdx] + // "Step":1,"TestCase":"BABA-1","Version":1[/ghost] RIGHT + // Then $ydx = trim(str_replace($replaceSet,'',$xx[$xdx])); + // + // WRONG!!! => "Step":1,"TestCase":"BABA-1","Version":1 + // + // Need to CUT WHERE I have found $tlEndMark + // + $leftside = trim( + substr($xx[$xdx], 0, $cutting_point)); + $rightside = trim( + substr($xx[$xdx], + $cutting_point + $tlEndMarkLen)); + $dx = '{' . + html_entity_decode(trim($leftside, '\n')) . + '}'; + $dx = json_decode($dx, true); + + if (isset($dx['Step'])) { + if (($xid = $this->getInternalID( + $dx['TestCase'])) > 0) { + // Start looking initially just for ACTIVE Test Case Versions + $vn = isset($dx['Version']) ? intval( + $dx['Version']) : 0; + if ($vn == 0) { + // User wants to follow latest ACTIVE VERSION + $yy = $this->getLastVersionInfo( + $xid, + array( + 'output' => 'full', + 'active' => 1 + )); + if (is_null($yy)) { + // seems all versions are inactive, in this situation will get latest + $yy = $this->getLastVersionInfo( + $xid, + array( + 'output' => 'full' + )); + } + $vn = intval($yy['version']); + } + + $fi = $this->get_basic_info($xid, + array( + 'number' => $vn + )); + if (! is_null($fi) && + intval($dx['Step']) > 0) { + $deghosted = true; + $stx = $this->get_steps( + $fi[0]['tcversion_id'], + $dx['Step']); + $ghost .= str_replace( + $replaceSetWebRichEditor, '', + $stx[0][$item_key]) . $rightside; + } + } + } else { + // seems we have found a ghost test case INSTEAD OF a GHOST test case STEP + // Then I do a trick creating an artificial 'summary' member + $zorro = array( + 'summary' => $tlBeginMark . $leftside . + $tlEndMark + ); + $this->renderGhost($zorro); + $deghosted = true; + $ghost .= $zorro['summary'] . $rightside; + } + } else { + $ghost = $xx[$xdx]; // 20131022 + } + } catch (Exception $e) { + $deghosted = true; + $ghost .= $warningRenderException . + $rse[$gdx][$item_key]; + } + } + } // $start + + if ($deghosted) { + $rse[$gdx][$item_key] = $ghost; + } + + if (null != $scan) { + $gaga = implode(',', $scan); + $rse[$gdx][$item_key] = $this->replaceTextBTWTags( + $rse[$gdx][$item_key], $gaga); + } + } + } + } + + /** + * Gets test cases created per user. + * The test cases are restricted to a + * test plan of a test project. This method performs a query to database + * using the given arguments. + * + * Optional values may be passed in the options array. These optional + * values include tplan_id - Test plan ID. + * + * @param integer $user_id + * User ID + * @param integer $tproject_id + * Test Project ID + * @param mixed $options + * Optional array of options + * @return mixed Array of test cases created per user + */ + private function getCreatedPerUser($user_id, $tproject_id, $options) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $has_options = ! is_null($options); + + $sql = "/* $debugMsg */ SELECT " . + "TPROJ.id AS testproject_id, TPTCV.testplan_id, TCV.id AS tcversion_id," . + "TCV.version, TCV.tc_external_id, NHTC.id AS testcase_id, NHTC.name, " . + "TCV.creation_ts, TCV.modification_ts, TPROJ.prefix, U.first AS first_name," . + "U.last AS last_name, U.login, (TPTCV.urgency * TCV.importance) AS priority " . + "FROM testprojects TPROJ, users U JOIN tcversions TCV ON U.id = TCV.author_id " . + "JOIN nodes_hierarchy NHTCV ON TCV.id = NHTCV.id " . + "JOIN nodes_hierarchy NHTC ON NHTCV.parent_id = NHTC.id " . + "LEFT OUTER JOIN testplan_tcversions TPTCV ON TCV.id = TPTCV.tcversion_id " . + "LEFT OUTER JOIN testplans TPLAN ON TPTCV.testplan_id = TPLAN.id " . + "LEFT OUTER JOIN testprojects TPROJ_TPLAN ON TPLAN.testproject_id = TPROJ_TPLAN.id " . + "WHERE TPROJ.id = {$tproject_id}"; + + if ($user_id !== 0) { + $sql .= " AND U.id = {$user_id}"; + } + + if ($has_options && isset($options->tplan_id)) { + $sql .= " AND TPTCV.testplan_id = {$options->tplan_id}"; + } + + if ($has_options && isset($options->startTime)) { + $sql .= " AND TCV.creation_ts >= '{$options->startTime}'"; + } + + if ($has_options && isset($options->endTime)) { + $sql .= " AND TCV.creation_ts <= '{$options->endTime}'"; + } + + $access_key = array( + 'testplan_id', + 'testcase_id' + ); + if ($has_options && isset($options->access_keys)) { + switch ($options->access_keys) { + case 'testplan_testcase': + $access_key = array( + 'testplan_id', + 'testcase_id' + ); + break; + + case 'testcase_testplan': + $access_key = array( + 'testcase_id', + 'testplan_id' + ); + break; + + default: + $access_key = array( + 'testplan_id', + 'testcase_id' + ); + break; + } + } + + $rs = $this->db->fetchMapRowsIntoMap($sql, $access_key[0], + $access_key[1], database::CUMULATIVE); + + if ($has_options && ! is_null($rs) && ! isset($options->access_keys) || + (is_null($options->access_keys) || + $options->access_keys = 'testplan_testcase')) { + $tcaseSet = null; + $main_keys = array_keys($rs); + foreach ($main_keys as $maccess_key) { + $sec_keys = array_keys($rs[$maccess_key]); + foreach ($sec_keys as $saccess_key) { + // is enough I process first element + $item = $rs[$maccess_key][$saccess_key][0]; + if (! isset($tcaseSet[$item['testcase_id']])) { + $tcaseSet[$item['testcase_id']] = $item['testcase_id']; + } + } + } + + $path_info = $this->tree_manager->get_full_path_verbose($tcaseSet); + + // Remove test project piece and convert to string + $flat_path = null; + foreach ($path_info as $tcase_id => $pieces) { + unset($pieces[0]); + $flat_path[$tcase_id] = implode('/', $pieces); + } + $main_keys = array_keys($rs); + + foreach ($main_keys as $idx) { + $sec_keys = array_keys($rs[$idx]); + foreach ($sec_keys as $jdx) { + $third_keys = array_keys($rs[$idx][$jdx]); + foreach ($third_keys as $tdx) { + $fdx = $rs[$idx][$jdx][$tdx]['testcase_id']; + $rs[$idx][$jdx][$tdx]['tcase_full_path'] = $flat_path[$fdx]; + } + } + break; + } + } + + return $rs; + } + + /** + */ + public function setExecutionType($tcversionID, $value, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['opt'] = array( + 'updSteps' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $execType = $this->db->prepare_int(intval($value)); + $safeTCVID = $this->db->prepare_int($tcversionID); + + $sql = "/* $debugMsg */ " . " UPDATE {$this->tables['tcversions']} " . + " SET execution_type={$execType} WHERE id = {$safeTCVID} "; + $this->db->exec_query($sql); + + if ($my['opt']['updSteps']) { + $opx = array( + 'fields2get' => 'id' + ); + $stepIDSet = $this->get_steps($safeTCVID, null, $opx); + + if (! is_null($stepIDSet)) { + $target = array(); + foreach ($stepIDSet as $elem) { + $target[] = $elem['id']; + } + $inClause = implode(',', $target); + $sqlX = " UPDATE {$this->tables['tcsteps']} " . + " SET execution_type={$execType} WHERE id IN (" . $inClause . + ")"; + $this->db->exec_query($sqlX); + } + } + + return array( + $value, + $execType, + $sql + ); + } + + /** + */ + public function setEstimatedExecDuration($tcversionID, $value) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $safe = trim($value); + $safe = is_numeric($safe) ? $safe : null; + + $sql = "/* $debugMsg */ " . " UPDATE {$this->tables['tcversions']} " . + " SET estimated_exec_duration=" . + ((is_null($safe) || $safe == '') ? 'NULL' : $safe) . " WHERE id = " . + $this->db->prepare_int($tcversionID); + $this->db->exec_query($sql); + return array( + $value, + $safe, + $sql + ); + } + + /** + * + * @param array $identity: + * id, version_id + * @param array $execContext: + * tplan_id, platform_id,build_id + * @internal revisions + * + * @since 1.9.4 + */ + public function getLatestExecSingleContext($identity, $execContext, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $cfg = config_get('results'); + $status_not_run = $cfg['status_code']['not_run']; + + $my = array( + 'opt' => array( + 'output' => 'full' + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $options); + $safeContext = $execContext; + $safeIdentity = $identity; + foreach ($safeContext as &$ele) { + $ele = intval($ele); + } + + foreach ($safeIdentity as &$ele) { + $ele = intval($ele); + } + + // dammed names!!! + $safeContext['tplan_id'] = isset($safeContext['tplan_id']) ? $safeContext['tplan_id'] : $safeContext['testplan_id']; + if ($safeContext['platform_id'] < 0) { + $safeContext['platform_id'] = 0; + } + + // we have to manage following situations + // 1. we do not know test case version id. + if ($safeIdentity['version_id'] > 0) { + $addJoinLEX = ''; + $addWhereLEX = " AND EE.tcversion_id = " . + $safeIdentity['version_id']; + $addWhere = " AND TPTCV.tcversion_id = " . + $safeIdentity['version_id']; + } else { + $addJoinLEX = " JOIN {$this->tables['nodes_hierarchy']} H2O " . + " ON H2O.id = EE.tcversion_id "; + $addWhereLEX = " AND H2O.parent_id = " . $safeIdentity['id']; + $addWhere = " AND NHTC.id = " . $safeIdentity['id']; + } + + $sqlLEX = ' SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id,' . + ' MAX(EE.id) AS id ' . " FROM {$this->tables['executions']} EE " . + $addJoinLEX . ' WHERE EE.testplan_id = ' . $safeContext['tplan_id'] . + ' AND EE.platform_id = ' . $safeContext['platform_id'] . + ' AND EE.build_id = ' . $safeContext['build_id'] . $addWhereLEX . + ' GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id ,EE.build_id '; + + $out = null; + switch ($my['opt']['output']) { + case 'exec_id': + $dummy = $this->db->get_recordset($sqlLEX); + $out = (! is_null($dummy) ? $dummy[0]['id'] : null); + break; + + case 'timestamp': + $sql = "/* $debugMsg */ SELECT E.id AS execution_id, " . + " COALESCE(E.status,'{$status_not_run}') AS status," . + " NHTC.id AS testcase_id, TCV.id AS tcversion_id, E.execution_ts" . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id" . + " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.platform_id = TPTCV.platform_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.build_id = {$safeContext['build_id']} " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.id = LEX.id " . + " WHERE TPTCV.testplan_id = {$safeContext['tplan_id']} " . + " AND TPTCV.platform_id = {$safeContext['platform_id']} " . + $addWhere . + " AND (E.build_id = {$safeContext['build_id']} OR E.build_id IS NULL)"; + + // using database::CUMULATIVE is just a trick to return data structure + // that will be liked on execSetResults.php + $out = $this->db->fetchRowsIntoMap($sql, 'testcase_id', + database::CUMULATIVE); + break; + + case 'full': + default: + $sql = "/* $debugMsg */ SELECT E.id AS execution_id, " . + " COALESCE(E.status,'{$status_not_run}') AS status, E.execution_type AS execution_run_type," . + " NHTC.name, NHTC.id AS testcase_id, NHTC.parent_id AS tsuite_id," . + " TCV.id AS tcversion_id,TCV.tc_external_id,TCV.version,TCV.summary," . + " TCV.preconditions,TCV.importance,TCV.author_id," . + " TCV.creation_ts,TCV.updater_id,TCV.modification_ts,TCV.active," . + " TCV.is_open,TCV.execution_type," . + " U.login AS tester_login,U.first AS tester_first_name," . + " U.last AS tester_last_name, E.tester_id AS tester_id," . + " E.notes AS execution_notes, E.execution_ts, E.build_id,E.tcversion_number," . + " B.name AS build_name, B.active AS build_is_active, B.is_open AS build_is_open," . + " COALESCE(PLATF.id,0) AS platform_id,PLATF.name AS platform_name, TPTCV.id AS feature_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id" . + " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.platform_id = TPTCV.platform_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.build_id = {$safeContext['build_id']} " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.id = LEX.id " . + " JOIN {$this->tables['builds']} B ON B.id = {$safeContext['build_id']} " . + " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = E.tester_id " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLATF ON PLATF.id = {$safeContext['platform_id']} " . + " WHERE TPTCV.testplan_id = {$safeContext['tplan_id']} " . + " AND TPTCV.platform_id = {$safeContext['platform_id']} " . + $addWhere . + " AND (E.build_id = {$safeContext['build_id']} OR E.build_id IS NULL)"; + + // using database::CUMULATIVE is just a trick to return data structure + // that will be liked on execSetResults.php + $out = $this->db->fetchRowsIntoMap($sql, 'testcase_id', + database::CUMULATIVE); + break; + } + return $out; + } + + /** + * + * DBExec means we do not considered NOT RUN, because are not written to DB. + * + * @param array $identity: + * id, version_id + * @param array $execContext: + * tplan_id, platform_id + * @internal revisions + * + * @since 1.9.4 + */ + private function getLatestDBExecPlatformContext($identity, $execContext, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $cfg = config_get('results'); + $status_not_run = $cfg['status_code']['not_run']; + + $my = array( + 'opt' => array( + 'output' => 'full' + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $options); + $safeContext = $execContext; + $safeIdentity = $identity; + foreach ($safeContext as &$ele) { + $ele = intval($ele); + } + foreach ($safeIdentity as &$ele) { + $ele = intval($ele); + } + + // we have to manage following situations + // 1. we do not know test case version id. + if ($safeIdentity['version_id'] > 0) { + $addJoinLEX = ''; + $addWhereLEX = " AND EE.tcversion_id = " . + $safeIdentity['version_id']; + $addWhere = " AND TPTCV.tcversion_id = " . + $safeIdentity['version_id']; + } else { + $addJoinLEX = " JOIN {$this->tables['nodes_hierarchy']} H2O " . + " ON H2O.id = EE.tcversion_id "; + $addWhereLEX = " AND H2O.parent_id = " . $safeIdentity['id']; + $addWhere = " AND NHTC.id = " . $safeIdentity['id']; + } + + $sqlLEX = ' SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,' . + ' MAX(EE.id) AS id ' . " FROM {$this->tables['executions']} EE " . + $addJoinLEX . ' WHERE EE.testplan_id = ' . $safeContext['tplan_id'] . + ' AND EE.platform_id = ' . $safeContext['platform_id'] . $addWhereLEX . + ' GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id'; + + $out = null; + switch ($my['opt']['output']) { + case 'exec_id': + $dummy = $this->db->get_recordset($sqlLEX); + $out = (! is_null($dummy) ? $dummy[0]['id'] : null); + break; + + case 'full': + default: + $sql = "/* $debugMsg */ SELECT E.id AS execution_id, " . + " COALESCE(E.status,'{$status_not_run}') AS status, E.execution_type AS execution_run_type," . + " NHTC.name, NHTC.id AS testcase_id, NHTC.parent_id AS tsuite_id," . + " TCV.id AS tcversion_id,TCV.tc_external_id,TCV.version,TCV.summary," . + " TCV.preconditions,TCV.importance,TCV.author_id," . + " TCV.creation_ts,TCV.updater_id,TCV.modification_ts,TCV.active," . + " TCV.is_open,TCV.execution_type," . + " U.login AS tester_login,U.first AS tester_first_name," . + " U.last AS tester_last_name, E.tester_id AS tester_id," . + " E.notes AS execution_notes, E.execution_ts, E.build_id,E.tcversion_number," . + " B.name AS build_name, B.active AS build_is_active, B.is_open AS build_is_open," . + " COALESCE(PLATF.id,0) AS platform_id,PLATF.name AS platform_name, TPTCV.id AS feature_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id" . + " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.platform_id = TPTCV.platform_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " JOIN {$this->tables['executions']} E " . + " ON E.id = LEX.id " . + " JOIN {$this->tables['builds']} B ON B.id = E.build_id " . + " JOIN {$this->tables['users']} U ON U.id = E.tester_id " . + " /* Left outer on Platforms because Test plan can have NO PLATFORMS */ " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLATF " . + " ON PLATF.id = {$safeContext['platform_id']} " . + " WHERE TPTCV.testplan_id = {$safeContext['tplan_id']} " . + " AND TPTCV.platform_id = {$safeContext['platform_id']} " . + $addWhere; + + // using database::CUMULATIVE is just a trick to return data structure + // that will be liked on execSetResults.php + $out = $this->db->fetchRowsIntoMap($sql, 'testcase_id', + database::CUMULATIVE); + break; + } + + return $out; + } + + /** + */ + public function getExecution($execID, $tcversionID) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT NHTC.name,NHTCV.parent_id AS testcase_id, NHTCV.id AS tcversion_id, " . + " TCV.*, " . + " U.login AS tester_login, U.first AS tester_first_name, U.last AS tester_last_name," . + " E.tester_id AS tester_id,E.id AS execution_id, E.status,E.tcversion_number," . + " E.notes AS execution_notes, E.execution_ts, E.execution_type AS execution_run_type," . + " E.build_id AS build_id, B.name AS build_name, B.active AS build_is_active, " . + " B.is_open AS build_is_open,E.platform_id, PLATF.name AS platform_name," . + " E.testplan_id,NHTPLAN.name AS testplan_name,TPTCV.id AS feature_id, TPLAN.api_key AS testplan_api_key" . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = NHTCV.id " . " /* To get build name */ " . + " JOIN {$this->tables['builds']} B ON B.id=E.build_id " . + " /* To get test plan name */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN ON NHTPLAN.id = E.testplan_id " . + " JOIN {$this->tables['testplans']} TPLAN ON TPLAN.id = E.testplan_id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.testplan_id = E.testplan_id " . + " AND TPTCV.tcversion_id = E.tcversion_id " . + " AND TPTCV.platform_id = E.platform_id " . + " LEFT OUTER JOIN {$this->tables['users']} U ON U.id = E.tester_id " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLATF ON PLATF.id = E.platform_id " . + " WHERE E.id = " . intval($execID) . " AND E.tcversion_id = " . + intval($tcversionID); + $rs = $this->db->get_recordset($sql); + return $rs ? $rs : null; + } + + /** + */ + public function getAuditSignature($context, $options = null) + { + $key2check = array( + 'tcversion_id', + 'id' + ); + $safeID = array(); + foreach ($key2check as $key) { + if (property_exists($context, $key)) { + $safeID[$key] = intval($context->$key); + } else { + $safeID[$key] = - 1; + } + } + + if ($safeID['id'] <= 0 && $safeID['tcversion_id'] > 0) { + $node = $this->tree_manager->get_node_hierarchy_info( + $safeID['tcversion_id']); + $safeID['id'] = $node['parent_id']; + } + + // we need: + // Test Case External ID + // Test Case Name + // Test Case Path + // What about test case version ID ? => only if argument provided + // + $pathInfo = $this->tree_manager->get_full_path_verbose($safeID['id'], + array( + 'output_format' => 'id_name' + )); + $pathInfo = current($pathInfo); + $path = '/' . implode('/', $pathInfo['name']) . '/'; + $tcase_prefix = $this->getPrefix($safeID['id'], $pathInfo['node_id'][0]); + $info = $this->getLastVersionInfo($safeID['id'], + array( + 'output' => 'medium' + )); + return $path . $tcase_prefix[0] . $this->cfg->testcase->glue_character . + $info['tc_external_id'] . ':' . $info['name']; + } + + /** + */ + public function getTestSuite($id) + { + $dummy = $this->tree_manager->get_node_hierarchy_info($id); + return $dummy['parent_id']; + } + + public function getIdCardByStepID($step_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT NH_TCV.parent_id AS tcase_id, NH_STEPS.parent_id AS tcversion_id" . + " FROM {$this->tables['nodes_hierarchy']} NH_STEPS " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = NH_STEPS.parent_id " . + " WHERE NH_STEPS.id = " . intval($step_id); + $rs = $this->db->get_recordset($sql); + return is_null($rs) ? $rs : $rs[0]; + } + + /** + */ + private function initShowGui($guiObj, $grantsObj, $idCard) + { + $id = $idCard->tcase_id; + $goo = is_null($guiObj) ? new stdClass() : $guiObj; + + if (! property_exists($goo, 'closeMyWindow')) { + $goo->closeMyWindow = 0; + } + + if (! property_exists($goo, 'uploadOp')) { + $goo->uploadOp = null; + } + + $goo->new_version_source = 'this'; + + $goo->execution_types = $this->execution_types; + $goo->tcase_cfg = $this->cfg->testcase; + $goo->import_limit = TL_REPOSITORY_MAXFILESIZE; + $goo->msg = ''; + $goo->fileUploadMsg = ''; + + $goo->requirement_mgmt = property_exists($grantsObj, 'mgt_modify_req') ? $grantsObj->mgt_modify_req : null; + if (is_null($goo->requirement_mgmt)) { + $goo->requirement_mgmt = property_exists($grantsObj, + 'requirement_mgmt') ? $grantsObj->requirement_mgmt : 0; + } + + // some config options have been migrated to rights + // In order to refactor less code, we will remap to OLD config options present on config file. + $goo->tcase_cfg->can_edit_executed = $grantsObj->testproject_edit_executed_testcases == + 'yes' ? 1 : 0; + $goo->tcase_cfg->can_delete_executed = $grantsObj->testproject_delete_executed_testcases == + 'yes' ? 1 : 0; + + $goo->tcase_cfg->can_add_remove_kw_on_executed = 0; + $g2c = 'testproject_add_remove_keywords_executed_tcversions'; + if (property_exists($grantsObj, $g2c)) { + $goo->tcase_cfg->can_add_remove_kw_on_executed = ($grantsObj->$g2c == + 'yes' ? 1 : 0); + } + + $goo->view_req_rights = property_exists($grantsObj, 'mgt_view_req') ? $grantsObj->mgt_view_req : 0; + $goo->assign_keywords = property_exists($grantsObj, 'keyword_assignment') ? $grantsObj->keyword_assignment : 0; + $goo->req_tcase_link_management = property_exists($grantsObj, + 'req_tcase_link_management') ? $grantsObj->req_tcase_link_management : 0; + + $goo->parentTestSuiteName = ''; + $goo->tprojectName = ''; + $goo->submitCode = ""; + $goo->dialogName = ''; + $goo->bodyOnLoad = ""; + $goo->bodyOnUnload = "storeWindowSize('TCEditPopup')"; + + $goo->tableColspan = $this->layout->tableToDisplayTestCaseSteps->colspan; + + $goo->tc_current_version = array(); + $goo->status_quo = array(); + $goo->keywords_map = array(); + $goo->arrReqs = array(); + + $goo->cf_current_version = null; + $goo->cf_other_versions = null; + $goo->linked_versions = null; + $goo->platforms = null; + + // add_relation_feedback_msg @used-by testcaseCommands.class.php:doAddRelation() + $viewer_defaults = array( + 'title' => lang_get('title_test_case'), + 'show_title' => 'no', + 'action' => '', + 'msg_result' => '', + 'user_feedback' => '', + 'refreshTree' => 1, + 'disable_edit' => 0, + 'display_testproject' => 0, + 'display_parent_testsuite' => 0, + 'hilite_testcase_name' => 0, + 'show_match_count' => 0, + 'add_relation_feedback_msg' => '' + ); + + $viewer_defaults = array_merge($viewer_defaults, + (array) $guiObj->viewerArgs); + + $goo->display_testproject = $viewer_defaults['display_testproject']; + $goo->display_parent_testsuite = $viewer_defaults['display_parent_testsuite']; + $goo->show_title = $viewer_defaults['show_title']; + $goo->hilite_testcase_name = $viewer_defaults['hilite_testcase_name']; + $goo->action = $viewer_defaults['action']; + $goo->user_feedback = $viewer_defaults['user_feedback']; + $goo->add_relation_feedback_msg = $viewer_defaults['add_relation_feedback_msg']; + + $goo->pageTitle = $viewer_defaults['title']; + $goo->display_testcase_path = ! is_null($goo->path_info); + $goo->show_match_count = $viewer_defaults['show_match_count']; + if ($goo->show_match_count && $goo->display_testcase_path) { + $goo->pageTitle .= '-' . lang_get('match_count') . ':' . + ($goo->match_count = count($goo->path_info)); + } + + $goo->refreshTree = isset($goo->refreshTree) ? $goo->refreshTree : $viewer_defaults['refreshTree']; + $goo->sqlResult = $viewer_defaults['msg_result']; + + // fine grain control of operations + if ($viewer_defaults['disable_edit'] == 1 || + (! $grantsObj->mgt_modify_tc)) { + $goo->show_mode = 'editDisabled'; + } elseif (! is_null($goo->show_mode) && $goo->show_mode == 'editOnExec') { + // refers to two javascript functions present in testlink_library.js + // and logic used to refresh both frames when user call this + // method to edit a test case while executing it. + $goo->dialogName = 'tcview_dialog'; + $goo->bodyOnLoad = "dialog_onLoad($guiObj->dialogName)"; + $goo->bodyOnUnload = "dialog_onUnload($guiObj->dialogName)"; + $goo->submitCode = "return dialog_onSubmit($guiObj->dialogName)"; + + if (! property_exists($goo, 'additionalURLPar')) { + $goo->additionalURLPar = ''; + } + } + + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + $goo->domainTCStatus = $dummy['lbl']; + $goo->TCWKFStatusVerboseCode = config_get('testCaseStatus'); + $goo->TCWKFStatusDisplayHintOnTestDesign = config_get( + 'testCaseStatusDisplayHintOnTestDesign'); + + // editOnExec is part of show_mode Domain + $goo->can_do = $this->getShowViewerActions($goo->show_mode); + $key = 'testcase_freeze'; + if (property_exists($grantsObj, $key)) { + $goo->can_do->freeze = $grantsObj->$key; + } + + $key = 'delete_frozen_tcversion'; + if (property_exists($grantsObj, $key)) { + $goo->can_do->delete_frozen_tcversion = $grantsObj->$key; + } + + $path2root = $this->tree_manager->get_path($id); + $goo->tproject_id = $path2root[0]['parent_id']; + $info = $this->tproject_mgr->get_by_id($goo->tproject_id); + $goo->requirementsEnabled = $info['opt']->requirementsEnabled; + + if ($goo->display_testproject) { + $goo->tprojectName = $info['name']; + } + + if ($goo->display_parent_testsuite) { + $parent = count($path2root) - 2; + $goo->parentTestSuiteName = $path2root[$parent]['name']; + } + + $testplans = $this->tproject_mgr->get_all_testplans($goo->tproject_id, + array( + 'plan_status' => 1 + )); + $goo->has_testplans = ! empty($testplans) ? 1 : 0; + + $platformMgr = new tlPlatform($this->db, $goo->tproject_id); + + $opx = array( + 'enable_on_design' => true, + 'enable_on_execution' => false + ); + $goo->platforms = $platformMgr->getAllAsMap($opx); + + $goo->tcasePrefix = $this->tproject_mgr->getTestCasePrefix( + $goo->tproject_id) . $this->cfg->testcase->glue_character; + + $goo->scripts = null; + $goo->tcase_id = $idCard->tcase_id; + $goo->tcversion_id = $idCard->tcversion_id; + $goo->allowStepAttachments = false; + $designEditorCfg = getWebEditorCfg('design'); + $goo->designEditorType = $designEditorCfg['type']; + $stepDesignEditorCfg = getWebEditorCfg('steps_design'); + $goo->stepDesignEditorType = $stepDesignEditorCfg['type']; + + // Add To Testplan button will be disabled if + // the testcase doesn't belong to the current selected testproject + if ($idCard->tproject_id == $goo->tproject_id) { + $goo->can_do->add2tplan = ($goo->can_do->add2tplan == 'yes') ? $grantsObj->testplan_planning : 'no'; + } else { + $goo->can_do->add2tplan = 'no'; + } + + return $goo; + } + + /** + */ + private function initShowGuiActions(&$gui) + { + $gui->deleteStepAction = "lib/testcases/tcEdit.php?tproject_id=$gui->tproject_id&show_mode=$gui->show_mode" . + "&doAction=doDeleteStep&step_id="; + + $gui->tcExportAction = "lib/testcases/tcExport.php?tproject_id=$gui->tproject_id&show_mode=$gui->show_mode"; + $gui->tcViewAction = "lib/testcases/archiveData.php?tproject_id={$gui->tproject_id}" . + "&show_mode=$gui->show_mode&tcase_id="; + + $gui->printTestCaseAction = "lib/testcases/tcPrint.php?tproject_id=$gui->tproject_id&show_mode=$gui->show_mode"; + + $gui->keywordsViewHREF = "lib/keywords/keywordsView.php?tproject_id={$gui->tproject_id} " . + ' target="mainframe" class="bold" title="' . + lang_get('menu_manage_keywords') . '"'; + + $gui->reqSpecMgmtHREF = "lib/general/frmWorkArea.php?tproject_id={$gui->tproject_id}&feature=reqSpecMgmt"; + $gui->reqMgmtHREF = "lib/requirements/reqView.php?tproject_id={$gui->tproject_id}" . + "&showReqSpecTitle=1&requirement_id="; + + $gui->addTc2TplanHREF = "lib/testcases/tcAssign2Tplan.php?tproject_id={$gui->tproject_id}"; + } + + /** + * render Ghost Test Case + * + * + * @used by this.get_by_id(), this.getLastExecution() + * @used by this.renderGhostSteps() + */ + public function renderGhost(&$item2render) + { + $versionTag = '[version:%s]'; + $hint = "(link%s"; + + // $href = '%s:%s' . " $versionTag (link)

    "; + // second \'%s\' needed if I want to use Latest as indication, need to understand + // Javascript instead of javascript, because CKeditor sometimes complains + $href = '%s:%s' . + " $versionTag $hint

    "; + $tlBeginMark = self::GHOSTBEGIN; + $tlEndMark = self::GHOSTEND; + $tlEndMarkLen = strlen($tlEndMark); + + // I've discovered that working with Web Rich Editor generates + // some additional not wanted entities, that disturb a lot + // when trying to use json_decode(). + // Hope this set is enough. + // 20130605 - after algorithm change, this seems useless + // $replaceSet = array($tlEndMark, '

    ', '

    ',' '); + // $replaceSetWebRichEditor = array('

    ', '

    ',' '); + $key2check = array( + 'summary', + 'preconditions' + ); + $rse = &$item2render; + foreach ($key2check as $item_key) { + if (! isset($rse[$item_key])) { + continue; + } + $start = strpos($rse[$item_key], $tlBeginMark); + $ghost = $rse[$item_key]; + + // There is at least one request to replace ? + if ($start !== false) { + $xx = explode($tlBeginMark, $rse[$item_key]); + + // How many requests to replace ? + $xx2do = count($xx); + $ghost = ''; + for ($xdx = 0; $xdx < $xx2do; $xdx ++) { + $isTestCaseGhost = true; + + // Hope was not a false request. + // if( strpos($xx[$xdx],$tlEndMark) !== FALSE) + if (($cutting_point = strpos($xx[$xdx], $tlEndMark)) !== + false) { + // Separate command string from other text + // Theorically can be just ONE, but it depends + // is user had not messed things. + $yy = explode($tlEndMark, $xx[$xdx]); + + if (($elc = count($yy)) > 0) { + $dx = $yy[0]; + + // trick to convert to array + $dx = '{' . html_entity_decode(trim($dx, '\n')) . '}'; + $dx = json_decode($dx, true); + + try { + $xid = $this->getInternalID($dx['TestCase']); + if ($xid > 0) { + $linkFeedback = ")"; + $addInfo = ""; + $vn = isset($dx['Version']) ? intval( + $dx['Version']) : 0; + if ($vn == 0) { + // User wants to follow latest ACTIVE VERSION + $zorro = $this->getLastVersionInfo($xid, + array( + 'output' => 'full', + 'active' => 1 + )); + $linkFeedback = " to Latest ACTIVE Version)"; + if (is_null($zorro)) { + // seems all versions are inactive, in this situation will get latest + $zorro = $this->getLastVersionInfo( + $xid, + array( + 'output' => 'full' + )); + $addInfo = " - All versions are inactive!!"; + $linkFeedback = " to Latest Version{$addInfo})"; + } + $vn = intval($zorro['version']); + } + + $fi = $this->get_basic_info($xid, + array( + 'number' => $vn + )); + if (! is_null($fi)) { + if (isset($dx['Step'])) { + $isTestCaseGhost = false; + + // ghost for rendering Test Case Step (string display) + // [ghost]"Step":1,"TestCase":"MOK-2","Version":1[/ghost] + // + // ATTENTION REMEMBER THAT ALSO CAN BE: + // [ghost]"Step":1,"TestCase":"MOK-2","Version":""[/ghost] + // [ghost]"Step":1,"TestCase":"MOK-2"[/ghost] + // + if (intval($dx['Step']) > 0) { + $rightside = trim( + substr($xx[$xdx], + $cutting_point + + $tlEndMarkLen)); + $stx = $this->get_steps( + $fi[0]['tcversion_id'], + $dx['Step']); + + $ghost .= $stx[0]['actions'] . + $rightside; + } + } elseif ($dx['Preconditions']) { + $withPrecond = $this->get_basic_info( + $xid, [ + 'number' => $vn + ], [ + 'preconditions' + ]); + $isTestCaseGhost = false; + $rightside = trim( + substr($xx[$xdx], + $cutting_point + + $tlEndMarkLen)); + $ghost .= $withPrecond[0]['preconditions'] . + $rightside; + } else { + // ghost for rendering Test Case (create link) + $ghost .= sprintf($href, + $dx['TestCase'], $vn, + $dx['TestCase'], $fi[0]['name'], + $vn, $linkFeedback); + } + } + } + + if ($isTestCaseGhost) { + $lim = $elc - 1; + for ($cpx = 1; $cpx <= $lim; $cpx ++) { + $ghost .= $yy[$cpx]; + } + } + } catch (Exception $e) { + $ghost .= $rse[$item_key]; + } + } + } else { + $ghost .= $xx[$xdx]; + } + } + } + + if ($ghost != '') { + $rse[$item_key] = $ghost; + } + } + } + + /** + */ + public function setImportance($tcversionID, $value) + { + $sql = " UPDATE {$this->tables['tcversions']} " . " SET importance=" . + $this->db->prepare_int($value) . " WHERE id = " . + $this->db->prepare_int($tcversionID); + $this->db->exec_query($sql); + } + + /** + */ + public function setStatus($tcversionID, $value) + { + $sql = " UPDATE {$this->tables['tcversions']} " . " SET status=" . + $this->db->prepare_int($value) . " WHERE id = " . + $this->db->prepare_int($tcversionID); + $this->db->exec_query($sql); + } + + /** + * updateSimpleFields + * used to update fields of type int, string on test case version + * + * @param int $tcversionID + * item ID to update + * @param array $fieldsValues + * key DB field to update + * supported fields: + * summary,preconditions,execution_type,importance,status, + * updater_id,estimated_exec_duration + * + * @internal revisions + * + */ + public function updateSimpleFields($tcversionID, $fieldsValues) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $fieldsConvertions = array( + 'summary' => 'prepare_string', + 'preconditions' => 'prepare_string', + 'execution_type' => 'prepare_int', + 'importance' => 'prepare_int', + 'status' => 'prepare_int', + 'estimated_exec_duration' => null, + 'updater_id' => null + ); + $dummy = null; + $sql = null; + $ddx = 0; + foreach ($fieldsConvertions as $fkey => $fmethod) { + if (isset($fieldsValues[$fkey])) { + $dummy[$ddx] = $fkey . " = "; + if (! is_null($fmethod)) { + $sep = ($fmethod == 'prepare_string') ? "'" : ""; + $dummy[$ddx] .= $sep . + $this->db->$fmethod($fieldsValues[$fkey]) . $sep; + } else { + $dummy[$ddx] .= $fieldsValues[$fkey]; + } + $ddx ++; + } + } + if (! is_null($dummy)) { + $sqlSET = implode(",", $dummy); + $sql = "/* {$debugMsg} */ UPDATE {$this->tables['tcversions']} " . + "SET {$sqlSET} WHERE id={$tcversionID}"; + + $this->db->exec_query($sql); + } + return $sql; + } + + /** + * updateName + * check for duplicate name under same parent + * + * @param int $id + * test case id + * @param string $name + * + * @used-by XML-RPC API + */ + public function updateName($id, $name) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $ret['status_ok'] = true; + $ret['msg'] = 'ok'; + $ret['debug'] = ''; + $ret['API_error_code'] = 0; + + $field_size = config_get('field_size'); + $new_name = trim($name); + + if (($nl = mb_strlen($new_name)) <= 0) { + $ret['status_ok'] = false; + $ret['API_error_code'] = 'TESTCASE_EMPTY_NAME'; + $ret['msg'] = lang_get('API_' . $ret['API_error_code']); + } + + if ($ret['status_ok'] && $nl > $field_size->testcase_name) { + $ret['status_ok'] = false; + $ret['API_error_code'] = 'TESTCASE_NAME_LEN_EXCEEDED'; + $ret['msg'] = sprintf(lang_get('API_' . $ret['API_error_code']), $nl, + $field_size->testcase_name); + } + + if ($ret['status_ok']) { + // Go ahead + $check = $this->tree_manager->nodeNameExists($name, + $this->my_node_type, $id); + $ret['status_ok'] = ! $check['status']; + $ret['API_error_code'] = 'TESTCASE_SIBLING_WITH_SAME_NAME_EXISTS'; + $ret['msg'] = sprintf(lang_get('API_' . $ret['API_error_code']), + $name); + $ret['debug'] = ''; + } + + if ($ret['status_ok']) { + $rs = $this->tree_manager->get_node_hierarchy_info($id); + if (! is_null($rs) && $rs['node_type_id'] == $this->my_node_type) { + $sql = "/* {$debugMsg} */ UPDATE {$this->tables['nodes_hierarchy']} " . + " SET name='" . $this->db->prepare_string($name) . "' " . + " WHERE id= {$id}"; + $this->db->exec_query($sql); + $ret['debug'] = "Old name:{$rs['name']} - new name:{$name}"; + } + } + return $ret; + } + + public function getAttachmentTable() + { + return $this->attachmentTableName; + } + + /** + */ + public function updateChangeAuditTrial($tcversion_id, $user_id) + { + $sql = " UPDATE {$this->tables['tcversions']} " . " SET updater_id=" . + $this->db->prepare_int($user_id) . ", " . " modification_ts = " . + $this->db->db_now() . " WHERE id = " . + $this->db->prepare_int(intval($tcversion_id)); + $this->db->exec_query($sql); + } + + /** + */ + public function getStepsExecInfo($execution_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* {$debugMsg} */ " . + " SELECT id, execution_id,tcstep_id,notes,status FROM {$this->tables['execution_tcsteps']} " . + " WHERE execution_id = " . intval($execution_id); + + return $this->db->fetchRowsIntoMap($sql, 'tcstep_id'); + } + + /** + */ + public function getWorkFlowStatusDomain() + { + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + return $dummy['lbl']; + } + + /** + */ + public function getRelations($id) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + + $relSet = array(); + $relSet['num_relations'] = 0; + + $dummy = $this->get_by_id($id, self::LATEST_VERSION, null, + array( + 'output' => 'essential', + 'getPrefix' => true, + 'caller' => __FUNCTION__ + )); + // Get the TC version ID + $versionID = intval($dummy[0]['id']); + + $relSet['item'] = (null != $dummy) ? current($dummy) : null; + + $sql = " $debugMsg SELECT id, source_id, destination_id, relation_type, author_id, creation_ts " . + " FROM {$this->tables['testcase_relations']} " . + " WHERE source_id=$versionID OR destination_id=$versionID " . + " ORDER BY id ASC "; + + $relSet['relations'] = $this->db->get_recordset($sql); + + if (! empty($relSet['relations'])) { + $labels = $this->getRelationLabels(); + $label_keys = array_keys($labels); + foreach ($relSet['relations'] as $key => $rel) { + // is this relation type is configured? + if ($relTypeAllowed = in_array($rel['relation_type'], + $label_keys)) { + $relSet['relations'][$key]['source_localized'] = $labels[$rel['relation_type']]['source']; + $relSet['relations'][$key]['destination_localized'] = $labels[$rel['relation_type']]['destination']; + + $type_localized = 'destination_localized'; + $other_key = 'source_id'; + if ($versionID == $rel['source_id']) { + $type_localized = 'source_localized'; + $other_key = 'destination_id'; + } + $relSet['relations'][$key]['type_localized'] = $relSet['relations'][$key][$type_localized]; + $otherItem = $this->get_by_id(null, $rel[$other_key], null, + array( + 'output' => 'full_without_users', + 'getPrefix' => true + )); + + // only add it, if either interproject linking is on or if it is in the same project + $relTypeAllowed = true; + $relSet['relations'][$key]['related_tcase'] = $otherItem[0]; + + $user = tlUser::getByID($this->db, $rel['author_id']); + $relSet['relations'][$key]['author'] = $user->getDisplayName(); + } + + if (! $relTypeAllowed) { + unset($relSet['relations'][$key]); + } + } // end foreach + + $relSet['num_relations'] = count($relSet['relations']); + } + + return $relSet; + } + + /** + * idCard['tcase_id'] + * idCard['tcversion_id'] + */ + public function getTCVersionRelations($idCard) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + + $safeID = $idCard; + foreach ($safeID as $prop => $val) { + $safeID[$prop] = intval($val); + } + + $getOpt = array( + 'output' => 'essential', + 'getPrefix' => true, + 'caller' => __FUNCTION__ + ); + $relSet = array( + 'num_relations' => 0, + 'relations' => array() + ); + + $relSet['item'] = current( + $this->get_by_id($safeID['tcase_id'], $safeID['tcversion_id'], null, + $getOpt)); + + $sql = " $debugMsg " . + " SELECT TR.id, source_id, destination_id, relation_type, " . + " TR.author_id, TR.creation_ts,TR.link_status, " . + " NHTCV_S.parent_id AS tcase_source, " . + " NHTCV_D.parent_id AS tcase_destination " . + " FROM {$this->tables['testcase_relations']} TR " . + " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_D " . + " ON NHTCV_D.id = destination_id " . + " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_S " . + " ON NHTCV_S.id = source_id " . + " WHERE source_id = {$safeID['tcversion_id']} OR " . + " destination_id = {$safeID['tcversion_id']} " . " ORDER BY id ASC "; + + $relSet['relations'] = $this->db->get_recordset($sql); + + if (! empty($relSet['relations'])) { + $labels = $this->getRelationLabels(); + $label_keys = array_keys($labels); + + foreach ($relSet['relations'] as $key => $rel) { + // is this relation type is configured? + if ($relTypeAllowed = in_array($rel['relation_type'], + $label_keys)) { + $relSet['relations'][$key]['source_localized'] = $labels[$rel['relation_type']]['source']; + $relSet['relations'][$key]['destination_localized'] = $labels[$rel['relation_type']]['destination']; + + $type_localized = 'destination_localized'; + $oKeyTCVID = 'source_id'; + $oKeyTCID = 'tcase_source'; + if ($safeID['tcversion_id'] == $rel['source_id']) { + $type_localized = 'source_localized'; + $oKeyTCVID = 'destination_id'; + $oKeyTCID = 'tcase_destination'; + } + $otherTCID = $rel[$oKeyTCID]; + $otherTCVID = $rel[$oKeyTCVID]; + + $relSet['relations'][$key]['type_localized'] = $relSet['relations'][$key][$type_localized]; + + $otherItem = $this->get_by_id($otherTCID, $otherTCVID, null, + array( + 'output' => 'full_without_users', + 'getPrefix' => true + )); + + // only add it to output set, if either interproject linking is on + // or if it is in the same project + $relTypeAllowed = true; + $relSet['relations'][$key]['related_tcase'] = $otherItem[0]; + + $user = tlUser::getByID($this->db, $rel['author_id']); + $relSet['relations'][$key]['author'] = $user->getDisplayName(); + } + + if (! $relTypeAllowed) { + unset($relSet['relations'][$key]); + } + } // end foreach + + $relSet['num_relations'] = count($relSet['relations']); + } + + return $relSet; + } + + /** + */ + public function getTCVRelationsRaw($tcversionID, $opt = null) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + + $safeID['tcversion_id'] = intval($tcversionID); + + $my = array( + 'opt' => array( + 'side' => null + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = " $debugMsg " . + " SELECT TR.id, source_id, destination_id, relation_type, " . + " TR.author_id, TR.creation_ts,TR.link_status, " . + " NHTCV_S.parent_id AS tcase_source, " . + " NHTCV_D.parent_id AS tcase_destination " . + " FROM {$this->tables['testcase_relations']} TR " . + " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_D " . + " ON NHTCV_D.id = destination_id " . + " JOIN {$this->tables['nodes_hierarchy']} AS NHTCV_S " . + " ON NHTCV_S.id = source_id "; + + switch ($my['opt']['side']) { + case 'source': + $where = " WHERE source_id = {$safeID['tcversion_id']} "; + break; + + case 'destination': + case 'dest': + $where = " WHERE destination_id = {$safeID['tcversion_id']} "; + break; + + default: + $where = " WHERE source_id = {$safeID['tcversion_id']} OR " . + " destination_id = {$safeID['tcversion_id']} "; + break; + } + + $sql .= $where; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + */ + public static function getRelationLabels() + { + $cfg = config_get('testcase_cfg'); + $labels = $cfg->relations->type_labels; + foreach ($labels as $key => $label) { + $labels[$key] = init_labels($label); + } + return $labels; + } + + /** + */ + public function deleteAllTestCaseRelations($id) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + + $tcaseSet = (array) $id; + array_walk($tcaseSet, 'intval'); + + // @since 1.9.18 + // Relations on test case versions + $tcVIDSet = $this->getAllVersionsID($tcaseSet); + $inValues = implode(',', $tcVIDSet); + $sql = " $debugMsg DELETE FROM {$this->tables['testcase_relations']} " . + " WHERE source_id IN ($inValues) OR " . + " destination_id IN ($inValues) "; + $this->db->exec_query($sql); + } + + /** + * checks if there is a relation of a given type between two requirements + * + * @author Andreas Simon + * + * @param integer $first_id + * ID to check + * @param integer $second_id + * ID to check + * @param integer $rel_type_id + * relation type ID to check + * + * @return true, if relation already exists, false if not + */ + public function relationExits($first_id, $second_id, $rel_type_id) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + + $safe_first_id = intval($first_id); + $safe_second_id = intval($second_id); + + $sql = " $debugMsg SELECT COUNT(0) AS qty " . + " FROM {$this->tables['testcase_relations']} " . + " WHERE ((source_id=" . $safe_first_id . " AND destination_id=" . + $safe_second_id . ") " . " OR (source_id=" . $safe_second_id . + " AND destination_id=" . $safe_first_id . ")) " . + " AND relation_type=" . intval($rel_type_id); + + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty'] > 0; + } + + /** + * Get count of all relations, no matter if it is source or destination + * or what type of relation it is. + * + * @param integer $id + * requirement ID to check + * + * @return integer $count + */ + public function getRelationsCount($id) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + $safeID = intval($id); + $sql = " $debugMsg SELECT COUNT(*) AS qty " . + " FROM {$this->tables['testcase_relations']} " . + " WHERE source_id=$safeID OR destination_id=$safeID "; + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty']; + } + + /** + * add a relation of a given type between Test Case Versions + * + * @param integer $source_id: + * ID of source test case version or test case. + * If test case is provided, latest active version + * will be used. + * + * @param integer $destination_id: + * ID of destination test case version or test case + * If test case is provided, latest active version + * will be used. + * + * @param integer $type_id + * relation type ID to set + * @param integer $author_id + * user's ID + */ + public function addRelation($source_id, $destination_id, $type_id, + $author_id, $ts = null) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + + // Check if items are test cases or test case versions + // Will check only source + $safeID = array( + 's' => intval($source_id), + 'd' => intval($destination_id) + ); + + $extr = array( + $safeID['s'] + ); + $sql = " SELECT node_type_id,id " . + " FROM {$this->tables['nodes_hierarchy']} " . " WHERE id IN(" . + implode(',', $extr) . ")"; + + $nu = current($this->db->get_recordset($sql)); + + if ($nu['node_type_id'] == $this->node_types_descr_id['testcase']) { + // Need to get latest active version for source and dest + $tcvSet = $this->get_last_active_version( + array( + $safeID['s'], + $safeID['d'] + ), null, array( + 'access_key' => 'testcase_id' + )); + + // Overwrite + $safeID['s'] = intval($tcvSet[$safeID['s']]['tcversion_id']); + $safeID['d'] = intval($tcvSet[$safeID['d']]['tcversion_id']); + } + + // check if exists before trying to add + if (! $this->relationExits($source_id, $destination_id, $type_id)) { + // check if related testcase is open + $dummy = $this->get_by_id($destination_id, self::LATEST_VERSION); + if (($dummy[0]['is_open']) == 1) { + $time = is_null($ts) ? $this->db->db_now() : $ts; + $sql = " $debugMsg INSERT INTO {$this->tables['testcase_relations']} " . + " (source_id, destination_id, relation_type, author_id, creation_ts) " . + " values ({$safeID['s']},{$safeID['d']}, $type_id, $author_id, $time)"; + $this->db->exec_query($sql); + $ret = array( + 'status_ok' => true, + 'msg' => 'relation_added' + ); + } else { + $ret = array( + 'status_ok' => false, + 'msg' => 'related_tcase_not_open' + ); + } + } else { + $ret = array( + 'status_ok' => false, + 'msg' => 'relation_already_exists' + ); + } + + return $ret; + } + + /** + * delete an existing relation + * + * @author Andreas Simon + * + * @param int $id + * relation id + */ + public function deleteRelationByID($relID) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */'; + $sql = " $debugMsg DELETE FROM {$this->tables['testcase_relations']} WHERE id=" . + intval($relID); + $this->db->exec_query($sql); + } + + /** + * + * @return array $htmlSelect info needed to create select box on multiple templates + */ + private function getRelationTypeDomainForHTMLSelect() + { + $htmlSelect = array( + 'items' => array(), + 'selected' => null, + 'equal_relations' => array() + ); + $labels = $this->getRelationLabels(); + + foreach ($labels as $key => $lab) { + $htmlSelect['items'][$key . "_source"] = $lab['source']; + if ($lab['source'] != $lab['destination']) { + // relation is not equal as labels for source and dest are different + $htmlSelect['items'][$key . "_destination"] = $lab['destination']; + } else { + // mark this as equal relation - no parent/child, makes searching simpler + $htmlSelect['equal_relations'][] = $key . "_source"; + } + } + + // set "related to" as default preselected value in forms + if (defined('TL_REL_TYPE_RELATED') && + isset($htmlSelect[TL_REL_TYPE_RELATED . "_source"])) { + $selected_key = TL_REL_TYPE_RELATED . "_source"; + } else { + // "related to" is not configured, so take last element as selected one + $keys = array_keys($htmlSelect['items']); + $selected_key = end($keys); + } + $htmlSelect['selected'] = $selected_key; + + return $htmlSelect; + } + + /** + * exportRelationToXML + * + * Function to export a test case relation to XML. + * + * @param int $relation + * relation data array + * @param string $troject_id + * + * @return string with XML code + * + * + * testcase external id + * prj + * doc2_id + * testcase external id + * 0 + * + * + * @internal revisions + * + */ + public function exportRelationToXML($relation, $item) + { + $xmlStr = ''; + + if (! is_null($relation)) { + // need to understand if swap is needed, this happens when + // relation type is + // - child_of + // - depends_on + // where item is DESTINATION and NOT SOURCE + if ($relation['source_id'] == $item['id']) { + $ele['source_ext_id'] = $item['fullExternalID']; + $ele['source_version'] = $item['version']; + $ele['destination_ext_id'] = $relation['related_tcase']['fullExternalID']; + $ele['destination_version'] = $relation['related_tcase']['version']; + } else { + // SWAP + $ele['source_ext_id'] = $relation['related_tcase']['fullExternalID']; + $ele['source_version'] = $relation['related_tcase']['version']; + $ele['destination_ext_id'] = $item['fullExternalID']; + $ele['destination_version'] = $item['version']; + } + $ele['relation_type'] = $relation['relation_type']; + + $info = array( + "||SOURCE||" => "source_ext_id", + "||SOURCE_VERSION||" => "source_version", + "||DESTINATION||" => "destination_ext_id", + "||DESTINATION_VERSION||" => "destination_version", + "||TYPE||" => "relation_type" + ); + + $elemTpl = "\t" . "" . "\n\t\t" . + "||SOURCE||"; + $elemTpl .= "\n\t\t" . + "||DESTINATION||"; + $elemTpl .= "\n\t\t" . "||TYPE||" . "\n\t" . + "" . "\n"; + + $work[] = $ele; + $xmlStr = exportDataToXML($work, "{{XMLCODE}}", $elemTpl, $info, + true); + } + + return $xmlStr; + } + + /** + * Will do analisys IGNORING test plan, platform and build + * get info of execution WRITTEN to DB. + */ + public function getSystemWideLastestExecutionID($tcversion_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . " SELECT MAX(e.id) AS execution_id " . + " FROM {$this->tables['executions']} e " . " WHERE e.tcversion_id = " . + intval($tcversion_id); + + $rs = $this->db->get_recordset($sql); + return intval($rs[0]['execution_id']); + } + + /** + * render Image Attachments INLINE + */ + private function renderImageAttachments($id, &$item2render, + $key2check = array( + 'summary', + 'preconditions' + ), $basehref = null) + { + static $attSet; + static $beginTag; + static $endTag; + static $repoDir; + + if (! $attSet || ! isset($attSet[$id])) { + $attSet[$id] = $this->attachmentRepository->getAttachmentInfosFor( + $id, $this->attachmentTableName, 'id'); + $beginTag = '[tlInlineImage]'; + $endTag = '[/tlInlineImage]'; + $repoDir = config_get('repositoryPath'); + } + + if (is_null($attSet[$id])) { + return; + } + + // $href = '%s:%s' . " $versionTag (link)

    "; + // second \'%s\' needed if I want to use Latest as indication, need to understand + // Javascript instead of javascript, because CKeditor sometimes complains + // + // CRITIC: skipCheck is needed to render OK when creating report on Pseudo-Word format. + $bhref = is_null($basehref) ? $_SESSION['basehref'] : $basehref; + + $src = ' src="' . $bhref . + '/lib/attachments/attachmentdownload.php?skipCheck=%sec%&id=%id%">

    '; + $img = '

    0) { + + $atx = $yy[0]; + if (intval($atx) == 0) { + $atx = $this->getTCVersionAttachIDFromTitle($id, + $atx); + } + + try { + if (isset($attSet[$id][$atx]) && + $attSet[$id][$atx]['is_image']) { + $sec = hash('sha256', + $attSet[$id][$atx]['file_name']); + + // Need file dimension!!! + $pathname = $repoDir . + $attSet[$id][$atx]['file_path']; + list ($iWidth, $iHeight, ,) = getimagesize( + $pathname); + + $iDim = ' width=' . $iWidth . ' height=' . + $iHeight; + $icarus = str_replace( + array( + '%id%', + '%sec%' + ), array( + $atx, + $sec + ), $img); + $ghost .= sprintf($icarus, $iDim); + } + $lim = $elc - 1; + for ($cpx = 1; $cpx <= $lim; $cpx ++) { + $ghost .= $yy[$cpx]; + } + } catch (Exception $e) { + $ghost .= $rse[$item_key]; + } + } + } else { + // nothing to do + $ghost .= $xx[$xdx]; + } + } + } + + // reconstruct field contents + if ($ghost != '') { + $rse[$item_key] = $ghost; + } + } + } + + /** + */ + private function trimAndLimit($s, $len = 100) + { + $s = trim($s); + if (tlStringLen($s) > $len) { + $s = tlSubStr($s, 0, $len); + } + + return $s; + } + + /** + */ + public function generateTimeStampName($name) + { + return @strftime("%Y%m%d-%H:%M:%S", time()) . ' ' . $name; + } + + /** + */ + public static function getLayout() + { + $ly = new stdClass(); + $ly->tableToDisplayTestCaseSteps = new stdClass(); + + // MAGIC: columns are: + // column for reorder, action, expected results, exec type, delete, insert + $ly->tableToDisplayTestCaseSteps->colspan = 6; + + return $ly; + } + + /** + */ + public function setIntAttrForAllVersions($id, $attr, $value, + $forceFrozenVersions = false) + { + $sql = " UPDATE {$this->tables['tcversions']} " . " SET {$attr} = " . + $this->db->prepare_int($value); + + if (! $forceFrozenVersions) { + $sql .= " WHERE is_open=1 AND "; + } else { + $sql .= " WHERE "; + } + $sql .= " id IN (" . + " SELECT NHTCV.id FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " WHERE NHTCV.parent_id = " . intval($id) . ")"; + $this->db->exec_query($sql); + } + + /** + */ + public function getTcSearchSkeleton($userInput = null) + { + $sk = new stdClass(); + + $sk->creation_date_from = null; + $sk->creation_date_to = null; + $sk->modification_date_from = null; + $sk->modification_date_to = null; + $sk->search_important_notice = ''; + $sk->design_cf = ''; + $sk->keywords = ''; + $sk->filter_by['design_scope_custom_fields'] = false; + $sk->filter_by['keyword'] = false; + $sk->filter_by['requirement_doc_id'] = false; + $sk->option_importance = array( + 0 => '', + HIGH => lang_get('high_importance'), + MEDIUM => lang_get('medium_importance'), + LOW => lang_get('low_importance') + ); + + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + $sk->domainTCStatus = array( + 0 => '' + ) + $dummy['lbl']; + $sk->importance = null; + $sk->status = null; + $sk->tcversion = null; + $sk->tcasePrefix = ''; + $sk->targetTestCase = ''; + + $txtin = array( + "created_by", + "edited_by", + "jolly" + ); + $jollyKilled = array( + "summary", + "steps", + "expected_results", + "preconditions", + "name" + ); + $txtin = array_merge($txtin, $jollyKilled); + + foreach ($txtin as $key) { + $sk->$key = ! is_null($userInput) ? $userInput->$key : ''; + } + + if (! is_null($userInput) && $userInput->jolly != '') { + foreach ($jollyKilled as $key) { + $sk->$key = ''; + } + } + + return $sk; + } + + /** + */ + public function setIsOpen($id, $tcversion_id, $value) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $bv = (intval($value) > 0) ? 1 : 0; + $sql = " /* $debugMsg */ UPDATE {$this->tables['tcversions']} " . + " SET is_open = {$bv}" . " WHERE id = " . intval($tcversion_id); + + $this->db->exec_query($sql); + } + + /** + * render CF values + * + *

    added by web rich editor create some layout issues + * + * @used by this.get_by_id(), this.getLastExecution() + * + */ + public function renderVariables(&$item2render, $tproj_id) + { + $tcase_id = $item2render['testcase_id']; + $tcversion_id = $item2render['id']; + $cfSet = $this->get_linked_cfields_at_design($tcase_id, $tcversion_id); + $kwSet = $this->getTestProjectKeywords($tproj_id); + + if (is_null($cfSet) && is_null($kwSet)) { + return; + } + + $key2check = [ + 'summary', + 'preconditions' + ]; + $tlBeginTag = '[tlVar]'; + $tlEndTag = '[/tlVar]'; + + $rse = &$item2render; + foreach ($key2check as $item_key) { + $start = strpos($rse[$item_key], $tlBeginTag); + // There is at least one request to replace ? + if ($start !== false) { + // This way remove may be the

    that webrich editor adds + $play = substr($rse[$item_key], $start); + $xx = explode($tlBeginTag, $play); + + // How many requests to replace ? + $xx2do = count($xx); + for ($xdx = 0; $xdx < $xx2do; $xdx ++) { + + // Hope was not a false request. + if ((strpos($xx[$xdx], $tlEndTag)) !== false) { + + // Separate command string from other text + // Theorically can be just ONE, but it depends + // is user had not messed things. + $yy = explode($tlEndTag, $xx[$xdx]); + if (! empty($yy)) { + $variableName = trim($yy[0]); + + try { + // Step #1 Look in Custom Fields + // look for the custom field + if (! is_null($cfSet)) { + foreach ($cfSet as $cfDef) { + if ($cfDef['name'] === $variableName) { + $duckTape = $this->cfield_mgr->string_custom_field_value( + $cfDef, $tcversion_id); + $rse[$item_key] = str_replace( + $tlBeginTag . $variableName . + $tlEndTag, $duckTape, + $rse[$item_key]); + } + } + } + + // Step #2 Look in Keywords + if (! is_null($kwSet)) { + foreach ($kwSet as $kw => $kwNotes) { + if ($kw === $variableName) { + $rse[$item_key] = str_replace( + $tlBeginTag . $variableName . + $tlEndTag, $kwNotes, + $rse[$item_key]); + } + } + } + } catch (Exception $e) { + // Do nothing + } + } + } + } + } + } + } + + /** + * get data about code links from external tool + * + * @param + * resource &$db reference to database handler + * @param + * object &$code_interface reference to instance of bugTracker class + * @param integer $tcversion_id + * Identifier of test case version + * + * @return array list of 'script_name' with values: link_to_cts, + * project_key, repository_name, code_path, branch_name + */ + public function getScriptsForTestCaseVersion(&$code_interface, $tcversion_id) + { + $tables = tlObjectWithDB::getDBTables( + array( + 'tcversions', + 'testcase_script_links' + )); + $script_list = array(); + + $debugMsg = 'FILE:: ' . __FILE__ . ' :: FUNCTION:: ' . __FUNCTION__; + if (is_object($code_interface)) { + + $sql = "/* $debugMsg */ SELECT TSL.*,TCV.version " . + " FROM {$tables['testcase_script_links']} TSL, {$tables['tcversions']} TCV " . + " WHERE TSL.tcversion_id = " . intval($tcversion_id) . + " AND TSL.tcversion_id = TCV.id " . " ORDER BY TSL.code_path"; + + $map = $this->db->get_recordset($sql); + if (! is_null($map)) { + $opt = array(); + foreach ($map as $elem) { + $script_id = $elem['project_key'] . '&&' . + $elem['repository_name'] . '&&' . $elem['code_path']; + if (! isset($script_list[$script_id])) { + $opt['branch'] = $elem['branch_name']; + $opt['commit_id'] = $elem['commit_id']; + $dummy = $code_interface->buildViewCodeLink( + $elem['project_key'], $elem['repository_name'], + $elem['code_path'], $opt); + $script_list[$script_id]['link_to_cts'] = $dummy->link; + $script_list[$script_id]['project_key'] = $elem['project_key']; + $script_list[$script_id]['repository_name'] = $elem['repository_name']; + $script_list[$script_id]['code_path'] = $elem['code_path']; + $script_list[$script_id]['branch_name'] = $elem['branch_name']; + $script_list[$script_id]['commit_id'] = $elem['commit_id']; + $script_list[$script_id]['tcversion_id'] = $elem['tcversion_id']; + } + unset($dummy); + } + } + } + + if (empty($script_list)) { + $script_list = null; + } + return $script_list; + } + + /** + */ + private function ckEditorCopyAndPasteCleanUp(&$items, $keys) + { + $offending = array( + '' + ); + $good = array( + '<body id="cke_pastebin"', + '</body>' + ); + foreach ($keys as $fi) { + $items->$fi = str_ireplace($offending, $good, $items->$fi); + } + } + + /** + */ + public function getPathName($tcase_id) + { + $pathInfo = $this->tree_manager->get_full_path_verbose($tcase_id, + array( + 'output_format' => 'id_name' + )); + $pathInfo = current($pathInfo); + $path = '/' . implode('/', $pathInfo['name']) . '/'; + + $pfx = $this->tproject_mgr->getTestCasePrefix($pathInfo['node_id'][0]); + + $info = $this->getLastVersionInfo($tcase_id, + array( + 'output' => 'medium' + )); + + $path .= $pfx . $this->cfg->testcase->glue_character . + $info['tc_external_id'] . ':' . $info['name']; + + return $path; + } + + /** + * build Test Case Name getting information + * from special marked text inside string + * + * string can be test case summary or test case precondition + */ + private function buildTCName($name, $text2scan) + { + $taglen = strlen(self::NAME_PHOPEN); + + $where['open'] = strpos($name, self::NAME_PHOPEN); + $where['close'] = strpos($name, self::NAME_PHCLOSE); + + if (false !== $where['open']) { + $whoami['l'] = substr($name, 0, $where['open']); + $meat = substr($name, $where['open'] + $taglen, + ($where['close'] - $where['open'] - $taglen)); + + $dummy = strstr($name, self::NAME_PHCLOSE); + $whoami['r'] = ''; + if ($dummy !== false) { + $whoami['r'] = ltrim($dummy, self::NAME_PHCLOSE); + } + + $dm = explode(self::NAME_DIVIDE, $meat); + $name = $whoami['l'] . self::NAME_PHOPEN; + + $juice = $this->orangeJuice($text2scan); + $name .= (! empty($dm)) ? $dm[0] : $meat; + $name .= self::NAME_DIVIDE . $juice . self::NAME_PHCLOSE . + $whoami['r']; + } + return $name; + } + + /** + * target => [[All you need]] + * scan4values => [[is Love]] + * + * returned text => All you need::is Love + */ + private function replaceTextBTWTags($target, $scan4values) + { + $taglen = strlen(self::NAME_PHOPEN); + $side = array( + 'l' => '', + 'r' => '' + ); + + $where['open'] = strpos($target, self::NAME_PHOPEN); + $where['close'] = strpos($target, self::NAME_PHCLOSE); + + // Both tags present or ... skip + if (false !== $where['open'] && false !== $where['close']) { + + // the needle will NOT BE replaced. + $needle = substr($target, $where['open'] + $taglen, + ($where['close'] - $where['open'] - $taglen)); + + // start disecting the target + // first to the left + $side['l'] = substr($target, 0, $where['open']); + + // haystack = $target + // needle = self::NAME_PHCLOSE + $dummy = strstr($target, self::NAME_PHCLOSE); + $whoami['r'] = ''; + + if ($dummy !== false) { + // dummy => ]]xxxxxxxxxxx + $side['r'] = ltrim($dummy, self::NAME_PHCLOSE); + } + + $dm = explode(self::NAME_DIVIDE, $needle); + $target = $side['l'] . ((! empty($dm)) ? $dm[0] : $needle); + + $juice = $this->orangeJuice($scan4values); + $target .= self::NAME_DIVIDE . $juice . $side['r']; + } + return $target; + } + + /** + */ + private function orangeJuice($str) + { + $juice = ''; + $taglen = strlen(self::NAME_PHOPEN); + + $where['open'] = strpos($str, self::NAME_PHOPEN); + $where['close'] = strpos($str, self::NAME_PHCLOSE); + + if (false !== $where['open']) { + $juice = substr($str, $where['open'] + $taglen, + ($where['close'] - $where['open'] - $taglen)); + } + return $juice; + } + + /** + */ + private function getVersionNumber($version_id) + { + $sql = " SELECT version FROM {$this->tables['tcversions']} " . + " WHERE id=" . intval($version_id); + + $rs = $this->db->get_recordset($sql); + return intval($rs[0]['version']); + } + + /** + */ + public function getAllVersionsID($id) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; + + $target = (array) $id; + array_walk($target, 'intval'); + + $sql = $debugMsg . " SELECT id AS tcversion_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " WHERE NHTCV.parent_id =" . implode(',', $target) . + " AND NHTCV.node_type_id = " . + $this->node_types_descr_id['testcase_version']; + + $xx = $this->db->fetchRowsIntoMap($sql, 'tcversion_id'); + + if (null != $xx && count($xx) > 0) { + return array_keys($xx); + } + + return null; + } + + /** + */ + private function getAttXMLCfg() + { + $attXML = new stdClass(); + + $attXML->root = "\t\n{{XMLCODE}}\t\n"; + $attXML->elemTPL = "\t\t\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\n"; + + $attXML->decode = array( + "||ATTACHMENT_ID||" => "id", + "||ATTACHMENT_NAME||" => "name", + "||ATTACHMENT_FILE_TYPE||" => "file_type", + "||ATTACHMENT_FILE_SIZE||" => "file_size", + "||ATTACHMENT_TITLE||" => "title", + "||ATTACHMENT_DATE_ADDED||" => "date_added", + "||ATTACHMENT_CONTENT||" => "content" + ); + + return $attXML; + } + + /** + */ + public function closeOpenTCVRelation($relationID, $reason) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; + $sql = " $debugMsg UPDATE {$this->tables['testcase_relations']} " . + " SET link_status = " . intval($reason) . " WHERE id IN(" . + implode(',', $relationID) . ")" . " AND link_status = " . + LINK_TC_RELATION_OPEN; + + $this->db->exec_query($sql); + } + + /** + */ + private function copyTCVRelations($source_id, $dest_id) + { + + // Step 1 - get existent relations + $relSource = $this->getTCVRelationsRaw($source_id, + array( + 'side' => 'source' + )); + $relDest = $this->getTCVRelationsRaw($source_id, + array( + 'side' => 'dest' + )); + + $ins = "(source_id,destination_id,relation_type," . + " link_status,author_id) "; + + $values = array(); + if (! empty($relSource)) { + foreach ($relSource as $elem) { + $stm = "($dest_id,{$elem['destination_id']}," . + "{$elem['relation_type']},{$elem['link_status']}," . + "{$elem['author_id']})"; + $values[] = $stm; + } + } + + if (! empty($relDest)) { + foreach ($relDest as $elem) { + $stm = "({$elem['source_id']},$dest_id," . + "{$elem['relation_type']},{$elem['link_status']}," . + "{$elem['author_id']})"; + $values[] = $stm; + } + } + + if (! empty($values)) { + $sql = 'INSERT INTO ' . $this->tables['testcase_relations'] . $ins . + ' VALUES ' . implode(',', $values); + + $this->db->exec_query($sql); + } + } + + /** + */ + private function updateCoverage($link, $whoWhen, $opt = null) + { + + // Set coverage for previous version to FROZEN & INACTIVE + // Create coverage for NEW Version + $debugMsg = $this->debugMsg . __FUNCTION__; + + $options = array( + 'freezePrevious' => true + ); + $options = array_merge($options, (array) $opt); + $safeF = intval($link['source']); + $safeT = intval($link['dest']); + + // Set coverage for previous version to FROZEN & INACTIVE ? + if ($options['freezePrevious']) { + $sql = " /* $debugMsg */ " . + " UPDATE {$this->tables['req_coverage']} " . + " SET link_status = " . LINK_TC_REQ_CLOSED_BY_NEW_TCVERSION . "," . + " is_active=0 " . " WHERE tcversion_id=" . $safeF; + $this->db->exec_query($sql); + } + + // Create coverage for NEW Version + $sql = "/* $debugMsg */ " . + " INSERT INTO {$this->tables['req_coverage']} " . + " (req_id,req_version_id,testcase_id,tcversion_id," . + " author_id,creation_ts) " . + " SELECT req_id,req_version_id,testcase_id, " . + " {$safeT} AS tcversion_id," . + " {$whoWhen['user_id']} AS author_id, " . + " {$whoWhen['when']} AS creation_ts" . + " FROM {$this->tables['req_coverage']} " . " WHERE tcversion_id=" . + $safeF; + + $this->db->exec_query($sql); + } + + /** + */ + public function closeOpenReqLinks($tcversion_id, $reason, $opt = null) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; + + $options = array( + 'freeze_req_version' => false + ); + $options = array_merge($options, (array) $opt); + + $commonWhere = " WHERE tcversion_id = " . intval($tcversion_id) . + " AND link_status = " . LINK_TC_REQ_OPEN; + + // This has to be done BEFORE changing link_status + if ($options['freeze_req_version']) { + + /* + * execution time issues + * $sql = " $debugMsg UPDATE {$this->tables['req_versions']} + * SET is_open = 0 + * WHERE id IN ( + * SELECT req_version_id + * FROM {$this->tables['req_coverage']} + * $commonWhere + * ) "; + */ + switch (DB_TYPE) { + case 'mysql': + $sql = " $debugMsg UPDATE {$this->tables['req_versions']} RQV INNER JOIN {$this->tables['req_coverage']} RC ON RQV.id = RC.req_version_id - SET is_open = 0 $commonWhere "; - break; - - - case 'postgres': - // https://stackoverflow.com/questions/11369757/ - // postgres-wont-accept-table-alias-before-column-name - // - $sql = " $debugMsg + SET is_open = 0 $commonWhere "; + break; + + case 'postgres': + // https://stackoverflow.com/questions/11369757/ + // postgres-wont-accept-table-alias-before-column-name + // + $sql = " $debugMsg UPDATE {$this->tables['req_versions']} RQV - SET is_open = 0 + SET is_open = 0 FROM {$this->tables['req_coverage']} RC - $commonWhere AND RQV.id = RC.req_version_id"; - break; - } - - $this->db->exec_query($sql); - } - - - // Work on Coverage - $sql = " $debugMsg UPDATE {$this->tables['req_coverage']} " . - " SET link_status = " . intval($reason) . $commonWhere; - $this->db->exec_query($sql); - - // No audit yet - } - - /** - * - */ - function closeOpenReqVersionOnOpenLinks( $tcversion_id ) { - - $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; - - $commonWhere = " WHERE tcversion_id = " . intval($tcversion_id) . - " AND link_status = " . LINK_TC_REQ_OPEN; - - // https://stackoverflow.com/questions/11369757/postgres-wont-accept-table-alias-before-column-name - $sql = " $debugMsg UPDATE {$this->tables['req_versions']} + $commonWhere AND RQV.id = RC.req_version_id"; + break; + } + + $this->db->exec_query($sql); + } + + // Work on Coverage + $sql = " $debugMsg UPDATE {$this->tables['req_coverage']} " . + " SET link_status = " . intval($reason) . $commonWhere; + $this->db->exec_query($sql); + + // No audit yet + } + + /** + */ + private function closeOpenReqVersionOnOpenLinks($tcversion_id) + { + $debugMsg = "/* {$this->debugMsg}" . __FUNCTION__ . ' */ '; + + $commonWhere = " WHERE tcversion_id = " . intval($tcversion_id) . + " AND link_status = " . LINK_TC_REQ_OPEN; + + // https://stackoverflow.com/questions/11369757/postgres-wont-accept-table-alias-before-column-name + $sql = " $debugMsg UPDATE {$this->tables['req_versions']} SET is_open = 0 WHERE id IN ( - SELECT req_version_id - FROM {$this->tables['req_coverage']} + SELECT req_version_id + FROM {$this->tables['req_coverage']} $commonWhere - ) AND is_open = 1"; - - $this->db->exec_query($sql); - } - - - /** - * copyReqVersionLinksTo - * $source test case info. - * $dest test case info. - * - */ - function copyReqVersionLinksTo($source,$dest,$mappings,$userID) { - - static $reqMgr; - if( is_null($reqMgr) ) { - $reqMgr=new requirement_mgr($this->db); - } - - $itemSet = $reqMgr->getGoodForTCVersion($source['version_id']); - - if( !is_null($itemSet) ) { - - $reqSet = null; - $reqVerSet = null; - - $loop2do=count($itemSet); - for($idx=0; $idx < $loop2do; $idx++) { - - $reqID = $itemSet[$idx]['req_id']; - $reqVerID = $itemSet[$idx]['req_version_id']; - - if( isset($mappings['req'][$reqID]) ) { - $reqSet[$idx] = $mappings['req'][$reqID]; - $reqVerSet[$idx] = $mappings['req_version'][$reqVerID]; - } else { - $reqSet[$idx] = $reqID; - $reqVerSet[$idx] = $reqVerID; - } - - $reqIdCard = array('id' => $reqSet[$idx], - 'version_id' => $reqVerSet[$idx]); - $reqMgr->assignReqVerToTCVer($reqIdCard, $dest, $userID); - } - } - } - - - /** - * - */ - function getReqXMLCfg() { - - $cfgXML = new stdClass(); - - $cfgXML->root = "\t\n{{XMLCODE}}\t\n"; - $cfgXML->elemTPL = - "\t\t\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\t\n" . - "\t\t\t<![CDATA[||REQ_TITLE||]]>\n" . - "\t\t\n"; - - $cfgXML->decode = array( "||REQ_SPEC_TITLE||" => "req_spec_title", - "||REQ_DOC_ID||" => "req_doc_id", - "||REQ_VERSION||" => "version", - "||REQ_TITLE||" => "title"); - - return $cfgXML; - } - - - - /** - * - */ - function getLatestVersionID($tcaseID) { - $sql = "SELECT LTCV.tcversion_id + ) AND is_open = 1"; + + $this->db->exec_query($sql); + } + + /** + * copyReqVersionLinksTo + * $source test case info. + * $dest test case info. + */ + private function copyReqVersionLinksTo($source, $dest, $mappings, $userID) + { + static $reqMgr; + if (is_null($reqMgr)) { + $reqMgr = new requirement_mgr($this->db); + } + + $itemSet = $reqMgr->getGoodForTCVersion($source['version_id']); + + if (! is_null($itemSet)) { + + $reqSet = null; + $reqVerSet = null; + + $loop2do = count($itemSet); + for ($idx = 0; $idx < $loop2do; $idx ++) { + + $reqID = $itemSet[$idx]['req_id']; + $reqVerID = $itemSet[$idx]['req_version_id']; + + if (isset($mappings['req'][$reqID])) { + $reqSet[$idx] = $mappings['req'][$reqID]; + $reqVerSet[$idx] = $mappings['req_version'][$reqVerID]; + } else { + $reqSet[$idx] = $reqID; + $reqVerSet[$idx] = $reqVerID; + } + + $reqIdCard = array( + 'id' => $reqSet[$idx], + 'version_id' => $reqVerSet[$idx] + ); + $reqMgr->assignReqVerToTCVer($reqIdCard, $dest, $userID); + } + } + } + + /** + */ + private function getReqXMLCfg() + { + $cfgXML = new stdClass(); + + $cfgXML->root = "\t\n{{XMLCODE}}\t\n"; + $cfgXML->elemTPL = "\t\t\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\t\n" . + "\t\t\t<![CDATA[||REQ_TITLE||]]>\n" . + "\t\t\n"; + + $cfgXML->decode = array( + "||REQ_SPEC_TITLE||" => "req_spec_title", + "||REQ_DOC_ID||" => "req_doc_id", + "||REQ_VERSION||" => "version", + "||REQ_TITLE||" => "title" + ); + + return $cfgXML; + } + + /** + */ + public function getLatestVersionID($tcaseID) + { + $sql = "SELECT LTCV.tcversion_id FROM {$this->views['latest_tcase_version_id']} LTCV - WHERE LTCV.testcase_id=" . intval($tcaseID); - - $rs = current($this->db->get_recordset($sql)); - - return $rs['tcversion_id']; - } - - /** - * render CF BUILD values with a defined name prefix - * - *

    added by web rich editor create some layout issues - */ - function renderBuildExecVars($context,&$item2render) { - - static $execVars; - - $tplan_id = $context['tplan_id']; - $build_id = $context['build_id']; - - $sql = " SELECT parent_id FROM {$this->tables['nodes_hierarchy']} NHTP - WHERE NHTP.id = $tplan_id - AND NHTP.node_type_id = {$this->node_types_descr_id['testplan']} "; - $dummy = current($this->db->get_recordset($sql)); - - $tproject_id = $dummy['parent_id']; - - if( !($execVars) ) { - $cfx = array('tproject_id' => $tproject_id, 'node_type' => 'build', - 'node_id' => $build_id); - $CFSet = $this->cfield_mgr->getLinkedCfieldsAtDesign($cfx); - - $execVars = array(); - if( null != $CFSet ) { - foreach($CFSet as $cfDef) { - $execVars[$cfDef['name']] = - $this->cfield_mgr->string_custom_field_value($cfDef,$build_id); - } - } - } - - $tcase_id = $item2render['testcase_id']; - $tcversion_id = $item2render['id']; - if( is_null($execVars) ) { - return; - } - - $key2check = array('summary','preconditions'); - $tlBeginTag = '[tlExecVar]'; - $tlEndTag = '[/tlExecVar]'; - $tlEndTagLen = strlen($tlEndTag); - - // I've discovered that working with Web Rich Editor generates - // some additional not wanted entities, that disturb a lot - // when trying to use json_decode(). - // Hope this set is enough. - // $replaceSet = array($tlEndTag, '

    ', '

    ',' '); - // $replaceSetWebRichEditor = array('

    ', '

    ',' '); - - $rse = &$item2render; - foreach($key2check as $item_key) { - $start = strpos($rse[$item_key],$tlBeginTag); - $ghost = $rse[$item_key]; - - // There is at least one request to replace ? - if($start !== FALSE) { - // This way remove may be the

    that webrich editor adds - $play = $rse[$item_key]; - $xx = explode($tlBeginTag,$play); - - // How many requests to replace ? - $xx2do = count($xx); - $ghost = ''; - for($xdx=0; $xdx < $xx2do; $xdx++) { - - // Hope was not a false request. - if( ($es=strpos($xx[$xdx],$tlEndTag)) !== FALSE) { - // Separate command string from other text - // Theorically can be just ONE, but it depends - // is user had not messed things. - $yy = explode($tlEndTag,$xx[$xdx]); - if( ($elc = count($yy)) > 0) { - $cfname = trim($yy[0]); - try { - // look for the custom field - foreach ($execVars as $cfn => $cfv ) { - if( $cfn === $cfname ) { - $ghost .= $execVars[$cfname]; - } - } - - // reconstruct the contect with the other pieces - $lim = $elc-1; - for($cpx=1; $cpx <= $lim; $cpx++) { - $ghost .= $yy[$cpx]; - } - } catch (Exception $e) { - $ghost .= $rse[$item_key]; - } - } - } else { - $ghost .= $xx[$xdx]; - } - } - } - - // reconstruct field contents - if($ghost != '') { - $rse[$item_key] = $ghost; - } - } - } - - /** - * render Special Test Suite Keywords - * - *

    added by web rich editor create some layout issues - */ - function renderSpecialTSuiteKeywords(&$item2render) { - - static $skwSet; - static $key2check; - - $tcase_id = $item2render['testcase_id']; - $tcversion_id = $item2render['id']; - - if( !$key2check ) { - $key2check = array('summary','preconditions'); - } - - if( null==$skwSet || !$skwSet[$tcase_id] ) { - $optSKW = array('getTSuiteKeywords' => true); - $skwSet[$tcase_id] = $this->getPathLayered($tcase_id,$optSKW); - } - - if( is_null($skwSet) ) { - return; - } - - $rse = &$item2render; - - // From PHP Documentation - // $phrase = "You should eat fruits, vegetables, and fiber every day."; - // $healthy = array("fruits", "vegetables", "fiber"); - // $yummy = array("pizza", "beer", "ice cream"); - // - // $newphrase = str_replace($healthy, $yummy, $phrase); - // Provides: You should eat pizza, beer, and ice cream every day - // - $searchSet = null; - $replaceSet = null; - foreach($skwSet as $xdx => $eSet ) { - foreach($eSet as $dm) { - if( null != $dm['data_management'] ) { - foreach($dm['data_management'] as $search => $replace ) { - $searchSet[] = $search; - $replaceSet[] = $replace; - } - } - } - } - - foreach($key2check as $item_key) { - $rse[$item_key] = str_replace($searchSet,$replaceSet,$rse[$item_key]); - } - - } - - /** - * - */ - function getTCVersionAttachIDFromTitle($tcversion_id,$target) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $id = 0; - $sql = " /* $debugMsg */ - SELECT id + WHERE LTCV.testcase_id=" . intval($tcaseID); + + $rs = current($this->db->get_recordset($sql)); + + return $rs['tcversion_id']; + } + + /** + * render CF BUILD values with a defined name prefix + * + *

    added by web rich editor create some layout issues + */ + public function renderBuildExecVars($context, &$item2render) + { + static $execVars; + + $tplan_id = $context['tplan_id']; + $build_id = $context['build_id']; + + $sql = " SELECT parent_id FROM {$this->tables['nodes_hierarchy']} NHTP + WHERE NHTP.id = $tplan_id + AND NHTP.node_type_id = {$this->node_types_descr_id['testplan']} "; + $dummy = current($this->db->get_recordset($sql)); + + $tproject_id = $dummy['parent_id']; + + if (! ($execVars)) { + $cfx = array( + 'tproject_id' => $tproject_id, + 'node_type' => 'build', + 'node_id' => $build_id + ); + $CFSet = $this->cfield_mgr->getLinkedCfieldsAtDesign($cfx); + + $execVars = array(); + if (null != $CFSet) { + foreach ($CFSet as $cfDef) { + $execVars[$cfDef['name']] = $this->cfield_mgr->string_custom_field_value( + $cfDef, $build_id); + } + } + } + + if (is_null($execVars)) { + return; + } + + $key2check = array( + 'summary', + 'preconditions' + ); + $tlBeginTag = '[tlExecVar]'; + $tlEndTag = '[/tlExecVar]'; + + // I've discovered that working with Web Rich Editor generates + // some additional not wanted entities, that disturb a lot + // when trying to use json_decode(). + // Hope this set is enough. + // $replaceSet = array($tlEndTag, '

    ', '

    ',' '); + // $replaceSetWebRichEditor = array('

    ', '

    ',' '); + + $rse = &$item2render; + foreach ($key2check as $item_key) { + $start = strpos($rse[$item_key], $tlBeginTag); + $ghost = $rse[$item_key]; + + // There is at least one request to replace ? + if ($start !== false) { + // This way remove may be the

    that webrich editor adds + $play = $rse[$item_key]; + $xx = explode($tlBeginTag, $play); + + // How many requests to replace ? + $xx2do = count($xx); + $ghost = ''; + for ($xdx = 0; $xdx < $xx2do; $xdx ++) { + + // Hope was not a false request. + if ((strpos($xx[$xdx], $tlEndTag)) !== false) { + // Separate command string from other text + // Theorically can be just ONE, but it depends + // is user had not messed things. + $yy = explode($tlEndTag, $xx[$xdx]); + if (($elc = count($yy)) > 0) { + $cfname = trim($yy[0]); + try { + // look for the custom field + foreach ($execVars as $cfn => $cfv) { + if ($cfn === $cfname) { + $ghost .= $execVars[$cfname]; + } + } + + // reconstruct the contect with the other pieces + $lim = $elc - 1; + for ($cpx = 1; $cpx <= $lim; $cpx ++) { + $ghost .= $yy[$cpx]; + } + } catch (Exception $e) { + $ghost .= $rse[$item_key]; + } + } + } else { + $ghost .= $xx[$xdx]; + } + } + } + + // reconstruct field contents + if ($ghost != '') { + $rse[$item_key] = $ghost; + } + } + } + + /** + * render Special Test Suite Keywords + * + *

    added by web rich editor create some layout issues + */ + private function renderSpecialTSuiteKeywords(&$item2render) + { + static $skwSet; + static $key2check; + + $tcase_id = $item2render['testcase_id']; + + if (! $key2check) { + $key2check = array( + 'summary', + 'preconditions' + ); + } + + if (null == $skwSet || ! isset($skwSet[$tcase_id])) { + $optSKW = array( + 'getTSuiteKeywords' => true + ); + $skwSet[$tcase_id] = $this->getPathLayered($tcase_id, $optSKW); + } + + if (is_null($skwSet)) { + return; + } + + $rse = &$item2render; + + // From PHP Documentation + // $phrase = "You should eat fruits, vegetables, and fiber every day."; + // $healthy = array("fruits", "vegetables", "fiber"); + // $yummy = array("pizza", "beer", "ice cream"); + // + // $newphrase = str_replace($healthy, $yummy, $phrase); + // Provides: You should eat pizza, beer, and ice cream every day + // + $searchSet = null; + $replaceSet = null; + foreach ($skwSet as $eSet) { + foreach ($eSet as $dm) { + if (null != $dm['data_management']) { + foreach ($dm['data_management'] as $search => $replace) { + $searchSet[] = $search; + $replaceSet[] = $replace; + } + } + } + } + + foreach ($key2check as $item_key) { + $rse[$item_key] = str_replace($searchSet, $replaceSet, + $rse[$item_key]); + } + } + + /** + */ + private function getTCVersionAttachIDFromTitle($tcversion_id, $target) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $id = 0; + $sql = " /* $debugMsg */ + SELECT id FROM {$this->tables['attachments']} - WHERE fk_id=" . intval($tcversion_id) . - " AND title = '" . - $this->db->prepare_string($target) . "'"; - - $rs = $this->db->get_recordset($sql); - if( null != $rs ) { - $id = intval($rs[0]['id']); - } - - return $id; - } - - /** - * - * - */ - function getFreeKeywords($idCard,$opt = null) { - $my['opt'] = array('accessKey' => 'keyword_id', 'fields' => null, - 'orderBy' => null, 'tproject_id' => null, - 'output' => 'std', 'add_blank' => false); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $safe = array(); - foreach($idCard as $key => $val) { - $safe[$key] = intval($val); - } - - // CRITIC - $tproject_id = $my['opt']['tproject_id']; - if( null == $tproject_id ) { - $tproject_id = $this->get_testproject($safe['tcase_id']); - } - $tproject_id = intval($tproject_id); - - $sql = " SELECT KW.id AS keyword_id, KW.keyword + WHERE fk_id=" . intval($tcversion_id) . " AND title = '" . + $this->db->prepare_string($target) . "'"; + + $rs = $this->db->get_recordset($sql); + if (null != $rs) { + $id = intval($rs[0]['id']); + } + + return $id; + } + + /** + */ + private function getFreeKeywords($idCard, $opt = null) + { + $my['opt'] = array( + 'accessKey' => 'keyword_id', + 'fields' => null, + 'orderBy' => null, + 'tproject_id' => null, + 'output' => 'std', + 'add_blank' => false + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $safe = array(); + foreach ($idCard as $key => $val) { + $safe[$key] = intval($val); + } + + // CRITIC + $tproject_id = $my['opt']['tproject_id']; + if (null == $tproject_id) { + $tproject_id = $this->get_testproject($safe['tcase_id']); + } + $tproject_id = intval($tproject_id); + + $sql = " SELECT KW.id AS keyword_id, KW.keyword FROM {$this->tables['keywords']} KW WHERE KW.testproject_id = {$tproject_id} - AND KW.id NOT IN + AND KW.id NOT IN ( - SELECT TCKW.keyword_id + SELECT TCKW.keyword_id FROM {$this->tables['testcase_keywords']} TCKW WHERE TCKW.testcase_id = {$safe['tcase_id']} AND TCKW.tcversion_id = {$safe['tcversion_id']} - ) "; - - if (!is_null($my['opt']['orderBy'])) { - $sql .= ' ' . $my['opt']['orderBy']; - } - - switch($my['opt']['output']) { - case 'html_options': - $items = $this->db->fetchColumnsIntoMap($sql,'keyword_id','keyword'); - if( null != $items && $my['opt']['add_blank']) { - $items = array(0 => '') + $items; - } - - break; - - default: - $items = $this->db->fetchRowsIntoMap($sql,$my['opt']['accessKey']); - break; - } - - return $items; - } - - /** - * - */ - function getTCVersionIDFromVersion($tcaseID,$version) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safeTCID = intval($tcaseID); - $safeVersion = intval($version); - - $sql = " SELECT TCV.tcversion_id + ) "; + + if (! is_null($my['opt']['orderBy'])) { + $sql .= ' ' . $my['opt']['orderBy']; + } + + switch ($my['opt']['output']) { + case 'html_options': + $items = $this->db->fetchColumnsIntoMap($sql, 'keyword_id', + 'keyword'); + if (null != $items && $my['opt']['add_blank']) { + $items = array( + 0 => '' + ) + $items; + } + + break; + + default: + $items = $this->db->fetchRowsIntoMap($sql, + $my['opt']['accessKey']); + break; + } + + return $items; + } + + /** + */ + public function getTCVersionIDFromVersion($tcaseID, $version) + { + $safeTCID = intval($tcaseID); + $safeVersion = intval($version); + + $sql = " SELECT TCV.tcversion_id FROM {$this->tables['nodes_hierarcy']} NHTC JOIN {$this->tables['nodes_hierarcy']} NHTCV ON NHTCV.parent_id = NHTC.id JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id - WHERE NHTC.id = $safeTCID AND TCV.version = $safeVersion"; - - - $rs = current($this->db->get_recordset($sql)); - - return $rs['tcversion_id']; - } - - /** - * - */ - function latestVersionHasBeenExecuted($tcaseID) { - $sql = "SELECT COALESCE(E.tcversion_id,0) AS executed + WHERE NHTC.id = $safeTCID AND TCV.version = $safeVersion"; + + $rs = current($this->db->get_recordset($sql)); + + return $rs['tcversion_id']; + } + + /** + */ + public function latestVersionHasBeenExecuted($tcaseID) + { + $sql = "SELECT COALESCE(E.tcversion_id,0) AS executed FROM {$this->views['latest_tcase_version_id']} LTCV - LEFT OUTER JOIN {$this->tables['executions']} E + LEFT OUTER JOIN {$this->tables['executions']} E ON E.tcversion_id = LTCV.tcversion_id - WHERE LTCV.testcase_id=" . intval($tcaseID); - - $rs = current($this->db->get_recordset($sql)); - - return ($rs['executed'] != 0); - } - - - /** - * - */ - function updateTPlanLinkToLatestTCV($tcversionID,$tplanID,$platformID=null,$auditContext=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $fromTCV = intval($tcversionID); - - $sql = "SELECT parent_id AS tc_id - FROM {$this->tables['nodes_hierarchy']} - WHERE id = $fromTCV"; - $rs = current($this->db->get_recordset($sql)); - - $ltcv = $this->getLatestVersionID($rs['tc_id']); - - $safeTP = intval($tplanID); - $whereClause = " WHERE testplan_id = {$safeTP} - AND tcversion_id = $fromTCV "; - - if( ($plat = intval($platformID)) > 0 ) { - $whereClause .= " AND platform_id=$plat "; - } - - $sql = "/* $debugMsg */ - UPDATE {$this->tables['testplan_tcversions']} - SET tcversion_id = " . $ltcv . $whereClause; - $this->db->exec_query($sql); - - - // Execution results - $sql = "/* $debugMsg */ - UPDATE {$this->tables['executions']} - SET tcversion_id = " . $ltcv . $whereClause; - $this->db->exec_query($sql); - - // Update link in cfields values for executions - // ATTENTION: - // platform seems not to be important because - // each execution in each platform has a new id. - // mmm, maybe this will create some minor issue - // in the future. - // - $sql = "/* $debugMsg */ - UPDATE {$this->tables['cfield_execution_values']} - SET tcversion_id = $ltcv - WHERE testplan_id = {$safeTP} - AND tcversion_id = $fromTCV "; - - $this->db->exec_query($sql); - - return $ltcv; - } - - - /** - * Insert note and status for steps to DB - * Delete data before insert => this way we will not add duplicates - * - * IMPORTANT NOTICE: - * if status is not a valid one, blank will be written - * - */ - public function saveStepsPartialExec($partialExec,$context) { - - if (!is_null($partialExec) && count($partialExec) > 0) { - - $stepsIDSet = array_keys($partialExec['notes']); - $this->deleteStepsPartialExec($stepsIDSet,$context); - - $prop = get_object_vars($context); - $safeID = array(); - foreach($prop as $key => $value) { - $safeID[$key] = $this->db->prepare_int($value); - } - - $rCfg = config_get('results'); - $statusSet = $rCfg['status_code']; - $not_run = $statusSet['not_run']; - $statusSet = array_flip($statusSet); - - $statusToExclude = (array)$rCfg['execStatusToExclude']['step']; - $statusToExclude[] = $not_run; - $statusToExclude = array_flip($statusToExclude); - - foreach( $partialExec['notes'] as $stepID => $note ) { - - $s2w = $partialExec['status'][$stepID]; - if( isset($statusToExclude[$s2w]) || - !isset($statusSet[$s2w]) ) { - $s2w = ''; - } - - $sql = " INSERT INTO {$this->tables['execution_tcsteps_wip']} - (tcstep_id,testplan_id,platform_id,build_id,tester_id, - notes,status) VALUES - ({$stepID} ,{$safeID['testplan_id']}, - {$safeID['platform_id']},{$safeID['build_id']}, - {$safeID['tester_id']},'" . - $this->db->prepare_string(htmlspecialchars($note)) . - "', '" . - $this->db->prepare_string($s2w) . - "');"; - $this->db->exec_query($sql); - } - } - } - - /** - * - */ - function updateLatestTPlanLinkToTCV($tcversionID,$tplanID,$auditContext=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // What is the linked version ? - // Need to get all siblings - $sqlA = " SELECT NH_SIB.id + WHERE LTCV.testcase_id=" . intval($tcaseID); + + $rs = current($this->db->get_recordset($sql)); + + return $rs['executed'] != 0; + } + + /** + * ATTENTION: work done here need to be fixed when deleting the latest executed tcversion + * + * @see https://forum.testlink.org/viewtopic.php?f=11&p=21038#p21038 + * + * @see _execution_delete() + * + * @todo 20210901 - understand differences with updateLatestTPlanLinkToTCV(); + * + */ + public function updateTPlanLinkToLatestTCV($tcversionID, $tplanID, + $platformID = null, $auditContext = null) + { + $execContext = new stdClass(); + $execContext->target = new stdClass(); + $execContext->target->tplanID = intval($tplanID); + $execContext->target->tcversionID = intval($tcversionID); + $execContext->target->platformID = intval($platformID); + + return $this->updateTPlanLinkTCV($execContext); + } + + /** + * + * @used by testcaseCommanda.class -> updateTPlanLinkToTCV() + * + * + * @todo 20210901 - understand differences with updateTPlanLinkToLatestTCV(); + * + */ + public function updateLatestTPlanLinkToTCV($tcversionID, $tplanID, + $auditContext = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + // What is the linked version ? + // Need to get all siblings + $sqlA = " SELECT NH_SIB.id FROM {$this->tables['nodes_hierarchy']} NH_SIB WHERE parent_id IN ( - SELECT NH_TCV.parent_id + SELECT NH_TCV.parent_id FROM {$this->tables['nodes_hierarchy']} NH_TCV WHERE id = $tcversionID - ) "; - - $sql = " SELECT TPTCV.id AS link_id - FROM {$this->tables['testplan_tcversions']} TPTCV - WHERE testplan_id = $tplanID - AND tcversion_id IN ($sqlA) "; - - $linkSet = $this->db->fetchRowsIntoMap($sql,'link_id'); - - $sql = " SELECT TPTCV.tcversion_id + ) "; + + $sql = " SELECT TPTCV.id AS link_id FROM {$this->tables['testplan_tcversions']} TPTCV WHERE testplan_id = $tplanID - AND tcversion_id IN ($sqlA) "; - - $tcvSet = $this->db->fetchRowsIntoMap($sqlA,'id'); - - if( count($linkSet) > 0 ) { - - $safeTP = intval($tplanID); - $linkItems = array_keys($linkSet); - $inClause = implode(',',$linkItems); - - // Links to testplan - $sql = "/* $debugMsg */ + AND tcversion_id IN ($sqlA) "; + + $linkSet = $this->db->fetchRowsIntoMap($sql, 'link_id'); + + $tcvSet = $this->db->fetchRowsIntoMap($sqlA, 'id'); + + if (! empty($linkSet)) { + + $safeTP = intval($tplanID); + $linkItems = array_keys($linkSet); + $inClause = implode(',', $linkItems); + + // @TODO 20210901 Understand if order is OK if we add Foreing Keys + // Links to testplan + $sql = "/* $debugMsg */ UPDATE {$this->tables['testplan_tcversions']} - SET tcversion_id = $tcversionID - WHERE testplan_id = $safeTP - AND id IN( $inClause ) "; - - $this->db->exec_query($sql); - - // Access by test case version id - $tcvItems = array_keys($tcvSet); - $inClause = implode(',',$tcvItems); - - // Execution results - $sql = "UPDATE {$this->tables['executions']} - SET tcversion_id = $tcversionID + SET tcversion_id = $tcversionID + WHERE testplan_id = $safeTP + AND id IN( $inClause ) "; + + $this->db->exec_query($sql); + + // Access by test case version id + $tcvItems = array_keys($tcvSet); + $inClause = implode(',', $tcvItems); + + // Execution results + $sql = "UPDATE {$this->tables['executions']} + SET tcversion_id = $tcversionID WHERE testplan_id = $safeTP - AND tcversion_id IN( $inClause )"; - $this->db->exec_query($sql); - - // Update link in cfields values - $sql = "UPDATE {$this->tables['cfield_execution_values']} - SET tcversion_id = $tcversionID + AND tcversion_id IN( $inClause )"; + $this->db->exec_query($sql); + + // Update link in cfields values + $sql = "UPDATE {$this->tables['cfield_execution_values']} + SET tcversion_id = $tcversionID WHERE testplan_id = $safeTP - AND tcversion_id IN( $inClause )"; - $this->db->exec_query($sql); - } - } - - - /** - * - */ - function isLinkedTCVersion($tcVersionID,$tplanID) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe = array('tcVersionID' => intval($tcVersionID), - 'tplanID' => intval($tplanID)); - - $sql = "/* $debugMsg */ + AND tcversion_id IN( $inClause )"; + $this->db->exec_query($sql); + } + } + + /** + * ATTENTION: work done here need to be fixed when deleting the latest executed tcversion + * + * @see https://forum.testlink.org/viewtopic.php?f=11&p=21038#p21038 + * + * @see _execution_delete() execContext->target->tplanID + * ->platformID (can be null => any platform) MAY BE is USELESS + * ->tcversionID + * + * ->update->tcversionID + * if update property does not exists -> Latest TC Version + * + */ + private function updateTPlanLinkTCV($execContext) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $fromTCV = intval($execContext->target->tcversionID); + $sql = "SELECT parent_id AS tc_id + FROM {$this->tables['nodes_hierarchy']} + WHERE id = $fromTCV"; + $rs = current($this->db->get_recordset($sql)); + + if (! property_exists($execContext, 'update')) { + $newTCV = $this->getLatestVersionID($rs['tc_id']); + } else { + $newTCV = $execContext->update->tcversionID; + } + + $safeTP = intval($execContext->target->tplanID); + $whereClause = " WHERE testplan_id = {$safeTP} + AND tcversion_id = $fromTCV "; + + if (property_exists($execContext->target, 'platformID') && + ($plat = intval($execContext->target->platformID)) > 0) { + $whereClause .= " AND platform_id=$plat "; + } + + $sql = "/* $debugMsg */ + UPDATE {$this->tables['testplan_tcversions']} + SET tcversion_id = " . $newTCV . $whereClause; + $this->db->exec_query($sql); + + // Execution results + $sql = "/* $debugMsg */ + UPDATE {$this->tables['executions']} + SET tcversion_id = " . $newTCV . $whereClause; + $this->db->exec_query($sql); + + // Update link in cfields values for executions + // ATTENTION: + // platform seems not to be important because + // each execution in each platform has a new id. + // mmm, maybe this will create some minor issue + // in the future. + // + $sql = "/* $debugMsg */ + UPDATE {$this->tables['cfield_execution_values']} + SET tcversion_id = $newTCV + WHERE testplan_id = {$safeTP} + AND tcversion_id = $fromTCV "; + + $this->db->exec_query($sql); + + return $newTCV; + } + + /** + * Insert note and status for steps to DB + * Delete data before insert => this way we will not add duplicates + * + * IMPORTANT NOTICE: + * if status is not a valid one, blank will be written + */ + public function saveStepsPartialExec($partialExec, $context) + { + if (! empty($partialExec)) { + $stepsIDSet = array_keys($partialExec['notes']); + $this->deleteStepsPartialExec($stepsIDSet, $context); + + $prop = get_object_vars($context); + $safeID = array(); + foreach ($prop as $key => $value) { + $safeID[$key] = $this->db->prepare_int($value); + } + + $rCfg = config_get('results'); + $statusSet = $rCfg['status_code']; + $not_run = $statusSet['not_run']; + $statusSet = array_flip($statusSet); + + $statusToExclude = (array) $rCfg['execStatusToExclude']['step']; + $statusToExclude[] = $not_run; + $statusToExclude = array_flip($statusToExclude); + + foreach ($partialExec['notes'] as $stepID => $note) { + $s2w = $partialExec['status'][$stepID]; + if (isset($statusToExclude[$s2w]) || ! isset($statusSet[$s2w])) { + $s2w = ''; + } + + $safeID['platform_id'] = ($safeID['platform_id'] == - 1) ? 0 : $safeID['platform_id']; + $sql = " INSERT INTO {$this->tables['execution_tcsteps_wip']} + (tcstep_id,testplan_id,platform_id,build_id,tester_id, + notes,status) VALUES + ({$stepID} ,{$safeID['testplan_id']}, + {$safeID['platform_id']},{$safeID['build_id']}, + {$safeID['tester_id']},'" . + $this->db->prepare_string(htmlspecialchars($note)) . "', '" . + $this->db->prepare_string($s2w) . "');"; + $this->db->exec_query($sql); + } + } + } + + /** + */ + private function isLinkedTCVersion($tcVersionID, $tplanID) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $safe = array( + 'tcVersionID' => intval($tcVersionID), + 'tplanID' => intval($tplanID) + ); + + $sql = "/* $debugMsg */ SELECT id FROM {$this->tables['testplan_tcversions']} - WHERE testplan_id = {$safe['tplanID']} - AND tcversion_id = {$safe['tcVersionID']}"; - - $rs = (array)$this->db->get_recordset($sql); - - return (count($rs) > 0); - } - - - /** - * Get Steps Partial Execution record - * - * - * @return array map of result with "tcstep_id" in keys. - * - */ - public function getStepsPartialExec($stepsIds,$context) { - $rs = null; - if (!is_null($stepsIds) && count($stepsIds) > 0) { - - $fields2get = "tcstep_id,testplan_id,platform_id,build_id, - tester_id,notes,status,creation_ts"; - - $sql = "SELECT {$fields2get} - FROM {$this->tables['execution_tcsteps_wip']} - WHERE tcstep_id IN (" . implode(",", $stepsIds) . ") " . - " AND testplan_id = " . - $this->db->prepare_int($context->testplan_id) . - " AND platform_id = " . - $this->db->prepare_int($context->platform_id) . - " AND build_id = " . - $this->db->prepare_int($context->build_id); - $rs = $this->db->fetchRowsIntoMap($sql,"tcstep_id"); - } - return $rs; - } - - /** - * - */ - public function deleteStepsPartialExec($stepsIds,$context) { - $inClause = implode(",",$stepsIds); - if( count($stepsIds) > 0 ) { - $sql = " DELETE FROM {$this->tables['execution_tcsteps_wip']} - WHERE tcstep_id IN (" . $inClause . ") " . - " AND testplan_id = " . - $this->db->prepare_int($context->testplan_id) . - " AND platform_id = " . - $this->db->prepare_int($context->platform_id) . - " AND build_id = " . - $this->db->prepare_int($context->build_id); - $this->db->exec_query($sql); - } - } - - /** - * - */ - function getLatestExecIDInContext($tcversion_id,$ctx) { - - $tplan_id = -1; - $p2c = array('tplan_id','testplan_id'); - foreach( $p2c as $pp ) { - if( property_exists($ctx, $pp) ) { - $tplan_id = $ctx->$pp; - break; - } - } - - $sql = "SELECT id - FROM {$this->views['latest_exec_by_context']} - WHERE tcversion_id= $tcversion_id - AND testplan_id = $tplan_id - AND platform_id = $ctx->platform_id - AND build_id = $ctx->build_id"; - - $rs = $this->db->get_recordset($sql); - - if( null != $rs ) { - return $rs[0]['id']; - } - return -1; - } - - /** - * - * - */ - function getFreePlatforms($idCard,$opt = null) { - $my['opt'] = array('accessKey' => 'platform_id', 'fields' => null, - 'orderBy' => null, 'tproject_id' => null, - 'output' => 'std', 'add_blank' => false); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $safe = array(); - foreach($idCard as $key => $val) { - $safe[$key] = intval($val); - } - - // CRITIC - $tproject_id = $my['opt']['tproject_id']; - if( null == $tproject_id ) { - $tproject_id = $this->get_testproject($safe['tcase_id']); - } - $tproject_id = intval($tproject_id); - - $sql = " SELECT PL.id AS platform_id, PL.name AS platform - FROM {$this->tables['platforms']} PL - WHERE PL.testproject_id = {$tproject_id} - AND PL.enable_on_design = 1 - AND PL.enable_on_execution = 0 - AND PL.id NOT IN - ( - SELECT TCPL.platform_id - FROM {$this->tables['testcase_platforms']} TCPL - WHERE TCPL.testcase_id = {$safe['tcase_id']} - AND TCPL.tcversion_id = {$safe['tcversion_id']} - ) "; - - if (!is_null($my['opt']['orderBy'])) { - $sql .= ' ' . $my['opt']['orderBy']; - } - - switch($my['opt']['output']) { - case 'html_options': - $items = $this->db->fetchColumnsIntoMap($sql,'platform_id','platform'); - if( null != $items && $my['opt']['add_blank']) { - $items = array(0 => '') + $items; - } - - break; - - default: - $items = $this->db->fetchRowsIntoMap($sql,$my['opt']['accessKey']); - break; - } - - return $items; - } - - /** - * - */ - function deletePlatformsByLink($tcID, $linkID, $audit=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safeTCID = intval($tcID); - - $links = (array)$linkID; - $inClause = implode(',',$links); - - $sql = " /* $debugMsg */ - SELECT TCPL.tcversion_id, TCPL.platform_id - FROM {$this->tables['testcase_platforms']} TCPL - WHERE TCPL.testcase_id = {$safeTCID} - AND TCPL.id IN ($inClause) "; - - - $rs = $this->db->get_recordset($sql); - - foreach($rs as $link) { - $this->deletePlatforms($safeTCID, $link['tcversion_id'], - $link['platform_id'],$audit); - } - } - - /** - * - */ - function deletePlatforms($tcID,$versionID,$platID=null,$audit=null) { - - $sql = " DELETE FROM {$this->tables['testcase_platforms']} " . - " WHERE testcase_id = " . intval($tcID) . - " AND tcversion_id = " . intval($versionID); - - $adt = array('on' => self::AUDIT_ON); - $adt = array_merge($adt,(array)$audit); - - if (!is_null($platID)) { - if(is_array($platID)) { - $sql .= " AND platform_id IN (" . implode(',',$platID) . ")"; - $key4log=$platID; - } - else { - $sql .= " AND platform_id = {$platID}"; - $key4log = array($platID); - } - } - else { - $key4log = - array_keys((array)$this->getPlatformsMap($tcID,$versionID)); - } - - $result = $this->db->exec_query($sql); - - /* - if ($result) { - $tcInfo = $this->tree_manager->get_node_hierarchy_info($tcID); - if ($tcInfo && $key4log) { - foreach($key4log as $key2get) { - $keyword = tlKeyword::getByID($this->db,$key2get); - if ($keyword && $adt['on']==self::AUDIT_ON) { - logAuditEvent(TLS("audit_keyword_assignment_removed_tc",$keyword->name,$tcInfo['name']), - "ASSIGN",$tcID,"nodes_hierarchy"); - } - } - } - } - */ - - return $result; - } - - /** - * - */ - function getPlatformsMap($id,$version_id,$opt=null) { - $my['opt'] = array('orderByClause' => '', 'output' => null); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - - switch($my['opt']['output']) { - case 'full': - $sql = "SELECT TCPL.platform_id,PL.name,PL.notes"; - break; - - default: - $sql = "SELECT TCPL.platform_id,PL.name"; - break; - } - $sql .= " FROM {$this->tables['testcase_platforms']} TCPL, - {$this->tables['platforms']} PL - WHERE platform_id = PL.id "; - - $sql .= " AND TCPL.testcase_id = " . intval($id) . - " AND TCPL.tcversion_id = " . intval($version_id); - - $sql .= $my['opt']['orderByClause']; - - - switch($my['opt']['output']) { - case 'full': - $items = $this->db->fetchRowsIntoMap($sql,'platform_id'); - break; - - default: - $items = $this->db->fetchColumnsIntoMap($sql,'platform_id','name'); - break; - } - - return $items; - } - - /** - * - */ - function addPlatforms($id,$version_id,$idSet,$audit=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $adt = array('on' => self::AUDIT_ON, 'version' => null); - $adt = array_merge($adt, (array)$audit); - - if( count($idSet) == 0 ) { - return true; - } - - $safeID = array('tc' => intval($id), 'tcv' => intval($version_id)); - foreach($safeID as $key => $val ) { - if($val <= 0) { - throw new Exception(__METHOD__ . " $key cannot be $val ", 1); - } - } - - // Firts check if records exist - $sql = "/* $debugMsg */ - SELECT platform_id FROM - {$this->tables['testcase_platforms']} - WHERE testcase_id = {$safeID['tc']} - AND tcversion_id = {$safeID['tcv']} - AND platform_id IN (" . implode(',',$idSet) . ")"; - - $nuCheck = $this->db->fetchRowsIntoMap($sql,'platform_id'); - - $sql = "/* $debugMsg */" . - " INSERT INTO {$this->tables['testcase_platforms']} " . - " (testcase_id,tcversion_id,platform_id) VALUES "; - - $dummy = array(); - foreach( $idSet as $kiwi ) { - if( !isset($nuCheck[$kiwi]) ) { - $dummy[] = "($id,$version_id,$kiwi)"; - } - } - - if( count($dummy) <= 0 ) { - return; - } - - // Go ahead - $sql .= implode(',', $dummy); - $this->db->exec_query($sql); - - // Now AUDIT - if ( $adt['on'] == self::AUDIT_ON ) { - - // Audit Context - $tcPath = $this->getPathName( $id ); - - /* - $kwOpt = array('cols' => 'id,keyword', - 'accessKey' => 'id', 'kwSet' => $kw_ids); - $keywordSet = tlKeyword::getSimpleSet($this->db,$kwOpt); - */ - - /* - foreach($keywordSet as $elem ) { - logAuditEvent(TLS("audit_keyword_assigned_tc",$elem['keyword'], - $tcPath,$adt['version']), - "ASSIGN",$version_id,"nodes_hierarchy"); - } - */ - } - - return true; - } - - /** - * - */ - function getPlatforms($tcID,$versionID,$platID = null,$opt = null) { - $my['opt'] = array('accessKey' => 'platform_id', 'fields' => null, - 'orderBy' => null); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $f2g = is_null($my['opt']['fields']) ? - ' TCPL.id AS tcplat_link,platform_id,PL.name,PL.notes, - testcase_id,tcversion_id ' : - $my['opt']['fields']; - - $sql = " SELECT {$f2g} + WHERE testplan_id = {$safe['tplanID']} + AND tcversion_id = {$safe['tcVersionID']}"; + + $rs = (array) $this->db->get_recordset($sql); + + return count($rs) > 0; + } + + /** + * Get Steps Partial Execution record + * + * + * @return array map of result with "tcstep_id" in keys. + * + */ + public function getStepsPartialExec($stepsIds, $context) + { + $rs = null; + if (! empty($stepsIds)) { + $fields2get = "tcstep_id,testplan_id,platform_id,build_id, + tester_id,notes,status,creation_ts"; + + $sql = "SELECT {$fields2get} " . + " FROM {$this->tables['execution_tcsteps_wip']} " . + " WHERE tcstep_id IN (" . implode(",", $stepsIds) . ") " . + " AND testplan_id = " . + $this->db->prepare_int($context->testplan_id) . + " AND platform_id = " . + $this->db->prepare_int($context->platform_id) . + " AND build_id = " . $this->db->prepare_int($context->build_id); + $rs = $this->db->fetchRowsIntoMap($sql, "tcstep_id"); + } + return $rs; + } + + /** + */ + public function deleteStepsPartialExec($stepsIds, $context) + { + if (count($stepsIds) > 0) { + // https://github.com/TestLinkOpenSourceTRMS/testlink-code/pull/327 + // Security + $inClause = $this->db->prepare_string(implode(",", $stepsIds)); + + $sql = " DELETE FROM {$this->tables['execution_tcsteps_wip']} " . + " WHERE tcstep_id IN (" . $inClause . ") " . + " AND testplan_id = " . + $this->db->prepare_int($context->testplan_id) . + " AND platform_id = " . + $this->db->prepare_int($context->platform_id) . + " AND build_id = " . $this->db->prepare_int($context->build_id); + $this->db->exec_query($sql); + } + } + + /** + */ + public function getLatestExecIDInContext($tcversion_id, $ctx) + { + $tplan_id = - 1; + $p2c = array( + 'tplan_id', + 'testplan_id' + ); + foreach ($p2c as $pp) { + if (property_exists($ctx, $pp)) { + $tplan_id = $ctx->$pp; + break; + } + } + + $sql = "SELECT id " . " FROM {$this->views['latest_exec_by_context']} " . + " WHERE tcversion_id= $tcversion_id " . + " AND testplan_id = $tplan_id " . + " AND platform_id = $ctx->platform_id " . + " AND build_id = $ctx->build_id"; + + $rs = $this->db->get_recordset($sql); + + if (null != $rs) { + return $rs[0]['id']; + } + return - 1; + } + + /** + */ + private function getFreePlatforms($idCard, $opt = null) + { + $my['opt'] = array( + 'accessKey' => 'platform_id', + 'fields' => null, + 'orderBy' => null, + 'tproject_id' => null, + 'output' => 'std', + 'add_blank' => false + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $safe = array(); + foreach ($idCard as $key => $val) { + $safe[$key] = intval($val); + } + + // CRITIC + $tproject_id = $my['opt']['tproject_id']; + if (null == $tproject_id) { + $tproject_id = $this->get_testproject($safe['tcase_id']); + } + $tproject_id = intval($tproject_id); + + $sql = " SELECT PL.id AS platform_id, PL.name AS platform " . + " FROM {$this->tables['platforms']} PL " . + " WHERE PL.testproject_id = {$tproject_id} " . + " AND PL.enable_on_design = 1 " . " AND PL.id NOT IN " . + " ( SELECT TCPL.platform_id " . + " FROM {$this->tables['testcase_platforms']} TCPL " . + " WHERE TCPL.testcase_id = {$safe['tcase_id']} " . + " AND TCPL.tcversion_id = {$safe['tcversion_id']} ) "; + + if (! is_null($my['opt']['orderBy'])) { + $sql .= ' ' . $my['opt']['orderBy']; + } + + switch ($my['opt']['output']) { + case 'html_options': + $items = $this->db->fetchColumnsIntoMap($sql, 'platform_id', + 'platform'); + if (null != $items && $my['opt']['add_blank']) { + $items = array( + 0 => '' + ) + $items; + } + + break; + + default: + $items = $this->db->fetchRowsIntoMap($sql, + $my['opt']['accessKey']); + break; + } + + return $items; + } + + /** + */ + public function deletePlatformsByLink($tcID, $linkID, $audit = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $safeTCID = intval($tcID); + + $links = (array) $linkID; + $inClause = implode(',', $links); + + $sql = " /* $debugMsg */ SELECT TCPL.tcversion_id, TCPL.platform_id " . + " FROM {$this->tables['testcase_platforms']} TCPL " . + " WHERE TCPL.testcase_id = {$safeTCID} " . + " AND TCPL.id IN ($inClause) "; + + $rs = $this->db->get_recordset($sql); + + foreach ($rs as $link) { + $this->deletePlatforms($safeTCID, $link['tcversion_id'], + $link['platform_id'], $audit); + } + } + + /** + */ + public function deletePlatforms($tcID, $versionID, $platID = null, + $audit = null) + { + $adt = array( + 'on' => self::AUDIT_ON + ); + $adt = array_merge($adt, (array) $audit); + + $sql = " DELETE FROM {$this->tables['testcase_platforms']} " . + " WHERE testcase_id = " . intval($tcID) . " AND tcversion_id = " . + intval($versionID); + if (! is_null($platID)) { + if (is_array($platID)) { + $sql .= " AND platform_id IN (" . implode(',', $platID) . ")"; + } else { + $sql .= " AND platform_id = {$platID}"; + } + } + + return $this->db->exec_query($sql); + } + + /** + */ + private function getPlatformsMap($id, $version_id, $opt = null) + { + $my['opt'] = array( + 'orderByClause' => '', + 'output' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + switch ($my['opt']['output']) { + case 'full': + $sql = "SELECT TCPL.platform_id,PL.name,PL.notes, + PL.enable_on_design,PL.enable_on_execution"; + break; + + default: + $sql = "SELECT TCPL.platform_id,PL.name"; + break; + } + $sql .= " FROM {$this->tables['testcase_platforms']} TCPL, + {$this->tables['platforms']} PL + WHERE platform_id = PL.id "; + + $sql .= " AND TCPL.testcase_id = " . intval($id) . + " AND TCPL.tcversion_id = " . intval($version_id); + + $sql .= $my['opt']['orderByClause']; + + switch ($my['opt']['output']) { + case 'full': + $items = $this->db->fetchRowsIntoMap($sql, 'platform_id'); + break; + + default: + $items = $this->db->fetchColumnsIntoMap($sql, 'platform_id', + 'name'); + break; + } + + return $items; + } + + /** + */ + private function getPlatformsAsXMLString($id, $version_id) + { + require_once '../../third_party/adodb_xml/class.ADODB_XML.php'; + + $sql = "SELECT TCPL.platform_id,PL.name,PL.notes, + PL.enable_on_design,PL.enable_on_execution + FROM {$this->tables['testcase_platforms']} TCPL, + {$this->tables['platforms']} PL + WHERE platform_id = PL.id "; + $sql .= " AND TCPL.testcase_id = " . intval($id) . + " AND TCPL.tcversion_id = " . intval($version_id); + + $adodbXML = new ADODB_XML(); // it's ok because we do not want to write the header + $adodbXML->setRootTagName('platforms'); + $adodbXML->setRowTagName('platform'); + return $adodbXML->ConvertToXMLString($this->db->db, $sql, + ('write_header' == 'no')); + } + + /** + */ + public function addPlatforms($id, $version_id, $idSet, $audit = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $adt = array( + 'on' => self::AUDIT_ON, + 'version' => null + ); + $adt = array_merge($adt, (array) $audit); + + if (count($idSet) == 0) { + return true; + } + + $safeID = array( + 'tc' => intval($id), + 'tcv' => intval($version_id) + ); + foreach ($safeID as $key => $val) { + if ($val <= 0) { + throw new Exception(__METHOD__ . " $key cannot be $val ", 1); + } + } + + // Firts check if records exist + $sql = "/* $debugMsg */ + SELECT platform_id FROM + {$this->tables['testcase_platforms']} + WHERE testcase_id = {$safeID['tc']} + AND tcversion_id = {$safeID['tcv']} + AND platform_id IN (" . implode(',', $idSet) . ")"; + + $nuCheck = $this->db->fetchRowsIntoMap($sql, 'platform_id'); + + $sql = "/* $debugMsg */" . + " INSERT INTO {$this->tables['testcase_platforms']} " . + " (testcase_id,tcversion_id,platform_id) VALUES "; + + $dummy = array(); + foreach ($idSet as $kiwi) { + if (! isset($nuCheck[$kiwi])) { + $dummy[] = "($id,$version_id,$kiwi)"; + } + } + + if (empty($dummy)) { + return; + } + + // Go ahead + $sql .= implode(',', $dummy); + $this->db->exec_query($sql); + + // Now AUDIT + if ($adt['on'] == self::AUDIT_ON) { + // Audit Context + $this->getPathName($id); + } + + return true; + } + + /** + */ + public function getPlatforms($tcID, $versionID, $platID = null, $opt = null) + { + $my['opt'] = array( + 'accessKey' => 'platform_id', + 'fields' => null, + 'orderBy' => null + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $f2g = is_null($my['opt']['fields']) ? ' TCPL.id AS tcplat_link,platform_id,PL.name,PL.notes, + testcase_id,tcversion_id ' : $my['opt']['fields']; + + $sql = " SELECT {$f2g} FROM {$this->tables['testcase_platforms']} TCPL JOIN {$this->tables['platforms']} PL - ON platform_id = PL.id "; - - $sql .= " WHERE testcase_id = " . intval($tcID) . - " AND tcversion_id=" . intval($versionID); - - if (!is_null($platID)) { - $sql .= " AND platform_id = " . intval($platID); - } - - if (!is_null($my['opt']['orderBy'])) { - $sql .= ' ' . $my['opt']['orderBy']; - } - - switch( $my['opt']['accessKey'] ) { - case 'testcase_id,tcversion_id'; - $items = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','tcversion_id',database::CUMULATIVE); - break; - - default: - $items = $this->db->fetchRowsIntoMap($sql,$my['opt']['accessKey']); - break; - } - - return $items; - } - - - /** - * - */ - function getDeleteTCVPlatformRelativeURL($identity,&$guiObj=null) { - $url = "lib/testcases/tcEdit.php?doAction=removePlatform"; - - if( null != $guiObj ) { - $p2l = array('show_mode','tplan_id'); - foreach($p2l as $pr) { - if( property_exists($guiObj, $pr) ) { - $url .= "&$pr=" . $guiObj->$pr; - } - } - } - - $url .= '&tcase_id=%1&tcplat_link_id=%2'; - return $url; - } - - /** - * mappings is only useful when source_id and target_id do not belong - * to same Test Project. - * Because platforms are defined INSIDE a Test Project, - * ID will be different for same keyword - * in a different Test Project. - * - */ - function copyPlatformsTo($source,$dest,$platMap,$auditContext=null,$opt=null) { - - $adt = array('on' => self::AUDIT_ON); - if( isset($dest['version']) ) { - $adt['version'] = $dest['version']; - } - $adt = array_merge($adt,(array)$auditContext); - - $what = array('delete' => true); - $what = array_merge($what,(array)$opt); - - // Not sure that this delete is needed (@20180610) - if( $what['delete'] ) { - $this->deletePlatforms($dest['id'],$dest['version_id'],null,$auditContext); - } - - $sourceIT = $this->getPlatforms($source['id'],$source['version_id']); - - if( !is_null($sourceIT) ) { - - // build item id list - $itSet = array_keys($sourceIT); - if( null != $platMap ) { - foreach($itSet as $itemPos => $itemID) { - if( isset($mappings[$itemID]) ) { - $itSet[$itemPos] = $mappings[$itemID]; - } - } - } - - $this->addPlatforms($dest['id'],$dest['version_id'],$itSet,$adt); - } - - return true; - } - - - - -} // Class end + ON platform_id = PL.id "; + + $sql .= " WHERE testcase_id = " . intval($tcID) . " AND tcversion_id=" . + intval($versionID); + + if (! is_null($platID)) { + $sql .= " AND platform_id = " . intval($platID); + } + + if (! is_null($my['opt']['orderBy'])) { + $sql .= ' ' . $my['opt']['orderBy']; + } + + switch ($my['opt']['accessKey']) { + case 'testcase_id,tcversion_id': + $items = $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', + 'tcversion_id', database::CUMULATIVE); + break; + + default: + $items = $this->db->fetchRowsIntoMap($sql, + $my['opt']['accessKey']); + break; + } + + return $items; + } + + /** + */ + private function getDeleteTCVPlatformRelativeURL(&$guiObj = null) + { + $url = "lib/testcases/tcEdit.php?doAction=removePlatform"; + + if (null != $guiObj) { + $p2l = array( + 'show_mode', + 'tplan_id' + ); + foreach ($p2l as $pr) { + if (property_exists($guiObj, $pr)) { + $url .= "&$pr=" . $guiObj->$pr; + } + } + } + + $url .= '&tcase_id=%1&tcplat_link_id=%2'; + return $url; + } + + /** + * mappings is only useful when source_id and target_id do not belong + * to same Test Project. + * Because platforms are defined INSIDE a Test Project, + * ID will be different for same keyword + * in a different Test Project. + */ + private function copyPlatformsTo(array $source, $dest, $platMap, + $auditContext = null, $opt = null): bool + { + $adt = array( + 'on' => self::AUDIT_ON + ); + if (isset($dest['version'])) { + $adt['version'] = $dest['version']; + } + $adt = array_merge($adt, (array) $auditContext); + + $what = array( + 'delete' => true + ); + $what = array_merge($what, (array) $opt); + + // Not sure that this delete is needed (@20180610) + if ($what['delete']) { + $this->deletePlatforms($dest['id'], $dest['version_id'], null, + $auditContext); + } + + $sourceIT = $this->getPlatforms($source['id'], $source['version_id']); + + if (! is_null($sourceIT)) { + + // build item id list + $itSet = array_keys($sourceIT); + if (null != $platMap) { + foreach ($itSet as $itemPos => $itemID) { + if (isset($mappings[$itemID])) { + $itSet[$itemPos] = $mappings[$itemID]; + } + } + } + + $this->addPlatforms($dest['id'], $dest['version_id'], $itSet, $adt); + } + + return true; + } + + /** + */ + public function getLTCVInfo($tcaseID) + { + $parentSet = (array) $tcaseID; + $sql = "SELECT + NHTC.name, NHTCV.node_order, + NHTC.parent_id AS testsuite_id, + LTCV.tcversion_id, TCV.id, TCV.version, + NHTCV.parent_id AS testcase_id, + TCV.active, TCV.tc_external_id, + TCV.execution_type, TCV.importance, + TCV.status + FROM {$this->views['latest_tcase_version_id']} LTCV + JOIN {$this->tables['tcversions']} TCV + ON TCV.id = LTCV.tcversion_id + JOIN {$this->tables['nodes_hierarchy']} NHTCV + ON NHTCV.id = TCV.id + JOIN {$this->tables['nodes_hierarchy']} NHTC + ON NHTC.id = NHTCV.parent_id + WHERE LTCV.testcase_id IN (" . implode(',', $parentSet) . ")"; + + return $this->db->get_recordset($sql); + } + + /** + */ + private function getTestProjectKeywords($tproj_id) + { + return $this->tproject_mgr->getKeywordsAsMapByName($tproj_id); + } +} diff --git a/lib/functions/testplan.class.php b/lib/functions/testplan.class.php index 71384fd29f..597bee26d6 100644 --- a/lib/functions/testplan.class.php +++ b/lib/functions/testplan.class.php @@ -1,8935 +1,8645 @@ - "XML"); // array("XML" => "XML", "XLS" => "XLS" ); - - var $resultsCfg; - var $tcaseCfg; - - var $notRunStatusCode; - var $execTaskCode; - - - // Nodes to exclude when do test plan tree traversal - var $nt2exclude=array('testplan' => 'exclude_me', - 'requirement_spec'=> 'exclude_me', - 'requirement'=> 'exclude_me'); - - var $nt2exclude_children=array('testcase' => 'exclude_my_children', - 'requirement_spec'=> 'exclude_my_children'); - - /** - * testplan class constructor - * - * @param resource &$db reference to database handler - */ - function __construct(&$db) - { - $this->db = &$db; - $this->tree_manager = new tree($this->db); - $this->node_types_descr_id = $this->tree_manager->get_available_node_types(); - $this->node_types_id_descr = array_flip($this->node_types_descr_id); - - $this->assignment_mgr = new assignment_mgr($this->db); - $this->assignment_types = $this->assignment_mgr->get_available_types(); - $this->assignment_status = $this->assignment_mgr->get_available_status(); - - $this->cfield_mgr = new cfield_mgr($this->db); - $this->tcase_mgr = New testcase($this->db); - $this->platform_mgr = new tlPlatform($this->db); - $this->tproject_mgr = new testproject($this->db); - - - $this->resultsCfg = config_get('results'); - $this->tcaseCfg = config_get('testcase_cfg'); - - - // special values used too many times - $this->notRunStatusCode = $this->resultsCfg['status_code']['not_run']; - $this->execTaskCode = intval($this->assignment_types['testcase_execution']['id']); - - tlObjectWithAttachments::__construct($this->db,'testplans'); - } - - /** - * getter for import types - * @return array key: import file type code, value: import file type verbose description - */ - function get_import_file_types() - { - return $this->import_file_types; - } - - /** - * creates a tesplan on Database, for a testproject. - * - * @param string $name: testplan name - * @param string $notes: testplan notes - * @param string $testproject_id: testplan parent - * - * @return integer status code - * if everything ok -> id of new testplan (node id). - * if problems -> 0. - */ - function create($name,$notes,$testproject_id,$is_active=1,$is_public=1) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $node_types=$this->tree_manager->get_available_node_types(); - $tplan_id = $this->tree_manager->new_node($testproject_id,$node_types['testplan'],$name); - - $active_status=intval($is_active) > 0 ? 1 : 0; - $public_status=intval($is_public) > 0 ? 1 : 0; - - $api_key = md5(rand()) . md5(rand()); - - $sql = "/* $debugMsg */ " . - " INSERT INTO {$this->tables['testplans']} (id,notes,api_key,testproject_id,active,is_public) " . - " VALUES ( {$tplan_id} " . ", '" . $this->db->prepare_string($notes) . "'," . - "'" . $this->db->prepare_string($api_key) . "'," . - $testproject_id . "," . $active_status . "," . $public_status . ")"; - $result = $this->db->exec_query($sql); - $id = 0; - if ($result) - { - $id = $tplan_id; - } - - return $id; - } - - - /** - * - */ - function createFromObject($item,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('doChecks' => false, 'setSessionProject' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - try { - // mandatory checks - if(strlen($item->name)==0) { - throw new Exception('Empty name is not allowed'); - } - - // what checks need to be done ? - // 1. test project exist - $pinfo = null; - if( is_numeric($item->testProjectID) ) { - $pinfo = $this->tproject_mgr->get_by_id(intval($item->testProjectID)); - } - - if( null == $pinfo || count($pinfo) == 0 ) { - $pinfo = $this->tproject_mgr->get_by_prefix($item->testProjectID); - } - - if( is_null($pinfo) || count($pinfo) == 0 ) { - throw new Exception('Test project ID does not exist'); - } - - $tproject_id = intval($pinfo['id']); - - // 2. there is NO other test plan on test project with same name - $name = trim($item->name); - $op = $this->checkNameExistence($name,$tproject_id); - if(!$op['status_ok']) { - throw new Exception('Test plan name is already in use on Test project'); - } - } catch (Exception $e) { - throw $e; // rethrow - } - - // seems OK => go - $active_status = intval($item->active) > 0 ? 1 : 0; - $public_status = intval($item->is_public) > 0 ? 1 : 0; - - $api_key = md5(rand()) . md5(rand()); - - $id = $this->tree_manager->new_node($tproject_id,$this->node_types_descr_id['testplan'],$name); - $sql = "/* $debugMsg */ " . - " INSERT INTO {$this->tables['testplans']} (id,notes,api_key,testproject_id,active,is_public) " . - " VALUES ( {$id} " . ", '" . $this->db->prepare_string($item->notes) . "'," . - "'" . $this->db->prepare_string($api_key) . "'," . - $tproject_id . "," . - $active_status . "," . $public_status . ")"; - $result = $this->db->exec_query($sql); - return $result ? $id : 0; - } - - - /** - * - */ - function updateFromObject($item,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('doChecks' => false, 'setSessionProject' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - if( !property_exists($item, 'id') ) { - throw new Exception('Test plan ID is missing'); - } - - if( ($safeID = intval($item->id)) == 0 ) { - throw new Exception('Test plan ID 0 is not allowed'); - } - - $pinfo = $this->get_by_id($safeID, array( 'output' => 'minimun')); - if(is_null($pinfo)) { - throw new Exception('Test plan ID does not exist'); - } - - $attr = array(); - $upd = ''; - try { - if( property_exists($item, 'name') ) { - $name = trim($item->name); - if(strlen($name)==0) { - throw new Exception('Empty name is not allowed'); - } - - // 1. NO other test plan on test project with same name - $op = $this->checkNameExistence($name,$pinfo['testproject_id'],$safeID); - if(!$op['status_ok']) { - throw new Exception('Test plan name is already in use on Test project'); - } - - $sql = "/* $debugMsg */ " . - " UPDATE {$this->tables['nodes_hierarchy']} " . - " SET name='" . $this->db->prepare_string($name) . "'" . - " WHERE id={$safeID}"; - $result = $this->db->exec_query($sql); - } - - if( property_exists($item, 'notes') ) { - $upd = ($upd != '' ? ',' : '') . " notes = '" . $this->db->prepare_string($item->notes) . "' "; - } - - $intAttr = array('active','is_public'); - foreach($intAttr as $key) { - if( property_exists($item, $key) ) { - $upd = ($upd != '' ? ',' : '') . $key . ' = ' . (intval($item->$key) > 0 ? 1 : 0); - } - } - - if($upd != '') { - $sql = " UPDATE {$this->tables['testplans']} " . - " SET {$upd} WHERE id=" . $safeID; - $result = $this->db->exec_query($sql); - } - } catch (Exception $e) { - throw $e; // rethrow - } - return $safeID; - } - - - - /** - * Checks is there is another test plan inside test project - * with different id but same name - * - **/ - function checkNameExistence($name,$tprojectID,$id=0) { - $check_op['msg'] = ''; - $check_op['status_ok'] = 1; - - if($this->get_by_name($name,intval($tprojectID), array('id' => intval($id))) ) { - $check_op['msg'] = sprintf(lang_get('error_product_name_duplicate'),$name); - $check_op['status_ok'] = 0; - } - return $check_op; - } - - - /** - * update testplan information - * - * @param integer $id Test plan identifier - * @param string $name: testplan name - * @param string $notes: testplan notes - * @param boolean $is_active - * - * @return integer result code (1=ok) - */ - function update($id,$name,$notes,$is_active=null,$is_public=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $do_update = 1; - $result = null; - // $active = to_boolean($is_active); - $name = trim($name); - - // two tables to update and we have no transaction yet. - $rsa = $this->get_by_id($id); - $duplicate_check = (strcmp($rsa['name'],$name) != 0 ); - - if($duplicate_check) - { - $rs = $this->get_by_name($name,$rsa['parent_id']); - $do_update = is_null($rs); - } - - if($do_update) - { - // Update name - $sql = "/* $debugMsg */ "; - $sql .= "UPDATE {$this->tables['nodes_hierarchy']} " . - "SET name='" . $this->db->prepare_string($name) . "'" . - "WHERE id={$id}"; - $result = $this->db->exec_query($sql); - - if($result) - { - $add_upd=''; - if( !is_null($is_active) ) - { - $add_upd .=',active=' . (intval($is_active) > 0 ? 1 : 0); - } - if( !is_null($is_public) ) - { - $add_upd .=',is_public=' . (intval($is_public) > 0 ? 1:0); - } - - $sql = " UPDATE {$this->tables['testplans']} " . - " SET notes='" . $this->db->prepare_string($notes). "' " . - " {$add_upd} WHERE id=" . $id; - $result = $this->db->exec_query($sql); - } - } - return ($result ? 1 : 0); - } - - - /* - function: get_by_name - get information about a testplan using name as access key. - Search can be narrowed, givin a testproject id as filter criteria. - - args: name: testplan name - [tproject_id]: default:0 -> system wide search i.e. inside all testprojects - - returns: if nothing found -> null - if found -> array where every element is a map with following keys: - id: testplan id - notes: - active: active status - is_open: open status - name: testplan name - testproject_id - */ - function get_by_name($name,$tproject_id=0,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my = array(); - $my['opt'] = array('output' => 'full', 'id' => 0); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = "/* $debugMsg */ "; - - switch($my['opt']['output']) - { - case 'minimun': - $sql .= " SELECT testplans.id, NH.name "; - break; - - case 'full': - default: - $sql .= " SELECT testplans.*, NH.name "; - break; - } - - $sql .= " FROM {$this->tables['testplans']} testplans, " . - " {$this->tables['nodes_hierarchy']} NH" . - " WHERE testplans.id = NH.id " . - " AND NH.name = '" . $this->db->prepare_string($name) . "'"; - - if( ($safe_id = intval($tproject_id)) > 0 ) - { - $sql .= " AND NH.parent_id={$safe_id} "; - } - - // useful when trying to check for duplicates ? - if( ($my['opt']['id'] = intval($my['opt']['id'])) > 0) - { - $sql .= " AND testplans.id != {$my['opt']['id']} "; - } - - $rs = $this->db->get_recordset($sql); - return($rs); - } - - - /* - function: get_by_id - - args : id: testplan id - - returns: map with following keys: - id: testplan id - name: testplan name - notes: testplan notes - testproject_id - active - is_open - parent_id - */ - function get_by_id($id, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array(); - $my['opt'] = array('output' => 'full','active' => null, 'testPlanFields' => ''); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $safe_id = intval($id); - switch($my['opt']['output']) { - case 'testPlanFields': - $sql = "/* $debugMsg */ " . - " SELECT {$my['opt']['testPlanFields']} FROM {$this->tables['testplans']} " . - " WHERE id = " . $safe_id; - break; - - case 'minimun': - $sql = "/* $debugMsg */ " . - " SELECT NH_TPLAN.name," . - " NH_TPROJ.id AS tproject_id, NH_TPROJ.name AS tproject_name,TPROJ.prefix" . - " FROM {$this->tables['nodes_hierarchy']} NH_TPLAN " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TPROJ ON NH_TPROJ.id = NH_TPLAN.parent_id " . - " JOIN {$this->tables['testprojects']} TPROJ ON TPROJ.ID = NH_TPROJ.id " . - " WHERE NH_TPLAN.id = " . $safe_id; - break; - - case 'full': - default: - $sql = "/* $debugMsg */ " . - " SELECT TPLAN.*,NH_TPLAN.name,NH_TPLAN.parent_id, - NH_TPROJ.id AS tproject_id, - NH_TPROJ.name AS tproject_name,TPROJ.prefix - FROM {$this->tables['testplans']} TPLAN, + "XML" + ); + + private $resultsCfg; + + private $tcaseCfg; + + protected $notRunStatusCode; + + protected $execTaskCode; + + // Nodes to exclude when do test plan tree traversal + private $nt2exclude = array( + 'testplan' => 'exclude_me', + 'requirement_spec' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + + private $nt2exclude_children = array( + 'testcase' => 'exclude_my_children', + 'requirement_spec' => 'exclude_my_children' + ); + + /** + * testplan class constructor + * + * @param + * resource &$db reference to database handler + */ + public function __construct(&$db) + { + $this->db = &$db; + $this->tree_manager = new tree($this->db); + $this->node_types_descr_id = $this->tree_manager->get_available_node_types(); + $this->node_types_id_descr = array_flip($this->node_types_descr_id); + + $this->assignment_mgr = new assignment_mgr($this->db); + $this->assignment_types = $this->assignment_mgr->get_available_types(); + $this->assignment_status = $this->assignment_mgr->get_available_status(); + + $this->cfield_mgr = new cfield_mgr($this->db); + $this->tcaseMgr = new testcase($this->db); + $this->platform_mgr = new tlPlatform($this->db); + $this->tproject_mgr = new testproject($this->db); + + $this->resultsCfg = config_get('results'); + $this->tcaseCfg = config_get('testcase_cfg'); + + // special values used too many times + $this->notRunStatusCode = $this->resultsCfg['status_code']['not_run']; + $this->execTaskCode = intval( + $this->assignment_types['testcase_execution']['id']); + + tlObjectWithAttachments::__construct($this->db, 'testplans'); + } + + /** + * getter for import types + * + * @return array key: import file type code, value: import file type verbose description + */ + public function get_import_file_types() + { + return $this->import_file_types; + } + + /** + * creates a tesplan on Database, for a testproject. + * + * @param string $name: + * testplan name + * @param string $notes: + * testplan notes + * @param string $testproject_id: + * testplan parent + * + * @return integer status code + * if everything ok -> id of new testplan (node id). + * if problems -> 0. + */ + public function create($name, $notes, $testproject_id, $is_active = 1, + $is_public = 1) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $node_types = $this->tree_manager->get_available_node_types(); + $tplan_id = $this->tree_manager->new_node($testproject_id, + $node_types['testplan'], $name); + + $active_status = intval($is_active) > 0 ? 1 : 0; + $public_status = intval($is_public) > 0 ? 1 : 0; + + $api_key = md5(rand()) . md5(rand()); + + $sql = "/* $debugMsg */ " . + " INSERT INTO {$this->tables['testplans']} (id,notes,api_key,testproject_id,active,is_public) " . + " VALUES ( {$tplan_id} " . ", '" . $this->db->prepare_string($notes) . + "'," . "'" . $this->db->prepare_string($api_key) . "'," . + $testproject_id . "," . $active_status . "," . $public_status . ")"; + $result = $this->db->exec_query($sql); + $id = 0; + if ($result) { + $id = $tplan_id; + } + + return $id; + } + + /** + */ + public function createFromObject($item, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $my['opt'] = array( + 'doChecks' => false, + 'setSessionProject' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + try { + // mandatory checks + if (strlen($item->name) == 0) { + throw new Exception('Empty name is not allowed'); + } + + // what checks need to be done ? + // 1. test project exist + $pinfo = null; + if (is_numeric($item->testProjectID)) { + $pinfo = $this->tproject_mgr->get_by_id( + intval($item->testProjectID)); + } + + if (null == $pinfo || empty($pinfo)) { + $pinfo = $this->tproject_mgr->get_by_prefix( + $item->testProjectID); + } + + if (is_null($pinfo) || empty($pinfo)) { + throw new Exception('Test project ID does not exist'); + } + + $tproject_id = intval($pinfo['id']); + + // 2. there is NO other test plan on test project with same name + $name = trim($item->name); + $op = $this->checkNameExistence($name, $tproject_id); + if (! $op['status_ok']) { + throw new Exception( + 'Test plan name is already in use on Test project'); + } + } catch (Exception $e) { + throw $e; + } + + // seems OK => go + $active_status = intval($item->active) > 0 ? 1 : 0; + $public_status = intval($item->is_public) > 0 ? 1 : 0; + + $api_key = md5(rand()) . md5(rand()); + + $id = $this->tree_manager->new_node($tproject_id, + $this->node_types_descr_id['testplan'], $name); + $sql = "/* $debugMsg */ " . + " INSERT INTO {$this->tables['testplans']} (id,notes,api_key,testproject_id,active,is_public) " . + " VALUES ( {$id} " . ", '" . $this->db->prepare_string($item->notes) . + "'," . "'" . $this->db->prepare_string($api_key) . "'," . + $tproject_id . "," . $active_status . "," . $public_status . ")"; + $result = $this->db->exec_query($sql); + return $result ? $id : 0; + } + + /** + */ + public function updateFromObject($item, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $my['opt'] = array( + 'doChecks' => false, + 'setSessionProject' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + if (! property_exists($item, 'id')) { + throw new Exception('Test plan ID is missing'); + } + + if (($safeID = intval($item->id)) == 0) { + throw new Exception('Test plan ID 0 is not allowed'); + } + + $pinfo = $this->get_by_id($safeID, array( + 'output' => 'minimun' + )); + if (is_null($pinfo)) { + throw new Exception('Test plan ID does not exist'); + } + + $upd = ''; + try { + if (property_exists($item, 'name')) { + $name = trim($item->name); + if (strlen($name) == 0) { + throw new Exception('Empty name is not allowed'); + } + + // 1. NO other test plan on test project with same name + $op = $this->checkNameExistence($name, $pinfo['testproject_id'], + $safeID); + if (! $op['status_ok']) { + throw new Exception( + 'Test plan name is already in use on Test project'); + } + + $sql = "/* $debugMsg */ " . + " UPDATE {$this->tables['nodes_hierarchy']} " . " SET name='" . + $this->db->prepare_string($name) . "'" . + " WHERE id={$safeID}"; + $this->db->exec_query($sql); + } + + if (property_exists($item, 'notes')) { + $upd .= ($upd != '' ? ',' : '') . " notes = '" . + $this->db->prepare_string($item->notes) . "' "; + } + + $intAttr = array( + 'active', + 'is_public' + ); + foreach ($intAttr as $key) { + if (property_exists($item, $key)) { + $upd .= ($upd != '' ? ',' : '') . $key . ' = ' . + (intval($item->$key) > 0 ? 1 : 0); + } + } + + if ($upd != '') { + $sql = " UPDATE {$this->tables['testplans']} " . + " SET {$upd} WHERE id=" . $safeID; + $this->db->exec_query($sql); + } + } catch (Exception $e) { + throw $e; // rethrow + } + return $safeID; + } + + /** + * Checks is there is another test plan inside test project + * with different id but same name + */ + private function checkNameExistence($name, $tprojectID, $id = 0) + { + $check_op['msg'] = ''; + $check_op['status_ok'] = 1; + + if ($this->get_by_name($name, intval($tprojectID), + array( + 'id' => intval($id) + ))) { + $check_op['msg'] = sprintf(lang_get('error_product_name_duplicate'), + $name); + $check_op['status_ok'] = 0; + } + return $check_op; + } + + /** + * update testplan information + * + * @param integer $id + * Test plan identifier + * @param string $name: + * testplan name + * @param string $notes: + * testplan notes + * @param boolean $is_active + * + * @return integer result code (1=ok) + */ + public function update($id, $name, $notes, $is_active = null, + $is_public = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $do_update = 1; + $result = null; + $name = trim($name); + + // two tables to update and we have no transaction yet. + $rsa = $this->get_by_id($id); + $duplicate_check = (strcmp($rsa['name'], $name) != 0); + + if ($duplicate_check) { + $rs = $this->get_by_name($name, $rsa['parent_id']); + $do_update = is_null($rs); + } + + if ($do_update) { + // Update name + $sql = "/* $debugMsg */ "; + $sql .= "UPDATE {$this->tables['nodes_hierarchy']} " . "SET name='" . + $this->db->prepare_string($name) . "'" . "WHERE id={$id}"; + $result = $this->db->exec_query($sql); + + if ($result) { + $add_upd = ''; + if (! is_null($is_active)) { + $add_upd .= ',active=' . (intval($is_active) > 0 ? 1 : 0); + } + if (! is_null($is_public)) { + $add_upd .= ',is_public=' . (intval($is_public) > 0 ? 1 : 0); + } + + $sql = " UPDATE {$this->tables['testplans']} " . " SET notes='" . + $this->db->prepare_string($notes) . "' " . + " {$add_upd} WHERE id=" . $id; + $result = $this->db->exec_query($sql); + } + } + return $result ? 1 : 0; + } + + /* + * function: get_by_name + * get information about a testplan using name as access key. + * Search can be narrowed, givin a testproject id as filter criteria. + * + * args: name: testplan name + * [tproject_id]: default:0 -> system wide search i.e. inside all testprojects + * + * returns: if nothing found -> null + * if found -> array where every element is a map with following keys: + * id: testplan id + * notes: + * active: active status + * is_open: open status + * name: testplan name + * testproject_id + */ + public function get_by_name($name, $tproject_id = 0, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $my = array(); + $my['opt'] = array( + 'output' => 'full', + 'id' => 0 + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = "/* $debugMsg */ "; + + switch ($my['opt']['output']) { + case 'minimun': + $sql .= " SELECT testplans.id, NH.name "; + break; + + case 'full': + default: + $sql .= " SELECT testplans.*, NH.name "; + break; + } + + $sql .= " FROM {$this->tables['testplans']} testplans, " . + " {$this->tables['nodes_hierarchy']} NH" . + " WHERE testplans.id = NH.id " . " AND NH.name = '" . + $this->db->prepare_string($name) . "'"; + + if (($safe_id = intval($tproject_id)) > 0) { + $sql .= " AND NH.parent_id={$safe_id} "; + } + + // useful when trying to check for duplicates ? + if (($my['opt']['id'] = intval($my['opt']['id'])) > 0) { + $sql .= " AND testplans.id != {$my['opt']['id']} "; + } + + return $this->db->get_recordset($sql); + } + + /* + * function: get_by_id + * + * args : id: testplan id + * + * returns: map with following keys: + * id: testplan id + * name: testplan name + * notes: testplan notes + * testproject_id + * active + * is_open + * parent_id + */ + public function get_by_id($id, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array(); + $my['opt'] = array( + 'output' => 'full', + 'active' => null, + 'testPlanFields' => '' + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $safe_id = intval($id); + switch ($my['opt']['output']) { + case 'testPlanFields': + $sql = "/* $debugMsg */ " . + " SELECT {$my['opt']['testPlanFields']} FROM {$this->tables['testplans']} " . + " WHERE id = " . $safe_id; + break; + + case 'minimun': + $sql = "/* $debugMsg */ " . " SELECT NH_TPLAN.name," . + " NH_TPROJ.id AS tproject_id, NH_TPROJ.name AS tproject_name,TPROJ.prefix" . + " FROM {$this->tables['nodes_hierarchy']} NH_TPLAN " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TPROJ ON NH_TPROJ.id = NH_TPLAN.parent_id " . + " JOIN {$this->tables['testprojects']} TPROJ ON TPROJ.ID = NH_TPROJ.id " . + " WHERE NH_TPLAN.id = " . $safe_id; + break; + + case 'full': + default: + $sql = "/* $debugMsg */ " . + " SELECT TPLAN.*,NH_TPLAN.name,NH_TPLAN.parent_id, + NH_TPROJ.id AS tproject_id, + NH_TPROJ.name AS tproject_name,TPROJ.prefix + FROM {$this->tables['testplans']} TPLAN, {$this->tables['nodes_hierarchy']} NH_TPLAN - JOIN {$this->tables['nodes_hierarchy']} NH_TPROJ - ON NH_TPROJ.id = NH_TPLAN.parent_id - JOIN {$this->tables['testprojects']} TPROJ - ON TPROJ.ID = NH_TPROJ.id - WHERE TPLAN.id = NH_TPLAN.id AND - TPLAN.id = " . $safe_id; - break; - } - - if(!is_null($my['opt']['active'])) { - $sql .= " AND active=" . (intval($my['opt']['active']) > 0 ? 1 : 0) . " "; - } - - $rs = $this->db->get_recordset($sql); - return ($rs ? $rs[0] : null); - } - - - /* - function: get_all - get array of info for every test plan, - without considering Test Project and any other kind of filter. - Every array element contains an assoc array - - args : - - - returns: array, every element is a map with following keys: - id: testplan id - name: testplan name - notes: testplan notes - testproject_id - active - is_open - parent_id - */ - function get_all() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . " SELECT testplans.*, NH.name " . - " FROM {$this->tables['testplans']} testplans, " . - " {$this->tables['nodes_hierarchy']} NH " . - " WHERE testplans.id=NH.id"; - $recordset = $this->db->get_recordset($sql); - return $recordset; - } - - /* - function: count_testcases - get number of testcases linked to a testplan - - args: id: testplan id, can be array of id, - - [platform_id]: null => do not filter by platform - can be array of platform id - - returns: number - */ - public function count_testcases($id,$platform_id=null,$opt=null) - { - // output: - // 'number', just the count - // 'groupByTestPlan' => map: key test plan id - // element: count - // - // 'groupByTestPlanPlatform' => map: first level key test plan id - // second level key platform id - // - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // protect yourself :) - 20140607 - if( is_null($id) || (is_int($id) && intval($id) <= 0 ) || (is_array($id) && count($id) == 0) ) - { - return 0; // >>>----> Bye - } - - - $my['opt'] = array('output' => 'number'); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql_filter = ''; - if( !is_null($platform_id) ) - { - $sql_filter = ' AND platform_id IN (' . implode(',',(array)$platform_id) . ')'; - } - - - - $out = null; - $outfields = "/* $debugMsg */ " . ' SELECT COUNT(testplan_id) AS qty '; - $dummy = " FROM {$this->tables['testplan_tcversions']} " . - " WHERE testplan_id IN (" . implode(',',(array)$id) . ") {$sql_filter}"; - - switch( $my['opt']['output'] ) - { - case 'groupByTestPlan': - $sql = $outfields . ', testplan_id' . $dummy . ' GROUP BY testplan_id '; - $out = $this->db->fetchRowsIntoMap($sql,'testplan_id'); - break; - - case 'groupByTestPlanPlatform': - $groupBy = ' GROUP BY testplan_id, platform_id '; - $sql = $outfields . ', testplan_id, platform_id' . $dummy . - ' GROUP BY testplan_id,platform_id '; - $out = $this->db->fetchMapsRowsIntoMap($sql,'testplan_id','platform_id'); - break; - - case 'number': - default: - $sql = $outfields . $dummy; - $rs = $this->db->get_recordset($sql); - - $out = 0; - if(!is_null($rs)) - { - $out = $rs[0]['qty']; - } - break; - } - - return $out; - } - - - - - /* - function: tcversionInfoForAudit - get info regarding tcversions, to generate useful audit messages - - - args : - $tplan_id: test plan id - $items_to_link: map key=tc_id - value: tcversion_id - returns: - - - rev: 20080629 - franciscom - audit message improvements - */ - function tcversionInfoForAudit($tplan_id,&$items) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // Get human readeable info for audit - $ret=array(); - // $tcase_cfg = config_get('testcase_cfg'); - $dummy=reset($items); - - list($ret['tcasePrefix'],$tproject_id) = $this->tcase_mgr->getPrefix($dummy); - $ret['tcasePrefix'] .= $this->tcaseCfg->glue_character; - - $sql = "/* $debugMsg */ " . - " SELECT TCV.id, tc_external_id, version, NHB.name " . - " FROM {$this->tables['tcversions']} TCV,{$this->tables['nodes_hierarchy']} NHA, " . - " {$this->tables['nodes_hierarchy']} NHB " . - " WHERE NHA.id=TCV.id " . - " AND NHB.id=NHA.parent_id " . - " AND TCV.id IN (" . implode(',',$items) . ")"; - - $ret['info']=$this->db->fetchRowsIntoMap($sql,'id'); - $ret['tplanInfo']=$this->get_by_id($tplan_id); - - return $ret; - } - - - /** - * associates version of different test cases to a test plan. - * this is the way to populate a test plan - - args : - $id: test plan id - $items_to_link: map key=tc_id - value= map with - key: platform_id (can be 0) - value: tcversion_id - passed by reference for speed - returns: - - - rev: 20080629 - franciscom - audit message improvements - */ - function link_tcversions($id,&$items_to_link,$userId) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // Get human readeable info for audit - $title_separator = config_get('gui_title_separator_1'); - $auditInfo=$this->tcversionInfoForAudit($id,$items_to_link['tcversion']); - - $optLTT = null; - $platformInfo = $this->platform_mgr->getLinkedToTestplanAsMap($id,$optLTT); - $platformLabel = lang_get('platform'); - - // Important: MySQL do not support default values on datetime columns that are functions - // that's why we are using db_now(). - $sql = "/* $debugMsg */ " . - "INSERT INTO {$this->tables['testplan_tcversions']} " . - "(testplan_id,author_id,creation_ts,tcversion_id,platform_id) " . - " VALUES ({$id},{$userId},{$this->db->db_now()},"; - $features=null; - foreach($items_to_link['items'] as $tcase_id => $items) - { - foreach($items as $platform_id => $tcversion) - { - $addInfo=''; - $result = $this->db->exec_query($sql . "{$tcversion}, {$platform_id})"); - if ($result) - { - $features[$platform_id][$tcversion]=$this->db->insert_id($this->tables['testplan_tcversions']); - if( isset($platformInfo[$platform_id]) ) - { - $addInfo = ' - ' . $platformLabel . ':' . $platformInfo[$platform_id]; - } - $auditMsg=TLS("audit_tc_added_to_testplan", - $auditInfo['tcasePrefix'] . $auditInfo['info'][$tcversion]['tc_external_id'] . - $title_separator . $auditInfo['info'][$tcversion]['name'], - $auditInfo['info'][$tcversion]['version'], - $auditInfo['tplanInfo']['name'] . $addInfo ); - - logAuditEvent($auditMsg,"ASSIGN",$id,"testplans"); - } - } - } - return $features; - } - - - /* - function: setExecutionOrder - - args : - $id: test plan id - $executionOrder: assoc array key=tcversion_id value=order - passed by reference for speed - - returns: - - */ - function setExecutionOrder($id,&$executionOrder) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - foreach($executionOrder as $tcVersionID => $execOrder) - { - $execOrder=intval($execOrder); - $sql="/* $debugMsg */ UPDATE {$this->tables['testplan_tcversions']} " . - "SET node_order={$execOrder} " . - "WHERE testplan_id={$id} " . - "AND tcversion_id={$tcVersionID}"; - $result = $this->db->exec_query($sql); - } - } - - - /** - * Ignores Platforms, then if a test case version is linked to a test plan - * and two platforms, we will get item once. - * Need to understand if in context where we want to use this method this is - * a problem - * - * - * @internal revisions: - */ - function get_linked_items_id($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ ". - " SELECT DISTINCT parent_id FROM {$this->tables['nodes_hierarchy']} NHTC " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTC.id " . - " WHERE TPTCV.testplan_id = " . intval($id); - - $linked_items = $this->db->fetchRowsIntoMap($sql,'parent_id'); - return $linked_items; - } - - - /** - * @internal revisions - * - */ - function get_linked_tcvid($id,$platformID,$opt=null){ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $options = array('addEstimatedExecDuration' => false, - 'tcase_id' => 0); - $options = array_merge($options,(array)$opt); - - $addFields = ''; - $addSql = ''; - $addWhere = ''; - - if($options['addEstimatedExecDuration']) - { - $addFields = ',TCV.estimated_exec_duration '; - $addSql .= " JOIN {$this->tables['tcversions']} TCV ON TCV.id = tcversion_id "; - } - - if($options['tcase_id'] > 0) - { - $addFields = ', NHTCV.parent_id AS tcase_id '; - $addSql .= " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = tcversion_id "; - - $addWhere = ' AND NHTCV.parent_id = ' . - intval($options['tcase_id']); - } - - $sql = " /* $debugMsg */ " . - " SELECT tcversion_id {$addFields} " . - " FROM {$this->tables['testplan_tcversions']} " . - $addSql; - - $sql .= " WHERE testplan_id = " . intval($id) . - " AND platform_id = " . intval($platformID) . - $addWhere; - - $items = $this->db->fetchRowsIntoMap($sql,'tcversion_id'); - return $items; - } - - - /** - * - * - */ - function getLinkedCount($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " /* $debugMsg */ ". - " SELECT COUNT( DISTINCT(TPTCV.tcversion_id) ) AS qty " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " WHERE TPTCV.testplan_id = " . intval($id); - - $rs = $this->db->get_recordset($sql); - return $rs[0]['qty']; - } - - - - /** - * @internal revisions: - * - */ - function getFeatureID($id,$platformID,$tcversionID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " /* $debugMsg */ ". - " SELECT id FROM {$this->tables['testplan_tcversions']} " . - " WHERE testplan_id = " . intval($id) . - " AND tcversion_id = " . intval($tcversionID) . - " AND platform_id = " . intval($platformID) ; - - $linked_items = $this->db->fetchRowsIntoMap($sql,'id'); - return !is_null($linked_items) ? key($linked_items) : -1; - } - - - /** - * @internal revisions: - * - */ - function getRootTestSuites($id,$tproject_id,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('opt' => array('output' => 'std')); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = " /* $debugMsg */ ". - " SELECT DISTINCT NHTCASE.parent_id AS tsuite_id" . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.tcversion_id = NHTCV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCASE " . - " ON NHTCASE.id = NHTCV.parent_id " . - " WHERE TPTCV.testplan_id = {$id} "; - - $items = $this->db->fetchRowsIntoMap($sql,'tsuite_id',database::CUMULATIVE); - $xsql = " SELECT COALESCE(parent_id,0) AS parent_id,id,name" . - " FROM {$this->tables['nodes_hierarchy']} " . - " WHERE id IN (" . implode(',',array_keys($items)) . ") AND parent_id IS NOT NULL"; - - unset($items); - $xmen = $this->db->fetchMapRowsIntoMap($xsql,'parent_id','id'); - $tlnodes = array(); - foreach($xmen as $parent_id => &$children) - { - if($parent_id == $tproject_id) - { - foreach($children as $item_id => &$elem) - { - $tlnodes[$item_id] = ''; - } - } - else - { - $paty = $this->tree_manager->get_path($parent_id); - if( !isset($tlnodes[$paty[0]['id']]) ) - { - $tlnodes[$paty[0]['id']] = ''; - } - unset($paty); - } - } - unset($xmen); - - // Now with node list get order - $xsql = " SELECT id,name,node_order " . - " FROM {$this->tables['nodes_hierarchy']} " . - " WHERE id IN (" . implode(',',array_keys($tlnodes)) . ")" . - " ORDER BY node_order,name "; - $xmen = $this->db->fetchRowsIntoMap($xsql,'id'); - switch($my['opt']['output']) - { - case 'std': - foreach($xmen as $xid => $elem) - { - $xmen[$xid] = $elem['name']; - } - break; - } - unset($tlnodes); - return $xmen; - } - - - - - - - - /** - * - * - */ - function helper_keywords_sql($filter,$options=null) { - - $sql = array('filter' => '', 'join' => ''); - - if( is_array($filter) ) { - // 0 -> no keyword, remove - if( $filter[0] == 0 ) { - array_shift($filter); - } - - if(count($filter)) { - $sql['filter'] = " AND TK.keyword_id IN (" . implode(',',$filter) . ")"; - } - } - else if($filter > 0) { - $sql['filter'] = " AND TK.keyword_id = {$filter} "; - } - - if( $sql['filter'] != '' ) { - $sql['join'] = " JOIN {$this->tables['testcase_keywords']} TK - ON TK.tcversion_id = NH_TCV.id "; - } - - // mmm, here there is missing documentation - $ret = is_null($options) ? $sql : array($sql['join'],$sql['filter']); - return $ret; - } - - - /** - * - * - */ - function helper_urgency_sql($filter) - { - - $cfg = config_get("urgencyImportance"); - $sql = ''; - if ($filter == HIGH) - { - $sql .= " AND (urgency * importance) >= " . $cfg->threshold['high']; - } - else if($filter == LOW) - { - $sql .= " AND (urgency * importance) < " . $cfg->threshold['low']; - } - else - { - $sql .= " AND ( ((urgency * importance) >= " . $cfg->threshold['low'] . - " AND ((urgency * importance) < " . $cfg->threshold['high']."))) "; - } - - return $sql; - } - - - /** - * - * - */ - function helper_assigned_to_sql($filter,$opt,$build_id) - { - - $join = " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id = " . $build_id . - " AND UA.type = {$this->execTaskCode} "; - - // Warning!!!: - // If special user id TL_USER_NOBODY is present in set of user id - // we will ignore any other user id present on set. - $ff = (array)$filter; - $sql = " UA.user_id "; - if( in_array(TL_USER_NOBODY,$ff) ) - { - $sql .= " IS NULL "; - $join = ' LEFT OUTER ' . $join; - } - else if( in_array(TL_USER_SOMEBODY,$ff) ) - { - $sql .= " IS NOT NULL "; - } - else - { - $sql_unassigned=""; - $sql = ''; - if( $opt['include_unassigned'] ) - { - $join = ' LEFT OUTER ' . $join; // 20130729 - - $sql = "("; - $sql_unassigned=" OR UA.user_id IS NULL)"; - } - $sql .= " UA.user_id IN (" . implode(",",$ff) . ") " . $sql_unassigned; - } - - return array($join, ' AND ' . $sql); - } - - - - - /** - * - * - */ - function helper_exec_status_filter($filter,$lastExecSql) - { - $notRunFilter = null; - $execFilter = ''; - - $notRunPresent = array_search($this->notRunStatusCode,$filter); - if($notRunPresent !== false) - { - $notRunFilter = " E.status IS NULL "; - unset($filter[$this->notRunStatusCode]); - } - - if(count($filter) > 0) - { - $dummy = " E.status IN ('" . implode("','",$filter) . "') "; - $execFilter = " ( {$dummy} {$lastExecSql} ) "; - } - - if( !is_null($notRunFilter) ) - { - if($execFilter != "") - { - $execFilter .= " OR "; - } - $execFilter .= $notRunFilter; - } - - if( $execFilter != "") - { - // Just add the AND - $execFilter = " AND ({$execFilter} )"; - } - return array($execFilter,$notRunFilter); - } - - /** - * - * - */ - function helper_bugs_sql($filter) - { - $sql = array('filter' => '', 'join' => ''); - $dummy = explode(',',$filter); - $items = null; - foreach($dummy as $v) - { - $x = trim($v); - if($x != '') - { - $items[] = $x; - } - } - if(!is_null($items)) - { - $sql['filter'] = " AND EB.bug_id IN ('" . implode("','",$items) . "')"; - $sql['join'] = " JOIN {$this->tables['execution_bugs']} EB ON EB.execution_id = E.id "; - } - return array($sql['join'],$sql['filter']); - } - - - - - /* - function: get_linked_and_newest_tcversions - returns for every test case in a test plan - the tc version linked and the newest available version - - args: id: testplan id - [tcase_id]: default null => all testcases linked to testplan - - returns: map key: testcase internal id - values: map with following keys: - - [name] - [tc_id] (internal id) - [tcversion_id] - [newest_tcversion_id] - [tc_external_id] - [version] (for humans) - [newest_version] (for humans) - - */ - function get_linked_and_newest_tcversions($id,$tcase_id=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $tc_id_filter = " "; - if (!is_null($tcase_id) ) - { - if( is_array($tcase_id) ) - { - // ??? implement as in ? - } - else if ($tcase_id > 0 ) - { - $tc_id_filter = " AND NHA.parent_id = {$tcase_id} "; - } - } - - // Peter Rooms found bug due to wrong SQL, accepted by MySQL but not by PostGres - // Missing column in GROUP BY Clause - - $sql = " /* $debugMsg */ SELECT MAX(NHB.id) AS newest_tcversion_id, " . - " NHA.parent_id AS tc_id, NHC.name, T.tcversion_id AS tcversion_id," . - " TCVA.tc_external_id AS tc_external_id, TCVA.version AS version " . - " FROM {$this->tables['nodes_hierarchy']} NHA " . - - // NHA - will contain ONLY nodes of type testcase_version that are LINKED to test plan - " JOIN {$this->tables['testplan_tcversions']} T ON NHA.id = T.tcversion_id " . - - // Get testcase_version data for LINKED VERSIONS - " JOIN {$this->tables['tcversions']} TCVA ON TCVA.id = T.tcversion_id" . - - // Work on Sibblings - Start - // NHB - Needed to get ALL testcase_version sibblings nodes - " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHB.parent_id = NHA.parent_id " . - - // Want only ACTIVE Sibblings - " JOIN {$this->tables['tcversions']} TCVB ON TCVB.id = NHB.id AND TCVB.active=1 " . - // Work on Sibblings - STOP - - // NHC will contain - nodes of type TESTCASE (parent of testcase versions we are working on) - // we use NHC to get testcase NAME ( testcase version nodes have EMPTY NAME) - " JOIN {$this->tables['nodes_hierarchy']} NHC ON NHC.id = NHA.parent_id " . - - // Want to get only testcase version with id (NHB.id) greater than linked one (NHA.id) - " WHERE T.testplan_id={$id} AND NHB.id > NHA.id" . $tc_id_filter . - " GROUP BY NHA.parent_id, NHC.name, T.tcversion_id, TCVA.tc_external_id, TCVA.version "; - - // BUGID 4682 - phidotnet - Newest version is smaller than Linked version - $sql2 = " SELECT SUBQ.name, SUBQ.newest_tcversion_id, SUBQ.tc_id, " . - " SUBQ.tcversion_id, SUBQ.version, SUBQ.tc_external_id, " . - " TCV.version AS newest_version " . - " FROM {$this->tables['tcversions']} TCV, ( $sql ) AS SUBQ " . - " WHERE SUBQ.newest_tcversion_id = TCV.id AND SUBQ.version < TCV.version " . - " ORDER BY SUBQ.tc_id "; - - return $this->db->fetchRowsIntoMap($sql2,'tc_id'); - } - - - /** - * Remove of records from user_assignments table - * @author franciscom - * - * @param integer $id : test plan id - * @param array $items: assoc array key=tc_id value=tcversion_id - * - */ - function unlink_tcversions($id,&$items) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($items)) { - return; - } - - // Get human readeable info for audit - $gui_cfg = config_get('gui'); - $title_separator = config_get('gui_title_separator_1'); - $auditInfo=$this->tcversionInfoForAudit($id,$items['tcversion']); - $platformInfo = $this->platform_mgr->getLinkedToTestplanAsMap($id); - $platformLabel = lang_get('platform'); - - $dummy = null; - foreach($items['items'] as $tcase_id => $elem) { - foreach($elem as $platform_id => $tcversion_id) { - $dummy[] = "(tcversion_id = {$tcversion_id} AND platform_id = {$platform_id})"; - } - } - $where_clause = implode(" OR ", $dummy); - - /* - * asimon - BUGID 3497 and hopefully also 3530 - * A very litte error, missing braces in the $where_clause, was causing this bug. - * When one set of testcases is linked to two testplans, this statement should check - * that the combination of testplan_id, tcversion_id and platform_id was the same, - * but instead it checked for either testplan_id OR tcversion_id and platform_id. - * So every linked testcase with fitting tcversion_id and platform_id without execution - * was deleted, regardless of testplan_id. - * Simply adding braces around the where clause solves this. - * So innstead of: - * SELECT id AS link_id FROM testplan_tcversions - * WHERE testplan_id=12 AND (tcversion_id = 5 AND platform_id = 0) - * OR (tcversion_id = 7 AND platform_id = 0) - * OR (tcversion_id = 9 AND platform_id = 0) - * OR (tcversion_id = 11 AND platform_id = 0) - * we need this: - * SELECT ... WHERE testplan_id=12 AND (... OR ...) - */ - $where_clause = " ( {$where_clause} ) "; - - // First get the executions id if any exist - $sql = " /* $debugMsg */ SELECT id AS execution_id + JOIN {$this->tables['nodes_hierarchy']} NH_TPROJ + ON NH_TPROJ.id = NH_TPLAN.parent_id + JOIN {$this->tables['testprojects']} TPROJ + ON TPROJ.ID = NH_TPROJ.id + WHERE TPLAN.id = NH_TPLAN.id AND + TPLAN.id = " . $safe_id; + break; + } + + if (! is_null($my['opt']['active'])) { + $sql .= " AND active=" . (intval($my['opt']['active']) > 0 ? 1 : 0) . + " "; + } + + $rs = $this->db->get_recordset($sql); + return $rs ? $rs[0] : null; + } + + /* + * function: get_all + * get array of info for every test plan, + * without considering Test Project and any other kind of filter. + * Every array element contains an assoc array + * + * args : - + * + * returns: array, every element is a map with following keys: + * id: testplan id + * name: testplan name + * notes: testplan notes + * testproject_id + * active + * is_open + * parent_id + */ + private function getAll() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ " . " SELECT testplans.*, NH.name " . + " FROM {$this->tables['testplans']} testplans, " . + " {$this->tables['nodes_hierarchy']} NH " . + " WHERE testplans.id=NH.id"; + return $this->db->get_recordset($sql); + } + + /* + * function: count_testcases + * get number of testcases linked to a testplan + * + * args: id: testplan id, can be array of id, + * + * [platform_id]: null => do not filter by platform + * can be array of platform id + * + * returns: number + */ + public function count_testcases($id, $platform_id = null, $opt = null) + { + // output: + // 'number', just the count + // 'groupByTestPlan' => map: key test plan id + // element: count + // + // 'groupByTestPlanPlatform' => map: first level key test plan id + // second level key platform id + // + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + // protect yourself :) - 20140607 + if (is_null($id) || (is_int($id) && intval($id) <= 0) || + (is_array($id) && count($id) == 0)) { + return 0; // >>>----> Bye + } + + $my['opt'] = array( + 'output' => 'number' + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql_filter = ''; + if (! is_null($platform_id)) { + $sql_filter = ' AND platform_id IN (' . + implode(',', (array) $platform_id) . ')'; + } + + $out = null; + $outfields = "/* $debugMsg */ " . ' SELECT COUNT(testplan_id) AS qty '; + $dummy = " FROM {$this->tables['testplan_tcversions']} " . + " WHERE testplan_id IN (" . implode(',', (array) $id) . + ") {$sql_filter}"; + + switch ($my['opt']['output']) { + case 'groupByTestPlan': + $sql = $outfields . ', testplan_id' . $dummy . + ' GROUP BY testplan_id '; + $out = $this->db->fetchRowsIntoMap($sql, 'testplan_id'); + break; + + case 'groupByTestPlanPlatform': + $sql = $outfields . ', testplan_id, platform_id' . $dummy . + ' GROUP BY testplan_id,platform_id '; + $out = $this->db->fetchMapsRowsIntoMap($sql, 'testplan_id', + 'platform_id'); + break; + + case 'number': + default: + $sql = $outfields . $dummy; + $rs = $this->db->get_recordset($sql); + + $out = 0; + if (! is_null($rs)) { + $out = $rs[0]['qty']; + } + break; + } + + return $out; + } + + /* + * function: tcversionInfoForAudit + * get info regarding tcversions, to generate useful audit messages + * + * + * args : + * $tplan_id: test plan id + * $items_to_link: map key=tc_id + * value: tcversion_id + * returns: - + * + * rev: 20080629 - franciscom - audit message improvements + */ + private function tcversionInfoForAudit($tplan_id, &$items) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + // Get human readeable info for audit + $ret = array(); + $dummy = reset($items); + + list ($ret['tcasePrefix'],) = $this->tcaseMgr->getPrefix($dummy); + $ret['tcasePrefix'] .= $this->tcaseCfg->glue_character; + + $sql = "/* $debugMsg */ " . + " SELECT TCV.id, tc_external_id, version, NHB.name " . + " FROM {$this->tables['tcversions']} TCV,{$this->tables['nodes_hierarchy']} NHA, " . + " {$this->tables['nodes_hierarchy']} NHB " . " WHERE NHA.id=TCV.id " . + " AND NHB.id=NHA.parent_id " . " AND TCV.id IN (" . + implode(',', $items) . ")"; + + $ret['info'] = $this->db->fetchRowsIntoMap($sql, 'id'); + $ret['tplanInfo'] = $this->get_by_id($tplan_id); + + return $ret; + } + + /** + * associates version of different test cases to a test plan. + * this is the way to populate a test plan + * + * args : + * $id: test plan id + * $items_to_link: map key=tc_id + * value= map with + * key: platform_id (can be 0) + * value: tcversion_id + * passed by reference for speed + * returns: - + * + * rev: 20080629 - franciscom - audit message improvements + */ + public function link_tcversions($id, &$items_to_link, $userId) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + // Get human readeable info for audit + $title_separator = config_get('gui_title_separator_1'); + $auditInfo = $this->tcversionInfoForAudit($id, + $items_to_link['tcversion']); + + $optLTT = null; + $platformInfo = $this->platform_mgr->getLinkedToTestplanAsMap($id, + $optLTT); + $platformLabel = lang_get('platform'); + + // Important: MySQL do not support default values on datetime columns that are functions + // that's why we are using db_now(). + $sql = "/* $debugMsg */ " . + "INSERT INTO {$this->tables['testplan_tcversions']} " . + "(testplan_id,author_id,creation_ts,tcversion_id,platform_id) " . + " VALUES ({$id},{$userId},{$this->db->db_now()},"; + $features = null; + foreach ($items_to_link['items'] as $items) { + foreach ($items as $platform_id => $tcversion) { + $addInfo = ''; + $result = $this->db->exec_query( + $sql . "{$tcversion}, {$platform_id})"); + if ($result) { + $features[$platform_id][$tcversion] = $this->db->insert_id( + $this->tables['testplan_tcversions']); + if (isset($platformInfo[$platform_id])) { + $addInfo = ' - ' . $platformLabel . ':' . + $platformInfo[$platform_id]; + } + $auditMsg = TLS("audit_tc_added_to_testplan", + $auditInfo['tcasePrefix'] . + $auditInfo['info'][$tcversion]['tc_external_id'] . + $title_separator . $auditInfo['info'][$tcversion]['name'], + $auditInfo['info'][$tcversion]['version'], + $auditInfo['tplanInfo']['name'] . $addInfo); + + logAuditEvent($auditMsg, "ASSIGN", $id, "testplans"); + } + } + } + return $features; + } + + /* + * function: setExecutionOrder + * + * args : + * $id: test plan id + * $executionOrder: assoc array key=tcversion_id value=order + * passed by reference for speed + * + * returns: - + */ + public function setExecutionOrder($id, &$executionOrder) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + foreach ($executionOrder as $tcVersionID => $execOrder) { + $execOrder = intval($execOrder); + $sql = "/* $debugMsg */ UPDATE {$this->tables['testplan_tcversions']} " . + "SET node_order={$execOrder} " . "WHERE testplan_id={$id} " . + "AND tcversion_id={$tcVersionID}"; + $this->db->exec_query($sql); + } + } + + /** + * Ignores Platforms, then if a test case version is linked to a test plan + * and two platforms, we will get item once. + * Need to understand if in context where we want to use this method this is + * a problem + * + * @internal revisions: + */ + public function get_linked_items_id($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT parent_id FROM {$this->tables['nodes_hierarchy']} NHTC " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTC.id " . + " WHERE TPTCV.testplan_id = " . intval($id); + + return $this->db->fetchRowsIntoMap($sql, 'parent_id'); + } + + /** + * + * @internal revisions + * + */ + public function get_linked_tcvid($id, $platformID, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $options = array( + 'addEstimatedExecDuration' => false, + 'tcase_id' => 0 + ); + $options = array_merge($options, (array) $opt); + + $addFields = ''; + $addSql = ''; + $addWhere = ''; + + if ($options['addEstimatedExecDuration']) { + $addFields = ',TCV.estimated_exec_duration '; + $addSql .= " JOIN {$this->tables['tcversions']} TCV ON TCV.id = tcversion_id "; + } + + if ($options['tcase_id'] > 0) { + $addFields = ', NHTCV.parent_id AS tcase_id '; + $addSql .= " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = tcversion_id "; + + $addWhere = ' AND NHTCV.parent_id = ' . intval($options['tcase_id']); + } + + $sql = " /* $debugMsg */ " . " SELECT tcversion_id {$addFields} " . + " FROM {$this->tables['testplan_tcversions']} " . $addSql; + + $sql .= " WHERE testplan_id = " . intval($id) . " AND platform_id = " . + intval($platformID) . $addWhere; + + return $this->db->fetchRowsIntoMap($sql, 'tcversion_id'); + } + + /** + */ + public function getLinkedCount($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = " /* $debugMsg */ " . + " SELECT COUNT( DISTINCT(TPTCV.tcversion_id) ) AS qty " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " WHERE TPTCV.testplan_id = " . intval($id); + + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty']; + } + + /** + * + * @internal revisions: + * + */ + public function getFeatureID($id, $platformID, $tcversionID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = " /* $debugMsg */ " . + " SELECT id FROM {$this->tables['testplan_tcversions']} " . + " WHERE testplan_id = " . intval($id) . " AND tcversion_id = " . + intval($tcversionID) . " AND platform_id = " . intval($platformID); + + $linked_items = $this->db->fetchRowsIntoMap($sql, 'id'); + return ! is_null($linked_items) ? key($linked_items) : - 1; + } + + /** + * + * @internal revisions: + * + */ + public function getRootTestSuites($id, $tproject_id, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array( + 'opt' => array( + 'output' => 'std' + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT NHTCASE.parent_id AS tsuite_id" . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.tcversion_id = NHTCV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCASE " . + " ON NHTCASE.id = NHTCV.parent_id " . + " WHERE TPTCV.testplan_id = {$id} "; + + $items = $this->db->fetchRowsIntoMap($sql, 'tsuite_id', + database::CUMULATIVE); + $xsql = " SELECT COALESCE(parent_id,0) AS parent_id,id,name" . + " FROM {$this->tables['nodes_hierarchy']} " . " WHERE id IN (" . + implode(',', array_keys($items)) . ") AND parent_id IS NOT NULL"; + + unset($items); + $xmen = $this->db->fetchMapRowsIntoMap($xsql, 'parent_id', 'id'); + $tlnodes = array(); + foreach ($xmen as $parent_id => &$children) { + if ($parent_id == $tproject_id) { + foreach ($children as $item_id => &$elem) { + $tlnodes[$item_id] = ''; + } + } else { + $paty = $this->tree_manager->get_path($parent_id); + if (! isset($tlnodes[$paty[0]['id']])) { + $tlnodes[$paty[0]['id']] = ''; + } + unset($paty); + } + } + unset($xmen); + + // Now with node list get order + $xsql = " SELECT id,name,node_order " . + " FROM {$this->tables['nodes_hierarchy']} " . " WHERE id IN (" . + implode(',', array_keys($tlnodes)) . ")" . + " ORDER BY node_order,name "; + $xmen = $this->db->fetchRowsIntoMap($xsql, 'id'); + switch ($my['opt']['output']) { + case 'std': + foreach ($xmen as $xid => $elem) { + $xmen[$xid] = $elem['name']; + } + break; + } + unset($tlnodes); + return $xmen; + } + + /** + */ + protected function helper_keywords_sql($filter, $options = null) + { + $sql = array( + 'filter' => '', + 'join' => '' + ); + + if (is_array($filter)) { + // 0 -> no keyword, remove + if ($filter[0] == 0) { + array_shift($filter); + } + + if (count($filter)) { + $sql['filter'] = " AND TK.keyword_id IN (" . + implode(',', $filter) . ")"; + } + } elseif ($filter > 0) { + $sql['filter'] = " AND TK.keyword_id = {$filter} "; + } + + if ($sql['filter'] != '') { + $sql['join'] = " JOIN {$this->tables['testcase_keywords']} TK + ON TK.tcversion_id = NH_TCV.id "; + } + + // mmm, here there is missing documentation + return is_null($options) ? $sql : array( + $sql['join'], + $sql['filter'] + ); + } + + /** + */ + private function helperUrgencySQL($filter) + { + $cfg = config_get("urgencyImportance"); + $sql = ''; + if ($filter == HIGH) { + $sql .= " AND (urgency * importance) >= " . $cfg->threshold['high']; + } elseif ($filter == LOW) { + $sql .= " AND (urgency * importance) < " . $cfg->threshold['low']; + } else { + $sql .= " AND ( ((urgency * importance) >= " . $cfg->threshold['low'] . + " AND ((urgency * importance) < " . $cfg->threshold['high'] . + "))) "; + } + + return $sql; + } + + /** + */ + private function helperAssignedToSQL($filter, $opt, $build_id) + { + $join = " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . " AND UA.build_id = " . $build_id . + " AND UA.type = {$this->execTaskCode} "; + + // Warning!!!: + // If special user id TL_USER_NOBODY is present in set of user id + // we will ignore any other user id present on set. + $ff = (array) $filter; + $sql = " UA.user_id "; + if (in_array(TL_USER_NOBODY, $ff)) { + $sql .= " IS NULL "; + $join = ' LEFT OUTER ' . $join; + } elseif (in_array(TL_USER_SOMEBODY, $ff)) { + $sql .= " IS NOT NULL "; + } else { + $sql_unassigned = ""; + $sql = ''; + if ($opt['include_unassigned']) { + $join = ' LEFT OUTER ' . $join; // 20130729 + + $sql = "("; + $sql_unassigned = " OR UA.user_id IS NULL)"; + } + $sql .= " UA.user_id IN (" . implode(",", $ff) . ") " . + $sql_unassigned; + } + + return array( + $join, + ' AND ' . $sql + ); + } + + /** + */ + private function helperExecStatusFilter($filter, $lastExecSql) + { + $notRunFilter = null; + $execFilter = ''; + + $notRunPresent = array_search($this->notRunStatusCode, $filter); + if ($notRunPresent !== false) { + $notRunFilter = " E.status IS NULL "; + unset($filter[$this->notRunStatusCode]); + } + + if (! empty($filter)) { + $dummy = " E.status IN ('" . implode("','", $filter) . "') "; + $execFilter = " ( {$dummy} {$lastExecSql} ) "; + } + + if (! is_null($notRunFilter)) { + if ($execFilter != "") { + $execFilter .= " OR "; + } + $execFilter .= $notRunFilter; + } + + if ($execFilter != "") { + // Just add the AND + $execFilter = " AND ({$execFilter} )"; + } + return array( + $execFilter, + $notRunFilter + ); + } + + /** + */ + private function helperBugsSQL($filter) + { + $sql = array( + 'filter' => '', + 'join' => '' + ); + $dummy = explode(',', $filter); + $items = null; + foreach ($dummy as $v) { + $x = trim($v); + if ($x != '') { + $items[] = $x; + } + } + if (! is_null($items)) { + $sql['filter'] = " AND EB.bug_id IN ('" . implode("','", $items) . + "')"; + $sql['join'] = " JOIN {$this->tables['execution_bugs']} EB ON EB.execution_id = E.id "; + } + return array( + $sql['join'], + $sql['filter'] + ); + } + + /* + * function: get_linked_and_newest_tcversions + * returns for every test case in a test plan + * the tc version linked and the newest available version + * + * args: id: testplan id + * [tcase_id]: default null => all testcases linked to testplan + * + * returns: map key: testcase internal id + * values: map with following keys: + * + * [name] + * [tc_id] (internal id) + * [tcversion_id] + * [newest_tcversion_id] + * [tc_external_id] + * [version] (for humans) + * [newest_version] (for humans) + * + */ + public function get_linked_and_newest_tcversions($id, $tcase_id = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $tc_id_filter = " "; + if (! is_null($tcase_id) && $tcase_id > 0) { + $tc_id_filter = " AND NHA.parent_id = {$tcase_id} "; + } + + // Peter Rooms found bug due to wrong SQL, accepted by MySQL but not by PostGres + // Missing column in GROUP BY Clause + + $sql = " /* $debugMsg */ SELECT MAX(NHB.id) AS newest_tcversion_id, " . + " NHA.parent_id AS tc_id, NHC.name, T.tcversion_id AS tcversion_id," . + " TCVA.tc_external_id AS tc_external_id, TCVA.version AS version " . + " FROM {$this->tables['nodes_hierarchy']} NHA " . + + // NHA - will contain ONLY nodes of type testcase_version that are LINKED to test plan + " JOIN {$this->tables['testplan_tcversions']} T ON NHA.id = T.tcversion_id " . + + // Get testcase_version data for LINKED VERSIONS + " JOIN {$this->tables['tcversions']} TCVA ON TCVA.id = T.tcversion_id" . + + // Work on Sibblings - Start + // NHB - Needed to get ALL testcase_version sibblings nodes + " JOIN {$this->tables['nodes_hierarchy']} NHB ON NHB.parent_id = NHA.parent_id " . + + // Want only ACTIVE Sibblings + " JOIN {$this->tables['tcversions']} TCVB ON TCVB.id = NHB.id AND TCVB.active=1 " . + // Work on Sibblings - STOP + + // NHC will contain - nodes of type TESTCASE (parent of testcase versions we are working on) + // we use NHC to get testcase NAME ( testcase version nodes have EMPTY NAME) + " JOIN {$this->tables['nodes_hierarchy']} NHC ON NHC.id = NHA.parent_id " . + + // Want to get only testcase version with id (NHB.id) greater than linked one (NHA.id) + " WHERE T.testplan_id={$id} AND NHB.id > NHA.id" . $tc_id_filter . + " GROUP BY NHA.parent_id, NHC.name, T.tcversion_id, TCVA.tc_external_id, TCVA.version "; + + // BUGID 4682 - phidotnet - Newest version is smaller than Linked version + $sql2 = " SELECT SUBQ.name, SUBQ.newest_tcversion_id, SUBQ.tc_id, " . + " SUBQ.tcversion_id, SUBQ.version, SUBQ.tc_external_id, " . + " TCV.version AS newest_version " . + " FROM {$this->tables['tcversions']} TCV, ( $sql ) AS SUBQ " . + " WHERE SUBQ.newest_tcversion_id = TCV.id AND SUBQ.version < TCV.version " . + " ORDER BY SUBQ.tc_id "; + + return $this->db->fetchRowsIntoMap($sql2, 'tc_id'); + } + + /** + * Remove of records from user_assignments table + * + * @author franciscom + * + * @param integer $id + * : test plan id + * @param array $items: + * assoc array key=tc_id value=tcversion_id + * + */ + public function unlink_tcversions($id, &$items) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($items)) { + return; + } + + // Get human readeable info for audit + config_get('gui'); + $title_separator = config_get('gui_title_separator_1'); + $auditInfo = $this->tcversionInfoForAudit($id, $items['tcversion']); + $platformInfo = $this->platform_mgr->getLinkedToTestplanAsMap($id); + $platformLabel = lang_get('platform'); + + $dummy = null; + foreach ($items['items'] as $tcase_id => $elem) { + foreach ($elem as $platform_id => $tcversion_id) { + $dummy[] = "(tcversion_id = {$tcversion_id} AND platform_id = {$platform_id})"; + } + } + $where_clause = implode(" OR ", $dummy); + + /* + * asimon - BUGID 3497 and hopefully also 3530 + * A very litte error, missing braces in the $where_clause, was causing this bug. + * When one set of testcases is linked to two testplans, this statement should check + * that the combination of testplan_id, tcversion_id and platform_id was the same, + * but instead it checked for either testplan_id OR tcversion_id and platform_id. + * So every linked testcase with fitting tcversion_id and platform_id without execution + * was deleted, regardless of testplan_id. + * Simply adding braces around the where clause solves this. + * So innstead of: + * SELECT id AS link_id FROM testplan_tcversions + * WHERE testplan_id=12 AND (tcversion_id = 5 AND platform_id = 0) + * OR (tcversion_id = 7 AND platform_id = 0) + * OR (tcversion_id = 9 AND platform_id = 0) + * OR (tcversion_id = 11 AND platform_id = 0) + * we need this: + * SELECT ... WHERE testplan_id=12 AND (... OR ...) + */ + $where_clause = " ( {$where_clause} ) "; + + // First get the executions id if any exist + $sql = " /* $debugMsg */ SELECT id AS execution_id FROM {$this->tables['executions']} - WHERE testplan_id = {$id} AND ${where_clause}"; - - $exec_ids = $this->db->fetchRowsIntoMap($sql,'execution_id'); - - if( !is_null($exec_ids) and count($exec_ids) > 0 ) { - // has executions - $exec_ids = array_keys($exec_ids); - $exec_id_list = implode(",",$exec_ids); - $exec_id_where= " WHERE execution_id IN ($exec_id_list)"; - - // Remove bugs if any exist - // This will remove the bug @step level if any exists. - $sql = " /* $debugMsg */ DELETE FROM {$this->tables['execution_bugs']} - {$exec_id_where} "; - $result = $this->db->exec_query($sql); - - // Remove CF exec values - $sql = " /* $debugMsg */ - DELETE FROM {$this->tables['cfield_execution_values']} - {$exec_id_where} "; - $result = $this->db->exec_query($sql); - - // execution attachments - $dummy = " /* $debugMsg */ SELECT id FROM {$this->tables['attachments']} + WHERE testplan_id = {$id} AND ${where_clause}"; + + $exec_ids = $this->db->fetchRowsIntoMap($sql, 'execution_id'); + + if (! empty($exec_ids)) { + // has executions + $exec_ids = array_keys($exec_ids); + $exec_id_list = implode(",", $exec_ids); + $exec_id_where = " WHERE execution_id IN ($exec_id_list)"; + + // Remove bugs if any exist + // This will remove the bug @step level if any exists. + $sql = " /* $debugMsg */ DELETE FROM {$this->tables['execution_bugs']} + {$exec_id_where} "; + $this->db->exec_query($sql); + + // Remove CF exec values + $sql = " /* $debugMsg */ + DELETE FROM {$this->tables['cfield_execution_values']} + {$exec_id_where} "; + $this->db->exec_query($sql); + + // execution attachments + $dummy = " /* $debugMsg */ SELECT id FROM {$this->tables['attachments']} WHERE fk_table = 'executions' - AND fk_id IN ({$exec_id_list}) "; - - $rs = $this->db->fetchRowsIntoMap($dummy,'id'); - if(!is_null($rs)) { - foreach($rs as $fik => $v) { - deleteAttachment($this->db,$fik,false); - } - } - - - // Work on Execution on Test Case Steps - // Attachments - $dummy = " /* $debugMsg */ SELECT id FROM {$this->tables['attachments']} - WHERE fk_table = 'execution_tcsteps' + AND fk_id IN ({$exec_id_list}) "; + + $rs = $this->db->fetchRowsIntoMap($dummy, 'id'); + if (! is_null($rs)) { + foreach ($rs as $fik => $v) { + deleteAttachment($this->db, $fik, false); + } + } + + // Work on Execution on Test Case Steps + // Attachments + $dummy = " /* $debugMsg */ SELECT id FROM {$this->tables['attachments']} + WHERE fk_table = 'execution_tcsteps' AND fk_id IN ( SELECT id FROM {$this->tables['execution_tcsteps']} - {$exec_id_where} )"; - - $rs = $this->db->fetchRowsIntoMap($dummy,'id'); - if(!is_null($rs)) { - foreach($rs as $fik => $v) { - deleteAttachment($this->db,$fik,false); - } - } - - // Remove test case STEP executions if any exists - // execution_id is an attribute. - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['execution_tcsteps']} - {$exec_id_where} "; - $result = $this->db->exec_query($sql); - - - // Grand Finale now remove executions - $sql = " /* $debugMsg */ DELETE FROM {$this->tables['executions']} - WHERE testplan_id = {$id} AND ${where_clause}"; - $result = $this->db->exec_query($sql); - } - - // ---------------------------------------------------------------- - // to remove the assignment to users (if any exists) we need the list of id - $sql=" SELECT id AS link_id FROM {$this->tables['testplan_tcversions']} " . - " WHERE testplan_id={$id} AND {$where_clause} "; - $link_ids = $this->db->fetchRowsIntoMap($sql,'link_id'); - $features = array_keys($link_ids); - if( count($features) == 1) { - $features=$features[0]; - } - $this->assignment_mgr->delete_by_feature_id($features); - // ---------------------------------------------------------------- - - // Delete from link table - $sql=" DELETE FROM {$this->tables['testplan_tcversions']} " . - " WHERE testplan_id={$id} AND {$where_clause} "; - $result = $this->db->exec_query($sql); - - foreach($items['items'] as $tcase_id => $elem) { - foreach($elem as $platform_id => $tcversion) { - $addInfo=''; - if( isset($platformInfo[$platform_id]) ) { - $addInfo = ' - ' . $platformLabel . ':' . $platformInfo[$platform_id]; - } - $auditMsg=TLS("audit_tc_removed_from_testplan", - $auditInfo['tcasePrefix'] . $auditInfo['info'][$tcversion]['tc_external_id'] . - $title_separator . $auditInfo['info'][$tcversion]['name'], - $auditInfo['info'][$tcversion]['version'], - $auditInfo['tplanInfo']['name'] . $addInfo ); - - logAuditEvent($auditMsg,"UNASSIGN",$id,"testplans"); - } - } - - } // end function unlink_tcversions - - - - /** - * - */ - function get_keywords_map($id,$order_by_clause='') { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $keywords = null; - - $sql = " /* $debugMsg */ "; - $sql .= " SELECT TCKW.keyword_id,KW.keyword " . - " FROM {$this->tables['keywords']} KW " . - " JOIN {$this->tables['testcase_keywords']} TCKW " . - " ON KW.id = TCKW.keyword_id " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TCKW.tcversion_id = TPTCV.tcversion_id " . - " WHERE TPTCV.testplan_id = " . intval($id) . - $order_by_clause; - - $keywords = $this->db->fetchColumnsIntoMap($sql,'keyword_id','keyword'); - - return $keywords; - } - - - /** - * args : - * [$keyword_id]: can be an array - */ - function DEPRECATED_get_keywords_tcases($id,$keyword_id=0) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $CUMULATIVE=1; - $map_keywords=null; - - // keywords are associated to testcase id, then first - // we need to get the list of testcases linked to the testplan - $linked_items = $this->get_linked_items_id($id); - if( !is_null($linked_items) ) - { - $keyword_filter= '' ; - - if( is_array($keyword_id) ) - { - $keyword_filter = " AND keyword_id IN (" . implode(',',$keyword_id) . ")"; - } - else if( $keyword_id > 0 ) - { - $keyword_filter = " AND keyword_id = {$keyword_id} "; - } - - - $tc_id_list = implode(",",array_keys($linked_items)); - - // 20081116 - franciscom - - // Does DISTINCT is needed ? Humm now I think no. - $sql = "SELECT DISTINCT testcase_id,keyword_id,keyword + {$exec_id_where} )"; + + $rs = $this->db->fetchRowsIntoMap($dummy, 'id'); + if (! is_null($rs)) { + foreach ($rs as $fik => $v) { + deleteAttachment($this->db, $fik, false); + } + } + + // Remove test case STEP executions if any exists + // execution_id is an attribute. + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['execution_tcsteps']} + {$exec_id_where} "; + $this->db->exec_query($sql); + + // Grand Finale now remove executions + $sql = " /* $debugMsg */ DELETE FROM {$this->tables['executions']} + WHERE testplan_id = {$id} AND ${where_clause}"; + $this->db->exec_query($sql); + } + + // to remove the assignment to users (if any exists) we need the list of id + $sql = " SELECT id AS link_id FROM {$this->tables['testplan_tcversions']} " . + " WHERE testplan_id={$id} AND {$where_clause} "; + $link_ids = $this->db->fetchRowsIntoMap($sql, 'link_id'); + $features = array_keys($link_ids); + if (count($features) == 1) { + $features = $features[0]; + } + $this->assignment_mgr->delete_by_feature_id($features); + + // Delete from link table + $sql = " DELETE FROM {$this->tables['testplan_tcversions']} " . + " WHERE testplan_id={$id} AND {$where_clause} "; + $this->db->exec_query($sql); + + foreach ($items['items'] as $elem) { + foreach ($elem as $platform_id => $tcversion) { + $addInfo = ''; + if (isset($platformInfo[$platform_id])) { + $addInfo = ' - ' . $platformLabel . ':' . + $platformInfo[$platform_id]; + } + $auditMsg = TLS("audit_tc_removed_from_testplan", + $auditInfo['tcasePrefix'] . + $auditInfo['info'][$tcversion]['tc_external_id'] . + $title_separator . $auditInfo['info'][$tcversion]['name'], + $auditInfo['info'][$tcversion]['version'], + $auditInfo['tplanInfo']['name'] . $addInfo); + + logAuditEvent($auditMsg, "UNASSIGN", $id, "testplans"); + } + } + } + + /** + */ + public function get_keywords_map($id, $order_by_clause = '') + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ "; + $sql .= " SELECT TCKW.keyword_id,KW.keyword " . + " FROM {$this->tables['keywords']} KW " . + " JOIN {$this->tables['testcase_keywords']} TCKW " . + " ON KW.id = TCKW.keyword_id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TCKW.tcversion_id = TPTCV.tcversion_id " . + " WHERE TPTCV.testplan_id = " . intval($id) . $order_by_clause; + + return $this->db->fetchColumnsIntoMap($sql, 'keyword_id', 'keyword'); + } + + /** + * args : + * [$keyword_id]: can be an array + * + * @deprecated + */ + private function getKeywordsTestcases($id, $keyword_id = 0) + { + $cumulative = 1; + $map_keywords = null; + + // keywords are associated to testcase id, then first + // we need to get the list of testcases linked to the testplan + $linked_items = $this->get_linked_items_id($id); + if (! is_null($linked_items)) { + $keyword_filter = ''; + + if (is_array($keyword_id)) { + $keyword_filter = " AND keyword_id IN (" . + implode(',', $keyword_id) . ")"; + } elseif ($keyword_id > 0) { + $keyword_filter = " AND keyword_id = {$keyword_id} "; + } + + $tc_id_list = implode(",", array_keys($linked_items)); + + // 20081116 - franciscom - + // Does DISTINCT is needed ? Humm now I think no. + $sql = "SELECT DISTINCT testcase_id,keyword_id,keyword FROM {$this->tables['testcase_keywords']} testcase_keywords, {$this->tables['keywords']} keywords WHERE keyword_id = keywords.id AND testcase_id IN ( {$tc_id_list} ) {$keyword_filter} - ORDER BY keyword ASC "; - - // 20081116 - franciscom - // CUMULATIVE is needed to get all keywords assigned to each testcase linked to testplan - $map_keywords = $this->db->fetchRowsIntoMap($sql,'testcase_id',$CUMULATIVE); - } - - return ($map_keywords); - } // end function - - - - /** - * args : - * [$keyword_id]: can be an array - */ - function getKeywordsLinkedTCVersions($id,$keyword_id=0) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $keywords=null; - $safeID = intval($id); - - $kwFilter= '' ; - if( is_array($keyword_id) ) { - $kwFilter = " AND keyword_id IN (" . implode(',',$keyword_id) . ")"; - } - else if( $keyword_id > 0 ) { - $kwFilter = " AND keyword_id = {$keyword_id} "; - } - - $sql = " /* $debugMsg */ "; - $sql .= " SELECT TCKW.testcase_id,TCKW.keyword_id,KW.keyword " . - " FROM {$this->tables['keywords']} KW " . - " JOIN {$this->tables['testcase_keywords']} TCKW " . - " ON KW.id = TCKW.keyword_id " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TCKW.tcversion_id = TPTCV.tcversion_id " . - " WHERE TPTCV.testplan_id = " . intval($id) . - " {$kwFilter} ORDER BY keyword ASC "; - - // CUMULATIVE is needed to get all keywords assigned - // to each testcase linked to testplan - $keywords = - $this->db->fetchRowsIntoMap($sql,'testcase_id',database::CUMULATIVE); - - return $keywords; - } // end function - - /** - * args : - * [$platform_id]: can be an array - */ - function getPlatformsLinkedTCVersions($id,$platform_id=0) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $platforms = null; - $safeID = intval($id); - - $platFilter= '' ; - if (is_array($platform_id) ) { - $platFilter = " AND platform_id IN (" . implode(',',$platform_id) . ")"; - } - else if( $platform_id > 0 ) { - $platFilter = " AND $platform_id = {$platform_id} "; - } - - $sql = " /* $debugMsg */ "; - $sql .= " SELECT TCPL.testcase_id,TCPL.platform_id,PL.name + ORDER BY keyword ASC "; + + // 20081116 - franciscom + // CUMULATIVE is needed to get all keywords assigned to each testcase linked to testplan + $map_keywords = $this->db->fetchRowsIntoMap($sql, 'testcase_id', + $cumulative); + } + + return $map_keywords; + } + + /** + * args : + * [$keyword_id]: can be an array + */ + public function getKeywordsLinkedTCVersions($id, $keyword_id = 0) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $kwFilter = ''; + if (is_array($keyword_id)) { + $kwFilter = " AND keyword_id IN (" . implode(',', $keyword_id) . ")"; + } elseif ($keyword_id > 0) { + $kwFilter = " AND keyword_id = {$keyword_id} "; + } + + $sql = " /* $debugMsg */ "; + $sql .= " SELECT TCKW.testcase_id,TCKW.keyword_id,KW.keyword " . + " FROM {$this->tables['keywords']} KW " . + " JOIN {$this->tables['testcase_keywords']} TCKW " . + " ON KW.id = TCKW.keyword_id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TCKW.tcversion_id = TPTCV.tcversion_id " . + " WHERE TPTCV.testplan_id = " . intval($id) . + " {$kwFilter} ORDER BY keyword ASC "; + + // CUMULATIVE is needed to get all keywords assigned + // to each testcase linked to testplan + return $this->db->fetchRowsIntoMap($sql, 'testcase_id', + database::CUMULATIVE); + } + + /** + * args : + * [$platform_id]: can be an array + */ + public function getPlatformsLinkedTCVersions($id, $platform_id = 0) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $platFilter = ''; + if (is_array($platform_id)) { + $platFilter = " AND platform_id IN (" . implode(',', $platform_id) . + ")"; + } elseif ($platform_id > 0) { + $platFilter = " AND $platform_id = {$platform_id} "; + } + + $sql = " /* $debugMsg */ "; + $sql .= " SELECT TCPL.testcase_id,TCPL.platform_id,PL.name FROM {$this->tables['platforms']} PL JOIN {$this->tables['testcase_platforms']} TCPL ON PL.id = TCPL.platform_id JOIN {$this->tables['testplan_tcversions']} TPTCV ON TCPL.tcversion_id = TPTCV.tcversion_id - WHERE TPTCV.testplan_id = " . intval($id) . - " {$platFilter} ORDER BY name ASC "; - - // CUMULATIVE is needed to get all platforms assigned - // to each testcase linked to testplan - $platforms = - $this->db->fetchRowsIntoMap($sql,'testcase_id',database::CUMULATIVE); - - return $platforms; - } // end function - - - /* - function: copy_as - creates a new test plan using an existent one as source. - Note: copy_test_urgency is not appropriate to copy - - - args: id: source testplan id - new_tplan_id: destination - [tplan_name]: default null. - != null => set this as the new name - - [tproject_id]: default null. - != null => set this as the new testproject for the testplan - this allow us to copy testplans to differents test projects. - - [user_id] - [options]: default null - allowed keys: - items2copy: - null: do a deep copy => copy following test plan child elements: - builds,linked tcversions,milestones,user_roles,priorities, - platforms,execution assignment. - - != null, a map with keys that controls what child elements to copy - - copy_assigned_to: - tcversion_type: - null/'current' -> use same version present on source testplan - 'lastest' -> for every testcase linked to source testplan - use lastest available version - - [mappings]: need to be documented - returns: N/A - - - 20101114 - franciscom - Because user assignment is done at BUILD Level, we will force - BUILD COPY no matter user choice if user choose to copy - Test Case assignment. - - - */ - function copy_as($id,$new_tplan_id,$tplan_name=null,$tproject_id=null,$user_id=null, - $options=null,$mappings=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $cp_methods = array('copy_milestones' => 'copy_milestones', - 'copy_user_roles' => 'copy_user_roles', - 'copy_platforms_links' => 'copy_platforms_links', - 'copy_attachments' => 'copy_attachments'); - - $mapping_methods = array('copy_platforms_links' => 'platforms'); - - $my['options'] = array(); - - // Configure here only elements that has his own table. - $my['options']['items2copy']= array('copy_tcases' => 1,'copy_milestones' => 1, 'copy_user_roles' => 1, - 'copy_builds' => 1, 'copy_platforms_links' => 1, - 'copy_attachments' => 1, 'copy_priorities' => 1); - - $my['options']['copy_assigned_to'] = 0; - $my['options']['tcversion_type'] = null; - - $my['options'] = array_merge($my['options'], (array)$options); - - $safe['new_tplan_id'] = intval($new_tplan_id); - - // get source testplan general info - $rs_source=$this->get_by_id($id); - - if(!is_null($tplan_name)) - { - $sql="/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . - "SET name='" . $this->db->prepare_string(trim($tplan_name)) . "' " . - "WHERE id=" . $safe['new_tplan_id']; - $this->db->exec_query($sql); - } - - if(!is_null($tproject_id)) - { - $sql="/* $debugMsg */ UPDATE {$this->tables['testplans']} SET testproject_id={$tproject_id} " . - "WHERE id=" . $safe['new_tplan_id']; - $this->db->exec_query($sql); - } - - // copy builds and tcversions out of following loop, because of the user assignments per build - // special measures have to be taken - $build_id_mapping = null; - if($my['options']['items2copy']['copy_builds']) - { - $build_id_mapping = $this->copy_builds($id,$safe['new_tplan_id']); - } - - // Important Notice: - // Since the addition of Platforms, test case versions are linked to Test Plan AND Platforms - // this means, that not matter user choice, we will force Platforms COPY. - // This is a lazy approach, instead of complex one that requires understand what Platforms - // have been used on SOURCE Test Plan. - // - // copy test cases is an special copy - if( $my['options']['items2copy']['copy_tcases'] ) - { - $my['options']['items2copy']['copy_platforms_links'] = 1; - $this->copy_linked_tcversions($id,$new_tplan_id,$user_id,$my['options'],$mappings, $build_id_mapping); - } - - foreach( $my['options']['items2copy'] as $key => $do_copy ) - { - if( $do_copy ) - { - if( isset($cp_methods[$key]) ) - { - $copy_method=$cp_methods[$key]; - if( isset($mapping_methods[$key]) && isset($mappings[$mapping_methods[$key]])) - { - $this->$copy_method($id,$new_tplan_id,$mappings[$mapping_methods[$key]]); - } - else - { - $this->$copy_method($id,$new_tplan_id); - } - } - } - } - } // end function copy_as - - - - /** - * $id: source testplan id - * $new_tplan_id: destination - */ - private function copy_builds($id,$new_tplan_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $rs=$this->get_builds($id); - - $id_mapping = array(); - if(!is_null($rs)) - { - foreach($rs as $build) - { - $add2sql = ''; - $fields = 'name,notes,'; - if(strlen(trim($build['release_date'])) > 0) - { - $fields .= 'release_date,'; - $add2sql = "'" . $this->db->prepare_string($build['release_date']) . "',"; - } - $fields .= 'testplan_id'; - - $sql = " /* $debugMsg */ INSERT INTO {$this->tables['builds']} " . - " ({$fields}) " . - "VALUES ('" . $this->db->prepare_string($build['name']) ."'," . - "'" . $this->db->prepare_string($build['notes']) . "', {$add2sql} {$new_tplan_id})"; - - $this->db->exec_query($sql); - $new_id = $this->db->insert_id($this->tables['builds']); - $id_mapping[$build['id']] = $new_id; - } - } - return $id_mapping; - } - - - /* - function: copy_linked_tcversions - - args: id: source testplan id - new_tplan_id: destination - [options] - [tcversion_type]: default null -> use same version present on source testplan - 'lastest' -> for every testcase linked to source testplan - use lastest available version - [copy_assigned_to]: 1 -> copy execution assignments without role control - - [$mappings] useful when this method is called due to a Test Project COPY AS (yes PROJECT no PLAN) - - returns: - - Note: test urgency is set to default in the new Test plan (not copied) - - @internal revisions - 20110104 - asimon - BUGID 4118: Copy Test plan feature is not copying test cases for all platforms - 20101114 - franciscom - BUGID 4017: Create plan as copy - Priorities are ALWAYS COPIED - */ - private function copy_linked_tcversions($id,$new_tplan_id,$user_id=-1, $options=null,$mappings=null, $build_id_mapping) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options']['tcversion_type'] = null; - $my['options']['copy_assigned_to'] = 0; - $my['options'] = array_merge($my['options'], (array)$options); - $now_ts = $this->db->db_now(); - - $sql="/* $debugMsg */ "; - if($my['options']['copy_assigned_to']) - { - $sql .= " SELECT TPTCV.*, COALESCE(UA.user_id,-1) AS tester, " . - " COALESCE(UA.build_id,0) as assigned_build " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " LEFT OUTER JOIN {$this->tables['user_assignments']} UA ON " . - " UA.feature_id = TPTCV.id " . - " WHERE testplan_id={$id} "; - } - else - { - $sql .= " SELECT TPTCV.* FROM {$this->tables['testplan_tcversions']} TPTCV" . - " WHERE testplan_id={$id} "; - } - - $rs=$this->db->get_recordset($sql); - if(!is_null($rs)) - { - $tcase_mgr = new testcase($this->db); - $doMappings = !is_null($mappings); - $already_linked_versions = array(); - - foreach($rs as $elem) - { - $tcversion_id = $elem['tcversion_id']; - - // Seems useless - 20100204 - $feature_id = $elem['id']; - if( !is_null($my['options']['tcversion_type']) ) - { - $sql="/* $debugMsg */ SELECT * FROM {$this->tables['nodes_hierarchy']} WHERE id={$tcversion_id} "; - $rs2=$this->db->get_recordset($sql); - // Ticket 4696 - if tcversion_type is set to latest -> update linked version - if ($my['options']['tcversion_type'] == 'latest') - { - $last_version_info = $tcase_mgr->get_last_version_info($rs2[0]['parent_id']); - $tcversion_id = $last_version_info ? $last_version_info['id'] : $tcversion_id ; - } - } - - // mapping need to be done with: - // platforms - // test case versions - $platform_id = $elem['platform_id']; - if( $doMappings ) - { - if( isset($mappings['platforms'][$platform_id]) ) - { - $platform_id = $mappings['platforms'][$platform_id]; - } - if( isset($mappings['test_spec'][$tcversion_id]) ) - { - $tcversion_id = $mappings['test_spec'][$tcversion_id]; - } - } - - // Create plan as copy - Priorities are ALWAYS COPIED - $sql = "/* $debugMsg */ " . - " INSERT INTO {$this->tables['testplan_tcversions']} " . - " (testplan_id,tcversion_id,platform_id,node_order "; - $sql_values = " VALUES({$new_tplan_id},{$tcversion_id},{$platform_id}," . - " {$elem['node_order']} "; - - if($my['options']['items2copy']['copy_priorities']) - { - $sql .= ",urgency "; - $sql_values .= ",{$elem['urgency']}"; - } - $sql .= " ) " . $sql_values . " ) "; - - // to avoid warnings - $doIt = !isset($already_linked_versions[$platform_id]); - if ($doIt || !in_array($tcversion_id, $already_linked_versions[$platform_id])) - { - $this->db->exec_query($sql); - $new_feature_id = $this->db->insert_id($this->tables['testplan_tcversions']); - $already_linked_versions[$platform_id][] = $tcversion_id; - } - - if($my['options']['copy_assigned_to'] && $elem['tester'] > 0) - { - $features_map = array(); - $feature_id=$new_feature_id; - $features_map[$feature_id]['user_id'] = $elem['tester']; - $features_map[$feature_id]['build_id'] = $build_id_mapping[$elem['assigned_build']]; - $features_map[$feature_id]['type'] = $this->assignment_types['testcase_execution']['id']; - $features_map[$feature_id]['status'] = $this->assignment_status['open']['id']; - $features_map[$feature_id]['creation_ts'] = $now_ts; - $features_map[$feature_id]['assigner_id'] = $user_id; - - if ($features_map[$feature_id]['build_id'] != 0) { - $this->assignment_mgr->assign($features_map); - } - } - - } - } - } - - -/* - function: copy_milestones - - args: id: source testplan id - new_tplan_id: destination - - returns: - - rev : - 20090910 - franciscom - added start_date - - 20070519 - franciscom - changed date to target_date, because date is an Oracle reverved word. -*/ - private function copy_milestones($tplan_id,$new_tplan_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $rs=$this->get_milestones($tplan_id); - if(!is_null($rs)) - { - foreach($rs as $mstone) - { - // BUGID 3430 - need to check if start date is NOT NULL - $add2fields = ''; - $add2values = ''; - $use_start_date = strlen(trim($mstone['start_date'])) > 0; - if( $use_start_date ) - { - $add2fields = 'start_date,'; - $add2values = "'" . $mstone['start_date'] . "',"; - } - - $sql = "INSERT INTO {$this->tables['milestones']} (name,a,b,c,target_date,{$add2fields} testplan_id)"; - $sql .= " VALUES ('" . $this->db->prepare_string($mstone['name']) ."'," . - $mstone['high_percentage'] . "," . $mstone['medium_percentage'] . "," . - $mstone['low_percentage'] . ",'" . $mstone['target_date'] . "', {$add2values}{$new_tplan_id})"; - $this->db->exec_query($sql); - } - } - } - - - /** - * Get all milestones for a Test Plan - * @param int $tplan_id Test Plan identificator - * @return array of arrays TBD fields description - */ - function get_milestones($tplan_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql=" /* $debugMsg */ SELECT id, name, a AS high_percentage, b AS medium_percentage, c AS low_percentage, " . - "target_date, start_date,testplan_id " . - "FROM {$this->tables['milestones']} " . - "WHERE testplan_id={$tplan_id} ORDER BY target_date,name"; - return $this->db->get_recordset($sql); - } - - - /** - * Copy user roles to a new Test Plan - * - * @param int $source_id original Test Plan id - * @param int $target_id new Test Plan id - */ - private function copy_user_roles($source_id, $target_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT user_id,role_id FROM {$this->tables['user_testplan_roles']} " . - " WHERE testplan_id={$source_id} "; - $rs = $this->db->get_recordset($sql); - if(!is_null($rs)) - { - foreach($rs as $elem) - { - $sql="INSERT INTO {$this->tables['user_testplan_roles']} " . - "(testplan_id,user_id,role_id) " . - "VALUES({$target_id}," . $elem['user_id'] ."," . $elem['role_id'] . ")"; - $this->db->exec_query($sql); - } - } - } - - - /** - * Gets all testplan related user roles - * - * @param integer $id the testplan id - * @return array assoc map with keys taken from the user_id column - **/ - function getUserRoleIDs($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT user_id,role_id FROM {$this->tables['user_testplan_roles']} " . - "WHERE testplan_id = {$id}"; - $roles = $this->db->fetchRowsIntoMap($sql,'user_id'); - return $roles; - } - - - /** - * Inserts a testplan related role for a given user - * - * @param int $userID the id of the user - * @param int $id the testplan id - * @param int $roleID the role id - * - * @return integer returns tl::OK on success, tl::ERROR else - **/ - - function addUserRole($userID,$id,$roleID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $status = tl::ERROR; - $sql = " /* $debugMsg */ INSERT INTO {$this->tables['user_testplan_roles']} (user_id,testplan_id,role_id) VALUES " . - " ({$userID},{$id},{$roleID})"; - if ($this->db->exec_query($sql)) - { - $testPlan = $this->get_by_id($id); - $role = tlRole::getByID($this->db,$roleID,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); - $user = tlUser::getByID($this->db,$userID,tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); - if ($user && $testPlan && $role) - { - logAuditEvent(TLS("audit_users_roles_added_testplan",$user->getDisplayName(), - $testPlan['name'],$role->name),"ASSIGN",$id,"testplans"); - } - $status = tl::OK; - } - return $status; - } - - - /** - * Deletes all testplan related role assignments for a given testplan - * - * @param int $id the testplan id - * @return tl::OK on success, tl::FALSE else - **/ - function deleteUserRoles($id,$users=null,$opt=null) - { - $my['opt'] = array('auditlog' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $status = tl::ERROR; - $sql = " /* $debugMsg */ DELETE FROM {$this->tables['user_testplan_roles']} " . - " WHERE testplan_id = " . intval($id); - if(!is_null($users)) - { - $sql .= " AND user_id IN(" . implode(',',$users) . ")"; - } - - if ($this->db->exec_query($sql) && $my['opt']['auditlog']) - { - $testPlan = $this->get_by_id($id); - if ($testPlan) - { - if(is_null($users)) - { - logAuditEvent(TLS("audit_all_user_roles_removed_testplan", - $testPlan['name']),"ASSIGN",$id,"testplans"); - } - else - { - // TBD - } - } - $status = tl::OK; - } - return $status; - } - - - /** - * Delete test plan and all related link to other items - * - */ - function delete($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $id = intval($id); - - $the_sql=array(); - $main_sql=array(); - - $this->deleteUserRoles($id); - $getFeaturesSQL = " /* $debugMsg */ SELECT id FROM {$this->tables['testplan_tcversions']} WHERE testplan_id={$id} "; - $the_sql[]="DELETE FROM {$this->tables['milestones']} WHERE testplan_id={$id}"; - - // CF used on testplan_design are linked by testplan_tcversions.id - $the_sql[]="DELETE FROM {$this->tables['cfield_testplan_design_values']} WHERE link_id ". - "IN ({$getFeaturesSQL})"; - - $the_sql[]="DELETE FROM {$this->tables['user_assignments']} WHERE feature_id ". - "IN ({$getFeaturesSQL})"; - - $the_sql[]="DELETE FROM {$this->tables['testplan_platforms']} WHERE testplan_id={$id}"; - - $the_sql[]="DELETE FROM {$this->tables['testplan_tcversions']} WHERE testplan_id={$id}"; - - $the_sql[]="DELETE FROM {$this->tables['cfield_execution_values']} WHERE testplan_id={$id}"; - $the_sql[]="DELETE FROM {$this->tables['user_testplan_roles']} WHERE testplan_id={$id}"; - - - - // When deleting from executions, we need to clean related tables - $execIDSetSQL = " SELECT id FROM {$this->tables['executions']} WHERE testplan_id={$id} "; - - // get test step exec attachments if any exists - $dummy = " SELECT id FROM {$this->tables['execution_tcsteps']} " . - " WHERE execution_id IN ({$execIDSetSQL}) "; - - $rs = $this->db->fetchRowsIntoMap($dummy,'id'); - if(!is_null($rs)) { - foreach($rs as $fik => $v) { - deleteAttachment($this->db,$fik,false); - } - } - - // execution attachments - $dummy = " SELECT id FROM {$this->tables['attachments']} " . - " WHERE fk_table = 'executions' " . - " AND fk_id IN ({$execIDSetSQL}) "; - - $rs = $this->db->fetchRowsIntoMap($dummy,'id'); - if(!is_null($rs)) { - foreach($rs as $fik => $v) { - deleteAttachment($this->db,$fik,false); - } - } - - - $the_sql[]="DELETE FROM {$this->tables['execution_bugs']} WHERE execution_id ". - "IN ($execIDSetSQL)"; - - $the_sql[]="DELETE FROM {$this->tables['execution_tcsteps']} WHERE execution_id ". - "IN ($execIDSetSQL) "; - $the_sql[]="DELETE FROM {$this->tables['executions']} WHERE testplan_id={$id}"; - $the_sql[]="DELETE FROM {$this->tables['builds']} WHERE testplan_id={$id}"; - - - foreach($the_sql as $sql) { - $this->db->exec_query($sql); - } - - $this->deleteAttachments($id); - - $this->cfield_mgr->remove_all_design_values_from_node($id); - // ------------------------------------------------------------------------ - - // Finally delete from main table - $main_sql[]="DELETE FROM {$this->tables['testplans']} WHERE id={$id}"; - $main_sql[]="DELETE FROM {$this->tables['nodes_hierarchy']} " . - "WHERE id={$id} AND node_type_id=" . - $this->node_types_descr_id['testplan']; - - foreach($main_sql as $sql) - { - $this->db->exec_query($sql); - } - } // end delete() - - - - // -------------------------------------------------------------------------------------- - // Build related methods - // -------------------------------------------------------------------------------------- - - /* - function: get_builds_for_html_options() - - - args : - $id : test plan id. - [active]: default:null -> all, 1 -> active, 0 -> inactive BUILDS - [open] : default:null -> all, 1 -> open , 0 -> closed/completed BUILDS - [opt] - - returns: - - rev : - */ - function get_builds_for_html_options($id,$active=null,$open=null,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my = array(); - $my['opt'] = array('orderByDir' => null,'excludeBuild' => 0); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = " /* $debugMsg */ SELECT id, name " . - " FROM {$this->tables['builds']} WHERE testplan_id = {$id} "; - - if( !is_null($active) ) - { - $sql .= " AND active=" . intval($active) . " "; - } - - if( !is_null($open) ) - { - $sql .= " AND is_open=" . intval($open) . " "; - } - - if( $my['opt']['excludeBuild'] > 0) - { - $sql .= " AND id <> " . intval($my['opt']['excludeBuild']) . " "; - } - - $orderClause = " ORDER BY name ASC"; - if( !is_null($my['opt']['orderByDir']) ) - { - $xx = explode(':',$my['opt']['orderByDir']); - $orderClause = 'ORDER BY ' . $xx[0] . ' ' . $xx[1]; - } - $sql .= $orderClause; - - $recordset=$this->db->fetchColumnsIntoMap($sql,'id','name'); - - // we will apply natsort only if order by name was requested - if( !is_null($recordset) && stripos($orderClause, 'name') !== FALSE) - { - natsort($recordset); - } - - return $recordset; - } - - - /* - function: get_max_build_id - - args : - $id : test plan id. - - returns: - */ - function get_max_build_id($id,$active = null,$open = null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT MAX(id) AS maxbuildid " . - " FROM {$this->tables['builds']} " . - " WHERE testplan_id = {$id}"; - - if(!is_null($active)) - { - $sql .= " AND active = " . intval($active) . " "; - } - if( !is_null($open) ) - { - $sql .= " AND is_open = " . intval($open) . " "; - } - - $recordset = $this->db->get_recordset($sql); - $maxBuildID = 0; - if ($recordset) - { - $maxBuildID = intval($recordset[0]['maxbuildid']); - } - return $maxBuildID; - } - - /* - function: get_testsuites - args : - $id : test plan id. - returns: returns flat list of names of test suites (including nest test suites) No particular Order. - */ - function get_testsuites($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " /* $debugMsg */ SELECT NHTSUITE.name, NHTSUITE.id, NHTSUITE.parent_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV, {$this->tables['nodes_hierarchy']} NHTCV, " . - " {$this->tables['nodes_hierarchy']} NHTCASE, {$this->tables['nodes_hierarchy']} NHTSUITE " . - " WHERE TPTCV.tcversion_id = NHTCV.id " . - " AND NHTCV.parent_id = NHTCASE.id " . - " AND NHTCASE.parent_id = NHTSUITE.id " . - " AND TPTCV.testplan_id = " . $id . " " . - " GROUP BY NHTSUITE.name,NHTSUITE.id,NHTSUITE.parent_id " . - " ORDER BY NHTSUITE.name" ; - - $recordset = $this->db->get_recordset($sql); - - // Now the recordset contains testsuites that have child test cases. - // However there could potentially be testsuites that only have grandchildren/greatgrandchildren - // this will iterate through found test suites and check for - $superset = $recordset; - foreach($recordset as $value) - { - $superset = array_merge($superset, $this->get_parenttestsuites($value['id'])); - } - - // At this point there may be duplicates - $dup_track = array(); - foreach($superset as $value) - { - if (!array_key_exists($value['id'],$dup_track)) - { - $dup_track[$value['id']] = true; - $finalset[] = $value; - } - } - - // Needs to be alphabetical based upon name attribute - usort($finalset, array("testplan", "compare_name")); - return $finalset; - } - - - /* - function: compare_name - Used for sorting a list by nest name attribute - - args : - $a : first array to compare - $b : second array to compare - - returns: an integer indicating the result of the comparison - */ - static private function compare_name($a, $b) - { - return strcasecmp($a['name'], $b['name']); - } - - - /* - function: get_parenttestsuites - - Used by get_testsuites - - Recursive function used to get all the parent test suites of potentially testcase free testsuites. - If passed node id isn't the product then it's merged into result set. - - args : - $id : $id of potential testsuite - - returns: an array of all testsuite ancestors of $id - */ - private function get_parenttestsuites($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT name, id, parent_id " . - "FROM {$this->tables['nodes_hierarchy']} NH " . - "WHERE NH.node_type_id <> {$this->node_types_descr_id['testproject']} " . - "AND NH.id = " . $id; - - $recordset = (array)$this->db->get_recordset($sql); - $myarray = array(); - if (count($recordset) > 0) { - $myarray = array($recordset[0]); - $myarray = array_merge($myarray, $this->get_parenttestsuites($recordset[0]['parent_id'])); - } - - return $myarray; - } - - - /* - function: get_builds - get info about builds defined for a testlan. - Build can be filtered by active and open status. - - args : - id: test plan id. - [active]: default:null -> all, 1 -> active, 0 -> inactive BUILDS - [open]: default:null -> all, 1 -> open , 0 -> closed/completed BUILDS - [opt] - - returns: opt['getCount'] == false - map, where elements are ordered by build name, using variant of nasort php function. - key: build id - value: map with following keys - id: build id - name: build name - notes: build notes - active: build active status - is_open: build open status - testplan_id - release_date - - opt['getCount'] == true - map key: test plan id - values: map with following key testplan_id, build_qty - rev : - */ - function get_builds($id,$active=null,$open=null,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('fields' => - 'id,testplan_id, name, notes, active, is_open,release_date,closed_on_date,creation_ts', - 'orderBy' => " ORDER BY name ASC", 'getCount' => false, 'buildID' => null); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - if( $my['opt']['getCount'] ) - { - $my['opt']['orderBy'] = null; - - $accessField = 'testplan_id'; - $groupBy = " GROUP BY testplan_id "; - $itemSet = (array)$id; - - $sql = " /* $debugMsg */ " . - " SELECT testplan_id, count(0) AS build_qty " . - " FROM {$this->tables['builds']} " . - " WHERE testplan_id IN ('" . implode("','", $itemSet) . "') "; - } - else - { - $accessField = 'id'; - $groupBy = ''; - $sql = " /* $debugMsg */ " . - " SELECT {$my['opt']['fields']} " . - " FROM {$this->tables['builds']} WHERE testplan_id = {$id} " ; - - if( !is_null($my['opt']['buildID']) ) - { - $sql .= " AND id=" . intval($my['opt']['buildID']) . " "; - } - } - - - if( !is_null($active) ) - { - $sql .= " AND active=" . intval($active) . " "; - } - if( !is_null($open) ) - { - $sql .= " AND is_open=" . intval($open) . " "; - } - - $sql .= $groupBy; - $sql .= ($doOrderBy = !is_null($my['opt']['orderBy'])) ? $my['opt']['orderBy'] : ''; - - $rs = $this->db->fetchRowsIntoMap($sql,$accessField); - - // _natsort_builds() has to be used ONLY if name is used on ORDER BY - if( !is_null($rs) && $doOrderBy && strpos($my['opt']['orderBy'],'name') !== FALSE) - { - $rs = $this->_natsort_builds($rs); - } - - return $rs; - } - - - /** - * Get a build belonging to a test plan, using build name as access key - * - * @param int $id test plan id - * @param string $build_name - * - * @return array [id,testplan_id, name, notes, active, is_open] - */ - function get_build_by_name($id,$build_name) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe_build_name=$this->db->prepare_string(trim($build_name)); - - $sql = " /* $debugMsg */ SELECT id,testplan_id, name, notes, active, is_open " . - " FROM {$this->tables['builds']} " . - " WHERE testplan_id = {$id} AND name='{$safe_build_name}'"; - - - $recordset = $this->db->get_recordset($sql); - $rs=null; - if( !is_null($recordset) ) - { - $rs=$recordset[0]; - } - return $rs; - } - - - /** - * Get a build belonging to a test plan, using build id as access key - * - * @param int $id test plan id - * @param int $build_id - * - * @return array [id,testplan_id, name, notes, active, is_open] - */ - function get_build_by_id($id,$build_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT id,testplan_id, name, notes, active, is_open " . - " FROM {$this->tables['builds']} BUILDS " . - " WHERE testplan_id = {$id} AND BUILDS.id={$build_id}"; - - $recordset = $this->db->get_recordset($sql); - $rs=null; - if( !is_null($recordset) ) - { - $rs=$recordset[0]; - } - return $rs; - } - - - /** - * Get the number of builds of a given Testplan - * - * @param int tplanID test plan id - * - * @return int number of builds - * - * @internal revisions: - * 20100217 - asimon - added parameters active and open to get only number of active/open builds - */ - function getNumberOfBuilds($tplanID, $active = null, $open = null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ SELECT count(id) AS num_builds FROM {$this->tables['builds']} builds " . - "WHERE builds.testplan_id = " . $tplanID; - - if( !is_null($active) ) - { - $sql .= " AND builds.active=" . intval($active) . " "; - } - if( !is_null($open) ) - { - $sql .= " AND builds.is_open=" . intval($open) . " "; - } - - return $this->db->fetchOneValue($sql); - } - - /** - * - */ - function _natsort_builds($builds_map) { - // sort in natural order (see natsort in PHP manual) - foreach($builds_map as $key => $value) { - $build_names[$key] = $value['name']; - } - - natsort($build_names); - foreach($build_names as $key => $value) { - $dummy[$key] = $builds_map[$key]; - } - return $dummy; - } - - - /* - function: check_build_name_existence - - args: - tplan_id: test plan id. - build_name - [build_id}: default: null - when is not null we add build_id as filter, this is useful - to understand if is really a duplicate when using this method - while managing update operations via GUI - - returns: 1 => name exists - - */ - function check_build_name_existence($tplan_id,$build_name,$build_id=null,$case_sensitive=0) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT id, name, notes " . - " FROM {$this->tables['builds']} " . - " WHERE testplan_id = {$tplan_id} "; - - - if($case_sensitive) { - $sql .= " AND name="; - } else { - $build_name=strtoupper($build_name); - $sql .= " AND UPPER(name)="; - } - $sql .= "'" . $this->db->prepare_string($build_name) . "'"; - - if( !is_null($build_id) ) { - $sql .= " AND id <> " . $this->db->prepare_int($build_id); - } - - $result = $this->db->exec_query($sql); - $status= $this->db->num_rows($result) ? 1 : 0; - - return $status; - } - - - /* - function: get_build_id_by_name - - Ignores case - - args : - $tplan_id : test plan id. - $build_name : build name. - - returns: - The ID of the build name specified regardless of case. - - rev : - */ - function get_build_id_by_name($tplan_id,$build_name) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT builds.id, builds.name, builds.notes " . - " FROM {$this->tables['builds']} builds " . - " WHERE builds.testplan_id = {$tplan_id} "; - - $build_name=strtoupper($build_name); - $sql .= " AND UPPER(builds.name)="; - $sql .= "'" . $this->db->prepare_string($build_name) . "'"; - - $recordset = $this->db->get_recordset($sql); - $BuildID = $recordset ? intval($recordset[0]['id']) : 0; - - return $BuildID; - } - - - // -------------------------------------------------------------------------------------- - // Custom field related methods - // -------------------------------------------------------------------------------------- - /* - function: get_linked_cfields_at_design - - args: $id - [$parent_id]: testproject id - [$show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - 0 or null -> don't filter - - returns: hash - - rev : - */ - function get_linked_cfields_at_design($id,$parent_id=null,$show_on_execution=null) - { - $path_len=0; - if( is_null($parent_id) ) - { - // Need to get testplan parent (testproject id) in order to get custom fields - // 20081122 - franciscom - need to check when we can call this with ID=NULL - $the_path = $this->tree_manager->get_path(!is_null($id) ? $id : $parent_id); - $path_len = count($the_path); - } - $tproject_id = ($path_len > 0)? $the_path[$path_len-1]['parent_id'] : $parent_id; - - $cf_map = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id,self::ENABLED, - $show_on_execution,'testplan',$id); - - return $cf_map; - } - - - /* - function: get_linked_cfields_at_execution - - args: $id - [$parent_id]: if present is testproject id - [$show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - 0 or null -> don't filter - - returns: hash - - rev : - */ - function get_linked_cfields_at_execution($id,$parent_id=null,$show_on_execution=null) - { - $path_len=0; - if( is_null($parent_id) ) - { - // Need to get testplan parent (testproject id) in order to get custom fields - // 20081122 - franciscom - need to check when we can call this with ID=NULL - $the_path = $this->tree_manager->get_path(!is_null($id) ? $id : $parent_id); - $path_len = count($the_path); - } - $tproject_id = ($path_len > 0)? $the_path[$path_len-1]['parent_id'] : $parent_id; - - // 20081122 - franciscom - humm!! need to look better IMHO this call is done to wrong function - $cf_map=$this->cfield_mgr->get_linked_cfields_at_execution($tproject_id,self::ENABLED, - $show_on_execution,'testplan',$id); - return($cf_map); - } - - - /* Get Custom Fields Detail which are enabled on Execution of a TestCase/TestProject. - function: get_linked_cfields_id - - args: $testproject_id - - returns: hash map of id : label - - rev : - - */ - - function get_linked_cfields_id($tproject_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $field_map = new stdClass(); - - $sql = " /* $debugMsg */ SELECT field_id,label - FROM {$this->tables['cfield_testprojects']} cfield_testprojects, + WHERE TPTCV.testplan_id = " . intval($id) . + " {$platFilter} ORDER BY name ASC "; + + // CUMULATIVE is needed to get all platforms assigned + // to each testcase linked to testplan + return $this->db->fetchRowsIntoMap($sql, 'testcase_id', + database::CUMULATIVE); + } + + /* + * function: copy_as + * creates a new test plan using an existent one as source. + * Note: copy_test_urgency is not appropriate to copy + * + * + * args: id: source testplan id + * new_tplan_id: destination + * [tplan_name]: default null. + * != null => set this as the new name + * + * [tproject_id]: default null. + * != null => set this as the new testproject for the testplan + * this allow us to copy testplans to differents test projects. + * + * [user_id] + * [options]: default null + * allowed keys: + * items2copy: + * null: do a deep copy => copy following test plan child elements: + * builds,linked tcversions,milestones,user_roles,priorities, + * platforms,execution assignment. + * + * != null, a map with keys that controls what child elements to copy + * + * copy_assigned_to: + * tcversion_type: + * null/'current' -> use same version present on source testplan + * 'lastest' -> for every testcase linked to source testplan + * use lastest available version + * + * [mappings]: need to be documented + * returns: N/A + * + * + * 20101114 - franciscom - Because user assignment is done at BUILD Level, we will force + * BUILD COPY no matter user choice if user choose to copy + * Test Case assignment. + * + * + */ + public function copy_as($id, $new_tplan_id, $tplan_name = null, + $tproject_id = null, $user_id = null, $options = null, $mappings = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $cp_methods = array( + 'copyMilestones' => 'copyMilestones', + 'copyUserRoles' => 'copyUserRoles', + 'copyPlatformsLinks' => 'copyPlatformsLinks', + 'copyAttachments' => 'copyAttachments' + ); + + $mapping_methods = array( + 'copyPlatformsLinks' => 'platforms' + ); + + $my['options'] = array(); + + // Configure here only elements that has his own table. + $my['options']['items2copy'] = array( + 'copyTcases' => 1, + 'copyMilestones' => 1, + 'copyUserRoles' => 1, + 'copyBuilds' => 1, + 'copyPlatformsLinks' => 1, + 'copyAttachments' => 1, + 'copyPriorities' => 1 + ); + + $my['options']['copy_assigned_to'] = 0; + $my['options']['tcversion_type'] = null; + + $my['options'] = array_merge($my['options'], (array) $options); + + $safe['new_tplan_id'] = intval($new_tplan_id); + + if (! is_null($tplan_name)) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . + "SET name='" . $this->db->prepare_string(trim($tplan_name)) . + "' " . "WHERE id=" . $safe['new_tplan_id']; + $this->db->exec_query($sql); + } + + if (! is_null($tproject_id)) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['testplans']} SET testproject_id={$tproject_id} " . + "WHERE id=" . $safe['new_tplan_id']; + $this->db->exec_query($sql); + } + + // copy builds and tcversions out of following loop, because of the user assignments per build + // special measures have to be taken + $build_id_mapping = null; + if ($my['options']['items2copy']['copyBuilds']) { + $build_id_mapping = $this->copyBuilds($id, $safe['new_tplan_id']); + } + + // Important Notice: + // Since the addition of Platforms, test case versions are linked to Test Plan AND Platforms + // this means, that not matter user choice, we will force Platforms COPY. + // This is a lazy approach, instead of complex one that requires understand what Platforms + // have been used on SOURCE Test Plan. + // + // copy test cases is an special copy + if ($my['options']['items2copy']['copyTcases']) { + $my['options']['items2copy']['copy_platforms_links'] = 1; + $this->copyLinkedTestcaseVersions($id, $new_tplan_id, $user_id, + $my['options'], $mappings, $build_id_mapping); + } + + foreach ($my['options']['items2copy'] as $key => $do_copy) { + if ($do_copy && isset($cp_methods[$key])) { + $copy_method = $cp_methods[$key]; + if (isset($mapping_methods[$key]) && + isset($mappings[$mapping_methods[$key]])) { + $this->$copy_method($id, $new_tplan_id, + $mappings[$mapping_methods[$key]]); + } else { + $this->$copy_method($id, $new_tplan_id); + } + } + } + } + + /** + * $id: source testplan id + * $new_tplan_id: destination + */ + private function copyBuilds($id, $new_tplan_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $rs = $this->get_builds($id); + + $id_mapping = array(); + if (! is_null($rs)) { + foreach ($rs as $build) { + $add2sql = ''; + $fields = 'name,notes,'; + if (strlen(trim($build['release_date'])) > 0) { + $fields .= 'release_date,'; + $add2sql = "'" . + $this->db->prepare_string($build['release_date']) . "',"; + } + $fields .= 'testplan_id'; + + $sql = " /* $debugMsg */ INSERT INTO {$this->tables['builds']} " . + " ({$fields}) " . "VALUES ('" . + $this->db->prepare_string($build['name']) . "'," . "'" . + $this->db->prepare_string($build['notes']) . + "', {$add2sql} {$new_tplan_id})"; + + $this->db->exec_query($sql); + $new_id = $this->db->insert_id($this->tables['builds']); + $id_mapping[$build['id']] = $new_id; + } + } + return $id_mapping; + } + + /* + * function: copy_linked_tcversions + * + * args: id: source testplan id + * new_tplan_id: destination + * [options] + * [tcversion_type]: default null -> use same version present on source testplan + * 'lastest' -> for every testcase linked to source testplan + * use lastest available version + * [copy_assigned_to]: 1 -> copy execution assignments without role control + * + * [$mappings] useful when this method is called due to a Test Project COPY AS (yes PROJECT no PLAN) + * + * returns: + * + * Note: test urgency is set to default in the new Test plan (not copied) + * + */ + private function copyLinkedTestcaseVersions($id, $new_tplan_id, + $user_id = - 1, $options = null, $mappings = null, + $build_id_mapping = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['options']['tcversion_type'] = null; + $my['options']['copy_assigned_to'] = 0; + $my['options'] = array_merge($my['options'], (array) $options); + $now_ts = $this->db->db_now(); + + $sql = "/* $debugMsg */ "; + if ($my['options']['copy_assigned_to']) { + $sql .= " SELECT TPTCV.*, COALESCE(UA.user_id,-1) AS tester, " . + " COALESCE(UA.build_id,0) as assigned_build " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " LEFT OUTER JOIN {$this->tables['user_assignments']} UA ON " . + " UA.feature_id = TPTCV.id " . " WHERE testplan_id={$id} "; + } else { + $sql .= " SELECT TPTCV.* FROM {$this->tables['testplan_tcversions']} TPTCV" . + " WHERE testplan_id={$id} "; + } + + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + $tcaseMgr = new testcase($this->db); + $doMappings = ! is_null($mappings); + $already_linked_versions = array(); + + foreach ($rs as $elem) { + $tcversion_id = $elem['tcversion_id']; + + if (! is_null($my['options']['tcversion_type'])) { + $sql = "/* $debugMsg */ SELECT * FROM {$this->tables['nodes_hierarchy']} WHERE id={$tcversion_id} "; + $rs2 = $this->db->get_recordset($sql); + // Ticket 4696 - if tcversion_type is set to latest -> update linked version + if ($my['options']['tcversion_type'] == 'latest') { + $last_version_info = $tcaseMgr->getLastVersionInfo( + $rs2[0]['parent_id']); + $tcversion_id = $last_version_info ? $last_version_info['id'] : $tcversion_id; + } + } + + // mapping need to be done with: + // platforms + // test case versions + $platform_id = $elem['platform_id']; + if ($doMappings) { + if (isset($mappings['platforms'][$platform_id])) { + $platform_id = $mappings['platforms'][$platform_id]; + } + if (isset($mappings['test_spec'][$tcversion_id])) { + $tcversion_id = $mappings['test_spec'][$tcversion_id]; + } + } + + // Create plan as copy - Priorities are ALWAYS COPIED + $sql = "/* $debugMsg */ " . + " INSERT INTO {$this->tables['testplan_tcversions']} " . + " (testplan_id,tcversion_id,platform_id,node_order "; + $sql_values = " VALUES({$new_tplan_id},{$tcversion_id},{$platform_id}," . + " {$elem['node_order']} "; + + if ($my['options']['items2copy']['copyPriorities']) { + $sql .= ",urgency "; + $sql_values .= ",{$elem['urgency']}"; + } + $sql .= " ) " . $sql_values . " ) "; + + // to avoid warnings + $doIt = ! isset($already_linked_versions[$platform_id]); + if ($doIt || + ! in_array($tcversion_id, + $already_linked_versions[$platform_id])) { + $this->db->exec_query($sql); + $new_feature_id = $this->db->insert_id( + $this->tables['testplan_tcversions']); + $already_linked_versions[$platform_id][] = $tcversion_id; + } + + if ($my['options']['copy_assigned_to'] && $elem['tester'] > 0) { + $features_map = array(); + $feature_id = $new_feature_id; + $features_map[$feature_id]['user_id'] = $elem['tester']; + $features_map[$feature_id]['build_id'] = $build_id_mapping[$elem['assigned_build']]; + $features_map[$feature_id]['type'] = $this->assignment_types['testcase_execution']['id']; + $features_map[$feature_id]['status'] = $this->assignment_status['open']['id']; + $features_map[$feature_id]['creation_ts'] = $now_ts; + $features_map[$feature_id]['assigner_id'] = $user_id; + + if ($features_map[$feature_id]['build_id'] != 0) { + $this->assignment_mgr->assign($features_map); + } + } + } + } + } + + /* + * function: copy_milestones + * + * args: id: source testplan id + * new_tplan_id: destination + * + * returns: + * + * rev : + * 20090910 - franciscom - added start_date + * + * 20070519 - franciscom + * changed date to target_date, because date is an Oracle reverved word. + */ + private function copyMilestones($tplan_id, $new_tplan_id) + { + $rs = $this->get_milestones($tplan_id); + if (! is_null($rs)) { + foreach ($rs as $mstone) { + // BUGID 3430 - need to check if start date is NOT NULL + $add2fields = ''; + $add2values = ''; + $use_start_date = strlen(trim($mstone['start_date'])) > 0; + if ($use_start_date) { + $add2fields = 'start_date,'; + $add2values = "'" . $mstone['start_date'] . "',"; + } + + $sql = "INSERT INTO {$this->tables['milestones']} (name,a,b,c,target_date,{$add2fields} testplan_id)"; + $sql .= " VALUES ('" . + $this->db->prepare_string($mstone['name']) . "'," . + $mstone['high_percentage'] . "," . + $mstone['medium_percentage'] . "," . + $mstone['low_percentage'] . ",'" . $mstone['target_date'] . + "', {$add2values}{$new_tplan_id})"; + $this->db->exec_query($sql); + } + } + } + + /** + * Get all milestones for a Test Plan + * + * @param int $tplan_id + * Test Plan identificator + * @return array of arrays TBD fields description + */ + public function get_milestones($tplan_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT id, name, a AS high_percentage, b AS medium_percentage, c AS low_percentage, " . + "target_date, start_date,testplan_id " . + "FROM {$this->tables['milestones']} " . + "WHERE testplan_id={$tplan_id} ORDER BY target_date,name"; + return $this->db->get_recordset($sql); + } + + /** + * Copy user roles to a new Test Plan + * + * @param int $source_id + * original Test Plan id + * @param int $target_id + * new Test Plan id + */ + private function copyUserRoles($source_id, $target_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT user_id,role_id FROM {$this->tables['user_testplan_roles']} " . + " WHERE testplan_id={$source_id} "; + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + foreach ($rs as $elem) { + $sql = "INSERT INTO {$this->tables['user_testplan_roles']} " . + "(testplan_id,user_id,role_id) " . "VALUES({$target_id}," . + $elem['user_id'] . "," . $elem['role_id'] . ")"; + $this->db->exec_query($sql); + } + } + } + + /** + * Gets all testplan related user roles + * + * @param integer $id + * the testplan id + * @return array assoc map with keys taken from the user_id column + */ + private function getUserRoleIDs($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT user_id,role_id FROM {$this->tables['user_testplan_roles']} " . + "WHERE testplan_id = {$id}"; + return $this->db->fetchRowsIntoMap($sql, 'user_id'); + } + + /** + * Inserts a testplan related role for a given user + * + * @param int $userID + * the id of the user + * @param int $id + * the testplan id + * @param int $roleID + * the role id + * + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function addUserRole($userID, $id, $roleID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $status = tl::ERROR; + $sql = " /* $debugMsg */ INSERT INTO {$this->tables['user_testplan_roles']} (user_id,testplan_id,role_id) VALUES " . + " ({$userID},{$id},{$roleID})"; + if ($this->db->exec_query($sql)) { + $testPlan = $this->get_by_id($id); + $role = tlRole::getByID($this->db, $roleID, + tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); + $user = tlUser::getByID($this->db, $userID, + tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); + if ($user && $testPlan && $role) { + logAuditEvent( + TLS("audit_users_roles_added_testplan", + $user->getDisplayName(), $testPlan['name'], $role->name), + "ASSIGN", $id, "testplans"); + } + $status = tl::OK; + } + return $status; + } + + /** + * Deletes all testplan related role assignments for a given testplan + * + * @param int $id + * the testplan id + * @return tl::OK on success, tl::FALSE else + */ + public function deleteUserRoles($id, $users = null, $opt = null) + { + $my['opt'] = array( + 'auditlog' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $status = tl::ERROR; + $sql = " /* $debugMsg */ DELETE FROM {$this->tables['user_testplan_roles']} " . + " WHERE testplan_id = " . intval($id); + if (! is_null($users)) { + $sql .= " AND user_id IN(" . implode(',', $users) . ")"; + } + + if ($this->db->exec_query($sql) && $my['opt']['auditlog']) { + $testPlan = $this->get_by_id($id); + if ($testPlan && is_null($users)) { + logAuditEvent( + TLS("audit_all_user_roles_removed_testplan", + $testPlan['name']), "ASSIGN", $id, "testplans"); + } + $status = tl::OK; + } + return $status; + } + + /** + * Delete test plan and all related link to other items + */ + public function delete($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $id = intval($id); + + $the_sql = array(); + $main_sql = array(); + + $this->deleteUserRoles($id); + $getFeaturesSQL = " /* $debugMsg */ SELECT id FROM {$this->tables['testplan_tcversions']} WHERE testplan_id={$id} "; + $the_sql[] = "DELETE FROM {$this->tables['milestones']} WHERE testplan_id={$id}"; + + // CF used on testplan_design are linked by testplan_tcversions.id + $the_sql[] = "DELETE FROM {$this->tables['cfield_testplan_design_values']} WHERE link_id " . + "IN ({$getFeaturesSQL})"; + + $the_sql[] = "DELETE FROM {$this->tables['user_assignments']} WHERE feature_id " . + "IN ({$getFeaturesSQL})"; + + $the_sql[] = "DELETE FROM {$this->tables['testplan_platforms']} WHERE testplan_id={$id}"; + + $the_sql[] = "DELETE FROM {$this->tables['testplan_tcversions']} WHERE testplan_id={$id}"; + + $the_sql[] = "DELETE FROM {$this->tables['cfield_execution_values']} WHERE testplan_id={$id}"; + $the_sql[] = "DELETE FROM {$this->tables['user_testplan_roles']} WHERE testplan_id={$id}"; + + // When deleting from executions, we need to clean related tables + $execIDSetSQL = " SELECT id FROM {$this->tables['executions']} WHERE testplan_id={$id} "; + + // get test step exec attachments if any exists + $dummy = " SELECT id FROM {$this->tables['execution_tcsteps']} " . + " WHERE execution_id IN ({$execIDSetSQL}) "; + + $rs = $this->db->fetchRowsIntoMap($dummy, 'id'); + if (! is_null($rs)) { + foreach ($rs as $fik => $v) { + deleteAttachment($this->db, $fik, false); + } + } + + // execution attachments + $dummy = " SELECT id FROM {$this->tables['attachments']} " . + " WHERE fk_table = 'executions' " . + " AND fk_id IN ({$execIDSetSQL}) "; + + $rs = $this->db->fetchRowsIntoMap($dummy, 'id'); + if (! is_null($rs)) { + foreach ($rs as $fik => $v) { + deleteAttachment($this->db, $fik, false); + } + } + + $the_sql[] = "DELETE FROM {$this->tables['execution_bugs']} WHERE execution_id " . + "IN ($execIDSetSQL)"; + $the_sql[] = "DELETE FROM {$this->tables['execution_tcsteps_wip']} WHERE testplan_id={$id}"; + $the_sql[] = "DELETE FROM {$this->tables['execution_tcsteps']} WHERE execution_id " . + "IN ($execIDSetSQL) "; + $the_sql[] = "DELETE FROM {$this->tables['executions']} WHERE testplan_id={$id}"; + $the_sql[] = "DELETE FROM {$this->tables['builds']} WHERE testplan_id={$id}"; + + foreach ($the_sql as $sql) { + $this->db->exec_query($sql); + } + + $this->deleteAttachments($id); + + $this->cfield_mgr->remove_all_design_values_from_node($id); + + // Finally delete from main table + $main_sql[] = "DELETE FROM {$this->tables['testplans']} WHERE id={$id}"; + $main_sql[] = "DELETE FROM {$this->tables['nodes_hierarchy']} " . + "WHERE id={$id} AND node_type_id=" . + $this->node_types_descr_id['testplan']; + + foreach ($main_sql as $sql) { + $this->db->exec_query($sql); + } + } + + // Build related methods + + /* + * function: get_builds_for_html_options() + * + * + * args : + * $id : test plan id. + * [active]: default:null -> all, 1 -> active, 0 -> inactive BUILDS + * [open] : default:null -> all, 1 -> open , 0 -> closed/completed BUILDS + * [opt] + * + * returns: + * + * rev : + */ + public function get_builds_for_html_options($id, $active = null, + $open = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $my = array(); + $my['opt'] = array( + 'orderByDir' => null, + 'excludeBuild' => 0 + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = " /* $debugMsg */ SELECT id, name " . + " FROM {$this->tables['builds']} WHERE testplan_id = {$id} "; + + if (! is_null($active)) { + $sql .= " AND active=" . intval($active) . " "; + } + + if (! is_null($open)) { + $sql .= " AND is_open=" . intval($open) . " "; + } + + if ($my['opt']['excludeBuild'] > 0) { + $sql .= " AND id <> " . intval($my['opt']['excludeBuild']) . " "; + } + + $orderClause = " ORDER BY name ASC"; + if (! is_null($my['opt']['orderByDir'])) { + $xx = explode(':', $my['opt']['orderByDir']); + $orderClause = 'ORDER BY ' . $xx[0] . ' ' . $xx[1]; + } + $sql .= $orderClause; + + $recordset = $this->db->fetchColumnsIntoMap($sql, 'id', 'name'); + + // we will apply natsort only if order by name was requested + if (! is_null($recordset) && stripos($orderClause, 'name') !== false) { + natsort($recordset); + } + + return $recordset; + } + + /* + * function: get_max_build_id + * + * args : + * $id : test plan id. + * + * returns: + */ + public function get_max_build_id($id, $active = null, $open = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT MAX(id) AS maxbuildid " . + " FROM {$this->tables['builds']} " . " WHERE testplan_id = {$id}"; + + if (! is_null($active)) { + $sql .= " AND active = " . intval($active) . " "; + } + if (! is_null($open)) { + $sql .= " AND is_open = " . intval($open) . " "; + } + + $recordset = $this->db->get_recordset($sql); + $maxBuildID = 0; + if ($recordset) { + $maxBuildID = intval($recordset[0]['maxbuildid']); + } + return $maxBuildID; + } + + /* + * function: get_testsuites + * args : + * $id : test plan id. + * returns: returns flat list of names of test suites (including nest test suites) No particular Order. + */ + public function get_testsuites($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = " /* $debugMsg */ SELECT NHTSUITE.name, NHTSUITE.id, NHTSUITE.parent_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV, {$this->tables['nodes_hierarchy']} NHTCV, " . + " {$this->tables['nodes_hierarchy']} NHTCASE, {$this->tables['nodes_hierarchy']} NHTSUITE " . + " WHERE TPTCV.tcversion_id = NHTCV.id " . + " AND NHTCV.parent_id = NHTCASE.id " . + " AND NHTCASE.parent_id = NHTSUITE.id " . " AND TPTCV.testplan_id = " . + $id . " " . " GROUP BY NHTSUITE.name,NHTSUITE.id,NHTSUITE.parent_id " . + " ORDER BY NHTSUITE.name"; + + $recordset = $this->db->get_recordset($sql); + + // Now the recordset contains testsuites that have child test cases. + // However there could potentially be testsuites that only have grandchildren/greatgrandchildren + // this will iterate through found test suites and check for + $superset = $recordset; + foreach ($recordset as $value) { + $superset = array_merge($superset, + $this->getParentTestsuites($value['id'])); + } + + // At this point there may be duplicates + $dup_track = array(); + foreach ($superset as $value) { + if (! array_key_exists($value['id'], $dup_track)) { + $dup_track[$value['id']] = true; + $finalset[] = $value; + } + } + + // Needs to be alphabetical based upon name attribute + usort($finalset, array( + "testplan", + "compareName" + )); + return $finalset; + } + + /* + * function: compare_name + * Used for sorting a list by nest name attribute + * + * args : + * $a : first array to compare + * $b : second array to compare + * + * returns: an integer indicating the result of the comparison + */ + private static function compareName($a, $b) + { + return strcasecmp($a['name'], $b['name']); + } + + /* + * function: get_parenttestsuites + * + * Used by get_testsuites + * + * Recursive function used to get all the parent test suites of potentially testcase free testsuites. + * If passed node id isn't the product then it's merged into result set. + * + * args : + * $id : $id of potential testsuite + * + * returns: an array of all testsuite ancestors of $id + */ + private function getParentTestsuites($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT name, id, parent_id " . + "FROM {$this->tables['nodes_hierarchy']} NH " . + "WHERE NH.node_type_id <> {$this->node_types_descr_id['testproject']} " . + "AND NH.id = " . $id; + + $recordset = (array) $this->db->get_recordset($sql); + $myarray = array(); + if (! empty($recordset)) { + $myarray = array( + $recordset[0] + ); + $myarray = array_merge($myarray, + $this->getParentTestsuites($recordset[0]['parent_id'])); + } + + return $myarray; + } + + /* + * function: get_builds + * get info about builds defined for a testlan. + * Build can be filtered by active and open status. + * + * args : + * id: test plan id. + * [active]: default:null -> all, 1 -> active, 0 -> inactive BUILDS + * [open]: default:null -> all, 1 -> open , 0 -> closed/completed BUILDS + * [opt] + * + * returns: opt['getCount'] == false + * map, where elements are ordered by build name, using variant of nasort php function. + * key: build id + * value: map with following keys + * id: build id + * name: build name + * notes: build notes + * active: build active status + * is_open: build open status + * testplan_id + * release_date + * + * opt['getCount'] == true + * map key: test plan id + * values: map with following key testplan_id, build_qty + * rev : + */ + public function get_builds($id, $active = null, $open = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['opt'] = array( + 'fields' => 'id,testplan_id, name, notes, active, is_open,release_date,closed_on_date,creation_ts', + 'orderBy' => " ORDER BY name ASC", + 'getCount' => false, + 'buildID' => null + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + if ($my['opt']['getCount']) { + $my['opt']['orderBy'] = null; + + $accessField = 'testplan_id'; + $groupBy = " GROUP BY testplan_id "; + $itemSet = (array) $id; + + $sql = " /* $debugMsg */ " . + " SELECT testplan_id, count(0) AS build_qty " . + " FROM {$this->tables['builds']} " . " WHERE testplan_id IN ('" . + implode("','", $itemSet) . "') "; + } else { + $accessField = 'id'; + $groupBy = ''; + $sql = " /* $debugMsg */ " . " SELECT {$my['opt']['fields']} " . + " FROM {$this->tables['builds']} WHERE testplan_id = {$id} "; + + if (! is_null($my['opt']['buildID'])) { + $sql .= " AND id=" . intval($my['opt']['buildID']) . " "; + } + } + + if (! is_null($active)) { + $sql .= " AND active=" . intval($active) . " "; + } + if (! is_null($open)) { + $sql .= " AND is_open=" . intval($open) . " "; + } + + $sql .= $groupBy; + $sql .= ($doOrderBy = ! is_null($my['opt']['orderBy'])) ? $my['opt']['orderBy'] : ''; + + $rs = $this->db->fetchRowsIntoMap($sql, $accessField); + + // _natsort_builds() has to be used ONLY if name is used on ORDER BY + if (! is_null($rs) && $doOrderBy && + strpos($my['opt']['orderBy'], 'name') !== false) { + $rs = $this->natsortBuilds($rs); + } + + return $rs; + } + + /** + * Get a build belonging to a test plan, using build name as access key + * + * @param int $id + * test plan id + * @param string $build_name + * + * @return array [id,testplan_id, name, notes, active, is_open] + */ + public function get_build_by_name($id, $build_name) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $safe_build_name = $this->db->prepare_string(trim($build_name)); + + $sql = " /* $debugMsg */ SELECT id,testplan_id, name, notes, active, is_open " . + " FROM {$this->tables['builds']} " . + " WHERE testplan_id = {$id} AND name='{$safe_build_name}'"; + + $recordset = $this->db->get_recordset($sql); + $rs = null; + if (! is_null($recordset)) { + $rs = $recordset[0]; + } + return $rs; + } + + /** + * Get a build belonging to a test plan, using build id as access key + * + * @param int $id + * test plan id + * @param int $build_id + * + * @return array [id,testplan_id, name, notes, active, is_open] + */ + public function get_build_by_id($id, $build_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT id,testplan_id, name, notes, active, is_open " . + " FROM {$this->tables['builds']} BUILDS " . + " WHERE testplan_id = {$id} AND BUILDS.id={$build_id}"; + + $recordset = $this->db->get_recordset($sql); + $rs = null; + if (! is_null($recordset)) { + $rs = $recordset[0]; + } + return $rs; + } + + /** + * Get the number of builds of a given Testplan + * + * @param + * int tplanID test plan id + * + * @return int number of builds + * + * @internal revisions: + * 20100217 - asimon - added parameters active and open to get only number of active/open builds + */ + public function getNumberOfBuilds($tplanID, $active = null, $open = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = "/* $debugMsg */ SELECT count(id) AS num_builds FROM {$this->tables['builds']} builds " . + "WHERE builds.testplan_id = " . $tplanID; + + if (! is_null($active)) { + $sql .= " AND builds.active=" . intval($active) . " "; + } + if (! is_null($open)) { + $sql .= " AND builds.is_open=" . intval($open) . " "; + } + + return $this->db->fetchOneValue($sql); + } + + /** + */ + private function natsortBuilds($builds_map) + { + // sort in natural order (see natsort in PHP manual) + foreach ($builds_map as $key => $value) { + $build_names[$key] = $value['name']; + } + + natsort($build_names); + foreach ($build_names as $key => $value) { + $dummy[$key] = $builds_map[$key]; + } + return $dummy; + } + + /* + * function: check_build_name_existence + * + * args: + * tplan_id: test plan id. + * build_name + * [build_id}: default: null + * when is not null we add build_id as filter, this is useful + * to understand if is really a duplicate when using this method + * while managing update operations via GUI + * + * returns: 1 => name exists + * + */ + public function check_build_name_existence($tplan_id, $build_name, + $build_id = null, $case_sensitive = 0) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT id, name, notes " . + " FROM {$this->tables['builds']} " . + " WHERE testplan_id = {$tplan_id} "; + + if ($case_sensitive) { + $sql .= " AND name="; + } else { + $build_name = strtoupper($build_name); + $sql .= " AND UPPER(name)="; + } + $sql .= "'" . $this->db->prepare_string($build_name) . "'"; + + if (! is_null($build_id)) { + $sql .= " AND id <> " . $this->db->prepare_int($build_id); + } + + $result = $this->db->exec_query($sql); + + return $this->db->num_rows($result) ? 1 : 0; + } + + /* + * function: get_build_id_by_name + * + * Ignores case + * + * args : + * $tplan_id : test plan id. + * $build_name : build name. + * + * returns: + * The ID of the build name specified regardless of case. + * + * rev : + */ + public function get_build_id_by_name($tplan_id, $build_name) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT builds.id, builds.name, builds.notes " . + " FROM {$this->tables['builds']} builds " . + " WHERE builds.testplan_id = {$tplan_id} "; + + $build_name = strtoupper($build_name); + $sql .= " AND UPPER(builds.name)="; + $sql .= "'" . $this->db->prepare_string($build_name) . "'"; + + $recordset = $this->db->get_recordset($sql); + return $recordset ? intval($recordset[0]['id']) : 0; + } + + // Custom field related methods + /* + * function: get_linked_cfields_at_design + * + * args: $id + * [$parent_id]: testproject id + * [$show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * 0 or null -> don't filter + * + * returns: hash + * + * rev : + */ + public function get_linked_cfields_at_design($id, $parent_id = null, + $show_on_execution = null) + { + $path_len = 0; + if (is_null($parent_id)) { + // Need to get testplan parent (testproject id) in order to get custom fields + // 20081122 - franciscom - need to check when we can call this with ID=NULL + $the_path = $this->tree_manager->get_path( + ! is_null($id) ? $id : $parent_id); + $path_len = count($the_path); + } + $tproject_id = ($path_len > 0) ? $the_path[$path_len - 1]['parent_id'] : $parent_id; + + return $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, + self::ENABLED, $show_on_execution, 'testplan', $id); + } + + /* + * function: get_linked_cfields_at_execution + * + * args: $id + * [$parent_id]: if present is testproject id + * [$show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * 0 or null -> don't filter + * + * returns: hash + * + * rev : + */ + private function getLinkedCfieldsAtExecution($id, $parent_id = null, + $show_on_execution = null) + { + $path_len = 0; + if (is_null($parent_id)) { + // Need to get testplan parent (testproject id) in order to get custom fields + // 20081122 - franciscom - need to check when we can call this with ID=NULL + $the_path = $this->tree_manager->get_path( + ! is_null($id) ? $id : $parent_id); + $path_len = count($the_path); + } + $tproject_id = ($path_len > 0) ? $the_path[$path_len - 1]['parent_id'] : $parent_id; + + // 20081122 - franciscom - humm!! need to look better IMHO this call is done to wrong function + return $this->cfield_mgr->get_linked_cfields_at_execution($tproject_id, + self::ENABLED, $show_on_execution, 'testplan', $id); + } + + /* + * Get Custom Fields Detail which are enabled on Execution of a TestCase/TestProject. + * function: get_linked_cfields_id + * + * args: $testproject_id + * + * returns: hash map of id : label + * + * rev : + * + */ + private function getLinkedCfieldsID($tproject_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT field_id,label + FROM {$this->tables['cfield_testprojects']} cfield_testprojects, {$this->tables['custom_fields']} custom_fields WHERE - custom_fields.id = cfield_testprojects.field_id - and cfield_testprojects.active = 1 - and custom_fields.enable_on_execution = 1 - and custom_fields.show_on_execution = 1 - and cfield_testprojects.testproject_id = " . $this->db->prepare_int($tproject_id) . - "order by field_id"; - - $field_map = $this->db->fetchColumnsIntoMap($sql,'field_id','label'); - return($field_map); - } - - /* - function: html_table_of_custom_field_inputs - - - args: $id - [$parent_id]: need when you call this method during the creation - of a test suite, because the $id will be 0 or null. - - [$scope]: 'design','execution' - - returns: html string - - */ - function html_table_of_custom_field_inputs($id,$parent_id=null,$scope='design',$name_suffix='',$input_values=null) - { - $cf_smarty=''; - $method_suffix = $scope=='design' ? $scope : 'execution'; - $method_name = "get_linked_cfields_at_{$method_suffix}"; - $cf_map=$this->$method_name($id,$parent_id); - - if(!is_null($cf_map)) - { - $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map,$name_suffix,$input_values); - } - return($cf_smarty); - } - - - /* - function: html_table_of_custom_field_values - - args: $id - [$scope]: 'design','execution' - - [$filters]:default: null - - map with keys: - - [show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - include ONLY custom fields that can be viewed - while user is execution testcases. - - 0 or null -> don't filter - - returns: html string - - rev : - 20080811 - franciscom - BUGID 1650 (REQ) - 20070701 - franciscom - fixed return string when there are no custom fields. - */ - function html_table_of_custom_field_values($id,$scope='design',$filters=null,$formatOptions=null) - { - $cf_smarty=''; - $parent_id=null; - $label_css_style=' class="labelHolder" ' ; - $value_css_style = ' '; - - $add_table=true; - $table_style=''; - if( !is_null($formatOptions) ) - { - $label_css_style = isset($formatOptions['label_css_style']) ? $formatOptions['label_css_style'] : $label_css_style; - $value_css_style = isset($formatOptions['value_css_style']) ? $formatOptions['value_css_style'] : $value_css_style; - - $add_table=isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; - $table_style=isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; - } - - $show_cf = config_get('custom_fields')->show_custom_fields_without_value; - if( $scope=='design' ) - { - $cf_map=$this->get_linked_cfields_at_design($id,$parent_id,$filters); - } - else - { - $cf_map=$this->get_linked_cfields_at_execution($id); - } - - if( !is_null($cf_map) ) - { - foreach($cf_map as $cf_id => $cf_info) - { - // if user has assigned a value, then node_id is not null - // BUGID 3989 - if(isset($cf_info['node_id']) || $cf_info['node_id'] || $show_cf) - { - // true => do not create input in audit log - $label=str_replace(TL_LOCALIZE_TAG,'',lang_get($cf_info['label'],null,true)); - $cf_smarty .= "" . htmlspecialchars($label) . "" . - "" . - $this->cfield_mgr->string_custom_field_value($cf_info,$id) . "\n"; - } - } - } - - if($cf_smarty != '' && $add_table) - { - $cf_smarty = "" . $cf_smarty . "
    "; - } - return($cf_smarty); - } // function end - - - /* - function: filterByOnDesignCustomFields - Filter on values of custom fields that are managed - ON DESIGN Area (i.e. when creating Test Specification). - - @used by getLinkedItems() in file execSetResults.php - - args : - $tp_tcs - key: test case ID - value: map with keys tcase_id,tcversion_id,... - - $cf_hash [cf_id] = value of cfields to filter by. - - returns: array filtered by selected custom fields. - - @internal revisions - - */ - function filterByOnDesignCustomFields($tp_tcs, $cf_hash) - { - $new_tp_tcs = null; - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $or_clause = ''; - $cf_query = ''; - $ignored = 0; - $doFilter = false; - $doIt = true; - - if (isset($cf_hash)) - { - $countmain = 1; - foreach ($cf_hash as $cf_id => $cf_value) - { - // single value or array? - if (is_array($cf_value)) - { - $count = 1; - $cf_query .= $or_clause; - foreach ($cf_value as $value) - { - if ($count > 1) - { - $cf_query .= " AND "; - } - $cf_query .= " ( CFD.value LIKE '%{$value}%' AND CFD.field_id = {$cf_id} )"; - $count++; - } - } - else - { - // Because cf value can NOT exists on DB depending on system config. - if( trim($cf_value) != '') - { - $cf_query .= $or_clause; - $cf_query .= " ( CFD.value LIKE '%{$cf_value}%' AND CFD.field_id = {$cf_id} ) "; - } - else - { - $ignored++; - } - } - - if($or_clause == '') - { - $or_clause = ' OR '; - } - } - - // grand finale - if( $cf_query != '') - { - $cf_query = " AND ({$cf_query}) "; - $doFilter = true; - } - } - $cf_qty = count($cf_hash) - $ignored; - $doIt = !$doFilter; - foreach ($tp_tcs as $tc_id => $tc_value) - { - if( $doFilter ) - { - $sql = " /* $debugMsg */ SELECT CFD.value FROM {$this->tables['cfield_design_values']} CFD," . - " {$this->tables['nodes_hierarchy']} NH" . - " WHERE CFD.node_id = NH.id " . - " AND NH.parent_id = {$tc_value['tcase_id']} " . - " {$cf_query} "; - - $rows = $this->db->fetchColumnsIntoArray($sql,'value'); //BUGID 4115 - - // if there exist as many rows as custom fields to be filtered by => tc does meet the criteria - // TO CHECK - 20140126 - Give a look to treeMenu.inc.php - filter_by_cf_values() - // to understand if both logics are coerent. - // - $doIt = (count($rows) == $cf_qty); - } - if( $doIt ) - { - $new_tp_tcs[$tc_id] = $tp_tcs[$tc_id]; - } - } - return ($new_tp_tcs); - } - - - - - - /* - function: get_estimated_execution_time - - Takes all testcases linked to testplan and computes - SUM of values assigned AT DESIGN TIME to customa field - named CF_ESTIMATED_EXEC_TIME - - IMPORTANT: - 1. at time of this writting (20080820) this CF can be of type: string,numeric or float. - 2. YOU NEED TO USE . (dot) as decimal separator (US decimal separator?) or - sum will be wrong. - - - - args:id testplan id - itemSet: default null - can be an arry with test case VERSION ID - - returns: sum of CF values for all testcases linked to testplan - - rev: - - */ - function get_estimated_execution_time($id,$itemSet=null,$platformID=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $estimated = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - - // check if cf exist and is assigned and active intest plan parent (TEST PROJECT) - $pinfo = $this->tree_manager->get_node_hierarchy_info($id); - $cf_info = $this->cfield_mgr->get_linked_to_testproject($pinfo['parent_id'],1,array('name' => 'CF_ESTIMATED_EXEC_TIME')); - if( is_null($cf_info) ) - { - return $this->getEstimatedExecutionTime($id,$itemSet,$platformID); - } - else - { - return $this->getEstimatedExecutionTimeFromCF($id,$itemSet,$platformID); - } - - } - - /** - * - */ - function getEstimatedExecutionTime($id,$itemSet=null,$platformID=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $estimated = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - - $tcVersionIDSet = array(); - $getOpt = array('outputFormat' => 'mapAccessByID' , 'addIfNull' => true); - $platformSet = array_keys($this->getPlatforms($id,$getOpt)); - - if( is_null($itemSet) ) - { - // we need to loop over all linked PLATFORMS (if any) - $tcVersionIDSet = array(); - foreach($platformSet as $platfID) - { - if(is_null($platformID) || $platformID == $platfID ) - { - $linkedItems = $this->get_linked_tcvid($id,$platfID,array('addEstimatedExecDuration' => true)); - if( (!is_null($linkedItems)) ) - { - $tcVersionIDSet[$platfID]= $linkedItems; - } - } - } - } - else - { - // Important NOTICE - // we can found SOME LIMITS on number of elements on IN CLAUSE - // need to make as many set as platforms linked to test plan - $sql4tplantcv = " /* $debugMsg */ SELECT tcversion_id, platform_id,TCV.estimated_exec_duration " . - " FROM {$this->tables['testplan_tcversions']} " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = tcversion_id " . - " WHERE testplan_id=" . intval($id) . - " AND tcversion_id IN (" . implode(',',$itemSet) . ")"; - if( !is_null($platformID) ) - { - $sql4tplantcv .= " AND platform_id= " . intval($platformID); - } - - $rs = $this->db->fetchRowsIntoMap($sql4tplantcv,'platform_id',database::CUMULATIVE); - foreach($rs as $platfID => $elem) - { - $tcVersionIDSet[$platfID] = $elem; - } - } - - $estimated = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - foreach($tcVersionIDSet as $platfID => $items) - { - $estimated['platform'][$platfID]['minutes'] = 0; - $estimated['platform'][$platfID]['tcase_qty'] = count($items); - foreach($items as $dx) - { - if(!is_null($dx['estimated_exec_duration'])) - { - $estimated['platform'][$platfID]['minutes'] += $dx['estimated_exec_duration']; - } - } - $estimated['totalMinutes'] += $estimated['platform'][$platfID]['minutes']; - $estimated['totalTestCases'] += $estimated['platform'][$platfID]['tcase_qty']; - } - - return $estimated; - } - - - /** - * - */ - function getEstimatedExecutionTimeFromCF($id,$itemSet=null,$platformID=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $estimated = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - $cf_info = $this->cfield_mgr->get_by_name('CF_ESTIMATED_EXEC_TIME'); - - // CF exists ? - if( ($status_ok=!is_null($cf_info)) ) - { - $cfield_id=key($cf_info); - } - - if( $status_ok) - { - $tcVersionIDSet = array(); - $getOpt = array('outputFormat' => 'mapAccessByID' , 'addIfNull' => true); - $platformSet = array_keys($this->getPlatforms($id,$getOpt)); - - $sql = " /* $debugMsg */ "; - if( DB_TYPE == 'mysql') - { - $sql .= " SELECT SUM(value) "; - } - else if ( DB_TYPE == 'postgres' || DB_TYPE == 'mssql' ) - { - $sql .= " SELECT SUM(CAST(value AS NUMERIC)) "; - } - - $sql .= " AS SUM_VALUE FROM {$this->tables['cfield_design_values']} CFDV " . - " WHERE CFDV.field_id={$cfield_id} "; - - if( is_null($itemSet) ) - { - // 20110112 - franciscom - // we need to loop over all linked PLATFORMS (if any) - $tcVersionIDSet = array(); - foreach($platformSet as $platfID) - { - if(is_null($platformID) || $platformID == $platfID ) - { - $linkedItems = $this->get_linked_tcvid($id,$platfID); - if( (!is_null($linkedItems)) ) - { - $tcVersionIDSet[$platfID]= array_keys($linkedItems); - } - } - } - } - else - { - // Important NOTICE - // we can found SOME LIMITS on number of elements on IN CLAUSE - // - // need to make as many set as platforms linked to test plan - $sql4tplantcv = " /* $debugMsg */ SELECT tcversion_id, platform_id " . - " FROM {$this->tables['testplan_tcversions']} " . - " WHERE testplan_id=" . intval($id) . - " AND tcversion_id IN (" . implode(',',$itemSet) . ")"; - - if( !is_null($platformID) ) - { - $sql4tplantcv .= " AND platform_id= " . intval($platformID); - } - - $rs = $this->db->fetchColumnsIntoMap($sql4tplantcv,'platform_id','tcversion_id', - database::CUMULATIVE); - foreach($rs as $platfID => $elem) - { - $tcVersionIDSet[$platfID] = array_values($elem); - } - } - } - - if($status_ok) - { - // Important NOTICE - // we can found SOME LIMITS on number of elements on IN CLAUSE - // - $estimated = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - foreach($tcVersionIDSet as $platfID => $items) - { - $sql2exec = $sql . " AND node_id IN (" . implode(',',$items) . ")"; - $dummy = $this->db->fetchOneValue($sql2exec); - $estimated['platform'][$platfID]['minutes'] = is_null($dummy) ? 0 : $dummy; - $estimated['platform'][$platfID]['tcase_qty'] = count($items); - - $estimated['totalMinutes'] += $estimated['platform'][$platfID]['minutes']; - $estimated['totalTestCases'] += $estimated['platform'][$platfID]['tcase_qty']; - } - } - return $estimated; - } - - - /* - function: get_execution_time - Takes all executions or a subset of executions, regarding a testplan and - computes SUM of values assigned AT EXECUTION TIME to custom field named CF_EXEC_TIME - - IMPORTANT: - 1. at time of this writting (20081207) this CF can be of type: string,numeric or float. - 2. YOU NEED TO USE . (dot) as decimal separator (US decimal separator?) or - sum will be wrong. - - args:id testplan id - $execIDSet: default null - - returns: sum of CF values for all testcases linked to testplan - - rev: - @internal revision - */ - function get_execution_time($context,$execIDSet=null) - { - // check if cf exist and is assigned and active intest plan parent (TEST PROJECT) - $pinfo = $this->tree_manager->get_node_hierarchy_info($id); - $cf_info = $this->cfield_mgr->get_linked_to_testproject($pinfo['parent_id'],1,array('name' => 'CF_EXEC_TIME')); - if( is_null($cf_info) ) - { - return $this->getExecutionTime($context,$execIDSet); - } - else - { - return $this->getExecutionTimeFromCF($context->tplan_id,$execIDSet, - $context->platform_id); - } - } - - - /** - * - */ - function getExecutionTime($context,$execIDSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $total_time = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - $targetSet = array(); - - $getOpt = array('outputFormat' => 'mapAccessByID' , 'addIfNull' => true); - $platformSet = array_keys($this->getPlatforms($context->tplan_id,$getOpt)); - - if( is_null($execIDSet) ) - { - $filters = null; - if( !is_null($context->platform_id) ) - { - $filters = array('platform_id' => $context->platform_id); - } - - if( !is_null($context->build_id) && $context->build_id > 0) - { - $filters['build_id'] = $context->build_id; - } - - - // we will compute time for ALL linked and executed test cases, - // BUT USING ONLY TIME SPEND for LATEST executed TCVERSION - $options = array('addExecInfo' => true); - $executed = $this->getLTCVNewGeneration($context->tplan_id,$filters,$options); - - if( ($status_ok = !is_null($executed)) ) - { - $tc2loop = array_keys($executed); - foreach($tc2loop as $tcase_id) - { - $p2loop = array_keys($executed[$tcase_id]); - foreach($p2loop as $platf_id) - { - $targetSet[$platf_id][]=array('id' => $executed[$tcase_id][$platf_id]['exec_id'], - 'duration' => $executed[$tcase_id][$platf_id]['execution_duration']); - } - } - } - } - else - { - // If user has passed in a set of exec id, we assume that - // he has make a good work, i.e. if he/she wanted just analize - // executions for just a PLATFORM he/she has filtered BEFORE - // passing in input to this method the item set. - // Then we will IGNORE value of argument platformID to avoid - // run a second (and probably useless query). - // We will use platformID JUST as index for output result - if( is_null($context->platform_id) ) - { - throw new Exception(__FUNCTION__ . ' When you pass $execIDSet an YOU NEED TO PROVIDE a platform ID'); - } - $targetSet[$context->platform_id] = $this->getExecutionDurationForSet($execIDSet); - } - - foreach($targetSet as $platfID => $itemSet) - { - $total_time['platform'][$platfID]['minutes'] = 0; - $total_time['platform'][$platfID]['tcase_qty'] = count($itemSet); - foreach($itemSet as $dx) - { - if(!is_null($dx['duration'])) - { - $total_time['platform'][$platfID]['minutes'] += $dx['duration']; - } - } - - $total_time['totalMinutes'] += $total_time['platform'][$platfID]['minutes']; - $total_time['totalTestCases'] += $total_time['platform'][$platfID]['tcase_qty']; - } - return $total_time; - } - - - /** - * - */ - function getExecutionTimeFromCF($id,$execIDSet=null,$platformID=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $total_time = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - $targetSet = array(); - $cf_info = $this->cfield_mgr->get_by_name('CF_EXEC_TIME'); - - // CF exists ? - if( ($status_ok=!is_null($cf_info)) ) - { - $cfield_id=key($cf_info); - } - - - if( $status_ok) - { - $getOpt = array('outputFormat' => 'mapAccessByID' , 'addIfNull' => true); - $platformSet = array_keys($this->getPlatforms($id,$getOpt)); - - // ---------------------------------------------------------------------------- - $sql="SELECT SUM(CAST(value AS NUMERIC)) "; - if( DB_TYPE == 'mysql') - { - $sql="SELECT SUM(value) "; - } - else if ( DB_TYPE == 'postgres' || DB_TYPE == 'mssql' ) - { - $sql="SELECT SUM(CAST(value AS NUMERIC)) "; - } - $sql .= " AS SUM_VALUE FROM {$this->tables['cfield_execution_values']} CFEV " . - " WHERE CFEV.field_id={$cfield_id} " . - " AND testplan_id={$id} "; - // ---------------------------------------------------------------------------- - - if( is_null($execIDSet) ) - { - - $filters = null; - if( !is_null($platformID) ) - { - $filters = array('platform_id' => $platformID); - } - - // we will compute time for ALL linked and executed test cases, - // BUT USING ONLY TIME SPEND for LAST executed TCVERSION - // $options = array('only_executed' => true, 'output' => 'mapOfMap'); - $options = array('addExecInfo' => true); - $executed = $this->getLTCVNewGeneration($id,$filters,$options); - if( ($status_ok = !is_null($executed)) ) - { - $tc2loop = array_keys($executed); - foreach($tc2loop as $tcase_id) - { - $p2loop = array_keys($executed[$tcase_id]); - foreach($p2loop as $platf_id) - { - $targetSet[$platf_id][]=$executed[$tcase_id][$platf_id]['exec_id']; - } - } - } - } - else - { - // If user has passed in a set of exec id, we assume that - // he has make a good work, i.e. if he/she wanted just analize - // executions for just a PLATFORM he/she has filtered BEFORE - // passing in input to this method the item set. - // Then we will IGNORE value of argument platformID to avoid - // run a second (and probably useless query). - // We will use platformID JUST as index for output result - - if( is_null($platformID) ) - { - throw new Exception(__FUNCTION__ . ' When you pass $execIDSet an YOU NEED TO PROVIDE a platform ID'); - } - $targetSet[$platformID] = $execIDSet; - } - } - - if($status_ok) - { - // Important NOTICE - // we can found SOME LIMITS on number of elements on IN CLAUSE - // - $estimated = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); - foreach($targetSet as $platfID => $items) - { - $sql2exec = $sql . " AND execution_id IN (" . implode(',',$items) . ")"; - - $dummy = $this->db->fetchOneValue($sql2exec); - $total_time['platform'][$platfID]['minutes'] = is_null($dummy) ? 0 : $dummy; - $total_time['platform'][$platfID]['tcase_qty'] = count($items); - - $total_time['totalMinutes'] += $total_time['platform'][$platfID]['minutes']; - $total_time['totalTestCases'] += $total_time['platform'][$platfID]['tcase_qty']; - } - } - - - - return $total_time; - } - - - - - - - /* - function: get_prev_builds() - - args: id: testplan id - build_id: all builds belonging to choosen testplan, - with id < build_id will be retreived. - [active]: default null -> do not filter on active status - - returns: - - */ - function get_prev_builds($id,$build_id,$active=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT id,testplan_id, name, notes, active, is_open " . - " FROM {$this->tables['builds']} " . - " WHERE testplan_id = {$id} AND id < {$build_id}" ; - - if( !is_null($active) ) - { - $sql .= " AND active=" . intval($active) . " "; - } - - $recordset = $this->db->fetchRowsIntoMap($sql,'id'); - return $recordset; - } - - - /** - * returns set of tcversions that has same execution status - * in every build present on buildSet for selected Platform. - * - * id: testplan id - * buildSet: builds to analise. - * status: status code (can be an array) - * - */ - function get_same_status_for_build_set($id, $buildSet, $status, $platformID=NULL) - { - // On Postgresql - // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, - // but not in the WHERE or HAVING clauses; there you must write out the expression instead. - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $node_types = $this->tree_manager->get_available_node_types(); - $num_exec = count($buildSet); - $build_in = implode(",", $buildSet); - $status_in = implode("',", (array)$status); - - $tcversionPlatformString = ""; - $executionPlatformString = ""; - if($platformid) { - $tcversionPlatformString = "AND T.platform_id=$platformid"; - $executionPlatformString = "AND E.platform_id=$platformid"; - } - - $first_results = null; - if( in_array($this->notRunStatusCode, (array)$status) ) - { - - $sql = " /* $debugMsg */ SELECT distinct T.tcversion_id,E.build_id,NH.parent_id AS tcase_id " . - " FROM {$this->tables['testplan_tcversions']} T " . - " JOIN {$this->tables['nodes_hierarchy']} NH ON T.tcversion_id=NH.id " . - " AND NH.node_type_id={$node_types['testcase_version']} " . - " LEFT OUTER JOIN {$this->tables['executions']} E ON T.tcversion_id = E.tcversion_id " . - " AND T.testplan_id=E.testplan_id AND E.build_id IN ({$build_in}) " . - " WHERE T.testplan_id={$id} AND E.build_id IS NULL "; - - $first_results = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - } - - $sql = " SELECT EE.status,SQ1.tcversion_id, NH.parent_id AS tcase_id, COUNT(EE.status) AS exec_qty " . - " FROM {$this->tables['executions']} EE, {$this->tables['nodes_hierarchy']} NH," . - " (SELECT E.tcversion_id,E.build_id,MAX(E.id) AS last_exec_id " . - " FROM {$this->tables['executions']} E " . - " WHERE E.build_id IN ({$build_in}) " . - " GROUP BY E.tcversion_id,E.build_id) AS SQ1 " . - " WHERE EE.build_id IN ({$build_in}) " . - " AND EE.status IN ('" . $status . "') AND NH.node_type_id={$node_types['testcase_version']} " . - " AND SQ1.last_exec_id=EE.id AND SQ1.tcversion_id=NH.id " . - " GROUP BY status,SQ1.tcversion_id,NH.parent_id" . - " HAVING COUNT(EE.status)= {$num_exec} " ; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - - if (count($first_results)) { - foreach ($first_results as $key => $value) { - $recordset[$key] = $value; - } - } - - return $recordset; - } - - - - /** - * BUGID 2455, BUGID 3026 - * find all builds for which a testcase has not been executed - * - * @author asimon - * @param integer $id Build ID - * @param array $buildSet build set to check - * @return array $new_set set of builds which match the search criterium - * @internal revisions - * 20101215 - asimon - BUGID 4023: correct filtering also with platforms - */ - function get_not_run_for_any_build($id, $buildSet, $platformid=NULL) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $node_types=$this->tree_manager->get_available_node_types(); - - $results = array(); - - $tcversionPlatformString = ""; - $executionPlatformString = ""; - if($platformid) { - $tcversionPlatformString = "AND T.platform_id=$platformid"; - $executionPlatformString = "AND E.platform_id=$platformid"; - } - - foreach ($buildSet as $build) { - $sql = "/* $debugMsg */ SELECT distinct T.tcversion_id, E.build_id, E.status, NH.parent_id AS tcase_id " . - " FROM {$this->tables['testplan_tcversions']} T " . - " JOIN {$this->tables['nodes_hierarchy']} NH ON T.tcversion_id=NH.id AND NH.node_type_id=4 " . - " LEFT OUTER JOIN {$this->tables['executions']} E ON T.tcversion_id = E.tcversion_id " . - " AND T.testplan_id=E.testplan_id AND E.build_id=$build $executionPlatformString" . - " WHERE T.testplan_id={$id} AND E.status IS NULL $tcversionPlatformString"; - $results[] = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - } - - $recordset = array(); - foreach ($results as $result) - { - if (!is_null($result) && (is_array($result)) ) //BUGID 3806 - { - $recordset = array_merge_recursive($recordset, $result); - } - } - $new_set = array(); - foreach ($recordset as $key => $val) { - $new_set[$val['tcase_id']] = $val; - } - - return $new_set; - } - - - /** - * link platforms to a new Test Plan - * - * @param int $source_id original Test Plan id - * @param int $target_id new Test Plan id - * @param array $mappings: key source platform id, target platform id - * USED when copy is done to a test plan that BELONGS to - * another Test Project. - */ - private function copy_platforms_links($source_id, $target_id, $mappings = null) - { - $sourceLinks = $this->platform_mgr->getLinkedToTestplanAsMap($source_id); - if( !is_null($sourceLinks) ) - { - $sourceLinks = array_keys($sourceLinks); - if( !is_null($mappings) ) - { - foreach($sourceLinks as $key => $value) - { - $sourceLinks[$key] = $mappings[$value]; - } - } - $this->platform_mgr->linkToTestplan($sourceLinks,$target_id); - } - } - - /** - * link attachments to a new Test Plan - * - * @param int $source_id original Test Plan id - * @param int $target_id new Test Plan id - */ - private function copy_attachments($source_id, $target_id) - { - $this->attachmentRepository->copyAttachments($source_id,$target_id,$this->attachmentTableName); - } - - /** - * - * - * outputFormat: - * 'array', - * 'map', - * 'mapAccessByID' => map access key: id - * 'mapAccessByName' => map access key: name - * - */ - function getPlatforms($id,$options=null) { - $my['options'] = array('outputFormat' => 'array', 'outputDetails' => 'full', 'addIfNull' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - switch($my['options']['outputFormat']) { - case 'map': - $platforms = $this->platform_mgr->getLinkedToTestplanAsMap($id); - break; - - default: - $opt = array('outputFormat' => $my['options']['outputFormat']); - $platforms = $this->platform_mgr->getLinkedToTestplan($id,$opt); - break; - } - - if( !is_null($platforms) ) { - switch($my['options']['outputDetails']) { - case 'name': - foreach($platforms as $id => $elem) { - $platforms[$id] = $elem['name']; - } - break; - - default: - break; - } - } else if( $my['options']['addIfNull'] ) { - $platforms = array( 0 => ''); - } - return $platforms; - } - - /** - * Logic to determine if platforms should be visible for a given testplan. - * @return bool true if the testplan has one or more linked platforms; - * otherwise false. - */ - function hasLinkedPlatforms($id) { - return $this->platform_mgr->platformsActiveForTestplan($id); - } - - - - /** - * changes platform id on a test plan linked test case versions for - * a target platform. - * Corresponding executions information is also updated - * - * @param id: test plan id - * @param from: plaftorm id to update (used as filter criteria). - * @param to: new plaftorm id value - * @param tcversionSet: default null, can be array with tcversion id - * (used as filter criteria). - * - * - */ - function changeLinkedTCVersionsPlatform($id,$from,$to,$tcversionSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sqlFilter = ''; - if( !is_null($tcversionSet) ) - { - $sqlFilter = " AND tcversion_id IN (" . implode(',',(array)$tcversionSet) . " ) "; - } - $whereClause = " WHERE testplan_id = {$id} AND platform_id = {$from} {$sqlFilter}"; - - $sqlStm = array(); - $sqlStm[] = "/* {$debugMsg} */ " . - " UPDATE {$this->tables['testplan_tcversions']} " . - " SET platform_id = {$to} " . $whereClause; - - $sqlStm[] = "/* {$debugMsg} */" . - " UPDATE {$this->tables['executions']} " . - " SET platform_id = {$to} " . $whereClause; - - foreach($sqlStm as $sql) - { - $this->db->exec_query($sql); - } - } - - /** - * - * @param id: test plan id - * @param platformSet: default null, used as filter criteria. - * @return map: key platform id, values count,platform_id - */ - public function countLinkedTCVersionsByPlatform($id,$platformSet=null) - { - $sqlFilter = ''; - if( !is_null($platformSet) ) - { - $sqlFilter = " AND platform_id IN (" . implode(',',(array)$platformSet). ") "; - } - $sql = " SELECT COUNT(testplan_id) AS qty,platform_id " . - " FROM {$this->tables['testplan_tcversions']} " . - " WHERE testplan_id={$id} {$sqlFilter} " . - " GROUP BY platform_id "; - $rs = $this->db->fetchRowsIntoMap($sql,'platform_id'); - return $rs; - } - - - - /** - * - * - */ - public function getStatusForReports() - { - // This will be used to create dynamically counters if user add new status - foreach( $this->resultsCfg['status_label_for_exec_ui'] as $tc_status_verbose => $label) - { - $code_verbose[$this->resultsCfg['status_code'][$tc_status_verbose]] = $tc_status_verbose; - } - if( !isset($this->resultsCfg['status_label_for_exec_ui']['not_run']) ) - { - $code_verbose[$this->resultsCfg['status_code']['not_run']] = 'not_run'; - } - return $code_verbose; - } - - - - /** - * getTestCaseSiblings() - * - * @internal revisions - */ - function getTestCaseSiblings($id,$tcversion_id,$platform_id,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('assigned_to' => null); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = " SELECT NHTSET.name as testcase_name,NHTSET.id AS testcase_id , NHTCVSET.id AS tcversion_id," . - " NHTC.parent_id AS testsuite_id, " . - " TPTCVX.id AS feature_id, TPTCVX.node_order, TCV.tc_external_id " . - " from {$this->tables['testplan_tcversions']} TPTCVMAIN " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCVMAIN.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTSET ON NHTSET.parent_id = NHTC.parent_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCVSET ON NHTCVSET.parent_id = NHTSET.id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCVSET.id " . - " JOIN {$this->tables['testplan_tcversions']} TPTCVX " . - " ON TPTCVX.tcversion_id = NHTCVSET.id " . - " AND TPTCVX.testplan_id = TPTCVMAIN.testplan_id " . - " AND TPTCVX.platform_id = TPTCVMAIN.platform_id "; - - if( !is_null($my['opt']['assigned_to']) ) - { - $user_id = intval($my['opt']['assigned_to']['user_id']); - $build_id = intval($my['opt']['assigned_to']['build_id']); - - $addJoin = " /* Analise user assignment to get sibling */ " . - " JOIN {$this->tables['user_assignments']} UAMAIN " . - " ON UAMAIN.feature_id = TPTCVMAIN.id " . - " AND UAMAIN.build_id = " . $build_id . - " AND UAMAIN.user_id = " . $user_id . - " AND UAMAIN.type = {$this->execTaskCode} " . - " JOIN {$this->tables['user_assignments']} UAX " . - " ON UAX.feature_id = TPTCVX.id " . - " AND UAX.build_id = " . $build_id . - " AND UAX.user_id = " . $user_id . - " AND UAX.type = {$this->execTaskCode} "; - $sql .= $addJoin; - - } - - $sql .= " WHERE TPTCVMAIN.testplan_id = {$id} AND TPTCVMAIN.tcversion_id = {$tcversion_id} " . - " AND TPTCVMAIN.platform_id = {$platform_id} " . - " ORDER BY node_order,tc_external_id "; - - // " ORDER BY node_order,external_id,testcase_name "; - - $siblings = $this->db->fetchRowsIntoMap($sql,'tcversion_id'); - return $siblings; - } - - - /** - * getTestCaseNextSibling() - * - * @used-by execSetResults.php - * - */ - function getTestCaseNextSibling($id,$tcversion_id,$platform_id,$opt=null) - { - $my['opt'] = array('move' => 'forward', 'scope' => 'local'); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - - $sibling = null; - switch($my['opt']['scope']) - { - case 'world': - $tptcv = $this->tables['testplan_tcversions']; - - $subq = " SELECT node_order FROM {$this->tables['testplan_tcversions']} TX " . - " WHERE TX.testplan_id = {$id} AND " . - " TX.tcversion_id = {$tcversion_id} "; - - if( $platform_id > 0) - { - $subq .= " AND TX.platform_id = {$platform_id} "; - } - $sql= " SELECT tcversion_id,node_order " . - " FROM {$tptcv} TZ " . - " WHERE TZ.testplan_id = {$id} AND " . - " TZ.tcversion_id <> {$tcversion_id} "; - if( $platform_id > 0) - { - $sql .= " AND TZ.platform_id = {$platform_id} "; - } - - $sql .= " ORDER BY TZ.node_order >= ($subq) "; - break; - - case 'local': - default: - $sib = $this->getTestCaseSiblings($id,$tcversion_id,$platform_id,$my['opt']); - break; - } - $tcversionSet = array_keys($sib); - $elemQty = count($tcversionSet); - $dummy = array_flip($tcversionSet); - - $pos = $dummy[$tcversion_id]; - switch($my['opt']['move']) - { - case 'backward': - $pos--; - $pos = $pos < 0 ? 0 : $pos; - break; - - case 'forward': - default: - $pos++; - break; - } - - $sibling_tcversion = $pos < $elemQty ? $tcversionSet[$pos] : 0; - if( $sibling_tcversion > 0 ) - { - $sibling = array('tcase_id' => $sib[$sibling_tcversion]['testcase_id'], - 'tcversion_id' => $sibling_tcversion); - } - return $sibling; - } - - /** - * Convert a given urgency and importance to a priority level using - * threshold values in $tlCfg->priority_levels. - * - * @param mixed $urgency Urgency of the testcase. - * If this is the only parameter given then interpret it as - * $urgency*$importance. - * @param mixed $importance Importance of the testcase. (Optional) - * - * @return int HIGH, MEDIUM or LOW - */ - public function urgencyImportanceToPriorityLevel($urgency, $importance=null) - { - $urgencyImportance = intval($urgency) * (is_null($importance) ? 1 : intval($importance)) ; - return priority_to_level($urgencyImportance); - } - - - /** - * create XML string with following structure - * - * - * - * - * - * - * - * - * - * - * ... - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ... - * - * - * - * - * - */ - function exportLinkedItemsToXML($id) - { - $item_info = $this->get_by_id($id); - - // Linked platforms - $xml_root = "{{XMLCODE}}\n"; - - // ||yyy||-> tags, {{xxx}} -> attribute - // tags and attributes receive different treatment on exportDataToXML() - // - // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl - // - $xml_template = "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . ""; - - $xml_mapping = null; - $xml_mapping = array("||PLATFORMNAME||" => "platform_name", "||PLATFORMID||" => 'id'); - - $mm = (array)$this->platform_mgr->getLinkedToTestplanAsMap($id); - $loop2do = count($mm); - if( $loop2do > 0 ) - { - $items2loop = array_keys($mm); - foreach($items2loop as $itemkey) - { - $mm[$itemkey] = array('platform_name' => $mm[$itemkey], 'id' => $itemkey); - } - } - $linked_platforms = exportDataToXML($mm,$xml_root,$xml_template,$xml_mapping,('noXMLHeader'=='noXMLHeader')); - - // Linked test cases - $xml_root = "\n{{XMLCODE}}\n"; - $xml_template = "\n\t" . - "" . "\n" . - "\t\t" . "" . "\n" . - "\t\t\t" . "" . "\n" . - "\t\t" . "" . "\n" . - "\t\t" . "" . "\n" . - "\t\t\t" . "\n" . - "\t\t\t" . "\n" . - "\t\t\t" . "\n" . - "\t\t\t" . "\n" . - "\t\t" . "" . "\n" . - "" . "\n" . - - $xml_mapping = null; - $xml_mapping = array("||PLATFORMNAME||" => "platform_name","||EXTERNALID||" => "external_id", - "||NAME||" => "name","||VERSION||" => "version", - "||EXECUTION_ORDER||" => "execution_order"); - - $mm = $this->getLinkedStaticView($id,null,array('output' => 'array')); - $linked_testcases = exportDataToXML($mm,$xml_root,$xml_template,$xml_mapping,('noXMLHeader'=='noXMLHeader')); - - - $item_info['linked_platforms'] = $linked_platforms; - $item_info['linked_testcases'] = $linked_testcases; - $xml_root = "\n\t{{XMLCODE}}\n\t"; - $xml_template = "\n\t\t" . "" . "\n" . - "\t\t||LINKED_PLATFORMS||\n" . "\t\t||LINKED_TESTCASES||\n"; - - $xml_mapping = null; - $xml_mapping = array("||TESTPLANNAME||" => "name","||LINKED_PLATFORMS||" => "linked_platforms", - "||LINKED_TESTCASES||" => "linked_testcases"); - - $xml = exportDataToXML(array($item_info),$xml_root,$xml_template,$xml_mapping); - - return $xml; - } - - - - - /** - * create XML string with following structure - * - * - * - * @param mixed context: map with following keys - * platform_id: MANDATORY - * build_id: OPTIONAL - * tproject_id: OPTIONAL - */ - function exportTestPlanDataToXML($id,$context,$optExport = array()) - { - $platform_id = $context['platform_id']; - if( !isset($context['tproject_id']) || is_null($context['tproject_id']) ) - { - $dummy = $this->tree_manager->get_node_hierarchy_info($id); - $context['tproject_id'] = $dummy['parent_id']; - } - $context['tproject_id'] = intval($context['tproject_id']); - - $xmlTC = null; - - - // CRITIC - this has to be firt population of item_info. - // Other processes adds info to this map. - $item_info = $this->get_by_id($id); - - // Need to get family - $nt2exclude = array('testplan' => 'exclude_me','requirement_spec'=> 'exclude_me', - 'requirement'=> 'exclude_me'); - $nt2exclude_children = array('testcase' => 'exclude_my_children', - 'requirement_spec'=> 'exclude_my_children'); - - $my = array(); - - // this can be a litte weird but ... - // when - // 'order_cfg' => array("type" =>'exec_order' - // additional info test plan id, and platform id are used to get - // a filtered view of tree. - // - $order_cfg = array("type" =>'exec_order',"tplan_id" => $id); - if( $context['platform_id'] > 0 ) - { - $order_cfg['platform_id'] = $context['platform_id']; - } - $my['options']=array('recursive' => true, 'order_cfg' => $order_cfg, - 'remove_empty_nodes_of_type' => $this->tree_manager->node_descr_id['testsuite']); - $my['filters'] = array('exclude_node_types' => $nt2exclude,'exclude_children_of' => $nt2exclude_children); - $tplan_spec = $this->tree_manager->get_subtree($context['tproject_id'],$my['filters'],$my['options']); - - // ----------------------------------------------------------------------------------------------------- - // Generate test project info - $tproject_mgr = new testproject($this->db); - $tproject_info = $tproject_mgr->get_by_id($context['tproject_id']); - - // ||yyy||-> tags, {{xxx}} -> attribute - // tags and attributes receive different treatment on exportDataToXML() - // - // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl - // - $xml_template = "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . ""; - - $xml_root = "{{XMLCODE}}"; - $xml_mapping = null; - $xml_mapping = array("||TESTPROJECTNAME||" => "name", "||TESTPROJECTPREFIX||" => "prefix","||TESTPROJECTID||" => 'id'); - $mm = array(); - $mm[$context['tproject_id']] = array('name' => $tproject_info['name'],'prefix' => $tproject_info['prefix'], - 'id' => $context['tproject_id']); - $item_info['testproject'] = exportDataToXML($mm,$xml_root,$xml_template,$xml_mapping,('noXMLHeader'=='noXMLHeader')); - // ----------------------------------------------------------------------------------------------------- - - // ----------------------------------------------------------------------------------------------------- - // get target platform (if exists) - $target_platform = ''; - if( $context['platform_id'] > 0) - { - $info = $this->platform_mgr->getByID($context['platform_id']); - // ||yyy||-> tags, {{xxx}} -> attribute - // tags and attributes receive different treatment on exportDataToXML() - // - // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl - // - $xml_template = "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . ""; - - $xml_root = "{{XMLCODE}}"; - $xml_mapping = null; - $xml_mapping = array("||PLATFORMNAME||" => "platform_name", "||PLATFORMID||" => 'id'); - - $mm = array(); - $mm[$context['platform_id']] = array('platform_name' => $info['name'], 'id' => $context['platform_id']); - $item_info['target_platform'] = exportDataToXML($mm,$xml_root,$xml_template,$xml_mapping, - ('noXMLHeader'=='noXMLHeader')); - $target_platform = "\t\t||TARGET_PLATFORM||\n"; - } - // ----------------------------------------------------------------------------------------------------- - - // ----------------------------------------------------------------------------------------------------- - // get Build info (if possible) - $target_build = ''; - if( isset($context['build_id']) && $context['build_id'] > 0) - { - $dummy = $this->get_builds($id); - $info = $dummy[$context['build_id']]; - - // ||yyy||-> tags, {{xxx}} -> attribute - // tags and attributes receive different treatment on exportDataToXML() - // - // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl - // - $xml_template = "\n\t" . - "" . - "\t\t" . "" . - "\t\t" . "" . - "\n\t" . ""; - - $xml_root = "{{XMLCODE}}"; - $xml_mapping = null; - $xml_mapping = array("||BUILDNAME||" => "name", "||BUILDID||" => 'id'); - - $mm = array(); - $mm[$context['build_id']] = array('name' => $info['name'], 'id' => $context['build_id']); - $item_info['target_build'] = exportDataToXML($mm,$xml_root,$xml_template,$xml_mapping, - ('noXMLHeader'=='noXMLHeader')); - $target_build = "\t\t||TARGET_BUILD||\n"; - } - // ----------------------------------------------------------------------------------------------------- - - // ----------------------------------------------------------------------------------------------------- - // get test plan contents (test suites and test cases) - $item_info['testsuites'] = null; - if( !is_null($tplan_spec) && isset($tplan_spec['childNodes']) && ($loop2do = count($tplan_spec['childNodes'])) > 0) - { - $item_info['testsuites'] = '' . - $this->exportTestSuiteDataToXML($tplan_spec,$context['tproject_id'],$id, - $context['platform_id'],$context['build_id']) . - ''; - } - - $xml_root = "\n\t{{XMLCODE}}\n\t"; - $xml_template = "\n\t\t" . "" . "\n" . - "\t\t||TESTPROJECT||\n" . $target_platform . $target_build . "\t\t||TESTSUITES||\n"; - - $xml_mapping = null; - $xml_mapping = array("||TESTPLANNAME||" => "name", "||TESTPROJECT||" => "testproject", - "||TARGET_PLATFORM||" => "target_platform","||TARGET_BUILD||" => "target_build", - "||TESTSUITES||" => "testsuites"); - - $zorba = exportDataToXML(array($item_info),$xml_root,$xml_template,$xml_mapping); - - return $zorba; - } - - - /** - * - * - */ - private function exportTestSuiteDataToXML($container,$tproject_id,$tplan_id,$platform_id,$build_id) - { - static $keywordMgr; - static $getLastVersionOpt = array('output' => 'minimun'); - static $tcaseMgr; - static $tsuiteMgr; - static $tcaseExportOptions; - static $linkedItems; - - if(is_null($keywordMgr)) - { - $tcaseExportOptions = array('CFIELDS' => true, 'KEYWORDS' => true, 'EXEC_ORDER' => 0); - $keywordMgr = new tlKeyword(); - $tsuiteMgr = new testsuite($this->db); - $linkedItems = $this->getLinkedItems($tplan_id); - } - - $xmlTC = null; - $cfXML = null; - $kwXML = null; - - if( isset($container['id']) ) - { - $kwMap = $tsuiteMgr->getKeywords($container['id']); - if ($kwMap) - { - $kwXML = "" . $keywordMgr->toXMLString($kwMap,true) . ""; - } - - $cfMap = (array)$tsuiteMgr->get_linked_cfields_at_design($container['id'],null,null,$tproject_id); - if( count($cfMap) > 0 ) - { - $cfXML = $this->cfield_mgr->exportValueAsXML($cfMap); - } - - $tsuiteData = $tsuiteMgr->get_by_id($container['id']); - $xmlTC = "\n\t' . - "\n\t\t" . - "\n\t\t
    " . - "\n\t\t{$kwXML}{$cfXML}
    "; - } - $childNodes = isset($container['childNodes']) ? $container['childNodes'] : null ; - if( !is_null($childNodes) ) - { - $loop_qty=sizeof($childNodes); - for($idx = 0;$idx < $loop_qty;$idx++) - { - $cNode = $childNodes[$idx]; - switch($cNode['node_table']) - { - case 'testsuites': - $xmlTC .= $this->exportTestSuiteDataToXML($cNode,$tproject_id,$tplan_id,$platform_id,$build_id); - break; - - case 'testcases': - if( is_null($tcaseMgr) ) - { - $tcaseMgr = new testcase($this->db); - } - // testcase::LATEST_VERSION, - $tcaseExportOptions['EXEC_ORDER'] = $linkedItems[$cNode['id']][$platform_id]['node_order']; - - $filter_lv = array( 'exec_status' => 'ALL', 'active_status' => 'ALL','tplan_id' => $tplan_id, 'platform_id' => $platform_id ); - $output_lv = array( 'output' => 'simple' ); - // get tc versions linked in current testplan for current platform - $info = $tcaseMgr->get_linked_versions($cNode['id'],$filter_lv,$output_lv); - if( !is_null($info) ) - { - $tcversID = key($info); - } - - // get users assigned to tc version in current testplan for the current build - $versionAssignInfo = $tcaseMgr->get_version_exec_assignment($tcversID, $tplan_id, $build_id ); - $userList = array(); - // extract user names - if(!is_null($versionAssignInfo)) - { - foreach($versionAssignInfo[$tcversID][$platform_id] as $vaInfo) - { - $assignedTesterId = intval($vaInfo['user_id']); - if($assignedTesterId) - { - $user = tlUser::getByID($this->db,$assignedTesterId); - if ($user) - { - $userList[] = $user->getDisplayName(); - } - } - } - } - (count($userList) > 0) ? $tcaseExportOptions['ASSIGNED_USER'] = $userList : $tcaseExportOptions['ASSIGNED_USER'] = null; - - $xmlTC .= $tcaseMgr->exportTestCaseDataToXML($cNode['id'],$cNode['tcversion_id'], - $tproject_id,testcase::NOXMLHEADER, - $tcaseExportOptions); - break; - } - } - } - - if( isset($container['id']) ) - { - $xmlTC .= "
    "; - } - return $xmlTC; - } - - - - /** - * - */ - function getFeatureAssignments($tplan_id,$filters=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ "; - - $my['filters'] = array('build' => null, 'tcversion' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $sql .= " SELECT COALESCE(UA.user_id,-1) AS user_id, " . - " TPTCV.id AS feature_id, B.id AS build_id, TPTCV.platform_id " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - - " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id AND UA.build_id = B.id " . - " WHERE TPTCV.testplan_id={$tplan_id} "; - - if(!is_null($my['filters']['build'])) - { - $sql .= " AND B.id IN (" . implode(',',(array)$my['filters']['build']) . ") "; - } - if(!is_null($my['filters']['tcversion'])) - { - $sql .= " AND TPTCV.tcversion_id IN (" . implode(',',(array)$my['filters']['tcversion']) . ") "; - } - - $rs = $this->db->fetchMapRowsIntoMap($sql,'feature_id','build_id'); - return $rs; - } // end function - - - - /** - * getSkeleton - * - * get structure with Test suites and Test Cases - * Filters that act on test cases work on attributes that are common to all - * test cases versions: test case name - * - * Development Note: - * Due to the tree structure is not so easy to try to do as much as filter as - * possibile using SQL. - * - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getSkeleton($id,$tprojectID,$filters=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $items = array(); - $my['options'] = array('recursive' => false, 'exclude_testcases' => false, - 'remove_empty_branches' => false); - - $my['filters'] = array('exclude_node_types' => $this->nt2exclude, - 'exclude_children_of' => $this->nt2exclude_children, - 'exclude_branches' => null, - 'testcase_name' => null,'testcase_id' => null, - 'execution_type' => null, 'platform_id' => null, - 'additionalWhereClause' => null); - - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - if( $my['options']['exclude_testcases'] ) - { - $my['filters']['exclude_node_types']['testcase']='exclude me'; - } - - // transform some of our options/filters on something the 'worker' will understand - // when user has request filter by test case name, we do not want to display empty branches - - // If we have choose any type of filter, we need to force remove empty test suites - // - if( !is_null($my['filters']['testcase_name']) || !is_null($my['filters']['testcase_id']) || - !is_null($my['filters']['execution_type']) || !is_null($my['filters']['exclude_branches']) || - !is_null($my['filters']['platform_id']) || $my['options']['remove_empty_branches'] ) - { - $my['options']['remove_empty_nodes_of_type'] = 'testsuite'; - } - - $method2call = $my['options']['recursive'] ? '_get_subtree_rec' : '_get_subtree'; - $tcaseSet = array(); - if($my['options']['recursive']) - { - $qnum = $this->$method2call($id,$tprojectID,$items,$tcaseSet, - $my['filters'],$my['options']); - } - else - { - $qnum = $this->$method2call($id,$tprojectID,$items,$my['filters'],$my['options']); - } - return array($items,$tcaseSet); - } - - - - /** - * - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function _get_subtree_rec($tplan_id,$node_id,&$pnode,&$itemSet,$filters = null, $options = null) - { - static $qnum; - static $my; - static $exclude_branches; - static $exclude_children_of; - static $node_types; - static $tcaseFilter; - static $tcversionFilter; - static $pltaformFilter; - - static $childFilterOn; - static $staticSql; - static $debugMsg; - - if (!$my) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $qnum=0; - $node_types = array_flip($this->tree_manager->get_available_node_types()); - $my['filters'] = array('exclude_children_of' => null,'exclude_branches' => null, - 'additionalWhereClause' => '', 'testcase_name' => null, - 'platform_id' => null, - 'testcase_id' => null,'active_testcase' => false); - - $my['options'] = array('remove_empty_nodes_of_type' => null); - - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - $exclude_branches = $my['filters']['exclude_branches']; - $exclude_children_of = $my['filters']['exclude_children_of']; - - - $tcaseFilter['name'] = !is_null($my['filters']['testcase_name']); - $tcaseFilter['id'] = !is_null($my['filters']['testcase_id']); - - $tcaseFilter['is_active'] = !is_null($my['filters']['active_testcase']) && $my['filters']['active_testcase']; - $tcaseFilter['enabled'] = $tcaseFilter['name'] || $tcaseFilter['id'] || $tcaseFilter['is_active']; - - - $tcversionFilter['execution_type'] = !is_null($my['filters']['execution_type']); - $tcversionFilter['enabled'] = $tcversionFilter['execution_type']; - - $childFilterOn = $tcaseFilter['enabled'] || $tcversionFilter['enabled']; - - - - if( !is_null($my['options']['remove_empty_nodes_of_type']) ) - { - // this way I can manage code or description - if( !is_numeric($my['options']['remove_empty_nodes_of_type']) ) - { - $my['options']['remove_empty_nodes_of_type'] = - $this->tree_manager->node_descr_id[$my['options']['remove_empty_nodes_of_type']]; - } - } - - - $platformFilter = ""; - if( !is_null($my['filters']['platform_id']) && $my['filters']['platform_id'] > 0 ) - { - $platformFilter = " AND T.platform_id = " . intval($my['filters']['platform_id']) ; - } - - // Create invariant sql sentences - $staticSql[0] = " /* $debugMsg - Get ONLY TestSuites */ " . - " SELECT NHTS.node_order AS spec_order," . - " NHTS.node_order AS node_order, NHTS.id, NHTS.parent_id," . - " NHTS.name, NHTS.node_type_id, 0 AS tcversion_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTS" . - " WHERE NHTS.node_type_id = {$this->tree_manager->node_descr_id['testsuite']} " . - " AND NHTS.parent_id = "; - - $staticSql[1] = " /* $debugMsg - Get ONLY Test Cases with version linked to (testplan,platform) */ " . - " SELECT NHTC.node_order AS spec_order, " . - " TPTCV.node_order AS node_order, NHTC.id, NHTC.parent_id, " . - " NHTC.name, NHTC.node_type_id, TPTCV.tcversion_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTC " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.parent_id = NHTC.id " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id " . - " WHERE NHTC.node_type_id = {$this->tree_manager->node_descr_id['testcase']} " . - " AND TPTCV.testplan_id = " . intval($tplan_id) . " {$platformFilter} " . - " AND NHTC.parent_id = "; - - } // End init static area - - $target = intval($node_id); - $sql = $staticSql[0] . $target . " UNION " . $staticSql[1] . $target; - - if( $tcaseFilter['enabled'] ) - { - foreach($tcaseFilter as $key => $apply) - { - if( $apply ) - { - switch($key) - { - case 'name': - $sql .= " AND NHTC.name LIKE '%{$my['filters']['testcase_name']}%' "; - break; - - case 'id': - $sql .= " AND NHTC.id = {$my['filters']['testcase_id']} "; - break; - } - } - } - } - - $sql .= " ORDER BY node_order,id"; - - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - if( null == $rs || count($rs) == 0 ) { - return $qnum; - } - - - foreach($rs as $row) { - if(!isset($exclude_branches[$row['id']])) { - $node = $row + - array('node_type' => $this->tree_manager->node_types[$row['node_type_id']], - 'node_table' => $this->tree_manager->node_tables_by['id'][$row['node_type_id']]); - $node['childNodes'] = null; - - if($node['node_table'] == 'testcases') { - $node['leaf'] = true; - $node['external_id'] = ''; - $itemSet['nindex'][] = $node['id']; - } - - - // why we use exclude_children_of ? - // 1. Sometimes we don't want the children if the parent is a testcase, - // due to the version management - // - if(!isset($exclude_children_of[$node_types[$row['node_type_id']]])) { - // Keep walking (Johny Walker Whisky) - $this->_get_subtree_rec($tplan_id,$row['id'],$node,$itemSet,$my['filters'],$my['options']); - } - - - // Have added this logic, because when export test plan will be developed - // having a test spec tree where test suites that do not contribute to test plan - // are pruned/removed is very important, to avoid additional processing - // - // If node has no childNodes, we check if this kind of node without children - // can be removed. - // - $doRemove = is_null($node['childNodes']) && - ($node['node_type_id'] == $my['options']['remove_empty_nodes_of_type']); - if(!$doRemove) { - $pnode['childNodes'][] = $node; - } - } // if(!isset($exclude_branches[$rowID])) - } //while - return $qnum; - } - - - /** - * - * - */ - function getNotRunAllBuildsForPlatform($id,$platformID,$buildSet=null) { - // On Postgresql - // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, - // but not in the WHERE or HAVING clauses; there you must write out the expression instead. - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - $sql = "/* $debugMsg */ " . - " SELECT count(0) AS COUNTER ,NHTCV.parent_id AS tcase_id " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . - " NHTCV.id = TPTCV.tcversion_id " . - " LEFT OUTER JOIN {$this->tables['executions']} E ON " . - " E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.build_id = B.id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id " . $safe_id['platform'] . - " AND E.status IS NULL "; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $sql .= $groupBy . - " HAVING COUNT(0) = " . intval($buildsCfg['count']) ; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - /** - * - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsNotRunForBuildAndPlatform($id,$platformID,$buildID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - // list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - $safe_id['tplan'] = intval($id); - $safe_id['platform'] = intval($platformID); - $safe_id['build'] = intval($buildID); - - $sql = "/* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id, E.status, B.id AS build_id " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - - " /* Needed to get TEST CASE ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Need to Get Execution Info on REQUESTED build set */ " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = B.id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id = " . $safe_id['platform'] . - " AND B.id = " . $safe_id['build'] . - " AND E.status IS NULL "; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - /** - * - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getNotRunAtLeastOneBuildForPlatform($id,$platformID,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - $sql = "/* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id, E.status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Needed to get TEST CASE ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Need to Get Execution Info on REQUESTED build set */ " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.build_id = B.id " . - " AND E.build_in IN ({$buildsCfg['inClause']}) " . - - " WHERE TPTCV.testplan_id = $id " . - " AND TPTCV.platform_id={$platformID} " . - " AND E.build_in IN ({$buildsCfg['inClause']}) " . - " AND E.status IS NULL "; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - /** - * returns recordset with test cases that has requested status - * (only statuses that are written to DB => this does not work for not run) - * for LAST EXECUTION on build Set provided, for a platform. - * - * FULL means that we have to have SAME STATUS on all builds present on set. - * If build set is NOT PROVIDED, we will use ALL ACTIVE BUILDS - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsSingleStatusFull($id,$platformID,$status,$buildSet=null) - { - // On Postgresql - // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, - // but not in the WHERE or HAVING clauses; there you must write out the expression instead. - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - $sql = " /* $debugMsg */ " . - " /* Count() to be used on HAVING */ " . - " SELECT COUNT(0) AS COUNTER ,NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution by BUILD and PLATFORM */ " . - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.build_id = B.id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . - " AND E.tcversion_id = LEBBP.tcversion_id " . - " AND E.testplan_id = LEBBP.testplan_id " . - " AND E.platform_id = LEBBP.platform_id " . - " AND E.build_id = LEBBP.build_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id=" . $safe_id['platform'] . - " AND E.build_id IN ({$buildsCfg['inClause']}) " . - " AND E.status ='" .$this->db->prepare_string($status) . "'"; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $sql .= $groupBy . - " HAVING COUNT(0) = " . intval($buildsCfg['count']) ; - - unset($safe_id,$buildsCfg,$sqlLEBBP); - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - /** - * getHitsNotRunFullOnPlatform($id,$platformID,$buildSet) - * - * returns recordset with: - * test cases with NOT RUN status on ALL builds in build set (full), for a platform. - * - * If build set is null - * test cases with NOT RUN status on ALL ACTIVE builds (full), for a platform. - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsNotRunFullOnPlatform($id,$platformID,$buildSet=null) - { - // On Postgresql - // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, - // but not in the WHERE or HAVING clauses; there you must write out the expression instead. - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - $sql = " /* $debugMsg */ " . - " /* Count() to be used on HAVING */ " . - " SELECT COUNT(0) AS COUNTER ,NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . - " NHTCV.id = TPTCV.tcversion_id " . - - " LEFT OUTER JOIN {$this->tables['executions']} E ON " . - " E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = B.id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id = " . $safe_id['platform'] . - " AND E.status IS NULL "; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $sql .= $groupBy . - " HAVING COUNT(0) = " . intval($buildsCfg['count']) ; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - /** - * getHitsStatusSetFullOnPlatform($id,$platformID,$statusSet,$buildQty=0) - * - * returns recordset with: - * test cases that has at least ONE of requested status - * ON LAST EXECUTION ON ALL builds in set (full) , for a platform - * - * If build set is not provided, thena analisys will be done on - * ALL ACTIVE BUILDS - * - * - * IMPORTANT / CRITIC: This has NOT BE USED FOR NOT RUN, - * there is an special method for NOT RUN status. - * - * Example: - * - * Test Plan: PLAN B - * Builds: B1,B2,B3 - * Test Cases: TC-100, TC-200,TC-300 - * - * Test Case - Build - LAST Execution status - * TC-100 B1 Passed - * TC-100 B2 FAILED - * TC-100 B3 Not Run - * - * TC-200 B1 FAILED - * TC-200 B2 FAILED - * TC-200 B3 BLOCKED - * - * TC-300 B1 Passed - * TC-300 B2 Passed - * TC-300 B3 BLOCKED - * - * TC-400 B1 FAILED - * TC-400 B2 BLOCKED - * TC-400 B3 FAILED - * - * Request 1: - * Provide test cases with status (LAST EXECUTION) in ('Passed','BLOCKED') - * ON ALL ACTIVE Builds - * - * ANSWER: - * TC-300 - * - * Request 2: - * Provide test cases with status in ('FAILED','BLOCKED') - * ON ALL ACTIVE Builds - * - * ANSWER: - * TC-300, TC-400 - * - * @return - * - * @internal revisions - * @since 1.9.4 - * 20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches - */ - function getHitsStatusSetFullOnPlatform($id,$platformID,$statusSet,$buildSet=null) - { - - // On Postgresql - // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, - // but not in the WHERE or HAVING clauses; there you must write out the expression instead. - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - $dummy = $this->sanitizeExecStatus( (array)$statusSet ); - $statusInClause = implode("','",$dummy); - - // ATTENTION: - // if I've requested (Passed or Blocked) on ALL BUILDS - // Have 2 results for build number. - // - // That logic is wrong when filtering for the SAME STATUS on ALL builds. - // Maybe copy/paste-error on refactoring? - // Example: With 3 builds and filtering for FAILED or BLOCKED on ALL builds - // we have to get 3 hits for each test case to be shown, not six hits. - // $countTarget = intval($buildsCfg['count']) * count($dummy); - $countTarget = intval($buildsCfg['count']); - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $sql = " /* $debugMsg */ " . - " /* Count() to be used on HAVING */ " . - " SELECT COUNT(0) AS COUNTER ,NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution by BUILD and PLATFORM */ " . - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.build_id = B.id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . - " AND E.tcversion_id = LEBBP.tcversion_id " . - " AND E.testplan_id = LEBBP.testplan_id " . - " AND E.platform_id = LEBBP.platform_id " . - " AND E.build_id = LEBBP.build_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id=" . $safe_id['platform'] . - " AND E.build_id IN ({$buildsCfg['inClause']}) " . - " AND E.status IN ('{$statusInClause}')" . - $groupBy . " HAVING COUNT(0) = " . $countTarget ; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - /** - * getHitsNotRunPartialOnPlatform($id,$platformID,buildSet) - * - * returns recordset with: - * test cases with NOT RUN status at LEAST ON ONE off ALL ACTIVE builds (Partial), - * for a platform. - * - * Example: - * - * Test Plan: PLAN B - * Builds: B1,B2,B3 - * Test Cases: TC-100, TC-200,TC-300 - * - * Test Case - Build - LAST Execution status - * TC-100 B1 Passed - * TC-100 B2 FAILED - * TC-100 B3 Not Run => to have this status means THAT HAS NEVER EXECUTED ON B3 - * - * TC-200 B1 FAILED - * TC-200 B2 FAILED - * TC-200 B3 BLOCKED - * - * TC-300 B1 Passed - * TC-300 B2 Passed - * TC-300 B3 BLOCKED - * - * TC-400 B1 FAILED - * TC-400 B2 BLOCKED - * TC-400 B3 FAILED - * - * Request : - * Provide test cases with status 'NOT RUN' - * ON At Least ON OF all ACTIVE Builds - * - * ANSWER: - * TC-100 - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsNotRunPartialOnPlatform($id,$platformID,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - - $sql = " /* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . - " NHTCV.id = TPTCV.tcversion_id " . - - " /* Executions, looking for status NULL (remember NOT RUN is not written on DB) */ " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = B.id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id = " . $safe_id['platform'] . - " AND E.status IS NULL "; - - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - /** - * getHitsStatusSetPartialOnPlatform($id,$platformID,$statusSet,$buildSet) - * - * returns recordset with: - * test cases that has at least ONE of requested status - * on LAST EXECUTION ON At Least ONE of builds present on Build Set (Partial), for a platform - * - * If build set is EMPTY - * on LAST EXECUTION ON At Least ONE of ALL ACTIVE builds (full), for a platform - * - * Example: - * - * Test Plan: PLAN B - * Builds: B1,B2,B3 - * Test Cases: TC-100, TC-200,TC-300 - * - * Test Case - Build - LAST Execution status - * TC-100 B1 Passed - * TC-100 B2 FAILED - * TC-100 B3 Not Run - * - * TC-200 B1 FAILED - * TC-200 B2 FAILED - * TC-200 B3 BLOCKED - * - * TC-300 B1 Passed - * TC-300 B2 Passed - * TC-300 B3 BLOCKED - * - * TC-400 B1 FAILED - * TC-400 B2 BLOCKED - * TC-400 B3 FAILED - * - * Request 1: - * Provide test cases with status in ('Passed','BLOCKED') - * ON At Least ONE, OF ALL ACTIVE Builds - * - * ANSWER: - * TC-200, TC300, TC400 - * - * Request 2: ???? - * Provide test cases with status in ('FAILED','BLOCKED') - * ON ALL ACTIVE Builds - * - * ANSWER: - * TC-300, TC-400 - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsStatusSetPartialOnPlatform($id,$platformID,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $statusSet = $this->sanitizeExecStatus( $statusSet ); - $statusInClause = implode("','",$statusSet); - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,$buildSet); - - $sql = " /* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution by BUILD and PLATFORM */ " . - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.build_id = B.id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . - " AND E.tcversion_id = LEBBP.tcversion_id " . - " AND E.testplan_id = LEBBP.testplan_id " . - " AND E.platform_id = LEBBP.platform_id " . - " AND E.build_id = LEBBP.build_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id=" . $safe_id['platform'] . - " AND E.build_id IN ({$buildsCfg['inClause']}) " . - " AND E.status IN('{$statusInClause}') "; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $sql .= $groupBy; - - unset($safe_id,$buildsCfg,$sqlLEBBP); - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - - - } - - /** - * getHitsSameStatusFullOnPlatform($id,$platformID,$statusSet,$buildSet) - * - * returns recordset with: - * test cases that has at least ONE of requested status - * ON LAST EXECUTION ON ALL builds on buils set (full) , for a platform - * - * If build set is NULL => ON LAST EXECUTION ON ALL ACTIVE builds (full), for a platform - */ - function getHitsSameStatusFullOnPlatform($id,$platformID,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $statusSet = $this->sanitizeExecStatus( $statusSet ); - - return $this->helperGetHitsSameStatusOnPlatform('full',$id,$platformID,$statusSet,$buildSet); - } - - - - - /** - * getHitsSameStatusFullALOP($id,$statusSet,$buildSet) - * - * returns recordset with: - * test cases that has at least ONE of requested status - * ON LAST EXECUTION ON ALL builds on buils set (full) , for a platform - * - * If build set is NULL => ON LAST EXECUTION ON ALL ACTIVE builds (full), for a platform - * - */ - function getHitsSameStatusFullALOP($id,$statusSet,$buildSet=null,$opt=null) { - // On Postgresql - // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, - // but not in the WHERE or HAVING clauses; there you must write out the expression instead. - - $options = array('onlyActiveBuilds' => true); - $options = array_merge($options,(array)$opt); - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - list($safe_id,$buildsCfg,$sqlLEX) = $this->helperGetHits($id,null,$buildSet, - array('ignorePlatform' => true)); - if( $options['onlyActiveBuilds'] ) { - $buildsCfg['statusClause'] = " AND B.active = 1 "; - } - - // TICKET 5226: Filtering by test result did not always show the correct matches - // The filtering for "not run" status was simply not implemented for the case - // of not using platforms. - // Maybe that part was forgotten when refactoring the filters. - // - // I adopted logic from helperGetHitsSameStatusOnPlatform() to get this working. - // - $flippedStatusSet = array_flip($statusSet); // (code => idx) - $get = array('notRun' => isset($flippedStatusSet[$this->notRunStatusCode]), 'otherStatus' => false); - $hits = array('notRun' => array(), 'otherStatus' => array()); - - if($get['notRun']) { - $notRunSQL = " /* $debugMsg */ " . - " /* COUNT() is needed as parameter for HAVING clause */ " . - " SELECT COUNT(0) AS COUNTER, NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . - " NHTCV.id = TPTCV.tcversion_id " . - - " LEFT OUTER JOIN {$this->tables['executions']} E ON " . - " E.testplan_id = TPTCV.testplan_id " . - " AND E.build_id = B.id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND E.status IS NULL "; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $notRunSQL .= $groupBy . - " HAVING COUNT(0) = " . intval($buildsCfg['count']) ; - - $hits['notRun'] = $this->db->fetchRowsIntoMap($notRunSQL,'tcase_id'); - - unset($statusSet[$flippedStatusSet[$this->notRunStatusCode]]); - } - - $get['otherStatus'] = count($statusSet) > 0; - if($get['otherStatus']) { - $statusSet = $this->sanitizeExecStatus($statusSet); - $statusInClause = implode("','",$statusSet); - - // ATTENTION: - // if I've requested (Passed or Blocked) on ALL BUILDS - // Have 2 results for build number. - - // That logic is wrong when filtering for the SAME STATUS on ALL builds. - // Maybe copy/paste-error on refactoring? - // Example: With 3 builds and filtering for FAILED or BLOCKED on ALL builds - // we have to get 3 hits for each test case to be shown, not six hits. - // $countTarget = intval($buildsCfg['count']) * count($statusSet); - $countTarget = intval($buildsCfg['count']); - - $otherStatusSQL = " /* $debugMsg */ " . - " /* Count() to be used on HAVING - ALOP */ " . - " SELECT COUNT(0) AS COUNTER ,tcase_id " . - " FROM ( " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id, E.build_id " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution by BUILD IGNORE PLATFORM */ " . - " JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.build_id = B.id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEX.id " . - " AND E.tcversion_id = LEX.tcversion_id " . - " AND E.testplan_id = LEX.testplan_id " . - " AND E.build_id = LEX.build_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND E.build_id IN ({$buildsCfg['inClause']}) " . - " AND E.status IN ('{$statusInClause}')" . - " ) SQX "; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $otherStatusSQL .= $groupBy . - " HAVING COUNT(0) = " . $countTarget ; - - $hits['otherStatus'] = $this->db->fetchRowsIntoMap($otherStatusSQL,'tcase_id'); - } - - // build results record set - $hitsFoundOn = array(); - $hitsFoundOn['notRun'] = count($hits['notRun']) > 0; - $hitsFoundOn['otherStatus'] = count($hits['otherStatus']) > 0; - - if($hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus']) { - $items = array_merge(array_keys($hits['notRun']), array_keys($hits['otherStatus'])); - } else if($hitsFoundOn['notRun']) { - $items = array_keys($hits['notRun']); - } else if($hitsFoundOn['otherStatus']) { - $items = array_keys($hits['otherStatus']); - } - - - return is_null($items) ? $items : array_flip($items); - } - - - - /** - * getHitsNotRunOnBuildPlatform($id,$platformID,$buildID) - * - * returns recordset with: - * test cases with NOT RUN status on SPECIFIC build for a PLATFORM. - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsNotRunOnBuildPlatform($id,$platformID,$buildID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " /* $debugMsg */ " . - " SELECT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . - " NHTCV.id = TPTCV.tcversion_id " . - - " /* Work on Executions */ " . - " LEFT OUTER JOIN {$this->tables['executions']} E ON " . - " E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.build_id = " . intval($buildID) . - - " WHERE TPTCV.testplan_id = " . intval($id) . - " AND TPTCV.platform_id = " . intval($platformID) . - " AND E.status IS NULL "; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return is_null($recordset) ? $recordset : array_flip(array_keys($recordset)); - } - - - /** - * getHitsNotRunOnBuildALOP($id,$buildID) - * - * returns recordset with: - * test cases with NOT RUN status on SPECIFIC build On AT LEAST ONE PLATFORM. (ALOP) - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsNotRunOnBuildALOP($id,$buildID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " /* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . - " NHTCV.id = TPTCV.tcversion_id " . - - " /* Work on Executions */ " . - " LEFT OUTER JOIN {$this->tables['executions']} E ON " . - " E.testplan_id = TPTCV.testplan_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.build_id = " . intval($buildID) . - - " WHERE TPTCV.testplan_id = " . intval($id) . - " AND E.status IS NULL "; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return is_null($recordset) ? $recordset : array_flip(array_keys($recordset)); - } - - - - /** - * getHitsStatusSetOnBuildPlatform($id,$platformID,$buildID,$statusSet) - * - * returns recordset with: - * test cases with LAST EXECUTION STATUS on SPECIFIC build for a PLATFORM, IN status SET. - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsStatusSetOnBuildPlatform($id,$platformID,$buildID,$statusSet) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - list($safe_id,$buildsCfg,$sqlLEBBP) = $this->helperGetHits($id,$platformID,null,array('buildID' => $buildID)); - - $safe_id['build'] = intval($buildID); - $statusList = $this->sanitizeExecStatus( (array)$statusSet ); - - // Manage also not run - $notRunHits = null; - $dummy = array_flip($statusList); - if( isset($dummy[$this->notRunStatusCode]) ) { - tLog(__FUNCTION__ . ':: getHitsNotRunOnBuildPlatform','DEBUG'); - $notRunHits = $this->getHitsNotRunOnBuildPlatform($safe_id['tplan'],$safe_id['platform'],$safe_id['build']); - unset($statusList[$dummy[$this->notRunStatusCode]]); - } - - $statusInClause = implode("','",$statusList); - $sql = " /* $debugMsg */ " . - " SELECT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution by BUILD and PLATFORM */ " . - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.build_id = " . $safe_id['build'] . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . - " AND E.tcversion_id = LEBBP.tcversion_id " . - " AND E.testplan_id = LEBBP.testplan_id " . - " AND E.platform_id = LEBBP.platform_id " . - " AND E.build_id = LEBBP.build_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id = " . $safe_id['platform'] . - " AND E.build_id = " . $safe_id['build'] . - " AND E.status IN('{$statusInClause}')"; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - $hits = is_null($recordset) ? $recordset : array_flip(array_keys($recordset)); - - $items = (array)$hits + (array)$notRunHits; - return count($items) > 0 ? $items : null; - } - - - /** - * getHitsStatusSetOnBuildALOP($id,$buildID,$statusSet) - * - * returns recordset with: - * test cases with LAST EXECUTION STATUS on SPECIFIC build for At Least One PLATFORM, - * IN status SET. - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsStatusSetOnBuildALOP($id,$buildID,$statusSet) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - list($safe_id,$buildsCfg,$sqlLEX) = $this->helperGetHits($id,null,null, - array('buildID' => $buildID, - 'ignorePlatform' => true)); - - $safe_id['build'] = intval($buildID); - $statusList = $this->sanitizeExecStatus( (array)$statusSet ); - - // Manage also not run - $notRunHits = null; - $dummy = array_flip($statusList); - if( isset($dummy[$this->notRunStatusCode]) ) { - $notRunHits = $this->getHitsNotRunOnBuildALOP($safe_id['tplan'],$safe_id['build']); - unset($statusList[$dummy[$this->notRunStatusCode]]); - } - - $statusInClause = implode("','",$statusList); - $sql = " /* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution by BUILD IGNORE PLATFORM */ " . - " JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.build_id = " . $safe_id['build'] . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEX.id " . - " AND E.tcversion_id = LEX.tcversion_id " . - " AND E.testplan_id = LEX.testplan_id " . - " AND E.build_id = LEX.build_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND E.build_id = " . $safe_id['build'] . - " AND E.status IN('{$statusInClause}')"; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - $hits = is_null($recordset) ? $recordset : array_flip(array_keys($recordset)); - - $items = (array)$hits + (array)$notRunHits; - return count($items) > 0 ? $items : null; - } - - - /** - * getHitsStatusSetOnLatestExecALOP($id,$statusSet,$buildSet) - * - * returns recordset with: - * test cases that has at least ONE of requested status - * on ABSOLUTE LASTEST EXECUTION considering all builds on build set IGNORING platform. - * - * If build set is NULL, we will analyse ALL ACTIVE builds (full) IGNORING platform. - * - * IMPORTANT / CRITIC: THIS DOES NOT WORK for Not Run STATUS - * HAS NO SENSE, because Not Run IN NOT SAVED to DB - * => we can not find LATEST NON RUN - * Example: - * - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsStatusSetOnLatestExecALOP($id,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - list($safe_id,$buildsCfg,$sqlLEX) = $this->helperGetHits($id,null,$buildSet, - array('ignorePlatform' => true, - 'ignoreBuild' => true)); - - // Check if 'not run' in present in statusSet => throw exception - $statusList = $this->sanitizeExecStatus( (array)$statusSet ); - $dummy = array_flip($statusList); - if( isset($dummy[$this->notRunStatusCode]) ) - { - throw new Exception (__METHOD__ . ':: Status Not Run can not be used'); - } - $statusInClause = implode("','",$statusList); - - $sql = " /* $debugMsg */ " . - " SELECT MAX(LEX.id) AS latest_exec_id ,NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution IGNORE BUILD, PLATFORM */ " . - " JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEX.id " . - " AND E.tcversion_id = LEX.tcversion_id " . - " AND E.testplan_id = LEX.testplan_id " . - " AND E.build_id = B.id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND E.build_id IN ({$buildsCfg['inClause']}) " . - " AND E.status IN('{$statusInClause}') "; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $sql .= $groupBy; - - unset($safe_id,$buildsCfg,$sqlLEX); - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return is_null($recordset) ? $recordset : array_flip(array_keys($recordset)); - } - - - - /** - * getHitsStatusSetOnLatestExecOnPlatform($id,$platformID,$statusSet,$buildSet) - * - * returns recordset with: - * test cases that has at least ONE of requested status - * on ABSOLUTE LASTEST EXECUTION considering all builds on build set, for a platform - * - * If build set is NULL, we will analyse ALL ACTIVE builds (full), for a platform - * - * IMPORTANT / CRITIC: THIS DOES NOT WORK for Not Run STATUS - * HAS NO SENSE, because Not Run IN NOT SAVED to DB - * => we can not find LATEST NON RUN - * Example: - * - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsStatusSetOnLatestExecOnPlatform($id,$platformID,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - list($safe_id,$buildsCfg,$sqlLEBP) = $this->helperGetHits($id,$platformID,$buildSet, - array('ignoreBuild' => true)); - - // Check if 'not run' in present in statusSet => throw exception - $statusList = $this->sanitizeExecStatus( (array)$statusSet ); - $dummy = array_flip($statusList); - if( isset($dummy[$this->notRunStatusCode]) ) - { - throw new Exception (__METHOD__ . ':: Status Not Run can not be used'); - } - $statusInClause = implode("','",$statusList); - - // -------------------------------------------------------------------------------------- - $sql = " /* $debugMsg */ " . - " SELECT MAX(LEBP.id) AS latest_exec_id ,NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution on PLATFORM IGNORE BUILD */ " . - " JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - // " AND LEBP.build_id = B.id " . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBP.id " . - " AND E.tcversion_id = LEBP.tcversion_id " . - " AND E.testplan_id = LEBP.testplan_id " . - " AND E.platform_id = LEBP.platform_id " . - // " AND E.build_id = LEBBP.build_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND TPTCV.platform_id=" . $safe_id['platform'] . - " AND E.build_id IN ({$buildsCfg['inClause']}) " . - " AND E.status IN ('{$statusInClause}') "; - - $groupBy = ' GROUP BY ' . ((DB_TYPE == 'mssql') ? 'parent_id ':'tcase_id'); - $sql .= $groupBy; - - unset($safe_id,$buildsCfg,$sqlLEBP); - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return is_null($recordset) ? $recordset : array_flip(array_keys($recordset)); - } - - - - /** - * getHitsSameStatusPartialOnPlatform($id,$platformID,$statusSet,$buildSet) - * - * returns recordset with: - * - * test cases that has at least ONE of requested status - * ON LAST EXECUTION ON AT LEAST ONE OF builds on build set, for a platform - * - * If build set is empty - * test cases that has at least ONE of requested status - * ON LAST EXECUTION ON AT LEAST ONE OF ALL ACTIVE builds, for a platform - * - */ - function getHitsSameStatusPartialOnPlatform($id,$platformID,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $statusSet = $this->sanitizeExecStatus( (array)$statusSet ); - return $this->helperGetHitsSameStatusOnPlatform('partial',$id,$platformID,$statusSet,$buildSet); - } - - - /** - * getHitsSameStatusPartialALOP($id,$statusSet) - * - * returns recordset with: - * - * test cases that has at least ONE of requested status - * ON LAST EXECUTION ON AT LEAST ONE OF builds on build set, for a platform - * - * If build set is empty - * test cases that has at least ONE of requested status - * ON LAST EXECUTION ON AT LEAST ONE OF ALL ACTIVE builds, for a platform - * - */ - function getHitsSameStatusPartialALOP($id,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $getHitsNotRunMethod = 'getHitsNotRunPartialALOP'; - $getHitsStatusSetMethod = 'getHitsStatusSetPartialALOP'; - - // Needed because, may be we will need to remove an element - $statusSetLocal = $this->sanitizeExecStatus( (array)$statusSet ); - - $items = null; - $hits = array('notRun' => array(), 'otherStatus' => array()); - $dummy = array_flip($statusSetLocal); // (code => idx) - $get = array('notRun' => isset($dummy[$this->notRunStatusCode]), 'otherStatus' => false); - - - if($get['notRun']) - { - tLog(__METHOD__ . ":: \$tplan_mgr->$getHitsNotRunMethod", 'DEBUG'); - $hits['notRun'] = (array)$this->$getHitsNotRunMethod($id,$buildSet); - unset($statusSetLocal[$dummy[$this->notRunStatusCode]]); - } - - if( ($get['otherStatus']=(count($statusSetLocal) > 0)) ) - { - tLog(__METHOD__ . ":: \$tplan_mgr->$getHitsStatusSetMethod", 'DEBUG'); - $hits['otherStatus'] = (array)$this->$getHitsStatusSetMethod($id,$statusSetLocal,$buildSet); - } - - // build results recordset - $hitsFoundOn = array(); - $hitsFoundOn['notRun'] = count($hits['notRun']) > 0; - $hitsFoundOn['otherStatus'] = count($hits['otherStatus']) > 0; - - - if($get['notRun'] && $get['otherStatus']) - { - if( $hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus'] ) - { - $items = array_keys($hits['notRun']) + array_keys($hits['otherStatus']); - } - } - else if($get['notRun'] && $hitsFoundOn['notRun']) - { - $items = array_keys($hits['notRun']); - } - else if($get['otherStatus'] && $hitsFoundOn['otherStatus']) - { - $items = array_keys($hits['otherStatus']); - } - - return is_null($items) ? $items : array_flip($items); - } - - - - /** - * getHitsStatusSetPartialALOP($id,$platformID,$statusSet,$buildSet) - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsStatusSetPartialALOP($id,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $statusSet = $this->sanitizeExecStatus( $statusSet ); - $statusInClause = implode("','",$statusSet); - list($safe_id,$buildsCfg,$sqlLEX) = $this->helperGetHits($id,null,$buildSet, - array('ignorePlatform' => true)); - - $sql = " /* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Latest Execution by JUST BUILD IGNORE PLATFORM */ " . - " JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.build_id = B.id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - - // " AND LEX.platform_id = TPTCV.platform_id " . - - " /* Get STATUS INFO From Executions */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEX.id " . - " AND E.tcversion_id = LEX.tcversion_id " . - " AND E.testplan_id = LEX.testplan_id " . - " AND E.build_id = LEX.build_id " . - - // " AND E.platform_id = LEX.platform_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND E.build_id IN ({$buildsCfg['inClause']}) " . - " AND E.status IN ('{$statusInClause}') "; - - - unset($safe_id,$buildsCfg,$sqlLEX); - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - - /** - * getHitsNotRunPartialALOP($id,buildSet) - * - * returns recordset with: - * - * test cases with NOT RUN status at LEAST ON ONE of builds - * present on build set (Partial), IGNORING Platforms - * - * If build set is empty: - * test cases with NOT RUN status at LEAST ON ONE of builds - * present on ACTIVE BUILDS set (Partial), IGNORING Platforms - * - * - * Example: (TO BE REWORKED) - * - * Test Plan: PLAN B - * Builds: B1,B2,B3 - * Test Cases: TC-100, TC-200,TC-300 - * - * Test Case - Build - LAST Execution status - * TC-100 B1 Passed - * TC-100 B2 FAILED - * TC-100 B3 Not Run => to have this status means THAT HAS NEVER EXECUTED ON B3 - * - * TC-200 B1 FAILED - * TC-200 B2 FAILED - * TC-200 B3 BLOCKED - * - * TC-300 B1 Passed - * TC-300 B2 Passed - * TC-300 B3 BLOCKED - * - * TC-400 B1 FAILED - * TC-400 B2 BLOCKED - * TC-400 B3 FAILED - * - * Request : - * Provide test cases with status 'NOT RUN' - * ON At Least ON OF all ACTIVE Builds - * - * ANSWER: - * TC-100 - * - * @return - * - * @internal revisions - * @since 1.9.4 - * - */ - function getHitsNotRunPartialALOP($id,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($safe_id,$buildsCfg,$sqlLEX) = $this->helperGetHits($id,null,$buildSet, - array('ignorePlatform' => true)); - - $sql = " /* $debugMsg */ " . - " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . - $buildsCfg['statusClause'] . - - " /* Get Test Case ID */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . - " NHTCV.id = TPTCV.tcversion_id " . - - " /* Executions, looking for status NULL (remember NOT RUN is not written on DB) */ " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = B.id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - - " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . - " AND B.id IN ({$buildsCfg['inClause']}) " . - " AND E.status IS NULL "; - - $recordset = $this->db->fetchRowsIntoMap($sql,'tcase_id'); - return $recordset; - } - - - - /** - * helperGetHitsSameStatusOnPlatform($mode,$id,$platformID,$statusSet,$buildSet) - * - * @internal revisions: - * 20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches - */ - function helperGetHitsSameStatusOnPlatform($mode,$id,$platformID,$statusSet,$buildSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - switch($mode) - { - case 'partial': - $getHitsNotRunMethod = 'getHitsNotRunPartialOnPlatform'; - $getHitsStatusSetMethod = 'getHitsStatusSetPartialOnPlatform'; - - break; - - case 'full': - $getHitsNotRunMethod = 'getHitsNotRunFullOnPlatform'; - $getHitsStatusSetMethod = 'getHitsStatusSetFullOnPlatform'; - break; - } - - // Needed because, may be we will need to remove an element - $statusSetLocal = $this->sanitizeExecStatus( $statusSet ); - - $items = null; - $hits = array('notRun' => array(), 'otherStatus' => array()); - - $dummy = array_flip($statusSetLocal); // (code => idx) - $get = array('notRun' => isset($dummy[$this->notRunStatusCode]), 'otherStatus' => false); - - - if($get['notRun']) - { - $hits['notRun'] = (array)$this->$getHitsNotRunMethod($id,$platformID,$buildSet); - unset($statusSetLocal[$dummy[$this->notRunStatusCode]]); - } - if( ($get['otherStatus']=(count($statusSetLocal) > 0)) ) - { - $hits['otherStatus'] = (array)$this->$getHitsStatusSetMethod($id,$platformID,$statusSetLocal,$buildSet); - } - - // build results recordset - $hitsFoundOn = array(); - $hitsFoundOn['notRun'] = count($hits['notRun']) > 0; - $hitsFoundOn['otherStatus'] = count($hits['otherStatus']) > 0; - - //20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches - //if($get['notRun'] && $get['otherStatus']) - //{ - //if( $hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus'] ) - // The problem with this if clause: - // When $get['notRun'] && $get['otherStatus'] evaluated as TRUE but there were no hits - // in one of $hitsFoundOn['notRun'] or $hitsFoundOn['otherStatus'], then no results were returned at all. - - if($hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus']) - { - // THIS DOES NOT WORK with numeric keys - // $items = array_merge(array_keys($hits['notRun']),array_keys($hits['otherStatus'])); - //$items = array_keys($hits['notRun']) + array_keys($hits['otherStatus']); - - // 20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches - // - // ATTENTION: Using the + operator instead of array_merge() for numeric keys is wrong! - // - // Quotes from documentation http://www.php.net/manual/en/function.array-merge.php: - // - // array_merge(): "If the input arrays have the same string keys, then the later value for that key - // will overwrite the previous one. If, however, the arrays contain numeric keys, - // the later value will not overwrite the original value, but will be appended." - // - // + operator: "The keys from the first array will be preserved. - // If an array key exists in both arrays, then the element from the first array will be used - // and the matching key's element from the second array will be ignored." - // - // That means if there were 5 results in $hits['notRun']) and 10 results in $hits['otherStatus']), - // the first 5 testcases from $hits['otherStatus']) were not in the result set because of the + operator. - // - // After using array_keys() we have numeric keys => we HAVE TO USE array_merge(). - $items = array_merge(array_keys($hits['notRun']), array_keys($hits['otherStatus'])); - } - else if($hitsFoundOn['notRun']) - { - $items = array_keys($hits['notRun']); - } - else if($hitsFoundOn['otherStatus']) - { - $items = array_keys($hits['otherStatus']); - } - - return is_null($items) ? $items : array_flip($items); - } - - - /** - * helperGetHits($id,$platformID,$buildSet,$options) - * - * - */ - function helperGetHits($id,$platformID,$buildSet=null,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('buildID' => 0, 'ignorePlatform' => false, 'ignoreBuild' => false); - $my['options'] = array_merge($my['options'],(array)$options); - - - $safe_id['tplan'] = intval($id); - $safe_id['platform'] = intval($platformID); - - $buildsCfg['statusClause'] = ""; - $buildsCfg['inClause'] = ""; - $buildsCfg['count'] = 0; - - if($my['options']['buildID'] <= 0) { - if( is_null($buildSet) ) { - $buildSet = array_keys($this->get_builds($id, self::ACTIVE_BUILDS)); - $buildsCfg['statusClause'] = " AND B.active = 1 "; - } - $buildsCfg['count'] = count($buildSet); - $buildsCfg['inClause'] = implode(",",$buildSet); - } else { - $buildsCfg['inClause'] = intval($my['options']['buildID']); - } - - $platformClause = " AND EE.platform_id = " . $safe_id['platform']; - $platformField = " ,EE.platform_id "; - if( $my['options']['ignorePlatform'] ) { - $platformClause = " "; - $platformField = " "; - } - - $buildField = " ,EE.build_id "; - if( $my['options']['ignoreBuild'] ) { - $buildField = " "; - } - - - - $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id + custom_fields.id = cfield_testprojects.field_id + and cfield_testprojects.active = 1 + and custom_fields.enable_on_execution = 1 + and custom_fields.show_on_execution = 1 + and cfield_testprojects.testproject_id = " . + $this->db->prepare_int($tproject_id) . "order by field_id"; + + return $this->db->fetchColumnsIntoMap($sql, 'field_id', 'label'); + } + + /* + * function: html_table_of_custom_field_inputs + * + * + * args: $id + * [$parent_id]: need when you call this method during the creation + * of a test suite, because the $id will be 0 or null. + * + * [$scope]: 'design','execution' + * + * returns: html string + * + */ + public function html_table_of_custom_field_inputs($id, $parent_id = null, + $scope = 'design', $name_suffix = '', $input_values = null) + { + $cf_smarty = ''; + $method_suffix = $scope == 'design' ? $scope : 'execution'; + $method_name = "get_linked_cfields_at_{$method_suffix}"; + $cf_map = $this->$method_name($id, $parent_id); + + if (! is_null($cf_map)) { + $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map, + $name_suffix, $input_values); + } + return $cf_smarty; + } + + /* + * function: html_table_of_custom_field_values + * + * args: $id + * [$scope]: 'design','execution' + * + * [$filters]:default: null + * + * map with keys: + * + * [show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * include ONLY custom fields that can be viewed + * while user is execution testcases. + * + * 0 or null -> don't filter + * + * returns: html string + * + * rev : + * 20080811 - franciscom - BUGID 1650 (REQ) + * 20070701 - franciscom - fixed return string when there are no custom fields. + */ + public function html_table_of_custom_field_values($id, $scope = 'design', + $filters = null, $formatOptions = null) + { + $cf_smarty = ''; + $parent_id = null; + $label_css_style = ' class="labelHolder" '; + $value_css_style = ' '; + + $add_table = true; + $table_style = ''; + if (! is_null($formatOptions)) { + $label_css_style = isset($formatOptions['label_css_style']) ? $formatOptions['label_css_style'] : $label_css_style; + $value_css_style = isset($formatOptions['value_css_style']) ? $formatOptions['value_css_style'] : $value_css_style; + + $add_table = isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; + $table_style = isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; + } + + $show_cf = config_get('custom_fields')->show_custom_fields_without_value; + if ($scope == 'design') { + $cf_map = $this->get_linked_cfields_at_design($id, $parent_id, + $filters); + } else { + $cf_map = $this->getLinkedCfieldsAtExecution($id); + } + + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + // if user has assigned a value, then node_id is not null + // BUGID 3989 + if (isset($cf_info['node_id']) || $cf_info['node_id'] || $show_cf) { + // true => do not create input in audit log + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, true)); + $cf_smarty .= "" . + htmlspecialchars($label) . "" . + "" . + $this->cfield_mgr->string_custom_field_value($cf_info, + $id) . "\n"; + } + } + } + + if ($cf_smarty != '' && $add_table) { + $cf_smarty = "" . $cf_smarty . "
    "; + } + return $cf_smarty; + } + + // function end + + /* + * function: filterByOnDesignCustomFields + * Filter on values of custom fields that are managed + * ON DESIGN Area (i.e. when creating Test Specification). + * + * @used by getLinkedItems() in file execSetResults.php + * + * args : + * $tp_tcs - key: test case ID + * value: map with keys tcase_id,tcversion_id,... + * + * $cf_hash [cf_id] = value of cfields to filter by. + * + * returns: array filtered by selected custom fields. + * + * @internal revisions + * + */ + public function filterByOnDesignCustomFields($tp_tcs, $cf_hash) + { + $new_tp_tcs = null; + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $or_clause = ''; + $cf_query = ''; + $ignored = 0; + $doFilter = false; + $doIt = true; + + if (isset($cf_hash)) { + foreach ($cf_hash as $cf_id => $cf_value) { + // single value or array? + if (is_array($cf_value)) { + $count = 1; + $cf_query .= $or_clause; + foreach ($cf_value as $value) { + if ($count > 1) { + $cf_query .= " AND "; + } + $cf_query .= " ( CFD.value LIKE '%{$value}%' AND CFD.field_id = {$cf_id} )"; + $count ++; + } + } else { + // Because cf value can NOT exists on DB depending on system config. + if (trim($cf_value) != '') { + $cf_query .= $or_clause; + $cf_query .= " ( CFD.value LIKE '%{$cf_value}%' AND CFD.field_id = {$cf_id} ) "; + } else { + $ignored ++; + } + } + + if ($or_clause == '') { + $or_clause = ' OR '; + } + } + + // grand finale + if ($cf_query != '') { + $cf_query = " AND ({$cf_query}) "; + $doFilter = true; + } + } + $cf_qty = count($cf_hash) - $ignored; + $doIt = ! $doFilter; + foreach ($tp_tcs as $tc_id => $tc_value) { + if ($doFilter) { + $sql = " /* $debugMsg */ SELECT CFD.value FROM {$this->tables['cfield_design_values']} CFD," . + " {$this->tables['nodes_hierarchy']} NH" . + " WHERE CFD.node_id = NH.id " . + " AND NH.parent_id = {$tc_value['tcase_id']} " . + " {$cf_query} "; + + $rows = $this->db->fetchColumnsIntoArray($sql, 'value'); // BUGID 4115 + + // if there exist as many rows as custom fields to be filtered by => tc does meet the criteria + // TO CHECK - 20140126 - Give a look to treeMenu.inc.php - filter_by_cf_values() + // to understand if both logics are coerent. + $doIt = (count($rows) == $cf_qty); + } + if ($doIt) { + $new_tp_tcs[$tc_id] = $tp_tcs[$tc_id]; + } + } + return $new_tp_tcs; + } + + /* + * function: get_estimated_execution_time + * + * Takes all testcases linked to testplan and computes + * SUM of values assigned AT DESIGN TIME to customa field + * named CF_ESTIMATED_EXEC_TIME + * + * IMPORTANT: + * 1. at time of this writting (20080820) this CF can be of type: string,numeric or float. + * 2. YOU NEED TO USE . (dot) as decimal separator (US decimal separator?) or + * sum will be wrong. + * + * + * + * args:id testplan id + * itemSet: default null - can be an arry with test case VERSION ID + * + * returns: sum of CF values for all testcases linked to testplan + * + * rev: + * + */ + public function get_estimated_execution_time($id, $itemSet = null, + $platformID = null) + { + // check if cf exist and is assigned and active intest plan parent (TEST PROJECT) + $pinfo = $this->tree_manager->get_node_hierarchy_info($id); + $cf_info = $this->cfield_mgr->get_linked_to_testproject( + $pinfo['parent_id'], 1, array( + 'name' => 'CF_ESTIMATED_EXEC_TIME' + )); + if (is_null($cf_info)) { + return $this->getEstimatedExecutionTime($id, $itemSet, $platformID); + } else { + return $this->getEstimatedExecutionTimeFromCF($id, $itemSet, + $platformID); + } + } + + /** + */ + private function getEstimatedExecutionTime($id, $itemSet = null, + $platformID = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $tcVersionIDSet = array(); + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'addIfNull' => true + ); + $platformSet = array_keys($this->getPlatforms($id, $getOpt)); + + if (is_null($itemSet)) { + // we need to loop over all linked PLATFORMS (if any) + $tcVersionIDSet = array(); + foreach ($platformSet as $platfID) { + if (is_null($platformID) || $platformID == $platfID) { + $linkedItems = $this->get_linked_tcvid($id, $platfID, + array( + 'addEstimatedExecDuration' => true + )); + if (! is_null($linkedItems)) { + $tcVersionIDSet[$platfID] = $linkedItems; + } + } + } + } else { + // Important NOTICE + // we can found SOME LIMITS on number of elements on IN CLAUSE + // need to make as many set as platforms linked to test plan + $sql4tplantcv = " /* $debugMsg */ SELECT tcversion_id, platform_id,TCV.estimated_exec_duration " . + " FROM {$this->tables['testplan_tcversions']} " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = tcversion_id " . + " WHERE testplan_id=" . intval($id) . " AND tcversion_id IN (" . + implode(',', $itemSet) . ")"; + if (! is_null($platformID)) { + $sql4tplantcv .= " AND platform_id= " . intval($platformID); + } + + $rs = $this->db->fetchRowsIntoMap($sql4tplantcv, 'platform_id', + database::CUMULATIVE); + foreach ($rs as $platfID => $elem) { + $tcVersionIDSet[$platfID] = $elem; + } + } + + $estimated = array( + 'platform' => array(), + 'totalMinutes' => 0, + 'totalTestCases' => 0 + ); + foreach ($tcVersionIDSet as $platfID => $items) { + $estimated['platform'][$platfID]['minutes'] = 0; + $estimated['platform'][$platfID]['tcase_qty'] = count($items); + foreach ($items as $dx) { + if (! is_null($dx['estimated_exec_duration'])) { + $estimated['platform'][$platfID]['minutes'] += $dx['estimated_exec_duration']; + } + } + $estimated['totalMinutes'] += $estimated['platform'][$platfID]['minutes']; + $estimated['totalTestCases'] += $estimated['platform'][$platfID]['tcase_qty']; + } + + return $estimated; + } + + /** + */ + private function getEstimatedExecutionTimeFromCF($id, $itemSet = null, + $platformID = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $estimated = array( + 'platform' => array(), + 'totalMinutes' => 0, + 'totalTestCases' => 0 + ); + $cf_info = $this->cfield_mgr->get_by_name('CF_ESTIMATED_EXEC_TIME'); + + // CF exists ? + if ($status_ok = ! is_null($cf_info)) { + $cfield_id = key($cf_info); + } + + if ($status_ok) { + $tcVersionIDSet = array(); + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'addIfNull' => true + ); + $platformSet = array_keys($this->getPlatforms($id, $getOpt)); + + $sql = " /* $debugMsg */ "; + if (DB_TYPE == 'mysql') { + $sql .= " SELECT SUM(value) "; + } elseif (DB_TYPE == 'postgres' || DB_TYPE == 'mssql') { + $sql .= " SELECT SUM(CAST(value AS NUMERIC)) "; + } + + $sql .= " AS SUM_VALUE FROM {$this->tables['cfield_design_values']} CFDV " . + " WHERE CFDV.field_id={$cfield_id} "; + + if (is_null($itemSet)) { + // 20110112 - franciscom + // we need to loop over all linked PLATFORMS (if any) + $tcVersionIDSet = array(); + foreach ($platformSet as $platfID) { + if (is_null($platformID) || $platformID == $platfID) { + $linkedItems = $this->get_linked_tcvid($id, $platfID); + if (! is_null($linkedItems)) { + $tcVersionIDSet[$platfID] = array_keys($linkedItems); + } + } + } + } else { + // Important NOTICE + // we can found SOME LIMITS on number of elements on IN CLAUSE + // + // need to make as many set as platforms linked to test plan + $sql4tplantcv = " /* $debugMsg */ SELECT tcversion_id, platform_id " . + " FROM {$this->tables['testplan_tcversions']} " . + " WHERE testplan_id=" . intval($id) . + " AND tcversion_id IN (" . implode(',', $itemSet) . ")"; + + if (! is_null($platformID)) { + $sql4tplantcv .= " AND platform_id= " . intval($platformID); + } + + $rs = $this->db->fetchColumnsIntoMap($sql4tplantcv, + 'platform_id', 'tcversion_id', database::CUMULATIVE); + foreach ($rs as $platfID => $elem) { + $tcVersionIDSet[$platfID] = array_values($elem); + } + } + } + + if ($status_ok) { + // Important NOTICE + // we can found SOME LIMITS on number of elements on IN CLAUSE + $estimated = array( + 'platform' => array(), + 'totalMinutes' => 0, + 'totalTestCases' => 0 + ); + foreach ($tcVersionIDSet as $platfID => $items) { + $sql2exec = $sql . " AND node_id IN (" . implode(',', $items) . + ")"; + $dummy = $this->db->fetchOneValue($sql2exec); + $estimated['platform'][$platfID]['minutes'] = is_null($dummy) ? 0 : $dummy; + $estimated['platform'][$platfID]['tcase_qty'] = count($items); + + $estimated['totalMinutes'] += $estimated['platform'][$platfID]['minutes']; + $estimated['totalTestCases'] += $estimated['platform'][$platfID]['tcase_qty']; + } + } + return $estimated; + } + + /* + * function: get_execution_time + * Takes all executions or a subset of executions, regarding a testplan and + * computes SUM of values assigned AT EXECUTION TIME to custom field named CF_EXEC_TIME + * + * IMPORTANT: + * 1. at time of this writting (20081207) this CF can be of type: string,numeric or float. + * 2. YOU NEED TO USE . (dot) as decimal separator (US decimal separator?) or + * sum will be wrong. + * + * args:id testplan id + * $execIDSet: default null + * + * returns: sum of CF values for all testcases linked to testplan + * + * rev: + * @internal revision + */ + private function getExecutionTime2($context, $execIDSet = null) + { + // check if cf exist and is assigned and active intest plan parent (TEST PROJECT) + $pinfo = $this->tree_manager->get_node_hierarchy_info($id); + $cf_info = $this->cfield_mgr->get_linked_to_testproject( + $pinfo['parent_id'], 1, array( + 'name' => 'CF_EXEC_TIME' + )); + if (is_null($cf_info)) { + return $this->getExecutionTime($context, $execIDSet); + } else { + return $this->getExecutionTimeFromCF($context->tplan_id, $execIDSet, + $context->platform_id); + } + } + + /** + */ + public function getExecutionTime($context, $execIDSet = null) + { + $total_time = array( + 'platform' => array(), + 'totalMinutes' => 0, + 'totalTestCases' => 0 + ); + $targetSet = array(); + + if (is_null($execIDSet)) { + $filters = null; + if (! is_null($context->platform_id)) { + $filters = array( + 'platform_id' => $context->platform_id + ); + } + + if (! is_null($context->build_id) && $context->build_id > 0) { + $filters['build_id'] = $context->build_id; + } + + // we will compute time for ALL linked and executed test cases, + // BUT USING ONLY TIME SPEND for LATEST executed TCVERSION + $options = array( + 'addExecInfo' => true + ); + $executed = $this->getLTCVNewGeneration($context->tplan_id, $filters, + $options); + + // if( $status_ok = !is_null($executed) ) + if (! is_null($executed)) { + $tc2loop = array_keys($executed); + foreach ($tc2loop as $tcase_id) { + $p2loop = array_keys($executed[$tcase_id]); + foreach ($p2loop as $platf_id) { + $targetSet[$platf_id][] = array( + 'id' => $executed[$tcase_id][$platf_id]['exec_id'], + 'duration' => $executed[$tcase_id][$platf_id]['execution_duration'] + ); + } + } + } + } else { + // If user has passed in a set of exec id, we assume that + // he has make a good work, i.e. if he/she wanted just analize + // executions for just a PLATFORM he/she has filtered BEFORE + // passing in input to this method the item set. + // Then we will IGNORE value of argument platformID to avoid + // run a second (and probably useless query). + // We will use platformID JUST as index for output result + if (is_null($context->platform_id)) { + throw new Exception( + __FUNCTION__ . + ' When you pass $execIDSet an YOU NEED TO PROVIDE a platform ID'); + } + $targetSet[$context->platform_id] = $this->getExecutionDurationForSet( + $execIDSet); + } + + foreach ($targetSet as $platfID => $itemSet) { + $total_time['platform'][$platfID]['minutes'] = 0; + $total_time['platform'][$platfID]['tcase_qty'] = count($itemSet); + foreach ($itemSet as $dx) { + if (! is_null($dx['duration'])) { + $total_time['platform'][$platfID]['minutes'] += $dx['duration']; + } + } + + $total_time['totalMinutes'] += $total_time['platform'][$platfID]['minutes']; + $total_time['totalTestCases'] += $total_time['platform'][$platfID]['tcase_qty']; + } + return $total_time; + } + + /** + */ + private function getExecutionTimeFromCF($id, $execIDSet = null, + $platformID = null) + { + $total_time = array( + 'platform' => array(), + 'totalMinutes' => 0, + 'totalTestCases' => 0 + ); + $targetSet = array(); + $cf_info = $this->cfield_mgr->get_by_name('CF_EXEC_TIME'); + + // CF exists ? + if ($status_ok = ! is_null($cf_info)) { + $cfield_id = key($cf_info); + } + + if ($status_ok) { + $sql = "SELECT SUM(CAST(value AS NUMERIC)) "; + if (DB_TYPE == 'mysql') { + $sql = "SELECT SUM(value) "; + } elseif (DB_TYPE == 'postgres' || DB_TYPE == 'mssql') { + $sql = "SELECT SUM(CAST(value AS NUMERIC)) "; + } + $sql .= " AS SUM_VALUE FROM {$this->tables['cfield_execution_values']} CFEV " . + " WHERE CFEV.field_id={$cfield_id} " . " AND testplan_id={$id} "; + + if (is_null($execIDSet)) { + + $filters = null; + if (! is_null($platformID)) { + $filters = array( + 'platform_id' => $platformID + ); + } + + // we will compute time for ALL linked and executed test cases, + // BUT USING ONLY TIME SPEND for LAST executed TCVERSION + // $options = array('only_executed' => true, 'output' => 'mapOfMap'); + $options = array( + 'addExecInfo' => true + ); + $executed = $this->getLTCVNewGeneration($id, $filters, $options); + if ($status_ok = ! is_null($executed)) { + $tc2loop = array_keys($executed); + foreach ($tc2loop as $tcase_id) { + $p2loop = array_keys($executed[$tcase_id]); + foreach ($p2loop as $platf_id) { + $targetSet[$platf_id][] = $executed[$tcase_id][$platf_id]['exec_id']; + } + } + } + } else { + // If user has passed in a set of exec id, we assume that + // he has make a good work, i.e. if he/she wanted just analize + // executions for just a PLATFORM he/she has filtered BEFORE + // passing in input to this method the item set. + // Then we will IGNORE value of argument platformID to avoid + // run a second (and probably useless query). + // We will use platformID JUST as index for output result + + if (is_null($platformID)) { + throw new Exception( + __FUNCTION__ . + ' When you pass $execIDSet an YOU NEED TO PROVIDE a platform ID'); + } + $targetSet[$platformID] = $execIDSet; + } + } + + if ($status_ok) { + // Important NOTICE + // we can found SOME LIMITS on number of elements on IN CLAUSE + // + // $estimated = array('platform' => array(), 'totalMinutes' => 0, 'totalTestCases' => 0); + foreach ($targetSet as $platfID => $items) { + $sql2exec = $sql . " AND execution_id IN (" . + implode(',', $items) . ")"; + + $dummy = $this->db->fetchOneValue($sql2exec); + $total_time['platform'][$platfID]['minutes'] = is_null($dummy) ? 0 : $dummy; + $total_time['platform'][$platfID]['tcase_qty'] = count($items); + + $total_time['totalMinutes'] += $total_time['platform'][$platfID]['minutes']; + $total_time['totalTestCases'] += $total_time['platform'][$platfID]['tcase_qty']; + } + } + + return $total_time; + } + + /* + * function: get_prev_builds() + * + * args: id: testplan id + * build_id: all builds belonging to choosen testplan, + * with id < build_id will be retreived. + * [active]: default null -> do not filter on active status + * + * returns: + * + */ + private function getPrevBuilds($id, $build_id, $active = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT id,testplan_id, name, notes, active, is_open " . + " FROM {$this->tables['builds']} " . + " WHERE testplan_id = {$id} AND id < {$build_id}"; + + if (! is_null($active)) { + $sql .= " AND active=" . intval($active) . " "; + } + + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + * returns set of tcversions that has same execution status + * in every build present on buildSet for selected Platform. + * + * id: testplan id + * buildSet: builds to analise. + * status: status code (can be an array) + */ + private function getSameStatusForBuildSet($id, $buildSet, $status) + { + // On Postgresql + // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, + // but not in the WHERE or HAVING clauses; there you must write out the expression instead. + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $node_types = $this->tree_manager->get_available_node_types(); + $num_exec = count($buildSet); + $build_in = implode(",", $buildSet); + + $first_results = null; + if (in_array($this->notRunStatusCode, (array) $status)) { + + $sql = " /* $debugMsg */ SELECT distinct T.tcversion_id,E.build_id,NH.parent_id AS tcase_id " . + " FROM {$this->tables['testplan_tcversions']} T " . + " JOIN {$this->tables['nodes_hierarchy']} NH ON T.tcversion_id=NH.id " . + " AND NH.node_type_id={$node_types['testcase_version']} " . + " LEFT OUTER JOIN {$this->tables['executions']} E ON T.tcversion_id = E.tcversion_id " . + " AND T.testplan_id=E.testplan_id AND E.build_id IN ({$build_in}) " . + " WHERE T.testplan_id={$id} AND E.build_id IS NULL "; + + $first_results = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + $sql = " SELECT EE.status,SQ1.tcversion_id, NH.parent_id AS tcase_id, COUNT(EE.status) AS exec_qty " . + " FROM {$this->tables['executions']} EE, {$this->tables['nodes_hierarchy']} NH," . + " (SELECT E.tcversion_id,E.build_id,MAX(E.id) AS last_exec_id " . + " FROM {$this->tables['executions']} E " . + " WHERE E.build_id IN ({$build_in}) " . + " GROUP BY E.tcversion_id,E.build_id) AS SQ1 " . + " WHERE EE.build_id IN ({$build_in}) " . " AND EE.status IN ('" . + $status . "') AND NH.node_type_id={$node_types['testcase_version']} " . + " AND SQ1.last_exec_id=EE.id AND SQ1.tcversion_id=NH.id " . + " GROUP BY status,SQ1.tcversion_id,NH.parent_id" . + " HAVING COUNT(EE.status)= {$num_exec} "; + + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + + if (count($first_results)) { + foreach ($first_results as $key => $value) { + $recordset[$key] = $value; + } + } + + return $recordset; + } + + /** + * BUGID 2455, BUGID 3026 + * find all builds for which a testcase has not been executed + * + * @author asimon + * @param integer $id + * Build ID + * @param array $buildSet + * build set to check + * @return array $new_set set of builds which match the search criterium + * @internal revisions + * 20101215 - asimon - BUGID 4023: correct filtering also with platforms + */ + private function getNotRunForAnyBuild($id, $buildSet, $platformid = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $results = array(); + + $tcversionPlatformString = ""; + $executionPlatformString = ""; + if ($platformid) { + $tcversionPlatformString = "AND T.platform_id=$platformid"; + $executionPlatformString = "AND E.platform_id=$platformid"; + } + + foreach ($buildSet as $build) { + $sql = "/* $debugMsg */ SELECT distinct T.tcversion_id, E.build_id, E.status, NH.parent_id AS tcase_id " . + " FROM {$this->tables['testplan_tcversions']} T " . + " JOIN {$this->tables['nodes_hierarchy']} NH ON T.tcversion_id=NH.id AND NH.node_type_id=4 " . + " LEFT OUTER JOIN {$this->tables['executions']} E ON T.tcversion_id = E.tcversion_id " . + " AND T.testplan_id=E.testplan_id AND E.build_id=$build $executionPlatformString" . + " WHERE T.testplan_id={$id} AND E.status IS NULL $tcversionPlatformString"; + $results[] = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + $recordset = array(); + foreach ($results as $result) { + if (! is_null($result) && (is_array($result))) // BUGID 3806 + { + $recordset = array_merge_recursive($recordset, $result); + } + } + $new_set = array(); + foreach ($recordset as $val) { + $new_set[$val['tcase_id']] = $val; + } + + return $new_set; + } + + /** + * link platforms to a new Test Plan + * + * @param int $source_id + * original Test Plan id + * @param int $target_id + * new Test Plan id + * @param array $mappings: + * key source platform id, target platform id + * USED when copy is done to a test plan that BELONGS to + * another Test Project. + */ + private function copyPlatformsLinks($source_id, $target_id, $mappings = null) + { + $sourceLinks = $this->platform_mgr->getLinkedToTestplanAsMap($source_id); + if (! is_null($sourceLinks)) { + $sourceLinks = array_keys($sourceLinks); + if (! is_null($mappings)) { + foreach ($sourceLinks as $key => $value) { + $sourceLinks[$key] = $mappings[$value]; + } + } + $this->platform_mgr->linkToTestplan($sourceLinks, $target_id); + } + } + + /** + * link attachments to a new Test Plan + * + * @param int $source_id + * original Test Plan id + * @param int $target_id + * new Test Plan id + */ + private function copyAttachments($source_id, $target_id) + { + $this->attachmentRepository->copyAttachments($source_id, $target_id, + $this->attachmentTableName); + } + + /** + * outputFormat: + * 'array', + * 'map', + * 'mapAccessByID' => map access key: id + * 'mapAccessByName' => map access key: name + */ + public function getPlatforms($id, $options = null) + { + $my['options'] = array( + 'outputFormat' => 'array', + 'outputDetails' => 'full', + 'addIfNull' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + switch ($my['options']['outputFormat']) { + case 'map': + $platforms = $this->platform_mgr->getLinkedToTestplanAsMap($id); + break; + + default: + $opt = array( + 'outputFormat' => $my['options']['outputFormat'] + ); + $platforms = $this->platform_mgr->getLinkedToTestplan($id, $opt); + break; + } + + if (! is_null($platforms)) { + switch ($my['options']['outputDetails']) { + case 'name': + foreach ($platforms as $id => $elem) { + $platforms[$id] = $elem['name']; + } + break; + + default: + break; + } + } elseif ($my['options']['addIfNull']) { + $platforms = array( + 0 => '' + ); + } + return $platforms; + } + + /** + * Logic to determine if platforms should be visible for a given testplan. + * + * @return bool true if the testplan has one or more linked platforms; + * otherwise false. + */ + public function hasLinkedPlatforms($id) + { + return $this->platform_mgr->platformsActiveForTestplan($id); + } + + /** + * changes platform id on a test plan linked test case versions for + * a target platform. + * Corresponding executions information is also updated + * + * @param + * id: test plan id + * @param + * from: plaftorm id to update (used as filter criteria). + * @param + * to: new plaftorm id value + * @param + * tcversionSet: default null, can be array with tcversion id + * (used as filter criteria). + * + * + */ + public function changeLinkedTCVersionsPlatform($id, $from, $to, + $tcversionSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sqlFilter = ''; + if (! is_null($tcversionSet)) { + $sqlFilter = " AND tcversion_id IN (" . + implode(',', (array) $tcversionSet) . " ) "; + } + $whereClause = " WHERE testplan_id = {$id} AND platform_id = {$from} {$sqlFilter}"; + + $sqlStm = array(); + $sqlStm[] = "/* {$debugMsg} */ " . + " UPDATE {$this->tables['testplan_tcversions']} " . + " SET platform_id = {$to} " . $whereClause; + + $sqlStm[] = "/* {$debugMsg} */" . + " UPDATE {$this->tables['executions']} " . + " SET platform_id = {$to} " . $whereClause; + + foreach ($sqlStm as $sql) { + $this->db->exec_query($sql); + } + } + + /** + * + * @param + * id: test plan id + * @param + * platformSet: default null, used as filter criteria. + * @return map: key platform id, values count,platform_id + */ + public function countLinkedTCVersionsByPlatform($id, $platformSet = null) + { + $sqlFilter = ''; + if (! is_null($platformSet)) { + $sqlFilter = " AND platform_id IN (" . + implode(',', (array) $platformSet) . ") "; + } + $sql = " SELECT COUNT(testplan_id) AS qty,platform_id " . + " FROM {$this->tables['testplan_tcversions']} " . + " WHERE testplan_id={$id} {$sqlFilter} " . " GROUP BY platform_id "; + return $this->db->fetchRowsIntoMap($sql, 'platform_id'); + } + + /** + */ + public function getStatusForReports() + { + // This will be used to create dynamically counters if user add new status + foreach ($this->resultsCfg['status_label_for_exec_ui'] as $tc_status_verbose => $label) { + $code_verbose[$this->resultsCfg['status_code'][$tc_status_verbose]] = $tc_status_verbose; + } + if (! isset($this->resultsCfg['status_label_for_exec_ui']['not_run'])) { + $code_verbose[$this->resultsCfg['status_code']['not_run']] = 'not_run'; + } + return $code_verbose; + } + + /** + * getTestCaseSiblings() + * + * @internal revisions + */ + private function getTestCaseSiblings($id, $tcversion_id, $platform_id, + $opt = null) + { + $my['opt'] = array( + 'assigned_to' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = " SELECT NHTSET.name as testcase_name,NHTSET.id AS testcase_id , NHTCVSET.id AS tcversion_id," . + " NHTC.parent_id AS testsuite_id, " . + " TPTCVX.id AS feature_id, TPTCVX.node_order, TCV.tc_external_id " . + " from {$this->tables['testplan_tcversions']} TPTCVMAIN " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCVMAIN.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTSET ON NHTSET.parent_id = NHTC.parent_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCVSET ON NHTCVSET.parent_id = NHTSET.id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCVSET.id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCVX " . + " ON TPTCVX.tcversion_id = NHTCVSET.id " . + " AND TPTCVX.testplan_id = TPTCVMAIN.testplan_id " . + " AND TPTCVX.platform_id = TPTCVMAIN.platform_id "; + + if (! is_null($my['opt']['assigned_to'])) { + $user_id = intval($my['opt']['assigned_to']['user_id']); + $build_id = intval($my['opt']['assigned_to']['build_id']); + + $addJoin = " /* Analise user assignment to get sibling */ " . + " JOIN {$this->tables['user_assignments']} UAMAIN " . + " ON UAMAIN.feature_id = TPTCVMAIN.id " . + " AND UAMAIN.build_id = " . $build_id . " AND UAMAIN.user_id = " . + $user_id . " AND UAMAIN.type = {$this->execTaskCode} " . + " JOIN {$this->tables['user_assignments']} UAX " . + " ON UAX.feature_id = TPTCVX.id " . " AND UAX.build_id = " . + $build_id . " AND UAX.user_id = " . $user_id . + " AND UAX.type = {$this->execTaskCode} "; + $sql .= $addJoin; + } + + $sql .= " WHERE TPTCVMAIN.testplan_id = {$id} AND TPTCVMAIN.tcversion_id = {$tcversion_id} " . + " AND TPTCVMAIN.platform_id = {$platform_id} " . + " ORDER BY node_order,tc_external_id "; + + return $this->db->fetchRowsIntoMap($sql, 'tcversion_id'); + } + + /** + * getTestCaseNextSibling() + * + * @used-by execSetResults.php + * + */ + public function getTestCaseNextSibling($id, $tcversion_id, $platform_id, + $opt = null) + { + $my['opt'] = array( + 'move' => 'forward', + 'scope' => 'local' + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sibling = null; + switch ($my['opt']['scope']) { + case 'world': + $tptcv = $this->tables['testplan_tcversions']; + + $subq = " SELECT node_order FROM {$this->tables['testplan_tcversions']} TX " . + " WHERE TX.testplan_id = {$id} AND " . + " TX.tcversion_id = {$tcversion_id} "; + + if ($platform_id > 0) { + $subq .= " AND TX.platform_id = {$platform_id} "; + } + $sql = " SELECT tcversion_id,node_order " . " FROM {$tptcv} TZ " . + " WHERE TZ.testplan_id = {$id} AND " . + " TZ.tcversion_id <> {$tcversion_id} "; + if ($platform_id > 0) { + $sql .= " AND TZ.platform_id = {$platform_id} "; + } + + $sql .= " ORDER BY TZ.node_order >= ($subq) "; + break; + + case 'local': + default: + $sib = $this->getTestCaseSiblings($id, $tcversion_id, + $platform_id, $my['opt']); + break; + } + $tcversionSet = array_keys($sib); + $elemQty = count($tcversionSet); + $dummy = array_flip($tcversionSet); + + $pos = $dummy[$tcversion_id]; + switch ($my['opt']['move']) { + case 'backward': + $pos --; + $pos = $pos < 0 ? 0 : $pos; + break; + + case 'forward': + default: + $pos ++; + break; + } + + $sibling_tcversion = $pos < $elemQty ? $tcversionSet[$pos] : 0; + if ($sibling_tcversion > 0) { + $sibling = array( + 'tcase_id' => $sib[$sibling_tcversion]['testcase_id'], + 'tcversion_id' => $sibling_tcversion + ); + } + return $sibling; + } + + /** + * Convert a given urgency and importance to a priority level using + * threshold values in $tlCfg->priority_levels. + * + * @param mixed $urgency + * Urgency of the testcase. + * If this is the only parameter given then interpret it as + * $urgency*$importance. + * @param mixed $importance + * Importance of the testcase. (Optional) + * + * @return int HIGH, MEDIUM or LOW + */ + public function urgencyImportanceToPriorityLevel($urgency, + $importance = null) + { + $urgencyImportance = intval($urgency) * + (is_null($importance) ? 1 : intval($importance)); + return priority_to_level($urgencyImportance); + } + + /** + * create XML string with following structure + * + * + * + * + * + * + * + * + * + * + * ... + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ... + * + * + * + * + */ + public function exportLinkedItemsToXML($id) + { + $item_info = $this->get_by_id($id); + + // Linked platforms + $xml_root = "{{XMLCODE}}\n"; + + // ||yyy||-> tags, {{xxx}} -> attribute + // tags and attributes receive different treatment on exportDataToXML() + // + // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl + // + $xml_template = "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + ""; + + $xml_mapping = null; + $xml_mapping = array( + "||PLATFORMNAME||" => "platform_name", + "||PLATFORMID||" => 'id' + ); + + $mm = (array) $this->platform_mgr->getLinkedToTestplanAsMap($id); + $loop2do = count($mm); + if ($loop2do > 0) { + $items2loop = array_keys($mm); + foreach ($items2loop as $itemkey) { + $mm[$itemkey] = array( + 'platform_name' => $mm[$itemkey], + 'id' => $itemkey + ); + } + } + $linked_platforms = exportDataToXML($mm, $xml_root, $xml_template, + $xml_mapping, true); + + // Linked test cases + $xml_root = "\n{{XMLCODE}}\n"; + $xml_template = "\n\t" . "" . "\n" . "\t\t" . "" . "\n" . + "\t\t\t" . "" . "\n" . + "\t\t" . "" . "\n" . "\t\t" . "" . "\n" . + "\t\t\t" . "\n" . "\t\t\t" . + "\n" . "\t\t\t" . + "\n" . "\t\t\t" . + "\n" . + "\t\t" . "" . "\n" . "" . "\n" . + $xml_mapping = array( + "||PLATFORMNAME||" => "platform_name", + "||EXTERNALID||" => "external_id", + "||NAME||" => "name", + "||VERSION||" => "version", + "||EXECUTION_ORDER||" => "execution_order" + ); + + $mm = $this->getLinkedStaticView($id, null, array( + 'output' => 'array' + )); + $linked_testcases = exportDataToXML($mm, $xml_root, $xml_template, + $xml_mapping, true); + + $item_info['linked_platforms'] = $linked_platforms; + $item_info['linked_testcases'] = $linked_testcases; + $xml_root = "\n\t{{XMLCODE}}\n\t"; + $xml_template = "\n\t\t" . "" . + "\n" . "\t\t||LINKED_PLATFORMS||\n" . "\t\t||LINKED_TESTCASES||\n"; + + $xml_mapping = null; + $xml_mapping = array( + "||TESTPLANNAME||" => "name", + "||LINKED_PLATFORMS||" => "linked_platforms", + "||LINKED_TESTCASES||" => "linked_testcases" + ); + + return exportDataToXML(array( + $item_info + ), $xml_root, $xml_template, $xml_mapping); + } + + /** + * create XML string with following structure + * + * + * + * @param + * mixed context: map with following keys + * platform_id: MANDATORY + * build_id: OPTIONAL + * tproject_id: OPTIONAL + */ + public function exportTestPlanDataToXML($id, $context, $optExport = array()) + { + if (! isset($context['tproject_id']) || is_null($context['tproject_id'])) { + $dummy = $this->tree_manager->get_node_hierarchy_info($id); + $context['tproject_id'] = $dummy['parent_id']; + } + $context['tproject_id'] = intval($context['tproject_id']); + + // CRITIC - this has to be firt population of item_info. + // Other processes adds info to this map. + $item_info = $this->get_by_id($id); + + // Need to get family + $nt2exclude = array( + 'testplan' => 'exclude_me', + 'requirement_spec' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + $nt2exclude_children = array( + 'testcase' => 'exclude_my_children', + 'requirement_spec' => 'exclude_my_children' + ); + + $my = array(); + + // this can be a litte weird but ... + // when + // 'order_cfg' => array("type" =>'exec_order' + // additional info test plan id, and platform id are used to get + // a filtered view of tree. + // + $order_cfg = array( + "type" => 'exec_order', + "tplan_id" => $id + ); + if ($context['platform_id'] > 0) { + $order_cfg['platform_id'] = $context['platform_id']; + } + $my['options'] = array( + 'recursive' => true, + 'order_cfg' => $order_cfg, + 'remove_empty_nodes_of_type' => $this->tree_manager->node_descr_id['testsuite'] + ); + $my['filters'] = array( + 'exclude_node_types' => $nt2exclude, + 'exclude_children_of' => $nt2exclude_children + ); + $tplan_spec = $this->tree_manager->get_subtree($context['tproject_id'], + $my['filters'], $my['options']); + + // Generate test project info + $tproject_mgr = new testproject($this->db); + $tproject_info = $tproject_mgr->get_by_id($context['tproject_id']); + + // ||yyy||-> tags, {{xxx}} -> attribute + // tags and attributes receive different treatment on exportDataToXML() + // + // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl + // + $xml_template = "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + ""; + + $xml_root = "{{XMLCODE}}"; + $xml_mapping = null; + $xml_mapping = array( + "||TESTPROJECTNAME||" => "name", + "||TESTPROJECTPREFIX||" => "prefix", + "||TESTPROJECTID||" => 'id' + ); + $mm = array(); + $mm[$context['tproject_id']] = array( + 'name' => $tproject_info['name'], + 'prefix' => $tproject_info['prefix'], + 'id' => $context['tproject_id'] + ); + $item_info['testproject'] = exportDataToXML($mm, $xml_root, + $xml_template, $xml_mapping, true); + + // get target platform (if exists) + $target_platform = ''; + if ($context['platform_id'] > 0) { + $info = $this->platform_mgr->getByID($context['platform_id']); + // ||yyy||-> tags, {{xxx}} -> attribute + // tags and attributes receive different treatment on exportDataToXML() + // + // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl + // + $xml_template = "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + ""; + + $xml_root = "{{XMLCODE}}"; + $xml_mapping = null; + $xml_mapping = array( + "||PLATFORMNAME||" => "platform_name", + "||PLATFORMID||" => 'id' + ); + + $mm = array(); + $mm[$context['platform_id']] = array( + 'platform_name' => $info['name'], + 'id' => $context['platform_id'] + ); + $item_info['target_platform'] = exportDataToXML($mm, $xml_root, + $xml_template, $xml_mapping, true); + $target_platform = "\t\t||TARGET_PLATFORM||\n"; + } + + // get Build info (if possible) + $target_build = ''; + if (isset($context['build_id']) && $context['build_id'] > 0) { + $dummy = $this->get_builds($id); + $info = $dummy[$context['build_id']]; + + // ||yyy||-> tags, {{xxx}} -> attribute + // tags and attributes receive different treatment on exportDataToXML() + // + // each UPPER CASE word in this map is a KEY, that MUST HAVE AN OCCURENCE on $elemTpl + // + $xml_template = "\n\t" . "" . "\t\t" . + "" . "\t\t" . + "" . "\n\t" . + ""; + + $xml_root = "{{XMLCODE}}"; + $xml_mapping = null; + $xml_mapping = array( + "||BUILDNAME||" => "name", + "||BUILDID||" => 'id' + ); + + $mm = array(); + $mm[$context['build_id']] = array( + 'name' => $info['name'], + 'id' => $context['build_id'] + ); + $item_info['target_build'] = exportDataToXML($mm, $xml_root, + $xml_template, $xml_mapping, true); + $target_build = "\t\t||TARGET_BUILD||\n"; + } + + // get test plan contents (test suites and test cases) + $item_info['testsuites'] = null; + if (! is_null($tplan_spec) && isset($tplan_spec['childNodes']) && + ! empty($tplan_spec['childNodes'])) { + $item_info['testsuites'] = '' . + $this->exportTestSuiteDataToXML($tplan_spec, + $context['tproject_id'], $id, $context['platform_id'], + $context['build_id']) . ''; + } + + $xml_root = "\n\t{{XMLCODE}}\n\t"; + $xml_template = "\n\t\t" . "" . + "\n" . "\t\t||TESTPROJECT||\n" . $target_platform . $target_build . + "\t\t||TESTSUITES||\n"; + + $xml_mapping = null; + $xml_mapping = array( + "||TESTPLANNAME||" => "name", + "||TESTPROJECT||" => "testproject", + "||TARGET_PLATFORM||" => "target_platform", + "||TARGET_BUILD||" => "target_build", + "||TESTSUITES||" => "testsuites" + ); + + return exportDataToXML(array( + $item_info + ), $xml_root, $xml_template, $xml_mapping); + } + + /** + */ + private function exportTestSuiteDataToXML($container, $tproject_id, + $tplan_id, $platform_id, $build_id) + { + static $keywordMgr; + static $tcaseMgr; + static $tsuiteMgr; + static $tcaseExportOptions; + static $linkedItems; + + if (is_null($keywordMgr)) { + $tcaseExportOptions = array( + 'CFIELDS' => true, + 'KEYWORDS' => true, + 'EXEC_ORDER' => 0 + ); + $keywordMgr = new tlKeyword(); + $tsuiteMgr = new testsuite($this->db); + $linkedItems = $this->getLinkedItems($tplan_id); + } + + $xmlTC = null; + $cfXML = null; + $kwXML = null; + + if (isset($container['id'])) { + $kwMap = $tsuiteMgr->getKeywords($container['id']); + if ($kwMap) { + $kwXML = "" . $keywordMgr->toXMLString($kwMap, true) . + ""; + } + + $cfMap = (array) $tsuiteMgr->get_linked_cfields_at_design( + $container['id'], null, null, $tproject_id); + if (count($cfMap) > 0) { + $cfXML = $this->cfield_mgr->exportValueAsXML($cfMap); + } + + $tsuiteData = $tsuiteMgr->get_by_id($container['id']); + $xmlTC = "\n\t' . + "\n\t\t" . + "\n\t\t
    " . + "\n\t\t{$kwXML}{$cfXML}
    "; + } + $childNodes = isset($container['childNodes']) ? $container['childNodes'] : null; + if (! is_null($childNodes)) { + $loop_qty = count($childNodes); + for ($idx = 0; $idx < $loop_qty; $idx ++) { + $cNode = $childNodes[$idx]; + switch ($cNode['node_table']) { + case 'testsuites': + $xmlTC .= $this->exportTestSuiteDataToXML($cNode, + $tproject_id, $tplan_id, $platform_id, $build_id); + break; + + case 'testcases': + if (is_null($tcaseMgr)) { + $tcaseMgr = new testcase($this->db); + } + // testcase::LATEST_VERSION, + $tcaseExportOptions['EXEC_ORDER'] = $linkedItems[$cNode['id']][$platform_id]['node_order']; + + $filter_lv = array( + 'exec_status' => 'ALL', + 'active_status' => 'ALL', + 'tplan_id' => $tplan_id, + 'platform_id' => $platform_id + ); + $output_lv = array( + 'output' => 'simple' + ); + // get tc versions linked in current testplan for current platform + $info = $tcaseMgr->get_linked_versions($cNode['id'], + $filter_lv, $output_lv); + if (! is_null($info)) { + $tcversID = key($info); + } + + // get users assigned to tc version in current testplan for the current build + $versionAssignInfo = $tcaseMgr->getVersionExecAssignment( + $tcversID, $tplan_id, $build_id); + $userList = array(); + // extract user names + if (! is_null($versionAssignInfo)) { + foreach ($versionAssignInfo[$tcversID][$platform_id] as $vaInfo) { + $assignedTesterId = intval($vaInfo['user_id']); + if ($assignedTesterId) { + $user = tlUser::getByID($this->db, + $assignedTesterId); + if ($user) { + $userList[] = $user->getDisplayName(); + } + } + } + } + (! empty($userList)) ? $tcaseExportOptions['ASSIGNED_USER'] = $userList : $tcaseExportOptions['ASSIGNED_USER'] = null; + + $xmlTC .= $tcaseMgr->exportTestCaseDataToXML( + $cNode['id'], $cNode['tcversion_id'], $tproject_id, + testcase::NOXMLHEADER, $tcaseExportOptions); + break; + } + } + } + + if (isset($container['id'])) { + $xmlTC .= "
    "; + } + return $xmlTC; + } + + /** + */ + private function getFeatureAssignments($tplan_id, $filters = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ "; + + $my['filters'] = array( + 'build' => null, + 'tcversion' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $sql .= " SELECT COALESCE(UA.user_id,-1) AS user_id, " . + " TPTCV.id AS feature_id, B.id AS build_id, TPTCV.platform_id " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id AND UA.build_id = B.id " . + " WHERE TPTCV.testplan_id={$tplan_id} "; + + if (! is_null($my['filters']['build'])) { + $sql .= " AND B.id IN (" . + implode(',', (array) $my['filters']['build']) . ") "; + } + if (! is_null($my['filters']['tcversion'])) { + $sql .= " AND TPTCV.tcversion_id IN (" . + implode(',', (array) $my['filters']['tcversion']) . ") "; + } + + return $this->db->fetchMapRowsIntoMap($sql, 'feature_id', 'build_id'); + } + + /** + * getSkeleton + * + * get structure with Test suites and Test Cases + * Filters that act on test cases work on attributes that are common to all + * test cases versions: test case name + * + * Development Note: + * Due to the tree structure is not so easy to try to do as much as filter as + * possibile using SQL. + * + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getSkeleton($id, $tprojectID, $filters = null, + $options = null) + { + $items = array(); + $my['options'] = array( + 'recursive' => false, + 'exclude_testcases' => false, + 'remove_empty_branches' => false + ); + + $my['filters'] = array( + 'exclude_node_types' => $this->nt2exclude, + 'exclude_children_of' => $this->nt2exclude_children, + 'exclude_branches' => null, + 'testcase_name' => null, + 'testcase_id' => null, + 'execution_type' => null, + 'platform_id' => null, + 'additionalWhereClause' => null + ); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + if ($my['options']['exclude_testcases']) { + $my['filters']['exclude_node_types']['testcase'] = 'exclude me'; + } + + // transform some of our options/filters on something the 'worker' will understand + // when user has request filter by test case name, we do not want to display empty branches + + // If we have choose any type of filter, we need to force remove empty test suites + // + if (! is_null($my['filters']['testcase_name']) || + ! is_null($my['filters']['testcase_id']) || + ! is_null($my['filters']['execution_type']) || + ! is_null($my['filters']['exclude_branches']) || + ! is_null($my['filters']['platform_id']) || + $my['options']['remove_empty_branches']) { + $my['options']['remove_empty_nodes_of_type'] = 'testsuite'; + } + + $method2call = $my['options']['recursive'] ? 'getSubtreeRec' : '_get_subtree'; + $tcaseSet = array(); + if ($my['options']['recursive']) { + $this->$method2call($id, $tprojectID, $items, $tcaseSet, + $my['filters'], $my['options']); + } else { + $this->$method2call($id, $tprojectID, $items, $my['filters'], + $my['options']); + } + return array( + $items, + $tcaseSet + ); + } + + /** + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + private function getSubtreeRec($tplan_id, $node_id, &$pnode, &$itemSet, + $filters = null, $options = null) + { + static $qnum; + static $my; + static $exclude_branches; + static $exclude_children_of; + static $node_types; + static $tcaseFilter; + static $tcversionFilter; + static $staticSql; + static $debugMsg; + + if (! $my) { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $qnum = 0; + $node_types = array_flip( + $this->tree_manager->get_available_node_types()); + $my['filters'] = array( + 'exclude_children_of' => null, + 'exclude_branches' => null, + 'additionalWhereClause' => '', + 'testcase_name' => null, + 'platform_id' => null, + 'testcase_id' => null, + 'active_testcase' => false + ); + + $my['options'] = array( + 'remove_empty_nodes_of_type' => null + ); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + $exclude_branches = $my['filters']['exclude_branches']; + $exclude_children_of = $my['filters']['exclude_children_of']; + + $tcaseFilter['name'] = ! is_null($my['filters']['testcase_name']); + $tcaseFilter['id'] = ! is_null($my['filters']['testcase_id']); + + $tcaseFilter['is_active'] = ! is_null( + $my['filters']['active_testcase']) && + $my['filters']['active_testcase']; + $tcaseFilter['enabled'] = $tcaseFilter['name'] || $tcaseFilter['id'] || + $tcaseFilter['is_active']; + + $tcversionFilter['execution_type'] = ! is_null( + $my['filters']['execution_type']); + $tcversionFilter['enabled'] = $tcversionFilter['execution_type']; + + // this way I can manage code or description + if (! is_null($my['options']['remove_empty_nodes_of_type']) && + ! is_numeric($my['options']['remove_empty_nodes_of_type'])) { + $my['options']['remove_empty_nodes_of_type'] = $this->tree_manager->node_descr_id[$my['options']['remove_empty_nodes_of_type']]; + } + + $platformFilter = ""; + if (! is_null($my['filters']['platform_id']) && + $my['filters']['platform_id'] > 0) { + $platformFilter = " AND T.platform_id = " . + intval($my['filters']['platform_id']); + } + + // Create invariant sql sentences + $staticSql[0] = " /* $debugMsg - Get ONLY TestSuites */ " . + " SELECT NHTS.node_order AS spec_order," . + " NHTS.node_order AS node_order, NHTS.id, NHTS.parent_id," . + " NHTS.name, NHTS.node_type_id, 0 AS tcversion_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTS" . + " WHERE NHTS.node_type_id = {$this->tree_manager->node_descr_id['testsuite']} " . + " AND NHTS.parent_id = "; + $staticSql[1] = " /* $debugMsg - Get ONLY Test Cases with version linked to (testplan,platform) */ " . + " SELECT NHTC.node_order AS spec_order, " . + " TPTCV.node_order AS node_order, NHTC.id, NHTC.parent_id, " . + " NHTC.name, NHTC.node_type_id, TPTCV.tcversion_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTC " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.parent_id = NHTC.id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTCV.id " . + " WHERE NHTC.node_type_id = {$this->tree_manager->node_descr_id['testcase']} " . + " AND TPTCV.testplan_id = " . intval($tplan_id) . + " {$platformFilter} " . " AND NHTC.parent_id = "; + } + + $target = intval($node_id); + $sql = $staticSql[0] . $target . " UNION " . $staticSql[1] . $target; + + if ($tcaseFilter['enabled']) { + foreach ($tcaseFilter as $key => $apply) { + if ($apply) { + switch ($key) { + case 'name': + $sql .= " AND NHTC.name LIKE '%{$my['filters']['testcase_name']}%' "; + break; + + case 'id': + $sql .= " AND NHTC.id = {$my['filters']['testcase_id']} "; + break; + } + } + } + } + + $sql .= " ORDER BY node_order,id"; + + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + if (null == $rs || count($rs) == 0) { + return $qnum; + } + + foreach ($rs as $row) { + if (! isset($exclude_branches[$row['id']])) { + $node = $row + + array( + 'node_type' => $this->tree_manager->node_types[$row['node_type_id']], + 'node_table' => $this->tree_manager->node_tables_by['id'][$row['node_type_id']] + ); + $node['childNodes'] = null; + + if ($node['node_table'] == 'testcases') { + $node['leaf'] = true; + $node['external_id'] = ''; + $itemSet['nindex'][] = $node['id']; + } + + // why we use exclude_children_of ? + // 1. Sometimes we don't want the children if the parent is a testcase, + // due to the version management + // + if (! isset( + $exclude_children_of[$node_types[$row['node_type_id']]])) { + // Keep walking (Johny Walker Whisky) + $this->getSubtreeRec($tplan_id, $row['id'], $node, $itemSet, + $my['filters'], $my['options']); + } + + // Have added this logic, because when export test plan will be developed + // having a test spec tree where test suites that do not contribute to test plan + // are pruned/removed is very important, to avoid additional processing + // + // If node has no childNodes, we check if this kind of node without children + // can be removed. + // + $doRemove = is_null($node['childNodes']) && + ($node['node_type_id'] == + $my['options']['remove_empty_nodes_of_type']); + if (! $doRemove) { + $pnode['childNodes'][] = $node; + } + } + } + + return $qnum; + } + + /** + */ + private function getNotRunAllBuildsForPlatform($id, $platformID, + $buildSet = null) + { + // On Postgresql + // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, + // but not in the WHERE or HAVING clauses; there you must write out the expression instead. + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($safe_id, $buildsCfg,) = $this->helperGetHits($id, $platformID, + $buildSet); + + $sql = "/* $debugMsg */ " . + " SELECT count(0) AS COUNTER ,NHTCV.parent_id AS tcase_id " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . + " NHTCV.id = TPTCV.tcversion_id " . + " LEFT OUTER JOIN {$this->tables['executions']} E ON " . + " E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.build_id = B.id " . " WHERE TPTCV.testplan_id = " . + $safe_id['tplan'] . " AND TPTCV.platform_id " . $safe_id['platform'] . + " AND E.status IS NULL "; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $sql .= $groupBy . " HAVING COUNT(0) = " . intval($buildsCfg['count']); + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsNotRunForBuildAndPlatform($id, $platformID, $buildID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $safe_id['tplan'] = intval($id); + $safe_id['platform'] = intval($platformID); + $safe_id['build'] = intval($buildID); + + $sql = "/* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id, E.status, B.id AS build_id " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + " /* Needed to get TEST CASE ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Need to Get Execution Info on REQUESTED build set */ " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . " AND E.build_id = B.id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND TPTCV.platform_id = " . $safe_id['platform'] . " AND B.id = " . + $safe_id['build'] . " AND E.status IS NULL "; + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + private function getNotRunAtLeastOneBuildForPlatform($id, $platformID, + $buildSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list (, $buildsCfg,) = $this->helperGetHits($id, $platformID, $buildSet); + + $sql = "/* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id, E.status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Needed to get TEST CASE ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Need to Get Execution Info on REQUESTED build set */ " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.build_id = B.id " . + " AND E.build_in IN ({$buildsCfg['inClause']}) " . + " WHERE TPTCV.testplan_id = $id " . + " AND TPTCV.platform_id={$platformID} " . + " AND E.build_in IN ({$buildsCfg['inClause']}) " . + " AND E.status IS NULL "; + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * returns recordset with test cases that has requested status + * (only statuses that are written to DB => this does not work for not run) + * for LAST EXECUTION on build Set provided, for a platform. + * + * FULL means that we have to have SAME STATUS on all builds present on set. + * If build set is NOT PROVIDED, we will use ALL ACTIVE BUILDS + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsSingleStatusFull($id, $platformID, $status, + $buildSet = null) + { + // On Postgresql + // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, + // but not in the WHERE or HAVING clauses; there you must write out the expression instead. + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($safe_id, $buildsCfg, $sqlLEBBP) = $this->helperGetHits($id, + $platformID, $buildSet); + + $sql = " /* $debugMsg */ " . " /* Count() to be used on HAVING */ " . + " SELECT COUNT(0) AS COUNTER ,NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution by BUILD and PLATFORM */ " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.build_id = B.id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.tcversion_id = LEBBP.tcversion_id " . + " AND E.testplan_id = LEBBP.testplan_id " . + " AND E.platform_id = LEBBP.platform_id " . + " AND E.build_id = LEBBP.build_id " . " WHERE TPTCV.testplan_id = " . + $safe_id['tplan'] . " AND TPTCV.platform_id=" . $safe_id['platform'] . + " AND E.build_id IN ({$buildsCfg['inClause']}) " . " AND E.status ='" . + $this->db->prepare_string($status) . "'"; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $sql .= $groupBy . " HAVING COUNT(0) = " . intval($buildsCfg['count']); + + unset($safe_id, $buildsCfg, $sqlLEBBP); + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * getHitsNotRunFullOnPlatform($id,$platformID,$buildSet) + * + * returns recordset with: + * test cases with NOT RUN status on ALL builds in build set (full), for a platform. + * + * If build set is null + * test cases with NOT RUN status on ALL ACTIVE builds (full), for a platform. + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + private function getHitsNotRunFullOnPlatform($id, $platformID, + $buildSet = null) + { + // On Postgresql + // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, + // but not in the WHERE or HAVING clauses; there you must write out the expression instead. + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($safe_id, $buildsCfg,) = $this->helperGetHits($id, $platformID, + $buildSet); + + $sql = " /* $debugMsg */ " . " /* Count() to be used on HAVING */ " . + " SELECT COUNT(0) AS COUNTER ,NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . + " NHTCV.id = TPTCV.tcversion_id " . + " LEFT OUTER JOIN {$this->tables['executions']} E ON " . + " E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . " AND E.build_id = B.id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND TPTCV.platform_id = " . $safe_id['platform'] . + " AND E.status IS NULL "; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $sql .= $groupBy . " HAVING COUNT(0) = " . intval($buildsCfg['count']); + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * getHitsStatusSetFullOnPlatform($id,$platformID,$statusSet,$buildQty=0) + * + * returns recordset with: + * test cases that has at least ONE of requested status + * ON LAST EXECUTION ON ALL builds in set (full) , for a platform + * + * If build set is not provided, thena analisys will be done on + * ALL ACTIVE BUILDS + * + * + * IMPORTANT / CRITIC: This has NOT BE USED FOR NOT RUN, + * there is an special method for NOT RUN status. + * + * Example: + * + * Test Plan: PLAN B + * Builds: B1,B2,B3 + * Test Cases: TC-100, TC-200,TC-300 + * + * Test Case - Build - LAST Execution status + * TC-100 B1 Passed + * TC-100 B2 FAILED + * TC-100 B3 Not Run + * + * TC-200 B1 FAILED + * TC-200 B2 FAILED + * TC-200 B3 BLOCKED + * + * TC-300 B1 Passed + * TC-300 B2 Passed + * TC-300 B3 BLOCKED + * + * TC-400 B1 FAILED + * TC-400 B2 BLOCKED + * TC-400 B3 FAILED + * + * Request 1: + * Provide test cases with status (LAST EXECUTION) in ('Passed','BLOCKED') + * ON ALL ACTIVE Builds + * + * ANSWER: + * TC-300 + * + * Request 2: + * Provide test cases with status in ('FAILED','BLOCKED') + * ON ALL ACTIVE Builds + * + * ANSWER: + * TC-300, TC-400 + * + * @return + * + * @internal revisions + * @since 1.9.4 + * 20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches + */ + private function getHitsStatusSetFullOnPlatform($id, $platformID, $statusSet, + $buildSet = null) + { + + // On Postgresql + // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, + // but not in the WHERE or HAVING clauses; there you must write out the expression instead. + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + list ($safe_id, $buildsCfg, $sqlLEBBP) = $this->helperGetHits($id, + $platformID, $buildSet); + + $dummy = $this->sanitizeExecStatus((array) $statusSet); + $statusInClause = implode("','", $dummy); + + // ATTENTION: + // if I've requested (Passed or Blocked) on ALL BUILDS + // Have 2 results for build number. + // + // That logic is wrong when filtering for the SAME STATUS on ALL builds. + // Maybe copy/paste-error on refactoring? + // Example: With 3 builds and filtering for FAILED or BLOCKED on ALL builds + // we have to get 3 hits for each test case to be shown, not six hits. + // $countTarget = intval($buildsCfg['count']) * count($dummy); + $countTarget = intval($buildsCfg['count']); + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $sql = " /* $debugMsg */ " . " /* Count() to be used on HAVING */ " . + " SELECT COUNT(0) AS COUNTER ,NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution by BUILD and PLATFORM */ " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.build_id = B.id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.tcversion_id = LEBBP.tcversion_id " . + " AND E.testplan_id = LEBBP.testplan_id " . + " AND E.platform_id = LEBBP.platform_id " . + " AND E.build_id = LEBBP.build_id " . " WHERE TPTCV.testplan_id = " . + $safe_id['tplan'] . " AND TPTCV.platform_id=" . $safe_id['platform'] . + " AND E.build_id IN ({$buildsCfg['inClause']}) " . + " AND E.status IN ('{$statusInClause}')" . $groupBy . + " HAVING COUNT(0) = " . $countTarget; + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * getHitsNotRunPartialOnPlatform($id,$platformID,buildSet) + * + * returns recordset with: + * test cases with NOT RUN status at LEAST ON ONE off ALL ACTIVE builds (Partial), + * for a platform. + * + * Example: + * + * Test Plan: PLAN B + * Builds: B1,B2,B3 + * Test Cases: TC-100, TC-200,TC-300 + * + * Test Case - Build - LAST Execution status + * TC-100 B1 Passed + * TC-100 B2 FAILED + * TC-100 B3 Not Run => to have this status means THAT HAS NEVER EXECUTED ON B3 + * + * TC-200 B1 FAILED + * TC-200 B2 FAILED + * TC-200 B3 BLOCKED + * + * TC-300 B1 Passed + * TC-300 B2 Passed + * TC-300 B3 BLOCKED + * + * TC-400 B1 FAILED + * TC-400 B2 BLOCKED + * TC-400 B3 FAILED + * + * Request : + * Provide test cases with status 'NOT RUN' + * ON At Least ON OF all ACTIVE Builds + * + * ANSWER: + * TC-100 + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsNotRunPartialOnPlatform($id, $platformID, + $buildSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($safe_id, $buildsCfg,) = $this->helperGetHits($id, $platformID, + $buildSet); + + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . + " NHTCV.id = TPTCV.tcversion_id " . + " /* Executions, looking for status NULL (remember NOT RUN is not written on DB) */ " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . " AND E.build_id = B.id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND TPTCV.platform_id = " . $safe_id['platform'] . + " AND E.status IS NULL "; + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * getHitsStatusSetPartialOnPlatform($id,$platformID,$statusSet,$buildSet) + * + * returns recordset with: + * test cases that has at least ONE of requested status + * on LAST EXECUTION ON At Least ONE of builds present on Build Set (Partial), for a platform + * + * If build set is EMPTY + * on LAST EXECUTION ON At Least ONE of ALL ACTIVE builds (full), for a platform + * + * Example: + * + * Test Plan: PLAN B + * Builds: B1,B2,B3 + * Test Cases: TC-100, TC-200,TC-300 + * + * Test Case - Build - LAST Execution status + * TC-100 B1 Passed + * TC-100 B2 FAILED + * TC-100 B3 Not Run + * + * TC-200 B1 FAILED + * TC-200 B2 FAILED + * TC-200 B3 BLOCKED + * + * TC-300 B1 Passed + * TC-300 B2 Passed + * TC-300 B3 BLOCKED + * + * TC-400 B1 FAILED + * TC-400 B2 BLOCKED + * TC-400 B3 FAILED + * + * Request 1: + * Provide test cases with status in ('Passed','BLOCKED') + * ON At Least ONE, OF ALL ACTIVE Builds + * + * ANSWER: + * TC-200, TC300, TC400 + * + * Request 2: ???? + * Provide test cases with status in ('FAILED','BLOCKED') + * ON ALL ACTIVE Builds + * + * ANSWER: + * TC-300, TC-400 + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + private function getHitsStatusSetPartialOnPlatform($id, $platformID, + $statusSet, $buildSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $statusSet = $this->sanitizeExecStatus($statusSet); + $statusInClause = implode("','", $statusSet); + list ($safe_id, $buildsCfg, $sqlLEBBP) = $this->helperGetHits($id, + $platformID, $buildSet); + + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution by BUILD and PLATFORM */ " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.build_id = B.id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.tcversion_id = LEBBP.tcversion_id " . + " AND E.testplan_id = LEBBP.testplan_id " . + " AND E.platform_id = LEBBP.platform_id " . + " AND E.build_id = LEBBP.build_id " . " WHERE TPTCV.testplan_id = " . + $safe_id['tplan'] . " AND TPTCV.platform_id=" . $safe_id['platform'] . + " AND E.build_id IN ({$buildsCfg['inClause']}) " . + " AND E.status IN('{$statusInClause}') "; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $sql .= $groupBy; + + unset($safe_id, $buildsCfg, $sqlLEBBP); + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * getHitsSameStatusFullOnPlatform($id,$platformID,$statusSet,$buildSet) + * + * returns recordset with: + * test cases that has at least ONE of requested status + * ON LAST EXECUTION ON ALL builds on buils set (full) , for a platform + * + * If build set is NULL => ON LAST EXECUTION ON ALL ACTIVE builds (full), for a platform + */ + public function getHitsSameStatusFullOnPlatform($id, $platformID, $statusSet, + $buildSet = null) + { + $statusSet = $this->sanitizeExecStatus($statusSet); + + return $this->helperGetHitsSameStatusOnPlatform('full', $id, $platformID, + $statusSet, $buildSet); + } + + /** + * getHitsSameStatusFullALOP($id,$statusSet,$buildSet) + * + * returns recordset with: + * test cases that has at least ONE of requested status + * ON LAST EXECUTION ON ALL builds on buils set (full) , for a platform + * + * If build set is NULL => ON LAST EXECUTION ON ALL ACTIVE builds (full), for a platform + */ + public function getHitsSameStatusFullALOP($id, $statusSet, $buildSet = null, + $opt = null) + { + // On Postgresql + // An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, + // but not in the WHERE or HAVING clauses; there you must write out the expression instead. + $options = array( + 'onlyActiveBuilds' => true + ); + $options = array_merge($options, (array) $opt); + + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + list ($safe_id, $buildsCfg, $sqlLEX) = $this->helperGetHits($id, null, + $buildSet, array( + 'ignorePlatform' => true + )); + if ($options['onlyActiveBuilds']) { + $buildsCfg['statusClause'] = " AND B.active = 1 "; + } + + // TICKET 5226: Filtering by test result did not always show the correct matches + // The filtering for "not run" status was simply not implemented for the case + // of not using platforms. + // Maybe that part was forgotten when refactoring the filters. + // + // I adopted logic from helperGetHitsSameStatusOnPlatform() to get this working. + // + $flippedStatusSet = array_flip($statusSet); // (code => idx) + $get = array( + 'notRun' => isset($flippedStatusSet[$this->notRunStatusCode]), + 'otherStatus' => false + ); + $hits = array( + 'notRun' => array(), + 'otherStatus' => array() + ); + + if ($get['notRun']) { + $notRunSQL = " /* $debugMsg */ " . + " /* COUNT() is needed as parameter for HAVING clause */ " . + " SELECT COUNT(0) AS COUNTER, NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . + " NHTCV.id = TPTCV.tcversion_id " . + " LEFT OUTER JOIN {$this->tables['executions']} E ON " . + " E.testplan_id = TPTCV.testplan_id " . " AND E.build_id = B.id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND E.status IS NULL "; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $notRunSQL .= $groupBy . " HAVING COUNT(0) = " . + intval($buildsCfg['count']); + + $hits['notRun'] = $this->db->fetchRowsIntoMap($notRunSQL, 'tcase_id'); + + unset($statusSet[$flippedStatusSet[$this->notRunStatusCode]]); + } + + $get['otherStatus'] = ! empty($statusSet); + if ($get['otherStatus']) { + $statusSet = $this->sanitizeExecStatus($statusSet); + $statusInClause = implode("','", $statusSet); + + // ATTENTION: + // if I've requested (Passed or Blocked) on ALL BUILDS + // Have 2 results for build number. + + // That logic is wrong when filtering for the SAME STATUS on ALL builds. + // Maybe copy/paste-error on refactoring? + // Example: With 3 builds and filtering for FAILED or BLOCKED on ALL builds + // we have to get 3 hits for each test case to be shown, not six hits. + // $countTarget = intval($buildsCfg['count']) * count($statusSet); + $countTarget = intval($buildsCfg['count']); + + $otherStatusSQL = " /* $debugMsg */ " . + " /* Count() to be used on HAVING - ALOP */ " . + " SELECT COUNT(0) AS COUNTER ,tcase_id " . " FROM ( " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id, E.build_id " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution by BUILD IGNORE PLATFORM */ " . + " JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.build_id = B.id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEX.id " . + " AND E.tcversion_id = LEX.tcversion_id " . + " AND E.testplan_id = LEX.testplan_id " . + " AND E.build_id = LEX.build_id " . " WHERE TPTCV.testplan_id = " . + $safe_id['tplan'] . + " AND E.build_id IN ({$buildsCfg['inClause']}) " . + " AND E.status IN ('{$statusInClause}')" . " ) SQX "; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $otherStatusSQL .= $groupBy . " HAVING COUNT(0) = " . $countTarget; + + $hits['otherStatus'] = $this->db->fetchRowsIntoMap($otherStatusSQL, + 'tcase_id'); + } + + // build results record set + $hitsFoundOn = array(); + $hitsFoundOn['notRun'] = count($hits['notRun']) > 0; + $hitsFoundOn['otherStatus'] = count($hits['otherStatus']) > 0; + + if ($hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus']) { + $items = array_merge(array_keys($hits['notRun']), + array_keys($hits['otherStatus'])); + } elseif ($hitsFoundOn['notRun']) { + $items = array_keys($hits['notRun']); + } elseif ($hitsFoundOn['otherStatus']) { + $items = array_keys($hits['otherStatus']); + } + + return is_null($items) ? $items : array_flip($items); + } + + /** + * getHitsNotRunOnBuildPlatform($id,$platformID,$buildID) + * + * returns recordset with: + * test cases with NOT RUN status on SPECIFIC build for a PLATFORM. + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + private function getHitsNotRunOnBuildPlatform($id, $platformID, $buildID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = " /* $debugMsg */ " . " SELECT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . + " NHTCV.id = TPTCV.tcversion_id " . " /* Work on Executions */ " . + " LEFT OUTER JOIN {$this->tables['executions']} E ON " . + " E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . " AND E.build_id = " . + intval($buildID) . " WHERE TPTCV.testplan_id = " . intval($id) . + " AND TPTCV.platform_id = " . intval($platformID) . + " AND E.status IS NULL "; + + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + return is_null($recordset) ? $recordset : array_flip( + array_keys($recordset)); + } + + /** + * getHitsNotRunOnBuildALOP($id,$buildID) + * + * returns recordset with: + * test cases with NOT RUN status on SPECIFIC build On AT LEAST ONE PLATFORM. (ALOP) + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + private function getHitsNotRunOnBuildALOP($id, $buildID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . + " NHTCV.id = TPTCV.tcversion_id " . " /* Work on Executions */ " . + " LEFT OUTER JOIN {$this->tables['executions']} E ON " . + " E.testplan_id = TPTCV.testplan_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . " AND E.build_id = " . + intval($buildID) . " WHERE TPTCV.testplan_id = " . intval($id) . + " AND E.status IS NULL "; + + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + return is_null($recordset) ? $recordset : array_flip( + array_keys($recordset)); + } + + /** + * getHitsStatusSetOnBuildPlatform($id,$platformID,$buildID,$statusSet) + * + * returns recordset with: + * test cases with LAST EXECUTION STATUS on SPECIFIC build for a PLATFORM, IN status SET. + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsStatusSetOnBuildPlatform($id, $platformID, $buildID, + $statusSet) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + list ($safe_id, , $sqlLEBBP) = $this->helperGetHits($id, $platformID, + null, array( + 'buildID' => $buildID + )); + + $safe_id['build'] = intval($buildID); + $statusList = $this->sanitizeExecStatus((array) $statusSet); + + // Manage also not run + $notRunHits = null; + $dummy = array_flip($statusList); + if (isset($dummy[$this->notRunStatusCode])) { + tLog(__FUNCTION__ . ':: getHitsNotRunOnBuildPlatform', 'DEBUG'); + $notRunHits = $this->getHitsNotRunOnBuildPlatform($safe_id['tplan'], + $safe_id['platform'], $safe_id['build']); + unset($statusList[$dummy[$this->notRunStatusCode]]); + } + + $statusInClause = implode("','", $statusList); + $sql = " /* $debugMsg */ " . " SELECT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution by BUILD and PLATFORM */ " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.build_id = " . $safe_id['build'] . + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.tcversion_id = LEBBP.tcversion_id " . + " AND E.testplan_id = LEBBP.testplan_id " . + " AND E.platform_id = LEBBP.platform_id " . + " AND E.build_id = LEBBP.build_id " . " WHERE TPTCV.testplan_id = " . + $safe_id['tplan'] . " AND TPTCV.platform_id = " . + $safe_id['platform'] . " AND E.build_id = " . $safe_id['build'] . + " AND E.status IN('{$statusInClause}')"; + + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + $hits = is_null($recordset) ? $recordset : array_flip( + array_keys($recordset)); + + $items = (array) $hits + (array) $notRunHits; + return count($items) > 0 ? $items : null; + } + + /** + * getHitsStatusSetOnBuildALOP($id,$buildID,$statusSet) + * + * returns recordset with: + * test cases with LAST EXECUTION STATUS on SPECIFIC build for At Least One PLATFORM, + * IN status SET. + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsStatusSetOnBuildALOP($id, $buildID, $statusSet) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + list ($safe_id, , $sqlLEX) = $this->helperGetHits($id, null, null, + array( + 'buildID' => $buildID, + 'ignorePlatform' => true + )); + + $safe_id['build'] = intval($buildID); + $statusList = $this->sanitizeExecStatus((array) $statusSet); + + // Manage also not run + $notRunHits = null; + $dummy = array_flip($statusList); + if (isset($dummy[$this->notRunStatusCode])) { + $notRunHits = $this->getHitsNotRunOnBuildALOP($safe_id['tplan'], + $safe_id['build']); + unset($statusList[$dummy[$this->notRunStatusCode]]); + } + + $statusInClause = implode("','", $statusList); + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution by BUILD IGNORE PLATFORM */ " . + " JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.build_id = " . $safe_id['build'] . + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEX.id " . + " AND E.tcversion_id = LEX.tcversion_id " . + " AND E.testplan_id = LEX.testplan_id " . + " AND E.build_id = LEX.build_id " . " WHERE TPTCV.testplan_id = " . + $safe_id['tplan'] . " AND E.build_id = " . $safe_id['build'] . + " AND E.status IN('{$statusInClause}')"; + + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + $hits = is_null($recordset) ? $recordset : array_flip( + array_keys($recordset)); + + $items = (array) $hits + (array) $notRunHits; + return count($items) > 0 ? $items : null; + } + + /** + * getHitsStatusSetOnLatestExecALOP($id,$statusSet,$buildSet) + * + * returns recordset with: + * test cases that has at least ONE of requested status + * on ABSOLUTE LASTEST EXECUTION considering all builds on build set IGNORING platform. + * + * If build set is NULL, we will analyse ALL ACTIVE builds (full) IGNORING platform. + * + * IMPORTANT / CRITIC: THIS DOES NOT WORK for Not Run STATUS + * HAS NO SENSE, because Not Run IN NOT SAVED to DB + * => we can not find LATEST NON RUN + * Example: + * + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsStatusSetOnLatestExecALOP($id, $statusSet, + $buildSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + list ($safe_id, $buildsCfg, $sqlLEX) = $this->helperGetHits($id, null, + $buildSet, array( + 'ignorePlatform' => true, + 'ignoreBuild' => true + )); + + // Check if 'not run' in present in statusSet => throw exception + $statusList = $this->sanitizeExecStatus((array) $statusSet); + $dummy = array_flip($statusList); + if (isset($dummy[$this->notRunStatusCode])) { + throw new Exception( + __METHOD__ . ':: Status Not Run can not be used'); + } + $statusInClause = implode("','", $statusList); + + $sql = " /* $debugMsg */ " . + " SELECT MAX(LEX.id) AS latest_exec_id ,NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution IGNORE BUILD, PLATFORM */ " . + " JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEX.id " . + " AND E.tcversion_id = LEX.tcversion_id " . + " AND E.testplan_id = LEX.testplan_id " . " AND E.build_id = B.id " . + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND E.build_id IN ({$buildsCfg['inClause']}) " . + " AND E.status IN('{$statusInClause}') "; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $sql .= $groupBy; + + unset($safe_id, $buildsCfg, $sqlLEX); + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + return is_null($recordset) ? $recordset : array_flip( + array_keys($recordset)); + } + + /** + * getHitsStatusSetOnLatestExecOnPlatform($id,$platformID,$statusSet,$buildSet) + * + * returns recordset with: + * test cases that has at least ONE of requested status + * on ABSOLUTE LASTEST EXECUTION considering all builds on build set, for a platform + * + * If build set is NULL, we will analyse ALL ACTIVE builds (full), for a platform + * + * IMPORTANT / CRITIC: THIS DOES NOT WORK for Not Run STATUS + * HAS NO SENSE, because Not Run IN NOT SAVED to DB + * => we can not find LATEST NON RUN + * Example: + * + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsStatusSetOnLatestExecOnPlatform($id, $platformID, + $statusSet, $buildSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + list ($safe_id, $buildsCfg, $sqlLEBP) = $this->helperGetHits($id, + $platformID, $buildSet, array( + 'ignoreBuild' => true + )); + + // Check if 'not run' in present in statusSet => throw exception + $statusList = $this->sanitizeExecStatus((array) $statusSet); + $dummy = array_flip($statusList); + if (isset($dummy[$this->notRunStatusCode])) { + throw new Exception( + __METHOD__ . ':: Status Not Run can not be used'); + } + $statusInClause = implode("','", $statusList); + + $sql = " /* $debugMsg */ " . + " SELECT MAX(LEBP.id) AS latest_exec_id ,NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution on PLATFORM IGNORE BUILD */ " . + " JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + // " AND LEBP.build_id = B.id " . + + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBP.id " . + " AND E.tcversion_id = LEBP.tcversion_id " . + " AND E.testplan_id = LEBP.testplan_id " . + " AND E.platform_id = LEBP.platform_id " . + // " AND E.build_id = LEBBP.build_id " . + + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND TPTCV.platform_id=" . $safe_id['platform'] . + " AND E.build_id IN ({$buildsCfg['inClause']}) " . + " AND E.status IN ('{$statusInClause}') "; + + $groupBy = ' GROUP BY ' . + ((DB_TYPE == 'mssql') ? 'parent_id ' : 'tcase_id'); + $sql .= $groupBy; + + unset($safe_id, $buildsCfg, $sqlLEBP); + $recordset = $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + return is_null($recordset) ? $recordset : array_flip( + array_keys($recordset)); + } + + /** + * getHitsSameStatusPartialOnPlatform($id,$platformID,$statusSet,$buildSet) + * + * returns recordset with: + * + * test cases that has at least ONE of requested status + * ON LAST EXECUTION ON AT LEAST ONE OF builds on build set, for a platform + * + * If build set is empty + * test cases that has at least ONE of requested status + * ON LAST EXECUTION ON AT LEAST ONE OF ALL ACTIVE builds, for a platform + */ + public function getHitsSameStatusPartialOnPlatform($id, $platformID, + $statusSet, $buildSet = null) + { + $statusSet = $this->sanitizeExecStatus((array) $statusSet); + return $this->helperGetHitsSameStatusOnPlatform('partial', $id, + $platformID, $statusSet, $buildSet); + } + + /** + * getHitsSameStatusPartialALOP($id,$statusSet) + * + * returns recordset with: + * + * test cases that has at least ONE of requested status + * ON LAST EXECUTION ON AT LEAST ONE OF builds on build set, for a platform + * + * If build set is empty + * test cases that has at least ONE of requested status + * ON LAST EXECUTION ON AT LEAST ONE OF ALL ACTIVE builds, for a platform + */ + public function getHitsSameStatusPartialALOP($id, $statusSet, + $buildSet = null) + { + $getHitsNotRunMethod = 'getHitsNotRunPartialALOP'; + $getHitsStatusSetMethod = 'getHitsStatusSetPartialALOP'; + + // Needed because, may be we will need to remove an element + $statusSetLocal = $this->sanitizeExecStatus((array) $statusSet); + + $items = null; + $hits = array( + 'notRun' => array(), + 'otherStatus' => array() + ); + $dummy = array_flip($statusSetLocal); // (code => idx) + $get = array( + 'notRun' => isset($dummy[$this->notRunStatusCode]), + 'otherStatus' => false + ); + + if ($get['notRun']) { + tLog(__METHOD__ . ":: \$tplan_mgr->$getHitsNotRunMethod", 'DEBUG'); + $hits['notRun'] = (array) $this->$getHitsNotRunMethod($id, $buildSet); + unset($statusSetLocal[$dummy[$this->notRunStatusCode]]); + } + + if ($get['otherStatus'] = (! empty($statusSetLocal))) { + tLog(__METHOD__ . ":: \$tplan_mgr->$getHitsStatusSetMethod", 'DEBUG'); + $hits['otherStatus'] = (array) $this->$getHitsStatusSetMethod($id, + $statusSetLocal, $buildSet); + } + + // build results recordset + $hitsFoundOn = array(); + $hitsFoundOn['notRun'] = count($hits['notRun']) > 0; + $hitsFoundOn['otherStatus'] = count($hits['otherStatus']) > 0; + + if ($get['notRun'] && $get['otherStatus']) { + if ($hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus']) { + $items = array_keys($hits['notRun']) + + array_keys($hits['otherStatus']); + } + } elseif ($get['notRun'] && $hitsFoundOn['notRun']) { + $items = array_keys($hits['notRun']); + } elseif ($get['otherStatus'] && $hitsFoundOn['otherStatus']) { + $items = array_keys($hits['otherStatus']); + } + + return is_null($items) ? $items : array_flip($items); + } + + /** + * getHitsStatusSetPartialALOP($id,$platformID,$statusSet,$buildSet) + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + private function getHitsStatusSetPartialALOP($id, $statusSet, + $buildSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $statusSet = $this->sanitizeExecStatus($statusSet); + $statusInClause = implode("','", $statusSet); + list ($safe_id, $buildsCfg, $sqlLEX) = $this->helperGetHits($id, null, + $buildSet, array( + 'ignorePlatform' => true + )); + + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Latest Execution by JUST BUILD IGNORE PLATFORM */ " . + " JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.build_id = B.id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + + // " AND LEX.platform_id = TPTCV.platform_id " . + + " /* Get STATUS INFO From Executions */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEX.id " . + " AND E.tcversion_id = LEX.tcversion_id " . + " AND E.testplan_id = LEX.testplan_id " . + " AND E.build_id = LEX.build_id " . + + // " AND E.platform_id = LEX.platform_id " . + + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND E.build_id IN ({$buildsCfg['inClause']}) " . + " AND E.status IN ('{$statusInClause}') "; + + unset($safe_id, $buildsCfg, $sqlLEX); + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * getHitsNotRunPartialALOP($id,buildSet) + * + * returns recordset with: + * + * test cases with NOT RUN status at LEAST ON ONE of builds + * present on build set (Partial), IGNORING Platforms + * + * If build set is empty: + * test cases with NOT RUN status at LEAST ON ONE of builds + * present on ACTIVE BUILDS set (Partial), IGNORING Platforms + * + * + * Example: (TO BE REWORKED) + * + * Test Plan: PLAN B + * Builds: B1,B2,B3 + * Test Cases: TC-100, TC-200,TC-300 + * + * Test Case - Build - LAST Execution status + * TC-100 B1 Passed + * TC-100 B2 FAILED + * TC-100 B3 Not Run => to have this status means THAT HAS NEVER EXECUTED ON B3 + * + * TC-200 B1 FAILED + * TC-200 B2 FAILED + * TC-200 B3 BLOCKED + * + * TC-300 B1 Passed + * TC-300 B2 Passed + * TC-300 B3 BLOCKED + * + * TC-400 B1 FAILED + * TC-400 B2 BLOCKED + * TC-400 B3 FAILED + * + * Request : + * Provide test cases with status 'NOT RUN' + * ON At Least ON OF all ACTIVE Builds + * + * ANSWER: + * TC-100 + * + * @return + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getHitsNotRunPartialALOP($id, $buildSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($safe_id, $buildsCfg,) = $this->helperGetHits($id, null, $buildSet, + array( + 'ignorePlatform' => true + )); + + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT NHTCV.parent_id AS tcase_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B ON B.testplan_id = TPTCV.testplan_id " . + $buildsCfg['statusClause'] . " /* Get Test Case ID */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON " . + " NHTCV.id = TPTCV.tcversion_id " . + " /* Executions, looking for status NULL (remember NOT RUN is not written on DB) */ " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . " AND E.build_id = B.id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " WHERE TPTCV.testplan_id = " . $safe_id['tplan'] . + " AND B.id IN ({$buildsCfg['inClause']}) " . " AND E.status IS NULL "; + + return $this->db->fetchRowsIntoMap($sql, 'tcase_id'); + } + + /** + * helperGetHitsSameStatusOnPlatform($mode,$id,$platformID,$statusSet,$buildSet) + * + * @internal revisions: + * 20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches + */ + private function helperGetHitsSameStatusOnPlatform($mode, $id, $platformID, + $statusSet, $buildSet = null) + { + switch ($mode) { + case 'partial': + $getHitsNotRunMethod = 'getHitsNotRunPartialOnPlatform'; + $getHitsStatusSetMethod = 'getHitsStatusSetPartialOnPlatform'; + + break; + + case 'full': + $getHitsNotRunMethod = 'getHitsNotRunFullOnPlatform'; + $getHitsStatusSetMethod = 'getHitsStatusSetFullOnPlatform'; + break; + } + + // Needed because, may be we will need to remove an element + $statusSetLocal = $this->sanitizeExecStatus($statusSet); + + $items = null; + $hits = array( + 'notRun' => array(), + 'otherStatus' => array() + ); + + $dummy = array_flip($statusSetLocal); // (code => idx) + $get = array( + 'notRun' => isset($dummy[$this->notRunStatusCode]), + 'otherStatus' => false + ); + + if ($get['notRun']) { + $hits['notRun'] = (array) $this->$getHitsNotRunMethod($id, + $platformID, $buildSet); + unset($statusSetLocal[$dummy[$this->notRunStatusCode]]); + } + if ($get['otherStatus'] = (! empty($statusSetLocal))) { + $hits['otherStatus'] = (array) $this->$getHitsStatusSetMethod($id, + $platformID, $statusSetLocal, $buildSet); + } + + // build results recordset + $hitsFoundOn = array(); + $hitsFoundOn['notRun'] = count($hits['notRun']) > 0; + $hitsFoundOn['otherStatus'] = count($hits['otherStatus']) > 0; + + // 20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches + // if($get['notRun'] && $get['otherStatus']) + // { + // if( $hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus'] ) + // The problem with this if clause: + // When $get['notRun'] && $get['otherStatus'] evaluated as TRUE but there were no hits + // in one of $hitsFoundOn['notRun'] or $hitsFoundOn['otherStatus'], then no results were returned at all. + + if ($hitsFoundOn['notRun'] && $hitsFoundOn['otherStatus']) { + // THIS DOES NOT WORK with numeric keys + // $items = array_merge(array_keys($hits['notRun']),array_keys($hits['otherStatus'])); + // $items = array_keys($hits['notRun']) + array_keys($hits['otherStatus']); + + // 20120919 - asimon - TICKET 5226: Filtering by test result did not always show the correct matches + // + // ATTENTION: Using the + operator instead of array_merge() for numeric keys is wrong! + // + // Quotes from documentation http://www.php.net/manual/en/function.array-merge.php: + // + // array_merge(): "If the input arrays have the same string keys, then the later value for that key + // will overwrite the previous one. If, however, the arrays contain numeric keys, + // the later value will not overwrite the original value, but will be appended." + // + // + operator: "The keys from the first array will be preserved. + // If an array key exists in both arrays, then the element from the first array will be used + // and the matching key's element from the second array will be ignored." + // + // That means if there were 5 results in $hits['notRun']) and 10 results in $hits['otherStatus']), + // the first 5 testcases from $hits['otherStatus']) were not in the result set because of the + operator. + // + // After using array_keys() we have numeric keys => we HAVE TO USE array_merge(). + $items = array_merge(array_keys($hits['notRun']), + array_keys($hits['otherStatus'])); + } elseif ($hitsFoundOn['notRun']) { + $items = array_keys($hits['notRun']); + } elseif ($hitsFoundOn['otherStatus']) { + $items = array_keys($hits['otherStatus']); + } + + return is_null($items) ? $items : array_flip($items); + } + + /** + * helperGetHits($id,$platformID,$buildSet,$options) + */ + protected function helperGetHits($id, $platformID, $buildSet = null, + $options = null) + { + $my['options'] = array( + 'buildID' => 0, + 'ignorePlatform' => false, + 'ignoreBuild' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $safe_id['tplan'] = intval($id); + $safe_id['platform'] = intval($platformID); + + $buildsCfg['statusClause'] = ""; + $buildsCfg['inClause'] = ""; + $buildsCfg['count'] = 0; + + if ($my['options']['buildID'] <= 0) { + if (is_null($buildSet)) { + $buildSet = array_keys( + $this->get_builds($id, self::ACTIVE_BUILDS)); + $buildsCfg['statusClause'] = " AND B.active = 1 "; + } + $buildsCfg['count'] = count($buildSet); + $buildsCfg['inClause'] = implode(",", $buildSet); + } else { + $buildsCfg['inClause'] = intval($my['options']['buildID']); + } + + $platformClause = " AND EE.platform_id = " . $safe_id['platform']; + $platformField = " ,EE.platform_id "; + if ($my['options']['ignorePlatform'] || $safe_id['platform'] == - 1) { // 20230826 + $platformClause = " "; + $platformField = " "; + } + + $buildField = " ,EE.build_id "; + if ($my['options']['ignoreBuild']) { + $buildField = " "; + } + + $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id {$platformField} {$buildField} , - MAX(EE.id) AS id - FROM {$this->tables['executions']} EE - WHERE EE.testplan_id = " . $safe_id['tplan'] . - " AND EE.build_id IN ({$buildsCfg['inClause']}) - $platformClause - GROUP BY EE.tcversion_id,EE.testplan_id - {$platformField} {$buildField} "; - - return array($safe_id,$buildsCfg,$sqlLEX); - } - - - /** - * - * - * - */ - function helperConcatTCasePrefix($id) - { - // Get test case prefix - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $io = $this->tree_manager->get_node_hierarchy_info($id); - - list($prefix,$garbage) = $this->tcase_mgr->getPrefix(null,$io['parent_id']); - $prefix .= $this->tcaseCfg->glue_character; - $concat = $this->db->db->concat("'{$prefix}'",'TCV.tc_external_id'); - - unset($io); - unset($garbage); - unset($prefix); - - return $concat; - } - - - /** - * - * - * - */ - function helperColumns($tplanID,&$filters,&$opt) - { - $safe_id = intval($tplanID); - - $join['tsuite'] = ''; - $join['builds'] = ''; - - $order_by['exec'] = ''; - - $fields['tcase'] = ''; - $fields['tsuite'] = ''; - $fields['priority'] = " (urgency * importance) AS priority "; - - - $fields['ua'] = " UA.build_id AS assigned_build_id, UA.user_id,UA.type,UA.status,UA.assigner_id "; - - $default_fields['exec'] = " E.id AS exec_id, E.tcversion_number," . - " E.tcversion_id AS executed, E.testplan_id AS exec_on_tplan, {$more_exec_fields}" . - " E.execution_type AS execution_run_type, " . - " E.execution_ts, E.tester_id, E.notes as execution_notes," . - " E.build_id as exec_on_build, "; - - $fields['exec'] = $default_fields['exec']; - if($opt['execution_details'] == 'add_build') - { - $fields['exec'] .= 'E.build_id,B.name AS build_name, B.active AS build_is_active,'; - } - if( is_null($opt['forced_exec_status']) ) - { - $fields['exec'] .= " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status "; - } - else - { - $fields['exec'] .= " '{$opt['forced_exec_status']}' AS exec_status "; - } - - switch($opt['details']) - { - case 'full': - $fields['tcase'] = 'TCV.summary,'; - $fields['tsuite'] = 'NH_TSUITE.name as tsuite_name,'; - $join['tsuite'] = " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . - " ON NH_TCASE.parent_id = NH_TSUITE.id "; - $opt['steps_info'] = true; - break; - - - case 'summary': - $fields['tcase'] = 'TCV.summary,'; - break; - - case 'spec_essential': // TICKET 4710 - $fields['exec'] = ''; - $fields['ua'] = ''; - $join['builds'] = ''; - $filters['ua'] = ''; - break; - - - case 'exec_tree_optimized': // TICKET 4710 - // if all following filters are NOT USED, then we will REMOVE executions JOIN - if( $filters['builds'] == '' && $filters['executions'] == '') - { - $join['builds'] = ''; - $join['executions'] = ''; - - $fields['exec'] = ''; - $fields['ua'] = ''; - - $filters['executions'] = ''; - $filters['ua'] = ''; - $order_by['exec'] = ''; - } - break; - - case 'report': // Results Performance - $fields['ua'] = ''; - $filters['ua'] = ''; - break; - } - - if( !is_null($opt['exclude_info']) ) - { - foreach($opt['exclude_info'] as $victim) - { - switch($victim) - { - case 'exec_info': - $fields['exec'] = ''; - $order_by['exec'] = " "; - $join['executions'] = ''; - break; - - case 'priority': - $fields['priority'] = ''; - break; - - case 'assigned_on_build': - case 'assigned_to': - $fields['ua'] = ''; - $filters['ua'] = ''; - break; - - } - - } - - } - - $fullEID = $this->helperConcatTCasePrefix($safe_id); - $sql = " SELECT NH_TCASE.parent_id AS testsuite_id, {$fields['tcase']} {$fields['tsuite']} " . - " NH_TCV.parent_id AS tc_id, NH_TCASE.node_order AS z, NH_TCASE.name," . - " TPTCV.platform_id, PLAT.name as platform_name ,TPTCV.id AS feature_id, " . - " TPTCV.tcversion_id AS tcversion_id, " . - " TPTCV.node_order AS execution_order, TPTCV.creation_ts AS linked_ts, " . - " TPTCV.author_id AS linked_by,TPTCV.urgency," . - " TCV.version AS version, TCV.active, TCV.tc_external_id AS external_id, " . - " TCV.execution_type,TCV.importance," . - " $fullEID AS full_external_id"; - - $dummy = array('exec','priority','ua'); - foreach($dummy as $ki) - { - $sql .= ($fields[$ki] != '' ? ',' . $fields[$ki] : ''); - } - - if( $fields['ua'] != '' ) - { - $join['ua'] = " LEFT OUTER JOIN {$this->tables['user_assignments']} UA ON " . - " UA.feature_id = TPTCV.id " . - " AND UA.build_id IN (" . $this->helperBuildInClause($tplanID,$filters,$opt) . ")"; - } - - - return array($sql,$join,$order_by); - } - - - /** - * - * - * - */ - function helperLastExecution($tplanID,$filters,$options) - { - $safe_id = intval($tplanID); - - $filterBuildActiveStatus = ''; - $activeStatus = null; - $domain = array('active' => 1, 'inactive' => 0 , 'any' => null); - if( !is_null($domain[$options['build_active_status']]) ) - { - $activeStatus = intval($domain[$options['build_active_status']]); - $filterBuildActiveStatus = " AND BB.active = " . $activeStatus; - } - - $buildsInClause = $this->helperBuildInClause($tplanID,$filters,$options); - - // Last Executions By Build and Platform (LEBBP) - $sqlLEBBP = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " /* use builds table to filter on active status */ " . - " JOIN {$this->tables['builds']} BB " . - " ON BB.id = EE.build_id " . - " WHERE EE.testplan_id=" . $safe_id . - " AND EE.build_id IN ({$buildsInClause}) " . - $filterBuildActiveStatus . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; - - unset($dummy); - unset($buildsInClause); - unset($filterBuildActiveStatus); - - return $sqlLEBBP; - } - - - - /** - * - * - * - */ - function helperBuildInClause($tplanID,$filters,$options) - { - $safe_id = intval($tplanID); - if(!is_null($filters['builds'])) - { - $dummy = $filters['builds']; - } - else - { - $activeStatus = null; - $domain = array('active' => 1, 'inactive' => 0 , 'any' => null); - if( !is_null($domain[$options['build_active_status']]) ) - { - $activeStatus = intval($domain[$options['build_active_status']]); - } - $dummy = array_keys($this->get_builds($safe_id,$activeStatus)); - } - - return implode(",",$dummy); - } - - - /** - * - * - * - */ - function helperBuildActiveStatus($filters,$options) - { - $activeStatus = null; - $domain = array('active' => 1, 'inactive' => 0 , 'any' => null); - if( !is_null($domain[$options['build_active_status']]) ) - { - $activeStatus = intval($domain[$options['build_active_status']]); - } - - return $activeStatus; - } - - - // This method is intended to return minimal data useful - // to create Execution Tree. - // Status on Latest execution on Build,Platform is needed - // - // @param int $id test plan id - // @param mixed $filters - // @param mixed $options - // - // [tcase_id]: default null => get any testcase - // numeric => just get info for this testcase - // - // - // [keyword_id]: default 0 => do not filter by keyword id - // numeric/array() => filter by keyword id - // - // - // [assigned_to]: default NULL => do not filter by user assign. - // array() with user id to be used on filter - // IMPORTANT NOTICE: this argument is affected by - // [assigned_on_build] - // - // [build_id]: default 0 or null => do not filter by build id - // numeric => filter by build id - // - // - // [cf_hash]: default null => do not filter by Custom Fields values - // - // - // [urgencyImportance] : filter only Tc's with certain (urgency*importance)-value - // - // [tsuites_id]: default null. - // If present only tcversions that are children of this testsuites - // will be included - // - // [exec_type] default null -> all types. - // [platform_id] - // - function getLinkedForExecTree($id,$filters=null,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - - $safe['tplan_id'] = intval($id); - $my = $this->initGetLinkedForTree($safe['tplan_id'],$filters,$options); - - - if( $my['filters']['build_id'] <= 0 ) - { - // CRASH IMMEDIATELY - throw new Exception( $debugMsg . " Can NOT WORK with \$my['filters']['build_id'] <= 0"); - } - - if( !$my['green_light'] ) - { - // No query has to be run, because we know in advance that we are - // going to get NO RECORDS - return null; - } - - $platform4EE = " "; - if( !is_null($my['filters']['platform_id']) ) - { - $platform4EE = " AND EE.platform_id = " . intval($my['filters']['platform_id']); - } - - $sqlLEBBP = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id = " . $safe['tplan_id'] . - " AND EE.build_id = " . intval($my['filters']['build_id']) . - $platform4EE . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; - - - // When there is request to filter by BUG ID, because till now (@20131216) BUGS are linked - // only to EXECUTED test case versions, the not_run piece of union is USELESS - $union['not_run'] = null; - - // if(isset($my['filters']['bug_id']) - - if(!isset($my['filters']['bug_id'])) - { - // adding tcversion on output can be useful for Filter on Custom Field values, - // because we are saving values at TCVERSION LEVEL - // - $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . - // $fullEIDClause . - " TCV.tc_external_id AS external_id, " . - " TPTCV.node_order AS exec_order," . - " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status " . - $my['fields']['tsuites'] . - - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['ua'] . - $my['join']['keywords'] . - $my['join']['cf'] . - $my['join']['tsuites'] . - - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.testplan_id = " . $safe['tplan_id'] . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = " . $my['filters']['build_id'] . - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['not_run'] . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEBBP.id IS NULL"; - } - - - $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . - // $fullEIDClause . - " TCV.tc_external_id AS external_id, " . - " TPTCV.node_order AS exec_order," . - " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status " . - $my['fields']['tsuites'] . - - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['ua'] . - $my['join']['keywords'] . - $my['join']['cf'] . - $my['join']['tsuites'] . - - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . - - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.testplan_id = " . $safe['tplan_id'] . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . // TICKET 5191 - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = " . $my['filters']['build_id'] . - - $my['join']['bugs'] . // need to be here because uses join with E table alias - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['where']; - - return (is_null($union['not_run']) ? $union['exec'] : $union); - } - - - /* - * - * @used-by - * getLinkedForExecTree() - * getLinkedForTesterAssignmentTree() - * getLinkedTCVersionsSQL() - * getLinkedForExecTreeCross() - * getLinkedForExecTreeIVU() - * - * filters => - * 'tcase_id','keyword_id','assigned_to','exec_status','build_id', - * 'cf_hash','urgencyImportance', 'tsuites_id','platform_id', - * 'exec_type','tcase_name' - * - * - * CRITIC: - * cf_hash can contains Custom Fields that are applicable to DESIGN and - * TESTPLAN_DESIGN. - * - * Here we are generating SQL that will be used ON TESTPLAN - * related tables NOT ON TEST SPEC related tables. - * Due to this we are going to consider while building - * the query ONLY CF for TESTPLAN DESING - * - */ - function initGetLinkedForTree($tplanID,$filtersCfg,$optionsCfg) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $dummy = array('exec_type','tc_id','builds','keywords','executions','platforms'); - - $ic['fields']['tsuites'] = ''; - - $ic['join'] = array(); - $ic['join']['ua'] = ''; - $ic['join']['bugs'] = ''; - $ic['join']['cf'] = ''; - $ic['join']['tsuites'] = ''; - - - $ic['where'] = array(); - $ic['where']['where'] = ''; - $ic['where']['platforms'] = ''; - $ic['where']['not_run'] = ''; - $ic['where']['cf'] = ''; - - $ic['green_light'] = true; - $ic['filters'] = array('tcase_id' => null, 'keyword_id' => 0, - 'assigned_to' => null, 'exec_status' => null, - 'build_id' => 0, 'cf_hash' => null, - 'urgencyImportance' => null, - 'tsuites_id' => null, - 'platform_id' => null, 'exec_type' => null, - 'tcase_name' => null); - - $ic['options'] = array('hideTestCases' => 0, - 'include_unassigned' => false, - 'allow_empty_build' => 0, - 'addTSuiteOrder' => false, - 'addImportance' => false, 'addPriority' => false); - $ic['filters'] = array_merge($ic['filters'], (array)$filtersCfg); - $ic['options'] = array_merge($ic['options'], (array)$optionsCfg); - - - $ic['filters']['build_id'] = intval($ic['filters']['build_id']); - - if($ic['options']['addTSuiteOrder']) { - // PREFIX ALWAYS with COMMA - $ic['fields']['tsuites'] = ', NH_TSUITE.node_order AS tsuite_order '; - $ic['join']['tsuites'] = - " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . - " ON NH_TSUITE.id = NH_TCASE.parent_id "; - } - - // This NEVER HAPPENS for Execution Tree, but if we want to reuse - // this method for Tester Assignment Tree, we need to add this check - // - if( !is_null($ic['filters']['platform_id']) && $ic['filters']['platform_id'] > 0) { - $ic['filters']['platform_id'] = intval($ic['filters']['platform_id']); - $ic['where']['platforms'] = " AND TPTCV.platform_id = {$ic['filters']['platform_id']} "; - } - - - $ic['where']['where'] .= $ic['where']['platforms']; - - $dk = 'exec_type'; - if( !is_null($ic['filters'][$dk]) ) { - $ic['where'][$dk]= " AND TCV.execution_type IN (" . - implode(",",(array)$ic['filters'][$dk]) . " ) "; - $ic['where']['where'] .= $ic['where'][$dk]; - } - - $dk = 'tcase_id'; - if (!is_null($ic['filters'][$dk]) ) { - if( is_array($ic['filters'][$dk]) ) { - $ic['where'][$dk] = " AND NH_TCV.parent_id IN (" . implode(',',$ic['filters'][$dk]) . ")"; - } - else if ($ic['filters'][$dk] > 0) { - $ic['where'][$dk] = " AND NH_TCV.parent_id = " . intval($ic['filters'][$dk]); - } - else { - // Best Option on this situation will be signal - // that query will fail => NO SENSE run the query - $ic['green_light'] = false; - } - $ic['where']['where'] .= $ic['where'][$dk]; - } - - if (!is_null($ic['filters']['tsuites_id'])) { - $dummy = (array)$ic['filters']['tsuites_id']; - $ic['where']['where'] .= " AND NH_TCASE.parent_id IN (" . implode(',',$dummy) . ")"; - } - - if (!is_null($ic['filters']['urgencyImportance'])) { - $ic['where']['where'] .= $this->helper_urgency_sql($ic['filters']['urgencyImportance']); - } - - if( !is_null($ic['filters']['keyword_id']) ) { - - list($ic['join']['keywords'],$ic['where']['keywords']) = - $this->helper_keywords_sql($ic['filters']['keyword_id'],array('output' => 'array')); - - // **** // CHECK THIS CAN BE NON OK - $ic['where']['where'] .= $ic['where']['keywords']; - } - - - // If special user id TL_USER_ANYBODY is present in set of user id, - // we will DO NOT FILTER by user ID - if( !is_null($ic['filters']['assigned_to']) && - !in_array(TL_USER_ANYBODY,(array)$ic['filters']['assigned_to']) ) { - list($ic['join']['ua'],$ic['where']['ua']) = - $this->helper_assigned_to_sql($ic['filters']['assigned_to'], - $ic['options'],$ic['filters']['build_id']); - - $ic['where']['where'] .= $ic['where']['ua']; - - } - - if( isset($ic['options']['assigned_on_build']) && - !is_null($ic['options']['assigned_on_build']) ) { - $ic['join']['ua'] = - " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id = " . $ic['options']['assigned_on_build'] . - " AND UA.type = {$this->execTaskCode} "; - } - - - if( !is_null($ic['filters']['tcase_name']) && - ($dummy = trim($ic['filters']['tcase_name'])) != '' ) { - $ic['where']['where'] .= " AND NH_TCASE.name LIKE '%{$dummy}%' "; - } - - - // Custom fields on testplan_design ONLY => AFFECTS run and NOT RUN. - if( isset($ic['filters']['cf_hash']) && !is_null($ic['filters']['cf_hash']) ) { - $ic['where']['cf'] = ''; - - list($ic['filters']['cf_hash'],$cf_sql) = $this->helperTestPlanDesignCustomFields($ic['filters']['cf_hash']); - - if(strlen(trim($cf_sql)) > 0) { - $ic['where']['cf'] .= " AND ({$cf_sql}) "; - $ic['join']['cf'] = - " JOIN {$this->tables['cfield_testplan_design_values']} CFTPD " . - " ON CFTPD.link_id = TPTCV.id "; - } - $ic['where']['where'] .= $ic['where']['cf']; - } - - - // I've made the choice to create the not_run key, - // to manage the not_run part of UNION on getLinkedForExecTree(). - // - // ATTENTION: - // on other methods: - // getLinkedForTesterAssignmentTree() - // getLinkedTCVersionsSQL() - // Still is used $ic['where']['where'] on BOTH components of UNION - // - // TICKET 5566: "Assigned to" does not work in "test execution" page - // TICKET 5572: Filter by Platforms - Wrong test case state count in test plan execution - $ic['where']['not_run'] = $ic['where']['where']; - - - // ************************************************************************ - // CRITIC - CRITIC - CRITIC - // Position on code flow is CRITIC - // CRITIC - CRITIC - CRITIC - // ************************************************************************ - if (!is_null($ic['filters']['exec_status'])) { - // $ic['where']['not_run'] = $ic['where']['where']; - $dummy = (array)$ic['filters']['exec_status']; - - $ic['where']['where'] .= " AND E.status IN ('" . implode("','",$dummy) . "')"; - - if( in_array($this->notRunStatusCode,$dummy) ) { - $ic['where']['not_run'] .= ' AND E.status IS NULL '; - } - else { - $ic['where']['not_run'] = $ic['where']['where']; - } - } - - // BUG ID HAS NO EFFECT ON NOT RUN (at least @20140126) - // bug_id => will be a list to create an IN() clause - if( isset($ic['filters']['bug_id']) && !is_null($ic['filters']['bug_id']) ) { - list($ic['join']['bugs'],$ic['where']['bugs']) = $this->helper_bugs_sql($ic['filters']['bug_id']); - $ic['where']['where'] .= $ic['where']['bugs']; - } - - return $ic; - } - - - /** - * - */ - function helperTestPlanDesignCustomFields($cfSet) - { - $type_domain = $this->cfield_mgr->get_available_types(); - $ret = null; - $cf_type = null; - foreach($cfSet as $id => $val) - { - $xx = $this->cfield_mgr->get_by_id($id); - if( $xx[$id]['enable_on_testplan_design'] ) - { - $ret[$id] = $val; - $cf_type[$id] = $type_domain[$xx[$id]['type']]; - } - } - - - $cf_sql = ''; - if( !is_null($ret) ) - { - $countmain = 1; - foreach( $ret as $cf_id => $cf_value) - { - if ( $countmain != 1 ) - { - $cf_sql .= " AND "; - } - - if (is_array($cf_value)) - { - $count = 1; - switch($cf_type[$cf_id]) - { - case 'multiselection list': - // - if( count($cf_value) > 1) - { - $combo = implode('|',$cf_value); - $cf_sql .= "( CFTPD.value = '{$combo}' AND CFTPD.field_id = {$cf_id} )"; - } - else - { - // close set, open set, is sandwiched, is alone - //$cf_sql .= "( (CFTPD.value LIKE '%|{$cf_value[0]}' AND CFTPD.field_id = {$cf_id}) OR " . - // " (CFTPD.value LIKE '{$cf_value[0]}|%' AND CFTPD.field_id = {$cf_id}) OR " . - // " (CFTPD.value LIKE '%|{$cf_value[0]}|%' AND CFTPD.field_id = {$cf_id}) OR " . - // " (CFTPD.value = '{$cf_value[0]}' AND CFTPD.field_id = {$cf_id}) )"; - - $cf_sql .= "( CFTPD.field_id = {$cf_id} AND " . - " (CFTPD.value LIKE '%|{$cf_value[0]}' OR " . - " CFTPD.value LIKE '{$cf_value[0]}|%' OR " . - " CFTPD.value LIKE '%|{$cf_value[0]}|%' OR " . - " CFTPD.value = '{$cf_value[0]}') )"; - } - break; - - default: - foreach ($cf_value as $value) - { - if ($count > 1) - { - $cf_sql .= " AND "; - } - - // When ARRAY NO LIKE but EQUAL - // Need to document what type of CF are managed as ARRAY - $cf_sql .= "( CFTPD.value = '{$value}' AND CFTPD.field_id = {$cf_id} )"; - $count++; - } - break; - } - - } - else - { - $cf_sql .= " ( CFTPD.value LIKE '%{$cf_value}%' AND CFTPD.field_id = {$cf_id} ) "; - } - $countmain++; - } - } - - return array($ret,$cf_sql); - } - - - - - - // This method is intended to return minimal data useful to create Test Plan Tree, - // for feature: - // test case tester execution assignment: - // PLATFORM IS NOT USED TO NAVIGATE => is not present on Settings Section. - // ONLY BUILD IS PRESENT on settings area - // - // - // Status on Latest execution on Build ANY PLATFORM is needed - // - // @param int $id test plan id - // @param mixed $filters - // @param mixed $options - // - // [tcase_id]: default null => get any testcase - // numeric => just get info for this testcase - // - // - // [keyword_id]: default 0 => do not filter by keyword id - // numeric/array() => filter by keyword id - // - // - // [assigned_to]: default NULL => do not filter by user assign. - // array() with user id to be used on filter - // IMPORTANT NOTICE: this argument is affected by - // [assigned_on_build] - // - // [build_id]: default 0 or null => do not filter by build id - // numeric => filter by build id - // - // - // [cf_hash]: default null => do not filter by Custom Fields values - // - // - // [urgencyImportance] : filter only Tc's with certain (urgency*importance)-value - // - // [tsuites_id]: default null. - // If present only tcversions that are children of this testsuites - // will be included - // - // [exec_type] default null -> all types. - // - function getLinkedForTesterAssignmentTree($id,$filters=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe['tplan_id'] = intval($id); - - $my = $this->initGetLinkedForTree($safe['tplan_id'],$filters,$options); - - // Need to detail better, origin of build_id. - // is got from GUI Filters area ? - if( ($my['options']['allow_empty_build'] == 0) && $my['filters']['build_id'] <= 0 ) - { - // CRASH IMMEDIATELY - throw new Exception( $debugMsg . " Can NOT WORK with \$my['filters']['build_id'] <= 0"); - } - if( !$my['green_light'] ) - { - // No query has to be run, because we know in advance that we are - // going to get NO RECORDS - return null; - } - - $buildClause = array('lex' => (' AND EE.build_id = ' . $my['filters']['build_id']), - 'exec_join' => (" AND E.build_id = " . $my['filters']['build_id'])); - if( $my['options']['allow_empty_build'] && $my['filters']['build_id'] <= 0 ) - { - $buildClause = array('lex' => '','exec_join' => ''); - } - - // - // Platforms have NOTHING TO DO HERE - $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.build_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id = " . $safe['tplan_id'] . - $buildClause['lex'] . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.build_id "; - - // ------------------------------------------------------------------------------------- - // adding tcversion on output can be useful for Filter on Custom Field values, - // because we are saving values at TCVERSION LEVEL - // - $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . - " TCV.tc_external_id AS external_id, " . - " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status " . - - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['ua'] . - $my['join']['keywords'] . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.testplan_id = " . $safe['tplan_id'] . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.id = LEX.id " . // 20120903 - $buildClause['exec_join'] . - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['where'] . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEX.id IS NULL"; - - - $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . - " TCV.tc_external_id AS external_id, " . - " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status " . - - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['ua'] . - $my['join']['keywords'] . - - " JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.testplan_id = " . $safe['tplan_id'] . - " JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.id = LEX.id " . // 20120903 - $buildClause['exec_join'] . - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['where']; - - return $union; - } - - - /** - * - * - */ - function getLinkInfo($id,$tcase_id,$platform_id=null,$opt=null) - { - $debugMsg = 'Class: ' . __CLASS__ . ' - Method:' . __FUNCTION__; - $safe_id = array('tplan_id' => 0, 'platform_id' => 0, 'tcase_id' => 0); - $safe_id['tplan_id'] = intval($id); - $safe_id['tcase_id'] = intval($tcase_id); - - // check and die? - $my = array('opt' => array('output' => 'version_info','tproject_id' => null, - 'build4assignment' => null, 'collapse' => false)); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = "/* $debugMsg */ " . - " SELECT TCV.id AS tcversion_id,TCV.version %%needle%% " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id "; - - $more_cols = ' '; - switch($my['opt']['output']) - { - case 'tcase_info': - if(is_null($my['opt']['tproject_id'])) - { - $dummy = $this->tree_manager->get_node_hierarchy_info($safe_id['tplan_id']); - $my['opt']['tproject_id'] = $dummy['parent_id']; - } - $pp = $this->tcase_mgr->getPrefix($safe_id['tcase_id'],$my['opt']['tproject_id']); - $prefix = $pp[0] . $this->tcaseCfg->glue_character; - $more_cols = ', NHTC.name, NHTC.id AS tc_id, ' . - $this->db->db->concat("'{$prefix}'",'TCV.tc_external_id') . ' AS full_external_id '; - - $sql .= " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id "; - break; - - case 'assignment_info': - if(is_null($my['opt']['build4assignment'])) - { - // CRASH IMMEDIATELY - throw new Exception(__METHOD__ . - ' When your choice is to get assignment_info ' . - " you need to provide build id using 'build4assignment'"); - } - // Go ahead - $safe_id['build_id'] = intval($my['opt']['build4assignment']); - - $more_cols = ',USERS.login,USERS.first,USERS.last' . - ',TPTCV.id AS feature_id,TPTCV.platform_id,PLAT.name AS platform_name' . - ',NHTCV.parent_id AS tc_id,UA.user_id,TCV.importance,TPTCV.urgency' . - ',(TCV.importance * TPTCV.urgency) AS priority '; - $sql .= " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . - " ON UA.build_id = " . $safe_id['build_id'] . - " AND UA.feature_id = TPTCV.id "; - - $sql .= " LEFT OUTER JOIN {$this->tables['platforms']} PLAT " . - " ON PLAT.id = TPTCV.platform_id "; - - $sql .= " LEFT OUTER JOIN {$this->tables['users']} USERS " . - " ON USERS.id = UA.user_id "; - - break; - - - case 'version_info': - $more_cols = ',TPTCV.platform_id'; - default: - break; - } - $sql = str_replace('%%needle%%',$more_cols,$sql) . - " WHERE TPTCV.testplan_id = {$safe_id['tplan_id']} " . - " AND NHTCV.parent_id = {$safe_id['tcase_id']} "; - - if( !is_null($platform_id) ) - { - if( ($safe_id['platform_id'] = intval($platform_id)) > 0) - { - $sql .= " AND TPTCV.platform_id = " . $safe_id['platform_id']; - } - } - - $rs = $this->db->get_recordset($sql); - if(!is_null($rs)) - { - $rs = $my['opt']['collapse'] ? $rs[0] : $rs; - } - return $rs; - } - - - - /** - * @used-by printDocument.php - * testplan.class.exportLinkedItemsToXML() - * testplan.class.exportForResultsToXML - */ - public function getLinkedStaticView($id,$filters=null,$options=null) - { - $debugMsg = 'Class: ' . __CLASS__ . ' - Method:' . __FUNCTION__; - $my = array('filters' => '', 'options' => ''); - - $my['filters'] = array('platform_id' => null,'tsuites_id' => null, - 'tcaseSet' => null, 'build_id' => null); - $my['filters'] = array_merge($my['filters'],(array)$filters); - - $my['options'] = array('output' => 'map','order_by' => null, 'detail' => 'full'); - $my['options'] = array_merge($my['options'],(array)$options); - - $safe['tplan'] = intval($id); - $io = $this->tree_manager->get_node_hierarchy_info($safe['tplan']); - list($prefix,$garbage) = $this->tcase_mgr->getPrefix(null,$io['parent_id']); - unset($io); - $prefix .= $this->tcaseCfg->glue_character; - $feid = $this->db->db->concat("'{$prefix}'",'TCV.tc_external_id'); - - - $addWhere = array('platform' => '','tsuite' => '', 'tcases' => '', 'build' => ''); - $platQty = 0; - if( !is_null($my['filters']['platform_id']) ) - { - $dummy = (array)$my['filters']['platform_id']; - array_walk($dummy,'intval'); - $addWhere['platform'] = 'AND TPTCV.platform_id IN (' . implode(',',$dummy) . ')'; - $platQty = count((array)$my['filters']['platform_id']); - } - - if( !is_null($my['filters']['tsuites_id']) ) - { - $dummy = (array)$my['filters']['tsuites_id']; - array_walk($dummy,'intval'); - $addWhere['tsuite'] = 'AND NH_TCASE.parent_id IN (' . implode(',',$dummy) . ')'; - } - - if( !is_null($my['filters']['tcaseSet']) ) - { - $dummy = (array)$my['filters']['tcaseSet']; - array_walk($dummy,'intval'); - $addWhere['tsuite'] = 'AND NH_TCASE.id IN (' . implode(',',$dummy) . ')'; - } - - $join['build'] = ''; - $addField = '-1 AS assigned_to, '; - if( !is_null($my['filters']['build_id']) ) - { - $dummy = intval($my['filters']['build_id']); - $addWhere['build'] = 'AND UA.build_id =' . $dummy; - - $join['build'] = " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id "; - - $addField = " UA.user_id AS assigned_to,"; - } - - - - switch($my['options']['detail']) - { - case '4results': - $my['options']['output'] = 'array'; // FORCED - // have had some issues with query and ADODB on MySQL if only - // $sql = " SELECT NH_TCV.parent_id AS tc_id, {$feid} AS full_external_id,TCV.tc_external_id "; - // Need to understand why in future - $sql = "/* $debugMsg */ " . - " SELECT {$addField} NH_TCV.parent_id AS tc_id, TPTCV.platform_id, TPTCV.id AS feature_id, " . - " TCV.tc_external_id AS external_id, {$feid} AS full_external_id, TPTCV.tcversion_id "; - break; - - case 'full': - default: - $sql = "/* $debugMsg */ " . - " SELECT {$addField} NH_TCASE.parent_id AS testsuite_id, NH_TCV.parent_id AS tc_id, " . - " NH_TCASE.node_order AS spec_order, NH_TCASE.name," . - " TPTCV.platform_id, PLAT.name as platform_name, TPTCV.id AS feature_id, " . - " TPTCV.tcversion_id AS tcversion_id, " . - " TPTCV.node_order AS execution_order, TPTCV.urgency," . - " TCV.version AS version, TCV.active, TCV.summary," . - " TCV.tc_external_id AS external_id, TCV.execution_type,TCV.importance," . - " {$feid} AS full_external_id, (TPTCV.urgency * TCV.importance) AS priority "; - break; - } - - $sql .=" FROM {$this->tables['nodes_hierarchy']} NH_TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCV.parent_id = NH_TCASE.id " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NH_TCV.id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH_TCV.id " . - $join['build'] . - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id "; - - $sql .= " WHERE TPTCV.testplan_id={$safe['tplan']} " . - " {$addWhere['platform']} {$addWhere['tsuite']} {$addWhere['build']}"; - - - switch($my['options']['output']) - { - case 'array': - $rs = $this->db->get_recordset($sql); - break; - - case 'map': - if($platQty == 1) - { - $rs = $this->db->fetchRowsIntoMap($sql,'tc_id',0,-1,'assigned_to'); - } - else - { - $rs = $this->db->fetchMapRowsIntoMap($sql,'platform_id','tc_id'); - } - break; - } - - return $rs; - } - - - // need to recheck, because probably we need to be able - // to work without build id provided - // has to be based on TREE USED on features like: - // assign test case execution or set test case urgency - // - public function getLTCVNewGeneration($id,$filters=null,$options=null) - { - $debugMsg = 'Class: ' . __CLASS__ . ' - Method:' . __FUNCTION__; - $my = array('filters' => array(), - 'options' => array('allow_empty_build' => 1,'addPriority' => false, - 'accessKeyType' => 'tcase+platform', - 'addImportance' => false,'addExecInfo' => true, - 'assigned_on_build' => null, - 'ua_user_alias' => '', 'includeNotRun' => true, - 'ua_force_join' => false, - 'orderBy' => null)); - $amk = array('filters','options'); - foreach($amk as $mk) - { - $my[$mk] = array_merge($my[$mk], (array)$$mk); - } - - if( !is_null($sql2do = $this->getLinkedTCVersionsSQL($id,$my['filters'],$my['options'])) ) - { - // need to document better - if( is_array($sql2do) ) - { - $sql2run = $sql2do['exec']; - if($my['options']['includeNotRun']) - { - $sql2run .= ' UNION ' . $sql2do['not_run']; - } - } - else - { - $sql2run = $sql2do; - } - - // added when trying to fix: - // TICKET 5788: test case execution order not working on RIGHT PANE - // Anyway this did not help - if( !is_null($my['options']['orderBy']) ) - { - $sql2run = " SELECT * FROM ($sql2run) XX ORDER BY " . $my['options']['orderBy']; - } - - switch($my['options']['accessKeyType']) - { - case 'tcase+platform': - $tplan_tcases = $this->db->fetchMapRowsIntoMap($sql2run,'tcase_id','platform_id'); // ,0,-1,'user_id'); - break; - - case 'tcase+platform+stackOnUser': - $tplan_tcases = $this->db->fetchMapRowsIntoMapStackOnCol($sql2run,'tcase_id','platform_id','user_id'); - break; - - case 'index': - $tplan_tcases = $this->db->get_recordset($sql2run); - break; - - default: - $tplan_tcases = $this->db->fetchRowsIntoMap($sql2run,'tcase_id'); - break; - } - } - return $tplan_tcases; - } - - - - - /** - * - * @used-by testplan::getLTCVNewGeneration() - * @use initGetLinkedForTree() - * - * @parameter map filters - * keys: - * 'tcase_id','keyword_id','assigned_to','exec_status','build_id', 'cf_hash', - * 'urgencyImportance', 'tsuites_id','platform_id', 'exec_type','tcase_name' - * filters defaults values are setted on initGetLinkedForTree() - * - * @parameter map options - * some defaults are managed here - * - * defaults for keys: 'hideTestCases','include_unassigned','allow_empty_build' - * are setted on initGetLinkedForTree(). - * - * - * - * @internal revisions - * @since 1.9.13 - */ - function getLinkedTCVersionsSQL($id,$filters=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe['tplan_id'] = intval($id); - $my = $this->initGetLinkedForTree($safe['tplan_id'],$filters,$options); - - - $mop = array('options' => array('addExecInfo' => false,'specViewFields' => false, - 'assigned_on_build' => null, 'testSuiteInfo' => false, - 'addPriority' => false,'addImportance' => false, - 'ignorePlatformAndBuild' => false, - 'ignoreBuild' => false, 'ignorePlatform' => false, - 'ua_user_alias' => '', 'ua_force_join' => false, - 'build_is_active' => false)); - - $my['options'] = array_merge($mop['options'],$my['options']); - - if( ($my['options']['allow_empty_build'] == 0) && $my['filters']['build_id'] <= 0 ) - { - // CRASH IMMEDIATELY - throw new Exception( $debugMsg . " Can NOT WORK with \$my['filters']['build_id'] <= 0"); - } - if( !$my['green_light'] ) - { - // No query has to be run, because we know in advance that we are - // going to get NO RECORDS - return null; - } - - $buildClause = array('lex' => (' AND EE.build_id = ' . $my['filters']['build_id']), - 'exec_join' => (" AND E.build_id = " . $my['filters']['build_id'])); - if( $my['options']['allow_empty_build'] && $my['filters']['build_id'] <= 0 ) - { - $buildClause = array('lex' => '','exec_join' => ''); - } - - // TICKET 5182: Add/Remove Test Cases -> Trying to assign new platform to executed test cases - // Before this ticket LEX was just on BUILD => ignoring platforms - // Need to understand if will create side effects. - // - if($my['options']['ignorePlatformAndBuild']) - { - $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id = " . $safe['tplan_id'] . - " GROUP BY EE.tcversion_id,EE.testplan_id "; - - $platformLEX = " "; - $platformEXEC = " "; - - } - else if ($my['options']['ignoreBuild'] && $my['options']['build_is_active']) - { - $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " JOIN {$this->tables['builds']} B " . - " ON B.id = EE.build_id " . - " WHERE EE.testplan_id = " . $safe['tplan_id'] . " AND B.active = 1" . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id, B.id"; - - $platformLEX = " AND LEX.platform_id = TPTCV.platform_id "; - $platformEXEC = " AND E.platform_id = TPTCV.platform_id "; - - } - else if ($my['options']['ignoreBuild']) - { - $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id = " . $safe['tplan_id'] . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id"; - - // TICKET 5182 - $platformLEX = " AND LEX.platform_id = TPTCV.platform_id "; - $platformEXEC = " AND E.platform_id = TPTCV.platform_id "; - - } - else - { - $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id = " . $safe['tplan_id'] . - $buildClause['lex'] . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; - - // TICKET 5182 - $platformLEX = " AND LEX.platform_id = TPTCV.platform_id "; - $platformEXEC = " AND E.platform_id = TPTCV.platform_id "; - - } - - // ------------------------------------------------------------------------------------- - // adding tcversion on output can be useful for Filter on Custom Field values, - // because we are saving values at TCVERSION LEVEL - // - - // TICKET 5165: Issues with DISTINCT CLAUSE on TEXT field - // Do not know if other usages are going to cry due to missing fields - // - // $commonFields = " SELECT NH_TCASE.id AS tcase_id,NH_TCASE.id AS tc_id,TPTCV.tcversion_id,TCV.version," . - // " TCV.tc_external_id AS external_id, TCV.execution_type," . - // " TCV.summary, TCV.preconditions,TPTCV.id AS feature_id," . - // " TPTCV.platform_id,PLAT.name AS platform_name,TPTCV.node_order AS execution_order,". - // " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status "; - // - // $fullEID = $this->helperConcatTCasePrefix($safe['tplan_id']); - $commonFields = " SELECT NH_TCASE.name AS tcase_name, NH_TCASE.id AS tcase_id, " . - " NH_TCASE.id AS tc_id,TPTCV.tcversion_id,TCV.version," . - " TCV.tc_external_id AS external_id, TCV.execution_type,TCV.status," . - " TPTCV.id AS feature_id," . - ($my['options']['addPriority'] ? "(TPTCV.urgency * TCV.importance) AS priority," : '') . - " TPTCV.platform_id,PLAT.name AS platform_name,TPTCV.node_order AS execution_order,". - " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status, " . - " E.execution_duration, " . - ($my['options']['addImportance'] ? " TCV.importance," : '') . - $this->helperConcatTCasePrefix($safe['tplan_id']) . " AS full_external_id "; - - // used on tester assignment feature when working at test suite level - if( !is_null($my['options']['assigned_on_build']) ) - { - $commonFields .= ",UA.user_id {$my['options']['ua_user_alias']} "; - } - - if($my['options']['addExecInfo']) - { - $commonFields .= ",COALESCE(E.id,0) AS exec_id,E.tcversion_number,E.build_id AS exec_on_build,E.testplan_id AS exec_on_tplan"; - } - - if($my['options']['specViewFields']) - { - $commonFields .= ",NH_TCASE.name,TPTCV.creation_ts AS linked_ts,TPTCV.author_id AS linked_by" . - ",NH_TCASE.parent_id AS testsuite_id"; - } - - $my['join']['tsuites'] = ''; - if($my['options']['testSuiteInfo']) - { - $commonFields .= ",NH_TSUITE.name AS tsuite_name "; - $my['join']['tsuites'] = " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . - " ON NH_TSUITE.id = NH_TCASE.parent_id "; - } - - if($my['options']['ua_force_join']) - { - $my['join']['ua'] = str_replace('LEFT OUTER',' ', $my['join']['ua']); - } - - $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . $commonFields . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['tsuites'] . - $my['join']['ua'] . - $my['join']['keywords'] . - - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - $platformLEX . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.testplan_id = " . $safe['tplan_id'] . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - $platformEXEC . - " AND E.id = LEX.id " . // TICKET 6159 - $buildClause['exec_join'] . - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . ' ' . - $my['where']['where'] . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEX.id IS NULL"; - - - $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . $commonFields . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . - $my['join']['tsuites'] . - $my['join']['ua'] . - $my['join']['keywords'] . - - " JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - $platformLEX . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.testplan_id = " . $safe['tplan_id'] . - - " JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - $platformEXEC . - " AND E.id = LEX.id " . // TICKET 6159 - $buildClause['exec_join'] . - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . ' ' . - $my['where']['where']; - - return $union; - } - - - /** - * - * - */ - function getPublicAttr($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT is_public FROM {$this->tables['testplans']} " . - " WHERE id =" . intval($id); - $ret = $this->db->get_recordset($sql); - return $ret[0]['is_public']; - } - - - - /** - * - * - */ - function getBuildByCriteria($id, $criteria, $filters=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('active' => null, 'open' => null); - $my['opt'] = array_merge($my['opt'],(array)$options); - - - switch($criteria) - { - case 'maxID': - $sql = " /* $debugMsg */ " . - " SELECT MAX(id) AS id,testplan_id, name, notes, active, is_open," . - " release_date,closed_on_date " . - " FROM {$this->tables['builds']} WHERE testplan_id = {$id} " ; - break; - } - - if(!is_null($my['opt']['active'])) - { - $sql .= " AND active = " . intval($my['opt']['active']) . " "; - } - if( !is_null($my['opt']['open']) ) - { - $sql .= " AND is_open = " . intval($my['opt']['open']) . " "; - } - - $rs = $this->db->get_recordset($sql); - - return $rs; - } - - - /** - * - * - */ - function writeExecution($ex) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $execNotes = $this->db->prepare_string($ex->notes); - if(property_exists($ex, 'executionTimeStampISO')) - { - $execTS = "'" . $ex->executionTimeStampISO . "'"; - } - else - { - $execTS = $this->db->db_now(); - } - - $sql = "/* {$debugMsg} */ " . - "INSERT INTO {$this->tables['executions']} " . - " (testplan_id, platform_id, build_id, " . - " tcversion_id, tcversion_number, status, " . - " tester_id, execution_ts, execution_type, notes) " . - " VALUES(" . - " {$ex->testPlanID},{$ex->platformID},{$ex->buildID}," . - " {$ex->testCaseVersionID}, {$ex->testCaseVersionNumber},'{$ex->statusCode}'," . - " {$ex->testerID},{$execTS}, {$ex->executionType}, '{$execNotes}')"; - - $this->db->exec_query($sql); - return $this->db->insert_id($this->tables['executions']); - } - - /** - * - */ - function getExecutionDurationForSet($execIDSet) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - "SELECT E.id, E.execution_duration AS duration ". - "FROM {$this->tables['executions']} E " . - "WHERE id IN (" . implode(',',$execIDSet) . ')'; - return $this->db->get_recordset($sql); - } - - /** - * - */ - function exportForResultsToXML($id,$context,$optExport = array(),$filters=null) - { - $my['filters'] = array('platform_id' => null, 'tcaseSet' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - - $item = $this->get_by_id($id,array('output' => 'minimun','caller' => __METHOD__)); - - $xmlString = "\n" . - "\n"; - $xmlString .= "\n"; - $xmlString .= "\t\n"; - - $xmlString .= "\t\n"; - - if( isset($context['build_id']) && $context['build_id'] > 0) - { - $dummy = $this->get_builds($id); - $info = $dummy[$context['build_id']]; - $xmlString .= "\t\n"; - } - - // get target platform (if exists) - if( $context['platform_id'] > 0) - { - $info = $this->platform_mgr->getByID($context['platform_id']); - $xmlString .= "\t\n"; - $my['filters']['platform_id'] = $context['platform_id']; - } - - // - // - // - // u0113 - // - // 2008-09-08 14:00:00 - // p - // functionality works great - // - $mm = $this->getLinkedStaticView($id,$my['filters'],array('output' => 'array','detail' => '4results')); - - - if(!is_null($mm) && ($tcaseQty=count($mm)) > 0) - { - - // Custom fields processing - $xcf = $this->cfield_mgr->get_linked_cfields_at_execution($item['tproject_id'],1,'testcase'); - if(!is_null($xcf) && ($cfQty=count($xcf)) > 0) - { - for($gdx=0; $gdx < $tcaseQty; $gdx++) - { - $mm[$gdx]['xmlcustomfields'] = $this->cfield_mgr->exportValueAsXML($xcf); - } - } - - // Test Case Steps - $gso = array('fields2get' => 'TCSTEPS.id,TCSTEPS.step_number', 'renderGhostSteps' => false, 'renderImageInline' => false); - $stepRootElem = "{{XMLCODE}}"; - $stepTemplate = "\n" . '' . "\n" . - "\t||STEP_NUMBER||\n" . - "\tp\n" . - "\t||NOTES||\n" . - "\n"; - $stepInfo = array("||STEP_NUMBER||" => "step_number", "||NOTES||" => "notes"); - - for($gdx=0; $gdx < $tcaseQty; $gdx++) - { - $mm[$gdx]['steps'] = $this->tcase_mgr->getStepsSimple($mm[$gdx]['tcversion_id'],0,$gso); - if(!is_null($mm[$gdx]['steps'])) - { - $qs = count($mm[$gdx]['steps']); - for($scx=0; $scx < $qs; $scx++) - { - $mm[$gdx]['steps'][$scx]['notes'] = 'your step exec notes'; - } - $mm[$gdx]['xmlsteps'] = exportDataToXML($mm[$gdx]['steps'],$stepRootElem,$stepTemplate,$stepInfo,true); - } - } - } - - - $xml_root = null; - $xml_template = "\n" . - "\t" . "\n" . - "\t\t" . "X" . "\n" . - "\t\t" . "test link rocks " . "\n" . - "\t\t" . "put login here" . "\n" . - "\t\t" . "" . "\n" . - "\t\t" . "YYYY-MM-DD HH:MM:SS" . "\n" . - "\t\t" . "put one of your bugs id here (repeat the line as many times you need)" . "\n" . - "\t\t" . "put another of your bugs id here" . "\n" . - "\t\t" . "||STEPS||" . "\n" . - "\t\t" . "||CUSTOMFIELDS||" . "\n" . - "\t" . "\n"; - - $xml_mapping = null; - $xml_mapping = array("{{FULLEXTERNALID}}" => "full_external_id", "||CUSTOMFIELDS||" => "xmlcustomfields", - "||STEPS||" => "xmlsteps"); - - $linked_testcases = exportDataToXML($mm,$xml_root,$xml_template,$xml_mapping,('noXMLHeader'=='noXMLHeader')); - $zorba = $xmlString .= $linked_testcases . "\n\n"; - - return $zorba; - } - - - /** - * - */ - function setActive($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . "UPDATE {$this->tables['testplans']} SET active=1 WHERE id=" . intval($id); - $this->db->exec_query($sql); - } - - /** - * - */ - function setInactive($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . "UPDATE {$this->tables['testplans']} SET active=0 WHERE id=" . intval($id); - $this->db->exec_query($sql); - } - - - - /** - * - */ - function getByAPIKey($apiKey,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('checkIsValid' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - $fields2get = $my['opt']['checkIsValid'] ? 'id' : '*'; - - $safe = $this->db->prepare_string($apiKey); - - $sql = "/* $debugMsg */ " . - " SELECT {$fields2get} FROM {$this->tables['testplans']} " . - " WHERE api_key = '{$safe}'"; - - $rs = $this->db->get_recordset($sql); - return ($rs ? $rs[0] : null); - } - - - - /** - * - * @used-by planEdit.php - */ - function getFileUploadRelativeURL($id) - { - // do_action,tplan_id as expected in planEdit.php - $url = "lib/plan/planEdit.php?do_action=fileUpload&tplan_id=" . intval($id); - return $url; - } - - /** - * @used-by planEdit.php - */ - function getDeleteAttachmentRelativeURL($id) - { - // do_action,tplan_id as expected in planEdit.php - $url = "lib/plan/planEdit.php?do_action=deleteFile&tplan_id=" . intval($id) . "&file_id=" ; - return $url; - } - - - /** - * @used-by - */ - function getAllExecutionsWithBugs($id,$platform_id=null,$build_id=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe['tplan_id'] = intval($id); - $fullEID = $this->helperConcatTCasePrefix($safe['tplan_id']); - - $sql = " /* $debugMsg */ ". - " SELECT DISTINCT E.id AS exec_id,EB.bug_id,NHTC.id AS tcase_id, NHTC.id AS tc_id, " . - " NHTC.name AS name, NHTSUITE.name AS tsuite_name, TCV.tc_external_id AS external_id," . - " $fullEID AS full_external_id " . - " FROM {$this->tables['executions']} E " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.tcversion_id = E.tcversion_id " . - " AND TPTCV.testplan_id = E.testplan_id " . - " JOIN {$this->tables['execution_bugs']} EB " . - " ON EB.execution_id = E.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = E.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = E.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTSUITE " . - " ON NHTSUITE.id = NHTC.parent_id " . - " WHERE TPTCV.testplan_id = " . $safe['tplan_id']; - - $items = $this->db->get_recordset($sql); - return $items; - } - - - /** - * - */ - public function getLTCVOnTestPlan($id,$filters=null,$options=null) - { - $debugMsg = 'Class: ' . __CLASS__ . ' - Method:' . __FUNCTION__; - $my = array('filters' => array(), - 'options' => array('allow_empty_build' => 1,'addPriority' => false, - 'accessKeyType' => 'tcase+platform', - 'addImportance' => false, - 'includeNotRun' => true, 'orderBy' => null)); - $amk = array('filters','options'); - foreach($amk as $mk) - { - $my[$mk] = array_merge($my[$mk], (array)$$mk); - } - - $my['options']['ignorePlatformAndBuild'] = true; - if( !is_null($sql2do = $this->getLinkedTCVersionsSQL($id,$my['filters'],$my['options'])) ) - { - // need to document better - if( is_array($sql2do) ) - { - $sql2run = $sql2do['exec']; - if($my['options']['includeNotRun']) - { - $sql2run .= ' UNION ' . $sql2do['not_run']; - } - } - else - { - $sql2run = $sql2do; - } - - // added when trying to fix: - // TICKET 5788: test case execution order not working on RIGHT PANE - // Anyway this did not help - if( !is_null($my['options']['orderBy']) ) - { - $sql2run = " SELECT * FROM ($sql2run) XX ORDER BY " . $my['options']['orderBy']; - } - - switch($my['options']['accessKeyType']) - { - case 'tcase+platform': - $tplan_tcases = $this->db->fetchMapRowsIntoMap($sql2run,'tcase_id','platform_id'); // ,0,-1,'user_id'); - break; - - case 'tcase+platform+stackOnUser': - $tplan_tcases = $this->db->fetchMapRowsIntoMapStackOnCol($sql2run,'tcase_id','platform_id','user_id'); - break; - - case 'index': - $tplan_tcases = $this->db->get_recordset($sql2run); - break; - - default: - $tplan_tcases = $this->db->fetchRowsIntoMap($sql2run,'tcase_id'); - break; - } - } - return $tplan_tcases; - } - - - /** - * - */ - public function getLTCVOnTestPlanPlatform($id,$filters=null,$options=null) - { - $debugMsg = 'Class: ' . __CLASS__ . ' - Method:' . __FUNCTION__; - $my = array('filters' => array(), - 'options' => array('allow_empty_build' => 1,'addPriority' => false, - 'accessKeyType' => 'tcase+platform', - 'addImportance' => false, - 'includeNotRun' => true, 'orderBy' => null)); - $amk = array('filters','options'); - foreach($amk as $mk) - { - $my[$mk] = array_merge($my[$mk], (array)$$mk); - } - - $my['options']['ignoreBuild'] = true; - if( !is_null($sql2do = $this->getLinkedTCVersionsSQL($id,$my['filters'],$my['options'])) ) - { - // need to document better - if( is_array($sql2do) ) - { - $sql2run = $sql2do['exec']; - if($my['options']['includeNotRun']) - { - $sql2run .= ' UNION ' . $sql2do['not_run']; - } - } - else - { - $sql2run = $sql2do; - } - - // added when trying to fix: - // TICKET 5788: test case execution order not working on RIGHT PANE - // Anyway this did not help - if( !is_null($my['options']['orderBy']) ) - { - $sql2run = " SELECT * FROM ($sql2run) XX ORDER BY " . $my['options']['orderBy']; - } - - switch($my['options']['accessKeyType']) - { - case 'tcase+platform': - $tplan_tcases = $this->db->fetchMapRowsIntoMap($sql2run,'tcase_id','platform_id'); // ,0,-1,'user_id'); - break; - - case 'tcase+platform+stackOnUser': - $tplan_tcases = $this->db->fetchMapRowsIntoMapStackOnCol($sql2run,'tcase_id','platform_id','user_id'); - break; - - case 'index': - $tplan_tcases = $this->db->get_recordset($sql2run); - break; - - default: - $tplan_tcases = $this->db->fetchRowsIntoMap($sql2run,'tcase_id'); - break; - } - } - return $tplan_tcases; - } - - - - /** - * - */ - function getLinkedItems($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ ". - " SELECT parent_id AS tcase_id,TPTCV.platform_id,TPTCV.node_order " . - " FROM {$this->tables['nodes_hierarchy']} NHTC " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTC.id " . - " WHERE TPTCV.testplan_id = " . intval($id); - - $items = $this->db->fetchMapRowsIntoMap($sql,'tcase_id','platform_id'); - - return $items; - } - - - - /** - * - * @since 1.9.14 - */ - function getLinkedFeatures($id,$filters=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('filters' => array(), $options => array()); - $my['filters'] = array('platform_id' => null); - $my['options'] = array('accessKey' => array('tcase_id','platform_id')); - - $my['filters'] = array_merge($my['filters'],(array)$filters); - $my['options'] = array_merge($my['options'],(array)$options); - - $sql = " /* $debugMsg */ ". - " SELECT parent_id AS tcase_id,TPTCV.platform_id,TPTCV.id AS feature_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTC " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTC.id " . - " WHERE TPTCV.testplan_id = " . intval($id); - - if(!is_null($my['filters']['platform_id'])) - { - $sql .= " AND TPTCV.platform_id = " . intval($my['filters']['platform_id']); - } - - if(!is_null($my['filters']['tcase_id'])) - { - $sql .= " AND NHTC.parent_id IN (" . implode(',',$my['filters']['tcase_id']) . ") "; - } - - $items = $this->db->fetchMapRowsIntoMap($sql,$my['options']['accessKey'][0], - $my['options']['accessKey'][1]); - - return $items; - } - - /** - * @used-by getFilteredLinkedVersions() - specview.php - * @used-by indirectly on tc_exec_assigment.php for test suites - * - */ - function getLinkedTCVXmen($id,$filters=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe['tplan_id'] = intval($id); - $my = $this->initGetLinkedForTree($safe['tplan_id'],$filters,$options); - - // adding tcversion on output can be useful for Filter on Custom Field values, - // because we are saving values at TCVERSION LEVEL - $commonFields = "/* $debugMsg */ " . - " SELECT NH_TCASE.name AS tcase_name, NH_TCASE.id AS tcase_id, " . - " NH_TCASE.id AS tc_id,TPTCV.tcversion_id,TCV.version," . - " TCV.tc_external_id AS external_id, TCV.execution_type,TCV.status," . - " TPTCV.id AS feature_id," . - ($my['options']['addPriority'] ? "(TPTCV.urgency * TCV.importance) AS priority," : '') . - " TPTCV.platform_id,TPTCV.node_order AS execution_order,". - ($my['options']['addImportance'] ? " TCV.importance," : '') . - $this->helperConcatTCasePrefix($safe['tplan_id']) . " AS full_external_id "; - - $commonFields .= ",UA.user_id"; - $commonFields .= ",NH_TCASE.name,TPTCV.creation_ts AS linked_ts,TPTCV.author_id AS linked_by" . - ",NH_TCASE.parent_id AS testsuite_id"; - - $commonFields .= ",NH_TSUITE.name AS tsuite_name "; - - $my['join']['tsuites'] = " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . - " ON NH_TSUITE.id = NH_TCASE.parent_id "; - - - - $sql = $commonFields . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['tsuites'] . - $my['join']['ua'] . - $my['join']['keywords'] . - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['where']; - - $items = $this->db->fetchMapRowsIntoMapStackOnCol($sql,'tcase_id','platform_id','user_id'); - return $items; - } - - /** - * - */ - function getExecCountOnBuild($id,$build_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe['tplan_id'] = intval($id); - $safe['build_id'] = intval($build_id); - - $sql = "/* debugMsg */ SELECT COUNT(0) AS qty " . - " FROM {$this->tables['executions']} E " . - " WHERE E.testplan_id = {$safe['tplan_id']} " . - " AND E.build_id = {$safe['build_id']}"; - - $rs = $this->db->get_recordset($sql); - - return $rs[0]['qty']; - } - - /** - * - */ - function getFeatureByID($feature_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $target = (array)$feature_id; - foreach($target as $idx => $tg) - { - $target[$idx] = intval($tg); - } - $inSet = implode(',', $target); - - $sql = " /* $debugMsg */ ". - " SELECT parent_id AS tcase_id,tcversion_id,platform_id,TPTCV.id " . - " FROM {$this->tables['nodes_hierarchy']} NHTC " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.tcversion_id = NHTC.id " . - " WHERE TPTCV.id IN (" . $inSet . ")"; - - $items = $this->db->fetchRowsIntoMap($sql,'id'); - return $items; - } - - - /** - * - */ - function getVersionLinked($tplan_id, $tcase_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ - SELECT tcversion_id + MAX(EE.id) AS id + FROM {$this->tables['executions']} EE + WHERE EE.testplan_id = " . $safe_id['tplan'] . + " AND EE.build_id IN ({$buildsCfg['inClause']}) + $platformClause + GROUP BY EE.tcversion_id,EE.testplan_id + {$platformField} {$buildField} "; + + return array( + $safe_id, + $buildsCfg, + $sqlLEX + ); + } + + /** + */ + public function helperConcatTCasePrefix($id) + { + // Get test case prefix + // $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $io = $this->tree_manager->get_node_hierarchy_info($id); + + list ($prefix, $garbage) = $this->tcaseMgr->getPrefix(null, + $io['parent_id']); + $prefix .= $this->tcaseCfg->glue_character; + $concat = $this->db->db->concat("'{$prefix}'", 'TCV.tc_external_id'); + + unset($io); + unset($garbage); + unset($prefix); + + return $concat; + } + + /** + */ + private function helperColumns($tplanID, &$filters, &$opt) + { + $safe_id = intval($tplanID); + + $join['tsuite'] = ''; + $join['builds'] = ''; + + $order_by['exec'] = ''; + + $fields['tcase'] = ''; + $fields['tsuite'] = ''; + $fields['priority'] = " (urgency * importance) AS priority "; + + $fields['ua'] = " UA.build_id AS assigned_build_id, UA.user_id,UA.type,UA.status,UA.assigner_id "; + + $default_fields['exec'] = " E.id AS exec_id, E.tcversion_number," . + " E.tcversion_id AS executed, E.testplan_id AS exec_on_tplan, {$more_exec_fields}" . + " E.execution_type AS execution_run_type, " . + " E.execution_ts, E.tester_id, E.notes as execution_notes," . + " E.build_id as exec_on_build, "; + + $fields['exec'] = $default_fields['exec']; + if ($opt['execution_details'] == 'add_build') { + $fields['exec'] .= 'E.build_id,B.name AS build_name, B.active AS build_is_active,'; + } + if (is_null($opt['forced_exec_status'])) { + $fields['exec'] .= " COALESCE(E.status,'" . $this->notRunStatusCode . + "') AS exec_status "; + } else { + $fields['exec'] .= " '{$opt['forced_exec_status']}' AS exec_status "; + } + + switch ($opt['details']) { + case 'full': + $fields['tcase'] = 'TCV.summary,'; + $fields['tsuite'] = 'NH_TSUITE.name as tsuite_name,'; + $join['tsuite'] = " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . + " ON NH_TCASE.parent_id = NH_TSUITE.id "; + $opt['steps_info'] = true; + break; + + case 'summary': + $fields['tcase'] = 'TCV.summary,'; + break; + + case 'spec_essential': // TICKET 4710 + $fields['exec'] = ''; + $fields['ua'] = ''; + $join['builds'] = ''; + $filters['ua'] = ''; + break; + + case 'exec_tree_optimized': // TICKET 4710 + // if all following filters are NOT USED, then we will REMOVE executions JOIN + if ($filters['builds'] == '' && $filters['executions'] == '') { + $join['builds'] = ''; + $join['executions'] = ''; + + $fields['exec'] = ''; + $fields['ua'] = ''; + + $filters['executions'] = ''; + $filters['ua'] = ''; + $order_by['exec'] = ''; + } + break; + + case 'report': // Results Performance + $fields['ua'] = ''; + $filters['ua'] = ''; + break; + } + + if (! is_null($opt['exclude_info'])) { + foreach ($opt['exclude_info'] as $victim) { + switch ($victim) { + case 'exec_info': + $fields['exec'] = ''; + $order_by['exec'] = " "; + $join['executions'] = ''; + break; + + case 'priority': + $fields['priority'] = ''; + break; + + case 'assigned_on_build': + case 'assigned_to': + $fields['ua'] = ''; + $filters['ua'] = ''; + break; + } + } + } + + $fullEID = $this->helperConcatTCasePrefix($safe_id); + $sql = " SELECT NH_TCASE.parent_id AS testsuite_id, {$fields['tcase']} {$fields['tsuite']} " . + " NH_TCV.parent_id AS tc_id, NH_TCASE.node_order AS z, NH_TCASE.name," . + " TPTCV.platform_id, PLAT.name as platform_name ,TPTCV.id AS feature_id, " . + " TPTCV.tcversion_id AS tcversion_id, " . + " TPTCV.node_order AS execution_order, TPTCV.creation_ts AS linked_ts, " . + " TPTCV.author_id AS linked_by,TPTCV.urgency," . + " TCV.version AS version, TCV.active, TCV.tc_external_id AS external_id, " . + " TCV.execution_type,TCV.importance," . + " $fullEID AS full_external_id"; + + $dummy = array( + 'exec', + 'priority', + 'ua' + ); + foreach ($dummy as $ki) { + $sql .= ($fields[$ki] != '' ? ',' . $fields[$ki] : ''); + } + + if ($fields['ua'] != '') { + $join['ua'] = " LEFT OUTER JOIN {$this->tables['user_assignments']} UA ON " . + " UA.feature_id = TPTCV.id " . " AND UA.build_id IN (" . + $this->helperBuildInClause($tplanID, $filters, $opt) . ")"; + } + + return array( + $sql, + $join, + $order_by + ); + } + + /** + */ + private function helperLastExecution($tplanID, $filters, $options) + { + $safe_id = intval($tplanID); + + $filterBuildActiveStatus = ''; + $activeStatus = null; + $domain = array( + 'active' => 1, + 'inactive' => 0, + 'any' => null + ); + if (! is_null($domain[$options['build_active_status']])) { + $activeStatus = intval($domain[$options['build_active_status']]); + $filterBuildActiveStatus = " AND BB.active = " . $activeStatus; + } + + $buildsInClause = $this->helperBuildInClause($tplanID, $filters, + $options); + + // Last Executions By Build and Platform (LEBBP) + $sqlLEBBP = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " /* use builds table to filter on active status */ " . + " JOIN {$this->tables['builds']} BB " . " ON BB.id = EE.build_id " . + " WHERE EE.testplan_id=" . $safe_id . + " AND EE.build_id IN ({$buildsInClause}) " . $filterBuildActiveStatus . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; + + unset($dummy); + unset($buildsInClause); + unset($filterBuildActiveStatus); + + return $sqlLEBBP; + } + + /** + */ + private function helperBuildInClause($tplanID, $filters, $options) + { + $safe_id = intval($tplanID); + if (! is_null($filters['builds'])) { + $dummy = $filters['builds']; + } else { + $activeStatus = null; + $domain = array( + 'active' => 1, + 'inactive' => 0, + 'any' => null + ); + if (! is_null($domain[$options['build_active_status']])) { + $activeStatus = intval($domain[$options['build_active_status']]); + } + $dummy = array_keys($this->get_builds($safe_id, $activeStatus)); + } + + return implode(",", $dummy); + } + + /** + */ + private function helperBuildActiveStatus($options) + { + $activeStatus = null; + $domain = array( + 'active' => 1, + 'inactive' => 0, + 'any' => null + ); + if (! is_null($domain[$options['build_active_status']])) { + $activeStatus = intval($domain[$options['build_active_status']]); + } + + return $activeStatus; + } + + // This method is intended to return minimal data useful + // to create Execution Tree. + // Status on Latest execution on Build,Platform is needed + // + // @param int $id test plan id + // @param mixed $filters + // @param mixed $options + // + // [tcase_id]: default null => get any testcase + // numeric => just get info for this testcase + // + // + // [keyword_id]: default 0 => do not filter by keyword id + // numeric/array() => filter by keyword id + // + // + // [assigned_to]: default NULL => do not filter by user assign. + // array() with user id to be used on filter + // IMPORTANT NOTICE: this argument is affected by + // [assigned_on_build] + // + // [build_id]: default 0 or null => do not filter by build id + // numeric => filter by build id + // [cf_hash]: default null => do not filter by Custom Fields values + // + // + // [urgencyImportance] : filter only Tc's with certain (urgency*importance)-value + // + // [tsuites_id]: default null. + // If present only tcversions that are children of this testsuites + // will be included + // + // [exec_type] default null -> all types. + // [platform_id] + public function getLinkedForExecTree($id, $filters = null, $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $safe['tplan_id'] = intval($id); + $my = $this->initGetLinkedForTree($filters, $options); + + if ($my['filters']['build_id'] <= 0) { + // CRASH IMMEDIATELY + throw new Exception( + $debugMsg . " Can NOT WORK with \$my['filters']['build_id'] <= 0"); + } + + if (! $my['green_light']) { + // No query has to be run, because we know in advance that we are + // going to get NO RECORDS + return null; + } + + $platform4EE = " "; + if (! is_null($my['filters']['platform_id']) && + (intval($my['filters']['platform_id'])) > 0) { + $platform4EE = " AND EE.platform_id = " . + intval($my['filters']['platform_id']); + } + + $sqlLEBBP = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " WHERE EE.testplan_id = " . $safe['tplan_id'] . + " AND EE.build_id = " . intval($my['filters']['build_id']) . + $platform4EE . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; + + // When there is request to filter by BUG ID, because till now (@20131216) BUGS are linked + // only to EXECUTED test case versions, the not_run piece of union is USELESS + $union['not_run'] = null; + + // if(isset($my['filters']['bug_id']) + + if (! isset($my['filters']['bug_id'])) { + // adding tcversion on output can be useful for Filter on Custom Field values, + // because we are saving values at TCVERSION LEVEL + $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . + // $fullEIDClause . + " TCV.tc_external_id AS external_id, " . + " TPTCV.node_order AS exec_order," . " COALESCE(E.status,'" . + $this->notRunStatusCode . "') AS exec_status " . + $my['fields']['tsuites'] . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['ua'] . $my['join']['keywords'] . $my['join']['cf'] . + $my['join']['tsuites'] . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.testplan_id = " . $safe['tplan_id'] . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . " AND E.build_id = " . + $my['filters']['build_id'] . " WHERE TPTCV.testplan_id =" . + $safe['tplan_id'] . $my['where']['not_run'] . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEBBP.id IS NULL"; + } + + $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . + // $fullEIDClause . + " TCV.tc_external_id AS external_id, " . + " TPTCV.node_order AS exec_order," . " COALESCE(E.status,'" . + $this->notRunStatusCode . "') AS exec_status " . + $my['fields']['tsuites'] . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['ua'] . $my['join']['keywords'] . $my['join']['cf'] . + $my['join']['tsuites'] . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.testplan_id = " . $safe['tplan_id'] . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . " AND E.build_id = " . + $my['filters']['build_id'] . $my['join']['bugs'] . // need to be here because uses join with E table alias + + " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . + $my['where']['where']; + + return is_null($union['not_run']) ? $union['exec'] : $union; + } + + /* + * + * @used-by + * getLinkedForExecTree() + * getLinkedForTesterAssignmentTree() + * getLinkedTCVersionsSQL() + * getLinkedForExecTreeCross() + * getLinkedForExecTreeIVU() + * + * filters => + * 'tcase_id','keyword_id','assigned_to','exec_status','build_id', + * 'cf_hash','urgencyImportance', 'tsuites_id','platform_id', + * 'exec_type','tcase_name' + * + * + * CRITIC: + * cf_hash can contains Custom Fields that are applicable to DESIGN and + * TESTPLAN_DESIGN. + * + * Here we are generating SQL that will be used ON TESTPLAN + * related tables NOT ON TEST SPEC related tables. + * Due to this we are going to consider while building + * the query ONLY CF for TESTPLAN DESING + * + */ + private function initGetLinkedForTree($filtersCfg, $optionsCfg) + { + $ic['fields']['tsuites'] = ''; + + $ic['join'] = array(); + $ic['join']['ua'] = ''; + $ic['join']['bugs'] = ''; + $ic['join']['cf'] = ''; + $ic['join']['tsuites'] = ''; + + $ic['where'] = array(); + $ic['where']['where'] = ''; + $ic['where']['platforms'] = ''; + $ic['where']['not_run'] = ''; + $ic['where']['cf'] = ''; + + $ic['green_light'] = true; + $ic['filters'] = array( + 'tcase_id' => null, + 'keyword_id' => 0, + 'assigned_to' => null, + 'exec_status' => null, + 'build_id' => 0, + 'cf_hash' => null, + 'urgencyImportance' => null, + 'tsuites_id' => null, + 'platform_id' => null, + 'exec_type' => null, + 'tcase_name' => null + ); + + $ic['options'] = array( + 'hideTestCases' => 0, + 'include_unassigned' => false, + 'allow_empty_build' => 0, + 'addTSuiteOrder' => false, + 'addImportance' => false, + 'addPriority' => false + ); + $ic['filters'] = array_merge($ic['filters'], (array) $filtersCfg); + $ic['options'] = array_merge($ic['options'], (array) $optionsCfg); + + $ic['filters']['build_id'] = intval($ic['filters']['build_id']); + + if ($ic['options']['addTSuiteOrder']) { + // PREFIX ALWAYS with COMMA + $ic['fields']['tsuites'] = ', NH_TSUITE.node_order AS tsuite_order '; + $ic['join']['tsuites'] = " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . + " ON NH_TSUITE.id = NH_TCASE.parent_id "; + } + + // This NEVER HAPPENS for Execution Tree, but if we want to reuse + // this method for Tester Assignment Tree, we need to add this check + // + if (! is_null($ic['filters']['platform_id']) && + $ic['filters']['platform_id'] > 0) { + $ic['filters']['platform_id'] = intval( + $ic['filters']['platform_id']); + $ic['where']['platforms'] = " AND TPTCV.platform_id = {$ic['filters']['platform_id']} "; + } + + $ic['where']['where'] .= $ic['where']['platforms']; + + $dk = 'exec_type'; + if (! is_null($ic['filters'][$dk])) { + $ic['where'][$dk] = " AND TCV.execution_type IN (" . + implode(",", (array) $ic['filters'][$dk]) . " ) "; + $ic['where']['where'] .= $ic['where'][$dk]; + } + + $dk = 'tcase_id'; + if (! is_null($ic['filters'][$dk])) { + if (is_array($ic['filters'][$dk])) { + $ic['where'][$dk] = " AND NH_TCV.parent_id IN (" . + implode(',', $ic['filters'][$dk]) . ")"; + } elseif ($ic['filters'][$dk] > 0) { + $ic['where'][$dk] = " AND NH_TCV.parent_id = " . + intval($ic['filters'][$dk]); + } else { + // Best Option on this situation will be signal + // that query will fail => NO SENSE run the query + $ic['green_light'] = false; + } + $ic['where']['where'] .= $ic['where'][$dk]; + } + + if (! is_null($ic['filters']['tsuites_id'])) { + $dummy = (array) $ic['filters']['tsuites_id']; + $ic['where']['where'] .= " AND NH_TCASE.parent_id IN (" . + implode(',', $dummy) . ")"; + } + + if (! is_null($ic['filters']['urgencyImportance'])) { + $ic['where']['where'] .= $this->helperUrgencySQL( + $ic['filters']['urgencyImportance']); + } + + if (! is_null($ic['filters']['keyword_id'])) { + + list ($ic['join']['keywords'], $ic['where']['keywords']) = $this->helper_keywords_sql( + $ic['filters']['keyword_id'], array( + 'output' => 'array' + )); + + // **** // CHECK THIS CAN BE NON OK + $ic['where']['where'] .= $ic['where']['keywords']; + } + + // If special user id TL_USER_ANYBODY is present in set of user id, + // we will DO NOT FILTER by user ID + if (! is_null($ic['filters']['assigned_to']) && + ! in_array(TL_USER_ANYBODY, (array) $ic['filters']['assigned_to'])) { + list ($ic['join']['ua'], $ic['where']['ua']) = $this->helperAssignedToSQL( + $ic['filters']['assigned_to'], $ic['options'], + $ic['filters']['build_id']); + + $ic['where']['where'] .= $ic['where']['ua']; + } + + if (isset($ic['options']['assigned_on_build']) && + ! is_null($ic['options']['assigned_on_build'])) { + $ic['join']['ua'] = " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . " AND UA.build_id = " . + $ic['options']['assigned_on_build'] . + " AND UA.type = {$this->execTaskCode} "; + } + + if (! is_null($ic['filters']['tcase_name']) && + ($dummy = trim($ic['filters']['tcase_name'])) != '') { + $ic['where']['where'] .= " AND NH_TCASE.name LIKE '%{$dummy}%' "; + } + + // Custom fields on testplan_design ONLY => AFFECTS run and NOT RUN. + if (isset($ic['filters']['cf_hash']) && + ! is_null($ic['filters']['cf_hash'])) { + $ic['where']['cf'] = ''; + + list ($ic['filters']['cf_hash'], $cf_sql) = $this->helperTestPlanDesignCustomFields( + $ic['filters']['cf_hash']); + + if (strlen(trim($cf_sql)) > 0) { + $ic['where']['cf'] .= " AND ({$cf_sql}) "; + $ic['join']['cf'] = " JOIN {$this->tables['cfield_testplan_design_values']} CFTPD " . + " ON CFTPD.link_id = TPTCV.id "; + } + $ic['where']['where'] .= $ic['where']['cf']; + } + + // I've made the choice to create the not_run key, + // to manage the not_run part of UNION on getLinkedForExecTree(). + // + // ATTENTION: + // on other methods: + // getLinkedForTesterAssignmentTree() + // getLinkedTCVersionsSQL() + // Still is used $ic['where']['where'] on BOTH components of UNION + // + // TICKET 5566: "Assigned to" does not work in "test execution" page + // TICKET 5572: Filter by Platforms - Wrong test case state count in test plan execution + $ic['where']['not_run'] = $ic['where']['where']; + + // ************************************************************************ + // CRITIC - CRITIC - CRITIC + // Position on code flow is CRITIC + // CRITIC - CRITIC - CRITIC + // ************************************************************************ + if (! is_null($ic['filters']['exec_status'])) { + $dummy = (array) $ic['filters']['exec_status']; + + $ic['where']['where'] .= " AND E.status IN ('" . + implode("','", $dummy) . "')"; + + if (in_array($this->notRunStatusCode, $dummy)) { + $ic['where']['not_run'] .= ' AND E.status IS NULL '; + } else { + $ic['where']['not_run'] = $ic['where']['where']; + } + } + + // BUG ID HAS NO EFFECT ON NOT RUN (at least @20140126) + // bug_id => will be a list to create an IN() clause + if (isset($ic['filters']['bug_id']) && + ! is_null($ic['filters']['bug_id'])) { + list ($ic['join']['bugs'], $ic['where']['bugs']) = $this->helperBugsSQL( + $ic['filters']['bug_id']); + $ic['where']['where'] .= $ic['where']['bugs']; + } + + return $ic; + } + + /** + */ + private function helperTestPlanDesignCustomFields($cfSet) + { + $type_domain = $this->cfield_mgr->get_available_types(); + $ret = null; + $cf_type = null; + foreach ($cfSet as $id => $val) { + $xx = $this->cfield_mgr->get_by_id($id); + if ($xx[$id]['enable_on_testplan_design']) { + $ret[$id] = $val; + $cf_type[$id] = $type_domain[$xx[$id]['type']]; + } + } + + $cf_sql = ''; + if (! is_null($ret)) { + $countmain = 1; + foreach ($ret as $cf_id => $cf_value) { + if ($countmain != 1) { + $cf_sql .= " AND "; + } + + if (is_array($cf_value)) { + $count = 1; + switch ($cf_type[$cf_id]) { + case 'multiselection list': + if (count($cf_value) > 1) { + $combo = implode('|', $cf_value); + $cf_sql .= "( CFTPD.value = '{$combo}' AND CFTPD.field_id = {$cf_id} )"; + } else { + // close set, open set, is sandwiched, is alone + // $cf_sql .= "( (CFTPD.value LIKE '%|{$cf_value[0]}' AND CFTPD.field_id = {$cf_id}) OR " . + // " (CFTPD.value LIKE '{$cf_value[0]}|%' AND CFTPD.field_id = {$cf_id}) OR " . + // " (CFTPD.value LIKE '%|{$cf_value[0]}|%' AND CFTPD.field_id = {$cf_id}) OR " . + // " (CFTPD.value = '{$cf_value[0]}' AND CFTPD.field_id = {$cf_id}) )"; + + $cf_sql .= "( CFTPD.field_id = {$cf_id} AND " . + " (CFTPD.value LIKE '%|{$cf_value[0]}' OR " . + " CFTPD.value LIKE '{$cf_value[0]}|%' OR " . + " CFTPD.value LIKE '%|{$cf_value[0]}|%' OR " . + " CFTPD.value = '{$cf_value[0]}') )"; + } + break; + + default: + foreach ($cf_value as $value) { + if ($count > 1) { + $cf_sql .= " AND "; + } + + // When ARRAY NO LIKE but EQUAL + // Need to document what type of CF are managed as ARRAY + $cf_sql .= "( CFTPD.value = '{$value}' AND CFTPD.field_id = {$cf_id} )"; + $count ++; + } + break; + } + } else { + $cf_sql .= " ( CFTPD.value LIKE '%{$cf_value}%' AND CFTPD.field_id = {$cf_id} ) "; + } + $countmain ++; + } + } + + return array( + $ret, + $cf_sql + ); + } + + // This method is intended to return minimal data useful to create Test Plan Tree, + // for feature: + // test case tester execution assignment: + // PLATFORM IS NOT USED TO NAVIGATE => is not present on Settings Section. + // ONLY BUILD IS PRESENT on settings area + // + // Status on Latest execution on Build ANY PLATFORM is needed + // + // @param int $id test plan id + // @param mixed $filters + // @param mixed $options + // + // [tcase_id]: default null => get any testcase + // numeric => just get info for this testcase + // + // [keyword_id]: default 0 => do not filter by keyword id + // numeric/array() => filter by keyword id + // + // [assigned_to]: default NULL => do not filter by user assign. + // array() with user id to be used on filter + // IMPORTANT NOTICE: this argument is affected by + // [assigned_on_build] + // + // [build_id]: default 0 or null => do not filter by build id + // numeric => filter by build id + // + // [cf_hash]: default null => do not filter by Custom Fields values + // + // + // [urgencyImportance] : filter only Tc's with certain (urgency*importance)-value + // + // [tsuites_id]: default null. + // If present only tcversions that are children of this testsuites + // will be included + // + // [exec_type] default null -> all types. + public function getLinkedForTesterAssignmentTree($id, $filters = null, + $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe['tplan_id'] = intval($id); + + $my = $this->initGetLinkedForTree($filters, $options); + + // Need to detail better, origin of build_id. + // is got from GUI Filters area ? + if (($my['options']['allow_empty_build'] == 0) && + $my['filters']['build_id'] <= 0) { + // CRASH IMMEDIATELY + throw new Exception( + $debugMsg . " Can NOT WORK with \$my['filters']['build_id'] <= 0"); + } + if (! $my['green_light']) { + // No query has to be run, because we know in advance that we are + // going to get NO RECORDS + return null; + } + + $buildClause = array( + 'lex' => (' AND EE.build_id = ' . $my['filters']['build_id']), + 'exec_join' => (" AND E.build_id = " . $my['filters']['build_id']) + ); + if ($my['options']['allow_empty_build'] && + $my['filters']['build_id'] <= 0) { + $buildClause = array( + 'lex' => '', + 'exec_join' => '' + ); + } + + // + // Platforms have NOTHING TO DO HERE + $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.build_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " WHERE EE.testplan_id = " . $safe['tplan_id'] . $buildClause['lex'] . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.build_id "; + + // ------------------------------------------------------------------------------------- + // adding tcversion on output can be useful for Filter on Custom Field values, + // because we are saving values at TCVERSION LEVEL + $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . + " TCV.tc_external_id AS external_id, " . " COALESCE(E.status,'" . + $this->notRunStatusCode . "') AS exec_status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['ua'] . $my['join']['keywords'] . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.testplan_id = " . $safe['tplan_id'] . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . " AND E.id = LEX.id " . + $buildClause['exec_join'] . " WHERE TPTCV.testplan_id =" . + $safe['tplan_id'] . $my['where']['where'] . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEX.id IS NULL"; + + $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . + " TCV.tc_external_id AS external_id, " . " COALESCE(E.status,'" . + $this->notRunStatusCode . "') AS exec_status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['ua'] . $my['join']['keywords'] . + " JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.testplan_id = " . $safe['tplan_id'] . + " JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . " AND E.id = LEX.id " . // 20120903 + $buildClause['exec_join'] . " WHERE TPTCV.testplan_id =" . + $safe['tplan_id'] . $my['where']['where']; + + return $union; + } + + /** + */ + public function getLinkInfo($id, $tcase_id, $platform_id = null, $opt = null) + { + $debugMsg = 'Class: ' . __CLASS__ . ' - Method:' . __FUNCTION__; + $safe_id = array( + 'tplan_id' => 0, + 'platform_id' => 0, + 'tcase_id' => 0 + ); + $safe_id['tplan_id'] = intval($id); + $safe_id['tcase_id'] = intval($tcase_id); + + // check and die? + $my = array( + 'opt' => array( + 'output' => 'version_info', + 'tproject_id' => null, + 'build4assignment' => null, + 'collapse' => false + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = "/* $debugMsg */ " . + " SELECT TCV.id AS tcversion_id,TCV.version %%needle%% " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id "; + + $more_cols = ' '; + switch ($my['opt']['output']) { + case 'tcase_info': + if (is_null($my['opt']['tproject_id'])) { + $dummy = $this->tree_manager->get_node_hierarchy_info( + $safe_id['tplan_id']); + $my['opt']['tproject_id'] = $dummy['parent_id']; + } + $pp = $this->tcaseMgr->getPrefix($safe_id['tcase_id'], + $my['opt']['tproject_id']); + $prefix = $pp[0] . $this->tcaseCfg->glue_character; + $more_cols = ', NHTC.name, NHTC.id AS tc_id, ' . + $this->db->db->concat("'{$prefix}'", 'TCV.tc_external_id') . + ' AS full_external_id '; + + $sql .= " JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id "; + break; + + case 'assignment_info': + if (is_null($my['opt']['build4assignment'])) { + // CRASH IMMEDIATELY + throw new Exception( + __METHOD__ . + ' When your choice is to get assignment_info ' . + " you need to provide build id using 'build4assignment'"); + } + // Go ahead + $safe_id['build_id'] = intval($my['opt']['build4assignment']); + + $more_cols = ',USERS.login,USERS.first,USERS.last' . + ',TPTCV.id AS feature_id,TPTCV.platform_id,PLAT.name AS platform_name' . + ',NHTCV.parent_id AS tc_id,UA.user_id,TCV.importance,TPTCV.urgency' . + ',(TCV.importance * TPTCV.urgency) AS priority '; + $sql .= " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . + " ON UA.build_id = " . $safe_id['build_id'] . + " AND UA.feature_id = TPTCV.id "; + + $sql .= " LEFT OUTER JOIN {$this->tables['platforms']} PLAT " . + " ON PLAT.id = TPTCV.platform_id "; + + $sql .= " LEFT OUTER JOIN {$this->tables['users']} USERS " . + " ON USERS.id = UA.user_id "; + + break; + + case 'version_info': + $more_cols = ',TPTCV.platform_id'; + default: + break; + } + $sql = str_replace('%%needle%%', $more_cols, $sql) . + " WHERE TPTCV.testplan_id = {$safe_id['tplan_id']} " . + " AND NHTCV.parent_id = {$safe_id['tcase_id']} "; + + if (! is_null($platform_id) && + ($safe_id['platform_id'] = intval($platform_id)) > 0) { + $sql .= " AND TPTCV.platform_id = " . $safe_id['platform_id']; + } + + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + $rs = $my['opt']['collapse'] ? $rs[0] : $rs; + } + return $rs; + } + + /** + * + * @used-by printDocument.php + * testplan.class.exportLinkedItemsToXML() + * testplan.class.exportForResultsToXML + */ + public function getLinkedStaticView($id, $filters = null, $options = null) + { + $debugMsg = 'Class: ' . __CLASS__ . ' - Method:' . __FUNCTION__; + $my = array( + 'filters' => '', + 'options' => '' + ); + + $my['filters'] = array( + 'platform_id' => null, + 'tsuites_id' => null, + 'tcaseSet' => null, + 'build_id' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $my['options'] = array( + 'output' => 'map', + 'order_by' => null, + 'detail' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $safe['tplan'] = intval($id); + $io = $this->tree_manager->get_node_hierarchy_info($safe['tplan']); + list ($prefix,) = $this->tcaseMgr->getPrefix(null, $io['parent_id']); + unset($io); + $prefix .= $this->tcaseCfg->glue_character; + $feid = $this->db->db->concat("'{$prefix}'", 'TCV.tc_external_id'); + + $addWhere = array( + 'platform' => '', + 'tsuite' => '', + 'tcases' => '', + 'build' => '' + ); + $platQty = 0; + if (! is_null($my['filters']['platform_id'])) { + $dummy = (array) $my['filters']['platform_id']; + array_walk($dummy, 'intval'); + $addWhere['platform'] = 'AND TPTCV.platform_id IN (' . + implode(',', $dummy) . ')'; + $platQty = count((array) $my['filters']['platform_id']); + } + + if (! is_null($my['filters']['tsuites_id'])) { + $dummy = (array) $my['filters']['tsuites_id']; + array_walk($dummy, 'intval'); + $addWhere['tsuite'] = 'AND NH_TCASE.parent_id IN (' . + implode(',', $dummy) . ')'; + } + + if (! is_null($my['filters']['tcaseSet'])) { + $dummy = (array) $my['filters']['tcaseSet']; + array_walk($dummy, 'intval'); + $addWhere['tsuite'] = 'AND NH_TCASE.id IN (' . implode(',', $dummy) . + ')'; + } + + $join['build'] = ''; + $addField = '-1 AS assigned_to, '; + if (! is_null($my['filters']['build_id'])) { + $dummy = intval($my['filters']['build_id']); + $addWhere['build'] = 'AND UA.build_id =' . $dummy; + + $join['build'] = " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id "; + + $addField = " UA.user_id AS assigned_to,"; + } + + switch ($my['options']['detail']) { + case '4results': + $my['options']['output'] = 'array'; // FORCED + // have had some issues with query and ADODB on MySQL if only + // $sql = " SELECT NH_TCV.parent_id AS tc_id, {$feid} AS full_external_id,TCV.tc_external_id "; + // Need to understand why in future + $sql = "/* $debugMsg */ " . + " SELECT {$addField} NH_TCV.parent_id AS tc_id, TPTCV.platform_id, TPTCV.id AS feature_id, " . + " TCV.tc_external_id AS external_id, {$feid} AS full_external_id, TPTCV.tcversion_id "; + break; + + case 'full': + default: + $sql = "/* $debugMsg */ " . + " SELECT {$addField} NH_TCASE.parent_id AS testsuite_id, NH_TCV.parent_id AS tc_id, " . + " NH_TCASE.node_order AS spec_order, NH_TCASE.name," . + " TPTCV.platform_id, PLAT.name as platform_name, TPTCV.id AS feature_id, " . + " TPTCV.tcversion_id AS tcversion_id, " . + " TPTCV.node_order AS execution_order, TPTCV.urgency," . + " TCV.version AS version, TCV.active, TCV.summary," . + " TCV.tc_external_id AS external_id, TCV.execution_type,TCV.importance," . + " {$feid} AS full_external_id, (TPTCV.urgency * TCV.importance) AS priority "; + break; + } + + $sql .= " FROM {$this->tables['nodes_hierarchy']} NH_TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCV.parent_id = NH_TCASE.id " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NH_TCV.id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH_TCV.id " . + $join['build'] . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id "; + + $sql .= " WHERE TPTCV.testplan_id={$safe['tplan']} " . + " {$addWhere['platform']} {$addWhere['tsuite']} {$addWhere['build']}"; + + switch ($my['options']['output']) { + case 'array': + $rs = $this->db->get_recordset($sql); + break; + + case 'map': + if ($platQty == 1) { + $rs = $this->db->fetchRowsIntoMap($sql, 'tc_id', 0, - 1, + 'assigned_to'); + } else { + $rs = $this->db->fetchMapRowsIntoMap($sql, 'platform_id', + 'tc_id'); + } + break; + } + + return $rs; + } + + // need to recheck, because probably we need to be able + // to work without build id provided + // has to be based on TREE USED on features like: + // assign test case execution or set test case urgency + // + public function getLTCVNewGeneration($id, $filters = null, $options = null) + { + $my = array( + 'filters' => array(), + 'options' => array( + 'allow_empty_build' => 1, + 'addPriority' => false, + 'accessKeyType' => 'tcase+platform', + 'addImportance' => false, + 'addExecInfo' => true, + 'assigned_on_build' => null, + 'ua_user_alias' => '', + 'includeNotRun' => true, + 'ua_force_join' => false, + 'orderBy' => null + ) + ); + $amk = array( + 'filters', + 'options' + ); + foreach ($amk as $mk) { + $my[$mk] = array_merge($my[$mk], (array) $$mk); + } + + if (! is_null( + $sql2do = $this->getLinkedTCVersionsSQL($id, $my['filters'], + $my['options']))) { + // need to document better + if (is_array($sql2do)) { + $sql2run = $sql2do['exec']; + if ($my['options']['includeNotRun']) { + $sql2run .= ' UNION ' . $sql2do['not_run']; + } + } else { + $sql2run = $sql2do; + } + + // added when trying to fix: + // TICKET 5788: test case execution order not working on RIGHT PANE + // Anyway this did not help + if (! is_null($my['options']['orderBy'])) { + $sql2run = " SELECT * FROM ($sql2run) XX ORDER BY " . + $my['options']['orderBy']; + } + + switch ($my['options']['accessKeyType']) { + case 'tcase+platform': + $tplan_tcases = $this->db->fetchMapRowsIntoMap($sql2run, + 'tcase_id', 'platform_id'); // ,0,-1,'user_id'); + break; + + case 'tcase+platform+stackOnUser': + $tplan_tcases = $this->db->fetchMapRowsIntoMapStackOnCol( + $sql2run, 'tcase_id', 'platform_id', 'user_id'); + break; + + case 'index': + $tplan_tcases = $this->db->get_recordset($sql2run); + break; + + default: + $tplan_tcases = $this->db->fetchRowsIntoMap($sql2run, + 'tcase_id'); + break; + } + } + return $tplan_tcases; + } + + /** + * + * @used-by testplan::getLTCVNewGeneration() + * @use initGetLinkedForTree() + * + * @parameter map filters + * keys: + * 'tcase_id','keyword_id','assigned_to','exec_status','build_id', 'cf_hash', + * 'urgencyImportance', 'tsuites_id','platform_id', 'exec_type','tcase_name' + * filters defaults values are setted on initGetLinkedForTree() + * + * @parameter map options + * some defaults are managed here + * + * defaults for keys: 'hideTestCases','include_unassigned','allow_empty_build' + * are setted on initGetLinkedForTree(). + * + * + * + * @internal revisions + * @since 1.9.13 + */ + private function getLinkedTCVersionsSQL($id, $filters = null, + $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe['tplan_id'] = intval($id); + $my = $this->initGetLinkedForTree($filters, $options); + + $mop = array( + 'options' => array( + 'addExecInfo' => false, + 'specViewFields' => false, + 'assigned_on_build' => null, + 'testSuiteInfo' => false, + 'addPriority' => false, + 'addImportance' => false, + 'ignorePlatformAndBuild' => false, + 'ignoreBuild' => false, + 'ignorePlatform' => false, + 'ua_user_alias' => '', + 'ua_force_join' => false, + 'build_is_active' => false + ) + ); + + $my['options'] = array_merge($mop['options'], $my['options']); + + if (($my['options']['allow_empty_build'] == 0) && + $my['filters']['build_id'] <= 0) { + // CRASH IMMEDIATELY + throw new Exception( + $debugMsg . " Can NOT WORK with \$my['filters']['build_id'] <= 0"); + } + if (! $my['green_light']) { + // No query has to be run, because we know in advance that we are + // going to get NO RECORDS + return null; + } + + $buildClause = array( + 'lex' => (' AND EE.build_id = ' . $my['filters']['build_id']), + 'exec_join' => (" AND E.build_id = " . $my['filters']['build_id']) + ); + if ($my['options']['allow_empty_build'] && + $my['filters']['build_id'] <= 0) { + $buildClause = array( + 'lex' => '', + 'exec_join' => '' + ); + } + + // TICKET 5182: Add/Remove Test Cases -> Trying to assign new platform to executed test cases + // Before this ticket LEX was just on BUILD => ignoring platforms + // Need to understand if will create side effects. + // + if ($my['options']['ignorePlatformAndBuild']) { + $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " WHERE EE.testplan_id = " . $safe['tplan_id'] . + " GROUP BY EE.tcversion_id,EE.testplan_id "; + + $platformLEX = " "; + $platformEXEC = " "; + } elseif ($my['options']['ignoreBuild'] && + $my['options']['build_is_active']) { + $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " JOIN {$this->tables['builds']} B " . " ON B.id = EE.build_id " . + " WHERE EE.testplan_id = " . $safe['tplan_id'] . + " AND B.active = 1" . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id, B.id"; + + $platformLEX = " AND LEX.platform_id = TPTCV.platform_id "; + $platformEXEC = " AND E.platform_id = TPTCV.platform_id "; + } elseif ($my['options']['ignoreBuild']) { + $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " WHERE EE.testplan_id = " . $safe['tplan_id'] . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id"; + + // TICKET 5182 + $platformLEX = " AND LEX.platform_id = TPTCV.platform_id "; + $platformEXEC = " AND E.platform_id = TPTCV.platform_id "; + } else { + $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " WHERE EE.testplan_id = " . $safe['tplan_id'] . + $buildClause['lex'] . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; + + // TICKET 5182 + $platformLEX = " AND LEX.platform_id = TPTCV.platform_id "; + $platformEXEC = " AND E.platform_id = TPTCV.platform_id "; + } + + // ------------------------------------------------------------------------------------- + // adding tcversion on output can be useful for Filter on Custom Field values, + // because we are saving values at TCVERSION LEVEL + // + + // TICKET 5165: Issues with DISTINCT CLAUSE on TEXT field + // Do not know if other usages are going to cry due to missing fields + // + // $commonFields = " SELECT NH_TCASE.id AS tcase_id,NH_TCASE.id AS tc_id,TPTCV.tcversion_id,TCV.version," . + // " TCV.tc_external_id AS external_id, TCV.execution_type," . + // " TCV.summary, TCV.preconditions,TPTCV.id AS feature_id," . + // " TPTCV.platform_id,PLAT.name AS platform_name,TPTCV.node_order AS execution_order,". + // " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status "; + // + // $fullEID = $this->helperConcatTCasePrefix($safe['tplan_id']); + $commonFields = " SELECT NH_TCASE.name AS tcase_name, NH_TCASE.id AS tcase_id, " . + " NH_TCASE.id AS tc_id,TPTCV.tcversion_id,TCV.version," . + " TCV.tc_external_id AS external_id, TCV.execution_type,TCV.status," . + " TPTCV.id AS feature_id," . + ($my['options']['addPriority'] ? "(TPTCV.urgency * TCV.importance) AS priority," : '') . + " TPTCV.platform_id,PLAT.name AS platform_name,TPTCV.node_order AS execution_order," . + " COALESCE(E.status,'" . $this->notRunStatusCode . + "') AS exec_status, " . " E.execution_duration, " . + ($my['options']['addImportance'] ? " TCV.importance," : '') . + $this->helperConcatTCasePrefix($safe['tplan_id']) . + " AS full_external_id "; + + // used on tester assignment feature when working at test suite level + if (! is_null($my['options']['assigned_on_build'])) { + $commonFields .= ",UA.user_id {$my['options']['ua_user_alias']} "; + } + + if ($my['options']['addExecInfo']) { + $commonFields .= ",COALESCE(E.id,0) AS exec_id,E.tcversion_number,E.build_id AS exec_on_build,E.testplan_id AS exec_on_tplan"; + } + + if ($my['options']['specViewFields']) { + $commonFields .= ",NH_TCASE.name,TPTCV.creation_ts AS linked_ts,TPTCV.author_id AS linked_by" . + ",NH_TCASE.parent_id AS testsuite_id"; + } + + $my['join']['tsuites'] = ''; + if ($my['options']['testSuiteInfo']) { + $commonFields .= ",NH_TSUITE.name AS tsuite_name "; + $my['join']['tsuites'] = " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . + " ON NH_TSUITE.id = NH_TCASE.parent_id "; + } + + if ($my['options']['ua_force_join']) { + $my['join']['ua'] = str_replace('LEFT OUTER', ' ', $my['join']['ua']); + } + + $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . + $commonFields . " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['tsuites'] . $my['join']['ua'] . $my['join']['keywords'] . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . $platformLEX . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.testplan_id = " . $safe['tplan_id'] . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . $platformEXEC . + " AND E.id = LEX.id " . // TICKET 6159 + $buildClause['exec_join'] . " WHERE TPTCV.testplan_id =" . + $safe['tplan_id'] . ' ' . $my['where']['where'] . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEX.id IS NULL"; + + $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . + $commonFields . " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . + $my['join']['tsuites'] . $my['join']['ua'] . $my['join']['keywords'] . + " JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . $platformLEX . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.testplan_id = " . $safe['tplan_id'] . + " JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . $platformEXEC . + " AND E.id = LEX.id " . // TICKET 6159 + $buildClause['exec_join'] . " WHERE TPTCV.testplan_id =" . + $safe['tplan_id'] . ' ' . $my['where']['where']; + + return $union; + } + + /** + */ + public function getPublicAttr($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT is_public FROM {$this->tables['testplans']} " . + " WHERE id =" . intval($id); + $ret = $this->db->get_recordset($sql); + return $ret[0]['is_public']; + } + + /** + */ + private function getBuildByCriteria($id, $criteria) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['opt'] = array( + 'active' => null, + 'open' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $options); + + switch ($criteria) { + case 'maxID': + $sql = " /* $debugMsg */ " . + " SELECT MAX(id) AS id,testplan_id, name, notes, active, is_open," . + " release_date,closed_on_date " . + " FROM {$this->tables['builds']} WHERE testplan_id = {$id} "; + break; + } + + if (! is_null($my['opt']['active'])) { + $sql .= " AND active = " . intval($my['opt']['active']) . " "; + } + if (! is_null($my['opt']['open'])) { + $sql .= " AND is_open = " . intval($my['opt']['open']) . " "; + } + + return $this->db->get_recordset($sql); + } + + /** + */ + public function writeExecution($ex) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $execNotes = $this->db->prepare_string($ex->notes); + if (property_exists($ex, 'executionTimeStampISO')) { + $execTS = "'" . $ex->executionTimeStampISO . "'"; + } else { + $execTS = $this->db->db_now(); + } + + $sql = "/* {$debugMsg} */ " . + "INSERT INTO {$this->tables['executions']} " . + " (testplan_id, platform_id, build_id, " . + " tcversion_id, tcversion_number, status, " . + " tester_id, execution_ts, execution_type, notes) " . " VALUES(" . + " {$ex->testPlanID},{$ex->platformID},{$ex->buildID}," . + " {$ex->testCaseVersionID}, {$ex->testCaseVersionNumber},'{$ex->statusCode}'," . + " {$ex->testerID},{$execTS}, {$ex->executionType}, '{$execNotes}')"; + + $this->db->exec_query($sql); + $execID = $this->db->insert_id($this->tables['executions']); + + // Do we have steps exec info? + if (property_exists($ex, 'steps')) { + // steps [] of stepExec + // + // Same execution ts that WHOLE Testcase, the field do not exists in table + // + // Here as JSON + // { + // "stepNumber":1, + // "notes":"This is an execution created via REST API", + // "statusCode":"b", + // } + // + // Brute force approach: + // Get all steps from specification + $allsteps = 0; + $gssOpt = [ + 'fields2get' => 'TCSTEPS.id,TCSTEPS.step_number', + 'accessKey' => "step_number", + 'renderGhostSteps' => false, + 'renderImageInline' => false + ]; + $stepsSpec = $this->tcaseMgr->getStepsSimple($ex->testCaseVersionID, + $allsteps, $gssOpt); + + foreach ($ex->steps as $stepExec) { + // if step number does not exist -> ignore it in silence + if (isset($stepsSpec[$stepExec->stepNumber])) { + $stepID = intval($stepsSpec[$stepExec->stepNumber]["id"]); + $sql = " INSERT INTO {$this->tables['execution_tcsteps']} + (execution_id,tcstep_id,notes,status) "; + $values = " VALUES ( {$execID}, {$stepID}," . "'" . + $this->db->prepare_string($stepExec->notes) . "'," . "'" . + $this->db->prepare_string($stepExec->statusCode) . "')"; + $sql .= " " . $values; + + $this->db->exec_query($sql); + } + } + } + + return $execID; + } + + /** + */ + private function getExecutionDurationForSet($execIDSet) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ " . + "SELECT E.id, E.execution_duration AS duration " . + "FROM {$this->tables['executions']} E " . "WHERE id IN (" . + implode(',', $execIDSet) . ')'; + return $this->db->get_recordset($sql); + } + + /** + */ + public function exportForResultsToXML($id, $context, $optExport = array(), + $filters = null) + { + $my['filters'] = array( + 'platform_id' => null, + 'tcaseSet' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $item = $this->get_by_id($id, + array( + 'output' => 'minimun', + 'caller' => __METHOD__ + )); + + $xmlString = "\n" . + "\n"; + $xmlString .= "\n"; + $xmlString .= "\t\n"; + + $xmlString .= "\t\n"; + + if (isset($context['build_id']) && $context['build_id'] > 0) { + $dummy = $this->get_builds($id); + $info = $dummy[$context['build_id']]; + $xmlString .= "\t\n"; + } + + // get target platform (if exists) + if ($context['platform_id'] > 0) { + $info = $this->platform_mgr->getByID($context['platform_id']); + $xmlString .= "\t\n"; + $my['filters']['platform_id'] = $context['platform_id']; + } + + // + // + // + // u0113 + // + // 2008-09-08 14:00:00 + // p + // functionality works great + // + $mm = $this->getLinkedStaticView($id, $my['filters'], + array( + 'output' => 'array', + 'detail' => '4results' + )); + + if (! is_null($mm) && ($tcaseQty = count($mm)) > 0) { + + // Custom fields processing + $xcf = $this->cfield_mgr->get_linked_cfields_at_execution( + $item['tproject_id'], 1, 'testcase'); + if (! is_null($xcf) && ! empty($xcf)) { + for ($gdx = 0; $gdx < $tcaseQty; $gdx ++) { + $mm[$gdx]['xmlcustomfields'] = $this->cfield_mgr->exportValueAsXML( + $xcf); + } + } + + // Test Case Steps + $gso = array( + 'fields2get' => 'TCSTEPS.id,TCSTEPS.step_number', + 'renderGhostSteps' => false, + 'renderImageInline' => false + ); + $stepRootElem = "{{XMLCODE}}"; + $stepTemplate = "\n" . '' . "\n" . + "\t||STEP_NUMBER||\n" . + "\tp\n" . "\t||NOTES||\n" . + "\n"; + $stepInfo = array( + "||STEP_NUMBER||" => "step_number", + "||NOTES||" => "notes" + ); + + for ($gdx = 0; $gdx < $tcaseQty; $gdx ++) { + $mm[$gdx]['steps'] = $this->tcaseMgr->getStepsSimple( + $mm[$gdx]['tcversion_id'], 0, $gso); + if (! is_null($mm[$gdx]['steps'])) { + $qs = count($mm[$gdx]['steps']); + for ($scx = 0; $scx < $qs; $scx ++) { + $mm[$gdx]['steps'][$scx]['notes'] = 'your step exec notes'; + } + $mm[$gdx]['xmlsteps'] = exportDataToXML($mm[$gdx]['steps'], + $stepRootElem, $stepTemplate, $stepInfo, true); + } + } + } + + $xml_root = null; + $xml_template = "\n" . "\t" . + "\n" . "\t\t" . "X" . "\n" . "\t\t" . + "test link rocks " . "\n" . "\t\t" . + "put login here" . "\n" . "\t\t" . + "" . "\n" . "\t\t" . + "YYYY-MM-DD HH:MM:SS" . "\n" . "\t\t" . + "put one of your bugs id here (repeat the line as many times you need)" . + "\n" . "\t\t" . "put another of your bugs id here" . + "\n" . "\t\t" . "||STEPS||" . "\n" . "\t\t" . "||CUSTOMFIELDS||" . + "\n" . "\t" . "\n"; + + $xml_mapping = null; + $xml_mapping = array( + "{{FULLEXTERNALID}}" => "full_external_id", + "||CUSTOMFIELDS||" => "xmlcustomfields", + "||STEPS||" => "xmlsteps" + ); + + $linked_testcases = exportDataToXML($mm, $xml_root, $xml_template, + $xml_mapping, true); + return $xmlString .= $linked_testcases . "\n\n"; + } + + /** + */ + public function setActive($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ " . + "UPDATE {$this->tables['testplans']} SET active=1 WHERE id=" . + intval($id); + $this->db->exec_query($sql); + } + + /** + */ + public function setInactive($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ " . + "UPDATE {$this->tables['testplans']} SET active=0 WHERE id=" . + intval($id); + $this->db->exec_query($sql); + } + + /** + */ + public function getByAPIKey($apiKey, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['opt'] = array( + 'checkIsValid' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + $fields2get = $my['opt']['checkIsValid'] ? 'id' : '*'; + + $safe = $this->db->prepare_string($apiKey); + + $sql = "/* $debugMsg */ " . + " SELECT {$fields2get} FROM {$this->tables['testplans']} " . + " WHERE api_key = '{$safe}'"; + + $rs = $this->db->get_recordset($sql); + return $rs ? $rs[0] : null; + } + + /** + * + * @used-by planEdit.php + */ + public function getFileUploadRelativeURL($id) + { + // do_action,tplan_id as expected in planEdit.php + return "lib/plan/planEdit.php?do_action=fileUpload&tplan_id=" . + intval($id); + } + + /** + * + * @used-by planEdit.php + */ + public function getDeleteAttachmentRelativeURL($id) + { + // do_action,tplan_id as expected in planEdit.php + return "lib/plan/planEdit.php?do_action=deleteFile&tplan_id=" . + intval($id) . "&file_id="; + } + + /** + * + * @used-by + */ + public function getAllExecutionsWithBugs($id, $platform_id = null, + $build_id = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $safe['tplan_id'] = intval($id); + $fullEID = $this->helperConcatTCasePrefix($safe['tplan_id']); + + $sql = " /* $debugMsg */ " . + " SELECT DISTINCT E.id AS exec_id,EB.bug_id,NHTC.id AS tcase_id, NHTC.id AS tc_id, " . + " NHTC.name AS name, NHTSUITE.name AS tsuite_name, TCV.tc_external_id AS external_id," . + " $fullEID AS full_external_id " . + " FROM {$this->tables['executions']} E " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.tcversion_id = E.tcversion_id " . + " AND TPTCV.testplan_id = E.testplan_id " . + " JOIN {$this->tables['execution_bugs']} EB " . + " ON EB.execution_id = E.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = E.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = E.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTSUITE " . + " ON NHTSUITE.id = NHTC.parent_id " . " WHERE TPTCV.testplan_id = " . + $safe['tplan_id']; + + return $this->db->get_recordset($sql); + } + + /** + */ + public function getLTCVOnTestPlan($id, $filters = null, $options = null) + { + $my = array( + 'filters' => array(), + 'options' => array( + 'allow_empty_build' => 1, + 'addPriority' => false, + 'accessKeyType' => 'tcase+platform', + 'addImportance' => false, + 'includeNotRun' => true, + 'orderBy' => null + ) + ); + $amk = array( + 'filters', + 'options' + ); + foreach ($amk as $mk) { + $my[$mk] = array_merge($my[$mk], (array) $$mk); + } + + $my['options']['ignorePlatformAndBuild'] = true; + if (! is_null( + $sql2do = $this->getLinkedTCVersionsSQL($id, $my['filters'], + $my['options']))) { + // need to document better + if (is_array($sql2do)) { + $sql2run = $sql2do['exec']; + if ($my['options']['includeNotRun']) { + $sql2run .= ' UNION ' . $sql2do['not_run']; + } + } else { + $sql2run = $sql2do; + } + + // added when trying to fix: + // TICKET 5788: test case execution order not working on RIGHT PANE + // Anyway this did not help + if (! is_null($my['options']['orderBy'])) { + $sql2run = " SELECT * FROM ($sql2run) XX ORDER BY " . + $my['options']['orderBy']; + } + + switch ($my['options']['accessKeyType']) { + case 'tcase+platform': + $tplan_tcases = $this->db->fetchMapRowsIntoMap($sql2run, + 'tcase_id', 'platform_id'); // ,0,-1,'user_id'); + break; + + case 'tcase+platform+stackOnUser': + $tplan_tcases = $this->db->fetchMapRowsIntoMapStackOnCol( + $sql2run, 'tcase_id', 'platform_id', 'user_id'); + break; + + case 'index': + $tplan_tcases = $this->db->get_recordset($sql2run); + break; + + default: + $tplan_tcases = $this->db->fetchRowsIntoMap($sql2run, + 'tcase_id'); + break; + } + } + return $tplan_tcases; + } + + /** + */ + public function getLTCVOnTestPlanPlatform($id, $filters = null, + $options = null) + { + $my = array( + 'filters' => array(), + 'options' => array( + 'allow_empty_build' => 1, + 'addPriority' => false, + 'accessKeyType' => 'tcase+platform', + 'addImportance' => false, + 'includeNotRun' => true, + 'orderBy' => null + ) + ); + $amk = array( + 'filters', + 'options' + ); + foreach ($amk as $mk) { + $my[$mk] = array_merge($my[$mk], (array) $$mk); + } + + $my['options']['ignoreBuild'] = true; + if (! is_null( + $sql2do = $this->getLinkedTCVersionsSQL($id, $my['filters'], + $my['options']))) { + // need to document better + if (is_array($sql2do)) { + $sql2run = $sql2do['exec']; + if ($my['options']['includeNotRun']) { + $sql2run .= ' UNION ' . $sql2do['not_run']; + } + } else { + $sql2run = $sql2do; + } + + // added when trying to fix: + // TICKET 5788: test case execution order not working on RIGHT PANE + // Anyway this did not help + if (! is_null($my['options']['orderBy'])) { + $sql2run = " SELECT * FROM ($sql2run) XX ORDER BY " . + $my['options']['orderBy']; + } + + switch ($my['options']['accessKeyType']) { + case 'tcase+platform': + $tplan_tcases = $this->db->fetchMapRowsIntoMap($sql2run, + 'tcase_id', 'platform_id'); // ,0,-1,'user_id'); + break; + + case 'tcase+platform+stackOnUser': + $tplan_tcases = $this->db->fetchMapRowsIntoMapStackOnCol( + $sql2run, 'tcase_id', 'platform_id', 'user_id'); + break; + + case 'index': + $tplan_tcases = $this->db->get_recordset($sql2run); + break; + + default: + $tplan_tcases = $this->db->fetchRowsIntoMap($sql2run, + 'tcase_id'); + break; + } + } + return $tplan_tcases; + } + + /** + */ + public function getLinkedItems($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ " . + " SELECT parent_id AS tcase_id,TPTCV.platform_id,TPTCV.node_order " . + " FROM {$this->tables['nodes_hierarchy']} NHTC " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTC.id " . + " WHERE TPTCV.testplan_id = " . intval($id); + + return $this->db->fetchMapRowsIntoMap($sql, 'tcase_id', 'platform_id'); + } + + /** + * + * @since 1.9.14 + */ + public function getLinkedFeatures($id, $filters = null, $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array( + 'filters' => array(), + $options => array() + ); + $my['filters'] = array( + 'platform_id' => null + ); + $my['options'] = array( + 'accessKey' => array( + 'tcase_id', + 'platform_id' + ) + ); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = " /* $debugMsg */ " . + " SELECT parent_id AS tcase_id,TPTCV.platform_id,TPTCV.id AS feature_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTC " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.tcversion_id = NHTC.id " . + " WHERE TPTCV.testplan_id = " . intval($id); + + if (! is_null($my['filters']['platform_id'])) { + $sql .= " AND TPTCV.platform_id = " . + intval($my['filters']['platform_id']); + } + + if (! is_null($my['filters']['tcase_id'])) { + $sql .= " AND NHTC.parent_id IN (" . + implode(',', $my['filters']['tcase_id']) . ") "; + } + + return $this->db->fetchMapRowsIntoMap($sql, + $my['options']['accessKey'][0], $my['options']['accessKey'][1]); + } + + /** + * + * @used-by getFilteredLinkedVersions() - specview.php + * @used-by indirectly on tc_exec_assigment.php for test suites + * + */ + public function getLinkedTCVXmen($id, $filters = null, $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe['tplan_id'] = intval($id); + $my = $this->initGetLinkedForTree($filters, $options); + + // adding tcversion on output can be useful for Filter on Custom Field values, + // because we are saving values at TCVERSION LEVEL + $commonFields = "/* $debugMsg */ " . + " SELECT NH_TCASE.name AS tcase_name, NH_TCASE.id AS tcase_id, " . + " NH_TCASE.id AS tc_id,TPTCV.tcversion_id,TCV.version," . + " TCV.tc_external_id AS external_id, TCV.execution_type,TCV.status," . + " TPTCV.id AS feature_id," . + ($my['options']['addPriority'] ? "(TPTCV.urgency * TCV.importance) AS priority," : '') . + " TPTCV.platform_id,TPTCV.node_order AS execution_order," . + ($my['options']['addImportance'] ? " TCV.importance," : '') . + $this->helperConcatTCasePrefix($safe['tplan_id']) . + " AS full_external_id "; + + $commonFields .= ",UA.user_id"; + $commonFields .= ",NH_TCASE.name,TPTCV.creation_ts AS linked_ts,TPTCV.author_id AS linked_by" . + ",NH_TCASE.parent_id AS testsuite_id"; + + $commonFields .= ",NH_TSUITE.name AS tsuite_name "; + + $my['join']['tsuites'] = " JOIN {$this->tables['nodes_hierarchy']} NH_TSUITE " . + " ON NH_TSUITE.id = NH_TCASE.parent_id "; + + $sql = $commonFields . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['tsuites'] . $my['join']['ua'] . $my['join']['keywords'] . + " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . + $my['where']['where']; + + return $this->db->fetchMapRowsIntoMapStackOnCol($sql, 'tcase_id', + 'platform_id', 'user_id'); + } + + /** + */ + public function getExecCountOnBuild($id, $build_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe['tplan_id'] = intval($id); + $safe['build_id'] = intval($build_id); + + $sql = "/* $debugMsg */ SELECT COUNT(0) AS qty " . + " FROM {$this->tables['executions']} E " . + " WHERE E.testplan_id = {$safe['tplan_id']} " . + " AND E.build_id = {$safe['build_id']}"; + + $rs = $this->db->get_recordset($sql); + + return $rs[0]['qty']; + } + + /** + */ + public function getFeatureByID($feature_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $target = (array) $feature_id; + foreach ($target as $idx => $tg) { + $target[$idx] = intval($tg); + } + $inSet = implode(',', $target); + + $sql = " /* $debugMsg */ " . + " SELECT parent_id AS tcase_id,tcversion_id,platform_id,TPTCV.id " . + " FROM {$this->tables['nodes_hierarchy']} NHTC " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.tcversion_id = NHTC.id " . " WHERE TPTCV.id IN (" . $inSet . + ")"; + + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + */ + public function getVersionLinked($tplan_id, $tcase_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = "/* $debugMsg */ + SELECT tcversion_id FROM {$this->tables['testplan_tcversions']} TPTCV JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id - WHERE TPTCV.testplan_id = $tplan_id - AND NH_TCV.parent_id = $tcase_id"; - - $rs = $this->db->get_recordset($sql); - - // We trust DB is OK => no matter the record I use - // testcase version id will be the same. - // - return $rs[0]['tcversion_id']; - - } - - // - // This method is intended to return minimal data useful - // to create Execution Tree. - // - // The Status on Latest execution: - // is computed considering only the selected Platform - // - // @param int $id test plan id - // @param mixed $filters - // @param mixed $options - // - // [tcase_id]: default null => get any testcase - // numeric => just get info for this testcase - // - // - // [keyword_id]: default 0 => do not filter by keyword id - // numeric/array() => filter by keyword id - // - // - // [assigned_to]: default NULL => do not filter by user assign. - // array() with user id to be used on filter - // IMPORTANT NOTICE: this argument is affected by - // [assigned_on_build] - // - // [build_id]: default 0 or null => do not filter by build id - // numeric => filter by build id - // - // - // [cf_hash]: default null => do not filter by Custom Fields values - // - // - // [urgencyImportance] : - // filter only Tc's with certain (urgency*importance)-value - // - // [tsuites_id]: default null. - // If present only tcversions that are children - // of this testsuites will be included - // - // [exec_type] default null -> all types. - // [platform_id] - // - function getLinkedForExecTreeIVU($id,$filters=null,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - - $safe['tplan_id'] = intval($id); - $my = $this->initGetLinkedForTree($safe['tplan_id'],$filters,$options); - - if( !isset($my['filters']['platform_id']) || - $my['filters']['platform_id'] == 0 ) { - throw new Exception(__FUNCTION__ . " Needs Platform ID", 1); - } - - - if( !$my['green_light'] ) { - // No query has to be run, because we know in advance that we are - // going to get NO RECORDS - return null; - } - - $safe['platform_id'] = intval($my['filters']['platform_id']); - - $sqlLExecOnTPLANPL = - " SELECT LEBTPPL.tcversion_id,LEBTPPL.testplan_id, - LEBTPPL.platform_id, LEBTPPL.id - FROM {$this->views['latest_exec_by_testplan_plat']} LEBTPPL - WHERE LEBTPPL.testplan_id = {$safe['tplan_id']} - AND LEBTPPL.platform_id = {$safe['platform_id']} "; - - // When there is request to filter by BUG ID, - // because BUGS are linked only to EXECUTED test case versions, - // the not_run piece of union is USELESS - $union['not_run'] = null; - - $nht = $this->tables['nodes_hierarchy']; - - $theView = $this->views['latest_exec_by_testplan_plat']; - - if(!isset($my['filters']['bug_id'])) { - // adding tcversion on output can be useful for - // Filter on Custom Field values, - // because we are saving values at TCVERSION LEVEL - // - - - // no need to add - // " AND TPTCV.platform_id =" . $safe['platform_id'] . - // Because is added in $where - // - $notrun = $this->notRunStatusCode; - $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, - '" . $this->notRunStatusCode . "' AS exec_status - FROM {$this->tables['testplan_tcversions']} TPTCV - JOIN $nht NH_TCV ON NH_TCV.id = TPTCV.tcversion_id - JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['keywords'] . - $my['join']['ua'] . - $my['join']['cf'] . - - " /* Get REALLY NOT RUN => - BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ - LEFT OUTER JOIN {$theView} AS LEXBTPLANPL - ON LEXBTPLANPL.testplan_id = TPTCV.testplan_id - AND LEXBTPLANPL.tcversion_id = TPTCV.tcversion_id " . - - "/* - mmm, we want not run => why to use Executions? - LEFT OUTER JOIN {$this->tables['executions']} E - ON E.tcversion_id = TPTCV.tcversion_id - AND E.testplan_id = TPTCV.testplan_id - */ " . - - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['not_run'] . - " AND LEXBTPLANPL.id IS NULL"; - } - - $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, - E.status AS exec_status + WHERE TPTCV.testplan_id = $tplan_id + AND NH_TCV.parent_id = $tcase_id"; + + $rs = $this->db->get_recordset($sql); + + // We trust DB is OK => no matter the record I use + // testcase version id will be the same. + // + return $rs[0]['tcversion_id']; + } + + // This method is intended to return minimal data useful + // to create Execution Tree. + // + // The Status on Latest execution: + // is computed considering only the selected Platform + // + // @param int $id test plan id + // @param mixed $filters + // @param mixed $options + // + // [tcase_id]: default null => get any testcase + // numeric => just get info for this testcase + // + // [keyword_id]: default 0 => do not filter by keyword id + // numeric/array() => filter by keyword id + // + // [assigned_to]: default NULL => do not filter by user assign. + // array() with user id to be used on filter + // IMPORTANT NOTICE: this argument is affected by + // [assigned_on_build] + // + // [build_id]: default 0 or null => do not filter by build id + // numeric => filter by build id + // + // + // [cf_hash]: default null => do not filter by Custom Fields values + // + // + // [urgencyImportance] : + // filter only Tc's with certain (urgency*importance)-value + // + // [tsuites_id]: default null. + // If present only tcversions that are children + // of this testsuites will be included + // + // [exec_type] default null -> all types. + // [platform_id] + public function getLinkedForExecTreeIVU($id, $filters = null, + $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $safe['tplan_id'] = intval($id); + $my = $this->initGetLinkedForTree($filters, $options); + + if (! isset($my['filters']['platform_id']) || + $my['filters']['platform_id'] == 0) { + throw new Exception(__FUNCTION__ . " Needs Platform ID", 1); + } + + if (! $my['green_light']) { + // No query has to be run, because we know in advance that we are + // going to get NO RECORDS + return null; + } + + $safe['platform_id'] = intval($my['filters']['platform_id']); + + // When there is request to filter by BUG ID, + // because BUGS are linked only to EXECUTED test case versions, + // the not_run piece of union is USELESS + $union['not_run'] = null; + + $nht = $this->tables['nodes_hierarchy']; + + $theView = $this->views['latest_exec_by_testplan_plat']; + + if (! isset($my['filters']['bug_id'])) { + // adding tcversion on output can be useful for + // Filter on Custom Field values, + // because we are saving values at TCVERSION LEVEL + + // no need to add + // " AND TPTCV.platform_id =" . $safe['platform_id'] . + // Because is added in $where + // $notrun = $this->notRunStatusCode; + $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, + '" . $this->notRunStatusCode . + "' AS exec_status + FROM {$this->tables['testplan_tcversions']} TPTCV + JOIN $nht NH_TCV ON NH_TCV.id = TPTCV.tcversion_id + JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['keywords'] . $my['join']['ua'] . $my['join']['cf'] . + " /* Get REALLY NOT RUN => + BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ + LEFT OUTER JOIN {$theView} AS LEXBTPLANPL + ON LEXBTPLANPL.testplan_id = TPTCV.testplan_id + AND LEXBTPLANPL.tcversion_id = TPTCV.tcversion_id " . + "/* + mmm, we want not run => why to use Executions? + LEFT OUTER JOIN {$this->tables['executions']} E + ON E.tcversion_id = TPTCV.tcversion_id + AND E.testplan_id = TPTCV.testplan_id + */ " . " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . + $my['where']['not_run'] . " AND LEXBTPLANPL.id IS NULL"; + } + + $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, + E.status AS exec_status FROM {$this->tables['testplan_tcversions']} TPTCV JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id JOIN $nht NH_TCV ON NH_TCV.id = TPTCV.tcversion_id - JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['keywords'] . - $my['join']['ua'] . - $my['join']['cf'] . - - " JOIN {$theView} AS LEXBTPLANPL - ON LEXBTPLANPL.testplan_id = TPTCV.testplan_id + JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['keywords'] . $my['join']['ua'] . $my['join']['cf'] . + " JOIN {$theView} AS LEXBTPLANPL + ON LEXBTPLANPL.testplan_id = TPTCV.testplan_id AND LEXBTPLANPL.platform_id = TPTCV.platform_id AND LEXBTPLANPL.tcversion_id = TPTCV.tcversion_id - JOIN {$this->tables['executions']} E - ON E.id = LEXBTPLANPL.id - AND E.testplan_id = LEXBTPLANPL.testplan_id - AND E.platform_id = LEXBTPLANPL.platform_id - WHERE TPTCV.testplan_id = {$safe['tplan_id']} " . - $my['where']['where']; - - $xql = is_null($union['not_run']) ? $union['exec'] : $union; - - return $xql; - } - - // - // This method is intended to return minimal data useful - // to create Execution Tree. - // - // The Status on Latest execution: - // is computed considering only the test plan, doing - // logic ignoring selected build & selected platform - // - // @param int $id test plan id - // @param mixed $filters - // @param mixed $options - // - // [tcase_id]: default null => get any testcase - // numeric => just get info for this testcase - // - // - // [keyword_id]: default 0 => do not filter by keyword id - // numeric/array() => filter by keyword id - // - // - // [assigned_to]: default NULL => do not filter by user assign. - // array() with user id to be used on filter - // IMPORTANT NOTICE: this argument is affected by - // [assigned_on_build] - // - // [build_id]: default 0 or null => do not filter by build id - // numeric => filter by build id - // - // - // [cf_hash]: default null => do not filter by Custom Fields values - // - // - // [urgencyImportance] : - // filter only Tc's with certain (urgency*importance)-value - // - // [tsuites_id]: default null. - // If present only tcversions that are children - // of this testsuites will be included - // - // [exec_type] default null -> all types. - // [platform_id] - // - function getLinkedForExecTreeCross($id,$filters=null,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - - $safe['tplan_id'] = intval($id); - $my = $this->initGetLinkedForTree($safe['tplan_id'],$filters,$options); - - if( !$my['green_light'] ) { - // No query has to be run, because we know in advance that we are - // going to get NO RECORDS - return null; - } - - - $sqlLatestExecOnTPLAN = - " SELECT LEBTP.tcversion_id,LEBTP.testplan_id, LEBTP.id - FROM {$this->views['latest_exec_by_testplan']} LEBTP - WHERE LEBTP.testplan_id = {$safe['tplan_id']} "; - - // When there is request to filter by BUG ID, - // because BUGS are linked only to EXECUTED test case versions, - // the not_run piece of union is USELESS - $union['not_run'] = null; - - $nht = $this->tables['nodes_hierarchy']; - - if(!isset($my['filters']['bug_id'])) { - // adding tcversion on output can be useful for - // Filter on Custom Field values, - // because we are saving values at TCVERSION LEVEL - // - - $notrun = $this->notRunStatusCode; - $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, - COALESCE(E.status,'" . $notrun . "') AS exec_status - FROM {$this->tables['testplan_tcversions']} TPTCV - JOIN $nht NH_TCV ON NH_TCV.id = TPTCV.tcversion_id - JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['keywords'] . - $my['join']['ua'] . - $my['join']['cf'] . - - " /* Get REALLY NOT RUN => - BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLatestExecOnTPLAN}) AS LEXBTPLAN " . - " ON LEXBTPLAN.testplan_id = TPTCV.testplan_id " . - " AND LEXBTPLAN.tcversion_id = TPTCV.tcversion_id " . - " AND LEXBTPLAN.testplan_id = " . $safe['tplan_id'] . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['not_run'] . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND LEXBTPLAN.id IS NULL"; - } - - $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, - COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status + JOIN {$this->tables['executions']} E + ON E.id = LEXBTPLANPL.id + AND E.testplan_id = LEXBTPLANPL.testplan_id + AND E.platform_id = LEXBTPLANPL.platform_id " . $my['join']['bugs'] . + " WHERE TPTCV.testplan_id = {$safe['tplan_id']} " . + $my['where']['where']; + + return is_null($union['not_run']) ? $union['exec'] : $union; + } + + // This method is intended to return minimal data useful + // to create Execution Tree. + // + // The Status on Latest execution: + // is computed considering only the test plan, doing + // logic ignoring selected build & selected platform + // + // @param int $id test plan id + // @param mixed $filters + // @param mixed $options + // + // [tcase_id]: default null => get any testcase + // numeric => just get info for this testcase + // + // + // [keyword_id]: default 0 => do not filter by keyword id + // numeric/array() => filter by keyword id + // + // + // [assigned_to]: default NULL => do not filter by user assign. + // array() with user id to be used on filter + // IMPORTANT NOTICE: this argument is affected by + // [assigned_on_build] + // + // [build_id]: default 0 or null => do not filter by build id + // numeric => filter by build id + // + // + // [cf_hash]: default null => do not filter by Custom Fields values + // + // + // [urgencyImportance] : + // filter only Tc's with certain (urgency*importance)-value + // + // [tsuites_id]: default null. + // If present only tcversions that are children + // of this testsuites will be included + // + // [exec_type] default null -> all types. + // [platform_id] + public function getLinkedForExecTreeCross($id, $filters = null, + $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $safe['tplan_id'] = intval($id); + $my = $this->initGetLinkedForTree($filters, $options); + + if (! $my['green_light']) { + // No query has to be run, because we know in advance that we are + // going to get NO RECORDS + return null; + } + + $sqlLatestExecOnTPLAN = " SELECT LEBTP.tcversion_id,LEBTP.testplan_id, LEBTP.id + FROM {$this->views['latest_exec_by_testplan']} LEBTP + WHERE LEBTP.testplan_id = {$safe['tplan_id']} "; + + // When there is request to filter by BUG ID, + // because BUGS are linked only to EXECUTED test case versions, + // the not_run piece of union is USELESS + $union['not_run'] = null; + + $nht = $this->tables['nodes_hierarchy']; + + if (! isset($my['filters']['bug_id'])) { + // adding tcversion on output can be useful for + // Filter on Custom Field values, + // because we are saving values at TCVERSION LEVEL + + $notrun = $this->notRunStatusCode; + $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, + COALESCE(E.status,'" . $notrun . + "') AS exec_status + FROM {$this->tables['testplan_tcversions']} TPTCV + JOIN $nht NH_TCV ON NH_TCV.id = TPTCV.tcversion_id + JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH_TCV.id + JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['keywords'] . $my['join']['ua'] . $my['join']['cf'] . + " /* Get REALLY NOT RUN => + BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLatestExecOnTPLAN}) AS LEXBTPLAN " . + " ON LEXBTPLAN.testplan_id = TPTCV.testplan_id " . + " AND LEXBTPLAN.tcversion_id = TPTCV.tcversion_id " . + " AND LEXBTPLAN.testplan_id = " . $safe['tplan_id'] . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . + $my['where']['not_run'] . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND LEXBTPLAN.id IS NULL"; + } + + $union['exec'] = "/* {$debugMsg} sqlUnion - executions */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id, + COALESCE(E.status,'" . $this->notRunStatusCode . + "') AS exec_status FROM {$this->tables['testplan_tcversions']} TPTCV JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id JOIN $nht NH_TCV ON NH_TCV.id = TPTCV.tcversion_id - JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['keywords'] . - $my['join']['ua'] . - $my['join']['cf'] . - - " JOIN ({$sqlLatestExecOnTPLAN}) AS LEXBTPLAN " . - " ON LEXBTPLAN.testplan_id = TPTCV.testplan_id " . - " AND LEXBTPLAN.tcversion_id = TPTCV.tcversion_id " . - " AND LEXBTPLAN.testplan_id = " . $safe['tplan_id'] . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEXBTPLAN.id " . - " AND E.testplan_id = LEXBTPLAN.testplan_id " . - $my['where']['where']; - - $xql = is_null($union['not_run']) ? $union['exec'] : $union; - return $xql; - } - - - /** - * Rules - * 1. code is a string of length = 1 => one character - * 2. domain will be a-z - * - */ - function sanitizeExecStatus( $status ) { - - $statusSet = (array)$status; - $sane = array(); - foreach ($statusSet as $code) { - $oascii = ord($code[0]); - if( $oascii >= ord('a') && $oascii <= ord('z') ) { - $sane[] = $code[0]; - } - } - return $sane; - } - - /** - * - */ - static function getName(&$dbh,$id) { - $sch = tlDBObject::getDBTables(array('nodes_hierarchy','testplans')); - $sql = "SELECT name FROM {$sch['nodes_hierarchy']} NH + JOIN $nht NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['keywords'] . $my['join']['ua'] . $my['join']['cf'] . + " JOIN ({$sqlLatestExecOnTPLAN}) AS LEXBTPLAN " . + " ON LEXBTPLAN.testplan_id = TPTCV.testplan_id " . + " AND LEXBTPLAN.tcversion_id = TPTCV.tcversion_id " . + " AND LEXBTPLAN.testplan_id = " . $safe['tplan_id'] . + " JOIN {$this->tables['executions']} E " . + " ON E.id = LEXBTPLAN.id " . + " AND E.testplan_id = LEXBTPLAN.testplan_id " . $my['join']['bugs'] . + $my['where']['where']; + + return is_null($union['not_run']) ? $union['exec'] : $union; + } + + /** + * Rules + * 1. + * code is a string of length = 1 => one character + * 2. domain will be a-z + */ + private function sanitizeExecStatus($status) + { + $statusSet = (array) $status; + $sane = array(); + foreach ($statusSet as $code) { + $oascii = ord($code[0]); + if ($oascii >= ord('a') && $oascii <= ord('z')) { + $sane[] = $code[0]; + } + } + return $sane; + } + + /** + */ + public static function getName(&$dbh, $id) + { + $sch = tlDBObject::getDBTables(array( + 'nodes_hierarchy', + 'testplans' + )); + $sql = "SELECT name FROM {$sch['nodes_hierarchy']} NH JOIN {$sch['testplans']} TPLAN ON TPLAN.id = NH.id - WHERE TPLAN.id=" . intval($id); - $rs = $dbh->get_recordset($sql); - return is_null($rs) ? $rs : $rs[0]['name']; - } - -} // end class testplan - - -// ###################################################################################### -/** - * Build Manager Class - * @package TestLink - **/ -class build_mgr extends tlObject { - /** @var database handler */ - var $db; - var $cfield_mgr; - - /** - * Build Manager class constructor - * - * @param resource &$db reference to database handler - **/ - function build_mgr(&$db) { - parent::__construct(); - $this->db = &$db; - $this->cfield_mgr = new cfield_mgr($this->db); - } - - - /** - * Build Manager - */ - function setZeroOneAttr($id,$attr,$zeroOne) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ " . - "UPDATE {$this->tables['builds']} SET {$attr}=" . ($zeroOne ? 1 : 0) . " WHERE id=" . intval($id); - $this->db->exec_query($sql); - } - - - /** - * Build Manager - */ - function setActive($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $this->setZeroOneAttr($id,'active',1); - } - - /** - * Build Manager - */ - function setInactive($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $this->setZeroOneAttr($id,'active',0); - } - - /** - * Build Manager - */ - function setOpen($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $this->setZeroOneAttr($id,'is_open',1); - $this->setClosedOnDate($id,null); - } - - /** - * Build Manager - */ - function setClosed($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $this->setZeroOneAttr($id,'is_open',0); - $timestamp = explode(' ',trim($this->db->db_now(),"'")); - $this->setClosedOnDate($id,$timestamp[0]); - } - - - - /** - * Build Manager - * - * createFromObject - */ - function createFromObject($item,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - try { - // mandatory checks - if(strlen($item->name)==0) { - throw new Exception('Build - Empty name is not allowed'); - } - - // what checks need to be done ? - // 1. does test plan exist? - $item->tplan_id = intval($item->tplan_id); - $tm = new tree($this->db); - $ntv = array_flip($tm->get_available_node_types()); - $pinfo = $tm->get_node_hierarchy_info($item->tplan_id); - if(is_null($pinfo) || - $ntv[$pinfo['node_type_id']] != 'testplan') { - throw new Exception( - "Build - Test Plan ID {$item->tplan_id} does not exist"); - } - - // 2. there is NO other build on test plan with same name - $name = trim($item->name); - $op = $this->checkNameExistence($item->tplan_id,$name); - if(!$op['status_ok']) { - throw new Exception( - "Build name {$name} is already in use on Test Plan {$item->tplan_id}"); - } - } catch (Exception $e) { - throw $e; // rethrow - } - - // seems OK => check all optional attributes - $build = new stdClass(); - $prop = array('release_date' => '','notes' => '', - 'commit_id' => '', 'tag' => '', - 'branch' => '', 'release_candidate' => '', - 'is_active' => 1,'is_open' => 1, - 'creation_ts' => $this->db->db_now()); - - $build->name = $item->name; - $build->tplan_id = $item->tplan_id; - foreach( $prop as $nu => $value ) { - $build->$nu = $value; - if( property_exists($item, $nu) ) { - switch( $nu ) { - case 'creation_ts': - if(null != $item->$nu && '' == trim($item->$nu) ) { - $build->$nu = $item->$nu; - } - break; - - case 'is_active': - case 'is_open': - $build->$nu = intval($item->$nu) > 0 ? 1 : 0; - break; - - default: - $build->$nu = $item->$nu; - break; - } - } - } - $build->release_date = trim($build->release_date); - $ps = 'prepare_string'; - $sql = " INSERT INTO {$this->tables['builds']} " . - " (testplan_id,name,notes, + WHERE TPLAN.id=" . intval($id); + $rs = $dbh->get_recordset($sql); + return is_null($rs) ? $rs : $rs[0]['name']; + } + + /** + */ + public function getCustomFieldsValues($id, $tproject_id, $scope = 'design', + $filters = null) + { + $cf_map = $this->get_linked_cfields_at_design($id, $tproject_id, + $filters); + $cf = []; + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + $value = ''; + if (isset($cf_info['node_id']) || $cf_info['node_id']) { + $value = $this->cfield_mgr->string_custom_field_value( + $cf_info, $id); + } + $cf[] = [ + "label" => $cf_info['label'], + "name" => $cf_info['name'], + "type" => trim( + $this->cfield_mgr->custom_field_types[$cf_info['type']]), + "value" => $value + ]; + } + } + return $cf; + } +} + +// ###################################################################################### +/** + * Build Manager Class + * + * @package TestLink + */ +class build_mgr extends tlObject +{ + + /** @var database handler */ + protected $db; + + public $cfield_mgr; + + /** + * Build Manager class constructor + * + * @param + * resource &$db reference to database handler + */ + public function __construct(&$db) + { + parent::__construct(); + $this->db = &$db; + $this->cfield_mgr = new cfield_mgr($this->db); + } + + /** + * builds + */ + public function getCustomFieldsValues($build_id, $tproject_id, + $scope = 'design', $filters = null) + { + $cf_map = $this->get_linked_cfields_at_design($build_id, $tproject_id, + $filters); + $cf = []; + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + $value = ''; + if (isset($cf_info['node_id']) || $cf_info['node_id']) { + $value = $this->cfield_mgr->string_custom_field_value( + $cf_info, $build_id); + } + $cf[] = [ + "label" => $cf_info['label'], + "name" => $cf_info['name'], + "type" => trim( + $this->cfield_mgr->custom_field_types[$cf_info['type']]), + "value" => $value + ]; + } + } + return $cf; + } + + /** + * Build Manager + */ + private function setZeroOneAttr($id, $attr, $zeroOne) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = "/* $debugMsg */ " . + "UPDATE {$this->tables['builds']} SET {$attr}=" . ($zeroOne ? 1 : 0) . + " WHERE id=" . intval($id); + $this->db->exec_query($sql); + } + + /** + * Build Manager + */ + public function setActive($id) + { + $this->setZeroOneAttr($id, 'active', 1); + } + + /** + * Build Manager + */ + public function setInactive($id) + { + $this->setZeroOneAttr($id, 'active', 0); + } + + /** + * Build Manager + */ + public function setOpen($id) + { + $this->setZeroOneAttr($id, 'is_open', 1); + $this->setClosedOnDate($id, null); + } + + /** + * Build Manager + */ + public function setClosed($id) + { + $this->setZeroOneAttr($id, 'is_open', 0); + $timestamp = explode(' ', trim($this->db->db_now(), "'")); + $this->setClosedOnDate($id, $timestamp[0]); + } + + /** + * Build Manager + * + * createFromObject + */ + public function createFromObject($item, $opt = null) + { + try { + // mandatory checks + if (strlen($item->name) == 0) { + throw new Exception('Build - Empty name is not allowed'); + } + + // what checks need to be done ? + // 1. does test plan exist? + $item->tplan_id = intval($item->tplan_id); + $tm = new tree($this->db); + $ntv = array_flip($tm->get_available_node_types()); + $pinfo = $tm->get_node_hierarchy_info($item->tplan_id); + if (is_null($pinfo) || $ntv[$pinfo['node_type_id']] != 'testplan') { + throw new Exception( + "Build - Test Plan ID {$item->tplan_id} does not exist"); + } + + // 2. there is NO other build on test plan with same name + $name = trim($item->name); + $op = $this->checkNameExistence($item->tplan_id, $name); + if (! $op['status_ok']) { + throw new Exception( + "Build name {$name} is already in use on Test Plan {$item->tplan_id}"); + } + } catch (Exception $e) { + throw $e; // rethrow + } + + // seems OK => check all optional attributes + $build = new stdClass(); + $prop = array( + 'release_date' => '', + 'notes' => '', + 'commit_id' => '', + 'tag' => '', + 'branch' => '', + 'release_candidate' => '', + 'is_active' => 1, + 'is_open' => 1, + 'creation_ts' => $this->db->db_now() + ); + + $build->name = $item->name; + $build->tplan_id = $item->tplan_id; + foreach ($prop as $nu => $value) { + $build->$nu = $value; + if (property_exists($item, $nu)) { + switch ($nu) { + case 'creation_ts': + if (null != $item->$nu && '' == trim($item->$nu)) { + $build->$nu = $item->$nu; + } + break; + + case 'is_active': + case 'is_open': + $build->$nu = intval($item->$nu) > 0 ? 1 : 0; + break; + + default: + $build->$nu = $item->$nu; + break; + } + } + } + $build->release_date = trim($build->release_date); + $ps = 'prepare_string'; + $sql = " INSERT INTO {$this->tables['builds']} " . + " (testplan_id,name,notes, commit_id,tag,branch,release_candidate, - active,is_open,creation_ts,release_date) " . - " VALUES ('". $build->tplan_id . "','" . - $this->db->$ps($build->name) . "','" . - $this->db->$ps($build->notes) . "',"; - - $sql .= "'" . $this->db->$ps($build->commit_id) . "'," . - "'" . $this->db->$ps($build->tag) . "'," . - "'" . $this->db->$ps($build->branch) . "'," . - "'" . $this->db->$ps($build->release_candidate) . "',"; - - $sql .= "{$build->is_active},{$build->is_open},{$build->creation_ts}"; - - if($build->release_date == '') { - $sql .= ",NULL)"; - } else { - $sql .= ",'" . $this->db->$ps($build->release_date) . "')"; - } - - $id = 0; - $result = $this->db->exec_query($sql); - if ($result) { - $id = $this->db->insert_id($this->tables['builds']); - } - - return $id; - } - - - /* - Build Manager - - function: create - - args : - $tplan_id - $name - $notes - [$active]: default: 1 - [$open]: default: 1 - [release_date]: YYYY-MM-DD - - - returns: - - rev : - */ - function create($tplan_id,$name,$notes = '',$active=1,$open=1,$release_date='') - { - $targetDate = trim($release_date); - $sql = " INSERT INTO {$this->tables['builds']} " . - " (testplan_id,name,notes,release_date,active,is_open,creation_ts) " . - " VALUES ('". $tplan_id . "','" . $this->db->prepare_string($name) . "','" . - $this->db->prepare_string($notes) . "',"; - - if($targetDate == '') { - $sql .= "NULL,"; - } - else { - $sql .= "'" . $this->db->prepare_string($targetDate) . "',"; - } - - $sql .= "{$active},{$open},{$this->db->db_now()})"; - - $id = 0; - $result = $this->db->exec_query($sql); - if ($result) { - $id = $this->db->insert_id($this->tables['builds']); - } - - return $id; - } - - - /* - function: update - - args : - $id - $name - $notes - [$active]: default: null - [$open]: default: null - [$release_date]='' FORMAT YYYY-MM-DD - [$closed_on_date]='' FORMAT YYYY-MM-DD - - returns: - - rev : - */ - function update($id,$name,$notes,$attr=null) { - - $members = array('is_active' => null, 'is_open' => null, - 'release_date' => '', 'closed_on_date=' => '', - 'commit_id' => '', 'tag' => '', - 'branch' => '', 'release_candidate' => ''); - - $members = array_merge($members,(array)$attr); - - $closure_date = ''; - $targetDate = trim($members['release_date']); - $sql = " UPDATE {$this->tables['builds']} " . - " SET name='" . $this->db->prepare_string($name) . "'," . - " notes='" . $this->db->prepare_string($notes) . "'"; - - if($targetDate == '') { - $sql .= ",release_date=NULL"; - } else { - $sql .= ",release_date='" . $this->db->prepare_string($targetDate) . "'"; - } - - if( !is_null($members['is_active']) ) { - $sql .=" , active=" . intval($members['is_active']); - } - - if( !is_null($members['is_open']) ) { - $open_status=intval($members['is_open']) ? 1 : 0; - $sql .=" , is_open=" . $open_status; - - if($open_status == 1) { - $closure_date = ''; - } - } - - // New attributes - $ps = 'prepare_string'; - $ax = array('commit_id','tag','branch','release_candidate'); - foreach( $ax as $fi ) { - $sql .= ", $fi='" . $this->db->$ps($members[$fi]) . "'"; - } - - if($closure_date == '') { - $sql .= ",closed_on_date=NULL"; - } else { - // may be will be useful validate date format - $sql .= ",closed_on_date='" . $this->db->prepare_string($closure_date) . "'"; - } - - $sql .= " WHERE id={$id}"; - $result = $this->db->exec_query($sql); - return $result ? 1 : 0; - } - - /** - * Delete a build - * - * @param integer $id - * @return integer status code - * - */ - function delete($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe_id = intval($id); - $where = " WHERE build_id={$safe_id}"; - $execIDSetSQL = " SELECT id FROM {$this->tables['executions']} {$where} "; - - - // Attachments NEED special processing. - - // get test step exec attachments if any exists - $dummy = " SELECT id FROM {$this->tables['execution_tcsteps']} " . - " WHERE execution_id IN ({$execIDSetSQL}) "; - - $rs = $this->db->fetchRowsIntoMap($dummy,'id'); - if(!is_null($rs)) { - foreach($rs as $fik => $v) { - deleteAttachment($this->db,$fik,false); - } - } - - // execution attachments - $dummy = " SELECT id FROM {$this->tables['attachments']} " . - " WHERE fk_table = 'executions' " . - " AND fk_id IN ({$execIDSetSQL}) "; - - $rs = $this->db->fetchRowsIntoMap($dummy,'id'); - if(!is_null($rs)) { - foreach($rs as $fik => $v) { - deleteAttachment($this->db,$fik,false); - } - } - - - // Execution Bugs - $sql = " DELETE FROM {$this->tables['execution_bugs']} " . - " WHERE execution_id IN ({$execIDSetSQL}) "; - $result = $this->db->exec_query($sql); - - // Execution tcsteps results - $sql = "DELETE FROM {$this->tables['execution_tcsteps']} " . - " WHERE execution_id IN ({$execIDSetSQL}) "; - $result = $this->db->exec_query($sql); - - $sql = "DELETE FROM {$this->tables['cfield_execution_values']} " . - " WHERE execution_id IN ({$execIDSetSQL}) "; - $result = $this->db->exec_query($sql); - - - // Finally Executions table - $sql = " DELETE FROM {$this->tables['executions']} {$where}"; - $result = $this->db->exec_query($sql); - - - // Build ID is the Access Key - // User Task Assignment - $sql = " DELETE FROM {$this->tables['user_assignments']} {$where}"; - $result=$this->db->exec_query($sql); - - // Custom fields - $this->cfield_mgr->remove_all_design_values_from_node($safe_id,'build'); - - $sql = " DELETE FROM {$this->tables['builds']} WHERE id={$safe_id}"; - $result=$this->db->exec_query($sql); - return $result ? 1 : 0; - } - - - /* - function: get_by_id - get information about a build - - args : id: build id - - returns: map with following keys - id: build id - name: build name - notes: build notes - active: build active status - is_open: build open status - testplan_id - */ - function get_by_id($id,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('options' => - array('tplan_id' => null, 'output' => 'full', 'fields' => '*')); - $my['options'] = array_merge($my['options'],(array)$opt); - - $safe_id = intval($id); - - $sql = "/* {$debugMsg} */"; - switch($my['options']['output']) { - case 'minimun': - $sql .= " SELECT id,is_open,active,active AS is_active "; - break; - - case 'fields': - $sql .= " SELECT {$my['options']['fields']} "; - break; - - case 'full': - default: - $sql .= " SELECT *, active AS is_active "; - break; - } - - $sql .= " FROM {$this->tables['builds']} WHERE id = {$safe_id} "; - if(!is_null($my['options']['tplan_id']) && ($safe_tplan = intval($my['options']['tplan_id'])) > 0) { - $sql .= " AND testplan_id = {$safe_tplan} "; - } - - $result = $this->db->exec_query($sql); - $myrow = $this->db->fetch_array($result); - return $myrow; - } - - - - /* - function: get_by_name - get information about a build by name - */ - - function get_by_name($name,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('options' => array('tplan_id' => null, 'output' => 'full')); - $my['options'] = array_merge($my['options'],(array)$opt); - - $sql = "/* {$debugMsg} */"; - switch($my['options']['output']) - { - case 'minimun': - $sql .= " SELECT B.id, B.name, B.is_open, B.active "; - break; - - case 'full': - default: - $sql .= " SELECT B.* "; - break; - } - - $sql .= " FROM {$this->tables['builds']} B " . - " WHERE B.name = '" . $this->db->prepare_string($name) . "'"; - - if(!is_null($my['options']['tplan_id']) && ($safe_tplan = intval($my['options']['tplan_id'])) > 0) - { - $sql .= " AND B.testplan_id = {$safe_tplan} "; - } - - $rs = $this->db->get_recordset($sql); - return($rs); - } - - - - /** - * Set date of closing build - * - * @param integer $id Build identifier - * @param string $targetDate, format YYYY-MM-DD. can be null - * - * @return TBD TBD - */ - function setClosedOnDate($id,$targetDate) - { - $sql = " UPDATE {$this->tables['builds']} "; - - if( is_null($targetDate) ) - { - $sql .= " SET closed_on_date=NULL "; - } - else - { - $sql .= " SET closed_on_date='" . $this->db->prepare_string($targetDate) . "'"; - } - $sql .= " WHERE id={$id} "; - - $result = $this->db->exec_query($sql); - } - - - /** - * - * NEWNEW - */ - function get_linked_cfields_at_design($id,$tproject_id,$filters=null,$access_key='id') - { - $safeID = $id == 0 ? null : intval($id); - $cf_map = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id,cfield_mgr::CF_ENABLED, - $filters,'build',$id,$access_key); - return $cf_map; - } - - /* - function: html_table_of_custom_field_inputs - - - args: $id - returns: html string - - */ - function html_table_of_custom_field_inputs($id,$tproject_id,$scope='design',$name_suffix='',$input_values=null) - { - $cf_smarty=''; - $method_suffix = $scope=='design' ? $scope : 'execution'; - $method_name = "get_linked_cfields_at_{$method_suffix}"; - $cf_map=$this->$method_name($id,$tproject_id); - if(!is_null($cf_map)) - { - $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map,$name_suffix,$input_values); - } - return($cf_smarty); - } - - - /* - function: html_table_of_custom_field_inputs - - - args: $id - [$parent_id]: need when you call this method during the creation - of a test suite, because the $id will be 0 or null. - - [$scope]: 'design','execution' - - returns: html string - - */ - function html_custom_field_inputs($id,$tproject_id,$scope='design',$name_suffix='',$input_values=null) - { - $itemSet=''; - $method_suffix = $scope=='design' ? $scope : 'execution'; - $method_name = "get_linked_cfields_at_{$method_suffix}"; - $cf_map=$this->$method_name($id,$tproject_id); - if(!is_null($cf_map)) - { - $itemSet = $this->cfield_mgr->html_inputs($cf_map,$name_suffix,$input_values); - } - return $itemSet; - } - - /* - function: html_table_of_custom_field_values - - args: $id - [$scope]: 'design','execution' - - [$filters]:default: null - - map with keys: - - [show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - include ONLY custom fields that can be viewed - while user is execution testcases. - - 0 or null -> don't filter - - returns: html string - - rev : - */ - function html_table_of_custom_field_values($id,$tproject_id,$scope='design',$filters=null,$formatOptions=null) - { - $cf_smarty=''; - $parent_id=null; - $label_css_style=' class="labelHolder" ' ; - $value_css_style = ' '; - - $add_table=true; - $table_style=''; - if( !is_null($formatOptions) ) - { - $label_css_style = isset($formatOptions['label_css_style']) ? $formatOptions['label_css_style'] : $label_css_style; - $value_css_style = isset($formatOptions['value_css_style']) ? $formatOptions['value_css_style'] : $value_css_style; - - $add_table=isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; - $table_style=isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; - } - - $show_cf = config_get('custom_fields')->show_custom_fields_without_value; - $cf_map=$this->get_linked_cfields_at_design($id,$tproject_id,$filters); - - if( !is_null($cf_map) ) - { - foreach($cf_map as $cf_id => $cf_info) - { - if(isset($cf_info['node_id']) || $cf_info['node_id'] || $show_cf) - { - $label=str_replace(TL_LOCALIZE_TAG,'',lang_get($cf_info['label'],null,true)); - $cf_smarty .= "" . htmlspecialchars($label) . "" . - "" . - $this->cfield_mgr->string_custom_field_value($cf_info,$id) . "\n"; - } - } - } - - if($cf_smarty != '' && $add_table) - { - $cf_smarty = "" . $cf_smarty . "
    "; - } - - return $cf_smarty; - } - - - - /** - * Build Manager - * - */ - function checkNameExistence($tplan_id,$build_name,$build_id=null, - $caseSens=0) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = " /* $debugMsg */ SELECT id, name, notes " . - " FROM {$this->tables['builds']} " . - " WHERE testplan_id = {$tplan_id} "; - - if($caseSens) { - $sql .= " AND name="; - } else { - $build_name = strtoupper($build_name); - $sql .= " AND UPPER(name)="; - } - $sql .= "'" . $this->db->prepare_string($build_name) . "'"; - - if( !is_null($build_id) ) { - $sql .= " AND id <> " . $this->db->prepare_int($build_id); - } - - $result = $this->db->exec_query($sql); - $rn = $this->db->num_rows($result); - $status = array(); - $status['status_ok'] = $rn == 0 ? 1 : 0; - return $status; - } - - - - -} // end class build_mgr - - -// ################################################################################## -/** - * Milestone Manager Class - * @package TestLink - **/ -class milestone_mgr extends tlObject -{ - /** @var database handler */ - var $db; - - /** - * class constructor - * - * @param resource &$db reference to database handler - **/ - function milestone_mgr(&$db) - { - parent::__construct(); - $this->db = &$db; - } - - /* - function: create() - - args : keys - $tplan_id - $name - $target_date: string with format: - $start_date: - $low_priority: percentage - $medium_priority: percentage - $high_priority: percentage - - returns: - - */ - function create($mi) - { - $item_id=0; - $dateFields=null; - $dateValues=null; - $dateKeys=array('target_date','start_date'); - - // check dates - foreach($dateKeys as $varname) - { - $value= trim($mi->$varname); - if($value != '') - { - if (($time = strtotime($value)) == -1 || $time === false) - { - die (__FUNCTION__ . ' Abort - Invalid date'); - } - $dateFields[]=$varname; - $dateValues[]=" '{$this->db->prepare_string($value)}' "; - } - } - $additionalFields=''; - if( !is_null($dateFields) ) - { - $additionalFields= ',' . implode(',',$dateFields) ; - $additionalValues= ',' . implode(',',$dateValues) ; - } - /* for future - $sql = "INSERT INTO {$this->tables['milestones']} " . - " (testplan_id,name,platform_id,build_id,a,b,c{$additionalFields}) " . - " VALUES (" . intval($mi->tplan_id) . "," . - "'{$this->db->prepare_string($mi->name)}'," . - intval($mi->platform_id) . "," . intval($mi->build_id) . "," . - $mi->low_priority . "," . $mi->medium_priority . "," . $mi->high_priority . - $additionalValues . ")"; - */ - $sql = "INSERT INTO {$this->tables['milestones']} " . - " (testplan_id,name,a,b,c{$additionalFields}) " . - " VALUES (" . intval($mi->tplan_id) . "," . - "'{$this->db->prepare_string($mi->name)}'," . - $mi->low_priority . "," . $mi->medium_priority . "," . $mi->high_priority . - $additionalValues . ")"; - - $result = $this->db->exec_query($sql); - - if ($result) - { - $item_id = $this->db->insert_id($this->tables['milestones']); - } - - return $item_id; - } - - /* - function: update - - args : - $id - $name - $notes - [$active]: default: 1 - [$open]: default: 1 - - - - returns: - - rev : - */ - function update($id,$name,$target_date,$start_date,$low_priority,$medium_priority,$high_priority) - { - $sql = "UPDATE {$this->tables['milestones']} " . - " SET name='{$this->db->prepare_string($name)}', " . - " target_date='{$this->db->prepare_string($target_date)}', " . - " start_date='{$this->db->prepare_string($start_date)}', " . - " a={$low_priority}, b={$medium_priority}, c={$high_priority} WHERE id={$id}"; - $result = $this->db->exec_query($sql); - return $result ? 1 : 0; - } - - - - /* - function: delete - - args : - $id - - - returns: - - */ - function delete($id) - { - $sql = "DELETE FROM {$this->tables['milestones']} WHERE id={$id}"; - $result=$this->db->exec_query($sql); - return $result ? 1 : 0; - } - - - /* - function: get_by_id - - args : - $id - returns: - - */ - function get_by_id($id) - { - $sql=" SELECT M.id, M.name, M.a AS high_percentage, " . - " M.b AS medium_percentage, M.c AS low_percentage, " . - " M.target_date, M.start_date, " . - " M.testplan_id, NH_TPLAN.name AS testplan_name " . - // " M.build_id, B.name AS build_name, " . - // " M.platform_id, P.name AS platform_name " . - " FROM {$this->tables['milestones']} M " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TPLAN " . - " ON NH_TPLAN.id=M.testplan_id " . - // " LEFT OUTER JOIN {$this->tables['builds']} B " . - // " ON B.id=M.build_id " . - // " LEFT OUTER JOIN {$this->tables['platforms']} P " . - // " ON P.id=M.platform_id " . - " WHERE M.id = " . $this->db->prepare_int($id); - - $row = $this->db->fetchRowsIntoMap($sql,'id'); - return $row; - } - - /** - * check existence of milestone name in Test Plan - * - * @param integer $tplan_id test plan id. - * @param string $milestone_name milestone name - * @param integer $milestone_id default: null - * when is not null we add milestone_id as filter, this is useful - * to understand if is really a duplicate when using this method - * while managing update operations via GUI - * - * @return integer 1 => name exists - */ - function check_name_existence($tplan_id,$milestone_name,$milestone_id=null,$case_sensitive=0) - { - $sql = " SELECT id, name FROM {$this->tables['milestones']} " . - " WHERE testplan_id = " . $this->db->prepare_int($tplan_id); - - if($case_sensitive) - { - $sql .= " AND name="; - } - else - { - $milestone_name=strtoupper($milestone_name); - $sql .= " AND UPPER(name)="; - } - $sql .= "'{$this->db->prepare_string($milestone_name)}'"; - - if( !is_null($milestone_id) ) - { - $sql .= " AND id <> " . $this->db->prepare_int($milestone_id); - } - - $result = $this->db->exec_query($sql); - $status= $this->db->num_rows($result) ? 1 : 0; - - return $status; - } - - - /* - function: get_all_by_testplan - get info about all milestones defined for a testlan - args : - tplan_id - - - returns: - - rev : - */ - function get_all_by_testplan($tplan_id) - { - $sql=" SELECT M.id, M.name, M.a AS high_percentage, M.b AS medium_percentage, M.c AS low_percentage, " . - " M.target_date, M.start_date, M.testplan_id, NH.name as testplan_name " . - " FROM {$this->tables['milestones']} M, {$this->tables['nodes_hierarchy']} NH " . - " WHERE testplan_id={$tplan_id} AND NH.id = testplan_id " . - " ORDER BY M.target_date,M.name"; - $rs=$this->db->get_recordset($sql); - return $rs; - } - - -} // end class milestone_mgr \ No newline at end of file + active,is_open,creation_ts,release_date) " . " VALUES ('" . + $build->tplan_id . "','" . $this->db->$ps($build->name) . "','" . + $this->db->$ps($build->notes) . "',"; + + $sql .= "'" . $this->db->$ps($build->commit_id) . "'," . "'" . + $this->db->$ps($build->tag) . "'," . "'" . + $this->db->$ps($build->branch) . "'," . "'" . + $this->db->$ps($build->release_candidate) . "',"; + + $sql .= "{$build->is_active},{$build->is_open},{$build->creation_ts}"; + + if ($build->release_date == '') { + $sql .= ",NULL)"; + } else { + $sql .= ",'" . $this->db->$ps($build->release_date) . "')"; + } + + $id = 0; + $result = $this->db->exec_query($sql); + if ($result) { + $id = $this->db->insert_id($this->tables['builds']); + } + + return $id; + } + + /* + * Build Manager + * + * function: create + * + * args : + * $tplan_id + * $name + * $notes + * [$active]: default: 1 + * [$open]: default: 1 + * [release_date]: YYYY-MM-DD + * + * + * returns: + * + * rev : + */ + public function create($tplan_id, $name, $notes = '', $active = 1, $open = 1, + $release_date = '') + { + $targetDate = trim($release_date); + $sql = " INSERT INTO {$this->tables['builds']} " . + " (testplan_id,name,notes,release_date,active,is_open,creation_ts) " . + " VALUES ('" . $tplan_id . "','" . $this->db->prepare_string($name) . + "','" . $this->db->prepare_string($notes) . "',"; + + if ($targetDate == '') { + $sql .= "NULL,"; + } else { + $sql .= "'" . $this->db->prepare_string($targetDate) . "',"; + } + + $sql .= "{$active},{$open},{$this->db->db_now()})"; + + $id = 0; + $result = $this->db->exec_query($sql); + if ($result) { + $id = $this->db->insert_id($this->tables['builds']); + } + + return $id; + } + + /* + * function: update + * + * args : + * $id + * $name + * $notes + * [$active]: default: null + * [$open]: default: null + * [$release_date]='' FORMAT YYYY-MM-DD + * [$closed_on_date]='' FORMAT YYYY-MM-DD + * + * returns: + * + * rev : + */ + public function update($id, $name, $notes, $attr = null) + { + $members = array( + 'is_active' => null, + 'is_open' => null, + 'release_date' => '', + 'closed_on_date=' => '', + 'commit_id' => '', + 'tag' => '', + 'branch' => '', + 'release_candidate' => '' + ); + + $members = array_merge($members, (array) $attr); + + $closure_date = ''; + $targetDate = trim($members['release_date']); + $sql = " UPDATE {$this->tables['builds']} " . " SET name='" . + $this->db->prepare_string($name) . "'," . " notes='" . + $this->db->prepare_string($notes) . "'"; + + if ($targetDate == '') { + $sql .= ",release_date=NULL"; + } else { + $sql .= ",release_date='" . $this->db->prepare_string($targetDate) . + "'"; + } + + if (! is_null($members['is_active'])) { + $sql .= " , active=" . intval($members['is_active']); + } + + if (! is_null($members['is_open'])) { + $open_status = intval($members['is_open']) ? 1 : 0; + $sql .= " , is_open=" . $open_status; + + if ($open_status == 1) { + $closure_date = ''; + } + } + + // New attributes + $ps = 'prepare_string'; + $ax = array( + 'commit_id', + 'tag', + 'branch', + 'release_candidate' + ); + foreach ($ax as $fi) { + $sql .= ", $fi='" . $this->db->$ps($members[$fi]) . "'"; + } + + if ($closure_date == '') { + $sql .= ",closed_on_date=NULL"; + } else { + // may be will be useful validate date format + $sql .= ",closed_on_date='" . + $this->db->prepare_string($closure_date) . "'"; + } + + $sql .= " WHERE id={$id}"; + $result = $this->db->exec_query($sql); + return $result ? 1 : 0; + } + + /** + * Delete a build + * + * @param integer $id + * @return integer status code + * + */ + public function delete($id) + { + $safe_id = intval($id); + $where = " WHERE build_id={$safe_id}"; + $execIDSetSQL = " SELECT id FROM {$this->tables['executions']} {$where} "; + + // Attachments NEED special processing. + + // get test step exec attachments if any exists + $dummy = " SELECT id FROM {$this->tables['execution_tcsteps']} " . + " WHERE execution_id IN ({$execIDSetSQL}) "; + + $rs = $this->db->fetchRowsIntoMap($dummy, 'id'); + if (! is_null($rs)) { + foreach ($rs as $fik => $v) { + deleteAttachment($this->db, $fik, false); + } + } + + // execution attachments + $dummy = " SELECT id FROM {$this->tables['attachments']} " . + " WHERE fk_table = 'executions' " . + " AND fk_id IN ({$execIDSetSQL}) "; + + $rs = $this->db->fetchRowsIntoMap($dummy, 'id'); + if (! is_null($rs)) { + foreach ($rs as $fik => $v) { + deleteAttachment($this->db, $fik, false); + } + } + + // Execution Bugs + $sql = " DELETE FROM {$this->tables['execution_bugs']} " . + " WHERE execution_id IN ({$execIDSetSQL}) "; + $this->db->exec_query($sql); + + // Execution tcsteps results + $sql = "DELETE FROM {$this->tables['execution_tcsteps']} " . + " WHERE execution_id IN ({$execIDSetSQL}) "; + $this->db->exec_query($sql); + + $sql = "DELETE FROM {$this->tables['cfield_execution_values']} " . + " WHERE execution_id IN ({$execIDSetSQL}) "; + $this->db->exec_query($sql); + + // Finally Executions table + $sql = " DELETE FROM {$this->tables['executions']} {$where}"; + $this->db->exec_query($sql); + + // Build ID is the Access Key + // User Task Assignment + $sql = " DELETE FROM {$this->tables['user_assignments']} {$where}"; + $this->db->exec_query($sql); + + // Custom fields + $this->cfield_mgr->remove_all_design_values_from_node($safe_id, 'build'); + + $sql = " DELETE FROM {$this->tables['builds']} WHERE id={$safe_id}"; + $result = $this->db->exec_query($sql); + return $result ? 1 : 0; + } + + /* + * function: get_by_id + * get information about a build + * + * args : id: build id + * + * returns: map with following keys + * id: build id + * name: build name + * notes: build notes + * active: build active status + * is_open: build open status + * testplan_id + */ + public function get_by_id($id, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array( + 'options' => array( + 'tplan_id' => null, + 'output' => 'full', + 'fields' => '*' + ) + ); + $my['options'] = array_merge($my['options'], (array) $opt); + + $safe_id = intval($id); + + $sql = "/* {$debugMsg} */"; + switch ($my['options']['output']) { + case 'minimun': + $sql .= " SELECT id,is_open,active,active AS is_active "; + break; + + case 'fields': + $sql .= " SELECT {$my['options']['fields']} "; + break; + + case 'full': + default: + $sql .= " SELECT *, active AS is_active "; + break; + } + + $sql .= " FROM {$this->tables['builds']} WHERE id = {$safe_id} "; + if (! is_null($my['options']['tplan_id']) && + ($safe_tplan = intval($my['options']['tplan_id'])) > 0) { + $sql .= " AND testplan_id = {$safe_tplan} "; + } + + $result = $this->db->exec_query($sql); + return $this->db->fetch_array($result); + } + + /* + * function: get_by_name + * get information about a build by name + */ + public function get_by_name($name, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array( + 'options' => array( + 'tplan_id' => null, + 'output' => 'full' + ) + ); + $my['options'] = array_merge($my['options'], (array) $opt); + + $sql = "/* {$debugMsg} */"; + switch ($my['options']['output']) { + case 'minimun': + $sql .= " SELECT B.id, B.name, B.is_open, B.active "; + break; + + case 'full': + default: + $sql .= " SELECT B.* "; + break; + } + + $sql .= " FROM {$this->tables['builds']} B " . " WHERE B.name = '" . + $this->db->prepare_string($name) . "'"; + + if (! is_null($my['options']['tplan_id']) && + ($safe_tplan = intval($my['options']['tplan_id'])) > 0) { + $sql .= " AND B.testplan_id = {$safe_tplan} "; + } + + return $this->db->get_recordset($sql); + } + + /** + * Set date of closing build + * + * @param integer $id + * Build identifier + * @param string $targetDate, + * format YYYY-MM-DD. can be null + * + * @return array TBD + */ + public function setClosedOnDate($id, $targetDate) + { + $sql = " UPDATE {$this->tables['builds']} "; + + if (is_null($targetDate)) { + $sql .= " SET closed_on_date=NULL "; + } else { + $sql .= " SET closed_on_date='" . + $this->db->prepare_string($targetDate) . "'"; + } + $sql .= " WHERE id={$id} "; + + $this->db->exec_query($sql); + } + + /** + * NEWNEW + */ + public function get_linked_cfields_at_design($build_id, $tproject_id, + $filters = null, $access_key = 'id') + { + $safeID = $build_id == 0 ? null : intval($build_id); + return $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, + cfield_mgr::CF_ENABLED, $filters, 'build', $safeID, $access_key); + } + + /* + * function: html_table_of_custom_field_inputs + * + * + * args: $id + * returns: html string + * + */ + public function html_table_of_custom_field_inputs($id, $tproject_id, + $scope = 'design', $name_suffix = '', $input_values = null) + { + $cf_smarty = ''; + $method_suffix = $scope == 'design' ? $scope : 'execution'; + $method_name = "get_linked_cfields_at_{$method_suffix}"; + $cf_map = $this->$method_name($id, $tproject_id); + if (! is_null($cf_map)) { + $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map, + $name_suffix, $input_values); + } + return $cf_smarty; + } + + /* + * function: html_table_of_custom_field_inputs + * + * + * args: $id + * [$parent_id]: need when you call this method during the creation + * of a test suite, because the $id will be 0 or null. + * + * [$scope]: 'design','execution' + * + * returns: html string + * + */ + public function html_custom_field_inputs($id, $tproject_id, + $scope = 'design', $name_suffix = '', $input_values = null) + { + $itemSet = ''; + $method_suffix = $scope == 'design' ? $scope : 'execution'; + $method_name = "get_linked_cfields_at_{$method_suffix}"; + $cf_map = $this->$method_name($id, $tproject_id); + if (! is_null($cf_map)) { + $itemSet = $this->cfield_mgr->html_inputs($cf_map, $name_suffix, + $input_values); + } + return $itemSet; + } + + /* + * function: html_table_of_custom_field_values + * + * args: $id + * [$scope]: 'design','execution' + * + * [$filters]:default: null + * + * map with keys: + * + * [show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * include ONLY custom fields that can be viewed + * while user is execution testcases. + * + * 0 or null -> don't filter + * + * returns: html string + * + * rev : + */ + public function html_table_of_custom_field_values($id, $tproject_id, + $scope = 'design', $filters = null, $formatOptions = null) + { + $cf_smarty = ''; + $label_css_style = ' class="labelHolder" '; + $value_css_style = ' '; + + $add_table = true; + $table_style = ''; + if (! is_null($formatOptions)) { + $label_css_style = isset($formatOptions['label_css_style']) ? $formatOptions['label_css_style'] : $label_css_style; + $value_css_style = isset($formatOptions['value_css_style']) ? $formatOptions['value_css_style'] : $value_css_style; + + $add_table = isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; + $table_style = isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; + } + + $show_cf = config_get('custom_fields')->show_custom_fields_without_value; + $cf_map = $this->get_linked_cfields_at_design($id, $tproject_id, + $filters); + + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + if (isset($cf_info['node_id']) || $cf_info['node_id'] || $show_cf) { + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, true)); + $cf_smarty .= "" . + htmlspecialchars($label) . "" . + "" . + $this->cfield_mgr->string_custom_field_value($cf_info, + $id) . "\n"; + } + } + } + + if ($cf_smarty != '' && $add_table) { + $cf_smarty = "" . $cf_smarty . "
    "; + } + + return $cf_smarty; + } + + /** + * Build Manager + */ + private function checkNameExistence($tplan_id, $build_name, $build_id = null, + $caseSens = 0) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = " /* $debugMsg */ SELECT id, name, notes " . + " FROM {$this->tables['builds']} " . + " WHERE testplan_id = {$tplan_id} "; + + if ($caseSens) { + $sql .= " AND name="; + } else { + $build_name = strtoupper($build_name); + $sql .= " AND UPPER(name)="; + } + $sql .= "'" . $this->db->prepare_string($build_name) . "'"; + + if (! is_null($build_id)) { + $sql .= " AND id <> " . $this->db->prepare_int($build_id); + } + + $result = $this->db->exec_query($sql); + $rn = $this->db->num_rows($result); + $status = array(); + $status['status_ok'] = $rn == 0 ? 1 : 0; + return $status; + } +} + +/** + * Milestone Manager Class + * + * @package TestLink + */ +class milestone_mgr extends tlObject +{ + + /** @var database handler */ + protected $db; + + /** + * class constructor + * + * @param + * resource &$db reference to database handler + */ + public function __construct(&$db) + { + parent::__construct(); + $this->db = &$db; + } + + /* + * function: create() + * + * args : keys + * $tplan_id + * $name + * $target_date: string with format: + * $start_date: + * $low_priority: percentage + * $medium_priority: percentage + * $high_priority: percentage + * + * returns: + * + */ + public function create($mi) + { + $item_id = 0; + $dateFields = null; + $dateValues = null; + $dateKeys = array( + 'target_date', + 'start_date' + ); + + // check dates + foreach ($dateKeys as $varname) { + $value = trim($mi->$varname); + if ($value != '') { + if (($time = strtotime($value)) == - 1 || $time === false) { + die(__FUNCTION__ . ' Abort - Invalid date'); + } + $dateFields[] = $varname; + $dateValues[] = " '{$this->db->prepare_string($value)}' "; + } + } + $additionalFields = ''; + if (! is_null($dateFields)) { + $additionalFields = ',' . implode(',', $dateFields); + $additionalValues = ',' . implode(',', $dateValues); + } + /* + * for future + * $sql = "INSERT INTO {$this->tables['milestones']} " . + * " (testplan_id,name,platform_id,build_id,a,b,c{$additionalFields}) " . + * " VALUES (" . intval($mi->tplan_id) . "," . + * "'{$this->db->prepare_string($mi->name)}'," . + * intval($mi->platform_id) . "," . intval($mi->build_id) . "," . + * $mi->low_priority . "," . $mi->medium_priority . "," . $mi->high_priority . + * $additionalValues . ")"; + */ + $sql = "INSERT INTO {$this->tables['milestones']} " . + " (testplan_id,name,a,b,c{$additionalFields}) " . " VALUES (" . + intval($mi->tplan_id) . "," . + "'{$this->db->prepare_string($mi->name)}'," . $mi->low_priority . "," . + $mi->medium_priority . "," . $mi->high_priority . $additionalValues . + ")"; + + $result = $this->db->exec_query($sql); + + if ($result) { + $item_id = $this->db->insert_id($this->tables['milestones']); + } + + return $item_id; + } + + /* + * function: update + * + * args : + * $id + * $name + * $notes + * [$active]: default: 1 + * [$open]: default: 1 + * + * + * + * returns: + * + * rev : + */ + public function update($id, $name, $target_date, $start_date, $low_priority, + $medium_priority, $high_priority) + { + $sql = "UPDATE {$this->tables['milestones']} " . + " SET name='{$this->db->prepare_string($name)}', " . + " target_date='{$this->db->prepare_string($target_date)}', " . + " start_date='{$this->db->prepare_string($start_date)}', " . + " a={$low_priority}, b={$medium_priority}, c={$high_priority} WHERE id={$id}"; + $result = $this->db->exec_query($sql); + return $result ? 1 : 0; + } + + /* + * function: delete + * + * args : + * $id + * + * + * returns: + * + */ + public function delete($id) + { + $sql = "DELETE FROM {$this->tables['milestones']} WHERE id={$id}"; + $result = $this->db->exec_query($sql); + return $result ? 1 : 0; + } + + /* + * function: get_by_id + * + * args : + * $id + * returns: + * + */ + public function get_by_id($id) + { + $sql = " SELECT M.id, M.name, M.a AS high_percentage, " . + " M.b AS medium_percentage, M.c AS low_percentage, " . + " M.target_date, M.start_date, " . + " M.testplan_id, NH_TPLAN.name AS testplan_name " . + " FROM {$this->tables['milestones']} M " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TPLAN " . + " ON NH_TPLAN.id=M.testplan_id " . " WHERE M.id = " . + $this->db->prepare_int($id); + + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + * check existence of milestone name in Test Plan + * + * @param integer $tplan_id + * test plan id. + * @param string $milestone_name + * milestone name + * @param integer $milestone_id + * default: null + * when is not null we add milestone_id as filter, this is useful + * to understand if is really a duplicate when using this method + * while managing update operations via GUI + * + * @return integer 1 => name exists + */ + public function check_name_existence($tplan_id, $milestone_name, + $milestone_id = null, $case_sensitive = 0) + { + $sql = " SELECT id, name FROM {$this->tables['milestones']} " . + " WHERE testplan_id = " . $this->db->prepare_int($tplan_id); + + if ($case_sensitive) { + $sql .= " AND name="; + } else { + $milestone_name = strtoupper($milestone_name); + $sql .= " AND UPPER(name)="; + } + $sql .= "'{$this->db->prepare_string($milestone_name)}'"; + + if (! is_null($milestone_id)) { + $sql .= " AND id <> " . $this->db->prepare_int($milestone_id); + } + + $result = $this->db->exec_query($sql); + return $this->db->num_rows($result) ? 1 : 0; + } + + /* + * function: get_all_by_testplan + * get info about all milestones defined for a testlan + * args : + * tplan_id + * + * + * returns: + * + * rev : + */ + public function get_all_by_testplan($tplan_id) + { + $sql = " SELECT M.id, M.name, M.a AS high_percentage, M.b AS medium_percentage, M.c AS low_percentage, " . + " M.target_date, M.start_date, M.testplan_id, NH.name as testplan_name " . + " FROM {$this->tables['milestones']} M, {$this->tables['nodes_hierarchy']} NH " . + " WHERE testplan_id={$tplan_id} AND NH.id = testplan_id " . + " ORDER BY M.target_date,M.name"; + return $this->db->get_recordset($sql); + } +} diff --git a/lib/functions/testproject.class.php b/lib/functions/testproject.class.php index e8e4c6f76e..7ff575db65 100644 --- a/lib/functions/testproject.class.php +++ b/lib/functions/testproject.class.php @@ -1,3395 +1,3453 @@ - 'exclude_me','requirement_spec'=> 'exclude_me','requirement'=> 'exclude_me'); - - var $nt2exclude_children=array('testcase' => 'exclude_my_children','requirement_spec'=> 'exclude_my_children'); - - var $debugMsg; - var $tmp_dir; - var $node_types_descr_id; - var $my_node_type; - var $cfg; - - /** - * Class constructor - * - * @param resource &$db reference to database handler - */ - function __construct(&$db) { - - $this->tmp_dir = config_get('temp_dir'); - - $this->db = &$db; - $this->tree_manager = new tree($this->db); - $this->cfield_mgr=new cfield_mgr($this->db); - $this->debugMsg = 'Class:' . __CLASS__ . ' - Method: '; - tlObjectWithAttachments::__construct($this->db,'nodes_hierarchy'); - $this->object_table = $this->tables['testprojects']; - - $this->node_types_descr_id = &$this->tree_manager->node_descr_id; - $this->my_node_type = $this->tree_manager->node_descr_id['testproject']; - - $this->cfg = new stdClass(); - $this->cfg->keywords = config_get('keywords'); - } - -/** - * Create a new Test project - * - * @param string $name Name of project - * @param string $color value according to CSS color definition - * @param string $notes project description (HTML text) - * @param array $options project features/options - * bolean keys: inventoryEnabled, automationEnabled, - * testPriorityEnabled, requirementsEnabled - * @param boolean $active [1,0] optional - * @param string $tcasePrefix [''] - * @param boolean $is_public [1,0] optional - * - * @return integer test project id or 0 (if fails) - * - * @internal revisions - * - */ -function create($item,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('doChecks' => false, 'setSessionProject' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $serOptions = serialize($item->options); - - try { - $tcPrefix = $this->formatTcPrefix($item->prefix); // will truncate prefix is len() > limit - - // mandatory checks - if(strlen($item->name)==0) { - throw new Exception('Empty name is not allowed'); - } - - if($my['opt']['doChecks']) { - $check = $this->checkNameSintax($item->name); - if($check['status_ok']) { - $check = $this->checkNameExistence($item->name); - } - if($check['status_ok']) { - $check = $this->checkTestCasePrefixExistence($tcPrefix); - } - - if(!$check['status_ok']) { - throw new Exception($check['msg']); - } - } - } - catch (Exception $e) { - throw $e; // rethrow - } - - // Create API KEY 64 bit long - $api_key = md5(rand()) . md5(rand()); - - // Create Node and get the id - $id = $this->tree_manager->new_root_node($item->name); - $sql = " INSERT INTO {$this->object_table} (id,color," . - " options,notes,active,is_public,prefix,api_key) " . - " VALUES (" . $id . ", '" . - $this->db->prepare_string($item->color) . "','" . - $serOptions . "','" . - $this->db->prepare_string($item->notes) . "'," . - $item->active . "," . $item->is_public . ",'" . - $this->db->prepare_string($tcPrefix) . "','" . - $this->db->prepare_string($api_key) . "')"; - $result = $this->db->exec_query($sql); - - $evt = new stdClass(); - $evt->message = TLS("audit_testproject_created", $item->name); - $evt->code = "CREATE"; - $evt->source = $this->auditCfg->eventSource; - $evt->objectType = 'testprojects'; - - if ($result) { - // set project to session if not defined (the first project) or update the current - if (!isset($_SESSION['testprojectID']) && $my['opt']['setSessionProject']) { - $this->setSessionProject($id); - } - $evt->logLevel = 'AUDIT'; - - // Send Event - $ctx = array('id' => $id, 'name' => $item->name, 'prefix' => $tcPrefix); - event_signal('EVENT_TEST_PROJECT_CREATE', $ctx); - } - else { - $id = 0; - $evt->logLevel = 'ERROR'; - } - - $evt->objectID = $id; - - logEvent($evt); - - return $id; -} - -/** - * Update Test project data in DB and (if applicable) current session data - * - * @param integer $id project Identifier - * @param string $name Name of project - * @param string $color value according to CSS color definition - * @param string $notes project description (HTML text) - * @param array $options project features/options - * bolean keys: inventoryEnabled, automationEnabled, - * testPriorityEnabled, requirementsEnabled - * - * @return boolean result of DB update - * - * @internal - * - **/ -function update($id, $name, $color, $notes,$options,$active=null, - $tcasePrefix=null,$is_public=null) -{ - $status_ok=1; - $status_msg = 'ok'; - $log_msg = 'Test project ' . $name . ' update: Ok.'; - $log_level = 'INFO'; - $safeID = intval($id); - - $add_upd=''; - if( !is_null($active) ) - { - $add_upd .=',active=' . (intval($active) > 0 ? 1:0); - } - - if( !is_null($is_public) ) - { - $add_upd .=',is_public=' . (intval($is_public) > 0 ? 1:0); - } - - if( !is_null($tcasePrefix) ) - { - $tcprefix=$this->formatTcPrefix($tcasePrefix); - $add_upd .=",prefix='" . $this->db->prepare_string($tcprefix) . "'" ; - } - $serOptions = serialize($options); - - $sql = " UPDATE {$this->object_table} SET color='" . $this->db->prepare_string($color) . "', ". - " options='" . $serOptions . "', " . - " notes='" . $this->db->prepare_string($notes) . "' {$add_upd} " . - " WHERE id=" . $safeID; - $result = $this->db->exec_query($sql); - - if ($result) - { - // update related node - $sql = "UPDATE {$this->tables['nodes_hierarchy']} SET name='" . - $this->db->prepare_string($name) . "' WHERE id= {$safeID}"; - $result = $this->db->exec_query($sql); - } - - if ($result) - { - // update session data - $this->setSessionProject($safeID); - - // Send Event - $ctx = array('id' => $id, 'name' => $name, 'prefix' => $tcprefix); - event_signal('EVENT_TEST_PROJECT_UPDATE', $ctx); - } - else - { - $status_msg = 'Update FAILED!'; - $status_ok = 0; - $log_level ='ERROR'; - $log_msg = $status_msg; - } - - tLog($log_msg,$log_level); - return ($status_ok); -} - -/** - * Set session data related to a Test project - * - * @param integer $projectId Project ID; zero causes unset data - */ -public function setSessionProject($projectId) -{ - $tproject_info = null; - - if ($projectId) - { - $tproject_info = $this->get_by_id($projectId); - } - - if ($tproject_info) - { - $_SESSION['testprojectID'] = $tproject_info['id']; - $_SESSION['testprojectName'] = $tproject_info['name']; - $_SESSION['testprojectColor'] = $tproject_info['color']; - $_SESSION['testprojectPrefix'] = $tproject_info['prefix']; - - if(!isset($_SESSION['testprojectOptions']) ) - { - $_SESSION['testprojectOptions'] = new stdClass(); - } - $_SESSION['testprojectOptions']->requirementsEnabled = - isset($tproject_info['opt']->requirementsEnabled) - ? $tproject_info['opt']->requirementsEnabled : 0; - $_SESSION['testprojectOptions']->testPriorityEnabled = - isset($tproject_info['opt']->testPriorityEnabled) - ? $tproject_info['opt']->testPriorityEnabled : 0; - $_SESSION['testprojectOptions']->automationEnabled = - isset($tproject_info['opt']->automationEnabled) - ? $tproject_info['opt']->automationEnabled : 0; - $_SESSION['testprojectOptions']->inventoryEnabled = - isset($tproject_info['opt']->inventoryEnabled) - ? $tproject_info['opt']->inventoryEnabled : 0; - - tLog("Test Project was activated: [" . $tproject_info['id'] . "]" . - $tproject_info['name'], 'INFO'); - } - else - { - if (isset($_SESSION['testprojectID'])) - { - tLog("Test Project deactivated: [" . $_SESSION['testprojectID'] . "] " . - $_SESSION['testprojectName'], 'INFO'); - } - unset($_SESSION['testprojectID']); - unset($_SESSION['testprojectName']); - unset($_SESSION['testprojectColor']); - unset($_SESSION['testprojectOptions']); - unset($_SESSION['testprojectPrefix']); - } - -} - - -/** - * Unserialize project options - * - * @param array $recorset produced by getTestProject() - */ -protected function parseTestProjectRecordset(&$recordset) { - if (null != $recordset && count($recordset) > 0) { - foreach ($recordset as $number => $row) { - $recordset[$number]['opt'] = unserialize($row['options']); - } - } else { - $recordset = null; - tLog('parseTestProjectRecordset: No project on query', 'DEBUG'); - } -} - - -/** - * Get Test project data according to parameter with unique value - * - * @param string $condition (optional) additional SQL condition(s) - * @return array map with test project info; null if query fails - */ -protected function getTestProject($condition = null, $opt=null) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('options' => array('output' => 'full')); - $my['options'] = array_merge($my['options'],(array)$opt); - - $doParse = true; - $tprojCols = ' testprojects.* '; - - switch($my['options']['output']) { - case 'existsByID': - $doParse = false; - $sql = "/* debugMsg */ SELECT testprojects.id ". - " FROM {$this->object_table} testprojects " . - " WHERE 1=1 "; - break; - - case 'existsByName': - $doParse = false; - $sql = "/* debugMsg */ SELECT testprojects.id ". - " FROM {$this->object_table} testprojects, " . - " {$this->tables['nodes_hierarchy']} nodes_hierarchy". - " WHERE testprojects.id = nodes_hierarchy.id " . - " AND nodes_hierarchy.node_type_id = " . - $this->tree_manager->node_descr_id['testproject']; - break; - - case 'name': - $doParse = false; - $tprojCols = 'testprojects.id'; - - case 'full': - default: - $sql = "/* debugMsg */ SELECT {$tprojCols}, nodes_hierarchy.name ". - " FROM {$this->object_table} testprojects, " . - " {$this->tables['nodes_hierarchy']} nodes_hierarchy". - " WHERE testprojects.id = nodes_hierarchy.id "; - " AND nodes_hierarchy.node_type_id = " . - $this->tree_manager->node_descr_id['testproject']; - break; - } - if (!is_null($condition) ) - { - $sql .= " AND " . $condition; - } - - $rs = $this->db->get_recordset($sql); - if($doParse) - { - $this->parseTestProjectRecordset($rs); - } - return $rs; -} - - -/** - * Get Test project data according to name - * - * @param string $name - * @param string $addClause (optional) additional SQL condition(s) - * - * @return array map with test project info; null if query fails - */ -public function get_by_name($name, $addClause = null, $opt=null) -{ - $condition = "nodes_hierarchy.name='" . $this->db->prepare_string($name) . "'"; - $condition .= is_null($addClause) ? '' : " AND {$addClause} "; - - return $this->getTestProject($condition); -} - - -/** - * Get Test project data according to ID - * - * @param integer $id test project - * @return array map with test project info; null if query fails - */ -public function get_by_id($id, $opt=null) -{ - $condition = "testprojects.id=". intval($id); - $result = $this->getTestProject($condition,$opt); - return $result[0]; -} - - -/** - * Get Test project data according to prefix - * - * @param string $prefix - * @param string $addClause optional additional SQL 'AND filter' clause - * - * @return array map with test project info; null if query fails - */ -public function get_by_prefix($prefix, $addClause = null) { - $safe_prefix = $this->db->prepare_string($prefix); - $condition = "testprojects.prefix='{$safe_prefix}'"; - $condition .= is_null($addClause) ? '' : " AND {$addClause} "; - - $rs = $this->getTestProject($condition); - return $rs != null ? $rs[0] : null; -} - - -/** - * Get Test project data according to APIKEY - * - * @param string 64 chars - * @return array map with test project info; null if query fails - */ -public function getByAPIKey($apiKey, $opt=null) { - $condition = "testprojects.api_key='{$apiKey}'"; - $result = $this->getTestProject($condition,$opt); - return $result[0]; -} - - -/* - function: get_all - get array of info for every test project - without any kind of filter. - Every array element contains an assoc array with test project info - -args:[order_by]: default " ORDER BY nodes_hierarchy.name " -> testproject name - - -*/ -function get_all($filters=null,$options=null) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my = array ('filters' => '', 'options' => ''); - - - $my['filters'] = array('active' => null); - $my['options'] = array('order_by' => " ORDER BY nodes_hierarchy.name ", - 'access_key' => null, 'output' => 'std'); - - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - - if($my['options']['output'] == 'count') - { - $sql = "/* $debugMsg */ SELECT COUNT(testprojects.id) AS qty ". - " FROM {$this->object_table} testprojects"; - - $rs = $this->db->get_recordset($sql); - return $rs[0]['qty']; - } - - // - $sql = "/* $debugMsg */ SELECT testprojects.*, nodes_hierarchy.name ". - " FROM {$this->object_table} testprojects, " . - " {$this->tables['nodes_hierarchy']} nodes_hierarchy ". - " WHERE testprojects.id = nodes_hierarchy.id "; - - if (!is_null($my['filters']['active']) ) - { - $sql .= " AND active=" . intval($my['filters']['active']) . " "; - } - - if( !is_null($my['options']['order_by']) ) - { - $sql .= $my['options']['order_by']; - } - - if( is_null($my['options']['access_key'])) { - $recordset = $this->db->get_recordset($sql); - $this->parseTestProjectRecordset($recordset); - } else { - $recordset = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']); - if (null != $recordset && count($recordset) > 0) { - foreach ($recordset as $number => $row) { - $recordset[$number]['opt'] = unserialize($row['options']); - } - } - } - - return $recordset; -} - - -/* -function: get_accessible_for_user - get list of testprojects, considering user roles. - Remember that user has: - 1. one default role, assigned when user was created - 2. a different role can be assigned for every testproject. - - For users roles that has not rigth to modify testprojects - only active testprojects are returned. - -args: - user_id - [output_type]: choose the output data structure. - possible values: map, map_of_map - map: key -> test project id - value -> test project name - - map_of_map: key -> test project id - value -> array ('name' => test project name, - 'active' => active status) - - array_of_map: value -> array with all testproject table fields plus name. - - - default: map - [order_by]: default: ORDER BY name - -*/ -function get_accessible_for_user($user_id,$opt = null,$filters = null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my = array(); - $my['opt'] = array('output' => 'map', 'order_by' => ' ORDER BY name ', 'field_set' => 'full', - 'format' => 'std', 'add_issuetracker' => false, 'add_codetracker' => false, - 'add_reqmgrsystem' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - // key = field name - // value = array('op' => Domain ('=','like'), 'value' => the value) - $my['filters'] = array('name' => null, 'id' => null, 'prefix' => null); - $my['filters'] = array_merge($my['filters'],(array)$filters); - - - $items = array(); - $safe_user_id = intval($user_id); - - // Get default/global role - $sql = "/* $debugMsg */ SELECT id,role_id FROM {$this->tables['users']} where id=" . $safe_user_id; - $user_info = $this->db->get_recordset($sql); - $globalRoleID = intval($user_info[0]['role_id']); - - $itsql = ''; - $itf = ''; - if($my['opt']['add_issuetracker']) { - $itsql = " LEFT OUTER JOIN {$this->tables['testproject_issuetracker']} AS TIT " . - " ON TIT.testproject_id = TPROJ.id " . - " LEFT OUTER JOIN {$this->tables['issuetrackers']} AS ITMD " . - " ON ITMD.id = TIT.issuetracker_id "; - $itf = ",ITMD.name AS itname,ITMD.type AS ittype"; - } - - $ctsql = ''; - $ctf = ''; - if($my['opt']['add_codetracker']) { - $ctsql = " LEFT OUTER JOIN {$this->tables['testproject_codetracker']} AS TCT " . - " ON TCT.testproject_id = TPROJ.id " . - " LEFT OUTER JOIN {$this->tables['codetrackers']} AS CTMD " . - " ON CTMD.id = TCT.codetracker_id "; - $ctf = ",CTMD.name AS ctname,CTMD.type AS cttype"; - } - - - $rmssql = ''; - $rmsf = ''; - if($my['opt']['add_reqmgrsystem']) { - $rmssql = " LEFT OUTER JOIN {$this->tables['testproject_reqmgrsystem']} AS TRMS " . - " ON TRMS.testproject_id = TPROJ.id " . - " LEFT OUTER JOIN {$this->tables['reqmgrsystems']} AS RMSMD " . - " ON RMSMD.id = TRMS.reqmgrsystem_id "; - $rmsf = ",RMSMD.name AS rmsname,RMSMD.type AS rmstype"; - } - - switch($my['opt']['field_set']) { - case 'id': - $cols = ' TPROJ.id,NHTPROJ.name '; - $my['opt']['format'] = 'do not parse'; - break; - - case 'prefix': - $cols = ' TPROJ.id,TPROJ.prefix,TPROJ.active,NHTPROJ.name '; - $my['opt']['format'] = 'do not parse'; - break; - - case 'full': - default: - $cols = ' TPROJ.*,NHTPROJ.name,COALESCE(UTR.role_id,U.role_id) AS effective_role '; - break; - } - - $sql = " /* $debugMsg */ SELECT {$cols} {$itf} {$ctf} {$rmsf} " . - " FROM {$this->tables['nodes_hierarchy']} NHTPROJ " . - " JOIN {$this->object_table} TPROJ ON NHTPROJ.id=TPROJ.id " . - " JOIN {$this->tables['users']} U ON U.id = {$safe_user_id} " . - " LEFT OUTER JOIN {$this->tables['user_testproject_roles']} UTR " . - " ON TPROJ.id = UTR.testproject_id " . - " AND UTR.user_id =" . $safe_user_id . $itsql . $ctsql . $rmssql . - " WHERE 1=1 "; - - // Private test project feature - if( $globalRoleID != TL_ROLES_ADMIN ) { - if ($globalRoleID != TL_ROLES_NO_RIGHTS) { - $sql .= " AND "; - $sql_public = " ( TPROJ.is_public = 1 AND (UTR.role_id IS NULL OR UTR.role_id != " . TL_ROLES_NO_RIGHTS. ") )"; - $sql_private = " ( TPROJ.is_public = 0 AND UTR.role_id != " . TL_ROLES_NO_RIGHTS. ") "; - $sql .= " ( {$sql_public} OR {$sql_private} ) "; - } else { - // User needs specific role - $sql .= " AND (UTR.role_id IS NOT NULL AND UTR.role_id != ".TL_ROLES_NO_RIGHTS.")"; - } - } - - $userObj = tlUser::getByID($this->db,$safe_user_id,tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); - if ($userObj->hasRight($this->db,'mgt_modify_product') != 'yes') { - $sql .= " AND TPROJ.active=1 "; - } - unset($userObj); - - foreach($my['filters'] as $fname => $fspec) { - if(!is_null($fspec)) { - switch($fname) { - case 'prefix': - $sql .= " AND TPROJ.$fname"; - $sm = 'prepare_string'; - break; - - case 'name': - $sql .= " AND NHTPROJ.$fname"; - $sm = 'prepare_string'; - break; - - case 'id': - $sql .= " AND NHTPROJ.$fname"; - $sm = 'prepare_int'; - break; - } - - $safe = $this->db->$sm($fspec['value']); - switch($fspec['op']) { - case '=': - if($sm == 'prepare_string') { - $sql .= "='" . $safe . "'"; - } else { - $sql .= "=" . $safe; - } - break; - - case 'like': - $sql .= " LIKE '%" . $safe ."%'"; - break; - } - } - } - - - $sql .= str_replace('nodes_hierarchy','NHTPROJ',$my['opt']['order_by']); - $parseOpt = false; - $do_post_process = 0; - $arrTemp = array(); - switch($my['opt']['output']) { - case 'array_of_map': - $items = $this->db->get_recordset($sql); //,null,3,1); - $parseOpt = true; - break; - - case 'map_of_map_full': - $items = $this->db->fetchRowsIntoMap($sql,'id'); - $parseOpt = true; - break; - - case 'map': - $items = $this->db->fetchRowsIntoMap($sql,'id'); - break; - - case 'map_with_inactive_mark': - default: - $arrTemp = (array)$this->db->fetchRowsIntoMap($sql,'id'); - $do_post_process = (count($arrTemp) > 0); - break; - } - - if($my['opt']['format'] == 'std' && $parseOpt) { - $this->parseTestProjectRecordset($items); - } - - if ($do_post_process) { - switch ($my['opt']['output']) { - case 'map_name_with_inactive_mark': - foreach($arrTemp as $id => $row) { - $noteActive = ''; - if (!$row['active']) { - $noteActive = TL_INACTIVE_MARKUP; - } - $items[$id] = $noteActive . - ( ($my['opt']['field_set'] =='prefix') ? ($row['prefix'] . ':') : '' ) . $row['name']; - } - break; - - case 'map_of_map': - foreach($arrTemp as $id => $row) { - $items[$id] = array('name' => $row['name'],'active' => $row['active']); - } - break; - } - unset($arrTemp); - } - - return $items; -} - - -/* - function: get_subtree - Get subtree that has choosen testproject as root. - Only nodes of type: - testsuite and testcase are explored and retrieved. - - args: id: testsuite id - [recursive_mode]: default false - [exclude_testcases]: default: false - [exclude_branches] - [additionalWhereClause]: - - - returns: map - see tree->get_subtree() for details. - - -*/ -function get_subtree($id,$filters=null,$opt=null) -{ - $my = array(); - $my['options'] = array('recursive' => false, 'exclude_testcases' => false, 'output' => 'full'); - $my['filters'] = array('exclude_node_types' => $this->nt2exclude, - 'exclude_children_of' => $this->nt2exclude_children, - 'exclude_branches' => null, - 'additionalWhereClause' => ''); - - $my['options'] = array_merge($my['options'],(array)$opt); - $my['filters'] = array_merge($my['filters'],(array)$filters); - - if($my['options']['exclude_testcases']) - { - $my['filters']['exclude_node_types']['testcase']='exclude me'; - } - - $subtree = $this->tree_manager->get_subtree(intval($id),$my['filters'],$my['options']); - return $subtree; -} - - -/** - * Displays smarty template to show test project info to users. - * - * @param type $smarty [ref] smarty object - * @param type $id test project - * @param type $sqlResult [default = ''] - * @param type $action [default = 'update'] - * @param type $modded_item_id [default = 0] - * - * @internal revisions - * - **/ -function show(&$smarty,$guiObj,$template_dir,$id,$sqlResult='', $action = 'update',$modded_item_id = 0) -{ - $gui = $guiObj; - - $gui->sqlResult = ''; - $gui->sqlAction = ''; - if ($sqlResult) { - $gui->sqlResult = $sqlResult; - } - - $p2ow = array('refreshTree' => false, 'user_feedback' => ''); - foreach ($p2ow as $prop => $value) { - if (!property_exists($gui,$prop)) { - $gui->$prop = $value; - } - } - - $safeID = intval($id); - $gui->tproject_id = $safeID; - $gui->modify_tc_rights = has_rights($this->db,"mgt_modify_tc",$safeID); - $gui->mgt_modify_product = has_rights($this->db,"mgt_modify_product"); - - - - $gui->container_data = $this->get_by_id($safeID); - $gui->moddedItem = $gui->container_data; - $gui->level = 'testproject'; - $gui->page_title = lang_get('testproject'); - $gui->refreshTree = property_exists($gui,'refreshTree') ? $gui->refreshTree : false; - $gui->attachmentInfos = getAttachmentInfosFrom($this,$safeID); - - // attachments management on page - $gui->fileUploadURL = $_SESSION['basehref'] . $this->getFileUploadRelativeURL($safeID); - $gui->delAttachmentURL = $_SESSION['basehref'] . $this->getDeleteAttachmentRelativeURL($safeID); - $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; - $gui->fileUploadMsg = ''; - - $exclusion = array( 'testcase', 'me', 'testplan' => 'me', 'requirement_spec' => 'me'); - $gui->canDoExport = count((array)$this->tree_manager->get_children($safeID,$exclusion)) > 0; - if ($modded_item_id) { - $gui->moddedItem = $this->get_by_id(intval($modded_item_id)); - } - $cfg = getWebEditorCfg('testproject'); - $gui->testProjectEditorType = $cfg['type']; - - $smarty->assign('gui', $gui); - $smarty->display($template_dir . 'containerView.tpl'); -} - - -/** - * Count testcases without considering active/inactive status. - * - * @param integer $id: test project identifier - * @return integer count of test cases presents on test project. - */ -function count_testcases($id) -{ - $tcIDs = array(); - $this->get_all_testcases_id($id,$tcIDs); - $qty = sizeof($tcIDs); - return $qty; -} - - - /* - function: gen_combo_test_suites - create array with test suite names - test suites are ordered in parent-child way, means - order on array is creating traversing tree branches, reaching end - of branch, and starting again. (recursive algorithim). - - - args : $id: test project id - [$exclude_branches]: array with testsuite id to exclude - useful to exclude myself ($id) - [$mode]: dotted -> $level number of dot characters are appended to - the left of test suite name to create an indent effect. - Level indicates on what tree layer testsuite is positioned. - Example: - - null - \ - id=1 <--- Tree Root = Level 0 - | - + ------+ - / \ \ - id=9 id=2 id=8 <----- Level 1 - \ - id=3 <----- Level 2 - \ - id=4 <----- Level 3 - - - key: testsuite id (= node id on tree). - value: every array element is an string, containing testsuite name. - - Result example: - - 2 .TS1 - 3 ..TS2 - 9 .20071014-16:22:07 TS1 - 10 ..TS2 - - - array -> key: testsuite id (= node id on tree). - value: every array element is a map with the following keys - 'name', 'level' - - 2 array(name => 'TS1',level => 1) - 3 array(name => 'TS2',level => 2) - 9 array(name => '20071014-16:22:07 TS1',level =>1) - 10 array(name => 'TS2', level => 2) - - - returns: map , structure depens on $mode argument. - - */ - function gen_combo_test_suites($id,$exclude_branches=null,$mode='dotted') - { - $ret = array(); - $test_spec = $this->get_subtree($id, array('exclude_branches' => $exclude_branches), - array('recursive' => !self::RECURSIVE_MODE, - 'exclude_testcases' => self::EXCLUDE_TESTCASES)); - - if(count($test_spec)) - { - $ret = $this->_createHierarchyMap($test_spec,$mode); - } - return $ret; - } - - /** - * Checks a test project name for correctness - * - * @param string $name the name to check - * @return map with keys: status_ok, msg - **/ - function checkName($name) - { - $forbidden_pattern = config_get('ereg_forbidden'); - $ret['status_ok'] = 1; - $ret['msg'] = 'ok'; - - if ($name == "") - { - $ret['msg'] = lang_get('info_product_name_empty'); - $ret['status_ok'] = 0; - } - if ($ret['status_ok'] && !check_string($name,$forbidden_pattern)) - { - $ret['msg'] = lang_get('string_contains_bad_chars'); - $ret['status_ok'] = 0; - } - return $ret; - } - - /** - * Checks a test project name for sintax correctness - * - * @param string $name the name to check - * @return map with keys: status_ok, msg - **/ - function checkNameSintax($name) - { - $forbidden_pattern = config_get('ereg_forbidden'); - $ret['status_ok'] = 1; - $ret['msg'] = 'ok'; - - if ($name == "") - { - $ret['msg'] = lang_get('info_product_name_empty'); - $ret['status_ok'] = 0; - } - if ($ret['status_ok'] && !check_string($name,$forbidden_pattern)) - { - $ret['msg'] = lang_get('string_contains_bad_chars'); - $ret['status_ok'] = 0; - } - return $ret; - } - - /** - * Checks is there is another testproject with different id but same name - * - **/ - function checkNameExistence($name,$id=0) - { - $check_op['msg'] = ''; - $check_op['status_ok'] = 1; - - if($this->get_by_name($name,"testprojects.id <> {$id}") ) - { - $check_op['msg'] = sprintf(lang_get('error_product_name_duplicate'),$name); - $check_op['status_ok'] = 0; - } - return $check_op; - } - - /** - * Checks is there is another testproject with different id but same prefix - * - **/ - function checkTestCasePrefixExistence($prefix,$id=0) - { - $check_op = array('msg' => '', 'status_ok' => 1); - $sql = " SELECT id FROM {$this->object_table} " . - " WHERE prefix='" . $this->db->prepare_string($prefix) . "'"; - " AND id <> {$id}"; - - $rs = $this->db->get_recordset($sql); - if(!is_null($rs)) - { - $check_op['msg'] = sprintf(lang_get('error_tcase_prefix_exists'),$prefix); - $check_op['status_ok'] = 0; - } - - return $check_op; - } - - - - /** - * allow activate or deactivate a test project - * - * @param integer $id test project ID - * @param integer $status 1=active || 0=inactive - */ - function activate($id, $status) - { - $sql = "UPDATE {$this->tables['testprojects']} SET active=" . $status . " WHERE id=" . $id; - $result = $this->db->exec_query($sql); - - return $result ? 1 : 0; - } - - /** @TODO add description */ - function formatTcPrefix($str) - { - $fstr = trim($str); - if(tlStringLen($fstr) == 0) - { - throw new Exception('Empty prefix is not allowed'); - } - - // limit tcasePrefix len. - if(tlStringLen($fstr) > self::TESTCASE_PREFIX_MAXLEN) - { - $fstr = substr($fstr, 0, self::TESTCASE_PREFIX_MAXLEN); - } - return $fstr; - } - - - /* - args : id: test project - returns: null if query fails - string - */ - function getTestCasePrefix($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret=null; - $sql = "/* $debugMsg */ SELECT prefix FROM {$this->object_table} WHERE id = {$id}"; - $ret = $this->db->fetchOneValue($sql); - return ($ret); - } - - - /* - args: id: test project - returns: null if query fails - a new test case number - */ - function generateTestCaseNumber($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $retry = 3; - $lockfile = $this->tmp_dir . __FUNCTION__ . '.lock'; - $lock = fopen($lockfile, 'a'); - - $gotLock = false; - while( $retry > 0 && !$gotLock ) - { - if( flock($lock,LOCK_EX) ) - { - $gotLock = true; - } - else - { - $retry--; - usleep(20); - } - } - - if( $gotLock || $retry == 0 ) - { - $safeID = intval($id); - - $ret=null; - $sql = "/* $debugMsg */ UPDATE {$this->object_table} " . - " SET tc_counter=tc_counter+1 WHERE id = {$safeID}"; - $rs = $this->db->exec_query($sql); - - $sql = " SELECT tc_counter FROM {$this->object_table} WHERE id = {$safeID}"; - $rs = $this->db->get_recordset($sql); - $ret = $rs[0]['tc_counter']; - - if( $gotLock ) - { - flock($lock, LOCK_UN); - } - fclose($lock); - - return $ret; - } - - } - - /** - * - * - */ - function setTestCaseCounter($id,$value,$force=false) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safeValue = intval($value); - $ret=null; - $sql = " /* $debugMsg */ UPDATE {$this->object_table} " . - ' SET tc_counter=' . $safeValue . - ' WHERE id =' . intval($id); - - if(!$force) - { - $sql .= ' AND tc_counter < ' . $safeValue; - } - $rs = $this->db->exec_query($sql); - } - - - -/** - * @param integer $id test project ID - */ -function setPublicStatus($id,$status) -{ - $isPublic = val($status) > 0 ? 1 : 0; - $sql = "UPDATE {$this->object_table} SET is_public={$isPublic} WHERE id={$id}"; - $result = $this->db->exec_query($sql); - return $result ? 1 : 0; -} - - - - /* Keywords related methods */ - /** - * Adds a new keyword to the given test project - * - * @param int $testprojectID - * @param string $keyword - * @param string $notes - * - **/ - public function addKeyword($testprojectID,$keyword,$notes) { - $kw = new tlKeyword(); - $kw->initialize(null,$testprojectID,$keyword,$notes); - $op = array('status' => tlKeyword::E_DBERROR, 'id' => -1, - 'msg' => 'ko DB Error'); - $op['status'] = $kw->writeToDB($this->db); - if ($op['status'] >= tl::OK) { - $op['id'] = $kw->dbID; - logAuditEvent(TLS("audit_keyword_created",$keyword),"CREATE",$op['id'],"keywords"); - } else { - $op['msg'] = tlKeyword::getError($op['status']); - } - - return $op; - } - - /** - * updates the keyword with the given id - * - * @param type $testprojectID - * @param type $id - * @param type $keyword - * @param type $notes - * - **/ - function updateKeyword($testprojectID,$id,$keyword,$notes) { - $kw = new tlKeyword($id); - $kw->initialize($id,$testprojectID,$keyword,$notes); - $result = $kw->writeToDB($this->db); - if ($result >= tl::OK) { - logAuditEvent(TLS("audit_keyword_saved",$keyword),"SAVE",$kw->dbID,"keywords"); - } - return $result; - } - - /** - * gets the keyword with the given id - * - * @param type $kwid - **/ - public function getKeyword($id) { - return tlKeyword::getByID($this->db,$id); - } - - /** - * Gets the keywords of the given test project - * - * @param int $tprojectID the test project id - * @param int $keywordID [default = null] the optional keyword id - * - * @return array, every elemen is map with following structure: - * id - * keyword - * notes - **/ - public function getKeywords($testproject_id) { - $ids = $this->getKeywordIDsFor($testproject_id); - return tlKeyword::getByIDs($this->db,$ids); - } - - /** - * Deletes the keyword with the given id - * - * @param int $id the keywordID - * @return int returns 1 on success, 0 else - * - **/ - function deleteKeyword($id, $opt=null) { - $result = tl::ERROR; - $my['opt'] = array('checkBeforeDelete' => true, 'nameForAudit' => null, - 'context' => '', 'tproject_id' => null); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $doIt = !$my['opt']['checkBeforeDelete']; - $keyword = $my['opt']['nameForAudit']; - - if($my['opt']['checkBeforeDelete']) { - $doIt = true; - if( $this->cfg->keywords->onDeleteCheckExecutedTCVersions ) { - $linkedAndNotExec = $this->checkKeywordIsLinkedAndNotExecuted($id); - $doIt = $doIt && $linkedAndNotExec; - } - - if( $this->cfg->keywords->onDeleteCheckFrozenTCVersions ) { - $linkedToFrozen = $this->checkKeywordIsLinkedToFrozenVersions($id); - $doIt = $doIt && !$linkedToFrozen; - } - } - - if( $doIt ) { - if( $this->auditCfg->logEnabled ) { - $keyword = $this->getKeywordSimple($id); - } - $result = tlDBObject::deleteObjectFromDB($this->db,$id,"tlKeyword"); - } - - if ($result >= tl::OK && $this->auditCfg->logEnabled) { - - switch($my['opt']['context']) { - case 'getTestProjectName': - $dummy = $this->get_by_id($my['opt']['tproject_id'],array('output'=>'name')); - $my['opt']['context'] = $dummy['name']; - break; - } - - logAuditEvent(TLS("audit_keyword_deleted",$keyword,$my['opt']['context']), - "DELETE",$id,"keywords"); - } - return $result; - } - - /** - * delete Keywords - */ - function deleteKeywords($tproject_id,$tproject_name=null) { - $result = tl::OK; - - $itemSet = (array)$this->getKeywordSet($tproject_id); - $kwIDs = array_keys($itemSet); - - $opt = array('checkBeforeDelete' => false, - 'context' => $tproject_name); - - $loop2do = sizeof($kwIDs); - for($idx = 0;$idx < $loop2do; $idx++) { - $opt['nameForAudit'] = $itemSet[$kwIDs[$idx]]['keyword']; - - $resultKw = $this->deleteKeyword($kwIDs[$idx],$opt); - if ($resultKw != tl::OK) { - $result = $resultKw; - } - } - return $result; - } - - - /** - * - * - */ - protected function getKeywordIDsFor($testproject_id) { - $query = " SELECT id FROM {$this->tables['keywords']} " . - " WHERE testproject_id = {$testproject_id}" . - " ORDER BY keyword ASC"; - $keywordIDs = $this->db->fetchColumnsIntoArray($query,'id'); - return $keywordIDs; - } - - /** - * - * - */ - function getKeywordSet($tproject_id) { - $sql = " SELECT id,keyword FROM {$this->tables['keywords']} " . - " WHERE testproject_id = {$tproject_id}" . - " ORDER BY keyword ASC"; - - $items = $this->db->fetchRowsIntoMap($sql,'id'); - return $items; - } - - - /** - * - * - */ - function hasKeywords($id) { - // seems that postgres PHP driver do not manage well UPPERCASE in AS CLAUSE - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* {$debugMsg} */ SELECT COUNT(0) AS qty FROM {$this->tables['keywords']} " . - " WHERE testproject_id = " . intval($id); - $rs = $this->db->get_recordset($sql); - - return ((is_null($rs) || $rs[0]['qty'] == 0) ? false : true); - } - - - /** - * Exports the given keywords to a XML file - * - * @return strings the generated XML Code - **/ - public function exportKeywordsToXML($testproject_id,$bNoXMLHeader = false) - { - $kwIDs = $this->getKeywordIDsFor($testproject_id); - $xmlCode = ''; - if (!$bNoXMLHeader) - { - $xmlCode .= TL_XMLEXPORT_HEADER."\n"; - } - $xmlCode .= ""; - for($idx = 0;$idx < sizeof($kwIDs);$idx++) - { - $keyword = new tlKeyword($kwIDs[$idx]); - $keyword->readFromDb($this->db); - $keyword->writeToXML($xmlCode,true); - } - $xmlCode .= ""; - - return $xmlCode; - } - - /** - * Exports the given keywords to CSV - * - * @return string the generated CSV code - **/ - function exportKeywordsToCSV($testproject_id,$delim = ';') { - $kwIDs = $this->getKeywordIDsFor($testproject_id); - $csv = null; - for($idx = 0;$idx < sizeof($kwIDs);$idx++) { - $keyword = new tlKeyword($kwIDs[$idx]); - $keyword->readFromDb($this->db); - $keyword->writeToCSV($csv,$delim); - } - return $csv; - } - - function importKeywordsFromCSV($testproject_id,$fileName,$delim = ';') - { - $handle = fopen($fileName,"r"); - if ($handle) - { - while($data = fgetcsv($handle, TL_IMPORT_ROW_MAX, $delim)) - { - $kw = new tlKeyword(); - $kw->initialize(null,$testproject_id,NULL,NULL); - if ($kw->readFromCSV(implode($delim,$data)) >= tl::OK) - { - if ($kw->writeToDB($this->db) >= tl::OK) - { - logAuditEvent(TLS("audit_keyword_created",$kw->name),"CREATE",$kw->dbID,"keywords"); - } - } - } - fclose($handle); - return tl::OK; - } - else - { - return ERROR; - } - } - - /** - * @param $testproject_id - * @param $fileName - */ - function importKeywordsFromXMLFile($testproject_id,$fileName) - { - $simpleXMLObj = @$this->simplexml_load_file_helper($fileName); - return $this->importKeywordsFromSimpleXML($testproject_id,$simpleXMLObj); - } - - - /** - * @param $testproject_id - * @param $xmlString - */ - function importKeywordsFromXML($testproject_id,$xmlString) - { - $simpleXMLObj = simplexml_load_string($xmlString); - return $this->importKeywordsFromSimpleXML($testproject_id,$simpleXMLObj); - } - - /** - * @param $testproject_id - * @param $simpleXMLObj - */ - function importKeywordsFromSimpleXML($testproject_id,$simpleXMLObj) - { - $status = tl::OK; - if(!$simpleXMLObj || $simpleXMLObj->getName() != 'keywords') - { - $status = tlKeyword::E_WRONGFORMAT; - } - - if( ($status == tl::OK) && $simpleXMLObj->keyword ) - { - foreach($simpleXMLObj->keyword as $keyword) - { - $kw = new tlKeyword(); - $kw->initialize(null,$testproject_id,NULL,NULL); - $status = tlKeyword::E_WRONGFORMAT; - if ($kw->readFromSimpleXML($keyword) >= tl::OK) - { - $status = tl::OK; - if ($kw->writeToDB($this->db) >= tl::OK) - { - logAuditEvent(TLS("audit_keyword_created",$kw->name),"CREATE",$kw->dbID,"keywords"); - } - } - } - } - return $status; - } - - /** - * Returns all testproject keywords - * - * @param integer $testproject_id the ID of the testproject - * @return array map: key: keyword_id, value: keyword - */ - function get_keywords_map($testproject_id) { - $keywordMap = null; - $keywords = $this->getKeywords($testproject_id); - if ($keywords) { - foreach($keywords as $kw) { - $keywordMap[$kw->dbID] = $kw->name; - } - } - return $keywordMap; - } - - /** - * Returns keywords that are linked to test cases - * - * @param integer $id testproject - * @return array map: key: keyword_id, value: keyword - */ - function getUsedKeywordsMap($id) { - $debugMsg = $this->debugMsg . __FUNCTION__; - $sql = "/* $debugMsg */ - SELECT DISTINCT KW.id,KW.keyword - FROM {$this->tables['keywords']} KW - JOIN {$this->tables['testcase_keywords']} TCKW - ON TCKW.keyword_id = KW.id - WHERE KW.testproject_id =" . intval($id); - $sql .= " ORDER BY keyword"; - $rs = $this->db->fetchColumnsIntoMap($sql,'id','keyword'); - return $rs; - } - - - /* END KEYWORDS RELATED */ - - /* REQUIREMENTS RELATED */ - /** - * get list of all SRS for a test project, no distinction between levels - * - * - * @used-by lib/results/uncoveredTestCases.php - * lib/requirements/reqTcAssign.php - * lib/requirements/reqSpecSearchForm.php - * lib/requirements/reqSearchForm.php - * - * @author Martin Havlat - * @return associated array List of titles according to IDs - * - * @internal revisions - * - **/ - function getOptionReqSpec($tproject_id,$get_not_empty=self::GET_EMPTY_REQSPEC) - { - $additional_table=''; - $additional_join=''; - if( $get_not_empty ) - { - $additional_table=", {$this->tables['requirements']} REQ "; - $additional_join=" AND SRS.id = REQ.srs_id "; - } - $sql = " SELECT SRS.id,NH.name AS title " . - " FROM {$this->tables['req_specs']} SRS, " . - " {$this->tables['nodes_hierarchy']} NH " . - $additional_table . - " WHERE testproject_id={$tproject_id} " . - " AND SRS.id=NH.id " . - $additional_join . - " ORDER BY title"; - return $this->db->fetchColumnsIntoMap($sql,'id','title'); - //return $this->db->fetchRowsIntoMap($sql,'id'); SRS.doc_id, - } // function end - - - /** - * @author Francisco Mancardi - francisco.mancardi@gmail.com - * - * @TODO check who uses it, is duplicated of getOptionReqSpec? - * - * @used-by lib/results/uncoveredTestCases.php - * lib/requirements/reqTcAssign.php - * lib/requirements/reqSpecSearchForm.php - * lib/requirements/reqSearchForm.php - * - * @internal revisions - * - * - **/ - function genComboReqSpec($id,$mode='dotted',$dot='.') - { - $ret = array(); - $exclude_node_types=array('testplan' => 'exclude_me','testsuite' => 'exclude_me', - 'testcase'=> 'exclude_me','requirement' => 'exclude_me', - 'requirement_spec_revision' => 'exclude_me'); - - $my['filters'] = array('exclude_node_types' => $exclude_node_types); - - $my['options'] = array('order_cfg' => array('type' => 'rspec'), 'output' => 'rspec'); - $subtree = $this->tree_manager->get_subtree($id,$my['filters'],$my['options']); - if(count($subtree)) - { - $ret = $this->_createHierarchyMap($subtree,$mode,$dot,'doc_id'); - } - return $ret; - } - - /* - - [$mode]: dotted -> $level number of dot characters are appended to - the left of item name to create an indent effect. - Level indicates on what tree layer item is positioned. - Example: - - null - \ - id=1 <--- Tree Root = Level 0 - | - + ------+ - / \ \ - id=9 id=2 id=8 <----- Level 1 - \ - id=3 <----- Level 2 - \ - id=4 <----- Level 3 - - - key: item id (= node id on tree). - value: every array element is an string, containing item name. - - Result example: - - 2 .TS1 - 3 ..TS2 - 9 .20071014-16:22:07 TS1 - 10 ..TS2 - - - array -> key: item id (= node id on tree). - value: every array element is a map with the following keys - 'name', 'level' - - 2 array(name => 'TS1',level => 1) - 3 array(name => 'TS2',level => 2) - 9 array(name => '20071014-16:22:07 TS1',level =>1) - 10 array(name => 'TS2', level => 2) - - */ - protected function _createHierarchyMap($array2map,$mode='dotted',$dot='.',$addfield=null) - { - $hmap=array(); - $the_level = 1; - $level = array(); - $pivot = $array2map[0]; - - $addprefix = !is_null($addfield); - foreach($array2map as $elem) - { - $current = $elem; - - if ($pivot['id'] == $current['parent_id']) - { - $the_level++; - $level[$current['parent_id']]=$the_level; - } - else if ($pivot['parent_id'] != $current['parent_id']) - { - $the_level = $level[$current['parent_id']]; - } - - switch($mode) - { - case 'dotted': - $dm = $addprefix ? "[{$current[$addfield]}] - " : ''; - $pding = ($the_level == 1) ? 0 : $the_level+1; - $hmap[$current['id']] = str_repeat($dot,$pding) . $dm . $current['name']; - break; - - case 'array': - $hmap[$current['id']] = array('name' => $current['name'], 'level' =>$the_level); - break; - } - - // update pivot - $level[$current['parent_id']]= $the_level; - $pivot=$elem; - } - - return $hmap; - } - - - - /** - * collect information about current list of Requirements Specification - * - * @param integer $testproject_id - * @param string $id optional id of the requirement specification - * - * @return mixed - * null if no srs exits, or no srs exists for id - * array, where each element is a map with SRS data. - * - * map keys: - * id - * testproject_id - * title - * scope - * total_req - * type - * author_id - * creation_ts - * modifier_id - * modification_ts - * - * @author Martin Havlat - * @internal revisions - * - **/ - public function getReqSpec($testproject_id, $id = null, $fields=null,$access_key=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $fields2get = " RSPEC.id, RSPEC.testproject_id, RSPECREV.scope, RSPECREV.doc_id," . - " RSPECREV.total_req, RSPECREV.type, RSPECREV.author_id, RSPECREV.creation_ts, " . - " RSPECREV.modifier_id, RSPECREV.modification_ts, RSPECREV.name AS title, NH.parent_id"; - - $fields = is_null($fields) ? $fields2get : implode(',',$fields); - $sql = " /* $debugMsg */ " . - " SELECT {$fields} FROM {$this->tables['req_specs_revisions']} RSPECREV, " . - " {$this->tables['req_specs']} RSPEC, {$this->tables['nodes_hierarchy']} NH, " . - " {$this->tables['requirements']} REQ " . - " WHERE RSPECREV.parent_id=RSPEC.id " . - " AND NH.id=RSPEC.id AND REQ.srs_id = RSPEC.id " . - " AND RSPEC.testproject_id={$testproject_id} "; - - - if (!is_null($id)) - { - $sql .= " AND RSPEC.id=" . $id; - } - - $sql .= " GROUP BY RSPEC.id" ; - $sql .= " ORDER BY RSPEC.id,title"; - - $rs = is_null($access_key) ? $this->db->get_recordset($sql) - : $this->db->fetchRowsIntoMap($sql,$access_key); - return $rs; - } - - /** - * create a new System Requirements Specification - * - * @param string $title - * @param string $scope - * @param string $countReq - * @param numeric $testproject_id - * @param numeric $user_id - * @param string $type - * - * @author Martin Havlat - * - * rev: 20071106 - franciscom - changed return type - */ - function createReqSpec($testproject_id,$title, $scope, $countReq,$user_id,$type = 'n') - { - $ignore_case=1; - $result=array(); - - $result['status_ok'] = 0; - $result['msg'] = 'ko'; - $result['id'] = 0; - - $title=trim($title); - - $chk=$this->check_srs_title($testproject_id,$title,$ignore_case); - if ($chk['status_ok']) - { - $sql = "INSERT INTO {$this->tables['req_specs']} " . - " (testproject_id, title, scope, type, total_req, author_id, creation_ts) - VALUES (" . $testproject_id . ",'" . $this->db->prepare_string($title) . "','" . - $this->db->prepare_string($scope) . "','" . $this->db->prepare_string($type) . "','" . - $this->db->prepare_string($countReq) . "'," . $this->db->prepare_string($user_id) . ", " . - $this->db->db_now() . ")"; - - if (!$this->db->exec_query($sql)) - { - $result['msg']=lang_get('error_creating_req_spec'); - } - else - { - $result['id']=$this->db->insert_id($this->tables['req_specs']); - $result['status_ok'] = 1; - $result['msg'] = 'ok'; - } - } - else - { - $result['msg']=$chk['msg']; - } - return $result; - } - - - - /* - function: get_srs_by_title - get srs information using title as access key. - - args : tesproject_id - title: srs title - [ignore_case]: control case sensitive search. - default 0 -> case sensivite search - - returns: map. - key: srs id - value: srs info, map with folowing keys: - id - testproject_id - title - scope - total_req - type - author_id - creation_ts - modifier_id - modification_ts - */ - public function get_srs_by_title($testproject_id,$title,$ignore_case=0) - { - $output=null; - $title=trim($title); - - $sql = "SELECT * FROM req_specs "; - - if($ignore_case) - { - $sql .= " WHERE UPPER(title)='" . strtoupper($this->db->prepare_string($title)) . "'"; - } - else - { - $sql .= " WHERE title='" . $this->db->prepare_string($title) . "'"; - } - $sql .= " AND testproject_id={$testproject_id}"; - $output = $this->db->fetchRowsIntoMap($sql,'id'); - - return $output; - } - - - - /* - function: check_srs_title - Do checks on srs title, to understand if can be used. - - Checks: - 1. title is empty ? - 2. does already exist a srs with this title? - - args : tesproject_id - title: srs title - [ignore_case]: control case sensitive search. - default 0 -> case sensivite search - - returns: - - */ - function check_srs_title($testproject_id,$title,$ignore_case=0) - { - $ret['status_ok'] = 1; - $ret['msg'] = ''; - - $title = trim($title); - - if ($title == "") - { - $ret['status_ok'] = 0; - $ret['msg'] = lang_get("warning_empty_req_title"); - } - - if($ret['status_ok']) - { - $ret['msg'] = 'ok'; - $rs = $this->get_srs_by_title($testproject_id,$title,$ignore_case); - - if(!is_null($rs)) - { - $ret['msg'] = lang_get("warning_duplicate_req_title"); - $ret['status_ok'] = 0; - } - } - return $ret; - } -/* END REQUIREMENT RELATED */ -// ---------------------------------------------------------------------------------------- - - - /** - * Deletes all testproject related role assignments for a given testproject - * - * @param integer $tproject_id - * @return integer tl::OK on success, tl::ERROR else - **/ - function deleteUserRoles($tproject_id,$users=null,$opt=null) - { - $my['opt'] = array('auditlog' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - $query = " DELETE FROM {$this->tables['user_testproject_roles']} " . - " WHERE testproject_id = " . intval($tproject_id) ; - - if(!is_null($users)) - { - $query .= " AND user_id IN(" . implode(',',$users) . ")"; - } - - if ($this->db->exec_query($query) && $my['opt']['auditlog']) - { - $testProject = $this->get_by_id($tproject_id); - - if ($testProject) - { - if(is_null($users)) - { - logAuditEvent(TLS("audit_all_user_roles_removed_testproject",$testProject['name']), - "ASSIGN",$tproject_id,"testprojects"); - } - else - { - // TBD - } - } - return tl::OK; - } - - return tl::ERROR; - } - - /** - * Gets all testproject related role assignments - * - * @param integer $tproject_id - * @return array assoc array with keys take from the user_id column - **/ - function getUserRoleIDs($tproject_id) - { - $query = "SELECT user_id,role_id FROM {$this->tables['user_testproject_roles']} " . - "WHERE testproject_id = {$tproject_id}"; - $roles = $this->db->fetchRowsIntoMap($query,'user_id'); - - return $roles; - } - - /** - * Inserts a testproject related role for a given user - * - * @param integer $userID the id of the user - * @param integer $tproject_id - * @param integer $roleID the role id - * - * @return integer tl::OK on success, tl::ERROR else - **/ - function addUserRole($userID,$tproject_id,$roleID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $query = "/* debugMsg*/ INSERT INTO {$this->tables['user_testproject_roles']} " . - " (user_id,testproject_id,role_id) VALUES ({$userID},{$tproject_id},{$roleID})"; - if($this->db->exec_query($query)) - { - $testProject = $this->get_by_id($tproject_id); - $role = tlRole::getByID($this->db,$roleID,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); - $user = tlUser::getByID($this->db,$userID,tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); - if ($user && $testProject && $role) - { - logAuditEvent(TLS("audit_users_roles_added_testproject",$user->getDisplayName(), - $testProject['name'],$role->name),"ASSIGN",$tproject_id,"testprojects"); - } - unset($user); - unset($role); - unset($testProject); - return tl::OK; - } - return tl::ERROR; - } - - /** - * delete test project from system, deleting all dependent data: - * keywords, requirements, custom fields, testsuites, testplans, - * testcases, results, testproject related roles, - * - * @param integer $id test project id - * @return integer status - * - */ - function delete($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $ret['msg']='ok'; - $ret['status_ok']=1; - - $error = ''; - $reqspec_mgr = new requirement_spec_mgr($this->db); - - // get some info for audit - $info['name'] = ''; - if($this->auditCfg->logEnabled) - { - $info = $this->tree_manager->get_node_hierarchy_info($id); - $event = new stdClass(); - $event->message = TLS("audit_testproject_deleted",$info['name']); - $event->objectID = $id; - $event->objectType = 'testprojects'; - $event->source = $this->auditCfg->eventSource; - $event->logLevel = 'AUDIT'; - $event->code = 'DELETE'; - } - - // - // Notes on delete related to Foreing Keys - // All link tables has to be deleted first - // - // req_relations - // - // testplan_tcversions - // testplan_platforms - // object_keywords - // user_assignments - // builds - // milestones - // - // testplans - // keywords - // platforms - // attachtments - // testcases - // testsuites - // inventory - // - // testproject - $this->deleteKeywords($id,$info['name']); - $this->deleteAttachments($id); - - $reqSpecSet=$reqspec_mgr->get_all_id_in_testproject($id); - if( !is_null($reqSpecSet) && count($reqSpecSet) > 0 ) { - foreach($reqSpecSet as $reqSpec) { - $reqspec_mgr->delete_deep($reqSpec['id']); - } - } - - $tplanSet = $this->get_all_testplans($id); - if( !is_null($tplanSet) && count($tplanSet) > 0 ) { - $tplan_mgr = new testplan($this->db); - $items=array_keys($tplanSet); - foreach($items as $key) { - $tplan_mgr->delete($key); - } - } - - $platform_mgr = new tlPlatform($this->db,$id); - $platform_mgr->deleteByTestProject($id); - - $a_sql[] = array("/* $debugMsg */ UPDATE {$this->tables['users']} " . - " SET default_testproject_id = NULL " . - " WHERE default_testproject_id = {$id}", - 'info_resetting_default_project_fails'); - - - $inventory_mgr = new tlInventory($id,$this->db); - $invOpt = array('detailLevel' => 'minimun', 'accessKey' => 'id'); - $inventorySet = $inventory_mgr->getAll($invOpt); - if( !is_null($inventorySet) ) { - foreach($inventorySet as $key => $dummy) { - $inventory_mgr->deleteInventory($key); - } - } - - foreach ($a_sql as $oneSQL) - { - if (empty($error)) - { - $sql = $oneSQL[0]; - $result = $this->db->exec_query($sql); - if (!$result) - { - $error .= lang_get($oneSQL[1]); - } - } - } - - - if ($this->deleteUserRoles($id) < tl::OK) - { - $error .= lang_get('info_deleting_project_roles_fails'); - } - - $xSQL = array('testproject_issuetracker','testproject_codetracker', - 'testproject_reqmgrsystem'); - foreach($xSQL as $target) - { - $sql = "/* $debugMsg */ DELETE FROM " . $this->tables[$target] . - " WHERE testproject_id = " . intval($id); - $result = $this->db->exec_query($sql); - } - - // --------------------------------------------------------------------------------------- - // delete product itself and items directly related to it like: - // custom fields assignments - // custom fields values ( right now we are not using custom fields on test projects) - // attachments - if (empty($error)) - { - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_testprojects']} WHERE testproject_id = {$id} "; - $this->db->exec_query($sql); - - $sql = "/* $debugMsg */ DELETE FROM {$this->object_table} WHERE id = {$id}"; - - $result = $this->db->exec_query($sql); - if ($result) - { - $tproject_id_on_session = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : $id; - if ($id == $tproject_id_on_session) - { - $this->setSessionProject(null); - } - } - else - { - $error .= lang_get('info_product_delete_fails'); - } - } - - if (empty($error)) - { - // Delete test project with requirements defined crashed with memory exhausted - $this->tree_manager->delete_subtree_objects($id,$id,'',array('testcase' => 'exclude_tcversion_nodes')); - $sql = "/* $debugMsg */ " . - " DELETE FROM {$this->tables['nodes_hierarchy']} " . - " WHERE id = {$id} AND node_type_id=" . - $this->tree_manager->node_descr_id['testproject']; - - $this->db->exec_query($sql); - - if($this->auditCfg->logEnabled) - { - logEvent($event); - } - } - - if( !empty($error) ) - { - $ret['msg']=$error; - $ret['status_ok']=0; - } - - return $ret; - } - - -/* - function: get_all_testcases_id - All testproject testcases node id. - - args :idList: comma-separated list of IDs (should be the projectID, but could - also be an arbitrary suiteID - - returns: array with testcases node id in parameter tcIDs. - null is nothing found - -*/ - function get_all_testcases_id($idList,&$tcIDs,$options = null) - { - static $tcNodeTypeID; - static $tsuiteNodeTypeID; - static $debugMsg; - if (!$tcNodeTypeID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $tcNodeTypeID = $this->tree_manager->node_descr_id['testcase']; - $tsuiteNodeTypeID = $this->tree_manager->node_descr_id['testsuite']; - } - - $my = array(); - $my['options'] = array('output' => 'just_id'); - $my['options'] = array_merge($my['options'], (array)$options); - - switch($my['options']['output']) - { - case 'external_id': - $use_array = true; - break; - - case 'just_id': - default: - $use_array = false; - break; - } - - $sql = "/* $debugMsg */ SELECT id,node_type_id from {$this->tables['nodes_hierarchy']} " . - " WHERE parent_id IN ({$idList})"; - $sql .= " AND node_type_id IN ({$tcNodeTypeID},{$tsuiteNodeTypeID}) "; - - $result = $this->db->exec_query($sql); - if ($result) - { - $suiteIDs = array(); - while($row = $this->db->fetch_array($result)) - { - if ($row['node_type_id'] == $tcNodeTypeID) - { - if( $use_array ) - { - $sql = " SELECT DISTINCT NH.parent_id, TCV.tc_external_id " . - " FROM {$this->tables['nodes_hierarchy']} NH " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH.id " . - " WHERE NH.parent_id = {$row['id']} "; - - $rs = $this->db->fetchRowsIntoMap($sql,'parent_id'); - $tcIDs[$row['id']] = $rs[$row['id']]['tc_external_id']; - } - else - { - $tcIDs[] = $row['id']; - } - } - else - { - $suiteIDs[] = $row['id']; - } - } - if (sizeof($suiteIDs)) - { - $suiteIDs = implode(",",$suiteIDs); - $this->get_all_testcases_id($suiteIDs,$tcIDs,$options); - } - } - } - - -/* - function: DEPRECATED_get_keywords_tcases - testproject keywords (with related testcase node id), - that are used on testcases. - - args :testproject_id - [keyword_id]= 0 -> no filter - <> 0 -> look only for this keyword - can be an array. - - - - returns: map: key: testcase_id - value: map - key: keyword_id - value: testcase_id,keyword_id,keyword - - Example: - [24] => Array ( [3] => Array( [testcase_id] => 24 - [keyword_id] => 3 - [keyword] => MaxFactor ) - - [2] => Array( [testcase_id] => 24 - [keyword_id] => 2 - [keyword] => Terminator ) ) - -@internal revisions: - 20100929 - asimon - BUGID 3814: fixed keyword filtering with "and" selected as type -*/ -function DEPRECATED_get_keywords_tcases($testproject_id, $keyword_id=0, $keyword_filter_type='Or') -{ - $keyword_filter= '' ; - $subquery=''; - - if( is_array($keyword_id) ) - { - $keyword_filter = " AND keyword_id IN (" . implode(',',$keyword_id) . ")"; - if($keyword_filter_type == 'And') { - $subquery = "AND testcase_id IN (" . - " SELECT FOXDOG.testcase_id FROM - ( SELECT COUNT(testcase_id) AS HITS,testcase_id - FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']} - WHERE keyword_id = K.id - AND testproject_id = {$testproject_id} - {$keyword_filter} - GROUP BY testcase_id ) AS FOXDOG " . - " WHERE FOXDOG.HITS=" . count($keyword_id) . ")"; - - $keyword_filter =''; - } - } - else if( $keyword_id > 0 ) - { - $keyword_filter = " AND keyword_id = {$keyword_id} "; - } - - $map_keywords = null; - $sql = " SELECT testcase_id,keyword_id,keyword - FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']} - WHERE keyword_id = K.id - AND testproject_id = {$testproject_id} - {$keyword_filter} {$subquery} - ORDER BY keyword ASC "; - - $map_keywords = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','keyword_id'); - - return($map_keywords); -} //end function - - -/** - * - */ -function getKeywordsLatestTCV($tproject_id, $keyword_id=0, $kwFilterType='Or') { - - $kwFilter= '' ; - $subquery=''; - $ltcvJoin = " JOIN {$this->views['latest_tcase_version_id']} LTCV - ON LTCV.tcversion_id = TK.tcversion_id "; - - if( is_array($keyword_id) ) { - $kwFilter = " AND keyword_id IN (" . implode(',',$keyword_id) . ")"; - if($kwFilterType == 'And') { - $ltcvJoin = " "; - $sqlCount = " /* SQL COUNT */ " . - " SELECT COUNT(TK.tcversion_id) AS HITS,TK.tcversion_id - FROM {$this->tables['keywords']} K - JOIN {$this->tables['testcase_keywords']} TK - ON keyword_id = K.id - - JOIN {$this->views['latest_tcase_version_id']} LTCV - ON LTCV.tcversion_id = TK.tcversion_id - - WHERE testproject_id = {$tproject_id} - {$kwFilter} - GROUP BY TK.tcversion_id "; - - $subquery = " AND tcversion_id IN (" . - " SELECT FOXDOG.tcversion_id FROM - ( $sqlCount ) AS FOXDOG " . - " WHERE FOXDOG.HITS=" . count($keyword_id) . ")"; - $kwFilter =''; - } - } - else if( $keyword_id > 0 ) { - $kwFilter = " AND keyword_id = {$keyword_id} "; - } - - $items = null; - $sql = " SELECT TK.testcase_id,TK.keyword_id,K.keyword - FROM {$this->tables['keywords']} K - JOIN {$this->tables['testcase_keywords']} TK - ON TK.keyword_id = K.id - {$ltcvJoin} - WHERE K.testproject_id = {$tproject_id} - {$kwFilter} {$subquery} - ORDER BY keyword ASC "; - - $items = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','keyword_id'); - - return $items; -} //end function - -/** - * - * 20200117 - * it seems I've duplicated code - * designed to be used by - * @used-by specview.php - */ -function XXXgetPlatformsLatestTCV($tproject_id, $platform_id=0, $filterType='Or') { - - $platFilter= '' ; - $subquery=''; - $ltcvJoin = " JOIN {$this->views['latest_tcase_version_id']} LTCV - ON LTCV.tcversion_id = TK.tcversion_id "; - - if( is_array($platform_id) ) { - $platFilter = " AND platform_id IN (" . implode(',',$platform_id) . ")"; - if($filterType == 'And') { - $ltcvJoin = " "; - $sqlCount = " /* SQL COUNT */ " . - " SELECT COUNT(TK.tcversion_id) AS HITS,TPL.tcversion_id - FROM {$this->tables['platforms']} K - JOIN {$this->tables['testcase_platforms']} TPL - ON platform_id = PL.id - - JOIN {$this->views['latest_tcase_version_id']} LTCV - ON LTCV.tcversion_id = TPL.tcversion_id - - WHERE testproject_id = {$tproject_id} - {$platFilter} - GROUP BY TPL.tcversion_id "; - - $subquery = " AND tcversion_id IN (" . - " SELECT FOXDOG.tcversion_id FROM - ( $sqlCount ) AS FOXDOG " . - " WHERE FOXDOG.HITS=" . count($platform_id) . ")"; - $platFilter =''; - } - } - else if( $platform_id > 0 ) { - $platFilter = " AND platform_id = {$platform_id} "; - } - - $items = null; - $sql = " SELECT TPL.testcase_id,TPL.keyword_id,PL.name - FROM {$this->tables['platforms']} K - JOIN {$this->tables['testcase_platforms']} TPL - ON TPL.platforms = PL.id - {$ltcvJoin} - WHERE PL.testproject_id = {$tproject_id} - {$platFilter} {$subquery} - ORDER BY name ASC "; - - $items = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','platform_id'); - - return $items; -} //end function - - - -/* - function: get_all_testplans - - args : $testproject_id - - [$filters]: optional map, with optional keys - [$get_tp_without_tproject_id] - used just for backward compatibility (TL 1.5) - default: 0 -> 1.6 and up behaviour - - [$plan_status] - default: null -> no filter on test plan status - 1 -> active test plans - 0 -> inactive test plans - - [$exclude_tplans]: null -> do not apply exclusion - id -> test plan id to exclude - - [options]: - - returns: - -*/ -function get_all_testplans($id,$filters=null,$options=null) { - - $my['options'] = array('fields2get' => - 'NH.id,NH.name,notes,active, - is_public,testproject_id,api_key', - 'outputType' => null); - $my['options'] = array_merge($my['options'], (array)$options); - - $forHMLSelect = false; - if( !is_null($my['options']['outputType']) && $my['options']['outputType'] == 'forHMLSelect') { - $forHMLSelect = true; - $my['options']['fields2get'] = 'NH.id,NH.name'; - } - - $sql = " SELECT {$my['options']['fields2get']} " . - " FROM {$this->tables['nodes_hierarchy']} NH,{$this->tables['testplans']} TPLAN"; - - $where = " WHERE NH.id=TPLAN.id AND (testproject_id = " . - $this->db->prepare_int($id) . " "; - if( !is_null($filters) ) { - $key2check=array('get_tp_without_tproject_id' => 0, 'plan_status' => null,'tplan2exclude' => null); - - foreach($key2check as $varname => $defValue) { - $$varname=isset($filters[$varname]) ? $filters[$varname] : $defValue; - } - - $where .= " ) "; - - if(!is_null($plan_status)) { - $my_active = to_boolean($plan_status); - $where .= " AND active = " . $my_active; - } - - if(!is_null($tplan2exclude)) { - $where .= " AND TPLAN.id != {$tplan2exclude} "; - } - } else { - $where .= ")"; - } - - $sql .= $where . " ORDER BY name"; - if( $forHMLSelect ) { - $map = $this->db->fetchColumnsIntoMap($sql,'id','name'); - } else { - $map = $this->db->fetchRowsIntoMap($sql,'id'); - } - - return $map; -} - - -/* - function: check_tplan_name_existence - - args : - tproject_id: - tplan_id: - [case_sensitive]: 1-> do case sensitive search - default: 0 - - returns: 1 -> tplan name exists - - -*/ -function check_tplan_name_existence($tproject_id,$tplan_name,$case_sensitive=0) -{ - $sql = " SELECT NH.id, NH.name, testproject_id " . - " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['testplans']} testplans " . - " WHERE NH.id=testplans.id " . - " AND testproject_id = {$tproject_id} "; - - if($case_sensitive) - { - $sql .= " AND NH.name="; - } - else - { - $tplan_name=strtoupper($tplan_name); - $sql .= " AND UPPER(NH.name)="; - } - $sql .= "'" . $this->db->prepare_string($tplan_name) . "'"; - $result = $this->db->exec_query($sql); - $status= $this->db->num_rows($result) ? 1 : 0; - - return $status; -} - - - /* - function: gen_combo_first_level_test_suites - create array with test suite names - - args : id: testproject_id - [mode] - - returns: - array, every element is a map - - rev : - 20070219 - franciscom - fixed bug when there are no children - -*/ -function get_first_level_test_suites($tproject_id,$mode='simple',$opt=null) -{ - $fl=$this->tree_manager->get_children($tproject_id, - array( 'testcase', 'exclude_me', - 'testplan' => 'exclude_me', - 'requirement_spec' => 'exclude_me' ),$opt); - switch ($mode) - { - case 'simple': - break; - - case 'smarty_html_options': - if( !is_null($fl) && count($fl) > 0) - { - foreach($fl as $idx => $map) - { - $dummy[$map['id']]=$map['name']; - } - $fl=null; - $fl=$dummy; - } - break; - } - return($fl); -} - - - -/** - * getTCasesLinkedToAnyTPlan - * - * for target test project id ($id) get test case id of - * every test case that has been assigned at least to one of all test plans - * belonging to test project. - * - * @param int $id test project id - * - */ -function getTCasesLinkedToAnyTPlan($id) -{ - $tplanNodeType = $this->tree_manager->node_descr_id['testplan']; - - // len of lines must be <= 100/110 as stated on development standard guide. - $sql = " SELECT DISTINCT NHTCV.parent_id AS testcase_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV " . - " ON NHTCV.id = TPTCV.tcversion_id "; - - // get testplan id for target test�project, to get test case versions linked to testplan. - $sql .= " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN " . - " ON TPTCV.testplan_id = NHTPLAN.id " . - " WHERE NHTPLAN.node_type_id = {$tplanNodeType} AND NHTPLAN.parent_id = " . intval($id); - $rs = $this->db->fetchRowsIntoMap($sql,'testcase_id'); - - return $rs; -} - - -/** - * getFreeTestCases - * - * - * @param int $id test project id - * @param $options for future uses. - */ -function getFreeTestCases($id,$options=null) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $retval['items']=null; - $retval['allfree']=false; - - $all=array(); - $this->get_all_testcases_id($id,$all); - $linked=array(); - $free=null; - if(!is_null($all)) - { - $all=array_flip($all); - $linked=$this->getTCasesLinkedToAnyTPlan($id); - $retval['allfree']=is_null($linked); - $free=$retval['allfree'] ? $all : array_diff_key($all,$linked); - } - - if( !is_null($free) && count($free) > 0) - { - $in_clause=implode(',',array_keys($free)); - $sql = " /* $debugMsg */ " . - " SELECT MAX(TCV.version) AS version, TCV.tc_external_id, " . - " TCV.importance AS importance, NHTCV.parent_id AS id, NHTC.name " . - " FROM {$this->tables['tcversions']} TCV " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TCV.id " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - " WHERE NHTCV.parent_id IN ({$in_clause}) " . - " GROUP BY NHTC.name,NHTCV.parent_id,TCV.tc_external_id,TCV.importance " . - " ORDER BY NHTCV.parent_id"; - $retval['items']=$this->db->fetchRowsIntoMap($sql,'id'); - } - - - return $retval; -} - - -// ------------------------------------------------------------------------------- -// Custom field related methods -// ------------------------------------------------------------------------------- -/* - function: get_linked_custom_fields - Get custom fields that has been linked to testproject. - Search can be narrowed by: - node type - node id - - Important: - custom fields id will be sorted based on the sequence number - that can be specified at User Interface (UI) level, while - linking is done. - - args : id: testproject id - [node_type]: default: null -> no filter - verbose string that identifies a node type. - (see tree class, method get_available_node_types). - Example: - You want linked custom fields , but can be used - only on testcase -> 'testcase'. - - returns: map. - key: custom field id - value: map (custom field definition) with following keys - - id (custom field id) - name - label - type - possible_values - default_value - valid_regexp - length_min - length_max - show_on_design - enable_on_design - show_on_execution - enable_on_execution - display_order - - -*/ -function get_linked_custom_fields($id,$node_type=null,$access_key='id') -{ - $additional_table=""; - $additional_join=""; - - if( !is_null($node_type) ) - { - $hash_descr_id = $this->tree_manager->get_available_node_types(); - $node_type_id=$hash_descr_id[$node_type]; - - $additional_table=",{$this->tables['cfield_node_types']} CFNT "; - $additional_join=" AND CFNT.field_id=CF.id AND CFNT.node_type_id={$node_type_id} "; - } - - $sql="SELECT CF.*,CFTP.display_order " . - " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_testprojects']} CFTP " . - $additional_table . - " WHERE CF.id=CFTP.field_id " . - " AND CFTP.testproject_id={$id} " . - $additional_join . - " ORDER BY CFTP.display_order"; - $map = $this->db->fetchRowsIntoMap($sql,$access_key); - return($map); -} - - - -/* -function: copy_as - creates a new test project using an existent one as source. - - -args: id: source testproject id - new_id: destination - [new_name]: default null. - != null => set this as the new name - - [copy_options]: default null - null: do a deep copy => copy following child elements: - test plans - builds - linked tcversions - milestones - user_roles - priorities, - platforms - execution assignment. - - != null, a map with keys that controls what child elements to copy - - -returns: N/A - - -*/ -function copy_as($id,$new_id,$user_id,$new_name=null,$options=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('copy_requirements' => 1, - 'copy_user_roles' => 1,'copy_platforms' => 1); - $my['options'] = array_merge($my['options'], (array)$options); - - // get source test project general info - $rs_source = $this->get_by_id($id); - - if(!is_null($new_name)) { - $sql="/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . - "SET name='" . $this->db->prepare_string(trim($new_name)) . "' " . - "WHERE id={$new_id}"; - $this->db->exec_query($sql); - } - - // Copy elements that can be used by other elements - // Custom Field assignments - $this->copy_cfields_assignments($id,$new_id); - - // Keywords - $oldNewMappings['keywords'] = $this->copy_keywords($id,$new_id); - - // Platforms - $oldNewMappings['platforms'] = $this->copy_platforms($id,$new_id); - - // Requirements - if( $my['options']['copy_requirements'] ) { - list($oldNewMappings['requirements'],$onReqSet) = - $this->copy_requirements($id,$new_id,$user_id); - - // need to copy relations between requirements - $rel = null; - foreach ($oldNewMappings['requirements'] as $erek) { - foreach ($erek['req'] as $okey => $nkey) { - $sql = "/* $debugMsg */ SELECT id, source_id, destination_id," . - " relation_type, author_id, creation_ts " . - " FROM {$this->tables['req_relations']} " . - " WHERE source_id=$okey OR destination_id=$okey "; - $rel[$okey] = $this->db->get_recordset($sql); - } - } - - if(!is_null($rel)) { - $totti = $this->db->db_now(); - foreach($rel as $okey => $ir) { - if(!is_null($ir)) { - foreach ($ir as $rval) { - if( isset($done[$rval['id']]) ) { - continue; - } - - $done[$rval['id']] = $rval['id']; - $sql = "/* $debugMsg */ - INSERT INTO {$this->tables['req_relations']} " . - " (source_id, destination_id, relation_type, author_id, creation_ts) " . - " values (" . - $onReqSet[$rval['source_id']] . "," . - $onReqSet[$rval['destination_id']] . "," . - $rval['relation_type'] . "," . $rval['author_id'] . "," . - "$totti)"; - $this->db->exec_query($sql); - } - } - } - } - } - - // need to get subtree and create a new one - $filters = array(); - $filters['exclude_node_types'] = array('testplan' => 'exclude_me','requirement_spec' => 'exclude_me'); - $filters['exclude_children_of'] = array('testcase' => 'exclude_me', 'requirement' => 'exclude_me', - 'testcase_step' => 'exclude_me'); - - $elements = $this->tree_manager->get_children($id,$filters['exclude_node_types']); - - // Copy Test Specification - $item_mgr['testsuites'] = new testsuite($this->db); - $copyTSuiteOpt = array(); - $copyTSuiteOpt['preserve_external_id'] = true; - $copyTSuiteOpt['copyKeywords'] = 1; - - // Attention: - // copyRequirements really means copy requirement to testcase assignments - $copyTSuiteOpt['copyRequirements'] = $my['options']['copy_requirements']; - - $oldNewMappings['test_spec'] = array(); - foreach($elements as $piece) { - $op = $item_mgr['testsuites']->copy_to($piece['id'],$new_id,$user_id,$copyTSuiteOpt,$oldNewMappings); - $oldNewMappings['test_spec'] += $op['mappings']; - } - - // Copy Test Plans and all related information - $this->copy_testplans($id,$new_id,$user_id,$oldNewMappings); - - $this->copy_user_roles($id,$new_id); - - // need to understand if we need to change this and - // PRESERVE External Test case ID - // - // When copying a project, external TC ID is not preserved - // need to update external test case id numerator - $sql = "/* $debugMsg */ UPDATE {$this->object_table} " . - " SET tc_counter = {$rs_source['tc_counter']} " . - " WHERE id = {$new_id}"; - $recordset = $this->db->exec_query($sql); - - -} // end function copy_as - - -/** - * function to get an array with all requirement IDs in testproject - * - * @param string $IDList commaseparated list of Container-IDs - can be testproject ID or reqspec IDs - * @return array $reqIDs result IDs - * - * @internal revisions: - * 20100310 - asimon - removed recursion logic - */ -public function get_all_requirement_ids($IDList) { - - $coupleTypes = array(); - $coupleTypes['target'] = $this->tree_manager->node_descr_id['requirement']; - $coupleTypes['container'] = $this->tree_manager->node_descr_id['requirement_spec']; - - $reqIDs = array(); - $this->tree_manager->getAllItemsID($IDList,$reqIDs,$coupleTypes); - - return $reqIDs; -} - - -/** - * uses get_all_requirements_ids() to count all requirements in testproject - * - * @param integer $tp_id ID of testproject - * @return integer count of requirements in given testproject - */ -public function count_all_requirements($tp_id) { - return count($this->get_all_requirement_ids($tp_id)); -} - -/** - * Copy user roles to a new Test Project - * - * @param int $source_id original Test Project identificator - * @param int $target_id new Test Project identificator - */ -private function copy_user_roles($source_id, $target_id) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ SELECT * FROM {$this->tables['user_testproject_roles']} " . - "WHERE testproject_id={$source_id} "; - $rs=$this->db->get_recordset($sql); - - if(!is_null($rs)) - { - foreach($rs as $elem) - { - $sql="/* $debugMsg */ INSERT INTO {$this->tables['user_testproject_roles']} " . - "(testproject_id,user_id,role_id) " . - "VALUES({$target_id}," . $elem['user_id'] ."," . $elem['role_id'] . ")"; - $this->db->exec_query($sql); - } - } -} - - -/** - * Copy platforms - * - * @param int $source_id original Test Project identificator - * @param int $target_id new Test Project identificator - */ -private function copy_platforms($source_id, $target_id) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $platform_mgr = new tlPlatform($this->db,$source_id); - $old_new = null; - - $platformSet = $platform_mgr->getAll(); - - if( !is_null($platformSet) ) - { - $platform_mgr->setTestProjectID($target_id); - foreach($platformSet as $platform) - { - $op = $platform_mgr->create($platform['name'],$platform['notes']); - $old_new[$platform['id']] = $op['id']; - } - } - return $old_new; -} - - -/** - * Copy platforms - * - * @param int $source_id original Test Project identificator - * @param int $target_id new Test Project identificator - */ -private function copy_keywords($source_id, $target_id) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $old_new = null; - $sql = "/* $debugMsg */ SELECT * FROM {$this->tables['keywords']} " . - " WHERE testproject_id = {$source_id}"; - - $itemSet = $this->db->fetchRowsIntoMap($sql,'id'); - if( !is_null($itemSet) ) { - foreach($itemSet as $item) { - $op = $this->addKeyword($target_id,$item['keyword'],$item['notes']); - $old_new[$item['id']] = $op['id']; - } - } - return $old_new; -} - - - - - -/** - * - * - */ -private function copy_cfields_assignments($source_id, $target_id) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT field_id FROM {$this->tables['cfield_testprojects']} " . - " WHERE testproject_id = {$source_id}"; - $row_set = $this->db->fetchRowsIntoMap($sql,'field_id'); - if( !is_null($row_set) ) - { - $cfield_set = array_keys($row_set); - $this->cfield_mgr->link_to_testproject($target_id,$cfield_set); - } -} - - -/** - * - * - */ -private function copy_testplans($source_id,$target_id,$user_id,$mappings) -{ - static $tplanMgr; - - $tplanSet = $this->get_all_testplans($source_id); - if( !is_null($tplanSet) ) - { - $keySet = array_keys($tplanSet); - if( is_null($tplanMgr) ) - { - $tplanMgr = new testplan($this->db); - } - - foreach($keySet as $itemID) - { - $new_id = $tplanMgr->create($tplanSet[$itemID]['name'],$tplanSet[$itemID]['notes'], - $target_id,$tplanSet[$itemID]['active'],$tplanSet[$itemID]['is_public']); - - if( $new_id > 0 ) - { - // TICKET 5190: Copy Test projects - tester assignments to testplan+build are not copied - $tplanMgr->copy_as($itemID,$new_id,null,$target_id,$user_id,array('copy_assigned_to' => 1),$mappings); - } - } - - } -} - - -/** - * - * - */ -private function copy_requirements($source_id,$target_id,$user_id) { - $mappings = null; - $or = array(); - - // need to get subtree and create a new one - $filters = array(); - $filters['exclude_node_types'] = - array('testplan' => 'exclude','testcase' => 'exclude', - 'testsuite' => 'exclude','requirement' => 'exclude'); - - $elements = $this->tree_manager->get_children($source_id, - $filters['exclude_node_types']); - - if( !is_null($elements) ) { - $mappings = array(); - $reqSpecMgr = new requirement_spec_mgr($this->db); - - // Development Note - 20110817 - // why we choose to do not copy testcase_assignments ? - // Because due to order used to copy different items, - // when we ask to copy requirements WE DO NOT HAVE - // TEST CASES on new test project. - // - $options = array('copy_also' => - array('testcase_assignments' => false), - 'caller' => 'copy_testproject'); - - $rel = null; - foreach($elements as $piece) { - $op = $reqSpecMgr->copy_to($piece['id'],$target_id,$target_id,$user_id,$options); - - $mappings[] = $op['mappings']; - $or += $op['mappings']['req']; - } - } - - return array($mappings,$or); -} - - - - - - - - -/** - * getTestSpec - * - * get structure with Test suites and Test Cases - * Filters that act on test cases work on attributes that are common to all - * test cases versions: test case name - * - * Development Note: - * Due to the tree structure is not so easy to try to do as much as filter as - * possibile using SQL. - * - * - * @param int id test project ID - * @param mixed filters - * @param mixed options - * recursive true/false changes output format - * testcase_name filter in LIKE %string%, if will be case sensitive or not - * will depend of DBMS. - * - * - */ -function getTestSpec($id,$filters=null,$options=null) { - - $items = array(); - - $my['options'] = array('recursive' => false, - 'exclude_testcases' => false, - 'remove_empty_branches' => false); - - $my['filters'] = array('exclude_node_types' => $this->nt2exclude, - 'exclude_children_of' => $this->nt2exclude_children, - 'exclude_branches' => null, - 'testcase_name' => null, 'importance' => null, - 'testcase_id' => null, 'execution_type' => null, - 'status' => null, 'keywords' => null, - 'additionalWhereClause' => null, - 'platforms' => null); - - - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - - if( $my['options']['exclude_testcases'] ) { - $my['filters']['exclude_node_types']['testcase']='exclude me'; - } - - // transform some of our options/filters on something the 'worker' will understand - // when user has request filter by test case name, we do not want to display empty branches - // If we have choose any type of filter, we need to force remove empty test suites - // TICKET 4217: added filter for importance - if( !is_null($my['filters']['testcase_name']) || !is_null($my['filters']['testcase_id']) || - !is_null($my['filters']['execution_type']) || !is_null($my['filters']['exclude_branches']) || - !is_null($my['filters']['importance']) || $my['options']['remove_empty_branches'] ) - { - $my['options']['remove_empty_nodes_of_type'] = 'testsuite'; - } - - $method2call = $my['options']['recursive'] ? '_get_subtree_rec' : '_get_subtree'; - - // var_dump($method2call); - $qnum = $this->$method2call($id,$items,$my['filters'],$my['options']); - return $items; -} - - -/** - * - * @return - * - * @internal revisions - */ -function _get_subtree_rec($node_id,&$pnode,$filters = null, $options = null) { - static $qnum; - static $my; - static $exclude_branches; - static $exclude_children_of; - static $node_types; - static $tcaseFilter; - static $tcversionFilter; - static $childFilterOn; - static $staticSql; - static $inClause; - static $kwJoin; - - if (!$my) { - $qnum=0; - $node_types = array_flip($this->tree_manager->get_available_node_types()); - - $my['filters'] = array('exclude_children_of' => null,'exclude_branches' => null, - 'additionalWhereClause' => '', 'testcase_name' => null, - 'testcase_id' => null,'active_testcase' => false, - 'importance' => null, 'status' => null); - - $my['options'] = array('remove_empty_nodes_of_type' => null); - - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - $exclude_branches = $my['filters']['exclude_branches']; - $exclude_children_of = $my['filters']['exclude_children_of']; - - - $tcaseFilter['name'] = !is_null($my['filters']['testcase_name']); - $tcaseFilter['id'] = !is_null($my['filters']['testcase_id']); - - $tcaseFilter['is_active'] = !is_null($my['filters']['active_testcase']) && $my['filters']['active_testcase']; - $tcaseFilter['enabled'] = $tcaseFilter['name'] || $tcaseFilter['id'] || $tcaseFilter['is_active']; - - $actOnVersion = array('execution_type','importance','status', - 'keywords', 'platforms'); - foreach($actOnVersion as $ck) { - $tcversionFilter[$ck] = !is_null($my['filters'][$ck]); - } - - $tcversionFilter['enabled'] = false; - foreach($actOnVersion as $target) { - $tcversionFilter['enabled'] = $tcversionFilter['enabled'] || $tcversionFilter[$target]; - } - - - $childFilterOn = $tcaseFilter['enabled'] || $tcversionFilter['enabled']; - - if( !is_null($my['options']['remove_empty_nodes_of_type']) ) { - // this way I can manage code or description - if( !is_numeric($my['options']['remove_empty_nodes_of_type']) ) { - $my['options']['remove_empty_nodes_of_type'] = - $this->tree_manager->node_descr_id[$my['options']['remove_empty_nodes_of_type']]; - } - } - - // Create invariant sql sentences - $tfields = "NH.id, NH.parent_id, NH.name, NH.node_type_id, NH.node_order, '' AS external_id "; - $staticSql = " SELECT DISTINCT {$tfields} " . - " FROM {$this->tables['nodes_hierarchy']} NH "; - - // Generate IN Clauses - $inClause = array('status' => ' ', 'importance' => ' '); - - foreach($inClause as $tgf => $dummy) { - if( $tcversionFilter[$tgf] ) { - $inClause[$tgf] = - " TCV.$tgf IN (" . implode(',',$my['filters'][$tgf]) . ')'; - } - } - } - - $sql = $staticSql . " WHERE NH.parent_id = " . intval($node_id) . - " AND (" . - " NH.node_type_id = {$this->tree_manager->node_descr_id['testsuite']} " . - " OR (NH.node_type_id = {$this->tree_manager->node_descr_id['testcase']} "; - - if( $tcaseFilter['enabled'] ) { - foreach($tcaseFilter as $key => $apply) { - if( $apply ) { - switch($key) { - case 'name': - $safe4DB = $this->db->prepare_string($my['filters']['testcase_name']); - $sql .= " AND NH.name LIKE '%{$safe4DB}%' "; - break; - - case 'id': - $safe4DB = intval($my['filters']['testcase_id']); - $sql .= " AND NH.id = {$safe4DB} "; - break; - } - } - } - } - $sql .= " )) "; - $sql .= " ORDER BY NH.node_order,NH.id"; - - // Approach Change - get all - $rs = (array)$this->db->fetchRowsIntoMap($sql,'id'); - if( count($rs) == 0 ) { - return $qnum; - } - - // create list with test cases nodes - $tclist = null; - $ks = array_keys($rs); - foreach($ks as $ikey) { - if( $rs[$ikey]['node_type_id'] == $this->tree_manager->node_descr_id['testcase'] ) { - $tclist[$rs[$ikey]['id']] = $rs[$ikey]['id']; - } - } - if( !is_null($tclist) ) { - $filterOnTC = false; - - // 2018, where is the active check? - - // Can be replace with a view? - $glvn = " /* Get LATEST ACTIVE tcversion NUMBER */ " . - " SELECT MAX(TCVX.version) AS version, NHTCX.parent_id AS tc_id " . - " FROM {$this->tables['tcversions']} TCVX " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCX " . - " ON NHTCX.id = TCVX.id AND TCVX.active = 1 " . - " WHERE NHTCX.parent_id IN (" . implode($tclist,',') . ")" . - " GROUP BY NHTCX.parent_id"; - - // 2018, again where is the active check? - $ssx = " /* Get LATEST ACTIVE tcversion MAIN ATTRIBUTES */ " . - " SELECT TCV.id AS tcversion_id, TCV.tc_external_id AS external_id, SQ.tc_id " . - " FROM {$this->tables['nodes_hierarchy']} NHTCV " . - " JOIN ( $glvn ) SQ " . - " ON NHTCV.parent_id = SQ.tc_id " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON NHTCV.id = TCV.id "; - - // 2018 - $where = " WHERE SQ.version = TCV.version "; - - // We can add here keyword filtering if exist ? - if( $tcversionFilter['enabled'] || $tcaseFilter['is_active'] ) { - $addAnd = false; - if ($tcversionFilter['importance'] || $tcversionFilter['execution_type'] || - $tcversionFilter['status'] ) { - $where .= " AND "; - } - - if( $tcversionFilter['importance'] ) { - $where .= $inClause['importance']; - $filterOnTC = true; - $addAnd = true; - } - - if( $addAnd && $tcversionFilter['execution_type']) { - $where .= " AND "; - } - - if( $tcversionFilter['execution_type'] ) { - $where .= " TCV.execution_type = " . $my['filters']['execution_type']; - $filterOnTC = true; - $addAnd = true; - } - - if( $addAnd && $tcversionFilter['status']) { - $where .= " AND "; - } - - if( $tcversionFilter['status'] ) { - $where .= $inClause['status']; - $filterOnTC = true; - $addAnd = true; - } - - /* - if( $addAnd && $tcversionFilter['keywords']) { - $where .= " AND "; - } - - if( $tcversionFilter['keywords'] ) { - $kwJoin = ''; - } - */ - } - - // $ssx .= $kwJoin . $where; - $ssx .= $where; - - $highlander = $this->db->fetchRowsIntoMap($ssx,'tc_id'); - if( $filterOnTC ) { - $ky = !is_null($highlander) ? array_diff_key($tclist,$highlander) : $tclist; - if( count($ky) > 0 ) { - foreach($ky as $tcase) { - unset($rs[$tcase]); - } - } - } - - } - - foreach($rs as $row) { - if(!isset($exclude_branches[$row['id']])) { - $node = $row + array('node_table' => $this->tree_manager->node_tables_by['id'][$row['node_type_id']]); - $node['childNodes'] = null; - - if($node['node_table'] == 'testcases') { - $node['leaf'] = true; - $node['external_id'] = isset($highlander[$row['id']]) ? $highlander[$row['id']]['external_id'] : null; - } - - // why we use exclude_children_of ? - // 1. Sometimes we don't want the children if the parent is a testcase, - // due to the version management - // - if(!isset($exclude_children_of[$node_types[$row['node_type_id']]])) { - // Keep walking (Johny Walker Whisky) - $this->_get_subtree_rec($row['id'],$node,$my['filters'],$my['options']); - } - - - // Have added this logic, because when export test plan will be developed - // having a test spec tree where test suites that do not contribute to test plan - // are pruned/removed is very important, to avoid additional processing - // - // If node has no childNodes, we check if this kind of node without children - // can be removed. - // - $doRemove = is_null($node['childNodes']) && - ($node['node_type_id'] == $my['options']['remove_empty_nodes_of_type']); - if(!$doRemove) { - $pnode['childNodes'][] = $node; - } - - } // if(!isset($exclude_branches[$rowID])) - } //while - - return $qnum; -} - + 'exclude_me', + 'requirement_spec' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + + private $nt2exclude_children = array( + 'testcase' => 'exclude_my_children', + 'requirement_spec' => 'exclude_my_children' + ); + + private $debugMsg; + + private $tmp_dir; + + private $node_types_descr_id; + + private $my_node_type; + + private $cfg; + + /** + * Class constructor + * + * @param + * resource &$db reference to database handler + */ + public function __construct(&$db) + { + $this->tmp_dir = config_get('temp_dir'); + + $this->db = &$db; + $this->tree_manager = new tree($this->db); + $this->cfield_mgr = new cfield_mgr($this->db); + $this->debugMsg = 'Class:' . __CLASS__ . ' - Method: '; + tlObjectWithAttachments::__construct($this->db, 'nodes_hierarchy'); + $this->object_table = $this->tables['testprojects']; + + $this->node_types_descr_id = &$this->tree_manager->node_descr_id; + $this->my_node_type = $this->tree_manager->node_descr_id['testproject']; + + $this->cfg = new stdClass(); + $this->cfg->keywords = config_get('keywords'); + } + + /** + * Create a new Test project + * + * @param string $name + * Name of project + * @param string $color + * value according to CSS color definition + * @param string $notes + * project description (HTML text) + * @param array $options + * project features/options + * bolean keys: inventoryEnabled, automationEnabled, + * testPriorityEnabled, requirementsEnabled + * @param boolean $active + * [1,0] optional + * @param string $tcasePrefix + * [''] + * @param boolean $is_public + * [1,0] optional + * + * @return integer test project id or 0 (if fails) + * + * @internal revisions + * + */ + public function create($item, $opt = null) + { + $my['opt'] = array( + 'doChecks' => false, + 'setSessionProject' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $serOptions = serialize($item->options); + + try { + $tcPrefix = $this->formatTcPrefix($item->prefix); // will truncate prefix is len() > limit + + // mandatory checks + if (strlen($item->name) == 0) { + throw new Exception('Empty name is not allowed'); + } + + if ($my['opt']['doChecks']) { + $check = $this->checkNameSintax($item->name); + if ($check['status_ok']) { + $check = $this->checkNameExistence($item->name); + } + if ($check['status_ok']) { + $check = $this->checkTestCasePrefixExistence($tcPrefix); + } + + if (! $check['status_ok']) { + throw new Exception($check['msg']); + } + } + } catch (Exception $e) { + throw $e; // rethrow + } + + // Create API KEY 64 bit long + $api_key = md5(rand()) . md5(rand()); + + // Create Node and get the id + $id = $this->tree_manager->new_root_node($item->name); + $sql = " INSERT INTO {$this->object_table} (id,color," . + " options,notes,active,is_public,prefix,api_key) " . " VALUES (" . + $id . ", '" . $this->db->prepare_string($item->color) . "','" . + $serOptions . "','" . $this->db->prepare_string($item->notes) . "'," . + $item->active . "," . $item->is_public . ",'" . + $this->db->prepare_string($tcPrefix) . "','" . + $this->db->prepare_string($api_key) . "')"; + $result = $this->db->exec_query($sql); + + $evt = new stdClass(); + $evt->message = TLS("audit_testproject_created", $item->name); + $evt->code = "CREATE"; + $evt->source = $this->auditCfg->eventSource; + $evt->objectType = 'testprojects'; + + if ($result) { + // set project to session if not defined (the first project) or update the current + if (! isset($_SESSION['testprojectID']) && + $my['opt']['setSessionProject']) { + $this->setSessionProject($id); + } + $evt->logLevel = 'AUDIT'; + + // Send Event + $ctx = array( + 'id' => $id, + 'name' => $item->name, + 'prefix' => $tcPrefix + ); + event_signal('EVENT_TEST_PROJECT_CREATE', $ctx); + } else { + $id = 0; + $evt->logLevel = 'ERROR'; + } + + $evt->objectID = $id; + + logEvent($evt); + + return $id; + } + + /** + * Update Test project data in DB and (if applicable) current session data + * + * @param integer $id + * project Identifier + * @param string $name + * Name of project + * @param string $color + * value according to CSS color definition + * @param string $notes + * project description (HTML text) + * @param array $options + * project features/options + * bolean keys: inventoryEnabled, automationEnabled, + * testPriorityEnabled, requirementsEnabled + * + * @return boolean result of DB update + * + * @internal + * + */ + public function update($id, $name, $color, $notes, $options, $active = null, + $tcasePrefix = null, $is_public = null) + { + $status_ok = 1; + $log_msg = 'Test project ' . $name . ' update: Ok.'; + $log_level = 'INFO'; + $safeID = intval($id); + + $add_upd = ''; + if (! is_null($active)) { + $add_upd .= ',active=' . (intval($active) > 0 ? 1 : 0); + } + + if (! is_null($is_public)) { + $add_upd .= ',is_public=' . (intval($is_public) > 0 ? 1 : 0); + } + + if (! is_null($tcasePrefix)) { + $tcprefix = $this->formatTcPrefix($tcasePrefix); + $add_upd .= ",prefix='" . $this->db->prepare_string($tcprefix) . "'"; + } + $serOptions = serialize($options); + + $sql = " UPDATE {$this->object_table} SET color='" . + $this->db->prepare_string($color) . "', " . " options='" . + $serOptions . "', " . " notes='" . $this->db->prepare_string($notes) . + "' {$add_upd} " . " WHERE id=" . $safeID; + $result = $this->db->exec_query($sql); + + if ($result) { + // update related node + $sql = "UPDATE {$this->tables['nodes_hierarchy']} SET name='" . + $this->db->prepare_string($name) . "' WHERE id= {$safeID}"; + $result = $this->db->exec_query($sql); + } + + if ($result) { + // update session data + $this->setSessionProject($safeID); + + // Send Event + $ctx = array( + 'id' => $id, + 'name' => $name, + 'prefix' => $tcprefix + ); + event_signal('EVENT_TEST_PROJECT_UPDATE', $ctx); + } else { + $status_msg = 'Update FAILED!'; + $status_ok = 0; + $log_level = 'ERROR'; + $log_msg = $status_msg; + } + + tLog($log_msg, $log_level); + return $status_ok; + } + + /** + * Set session data related to a Test project + * + * @param integer $projectId + * Project ID; zero causes unset data + */ + public function setSessionProject($projectId) + { + $tproject_info = null; + + if ($projectId) { + $tproject_info = $this->get_by_id($projectId); + } + + if ($tproject_info) { + $_SESSION['testprojectID'] = $tproject_info['id']; + $_SESSION['testprojectName'] = $tproject_info['name']; + $_SESSION['testprojectColor'] = $tproject_info['color']; + $_SESSION['testprojectPrefix'] = $tproject_info['prefix']; + + $_SESSION['testprojectOptions'] = new stdClass(); + $_SESSION['testprojectOptions']->requirementsEnabled = isset( + $tproject_info['opt']->requirementsEnabled) ? $tproject_info['opt']->requirementsEnabled : 0; + $_SESSION['testprojectOptions']->testPriorityEnabled = isset( + $tproject_info['opt']->testPriorityEnabled) ? $tproject_info['opt']->testPriorityEnabled : 0; + $_SESSION['testprojectOptions']->automationEnabled = isset( + $tproject_info['opt']->automationEnabled) ? $tproject_info['opt']->automationEnabled : 0; + $_SESSION['testprojectOptions']->inventoryEnabled = isset( + $tproject_info['opt']->inventoryEnabled) ? $tproject_info['opt']->inventoryEnabled : 0; + + tLog( + "Test Project was activated: [" . $tproject_info['id'] . "]" . + $tproject_info['name'], 'INFO'); + } else { + if (isset($_SESSION['testprojectID'])) { + tLog( + "Test Project deactivated: [" . $_SESSION['testprojectID'] . + "] " . $_SESSION['testprojectName'], 'INFO'); + } + unset($_SESSION['testprojectID']); + unset($_SESSION['testprojectName']); + unset($_SESSION['testprojectColor']); + unset($_SESSION['testprojectOptions']); + unset($_SESSION['testprojectPrefix']); + } + } + + /** + * Unserialize project options + * + * @param array $recorset + * produced by getTestProject() + */ + protected function parseTestProjectRecordset(&$recordset) + { + if (! empty($recordset)) { + foreach ($recordset as $number => $row) { + $recordset[$number]['opt'] = unserialize($row['options']); + } + } else { + $recordset = null; + tLog('parseTestProjectRecordset: No project on query', 'DEBUG'); + } + } + + /** + * Get Test project data according to parameter with unique value + * + * @param string $condition + * (optional) additional SQL condition(s) + * @return array map with test project info; null if query fails + */ + protected function getTestProject($condition = null, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my = array( + 'options' => array( + 'output' => 'full' + ) + ); + $my['options'] = array_merge($my['options'], (array) $opt); + + $doParse = true; + $tprojCols = ' testprojects.* '; + + switch ($my['options']['output']) { + case 'existsByID': + $doParse = false; + $sql = "/* $debugMsg */ SELECT testprojects.id " . + " FROM {$this->object_table} testprojects " . " WHERE 1=1 "; + break; + + case 'existsByName': + $doParse = false; + $sql = "/* $debugMsg */ SELECT testprojects.id " . + " FROM {$this->object_table} testprojects, " . + " {$this->tables['nodes_hierarchy']} nodes_hierarchy" . + " WHERE testprojects.id = nodes_hierarchy.id " . + " AND nodes_hierarchy.node_type_id = " . + $this->tree_manager->node_descr_id['testproject']; + break; + + case 'name': + $doParse = false; + $tprojCols = 'testprojects.id'; + case 'full': + default: + $sql = "/* $debugMsg */ SELECT {$tprojCols}, nodes_hierarchy.name " . + " FROM {$this->object_table} testprojects, " . + " {$this->tables['nodes_hierarchy']} nodes_hierarchy" . + " WHERE testprojects.id = nodes_hierarchy.id " . + " AND nodes_hierarchy.node_type_id = " . + $this->tree_manager->node_descr_id['testproject']; + break; + } + if (! is_null($condition)) { + $sql .= " AND " . $condition; + } + + $rs = $this->db->get_recordset($sql); + if ($doParse) { + $this->parseTestProjectRecordset($rs); + } + return $rs; + } + + /** + * Get Test project data according to name + * + * @param string $name + * @param string $addClause + * (optional) additional SQL condition(s) + * + * @return array map with test project info; null if query fails + */ + public function get_by_name($name, $addClause = null, $opt = null) + { + $condition = "nodes_hierarchy.name='" . $this->db->prepare_string($name) . + "'"; + $condition .= is_null($addClause) ? '' : " AND {$addClause} "; + + return $this->getTestProject($condition); + } + + /** + * Get Test project data according to ID + * + * @param integer $id + * test project + * @return array map with test project info; null if query fails + */ + public function get_by_id($id, $opt = null) + { + $condition = "testprojects.id=" . intval($id); + $result = $this->getTestProject($condition, $opt); + return $result[0]; + } + + /** + * Get Test project data according to prefix + * + * @param string $prefix + * @param string $addClause + * optional additional SQL 'AND filter' clause + * + * @return array map with test project info; null if query fails + */ + public function get_by_prefix($prefix, $addClause = null) + { + $safe_prefix = $this->db->prepare_string($prefix); + $condition = "testprojects.prefix='{$safe_prefix}'"; + $condition .= is_null($addClause) ? '' : " AND {$addClause} "; + + $rs = $this->getTestProject($condition); + return $rs != null ? $rs[0] : null; + } + + /** + * Get Test project data according to APIKEY + * + * @param + * string 64 chars + * @return array map with test project info; null if query fails + */ + public function getByAPIKey($apiKey, $opt = null) + { + $condition = "testprojects.api_key='{$apiKey}'"; + $result = $this->getTestProject($condition, $opt); + return $result[0]; + } + + /* + * function: get_all + * get array of info for every test project + * without any kind of filter. + * Every array element contains an assoc array with test project info + * + * args:[order_by]: default " ORDER BY nodes_hierarchy.name " -> testproject name + * + * + */ + public function get_all($filters = null, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my = array( + 'filters' => '', + 'options' => '' + ); + + $my['filters'] = array( + 'active' => null + ); + $my['options'] = array( + 'order_by' => " ORDER BY nodes_hierarchy.name ", + 'access_key' => null, + 'output' => 'std' + ); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + if ($my['options']['output'] == 'count') { + $sql = "/* $debugMsg */ SELECT COUNT(testprojects.id) AS qty " . + " FROM {$this->object_table} testprojects"; + + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty']; + } + + // + $sql = "/* $debugMsg */ SELECT testprojects.*, nodes_hierarchy.name " . + " FROM {$this->object_table} testprojects, " . + " {$this->tables['nodes_hierarchy']} nodes_hierarchy " . + " WHERE testprojects.id = nodes_hierarchy.id "; + + if (! is_null($my['filters']['active'])) { + $sql .= " AND active=" . intval($my['filters']['active']) . " "; + } + + if (! is_null($my['options']['order_by'])) { + $sql .= $my['options']['order_by']; + } + + if (is_null($my['options']['access_key'])) { + $recordset = $this->db->get_recordset($sql); + $this->parseTestProjectRecordset($recordset); + } else { + $recordset = $this->db->fetchRowsIntoMap($sql, + $my['options']['access_key']); + if (! empty($recordset)) { + foreach ($recordset as $number => $row) { + $recordset[$number]['opt'] = unserialize($row['options']); + } + } + } + + return $recordset; + } + + /* + * function: get_accessible_for_user + * get list of testprojects, considering user roles. + * Remember that user has: + * 1. one default role, assigned when user was created + * 2. a different role can be assigned for every testproject. + * + * For users roles that has not rigth to modify testprojects + * only active testprojects are returned. + * + * args: + * user_id + * [output_type]: choose the output data structure. + * possible values: map, map_of_map + * map: key -> test project id + * value -> test project name + * + * map_of_map: key -> test project id + * value -> array ('name' => test project name, + * 'active' => active status) + * + * array_of_map: value -> array with all testproject table fields plus name. + * + * + * default: map + * [order_by]: default: ORDER BY name + * + */ + public function get_accessible_for_user($user_id, $opt = null, + $filters = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my = array(); + $my['opt'] = array( + 'output' => 'map', + 'order_by' => ' ORDER BY name ', + 'field_set' => 'full', + 'format' => 'std', + 'add_issuetracker' => false, + 'add_codetracker' => false, + 'add_reqmgrsystem' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + // key = field name + // value = array('op' => Domain ('=','like'), 'value' => the value) + $my['filters'] = array( + 'name' => null, + 'id' => null, + 'prefix' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $items = array(); + $safe_user_id = intval($user_id); + + // Get default/global role + $sql = "/* $debugMsg */ SELECT id,role_id FROM {$this->tables['users']} where id=" . + $safe_user_id; + $user_info = $this->db->get_recordset($sql); + $globalRoleID = intval($user_info[0]['role_id']); + + $itsql = ''; + $itf = ''; + if ($my['opt']['add_issuetracker']) { + $itsql = " LEFT OUTER JOIN {$this->tables['testproject_issuetracker']} AS TIT " . + " ON TIT.testproject_id = TPROJ.id " . + " LEFT OUTER JOIN {$this->tables['issuetrackers']} AS ITMD " . + " ON ITMD.id = TIT.issuetracker_id "; + $itf = ",ITMD.name AS itname,ITMD.type AS ittype"; + } + + $ctsql = ''; + $ctf = ''; + if ($my['opt']['add_codetracker']) { + $ctsql = " LEFT OUTER JOIN {$this->tables['testproject_codetracker']} AS TCT " . + " ON TCT.testproject_id = TPROJ.id " . + " LEFT OUTER JOIN {$this->tables['codetrackers']} AS CTMD " . + " ON CTMD.id = TCT.codetracker_id "; + $ctf = ",CTMD.name AS ctname,CTMD.type AS cttype"; + } + + $rmssql = ''; + $rmsf = ''; + if ($my['opt']['add_reqmgrsystem']) { + $rmssql = " LEFT OUTER JOIN {$this->tables['testproject_reqmgrsystem']} AS TRMS " . + " ON TRMS.testproject_id = TPROJ.id " . + " LEFT OUTER JOIN {$this->tables['reqmgrsystems']} AS RMSMD " . + " ON RMSMD.id = TRMS.reqmgrsystem_id "; + $rmsf = ",RMSMD.name AS rmsname,RMSMD.type AS rmstype"; + } + + switch ($my['opt']['field_set']) { + case 'id': + $cols = ' TPROJ.id,NHTPROJ.name '; + $my['opt']['format'] = 'do not parse'; + break; + + case 'prefix': + $cols = ' TPROJ.id,TPROJ.prefix,TPROJ.active,NHTPROJ.name '; + $my['opt']['format'] = 'do not parse'; + break; + + case 'full': + default: + $cols = ' TPROJ.*,NHTPROJ.name,COALESCE(UTR.role_id,U.role_id) AS effective_role '; + break; + } + + $sql = " /* $debugMsg */ SELECT {$cols} {$itf} {$ctf} {$rmsf} " . + " FROM {$this->tables['nodes_hierarchy']} NHTPROJ " . + " JOIN {$this->object_table} TPROJ ON NHTPROJ.id=TPROJ.id " . + " JOIN {$this->tables['users']} U ON U.id = {$safe_user_id} " . + " LEFT OUTER JOIN {$this->tables['user_testproject_roles']} UTR " . + " ON TPROJ.id = UTR.testproject_id " . " AND UTR.user_id =" . + $safe_user_id . $itsql . $ctsql . $rmssql . " WHERE 1=1 "; + + // Private test project feature + if ($globalRoleID != TL_ROLES_ADMIN) { + if ($globalRoleID != TL_ROLES_NO_RIGHTS) { + $sql .= " AND "; + $sql_public = " ( TPROJ.is_public = 1 AND (UTR.role_id IS NULL OR UTR.role_id != " . + TL_ROLES_NO_RIGHTS . ") )"; + $sql_private = " ( TPROJ.is_public = 0 AND UTR.role_id != " . + TL_ROLES_NO_RIGHTS . ") "; + $sql .= " ( {$sql_public} OR {$sql_private} ) "; + } else { + // User needs specific role + $sql .= " AND (UTR.role_id IS NOT NULL AND UTR.role_id != " . + TL_ROLES_NO_RIGHTS . ")"; + } + } + + $userObj = tlUser::getByID($this->db, $safe_user_id, + tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); + if ($userObj->hasRight($this->db, 'mgt_modify_product') != 'yes') { + $sql .= " AND TPROJ.active=1 "; + } + unset($userObj); + + foreach ($my['filters'] as $fname => $fspec) { + if (! is_null($fspec)) { + switch ($fname) { + case 'prefix': + $sql .= " AND TPROJ.$fname"; + $sm = 'prepare_string'; + break; + + case 'name': + $sql .= " AND NHTPROJ.$fname"; + $sm = 'prepare_string'; + break; + + case 'id': + $sql .= " AND NHTPROJ.$fname"; + $sm = 'prepare_int'; + break; + } + + $safe = $this->db->$sm($fspec['value']); + switch ($fspec['op']) { + case '=': + if ($sm == 'prepare_string') { + $sql .= "='" . $safe . "'"; + } else { + $sql .= "=" . $safe; + } + break; + + case 'like': + $sql .= " LIKE '%" . $safe . "%'"; + break; + } + } + } + + $sql .= str_replace('nodes_hierarchy', 'NHTPROJ', $my['opt']['order_by']); + $parseOpt = false; + $do_post_process = 0; + $arrTemp = array(); + switch ($my['opt']['output']) { + case 'array_of_map': + $items = $this->db->get_recordset($sql); // ,null,3,1); + $parseOpt = true; + break; + + case 'map_of_map_full': + $items = $this->db->fetchRowsIntoMap($sql, 'id'); + $parseOpt = true; + break; + + case 'map': + $items = $this->db->fetchRowsIntoMap($sql, 'id'); + break; + + case 'map_with_inactive_mark': + default: + $arrTemp = (array) $this->db->fetchRowsIntoMap($sql, 'id'); + $do_post_process = ! empty($arrTemp); + break; + } + + if ($my['opt']['format'] == 'std' && $parseOpt) { + $this->parseTestProjectRecordset($items); + } + + if ($do_post_process) { + switch ($my['opt']['output']) { + case 'map_name_with_inactive_mark': + foreach ($arrTemp as $id => $row) { + $noteActive = ''; + if (! $row['active']) { + $noteActive = TL_INACTIVE_MARKUP; + } + $items[$id] = $noteActive . + (($my['opt']['field_set'] == 'prefix') ? ($row['prefix'] . + ':') : '') . $row['name']; + } + break; + + case 'map_of_map': + foreach ($arrTemp as $id => $row) { + $items[$id] = array( + 'name' => $row['name'], + 'active' => $row['active'] + ); + } + break; + } + unset($arrTemp); + } + + return $items; + } + + /* + * function: get_subtree + * Get subtree that has choosen testproject as root. + * Only nodes of type: + * testsuite and testcase are explored and retrieved. + * + * args: id: testsuite id + * [recursive_mode]: default false + * [exclude_testcases]: default: false + * [exclude_branches] + * [additionalWhereClause]: + * + * + * returns: array + * see tree->get_subtree() for details. + * + * + */ + public function get_subtree($id, $filters = null, $opt = null) + { + $my = array(); + $my['options'] = array( + 'recursive' => false, + 'exclude_testcases' => false, + 'output' => 'full' + ); + $my['filters'] = array( + 'exclude_node_types' => $this->nt2exclude, + 'exclude_children_of' => $this->nt2exclude_children, + 'exclude_branches' => null, + 'additionalWhereClause' => '' + ); + + $my['options'] = array_merge($my['options'], (array) $opt); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + if ($my['options']['exclude_testcases']) { + $my['filters']['exclude_node_types']['testcase'] = 'exclude me'; + } + + return $this->tree_manager->get_subtree(intval($id), $my['filters'], + $my['options']); + } + + /** + * Displays smarty template to show test project info to users. + * + * @param TLSmarty $smarty + * [ref] smarty object + * @param int $id + * test project + * @param string $sqlResult + * [default = ''] + * @param string $action + * [default = 'update'] + * @param int $modded_item_id + * [default = 0] + * + * @internal revisions + * + */ + public function show(&$smarty, $guiObj, $template_dir, $id, $sqlResult = '', + $action = 'update', $modded_item_id = 0) + { + $gui = $guiObj; + + if (! property_exists($gui, 'uploadOp')) { + $gui->uploadOp = null; + } + + $gui->sqlResult = ''; + $gui->sqlAction = ''; + if ($sqlResult) { + $gui->sqlResult = $sqlResult; + } + + $p2ow = array( + 'refreshTree' => false, + 'user_feedback' => '' + ); + foreach ($p2ow as $prop => $value) { + if (! property_exists($gui, $prop)) { + $gui->$prop = $value; + } + } + + $safeID = intval($id); + $gui->tproject_id = $safeID; + $gui->modify_tc_rights = has_rights($this->db, "mgt_modify_tc", $safeID); + $gui->mgt_modify_product = has_rights($this->db, "mgt_modify_product"); + + $gui->container_data = $this->get_by_id($safeID); + $gui->moddedItem = $gui->container_data; + $gui->level = 'testproject'; + $gui->page_title = lang_get('testproject'); + $gui->refreshTree = property_exists($gui, 'refreshTree') ? $gui->refreshTree : false; + $gui->attachmentInfos = getAttachmentInfosFrom($this, $safeID); + + // attachments management on page + $gui->fileUploadURL = $_SESSION['basehref'] . + $this->getFileUploadRelativeURL($safeID); + $gui->delAttachmentURL = $_SESSION['basehref'] . + $this->getDeleteAttachmentRelativeURL($safeID); + $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; + $gui->fileUploadMsg = ''; + + $exclusion = array( + 'testcase', + 'me', + 'testplan' => 'me', + 'requirement_spec' => 'me' + ); + $gui->canDoExport = count( + (array) $this->tree_manager->get_children($safeID, $exclusion)) > 0; + if ($modded_item_id) { + $gui->moddedItem = $this->get_by_id(intval($modded_item_id)); + } + $cfg = getWebEditorCfg('testproject'); + $gui->testProjectEditorType = $cfg['type']; + + $smarty->assign('gui', $gui); + $smarty->display($template_dir . 'containerView.tpl'); + } + + /** + * Count testcases without considering active/inactive status. + * + * @param integer $id: + * test project identifier + * @return integer count of test cases presents on test project. + */ + public function count_testcases($id) + { + $tcIDs = array(); + $this->get_all_testcases_id($id, $tcIDs); + return count($tcIDs); + } + + /* + * function: gen_combo_test_suites + * create array with test suite names + * test suites are ordered in parent-child way, means + * order on array is creating traversing tree branches, reaching end + * of branch, and starting again. (recursive algorithim). + * + * + * args : $id: test project id + * [$exclude_branches]: array with testsuite id to exclude + * useful to exclude myself ($id) + * [$mode]: dotted -> $level number of dot characters are appended to + * the left of test suite name to create an indent effect. + * Level indicates on what tree layer testsuite is positioned. + * Example: + * + * null + * \ + * id=1 <--- Tree Root = Level 0 + * | + * + ------+ + * / \ \ + * id=9 id=2 id=8 <----- Level 1 + * \ + * id=3 <----- Level 2 + * \ + * id=4 <----- Level 3 + * + * + * key: testsuite id (= node id on tree). + * value: every array element is an string, containing testsuite name. + * + * Result example: + * + * 2 .TS1 + * 3 ..TS2 + * 9 .20071014-16:22:07 TS1 + * 10 ..TS2 + * + * + * array -> key: testsuite id (= node id on tree). + * value: every array element is a map with the following keys + * 'name', 'level' + * + * 2 array(name => 'TS1',level => 1) + * 3 array(name => 'TS2',level => 2) + * 9 array(name => '20071014-16:22:07 TS1',level =>1) + * 10 array(name => 'TS2', level => 2) + * + * + * returns: map , structure depens on $mode argument. + * + */ + public function gen_combo_test_suites($id, $exclude_branches = null, + $mode = 'dotted') + { + $ret = array(); + $test_spec = $this->get_subtree($id, + array( + 'exclude_branches' => $exclude_branches + ), + array( + 'recursive' => ! self::RECURSIVE_MODE, + 'exclude_testcases' => self::EXCLUDE_TESTCASES + )); + + if (count($test_spec)) { + $ret = $this->_createHierarchyMap($test_spec, $mode); + } + return $ret; + } + + /** + * Checks a test project name for correctness + * + * @param string $name + * the name to check + * @return array with keys: status_ok, msg + */ + public function checkName($name) + { + $forbidden_pattern = config_get('ereg_forbidden'); + $ret['status_ok'] = 1; + $ret['msg'] = 'ok'; + + if ($name == "") { + $ret['msg'] = lang_get('info_product_name_empty'); + $ret['status_ok'] = 0; + } + if ($ret['status_ok'] && ! checkString($name, $forbidden_pattern)) { + $ret['msg'] = lang_get('string_contains_bad_chars'); + $ret['status_ok'] = 0; + } + return $ret; + } + + /** + * Checks a test project name for sintax correctness + * + * @param string $name + * the name to check + * @return array with keys: status_ok, msg + */ + public function checkNameSintax($name) + { + $forbidden_pattern = config_get('ereg_forbidden'); + $ret['status_ok'] = 1; + $ret['msg'] = 'ok'; + + if ($name == "") { + $ret['msg'] = lang_get('info_product_name_empty'); + $ret['status_ok'] = 0; + } + if ($ret['status_ok'] && ! checkString($name, $forbidden_pattern)) { + $ret['msg'] = lang_get('string_contains_bad_chars'); + $ret['status_ok'] = 0; + } + return $ret; + } + + /** + * Checks is there is another testproject with different id but same name + */ + public function checkNameExistence($name, $id = 0) + { + $check_op['msg'] = ''; + $check_op['status_ok'] = 1; + + if ($this->get_by_name($name, "testprojects.id <> {$id}")) { + $check_op['msg'] = sprintf(lang_get('error_product_name_duplicate'), + $name); + $check_op['status_ok'] = 0; + } + return $check_op; + } + + /** + * Checks is there is another testproject with different id but same prefix + */ + private function checkTestCasePrefixExistence($prefix, $id = 0) + { + $check_op = array( + 'msg' => '', + 'status_ok' => 1 + ); + $sql = " SELECT id FROM {$this->object_table} " . " WHERE prefix='" . + $this->db->prepare_string($prefix) . "'" . " AND id <> {$id}"; + + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + $check_op['msg'] = sprintf(lang_get('error_tcase_prefix_exists'), + $prefix); + $check_op['status_ok'] = 0; + } + + return $check_op; + } + + /** + * allow activate or deactivate a test project + * + * @param integer $id + * test project ID + * @param integer $status + * 1=active || 0=inactive + */ + public function activate($id, $status) + { + $sql = "UPDATE {$this->tables['testprojects']} SET active=" . $status . + " WHERE id=" . $id; + $result = $this->db->exec_query($sql); + + return $result ? 1 : 0; + } + + /** + * + * @param string $str + * @return string + */ + private function formatTcPrefix($str) + { + $fstr = trim($str); + if (tlStringLen($fstr) == 0) { + throw new Exception('Empty prefix is not allowed'); + } + + // limit tcasePrefix len. + if (tlStringLen($fstr) > self::TESTCASE_PREFIX_MAXLEN) { + $fstr = substr($fstr, 0, self::TESTCASE_PREFIX_MAXLEN); + } + return $fstr; + } + + /* + * args : id: test project + * returns: null if query fails + * string + */ + public function getTestCasePrefix($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT prefix FROM {$this->object_table} WHERE id = {$id}"; + return $this->db->fetchOneValue($sql); + } + + /* + * args: id: test project + * returns: null if query fails + * a new test case number + */ + public function generateTestCaseNumber($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $retry = 3; + $lockfile = $this->tmp_dir . __FUNCTION__ . '.lock'; + $lock = fopen($lockfile, 'a'); + + $gotLock = false; + while ($retry > 0 && ! $gotLock) { + if (flock($lock, LOCK_EX)) { + $gotLock = true; + } else { + $retry --; + usleep(20); + } + } + + if ($gotLock || $retry == 0) { + $safeID = intval($id); + + $ret = null; + $sql = "/* $debugMsg */ UPDATE {$this->object_table} " . + " SET tc_counter=tc_counter+1 WHERE id = {$safeID}"; + $this->db->exec_query($sql); + + $sql = " SELECT tc_counter FROM {$this->object_table} WHERE id = {$safeID}"; + $rs = $this->db->get_recordset($sql); + $ret = $rs[0]['tc_counter']; + + if ($gotLock) { + flock($lock, LOCK_UN); + } + fclose($lock); + + return $ret; + } + } + + /** + */ + protected function setTestCaseCounter($id, $value, $force = false) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $safeValue = intval($value); + $sql = " /* $debugMsg */ UPDATE {$this->object_table} " . + ' SET tc_counter=' . $safeValue . ' WHERE id =' . intval($id); + + if (! $force) { + $sql .= ' AND tc_counter < ' . $safeValue; + } + $this->db->exec_query($sql); + } + + /** + * + * @param integer $id + * test project ID + */ + private function setPublicStatus($id, $status) + { + $isPublic = val($status) > 0 ? 1 : 0; + $sql = "UPDATE {$this->object_table} SET is_public={$isPublic} WHERE id={$id}"; + $result = $this->db->exec_query($sql); + return $result ? 1 : 0; + } + + /* Keywords related methods */ + /** + * Adds a new keyword to the given test project + * + * @param int $testprojectID + * @param string $keyword + * @param string $notes + * + */ + public function addKeyword($testprojectID, $keyword, $notes) + { + $kw = new tlKeyword(); + $kw->initialize(null, $testprojectID, $keyword, $notes); + $op = array( + 'status' => tlKeyword::E_DBERROR, + 'id' => - 1, + 'msg' => 'ko DB Error' + ); + + $op['status'] = $kw->writeToDB($this->db); + $op['id'] = $kw->dbID; + + if ($op['status'] >= self::OK) { + logAuditEvent(TLS("audit_keyword_created", $keyword), "CREATE", + $op['id'], "keywords"); + } else { + $op['msg'] = tlKeyword::getError($op['status']); + } + + return $op; + } + + /** + * updates the keyword with the given id + * + * @param int $testprojectID + * @param int $id + * @param string $keyword + * @param string $notes + * + */ + public function updateKeyword($testprojectID, $id, $keyword, $notes) + { + $kw = new tlKeyword($id); + $kw->initialize($id, $testprojectID, $keyword, $notes); + $result = $kw->writeToDB($this->db); + if ($result >= self::OK) { + logAuditEvent(TLS("audit_keyword_saved", $keyword), "SAVE", + $kw->dbID, "keywords"); + } + return $result; + } + + /** + * gets the keyword with the given id + * + * @param int $id + */ + public function getKeyword($id) + { + return tlKeyword::getByID($this->db, $id); + } + + /** + * Gets the keywords of the given test project + * + * @param int $tprojectID + * the test project id + * @param int $keywordID + * [default = null] the optional keyword id + * + * @return array, every elemen is map with following structure: + * id + * keyword + * notes + */ + public function getKeywords($testproject_id) + { + $ids = $this->getKeywordIDsFor($testproject_id); + return tlKeyword::getByIDs($this->db, $ids); + } + + /** + * Deletes the keyword with the given id + * + * @param int $id + * the keywordID + * @return int returns 1 on success, 0 else + * + */ + public function deleteKeyword($id, $opt = null) + { + $result = self::ERROR; + $my['opt'] = array( + 'checkBeforeDelete' => true, + 'nameForAudit' => null, + 'context' => '', + 'tproject_id' => null + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $doIt = ! $my['opt']['checkBeforeDelete']; + $keyword = $my['opt']['nameForAudit']; + + if ($my['opt']['checkBeforeDelete']) { + $doIt = true; + if ($this->cfg->keywords->onDeleteCheckExecutedTCVersions) { + $linkedAndNotExec = $this->checkKeywordIsLinkedAndNotExecuted( + $id); + $doIt = $doIt && $linkedAndNotExec; + } + + if ($this->cfg->keywords->onDeleteCheckFrozenTCVersions) { + $linkedToFrozen = $this->checkKeywordIsLinkedToFrozenVersions( + $id); + $doIt = $doIt && ! $linkedToFrozen; + } + } + + if ($doIt) { + if ($this->auditCfg->logEnabled) { + $keyword = $this->getKeywordSimple($id); + } + $result = tlDBObject::deleteObjectFromDB($this->db, $id, "tlKeyword"); + } + + if ($result >= self::OK && $this->auditCfg->logEnabled) { + + switch ($my['opt']['context']) { + case 'getTestProjectName': + $dummy = $this->get_by_id($my['opt']['tproject_id'], + array( + 'output' => 'name' + )); + $my['opt']['context'] = $dummy['name']; + break; + } + + logAuditEvent( + TLS("audit_keyword_deleted", $keyword, $my['opt']['context']), + "DELETE", $id, "keywords"); + } + return $result; + } + + /** + * delete Keywords + */ + public function deleteKeywords($tproject_id, $tproject_name = null) + { + $result = self::OK; + + $itemSet = (array) $this->getKeywordSet($tproject_id); + $kwIDs = array_keys($itemSet); + + $opt = array( + 'checkBeforeDelete' => false, + 'context' => $tproject_name + ); + + $loop2do = count($kwIDs); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $opt['nameForAudit'] = $itemSet[$kwIDs[$idx]]['keyword']; + + $resultKw = $this->deleteKeyword($kwIDs[$idx], $opt); + if ($resultKw != self::OK) { + $result = $resultKw; + } + } + return $result; + } + + /** + */ + protected function getKeywordIDsFor($testproject_id) + { + $query = " SELECT id FROM {$this->tables['keywords']} " . + " WHERE testproject_id = {$testproject_id}" . " ORDER BY keyword ASC"; + return $this->db->fetchColumnsIntoArray($query, 'id'); + } + + /** + */ + public function getKeywordSet($tproject_id) + { + $sql = " SELECT id,keyword FROM {$this->tables['keywords']} " . + " WHERE testproject_id = {$tproject_id}" . " ORDER BY keyword ASC"; + + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + */ + public function hasKeywords($id) + { + // seems that postgres PHP driver do not manage well UPPERCASE in AS CLAUSE + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT COUNT(0) AS qty FROM {$this->tables['keywords']} " . + " WHERE testproject_id = " . intval($id); + $rs = $this->db->get_recordset($sql); + + return (is_null($rs) || $rs[0]['qty'] == 0) ? false : true; + } + + /** + * Exports the given keywords to a XML file + * + * @return string the generated XML Code + */ + public function exportKeywordsToXML($testproject_id, $bNoXMLHeader = false) + { + $kwIDs = $this->getKeywordIDsFor($testproject_id); + $xmlCode = ''; + if (! $bNoXMLHeader) { + $xmlCode .= TL_XMLEXPORT_HEADER . "\n"; + } + $xmlCode .= ""; + for ($idx = 0; $idx < count($kwIDs); $idx ++) { + $keyword = new tlKeyword($kwIDs[$idx]); + $keyword->readFromDb($this->db); + $keyword->writeToXML($xmlCode, true); + } + $xmlCode .= ""; + + return $xmlCode; + } + + /** + * Exports the given keywords to CSV + * + * @return string the generated CSV code + */ + private function exportKeywordsToCSV($testproject_id, $delim = ';') + { + $kwIDs = $this->getKeywordIDsFor($testproject_id); + $csv = null; + for ($idx = 0; $idx < count($kwIDs); $idx ++) { + $keyword = new tlKeyword($kwIDs[$idx]); + $keyword->readFromDb($this->db); + $keyword->writeToCSV($csv, $delim); + } + return $csv; + } + + public function importKeywordsFromCSV($testproject_id, $fileName, + $delim = ';') + { + $handle = fopen($fileName, "r"); + if ($handle) { + while ($data = fgetcsv($handle, TL_IMPORT_ROW_MAX, $delim)) { + $kw = new tlKeyword(); + $kw->initialize(null, $testproject_id, null, null); + if ($kw->readFromCSV(implode($delim, $data)) >= self::OK && + $kw->writeToDB($this->db) >= self::OK) { + logAuditEvent(TLS("audit_keyword_created", $kw->name), + "CREATE", $kw->dbID, "keywords"); + } + } + fclose($handle); + return self::OK; + } else { + return self::ERROR; + } + } + + /** + * + * @param + * $testproject_id + * @param + * $fileName + */ + public function importKeywordsFromXMLFile($testproject_id, $fileName) + { + $simpleXMLObj = @$this->simplexmlLoadFileHelper($fileName); + return $this->importKeywordsFromSimpleXML($testproject_id, $simpleXMLObj); + } + + /** + * + * @param + * $testproject_id + * @param + * $xmlString + */ + public function importKeywordsFromXML($testproject_id, $xmlString) + { + $simpleXMLObj = simplexml_load_string($xmlString); + return $this->importKeywordsFromSimpleXML($testproject_id, $simpleXMLObj); + } + + /** + * + * @param + * $testproject_id + * @param + * $simpleXMLObj + */ + public function importKeywordsFromSimpleXML($testproject_id, $simpleXMLObj) + { + $status = self::OK; + if (! $simpleXMLObj || $simpleXMLObj->getName() != 'keywords') { + $status = tlKeyword::E_WRONGFORMAT; + } + + if (($status == self::OK) && $simpleXMLObj->keyword) { + foreach ($simpleXMLObj->keyword as $keyword) { + $kw = new tlKeyword(); + $kw->initialize(null, $testproject_id, null, null); + $status = tlKeyword::E_WRONGFORMAT; + if ($kw->readFromSimpleXML($keyword) >= self::OK) { + $status = self::OK; + if ($kw->writeToDB($this->db) >= self::OK) { + logAuditEvent(TLS("audit_keyword_created", $kw->name), + "CREATE", $kw->dbID, "keywords"); + } + } + } + } + return $status; + } + + /** + * Returns all testproject keywords + * + * @param integer $testproject_id + * the ID of the testproject + * @return array map: key: keyword_id, value: keyword + */ + public function get_keywords_map($testproject_id) + { + $keywordMap = null; + $keywords = $this->getKeywords($testproject_id); + if ($keywords) { + foreach ($keywords as $kw) { + $keywordMap[$kw->dbID] = $kw->name; + } + } + return $keywordMap; + } + + /** + * Returns keywords that are linked to test cases + * + * @param integer $id + * testproject + * @return array map: key: keyword_id, value: keyword + */ + public function getUsedKeywordsMap($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ + SELECT DISTINCT KW.id,KW.keyword + FROM {$this->tables['keywords']} KW + JOIN {$this->tables['testcase_keywords']} TCKW + ON TCKW.keyword_id = KW.id + WHERE KW.testproject_id =" . intval($id); + $sql .= " ORDER BY keyword"; + return $this->db->fetchColumnsIntoMap($sql, 'id', 'keyword'); + } + + /* END KEYWORDS RELATED */ + + /* REQUIREMENTS RELATED */ + /** + * get list of all SRS for a test project, no distinction between levels + * + * + * @used-by lib/results/uncoveredTestCases.php + * lib/requirements/reqTcAssign.php + * lib/requirements/reqSpecSearchForm.php + * lib/requirements/reqSearchForm.php + * + * @author Martin Havlat + * @return array List of titles according to IDs + * + * @internal revisions + * + */ + public function getOptionReqSpec($tproject_id, + $get_not_empty = self::GET_EMPTY_REQSPEC) + { + $additional_table = ''; + $additional_join = ''; + if ($get_not_empty) { + $additional_table = ", {$this->tables['requirements']} REQ "; + $additional_join = " AND SRS.id = REQ.srs_id "; + } + $sql = " SELECT SRS.id,NH.name AS title " . + " FROM {$this->tables['req_specs']} SRS, " . + " {$this->tables['nodes_hierarchy']} NH " . $additional_table . + " WHERE testproject_id={$tproject_id} " . " AND SRS.id=NH.id " . + $additional_join . " ORDER BY title"; + return $this->db->fetchColumnsIntoMap($sql, 'id', 'title'); + // return $this->db->fetchRowsIntoMap($sql,'id'); SRS.doc_id, + } + + /** + * + * @author Francisco Mancardi - francisco.mancardi@gmail.com + * + * @todo check who uses it, is duplicated of getOptionReqSpec? + * + * @used-by lib/results/uncoveredTestCases.php + * lib/requirements/reqTcAssign.php + * lib/requirements/reqSpecSearchForm.php + * lib/requirements/reqSearchForm.php + * + * @internal revisions + * + * + */ + public function genComboReqSpec($id, $mode = 'dotted', $dot = '.') + { + $ret = array(); + $exclude_node_types = array( + 'testplan' => 'exclude_me', + 'testsuite' => 'exclude_me', + 'testcase' => 'exclude_me', + 'requirement' => 'exclude_me', + 'requirement_spec_revision' => 'exclude_me' + ); + + $my['filters'] = array( + 'exclude_node_types' => $exclude_node_types + ); + + $my['options'] = array( + 'order_cfg' => array( + 'type' => 'rspec' + ), + 'output' => 'rspec' + ); + $subtree = $this->tree_manager->get_subtree($id, $my['filters'], + $my['options']); + if (count($subtree)) { + $ret = $this->_createHierarchyMap($subtree, $mode, $dot, 'doc_id'); + } + return $ret; + } + + /* + * + * [$mode]: dotted -> $level number of dot characters are appended to + * the left of item name to create an indent effect. + * Level indicates on what tree layer item is positioned. + * Example: + * + * null + * \ + * id=1 <--- Tree Root = Level 0 + * | + * + ------+ + * / \ \ + * id=9 id=2 id=8 <----- Level 1 + * \ + * id=3 <----- Level 2 + * \ + * id=4 <----- Level 3 + * + * + * key: item id (= node id on tree). + * value: every array element is an string, containing item name. + * + * Result example: + * + * 2 .TS1 + * 3 ..TS2 + * 9 .20071014-16:22:07 TS1 + * 10 ..TS2 + * + * + * array -> key: item id (= node id on tree). + * value: every array element is a map with the following keys + * 'name', 'level' + * + * 2 array(name => 'TS1',level => 1) + * 3 array(name => 'TS2',level => 2) + * 9 array(name => '20071014-16:22:07 TS1',level =>1) + * 10 array(name => 'TS2', level => 2) + * + */ + protected function _createHierarchyMap($array2map, $mode = 'dotted', + $dot = '.', $addfield = null) + { + $hmap = array(); + $the_level = 1; + $level = array(); + $pivot = $array2map[0]; + + $addprefix = ! is_null($addfield); + foreach ($array2map as $elem) { + $current = $elem; + + if ($pivot['id'] == $current['parent_id']) { + $the_level ++; + $level[$current['parent_id']] = $the_level; + } elseif ($pivot['parent_id'] != $current['parent_id']) { + $the_level = $level[$current['parent_id']]; + } + + switch ($mode) { + case 'dotted': + $dm = $addprefix ? "[{$current[$addfield]}] - " : ''; + $pding = ($the_level == 1) ? 0 : $the_level + 1; + $hmap[$current['id']] = str_repeat($dot, $pding) . $dm . + $current['name']; + break; + + case 'array': + $hmap[$current['id']] = array( + 'name' => $current['name'], + 'level' => $the_level + ); + break; + } + + // update pivot + $level[$current['parent_id']] = $the_level; + $pivot = $elem; + } + + return $hmap; + } + + /** + * collect information about current list of Requirements Specification + * + * @param integer $testproject_id + * @param string $id + * optional id of the requirement specification + * + * @return mixed null if no srs exits, or no srs exists for id + * array, where each element is a map with SRS data. + * + * map keys: + * id + * testproject_id + * title + * scope + * total_req + * type + * author_id + * creation_ts + * modifier_id + * modification_ts + * + * @author Martin Havlat + * @internal revisions + * + */ + public function getReqSpec($testproject_id, $id = null, $fields = null, + $access_key = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $fields2get = " RSPEC.id, RSPEC.testproject_id, RSPECREV.scope, RSPECREV.doc_id," . + " RSPECREV.total_req, RSPECREV.type, RSPECREV.author_id, RSPECREV.creation_ts, " . + " RSPECREV.modifier_id, RSPECREV.modification_ts, RSPECREV.name AS title, NH.parent_id"; + + $fields = is_null($fields) ? $fields2get : implode(',', $fields); + $sql = " /* $debugMsg */ " . + " SELECT {$fields} FROM {$this->tables['req_specs_revisions']} RSPECREV, " . + " {$this->tables['req_specs']} RSPEC, {$this->tables['nodes_hierarchy']} NH, " . + " {$this->tables['requirements']} REQ " . + " WHERE RSPECREV.parent_id=RSPEC.id " . + " AND NH.id=RSPEC.id AND REQ.srs_id = RSPEC.id " . + " AND RSPEC.testproject_id={$testproject_id} "; + + if (! is_null($id)) { + $sql .= " AND RSPEC.id=" . $id; + } + + $sql .= " GROUP BY RSPEC.id"; + $sql .= " ORDER BY RSPEC.id,title"; + + return is_null($access_key) ? $this->db->get_recordset($sql) : $this->db->fetchRowsIntoMap( + $sql, $access_key); + } + + /** + * create a new System Requirements Specification + * + * @param string $title + * @param string $scope + * @param string $countReq + * @param int $testproject_id + * @param int $user_id + * @param string $type + * + * @author Martin Havlat + * + * rev: 20071106 - franciscom - changed return type + */ + private function createReqSpec($testproject_id, $title, $scope, $countReq, + $user_id, $type = 'n') + { + $ignore_case = 1; + $result = array(); + + $result['status_ok'] = 0; + $result['msg'] = 'ko'; + $result['id'] = 0; + + $title = trim($title); + + $chk = $this->check_srs_title($testproject_id, $title, $ignore_case); + if ($chk['status_ok']) { + $sql = "INSERT INTO {$this->tables['req_specs']} " . + " (testproject_id, title, scope, type, total_req, author_id, creation_ts) + VALUES (" . $testproject_id . ",'" . + $this->db->prepare_string($title) . "','" . + $this->db->prepare_string($scope) . "','" . + $this->db->prepare_string($type) . "','" . + $this->db->prepare_string($countReq) . "'," . + $this->db->prepare_string($user_id) . ", " . $this->db->db_now() . + ")"; + + if (! $this->db->exec_query($sql)) { + $result['msg'] = lang_get('error_creating_req_spec'); + } else { + $result['id'] = $this->db->insert_id($this->tables['req_specs']); + $result['status_ok'] = 1; + $result['msg'] = 'ok'; + } + } else { + $result['msg'] = $chk['msg']; + } + return $result; + } + + /* + * function: get_srs_by_title + * get srs information using title as access key. + * + * args : tesproject_id + * title: srs title + * [ignore_case]: control case sensitive search. + * default 0 -> case sensivite search + * + * returns: map. + * key: srs id + * value: srs info, map with folowing keys: + * id + * testproject_id + * title + * scope + * total_req + * type + * author_id + * creation_ts + * modifier_id + * modification_ts + */ + public function get_srs_by_title($testproject_id, $title, $ignore_case = 0) + { + $title = trim($title); + + $sql = "SELECT * FROM req_specs "; + + if ($ignore_case) { + $sql .= " WHERE UPPER(title)='" . + strtoupper($this->db->prepare_string($title)) . "'"; + } else { + $sql .= " WHERE title='" . $this->db->prepare_string($title) . "'"; + } + $sql .= " AND testproject_id={$testproject_id}"; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /* + * function: check_srs_title + * Do checks on srs title, to understand if can be used. + * + * Checks: + * 1. title is empty ? + * 2. does already exist a srs with this title? + * + * args : tesproject_id + * title: srs title + * [ignore_case]: control case sensitive search. + * default 0 -> case sensivite search + * + * returns: + * + */ + public function check_srs_title($testproject_id, $title, $ignore_case = 0) + { + $ret['status_ok'] = 1; + $ret['msg'] = ''; + + $title = trim($title); + + if ($title == "") { + $ret['status_ok'] = 0; + $ret['msg'] = lang_get("warning_empty_req_title"); + } + + if ($ret['status_ok']) { + $ret['msg'] = 'ok'; + $rs = $this->get_srs_by_title($testproject_id, $title, $ignore_case); + + if (! is_null($rs)) { + $ret['msg'] = lang_get("warning_duplicate_req_title"); + $ret['status_ok'] = 0; + } + } + return $ret; + } + + /* END REQUIREMENT RELATED */ + // ---------------------------------------------------------------------------------------- + + /** + * Deletes all testproject related role assignments for a given testproject + * + * @param integer $tproject_id + * @return integer self::OK on success, self::ERROR else + */ + public function deleteUserRoles($tproject_id, $users = null, $opt = null) + { + $my['opt'] = array( + 'auditlog' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + $query = " DELETE FROM {$this->tables['user_testproject_roles']} " . + " WHERE testproject_id = " . intval($tproject_id); + + if (! is_null($users)) { + $query .= " AND user_id IN(" . implode(',', $users) . ")"; + } + + if ($this->db->exec_query($query) && $my['opt']['auditlog']) { + $testProject = $this->get_by_id($tproject_id); + + if ($testProject) { + if (is_null($users)) { + logAuditEvent( + TLS("audit_all_user_roles_removed_testproject", + $testProject['name']), "ASSIGN", $tproject_id, + "testprojects"); + } else { + // TBD + } + } + return self::OK; + } + + return self::ERROR; + } + + /** + * Gets all testproject related role assignments + * + * @param integer $tproject_id + * @return array assoc array with keys take from the user_id column + */ + private function getUserRoleIDs($tproject_id) + { + $query = "SELECT user_id,role_id FROM {$this->tables['user_testproject_roles']} " . + "WHERE testproject_id = {$tproject_id}"; + return $this->db->fetchRowsIntoMap($query, 'user_id'); + } + + /** + * Inserts a testproject related role for a given user + * + * @param integer $userID + * the id of the user + * @param integer $tproject_id + * @param integer $roleID + * the role id + * + * @return integer self::OK on success, self::ERROR else + */ + public function addUserRole($userID, $tproject_id, $roleID) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $query = "/* $debugMsg */ INSERT INTO {$this->tables['user_testproject_roles']} " . + " (user_id,testproject_id,role_id) VALUES ({$userID},{$tproject_id},{$roleID})"; + if ($this->db->exec_query($query)) { + $testProject = $this->get_by_id($tproject_id); + $role = tlRole::getByID($this->db, $roleID, + tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); + $user = tlUser::getByID($this->db, $userID, + tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); + if ($user && $testProject && $role) { + logAuditEvent( + TLS("audit_users_roles_added_testproject", + $user->getDisplayName(), $testProject['name'], + $role->name), "ASSIGN", $tproject_id, "testprojects"); + } + unset($user); + unset($role); + unset($testProject); + return self::OK; + } + return self::ERROR; + } + + /** + * delete test project from system, deleting all dependent data: + * keywords, requirements, custom fields, testsuites, testplans, + * testcases, results, testproject related roles, + * + * @param integer $id + * test project id + * @return integer status + * + */ + public function delete($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $ret['msg'] = 'ok'; + $ret['status_ok'] = 1; + + $error = ''; + $reqspec_mgr = new requirement_spec_mgr($this->db); + + // get some info for audit + $info['name'] = ''; + if ($this->auditCfg->logEnabled) { + $info = $this->tree_manager->get_node_hierarchy_info($id); + $event = new stdClass(); + $event->message = TLS("audit_testproject_deleted", $info['name']); + $event->objectID = $id; + $event->objectType = 'testprojects'; + $event->source = $this->auditCfg->eventSource; + $event->logLevel = 'AUDIT'; + $event->code = 'DELETE'; + } + + // + // Notes on delete related to Foreing Keys + // All link tables has to be deleted first + // + // req_relations + // + // testplan_tcversions + // testplan_platforms + // object_keywords + // user_assignments + // builds + // milestones + // + // testplans + // keywords + // platforms + // attachtments + // testcases + // testsuites + // inventory + // + // testproject + $this->deleteKeywords($id, $info['name']); + $this->deleteAttachments($id); + + $reqSpecSet = $reqspec_mgr->get_all_id_in_testproject($id); + if (! empty($reqSpecSet)) { + foreach ($reqSpecSet as $reqSpec) { + $reqspec_mgr->delete_deep($reqSpec['id']); + } + } + + $tplanSet = $this->get_all_testplans($id); + if (! is_null($tplanSet) && count($tplanSet) > 0) { + $tplan_mgr = new testplan($this->db); + $items = array_keys($tplanSet); + foreach ($items as $key) { + $tplan_mgr->delete($key); + } + } + + $platform_mgr = new tlPlatform($this->db, $id); + $platform_mgr->deleteByTestProject($id); + + $a_sql[] = array( + "/* $debugMsg */ UPDATE {$this->tables['users']} " . + " SET default_testproject_id = NULL " . + " WHERE default_testproject_id = {$id}", + 'info_resetting_default_project_fails' + ); + + $inventory_mgr = new tlInventory($id, $this->db); + $invOpt = array( + 'detailLevel' => 'minimun', + 'accessKey' => 'id' + ); + $inventorySet = $inventory_mgr->getAll($invOpt); + if (! is_null($inventorySet)) { + foreach ($inventorySet as $key => $dummy) { + $inventory_mgr->deleteInventory($key); + } + } + + foreach ($a_sql as $oneSQL) { + if (empty($error)) { + $sql = $oneSQL[0]; + $result = $this->db->exec_query($sql); + if (! $result) { + $error .= lang_get($oneSQL[1]); + } + } + } + + if ($this->deleteUserRoles($id) < self::OK) { + $error .= lang_get('info_deleting_project_roles_fails'); + } + + $xSQL = array( + 'testproject_issuetracker', + 'testproject_codetracker', + 'testproject_reqmgrsystem' + ); + foreach ($xSQL as $target) { + $sql = "/* $debugMsg */ DELETE FROM " . $this->tables[$target] . + " WHERE testproject_id = " . intval($id); + $this->db->exec_query($sql); + } + + // --------------------------------------------------------------------------------------- + // delete product itself and items directly related to it like: + // custom fields assignments + // custom fields values ( right now we are not using custom fields on test projects) + // attachments + if (empty($error)) { + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_testprojects']} WHERE testproject_id = {$id} "; + $this->db->exec_query($sql); + + $sql = "/* $debugMsg */ DELETE FROM {$this->object_table} WHERE id = {$id}"; + + $result = $this->db->exec_query($sql); + if ($result) { + $tproject_id_on_session = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : $id; + if ($id == $tproject_id_on_session) { + $this->setSessionProject(null); + } + } else { + $error .= lang_get('info_product_delete_fails'); + } + } + + if (empty($error)) { + // Delete test project with requirements defined crashed with memory exhausted + $this->tree_manager->delete_subtree_objects($id, $id, '', + array( + 'testcase' => 'exclude_tcversion_nodes' + )); + $sql = "/* $debugMsg */ " . + " DELETE FROM {$this->tables['nodes_hierarchy']} " . + " WHERE id = {$id} AND node_type_id=" . + $this->tree_manager->node_descr_id['testproject']; + + $this->db->exec_query($sql); + + if ($this->auditCfg->logEnabled) { + logEvent($event); + } + } + + if (! empty($error)) { + $ret['msg'] = $error; + $ret['status_ok'] = 0; + } + + return $ret; + } + + /* + * function: get_all_testcases_id + * All testproject testcases node id. + * + * args :idList: comma-separated list of IDs (should be the projectID, but could + * also be an arbitrary suiteID + * + * returns: array with testcases node id in parameter tcIDs. + * null is nothing found + * + */ + public function get_all_testcases_id($idList, &$tcIDs, $options = null) + { + static $tcNodeTypeID; + static $tsuiteNodeTypeID; + static $debugMsg; + if (! $tcNodeTypeID) { + $debugMsg = $this->debugMsg . __FUNCTION__; + $tcNodeTypeID = $this->tree_manager->node_descr_id['testcase']; + $tsuiteNodeTypeID = $this->tree_manager->node_descr_id['testsuite']; + } + + $my = array(); + $my['options'] = array( + 'output' => 'just_id' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + switch ($my['options']['output']) { + case 'external_id': + $use_array = true; + break; + + case 'just_id': + default: + $use_array = false; + break; + } + + $sql = "/* $debugMsg */ SELECT id,node_type_id from {$this->tables['nodes_hierarchy']} " . + " WHERE parent_id IN ({$idList})"; + $sql .= " AND node_type_id IN ({$tcNodeTypeID},{$tsuiteNodeTypeID}) "; + + $result = $this->db->exec_query($sql); + if ($result) { + $suiteIDs = array(); + while ($row = $this->db->fetch_array($result)) { + if ($row['node_type_id'] == $tcNodeTypeID) { + if ($use_array) { + $sql = " SELECT DISTINCT NH.parent_id, TCV.tc_external_id " . + " FROM {$this->tables['nodes_hierarchy']} NH " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NH.id " . + " WHERE NH.parent_id = {$row['id']} "; + + $rs = $this->db->fetchRowsIntoMap($sql, 'parent_id'); + $tcIDs[$row['id']] = $rs[$row['id']]['tc_external_id']; + } else { + $tcIDs[] = $row['id']; + } + } else { + $suiteIDs[] = $row['id']; + } + } + if (count($suiteIDs)) { + $suiteIDs = implode(",", $suiteIDs); + $this->get_all_testcases_id($suiteIDs, $tcIDs, $options); + } + } + } + + /* + * function: DEPRECATED_get_keywords_tcases + * testproject keywords (with related testcase node id), + * that are used on testcases. + * + * args :testproject_id + * [keyword_id]= 0 -> no filter + * <> 0 -> look only for this keyword + * can be an array. + * + * + * + * returns: map: key: testcase_id + * value: map + * key: keyword_id + * value: testcase_id,keyword_id,keyword + * + * Example: + * [24] => Array ( [3] => Array( [testcase_id] => 24 + * [keyword_id] => 3 + * [keyword] => MaxFactor ) + * + * [2] => Array( [testcase_id] => 24 + * [keyword_id] => 2 + * [keyword] => Terminator ) ) + * + * @internal revisions: + * 20100929 - asimon - BUGID 3814: fixed keyword filtering with "and" selected as type + */ + private function DEPRECATED_get_keywords_tcases($testproject_id, + $keyword_id = 0, $keyword_filter_type = 'Or') + { + $keyword_filter = ''; + $subquery = ''; + + if (is_array($keyword_id)) { + $keyword_filter = " AND keyword_id IN (" . implode(',', $keyword_id) . + ")"; + if ($keyword_filter_type == 'And') { + $subquery = "AND testcase_id IN (" . + " SELECT FOXDOG.testcase_id FROM + ( SELECT COUNT(testcase_id) AS HITS,testcase_id + FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']} + WHERE keyword_id = K.id + AND testproject_id = {$testproject_id} + {$keyword_filter} + GROUP BY testcase_id ) AS FOXDOG " . + " WHERE FOXDOG.HITS=" . count($keyword_id) . ")"; + + $keyword_filter = ''; + } + } elseif ($keyword_id > 0) { + $keyword_filter = " AND keyword_id = {$keyword_id} "; + } + + $sql = " SELECT testcase_id,keyword_id,keyword + FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']} + WHERE keyword_id = K.id + AND testproject_id = {$testproject_id} + {$keyword_filter} {$subquery} + ORDER BY keyword ASC "; + + return $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', 'keyword_id'); + } + + /** + */ + public function getKeywordsLatestTCV($tproject_id, $keyword_id = 0, + $kwFilterType = 'Or') + { + $kwFilter = ''; + $subquery = ''; + $ltcvJoin = " JOIN {$this->views['latest_tcase_version_id']} LTCV + ON LTCV.tcversion_id = TK.tcversion_id "; + + if (is_array($keyword_id)) { + $kwFilter = " AND keyword_id IN (" . implode(',', $keyword_id) . ")"; + if ($kwFilterType == 'And') { + $ltcvJoin = " "; + $sqlCount = " /* SQL COUNT */ " . + " SELECT COUNT(TK.tcversion_id) AS HITS,TK.tcversion_id + FROM {$this->tables['keywords']} K + JOIN {$this->tables['testcase_keywords']} TK + ON keyword_id = K.id -/** - * - * -1 => WITHOUT KEYWORDS - * - */ -function getTCLatestVersionFilteredByKeywords($tproject_id, $keyword_id=0, $keyword_filter_type='Or') { - $keySet = (array)$keyword_id; - $sql = null; - $tcaseSet = array(); - $delTT = false; - $hasTCases = false; + JOIN {$this->views['latest_tcase_version_id']} LTCV + ON LTCV.tcversion_id = TK.tcversion_id - // -1 => WITHOUT KEYWORDS - $getWithOutKeywords = in_array(-1,$keySet); - if( $getWithOutKeywords || $keyword_filter_type == 'NotLinked') { + WHERE testproject_id = {$tproject_id} + {$kwFilter} + GROUP BY TK.tcversion_id "; + + $subquery = " AND tcversion_id IN (" . + " SELECT FOXDOG.tcversion_id FROM + ( $sqlCount ) AS FOXDOG " . " WHERE FOXDOG.HITS=" . + count($keyword_id) . ")"; + $kwFilter = ''; + } + } elseif ($keyword_id > 0) { + $kwFilter = " AND keyword_id = {$keyword_id} "; + } + + $sql = " SELECT TK.testcase_id,TK.keyword_id,K.keyword + FROM {$this->tables['keywords']} K + JOIN {$this->tables['testcase_keywords']} TK + ON TK.keyword_id = K.id + {$ltcvJoin} + WHERE K.testproject_id = {$tproject_id} + {$kwFilter} {$subquery} + ORDER BY keyword ASC "; + + return $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', 'keyword_id'); + } + + /** + * + * 20200117 + * it seems I've duplicated code + * designed to be used by + * + * @used-by specview.php + */ + private function XXXgetPlatformsLatestTCV($tproject_id, $platform_id = 0, + $filterType = 'Or') + { + $platFilter = ''; + $subquery = ''; + $ltcvJoin = " JOIN {$this->views['latest_tcase_version_id']} LTCV + ON LTCV.tcversion_id = TK.tcversion_id "; + + if (is_array($platform_id)) { + $platFilter = " AND platform_id IN (" . implode(',', $platform_id) . + ")"; + if ($filterType == 'And') { + $ltcvJoin = " "; + $sqlCount = " /* SQL COUNT */ " . + " SELECT COUNT(TK.tcversion_id) AS HITS,TPL.tcversion_id + FROM {$this->tables['platforms']} K + JOIN {$this->tables['testcase_platforms']} TPL + ON platform_id = PL.id - $this->get_all_testcases_id($tproject_id,$tcaseSet); - if( ($hasTCases = count($tcaseSet) > 0) ) { - $delTT = true; - $tt = 'temp_tcset_' . $tproject_id . md5(microtime()); - $sql = "CREATE TEMPORARY TABLE IF NOT EXISTS $tt AS - ( SELECT id FROM {$this->tables['nodes_hierarchy']} - LIMIT 0 )"; - $this->db->exec_query($sql); - $a4ins = array_chunk($tcaseSet, 2000); // MAGIC - foreach($a4ins as $chu) { - $sql = "INSERT INTO $tt (id) VALUES (" . - implode('),(',$chu) . ")"; - $this->db->exec_query($sql); - } - } - } + JOIN {$this->views['latest_tcase_version_id']} LTCV + ON LTCV.tcversion_id = TPL.tcversion_id - if( $getWithOutKeywords && $hasTCases) { - $sql = " /* WITHOUT KEYWORDS */ + WHERE testproject_id = {$tproject_id} + {$platFilter} + GROUP BY TPL.tcversion_id "; + + $subquery = " AND tcversion_id IN (" . + " SELECT FOXDOG.tcversion_id FROM + ( $sqlCount ) AS FOXDOG " . " WHERE FOXDOG.HITS=" . + count($platform_id) . ")"; + $platFilter = ''; + } + } elseif ($platform_id > 0) { + $platFilter = " AND platform_id = {$platform_id} "; + } + + $sql = " SELECT TPL.testcase_id,TPL.keyword_id,PL.name + FROM {$this->tables['platforms']} K + JOIN {$this->tables['testcase_platforms']} TPL + ON TPL.platforms = PL.id + {$ltcvJoin} + WHERE PL.testproject_id = {$tproject_id} + {$platFilter} {$subquery} + ORDER BY name ASC "; + + return $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', 'platform_id'); + } + + /* + * function: get_all_testplans + * + * args : $testproject_id + * + * [$filters]: optional map, with optional keys + * [$get_tp_without_tproject_id] + * used just for backward compatibility (TL 1.5) + * default: 0 -> 1.6 and up behaviour + * + * [$plan_status] + * default: null -> no filter on test plan status + * 1 -> active test plans + * 0 -> inactive test plans + * + * [$exclude_tplans]: null -> do not apply exclusion + * id -> test plan id to exclude + * + * [options]: + * + * returns: + * + */ + public function get_all_testplans($id, $filters = null, $options = null) + { + $my['options'] = array( + 'fields2get' => 'NH.id,NH.name,notes,active, + is_public,testproject_id,api_key', + 'outputType' => null + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $forHMLSelect = false; + if (! is_null($my['options']['outputType']) && + $my['options']['outputType'] == 'forHMLSelect') { + $forHMLSelect = true; + $my['options']['fields2get'] = 'NH.id,NH.name'; + } + + $sql = " SELECT {$my['options']['fields2get']} " . + " FROM {$this->tables['nodes_hierarchy']} NH,{$this->tables['testplans']} TPLAN"; + + $where = " WHERE NH.id=TPLAN.id AND (testproject_id = " . + $this->db->prepare_int($id) . " "; + if (! is_null($filters)) { + $key2check = array( + 'get_tp_without_tproject_id' => 0, + 'plan_status' => null, + 'tplan2exclude' => null + ); + + foreach ($key2check as $varname => $defValue) { + $$varname = isset($filters[$varname]) ? $filters[$varname] : $defValue; + } + + $where .= " ) "; + + if (! is_null($plan_status)) { + $my_active = to_boolean($plan_status); + $where .= " AND active = " . $my_active; + } + + if (! is_null($tplan2exclude)) { + $where .= " AND TPLAN.id != {$tplan2exclude} "; + } + } else { + $where .= ")"; + } + + $sql .= $where . " ORDER BY name"; + if ($forHMLSelect) { + $map = $this->db->fetchColumnsIntoMap($sql, 'id', 'name'); + } else { + $map = $this->db->fetchRowsIntoMap($sql, 'id'); + } + + return $map; + } + + /* + * function: check_tplan_name_existence + * + * args : + * tproject_id: + * tplan_id: + * [case_sensitive]: 1-> do case sensitive search + * default: 0 + * + * returns: 1 -> tplan name exists + * + * + */ + public function check_tplan_name_existence($tproject_id, $tplan_name, + $case_sensitive = 0) + { + $sql = " SELECT NH.id, NH.name, testproject_id " . + " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['testplans']} testplans " . + " WHERE NH.id=testplans.id " . + " AND testproject_id = {$tproject_id} "; + + if ($case_sensitive) { + $sql .= " AND NH.name="; + } else { + $tplan_name = strtoupper($tplan_name); + $sql .= " AND UPPER(NH.name)="; + } + $sql .= "'" . $this->db->prepare_string($tplan_name) . "'"; + $result = $this->db->exec_query($sql); + return $this->db->num_rows($result) ? 1 : 0; + } + + /* + * function: gen_combo_first_level_test_suites + * create array with test suite names + * + * args : id: testproject_id + * [mode] + * + * returns: + * array, every element is a map + * + * rev : + * 20070219 - franciscom + * fixed bug when there are no children + * + */ + public function get_first_level_test_suites($tproject_id, $mode = 'simple', + $opt = null) + { + $fl = $this->tree_manager->get_children($tproject_id, + array( + 'testcase', + 'exclude_me', + 'testplan' => 'exclude_me', + 'requirement_spec' => 'exclude_me' + ), $opt); + switch ($mode) { + case 'simple': + break; + + case 'smarty_html_options': + if (! empty($fl)) { + foreach ($fl as $map) { + $dummy[$map['id']] = $map['name']; + } + $fl = null; + $fl = $dummy; + } + break; + } + return $fl; + } + + /** + * getTCasesLinkedToAnyTPlan + * + * for target test project id ($id) get test case id of + * every test case that has been assigned at least to one of all test plans + * belonging to test project. + * + * @param int $id + * test project id + * + */ + private function getTCasesLinkedToAnyTPlan($id) + { + $tplanNodeType = $this->tree_manager->node_descr_id['testplan']; + + // len of lines must be <= 100/110 as stated on development standard guide. + $sql = " SELECT DISTINCT NHTCV.parent_id AS testcase_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV " . + " ON NHTCV.id = TPTCV.tcversion_id "; + + // get testplan id for target test�project, to get test case versions linked to testplan. + $sql .= " JOIN {$this->tables['nodes_hierarchy']} NHTPLAN " . + " ON TPTCV.testplan_id = NHTPLAN.id " . + " WHERE NHTPLAN.node_type_id = {$tplanNodeType} AND NHTPLAN.parent_id = " . + intval($id); + return $this->db->fetchRowsIntoMap($sql, 'testcase_id'); + } + + /** + * getFreeTestCases + * + * + * @param int $id + * test project id + * @param $options for + * future uses. + */ + public function getFreeTestCases($id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $retval['items'] = null; + $retval['allfree'] = false; + + $all = array(); + $this->get_all_testcases_id($id, $all); + $linked = array(); + $free = null; + if (! is_null($all)) { + $all = array_flip($all); + $linked = $this->getTCasesLinkedToAnyTPlan($id); + $retval['allfree'] = is_null($linked); + $free = $retval['allfree'] ? $all : array_diff_key($all, $linked); + } + + if (! empty($free)) { + $in_clause = implode(',', array_keys($free)); + $sql = " /* $debugMsg */ " . + " SELECT MAX(TCV.version) AS version, TCV.tc_external_id, " . + " TCV.importance AS importance, NHTCV.parent_id AS id, NHTC.name " . + " FROM {$this->tables['tcversions']} TCV " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TCV.id " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " WHERE NHTCV.parent_id IN ({$in_clause}) " . + " GROUP BY NHTC.name,NHTCV.parent_id,TCV.tc_external_id,TCV.importance " . + " ORDER BY NHTCV.parent_id"; + $retval['items'] = $this->db->fetchRowsIntoMap($sql, 'id'); + } + + return $retval; + } + + // Custom field related methods + /* + * function: get_linked_custom_fields + * Get custom fields that has been linked to testproject. + * Search can be narrowed by: + * node type + * node id + * + * Important: + * custom fields id will be sorted based on the sequence number + * that can be specified at User Interface (UI) level, while + * linking is done. + * + * args : id: testproject id + * [node_type]: default: null -> no filter + * verbose string that identifies a node type. + * (see tree class, method get_available_node_types). + * Example: + * You want linked custom fields , but can be used + * only on testcase -> 'testcase'. + * + * returns: map. + * key: custom field id + * value: map (custom field definition) with following keys + * + * id (custom field id) + * name + * label + * type + * possible_values + * default_value + * valid_regexp + * length_min + * length_max + * show_on_design + * enable_on_design + * show_on_execution + * enable_on_execution + * display_order + * + * + */ + public function get_linked_custom_fields($id, $node_type = null, + $access_key = 'id') + { + $additional_table = ""; + $additional_join = ""; + + if (! is_null($node_type)) { + $hash_descr_id = $this->tree_manager->get_available_node_types(); + $node_type_id = $hash_descr_id[$node_type]; + + $additional_table = ",{$this->tables['cfield_node_types']} CFNT "; + $additional_join = " AND CFNT.field_id=CF.id AND CFNT.node_type_id={$node_type_id} "; + } + + $sql = "SELECT CF.*,CFTP.display_order " . + " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_testprojects']} CFTP " . + $additional_table . " WHERE CF.id=CFTP.field_id " . + " AND CFTP.testproject_id={$id} " . $additional_join . + " ORDER BY CFTP.display_order"; + return $this->db->fetchRowsIntoMap($sql, $access_key); + } + + /* + * function: copy_as + * creates a new test project using an existent one as source. + * + * + * args: id: source testproject id + * new_id: destination + * [new_name]: default null. + * != null => set this as the new name + * + * [copy_options]: default null + * null: do a deep copy => copy following child elements: + * test plans + * builds + * linked tcversions + * milestones + * user_roles + * priorities, + * platforms + * execution assignment. + * + * != null, a map with keys that controls what child elements to copy + * + * + * returns: N/A + * + * + */ + public function copy_as($id, $new_id, $user_id, $new_name = null, + $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $my['options'] = array( + 'copy_requirements' => 1, + 'copyUserRoles' => 1, + 'copy_platforms' => 1 + ); + $my['options'] = array_merge($my['options'], (array) $options); + + // get source test project general info + $rs_source = $this->get_by_id($id); + + if (! is_null($new_name)) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " . + "SET name='" . $this->db->prepare_string(trim($new_name)) . "' " . + "WHERE id={$new_id}"; + $this->db->exec_query($sql); + } + + // Copy elements that can be used by other elements + // Custom Field assignments + $this->copyCfieldsAssignments($id, $new_id); + + // Keywords + $oldNewMappings['keywords'] = $this->copyKeywords($id, $new_id); + + // Platforms + $oldNewMappings['platforms'] = $this->copyPlatforms($id, $new_id); + + // Requirements + if ($my['options']['copy_requirements']) { + list ($oldNewMappings['requirements'], $onReqSet) = $this->copyRequirements( + $id, $new_id, $user_id); + + // need to copy relations between requirements + $rel = null; + foreach ($oldNewMappings['requirements'] as $erek) { + foreach ($erek['req'] as $okey => $nkey) { + $sql = "/* $debugMsg */ SELECT id, source_id, destination_id," . + " relation_type, author_id, creation_ts " . + " FROM {$this->tables['req_relations']} " . + " WHERE source_id=$okey OR destination_id=$okey "; + $rel[$okey] = $this->db->get_recordset($sql); + } + } + + if (! is_null($rel)) { + $totti = $this->db->db_now(); + foreach ($rel as $ir) { + if (! is_null($ir)) { + foreach ($ir as $rval) { + if (isset($done[$rval['id']])) { + continue; + } + + $done[$rval['id']] = $rval['id']; + $sql = "/* $debugMsg */ + INSERT INTO {$this->tables['req_relations']} " . + " (source_id, destination_id, relation_type, author_id, creation_ts) " . + " values (" . $onReqSet[$rval['source_id']] . "," . + $onReqSet[$rval['destination_id']] . "," . + $rval['relation_type'] . "," . $rval['author_id'] . + "," . "$totti)"; + $this->db->exec_query($sql); + } + } + } + } + } + + // need to get subtree and create a new one + $filters = array(); + $filters['exclude_node_types'] = array( + 'testplan' => 'exclude_me', + 'requirement_spec' => 'exclude_me' + ); + $filters['exclude_children_of'] = array( + 'testcase' => 'exclude_me', + 'requirement' => 'exclude_me', + 'testcase_step' => 'exclude_me' + ); + + $elements = $this->tree_manager->get_children($id, + $filters['exclude_node_types']); + + // Copy Test Specification + $item_mgr['testsuites'] = new testsuite($this->db); + $copyTSuiteOpt = array(); + $copyTSuiteOpt['preserve_external_id'] = true; + $copyTSuiteOpt['copyKeywords'] = 1; + + // Attention: + // copyRequirements really means copy requirement to testcase assignments + $copyTSuiteOpt['copyRequirements'] = $my['options']['copy_requirements']; + + $oldNewMappings['test_spec'] = array(); + foreach ($elements as $piece) { + $op = $item_mgr['testsuites']->copy_to($piece['id'], $new_id, + $user_id, $copyTSuiteOpt, $oldNewMappings); + $oldNewMappings['test_spec'] += $op['mappings']; + } + + // Copy Test Plans and all related information + $this->copyTestplans($id, $new_id, $user_id, $oldNewMappings); + + $this->copyUserRoles($id, $new_id); + + // need to understand if we need to change this and + // PRESERVE External Test case ID + // + // When copying a project, external TC ID is not preserved + // need to update external test case id numerator + $sql = "/* $debugMsg */ UPDATE {$this->object_table} " . + " SET tc_counter = {$rs_source['tc_counter']} " . + " WHERE id = {$new_id}"; + $this->db->exec_query($sql); + } + + /** + * function to get an array with all requirement IDs in testproject + * + * @param string $idList + * commaseparated list of Container-IDs - can be testproject ID or reqspec IDs + * @return array $reqIDs result IDs + * + * @internal revisions: + * 20100310 - asimon - removed recursion logic + */ + public function get_all_requirement_ids($idList) + { + $coupleTypes = array(); + $coupleTypes['target'] = $this->tree_manager->node_descr_id['requirement']; + $coupleTypes['container'] = $this->tree_manager->node_descr_id['requirement_spec']; + + $reqIDs = array(); + $this->tree_manager->getAllItemsID($idList, $reqIDs, $coupleTypes); + + return $reqIDs; + } + + /** + * uses get_all_requirements_ids() to count all requirements in testproject + * + * @param integer $tp_id + * ID of testproject + * @return integer count of requirements in given testproject + */ + public function count_all_requirements($tp_id) + { + return count($this->get_all_requirement_ids($tp_id)); + } + + /** + * Copy user roles to a new Test Project + * + * @param int $source_id + * original Test Project identificator + * @param int $target_id + * new Test Project identificator + */ + private function copyUserRoles($source_id, $target_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $sql = "/* $debugMsg */ SELECT * FROM {$this->tables['user_testproject_roles']} " . + "WHERE testproject_id={$source_id} "; + $rs = $this->db->get_recordset($sql); + + if (! is_null($rs)) { + foreach ($rs as $elem) { + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['user_testproject_roles']} " . + "(testproject_id,user_id,role_id) " . "VALUES({$target_id}," . + $elem['user_id'] . "," . $elem['role_id'] . ")"; + $this->db->exec_query($sql); + } + } + } + + /** + * Copy platforms + * + * @param int $source_id + * original Test Project identificator + * @param int $target_id + * new Test Project identificator + */ + private function copyPlatforms($source_id, $target_id) + { + $platform_mgr = new tlPlatform($this->db, $source_id); + $old_new = null; + + $platformSet = $platform_mgr->getAll(); + + if (! is_null($platformSet)) { + $platform_mgr->setTestProjectID($target_id); + foreach ($platformSet as $platform) { + $item = new stdClass(); + $item->name = $platform['name']; + $item->notes = (string) $platform['notes']; + $item->enable_on_design = intval($platform['enable_on_design']); + $item->enable_on_execution = intval( + $platform['enable_on_execution']); + + $op = $platform_mgr->create($item); + $old_new[$platform['id']] = $op['id']; + } + } + return $old_new; + } + + /** + * Copy platforms + * + * @param int $source_id + * original Test Project identificator + * @param int $target_id + * new Test Project identificator + */ + private function copyKeywords($source_id, $target_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $old_new = null; + $sql = "/* $debugMsg */ SELECT * FROM {$this->tables['keywords']} " . + " WHERE testproject_id = {$source_id}"; + + $itemSet = $this->db->fetchRowsIntoMap($sql, 'id'); + if (! is_null($itemSet)) { + foreach ($itemSet as $item) { + $op = $this->addKeyword($target_id, $item['keyword'], + $item['notes']); + $old_new[$item['id']] = $op['id']; + } + } + return $old_new; + } + + /** + */ + private function copyCfieldsAssignments($source_id, $target_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT field_id FROM {$this->tables['cfield_testprojects']} " . + " WHERE testproject_id = {$source_id}"; + $row_set = $this->db->fetchRowsIntoMap($sql, 'field_id'); + if (! is_null($row_set)) { + $cfield_set = array_keys($row_set); + $this->cfield_mgr->link_to_testproject($target_id, $cfield_set); + } + } + + /** + */ + private function copyTestplans($source_id, $target_id, $user_id, $mappings) + { + static $tplanMgr; + + $tplanSet = $this->get_all_testplans($source_id); + if (! is_null($tplanSet)) { + $keySet = array_keys($tplanSet); + if (is_null($tplanMgr)) { + $tplanMgr = new testplan($this->db); + } + + foreach ($keySet as $itemID) { + $new_id = $tplanMgr->create($tplanSet[$itemID]['name'], + $tplanSet[$itemID]['notes'], $target_id, + $tplanSet[$itemID]['active'], + $tplanSet[$itemID]['is_public']); + + if ($new_id > 0) { + // TICKET 5190: Copy Test projects - tester assignments to testplan+build are not copied + $tplanMgr->copy_as($itemID, $new_id, null, $target_id, + $user_id, array( + 'copy_assigned_to' => 1 + ), $mappings); + } + } + } + } + + /** + */ + private function copyRequirements($source_id, $target_id, $user_id) + { + $mappings = null; + $or = array(); + + // need to get subtree and create a new one + $filters = array(); + $filters['exclude_node_types'] = array( + 'testplan' => 'exclude', + 'testcase' => 'exclude', + 'testsuite' => 'exclude', + 'requirement' => 'exclude' + ); + + $elements = $this->tree_manager->get_children($source_id, + $filters['exclude_node_types']); + + if (! is_null($elements)) { + $mappings = array(); + $reqSpecMgr = new requirement_spec_mgr($this->db); + + // Development Note - 20110817 + // why we choose to do not copy testcase_assignments ? + // Because due to order used to copy different items, + // when we ask to copy requirements WE DO NOT HAVE + // TEST CASES on new test project. + // + $options = array( + 'copy_also' => array( + 'testcase_assignments' => false + ), + 'caller' => 'copy_testproject' + ); + + foreach ($elements as $piece) { + $op = $reqSpecMgr->copy_to($piece['id'], $target_id, $target_id, + $user_id, $options); + + $mappings[] = $op['mappings']; + $or += $op['mappings']['req']; + } + } + + return array( + $mappings, + $or + ); + } + + /** + * getTestSpec + * + * get structure with Test suites and Test Cases + * Filters that act on test cases work on attributes that are common to all + * test cases versions: test case name + * + * Development Note: + * Due to the tree structure is not so easy to try to do as much as filter as + * possibile using SQL. + * + * + * @param + * int id test project ID + * @param + * mixed filters + * @param + * mixed options + * recursive true/false changes output format + * testcase_name filter in LIKE %string%, if will be case sensitive or not + * will depend of DBMS. + * + * + */ + private function getTestSpec($id, $filters = null, $options = null) + { + $items = array(); + + $my['options'] = array( + 'recursive' => false, + 'exclude_testcases' => false, + 'remove_empty_branches' => false + ); + + $my['filters'] = array( + 'exclude_node_types' => $this->nt2exclude, + 'exclude_children_of' => $this->nt2exclude_children, + 'exclude_branches' => null, + 'testcase_name' => null, + 'importance' => null, + 'testcase_id' => null, + 'execution_type' => null, + 'status' => null, + 'keywords' => null, + 'additionalWhereClause' => null, + 'platforms' => null + ); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + if ($my['options']['exclude_testcases']) { + $my['filters']['exclude_node_types']['testcase'] = 'exclude me'; + } + + // transform some of our options/filters on something the 'worker' will understand + // when user has request filter by test case name, we do not want to display empty branches + // If we have choose any type of filter, we need to force remove empty test suites + // TICKET 4217: added filter for importance + if (! is_null($my['filters']['testcase_name']) || + ! is_null($my['filters']['testcase_id']) || + ! is_null($my['filters']['execution_type']) || + ! is_null($my['filters']['exclude_branches']) || + ! is_null($my['filters']['importance']) || + $my['options']['remove_empty_branches']) { + $my['options']['remove_empty_nodes_of_type'] = 'testsuite'; + } + + $method2call = $my['options']['recursive'] ? 'getSubtreeRec' : '_get_subtree'; + + $this->$method2call($id, $items, $my['filters'], $my['options']); + return $items; + } + + /** + * + * @return + * + * @internal revisions + */ + private function getSubtreeRec($node_id, &$pnode, $filters = null, + $options = null) + { + static $qnum; + static $my; + static $exclude_branches; + static $exclude_children_of; + static $node_types; + static $tcaseFilter; + static $tcversionFilter; + static $staticSql; + static $inClause; + + if (! $my) { + $qnum = 0; + $node_types = array_flip( + $this->tree_manager->get_available_node_types()); + + $my['filters'] = array( + 'exclude_children_of' => null, + 'exclude_branches' => null, + 'additionalWhereClause' => '', + 'testcase_name' => null, + 'testcase_id' => null, + 'active_testcase' => false, + 'importance' => null, + 'status' => null + ); + + $my['options'] = array( + 'remove_empty_nodes_of_type' => null + ); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + $exclude_branches = $my['filters']['exclude_branches']; + $exclude_children_of = $my['filters']['exclude_children_of']; + + $tcaseFilter['name'] = ! is_null($my['filters']['testcase_name']); + $tcaseFilter['id'] = ! is_null($my['filters']['testcase_id']); + + $tcaseFilter['is_active'] = ! is_null( + $my['filters']['active_testcase']) && + $my['filters']['active_testcase']; + $tcaseFilter['enabled'] = $tcaseFilter['name'] || $tcaseFilter['id'] || + $tcaseFilter['is_active']; + + $actOnVersion = array( + 'execution_type', + 'importance', + 'status', + 'keywords', + 'platforms' + ); + foreach ($actOnVersion as $ck) { + $tcversionFilter[$ck] = ! is_null($my['filters'][$ck]); + } + + $tcversionFilter['enabled'] = false; + foreach ($actOnVersion as $target) { + $tcversionFilter['enabled'] = $tcversionFilter['enabled'] || + $tcversionFilter[$target]; + } + + // this way I can manage code or description + if (! is_null($my['options']['remove_empty_nodes_of_type']) && + ! is_numeric($my['options']['remove_empty_nodes_of_type'])) { + $my['options']['remove_empty_nodes_of_type'] = $this->tree_manager->node_descr_id[$my['options']['remove_empty_nodes_of_type']]; + } + + // Create invariant sql sentences + $tfields = "NH.id, NH.parent_id, NH.name, NH.node_type_id, NH.node_order, '' AS external_id "; + $staticSql = " SELECT DISTINCT {$tfields} " . + " FROM {$this->tables['nodes_hierarchy']} NH "; + + // Generate IN Clauses + $inClause = array( + 'status' => ' ', + 'importance' => ' ' + ); + + foreach ($inClause as $tgf => $dummy) { + if ($tcversionFilter[$tgf]) { + $inClause[$tgf] = " TCV.$tgf IN (" . + implode(',', $my['filters'][$tgf]) . ')'; + } + } + } + + $sql = $staticSql . " WHERE NH.parent_id = " . intval($node_id) . + " AND (" . + " NH.node_type_id = {$this->tree_manager->node_descr_id['testsuite']} " . + " OR (NH.node_type_id = {$this->tree_manager->node_descr_id['testcase']} "; + + if ($tcaseFilter['enabled']) { + foreach ($tcaseFilter as $key => $apply) { + if ($apply) { + switch ($key) { + case 'name': + $safe4DB = $this->db->prepare_string( + $my['filters']['testcase_name']); + $sql .= " AND NH.name LIKE '%{$safe4DB}%' "; + break; + + case 'id': + $safe4DB = intval($my['filters']['testcase_id']); + $sql .= " AND NH.id = {$safe4DB} "; + break; + } + } + } + } + $sql .= " )) "; + $sql .= " ORDER BY NH.node_order,NH.id"; + + // Approach Change - get all + $rs = (array) $this->db->fetchRowsIntoMap($sql, 'id'); + if (empty($rs)) { + return $qnum; + } + + // create list with test cases nodes + $tclist = null; + $ks = array_keys($rs); + foreach ($ks as $ikey) { + if ($rs[$ikey]['node_type_id'] == + $this->tree_manager->node_descr_id['testcase']) { + $tclist[$rs[$ikey]['id']] = $rs[$ikey]['id']; + } + } + if (! is_null($tclist)) { + $filterOnTC = false; + + // 2018, where is the active check? + + // Can be replace with a view? + $glvn = " /* Get LATEST ACTIVE tcversion NUMBER */ " . + " SELECT MAX(TCVX.version) AS version, NHTCX.parent_id AS tc_id " . + " FROM {$this->tables['tcversions']} TCVX " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCX " . + " ON NHTCX.id = TCVX.id AND TCVX.active = 1 " . + " WHERE NHTCX.parent_id IN (" . implode(',', $tclist) . ")" . + " GROUP BY NHTCX.parent_id"; + + // 2018, again where is the active check? + $ssx = " /* Get LATEST ACTIVE tcversion MAIN ATTRIBUTES */ " . + " SELECT TCV.id AS tcversion_id, TCV.tc_external_id AS external_id, SQ.tc_id " . + " FROM {$this->tables['nodes_hierarchy']} NHTCV " . + " JOIN ( $glvn ) SQ " . " ON NHTCV.parent_id = SQ.tc_id " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON NHTCV.id = TCV.id "; + + // 2018 + $where = " WHERE SQ.version = TCV.version "; + + // We can add here keyword filtering if exist ? + if ($tcversionFilter['enabled'] || $tcaseFilter['is_active']) { + $addAnd = false; + if ($tcversionFilter['importance'] || + $tcversionFilter['execution_type'] || + $tcversionFilter['status']) { + $where .= " AND "; + } + + if ($tcversionFilter['importance']) { + $where .= $inClause['importance']; + $filterOnTC = true; + $addAnd = true; + } + + if ($addAnd && $tcversionFilter['execution_type']) { + $where .= " AND "; + } + + if ($tcversionFilter['execution_type']) { + $where .= " TCV.execution_type = " . + $my['filters']['execution_type']; + $filterOnTC = true; + $addAnd = true; + } + + if ($addAnd && $tcversionFilter['status']) { + $where .= " AND "; + } + + if ($tcversionFilter['status']) { + $where .= $inClause['status']; + $filterOnTC = true; + $addAnd = true; + } + } + + $ssx .= $where; + + $highlander = $this->db->fetchRowsIntoMap($ssx, 'tc_id'); + if ($filterOnTC) { + $ky = ! is_null($highlander) ? array_diff_key($tclist, + $highlander) : $tclist; + if (! empty($ky)) { + foreach ($ky as $tcase) { + unset($rs[$tcase]); + } + } + } + } + + foreach ($rs as $row) { + if (! isset($exclude_branches[$row['id']])) { + $node = $row + + array( + 'node_table' => $this->tree_manager->node_tables_by['id'][$row['node_type_id']] + ); + $node['childNodes'] = null; + + if ($node['node_table'] == 'testcases') { + $node['leaf'] = true; + $node['external_id'] = isset($highlander[$row['id']]) ? $highlander[$row['id']]['external_id'] : null; + } + + // why we use exclude_children_of ? + // 1. Sometimes we don't want the children if the parent is a testcase, + // due to the version management + // + if (! isset( + $exclude_children_of[$node_types[$row['node_type_id']]])) { + // Keep walking (Johny Walker Whisky) + $this->getSubtreeRec($row['id'], $node, $my['filters'], + $my['options']); + } + + // Have added this logic, because when export test plan will be developed + // having a test spec tree where test suites that do not contribute to test plan + // are pruned/removed is very important, to avoid additional processing + // + // If node has no childNodes, we check if this kind of node without children + // can be removed. + // + $doRemove = is_null($node['childNodes']) && + ($node['node_type_id'] == + $my['options']['remove_empty_nodes_of_type']); + if (! $doRemove) { + $pnode['childNodes'][] = $node; + } + } // if(!isset($exclude_branches[$rowID])) + } // while + + return $qnum; + } + + /** + * -1 => WITHOUT KEYWORDS + */ + protected function getTCLatestVersionFilteredByKeywords($tproject_id, + $keyword_id = 0, $keyword_filter_type = 'Or') + { + $keySet = (array) $keyword_id; + $sql = null; + $tcaseSet = array(); + $delTT = false; + $hasTCases = false; + + // -1 => WITHOUT KEYWORDS + $getWithOutKeywords = in_array(- 1, $keySet); + if ($getWithOutKeywords || $keyword_filter_type == 'NotLinked') { + + $this->get_all_testcases_id($tproject_id, $tcaseSet); + if ($hasTCases = ! empty($tcaseSet)) { + $delTT = true; + $tt = 'temp_tcset_' . $tproject_id . md5(microtime()); + $sql = "CREATE TEMPORARY TABLE IF NOT EXISTS $tt AS + ( SELECT id FROM {$this->tables['nodes_hierarchy']} + LIMIT 0 )"; + $this->db->exec_query($sql); + $a4ins = array_chunk($tcaseSet, 2000); // MAGIC + foreach ($a4ins as $chu) { + $sql = "INSERT INTO $tt (id) VALUES (" . implode('),(', $chu) . + ")"; + $this->db->exec_query($sql); + } + } + } + + if ($getWithOutKeywords && $hasTCases) { + $sql = " /* WITHOUT KEYWORDS */ SELECT TCVNO_KW.testcase_id FROM - {$this->views['tcversions_without_keywords']} TCVNO_KW + {$this->views['tcversions_without_keywords']} TCVNO_KW JOIN {$this->views['latest_tcase_version_id']} LTVC ON LTVC.tcversion_id = TCVNO_KW.id - JOIN $tt TT ON TT.id = TCVNO_KW.testcase_id "; - } else { - $kwFilter = " keyword_id IN (" . implode(',',$keySet) . ")"; - switch($keyword_filter_type) { - case 'NotLinked': - if($hasTCases) { - $sql = " /* WITHOUT SPECIFIC KEYWORDS */ - SELECT NHTCV.parent_id AS testcase_id - FROM {$this->tables['nodes_hierarchy']} NHTCV - JOIN {$this->views['latest_tcase_version_id']} LTCV - ON NHTCV.id = LTCV.tcversion_id - JOIN $tt TT ON TT.id = NHTCV.parent_id + JOIN $tt TT ON TT.id = TCVNO_KW.testcase_id "; + } else { + $kwFilter = " keyword_id IN (" . implode(',', $keySet) . ")"; + switch ($keyword_filter_type) { + case 'NotLinked': + if ($hasTCases) { + $sql = " /* WITHOUT SPECIFIC KEYWORDS */ + SELECT NHTCV.parent_id AS testcase_id + FROM {$this->tables['nodes_hierarchy']} NHTCV + JOIN {$this->views['latest_tcase_version_id']} LTCV + ON NHTCV.id = LTCV.tcversion_id + JOIN $tt TT ON TT.id = NHTCV.parent_id WHERE NOT EXISTS - (SELECT 1 FROM {$this->tables['testcase_keywords']} TCK - WHERE TCK.tcversion_id = LTCV.tcversion_id - AND {$kwFilter} )"; - } - break; - - - case 'And': - // MAX(TK.testcase_id) needed to be able to extract - // Test case id. - $sqlCount = " /* SQL COUNT */ " . - " SELECT COUNT(TK.tcversion_id) AS HITS, + (SELECT 1 FROM {$this->tables['testcase_keywords']} TCK + WHERE TCK.tcversion_id = LTCV.tcversion_id + AND {$kwFilter} )"; + } + break; + + case 'And': + // MAX(TK.testcase_id) needed to be able to extract + // Test case id. + $sqlCount = " /* SQL COUNT */ " . + " SELECT COUNT(TK.tcversion_id) AS HITS, MAX(TK.testcase_id) AS testcase_id, TK.tcversion_id FROM {$this->tables['keywords']} KW @@ -3399,667 +3457,627 @@ function getTCLatestVersionFilteredByKeywords($tproject_id, $keyword_id=0, $keyw ON LTCV.tcversion_id = TK.tcversion_id WHERE testproject_id = {$tproject_id} AND {$kwFilter} - GROUP BY TK.tcversion_id "; - - $sql = "/* Filter Type = AND */ - SELECT FOXDOG.testcase_id - FROM ( $sqlCount ) AS FOXDOG - WHERE FOXDOG.HITS=" . count($keyword_id); - break; - - - case 'Or': - default: - $sql = " /* Filter Type = OR */ " . - " SELECT TK.testcase_id " . - " FROM {$this->tables['testcase_keywords']} TK" . - " JOIN {$this->views['latest_tcase_version_id']} LTVC " . - " ON LTVC.tcversion_id = TK.tcversion_id " . - " JOIN {$this->tables['keywords']} KW " . - " ON KW.id = TK.keyword_id " . - " WHERE {$kwFilter} " . - " AND KW.testproject_id=" . $tproject_id; - break; - } - } - - $hits = !is_null($sql) ? $this->db->fetchRowsIntoMap($sql,'testcase_id') : null; - - // clean up - if( $delTT ) { - $sql = "DROP TABLE IF EXISTS $tt"; - $this->db->exec_query($sql); - } - - return $hits; -} - - - - -/** - * - * - * @internal revisions - * @since 1.9.4 - * - */ -function isIssueTrackerEnabled($id) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - "SELECT issue_tracker_enabled FROM {$this->object_table} " . - "WHERE id =" . intval($id); - - $ret = $this->db->get_recordset($sql); - return $ret[0]['issue_tracker_enabled']; -} - - - -/** - * - * - * @internal revisions - * @since 1.9.4 - * - */ -function enableIssueTracker($id) -{ - $this->setIssueTrackerEnabled($id,1); -} - -/** - * - * - * @internal revisions - * @since 1.9.4 - * - */ -function disableIssueTracker($id) -{ - $this->setIssueTrackerEnabled($id,0); -} - - -/** - * - * - * @internal revisions - * @since 1.9.4 - * - */ -function setIssueTrackerEnabled($id,$value) -{ - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " UPDATE {$this->object_table} " . - " SET issue_tracker_enabled = " . (intval($value) > 0 ? 1 : 0) . - " WHERE id =" . intval($id); - $ret = $this->db->exec_query($sql); -} - - -/** - * - * - */ -function isCodeTrackerEnabled($id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - "SELECT code_tracker_enabled FROM {$this->object_table} " . - "WHERE id =" . intval($id); - - $ret = $this->db->get_recordset($sql); - return $ret[0]['code_tracker_enabled']; -} - - - -/** - * - * - * @internal revisions - * @since 1.9.17 - * - */ -function enableCodeTracker($id) -{ - $this->setCodeTrackerEnabled($id,1); -} - -/** - * - * - * @internal revisions - * @since 1.9.17 - * - */ -function disableCodeTracker($id) -{ - $this->setCodeTrackerEnabled($id,0); -} - - -/** - * - * - * @internal revisions - * @since 1.9.17 - * - */ -function setCodeTrackerEnabled($id,$value) -{ - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " UPDATE {$this->object_table} " . - " SET code_tracker_enabled = " . (intval($value) > 0 ? 1 : 0) . - " WHERE id =" . intval($id); - $ret = $this->db->exec_query($sql); -} - -/** - * - */ -function getItemCount() { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT COUNT(0) AS qty FROM {$this->object_table} "; - $ret = $this->db->get_recordset($sql); - return $ret[0]['qty']; -} - -/** - * - */ -function getPublicAttr($id) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " SELECT is_public FROM {$this->object_table} " . - " WHERE id =" . intval($id); - - $ret = $this->db->get_recordset($sql); - if(is_null($ret)) - { - throw new Exception("Test Project ID does not exist!", 1); - } - return $ret[0]['is_public']; -} - - - - - /** - * Gets test cases created per user. - * The test cases are restricted to a test project. - * - * Optional values may be passed in the options array. - * - * @param integer $user_id User ID - * @param integer $tproject_id Test Project ID - * @param mixed $options Optional array of options - * @return mixed Array of test cases created per user - */ - function getTestCasesCreatedByUser($id,$user_id,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $opt = array('startTime' => null, 'endTime' => null); - $opt = array_merge($opt,(array)$options); - $safe = array('user_id' => intval($user_id), 'tproject_id' => intval($id)); - - $cfg = config_get('testcase_cfg'); - $eid = $this->db->db->concat('TPROJ.prefix',"'{$cfg->glue_character}'",'TCV.tc_external_id'); - - // - $target = array(); - $this->get_all_testcases_id($id,$target); - $itemQty = count($target); - - $rs = null; - if($itemQty > 0) - { - $sql = " /* $debugMsg */ SELECT TPROJ.id AS tproject_id, TCV.id AS tcversion_id," . - " TCV.version, {$eid} AS external_id, NHTC.id AS tcase_id, NHTC.name AS tcase_name, ". - " TCV.creation_ts, TCV.modification_ts, " . - " U.first AS first_name, U.last AS last_name, U.login, ". - " TCV.importance " . - " FROM {$this->tables['testprojects']} TPROJ,{$this->tables['nodes_hierarchy']} NHTC " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.parent_id = NHTC.id " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - " JOIN {$this->tables['users']} U ON U.id = TCV.author_id " . - " WHERE TPROJ.id = {$safe['tproject_id']} " . - " AND NHTC.id IN (" . implode(',', $target) . ")"; - - if($user_id !== 0) - { - $sql .= " AND U.id = {$safe['user_id']}"; - } - if( !is_null($opt['startTime']) ) - { - $sql .= " AND TCV.creation_ts >= '{$opt['startTime']}'"; - } - if( !is_null($opt['endTime']) ) - { - $sql .= " AND TCV.creation_ts <= '{$opt['endTime']}'"; - } - - $rs = $this->db->fetchRowsIntoMap($sql,'tcase_id',database::CUMULATIVE); - if( !is_null($rs) ) - { - $k2g = array_keys($rs); - $path_info = $this->tree_manager->get_full_path_verbose($k2g,array('output_format' => 'path_as_string')); - foreach($k2g as $tgx) - { - $rx = array_keys($rs[$tgx]); - foreach($rx as $ex) - { - $rs[$tgx][$ex]['path'] = $path_info[$tgx]; - } - } - } - } - return $rs; - } - - - /** - * - * @since 1.9.6 - * - * @internal revisions - * - */ - function isReqMgrIntegrationEnabled($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $targetField = 'reqmgr_integration_enabled'; - $sql = "/* $debugMsg */ " . - "SELECT {$targetField} FROM {$this->object_table} " . - "WHERE id =" . intval($id); - - $ret = $this->db->get_recordset($sql); - return $ret[0][$targetField]; - } - - /** - * - * @since 1.9.6 - * - * @internal revisions - * - */ - function enableReqMgrIntegration($id) - { - $this->setOneZeroField($id,'reqmgr_integration_enabled',1); - } - - /** - * - * @since 1.9.6 - * - * @internal revisions - * - */ - function disableReqMgrIntegration($id) - { - $this->setOneZeroField($id,'reqmgr_integration_enabled',0); - } - - function setReqMgrIntegrationEnabled($id,$value) - { - $this->setOneZeroField($id,'reqmgr_integration_enabled',$value); - } - - /** - * - * - * @internal revisions - * @since 1.9.4 - * - */ - function setOneZeroField($id,$field,$value) - { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ " . - " UPDATE {$this->object_table} " . - " SET {$field} = " . (intval($value) > 0 ? 1 : 0) . - " WHERE id =" . intval($id); - $ret = $this->db->exec_query($sql); - } - - - /** - * - * - * @internal revisions - * @since 1.9.4 - * - */ - function getByChildID($child) - { - $path = $this->tree_manager->get_path($child); - return $this->get_by_id(intval($path[0]['parent_id'])); - } - - /** - * @internal revisions - * @since 1.9.8 - */ - function setActive($id) - { - $this->setOneZeroField($id,'active',1); - } - - /** - * @internal revisions - * @since 1.9.8 - */ - function setInactive($id) - { - $this->setOneZeroField($id,'active',0); - } - - /** - * - */ - function simplexml_load_file_helper($filename) - { - // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html - libxml_disable_entity_loader(true); - $zebra = file_get_contents($filename); - $xml = @simplexml_load_string($zebra); - return $xml; - } - - - /** - * - * @used-by containerEdit.php - */ - function getFileUploadRelativeURL($id) - { - // I've to use testsuiteID because this is how is name on containerEdit.php - $url = "lib/testcases/containerEdit.php?containerType=testproject&doAction=fileUpload&tprojectID=" . intval($id); - return $url; - } - - /** - * @used-by containerEdit.php - */ - function getDeleteAttachmentRelativeURL($id) - { - // I've to use testsuiteID because this is how is name on containerEdit.php - $url = "lib/testcases/containerEdit.php?containerType=testproject&doAction=deleteFile&tprojectID=" . intval($id) . - "&file_id=" ; - return $url; - } - - - - /** - * @used-by projectEdit.php - */ - function enableRequirements($id) { - $debugMsg = $this->debugMsg . __FUNCTION__; - $opt = $this->getOptions($safeID = intval($id)); - $opt->requirementsEnabled = 1; - $this->setOptions($safeID,$opt); - } - - /** - * @used-by projectEdit.php - */ - function disableRequirements($id) - { - $debugMsg = $this->debugMsg . __FUNCTION__; - $opt = $this->getOptions($safeID = intval($id)); - $opt->requirementsEnabled = 0; - $this->setOptions($safeID,$opt); - } - - - /** - * @used-by - */ - function getOptions($id) { - $debugMsg = $this->debugMsg . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT testprojects.options ". - " FROM {$this->object_table} testprojects " . - " WHERE testprojects.id = " . intval($id); - $rs = $this->db->get_recordset($sql); - return unserialize($rs[0]['options']); - } - - /** - * @used-by - */ - function setOptions($id,$optObj) - { - $debugMsg = $this->debugMsg . __FUNCTION__; - - $nike = false; - $itemOpt = $this->getOptions( ($safeID = intval($id)) ); - foreach($itemOpt as $prop => $value) - { - if( property_exists($optObj, $prop) ) - { - $itemOpt->$prop = $optObj->$prop; - $nike = true; - } - } - - if($nike) - { - $sql = "/* $debugMsg */ UPDATE {$this->object_table} " . - " SET options = '" . $this->db->prepare_string(serialize($itemOpt)) . "'" . - " WHERE id = " . $safeID; - - $this->db->exec_query($sql); - } - } - - -/** - * - */ -function getActiveTestPlansCount($id) -{ - $debugMsg = $this->debugMsg . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT COUNT(0) AS qty". - " FROM {$this->tables['nodes_hierarchy']} NH_TPLAN " . - " JOIN {$this->tables['testplans']} TPLAN ON NH_TPLAN.id = TPLAN.id " . - " WHERE NH_TPLAN.parent_id = " . $this->db->prepare_int($id) . - " AND TPLAN.active = 1"; - - $rs = $this->db->get_recordset($sql); - return $rs[0]['qty']; -} - - /** - * - */ - static function getAPIKey(&$dbh,$id) { - $sch = tlDBObject::getDBTables('testprojects'); - $sql = "SELECT api_key FROM {$sch['testprojects']} WHERE id=" . intval($id); - $rs = $dbh->get_recordset($sql); - - return is_null($rs) ? $rs : $rs[0]['api_key']; - } - - - /** - * - */ - function checkKeywordIsLinkedAndNotExecuted($keyword_id,$tproject_id=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $whereAdd = ''; - $sql = " SELECT id,keyword FROM {$this->tables['keywords']} KW - WHERE id = {$keyword_id} "; - - if( null != $tproject_id ) { - $whereAdd = " AND testproject_id = " . intval($tproject_id); - } - $sql .= $whereAdd; - - $rs = $this->db->get_recordset($sql); - if( is_null($rs) ) { - return null; - } - - // Now try to understand if it is linked - if( !is_null($rs) ) { - $sql = "/* $debugMsg */ + GROUP BY TK.tcversion_id "; + + $sql = "/* Filter Type = AND */ + SELECT FOXDOG.testcase_id + FROM ( $sqlCount ) AS FOXDOG + WHERE FOXDOG.HITS=" . count($keyword_id); + break; + + case 'Or': + default: + $sql = " /* Filter Type = OR */ " . " SELECT TK.testcase_id " . + " FROM {$this->tables['testcase_keywords']} TK" . + " JOIN {$this->views['latest_tcase_version_id']} LTVC " . + " ON LTVC.tcversion_id = TK.tcversion_id " . + " JOIN {$this->tables['keywords']} KW " . + " ON KW.id = TK.keyword_id " . " WHERE {$kwFilter} " . + " AND KW.testproject_id=" . $tproject_id; + break; + } + } + + $hits = ! is_null($sql) ? $this->db->fetchRowsIntoMap($sql, + 'testcase_id') : null; + + // clean up + if ($delTT) { + $sql = "DROP TABLE IF EXISTS $tt"; + $this->db->exec_query($sql); + } + + return $hits; + } + + /** + * + * @internal revisions + * @since 1.9.4 + * + */ + public function isIssueTrackerEnabled($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + "SELECT issue_tracker_enabled FROM {$this->object_table} " . + "WHERE id =" . intval($id); + + $ret = $this->db->get_recordset($sql); + return $ret[0]['issue_tracker_enabled']; + } + + /** + * + * @internal revisions + * @since 1.9.4 + * + */ + public function enableIssueTracker($id) + { + $this->setIssueTrackerEnabled($id, 1); + } + + /** + * + * @internal revisions + * @since 1.9.4 + * + */ + public function disableIssueTracker($id) + { + $this->setIssueTrackerEnabled($id, 0); + } + + /** + * + * @internal revisions + * @since 1.9.4 + * + */ + public function setIssueTrackerEnabled($id, $value) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . " UPDATE {$this->object_table} " . + " SET issue_tracker_enabled = " . (intval($value) > 0 ? 1 : 0) . + " WHERE id =" . intval($id); + $this->db->exec_query($sql); + } + + /** + */ + public function isCodeTrackerEnabled($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + "SELECT code_tracker_enabled FROM {$this->object_table} " . + "WHERE id =" . intval($id); + + $ret = $this->db->get_recordset($sql); + return $ret[0]['code_tracker_enabled']; + } + + /** + * + * @internal revisions + * @since 1.9.17 + * + */ + public function enableCodeTracker($id) + { + $this->setCodeTrackerEnabled($id, 1); + } + + /** + * + * @internal revisions + * @since 1.9.17 + * + */ + public function disableCodeTracker($id) + { + $this->setCodeTrackerEnabled($id, 0); + } + + /** + * + * @internal revisions + * @since 1.9.17 + * + */ + public function setCodeTrackerEnabled($id, $value) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . " UPDATE {$this->object_table} " . + " SET code_tracker_enabled = " . (intval($value) > 0 ? 1 : 0) . + " WHERE id =" . intval($id); + $this->db->exec_query($sql); + } + + /** + */ + public function getItemCount() + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT COUNT(0) AS qty FROM {$this->object_table} "; + $ret = $this->db->get_recordset($sql); + return $ret[0]['qty']; + } + + /** + */ + public function getPublicAttr($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . + " SELECT is_public FROM {$this->object_table} " . " WHERE id =" . + intval($id); + + $ret = $this->db->get_recordset($sql); + if (is_null($ret)) { + throw new Exception("Test Project ID does not exist!", 1); + } + return $ret[0]['is_public']; + } + + /** + * Gets test cases created per user. + * The test cases are restricted to a test project. + * + * Optional values may be passed in the options array. + * + * @param integer $user_id + * User ID + * @param integer $tproject_id + * Test Project ID + * @param mixed $options + * Optional array of options + * @return mixed Array of test cases created per user + */ + public function getTestCasesCreatedByUser($id, $user_id, $options = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $opt = array( + 'startTime' => null, + 'endTime' => null + ); + $opt = array_merge($opt, (array) $options); + $safe = array( + 'user_id' => intval($user_id), + 'tproject_id' => intval($id) + ); + + $cfg = config_get('testcase_cfg'); + $eid = $this->db->db->concat('TPROJ.prefix', "'{$cfg->glue_character}'", + 'TCV.tc_external_id'); + + // + $target = array(); + $this->get_all_testcases_id($id, $target); + $itemQty = count($target); + + $rs = null; + if ($itemQty > 0) { + $sql = " /* $debugMsg */ SELECT TPROJ.id AS tproject_id, TCV.id AS tcversion_id," . + " TCV.version, {$eid} AS external_id, NHTC.id AS tcase_id, NHTC.name AS tcase_name, " . + " TCV.creation_ts, TCV.modification_ts, " . + " U.first AS first_name, U.last AS last_name, U.login, " . + " TCV.importance " . + " FROM {$this->tables['testprojects']} TPROJ,{$this->tables['nodes_hierarchy']} NHTC " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.parent_id = NHTC.id " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " JOIN {$this->tables['users']} U ON U.id = TCV.author_id " . + " WHERE TPROJ.id = {$safe['tproject_id']} " . " AND NHTC.id IN (" . + implode(',', $target) . ")"; + + if ($user_id !== 0) { + $sql .= " AND U.id = {$safe['user_id']}"; + } + if (! is_null($opt['startTime'])) { + $sql .= " AND TCV.creation_ts >= '{$opt['startTime']}'"; + } + if (! is_null($opt['endTime'])) { + $sql .= " AND TCV.creation_ts <= '{$opt['endTime']}'"; + } + + $rs = $this->db->fetchRowsIntoMap($sql, 'tcase_id', + database::CUMULATIVE); + if (! is_null($rs)) { + $k2g = array_keys($rs); + $path_info = $this->tree_manager->get_full_path_verbose($k2g, + array( + 'output_format' => 'path_as_string' + )); + foreach ($k2g as $tgx) { + $rx = array_keys($rs[$tgx]); + foreach ($rx as $ex) { + $rs[$tgx][$ex]['path'] = $path_info[$tgx]; + } + } + } + } + return $rs; + } + + /** + * + * @since 1.9.6 + * + * @internal revisions + * + */ + private function isReqMgrIntegrationEnabled($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $targetField = 'reqmgr_integration_enabled'; + $sql = "/* $debugMsg */ " . + "SELECT {$targetField} FROM {$this->object_table} " . "WHERE id =" . + intval($id); + + $ret = $this->db->get_recordset($sql); + return $ret[0][$targetField]; + } + + /** + * + * @since 1.9.6 + * + * @internal revisions + * + */ + private function enableReqMgrIntegration($id) + { + $this->setOneZeroField($id, 'reqmgr_integration_enabled', 1); + } + + /** + * + * @since 1.9.6 + * + * @internal revisions + * + */ + private function disableReqMgrIntegration($id) + { + $this->setOneZeroField($id, 'reqmgr_integration_enabled', 0); + } + + public function setReqMgrIntegrationEnabled($id, $value) + { + $this->setOneZeroField($id, 'reqmgr_integration_enabled', $value); + } + + /** + * + * @internal revisions + * @since 1.9.4 + * + */ + private function setOneZeroField($id, $field, $value) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ " . " UPDATE {$this->object_table} " . + " SET {$field} = " . (intval($value) > 0 ? 1 : 0) . " WHERE id =" . + intval($id); + $this->db->exec_query($sql); + } + + /** + * + * @internal revisions + * @since 1.9.4 + * + */ + public function getByChildID($child) + { + $path = $this->tree_manager->get_path($child); + return $this->get_by_id(intval($path[0]['parent_id'])); + } + + /** + * + * @internal revisions + * @since 1.9.8 + */ + public function setActive($id) + { + $this->setOneZeroField($id, 'active', 1); + } + + /** + * + * @internal revisions + * @since 1.9.8 + */ + public function setInactive($id) + { + $this->setOneZeroField($id, 'active', 0); + } + + /** + */ + private function simplexmlLoadFileHelper($filename) + { + // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + libxml_disable_entity_loader(true); + $zebra = file_get_contents($filename); + return @simplexml_load_string($zebra); + } + + /** + * + * @used-by containerEdit.php + */ + public function getFileUploadRelativeURL($id) + { + // I've to use testsuiteID because this is how is name on containerEdit.php + return "lib/testcases/containerEdit.php?containerType=testproject&doAction=fileUpload&tprojectID=" . + intval($id); + } + + /** + * + * @used-by containerEdit.php + */ + public function getDeleteAttachmentRelativeURL($id) + { + // I've to use testsuiteID because this is how is name on containerEdit.php + return "lib/testcases/containerEdit.php?containerType=testproject&doAction=deleteFile&tprojectID=" . + intval($id) . "&file_id="; + } + + /** + * + * @used-by projectEdit.php + */ + public function enableRequirements($id) + { + $opt = $this->getOptions($safeID = intval($id)); + $opt->requirementsEnabled = 1; + $this->setOptions($safeID, $opt); + } + + /** + * + * @used-by projectEdit.php + */ + public function disableRequirements($id) + { + $opt = $this->getOptions($safeID = intval($id)); + $opt->requirementsEnabled = 0; + $this->setOptions($safeID, $opt); + } + + /** + * + * @used-by + */ + public function getOptions($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT testprojects.options " . + " FROM {$this->object_table} testprojects " . + " WHERE testprojects.id = " . intval($id); + $rs = $this->db->get_recordset($sql); + return unserialize($rs[0]['options']); + } + + /** + * + * @used-by + */ + private function setOptions($id, $optObj) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $nike = false; + $itemOpt = $this->getOptions(($safeID = intval($id))); + foreach ($itemOpt as $prop => $value) { + if (property_exists($optObj, $prop)) { + $itemOpt->$prop = $optObj->$prop; + $nike = true; + } + } + + if ($nike) { + $sql = "/* $debugMsg */ UPDATE {$this->object_table} " . + " SET options = '" . + $this->db->prepare_string(serialize($itemOpt)) . "'" . + " WHERE id = " . $safeID; + + $this->db->exec_query($sql); + } + } + + /** + */ + public function getActiveTestPlansCount($id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT COUNT(0) AS qty" . + " FROM {$this->tables['nodes_hierarchy']} NH_TPLAN " . + " JOIN {$this->tables['testplans']} TPLAN ON NH_TPLAN.id = TPLAN.id " . + " WHERE NH_TPLAN.parent_id = " . $this->db->prepare_int($id) . + " AND TPLAN.active = 1"; + + $rs = $this->db->get_recordset($sql); + return $rs[0]['qty']; + } + + /** + */ + public static function getAPIKey(&$dbh, $id) + { + $sch = tlDBObject::getDBTables('testprojects'); + $sql = "SELECT api_key FROM {$sch['testprojects']} WHERE id=" . + intval($id); + $rs = $dbh->get_recordset($sql); + + return is_null($rs) ? $rs : $rs[0]['api_key']; + } + + /** + */ + private function checkKeywordIsLinkedAndNotExecuted($keyword_id, + $tproject_id = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $whereAdd = ''; + $sql = " SELECT id,keyword FROM {$this->tables['keywords']} KW + WHERE id = {$keyword_id} "; + + if (null != $tproject_id) { + $whereAdd = " AND testproject_id = " . intval($tproject_id); + } + $sql .= $whereAdd; + + $rs = $this->db->get_recordset($sql); + if (is_null($rs)) { + return null; + } + + // Now try to understand if it is linked + if (! is_null($rs)) { + $sql = "/* $debugMsg */ SELECT DISTINCT keyword_id,keyword, - CASE + CASE WHEN EX.status IS NULL THEN 'NOT_RUN' ELSE 'EXECUTED' - END AS exec_status + END AS exec_status FROM {$this->tables['keywords']} KW JOIN {$this->tables['testcase_keywords']} TCKW ON TCKW.keyword_id = KW.id - LEFT OUTER JOIN {$this->tables['executions']} EX + LEFT OUTER JOIN {$this->tables['executions']} EX ON EX.tcversion_id = TCKW.tcversion_id - - WHERE KW.id = {$keyword_id} {$whereAdd} "; - } - $rs = $this->db->fetchRowsIntoMap($sql,'exec_status'); - $rs = (array)$rs; - return isset($rs['EXECUTED']) ? 0 : 1; - } - - - /** - * - */ - function checkKeywordIsLinkedToFrozenVersions($keyword_id,$tproject_id=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $whereAdd = ''; - $sql = " SELECT id,keyword FROM {$this->tables['keywords']} KW - WHERE id = {$keyword_id} "; - - if( null != $tproject_id ) { - $whereAdd = " AND testproject_id = " . intval($tproject_id); - } - $sql .= $whereAdd; - - $rs = $this->db->get_recordset($sql); - if( is_null($rs) ) { - return null; - } - - if( !is_null($rs) ) { - $sql = "/* $debugMsg */ + WHERE KW.id = {$keyword_id} {$whereAdd} "; + } + $rs = $this->db->fetchRowsIntoMap($sql, 'exec_status'); + + $rs = (array) $rs; + return isset($rs['EXECUTED']) ? 0 : 1; + } + + /** + */ + private function checkKeywordIsLinkedToFrozenVersions($keyword_id, + $tproject_id = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $whereAdd = ''; + $sql = " SELECT id,keyword FROM {$this->tables['keywords']} KW + WHERE id = {$keyword_id} "; + + if (null != $tproject_id) { + $whereAdd = " AND testproject_id = " . intval($tproject_id); + } + $sql .= $whereAdd; + + $rs = $this->db->get_recordset($sql); + if (is_null($rs)) { + return null; + } + + if (! is_null($rs)) { + $sql = "/* $debugMsg */ SELECT DISTINCT keyword_id,keyword, - CASE + CASE WHEN TCV.is_open=0 THEN 'FROZEN' ELSE 'FRESH' - END AS freeze_status + END AS freeze_status FROM {$this->tables['keywords']} KW JOIN {$this->tables['testcase_keywords']} TCKW ON TCKW.keyword_id = KW.id - JOIN {$this->tables['tcversions']} TCV + JOIN {$this->tables['tcversions']} TCV ON TCV.id = TCKW.tcversion_id - - WHERE KW.id = {$keyword_id} {$whereAdd} "; - } - $rs = $this->db->fetchRowsIntoMap($sql,'freeze_status'); - - $rs = (array)$rs; - return isset($rs['FROZEN']) ? 1 : 0; - } - - /** - * - */ - function getKeywordSimple( $keyword_id ) { - $sql = " SELECT keyword FROM {$this->tables['keywords']} - WHERE id = " . intval($keyword_id); - $rs = current($this->db->get_recordset($sql)); - return $rs['keyword']; - } - - - /** - * - */ - function getKeywordsExecStatus($keywordSet,$tproject_id=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $whereAdd = ''; - if( null != $tproject_id ) { - $whereAdd = " AND testproject_id = " . intval($tproject_id); - } - - $idSet = implode(',', $keywordSet); - $sql = "/* $debugMsg */ + WHERE KW.id = {$keyword_id} {$whereAdd} "; + } + $rs = $this->db->fetchRowsIntoMap($sql, 'freeze_status'); + + $rs = (array) $rs; + return isset($rs['FROZEN']) ? 1 : 0; + } + + /** + */ + private function getKeywordSimple($keyword_id) + { + $sql = " SELECT keyword FROM {$this->tables['keywords']} + WHERE id = " . intval($keyword_id); + $rs = current($this->db->get_recordset($sql)); + + return $rs['keyword']; + } + + /** + */ + public function getKeywordsExecStatus($keywordSet, $tproject_id = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $whereAdd = ''; + if (null != $tproject_id) { + $whereAdd = " AND testproject_id = " . intval($tproject_id); + } + + $idSet = implode(',', $keywordSet); + $sql = "/* $debugMsg */ SELECT DISTINCT keyword_id,keyword, - CASE + CASE WHEN EX.status IS NULL THEN 'NOT_RUN' ELSE 'EXECUTED' - END AS exec_or_not + END AS exec_or_not FROM {$this->tables['keywords']} KW JOIN {$this->tables['testcase_keywords']} TCKW ON TCKW.keyword_id = KW.id - LEFT OUTER JOIN {$this->tables['executions']} EX + LEFT OUTER JOIN {$this->tables['executions']} EX ON EX.tcversion_id = TCKW.tcversion_id - - WHERE KW.id IN( {$idSet} ) {$whereAdd} "; - $rs = $this->db->fetchRowsIntoMap($sql,'keyword_id'); - - return $rs; - } - - /** - * - */ - function getKeywordsFreezeStatus($keywordSet,$tproject_id=null) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $whereAdd = ''; - if( null != $tproject_id ) { - $whereAdd = " AND testproject_id = " . intval($tproject_id); - } - - $idSet = implode(',', $keywordSet); - $sql = "/* $debugMsg */ + WHERE KW.id IN( {$idSet} ) {$whereAdd} "; + + return $this->db->fetchRowsIntoMap($sql, 'keyword_id'); + } + + /** + */ + public function getKeywordsFreezeStatus($keywordSet, $tproject_id = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $whereAdd = ''; + if (null != $tproject_id) { + $whereAdd = " AND testproject_id = " . intval($tproject_id); + } + + $idSet = implode(',', $keywordSet); + $sql = "/* $debugMsg */ SELECT DISTINCT keyword_id,keyword, - CASE + CASE WHEN TCV.is_open=0 THEN 'FROZEN' ELSE 'FRESH' - END AS fresh_or_frozen + END AS fresh_or_frozen FROM {$this->tables['keywords']} KW JOIN {$this->tables['testcase_keywords']} TCKW ON TCKW.keyword_id = KW.id - JOIN {$this->tables['tcversions']} TCV + JOIN {$this->tables['tcversions']} TCV ON TCV.id = TCKW.tcversion_id - - WHERE KW.id IN( {$idSet} ) {$whereAdd} "; - - $rs = $this->db->fetchRowsIntoMap($sql,'keyword_id'); - return $rs; - } - /** - * - */ - function countKeywordUsageInTCVersions($tproject_id) { - - $pid = intval($tproject_id); - $sql = " SELECT KW.id AS keyword_id, + WHERE KW.id IN( {$idSet} ) {$whereAdd} "; + + return $this->db->fetchRowsIntoMap($sql, 'keyword_id'); + } + + /** + */ + public function countKeywordUsageInTCVersions($tproject_id) + { + $pid = intval($tproject_id); + $sql = " SELECT KW.id AS keyword_id, CASE WHEN TCKW.keyword_id IS NULL THEN 0 ELSE count(0) @@ -4069,108 +4087,103 @@ function countKeywordUsageInTCVersions($tproject_id) { ON TCKW.keyword_id = KW.id WHERE testproject_id = {$pid} - GROUP BY KW.id,TCKW.keyword_id "; - - $rs = $this->db->fetchRowsIntoMap($sql,'keyword_id'); - return $rs; - } - -/** - * - */ -function getPlatformsLatestTCV($tproject_id, $platform_id=0) { - - $filter = '' ; - $ltcvJoin = " JOIN {$this->views['latest_tcase_version_id']} LTCV - ON LTCV.tcversion_id = TPL.tcversion_id "; - - if( is_array($platform_id) ) { - $filter = " AND platform_id IN (" . implode(',',$platform_id) . ")"; - } - else if( $platform_id > 0 ) { - $filter = " AND platform_id = {$platform_id} "; - } - - $items = null; - $sql = " SELECT TPL.testcase_id,TPL.platform_id,PL.name + GROUP BY KW.id,TCKW.keyword_id "; + + return $this->db->fetchRowsIntoMap($sql, 'keyword_id'); + } + + /** + */ + public function getPlatformsLatestTCV($tproject_id, $platform_id = 0) + { + $filter = ''; + $ltcvJoin = " JOIN {$this->views['latest_tcase_version_id']} LTCV + ON LTCV.tcversion_id = TPL.tcversion_id "; + + if (is_array($platform_id)) { + $filter = " AND platform_id IN (" . implode(',', $platform_id) . ")"; + } elseif ($platform_id > 0) { + $filter = " AND platform_id = {$platform_id} "; + } + + $sql = " SELECT TPL.testcase_id,TPL.platform_id,PL.name FROM {$this->tables['platforms']} PL JOIN {$this->tables['testcase_platforms']} TPL ON TPL.platform_id = PL.id {$ltcvJoin} WHERE PL.testproject_id = {$tproject_id} {$filter} - ORDER BY name ASC "; - - $items = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','platform_id'); - - return $items; -} //end function - - -/** - * @used-by getTestSpecTree()@treeMenu.inc.php - * -1 => WITHOUT PLATFORMS - * - */ -function getTCLatestVersionFilteredByPlatforms($tproject_id, $platform_id=0) { - $platSet = (array)$platform_id; - $sql = null; - $tcaseSet = array(); - $delTT = false; - $hasTCases = false; - - // -1 => WITHOUT PLATFORMS - $getWithOutPlatforms = in_array(-1,$platSet); - if( $getWithOutPlatforms ) { - $this->get_all_testcases_id($tproject_id,$tcaseSet); - if( ($hasTCases = count($tcaseSet) > 0) ) { - $delTT = true; - $tt = 'temp_tcset_' . $tproject_id . md5(microtime()); - $sql = "CREATE TEMPORARY TABLE IF NOT EXISTS $tt AS - ( SELECT id FROM {$this->tables['nodes_hierarchy']} - LIMIT 0 )"; - $this->db->exec_query($sql); - $a4ins = array_chunk($tcaseSet, 2000); // MAGIC - foreach($a4ins as $chu) { - $sql = "INSERT INTO $tt (id) VALUES (" . - implode('),(',$chu) . ")"; - $this->db->exec_query($sql); - } - } - } - - if( $getWithOutPlatforms && $hasTCases) { - $sql = " /* WITHOUT PLATFORMS */ + ORDER BY name ASC "; + + return $this->db->fetchMapRowsIntoMap($sql, 'testcase_id', 'platform_id'); + } + + /** + * + * @used-by getTestSpecTree()@treeMenu.inc.php + * -1 => WITHOUT PLATFORMS + * + */ + protected function getTCLatestVersionFilteredByPlatforms($tproject_id, + $platform_id = 0) + { + $platSet = (array) $platform_id; + $sql = null; + $tcaseSet = array(); + $delTT = false; + $hasTCases = false; + + // -1 => WITHOUT PLATFORMS + $getWithOutPlatforms = in_array(- 1, $platSet); + if ($getWithOutPlatforms) { + $this->get_all_testcases_id($tproject_id, $tcaseSet); + if ($hasTCases = count($tcaseSet) > 0) { + $delTT = true; + $tt = 'temp_tcset_' . $tproject_id . md5(microtime()); + $sql = "CREATE TEMPORARY TABLE IF NOT EXISTS $tt AS + ( SELECT id FROM {$this->tables['nodes_hierarchy']} + LIMIT 0 )"; + $this->db->exec_query($sql); + $a4ins = array_chunk($tcaseSet, 2000); // MAGIC + foreach ($a4ins as $chu) { + $sql = "INSERT INTO $tt (id) VALUES (" . implode('),(', $chu) . + ")"; + $this->db->exec_query($sql); + } + } + } + + if ($getWithOutPlatforms && $hasTCases) { + $sql = " /* WITHOUT PLATFORMS */ SELECT TCVNO_PL.testcase_id FROM - {$this->views['tcversions_without_platforms']} TCVNO_PL + {$this->views['tcversions_without_platforms']} TCVNO_PL JOIN {$this->views['latest_tcase_version_id']} LTVC ON LTVC.tcversion_id = TCVNO_PL.id - JOIN $tt TT ON TT.id = TCVNO_PL.testcase_id "; - } else { - $filter = " platform_id IN (" . implode(',',$platSet) . ")"; - $filter_type = 'And'; - switch($filter_type) { - case 'NotLinked': - if($hasTCases) { - $sql = " /* WITHOUT SPECIFIC KEYWORDS */ - SELECT NHTCV.parent_id AS testcase_id - FROM {$this->tables['nodes_hierarchy']} NHTCV - JOIN {$this->views['latest_tcase_version_id']} LTCV - ON NHTCV.id = LTCV.tcversion_id - JOIN $tt TT ON TT.id = NHTCV.parent_id + JOIN $tt TT ON TT.id = TCVNO_PL.testcase_id "; + } else { + $filter = " platform_id IN (" . implode(',', $platSet) . ")"; + $filter_type = 'And'; + switch ($filter_type) { + case 'NotLinked': + if ($hasTCases) { + $sql = " /* WITHOUT SPECIFIC KEYWORDS */ + SELECT NHTCV.parent_id AS testcase_id + FROM {$this->tables['nodes_hierarchy']} NHTCV + JOIN {$this->views['latest_tcase_version_id']} LTCV + ON NHTCV.id = LTCV.tcversion_id + JOIN $tt TT ON TT.id = NHTCV.parent_id WHERE NOT EXISTS - (SELECT 1 FROM {$this->tables['testcase_platforms']} TCPL - WHERE TCPL.tcversion_id = LTCV.tcversion_id - AND {$filter} )"; - } - break; - - - case 'And': - // MAX(TK.testcase_id) needed to be able to extract - // Test case id. - $sqlCount = " /* SQL COUNT */ " . - " SELECT COUNT(TPL.tcversion_id) AS HITS, + (SELECT 1 FROM {$this->tables['testcase_platforms']} TCPL + WHERE TCPL.tcversion_id = LTCV.tcversion_id + AND {$filter} )"; + } + break; + + case 'And': + // MAX(TK.testcase_id) needed to be able to extract + // Test case id. + $sqlCount = " /* SQL COUNT */ " . + " SELECT COUNT(TPL.tcversion_id) AS HITS, MAX(TPL.testcase_id) AS testcase_id, TPL.tcversion_id FROM {$this->tables['platforms']} PL @@ -4180,53 +4193,70 @@ function getTCLatestVersionFilteredByPlatforms($tproject_id, $platform_id=0) { ON LTCV.tcversion_id = TPL.tcversion_id WHERE testproject_id = {$tproject_id} AND {$filter} - GROUP BY TPL.tcversion_id "; - - $sql = "/* Filter Type = AND */ - SELECT PLTFOXDOG.testcase_id - FROM ( $sqlCount ) AS PLTFOXDOG - WHERE PLTFOXDOG.HITS=" . count($platform_id); - break; - - - case 'Or': - default: - $sql = " /* Filter Type = OR */ " . - " SELECT TK.testcase_id " . - " FROM {$this->tables['testcase_platforms']} TPL" . - " JOIN {$this->views['latest_tcase_version_id']} LTVC " . - " ON LTVC.tcversion_id = TPL.tcversion_id " . - " JOIN {$this->tables['platforms']} PL " . - " ON PL.id = TK.platform_id " . - " WHERE {$filter} " . - " AND PL.testproject_id=" . $tproject_id; - break; - } - } - - $hits = !is_null($sql) ? $this->db->fetchRowsIntoMap($sql,'testcase_id') : null; - - // clean up - if( $delTT ) { - $sql = "DROP TABLE IF EXISTS $tt"; - $this->db->exec_query($sql); - } - - return $hits; -} - - /** - * - */ - static function getName(&$dbh,$id) { - $sch = tlDBObject::getDBTables(array('nodes_hierarchy','testprojects')); - $sql = "SELECT name FROM {$sch['nodes_hierarchy']} NH - JOIN {$sch['testprojects']} TPRJ + GROUP BY TPL.tcversion_id "; + + $sql = "/* Filter Type = AND */ + SELECT PLTFOXDOG.testcase_id + FROM ( $sqlCount ) AS PLTFOXDOG + WHERE PLTFOXDOG.HITS=" . count($platform_id); + break; + + case 'Or': + default: + $sql = " /* Filter Type = OR */ " . " SELECT TK.testcase_id " . + " FROM {$this->tables['testcase_platforms']} TPL" . + " JOIN {$this->views['latest_tcase_version_id']} LTVC " . + " ON LTVC.tcversion_id = TPL.tcversion_id " . + " JOIN {$this->tables['platforms']} PL " . + " ON PL.id = TK.platform_id " . " WHERE {$filter} " . + " AND PL.testproject_id=" . $tproject_id; + break; + } + } + + $hits = ! is_null($sql) ? $this->db->fetchRowsIntoMap($sql, + 'testcase_id') : null; + + // clean up + if ($delTT) { + $sql = "DROP TABLE IF EXISTS $tt"; + $this->db->exec_query($sql); + } + + return $hits; + } + + /** + */ + public static function getName(&$dbh, $id) + { + $sch = tlDBObject::getDBTables( + array( + 'nodes_hierarchy', + 'testprojects' + )); + $sql = "SELECT name FROM {$sch['nodes_hierarchy']} NH + JOIN {$sch['testprojects']} TPRJ ON TPRJ.id = NH.id - WHERE TPRJ.id=" . intval($id); - $rs = $dbh->get_recordset($sql); - return is_null($rs) ? $rs : $rs[0]['name']; - } - - -} // end class + WHERE TPRJ.id=" . intval($id); + $rs = $dbh->get_recordset($sql); + return is_null($rs) ? $rs : $rs[0]['name']; + } + + /** + * * + * + * @used-by testcase.class.php + */ + public function getKeywordsAsMapByName($tproject_id) + { + $keywordMap = null; + $keywords = $this->getKeywords($tproject_id); + if ($keywords) { + foreach ($keywords as $kw) { + $keywordMap[$kw->name] = $kw->notes; + } + } + return $keywordMap; + } +} diff --git a/lib/functions/testsuite.class.php b/lib/functions/testsuite.class.php index 876e6ff7e3..e074a07e50 100644 --- a/lib/functions/testsuite.class.php +++ b/lib/functions/testsuite.class.php @@ -1,2085 +1,2113 @@ - "XML"); - var $export_file_types = array("XML" => "XML"); - - // Node Types (NT) - var $nt2exclude = array('testplan' => 'exclude_me', - 'requirement_spec'=> 'exclude_me', - 'requirement'=> 'exclude_me'); - - - var $nt2exclude_children=array('testcase' => 'exclude_my_children', - 'requirement_spec'=> 'exclude_my_children'); - - /** - * testplan class constructor - * - * @param resource &$db reference to database handler - */ - function __construct(&$db) - { - $this->db = &$db; - - $this->tree_manager = new tree($this->db); - $this->node_types_descr_id=$this->tree_manager->get_available_node_types(); - $this->node_types_id_descr=array_flip($this->node_types_descr_id); - $this->my_node_type=$this->node_types_descr_id['testsuite']; - - $this->cfield_mgr=new cfield_mgr($this->db); - - // ATTENTION: - // second argument is used to set $this->attachmentTableName,property that this calls - // get from his parent - // tlObjectWithAttachments::__construct($this->db,'nodes_hierarchy'); - parent::__construct($this->db,"nodes_hierarchy"); - - // Must be setted AFTER call to parent constructor - $this->object_table = $this->tables['testsuites']; - - } - - - /* - returns: map - key: export file type code - value: export file type verbose description - */ - function get_export_file_types() { - return $this->export_file_types; - } - - - /* - function: get_impor_file_types - getter - - args: - - - returns: map - key: import file type code - value: import file type verbose description - - */ - function get_import_file_types() - { - return $this->import_file_types; - } - - - /* - args : - $parent_id - $name - $details - [$check_duplicate_name] - [$action_on_duplicate_name] - [$order] - returns: hash - $ret['status_ok'] -> 0/1 - $ret['msg'] - $ret['id'] -> when status_ok=1, id of the new element - rev : - */ - function create($parent_id,$name,$details,$order=null, - $check_duplicate_name=0, - $action_on_duplicate_name='allow_repeat') { - static $l18n; - static $cfg; - if(!$cfg) { - $cfg = array(); - $cfg['prefix_name_for_copy'] = config_get('prefix_name_for_copy'); - $cfg['node_order'] = config_get('treemenu_default_testsuite_order'); - - $l18n = array(); - $l18n['component_name_already_exists'] = lang_get('component_name_already_exists'); - } - - if( is_null($order) ) { - // @since 1.9.13 - // - //$node_order = isset($cfg['treemenu_default_testsuite_order']) ? - // $cfg['treemenu_default_testsuite_order'] : 0; - // get all siblings, then calculate bottom - // this way theorically each will be a different order. - // this can be good when ordering - $node_order = $this->tree_manager->getBottomOrder($parent_id,array('node_type' => 'testsuite')) + 1; - } else { - $node_order = $order; - } - - $name = trim($name); - $ret = array('status_ok' => 1, 'id' => 0, 'msg' => 'ok', - 'name' => '', 'name_changed' => false); - - if ($check_duplicate_name) { - $check = $this->tree_manager->nodeNameExists($name,$this->my_node_type,null,$parent_id); - if( $check['status'] == 1) { - if ($action_on_duplicate_name == 'block') { - $ret['status_ok'] = 0; - $ret['msg'] = sprintf($l18n['component_name_already_exists'],$name); - } else { - - $ret['status_ok'] = 1; - if ($action_on_duplicate_name == 'generate_new') { - - $desired_name = $name; - $name = $cfg['prefix_name_for_copy'] . " " . $desired_name; - - if( strlen($name) > self::MAXLEN_NAME ) { - $len2cut = strlen($cfg['prefix_name_for_copy']); - $name = $cfg['prefix_name_for_copy'] . - substr($desired_name,0,self::MAXLEN_NAME-$len2cut); - } - $ret['name'] = $name; - - $ret['msg'] = sprintf(lang_get('created_with_new_name'),$name,$desired_name); - $ret['name_changed'] = true; - } - } - } - } - - if ($ret['status_ok']) - { - // get a new id - $tsuite_id = $this->tree_manager->new_node($parent_id,$this->my_node_type, - $name,$node_order); - $sql = " INSERT INTO {$this->tables['testsuites']} (id,details) " . - " VALUES ({$tsuite_id},'" . $this->db->prepare_string($details) . "')"; - - $result = $this->db->exec_query($sql); - if ($result) - { - $ret['id'] = $tsuite_id; - - if (defined('TL_APICALL')) - { - $ctx = array('id' => $tsuite_id,'name' => $name,'details' => $details); - event_signal('EVENT_TEST_SUITE_CREATE', $ctx); - } - } - } - - return $ret; - } - - - /** - * update - * - * @internal Revisions - * 20100904 - franciscom - added node_order - */ - function update($id, $name, $details, $parent_id=null, $node_order=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret['status_ok']=0; - $ret['msg']=''; - - $safeID = intval($id); - $check = $this->tree_manager->nodeNameExists($name,$this->my_node_type,$safeID,$parent_id); - - if($check['status']==0) - { - $where = " WHERE id = {$safeID} "; - - // Work on enity table - if( !is_null($details) ) - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['testsuites']} " . - " SET details = '" . $this->db->prepare_string($details) . "'" . $where; - $result = $this->db->exec_query($sql); - } - - // Work on nodes hierarchy table - $sqlUpd = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} "; - if( !is_null($name) ) - { - $sql = " SET name='" . $this->db->prepare_string($name) . "' "; - $sql = $sqlUpd . $sql . $where; - $result = $this->db->exec_query($sql); - } - - if( !is_null($node_order) && intval($node_order) > 0 ) - { - $sql = ' SET node_order=' . $this->db->prepare_int(intval($node_order)); - $sql = $sqlUpd . $sql . $where; - $result = $this->db->exec_query($sql); - } - - $ret['status_ok']=1; - $ret['msg']='ok'; - if (!$result) { - $ret['msg'] = $this->db->error_msg(); - } else { - if (defined('TL_APICALL')) { - $ctx = array('id' => $id,'name' => $name,'details' => $details); - event_signal('EVENT_TEST_SUITE_UPDATE', $ctx); - } - } - } else { - $ret['msg']=$check['msg']; - } - return $ret; - } - - - /** - * Delete a Test suite, deleting: - * - Children Test Cases - * - Test Suite Attachments - * - Test Suite Custom fields - * - Test Suite Keywords - * - * IMPORTANT/CRITIC: - * this can used to delete a Test Suite that contains ONLY Test Cases. - * - * This function is needed by tree class method: delete_subtree_objects() - * - * To delete a Test Suite that contains other Test Suites delete_deep() - * must be used. - * - * ATTENTION: may be in future this can be refactored, and written better. - * - */ - function delete($unsafe_id) - { - $tcase_mgr = new testcase($this->db); - $id = intval($unsafe_id); - $tsuite_info = $this->get_by_id($id); - - $testcases=$this->get_children_testcases($id); - if (!is_null($testcases)) - { - foreach($testcases as $the_key => $elem) - { - $tcase_mgr->delete($elem['id']); - } - } - - // What about keywords ??? - $this->cfield_mgr->remove_all_design_values_from_node($id); - $this->deleteAttachments($id); //inherited - $this->deleteKeywords($id); - - $sql = "DELETE FROM {$this->object_table} WHERE id={$id}"; - $result = $this->db->exec_query($sql); - - $sql = "DELETE FROM {$this->tables['nodes_hierarchy']} " . - "WHERE id={$id} AND node_type_id=" . $this->my_node_type; - $result = $this->db->exec_query($sql); - if ($result) - { - $ctx = array('id' => $id); - event_signal('EVENT_TEST_SUITE_DELETE', $ctx); - } - } - - - - /* - function: get_by_name - - args : name: testsuite name - - returns: array where every element is a map with following keys: - - id: testsuite id (node id) - details - name: testsuite name - - @internal revisions - */ - function get_by_name($name, $parent_id=null, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my = array(); - $my['opt'] = array('output' => 'full', 'id' => 0); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = "/* $debugMsg */ "; - - switch($my['opt']['output']) - { - case 'minimun': - $sql .= " SELECT TS.id, NH.name, "; - break; - - case 'full': - default: - $sql .= " SELECT TS.*, NH.name, "; - break; - } - - $sql .= " NH.parent_id " . - " FROM {$this->tables['testsuites']} TS " . - " JOIN {$this->tables['nodes_hierarchy']} NH " . - " ON NH.id = TS.id " . - " WHERE NH.name = '" . $this->db->prepare_string($name) . "'"; - - if( !is_null($parent_id) ) - { - $sql .= " AND NH.parent_id = " . $this->db->prepare_int($parent_id); - } - - // useful when trying to check for duplicates ? - if( ($my['opt']['id'] = intval($my['opt']['id'])) > 0) - { - $sql .= " AND TS.id != {$my['opt']['id']} "; - } - - - $rs = $this->db->get_recordset($sql); - return $rs; - } - - /* - function: get_by_id - get info for one (or several) test suite(s) - - args : id: testsuite id - - returns: map with following keys: - - id: testsuite id (node id) (can be an array) - details - name: testsuite name - - - rev : - - */ - function get_by_id($id,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('orderByClause' => '','renderImageInline' => false, - 'fields' => null); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $f2g = is_null($my['opt']['fields']) ? - 'TS.*, NH.name, NH.node_type_id, NH.node_order, NH.parent_id' : - $my['opt']['fields']; - - $sql = "/* $debugMsg */ SELECT {$f2g} " . - " FROM {$this->tables['testsuites']} TS " . - " JOIN {$this->tables['nodes_hierarchy']} NH ON TS.id = NH.id " . - " WHERE TS.id "; - - $sql .= is_array($id) ? " IN (" . implode(',',$id) . ")" : " = {$id} "; - $sql .= $my['opt']['orderByClause']; - - - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - if( !is_null($rs) ) - { - $rs = count($rs) == 1 ? current($rs) : $rs; - } - - // now inline image processing (if needed) - if( !is_null($rs) && $my['opt']['renderImageInline']) - { - $this->renderImageAttachments($id,$rs); - } - - return $rs; - } - - - /* - function: get_all() - get array of info for every test suite without any kind of filter. - Every array element contains an assoc array with test suite info - - args : - - - returns: array - - */ - function get_all() - { - $sql = " SELECT testsuites.*, nodes_hierarchy.name " . - " FROM {$this->tables['testsuites']} testsuites, " . - " {$this->tables['nodes_hierarchy']} nodes_hierarchy " . - " WHERE testsuites.id = nodes_hierarchy.id"; - - $recordset = $this->db->get_recordset($sql); - return($recordset); - } - - - /** - * show() - * - * args: smarty [reference] - * id - * sqlResult [default = ''] - * action [default = 'update'] - * modded_item_id [default = 0] - * - * returns: - - * - **/ - function show(&$smarty,$guiObj,$template_dir, $id, $options=null, - $sqlResult = '', $action = 'update',$modded_item_id = 0) { - - $gui = is_null($guiObj) ? new stdClass() : $guiObj; - $gui->cf = ''; - $gui->sqlResult = ''; - $gui->sqlAction = ''; - - if (!property_exists($gui, 'uploadOp')) { - $gui->uploadOp = null; - } - - $p2ow = array('refreshTree' => false, 'user_feedback' => ''); - foreach($p2ow as $prop => $value) { - if( !property_exists($gui,$prop) ) { - $gui->$prop = $value; - } - } - - - // attachments management on page - $gui->fileUploadURL = $_SESSION['basehref'] . $this->getFileUploadRelativeURL($id); - $gui->delAttachmentURL = $_SESSION['basehref'] . $this->getDeleteAttachmentRelativeURL($id); - $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; - $gui->fileUploadMsg = ''; - - - // After test suite edit, display of Test suite do not have upload button enabled for attachment - $my['options'] = array('show_mode' => 'readwrite'); - $my['options'] = array_merge($my['options'], (array)$options); - - if($sqlResult) { - $gui->sqlResult = $sqlResult; - $gui->sqlAction = $action; - } - - - $gui->item_id = $tsuite_id = $id; - if( !property_exists($gui,'tproject_id') ) { - $gui->tproject_id = $this->getTestProjectFromTestSuite($tsuite_id,null); - } - - $gui->modify_tc_rights = - has_rights($this->db,"mgt_modify_tc",$gui->tproject_id); - - if($my['options']['show_mode'] == 'readonly') { - $gui->modify_tc_rights = 'no'; - } - - - $gui->assign_keywords = 0; - if( property_exists($gui, 'user') ) { - $yn = $gui->user->hasRight($this->db,'mgt_modify_key',$gui->tproject_id); - $gui->assign_keywords = ($yn == "yes"); - } - - $gui->container_data = $this->get_by_id($id,array('renderImageInline' => true)); - $gui->moddedItem = $gui->container_data; - if ($modded_item_id) { - $gui->moddedItem = $this->get_by_id($modded_item_id,array('renderImageInline' => true)); - } - - $gui->cf = $this->html_table_of_custom_field_values($id); - $gui->attachmentInfos = getAttachmentInfosFrom($this,$id); - $gui->id = $id; - $gui->page_title = lang_get('testsuite'); - $gui->level = $gui->containerType = 'testsuite'; - $cfg = getWebEditorCfg('design'); - $gui->testDesignEditorType = $cfg['type']; - - $gui->calledByMethod = 'testsuite::show'; - - $kopt = array('order_by_clause' => ' ORDER BY keyword ASC ', - 'output' => 'with_link_id'); - $gui->keywords_map = $this->get_keywords_map($id,$kopt); - - $of = array('output' => 'html_options', - 'add_blank' => true, - 'tproject_id' => $gui->tproject_id); - $gui->freeKeywords = $this->getFreeKeywords($id,$of); - - $smarty->assign('gui',$gui); - $smarty->display($template_dir . 'containerView.tpl'); - } - - - /* - function: viewer_edit_new - Implements user interface (UI) for edit testuite and - new/create testsuite operations. - - - args : smarty [reference] - webEditorHtmlNames - oWebEditor: rich editor object (today is FCK editor) - action - parent_id: testsuite parent id on tree. - [id] - [messages]: default null - map with following keys - [result_msg]: default: null used to give information to user - [user_feedback]: default: null used to give information to user - - // [$userTemplateCfg]: configurations, Example: testsuite template usage - [$userTemplateKey]: main Key to access item template configuration - [$userInput] - - returns: - - - */ - function viewer_edit_new(&$smarty,$template_dir,$webEditorHtmlNames, $oWebEditor, - $action, $parent_id,$id=null, $messages=null, - $userTemplateKey=null, $userInput=null) - { - $internalMsg = array('result_msg' => null, 'user_feedback' => null); - $the_data = null; - $name = ''; - - if( !is_null($messages) ) { - $internalMsg = array_merge($internalMsg, $messages); - } - - $useUserInput = is_null($userInput) ? 0 : 1; - $cf_smarty=-2; // MAGIC must be explained - $pnode_info=$this->tree_manager->get_node_hierarchy_info($parent_id); - - $parent_info['description']=lang_get($this->node_types_id_descr[$pnode_info['node_type_id']]); - $parent_info['name']=$pnode_info['name']; - - - $a_tpl = array('edit_testsuite' => 'containerEdit.tpl','new_testsuite' => 'containerNew.tpl', - 'add_testsuite' => 'containerNew.tpl'); - - $the_tpl = $a_tpl[$action]; - $smarty->assign('sqlResult', $internalMsg['result_msg']); - $smarty->assign('containerID',$parent_id); - $smarty->assign('user_feedback', $internalMsg['user_feedback'] ); - - if( $useUserInput ) - { - $webEditorData = $userInput; - } - else - { - $the_data = null; - $name = ''; - if ($action == 'edit_testsuite') - { - $the_data = $this->get_by_id($id); - $name=$the_data['name']; - $smarty->assign('containerID',$id); - } - $webEditorData = $the_data; - } - - $cf_smarty = $this->html_table_of_custom_field_inputs($id,$parent_id,'design','',$userInput); - - // webeditor - // templates will be also used after 'add_testsuite', when - // presenting a new test suite with all other fields empty. - if( !$useUserInput ) - { - if( ($action == 'new_testsuite' || $action == 'add_testsuite') && !is_null($userTemplateKey) ) - { - // need to understand if need to use templates - $webEditorData=$this->_initializeWebEditors($webEditorHtmlNames,$userTemplateKey); - } - } - - foreach ($webEditorHtmlNames as $key) { - // Warning: - // the data assignment will work while the keys in $the_data are identical - // to the keys used on $oWebEditor. - $of = &$oWebEditor[$key]; - $of->Value = isset($webEditorData[$key]) ? $webEditorData[$key] : null; - $smarty->assign($key, $of->CreateHTML()); - } - - $smarty->assign('cf',$cf_smarty); - $smarty->assign('parent_info', $parent_info); - $smarty->assign('level', 'testsuite'); - $smarty->assign('name',$name); - $smarty->assign('container_data',$the_data); - $smarty->display($template_dir . $the_tpl); - } - - - /* - function: copy_to - deep copy one testsuite to another parent (testsuite or testproject). - - - args : id: testsuite id (source or copy) - parent_id: - user_id: who is requesting copy operation - [check_duplicate_name]: default: 0 -> do not check - 1 -> check for duplicate when doing copy - What to do if duplicate exists, is controlled - by action_on_duplicate_name argument. - - [action_on_duplicate_name argument]: default: 'allow_repeat'. - Used when check_duplicate_name=1. - Specifies how to react if duplicate name exists. - - - - - returns: map with foloowing keys: - status_ok: 0 / 1 - msg: 'ok' if status_ok == 1 - id: new created if everything OK, -1 if problems. - - @internal revisions - When copying a project, external TC ID is not preserved - added option 'preserve_external_id' needed by tcase copy_to() - - */ - function copy_to($id, $parent_id, $user_id,$options=null,$mappings=null) { - - $my['options'] = array('check_duplicate_name' => 0, - 'action_on_duplicate_name' => 'allow_repeat', - 'copyKeywords' => 0, 'copyRequirements' => 0, - 'preserve_external_id' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - $my['mappings'] = array(); - $my['mappings'] = array_merge($my['mappings'], (array)$mappings); - - $copyTCaseOpt = array('preserve_external_id' => - $my['options']['preserve_external_id'], - 'copy_also' => - array('keyword_assignments' => - $my['options']['copyKeywords'], - 'requirement_assignments' => - $my['options']['copyRequirements']) ); - - $copyOptions = array('keyword_assignments' => $my['options']['copyKeywords']); - - $tcase_mgr = new testcase($this->db); - $tsuite_info = $this->get_by_id($id); - - $op = $this->create($parent_id,$tsuite_info['name'], - $tsuite_info['details'], - $tsuite_info['node_order'], - $my['options']['check_duplicate_name'], - $my['options']['action_on_duplicate_name']); - - $op['mappings'][$id] = $op['id']; - $new_tsuite_id = $op['id']; - - // Work on root of these subtree - // Attachments - always copied - // Keyword assignment - according to user choice - // Custom Field values - always copied - $oldToNew = $this->copy_attachments($id,$new_tsuite_id); - $inlineImg = null; - if(!is_null($oldToNew)) { - $this->inlineImageProcessing($new_tsuite_id,$tsuite_info['details'],$oldToNew); - } - - if( $my['options']['copyKeywords'] ) { - $kmap = isset($my['mappings']['keywords']) ? $my['mappings']['keywords'] : null; - $this->copy_keyword_assignment($id,$new_tsuite_id,$kmap); - } - $this->copy_cfields_values($id,$new_tsuite_id); - - - $my['filters'] = array('exclude_children_of' => array('testcase' => 'exclude my children')); - $subtree = $this->tree_manager->get_subtree($id,$my['filters']); - if (!is_null($subtree)) { - $parent_decode=array(); - $parent_decode[$id]=$new_tsuite_id; - foreach($subtree as $the_key => $elem) { - $the_parent_id=$parent_decode[$elem['parent_id']]; - switch ($elem['node_type_id']) { - case $this->node_types_descr_id['testcase']: - // forgotten parameter $mappings caused requirement assignments to use wrong IDs - $tcOp = $tcase_mgr->copy_to($elem['id'],$the_parent_id,$user_id,$copyTCaseOpt, $my['mappings']); - $op['mappings'] += $tcOp['mappings']; - break; - - case $this->node_types_descr_id['testsuite']: - $tsuite_info = $this->get_by_id($elem['id']); - $ret = $this->create($the_parent_id,$tsuite_info['name'], - $tsuite_info['details'],$tsuite_info['node_order']); - - $parent_decode[$elem['id']] = $ret['id']; - $op['mappings'][$elem['id']] = $ret['id']; - - $oldToNew = $this->copy_attachments($elem['id'],$ret['id']); - $inlineImg = null; - if(!is_null($oldToNew)) - { - $this->inlineImageProcessing($ret['id'],$tsuite_info['details'],$oldToNew); - } - - if( $my['options']['copyKeywords'] ) - { - $this->copy_keyword_assignment($elem['id'],$ret['id'],$kmap); - } - $this->copy_cfields_values($elem['id'],$ret['id']); - - break; - } - } - } - return $op; - } - - - /* - function: get_subtree - Get subtree that has choosen testsuite as root. - Only nodes of type: - testsuite and testcase are explored and retrieved. - - args: id: testsuite id - [recursive_mode]: default false - - - returns: map - see tree->get_subtree() for details. - - */ - function get_subtree($id,$opt=null) { - $my['options'] = array('recursive' => 0, 'excludeTC' => 0); - $my['options'] = array_merge($my['options'],(array)$opt); - - $my['filters'] = array('exclude_node_types' => $this->nt2exclude, - 'exclude_children_of' => $this->nt2exclude_children); - - if( $my['options']['excludeTC'] ) { - $my['filters']['exclude_node_types']['testcase'] = 'exclude_me'; - } - - // var_dump($my['filters']); - //die(); - $subtree = $this->tree_manager->get_subtree($id,$my['filters'],$my['options']); - return $subtree; - } - - - - /* - function: get_testcases_deep - get all test cases in the test suite and all children test suites - no info about tcversions is returned. - - args : id: testsuite id - [details]: default 'simple' - Structure of elements in returned array, changes according to - this argument: - - 'only_id' - Array that contains ONLY testcase id, no other info. - - 'simple' - Array where each element is a map with following keys. - - id: testcase id - parent_id: testcase parent (a test suite id). - node_type_id: type id, for a testcase node - node_order - node_table: node table, for a testcase. - name: testcase name - external_id: - - 'full' - Complete info about testcase for LAST TCVERSION - TO BE IMPLEMENTED - - returns: array - - */ - function get_testcases_deep($id, $details = 'simple', $options=null) { - $tcase_mgr = new testcase($this->db); - $testcases = null; - - $opt = array('getKeywords' => false); - $opt = array_merge($opt,(array)$options); - - $subtree = $this->get_subtree($id); - $only_id=($details=='only_id') ? true : false; - $doit=!is_null($subtree); - $parentSet=null; - - if($doit) - { - $testcases = array(); - $tcNodeType = $this->node_types_descr_id['testcase']; - $prefix = null; - foreach ($subtree as $the_key => $elem) - { - if($elem['node_type_id'] == $tcNodeType) - { - if ($only_id) - { - $testcases[] = $elem['id']; - } - else - { - // After first call passing $prefix with right value, avoids a function call - // inside of getExternalID(); - list($identity,$prefix,$glueChar,$external) = $tcase_mgr->getExternalID($elem['id'],null,$prefix); - $elem['external_id'] = $identity; - $testcases[]= $elem; - $parentSet[$elem['parent_id']]=$elem['parent_id']; - } - } - } - $doit = count($testcases) > 0; - } - - if($doit && $details=='full') - { - $parentNodes=$this->tree_manager->get_node_hierarchy_info($parentSet); - - $rs=array(); - foreach($testcases as $idx => $value) - { - $item=$tcase_mgr->get_last_version_info($value['id'],array('output' => full, 'get_steps' => true)); - $item['tcversion_id']=$item['id']; - $tsuite['tsuite_name']=$parentNodes[$value['parent_id']]['name']; - - if( $opt['getKeywords'] ) - { - $kw = $tcase_mgr->getKeywords($value['id']); - if( !is_null($kw) ) - { - $item['keywords'] = $kw; - } - } - - unset($item['id']); - $rs[]=$value+$item+$tsuite; - } - $testcases=$rs; - } - return $testcases; - } - - - /** - * get_children_testcases - * get only test cases with parent=testsuite without doing a deep search - * - */ - function get_children_testcases($id, $details = 'simple', $options=null) { - $testcases=null; - $only_id=($details=='only_id') ? true : false; - $subtree=$this->tree_manager->get_children($id,array('testsuite' => 'exclude_me')); - $doit=!is_null($subtree); - - $opt = array('getKeywords' => false); - $opt = array_merge($opt,(array)$options); - - - if($doit) - { - $tsuite=$this->get_by_id($id); - $tsuiteName=$tsuite['name']; - $testcases = array(); - foreach ($subtree as $the_key => $elem) - { - if ($only_id) - { - $testcases[] = $elem['id']; - } - else - { - $testcases[]= $elem; - } - } - $doit = count($testcases) > 0; - } - - if($doit && $details=='full') - { - $rs=array(); - $tcase_mgr = new testcase($this->db); - foreach($testcases as $idx => $value) - { - $item=$tcase_mgr->get_last_version_info($value['id'],array('output' => full, 'get_steps' => true)); - $item['tcversion_id']=$item['id']; - $parent['tsuite_name']=$tsuiteName; - - if( $opt['getKeywords'] ) - { - $kw = $tcase_mgr->getKeywords($value['id']); - if( !is_null($kw) ) - { - $item['keywords'] = $kw; - } - } - unset($item['id']); - $rs[]=$value+$item+$tsuite; - } - $testcases=$rs; - } - return $testcases; - } - - - - - /* - function: delete_deep - - args : $id - - returns: - - rev : - 20070602 - franciscom - added delete attachments - */ - function delete_deep($id) - { - // BUGID 3147 - Delete test project with requirements defined crashed with memory exhausted - $this->tree_manager->delete_subtree_objects($id,$id,'',array('testcase' => 'exclude_tcversion_nodes')); - $this->delete($id); - } // end function - - - - - - /* - function: initializeWebEditors - - args: - - returns: - - */ - private function _initializeWebEditors($WebEditors,$itemTemplateCfgKey) - { - $wdata=array(); - foreach ($WebEditors as $key => $html_name) - { - $wdata[$html_name] = getItemTemplateContents($itemTemplateCfgKey, $html_name, ''); - } - return $wdata; - } - - - /** - function: getKeywords - Get keyword assigned to a testsuite. - Uses table object_keywords. - - args: id: testsuite id - kw_id: [default = null] the optional keyword id - - returns: null if nothing found. - array, every elemen is map with following structure: - id - keyword - notes - - */ - function getKeywords($id,$kw_id = null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ SELECT keyword_id,keywords.keyword, notes " . - " FROM {$this->tables['object_keywords']}, {$this->tables['keywords']} keywords " . - " WHERE keyword_id = keywords.id AND fk_id = {$id}"; - if (!is_null($kw_id)) { - $sql .= " AND keyword_id = {$kw_id}"; - } - $map_keywords = $this->db->fetchRowsIntoMap($sql,'keyword_id'); - - return $map_keywords; - } - - - /* - function: get_keywords_map - All keywords for a choosen testsuite - - Attention: - probably write on obejct_keywords has not been implemented yet, - then right now thie method can be useless. - - - args :id: testsuite id - [order_by_clause]: default: '' -> no order choosen - must be an string with complete clause, i.e. - 'ORDER BY keyword' - - - - returns: map: key: keyword_id - value: keyword - - - */ - function get_keywords_map($id,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $options = array('order_by_clause' => '', 'output' => 'std'); - $options = array_merge($options,(array)$opt); - $order_by_clause = $options['order_by_clause']; - - $sql = "/* $debugMsg */ - SELECT OKW.id AS kw_link,OKW.keyword_id,keywords.keyword - FROM {$this->tables['object_keywords']} OKW - JOIN {$this->tables['keywords']} keywords - ON OKW.keyword_id = keywords.id "; - - if (is_array($id)) { - $sql .= " AND fk_id IN (".implode(",",$id).") "; - } else { - $sql .= " AND fk_id = {$id} "; - } - - $sql .= $order_by_clause; - - switch( $options['output'] ) { - - case 'with_link_id': - $map_keywords = $this->db->fetchRowsIntoMap($sql,'keyword_id'); - break; - - case 'std': - default: - $map_keywords = $this->db->fetchColumnsIntoMap($sql,'keyword_id','keyword'); - break; - - } - - - return $map_keywords; - } - - - /** - * - * - */ - function addKeyword($id,$kw_id) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $status = 1; - $kw = $this->getKeywords($id,$kw_id); - if( ($doLink = !sizeof($kw)) ) - { - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['object_keywords']} " . - " (fk_id,fk_table,keyword_id) VALUES ($id,'nodes_hierarchy',$kw_id)"; - $status = $this->db->exec_query($sql) ? 1 : 0; - } - return $status; - } - - - /* - function: addKeywords - - args : - - returns: - - */ - function addKeywords($id,$kw_ids) { - $status = 1; - $num_kws = sizeof($kw_ids); - for($idx = 0; $idx < $num_kws; $idx++) { - $status = $status && $this->addKeyword($id,$kw_ids[$idx]); - } - return($status); - } - - - /** - * deleteKeywords - * - */ - function deleteKeywords($id,$kw_id = null) { - $sql = " DELETE FROM {$this->tables['object_keywords']} - WHERE fk_id = {$id} "; - - if (!is_null($kw_id)) { - $sql .= " AND keyword_id = {$kw_id}"; - } - return $this->db->exec_query($sql); - } - - /* - function: exportTestSuiteDataToXML - - args : - - returns: - - */ - function exportTestSuiteDataToXML($container_id,$tproject_id,$optExport = array()) { - static $keywordMgr; - static $getLastVersionOpt = array('output' => 'minimun'); - static $tcase_mgr; - - if(is_null($keywordMgr)) { - $keywordMgr = new tlKeyword(); - - - } - - $xmlTC = null; - $relCache = array(); - - $doRecursion = isset($optExport['RECURSIVE']) ? $optExport['RECURSIVE'] : 0; - - if($doRecursion) { - $cfXML = null; - $attachmentsXML = null; - $kwXML = null; - $tsuiteData = $this->get_by_id($container_id); - if( isset($optExport['KEYWORDS']) && $optExport['KEYWORDS']) { - $kwMap = $this->getKeywords($container_id); - if ($kwMap) { - $kwXML = "" . $keywordMgr->toXMLString($kwMap,true) . ""; - } - } - if (isset($optExport['CFIELDS']) && $optExport['CFIELDS']) { - $cfMap = (array)$this->get_linked_cfields_at_design($container_id,null,null,$tproject_id); - if( count($cfMap) > 0 ) { - $cfXML = $this->cfield_mgr->exportValueAsXML($cfMap); - } - } - if (isset($optExport['ATTACHMENTS']) && $optExport['ATTACHMENTS']) { - $attachments=null; - - // get all attachments - $attInfos = $this->attachmentRepository->getAttachmentInfosFor($container_id,$this->attachmentTableName,'id'); - - // get all attachments content and encode it in base64 - if ($attInfos) { - foreach ($attInfos as $axInfo) { - $aID = $axInfo["id"]; - $content = $this->attachmentRepository->getAttachmentContent($aID, $axInfo); - - if ($content != null) { - $attach[$aID]["id"] = $aID; - $attach[$aID]["name"] = $axInfo["file_name"]; - $attach[$aID]["file_type"] = $axInfo["file_type"]; - $attach[$aID]["title"] = $axInfo["title"]; - $attach[$aID]["date_added"] = $axInfo["date_added"]; - $attach[$aID]["content"] = base64_encode($content); - } - } - } - - if( !is_null($attach) && count($attach) > 0 ) { - $attchRootElem = "\n{{XMLCODE}}\n"; - $attchElemTemplate = "\t\n" . - "\t\t\n" . - "\t\t\n" . - "\t\t\n" . - "\t\t\n" . - "\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . - "\t\t\n" . - "\t\t\n" . - "\t\n"; - - $attchDecode = array ("||ATTACHMENT_ID||" => "id", "||ATTACHMENT_NAME||" => "name", - "||ATTACHMENT_FILE_TYPE||" => "file_type", - "||ATTACHMENT_FILE_SIZE||" => "file_size", - "||ATTACHMENT_TITLE||" => "title", - "||ATTACHMENT_DATE_ADDED||" => "date_added", - "||ATTACHMENT_CONTENT||" => "content"); - $attachXML = exportDataToXML($attach,$attchRootElem,$attchElemTemplate,$attchDecode,true); - } - } - - $xmlTC = '' . - "\n\n" . - "
    \n{$kwXML}{$cfXML}{$attachmentsXML}"; - - } else { - $xmlTC = ""; - } - - $topt = array('recursive' => self::USE_RECURSIVE_MODE); - if( isset($optExport['skeleton']) && $optExport['skeleton'] ) { - $topt['excludeTC'] = true; - } - $test_spec = $this->get_subtree($container_id,$topt); - - $childNodes = isset($test_spec['childNodes']) ? $test_spec['childNodes'] : null ; - $tcase_mgr=null; - $relXmlData = ''; - if( !is_null($childNodes) ) { - $loop_qty=sizeof($childNodes); - for($idx = 0;$idx < $loop_qty;$idx++) { - $cNode = $childNodes[$idx]; - $nTable = $cNode['node_table']; - if ($doRecursion && $nTable == 'testsuites') { - $xmlTC .= $this->exportTestSuiteDataToXML($cNode['id'],$tproject_id,$optExport); - } else if ($nTable == 'testcases') { - if( is_null($tcase_mgr) ) { - $tcase_mgr = new testcase($this->db); - } - $xmlTC .= $tcase_mgr->exportTestCaseDataToXML($cNode['id'], - testcase::LATEST_VERSION, - $tproject_id,true,$optExport); - - // 20140816 - // Collect and do cache of all test case relations that exists inside this test suite. - $relSet = $tcase_mgr->getRelations($cNode['id']); - if($relSet['num_relations'] >0) { - foreach($relSet['relations'] as $key => $rel) { - // If we have already found this relation, skip it. - if ( !in_array($rel['id'], $relCache) ) { - $relXmlData .= $tcase_mgr->exportRelationToXML($rel,$relSet['item']); - $relCache[] = $rel['id']; - } - } - } - } - } - } - // after we scanned all relations and exported all relations to xml, let's output it to the XML buffer - $xmlTC .= $relXmlData; - $xmlTC .= $doRecursion ? "
    " : ""; - return $xmlTC; - } - - - // ------------------------------------------------------------------------------- - // Custom field related methods - // ------------------------------------------------------------------------------- - /* - function: get_linked_cfields_at_design - - - args: $id - [$parent_id]: - [$filtesr]: default: null - - returns: hash - - rev : - */ - function get_linked_cfields_at_design($id,$parent_id=null,$filters=null,$tproject_id = null,$access_key='id') - { - if (!$tproject_id) - { - $tproject_id = $this->getTestProjectFromTestSuite($id,$parent_id); - } - $cf_map = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id,cfield_mgr::CF_ENABLED, - $filters,'testsuite',$id,$access_key); - return $cf_map; - } - - /** - * getTestProjectFromTestSuite() - * - */ - function getTestProjectFromTestSuite($id,$parent_id) { - $tproject_id = $this->tree_manager->getTreeRoot( (!is_null($id) && $id > 0) ? $id : $parent_id); - return $tproject_id; - } - - /* - function: get_linked_cfields_at_execution - - - args: $id - [$parent_id] - [$filters] - keys: $show_on_execution: default: null - 1 -> filter on field show_on_execution=1 - 0 or null -> don't filter - - - returns: hash - - rev : - 20110129 - franciscom - BUGID 4202 - */ - function get_linked_cfields_at_execution($id,$parent_id=null,$filters=null,$tproject_id=null) - { - - if (!$tproject_id) - { - $the_path=$this->tree_manager->get_path(!is_null($id) ? $id : $parent_id); - $path_len=count($the_path); - $tproject_id=($path_len > 0)? $the_path[$path_len-1]['parent_id'] : $parent_id; - } - - $cf_map=$this->cfield_mgr->get_linked_cfields_at_design($tproject_id,cfield_mgr::CF_ENABLED, - $filters,'testsuite',$id); - return($cf_map); - } - - - - /* - function: html_table_of_custom_field_inputs - - - args: $id - [$parent_id]: need when you call this method during the creation - of a test suite, because the $id will be 0 or null. - - [$scope]: 'design','execution' - - returns: html string - - */ - function html_table_of_custom_field_inputs($id,$parent_id=null,$scope='design',$name_suffix='',$input_values=null) - { - $cf_smarty=''; - $method_suffix = $scope=='design' ? $scope : 'execution'; - $method_name = "get_linked_cfields_at_{$method_suffix}"; - $cf_map=$this->$method_name($id,$parent_id); - - if(!is_null($cf_map)) - { - $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map,$name_suffix,$input_values); - } - return($cf_smarty); - } - - - /* - function: html_table_of_custom_field_values - - - args: $id - [$scope]: 'design','execution' - [$show_on_execution]: default: null - 1 -> filter on field show_on_execution=1 - 0 or null -> don't filter - - returns: html string - - */ - function html_table_of_custom_field_values($id,$scope='design',$show_on_execution=null, - $tproject_id = null,$formatOptions=null) - { - $filters=array('show_on_execution' => $show_on_execution); - $label_css_style=' class="labelHolder" ' ; - $value_css_style = ' '; - - $add_table=true; - $table_style=''; - if( !is_null($formatOptions) ) - { - $label_css_style = isset($formatOptions['label_css_style']) ? $formatOptions['label_css_style'] : $label_css_style; - $value_css_style = isset($formatOptions['value_css_style']) ? $formatOptions['value_css_style'] : $value_css_style; - - $add_table=isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; - $table_style=isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; - } - - $cf_smarty=''; - $parent_id=null; - - // BUGID 3989 - $show_cf = config_get('custom_fields')->show_custom_fields_without_value; - - if( $scope=='design' ) - { - $cf_map = $this->get_linked_cfields_at_design($id,$parent_id,$filters,$tproject_id); - } - else - { - // Important: remember that for Test Suite, custom field value CAN NOT BE changed - // at execution time just displayed. - // 20110129 - if we know test project id is better to use it - $cf_map=$this->get_linked_cfields_at_execution($id,null,null,$tproject_id); - } - - if( !is_null($cf_map) ) - { - foreach($cf_map as $cf_id => $cf_info) - { - // if user has assigned a value, then node_id is not null - // BUGID 3989 - if($cf_info['node_id'] || $show_cf) - { - // true => do not create input in audit log - $label=str_replace(TL_LOCALIZE_TAG,'',lang_get($cf_info['label'],null,true)); - $cf_smarty .= "" . htmlspecialchars($label) . "" . - "" . - $this->cfield_mgr->string_custom_field_value($cf_info,$id) . - "\n"; - } - } - } - if((trim($cf_smarty) != "") && $add_table) - { - $cf_smarty = "" . $cf_smarty . "
    "; - } - return($cf_smarty); - } // function end - - - /** - * Copy attachments from source test suite to target test suite - * - **/ - function copy_attachments($source_id,$target_id) - { - return $this->attachmentRepository->copyAttachments($source_id,$target_id,$this->attachmentTableName); - } - - /** - * Copy keyword assignment - * mappings is only useful when source_id and target_id do not belong to same Test Project. - * Because keywords are defined INSIDE a Test Project, ID will be different for same keyword - * in a different Test Project - * - **/ - function copy_keyword_assignment($source_id,$target_id,$mappings) - { - // Get source_id keyword assignment - $sourceItems = $this->getKeywords($source_id); - if( !is_null($sourceItems) ) - { - // build item id list - $keySet = array_keys($sourceItems); - foreach($keySet as $itemPos => $itemID) - { - if( isset($mappings[$itemID]) ) - { - $keySet[$itemPos] = $mappings[$itemID]; - } - } - $this->addKeywords($target_id,$keySet); - } - } - - /** - * Copy Custom Fields values - * - **/ - function copy_cfields_values($source_id,$target_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - // Get source_id cfields assignment - $sourceItems = $this->cfield_mgr->getByLinkID($source_id,array('scope' => 'design')); - if( !is_null($sourceItems) ) - { - $sql = "/* $debugMsg */ " . - " INSERT INTO {$this->tables['cfield_design_values']} " . - " (field_id,value,node_id) " . - " SELECT field_id,value,{$target_id} AS target_id" . - " FROM {$this->tables['cfield_design_values']} " . - " WHERE node_id = {$source_id} "; - $this->db->exec_query($sql); - } - } - - - /** - * get_children - * get test suites with parent = testsuite with given id - * - */ - function get_children($id,$options=null) - { - $itemSet = null; - $my['options'] = array('details' => 'full'); - $my['options'] = array_merge($my['options'], (array)$options); - - $subtree = $this->tree_manager->get_children($id, array('testcase' => 'exclude_me')); - if(!is_null($subtree) && count($subtree) > 0) - { - foreach( $subtree as $the_key => $elem) - { - $itemKeys[] = $elem['id']; - } - - if($my['options']['details'] == 'full') - { - $itemSet = $this->get_by_id($itemKeys, array('orderByClause' => 'ORDER BY node_order')); - } - else - { - $itemSet = $itemKeys; - } - } - return $itemSet; - } - - /** - * get_branch - * get ONLY test suites (no other kind of node) ON BRANCH with ROOT = testsuite with given id - * - */ - function get_branch($id) { - $branch = $this->tree_manager->get_subtree_list($id,$this->my_node_type); - return $branch; - } - - - /** - * - * 'name' - * 'testProjectID' - * 'parentID' - * 'notes' - * - */ - function createFromObject($item,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['opt'] = array('doChecks' => false, 'setSessionProject' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - define('DBUG_ON',1); - try - { - // mandatory checks - if(strlen($item->name)==0) - { - throw new Exception('Empty name is not allowed'); - } - - // what checks need to be done ? - // 1. test project exist - $pinfo = $this->tree_manager->get_node_hierarchy_info($item->testProjectID); - if(is_null($pinfo) || $this->node_types_id_descr[$pinfo['node_type_id']] != 'testproject') - { - throw new Exception('Test project ID does not exist'); - } - - // 2. parentID exists and its node type can be: - // testproject,testsuite - // - $pinfo = $this->tree_manager->get_node_hierarchy_info($item->parentID); - if(is_null($pinfo)) - { - throw new Exception('Parent ID does not exist'); - } - - if($this->node_types_id_descr[$pinfo['node_type_id']] != 'testproject' && - $this->node_types_id_descr[$pinfo['node_type_id']] != 'testsuite' - ) - { - throw new Exception('Node Type for Parent ID is not valid'); - } - - - // 3. there is NO other test suite children of parent id node with same name - $name = trim($item->name); - $op = $this->checkNameExistence($name,$item->parentID); - if(!$op['status_ok']) - { - throw new Exception('Test suite name is already in use at same level'); - } - } - catch (Exception $e) - { - throw $e; // rethrow - } - - $id = $this->tree_manager->new_node($item->parentID,$this->my_node_type, - $name,intval($item->order)); - - $sql = " INSERT INTO {$this->tables['testsuites']} (id,details) " . - " VALUES ({$id},'" . $this->db->prepare_string($item->notes) . "')"; - - $result = $this->db->exec_query($sql); - return $result ? $id : 0; - } - - /** - * Checks is there is another test plan inside test project - * with different id but same name - * - **/ - function checkNameExistence($name,$parentID,$id=0) - { - $check_op['msg'] = ''; - $check_op['status_ok'] = 1; - - $getOpt = array('output' => 'minimun', 'id' => intval($id)); - if( $this->get_by_name( $name,intval($parentID), $getOpt) ) - { - $check_op['msg'] = sprintf(lang_get('error_product_name_duplicate'),$name); - $check_op['status_ok'] = 0; - } - return $check_op; - } - - /** - * - * @used-by containerEdit.php, testsuite.class.php.show - */ - function getFileUploadRelativeURL($id) - { - // I've to use testsuiteID because this is how is name on containerEdit.php - $url = "lib/testcases/containerEdit.php?containerType=testsuite&doAction=fileUpload&testsuiteID=" . intval($id); - return $url; - } - - /** - * @used-by containerEdit.php, testsuite.class.php.show - */ - function getDeleteAttachmentRelativeURL($id) - { - // I've to use testsuiteID because this is how is name on containerEdit.php - $url = "lib/testcases/containerEdit.php?containerType=testsuite&doAction=deleteFile&testsuiteID=" . intval($id) . - "&file_id=" ; - return $url; - } - - - /** - * render Image Attachments INLINE - * - * - */ - function renderImageAttachments($id,&$item2render,$basehref=null) - { - static $attSet; - static $targetTag; - - if(!$attSet || !isset($attSet[$id])) - { - $attSet[$id] = $this->attachmentRepository->getAttachmentInfosFor($id,$this->attachmentTableName,'id'); - $beginTag = '[tlInlineImage]'; - $endTag = '[/tlInlineImage]'; - } - - if(is_null($attSet[$id])) - { - return; - } - - // $href = '%s:%s' . " $versionTag (link)

    "; - // second \'%s\' needed if I want to use Latest as indication, need to understand - // Javascript instead of javascript, because CKeditor sometimes complains - // - // CRITIC: skipCheck is needed to render OK when creating report on Pseudo-Word format. - $bhref = is_null($basehref) ? $_SESSION['basehref'] : $basehref; - $img = '

    '; - - $key2check = array('details'); - $rse = &$item2render; - foreach($key2check as $item_key) - { - $start = strpos($rse[$item_key],$beginTag); - $ghost = $rse[$item_key]; - - // There is at least one request to replace ? - if($start !== FALSE) - { - $xx = explode($beginTag,$rse[$item_key]); - - // How many requests to replace ? - $xx2do = count($xx); - $ghost = ''; - for($xdx=0; $xdx < $xx2do; $xdx++) - { - // Hope was not a false request. - if( strpos($xx[$xdx],$endTag) !== FALSE) - { - // Separate command string from other text - // Theorically can be just ONE, but it depends - // is user had not messed things. - $yy = explode($endTag,$xx[$xdx]); - if( ($elc = count($yy)) > 0) - { - $atx = $yy[0]; - try - { - if(isset($attSet[$id][$atx]) && $attSet[$id][$atx]['is_image']) - { - $sec = hash('sha256', $attSet[$id][$atx]['file_name']); - $ghost .= str_replace(array('%id%','%sec%'),array($atx,$sec),$img); - } - $lim = $elc-1; - for($cpx=1; $cpx <= $lim; $cpx++) - { - $ghost .= $yy[$cpx]; - } - } - catch (Exception $e) - { - $ghost .= $rse[$item_key]; - } - } - } - else - { - // nothing to do - $ghost .= $xx[$xdx]; - } - } - } - - // reconstruct field contents - if($ghost != '') - { - $rse[$item_key] = $ghost; - } - } - } - - /** - * - */ - function updateDetails($id,$details) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ UPDATE {$this->tables['testsuites']} " . - " SET details = '" . $this->db->prepare_string($details) . "'" . - " WHERE id = " . intval($id); - $result = $this->db->exec_query($sql); - - } - - /** - * - * - */ - function inlineImageProcessing($id,$details,$rosettaStone) { - // get all attachments, then check is there are images - $att = $this->attachmentRepository->getAttachmentInfosFor($id,$this->attachmentTableName,'id'); - - foreach($rosettaStone as $oid => $nid) { - if($att[$nid]['is_image']) { - $needle = str_replace($nid,$oid,$att[$nid]['inlineString']); - $inlineImg[] = array('needle' => $needle, 'rep' => $att[$nid]['inlineString']); - } - } - - if( !is_null($inlineImg) ) { - $dex = $details; - foreach($inlineImg as $elem) { - $dex = str_replace($elem['needle'],$elem['rep'],$dex); - } - $this->updateDetails($id,$dex); - } - } - - - /** - * - * - */ - function buildDirectWebLink($base_href,$id,$tproject_id) { - $tproject_mgr = new testproject($this->db); - $prefix = $tproject_mgr->getTestCasePrefix($tproject_id); - $dl = $base_href . 'linkto.php?tprojectPrefix=' . urlencode($prefix) . '&item=testsuite&id=' . $id; - return $dl; - } - - /** - * - * get only test cases with parent=testsuite without doing a deep search - * - */ - function getChildrenLatestTCVersion($id) { - - $testcases = null; - $items = null; - $subtree = - $this->tree_manager->get_children($id,array('testsuite' => 'exclude_me')); - - $doit = !is_null($subtree); - - if($doit) { - $tsuite = $this->get_by_id($id); - $tsuiteName = $tsuite['name']; - $testcases = array(); - foreach ($subtree as $the_key => $elem) { - $testcases[] = $elem['id']; - } - $doit = count($testcases) > 0; - } - - if( $doit ) { - $inClause = implode(',',$testcases); - $sql = " SELECT tcversion_id - FROM {$this->views['latest_tcase_version_id']} - WHERE testcase_id IN ($inClause) "; - - $items = $this->db->get_recordset($sql); - } - - - return $items; - } - - /** - * - */ - function getTSuitesFilteredByKWSet( $id, $opt = null, $filters = null ) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $options = array('output' => 'std'); - $options = array_merge($options, (array)$opt); - - $fil = array('keywordsIn' => null, 'keywordsLikeStart' => null); - $fil = array_merge($fil, (array)$filters); - - $fields = 'fk_id AS tsuite_id, NHTS.name AS tsuite_name,'; - - if( null != $fil['keywordsLikeStart'] ) { - $target = trim($fil['keywordsLikeStart']); - $fields .= " CONCAT(REPLACE(KW.keyword,'{$target}',''),':', NHTS.name) AS dyn_string "; - } else { - $fields .= " CONCAT(KW.keyword,':', NHTS.name) AS dyn_string "; - } - - switch($options['output']) { - case 'kwname': - $fields .= ',KW.keyword'; - break; - - default: - $fields .= ",keyword_id,KW.keyword"; - break; - } - - $sql = "/* $debugMsg */ + "XML" + ); + + private $exportFileTypes = array( + "XML" => "XML" + ); + + // Node Types (NT) + private $nt2exclude = array( + 'testplan' => 'exclude_me', + 'requirement_spec' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + + private $nt2excludeChildren = array( + 'testcase' => 'exclude_my_children', + 'requirement_spec' => 'exclude_my_children' + ); + + /** + * testplan class constructor + * + * @param + * resource &$db reference to database handler + */ + public function __construct(&$db) + { + $this->db = &$db; + + $this->tree_manager = new tree($this->db); + $this->node_types_descr_id = $this->tree_manager->get_available_node_types(); + $this->nodeTypesIdDescr = array_flip($this->node_types_descr_id); + $this->my_node_type = $this->node_types_descr_id['testsuite']; + + $this->cfield_mgr = new cfield_mgr($this->db); + + // ATTENTION: + // second argument is used to set $this->attachmentTableName,property that this calls + // get from his parent + // tlObjectWithAttachments::__construct($this->db,'nodes_hierarchy'); + parent::__construct($this->db, "nodes_hierarchy"); + + // Must be setted AFTER call to parent constructor + $this->objectTable = $this->tables['testsuites']; + + $this->debugMsg = ' Class:' . __CLASS__ . ' - Method: '; + } + + /* + * returns: map + * key: export file type code + * value: export file type verbose description + */ + public function get_export_file_types() + { + return $this->exportFileTypes; + } + + /* + * function: get_impor_file_types + * getter + * + * args: - + * + * returns: map + * key: import file type code + * value: import file type verbose description + * + */ + public function get_import_file_types() + { + return $this->importFileTypes; + } + + /* + * args : + * $parent_id + * $name + * $details + * [$check_duplicate_name] + * [$action_on_duplicate_name] + * [$order] + * returns: hash + * $ret['status_ok'] -> 0/1 + * $ret['msg'] + * $ret['id'] -> when status_ok=1, id of the new element + * rev : + */ + public function create($parent_id, $name, $details, $order = null, + $check_duplicate_name = 0, $action_on_duplicate_name = 'allow_repeat') + { + static $l18n; + static $cfg; + if (! $cfg) { + $cfg = array(); + $cfg['prefix_name_for_copy'] = config_get('prefix_name_for_copy'); + $cfg['node_order'] = config_get('treemenu_default_testsuite_order'); + + $l18n = array(); + $l18n['component_name_already_exists'] = lang_get( + 'component_name_already_exists'); + } + + if (is_null($order)) { + // @since 1.9.13 + // + // $node_order = isset($cfg['treemenu_default_testsuite_order']) ? + // $cfg['treemenu_default_testsuite_order'] : 0; + // get all siblings, then calculate bottom + // this way theorically each will be a different order. + // this can be good when ordering + $node_order = $this->tree_manager->getBottomOrder($parent_id, + array( + 'node_type' => 'testsuite' + )) + 1; + } else { + $node_order = $order; + } + + $name = trim($name); + $ret = array( + 'status_ok' => 1, + 'id' => 0, + 'msg' => 'ok', + 'name' => '', + 'name_changed' => false + ); + + if ($check_duplicate_name) { + $check = $this->tree_manager->nodeNameExists($name, + $this->my_node_type, null, $parent_id); + if ($check['status'] == 1) { + if ($action_on_duplicate_name == 'block') { + $ret['status_ok'] = 0; + $ret['msg'] = sprintf( + $l18n['component_name_already_exists'], $name); + } else { + + $ret['status_ok'] = 1; + if ($action_on_duplicate_name == 'generate_new') { + + $desired_name = $name; + $name = $cfg['prefix_name_for_copy'] . " " . + $desired_name; + + if (strlen($name) > self::MAXLEN_NAME) { + $len2cut = strlen($cfg['prefix_name_for_copy']); + $name = $cfg['prefix_name_for_copy'] . + substr($desired_name, 0, + self::MAXLEN_NAME - $len2cut); + } + $ret['name'] = $name; + + $ret['msg'] = sprintf(lang_get('created_with_new_name'), + $name, $desired_name); + $ret['name_changed'] = true; + } + } + } + } + + if ($ret['status_ok']) { + // get a new id + $tsuite_id = $this->tree_manager->new_node($parent_id, + $this->my_node_type, $name, $node_order); + $sql = " INSERT INTO {$this->tables['testsuites']} (id,details) " . + " VALUES ({$tsuite_id},'" . $this->db->prepare_string($details) . + "')"; + + $result = $this->db->exec_query($sql); + if ($result) { + $ret['id'] = $tsuite_id; + + if (defined('TL_APICALL')) { + $ctx = array( + 'id' => $tsuite_id, + 'name' => $name, + 'details' => $details + ); + event_signal('EVENT_TEST_SUITE_CREATE', $ctx); + } + } + } + + return $ret; + } + + /** + * update + * + * @internal Revisions + * 20100904 - franciscom - added node_order + */ + public function update($id, $name, $details, $parent_id = null, + $node_order = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $ret['status_ok'] = 0; + $ret['msg'] = ''; + + $safeID = intval($id); + $check = $this->tree_manager->nodeNameExists($name, $this->my_node_type, + $safeID, $parent_id); + + if ($check['status'] == 0) { + $where = " WHERE id = {$safeID} "; + + // Work on enity table + if (! is_null($details)) { + $sql = "/* $debugMsg */ UPDATE {$this->tables['testsuites']} " . + " SET details = '" . $this->db->prepare_string($details) . + "'" . $where; + $result = $this->db->exec_query($sql); + } + + // Work on nodes hierarchy table + $sqlUpd = "/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} "; + if (! is_null($name)) { + $sql = " SET name='" . $this->db->prepare_string($name) . "' "; + $sql = $sqlUpd . $sql . $where; + $result = $this->db->exec_query($sql); + } + + if (! is_null($node_order) && intval($node_order) > 0) { + $sql = ' SET node_order=' . + $this->db->prepare_int(intval($node_order)); + $sql = $sqlUpd . $sql . $where; + $result = $this->db->exec_query($sql); + } + + $ret['status_ok'] = 1; + $ret['msg'] = 'ok'; + if (! $result) { + $ret['msg'] = $this->db->error_msg(); + } else { + if (defined('TL_APICALL')) { + $ctx = array( + 'id' => $id, + 'name' => $name, + 'details' => $details + ); + event_signal('EVENT_TEST_SUITE_UPDATE', $ctx); + } + } + } else { + $ret['msg'] = $check['msg']; + } + return $ret; + } + + /** + * Delete a Test suite, deleting: + * - Children Test Cases + * - Test Suite Attachments + * - Test Suite Custom fields + * - Test Suite Keywords + * + * IMPORTANT/CRITIC: + * this can used to delete a Test Suite that contains ONLY Test Cases. + * + * This function is needed by tree class method: delete_subtree_objects() + * + * To delete a Test Suite that contains other Test Suites delete_deep() + * must be used. + * + * ATTENTION: may be in future this can be refactored, and written better. + */ + public function delete($unsafe_id) + { + $tcaseMgr = new testcase($this->db); + $id = intval($unsafe_id); + $this->get_by_id($id); + + $testcases = $this->get_children_testcases($id); + if (! is_null($testcases)) { + foreach ($testcases as $elem) { + $tcaseMgr->delete($elem['id']); + } + } + + // What about keywords ??? + $this->cfield_mgr->remove_all_design_values_from_node($id); + $this->deleteAttachments($id); // inherited + $this->deleteKeywords($id); + + $sql = "DELETE FROM {$this->objectTable} WHERE id={$id}"; + $this->db->exec_query($sql); + + $sql = "DELETE FROM {$this->tables['nodes_hierarchy']} " . + "WHERE id={$id} AND node_type_id=" . $this->my_node_type; + $result = $this->db->exec_query($sql); + if ($result) { + $ctx = array( + 'id' => $id + ); + event_signal('EVENT_TEST_SUITE_DELETE', $ctx); + } + } + + /* + * function: get_by_name + * + * args : name: testsuite name + * + * returns: array where every element is a map with following keys: + * + * id: testsuite id (node id) + * details + * name: testsuite name + * + * @internal revisions + */ + public function get_by_name($name, $parent_id = null, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my = array(); + $my['opt'] = array( + 'output' => 'full', + 'id' => 0 + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = "/* $debugMsg */ "; + + switch ($my['opt']['output']) { + case 'minimun': + $sql .= " SELECT TS.id, NH.name, "; + break; + + case 'full': + default: + $sql .= " SELECT TS.*, NH.name, "; + break; + } + + $sql .= " NH.parent_id " . " FROM {$this->tables['testsuites']} TS " . + " JOIN {$this->tables['nodes_hierarchy']} NH " . " ON NH.id = TS.id " . + " WHERE NH.name = '" . $this->db->prepare_string($name) . "'"; + + if (! is_null($parent_id)) { + $sql .= " AND NH.parent_id = " . $this->db->prepare_int($parent_id); + } + + // useful when trying to check for duplicates ? + if (($my['opt']['id'] = intval($my['opt']['id'])) > 0) { + $sql .= " AND TS.id != {$my['opt']['id']} "; + } + + return $this->db->get_recordset($sql); + } + + /* + * function: get_by_id + * get info for one (or several) test suite(s) + * + * args : id: testsuite id + * + * returns: map with following keys: + * + * id: testsuite id (node id) (can be an array) + * details + * name: testsuite name + * + * + * rev : + * + */ + public function get_by_id($id, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $my['opt'] = array( + 'orderByClause' => '', + 'renderImageInline' => false, + 'fields' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $f2g = is_null($my['opt']['fields']) ? 'TS.*, NH.name, NH.node_type_id, NH.node_order, NH.parent_id' : $my['opt']['fields']; + + $sql = "/* $debugMsg */ SELECT {$f2g} " . + " FROM {$this->tables['testsuites']} TS " . + " JOIN {$this->tables['nodes_hierarchy']} NH ON TS.id = NH.id " . + " WHERE TS.id "; + + $sql .= is_array($id) ? " IN (" . implode(',', $id) . ")" : " = {$id} "; + $sql .= $my['opt']['orderByClause']; + + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + if (! is_null($rs)) { + $rs = count($rs) == 1 ? current($rs) : $rs; + } + + // now inline image processing (if needed) + if (! is_null($rs) && $my['opt']['renderImageInline']) { + $this->renderImageAttachments($id, $rs); + } + + return $rs; + } + + /* + * function: get_all() + * get array of info for every test suite without any kind of filter. + * Every array element contains an assoc array with test suite info + * + * args : - + * + * returns: array + * + */ + public function get_all() + { + $sql = " SELECT testsuites.*, nodes_hierarchy.name " . + " FROM {$this->tables['testsuites']} testsuites, " . + " {$this->tables['nodes_hierarchy']} nodes_hierarchy " . + " WHERE testsuites.id = nodes_hierarchy.id"; + + return $this->db->get_recordset($sql); + } + + /** + * show() + * + * args: smarty [reference] + * id + * sqlResult [default = ''] + * action [default = 'update'] + * modded_item_id [default = 0] + * + * returns: - + */ + public function show(&$smarty, $guiObj, $template_dir, $id, $options = null, + $sqlResult = '', $action = 'update', $modded_item_id = 0) + { + $gui = is_null($guiObj) ? new stdClass() : $guiObj; + $gui->cf = ''; + $gui->sqlResult = ''; + $gui->sqlAction = ''; + + if (! property_exists($gui, 'uploadOp')) { + $gui->uploadOp = null; + } + + $p2ow = array( + 'refreshTree' => false, + 'user_feedback' => '' + ); + foreach ($p2ow as $prop => $value) { + if (! property_exists($gui, $prop)) { + $gui->$prop = $value; + } + } + + // attachments management on page + $gui->fileUploadURL = $_SESSION['basehref'] . + $this->getFileUploadRelativeURL($id); + $gui->delAttachmentURL = $_SESSION['basehref'] . + $this->getDeleteAttachmentRelativeURL($id); + $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; + $gui->fileUploadMsg = ''; + + // After test suite edit, display of Test suite do not have upload button enabled for attachment + $my['options'] = array( + 'show_mode' => 'readwrite' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + if ($sqlResult) { + $gui->sqlResult = $sqlResult; + $gui->sqlAction = $action; + } + + $gui->item_id = $tsuite_id = $id; + if (! property_exists($gui, 'tproject_id')) { + $gui->tproject_id = $this->getTestProjectFromTestSuite($tsuite_id, + null); + } + + $gui->modify_tc_rights = has_rights($this->db, "mgt_modify_tc", + $gui->tproject_id); + + if ($my['options']['show_mode'] == 'readonly') { + $gui->modify_tc_rights = 'no'; + } + + $gui->assign_keywords = 0; + if (property_exists($gui, 'user')) { + $yn = $gui->user->hasRight($this->db, 'mgt_modify_key', + $gui->tproject_id); + $gui->assign_keywords = ($yn == "yes"); + } + + $gui->container_data = $this->get_by_id($id, + array( + 'renderImageInline' => true + )); + $gui->moddedItem = $gui->container_data; + if ($modded_item_id) { + $gui->moddedItem = $this->get_by_id($modded_item_id, + array( + 'renderImageInline' => true + )); + } + + $gui->cf = $this->html_table_of_custom_field_values($id); + $gui->attachmentInfos = getAttachmentInfosFrom($this, $id); + $gui->id = $id; + $gui->page_title = lang_get('testsuite'); + $gui->level = $gui->containerType = 'testsuite'; + $cfg = getWebEditorCfg('design'); + $gui->testDesignEditorType = $cfg['type']; + + $gui->calledByMethod = 'testsuite::show'; + + $kopt = array( + 'order_by_clause' => ' ORDER BY keyword ASC ', + 'output' => 'with_link_id' + ); + $gui->keywords_map = $this->get_keywords_map($id, $kopt); + + $of = array( + 'output' => 'html_options', + 'add_blank' => true, + 'tproject_id' => $gui->tproject_id + ); + $gui->freeKeywords = $this->getFreeKeywords($id, $of); + + $smarty->assign('gui', $gui); + $smarty->display($template_dir . 'containerView.tpl'); + } + + /* + * function: viewer_edit_new + * Implements user interface (UI) for edit testuite and + * new/create testsuite operations. + * + * + * args : smarty [reference] + * webEditorHtmlNames + * oWebEditor: rich editor object (today is FCK editor) + * action + * parent_id: testsuite parent id on tree. + * [id] + * [messages]: default null + * map with following keys + * [result_msg]: default: null used to give information to user + * [user_feedback]: default: null used to give information to user + * + * // [$userTemplateCfg]: configurations, Example: testsuite template usage + * [$userTemplateKey]: main Key to access item template configuration + * [$userInput] + * + * returns: - + * + */ + public function viewer_edit_new(&$smarty, $template_dir, $webEditorHtmlNames, + $oWebEditor, $action, $parent_id, $id = null, $messages = null, + $userTemplateKey = null, $userInput = null) + { + $internalMsg = array( + 'result_msg' => null, + 'user_feedback' => null + ); + $the_data = null; + $name = ''; + + if (! is_null($messages)) { + $internalMsg = array_merge($internalMsg, $messages); + } + + $useUserInput = is_null($userInput) ? 0 : 1; + $pnode_info = $this->tree_manager->get_node_hierarchy_info($parent_id); + + $parent_info['description'] = lang_get( + $this->nodeTypesIdDescr[$pnode_info['node_type_id']]); + $parent_info['name'] = $pnode_info['name']; + + $a_tpl = array( + 'edit_testsuite' => 'containerEdit.tpl', + 'new_testsuite' => 'containerNew.tpl', + 'add_testsuite' => 'containerNew.tpl' + ); + + $the_tpl = $a_tpl[$action]; + $smarty->assign('sqlResult', $internalMsg['result_msg']); + $smarty->assign('containerID', $parent_id); + $smarty->assign('user_feedback', $internalMsg['user_feedback']); + + if ($useUserInput) { + $webEditorData = $userInput; + } else { + $the_data = null; + $name = ''; + if ($action == 'edit_testsuite') { + $the_data = $this->get_by_id($id); + $name = $the_data['name']; + $smarty->assign('containerID', $id); + } + $webEditorData = $the_data; + } + + $cf_smarty = $this->html_table_of_custom_field_inputs($id, $parent_id, + 'design', '', $userInput); + + // webeditor + // templates will be also used after 'add_testsuite', when + // presenting a new test suite with all other fields empty. + if (! $useUserInput && + ($action == 'new_testsuite' || $action == 'add_testsuite') && + ! is_null($userTemplateKey)) { + // need to understand if need to use templates + $webEditorData = $this->initializeWebEditors($webEditorHtmlNames, + $userTemplateKey); + } + + foreach ($webEditorHtmlNames as $key) { + // Warning: + // the data assignment will work while the keys in $the_data are identical + // to the keys used on $oWebEditor. + $of = &$oWebEditor[$key]; + $of->Value = isset($webEditorData[$key]) ? $webEditorData[$key] : null; + $smarty->assign($key, $of->CreateHTML()); + } + + $smarty->assign('cf', $cf_smarty); + $smarty->assign('parent_info', $parent_info); + $smarty->assign('level', 'testsuite'); + $smarty->assign('name', $name); + $smarty->assign('container_data', $the_data); + $smarty->display($template_dir . $the_tpl); + } + + /* + * function: copy_to + * deep copy one testsuite to another parent (testsuite or testproject). + * + * + * args : id: testsuite id (source or copy) + * parent_id: + * user_id: who is requesting copy operation + * [check_duplicate_name]: default: 0 -> do not check + * 1 -> check for duplicate when doing copy + * What to do if duplicate exists, is controlled + * by action_on_duplicate_name argument. + * + * [action_on_duplicate_name argument]: default: 'allow_repeat'. + * Used when check_duplicate_name=1. + * Specifies how to react if duplicate name exists. + * + * + * + * + * returns: map with foloowing keys: + * status_ok: 0 / 1 + * msg: 'ok' if status_ok == 1 + * id: new created if everything OK, -1 if problems. + * + * @internal revisions + * When copying a project, external TC ID is not preserved + * added option 'preserve_external_id' needed by tcase copy_to() + * + */ + public function copy_to($id, $parent_id, $user_id, $options = null, + $mappings = null) + { + $my['options'] = array( + 'check_duplicate_name' => 0, + 'action_on_duplicate_name' => 'allow_repeat', + 'copyKeywords' => 0, + 'copyRequirements' => 0, + 'preserve_external_id' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $my['mappings'] = array(); + $my['mappings'] = array_merge($my['mappings'], (array) $mappings); + + $copyTCaseOpt = array( + 'preserve_external_id' => $my['options']['preserve_external_id'], + 'copy_also' => array( + 'keyword_assignments' => $my['options']['copyKeywords'], + 'requirement_assignments' => $my['options']['copyRequirements'] + ) + ); + + $tcaseMgr = new testcase($this->db); + $tsuite_info = $this->get_by_id($id); + + $op = $this->create($parent_id, $tsuite_info['name'], + $tsuite_info['details'], $tsuite_info['node_order'], + $my['options']['check_duplicate_name'], + $my['options']['action_on_duplicate_name']); + + $op['mappings'][$id] = $op['id']; + $new_tsuite_id = $op['id']; + + // Work on root of these subtree + // Attachments - always copied + // Keyword assignment - according to user choice + // Custom Field values - always copied + $oldToNew = $this->copyAttachments($id, $new_tsuite_id); + if (! is_null($oldToNew)) { + $this->inlineImageProcessing($new_tsuite_id, $tsuite_info['details'], + $oldToNew); + } + + if ($my['options']['copyKeywords']) { + $kmap = isset($my['mappings']['keywords']) ? $my['mappings']['keywords'] : null; + $this->copyKeywordAssignment($id, $new_tsuite_id, $kmap); + } + $this->copyCfieldsValues($id, $new_tsuite_id); + + $my['filters'] = array( + 'exclude_children_of' => array( + 'testcase' => 'exclude my children' + ) + ); + $subtree = $this->tree_manager->get_subtree($id, $my['filters']); + if (! is_null($subtree)) { + $parent_decode = array(); + $parent_decode[$id] = $new_tsuite_id; + foreach ($subtree as $elem) { + $the_parent_id = $parent_decode[$elem['parent_id']]; + switch ($elem['node_type_id']) { + case $this->node_types_descr_id['testcase']: + // forgotten parameter $mappings caused requirement assignments to use wrong IDs + $tcOp = $tcaseMgr->copy_to($elem['id'], $the_parent_id, + $user_id, $copyTCaseOpt, $my['mappings']); + $op['mappings'] += $tcOp['mappings']; + break; + + case $this->node_types_descr_id['testsuite']: + $tsuite_info = $this->get_by_id($elem['id']); + $ret = $this->create($the_parent_id, + $tsuite_info['name'], $tsuite_info['details'], + $tsuite_info['node_order']); + + $parent_decode[$elem['id']] = $ret['id']; + $op['mappings'][$elem['id']] = $ret['id']; + + $oldToNew = $this->copyAttachments($elem['id'], + $ret['id']); + if (! is_null($oldToNew)) { + $this->inlineImageProcessing($ret['id'], + $tsuite_info['details'], $oldToNew); + } + + if ($my['options']['copyKeywords']) { + $this->copyKeywordAssignment($elem['id'], $ret['id'], + $kmap); + } + $this->copyCfieldsValues($elem['id'], $ret['id']); + + break; + } + } + } + return $op; + } + + /* + * function: get_subtree + * Get subtree that has choosen testsuite as root. + * Only nodes of type: + * testsuite and testcase are explored and retrieved. + * + * args: id: testsuite id + * [recursive_mode]: default false + * + * + * returns: map + * see tree->get_subtree() for details. + * + */ + public function get_subtree($id, $opt = null) + { + $my['options'] = array( + 'recursive' => 0, + 'excludeTC' => 0 + ); + $my['options'] = array_merge($my['options'], (array) $opt); + + $my['filters'] = array( + 'exclude_node_types' => $this->nt2exclude, + 'exclude_children_of' => $this->nt2excludeChildren + ); + + if ($my['options']['excludeTC']) { + $my['filters']['exclude_node_types']['testcase'] = 'exclude_me'; + } + + return $this->tree_manager->get_subtree($id, $my['filters'], + $my['options']); + } + + /* + * function: get_testcases_deep + * get all test cases in the test suite and all children test suites + * no info about tcversions is returned. + * + * args : id: testsuite id + * [details]: default 'simple' + * Structure of elements in returned array, changes according to + * this argument: + * + * 'only_id' + * Array that contains ONLY testcase id, no other info. + * + * 'simple' + * Array where each element is a map with following keys. + * + * id: testcase id + * parent_id: testcase parent (a test suite id). + * node_type_id: type id, for a testcase node + * node_order + * node_table: node table, for a testcase. + * name: testcase name + * external_id: + * + * 'full' + * Complete info about testcase for LAST TCVERSION + * TO BE IMPLEMENTED + * + * returns: array + * + */ + public function get_testcases_deep($id, $details = 'simple', $options = null) + { + $tcaseMgr = new testcase($this->db); + $testcases = null; + + $opt = array( + 'getKeywords' => false + ); + $opt = array_merge($opt, (array) $options); + + $subtree = $this->get_subtree($id); + $only_id = ($details == 'only_id') ? true : false; + $doit = ! is_null($subtree); + $parentSet = null; + + if ($doit) { + $testcases = array(); + $tcNodeType = $this->node_types_descr_id['testcase']; + $prefix = null; + foreach ($subtree as $elem) { + if ($elem['node_type_id'] == $tcNodeType) { + if ($only_id) { + $testcases[] = $elem['id']; + } else { + // After first call passing $prefix with right value, avoids a function call + // inside of getExternalID(); + list ($identity, $prefix, ,) = $tcaseMgr->getExternalID( + $elem['id'], null, $prefix); + $elem['external_id'] = $identity; + $testcases[] = $elem; + $parentSet[$elem['parent_id']] = $elem['parent_id']; + } + } + } + $doit = ! empty($testcases); + } + + if ($doit && $details == 'full') { + $parentNodes = $this->tree_manager->get_node_hierarchy_info( + $parentSet); + + $rs = array(); + foreach ($testcases as $value) { + $item = $tcaseMgr->getLastVersionInfo($value['id'], + array( + 'output' => 'full', + 'get_steps' => true + )); + $item['tcversion_id'] = $item['id']; + $tsuite['tsuite_name'] = $parentNodes[$value['parent_id']]['name']; + + if ($opt['getKeywords']) { + $kw = $tcaseMgr->getKeywords($value['id']); + if (! is_null($kw)) { + $item['keywords'] = $kw; + } + } + + unset($item['id']); + $rs[] = $value + $item + $tsuite; + } + $testcases = $rs; + } + return $testcases; + } + + /** + * get_children_testcases + * get only test cases with parent=testsuite without doing a deep search + */ + public function get_children_testcases($id, $details = 'simple', + $options = null) + { + $testcases = null; + $only_id = ($details == 'only_id') ? true : false; + $subtree = $this->tree_manager->get_children($id, + array( + 'testsuite' => 'exclude_me' + )); + $doit = ! is_null($subtree); + + $opt = array( + 'getKeywords' => false + ); + $opt = array_merge($opt, (array) $options); + + if ($doit) { + $tsuite = $this->get_by_id($id); + $tsuiteName = $tsuite['name']; + $testcases = array(); + foreach ($subtree as $elem) { + if ($only_id) { + $testcases[] = $elem['id']; + } else { + $testcases[] = $elem; + } + } + $doit = ! empty($testcases); + } + + if ($doit && $details == 'full') { + $rs = array(); + $tcaseMgr = new testcase($this->db); + foreach ($testcases as $value) { + $item = $tcaseMgr->getLastVersionInfo($value['id'], + array( + 'output' => 'full', + 'get_steps' => true + )); + $item['tcversion_id'] = $item['id']; + $parent['tsuite_name'] = $tsuiteName; + + if ($opt['getKeywords']) { + $kw = $tcaseMgr->getKeywords($value['id']); + if (! is_null($kw)) { + $item['keywords'] = $kw; + } + } + unset($item['id']); + $rs[] = $value + $item + $tsuite; + } + $testcases = $rs; + } + return $testcases; + } + + /* + * function: delete_deep + * + * args : $id + * + * returns: + * + * rev : + * 20070602 - franciscom + * added delete attachments + */ + public function delete_deep($id) + { + // BUGID 3147 - Delete test project with requirements defined crashed with memory exhausted + $this->tree_manager->delete_subtree_objects($id, $id, '', + array( + 'testcase' => 'exclude_tcversion_nodes' + )); + $this->delete($id); + } + + // end function + + /* + * function: initializeWebEditors + * + * args: + * + * returns: + * + */ + private function initializeWebEditors($webEditors, $itemTemplateCfgKey) + { + $wdata = array(); + foreach ($webEditors as $html_name) { + $wdata[$html_name] = getItemTemplateContents($itemTemplateCfgKey, + $html_name, ''); + } + return $wdata; + } + + /** + * function: getKeywords + * Get keyword assigned to a testsuite. + * Uses table object_keywords. + * + * args: id: testsuite id + * kw_id: [default = null] the optional keyword id + * + * returns: null if nothing found. + * array, every elemen is map with following structure: + * id + * keyword + * notes + */ + public function getKeywords($id, $kw_id = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $sql = "/* $debugMsg */ SELECT keyword_id,keywords.keyword, notes " . + " FROM {$this->tables['object_keywords']}, {$this->tables['keywords']} keywords " . + " WHERE keyword_id = keywords.id AND fk_id = {$id}"; + if (! is_null($kw_id)) { + $sql .= " AND keyword_id = {$kw_id}"; + } + return $this->db->fetchRowsIntoMap($sql, 'keyword_id'); + } + + /* + * function: get_keywords_map + * All keywords for a choosen testsuite + * + * Attention: + * probably write on obejct_keywords has not been implemented yet, + * then right now thie method can be useless. + * + * + * args :id: testsuite id + * [order_by_clause]: default: '' -> no order choosen + * must be an string with complete clause, i.e. + * 'ORDER BY keyword' + * + * + * + * returns: map: key: keyword_id + * value: keyword + * + * + */ + public function get_keywords_map($id, $opt = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $options = array( + 'order_by_clause' => '', + 'output' => 'std' + ); + $options = array_merge($options, (array) $opt); + $order_by_clause = $options['order_by_clause']; + + $sql = "/* $debugMsg */ SELECT OKW.id AS kw_link,OKW.keyword_id,keywords.keyword " . + " FROM {$this->tables['object_keywords']} OKW " . + " JOIN {$this->tables['keywords']} keywords " . + " ON OKW.keyword_id = keywords.id "; + + if (is_array($id)) { + $sql .= " AND fk_id IN (" . implode(",", $id) . ") "; + } else { + $sql .= " AND fk_id = {$id} "; + } + + $sql .= $order_by_clause; + + switch ($options['output']) { + + case 'with_link_id': + $map_keywords = $this->db->fetchRowsIntoMap($sql, 'keyword_id'); + break; + + case 'std': + default: + $map_keywords = $this->db->fetchColumnsIntoMap($sql, + 'keyword_id', 'keyword'); + break; + } + + return $map_keywords; + } + + /** + */ + private function addKeyword($id, $kw_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $status = 1; + $kw = $this->getKeywords($id, $kw_id); + if (empty($kw)) { + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['object_keywords']} " . + " (fk_id,fk_table,keyword_id) VALUES ($id,'nodes_hierarchy',$kw_id)"; + $status = $this->db->exec_query($sql) ? 1 : 0; + } + return $status; + } + + /* + * function: addKeywords + * + * args : + * + * returns: + * + */ + public function addKeywords($id, $kw_ids) + { + $status = 1; + $num_kws = count($kw_ids); + for ($idx = 0; $idx < $num_kws; $idx ++) { + $status = $status && $this->addKeyword($id, $kw_ids[$idx]); + } + return $status; + } + + /** + * deleteKeywords + */ + public function deleteKeywords($id, $kw_id = null) + { + $sql = " DELETE FROM {$this->tables['object_keywords']} + WHERE fk_id = {$id} "; + + if (! is_null($kw_id)) { + $sql .= " AND keyword_id = {$kw_id}"; + } + return $this->db->exec_query($sql); + } + + /* + * function: exportTestSuiteDataToXML + * + * args : + * + * returns: + * + */ + public function exportTestSuiteDataToXML($container_id, $tproject_id, + $optExport = array()) + { + static $keywordMgr; + static $tcaseMgr; + + if (is_null($keywordMgr)) { + $keywordMgr = new tlKeyword(); + } + + $xmlTC = null; + $relCache = array(); + + $doRecursion = isset($optExport['RECURSIVE']) ? $optExport['RECURSIVE'] : 0; + + if ($doRecursion) { + $cfXML = null; + $kwXML = null; + $attachXML = ''; + + if ($container_id == $tproject_id) { + $$tsuiteData = [ + 'id' => '', + 'name' => '', + 'node_order' => 0, + 'details' => '' + ]; + } else { + $tsuiteData = $this->get_by_id($container_id); + if (isset($optExport['KEYWORDS']) && $optExport['KEYWORDS']) { + $kwMap = $this->getKeywords($container_id); + if ($kwMap) { + $kwXML = "" . + $keywordMgr->toXMLString($kwMap, true) . + ""; + } + } + if (isset($optExport['CFIELDS']) && $optExport['CFIELDS']) { + $cfMap = (array) $this->get_linked_cfields_at_design( + $container_id, null, null, $tproject_id); + if (! empty($cfMap)) { + $cfXML = $this->cfield_mgr->exportValueAsXML($cfMap); + } + } + + $attach = []; + if (isset($optExport['ATTACHMENTS']) && $optExport['ATTACHMENTS']) { + // get all attachments + $attInfos = $this->attachmentRepository->getAttachmentInfosFor( + $container_id, $this->attachmentTableName, 'id'); + + // get all attachments content and encode it in base64 + if ($attInfos) { + foreach ($attInfos as $axInfo) { + $aID = $axInfo["id"]; + $content = $this->attachmentRepository->getAttachmentContent( + $aID, $axInfo); + + if ($content != null) { + $attach[$aID]["id"] = $aID; + $attach[$aID]["name"] = $axInfo["file_name"]; + $attach[$aID]["file_type"] = $axInfo["file_type"]; + $attach[$aID]["title"] = $axInfo["title"]; + $attach[$aID]["date_added"] = $axInfo["date_added"]; + $attach[$aID]["content"] = base64_encode( + $content); + } + } + } + + if (! empty($attach)) { + $attchRootElem = "\n{{XMLCODE}}\n"; + $attchElemTemplate = "\t\n" . + "\t\t\n" . + "\t\t\n" . + "\t\t\n" . + "\t\t\n" . + "\t\t<![CDATA[||ATTACHMENT_TITLE||]]>\n" . + "\t\t\n" . + "\t\t\n" . + "\t\n"; + + $attchDecode = array( + "||ATTACHMENT_ID||" => "id", + "||ATTACHMENT_NAME||" => "name", + "||ATTACHMENT_FILE_TYPE||" => "file_type", + "||ATTACHMENT_FILE_SIZE||" => "file_size", + "||ATTACHMENT_TITLE||" => "title", + "||ATTACHMENT_DATE_ADDED||" => "date_added", + "||ATTACHMENT_CONTENT||" => "content" + ); + $attachXML = exportDataToXML($attach, $attchRootElem, + $attchElemTemplate, $attchDecode, true); + } + } + } + $xmlTC = '' . + "\n\n" . + "
    \n{$kwXML}{$cfXML}{$attachXML}"; + } else { + $xmlTC = ""; + } + + $topt = array( + 'recursive' => self::USE_RECURSIVE_MODE + ); + if (isset($optExport['skeleton']) && $optExport['skeleton']) { + $topt['excludeTC'] = true; + } + $test_spec = $this->get_subtree($container_id, $topt); + + $childNodes = isset($test_spec['childNodes']) ? $test_spec['childNodes'] : null; + $tcaseMgr = null; + $relXmlData = ''; + if (! is_null($childNodes)) { + $loop_qty = count($childNodes); + for ($idx = 0; $idx < $loop_qty; $idx ++) { + $cNode = $childNodes[$idx]; + $nTable = $cNode['node_table']; + if ($doRecursion && $nTable == 'testsuites') { + $xmlTC .= $this->exportTestSuiteDataToXML($cNode['id'], + $tproject_id, $optExport); + } elseif ($nTable == 'testcases') { + if (is_null($tcaseMgr)) { + $tcaseMgr = new testcase($this->db); + } + $xmlTC .= $tcaseMgr->exportTestCaseDataToXML($cNode['id'], + testcase::LATEST_VERSION, $tproject_id, true, $optExport); + + // 20140816 + // Collect and do cache of all test case relations that exists inside this test suite. + $relSet = $tcaseMgr->getRelations($cNode['id']); + if ($relSet['num_relations'] > 0) { + foreach ($relSet['relations'] as $rel) { + // If we have already found this relation, skip it. + if (! in_array($rel['id'], $relCache)) { + $relXmlData .= $tcaseMgr->exportRelationToXML( + $rel, $relSet['item']); + $relCache[] = $rel['id']; + } + } + } + } + } + } + // after we scanned all relations and exported all relations to xml, let's output it to the XML buffer + $xmlTC .= $relXmlData; + $xmlTC .= $doRecursion ? "
    " : ""; + return $xmlTC; + } + + // Custom field related methods + /* + * function: get_linked_cfields_at_design + * + * + * args: $id + * [$parent_id]: + * [$filtesr]: default: null + * + * returns: hash + * + * rev : + */ + public function get_linked_cfields_at_design($id, $parent_id = null, + $filters = null, $tproject_id = null, $access_key = 'id') + { + if (! $tproject_id) { + $tproject_id = $this->getTestProjectFromTestSuite($id, $parent_id); + } + return $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, + cfield_mgr::CF_ENABLED, $filters, 'testsuite', $id, $access_key); + } + + /** + * getTestProjectFromTestSuite() + */ + public function getTestProjectFromTestSuite($id, $parent_id) + { + return $this->tree_manager->getTreeRoot( + (! is_null($id) && $id > 0) ? $id : $parent_id); + } + + /* + * function: get_linked_cfields_at_execution + * + * + * args: $id + * [$parent_id] + * [$filters] + * keys: $show_on_execution: default: null + * 1 -> filter on field show_on_execution=1 + * 0 or null -> don't filter + * + * + * returns: hash + * + * rev : + * 20110129 - franciscom - BUGID 4202 + */ + private function get_linked_cfields_at_execution($id, $parent_id = null, + $filters = null, $tproject_id = null) + { + if (! $tproject_id) { + $the_path = $this->tree_manager->get_path( + ! is_null($id) ? $id : $parent_id); + $path_len = count($the_path); + $tproject_id = ($path_len > 0) ? $the_path[$path_len - 1]['parent_id'] : $parent_id; + } + + return $this->cfield_mgr->get_linked_cfields_at_design($tproject_id, + cfield_mgr::CF_ENABLED, $filters, 'testsuite', $id); + } + + /* + * function: html_table_of_custom_field_inputs + * + * + * args: $id + * [$parent_id]: need when you call this method during the creation + * of a test suite, because the $id will be 0 or null. + * + * [$scope]: 'design','execution' + * + * returns: html string + * + */ + public function html_table_of_custom_field_inputs($id, $parent_id = null, + $scope = 'design', $name_suffix = '', $input_values = null) + { + $cf_smarty = ''; + $method_suffix = $scope == 'design' ? $scope : 'execution'; + $method_name = "get_linked_cfields_at_{$method_suffix}"; + $cf_map = $this->$method_name($id, $parent_id); + + if (! is_null($cf_map)) { + $cf_smarty = $this->cfield_mgr->html_table_inputs($cf_map, + $name_suffix, $input_values); + } + return $cf_smarty; + } + + /* + * function: html_table_of_custom_field_values + * + * + * args: $id + * [$scope]: 'design','execution' + * [$show_on_execution]: default: null + * 1 -> filter on field show_on_execution=1 + * 0 or null -> don't filter + * + * returns: html string + * + */ + public function html_table_of_custom_field_values($id, $scope = 'design', + $show_on_execution = null, $tproject_id = null, $formatOptions = null) + { + $filters = array( + 'show_on_execution' => $show_on_execution + ); + $label_css_style = ' class="labelHolder" '; + $value_css_style = ' '; + + $add_table = true; + $table_style = ''; + if (! is_null($formatOptions)) { + $label_css_style = isset($formatOptions['label_css_style']) ? $formatOptions['label_css_style'] : $label_css_style; + $value_css_style = isset($formatOptions['value_css_style']) ? $formatOptions['value_css_style'] : $value_css_style; + + $add_table = isset($formatOptions['add_table']) ? $formatOptions['add_table'] : true; + $table_style = isset($formatOptions['table_css_style']) ? $formatOptions['table_css_style'] : $table_style; + } + + $cf_smarty = ''; + $parent_id = null; + + // BUGID 3989 + $show_cf = config_get('custom_fields')->show_custom_fields_without_value; + + if ($scope == 'design') { + $cf_map = $this->get_linked_cfields_at_design($id, $parent_id, + $filters, $tproject_id); + } else { + // Important: remember that for Test Suite, custom field value CAN NOT BE changed + // at execution time just displayed. + // 20110129 - if we know test project id is better to use it + $cf_map = $this->get_linked_cfields_at_execution($id, null, null, + $tproject_id); + } + + if (! is_null($cf_map)) { + foreach ($cf_map as $cf_info) { + // if user has assigned a value, then node_id is not null + // BUGID 3989 + if ($cf_info['node_id'] || $show_cf) { + // true => do not create input in audit log + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf_info['label'], null, true)); + $cf_smarty .= "" . + htmlspecialchars($label) . "" . + "" . + $this->cfield_mgr->string_custom_field_value($cf_info, + $id) . "\n"; + } + } + } + if ((trim($cf_smarty) != "") && $add_table) { + $cf_smarty = "" . $cf_smarty . "
    "; + } + return $cf_smarty; + } + + /** + * Copy attachments from source test suite to target test suite + */ + private function copyAttachments($source_id, $target_id) + { + return $this->attachmentRepository->copyAttachments($source_id, + $target_id, $this->attachmentTableName); + } + + /** + * Copy keyword assignment + * mappings is only useful when source_id and target_id do not belong to same Test Project. + * Because keywords are defined INSIDE a Test Project, ID will be different for same keyword + * in a different Test Project + */ + private function copyKeywordAssignment($source_id, $target_id, $mappings) + { + // Get source_id keyword assignment + $sourceItems = $this->getKeywords($source_id); + if (! is_null($sourceItems)) { + // build item id list + $keySet = array_keys($sourceItems); + foreach ($keySet as $itemPos => $itemID) { + if (isset($mappings[$itemID])) { + $keySet[$itemPos] = $mappings[$itemID]; + } + } + $this->addKeywords($target_id, $keySet); + } + } + + /** + * Copy Custom Fields values + */ + private function copyCfieldsValues($source_id, $target_id) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + // Get source_id cfields assignment + $sourceItems = $this->cfield_mgr->getByLinkID($source_id, + array( + 'scope' => 'design' + )); + if (! is_null($sourceItems)) { + $sql = "/* $debugMsg */ " . + " INSERT INTO {$this->tables['cfield_design_values']} " . + " (field_id,value,node_id) " . + " SELECT field_id,value,{$target_id} AS target_id" . + " FROM {$this->tables['cfield_design_values']} " . + " WHERE node_id = {$source_id} "; + $this->db->exec_query($sql); + } + } + + /** + * get_children + * get test suites with parent = testsuite with given id + */ + public function get_children($id, $options = null) + { + $itemSet = null; + $my['options'] = array( + 'details' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $subtree = $this->tree_manager->get_children($id, + array( + 'testcase' => 'exclude_me' + )); + if (! empty($subtree)) { + foreach ($subtree as $elem) { + $itemKeys[] = $elem['id']; + } + + if ($my['options']['details'] == 'full') { + $itemSet = $this->get_by_id($itemKeys, + array( + 'orderByClause' => 'ORDER BY node_order' + )); + } else { + $itemSet = $itemKeys; + } + } + return $itemSet; + } + + /** + * get_branch + * get ONLY test suites (no other kind of node) ON BRANCH with ROOT = testsuite with given id + */ + public function get_branch($id) + { + return $this->tree_manager->get_subtree_list($id, $this->my_node_type); + } + + /** + * 'name' + * 'testProjectID' + * 'parentID' + * 'notes' + */ + public function createFromObject($item, $opt = null) + { + $my['opt'] = array( + 'doChecks' => false, + 'setSessionProject' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + define('DBUG_ON', 1); + try { + // mandatory checks + if (strlen($item->name) == 0) { + throw new Exception('Empty name is not allowed'); + } + + // what checks need to be done ? + // 1. test project exist + $pinfo = $this->tree_manager->get_node_hierarchy_info( + $item->testProjectID); + if (is_null($pinfo) || + $this->nodeTypesIdDescr[$pinfo['node_type_id']] != 'testproject') { + throw new Exception('Test project ID does not exist'); + } + + // 2. parentID exists and its node type can be: + // testproject,testsuite + // + $pinfo = $this->tree_manager->get_node_hierarchy_info( + $item->parentID); + if (is_null($pinfo)) { + throw new Exception('Parent ID does not exist'); + } + + if ($this->nodeTypesIdDescr[$pinfo['node_type_id']] != 'testproject' && + $this->nodeTypesIdDescr[$pinfo['node_type_id']] != 'testsuite') { + throw new Exception('Node Type for Parent ID is not valid'); + } + + // 3. there is NO other test suite children of parent id node with same name + $name = trim($item->name); + $op = $this->checkNameExistence($name, $item->parentID); + if (! $op['status_ok']) { + throw new Exception( + 'Test suite name is already in use at same level'); + } + } catch (Exception $e) { + throw $e; // rethrow + } + + $id = $this->tree_manager->new_node($item->parentID, $this->my_node_type, + $name, intval($item->order)); + + $sql = " INSERT INTO {$this->tables['testsuites']} (id,details) " . + " VALUES ({$id},'" . $this->db->prepare_string($item->notes) . "')"; + + $result = $this->db->exec_query($sql); + return $result ? $id : 0; + } + + /** + * Checks is there is another test plan inside test project + * with different id but same name + */ + private function checkNameExistence($name, $parentID, $id = 0) + { + $check_op['msg'] = ''; + $check_op['status_ok'] = 1; + + $getOpt = array( + 'output' => 'minimun', + 'id' => intval($id) + ); + if ($this->get_by_name($name, intval($parentID), $getOpt)) { + $check_op['msg'] = sprintf(lang_get('error_product_name_duplicate'), + $name); + $check_op['status_ok'] = 0; + } + return $check_op; + } + + /** + * + * @used-by containerEdit.php, testsuite.class.php.show + */ + public function getFileUploadRelativeURL($id) + { + // I've to use testsuiteID because this is how is name on containerEdit.php + return "lib/testcases/containerEdit.php?containerType=testsuite&doAction=fileUpload&testsuiteID=" . + intval($id); + } + + /** + * + * @used-by containerEdit.php, testsuite.class.php.show + */ + private function getDeleteAttachmentRelativeURL($id) + { + // I've to use testsuiteID because this is how is name on containerEdit.php + return "lib/testcases/containerEdit.php?containerType=testsuite&doAction=deleteFile&testsuiteID=" . + intval($id) . "&file_id="; + } + + /** + * render Image Attachments INLINE + * + * + */ + public function renderImageAttachments($id, &$item2render, $basehref = null) + { + static $attSet; + if (! $attSet || ! isset($attSet[$id])) { + $attSet[$id] = $this->attachmentRepository->getAttachmentInfosFor( + $id, $this->attachmentTableName, 'id'); + $beginTag = '[tlInlineImage]'; + $endTag = '[/tlInlineImage]'; + } + + if (is_null($attSet[$id])) { + return; + } + + // $href = '%s:%s' . " $versionTag (link)

    "; + // second \'%s\' needed if I want to use Latest as indication, need to understand + // Javascript instead of javascript, because CKeditor sometimes complains + // + // CRITIC: skipCheck is needed to render OK when creating report on Pseudo-Word format. + $bhref = is_null($basehref) ? $_SESSION['basehref'] : $basehref; + $img = '

    '; + + $key2check = array( + 'details' + ); + $rse = &$item2render; + foreach ($key2check as $item_key) { + $start = strpos($rse[$item_key], $beginTag); + $ghost = $rse[$item_key]; + + // There is at least one request to replace ? + if ($start !== false) { + $xx = explode($beginTag, $rse[$item_key]); + + // How many requests to replace ? + $xx2do = count($xx); + $ghost = ''; + for ($xdx = 0; $xdx < $xx2do; $xdx ++) { + // Hope was not a false request. + if (strpos($xx[$xdx], $endTag) !== false) { + // Separate command string from other text + // Theorically can be just ONE, but it depends + // is user had not messed things. + $yy = explode($endTag, $xx[$xdx]); + if (($elc = count($yy)) > 0) { + $atx = $yy[0]; + try { + if (isset($attSet[$id][$atx]) && + $attSet[$id][$atx]['is_image']) { + $sec = hash('sha256', + $attSet[$id][$atx]['file_name']); + $ghost .= str_replace( + array( + '%id%', + '%sec%' + ), array( + $atx, + $sec + ), $img); + } + $lim = $elc - 1; + for ($cpx = 1; $cpx <= $lim; $cpx ++) { + $ghost .= $yy[$cpx]; + } + } catch (Exception $e) { + $ghost .= $rse[$item_key]; + } + } + } else { + // nothing to do + $ghost .= $xx[$xdx]; + } + } + } + + // reconstruct field contents + if ($ghost != '') { + $rse[$item_key] = $ghost; + } + } + } + + /** + */ + private function updateDetails($id, $details) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = "/* $debugMsg */ UPDATE {$this->tables['testsuites']} " . + " SET details = '" . $this->db->prepare_string($details) . "'" . + " WHERE id = " . intval($id); + $this->db->exec_query($sql); + } + + /** + */ + private function inlineImageProcessing($id, $details, $rosettaStone) + { + // get all attachments, then check is there are images + $att = $this->attachmentRepository->getAttachmentInfosFor($id, + $this->attachmentTableName, 'id'); + + foreach ($rosettaStone as $oid => $nid) { + if ($att[$nid]['is_image']) { + $needle = str_replace($nid, $oid, $att[$nid]['inlineString']); + $inlineImg[] = array( + 'needle' => $needle, + 'rep' => $att[$nid]['inlineString'] + ); + } + } + + if (! is_null($inlineImg)) { + $dex = $details; + foreach ($inlineImg as $elem) { + $dex = str_replace($elem['needle'], $elem['rep'], $dex); + } + $this->updateDetails($id, $dex); + } + } + + /** + */ + public function buildDirectWebLink($base_href, $id, $tproject_id) + { + $tproject_mgr = new testproject($this->db); + $prefix = $tproject_mgr->getTestCasePrefix($tproject_id); + return $base_href . 'linkto.php?tprojectPrefix=' . urlencode($prefix) . + '&item=testsuite&id=' . $id; + } + + /** + * get only test cases with parent=testsuite without doing a deep search + */ + private function getChildrenLatestTCVersion($id) + { + $testcases = null; + $items = null; + $subtree = $this->tree_manager->get_children($id, + array( + 'testsuite' => 'exclude_me' + )); + + $doit = ! is_null($subtree); + + if ($doit) { + $this->get_by_id($id); + $testcases = array(); + foreach ($subtree as $elem) { + $testcases[] = $elem['id']; + } + $doit = ! empty($testcases); + } + + if ($doit) { + $inClause = implode(',', $testcases); + $sql = " SELECT tcversion_id + FROM {$this->views['latest_tcase_version_id']} + WHERE testcase_id IN ($inClause) "; + + $items = $this->db->get_recordset($sql); + } + + return $items; + } + + /** + */ + public function getTSuitesFilteredByKWSet($id, $opt = null, $filters = null) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $options = array( + 'output' => 'std' + ); + $options = array_merge($options, (array) $opt); + + $fil = array( + 'keywordsIn' => null, + 'keywordsLikeStart' => null + ); + $fil = array_merge($fil, (array) $filters); + + $fields = 'fk_id AS tsuite_id, NHTS.name AS tsuite_name,'; + + if (null != $fil['keywordsLikeStart']) { + $target = trim($fil['keywordsLikeStart']); + $fields .= " CONCAT(REPLACE(KW.keyword,'{$target}',''),':', NHTS.name) AS dyn_string "; + } else { + $fields .= " CONCAT(KW.keyword,':', NHTS.name) AS dyn_string "; + } + + switch ($options['output']) { + case 'kwname': + $fields .= ',KW.keyword'; + break; + + default: + $fields .= ",keyword_id,KW.keyword"; + break; + } + + $sql = "/* $debugMsg */ SELECT $fields FROM {$this->tables['object_keywords']} - JOIN {$this->tables['keywords']} KW - ON keyword_id = KW.id - JOIN {$this->tables['nodes_hierarchy']} NHTS - ON NHTS.id = fk_id "; - - $idSet = (array)$id; - $sql .= " WHERE fk_id IN (" . implode(",",$idSet) . ") "; - - if( null != $fil['keywordsIn'] ) { - $kwFilter = "'" . implode("','", $fil['keywordsIn']) . "'"; - $sql .= " AND KW.keyword IN (" . $kwFilter . ") "; - } - - if( null != $fil['keywordsLikeStart'] ) { - $target = $fil['keywordsLikeStart']; - $sql .= " AND KW.keyword LIKE '{$target}%' "; - } - - - $items = - $this->db->fetchRowsIntoMap($sql,'tsuite_id',database::CUMULATIVE); - - return $items; - } - - - /** - * - * - */ - function getFreeKeywords($tsuiteID, $opt = null) { - $my['opt'] = array('accessKey' => 'keyword_id', 'fields' => null, - 'orderBy' => null, 'tproject_id' => null, - 'output' => 'std', 'add_blank' => false); - - $my['opt'] = array_merge($my['opt'],(array)$opt); - - // CRITIC - $tproject_id = $my['opt']['tproject_id']; - if( null == $tproject_id ) { - $root = $this->getTestProjectFromTestSuite($tsuiteID,null); - } - $tproject_id = intval($tproject_id); - - - $safeID = intval($tsuiteID); - $sql = " SELECT KW.id AS keyword_id, KW.keyword + JOIN {$this->tables['keywords']} KW + ON keyword_id = KW.id + JOIN {$this->tables['nodes_hierarchy']} NHTS + ON NHTS.id = fk_id "; + + $idSet = (array) $id; + $sql .= " WHERE fk_id IN (" . implode(",", $idSet) . ") "; + + if (null != $fil['keywordsIn']) { + $kwFilter = "'" . implode("','", $fil['keywordsIn']) . "'"; + $sql .= " AND KW.keyword IN (" . $kwFilter . ") "; + } + + if (null != $fil['keywordsLikeStart']) { + $target = $fil['keywordsLikeStart']; + $sql .= " AND KW.keyword LIKE '{$target}%' "; + } + + return $this->db->fetchRowsIntoMap($sql, 'tsuite_id', + database::CUMULATIVE); + } + + /** + */ + private function getFreeKeywords($tsuiteID, $opt = null) + { + $my['opt'] = array( + 'accessKey' => 'keyword_id', + 'fields' => null, + 'orderBy' => null, + 'tproject_id' => null, + 'output' => 'std', + 'add_blank' => false + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + // CRITIC + $tproject_id = $my['opt']['tproject_id']; + if (null == $tproject_id) { + $this->getTestProjectFromTestSuite($tsuiteID, null); + } + $tproject_id = intval($tproject_id); + + $safeID = intval($tsuiteID); + $sql = " SELECT KW.id AS keyword_id, KW.keyword FROM {$this->tables['keywords']} KW - WHERE KW.testproject_id = {$tproject_id} - AND KW.id NOT IN + WHERE KW.testproject_id = {$tproject_id} + AND KW.id NOT IN ( - SELECT TSKW.keyword_id + SELECT TSKW.keyword_id FROM {$this->tables['object_keywords']} TSKW WHERE TSKW.fk_id = {$safeID} AND TSKW.fk_table = 'nodes_hierarchy' - ) "; - - if (!is_null($my['opt']['orderBy'])) { - $sql .= ' ' . $my['opt']['orderBy']; - } - - switch($my['opt']['output']) { - case 'html_options': - $items = $this->db->fetchColumnsIntoMap($sql,'keyword_id','keyword'); - if( null != $items && $my['opt']['add_blank']) { - $items = array(0 => '') + $items; - } - - break; - - default: - $items = $this->db->fetchRowsIntoMap($sql,$my['opt']['accessKey']); - break; - } - - return $items; - } - - /** - * - */ - function getTestproject($tsuiteID) { - $path = $this->tree_manager->get_path($tsuiteID); - return $path[0]['parent_id']; - } - - /** - * deleteKeywordByLinkID - * - */ - function deleteKeywordByLinkID( $kwLinkID ) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safeID = intval( $kwLinkID ); - $sql = " /* {$debugMsg} */ - DELETE FROM {$this->tables['object_keywords']} - WHERE id = {$kwLinkID} "; - return $this->db->exec_query($sql); - } - - - /** - * - * - */ - function addKeywordsDeep($rootTestSuiteID,$kwSet) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // Get tree of Test Suites - $tsList = $rootTestSuiteID; - - $tsSubList = trim($this->tree_manager->get_subtree_list($rootTestSuiteID,$this->my_node_type)); - - if( '' != $tsSubList ) { - $tsList .= ',' . $tsSubList; - } - - $tsSet = explode(',', $tsList); - $kwForTS = $this->getKeywordsForTSSet($tsSet); - - $vv = array(); - if( null == $kwForTS ) { - // we can add all - foreach($tsSet as $id) { - foreach($kwSet as $kaboom) { - $vv[] = "($id,'nodes_hierarchy',$kaboom)"; - } - } - } else { - // We want to avoid issue, that's why we want to get - // the difference bewteen already linked keywords and - // the new ones. - foreach($kwForTS as $tsk => $kwVenn) { - $kw2add = array_diff($kwSet,$kwVenn); - if( count($kw2add) > 0 ) { - foreach($kw2add as $kaboom) { - $vv[] = "($tsk,'nodes_hierarchy',$kaboom)"; - } - } - } - } - - if( count($vv) > 0 ) { - $sql = "/* $debugMsg */ - INSERT INTO {$this->tables['object_keywords']} - (fk_id,fk_table,keyword_id) - VALUES " . implode(',',$vv); - $this->db->exec_query($sql); - } - } - - - /** - * - */ - function getKeywordsForTSSet( $tsuiteIDSet ) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $idSet = implode(',',$tsuiteIDSet); - $sql = " /* $debugMsg */ - SELECT fk_id AS tsuite_id, OKW.keyword_id + ) "; + + if (! is_null($my['opt']['orderBy'])) { + $sql .= ' ' . $my['opt']['orderBy']; + } + + switch ($my['opt']['output']) { + case 'html_options': + $items = $this->db->fetchColumnsIntoMap($sql, 'keyword_id', + 'keyword'); + if (null != $items && $my['opt']['add_blank']) { + $items = array( + 0 => '' + ) + $items; + } + + break; + + default: + $items = $this->db->fetchRowsIntoMap($sql, + $my['opt']['accessKey']); + break; + } + + return $items; + } + + /** + */ + public function getTestproject($tsuiteID) + { + $path = $this->tree_manager->get_path($tsuiteID); + return $path[0]['parent_id']; + } + + /** + * deleteKeywordByLinkID + */ + public function deleteKeywordByLinkID($kwLinkID) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + $sql = " /* {$debugMsg} */ + DELETE FROM {$this->tables['object_keywords']} + WHERE id = {$kwLinkID} "; + return $this->db->exec_query($sql); + } + + /** + */ + public function addKeywordsDeep($rootTestSuiteID, $kwSet) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + // Get tree of Test Suites + $tsList = $rootTestSuiteID; + + $tsSubList = trim( + $this->tree_manager->get_subtree_list($rootTestSuiteID, + $this->my_node_type)); + + if ('' != $tsSubList) { + $tsList .= ',' . $tsSubList; + } + + $tsSet = explode(',', $tsList); + $kwForTS = $this->getKeywordsForTSSet($tsSet); + + $vv = array(); + if (null == $kwForTS) { + // we can add all + foreach ($tsSet as $id) { + foreach ($kwSet as $kaboom) { + $vv[] = "($id,'nodes_hierarchy',$kaboom)"; + } + } + } else { + // We want to avoid issue, that's why we want to get + // the difference bewteen already linked keywords and + // the new ones. + foreach ($kwForTS as $tsk => $kwVenn) { + $kw2add = array_diff($kwSet, $kwVenn); + if (! empty($kw2add)) { + foreach ($kw2add as $kaboom) { + $vv[] = "($tsk,'nodes_hierarchy',$kaboom)"; + } + } + } + } + + if (! empty($vv)) { + $sql = "/* $debugMsg */ + INSERT INTO {$this->tables['object_keywords']} + (fk_id,fk_table,keyword_id) + VALUES " . implode(',', $vv); + $this->db->exec_query($sql); + } + } + + /** + */ + private function getKeywordsForTSSet($tsuiteIDSet) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $idSet = implode(',', $tsuiteIDSet); + $sql = " /* $debugMsg */ + SELECT fk_id AS tsuite_id, OKW.keyword_id FROM {$this->tables['object_keywords']} OKW JOIN {$this->tables['keywords']} KW ON KW.id = OKW.keyword_id - WHERE fk_id IN ( {$idSet} ) - AND fk_table = 'nodes_hierarchy' "; - - $kw = $this->db->fetchColumnsIntoMap($sql,'tsuite_id','keyword_id',database::CUMULATIVE); - - return $kw; - } - - /** - * - * - */ - function keywordIsLinked($id,$kw) { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $idSet = $id; - $safeKW = "'" . $this->db->prepare_string(trim($kw)) . "'"; - $sql = " /* $debugMsg */ - SELECT fk_id AS tsuite_id, OKW.keyword_id + WHERE fk_id IN ( {$idSet} ) + AND fk_table = 'nodes_hierarchy' "; + + return $this->db->fetchColumnsIntoMap($sql, 'tsuite_id', 'keyword_id', + database::CUMULATIVE); + } + + /** + */ + public function keywordIsLinked($id, $kw) + { + $debugMsg = $this->debugMsg . __FUNCTION__; + + $idSet = $id; + $safeKW = "'" . $this->db->prepare_string(trim($kw)) . "'"; + $sql = " /* $debugMsg */ + SELECT fk_id AS tsuite_id, OKW.keyword_id FROM {$this->tables['object_keywords']} OKW JOIN {$this->tables['keywords']} KW ON KW.id = OKW.keyword_id - WHERE fk_id IN ( {$idSet} ) - AND fk_table = 'nodes_hierarchy' - AND KW.keyword = {$safeKW}"; - $rs = (array)$this->db->get_recordset($sql); - - return (count($rs) == 1); - } - - - - -} // end class + WHERE fk_id IN ( {$idSet} ) + AND fk_table = 'nodes_hierarchy' + AND KW.keyword = {$safeKW}"; + $rs = (array) $this->db->get_recordset($sql); + + return count($rs) == 1; + } +} diff --git a/lib/functions/tinymce.class.php b/lib/functions/tinymce.class.php index 60e101d278..288c531440 100644 --- a/lib/functions/tinymce.class.php +++ b/lib/functions/tinymce.class.php @@ -1,67 +1,71 @@ -InstanceName = $instanceName; - $this->Value = ''; - } - - function Create($rows = null,$cols = null) - { - echo $this->CreateHtml($rows,$cols); - } - - function CreateHtml($rows = null,$cols = null) - { - $HtmlValue = htmlspecialchars($this->Value); - - $my_rows = $rows; - $my_cols = $cols; - - if(is_null($my_rows) || $my_rows <= 0) - $my_rows = $this->rows; - if(is_null($my_cols) || $my_cols <= 0) - $my_cols = $this->cols; - - // rows must count place for toolbar !! - $Html = "" ; - return $Html ; - } - -} // class end - - -?> \ No newline at end of file +InstanceName = $instanceName; + $this->Value = ''; + } + + public function Create($rows = null, $cols = null) + { + echo $this->CreateHtml($rows, $cols); + } + + private function CreateHtml($rows = null, $cols = null) + { + $HtmlValue = htmlspecialchars($this->Value); + + $my_rows = $rows; + $my_cols = $cols; + + if (is_null($my_rows) || $my_rows <= 0) { + $my_rows = $this->rows; + } + if (is_null($my_cols) || $my_cols <= 0) { + $my_cols = $this->cols; + } + + // rows must count place for toolbar !! + return ""; + } +} +?> diff --git a/lib/functions/tlAttachment.class.php b/lib/functions/tlAttachment.class.php index a14bb0ad8e..ee6495496f 100644 --- a/lib/functions/tlAttachment.class.php +++ b/lib/functions/tlAttachment.class.php @@ -1,375 +1,413 @@ -fkID = NULL; - $this->fkTableName = NULL; - $this->fName = NULL; - $this->title = NULL; - $this->fType = NULL; - $this->fSize = NULL; - $this->destFPath = NULL; - $this->fContents = NULL; - $this->description = NULL; - $this->dateAdded = NULL; - $this->isImage = NULL; - $this->inlineString = NULL; - - if (!($options & self::TLOBJ_O_SEARCH_BY_ID)) - { - $this->dbID = null; - } - } - - /** - * Class constructor - * - * @param $dbID integer the database identifier of the attachment - */ - function __construct($dbID = null) - { - parent::__construct(); - - $this->compressionType = tlAttachmentRepository::getCompression(); - $this->repositoryPath = tlAttachmentRepository::getPathToRepository(); - $this->attachmentCfg = config_get('attachments'); - - $this->_clean(); - $this->dbID = $dbID; - } - - - /* - * Class destructor, cleans the object - */ - function __destruct() - { - parent::__destruct(); - $this->_clean(); - } - - /* - * - */ - function setID($id) - { - $this->dbID = $id; - } - - - - /* - * Initializes the attachment object - * - * @param object $db [ref] the db-object - * @param int $fkid the foreign key id (attachments.fk_id) - * @param string $fktableName the tablename to which the $id refers to (attachments.fk_table) - * @param string $fName the filename - * @param string $destFPath the file path - * @param string $fContents the contents of the file - * @param string $fType the mime-type of the file - * @param int $fSize the filesize (uncompressed) - * @param string $title the title used for the attachment - * - * @return integer returns tl::OK - */ - public function create($fkid,$fkTableName,$fName,$destFPath,$fContents,$fType, - $fSize,$title,$opt=null) - { - $this->_clean(); - - $title = trim($title); - $config = $this->attachmentCfg; - if($title == "") - { - switch($config->action_on_save_empty_title) - { - case 'use_filename': - $title = $fName; - break; - - default: - break; - } - - } - - $allowEmptyTitle = $config->allow_empty_title; - if( isset($opt['allow_empty_title']) ) - { - $allowEmptyTitle = $opt['allow_empty_title']; - } - - if( !$allowEmptyTitle && $title == "") - { - return self::E_TITLELENGTH; - } - - $this->fkID = $fkid; - $this->fkTableName = trim($fkTableName); - $this->fType = trim($fType); - $this->fSize = $fSize; - $this->fName = $fName; - $this->destFPath = trim($destFPath); - $this->fContents = $fContents; - - $this->isImage = !(strpos($this->fType,'image/') === FALSE); - $this->inlineString = NULL; - - //for FS-repository, the path to the repository itself is cut off, so the path is - // relative to the repository itself - $this->destFPath = str_replace($this->repositoryPath.DIRECTORY_SEPARATOR,"",$destFPath); - $this->title = trim($title); - - return tl::OK; - } - - /* Read the attachment information from the database, for filesystem repository this doesn't read - * the contents of the attachments - * - * @param $db [ref] the database connection - * @param $options integer null or TLOBJ_O_SEARCH_BY_ID - * - * @return integer returns tl::OK on success, tl::ERROR else - */ - public function readFromDB(&$db,$options = self::TLOBJ_O_SEARCH_BY_ID) - { - $this->_clean($options); - $query = "SELECT id,title,description,file_name,file_type,file_size,date_added,". - "compression_type,file_path,fk_id,fk_table FROM {$this->tables['attachments']} "; - - $clauses = null; - if ($options & self::TLOBJ_O_SEARCH_BY_ID) - $clauses[] = "id = {$this->dbID}"; - if ($clauses) - $query .= " WHERE " . implode(" AND ",$clauses); - - $info = $db->fetchFirstRow($query); - if ($info) - { - $this->fkID = $info['fk_id']; - $this->fkTableName = $info['fk_table']; - $this->fName = $info['file_name']; - $this->destFPath = $info['file_path']; - $this->fType = trim($info['file_type']); - $this->fSize = $info['file_size']; - $this->dbID = $info['id']; - $this->description = $info['description']; - $this->dateAdded = $info['date_added']; - $this->title = $info['title']; - $this->compressionType = $info['compression_type']; - - $this->isImage = !(strpos($this->fType,'image/') === FALSE); - $this->inlineString = NULL; - if($this->isImage) { - $this->inlineString = "[tlInlineImage]{$this->dbID}[/tlInlineImage]"; - } - } - - return $info ? tl::OK : tl::ERROR; - } - - /** - * Returns the attachment meta information in a legacy way - * - * @return array array with the attachment information - */ - public function getInfo() { - return array("id" => $this->dbID,"title" => $this->title, - "description" => $this->description, - "file_name" => $this->fName, "file_type" => $this->fType, - "file_size" => $this->fSize, "is_image" => $this->isImage, - "date_added" => $this->dateAdded, "inlineString" => $this->inlineString, - "compression_type" => $this->compressionType, - "file_path" => $this->destFPath, - "fk_id" => $this->fkID,"fk_table" => $this->fkTableName, - ); - } - - /* - * Writes the attachment into the database, for database repositories also the contents - * of the attachments are written - * - * @return integer returns tl::OK on success, tl::ERROR else - */ - public function writeToDB(&$db,&$itemID=null) { - $tableName = $db->prepare_string($this->fkTableName); - $fName = $db->prepare_string($this->fName); - $title = $db->prepare_string($this->title); - $fType = $db->prepare_string($this->fType); - - $destFPath = - is_null($this->destFPath) ? 'NULL' : "'" . $db->prepare_string($this->destFPath) . "'"; - - // for FS-repository the contents are null - $fContents = is_null($this->fContents) ? 'NULL' : "'" . base64_encode($this->fContents) . "'"; - - $query = "INSERT INTO {$this->tables['attachments']} - (fk_id,fk_table,file_name,file_path,file_size,file_type, date_added,content,compression_type,title) - VALUES ({$this->fkID},'{$tableName}','{$fName}',{$destFPath},{$this->fSize},'{$this->fType}'," . $db->db_now() . - ",$fContents,{$this->compressionType},'{$title}')"; - - $result = $db->exec_query($query); - if ($result) { - $this->dbID = $db->insert_id(); - $itemID = $this->dbID; - } - - return $result ? tl::OK : tl::ERROR; - } - - /* - * Deletes an attachment from the db, for databse repositories also the contents are deleted - * - * @return integer return tl::OK on success, tl::ERROR else - */ - public function deleteFromDB(&$db) { - $query = "DELETE FROM {$this->tables['attachments']} - WHERE id = {$this->dbID}"; - $result = $db->exec_query($query); - - return $result ? tl::OK : tl::ERROR; - } - - /** - * Creates an attachment by a given database identifier - * - * @param $db [ref] the database connection - * @param $id the database identifier of the attachment - * @param $detailLevel the detailLevel - * @return tlAttachment the created attachment or null on failure - */ - static public function getByID(&$db,$id,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) { - return tlDBObject::createObjectFromDB($db,$id,__CLASS__,tlAttachment::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - } - - /** - * Creates some attachments by given database identifiers - * - * @param $db [ref] the database connection - * @param $id the database identifier of the attachment - * @param $detailLevel the detailLevel - * @return array returns an array of tlAttachment (the created attachments) or null on failure - */ - static public function getByIDs(&$db,$ids,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return self::handleNotImplementedMethod(__FUNCTION__); - } - - /** - * Currently not implemented - * - * @param $db [ref] the database connection - * @param $whereClause string and addtional where clause - * @param $column string the name of column which holds the id - * @param $orderBy string an additional ORDER BY clause - * @param $detailLevel the detailLevel - * @return unknown_type - */ - static public function getAll(&$db,$whereClause = null,$column = null,$orderBy = null,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return self::handleNotImplementedMethod(__FUNCTION__); - } -}; -?> \ No newline at end of file +fkID = null; + $this->fkTableName = null; + $this->fName = null; + $this->title = null; + $this->fType = null; + $this->fSize = null; + $this->destFPath = null; + $this->fContents = null; + $this->description = null; + $this->dateAdded = null; + $this->isImage = null; + $this->inlineString = null; + + if (! ($options & self::TLOBJ_O_SEARCH_BY_ID)) { + $this->dbID = null; + } + } + + /** + * Class constructor + * + * @param $dbID integer + * the database identifier of the attachment + */ + public function __construct($dbID = null) + { + parent::__construct(); + + $this->compressionType = tlAttachmentRepository::getCompression(); + $this->repositoryPath = tlAttachmentRepository::getPathToRepository(); + $this->attachmentCfg = config_get('attachments'); + + $this->_clean(); + $this->dbID = $dbID; + } + + /* + * Class destructor, cleans the object + */ + public function __destruct() + { + parent::__destruct(); + $this->_clean(); + } + + /* + * + */ + public function setID($id) + { + $this->dbID = $id; + } + + /* + * Initializes the attachment object + * + * @param object $db [ref] the db-object + * @param int $fkid the foreign key id (attachments.fk_id) + * @param string $fktableName the tablename to which the $id refers to (attachments.fk_table) + * @param string $fName the filename + * @param string $destFPath the file path + * @param string $fContents the contents of the file + * @param string $fType the mime-type of the file + * @param int $fSize the filesize (uncompressed) + * @param string $title the title used for the attachment + * + * @return integer returns tl::OK + */ + public function create($fkid, $fkTableName, $fName, $destFPath, $fContents, + $fType, $fSize, $title, $opt = null) + { + $this->_clean(); + + $title = trim($title); + $config = $this->attachmentCfg; + if ($title == "") { + switch ($config->action_on_save_empty_title) { + case 'use_filename': + $title = $fName; + break; + + default: + break; + } + } + + $allowEmptyTitle = $config->allow_empty_title; + if (isset($opt['allow_empty_title'])) { + $allowEmptyTitle = $opt['allow_empty_title']; + } + + if (! $allowEmptyTitle && $title == "") { + return self::E_TITLELENGTH; + } + + $this->fkID = $fkid; + $this->fkTableName = trim($fkTableName); + $this->fType = trim($fType); + $this->fSize = $fSize; + $this->fName = $fName; + $this->destFPath = trim($destFPath); + $this->fContents = $fContents; + + $this->isImage = (strpos($this->fType, 'image/') !== false); + $this->inlineString = null; + + // for FS-repository, the path to the repository itself is cut off, so the path is + // relative to the repository itself + $this->destFPath = str_replace( + $this->repositoryPath . DIRECTORY_SEPARATOR, "", $destFPath); + $this->title = trim($title); + + return tl::OK; + } + + /* + * Read the attachment information from the database, for filesystem repository this doesn't read + * the contents of the attachments + * + * @param $db [ref] the database connection + * @param $options integer null or TLOBJ_O_SEARCH_BY_ID + * + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function readFromDB(&$db, $options = self::TLOBJ_O_SEARCH_BY_ID) + { + $this->_clean($options); + $query = "SELECT id,title,description,file_name,file_type,file_size,date_added," . + "compression_type,file_path,fk_id,fk_table FROM {$this->tables['attachments']} "; + + $clauses = null; + if ($options & self::TLOBJ_O_SEARCH_BY_ID) { + $clauses[] = "id = {$this->dbID}"; + } + if ($clauses) { + $query .= " WHERE " . implode(" AND ", $clauses); + } + + $info = $db->fetchFirstRow($query); + if ($info) { + $this->fkID = $info['fk_id']; + $this->fkTableName = $info['fk_table']; + $this->fName = $info['file_name']; + $this->destFPath = $info['file_path']; + $this->fType = trim($info['file_type']); + $this->fSize = $info['file_size']; + $this->dbID = $info['id']; + $this->description = $info['description']; + $this->dateAdded = $info['date_added']; + $this->title = $info['title']; + $this->compressionType = $info['compression_type']; + + $this->isImage = (strpos($this->fType, 'image/') !== false); + $this->inlineString = null; + if ($this->isImage) { + $this->inlineString = "[tlInlineImage]{$this->dbID}[/tlInlineImage]"; + } + } + + return $info ? tl::OK : tl::ERROR; + } + + /** + * Returns the attachment meta information in a legacy way + * + * @return array array with the attachment information + */ + public function getInfo() + { + return array( + "id" => $this->dbID, + "title" => $this->title, + "description" => $this->description, + "file_name" => $this->fName, + "file_type" => $this->fType, + "file_size" => $this->fSize, + "is_image" => $this->isImage, + "date_added" => $this->dateAdded, + "inlineString" => $this->inlineString, + "compression_type" => $this->compressionType, + "file_path" => $this->destFPath, + "fk_id" => $this->fkID, + "fk_table" => $this->fkTableName + ); + } + + /* + * Writes the attachment into the database, for database repositories also the contents + * of the attachments are written + * + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function writeToDB(&$db, &$itemID = null) + { + $tableName = $db->prepare_string($this->fkTableName); + $fName = $db->prepare_string($this->fName); + $title = $db->prepare_string($this->title); + $fType = $db->prepare_string($this->fType); + + $destFPath = is_null($this->destFPath) ? 'NULL' : "'" . + $db->prepare_string($this->destFPath) . "'"; + + // for FS-repository the contents are null + $fContents = is_null($this->fContents) ? 'NULL' : "'" . + base64_encode($this->fContents) . "'"; + + $query = "INSERT INTO {$this->tables['attachments']} + (fk_id,fk_table,file_name,file_path,file_size,file_type, date_added,content,compression_type,title) + VALUES ({$this->fkID},'{$tableName}','{$fName}',{$destFPath},{$this->fSize},'{$this->fType}'," . + $db->db_now() . ",$fContents,{$this->compressionType},'{$title}')"; + + $result = $db->exec_query($query); + if ($result) { + $this->dbID = $db->insert_id(); + $itemID = $this->dbID; + } + + return $result ? tl::OK : tl::ERROR; + } + + /* + * Deletes an attachment from the db, for databse repositories also the contents are deleted + * + * @return integer return tl::OK on success, tl::ERROR else + */ + public function deleteFromDB(&$db) + { + $query = "DELETE FROM {$this->tables['attachments']} + WHERE id = {$this->dbID}"; + $result = $db->exec_query($query); + + return $result ? tl::OK : tl::ERROR; + } + + /** + * Creates an attachment by a given database identifier + * + * @param $db [ref] + * the database connection + * @param $id the + * database identifier of the attachment + * @param $detailLevel the + * detailLevel + * @return tlAttachment the created attachment or null on failure + */ + public static function getByID(&$db, $id, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return tlDBObject::createObjectFromDB($db, $id, __CLASS__, + tlAttachment::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + } + + /** + * Creates some attachments by given database identifiers + * + * @param $db [ref] + * the database connection + * @param $id the + * database identifier of the attachment + * @param $detailLevel the + * detailLevel + * @return array returns an array of tlAttachment (the created attachments) or null on failure + */ + public static function getByIDs(&$db, $ids, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return self::handleNotImplementedMethod(__FUNCTION__); + } + + /** + * Currently not implemented + * + * @param database $db + * [ref] the database connection + * @param $whereClause string + * and addtional where clause + * @param $column string + * the name of column which holds the id + * @param $orderBy string + * an additional ORDER BY clause + * @param $detailLevel the + * detailLevel + * @return unknown_type + */ + public static function getAll(&$db, $whereClause = null, $column = null, + $orderBy = null, $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return self::handleNotImplementedMethod(__FUNCTION__); + } +} +?> diff --git a/lib/functions/tlAttachmentRepository.class.php b/lib/functions/tlAttachmentRepository.class.php index 02be4d4a53..5f032ff650 100644 --- a/lib/functions/tlAttachmentRepository.class.php +++ b/lib/functions/tlAttachmentRepository.class.php @@ -1,602 +1,664 @@ -repositoryType = self::getType(); - $this->repositoryCompressionType = self::getCompression(); - $this->repositoryPath = self::getPathToRepository(); - $this->attachmentCfg = config_get('attachments'); - - $this->attmObj = new tlAttachment(); - } - - /** - * Creates the one and only repository object - * - * @param $db [ref] resource the database connection - * @return tlAttachmenRepository - */ - public static function create(&$db) - { - if (!isset(self::$s_instance)) - { - $c = __CLASS__; - self::$s_instance = new $c($db); - } - - return self::$s_instance; - } - - /** - * Returns the type of the repository, like filesystem, database,... - * - * @return integer the type of the repository - */ - public static function getType() - { - return config_get('repositoryType'); - } - /** - * returns the compression type of the repository - * - * @return integer the compression type - */ - public static function getCompression() - { - return config_get('repositoryCompressionType'); - } - /** - * returns the path to the repository - * - * @return string path to the repository - */ - public static function getPathToRepository() - { - return config_get('repositoryPath'); - } - - - /** - * Inserts the information about an attachment into the db - * - * @param int $fkid the foreign key id (attachments.fk_id) - * @param string $fktableName the tablename to which the $id refers to (attachments.fk_table) - * @param string $title the title used for the attachment - * @param array $fInfo should be $_FILES in most cases - * - * @return int returns true if the information was successfully stored, false else - * - **/ - public function insertAttachment($fkid,$fkTableName,$title,$fInfo,$opt=null) - { - $op = new stdClass(); - $op->statusOK = false; - $op->msg = ''; - $op->statusCode = 0; - - $fName = isset($fInfo['name']) ? $fInfo['name'] : null; - $fType = isset($fInfo['type']) ? $fInfo['type'] : ''; - $fSize = isset($fInfo['size']) ? $fInfo['size'] : 0; - $fTmpName = isset($fInfo['tmp_name']) ? $fInfo['tmp_name'] : ''; - - if (null == $fName || '' == $fType || 0 == $fSize) { - $op->statusCode = 'fNameORfTypeOrfSize'; - return $op; - } - - // Process filename against XSS - // Thanks to http://owasp.org/index.php/Unrestricted_File_Upload - $pattern = trim($this->attachmentCfg->allowed_filenames_regexp); - if( '' != $pattern && !preg_match($pattern,$fName) ){ - $op->statusCode = 'allowed_filenames_regexp'; - $op->msg = lang_get('FILE_UPLOAD_' . $op->statusCode); - return $op; - } - - $fExt = getFileExtension($fName,""); - if( '' == $fExt ) { - $op->msg = 'empty extension -> failed'; - $op->statusCode = 'empty_extension'; - return $op; - } - - $allowed = explode(',',$this->attachmentCfg->allowed_files); - if (!in_array($fExt, $allowed)) { - $op->statusCode = 'allowed_files'; - $op->msg = lang_get('FILE_UPLOAD_' . $op->statusCode); - return $op; - } - - // Go ahead - $fContents = null; - $destFPath = null; - $destFName = getUniqueFileName($fExt); - - if ($this->repositoryType == TL_REPOSITORY_TYPE_FS) { - $destFPath = $this->buildRepositoryFilePath($destFName,$fkTableName,$fkid); - $op->statusOK = $this->storeFileInFSRepository($fTmpName,$destFPath); - } else { - $fContents = $this->getFileContentsForDBRepository($fTmpName,$destFName); - $op->statusOK = sizeof($fContents); - if($op->statusOK) { - @unlink($fTmpName); - } - } - - if ($op->statusOK) { - $op->statusOK = - ($this->attmObj->create($fkid,$fkTableName,$fName,$destFPath,$fContents,$fType,$fSize,$title,$opt) >= tl::OK); - - if ($op->statusOK) { - $op->statusOK = $this->attmObj->writeToDb($this->db); - } else { - @unlink($destFPath); - } - } - - return $op; - } - - /** - * Builds the path for a given filename according to the tablename and id - * - * @param string $destFName the fileName - * @param string $tableName the tablename to which $id referes to (attachments.fk_table) - * @param int $id the foreign key id attachments.fk_id) - * - * @return string returns the full path for the file - **/ - public function buildRepositoryFilePath($destFName,$tableName,$id) - { - $destFPath = $this->buildRepositoryFolderFor($tableName,$id,true); - $destFPath .= DIRECTORY_SEPARATOR.$destFName; - - return $destFPath; - } - - /** - * Fetches the contents of a file for storing it into the DB-repository - * - * @param string $fTmpName the filename of the attachment - * @param string $destFName a unique file name for temporary usage - * - * @return string the contents of the attachment to be stored into the db - **/ - protected function getFileContentsForDBRepository($fTmpName,$destFName) - { - $tmpGZName = null; - switch($this->repositoryCompressionType) - { - case TL_REPOSITORY_COMPRESSIONTYPE_NONE: - break; - - case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: - //copy the file into a dummy file in the repository and gz it and - //read the file contents from this new file - $tmpGZName = $this->repositoryPath.DIRECTORY_SEPARATOR.$destFName.".gz"; - gzip_compress_file($fTmpName, $tmpGZName); - $fTmpName = $tmpGZName; - break; - } - $fContents = getFileContents($fTmpName); - - //delete the dummy file if present - if (!is_null($tmpGZName)) - { - unlink($tmpGZName); - } - - return $fContents; - } - - /** - * Stores a file into the FS-repository. - * It checks if the given tmp name is of an uploaded file. - * If so, it moves the file from the temp dir to the upload destination using move_uploaded_file(). - * Else it simply rename the file through rename function. - * This process is needed to allow use of this method when uploading attachments via XML-RPC API - * - * @param string $fTmpName the filename - * @param string $destFPath [ref] the destination file name - * - * @return bool returns true if the file was uploaded, false else - * - * @internal revision - * 20100918 - francisco.mancardi@gruppotesi.com - BUGID 1890 - contribution by kinow - **/ - protected function storeFileInFSRepository($fTmpName,&$destFPath) - { - switch($this->repositoryCompressionType) - { - case TL_REPOSITORY_COMPRESSIONTYPE_NONE: - if ( is_uploaded_file($fTmpName)) - { - $fileUploaded = move_uploaded_file($fTmpName,$destFPath); - } - else - { - $fileUploaded = rename($fTmpName,$destFPath); - } - break; - - case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: - //add the gz extension and compress the file - $destFPath .= ".gz"; - $fileUploaded = gzip_compress_file($fTmpName,$destFPath); - break; - } - return $fileUploaded; - } - - /** - * Builds the repository folder for the attachment - * - * @param string $tableName the tablename to which $id referes to (attachments.fk_table) - * @param int $id the foreign key id attachments.fk_id) - * @param bool $mkDir if true then the the directory will be created, else not - * - * @return string returns the full path for the folder - **/ - protected function buildRepositoryFolderFor($tableName,$id,$mkDir = false) - { - $path = $this->repositoryPath.DIRECTORY_SEPARATOR.$tableName; - if ($mkDir && !file_exists($path)) - mkdir($path); - $path .= DIRECTORY_SEPARATOR.$id; - if ($mkDir && !file_exists($path)) - mkdir($path); - - return $path; - } - - /** - * Deletes an attachment from the filesystem - * - * @param $dummy not used, only there to keep the interface equal to deleteAttachmentFromDB - * @param $attachmentInfo array with information about the attachments - * @return interger returns tl::OK on success, tl::ERROR else - */ - protected function deleteAttachmentFromFS($dummy,$attachmentInfo = null) - { - $filePath = $attachmentInfo['file_path']; - - $destFPath = $this->repositoryPath.DIRECTORY_SEPARATOR.$filePath; - return @unlink($destFPath) ? tl::OK : tl::ERROR; - } - - /** - * Deletes an attachment from the database - * - * @param $id integer the database identifier of the attachment - * @param $dummy not used, only there to keep the interface equal to deleteAttachmentFromDB - * @return integer returns tl::OK on success, tl::ERROR else - */ - protected function deleteAttachmentFromDB($id,$dummy = null) - { - $this->attmObj->setID($id); - return $this->attmObj->deleteFromDB($this->db); - } - - /** - * Deletes the attachment with the given database id - * - * @param $id integer the database identifier of the attachment - * @param $attachmentInfo array, optional information about the attachment - * @return integer returns tl::OK on success, tl::ERROR else - */ - public function deleteAttachment($id,$attachmentInfo = null) - { - $bResult = tl::ERROR; - if (is_null($attachmentInfo)) - $attachmentInfo = $this->getAttachmentInfo($id); - if ($attachmentInfo) - { - $bResult = tl::OK; - if (trim($attachmentInfo['file_path']) != "") - $bResult = $this->deleteAttachmentFromFS($id,$attachmentInfo); - $bResult = $this->deleteAttachmentFromDB($id,null) && $bResult; - } - return $bResult ? tl::OK : tl::ERROR; - } - - /** - * Gets the contents of the attachments from the repository - * - * @param $id integer the database identifier of the attachment - * @param $attachmentInfo array, optional information about the attachment - * @return string the contents of the attachment or null on error - * - * @internal revision - * 20101208 - franciscom - BUGID 4085 - */ - public function getAttachmentContent($id,$attachmentInfo = null) - { - $content = null; - if (!$attachmentInfo) - { - $attachmentInfo = $this->getAttachmentInfo($id); - } - - if ($attachmentInfo) - { - $fname = 'getAttachmentContentFrom'; - $fname .= ($this->repositoryType == TL_REPOSITORY_TYPE_FS) ? 'FS' : 'DB'; - $content = $this->$fname($id); - } - return $content; - } - - /** - * Gets the contents of the attachment given by it's database identifier from the filesystem - * - * @param $id integer the database identifier of the attachment - * @return string the contents of the attachment or null on error - */ - protected function getAttachmentContentFromFS($id) - { - $query = "SELECT file_size,compression_type,file_path " . - " FROM {$this->tables['attachments']} WHERE id = {$id}"; - $row = $this->db->fetchFirstRow($query); - - $content = null; - if ($row) - { - $filePath = $row['file_path']; - $fileSize = $row['file_size']; - $destFPath = $this->repositoryPath.DIRECTORY_SEPARATOR.$filePath; - switch($row['compression_type']) - { - case TL_REPOSITORY_COMPRESSIONTYPE_NONE: - $content = getFileContents($destFPath); - break; - - case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: - $content = gzip_readFileContent($destFPath,$fileSize); - break; - } - } - - return $content; - } - /** - * Gets some common infos about attachments - * - * @param int $id the id of the attachment (attachments.id) - * - * @return string returns the contents of the attachment - */ - //@TODO schlundus, should be protected, but blocker is testcase::copy_attachments - public function getAttachmentContentFromDB($id) - { - $query = "SELECT content,file_size,compression_type " . - " FROM {$this->tables['attachments']} WHERE id = {$id}"; - $row = $this->db->fetchFirstRow($query); - - $content = null; - if ($row) - { - $content = $row['content']; - $fileSize = $row['file_size']; - switch($row['compression_type']) - { - case TL_REPOSITORY_COMPRESSIONTYPE_NONE: - break; - - case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: - $content = gzip_uncompress_content($content,$fileSize); - break; - } - } - - return $content; - } - - /** - * Creates a temporary file and writes the attachment content into this file. - * - * @param $base64encodedContent base64 encoded file content - * - * @since 1.9.17 - * @return file handler - */ - public function createAttachmentTempFile( $base64encodedContent ) - { - $resultInfo = array(); - $filename = tempnam(sys_get_temp_dir(), 'tl-'); - - $resultInfo["tmp_name"] = $filename; - $handle = fopen( $filename, "w" ); - fwrite($handle, base64_decode( $base64encodedContent )); - fclose( $handle ); - - $filesize = filesize($filename); - $resultInfo["size"] = $filesize; - - return $resultInfo; - } - - - /** - * Deletes all attachments of a certain object of a given type - * - * @param $fkid integer the id of the object whose attachments should be deleted - * @param $fkTableName the "type" of the object, or the table the object is stored in - * - * @return boolean returns bSuccess if all attachments are deleted, false else - */ - public function deleteAttachmentsFor($fkid,$fkTableName) { - $statusOK = true; - $attachmentIDs = (array)$this->getAttachmentIDsFor($fkid,$fkTableName); - - for($i = 0;$i < sizeof($attachmentIDs);$i++) { - $id = $attachmentIDs[$i]; - $statusOK = ($this->deleteAttachment($id) && $statusOK); - } - - if ($statusOK) { - $folder = $this->buildRepositoryFolderFor($fkTableName,$fkid); - if (is_dir($folder)) { - rmdir($folder); - } - } - return $statusOK; - } - - /** - * Reads the information about the attachment with the given database id - * - * @param $id integer the database identifier of the attachment - * @return array the information about the attachment - */ - public function getAttachmentInfo($id) - { - $info = null; - $this->attmObj->setID($id); - if ($this->attmObj->readFromDB($this->db)) - { - $info = $this->attmObj->getInfo(); - } - return $info; - } - - /** - * Reads all attachments for a certain object of a given type - * - * @param $fkid integer the id of the object whose attachments should be read - * @param $fkTableName the "type" of the object, or the table the object is stored in - * - * @return arrays returns an array with the attachments of the objects, or null on error - */ - public function getAttachmentInfosFor($fkid,$fkTableName,$accessKey='std') - { - $itemSet = null; - $idSet = (array)$this->getAttachmentIDsFor($fkid,$fkTableName); - $loop2do = sizeof($idSet); - for($idx = 0;$idx < $loop2do; $idx++) { - $attachmentInfo = $this->getAttachmentInfo($idSet[$idx]); - if (null != $attachmentInfo) { - // needed because on inc_attachments.tpl this test: - // {if $info.title eq ""} - // is used to undertand if icon or other handle is needed to access - // file content - $attachmentInfo['title'] = trim($attachmentInfo['title']); - switch($accessKey) { - case 'id': - $itemSet[$attachmentInfo['id']] = $attachmentInfo; - break; - - default: - $itemSet[] = $attachmentInfo; - break; - } - } - } - return $itemSet; - } - - /** - * Yields all attachmentids for a certain object of a given type - * - * @param $fkid integer the id of the object whose attachments should be read - * @param $fkTableName the "type" of the object, or the table the object is stored in - * - * @return arrays returns an array with the attachments of the objects, or null on error - */ - public function getAttachmentIDsFor($fkid,$fkTableName) - { - $order_by = $this->attachmentCfg->order_by; - - $query = "SELECT id FROM {$this->tables['attachments']} WHERE fk_id = {$fkid} " . - " AND fk_table = '" . $this->db->prepare_string($fkTableName). "' " . $order_by; - $attachmentIDs = $this->db->fetchColumnsIntoArray($query,'id'); - - return $attachmentIDs; - } - - /* - * @param $fkTableName the "type" of the object, or the table the object is stored in - */ - function copyAttachments($source_id,$target_id,$fkTableName) { - $mapping = null; - $f_parts = null; - $destFPath = null; - $mangled_fname = ''; - $status_ok = false; - $attachments = $this->getAttachmentInfosFor($source_id,$fkTableName); - if( null != $attachments && count($attachments) > 0) { - foreach($attachments as $key => $value) { - $file_contents = null; - $f_parts = explode(DIRECTORY_SEPARATOR,$value['file_path']); - $mangled_fname = $f_parts[count($f_parts)-1]; - - if ($this->repositoryType == TL_REPOSITORY_TYPE_FS) { - $destFPath = $this->buildRepositoryFilePath($mangled_fname,$fkTableName,$target_id); - $status_ok = copy($this->repositoryPath . $value['file_path'],$destFPath); - } else { - $file_contents = $this->getAttachmentContentFromDB($value['id']); - $status_ok = sizeof($file_contents); - } - - if($status_ok) { - $this->attmObj->create($target_id,$fkTableName,$value['file_name'], - $destFPath,$file_contents,$value['file_type'], - $value['file_size'],$value['title']); - $attID = 0; - $this->attmObj->writeToDB($this->db,$attID); - $mapping[$value['id']] = $attID; - } - } - } - - return $mapping; - } -} \ No newline at end of file +repositoryType = self::getType(); + $this->repositoryCompressionType = self::getCompression(); + $this->repositoryPath = self::getPathToRepository(); + $this->attachmentCfg = config_get('attachments'); + + $this->attmObj = new tlAttachment(); + } + + /** + * Creates the one and only repository object + * + * @param database $db + * [ref] + * resource the database connection + * @return tlAttachmentRepository + */ + public static function create(&$db) + { + if (! isset(self::$s_instance)) { + $c = __CLASS__; + self::$s_instance = new $c($db); + } + + return self::$s_instance; + } + + /** + * Returns the type of the repository, like filesystem, database,... + * + * @return integer the type of the repository + */ + public static function getType() + { + return config_get('repositoryType'); + } + + /** + * returns the compression type of the repository + * + * @return integer the compression type + */ + public static function getCompression() + { + return config_get('repositoryCompressionType'); + } + + /** + * returns the path to the repository + * + * @return string path to the repository + */ + public static function getPathToRepository() + { + return config_get('repositoryPath'); + } + + /** + * Inserts the information about an attachment into the db + * + * @param int $fkid + * the foreign key id (attachments.fk_id) + * @param string $fktableName + * the tablename to which the $id refers to (attachments.fk_table) + * @param string $title + * the title used for the attachment + * @param array $fInfo + * should be $_FILES in most cases + * + * @return int returns true if the information was successfully stored, false else + * + */ + public function insertAttachment($fkid, $fkTableName, $title, $fInfo, + $opt = null) + { + $op = new stdClass(); + $op->statusOK = false; + $op->msg = ''; + $op->statusCode = 0; + + $fName = isset($fInfo['name']) ? $fInfo['name'] : null; + $fType = isset($fInfo['type']) ? $fInfo['type'] : ''; + $fSize = isset($fInfo['size']) ? $fInfo['size'] : 0; + $fTmpName = isset($fInfo['tmp_name']) ? $fInfo['tmp_name'] : ''; + + if (null == $fName || '' == $fType || 0 == $fSize) { + $op->statusCode = 'fNameORfTypeOrfSize'; + return $op; + } + + // Process filename against XSS + // Thanks to http://owasp.org/index.php/Unrestricted_File_Upload + $pattern = trim($this->attachmentCfg->allowed_filenames_regexp); + if ('' != $pattern && ! preg_match($pattern, $fName)) { + $op->statusCode = 'allowed_filenames_regexp'; + $op->msg = str_replace('%filename%', $fName, + lang_get('FILE_UPLOAD_' . $op->statusCode)); + return $op; + } + + $fExt = getFileExtension($fName, ""); + if ('' == $fExt) { + $op->msg = 'empty extension -> failed'; + $op->statusCode = 'empty_extension'; + return $op; + } + + $allowed = explode(',', $this->attachmentCfg->allowed_files); + if (! in_array($fExt, $allowed)) { + $op->statusCode = 'allowed_files'; + $op->msg = str_replace('%filename%', $fName, + lang_get('FILE_UPLOAD_' . $op->statusCode)); + return $op; + } + + // Go ahead + $fContents = null; + $destFPath = null; + $destFName = getUniqueFileName($fExt); + + if ($this->repositoryType == TL_REPOSITORY_TYPE_FS) { + $destFPath = $this->buildRepositoryFilePath($destFName, $fkTableName, + $fkid); + $op->statusOK = $this->storeFileInFSRepository($fTmpName, $destFPath); + } else { + $fContents = $this->getFileContentsForDBRepository($fTmpName, + $destFName); + $op->statusOK = count($fContents); + if ($op->statusOK) { + @unlink($fTmpName); + } + } + + if ($op->statusOK) { + $stdTableUsedAsFolder = str_replace(DB_TABLE_PREFIX, '', + $fkTableName); + $op->statusOK = ($this->attmObj->create($fkid, $stdTableUsedAsFolder, + $fName, $destFPath, $fContents, $fType, $fSize, $title, $opt) >= + tl::OK); + + if ($op->statusOK) { + $op->statusOK = $this->attmObj->writeToDb($this->db); + } else { + @unlink($destFPath); + } + } + + return $op; + } + + /** + * Builds the path for a given filename according to the tablename and id + * + * @param string $destFName + * the fileName + * @param string $tableName + * the tablename to which $id referes to (attachments.fk_table) + * @param int $id + * the foreign key id attachments.fk_id) + * + * @return string returns the full path for the file + */ + public function buildRepositoryFilePath($destFName, $tableName, $id) + { + $destFPath = $this->buildRepositoryFolderFor($tableName, $id, true); + $destFPath .= DIRECTORY_SEPARATOR . $destFName; + + return $destFPath; + } + + /** + * Fetches the contents of a file for storing it into the DB-repository + * + * @param string $fTmpName + * the filename of the attachment + * @param string $destFName + * a unique file name for temporary usage + * + * @return string the contents of the attachment to be stored into the db + */ + protected function getFileContentsForDBRepository($fTmpName, $destFName) + { + $tmpGZName = null; + switch ($this->repositoryCompressionType) { + case TL_REPOSITORY_COMPRESSIONTYPE_NONE: + break; + + case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: + // copy the file into a dummy file in the repository and gz it and + // read the file contents from this new file + $tmpGZName = $this->repositoryPath . DIRECTORY_SEPARATOR . + $destFName . ".gz"; + gzip_compress_file($fTmpName, $tmpGZName); + $fTmpName = $tmpGZName; + break; + } + $fContents = getFileContents($fTmpName); + + // delete the dummy file if present + if (! is_null($tmpGZName)) { + unlink($tmpGZName); + } + + return $fContents; + } + + /** + * Stores a file into the FS-repository. + * It checks if the given tmp name is of an uploaded file. + * If so, it moves the file from the temp dir to the upload destination using move_uploaded_file(). + * Else it simply rename the file through rename function. + * This process is needed to allow use of this method when uploading attachments via XML-RPC API + * + * @param string $fTmpName + * the filename + * @param string $destFPath + * [ref] the destination file name + * + * @return bool returns true if the file was uploaded, false else + * + * @internal revision + * 20100918 - francisco.mancardi@gruppotesi.com - BUGID 1890 - contribution by kinow + */ + protected function storeFileInFSRepository($fTmpName, &$destFPath) + { + switch ($this->repositoryCompressionType) { + case TL_REPOSITORY_COMPRESSIONTYPE_NONE: + if (is_uploaded_file($fTmpName)) { + $fileUploaded = move_uploaded_file($fTmpName, $destFPath); + } else { + $fileUploaded = rename($fTmpName, $destFPath); + } + break; + + case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: + // add the gz extension and compress the file + $destFPath .= ".gz"; + $fileUploaded = gzip_compress_file($fTmpName, $destFPath); + break; + } + return $fileUploaded; + } + + /** + * Builds the repository folder for the attachment + * + * @param string $tableName + * the tablename to which $id referes to (attachments.fk_table) + * @param int $id + * the foreign key id attachments.fk_id) + * @param bool $mkDir + * if true then the the directory will be created, else not + * + * @return string returns the full path for the folder + */ + protected function buildRepositoryFolderFor($tableName, $id, $mkDir = false) + { + // use always the STANDARD table name i.e. WITHOUT PREFIX + $leafFolder = str_replace(DB_TABLE_PREFIX, '', $tableName); + $path = $this->repositoryPath . DIRECTORY_SEPARATOR . $leafFolder; + if (! empty($path) && $mkDir && ! file_exists($path)) { + mkdir($path); + } + + $path .= DIRECTORY_SEPARATOR . $id; + if (! empty($path) && $mkDir && ! file_exists($path)) { + mkdir($path); + } + + return $path; + } + + /** + * Deletes an attachment from the filesystem + * + * @param $dummy not + * used, only there to keep the interface equal to deleteAttachmentFromDB + * @param $attachmentInfo array + * with information about the attachments + * @return interger returns tl::OK on success, tl::ERROR else + */ + protected function deleteAttachmentFromFS($dummy, $attachmentInfo = null) + { + $filePath = $attachmentInfo['file_path']; + + $destFPath = $this->repositoryPath . DIRECTORY_SEPARATOR . $filePath; + return @unlink($destFPath) ? tl::OK : tl::ERROR; + } + + /** + * Deletes an attachment from the database + * + * @param $id integer + * the database identifier of the attachment + * @param $dummy not + * used, only there to keep the interface equal to deleteAttachmentFromDB + * @return integer returns tl::OK on success, tl::ERROR else + */ + protected function deleteAttachmentFromDB($id, $dummy = null) + { + $this->attmObj->setID($id); + return $this->attmObj->deleteFromDB($this->db); + } + + /** + * Deletes the attachment with the given database id + * + * @param $id integer + * the database identifier of the attachment + * @param $attachmentInfo array, + * optional information about the attachment + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function deleteAttachment($id, $attachmentInfo = null) + { + $bResult = tl::ERROR; + if (is_null($attachmentInfo)) { + $attachmentInfo = $this->getAttachmentInfo($id); + } + if ($attachmentInfo) { + $bResult = tl::OK; + if (trim($attachmentInfo['file_path']) != "") { + $bResult = $this->deleteAttachmentFromFS($id, $attachmentInfo); + } + $bResult = $this->deleteAttachmentFromDB($id, null) && $bResult; + } + return $bResult ? tl::OK : tl::ERROR; + } + + /** + * Gets the contents of the attachments from the repository + * + * @param $id integer + * the database identifier of the attachment + * @param $attachmentInfo array, + * optional information about the attachment + * @return string the contents of the attachment or null on error + * + * @internal revision + * 20101208 - franciscom - BUGID 4085 + */ + public function getAttachmentContent($id, $attachmentInfo = null) + { + $content = null; + if (! $attachmentInfo) { + $attachmentInfo = $this->getAttachmentInfo($id); + } + + if ($attachmentInfo) { + $fname = 'getAttachmentContentFrom'; + $fname .= ($this->repositoryType == TL_REPOSITORY_TYPE_FS) ? 'FS' : 'DB'; + $content = $this->$fname($id); + } + return $content; + } + + /** + * Gets the contents of the attachment given by it's database identifier from the filesystem + * + * @param $id integer + * the database identifier of the attachment + * @return string the contents of the attachment or null on error + */ + protected function getAttachmentContentFromFS($id) + { + $query = "SELECT file_size,compression_type,file_path " . + " FROM {$this->tables['attachments']} + WHERE id = {$id}"; + $row = $this->db->fetchFirstRow($query); + + $content = null; + if ($row) { + $filePath = $row['file_path']; + $fileSize = $row['file_size']; + $destFPath = $this->repositoryPath . DIRECTORY_SEPARATOR . $filePath; + + switch ($row['compression_type']) { + case TL_REPOSITORY_COMPRESSIONTYPE_NONE: + $content = getFileContents($destFPath); + break; + + case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: + $content = gzip_readFileContent($destFPath, $fileSize); + break; + } + } + return $content; + } + + /** + * Gets some common infos about attachments + * + * @param int $id + * the id of the attachment (attachments.id) + * + * @return string the contents of the attachment + */ + // @TODO schlundus, should be protected, but blocker is testcase::copy_attachments + public function getAttachmentContentFromDB($id) + { + $query = "SELECT content,file_size,compression_type " . + " FROM {$this->tables['attachments']} WHERE id = {$id}"; + $row = $this->db->fetchFirstRow($query); + + $content = null; + if ($row) { + $content = $row['content']; + $fileSize = $row['file_size']; + switch ($row['compression_type']) { + case TL_REPOSITORY_COMPRESSIONTYPE_NONE: + break; + + case TL_REPOSITORY_COMPRESSIONTYPE_GZIP: + $content = gzip_uncompress_content($content, $fileSize); + break; + } + } + + return $content; + } + + /** + * Creates a temporary file and writes the attachment content into this file. + * + * @param $base64encodedContent base64 + * encoded file content + * + * @since 1.9.17 + * @return file handler + */ + public function createAttachmentTempFile($base64encodedContent) + { + $resultInfo = array(); + $filename = tempnam(sys_get_temp_dir(), 'tl-'); + + $resultInfo["tmp_name"] = $filename; + $handle = fopen($filename, "w"); + fwrite($handle, base64_decode($base64encodedContent)); + fclose($handle); + + $filesize = filesize($filename); + $resultInfo["size"] = $filesize; + + return $resultInfo; + } + + /** + * Deletes all attachments of a certain object of a given type + * + * @param int $fkid + * the id of the object whose attachments should be deleted + * @param string $fkTableName + * the "type" of the object, or the table the object is stored in + * + * @return boolean returns bSuccess if all attachments are deleted, false else + */ + public function deleteAttachmentsFor($fkid, $fkTableName) + { + $stdTableUsedAsFolder = str_replace(DB_TABLE_PREFIX, '', $fkTableName); + $statusOK = true; + $attachmentIDs = (array) $this->getAttachmentIDsFor($fkid, + $stdTableUsedAsFolder); + + for ($i = 0; $i < count($attachmentIDs); $i ++) { + $id = $attachmentIDs[$i]; + $statusOK = ($this->deleteAttachment($id) && $statusOK); + } + + if ($statusOK) { + $folder = $this->buildRepositoryFolderFor($stdTableUsedAsFolder, + $fkid); + if (is_dir($folder)) { + rmdir($folder); + } + } + return $statusOK; + } + + /** + * Reads the information about the attachment with the given database id + * + * @param $id integer + * the database identifier of the attachment + * @return array the information about the attachment + */ + public function getAttachmentInfo($id) + { + $info = null; + $this->attmObj->setID($id); + if ($this->attmObj->readFromDB($this->db)) { + $info = $this->attmObj->getInfo(); + } + return $info; + } + + /** + * Reads all attachments for a certain object of a given type + * + * @param int $fkid + * integer + * the id of the object whose attachments should be read + * @param string $fkTableName + * the + * "type" of the object, or the table the object is stored in + * + * @return array with the attachments of the objects, or null on error + */ + public function getAttachmentInfosFor($fkid, $fkTableName, + $accessKey = 'std') + { + $itemSet = null; + $stdTableUsedAsFolder = str_replace(DB_TABLE_PREFIX, '', $fkTableName); + + $idSet = (array) $this->getAttachmentIDsFor($fkid, $stdTableUsedAsFolder); + $loop2do = count($idSet); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $attachmentInfo = $this->getAttachmentInfo($idSet[$idx]); + if (null != $attachmentInfo) { + // needed because on inc_attachments.tpl this test: + // {if $info.title eq ""} + // is used to undertand if icon or other handle is needed to access + // file content + $attachmentInfo['title'] = trim($attachmentInfo['title']); + switch ($accessKey) { + case 'id': + $itemSet[$attachmentInfo['id']] = $attachmentInfo; + break; + + default: + $itemSet[] = $attachmentInfo; + break; + } + } + } + return $itemSet; + } + + /** + * Yields all attachmentids for a certain object of a given type + * + * @param int $fkid + * the id of the object whose attachments should be read + * @param string $fkTableName + * the "type" of the object, or the table the object is stored in + * + * @return array with the attachments of the objects, or null on error + */ + public function getAttachmentIDsFor($fkid, $fkTableName) + { + $stdTableUsedAsFolder = str_replace(DB_TABLE_PREFIX, '', $fkTableName); + + $order_by = $this->attachmentCfg->order_by; + + $query = "SELECT id FROM {$this->tables['attachments']} WHERE fk_id = {$fkid} " . + " AND fk_table = '" . + $this->db->prepare_string($stdTableUsedAsFolder) . "' " . $order_by; + return $this->db->fetchColumnsIntoArray($query, 'id'); + } + + /* + * @param $fkTableName the "type" of the object, or the table the object is stored in + */ + public function copyAttachments($source_id, $target_id, $fkTableName) + { + $mapping = null; + $f_parts = null; + $destFPath = null; + $mangled_fname = ''; + $status_ok = false; + $stdTableUsedAsFolder = str_replace(DB_TABLE_PREFIX, '', $fkTableName); + + $attachments = $this->getAttachmentInfosFor($source_id, + $stdTableUsedAsFolder); + if (! empty($attachments)) { + foreach ($attachments as $value) { + $file_contents = null; + $f_parts = explode(DIRECTORY_SEPARATOR, $value['file_path']); + $mangled_fname = $f_parts[count($f_parts) - 1]; + + if ($this->repositoryType == TL_REPOSITORY_TYPE_FS) { + $destFPath = $this->buildRepositoryFilePath($mangled_fname, + $stdTableUsedAsFolder, $target_id); + $status_ok = copy( + $this->repositoryPath . $value['file_path'], $destFPath); + } else { + $file_contents = $this->getAttachmentContentFromDB( + $value['id']); + $status_ok = count($file_contents); + } + + if ($status_ok) { + $this->attmObj->create($target_id, $stdTableUsedAsFolder, + $value['file_name'], $destFPath, $file_contents, + $value['file_type'], $value['file_size'], + $value['title']); + $attID = 0; + $this->attmObj->writeToDB($this->db, $attID); + $mapping[$value['id']] = $attID; + } + } + } + + return $mapping; + } +} diff --git a/lib/functions/tlCodeTracker.class.php b/lib/functions/tlCodeTracker.class.php old mode 100755 new mode 100644 index 9d22b38e77..f320249551 --- a/lib/functions/tlCodeTracker.class.php +++ b/lib/functions/tlCodeTracker.class.php @@ -1,708 +1,658 @@ - array('type' => 'stash', 'api' => 'rest', 'enabled' => true, 'order' => -1)); - - - var $entitySpec = array('name' => 'string','cfg' => 'string','type' => 'int'); - - /** - * Class constructor - * - * @param resource &$db reference to the database handler - */ - function __construct(&$db) - { - parent::__construct(); - - // populate types property - $this->getTypes(); - $this->db = &$db; - } - - - - /** - * @return hash - * - * - */ - function getSystems($opt=null) - { - $my = array('options' => null); - $my['options']['status'] = 'enabled'; // enabled,disabled,all - $my['options'] = array_merge($my['options'],(array)$opt); - - switch($my['options']['status']) - { - case 'enabled': - $tval = true; - break; - - case 'disabled': - $tval = false; - break; - - default: - $tval = null; - break; - } - - $ret = array(); - foreach($this->systems as $code => $elem) - { - $idx = 0; - if($tval== null || $elem['enabled'] == $tval) - { - $ret[$code] = $elem; - } - } - return $ret; - } - - /** - * @return hash - * - * - */ - function getTypes() - { - if( is_null($this->types) ) - { - foreach($this->systems as $code => $spec) - { - if($spec['enabled']) - { - $this->types[$code] = $spec['type'] . " (Interface: {$spec['api']})"; - } - } - } - return $this->types; - } - - - /** - * @return - * - * - */ - function getImplementationForType($codeTrackerType) - { - $spec = $this->systems[$codeTrackerType]; - return $spec['type'] . $spec['api'] . 'Interface'; - } - - /** - * @return hash - * - * - */ - function getEntitySpec() - { - return $this->entitySpec; - } - - - /** - * - */ - function create($it) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array('status_ok' => 0, 'id' => 0, 'msg' => 'name already exists'); - - // Critic we need to do this before sanitize, because $it is changed - $xlmCfg = trim($it->cfg); - - // allow empty config - if(strlen($xlmCfg) > 0) - { - $ret = $this->checkXMLCfg($xlmCfg); - if(!$ret['status_ok']) - { - return $ret; // >>>---> Bye! - } - } - - - $safeobj = $this->sanitize($it); - // empty name is not allowed - if( is_null($safeobj->name) ) - { - $ret['msg'] = 'empty name is not allowed'; - return $ret; // >>>---> Bye! - } - - // need to check if name already exist - if( is_null($this->getByName($it->name,array('output' => 'id')) )) - { - $sql = "/* debugMsg */ INSERT INTO {$this->tables['codetrackers']} " . - " (name,cfg,type) " . - " VALUES('" . $safeobj->name . "','" . $safeobj->cfg . "',{$safeobj->type})"; - - if( $this->db->exec_query($sql) ) - { - // at least for Postgres DBMS table name is needed. - $itemID=$this->db->insert_id($this->tables['codetrackers']); - $ret = array('status_ok' => 1, 'id' => $itemID, 'msg' => 'ok'); - } - else - { - $ret = array('status_ok' => 0, 'id' => 0, 'msg' => $this->db->error_msg()); - } - } - - return $ret; - } - - - /** - * - */ - function update($it) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; - $msg = array(); - $msg['duplicate_name'] = "Update can not be done - name %s already exists for id %s"; - $msg['ok'] = "operation OK for id %s"; - - // Critic we need to do this before sanitize, because $it is changed - $xlmCfg = trim($it->cfg); - - $safeobj = $this->sanitize($it); - $ret = array('status_ok' => 1, 'id' => $it->id, 'msg' => ''); - - // allow empty config - if(strlen($xlmCfg) > 0) - { - $ret = $this->checkXMLCfg($xlmCfg); - } - - // check for duplicate name - if( $ret['status_ok'] ) - { - $info = $this->getByName($safeobj->name); - if( !is_null($info) && ($info['id'] != $it->id) ) - { - $ret['status_ok'] = 0; - $ret['msg'] .= sprintf($msg['duplicate_name'], $safeobj->name, $info['id']); - } - } - - if( $ret['status_ok'] ) - { - $sql = "UPDATE {$this->tables['codetrackers']} " . - " SET name = '" . $safeobj->name. "'," . - " cfg = '" . $safeobj->cfg . "'," . - " type = " . $safeobj->type . - " WHERE id = " . intval($it->id); - $result = $this->db->exec_query($sql); - $ret['msg'] .= sprintf($msg['ok'],$it->id); - - } - return $ret; - - } //function end - - - - /** - * delete can be done ONLY if ID is not linked to test project - */ - function delete($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; - - $msg = array(); - $msg['linked'] = "Failure - id %s is linked to: "; - $msg['tproject_details'] = " testproject '%s' with id %s %s"; - $msg['syntax_error'] = "Syntax failure - id %s seems to be an invalid value"; - $msg['ok'] = "operation OK for id %s"; - - $ret = array('status_ok' => 1, 'id' => $id, 'msg' => $debugMsg); - if(is_null($id) || ($safeID = intval($id)) <= 0) - { - $ret['status_ok'] = 0; - $ret['id'] = $id; - $ret['msg'] .= sprintf($msg['syntax_error'],$id); - return $ret; // >>>-----> Bye! - } - - - // check if ID is linked - $links = $this->getLinks($safeID); - if( is_null($links) ) - { - $sql = " /* $debugMsg */ DELETE FROM {$this->tables['codetrackers']} " . - " WHERE id = " . intval($safeID); - $result = $this->db->exec_query($sql); - $ret['msg'] .= sprintf($msg['ok'],$safeID); - - } - else - { - $ret['status_ok'] = 0; - $dummy = sprintf($msg['linked'],$safeID); - $sep = ' / '; - foreach($links as $item) - { - $dummy .= sprintf($msg['tproject_details'],$item['testproject_name'],$item['testproject_id'],$sep); - } - $ret['msg'] .= rtrim($dummy,$sep); - - } - return $ret; - - } //function end - - - - - - /** - * - */ - function getByID($id, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - return $this->getByAttr(array('key' => 'id', 'value' => $id),$options); - } - - - /** - * - */ - function getByName($name, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - return $this->getByAttr(array('key' => 'name', 'value' => $name),$options); - } - - - /** - * - */ - function getByAttr($attr, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('output' => 'full'); - $my['options'] = array_merge($my['options'], (array)$options); - - $sql = "/* debugMsg */ SELECT "; - switch($my['options']['output']) - { - case 'id': - $sql .= " id "; - break; - - case 'full': - default: - $sql .= " * "; - break; - - } - - switch($attr['key']) - { - case 'id': - $where = " WHERE id = " . intval($attr['value']); - break; - - case 'name': - default: - $where = " WHERE name = '" . $this->db->prepare_string($attr['value']) . "'"; - break; - } - - - $sql .= " FROM {$this->tables['codetrackers']} " . $where; - $rs = $this->db->get_recordset($sql); - if( !is_null($rs) ) - { - $rs = $rs[0]; - $rs['implementation'] = $this->getImplementationForType($rs['type']); - } - return $rs; - } - - - - /* - * Sanitize and do minor checks - * - * Sanitize Operations - * keys name -> trim will be applied - * type -> intval() wil be applied - * cfg - * - * For strings also db_prepare_string() will be applied - * - * - * Check Operations - * keys name -> if '' => will be set to NULL - * - */ - function sanitize($obj) - { - $sobj = $obj; - - // remove the standard set of characters considered harmful - // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab - // "\r" - carriage return - // and spaces - // fortunatelly this is trim standard behaviour - $k2san = array('name'); - foreach($k2san as $key) - { - $value = trim($obj->$key); - switch($key) - { - case 'name': - $sobj->$key = ($value == '') ? null : $value; - break; - } - - if( !is_null($sobj->$key) ) - { - $sobj->$key = $this->db->prepare_string($obj->$key); - } - - } - - // seems here is better do not touch. - $sobj->cfg = $this->db->prepare_string($obj->cfg); - $sobj->type = intval($obj->type); - - return $sobj; - } - - - - /* - * - * - */ - function link($id,$tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($id)) - { - return; - } - - // Check if link exist for test project ID, in order to INSERT or UPDATE - $statusQuo = $this->getLinkedTo($tprojectID); - - if( is_null($statusQuo) ) - { - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['testproject_codetracker']} " . - " (testproject_id,codetracker_id) " . - " VALUES(" . intval($tprojectID) . "," . intval($id) . ")"; - } - else - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['testproject_codetracker']} " . - " SET codetracker_id = " . intval($id) . - " WHERE testproject_id = " . intval($tprojectID); - } - $this->db->exec_query($sql); - } - - - /* - * - * - */ - function unlink($id,$tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($id)) - { - return; - } - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['testproject_codetracker']} " . - " WHERE testproject_id = " . intval($tprojectID) . - " AND codetracker_id = " . intval($id); - $this->db->exec_query($sql); - } - - - /* - * - * - */ - function getLinks($id, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('opt' => array('getDeadLinks' => false)); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - if(is_null($id)) - { - return; - } - - - $sql = "/* $debugMsg */ " . - " SELECT TPCT.testproject_id, NHTPR.name AS testproject_name " . - " FROM {$this->tables['testproject_codetracker']} TPCT" . - " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPCT.testproject_id " . - " WHERE TPCT.codetracker_id = " . intval($id); - - if($my['opt']['getDeadLinks']) - { - $sql .= ' AND NHTPR.id IS NULL AND NHTPR.name IS NULL '; - } - - $ret = $this->db->fetchRowsIntoMap($sql,'testproject_id'); - return $ret; - } - - - - /* - * - * - */ - function getLinkSet() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ " . - " SELECT TPCT.testproject_id, NHTPR.name AS testproject_name, TPCT.codetracker_id " . - " FROM {$this->tables['testproject_codetracker']} TPCT" . - " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPCT.testproject_id "; - - $ret = $this->db->fetchRowsIntoMap($sql,'testproject_id'); - return $ret; - } - - /* - * - * - */ - function getAll($options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('output' => null, 'orderByField' => 'name', 'checkEnv' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - $add_fields = ''; - if( $my['options']['output'] == 'add_link_count' ) - { - $add_fields = ", 0 AS link_count "; - } - - $orderByClause = is_null($my['options']['orderByField']) ? '' : 'ORDER BY ' . $my['options']['orderByField']; - - $sql = "/* debugMsg */ SELECT * {$add_fields} "; - $sql .= " FROM {$this->tables['codetrackers']} {$orderByClause} "; - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - - $lc = null; - if( !is_null($rs) ) - { - - if( $my['options']['output'] == 'add_link_count' ) - { - $sql = "/* debugMsg */ SELECT COUNT(0) AS lcount, CTD.id"; - $sql .= " FROM {$this->tables['codetrackers']} CTD " . - " JOIN {$this->tables['testproject_codetracker']} " . - " ON codetracker_id = CTD.id " . - " GROUP BY CTD.id "; - $lc = $this->db->fetchRowsIntoMap($sql,'id'); - } - - - foreach($rs as &$item) - { - $item['verbose'] = $item['name'] . " ( {$this->types[$item['type']]} )" ; - $item['type_descr'] = $this->types[$item['type']]; - $item['env_check_ok'] = true; - $item['env_check_msg'] = ''; - $item['connection_status'] = ''; - - if( $my['options']['checkEnv'] ) - { - $impl = $this->getImplementationForType($item['type']); - $dummy = $impl::checkEnv(); - $item['env_check_ok'] = $dummy['status']; - $item['env_check_msg'] = $dummy['msg']; - } - - - if( !is_null($lc) ) - { - if( isset($lc[$item['id']]) ) - { - $item['link_count'] = intval($lc[$item['id']]['lcount']); - } - } - } - } - return $rs; - } - - - /* - * - * - */ - function getLinkedTo($tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($tprojectID)) - { - return; - } - $sql = "/* $debugMsg */ " . - " SELECT TPCT.testproject_id, NHTPR.name AS testproject_name, " . - " TPCT.codetracker_id,CTRK.name AS codetracker_name, CTRK.type" . - " FROM {$this->tables['testproject_codetracker']} TPCT" . - " JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPCT.testproject_id " . - " JOIN {$this->tables['codetrackers']} CTRK " . - " ON CTRK.id = TPCT.codetracker_id " . - " WHERE TPCT.testproject_id = " . intval($tprojectID); - - $ret = $this->db->get_recordset($sql); - if( !is_null($ret) ) - { - $ret = $ret[0]; - $ret['verboseType'] = $this->types[$ret['type']]; - $spec = $this->systems[$ret['type']]; - $ret['api'] = $spec['api']; - } - - return $ret; - } - - - /** - * - * - */ - function getInterfaceObject($tprojectID) - { - $codeT = $this->getLinkedTo($tprojectID); - $name = $codeT['codetracker_name']; - $goodForSession = ($codeT['api'] != 'db'); - - if($goodForSession && isset($_SESSION['cts'][$name])) - { - return $_SESSION['cts'][$name]; - } - - try - { - if( !is_null($codeT) ) - { - $ctd = $this->getByID($codeT['codetracker_id']); - $cname = $ctd['implementation']; - - if($goodForSession) - { - $_SESSION['cts'][$name] = new $cname($cname,$ctd['cfg'],$ctd['name']); - } - else - { - $cxx = new $cname($cname,$ctd['cfg'],$ctd['name']); - return $cxx; - } - } - else - { - $_SESSION['cts'][$name] = null; - } - return $_SESSION['cts'][$name]; - } - catch (Exception $e) - { - echo('Probably there is some PHP Config issue regarding extension'); - echo($e->getMessage().'
    '.$e->getTraceAsString().'
    '); - } - } - - /* - * - * - */ - function checkConnection($cts) - { - $xx = $this->getByID($cts); - $class2create = $xx['implementation']; - $cts = new $class2create($xx['type'],$xx['cfg'],$xx['name']); - - $op = $cts->isConnected(); - - // because I've added simple cache on $_SESSION - // IMHO is better to update cache after this check - $_SESSION['cts'][$xx['name']] = $cts; - - return $op; - } - - /** - * - */ - function checkXMLCfg($xmlString) - { - $signature = 'Source:' . __METHOD__; - $op = array('status_ok' => true, 'msg' => ''); - - $xmlCfg = " " . trim($xmlString); - libxml_use_internal_errors(true); - try - { - $cfg = simplexml_load_string($xmlCfg); - if (!$cfg) - { - $op['status_ok'] = false; - $op['msg'] = $signature . " - Failure loading XML STRING\n"; - foreach(libxml_get_errors() as $error) - { - $op['msg'] .= "\t" . $error->message; - } - } - } - catch(Exception $e) - { - $op['status_ok'] = false; - $op['msg'] = $signature . " - Exception loading XML STRING\n" . 'Message: ' .$e->getMessage(); - } - - return $op; - } - -} // end class + array( + 'type' => 'stash', + 'api' => 'rest', + 'enabled' => true, + 'order' => - 1 + ) + ); + + private $entitySpec = array( + 'name' => 'string', + 'cfg' => 'string', + 'type' => 'int' + ); + + /** + * Class constructor + * + * @param + * resource &$db reference to the database handler + */ + public function __construct(&$db) + { + parent::__construct(); + + // populate types property + $this->getTypes(); + $this->db = &$db; + } + + /** + * + * @return array + * + * + */ + private function getSystems($opt = null) + { + $my = array( + 'options' => null + ); + $my['options']['status'] = 'enabled'; // enabled,disabled,all + $my['options'] = array_merge($my['options'], (array) $opt); + + switch ($my['options']['status']) { + case 'enabled': + $tval = true; + break; + + case 'disabled': + $tval = false; + break; + + default: + $tval = null; + break; + } + + $ret = array(); + foreach ($this->systems as $code => $elem) { + if ($tval == null || $elem['enabled'] == $tval) { + $ret[$code] = $elem; + } + } + return $ret; + } + + /** + * + * @return array + * + * + */ + public function getTypes() + { + if (is_null($this->types)) { + foreach ($this->systems as $code => $spec) { + if ($spec['enabled']) { + $this->types[$code] = $spec['type'] . + " (Interface: {$spec['api']})"; + } + } + } + return $this->types; + } + + /** + * + * @return + * + * + */ + public function getImplementationForType($codeTrackerType) + { + $spec = $this->systems[$codeTrackerType]; + return $spec['type'] . $spec['api'] . 'Interface'; + } + + /** + * + * @return array + * + * + */ + public function getEntitySpec() + { + return $this->entitySpec; + } + + /** + */ + public function create($it) + { + $ret = array( + 'status_ok' => 0, + 'id' => 0, + 'msg' => 'name already exists' + ); + + // Critic we need to do this before sanitize, because $it is changed + $xlmCfg = trim($it->cfg); + + // allow empty config + if (strlen($xlmCfg) > 0) { + $ret = $this->checkXMLCfg($xlmCfg); + if (! $ret['status_ok']) { + return $ret; // >>>---> Bye! + } + } + + $safeobj = $this->sanitize($it); + // empty name is not allowed + if (is_null($safeobj->name)) { + $ret['msg'] = 'empty name is not allowed'; + return $ret; // >>>---> Bye! + } + + // need to check if name already exist + if (is_null($this->getByName($it->name, array( + 'output' => 'id' + )))) { + $sql = "/* debugMsg */ INSERT INTO {$this->tables['codetrackers']} " . + " (name,cfg,type) " . " VALUES('" . $safeobj->name . "','" . + $safeobj->cfg . "',{$safeobj->type})"; + + if ($this->db->exec_query($sql)) { + // at least for Postgres DBMS table name is needed. + $itemID = $this->db->insert_id($this->tables['codetrackers']); + $ret = array( + 'status_ok' => 1, + 'id' => $itemID, + 'msg' => 'ok' + ); + } else { + $ret = array( + 'status_ok' => 0, + 'id' => 0, + 'msg' => $this->db->error_msg() + ); + } + } + + return $ret; + } + + /** + */ + public function update($it) + { + $msg = array(); + $msg['duplicate_name'] = "Update can not be done - name %s already exists for id %s"; + $msg['ok'] = "operation OK for id %s"; + + // Critic we need to do this before sanitize, because $it is changed + $xlmCfg = trim($it->cfg); + + $safeobj = $this->sanitize($it); + $ret = array( + 'status_ok' => 1, + 'id' => $it->id, + 'msg' => '' + ); + + // allow empty config + if (strlen($xlmCfg) > 0) { + $ret = $this->checkXMLCfg($xlmCfg); + } + + // check for duplicate name + if ($ret['status_ok']) { + $info = $this->getByName($safeobj->name); + if (! is_null($info) && ($info['id'] != $it->id)) { + $ret['status_ok'] = 0; + $ret['msg'] .= sprintf($msg['duplicate_name'], $safeobj->name, + $info['id']); + } + } + + if ($ret['status_ok']) { + $sql = "UPDATE {$this->tables['codetrackers']} " . " SET name = '" . + $safeobj->name . "'," . " cfg = '" . $safeobj->cfg . "'," . + " type = " . $safeobj->type . " WHERE id = " . + intval($it->id); + $this->db->exec_query($sql); + $ret['msg'] .= sprintf($msg['ok'], $it->id); + } + return $ret; + } + + /** + * delete can be done ONLY if ID is not linked to test project + */ + public function delete($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; + + $msg = array(); + $msg['linked'] = "Failure - id %s is linked to: "; + $msg['tproject_details'] = " testproject '%s' with id %s %s"; + $msg['syntax_error'] = "Syntax failure - id %s seems to be an invalid value"; + $msg['ok'] = "operation OK for id %s"; + + $ret = array( + 'status_ok' => 1, + 'id' => $id, + 'msg' => $debugMsg + ); + if (is_null($id) || ($safeID = intval($id)) <= 0) { + $ret['status_ok'] = 0; + $ret['id'] = $id; + $ret['msg'] .= sprintf($msg['syntax_error'], $id); + return $ret; // >>>-----> Bye! + } + + // check if ID is linked + $links = $this->getLinks($safeID); + if (is_null($links)) { + $sql = " /* $debugMsg */ DELETE FROM {$this->tables['codetrackers']} " . + " WHERE id = " . intval($safeID); + $this->db->exec_query($sql); + $ret['msg'] .= sprintf($msg['ok'], $safeID); + } else { + $ret['status_ok'] = 0; + $dummy = sprintf($msg['linked'], $safeID); + $sep = ' / '; + foreach ($links as $item) { + $dummy .= sprintf($msg['tproject_details'], + $item['testproject_name'], $item['testproject_id'], $sep); + } + $ret['msg'] .= rtrim($dummy, $sep); + } + return $ret; + } + + /** + */ + public function getByID($id, $options = null) + { + return $this->getByAttr(array( + 'key' => 'id', + 'value' => $id + ), $options); + } + + /** + */ + private function getByName($name, $options = null) + { + return $this->getByAttr(array( + 'key' => 'name', + 'value' => $name + ), $options); + } + + /** + */ + private function getByAttr($attr, $options = null) + { + $my['options'] = array( + 'output' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = "/* debugMsg */ SELECT "; + switch ($my['options']['output']) { + case 'id': + $sql .= " id "; + break; + + case 'full': + default: + $sql .= " * "; + break; + } + + switch ($attr['key']) { + case 'id': + $where = " WHERE id = " . intval($attr['value']); + break; + + case 'name': + default: + $where = " WHERE name = '" . + $this->db->prepare_string($attr['value']) . "'"; + break; + } + + $sql .= " FROM {$this->tables['codetrackers']} " . $where; + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + $rs = $rs[0]; + $rs['implementation'] = $this->getImplementationForType($rs['type']); + } + return $rs; + } + + /* + * Sanitize and do minor checks + * + * Sanitize Operations + * keys name -> trim will be applied + * type -> intval() wil be applied + * cfg + * + * For strings also db_prepare_string() will be applied + * + * + * Check Operations + * keys name -> if '' => will be set to NULL + * + */ + private function sanitize($obj) + { + $sobj = $obj; + + // remove the standard set of characters considered harmful + // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab + // "\r" - carriage return + // and spaces + // fortunatelly this is trim standard behaviour + $k2san = array( + 'name' + ); + foreach ($k2san as $key) { + $value = trim($obj->$key); + switch ($key) { + case 'name': + $sobj->$key = ($value == '') ? null : $value; + break; + } + + if (! is_null($sobj->$key)) { + $sobj->$key = $this->db->prepare_string($obj->$key); + } + } + + // seems here is better do not touch. + $sobj->cfg = $this->db->prepare_string($obj->cfg); + $sobj->type = intval($obj->type); + + return $sobj; + } + + /* + * + * + */ + public function link($id, $tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($id)) { + return; + } + + // Check if link exist for test project ID, in order to INSERT or UPDATE + $statusQuo = $this->getLinkedTo($tprojectID); + + if (is_null($statusQuo)) { + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['testproject_codetracker']} " . + " (testproject_id,codetracker_id) " . " VALUES(" . + intval($tprojectID) . "," . intval($id) . ")"; + } else { + $sql = "/* $debugMsg */ UPDATE {$this->tables['testproject_codetracker']} " . + " SET codetracker_id = " . intval($id) . + " WHERE testproject_id = " . intval($tprojectID); + } + $this->db->exec_query($sql); + } + + /* + * + * + */ + public function unlink($id, $tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($id)) { + return; + } + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['testproject_codetracker']} " . + " WHERE testproject_id = " . intval($tprojectID) . + " AND codetracker_id = " . intval($id); + $this->db->exec_query($sql); + } + + /* + * + * + */ + public function getLinks($id, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array( + 'opt' => array( + 'getDeadLinks' => false + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + if (is_null($id)) { + return; + } + + $sql = "/* $debugMsg */ " . + " SELECT TPCT.testproject_id, NHTPR.name AS testproject_name " . + " FROM {$this->tables['testproject_codetracker']} TPCT" . + " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPCT.testproject_id " . + " WHERE TPCT.codetracker_id = " . intval($id); + + if ($my['opt']['getDeadLinks']) { + $sql .= ' AND NHTPR.id IS NULL AND NHTPR.name IS NULL '; + } + + return $this->db->fetchRowsIntoMap($sql, 'testproject_id'); + } + + /* + * + * + */ + private function getLinkSet() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = "/* $debugMsg */ " . + " SELECT TPCT.testproject_id, NHTPR.name AS testproject_name, TPCT.codetracker_id " . + " FROM {$this->tables['testproject_codetracker']} TPCT" . + " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPCT.testproject_id "; + + return $this->db->fetchRowsIntoMap($sql, 'testproject_id'); + } + + /* + * + * + */ + public function getAll($options = null) + { + $my['options'] = array( + 'output' => null, + 'orderByField' => 'name', + 'checkEnv' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $add_fields = ''; + if ($my['options']['output'] == 'add_link_count') { + $add_fields = ", 0 AS link_count "; + } + + $orderByClause = is_null($my['options']['orderByField']) ? '' : 'ORDER BY ' . + $my['options']['orderByField']; + + $sql = "/* debugMsg */ SELECT * {$add_fields} "; + $sql .= " FROM {$this->tables['codetrackers']} {$orderByClause} "; + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + + $lc = null; + if (! is_null($rs)) { + + if ($my['options']['output'] == 'add_link_count') { + $sql = "/* debugMsg */ SELECT COUNT(0) AS lcount, CTD.id"; + $sql .= " FROM {$this->tables['codetrackers']} CTD " . + " JOIN {$this->tables['testproject_codetracker']} " . + " ON codetracker_id = CTD.id " . " GROUP BY CTD.id "; + $lc = $this->db->fetchRowsIntoMap($sql, 'id'); + } + + foreach ($rs as &$item) { + $item['verbose'] = $item['name'] . + " ( {$this->types[$item['type']]} )"; + $item['type_descr'] = $this->types[$item['type']]; + $item['env_check_ok'] = true; + $item['env_check_msg'] = ''; + $item['connection_status'] = ''; + + if ($my['options']['checkEnv']) { + $impl = $this->getImplementationForType($item['type']); + $dummy = $impl::checkEnv(); + $item['env_check_ok'] = $dummy['status']; + $item['env_check_msg'] = $dummy['msg']; + } + + if (! is_null($lc) && isset($lc[$item['id']])) { + $item['link_count'] = intval($lc[$item['id']]['lcount']); + } + } + } + return $rs; + } + + /* + * + * + */ + public function getLinkedTo($tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($tprojectID)) { + return; + } + $sql = "/* $debugMsg */ " . + " SELECT TPCT.testproject_id, NHTPR.name AS testproject_name, " . + " TPCT.codetracker_id,CTRK.name AS codetracker_name, CTRK.type" . + " FROM {$this->tables['testproject_codetracker']} TPCT" . + " JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPCT.testproject_id " . + " JOIN {$this->tables['codetrackers']} CTRK " . + " ON CTRK.id = TPCT.codetracker_id " . + " WHERE TPCT.testproject_id = " . intval($tprojectID); + + $ret = $this->db->get_recordset($sql); + if (! is_null($ret)) { + $ret = $ret[0]; + $ret['verboseType'] = $this->types[$ret['type']]; + $spec = $this->systems[$ret['type']]; + $ret['api'] = $spec['api']; + } + + return $ret; + } + + /** + */ + public function getInterfaceObject($tprojectID) + { + $codeT = $this->getLinkedTo($tprojectID); + $name = $codeT['codetracker_name']; + $goodForSession = ($codeT['api'] != 'db'); + + if ($goodForSession && isset($_SESSION['cts'][$name])) { + return $_SESSION['cts'][$name]; + } + + try { + if (! is_null($codeT)) { + $ctd = $this->getByID($codeT['codetracker_id']); + $cname = $ctd['implementation']; + + if ($goodForSession) { + $_SESSION['cts'][$name] = new $cname($cname, $ctd['cfg'], + $ctd['name']); + } else { + return new $cname($cname, $ctd['cfg'], $ctd['name']); + } + } else { + $_SESSION['cts'][$name] = null; + } + return $_SESSION['cts'][$name]; + } catch (Exception $e) { + echo 'Probably there is some PHP Config issue regarding extension'; + echo $e->getMessage() . '
    ' . $e->getTraceAsString() . '
    '; + } + } + + /* + * + * + */ + public function checkConnection($cts) + { + $xx = $this->getByID($cts); + $class2create = $xx['implementation']; + $cts = new $class2create($xx['type'], $xx['cfg'], $xx['name']); + + $op = $cts->isConnected(); + + // because I've added simple cache on $_SESSION + // IMHO is better to update cache after this check + $_SESSION['cts'][$xx['name']] = $cts; + + return $op; + } + + /** + */ + private function checkXMLCfg($xmlString) + { + $signature = 'Source:' . __METHOD__; + $op = array( + 'status_ok' => true, + 'msg' => '' + ); + + $xmlCfg = " " . trim($xmlString); + libxml_use_internal_errors(true); + try { + $cfg = simplexml_load_string($xmlCfg); + if (! $cfg) { + $op['status_ok'] = false; + $op['msg'] = $signature . " - Failure loading XML STRING\n"; + foreach (libxml_get_errors() as $error) { + $op['msg'] .= "\t" . $error->message; + } + } + } catch (Exception $e) { + $op['status_ok'] = false; + $op['msg'] = $signature . " - Exception loading XML STRING\n" . + 'Message: ' . $e->getMessage(); + } + + return $op; + } +} diff --git a/lib/functions/tlFilterControl.class.php b/lib/functions/tlFilterControl.class.php index 8d5e299d75..33f857ee6d 100644 --- a/lib/functions/tlFilterControl.class.php +++ b/lib/functions/tlFilterControl.class.php @@ -33,473 +33,502 @@ * @package TestLink * @uses testproject */ -abstract class tlFilterControl extends tlObjectWithDB +abstract class tlFilterControl extends tlObjectWithDB { - /** - * Label (and name) for the button to enable simple filter mode. - * @var string - */ - const SIMPLE_FILTER_BUTTON_LABEL = "btn_simple_filters"; - - /** - * Label (and name) for the button to enable advanced filter mode. - * @var string - */ - const ADVANCED_FILTER_BUTTON_LABEL = "btn_advanced_filters"; - - /** - * how many filter items will be displayed in a multiselect box in advanced filter mode? - * @var int - */ - const ADVANCED_FILTER_ITEM_QUANTITY = 4; - - /** - * how many filter items will be displayed in a select box in simple filter mode? - * @var int - */ - const SIMPLE_FILTER_ITEM_QUANTITY = 1; - - /** - * Length of custom field inputs in filter form. - * @var int - */ - const CF_INPUT_SIZE = 32; - - /** - * Value of [ANY]-selection in advanced filter mode. - * @var int - */ - const ANY = 0; - - /** - * defines, wether the button to unassign all test cases from test plan shall be drawn on template - * @var bool - */ - public $draw_tc_unassign_button = false; - - /** - * defines, wether the button to update all linked test cases to their newest version - * shall be drawn on template - * @var bool - */ - public $draw_bulk_update_button = false; - - /** - * defines, wether the button to export test plan tree shall be drawn on template - * @var bool - */ - public $draw_export_testplan_button = false; // BUGID 3270 - Export Test Plan in XML Format - - - /** - * @var bool - */ - public $draw_import_xml_results_button = false; - - - public $draw_tc_assignment_bulk_copy_button = false; - - /** - * will hold the localized string options (any/none/somebody/...) - * @var array - */ - public $option_strings = array(); - - /** - * holds the configuration that will be read from config file - * @var stdClass - */ - public $configuration = null; - - /** - * holds the user input read from request - * @var stdClass - */ - public $args = null; - - /** - * Will hold the configuration of filters (which ones are to be shown) and their values, - * that can be selected on GUI, if active. - * @var array - */ - public $filters = array(); - - /** - * This array holds only the user selected values of active filters. It will be passed - * to the underlying tree filter functions to set the values which are to be filtered. - * @var array - */ - protected $active_filters = array(); - - /** - * will hold the configuration about settings (which ones are to be shown) and their values - * @var array - */ - public $settings = array(); - - /** - * is advanced filter mode active? - * @var bool - */ - public $advanced_filter_mode = false; - - /** - * if true, settings panel will be displayed, if false it will not be visible - * @var bool - */ - public $display_settings = false; - - /** - * if true, filter panel will be displayed, if false it will not be visible - * @var bool - */ - public $display_filters = false; - - /** - * If set to true, settings panel for requirements will be displayed. - * @var bool - */ - public $display_req_settings = false; - - /** - * If set to true, filter panel for requirements will be displayed. - * @var bool - */ - public $display_req_filters = false; - - /** - * Is it allowed to choose advanced filter mode? - * @var bool - */ - public $filter_mode_choice_enabled = true; - - /** - * Holds the label for the button used to switch between filter modes (simple and advanced). - * @var string - */ - public $filter_mode_button_label = ''; - - /** - * Holds the filter item quantity (size of user inputs) for some of the menus. - * @var int - */ - public $filter_item_quantity = 0; - - /** - * This variable marks wether filtering on the tree has to be done in PHP or if lazy loading - * can be done in Javascript. It is TRUE, when user has sent data with filter/settings forms, - * and filtering on tree has to be done. Otherwise (e.g. on first opening of forms) it is FALSE. - * Value is always FALSE by default and after filter reset. - * When one of the init_filter_* methods gets a selected value it then sets it to TRUE. - * @var bool - */ - public $do_filtering = false; - - - // used by derived classes - public $cfieldsCfg = null; - - /** - * Testproject manager object. - * Initialized not in constructor, only on first use to save resources. - * @var testproject - */ - public $testproject_mgr = null; - - // used by derived classes - protected $cfield_mgr = null; - - /** - * - * @param database $dbHandler reference to database object - */ - public function __construct(&$dbHandler) - { - // call to constructor of parent class tlObjectWithDB - parent::__construct($dbHandler); - - // Here comes all initializing work: First read the config, then user input. - // According to these inputs all filters which are not needed will not be used. - // Then initialize and use only the remaining filters. - $this->read_config(); - $this->init_args(); - - // set filter mode to advanced or simple - $this->init_advanced_filter_mode(); - - // init button labels - if ($this->advanced_filter_mode) + + /** + * Label (and name) for the button to enable simple filter mode. + * + * @var string + */ + const SIMPLE_FILTER_BUTTON_LABEL = "btn_simple_filters"; + + /** + * Label (and name) for the button to enable advanced filter mode. + * + * @var string + */ + const ADVANCED_FILTER_BUTTON_LABEL = "btn_advanced_filters"; + + /** + * how many filter items will be displayed in a multiselect box in advanced filter mode? + * + * @var int + */ + const ADVANCED_FILTER_ITEM_QUANTITY = 4; + + /** + * how many filter items will be displayed in a select box in simple filter mode? + * + * @var int + */ + const SIMPLE_FILTER_ITEM_QUANTITY = 1; + + /** + * Length of custom field inputs in filter form. + * + * @var int + */ + const CF_INPUT_SIZE = 32; + + /** + * Value of [ANY]-selection in advanced filter mode. + * + * @var int + */ + const ANY = 0; + + /** + * defines, wether the button to unassign all test cases from test plan shall be drawn on template + * + * @var bool + */ + public $draw_tc_unassign_button = false; + + /** + * defines, wether the button to update all linked test cases to their newest version + * shall be drawn on template + * + * @var bool + */ + public $draw_bulk_update_button = false; + + /** + * defines, wether the button to export test plan tree shall be drawn on template + * + * @var bool + */ + public $draw_export_testplan_button = false; + + // BUGID 3270 - Export Test Plan in XML Format + + /** + * + * @var bool + */ + public $draw_import_xml_results_button = false; + + public $draw_tc_assignment_bulk_copy_button = false; + + /** + * will hold the localized string options (any/none/somebody/...) + * + * @var array + */ + public $option_strings = array(); + + /** + * holds the configuration that will be read from config file + * + * @var stdClass + */ + public $configuration = null; + + /** + * holds the user input read from request + * + * @var stdClass + */ + public $args = null; + + /** + * Will hold the configuration of filters (which ones are to be shown) and their values, + * that can be selected on GUI, if active. + * + * @var array + */ + public $filters = array(); + + /** + * This array holds only the user selected values of active filters. + * It will be passed + * to the underlying tree filter functions to set the values which are to be filtered. + * + * @var array + */ + protected $active_filters = array(); + + /** + * will hold the configuration about settings (which ones are to be shown) and their values + * + * @var array + */ + public $settings = array(); + + /** + * is advanced filter mode active? + * + * @var bool + */ + public $advanced_filter_mode = false; + + /** + * if true, settings panel will be displayed, if false it will not be visible + * + * @var bool + */ + public $display_settings = false; + + /** + * if true, filter panel will be displayed, if false it will not be visible + * + * @var bool + */ + public $display_filters = false; + + /** + * If set to true, settings panel for requirements will be displayed. + * + * @var bool + */ + public $display_req_settings = false; + + /** + * If set to true, filter panel for requirements will be displayed. + * + * @var bool + */ + public $display_req_filters = false; + + /** + * Is it allowed to choose advanced filter mode? + * + * @var bool + */ + public $filter_mode_choice_enabled = true; + + /** + * Holds the label for the button used to switch between filter modes (simple and advanced). + * + * @var string + */ + public $filter_mode_button_label = ''; + + /** + * Holds the filter item quantity (size of user inputs) for some of the menus. + * + * @var int + */ + public $filter_item_quantity = 0; + + /** + * This variable marks wether filtering on the tree has to be done in PHP or if lazy loading + * can be done in Javascript. + * It is TRUE, when user has sent data with filter/settings forms, + * and filtering on tree has to be done. Otherwise (e.g. on first opening of forms) it is FALSE. + * Value is always FALSE by default and after filter reset. + * When one of the init_filter_* methods gets a selected value it then sets it to TRUE. + * + * @var bool + */ + public $do_filtering = false; + + // used by derived classes + public $cfieldsCfg = null; + + /** + * Testproject manager object. + * Initialized not in constructor, only on first use to save resources. + * + * @var testproject + */ + public $testproject_mgr = null; + + // used by derived classes + protected $cfield_mgr = null; + + /** + * + * @param database $dbHandler + * reference to database object + */ + public function __construct(&$dbHandler) { - $label = self::SIMPLE_FILTER_BUTTON_LABEL; - $qty = self::ADVANCED_FILTER_ITEM_QUANTITY; - } - else + // call to constructor of parent class tlObjectWithDB + parent::__construct($dbHandler); + + // Here comes all initializing work: First read the config, then user input. + // According to these inputs all filters which are not needed will not be used. + // Then initialize and use only the remaining filters. + $this->read_config(); + $this->init_args(); + + // set filter mode to advanced or simple + $this->init_advanced_filter_mode(); + + // init button labels + if ($this->advanced_filter_mode) { + $label = self::SIMPLE_FILTER_BUTTON_LABEL; + $qty = self::ADVANCED_FILTER_ITEM_QUANTITY; + } else { + $label = self::ADVANCED_FILTER_BUTTON_LABEL; + $qty = self::SIMPLE_FILTER_ITEM_QUANTITY; + } + + $this->filter_mode_button_label = lang_get($label); + $this->filter_mode_button_name = $label; + $this->filter_item_quantity = $qty; + + $this->init_settings(); + } + + /** + * Destructor: deletes all member object which have to be deleted after use. + */ + public function __destruct() { - $label = self::ADVANCED_FILTER_BUTTON_LABEL; - $qty = self::SIMPLE_FILTER_ITEM_QUANTITY; + // delete member objects + unset($this->testproject_mgr); } - - $this->filter_mode_button_label = lang_get($label); - $this->filter_mode_button_name = $label; - $this->filter_item_quantity = $qty; - - $this->init_settings(); - - } // end of method - - /** - * Destructor: deletes all member object which have to be deleted after use. - * - */ - public function __destruct() { - // delete member objects - unset($this->testproject_mgr); - } // end of method - - /** - * Reads the configuration from the configuration file, which is not dependent on type of objects in tree. - * This function has to be implemented and extended also in extending classes to read specialized config - * for either test cases or requirements. - * Function has protected (in subclasses private) visibility because it will only be called by __construct(). - * @return bool - */ - protected function read_config() { - // opening and closing brackets - $go = config_get('gui_separator_open'); - $gc = config_get('gui_separator_close'); - - // configure string options for select inputs - $this->option_strings['any'] = $go . lang_get('any') . $gc; - $this->option_strings['none'] = $go . lang_get('nobody') . $gc; - $this->option_strings['somebody'] = $go . lang_get('filter_somebody') . $gc; - $this->option_strings['without_keywords'] = $go . - lang_get('without_keywords') . $gc; - - $this->option_strings['without_platforms'] = $go . - lang_get('without_platforms') . $gc; - - return tl::OK; - } // end of method - - /** - * Does what init_args() usually does in scripts: Reads the user input - * from request ($_GET and $_POST). Then it modifies configuration, - * settings and filters according to that user input. - * While the implementation here loads generic input (unrelated to choice of - * test case or requirements for the tree), it will be extended by - * child classes to load input specific for requirements and test cases. - */ - protected function init_args() { - - $this->args = new stdClass(); - $this->args->basehref = $_SESSION['basehref']; - - // get user's data - $this->user = $_SESSION['currentUser']; - $this->args->user_id = $this->user->dbID; - $this->args->user_name = $this->user->getDisplayName(); - - $this->args->testproject_id = intval(isset($_SESSION['testprojectID']) ? - $_SESSION['testprojectID'] : 0); - $this->args->testproject_name = isset($_SESSION['testprojectName']) ? - $_SESSION['testprojectName'] : 0; - - $params = array(); - $params['setting_refresh_tree_on_action'] = array("POST", tlInputParameter::CB_BOOL); - $params['hidden_setting_refresh_tree_on_action'] = - array("POST", tlInputParameter::INT_N); - - I_PARAMS($params, $this->args); - - // was a filter reset requested? - $this->args->reset_filters = false; - if (isset($_REQUEST['btn_reset_filters'])) { - $this->args->reset_filters = true; // mark filter reset in args - $this->do_filtering = false; // mark that no filtering has to be done after reset + + /** + * Reads the configuration from the configuration file, which is not dependent on type of objects in tree. + * This function has to be implemented and extended also in extending classes to read specialized config + * for either test cases or requirements. + * Function has protected (in subclasses private) visibility because it will only be called by __construct(). + * + * @return bool + */ + protected function read_config() + { + // opening and closing brackets + $go = config_get('gui_separator_open'); + $gc = config_get('gui_separator_close'); + + // configure string options for select inputs + $this->option_strings['any'] = $go . lang_get('any') . $gc; + $this->option_strings['none'] = $go . lang_get('nobody') . $gc; + $this->option_strings['somebody'] = $go . lang_get('filter_somebody') . + $gc; + $this->option_strings['without_keywords'] = $go . + lang_get('without_keywords') . $gc; + + $this->option_strings['without_platforms'] = $go . + lang_get('without_platforms') . $gc; + + return tl::OK; } - - // what filter mode has been chosen? - $this->args->simple_filter_mode = - isset($_REQUEST[self::SIMPLE_FILTER_BUTTON_LABEL]) ? true : false; - $this->args->advanced_filter_mode = - isset($_REQUEST[self::ADVANCED_FILTER_BUTTON_LABEL]) ? true : false; - - $this->args->loadExecDashboard = true; - if( isset($_REQUEST['loadExecDashboard']) ) { - $this->args->loadExecDashboard = intval($_REQUEST['loadExecDashboard']); - } - - } // end of method - - - /** - * - */ - protected function init_filter_custom_fields($application_areas=null) - { - $key = 'filter_custom_fields'; - $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; - $localesDateFormat = config_get('locales_date_format'); - $date_format = str_replace('%', '', $localesDateFormat[$locale]); - - $collapsed = isset($_SESSION['cf_filter_collapsed']) ? $_SESSION['cf_filter_collapsed'] : 0; - $collapsed = isset($_REQUEST['btn_toggle_cf']) ? !$collapsed : $collapsed; - $_SESSION['cf_filter_collapsed'] = $collapsed; - $btn_label = $collapsed ? lang_get('btn_show_cf') : lang_get('btn_hide_cf'); - - $cfields = $this->getCustomFields($application_areas); - $cf_prefix = $this->cfield_mgr->name_prefix; - - $cf_html_code = ""; - $selection = array(); - - $this->filters[$key] = false; - $this->active_filters[$key] = null; - - if (!is_null($cfields)) + + /** + * Does what init_args() usually does in scripts: Reads the user input + * from request ($_GET and $_POST). + * Then it modifies configuration, + * settings and filters according to that user input. + * While the implementation here loads generic input (unrelated to choice of + * test case or requirements for the tree), it will be extended by + * child classes to load input specific for requirements and test cases. + */ + protected function init_args() { - $cfInputOpt = array('name_suffix' => '', 'field_size' => self::CF_INPUT_SIZE, - 'show_on_filters' => true, 'remove_required' => true); - - foreach ($cfields as $cf_id => $cf) - { - // has a value been selected? - $id = $cf['id']; - $type = $cf['type']; - $verbose_type = trim($this->cfield_mgr->custom_field_types[$type]); - $cf_input_name = "{$cf_prefix}{$type}_{$id}"; - - // set special size for list inputs - if ($verbose_type == 'list' || $verbose_type == 'multiselection list') - { - $cfInputOpt['field_size'] = 3; + $this->args = new stdClass(); + $this->args->basehref = $_SESSION['basehref']; + + // get user's data + $this->user = $_SESSION['currentUser']; + $this->args->user_id = $this->user->dbID; + $this->args->user_name = $this->user->getDisplayName(); + + $this->args->testproject_id = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + $this->args->testproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; + + $params = array(); + $params['setting_refresh_tree_on_action'] = array( + "POST", + tlInputParameter::CB_BOOL + ); + $params['hidden_setting_refresh_tree_on_action'] = array( + "POST", + tlInputParameter::INT_N + ); + + I_PARAMS($params, $this->args); + + // was a filter reset requested? + $this->args->reset_filters = false; + if (isset($_REQUEST['btn_reset_filters'])) { + $this->args->reset_filters = true; // mark filter reset in args + $this->do_filtering = false; // mark that no filtering has to be done after reset } - - // custom fields on test spec did not retain value after apply - // IMPORTANT/CRITIC issue: trim() on array makes array = null !!! - $value = isset($_REQUEST[$cf_input_name]) ? $_REQUEST[$cf_input_name] : null; - - if ($this->args->reset_filters) - { - $value = null; - } - else - { - if ($verbose_type == 'datetime') - { - // convert the three given values to unixtime format - if (isset($_REQUEST[$cf_input_name . '_input']) && $_REQUEST[$cf_input_name . '_input'] != '' && - isset($_REQUEST[$cf_input_name . '_hour']) && $_REQUEST[$cf_input_name . '_hour'] != '' && - isset($_REQUEST[$cf_input_name . '_minute']) && $_REQUEST[$cf_input_name . '_minute'] != '' && - isset($_REQUEST[$cf_input_name . '_second']) && $_REQUEST[$cf_input_name . '_second'] != '') - { - $date = $_REQUEST[$cf_input_name . '_input']; - $hour = $_REQUEST[$cf_input_name . '_hour']; - $minute = $_REQUEST[$cf_input_name . '_minute']; - $second = $_REQUEST[$cf_input_name . '_second']; - - $date_array = split_localized_date($date, $date_format); - $value = mktime($hour, $minute, $second, $date_array['month'], $date_array['day'], $date_array['year']); - } - } - - if ($verbose_type == 'date') - { - // convert the three given values to unixtime format, only set values if different from 0 - if (isset($_REQUEST[$cf_input_name . '_input']) && $_REQUEST[$cf_input_name . '_input'] != '') - { - $date = $_REQUEST[$cf_input_name . '_input']; - $date_array = split_localized_date($date, $date_format); - $value = mktime(0, 0, 0, $date_array['month'], $date_array['day'], $date_array['year']); - } - } - } - - $value2display = $value; - if (!is_null($value2display) && is_array($value2display)) - { - $value2display = implode("|", $value2display); - } - else - { - $value = trim($value); - $value2display = $value; - } - $cf['value'] = $value2display; - if (!is_null($value) && $value !='') - { - $this->do_filtering = true; - $selection[$id] = $value; + // what filter mode has been chosen? + $this->args->simple_filter_mode = isset( + $_REQUEST[self::SIMPLE_FILTER_BUTTON_LABEL]) ? true : false; + $this->args->advanced_filter_mode = isset( + $_REQUEST[self::ADVANCED_FILTER_BUTTON_LABEL]) ? true : false; + + $this->args->loadExecDashboard = true; + if (isset($_REQUEST['loadExecDashboard'])) { + $this->args->loadExecDashboard = intval( + $_REQUEST['loadExecDashboard']); } + } - $label = str_replace(TL_LOCALIZE_TAG, '', lang_get($cf['label'], null, LANG_GET_NO_WARNING)); - - // don't show textarea inputs here, they are too large for filterpanel - if ($verbose_type != 'text area') - { - $cf_html_code .= '' . htmlspecialchars($label) . '' . - $this->cfield_mgr->string_custom_field_input($cf,$cfInputOpt) . - ''; + /** + */ + protected function init_filter_custom_fields($application_areas = null) + { + $key = 'filter_custom_fields'; + $locale = (isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB'; + $localesDateFormat = config_get('locales_date_format'); + $date_format = str_replace('%', '', $localesDateFormat[$locale]); + + $collapsed = isset($_SESSION['cf_filter_collapsed']) ? $_SESSION['cf_filter_collapsed'] : 0; + $collapsed = isset($_REQUEST['btn_toggle_cf']) ? ! $collapsed : $collapsed; + $_SESSION['cf_filter_collapsed'] = $collapsed; + $btn_label = $collapsed ? lang_get('btn_show_cf') : lang_get( + 'btn_hide_cf'); + + $cfields = $this->getCustomFields($application_areas); + $cf_prefix = $this->cfield_mgr->name_prefix; + + $cf_html_code = ""; + $selection = array(); + + $this->filters[$key] = false; + $this->active_filters[$key] = null; + + if (! is_null($cfields)) { + $cfInputOpt = array( + 'name_suffix' => '', + 'field_size' => self::CF_INPUT_SIZE, + 'show_on_filters' => true, + 'remove_required' => true + ); + + foreach ($cfields as $cf) { + // has a value been selected? + $id = $cf['id']; + $type = $cf['type']; + $verbose_type = trim( + $this->cfield_mgr->custom_field_types[$type]); + $cf_input_name = "{$cf_prefix}{$type}_{$id}"; + + // set special size for list inputs + if ($verbose_type == 'list' || + $verbose_type == 'multiselection list') { + $cfInputOpt['field_size'] = 3; + } + + // custom fields on test spec did not retain value after apply + // IMPORTANT/CRITIC issue: trim() on array makes array = null !!! + $value = isset($_REQUEST[$cf_input_name]) ? $_REQUEST[$cf_input_name] : null; + + if ($this->args->reset_filters) { + $value = null; + } else { + // convert the three given values to unixtime format + if ($verbose_type == 'datetime' && + isset($_REQUEST[$cf_input_name . '_input']) && + $_REQUEST[$cf_input_name . '_input'] != '' && + isset($_REQUEST[$cf_input_name . '_hour']) && + $_REQUEST[$cf_input_name . '_hour'] != '' && + isset($_REQUEST[$cf_input_name . '_minute']) && + $_REQUEST[$cf_input_name . '_minute'] != '' && + isset($_REQUEST[$cf_input_name . '_second']) && + $_REQUEST[$cf_input_name . '_second'] != '') { + $date = $_REQUEST[$cf_input_name . '_input']; + $hour = $_REQUEST[$cf_input_name . '_hour']; + $minute = $_REQUEST[$cf_input_name . '_minute']; + $second = $_REQUEST[$cf_input_name . '_second']; + + $date_array = split_localized_date($date, $date_format); + $value = mktime($hour, $minute, $second, + $date_array['month'], $date_array['day'], + $date_array['year']); + } + + // convert the three given values to unixtime format, only set values if different from 0 + if ($verbose_type == 'date' && + isset($_REQUEST[$cf_input_name . '_input']) && + $_REQUEST[$cf_input_name . '_input'] != '') { + $date = $_REQUEST[$cf_input_name . '_input']; + $date_array = split_localized_date($date, $date_format); + $value = mktime(0, 0, 0, $date_array['month'], + $date_array['day'], $date_array['year']); + } + } + + $value2display = $value; + if (! is_null($value2display) && is_array($value2display)) { + $value2display = implode("|", $value2display); + } else { + $value = trim($value); + $value2display = $value; + } + $cf['value'] = $value2display; + + if (! is_null($value) && $value != '') { + $this->do_filtering = true; + $selection[$id] = $value; + } + + $label = str_replace(TL_LOCALIZE_TAG, '', + lang_get($cf['label'], null, LANG_GET_NO_WARNING)); + + // don't show textarea inputs here, they are too large for filterpanel + if ($verbose_type != 'text area') { + $cf_html_code .= '' . + htmlspecialchars($label) . '' . + $this->cfield_mgr->string_custom_field_input($cf, + $cfInputOpt) . ''; + } + } + + // show/hide CF + $this->filters[$key] = array( + 'items' => $cf_html_code, + 'btn_label' => $btn_label, + 'collapsed' => $collapsed + ); + $this->active_filters[$key] = count($selection) ? $selection : null; } - } + } - // show/hide CF - $this->filters[$key] = array('items' => $cf_html_code,'btn_label' => $btn_label,'collapsed' => $collapsed); - $this->active_filters[$key] = count($selection) ? $selection : null; + /** + */ + protected function init_advanced_filter_mode() + { + $this->advanced_filter_mode = ($this->filter_mode_choice_enabled && + $this->args->advanced_filter_mode && + ! $this->args->simple_filter_mode); } - } // end of method - - - /** - * - */ - protected function init_advanced_filter_mode() - { - $this->advanced_filter_mode = ($this->filter_mode_choice_enabled && - $this->args->advanced_filter_mode && - !$this->args->simple_filter_mode); - - } // end of method - - - - /** - * Initializes the class member array for settings - * according to the data loaded from database and user input. - * Only initializes active settings, for a better performance. - * Abstract: has to be implemented in any child class. - */ - protected abstract function init_settings(); - - /** - * Initializes the class member array for filters - * according to the data loaded from database and user input. - * Only initializes filters which are still enabled and active, for a better performance. - * Abstract: has to be implemented in each child class. - */ - protected abstract function init_filters(); - - /** - * Returns the filter array with necessary data, - * ready to be processed/used by underlying filter functions in - * test spec/exec/requirement tree generator functions. - * Has to be implemented in child class. - */ - protected abstract function get_active_filters(); - - /** - * Build the tree menu for generation of JavaScript tree of either test cases or requirements. - * Depending on user 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 tree by later Ajax calls. - * @param object $gui Reference to GUI object (information will be written to it) - * @return object $tree_menu Tree object for display of JavaScript tree menu. - */ - public abstract function build_tree_menu(&$gui); - - - protected abstract function getCustomFields(); - - -} // end of class \ No newline at end of file + + /** + * Initializes the class member array for settings + * according to the data loaded from database and user input. + * Only initializes active settings, for a better performance. + * Abstract: has to be implemented in any child class. + */ + abstract protected function init_settings(); + + /** + * Initializes the class member array for filters + * according to the data loaded from database and user input. + * Only initializes filters which are still enabled and active, for a better performance. + * Abstract: has to be implemented in each child class. + */ + abstract protected function init_filters(); + + /** + * Returns the filter array with necessary data, + * ready to be processed/used by underlying filter functions in + * test spec/exec/requirement tree generator functions. + * Has to be implemented in child class. + */ + abstract protected function get_active_filters(); + + /** + * Build the tree menu for generation of JavaScript tree of either test cases or requirements. + * Depending on user 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 tree by later Ajax calls. + * + * @param object $gui + * Reference to GUI object (information will be written to it) + * @return object $tree_menu Tree object for display of JavaScript tree menu. + */ + abstract public function build_tree_menu(&$gui); + + abstract protected function getCustomFields(); +} diff --git a/lib/functions/tlHTMLTable.class.php b/lib/functions/tlHTMLTable.class.php index 78b8bb490f..b0943395a7 100644 --- a/lib/functions/tlHTMLTable.class.php +++ b/lib/functions/tlHTMLTable.class.php @@ -1,103 +1,94 @@ -code_status = $resultsCfg['code_status']; - $this->status_color = $resultsCfg['charts']['status_colour']; - $urgencyCfg = config_get('urgency'); - $this->prio_code_label = $urgencyCfg['code_label']; - } - - /** - * Does nothing. All rendering is contained in renderBodySection() - */ - public function renderCommonGlobals() - { - return ''; - } - - /** - * Does nothing. All rendering is contained in renderBodySection() - */ - public function renderHeadSection() - { - return ''; - } - - /** - * Renders a HTML table with css class "simple" and given id - */ - public function renderBodySection() - { - $s = ''; - // Render columns - $s .= ''; - foreach ($this->columns as $column) { - $title = is_array($column) ? $column['title'] : $column; - $s .= ""; - } - $s .= ''; - foreach ($this->data as $rowData) - { - $s .= ''; - foreach ($rowData as $colIndex => $value) - { - if( isset($this->columns[$colIndex]['type']) ) - { - if ($this->columns[$colIndex]['type'] == 'priority') { - $value = $this->renderPriority($value); - } - if ($this->columns[$colIndex]['type'] == 'status') { - $value = $this->renderStatus($value); - } - } - $s .= ""; - } - $s .= ''; - } - $s .= '
    {$title}
    {$value}
    '; - return $s; - } - - // BUGID 3418 - public function renderStatus($item) - { - return "{$item['text']}"; - } - - public function renderPriority($prio) - { - $label = lang_get($this->prio_code_label[$prio]); - return $label; - } +code_status = $resultsCfg['code_status']; + $this->status_color = $resultsCfg['charts']['status_colour']; + $urgencyCfg = config_get('urgency'); + $this->prio_code_label = $urgencyCfg['code_label']; + } + + /** + * Does nothing. + * All rendering is contained in renderBodySection() + */ + public function renderCommonGlobals() + { + return ''; + } + + /** + * Does nothing. + * All rendering is contained in renderBodySection() + */ + public function renderHeadSection() + { + return ''; + } + + /** + * Renders a HTML table with css class "simple" and given id + */ + public function renderBodySection() + { + $s = ''; + // Render columns + $s .= ''; + foreach ($this->columns as $column) { + $title = is_array($column) ? $column['title'] : $column; + $s .= ""; + } + $s .= ''; + foreach ($this->data as $rowData) { + $s .= ''; + foreach ($rowData as $colIndex => $value) { + if (isset($this->columns[$colIndex]['type'])) { + if ($this->columns[$colIndex]['type'] == 'priority') { + $value = $this->renderPriority($value); + } + if ($this->columns[$colIndex]['type'] == 'status') { + $value = $this->renderStatus($value); + } + } + $s .= ""; + } + $s .= ''; + } + $s .= '
    {$title}
    {$value}
    '; + return $s; + } + + // BUGID 3418 + public function renderStatus($item) + { + return "{$item['text']}"; + } + + public function renderPriority($prio) + { + return lang_get($this->prio_code_label[$prio]); + } } diff --git a/lib/functions/tlInventory.class.php b/lib/functions/tlInventory.class.php index 3903e0efba..cfd7ad90a9 100644 --- a/lib/functions/tlInventory.class.php +++ b/lib/functions/tlInventory.class.php @@ -1,382 +1,406 @@ -testProjectID = intval($testProjectID); - } - - /** - * Class destructor - */ - function __destruct() - { - parent::__destruct(); - $this->testProjectID = null; - } - - - /** - * Initializes the inventory object - * @param array $inputData the name of the server - */ - protected function initInventoryData($inputData) - { - $this->inventoryId = intval($inputData->machineID); - $this->name = $inputData->machineName; - $this->ipAddress = $inputData->machineIp; - $this->ownerId = $inputData->machineOwner; - $this->inventoryContent['notes'] = $inputData->machineNotes; - $this->inventoryContent['purpose'] = $inputData->machinePurpose; - $this->inventoryContent['hardware'] = $inputData->machineHw; - } - - - /** - * Get the current array - * @return array data record - */ - public function getCurrentData() - { - $out = new stdClass(); - $out->machineID = $this->inventoryId; - $out->machineName = $this->name; - $out->machineIp = $this->ipAddress; - $out->machineOwner = $this->ownerId; - $out->machineNotes = $this->inventoryContent['notes']; - $out->machinePurpose = $this->inventoryContent['purpose']; - $out->machineHw = $this->inventoryContent['hardware']; - - return $out; - } - - - /** - * returns inventory data - * - * @param mixed $ids integer or array of integer - ID of inventory items - */ - protected function readDB($ids = null, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('detailLevel' => null, 'accessKey' => null); - $my['options'] = array_merge($my['options'], (array)$options); - - $doUnserialize = true; - switch($my['options']['detailLevel']) { - case 'minimun': - $fields2get = ' id '; - $doUnserialize = false; - break; - - default: - $fields2get = ' * '; - break; - } - $sql = "/* $debugMsg */ - SELECT {$fields2get} +testProjectID = intval($testProjectID); + } + + /** + * Class destructor + */ + public function __destruct() + { + parent::__destruct(); + $this->testProjectID = null; + } + + /** + * Initializes the inventory object + * + * @param array $inputData + * the name of the server + */ + protected function initInventoryData($inputData) + { + $this->inventoryId = intval($inputData->machineID); + $this->name = $inputData->machineName; + $this->ipAddress = $inputData->machineIp; + $this->ownerId = $inputData->machineOwner; + $this->inventoryContent['notes'] = $inputData->machineNotes; + $this->inventoryContent['purpose'] = $inputData->machinePurpose; + $this->inventoryContent['hardware'] = $inputData->machineHw; + } + + /** + * Get the current array + * + * @return array data record + */ + public function getCurrentData() + { + $out = new stdClass(); + $out->machineID = $this->inventoryId; + $out->machineName = $this->name; + $out->machineIp = $this->ipAddress; + $out->machineOwner = $this->ownerId; + $out->machineNotes = $this->inventoryContent['notes']; + $out->machinePurpose = $this->inventoryContent['purpose']; + $out->machineHw = $this->inventoryContent['hardware']; + + return $out; + } + + /** + * returns inventory data + * + * @param mixed $ids + * integer or array of integer - ID of inventory items + */ + protected function readDB($ids = null, $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['options'] = array( + 'detailLevel' => null, + 'accessKey' => null + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $doUnserialize = true; + switch ($my['options']['detailLevel']) { + case 'minimun': + $fields2get = ' id '; + $doUnserialize = false; + break; + + default: + $fields2get = ' * '; + break; + } + $sql = "/* $debugMsg */ + SELECT {$fields2get} FROM {$this->tables['inventory']} - WHERE testproject_id={$this->testProjectID}"; - - $clauses = null; - if (!is_null($ids)) { - if (!is_array($ids)) { - $clauses[] = "id = {$ids}"; - } else { - $clauses[] = "id IN (".implode(",",$ids).")"; - } - } - if ($clauses) { - $sql .= " AND " . implode(" AND ",$clauses); - } - - - if( is_null($my['options']['accessKey']) ) { - $recordset = $this->db->get_recordset($sql); - } else { - $recordset = $this->db->fetchRowsIntoMap($sql,$my['options']['accessKey']); - } - - - if(!is_null($recordset) && $doUnserialize) { - // unserialize text parameters - foreach ($recordset as $key => $item) { - $dummy = unserialize($recordset[$key]['content']); - $recordset[$key]['content'] = null; // used for ? who knows? - $recordset[$key]['notes'] = isset($dummy['notes']) ? $dummy['notes'] : ''; - $recordset[$key]['purpose'] = isset($dummy['purpose']) ? $dummy['purpose'] : ''; - $recordset[$key]['hardware'] = isset($dummy['hardware']) ? $dummy['hardware'] : ''; - } - } - return $recordset; - } - - - /** - * Writes a device into the database - * (both create and update request are supported - based on $this->inventoryId) - * - * @param integer $db [ref] the database connection - * @return integer returns tl::OK on success, tl::E_DBERROR else - * - */ - protected function writeToDB(&$db) - { - $auditData = $this->getAuditData(); - $auditData = current($auditData); - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $name = $db->prepare_string($this->name); - $ip = $db->prepare_string($this->ipAddress); - $data_serialized = $db->prepare_string(serialize($this->inventoryContent)); - if (is_null($this->inventoryId) || ($this->inventoryId == 0)) - { - $sql = "/* $debugMsg */ - INSERT INTO {$this->tables['inventory']} + WHERE testproject_id={$this->testProjectID}"; + + $clauses = null; + if (! is_null($ids)) { + if (! is_array($ids)) { + $clauses[] = "id = {$ids}"; + } else { + $clauses[] = "id IN (" . implode(",", $ids) . ")"; + } + } + if ($clauses) { + $sql .= " AND " . implode(" AND ", $clauses); + } + + if (is_null($my['options']['accessKey'])) { + $recordset = $this->db->get_recordset($sql); + } else { + $recordset = $this->db->fetchRowsIntoMap($sql, + $my['options']['accessKey']); + } + + if (! is_null($recordset) && $doUnserialize) { + // unserialize text parameters + foreach ($recordset as $key => $item) { + $dummy = unserialize($recordset[$key]['content']); + $recordset[$key]['content'] = null; // used for ? who knows? + $recordset[$key]['notes'] = isset($dummy['notes']) ? $dummy['notes'] : ''; + $recordset[$key]['purpose'] = isset($dummy['purpose']) ? $dummy['purpose'] : ''; + $recordset[$key]['hardware'] = isset($dummy['hardware']) ? $dummy['hardware'] : ''; + } + } + return $recordset; + } + + /** + * Writes a device into the database + * (both create and update request are supported - based on $this->inventoryId) + * + * @param integer $db + * [ref] the database connection + * @return integer returns tl::OK on success, tl::E_DBERROR else + * + */ + protected function writeToDB(&$db) + { + $auditData = $this->getAuditData(); + $auditData = current($auditData); + + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $name = $db->prepare_string($this->name); + $ip = $db->prepare_string($this->ipAddress); + $data_serialized = $db->prepare_string( + serialize($this->inventoryContent)); + if (is_null($this->inventoryId) || ($this->inventoryId == 0)) { + $sql = "/* $debugMsg */ + INSERT INTO {$this->tables['inventory']} (name,testproject_id,content,ipaddress, - owner_id,creation_ts) " . - " VALUES ('" . $name . "'," . $this->testProjectID . ",'" . - $data_serialized . "','" . $ip . "'," . - $this->ownerId . "," . $this->db->db_now() . ")"; - - $result = $this->db->exec_query($sql); - if ($result) { - $this->inventoryId = $db->insert_id($this->tables['inventory']); - logAuditEvent(TLS("audit_inventory_created",$this->name,$auditData['tproject_name']), - "CREATE",$this->name,"inventory"); - $this->userFeedback = langGetFormated('inventory_create_success',$this->name); - } else { - $this->userFeedback = langGetFormated('inventory_create_fails',$this->name); - tLog('Internal error: An inventory device "'.$this->name.'" was not created.', 'ERROR'); - } - } else { - $sql = "/* $debugMsg */UPDATE {$this->tables['inventory']} " . - " SET name='{$name}', content='{$data_serialized}', " . - " ipaddress='{$ip}', modification_ts=" . $this->db->db_now() . - ", testproject_id={$this->testProjectID}, owner_id=" . $this->ownerId . - " WHERE id={$this->inventoryId}"; - $result = $this->db->exec_query($sql); - if ($result) { - tLog('A device "'.$this->name.'" was not updated.', 'INFO'); - $this->userFeedback = langGetFormated('inventory_update_success',$this->name); - } else { - $this->setUserFeedback(langGetFormated('inventory_update_fails',$this->name)); - tLog('Internal error: An inventory device "'.$this->name.'" was not updated.', 'ERROR'); - } - } - - return $result ? tl::OK : self::E_DBERROR; - } - - - /** - * DB request to delete a device from the database - * - * @return integer returns tl::OK on success, tl:ERROR else - */ - protected function deleteFromDB() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ - DELETE FROM {$this->tables['inventory']} - WHERE id = " . $this->inventoryId; - $result = $this->db->exec_query($sql); - return $result ? tl::OK : tl::ERROR; - } - - /** - * Deletes item from inventory on db - * - * @param int $itemID - * @return integer returns tl::OK on success, tl:ERROR else - */ - public function deleteInventory($itemID) - { - $auditData = $this->getAuditData(); - $auditData = current($auditData); - $this->inventoryId = intval($itemID); - - // check existence / get name of the record - $recordset = $this->readDB($this->inventoryId); - if(!is_null($recordset)) { - $this->name = $recordset[0]['name']; - $result = $this->deleteFromDB(); - - if ($result == tl::OK) { - logAuditEvent(TLS("audit_inventory_deleted",$this->name,$auditData['tproject_name']), - "DELETE",$this->name,"inventory"); - $this->userFeedback = langGetFormated('inventory_delete_success',$this->name); - } else { - $this->userFeedback = langGetFormated('inventory_delete_fails',$this->name); - tLog('Internal error: The device "'.$this->name.'" was not deleted.', 'ERROR'); - } - } else { - $this->userFeedback = lang_get('inventory_no_device').' ID='.$this->inventoryId; - tLog('Internal error: The device "'.$this->name.'" was not deleted.', 'ERROR'); - } - - return $result; - } - - - /** - * create or update an inventory - * - * @param array $data list of parameters - * @return boolean result of action - **/ - public function setInventory($data) - { - $this->initInventoryData($data); - $result = $this->checkInventoryData(); - if ($result == tl::OK) { - $result = $this->writeToDB($this->db); - } - return $result; - } - - - /** - * Get all inventory data for the project - * - * @param string $options: - * detailLevel - optional - indicates data you want to have - * null -> all columns - * minimun -> just the id, useful when you need to delete all inventories - * for a test project - * accessKey: field name, it's value will be used as accessKey - * - * @return array - */ - public function getAll($options=null) - { - $data = self::readDB(null,$options); - return $data; - } - - - /** - * Checks a server name and IP for a certain testproject already exists in the database - * Checking works for both create and update request - * - * @return integer return tl::OK on success, else error code like - * is tlInventory::E_NAMEALREADYEXISTS - */ - protected function checkInventoryData() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $result = tl::OK; - $name = $this->db->prepare_string(strtoupper($this->name)); - $ipAddress = $this->db->prepare_string(strtoupper($this->ipAddress)); - - if (strlen($name) == 0) { - $result = self::E_NAMELENGTH; - $this->userFeedback = - langGetFormated('inventory_name_empty',$name); - } - - if ($result == tl::OK) { - $sql = "/* $debugMsg */ - SELECT id FROM {$this->tables['inventory']} " . - " WHERE name='" . $name. - "' AND testproject_id={$this->testProjectID}"; - - if ($this->inventoryId > 0) { - $sql .= ' AND NOT id='.$this->inventoryId; - } - - if ($this->db->fetchFirstRow($sql)) { - $result = self::E_NAMEALREADYEXISTS; - $this->userFeedback = langGetFormated('inventory_name_exists',$this->name); - } - } - - if ($result == tl::OK && !empty($ipAddress)) { - $sql = "/* $debugMsg */ - SELECT id FROM {$this->tables['inventory']} " . - " WHERE ipaddress='" . $ipAddress . - "' AND testproject_id={$this->testProjectID}"; - - if ($this->inventoryId > 0) { - $sql .= ' AND NOT id='.$this->inventoryId; - } - if ($this->db->fetchFirstRow($sql)) { - $result = self::E_IPALREADYEXISTS; - $this->userFeedback = langGetFormated('inventory_ip_exists',$ipAddress); - } - } - - return $result; - } - - /** - * - * - */ - protected function getAuditData() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " /* $debugMsg */ " . - " SELECT id, name AS tproject_name FROM {$this->tables['nodes_hierarchy']} " . - " WHERE id = {$this->testProjectID} "; - $info = $this->db->fetchRowsIntoMap($sql,'id'); - return $info; - } -} \ No newline at end of file + owner_id,creation_ts) " . " VALUES ('" . $name . "'," . + $this->testProjectID . ",'" . $data_serialized . "','" . $ip . + "'," . $this->ownerId . "," . $this->db->db_now() . ")"; + + $result = $this->db->exec_query($sql); + if ($result) { + $this->inventoryId = $db->insert_id($this->tables['inventory']); + logAuditEvent( + TLS("audit_inventory_created", $this->name, + $auditData['tproject_name']), "CREATE", $this->name, + "inventory"); + $this->userFeedback = langGetFormated( + 'inventory_create_success', $this->name); + } else { + $this->userFeedback = langGetFormated('inventory_create_fails', + $this->name); + tLog( + 'Internal error: An inventory device "' . $this->name . + '" was not created.', 'ERROR'); + } + } else { + $sql = "/* $debugMsg */UPDATE {$this->tables['inventory']} " . + " SET name='{$name}', content='{$data_serialized}', " . + " ipaddress='{$ip}', modification_ts=" . $this->db->db_now() . + ", testproject_id={$this->testProjectID}, owner_id=" . + $this->ownerId . " WHERE id={$this->inventoryId}"; + $result = $this->db->exec_query($sql); + if ($result) { + tLog('A device "' . $this->name . '" was not updated.', 'INFO'); + $this->userFeedback = langGetFormated( + 'inventory_update_success', $this->name); + } else { + $this->setUserFeedback( + langGetFormated('inventory_update_fails', $this->name)); + tLog( + 'Internal error: An inventory device "' . $this->name . + '" was not updated.', 'ERROR'); + } + } + + return $result ? tl::OK : self::E_DBERROR; + } + + /** + * DB request to delete a device from the database + * + * @return integer returns tl::OK on success, tl:ERROR else + */ + protected function deleteFromDB() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ + DELETE FROM {$this->tables['inventory']} + WHERE id = " . $this->inventoryId; + $result = $this->db->exec_query($sql); + return $result ? tl::OK : tl::ERROR; + } + + /** + * Deletes item from inventory on db + * + * @param int $itemID + * @return integer returns tl::OK on success, tl:ERROR else + */ + public function deleteInventory($itemID) + { + $auditData = $this->getAuditData(); + $auditData = current($auditData); + $this->inventoryId = intval($itemID); + + // check existence / get name of the record + $recordset = $this->readDB($this->inventoryId); + if (! is_null($recordset)) { + $this->name = $recordset[0]['name']; + $result = $this->deleteFromDB(); + + if ($result == tl::OK) { + logAuditEvent( + TLS("audit_inventory_deleted", $this->name, + $auditData['tproject_name']), "DELETE", $this->name, + "inventory"); + $this->userFeedback = langGetFormated( + 'inventory_delete_success', $this->name); + } else { + $this->userFeedback = langGetFormated('inventory_delete_fails', + $this->name); + tLog( + 'Internal error: The device "' . $this->name . + '" was not deleted.', 'ERROR'); + } + } else { + $this->userFeedback = lang_get('inventory_no_device') . ' ID=' . + $this->inventoryId; + tLog( + 'Internal error: The device "' . $this->name . + '" was not deleted.', 'ERROR'); + } + + return $result; + } + + /** + * create or update an inventory + * + * @param array $data + * list of parameters + * @return boolean result of action + */ + public function setInventory($data) + { + $this->initInventoryData($data); + $result = $this->checkInventoryData(); + if ($result == tl::OK) { + $result = $this->writeToDB($this->db); + } + return $result; + } + + /** + * Get all inventory data for the project + * + * @param string $options: + * detailLevel - optional - indicates data you want to have + * null -> all columns + * minimun -> just the id, useful when you need to delete all inventories + * for a test project + * accessKey: field name, it's value will be used as accessKey + * + * @return array + */ + public function getAll($options = null) + { + return self::readDB(null, $options); + } + + /** + * Checks a server name and IP for a certain testproject already exists in the database + * Checking works for both create and update request + * + * @return integer return tl::OK on success, else error code like + * is tlInventory::E_NAMEALREADYEXISTS + */ + protected function checkInventoryData() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $result = tl::OK; + $name = $this->db->prepare_string(strtoupper($this->name)); + $ipAddress = $this->db->prepare_string(strtoupper($this->ipAddress)); + + if (strlen($name) == 0) { + $result = self::E_NAMELENGTH; + $this->userFeedback = langGetFormated('inventory_name_empty', $name); + } + + if ($result == tl::OK) { + $sql = "/* $debugMsg */ + SELECT id FROM {$this->tables['inventory']} " . " WHERE name='" . + $name . "' AND testproject_id={$this->testProjectID}"; + + if ($this->inventoryId > 0) { + $sql .= ' AND NOT id=' . $this->inventoryId; + } + + if ($this->db->fetchFirstRow($sql)) { + $result = self::E_NAMEALREADYEXISTS; + $this->userFeedback = langGetFormated('inventory_name_exists', + $this->name); + } + } + + if ($result == tl::OK && ! empty($ipAddress)) { + $sql = "/* $debugMsg */ + SELECT id FROM {$this->tables['inventory']} " . " WHERE ipaddress='" . + $ipAddress . "' AND testproject_id={$this->testProjectID}"; + + if ($this->inventoryId > 0) { + $sql .= ' AND NOT id=' . $this->inventoryId; + } + if ($this->db->fetchFirstRow($sql)) { + $result = self::E_IPALREADYEXISTS; + $this->userFeedback = langGetFormated('inventory_ip_exists', + $ipAddress); + } + } + + return $result; + } + + /** + */ + protected function getAuditData() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = " /* $debugMsg */ " . + " SELECT id, name AS tproject_name FROM {$this->tables['nodes_hierarchy']} " . + " WHERE id = {$this->testProjectID} "; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } +} diff --git a/lib/functions/tlIssueTracker.class.php b/lib/functions/tlIssueTracker.class.php index 79610eb1a5..3de5c35dbe 100644 --- a/lib/functions/tlIssueTracker.class.php +++ b/lib/functions/tlIssueTracker.class.php @@ -1,757 +1,819 @@ - array('type' => 'bugzilla', - 'api' => 'xmlrpc', 'enabled' => true, - 'order' => 1), - 2 => array('type' => 'bugzilla', 'api' => 'db', - 'enabled' => true, 'order' => 2), - - 3 => array('type' => 'mantis', 'api' =>'soap', - 'enabled' => true, 'order' => 3), - 4 => array('type' => 'mantis', 'api' =>'db', - 'enabled' => true, 'order' => 5), - 24 => array('type' => 'mantis','api' =>'rest', - 'enabled' => true, 'order' => 5), - - 5 => array('type' => 'jira', 'api' =>'soap', - 'enabled' => true, 'order' => 6), - 6 => array('type' => 'jira', 'api' =>'db', - 'enabled' => true, 'order' => 7), - 7 => array('type' => 'jira', 'api' =>'rest', - 'enabled' => true, 'order' => 8), - 8 => array('type' => 'fogbugz','api' =>'rest', - 'enabled' => true, 'order' => 9), - 9 => array('type' => 'fogbugz','api' =>'db', - 'enabled' => true, 'order' => 10), - 10 => array('type' => 'gforge','api' =>'soap', - 'enabled' => false, 'order' => 11), - 11 => array('type' => 'gforge','api' =>'db', - 'enabled' => false, 'order' => 12), - 12 => array('type' => 'eventum','api' =>'db', - 'enabled' => false, 'order' => 13), - 13 => array('type' => 'polarion', 'api' =>'soap', - 'enabled' => false, 'order' => 14), - 14 => array('type' => 'youtrack','api' =>'rest', - 'enabled' => true, 'order' => 15), - 15 => array('type' => 'redmine','api' =>'rest', - 'enabled' => true, 'order' => 16), - 16 => array('type' => 'redmine','api' =>'db', - 'enabled' => false, 'order' => 17), - 17 => array('type' => 'seapine','api' =>'soap', - 'enabled' => false, 'order' => 18), - 18 => array('type' => 'seapine','api' =>'db', - 'enabled' => false, 'order' => 19), - 19 => array('type' => 'trac','api' =>'xmlrpc', - 'enabled' => true, 'order' => 20), - 20 => array('type' => 'trackplus','api' =>'soap', - 'enabled' => false, 'order' => 21), - 21 => array('type' => 'trackplus','api' =>'db', - 'enabled' => false, 'order' => 22), - 22 => array('type' => 'gitlab','api' =>'rest', - 'enabled' => true, 'order' => 23), - 23 => array('type' => 'kaiten','api' =>'rest', - 'enabled' => true, 'order' => 24) - ); - - var $entitySpec = array('name' => 'string','cfg' => 'string','type' => 'int'); - - /** - * Class constructor - * - * @param resource &$db reference to the database handler - */ - function __construct(&$db) - { - parent::__construct(); - - // populate types property - $this->getTypes(); - $this->db = &$db; - } - - - - /** - * @return hash - * - * - */ - function getSystems($opt=null) - { - $my = array('options' => null); - $my['options']['status'] = 'enabled'; // enabled,disabled,all - $my['options'] = array_merge($my['options'],(array)$opt); - - switch($my['options']['status']) - { - case 'enabled': - $tval = true; - break; - - case 'disabled': - $tval = false; - break; - - default: - $tval = null; - break; - } - - $ret = array(); - foreach($this->systems as $code => $elem) - { - $idx = 0; - if($tval== null || $elem['enabled'] == $tval) - { - $ret[$code] = $elem; - } - } - return $ret; - } - - /** - * @return hash - * - * - */ - function getTypes() - { - if( is_null($this->types) ) { - foreach($this->systems as $code => $spec) { - if ($spec['enabled']) { - $this->types[$code] = $spec['type'] . " (Interface: {$spec['api']})"; - } - } - } - return $this->types; - } - - - /** - * @return - * - * - */ - function getImplementationForType($issueTrackerType) - { - $spec = $this->systems[$issueTrackerType]; - return $spec['type'] . $spec['api'] . 'Interface'; - } - - /** - * @return hash - * - * - */ - function getEntitySpec() - { - return $this->entitySpec; - } - - - /** - * - */ - function create($it) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array('status_ok' => 0, 'id' => 0, 'msg' => 'name already exists'); - - // Critic we need to do this before sanitize, because $it is changed - $xlmCfg = trim($it->cfg); - - // allow empty config - if(strlen($xlmCfg) > 0) - { - $ret = $this->checkXMLCfg($xlmCfg); - if(!$ret['status_ok']) - { - return $ret; // >>>---> Bye! - } - } - - - $safeobj = $this->sanitize($it); - // empty name is not allowed - if( is_null($safeobj->name) ) - { - $ret['msg'] = 'empty name is not allowed'; - return $ret; // >>>---> Bye! - } - - // need to check if name already exist - if( is_null($this->getByName($it->name,array('output' => 'id')) )) - { - $sql = "/* debugMsg */ INSERT INTO {$this->tables['issuetrackers']} " . - " (name,cfg,type) " . - " VALUES('" . $safeobj->name . "','" . $safeobj->cfg . "',{$safeobj->type})"; - - if( $this->db->exec_query($sql) ) - { - // at least for Postgres DBMS table name is needed. - $itemID=$this->db->insert_id($this->tables['issuetrackers']); - $ret = array('status_ok' => 1, 'id' => $itemID, 'msg' => 'ok'); - } - else - { - $ret = array('status_ok' => 0, 'id' => 0, 'msg' => $this->db->error_msg()); - } - } - - return $ret; - } - - - /** - * - */ - function update($it) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; - $msg = array(); - $msg['duplicate_name'] = "Update can not be done - name %s already exists for id %s"; - $msg['ok'] = "operation OK for id %s"; - - // Critic we need to do this before sanitize, because $it is changed - $xlmCfg = trim($it->cfg); - - $safeobj = $this->sanitize($it); - $ret = array('status_ok' => 1, 'id' => $it->id, 'msg' => ''); - - // allow empty config - if(strlen($xlmCfg) > 0) - { - $ret = $this->checkXMLCfg($xlmCfg); - } - - // check for duplicate name - if( $ret['status_ok'] ) - { - $info = $this->getByName($safeobj->name); - if( !is_null($info) && ($info['id'] != $it->id) ) - { - $ret['status_ok'] = 0; - $ret['msg'] .= sprintf($msg['duplicate_name'], $safeobj->name, $info['id']); - } - } - - if( $ret['status_ok'] ) - { - $sql = "UPDATE {$this->tables['issuetrackers']} " . - " SET name = '" . $safeobj->name. "'," . - " cfg = '" . $safeobj->cfg . "'," . - " type = " . $safeobj->type . - " WHERE id = " . intval($it->id); - $result = $this->db->exec_query($sql); - $ret['msg'] .= sprintf($msg['ok'],$it->id); - - } - return $ret; - - } //function end - - - - /** - * delete can be done ONLY if ID is not linked to test project - */ - function delete($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; - - $msg = array(); - $msg['linked'] = "Failure - id %s is linked to: "; - $msg['tproject_details'] = " testproject '%s' with id %s %s"; - $msg['syntax_error'] = "Syntax failure - id %s seems to be an invalid value"; - $msg['ok'] = "operation OK for id %s"; - - $ret = array('status_ok' => 1, 'id' => $id, 'msg' => $debugMsg); - if(is_null($id) || ($safeID = intval($id)) <= 0) - { - $ret['status_ok'] = 0; - $ret['id'] = $id; - $ret['msg'] .= sprintf($msg['syntax_error'],$id); - return $ret; // >>>-----> Bye! - } - - - // check if ID is linked - $links = $this->getLinks($safeID); - if( is_null($links) ) - { - $sql = " /* $debugMsg */ DELETE FROM {$this->tables['issuetrackers']} " . - " WHERE id = " . intval($safeID); - $result = $this->db->exec_query($sql); - $ret['msg'] .= sprintf($msg['ok'],$safeID); - - } - else - { - $ret['status_ok'] = 0; - $dummy = sprintf($msg['linked'],$safeID); - $sep = ' / '; - foreach($links as $item) - { - $dummy .= sprintf($msg['tproject_details'],$item['testproject_name'],$item['testproject_id'],$sep); - } - $ret['msg'] .= rtrim($dummy,$sep); - - } - return $ret; - - } //function end - - - - - - /** - * - */ - function getByID($id, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - return $this->getByAttr(array('key' => 'id', 'value' => $id),$options); - } - - - /** - * - */ - function getByName($name, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - return $this->getByAttr(array('key' => 'name', 'value' => $name),$options); - } - - - /** - * - */ - function getByAttr($attr, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('output' => 'full'); - $my['options'] = array_merge($my['options'], (array)$options); - - $sql = "/* debugMsg */ SELECT "; - switch($my['options']['output']) - { - case 'id': - $sql .= " id "; - break; - - case 'full': - default: - $sql .= " * "; - break; - - } - - switch($attr['key']) - { - case 'id': - $where = " WHERE id = " . intval($attr['value']); - break; - - case 'name': - default: - $where = " WHERE name = '" . $this->db->prepare_string($attr['value']) . "'"; - break; - } - - - $sql .= " FROM {$this->tables['issuetrackers']} " . $where; - $rs = $this->db->get_recordset($sql); - if( !is_null($rs) ) - { - $rs = $rs[0]; - $rs['implementation'] = $this->getImplementationForType($rs['type']); - } - return $rs; - } - - - - /* - * Sanitize and do minor checks - * - * Sanitize Operations - * keys name -> trim will be applied - * type -> intval() wil be applied - * cfg - * - * For strings also db_prepare_string() will be applied - * - * - * Check Operations - * keys name -> if '' => will be set to NULL - * - */ - function sanitize($obj) - { - $sobj = $obj; - - // remove the standard set of characters considered harmful - // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab - // "\r" - carriage return - // and spaces - // fortunatelly this is trim standard behaviour - $k2san = array('name'); - foreach($k2san as $key) - { - $value = trim($obj->$key); - switch($key) - { - case 'name': - $sobj->$key = ($value == '') ? null : $value; - break; - } - - if( !is_null($sobj->$key) ) - { - $sobj->$key = $this->db->prepare_string($obj->$key); - } - - } - - // seems here is better do not touch. - $sobj->cfg = $this->db->prepare_string($obj->cfg); - $sobj->type = intval($obj->type); - - return $sobj; - } - - - - /* - * - * - */ - function link($id,$tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($id)) - { - return; - } - - // Check if link exist for test project ID, in order to INSERT or UPDATE - $statusQuo = $this->getLinkedTo($tprojectID); - - if( is_null($statusQuo) ) - { - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['testproject_issuetracker']} " . - " (testproject_id,issuetracker_id) " . - " VALUES(" . intval($tprojectID) . "," . intval($id) . ")"; - } - else - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['testproject_issuetracker']} " . - " SET issuetracker_id = " . intval($id) . - " WHERE testproject_id = " . intval($tprojectID); - } - $this->db->exec_query($sql); - } - - - /* - * - * - */ - function unlink($id,$tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($id)) - { - return; - } - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['testproject_issuetracker']} " . - " WHERE testproject_id = " . intval($tprojectID) . - " AND issuetracker_id = " . intval($id); - $this->db->exec_query($sql); - } - - - /* - * - * - */ - function getLinks($id, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('opt' => array('getDeadLinks' => false)); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - if(is_null($id)) - { - return; - } - - - $sql = "/* $debugMsg */ " . - " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name " . - " FROM {$this->tables['testproject_issuetracker']} TPIT" . - " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPIT.testproject_id " . - " WHERE TPIT.issuetracker_id = " . intval($id); - - if($my['opt']['getDeadLinks']) - { - $sql .= ' AND NHTPR.id IS NULL AND NHTPR.name IS NULL '; - } - - $ret = $this->db->fetchRowsIntoMap($sql,'testproject_id'); - return $ret; - } - - - - /* - * - * - */ - function getLinkSet() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ " . - " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, TPIT.issuetracker_id " . - " FROM {$this->tables['testproject_issuetracker']} TPIT" . - " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPIT.testproject_id "; - - $ret = $this->db->fetchRowsIntoMap($sql,'testproject_id'); - return $ret; - } - - /* - * - * - */ - function getAll($options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('output' => null, 'orderByField' => 'name', 'checkEnv' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - $add_fields = ''; - if( $my['options']['output'] == 'add_link_count' ) - { - $add_fields = ", 0 AS link_count "; - } - - $orderByClause = is_null($my['options']['orderByField']) ? '' : 'ORDER BY ' . $my['options']['orderByField']; - - $sql = "/* debugMsg */ SELECT * {$add_fields} "; - $sql .= " FROM {$this->tables['issuetrackers']} {$orderByClause} "; - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - - $lc = null; - if( !is_null($rs) ) - { - - if( $my['options']['output'] == 'add_link_count' ) - { - $sql = "/* debugMsg */ SELECT COUNT(0) AS lcount, ITD.id"; - $sql .= " FROM {$this->tables['issuetrackers']} ITD " . - " JOIN {$this->tables['testproject_issuetracker']} " . - " ON issuetracker_id = ITD.id " . - " GROUP BY ITD.id "; - $lc = $this->db->fetchRowsIntoMap($sql,'id'); - } - - - foreach($rs as &$item) - { - $item['verbose'] = $item['name'] . " ( {$this->types[$item['type']]} )" ; - $item['type_descr'] = $this->types[$item['type']]; - $item['env_check_ok'] = true; - $item['env_check_msg'] = ''; - $item['connection_status'] = ''; - - if( $my['options']['checkEnv'] ) - { - $impl = $this->getImplementationForType($item['type']); - $dummy = $impl::checkEnv(); - $item['env_check_ok'] = $dummy['status']; - $item['env_check_msg'] = $dummy['msg']; - } - - - if( !is_null($lc) ) - { - if( isset($lc[$item['id']]) ) - { - $item['link_count'] = intval($lc[$item['id']]['lcount']); - } - } - } - } - return $rs; - } - - - /* - * - * - */ - function getLinkedTo($tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($tprojectID)) - { - return; - } - $sql = "/* $debugMsg */ " . - " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, " . - " TPIT.issuetracker_id,ITRK.name AS issuetracker_name, ITRK.type" . - " FROM {$this->tables['testproject_issuetracker']} TPIT" . - " JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPIT.testproject_id " . - " JOIN {$this->tables['issuetrackers']} ITRK " . - " ON ITRK.id = TPIT.issuetracker_id " . - " WHERE TPIT.testproject_id = " . intval($tprojectID); - - $ret = $this->db->get_recordset($sql); - if (!is_null($ret)) { - $ret = $ret[0]; - $ret['verboseType'] = $this->types[$ret['type']]; - $spec = $this->systems[$ret['type']]; - $ret['api'] = $spec['api']; - } - - return $ret; - } - - - /** - * - * - */ - function getInterfaceObject($tprojectID) - { - $issueT = $this->getLinkedTo($tprojectID); - $name = $issueT['issuetracker_name']; - $goodForSession = ($issueT['api'] != 'db'); - - if($goodForSession && isset($_SESSION['its'][$name])) - { - return $_SESSION['its'][$name]; - } - - try - { - if( !is_null($issueT) ) - { - $itd = $this->getByID($issueT['issuetracker_id']); - $iname = $itd['implementation']; - - if($goodForSession) - { - $_SESSION['its'][$name] = new $iname($iname,$itd['cfg'],$itd['name']); - } - else - { - $ixx = new $iname($iname,$itd['cfg'],$itd['name']); - return $ixx; - } - } - else - { - $_SESSION['its'][$name] = null; - } - return $_SESSION['its'][$name]; - } - catch (Exception $e) - { - echo('Probably there is some PHP Config issue regarding extension'); - echo($e->getMessage().'
    '.$e->getTraceAsString().'
    '); - } - } - - /* - * - * - */ - function checkConnection($its) { - $xx = $this->getByID($its); - $class2create = $xx['implementation']; - $its = new $class2create($xx['type'],$xx['cfg'],$xx['name']); - - $op = $its->isConnected(); - - // because I've added simple cache on $_SESSION - // IMHO is better to update cache after this check - $_SESSION['its'][$xx['name']] = $its; - - return $op; - } - - /** - * - */ - function checkXMLCfg($xmlString) - { - $signature = 'Source:' . __METHOD__; - $op = array('status_ok' => true, 'msg' => ''); - - $xmlCfg = " " . trim($xmlString); - libxml_use_internal_errors(true); - try - { - $cfg = simplexml_load_string($xmlCfg); - if (!$cfg) - { - $op['status_ok'] = false; - $op['msg'] = $signature . " - Failure loading XML STRING\n"; - foreach(libxml_get_errors() as $error) - { - $op['msg'] .= "\t" . $error->message; - } - } - } - catch(Exception $e) - { - $op['status_ok'] = false; - $op['msg'] = $signature . " - Exception loading XML STRING\n" . 'Message: ' .$e->getMessage(); - } - - return $op; - } - -} // end class \ No newline at end of file + array( + 'type' => 'bugzilla', + 'api' => 'xmlrpc', + 'enabled' => true, + 'order' => 1 + ), + 2 => array( + 'type' => 'bugzilla', + 'api' => 'db', + 'enabled' => true, + 'order' => 2 + ), + + 3 => array( + 'type' => 'mantis', + 'api' => 'soap', + 'enabled' => true, + 'order' => 3 + ), + 4 => array( + 'type' => 'mantis', + 'api' => 'db', + 'enabled' => true, + 'order' => 4 + ), + 24 => array( + 'type' => 'mantis', + 'api' => 'rest', + 'enabled' => true, + 'order' => 5 + ), + + 5 => array( + 'type' => 'jira', + 'api' => 'soap', + 'enabled' => true, + 'order' => 6 + ), + 6 => array( + 'type' => 'jira', + 'api' => 'db', + 'enabled' => true, + 'order' => 7 + ), + 7 => array( + 'type' => 'jira', + 'api' => 'rest', + 'enabled' => true, + 'order' => 8 + ), + 8 => array( + 'type' => 'fogbugz', + 'api' => 'rest', + 'enabled' => true, + 'order' => 9 + ), + 9 => array( + 'type' => 'fogbugz', + 'api' => 'db', + 'enabled' => true, + 'order' => 10 + ), + 10 => array( + 'type' => 'gforge', + 'api' => 'soap', + 'enabled' => false, + 'order' => 11 + ), + 11 => array( + 'type' => 'gforge', + 'api' => 'db', + 'enabled' => false, + 'order' => 12 + ), + 12 => array( + 'type' => 'eventum', + 'api' => 'db', + 'enabled' => false, + 'order' => 13 + ), + 13 => array( + 'type' => 'polarion', + 'api' => 'soap', + 'enabled' => false, + 'order' => 14 + ), + 14 => array( + 'type' => 'youtrack', + 'api' => 'rest', + 'enabled' => true, + 'order' => 15 + ), + 15 => array( + 'type' => 'redmine', + 'api' => 'rest', + 'enabled' => true, + 'order' => 16 + ), + 16 => array( + 'type' => 'redmine', + 'api' => 'db', + 'enabled' => false, + 'order' => 17 + ), + 17 => array( + 'type' => 'seapine', + 'api' => 'soap', + 'enabled' => false, + 'order' => 18 + ), + 18 => array( + 'type' => 'seapine', + 'api' => 'db', + 'enabled' => false, + 'order' => 19 + ), + 19 => array( + 'type' => 'trac', + 'api' => 'xmlrpc', + 'enabled' => true, + 'order' => 20 + ), + 20 => array( + 'type' => 'trackplus', + 'api' => 'soap', + 'enabled' => false, + 'order' => 21 + ), + 21 => array( + 'type' => 'trackplus', + 'api' => 'db', + 'enabled' => false, + 'order' => 22 + ), + 22 => array( + 'type' => 'gitlab', + 'api' => 'rest', + 'enabled' => true, + 'order' => 23 + ), + 23 => array( + 'type' => 'kaiten', + 'api' => 'rest', + 'enabled' => true, + 'order' => 24 + ), + 25 => array( + 'type' => 'github', + 'api' => 'rest', + 'enabled' => false, + 'order' => 25 + ), + 26 => array( + 'type' => 'trello', + 'api' => 'rest', + 'enabled' => true, + 'order' => 26 + ), + 27 => array( + 'type' => 'tuleap', + 'api' => 'rest', + 'enabled' => true, + 'order' => 27 + ) + ); + + private $entitySpec = array( + 'name' => 'string', + 'cfg' => 'string', + 'type' => 'int' + ); + + /** + * Class constructor + * + * @param + * resource &$db reference to the database handler + */ + public function __construct(&$db) + { + parent::__construct(); + + // populate types property + $this->getTypes(); + $this->db = &$db; + } + + /** + * + * @return array + * + * + */ + public function getSystems($opt = null) + { + $my = array( + 'options' => null + ); + $my['options']['status'] = 'enabled'; // enabled,disabled,all + $my['options'] = array_merge($my['options'], (array) $opt); + + switch ($my['options']['status']) { + case 'enabled': + $tval = true; + break; + + case 'disabled': + $tval = false; + break; + + default: + $tval = null; + break; + } + + $ret = []; + foreach ($this->systems as $code => $elem) { + if ($tval == null || $elem['enabled'] == $tval) { + $ret[$code] = $elem; + } + } + return $ret; + } + + /** + * + * @return array + * + * + */ + public function getTypes() + { + if (is_null($this->types)) { + foreach ($this->systems as $code => $spec) { + if ($spec['enabled']) { + $this->types[$code] = $spec['type'] . + " (Interface: {$spec['api']})"; + } + } + } + return $this->types; + } + + /** + * + * @return + * + * + */ + public function getImplementationForType($issueTrackerType) + { + $spec = $this->systems[$issueTrackerType]; + return $spec['type'] . $spec['api'] . 'Interface'; + } + + /** + * + * @return array + * + * + */ + public function getEntitySpec() + { + return $this->entitySpec; + } + + /** + */ + public function create($it) + { + $ret = array( + 'status_ok' => 0, + 'id' => 0, + 'msg' => 'name already exists' + ); + + // Critic we need to do this before sanitize, because $it is changed + $xlmCfg = trim($it->cfg); + + // allow empty config + if (strlen($xlmCfg) > 0) { + $ret = $this->checkXMLCfg($xlmCfg); + if (! $ret['status_ok']) { + return $ret; // >>>---> Bye! + } + } + + $safeobj = $this->sanitize($it); + // empty name is not allowed + if (is_null($safeobj->name)) { + $ret['msg'] = 'empty name is not allowed'; + return $ret; // >>>---> Bye! + } + + // need to check if name already exist + if (is_null($this->getByName($it->name, array( + 'output' => 'id' + )))) { + $sql = "/* debugMsg */ INSERT INTO {$this->tables['issuetrackers']} " . + " (name,cfg,type) " . " VALUES('" . $safeobj->name . "','" . + $safeobj->cfg . "',{$safeobj->type})"; + + if ($this->db->exec_query($sql)) { + // at least for Postgres DBMS table name is needed. + $itemID = $this->db->insert_id($this->tables['issuetrackers']); + $ret = array( + 'status_ok' => 1, + 'id' => $itemID, + 'msg' => 'ok' + ); + } else { + $ret = array( + 'status_ok' => 0, + 'id' => 0, + 'msg' => $this->db->error_msg() + ); + } + } + + return $ret; + } + + /** + */ + public function update($it) + { + $msg = array(); + $msg['duplicate_name'] = "Update can not be done - name %s already exists for id %s"; + $msg['ok'] = "operation OK for id %s"; + + // Critic we need to do this before sanitize, because $it is changed + $xlmCfg = trim($it->cfg); + + $safeobj = $this->sanitize($it); + $ret = array( + 'status_ok' => 1, + 'id' => $it->id, + 'msg' => '' + ); + + // allow empty config + if (strlen($xlmCfg) > 0) { + $ret = $this->checkXMLCfg($xlmCfg); + } + + // check for duplicate name + if ($ret['status_ok']) { + $info = $this->getByName($safeobj->name); + if (! is_null($info) && ($info['id'] != $it->id)) { + $ret['status_ok'] = 0; + $ret['msg'] .= sprintf($msg['duplicate_name'], $safeobj->name, + $info['id']); + } + } + + if ($ret['status_ok']) { + $sql = "UPDATE {$this->tables['issuetrackers']} " . " SET name = '" . + $safeobj->name . "'," . " cfg = '" . $safeobj->cfg . "'," . + " type = " . $safeobj->type . " WHERE id = " . + intval($it->id); + $this->db->exec_query($sql); + $ret['msg'] .= sprintf($msg['ok'], $it->id); + } + return $ret; + } + + /** + * delete can be done ONLY if ID is not linked to test project + */ + public function delete($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; + + $msg = array(); + $msg['linked'] = "Failure - id %s is linked to: "; + $msg['tproject_details'] = " testproject '%s' with id %s %s"; + $msg['syntax_error'] = "Syntax failure - id %s seems to be an invalid value"; + $msg['ok'] = "operation OK for id %s"; + + $ret = array( + 'status_ok' => 1, + 'id' => $id, + 'msg' => $debugMsg + ); + if (is_null($id) || ($safeID = intval($id)) <= 0) { + $ret['status_ok'] = 0; + $ret['id'] = $id; + $ret['msg'] .= sprintf($msg['syntax_error'], $id); + return $ret; // >>>-----> Bye! + } + + // check if ID is linked + $links = $this->getLinks($safeID); + if (is_null($links)) { + $sql = " /* $debugMsg */ DELETE FROM {$this->tables['issuetrackers']} " . + " WHERE id = " . intval($safeID); + $this->db->exec_query($sql); + $ret['msg'] .= sprintf($msg['ok'], $safeID); + } else { + $ret['status_ok'] = 0; + $dummy = sprintf($msg['linked'], $safeID); + $sep = ' / '; + foreach ($links as $item) { + $dummy .= sprintf($msg['tproject_details'], + $item['testproject_name'], $item['testproject_id'], $sep); + } + $ret['msg'] .= rtrim($dummy, $sep); + } + return $ret; + } + + /** + */ + public function getByID($id, $options = null) + { + return $this->getByAttr(array( + 'key' => 'id', + 'value' => $id + ), $options); + } + + /** + */ + public function getByName($name, $options = null) + { + return $this->getByAttr(array( + 'key' => 'name', + 'value' => $name + ), $options); + } + + /** + */ + private function getByAttr($attr, $options = null) + { + $my['options'] = array( + 'output' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = "/* debugMsg */ SELECT "; + switch ($my['options']['output']) { + case 'id': + $sql .= " id "; + break; + + case 'full': + default: + $sql .= " * "; + break; + } + + switch ($attr['key']) { + case 'id': + $where = " WHERE id = " . intval($attr['value']); + break; + + case 'name': + default: + $where = " WHERE name = '" . + $this->db->prepare_string($attr['value']) . "'"; + break; + } + + $sql .= " FROM {$this->tables['issuetrackers']} " . $where; + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + $rs = $rs[0]; + $rs['implementation'] = $this->getImplementationForType($rs['type']); + } + return $rs; + } + + /* + * Sanitize and do minor checks + * + * Sanitize Operations + * keys name -> trim will be applied + * type -> intval() wil be applied + * cfg + * + * For strings also db_prepare_string() will be applied + * + * + * Check Operations + * keys name -> if '' => will be set to NULL + * + */ + private function sanitize($obj) + { + $sobj = $obj; + + // remove the standard set of characters considered harmful + // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab + // "\r" - carriage return + // and spaces + // fortunatelly this is trim standard behaviour + $k2san = array( + 'name' + ); + foreach ($k2san as $key) { + $value = trim($obj->$key); + switch ($key) { + case 'name': + $sobj->$key = ($value == '') ? null : $value; + break; + } + + if (! is_null($sobj->$key)) { + $sobj->$key = $this->db->prepare_string($obj->$key); + } + } + + // seems here is better do not touch. + $sobj->cfg = $this->db->prepare_string($obj->cfg); + $sobj->type = intval($obj->type); + + return $sobj; + } + + /* + * + * + */ + public function link($id, $tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($id)) { + return; + } + + // Check if link exist for test project ID, in order to INSERT or UPDATE + $statusQuo = $this->getLinkedTo($tprojectID); + + if (is_null($statusQuo)) { + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['testproject_issuetracker']} " . + " (testproject_id,issuetracker_id) " . " VALUES(" . + intval($tprojectID) . "," . intval($id) . ")"; + } else { + $sql = "/* $debugMsg */ UPDATE {$this->tables['testproject_issuetracker']} " . + " SET issuetracker_id = " . intval($id) . + " WHERE testproject_id = " . intval($tprojectID); + } + $this->db->exec_query($sql); + } + + /* + * + * + */ + public function unlink($id, $tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($id)) { + return; + } + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['testproject_issuetracker']} " . + " WHERE testproject_id = " . intval($tprojectID) . + " AND issuetracker_id = " . intval($id); + $this->db->exec_query($sql); + } + + /* + * + * + */ + public function getLinks($id, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array( + 'opt' => array( + 'getDeadLinks' => false + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + if (is_null($id)) { + return; + } + + $sql = "/* $debugMsg */ " . + " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name " . + " FROM {$this->tables['testproject_issuetracker']} TPIT" . + " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPIT.testproject_id " . + " WHERE TPIT.issuetracker_id = " . intval($id); + + if ($my['opt']['getDeadLinks']) { + $sql .= ' AND NHTPR.id IS NULL AND NHTPR.name IS NULL '; + } + + return $this->db->fetchRowsIntoMap($sql, 'testproject_id'); + } + + /* + * + * + */ + public function getLinkSet() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = "/* $debugMsg */ " . + " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, TPIT.issuetracker_id " . + " FROM {$this->tables['testproject_issuetracker']} TPIT" . + " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPIT.testproject_id "; + + return $this->db->fetchRowsIntoMap($sql, 'testproject_id'); + } + + /* + * + * + */ + public function getAll($options = null) + { + $my['options'] = array( + 'output' => null, + 'orderByField' => 'name', + 'checkEnv' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $add_fields = ''; + if ($my['options']['output'] == 'add_link_count') { + $add_fields = ", 0 AS link_count "; + } + + $orderByClause = is_null($my['options']['orderByField']) ? '' : 'ORDER BY ' . + $my['options']['orderByField']; + + $sql = "/* debugMsg */ SELECT * {$add_fields} "; + $sql .= " FROM {$this->tables['issuetrackers']} {$orderByClause} "; + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + + $lc = null; + if (! is_null($rs)) { + + if ($my['options']['output'] == 'add_link_count') { + $sql = "/* debugMsg */ SELECT COUNT(0) AS lcount, ITD.id"; + $sql .= " FROM {$this->tables['issuetrackers']} ITD " . + " JOIN {$this->tables['testproject_issuetracker']} " . + " ON issuetracker_id = ITD.id " . " GROUP BY ITD.id "; + $lc = $this->db->fetchRowsIntoMap($sql, 'id'); + } + + foreach ($rs as &$item) { + $item['verbose'] = $item['name'] . + " ( {$this->types[$item['type']]} )"; + $item['type_descr'] = $this->types[$item['type']]; + $item['env_check_ok'] = true; + $item['env_check_msg'] = ''; + $item['connection_status'] = ''; + + if ($my['options']['checkEnv']) { + $impl = $this->getImplementationForType($item['type']); + $dummy = $impl::checkEnv(); + $item['env_check_ok'] = $dummy['status']; + $item['env_check_msg'] = $dummy['msg']; + } + + if (! is_null($lc) && isset($lc[$item['id']])) { + $item['link_count'] = intval($lc[$item['id']]['lcount']); + } + } + } + return $rs; + } + + /* + * + * + */ + public function getLinkedTo($tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($tprojectID)) { + return; + } + $sql = "/* $debugMsg */ " . + " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, " . + " TPIT.issuetracker_id,ITRK.name AS issuetracker_name, ITRK.type" . + " FROM {$this->tables['testproject_issuetracker']} TPIT" . + " JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPIT.testproject_id " . + " JOIN {$this->tables['issuetrackers']} ITRK " . + " ON ITRK.id = TPIT.issuetracker_id " . + " WHERE TPIT.testproject_id = " . intval($tprojectID); + + $ret = $this->db->get_recordset($sql); + if (! is_null($ret)) { + $ret = $ret[0]; + $ret['verboseType'] = $this->types[$ret['type']]; + $spec = $this->systems[$ret['type']]; + $ret['api'] = $spec['api']; + } + + return $ret; + } + + /** + */ + public function getInterfaceObject($tprojectID) + { + $issueT = $this->getLinkedTo($tprojectID); + $name = $issueT['issuetracker_name']; + $goodForSession = ($issueT['api'] != 'db'); + + if ($goodForSession && isset($_SESSION['its'][$name])) { + return $_SESSION['its'][$name]; + } + + try { + if (! is_null($issueT)) { + $itd = $this->getByID($issueT['issuetracker_id']); + $iname = $itd['implementation']; + + if ($goodForSession) { + $_SESSION['its'][$name] = new $iname($iname, $itd['cfg'], + $itd['name']); + } else { + return new $iname($iname, $itd['cfg'], $itd['name']); + } + } else { + $_SESSION['its'][$name] = null; + } + return $_SESSION['its'][$name]; + } catch (Exception $e) { + echo 'Probably there is some PHP Config issue regarding extension'; + echo $e->getMessage() . '
    ' . $e->getTraceAsString() . '
    '; + } + } + + /* + * + * + */ + public function checkConnection($its) + { + $xx = $this->getByID($its); + $class2create = $xx['implementation']; + $its = new $class2create($xx['type'], $xx['cfg'], $xx['name']); + + $op = $its->isConnected(); + + // because I've added simple cache on $_SESSION + // IMHO is better to update cache after this check + $_SESSION['its'][$xx['name']] = $its; + + return $op; + } + + /** + */ + private function checkXMLCfg($xmlString) + { + $signature = 'Source:' . __METHOD__; + $op = array( + 'status_ok' => true, + 'msg' => '' + ); + + $xmlCfg = " " . trim($xmlString); + libxml_use_internal_errors(true); + try { + $cfg = simplexml_load_string($xmlCfg); + if (! $cfg) { + $op['status_ok'] = false; + $op['msg'] = $signature . " - Failure loading XML STRING\n"; + foreach (libxml_get_errors() as $error) { + $op['msg'] .= "\t" . $error->message; + } + } + } catch (Exception $e) { + $op['status_ok'] = false; + $op['msg'] = $signature . " - Exception loading XML STRING\n" . + 'Message: ' . $e->getMessage(); + } + + return $op; + } +} diff --git a/lib/functions/tlKeyword.class.php b/lib/functions/tlKeyword.class.php index 72ddcbe2a9..d94f4d538e 100644 --- a/lib/functions/tlKeyword.class.php +++ b/lib/functions/tlKeyword.class.php @@ -1,520 +1,588 @@ -name = null; - $this->notes = null; - $this->testprojectID = null; - if (!($options & self::TLOBJ_O_SEARCH_BY_ID)) { - $this->dbID = null; - } - } - - /** - * Class constructor - * - * @param integer $dbID the database identifier of the keywords - */ - function __construct($dbID = null) { - parent::__construct($dbID); - } - - /* - * Class destructor - */ - function __destruct() { - parent::__destruct(); - $this->_clean(); - } - - /* - * error pseudo verbose - */ - static function getError($code) { - switch($code) { - case self::E_NAMENOTALLOWED: - $v = 'E_NAMENOTALLOWED'; - break; - - case self::E_NAMELENGTH: - $v = 'E_NAMENOTALLOWED'; - break; - - case self::E_NAMEALREADYEXISTS: - $v = 'E_NAMEALREADYEXISTS'; - break; - - case self::E_DBERROR: - $v = 'E_DBERROR'; - break; - - case self::E_WRONGFORMAT: - $v = 'E_WRONGFORMAT'; - break; - } - - return $v; - } - - - - - /** - * Initializes the keyword object - * - * @param integer $dbID the database id of the keyword - * @param integer $testprojectID the id of the testproject the keywords belongs to - * @param string $name the name of the keyword - * @param string $notes the notes for the keywords - */ - function initialize($dbID, $testprojectID,$name,$notes) - { - $this->dbID = $dbID; - $this->name = $name; - $this->notes = $notes; - $this->testprojectID = $testprojectID; - } - - //BEGIN interface iDBSerialization - /* Reads a keyword from the database - * - * @param resource $db [ref] the database connection - * @param integer $options any combination of TLOBJ_O_ Flags - * - * @return integer returns tl::OK on success, tl::ERROR else - */ - public function readFromDB(&$db,$options = self::TLOBJ_O_SEARCH_BY_ID) - { - $this->_clean($options); - - $query = $this->getReadFromDBQuery($this->dbID,$options); - $info = $db->fetchFirstRow($query); - if ($info) - { - $this->readFromDBRow($info); - } - - return $info ? tl::OK : tl::ERROR; - } - - /* Initializes a keyword object, from a single row read by a query obtained by getReadFromDBQuery - * @see lib/functions/iDBBulkReadSerialization#readFromDBRow($row) - * @param $row array map with keys 'id','testproject_id','keyword','notes' - */ - public function readFromDBRow($row) - { - $this->initialize($row['id'],$row['testproject_id'],$row['keyword'],$row['notes']); - } - - /* Returns a query which can be used to read one or multiple keywords from a db - * @param $ids array integer array of db ids (from keywords) - * @param integer $options any combination of TLOBJ_O_ Flags - * @see lib/functions/iDBBulkReadSerialization#getReadFromDBQuery($ids, $options) - */ - public function getReadFromDBQuery($ids,$options = self::TLOBJ_O_SEARCH_BY_ID) - { - $query = " SELECT id,keyword,notes,testproject_id FROM {$this->tables['keywords']} "; - - $clauses = null; - if ($options & self::TLOBJ_O_SEARCH_BY_ID) - { - if (!is_array($ids)) - $clauses[] = "id = {$ids}"; - else - $clauses[] = "id IN (".implode(",",$ids).")"; - } - if ($clauses) - { - $query .= " WHERE " . implode(" AND ",$clauses); - } - - $query .= " ORDER BY keyword ASC "; - - return $query; - } - - /* - * Writes an keyword into the database - * - * @param resource $db [ref] the database connection - * - * @return integer returns tl::OK on success, tl::ERROR else - */ - public function writeToDB(&$db) { - $result = $this->checkKeyword($db); - if ($result >= tl::OK) { - $name = $db->prepare_string($this->name); - $notes = $db->prepare_string($this->notes); - - if ($this->dbID) { - $query = "UPDATE {$this->tables['keywords']} " . - " SET keyword = '{$name}',notes = '{$notes}',testproject_id = {$this->testprojectID}" . - " WHERE id = {$this->dbID}"; - $result = $db->exec_query($query); - } else { - $query = " INSERT INTO {$this->tables['keywords']} (keyword,testproject_id,notes) " . - " VALUES ('" . $name . "'," . $this->testprojectID . ",'" . $notes . "')"; - - $result = $db->exec_query($query); - if ($result) { - $this->dbID = $db->insert_id($this->tables['keywords']); - } - } - $result = $result ? tl::OK : self::E_DBERROR; - } - return $result; - } - - /** - * Check if keyword name is not duplicated - * - * @param resource &$db [ref] database connection - * - * @return integer returns tl::OK on success, error code else - */ - protected function checkKeyword(&$db) { - $this->name = trim($this->name); - $this->notes = trim($this->notes); - - $result = tlKeyword::doesKeywordExist($db,$this->name,$this->testprojectID,$this->dbID); - if ($result >= tl::OK) { - $result = tlKeyword::checkKeywordName($this->name); - } - return $result; - } - - /* - * Deletes a keyword from the database, deletes also the keywords from the testcase_keywords, and object_keywords - * tables - * - * @param resource &$db [ref] database connection - * - * @return integer returns tl::OK on success, tl:ERROR else - */ - public function deleteFromDB(&$db) - { - $sql = "DELETE FROM {$this->tables['testcase_keywords']} WHERE keyword_id = " . $this->dbID; - $result = $db->exec_query($sql); - if ($result) - { - $sql = "DELETE FROM {$this->tables['object_keywords']} WHERE keyword_id = " . $this->dbID; - $result = $db->exec_query($sql); - } - if ($result) - { - $sql = "DELETE FROM {$this->tables['keywords']} WHERE id = " . $this->dbID; - $result = $db->exec_query($sql); - } - return $result ? tl::OK : tl::ERROR; - } - - /** - * create a keyword by a given id - * - * @param resource $db [ref] the database connection - * @param integer $id the databse identifier of the keyword - * @param integer $detailLevel an optional detaillevel, any combination of TLOBJ_O_GET_DETAIL Flags - * - * @return tlKeyword returns the created keyword on success, or null else - */ - static public function getByID(&$db,$id,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return tlDBObject::createObjectFromDB($db,$id,__CLASS__,tlKeyword::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - } - - /** - * create some keywords by given ids - * - * @param resource $db [ref] the database connection - * @param array $ids the database identifiers of the keywords - * @param integer $detailLevel an optional detaillevel, any combination of TLOBJ_O_GET_DETAIL Flags - * - * @return array returns the created keywords (tlKeyword) on success, or null else - */ - static public function getByIDs(&$db,$ids,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return tlDBObject::createObjectsFromDB($db,$ids,__CLASS__,false,$detailLevel); - } - - /** - * currently not implemented - * - * @param resource $db - * @param string $whereClause - * @param string $column - * @param string $orderBy - * @param integer $detailLevel - * @return integer returns tl::E_NOT_IMPLEMENTED - */ - static public function getAll(&$db,$whereClause = null,$column = null,$orderBy = null, - $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return self::handleNotImplementedMethod(__FUNCTION__); - } - - //END interface iDBSerialization - /* - * returns information about the keyword - * - * @return array the keyword information - */ - public function getInfo() { - return array("id" => $this->dbID,"keyword" => $this->name, - "notes" => $this->notes,"testproject_id" => $this->testprojectID); - } - - /** - * Checks a keyword against syntactic rules - * - * @param string $name the name of the keyword which should be checked - * - * @return integer returns tl::OK if the check was sucesssful, else errorcode - **/ - static public function checkKeywordName($name) - { - $result = tl::OK; - if ($name != "") { - //we shouldnt allow " and , in keywords any longer - $dummy = null; - if (preg_match("/(\"|,)/",$name,$dummy)) - $result = self::E_NAMENOTALLOWED; - } else { - $result = self::E_NAMELENGTH; - } - return $result; - } - - /** - * checks if a keyword for a certain testproject already exists in the database - * - * @param resource $db [ref] the database connection - * @param string $name the name of the keyword - * @param integer $tprojectID the testprojectID - * @param integer $kwID an additional keyword id which is excluded in the search - * @return integer return tl::OK if the keyword is found, else tlKeyword::E_NAMEALREADYEXISTS - */ - static public function doesKeywordExist(&$db,$name,$tprojectID,$kwID = null) { - $result = tl::OK; - $tables = tlObjectWithDB::getDBTables("keywords"); - - $name = $db->prepare_string(strtoupper($name)); - $query = " SELECT id FROM {$tables['keywords']} - WHERE UPPER(keyword) ='{$name}' - AND testproject_id = " . $tprojectID ; - - if ($kwID) { - $query .= " AND id <> " .$kwID; - } - - if ($db->fetchFirstRow($query)) { - $result = self::E_NAMEALREADYEXISTS; - } - return $result; - } - - //BEGIN interface iSerializationToXML - - /** - * gets the format descriptor for XML - * - * @return string returns the XML Format description for Keyword/Export - */ - public function getFormatDescriptionForXML() - { - return "Notes"; - } - - /* - * Writes the keyword to XML representation - * - * @param string $xml [ref] the generated XML Code will be appended here - * @param boolean $noHeader set this to true if no XML Header should be generated - */ - public function writeToXML(&$xml,$noHeader = false) - { - $keywords = array($this->getInfo()); - $keywordElemTpl = '" . - ''."\n"; - $keywordInfo = array ("{{NAME}}" => "keyword","||NOTES||" => "notes"); - $xml .= exportDataToXML($keywords,"{{XMLCODE}}",$keywordElemTpl,$keywordInfo,$noHeader); - } - - - /* - */ - public function toXMLString($keywordSet=null,$noHeader = false) - { - $keywords = is_null($keywordSet) ? array($this->getInfo()) : $keywordSet; - $rootElem = "{{XMLCODE}}"; - $elemXMLTemplate = '" . - ''."\n"; - $keywordInfo = array ("{{NAME}}" => "keyword","||NOTES||" => "notes"); - $xml = exportDataToXML($keywords,$rootElem,$elemXMLTemplate,$keywordInfo,$noHeader); - return $xml; - } - - - - /* - * Reads a keyword from a given XML representation - * @param string $xml the XML representation of a keyword - * - * @return returns tl::OK on success, errorcode else - */ - public function readFromXML($xml) - { - $keyword = simplexml_load_string($xml); - return $this->readFromSimpleXML($keyword); - } - - /* - * Reads a keyword from a simpleXML Object - * - * @param object $keyword the SimpleXML Object which hold the keyword information - * - * @return returns tl::OK on success, errorcode else - */ - public function readFromSimpleXML($keyword) - { - $this->name = NULL; - $this->notes = NULL; - - if (!$keyword || $keyword->getName() != 'keyword') - { - return self::E_WRONGFORMAT; - } - - $attributes = $keyword->attributes(); - if (!isset($attributes['name'])) - { - return self::E_WRONGFORMAT; - } - - $this->name = (string)$attributes['name']; - if ($keyword->notes) - { - $this->notes = (string)$keyword->notes[0]; - } - return tl::OK; - } - //END interface iSerializationToXML - - //BEGIN interface iSerializationToCSV - /* - * gets the Format description for the CSV Import/Export Format - * - * @return string the CSV Format - */ - public function getFormatDescriptionForCSV() - { - return "keyword;notes"; - } - - /* Writes a keyword to CSV - * - * @param string $csv the CSV representation of the keyword will be appended here - * @param string $delimiter an optional delimited for the CSV format - */ - public function writeToCSV(&$csv,$delimiter = ';') { - $keyword = array($this->getInfo()); - $sKeys = array( "keyword","notes" ); - $csv .= exportDataToCSV($keyword,$sKeys,$sKeys); - } - - /* reads a keyword from a CSV string - * @param string $csv the csv string for the keyword - * @param string $delimiter an optional delimited for the CSV format - * - * @return integer returns tl::OK on success, tl::ERROR else - */ - public function readFromCSV($csv,$delimiter = ';') { - $data = explode($delimiter,$csv); - - $this->name = isset($data[0]) ? $data[0] : null; - $this->notes = isset($data[1]) ? $data[1] : null; - - return sizeof($data) ? tl::OK : tl::ERROR; - } - //END interface iSerializationToCSV - - - /** - * - */ - static public function getSimpleSet(&$db,$opt=null) - { - $options = array('tproject_id' => 0, 'cols' => '*', - 'accessKey' => null, 'kwSet' => null); - - $options = array_merge($options,(array)$opt); - $tables = tlObjectWithDB::getDBTables("keywords"); - - $sql = " SELECT {$options['cols']} FROM {$tables['keywords']} "; - $where = ' WHERE 1=1 '; - - if( $options['tproject_id'] > 0 ) { - $where .= " AND testproject_id = " . intval($options['tproject_id']); - } - - if( null != $options['kwSet'] ) { - $kwFilter = (array)$options['kwSet']; - $where .= " AND id IN(" . implode(',',$kwFilter) . ")"; - } - - $sql .= $where; - if( is_null($options['accessKey']) ) - { - $rs = $db->get_recordset($sql); - } - else - { - $rs = $db->fetchRowsIntoMap($sql,$options['accessKey']); - } - - return $rs; - } - -} \ No newline at end of file +name = null; + $this->notes = null; + $this->testprojectID = null; + if (! ($options & self::TLOBJ_O_SEARCH_BY_ID)) { + $this->dbID = null; + } + } + + /** + * Class constructor + * + * @param integer $dbID + * the database identifier of the keywords + */ + public function __construct($dbID = null) + { + parent::__construct($dbID); + } + + /* + * Class destructor + */ + public function __destruct() + { + parent::__destruct(); + $this->_clean(); + } + + /* + * error pseudo verbose + */ + public static function getError($code) + { + switch ($code) { + case self::E_NAMENOTALLOWED: + $v = 'E_NAMENOTALLOWED'; + break; + + case self::E_NAMELENGTH: + $v = 'E_NAMENOTALLOWED'; + break; + + case self::E_NAMEALREADYEXISTS: + $v = 'E_NAMEALREADYEXISTS'; + break; + + case self::E_DBERROR: + $v = 'E_DBERROR'; + break; + + case self::E_WRONGFORMAT: + $v = 'E_WRONGFORMAT'; + break; + } + + return $v; + } + + /** + * Initializes the keyword object + * + * @param integer $dbID + * the database id of the keyword + * @param integer $testprojectID + * the id of the testproject the keywords belongs to + * @param string $name + * the name of the keyword + * @param string $notes + * the notes for the keywords + */ + public function initialize($dbID, $testprojectID, $name, $notes) + { + $this->dbID = $dbID; + $this->name = $name; + $this->notes = $notes; + $this->testprojectID = $testprojectID; + } + + // BEGIN interface iDBSerialization + /* + * Reads a keyword from the database + * + * @param resource $db [ref] the database connection + * @param integer $options any combination of TLOBJ_O_ Flags + * + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function readFromDB(&$db, $options = self::TLOBJ_O_SEARCH_BY_ID) + { + $this->_clean($options); + + $query = $this->getReadFromDBQuery($this->dbID, $options); + $info = $db->fetchFirstRow($query); + if ($info) { + $this->readFromDBRow($info); + } + + return $info ? tl::OK : tl::ERROR; + } + + /* + * Initializes a keyword object, from a single row read by a query obtained by getReadFromDBQuery + * @see lib/functions/iDBBulkReadSerialization#readFromDBRow($row) + * @param $row array map with keys 'id','testproject_id','keyword','notes' + */ + public function readFromDBRow($row) + { + $this->initialize($row['id'], $row['testproject_id'], $row['keyword'], + $row['notes']); + } + + /* + * Returns a query which can be used to read one or multiple keywords from a db + * @param $ids array integer array of db ids (from keywords) + * @param integer $options any combination of TLOBJ_O_ Flags + * @see lib/functions/iDBBulkReadSerialization#getReadFromDBQuery($ids, $options) + */ + public function getReadFromDBQuery($ids, + $options = self::TLOBJ_O_SEARCH_BY_ID) + { + $query = " SELECT id,keyword,notes,testproject_id FROM {$this->tables['keywords']} "; + + $clauses = null; + if ($options & self::TLOBJ_O_SEARCH_BY_ID) { + if (! is_array($ids)) { + $clauses[] = "id = {$ids}"; + } else { + $clauses[] = "id IN (" . implode(",", $ids) . ")"; + } + } + if ($clauses) { + $query .= " WHERE " . implode(" AND ", $clauses); + } + + $query .= " ORDER BY keyword ASC "; + + return $query; + } + + /* + * Writes an keyword into the database + * + * @param resource $db [ref] the database connection + * + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function writeToDB(&$db) + { + $result = $this->checkKeyword($db); + if ($result >= tl::OK) { + $name = $db->prepare_string($this->name); + $notes = $db->prepare_string($this->notes); + + if ($this->dbID) { + $query = "UPDATE {$this->tables['keywords']} " . + " SET keyword = '{$name}',notes = '{$notes}',testproject_id = {$this->testprojectID}" . + " WHERE id = {$this->dbID}"; + $result = $db->exec_query($query); + } else { + $query = " INSERT INTO {$this->tables['keywords']} (keyword,testproject_id,notes) " . + " VALUES ('" . $name . "'," . $this->testprojectID . ",'" . + $notes . "')"; + + $result = $db->exec_query($query); + if ($result) { + $this->dbID = $db->insert_id($this->tables['keywords']); + } + } + $result = $result ? tl::OK : self::E_DBERROR; + } + return $result; + } + + /** + * Check if keyword name is not duplicated + * + * @param + * resource &$db [ref] database connection + * + * @return integer returns tl::OK on success, error code else + */ + protected function checkKeyword(&$db) + { + $this->name = trim($this->name); + $this->notes = trim($this->notes); + + $op = tlKeyword::doesKeywordExist($db, $this->name, $this->testprojectID, + $this->dbID); + + $result = $op['status']; + $this->dbID = $op['kwID']; + if ($result >= tl::OK) { + $result = tlKeyword::checkKeywordName($this->name); + } + + return $result; + } + + /* + * Deletes a keyword from the database, deletes also the keywords from the testcase_keywords, and object_keywords + * tables + * + * @param resource &$db [ref] database connection + * + * @return integer returns tl::OK on success, tl:ERROR else + */ + public function deleteFromDB(&$db) + { + $sql = "DELETE FROM {$this->tables['testcase_keywords']} WHERE keyword_id = " . + $this->dbID; + $result = $db->exec_query($sql); + if ($result) { + $sql = "DELETE FROM {$this->tables['object_keywords']} WHERE keyword_id = " . + $this->dbID; + $result = $db->exec_query($sql); + } + if ($result) { + $sql = "DELETE FROM {$this->tables['keywords']} WHERE id = " . + $this->dbID; + $result = $db->exec_query($sql); + } + return $result ? tl::OK : tl::ERROR; + } + + /** + * create a keyword by a given id + * + * @param resource $db + * [ref] the database connection + * @param integer $id + * the databse identifier of the keyword + * @param integer $detailLevel + * an optional detaillevel, any combination of TLOBJ_O_GET_DETAIL Flags + * + * @return tlKeyword returns the created keyword on success, or null else + */ + public static function getByID(&$db, $id, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return tlDBObject::createObjectFromDB($db, $id, __CLASS__, + tlKeyword::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + } + + /** + * create some keywords by given ids + * + * @param resource $db + * [ref] the database connection + * @param array $ids + * the database identifiers of the keywords + * @param integer $detailLevel + * an optional detaillevel, any combination of TLOBJ_O_GET_DETAIL Flags + * + * @return array returns the created keywords (tlKeyword) on success, or null else + */ + public static function getByIDs(&$db, $ids, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return tlDBObject::createObjectsFromDB($db, $ids, __CLASS__, false, + $detailLevel); + } + + /** + * currently not implemented + * + * @param resource $db + * @param string $whereClause + * @param string $column + * @param string $orderBy + * @param integer $detailLevel + * @return integer returns tl::E_NOT_IMPLEMENTED + */ + public static function getAll(&$db, $whereClause = null, $column = null, + $orderBy = null, $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return self::handleNotImplementedMethod(__FUNCTION__); + } + + // END interface iDBSerialization + /* + * returns information about the keyword + * + * @return array the keyword information + */ + public function getInfo() + { + return array( + "id" => $this->dbID, + "keyword" => $this->name, + "notes" => $this->notes, + "testproject_id" => $this->testprojectID + ); + } + + /** + * Checks a keyword against syntactic rules + * + * @param string $name + * the name of the keyword which should be checked + * + * @return integer returns tl::OK if the check was sucesssful, else errorcode + */ + public static function checkKeywordName($name) + { + $result = tl::OK; + if ($name != "") { + // we shouldnt allow " and , in keywords any longer + $dummy = null; + if (preg_match("/(\"|,)/", $name, $dummy)) { + $result = self::E_NAMENOTALLOWED; + } + } else { + $result = self::E_NAMELENGTH; + } + return $result; + } + + /** + * checks if a keyword for a certain testproject already exists in the database + * + * @param resource $db + * [ref] the database connection + * @param string $name + * the name of the keyword + * @param integer $tprojectID + * the testprojectID + * @param integer $kwID + * an additional keyword id which is excluded in the search + * @return integer return tl::OK if the keyword is found, else tlKeyword::E_NAMEALREADYEXISTS + */ + public static function doesKeywordExist(&$db, $name, $tprojectID, + $kwID = null) + { + $op = array( + 'status' => tl::OK, + 'kwID' => $kwID + ); + + $tables = tlObjectWithDB::getDBTables("keywords"); + + $name = $db->prepare_string(strtoupper($name)); + $query = " SELECT id FROM {$tables['keywords']} + WHERE UPPER(keyword) ='{$name}' + AND testproject_id = " . $tprojectID; + + if ($kwID) { + $query .= " AND id <> " . $kwID; + } + + if ($rs = $db->fetchFirstRow($query)) { + $op['status'] = self::E_NAMEALREADYEXISTS; + $op['kwID'] = $rs['id']; + } + return $op; + } + + // BEGIN interface iSerializationToXML + + /** + * gets the format descriptor for XML + * + * @return string returns the XML Format description for Keyword/Export + */ + public function getFormatDescriptionForXML() + { + return "Notes"; + } + + /* + * Writes the keyword to XML representation + * + * @param string $xml [ref] the generated XML Code will be appended here + * @param boolean $noHeader set this to true if no XML Header should be generated + */ + public function writeToXML(&$xml, $noHeader = false) + { + $keywords = array( + $this->getInfo() + ); + $keywordElemTpl = '" . '' . "\n"; + $keywordInfo = array( + "{{NAME}}" => "keyword", + "||NOTES||" => "notes" + ); + $xml .= exportDataToXML($keywords, "{{XMLCODE}}", $keywordElemTpl, + $keywordInfo, $noHeader); + } + + /* + */ + public function toXMLString($keywordSet = null, $noHeader = false) + { + $keywords = is_null($keywordSet) ? array( + $this->getInfo() + ) : $keywordSet; + $rootElem = "{{XMLCODE}}"; + $elemXMLTemplate = '" . '' . "\n"; + $keywordInfo = array( + "{{NAME}}" => "keyword", + "||NOTES||" => "notes" + ); + return exportDataToXML($keywords, $rootElem, $elemXMLTemplate, + $keywordInfo, $noHeader); + } + + /* + * Reads a keyword from a given XML representation + * @param string $xml the XML representation of a keyword + * + * @return returns tl::OK on success, errorcode else + */ + public function readFromXML($xml) + { + $keyword = simplexml_load_string($xml); + return $this->readFromSimpleXML($keyword); + } + + /* + * Reads a keyword from a simpleXML Object + * + * @param object $keyword the SimpleXML Object which hold the keyword information + * + * @return returns tl::OK on success, errorcode else + */ + public function readFromSimpleXML($keyword) + { + $this->name = null; + $this->notes = null; + + if (! $keyword || $keyword->getName() != 'keyword') { + return self::E_WRONGFORMAT; + } + + $attributes = $keyword->attributes(); + if (! isset($attributes['name'])) { + return self::E_WRONGFORMAT; + } + + $this->name = (string) $attributes['name']; + if ($keyword->notes) { + $this->notes = (string) $keyword->notes[0]; + } + return tl::OK; + } + + /* + * gets the Format description for the CSV Import/Export Format + * + * @return string the CSV Format + */ + public function getFormatDescriptionForCSV() + { + return "keyword;notes"; + } + + /* + * Writes a keyword to CSV + * + * @param string $csv the CSV representation of the keyword will be appended here + * @param string $delimiter an optional delimited for the CSV format + */ + public function writeToCSV(&$csv, $delimiter = ';') + { + $keyword = array( + $this->getInfo() + ); + $sKeys = array( + "keyword", + "notes" + ); + $csv .= exportDataToCSV($keyword, $sKeys, $sKeys); + } + + /* + * reads a keyword from a CSV string + * @param string $csv the csv string for the keyword + * @param string $delimiter an optional delimited for the CSV format + * + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function readFromCSV($csv, $delimiter = ';') + { + $data = explode($delimiter, $csv); + + $this->name = isset($data[0]) ? $data[0] : null; + $this->notes = isset($data[1]) ? $data[1] : null; + + return count($data) ? tl::OK : tl::ERROR; + } + + // END interface iSerializationToCSV + + /** + */ + public static function getSimpleSet(&$db, $opt = null) + { + $options = array( + 'tproject_id' => 0, + 'cols' => '*', + 'accessKey' => null, + 'kwSet' => null + ); + + $options = array_merge($options, (array) $opt); + $tables = tlObjectWithDB::getDBTables("keywords"); + + $sql = " SELECT {$options['cols']} FROM {$tables['keywords']} "; + $where = ' WHERE 1=1 '; + + if ($options['tproject_id'] > 0) { + $where .= " AND testproject_id = " . intval($options['tproject_id']); + } + + if (null != $options['kwSet']) { + $kwFilter = (array) $options['kwSet']; + $where .= " AND id IN(" . implode(',', $kwFilter) . ")"; + } + + $sql .= $where; + if (is_null($options['accessKey'])) { + $rs = $db->get_recordset($sql); + } else { + $rs = $db->fetchRowsIntoMap($sql, $options['accessKey']); + } + + return $rs; + } +} diff --git a/lib/functions/tlPlatform.class.php b/lib/functions/tlPlatform.class.php index 7780f2156d..6043ecc574 100644 --- a/lib/functions/tlPlatform.class.php +++ b/lib/functions/tlPlatform.class.php @@ -1,616 +1,734 @@ -tproject_id = $tproject_id; - $this->stdFields = "id, name, notes, testproject_id, - enable_on_design,enable_on_execution"; - } - - /** - * - * - */ - public function setTestProjectID($tproject_id) { - $this->tproject_id = intval($tproject_id); - } - - - /** - * Creates a new platform. - * @return tl::OK on success otherwise E_DBERROR; - */ - public function create($platform) { - - $op = array('status' => self::E_DBERROR, 'id' => -1); - $safeName = $this->throwIfEmptyName($platform->name); - $alreadyExists = $this->getID($safeName); - - if ($alreadyExists) { - $op = array('status' => self::E_NAMEALREADYEXISTS, 'id' => -1); - } else { - $sql = "INSERT INTO {$this->tables['platforms']} - (name, testproject_id, notes, - enable_on_design,enable_on_execution) - VALUES (" . - "'" . $this->db->prepare_string($safeName) . "'" . - "," . $this->tproject_id . - ",'" . $this->db->prepare_string($platform->notes) . "'" . - "," . ($platform->enable_on_design ? 1 : 0) . - "," . ($platform->enable_on_execution ? 1 : 0) . ")"; - $result = $this->db->exec_query($sql); - - if( $result ) { - $op['status'] = tl::OK; - $op['id'] = $this->db->insert_id($this->tables['platforms']); - } - } - return $op; - } - - /** - * Gets info by ID - * - * @return array - */ - public function getByID($id,$opt=null) { - $idSet = implode(',',(array)$id); - $options = array('fields' => $this->stdFields, - 'accessKey' => null); - $options = array_merge($options,(array)$opt); - - $sql = " SELECT {$options['fields']} - FROM {$this->tables['platforms']} - WHERE id IN ($idSet) "; - - switch ($options['accessKey']) { - case 'id': - case 'name': - $accessKey = $options['accessKey']; - break; - - default: - if (count($idSet) == 1) { - return $this->db->fetchFirstRow($sql); - } - $accessKey = 'id'; - break; - } - return $this->db->fetchRowsIntoMap($sql,$accessKey); - } - - - /** - * - */ - public function getByName($name) - { - $val = trim($name); - $sql = " SELECT {$this->stdFields} - FROM {$this->tables['platforms']} - WHERE name = '" . - $this->db->prepare_string($val) . "'" . - " AND testproject_id = " . intval($this->tproject_id); - - $ret = $this->db->fetchFirstRow($sql); - return is_array($ret) ? $ret : null; - } - - - - /** - * Gets all info of a platform - * @return array with keys id, name and notes - * @TODO remove - francisco - */ - public function getPlatform($id) - { - return $this->getByID($id); - } - - /** - * Updates values of a platform in database. - * @param $id the id of the platform to update - * @param $name the new name to be set - * @param $notes new notes to be set - * - * @return tl::OK on success, otherwise E_DBERROR - */ - public function update($id, $name, $notes, $enable_on_design, $enable_on_execution) - { - $safeName = $this->throwIfEmptyName($name); - $sql = " UPDATE {$this->tables['platforms']} " . - " SET name = '" . $this->db->prepare_string($name) . "' " . - ", notes = '". $this->db->prepare_string($notes) . "' " . - ", enable_on_design = ". ($enable_on_design > 0 ? 1 : 0) . - ", enable_on_execution = ". ($enable_on_execution > 0 ? 1 : 0) . - " WHERE id = {$id}"; - - $result = $this->db->exec_query($sql); - return $result ? tl::OK : self::E_DBERROR; - } - - /** - * Removes a platform from the database. - * @TODO: remove all related data to this platform? - * YES! - * @param $id the platform_id to delete - * - * @return tl::OK on success, otherwise E_DBERROR - */ - public function delete($id) - { - $sql = "DELETE FROM {$this->tables['platforms']} WHERE id = {$id}"; - $result = $this->db->exec_query($sql); - - return $result ? tl::OK : self::E_DBERROR; - } - - /** - * links one or more platforms to a testplan - * - * @return tl::OK if successfull otherwise E_DBERROR - */ - public function linkToTestplan($id, $testplan_id) - { - $result = true; - if( !is_null($id) ) - { - $idSet = (array)$id; - foreach ($idSet as $platform_id) - { - $sql = " INSERT INTO {$this->tables['testplan_platforms']} " . - " (testplan_id, platform_id) " . - " VALUES ($testplan_id, $platform_id)"; - $result = $this->db->exec_query($sql); - if(!$result) - { - break; - } - } - } - return $result ? tl::OK : self::E_DBERROR; - } - - /** - * Removes one or more platforms from a testplan - * @TODO: should this also remove testcases and executions? - * - * @return tl::OK if successfull otherwise E_DBERROR - */ - public function unlinkFromTestplan($id,$testplan_id) - { - $result = true; - if( !is_null($id) ) - { - $idSet = (array)$id; - foreach ($idSet as $platform_id) - { - $sql = " DELETE FROM {$this->tables['testplan_platforms']} " . - " WHERE testplan_id = {$testplan_id} " . - " AND platform_id = {$platform_id} "; - - $result = $this->db->exec_query($sql); - if(!$result) - { - break; - } - } - } - return $result ? tl::OK : self::E_DBERROR; - } - - /** - * Gets the id of a platform given by name - * - * @return integer platform_id - */ - public function getID($name) - { - $sql = " SELECT id FROM {$this->tables['platforms']} - WHERE name = '" . $this->db->prepare_string($name) . "'" . - " AND testproject_id = {$this->tproject_id} "; - return $this->db->fetchOneValue($sql); - } - - /** - * get all available platforms on active test project - * - * @options array $options Optional params - * ['include_linked_count'] => adds the number of - * testplans this platform is used in - * - * @return array - * - * @internal revisions - */ - public function getAll($options = null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $default = array('include_linked_count' => false, - 'enable_on_design' => false, - 'enable_on_execution' => true); - $options = array_merge($default, (array)$options); - - $tproject_filter = " WHERE PLAT.testproject_id = {$this->tproject_id} "; - - $filterEnableOn = ""; - $enaSet = array('enable_on_design','enable_on_execution'); - foreach ($enaSet as $ena) { - if (null == $options[$ena]) { - continue; - } - if (is_bool($options[$ena]) || is_int($options[$ena])) { - $filterEnableOn .= " AND $ena = " . ($options[$ena] ? 1 : 0); - } - } - - $sql = " SELECT {$this->stdFields} - FROM {$this->tables['platforms']} PLAT +tproject_id = $tproject_id; + $this->stdFields = "id, name, notes, testproject_id, + enable_on_design,enable_on_execution,is_open"; + } + + /** + */ + public function setTestProjectID($tproject_id) + { + $this->tproject_id = intval($tproject_id); + } + + /** + * Creates a new platform. + * + * @return tl::OK on success otherwise E_DBERROR; + */ + public function create($platform) + { + $op = array( + 'status' => self::E_DBERROR, + 'id' => - 1 + ); + $safeName = $this->throwIfEmptyName($platform->name); + $alreadyExists = $this->getID($safeName); + + if ($alreadyExists) { + $op = array( + 'status' => self::E_NAMEALREADYEXISTS, + 'id' => - 1 + ); + } else { + $sql = "INSERT INTO {$this->tables['platforms']} + (name, testproject_id, notes, + enable_on_design,enable_on_execution,is_open) + VALUES (" . "'" . $this->db->prepare_string($safeName) . "'" . "," . + $this->tproject_id . ",'" . + $this->db->prepare_string($platform->notes) . "'" . "," . + ($platform->enable_on_design ? 1 : 0) . "," . + ($platform->enable_on_execution ? 1 : 0); + + if (property_exists($platform, 'is_open')) { + $sql .= "," . ($platform->is_open ? 1 : 0); + } else { + $sql .= ",1"; + } + $sql .= ")"; + + $result = $this->db->exec_query($sql); + + if ($result) { + $op['status'] = tl::OK; + $op['id'] = $this->db->insert_id($this->tables['platforms']); + } + } + return $op; + } + + /** + * Gets info by ID + * + * @return array + */ + public function getByID($id, $opt = null) + { + $idSet = implode(',', (array) $id); + $options = array( + 'fields' => $this->stdFields, + 'accessKey' => null + ); + $options = array_merge($options, (array) $opt); + + $sql = " SELECT {$options['fields']} + FROM {$this->tables['platforms']} + WHERE id IN ($idSet) "; + + switch ($options['accessKey']) { + case 'id': + case 'name': + $accessKey = $options['accessKey']; + break; + + default: + if (count((array) $id) == 1) { + return $this->db->fetchFirstRow($sql); + } + $accessKey = 'id'; + break; + } + return $this->db->fetchRowsIntoMap($sql, $accessKey); + } + + /** + */ + public function getByName($name) + { + $val = trim($name); + $sql = " SELECT {$this->stdFields} + FROM {$this->tables['platforms']} + WHERE name = '" . $this->db->prepare_string($val) . "'" . + " AND testproject_id = " . intval($this->tproject_id); + + $ret = $this->db->fetchFirstRow($sql); + return is_array($ret) ? $ret : null; + } + + /** + * Gets all info of a platform + * + * @return array with keys id, name and notes + * @todo remove - francisco + */ + public function getPlatform($id) + { + return $this->getByID($id); + } + + /** + * Updates values of a platform in database. + * + * @param int $id + * the + * id of the platform to update + * @param string $name + * the + * new name to be set + * @param string $notes + * new + * notes to be set + * + * @return tl::OK on success, otherwise E_DBERROR + */ + public function update($id, $name, $notes, $enable_on_design = null, + $enable_on_execution = null, $is_open = 1) + { + $this->throwIfEmptyName($name); + $sql = " UPDATE {$this->tables['platforms']} " . " SET name = '" . + $this->db->prepare_string($name) . "' " . ", notes = '" . + $this->db->prepare_string($notes) . "' "; + + /* Optional */ + if (! is_null($enable_on_design)) { + $sql .= ", enable_on_design = " . + ((($enable_on_design > 0) || $enable_on_design) ? 1 : 0); + } + if (! is_null($enable_on_execution)) { + $sql .= ", enable_on_execution = " . + ((($enable_on_execution > 0) || $enable_on_execution) ? 1 : 0); + } + + $sql .= ", is_open = " . ($is_open > 0 ? 1 : 0); + /* ---------------------------- */ + + $sql .= " WHERE id = {$id}"; + + $result = $this->db->exec_query($sql); + return $result ? tl::OK : self::E_DBERROR; + } + + /** + * Removes a platform from the database. + * + * @TODO: remove all related data to this platform? + * YES! + * @param int $id + * the + * platform_id to delete + * + * @return tl::OK on success, otherwise E_DBERROR + */ + public function delete($id) + { + $sql = "DELETE FROM {$this->tables['platforms']} WHERE id = {$id}"; + $result = $this->db->exec_query($sql); + + return $result ? tl::OK : self::E_DBERROR; + } + + /** + * links one or more platforms to a testplan + * + * @return tl::OK if successfull otherwise E_DBERROR + */ + public function linkToTestplan($id, $testplan_id) + { + $result = true; + if (! is_null($id)) { + $idSet = (array) $id; + foreach ($idSet as $platform_id) { + $sql = " INSERT INTO {$this->tables['testplan_platforms']} " . + " (testplan_id, platform_id) " . + " VALUES ($testplan_id, $platform_id)"; + $result = $this->db->exec_query($sql); + if (! $result) { + break; + } + } + } + return $result ? tl::OK : self::E_DBERROR; + } + + /** + * Removes one or more platforms from a testplan + * + * @TODO: should this also remove testcases and executions? + * + * @return tl::OK if successfull otherwise E_DBERROR + */ + public function unlinkFromTestplan($id, $testplan_id) + { + $result = true; + if (! is_null($id)) { + $idSet = (array) $id; + foreach ($idSet as $platform_id) { + $sql = " DELETE FROM {$this->tables['testplan_platforms']} " . + " WHERE testplan_id = {$testplan_id} " . + " AND platform_id = {$platform_id} "; + + $result = $this->db->exec_query($sql); + if (! $result) { + break; + } + } + } + return $result ? tl::OK : self::E_DBERROR; + } + + /** + * Gets the id of a platform given by name + * + * @return integer platform_id + */ + public function getID($name) + { + $sql = " SELECT id FROM {$this->tables['platforms']} + WHERE name = '" . $this->db->prepare_string($name) . "'" . + " AND testproject_id = {$this->tproject_id} "; + return $this->db->fetchOneValue($sql); + } + + /** + * get all available platforms on active test project + * + * @options array $options Optional params + * ['include_linked_count'] => adds the number of + * testplans this platform is used in + * + * @return array + * + * @internal revisions + */ + public function getAll($options = null) + { + $default = array( + 'include_linked_count' => false, + 'enable_on_design' => false, + 'enable_on_execution' => true, + 'is_open' => true + ); + $options = array_merge($default, (array) $options); + + $tproject_filter = " WHERE PLAT.testproject_id = {$this->tproject_id} "; + + $filterEnableOn = ""; + $enaSet = array( + 'enable_on_design', + 'enable_on_execution', + 'is_open' + ); + foreach ($enaSet as $ena) { + if (null == $options[$ena]) { + continue; + } + if (is_bool($options[$ena]) || is_int($options[$ena])) { + $filterEnableOn .= " AND $ena = " . ($options[$ena] ? 1 : 0); + } + } + + $sql = " SELECT {$this->stdFields} + FROM {$this->tables['platforms']} PLAT {$tproject_filter} {$filterEnableOn} - ORDER BY name"; - - $rs = $this->db->get_recordset($sql); - if (!is_null($rs) && $options['include_linked_count']) { - // At least on MS SQL Server 2005 you can not do GROUP BY - // fields of type TEXT - // notes is a TEXT field - // $sql = " SELECT PLAT.id,PLAT.name,PLAT.notes, " . - // " COUNT(TPLAT.testplan_id) AS linked_count " . - // " FROM {$this->tables['platforms']} PLAT " . - // " LEFT JOIN {$this->tables['testplan_platforms']} TPLAT " . - // " ON TPLAT.platform_id = PLAT.id " . $tproject_filter . - // " GROUP BY PLAT.id, PLAT.name, PLAT.notes"; - // - $sql = " SELECT PLAT.id, COUNT(TPLAT.testplan_id) AS linked_count + ORDER BY name"; + + $rs = $this->db->get_recordset($sql); + if (! is_null($rs) && $options['include_linked_count']) { + // At least on MS SQL Server 2005 you can not do GROUP BY + // fields of type TEXT + // notes is a TEXT field + // $sql = " SELECT PLAT.id,PLAT.name,PLAT.notes, " . + // " COUNT(TPLAT.testplan_id) AS linked_count " . + // " FROM {$this->tables['platforms']} PLAT " . + // " LEFT JOIN {$this->tables['testplan_platforms']} TPLAT " . + // " ON TPLAT.platform_id = PLAT.id " . $tproject_filter . + // " GROUP BY PLAT.id, PLAT.name, PLAT.notes"; + // + $sql = " SELECT PLAT.id, COUNT(TPLAT.testplan_id) AS linked_count FROM {$this->tables['platforms']} PLAT - LEFT JOIN {$this->tables['testplan_platforms']} TPLAT + LEFT JOIN {$this->tables['testplan_platforms']} TPLAT ON TPLAT.platform_id = PLAT.id {$tproject_filter} - GROUP BY PLAT.id "; - $figures = $this->db->fetchRowsIntoMap($sql,'id'); - - $loop2do = count($rs); - for ($idx=0; $idx < $loop2do; $idx++) { - $rs[$idx]['linked_count'] = - $figures[$rs[$idx]['id']]['linked_count']; - } - } - - return $rs; - } - - /** - * get all available platforms in the active testproject ($this->tproject_id) - * @param string $orderBy - * @return array Returns - * as array($platform_id => $platform_name) - */ - public function getAllAsMap($opt=null) - { - - $options = array('accessKey' => 'id', - 'output' => 'columns', - 'orderBy' => ' ORDER BY name ', - 'enable_on_design' => true, - 'enable_on_execution' => true); - - $options = array_merge($options,(array)$opt); - $accessKey = $options['accessKey']; - $output = $options['output']; - $orderBy = $options['orderBy']; - - $filterEnableOn = ""; - $enaSet = array('enable_on_design','enable_on_execution'); - foreach ($enaSet as $ena) { - if (is_bool($options[$ena]) || is_int($options[$ena])) { - $filterEnableOn .= " AND $ena = " . ($options[$ena] ? 1 : 0); - } - } - - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ + GROUP BY PLAT.id "; + $figures = $this->db->fetchRowsIntoMap($sql, 'id'); + + $loop2do = count($rs); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $rs[$idx]['linked_count'] = $figures[$rs[$idx]['id']]['linked_count']; + } + } + + return $rs; + } + + /** + * get all available platforms in the active testproject ($this->tproject_id) + * + * @param string $orderBy + * @return array Returns + * as array($platform_id => $platform_name) + */ + public function getAllAsMap($opt = null) + { + $options = array( + 'accessKey' => 'id', + 'output' => 'columns', + 'orderBy' => ' ORDER BY name ', + 'enable_on_design' => true, + 'enable_on_execution' => true, + 'is_open' => true + ); + + $options = array_merge($options, (array) $opt); + $accessKey = $options['accessKey']; + $output = $options['output']; + $orderBy = $options['orderBy']; + + $filterEnableOn = ""; + $enaSet = [ + 'enable_on_design', + 'enable_on_execution', + 'is_open' + ]; + + foreach ($enaSet as $ena) { + if (null == $options[$ena]) { + continue; + } + if (is_bool($options[$ena]) || is_int($options[$ena])) { + $filterEnableOn .= " AND $ena = " . ($options[$ena] ? 1 : 0); + } + } + + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT {$this->stdFields} - FROM {$this->tables['platforms']} - WHERE testproject_id = {$this->tproject_id} + FROM {$this->tables['platforms']} + WHERE testproject_id = {$this->tproject_id} {$filterEnableOn} - {$orderBy}"; - if( $output == 'columns' ) { - $rs = $this->db->fetchColumnsIntoMap($sql, $accessKey, 'name'); - } else { - $rs = $this->db->fetchRowsIntoMap($sql, $accessKey); - } - return $rs; - } - - /** - * Logic to determine if platforms should be visible for a given testplan. - * @return bool true if the testplan has one or more linked platforms; - * otherwise false. - */ - public function platformsActiveForTestplan($testplan_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT COUNT(0) AS num " . - " FROM {$this->tables['testplan_platforms']} " . - " WHERE testplan_id = {$testplan_id}"; - $num_tplans = $this->db->fetchOneValue($sql); - return ($num_tplans > 0); - } - - /** - * @param map $options - * @return array Returns all platforms associated to a given testplan - * - * @internal revision - * 20100705 - franciscom - interface - BUGID 3564 - * - */ - public function getLinkedToTestplan($testplanID, $options = null) - { - // output: - // array => indexed array - // mapAccessByID => map access key: id - // mapAccessByName => map access key: name - $my['options'] = array('outputFormat' => 'array', 'orderBy' => ' ORDER BY name '); - $my['options'] = array_merge($my['options'], (array)$options); - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $rs = null; - $sql = "/* $debugMsg */ + {$orderBy}"; + if ($output == 'columns') { + $rs = $this->db->fetchColumnsIntoMap($sql, $accessKey, 'name'); + } else { + $rs = $this->db->fetchRowsIntoMap($sql, $accessKey); + } + return $rs; + } + + /** + * Logic to determine if platforms should be visible for a given testplan. + * + * @return bool true if the testplan has one or more linked platforms; + * otherwise false. + */ + public function platformsActiveForTestplan($testplan_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT COUNT(0) AS num " . + " FROM {$this->tables['testplan_platforms']} " . + " WHERE testplan_id = {$testplan_id}"; + $num_tplans = $this->db->fetchOneValue($sql); + return $num_tplans > 0; + } + + /** + * + * @param array $options + * @return array Returns all platforms associated to a given testplan + * + * @internal revision + * 20100705 - franciscom - interface - BUGID 3564 + * + */ + public function getLinkedToTestplan($testplanID, $options = null) + { + // output: + // array => indexed array + // mapAccessByID => map access key: id + // mapAccessByName => map access key: name + $my['options'] = array( + 'outputFormat' => 'array', + 'orderBy' => ' ORDER BY name ' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $rs = null; + $sql = "/* $debugMsg */ SELECT P.id, P.name, P.notes, P.enable_on_design, - P.enable_on_execution - FROM {$this->tables['platforms']} P - JOIN {$this->tables['testplan_platforms']} TP - ON P.id = TP.platform_id - WHERE TP.testplan_id = {$testplanID} - {$my['options']['orderBy']}"; - - switch ($my['options']['outputFormat']) { - case 'array': - $rs = $this->db->get_recordset($sql); - break; - - case 'mapAccessByID': - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - break; - - case 'mapAccessByName': - $rs = $this->db->fetchRowsIntoMap($sql,'name'); - break; - } - return $rs; - } - - - /** - * @param string $orderBy - * @return array Returns all platforms associated to a given testplan - * on the form $platform_id => $platform_name - */ - public function getLinkedToTestplanAsMap($testplanID,$opt=null) - { - - $options = array('orderBy' => ' ORDER BY name ', - 'enable_on_design' => false, - 'enable_on_execution' => true); - - $options = array_merge($options,(array)$opt); - - $orderBy = $options['orderBy']; - - $filterEnableOn = ""; - $enaSet = array('enable_on_design','enable_on_execution'); - foreach ($enaSet as $ena) { - if (is_bool($options[$ena]) || is_int($options[$ena])) { - $filterEnableOn .= " AND $ena = " . ($options[$ena] ? 1 : 0); - } - } - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = "/* $debugMsg */ SELECT P.id, P.name " . - " FROM {$this->tables['platforms']} P " . - " JOIN {$this->tables['testplan_platforms']} TP " . - " ON P.id = TP.platform_id " . - " WHERE TP.testplan_id = {$testplanID} - {$filterEnableOn} {$orderBy}"; - - return $this->db->fetchColumnsIntoMap($sql, 'id', 'name'); - } - - - - /** - * @return - * - */ - public function throwIfEmptyName($name) - { - $safeName = trim($name); - if (tlStringLen($safeName) == 0) - { - $msg = "Class: " . __CLASS__ . " - " . "Method: " . __FUNCTION__ ; - $msg .= " Empty name "; - throw new Exception($msg); - } - return $safeName; - } - - - /** - * - * - */ - public function deleteByTestProject($tproject_id) - { - $sql = "DELETE FROM {$this->tables['platforms']} WHERE testproject_id = {$tproject_id}"; - $result = $this->db->exec_query($sql); - - return $result ? tl::OK : self::E_DBERROR; - } - - - /** - * - * @internal revisions - * @since 1.9.4 - */ - public function testProjectCount($opt=null) - { - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . '*/ '; - $my['opt'] = array('range' => 'tproject'); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - - // HINT: COALESCE(COUNT(PLAT.id),0) - // allows to get 0 on platform_qty - // - $sql = $debugMsg . " SELECT COALESCE(COUNT(PLAT.id),0) AS platform_qty, TPROJ.id AS tproject_id " . - " FROM {$this->tables['testprojects']} TPROJ " . - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.testproject_id = TPROJ.id "; - - switch($my['opt']['range']) - { - case 'tproject': - $sql .= " WHERE TPROJ.id = " . $this->tproject_id ; - break; - } - $sql .= " GROUP BY TPROJ.id "; - return ($this->db->fetchRowsIntoMap($sql,'tproject_id')); - } - - public function belongsToTestProject($id,$tproject_id = null) - { - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . '*/ '; - $pid = intval(is_null($tproject_id) ? $this->tproject_id : $tproject_id); - - $sql = " SELECT id FROM {$this->tables['platforms']} " . - " WHERE id = " . intval($id) . " AND testproject_id=" . $pid; - $dummy = $this->db->fetchRowsIntoMap($sql,'id'); - return isset($dummy['id']); - } - - public function isLinkedToTestplan($id,$testplan_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " SELECT platform_id FROM {$this->tables['testplan_platforms']} " . - " WHERE testplan_id = " . intval($testplan_id) . - " AND platform_id = " . intval($id); - $rs = $this->db->fetchRowsIntoMap($sql,'platform_id'); - return !is_null($rs); - } - - /** - * - */ - function initViewGUI( &$userObj ) { - $gaga = new stdClass(); - - $gaga->tproject_id = $this->tproject_id; - - $cfg = getWebEditorCfg('platform'); - $gaga->editorType = $cfg['type']; - $gaga->user_feedback = null; - $gaga->user_feedback = array('type' => 'INFO', 'message' => ''); - - $opx = array('include_linked_count' => true, - 'enable_on_design' => null, - 'enable_on_execution' => null); - $gaga->platforms = $this->getAll($opx); - - /* - echo '
    ';
    -    var_dump($gaga->platforms);
    -    echo '
    '; - */ - - $rx = array('canManage' => 'platform_management', - 'mgt_view_events' => 'mgt_view_events'); - foreach($rx as $prop => $right) { - $gaga->$prop = $userObj->hasRight($this->db->db,$right, - $this->tproject_id); - } - - return $gaga; - } - - /** - * - */ - function enableDesign($id) - { - $sql = "UPDATE {$this->tables['platforms']} + P.enable_on_execution, + P.is_open + FROM {$this->tables['platforms']} P + JOIN {$this->tables['testplan_platforms']} TP + ON P.id = TP.platform_id + WHERE TP.testplan_id = {$testplanID} + {$my['options']['orderBy']}"; + + switch ($my['options']['outputFormat']) { + case 'array': + $rs = $this->db->get_recordset($sql); + break; + + case 'mapAccessByID': + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + break; + + case 'mapAccessByName': + $rs = $this->db->fetchRowsIntoMap($sql, 'name'); + break; + } + return $rs; + } + + /** + * + * @param string $orderBy + * @return array Returns all platforms associated + * to a given testplan + * output format: $id => $name + */ + public function getLinkedToTestplanAsMap($testplanID, $opt = null) + { + // null -> any + $options = array( + 'orderBy' => ' ORDER BY name ', + 'enable_on_design' => null, + 'enable_on_execution' => true + ); + + $options = array_merge($options, (array) $opt); + + $orderBy = $options['orderBy']; + + $filterEnableOn = ""; + $enaSet = array( + 'enable_on_design', + 'enable_on_execution' + ); + foreach ($enaSet as $ena) { + if ($options[$ena] == null) { + // do not filter + continue; + } + + if (is_bool($options[$ena]) || is_int($options[$ena])) { + $filterEnableOn .= " AND $ena = " . ($options[$ena] ? 1 : 0); + } + } + + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $sql = "/* $debugMsg */ SELECT P.id, P.name, P.is_open " . + " FROM {$this->tables['platforms']} P " . + " JOIN {$this->tables['testplan_platforms']} TP " . + " ON P.id = TP.platform_id " . + " WHERE TP.testplan_id = {$testplanID} + {$filterEnableOn} {$orderBy}"; + + $pset = (array) $this->db->fetchRowsIntoMap($sql, 'id'); + $itemSet = []; + foreach ($pset as $pid => $elem) { + $pname = $elem['name']; + if ($elem['is_open'] == 0) { + $pname = "**closed for exec** " . $pname; + } + $itemSet[$pid] = $pname; + } + return $itemSet; + } + + /** + * + * @return + * + */ + public function throwIfEmptyName($name) + { + $safeName = trim($name); + if (tlStringLen($safeName) == 0) { + $msg = "Class: " . __CLASS__ . " - " . "Method: " . __FUNCTION__; + $msg .= " Empty name "; + throw new Exception($msg); + } + return $safeName; + } + + /** + */ + public function deleteByTestProject($tproject_id) + { + $sql = "DELETE FROM {$this->tables['platforms']} WHERE testproject_id = {$tproject_id}"; + $result = $this->db->exec_query($sql); + + return $result ? tl::OK : self::E_DBERROR; + } + + /** + * + * @internal revisions + * @since 1.9.4 + */ + public function testProjectCount($opt = null) + { + $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . + '*/ '; + $my['opt'] = array( + 'range' => 'tproject' + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + // HINT: COALESCE(COUNT(PLAT.id),0) + // allows to get 0 on platform_qty + // + $sql = $debugMsg . + " SELECT COALESCE(COUNT(PLAT.id),0) AS platform_qty, TPROJ.id AS tproject_id " . + " FROM {$this->tables['testprojects']} TPROJ " . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.testproject_id = TPROJ.id "; + + switch ($my['opt']['range']) { + case 'tproject': + $sql .= " WHERE TPROJ.id = " . $this->tproject_id; + break; + } + $sql .= " GROUP BY TPROJ.id "; + return $this->db->fetchRowsIntoMap($sql, 'tproject_id'); + } + + public function belongsToTestProject($id, $tproject_id = null) + { + $pid = intval(is_null($tproject_id) ? $this->tproject_id : $tproject_id); + + $sql = " SELECT id FROM {$this->tables['platforms']} " . " WHERE id = " . + intval($id) . " AND testproject_id=" . $pid; + $dummy = $this->db->fetchRowsIntoMap($sql, 'id'); + return isset($dummy['id']); + } + + public function isLinkedToTestplan($id, $testplan_id) + { + $sql = " SELECT platform_id FROM {$this->tables['testplan_platforms']} " . + " WHERE testplan_id = " . intval($testplan_id) . + " AND platform_id = " . intval($id); + $rs = $this->db->fetchRowsIntoMap($sql, 'platform_id'); + return ! is_null($rs); + } + + /** + */ + public function initViewGUI(&$userObj, &$argsObj) + { + $gaga = new stdClass(); + + // Context needed to avoid use of session + // then you can open multiple TABS!! + $gaga->tproject_id = $this->tproject_id; + $gaga->tplan_id = $argsObj->tplan_id; + + $cfg = getWebEditorCfg('platform'); + $gaga->editorType = $cfg['type']; + $gaga->user_feedback = null; + $gaga->user_feedback = array( + 'type' => 'INFO', + 'message' => '' + ); + + $opx = array( + 'include_linked_count' => true, + 'enable_on_design' => null, + 'enable_on_execution' => null, + 'is_open' => null + ); + $gaga->platforms = $this->getAll($opx); + + $rx = array( + 'canManage' => 'platform_management', + 'mgt_view_events' => 'mgt_view_events' + ); + foreach ($rx as $prop => $right) { + $gaga->$prop = $userObj->hasRight($this->db->db, $right, + $this->tproject_id); + } + + return $gaga; + } + + /** + */ + public function enableDesign($id) + { + $sql = "UPDATE {$this->tables['platforms']} SET enable_on_design = 1 - WHERE id = $id"; - $this->db->exec_query($sql); - } - - /** - * - */ - function disableDesign($id) - { - $sql = "UPDATE {$this->tables['platforms']} + WHERE id = $id"; + $this->db->exec_query($sql); + } + + /** + */ + public function disableDesign($id) + { + $sql = "UPDATE {$this->tables['platforms']} SET enable_on_design = 0 - WHERE id = $id"; - $this->db->exec_query($sql); - } - - - /** - * - */ - function enableExec($id) - { - $sql = "UPDATE {$this->tables['platforms']} + WHERE id = $id"; + $this->db->exec_query($sql); + } + + /** + */ + public function enableExec($id) + { + $sql = "UPDATE {$this->tables['platforms']} SET enable_on_execution = 1 - WHERE id = $id"; - $this->db->exec_query($sql); - } - - /** - * - */ - function disableExec($id) - { - $sql = "UPDATE {$this->tables['platforms']} + WHERE id = $id"; + $this->db->exec_query($sql); + } + + /** + */ + public function disableExec($id) + { + $sql = "UPDATE {$this->tables['platforms']} SET enable_on_execution = 0 - WHERE id = $id"; - $this->db->exec_query($sql); - } - + WHERE id = $id"; + $this->db->exec_query($sql); + } + + /** + */ + public function openForExec($id) + { + $sql = "UPDATE {$this->tables['platforms']} + SET is_open = 1 + WHERE id = $id"; + $this->db->exec_query($sql); + } + + /** + */ + public function closeForExec($id) + { + $sql = "UPDATE {$this->tables['platforms']} + SET is_open = 0 + WHERE id = $id"; + $this->db->exec_query($sql); + } + + private function getAsXMLString($tproject_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $tables = tlObjectWithDB::getDBTables(array( + 'platforms' + )); + $adodbXML = new ADODB_XML("1.0", "UTF-8"); + + $sql = "/* $debugMsg */ + SELECT name,notes,enable_on_design, + enable_on_execution + FROM {$tables['platforms']} PLAT + WHERE PLAT.testproject_id=" . intval($tproject_id); + + $adodbXML->setRootTagName('platforms'); + $adodbXML->setRowTagName('platform'); + $content = $adodbXML->ConvertToXMLString($db->db, $sql); + downloadContentsToFile($content, $filename); + exit(); + } } diff --git a/lib/functions/tlPlugin.class.php b/lib/functions/tlPlugin.class.php index b2e27379ba..42ef33d441 100644 --- a/lib/functions/tlPlugin.class.php +++ b/lib/functions/tlPlugin.class.php @@ -1,117 +1,120 @@ -db = $db; - parent::__construct($this->db); - - $this->basename = $p_basename; - $this->register(); - } - - final public function __init() - { - plugin_config_defaults($this->config()); - plugin_event_hook_many($this->hooks()); - - $this->init(); - } - +db = $db; + parent::__construct($this->db); + + $this->basename = $p_basename; + $this->register(); + } + + final public function __init() + { + plugin_config_defaults($this->config()); + plugin_event_hook_many($this->hooks()); + + $this->init(); + } } diff --git a/lib/functions/tlReqMgrSystem.class.php b/lib/functions/tlReqMgrSystem.class.php index 835e13afa4..53ad7b19a1 100644 --- a/lib/functions/tlReqMgrSystem.class.php +++ b/lib/functions/tlReqMgrSystem.class.php @@ -1,624 +1,591 @@ - array('type' => 'contour', 'api' => 'soap', 'enabled' => true, 'order' => -1)); - var $entitySpec = array('name' => 'string','cfg' => 'string','type' => 'int'); - - /** - * Class constructor - * - * @param resource &$db reference to the database handler - */ - function __construct(&$db) - { - parent::__construct(); - $this->getTypes(); // populate types property - $this->db = &$db; - } - - - - /** - * @return hash - * - * - */ - function getSystems($opt=null) - { - $my = array('options' => null); - $my['options']['status'] = 'enabled'; // enabled,disabled,all - $my['options'] = array_merge($my['options'],(array)$opt); - - switch($my['options']['status']) - { - case 'enabled': - $tval = true; - break; - - case 'disabled': - $tval = false; - break; - - default: - $tval = null; - break; - } - - $ret = array(); - foreach($this->systems as $code => $elem) - { - $idx = 0; - if($tval== null || $elem['enabled'] == $tval) - { - $ret[$code] = $elem; - } - } - return $ret; - } - - /** - * @return hash - * - * - */ - function getTypes() - { - if( is_null($this->types) ) - { - foreach($this->systems as $code => $spec) - { - $this->types[$code] = $spec['type'] . " (Interface: {$spec['api']})"; - } - } - return $this->types; - } - - - /** - * @return - * - * - */ - function getImplementationForType($system) - { - $spec = $this->systems[$system]; - return $spec['type'] . $spec['api'] . 'Interface'; - } - - /** - * @return hash - * - * - */ - function getEntitySpec() - { - return $this->entitySpec; - } - - - /** - * - */ - function create($system) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $ret = array('status_ok' => 0, 'id' => 0, 'msg' => 'name already exists'); - $safeobj = $this->sanitize($system); - - // empty name is not allowed - if( is_null($safeobj->name) ) - { - $ret['msg'] = 'empty name is not allowed'; - return $ret; // >>>---> Bye! - } - - // need to check if name already exist - if( is_null($this->getByName($system->name,array('output' => 'id')) )) - { - $sql = "/* debugMsg */ INSERT INTO {$this->tables['reqmgrsystems']} " . - " (name,cfg,type) " . - " VALUES('" . $safeobj->name . "','" . $safeobj->cfg . "',{$safeobj->type})"; - - if( $this->db->exec_query($sql) ) - { - // at least for Postgres DBMS table name is needed. - $itemID=$this->db->insert_id($this->tables['reqmgrsystems']); - $ret = array('status_ok' => 1, 'id' => $itemID, 'msg' => 'ok'); - } - else - { - $ret = array('status_ok' => 0, 'id' => 0, 'msg' => $this->db->error_msg()); - } - } - - return $ret; - } - - - /** - * - */ - function update($system) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; - $msg = array(); - $msg['duplicate_name'] = "Update can not be done - name %s already exists for id %s"; - $msg['ok'] = "operation OK for id %s"; - - $safeobj = $this->sanitize($system); - $ret = array('status_ok' => 1, 'id' => $system->id, 'msg' => ''); - - - // check for duplicate name - $info = $this->getByName($safeobj->name); - if( !is_null($info) && ($info['id'] != $system->id) ) - { - $ret['status_ok'] = 0; - $ret['msg'] .= sprintf($msg['duplicate_name'], $safeobj->name, $info['id']); - } - - if( $ret['status_ok'] ) - { - $sql = "UPDATE {$this->tables['reqmgrsystems']} " . - " SET name = '" . $safeobj->name. "'," . - " cfg = '" . $safeobj->cfg . "'," . - " type = " . $safeobj->type . - " WHERE id = " . intval($system->id); - $result = $this->db->exec_query($sql); - $ret['msg'] .= sprintf($msg['ok'],$system->id); - - } - return $ret; - - } //function end - - - - /** - * delete can be done ONLY if ID is not linked to test project - */ - function delete($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; - - $msg = array(); - $msg['linked'] = "Failure - id %s is linked to: "; - $msg['tproject_details'] = " testproject '%s' with id %s %s"; - $msg['syntax_error'] = "Syntax failure - id %s seems to be an invalid value"; - $msg['ok'] = "operation OK for id %s"; - - $ret = array('status_ok' => 1, 'id' => $id, 'msg' => $debugMsg); - if(is_null($id) || ($safeID = intval($id)) <= 0) - { - $ret['status_ok'] = 0; - $ret['id'] = $id; - $ret['msg'] .= sprintf($msg['syntax_error'],$id); - return $ret; // >>>-----> Bye! - } - - - // check if ID is linked - $links = $this->getLinks($safeID); - if( is_null($links) ) - { - $sql = " /* $debugMsg */ DELETE FROM {$this->tables['reqmgrsystems']} " . - " WHERE id = " . intval($safeID); - $result = $this->db->exec_query($sql); - $ret['msg'] .= sprintf($msg['ok'],$safeID); - - } - else - { - $ret['status_ok'] = 0; - $dummy = sprintf($msg['linked'],$safeID); - $sep = ' / '; - foreach($links as $item) - { - $dummy .= sprintf($msg['tproject_details'],$item['testproject_name'],$item['testproject_id'],$sep); - } - $ret['msg'] .= rtrim($dummy,$sep); - - } - return $ret; - - } //function end - - - - - - /** - * - */ - function getByID($id, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - return $this->getByAttr(array('key' => 'id', 'value' => $id),$options); - } - - - /** - * - */ - function getByName($name, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - return $this->getByAttr(array('key' => 'name', 'value' => $name),$options); - } - - - /** - * - */ - function getByAttr($attr, $options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['options'] = array('output' => 'full'); - $my['options'] = array_merge($my['options'], (array)$options); - - $sql = "/* debugMsg */ SELECT "; - switch($my['options']['output']) - { - case 'id': - $sql .= " id "; - break; - - case 'full': - default: - $sql .= " * "; - break; - - } - - switch($attr['key']) - { - case 'id': - $where = " WHERE id = " . intval($attr['value']); - break; - - case 'name': - default: - $where = " WHERE name = '" . $this->db->prepare_string($attr['value']) . "'"; - break; - } - - - $sql .= " FROM {$this->tables['reqmgrsystems']} " . $where; - $rs = $this->db->get_recordset($sql); - if( !is_null($rs) ) - { - $rs = $rs[0]; - $rs['implementation'] = $this->getImplementationForType($rs['type']); - } - return $rs; - } - - - - /* - * Sanitize and do minor checks - * - * Sanitize Operations - * keys name -> trim will be applied - * type -> intval() wil be applied - * cfg - * - * For strings also db_prepare_string() will be applied - * - * - * Check Operations - * keys name -> if '' => will be set to NULL - * - */ - function sanitize($obj) - { - $sobj = $obj; - - // remove the standard set of characters considered harmful - // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab - // "\r" - carriage return - // and spaces - // fortunatelly this is trim standard behaviour - $k2san = array('name'); - foreach($k2san as $key) - { - $value = trim($obj->$key); - switch($key) - { - case 'name': - $sobj->$key = ($value == '') ? null : $value; - break; - } - - if( !is_null($sobj->$key) ) - { - $sobj->$key = $this->db->prepare_string($obj->$key); - } - - } - - // seems here is better do not touch. - $sobj->cfg = $this->db->prepare_string($obj->cfg); - $sobj->type = intval($obj->type); - - return $sobj; - } - - - - /* - * - * - */ - function link($id,$tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($id)) - { - return; - } - - // Check if link exist for test project ID, in order to INSERT or UPDATE - $statusQuo = $this->getLinkedTo($tprojectID); - - if( is_null($statusQuo) ) - { - $sql = "/* $debugMsg */ INSERT INTO {$this->tables['testproject_reqmgrsystem']} " . - " (testproject_id,reqmgrsystem_id) " . - " VALUES(" . intval($tprojectID) . "," . intval($id) . ")"; - } - else - { - $sql = "/* $debugMsg */ UPDATE {$this->tables['testproject_reqmgrsystem']} " . - " SET reqmgrsystem_id = " . intval($id) . - " WHERE testproject_id = " . intval($tprojectID); - } - $this->db->exec_query($sql); - } - - - /* - * - * - */ - function unlink($id,$tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($id)) - { - return; - } - $sql = "/* $debugMsg */ DELETE FROM {$this->tables['testproject_reqmgrsystem']} " . - " WHERE testproject_id = " . intval($tprojectID) . - " AND reqmgrsystem_id = " . intval($id); - $this->db->exec_query($sql); - } - - - /* - * - * - */ - function getLinks($id, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array('opt' => array('getDeadLinks' => false)); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - if(is_null($id)) - { - return; - } - - - $sql = "/* $debugMsg */ " . - " SELECT TPMGR.testproject_id, NHTPR.name AS testproject_name " . - " FROM {$this->tables['testproject_reqmgrsystem']} TPMGR" . - " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPMGR.testproject_id " . - " WHERE TPMGR.reqmgrsystem_id = " . intval($id); - - if($my['opt']['getDeadLinks']) - { - $sql .= ' AND NHTPR.id IS NULL AND NHTPR.name IS NULL '; - } - - $ret = $this->db->fetchRowsIntoMap($sql,'testproject_id'); - return $ret; - } - - - - /* - * - * - */ - function getLinkSet() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $sql = "/* $debugMsg */ " . - " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, TPIT.reqmgrsystem_id " . - " FROM {$this->tables['testproject_reqmgrsystem']} TPIT" . - " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPIT.testproject_id "; - - $ret = $this->db->fetchRowsIntoMap($sql,'testproject_id'); - return $ret; - } - - /* - * - * - */ - function getAll($options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $my['options'] = array('output' => null, 'orderByField' => 'name', 'checkEnv' => false); - $my['options'] = array_merge($my['options'], (array)$options); - - $add_fields = ''; - if( $my['options']['output'] == 'add_link_count' ) - { - $add_fields = ", 0 AS link_count "; - } - - $orderByClause = is_null($my['options']['orderByField']) ? '' : 'ORDER BY ' . $my['options']['orderByField']; - - $sql = "/* debugMsg */ SELECT * {$add_fields} "; - $sql .= " FROM {$this->tables['reqmgrsystems']} {$orderByClause} "; - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - - $lc = null; - if( !is_null($rs) ) - { - - if( $my['options']['output'] == 'add_link_count' ) - { - $sql = "/* debugMsg */ SELECT COUNT(0) AS lcount, ITD.id"; - $sql .= " FROM {$this->tables['reqmgrsystems']} ITD " . - " JOIN {$this->tables['testproject_reqmgrsystem']} " . - " ON reqmgrsystem_id = ITD.id " . - " GROUP BY ITD.id "; - $lc = $this->db->fetchRowsIntoMap($sql,'id'); - } - - - foreach($rs as &$item) - { - $item['verbose'] = $item['name'] . " ( {$this->types[$item['type']]} )" ; - $item['type_descr'] = $this->types[$item['type']]; - $item['env_check_ok'] = true; - $item['env_check_msg'] = ''; - $item['connection_status'] = ''; - - if( $my['options']['checkEnv'] ) - { - $impl = $this->getImplementationForType($item['type']); - if( method_exists($impl,'checkEnv') ) - { - $dummy = $impl::checkEnv(); - $item['env_check_ok'] = $dummy['status']; - $item['env_check_msg'] = $dummy['msg']; - } - } - - - if( !is_null($lc) ) - { - if( isset($lc[$item['id']]) ) - { - $item['link_count'] = intval($lc[$item['id']]['lcount']); - } - } - } - } - return $rs; - } - - - /* - * - * - */ - function getLinkedTo($tprojectID) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - if(is_null($tprojectID)) - { - return; - } - $sql = "/* $debugMsg */ " . - " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, " . - " TPIT.reqmgrsystem_id,ITRK.name AS reqmgrsystem_name, ITRK.type" . - " FROM {$this->tables['testproject_reqmgrsystem']} TPIT" . - " JOIN {$this->tables['nodes_hierarchy']} NHTPR " . - " ON NHTPR.id = TPIT.testproject_id " . - " JOIN {$this->tables['reqmgrsystems']} ITRK " . - " ON ITRK.id = TPIT.reqmgrsystem_id " . - " WHERE TPIT.testproject_id = " . intval($tprojectID); - - $ret = $this->db->get_recordset($sql); - if( !is_null($ret) ) - { - $ret = $ret[0]; - $ret['verboseType'] = $this->types[$ret['type']]; - } - - return $ret; - } - - - /* - * - * - */ - function getInterfaceObject($tprojectID) - { - $its = null; - $system = $this->getLinkedTo($tprojectID); - - try - { - if( !is_null($system) ) - { - $itd = $this->getByID($system['reqmgrsystem_id']); - $iname = $itd['implementation']; - $its = new $iname($itd['implementation'],$itd['cfg']); - } - return $its; - } - catch (Exception $e) - { - echo('Probably there is some PHP Config issue regarding extension'); - echo($e->getMessage().'
    '.$e->getTraceAsString().'
    '); - } - } - - /* - * - * - */ - function checkConnection($systemID) - { - $xx = $this->getByID($systemID); - $class2create = $xx['implementation']; - $system = new $class2create($xx['type'],$xx['cfg']); - return $system->isConnected(); - } -} // end class -?> \ No newline at end of file + array( + 'type' => 'contour', + 'api' => 'soap', + 'enabled' => true, + 'order' => - 1 + ) + ); + + private $entitySpec = array( + 'name' => 'string', + 'cfg' => 'string', + 'type' => 'int' + ); + + /** + * Class constructor + * + * @param + * resource &$db reference to the database handler + */ + public function __construct(&$db) + { + parent::__construct(); + $this->getTypes(); // populate types property + $this->db = &$db; + } + + /** + * + * @return array + * + * + */ + private function getSystems($opt = null) + { + $my = array( + 'options' => null + ); + $my['options']['status'] = 'enabled'; // enabled,disabled,all + $my['options'] = array_merge($my['options'], (array) $opt); + + switch ($my['options']['status']) { + case 'enabled': + $tval = true; + break; + + case 'disabled': + $tval = false; + break; + + default: + $tval = null; + break; + } + + $ret = array(); + foreach ($this->systems as $code => $elem) { + if ($tval == null || $elem['enabled'] == $tval) { + $ret[$code] = $elem; + } + } + return $ret; + } + + /** + * + * @return array + * + * + */ + public function getTypes() + { + if (is_null($this->types)) { + foreach ($this->systems as $code => $spec) { + $this->types[$code] = $spec['type'] . + " (Interface: {$spec['api']})"; + } + } + return $this->types; + } + + /** + * + * @return + * + * + */ + public function getImplementationForType($system) + { + $spec = $this->systems[$system]; + return $spec['type'] . $spec['api'] . 'Interface'; + } + + /** + * + * @return array + * + * + */ + public function getEntitySpec() + { + return $this->entitySpec; + } + + /** + */ + public function create($system) + { + $ret = array( + 'status_ok' => 0, + 'id' => 0, + 'msg' => 'name already exists' + ); + $safeobj = $this->sanitize($system); + + // empty name is not allowed + if (is_null($safeobj->name)) { + $ret['msg'] = 'empty name is not allowed'; + return $ret; // >>>---> Bye! + } + + // need to check if name already exist + if (is_null($this->getByName($system->name, array( + 'output' => 'id' + )))) { + $sql = "/* debugMsg */ INSERT INTO {$this->tables['reqmgrsystems']} " . + " (name,cfg,type) " . " VALUES('" . $safeobj->name . "','" . + $safeobj->cfg . "',{$safeobj->type})"; + + if ($this->db->exec_query($sql)) { + // at least for Postgres DBMS table name is needed. + $itemID = $this->db->insert_id($this->tables['reqmgrsystems']); + $ret = array( + 'status_ok' => 1, + 'id' => $itemID, + 'msg' => 'ok' + ); + } else { + $ret = array( + 'status_ok' => 0, + 'id' => 0, + 'msg' => $this->db->error_msg() + ); + } + } + + return $ret; + } + + /** + */ + public function update($system) + { + $msg = array(); + $msg['duplicate_name'] = "Update can not be done - name %s already exists for id %s"; + $msg['ok'] = "operation OK for id %s"; + + $safeobj = $this->sanitize($system); + $ret = array( + 'status_ok' => 1, + 'id' => $system->id, + 'msg' => '' + ); + + // check for duplicate name + $info = $this->getByName($safeobj->name); + if (! is_null($info) && ($info['id'] != $system->id)) { + $ret['status_ok'] = 0; + $ret['msg'] .= sprintf($msg['duplicate_name'], $safeobj->name, + $info['id']); + } + + if ($ret['status_ok']) { + $sql = "UPDATE {$this->tables['reqmgrsystems']} " . " SET name = '" . + $safeobj->name . "'," . " cfg = '" . $safeobj->cfg . "'," . + " type = " . $safeobj->type . " WHERE id = " . + intval($system->id); + $this->db->exec_query($sql); + $ret['msg'] .= sprintf($msg['ok'], $system->id); + } + return $ret; + } + + /** + * delete can be done ONLY if ID is not linked to test project + */ + public function delete($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' - '; + + $msg = array(); + $msg['linked'] = "Failure - id %s is linked to: "; + $msg['tproject_details'] = " testproject '%s' with id %s %s"; + $msg['syntax_error'] = "Syntax failure - id %s seems to be an invalid value"; + $msg['ok'] = "operation OK for id %s"; + + $ret = array( + 'status_ok' => 1, + 'id' => $id, + 'msg' => $debugMsg + ); + if (is_null($id) || ($safeID = intval($id)) <= 0) { + $ret['status_ok'] = 0; + $ret['id'] = $id; + $ret['msg'] .= sprintf($msg['syntax_error'], $id); + return $ret; // >>>-----> Bye! + } + + // check if ID is linked + $links = $this->getLinks($safeID); + if (is_null($links)) { + $sql = " /* $debugMsg */ DELETE FROM {$this->tables['reqmgrsystems']} " . + " WHERE id = " . intval($safeID); + $this->db->exec_query($sql); + $ret['msg'] .= sprintf($msg['ok'], $safeID); + } else { + $ret['status_ok'] = 0; + $dummy = sprintf($msg['linked'], $safeID); + $sep = ' / '; + foreach ($links as $item) { + $dummy .= sprintf($msg['tproject_details'], + $item['testproject_name'], $item['testproject_id'], $sep); + } + $ret['msg'] .= rtrim($dummy, $sep); + } + return $ret; + } + + /** + */ + public function getByID($id, $options = null) + { + return $this->getByAttr(array( + 'key' => 'id', + 'value' => $id + ), $options); + } + + /** + */ + private function getByName($name, $options = null) + { + return $this->getByAttr(array( + 'key' => 'name', + 'value' => $name + ), $options); + } + + /** + */ + private function getByAttr($attr, $options = null) + { + $my['options'] = array( + 'output' => 'full' + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $sql = "/* debugMsg */ SELECT "; + switch ($my['options']['output']) { + case 'id': + $sql .= " id "; + break; + + case 'full': + default: + $sql .= " * "; + break; + } + + switch ($attr['key']) { + case 'id': + $where = " WHERE id = " . intval($attr['value']); + break; + + case 'name': + default: + $where = " WHERE name = '" . + $this->db->prepare_string($attr['value']) . "'"; + break; + } + + $sql .= " FROM {$this->tables['reqmgrsystems']} " . $where; + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + $rs = $rs[0]; + $rs['implementation'] = $this->getImplementationForType($rs['type']); + } + return $rs; + } + + /* + * Sanitize and do minor checks + * + * Sanitize Operations + * keys name -> trim will be applied + * type -> intval() wil be applied + * cfg + * + * For strings also db_prepare_string() will be applied + * + * + * Check Operations + * keys name -> if '' => will be set to NULL + * + */ + private function sanitize($obj) + { + $sobj = $obj; + + // remove the standard set of characters considered harmful + // "\0" - NULL, "\t" - tab, "\n" - new line, "\x0B" - vertical tab + // "\r" - carriage return + // and spaces + // fortunatelly this is trim standard behaviour + $k2san = array( + 'name' + ); + foreach ($k2san as $key) { + $value = trim($obj->$key); + switch ($key) { + case 'name': + $sobj->$key = ($value == '') ? null : $value; + break; + } + + if (! is_null($sobj->$key)) { + $sobj->$key = $this->db->prepare_string($obj->$key); + } + } + + // seems here is better do not touch. + $sobj->cfg = $this->db->prepare_string($obj->cfg); + $sobj->type = intval($obj->type); + + return $sobj; + } + + /* + * + * + */ + public function link($id, $tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($id)) { + return; + } + + // Check if link exist for test project ID, in order to INSERT or UPDATE + $statusQuo = $this->getLinkedTo($tprojectID); + + if (is_null($statusQuo)) { + $sql = "/* $debugMsg */ INSERT INTO {$this->tables['testproject_reqmgrsystem']} " . + " (testproject_id,reqmgrsystem_id) " . " VALUES(" . + intval($tprojectID) . "," . intval($id) . ")"; + } else { + $sql = "/* $debugMsg */ UPDATE {$this->tables['testproject_reqmgrsystem']} " . + " SET reqmgrsystem_id = " . intval($id) . + " WHERE testproject_id = " . intval($tprojectID); + } + $this->db->exec_query($sql); + } + + /* + * + * + */ + public function unlink($id, $tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($id)) { + return; + } + $sql = "/* $debugMsg */ DELETE FROM {$this->tables['testproject_reqmgrsystem']} " . + " WHERE testproject_id = " . intval($tprojectID) . + " AND reqmgrsystem_id = " . intval($id); + $this->db->exec_query($sql); + } + + /* + * + * + */ + public function getLinks($id, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array( + 'opt' => array( + 'getDeadLinks' => false + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + if (is_null($id)) { + return; + } + + $sql = "/* $debugMsg */ " . + " SELECT TPMGR.testproject_id, NHTPR.name AS testproject_name " . + " FROM {$this->tables['testproject_reqmgrsystem']} TPMGR" . + " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPMGR.testproject_id " . + " WHERE TPMGR.reqmgrsystem_id = " . intval($id); + + if ($my['opt']['getDeadLinks']) { + $sql .= ' AND NHTPR.id IS NULL AND NHTPR.name IS NULL '; + } + + return $this->db->fetchRowsIntoMap($sql, 'testproject_id'); + } + + /* + * + * + */ + private function getLinkSet() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $sql = "/* $debugMsg */ " . + " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, TPIT.reqmgrsystem_id " . + " FROM {$this->tables['testproject_reqmgrsystem']} TPIT" . + " LEFT OUTER JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPIT.testproject_id "; + + return $this->db->fetchRowsIntoMap($sql, 'testproject_id'); + } + + /* + * + * + */ + public function getAll($options = null) + { + $my['options'] = array( + 'output' => null, + 'orderByField' => 'name', + 'checkEnv' => false + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $add_fields = ''; + if ($my['options']['output'] == 'add_link_count') { + $add_fields = ", 0 AS link_count "; + } + + $orderByClause = is_null($my['options']['orderByField']) ? '' : 'ORDER BY ' . + $my['options']['orderByField']; + + $sql = "/* debugMsg */ SELECT * {$add_fields} "; + $sql .= " FROM {$this->tables['reqmgrsystems']} {$orderByClause} "; + $rs = $this->db->fetchRowsIntoMap($sql, 'id'); + + $lc = null; + if (! is_null($rs)) { + + if ($my['options']['output'] == 'add_link_count') { + $sql = "/* debugMsg */ SELECT COUNT(0) AS lcount, ITD.id"; + $sql .= " FROM {$this->tables['reqmgrsystems']} ITD " . + " JOIN {$this->tables['testproject_reqmgrsystem']} " . + " ON reqmgrsystem_id = ITD.id " . " GROUP BY ITD.id "; + $lc = $this->db->fetchRowsIntoMap($sql, 'id'); + } + + foreach ($rs as &$item) { + $item['verbose'] = $item['name'] . + " ( {$this->types[$item['type']]} )"; + $item['type_descr'] = $this->types[$item['type']]; + $item['env_check_ok'] = true; + $item['env_check_msg'] = ''; + $item['connection_status'] = ''; + + if ($my['options']['checkEnv']) { + $impl = $this->getImplementationForType($item['type']); + if (method_exists($impl, 'checkEnv')) { + $dummy = $impl::checkEnv(); + $item['env_check_ok'] = $dummy['status']; + $item['env_check_msg'] = $dummy['msg']; + } + } + + if (! is_null($lc) && isset($lc[$item['id']])) { + $item['link_count'] = intval($lc[$item['id']]['lcount']); + } + } + } + return $rs; + } + + /* + * + * + */ + public function getLinkedTo($tprojectID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + if (is_null($tprojectID)) { + return; + } + $sql = "/* $debugMsg */ " . + " SELECT TPIT.testproject_id, NHTPR.name AS testproject_name, " . + " TPIT.reqmgrsystem_id,ITRK.name AS reqmgrsystem_name, ITRK.type" . + " FROM {$this->tables['testproject_reqmgrsystem']} TPIT" . + " JOIN {$this->tables['nodes_hierarchy']} NHTPR " . + " ON NHTPR.id = TPIT.testproject_id " . + " JOIN {$this->tables['reqmgrsystems']} ITRK " . + " ON ITRK.id = TPIT.reqmgrsystem_id " . + " WHERE TPIT.testproject_id = " . intval($tprojectID); + + $ret = $this->db->get_recordset($sql); + if (! is_null($ret)) { + $ret = $ret[0]; + $ret['verboseType'] = $this->types[$ret['type']]; + } + + return $ret; + } + + /* + * + * + */ + public function getInterfaceObject($tprojectID) + { + $its = null; + $system = $this->getLinkedTo($tprojectID); + + try { + if (! is_null($system)) { + $itd = $this->getByID($system['reqmgrsystem_id']); + $iname = $itd['implementation']; + $its = new $iname($itd['implementation'], $itd['cfg']); + } + return $its; + } catch (Exception $e) { + echo 'Probably there is some PHP Config issue regarding extension'; + echo $e->getMessage() . '
    ' . $e->getTraceAsString() . '
    '; + } + } + + /* + * + * + */ + public function checkConnection($systemID) + { + $xx = $this->getByID($systemID); + $class2create = $xx['implementation']; + $system = new $class2create($xx['type'], $xx['cfg']); + return $system->isConnected(); + } +} +?> diff --git a/lib/functions/tlRequirementFilterControl.class.php b/lib/functions/tlRequirementFilterControl.class.php index d960d12f3b..ac9331a787 100644 --- a/lib/functions/tlRequirementFilterControl.class.php +++ b/lib/functions/tlRequirementFilterControl.class.php @@ -1,546 +1,559 @@ - array("POST", tlInputParameter::STRING_N), - 'filter_title' => array("POST", tlInputParameter::STRING_N), - 'filter_status' => array("POST", tlInputParameter::ARRAY_STRING_N), - 'filter_type' => array("POST", tlInputParameter::ARRAY_INT), - 'filter_spec_type' => array("POST", tlInputParameter::ARRAY_INT), - 'filter_coverage' => array("POST", tlInputParameter::INT_N), - 'filter_relation' => array("POST", tlInputParameter::ARRAY_STRING_N), - 'filter_tc_id' => array("POST", tlInputParameter::STRING_N), - 'filter_custom_fields' => null, 'filter_result' => false); - - /** - * 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_refresh_tree_on_action' => - array("POST", tlInputParameter::CB_BOOL)); - - - - /** - * - */ - public function __construct(&$dbHandler) - { - // 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); - $this->req_mgr = new requirement_mgr($this->db); - - // ATTENTION if you do not see it when debugging, can be because - // has been declared as PROTECTED - $this->cfield_mgr = &$this->req_mgr->cfield_mgr; - - // moved here from parent::__constructor() to be certain that - // all required objects has been created - $this->init_filters(); - } - - - public function __destruct() - { - parent::__destruct(); //destroys testproject manager - - // destroy member objects - unset($this->req_mgr); - } - - protected function read_config() - { - // some configuration reading already done in parent class - parent::read_config(); - - // load configuration for requirement filters - $this->configuration = config_get('tree_filter_cfg')->requirements; - - // load req and req spec config (for types, filters, status, ...) - $this->configuration->req_cfg = config_get('req_cfg'); - $this->configuration->req_spec_cfg = config_get('req_spec_cfg'); - - // is choice of advanced filter mode enabled? - $this->filter_mode_choice_enabled = false; - if ($this->configuration->advanced_filter_mode_choice) - { - $this->filter_mode_choice_enabled = true; - } - - return tl::OK; - } - - 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); - } // end of method - - /** - * Initializes the class member array for settings - * according to the data loaded from database and user input. - * Only initializes active settings, for a better performance. - * If no settings are active, the complete panel will be disabled and not be displayed. - */ - protected function init_settings() - { - // $at_least_one_active = false; - - foreach ($this->all_settings as $name => $info) - { - $init_method = "init_$name"; - if (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; - $this->display_req_settings = true; - } - else - { - // is not needed, simply deactivate it by setting it to false in main array - $this->settings[$name] = false; - } - } - - // 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 - - /** - * Initializes the class member array for filters - * according to the data loaded from database and user input. - * Only initializes filters which are still enabled and active, for a better performance. - * If no filters are active at all, the filters panel will be disabled and not displayed. - */ - protected function init_filters() - { - // iterate through all filters and activate the needed ones - if ($this->configuration->show_filters == ENABLED) - { - foreach ($this->all_filters as $name => $info) - { - $init_method = "init_$name"; - if (method_exists($this, $init_method) && $this->configuration->{$name} == ENABLED) - { - $this->$init_method(); - $this->display_req_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; - } - } - } - else - { - $this->display_req_filters = false; - } - } // end of method - - /** - * Returns the filter array with necessary data, - * ready to be processed/used by underlying filter functions in - * requirement tree generator function. - */ - protected function get_active_filters() - { - return $this->active_filters; - } - - /** - * Build the tree menu for generation of JavaScript tree of requirements. - * Depending on user selections in graphical user interface, - * either a completely filtered tree will be built and returned, - * or only the minimal necessary data to "lazy load" the objects in tree by later Ajax calls. - * @param object $gui Reference to GUI object (information will be written to it) - * @return object $tree_menu Tree object for display of JavaScript tree menu. - */ - public function build_tree_menu(&$gui) - { - $tree_menu = null; - $filters = $this->get_active_filters(); - $additional_info = null; - $options = null; - $loader = ''; - $children = "[]"; - - // enable drag and drop - $drag_and_drop = new stdClass(); - $drag_and_drop->enabled = true; - $drag_and_drop->BackEndUrl = $gui->basehref . 'lib/ajax/dragdroprequirementnodes.php'; - $drag_and_drop->useBeforeMoveNode = TRUE; - - if (!$this->testproject_mgr) - { - $this->testproject_mgr = new testproject($this->db); - } - - // when we use filtering, the tree will be statically built, - // otherwise it will be lazy loaded - if ($this->do_filtering) - { - $options = array('for_printing' => NOT_FOR_PRINTING,'exclude_branches' => null); - - $tree_menu = generate_reqspec_tree($this->db, $this->testproject_mgr, - $this->args->testproject_id, - $this->args->testproject_name, - $filters, $options); - - $root_node = $tree_menu->rootnode; - $root_node->name .= " ({$root_node->total_req_count})"; - $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; - $drag_and_drop->enabled = false; - } - else - { - $loader = $gui->basehref . 'lib/ajax/getrequirementnodes.php?mode=reqspec&' . - "root_node={$this->args->testproject_id}"; - - $req_qty = count($this->testproject_mgr->get_all_requirement_ids($this->args->testproject_id)); - - $root_node = new stdClass(); - $root_node->href = "javascript:TPROJECT_REQ_SPEC_MGMT({$this->args->testproject_id})"; - $root_node->id = $this->args->testproject_id; - $root_node->name = $this->args->testproject_name . " ($req_qty)"; - $root_node->testlink_node_type = 'testproject'; - } - - $gui->ajaxTree = new stdClass(); - $gui->ajaxTree->loader = $loader; - $gui->ajaxTree->root_node = $root_node; - $gui->ajaxTree->children = $children; - $gui->ajaxTree->dragDrop = $drag_and_drop; - $gui->ajaxTree->cookiePrefix = 'req_specification_tproject_id_' . $root_node->id . "_" ; - } - - /** - * - */ - 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] = 0; - - // 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 - { - $selection = ($this->configuration->automatic_tree_refresh == ENABLED) ? 1 : 0; - } - - $this->settings[$key]['selected'] = $selection; - $this->settings[$key][$hidden_key] = $selection; - $_SESSION[$key] = $selection; - } - - - /** - * - */ - private function init_filter_doc_id() - { - $key = 'filter_doc_id'; - $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_title() - { - $key = 'filter_title'; - $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_status() { - $key = 'filter_status'; - $selection = $this->args->{$key}; - - // get configured statuses and add "any" string to menu - $items = array(self::ANY => $this->option_strings['any']) + - (array) init_labels($this->configuration->req_cfg->status_labels); - - // BUGID 3852 - if (!$selection || $this->args->reset_filters - || (is_array($selection) && in_array('0', $selection, true))) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, 'items' => $items); - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_type() { - $key = 'filter_type'; - $selection = $this->args->{$key}; - - // get configured types and add "any" string to menu - $items = array(self::ANY => $this->option_strings['any']) + - (array) init_labels($this->configuration->req_cfg->type_labels); - - if (!$selection || $this->args->reset_filters - || (is_array($selection) && in_array(self::ANY, $selection))) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, 'items' => $items); - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_spec_type() { - $key = 'filter_spec_type'; - $selection = $this->args->{$key}; - - // get configured types and add "any" string to menu - $items = array(self::ANY => $this->option_strings['any']) + - (array) init_labels($this->configuration->req_spec_cfg->type_labels); - - if (!$selection || $this->args->reset_filters - || (is_array($selection) && in_array(self::ANY, $selection))) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, 'items' => $items); - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_coverage() { - - $key = 'filter_coverage'; - $this->filters[$key] = false; - $this->active_filters[$key] = null; - - // is coverage management enabled? - if ($this->configuration->req_cfg->expected_coverage_management) { - $selection = $this->args->{$key}; - - if (!$selection || !is_numeric($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_relation() { - - $key = 'filter_relation'; - - // are relations enabled? - if ($this->configuration->req_cfg->relations->enable) { - $selection = $this->args->{$key}; - - if (!$this->req_mgr) { - $this->req_mgr = new requirement_mgr($this->db); - } - - $req_relations = $this->req_mgr->init_relation_type_select(); - - // special case here: - // for equal type relations (where it doesn't matter if we find source or destination) - // we have to remove the source identficator from the array key - foreach ($req_relations['equal_relations'] as $array_key => $old_key) - { - // set new key in array and delete old one - $new_key = (int) str_replace("_source", "", $old_key); - $req_relations['items'][$new_key] = $req_relations['items'][$old_key]; - unset($req_relations['items'][$old_key]); - } - - $items = array(self::ANY => $this->option_strings['any']) + - (array) $req_relations['items']; - - if (!$selection || $this->args->reset_filters || - (is_array($selection) && in_array(self::ANY, $selection))) - { - $selection = null; - } - else - { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, - 'items' => $items); - $this->active_filters[$key] = $selection; - } else { - // not enabled, just nullify - $this->filters[$key] = false; - $this->active_filters[$key] = null; - } - } // end of method - - /** - * - */ - private function init_filter_tc_id() - { - $key = 'filter_tc_id'; - $selection = $this->args->{$key}; - - if (!$this->testproject_mgr) { - $this->testproject_mgr = new testproject($this->db); - } - - $tc_cfg = config_get('testcase_cfg'); - $tc_prefix = $this->testproject_mgr->getTestCasePrefix($this->args->testproject_id); - $tc_prefix .= $tc_cfg->glue_character; - - if (!$selection || $selection == $tc_prefix || $this->args->reset_filters) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection ? $selection : $tc_prefix); - $this->active_filters[$key] = $selection; - } // end of method - - - /** - * - */ - protected function getCustomFields() - { - if (!$this->req_mgr) - { - $this->req_mgr = new requirement_mgr($this->db); - $this->cfield_mgr = &$this->req_mgr->cfield_mgr; - } - - $cfields = $this->req_mgr->get_linked_cfields(null, null, $this->args->testproject_id); - return $cfields; - } - -} // end of class \ No newline at end of file + array( + "POST", + tlInputParameter::STRING_N + ), + 'filter_title' => array( + "POST", + tlInputParameter::STRING_N + ), + 'filter_status' => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + 'filter_type' => array( + "POST", + tlInputParameter::ARRAY_INT + ), + 'filter_spec_type' => array( + "POST", + tlInputParameter::ARRAY_INT + ), + 'filter_coverage' => array( + "POST", + tlInputParameter::INT_N + ), + 'filter_relation' => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + 'filter_tc_id' => array( + "POST", + tlInputParameter::STRING_N + ), + 'filter_custom_fields' => null, + 'filter_result' => false + ); + + /** + * 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_refresh_tree_on_action' => array( + "POST", + tlInputParameter::CB_BOOL + ) + ); + + /** + */ + public function __construct(&$dbHandler) + { + // 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); + $this->req_mgr = new requirement_mgr($this->db); + + // ATTENTION if you do not see it when debugging, can be because + // has been declared as PROTECTED + $this->cfield_mgr = &$this->req_mgr->cfield_mgr; + + // moved here from parent::__constructor() to be certain that + // all required objects has been created + $this->init_filters(); + } + + public function __destruct() + { + parent::__destruct(); // destroys testproject manager + + // destroy member objects + unset($this->req_mgr); + } + + protected function read_config() + { + // some configuration reading already done in parent class + parent::read_config(); + + // load configuration for requirement filters + $this->configuration = config_get('tree_filter_cfg')->requirements; + + // load req and req spec config (for types, filters, status, ...) + $this->configuration->req_cfg = config_get('req_cfg'); + $this->configuration->req_spec_cfg = config_get('req_spec_cfg'); + + // is choice of advanced filter mode enabled? + $this->filter_mode_choice_enabled = false; + if ($this->configuration->advanced_filter_mode_choice) { + $this->filter_mode_choice_enabled = true; + } + + return tl::OK; + } + + 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); + } + + /** + * Initializes the class member array for settings + * according to the data loaded from database and user input. + * Only initializes active settings, for a better performance. + * If no settings are active, the complete panel will be disabled and not be displayed. + */ + protected function init_settings() + { + foreach ($this->all_settings as $name => $info) { + $init_method = "init_$name"; + if (method_exists($this, $init_method)) { + // is valid, configured, exists and therefore can be used, so initialize this setting + $this->$init_method(); + $this->display_req_settings = true; + } else { + // is not needed, simply deactivate it by setting it to false in main array + $this->settings[$name] = false; + } + } + + // 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; + } + } + } + + /** + * Initializes the class member array for filters + * according to the data loaded from database and user input. + * Only initializes filters which are still enabled and active, for a better performance. + * If no filters are active at all, the filters panel will be disabled and not displayed. + */ + protected function init_filters() + { + // iterate through all filters and activate the needed ones + if ($this->configuration->show_filters == ENABLED) { + foreach ($this->all_filters as $name => $info) { + $init_method = "init_$name"; + if (method_exists($this, $init_method) && + $this->configuration->{$name} == ENABLED) { + $this->$init_method(); + $this->display_req_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; + } + } + } else { + $this->display_req_filters = false; + } + } + + /** + * Returns the filter array with necessary data, + * ready to be processed/used by underlying filter functions in + * requirement tree generator function. + */ + protected function get_active_filters() + { + return $this->active_filters; + } + + /** + * Build the tree menu for generation of JavaScript tree of requirements. + * Depending on user selections in graphical user interface, + * either a completely filtered tree will be built and returned, + * or only the minimal necessary data to "lazy load" the objects in tree by later Ajax calls. + * + * @param object $gui + * Reference to GUI object (information will be written to it) + * @return object $tree_menu Tree object for display of JavaScript tree menu. + */ + public function build_tree_menu(&$gui) + { + $tree_menu = null; + $filters = $this->get_active_filters(); + $options = null; + $loader = ''; + $children = "[]"; + + // enable drag and drop + $drag_and_drop = new stdClass(); + $drag_and_drop->enabled = true; + $drag_and_drop->BackEndUrl = $gui->basehref . + 'lib/ajax/dragdroprequirementnodes.php'; + $drag_and_drop->useBeforeMoveNode = true; + + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + + // when we use filtering, the tree will be statically built, + // otherwise it will be lazy loaded + if ($this->do_filtering) { + $options = array( + 'for_printing' => NOT_FOR_PRINTING, + 'exclude_branches' => null + ); + + $tree_menu = generate_reqspec_tree($this->db, $this->testproject_mgr, + $this->args->testproject_id, $this->args->testproject_name, + $filters, $options); + + $root_node = $tree_menu->rootnode; + $root_node->name .= " ({$root_node->total_req_count})"; + $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; + $drag_and_drop->enabled = false; + } else { + $loader = $gui->basehref . + 'lib/ajax/getrequirementnodes.php?mode=reqspec&' . + "root_node={$this->args->testproject_id}"; + + $req_qty = count( + $this->testproject_mgr->get_all_requirement_ids( + $this->args->testproject_id)); + + $root_node = new stdClass(); + $root_node->href = "javascript:TPROJECT_REQ_SPEC_MGMT({$this->args->testproject_id})"; + $root_node->id = $this->args->testproject_id; + $root_node->name = $this->args->testproject_name . " ($req_qty)"; + $root_node->testlink_node_type = 'testproject'; + } + + $gui->ajaxTree = new stdClass(); + $gui->ajaxTree->loader = $loader; + $gui->ajaxTree->root_node = $root_node; + $gui->ajaxTree->children = $children; + $gui->ajaxTree->dragDrop = $drag_and_drop; + $gui->ajaxTree->cookiePrefix = 'req_specification_tproject_id_' . + $root_node->id . "_"; + } + + /** + */ + public 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] = 0; + + // look where we can find the setting - POST, SESSION, config? + if (isset($this->args->{$key})) { + $selection = 1; + } elseif (isset($this->args->{$hidden_key})) { + $selection = 0; + } elseif (isset($_SESSION[$key])) { + $selection = $_SESSION[$key]; + } else { + $selection = ($this->configuration->automatic_tree_refresh == ENABLED) ? 1 : 0; + } + + $this->settings[$key]['selected'] = $selection; + $this->settings[$key][$hidden_key] = $selection; + $_SESSION[$key] = $selection; + } + + /** + */ + public function init_filter_doc_id() + { + $key = 'filter_doc_id'; + $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; + } + + public function init_filter_title() + { + $key = 'filter_title'; + $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; + } + + public function init_filter_status() + { + $key = 'filter_status'; + $selection = $this->args->{$key}; + + // get configured statuses and add "any" string to menu + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) init_labels($this->configuration->req_cfg->status_labels); + + // BUGID 3852 + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array('0', $selection, true))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_type() + { + $key = 'filter_type'; + $selection = $this->args->{$key}; + + // get configured types and add "any" string to menu + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) init_labels($this->configuration->req_cfg->type_labels); + + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array(self::ANY, $selection))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_spec_type() + { + $key = 'filter_spec_type'; + $selection = $this->args->{$key}; + + // get configured types and add "any" string to menu + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) init_labels($this->configuration->req_spec_cfg->type_labels); + + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array(self::ANY, $selection))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_coverage() + { + $key = 'filter_coverage'; + $this->filters[$key] = false; + $this->active_filters[$key] = null; + + // is coverage management enabled? + if ($this->configuration->req_cfg->expected_coverage_management) { + $selection = $this->args->{$key}; + + if (! $selection || ! is_numeric($selection) || + $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection + ); + $this->active_filters[$key] = $selection; + } + } + + /** + */ + public function init_filter_relation() + { + $key = 'filter_relation'; + + // are relations enabled? + if ($this->configuration->req_cfg->relations->enable) { + $selection = $this->args->{$key}; + + if (! $this->req_mgr) { + $this->req_mgr = new requirement_mgr($this->db); + } + + $req_relations = $this->req_mgr->init_relation_type_select(); + + // special case here: + // for equal type relations (where it doesn't matter if we find source or destination) + // we have to remove the source identficator from the array key + foreach ($req_relations['equal_relations'] as $old_key) { + // set new key in array and delete old one + $new_key = (int) str_replace("_source", "", $old_key); + $req_relations['items'][$new_key] = $req_relations['items'][$old_key]; + unset($req_relations['items'][$old_key]); + } + + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) $req_relations['items']; + + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array(self::ANY, $selection))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } else { + // not enabled, just nullify + $this->filters[$key] = false; + $this->active_filters[$key] = null; + } + } + + /** + */ + public function init_filter_tc_id() + { + $key = 'filter_tc_id'; + $selection = $this->args->{$key}; + + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + + $tc_cfg = config_get('testcase_cfg'); + $tc_prefix = $this->testproject_mgr->getTestCasePrefix( + $this->args->testproject_id); + $tc_prefix .= $tc_cfg->glue_character; + + if (! $selection || $selection == $tc_prefix || + $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection ? $selection : $tc_prefix + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + protected function getCustomFields() + { + if (! $this->req_mgr) { + $this->req_mgr = new requirement_mgr($this->db); + $this->cfield_mgr = &$this->req_mgr->cfield_mgr; + } + + return $this->req_mgr->get_linked_cfields(null, null, + $this->args->testproject_id); + } +} diff --git a/lib/functions/tlRight.class.php b/lib/functions/tlRight.class.php index bb43e1d7d2..601362bf0b 100644 --- a/lib/functions/tlRight.class.php +++ b/lib/functions/tlRight.class.php @@ -1,212 +1,233 @@ -activateCaching = true; - } - - /** - * brings the object to a clean state - * - * @param integer $options any combination of TLOBJ_O_ Flags - */ - protected function _clean($options = self::TLOBJ_O_SEARCH_BY_ID) - { - $this->name = null; - if (!($options & self::TLOBJ_O_SEARCH_BY_ID)) - { - $this->dbID = null; - } - } - - /** - * Magic function, called by PHP whenever a tlRight object should be printed - * - * @return string returns the name of the right - */ - public function __toString() - { - return $this->name; - } - - /** - * Initializes the right object - * - * @param integer $dbID the database id of the right - * @param string $name the name of the right - **/ - function initialize($dbID, $name) - { - $this->dbID = $dbID; - $this->name = $name; - } - - /* Copies a tlRole object from another - * - * @param $role tlRole the role which should be used to initialize this role - * - * @return integer always returns tl::OK - * @see lib/functions/tlDBObject#copyFromCache($object) - */ - public function copyFromCache($right) - { - $this->name = $right->name; - - return tl::OK; - } - /** - * Read a right object from the database - * - * @param resource &$db reference to database handler - * @param interger $option any combination of TLOBJ_O_ flags - * - * @return integer returns tl::OK on success, tl::ERROR else - */ - public function readFromDB(&$db,$options = self::TLOBJ_O_SEARCH_BY_ID) - { - if ($this->readFromCache() >= tl::OK) - { - return tl::OK; - } - - $readSucceeded = tl::ERROR; - $this->_clean($options); - $query = $this->getReadFromDBQuery($this->dbID,$options); - - $info = $db->fetchFirstRow($query); - if ($info) - { - $readSucceeded = $this->readFromDBRow($info); - } - - if ($readSucceeded >= tl::OK) - { - $this->addToCache(); - } - - return $info ? tl::OK : tl::ERROR; - } - - /* Initializes a right object, from a single row read by a query obtained by getReadFromDBQuery - * @see lib/functions/iDBBulkReadSerialization#readFromDBRow($row) - * @param $row array map with keys 'id',description' - */ - public function readFromDBRow($row) - { - $this->initialize($row['id'],$row['description']); - - return tl::OK; - } - - /* Returns a query which can be used to read one or multiple rights from a db - * @param $ids array integer array of db ids (from rights) - * @param integer $options any combination of TLOBJ_O_ Flags - * @see lib/functions/iDBBulkReadSerialization#getReadFromDBQuery($ids, $options) - */ - public function getReadFromDBQuery($ids,$options = self::TLOBJ_O_SEARCH_BY_ID) - { - $tables = tlObject::getDBTables('rights'); - $query = "SELECT id,description FROM {$tables['rights']} "; - - $clauses = null; - if ($options & self::TLOBJ_O_SEARCH_BY_ID) - { - if (!is_array($ids)) - { - $clauses[] = "id = {$ids}"; - } - else - { - $clauses[] = "id IN (".implode(",",$ids).")"; - } - } - - if ($clauses) - { - $query .= " WHERE " . implode(" AND ",$clauses); - } - return $query; - } - - /** - * Get a right by its database id - * - * @param resource &$db reference to database handler - * @param integer $id the database identifier - * @param integer $detailLevel the detail level, any combination TLOBJ_O_GET_DETAIL_ flags - * - * @return tlRight returns the create right or null - */ - static public function getByID(&$db,$id,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return tlDBObject::createObjectFromDB($db,$id,__CLASS__,self::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - } - - /** - * Get multiple rights by their database ids - * - * @param resource &$db reference to database handler - * @param array $ids the database identifier - * @param integer $detailLevel the detail level, any combination TLOBJ_O_GET_DETAIL_ flags - * - * @return tlRight returns the create right or null - */ - static public function getByIDs(&$db,$ids,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return tlDBObject::createObjectsFromDB($db,$ids,__CLASS__,false,$detailLevel); - } - - /** - * @param resource &$db reference to database handler - **/ - static public function getAll(&$db,$whereClause = null,$column = null, - $orderBy = null,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - $tables = tlObject::getDBTables('rights'); - $sql = " SELECT id FROM {$tables['rights']} "; - if (!is_null($whereClause)) - { - $sql .= ' ' . $whereClause; - } - $sql .= is_null($orderBy) ? " ORDER BY id ASC " : $orderBy; - return tlDBObject::createObjectsFromDBbySQL($db,$sql,'id',__CLASS__,true,$detailLevel); - } - - /** - * @param resource &$db reference to database handler - **/ - public function writeToDB(&$db) - { - //@TODO schlundus, now i removed the potentially modified object from the cache - //another optimization could be read the new contents if storing was successfully into the - //cache - $this->removeFromCache(); - return self::handleNotImplementedMethod(__FUNCTION__); - } - - /** - * @param resource &$db reference to database handler - **/ - public function deleteFromDB(&$db) - { - $this->removeFromCache(); - return self::handleNotImplementedMethod(__FUNCTION__); - } -} -?> \ No newline at end of file +activateCaching = true; + } + + /** + * brings the object to a clean state + * + * @param integer $options + * any combination of TLOBJ_O_ Flags + */ + protected function _clean($options = self::TLOBJ_O_SEARCH_BY_ID) + { + $this->name = null; + if (! ($options & self::TLOBJ_O_SEARCH_BY_ID)) { + $this->dbID = null; + } + } + + /** + * Magic function, called by PHP whenever a tlRight object should be printed + * + * @return string returns the name of the right + */ + public function __toString() + { + return $this->name; + } + + /** + * Initializes the right object + * + * @param integer $dbID + * the database id of the right + * @param string $name + * the name of the right + */ + private function initialize($dbID, $name) + { + $this->dbID = $dbID; + $this->name = $name; + } + + /* + * Copies a tlRole object from another + * + * @param $role tlRole the role which should be used to initialize this role + * + * @return integer always returns tl::OK + * @see lib/functions/tlDBObject#copyFromCache($object) + */ + public function copyFromCache($right) + { + $this->name = $right->name; + + return tl::OK; + } + + /** + * Read a right object from the database + * + * @param + * resource &$db reference to database handler + * @param integer $option + * any combination of TLOBJ_O_ flags + * + * @return integer returns tl::OK on success, tl::ERROR else + */ + public function readFromDB(&$db, $options = self::TLOBJ_O_SEARCH_BY_ID) + { + if ($this->readFromCache() >= tl::OK) { + return tl::OK; + } + + $readSucceeded = tl::ERROR; + $this->_clean($options); + $query = $this->getReadFromDBQuery($this->dbID, $options); + + $info = $db->fetchFirstRow($query); + if ($info) { + $readSucceeded = $this->readFromDBRow($info); + } + + if ($readSucceeded >= tl::OK) { + $this->addToCache(); + } + + return $info ? tl::OK : tl::ERROR; + } + + /* + * Initializes a right object, from a single row read by a query obtained by getReadFromDBQuery + * @see lib/functions/iDBBulkReadSerialization#readFromDBRow($row) + * @param $row array map with keys 'id',description' + */ + public function readFromDBRow($row) + { + $this->initialize($row['id'], $row['description']); + + return tl::OK; + } + + /* + * Returns a query which can be used to read one or multiple rights from a db + * @param $ids array integer array of db ids (from rights) + * @param integer $options any combination of TLOBJ_O_ Flags + * @see lib/functions/iDBBulkReadSerialization#getReadFromDBQuery($ids, $options) + */ + public function getReadFromDBQuery($ids, + $options = self::TLOBJ_O_SEARCH_BY_ID) + { + $tables = tlObject::getDBTables('rights'); + $query = "SELECT id,description FROM {$tables['rights']} "; + + $clauses = null; + if ($options & self::TLOBJ_O_SEARCH_BY_ID) { + if (! is_array($ids)) { + $clauses[] = "id = {$ids}"; + } else { + $clauses[] = "id IN (" . implode(",", $ids) . ")"; + } + } + + if ($clauses) { + $query .= " WHERE " . implode(" AND ", $clauses); + } + return $query; + } + + /** + * Get a right by its database id + * + * @param + * resource &$db reference to database handler + * @param integer $id + * the database identifier + * @param integer $detailLevel + * the detail level, any combination TLOBJ_O_GET_DETAIL_ flags + * + * @return tlRight returns the create right or null + */ + public static function getByID(&$db, $id, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return tlDBObject::createObjectFromDB($db, $id, __CLASS__, + self::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + } + + /** + * Get multiple rights by their database ids + * + * @param + * resource &$db reference to database handler + * @param array $ids + * the database identifier + * @param integer $detailLevel + * the detail level, any combination TLOBJ_O_GET_DETAIL_ flags + * + * @return tlRight returns the create right or null + */ + public static function getByIDs(&$db, $ids, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return tlDBObject::createObjectsFromDB($db, $ids, __CLASS__, false, + $detailLevel); + } + + /** + * + * @param + * resource &$db reference to database handler + */ + public static function getAll(&$db, $whereClause = null, $column = null, + $orderBy = null, $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + $tables = tlObject::getDBTables('rights'); + $sql = " SELECT id FROM {$tables['rights']} "; + if (! is_null($whereClause)) { + $sql .= ' ' . $whereClause; + } + $sql .= is_null($orderBy) ? " ORDER BY id ASC " : $orderBy; + return tlDBObject::createObjectsFromDBbySQL($db, $sql, 'id', __CLASS__, + true, $detailLevel); + } + + /** + * + * @param + * resource &$db reference to database handler + */ + public function writeToDB(&$db) + { + // @TODO schlundus, now i removed the potentially modified object from the cache + // another optimization could be read the new contents if storing was successfully into the + // cache + $this->removeFromCache(); + return self::handleNotImplementedMethod(__FUNCTION__); + } + + /** + * + * @param + * resource &$db reference to database handler + */ + public function deleteFromDB(&$db) + { + $this->removeFromCache(); + return self::handleNotImplementedMethod(__FUNCTION__); + } +} +?> diff --git a/lib/functions/tlRole.class.php b/lib/functions/tlRole.class.php index 86a5350b21..31f9d2755d 100644 --- a/lib/functions/tlRole.class.php +++ b/lib/functions/tlRole.class.php @@ -1,548 +1,570 @@ -object_table = $this->tables['roles']; - $this->replacementRoleID = config_get('role_replace_for_deleted_roles'); - $this->activateCaching = true; - } - - /* Used to clean up the tlRole object - * - * @param $options array any combination of TLOBJ_O_SEARCH_BY_ID or ROLE_O_SEARCH_BYNAME - * - * @return integer always returns tl::OK - * - * @see lib/functions/tlObject#_clean() - */ - protected function _clean($options = self::TLOBJ_O_SEARCH_BY_ID) - { - $this->description = null; - $this->rights = null; - if (!($options & self::ROLE_O_SEARCH_BYNAME)) - { - $this->name = null; - } - if (!($options & self::TLOBJ_O_SEARCH_BY_ID)) - { - $this->dbID = null; - } - - return tl::OK; - } - - /* Copies a tlRole object from another - * - * @param $role tlRole the role which should be used to initialize this role - * - * @return integer always returns tl::OK - * @see lib/functions/tlDBObject#copyFromCache($object) - */ - public function copyFromCache($role) - { - $this->description = $role->description; - $this->rights = $role->rights; - $this->name = $role->name; - - return tl::OK; - } - - /* Read a role from the database - * @param $db resource [ref] the database connection - * @param $options integer any combination of TLOBJ_O_ or ROLE_O - Flags - * - * @return integer returns tl::OK on success, tl::ERROR else - * - * @see lib/functions/iDBSerialization#readFromDB($db, $options) - */ - public function readFromDB(&$db,$options = self::TLOBJ_O_SEARCH_BY_ID) - { - if ($this->readFromCache() >= tl::OK) - { - return tl::OK; - } - - $this->_clean($options); - $getFullDetails = ($this->detailLevel & self::TLOBJ_O_GET_DETAIL_RIGHTS); - $sql = "SELECT a.id AS role_id,a.description AS role_desc, a.notes "; - if ($getFullDetails) - { - $sql .= " ,c.id AS right_id,c.description "; - } - - $sql .= " FROM {$this->object_table} a "; - - if ($getFullDetails) - { - $sql .= " LEFT OUTER JOIN {$this->tables['role_rights']} b ON a.id = b.role_id " . - " LEFT OUTER JOIN {$this->tables['rights']} c ON b.right_id = c.id "; - } - - $clauses = null; - if ($options & self::ROLE_O_SEARCH_BYNAME) - { - $clauses[] = "a.description = '".$db->prepare_string($this->name)."'"; - } - - if ($options & self::TLOBJ_O_SEARCH_BY_ID) - { - $clauses[] = "a.id = {$this->dbID}"; - } - - if ($clauses) - { - $sql .= " WHERE " . implode(" AND ",$clauses); - } - - $rightInfo = $db->get_recordset($sql); - if ($rightInfo) - { - $this->dbID = $rightInfo[0]['role_id']; - $this->name = $rightInfo[0]['role_desc']; - $this->description = $rightInfo[0]['notes']; - - if ($getFullDetails) - { - $this->rights = $this->buildRightsArray($rightInfo); - } - } - - $readSucceeded = $rightInfo ? tl::OK : tl::ERROR; - if ($readSucceeded >= tl::OK) - { - $this->addToCache(); - } - - return $readSucceeded; - } - - /** - * @param resource &$db reference to database handler - **/ - public function writeToDB(&$db) { - //@TODO schlundus, now i removed the potentially modified object from the cache - //another optimization could be: read the new contents if storing was successfully into the - //cache - $this->removeFromCache(); - - $result = $this->checkDetails($db); - - if ($result >= tl::OK) { - if ($this->dbID) { - $result = $this->deleteRightsFromDB($db); - if ($result >= tl::OK) { - $sql = "UPDATE {$this->object_table} " . - " SET description = '".$db->prepare_string($this->name)."',". - " notes ='".$db->prepare_string($this->description)."'". - " WHERE id = {$this->dbID}"; - $result = $db->exec_query($sql); - } - } else { - $sql = "INSERT INTO {$this->object_table} (description,notes) " . - " VALUES ('".$db->prepare_string($this->name)."',". - "'" . $db->prepare_string($this->description)."')"; - $result = $db->exec_query($sql); - if($result) { - $this->dbID = $db->insert_id($this->object_table); - } - } - - $result = $result ? tl::OK : self::E_DBERROR; - if ($result >= tl::OK) { - $result = $this->addRightsToDB($db); - } - } - - return $result; - } - - /** - * @param resource &$db reference to database handler - **/ - public function checkDetails(&$db) { - $this->name = trim($this->name); - $this->description = trim($this->description); - - $result = tl::OK; - if (!sizeof($this->rights)) { - $result = self::E_EMPTYROLE; - } - - if ($result >= tl::OK) { - $result = self::checkRoleName($this->name); - } - - if ($result >= tl::OK) { - $result = self::doesRoleExist($db,$this->name,$this->dbID) ? self::E_NAMEALREADYEXISTS : tl::OK; - } - - return $result; - } - - /** - * @param resource &$db reference to database handler - **/ - static public function doesRoleExist(&$db,$name,$id) - { - $role = new tlRole(); - $role->name = $name; - if ($role->readFromDB($db,self::ROLE_O_SEARCH_BYNAME) >= tl::OK && $role->dbID != $id) - { - return $role->dbID; - } - return null; - } - - static public function checkRoleName($name) - { - return is_blank($name) ? self::E_NAMELENGTH : tl::OK; - } - - public function getDisplayName() - { - $displayName = $this->name; - if ($displayName{0} == "<") - { - $roleName = str_replace(" ","_",substr($displayName,1,-1)); - $displayName = "<".lang_get($roleName).">"; - } - return $displayName; - } - - /** - * @param resource &$db reference to database handler - **/ - public function deleteFromDB(&$db) - { - $this->removeFromCache(); - - $result = $this->deleteRightsFromDB($db); - if ($result >= tl::OK) - { - //reset all affected users by replacing the deleted role with configured role - $this->replaceUserRolesWith($db,$this->replacementRoleID); - - $sql = "DELETE FROM {$this->object_table} WHERE id = {$this->dbID}"; - $result = $db->exec_query($sql) ? tl::OK : tl::ERROR; - } - return $result; - } - - /** - * @param resource &$db reference to database handler - **/ - protected function replaceUserRolesWith(&$db,$newRole) - { - $result = true; - $tables = array('users','user_testproject_roles','user_testplan_roles'); - foreach($tables as $table) - { - $sql = "UPDATE {$this->tables[$table]} SET role_id = {$newRole} WHERE role_id = {$this->dbID}"; - $result = $result && ($db->exec_query($sql) ? true : false); - } - return $result ? tl::OK : tl::ERROR; - } - - /** - * Gets all users with a certain global role - * - * @param resource &$db reference to database handler - * @return array assoc map with the user ids as the keys - **/ - public function getUsersWithGlobalRole(&$db,$opt=null) - { - $idSet = $this->getUserIDsWithGlobalRole($db,$opt); - return self::createObjectsFromDB($db,$idSet,"tlUser",true,self::TLOBJ_O_GET_DETAIL_MINIMUM); - } - - /** - * Gets all userids of users with a certain global role @TODO WRITE RIGHT COMMENTS FROM START - * - * @param resource &$db reference to database handler - * @return array of userids - **/ - protected function getUserIDsWithGlobalRole(&$db,$opt=null) - { - $my['opt'] = array('active' => -1); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $sql = "SELECT id FROM {$this->tables['users']} " . - " WHERE role_id = {$this->dbID}"; - - if($my['opt']['active'] != -1) - { - $sql .= ' and active = ' . (intval($my['opt']['active']) > 0 ? 1 : 0); - } - - $idSet = $db->fetchColumnsIntoArray($sql,"id"); - return $idSet; - } - - /** - * Gets all userids of users with a certain testproject role @TODO WRITE RIGHT COMMENTS FROM START - * - * @param resource &$db reference to database handler - * @return array returns array of userids - **/ - protected function getUserIDsWithTestProjectRole(&$db) - { - $sql = "SELECT DISTINCT id FROM {$this->tables['users']} users," . - " {$this->tables['user_testproject_roles']} user_testproject_roles " . - " WHERE users.id = user_testproject_roles.user_id"; - $sql .= " AND user_testproject_roles.role_id = {$this->dbID} "; - $idSet = $db->fetchColumnsIntoArray($sql,"id"); - - return $idSet; - } - - /** - * Gets all userids of users with a certain testplan role @TODO WRITE RIGHT COMMENTS FROM START - * - * @param resource &$db reference to database handler - * @return array returns array of userids - **/ - protected function getUserIDsWithTestPlanRole(&$db) - { - $sql = "SELECT DISTINCT id FROM {$this->tables['users']} users," . - " {$this->tables['user_testplan_roles']} user_testplan_roles " . - " WHERE users.id = user_testplan_roles.user_id"; - $sql .= " AND user_testplan_roles.role_id = {$this->dbID}"; - $idSet = $db->fetchColumnsIntoArray($sql,"id"); - - return $idSet; - } - - - /** - * Gets all users with a certain testproject role - * - * @param resource &$db reference to database handler - * @return array returns assoc map with the userids as the keys - **/ - protected function getUsersWithTestProjectRole(&$db) - { - $idSet = $this->getUserIDsWithTestProjectRole($db); - return self::createObjectsFromDB($db,$idSet,"tlUser",true,self::TLOBJ_O_GET_DETAIL_MINIMUM); - } - - - /** - * Gets all users with a certain testplan role - * - * @param resource &$db reference to database handler - * @return array returns assoc map with the userids as the keys - **/ - protected function getUsersWithTestPlanRole(&$db) - { - $idSet = $this->getUserIDsWithTestPlanRole($db); - return self::createObjectsFromDB($db,$idSet,"tlUser",true,self::TLOBJ_O_GET_DETAIL_MINIMUM); - } - - - /** - * Gets all users which have a certain global,testplan or testproject role - * - * @param resource &$db reference to database handler - * @return array returns assoc map with the userids as the keys - **/ - public function getAllUsersWithRole(&$db) - { - $global_users = $this->getUserIDsWithGlobalRole($db); - $tplan_users = $this->getUserIDsWithTestPlanRole($db); - $tproject_users = $this->getUserIDsWithTestProjectRole($db); - - $affectedUsers = (array)$global_users + (array)$tplan_users + (array)$tproject_users; - $affectedUsers = array_unique($affectedUsers); - return self::createObjectsFromDB($db,$affectedUsers,"tlUser",true,self::TLOBJ_O_GET_DETAIL_MINIMUM); - } - - /* - check if a role has requested right - - @param string $rightName the name of the right to check - - @return bool returns true if present, false else - */ - public function hasRight($rightName) - { - $roleRights = (array)$this->rights; - $rights = array(); - $needle = trim($rightName); - foreach($roleRights as $right) { - if (strcasecmp(trim($right->name),$needle) == 0 ) { - return true; - } - } - return false; - } - - /** - * Delete the rights of a role from the db - * - * @param resource &$db reference to database handler - * @return returns tl::OK on success, tl::ERROR else - */ - protected function deleteRightsFromDB(&$db) - { - $tablename = $this->tables['role_rights']; - $sql = "DELETE FROM {$tablename} WHERE role_id = {$this->dbID}"; - $result = $db->exec_query($sql); - - return $result ? tl::OK : tl::ERROR; - } - - /** - * - * - */ - protected function addRightsToDB(&$db) { - $status_ok = 1; - if ($this->rights) { - foreach($this->rights as $right) { - $rightID = $right->dbID; - $sql = "INSERT INTO {$this->tables['role_rights']} (role_id,right_id) " . - "VALUES ({$this->dbID},{$rightID})"; - - $status_ok = $status_ok && ($db->exec_query($sql) ? 1 : 0); - } - } - return $status_ok ? tl::OK : tl::ERROR; - } - - /** - * - * - */ - protected function readRights(&$db) { - $sql = "SELECT right_id,description FROM {$this->tables['role_rights']} a " . - "JOIN {$this->tables['rights']} b ON a.right_id = b.id " . - "WHERE role_id = {$this->dbID}"; - $rightInfo = $db->get_recordset($sql); - $this->rights = buildRightsArray($rightInfo); - - return tl::OK; - } - - protected function buildRightsArray($rightInfo) - { - $rights = null; - for($i = 0;$i < sizeof($rightInfo);$i++) - { - $id = $rightInfo[$i]; - $right = new tlRight($id['right_id']); - $right->name = $id['description']; - $rights[] = $right; - } - return $rights; - } - - static public function getByID(&$db,$id,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return tlDBObject::createObjectFromDB($db,$id,__CLASS__,self::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - } - - static public function getAll(&$db,$whereClause = null,$column = null, - $orderBy = null,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - $tables = tlObject::getDBTables("roles"); - $sql = "SELECT id FROM {$tables['roles']} "; - if (!is_null($whereClause)) - $sql .= ' '.$whereClause; - $sql .= is_null($orderBy) ? " ORDER BY id ASC " : $orderBy; - - $roles = tlDBObject::createObjectsFromDBbySQL($db,$sql,'id',__CLASS__,true,$detailLevel); - - $inheritedRole = new tlRole(TL_ROLES_INHERITED); - $inheritedRole->name = ""; - $roles[TL_ROLES_INHERITED] = $inheritedRole; - - return $roles; - } - - static public function getByIDs(&$db,$ids,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - return self::handleNotImplementedMethod(__FUNCTION__); - } - - - /** - * get roles present on system and return map with colour associations - * if there is no colour configured for role '' is returned as colour. - * - */ - static public function getRoleColourCfg(&$db) - { - $role_colour = config_get('role_colour'); - $tables = tlObject::getDBTables("roles"); - $sql = "SELECT description FROM {$tables['roles']} "; - $roles = $db->fetchColumnsIntoArray($sql,"description"); - foreach($roles as $description) - { - if(!isset($role_colour[$description])) - { - $role_colour[$description] = ''; - } - } - return $role_colour; - } -} +object_table = $this->tables['roles']; + $this->replacementRoleID = config_get('role_replace_for_deleted_roles'); + $this->activateCaching = true; + } + + /* + * Used to clean up the tlRole object + * + * @param $options array any combination of TLOBJ_O_SEARCH_BY_ID or ROLE_O_SEARCH_BYNAME + * + * @return integer always returns tl::OK + * + * @see lib/functions/tlObject#_clean() + */ + protected function _clean($options = self::TLOBJ_O_SEARCH_BY_ID) + { + $this->description = null; + $this->rights = null; + if (! ($options & self::ROLE_O_SEARCH_BYNAME)) { + $this->name = null; + } + if (! ($options & self::TLOBJ_O_SEARCH_BY_ID)) { + $this->dbID = null; + } + + return tl::OK; + } + + /* + * Copies a tlRole object from another + * + * @param $role tlRole the role which should be used to initialize this role + * + * @return integer always returns tl::OK + * @see lib/functions/tlDBObject#copyFromCache($object) + */ + public function copyFromCache($role) + { + $this->description = $role->description; + $this->rights = $role->rights; + $this->name = $role->name; + + return tl::OK; + } + + /* + * Read a role from the database + * @param $db resource [ref] the database connection + * @param $options integer any combination of TLOBJ_O_ or ROLE_O - Flags + * + * @return integer returns tl::OK on success, tl::ERROR else + * + * @see lib/functions/iDBSerialization#readFromDB($db, $options) + */ + public function readFromDB(&$db, $options = self::TLOBJ_O_SEARCH_BY_ID) + { + if ($this->readFromCache() >= tl::OK) { + return tl::OK; + } + + $this->_clean($options); + $getFullDetails = ($this->detailLevel & self::TLOBJ_O_GET_DETAIL_RIGHTS); + $sql = "SELECT a.id AS role_id,a.description AS role_desc, a.notes "; + if ($getFullDetails) { + $sql .= " ,c.id AS right_id,c.description "; + } + + $sql .= " FROM {$this->object_table} a "; + + if ($getFullDetails) { + $sql .= " LEFT OUTER JOIN {$this->tables['role_rights']} b ON a.id = b.role_id " . + " LEFT OUTER JOIN {$this->tables['rights']} c ON b.right_id = c.id "; + } + + $clauses = null; + if ($options & self::ROLE_O_SEARCH_BYNAME) { + $clauses[] = "a.description = '" . $db->prepare_string($this->name) . + "'"; + } + + if ($options & self::TLOBJ_O_SEARCH_BY_ID) { + $clauses[] = "a.id = {$this->dbID}"; + } + + if ($clauses) { + $sql .= " WHERE " . implode(" AND ", $clauses); + } + + $rightInfo = $db->get_recordset($sql); + if ($rightInfo) { + $this->dbID = $rightInfo[0]['role_id']; + $this->name = $rightInfo[0]['role_desc']; + $this->description = $rightInfo[0]['notes']; + + if ($getFullDetails) { + $this->rights = $this->buildRightsArray($rightInfo); + } + } + + $readSucceeded = $rightInfo ? tl::OK : tl::ERROR; + if ($readSucceeded >= tl::OK) { + $this->addToCache(); + } + + return $readSucceeded; + } + + /** + * + * @param + * resource &$db reference to database handler + */ + public function writeToDB(&$db) + { + // @TODO schlundus, now i removed the potentially modified object from the cache + // another optimization could be: read the new contents if storing was successfully into the + // cache + $this->removeFromCache(); + + $result = $this->checkDetails($db); + + if ($result >= tl::OK) { + if ($this->dbID) { + $result = $this->deleteRightsFromDB($db); + if ($result >= tl::OK) { + $sql = "UPDATE {$this->object_table} " . + " SET description = '" . + $db->prepare_string($this->name) . "'," . " notes ='" . + $db->prepare_string($this->description) . "'" . + " WHERE id = {$this->dbID}"; + $result = $db->exec_query($sql); + } + } else { + $sql = "INSERT INTO {$this->object_table} (description,notes) " . + " VALUES ('" . $db->prepare_string($this->name) . "'," . "'" . + $db->prepare_string($this->description) . "')"; + $result = $db->exec_query($sql); + if ($result) { + $this->dbID = $db->insert_id($this->object_table); + } + } + + $result = $result ? tl::OK : self::E_DBERROR; + if ($result >= tl::OK) { + $result = $this->addRightsToDB($db); + } + } + + return $result; + } + + /** + * + * @param + * resource &$db reference to database handler + */ + public function checkDetails(&$db) + { + $this->name = trim($this->name); + $this->description = trim($this->description); + + $result = tl::OK; + if (! count($this->rights)) { + $result = self::E_EMPTYROLE; + } + + if ($result >= tl::OK) { + $result = self::checkRoleName($this->name); + } + + if ($result >= tl::OK) { + $result = self::doesRoleExist($db, $this->name, $this->dbID) ? self::E_NAMEALREADYEXISTS : tl::OK; + } + + return $result; + } + + /** + * + * @param + * resource &$db reference to database handler + */ + public static function doesRoleExist(&$db, $name, $id) + { + $role = new tlRole(); + $role->name = $name; + if ($role->readFromDB($db, self::ROLE_O_SEARCH_BYNAME) >= tl::OK && + $role->dbID != $id) { + return $role->dbID; + } + return null; + } + + public static function checkRoleName($name) + { + return isBlank($name) ? self::E_NAMELENGTH : tl::OK; + } + + /** + */ + public function getDisplayName() + { + $displayName = $this->name; + if ($displayName[0] == "<") { + $roleName = str_replace(" ", "_", substr($displayName, 1, - 1)); + $displayName = "<" . lang_get($roleName) . ">"; + } + return $displayName; + } + + /** + * + * @param + * resource &$db reference to database handler + */ + public function deleteFromDB(&$db) + { + $this->removeFromCache(); + + $result = $this->deleteRightsFromDB($db); + if ($result >= tl::OK) { + // reset all affected users by replacing the deleted role with configured role + $this->replaceUserRolesWith($db, $this->replacementRoleID); + + $sql = "DELETE FROM {$this->object_table} WHERE id = {$this->dbID}"; + $result = $db->exec_query($sql) ? tl::OK : tl::ERROR; + } + return $result; + } + + /** + * + * @param + * resource &$db reference to database handler + */ + protected function replaceUserRolesWith(&$db, $newRole) + { + $result = true; + $tables = array( + 'users', + 'user_testproject_roles', + 'user_testplan_roles' + ); + foreach ($tables as $table) { + $sql = "UPDATE {$this->tables[$table]} SET role_id = {$newRole} WHERE role_id = {$this->dbID}"; + $result = $result && ($db->exec_query($sql) ? true : false); + } + return $result ? tl::OK : tl::ERROR; + } + + /** + * Gets all users with a certain global role + * + * @param + * resource &$db reference to database handler + * @return array assoc map with the user ids as the keys + */ + public function getUsersWithGlobalRole(&$db, $opt = null) + { + $idSet = $this->getUserIDsWithGlobalRole($db, $opt); + return self::createObjectsFromDB($db, $idSet, "tlUser", true, + self::TLOBJ_O_GET_DETAIL_MINIMUM); + } + + /** + * Gets all userids of users with a certain global role @TODO WRITE RIGHT COMMENTS FROM START + * + * @param + * resource &$db reference to database handler + * @return array of userids + */ + protected function getUserIDsWithGlobalRole(&$db, $opt = null) + { + $my['opt'] = array( + 'active' => - 1 + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = "SELECT id FROM {$this->tables['users']} " . + " WHERE role_id = {$this->dbID}"; + + if ($my['opt']['active'] != - 1) { + $sql .= ' and active = ' . (intval($my['opt']['active']) > 0 ? 1 : 0); + } + + return $db->fetchColumnsIntoArray($sql, "id"); + } + + /** + * Gets all userids of users with a certain testproject role @TODO WRITE RIGHT COMMENTS FROM START + * + * @param + * resource &$db reference to database handler + * @return array returns array of userids + */ + protected function getUserIDsWithTestProjectRole(&$db) + { + $sql = "SELECT DISTINCT id FROM {$this->tables['users']} users," . + " {$this->tables['user_testproject_roles']} user_testproject_roles " . + " WHERE users.id = user_testproject_roles.user_id"; + $sql .= " AND user_testproject_roles.role_id = {$this->dbID} "; + return $db->fetchColumnsIntoArray($sql, "id"); + } + + /** + * Gets all userids of users with a certain testplan role @TODO WRITE RIGHT COMMENTS FROM START + * + * @param + * resource &$db reference to database handler + * @return array returns array of userids + */ + protected function getUserIDsWithTestPlanRole(&$db) + { + $sql = "SELECT DISTINCT id FROM {$this->tables['users']} users," . + " {$this->tables['user_testplan_roles']} user_testplan_roles " . + " WHERE users.id = user_testplan_roles.user_id"; + $sql .= " AND user_testplan_roles.role_id = {$this->dbID}"; + return $db->fetchColumnsIntoArray($sql, "id"); + } + + /** + * Gets all users with a certain testproject role + * + * @param + * resource &$db reference to database handler + * @return array returns assoc map with the userids as the keys + */ + protected function getUsersWithTestProjectRole(&$db) + { + $idSet = $this->getUserIDsWithTestProjectRole($db); + return self::createObjectsFromDB($db, $idSet, "tlUser", true, + self::TLOBJ_O_GET_DETAIL_MINIMUM); + } + + /** + * Gets all users with a certain testplan role + * + * @param + * resource &$db reference to database handler + * @return array returns assoc map with the userids as the keys + */ + protected function getUsersWithTestPlanRole(&$db) + { + $idSet = $this->getUserIDsWithTestPlanRole($db); + return self::createObjectsFromDB($db, $idSet, "tlUser", true, + self::TLOBJ_O_GET_DETAIL_MINIMUM); + } + + /** + * Gets all users which have a certain global,testplan or testproject role + * + * @param + * resource &$db reference to database handler + * @return array returns assoc map with the userids as the keys + */ + public function getAllUsersWithRole(&$db) + { + $global_users = $this->getUserIDsWithGlobalRole($db); + $tplan_users = $this->getUserIDsWithTestPlanRole($db); + $tproject_users = $this->getUserIDsWithTestProjectRole($db); + + $affectedUsers = (array) $global_users + (array) $tplan_users + + (array) $tproject_users; + $affectedUsers = array_unique($affectedUsers); + return self::createObjectsFromDB($db, $affectedUsers, "tlUser", true, + self::TLOBJ_O_GET_DETAIL_MINIMUM); + } + + /* + * check if a role has requested right + * + * @param string $rightName the name of the right to check + * + * @return bool returns true if present, false else + */ + public function hasRight($rightName) + { + $roleRights = (array) $this->rights; + $needle = trim($rightName); + foreach ($roleRights as $right) { + if (strcasecmp(trim($right->name), $needle) == 0) { + return true; + } + } + return false; + } + + /** + * Delete the rights of a role from the db + * + * @param + * resource &$db reference to database handler + * @return int returns tl::OK on success, tl::ERROR else + */ + protected function deleteRightsFromDB(&$db) + { + $tablename = $this->tables['role_rights']; + $sql = "DELETE FROM {$tablename} WHERE role_id = {$this->dbID}"; + $result = $db->exec_query($sql); + + return $result ? tl::OK : tl::ERROR; + } + + /** + */ + protected function addRightsToDB(&$db) + { + $status_ok = 1; + if ($this->rights) { + foreach ($this->rights as $right) { + $rightID = $right->dbID; + $sql = "INSERT INTO {$this->tables['role_rights']} (role_id,right_id) " . + "VALUES ({$this->dbID},{$rightID})"; + + $status_ok = $status_ok && ($db->exec_query($sql) ? 1 : 0); + } + } + return $status_ok ? tl::OK : tl::ERROR; + } + + /** + */ + protected function readRights(&$db) + { + $sql = "SELECT right_id,description FROM {$this->tables['role_rights']} a " . + "JOIN {$this->tables['rights']} b ON a.right_id = b.id " . + "WHERE role_id = {$this->dbID}"; + $rightInfo = $db->get_recordset($sql); + $this->rights = buildRightsArray($rightInfo); + + return tl::OK; + } + + protected function buildRightsArray($rightInfo) + { + $rights = null; + for ($i = 0; $i < count($rightInfo); $i ++) { + $id = $rightInfo[$i]; + $right = new tlRight($id['right_id']); + $right->name = $id['description']; + $rights[] = $right; + } + return $rights; + } + + public static function getByID(&$db, $id, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return tlDBObject::createObjectFromDB($db, $id, __CLASS__, + self::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + } + + public static function getAll(&$db, $whereClause = null, $column = null, + $orderBy = null, $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + $tables = tlObject::getDBTables("roles"); + $sql = "SELECT id FROM {$tables['roles']} "; + if (! is_null($whereClause)) { + $sql .= ' ' . $whereClause; + } + $sql .= is_null($orderBy) ? " ORDER BY id ASC " : $orderBy; + + $roles = tlDBObject::createObjectsFromDBbySQL($db, $sql, 'id', __CLASS__, + true, $detailLevel); + + $inheritedRole = new tlRole(TL_ROLES_INHERITED); + $inheritedRole->name = ""; + $roles[TL_ROLES_INHERITED] = $inheritedRole; + + return $roles; + } + + public static function getByIDs(&$db, $ids, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return self::handleNotImplementedMethod(__FUNCTION__); + } + + /** + * get roles present on system and return map with colour associations + * if there is no colour configured for role '' is returned as colour. + */ + public static function getRoleColourCfg(&$db) + { + $role_colour = config_get('role_colour'); + $tables = tlObject::getDBTables("roles"); + $sql = "SELECT description FROM {$tables['roles']} "; + $roles = $db->fetchColumnsIntoArray($sql, "description"); + foreach ($roles as $description) { + if (! isset($role_colour[$description])) { + $role_colour[$description] = ''; + } + } + return $role_colour; + } +} ?> diff --git a/lib/functions/tlTestCaseFilterByRequirementControl.class.php b/lib/functions/tlTestCaseFilterByRequirementControl.class.php index 76ebd5253e..07dd97d4d3 100644 --- a/lib/functions/tlTestCaseFilterByRequirementControl.class.php +++ b/lib/functions/tlTestCaseFilterByRequirementControl.class.php @@ -1,1751 +1,1750 @@ - add/remove test cases - * - * - * @internal revisions - * @since 1.9.13 - */ - -/* - * -------------------------------------------------------- - * An important note on request-URL too large (BUGID 3516) - * -------------------------------------------------------- - * - * 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 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 tlTestCaseFilterByRequirementControl extends tlFilterControl { - - - public $req_mgr = null; - - /** - * 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 - */ - //public $cfield_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 - */ - - /* MAGIC NUMBERS are related to field size - * filter_tc_id: 0,30 arbitrary - * filter_bugs: 240 = 60 x 4 (60 bug_id size on execution_bugs table) - */ - private $all_filters = array('filter_doc_id' => array("POST", tlInputParameter::STRING_N), - 'filter_title' => array("POST", tlInputParameter::STRING_N), - 'filter_status' => array("POST", tlInputParameter::ARRAY_STRING_N), - 'filter_type' => array("POST", tlInputParameter::ARRAY_INT), - 'filter_spec_type' => array("POST", tlInputParameter::ARRAY_INT), - 'filter_coverage' => array("POST", tlInputParameter::INT_N), - 'filter_relation' => array("POST", tlInputParameter::ARRAY_STRING_N), - 'filter_tc_id' => array("POST", tlInputParameter::STRING_N), - 'filter_custom_fields' => null, 'filter_result' => false); - - - - /** - * 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('plan_add_mode' => array('filter_tc_id', - 'filter_testcase_name', - 'filter_toplevel_testsuite', - 'filter_keywords', - // 'filter_active_inactive', - 'filter_importance', - 'filter_execution_type', - 'filter_workflow_status', - '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("REQUEST", tlInputParameter::INT_N), - 'setting_refresh_tree_on_action' => array("POST", tlInputParameter::CB_BOOL), - 'setting_get_parent_child_relation' => array("POST", tlInputParameter::CB_BOOL), - 'hidden_setting_get_parent_child_relation' => array("POST", tlInputParameter::INT_N), - 'setting_testsgroupby' => array("REQUEST", tlInputParameter::INT_N)); - - /** - * This array is used to map the modes to their available settings. - * @var array - */ - - private $mode_setting_mapping = array('plan_add_mode' => array('setting_testplan', - 'setting_refresh_tree_on_action', - 'setting_get_parent_child_relation', - 'hidden_setting_get_parent_child_relation', - 'setting_testsgroupby')); - - /** - * 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 = 'plan_add_mode'; - - - /** - * Options to be used accordin to $this->mode, to build tree - * @var array - */ - private $treeOpt = array(); - - - /** - * 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 plan_add_mode, depending on usage - */ - public function __construct(&$dbHandler, $mode = 'plan_add_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); - $this->req_mgr = new requirement_mgr($this->db); - $this->cfield_mgr = &$this->req_mgr->cfield_mgr; - - // moved here from parent::__constructor() to be certain that - // all required objects has been created - $this->init_filters(); - - $this->initTreeOptions($this->mode); - - // delete any filter settings that may be left from previous calls in session - // Session data has been designed to provide an unidirectional channel - // between the left pane where tree lives and right pane. - // That's why delete each time our OWN session data. - $this->delete_own_session_data(); - $this->delete_old_session_data(); - - $this->save_session_data(); - - } - - /** - * - * - */ - public function __destruct() - { - parent::__destruct(); //destroys testproject manager - - unset($this->tc_mgr); - unset($this->testplan_mgr); - unset($this->platform_mgr); - unset($this->cfield_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. - * @return bool - */ - 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')->requirements; - - // 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'); - - // load req and req spec config (for types, filters, status, ...) - $this->configuration->req_cfg = config_get('req_cfg'); - $this->configuration->req_spec_cfg = config_get('req_spec_cfg'); - - // is switch filter mode enabled? - $this->filter_mode_choice_enabled = false; - switch( $this->mode ) - { - case 'edit_mode': - break; - - default: - if (isset($this->configuration->advanced_filter_mode_choice) && - $this->configuration->advanced_filter_mode_choice == ENABLED) - { - $this->filter_mode_choice_enabled = true; - } - break; - } - - 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); - } // 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 situations - // the build setting is in plan mode only needed for one feature - if ($this->mode == 'plan_mode' && - ($this->args->feature != 'tc_exec_assignment' && $this->args->feature != 'test_urgency') ) - { - $this->settings['setting_build'] = false; - $this->settings['setting_platform'] = 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() - { - // iterate through all filters and activate the needed ones - if ($this->configuration->show_filters == ENABLED) - { - foreach ($this->all_filters as $name => $info) - { - $init_method = "init_$name"; - if (method_exists($this, $init_method) && $this->configuration->{$name} == ENABLED) - { - $this->$init_method(); - $this->display_req_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; - } - } - - } - else - { - $this->display_req_filters = false; - } - } // 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($value = null) - { - // update active_filters - if (!is_null($value)) { - $this->active_filters['testcases_to_show'] = $value; - } - - // 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)) { - unset($_SESSION[$this->mode][$token]); // too old, delete! - } - } - } - } - - /** - * - * - */ - 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; - - $key2loop = array('setting_build','setting_platform'); - foreach($key2loop as $kiwi) - { - if($this->settings[$kiwi]) - { - $string .= "&{$kiwi}={$this->settings[$kiwi]['selected']}"; - } - - } - if ($this->active_filters['filter_priority'] > 0) - { - $string .= '&filter_priority=' . $this->active_filters['filter_priority']; - } - - - $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']; - } - - - // Need to undertand why for other filters that also are array - // we have choosen to serialize, and here not. - // may be to avoid more refactoring - if ($keyword_list) - { - $string .= '&filter_keywords=' . $keyword_list . - '&filter_keywords_filter_type=' . - $this->active_filters['filter_keywords_filter_type']; - } - - // Using serialization - if ($this->active_filters['filter_assigned_user']) - { - $string .= '&filter_assigned_user='. json_encode($this->active_filters['filter_assigned_user']) . - '&filter_assigned_user_include_unassigned=' . - ($this->active_filters['filter_assigned_user_include_unassigned'] ? '1' : '0'); - } - - if ($this->active_filters['filter_result_result']) - { - $string .= '&filter_result_result=' . json_encode($this->active_filters['filter_result_result']) . - '&filter_result_method=' . $this->active_filters['filter_result_method'] . - '&filter_result_build=' . $this->active_filters['filter_result_build']; - } - - if( !is_null($this->active_filters['filter_bugs'])) - { - $string .= '&' . http_build_query( array('filter_bugs' => $this->active_filters['filter_bugs'])); - } - - } - - 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(); - $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_add_mode': - // improved cookiePrefix - - // tree in plan_add_mode is only used for add/removed test cases features - // and shows all test cases defined within test project, - // but as test cases are added to a specified test plan -> store state for each test plan - // - // usage of wrong values in $this->args->xyz for cookiePrefix instead of correct - // values in $filters->setting_xyz - $cookie_prefix = "add_remove_tc_tplan_id_{$filters['setting_testplan']}_"; - - // get filter mode - $key = 'setting_testsgroupby'; - $mode = $this->args->$key; - - - if ($this->do_filtering) - { - $ignore_inactive_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - $ignore_active_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - - $options = array('forPrinting' => NOT_FOR_PRINTING, - 'hideTestCases' => HIDE_TESTCASES, - 'tc_action_enabled' => ACTION_TESTCASE_DISABLE, - 'viewType' => 'testSpecTreeForTestPlan', - 'ignore_inactive_testcases' => $ignore_inactive_testcases, - 'ignore_active_testcases' => $ignore_active_testcases); - - if ($mode == 'mode_req_coverage') - { - - $options = array('for_printing' => NOT_FOR_PRINTING,'exclude_branches' => null); - - $tree_menu = generateTestReqCoverageTree($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 - { - if ($mode == 'mode_req_coverage') - { - $loader = $gui->basehref . 'lib/ajax/getreqcoveragenodes.php?mode=reqspec&' . - "root_node={$this->args->testproject_id}"; - - $req_qty = count($this->testproject_mgr->get_all_requirement_ids($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 . " ($req_qty)"; - $root_node->testlink_node_type = 'testproject'; - } - } - 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_get_parent_child_relation() - { - $key = 'setting_get_parent_child_relation'; - $hidden_key = 'hidden_setting_get_parent_child_relation'; - $selection = 0; - - $this->settings[$key] = array(); - $this->settings[$key][$hidden_key] = false; - - // look where we can find the setting - POST, SESSION - if (isset($this->args->{$key})) { - $selection = 1; - } else if (isset($this->args->{$hidden_key})) { - $selection = 0; - } else if (isset($_SESSION[$key])) { - $selection = $this->settings[$key]; - } else { - $selection = 0; - } - - $this->settings[$key]['selected'] = $selection; - $this->settings[$key][$hidden_key] = $selection; - $_SESSION[$key] = $selection; - } // 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']; - - // Final filtering based on mode: - // Now get all selectable testplans for the user to display. - // For execution: - // For assign test case execution feature: - // 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' || - ( $this->mode == 'plan_mode' && $this->args->feature != 'tc_exec_assignment' ) ; - - 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']; - } - } - } - - - /** - * - */ - protected function init_setting_testsgroupby() { - $key = 'setting_testsgroupby'; - - // now load info from session - $mode = (isset($_REQUEST[$key])) ? $_REQUEST[$key] : 0; - $this->args->testsgroupedby_mode = $mode; - $this->args->{$key} = $mode; - $this->settings[$key]['selected'] = $mode; - - $k2l = array('mode_test_suite','mode_req_coverage'); - foreach( $k2l as $ak ) { - $this->settings[$key]['items'][$ak] = lang_get($ak); - } - } // end of method - - /* - * - */ - private function init_filter_tc_id() - { - $key = 'filter_tc_id'; - $selection = $this->args->{$key}; - - if (!$this->testproject_mgr) { - $this->testproject_mgr = new testproject($this->db); - } - - $tc_cfg = config_get('testcase_cfg'); - $tc_prefix = $this->testproject_mgr->getTestCasePrefix($this->args->testproject_id); - $tc_prefix .= $tc_cfg->glue_character; - - if (!$selection || $selection == $tc_prefix || $this->args->reset_filters) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection ? $selection : $tc_prefix); - $this->active_filters[$key] = $selection; - } // 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; - 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 - - /** - * - * @internal revision - * @since 1.9.13 - * mode this affect domain - */ - private function init_filter_keywords() - { - $key = 'filter_keywords'; - $type = 'filter_keywords_filter_type'; - $this->filters[$key] = false; - $keywords = null; - $l10n = init_labels(array('logical_or' => null,'logical_and' => null, 'not_linked' => null)); - - - switch ($this->mode) - { - case 'edit_mode': - case 'plan_add_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; - } - - $special = array('domain' => array(), 'filter_mode' => array()); - switch($this->mode) - { - case 'edit_mode': - $special['domain'] = array(-1 => $this->option_strings['without_keywords'], - 0 => $this->option_strings['any']); - $special['filter_mode'] = array('NotLinked' => $l10n['not_linked']); - break; - - case 'execution_mode': - case 'plan_add_mode': - case 'plan_mode': - default: - $special['domain'] = array(0 => $this->option_strings['any']); - $special['filter_mode'] = array(); - 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'] = $special['domain'] + $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' => $l10n['logical_or'], - 'And' => $l10n['logical_and']) + - $special['filter_mode']; - $this->filters[$key][$type]['selected'] = $type_selection; - } - - // set the active value to filter - // delete keyword filter if "any" (0) is part of the selection - regardless of filter mode - if (is_array($this->filters[$key]['selected']) && in_array(0, $this->filters[$key]['selected'])) - { - $this->active_filters[$key] = null; - } - else - { - $this->active_filters[$key] = $this->filters[$key]['selected']; - } - $this->active_filters[$type] = $selection ? $type_selection : null; - } - - - - // TICKET 4353: added active/inactive filter - private function init_filter_active_inactive() - { - $key = 'filter_active_inactive'; - - $items = array(DO_NOT_FILTER_INACTIVE_TESTCASES => $this->option_strings['any'], - IGNORE_INACTIVE_TESTCASES => lang_get('show_only_active_testcases'), - IGNORE_ACTIVE_TESTCASES => lang_get('show_only_inactive_testcases')); - - $selection = $this->args->{$key}; - - if (!$selection || $this->args->reset_filters) - { - $selection = null; - } - else - { - $this->do_filtering = true; - } - - $this->filters[$key] = array('items' => $items, 'selected' => $selection); - $this->active_filters[$key] = $selection; - } - - - /** - * - */ - private function init_filter_importance() - { - // show this filter only if test priority management is enabled - $key = 'filter_importance'; - $this->active_filters[$key] = null; - $this->filters[$key] = false; - - if (!$this->testproject_mgr) - { - $this->testproject_mgr = new testproject($this->db); - } - $tp_info = $this->testproject_mgr->get_by_id($this->args->testproject_id); - $enabled = $tp_info['opt']->testPriorityEnabled; - - if ($enabled) - { - $selection = $this->args->{$key}; - if (!$selection || $this->args->reset_filters) - { - $selection = null; - } - else - { - $this->do_filtering = true; - } - - - $this->filters[$key] = array('selected' => $selection); - - // Only drawback: no new user defined importance can be managed - // may be is a good design choice - $this->filters[$key]['items'] = array(0 => $this->option_strings['any'], - HIGH => lang_get('high_importance'), - MEDIUM => lang_get('medium_importance'), - LOW => lang_get('low_importance')); - - $this->filters[$key]['size'] = sizeof($this->filters[$key]['items']); - $this->active_filters[$key] = $selection; - } - } - - - /** - * - * - */ - private function init_filter_priority() - { - // This is a special case of filter: the menu items don't get initialized here, - // they are available as a global smarty variable. So the only thing to be managed - // here is the selection by user. - $key = 'filter_priority'; - - if (!$this->testproject_mgr) - { - $this->testproject_mgr = new testproject($this->db); - } - - $tp_info = $this->testproject_mgr->get_by_id($this->args->testproject_id); - $enabled = $tp_info['opt']->testPriorityEnabled; - - $this->active_filters[$key] = null; - $this->filters[$key] = false; - - if ($enabled) - { - $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_execution_type() - { - if (!$this->tc_mgr) { - $this->tc_mgr = new testcase($this->db); - } - $key = 'filter_execution_type'; - - $selection = $this->args->{$key}; - // handle filter reset - if (!$selection || $this->args->reset_filters) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('items' => array(), 'selected' => $selection); - - // load available execution types - // add "any" string to these types at index 0 as default selection - $this->filters[$key]['items'] = $this->tc_mgr->get_execution_types(); - $this->filters[$key]['items'] = array(0 => $this->option_strings['any']) - + $this->filters[$key]['items']; - - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_assigned_user() - { - if (!$this->testproject_mgr) { - $this->testproject_mgr = new testproject($this->db); - } - - $key = 'filter_assigned_user'; - $unassigned_key = 'filter_assigned_user_include_unassigned'; - $tplan_id = $this->settings['setting_testplan']['selected']; - - // set selection to default (any), only change if value is sent by user and reset is not requested - $selection = $this->args->{$key}; - if (!$selection || $this->args->reset_filters) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $tproject_info = $this->testproject_mgr->get_by_id($this->args->testproject_id); - - $all_testers = getTestersForHtmlOptions($this->db, $tplan_id, $tproject_info, null, - array(TL_USER_ANYBODY => $this->option_strings['any'], - TL_USER_NOBODY => $this->option_strings['none'], - TL_USER_SOMEBODY => $this->option_strings['somebody']), - 'any'); - $visible_testers = $all_testers; - - // in execution mode the rights of the user have to be regarded - if ($this->mode == 'execution_mode') - { - $role = $this->user->getEffectiveRole($this->db, $this->args->testproject_id, $tplan_id); - - $simple_tester_roles = array_flip($this->configuration->exec_cfg->simple_tester_roles); - - // check the user's rights to see what he may do - $right_to_execute = $role->hasRight('testplan_execute'); - $right_to_manage = $role->hasRight('testplan_planning'); - - $simple = false; - if (isset($simple_tester_roles[$role->dbID]) || ($right_to_execute && !$right_to_manage)) { - // user is only simple tester and may not see/execute everything - $simple = true; - } - - $view_mode = $simple ? $this->configuration->exec_cfg->view_mode->tester : 'all'; - - if ($view_mode != 'all') { - $visible_testers = (array)$this->user->getDisplayName(); - $selection = (array)$this->user->dbID; - } - - // re-enable option "user_filter_default" - if (!$selection && $this->configuration->exec_cfg->user_filter_default == 'logged_user') { - $selection = (array)$this->user->dbID; - } - } - - $this->filters[$key] = array('items' => $visible_testers, - 'selected' => $selection, - $unassigned_key => $this->args->{$unassigned_key}); - - // which value shall be passed to tree generation class? - - if ((is_array($selection) && in_array(TL_USER_ANYBODY, $selection)) - || ($selection == TL_USER_ANYBODY)) { - // delete user assignment filter if "any user" is part of the selection - $this->active_filters[$key] = null; - $this->active_filters[$unassigned_key] = 0; - } - - if (is_array($selection)) { - // get keys of the array as values - $this->active_filters[$key] = array_flip($selection); - foreach ($this->active_filters[$key] as $user_key => $user_value) { - $this->active_filters[$key][$user_key] = $user_key; - } - $this->active_filters[$unassigned_key] = $this->filters[$key][$unassigned_key]; - } - } // end of method - - - /** - * - */ - private function init_filter_result() - { - $result_key = 'filter_result_result'; - $method_key = 'filter_result_method'; - $build_key = 'filter_result_build'; - - if (is_null($this->testplan_mgr)) - { - $this->testplan_mgr = new testplan($this->db); - } - $tplan_id = $this->settings['setting_testplan']['selected']; - - $this->configuration->results = config_get('results'); - - // determine, which config to load and use for filter methods - depends on mode! - $cfg = ($this->mode == 'execution_mode') ? - 'execution_filter_methods' : 'execution_assignment_filter_methods'; - $this->configuration->filter_methods = config_get($cfg); - - // - // CRITIC - Differences bewteen this configuration and - // (file const.inc.php) - // $tlCfg->execution_filter_methods['default_type'] - // $tlCfg->execution_assignment_filter_methods['default_type'] - // - // Will create issues: you will see an string on HTML SELECT, but code - // returned on submit will not code for string you are seeing.!!!! - // - // determine which filter method shall be selected by the JS function in template, - // when only one build is selectable by the user - $js_key_to_select = 0; - if ($this->mode == 'execution_mode') - { - $js_key_to_select = $this->configuration->filter_methods['status_code']['current_build']; - } - else if ($this->mode == 'plan_mode') - { - $js_key_to_select = $this->configuration->filter_methods['status_code']['specific_build']; - } - - // values selected by user - $result_selection = $this->args->$result_key; - $method_selection = $this->args->$method_key; - $build_selection = $this->args->$build_key; - - // default values - $default_filter_method = $this->configuration->filter_methods['default_type']; - $any_result_key = $this->configuration->results['status_code']['all']; - $newest_build_id = $this->testplan_mgr->get_max_build_id($tplan_id, testplan::GET_ACTIVE_BUILD); - - if (is_null($method_selection)) - { - $method_selection = $default_filter_method; - } - - if (is_null($result_selection) || $this->args->reset_filters) - // if ($this->args->reset_filters) - { - // no selection yet or filter reset requested - $result_selection = $any_result_key; - $method_selection = $default_filter_method; - $build_selection = $newest_build_id; - } - else - { - $this->do_filtering = true; - } - - // init array structure - $key = 'filter_result'; - $this->filters[$key] = array($result_key => array('items' => null, - 'selected' => $result_selection), - $method_key => array('items' => array(), - 'selected' => $method_selection, - 'js_selection' => $js_key_to_select), - $build_key => array('items' => null, - 'selected' => $build_selection)); - - // init menu for result selection by function from exec.inc.php - $this->filters[$key][$result_key]['items'] = createResultsMenu(); - $this->filters[$key][$result_key]['items'][$any_result_key] = $this->option_strings['any']; - - // init menu for filter method selection - foreach ($this->configuration->filter_methods['status_code'] as $statusname => $statusshortcut) - { - $code = $this->configuration->filter_methods['status_code'][$statusname]; - $this->filters[$key][$method_key]['items'][$code] = - lang_get($this->configuration->filter_methods['status_label'][$statusname]); - } - - // init menu for build selection - $this->filters[$key][$build_key]['items'] = - $this->testplan_mgr->get_builds_for_html_options($tplan_id, testplan::GET_ACTIVE_BUILD); - - // if "any" is selected, nullify the active filters - if ((is_array($result_selection) && in_array($any_result_key, $result_selection)) || - $result_selection == $any_result_key) - { - $this->active_filters[$result_key] = null; - $this->active_filters[$method_key] = null; - $this->active_filters[$build_key] = null; - $this->filters[$key][$result_key]['selected'] = $any_result_key; - } - else - { - $this->active_filters[$result_key] = $result_selection; - $this->active_filters[$method_key] = $method_selection; - $this->active_filters[$build_key] = $build_selection; - } - } // end of method - - /** - * - */ - private function init_filter_bugs() - { - $key = str_replace('init_','',__FUNCTION__); - $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; - } - - - /** - * - * - * @internal revisions - * @since 1.9.14 - * allow multiple selection (if advanced mode) - */ - private function init_filter_workflow_status() - { - $key = 'filter_workflow_status'; - if (!$this->tc_mgr) - { - $this->tc_mgr = new testcase($this->db); - } - - // handle filter reset - $cfx = $this->configuration->{$key . "_values"}; - $selection = $this->args->{$key}; - if (!$selection || $this->args->reset_filters) - { - if( !is_null($this->args->caller) && !$selection) - { - $selection = null; - } - else if( count($cfx) > 0) - { - $selection = $cfx; - $this->do_filtering = true; - } - else - { - $selection = null; - } - } - else - { - $this->do_filtering = true; - } - - $this->filters[$key] = array('items' => array(), 'selected' => $selection); - - // load domain - // add "any" string to these types at index 0 as default selection - $this->filters[$key]['items'] = array(0 => $this->option_strings['any']) + - $this->tc_mgr->getWorkFlowStatusDomain(); - - $this->filters[$key]['size'] = min(count($this->filters[$key]['items']), - self::ADVANCED_FILTER_ITEM_QUANTITY); - - $this->active_filters[$key] = $selection; - } - - - - /** - * - * @used-by __construct - */ - private function initTreeOptions() - { - $this->treeOpt['plan_mode'] = new stdClass(); - $this->treeOpt['plan_mode']->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; - $this->treeOpt['plan_mode']->useColours = COLOR_BY_TC_STATUS_OFF; - $this->treeOpt['plan_mode']->testcases_colouring_by_selected_build = DISABLED; - $this->treeOpt['plan_mode']->absolute_last_execution = true; // hmm probably useless - - } - - /** - * - */ - protected function init_advanced_filter_mode() - { - switch( $this->mode ) - { - case 'edit_mode': - $this->advanced_filter_mode = TRUE; - break; - - default: - $m2c = __FUNCTION__; - parent::$m2c(); - break; - } - } // end of method - - - - - //--------------------- - /** - * - */ - private function init_filter_doc_id() - { - $key = 'filter_doc_id'; - $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_title() - { - $key = 'filter_title'; - $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_status() { - $key = 'filter_status'; - $selection = $this->args->{$key}; - - // get configured statuses and add "any" string to menu - $items = array(self::ANY => $this->option_strings['any']) + - (array) init_labels($this->configuration->req_cfg->status_labels); - - // BUGID 3852 - if (!$selection || $this->args->reset_filters - || (is_array($selection) && in_array('0', $selection, true))) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, 'items' => $items); - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_type() { - $key = 'filter_type'; - $selection = $this->args->{$key}; - - // get configured types and add "any" string to menu - $items = array(self::ANY => $this->option_strings['any']) + - (array) init_labels($this->configuration->req_cfg->type_labels); - - if (!$selection || $this->args->reset_filters - || (is_array($selection) && in_array(self::ANY, $selection))) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, 'items' => $items); - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_spec_type() { - $key = 'filter_spec_type'; - $selection = $this->args->{$key}; - - // get configured types and add "any" string to menu - $items = array(self::ANY => $this->option_strings['any']) + - (array) init_labels($this->configuration->req_spec_cfg->type_labels); - - if (!$selection || $this->args->reset_filters - || (is_array($selection) && in_array(self::ANY, $selection))) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, 'items' => $items); - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_coverage() { - - $key = 'filter_coverage'; - $this->filters[$key] = false; - $this->active_filters[$key] = null; - - // is coverage management enabled? - if ($this->configuration->req_cfg->expected_coverage_management) { - $selection = $this->args->{$key}; - - if (!$selection || !is_numeric($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_relation() { - - $key = 'filter_relation'; - - // are relations enabled? - if ($this->configuration->req_cfg->relations->enable) { - $selection = $this->args->{$key}; - - if (!$this->req_mgr) { - $this->req_mgr = new requirement_mgr($this->db); - } - - $req_relations = $this->req_mgr->init_relation_type_select(); - - // special case here: - // for equal type relations (where it doesn't matter if we find source or destination) - // we have to remove the source identficator from the array key - foreach ($req_relations['equal_relations'] as $array_key => $old_key) - { - // set new key in array and delete old one - $new_key = (int) str_replace("_source", "", $old_key); - $req_relations['items'][$new_key] = $req_relations['items'][$old_key]; - unset($req_relations['items'][$old_key]); - } - - $items = array(self::ANY => $this->option_strings['any']) + - (array) $req_relations['items']; - - if (!$selection || $this->args->reset_filters || - (is_array($selection) && in_array(self::ANY, $selection))) - { - $selection = null; - } - else - { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection, - 'items' => $items); - $this->active_filters[$key] = $selection; - } else { - // not enabled, just nullify - $this->filters[$key] = false; - $this->active_filters[$key] = null; - } - } // end of method - - - /** - * - */ - protected function getCustomFields() - { - if (!$this->req_mgr) - { - $this->req_mgr = new requirement_mgr($this->db); - $this->cfield_mgr = &$this->req_mgr->cfield_mgr; - } - - $cfields = $this->req_mgr->get_linked_cfields(null, null, $this->args->testproject_id); - return $cfields; - } - -} \ No newline at end of file + add/remove test cases + * + * + * @internal revisions + * @since 1.9.13 + */ + +/* + * -------------------------------------------------------- + * An important note on request-URL too large (BUGID 3516) + * -------------------------------------------------------- + * + * 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 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 tlTestCaseFilterByRequirementControl extends tlFilterControl +{ + + public $req_mgr = null; + + /** + * 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; + + /** + * 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 + */ + + /* + * MAGIC NUMBERS are related to field size + * filter_tc_id: 0,30 arbitrary + * filter_bugs: 240 = 60 x 4 (60 bug_id size on execution_bugs table) + */ + private $all_filters = array( + 'filter_doc_id' => array( + "POST", + tlInputParameter::STRING_N + ), + 'filter_title' => array( + "POST", + tlInputParameter::STRING_N + ), + 'filter_status' => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + 'filter_type' => array( + "POST", + tlInputParameter::ARRAY_INT + ), + 'filter_spec_type' => array( + "POST", + tlInputParameter::ARRAY_INT + ), + 'filter_coverage' => array( + "POST", + tlInputParameter::INT_N + ), + 'filter_relation' => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + 'filter_tc_id' => array( + "POST", + tlInputParameter::STRING_N + ), + 'filter_custom_fields' => null, + 'filter_result' => false + ); + + /** + * 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( + 'plan_add_mode' => array( + 'filter_tc_id', + 'filter_testcase_name', + 'filter_toplevel_testsuite', + 'filter_keywords', + // 'filter_active_inactive', + 'filter_importance', + 'filter_execution_type', + 'filter_workflow_status', + '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( + "REQUEST", + tlInputParameter::INT_N + ), + 'setting_refresh_tree_on_action' => array( + "POST", + tlInputParameter::CB_BOOL + ), + 'setting_get_parent_child_relation' => array( + "POST", + tlInputParameter::CB_BOOL + ), + 'hidden_setting_get_parent_child_relation' => array( + "POST", + tlInputParameter::INT_N + ), + 'setting_testsgroupby' => array( + "REQUEST", + tlInputParameter::INT_N + ) + ); + + /** + * This array is used to map the modes to their available settings. + * + * @var array + */ + private $mode_setting_mapping = array( + 'plan_add_mode' => array( + 'setting_testplan', + 'setting_refresh_tree_on_action', + 'setting_get_parent_child_relation', + 'hidden_setting_get_parent_child_relation', + 'setting_testsgroupby' + ) + ); + + /** + * 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 = 'plan_add_mode'; + + /** + * Options to be used accordin to $this->mode, to build tree + * + * @var array + */ + private $treeOpt = array(); + + /** + * 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 plan_add_mode, depending on usage + */ + public function __construct(&$dbHandler, $mode = 'plan_add_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); + $this->req_mgr = new requirement_mgr($this->db); + $this->cfield_mgr = &$this->req_mgr->cfield_mgr; + + // moved here from parent::__constructor() to be certain that + // all required objects has been created + $this->init_filters(); + + $this->initTreeOptions($this->mode); + + // delete any filter settings that may be left from previous calls in session + // Session data has been designed to provide an unidirectional channel + // between the left pane where tree lives and right pane. + // That's why delete each time our OWN session data. + $this->delete_own_session_data(); + $this->delete_old_session_data(); + + $this->save_session_data(); + } + + /** + */ + public function __destruct() + { + parent::__destruct(); // destroys testproject manager + + unset($this->tc_mgr); + unset($this->testplan_mgr); + unset($this->platform_mgr); + unset($this->cfield_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. + * + * @return bool + */ + 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')->requirements; + + // 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'); + + // load req and req spec config (for types, filters, status, ...) + $this->configuration->req_cfg = config_get('req_cfg'); + $this->configuration->req_spec_cfg = config_get('req_spec_cfg'); + + // is switch filter mode enabled? + $this->filter_mode_choice_enabled = false; + switch ($this->mode) { + case 'edit_mode': + break; + + default: + if (isset($this->configuration->advanced_filter_mode_choice) && + $this->configuration->advanced_filter_mode_choice == ENABLED) { + $this->filter_mode_choice_enabled = true; + } + break; + } + + return tl::OK; + } + + /** + * 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); + } + + /** + * 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 situations + // the build setting is in plan mode only needed for one feature + if ($this->mode == 'plan_mode' && + ($this->args->feature != 'tc_exec_assignment' && + $this->args->feature != 'test_urgency')) { + $this->settings['setting_build'] = false; + $this->settings['setting_platform'] = 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() + { + // iterate through all filters and activate the needed ones + if ($this->configuration->show_filters == ENABLED) { + foreach ($this->all_filters as $name => $info) { + $init_method = "init_$name"; + if (method_exists($this, $init_method) && + $this->configuration->{$name} == ENABLED) { + $this->$init_method(); + $this->display_req_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; + } + } + } else { + $this->display_req_filters = false; + } + } + + /** + * 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; + } + + /** + */ + public function set_testcases_to_show($value = null) + { + // update active_filters + if (! is_null($value)) { + $this->active_filters['testcases_to_show'] = $value; + } + + // 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)) { + unset($_SESSION[$this->mode][$token]); // too old, delete! + } + } + } + } + + /** + */ + 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; + + $key2loop = array( + 'setting_build', + 'setting_platform' + ); + foreach ($key2loop as $kiwi) { + if ($this->settings[$kiwi]) { + $string .= "&{$kiwi}={$this->settings[$kiwi]['selected']}"; + } + } + if ($this->active_filters['filter_priority'] > 0) { + $string .= '&filter_priority=' . + $this->active_filters['filter_priority']; + } + + $keyword_list = null; + if (is_array($this->active_filters['filter_keywords'])) { + $keyword_list = implode(',', + $this->active_filters['filter_keywords']); + } elseif ($this->active_filters['filter_keywords']) { + $keyword_list = $this->active_filters['filter_keywords']; + } + + // Need to undertand why for other filters that also are array + // we have choosen to serialize, and here not. + // may be to avoid more refactoring + if ($keyword_list) { + $string .= '&filter_keywords=' . $keyword_list . + '&filter_keywords_filter_type=' . + $this->active_filters['filter_keywords_filter_type']; + } + + // Using serialization + if ($this->active_filters['filter_assigned_user']) { + $string .= '&filter_assigned_user=' . + json_encode($this->active_filters['filter_assigned_user']) . + '&filter_assigned_user_include_unassigned=' . + ($this->active_filters['filter_assigned_user_include_unassigned'] ? '1' : '0'); + } + + if ($this->active_filters['filter_result_result']) { + $string .= '&filter_result_result=' . + json_encode($this->active_filters['filter_result_result']) . + '&filter_result_method=' . + $this->active_filters['filter_result_method'] . + '&filter_result_build=' . + $this->active_filters['filter_result_build']; + } + + if (! is_null($this->active_filters['filter_bugs'])) { + $string .= '&' . + http_build_query( + array( + 'filter_bugs' => $this->active_filters['filter_bugs'] + )); + } + } + + 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(); + $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); + } + + switch ($this->mode) { + case 'plan_add_mode': + // improved cookiePrefix - + // tree in plan_add_mode is only used for add/removed test cases features + // and shows all test cases defined within test project, + // but as test cases are added to a specified test plan -> store state for each test plan + // + // usage of wrong values in $this->args->xyz for cookiePrefix instead of correct + // values in $filters->setting_xyz + $cookie_prefix = "add_remove_tc_tplan_id_{$filters['setting_testplan']}_"; + + // get filter mode + $key = 'setting_testsgroupby'; + $mode = $this->args->$key; + + if ($this->do_filtering) { + if ($mode == 'mode_req_coverage') { + + $options = array( + 'for_printing' => NOT_FOR_PRINTING, + 'exclude_branches' => null + ); + + $tree_menu = generateTestReqCoverageTree($this->db, + $this->args->testproject_id, + $this->args->testproject_name, $filters, $options); + } + + $root_node = $tree_menu->rootnode; + $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; + } else { + if ($mode == 'mode_req_coverage') { + $loader = $gui->basehref . + 'lib/ajax/getreqcoveragenodes.php?mode=reqspec&' . + "root_node={$this->args->testproject_id}"; + + $req_qty = count( + $this->testproject_mgr->get_all_requirement_ids( + $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 . + " ($req_qty)"; + $root_node->testlink_node_type = 'testproject'; + } + } + 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; + } + + /** + */ + public 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; + } elseif (isset($this->args->{$hidden_key})) { + $selection = 0; + } elseif (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; + } + + /** + */ + public function init_setting_get_parent_child_relation() + { + $key = 'setting_get_parent_child_relation'; + $hidden_key = 'hidden_setting_get_parent_child_relation'; + $selection = 0; + + $this->settings[$key] = array(); + $this->settings[$key][$hidden_key] = false; + + // look where we can find the setting - POST, SESSION + if (isset($this->args->{$key})) { + $selection = 1; + } elseif (isset($this->args->{$hidden_key})) { + $selection = 0; + } elseif (isset($_SESSION[$key])) { + $selection = $this->settings[$key]; + } else { + $selection = 0; + } + + $this->settings[$key]['selected'] = $selection; + $this->settings[$key][$hidden_key] = $selection; + $_SESSION[$key] = $selection; + } + + /** + */ + public 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']; + + // Final filtering based on mode: + // Now get all selectable testplans for the user to display. + // For execution: + // For assign test case execution feature: + // 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' || + ($this->mode == 'plan_mode' && + $this->args->feature != 'tc_exec_assignment'); + + 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']; + } + } + } + + /** + */ + protected function init_setting_testsgroupby() + { + $key = 'setting_testsgroupby'; + + // now load info from session + $mode = (isset($_REQUEST[$key])) ? $_REQUEST[$key] : 0; + $this->args->testsgroupedby_mode = $mode; + $this->args->{$key} = $mode; + $this->settings[$key]['selected'] = $mode; + + $k2l = array( + 'mode_test_suite', + 'mode_req_coverage' + ); + foreach ($k2l as $ak) { + $this->settings[$key]['items'][$ak] = lang_get($ak); + } + } + + /* + * + */ + public function init_filter_tc_id() + { + $key = 'filter_tc_id'; + $selection = $this->args->{$key}; + + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + + $tc_cfg = config_get('testcase_cfg'); + $tc_prefix = $this->testproject_mgr->getTestCasePrefix( + $this->args->testproject_id); + $tc_prefix .= $tc_cfg->glue_character; + + if (! $selection || $selection == $tc_prefix || + $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection ? $selection : $tc_prefix + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public 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; + } + + /** + */ + public 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; + 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; + } + } + + /** + * + * @internal revision + * @since 1.9.13 + * mode this affect domain + */ + public function init_filter_keywords() + { + $key = 'filter_keywords'; + $type = 'filter_keywords_filter_type'; + $this->filters[$key] = false; + $keywords = null; + $l10n = init_labels( + array( + 'logical_or' => null, + 'logical_and' => null, + 'not_linked' => null + )); + + switch ($this->mode) { + case 'edit_mode': + case 'plan_add_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; + } + + $special = array( + 'domain' => array(), + 'filter_mode' => array() + ); + switch ($this->mode) { + case 'edit_mode': + $special['domain'] = array( + - 1 => $this->option_strings['without_keywords'], + 0 => $this->option_strings['any'] + ); + $special['filter_mode'] = array( + 'NotLinked' => $l10n['not_linked'] + ); + break; + + case 'execution_mode': + case 'plan_add_mode': + case 'plan_mode': + default: + $special['domain'] = array( + 0 => $this->option_strings['any'] + ); + $special['filter_mode'] = array(); + 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'] = $special['domain'] + $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' => $l10n['logical_or'], + 'And' => $l10n['logical_and'] + ) + $special['filter_mode']; + $this->filters[$key][$type]['selected'] = $type_selection; + } + + // set the active value to filter + // delete keyword filter if "any" (0) is part of the selection - regardless of filter mode + if (is_array($this->filters[$key]['selected']) && + in_array(0, $this->filters[$key]['selected'])) { + $this->active_filters[$key] = null; + } else { + $this->active_filters[$key] = $this->filters[$key]['selected']; + } + $this->active_filters[$type] = $selection ? $type_selection : null; + } + + // TICKET 4353: added active/inactive filter + public function init_filter_active_inactive() + { + $key = 'filter_active_inactive'; + + $items = array( + DO_NOT_FILTER_INACTIVE_TESTCASES => $this->option_strings['any'], + IGNORE_INACTIVE_TESTCASES => lang_get('show_only_active_testcases'), + IGNORE_ACTIVE_TESTCASES => lang_get('show_only_inactive_testcases') + ); + + $selection = $this->args->{$key}; + + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'items' => $items, + 'selected' => $selection + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_importance() + { + // show this filter only if test priority management is enabled + $key = 'filter_importance'; + $this->active_filters[$key] = null; + $this->filters[$key] = false; + + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + $tp_info = $this->testproject_mgr->get_by_id( + $this->args->testproject_id); + $enabled = $tp_info['opt']->testPriorityEnabled; + + if ($enabled) { + $selection = $this->args->{$key}; + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection + ); + + // Only drawback: no new user defined importance can be managed + // may be is a good design choice + $this->filters[$key]['items'] = array( + 0 => $this->option_strings['any'], + HIGH => lang_get('high_importance'), + MEDIUM => lang_get('medium_importance'), + LOW => lang_get('low_importance') + ); + + $this->filters[$key]['size'] = count($this->filters[$key]['items']); + $this->active_filters[$key] = $selection; + } + } + + /** + */ + public function init_filter_priority() + { + // This is a special case of filter: the menu items don't get initialized here, + // they are available as a global smarty variable. So the only thing to be managed + // here is the selection by user. + $key = 'filter_priority'; + + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + + $tp_info = $this->testproject_mgr->get_by_id( + $this->args->testproject_id); + $enabled = $tp_info['opt']->testPriorityEnabled; + + $this->active_filters[$key] = null; + $this->filters[$key] = false; + + if ($enabled) { + $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; + } + } + + /** + */ + public function init_filter_execution_type() + { + if (! $this->tc_mgr) { + $this->tc_mgr = new testcase($this->db); + } + $key = 'filter_execution_type'; + + $selection = $this->args->{$key}; + // handle filter reset + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'items' => array(), + 'selected' => $selection + ); + + // load available execution types + // add "any" string to these types at index 0 as default selection + $this->filters[$key]['items'] = $this->tc_mgr->get_execution_types(); + $this->filters[$key]['items'] = array( + 0 => $this->option_strings['any'] + ) + $this->filters[$key]['items']; + + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_assigned_user() + { + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + + $key = 'filter_assigned_user'; + $unassigned_key = 'filter_assigned_user_include_unassigned'; + $tplan_id = $this->settings['setting_testplan']['selected']; + + // set selection to default (any), only change if value is sent by user and reset is not requested + $selection = $this->args->{$key}; + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $tproject_info = $this->testproject_mgr->get_by_id( + $this->args->testproject_id); + + $all_testers = getTestersForHtmlOptions($this->db, $tplan_id, + $tproject_info, null, + array( + TL_USER_ANYBODY => $this->option_strings['any'], + TL_USER_NOBODY => $this->option_strings['none'], + TL_USER_SOMEBODY => $this->option_strings['somebody'] + ), 'any'); + $visible_testers = $all_testers; + + // in execution mode the rights of the user have to be regarded + if ($this->mode == 'execution_mode') { + $role = $this->user->getEffectiveRole($this->db, + $this->args->testproject_id, $tplan_id); + + $simple_tester_roles = array_flip( + $this->configuration->exec_cfg->simple_tester_roles); + + // check the user's rights to see what he may do + $right_to_execute = $role->hasRight('testplan_execute'); + $right_to_manage = $role->hasRight('testplan_planning'); + + $simple = false; + if (isset($simple_tester_roles[$role->dbID]) || + ($right_to_execute && ! $right_to_manage)) { + // user is only simple tester and may not see/execute everything + $simple = true; + } + + $view_mode = $simple ? $this->configuration->exec_cfg->view_mode->tester : 'all'; + + if ($view_mode != 'all') { + $visible_testers = (array) $this->user->getDisplayName(); + $selection = (array) $this->user->dbID; + } + + // re-enable option "user_filter_default" + if (! $selection && + $this->configuration->exec_cfg->user_filter_default == + 'logged_user') { + $selection = (array) $this->user->dbID; + } + } + + $this->filters[$key] = array( + 'items' => $visible_testers, + 'selected' => $selection, + $unassigned_key => $this->args->{$unassigned_key} + ); + + // which value shall be passed to tree generation class? + + if ((is_array($selection) && in_array(TL_USER_ANYBODY, $selection)) || + ($selection == TL_USER_ANYBODY)) { + // delete user assignment filter if "any user" is part of the selection + $this->active_filters[$key] = null; + $this->active_filters[$unassigned_key] = 0; + } + + if (is_array($selection)) { + // get keys of the array as values + $this->active_filters[$key] = array_flip($selection); + foreach ($this->active_filters[$key] as $user_key => $user_value) { + $this->active_filters[$key][$user_key] = $user_key; + } + $this->active_filters[$unassigned_key] = $this->filters[$key][$unassigned_key]; + } + } + + /** + */ + public function init_filter_result() + { + $result_key = 'filter_result_result'; + $method_key = 'filter_result_method'; + $build_key = 'filter_result_build'; + + if (is_null($this->testplan_mgr)) { + $this->testplan_mgr = new testplan($this->db); + } + $tplan_id = $this->settings['setting_testplan']['selected']; + + $this->configuration->results = config_get('results'); + + // determine, which config to load and use for filter methods - depends on mode! + $cfg = ($this->mode == 'execution_mode') ? 'execution_filter_methods' : 'execution_assignment_filter_methods'; + $this->configuration->filter_methods = config_get($cfg); + + // + // CRITIC - Differences bewteen this configuration and + // (file const.inc.php) + // $tlCfg->execution_filter_methods['default_type'] + // $tlCfg->execution_assignment_filter_methods['default_type'] + // + // Will create issues: you will see an string on HTML SELECT, but code + // returned on submit will not code for string you are seeing.!!!! + // + // determine which filter method shall be selected by the JS function in template, + // when only one build is selectable by the user + $js_key_to_select = 0; + if ($this->mode == 'execution_mode') { + $js_key_to_select = $this->configuration->filter_methods['status_code']['current_build']; + } elseif ($this->mode == 'plan_mode') { + $js_key_to_select = $this->configuration->filter_methods['status_code']['specific_build']; + } + + // values selected by user + $result_selection = $this->args->$result_key; + $method_selection = $this->args->$method_key; + $build_selection = $this->args->$build_key; + + // default values + $default_filter_method = $this->configuration->filter_methods['default_type']; + $any_result_key = $this->configuration->results['status_code']['all']; + $newest_build_id = $this->testplan_mgr->get_max_build_id($tplan_id, + testplan::GET_ACTIVE_BUILD); + + if (is_null($method_selection)) { + $method_selection = $default_filter_method; + } + + if (is_null($result_selection) || $this->args->reset_filters) + // if ($this->args->reset_filters) + { + // no selection yet or filter reset requested + $result_selection = $any_result_key; + $method_selection = $default_filter_method; + $build_selection = $newest_build_id; + } else { + $this->do_filtering = true; + } + + // init array structure + $key = 'filter_result'; + $this->filters[$key] = array( + $result_key => array( + 'items' => null, + 'selected' => $result_selection + ), + $method_key => array( + 'items' => array(), + 'selected' => $method_selection, + 'js_selection' => $js_key_to_select + ), + $build_key => array( + 'items' => null, + 'selected' => $build_selection + ) + ); + + // init menu for result selection by function from exec.inc.php + $this->filters[$key][$result_key]['items'] = createResultsMenu(); + $this->filters[$key][$result_key]['items'][$any_result_key] = $this->option_strings['any']; + + // init menu for filter method selection + foreach ($this->configuration->filter_methods['status_code'] as $statusname => $statusshortcut) { + $code = $this->configuration->filter_methods['status_code'][$statusname]; + $this->filters[$key][$method_key]['items'][$code] = lang_get( + $this->configuration->filter_methods['status_label'][$statusname]); + } + + // init menu for build selection + $this->filters[$key][$build_key]['items'] = $this->testplan_mgr->get_builds_for_html_options( + $tplan_id, testplan::GET_ACTIVE_BUILD); + + // if "any" is selected, nullify the active filters + if ((is_array($result_selection) && + in_array($any_result_key, $result_selection)) || + $result_selection == $any_result_key) { + $this->active_filters[$result_key] = null; + $this->active_filters[$method_key] = null; + $this->active_filters[$build_key] = null; + $this->filters[$key][$result_key]['selected'] = $any_result_key; + } else { + $this->active_filters[$result_key] = $result_selection; + $this->active_filters[$method_key] = $method_selection; + $this->active_filters[$build_key] = $build_selection; + } + } + + /** + */ + public function init_filter_bugs() + { + $key = str_replace('init_', '', __FUNCTION__); + $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; + } + + /** + * + * @internal revisions + * @since 1.9.14 + * allow multiple selection (if advanced mode) + */ + public function init_filter_workflow_status() + { + $key = 'filter_workflow_status'; + if (! $this->tc_mgr) { + $this->tc_mgr = new testcase($this->db); + } + + // handle filter reset + $cfx = $this->configuration->{$key . "_values"}; + $selection = $this->args->{$key}; + if (! $selection || $this->args->reset_filters) { + if (! is_null($this->args->caller) && ! $selection) { + $selection = null; + } elseif (count($cfx) > 0) { + $selection = $cfx; + $this->do_filtering = true; + } else { + $selection = null; + } + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'items' => array(), + 'selected' => $selection + ); + + // load domain + // add "any" string to these types at index 0 as default selection + $this->filters[$key]['items'] = array( + 0 => $this->option_strings['any'] + ) + $this->tc_mgr->getWorkFlowStatusDomain(); + + $this->filters[$key]['size'] = min(count($this->filters[$key]['items']), + self::ADVANCED_FILTER_ITEM_QUANTITY); + + $this->active_filters[$key] = $selection; + } + + /** + * + * @used-by __construct + */ + public function initTreeOptions() + { + $this->treeOpt['plan_mode'] = new stdClass(); + $this->treeOpt['plan_mode']->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; + $this->treeOpt['plan_mode']->useColours = COLOR_BY_TC_STATUS_OFF; + $this->treeOpt['plan_mode']->testcases_colouring_by_selected_build = DISABLED; + $this->treeOpt['plan_mode']->absolute_last_execution = true; // hmm probably useless + } + + /** + */ + protected function init_advanced_filter_mode() + { + switch ($this->mode) { + case 'edit_mode': + $this->advanced_filter_mode = true; + break; + + default: + $m2c = __FUNCTION__; + parent::$m2c(); + break; + } + } + + /** + */ + public function init_filter_doc_id() + { + $key = 'filter_doc_id'; + $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; + } + + /** + */ + public function init_filter_title() + { + $key = 'filter_title'; + $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; + } + + /* + * + */ + public function init_filter_status() + { + $key = 'filter_status'; + $selection = $this->args->{$key}; + + // get configured statuses and add "any" string to menu + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) init_labels($this->configuration->req_cfg->status_labels); + + // BUGID 3852 + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array('0', $selection, true))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_type() + { + $key = 'filter_type'; + $selection = $this->args->{$key}; + + // get configured types and add "any" string to menu + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) init_labels($this->configuration->req_cfg->type_labels); + + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array(self::ANY, $selection))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_spec_type() + { + $key = 'filter_spec_type'; + $selection = $this->args->{$key}; + + // get configured types and add "any" string to menu + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) init_labels($this->configuration->req_spec_cfg->type_labels); + + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array(self::ANY, $selection))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } + + /** + */ + public function init_filter_coverage() + { + $key = 'filter_coverage'; + $this->filters[$key] = false; + $this->active_filters[$key] = null; + + // is coverage management enabled? + if ($this->configuration->req_cfg->expected_coverage_management) { + $selection = $this->args->{$key}; + + if (! $selection || ! is_numeric($selection) || + $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection + ); + $this->active_filters[$key] = $selection; + } + } + + /** + */ + public function init_filter_relation() + { + $key = 'filter_relation'; + + // are relations enabled? + if ($this->configuration->req_cfg->relations->enable) { + $selection = $this->args->{$key}; + + if (! $this->req_mgr) { + $this->req_mgr = new requirement_mgr($this->db); + } + + $req_relations = $this->req_mgr->init_relation_type_select(); + + // special case here: + // for equal type relations (where it doesn't matter if we find source or destination) + // we have to remove the source identficator from the array key + foreach ($req_relations['equal_relations'] as $old_key) { + // set new key in array and delete old one + $new_key = (int) str_replace("_source", "", $old_key); + $req_relations['items'][$new_key] = $req_relations['items'][$old_key]; + unset($req_relations['items'][$old_key]); + } + + $items = array( + self::ANY => $this->option_strings['any'] + ) + (array) $req_relations['items']; + + if (! $selection || $this->args->reset_filters || + (is_array($selection) && in_array(self::ANY, $selection))) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection, + 'items' => $items + ); + $this->active_filters[$key] = $selection; + } else { + // not enabled, just nullify + $this->filters[$key] = false; + $this->active_filters[$key] = null; + } + } + + /** + */ + protected function getCustomFields() + { + if (! $this->req_mgr) { + $this->req_mgr = new requirement_mgr($this->db); + $this->cfield_mgr = &$this->req_mgr->cfield_mgr; + } + + return $this->req_mgr->get_linked_cfields(null, null, + $this->args->testproject_id); + } +} diff --git a/lib/functions/tlTestCaseFilterControl.class.php b/lib/functions/tlTestCaseFilterControl.class.php index 4b6f5c62c9..e6c7c98a2c 100644 --- a/lib/functions/tlTestCaseFilterControl.class.php +++ b/lib/functions/tlTestCaseFilterControl.class.php @@ -1,4 +1,5 @@ assign test case execution * --> update linked test case versions * --> set urgent tests - * - * - execNavigator.php/tpl in "execution_mode" + * + * - 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 @@ -39,23 +40,23 @@ * -------------------------------------------------------- * An important note on request-URL too large (BUGID 3516) * -------------------------------------------------------- - * + * * 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 + * 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 + * 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), @@ -63,84 +64,84 @@ * 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 - * ) + * [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 + * ) + * ) * - * [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; - * + * ? $_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 + * 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 + * 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 + * 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 + * 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. * ------------------------------------------------------------------------------------------------ @@ -158,2014 +159,2174 @@ * @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; - - - /** - * 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 - */ - - /* MAGIC NUMBERS are related to field size - * filter_tc_id: 0,30 arbitrary - * filter_bugs: 240 = 60 x 4 (60 bug_id size on execution_bugs table) - */ - private $all_filters; - - /** - * 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_workflow_status', - 'filter_importance', - 'filter_execution_type', - 'filter_custom_fields', - 'filter_platforms'), - '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', - 'filter_bugs'), - 'plan_mode' => array('filter_tc_id', - 'filter_testcase_name', - 'filter_toplevel_testsuite', - 'filter_keywords', - 'filter_priority', - 'filter_execution_type', - // enabled user filter when assigning testcases - 'filter_assigned_user', - 'filter_custom_fields', - 'filter_result'), - 'plan_add_mode' => array('filter_tc_id', - 'filter_testcase_name', - 'filter_toplevel_testsuite', - 'filter_keywords', - 'filter_importance', - 'filter_execution_type', - 'filter_workflow_status', - 'filter_custom_fields', - 'filter_platforms')); - - /** - * 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("REQUEST", tlInputParameter::INT_N), - 'setting_build' => array("REQUEST", tlInputParameter::INT_N), - 'setting_platform' => array("REQUEST", tlInputParameter::INT_N), - 'setting_refresh_tree_on_action' => array("POST", tlInputParameter::CB_BOOL), - 'setting_testsgroupby' => array("REQUEST", tlInputParameter::INT_N), - 'setting_exec_tree_counters_logic' => - array("REQUEST", tlInputParameter::INT_N) - ); - - /** - * 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_exec_tree_counters_logic', - 'setting_refresh_tree_on_action'), - 'plan_mode' => array('setting_testplan','setting_build', - 'setting_platform', - 'setting_refresh_tree_on_action'), - 'plan_add_mode' => array('setting_testplan','setting_testsgroupby', - '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'; - - - /** - * Options to be used accordin to $this->mode, to build tree - * @var array - */ - private $treeOpt = array(); - - - /** - * 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') { - - // execution order is CRITIC - $this->setFiltersDefinition(); - - // 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); - - $this->cfield_mgr = new cfield_mgr($this->db); - - - $this->settings['setting_get_parent_child_relation'] = false; - - // moved here from parent::__constructor() to be certain that - // all required objects has been created - $this->init_filters(); - - $this->initTreeOptions($this->mode); - - // delete any filter settings that may be left from previous calls in session - // Session data has been designed to provide an unidirectional channel - // between the left pane where tree lives and right pane. - // That's why delete each time our OWN session data. - $this->delete_own_session_data(); - $this->delete_old_session_data(); - - $this->save_session_data(); - - } - - /** - * - * - */ - public function __destruct() { - parent::__destruct(); //destroys testproject manager - - unset($this->tc_mgr); - unset($this->testplan_mgr); - unset($this->platform_mgr); - unset($this->cfield_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. - * @return bool - */ - 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 switch filter mode enabled? - $this->filter_mode_choice_enabled = false; - switch( $this->mode ) +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; + + /** + * 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 + */ + + /* + * MAGIC NUMBERS are related to field size + * filter_tc_id: 0,30 arbitrary + * filter_bugs: 240 = 60 x 4 (60 bug_id size on execution_bugs table) + */ + private $all_filters; + + /** + * 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_workflow_status', + 'filter_importance', + 'filter_execution_type', + 'filter_custom_fields', + 'filter_platforms' + ), + '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', + 'filter_bugs' + ), + 'plan_mode' => array( + 'filter_tc_id', + 'filter_testcase_name', + 'filter_toplevel_testsuite', + 'filter_keywords', + 'filter_priority', + 'filter_execution_type', + // enabled user filter when assigning testcases + 'filter_assigned_user', + 'filter_custom_fields', + 'filter_result' + ), + 'plan_add_mode' => array( + 'filter_tc_id', + 'filter_testcase_name', + 'filter_toplevel_testsuite', + 'filter_keywords', + 'filter_importance', + 'filter_execution_type', + 'filter_workflow_status', + 'filter_custom_fields', + 'filter_platforms' + ) + ); + + /** + * 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 = [ + 'setting_testplan' => [ + "REQUEST", + tlInputParameter::INT_N + ], + 'setting_build' => [ + "REQUEST", + tlInputParameter::INT_N + ], + 'setting_platform' => [ + "REQUEST", + tlInputParameter::INT_N + ], + 'setting_testsgroupby' => [ + "REQUEST", + tlInputParameter::INT_N + ], + 'setting_refresh_tree_on_action' => [ + "POST", + tlInputParameter::CB_BOOL + ], + 'setting_exec_tree_counters_logic' => [ + "REQUEST", + tlInputParameter::INT_N + ] + ]; + + /** + * 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_exec_tree_counters_logic', + 'setting_refresh_tree_on_action' + ), + 'plan_mode' => array( + 'setting_testplan', + 'setting_build', + 'setting_platform', + 'setting_refresh_tree_on_action' + ), + 'plan_add_mode' => array( + 'setting_testplan', + 'setting_testsgroupby', + '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'; + + /** + * Options to be used accordin to $this->mode, to build tree + * + * @var array + */ + private $treeOpt = array(); + + /** + * 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; + + const DISABLED = 0; + + const ENABLED = 1; + + /** + * + * @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') { - case 'edit_mode': - break; - default: - if (isset($this->configuration->advanced_filter_mode_choice) && - $this->configuration->advanced_filter_mode_choice == ENABLED) - { - $this->filter_mode_choice_enabled = true; - } - break; - } + // execution order is CRITIC + $this->setFiltersDefinition(); - return tl::OK; - } // end of method + // set mode to define further actions before calling parent constructor + $this->mode = array_key_exists($mode, $this->mode_filter_mapping) ? $mode : 'edit_mode'; - /** - * 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() { + // 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); - // some common user input is already read in parent class - parent::init_args(); + $this->cfield_mgr = new cfield_mgr($this->db); - // add settings and filters to parameter info array for request parsers - $params = array(); + $this->settings['setting_get_parent_child_relation'] = false; - foreach ($this->all_settings as $name => $info) { - if (is_array($info)) { - $params[$name] = $info; - } - } - - // Do first get, to have info that can change config - I_PARAMS($params, $this->args); - - switch( $this->mode ) { - case 'edit_mode': - $this->args->advanced_filter_mode = TRUE; - break; - } + // moved here from parent::__constructor() to be certain that + // all required objects has been created + $this->init_filters(); + $this->initTreeOptions($this->mode); - if($this->args->advanced_filter_mode) { - // 20160106 - fman - // it's not clear why we have choosen to do - // this check, because this makes that - // config option advanced_filter_mode_choice - // does not work as expected. - switch($this->mode) { - case 'plan_add_mode': - case 'edit_mode': - $this->all_filters['filter_workflow_status'] = - array("POST", tlInputParameter::ARRAY_INT); + // delete any filter settings that may be left from previous calls in session + // Session data has been designed to provide an unidirectional channel + // between the left pane where tree lives and right pane. + // That's why delete each time our OWN session data. + $this->delete_own_session_data(); + $this->delete_old_session_data(); - $this->all_filters['filter_importance'] = - array("POST", tlInputParameter::ARRAY_INT); - break; - } + $this->save_session_data(); + } + /** + */ + public function __destruct() + { + parent::__destruct(); // destroys testproject manager + + unset($this->tc_mgr); + unset($this->testplan_mgr); + unset($this->platform_mgr); + unset($this->cfield_mgr); } - foreach ($this->all_filters as $name => $info) { - if (is_array($info)) { - $params[$name] = $info; - } + /** + * 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. + * + * @return bool + */ + 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 switch filter mode enabled? + $this->filter_mode_choice_enabled = false; + switch ($this->mode) { + case 'edit_mode': + break; + + default: + if (isset($this->configuration->advanced_filter_mode_choice) && + $this->configuration->advanced_filter_mode_choice == + self::ENABLED) { + $this->filter_mode_choice_enabled = true; + } + break; + } + + return tl::OK; } - I_PARAMS($params, $this->args); - $type = 'filter_keywords_filter_type'; - $this->args->{$type} = (isset($_REQUEST[$type])) ? trim($_REQUEST[$type]) : 'Or'; + /** + * 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() + { - // caller is needed for the logic to apply default values to filters when accessing - // from desktop/main page - $extra_keys = array('caller','filter_result_result','filter_result_method','filter_result_build'); + // some common user input is already read in parent class + parent::init_args(); - foreach ($extra_keys as $ek) { - $this->args->{$ek} = (isset($_REQUEST[$ek])) ? $_REQUEST[$ek] : null; - } + // add settings and filters to parameter info array for request parsers + $params = array(); - $this->args->{'filter_assigned_user_include_unassigned'} = - isset($_REQUEST['filter_assigned_user_include_unassigned']) ? 1 : 0; + foreach ($this->all_settings as $name => $info) { + if (is_array($info)) { + $params[$name] = $info; + } + } - // 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'])) { - $sent_token = $_REQUEST['form_token']; - } + // Do first get, to have info that can change config + I_PARAMS($params, $this->args); - 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; - $doLog = false; - switch ($this->mode) { - case 'plan_mode': - switch($this->args->feature) { - case 'planUpdateTC': - case 'test_urgency': - case 'tc_exec_assignment': - break; - - default: - $doLog = true; - break; - } - break; - - case 'edit_mode': - switch($this->args->feature) { - case 'edit_tc': - case 'keywordsAssign': - case 'assignReqs': - break; - - default: - $doLog = true; - break; - } - break; - } - if($doLog) { - tLog( __CLASS__ . ' :: Mode:' . $this->mode . ' - Wrong or missing GET argument: feature', 'ERROR'); - exit(); - } - - - } // end of method - - /** - * Initializes all settings. - * Iterates through all available settings and - * 1) adds an array to $this->settings for the active ones, - * 2) sets the rest to false - * - * so this can be checked from templates and elsewhere. - * Then calls the initializing method (init_$$$$) - * 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"; - //echo '
    '; echo $init_method; echo '
    '; - 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 situations - // the build setting is in plan mode only needed for one feature - if ($this->mode == 'plan_mode' && - ($this->args->feature != 'tc_exec_assignment' && $this->args->feature != 'test_urgency') ) { - $this->settings['setting_build'] = false; - $this->settings['setting_platform'] = 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. - $dummy = array('filter_keywords_filter_type','filter_result_result', - 'filter_result_method','filter_result_build', - 'filter_assigned_user_include_unassigned'); - - foreach ($dummy 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( $this->configuration->show_filters == ENABLED && - property_exists($this->configuration, $name) && $this->configuration->{$name} == ENABLED && - in_array($name, $this->mode_filter_mapping[$this->mode]) && method_exists($this, $init_method) ) - { - - switch($name) { - case 'filter_custom_fields': - $params = $this->mode == 'execution_mode' ? array('design' => true, 'testplan_design' => true) : null; - break; - - default: - $params=null; - break; - } - - // there is at least one filter item to display => switch panel on - $this->display_filters = true; - $this->$init_method($params); - } 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; - } - } + switch ($this->mode) { + case 'edit_mode': + $this->args->advanced_filter_mode = true; + break; + } - // special situation: the assigned user filter is in plan mode only needed for one feature - if ($this->mode == 'plan_mode' && $this->args->feature != 'tc_exec_assignment') { - $this->settings['filter_assigned_user'] = false; - } + if ($this->args->advanced_filter_mode) { + // 20160106 - fman + // it's not clear why we have choosen to do + // this check, because this makes that + // config option advanced_filter_mode_choice + // does not work as expected. + switch ($this->mode) { + case 'plan_add_mode': + case 'edit_mode': + $this->all_filters['filter_workflow_status'] = array( + "POST", + tlInputParameter::ARRAY_INT + ); + + $this->all_filters['filter_importance'] = array( + "POST", + tlInputParameter::ARRAY_INT + ); + break; + } + } + + 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'; + + // caller is needed for the logic to apply default values to filters when accessing + // from desktop/main page + $extra_keys = array( + 'caller', + 'filter_result_result', + 'filter_result_method', + 'filter_result_build' + ); + + foreach ($extra_keys as $ek) { + $this->args->{$ek} = (isset($_REQUEST[$ek])) ? $_REQUEST[$ek] : 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; - } + $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'])) { + $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; + $doLog = false; + switch ($this->mode) { + case 'plan_mode': + switch ($this->args->feature) { + case 'planUpdateTC': + case 'test_urgency': + case 'tc_exec_assignment': + break; + + default: + $doLog = true; + break; + } + break; + + case 'edit_mode': + switch ($this->args->feature) { + case 'edit_tc': + case 'keywordsAssign': + case 'assignReqs': + break; + + default: + $doLog = true; + break; + } + break; + } + if ($doLog) { + tLog( + __CLASS__ . ' :: Mode:' . $this->mode . + ' - Wrong or missing GET argument: feature', 'ERROR'); + exit(); + } } - } // 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) + + /** + * Initializes all settings. + * Iterates through all available settings and + * 1) adds an array to $this->settings for the active ones, + * 2) sets the rest to false + * + * so this can be checked from templates and elsewhere. + * Then calls the initializing method (init_$$$$) + * for each still active setting. + */ + protected function init_settings() { - 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($value = null) { - // update active_filters - if (!is_null($value)) { - $this->active_filters['testcases_to_show'] = $value; - } - - // 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)) { - unset($_SESSION[$this->mode][$token]); // too old, delete! - } - } + $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 + $this->settings[$name] = [ + "items" => null, + "selected" => - 1 + ]; + } + } + + // special situations + // the build setting is in plan mode only needed for one feature + if ($this->mode == 'plan_mode' && + ($this->args->feature != 'tc_exec_assignment' && + $this->args->feature != 'test_urgency')) { + $this->settings['setting_build'] = [ + "items" => null, + "selected" => - 1 + ]; + $this->settings['setting_platform'] = [ + "items" => null, + "selected" => - 1 + ]; + } + + // if at least one active setting is left to display, switch settings panel on + if ($at_least_one_active) { + $this->display_settings = true; + } } - } - - /** - * - * - */ - public function delete_own_session_data() - { - if (isset($_SESSION[$this->mode]) && isset($_SESSION[$this->mode][$this->form_token])) + + /** + * 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() { - 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; - - $key2loop = array('setting_build','setting_platform'); - foreach($key2loop as $kiwi) { - if($this->settings[$kiwi]) { - $string .= "&{$kiwi}={$this->settings[$kiwi]['selected']}"; - } - } - - if ($this->active_filters['filter_priority'] > 0) { - $string .= '&filter_priority=' . $this->active_filters['filter_priority']; - } - - - $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']; - } - - - // Need to undertand why for other filters that also are array - // we have choosen to serialize, and here not. - // may be to avoid more refactoring - if ($keyword_list) - { - $string .= '&filter_keywords=' . $keyword_list . - '&filter_keywords_filter_type=' . - $this->active_filters['filter_keywords_filter_type']; - } - - // Using serialization - if ($this->active_filters['filter_assigned_user']) - { - $string .= '&filter_assigned_user='. json_encode($this->active_filters['filter_assigned_user']) . - '&filter_assigned_user_include_unassigned=' . - ($this->active_filters['filter_assigned_user_include_unassigned'] ? '1' : '0'); - } - - if ($this->active_filters['filter_result_result']) - { - $string .= '&filter_result_result=' . json_encode($this->active_filters['filter_result_result']) . - '&filter_result_method=' . $this->active_filters['filter_result_method'] . - '&filter_result_build=' . $this->active_filters['filter_result_build']; - } - - if( !is_null($this->active_filters['filter_bugs'])) - { - $string .= '&' . http_build_query( array('filter_bugs' => $this->active_filters['filter_bugs'])); - } + // In resulting data structure, all values have to be defined (at least initialized), + // no matter wether they are wanted for filtering or not. + $dummy = [ + 'filter_keywords_filter_type', + 'filter_result_result', + 'filter_result_method', + 'filter_result_build', + 'filter_assigned_user_include_unassigned' + ]; + + foreach ($dummy as $filtername) { + $this->active_filters[$filtername] = null; + } - } - - 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(); - $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. - $opt_etree = $this->treeOpt[$this->mode]; - $filters->show_testsuite_contents = 1; - switch($this->args->feature) { - case 'test_urgency': - $filters->hide_testcases = 1; // ?? - $opt_etree->allow_empty_build = 1; - $opt_etree->hideTestCases = 1; - $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; - break; - - case 'tc_exec_assignment': - $filters->hide_testcases = 0; - $opt_etree->hideTestCases = 0; - $opt_etree->allow_empty_build = 0; - $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; - - // TICKET 4905: Test Case Tester Assignment - filters dont work properly - // for 'Assigned to' Field - // This way we are GOING TO IGNORE SETTING BUILD - $opt_etree->buildIDKeyMap = 'filter_result_build'; - - break; - - case 'planUpdateTC': - $filters->hide_testcases = 0; - $opt_etree->hideTestCases = 0; - $opt_etree->allow_empty_build = 1; - $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; - break; - - } - list($tree_menu, $testcases_to_show) = testPlanTree($this->db,$gui->menuUrl, - $this->args->testproject_id, - $this->args->testproject_name, - $this->args->testplan_id, - $this->args->testplan_name, - $filters,$opt_etree); - $this->set_testcases_to_show($testcases_to_show); - - $root_node = $tree_menu->rootnode; - $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; - - // improved cookiePrefix - all trees in plan mode show test cases - // assigned to a specified test plan -> store state for each feature and each project - // - // usage of wrong values in $this->args->xyz for cookiePrefix - // instead of correct values in $filters->setting_xyz - $cookie_prefix = $this->args->feature . "_tplan_id_" . $filters->setting_testplan ."_"; - 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 = true; - } - // improved cookiePrefix - - // all trees in edit mode show test cases of whole test project - // -> store state for each feature and each project - $cookie_prefix = $this->args->feature . "_tproject_id_" . $this->args->testproject_id ."_"; - - if ($this->do_filtering) { - // TICKET 4353: added active/inactive filter - $ignore_inactive_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - $ignore_active_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - if (isset($filters['filter_active_inactive'])) { - if ($filters['filter_active_inactive'] == IGNORE_INACTIVE_TESTCASES) { - $ignore_inactive_testcases = IGNORE_INACTIVE_TESTCASES; - } - if ($filters['filter_active_inactive'] == IGNORE_ACTIVE_TESTCASES) - { - $ignore_active_testcases = IGNORE_ACTIVE_TESTCASES; + // 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 ($this->configuration->show_filters == self::ENABLED && + property_exists($this->configuration, $name) && + $this->configuration->{$name} == self::ENABLED && + in_array($name, $this->mode_filter_mapping[$this->mode]) && + method_exists($this, $init_method)) { + + switch ($name) { + case 'filter_custom_fields': + $params = $this->mode == 'execution_mode' ? array( + 'design' => true, + 'testplan_design' => true + ) : null; + break; + + default: + $params = null; + break; + } + + // there is at least one filter item to display => switch panel on + $this->display_filters = true; + $this->$init_method($params); + } 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; } - } - $options = array('forPrinting' => NOT_FOR_PRINTING, - 'hideTestCases' => SHOW_TESTCASES, - 'tc_action_enabled' => DO_ON_TESTCASE_CLICK, - 'exclude_branches' => null, - 'ignore_inactive_testcases' => $ignore_inactive_testcases, - 'ignore_active_testcases' => $ignore_active_testcases); - - $forrest = generateTestSpecTree($this->db, - $this->args->testproject_id, - $this->args->testproject_name, - $gui->menuUrl, $filters, $options); - - - $this->set_testcases_to_show($forrest['leaves']); - $tree_menu = $forrest['menu']; - $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}&" . - "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->wrapOpen = $root_node->wrapClose = ''; - $root_node->testlink_node_type='testproject'; - } - break; - - case 'plan_add_mode': - // improved cookiePrefix - - // tree in plan_add_mode is only used for add/removed test cases features - // and shows all test cases defined within test project, - // but as test cases are added to a specified test plan -> store state for each test plan - // - // usage of wrong values in $this->args->xyz for cookiePrefix instead of correct - // values in $filters->setting_xyz - $cookie_prefix = "add_remove_tc_tplan_id_{$filters['setting_testplan']}_"; - - // get filter mode - $key = 'setting_testsgroupby'; - $mode = $this->args->$key; - - if ($this->do_filtering) { - // TICKET 4496: added active/inactive filter - // Will be refactored in future versions - // $ignore_inactive_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - // $ignore_active_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - // if ($filters['filter_active_inactive'] == IGNORE_INACTIVE_TESTCASES) - // { - // $ignore_inactive_testcases = IGNORE_INACTIVE_TESTCASES; - // } - // if ($filters['filter_active_inactive'] == IGNORE_ACTIVE_TESTCASES) - // { - // $ignore_active_testcases = IGNORE_ACTIVE_TESTCASES; - // } - // need to be refactored - $ignore_inactive_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - $ignore_active_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; - - $options = array('forPrinting' => NOT_FOR_PRINTING, - 'hideTestCases' => HIDE_TESTCASES, - 'tc_action_enabled' => ACTION_TESTCASE_DISABLE, - 'viewType' => 'testSpecTreeForTestPlan', - 'ignore_inactive_testcases' => $ignore_inactive_testcases, - 'ignore_active_testcases' => $ignore_active_testcases); - - - if ($mode == 'mode_test_suite') { - $tree_menu = generateTestSpecTree($this->db, - $this->args->testproject_id, - $this->args->testproject_name, - $gui->menuUrl,$filters,$options); - } - - $tree_menu = $tree_menu['menu']; - $root_node = $tree_menu->rootnode; - $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; - } - else - { - if ($mode == 'mode_test_suite') - { - $loader = $this->args->basehref . 'lib/ajax/gettprojectnodes.php?' . - "root_node={$this->args->testproject_id}&show_tcases=0" . - "&" . http_build_query(array('tsuiteHelp' => lang_get('display_tsuite_contents'))); - - $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->wrapOpen = ''; - $root_node->wrapClose = ''; - $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. - $filters->hide_testcases = false; - $filters->show_testsuite_contents = $this->configuration->exec_cfg->show_testsuite_contents; - - $exec_cfg = &$this->configuration->exec_cfg; - $opt_etree = new stdClass(); - $opt_etree->useCounters = $exec_cfg->enable_tree_testcase_counters; - - $opt_etree->useColours = new stdClass(); - $opt_etree->useColours->testcases = $exec_cfg->enable_tree_testcases_colouring; - $opt_etree->useColours->counters = $exec_cfg->enable_tree_counters_colouring; - $opt_etree->testcases_colouring_by_selected_build = $exec_cfg->testcases_colouring_by_selected_build; - - if($this->mode == 'execution_mode') { - $opt_etree->actionJS['testproject'] = 'EXDS'; - $opt_etree->exec_tree_counters_logic = - $this->args->setting_exec_tree_counters_logic; - } - - list($tree_menu, $testcases_to_show) = - execTree($this->db,$gui->menuUrl, - array('tproject_id' => $this->args->testproject_id, - 'tproject_name' => $this->args->testproject_name, - 'tplan_id' => $this->args->testplan_id, - 'tplan_name' => $this->args->testplan_name), - $filters,$opt_etree); - - $this->set_testcases_to_show($testcases_to_show); - - $root_node = $tree_menu->rootnode; - $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; - - // - // improved cookiePrefix - - // tree on test execution shows test cases depending on test plan, platform and build. - // Because test plan is implicitily given with build -> store state for each (platform-build) - // combination - // - // Usage of wrong values in $this->args->xyz for cookiePrefix - // instead of correct values in $filters->setting_xyz - // - $cookie_prefix = 'test_exec_build_id_' . $filters->setting_build . '_'; - if (isset($filters->setting_platform)) { - $cookie_prefix .= 'platform_id_' . $filters->setting_platform . '_'; } - 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 + // special situation: the assigned user filter is in plan mode only needed for one feature + if ($this->mode == 'plan_mode' && + $this->args->feature != 'tc_exec_assignment') { + $this->settings['filter_assigned_user'] = false; + } + // 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; + } + } + } - /** - * - * - */ - private function init_setting_build() { + /** + * 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; + } + } - $key = 'setting_build'; - if (is_null($this->testplan_mgr)) { - $this->testplan_mgr = new testplan($this->db); + return $value; } - $tplan_id = $this->settings['setting_testplan']['selected']; + /** + */ + public function set_testcases_to_show($value = null) + { + // update active_filters + if (! is_null($value)) { + $this->active_filters['testcases_to_show'] = $value; + } - switch( $this->mode ) { - case 'plan_mode': - $active = $open = null; - if( $this->configuration->setting_build_inactive_out ) { - $active = testplan::GET_ACTIVE_BUILD; - } + // 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(); + } - if( $this->configuration->setting_build_close_out ) { - $open = testplan::GET_OPEN_BUILD; - } - break; + $_SESSION[$this->mode][$this->form_token] = $this->active_filters; + $_SESSION[$this->mode][$this->form_token]['timestamp'] = time(); - default: - $active = testplan::GET_ACTIVE_BUILD; - $open = testplan::GET_OPEN_BUILD; - break; + return tl::OK; } - - $this->settings[$key]['items'] = $this->testplan_mgr->get_builds_for_html_options($tplan_id, $active, $open); - $tplan_builds = array_keys((array)$this->settings[$key]['items']); - // According to 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); + /** + * 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) + { - $session_key = $tplan_id . '_stored_setting_build'; - $session_selection = isset($_SESSION[$session_key]) ? $_SESSION[$session_key] : null; + // TODO this duration could maybe also be configured in config/const.inc.php - $this->args->{$key} = $this->args->{$key} > 0 ? $this->args->{$key} : $session_selection; + // 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 + } - if (!$this->args->$key) - { - $this->args->$key = $newest_build_id; + // 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)) { + unset($_SESSION[$this->mode][$token]); // too old, delete! + } + } + } } - - // only take build ID into account if it really is a build from this testplan - $this->settings[$key]['selected'] = (in_array($this->args->$key, (array)$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'])) + /** + */ + public function delete_own_session_data() { - $this->settings[$key]['selected'] = end($tplan_builds); + if (isset($_SESSION[$this->mode]) && + isset($_SESSION[$this->mode][$this->form_token])) { + unset($_SESSION[$this->mode][$this->form_token]); + } } - $_SESSION[$session_key] = $this->settings[$key]['selected']; - } // end of method + /** + * 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; + + $key2loop = [ + 'setting_build', + 'setting_platform' + ]; + foreach ($key2loop as $kiwi) { + if ($this->settings[$kiwi]) { + $string .= "&{$kiwi}={$this->settings[$kiwi]['selected']}"; + } + } + if ($this->active_filters['filter_priority'] > 0) { + $string .= '&filter_priority=' . + $this->active_filters['filter_priority']; + } - /** - * - * @used-by: tlTestCaseFilterControl->init_settings() - */ - 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); + $keyword_list = null; + if (is_array($this->active_filters['filter_keywords'])) { + $keyword_list = implode(',', + $this->active_filters['filter_keywords']); + } elseif ($this->active_filters['filter_keywords']) { + $keyword_list = $this->active_filters['filter_keywords']; + } - 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; + // Need to undertand why for other filters that also are array + // we have choosen to serialize, and here not. + // may be to avoid more refactoring + if ($keyword_list) { + $string .= '&filter_keywords=' . $keyword_list . + '&filter_keywords_filter_type=' . + $this->active_filters['filter_keywords_filter_type']; + } + + // Using serialization + if ($this->active_filters['filter_assigned_user']) { + $string .= '&filter_assigned_user=' . + json_encode($this->active_filters['filter_assigned_user']) . + '&filter_assigned_user_include_unassigned=' . + ($this->active_filters['filter_assigned_user_include_unassigned'] ? '1' : '0'); + } + + if ($this->active_filters['filter_result_result']) { + $string .= '&filter_result_result=' . + json_encode($this->active_filters['filter_result_result']) . + '&filter_result_method=' . + $this->active_filters['filter_result_method'] . + '&filter_result_build=' . + $this->active_filters['filter_result_build']; + } - // check if user is allowed to set chosen testplan before changing - foreach ($testplans as $plan) { - if ($plan['id'] == $this->args->{$key}) { - setSessionTestPlan($plan); + if (! is_null($this->active_filters['filter_bugs'])) { + $string .= '&' . + http_build_query( + array( + 'filter_bugs' => $this->active_filters['filter_bugs'] + )); + } } - } - } - // 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']; - - // Final filtering based on mode: - // Now get all selectable testplans for the user to display. - // For execution: - // For assign test case execution feature: - // 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' || - ( $this->mode == 'plan_mode' && $this->args->feature != 'tc_exec_assignment' ) ; - - 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']; - } - } - } - - /** - * - * Possibility to filter by Platform: - * according mode we need to add [Any] option - * it's really a filter? - * - */ - private function init_setting_platform() { - if (!$this->platform_mgr) { - $this->platform_mgr = new tlPlatform($this->db); - } + 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(); + $loader = ''; + $children = "[]"; + $cookie_prefix = ''; + $root_node = new stdClass(); + + // 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. + $opt_etree = $this->treeOpt[$this->mode]; + $filters->show_testsuite_contents = 1; + switch ($this->args->feature) { + case 'test_urgency': + $filters->hide_testcases = 1; // ?? + $opt_etree->allow_empty_build = 1; + $opt_etree->hideTestCases = 1; + $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; + break; + + case 'tc_exec_assignment': + $filters->hide_testcases = 0; + $opt_etree->hideTestCases = 0; + $opt_etree->allow_empty_build = 0; + $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; + + // TICKET 4905: Test Case Tester Assignment - filters dont work properly + // for 'Assigned to' Field + // This way we are GOING TO IGNORE SETTING BUILD + $opt_etree->buildIDKeyMap = 'filter_result_build'; + + break; + + case 'planUpdateTC': + $filters->hide_testcases = 0; + $opt_etree->hideTestCases = 0; + $opt_etree->allow_empty_build = 1; + $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; + break; + } + list ($tree_menu, $testcases_to_show) = testPlanTree($this->db, + $gui->menuUrl, $this->args->testproject_id, + $this->args->testproject_name, $this->args->testplan_id, + $this->args->testplan_name, $filters, $opt_etree); + $this->set_testcases_to_show($testcases_to_show); + + $root_node = $tree_menu->rootnode; + $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; + + // improved cookiePrefix - all trees in plan mode show test cases + // assigned to a specified test plan -> store state for each feature and each project + // + // usage of wrong values in $this->args->xyz for cookiePrefix + // instead of correct values in $filters->setting_xyz + $cookie_prefix = $this->args->feature . "_tplan_id_" . + $filters->setting_testplan . "_"; + 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 = true; + } + // improved cookiePrefix - + // all trees in edit mode show test cases of whole test project + // -> store state for each feature and each project + $cookie_prefix = $this->args->feature . "_tproject_id_" . + $this->args->testproject_id . "_"; + + if ($this->do_filtering) { + // TICKET 4353: added active/inactive filter + $ignore_inactive_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; + $ignore_active_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; + if (isset($filters['filter_active_inactive'])) { + if ($filters['filter_active_inactive'] == + IGNORE_INACTIVE_TESTCASES) { + $ignore_inactive_testcases = IGNORE_INACTIVE_TESTCASES; + } + if ($filters['filter_active_inactive'] == + IGNORE_ACTIVE_TESTCASES) { + $ignore_active_testcases = IGNORE_ACTIVE_TESTCASES; + } + } + $options = array( + 'forPrinting' => NOT_FOR_PRINTING, + 'hideTestCases' => SHOW_TESTCASES, + 'tc_action_enabled' => DO_ON_TESTCASE_CLICK, + 'exclude_branches' => null, + 'ignore_inactive_testcases' => $ignore_inactive_testcases, + 'ignore_active_testcases' => $ignore_active_testcases + ); + + $forrest = generateTestSpecTree($this->db, + $this->args->testproject_id, + $this->args->testproject_name, $gui->menuUrl, $filters, + $options); + + $this->set_testcases_to_show($forrest['leaves']); + $tree_menu = $forrest['menu']; + $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}&" . "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->wrapOpen = $root_node->wrapClose = ''; + $root_node->testlink_node_type = 'testproject'; + } + break; + + case 'plan_add_mode': + // improved cookiePrefix - + // tree in plan_add_mode is only used for add/removed test cases features + // and shows all test cases defined within test project, + // but as test cases are added to a specified test plan -> store state for each test plan + // + // usage of wrong values in $this->args->xyz for cookiePrefix instead of correct + // values in $filters->setting_xyz + $cookie_prefix = "add_remove_tc_tplan_id_{$filters['setting_testplan']}_"; + + // get filter mode + $key = 'setting_testsgroupby'; + $mode = $this->args->$key; + + if ($this->do_filtering) { + // TICKET 4496: added active/inactive filter + // Will be refactored in future versions + // $ignore_inactive_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; + // $ignore_active_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; + // if ($filters['filter_active_inactive'] == IGNORE_INACTIVE_TESTCASES) + // { + // $ignore_inactive_testcases = IGNORE_INACTIVE_TESTCASES; + // } + // if ($filters['filter_active_inactive'] == IGNORE_ACTIVE_TESTCASES) + // { + // $ignore_active_testcases = IGNORE_ACTIVE_TESTCASES; + // } + // need to be refactored + $ignore_inactive_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; + $ignore_active_testcases = DO_NOT_FILTER_INACTIVE_TESTCASES; + + $options = array( + 'forPrinting' => NOT_FOR_PRINTING, + 'hideTestCases' => HIDE_TESTCASES, + 'tc_action_enabled' => ACTION_TESTCASE_DISABLE, + 'viewType' => 'testSpecTreeForTestPlan', + 'ignore_inactive_testcases' => $ignore_inactive_testcases, + 'ignore_active_testcases' => $ignore_active_testcases + ); + + if ($mode == 'mode_test_suite') { + $tree_menu = generateTestSpecTree($this->db, + $this->args->testproject_id, + $this->args->testproject_name, $gui->menuUrl, + $filters, $options); + } + + $tree_menu = $tree_menu['menu']; + $root_node = $tree_menu->rootnode; + $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; + } else { + if ($mode == 'mode_test_suite') { + $loader = $this->args->basehref . + 'lib/ajax/gettprojectnodes.php?' . + "root_node={$this->args->testproject_id}&show_tcases=0" . + "&" . + http_build_query( + array( + 'tsuiteHelp' => lang_get( + 'display_tsuite_contents') + )); + + $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->wrapOpen = ''; + $root_node->wrapClose = ''; + $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. + $filters->hide_testcases = false; + $filters->show_testsuite_contents = $this->configuration->exec_cfg->show_testsuite_contents; + + $exec_cfg = &$this->configuration->exec_cfg; + $opt_etree = new stdClass(); + $opt_etree->useCounters = $exec_cfg->enable_tree_testcase_counters; + + $opt_etree->useColours = new stdClass(); + $opt_etree->useColours->testcases = $exec_cfg->enable_tree_testcases_colouring; + $opt_etree->useColours->counters = $exec_cfg->enable_tree_counters_colouring; + $opt_etree->testcases_colouring_by_selected_build = $exec_cfg->testcases_colouring_by_selected_build; + + if ($this->mode == 'execution_mode') { + $opt_etree->actionJS['testproject'] = 'EXDS'; + $opt_etree->exec_tree_counters_logic = $this->args->setting_exec_tree_counters_logic; + } + + list ($tree_menu, $testcases_to_show) = execTree($this->db, + $gui->menuUrl, + array( + 'tproject_id' => $this->args->testproject_id, + 'tproject_name' => $this->args->testproject_name, + 'tplan_id' => $this->args->testplan_id, + 'tplan_name' => $this->args->testplan_name + ), $filters, $opt_etree); + + $this->set_testcases_to_show($testcases_to_show); + + $root_node = $tree_menu->rootnode; + $children = $tree_menu->menustring ? $tree_menu->menustring : "[]"; + + // + // improved cookiePrefix - + // tree on test execution shows test cases depending on test plan, platform and build. + // Because test plan is implicitily given with build -> store state for each (platform-build) + // combination + // + // Usage of wrong values in $this->args->xyz for cookiePrefix + // instead of correct values in $filters->setting_xyz + // + + // does this plan has platforms? + $cookie_prefix = 'test_exec_build_id_' . $filters->setting_build . + '_'; + if (isset($filters->setting_platform)) { + $cookie_prefix .= 'platform_id_' . $filters->setting_platform . + '_'; + } + break; + } - $testplan_id = $this->settings['setting_testplan']['selected']; - $session_key = $testplan_id . '_stored_setting_platform'; - $session_selection = isset($_SESSION[$session_key]) ? $_SESSION[$session_key] : null; - $key = 'setting_platform'; + $gui->tree = $tree_menu; - $optx = null; - switch ($this->mode) { - case 'edit_mode': - case 'plan_add_mode': - break; - } - - $platformSet = $this->platform_mgr->getLinkedToTestplanAsMap($testplan_id); - - if( is_null($platformSet) ) { - // Brute force bye, bye !! >>---> - $this->settings[$key] = false; - $_SESSION[$session_key] = null; - return; - } - - // Ok, there are platforms, go ahead - $this->settings[$key] = array('items' => null, 'selected' => -1); - if( is_null($this->args->$key) ) { - $this->args->$key = intval($session_selection); - } - - - switch($this->mode) { - case 'plan_mode': - $this->settings[$key]['items'] = array(0 => $this->option_strings['any']); - $this->settings[$key]['items'] += $platformSet; - break; - - case 'execution_mode': - $this->settings[$key]['items'] = $platformSet; - break; - - default: - throw new Exception(__METHOD__ . "Mode:" . $this->mode . 'Do not know what to do', 1); - break; + $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; } - // If this platform is NOT valid for Test plan, I will set the first one - // (is any exists). - if( !isset($this->settings[$key]['items']) ) { - $this->args->$key = key($this->settings[$key]['items']); - } - - $this->settings[$key]['selected'] = $this->args->$key; - if($this->args->$key <= 0) { - $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 + /** + */ + public function init_setting_refresh_tree_on_action() { - $this->do_filtering = true; + $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; + } elseif (isset($this->args->{$hidden_key})) { + $selection = 0; + } elseif (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; } - - // this filter should only be visible if there are any top level testsuites - $this->filters[$key] = null; - 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 + + /** + */ + public function init_setting_build() { - $this->active_filters[$key] = null; - } - } // end of method - - /** - * - * mode this affect domain - */ - private function init_filter_keywords() { - $key = 'filter_keywords'; - $type = 'filter_keywords_filter_type'; - $this->filters[$key] = false; - $keywords = null; - $l10n = init_labels(array('logical_or' => null,'logical_and' => null, 'not_linked' => null)); - - switch ($this->mode) { - case 'edit_mode': - case 'plan_add_mode': - // we need the keywords for the whole testproject - if (!$this->testproject_mgr) { - $this->testproject_mgr = new testproject($this->db); - } - $keywords = $this->testproject_mgr->getUsedKeywordsMap($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); + $key = 'setting_build'; + if (is_null($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; - } - $special = array('domain' => array(), 'filter_mode' => array()); - switch($this->mode) { - case 'edit_mode': - $special['domain'] = - array(-1 => $this->option_strings['without_keywords'], - 0 => $this->option_strings['any']); - $special['filter_mode'] = array('NotLinked' => $l10n['not_linked']); - break; - - case 'execution_mode': - case 'plan_add_mode': - case 'plan_mode': - default: - $special['domain'] = array(0 => $this->option_strings['any']); - $special['filter_mode'] = array(); - break; - } + switch ($this->mode) { + case 'plan_mode': + $active = $open = null; + if ($this->configuration->setting_build_inactive_out) { + $active = testplan::GET_ACTIVE_BUILD; + } + + if ($this->configuration->setting_build_close_out) { + $open = testplan::GET_OPEN_BUILD; + } + break; + + default: + $active = testplan::GET_ACTIVE_BUILD; + $open = testplan::GET_OPEN_BUILD; + break; + } + + $this->settings[$key]['items'] = $this->testplan_mgr->get_builds_for_html_options( + $tplan_id, $active, $open); + $tplan_builds = array_keys((array) $this->settings[$key]['items']); + + // According to 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); + + $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, + (array) $tplan_builds)) ? $this->args->$key : $newest_build_id; - $selection = $this->args->{$key}; - $type_selection = $this->args->{$type}; - - // are there any keywords? - $atLeastOneKW = !is_null($keywords) && count($keywords); - if ($atLeastOneKW) { - $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'] = $special['domain'] + $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' => $l10n['logical_or'], - 'And' => $l10n['logical_and']) + - $special['filter_mode']; - $this->filters[$key][$type]['selected'] = $type_selection; + // still no build selected? take first one from selection. + if (empty($this->settings[$key]['selected']) && + empty($this->settings[$key]['items'])) { + $this->settings[$key]['selected'] = end($tplan_builds); + } + + $_SESSION[$session_key] = $this->settings[$key]['selected']; } - - if ($atLeastOneKW) { - // set the active value to filter - // delete keyword filter if "any" (0) is part of the selection - regardless of filter mode - if (is_array($this->filters[$key]['selected']) && in_array(0, $this->filters[$key]['selected'])) { - $this->active_filters[$key] = null; - } else { - $this->active_filters[$key] = $this->filters[$key]['selected']; - } - $this->active_filters[$type] = $selection ? $type_selection : null; - } else { - $this->active_filters[$key] = 0; - } - } - - - - // TICKET 4353: added active/inactive filter - private function init_filter_active_inactive() { - $key = 'filter_active_inactive'; - - $items = array(DO_NOT_FILTER_INACTIVE_TESTCASES => $this->option_strings['any'], - IGNORE_INACTIVE_TESTCASES => lang_get('show_only_active_testcases'), - IGNORE_ACTIVE_TESTCASES => lang_get('show_only_inactive_testcases')); - - $selection = $this->args->{$key}; - - if (!$selection || $this->args->reset_filters) - { - $selection = null; - } - else + + /** + * + * @used-by: tlTestCaseFilterControl->init_settings() + */ + public function init_setting_testplan() { - $this->do_filtering = true; + 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']; + + // Final filtering based on mode: + // Now get all selectable testplans for the user to display. + // For execution: + // For assign test case execution feature: + // 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' || + ($this->mode == 'plan_mode' && + $this->args->feature != 'tc_exec_assignment'); + + 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']; + } + } } - $this->filters[$key] = array('items' => $items, 'selected' => $selection); - $this->active_filters[$key] = $selection; - } - - - /** - * - */ - private function init_filter_importance() - { - // show this filter only if test priority management is enabled - $key = 'filter_importance'; - $this->active_filters[$key] = null; - $this->filters[$key] = false; - - if (!$this->testproject_mgr) + /** + * Possibility to filter by Platform: + * according mode we need to add [Any] option + * it's really a filter? + */ + public function init_setting_platform() { - $this->testproject_mgr = new testproject($this->db); + if (! $this->platform_mgr) { + $this->platform_mgr = new tlPlatform($this->db); + } + + $testplan_id = $this->settings['setting_testplan']['selected']; + $session_key = $testplan_id . '_stored_setting_platform'; + $session_selection = isset($_SESSION[$session_key]) ? $_SESSION[$session_key] : null; + $key = 'setting_platform'; + + switch ($this->mode) { + case 'edit_mode': + case 'plan_add_mode': + break; + } + + $platformSet = $this->platform_mgr->getLinkedToTestplanAsMap( + $testplan_id); + if (is_null($platformSet) || count($platformSet) == 0) { + // Brute force bye, bye !! >>---> + $this->settings[$key] = [ + 'items' => null, + 'selected' => - 1 + ]; + $_SESSION[$session_key] = null; + return; + } + + // Ok, there are platforms, go ahead + $this->settings[$key] = [ + 'items' => null, + 'selected' => - 1 + ]; + + if (is_null($this->args->$key)) { + $this->args->$key = intval($session_selection); + } + + switch ($this->mode) { + case 'plan_mode': + $this->settings[$key]['items'] = [ + 0 => $this->option_strings['any'] + ]; + $this->settings[$key]['items'] += $platformSet; + break; + + case 'execution_mode': + $this->settings[$key]['items'] = $platformSet; + break; + + default: + throw new Exception( + __METHOD__ . "Mode:" . $this->mode . 'Do not know what to do', + 1); + break; + } + + // If this platform is NOT valid for Test plan, I will set the first one + // (is any exists). + if (! isset($this->settings[$key]['items'])) { + $this->args->$key = key($this->settings[$key]['items']); + } + + $this->settings[$key]['selected'] = $this->args->$key; + if ($this->args->$key <= 0) { + $this->settings[$key]['selected'] = key( + $this->settings[$key]['items']); + } + + $_SESSION[$session_key] = $this->settings[$key]['selected']; } - $tp_info = $this->testproject_mgr->get_by_id($this->args->testproject_id); - $enabled = $tp_info['opt']->testPriorityEnabled; - if ($enabled) + /** + */ + public function init_filter_tc_id() { - $selection = $this->args->{$key}; - if (!$selection || $this->args->reset_filters) - { - $selection = null; - } - else - { - $this->do_filtering = true; - } - - - $this->filters[$key] = array('selected' => $selection); - - // Only drawback: no new user defined importance can be managed - // may be is a good design choice - $this->filters[$key]['items'] = array(0 => $this->option_strings['any'], - HIGH => lang_get('high_importance'), - MEDIUM => lang_get('medium_importance'), - LOW => lang_get('low_importance')); - - $this->filters[$key]['size'] = sizeof($this->filters[$key]['items']); - $this->active_filters[$key] = $selection; + $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 + $oget = [ + 'tproject_id' => $this->args->testproject_id + ]; + $internal_id = $this->tc_mgr->getInternalID($selection, $oget); + } + + $this->filters[$key] = array( + 'selected' => $selection ? $selection : $tc_prefix + ); + $this->active_filters[$key] = $internal_id; } - } - - - /** - * - * - */ - private function init_filter_priority() - { - // This is a special case of filter: the menu items don't get initialized here, - // they are available as a global smarty variable. So the only thing to be managed - // here is the selection by user. - $key = 'filter_priority'; - - if (!$this->testproject_mgr) + + /** + */ + public function init_filter_testcase_name() { - $this->testproject_mgr = new testproject($this->db); + $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; } - - $tp_info = $this->testproject_mgr->get_by_id($this->args->testproject_id); - $enabled = $tp_info['opt']->testPriorityEnabled; - - $this->active_filters[$key] = null; - $this->filters[$key] = false; - - if ($enabled) + + /** + */ + public function init_filter_toplevel_testsuite() { - $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_execution_type() - { - if (!$this->tc_mgr) { - $this->tc_mgr = new testcase($this->db); - } - $key = 'filter_execution_type'; - - $selection = $this->args->{$key}; - // handle filter reset - if (!$selection || $this->args->reset_filters) { - $selection = null; - } else { - $this->do_filtering = true; - } - - $this->filters[$key] = array('items' => array(), 'selected' => $selection); - - // load available execution types - // add "any" string to these types at index 0 as default selection - $this->filters[$key]['items'] = $this->tc_mgr->get_execution_types(); - $this->filters[$key]['items'] = array(0 => $this->option_strings['any']) - + $this->filters[$key]['items']; - - $this->active_filters[$key] = $selection; - } // end of method - - /** - * - */ - private function init_filter_assigned_user() { - if (!$this->testproject_mgr) { - $this->testproject_mgr = new testproject($this->db); - } + 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; + } - $key = 'filter_assigned_user'; - $unassigned_key = 'filter_assigned_user_include_unassigned'; - $tplan_id = $this->settings['setting_testplan']['selected']; + // this filter should only be visible if there are any top level testsuites + $this->filters[$key] = null; + 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'; + } + } - // set selection to default (any), only change if value is sent by user and reset is not requested - $selection = $this->args->{$key}; - if (!$selection || $this->args->reset_filters) { - $selection = null; - } else { - $this->do_filtering = true; + // 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; + } } - $tproject_info = $this->testproject_mgr->get_by_id($this->args->testproject_id); - - $all_testers = getTestersForHtmlOptions($this->db, $tplan_id, $tproject_info, null, - array(TL_USER_ANYBODY => $this->option_strings['any'], - TL_USER_NOBODY => $this->option_strings['none'], - TL_USER_SOMEBODY => $this->option_strings['somebody']), - 'any'); - $visible_testers = $all_testers; - - // in execution mode the rights of the user have to be regarded - if ($this->mode == 'execution_mode') + /** + * mode this affect domain + */ + public function init_filter_keywords() { - $role = $this->user->getEffectiveRole($this->db, $this->args->testproject_id, $tplan_id); - - $simple_tester_roles = array_flip($this->configuration->exec_cfg->simple_tester_roles); - - // check the user's rights to see what he may do - $right_to_execute = $role->hasRight('testplan_execute'); - $right_to_manage = $role->hasRight('testplan_planning'); - - $simple = false; - if (isset($simple_tester_roles[$role->dbID]) || ($right_to_execute && !$right_to_manage)) { - // user is only simple tester and may not see/execute everything - $simple = true; - } - - $view_mode = $simple ? $this->configuration->exec_cfg->view_mode->tester : 'all'; - - if ($view_mode != 'all') { - $visible_testers = (array)$this->user->getDisplayName(); - $selection = (array)$this->user->dbID; - } - - // re-enable option "user_filter_default" - if (!$selection && $this->configuration->exec_cfg->user_filter_default == 'logged_user') { - $selection = (array)$this->user->dbID; - } - } - - $this->filters[$key] = array('items' => $visible_testers, - 'selected' => $selection, - $unassigned_key => $this->args->{$unassigned_key}); - - // which value shall be passed to tree generation class? - - if ((is_array($selection) && in_array(TL_USER_ANYBODY, $selection)) - || ($selection == TL_USER_ANYBODY)) { - // delete user assignment filter if "any user" is part of the selection - $this->active_filters[$key] = null; - $this->active_filters[$unassigned_key] = 0; - } - - if (is_array($selection)) { - // get keys of the array as values - $this->active_filters[$key] = array_flip($selection); - foreach ($this->active_filters[$key] as $user_key => $user_value) { - $this->active_filters[$key][$user_key] = $user_key; - } - $this->active_filters[$unassigned_key] = $this->filters[$key][$unassigned_key]; + $key = 'filter_keywords'; + $type = 'filter_keywords_filter_type'; + $this->filters[$key] = false; + $keywords = null; + $l10n = init_labels( + array( + 'logical_or' => null, + 'logical_and' => null, + 'not_linked' => null + )); + + switch ($this->mode) { + case 'edit_mode': + case 'plan_add_mode': + // we need the keywords for the whole testproject + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + $keywords = $this->testproject_mgr->getUsedKeywordsMap( + $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; + } + + $special = array( + 'domain' => array(), + 'filter_mode' => array() + ); + switch ($this->mode) { + case 'edit_mode': + $special['domain'] = array( + - 1 => $this->option_strings['without_keywords'], + 0 => $this->option_strings['any'] + ); + $special['filter_mode'] = array( + 'NotLinked' => $l10n['not_linked'] + ); + break; + + case 'execution_mode': + case 'plan_add_mode': + case 'plan_mode': + default: + $special['domain'] = array( + 0 => $this->option_strings['any'] + ); + $special['filter_mode'] = array(); + break; + } + + $selection = $this->args->{$key}; + $type_selection = $this->args->{$type}; + + // are there any keywords? + $atLeastOneKW = ! is_null($keywords) && count($keywords); + if ($atLeastOneKW) { + $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'] = $special['domain'] + $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' => $l10n['logical_or'], + 'And' => $l10n['logical_and'] + ) + $special['filter_mode']; + $this->filters[$key][$type]['selected'] = $type_selection; + } + + if ($atLeastOneKW) { + // set the active value to filter + // delete keyword filter if: + // - "any" (0) is part of the selection AND + // - there are several elements in the array (user selection) + // !!Regardless of filter mode!! + // + if (is_array($this->filters[$key]['selected']) && + count($this->filters[$key]['selected']) > 1 && + in_array(0, $this->filters[$key]['selected'])) { + $this->active_filters[$key] = null; + } else { + $this->active_filters[$key] = $this->filters[$key]['selected']; + } + $this->active_filters[$type] = $selection ? $type_selection : null; + } else { + $this->active_filters[$key] = 0; + } } - } // end of method - - - /** - * - */ - private function init_filter_result() { - $result_key = 'filter_result_result'; - $method_key = 'filter_result_method'; - $build_key = 'filter_result_build'; - - if (is_null($this->testplan_mgr)) + + // TICKET 4353: added active/inactive filter + public function init_filter_active_inactive() { - $this->testplan_mgr = new testplan($this->db); + $key = 'filter_active_inactive'; + + $items = array( + DO_NOT_FILTER_INACTIVE_TESTCASES => $this->option_strings['any'], + IGNORE_INACTIVE_TESTCASES => lang_get('show_only_active_testcases'), + IGNORE_ACTIVE_TESTCASES => lang_get('show_only_inactive_testcases') + ); + + $selection = $this->args->{$key}; + + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'items' => $items, + 'selected' => $selection + ); + $this->active_filters[$key] = $selection; } - $tplan_id = $this->settings['setting_testplan']['selected']; - - $this->configuration->results = config_get('results'); - - // determine, which config to load and use for filter methods - depends on mode! - $cfg = ($this->mode == 'execution_mode') ? - 'execution_filter_methods' : 'execution_assignment_filter_methods'; - $this->configuration->filter_methods = config_get($cfg); - - // - // CRITIC - Differences bewteen this configuration and - // (file const.inc.php) - // $tlCfg->execution_filter_methods['default_type'] - // $tlCfg->execution_assignment_filter_methods['default_type'] - // - // Will create issues: you will see an string on HTML SELECT, but code - // returned on submit will not code for string you are seeing.!!!! - // - // determine which filter method shall be selected by the JS function in template, - // when only one build is selectable by the user - $js_key_to_select = 0; - if ($this->mode == 'execution_mode') - { - $js_key_to_select = $this->configuration->filter_methods['status_code']['current_build']; - } - else if ($this->mode == 'plan_mode') + + /** + */ + public function init_filter_importance() { - $js_key_to_select = $this->configuration->filter_methods['status_code']['specific_build']; + // show this filter only if test priority management is enabled + $key = 'filter_importance'; + $this->active_filters[$key] = null; + $this->filters[$key] = false; + + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + $tp_info = $this->testproject_mgr->get_by_id( + $this->args->testproject_id); + $enabled = $tp_info['opt']->testPriorityEnabled; + + if ($enabled) { + $selection = $this->args->{$key}; + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'selected' => $selection + ); + + // Only drawback: no new user defined importance can be managed + // may be is a good design choice + $this->filters[$key]['items'] = array( + 0 => $this->option_strings['any'], + HIGH => lang_get('high_importance'), + MEDIUM => lang_get('medium_importance'), + LOW => lang_get('low_importance') + ); + + $this->filters[$key]['size'] = count($this->filters[$key]['items']); + $this->active_filters[$key] = $selection; + } } - - // values selected by user - $result_selection = $this->args->$result_key; - $method_selection = $this->args->$method_key; - $build_selection = $this->args->$build_key; - - // default values - $default_filter_method = $this->configuration->filter_methods['default_type']; - $any_result_key = $this->configuration->results['status_code']['all']; - $newest_build_id = $this->testplan_mgr->get_max_build_id($tplan_id, testplan::GET_ACTIVE_BUILD); - - if (is_null($method_selection)) + + /** + */ + public function init_filter_priority() { - $method_selection = $default_filter_method; + // This is a special case of filter: the menu items don't get initialized here, + // they are available as a global smarty variable. So the only thing to be managed + // here is the selection by user. + $key = 'filter_priority'; + + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + + $tp_info = $this->testproject_mgr->get_by_id( + $this->args->testproject_id); + $enabled = $tp_info['opt']->testPriorityEnabled; + + $this->active_filters[$key] = null; + $this->filters[$key] = false; + + if ($enabled) { + $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; + } } - if (is_null($result_selection) || $this->args->reset_filters) - // if ($this->args->reset_filters) + /** + */ + public function init_filter_execution_type() { - // no selection yet or filter reset requested - $result_selection = $any_result_key; - $method_selection = $default_filter_method; - $build_selection = $newest_build_id; - } - else - { - $this->do_filtering = true; + if (! $this->tc_mgr) { + $this->tc_mgr = new testcase($this->db); + } + $key = 'filter_execution_type'; + + $selection = $this->args->{$key}; + // handle filter reset + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'items' => array(), + 'selected' => $selection + ); + + // load available execution types + // add "any" string to these types at index 0 as default selection + $this->filters[$key]['items'] = $this->tc_mgr->get_execution_types(); + $this->filters[$key]['items'] = array( + 0 => $this->option_strings['any'] + ) + $this->filters[$key]['items']; + + $this->active_filters[$key] = $selection; } - - // init array structure - $key = 'filter_result'; - $this->filters[$key] = array($result_key => array('items' => null, - 'selected' => $result_selection), - $method_key => array('items' => array(), - 'selected' => $method_selection, - 'js_selection' => $js_key_to_select), - $build_key => array('items' => null, - 'selected' => $build_selection)); - - // init menu for result selection by function from exec.inc.php - $this->filters[$key][$result_key]['items'] = createResultsMenu(); - $this->filters[$key][$result_key]['items'][$any_result_key] = $this->option_strings['any']; - - // init menu for filter method selection - foreach ($this->configuration->filter_methods['status_code'] as $statusname => $statusshortcut) + + /** + */ + public function init_filter_assigned_user() { - $code = $this->configuration->filter_methods['status_code'][$statusname]; - $this->filters[$key][$method_key]['items'][$code] = - lang_get($this->configuration->filter_methods['status_label'][$statusname]); + if (! $this->testproject_mgr) { + $this->testproject_mgr = new testproject($this->db); + } + + $key = 'filter_assigned_user'; + $unassigned_key = 'filter_assigned_user_include_unassigned'; + $tplan_id = $this->settings['setting_testplan']['selected']; + + // set selection to default (any), only change if value is sent by user and reset is not requested + $selection = $this->args->{$key}; + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + + $tproject_info = $this->testproject_mgr->get_by_id( + $this->args->testproject_id); + + $all_testers = getTestersForHtmlOptions($this->db, $tplan_id, + $tproject_info, null, + array( + TL_USER_ANYBODY => $this->option_strings['any'], + TL_USER_NOBODY => $this->option_strings['none'], + TL_USER_SOMEBODY => $this->option_strings['somebody'] + ), 'any'); + $visible_testers = $all_testers; + + // in execution mode the rights of the user have to be regarded + if ($this->mode == 'execution_mode') { + $role = $this->user->getEffectiveRole($this->db, + $this->args->testproject_id, $tplan_id); + + $simple_tester_roles = array_flip( + $this->configuration->exec_cfg->simple_tester_roles); + + // check the user's rights to see what he may do + $right_to_execute = $role->hasRight('testplan_execute'); + $right_to_manage = $role->hasRight('testplan_planning'); + + $simple = false; + if (isset($simple_tester_roles[$role->dbID]) || + ($right_to_execute && ! $right_to_manage)) { + // user is only simple tester and may not see/execute everything + $simple = true; + } + + $view_mode = $simple ? $this->configuration->exec_cfg->view_mode->tester : 'all'; + + if ($view_mode != 'all') { + $visible_testers = (array) $this->user->getDisplayName(); + $selection = (array) $this->user->dbID; + } + + // re-enable option "user_filter_default" + if (! $selection && + $this->configuration->exec_cfg->user_filter_default == + 'logged_user') { + $selection = (array) $this->user->dbID; + } + } + + $this->filters[$key] = array( + 'items' => $visible_testers, + 'selected' => $selection, + $unassigned_key => $this->args->{$unassigned_key} + ); + + // which value shall be passed to tree generation class? + + if ((is_array($selection) && in_array(TL_USER_ANYBODY, $selection)) || + ($selection == TL_USER_ANYBODY)) { + // delete user assignment filter if "any user" is part of the selection + $this->active_filters[$key] = null; + $this->active_filters[$unassigned_key] = 0; + } + + if (is_array($selection)) { + // get keys of the array as values + $this->active_filters[$key] = array_flip($selection); + foreach ($this->active_filters[$key] as $user_key => $user_value) { + $this->active_filters[$key][$user_key] = $user_key; + } + $this->active_filters[$unassigned_key] = $this->filters[$key][$unassigned_key]; + } } - - // init menu for build selection - $this->filters[$key][$build_key]['items'] = - $this->testplan_mgr->get_builds_for_html_options($tplan_id, testplan::GET_ACTIVE_BUILD); - - // if "any" is selected, nullify the active filters - if ((is_array($result_selection) && in_array($any_result_key, $result_selection)) || - $result_selection == $any_result_key) - { - $this->active_filters[$result_key] = null; - $this->active_filters[$method_key] = null; - $this->active_filters[$build_key] = null; - $this->filters[$key][$result_key]['selected'] = $any_result_key; - } - else + + /** + */ + public function init_filter_result() { - $this->active_filters[$result_key] = $result_selection; - $this->active_filters[$method_key] = $method_selection; - $this->active_filters[$build_key] = $build_selection; + $result_key = 'filter_result_result'; + $method_key = 'filter_result_method'; + $build_key = 'filter_result_build'; + + if (is_null($this->testplan_mgr)) { + $this->testplan_mgr = new testplan($this->db); + } + $tplan_id = $this->settings['setting_testplan']['selected']; + + $this->configuration->results = config_get('results'); + + // determine, which config to load and use for filter methods - depends on mode! + $cfg = ($this->mode == 'execution_mode') ? 'execution_filter_methods' : 'execution_assignment_filter_methods'; + $this->configuration->filter_methods = config_get($cfg); + + // + // CRITIC - Differences bewteen this configuration and + // (file const.inc.php) + // $tlCfg->execution_filter_methods['default_type'] + // $tlCfg->execution_assignment_filter_methods['default_type'] + // + // Will create issues: you will see an string on HTML SELECT, but code + // returned on submit will not code for string you are seeing.!!!! + // + // determine which filter method shall be selected by the JS function in template, + // when only one build is selectable by the user + $js_key_to_select = 0; + if ($this->mode == 'execution_mode') { + $js_key_to_select = $this->configuration->filter_methods['status_code']['current_build']; + } elseif ($this->mode == 'plan_mode') { + $js_key_to_select = $this->configuration->filter_methods['status_code']['specific_build']; + } + + // values selected by user + $result_selection = $this->args->$result_key; + $method_selection = $this->args->$method_key; + $build_selection = $this->args->$build_key; + + // default values + $default_filter_method = $this->configuration->filter_methods['default_type']; + $any_result_key = $this->configuration->results['status_code']['all']; + $newest_build_id = $this->testplan_mgr->get_max_build_id($tplan_id, + testplan::GET_ACTIVE_BUILD); + + if (is_null($method_selection)) { + $method_selection = $default_filter_method; + } + + if (is_null($result_selection) || $this->args->reset_filters) + // if ($this->args->reset_filters) + { + // no selection yet or filter reset requested + $result_selection = $any_result_key; + $method_selection = $default_filter_method; + $build_selection = $newest_build_id; + } else { + $this->do_filtering = true; + } + + // init array structure + $key = 'filter_result'; + $this->filters[$key] = array( + $result_key => array( + 'items' => null, + 'selected' => $result_selection + ), + $method_key => array( + 'items' => array(), + 'selected' => $method_selection, + 'js_selection' => $js_key_to_select + ), + $build_key => array( + 'items' => null, + 'selected' => $build_selection + ) + ); + + // init menu for result selection by function from exec.inc.php + $this->filters[$key][$result_key]['items'] = createResultsMenu(); + $this->filters[$key][$result_key]['items'][$any_result_key] = $this->option_strings['any']; + + // init menu for filter method selection + foreach ($this->configuration->filter_methods['status_code'] as $statusname => $statusshortcut) { + $code = $this->configuration->filter_methods['status_code'][$statusname]; + $this->filters[$key][$method_key]['items'][$code] = lang_get( + $this->configuration->filter_methods['status_label'][$statusname]); + } + + // init menu for build selection + $this->filters[$key][$build_key]['items'] = $this->testplan_mgr->get_builds_for_html_options( + $tplan_id, testplan::GET_ACTIVE_BUILD); + + // if "any" is selected, nullify the active filters + if ((is_array($result_selection) && + in_array($any_result_key, $result_selection)) || + $result_selection == $any_result_key) { + $this->active_filters[$result_key] = null; + $this->active_filters[$method_key] = null; + $this->active_filters[$build_key] = null; + $this->filters[$key][$result_key]['selected'] = $any_result_key; + } else { + $this->active_filters[$result_key] = $result_selection; + $this->active_filters[$method_key] = $method_selection; + $this->active_filters[$build_key] = $build_selection; + } } - } // end of method - - /** - * - */ - private function init_filter_bugs() - { - $key = str_replace('init_','',__FUNCTION__); - $selection = $this->args->{$key}; - - if (!$selection || $this->args->reset_filters) - { - $selection = null; - } - else + + /** + */ + public function init_filter_bugs() { - $this->do_filtering = true; - } - - $this->filters[$key] = array('selected' => $selection); - $this->active_filters[$key] = $selection; - } - - - /** - * - * - * @internal revisions - * @since 1.9.14 - * allow multiple selection (if advanced mode) - */ - private function init_filter_workflow_status() { - $key = 'filter_workflow_status'; - if (!$this->tc_mgr) { - $this->tc_mgr = new testcase($this->db); - } + $key = str_replace('init_', '', __FUNCTION__); + $selection = $this->args->{$key}; - // handle filter reset - $ak = $key . "_values"; - $cfx = array(); - if( property_exists($this->configuration, $ak) ) { - $cfx = $this->configuration->{$key . "_values"}; - } + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } - $selection = $this->args->{$key}; - if (!$selection || $this->args->reset_filters) { - if( !is_null($this->args->caller) && !$selection) { - $selection = null; - } else if( count($cfx) > 0) { - $selection = $cfx; - $this->do_filtering = true; - } else { - $selection = null; - } - } else { - $this->do_filtering = true; + $this->filters[$key] = array( + 'selected' => $selection + ); + $this->active_filters[$key] = $selection; } - - $this->filters[$key] = array('items' => array(), 'selected' => $selection); - - // load domain - // add "any" string to these types at index 0 as default selection - $this->filters[$key]['items'] = array(0 => $this->option_strings['any']) + - $this->tc_mgr->getWorkFlowStatusDomain(); - - $this->filters[$key]['size'] = min(count($this->filters[$key]['items']), - self::ADVANCED_FILTER_ITEM_QUANTITY); - - $this->active_filters[$key] = $selection; - } - - - - /** - * - * @used-by __construct - */ - private function initTreeOptions() { - $this->treeOpt['plan_mode'] = new stdClass(); - $this->treeOpt['plan_mode']->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; - $this->treeOpt['plan_mode']->useColours = COLOR_BY_TC_STATUS_OFF; - $this->treeOpt['plan_mode']->testcases_colouring_by_selected_build = DISABLED; - $this->treeOpt['plan_mode']->absolute_last_execution = true; // hmm probably useless - - } - - /** - * - */ - protected function getCustomFields($application_area=null) - { - if (!$this->cfield_mgr) + + /** + * + * @internal revisions + * @since 1.9.14 + * allow multiple selection (if advanced mode) + */ + public function init_filter_workflow_status() { - $this->cfield_mgr = new cfield_mgr($this->db); + $key = 'filter_workflow_status'; + if (! $this->tc_mgr) { + $this->tc_mgr = new testcase($this->db); + } + + // handle filter reset + $ak = $key . "_values"; + $cfx = array(); + if (property_exists($this->configuration, $ak)) { + $cfx = $this->configuration->{$key . "_values"}; + } + + $selection = $this->args->{$key}; + if (! $selection || $this->args->reset_filters) { + if (! is_null($this->args->caller) && ! $selection) { + $selection = null; + } elseif (count($cfx) > 0) { + $selection = $cfx; + $this->do_filtering = true; + } else { + $selection = null; + } + } else { + $this->do_filtering = true; + } + + $this->filters[$key] = array( + 'items' => array(), + 'selected' => $selection + ); + + // load domain + // add "any" string to these types at index 0 as default selection + $this->filters[$key]['items'] = array( + 0 => $this->option_strings['any'] + ) + $this->tc_mgr->getWorkFlowStatusDomain(); + + $this->filters[$key]['size'] = min(count($this->filters[$key]['items']), + self::ADVANCED_FILTER_ITEM_QUANTITY); + + $this->active_filters[$key] = $selection; } - $scope = array('design' => true,'execution' => false, 'testplan_design' => false); - $scope = array_merge($scope,(array)$application_area); - $cfields = array('design' => null,'execution' => null, 'testplan_design' => null); - if($scope['design']) + /** + * + * @used-by __construct + */ + public function initTreeOptions() { - $cfields['design'] = $this->cfield_mgr->get_linked_cfields_at_design($this->args->testproject_id, 1, null, 'testcase'); - } + $this->treeOpt['plan_mode'] = new stdClass(); + $this->treeOpt['plan_mode']->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; + $this->treeOpt['plan_mode']->useColours = COLOR_BY_TC_STATUS_OFF; + $this->treeOpt['plan_mode']->testcases_colouring_by_selected_build = self::DISABLED; + $this->treeOpt['plan_mode']->absolute_last_execution = true; // hmm probably useless + } - if($scope['testplan_design']) + /** + */ + protected function getCustomFields($application_area = null) { - $cfields['testplan_design'] = $this->cfield_mgr->get_linked_cfields_at_testplan_design($this->args->testproject_id,1,'testcase'); - } + if (! $this->cfield_mgr) { + $this->cfield_mgr = new cfield_mgr($this->db); + } + $scope = array( + 'design' => true, + 'execution' => false, + 'testplan_design' => false + ); + $scope = array_merge($scope, (array) $application_area); + + $cfields = array( + 'design' => null, + 'execution' => null, + 'testplan_design' => null + ); + if ($scope['design']) { + $cfields['design'] = $this->cfield_mgr->get_linked_cfields_at_design( + $this->args->testproject_id, 1, null, 'testcase'); + } - $cf = (array)$cfields['design'] + (array)$cfields['testplan_design']; + if ($scope['testplan_design']) { + $cfields['testplan_design'] = $this->cfield_mgr->get_linked_cfields_at_testplan_design( + $this->args->testproject_id, 1, 'testcase'); + } - // Because I'm using these as filters, need a special processing - // for CF types that present a domain like LIST, then if the blank option is - // not present will be added as FIRST OPTION + $cf = (array) $cfields['design'] + (array) $cfields['testplan_design']; + + // Because I'm using these as filters, need a special processing + // for CF types that present a domain like LIST, then if the blank option is + // not present will be added as FIRST OPTION + + if (! empty($cf)) { + $cfTypes = array_flip($this->cfield_mgr->get_available_types()); + $key2loop = array_keys($cf); + foreach ($key2loop as $cfID) { + // we will use these CF as filter => required property has to be + // set to false + $cf[$cfID]['required'] = 0; + + if ($cf[$cfID]['type'] == $cfTypes['list']) { + $addBlank = true; + $vv = explode('|', $cf[$cfID]['possible_values']); + foreach ($vv as $value) { + if (trim($value) == '') { + $addBlank = false; + break; + } + } + + if ($addBlank) { + $cf[$cfID]['possible_values'] = ' |' . + $cf[$cfID]['possible_values']; + } + } + } + return $cf; + } else { + return null; + } + } - if(count($cf) > 0) + /** + */ + protected function init_advanced_filter_mode() { - $cfTypes = array_flip($this->cfield_mgr->get_available_types()); - $key2loop = array_keys($cf); - foreach($key2loop as $cfID) - { - // we will use these CF as filter => required property has to be - // set to false - $cf[$cfID]['required'] = 0; - - if($cf[$cfID]['type'] == $cfTypes['list']) - { - $addBlank = true; - $vv = explode('|',$cf[$cfID]['possible_values']); - foreach($vv as $value) - { - if(trim($value) == '') - { - $addBlank = false; - break; - } - } - - if($addBlank) - { - $cf[$cfID]['possible_values'] = ' |' . $cf[$cfID]['possible_values']; - } - } - } - return $cf; - } - else - { - return null; - } - } - - /** - * - */ - protected function init_advanced_filter_mode() { - switch( $this->mode ) { - case 'edit_mode': - $this->advanced_filter_mode = TRUE; - break; - - default: - $m2c = __FUNCTION__; - parent::$m2c(); - break; - } - } // end of method - - - /** - * - */ - protected function init_setting_testsgroupby() { - $key = 'setting_testsgroupby'; - - // now load info from session - $mode = (isset($_REQUEST[$key])) ? $_REQUEST[$key] : 0; - $this->args->testsgroupedby_mode = $mode; - $this->args->{$key} = $mode; - $this->settings[$key]['selected'] = $mode; - - $this->settings[$key]['items']['mode_test_suite'] = lang_get('mode_test_suite'); - $this->settings[$key]['items']['mode_req_coverage'] = lang_get('mode_req_coverage'); - } // end of method - - /** - * - * @used-by tlTestCaseFilterControl->init_settings() - * - */ - protected function init_setting_exec_tree_counters_logic() { - $key = str_replace('init_','',__FUNCTION__); - - // we need to understand if select Test Plan has platforms - $cfx = $this->configuration->exec_cfg->tcases_counters_mode_domain; - $logic = $this->configuration->exec_cfg->tcases_counters_mode; - $wow = 'without_platforms'; - if( $this->settings['setting_platform'] != false ) { - $wow = 'with_platforms'; - } - $defaultAlgo = $logic[$wow]; - $lblKS = $cfx[$wow]; - $flipper = array(); - foreach($cfx[$wow] as $def ) { - $flipper[constant($def)] = $def; + switch ($this->mode) { + case 'edit_mode': + $this->advanced_filter_mode = true; + break; + + default: + $m2c = __FUNCTION__; + parent::$m2c(); + break; + } } - foreach( $lblKS as $lblKey ) { - $code = constant($lblKey); - $ak = strtolower($lblKey); - $this->settings[$key]['items'][$code] = lang_get($ak); - } - - $algo = intval( isset($_REQUEST[$key]) ? $_REQUEST[$key] : 0); - if( $algo == 0 ) { - if( isset($_SESSION[$key]) ) { - $algo = intval($_SESSION[$key]); - } - } + /** + */ + protected function init_setting_testsgroupby() + { + $key = 'setting_testsgroupby'; - // Validate Domain - if( !isset($flipper[$algo]) ) { - $algo = intval($defaultAlgo); - } + $mode = (isset($_REQUEST[$key])) ? $_REQUEST[$key] : 'mode_test_suite'; + $this->args->testsgroupedby_mode = $mode; + $this->args->{$key} = $mode; + $this->settings[$key]['selected'] = $mode; - $_SESSION[$key] = $this->args->{$key} = - $this->settings[$key]['selected'] = $algo; - - } // end of method - - - /** - * - */ - function setFiltersDefinition() { - - $this->all_filters = - array('filter_tc_id' => array("POST", tlInputParameter::STRING_N,0,30), - 'filter_testcase_name' => array("POST", tlInputParameter::STRING_N,0,100), - 'filter_toplevel_testsuite' => array("POST", tlInputParameter::STRING_N,0,100), - 'filter_keywords' => array("POST", tlInputParameter::ARRAY_INT), - 'filter_workflow_status' => array("POST", tlInputParameter::INT_N), - 'filter_importance' => array("POST", tlInputParameter::INT_N), - '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, - 'filter_bugs' => array("POST", tlInputParameter::STRING_N,0,240), - 'filter_platforms' => array("POST", tlInputParameter::ARRAY_INT)); - - - } - - /** - * - * - */ - private function init_filter_platforms() { - if (!$this->platform_mgr) { - $this->platform_mgr = new tlPlatform($this->db); - } - $key = 'filter_platforms'; - $special = array(-1 => $this->option_strings['without_platforms'], - 0 => $this->option_strings['any']); - - // set selection to default (any), only change - // if value is sent by user and reset is not requested - $selection = $this->args->{$key}; - if (!$selection || $this->args->reset_filters) { - $selection = null; - } else { - $this->do_filtering = true; + $this->settings[$key]['items']['mode_test_suite'] = lang_get( + 'mode_test_suite'); + $this->settings[$key]['items']['mode_req_coverage'] = lang_get( + 'mode_req_coverage'); } - $this->platform_mgr->setTestProjectID($this->args->testproject_id); - + /** + * + * @used-by tlTestCaseFilterControl->init_settings() + * + */ + protected function init_setting_exec_tree_counters_logic() + { + $key = str_replace('init_', '', __FUNCTION__); + + // we need to understand if select Test Plan has platforms + $cfx = $this->configuration->exec_cfg->tcases_counters_mode_domain; + $logic = $this->configuration->exec_cfg->tcases_counters_mode; + $wow = 'without_platforms'; + if ($this->settings['setting_platform']["items"] != null) { + $wow = 'with_platforms'; + } + $defaultAlgo = $logic[$wow]; + $lblKS = $cfx[$wow]; + $flipper = array(); + foreach ($cfx[$wow] as $def) { + $flipper[constant($def)] = $def; + } - $opx = null; - switch ($this->mode) { - case 'edit_mode': - case 'plan_add_mode': - $opxy = array('enable_on_design' => true, - 'enable_on_execution' => false); - break; + foreach ($lblKS as $lblKey) { + $code = constant($lblKey); + $ak = strtolower($lblKey); + $this->settings[$key]['items'][$code] = lang_get($ak); + } - } + $algo = intval(isset($_REQUEST[$key]) ? $_REQUEST[$key] : 0); + if ($algo == 0 && isset($_SESSION[$key])) { + $algo = intval($_SESSION[$key]); + } - $platformSet = $this->platform_mgr->getAllAsMap($opxy); - $this->filters[$key] = array('items' => $platformSet, - 'selected' => $selection); + // Validate Domain + if (! isset($flipper[$algo])) { + $algo = intval($defaultAlgo); + } - // set the active value to filter - // delete keyword filter if "any" (0) is part of the selection - regardless of filter mode - if (is_array($this->filters[$key]['selected']) && in_array(0, $this->filters[$key]['selected'])) { - $this->active_filters[$key] = null; - } else { - $this->active_filters[$key] = $this->filters[$key]['selected']; + $_SESSION[$key] = $this->args->{$key} = $this->settings[$key]['selected'] = $algo; } - - } // end of method + /** + */ + private function setFiltersDefinition() + { + $this->all_filters = array( + 'filter_tc_id' => array( + "POST", + tlInputParameter::STRING_N, + 0, + 30 + ), + 'filter_testcase_name' => array( + "POST", + tlInputParameter::STRING_N, + 0, + 100 + ), + 'filter_toplevel_testsuite' => array( + "POST", + tlInputParameter::STRING_N, + 0, + 100 + ), + 'filter_keywords' => array( + "POST", + tlInputParameter::ARRAY_INT + ), + 'filter_workflow_status' => array( + "POST", + tlInputParameter::INT_N + ), + 'filter_importance' => array( + "POST", + tlInputParameter::INT_N + ), + '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, + 'filter_bugs' => array( + "POST", + tlInputParameter::STRING_N, + 0, + 240 + ), + 'filter_platforms' => array( + "POST", + tlInputParameter::ARRAY_INT + ) + ); + } + + /** + */ + public function init_filter_platforms() + { + if (! $this->platform_mgr) { + $this->platform_mgr = new tlPlatform($this->db); + } + $key = 'filter_platforms'; + + // set selection to default (any), only change + // if value is sent by user and reset is not requested + $selection = $this->args->{$key}; + if (! $selection || $this->args->reset_filters) { + $selection = null; + } else { + $this->do_filtering = true; + } + $this->platform_mgr->setTestProjectID($this->args->testproject_id); + switch ($this->mode) { + case 'edit_mode': + case 'plan_add_mode': + $opxy = array( + 'enable_on_design' => true, + 'enable_on_execution' => false + ); + break; + } -} \ No newline at end of file + $platformSet = (array) $this->platform_mgr->getAllAsMap($opxy); + $this->filters[$key] = array( + 'items' => $platformSet, + 'selected' => $selection + ); + $this->filters[$key]['size'] = min(count($this->filters[$key]['items']), + self::ADVANCED_FILTER_ITEM_QUANTITY); + + // set the active value to filter + // delete filter if "any" (0) is part of the selection - regardless of filter mode + if (is_array($this->filters[$key]['selected']) && + in_array(0, $this->filters[$key]['selected'])) { + $this->active_filters[$key] = null; + } else { + $this->active_filters[$key] = $this->filters[$key]['selected']; + } + } +} diff --git a/lib/functions/tlTestPlanMetrics.class.php b/lib/functions/tlTestPlanMetrics.class.php index d9d1e7048d..1b2a2bb309 100644 --- a/lib/functions/tlTestPlanMetrics.class.php +++ b/lib/functions/tlTestPlanMetrics.class.php @@ -1,3188 +1,3083 @@ -resultsCfg = config_get('results'); - $this->testCaseCfg = config_get('testcase_cfg'); - - $this->db = $db; - parent::__construct($db); - - $this->map_tc_status = $this->resultsCfg['status_code']; - - // This will be used to create dynamically counters if user add new status - foreach( $this->resultsCfg['status_label_for_exec_ui'] as $tc_status_verbose => $label) - { - $this->tc_status_for_statistics[$tc_status_verbose] = $this->map_tc_status[$tc_status_verbose]; - } - if( !isset($this->resultsCfg['status_label_for_exec_ui']['not_run']) ) - { - $this->tc_status_for_statistics['not_run'] = $this->map_tc_status['not_run']; - } - // $this->notRunStatusCode = $this->tc_status_for_statistics['not_run']; - - $this->statusCode = array_flip(array_keys($this->resultsCfg['status_label_for_exec_ui'])); - foreach($this->statusCode as $key => $dummy) - { - $this->statusCode[$key] = $this->resultsCfg['status_code'][$key]; - } - - // $this->execTaskCode = intval($this->assignment_types['testcase_execution']['id']); - - } // end results constructor - - - - public function getStatusConfig() - { - return $this->tc_status_for_statistics; - } - - - /** - * Function returns prioritized test result counter - * - * @param timestamp $milestoneTargetDate - (optional) milestone deadline - * @param timestamp $milestoneStartDate - (optional) milestone start date - * @return array with three priority counters - */ - public function getPrioritizedResults($tplanID,$milestoneTargetDate = null, $milestoneStartDate = null) - { - $output = array (HIGH=>0,MEDIUM=>0,LOW=>0); - - - for($urgency=1; $urgency <= 3; $urgency++) - { - for($importance=1; $importance <= 3; $importance++) - { - $sql = "SELECT COUNT(DISTINCT(TPTCV.id )) " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['executions']} E ON " . - " TPTCV.tcversion_id = E.tcversion_id " . - " JOIN {$this->tables['tcversions']} TCV ON " . - " TPTCV.tcversion_id = TCV.id " . - " WHERE TPTCV.testplan_id = {$tplanID} " . - " AND TPTCV.platform_id = E.platform_id " . - " AND E.testplan_id = {$tplanID} " . - " AND NOT E.status = '{$this->map_tc_status['not_run']}' " . - " AND TCV.importance={$importance} AND TPTCV.urgency={$urgency}"; - - // Milestones did not handle start and target date properly - $end_of_the_day = " 23:59:59"; - $beginning_of_the_day = " 00:00:00"; - - if( !is_null($milestoneTargetDate) ) - { - $sql .= " AND execution_ts < '" . $milestoneTargetDate . $end_of_the_day ."'"; - } - - if( !is_null($milestoneStartDate) ) - { - $sql .= " AND execution_ts > '" . $milestoneStartDate . $beginning_of_the_day ."'"; - } - - $tmpResult = $this->db->fetchOneValue($sql); - - // parse results into three levels of priority - $priority = priority_to_level($urgency*$importance); - $output[$priority] = $output[$priority] + $tmpResult; - } - } - - return $output; - } - - /** - * Function returns prioritized test case counter (in Test Plan) - * - * @return array with three priority counters - */ - public function getPrioritizedTestCaseCounters($tplanID) - { - $output = array (HIGH=>0,MEDIUM=>0,LOW=>0); - - /** @TODO - REFACTOR IS OUT OF STANDARD MAGIC NUMBERS */ - for($urgency=1; $urgency <= 3; $urgency++) - { - for($importance=1; $importance <= 3; $importance++) - { - // get total count of related TCs - $sql = "SELECT COUNT( TPTCV.id ) FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TPTCV.tcversion_id = TCV.id " . - " WHERE TPTCV.testplan_id = " . $tplanID . - " AND TCV.importance={$importance} AND TPTCV.urgency={$urgency}"; - - $tmpResult = $this->db->fetchOneValue($sql); - - // clean up priority usage - $priority = priority_to_level($urgency*$importance); - $output[$priority] = $output[$priority] + $tmpResult; - } - } - - return $output; - } - - - /** - * - */ - function getMilestonesMetrics($tplanID, $milestoneSet=null) - { - $results = array(); - - // get amount of test cases for each execution result + total amount of test cases - $planMetrics = $this->getExecCountersByExecStatus($tplanID); - - $milestones = is_null($milestoneSet) ? $this->get_milestones($tplanID) : $milestoneSet; - - // get amount of test cases for each priority for test plan - $priorityCounters = $this->getPrioritizedTestCaseCounters($tplanID); - $pc = array(LOW => 'result_low_percentage', MEDIUM => 'result_medium_percentage', - HIGH => 'result_high_percentage' ); - - $checks = array(LOW => 'low_percentage', MEDIUM => 'medium_percentage', - HIGH => 'high_percentage' ); - - $on_off = array(LOW => 'low_incomplete', MEDIUM => 'medium_incomplete', - HIGH => 'high_incomplete' ); - - // Important: - // key already defined on item: high_percentage,medium_percentage,low_percentage - foreach($milestones as $item) - { - $item['tcs_priority'] = $priorityCounters; - $item['tc_total'] = $planMetrics['total']; - - // get amount of executed test cases for each priority before target_date - $item['results'] = $this->getPrioritizedResults($tplanID, $item['target_date'], $item['start_date']); - $item['tc_completed'] = 0; - - // calculate percentage of executed test cases for each priority - foreach( $pc as $key => $item_key) - { - $target_key = $checks[$key]; - if( $item[$target_key] == 0 ) - { - $item[$item_key] = 100; - } - else - { - $item[$item_key] = ($priorityCounters[$key] > 0) ? - $this->get_percentage($priorityCounters[$key], $item['results'][$key]) : 0; - } - $item['tc_completed'] += $item['results'][$key]; - } - - // amount of all executed tc with any priority before target_date / all test cases - $item['percentage_completed'] = $this->get_percentage($item['tc_total'], $item['tc_completed']); - - foreach( $checks as $key => $item_key) { - // add 1 decimal places to expected percentages - $item[$checks[$key]] = number_format($item[$checks[$key]], 1); - - // check if target for each priority is reached - // show target as reached if expected percentage is greater than executed percentage - $item[$on_off[$key]] = ($item[$checks[$key]] > $item[$pc[$key]]) ? ON : OFF; - } - $results[$item['id']] = $item; - } - return $results; - } - - - /** - * calculate percentage and format - * - * @param int $total Total count - * @param int $parameter a parameter count - * @return string formatted percentage - */ - function get_percentage($total, $parameter) - { - $percentCompleted = ($total > 0) ? (($parameter / $total) * 100) : 100; - return number_format($percentCompleted,1); - } - - - - /** - * @used-by getOverallBuildStatusForRender() - * XML-RPC getExecCountersByBuild() - * - * - * No matter we are trying to calculate metrics for BUILDS, - * we need to consider execution status at Build and Platform level. - * - * Why? - * Let's review help we provide on GUI: - * - * The use of platforms has impact on metrics, because - * a test case that must be executed for N platforms is considered - * as N test cases on metrics. - * - * Example: Platform X and Y - * - * Test Case - Tester Assigned - * TC1 U1 - * - * user U1 has to execute TWO test cases, NOT ONE. - * This means that we HAVE to consider execution status ON (BUILD,PLATFORM), - * but we are not going to display results with BUILD and PLATFORM, - * but ONLY with BUILD indication. - * - * opt => array('getOnlyAssigned' => false, 'tprojectID' => 0, - * 'getPlatformSet' => false, 'processClosedBuilds' => true); - * filters => array('buildSet' => null); - * - */ - function getExecCountersByBuildExecStatus($id, $filters=null, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe_id = intval($id); - list($my,$builds,$sqlStm) = - $this->helperGetExecCounters($safe_id, $filters, $opt); - - $fields = ""; - if( $my['opt']['groupByPlatform'] ) { - $fields = ",platform_id"; - } - - // This subquery is BETTER than the VIEW, need to understand why - // Last Executions By Build and Platform (LEBBP) - $sqlLEBBP = $sqlStm['LEBBP']; - - $sqlUnionAB = "/* {$debugMsg} sqlUnionAB - executions */" . - " SELECT DISTINCT UA.build_id, TPTCV.tcversion_id, TPTCV.platform_id, " . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id IN ({$builds->inClause}) AND UA.type = {$this->execTaskCode} " . - - " /* GO FOR Absolute LATEST exec ID ON BUILD and PLATFORM */ " . - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.testplan_id = " . $safe_id . - - " /* Get execution status WRITTEN on DB */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . - " AND E.build_id = UA.build_id " . - - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND UA.build_id IN ({$builds->inClause}) "; - - $sqlUnionBB = "/* {$debugMsg} sqlUnionBB - NOT RUN */" . - " SELECT DISTINCT UA.build_id, TPTCV.tcversion_id, TPTCV.platform_id, " . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id IN ({$builds->inClause}) AND UA.type = {$this->execTaskCode} " . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - - // Without this I've created issue 5272 - " AND LEBBP.build_id = UA.build_id " . - - " AND LEBBP.testplan_id = " . $safe_id . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - - // Without this I've created issue 5272 - " AND E.build_id = LEBBP.build_id " . - - " /* FILTER BUILDS in set on target test plan (not alway can be applied) */ " . - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND UA.build_id IN ({$builds->inClause}) " . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEBBP.id IS NULL"; - - - // 20140819 - I've not documented why I've use UNION ALL - // UNION ALL includes duplicates, but now (@20140819) because I've implemented - // test case exec assignment to MULTIPLE TESTERS, I need to remove duplicates - // to avoid wrong exec_qty. - // My choice was: add DISTINCT to each union piece. - // May be is a wrong choice, but I need to read and test more to understand - $sql = " /* {$debugMsg} UNION WITH ALL CLAUSE */" . - " SELECT count(0) AS exec_qty, build_id,status $fields " . - " FROM ($sqlUnionAB UNION ALL $sqlUnionBB ) AS SQBU " . - " GROUP BY build_id,status $fields"; - // 366 - if( $my['opt']['groupByPlatform'] ) { - $kol = array('platform_id','build_id','status'); - $exec['with_tester'] = - (array)$this->db->fetchRowsIntoMap3l($sql,$kol); - - // Need to Add info regarding: - // - Add info for ACTIVE BUILD WITHOUT any execution. ??? - // Hmm, think about Need to check is this way is better that request DBMS to do it. - // - Execution status that have not happened - foreach($exec as $dum => &$elem) { - $platSet = array_keys($elem); - foreach($platSet as $platId ) { - $itemSet = array_keys($elem[$platId]); - foreach($itemSet as $itemID) { - foreach($this->statusCode as $verbose => $code) { - if(!isset($elem[$platId][$itemID][$code])) { - $elem[$platId][$itemID][$code] = - array('build_id' => $itemID,'status' => $code, 'exec_qty' => 0); - } - } - } - } - } - } else { - $exec['with_tester'] = - (array)$this->db->fetchMapRowsIntoMap($sql,'build_id','status'); - // Need to Add info regarding: - // - Add info for ACTIVE BUILD WITHOUT any execution. ??? - // Hmm, think about Need to check is this way is better that request DBMS to do it. - // - Execution status that have not happened - foreach($exec as &$elem) { - $itemSet = array_keys($elem); - foreach($itemSet as $itemID) { - foreach($this->statusCode as $verbose => $code) { - if(!isset($elem[$itemID][$code])) { - $elem[$itemID][$code] = - array('build_id' => $itemID,'status' => $code, 'exec_qty' => 0); - } - } - } - } - } - - // get total assignments by BUILD ID - // changes due to test case exec assignment to MULTIPLE TESTERS - - if( $my['opt']['groupByPlatform'] ) { - - $sql = " /* $debugMsg */ - SELECT COUNT(0) AS qty, TT.build_id, TT.platform_id - FROM ( +resultsCfg = config_get('results'); + $this->testCaseCfg = config_get('testcase_cfg'); + + $this->db = $db; + parent::__construct($db); + + $this->map_tc_status = $this->resultsCfg['status_code']; + + // This will be used to create dynamically counters if user add new status + foreach ($this->resultsCfg['status_label_for_exec_ui'] as $tc_status_verbose => $label) { + $this->tc_status_for_statistics[$tc_status_verbose] = $this->map_tc_status[$tc_status_verbose]; + } + if (! isset($this->resultsCfg['status_label_for_exec_ui']['not_run'])) { + $this->tc_status_for_statistics['not_run'] = $this->map_tc_status['not_run']; + } + + $this->statusCode = array_flip( + array_keys($this->resultsCfg['status_label_for_exec_ui'])); + foreach ($this->statusCode as $key => $dummy) { + $this->statusCode[$key] = $this->resultsCfg['status_code'][$key]; + } + } + + // end results constructor + public function getStatusConfig() + { + return $this->tc_status_for_statistics; + } + + /** + * Function returns prioritized test result counter + * + * @param int $milestoneTargetDate + * - (optional) milestone deadline + * @param int $milestoneStartDate + * - (optional) milestone start date + * @return array with three priority counters + */ + public function getPrioritizedResults($tplanID, $milestoneTargetDate = null, + $milestoneStartDate = null) + { + $output = array( + HIGH => 0, + MEDIUM => 0, + LOW => 0 + ); + + for ($urgency = 1; $urgency <= 3; $urgency ++) { + for ($importance = 1; $importance <= 3; $importance ++) { + $sql = "SELECT COUNT(DISTINCT(TPTCV.id )) " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['executions']} E ON " . + " TPTCV.tcversion_id = E.tcversion_id " . + " JOIN {$this->tables['tcversions']} TCV ON " . + " TPTCV.tcversion_id = TCV.id " . + " WHERE TPTCV.testplan_id = {$tplanID} " . + " AND TPTCV.platform_id = E.platform_id " . + " AND E.testplan_id = {$tplanID} " . + " AND NOT E.status = '{$this->map_tc_status['not_run']}' " . + " AND TCV.importance={$importance} AND TPTCV.urgency={$urgency}"; + + // Milestones did not handle start and target date properly + $end_of_the_day = " 23:59:59"; + $beginning_of_the_day = " 00:00:00"; + + if (! is_null($milestoneTargetDate)) { + $sql .= " AND execution_ts < '" . $milestoneTargetDate . + $end_of_the_day . "'"; + } + + if (! is_null($milestoneStartDate)) { + $sql .= " AND execution_ts > '" . $milestoneStartDate . + $beginning_of_the_day . "'"; + } + + $tmpResult = $this->db->fetchOneValue($sql); + + // parse results into three levels of priority + $priority = priority_to_level($urgency * $importance); + $output[$priority] += $tmpResult; + } + } + + return $output; + } + + /** + * Function returns prioritized test case counter (in Test Plan) + * + * @return array with three priority counters + */ + public function getPrioritizedTestCaseCounters($tplanID) + { + $output = array( + HIGH => 0, + MEDIUM => 0, + LOW => 0 + ); + + /** + * + * @todo - REFACTOR IS OUT OF STANDARD MAGIC NUMBERS + */ + for ($urgency = 1; $urgency <= 3; $urgency ++) { + for ($importance = 1; $importance <= 3; $importance ++) { + // get total count of related TCs + $sql = "SELECT COUNT( TPTCV.id ) FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TPTCV.tcversion_id = TCV.id " . + " WHERE TPTCV.testplan_id = " . $tplanID . + " AND TCV.importance={$importance} AND TPTCV.urgency={$urgency}"; + + $tmpResult = $this->db->fetchOneValue($sql); + + // clean up priority usage + $priority = priority_to_level($urgency * $importance); + $output[$priority] += $tmpResult; + } + } + + return $output; + } + + /** + */ + public function getMilestonesMetrics($tplanID, $milestoneSet = null) + { + $results = array(); + + // get amount of test cases for each execution result + total amount of test cases + $planMetrics = $this->getExecCountersByExecStatus($tplanID); + + $milestones = is_null($milestoneSet) ? $this->get_milestones($tplanID) : $milestoneSet; + + // get amount of test cases for each priority for test plan + $priorityCounters = $this->getPrioritizedTestCaseCounters($tplanID); + $pc = array( + LOW => 'result_low_percentage', + MEDIUM => 'result_medium_percentage', + HIGH => 'result_high_percentage' + ); + + $checks = array( + LOW => 'low_percentage', + MEDIUM => 'medium_percentage', + HIGH => 'high_percentage' + ); + + $on_off = array( + LOW => 'low_incomplete', + MEDIUM => 'medium_incomplete', + HIGH => 'high_incomplete' + ); + + // Important: + // key already defined on item: high_percentage,medium_percentage,low_percentage + foreach ($milestones as $item) { + $item['tcs_priority'] = $priorityCounters; + $item['tc_total'] = $planMetrics['total']; + + // get amount of executed test cases for each priority before target_date + $item['results'] = $this->getPrioritizedResults($tplanID, + $item['target_date'], $item['start_date']); + $item['tc_completed'] = 0; + + // calculate percentage of executed test cases for each priority + foreach ($pc as $key => $item_key) { + $target_key = $checks[$key]; + if ($item[$target_key] == 0) { + $item[$item_key] = 100; + } else { + $item[$item_key] = ($priorityCounters[$key] > 0) ? $this->get_percentage( + $priorityCounters[$key], $item['results'][$key]) : 0; + } + $item['tc_completed'] += $item['results'][$key]; + } + + // amount of all executed tc with any priority before target_date / all test cases + $item['percentage_completed'] = $this->get_percentage( + $item['tc_total'], $item['tc_completed']); + + foreach ($checks as $key => $item_key) { + // add 1 decimal places to expected percentages + $item[$checks[$key]] = number_format($item[$checks[$key]], 1); + + // check if target for each priority is reached + // show target as reached if expected percentage is greater than executed percentage + $item[$on_off[$key]] = ($item[$checks[$key]] > $item[$pc[$key]]) ? ON : OFF; + } + $results[$item['id']] = $item; + } + return $results; + } + + /** + * calculate percentage and format + * + * @param int $total + * Total count + * @param int $parameter + * a parameter count + * @return string formatted percentage + */ + private function get_percentage($total, $parameter) + { + $percentCompleted = ($total > 0) ? (($parameter / $total) * 100) : 100; + + return number_format($percentCompleted, 1); + } + + /** + * + * @used-by getOverallBuildStatusForRender() + * XML-RPC getExecCountersByBuild() + * + * + * No matter we are trying to calculate metrics for BUILDS, + * we need to consider execution status at Build and Platform level. + * + * Why? + * Let's review help we provide on GUI: + * + * The use of platforms has impact on metrics, because + * a test case that must be executed for N platforms is considered + * as N test cases on metrics. + * + * Example: Platform X and Y + * + * Test Case - Tester Assigned + * TC1 U1 + * + * user U1 has to execute TWO test cases, NOT ONE. + * This means that we HAVE to consider execution status ON (BUILD,PLATFORM), + * but we are not going to display results with BUILD and PLATFORM, + * but ONLY with BUILD indication. + * + * opt => array('getOnlyAssigned' => false, 'tprojectID' => 0, + * 'getPlatformSet' => false, 'processClosedBuilds' => true); + * filters => array('buildSet' => null); + * + */ + public function getExecCountersByBuildExecStatus($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe_id = intval($id); + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($safe_id, + $filters, $opt); + + $fields = ""; + if ($my['opt']['groupByPlatform']) { + $fields = ",platform_id"; + } + + // This subquery is BETTER than the VIEW, need to understand why + // Last Executions By Build and Platform (LEBBP) + $sqlLEBBP = $sqlStm['LEBBP']; + + $sqlUnionAB = "/* {$debugMsg} sqlUnionAB - executions */" . + " SELECT DISTINCT UA.build_id, TPTCV.tcversion_id, TPTCV.platform_id, " . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . + " AND UA.build_id IN ({$builds->inClause}) AND UA.type = {$this->execTaskCode} " . + " /* GO FOR Absolute LATEST exec ID ON BUILD and PLATFORM */ " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.testplan_id = " . $safe_id . + " /* Get execution status WRITTEN on DB */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.build_id = UA.build_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . " AND UA.build_id IN ({$builds->inClause}) "; + + $sqlUnionBB = "/* {$debugMsg} sqlUnionBB - NOT RUN */" . + " SELECT DISTINCT UA.build_id, TPTCV.tcversion_id, TPTCV.platform_id, " . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . + " AND UA.build_id IN ({$builds->inClause}) AND UA.type = {$this->execTaskCode} " . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + + // Without this I've created issue 5272 + " AND LEBBP.build_id = UA.build_id " . " AND LEBBP.testplan_id = " . + $safe_id . " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + + // Without this I've created issue 5272 + " AND E.build_id = LEBBP.build_id " . + " /* FILTER BUILDS in set on target test plan (not alway can be applied) */ " . + " WHERE TPTCV.testplan_id=" . $safe_id . + " AND UA.build_id IN ({$builds->inClause}) " . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEBBP.id IS NULL"; + + // 20140819 - I've not documented why I've use UNION ALL + // UNION ALL includes duplicates, but now (@20140819) because I've implemented + // test case exec assignment to MULTIPLE TESTERS, I need to remove duplicates + // to avoid wrong exec_qty. + // My choice was: add DISTINCT to each union piece. + // May be is a wrong choice, but I need to read and test more to understand + $sql = " /* {$debugMsg} UNION WITH ALL CLAUSE */" . + " SELECT count(0) AS exec_qty, build_id,status $fields " . + " FROM ($sqlUnionAB UNION ALL $sqlUnionBB ) AS SQBU " . + " GROUP BY build_id,status $fields"; + // 366 + if ($my['opt']['groupByPlatform']) { + $kol = array( + 'platform_id', + 'build_id', + 'status' + ); + $exec['with_tester'] = (array) $this->db->fetchRowsIntoMap3l($sql, + $kol); + + // Need to Add info regarding: + // - Add info for ACTIVE BUILD WITHOUT any execution. ??? + // Hmm, think about Need to check is this way is better that request DBMS to do it. + // - Execution status that have not happened + foreach ($exec as &$elem) { + $platSet = array_keys($elem); + foreach ($platSet as $platId) { + $itemSet = array_keys($elem[$platId]); + foreach ($itemSet as $itemID) { + foreach ($this->statusCode as $code) { + if (! isset($elem[$platId][$itemID][$code])) { + $elem[$platId][$itemID][$code] = array( + 'build_id' => $itemID, + 'status' => $code, + 'exec_qty' => 0 + ); + } + } + } + } + } + } else { + $exec['with_tester'] = (array) $this->db->fetchMapRowsIntoMap($sql, + 'build_id', 'status'); + // Need to Add info regarding: + // - Add info for ACTIVE BUILD WITHOUT any execution. ??? + // Hmm, think about Need to check is this way is better that request DBMS to do it. + // - Execution status that have not happened + foreach ($exec as &$elem) { + $itemSet = array_keys($elem); + foreach ($itemSet as $itemID) { + foreach ($this->statusCode as $code) { + if (! isset($elem[$itemID][$code])) { + $elem[$itemID][$code] = array( + 'build_id' => $itemID, + 'status' => $code, + 'exec_qty' => 0 + ); + } + } + } + } + } + + // get total assignments by BUILD ID + // changes due to test case exec assignment to MULTIPLE TESTERS + + if ($my['opt']['groupByPlatform']) { + + $sql = " /* $debugMsg */ + SELECT COUNT(0) AS qty, TT.build_id, TT.platform_id + FROM ( SELECT DISTINCT UA.build_id, UA.feature_id, - TPTCV.platform_id + TPTCV.platform_id FROM {$this->tables['user_assignments']} UA - JOIN {$this->tables['testplan_tcversions']} TPTCV - ON TPTCV.id = UA.feature_id + JOIN {$this->tables['testplan_tcversions']} TPTCV + ON TPTCV.id = UA.feature_id WHERE UA. build_id IN ( {$builds->inClause} ) - AND UA.type = {$this->execTaskCode} + AND UA.type = {$this->execTaskCode} GROUP BY build_id,feature_id, platform_id - ) TT - GROUP BY build_id, platform_id "; - - $exec['total'] = - (array)$this->db->fetchMapRowsIntoMap( - $sql,'platform_id','build_id'); - - } else { - $sql = " /* $debugMsg */ - SELECT COUNT(0) AS qty, TT.build_id - FROM ( - SELECT DISTINCT UA.build_id, UA.feature_id + ) TT + GROUP BY build_id, platform_id "; + + $exec['total'] = (array) $this->db->fetchMapRowsIntoMap($sql, + 'platform_id', 'build_id'); + } else { + $sql = " /* $debugMsg */ + SELECT COUNT(0) AS qty, TT.build_id + FROM ( + SELECT DISTINCT UA.build_id, UA.feature_id FROM {$this->tables['user_assignments']} UA - JOIN {$this->tables['testplan_tcversions']} TPTCV - ON TPTCV.id = UA.feature_id + JOIN {$this->tables['testplan_tcversions']} TPTCV + ON TPTCV.id = UA.feature_id WHERE UA. build_id IN ( {$builds->inClause} ) - AND UA.type = {$this->execTaskCode} + AND UA.type = {$this->execTaskCode} GROUP BY build_id,feature_id - ) TT - GROUP BY build_id "; - - $exec['total'] = - (array)$this->db->fetchRowsIntoMap($sql,'build_id'); - } - - $exec['active_builds'] = $builds->infoSet; - - return $exec; - } - - - /** - * - * - **/ - function getOverallBuildStatusForRender($id, $totalKey='total_assigned') { - $renderObj = null; - $code_verbose = $this->getStatusForReports(); - $labels = $this->resultsCfg['status_label']; - - $metrics = $this->getExecCountersByBuildExecStatus($id); - if( !is_null($metrics) ) { - $renderObj = new stdClass(); - - // Creating item list this way will generate a row also for - // ACTIVE BUILDS were ALL TEST CASES HAVE NO TESTER ASSIGNMENT - // $buildList = array_keys($metrics['active_builds']); - - // Creating item list this way will generate a row ONLY FOR - // ACTIVE BUILDS were TEST CASES HAVE TESTER ASSIGNMENT - $buildList = array_keys($metrics['with_tester']); - $renderObj->info = array(); - foreach($buildList as $buildID) - { - $totalRun = 0; - $renderObj->info[$buildID]['build_name'] = $metrics['active_builds'][$buildID]['name']; - $renderObj->info[$buildID][$totalKey] = $metrics['total'][$buildID]['qty']; - - $renderObj->info[$buildID]['details'] = array(); - - $rf = &$renderObj->info[$buildID]['details']; - foreach($code_verbose as $statusCode => $statusVerbose) - { - $rf[$statusVerbose] = array('qty' => 0, 'percentage' => 0); - $rf[$statusVerbose]['qty'] = $metrics['with_tester'][$buildID][$statusCode]['exec_qty']; - - if( $renderObj->info[$buildID][$totalKey] > 0 ) - { - $rf[$statusVerbose]['percentage'] = number_format(100 * - ($rf[$statusVerbose]['qty'] / - $renderObj->info[$buildID][$totalKey]),1); - } - - $totalRun += $statusVerbose == 'not_run' ? 0 : $rf[$statusVerbose]['qty']; - } - $renderObj->info[$buildID]['percentage_completed'] = - number_format(100 * - ($totalRun / $renderObj->info[$buildID][$totalKey]),1); - } - - foreach($code_verbose as $human) { - $l10n = isset($labels[$human]) ? lang_get($labels[$human]) : lang_get($human); - $renderObj->colDefinition[$human]['qty'] = $l10n; - $renderObj->colDefinition[$human]['percentage'] = '[%]'; - } - - } - return $renderObj; - } - - - - /** - * Important Notice about algorithm - * We are trying to provide WHOLE Test Plan metrics, - * then BUILD INFO will not be IMPORTANT. - * - * In addition, Keywords are attributes used on - * Test Case specification, for this reason, - * our choice is that platforms will be ignored for this metrics. - * - * Example: Platform X and Y - * Test Case: TC1 with one Keyword K1 - * - * we can develop this data in this way - * - * Test Case - Platform - Keyword - Build - Exec. ID - Exec. Status - * TC1 X K1 1.0 11 FAILED - * TC1 Y K1 1.0 13 BLOCKED - * TC1 X K1 2.0 16 PASSED - * TC1 Y K1 2.0 15 BLOCKED - * - * - * We have two choices: - * OPT 1. Platform multiplication - * - * consider (as was done on Builds Overall Status) - * TC1 as two test cases. - * If we proceed this way, may be user will be confused, because - * when searching test case spec according keyword, we are going to - * find ONLY ONE. - * - * OPT 2. IGNORE PLAFORMS - * Consider only LATEST execution, means we are going - * to count ONE test case no matter how many Platforms - * exists on test plan. - * - * Our design choice is on OPT 1 - * - * @used-by - * - * 20190711 - things are changing need to relaborate - * - */ - function getExecCountersByKeywordExecStatus($id, $filters=null, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe_id = intval($id); - list($my,$builds,$sqlStm) = - $this->helperGetExecCounters($safe_id, $filters, $opt); - - - // may be too brute force but ... - if( ($tprojectID = $my['opt']['tprojectID']) == 0 ) { - $info = $this->tree_manager->get_node_hierarchy_info($safe_id); - $tprojectID = $info['parent_id']; - } - $tproject_mgr = new testproject($this->db); - $keywordSet = $tproject_mgr->get_keywords_map($tprojectID); - $tproject_mgr = null; - - - // This subquery is BETTER than a VIEW, need to understand why - // Latest Execution Ignoring Build => Cross Build - $sqlLEBP = $sqlStm['LEBP']; - - // Development Important Notice - // DISTINCT is needed when you what to get data ONLY FOR test cases with assigned testers, - // because we are (to make things worst) working on a BUILD SET, not on a SINGLE build, - // Use of IN clause, will have a NOT wanted multiplier effect on this query. - // - // This do not happens with other queries on other metric attributes, - // be careful before changing other queries. - // - $sqlUnionAK = "/* {$debugMsg} sqlUnionAK - executions */" . - " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id, TPTCV.platform_id,KW.keyword," . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . - " JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.testplan_id = " . $safe_id . - - " /* Get execution status WRITTEN on DB */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBP.id " . - - " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ ". - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['testcase_keywords']} TCK " . - " ON TCK.testcase_id = NHTCV.parent_id " . - - " JOIN {$this->tables['keywords']} KW " . - " ON KW.id = TCK.keyword_id " . - - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddExec; - - // See Note about DISTINCT, on sqlUnionAK - $sqlUnionBK = "/* {$debugMsg} sqlUnionBK - NOT RUN */" . - " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id, TPTCV.platform_id,KW.keyword," . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* Get REALLY NOT RUN => BOTH LEBP.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.testplan_id = " . $safe_id . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - $builds->joinAdd . - - " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ ". - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " JOIN {$this->tables['testcase_keywords']} TCK " . - " ON TCK.testcase_id = NHTCV.parent_id " . - - " JOIN {$this->tables['keywords']} KW " . - " ON KW.id = TCK.keyword_id " . - - " /* FILTER BUILDS in set on target test plan */ " . - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddNotRun . - - " /* Get REALLY NOT RUN => BOTH E.id AND LEBP.id NULL */ " . - " AND E.id IS NULL AND LEBP.id IS NULL"; - - // Due to PLATFORMS we will have MULTIPLIER EFFECT - $fields = "keyword_id"; - if( $my['opt']['groupByPlatform'] ) { - $fields = "platform_id,keyword_id"; - } - $sql = - " /* {$debugMsg} UNION Without ALL CLAUSE => DISCARD Duplicates */" . - " SELECT status,keyword,$fields,count(0) AS exec_qty " . - " FROM ($sqlUnionAK UNION $sqlUnionBK ) AS SQK " . - " GROUP BY status,keyword,$fields - ORDER BY keyword "; - - if( $my['opt']['groupByPlatform'] ) { - $kol = array('platform_id','keyword_id','status'); - $exec['with_tester'] = - (array)$this->db->fetchRowsIntoMap3l($sql,$kol); - - $this->helperStatusDomainMatrix($exec,'platform_id','keyword_id'); - - } else { - $exec['with_tester'] = - (array)$this->db->fetchMapRowsIntoMap($sql,'keyword_id','status'); - $this->helperCompleteStatusDomain($exec,'keyword_id'); - } - - // On next queries: - // we need to use distinct, because IF NOT we are going - // to get one record for each build where test case - // has TESTER ASSIGNMENT - // - // $exec['total_assigned'] = null; - $exec['total'] = null; - $exec['key4total'] = 'total'; - if( $my['opt']['getOnlyAssigned'] ) { - // $exec['key4total'] = 'total_assigned'; - $sql = "/* $debugMsg */ ". - " SELECT COUNT(0) AS qty,$fields " . - " FROM " . - " ( /* Get test case,keyword pairs */ " . - " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id,TPTCV.platform_id " . - " FROM {$this->tables['user_assignments']} UA " . - " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.id = UA.feature_id " . - - " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ ". - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['testcase_keywords']} TCK " . - " ON TCK.testcase_id = NHTCV.parent_id " . - " WHERE UA. build_id IN ( " . $builds->inClause . " ) " . - " AND UA.type = {$execCode} ) AS SQK ". - " GROUP BY $fields"; - } else { - $sql = "/* $debugMsg */ ". - " SELECT COUNT(0) AS qty, $fields" . - " FROM " . - " ( /* Get test case,keyword pairs */ " . - " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id,TPTCV.platform_id " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ ". - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['testcase_keywords']} TCK " . - " ON TCK.testcase_id = NHTCV.parent_id " . - " WHERE TPTCV.testplan_id = " . $safe_id . " ) AS SQK ". - " GROUP BY $fields"; - } - - if( $my['opt']['groupByPlatform'] ) { - - $exec[$exec['key4total']] = - (array)$this->db->fetchMapRowsIntoMap($sql, - 'platform_id','keyword_id'); - } else { - $exec[$exec['key4total']] = (array)$this->db->fetchRowsIntoMap($sql,'keyword_id'); - } - $exec['keywords'] = $keywordSet; - - return $exec; - } - - - /** - * - */ - function getStatusTotalsByKeywordForRender($id,$filters=null,$opt=null) { - $renderObj = $this->getStatusTotalsByItemForRender($id,'keyword',$filters,$opt); - return $renderObj; - } - - - - /** - * - * @internal revisions - * - * @since 1.9.4 - * 20120429 - franciscom - - */ - function getExecCountersByPlatformExecStatus($id, $filters=null, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe_id = intval($id); - list($my,$builds,$sqlStm,$union,$platformSet) = $this->helperBuildSQLExecCounters($id, $filters, $opt); - - $add2key = ''; - $addOnWhere = ''; - $addOnJoin = ''; - if( isset($opt['getOnlyActiveTCVersions']) ) - { - $add2key='Active'; - $addOnWhere = ' AND TCV.active = 1 '; - $addOnJoin = " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id "; - } - $sqlUnionAP = $union['exec' . $add2key]; - $sqlUnionBP = $union['not_run' . $add2key]; - - $sql = " /* {$debugMsg} UNION ALL CLAUSE => INCLUDE Duplicates */" . - " SELECT platform_id,status, count(0) AS exec_qty " . - " FROM ($sqlUnionAP UNION ALL $sqlUnionBP ) AS SQPL " . - " GROUP BY platform_id,status "; - - $exec['with_tester'] = (array)$this->db->fetchMapRowsIntoMap($sql,'platform_id','status'); - - $this->helperCompleteStatusDomain($exec,'platform_id'); - - // get total test cases by Platform id ON TEST PLAN (With & WITHOUT tester assignment) - $sql = "/* $debugMsg */ ". - " SELECT COUNT(0) AS qty, TPTCV.platform_id " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - $addOnJoin . - " WHERE TPTCV.testplan_id=" . $safe_id . $addOnWhere . - " GROUP BY platform_id"; - - $exec['total'] = (array)$this->db->fetchRowsIntoMap($sql,'platform_id'); - $exec['platforms'] = $platformSet; - - return $exec; - } - - - - /** - * - * @internal revisions - * - */ - function getStatusTotalsByPlatformForRender($id,$filters=null,$opt=null) { - $renderObj = $this->getStatusTotalsByItemForRender($id,'platform',$filters,$opt); - return $renderObj; - } - - - - /** - * - * If no build set provided, ONLY ACTIVE BUILDS will be considered - * - * - */ - function getExecCountersByPriorityExecStatus($id, $filters=null, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe_id = intval($id); - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($safe_id, $filters, $opt); - - // Due to PLATFORMS we will have MULTIPLIER EFFECT - $fields = ""; - if( $my['opt']['groupByPlatform'] ) { - $fields = ",platform_id"; - } - - $sqlLEBP = $sqlStm['LEBP']; - - $sqlUnionA = "/* {$debugMsg} sqlUnionA - executions */" . - " SELECT (TPTCV.urgency * TCV.importance) AS urg_imp, " . - " TPTCV.tcversion_id, TPTCV.platform_id," . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* Get importance */ ". - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - - " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . - " JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.testplan_id = " . $safe_id . - - " /* Get execution statuses that CAN BE WRITTEN TO DB */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBP.id " . - - // Without this we get duplicates ?? => 20121121 CONFIRMED at least with NOT RUN WE GET DUPS - $builds->joinAdd . - - " /* FILTER BUILD Set on target test plan */ " . - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddExec; - - - $sqlUnionB = "/* {$debugMsg} sqlUnionB - NOT RUN */" . - " SELECT (TPTCV.urgency * TCV.importance) AS urg_imp, " . - " TPTCV.tcversion_id, TPTCV.platform_id," . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* Get importance */ ". - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.testplan_id = " . intval($id) . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - $builds->joinAdd . - - " /* FILTER BUILDS in set on target test plan */ " . - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddNotRun . - - " /* Get REALLY NOT RUN => BOTH LEBP.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEBP.id IS NULL"; - - - // ATTENTION: - // Each piece of UNION has 3 fields: urg_imp,status, TPTCV.tcversion_id - // There is no way we can get more that ONE record with same TUPLE - // on sqlUionA or sqlUnionB ?. - // - // If we have PLATFORM we are going to get a MULTIPLIER EFFECT - // - $sql = " /* {$debugMsg} UNION WITHOUT ALL => DISCARD Duplicates */" . - " SELECT count(0) as exec_qty, urg_imp,status $fields " . - " FROM ($sqlUnionA UNION $sqlUnionB ) AS SU " . - " GROUP BY urg_imp,status $fields"; - - if( $my['opt']['groupByPlatform'] ) { - $kol = array('platform_id','urg_imp','status'); - $rs = (array)$this->db->fetchRowsIntoMap3l($sql,$kol); - } else { - $rs = $this->db->get_recordset($sql); - } - - // Now we need to get priority LEVEL from (urgency * importance) - $out = array(); - $totals = array(); - $priorityCfg = config_get('urgencyImportance'); - if( !is_null($rs) ) { - $loop2do = count($rs); - if( $my['opt']['groupByPlatform'] ) { - // loop2do => platform Qty - foreach( $rs as $platID => $elem ) { - foreach( $elem as $urgImpVal => $statusUrgImp ) { - - if ($urgImpVal >= $priorityCfg->threshold['high']) { - $hitOn = HIGH; - } else if( $urgImpVal < $priorityCfg->threshold['low']) { - $hitOn = LOW; - } else { - $hitOn = MEDIUM; - } - - $llx = 0; - foreach( $statusUrgImp as $statusCode => $dummy ) { - - $rz = &$rs[$platID][$urgImpVal][$statusCode]; - $rz['priority_level'] = $hitOn; - - // to improve readability - if( !isset($out[$hitOn][$statusCode]) ) { - $out[$platID][$hitOn][$statusCode] = $rz; - } else { - $out[$platID][$hitOn][$statusCode]['exec_qty'] += - $rz['exec_qty']; - } - if( !isset($totals[$platID][$hitOn]) ) { - $totals[$platID][$hitOn] = - array('priority_level' => $hitOn, 'qty' => 0); - } - $totals[$platID][$hitOn]['qty'] += $rz['exec_qty']; - } - } - } - } else { - // The OLD WAY - if( !is_null($rs) ) { - for($jdx=0; $jdx < $loop2do; $jdx++) { - if ($rs[$jdx]['urg_imp'] >= $priorityCfg->threshold['high']) { - $rs[$jdx]['priority_level'] = HIGH; - $hitOn = HIGH; - } else if( $rs[$jdx]['urg_imp'] < $priorityCfg->threshold['low']) { - $rs[$jdx]['priority_level'] = LOW; - $hitOn = LOW; - } else { - $rs[$jdx]['priority_level'] = MEDIUM; - $hitOn = MEDIUM; - } - - // to improve readability - $status = $rs[$jdx]['status']; - if( !isset($out[$hitOn][$status]) ) { - $out[$hitOn][$status] = $rs[$jdx]; - } else { - $out[$hitOn][$status]['exec_qty'] += $rs[$jdx]['exec_qty']; - } - - if( !isset($totals[$hitOn]) ) { - $totals[$hitOn] = array('priority_level' => $hitOn, 'qty' => 0); - } - $totals[$hitOn]['qty'] += $rs[$jdx]['exec_qty']; - } - } - } - $exec['with_tester'] = $out; - $out = null; - - } - - // $this->helperCompleteStatusDomain($exec,'priority_level'); - if( $my['opt']['groupByPlatform'] ) { - $this->helperStatusDomainMatrix($exec,'platform_id','priority_level'); - } else { - $this->helperCompleteStatusDomain($exec,'priority_level'); - } - - $exec['total'] = $totals; - - $levels = config_get('urgency'); - foreach($levels['code_label'] as $lc => $lbl) { - $exec['priority_levels'][$lc] = lang_get($lbl); - } - - return $exec; - } - - - - /** - * - * @internal revisions - * - * @since 1.9.4 - * 20120429 - franciscom - - */ - function getStatusTotalsByPriorityForRender($id,$filters=null,$opt=null) { - $renderObj = $this->getStatusTotalsByItemForRender($id,'priority_level',$filters,$opt); - return $renderObj; - } - - - /** - * - * - */ - function getExecCountersByExecStatus($id, $filters=null, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe_id = intval($id); - list($my,$builds,$sqlStm,$union,$platformSet) = $this->helperBuildSQLExecCounters($id, $filters, $opt); - - if(is_null($builds) || count($builds) <= 0) { - return null; // >>---> Bye! - } - - - // Latest Executions By Platform (LEBP) - $add2key = ''; - if( isset($opt['getOnlyActiveTCVersions']) ) { - $add2key='Active'; - } - - $sqlUnionAP = $union['exec' . $add2key]; //echo 'QD -
    ' . $sqlUnionAP . '
    '; - $sqlUnionBP = $union['not_run' . $add2key]; //echo 'QD -
    ' . $sqlUnionBP . '
    '; - - $sql = " /* {$debugMsg} UNION ALL CLAUSE => INCLUDE Duplicates */" . - " SELECT status, count(0) AS exec_qty " . - " FROM ($sqlUnionAP UNION ALL $sqlUnionBP ) AS SQPL " . - " GROUP BY status "; - - $dummy = (array)$this->db->fetchRowsIntoMap($sql,'status'); - - $statusCounters = array('total' => 0); - $codeVerbose = array_flip($this->map_tc_status); - foreach($dummy as $code => $elem) - { - - $statusCounters['total'] += $elem['exec_qty']; - $statusCounters[$codeVerbose[$code]] = $elem['exec_qty']; - } - - return $statusCounters; - } - - /** - * @internal revisions - * - * @since 1.9.6 - * 20130107 - franciscom - TICKET 5457: Incorrect data in "Report by tester per build" - */ - function getExecCountersByBuildUAExecStatus($id, $filters=null, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe_id = intval($id); - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($safe_id, $filters, $opt); - - // Last Executions By Build and Platform (LEBBP) - // Please remember that Platforms (when exists) has Multiplier effect on test cases - // - $sqlLEBBP = $sqlStm['LEBBP']; - - // 20120817 - franciscom - - // I'm not to happy with DISTINCT I've added to do this work. - // Do not understand why i get multiple identical records - // - // 20130107 - Think I've got the issue: - // was the missing clause - // " AND UA.feature_id = TPTCV.id AND UA.build_id = LEBBP.build_id " . - // - // 20140128 - do not understand why I've added DISTINT. - // without it I can add also get exec times - $sqlUnionBU = "/* {$debugMsg} */" . - " SELECT /* DISTINCT */ UA.user_id, UA.build_id, TPTCV.tcversion_id, TPTCV.platform_id, " . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status, " . - " COALESCE(E.execution_duration,0) AS execution_duration " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id IN ({$builds->inClause}) AND UA.type = {$this->execTaskCode} " . - - " /* LEFT OUTER in order to get NOT RUN */ " . - " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.testplan_id = " . $safe_id . - - " /* Without this piece we are including results for features without tester */ ". - " AND UA.feature_id = TPTCV.id AND UA.build_id = LEBBP.build_id " . - - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.build_id = UA.build_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.id = LEBBP.id " . // TICKET 5192 - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND UA.build_id IN ({$builds->inClause}) " ; - - $sql = " /* {$debugMsg} */" . - " SELECT user_id, build_id,status, count(0) AS exec_qty, SUM(execution_duration) AS total_time" . - " FROM ($sqlUnionBU) AS SQBU " . - " GROUP BY user_id,build_id,status "; - - $keyColumns = array('build_id','user_id','status'); - $exec['with_tester'] = (array)$this->db->fetchRowsIntoMap3l($sql,$keyColumns); - - $totals = array(); - foreach($exec as &$topLevelElem) - { - $topLevelItemSet = array_keys($topLevelElem); - foreach($topLevelItemSet as $topLevelItemID) - { - $itemSet = array_keys($topLevelElem[$topLevelItemID]); - foreach($itemSet as $itemID) - { - $elem = &$topLevelElem[$topLevelItemID]; - foreach($this->statusCode as $verbose => $code) - { - if(!isset($elem[$itemID][$code])) - { - $elem[$itemID][$code] = array('build_id' => $topLevelItemID, 'user_id' => $itemID, - 'status' => $code, 'exec_qty' => 0, 'total_time' => 0); - } - - if( !isset($totals[$topLevelItemID][$itemID]) ) - { - $totals[$topLevelItemID][$itemID] = array('build_id' => $topLevelItemID, - 'user_id' => $itemID, 'qty' => 0,'total_time' => 0); - } - $totals[$topLevelItemID][$itemID]['qty'] += $elem[$itemID][$code]['exec_qty']; - $totals[$topLevelItemID][$itemID]['total_time'] += $elem[$itemID][$code]['total_time']; - } - } - } - } - $exec['total'] = $totals; - - return $exec; - } - - - - /** - * @see resultsByTesterPerBuild.php - * @internal revisions - * - * @since 1.9.6 - */ - function getStatusTotalsByBuildUAForRender($id,$opt=null) - { - $my = array('opt' => array('processClosedBuilds' => true)); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $renderObj = null; - $code_verbose = $this->getStatusForReports(); - $labels = $this->resultsCfg['status_label']; - $metrics = $this->getExecCountersByBuildUAExecStatus($id,null,$my['opt']); - - if( !is_null($metrics) ) - { - $renderObj = new stdClass(); - $topItemSet = array_keys($metrics['with_tester']); - $renderObj->info = array(); - $out = &$renderObj->info; - - $topElem = &$metrics['with_tester']; - foreach($topItemSet as $topItemID) - { - $itemSet = array_keys($topElem[$topItemID]); - foreach($itemSet as $itemID) - { - $elem = &$topElem[$topItemID][$itemID]; - - $out[$topItemID][$itemID]['total'] = $metrics['total'][$topItemID][$itemID]['qty']; - $progress = 0; - foreach($code_verbose as $statusCode => $statusVerbose) - { - $out[$topItemID][$itemID][$statusVerbose]['count'] = $elem[$statusCode]['exec_qty']; - $pc = ($elem[$statusCode]['exec_qty'] / $out[$topItemID][$itemID]['total']) * 100; - $out[$topItemID][$itemID][$statusVerbose]['percentage'] = number_format($pc, 1); - - if($statusVerbose != 'not_run') - { - $progress += $elem[$statusCode]['exec_qty']; - } - } - $progress = ($progress / $out[$topItemID][$itemID]['total']) * 100; - $out[$topItemID][$itemID]['progress'] = number_format($progress,1); - $out[$topItemID][$itemID]['total_time'] = - number_format($metrics['total'][$topItemID][$itemID]['total_time'],2,'.',''); - } - } - } - return $renderObj; - } - - - /** - * - * @used-by getStatusTotalsByKeywordForRender() - * - */ - function getStatusTotalsByItemForRender($id,$itemType,$filters=null,$opt=null) { - $renderObj = null; - $code_verbose = $this->getStatusForReports(); - $labels = $this->resultsCfg['status_label']; - - - $returnArray = false; - $byPlatform = false; - - switch($itemType) { - case 'keyword': - $metrics = $this->getExecCountersByKeywordExecStatus($id,$filters,$opt); - $setKey = 'keywords'; - $byPlatform = isset($opt['groupByPlatform']) && - $opt['groupByPlatform']; - break; - - case 'platform': - $myOpt = array_merge(array('getPlatformSet' => true),(array)$opt); - $metrics = $this->getExecCountersByPlatformExecStatus($id,$filters,$myOpt); - $setKey = 'platforms'; - break; - - case 'priority_level': - $metrics = $this->getExecCountersByPriorityExecStatus($id,$filters,$opt); - $setKey = 'priority_levels'; - $byPlatform = isset($opt['groupByPlatform']) && - $opt['groupByPlatform']; - break; - - case 'tsuite': - $metrics = $this->getExecCountersByTestSuiteExecStatus($id,$filters,$opt); - $setKey = 'tsuites'; - $returnArray = true; - $byPlatform = isset($opt['groupByPlatform']) && - $opt['groupByPlatform']; - break; - } - - if( !is_null($metrics) && !is_null($metrics[$setKey]) > 0) { - $renderObj = new stdClass(); - $renderObj->info = array(); - - if( $byPlatform == false ) { - $itemList = array_keys($metrics[$setKey]); - foreach($itemList as $itemID) { - if( isset($metrics['with_tester'][$itemID]) ) { - $totalRun = 0; - $renderObj->info[$itemID]['type'] = $itemType; - $renderObj->info[$itemID]['name'] = $metrics[$setKey][$itemID]; - $renderObj->info[$itemID]['total_tc'] = $metrics['total'][$itemID]['qty']; - $renderObj->info[$itemID]['details'] = array(); - - $rf = &$renderObj->info[$itemID]['details']; - $doPerc = ($renderObj->info[$itemID]['total_tc'] > 0); - foreach($code_verbose as $statusCode => $statusVerbose) { - $rf[$statusVerbose] = array('qty' => 0, 'percentage' => 0); - $rf[$statusVerbose]['qty'] = $metrics['with_tester'][$itemID][$statusCode]['exec_qty']; - - if($doPerc) { - $rf[$statusVerbose]['percentage'] = - number_format(100 * ($rf[$statusVerbose]['qty'] / - $renderObj->info[$itemID]['total_tc']),1); - } - $totalRun += - ($statusVerbose == 'not_run' ? 0 : $rf[$statusVerbose]['qty']); - } - if($doPerc) { - $renderObj->info[$itemID]['percentage_completed'] = number_format(100 * - ($totalRun/$renderObj->info[$itemID]['total_tc']),1); - } - } - } - } else { - // mainKey is Platform ID - $platList = array_keys($metrics['with_tester']); - $mex = &$metrics['with_tester']; - foreach($platList as $platID) { - $itemList = array_keys($mex[$platID]); - foreach($itemList as $itemID) { - if( isset($mex[$platID]) ) { - $totalRun = 0; - $renderObj->info[$platID][$itemID]['type'] = $itemType; - $renderObj->info[$platID][$itemID]['name'] = $metrics[$setKey][$itemID]; - $renderObj->info[$platID][$itemID]['total_tc'] = $metrics['total'][$platID][$itemID]['qty']; - $renderObj->info[$platID][$itemID]['details'] = array(); - - $rf = &$renderObj->info[$platID][$itemID]['details']; - $doPerc = ($renderObj->info[$platID][$itemID]['total_tc'] > 0); - foreach($code_verbose as $statusCode => $statusVerbose) { - $rf[$statusVerbose] = array('qty' => 0, 'percentage' => 0); - $rf[$statusVerbose]['qty'] = $mex[$platID][$itemID][$statusCode]['exec_qty']; - - if($doPerc) { - $rf[$statusVerbose]['percentage'] = - number_format(100 * ($rf[$statusVerbose]['qty'] / - $renderObj->info[$platID][$itemID]['total_tc']),1); - } - $totalRun += - ($statusVerbose == 'not_run' ? 0 : $rf[$statusVerbose]['qty']); - } - if($doPerc) { - $renderObj->info[$platID][$itemID]['percentage_completed'] = number_format(100 * - ($totalRun/$renderObj->info[$platID][$itemID]['total_tc']),1); - } - } - } - } - } - - // Verbosity! - foreach($code_verbose as $status_verbose) { - $l10n = isset($labels[$status_verbose]) ? lang_get($labels[$status_verbose]) : lang_get($status_verbose); - - $renderObj->colDefinition[$status_verbose]['qty'] = $l10n; - $renderObj->colDefinition[$status_verbose]['percentage'] = '[%]'; - } - } - - // How to return things - if($returnArray) { - return array($renderObj,$metrics['staircase']); - } else { - unset($metrics); - return $renderObj; - } - } - - - /** - * - * @internal revisions - * - * @since 1.9.4 - * 20120429 - franciscom - - */ - function getStatusTotalsByTestSuiteForRender($id,$filters=null,$opt=null) - { - list($renderObj,$staircase) = $this->getStatusTotalsByItemForRender($id,'tsuite',$filters,$opt); - unset($staircase); - return $renderObj; - } - - /** - * - * @internal revisions - * - * @since 1.9.4 - */ - function getStatusTotalsByTopLevelTestSuiteForRender($id,$filters=null,$opt=null) - { - - list($rx,$staircase) = $this->getStatusTotalsByItemForRender($id,'tsuite',$filters,$opt); - - // ??? $key2loop = array_keys($rx->info); - $template = array('type' => 'tsuite', 'name' => '','total_tc' => 0, - 'percentage_completed' => 0, 'details' => array()); - - foreach($this->statusCode as $verbose => $code) { - $template['details'][$verbose] = array('qty' => 0, 'percentage' => 0); - } - - $renderObj = new stdClass(); - $renderObj->colDefinition = $rx->colDefinition; - - // collect qty - $topNameCache = null; - $execQty = null; - - $key2loop = array_keys($staircase); - $wp = isset($opt['groupByPlatform']) && $opt['groupByPlatform']; - - if( $wp ) { - $plat2loop = array_keys($rx->info); - foreach($key2loop as $tsuite_id) { - // (count() == 1) => is a TOP LEVEL SUITE, - // only element contains Root node, is useless for this algorithm - // - - if( count($staircase[$tsuite_id]) > 1) { - // element at position 1 is a TOP LEVEL SUITE - //$topSuiteID = &$staircase[$tsuite_id][1]; - $topSuiteID = $staircase[$tsuite_id][1]; - $initName = false; - } else { - $topSuiteID = $tsuite_id; - $initName = true; - } - - // Over Platform - foreach( $plat2loop as $platId ) { - if( !isset($renderObj->info[$platId][$topSuiteID]) ) { - $renderObj->info[$platId][$topSuiteID] = $template; - $execQty[$platId][$topSuiteID] = 0; - $initName = true; - } - - if( $initName ) { - $dummy = $this->tree_manager->get_node_hierarchy_info($topSuiteID); - $renderObj->info[$platId][$topSuiteID]['name'] = - $dummy['name']; - unset($dummy); - } - - // Loop to get executions counters - $r2d2 = &$rx->info[$platId][$tsuite_id]; - if( null !== $r2d2 ) { - foreach($r2d2['details'] as $code => &$elem) { - $renderObj->info[$platId][$topSuiteID]['details'][$code] - ['qty'] += $elem['qty']; - $renderObj->info[$platId][$topSuiteID]['total_tc'] += - $elem['qty']; - - if( $code != 'not_run' ) { - $execQty[$platId][$topSuiteID] += $elem['qty']; - } - } - } - } - } - - // Last step: get percentages - foreach($renderObj->info as $platID => &$tsuiteMetrics) { - foreach($tsuiteMetrics as $tsuite_id => &$elem) { - if( $execQty[$platID][$tsuite_id] > 0 ) { - $elem['percentage_completed'] = number_format( 100 * - ($execQty[$platID][$tsuite_id] / $elem['total_tc']),1); - } - if( $elem['total_tc'] > 0 ) { - foreach($elem['details'] as $code => &$yumyum) { - $yumyum['percentage'] = number_format( 100 * ($yumyum['qty'] / $elem['total_tc']),1); - } - } - } - } - - } else { - - // OLD WAY - foreach($key2loop as $tsuite_id) { - // (count() == 1) => is a TOP LEVEL SUITE, - // only element contains Root node, is useless for this algorithm - // - - if( count($staircase[$tsuite_id]) > 1) { - // element at position 1 is a TOP LEVEL SUITE - $topSuiteID = &$staircase[$tsuite_id][1]; - $initName = false; - } else { - $topSuiteID = $tsuite_id; - $initName = true; - } - - if( !isset($renderObj->info[$topSuiteID]) ) { - $renderObj->info[$topSuiteID] = $template; - $execQty[$topSuiteID] = 0; - $initName = true; - } - - if( $initName ) { - $dummy = $this->tree_manager->get_node_hierarchy_info($topSuiteID); - $renderObj->info[$topSuiteID]['name'] = $dummy['name']; - unset($dummy); - } - - - // Loop to get executions counters - foreach($rx->info[$tsuite_id]['details'] as $code => &$elem) { - $renderObj->info[$topSuiteID]['details'][$code]['qty'] += $elem['qty']; - $renderObj->info[$topSuiteID]['total_tc'] += $elem['qty']; - - if( $code != 'not_run' ) { - $execQty[$topSuiteID] += $elem['qty']; - } - } - } - - // Last step: get percentages - foreach($renderObj->info as $tsuite_id => &$elem) { - if( $execQty[$tsuite_id] > 0 ) { - $elem['percentage_completed'] = number_format( 100 * ($execQty[$tsuite_id] / $elem['total_tc']),1); - } - - if( $elem['total_tc'] > 0 ) { - foreach($elem['details'] as $code => &$yumyum) { - $yumyum['percentage'] = number_format( 100 * ($yumyum['qty'] / $elem['total_tc']),1); - } - } - } - } - - - unset($topNameCache); - unset($rx); - unset($staircase); - unset($template); - unset($key2loop); - unset($execQty); - - return $renderObj; - } - - /** - * - * - * - * - */ - function getExecCountersByTestSuiteExecStatus($id, $filters=null, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safe_id = intval($id); - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($id, $filters, $opt); - - $fields = ""; - if( $my['opt']['groupByPlatform'] ) { - $fields = ",platform_id"; - } - - - // Latest Execution Ignoring Build - $sqlLEBP = $sqlStm['LEBP']; - - $sqlUnionAT = "/* {$debugMsg} sqlUnionAT - executions */" . - " SELECT NHTC.parent_id AS tsuite_id,TPTCV.platform_id," . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . - " JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.testplan_id = " . $safe_id . - - " /* Get execution status WRITTEN on DB */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBP.id " . - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddExec; - - - - $sqlUnionBT = "/* {$debugMsg} sqlUnionBK - NOT RUN */" . - " SELECT NHTC.parent_id AS tsuite_id,TPTCV.platform_id," . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* Get REALLY NOT RUN => BOTH LEBP.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.testplan_id = " . $safe_id . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - - $builds->joinAdd . - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " /* FILTER BUILDS in set on target test plan (not alway can be applied) */ " . - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddNotRun . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEBP.id IS NULL"; - - - $sql = " /* {$debugMsg} UNION ALL DO NOT DISCARD Duplicates */" . - " SELECT count(0) AS exec_qty, tsuite_id, status $fields" . - " FROM ($sqlUnionAT UNION ALL $sqlUnionBT ) AS SQT " . - " GROUP BY tsuite_id ,status $fields"; - - if( $my['opt']['groupByPlatform'] ) { - $kol = array('platform_id','tsuite_id','status'); - $exec['with_tester'] = - (array)$this->db->fetchRowsIntoMap3l($sql,$kol); - $this->helperStatusDomainMatrix($exec,'platform_id','tsuite_id'); - - } else { - $exec['with_tester'] = - (array)$this->db->fetchMapRowsIntoMap($sql,'tsuite_id','status'); - - // now we need to complete status domain - $this->helperCompleteStatusDomain($exec,'tsuite_id'); - } - - - // Build item set - $exec['tsuites_full'] = $this->get_testsuites($safe_id); - $loop2do = count($exec['tsuites_full']); - for($idx=0; $idx < $loop2do; $idx++) { - $keySet[] = $exec['tsuites_full'][$idx]['id']; - - } - $dx = $this->tree_manager->get_full_path_verbose($keySet,array('output_format' => 'stairway2heaven')); - for($idx=0; $idx < $loop2do; $idx++) { - $exec['tsuites'][$exec['tsuites_full'][$idx]['id']] = $dx['flat'][$exec['tsuites_full'][$idx]['id']]; - } - $exec['staircase'] = $dx['staircase']; - - unset($dx); - unset($keySet); - return $exec; - } - - - - - /** - * - * - * - * - */ - function getExecStatusMatrix($id, $filters=null, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array(); - $my['opt'] = array('getExecutionNotes' => false, - 'getTester' => false, - 'getUserAssignment' => false, - 'output' => null, - 'getExecutionTimestamp' => false, - 'getExecutionDuration' => false); - - $my['opt'] = array_merge($my['opt'], (array)$opt); - $safe_id = intval($id); - list($my,$builds,$sqlStm,$union) = $this->helperBuildSQLTestSuiteExecCounters($id, $filters, $my['opt']); - - $sql = " /* {$debugMsg} UNION WITH ALL CLAUSE */ " . - " {$union['exec']} UNION ALL {$union['not_run']} "; - - - $keyColumns = array('tsuite_id','tcase_id','platform_id','build_id'); - $cumulative = ($my['opt']['output'] == 'cumulative'); - $dummy = (array)$this->db->fetchRowsIntoMap4l($sql,$keyColumns,$cumulative); - - unset($sqlStm); - unset($union); - unset($my); - unset($builds); - - // now is time do some decoding - // Key is a tuple (PARENT tsuite_id, test case id, platform id) - // - $item2loop = array_keys($dummy); - $stairway2heaven = null; - $pathway = null; - $latestExec = null; - $priorityCfg = config_get('urgencyImportance'); - - foreach ($item2loop as $item_id) { - $stairway2heaven = $this->tree_manager->get_path($item_id,null,'name'); - $pathway[$item_id] = implode("/",$stairway2heaven); - unset($stairway2heaven); - - // go inside test case - $tcase2loop = array_keys($dummy[$item_id]); - foreach($tcase2loop as $tcase_id) { - $platform2loop = array_keys($dummy[$item_id][$tcase_id]); - foreach($platform2loop as $platform_id) { - $latestExec[$platform_id][$tcase_id] = - array('id' => -1, 'status' => $this->notRunStatusCode); - $rf = &$dummy[$item_id][$tcase_id][$platform_id]; - foreach($rf as $build_id => &$exec) { - $exec['suiteName'] = $pathway[$item_id]; - if($exec['executions_id'] > $latestExec[$platform_id][$tcase_id]['id']) { - $latestExec[$platform_id][$tcase_id]['id'] = $exec['executions_id']; - $latestExec[$platform_id][$tcase_id]['status'] = $exec['status']; - $latestExec[$platform_id][$tcase_id]['build_id'] = $exec['build_id']; - $latestExec[$platform_id][$tcase_id]['execution_notes'] = $exec['execution_notes']; - } - - // --------------------------------------------------- - // Now we need to get priority LEVEL from - // (urgency * impact) - // we do not use a function to improve performance - if ($exec['urg_imp'] >= $priorityCfg->threshold['high']) { - $exec['priority_level'] = HIGH; - } else if( $exec['urg_imp'] < $priorityCfg->threshold['low']) { - $exec['priority_level'] = LOW; - } else { - $exec['priority_level'] = MEDIUM; - } - // ------------------------------------------------------ - } // $rf - } // $platform2loop - } // $tcase2loop - - unset($tcase2loop); - unset($platform2loop); - } // - - unset($pathway); - return array('metrics' => $dummy, 'latestExec' => $latestExec); - } - - - - /** - * - * @used-by - * getExecutionsByStatus() - * getNotRunWithTesterAssigned() - * getNotRunWOTesterAssigned() - * getExecCountersByBuildExecStatus() - * getExecCountersByKeywordExecStatus() - * getExecCountersByPriorityExecStatus() - * getExecCountersByBuildUAExecStatus() - * getExecCountersByTestSuiteExecStatus() - * - * - * - */ - function helperGetExecCounters($id, $filters, $opt) { - $sql = array(); - $my = array(); - $my['opt'] = array('getOnlyAssigned' => false, - 'tprojectID' => 0, - 'getUserAssignment' => false, - 'getPlatformSet' => false, - 'processClosedBuilds' => true, - 'groupByPlatform' => false); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - $my['filters'] = array('buildSet' => null); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - // Build Info - $bi = new stdClass(); - $bi->idSet = $my['filters']['buildSet']; - $bi->inClause = ''; - $bi->infoSet = null; - - if (is_null($bi->idSet)) { - $openStatus = $my['opt']['processClosedBuilds'] ? null : 1; - - $bi->infoSet = $this->get_builds($id,testplan::ACTIVE_BUILDS, - $openStatus); - if (!is_null($bi->infoSet)) { - $bi->idSet = array_keys($bi->infoSet); - } - } - - // ========================================================= - // Emergency Exit !!! - if ( is_null($bi->idSet) ) { - throw new Exception(__METHOD__ . " - Can not work with empty build set"); - } - // ========================================================= - - - // Things seems to be OK - $bi->inClause = implode(",",$bi->idSet); - if( $my['opt']['getOnlyAssigned'] ) { - $sql['getAssignedFeatures'] = " /* Get feature id with Tester Assignment */ " . - " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id IN ({$bi->inClause}) " . - " AND UA.type = {$this->execTaskCode} "; - $bi->source = "UA"; - $bi->joinAdd = " AND E.build_id = UA.build_id "; - $bi->whereAddExec = " AND {$bi->source}.build_id IN ({$bi->inClause}) "; - $bi->whereAddNotRun = $bi->whereAddExec; - } - else - { - $sql['getAssignedFeatures'] = ''; - $bi->source = "E"; - - // TICKET 5353 - // $bi->joinAdd = ""; - $bi->joinAdd = " AND E.build_id IN ({$bi->inClause}) "; - - // Why ? - // If I'm consider test cases WITH and WITHOUT Tester assignment, - // I will have no place to go to filter for builds. - // Well at least when trying to get EXECUTED test case, I will be able - // to apply filter on Executions table. - // Why then I choose to have this blank ANYWAY ? - // Because I will get filtering on Build set through - // the Latest Execution queries (see below sql['LE'], sql['LEBP']. - // - // Anyway we need to backup all these thoughts with a long, long test run - // on test link itself. - $bi->whereAddExec = " AND {$bi->source}.build_id IN ({$bi->inClause}) "; - $bi->whereAddNotRun = ""; - } - - $sql['getUserAssignment']['not_run'] = ""; - $sql['getUserAssignment']['exec'] = ""; - - if( $my['opt']['getUserAssignment'] ) - { - $sql['getUserAssignment']['not_run'] = - " LEFT JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id = BU.id " . - " AND UA.type = {$this->execTaskCode} "; - - $sql['getUserAssignment']['exec'] = - " LEFT JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id = E.build_id " . - " AND UA.type = {$this->execTaskCode} "; - } - - - // Latest Execution IGNORING Build and Platform - $sql['LE'] = " SELECT EE.tcversion_id,EE.testplan_id,MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id=" . intval($id) . - " AND EE.build_id IN ({$bi->inClause}) " . - " GROUP BY EE.tcversion_id,EE.testplan_id "; - - - // Latest Execution By Platform (ignore build) - $sql['LEBP'] = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id=" . intval($id) . - " AND EE.build_id IN ({$bi->inClause}) " . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id "; - - // Last Executions By Build (LEBB) (ignore platform) - $sql['LEBB'] = " SELECT EE.tcversion_id,EE.testplan_id,EE.build_id,MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id=" . intval($id) . - " AND EE.build_id IN ({$bi->inClause}) " . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.build_id "; - - - // Last Executions By Build and Platform (LEBBP) - $sql['LEBBP'] = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id=" . intval($id) . - " AND EE.build_id IN ({$bi->inClause}) " . - " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; - - - return array($my,$bi,$sql); - } - - - - /** - * - * - * - * - */ - function helperCompleteStatusDomain(&$out,$key) { - $totalByItemID = array(); - - // refence is critic - foreach($out as &$elem) { - $itemSet = array_keys($elem); - foreach($itemSet as $itemID) { - $totalByItemID[$itemID]['qty'] = 0; - foreach($this->statusCode as $verbose => $code) { - if(!isset($elem[$itemID][$code])) { - $elem[$itemID][$code] = array($key => $itemID,'status' => $code, 'exec_qty' => 0); - } - $totalByItemID[$itemID]['qty'] += $elem[$itemID][$code]['exec_qty']; - } - } - } - $out['total'] = $totalByItemID; - } - - - - /** - * - * - */ - function helperBuildSQLExecCounters($id, $filters=null, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - try - { - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($id, $filters, $opt); - } - catch(Exception $e) - { - return null; - } - - - $safe_id = intval($id); - $platformSet = null; - if( $my['opt']['getPlatformSet'] ) { - $getOpt = array('outputFormat' => 'mapAccessByID', 'outputDetails' => 'name', 'addIfNull' => true); - $platformSet = $this->getPlatforms($safe_id,$getOpt); - } - - // Latest Executions By Platform (LEBP) - $sqlLEBP = $sqlStm['LEBP']; - - - // 20121121 - franciscom - // Need to understand if this sentence is right: - // - // GO FOR Absolute LATEST exec ID IGNORE BUILD - // Is this right for each use of this method ? - // - $dummy['exec'] = "/* {$debugMsg} sqlUnion - executions */" . - " SELECT TPTCV.tcversion_id,TPTCV.platform_id, " . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . - " JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.testplan_id = " . $safe_id . - - " /* Get execution status WRITTEN on DB */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBP.id "; - - - $union['exec'] = $dummy['exec'] . " WHERE TPTCV.testplan_id=" . $safe_id . $builds->whereAddExec; - - $union['execActive'] = $dummy['exec'] . - " /* Used to filter ON ACTIVE TCVersion */ " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - " WHERE TPTCV.testplan_id=" . $safe_id . $builds->whereAddExec . - " AND TCV.active = 1 "; - - - // 20121121 - An issue was reported in this scenario: - // Test Plan with Platforms (ONE) - // Two Build: - // B1 with TC1 passed, TC2 failed, TC3 not run - BUT B1 INACTIVE - // B3 ALL TEST CASES NOT RUN - // - // we got WRONG figures if build set is NOT USING when trying to access Executions TABLE - // - $dummy['not_run'] = "/* {$debugMsg} sqlUnion - NOT RUN */" . - " SELECT TPTCV.tcversion_id,TPTCV.platform_id, " . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . - " ON LEBP.testplan_id = TPTCV.testplan_id " . - " AND LEBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBP.platform_id = TPTCV.platform_id " . - " AND LEBP.testplan_id = " . $safe_id . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - $builds->joinAdd; - - - $union['not_run'] = $dummy['not_run'] . - " /* FILTER BUILDS in set on target test plan (not always can be applied) */ " . - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddNotRun . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEBP.id IS NULL"; - - - $union['not_runActive'] = $dummy['not_run'] . - " /* Used to filter ON ACTIVE TCVersion */ " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddNotRun . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEBP.id IS NULL" . - " AND TCV.active = 1 "; - - - //echo 'QD -
    ' . $sqlUnionBP . '
    '; - return array($my,$builds,$sqlStm,$union,$platformSet); - } - - - /** - * - * - * - * @internal revision - * @since 1.9.8 - * 20130713 - franciscom - - * when getting info for executed test cases, RIGHT version number for execution - * is on EXECUTIONS TABLE not on testplan_tcversions TABLE. - * - * REMEMBER that when we update TCVERSION for executed Test Cases, we HAVE TO UPDATE - * testplan_tcversions table. - * - * We also need to use E.tcversion_id and NOT TPTCV.tcversion_id. - * - */ - function helperBuildSQLTestSuiteExecCounters($id, $filters=null, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('getExecutionNotes' => false, - 'getTester' => false, - 'getUserAssignment' => false, - 'getExecutionTimestamp' => false, - 'getExecutionDuration' => false); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($id, $filters, $opt); - - $safe_id = intval($id); - - - // Additional Execution fields - $moreExecFields = ""; - if ($my['opt']['getExecutionNotes']) { - $moreExecFields .= "E.notes AS execution_notes,"; - } - - if ($my['opt']['getTester']) { - $moreExecFields .= "E.tester_id,"; - } - - if ($my['opt']['getExecutionTimestamp']) { - $moreExecFields .= "E.execution_ts,"; - } - - if ($my['opt']['getExecutionDuration']) { - $moreExecFields .= "E.execution_duration,"; - } - - if ($my['opt']['getUserAssignment']) { - $moreExecFields .= "UA.user_id,"; - } - - // Latest Executions By Build Platform (LEBBP) - $sqlLEBBP = $sqlStm['LEBBP']; - - $union['exec'] = "/* {$debugMsg} sqlUnion Test suites - executions */" . - " SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . - " TPTCV.tcversion_id,TPTCV.platform_id," . - " E.build_id,E.tcversion_number AS version,TCV.tc_external_id AS external_id, " . - " E.id AS executions_id, E.status AS status, " . - $moreExecFields . - " E.execution_type AS exec_type," . - - " (TPTCV.urgency * TCV.importance) AS urg_imp " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* GO FOR Absolute LATEST exec ID On BUILD,PLATFORM */ " . - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.testplan_id = " . $safe_id . - - " /* Get execution status WRITTEN on DB */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . - " AND E.build_id = LEBBP.build_id " . - - $sqlStm['getUserAssignment']['exec'] . - - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " /* Get Test Case Version attributes */ " . - " JOIN {$this->tables['tcversions']} TCV " . - // " ON TCV.id = TPTCV.tcversion_id " . - " ON TCV.id = E.tcversion_id " . - - " WHERE TPTCV.testplan_id=" . $safe_id . - $builds->whereAddExec; - - $union['not_run'] = - "/* {$debugMsg} sqlUnion Test suites - not run */" . - " SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . - " TPTCV.tcversion_id, TPTCV.platform_id," . - " BU.id AS build_id,TCV.version,TCV.tc_external_id AS external_id, " . - " COALESCE(E.id,-1) AS executions_id, " . - " COALESCE(E.status,'{$this->notRunStatusCode}') AS status, " . - $moreExecFields . - " TCV.execution_type AS exec_type," . - " (TPTCV.urgency * TCV.importance) AS urg_imp " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* Needed to be able to put a value on build_id on output set */ " . - " JOIN {$this->tables['builds']} BU " . - " ON BU.id IN ({$builds->inClause}) " . - - $sqlStm['getUserAssignment']['not_run'] . - - " /* GO FOR Absolute LATEST exec ID On BUILD,PLATFORM */ " . - " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.build_id = BU.id " . - " AND LEBBP.testplan_id = " . $safe_id . - - " /* Get execution status WRITTEN on DB */ " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.build_id = LEBBP.build_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " /* Get Test Case Version attributes */ " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND BU.id IN ({$builds->inClause}) " . - $builds->whereAddNotRun . - - " /* Get REALLY NOT RUN => BOTH LEBBP.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEBBP.id IS NULL"; - - //echo 'QD -
    ' . $union['not_run'] . '
    '; - return array($my,$builds,$sqlStm,$union); - } - - - /** - * get executions (Not Run is not included) - * - * @param int $id test plan id - * @param char $status status code (one char) - * @param mixed $filters - * keys: 'buildSet' - * - * @param mixed opt - * keys: 'output' elem domain 'map','array' - * - */ - function getExecutionsByStatus($id,$status,$filters=null,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($id, $filters, $opt); - - // particular options - $options = array('output' => 'map', - 'add2fields' => ''); - $my['opt'] = array_merge($options,$my['opt']); - $safe_id = intval($id); - - $fullEID = $this->helperConcatTCasePrefix($safe_id); - - $addFields = ''; - if ( '' != $my['opt']['add2fields']) { - $addFields = ',' . $my['opt']['add2fields']; - } - - $sqlLEBBP = $sqlStm['LEBBP']; - $sql = "/* {$debugMsg} executions with status WRITTEN on DB => not run is not present */" . - " SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . - " TPTCV.tcversion_id,TPTCV.platform_id," . - " E.tcversion_number, E.build_id,E.id AS executions_id, E.status AS status, " . - " E.notes AS execution_notes, E.tester_id,E.execution_ts," . - " TCV.version,TCV.tc_external_id AS external_id, " . - " $fullEID AS full_external_id," . - " (TPTCV.urgency * TCV.importance) AS urg_imp " . - $addFields . - - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " /* GO FOR Absolute LATEST exec ID On BUILD,PLATFORM */ " . - " JOIN ({$sqlLEBBP}) AS LEBBP " . - " ON LEBBP.testplan_id = TPTCV.testplan_id " . - " AND LEBBP.platform_id = TPTCV.platform_id " . - " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . - " AND LEBBP.testplan_id = " . $safe_id . - - $sqlStm['getAssignedFeatures'] . - - " /* Get execution status WRITTEN on DB */ " . - " JOIN {$this->tables['executions']} E " . - " ON E.id = LEBBP.id " . - " AND E.build_id = LEBBP.build_id " . - $builds->joinAdd . - - - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + ) TT + GROUP BY build_id "; + + $exec['total'] = (array) $this->db->fetchRowsIntoMap($sql, + 'build_id'); + } + + $exec['active_builds'] = $builds->infoSet; + + return $exec; + } + + /** + */ + public function getOverallBuildStatusForRender($id, + $totalKey = 'total_assigned') + { + $renderObj = null; + $code_verbose = $this->getStatusForReports(); + $labels = $this->resultsCfg['status_label']; + + $metrics = $this->getExecCountersByBuildExecStatus($id); + if (! is_null($metrics)) { + $renderObj = new stdClass(); + + // Creating item list this way will generate a row also for + // ACTIVE BUILDS were ALL TEST CASES HAVE NO TESTER ASSIGNMENT + // $buildList = array_keys($metrics['active_builds']); + + // Creating item list this way will generate a row ONLY FOR + // ACTIVE BUILDS were TEST CASES HAVE TESTER ASSIGNMENT + $buildList = array_keys($metrics['with_tester']); + $renderObj->info = array(); + foreach ($buildList as $buildID) { + $totalRun = 0; + $renderObj->info[$buildID]['build_name'] = $metrics['active_builds'][$buildID]['name']; + $renderObj->info[$buildID][$totalKey] = $metrics['total'][$buildID]['qty']; + + $renderObj->info[$buildID]['details'] = array(); + + $rf = &$renderObj->info[$buildID]['details']; + foreach ($code_verbose as $statusCode => $statusVerbose) { + $rf[$statusVerbose] = array( + 'qty' => 0, + 'percentage' => 0 + ); + $rf[$statusVerbose]['qty'] = $metrics['with_tester'][$buildID][$statusCode]['exec_qty']; + + if ($renderObj->info[$buildID][$totalKey] > 0) { + $rf[$statusVerbose]['percentage'] = number_format( + 100 * + ($rf[$statusVerbose]['qty'] / + $renderObj->info[$buildID][$totalKey]), 1); + } + + $totalRun += $statusVerbose == 'not_run' ? 0 : $rf[$statusVerbose]['qty']; + } + $renderObj->info[$buildID]['percentage_completed'] = number_format( + 100 * ($totalRun / $renderObj->info[$buildID][$totalKey]), 1); + } + + foreach ($code_verbose as $human) { + $l10n = isset($labels[$human]) ? lang_get($labels[$human]) : lang_get( + $human); + $renderObj->colDefinition[$human]['qty'] = $l10n; + $renderObj->colDefinition[$human]['percentage'] = '[%]'; + } + } + return $renderObj; + } + + /** + * Important Notice about algorithm + * We are trying to provide WHOLE Test Plan metrics, + * then BUILD INFO will not be IMPORTANT. + * + * In addition, Keywords are attributes used on + * Test Case specification, for this reason, + * our choice is that platforms will be ignored for this metrics. + * + * Example: Platform X and Y + * Test Case: TC1 with one Keyword K1 + * + * we can develop this data in this way + * + * Test Case - Platform - Keyword - Build - Exec. ID - Exec. Status + * TC1 X K1 1.0 11 FAILED + * TC1 Y K1 1.0 13 BLOCKED + * TC1 X K1 2.0 16 PASSED + * TC1 Y K1 2.0 15 BLOCKED + * + * + * We have two choices: + * OPT 1. Platform multiplication + * + * consider (as was done on Builds Overall Status) + * TC1 as two test cases. + * If we proceed this way, may be user will be confused, because + * when searching test case spec according keyword, we are going to + * find ONLY ONE. + * + * OPT 2. IGNORE PLAFORMS + * Consider only LATEST execution, means we are going + * to count ONE test case no matter how many Platforms + * exists on test plan. + * + * Our design choice is on OPT 1 + * + * @used-by + * + * 20190711 - things are changing need to relaborate + * + */ + private function getExecCountersByKeywordExecStatus($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe_id = intval($id); + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($safe_id, + $filters, $opt); + + // may be too brute force but ... + if (($tprojectID = $my['opt']['tprojectID']) == 0) { + $info = $this->tree_manager->get_node_hierarchy_info($safe_id); + $tprojectID = $info['parent_id']; + } + $tproject_mgr = new testproject($this->db); + $keywordSet = $tproject_mgr->get_keywords_map($tprojectID); + $tproject_mgr = null; + + // This subquery is BETTER than a VIEW, need to understand why + // Latest Execution Ignoring Build => Cross Build + $sqlLEBP = $sqlStm['LEBP']; + + // Development Important Notice + // DISTINCT is needed when you what to get data ONLY FOR test cases with assigned testers, + // because we are (to make things worst) working on a BUILD SET, not on a SINGLE build, + // Use of IN clause, will have a NOT wanted multiplier effect on this query. + // + // This do not happens with other queries on other metric attributes, + // be careful before changing other queries. + // + $sqlUnionAK = "/* {$debugMsg} sqlUnionAK - executions */" . + " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id, TPTCV.platform_id,KW.keyword," . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . + " JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.testplan_id = " . $safe_id . + " /* Get execution status WRITTEN on DB */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBP.id " . + " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['testcase_keywords']} TCK " . + " ON TCK.testcase_id = NHTCV.parent_id " . + " JOIN {$this->tables['keywords']} KW " . + " ON KW.id = TCK.keyword_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . $builds->whereAddExec; + + // See Note about DISTINCT, on sqlUnionAK + $sqlUnionBK = "/* {$debugMsg} sqlUnionBK - NOT RUN */" . + " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id, TPTCV.platform_id,KW.keyword," . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* Get REALLY NOT RUN => BOTH LEBP.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.testplan_id = " . $safe_id . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . $builds->joinAdd . + " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['testcase_keywords']} TCK " . + " ON TCK.testcase_id = NHTCV.parent_id " . + " JOIN {$this->tables['keywords']} KW " . + " ON KW.id = TCK.keyword_id " . + " /* FILTER BUILDS in set on target test plan */ " . + " WHERE TPTCV.testplan_id=" . $safe_id . $builds->whereAddNotRun . + " /* Get REALLY NOT RUN => BOTH E.id AND LEBP.id NULL */ " . + " AND E.id IS NULL AND LEBP.id IS NULL"; + + // Due to PLATFORMS we will have MULTIPLIER EFFECT + $fields = "keyword_id"; + if ($my['opt']['groupByPlatform']) { + $fields = "platform_id,keyword_id"; + } + $sql = " /* {$debugMsg} UNION Without ALL CLAUSE => DISCARD Duplicates */" . + " SELECT status,keyword,$fields,count(0) AS exec_qty " . + " FROM ($sqlUnionAK UNION $sqlUnionBK ) AS SQK " . + " GROUP BY status,keyword,$fields + ORDER BY keyword "; + + if ($my['opt']['groupByPlatform']) { + $kol = array( + 'platform_id', + 'keyword_id', + 'status' + ); + $exec['with_tester'] = (array) $this->db->fetchRowsIntoMap3l($sql, + $kol); + + $this->helperStatusDomainMatrix($exec, 'platform_id', 'keyword_id'); + } else { + $exec['with_tester'] = (array) $this->db->fetchMapRowsIntoMap($sql, + 'keyword_id', 'status'); + $this->helperCompleteStatusDomain($exec, 'keyword_id'); + } + + // On next queries: + // we need to use distinct, because IF NOT we are going + // to get one record for each build where test case + // has TESTER ASSIGNMENT + // + // $exec['total_assigned'] = null; + $exec['total'] = null; + $exec['key4total'] = 'total'; + if ($my['opt']['getOnlyAssigned']) { + + $sql = "/* $debugMsg */ " . " SELECT COUNT(0) AS qty,$fields " . + " FROM " . " ( /* Get test case,keyword pairs */ " . + " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id,TPTCV.platform_id " . + " FROM {$this->tables['user_assignments']} UA " . + " JOIN {$this->tables['testplan_tcversions']} TPTCV ON TPTCV.id = UA.feature_id " . + " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['testcase_keywords']} TCK " . + " ON TCK.testcase_id = NHTCV.parent_id " . + " WHERE UA. build_id IN ( " . $builds->inClause . " ) " . + " AND UA.type = {$execCode} ) AS SQK " . " GROUP BY $fields"; + } else { + $sql = "/* $debugMsg */ " . " SELECT COUNT(0) AS qty, $fields" . + " FROM " . " ( /* Get test case,keyword pairs */ " . + " SELECT DISTINCT NHTCV.parent_id, TCK.keyword_id,TPTCV.platform_id " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " /* Get ONLY Test case versions that has AT LEAST one Keyword assigned */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['testcase_keywords']} TCK " . + " ON TCK.testcase_id = NHTCV.parent_id " . + " WHERE TPTCV.testplan_id = " . $safe_id . " ) AS SQK " . + " GROUP BY $fields"; + } + + if ($my['opt']['groupByPlatform']) { + + $exec[$exec['key4total']] = (array) $this->db->fetchMapRowsIntoMap( + $sql, 'platform_id', 'keyword_id'); + } else { + $exec[$exec['key4total']] = (array) $this->db->fetchRowsIntoMap( + $sql, 'keyword_id'); + } + $exec['keywords'] = $keywordSet; + + return $exec; + } + + /** + */ + public function getStatusTotalsByKeywordForRender($id, $filters = null, + $opt = null) + { + return $this->getStatusTotalsByItemForRender($id, 'keyword', $filters, + $opt); + } + + /** + * + * @internal revisions + * + * @since 1.9.4 + * 20120429 - franciscom - + */ + public function getExecCountersByPlatformExecStatus($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe_id = intval($id); + list (, , , $union, $platformSet) = $this->helperBuildSQLExecCounters( + $id, $filters, $opt); + + $add2key = ''; + $addOnWhere = ''; + $addOnJoin = ''; + if (isset($opt['getOnlyActiveTCVersions'])) { + $add2key = 'Active'; + $addOnWhere = ' AND TCV.active = 1 '; + $addOnJoin = " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id "; + } + $sqlUnionAP = $union['exec' . $add2key]; + $sqlUnionBP = $union['not_run' . $add2key]; + + $sql = " /* {$debugMsg} UNION ALL CLAUSE => INCLUDE Duplicates */" . + " SELECT platform_id,status, count(0) AS exec_qty " . + " FROM ($sqlUnionAP UNION ALL $sqlUnionBP ) AS SQPL " . + " GROUP BY platform_id,status "; + + $exec['with_tester'] = (array) $this->db->fetchMapRowsIntoMap($sql, + 'platform_id', 'status'); + + $this->helperCompleteStatusDomain($exec, 'platform_id'); + + // get total test cases by Platform id ON TEST PLAN (With & WITHOUT tester assignment) + $sql = "/* $debugMsg */ " . " SELECT COUNT(0) AS qty, TPTCV.platform_id " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . $addOnJoin . + " WHERE TPTCV.testplan_id=" . $safe_id . $addOnWhere . + " GROUP BY platform_id"; + + $exec['total'] = (array) $this->db->fetchRowsIntoMap($sql, 'platform_id'); + $exec['platforms'] = $platformSet; + + return $exec; + } + + /** + * + * @internal revisions + * + */ + public function getStatusTotalsByPlatformForRender($id, $filters = null, + $opt = null) + { + return $this->getStatusTotalsByItemForRender($id, 'platform', $filters, + $opt); + } + + /** + * If no build set provided, ONLY ACTIVE BUILDS will be considered + */ + private function getExecCountersByPriorityExecStatus($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe_id = intval($id); + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($safe_id, + $filters, $opt); + + // Due to PLATFORMS we will have MULTIPLIER EFFECT + $fields = ""; + if ($my['opt']['groupByPlatform']) { + $fields = ",platform_id"; + } + + $sqlLEBP = $sqlStm['LEBP']; + + $sqlUnionA = "/* {$debugMsg} sqlUnionA - executions */" . + " SELECT (TPTCV.urgency * TCV.importance) AS urg_imp, " . + " TPTCV.tcversion_id, TPTCV.platform_id," . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . " /* Get importance */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . + " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . + " JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.testplan_id = " . $safe_id . + " /* Get execution statuses that CAN BE WRITTEN TO DB */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBP.id " . + + // Without this we get duplicates ?? => 20121121 CONFIRMED at least with NOT RUN WE GET DUPS + $builds->joinAdd . " /* FILTER BUILD Set on target test plan */ " . + " WHERE TPTCV.testplan_id=" . $safe_id . $builds->whereAddExec; + + $sqlUnionB = "/* {$debugMsg} sqlUnionB - NOT RUN */" . + " SELECT (TPTCV.urgency * TCV.importance) AS urg_imp, " . + " TPTCV.tcversion_id, TPTCV.platform_id," . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . " /* Get importance */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.testplan_id = " . intval($id) . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . $builds->joinAdd . + " /* FILTER BUILDS in set on target test plan */ " . + " WHERE TPTCV.testplan_id=" . $safe_id . $builds->whereAddNotRun . + " /* Get REALLY NOT RUN => BOTH LEBP.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEBP.id IS NULL"; + + // ATTENTION: + // Each piece of UNION has 3 fields: urg_imp,status, TPTCV.tcversion_id + // There is no way we can get more that ONE record with same TUPLE + // on sqlUionA or sqlUnionB ?. + // + // If we have PLATFORM we are going to get a MULTIPLIER EFFECT + // + $sql = " /* {$debugMsg} UNION WITHOUT ALL => DISCARD Duplicates */" . + " SELECT count(0) as exec_qty, urg_imp,status $fields " . + " FROM ($sqlUnionA UNION $sqlUnionB ) AS SU " . + " GROUP BY urg_imp,status $fields"; + + if ($my['opt']['groupByPlatform']) { + $kol = array( + 'platform_id', + 'urg_imp', + 'status' + ); + $rs = (array) $this->db->fetchRowsIntoMap3l($sql, $kol); + } else { + $rs = $this->db->get_recordset($sql); + } + + // Now we need to get priority LEVEL from (urgency * importance) + $out = array(); + $totals = array(); + $priorityCfg = config_get('urgencyImportance'); + if (! is_null($rs)) { + $loop2do = count($rs); + if ($my['opt']['groupByPlatform']) { + // loop2do => platform Qty + foreach ($rs as $platID => $elem) { + foreach ($elem as $urgImpVal => $statusUrgImp) { + + if ($urgImpVal >= $priorityCfg->threshold['high']) { + $hitOn = HIGH; + } elseif ($urgImpVal < $priorityCfg->threshold['low']) { + $hitOn = LOW; + } else { + $hitOn = MEDIUM; + } + + foreach ($statusUrgImp as $statusCode => $dummy) { + + $rz = &$rs[$platID][$urgImpVal][$statusCode]; + $rz['priority_level'] = $hitOn; + + // to improve readability + if (! isset($out[$hitOn][$statusCode])) { + $out[$platID][$hitOn][$statusCode] = $rz; + } else { + $out[$platID][$hitOn][$statusCode]['exec_qty'] += $rz['exec_qty']; + } + if (! isset($totals[$platID][$hitOn])) { + $totals[$platID][$hitOn] = array( + 'priority_level' => $hitOn, + 'qty' => 0 + ); + } + $totals[$platID][$hitOn]['qty'] += $rz['exec_qty']; + } + } + } + } else { + // The OLD WAY + if (! is_null($rs)) { + for ($jdx = 0; $jdx < $loop2do; $jdx ++) { + if ($rs[$jdx]['urg_imp'] >= + $priorityCfg->threshold['high']) { + $rs[$jdx]['priority_level'] = HIGH; + $hitOn = HIGH; + } elseif ($rs[$jdx]['urg_imp'] < + $priorityCfg->threshold['low']) { + $rs[$jdx]['priority_level'] = LOW; + $hitOn = LOW; + } else { + $rs[$jdx]['priority_level'] = MEDIUM; + $hitOn = MEDIUM; + } + + // to improve readability + $status = $rs[$jdx]['status']; + if (! isset($out[$hitOn][$status])) { + $out[$hitOn][$status] = $rs[$jdx]; + } else { + $out[$hitOn][$status]['exec_qty'] += $rs[$jdx]['exec_qty']; + } + + if (! isset($totals[$hitOn])) { + $totals[$hitOn] = array( + 'priority_level' => $hitOn, + 'qty' => 0 + ); + } + $totals[$hitOn]['qty'] += $rs[$jdx]['exec_qty']; + } + } + } + $exec['with_tester'] = $out; + $out = null; + } + if ($my['opt']['groupByPlatform']) { + $this->helperStatusDomainMatrix($exec, 'platform_id', + 'priority_level'); + } else { + $this->helperCompleteStatusDomain($exec, 'priority_level'); + } + + $exec['total'] = $totals; + + $levels = config_get('urgency'); + foreach ($levels['code_label'] as $lc => $lbl) { + $exec['priority_levels'][$lc] = lang_get($lbl); + } + + return $exec; + } + + /** + * + * @internal revisions + * + * @since 1.9.4 + * 20120429 - franciscom - + */ + public function getStatusTotalsByPriorityForRender($id, $filters = null, + $opt = null) + { + return $this->getStatusTotalsByItemForRender($id, 'priority_level', + $filters, $opt); + } + + /** + */ + public function getExecCountersByExecStatus($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list (, $builds, , $union,) = $this->helperBuildSQLExecCounters($id, + $filters, $opt); + + if (is_null($builds) || empty($builds)) { + return null; // >>---> Bye! + } + + // Latest Executions By Platform (LEBP) + $add2key = ''; + if (isset($opt['getOnlyActiveTCVersions'])) { + $add2key = 'Active'; + } + + $sqlUnionAP = $union['exec' . $add2key]; + $sqlUnionBP = $union['not_run' . $add2key]; + + $sql = " /* {$debugMsg} UNION ALL CLAUSE => INCLUDE Duplicates */" . + " SELECT status, count(0) AS exec_qty " . + " FROM ($sqlUnionAP UNION ALL $sqlUnionBP ) AS SQPL " . + " GROUP BY status "; + + $dummy = (array) $this->db->fetchRowsIntoMap($sql, 'status'); + + $statusCounters = array( + 'total' => 0 + ); + $codeVerbose = array_flip($this->map_tc_status); + foreach ($dummy as $code => $elem) { + + $statusCounters['total'] += $elem['exec_qty']; + $statusCounters[$codeVerbose[$code]] = $elem['exec_qty']; + } + + return $statusCounters; + } + + /** + * + * @internal revisions + * + * @since 1.9.6 + * 20130107 - franciscom - TICKET 5457: Incorrect data in "Report by tester per build" + */ + private function getExecCountersByBuildUAExecStatus($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe_id = intval($id); + list (, $builds, $sqlStm) = $this->helperGetExecCounters($safe_id, + $filters, $opt); + + // Last Executions By Build and Platform (LEBBP) + // Please remember that Platforms (when exists) has Multiplier effect on test cases + // + $sqlLEBBP = $sqlStm['LEBBP']; + + // 20120817 - franciscom - + // I'm not to happy with DISTINCT I've added to do this work. + // Do not understand why i get multiple identical records + // + // 20130107 - Think I've got the issue: + // was the missing clause + // " AND UA.feature_id = TPTCV.id AND UA.build_id = LEBBP.build_id " . + // + // 20140128 - do not understand why I've added DISTINT. + // without it I can add also get exec times + $sqlUnionBU = "/* {$debugMsg} */" . + " SELECT /* DISTINCT */ UA.user_id, UA.build_id, TPTCV.tcversion_id, TPTCV.platform_id, " . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status, " . + " COALESCE(E.execution_duration,0) AS execution_duration " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . + " AND UA.build_id IN ({$builds->inClause}) AND UA.type = {$this->execTaskCode} " . + " /* LEFT OUTER in order to get NOT RUN */ " . + " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.testplan_id = " . $safe_id . + " /* Without this piece we are including results for features without tester */ " . + " AND UA.feature_id = TPTCV.id AND UA.build_id = LEBBP.build_id " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.build_id = UA.build_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . " AND E.id = LEBBP.id " . // TICKET 5192 + " WHERE TPTCV.testplan_id=" . $safe_id . + " AND UA.build_id IN ({$builds->inClause}) "; + + $sql = " /* {$debugMsg} */" . + " SELECT user_id, build_id,status, count(0) AS exec_qty, SUM(execution_duration) AS total_time" . + " FROM ($sqlUnionBU) AS SQBU " . " GROUP BY user_id,build_id,status "; + + $keyColumns = array( + 'build_id', + 'user_id', + 'status' + ); + $exec['with_tester'] = (array) $this->db->fetchRowsIntoMap3l($sql, + $keyColumns); + + $totals = array(); + foreach ($exec as &$topLevelElem) { + $topLevelItemSet = array_keys($topLevelElem); + foreach ($topLevelItemSet as $topLevelItemID) { + $itemSet = array_keys($topLevelElem[$topLevelItemID]); + foreach ($itemSet as $itemID) { + $elem = &$topLevelElem[$topLevelItemID]; + foreach ($this->statusCode as $code) { + if (! isset($elem[$itemID][$code])) { + $elem[$itemID][$code] = array( + 'build_id' => $topLevelItemID, + 'user_id' => $itemID, + 'status' => $code, + 'exec_qty' => 0, + 'total_time' => 0 + ); + } + + if (! isset($totals[$topLevelItemID][$itemID])) { + $totals[$topLevelItemID][$itemID] = array( + 'build_id' => $topLevelItemID, + 'user_id' => $itemID, + 'qty' => 0, + 'total_time' => 0 + ); + } + $totals[$topLevelItemID][$itemID]['qty'] += $elem[$itemID][$code]['exec_qty']; + $totals[$topLevelItemID][$itemID]['total_time'] += $elem[$itemID][$code]['total_time']; + } + } + } + } + $exec['total'] = $totals; + + return $exec; + } + + /** + * + * @see resultsByTesterPerBuild.php + * @internal revisions + * + * @since 1.9.6 + */ + public function getStatusTotalsByBuildUAForRender($id, $opt = null) + { + $my = array( + 'opt' => array( + 'processClosedBuilds' => true + ) + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $renderObj = null; + $code_verbose = $this->getStatusForReports(); + $metrics = $this->getExecCountersByBuildUAExecStatus($id, null, + $my['opt']); + + if (! is_null($metrics)) { + $renderObj = new stdClass(); + $topItemSet = array_keys($metrics['with_tester']); + $renderObj->info = array(); + $out = &$renderObj->info; + + $topElem = &$metrics['with_tester']; + foreach ($topItemSet as $topItemID) { + $itemSet = array_keys($topElem[$topItemID]); + foreach ($itemSet as $itemID) { + $elem = &$topElem[$topItemID][$itemID]; + + $out[$topItemID][$itemID]['total'] = $metrics['total'][$topItemID][$itemID]['qty']; + $progress = 0; + foreach ($code_verbose as $statusCode => $statusVerbose) { + $out[$topItemID][$itemID][$statusVerbose]['count'] = $elem[$statusCode]['exec_qty']; + $pc = ($elem[$statusCode]['exec_qty'] / + $out[$topItemID][$itemID]['total']) * 100; + $out[$topItemID][$itemID][$statusVerbose]['percentage'] = number_format( + $pc, 1); + + if ($statusVerbose != 'not_run') { + $progress += $elem[$statusCode]['exec_qty']; + } + } + $progress = ($progress / $out[$topItemID][$itemID]['total']) * + 100; + $out[$topItemID][$itemID]['progress'] = number_format( + $progress, 1); + $out[$topItemID][$itemID]['total_time'] = number_format( + $metrics['total'][$topItemID][$itemID]['total_time'], 2, + '.', ''); + } + } + } + return $renderObj; + } + + /** + * + * @used-by getStatusTotalsByKeywordForRender() + * + */ + private function getStatusTotalsByItemForRender($id, $itemType, + $filters = null, $opt = null) + { + $renderObj = null; + $code_verbose = $this->getStatusForReports(); + $labels = $this->resultsCfg['status_label']; + + $returnArray = false; + $byPlatform = false; + + switch ($itemType) { + case 'keyword': + $metrics = $this->getExecCountersByKeywordExecStatus($id, + $filters, $opt); + $setKey = 'keywords'; + $byPlatform = isset($opt['groupByPlatform']) && + $opt['groupByPlatform']; + break; + + case 'platform': + $myOpt = array_merge(array( + 'getPlatformSet' => true + ), (array) $opt); + $metrics = $this->getExecCountersByPlatformExecStatus($id, + $filters, $myOpt); + $setKey = 'platforms'; + break; + + case 'priority_level': + $metrics = $this->getExecCountersByPriorityExecStatus($id, + $filters, $opt); + $setKey = 'priority_levels'; + $byPlatform = isset($opt['groupByPlatform']) && + $opt['groupByPlatform']; + break; + + case 'tsuite': + $metrics = $this->getExecCountersByTestSuiteExecStatus($id, + $filters, $opt); + $setKey = 'tsuites'; + $returnArray = true; + $byPlatform = isset($opt['groupByPlatform']) && + $opt['groupByPlatform']; + break; + } + + if (! is_null($metrics) && ! is_null($metrics[$setKey]) > 0) { + $renderObj = new stdClass(); + $renderObj->info = array(); + + if (! $byPlatform) { + $itemList = array_keys($metrics[$setKey]); + foreach ($itemList as $itemID) { + if (isset($metrics['with_tester'][$itemID])) { + $totalRun = 0; + $renderObj->info[$itemID]['type'] = $itemType; + $renderObj->info[$itemID]['name'] = $metrics[$setKey][$itemID]; + $renderObj->info[$itemID]['total_tc'] = $metrics['total'][$itemID]['qty']; + $renderObj->info[$itemID]['details'] = array(); + + $rf = &$renderObj->info[$itemID]['details']; + $doPerc = ($renderObj->info[$itemID]['total_tc'] > 0); + foreach ($code_verbose as $statusCode => $statusVerbose) { + $rf[$statusVerbose] = array( + 'qty' => 0, + 'percentage' => 0 + ); + $rf[$statusVerbose]['qty'] = $metrics['with_tester'][$itemID][$statusCode]['exec_qty']; + + if ($doPerc) { + $rf[$statusVerbose]['percentage'] = number_format( + 100 * + ($rf[$statusVerbose]['qty'] / + $renderObj->info[$itemID]['total_tc']), 1); + } + $totalRun += ($statusVerbose == 'not_run' ? 0 : $rf[$statusVerbose]['qty']); + } + if ($doPerc) { + $renderObj->info[$itemID]['percentage_completed'] = number_format( + 100 * + ($totalRun / + $renderObj->info[$itemID]['total_tc']), 1); + } + } + } + } else { + // mainKey is Platform ID + $platList = array_keys($metrics['with_tester']); + $mex = &$metrics['with_tester']; + foreach ($platList as $platID) { + $itemList = array_keys($mex[$platID]); + foreach ($itemList as $itemID) { + if (isset($mex[$platID])) { + $totalRun = 0; + $renderObj->info[$platID][$itemID]['type'] = $itemType; + $renderObj->info[$platID][$itemID]['name'] = $metrics[$setKey][$itemID]; + $renderObj->info[$platID][$itemID]['total_tc'] = $metrics['total'][$platID][$itemID]['qty']; + $renderObj->info[$platID][$itemID]['details'] = array(); + + $rf = &$renderObj->info[$platID][$itemID]['details']; + $doPerc = ($renderObj->info[$platID][$itemID]['total_tc'] > + 0); + foreach ($code_verbose as $statusCode => $statusVerbose) { + $rf[$statusVerbose] = array( + 'qty' => 0, + 'percentage' => 0 + ); + $rf[$statusVerbose]['qty'] = $mex[$platID][$itemID][$statusCode]['exec_qty']; + + if ($doPerc) { + $rf[$statusVerbose]['percentage'] = number_format( + 100 * + ($rf[$statusVerbose]['qty'] / + $renderObj->info[$platID][$itemID]['total_tc']), + 1); + } + $totalRun += ($statusVerbose == 'not_run' ? 0 : $rf[$statusVerbose]['qty']); + } + if ($doPerc) { + $renderObj->info[$platID][$itemID]['percentage_completed'] = number_format( + 100 * + ($totalRun / + $renderObj->info[$platID][$itemID]['total_tc']), + 1); + } + } + } + } + } + + // Verbosity! + foreach ($code_verbose as $status_verbose) { + $l10n = isset($labels[$status_verbose]) ? lang_get( + $labels[$status_verbose]) : lang_get($status_verbose); + + $renderObj->colDefinition[$status_verbose]['qty'] = $l10n; + $renderObj->colDefinition[$status_verbose]['percentage'] = '[%]'; + } + } + + // How to return things + if ($returnArray) { + return array( + $renderObj, + $metrics['staircase'] + ); + } else { + unset($metrics); + return $renderObj; + } + } + + /** + * + * @internal revisions + * + * @since 1.9.4 + * 20120429 - franciscom - + */ + private function getStatusTotalsByTestSuiteForRender($id, $filters = null, + $opt = null) + { + list ($renderObj, $staircase) = $this->getStatusTotalsByItemForRender( + $id, 'tsuite', $filters, $opt); + unset($staircase); + return $renderObj; + } + + /** + * + * @internal revisions + * + * @since 1.9.4 + */ + public function getStatusTotalsByTopLevelTestSuiteForRender($id, + $filters = null, $opt = null) + { + list ($rx, $staircase) = $this->getStatusTotalsByItemForRender($id, + 'tsuite', $filters, $opt); + + // ??? $key2loop = array_keys($rx->info); + $template = array( + 'type' => 'tsuite', + 'name' => '', + 'total_tc' => 0, + 'percentage_completed' => 0, + 'details' => array() + ); + + foreach ($this->statusCode as $verbose => $code) { + $template['details'][$verbose] = array( + 'qty' => 0, + 'percentage' => 0 + ); + } + + $renderObj = new stdClass(); + $renderObj->colDefinition = $rx->colDefinition; + + // collect qty + $topNameCache = null; + $execQty = null; + + $key2loop = array_keys($staircase); + $wp = isset($opt['groupByPlatform']) && $opt['groupByPlatform']; + + if ($wp) { + $plat2loop = array_keys($rx->info); + foreach ($key2loop as $tsuite_id) { + // (count() == 1) => is a TOP LEVEL SUITE, + // only element contains Root node, is useless for this algorithm + // + + if (count($staircase[$tsuite_id]) > 1) { + // element at position 1 is a TOP LEVEL SUITE + // $topSuiteID = &$staircase[$tsuite_id][1]; + $topSuiteID = $staircase[$tsuite_id][1]; + $initName = false; + } else { + $topSuiteID = $tsuite_id; + $initName = true; + } + + // Over Platform + foreach ($plat2loop as $platId) { + if (! isset($renderObj->info[$platId][$topSuiteID])) { + $renderObj->info[$platId][$topSuiteID] = $template; + $execQty[$platId][$topSuiteID] = 0; + $initName = true; + } + + if ($initName) { + $dummy = $this->tree_manager->get_node_hierarchy_info( + $topSuiteID); + $renderObj->info[$platId][$topSuiteID]['name'] = $dummy['name']; + unset($dummy); + } + + // Loop to get executions counters + $r2d2 = &$rx->info[$platId][$tsuite_id]; + if (null !== $r2d2) { + foreach ($r2d2['details'] as $code => &$elem) { + $renderObj->info[$platId][$topSuiteID]['details'][$code]['qty'] += $elem['qty']; + $renderObj->info[$platId][$topSuiteID]['total_tc'] += $elem['qty']; + + if ($code != 'not_run') { + $execQty[$platId][$topSuiteID] += $elem['qty']; + } + } + } + } + } + + // Last step: get percentages + foreach ($renderObj->info as $platID => &$tsuiteMetrics) { + foreach ($tsuiteMetrics as $tsuite_id => &$elem) { + if ($execQty[$platID][$tsuite_id] > 0) { + $elem['percentage_completed'] = number_format( + 100 * + ($execQty[$platID][$tsuite_id] / $elem['total_tc']), + 1); + } + if ($elem['total_tc'] > 0) { + foreach ($elem['details'] as &$yumyum) { + $yumyum['percentage'] = number_format( + 100 * ($yumyum['qty'] / $elem['total_tc']), 1); + } + } + } + } + } else { + + // OLD WAY + foreach ($key2loop as $tsuite_id) { + // (count() == 1) => is a TOP LEVEL SUITE, + // only element contains Root node, is useless for this algorithm + // + + if (count($staircase[$tsuite_id]) > 1) { + // element at position 1 is a TOP LEVEL SUITE + $topSuiteID = &$staircase[$tsuite_id][1]; + $initName = false; + } else { + $topSuiteID = $tsuite_id; + $initName = true; + } + + if (! isset($renderObj->info[$topSuiteID])) { + $renderObj->info[$topSuiteID] = $template; + $execQty[$topSuiteID] = 0; + $initName = true; + } + + if ($initName) { + $dummy = $this->tree_manager->get_node_hierarchy_info( + $topSuiteID); + $renderObj->info[$topSuiteID]['name'] = $dummy['name']; + unset($dummy); + } + + // Loop to get executions counters + if (isset($rx->info[$tsuite_id]['details'])) { + foreach ($rx->info[$tsuite_id]['details'] as $code => &$elem) { + $renderObj->info[$topSuiteID]['details'][$code]['qty'] += $elem['qty']; + $renderObj->info[$topSuiteID]['total_tc'] += $elem['qty']; + + if ($code != 'not_run') { + $execQty[$topSuiteID] += $elem['qty']; + } + } + } + } + + // Last step: get percentages + foreach ($renderObj->info as $tsuite_id => &$elem) { + if ($execQty[$tsuite_id] > 0) { + $elem['percentage_completed'] = number_format( + 100 * ($execQty[$tsuite_id] / $elem['total_tc']), 1); + } + + if ($elem['total_tc'] > 0) { + foreach ($elem['details'] as &$yumyum) { + $yumyum['percentage'] = number_format( + 100 * ($yumyum['qty'] / $elem['total_tc']), 1); + } + } + } + } + + unset($topNameCache); + unset($rx); + unset($staircase); + unset($template); + unset($key2loop); + unset($execQty); + + return $renderObj; + } + + /** + */ + private function getExecCountersByTestSuiteExecStatus($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safe_id = intval($id); + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($id, + $filters, $opt); + + $fields = ""; + if ($my['opt']['groupByPlatform']) { + $fields = ",platform_id"; + } + + // Latest Execution Ignoring Build + $sqlLEBP = $sqlStm['LEBP']; + + $sqlUnionAT = "/* {$debugMsg} sqlUnionAT - executions */" . + " SELECT NHTC.parent_id AS tsuite_id,TPTCV.platform_id," . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . + " JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.testplan_id = " . $safe_id . + " /* Get execution status WRITTEN on DB */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBP.id " . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . $builds->whereAddExec; + + $sqlUnionBT = "/* {$debugMsg} sqlUnionBK - NOT RUN */" . + " SELECT NHTC.parent_id AS tsuite_id,TPTCV.platform_id," . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* Get REALLY NOT RUN => BOTH LEBP.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.testplan_id = " . $safe_id . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . $builds->joinAdd . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " /* FILTER BUILDS in set on target test plan (not alway can be applied) */ " . + " WHERE TPTCV.testplan_id=" . $safe_id . $builds->whereAddNotRun . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEBP.id IS NULL"; + + $sql = " /* {$debugMsg} UNION ALL DO NOT DISCARD Duplicates */" . + " SELECT count(0) AS exec_qty, tsuite_id, status $fields" . + " FROM ($sqlUnionAT UNION ALL $sqlUnionBT ) AS SQT " . + " GROUP BY tsuite_id ,status $fields"; + + if ($my['opt']['groupByPlatform']) { + $kol = array( + 'platform_id', + 'tsuite_id', + 'status' + ); + $exec['with_tester'] = (array) $this->db->fetchRowsIntoMap3l($sql, + $kol); + $this->helperStatusDomainMatrix($exec, 'platform_id', 'tsuite_id'); + } else { + $exec['with_tester'] = (array) $this->db->fetchMapRowsIntoMap($sql, + 'tsuite_id', 'status'); + + // now we need to complete status domain + $this->helperCompleteStatusDomain($exec, 'tsuite_id'); + } + + // Build item set + $exec['tsuites_full'] = $this->get_testsuites($safe_id); + $loop2do = count($exec['tsuites_full']); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $keySet[] = $exec['tsuites_full'][$idx]['id']; + } + $dx = $this->tree_manager->get_full_path_verbose($keySet, + array( + 'output_format' => 'stairway2heaven' + )); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $exec['tsuites'][$exec['tsuites_full'][$idx]['id']] = $dx['flat'][$exec['tsuites_full'][$idx]['id']]; + } + $exec['staircase'] = $dx['staircase']; + + unset($dx); + unset($keySet); + return $exec; + } + + /** + */ + public function getExecStatusMatrix($id, $filters = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array(); + $my['opt'] = array( + 'getExecutionNotes' => false, + 'getTester' => false, + 'getUserAssignment' => false, + 'output' => null, + 'getExecutionTimestamp' => false, + 'getExecutionDuration' => false + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + list ($my, $builds, $sqlStm, $union) = $this->helperBuildSQLTestSuiteExecCounters( + $id, $filters, $my['opt']); + + $sql = " /* {$debugMsg} UNION WITH ALL CLAUSE */ " . + " {$union['exec']} UNION ALL {$union['not_run']} "; + + $keyColumns = array( + 'tsuite_id', + 'tcase_id', + 'platform_id', + 'build_id' + ); + $cumulative = ($my['opt']['output'] == 'cumulative'); + $dummy = (array) $this->db->fetchRowsIntoMap4l($sql, $keyColumns, + $cumulative); + + unset($sqlStm); + unset($union); + unset($my); + unset($builds); + + // now is time do some decoding + // Key is a tuple (PARENT tsuite_id, test case id, platform id) + // + $item2loop = array_keys($dummy); + $stairway2heaven = null; + $pathway = null; + $latestExec = null; + $priorityCfg = config_get('urgencyImportance'); + + foreach ($item2loop as $item_id) { + $stairway2heaven = $this->tree_manager->get_path($item_id, null, + 'name'); + $pathway[$item_id] = implode("/", $stairway2heaven); + unset($stairway2heaven); + + // go inside test case + $tcase2loop = array_keys($dummy[$item_id]); + foreach ($tcase2loop as $tcase_id) { + $platform2loop = array_keys($dummy[$item_id][$tcase_id]); + foreach ($platform2loop as $platform_id) { + $latestExec[$platform_id][$tcase_id] = array( + 'id' => - 1, + 'status' => $this->notRunStatusCode + ); + $rf = &$dummy[$item_id][$tcase_id][$platform_id]; + foreach ($rf as &$exec) { + $exec['suiteName'] = $pathway[$item_id]; + if ($exec['executions_id'] > + $latestExec[$platform_id][$tcase_id]['id']) { + $latestExec[$platform_id][$tcase_id]['id'] = $exec['executions_id']; + $latestExec[$platform_id][$tcase_id]['status'] = $exec['status']; + $latestExec[$platform_id][$tcase_id]['build_id'] = $exec['build_id']; + $latestExec[$platform_id][$tcase_id]['execution_notes'] = $exec['execution_notes']; + } + + // --------------------------------------------------- + // Now we need to get priority LEVEL from + // (urgency * impact) + // we do not use a function to improve performance + if ($exec['urg_imp'] >= $priorityCfg->threshold['high']) { + $exec['priority_level'] = HIGH; + } elseif ($exec['urg_imp'] < + $priorityCfg->threshold['low']) { + $exec['priority_level'] = LOW; + } else { + $exec['priority_level'] = MEDIUM; + } + } + } + } + + unset($tcase2loop); + unset($platform2loop); + } + + unset($pathway); + return array( + 'metrics' => $dummy, + 'latestExec' => $latestExec + ); + } + + /** + * + * @used-by + * getExecutionsByStatus() + * getNotRunWithTesterAssigned() + * getNotRunWOTesterAssigned() + * getExecCountersByBuildExecStatus() + * getExecCountersByKeywordExecStatus() + * getExecCountersByPriorityExecStatus() + * getExecCountersByBuildUAExecStatus() + * getExecCountersByTestSuiteExecStatus() + * + * + * + */ + private function helperGetExecCounters($id, $filters, $opt) + { + $sql = array(); + $my = array(); + $my['opt'] = array( + 'getOnlyAssigned' => false, + 'tprojectID' => 0, + 'getUserAssignment' => false, + 'getPlatformSet' => false, + 'processClosedBuilds' => true, + 'groupByPlatform' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $my['filters'] = array( + 'buildSet' => null + ); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + // Build Info + $bi = new stdClass(); + $bi->idSet = $my['filters']['buildSet']; + $bi->inClause = ''; + $bi->infoSet = null; + + if (is_null($bi->idSet)) { + $openStatus = $my['opt']['processClosedBuilds'] ? null : 1; + + $bi->infoSet = $this->get_builds($id, testplan::ACTIVE_BUILDS, + $openStatus); + if (! is_null($bi->infoSet)) { + $bi->idSet = array_keys($bi->infoSet); + } + } + + // ========================================================= + // Emergency Exit !!! + if (is_null($bi->idSet)) { + throw new Exception( + __METHOD__ . " - Can not work with empty build set"); + } + // ========================================================= + + // Things seems to be OK + $bi->inClause = implode(",", $bi->idSet); + if ($my['opt']['getOnlyAssigned']) { + $sql['getAssignedFeatures'] = " /* Get feature id with Tester Assignment */ " . + " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . + " AND UA.build_id IN ({$bi->inClause}) " . + " AND UA.type = {$this->execTaskCode} "; + $bi->source = "UA"; + $bi->joinAdd = " AND E.build_id = UA.build_id "; + $bi->whereAddExec = " AND {$bi->source}.build_id IN ({$bi->inClause}) "; + $bi->whereAddNotRun = $bi->whereAddExec; + } else { + $sql['getAssignedFeatures'] = ''; + $bi->source = "E"; + + // TICKET 5353 + // $bi->joinAdd = ""; + $bi->joinAdd = " AND E.build_id IN ({$bi->inClause}) "; + + // Why ? + // If I'm consider test cases WITH and WITHOUT Tester assignment, + // I will have no place to go to filter for builds. + // Well at least when trying to get EXECUTED test case, I will be able + // to apply filter on Executions table. + // Why then I choose to have this blank ANYWAY ? + // Because I will get filtering on Build set through + // the Latest Execution queries (see below sql['LE'], sql['LEBP']. + // + // Anyway we need to backup all these thoughts with a long, long test run + // on test link itself. + $bi->whereAddExec = " AND {$bi->source}.build_id IN ({$bi->inClause}) "; + $bi->whereAddNotRun = ""; + } + + $sql['getUserAssignment']['not_run'] = ""; + $sql['getUserAssignment']['exec'] = ""; + + if ($my['opt']['getUserAssignment']) { + $sql['getUserAssignment']['not_run'] = " LEFT JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . " AND UA.build_id = BU.id " . + " AND UA.type = {$this->execTaskCode} "; + + $sql['getUserAssignment']['exec'] = " LEFT JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . + " AND UA.build_id = E.build_id " . + " AND UA.type = {$this->execTaskCode} "; + } + + // Latest Execution IGNORING Build and Platform + $sql['LE'] = " SELECT EE.tcversion_id,EE.testplan_id,MAX(EE.id) AS id " . + " FROM {$this->tables['executions']} EE " . " WHERE EE.testplan_id=" . + intval($id) . " AND EE.build_id IN ({$bi->inClause}) " . + " GROUP BY EE.tcversion_id,EE.testplan_id "; + + // Latest Execution By Platform (ignore build) + $sql['LEBP'] = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,MAX(EE.id) AS id " . + " FROM {$this->tables['executions']} EE " . " WHERE EE.testplan_id=" . + intval($id) . " AND EE.build_id IN ({$bi->inClause}) " . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id "; + + // Last Executions By Build (LEBB) (ignore platform) + $sql['LEBB'] = " SELECT EE.tcversion_id,EE.testplan_id,EE.build_id,MAX(EE.id) AS id " . + " FROM {$this->tables['executions']} EE " . " WHERE EE.testplan_id=" . + intval($id) . " AND EE.build_id IN ({$bi->inClause}) " . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.build_id "; + + // Last Executions By Build and Platform (LEBBP) + $sql['LEBBP'] = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " WHERE EE.testplan_id=" . intval($id) . + " AND EE.build_id IN ({$bi->inClause}) " . + " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; + + return array( + $my, + $bi, + $sql + ); + } + + /** + */ + private function helperCompleteStatusDomain(&$out, $key) + { + $totalByItemID = array(); + + // refence is critic + foreach ($out as &$elem) { + $itemSet = array_keys($elem); + foreach ($itemSet as $itemID) { + $totalByItemID[$itemID]['qty'] = 0; + foreach ($this->statusCode as $code) { + if (! isset($elem[$itemID][$code])) { + $elem[$itemID][$code] = array( + $key => $itemID, + 'status' => $code, + 'exec_qty' => 0 + ); + } + $totalByItemID[$itemID]['qty'] += $elem[$itemID][$code]['exec_qty']; + } + } + } + $out['total'] = $totalByItemID; + } + + /** + */ + private function helperBuildSQLExecCounters($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + try { + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($id, + $filters, $opt); + } catch (Exception $e) { + return null; + } + + $safe_id = intval($id); + $platformSet = null; + if ($my['opt']['getPlatformSet']) { + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'outputDetails' => 'name', + 'addIfNull' => true + ); + $platformSet = $this->getPlatforms($safe_id, $getOpt); + } + + // Latest Executions By Platform (LEBP) + $sqlLEBP = $sqlStm['LEBP']; + + // 20121121 - franciscom + // Need to understand if this sentence is right: + // + // GO FOR Absolute LATEST exec ID IGNORE BUILD + // Is this right for each use of this method ? + // + $dummy['exec'] = "/* {$debugMsg} sqlUnion - executions */" . + " SELECT TPTCV.tcversion_id,TPTCV.platform_id, " . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* GO FOR Absolute LATEST exec ID IGNORE BUILD */ " . + " JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.testplan_id = " . $safe_id . + " /* Get execution status WRITTEN on DB */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBP.id "; + + $union['exec'] = $dummy['exec'] . " WHERE TPTCV.testplan_id=" . $safe_id . + $builds->whereAddExec; + + $union['execActive'] = $dummy['exec'] . + " /* Used to filter ON ACTIVE TCVersion */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . $builds->whereAddExec . " AND TCV.active = 1 "; + + // 20121121 - An issue was reported in this scenario: + // Test Plan with Platforms (ONE) + // Two Build: + // B1 with TC1 passed, TC2 failed, TC3 not run - BUT B1 INACTIVE + // B3 ALL TEST CASES NOT RUN + // + // we got WRONG figures if build set is NOT USING when trying to access Executions TABLE + // + $dummy['not_run'] = "/* {$debugMsg} sqlUnion - NOT RUN */" . + " SELECT TPTCV.tcversion_id,TPTCV.platform_id, " . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEBP}) AS LEBP " . + " ON LEBP.testplan_id = TPTCV.testplan_id " . + " AND LEBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBP.platform_id = TPTCV.platform_id " . + " AND LEBP.testplan_id = " . $safe_id . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . $builds->joinAdd; + + $union['not_run'] = $dummy['not_run'] . + " /* FILTER BUILDS in set on target test plan (not always can be applied) */ " . + " WHERE TPTCV.testplan_id=" . $safe_id . $builds->whereAddNotRun . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEBP.id IS NULL"; + + $union['not_runActive'] = $dummy['not_run'] . + " /* Used to filter ON ACTIVE TCVersion */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . $builds->whereAddNotRun . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEBP.id IS NULL" . " AND TCV.active = 1 "; + + return array( + $my, + $builds, + $sqlStm, + $union, + $platformSet + ); + } + + /** + * + * @internal revision + * @since 1.9.8 + * 20130713 - franciscom - + * when getting info for executed test cases, RIGHT version number for execution + * is on EXECUTIONS TABLE not on testplan_tcversions TABLE. + * + * REMEMBER that when we update TCVERSION for executed Test Cases, we HAVE TO UPDATE + * testplan_tcversions table. + * + * We also need to use E.tcversion_id and NOT TPTCV.tcversion_id. + * + */ + private function helperBuildSQLTestSuiteExecCounters($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['opt'] = array( + 'getExecutionNotes' => false, + 'getTester' => false, + 'getUserAssignment' => false, + 'getExecutionTimestamp' => false, + 'getExecutionDuration' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($id, + $filters, $opt); + + $safe_id = intval($id); + + // Additional Execution fields + $moreExecFields = ""; + if ($my['opt']['getExecutionNotes']) { + $moreExecFields .= "COALESCE(E.notes,'') AS execution_notes,"; + } + + if ($my['opt']['getTester']) { + $moreExecFields .= "E.tester_id,"; + } + + if ($my['opt']['getExecutionTimestamp']) { + $moreExecFields .= "E.execution_ts,"; + } + + if ($my['opt']['getExecutionDuration']) { + $moreExecFields .= "E.execution_duration,"; + } + + if ($my['opt']['getUserAssignment']) { + $moreExecFields .= "UA.user_id,"; + } + + // Latest Executions By Build Platform (LEBBP) + $sqlLEBBP = $sqlStm['LEBBP']; + + $union['exec'] = "/* {$debugMsg} sqlUnion Test suites - executions */" . + " SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . + " TPTCV.tcversion_id,TPTCV.platform_id," . + " E.build_id,E.tcversion_number AS version,TCV.tc_external_id AS external_id, " . + " E.id AS executions_id, E.status AS status, " . $moreExecFields . + " E.execution_type AS exec_type," . + " (TPTCV.urgency * TCV.importance) AS urg_imp " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* GO FOR Absolute LATEST exec ID On BUILD,PLATFORM */ " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.testplan_id = " . $safe_id . + " /* Get execution status WRITTEN on DB */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.build_id = LEBBP.build_id " . + $sqlStm['getUserAssignment']['exec'] . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " /* Get Test Case Version attributes */ " . + " JOIN {$this->tables['tcversions']} TCV " . + // " ON TCV.id = TPTCV.tcversion_id " . + " ON TCV.id = E.tcversion_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . $builds->whereAddExec; + + $union['not_run'] = "/* {$debugMsg} sqlUnion Test suites - not run */" . + " SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . + " TPTCV.tcversion_id, TPTCV.platform_id," . + " BU.id AS build_id,TCV.version,TCV.tc_external_id AS external_id, " . + " COALESCE(E.id,-1) AS executions_id, " . + " COALESCE(E.status,'{$this->notRunStatusCode}') AS status, " . + $moreExecFields . " TCV.execution_type AS exec_type," . + " (TPTCV.urgency * TCV.importance) AS urg_imp " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* Needed to be able to put a value on build_id on output set */ " . + " JOIN {$this->tables['builds']} BU " . + " ON BU.id IN ({$builds->inClause}) " . + $sqlStm['getUserAssignment']['not_run'] . + " /* GO FOR Absolute LATEST exec ID On BUILD,PLATFORM */ " . + " LEFT OUTER JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.build_id = BU.id " . " AND LEBBP.testplan_id = " . + $safe_id . " /* Get execution status WRITTEN on DB */ " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.build_id = LEBBP.build_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " /* Get Test Case Version attributes */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . " AND BU.id IN ({$builds->inClause}) " . + $builds->whereAddNotRun . + " /* Get REALLY NOT RUN => BOTH LEBBP.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEBBP.id IS NULL"; + + return array( + $my, + $builds, + $sqlStm, + $union + ); + } + + /** + * get executions (Not Run is not included) + * + * @param int $id + * test plan id + * @param string $status + * status code (one char) + * @param mixed $filters + * keys: 'buildSet' + * + * @param + * mixed opt + * keys: 'output' elem domain 'map','array' + * + */ + public function getExecutionsByStatus($id, $status, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($id, + $filters, $opt); + + // particular options + $options = array( + 'output' => 'map', + 'add2fields' => '' + ); + $my['opt'] = array_merge($options, $my['opt']); + $safe_id = intval($id); + + $fullEID = $this->helperConcatTCasePrefix($safe_id); + + $addFields = ''; + if ('' != $my['opt']['add2fields']) { + $addFields = ',' . $my['opt']['add2fields']; + } + + $sqlLEBBP = $sqlStm['LEBBP']; + $sql = "/* {$debugMsg} executions with status WRITTEN on DB => not run is not present */" . + " SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . + " TPTCV.tcversion_id,TPTCV.platform_id," . + " E.tcversion_number, E.build_id,E.id AS executions_id, E.status AS status, " . + " E.notes AS execution_notes, E.tester_id,E.execution_ts," . + " TCV.version,TCV.tc_external_id AS external_id, " . + " $fullEID AS full_external_id," . + " (TPTCV.urgency * TCV.importance) AS urg_imp " . $addFields . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " /* GO FOR Absolute LATEST exec ID On BUILD,PLATFORM */ " . + " JOIN ({$sqlLEBBP}) AS LEBBP " . + " ON LEBBP.testplan_id = TPTCV.testplan_id " . + " AND LEBBP.platform_id = TPTCV.platform_id " . + " AND LEBBP.tcversion_id = TPTCV.tcversion_id " . + " AND LEBBP.testplan_id = " . $safe_id . + $sqlStm['getAssignedFeatures'] . + " /* Get execution status WRITTEN on DB */ " . + " JOIN {$this->tables['executions']} E " . " ON E.id = LEBBP.id " . + " AND E.build_id = LEBBP.build_id " . $builds->joinAdd . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " /* Get Test Case Version attributes */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . " WHERE TPTCV.testplan_id=" . + $safe_id . " AND E.status='{$status}' " . $builds->whereAddExec; + + switch ($my['opt']['output']) { + case 'array': + $dummy = (array) $this->db->get_recordset($sql); + break; + + case 'mapByExecID': + $dummy = (array) $this->db->fetchRowsIntoMap($sql, + 'executions_id'); + break; + + case 'map': + default: + $keyColumns = array( + 'tsuite_id', + 'tcase_id', + 'platform_id', + 'build_id' + ); + $dummy = (array) $this->db->fetchRowsIntoMap4l($sql, $keyColumns); + break; + } + + return $dummy; + } + + /** + * get just Not Run test case on test plan, but ONLY THESE + * that has tester assigned. + * This is critic: + * + * example: + * test plan with 11 test cases linked. + * two Builds B1, B2 + * 1. Assign tester to all test cases on BUILD B1 + * 2. run getNotRunWithTesterAssigned() + * you will get 11 records all for B1 + * + * 3. Assign tester to 4 test cases on BUILD B2 + * 4. run getNotRunWithTesterAssigned() + * you will get: 15 records + * 11 records for B1 + * 4 records for B2 + * + * @param int $id + * test plan id + * @param string $status + * status code (one char) + * @param mixed $filters + * keys: 'buildSet' + * + * @param + * mixed opt + * keys: 'output' elem domain 'map','array' + * + */ + public function getNotRunWithTesterAssigned($id, $filters = null, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($my, $builds,) = $this->helperGetExecCounters($id, $filters, $opt); + + // particular options + $my['opt'] = array_merge(array( + 'output' => 'map' + ), $my['opt']); + $safe_id = intval($id); + + $fullEID = $this->helperConcatTCasePrefix($safe_id); + + // Because we now allow assignment of MULTIPLE testers to same test case + // we need to remove UA.user_id, in order to avoid duplication + // UA.user_id, + // we will need a second step to populate this info. + // + $sql = "/* {$debugMsg} Not Run */" . + " SELECT DISTINCT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . + " TPTCV.tcversion_id,TPTCV.platform_id,TPTCV.id AS feature_id," . + " TCV.version AS tcversion_number, B.id AS build_id," . + " '{$this->notRunStatusCode}' AS status, " . + " TCV.version,TCV.tc_external_id AS external_id, " . + " $fullEID AS full_external_id," . + " (TPTCV.urgency * TCV.importance) AS urg_imp, TCV.summary " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B " . + " ON B.testplan_id = TPTCV.testplan_id " . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " /* Get Test Case Version attributes */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . " AND UA.build_id = B.id " . + " AND UA.type = {$this->execTaskCode} " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.build_id = B.id " . " WHERE TPTCV.testplan_id=" . $safe_id . + " AND E.id IS NULL " . " AND B.id IN ({$builds->inClause}) "; + + switch ($my['opt']['output']) { + case 'array': + $dummy = (array) $this->db->get_recordset($sql); + + // Second Loop + // get features to get testers + if (! is_null($dummy)) { + // will try with a query + $sql = "/* {$debugMsg} Not Run */" . + " SELECT UA.user_id, UA.feature_id,UA.build_id" . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B " . + " ON B.testplan_id = TPTCV.testplan_id " . + " JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . + " AND UA.build_id = B.id " . + " AND UA.type = {$this->execTaskCode} " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.build_id = B.id " . " WHERE TPTCV.testplan_id=" . + $safe_id . " AND E.id IS NULL " . + " AND B.id IN ({$builds->inClause}) "; + + $dx = (array) $this->db->get_recordset($sql); + + $l2do = count($dx); + $loop2do = count($dummy); + for ($vdx = 0; $vdx < $l2do; $vdx ++) { + for ($fdx = 0; $fdx < $loop2do; $fdx ++) { + if ($dummy[$fdx]['feature_id'] == + $dx[$vdx]['feature_id'] && + $dummy[$fdx]['build_id'] == $dx[$vdx]['build_id']) { + $dummy[$fdx]['user_id'][$dx[$vdx]['user_id']] = $dx[$vdx]['user_id']; + break; + } + } + } + } + + break; + + case 'map': + default: + $keyColumns = array( + 'tsuite_id', + 'tcase_id', + 'platform_id', + 'build_id' + ); + $dummy = (array) $this->db->fetchRowsIntoMap4l($sql, $keyColumns); + throw new Exception("NOT REFACTORED YET for output 'map'", 1); + + break; + } + + return $dummy; + } + + /* + * + * @used-by lib/results/testCasesWithoutTester.php + * + * IMPORTANT NOTICE + * When doing count() with having, if there are platforms defined + * we have to consider for having clause BuildQty * PlatformQty, + * or we are going to get WRONG results. + */ + public function getNotRunWOTesterAssigned($id, $buildSet = null, + $filters = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + list ($my, $builds,) = $this->helperGetExecCounters($id, $filters, $opt); + list (, $buildsCfg,) = $this->helperGetHits($id, null, $buildSet, + array( + 'ignorePlatform' => true + )); + // particular options + $my['opt'] = array_merge( + array( + 'output' => 'map', + 'ignoreBuild' => false + ), $my['opt']); + $safe_id = intval($id); + + $fullEID = $this->helperConcatTCasePrefix($safe_id); + + $add2select = ' DISTINCT '; + $buildInfo = ''; + + // 20130106 - TICKET 5451 - added A_TPTCV.platform_id on GROUP BY + // this query try to indentify test cases that has NO ASSIGNMENT ON ALL Builds + // for EACH PLATFORM. + $sqlc = "/* $debugMsg */ " . + " SELECT count(0) AS TESTER_COUNTER ,A_NHTCV.parent_id AS tcase_id,A_TPTCV.platform_id " . + " FROM {$this->tables['testplan_tcversions']} A_TPTCV " . + " JOIN {$this->tables['builds']} A_B ON A_B.testplan_id = A_TPTCV.testplan_id " . + str_replace('B.active', 'A_B.active', $buildsCfg['statusClause']) . + " JOIN {$this->tables['nodes_hierarchy']} A_NHTCV ON " . + " A_NHTCV.id = A_TPTCV.tcversion_id " . + " LEFT OUTER JOIN {$this->tables['executions']} A_E " . + " ON A_E.testplan_id = A_TPTCV.testplan_id " . + " AND A_E.platform_id = A_TPTCV.platform_id " . + " AND A_E.tcversion_id = A_TPTCV.tcversion_id " . + " AND A_E.build_id = A_B.id " . + " LEFT OUTER JOIN {$this->tables['user_assignments']} A_UA " . + " ON A_UA.feature_id = A_TPTCV.id " . " AND A_UA.build_id = A_B.id " . + " AND A_UA.type = {$this->execTaskCode} " . + " WHERE A_TPTCV.testplan_id = " . $safe_id . + " AND A_E.status IS NULL " . " AND A_UA.user_id IS NULL "; + + // http://stackoverflow.com/questions/7511064/postresql-aliases-column-and-having + // + // if( DB_TYPE == 'mssql' ) + // { + // $sqlc .= " GROUP BY tcase_id " . + // " HAVING TESTER_COUNTER = " . intval($buildsCfg['count']) ; + // } + // else + // { + // $sqlc .= " GROUP BY A_NHTCV.parent_id " . + // " HAVING count(0) = " . intval($buildsCfg['count']) ; + // } + $sqlc .= " GROUP BY A_NHTCV.parent_id, A_TPTCV.platform_id " . + " HAVING count(0) = " . intval($buildsCfg['count']); + + $sql = "/* {$debugMsg} Not Run */" . + " SELECT $add2select NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . + " TPTCV.tcversion_id,TPTCV.platform_id," . + " TCV.version AS tcversion_number, {$buildInfo}" . + " '{$this->notRunStatusCode}' AS status, " . + " TCV.version,TCV.tc_external_id AS external_id, " . + " $fullEID AS full_external_id,UA.user_id," . + " (TPTCV.urgency * TCV.importance) AS urg_imp, TCV.summary " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['builds']} B " . + " ON B.testplan_id = TPTCV.testplan_id " . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " /* Get Test Case Version attributes */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . " JOIN ({$sqlc}) AS NR " . + " ON NR.tcase_id = NHTC.id " . + " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . + " ON UA.feature_id = TPTCV.id " . " AND UA.build_id = B.id " . + " AND UA.type = {$this->execTaskCode} " . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.build_id = B.id " . " WHERE TPTCV.testplan_id=" . $safe_id . + " AND E.id IS NULL AND UA.user_id IS NULL " . + + // 20130106 - TICKET 5451 - added CONDITION ON NR.platform_id + " AND B.id IN ({$builds->inClause}) AND TPTCV.platform_id = NR.platform_id "; + + switch ($my['opt']['output']) { + case 'array': + $dummy = $this->db->get_recordset($sql); + break; + + case 'map': + default: + $keyColumns = array( + 'tsuite_id', + 'tcase_id', + 'platform_id', + 'build_id' + ); + $dummy = $this->db->fetchRowsIntoMap4l($sql, $keyColumns); + break; + } + return (array) $dummy; + } + + /** + * + * @internal revisions + * + * @since 1.9.4 + */ + private function helperGetUserIdentity($idSet = null) + { + $sql = " SELECT id,login,first,last " . " FROM {$this->tables['users']}"; + + $inClause = ''; + if (! is_null($idSet) && ((array) $idSet > 0) && + ($dummy = implode(',', (array) $idSet)) != '') { + $inClause = " WHERE id IN ({$dummy}) "; + } + + return $this->db->fetchRowsIntoMap($sql . $inClause, 'id'); + } + + /** + * + * @used-by /lib/results/resultsMoreBuilds.php + * + * @internal revisions + * + * @since 1.9.4 + */ + private function queryMetrics($id, $filters = null, $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $safe = array(); + $safe['tplan_id'] = intval($id); + + $my = array(); + list ($my, $sqlLEX) = $this->initQueryMetrics($safe['tplan_id'], + $filters, $options); + + // ------------------------------------------------------------------------------------------- + // We will work always using last execution result as filter criteria. + // ------------------------------------------------------------------------------------------- + + // we will need a union to manage 'not run' (remember this status is NEVER WRITTEN to DB) + // and other statuses + // This logic have been borrowed from testplan.class.php - getLinkedForExecTree(). + // + $key2check = array( + 'builds' => 'build_id', + 'platforms' => 'platform_id' + ); + $ejoin = array(); + foreach ($key2check as $check => $field) { + $ejoin[$check] = is_null($my['filters'][$check]) ? '' : " AND E.$field IN (" . + implode(',', (array) $my['filters'][$check]) . ')'; + } + + $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . + " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . + " TCV.tc_external_id AS external_id, " . " COALESCE(E.status,'" . + $this->notRunStatusCode . "') AS exec_status " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . + " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . + $my['join']['ua'] . $my['join']['keywords'] . + " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . + " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . + " ON LEX.testplan_id = TPTCV.testplan_id " . + " AND LEX.tcversion_id = TPTCV.tcversion_id " . + " AND LEX.platform_id = TPTCV.platform_id " . + " AND LEX.testplan_id = " . $safe['tplan_id'] . + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.tcversion_id = TPTCV.tcversion_id " . + " AND E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . " AND E.build_id = " . + $my['filters']['build_id'] . " WHERE TPTCV.testplan_id =" . + $safe['tplan_id'] . $my['where']['where'] . + " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . + " AND E.id IS NULL AND LEX.id IS NULL"; + + // executions + $sex = "/* $debugMsg */" . + "SELECT E.status,E.notes,E.tcversion_number,E.execution_ts,E.build_id,E.platform_id " . + "FROM {$this->tables['testplan_tcversions']} TPTCV " . + "JOIN {$this->tables['executions']} E " . + "ON E.tcversion_id = TPTCV.tcversion_id " . + "AND E.testplan_id = TPTCV.testplan_id " . + "AND E.platform_id = TPTCV.platform_id "; + + // build up where clause + $where = "WHERE TPTCV.testplan_id = " . $safe['tplan_id']; + + $key2check = array( + 'builds' => 'build_id', + 'platforms' => 'platform_id' + ); + foreach ($key2check as $check => $field) { + if (! is_null($my['filters'][$check])) { + $where .= " AND E.$field IN (" . + implode(',', (array) $my['filters'][$check]) . ')'; + } + } + + $sql = $sex . $where; + + return $this->db->get_recordset($sql); + } + + /* + * + * @used-by + * + * + * @internal revisions + * @since 1.9.4 + */ + private function initQueryMetrics($tplanID, $filtersCfg, $optionsCfg) + { + $ic = array(); + + $ic['join'] = array(); + $ic['join']['ua'] = ''; + + $ic['where'] = array(); + $ic['where']['where'] = ''; + $ic['where']['platforms'] = ''; + + $ic['green_light'] = true; + + $ic['filters'] = array( + 'exec_ts_from' => null, + 'exec_ts_to' => null, + 'assigned_to' => null, + 'tester_id' => null, + 'keywords' => null, + 'builds' => null, + 'platforms' => null, + 'top_level_tsuites' => null + ); + + $ic['filters'] = array_merge($ic['filters'], (array) $filtersCfg); + + $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . + " MAX(EE.id) AS id " . " FROM {$this->tables['executions']} EE " . + " WHERE EE.testplan_id = " . $tplanID; + + $key2check = array( + 'builds' => 'build_id', + 'platforms' => 'platform_id' + ); + foreach ($key2check as $check => $field) { + $ic['where'][$check] = ''; + if (! is_null($ic['filters'][$check])) { + $sqlLEX .= " AND EE.$field IN (" . + implode(',', (array) $ic['filters'][$check]) . ')'; + $ic['where'][$check] = " AND TPTCV.$field IN (" . + implode(',', (array) $ic['filters'][$check]) . ')'; + } + } + $sqlLEX .= " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; + + if (! is_null($ic['filters']['keywords'])) { + list ($ic['join']['keywords'], $ic['where']['keywords']) = $this->helper_keywords_sql( + $ic['filters']['keywords'], array( + 'output' => 'array' + )); + + $ic['where']['where'] .= $ic['where']['keywords']; // **** // CHECK THIS CAN BE NON OK + } + + return array( + $ic, + $sqlLEX + ); + } + + /** + */ + public function getExecStatusMatrixFlat($id, $filters = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array(); + $my['opt'] = array( + 'getExecutionNotes' => false, + 'getTester' => false, + 'getUserAssignment' => false, + 'output' => null, + 'getExecutionTimestamp' => false, + 'getExecutionDuration' => false + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + + list ($my, , , $union) = $this->helperBuildSQLTestSuiteExecCounters($id, + $filters, $my['opt']); + + $sql = " /* {$debugMsg} UNION WITH ALL CLAUSE */ " . + " {$union['exec']} UNION ALL {$union['not_run']} "; + + $rs = $this->db->get_recordset($sql); + + $ltx = null; + if (! is_null($rs)) { + $priorityCfg = config_get('urgencyImportance'); + $cache = array( + 'tsuite' => null, + 'tcase' => null + ); + + $loop2do = count($rs); + + for ($adx = 0; $adx < $loop2do; $adx ++) { + if (! isset($cache['tsuite'][$rs[$adx]['tsuite_id']])) { + $stairway2heaven = $this->tree_manager->get_path( + $rs[$adx]['tsuite_id'], null, 'name'); + $cache['tsuite'][$rs[$adx]['tsuite_id']] = implode("/", + $stairway2heaven); + } + $rs[$adx]['suiteName'] = $cache['tsuite'][$rs[$adx]['tsuite_id']]; + + if ($rs[$adx]['urg_imp'] >= $priorityCfg->threshold['high']) { + $rs[$adx]['priority_level'] = HIGH; + } elseif ($rs[$adx]['urg_imp'] < $priorityCfg->threshold['low']) { + $rs[$adx]['priority_level'] = LOW; + } else { + $rs[$adx]['priority_level'] = MEDIUM; + } + + $keyExists = isset( + $ltx[$rs[$adx]['platform_id']][$rs[$adx]['tcase_id']]); + $doSet = ! $keyExists; + if ($keyExists) { + $doSet = ($ltx[$rs[$adx]['platform_id']][$rs[$adx]['tcase_id']]['id'] < + $rs[$adx]['executions_id']); + } + if ($doSet) { + $ltx[$rs[$adx]['platform_id']][$rs[$adx]['tcase_id']] = array( + 'id' => $rs[$adx]['executions_id'], + 'build_id' => $rs[$adx]['build_id'], + 'status' => $rs[$adx]['status'] + ); + } + } + } + + return array( + 'metrics' => $rs, + 'latestExec' => $ltx + ); + } + + /** + */ + private function helperStatusDomainMatrix(&$out, $rowKey, $colKey) + { + $totalByMatrix = array(); + + foreach ($out as &$elem) { + + $rowSet = array_keys($elem); + foreach ($rowSet as $rowID) { + + $colSet = array_keys($elem[$rowID]); + foreach ($colSet as $colID) { + $totalByMatrix[$rowID][$colID]['qty'] = 0; + foreach ($this->statusCode as $code) { + if (! isset($elem[$rowID][$colID][$code])) { + $elem[$rowID][$colID][$code] = array( + $rowKey => $rowID, + $colKey => $colID, + 'status' => $code, + 'exec_qty' => 0 + ); + } + $totalByMatrix[$rowID][$colID]['qty'] += $elem[$rowID][$colID][$code]['exec_qty']; + } + } + } + } + $out['total'] = $totalByMatrix; + } + + /** + */ + public function getBuildByPlatStatusForRender($id, + $totalKey = 'total_assigned') + { + $codeSet = $this->getStatusForReports(); + $labels = $this->resultsCfg['status_label']; + + $opt = array( + 'groupByPlatform' => true + ); + $metrics = $this->getExecCountersByBuildExecStatus($id, null, $opt); + + // Creating item list this way will generate a row also for + // ACTIVE BUILDS were ALL TEST CASES HAVE NO TESTER ASSIGNMENT + // $buildList = array_keys($metrics['active_builds']); + + // Creating item list this way will generate a row ONLY FOR + // ACTIVE BUILDS were TEST CASES HAVE TESTER ASSIGNMENT + if (! is_null($metrics)) { + $renObj = new stdClass(); + $renObj->info = array(); + + $mwt = &$metrics['with_tester']; + foreach ($mwt as $platID => $buildMetrics) { + foreach ($buildMetrics as $buildID => $met) { + $totalRun = 0; + $yo = &$renObj->info[$platID][$buildID]; + $yo['build_name'] = $metrics['active_builds'][$buildID]['name']; + $yo[$totalKey] = $metrics['total'][$platID][$buildID]['qty']; + $yo['details'] = array(); + + $rf = &$yo['details']; + foreach ($codeSet as $cCode => $code4h) { + $rf[$code4h] = array( + 'qty' => 0, + 'percentage' => 0 + ); + $rf[$code4h]['qty'] = $buildMetrics[$buildID][$cCode]['exec_qty']; + + if ($yo[$totalKey] > 0) { + $rf[$code4h]['percentage'] = number_format( + 100 * ($rf[$code4h]['qty'] / $yo[$totalKey]), 1); + } + } + } + } + + foreach ($codeSet as $human) { + $l10n = isset($labels[$human]) ? lang_get($labels[$human]) : lang_get( + $human); + $renObj->colDefinition[$human]['qty'] = $l10n; + $renObj->colDefinition[$human]['percentage'] = '[%]'; + } + + // Last step: get completness percentages + $platList = array_keys($renObj->info); + $tk = 'total_assigned'; + foreach ($platList as $platID) { + $itemList = array_keys($renObj->info[$platID]); + foreach ($itemList as $itemID) { + if (isset($renObj->info[$platID])) { + $totalRun = 0; + $rf = &$renObj->info[$platID][$itemID]['details']; + $doPerc = ($renObj->info[$platID][$itemID][$tk] > 0); + foreach ($codeSet as $c4human) { + $totalRun += ($c4human == 'not_run' ? 0 : $rf[$c4human]['qty']); + } + if ($doPerc) { + $renObj->info[$platID][$itemID]['percentage_completed'] = number_format( + 100 * + ($totalRun / $renObj->info[$platID][$itemID][$tk]), + 1); + } + } + } + } + } + return $renObj; + } + + /** + */ + public function getNeverRunByPlatform($tplanID, $platformSet = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safeID = intval($tplanID); + + $fullEID = $this->helperConcatTCasePrefix($safeID); + + // Because we now allow assignment of MULTIPLE testers to same test case + // we need to remove UA.user_id, in order to avoid duplication + // UA.user_id, + // we will need a second step to populate this info. + // + $sql = "/* {$debugMsg} Not Run */" . "SELECT COUNT(0) AS qty, " . + " NHTC.parent_id AS tsuite_id, " . + " NHTC.id AS tcase_id, TPTCV.platform_id, " . + " NHTC.name AS name, PLAT.name AS platform_name, " . + " $fullEID AS full_external_id " . + " FROM {$this->tables['testplan_tcversions']} TPTCV " . + + " JOIN {$this->tables['builds']} B " . + " ON B.testplan_id = TPTCV.testplan_id " . + + /* Get Test Case info from Test Case Version */ + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " /* Get Test Case Version attributes */ " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - - - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND E.status='{$status}' " . - $builds->whereAddExec; - - - switch($my['opt']['output']) - { - case 'array': - $dummy = (array)$this->db->get_recordset($sql); - break; - - case 'mapByExecID': - $dummy = (array)$this->db->fetchRowsIntoMap($sql,'executions_id'); - break; - - - case 'map': - default: - $keyColumns = array('tsuite_id','tcase_id','platform_id','build_id'); - $dummy = (array)$this->db->fetchRowsIntoMap4l($sql,$keyColumns); - break; - } - - return $dummy; - - } - - - /** - * get just Not Run test case on test plan, but ONLY THESE - * that has tester assigned. - * This is critic: - * - * example: - * test plan with 11 test cases linked. - * two Builds B1, B2 - * 1. Assign tester to all test cases on BUILD B1 - * 2. run getNotRunWithTesterAssigned() - * you will get 11 records all for B1 - * - * 3. Assign tester to 4 test cases on BUILD B2 - * 4. run getNotRunWithTesterAssigned() - * you will get: 15 records - * 11 records for B1 - * 4 records for B2 - * - * @param int $id test plan id - * @param char $status status code (one char) - * @param mixed $filters - * keys: 'buildSet' - * - * @param mixed opt - * keys: 'output' elem domain 'map','array' - * - */ - function getNotRunWithTesterAssigned($id,$filters=null,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($id, $filters, $opt); - - - // particular options - $my['opt'] = array_merge(array('output' => 'map'),$my['opt']); - $safe_id = intval($id); - - $fullEID = $this->helperConcatTCasePrefix($safe_id); - // $sqlLEBBP = $sqlStm['LEBBP']; - - // Because we now allow assignment of MULTIPLE testers to same test case - // we need to remove UA.user_id, in order to avoid duplication - // UA.user_id, - // we will need a second step to populate this info. - // - $sql = "/* {$debugMsg} Not Run */" . - " SELECT DISTINCT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . - " TPTCV.tcversion_id,TPTCV.platform_id,TPTCV.id AS feature_id," . - " TCV.version AS tcversion_number, B.id AS build_id," . - " '{$this->notRunStatusCode}' AS status, " . - " TCV.version,TCV.tc_external_id AS external_id, " . - " $fullEID AS full_external_id," . - " (TPTCV.urgency * TCV.importance) AS urg_imp, TCV.summary " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B " . - " ON B.testplan_id = TPTCV.testplan_id " . - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " /* Get Test Case Version attributes */ " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - - " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id = B.id " . - " AND UA.type = {$this->execTaskCode} " . - - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.build_id = B.id ". - - - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND E.id IS NULL " . - " AND B.id IN ({$builds->inClause}) "; - - - switch($my['opt']['output']) - { - case 'array': - $dummy = (array)$this->db->get_recordset($sql); - - // Second Loop - // get features to get testers - if(!is_null($dummy)) - { - // will try with a query - $sql = "/* {$debugMsg} Not Run */" . - " SELECT UA.user_id, UA.feature_id,UA.build_id" . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B " . - " ON B.testplan_id = TPTCV.testplan_id " . - - " JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id = B.id " . - " AND UA.type = {$this->execTaskCode} " . - - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.build_id = B.id ". - - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND E.id IS NULL " . - " AND B.id IN ({$builds->inClause}) "; - - $dx = $this->db->get_recordset($sql); - - $l2do = count($dx); - $loop2do = count($dummy); - for($vdx=0; $vdx < $l2do; $vdx++) - { - for($fdx=0; $fdx < $loop2do; $fdx++) - { - if($dummy[$fdx]['feature_id'] == $dx[$vdx]['feature_id'] && - $dummy[$fdx]['build_id'] == $dx[$vdx]['build_id'] - ) - { - $dummy[$fdx]['user_id'][$dx[$vdx]['user_id']] = $dx[$vdx]['user_id']; - break; - } - } - } - - } - - - break; - - case 'map': - default: - throw new Exception("NOT REFACTORED YET for output 'map'", 1); - $keyColumns = array('tsuite_id','tcase_id','platform_id','build_id'); - $dummy = (array)$this->db->fetchRowsIntoMap4l($sql,$keyColumns); - break; - } - - return $dummy; - - } - - - /* - * - * @used-by lib/results/testCasesWithoutTester.php - * - * IMPORTANT NOTICE - * When doing count() with having, if there are platforms defined - * we have to consider for having clause BuildQty * PlatformQty, - * or we are going to get WRONG results. - */ - function getNotRunWOTesterAssigned($id,$buildSet=null,$filters=null,$opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($id, $filters, $opt); - list($safe_id,$buildsCfg,$sqlLEX) = $this->helperGetHits($id,null,$buildSet, - array('ignorePlatform' => true)); - // particular options - $my['opt'] = array_merge(array('output' => 'map','ignoreBuild' => false),$my['opt']); - $safe_id = intval($id); - - $fullEID = $this->helperConcatTCasePrefix($safe_id); - - // $sqlLEBBP = $sqlStm['LEBBP']; - $add2select = ' DISTINCT '; - $buildInfo = ''; - - // 20130106 - TICKET 5451 - added A_TPTCV.platform_id on GROUP BY - // this query try to indentify test cases that has NO ASSIGNMENT ON ALL Builds - // for EACH PLATFORM. - $sqlc = "/* $debugMsg */ " . - " SELECT count(0) AS TESTER_COUNTER ,A_NHTCV.parent_id AS tcase_id,A_TPTCV.platform_id " . - " FROM {$this->tables['testplan_tcversions']} A_TPTCV " . - " JOIN {$this->tables['builds']} A_B ON A_B.testplan_id = A_TPTCV.testplan_id " . - str_replace('B.active','A_B.active',$buildsCfg['statusClause']) . - - " JOIN {$this->tables['nodes_hierarchy']} A_NHTCV ON " . - " A_NHTCV.id = A_TPTCV.tcversion_id " . - - " LEFT OUTER JOIN {$this->tables['executions']} A_E " . - " ON A_E.testplan_id = A_TPTCV.testplan_id " . - " AND A_E.platform_id = A_TPTCV.platform_id " . - " AND A_E.tcversion_id = A_TPTCV.tcversion_id " . - " AND A_E.build_id = A_B.id " . - - " LEFT OUTER JOIN {$this->tables['user_assignments']} A_UA " . - " ON A_UA.feature_id = A_TPTCV.id " . - " AND A_UA.build_id = A_B.id " . - " AND A_UA.type = {$this->execTaskCode} " . - - " WHERE A_TPTCV.testplan_id = " . $safe_id . - " AND A_E.status IS NULL " . - " AND A_UA.user_id IS NULL "; - - - // http://stackoverflow.com/questions/7511064/postresql-aliases-column-and-having - // - //if( DB_TYPE == 'mssql' ) - //{ - // $sqlc .= " GROUP BY tcase_id " . - // " HAVING TESTER_COUNTER = " . intval($buildsCfg['count']) ; - //} - //else - //{ - // $sqlc .= " GROUP BY A_NHTCV.parent_id " . - // " HAVING count(0) = " . intval($buildsCfg['count']) ; - //} - $sqlc .= " GROUP BY A_NHTCV.parent_id, A_TPTCV.platform_id " . - " HAVING count(0) = " . intval($buildsCfg['count']) ; - - - $sql = "/* {$debugMsg} Not Run */" . - " SELECT $add2select NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name," . - " TPTCV.tcversion_id,TPTCV.platform_id," . - " TCV.version AS tcversion_number, {$buildInfo}" . - " '{$this->notRunStatusCode}' AS status, " . - " TCV.version,TCV.tc_external_id AS external_id, " . - " $fullEID AS full_external_id,UA.user_id," . - " (TPTCV.urgency * TCV.importance) AS urg_imp, TCV.summary " . - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - - " JOIN {$this->tables['builds']} B " . - " ON B.testplan_id = TPTCV.testplan_id " . - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " /* Get Test Case Version attributes */ " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = TPTCV.tcversion_id " . - - " JOIN ({$sqlc}) AS NR " . - " ON NR.tcase_id = NHTC.id " . - - " LEFT OUTER JOIN {$this->tables['user_assignments']} UA " . - " ON UA.feature_id = TPTCV.id " . - " AND UA.build_id = B.id " . - " AND UA.type = {$this->execTaskCode} " . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.tcversion_id = TPTCV.tcversion_id " . - " AND E.build_id = B.id ". - - - " WHERE TPTCV.testplan_id=" . $safe_id . - " AND E.id IS NULL AND UA.user_id IS NULL " . - - // 20130106 - TICKET 5451 - added CONDITION ON NR.platform_id - " AND B.id IN ({$builds->inClause}) AND TPTCV.platform_id = NR.platform_id "; - - switch($my['opt']['output']) - { - case 'array': - $dummy = $this->db->get_recordset($sql); - break; - - case 'map': - default: - $keyColumns = array('tsuite_id','tcase_id','platform_id','build_id'); - $dummy = $this->db->fetchRowsIntoMap4l($sql,$keyColumns); - break; - } - return (array)$dummy; - } - - - - /** - * - * @internal revisions - * - * @since 1.9.4 - */ - function helperGetUserIdentity($idSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " SELECT id,login,first,last " . - " FROM {$this->tables['users']}"; - - $inClause = ''; - if( !is_null($idSet) && ((array)$idSet >0)) - { - if( ($dummy=implode(',',(array)$idSet)) != '' ) - { - $inClause = " WHERE id IN ({$dummy}) "; - } - } - - $rs = $this->db->fetchRowsIntoMap($sql . $inClause,'id'); - return $rs; - } - - - /** - * - * @used-by /lib/results/resultsMoreBuilds.php - * - * @internal revisions - * - * @since 1.9.4 - */ - function queryMetrics($id,$filters=null,$options=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $safe = array(); - $safe['tplan_id'] = intval($id); - - $my = array(); - list($my,$sqlLEX) = $this->initQueryMetrics($safe['tplan_id'],$filters,$options); - - - // ------------------------------------------------------------------------------------------- - // We will work always using last execution result as filter criteria. - // ------------------------------------------------------------------------------------------- - - // we will need a union to manage 'not run' (remember this status is NEVER WRITTEN to DB) - // and other statuses - // This logic have been borrowed from testplan.class.php - getLinkedForExecTree(). - // - $key2check = array('builds' => 'build_id', 'platforms' => 'platform_id'); - $ejoin = array(); - foreach($key2check as $check => $field) - { - $ejoin[$check] = is_null($my['filters'][$check]) ? '' : - " AND E.$field IN (" . implode(',',(array)$my['filters'][$check]) . ')'; - } - - - - $union['not_run'] = "/* {$debugMsg} sqlUnion - not run */" . - " SELECT NH_TCASE.id AS tcase_id,TPTCV.tcversion_id,TCV.version," . - " TCV.tc_external_id AS external_id, " . - " COALESCE(E.status,'" . $this->notRunStatusCode . "') AS exec_status " . - - " FROM {$this->tables['testplan_tcversions']} TPTCV " . - " JOIN {$this->tables['tcversions']} TCV ON TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = TPTCV.tcversion_id " . - " JOIN {$this->tables['nodes_hierarchy']} NH_TCASE ON NH_TCASE.id = NH_TCV.parent_id " . - $my['join']['ua'] . - $my['join']['keywords'] . - " LEFT OUTER JOIN {$this->tables['platforms']} PLAT ON PLAT.id = TPTCV.platform_id " . - - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id ON LEFT OUTER see WHERE */ " . - " LEFT OUTER JOIN ({$sqlLEX}) AS LEX " . - " ON LEX.testplan_id = TPTCV.testplan_id " . - " AND LEX.tcversion_id = TPTCV.tcversion_id " . - " AND LEX.platform_id = TPTCV.platform_id " . - " AND LEX.testplan_id = " . $safe['tplan_id'] . - " LEFT OUTER JOIN {$this->tables['executions']} E " . - " ON E.tcversion_id = TPTCV.tcversion_id " . - " AND E.testplan_id = TPTCV.testplan_id " . - " AND E.platform_id = TPTCV.platform_id " . - " AND E.build_id = " . $my['filters']['build_id'] . - - " WHERE TPTCV.testplan_id =" . $safe['tplan_id'] . - $my['where']['where'] . - " /* Get REALLY NOT RUN => BOTH LE.id AND E.id NULL */ " . - " AND E.id IS NULL AND LEX.id IS NULL"; - - - // executions - $sex = "/* $debugMsg */" . - "SELECT E.status,E.notes,E.tcversion_number,E.execution_ts,E.build_id,E.platform_id " . - "FROM {$this->tables['testplan_tcversions']} TPTCV " . - "JOIN {$this->tables['executions']} E " . - "ON E.tcversion_id = TPTCV.tcversion_id " . - "AND E.testplan_id = TPTCV.testplan_id " . - "AND E.platform_id = TPTCV.platform_id "; - - - - // build up where clause - $where = "WHERE TPTCV.testplan_id = " . $safe['tplan_id']; - - $key2check = array('builds' => 'build_id', 'platforms' => 'platform_id'); - foreach($key2check as $check => $field) - { - if( !is_null($my['filters'][$check]) ) - { - $where .= " AND E.$field IN (" . implode(',',(array)$my['filters'][$check]) . ')'; - } - } - - $sql = $sex . $where; - - // echo $sql; - $rs = $this->db->get_recordset($sql); - return $rs; - } - - - - /* - * - * @used-by - * - * - * @internal revisions - * @since 1.9.4 - */ - function initQueryMetrics($tplanID,$filtersCfg,$optionsCfg) - { - $ic = array(); - - $ic['join'] = array(); - $ic['join']['ua'] = ''; - - $ic['where'] = array(); - $ic['where']['where'] = ''; - $ic['where']['platforms'] = ''; - - $ic['green_light'] = true; - - $ic['filters'] = array('exec_ts_from' => null, 'exec_ts_to' => null, - 'assigned_to' => null, 'tester_id' => null, - 'keywords' => null, 'builds' => null, - 'platforms' => null, 'top_level_tsuites' => null); - - $ic['filters'] = array_merge($ic['filters'],(array)$filtersCfg); - - - // --------------------------------------------------------------------------------------------- - $sqlLEX = " SELECT EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id," . - " MAX(EE.id) AS id " . - " FROM {$this->tables['executions']} EE " . - " WHERE EE.testplan_id = " . $tplanID; - - $key2check = array('builds' => 'build_id', 'platforms' => 'platform_id'); - foreach($key2check as $check => $field) - { - $ic['where'][$check] = ''; - if( !is_null($ic['filters'][$check]) ) - { - $sqlLEX .= " AND EE.$field IN (" . implode(',',(array)$ic['filters'][$check]) . ')'; - $ic['where'][$check] = " AND TPTCV.$field IN (" . implode(',',(array)$ic['filters'][$check]) . ')'; - } - } - $sqlLEX .= " GROUP BY EE.tcversion_id,EE.testplan_id,EE.platform_id,EE.build_id "; - // --------------------------------------------------------------------------------------------- - - if( !is_null($ic['filters']['keywords']) ) - { - list($ic['join']['keywords'],$ic['where']['keywords']) = - $this->helper_keywords_sql($ic['filters']['keywords'],array('output' => 'array')); - - $ic['where']['where'] .= $ic['where']['keywords']; // **** // CHECK THIS CAN BE NON OK - } - - - return array($ic,$sqlLEX); - } - - /** - * - * - */ - function getExecStatusMatrixFlat($id, $filters=null, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array(); - $my['opt'] = array('getExecutionNotes' => false, 'getTester' => false, - 'getUserAssignment' => false, 'output' => null, - 'getExecutionTimestamp' => false, 'getExecutionDuration' => false); - - $my['opt'] = array_merge($my['opt'], (array)$opt); - $safe_id = intval($id); - list($my,$builds,$sqlStm,$union) = $this->helperBuildSQLTestSuiteExecCounters($id, $filters, $my['opt']); - - $sql = " /* {$debugMsg} UNION WITH ALL CLAUSE */ " . - " {$union['exec']} UNION ALL {$union['not_run']} "; - - //echo $sql; - $rs = $this->db->get_recordset($sql); - // new dBug($rs); - - $ltx = null; - if(!is_null($rs)) - { - $priorityCfg = config_get('urgencyImportance'); - $cache = array('tsuite' => null, 'tcase' => null); - - $loop2do = count($rs); - $gnOpt = array('fields' => 'name'); - - for($adx=0; $adx < $loop2do; $adx++) - { - if(!isset($cache['tsuite'][$rs[$adx]['tsuite_id']])) - { - $stairway2heaven = $this->tree_manager->get_path($rs[$adx]['tsuite_id'],null,'name'); - $cache['tsuite'][$rs[$adx]['tsuite_id']] = implode("/",$stairway2heaven); - } - $rs[$adx]['suiteName'] = $cache['tsuite'][$rs[$adx]['tsuite_id']]; - - if($rs[$adx]['urg_imp'] >= $priorityCfg->threshold['high']) - { - $rs[$adx]['priority_level'] = HIGH; - } - else if( $rs[$adx]['urg_imp'] < $priorityCfg->threshold['low']) - { - $rs[$adx]['priority_level'] = LOW; - } - else - { - $rs[$adx]['priority_level'] = MEDIUM; - } - - $kyy = $rs[$adx]['platform_id'] . '-' . $rs[$adx]['tcase_id']; - - // $keyExists = isset($ltx[$kyy]); - $keyExists = isset($ltx[$rs[$adx]['platform_id']][$rs[$adx]['tcase_id']]); - $doSet = !$keyExists; - if( $keyExists ) - { - $doSet = ($ltx[$rs[$adx]['platform_id']][$rs[$adx]['tcase_id']]['id'] < - $rs[$adx]['executions_id']); - } - if( $doSet ) - { - $ltx[$rs[$adx]['platform_id']][$rs[$adx]['tcase_id']] = - array('id' => $rs[$adx]['executions_id'],'build_id' => $rs[$adx]['build_id'], - 'status' => $rs[$adx]['status']); - } - } - } - //new dBug($cache); - //new dBug($rs); - //new dBug($ltx); - - return array('metrics' => $rs, 'latestExec' => $ltx); - } - - - /** - * - * - * - * - */ - function helperStatusDomainMatrix(&$out,$rowKey,$colKey) { - $totalByMatrix = array(); - - foreach($out as &$elem) { - - $rowSet = array_keys($elem); - foreach($rowSet as $rowID) { - - $colSet = array_keys($elem[$rowID]); - foreach($colSet as $colID) { - $totalByMatrix[$rowID][$colID]['qty'] = 0; - foreach($this->statusCode as $verbose => $code) { - if(!isset($elem[$rowID][$colID][$code])) { - $elem[$rowID][$colID][$code] = - array($rowKey => $rowID, $colKey => $colID, - 'status' => $code, 'exec_qty' => 0); - } - $totalByMatrix[$rowID][$colID]['qty'] += - $elem[$rowID][$colID][$code]['exec_qty']; - } - } - } - } - $out['total'] = $totalByMatrix; - } - - /** - * - * NEWZ - **/ - function getBuildByPlatStatusForRender($id, $totalKey='total_assigned') { - - $renderObj = null; - $codeSet = $this->getStatusForReports(); - $labels = $this->resultsCfg['status_label']; - - $opt = array('groupByPlatform' => true); - $metrics = $this->getExecCountersByBuildExecStatus($id,null,$opt); - - // Creating item list this way will generate a row also for - // ACTIVE BUILDS were ALL TEST CASES HAVE NO TESTER ASSIGNMENT - // $buildList = array_keys($metrics['active_builds']); - - // Creating item list this way will generate a row ONLY FOR - // ACTIVE BUILDS were TEST CASES HAVE TESTER ASSIGNMENT - if( !is_null($metrics) ) { - $renObj = new stdClass(); - $renObj->info = array(); - - $platList = array_keys($metrics['with_tester']); - $mwt = &$metrics['with_tester']; - foreach( $mwt as $platID => $buildMetrics ) { - foreach($buildMetrics as $buildID => $met ) { - $totalRun = 0; - $yo = &$renObj->info[$platID][$buildID]; - $yo['build_name'] = - $metrics['active_builds'][$buildID]['name']; - $yo[$totalKey] = - $metrics['total'][$platID][$buildID]['qty']; - $yo['details'] = array(); - - $rf = &$yo['details']; - foreach($codeSet as $cCode => $code4h) { - $rf[$code4h] = array('qty' => 0, 'percentage' => 0); - $rf[$code4h]['qty'] = $buildMetrics[$buildID][$cCode]['exec_qty']; - - if( $yo[$totalKey] > 0 ) { - $rf[$code4h]['percentage'] = - number_format(100 * ($rf[$code4h]['qty'] / $yo[$totalKey]),1); - } - //$totalRun += - // $code4h == 'not_run' ? 0 : $rf[$code4h]['qty']; - } - } - } - - foreach($codeSet as $human) { - $l10n = isset($labels[$human]) ? lang_get($labels[$human]) : lang_get($human); - $renObj->colDefinition[$human]['qty'] = $l10n; - $renObj->colDefinition[$human]['percentage'] = '[%]'; - } - - // Last step: get completness percentages - $platList = array_keys($renObj->info); - $tk = 'total_assigned'; - foreach($platList as $platID) { - $itemList = array_keys($renObj->info[$platID]); - foreach($itemList as $itemID) { - if( isset($renObj->info[$platID]) ) { - $totalRun = 0; - $rf = &$renObj->info[$platID][$itemID]['details']; - $doPerc = ($renObj->info[$platID][$itemID][$tk] > 0); - foreach($codeSet as $sCode => $c4human) { - $totalRun += ($c4human == 'not_run' ? 0 : - $rf[$c4human]['qty']); - } - if($doPerc) { - $renObj->info[$platID][$itemID]['percentage_completed'] = - number_format(100 * - ($totalRun/$renObj->info[$platID][$itemID][$tk]),1); - } - } - } - } - } - return $renObj; - } - - /** - * - * - */ - function getNeverRunByPlatform($tplanID,$platformSet=null) - { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safeID = intval($tplanID); - - $fullEID = $this->helperConcatTCasePrefix($safeID); - - // Because we now allow assignment of MULTIPLE testers to same test case - // we need to remove UA.user_id, in order to avoid duplication - // UA.user_id, - // we will need a second step to populate this info. - // - $sql = "/* {$debugMsg} Not Run */" . - "SELECT COUNT(0) AS qty, - NHTC.parent_id AS tsuite_id, - NHTC.id AS tcase_id, TPTCV.platform_id, - NHTC.name AS name, PLAT.name AS platform_name, - $fullEID AS full_external_id - FROM {$this->tables['testplan_tcversions']} TPTCV - - JOIN {$this->tables['builds']} B - ON B.testplan_id = TPTCV.testplan_id + /* Get Test Suite info from Test Case */ + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . - /* Get Test Case info from Test Case Version */ - JOIN {$this->tables['nodes_hierarchy']} NHTCV - ON NHTCV.id = TPTCV.tcversion_id - - /* Get Test Suite info from Test Case */ - JOIN {$this->tables['nodes_hierarchy']} NHTC - ON NHTC.id = NHTCV.parent_id - /* Get Test Case Version attributes */ - JOIN {$this->tables['tcversions']} TCV - ON TCV.id = TPTCV.tcversion_id - - JOIN {$this->tables['platforms']} PLAT - ON PLAT.id = TPTCV.platform_id - - - LEFT OUTER JOIN {$this->tables['executions']} E - ON E.testplan_id = TPTCV.testplan_id - AND E.platform_id = TPTCV.platform_id - AND E.tcversion_id = TPTCV.tcversion_id - AND E.build_id = B.id - - WHERE TPTCV.testplan_id=$safeID - AND E.id IS NULL"; - - if( null != $platformSet ) { - $sql .= " AND TPTCV.platform_id IN (" . - implode(",", $platformSet) . ")"; - } - $sql .= " GROUP BY tsuite_id, tcase_id, - TPTCV.platform_id,NHTC.name,platform_name, - full_external_id "; - - $buildSet = $this->get_builds($safeID,testplan::ACTIVE_BUILDS, - testplan::OPEN_BUILDS); - - $sql .= " HAVING COUNT(0) = " . count($buildSet); - $sql .= " ORDER BY platform_name,full_external_id "; - - return $this->db->get_recordset($sql); - } - - - /** - * - * - * - * - */ - function getLatestExecOnSinglePlatformMatrix($id, $platform_id, - $filters=null, $opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my = array(); - $my['opt'] = array('getExecutionNotes' => TRUE, - 'getTester' => false, - 'getUserAssignment' => false, - 'output' => null, - 'getExecutionTimestamp' => false, - 'getExecutionDuration' => false); - - $my['opt'] = array_merge($my['opt'], (array)$opt); - $safe_id = intval($id); - list($my,$builds,$sqlStm,$union) = $this->buildSQLTSLatestExecOneSinglePlatform($id, $platform_id,$filters, $my['opt']); - - $sql = " /* {$debugMsg} {$union['exec']} "; - - $keyColumns = array('tsuite_id','tcase_id','platform_id'); - $cumulative = true; - $dummy = (array)$this->db->fetchRowsIntoMap3l($sql,$keyColumns,$cumulative); - - unset($sqlStm); - unset($union); - unset($my); - unset($builds); - - // now is time do some decoding - // Key is a tuple (PARENT tsuite_id, test case id, platform id) - // - $item2loop = array_keys($dummy); - $stairway2heaven = null; - $pathway = null; - $latestExec = null; - $priorityCfg = config_get('urgencyImportance'); - - foreach ($item2loop as $item_id) { - $stairway2heaven = $this->tree_manager->get_path($item_id,null,'name'); - $pathway[$item_id] = implode("/",$stairway2heaven); - unset($stairway2heaven); - - // go inside test case - $tcase2loop = array_keys($dummy[$item_id]); - foreach($tcase2loop as $tcase_id) { - $platform2loop = array_keys($dummy[$item_id][$tcase_id]); - foreach($platform2loop as $platform_id) { - $rf = &$dummy[$item_id][$tcase_id][$platform_id][0]; - $rf['suiteName'] = $pathway[$item_id]; - - // --------------------------------------------------- - // Now we need to get priority LEVEL from - // (urgency * impact) - // we do not use a function to improve performance - if ($rf['urg_imp'] >= $priorityCfg->threshold['high']) { - $rf['priority_level'] = HIGH; - } else if($rf['urg_imp'] < $priorityCfg->threshold['low']) { - $rf['priority_level'] = LOW; - } else { - $rf['priority_level'] = MEDIUM; - } - } // $platform2loop - } // $tcase2loop - - unset($tcase2loop); - unset($platform2loop); - } // - - unset($pathway); - return $dummy; - } - - /** - * - * - */ - function getNeverRunOnTestPlanWithoutPlatforms($tplanID) - { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safeID = intval($tplanID); - - $fullEID = $this->helperConcatTCasePrefix($safeID); - - // Because we now allow assignment of MULTIPLE testers to same test case - // we need to remove UA.user_id, in order to avoid duplication - // UA.user_id, - // we will need a second step to populate this info. - // - $sql = "/* {$debugMsg} Not Run */ + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = TPTCV.tcversion_id " . + + " JOIN {$this->tables['platforms']} PLAT " . + " ON PLAT.id = TPTCV.platform_id " . + + " LEFT OUTER JOIN {$this->tables['executions']} E " . + " ON E.testplan_id = TPTCV.testplan_id " . + " AND E.platform_id = TPTCV.platform_id " . + " AND E.tcversion_id = TPTCV.tcversion_id " . + " AND E.build_id = B.id " . " WHERE TPTCV.testplan_id=$safeID " . + " AND E.id IS NULL"; + + if (null != $platformSet) { + $sql .= " AND TPTCV.platform_id IN (" . implode(",", $platformSet) . + ")"; + } + $sql .= " GROUP BY tsuite_id, tcase_id, " . + " TPTCV.platform_id,NHTC.name,platform_name, " . " full_external_id "; + + $buildSet = $this->get_builds($safeID, testplan::ACTIVE_BUILDS, + testplan::OPEN_BUILDS); + + $sql .= " HAVING COUNT(0) > " . count($buildSet) - 1; + $sql .= " ORDER BY platform_name,full_external_id "; + + return $this->db->get_recordset($sql); + } + + /** + */ + public function getLatestExecOnSinglePlatformMatrix($id, $platform_id, + $filters = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my = array(); + $my['opt'] = array( + 'getExecutionNotes' => true, + 'getTester' => false, + 'getUserAssignment' => false, + 'output' => null, + 'getExecutionTimestamp' => false, + 'getExecutionDuration' => false + ); + + $my['opt'] = array_merge($my['opt'], (array) $opt); + list ($my, $builds, $sqlStm, $union) = $this->buildSQLTSLatestExecOneSinglePlatform( + $id, $platform_id, $filters, $my['opt']); + + $sql = " /* {$debugMsg} {$union['exec']} "; + + $keyColumns = array( + 'tsuite_id', + 'tcase_id', + 'platform_id' + ); + $cumulative = true; + $dummy = (array) $this->db->fetchRowsIntoMap3l($sql, $keyColumns, + $cumulative); + + unset($sqlStm); + unset($union); + unset($my); + unset($builds); + + // now is time do some decoding + // Key is a tuple (PARENT tsuite_id, test case id, platform id) + // + $item2loop = array_keys($dummy); + $stairway2heaven = null; + $pathway = null; + $priorityCfg = config_get('urgencyImportance'); + + foreach ($item2loop as $item_id) { + $stairway2heaven = $this->tree_manager->get_path($item_id, null, + 'name'); + $pathway[$item_id] = implode("/", $stairway2heaven); + unset($stairway2heaven); + + // go inside test case + $tcase2loop = array_keys($dummy[$item_id]); + foreach ($tcase2loop as $tcase_id) { + $platform2loop = array_keys($dummy[$item_id][$tcase_id]); + foreach ($platform2loop as $platform_id) { + $rf = &$dummy[$item_id][$tcase_id][$platform_id][0]; + $rf['suiteName'] = $pathway[$item_id]; + + // Now we need to get priority LEVEL from + // (urgency * impact) + // we do not use a function to improve performance + if ($rf['urg_imp'] >= $priorityCfg->threshold['high']) { + $rf['priority_level'] = HIGH; + } elseif ($rf['urg_imp'] < $priorityCfg->threshold['low']) { + $rf['priority_level'] = LOW; + } else { + $rf['priority_level'] = MEDIUM; + } + } + } + + unset($tcase2loop); + unset($platform2loop); + } + + unset($pathway); + return $dummy; + } + + /** + */ + private function getNeverRunOnTestPlanWithoutPlatforms($tplanID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safeID = intval($tplanID); + + $fullEID = $this->helperConcatTCasePrefix($safeID); + + // Because we now allow assignment of MULTIPLE testers to same test case + // we need to remove UA.user_id, in order to avoid duplication + // UA.user_id, + // we will need a second step to populate this info. + // + $sql = "/* {$debugMsg} Not Run */ SELECT COUNT(0) AS qty, NHTC.parent_id AS tsuite_id, NHTC.id AS tcase_id, NHTC.name AS name, $fullEID AS full_external_id - FROM {$this->tables['testplan_tcversions']} TPTCV - + FROM {$this->tables['testplan_tcversions']} TPTCV + JOIN {$this->tables['builds']} B - ON B.testplan_id = TPTCV.testplan_id + ON B.testplan_id = TPTCV.testplan_id - /* Get Test Case info from Test Case Version */ - JOIN {$this->tables['nodes_hierarchy']} NHTCV + /* Get Test Case info from Test Case Version */ + JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id - - /* Get Test Suite info from Test Case */ + + /* Get Test Suite info from Test Case */ JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id - + /* Get Test Case Version attributes */ JOIN {$this->tables['tcversions']} TCV - ON TCV.id = TPTCV.tcversion_id + ON TCV.id = TPTCV.tcversion_id - LEFT OUTER JOIN {$this->tables['executions']} E + LEFT OUTER JOIN {$this->tables['executions']} E ON E.testplan_id = TPTCV.testplan_id AND E.tcversion_id = TPTCV.tcversion_id AND E.build_id = B.id - - WHERE TPTCV.testplan_id=$safeID - AND E.id IS NULL"; - - $sql .= " GROUP BY tsuite_id, tcase_id, NHTC.name, - full_external_id "; - - $buildSet = $this->get_builds($safeID,testplan::ACTIVE_BUILDS, - testplan::OPEN_BUILDS); - - $sql .= " HAVING COUNT(0) = " . count($buildSet); - - // echo $sql; - return $this->db->get_recordset($sql); - } - - - /** - * - * - */ - function getNeverRunOnSinglePlatform($tplanID,$platformID,$opt=null) - { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $safeID = intval($tplanID); - - $fullEID = $this->helperConcatTCasePrefix($safeID); - $notRunCode = $this->resultsCfg['status_code']['not_run']; - // Because we now allow assignment of MULTIPLE testers to same test case - // we need to remove UA.user_id, in order to avoid duplication - // UA.user_id, - // we will need a second step to populate this info. - // - $sql = "/* {$debugMsg} Not Run */" . - "SELECT COUNT(0) AS qty, + WHERE TPTCV.testplan_id=$safeID + AND E.id IS NULL"; + + $sql .= " GROUP BY tsuite_id, tcase_id, NHTC.name, + full_external_id "; + + $buildSet = $this->get_builds($safeID, testplan::ACTIVE_BUILDS, + testplan::OPEN_BUILDS); + + $sql .= " HAVING COUNT(0) = " . count($buildSet); + + return $this->db->get_recordset($sql); + } + + /** + */ + public function getNeverRunOnSinglePlatform($tplanID, $platformID, + $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + $safeID = intval($tplanID); + + $fullEID = $this->helperConcatTCasePrefix($safeID); + $notRunCode = $this->resultsCfg['status_code']['not_run']; + + // Because we now allow assignment of MULTIPLE testers to same test case + // we need to remove UA.user_id, in order to avoid duplication + // UA.user_id, + // we will need a second step to populate this info. + // + $sql = "/* {$debugMsg} Not Run */" . + "SELECT COUNT(0) AS qty, NHTC.parent_id AS tsuite_id, NHTC.id AS tcase_id, TPTCV.platform_id, NHTC.name AS name, PLAT.name AS platform_name, @@ -3191,468 +3086,460 @@ function getNeverRunOnSinglePlatform($tplanID,$platformID,$opt=null) TCV.version,TCV.execution_type AS exec_type, (TPTCV.urgency * TCV.importance) AS urg_imp, '{$notRunCode}' AS status - FROM {$this->tables['testplan_tcversions']} TPTCV - + FROM {$this->tables['testplan_tcversions']} TPTCV + JOIN {$this->tables['builds']} B - ON B.testplan_id = TPTCV.testplan_id + ON B.testplan_id = TPTCV.testplan_id - /* Get Test Case info from Test Case Version */ - JOIN {$this->tables['nodes_hierarchy']} NHTCV + /* Get Test Case info from Test Case Version */ + JOIN {$this->tables['nodes_hierarchy']} NHTCV ON NHTCV.id = TPTCV.tcversion_id - - /* Get Test Suite info from Test Case */ + + /* Get Test Suite info from Test Case */ JOIN {$this->tables['nodes_hierarchy']} NHTC ON NHTC.id = NHTCV.parent_id - + /* Get Test Case Version attributes */ JOIN {$this->tables['tcversions']} TCV - ON TCV.id = TPTCV.tcversion_id + ON TCV.id = TPTCV.tcversion_id JOIN {$this->tables['platforms']} PLAT - ON PLAT.id = TPTCV.platform_id + ON PLAT.id = TPTCV.platform_id - LEFT OUTER JOIN {$this->tables['executions']} E + LEFT OUTER JOIN {$this->tables['executions']} E ON E.testplan_id = TPTCV.testplan_id AND E.platform_id = TPTCV.platform_id AND E.tcversion_id = TPTCV.tcversion_id AND E.build_id = B.id - - WHERE TPTCV.testplan_id=$safeID - AND E.id IS NULL"; - $sql .= " AND TPTCV.platform_id=" . intval($platformID); - $sql .= " GROUP BY tsuite_id, tcase_id, + WHERE TPTCV.testplan_id=$safeID + AND E.id IS NULL"; + + $sql .= " AND TPTCV.platform_id=" . intval($platformID); + $sql .= " GROUP BY tsuite_id, tcase_id, TPTCV.platform_id,NHTC.name,platform_name, full_external_id,external_id, - TCV.version,exec_type,urg_imp,status"; - - //echo $sql; - $buildSet = $this->get_builds($safeID,testplan::ACTIVE_BUILDS, - testplan::OPEN_BUILDS); - - $sql .= " HAVING COUNT(0) = " . count($buildSet); - // $sql .= " ORDER BY platform_name,full_external_id "; - - // echo $sql; - $keyColumns = array('tsuite_id','tcase_id','platform_id'); - $cumulative = true; - $dummy = (array)$this->db->fetchRowsIntoMap3l($sql,$keyColumns,$cumulative); - - return $dummy; - } - - - /** - * - * when getting info for executed test cases, RIGHT version number - * for execution is on EXECUTIONS TABLE not on testplan_tcversions TABLE. - * - * REMEMBER that when we update TCVERSION for executed Test Cases, - * we HAVE TO UPDATE testplan_tcversions table. - * - * We also need to use E.tcversion_id and NOT TPTCV.tcversion_id. - * - */ - function buildSQLTSLatestExecOneSinglePlatform($id, $platform_id, $filters=null, $opt=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('getExecutionNotes' => false, - 'getTester' => false, - 'getUserAssignment' => false, - 'getExecutionTimestamp' => false, - 'getExecutionDuration' => false); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - - list($my,$builds,$sqlStm) = $this->helperGetExecCounters($id, $filters, $opt); - - $safe_id = intval($id); - - - // Additional Execution fields - $moreExecFields = ""; - if ($my['opt']['getExecutionNotes']) { - $moreExecFields .= "E.notes AS execution_notes,"; - } - - if ($my['opt']['getTester']) { - $moreExecFields .= "E.tester_id,"; - } - - if ($my['opt']['getExecutionTimestamp']) { - $moreExecFields .= "E.execution_ts,"; - } - - if ($my['opt']['getExecutionDuration']) { - $moreExecFields .= "E.execution_duration,"; - } - - if ($my['opt']['getUserAssignment']) { - $moreExecFields .= "UA.user_id,"; - } - - // Latest Executions By Platform (LEBP) - $sqlLEBP = $sqlStm['LEBP']; - - $union['exec'] = - "/* {$debugMsg} sqlUnion Test suites - executions */ - SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, + TCV.version,exec_type,urg_imp,status"; + + $buildSet = $this->get_builds($safeID, testplan::ACTIVE_BUILDS, + testplan::OPEN_BUILDS); + + $sql .= " HAVING COUNT(0) = " . count($buildSet); + + $keyColumns = array( + 'tsuite_id', + 'tcase_id', + 'platform_id' + ); + $cumulative = true; + return (array) $this->db->fetchRowsIntoMap3l($sql, $keyColumns, + $cumulative); + } + + /** + * when getting info for executed test cases, RIGHT version number + * for execution is on EXECUTIONS TABLE not on testplan_tcversions TABLE. + * + * REMEMBER that when we update TCVERSION for executed Test Cases, + * we HAVE TO UPDATE testplan_tcversions table. + * + * We also need to use E.tcversion_id and NOT TPTCV.tcversion_id. + */ + private function buildSQLTSLatestExecOneSinglePlatform($id, $platform_id, + $filters = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['opt'] = array( + 'getExecutionNotes' => false, + 'getTester' => false, + 'getUserAssignment' => false, + 'getExecutionTimestamp' => false, + 'getExecutionDuration' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + list ($my, $builds, $sqlStm) = $this->helperGetExecCounters($id, + $filters, $opt); + + $safe_id = intval($id); + + // Additional Execution fields + $moreExecFields = ""; + if ($my['opt']['getExecutionNotes']) { + $moreExecFields .= "E.notes AS execution_notes,"; + } + + if ($my['opt']['getTester']) { + $moreExecFields .= "E.tester_id,"; + } + + if ($my['opt']['getExecutionTimestamp']) { + $moreExecFields .= "E.execution_ts,"; + } + + if ($my['opt']['getExecutionDuration']) { + $moreExecFields .= "E.execution_duration,"; + } + + if ($my['opt']['getUserAssignment']) { + $moreExecFields .= "UA.user_id,"; + } + + // Latest Executions By Platform (LEBP) + $sqlLEBP = $sqlStm['LEBP']; + + $union['exec'] = "/* {$debugMsg} sqlUnion Test suites - executions */ + SELECT NHTC.parent_id AS tsuite_id,NHTC.id AS tcase_id, NHTC.name AS name, TPTCV.tcversion_id,TPTCV.platform_id, E.build_id,E.tcversion_number AS version, TCV.tc_external_id AS external_id, - E.id AS executions_id, E.status AS status, + E.id AS executions_id, E.status AS status, {$moreExecFields} E.execution_type AS exec_type, (TPTCV.urgency * TCV.importance) AS urg_imp - FROM {$this->tables['testplan_tcversions']} TPTCV " . - - $sqlStm['getAssignedFeatures'] . - - " /* GO FOR Absolute LATEST exec ID On PLATFORM */ - JOIN ({$sqlLEBP}) AS LEBP + FROM {$this->tables['testplan_tcversions']} TPTCV " . + $sqlStm['getAssignedFeatures'] . + " /* GO FOR Absolute LATEST exec ID On PLATFORM */ + JOIN ({$sqlLEBP}) AS LEBP ON LEBP.testplan_id = TPTCV.testplan_id AND LEBP.platform_id = TPTCV.platform_id AND LEBP.tcversion_id = TPTCV.tcversion_id - AND LEBP.testplan_id = " . $safe_id . - - " /* Get execution status WRITTEN on DB */ + AND LEBP.testplan_id = " . $safe_id . + " /* Get execution status WRITTEN on DB */ JOIN {$this->tables['executions']} E - ON E.id = LEBP.id " . - - " /* getUserAssignment */ " . - $sqlStm['getUserAssignment']['exec'] . - - " /* Get Test Case info from Test Case Version */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.id = TPTCV.tcversion_id " . - - " /* Get Test Suite info from Test Case */ " . - " JOIN {$this->tables['nodes_hierarchy']} NHTC " . - " ON NHTC.id = NHTCV.parent_id " . - - " /* Get Test Case Version attributes */ " . - " JOIN {$this->tables['tcversions']} TCV " . - " ON TCV.id = E.tcversion_id " . - - " WHERE TPTCV.testplan_id={$safe_id} " . - " AND TPTCV.platform_id=" . intval($platform_id); - - return array($my,$builds,$sqlStm,$union); - } - - - /** - * - * getStatusTotalsTSuiteDepth2ForRender - * - */ - function getStatusTotalsTSuiteDepth2ForRender($id,$filters=null,$opt=null) - { - list($rx,$staircase) = - $this->getStatusTotalsByItemForRender($id,'tsuite',$filters,$opt); - - // ??? $key2loop = array_keys($rx->info); - $template = array('type' => 'tsuite', - 'name' => '', - 'parent_id' => 0, - 'total_tc' => 0, - 'percentage_completed' => 0, - 'details' => array()); - - foreach($this->statusCode as $verbose => $code) { - $template['details'][$verbose] = array('qty' => 0, 'percentage' => 0); - } - - $renderObj = new stdClass(); - $renderObj->colDefinition = $rx->colDefinition; - $renderObj->info = array(); - $renderObj->infoL2 = array(); - $renderObj->idNameMap = array(); - - // collect qty - $execQty = null; - $execQtyL2 = null; - - $key2loop = array_keys($staircase); - $wp = isset($opt['groupByPlatform']) && $opt['groupByPlatform']; - - if( $wp ) { - $tsNameCache = array(); - $plat2loop = array_keys($rx->info); - - // In order to get SUM() for each Top (Level 1) Test Suite - // using the specific test suite we get the Level 1 - // test suite of it's branch - // - foreach($key2loop as $tsuite_id) { - /* staircase - array(2) { - // ELEMENT #1 - [33934]=> (Test Suite -> Elenco agenda) - array(1) { - [0]=> string(5) "33933" (Test Project - Academy) - } - - // ELEMENT #2 - [49238]=> (Test Suite -> SOTTO Elenco agenda) - array(2) { - [0]=> string(5) "33933" (Test Project - Academy) - [1]=> string(5) "33934" (Test Suite -> Elenco agenda) - } - } - */ - - $tsuiteDepth = count($staircase[$tsuite_id]); - - $l2id = -1; - if ($tsuiteDepth > 1) { - // element at position 1 on starcaise - // is a TOP LEVEL SUITE - $topSuiteID = $staircase[$tsuite_id][1]; - $initName = false; - if ($tsuiteDepth > 2) { - $l2id = $staircase[$tsuite_id][2]; - $initNameL2 = false; - } else { - $l2id = $tsuite_id; - $initNameL2 = true; - } - } else { - $topSuiteID = $tsuite_id; - $initName = true; - } - - // Over Platform - foreach( $plat2loop as $platId ) { - - // Level 1 - if (!isset($renderObj->info[$platId][$topSuiteID])) { - $renderObj->info[$platId][$topSuiteID] = $template; - $execQty[$platId][$topSuiteID] = 0; - $initName = true; - } - - // Level 2 - if ($l2id > 0 && !isset($renderObj->infoL2[$platId][$l2id])) { - $renderObj->infoL2[$platId][$l2id] = $template; - $renderObj->infoL2[$platId][$l2id]['parent_id'] = $topSuiteID; - $execQtyL2[$platId][$l2id] = 0; - $initNameL2 = true; - } - - - if ($initName) { - if (!isset($tsNameCache[$topSuiteID])) { - $dummy = $this->tree_manager->get_node_hierarchy_info($topSuiteID); - $tsNameCache[$topSuiteID] = $dummy['name']; - unset($dummy); - } - $renderObj->info[$platId][$topSuiteID]['name'] = - $tsNameCache[$topSuiteID]; - } - - if ($l2id > 0 && $initNameL2) { - if (!isset($tsNameCache[$l2id])) { - // , array('l2CutFirst' => 10) - // $dummy = $this->tree_manager->getNameL2($l2id); - $tsNameCache[$l2id] = - $this->tree_manager->getNameL2($l2id); - } - $renderObj->infoL2[$platId][$l2id]['name'] = - $tsNameCache[$l2id]; - $renderObj->idNameMap[$l2id] = $tsNameCache[$l2id]; - } - - - - // Loop to get executions counters - $r2d2 = &$rx->info[$platId][$tsuite_id]; - if( null !== $r2d2 ) { - foreach($r2d2['details'] as $code => &$elem) { - $renderObj->info[$platId][$topSuiteID]['details'][$code] - ['qty'] += $elem['qty']; - $renderObj->info[$platId][$topSuiteID]['total_tc'] += - $elem['qty']; - - if ($l2id > 0) { - $renderObj->infoL2[$platId][$l2id]['details'][$code] - ['qty'] += $elem['qty']; - $renderObj->infoL2[$platId][$l2id]['total_tc'] += - $elem['qty']; - } - - if( $code != 'not_run' ) { - $execQty[$platId][$topSuiteID] += $elem['qty']; - if ($l2id > 0) { - $execQtyL2[$platId][$l2id] += $elem['qty']; - } - } - } - } - } - } - - // Last step: get percentages - foreach($renderObj->info as $platID => &$tsuiteMetrics) { - foreach($tsuiteMetrics as $tsuite_id => &$elem) { - if( $execQty[$platID][$tsuite_id] > 0 ) { - $elem['percentage_completed'] = number_format( 100 * - ($execQty[$platID][$tsuite_id] / $elem['total_tc']),1); - } - if( $elem['total_tc'] > 0 ) { - foreach($elem['details'] as $code => &$yumyum) { - $yumyum['percentage'] = number_format( 100 * ($yumyum['qty'] / $elem['total_tc']),1); - } - } - } - } - - // Level 2 - foreach($renderObj->infoL2 as $platID => &$tsuiteMetrics) { - foreach($tsuiteMetrics as $tsuite_id => &$elem) { - if( $execQtyL2[$platID][$tsuite_id] > 0 ) { - $elem['percentage_completed'] = number_format( 100 * - ($execQtyL2[$platID][$tsuite_id] / $elem['total_tc']),1); - } - if( $elem['total_tc'] > 0 ) { - foreach($elem['details'] as $code => &$yumyum) { - $yumyum['percentage'] = number_format( 100 * ($yumyum['qty'] / $elem['total_tc']),1); - } - } - } - } - - - - } - return $renderObj; - } - - - /** - * - * getExecTimelineStats - * - */ - function getExecTimelineStats($id,$filters=null,$opt=null) - { - $flt = array('yyyy_mm_dd' => null); - $flt = array_merge($flt,(array)$filters); - - // timeline - // day -> sum by date - // day_hour -> sum by date & hour - // month -> sum by month - // - $options = array('timeline' => 'day', 'workforce' => false); - $options = array_merge($options,(array)$opt); - - switch ($options['timeline']) { - case 'day_hour': - $fields = 'yyyy_mm_dd,hh'; - break; - - case 'month': - $fields = 'yyyy_mm'; - break; - - case 'day': - default: - $fields = 'yyyy_mm_dd'; - break; - } - - $safeID = intval($id); - $sqlX = " SELECT COUNT(0) as qty,{$fields},tester_id + ON E.id = LEBP.id " . " /* getUserAssignment */ " . + $sqlStm['getUserAssignment']['exec'] . + " /* Get Test Case info from Test Case Version */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.id = TPTCV.tcversion_id " . + " /* Get Test Suite info from Test Case */ " . + " JOIN {$this->tables['nodes_hierarchy']} NHTC " . + " ON NHTC.id = NHTCV.parent_id " . + " /* Get Test Case Version attributes */ " . + " JOIN {$this->tables['tcversions']} TCV " . + " ON TCV.id = E.tcversion_id " . + " WHERE TPTCV.testplan_id={$safe_id} " . " AND TPTCV.platform_id=" . + intval($platform_id); + + return array( + $my, + $builds, + $sqlStm, + $union + ); + } + + /** + * getStatusTotalsTSuiteDepth2ForRender + */ + public function getStatusTotalsTSuiteDepth2ForRender($id, $filters = null, + $opt = null) + { + list ($rx, $staircase) = $this->getStatusTotalsByItemForRender($id, + 'tsuite', $filters, $opt); + + // ??? $key2loop = array_keys($rx->info); + $template = array( + 'type' => 'tsuite', + 'name' => '', + 'parent_id' => 0, + 'total_tc' => 0, + 'percentage_completed' => 0, + 'details' => array() + ); + + foreach ($this->statusCode as $verbose => $code) { + $template['details'][$verbose] = array( + 'qty' => 0, + 'percentage' => 0 + ); + } + + $renderObj = new stdClass(); + $renderObj->colDefinition = $rx->colDefinition; + $renderObj->info = array(); + $renderObj->infoL2 = array(); + $renderObj->idNameMap = array(); + + // collect qty + $execQty = null; + $execQtyL2 = null; + + $key2loop = array_keys($staircase); + $wp = isset($opt['groupByPlatform']) && $opt['groupByPlatform']; + + if ($wp) { + $tsNameCache = array(); + $plat2loop = array_keys($rx->info); + + // In order to get SUM() for each Top (Level 1) Test Suite + // using the specific test suite we get the Level 1 + // test suite of it's branch + // + foreach ($key2loop as $tsuite_id) { + /* + * staircase + * array(2) { + * // ELEMENT #1 + * [33934]=> (Test Suite -> Elenco agenda) + * array(1) { + * [0]=> string(5) "33933" (Test Project - Academy) + * } + * + * // ELEMENT #2 + * [49238]=> (Test Suite -> SOTTO Elenco agenda) + * array(2) { + * [0]=> string(5) "33933" (Test Project - Academy) + * [1]=> string(5) "33934" (Test Suite -> Elenco agenda) + * } + * } + */ + + $tsuiteDepth = count($staircase[$tsuite_id]); + + $l2id = - 1; + if ($tsuiteDepth > 1) { + // element at position 1 on starcaise + // is a TOP LEVEL SUITE + $topSuiteID = $staircase[$tsuite_id][1]; + $initName = false; + if ($tsuiteDepth > 2) { + $l2id = $staircase[$tsuite_id][2]; + $initNameL2 = false; + } else { + $l2id = $tsuite_id; + $initNameL2 = true; + } + } else { + $topSuiteID = $tsuite_id; + $initName = true; + } + + // Over Platform + foreach ($plat2loop as $platId) { + + // Level 1 + if (! isset($renderObj->info[$platId][$topSuiteID])) { + $renderObj->info[$platId][$topSuiteID] = $template; + $execQty[$platId][$topSuiteID] = 0; + $initName = true; + } + + // Level 2 + if ($l2id > 0 && ! isset($renderObj->infoL2[$platId][$l2id])) { + $renderObj->infoL2[$platId][$l2id] = $template; + $renderObj->infoL2[$platId][$l2id]['parent_id'] = $topSuiteID; + $execQtyL2[$platId][$l2id] = 0; + $initNameL2 = true; + } + + if ($initName) { + if (! isset($tsNameCache[$topSuiteID])) { + $dummy = $this->tree_manager->get_node_hierarchy_info( + $topSuiteID); + $tsNameCache[$topSuiteID] = $dummy['name']; + unset($dummy); + } + $renderObj->info[$platId][$topSuiteID]['name'] = $tsNameCache[$topSuiteID]; + } + + if ($l2id > 0 && $initNameL2) { + if (! isset($tsNameCache[$l2id])) { + // , array('l2CutFirst' => 10) + // $dummy = $this->tree_manager->getNameL2($l2id); + $tsNameCache[$l2id] = $this->tree_manager->getNameL2( + $l2id); + } + $renderObj->infoL2[$platId][$l2id]['name'] = $tsNameCache[$l2id]; + $renderObj->idNameMap[$l2id] = $tsNameCache[$l2id]; + } + + // Loop to get executions counters + $r2d2 = &$rx->info[$platId][$tsuite_id]; + if (null !== $r2d2) { + foreach ($r2d2['details'] as $code => &$elem) { + $renderObj->info[$platId][$topSuiteID]['details'][$code]['qty'] += $elem['qty']; + $renderObj->info[$platId][$topSuiteID]['total_tc'] += $elem['qty']; + + if ($l2id > 0) { + $renderObj->infoL2[$platId][$l2id]['details'][$code]['qty'] += $elem['qty']; + $renderObj->infoL2[$platId][$l2id]['total_tc'] += $elem['qty']; + } + + if ($code != 'not_run') { + $execQty[$platId][$topSuiteID] += $elem['qty']; + if ($l2id > 0) { + $execQtyL2[$platId][$l2id] += $elem['qty']; + } + } + } + } + } + } + + // Last step: get percentages + foreach ($renderObj->info as $platID => &$tsuiteMetrics) { + foreach ($tsuiteMetrics as $tsuite_id => &$elem) { + if ($execQty[$platID][$tsuite_id] > 0) { + $elem['percentage_completed'] = number_format( + 100 * + ($execQty[$platID][$tsuite_id] / $elem['total_tc']), + 1); + } + if ($elem['total_tc'] > 0) { + foreach ($elem['details'] as &$yumyum) { + $yumyum['percentage'] = number_format( + 100 * ($yumyum['qty'] / $elem['total_tc']), 1); + } + } + } + } + + // Level 2 + foreach ($renderObj->infoL2 as $platID => &$tsuiteMetrics) { + foreach ($tsuiteMetrics as $tsuite_id => &$elem) { + if ($execQtyL2[$platID][$tsuite_id] > 0) { + $elem['percentage_completed'] = number_format( + 100 * + ($execQtyL2[$platID][$tsuite_id] / $elem['total_tc']), + 1); + } + if ($elem['total_tc'] > 0) { + foreach ($elem['details'] as &$yumyum) { + $yumyum['percentage'] = number_format( + 100 * ($yumyum['qty'] / $elem['total_tc']), 1); + } + } + } + } + } + return $renderObj; + } + + /** + * getExecTimelineStats + */ + public function getExecTimelineStats($id, $filters = null, $opt = null) + { + $flt = array( + 'yyyy_mm_dd' => null + ); + $flt = array_merge($flt, (array) $filters); + + // timeline + // day -> sum by date + // day_hour -> sum by date & hour + // month -> sum by month + // + $options = array( + 'timeline' => 'day', + 'workforce' => false + ); + $options = array_merge($options, (array) $opt); + + switch ($options['timeline']) { + case 'day_hour': + $fields = 'yyyy_mm_dd,hh'; + break; + + case 'month': + $fields = 'yyyy_mm'; + break; + + case 'day': + default: + $fields = 'yyyy_mm_dd'; + break; + } + + $safeID = intval($id); + $sqlX = " SELECT COUNT(0) as qty,{$fields},tester_id FROM {$this->views['exec_by_date_time']} EBDT WHERE EBDT.testplan_id = {$safeID} - GROUP BY {$fields},tester_id"; - $sqlA = str_ireplace(',tester_id','', $sqlX); - $sql = $sqlA . " ORDER BY {$fields}"; - - switch ($options['timeline']) { - case 'day_hour': - $rs = $this->db->fetchMapRowsIntoMap($sql,'yyyy_mm_dd','hh'); - break; - - case 'month': - $rs = $this->db->fetchRowsIntoMap($sql,'yyyy_mm'); - break; - - case 'day': - default: - $rs = $this->db->fetchRowsIntoMap($sql,'yyyy_mm_dd'); - break; - } - - $rswf = null; - if ($options['workforce']) { - $sqlwf = " SELECT COUNT(0) AS testers, {$fields} - FROM ($sqlX) SQLBASE - GROUP BY {$fields}"; - - switch ($options['timeline']) { - case 'day_hour': - $rswf = $this->db->fetchMapRowsIntoMap($sqlwf,'yyyy_mm_dd','hh'); - break; - - case 'month': - $rswf = $this->db->fetchRowsIntoMap($sqlwf,'yyyy_mm'); - break; - - case 'day': - default: - $rswf = $this->db->fetchRowsIntoMap($sqlwf,'yyyy_mm_dd'); - break; - } - - foreach ($rswf as $rt => $elem) { - $rs[$rt]['testers'] = $elem['testers']; - } - } - - return array($rs,$rswf); - } - - /** - * get execution time span for context: - * Test Plan - * Platform (if any) - * Build - * - * - */ - function getExecTimeSpan($id,$context) { - - $fieldList .= implode(',', $context); - - $sql = "SELECT MIN(execution_ts) AS begin, - MAX(execution_ts) AS end, {$fieldList} - FROM {$this->tables['executions']} - WHERE testplan_id = $id - GROUP BY {$fieldList}"; - - $levels = count($context); - switch ($levels) { - case 1: - $rs = $this->db->fetchRowsIntoMap($sql,'testplan_id'); - break; - - case 2: - $rs = $this->db->fetchRowsIntoMap2l($sql,$context); - break; - - case 3: - $rs = $this->db->fetchRowsIntoMap3l($sql,$context); - break; - } - - return $rs; - } - - -} \ No newline at end of file + GROUP BY {$fields},tester_id"; + $sqlA = str_ireplace(',tester_id', '', $sqlX); + $sql = $sqlA . " ORDER BY {$fields}"; + + switch ($options['timeline']) { + case 'day_hour': + $rs = $this->db->fetchMapRowsIntoMap($sql, 'yyyy_mm_dd', 'hh'); + break; + + case 'month': + $rs = $this->db->fetchRowsIntoMap($sql, 'yyyy_mm'); + break; + + case 'day': + default: + $rs = $this->db->fetchRowsIntoMap($sql, 'yyyy_mm_dd'); + break; + } + + $rswf = null; + if ($options['workforce']) { + $sqlwf = " SELECT COUNT(0) AS testers, {$fields} + FROM ($sqlX) SQLBASE + GROUP BY {$fields}"; + + switch ($options['timeline']) { + case 'day_hour': + $rswf = $this->db->fetchMapRowsIntoMap($sqlwf, 'yyyy_mm_dd', + 'hh'); + break; + + case 'month': + $rswf = $this->db->fetchRowsIntoMap($sqlwf, 'yyyy_mm'); + break; + + case 'day': + default: + $rswf = $this->db->fetchRowsIntoMap($sqlwf, 'yyyy_mm_dd'); + break; + } + + foreach ($rswf as $rt => $elem) { + $rs[$rt]['testers'] = $elem['testers']; + } + } + + return array( + $rs, + $rswf + ); + } + + /** + * get execution time span for context: + * Test Plan + * Platform (if any) + * Build + */ + public function getExecTimeSpan($id, $context) + { + $fieldList = implode(',', $context); + $sql = "SELECT MIN(execution_ts) AS begin, MAX(execution_ts) AS end, {$fieldList}" . + " FROM {$this->tables['executions']} WHERE testplan_id = $id " . + " GROUP BY {$fieldList}"; + + $levels = count($context); + switch ($levels) { + case 1: + $rs = $this->db->fetchRowsIntoMap($sql, 'testplan_id'); + break; + + case 2: + $rs = $this->db->fetchRowsIntoMap2l($sql, $context); + break; + + case 3: + $rs = $this->db->fetchRowsIntoMap3l($sql, $context); + break; + } + + return $rs; + } +} diff --git a/lib/functions/tlUser.class.php b/lib/functions/tlUser.class.php index fc6f8959db..d2bfc31295 100644 --- a/lib/functions/tlUser.class.php +++ b/lib/functions/tlUser.class.php @@ -1,1525 +1,1574 @@ -object_table = $this->tables['users']; - - $authCfg = config_get('authentication'); - $this->usernameFormat = config_get('username_format'); - $this->loginRegExp = config_get('validation_cfg')->user_login_valid_regex; - $this->maxLoginLength = 100; - $this->loginMethod = $authCfg['method']; - - $this->globalRoleID = config_get('default_roleid'); - $this->locale = config_get('default_language'); - $this->isActive = 1; - $this->tprojectRoles = null; - $this->tplanRoles = null; - } - - /** - * Cleans the object by resetting the members to default values - * - * @param mixed $options tlUser/tlObject options - */ - protected function _clean($options = self::TLOBJ_O_SEARCH_BY_ID) { - $this->firstName = null; - $this->lastName = null; - $this->locale = null; - $this->password = null; - $this->isActive = null; - $this->defaultTestprojectID = null; - $this->globalRoleID = null; - $this->tprojectRoles = null; - $this->tplanRoles = null; - $this->userApiKey = null; - $this->securityCookie = null; - $this->authentication = null; - $this->expiration_date = null; - - if (!($options & self::TLOBJ_O_SEARCH_BY_ID)) { - $this->dbID = null; - } - - if (!($options & self::USER_O_SEARCH_BYLOGIN)) { - $this->login = null; - } - - if (!($options & self::USER_O_SEARCH_BYEMAIL)) { - $this->emailAddress = null; - } - - - } - - /** - * Checks if password management is external (like LDAP)... - * - * @param string $method2check must be one of the keys of configuration $tlCfg->authentication['domain'] - * - * @return boolean return true if password management is external, else false - */ - static public function isPasswordMgtExternal($method2check=null) - { - $target = $method2check; - - // Contains Domain and Default Method - $authCfg = config_get('authentication'); - - if( is_null($target) || $target=='') - { - $target = $authCfg['method']; - } - - $ret = true; - if( isset($authCfg['domain'][$target]) ) - { - $ret = !$authCfg['domain'][$target]['allowPasswordManagement']; - } - return $ret; - } - - /** - * Obtain a secure password. - * You can choose the number of alphanumeric characters to add and - * the number of non-alphanumeric characters. - * You can add another characters to the non-alphanumeric list if you need. - * - * @param integer $numAlpha number alphanumeric characters in generated password - * @param integer $numNonAlpha number special characters in generated password - * - * @return string the generated password - */ - static public function generatePassword($numAlpha = 6,$numNonAlpha = 2) - { - $listAlpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - $listNonAlpha = ',;:!?.$/*-+&@_+;./*&?$-!,'; - - return str_shuffle(substr(str_shuffle($listAlpha),0,$numAlpha) . - substr(str_shuffle($listNonAlpha),0,$numNonAlpha)); - } - - /** - * not used at the moment, only placeholder - * - * @return void - * @TODO implement - **/ - function create() - { - } - - //----- BEGIN interface iDBSerialization ----- - /** - * Reads an user object identified by its database id from the given database - * - * @param resource &$db reference to database handler - * @param mixed $options (optional) tlUser/tlObject options - * - * @return integer tl::OK if the object could be read from the db, else tl::ERROR - */ - public function readFromDB(&$db,$options = self::TLOBJ_O_SEARCH_BY_ID) { - $this->_clean($options); - $sql = " SELECT id,login,password,cookie_string,first,last,email," . - " role_id,locale, " . - " login AS fullname, active,default_testproject_id, script_key,auth_method,creation_ts,expiration_date " . - " FROM {$this->object_table}"; - $clauses = null; - - if ($options & self::TLOBJ_O_SEARCH_BY_ID) { - $clauses[] = "id = " . intval($this->dbID); - } - - if ($options & self::USER_O_SEARCH_BYLOGIN) { - $clauses[] = "login = '".$db->prepare_string($this->login)."'"; - } - - if ($options & self::USER_O_SEARCH_BYEMAIL) { - $clauses[] = "email = '".$db->prepare_string($this->emailAddress)."'"; - } - - if ($clauses) { - $sql .= " WHERE " . implode(" AND ",$clauses); - } - $info = $db->fetchFirstRow($sql); - if ($info) { - $this->dbID = $info['id']; - $this->firstName = $info['first']; - $this->lastName = $info['last']; - $this->login = $info['login']; - $this->emailAddress = $info['email']; - $this->globalRoleID = $info['role_id']; - $this->userApiKey = $info['script_key']; - $this->securityCookie = $info['cookie_string']; - $this->authentication = $info['auth_method']; - $this->expiration_date = $info['expiration_date']; - $this->creation_ts = $info['creation_ts']; - - if ($this->globalRoleID) { - $this->globalRole = new tlRole($this->globalRoleID); - $this->globalRole->readFromDB($db); - } - - if ($this->detailLevel & self::TLOBJ_O_GET_DETAIL_ROLES) { - $this->readTestProjectRoles($db); - $this->readTestPlanRoles($db); - } - - $this->locale = $info['locale']; - $this->password = $info['password']; - $this->isActive = $info['active']; - $this->defaultTestprojectID = $info['default_testproject_id']; - } - return $info ? tl::OK : tl::ERROR; - } - - /** - * Fetches all the testproject roles of of the user, and store them into the object. - * Result could be limited to a certain testproject - * - * @param resource &$db reference to database handler - * @param integer $testProjectID Identifier of the testproject to read the roles for, - * if null all roles are read - * - * @return integer returns tl::OK - */ - public function readTestProjectRoles(&$db,$testProjectID = null) { - $sql = "SELECT testproject_id,role_id " . - " FROM {$this->tables['user_testproject_roles']} user_testproject_roles " . - " WHERE user_id = " . intval($this->dbID); - - if ($testProjectID) { - $sql .= " AND testproject_id = " . intval($testProjectID); - } - $allRoles = $db->fetchColumnsIntoMap($sql,'testproject_id','role_id'); - $this->tprojectRoles = null; - if (null != $allRoles && sizeof($allRoles)) { - $roleCache = null; - foreach($allRoles as $tprojectID => $roleID) { - if (!isset($roleCache[$roleID])) { - $tprojectRole = tlRole::createObjectFromDB($db,$roleID,"tlRole",true); - $roleCache[$roleID] = $tprojectRole; - } else { - $tprojectRole = clone($roleCache[$roleID]); - } - - if ($tprojectRole) { - $this->tprojectRoles[$tprojectID] = $tprojectRole; - } - } - } - return tl::OK; - } - - /** - * Fetches all the testplan roles of of the user, and store them into the object. - * Result could be limited to a certain testplan - * - * @param resource &$db reference to database handler - * @param integer $testPlanID Identifier of the testplan to read the roles for, if null all roles are read - * - * @return integer returns tl::OK - */ - public function readTestPlanRoles(&$db,$testPlanID = null) { - $sql = "SELECT testplan_id,role_id " . - " FROM {$this->tables['user_testplan_roles']} user_testplan_roles " . - " WHERE user_id = " . intval($this->dbID); - if ($testPlanID) { - $sql .= " AND testplan_id = " . intval($testPlanID); - } - - $allRoles = $db->fetchColumnsIntoMap($sql,'testplan_id','role_id'); - $this->tplanRoles = null; - if (null != $allRoles && sizeof($allRoles)) { - $roleCache = null; - foreach($allRoles as $tplanID => $roleID) { - if (!isset($roleCache[$roleID])) { - $tplanRole = tlRole::createObjectFromDB($db,$roleID,"tlRole",true); - $roleCache[$roleID] = $tplanRole; - } else { - $tplanRole = clone($roleCache[$roleID]); - } - - if ($tplanRole) { - $this->tplanRoles[$tplanID] = $tplanRole; - } - } - } - return tl::OK; - } - - /** - * Writes the object into the database - * - * @param resource &$db reference to database handler - * @return integer tl::OK if the object could be written to the db, else error code - */ - public function writeToDB(&$db) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $result = $this->checkDetails($db); - if ($result >= tl::OK) - { - $t_cookie_string = $this->auth_generate_unique_cookie_string($db); - - // After addition of cookie_string, and following Mantisbt pattern, - // seems we need to check if password has changed. - // - // IMPORTANT NOTICE: - // this implementation works ONLY when password is under TestLink control - // i.e. is present on TestLink Database. - // - // if answer is yes => change also cookie_string. - if($this->dbID) - { - $gsql = " /* debugMsg */ SELECT password FROM {$this->object_table} WHERE id = " . $this->dbID; - $rs = $db->get_recordset($gsql); - if(strcmp($rs[0]['password'],$this->password) == 0) - { - // NO password change - $t_cookie_string = null; - } - - $sql = "/* debugMsg */ UPDATE {$this->tables['users']} " . - " SET first = '" . $db->prepare_string($this->firstName) . "'" . - ", last = '" . $db->prepare_string($this->lastName) . "'" . - ", email = '" . $db->prepare_string($this->emailAddress) . "'" . - ", locale = ". "'" . $db->prepare_string($this->locale) . "'" . - ", password = " . "'" . $db->prepare_string($this->password) . "'" . - ", role_id = ". $db->prepare_int($this->globalRoleID) . - ", active = ". $db->prepare_string($this->isActive) . - ", auth_method = ". "'" . $db->prepare_string($this->authentication) . "'"; - - if(!is_null($t_cookie_string) ) - { - $sql .= ", cookie_string = " . "'" . $db->prepare_string($t_cookie_string) . "'"; - } - $sql .= " WHERE id = " . intval($this->dbID); - $result = $db->exec_query($sql); - } - else - { - $sql = "/* debugMsg */ INSERT INTO {$this->tables['users']} " . - " (login,password,cookie_string,first,last,email,role_id,locale,active,auth_method) " . - " VALUES ('" . - $db->prepare_string($this->login) . "','" . $db->prepare_string($this->password) . "','" . - $db->prepare_string($t_cookie_string) . "','" . - $db->prepare_string($this->firstName) . "','" . $db->prepare_string($this->lastName) . "','" . - $db->prepare_string($this->emailAddress) . "'," . $db->prepare_int($this->globalRoleID) . ",'". - $db->prepare_string($this->locale). "'," . $this->isActive . "," . - "'" . $db->prepare_string($this->authentication). "'" . ")"; - - $result = $db->exec_query($sql); - if($result) - { - $this->dbID = $db->insert_id($this->tables['users']); - } - } - $result = $result ? tl::OK : self::E_DBERROR; - } - return $result; - } - - /** - * WARNING: DO NOT USE THE FUNCTION - CAUSES DB INCONSISTENCE! - * - * @deprecated 1.8.3 - * @see #2407 - **/ - public function deleteFromDB(&$db) - { - $safeUserID = intval($this->dbID); - $sqlSet = array(); - $sqlSet[] = "DELETE FROM {$this->table['user_assignments']} WHERE user_id = {$safeUserID}"; - $sqlSet[] = "DELETE FROM {$this->table['users']} WHERE id = {$safeUserID}"; - - foreach($sqlSet as $sql) - { - $result = $db->exec_query($sql) ? tl::OK : tl::ERROR; - if($result == tl::ERROR) - { - break; - } - } - - if ($result == tl::OK) - { - $result = $this->deleteTestProjectRoles($db); - } - return $result; - } - - /** - * Deletes all testproject related role assignments for a given user - * - * @param resource &$db reference to database handler - * @param integer $userID the user ID - * - * @return integer tl::OK on success, tl:ERROR else - **/ - protected function deleteTestProjectRoles(&$db) - { - $sql = "DELETE FROM {$this->tables['user_testproject_roles']} WHERE user_id = " . intval($this->dbID); - return $db->exec_query($sql) ? tl::OK : tl::ERROR; - } - - /** - * Returns a user friendly representation of the user name - * - * @return string the display nmae - */ - public function getDisplayName($format=null) - { - $keys = array('%first%','%last%','%login%','%email%'); - $values = array($this->firstName, $this->lastName,$this->login,$this->emailAddress); - - $fmt = is_null($format) ? $this->usernameFormat : $format; - $displayName = trim(str_replace($keys,$values,$fmt)); - - return $displayName; - } - - /** - * Encrypts a given password with MD5 - * - * @param $pwd the password to encrypt - * @return string the encrypted password - */ - protected function encryptPassword($pwd,$authentication=null) - { - if (self::isPasswordMgtExternal($authentication)) { - return self::S_PWDMGTEXTERNAL; - } - - return password_hash($pwd,PASSWORD_DEFAULT); - } - - /** - * Set encrypted password - * - * @param string $pwd the new password - * @return integer return tl::OK is the password is stored, else errorcode - */ - public function setPassword($pwd,$authentication=null) - { - if (self::isPasswordMgtExternal($authentication)) - { - return self::S_PWDMGTEXTERNAL; - } - $pwd = trim($pwd); - if ($pwd == "") { - return self::E_PWDEMPTY; - } - $this->password = $this->encryptPassword($pwd,$authentication); - return tl::OK; - } - - /** - * Getter for the password of the user - * - * @return string the password of the user - */ - public function getPassword() - { - return $this->password; - } - - /** - * compares a given password with the current password of the user - * - * @param string $pwd the password to compate with the password actually set - * @return integer returns tl::OK if the password's match, else errorcode - */ - public function comparePassword(&$dbH,$pwd) - { - if (self::isPasswordMgtExternal($this->authentication)) { - return self::S_PWDMGTEXTERNAL; - } - - // If we are here this means that we are using - // internal password management. - // - // Manage migration from MD5 - // MD5 hash check - // This is valid ONLY for internal password management - $encriptedPWD = $this->getPassword(); - if (strlen($encriptedPWD) == 32) { - /* Update the old MD5 hash to the new bcrypt */ - if ($encriptedPWD == md5($pwd)) { - $this->password = $this->encryptPassword($pwd,$this->authentication); - $this->writePasswordToDB($dbH); - return tl::OK; - } - } - - if (password_verify($pwd,$encriptedPWD)) { - return tl::OK; - } - - return self::E_PWDDONTMATCH; - } - - - /** - * - */ - public function checkDetails(&$db) { - $this->firstName = trim($this->firstName); - $this->lastName = trim($this->lastName); - $this->emailAddress = trim($this->emailAddress); - $this->locale = trim($this->locale); - $this->isActive = intval($this->isActive); - $this->login = trim($this->login); - - $result = self::checkEmailAddress($this->emailAddress); - if ($result >= tl::OK) - { - $result = $this->checkLogin($this->login); - } - if ($result >= tl::OK && !$this->dbID) - { - $result = self::doesUserExist($db,$this->login) ? self::E_LOGINALREADYEXISTS : tl::OK; - } - if ($result >= tl::OK) - { - $result = self::checkFirstName($this->firstName); - } - if ($result >= tl::OK) - { - $result = self::checkLastName($this->lastName); - } - return $result; - } - - - public function checkLogin($login) - { - $result = tl::OK; - $login = trim($login); - - if ($login == "" || (tlStringLen($login) > $this->maxLoginLength)) - { - $result = self::E_LOGINLENGTH; - } - else if (!preg_match($this->loginRegExp,$login)) - { - //Only allow a basic set of characters - $result = self::E_NOTALLOWED; - } - return $result; - } - - /** - * Returns the id of the effective role in the context of ($tproject_id,$tplan_id) - * - * @param resource &$db reference to database handler - * @param integer $tproject_id the testproject id - * @param integer $tplan_id the plan id - * - * @return integer tlRole the effective role - */ - function getEffectiveRole(&$db,$tproject_id,$tplan_id) - { - $tprojects_role = $this->tprojectRoles; - $tplans_role = $this->tplanRoles; - $effective_role = $this->globalRole; - - if(!is_null($tplans_role) && isset($tplans_role[$tplan_id])) { - $effective_role = $tplans_role[$tplan_id]; - } - else if(!is_null($tprojects_role) && isset($tprojects_role[$tproject_id])) { - $effective_role = $tprojects_role[$tproject_id]; - } - return $effective_role; - } - - /** - * Gets all userids of users with a certain testplan role @TODO WRITE RIGHT COMMENTS FROM START - * - * @param resource &$db reference to database handler - * @return array returns array of userids - **/ - protected function getUserNamesWithTestPlanRole(&$db) - { - $sql = "SELECT DISTINCT id FROM {$this->tables['users']} users," . - " {$this->tables['user_testplan_roles']} user_testplan_roles " . - " WHERE users.id = user_testplan_roles.user_id"; - $sql .= " AND user_testplan_roles.role_id = " . intval($this->dbID); - $idSet = $db->fetchColumnsIntoArray($sql,"id"); - - return $idSet; - } - - - /** - * Get a list of names with a defined project right (for example for combo-box) - * used by ajax script getUsersWithRight.php - * - * @param integer $db DB Identifier - * @param string $rightNick key corresponding with description in rights table - * @param integer $testprojectID Identifier of project - * - * @return array list of user IDs and names - * - * @todo fix the case that user has default role with a right but project role without - * i.e. he should be listed - */ - public function getNamesForProjectRight(&$db,$rightNick,$testprojectID = null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - if (is_null($testprojectID)) - { - tLog( $debugMsg . ' requires Test Project ID defined','ERROR'); - return null; - } - - $output = array(); - - //get users for default roles - $sql = "/* $debugMsg */ SELECT DISTINCT u.id,u.login,u.first,u.last FROM {$this->tables['users']} u" . - " JOIN {$this->tables['role_rights']} a ON a.role_id=u.role_id" . - " JOIN {$this->tables['rights']} b ON a.right_id = b.id " . - " WHERE b.description='" . $db->prepare_string($rightNick) . "'"; - $defaultRoles = $db->fetchRowsIntoMap($sql,'id'); - - // get users for project roles - $sql = "/* $debugMsg */ SELECT DISTINCT u.id,u.login,u.first,u.last FROM {$this->tables['users']} u" . - " JOIN {$this->tables['user_testproject_roles']} p ON p.user_id=u.id" . - " AND p.testproject_id=" . intval($testprojectID) . - " JOIN {$this->tables['role_rights']} a ON a.role_id=p.role_id" . - " JOIN {$this->tables['rights']} b ON a.right_id = b.id " . - " WHERE b.description='" . $db->prepare_string($rightNick) . "'"; - $projectRoles = $db->fetchRowsIntoMap($sql,'id'); - - // merge arrays - // the next function is available from php53 but we support php52 - // $output = array_replace($output1, $output2); - if( !is_null($projectRoles) ) - { - foreach($projectRoles as $k => $v) - { - if( !isset($defaultRoles[$k]) ) - { - $defaultRoles[$k] = $v; - } - } - } - - // format for ext-js combo-box (remove associated array) - // foreach($defaultRoles as $k => $v) - // { - // $output[] = $v; - // } - $output = array_values($defaultRoles); - - return $output; - } - - - /** - * Get a list of all names - * used for replacement user ID by user login - * - * @param integer $db DB Identifier - * @return array list of user IDs and names - */ - public function getNames(&$db,$idSet=null) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $sql = " SELECT id,login,first,last FROM {$this->tables['users']}"; - - $inClause = ''; - if( !is_null($idSet) ) - { - $inClause = " WHERE id IN (" . implode(',',(array)$idSet) . ") "; - } - - $output = $db->fetchRowsIntoMap($sql . $inClause,'id'); - return $output; - } - - - /** - * check right on effective role for user, using test project and test plan, - * means that check right on effective role. - * - * @return string|null 'yes' or null - * - * @internal revisions - */ - function hasRight(&$db,$roleQuestion,$tprojectID = null,$tplanID = null,$getAccess=false) - { - global $g_propRights_global; - global $g_propRights_product; - - $testprojectID = 0; - $testPlanID = 0; - - if (!is_null($tplanID)) { - $testPlanID = $tplanID; - } - - if (!is_null($tprojectID)) { - $testprojectID = $tprojectID; - } - - $accessPublic = null; - if ($getAccess) { - if($testprojectID > 0) { - $mgr = new testproject($db); - $accessPublic['tproject'] = $mgr->getPublicAttr($testprojectID); - unset($mgr); - } - - if($testPlanID > 0) { - $mgr = new testplan($db); - $accessPublic['tplan'] = $mgr->getPublicAttr($testPlanID); - unset($mgr); - } - } - - $userGlobalRights = (array)$this->globalRole->rights; - - $globalRights = array(); - foreach($userGlobalRights as $right) { - $globalRights[] = $right->name; - } - $allRights = $globalRights; - - $userTestProjectRoles = $this->tprojectRoles; - $userTestPlanRoles = $this->tplanRoles; - - if (isset($userTestProjectRoles[$testprojectID])) { - $userTestProjectRights = (array)$userTestProjectRoles[$testprojectID]->rights; - - // Special situation => just one right - $doMoreAnalysis = true; - if( count($userTestProjectRights) == 1) { - $doMoreAnalysis = !is_null($userTestProjectRights[0]->dbID); - } - - $allRights = null; - if( $doMoreAnalysis ) { - $testProjectRights = array(); - foreach($userTestProjectRights as $right) { - $testProjectRights[] = $right->name; - } - - // subtract global rights - $testProjectRights = array_diff($testProjectRights,array_keys($g_propRights_global)); - propagateRights($globalRights,$g_propRights_global,$testProjectRights); - $allRights = $testProjectRights; - } else { - return false; - } - } else { - if(!is_null($accessPublic) && $accessPublic['tproject'] == 0) { - return false; - } - } - - if( $testPlanID > 0) { - if (isset($userTestPlanRoles[$testPlanID])) { - $userTestPlanRights = (array) $userTestPlanRoles[$testPlanID]->rights; - $testPlanRights = array(); - foreach($userTestPlanRights as $right) { - $testPlanRights[] = $right->name; - } - - //subtract test projects rights - $testPlanRights = array_diff($testPlanRights,array_keys($g_propRights_product)); - - propagateRights($allRights,$g_propRights_product,$testPlanRights); - $allRights = $testPlanRights; - } else { - if(!is_null($accessPublic) && $accessPublic['tplan'] == 0) { - return false; - } - } - } - - $what = checkForRights($allRights,$roleQuestion); - - return $what; - } - - /** - * get array with accessible test plans for user on a test project, - * analising user roles. - * - * @param resource $db database handler - * @param int testprojectID - * @param int testplanID: default null. - * Used as filter when you want to check if this test plan - * is accessible. - * - * @param map options keys : - * 'output' => null -> get numeric array - * => map => map indexed by testplan id - * => combo => map indexed by testplan id and only returns name - * 'active' => ACTIVE (get active test plans) - * => INACTIVE (get inactive test plans) - * => TP_ALL_STATUS (get all test plans) - * - * @return array if 0 accessible test plans => null - * - * @internal revisions - * - */ - function getAccessibleTestPlans(&$db,$testprojectID,$testplanID=null, $options=null) { - $debugTag = 'Class:' . __CLASS__ . '- Method:' . __FUNCTION__ . '-'; - - $my['options'] = array( 'output' => null, 'active' => ACTIVE); - $my['options'] = array_merge($my['options'], (array)$options); - - $fields2get = ' NH.id, NH.name, TPLAN.is_public, ' . - ' COALESCE(USER_TPLAN_ROLES.testplan_id,0) AS has_role, ' . - ' USER_TPLAN_ROLES.role_id AS user_testplan_role, TPLAN.active, 0 AS selected '; - - if( $my['options']['output'] == 'mapfull' ) { - $fields2get .= ' ,TPLAN.notes, TPLAN.testproject_id '; - } - - $sql = " /* $debugTag */ SELECT {$fields2get} " . - " FROM {$this->tables['nodes_hierarchy']} NH" . - " JOIN {$this->tables['testplans']} TPLAN ON NH.id=TPLAN.id " . - " LEFT OUTER JOIN {$this->tables['user_testplan_roles']} USER_TPLAN_ROLES" . - " ON TPLAN.id = USER_TPLAN_ROLES.testplan_id " . - " AND USER_TPLAN_ROLES.user_id = " . intval($this->dbID); - - - // Construct where sentence - $where = " WHERE testproject_id = " . intval($testprojectID); - if (!is_null($my['options']['active'])) { - $where .= " AND active = {$my['options']['active']}"; - } - - if (!is_null($testplanID)) { - $where .= " AND NH.id = " . intval($testplanID); - } - - $analyseGlobalRole = 1; - $userGlobalRoleIsNoRights = ($this->globalRoleID == TL_ROLES_NO_RIGHTS); - - // Role at Test Project level is defined? - $userProjectRoleIsNoRights = 0; - if( isset($this->tprojectRoles[$testprojectID]->dbID) ) { - $userProjectRoleIsNoRights = - ($this->tprojectRoles[$testprojectID]->dbID == TL_ROLES_NO_RIGHTS); - } - - // according to new configuration option - // - // testplan_role_inheritance_mode - // - // this logic will be different - $joins = ''; - switch ( config_get('testplan_role_inheritance_mode') ) { - - case 'testproject': - // If user has a role for $testprojectID, then we DO NOT HAVE - // to check for globalRole - if( isset($this->tprojectRoles[$testprojectID]->dbID) ) { - $analyseGlobalRole = 0; - } - - // User can have NO RIGHT on test project under analisys ($testprojectID), - // in this situation he/she - // has to have a role at Test Plan level in order to access one or more test plans - // that belong to $testprojectID. - // - // Other situation: he/she has been created with role without rights ($globalNoRights) - // - if( $userProjectRoleIsNoRights || - ($analyseGlobalRole && $userGlobalRoleIsNoRights) ) { - // In order to access he/she needs specific configuration. - $where .= " AND (USER_TPLAN_ROLES.role_id IS NOT NULL AND "; - } - else { - // in this situation: - // We can use what we have inherited from test project - // OR - // We can use specific test plan role if defined - $where .= " AND (USER_TPLAN_ROLES.role_id IS NULL OR "; - } - $where .= " USER_TPLAN_ROLES.role_id != " . TL_ROLES_NO_RIGHTS .")"; - - break; - - - case 'global': - - // Because inheritance is from GLOBAL Role, do not need to care - // about existence of specific role defined AT TEST PROJECT LEVEL - - // If User has NO RIGHTS at GLOBAL Level he/she need specific - // on test plan - if( $userGlobalRoleIsNoRights ) { - // In order to access he/she needs specific configuration. - $where .= " AND (USER_TPLAN_ROLES.role_id IS NOT NULL AND "; - } - else { - // in this situation: - // We can use what we have inherited from GLOBAL - // - // OR - // We can use specific test plan role if defined - $where .= " AND (USER_TPLAN_ROLES.role_id IS NULL OR "; - } - $where .= " USER_TPLAN_ROLES.role_id != " . TL_ROLES_NO_RIGHTS .")"; - break; - } - - $sql .= $joins . $where; - - $sql .= " ORDER BY name"; - $numericIndex = false; - switch($my['options']['output']) { - case 'map': - case 'mapfull': - $testPlanSet = $db->fetchRowsIntoMap($sql,'id'); - break; - - case 'combo': - $testPlanSet = $db->fetchRowsIntoMap($sql,'id'); - break; - - default: - $testPlanSet = $db->get_recordset($sql); - $numericIndex = true; - break; - } - - // Admin exception - $doReindex = false; - if( $this->globalRoleID != TL_ROLES_ADMIN && null != $testPlanSet - && count($testPlanSet) > 0 ) { - foreach($testPlanSet as $idx => $item) { - if( $item['is_public'] == 0 && $item['has_role'] == 0 ) { - unset($testPlanSet[$idx]); - $doReindex = true; - } - } - } - - if($my['options']['output'] == 'combo') { - $dummy = array(); - foreach($testPlanSet as $idx => $item) { - $dummy[$idx] = $item['name']; - } - $testPlanSet = $dummy; - } - if( $doReindex && $numericIndex) - { - $testPlanSet = array_values($testPlanSet); - } - return $testPlanSet; - } - - - /** - * Checks the correctness of an email address - * - * @param string $email - * @return integer returns tl::OK on success, errorcode else - */ - static public function checkEmailAddress($email) - { - $result = is_blank($email) ? self::E_EMAILLENGTH : tl::OK; - if ($result == tl::OK) - { - $matches = array(); - $email_regex = config_get('validation_cfg')->user_email_valid_regex_php; - if (!preg_match($email_regex,$email,$matches)) - { - $result = self::E_EMAILFORMAT; - } - } - return $result; - } - - static public function checkFirstName($first) - { - return is_blank($first) ? self::E_FIRSTNAMELENGTH : tl::OK; - } - - static public function checkLastName($last) - { - return is_blank($last) ? self::E_LASTNAMELENGTH : tl::OK; - } - - /** - * - */ - static public function doesUserExist(&$db,$login) - { - $user = new tlUser(); - $user->login = $login; - if ($user->readFromDB($db,self::USER_O_SEARCH_BYLOGIN) >= tl::OK) { - return $user->dbID; - } - return null; - } - - /** - * - */ - static public function doesUserExistByEmail(&$db,$email) { - $user = new tlUser(); - $user->emailAddress = $email; - if ($user->readFromDB($db,self::USER_O_SEARCH_BYEMAIL) >= tl::OK) { - return $user->dbID; - } - return null; - } - - - /** - * - */ - static public function getByID(&$db,$id,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) { - return tlDBObject::createObjectFromDB($db,$id,__CLASS__,self::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - } - - - /** - * - */ - static public function getByIDs(&$db,$ids,$detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) { - $users = null; - - if( null == $ids ) { - return null; - } - - for($idx = 0;$idx < sizeof($ids);$idx++) { - $id = $ids[$idx]; - $user = tlDBObject::createObjectFromDB($db,$id,__CLASS__,self::TLOBJ_O_SEARCH_BY_ID,$detailLevel); - if ($user) { - $users[$id] = $user; - } - } - return $users ? $users : null; - } - - static public function getAll(&$db,$whereClause = null,$column = null,$orderBy = null, - $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) - { - $tables = tlObject::getDBTables('users'); - $sql = " SELECT id FROM {$tables['users']} "; - if (!is_null($whereClause)) - { - $sql .= ' '.$whereClause; - } - $sql .= is_null($orderBy) ? " ORDER BY login " : $orderBy; - - return tlDBObject::createObjectsFromDBbySQL($db,$sql,'id',__CLASS__,true,$detailLevel); - } - - /** - */ - public function setActive(&$db,$value) - { - $booleanVal = intval($value) > 0 ? 1 : 0; - $sql = " UPDATE {$this->tables['users']} SET active = {$booleanVal} " . - " WHERE id = " . intval($this->dbID); - $result = $db->exec_query($sql); - return tl::OK; - } - - - /** - * Writes user password into the database - * - * @param resource &$db reference to database handler - * @return integer tl::OK if no problem written to the db, else error code - * - * (ideas regarding cookie_string -> from Mantisbt). - * - * @internal revisions - */ - public function writePasswordToDB(&$db) - { - if($this->dbID) - { - // After addition of cookie_string, and following Mantisbt pattern, - // seems we need to check if password has changed. - // - // IMPORTANT NOTICE: - // this implementation works ONLY when password is under TestLink control - // i.e. is present on TestLink Database. - // - // if answer is yes => change also cookie_string. - $t_cookie_string = null; - - $gsql = " SELECT password FROM {$this->object_table} WHERE id = " . intval($this->dbID); - $rs = $db->get_recordset($gsql); - if(strcmp($rs[0]['password'],$this->password) != 0) - { - // Password HAS CHANGED - $t_cookie_string = $this->auth_generate_unique_cookie_string($db); - } - - $sql = "UPDATE {$this->tables['users']} " . - " SET password = ". "'" . $db->prepare_string($this->password) . "'"; - - if(!is_null($t_cookie_string) ) - { - $sql .= ", cookie_string = " . "'" . $db->prepare_string($t_cookie_string) . "'"; - } - $sql .= " WHERE id = " . intval($this->dbID); - $result = $db->exec_query($sql); - } - $result = $result ? tl::OK : self::E_DBERROR; - return $result; - } - - - /** - * (from Mantisbt) - * - * Generate a string to use as the identifier for the login cookie - * It is not guaranteed to be unique and should be checked - * The string returned should be 64 characters in length - * @return string 64 character cookie string - * @access public - */ - function auth_generate_cookie_string() - { - $t_val = mt_rand( 0, mt_getrandmax() ) + mt_rand( 0, mt_getrandmax() ); - $t_val = md5( $t_val ) . md5( time() ); - return $t_val; - } - - /** - * (from Mantisbt) - * - * Return true if the cookie login identifier is unique, false otherwise - * @param string $p_cookie_string - * @return bool indicating whether cookie string is unique - * @access public - */ - function auth_is_cookie_string_unique(&$db,$p_cookie_string) - { - $sql = "SELECT COUNT(0) AS hits FROM $this->object_table " . - "WHERE cookie_string = '" . $db->prepare_string($p_cookie_string) . "'" ; - $rs = $db->fetchFirstRow($sql); - - if( !is_array($rs) ) - { - // better die because this method is used in a do/while - // that can create infinite loop - die(__METHOD__); - } - $status = ($rs['hits'] == 0); - return $status; - } - - /** - * (from Mantisbt) - * - * Generate a UNIQUE string to use as the identifier for the login cookie - * The string returned should be 64 characters in length - * - * @return string 64 character cookie string - * @access public - * - * @since 1.9.4 - */ - function auth_generate_unique_cookie_string(&$db) - { - do { - $t_cookie_string = $this->auth_generate_cookie_string(); - } - while( !$this->auth_is_cookie_string_unique($db,$t_cookie_string ) ); - - return $t_cookie_string; - } - - - /** - * (from Mantisbt) - * - * @since 1.9.4 - */ - static function auth_get_current_user_cookie() - { - $t_cookie_name = config_get('auth_cookie'); - $t_cookie = isset($_COOKIE[$t_cookie_name]) ? $_COOKIE[$t_cookie_name] : null; - return $t_cookie; - } - - /** - * (from Mantisbt) - * - * is cookie valid? - * @param string $p_cookie_string - * @return bool - * @access public - * - * @since 1.9.4 - */ - function auth_is_cookie_valid(&$db,$p_cookie_string) - { - # fail if cookie is blank - $status = ('' === $p_cookie_string) ? false : true; - - if( $status ) - { - # look up cookie in the database to see if it is valid - $sql = "SELECT COUNT(0) AS hits FROM $this->object_table " . - "WHERE cookie_string = '" . $db->prepare_string($p_cookie_string) . "'" ; - $rs = $db->fetchFirstRow($sql); - - if( !is_array($rs) ) - { - // better die because this method is used in a do/while - // that can create infinite loop - die(__METHOD__); - } - $status = ($rs['hits'] == 1); - } - return $status; - } - - /** - * (from Mantisbt) - * - * Getter - * - * @return string - * - * @since 1.9.4 - */ - public function getSecurityCookie() - { - return $this->securityCookie; - } - - /** - * - */ - static function hasRoleOnTestProject(&$dbHandler,$id,$tprojectID) - { - $tables = tlObject::getDBTables('user_testproject_roles'); - $sql = " SELECT user_id FROM {$tables['user_testproject_roles']} " . - ' WHERE testproject_id=' . intval($tprojectID) . ' AND user_id=' . intval($id); - $rs = $dbHandler->fetchRowsIntoMap($sql, "user_id"); - return !is_null($rs); - } - - /** - * - */ - static function hasRoleOnTestPlan(&$dbHandler,$id,$tplanID) - { - $tables = tlObject::getDBTables('user_testplan_roles'); - $sql = " SELECT user_id FROM {$tables['user_testplan_roles']} " . - ' WHERE testplan_id=' . intval($tplanID) . ' AND user_id=' . intval($id); - $rs = $dbHandler->fetchRowsIntoMap($sql, "user_id"); - return !is_null($rs); - } - - - /** - * - */ - static public function getByAPIKey(&$dbHandler,$value) - { - $tables = tlObject::getDBTables('users'); - $target = $dbHandler->prepare_string($value); - $sql = "SELECT * FROM {$tables['users']} WHERE script_key='" . $dbHandler->prepare_string($target) . "'"; - - $rs = $dbHandler->fetchRowsIntoMap($sql, "id"); - return $rs; - } - - /** - * @use _SESSION - * - */ - function checkGUISecurityClearance(&$dbHandler,$context,$rightsToCheck,$checkMode) - { - $doExit = false; - $action = 'any'; - $myContext = array('tproject_id' => 0, 'tplan_id' => 0); - $myContext = array_merge($myContext, $context); - - if( $doExit = (is_null($myContext) || $myContext['tproject_id'] == 0) ) - { - logAuditEvent(TLS("audit_security_no_environment",$myContext['script']), $action,$this->dbID,"users"); - } - - if( !$doExit ) - { - foreach($rightsToCheck as $verboseRight) - { - $status = $this->hasRight($dbHandler,$verboseRight,$myContext['tproject_id'],$myContext['tplan_id']); - - if( ($doExit = !$status) && ($checkMode == 'and')) - { - $action = 'any'; - logAuditEvent(TLS("audit_security_user_right_missing",$this->login,$myContext['script'],$action), - $action,$this->dbID,"users"); - break; - } - } - } - - if ($doExit){ - redirect($_SESSION['basehref'],"top.location"); - exit(); - } - } - - - /** - * - */ - static function checkPasswordQuality($password) - { - $ret = array('status_ok' => tl::OK, 'msg' => 'ok'); - $cfg = config_get('passwordChecks'); - if( is_null($cfg) ) - { - return $ret; // >>---> Bye! - } - - $regexp['number'] = "#[0-9]+#"; - $regexp['letter'] = "#[a-z]+#"; - $regexp['capital'] = "#[A-Z]+#"; - $regexp['symbol'] = "#\W+#"; - - $pl = strlen($password); - - foreach($cfg as $attr => $val) - { - $base_msg = lang_get('bad_password_' . $attr); - switch($attr) - { - case 'minlen': - if( $pl < intval($val) ) - { - $ret['status_ok'] = tl::ERROR; - $ret['msg'] = sprintf($base_msg,intval($val), $pl); - } - break; - - case 'maxlen': - if( $pl > intval($val) ) - { - $ret['status_ok'] = tl::ERROR; - $ret['msg'] = sprintf($base_msg, intval($val), $pl); - } - break; - - case 'number': - case 'letter': - case 'capital': - case 'symbol': - if( !preg_match($regexp[$attr], $password) ) - { - $ret['status_ok'] = tl::ERROR; - $ret['msg'] = $base_msg; - } - break; - } - - if($ret['status_ok'] == tl::ERROR) - { - break; - } - } - return $ret; - - } - - /** - */ - static public function setExpirationDate(&$dbHandler,$userID,$ISODate) - { - $sch = tlObject::getDBTables(array('users')); - - $setClause = " SET expiration_date = "; - if( is_null($ISODate) || trim($ISODate) == '' ) - { - $setClause .= " NULL "; - } - else - { - // it's really a date? - // if not => do nothing - try { - $xx = new DateTime($ISODate); - $setClause .= "'" . $dbHandler->prepare_string($ISODate) . "'"; - } - catch (Exception $e) { - return; - } - } - - $sql = " UPDATE {$sch['users']} {$setClause} " . - " WHERE id = " . intval($userID); - - $rx = $dbHandler->exec_query($sql); - return tl::OK; - } - - /** - * - */ - function hasRightWrap(&$db,$roleQuestion,$context=null) { - - $cx = array('tproject_id' => null,'tplan_id' => null, - 'checkPublicPrivateAttr' => false); - $cx = array_merge($cx,(array)$context); - return $this->hasRight($db,$roleQuestion, - $cx['tproject_id'],$cx['tplan_id'], - $cx['checkPublicPrivateAttr']); - } - -} \ No newline at end of file +object_table = $this->tables['users']; + + $authCfg = config_get('authentication'); + $this->usernameFormat = config_get('username_format'); + $this->loginRegExp = config_get('validation_cfg')->user_login_valid_regex; + $this->maxLoginLength = 100; + $this->loginMethod = $authCfg['method']; + + $this->globalRoleID = config_get('default_roleid'); + $this->locale = config_get('default_language'); + $this->isActive = 1; + $this->tprojectRoles = null; + $this->tplanRoles = null; + } + + /** + * Cleans the object by resetting the members to default values + * + * @param mixed $options + * tlUser/tlObject options + */ + protected function _clean($options = self::TLOBJ_O_SEARCH_BY_ID) + { + $this->firstName = null; + $this->lastName = null; + $this->locale = null; + $this->password = null; + $this->isActive = null; + $this->defaultTestprojectID = null; + $this->globalRoleID = null; + $this->tprojectRoles = null; + $this->tplanRoles = null; + $this->userApiKey = null; + $this->securityCookie = null; + $this->authentication = null; + $this->expiration_date = null; + + if (! ($options & self::TLOBJ_O_SEARCH_BY_ID)) { + $this->dbID = null; + } + + if (! ($options & self::USER_O_SEARCH_BYLOGIN)) { + $this->login = null; + } + + if (! ($options & self::USER_O_SEARCH_BYEMAIL)) { + $this->emailAddress = null; + } + } + + /** + * Checks if password management is external (like LDAP)... + * + * @param string $method2check + * must be one of the keys of configuration $tlCfg->authentication['domain'] + * + * @return boolean return true if password management is external, else false + */ + public static function isPasswordMgtExternal($method2check = null) + { + $target = $method2check; + + // Contains Domain and Default Method + $authCfg = config_get('authentication'); + + if (is_null($target) || $target == '') { + $target = $authCfg['method']; + } + + $ret = true; + if (isset($authCfg['domain'][$target])) { + $ret = ! $authCfg['domain'][$target]['allowPasswordManagement']; + } + return $ret; + } + + /** + * Obtain a secure password. + * You can choose the number of alphanumeric characters to add and + * the number of non-alphanumeric characters. + * You can add another characters to the non-alphanumeric list if you need. + * + * @param integer $numAlpha + * number alphanumeric characters in generated password + * @param integer $numNonAlpha + * number special characters in generated password + * + * @return string the generated password + */ + public static function generatePassword($numAlpha = 6, $numNonAlpha = 2) + { + $listAlpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $listNonAlpha = ',;:!?.$/*-+&@_+;./*&?$-!,'; + + return str_shuffle( + substr(str_shuffle($listAlpha), 0, $numAlpha) . + substr(str_shuffle($listNonAlpha), 0, $numNonAlpha)); + } + + /** + * not used at the moment, only placeholder + * + * @return void + * @todo implement + */ + private function create() + {} + + // ----- BEGIN interface iDBSerialization ----- + /** + * Reads an user object identified by its database id from the given database + * + * @param + * resource &$db reference to database handler + * @param mixed $options + * (optional) tlUser/tlObject options + * + * @return integer tl::OK if the object could be read from the db, else tl::ERROR + */ + public function readFromDB(&$db, $options = self::TLOBJ_O_SEARCH_BY_ID) + { + $this->_clean($options); + $sql = " SELECT id,login,password,cookie_string,first,last,email," . + " role_id,locale, " . + " login AS fullname, active,default_testproject_id, script_key,auth_method,creation_ts,expiration_date " . + " FROM {$this->object_table}"; + $clauses = null; + + if ($options & self::TLOBJ_O_SEARCH_BY_ID) { + $clauses[] = "id = " . intval($this->dbID); + } + + if ($options & self::USER_O_SEARCH_BYLOGIN) { + $clauses[] = "login = '" . $db->prepare_string($this->login) . "'"; + } + + if ($options & self::USER_O_SEARCH_BYEMAIL) { + $clauses[] = "email = '" . $db->prepare_string($this->emailAddress) . + "'"; + } + + if ($clauses) { + $sql .= " WHERE " . implode(" AND ", $clauses); + } + $info = $db->fetchFirstRow($sql); + if ($info) { + $this->dbID = $info['id']; + $this->firstName = $info['first']; + $this->lastName = $info['last']; + $this->login = $info['login']; + $this->emailAddress = $info['email']; + $this->globalRoleID = $info['role_id']; + $this->userApiKey = $info['script_key']; + $this->securityCookie = $info['cookie_string']; + $this->authentication = $info['auth_method']; + $this->expiration_date = $info['expiration_date']; + $this->creation_ts = $info['creation_ts']; + + if ($this->globalRoleID) { + $this->globalRole = new tlRole($this->globalRoleID); + $this->globalRole->readFromDB($db); + } + + if ($this->detailLevel & self::TLOBJ_O_GET_DETAIL_ROLES) { + $this->readTestProjectRoles($db); + $this->readTestPlanRoles($db); + } + + $this->locale = $info['locale']; + $this->password = $info['password']; + $this->isActive = $info['active']; + $this->defaultTestprojectID = $info['default_testproject_id']; + } + return $info ? tl::OK : tl::ERROR; + } + + /** + * Fetches all the testproject roles of of the user, and store them into the object. + * Result could be limited to a certain testproject + * + * @param + * resource &$db reference to database handler + * @param integer $testProjectID + * Identifier of the testproject to read the roles for, + * if null all roles are read + * + * @return integer returns tl::OK + */ + public function readTestProjectRoles(&$db, $testProjectID = null) + { + $sql = "SELECT testproject_id,role_id " . + " FROM {$this->tables['user_testproject_roles']} user_testproject_roles " . + " WHERE user_id = " . intval($this->dbID); + + if ($testProjectID) { + $sql .= " AND testproject_id = " . intval($testProjectID); + } + $allRoles = $db->fetchColumnsIntoMap($sql, 'testproject_id', 'role_id'); + $this->tprojectRoles = null; + if (null != $allRoles && count($allRoles)) { + $roleCache = null; + foreach ($allRoles as $tprojectID => $roleID) { + if (! isset($roleCache[$roleID])) { + $tprojectRole = tlRole::createObjectFromDB($db, $roleID, + "tlRole", true); + $roleCache[$roleID] = $tprojectRole; + } else { + $tprojectRole = clone $roleCache[$roleID]; + } + + if ($tprojectRole) { + $this->tprojectRoles[$tprojectID] = $tprojectRole; + } + } + } + return tl::OK; + } + + /** + * Fetches all the testplan roles of of the user, and store them into the object. + * Result could be limited to a certain testplan + * + * @param + * resource &$db reference to database handler + * @param integer $testPlanID + * Identifier of the testplan to read the roles for, if null all roles are read + * + * @return integer returns tl::OK + */ + public function readTestPlanRoles(&$db, $testPlanID = null) + { + $sql = "SELECT testplan_id,role_id " . + " FROM {$this->tables['user_testplan_roles']} user_testplan_roles " . + " WHERE user_id = " . intval($this->dbID); + if ($testPlanID) { + $sql .= " AND testplan_id = " . intval($testPlanID); + } + + $allRoles = $db->fetchColumnsIntoMap($sql, 'testplan_id', 'role_id'); + $this->tplanRoles = null; + if (null != $allRoles && count($allRoles)) { + $roleCache = null; + foreach ($allRoles as $tplanID => $roleID) { + if (! isset($roleCache[$roleID])) { + $tplanRole = tlRole::createObjectFromDB($db, $roleID, + "tlRole", true); + $roleCache[$roleID] = $tplanRole; + } else { + $tplanRole = clone $roleCache[$roleID]; + } + + if ($tplanRole) { + $this->tplanRoles[$tplanID] = $tplanRole; + } + } + } + return tl::OK; + } + + /** + * Writes the object into the database + * + * @param + * resource &$db reference to database handler + * @return integer tl::OK if the object could be written to the db, else error code + */ + public function writeToDB(&$db) + { + $result = $this->checkDetails($db); + if ($result >= tl::OK) { + $t_cookie_string = $this->auth_generate_unique_cookie_string($db); + + // After addition of cookie_string, and following Mantisbt pattern, + // seems we need to check if password has changed. + // + // IMPORTANT NOTICE: + // this implementation works ONLY when password is under TestLink control + // i.e. is present on TestLink Database. + // + // if answer is yes => change also cookie_string. + if ($this->dbID) { + $gsql = " /* debugMsg */ SELECT password FROM {$this->object_table} WHERE id = " . + $this->dbID; + $rs = $db->get_recordset($gsql); + if (strcmp($rs[0]['password'], $this->password) == 0) { + // NO password change + $t_cookie_string = null; + } + + $sql = "/* debugMsg */ UPDATE {$this->tables['users']} " . + " SET first = '" . $db->prepare_string($this->firstName) . + "'" . ", last = '" . $db->prepare_string($this->lastName) . + "'" . ", email = '" . + $db->prepare_string($this->emailAddress) . "'" . + ", locale = " . "'" . $db->prepare_string($this->locale) . + "'" . ", password = " . "'" . + $db->prepare_string($this->password) . "'" . ", role_id = " . + $db->prepare_int($this->globalRoleID) . ", active = " . + $db->prepare_string($this->isActive) . ", auth_method = " . + "'" . $db->prepare_string($this->authentication) . "'"; + + if (! is_null($t_cookie_string)) { + $sql .= ", cookie_string = " . "'" . + $db->prepare_string($t_cookie_string) . "'"; + } + $sql .= " WHERE id = " . intval($this->dbID); + $result = $db->exec_query($sql); + } else { + $sql = "/* debugMsg */ INSERT INTO {$this->tables['users']} " . + " (login,password,cookie_string,first,last,email,role_id,locale,active,auth_method) " . + " VALUES ('" . $db->prepare_string($this->login) . "','" . + $db->prepare_string($this->password) . "','" . + $db->prepare_string($t_cookie_string) . "','" . + $db->prepare_string($this->firstName) . "','" . + $db->prepare_string($this->lastName) . "','" . + $db->prepare_string($this->emailAddress) . "'," . + $db->prepare_int($this->globalRoleID) . ",'" . + $db->prepare_string($this->locale) . "'," . $this->isActive . + "," . "'" . $db->prepare_string($this->authentication) . "'" . + ")"; + + $result = $db->exec_query($sql); + if ($result) { + $this->dbID = $db->insert_id($this->tables['users']); + } + } + $result = $result ? tl::OK : self::E_DBERROR; + } + return $result; + } + + /** + * WARNING: DO NOT USE THE FUNCTION - CAUSES DB INCONSISTENCE! + * + * @deprecated 1.8.3 + * @see #2407 + */ + public function deleteFromDB(&$db) + { + $safeUserID = intval($this->dbID); + $sqlSet = array(); + $sqlSet[] = "DELETE FROM {$this->table['user_assignments']} WHERE user_id = {$safeUserID}"; + $sqlSet[] = "DELETE FROM {$this->table['users']} WHERE id = {$safeUserID}"; + + foreach ($sqlSet as $sql) { + $result = $db->exec_query($sql) ? tl::OK : tl::ERROR; + if ($result == tl::ERROR) { + break; + } + } + + if ($result == tl::OK) { + $result = $this->deleteTestProjectRoles($db); + } + return $result; + } + + /** + * Deletes all testproject related role assignments for a given user + * + * @param + * resource &$db reference to database handler + * @param integer $userID + * the user ID + * + * @return integer tl::OK on success, tl:ERROR else + */ + protected function deleteTestProjectRoles(&$db) + { + $sql = "DELETE FROM {$this->tables['user_testproject_roles']} WHERE user_id = " . + intval($this->dbID); + return $db->exec_query($sql) ? tl::OK : tl::ERROR; + } + + /** + * Returns a user friendly representation of the user name + * + * @return string the display nmae + */ + public function getDisplayName($format = null) + { + $keys = array( + '%first%', + '%last%', + '%login%', + '%email%' + ); + $values = array( + $this->firstName, + $this->lastName, + $this->login, + $this->emailAddress + ); + + $fmt = is_null($format) ? $this->usernameFormat : $format; + return trim(str_replace($keys, $values, $fmt)); + } + + /** + * Encrypts a given password with MD5 + * + * @param string $pwd + * the password to encrypt + * @return string the encrypted password + */ + protected function encryptPassword($pwd, $authentication = null) + { + if (self::isPasswordMgtExternal($authentication)) { + return self::S_PWDMGTEXTERNAL; + } + + return password_hash($pwd, PASSWORD_DEFAULT); + } + + /** + * Set encrypted password + * + * @param string $pwd + * the new password + * @return integer return tl::OK is the password is stored, else errorcode + */ + public function setPassword($pwd, $authentication = null) + { + if (self::isPasswordMgtExternal($authentication)) { + return self::S_PWDMGTEXTERNAL; + } + $pwd = trim($pwd); + if ($pwd == "") { + return self::E_PWDEMPTY; + } + $this->password = $this->encryptPassword($pwd, $authentication); + return tl::OK; + } + + /** + * Getter for the password of the user + * + * @return string the password of the user + */ + public function getPassword() + { + return $this->password; + } + + /** + * compares a given password with the current password of the user + * + * @param string $pwd + * the password to compate with the password actually set + * @return integer returns tl::OK if the password's match, else errorcode + */ + public function comparePassword(&$dbH, $pwd) + { + if (self::isPasswordMgtExternal($this->authentication)) { + return self::S_PWDMGTEXTERNAL; + } + + // If we are here this means that we are using + // internal password management. + // + // Manage migration from MD5 + // MD5 hash check + // This is valid ONLY for internal password management + $encriptedPWD = $this->getPassword(); + /* Update the old MD5 hash to the new bcrypt */ + if (strlen($encriptedPWD) == 32 && $encriptedPWD === md5($pwd)) { + $this->password = $this->encryptPassword($pwd, $this->authentication); + $this->writePasswordToDB($dbH); + return tl::OK; + } + + if (password_verify($pwd, $encriptedPWD)) { + return tl::OK; + } + + return self::E_PWDDONTMATCH; + } + + /** + */ + public function checkDetails(&$db) + { + $this->firstName = trim($this->firstName); + $this->lastName = trim($this->lastName); + $this->emailAddress = trim($this->emailAddress); + $this->locale = trim($this->locale); + $this->isActive = intval($this->isActive); + $this->login = trim($this->login); + + $result = self::checkEmailAddress($this->emailAddress); + if ($result >= tl::OK) { + $result = $this->checkLogin($this->login); + } + if ($result >= tl::OK && ! $this->dbID) { + $result = self::doesUserExist($db, $this->login) ? self::E_LOGINALREADYEXISTS : tl::OK; + } + if ($result >= tl::OK) { + $result = self::checkFirstName($this->firstName); + } + if ($result >= tl::OK) { + $result = self::checkLastName($this->lastName); + } + return $result; + } + + public function checkLogin($login) + { + $result = tl::OK; + $login = trim($login); + + if ($login == "" || (tlStringLen($login) > $this->maxLoginLength)) { + $result = self::E_LOGINLENGTH; + } elseif (! preg_match($this->loginRegExp, $login)) { + // Only allow a basic set of characters + $result = self::E_NOTALLOWED; + } + return $result; + } + + /** + * Returns the id of the effective role in the context of ($tproject_id,$tplan_id) + * + * @param + * resource &$db reference to database handler + * @param integer $tproject_id + * the testproject id + * @param integer $tplan_id + * the plan id + * + * @return integer tlRole the effective role + */ + public function getEffectiveRole(&$db, $tproject_id, $tplan_id) + { + $tprojects_role = $this->tprojectRoles; + $tplans_role = $this->tplanRoles; + $effective_role = $this->globalRole; + + if (! is_null($tplans_role) && isset($tplans_role[$tplan_id])) { + $effective_role = $tplans_role[$tplan_id]; + } elseif (! is_null($tprojects_role) && + isset($tprojects_role[$tproject_id])) { + $effective_role = $tprojects_role[$tproject_id]; + } + return $effective_role; + } + + /** + * Gets all userids of users with a certain testplan role + * + * @todo WRITE RIGHT COMMENTS FROM START + * + * @param + * resource &$db reference to database handler + * @return array returns array of userids + */ + protected function getUserNamesWithTestPlanRole(&$db) + { + $sql = "SELECT DISTINCT id FROM {$this->tables['users']} users," . + " {$this->tables['user_testplan_roles']} user_testplan_roles " . + " WHERE users.id = user_testplan_roles.user_id"; + $sql .= " AND user_testplan_roles.role_id = " . intval($this->dbID); + return $db->fetchColumnsIntoArray($sql, "id"); + } + + /** + * Get a list of names with a defined project right (for example for combo-box) + * used by ajax script getUsersWithRight.php + * + * @param integer $db + * DB Identifier + * @param string $rightNick + * key corresponding with description in rights table + * @param integer $testprojectID + * Identifier of project + * + * @return array list of user IDs and names + * + * @todo fix the case that user has default role with a right but project role without + * i.e. he should be listed + */ + public function getNamesForProjectRight(&$db, $rightNick, + $testprojectID = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + if (is_null($testprojectID)) { + tLog($debugMsg . ' requires Test Project ID defined', 'ERROR'); + return null; + } + + // get users for default roles + $sql = "/* $debugMsg */ SELECT DISTINCT u.id,u.login,u.first,u.last FROM {$this->tables['users']} u" . + " JOIN {$this->tables['role_rights']} a ON a.role_id=u.role_id" . + " JOIN {$this->tables['rights']} b ON a.right_id = b.id " . + " WHERE b.description='" . $db->prepare_string($rightNick) . "'"; + $defaultRoles = $db->fetchRowsIntoMap($sql, 'id'); + + // get users for project roles + $sql = "/* $debugMsg */ SELECT DISTINCT u.id,u.login,u.first,u.last FROM {$this->tables['users']} u" . + " JOIN {$this->tables['user_testproject_roles']} p ON p.user_id=u.id" . + " AND p.testproject_id=" . intval($testprojectID) . + " JOIN {$this->tables['role_rights']} a ON a.role_id=p.role_id" . + " JOIN {$this->tables['rights']} b ON a.right_id = b.id " . + " WHERE b.description='" . $db->prepare_string($rightNick) . "'"; + $projectRoles = $db->fetchRowsIntoMap($sql, 'id'); + + // merge arrays + // the next function is available from php53 but we support php52 + // $output = array_replace($output1, $output2); + if (! is_null($projectRoles)) { + foreach ($projectRoles as $k => $v) { + if (! isset($defaultRoles[$k])) { + $defaultRoles[$k] = $v; + } + } + } + + // format for ext-js combo-box (remove associated array) + // foreach($defaultRoles as $k => $v) + // { + // $output[] = $v; + // } + return array_values($defaultRoles); + } + + /** + * Get a list of all names + * used for replacement user ID by user login + * + * @param integer $db + * DB Identifier + * @return array list of user IDs and names + */ + public function getNames(&$db, $idSet = null) + { + $sql = " SELECT id,login,first,last FROM {$this->tables['users']}"; + + $inClause = ''; + if (! is_null($idSet)) { + $inClause = " WHERE id IN (" . implode(',', (array) $idSet) . ") "; + } + + return $db->fetchRowsIntoMap($sql . $inClause, 'id'); + } + + /** + * check right on effective role for user, using test project and test plan, + * means that check right on effective role. + * + * @return string|null 'yes' or null + * + * @internal revisions + */ + public function hasRight(&$db, $roleQuestion, $tprojectID = null, + $tplanID = null, $getAccess = false) + { + global $g_propRights_global; + global $g_propRights_product; + + $testprojectID = 0; + $testPlanID = 0; + + if (! is_null($tplanID)) { + $testPlanID = $tplanID; + } + + if (! is_null($tprojectID)) { + $testprojectID = $tprojectID; + } + + $accessPublic = null; + if ($getAccess) { + if ($testprojectID > 0) { + $mgr = new testproject($db); + $accessPublic['tproject'] = $mgr->getPublicAttr($testprojectID); + unset($mgr); + } + + if ($testPlanID > 0) { + $mgr = new testplan($db); + $accessPublic['tplan'] = $mgr->getPublicAttr($testPlanID); + unset($mgr); + } + } + + $userGlobalRights = (array) $this->globalRole->rights; + + $globalRights = array(); + foreach ($userGlobalRights as $right) { + $globalRights[] = $right->name; + } + $allRights = $globalRights; + + $userTestProjectRoles = $this->tprojectRoles; + $userTestPlanRoles = $this->tplanRoles; + + if (isset($userTestProjectRoles[$testprojectID])) { + $userTestProjectRights = (array) $userTestProjectRoles[$testprojectID]->rights; + + // Special situation => just one right + $doMoreAnalysis = true; + if (count($userTestProjectRights) == 1) { + $doMoreAnalysis = ! is_null($userTestProjectRights[0]->dbID); + } + + $allRights = null; + if ($doMoreAnalysis) { + $testProjectRights = array(); + foreach ($userTestProjectRights as $right) { + $testProjectRights[] = $right->name; + } + + // subtract global rights + $testProjectRights = array_diff($testProjectRights, + array_keys($g_propRights_global)); + propagateRights($globalRights, $g_propRights_global, + $testProjectRights); + $allRights = $testProjectRights; + } else { + return false; + } + } else { + if (! is_null($accessPublic) && $accessPublic['tproject'] == 0) { + return false; + } + } + + if ($testPlanID > 0) { + if (isset($userTestPlanRoles[$testPlanID])) { + $userTestPlanRights = (array) $userTestPlanRoles[$testPlanID]->rights; + $testPlanRights = array(); + foreach ($userTestPlanRights as $right) { + $testPlanRights[] = $right->name; + } + + // subtract test projects rights + $testPlanRights = array_diff($testPlanRights, + array_keys($g_propRights_product)); + + propagateRights($allRights, $g_propRights_product, + $testPlanRights); + $allRights = $testPlanRights; + } else { + if (! is_null($accessPublic) && $accessPublic['tplan'] == 0) { + return false; + } + } + } + + return checkForRights($allRights, $roleQuestion); + } + + /** + * get array with accessible test plans for user on a test project, + * analising user roles. + * + * @param resource $db + * database handler + * @param + * int testprojectID + * @param + * int testplanID: default null. + * Used as filter when you want to check if this test plan + * is accessible. + * + * @param + * map options keys : + * 'output' => null -> get numeric array + * => map => map indexed by testplan id + * => combo => map indexed by testplan id and only returns name + * 'active' => ACTIVE (get active test plans) + * => INACTIVE (get inactive test plans) + * => TP_ALL_STATUS (get all test plans) + * + * @return array if 0 accessible test plans => null + * + * @internal revisions + * + */ + public function getAccessibleTestPlans(&$db, $testprojectID, + $testplanID = null, $options = null) + { + $debugTag = 'Class:' . __CLASS__ . '- Method:' . __FUNCTION__ . '-'; + + $my['options'] = array( + 'output' => null, + 'active' => ACTIVE + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $fields2get = ' NH.id, NH.name, TPLAN.is_public, ' . + ' COALESCE(USER_TPLAN_ROLES.testplan_id,0) AS has_role, ' . + ' USER_TPLAN_ROLES.role_id AS user_testplan_role, TPLAN.active, 0 AS selected '; + + if ($my['options']['output'] == 'mapfull') { + $fields2get .= ' ,TPLAN.notes, TPLAN.testproject_id '; + } + + $sql = " /* $debugTag */ SELECT {$fields2get} " . + " FROM {$this->tables['nodes_hierarchy']} NH" . + " JOIN {$this->tables['testplans']} TPLAN ON NH.id=TPLAN.id " . + " LEFT OUTER JOIN {$this->tables['user_testplan_roles']} USER_TPLAN_ROLES" . + " ON TPLAN.id = USER_TPLAN_ROLES.testplan_id " . + " AND USER_TPLAN_ROLES.user_id = " . intval($this->dbID); + + // Construct where sentence + $where = " WHERE testproject_id = " . intval($testprojectID); + if (! is_null($my['options']['active'])) { + $where .= " AND active = {$my['options']['active']}"; + } + + if (! is_null($testplanID)) { + $where .= " AND NH.id = " . intval($testplanID); + } + + $analyseGlobalRole = 1; + $userGlobalRoleIsNoRights = ($this->globalRoleID == TL_ROLES_NO_RIGHTS); + + // Role at Test Project level is defined? + $userProjectRoleIsNoRights = 0; + if (isset($this->tprojectRoles[$testprojectID]->dbID)) { + $userProjectRoleIsNoRights = ($this->tprojectRoles[$testprojectID]->dbID == + TL_ROLES_NO_RIGHTS); + } + + // according to new configuration option + // + // testplan_role_inheritance_mode + // + // this logic will be different + $joins = ''; + switch (config_get('testplan_role_inheritance_mode')) { + + case 'testproject': + // If user has a role for $testprojectID, then we DO NOT HAVE + // to check for globalRole + if (isset($this->tprojectRoles[$testprojectID]->dbID)) { + $analyseGlobalRole = 0; + } + + // User can have NO RIGHT on test project under analisys ($testprojectID), + // in this situation he/she + // has to have a role at Test Plan level in order to access one or more test plans + // that belong to $testprojectID. + // + // Other situation: he/she has been created with role without rights ($globalNoRights) + // + if ($userProjectRoleIsNoRights || + ($analyseGlobalRole && $userGlobalRoleIsNoRights)) { + // In order to access he/she needs specific configuration. + $where .= " AND (USER_TPLAN_ROLES.role_id IS NOT NULL AND "; + } else { + // in this situation: + // We can use what we have inherited from test project + // OR + // We can use specific test plan role if defined + $where .= " AND (USER_TPLAN_ROLES.role_id IS NULL OR "; + } + $where .= " USER_TPLAN_ROLES.role_id != " . TL_ROLES_NO_RIGHTS . + ")"; + + break; + + case 'global': + + // Because inheritance is from GLOBAL Role, do not need to care + // about existence of specific role defined AT TEST PROJECT LEVEL + + // If User has NO RIGHTS at GLOBAL Level he/she need specific + // on test plan + if ($userGlobalRoleIsNoRights) { + // In order to access he/she needs specific configuration. + $where .= " AND (USER_TPLAN_ROLES.role_id IS NOT NULL AND "; + } else { + // in this situation: + // We can use what we have inherited from GLOBAL + // + // OR + // We can use specific test plan role if defined + $where .= " AND (USER_TPLAN_ROLES.role_id IS NULL OR "; + } + $where .= " USER_TPLAN_ROLES.role_id != " . TL_ROLES_NO_RIGHTS . + ")"; + break; + } + + $sql .= $joins . $where; + + $sql .= " ORDER BY name"; + $numericIndex = false; + switch ($my['options']['output']) { + case 'map': + case 'mapfull': + $testPlanSet = $db->fetchRowsIntoMap($sql, 'id'); + break; + + case 'combo': + $testPlanSet = $db->fetchRowsIntoMap($sql, 'id'); + break; + + default: + $testPlanSet = $db->get_recordset($sql); + $numericIndex = true; + break; + } + + // Admin exception + $doReindex = false; + if ($this->globalRoleID != TL_ROLES_ADMIN && null != $testPlanSet && + ! empty($testPlanSet)) { + foreach ($testPlanSet as $idx => $item) { + if ($item['is_public'] == 0 && $item['has_role'] == 0) { + unset($testPlanSet[$idx]); + $doReindex = true; + } + } + } + + if ($my['options']['output'] == 'combo') { + $dummy = array(); + foreach ($testPlanSet as $idx => $item) { + $dummy[$idx] = $item['name']; + } + $testPlanSet = $dummy; + } + if ($doReindex && $numericIndex) { + $testPlanSet = array_values($testPlanSet); + } + return $testPlanSet; + } + + /** + * Checks the correctness of an email address + * + * @param string $email + * @return integer returns tl::OK on success, errorcode else + */ + public static function checkEmailAddress($email) + { + $result = isBlank($email) ? self::E_EMAILLENGTH : tl::OK; + if ($result == tl::OK) { + $matches = array(); + $email_regex = config_get('validation_cfg')->user_email_valid_regex_php; + if (! preg_match($email_regex, $email, $matches)) { + $result = self::E_EMAILFORMAT; + } + } + return $result; + } + + public static function checkFirstName($first) + { + return isBlank($first) ? self::E_FIRSTNAMELENGTH : tl::OK; + } + + public static function checkLastName($last) + { + return isBlank($last) ? self::E_LASTNAMELENGTH : tl::OK; + } + + /** + */ + public static function doesUserExist(&$db, $login) + { + $user = new tlUser(); + $user->login = $login; + if ($user->readFromDB($db, self::USER_O_SEARCH_BYLOGIN) >= tl::OK) { + return $user->dbID; + } + return null; + } + + /** + */ + public static function doesUserExistByEmail(&$db, $email) + { + $user = new tlUser(); + $user->emailAddress = $email; + if ($user->readFromDB($db, self::USER_O_SEARCH_BYEMAIL) >= tl::OK) { + return $user->dbID; + } + return null; + } + + /** + */ + public static function getByID(&$db, $id, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + return tlDBObject::createObjectFromDB($db, $id, __CLASS__, + self::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + } + + /** + */ + public static function getByIDs(&$db, $ids, + $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + $users = null; + + if (null == $ids) { + return null; + } + + for ($idx = 0; $idx < count($ids); $idx ++) { + $id = $ids[$idx]; + $user = tlDBObject::createObjectFromDB($db, $id, __CLASS__, + self::TLOBJ_O_SEARCH_BY_ID, $detailLevel); + if ($user) { + $users[$id] = $user; + } + } + return $users ? $users : null; + } + + public static function getAll(&$db, $whereClause = null, $column = null, + $orderBy = null, $detailLevel = self::TLOBJ_O_GET_DETAIL_FULL) + { + $tables = tlObject::getDBTables('users'); + $sql = " SELECT id FROM {$tables['users']} "; + if (! is_null($whereClause)) { + $sql .= ' ' . $whereClause; + } + $sql .= is_null($orderBy) ? " ORDER BY login " : $orderBy; + + return tlDBObject::createObjectsFromDBbySQL($db, $sql, 'id', __CLASS__, + true, $detailLevel); + } + + /** + */ + public function setActive(&$db, $value) + { + $booleanVal = intval($value) > 0 ? 1 : 0; + $sql = " UPDATE {$this->tables['users']} SET active = {$booleanVal} " . + " WHERE id = " . intval($this->dbID); + $db->exec_query($sql); + return tl::OK; + } + + /** + * Writes user password into the database + * + * @param + * resource &$db reference to database handler + * @return integer tl::OK if no problem written to the db, else error code + * + * (ideas regarding cookie_string -> from Mantisbt). + * + * @internal revisions + */ + public function writePasswordToDB(&$db) + { + if ($this->dbID) { + // After addition of cookie_string, and following Mantisbt pattern, + // seems we need to check if password has changed. + // + // IMPORTANT NOTICE: + // this implementation works ONLY when password is under TestLink control + // i.e. is present on TestLink Database. + // + // if answer is yes => change also cookie_string. + $t_cookie_string = null; + + $gsql = " SELECT password FROM {$this->object_table} WHERE id = " . + intval($this->dbID); + $rs = $db->get_recordset($gsql); + if (strcmp($rs[0]['password'], $this->password) != 0) { + // Password HAS CHANGED + $t_cookie_string = $this->auth_generate_unique_cookie_string( + $db); + } + + $sql = "UPDATE {$this->tables['users']} " . " SET password = " . "'" . + $db->prepare_string($this->password) . "'"; + + if (! is_null($t_cookie_string)) { + $sql .= ", cookie_string = " . "'" . + $db->prepare_string($t_cookie_string) . "'"; + } + $sql .= " WHERE id = " . intval($this->dbID); + $result = $db->exec_query($sql); + } + $result = $result ? tl::OK : self::E_DBERROR; + return $result; + } + + /** + * (from Mantisbt) + * + * Generate a string to use as the identifier for the login cookie + * It is not guaranteed to be unique and should be checked + * The string returned should be 64 characters in length + * + * @return string 64 character cookie string + * @access public + */ + private function auth_generate_cookie_string() + { + $t_val = mt_rand(0, mt_getrandmax()) + mt_rand(0, mt_getrandmax()); + $t_val = md5($t_val) . md5(time()); + return $t_val; + } + + /** + * (from Mantisbt) + * + * Return true if the cookie login identifier is unique, false otherwise + * + * @param string $p_cookie_string + * @return bool indicating whether cookie string is unique + * @access public + */ + private function auth_is_cookie_string_unique(&$db, $p_cookie_string) + { + $sql = "SELECT COUNT(0) AS hits FROM $this->object_table " . + "WHERE cookie_string = '" . $db->prepare_string($p_cookie_string) . + "'"; + $rs = $db->fetchFirstRow($sql); + + if (! is_array($rs)) { + // better die because this method is used in a do/while + // that can create infinite loop + die(__METHOD__); + } + return $rs['hits'] == 0; + } + + /** + * (from Mantisbt) + * + * Generate a UNIQUE string to use as the identifier for the login cookie + * The string returned should be 64 characters in length + * + * @return string 64 character cookie string + * @access public + * + * @since 1.9.4 + */ + private function auth_generate_unique_cookie_string(&$db) + { + do { + $t_cookie_string = $this->auth_generate_cookie_string(); + } while (! $this->auth_is_cookie_string_unique($db, $t_cookie_string)); + + return $t_cookie_string; + } + + /** + * (from Mantisbt) + * + * @since 1.9.4 + */ + public static function auth_get_current_user_cookie() + { + $t_cookie_name = config_get('auth_cookie'); + return isset($_COOKIE[$t_cookie_name]) ? $_COOKIE[$t_cookie_name] : null; + } + + /** + * (from Mantisbt) + * + * is cookie valid? + * + * @param string $p_cookie_string + * @return bool + * @access public + * + * @since 1.9.4 + */ + private function auth_is_cookie_valid(&$db, $p_cookie_string) + { + # fail if cookie is blank + $status = ('' === $p_cookie_string) ? false : true; + + if ($status) { + # look up cookie in the database to see if it is valid + $sql = "SELECT COUNT(0) AS hits FROM $this->object_table " . + "WHERE cookie_string = '" . + $db->prepare_string($p_cookie_string) . "'"; + $rs = $db->fetchFirstRow($sql); + + if (! is_array($rs)) { + // better die because this method is used in a do/while + // that can create infinite loop + die(__METHOD__); + } + $status = ($rs['hits'] == 1); + } + return $status; + } + + /** + * (from Mantisbt) + * + * Getter + * + * @return string + * + * @since 1.9.4 + */ + public function getSecurityCookie() + { + return $this->securityCookie; + } + + /** + */ + public static function hasRoleOnTestProject(&$dbHandler, $id, $tprojectID) + { + $tables = tlObject::getDBTables('user_testproject_roles'); + $sql = " SELECT user_id FROM {$tables['user_testproject_roles']} " . + ' WHERE testproject_id=' . intval($tprojectID) . ' AND user_id=' . + intval($id); + $rs = $dbHandler->fetchRowsIntoMap($sql, "user_id"); + return ! is_null($rs); + } + + /** + */ + public static function hasRoleOnTestPlan(&$dbHandler, $id, $tplanID) + { + $tables = tlObject::getDBTables('user_testplan_roles'); + $sql = " SELECT user_id FROM {$tables['user_testplan_roles']} " . + ' WHERE testplan_id=' . intval($tplanID) . ' AND user_id=' . + intval($id); + $rs = $dbHandler->fetchRowsIntoMap($sql, "user_id"); + return ! is_null($rs); + } + + /** + */ + public static function getByAPIKey(&$dbHandler, $value) + { + $tables = tlObject::getDBTables('users'); + $target = $dbHandler->prepare_string($value); + $sql = "SELECT * FROM {$tables['users']} WHERE script_key='" . + $dbHandler->prepare_string($target) . "'"; + + return $dbHandler->fetchRowsIntoMap($sql, "id"); + } + + /** + * + * @use _SESSION + * + */ + public function checkGUISecurityClearance(&$dbHandler, $context, + $rightsToCheck, $checkMode) + { + $doExit = false; + $action = 'any'; + $myContext = array( + 'tproject_id' => 0, + 'tplan_id' => 0 + ); + $myContext = array_merge($myContext, $context); + + if ($doExit = (is_null($myContext) || $myContext['tproject_id'] == 0)) { + logAuditEvent( + TLS("audit_security_no_environment", $myContext['script']), + $action, $this->dbID, "users"); + } + + if (! $doExit) { + foreach ($rightsToCheck as $verboseRight) { + $status = $this->hasRight($dbHandler, $verboseRight, + $myContext['tproject_id'], $myContext['tplan_id']); + + if (($doExit = ! $status) && ($checkMode == 'and')) { + $action = 'any'; + logAuditEvent( + TLS("audit_security_user_right_missing", $this->login, + $myContext['script'], $action), $action, $this->dbID, + "users"); + break; + } + } + } + + if ($doExit) { + redirect($_SESSION['basehref'], "top.location"); + exit(); + } + } + + /** + */ + public static function checkPasswordQuality($password) + { + $ret = array( + 'status_ok' => tl::OK, + 'msg' => 'ok' + ); + $cfg = config_get('passwordChecks'); + if (is_null($cfg)) { + return $ret; // >>---> Bye! + } + + $regexp['number'] = "#[0-9]+#"; + $regexp['letter'] = "#[a-z]+#"; + $regexp['capital'] = "#[A-Z]+#"; + $regexp['symbol'] = "#\W+#"; + + $pl = strlen($password); + + foreach ($cfg as $attr => $val) { + $base_msg = lang_get('bad_password_' . $attr); + switch ($attr) { + case 'minlen': + if ($pl < intval($val)) { + $ret['status_ok'] = tl::ERROR; + $ret['msg'] = sprintf($base_msg, intval($val), $pl); + } + break; + + case 'maxlen': + if ($pl > intval($val)) { + $ret['status_ok'] = tl::ERROR; + $ret['msg'] = sprintf($base_msg, intval($val), $pl); + } + break; + + case 'number': + case 'letter': + case 'capital': + case 'symbol': + if (! preg_match($regexp[$attr], $password)) { + $ret['status_ok'] = tl::ERROR; + $ret['msg'] = $base_msg; + } + break; + } + + if ($ret['status_ok'] == tl::ERROR) { + break; + } + } + return $ret; + } + + /** + */ + public static function setExpirationDate(&$dbHandler, $userID, $isoDate) + { + $sch = tlObject::getDBTables(array( + 'users' + )); + + $setClause = " SET expiration_date = "; + if (is_null($isoDate) || trim($isoDate) == '') { + $setClause .= " NULL "; + } else { + // it's really a date? + // if not => do nothing + try { + $setClause .= "'" . $dbHandler->prepare_string($isoDate) . "'"; + } catch (Exception $e) { + return; + } + } + + $sql = " UPDATE {$sch['users']} {$setClause} " . " WHERE id = " . + intval($userID); + + $dbHandler->exec_query($sql); + return tl::OK; + } + + /** + */ + private function hasRightWrap(&$db, $roleQuestion, $context = null) + { + $cx = array( + 'tproject_id' => null, + 'tplan_id' => null, + 'checkPublicPrivateAttr' => false + ); + $cx = array_merge($cx, (array) $context); + return $this->hasRight($db, $roleQuestion, $cx['tproject_id'], + $cx['tplan_id'], $cx['checkPublicPrivateAttr']); + } + + /** + */ + public function hasRightOnProj(&$db, $roleQuestion) + { + $tproj = null; + if (isset($_SESSION['testprojectID'])) { + $tproj = intval($_SESSION['testprojectID']); + } + return $this->hasRight($db, $roleQuestion, $tproj); + } +} diff --git a/lib/functions/tlsmarty.inc.php b/lib/functions/tlsmarty.inc.php index a2d0d9fd5c..c1d6a6a761 100644 --- a/lib/functions/tlsmarty.inc.php +++ b/lib/functions/tlsmarty.inc.php @@ -1,452 +1,478 @@ -assign($params['var'], $the_ret); + } else { + return $the_ret; + } +} + +/** + * Should be used to prevent certain templates to only get included once per page load. + * For example javascript includes, such as ext-js. + * + * Usage (in template): + * + * {if guard_header_smarty(__FILE__)} + * template code + * + * {/if} + * + */ +function guard_header_smarty($file) +{ + static $guarded = array(); + $status_ok = false; + + if (! isset($guarded[$file])) { + $guarded[$file] = true; + $status_ok = true; + } + return $status_ok; +} + +/** + * TestLink wrapper for external Smarty class + * + * @package TestLink + */ +class TLSmarty extends Smarty +{ + + private $tlImages; + + public $tlTemplateCfg; + + public function __construct() + { + global $tlCfg; + global $g_tpl; + + parent::__construct(); + + $this->template_dir = [ + 'main' => TL_ABS_PATH . 'gui/templates/' . $tlCfg->gui->ux . '/' + ]; + + $this->config_dir = TL_ABS_PATH . 'gui/templates/conf'; + $this->compile_dir = TL_TEMP_PATH; + + $testproject_coloring = $tlCfg->gui->testproject_coloring; + $testprojectColor = $tlCfg->gui->background_color; + + if (isset($_SESSION['testprojectColor'])) { + $testprojectColor = $_SESSION['testprojectColor']; + if ($testprojectColor == "") { + $testprojectColor = $tlCfg->gui->background_color; + } + } + $this->assign('testprojectColor', $testprojectColor); + + $my_locale = isset($_SESSION['locale']) ? $_SESSION['locale'] : TL_DEFAULT_LOCALE; + $basehref = isset($_SESSION['basehref']) ? $_SESSION['basehref'] : TL_BASE_HREF; + + if ($tlCfg->smarty_debug) { + $this->debugging = true; + tLog("Smarty debug window = ON"); + } + + // ---------------------------------------------------------------------- + // Must be initialized to avoid log on TestLink Event Viewer due to undefined variable. + // This means that optional/missing parameters on include can not be used. + // + // Good refactoring must be done in future, to create group of this variable + // with clear names that must be a hint for developers, to understand where this + // variables are used. + + // inc_head.tpl + $this->assign('SP_html_help_file', null); + $this->assign('menuUrl', null); + $this->assign('args', null); + $this->assign('additionalArgs', null); + $this->assign('pageTitle', null); + $this->assign('printPreferences', null); + + $this->assign('css_only', null); + $this->assign('body_onload', null); + + // inc_attachments.tpl + $this->assign('attach_tableStyles', "font-size:12px"); + $this->assign('attach_tableClassName', "simple"); + $this->assign('attach_inheritStyle', 0); + $this->assign('attach_show_upload_btn', 1); + $this->assign('attach_show_title', 1); + $this->assign('attach_downloadOnly', false); + + // inc_help.tpl + $this->assign('inc_help_alt', null); + $this->assign('inc_help_title', null); + $this->assign('inc_help_style', null); + $this->assign('show_help_icon', true); + + $this->assign('tplan_name', null); + $this->assign('name', null); + + $this->assign('basehref', $basehref); + $this->assign('css', $basehref . TL_TESTLINK_CSS); + $this->assign('use_custom_css', 0); + if (! is_null($tlCfg->custom_css) && $tlCfg->custom_css != '') { + $this->assign('use_custom_css', 1); + $this->assign('custom_css', + $basehref . TL_THEME_CSS_DIR . $tlCfg->custom_css); + } + + $this->assign('locale', $my_locale); + + // + $stdTPLCfg = array(); + $stdTPLCfg['inc_tcbody'] = 'testcases/inc_tcbody.tpl'; + $stdTPLCfg['inc_steps'] = 'testcases/inc_steps.tpl'; + + $stdTPLCfg['inc_show_scripts_table'] = 'inc_show_scripts_table.tpl'; + + $stdTPLCfg['keywords.inc'] = 'testcases/keywords.inc.tpl'; + + $stdTPLCfg['attributesLinearForViewer.inc'] = 'testcases/attributesLinearForViewer.inc.tpl'; + + $stdTPLCfg['relations.inc'] = 'testcases/relations.inc.tpl'; + $stdTPLCfg['quickexec.inc'] = 'testcases/quickexec.inc.tpl'; + + $stdTPLCfg['steps_horizontal.inc'] = 'testcases/steps_horizontal.inc.tpl'; + $stdTPLCfg['steps_vertical.inc'] = 'testcases/steps_vertical.inc.tpl'; + + $stdTPLCfg['platforms.inc'] = 'testcases/platforms.inc.tpl'; + + // load configuration + $this->assign('session', isset($_SESSION) ? $_SESSION : null); + $this->assign('tlCfg', $tlCfg); + $this->assign('tplConfig', array_merge($stdTPLCfg, (array) $g_tpl)); + $this->assign('gsmarty_gui', $tlCfg->gui); + $this->assign('gsmarty_spec_cfg', config_get('spec_cfg')); + $this->assign('gsmarty_attachments', config_get('attachments')); + + $this->assign('pageCharset', $tlCfg->charset); + $this->assign('tlVersion', TL_VERSION); + $this->assign('testproject_coloring', $testproject_coloring); + + // define a select structure for {html_options ...} + $this->assign('gsmarty_option_yes_no', + array( + 0 => lang_get('No'), + 1 => lang_get('Yes') + )); + $this->assign('gsmarty_option_priority', + array( + HIGH => lang_get('high_priority'), + MEDIUM => lang_get('medium_priority'), + LOW => lang_get('low_priority') + )); + + $this->assign('gsmarty_option_importance', + array( + HIGH => lang_get('high_importance'), + MEDIUM => lang_get('medium_importance'), + LOW => lang_get('low_importance') + )); + + $wkf = array(); + $xcfg = config_get('testCaseStatus'); + foreach ($xcfg as $human => $key) { + $wkf[$key] = lang_get('testCaseStatus_' . $human); + } + $this->assign('gsmarty_option_wkfstatus', $wkf); + + // this allows unclosed tag to add more information and link; see inc_head.tpl + $this->assign('openHead', 'no'); + + // there are some variables which should not be assigned for template but must be initialized + // inc_head.tpl + $this->assign('jsValidate', null); + $this->assign('jsTree', null); + $this->assign('editorType', null); + + // user feedback variables (used in inc_update.tpl) + $this->assign('user_feedback', null); + $this->assign('feedback_type', ''); // Possibile values: soft + $this->assign('action', 'updated'); // todo: simplify (remove) - use user_feedback + $this->assign('sqlResult', null); // todo: simplify (remove) - use user_feedback + + $this->assign('refresh', 'no'); + $this->assign('result', null); + + $this->assign('gsmarty_href_keywordsView', + ' "lib/keywords/keywordsView.php?tproject_id=%s%" ' . + ' target="mainframe" class="bold" ' . ' title="' . + lang_get('menu_manage_keywords') . '"'); + + $this->assign('gsmarty_href_platformsView', + ' "lib/platforms/platformsView.php?tproject_id=%s%" ' . + ' target="mainframe" class="bold" ' . ' title="' . + lang_get('menu_manage_platforms') . '"'); + + $this->assign('gsmarty_html_select_date_field_order', + $tlCfg->locales_html_select_date_field_order[$my_locale]); + + $this->assign('gsmarty_date_format', + $tlCfg->locales_date_format[$my_locale]); + + // add smarty variable to be able to set localized date format on datepicker + $this->assign('gsmarty_datepicker_format', + str_replace('%', '', $tlCfg->locales_date_format[$my_locale])); + + $this->assign('gsmarty_timestamp_format', + $tlCfg->locales_timestamp_format[$my_locale]); + + // Images + $this->tlImages = tlSmarty::getImageSet(); + + $msg = lang_get('show_hide_api_info'); + $this->tlImages['toggle_api_info'] = "\"{$msg}\"tlImages['api_info']}\" align=\"left\" />"; + + $msg = lang_get('show_hide_direct_link'); + $this->tlImages['toggle_direct_link'] = "\"{$msg}\"tlImages['direct_link']}\" align=\"left\" />"; + + // Some useful values for Sort Table Engine + $this->tlImages['sort_hint'] = ''; + switch (TL_SORT_TABLE_ENGINE) { + case 'kryogenix.org': + $sort_table_by_column = lang_get('sort_table_by_column'); + $this->tlImages['sort_hint'] = "\"{$sort_table_by_column}\"tlImages['sort']}\" align=\"left\" />"; + + $this->assign("noSortableColumnClass", "sorttable_nosort"); + break; + + default: + $this->assign("noSortableColumnClass", ''); + break; + } + + // Do not move!!! + $this->assign("tlImages", $this->tlImages); + + // Register functions + $this->registerPlugin("function", "lang_get", "lang_get_smarty"); + $this->registerPlugin("function", "localize_date", + "localize_date_smarty"); + $this->registerPlugin("function", "localize_timestamp", + "localize_timestamp_smarty"); + $this->registerPlugin("function", "localize_tc_status", + "translate_tc_status_smarty"); + + $this->registerPlugin("modifier", "basename", "basename"); + $this->registerPlugin("modifier", "dirname", "dirname"); + + // Call to smarty filter that adds a CSRF filter to all form elements + if ($tlCfg->csrf_filter_enabled && function_exists('smarty_csrf_filter')) { + $this->registerFilter('output', 'smarty_csrf_filter'); + } + } + + /** + */ + public function getImages() + { + return $this->tlImages; + } + + /** + */ + public static function getImageSet() + { + $burl = isset($_SESSION['basehref']) ? $_SESSION['basehref'] : TL_BASE_HREF; + $imgLoc = $burl . TL_THEME_IMG_DIR; + + $dummy = array( + 'active' => $imgLoc . 'flag_green.png', + 'activity' => $imgLoc . 'information.png', + 'account' => $imgLoc . 'user_edit.png', + 'add' => $imgLoc . 'add.png', + 'add2set' => $imgLoc . 'basket_put.png', + 'api_info' => $imgLoc . 'brick.png', + 'assign_task' => $imgLoc . 'assign_exec_task_to_me.png', + 'bug' => $imgLoc . 'bug.png', + 'bug_link_tl_to_bts' => $imgLoc . 'bug_link_famfamfam.png', + 'bug_create_into_bts' => $imgLoc . 'bug_add_famfamfam.png', + 'bug_link_tl_to_bts_disabled' => $imgLoc . + 'bug_link_disabled_famfamfam.png', + 'bug_create_into_bts_disabled' => $imgLoc . + 'bug_add_disabled_famfamfam.png', + 'bug_add_note' => $imgLoc . 'bug_edit.png', + 'bullet' => $imgLoc . 'slide_gripper.gif', + 'bulkOperation' => $imgLoc . 'bulkAssignTransparent.png', + 'calendar' => $imgLoc . 'calendar.gif', + 'checked' => $imgLoc . 'apply_f2_16.png', + 'choiceOn' => $imgLoc . 'accept.png', + 'clear' => $imgLoc . 'trash.png', + 'clear_notes' => $imgLoc . 'font_delete.png', + 'clipboard' => $imgLoc . 'page_copy.png', + 'check_ok' => $imgLoc . 'lightbulb.png', + 'check_ko' => $imgLoc . 'link_error.png', + 'cog' => $imgLoc . 'cog.png', + 'copyAttachments' => $imgLoc . 'folder_add.png', + 'create_copy' => $imgLoc . 'application_double.png', + 'create_from_xml' => $imgLoc . 'wand.png', + 'date' => $imgLoc . 'date.png', + 'delete' => $imgLoc . 'trash.png', + 'demo_mode' => $imgLoc . 'emoticon_tongue.png', + 'delete_disabled' => $imgLoc . 'trash_greyed.png', + 'disconnect' => $imgLoc . 'disconnect.png', + 'disconnect_small' => $imgLoc . 'disconnect_small.png', + 'direct_link' => $imgLoc . 'world_link.png', + 'duplicate' => $imgLoc . 'application_double.png', + 'edit' => $imgLoc . 'icon_edit.png', + 'edit_icon' => $imgLoc . 'edit_icon.png', + 'email' => $imgLoc . 'email.png', + 'events' => $imgLoc . 'bell.png', + 'eye' => $imgLoc . 'eye.png', + 'vorsicht' => $imgLoc . 'exclamation.png', + 'export' => $imgLoc . 'export.png', + 'export_import' => $imgLoc . 'export_import.png', + 'execute' => $imgLoc . 'lightning.png', + 'executed' => $imgLoc . 'lightning.png', + 'exec_icon' => $imgLoc . 'exec_icon.png', + 'exec_passed' => $imgLoc . 'emoticon_smile.png', + 'exec_failed' => $imgLoc . 'emoticon_unhappy.png', + 'exec_blocked' => $imgLoc . 'emoticon_surprised.png', + 'execution' => $imgLoc . 'controller.png', + 'execution_order' => $imgLoc . 'timeline_marker.png', + 'execution_duration' => $imgLoc . 'hourglass.png', + 'export_excel' => $imgLoc . 'page_excel.png', + 'export_for_results_import' => $imgLoc . 'brick_go.png', + 'ghost_item' => $imgLoc . 'ghost16x16.png', + 'user_group' => $imgLoc . 'group.png', + 'heads_up' => $imgLoc . 'lightbulb.png', + 'help' => $imgLoc . 'question.gif', + 'history' => $imgLoc . 'history.png', + 'history_small' => $imgLoc . 'history_small.png', + 'home' => $imgLoc . 'application_home.png', + 'import' => $imgLoc . 'door_in.png', + 'import_results' => $imgLoc . 'monitor_lightning.png', + 'inactive' => $imgLoc . 'flag_yellow.png', + 'info' => $imgLoc . 'question.gif', + 'info_small' => $imgLoc . 'information_small.png', + 'insert_step' => $imgLoc . 'insert_step.png', + 'item_link' => $imgLoc . 'folder_link.png', + 'link_to_report' => $imgLoc . 'link.png', + 'lock' => $imgLoc . 'lock.png', + 'lock_open' => $imgLoc . 'lock_open.png', + 'log_message' => $imgLoc . 'history.png', + 'log_message_small' => $imgLoc . 'history_small.png', + 'logout' => $imgLoc . 'computer_go.png', + 'magnifier' => $imgLoc . 'magnifier.png', + 'move_copy' => $imgLoc . 'application_double.png', + 'new_f2_16' => $imgLoc . 'new_f2_16.png', + 'note_edit' => $imgLoc . 'note_edit.png', + 'note_edit_greyed' => $imgLoc . 'note_edit_greyed.png', + 'on' => $imgLoc . 'lightbulb.png', + 'off' => $imgLoc . 'lightbulb_off.png', + 'order_alpha' => $imgLoc . 'style.png', + 'plugins' => $imgLoc . 'connect.png', + 'public' => $imgLoc . 'door_open.png', + 'private' => $imgLoc . 'door.png', + 'relations' => $imgLoc . 'asterisk_yellow.png', + 'remove' => $imgLoc . 'delete.png', + 'reorder' => $imgLoc . 'arrow_switch.png', + 'report' => $imgLoc . 'report.png', + 'report_test_automation' => $imgLoc . 'lightning.png', + 'report_word' => $imgLoc . 'page_word.png', + 'requirements' => $imgLoc . 'cart.png', + 'resequence' => $imgLoc . 'control_equalizer.png', + 'reset' => $imgLoc . 'arrow_undo.png', + 'saveForBaseline' => $imgLoc . 'lock.png', + 'summary_small' => $imgLoc . 'information_small.png', + 'sort' => $imgLoc . 'sort_hint.png', + 'steps' => $imgLoc . 'bricks.png', + 'table' => $imgLoc . 'application_view_columns.png', + 'testcases_table_view' => $imgLoc . 'application_view_columns.png', + 'testcase_execution_type_automatic' => $imgLoc . 'bullet_wrench.png', + 'testcase_execution_type_manual' => $imgLoc . 'user.png', + 'test_specification' => $imgLoc . 'chart_organisation.png', + 'toggle_all' => $imgLoc . 'toggle_all.gif', + 'user' => $imgLoc . 'user.png', + 'upload' => $imgLoc . 'upload_16.png', + 'upload_greyed' => $imgLoc . 'upload_16_greyed.png', + 'warning' => $imgLoc . 'error_triangle.png', + 'wrench' => $imgLoc . 'wrench.png', + 'test_status_not_run' => $imgLoc . 'test_status_not_run.png', + 'test_status_passed' => $imgLoc . 'test_status_passed.png', + 'test_status_failed' => $imgLoc . 'test_status_failed.png', + 'test_status_blocked' => $imgLoc . 'test_status_blocked.png', + 'test_status_passed_next' => $imgLoc . 'test_status_passed_next.png', + 'test_status_failed_next' => $imgLoc . 'test_status_failed_next.png', + 'test_status_blocked_next' => $imgLoc . + 'test_status_blocked_next.png', + 'keyword_add' => $imgLoc . 'tag_blue_add.png' + ); + + $imi = config_get('images'); + if (! empty($imi)) { + foreach ($imi as $key => $img) { + + // You need to configure in your custom config something like this + // $tlCfg->images['test_status_passed_with_remarks'] = + // '%imgLoc%test_status_passed_with_remarks.png'; + // PAY ATTENTION to the place holder %imgLoc% + $imi[$key] = str_replace('%imgLoc%', $imgLoc, $img); + } + $dummy = array_merge($dummy, $imi); + } + return $dummy; + } } - -if(!defined('TL_USE_LOG4JAVASCRIPT') ) { - define('TL_USE_LOG4JAVASCRIPT',0); -} - - -/** - * The next two functions was moved here from common.php */ -function translate_tc_status($status_code) { - $resultsCfg = config_get('results'); - $verbose = lang_get('test_status_not_run'); - if( $status_code != '') { - $suffix = $resultsCfg['code_status'][$status_code]; - $verbose = lang_get('test_status_' . $suffix); - } - return $verbose; -} - -/** - * function is registered in tlSmarty class - * @uses function translate_tc_status - * @todo should be moved to tlSmarty class - */ -function translate_tc_status_smarty($params, $smarty) { - $the_ret = translate_tc_status($params['s']); - if( isset($params['var']) ) { - $smarty->assign($params['var'], $the_ret); - } else { - return $the_ret; - } -} - -/** - * Should be used to prevent certain templates to only get included once per page load. - * For example javascript includes, such as ext-js. - * - * Usage (in template): - * - * {if guard_header_smarty(__FILE__)} - * template code - * - * {/if} - * - */ -function guard_header_smarty($file) { - static $guarded = array(); - $status_ok = false; - - if (!isset($guarded[$file])) { - $guarded[$file] = true; - $status_ok = true; - } - return $status_ok; -} - -/** - * TestLink wrapper for external Smarty class - * @package TestLink - */ -class TLSmarty extends Smarty { - private $tlImages; - var $tlTemplateCfg; - - function __construct() { - global $tlCfg; - global $g_tpl; - - parent::__construct(); - - $this->template_dir = - array('main' => TL_ABS_PATH . 'gui/templates/' . - $tlCfg->gui->ux . '/'); - - $this->config_dir = TL_ABS_PATH . 'gui/templates/conf'; - $this->compile_dir = TL_TEMP_PATH; - - - - $testproject_coloring = $tlCfg->gui->testproject_coloring; - $testprojectColor = $tlCfg->gui->background_color ; - - if (isset($_SESSION['testprojectColor'])) { - $testprojectColor = $_SESSION['testprojectColor']; - if ($testprojectColor == "") { - $testprojectColor = $tlCfg->gui->background_color; - } - } - $this->assign('testprojectColor', $testprojectColor); - - $my_locale = isset($_SESSION['locale']) ? $_SESSION['locale'] : TL_DEFAULT_LOCALE; - $basehref = isset($_SESSION['basehref']) ? $_SESSION['basehref'] : TL_BASE_HREF; - - if ($tlCfg->smarty_debug) { - $this->debugging = true; - tLog("Smarty debug window = ON"); - } - - // ---------------------------------------------------------------------- - // Must be initialized to avoid log on TestLink Event Viewer due to undefined variable. - // This means that optional/missing parameters on include can not be used. - // - // Good refactoring must be done in future, to create group of this variable - // with clear names that must be a hint for developers, to understand where this - // variables are used. - - // inc_head.tpl - $this->assign('SP_html_help_file',null); - $this->assign('menuUrl',null); - $this->assign('args',null); - $this->assign('additionalArgs',null); - $this->assign('pageTitle',null); - $this->assign('printPreferences',null); - - $this->assign('css_only',null); - $this->assign('body_onload',null); - - // inc_attachments.tpl - $this->assign('attach_tableStyles',"font-size:12px"); - $this->assign('attach_tableClassName',"simple"); - $this->assign('attach_inheritStyle',0); - $this->assign('attach_show_upload_btn',1); - $this->assign('attach_show_title',1); - $this->assign('attach_downloadOnly',false); - - // inc_help.tpl - $this->assign('inc_help_alt',null); - $this->assign('inc_help_title',null); - $this->assign('inc_help_style',null); - $this->assign('show_help_icon',true); - - $this->assign('tplan_name',null); - $this->assign('name',null); - // ----------------------------------------------------------------------------- - - $this->assign('basehref', $basehref); - $this->assign('css', $basehref . TL_TESTLINK_CSS); - $this->assign('use_custom_css', 0); - if(!is_null($tlCfg->custom_css) && $tlCfg->custom_css != '') { - $this->assign('use_custom_css', 1); - $this->assign('custom_css', - $basehref . TL_THEME_CSS_DIR . $tlCfg->custom_css); - } - - $this->assign('locale', $my_locale); - - // - $stdTPLCfg = array(); - $stdTPLCfg['inc_tcbody'] = 'testcases/inc_tcbody.tpl'; - $stdTPLCfg['inc_steps'] = 'testcases/inc_steps.tpl'; - - $stdTPLCfg['inc_show_scripts_table'] = 'inc_show_scripts_table.tpl'; - - $stdTPLCfg['keywords.inc'] = 'testcases/keywords.inc.tpl'; - - $stdTPLCfg['attributesLinearForViewer.inc'] = - 'testcases/attributesLinearForViewer.inc.tpl'; - - $stdTPLCfg['relations.inc'] = 'testcases/relations.inc.tpl'; - $stdTPLCfg['quickexec.inc'] = 'testcases/quickexec.inc.tpl'; - - $stdTPLCfg['steps_horizontal.inc'] = 'testcases/steps_horizontal.inc.tpl'; - $stdTPLCfg['steps_vertical.inc'] = 'testcases/steps_vertical.inc.tpl'; - - $stdTPLCfg['platforms.inc'] = 'testcases/platforms.inc.tpl'; - - - // ----------------------------------------------------------------------------- - // load configuration - $this->assign('session',isset($_SESSION) ? $_SESSION : null); - $this->assign('tlCfg',$tlCfg); - $this->assign('tplConfig',array_merge($stdTPLCfg,(array)$g_tpl)); - $this->assign('gsmarty_gui',$tlCfg->gui); - $this->assign('gsmarty_spec_cfg',config_get('spec_cfg')); - $this->assign('gsmarty_attachments',config_get('attachments')); - - $this->assign('pageCharset',$tlCfg->charset); - $this->assign('tlVersion',TL_VERSION); - $this->assign('testproject_coloring',null); - - - // ----------------------------------------------------------------------------- - // define a select structure for {html_options ...} - $this->assign('gsmarty_option_yes_no', array(0 => lang_get('No'), 1 => lang_get('Yes'))); - $this->assign('gsmarty_option_priority', array(HIGH => lang_get('high_priority'), - MEDIUM => lang_get('medium_priority'), - LOW => lang_get('low_priority'))); - - $this->assign('gsmarty_option_importance', array(HIGH => lang_get('high_importance'), - MEDIUM => lang_get('medium_importance'), - LOW => lang_get('low_importance'))); - - $wkf = array(); - $xcfg = config_get('testCaseStatus'); - foreach($xcfg as $human => $key) { - $wkf[$key] = lang_get('testCaseStatus_' . $human); - } - $this->assign('gsmarty_option_wkfstatus',$wkf); - - - // this allows unclosed tag to add more information and link; see inc_head.tpl - $this->assign('openHead', 'no'); - - // there are some variables which should not be assigned for template but must be initialized - // inc_head.tpl - $this->assign('jsValidate', null); - $this->assign('jsTree', null); - $this->assign('editorType', null); - - - // user feedback variables (used in inc_update.tpl) - $this->assign('user_feedback', null); - $this->assign('feedback_type', ''); // Possibile values: soft - $this->assign('action', 'updated'); //todo: simplify (remove) - use user_feedback - $this->assign('sqlResult', null); //todo: simplify (remove) - use user_feedback - - $this->assign('refresh', 'no'); - $this->assign('result', null); - - // $this->assign('optLocale',config_get('locales')); - $this->assign('gsmarty_href_keywordsView', - ' "lib/keywords/keywordsView.php?tproject_id=%s%" ' . ' target="mainframe" class="bold" ' . - ' title="' . lang_get('menu_manage_keywords') . '"'); - - - $this->assign('gsmarty_href_platformsView', - ' "lib/platforms/platformsView.php?tproject_id=%s%" ' . ' target="mainframe" class="bold" ' . - ' title="' . lang_get('menu_manage_platforms') . '"'); - - $this->assign('gsmarty_html_select_date_field_order', - $tlCfg->locales_html_select_date_field_order[$my_locale]); - - $this->assign('gsmarty_date_format',$tlCfg->locales_date_format[$my_locale]); - - // add smarty variable to be able to set localized date format on datepicker - $this->assign('gsmarty_datepicker_format', - str_replace('%','',$tlCfg->locales_date_format[$my_locale])); - - $this->assign('gsmarty_timestamp_format',$tlCfg->locales_timestamp_format[$my_locale]); - - // ----------------------------------------------------------------------------- - // Images - $this->tlImages = tlSmarty::getImageSet(); - - $msg = lang_get('show_hide_api_info'); - $this->tlImages['toggle_api_info'] = "\"{$msg}\"tlImages['api_info']}\" align=\"left\" />"; - - $msg = lang_get('show_hide_direct_link'); - $this->tlImages['toggle_direct_link'] = "\"{$msg}\"tlImages['direct_link']}\" align=\"left\" />"; - - // Some useful values for Sort Table Engine - $this->tlImages['sort_hint'] = ''; - switch (TL_SORT_TABLE_ENGINE) - { - case 'kryogenix.org': - $sort_table_by_column = lang_get('sort_table_by_column'); - $this->tlImages['sort_hint'] = "\"{$sort_table_by_column}\"tlImages['sort']}\" align=\"left\" />"; - - $this->assign("noSortableColumnClass","sorttable_nosort"); - break; - - default: - $this->assign("noSortableColumnClass",''); - break; - } - - // Do not move!!! - $this->assign("tlImages",$this->tlImages); - - // Register functions - $this->registerPlugin("function","lang_get", "lang_get_smarty"); - $this->registerPlugin("function","localize_date", "localize_date_smarty"); - $this->registerPlugin("function","localize_timestamp", "localize_timestamp_smarty"); - $this->registerPlugin("function","localize_tc_status","translate_tc_status_smarty"); - - $this->registerPlugin("modifier","basename","basename"); - $this->registerPlugin("modifier","dirname","dirname"); - - // Call to smarty filter that adds a CSRF filter to all form elements - if(isset($tlCfg->csrf_filter_enabled) && - $tlCfg->csrf_filter_enabled === TRUE && function_exists('smarty_csrf_filter')) { - $this->registerFilter('output','smarty_csrf_filter'); - } - - } // end of function TLSmarty() - - /** - * - */ - function getImages() { - return $this->tlImages; - } - - /** - * - */ - static function getImageSet() { - $burl = isset($_SESSION['basehref']) ? $_SESSION['basehref'] : TL_BASE_HREF; - $imgLoc = $burl . TL_THEME_IMG_DIR; - - $dummy = array('active' => $imgLoc . 'flag_green.png', - 'activity' => $imgLoc . 'information.png', - 'account' => $imgLoc . 'user_edit.png', - 'add' => $imgLoc . 'add.png', - 'add2set' => $imgLoc . 'basket_put.png', - 'api_info' => $imgLoc . 'brick.png', - 'assign_task' => $imgLoc . 'assign_exec_task_to_me.png', - 'bug' => $imgLoc . 'bug.png', - 'bug_link_tl_to_bts' => $imgLoc . 'bug_link_famfamfam.png', - 'bug_create_into_bts' => $imgLoc . 'bug_add_famfamfam.png', - 'bug_link_tl_to_bts_disabled' => $imgLoc . 'bug_link_disabled_famfamfam.png', - 'bug_create_into_bts_disabled' => $imgLoc . 'bug_add_disabled_famfamfam.png', - 'bug_add_note' => $imgLoc . 'bug_edit.png', - 'bullet' => $imgLoc . 'slide_gripper.gif', - 'bulkOperation' => $imgLoc . 'bulkAssignTransparent.png', - 'calendar' => $imgLoc . 'calendar.gif', - 'checked' => $imgLoc . 'apply_f2_16.png', - 'choiceOn' => $imgLoc . 'accept.png', - 'clear' => $imgLoc . 'trash.png', - 'clear_notes' => $imgLoc . 'font_delete.png', - 'clipboard' => $imgLoc . 'page_copy.png', - 'check_ok' => $imgLoc . 'lightbulb.png', - 'check_ko' => $imgLoc . 'link_error.png', - 'cog' => $imgLoc . 'cog.png', - 'copy_attachments' => $imgLoc . 'folder_add.png', - 'create_copy' => $imgLoc . 'application_double.png', - 'create_from_xml' => $imgLoc . 'wand.png', - 'date' => $imgLoc . 'date.png', - 'delete' => $imgLoc . 'trash.png', - 'demo_mode' => $imgLoc . 'emoticon_tongue.png', - 'delete_disabled' => $imgLoc . 'trash_greyed.png', - 'disconnect' => $imgLoc . 'disconnect.png', - 'disconnect_small' => $imgLoc . 'disconnect_small.png', - 'direct_link' => $imgLoc . 'world_link.png', - 'duplicate' => $imgLoc . 'application_double.png', - 'edit' => $imgLoc . 'icon_edit.png', - 'edit_icon' => $imgLoc . 'edit_icon.png', - 'email' => $imgLoc . 'email.png', - 'events' => $imgLoc . 'bell.png', - 'eye' => $imgLoc . 'eye.png', - 'vorsicht' => $imgLoc . 'exclamation.png', - 'export' => $imgLoc . 'export.png', - 'export_import' => $imgLoc . 'export_import.png', - 'execute' => $imgLoc . 'lightning.png', - 'executed' => $imgLoc . 'lightning.png', - 'exec_icon' => $imgLoc . 'exec_icon.png', - 'exec_passed' => $imgLoc . 'emoticon_smile.png', - 'exec_failed' => $imgLoc . 'emoticon_unhappy.png', - 'exec_blocked' => $imgLoc . 'emoticon_surprised.png', - 'execution' => $imgLoc . 'controller.png', - 'execution_order' => $imgLoc . 'timeline_marker.png', - 'execution_duration' => $imgLoc . 'hourglass.png', - 'export_excel' => $imgLoc . 'page_excel.png', - 'export_for_results_import' => $imgLoc . 'brick_go.png', - 'ghost_item' => $imgLoc . 'ghost16x16.png', - 'user_group' => $imgLoc . 'group.png', - 'heads_up' => $imgLoc . 'lightbulb.png', - 'help' => $imgLoc . 'question.gif', - 'history' => $imgLoc . 'history.png', - 'history_small' => $imgLoc . 'history_small.png', - 'home' => $imgLoc . 'application_home.png', - 'import' => $imgLoc . 'door_in.png', - 'import_results' => $imgLoc . 'monitor_lightning.png', - 'inactive' => $imgLoc . 'flag_yellow.png', - 'info' => $imgLoc . 'question.gif', - 'info_small' => $imgLoc . 'information_small.png', - 'insert_step' => $imgLoc . 'insert_step.png', - 'item_link' => $imgLoc . 'folder_link.png', - 'link_to_report' => $imgLoc . 'link.png', - 'lock' => $imgLoc . 'lock.png', - 'lock_open' => $imgLoc . 'lock_open.png', - 'log_message' => $imgLoc . 'history.png', - 'log_message_small' => $imgLoc . 'history_small.png', - 'logout' => $imgLoc . 'computer_go.png', - 'magnifier' => $imgLoc . 'magnifier.png', - 'move_copy' => $imgLoc . 'application_double.png', - 'new_f2_16' => $imgLoc . 'new_f2_16.png', - 'note_edit' => $imgLoc . 'note_edit.png', - 'note_edit_greyed' => $imgLoc . 'note_edit_greyed.png', - 'on' => $imgLoc . 'lightbulb.png', - 'off' => $imgLoc . 'lightbulb_off.png', - 'order_alpha' => $imgLoc . 'style.png', - 'plugins' => $imgLoc . 'connect.png', - 'public' => $imgLoc . 'door_open.png', - 'private' => $imgLoc . 'door.png', - 'remove' => $imgLoc . 'delete.png', - 'reorder' => $imgLoc . 'arrow_switch.png', - 'report' => $imgLoc . 'report.png', - 'report_word' => $imgLoc . 'page_word.png', - 'requirements' => $imgLoc . 'cart.png', - 'resequence' => $imgLoc . 'control_equalizer.png', - 'reset' => $imgLoc . 'arrow_undo.png', - 'saveForBaseline' => $imgLoc . 'lock.png', - 'summary_small' => $imgLoc . 'information_small.png', - 'sort' => $imgLoc . 'sort_hint.png', - 'steps' => $imgLoc . 'bricks.png', - 'table' => $imgLoc . 'application_view_columns.png', - 'testcases_table_view' => $imgLoc . 'application_view_columns.png', - 'testcase_execution_type_automatic' => $imgLoc . 'bullet_wrench.png', - 'testcase_execution_type_manual' => $imgLoc . 'user.png', - 'test_specification' => $imgLoc . 'chart_organisation.png', - 'toggle_all' => $imgLoc .'toggle_all.gif', - 'user' => $imgLoc . 'user.png', - 'upload' => $imgLoc . 'upload_16.png', - 'upload_greyed' => $imgLoc . 'upload_16_greyed.png', - 'warning' => $imgLoc . 'error_triangle.png', - 'wrench' => $imgLoc . 'wrench.png', - 'test_status_not_run' => $imgLoc . 'test_status_not_run.png', - 'test_status_passed' => $imgLoc . 'test_status_passed.png', - 'test_status_failed' => $imgLoc . 'test_status_failed.png', - 'test_status_blocked' => $imgLoc . 'test_status_blocked.png', - 'test_status_passed_next' => $imgLoc . 'test_status_passed_next.png', - 'test_status_failed_next' => $imgLoc . 'test_status_failed_next.png', - 'test_status_blocked_next' => $imgLoc . 'test_status_blocked_next.png', - 'keyword_add' => $imgLoc . 'tag_blue_add.png'); - - $imi = config_get('images'); - if(count($imi) >0) { - $dummy = array_merge($dummy,$imi); - } - return $dummy; - } - -} diff --git a/lib/functions/tree.class.php b/lib/functions/tree.class.php index f53a22fd16..6307329c6e 100644 --- a/lib/functions/tree.class.php +++ b/lib/functions/tree.class.php @@ -1,1710 +1,1658 @@ - build - var $node_types = array( 1 => 'testproject','testsuite', - 'testcase','tcversion','testplan', - 'requirement_spec','requirement','req_version', - 'testcase_step','req_revision','requirement_spec_revision', - 'build'); - - // key: node type id, value: class name - var $class_name = array( 1 => 'testproject','testsuite', - 'testcase',null,'testplan', - 'requirement_spec_mgr','requirement_mgr',null, - null,null,null,null); - - var $nodeWithoutClass = null; - - var $node_descr_id = array(); - - - // Order here means NOTHING - var $node_tables_by = array('id' => array(), - 'name' => - array('testproject' => 'testprojects', - 'testsuite' => 'testsuites', - 'testplan' => 'testplans', - 'testcase' => 'testcases', - 'tcversion' => 'tcversions', - 'requirement_spec' =>'req_specs', - 'requirement' => 'requirements', - 'req_version' => 'req_versions', - 'req_revision' => 'req_versions', - 'requirement_spec_revision' => 'req_specs_revisions')); - - var $node_tables; - - var $ROOT_NODE_TYPE_ID = 1; - var $ROOT_NODE_PARENT_ID = NULL; - - /** @var resource database handler */ - var $db; - - /** - * Class costructor - * @param resource &$db reference to database handler - */ - function __construct(&$db) - { - if( !is_object($db) ) - { - $msg = __METHOD__ . ' :: FATAL Error $db IS NOT AN Object'; - throw new Exception($msg); - } - - parent::__construct(); - $this->db = &$db; - $this->object_table = $this->tables['nodes_hierarchy']; - - $this->node_tables = $this->node_tables_by['name']; - $this->node_descr_id = array_flip($this->node_types); - foreach($this->node_tables_by['name'] as $key => $tbl) - { - $this->node_tables_by['id'][$this->node_descr_id[$key]] = $tbl; - } - - $nodeCodeId = array_flip($this->node_types); - $this->nodeWithoutClass[$nodeCodeId['requirement_spec_revision']] = 'deleted when reqspec is deleted'; - - - } - - /** - * get info from node_types table, regarding node types that can be used in a tree. - * - * @return array map - * key: description: single human friendly string describing node type - * value: numeric code used to identify a node type - * - */ - function get_available_node_types() { - static $nodeTypes; - if( !$nodeTypes ) { - $sql = " SELECT * FROM {$this->tables['node_types']} "; - $nodeTypes = $this->db->fetchColumnsIntoMap($sql,'description','id'); - } - return $nodeTypes; - } - - /** - * creates a new root node in the hierarchy table. - * root node is tree starting point. - * - * @param string $name node name; default='' - * @return integer node ID - */ - function new_root_node($name = '') - { - $this->new_node(null,$this->ROOT_NODE_TYPE_ID,$name,1); - return $this->db->insert_id($this->object_table); - } - - /* - function: new_node - creates a new node in the hierarchy table. - root node is tree starting point. - - args : parent_id: node id of new node parent - node_type_id: node type - [name]: node name. default='' - [node_order]= order on tree structure. default=0 - [node_id]= id to assign to new node, if you don't want - id bein created automatically. - default=0 -> id must be created automatically. - - returns: node_id of the new node created - - */ - function new_node($parent_id,$node_type_id,$name='',$node_order=0,$node_id=0) - { - $sql = "INSERT INTO {$this->object_table} " . - "(name,node_type_id,node_order"; - - $values=" VALUES('" . $this->db->prepare_string($name). "'," . - " {$node_type_id}," . intval($node_order); - if ($node_id) - { - $sql .= ",id"; - $values .= ",{$node_id}"; - } - - if(is_null($parent_id)) - { - $sql .= ") {$values} )"; - } - else - { - $sql .= ",parent_id) {$values},{$parent_id})"; - } - - $this->db->exec_query($sql); - return ($this->db->insert_id($this->object_table)); - } - - /* - get all node hierarchy info from hierarchy table - returns: node_id of the new node created - - - */ - /* - function: get_node_hierarchy_info - returns the row from nodes_hierarchy table that has - node_id as id. - - get all node hierarchy info from hierarchy table - - args : node_id: node id - can be an array - [parent_id] - - returns: - - */ - function get_node_hierarchy_info($node_id,$parent_id = null,$options=null) - { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $opt = array('nodeTypeID' => null, 'nodeType' => null, - 'fields' => 'id,name,parent_id,node_type_id,node_order'); - $opt = array_merge($opt, (array)$options); - $sql = "SELECT {$opt['fields']} " . - "FROM {$this->object_table} WHERE id"; - - $result=null; - - if( is_array($node_id) ) - { - $sql .= " IN (" . implode(",",$node_id) . ") "; - $result=$this->db->fetchRowsIntoMap($sql,'id'); - } - else - { - $sql .= "= " . intval($node_id); - if( !is_null($parent_id) ) - { - $sql .= " AND parent_id=" . intval($parent_id); - } - - if( !is_null($opt['nodeTypeID']) ) - { - $sql .= " AND node_type_id=" . intval($opt['nodeTypeID']); - } - - if( !is_null($opt['nodeType']) ) - { - $sql .= " AND node_type_id=" . intval($this->node_descr_id[$opt['nodeType']]); - } - - $rs = $this->db->get_recordset($sql); - $result = !is_null($rs) ? $rs[0] : null; - } - return $result; - } - - /* - function: get_subtree_list() - get a string representing a list, where elements are separated - by comma, with all nodes in tree starting on node_id. - node is can be considered as root of subtree. - - args : node_id: root of subtree - node_type_id: null => no filter - if present ONLY NODES OF this type will be ANALIZED and traversed - Example: - TREE - |__ TSUITE_1 - | - |__TSUITE_2 - | |__TC_XZ - | - |__TC1 - |__TC2 - - node_type_id = TC and ROOT=Tree => output=NULL - node_type_id = TC and ROOT=TSUITE_1 => output=TC1,TC2 - - - output: null => list, not null => array - - - returns: output=null => list (string with nodes_id, using ',' as list separator). - output != null => array - - */ - function get_subtree_list($node_id,$node_type_id=null,$output=null) { - $nodes = array(); - $this->_get_subtree_list($node_id,$nodes,$node_type_id); - $node_list = is_null($output) ? implode(',',$nodes) : $nodes; - return($node_list); - } - - - /* - function: _get_subtree_list() - private function (name start with _), that using recursion - get an array with all nodes in tree starting on node_id. - node is can be considered as root of subtree. - - - args : node_id: root of subtree - - returns: array with nodes_id - - */ - function _get_subtree_list($node_id,&$node_list,$node_type_id=null) - { - $sql = "SELECT id from {$this->object_table} WHERE parent_id = {$node_id}"; - if( !is_null($node_type_id) ) - { - $sql .= " AND node_type_id = {$node_type_id} "; - } - $result = $this->db->exec_query($sql); - - if (!$result || !$this->db->num_rows($result)) - { - return; - } - - while($row = $this->db->fetch_array($result)) - { - $node_list[] = $row['id']; - $this->_get_subtree_list($row['id'],$node_list,$node_type_id); - } - } - - /* - function: delete_subtree - delete all element on tree structure that forms a subtree - that has as root or starting point node_id. - - args : node_id: root of subtree - - returns: array with nodes_id - - */ - function delete_subtree($node_id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $children = $this->get_subtree_list($node_id); - $id2del = $node_id; - if($children != "") - { - $id2del .= ",{$children}"; - } - $sql = "/* $debugMsg */ DELETE FROM {$this->object_table} WHERE id IN ({$id2del})"; - - $result = $this->db->exec_query($sql); - } - - - /* - function: get_path - get list of nodes to traverse when you want to move - from node A (node at level N) to node B (node at level M), - where MUST BE ALLWAYS M < N, and remembering that level for root node is the minimun. - This means path on tree backwards (to the upper levels). - An array is used to represent list. - Last array element contains data regarding Node A, first element (element with index 0) - is data regarding child of node B. - What data is returned depends on value of optional argument 'format'. - - Attention: - 1 - destination node (node B) will be NOT INCLUDED in result. - 2 - This is refactoring of original get_path method. - - args : node_id: start of path - [to_node_id]: destination node. default null -> path to tree root. - [format]: default 'full' - defines type of elements of result array. - - format='full' - Element is a map with following keys: - id - parent_id - node_type_id - node_order - node_table - name - - Example - Is tree is : - - null - \ - id=1 <--- Tree Root - | - + ------+ - / \ \ - id=9 id=2 id=8 - \ - id=3 - \ - id=4 - - - get_path(4), returns: - - ( - [0] => Array([id] => 2 - [parent_id] => 1 - [node_type_id] => 2 - [node_order] => 1 - [node_table] => testsuites - [name] => TS1) - - [1] => Array([id] => 3 - [parent_id] => 2 - [node_type_id] => 2 - [node_order] => 1 - [node_table] => testsuites - [name] => TS2) - - [2] => Array([id] => 4 - [parent_id] => 3 - [node_type_id] => 3 - [node_order] => 0 - [node_table] => testcases - [name] => TC1) - ) - - - - format='simple' - every element is a number=PARENT ID, array index = value - For the above example result will be: - ( - [1] => 1 - [2] => 2 - [3] => 3 - ) - - - - returns: array - - */ - function get_path($node_id,$to_node_id = null,$format = 'full') { - $the_path = array(); - $this->_get_path($node_id,$the_path,$to_node_id,$format); - if( !is_null($the_path) && count($the_path) > 0 ) { - $the_path = array_reverse($the_path); - } - return $the_path; - } - - /** - * - */ - function get_path_new($node_id,$to_node_id = null,$format = 'full') { - $the_path = array(); - $trip=''; - $matrioska = array(); - $this->_get_path($node_id,$the_path,$to_node_id,$format); - - if( !is_null($the_path) && ($loop2do=count($the_path)) > 0 ) { - $the_path=array_reverse($the_path); - $matrioska = $the_path[0]; - $matrioska['childNodes']=array(); - $target = &$matrioska['childNodes']; - - $trip = ''; - for($idx=0; $idx < ($loop2do-1); $idx++) - { - $trip[] = $the_path[$idx]['id']; // . "({$idx})"; - $target[0] = $the_path[$idx+1]; - if($the_path[$idx+1]['node_table'] != 'testcases') - { - $target = &$target[0]['childNodes']; - } - } - } - - return array($trip,$the_path); - } - - - - /* - function: _get_path - This is refactoring of original get_path method. - Attention: - returns node in inverse order, that was done for original get_path - - args : node_id: start of path - node_list: passed by reference, to build the result. - [to_node_id]: destination node. default null -> path to tree root. - [format]: default 'full' - - returns: array - */ - function _get_path($node_id,&$node_list,$to_node_id=null,$format='full') - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - // look up the parent of this node - $sql = "/* $debugMsg */ " . - " SELECT id,name,parent_id,node_type_id,node_order " . - " FROM {$this->object_table} WHERE id = " . intval($node_id); - - $result = $this->db->exec_query($sql); - if( $this->db->num_rows($result) == 0 ) { - $node_list=null; - return; - } - - while ( $row = $this->db->fetch_array($result) ) { - // check & abort - if ($row['parent_id'] == $row['id']) { - throw new Exception("id = parent_id = " . $row['id'], 1); - } - - // only continue if this $node isn't the root node - // (that's the node with no parent) - if ($row['parent_id'] != '' && $row['id'] != $to_node_id) { - // the last part of the path to $node, is the name - // of the parent of $node - switch($format) { - case 'full': - $row['node_table'] = $this->node_tables_by['id'][$row['node_type_id']]; - $node_list[] = $row; - break; - - case 'simple': - // Warning: starting node is NOT INCLUDED in node_list - $node_list[$row['parent_id']] = $row['parent_id']; - break; - - case 'points': - $node_list[] = $row['id']; - break; - - case 'simple_me': - if( is_null($node_list) ) { - $node_list[$row['id']] = $row['id']; - } else { - $node_list[$row['parent_id']] = $row['parent_id']; - } - break; - - case 'name': - $node_list[] = $row['name']; - break; - - } - - // we should add the path to the parent of this node to the path - $this->_get_path($row['parent_id'],$node_list,$to_node_id,$format); - } - } - } - - - - - /* - function: change_parent - change node parent, using this method you implement move operation. - - args : node_id: node/nodes that need(s) to changed. - mixed type: single id or array containing set of id. - - parent_id: new parent - - returns: 1 -> operation OK - - */ - function change_parent($node_id, $parent_id) - { - $debugMsg='Class:' .__CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; - if( is_array($node_id) ) - { - $id_list = implode(",",$node_id); - $where_clause = " WHERE id IN ($id_list) "; - } - else - { - $where_clause=" WHERE id = {$node_id}"; - } - $sql = "/* $debugMsg */ UPDATE {$this->object_table} " . - " SET parent_id = " . $this->db->prepare_int($parent_id) . " {$where_clause}"; - - $result = $this->db->exec_query($sql); - - return $result ? 1 : 0; - } - - - /* - function: get_children - get nodes that have id as parent node. - Children can be filtering according to node type. - - args : id: node - [exclude_node_types]: map - key: verbose description of node type to exclude. - see get_available_node_types. - value: anything is ok - - returns: array of maps that contain children nodes. - map structure: - id - name - parent_id - node_type_id - node_order - node_table - - - */ - function get_children($id,$exclude_node_types=null,$opt=null) { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $my['opt'] = array('accessKey' => null); - $my['opt'] = array_merge($my['opt'], (array)$opt); - - $sql = "/* $debugMsg */ " . - " SELECT id,name,parent_id,node_type_id,node_order FROM {$this->object_table} " . - " WHERE parent_id = " . $this->db->prepare_int($id) . " ORDER BY node_order,id"; - - $node_list=array(); - $result = $this->db->exec_query($sql); - - if( $this->db->num_rows($result) == 0 ) - { - return(null); - } - - $xdx = 0; - while ( $row = $this->db->fetch_array($result) ) - { - if( !isset($exclude_node_types[$this->node_types[$row['node_type_id']]])) - { - $node_table = $this->node_tables_by['id'][$row['node_type_id']]; - - $ak = is_null($my['opt']['accessKey']) ? $xdx : $row[$my['opt']['accessKey']]; - $node_list[$ak] = array('id' => $row['id'], 'parent_id' => $row['parent_id'], - 'node_type_id' => $row['node_type_id'], - 'node_order' => $row['node_order'], - 'node_table' => $node_table,'name' => $row['name']); - $xdx++; - } - } - return $node_list; - } - - - /* - function: change_order_bulk - change order for all nodes is present in nodes array. - Order of node in tree, is set to position node has in nodes array. - - args : - nodes: array where value is node_id. Node order = node position on array - - returns: - - - */ - function change_order_bulk($nodes) - { - foreach($nodes as $order => $node_id) - { - $order = abs(intval($order)); - $node_id = intval($node_id); - $sql = "UPDATE {$this->object_table} SET node_order = {$order} WHERE id = {$node_id}"; - $result = $this->db->exec_query($sql); - } - } - - - /* - function: change_child_order - will change order of children of parent id, to position - choosen node on top or bottom of children. - - args: - parent_id: node used as root of a tree. - node_id: node which we want to reposition - $top_bottom: possible values 'top', 'bottom' - [exclude_node_types]: map - key: verbose description of node type to exclude. - see get_available_node_types. - value: anything is ok - - - returns: - - - */ - function change_child_order($parent_id,$node_id,$top_bottom,$exclude_node_types=null) - { - $node_type_filter=''; - if( !is_null($exclude_node_types) ) - { - $types=implode("','",array_keys($exclude_node_types)); - $node_type_filter=" AND NT.description NOT IN ('{$types}') "; - } - - $sql = " SELECT NH.id, NH.node_order, NH.name " . - " FROM {$this->object_table} NH, {$this->tables['node_types']} NT " . - " WHERE NH.node_type_id=NT.id " . - " AND NH.parent_id = {$parent_id} AND NH.id <> {$node_id} " . - $node_type_filter . - " ORDER BY NH.node_order,NH.id"; - $children=$this->db->get_recordset($sql); - - switch ($top_bottom) - { - case 'top': - $no[]=$node_id; - if( !is_null($children) ) - { - foreach($children as $key => $value) - { - $no[]=$value['id']; - } - } - break; - - case 'bottom': - $new_order=$this->getBottomOrder($parent_id)+1; - $no[$new_order]=$node_id; - break; - } - $this->change_order_bulk($no); - } - - /* - function: getBottomOrder - given a node id to be used as parent, returns the max(node_order) from the children nodes. - We consider this bottom order. - - args: parentID: - - returns: order - - */ - function getBottomOrder($parentID,$opt=null) { - $debugMsg='Class:' .__CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; - - $my['opt'] = array(); - $my['opt'] = array_merge(array('node_type' => null),(array)$opt); - - - $sql = "SELECT MAX(node_order) AS max_order" . - " FROM {$this->object_table} " . - " WHERE parent_id={$parentID} "; - - if(!is_null($my['opt']['node_type'])) { - $sql .= " AND node_type_id = " . $this->node_descr_id[$my['opt']['node_type']]; - } - - $sql .= " GROUP BY parent_id "; - $rs=$this->db->get_recordset($sql); - - return $rs[0]['max_order']; - } - - - - - /* - function: get_subtree - Giving a node_id, get the nodes that forma s subtree that - has node_id as root or starting point. - - Is possible to exclude: - branches that has as staring node, node of certain types. - children of some node types. - full branches. - - - args : - [filters] map with following keys - - [exclude_node_types]: map/hash. - default: null -> no exclusion filter will be applied. - Branches starting with nodes of type detailed, will not be - visited => no information will be returned. - key: verbose description of node type to exclude. - (see get_available_node_types). - value: can be any value, because is not used,anyway is suggested - to use 'exclude_me' as value. - - Example: - array('testplan' => 'exclude_me') - Node of type tesplan, will be excluded. - - - - [exclude_children_of]: map/hash - default: null -> no exclusion filter will be applied. - When traversing tree if the type of a node child, of node under analisys, - is contained in this map, traversing of branch starting with this child node - will not be done. - key: verbose description of node type to exclude. - (see get_available_node_types). - value: can be any value, because is not used,anyway is suggested - to use 'exclude_my_children' as value. - - Example: - array('testcase' => 'exclude_my_children') - Children of testcase nodes, (tcversion nodes) will be EXCLUDED. - - [exclude_branches]: map/hash. - default: null -> no exclusion filter will be applied. - key: node id. - value: anything is ok. - - When traversing tree branches that have these node is, will - not be visited => no information will be retrieved. - - - [additionalWhereClause]: sql filter to include in sql sentence used to retrieve nodes. - default: null -> no action taken. - - [family]: used to include guide the tree traversal. - map where key = node_id TO INCLUDE ON traversal - value = map where each key is a CHILD that HAS TO BE INCLUDED in return set. - - [options]: map with following keys - - [recursive]: changes structure of returned structure. - default: false -> a flat array will be generated - true -> a map with recursive structure will be generated. - - false returns array, every element is a map with following keys: - - id - parent_id - node_type_id - node_order - node_table - name - - - true returns a map, with only one element - key: childNodes. - value: array, that represents a tree branch. - Array elements are maps with following keys: - - id - parent_id - node_type_id - node_order - node_table - name - childNodes -> (array) - - - returns: array or map - - - */ - function get_subtree($node_id,$filters=null,$options=null) { - $my['filters'] = array('exclude_node_types' => null, 'exclude_children_of' => null, - 'exclude_branches' => null,'additionalWhereClause' => '', 'family' => null); - - $my['options'] = array('recursive' => false, 'order_cfg' => array("type" =>'spec_order'), - 'output' => 'essential', 'key_type' => 'std', 'addJoin' => '', 'addFields' => ''); - - // Cast to array to handle $options = null - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - $the_subtree = array(); - - // Generate NOT IN CLAUSE to exclude some node types - // $not_in_clause = $my['filters']['additionalWhereClause']; - if(!is_null($my['filters']['exclude_node_types'])) - { - $exclude = array(); - foreach($my['filters']['exclude_node_types'] as $the_key => $elem) - { - $exclude[] = $this->node_descr_id[$the_key]; - } - $my['filters']['additionalWhereClause'] .= " AND node_type_id NOT IN (" . implode(",",$exclude) . ")"; - } - - $method2call = $my['options']['recursive'] ? '_get_subtree_rec' : '_get_subtree'; - $qnum = $this->$method2call($node_id,$the_subtree,$my['filters'],$my['options']); - return $the_subtree; - } - - /** - * - */ - function _get_subtree($node_id,&$node_list,$filters = null, $options = null) - { - static $my; - if(!$my) - { - $my['filters'] = array('exclude_children_of' => null,'exclude_branches' => null, - 'additionalWhereClause' => '', 'family' => null); - - $my['options'] = array('order_cfg' => array("type" =>'spec_order'), - 'output' => 'full', 'key_type' => 'std', - 'addJoin' => '', 'addFields' => ''); - - } - - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - switch($my['options']['order_cfg']['type']) - { - case 'spec_order': - $sql = " SELECT id,name,parent_id,node_type_id,node_order " . - $my['options']['addFields'] . - " FROM {$this->object_table} {$my['options']['addJoin']} " . - " WHERE parent_id = {$node_id} " . - " {$my['filters']['additionalWhereClause']}" . - " ORDER BY node_order,id"; - break; - - case 'rspec': - $sql = " SELECT OBT.id,name,parent_id,node_type_id,node_order," . - " RSPEC.doc_id " . - " FROM {$this->object_table} AS OBT " . - " JOIN {$this->tables['req_specs']} AS RSPEC " . - " ON RSPEC.id = OBT.id " . - " WHERE parent_id = {$node_id} " . - " {$my['filters']['additionalWhereClause']}" . - " ORDER BY node_order,OBT.id"; - break; - - case 'exec_order': - // REMEMBER THAT DISTINCT IS NOT NEEDED when you does UNION WITHOUT ALL - // - // First query get Nodes that ARE NOT test case => test suites - // Second query get the TEST CASES - // - $sql = "SELECT * FROM ( SELECT NH.node_order AS spec_order," . - " NH.node_order AS node_order, NH.id, NH.parent_id," . - " NH.name, NH.node_type_id, 0 AS tcversion_id" . - " FROM {$this->object_table} NH, {$this->tables['node_types']} NT" . - " WHERE parent_id = {$node_id}" . - " AND NH.node_type_id=NT.id" . - " AND NT.description <> 'testcase' {$my['filters']['additionalWhereClause']}" . - " UNION" . - " SELECT NHA.node_order AS spec_order, " . - " T.node_order AS node_order, NHA.id, NHA.parent_id, " . - " NHA.name, NHA.node_type_id, T.tcversion_id" . - " FROM {$this->object_table} NHA, {$this->object_table} NHB," . - " {$this->tables['testplan_tcversions']} T,{$this->tables['node_types']} NT" . - " WHERE NHA.id=NHB.parent_id " . - " AND NHA.node_type_id=NT.id" . - " AND NHB.id=T.tcversion_id " . - " AND NT.description = 'testcase'" . - " AND NHA.parent_id = {$node_id}" . - " AND T.testplan_id = {$my['options']['order_cfg']['tplan_id']}) AC" . - " ORDER BY node_order,spec_order,id"; - break; - - case 'req_order': - $sql = " SELECT NH_TC.id,NH_TC.name,NH_TC.parent_id," . - " NH_TC.node_type_id,NH_TC.node_order " . - " {$my['options']['addFields']}" . - " FROM {$this->object_table} AS NH_TC " . - " {$my['options']['addJoin']} " . - " JOIN {$this->tables['req_coverage']} RC " . - " ON RC.testcase_id = NH_TC.id " . - " WHERE RC.req_id = {$node_id} " . - " {$my['filters']['additionalWhereClause']}" . - " ORDER BY NH_TC.node_order,NH_TC.id"; - break; - - } - - $result = $this->db->exec_query($sql); - - if( $this->db->num_rows($result) == 0 ) - { - return; - } - - while ( $row = $this->db->fetch_array($result) ) - { - if( !isset($my['filters']['exclude_branches'][$row['id']]) ) - { - - $node_table = $this->node_tables[$this->node_types[$row['node_type_id']]]; - - - switch($my['options']['output']) - { - case 'id': - $node_list[] = $row['id']; - break; - - case 'essential': - $node_list[] = array('id' => $row['id'], - 'parent_id' => $row['parent_id'], - 'node_type_id' => $row['node_type_id'], - 'node_order' => $row['node_order'], - 'node_table' => $node_table, - 'name' => $row['name']); - break; - - case 'rspec': - $node_list[] = array('id' => $row['id'], - 'parent_id' => $row['parent_id'], - 'doc_id' => $row['doc_id'], - 'node_type_id' => $row['node_type_id'], - 'node_order' => $row['node_order'], - 'node_table' => $node_table, - 'name' => $row['name']); - break; - - - case 'full': - default: - // this choice - // 'tcversion_id' => (isset($row['parent_id']) ? $row['parent_id'] : -1), - // need to be documented and REVIEWED, because can generate confusion - $node_list[] = array('id' => $row['id'], - 'parent_id' => $row['parent_id'], - 'tcversion_id' => (isset($row['parent_id']) ? $row['parent_id'] : -1), - 'node_type_id' => $row['node_type_id'], - 'node_order' => $row['node_order'], - 'node_table' => $node_table, - 'name' => $row['name']); - break; - } - // Basically we use this because: - // 1. Sometimes we don't want the children if the parent is a testcase, - // due to the version management - // - // 2. Sometime we want to exclude all descendants (branch) of a node. - // - // [franciscom]: - // I think ( but I have no figures to backup my thoughts) doing this check and - // avoiding the function call is better that passing a condition that will result - // in a null result set. - // - // - if( !isset($my['filters']['exclude_children_of'][$this->node_types[$row['node_type_id']]]) && - !isset($my['filters']['exclude_branches'][$row['id']]) ) - { - $this->_get_subtree($row['id'],$node_list,$filters,$options); - } - } - } - } // function end - - /** - * - */ - function _get_subtree_rec($node_id,&$pnode,$filters = null, $options = null) - { - static $tcNodeTypeID; - static $qnum; - static $my; - static $platform_filter; - static $fclause; - static $exclude_branches; - static $exclude_children_of; - - if (!$tcNodeTypeID) - { - $tcNodeTypeID = $this->node_descr_id['testcase']; - - $qnum=0; - - $my['filters'] = array('exclude_children_of' => null,'exclude_branches' => null, - 'additionalWhereClause' => '', 'family' => null); - - $my['options'] = array('order_cfg' => array("type" =>'spec_order'),'key_type' => 'std', - 'remove_empty_nodes_of_type' => null); - - // Cast to array to handle $options = null - $my['filters'] = array_merge($my['filters'], (array)$filters); - $my['options'] = array_merge($my['options'], (array)$options); - - $platform_filter = ""; - if( isset($my['options']['order_cfg']['platform_id']) && - ($safe_pid = intval($my['options']['order_cfg']['platform_id']) ) > 0 ) - { - $platform_filter = " /* Platform filter */ " . - " AND T.platform_id = " . $safe_pid; - } - - $fclause = " AND node_type_id <> {$tcNodeTypeID} {$my['filters']['additionalWhereClause']} "; - - - if( !is_null($my['options']['remove_empty_nodes_of_type']) ) - { - // this way I can manage code or description - if( !is_numeric($my['options']['remove_empty_nodes_of_type']) ) - { - $my['options']['remove_empty_nodes_of_type'] = - $this->node_descr_id[$my['options']['remove_empty_nodes_of_type']]; - } - } - - - $exclude_branches = $my['filters']['exclude_branches']; - $exclude_children_of = $my['filters']['exclude_children_of']; - } - - switch($my['options']['order_cfg']['type']) - { - case 'spec_order': - $sql = " SELECT id,name,parent_id,node_type_id,node_order FROM {$this->object_table} " . - " WHERE parent_id = {$node_id} {$my['filters']['additionalWhereClause']}" . - " ORDER BY node_order,id"; - break; - - case 'exec_order': - // Hmmm, no action regarding platforms. is OK ?? - // - // REMEMBER THAT DISTINCT IS NOT NEEDED when you does UNION - // - // Important Notice: - // Second part of UNION, allows to get from nodes hierarchy, - // only test cases that has a version linked to test plan. - // - $sql="SELECT * FROM ( SELECT NH.node_order AS spec_order," . - " NH.node_order AS node_order, NH.id, NH.parent_id," . - " NH.name, NH.node_type_id, 0 AS tcversion_id " . - " FROM {$this->tables['nodes_hierarchy']} NH" . - " WHERE parent_id = {$node_id} {$fclause} " . - " UNION" . - " SELECT NHA.node_order AS spec_order, " . - " T.node_order AS node_order, NHA.id, NHA.parent_id, " . - " NHA.name, NHA.node_type_id, T.tcversion_id " . - " FROM {$this->tables['nodes_hierarchy']} NHA, " . - " {$this->tables['nodes_hierarchy']} NHB," . - " {$this->tables['testplan_tcversions']} T" . - " WHERE NHA.id=NHB.parent_id " . - " AND NHA.node_type_id = {$tcNodeTypeID}" . - " AND NHB.id=T.tcversion_id " . - " AND NHA.parent_id = {$node_id} {$platform_filter} " . - " AND T.testplan_id = {$my['options']['order_cfg']['tplan_id']}) AC" . - " ORDER BY node_order,spec_order,id"; - break; - - } - - $result = $this->db->exec_query($sql); - $qnum++; - while($row = $this->db->fetch_array($result)) - { - if(!isset($exclude_branches[$row['id']])) - { - switch($my['options']['key_type']) - { - case 'std': - $node = array('parent_id' => $row['parent_id'], - 'id' => $row['id'], - 'name' => $row['name'], - 'childNodes' => null, - 'node_table' => $this->node_tables_by['id'][$row['node_type_id']], - 'node_type_id' => $row['node_type_id'], - 'node_order' => $row['node_order']); - - if( isset($row['tcversion_id']) && $row['tcversion_id'] > 0) - { - $node['tcversion_id'] = $row['tcversion_id']; - } - break; - - case 'extjs': - $node = array('text' => $row['name'], - 'id' => $row['id'], - 'parent_id' => $row['parent_id'], - 'node_type_id' => $row['node_type_id'], - 'position' => $row['node_order'], - 'childNodes' => null, - 'leaf' => false); - - switch($this->node_types[$row['node_type_id']]) - { - case 'testproject': - case 'testsuite': - $node['childNodes'] = null; - break; - - case 'testcase': - $node['leaf'] = true; - break; - } - break; - } - - // Basically we use this because: - // 1. Sometimes we don't want the children if the parent is a testcase, - // due to the version management - // - // 2. Sometime we want to exclude all descendants (branch) of a node. - // - // [franciscom]: - // I think ( but I have no figures to backup my thoughts) doing this check and - // avoiding the function call is better that passing a condition that will result - // in a null result set. - // - // - if(!isset($exclude_children_of[$this->node_types[$row['node_type_id']]]) && - !isset($exclude_branches[$row['id']])) - { - $this->_get_subtree_rec($row['id'],$node,$my['filters'],$my['options']); - } - - // Have added this logic, because when export test plan will be developed - // having a test spec tree where test suites that do not contribute to test plan - // are pruned/removed is very important, to avoid additional processing - // - $doRemove = is_null($node['childNodes']) && - $node['node_type_id'] == $my['options']['remove_empty_nodes_of_type']; - - if(!$doRemove) - { - $pnode['childNodes'][] = $node; - } - } // if(!isset($exclude_branches[$rowID])) - } //while - return $qnum; - } - - /** - * function: get_full_path_verbose - * when path can not be found instead of null, anyway a map will be returned, with key=itemID value=NULL - * @internal revisions - **/ - function get_full_path_verbose(&$items,$options=null) { - $debugMsg='Class:' .__CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; - $goto_root=null; - $path_to=null; - $all_nodes=array(); - $path_format = 'simple'; - $output_format = 'simple'; - - if( !is_null($options) ) - { - // not a good solution, but Quick & Dirty - $path_format = isset($options['path_format']) ? $options['path_format'] : $path_format; - if( !isset($options['path_format']) ) - { - $path_format = isset($options['include_starting_point']) ? 'points' : $path_format; - } - $output_format = isset($options['output_format']) ? $options['output_format'] : $output_format; - } - - // according to count($items) we will try to optimize, sorry for magic number - if( count((array)$items) > 200) - { - $xitems = array_flip((array)$items); - $xsql = " SELECT parent_id,id " . - " FROM {$this->tables['nodes_hierarchy']} " . - " WHERE id IN (" . implode(',',array_keys($xitems)) . ")"; - - $xmen = $this->db->fetchRowsIntoMap($xsql,'parent_id',database::CUMULATIVE); - $all_nodes = array(); - foreach($xmen as $parent_id => &$children) - { - $paty = $this->get_path($parent_id,$goto_root,$path_format); - $paty[] = $parent_id; - - $all_nodes = array_merge($all_nodes,$paty); - foreach($children as &$item) - { - $path_to[$item['id']]['name'] = $stairway2heaven[$item['id']] = $paty; - $all_nodes[] = $item['id']; - } - } - unset($xmen); - } - else - { - foreach((array)$items as $item_id) - { - $stairway2heaven[$item_id] = $this->get_path($item_id,$goto_root,$path_format); - $path_to[$item_id]['name'] = $stairway2heaven[$item_id]; - $all_nodes = array_merge($all_nodes,(array)$path_to[$item_id]['name']); - } - } - - $status_ok = (!is_null($all_nodes) && count($all_nodes) > 0); - if( $status_ok ) - { - // get only different items, to get descriptions - $unique_nodes=implode(',',array_unique($all_nodes)); - - $sql="/* $debugMsg */ " . - " SELECT id,name FROM {$this->tables['nodes_hierarchy']} WHERE id IN ({$unique_nodes})"; - $decode=$this->db->fetchRowsIntoMap($sql,'id'); - - foreach($path_to as $key => $elem) - { - foreach($elem['name'] as $idx => $node_id) - { - $path_to[$key]['name'][$idx]=$decode[$node_id]['name']; - $path_to[$key]['node_id'][$idx]=$node_id; - } - } - unset($decode); - } - else - { - $path_to=null; - } - - if( !is_null($path_to) ) - { - switch ($output_format) - { - case 'path_as_string': - case 'stairway2heaven': - $flat_path=null; - foreach($path_to as $item_id => $pieces) - { - // remove root node - unset($pieces['name'][0]); - $flat_path[$item_id]=implode('/',$pieces['name']); - } - if($output_format == 'path_as_string') - { - $path_to = $flat_path; - } - else - { - $path_to = null; - $path_to['flat'] = $flat_path; - $path_to['staircase'] = $stairway2heaven; - } - break; - - case 'id_name': - break; - - case 'simple': - default: - $keySet = array_keys($path_to); - foreach($keySet as $key) - { - $path_to[$key] = $path_to[$key]['name']; - } - break; - } - } - unset($stairway2heaven); - return $path_to; - } - - - /** - * check if there is a sibbling node of same type that has same name - * - * @param string name: name to check - * @param int node_type_id: node types to check. - * @param int id: optional. exclude this node id from result set - * this is useful when you want to check for name - * existence during an update operation. - * Using id you get node parent, to get sibblings. - * If null parent_id argument must be present - * - * @param int parent_id: optional. Mandatory if id is null - * Used to get children nodes to check for - * name existence. - * - * - * @return map ret: ret['status']=1 if name exists - * 0 if name does not exist - * ret['msg']= localized message - * - */ - function nodeNameExists($name,$node_type_id,$id=null,$parent_id=null) - { - $debugMsg='Class:' .__CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; - $ret['status'] = 0; - $ret['msg'] = ''; - if( is_null($id) && is_null($parent_id) ) - { - $msg = $debugMsg . 'Error on call $id and $parent_id can not be both null'; - throw new Exception($msg); - } - - - $additionalFilters = ''; - $parentNodeID = intval($parent_id); - if( !is_null($id) ) - { - // Try to get parent id if not provided on method call. - if( is_null($parentNodeID) || $parentNodeID <= 0) - { - $sql = "/* {$debugMsg} */ " . - " SELECT parent_id FROM {$this->object_table} NHA " . - " WHERE NHA.id = " . $this->db->prepare_int($id); - $rs = $this->db->get_recordset($sql); - $parentNodeID = intval($rs[0]['parent_id']); - - } - $additionalFilters = " AND NHA.id <> " . $this->db->prepare_int($id); - } - - if( $parentNodeID <= 0) - { - $msg = $debugMsg . ' FATAL Error $parentNodeID can not be <= 0'; - throw new Exception($msg); - } - - - $sql = "/* {$debugMsg} */ " . - " SELECT count(0) AS qty FROM {$this->object_table} NHA " . - " WHERE NHA.node_type_id = {$node_type_id} " . - " AND NHA.name = '" . $this->db->prepare_string($name) . "'" . - " AND NHA.parent_id = " . $this->db->prepare_int($parentNodeID) . " {$additionalFilters} "; - - $rs = $this->db->get_recordset($sql); - if( $rs[0]['qty'] > 0) - { - $ret['status'] = 1; - $ret['msg'] = sprintf(lang_get('name_already_exists'),$name); - } - - return $ret; - } - - /** - * getTreeRoot() - * - */ - function getTreeRoot($node_id) { - $path = (array)$this->get_path($node_id); - $path_len = count($path); - $root_node_id = ($path_len > 0)? $path[0]['parent_id'] : $node_id; - return $root_node_id; - } - - - /** - * delete_subtree_objects() - * - * ATTENTION: subtree root node ($node_id?? or root_id?) IS NOT DELETED. - * - */ - function delete_subtree_objects($root_id,$node_id,$additionalWhereClause = '',$exclude_children_of = null, - $exclude_branches = null) - { - static $debugMsg; - if( is_null($debugMsg) ) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - } - - $sql = "/* $debugMsg */ SELECT NH.* FROM {$this->object_table} NH " . - " WHERE NH.parent_id = " . $this->db->prepare_int($node_id) . " {$additionalWhereClause} "; - $rs = $this->db->get_recordset($sql); - if( !is_null($rs) ) - { - foreach($rs as $row) - { - $rowID = $row['id']; - $nodeTypeID = $row['node_type_id']; - $nodeType = $this->node_types[$nodeTypeID]; - $nodeClassName = $this->class_name[$nodeTypeID]; - if(!isset($exclude_branches[$rowID])) - { - // Basically we use this because: - // 1. Sometimes we don't want the children if the parent is a testcase, - // due to the version management - // - // 2. Sometime we want to exclude all descendants (branch) of a node. - // - if(!isset($exclude_children_of[$nodeType]) && !isset($exclude_branches[$rowID])) - { - // I'm paying not having commented this well - // Why I've set root_id to null ? - // doing this when traversing a tree, containers under level of subtree root - // will not be deleted => and this seems to be wrong. - $this->delete_subtree_objects($root_id,$rowID,$additionalWhereClause,$exclude_children_of,$exclude_branches); - } - else - { - // For us in this method context this node is a leaf => just delete - if( !is_null($nodeClassName) ) - { - $item_mgr = new $nodeClassName($this->db); - $item_mgr->delete($rowID); - } - else if (isset($this->nodeWithoutClass[$nodeTypeID])) - { - } - else - { - // need to signal error - TO BE DONE - // echo '
    AUCH!!!'; - } - } - } // if(!isset($exclude_branches[$rowID])) - } //while - } - - // Must delete myself if I'm empty, only if I'm not subtree root. - // Done this way to avoid infinte recursion for some type of nodes - // that use this method as it's delete method. (example testproject). - - // Hmmm, need to recheck if this condition is ok - // - if( !is_null($root_id) && ($node_id != $root_id) ) - { - $children = (array)$this->db->get_recordset($sql); - if( count($children) == 0 ) - { - $sql2 = "/* $debugMsg */ SELECT NH.* FROM {$this->object_table} NH " . - " WHERE NH.id = " . $this->db->prepare_int($node_id); - $node_info = $this->db->get_recordset($sql2); - if( isset($this->class_name[$node_info[0]['node_type_id']]) ) - { - $className = $this->class_name[$node_info[0]['node_type_id']]; - if( !is_null($className) ) - { - $item_mgr = new $className($this->db); - $item_mgr->delete($node_id); - } - } - else if (isset($this->nodeWithoutClass[$node_info[0]['node_type_id']])) - { - } - else - { - // need to signal error - TO BE DONE - // echo '
    AUCH!!!'; - } - } - } // if( $node_id != $root_id ) - } - - - /* - - [$mode]: dotted -> $level number of dot characters are appended to - the left of item name to create an indent effect. - Level indicates on what tree layer item is positioned. - Example: - - null - \ - id=1 <--- Tree Root = Level 0 - | - + ------+ - / \ \ - id=9 id=2 id=8 <----- Level 1 - \ - id=3 <----- Level 2 - \ - id=4 <----- Level 3 - - - key: item id (= node id on tree). - value: every array element is an string, containing item name. - - Result example: - - 2 .TS1 - 3 ..TS2 - 9 .20071014-16:22:07 TS1 - 10 ..TS2 - - - array -> key: item id (= node id on tree). - value: every array element is a map with the following keys - 'name', 'level' - - 2 array(name => 'TS1',level => 1) - 3 array(name => 'TS2',level => 2) - 9 array(name => '20071014-16:22:07 TS1',level =>1) - 10 array(name => 'TS2', level => 2) - - */ - function createHierarchyMap($array2map,$mode='dotted',$field2add=null) - { - $hmap=array(); - $the_level = 1; - $level = array(); - $pivot = $array2map[0]; - $addField = !is_null($field2add); - $mode = is_null($mode) ? 'dotted' : $mode; - - foreach($array2map as $elem) - { - $current = $elem; - if ($pivot['id'] == $current['parent_id']) - { - $the_level++; - $level[$current['parent_id']]=$the_level; - } - else if ($pivot['parent_id'] != $current['parent_id']) - { - $the_level = $level[$current['parent_id']]; - } - - switch($mode) - { - case 'dotted': - $hmap[$current['id']] = str_repeat('.',$the_level); - if($addField) - { - $hmap[$current['id']] .= sprintf($field2add['format'],$current[$field2add['field']]); - } - $hmap[$current['id']] .= $current['name']; - break; - - case 'array': - $str = ($addField ? $current[$field2add] : '') . $current['name']; - $hmap[$current['id']] = array('name' => $str, 'level' => $the_level); - break; - } - - // update pivot - $level[$current['parent_id']]= $the_level; - $pivot=$elem; - } - - return $hmap; - } - - /** - * getAllItemsID - * - * @internal revisions - * based on code from testproject->get_all_testcases_id - * - */ - function getAllItemsID($parentList,&$itemSet,$coupleTypes) - { - static $debugMsg; - if (!$debugMsg) - { - } - $sql = "/* $debugMsg */ " . - " SELECT id,node_type_id from {$this->tables['nodes_hierarchy']} " . - " WHERE parent_id IN ({$parentList})"; - $sql .= " AND node_type_id IN ({$coupleTypes['target']},{$coupleTypes['container']}) "; - - $result = $this->db->exec_query($sql); - if ($result) - { - $containerSet = array(); - while($row = $this->db->fetch_array($result)) - { - if ($row['node_type_id'] == $coupleTypes['target']) - { - $itemSet[] = $row['id']; - } - else - { - $containerSet[] = $row['id']; - } - } - if (sizeof($containerSet)) - { - $containerSet = implode(",",$containerSet); - $this->getAllItemsID($containerSet,$itemSet,$coupleTypes); - } - } - } - - /** - * - */ - function getNodeByAttributes($attr) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $addJoin = ''; - $sql = "/* $debugMsg */ "; - $sql .= " SELECT NH_MAIN.id,NH_MAIN.parent_id,NH_MAIN.name,NH_MAIN.node_type_id " . - " FROM {$this->object_table} AS NH_MAIN " . - " JOIN {$this->tables['node_types']} AS NT ON NT.id = NH_MAIN.node_type_id "; - - $where = " WHERE 1=1 "; - foreach($attr as $key => $value) - { - switch($key) - { - case 'type': - $where .= " AND NT.description = '" . $this->db->prepare_string($value) . "'"; - break; - - case 'id': - $where .= " AND NH_MAIN.is = " . $this->db->prepare_int($value); - break; - - case 'name': - $where .= " AND NH_MAIN.name = '" . $this->db->prepare_string($value) . "'"; - break; - - case 'parent_id': - $where .= " AND NH_MAIN.parent_id = " . $this->db->prepare_int($value); - $addJoin = " JOIN {$this->object_table} AS NH_PARENT ON NH_PARENT.id = NH_MAIN.parent_id "; - break; - } - } - - $sql .= $addJoin . $where; - $rs = $this->db->fetchRowsIntoMap($sql,'id'); - return $rs; - } - - /** - * - */ - function getNodeType($id) { - $sql = " SELECT node_type_id, NT.description AS node_type + build + public $node_types = array( + 1 => 'testproject', + 'testsuite', + 'testcase', + 'tcversion', + 'testplan', + 'requirement_spec', + 'requirement', + 'req_version', + 'testcase_step', + 'req_revision', + 'requirement_spec_revision', + 'build' + ); + + // key: node type id, value: class name + public $class_name = array( + 1 => 'testproject', + 'testsuite', + 'testcase', + null, + 'testplan', + 'requirement_spec_mgr', + 'requirement_mgr', + null, + null, + null, + null, + null + ); + + private $nodeWithoutClass = null; + + public $node_descr_id = array(); + + // Order here means NOTHING + public $node_tables_by = array( + 'id' => array(), + 'name' => array( + 'testproject' => 'testprojects', + 'testsuite' => 'testsuites', + 'testplan' => 'testplans', + 'testcase' => 'testcases', + 'tcversion' => 'tcversions', + 'requirement_spec' => 'req_specs', + 'requirement' => 'requirements', + 'req_version' => 'req_versions', + 'req_revision' => 'req_versions', + 'requirement_spec_revision' => 'req_specs_revisions' + ) + ); + + private $node_tables; + + private $ROOT_NODE_TYPE_ID = 1; + + private $ROOT_NODE_PARENT_ID = null; + + /** @var resource database handler */ + public $db; + + /** + * Class costructor + * + * @param + * resource &$db reference to database handler + */ + public function __construct(&$db) + { + if (! is_object($db)) { + $msg = __METHOD__ . ' :: FATAL Error $db IS NOT AN Object'; + throw new Exception($msg); + } + + parent::__construct(); + $this->db = &$db; + $this->object_table = $this->tables['nodes_hierarchy']; + + $this->node_tables = $this->node_tables_by['name']; + $this->node_descr_id = array_flip($this->node_types); + foreach ($this->node_tables_by['name'] as $key => $tbl) { + $this->node_tables_by['id'][$this->node_descr_id[$key]] = $tbl; + } + + $nodeCodeId = array_flip($this->node_types); + $this->nodeWithoutClass[$nodeCodeId['requirement_spec_revision']] = 'deleted when reqspec is deleted'; + } + + /** + * get info from node_types table, regarding node types that can be used in a tree. + * + * @return array map + * key: description: single human friendly string describing node type + * value: numeric code used to identify a node type + * + */ + public function get_available_node_types() + { + static $nodeTypes; + if (! $nodeTypes) { + $sql = " SELECT * FROM {$this->tables['node_types']} "; + $nodeTypes = $this->db->fetchColumnsIntoMap($sql, 'description', + 'id'); + } + return $nodeTypes; + } + + /** + * creates a new root node in the hierarchy table. + * root node is tree starting point. + * + * @param string $name + * node name; default='' + * @return integer node ID + */ + public function new_root_node($name = '') + { + $this->new_node(null, $this->ROOT_NODE_TYPE_ID, $name, 1); + return $this->db->insert_id($this->object_table); + } + + /* + * function: new_node + * creates a new node in the hierarchy table. + * root node is tree starting point. + * + * args : parent_id: node id of new node parent + * node_type_id: node type + * [name]: node name. default='' + * [node_order]= order on tree structure. default=0 + * [node_id]= id to assign to new node, if you don't want + * id bein created automatically. + * default=0 -> id must be created automatically. + * + * returns: node_id of the new node created + * + */ + public function new_node($parent_id, $node_type_id, $name = '', + $node_order = 0, $node_id = 0) + { + $sql = "INSERT INTO {$this->object_table} " . + "(name,node_type_id,node_order"; + + $values = " VALUES('" . $this->db->prepare_string($name) . "'," . + " {$node_type_id}," . intval($node_order); + if ($node_id) { + $sql .= ",id"; + $values .= ",{$node_id}"; + } + + if (is_null($parent_id)) { + $sql .= ") {$values} )"; + } else { + $sql .= ",parent_id) {$values},{$parent_id})"; + } + + $this->db->exec_query($sql); + return $this->db->insert_id($this->object_table); + } + + /* + * get all node hierarchy info from hierarchy table + * function: get_node_hierarchy_info + * returns the row from nodes_hierarchy table that has + * node_id as id. + * + * get all node hierarchy info from hierarchy table + * + * args : node_id: node id + * can be an array + * [parent_id] + * + * returns: node_id of the new node created + */ + public function get_node_hierarchy_info($node_id, $parent_id = null, + $options = null) + { + $opt = array( + 'nodeTypeID' => null, + 'nodeType' => null, + 'fields' => 'id,name,parent_id,node_type_id,node_order' + ); + $opt = array_merge($opt, (array) $options); + $sql = "SELECT {$opt['fields']} " . "FROM {$this->object_table} WHERE id"; + + $result = null; + + if (is_array($node_id)) { + $sql .= " IN (" . implode(",", $node_id) . ") "; + $result = $this->db->fetchRowsIntoMap($sql, 'id'); + } else { + $sql .= "= " . intval($node_id); + if (! is_null($parent_id)) { + $sql .= " AND parent_id=" . intval($parent_id); + } + + if (! is_null($opt['nodeTypeID'])) { + $sql .= " AND node_type_id=" . intval($opt['nodeTypeID']); + } + + if (! is_null($opt['nodeType'])) { + $sql .= " AND node_type_id=" . + intval($this->node_descr_id[$opt['nodeType']]); + } + + $rs = $this->db->get_recordset($sql); + $result = ! is_null($rs) ? $rs[0] : null; + } + return $result; + } + + /* + * function: get_subtree_list() + * get a string representing a list, where elements are separated + * by comma, with all nodes in tree starting on node_id. + * node is can be considered as root of subtree. + * + * args : node_id: root of subtree + * node_type_id: null => no filter + * if present ONLY NODES OF this type will be ANALIZED and traversed + * Example: + * TREE + * |__ TSUITE_1 + * | + * |__TSUITE_2 + * | |__TC_XZ + * | + * |__TC1 + * |__TC2 + * + * node_type_id = TC and ROOT=Tree => output=NULL + * node_type_id = TC and ROOT=TSUITE_1 => output=TC1,TC2 + * + * output: null => list, not null => array + * + * returns: output=null => list (string with nodes_id, using ',' as list separator). + * output != null => array + * + */ + public function get_subtree_list($node_id, $node_type_id = null, + $output = null) + { + $nodes = array(); + $this->_get_subtree_list($node_id, $nodes, $node_type_id); + return is_null($output) ? implode(',', $nodes) : $nodes; + } + + /* + * function: _get_subtree_list() + * private function (name start with _), that using recursion + * get an array with all nodes in tree starting on node_id. + * node is can be considered as root of subtree. + * + * args : node_id: root of subtree + * + * returns: array with nodes_i + */ + private function _get_subtree_list($node_id, &$node_list, + $node_type_id = null) + { + $sql = "SELECT id from {$this->object_table} WHERE parent_id = {$node_id}"; + if (! is_null($node_type_id)) { + $sql .= " AND node_type_id = {$node_type_id} "; + } + $result = $this->db->exec_query($sql); + + if (! $result || ! $this->db->num_rows($result)) { + return; + } + + while ($row = $this->db->fetch_array($result)) { + $node_list[] = $row['id']; + $this->_get_subtree_list($row['id'], $node_list, $node_type_id); + } + } + + /* + * function: delete_subtree + * delete all element on tree structure that forms a subtree + * that has as root or starting point node_id. + * + * args : node_id: root of subtree + * + * returns: array with nodes_id + */ + public function delete_subtree($node_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $children = $this->get_subtree_list($node_id); + $id2del = $node_id; + if ($children != "") { + $id2del .= ",{$children}"; + } + $sql = "/* $debugMsg */ DELETE FROM {$this->object_table} WHERE id IN ({$id2del})"; + + $this->db->exec_query($sql); + } + + /* + * function: get_path + * get list of nodes to traverse when you want to move + * from node A (node at level N) to node B (node at level M), + * where MUST BE ALLWAYS M < N, and remembering that level for root node is the minimun. + * This means path on tree backwards (to the upper levels). + * An array is used to represent list. + * Last array element contains data regarding Node A, first element (element with index 0) + * is data regarding child of node B. + * What data is returned depends on value of optional argument 'format'. + * + * Attention: + * 1 - destination node (node B) will be NOT INCLUDED in result. + * 2 - This is refactoring of original get_path method. + * + * args : node_id: start of path + * [to_node_id]: destination node. default null -> path to tree root. + * [format]: default 'full' + * defines type of elements of result array. + * + * format='full' + * Element is a map with following keys: + * id + * parent_id + * node_type_id + * node_order + * node_table + * name + * + * Example + * Is tree is : + * + * null + * \ + * id=1 <--- Tree Root + * | + * + ------+ + * / \ \ + * id=9 id=2 id=8 + * \ + * id=3 + * \ + * id=4 + * + * + * get_path(4), returns: + * + * ( + * [0] => Array([id] => 2 + * [parent_id] => 1 + * [node_type_id] => 2 + * [node_order] => 1 + * [node_table] => testsuites + * [name] => TS1) + * + * [1] => Array([id] => 3 + * [parent_id] => 2 + * [node_type_id] => 2 + * [node_order] => 1 + * [node_table] => testsuites + * [name] => TS2) + * + * [2] => Array([id] => 4 + * [parent_id] => 3 + * [node_type_id] => 3 + * [node_order] => 0 + * [node_table] => testcases + * [name] => TC1) + * ) + * + * + * + * format='simple' + * every element is a number=PARENT ID, array index = value + * For the above example result will be: + * ( + * [1] => 1 + * [2] => 2 + * [3] => 3 + * ) + * + * + * + * returns: array + */ + public function get_path($node_id, $to_node_id = null, $format = 'full') + { + $the_path = array(); + $this->_get_path($node_id, $the_path, $to_node_id, $format); + if (! is_null($the_path) && count($the_path) > 0) { + $the_path = array_reverse($the_path); + } + return $the_path; + } + + /** + */ + private function get_path_new($node_id, $to_node_id = null, $format = 'full') + { + $the_path = array(); + $trip = ''; + $matrioska = array(); + $this->_get_path($node_id, $the_path, $to_node_id, $format); + + if (! is_null($the_path) && ($loop2do = count($the_path)) > 0) { + $the_path = array_reverse($the_path); + $matrioska = $the_path[0]; + $matrioska['childNodes'] = array(); + $target = &$matrioska['childNodes']; + + $trip = ''; + for ($idx = 0; $idx < ($loop2do - 1); $idx ++) { + $trip[] = $the_path[$idx]['id']; // . "({$idx})"; + $target[0] = $the_path[$idx + 1]; + if ($the_path[$idx + 1]['node_table'] != 'testcases') { + $target = &$target[0]['childNodes']; + } + } + } + + return array( + $trip, + $the_path + ); + } + + /* + * function: _get_path + * This is refactoring of original get_path method. + * Attention: + * returns node in inverse order, that was done for original get_path + * + * args : node_id: start of path + * node_list: passed by reference, to build the result. + * [to_node_id]: destination node. default null -> path to tree root. + * [format]: default 'full' + * + * returns: array + */ + private function _get_path($node_id, &$node_list, $to_node_id = null, + $format = 'full') + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + // look up the parent of this node + $sql = "/* $debugMsg */ " . + " SELECT id,name,parent_id,node_type_id,node_order " . + " FROM {$this->object_table} WHERE id = " . intval($node_id); + + $result = $this->db->exec_query($sql); + if ($this->db->num_rows($result) == 0) { + $node_list = null; + return; + } + + while ($row = $this->db->fetch_array($result)) { + // check & abort + if ($row['parent_id'] == $row['id']) { + throw new Exception("id = parent_id = " . $row['id'], 1); + } + + // only continue if this $node isn't the root node + // (that's the node with no parent) + if ($row['parent_id'] != '' && $row['id'] != $to_node_id) { + // the last part of the path to $node, is the name + // of the parent of $node + switch ($format) { + case 'full': + $row['node_table'] = $this->node_tables_by['id'][$row['node_type_id']]; + $node_list[] = $row; + break; + + case 'simple': + // Warning: starting node is NOT INCLUDED in node_list + $node_list[$row['parent_id']] = $row['parent_id']; + break; + + case 'points': + $node_list[] = $row['id']; + break; + + case 'simple_me': + if (is_null($node_list)) { + $node_list[$row['id']] = $row['id']; + } else { + $node_list[$row['parent_id']] = $row['parent_id']; + } + break; + + case 'name': + $node_list[] = $row['name']; + break; + } + + // we should add the path to the parent of this node to the path + $this->_get_path($row['parent_id'], $node_list, $to_node_id, + $format); + } + } + } + + /* + * function: change_parent + * change node parent, using this method you implement move operation. + * + * args : node_id: node/nodes that need(s) to changed. + * mixed type: single id or array containing set of id. + * + * parent_id: new parent + * + * returns: 1 -> operation OK + */ + public function change_parent($node_id, $parent_id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; + + if (is_array($node_id)) { + $safeSet = array_map('intval', $node_id); + $id_list = implode(",", $safeSet); + $where_clause = " WHERE id IN ($id_list) "; + } else { + $safe = intval($node_id); + if ($safe <= 0) { + throw new Exception("BAD node_id", 1); + } + $where_clause = " WHERE id = $safe"; + } + + $safeP = $this->db->prepare_int($parent_id); + $sql = "/* $debugMsg */ + UPDATE {$this->object_table} + SET parent_id = $safeP + $where_clause "; + + $result = $this->db->exec_query($sql); + + return $result ? 1 : 0; + } + + /* + * function: get_children + * get nodes that have id as parent node. + * Children can be filtering according to node type. + * + * args : id: node + * [exclude_node_types]: map + * key: verbose description of node type to exclude. + * see get_available_node_types. + * value: anything is ok + * + * returns: array of maps that contain children nodes. + * map structure: + * id + * name + * parent_id + * node_type_id + * node_order + * node_table + */ + public function get_children($id, $exclude_node_types = null, $opt = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $my['opt'] = array( + 'accessKey' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $sql = "/* $debugMsg */ " . + " SELECT id,name,parent_id,node_type_id,node_order FROM {$this->object_table} " . + " WHERE parent_id = " . $this->db->prepare_int($id) . + " ORDER BY node_order,id"; + + $node_list = array(); + $result = $this->db->exec_query($sql); + + if ($this->db->num_rows($result) == 0) { + return null; + } + + $xdx = 0; + while ($row = $this->db->fetch_array($result)) { + if (! isset( + $exclude_node_types[$this->node_types[$row['node_type_id']]])) { + $node_table = $this->node_tables_by['id'][$row['node_type_id']]; + + $ak = is_null($my['opt']['accessKey']) ? $xdx : $row[$my['opt']['accessKey']]; + $node_list[$ak] = array( + 'id' => $row['id'], + 'parent_id' => $row['parent_id'], + 'node_type_id' => $row['node_type_id'], + 'node_order' => $row['node_order'], + 'node_table' => $node_table, + 'name' => $row['name'] + ); + $xdx ++; + } + } + return $node_list; + } + + /* + * function: change_order_bulk + * change order for all nodes is present in nodes array. + * Order of node in tree, is set to position node has in nodes array. + * + * args : + * nodes: array where value is node_id. Node order = node position on array + * + * returns: - + */ + public function change_order_bulk($nodes) + { + foreach ($nodes as $order => $node_id) { + $order = abs(intval($order)); + $node_id = intval($node_id); + $sql = "UPDATE {$this->object_table} SET node_order = {$order} WHERE id = {$node_id}"; + $this->db->exec_query($sql); + } + } + + /* + * function: change_child_order + * will change order of children of parent id, to position + * choosen node on top or bottom of children. + * + * args: + * parent_id: node used as root of a tree. + * node_id: node which we want to reposition + * $top_bottom: possible values 'top', 'bottom' + * [exclude_node_types]: map + * key: verbose description of node type to exclude. + * see get_available_node_types. + * value: anything is ok + * returns: - + * + */ + public function change_child_order($parent_id, $node_id, $top_bottom, + $exclude_node_types = null) + { + $node_type_filter = ''; + if (! is_null($exclude_node_types)) { + $types = implode("','", array_keys($exclude_node_types)); + $node_type_filter = " AND NT.description NOT IN ('{$types}') "; + } + + $sql = " SELECT NH.id, NH.node_order, NH.name " . + " FROM {$this->object_table} NH, {$this->tables['node_types']} NT " . + " WHERE NH.node_type_id=NT.id " . + " AND NH.parent_id = {$parent_id} AND NH.id <> {$node_id} " . + $node_type_filter . " ORDER BY NH.node_order,NH.id"; + $children = $this->db->get_recordset($sql); + + switch ($top_bottom) { + case 'top': + $no[] = $node_id; + if (! is_null($children)) { + foreach ($children as $value) { + $no[] = $value['id']; + } + } + break; + + case 'bottom': + $new_order = $this->getBottomOrder($parent_id) + 1; + $no[$new_order] = $node_id; + break; + } + $this->change_order_bulk($no); + } + + /* + * function: getBottomOrder + * given a node id to be used as parent, returns the max(node_order) from the children nodes. + * We consider this bottom order. + * + * args: parentID: + * + * returns: order + */ + public function getBottomOrder($parentID, $opt = null) + { + $my['opt'] = array_merge([ + 'node_type' => null + ], (array) $opt); + + $sql = "SELECT MAX(node_order) AS max_order" . + " FROM {$this->object_table} " . " WHERE parent_id={$parentID} "; + + if (! is_null($my['opt']['node_type'])) { + $sql .= " AND node_type_id = " . + $this->node_descr_id[$my['opt']['node_type']]; + } + + $sql .= " GROUP BY parent_id "; + $rs = (array) $this->db->get_recordset($sql); + + return ! empty($rs) ? $rs[0]['max_order'] : 0; + } + + /* + * function: get_subtree + * Giving a node_id, get the nodes that forma s subtree that + * has node_id as root or starting point. + * + * Is possible to exclude: + * branches that has as staring node, node of certain types. + * children of some node types. + * full branches. + * + * + * args : + * [filters] map with following keys + * + * [exclude_node_types]: map/hash. + * default: null -> no exclusion filter will be applied. + * Branches starting with nodes of type detailed, will not be + * visited => no information will be returned. + * key: verbose description of node type to exclude. + * (see get_available_node_types). + * value: can be any value, because is not used,anyway is suggested + * to use 'exclude_me' as value. + * + * Example: + * array('testplan' => 'exclude_me') + * Node of type tesplan, will be excluded. + * + * + * + * [exclude_children_of]: map/hash + * default: null -> no exclusion filter will be applied. + * When traversing tree if the type of a node child, of node under analisys, + * is contained in this map, traversing of branch starting with this child node + * will not be done. + * key: verbose description of node type to exclude. + * (see get_available_node_types). + * value: can be any value, because is not used,anyway is suggested + * to use 'exclude_my_children' as value. + * + * Example: + * array('testcase' => 'exclude_my_children') + * Children of testcase nodes, (tcversion nodes) will be EXCLUDED. + * + * [exclude_branches]: map/hash. + * default: null -> no exclusion filter will be applied. + * key: node id. + * value: anything is ok. + * + * When traversing tree branches that have these node is, will + * not be visited => no information will be retrieved. + * + * + * [additionalWhereClause]: sql filter to include in sql sentence used to retrieve nodes. + * default: null -> no action taken. + * + * [family]: used to include guide the tree traversal. + * map where key = node_id TO INCLUDE ON traversal + * value = map where each key is a CHILD that HAS TO BE INCLUDED in return set. + * + * [options]: map with following keys + * + * [recursive]: changes structure of returned structure. + * default: false -> a flat array will be generated + * true -> a map with recursive structure will be generated. + * + * false returns array, every element is a map with following keys: + * + * id + * parent_id + * node_type_id + * node_order + * node_table + * name + * + * + * true returns a map, with only one element + * key: childNodes. + * value: array, that represents a tree branch. + * Array elements are maps with following keys: + * + * id + * parent_id + * node_type_id + * node_order + * node_table + * name + * childNodes -> (array) + * + * + * returns: array or map + */ + public function get_subtree($node_id, $filters = null, $options = null) + { + $my['filters'] = array( + 'exclude_node_types' => null, + 'exclude_children_of' => null, + 'exclude_branches' => null, + 'additionalWhereClause' => '', + 'family' => null + ); + + $my['options'] = array( + 'recursive' => false, + 'order_cfg' => array( + "type" => 'spec_order' + ), + 'output' => 'essential', + 'key_type' => 'std', + 'addJoin' => '', + 'addFields' => '' + ); + + // Cast to array to handle $options = null + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + $the_subtree = array(); + + // Generate NOT IN CLAUSE to exclude some node types + // $not_in_clause = $my['filters']['additionalWhereClause']; + if (! is_null($my['filters']['exclude_node_types'])) { + $exclude = array(); + foreach ($my['filters']['exclude_node_types'] as $the_key => $elem) { + $exclude[] = $this->node_descr_id[$the_key]; + } + $my['filters']['additionalWhereClause'] .= " AND node_type_id NOT IN (" . + implode(",", $exclude) . ")"; + } + + $method2call = $my['options']['recursive'] ? 'getSubtreeRec' : '_get_subtree'; + $this->$method2call($node_id, $the_subtree, $my['filters'], + $my['options']); + return $the_subtree; + } + + /** + */ + private function _get_subtree($node_id, &$node_list, $filters = null, + $options = null) + { + static $my; + if (! $my) { + $my['filters'] = array( + 'exclude_children_of' => null, + 'exclude_branches' => null, + 'additionalWhereClause' => '', + 'family' => null + ); + + $my['options'] = array( + 'order_cfg' => array( + "type" => 'spec_order' + ), + 'output' => 'full', + 'key_type' => 'std', + 'addJoin' => '', + 'addFields' => '' + ); + } + + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + switch ($my['options']['order_cfg']['type']) { + case 'spec_order': + $sql = " SELECT id,name,parent_id,node_type_id,node_order " . + $my['options']['addFields'] . + " FROM {$this->object_table} {$my['options']['addJoin']} " . + " WHERE parent_id = {$node_id} " . + " {$my['filters']['additionalWhereClause']}" . + " ORDER BY node_order,id"; + break; + + case 'rspec': + $sql = " SELECT OBT.id,name,parent_id,node_type_id,node_order," . + " RSPEC.doc_id " . " FROM {$this->object_table} AS OBT " . + " JOIN {$this->tables['req_specs']} AS RSPEC " . + " ON RSPEC.id = OBT.id " . " WHERE parent_id = {$node_id} " . + " {$my['filters']['additionalWhereClause']}" . + " ORDER BY node_order,OBT.id"; + break; + + case 'exec_order': + // REMEMBER THAT DISTINCT IS NOT NEEDED when you does UNION WITHOUT ALL + // + // First query get Nodes that ARE NOT test case => test suites + // Second query get the TEST CASES + // + $sql = "SELECT * FROM ( SELECT NH.node_order AS spec_order," . + " NH.node_order AS node_order, NH.id, NH.parent_id," . + " NH.name, NH.node_type_id, 0 AS tcversion_id" . + " FROM {$this->object_table} NH, {$this->tables['node_types']} NT" . + " WHERE parent_id = {$node_id}" . + " AND NH.node_type_id=NT.id" . + " AND NT.description <> 'testcase' {$my['filters']['additionalWhereClause']}" . + " UNION" . + " SELECT NHA.node_order AS spec_order, " . + " T.node_order AS node_order, NHA.id, NHA.parent_id, " . + " NHA.name, NHA.node_type_id, T.tcversion_id" . + " FROM {$this->object_table} NHA, {$this->object_table} NHB," . + " {$this->tables['testplan_tcversions']} T,{$this->tables['node_types']} NT" . + " WHERE NHA.id=NHB.parent_id " . + " AND NHA.node_type_id=NT.id" . + " AND NHB.id=T.tcversion_id " . + " AND NT.description = 'testcase'" . + " AND NHA.parent_id = {$node_id}" . + " AND T.testplan_id = {$my['options']['order_cfg']['tplan_id']}) AC" . + " ORDER BY node_order,spec_order,id"; + break; + + case 'req_order': + $sql = " SELECT NH_TC.id,NH_TC.name,NH_TC.parent_id," . + " NH_TC.node_type_id,NH_TC.node_order " . + " {$my['options']['addFields']}" . + " FROM {$this->object_table} AS NH_TC " . + " {$my['options']['addJoin']} " . + " JOIN {$this->tables['req_coverage']} RC " . + " ON RC.testcase_id = NH_TC.id " . + " WHERE RC.req_id = {$node_id} " . + " {$my['filters']['additionalWhereClause']}" . + " ORDER BY NH_TC.node_order,NH_TC.id"; + break; + } + + $result = $this->db->exec_query($sql); + + if ($this->db->num_rows($result) == 0) { + return; + } + + while ($row = $this->db->fetch_array($result)) { + if (! isset($my['filters']['exclude_branches'][$row['id']])) { + + $node_table = $this->node_tables[$this->node_types[$row['node_type_id']]]; + + switch ($my['options']['output']) { + case 'id': + $node_list[] = $row['id']; + break; + + case 'essential': + $node_list[] = array( + 'id' => $row['id'], + 'parent_id' => $row['parent_id'], + 'node_type_id' => $row['node_type_id'], + 'node_order' => $row['node_order'], + 'node_table' => $node_table, + 'name' => $row['name'] + ); + break; + + case 'rspec': + $node_list[] = array( + 'id' => $row['id'], + 'parent_id' => $row['parent_id'], + 'doc_id' => $row['doc_id'], + 'node_type_id' => $row['node_type_id'], + 'node_order' => $row['node_order'], + 'node_table' => $node_table, + 'name' => $row['name'] + ); + break; + + case 'full': + default: + // this choice + // 'tcversion_id' => (isset($row['parent_id']) ? $row['parent_id'] : -1), + // need to be documented and REVIEWED, because can generate confusion + $node_list[] = array( + 'id' => $row['id'], + 'parent_id' => $row['parent_id'], + 'tcversion_id' => (isset($row['parent_id']) ? $row['parent_id'] : - 1), + 'node_type_id' => $row['node_type_id'], + 'node_order' => $row['node_order'], + 'node_table' => $node_table, + 'name' => $row['name'] + ); + break; + } + // Basically we use this because: + // 1. Sometimes we don't want the children if the parent is a testcase, + // due to the version management + // + // 2. Sometime we want to exclude all descendants (branch) of a node. + // + // [franciscom]: + // I think ( but I have no figures to backup my thoughts) doing this check and + // avoiding the function call is better that passing a condition that will result + // in a null result set. + if (! isset( + $my['filters']['exclude_children_of'][$this->node_types[$row['node_type_id']]]) && + ! isset($my['filters']['exclude_branches'][$row['id']])) { + $this->_get_subtree($row['id'], $node_list, $filters, + $options); + } + } + } + } + + /** + */ + public function getSubtreeRec($node_id, &$pnode, $filters = null, + $options = null) + { + static $tcNodeTypeID; + static $qnum; + static $my; + static $platform_filter; + static $fclause; + static $exclude_branches; + static $exclude_children_of; + + if (! $tcNodeTypeID) { + $tcNodeTypeID = $this->node_descr_id['testcase']; + + $qnum = 0; + + $my['filters'] = array( + 'exclude_children_of' => null, + 'exclude_branches' => null, + 'additionalWhereClause' => '', + 'family' => null + ); + + $my['options'] = array( + 'order_cfg' => array( + "type" => 'spec_order' + ), + 'key_type' => 'std', + 'remove_empty_nodes_of_type' => null + ); + + // Cast to array to handle $options = null + $my['filters'] = array_merge($my['filters'], (array) $filters); + $my['options'] = array_merge($my['options'], (array) $options); + + $platform_filter = ""; + if (isset($my['options']['order_cfg']['platform_id']) && + ($safe_pid = intval($my['options']['order_cfg']['platform_id'])) > + 0) { + $platform_filter = " /* Platform filter */ " . + " AND T.platform_id = " . $safe_pid; + } + + $fclause = " AND node_type_id <> {$tcNodeTypeID} {$my['filters']['additionalWhereClause']} "; + + // this way I can manage code or description + if (! is_null($my['options']['remove_empty_nodes_of_type']) && + ! is_numeric($my['options']['remove_empty_nodes_of_type'])) { + $my['options']['remove_empty_nodes_of_type'] = $this->node_descr_id[$my['options']['remove_empty_nodes_of_type']]; + } + + $exclude_branches = $my['filters']['exclude_branches']; + $exclude_children_of = $my['filters']['exclude_children_of']; + } + + switch ($my['options']['order_cfg']['type']) { + case 'spec_order': + $sql = " SELECT id,name,parent_id,node_type_id,node_order FROM {$this->object_table} " . + " WHERE parent_id = {$node_id} {$my['filters']['additionalWhereClause']}" . + " ORDER BY node_order,id"; + break; + + case 'exec_order': + // Hmmm, no action regarding platforms. is OK ?? + // + // REMEMBER THAT DISTINCT IS NOT NEEDED when you does UNION + // + // Important Notice: + // Second part of UNION, allows to get from nodes hierarchy, + // only test cases that has a version linked to test plan. + // + $sql = "SELECT * FROM ( SELECT NH.node_order AS spec_order," . + " NH.node_order AS node_order, NH.id, NH.parent_id," . + " NH.name, NH.node_type_id, 0 AS tcversion_id " . + " FROM {$this->tables['nodes_hierarchy']} NH" . + " WHERE parent_id = {$node_id} {$fclause} " . + " UNION" . + " SELECT NHA.node_order AS spec_order, " . + " T.node_order AS node_order, NHA.id, NHA.parent_id, " . + " NHA.name, NHA.node_type_id, T.tcversion_id " . + " FROM {$this->tables['nodes_hierarchy']} NHA, " . + " {$this->tables['nodes_hierarchy']} NHB," . + " {$this->tables['testplan_tcversions']} T" . + " WHERE NHA.id=NHB.parent_id " . + " AND NHA.node_type_id = {$tcNodeTypeID}" . + " AND NHB.id=T.tcversion_id " . + " AND NHA.parent_id = {$node_id} {$platform_filter} " . + " AND T.testplan_id = {$my['options']['order_cfg']['tplan_id']}) AC" . + " ORDER BY node_order,spec_order,id"; + break; + } + + $result = $this->db->exec_query($sql); + $qnum ++; + while ($row = $this->db->fetch_array($result)) { + if (! isset($exclude_branches[$row['id']])) { + switch ($my['options']['key_type']) { + case 'std': + $node = array( + 'parent_id' => $row['parent_id'], + 'id' => $row['id'], + 'name' => $row['name'], + 'childNodes' => null, + 'node_table' => $this->node_tables_by['id'][$row['node_type_id']], + 'node_type_id' => $row['node_type_id'], + 'node_order' => $row['node_order'] + ); + + if (isset($row['tcversion_id']) && + $row['tcversion_id'] > 0) { + $node['tcversion_id'] = $row['tcversion_id']; + } + break; + + case 'extjs': + $node = array( + 'text' => $row['name'], + 'id' => $row['id'], + 'parent_id' => $row['parent_id'], + 'node_type_id' => $row['node_type_id'], + 'position' => $row['node_order'], + 'childNodes' => null, + 'leaf' => false + ); + + switch ($this->node_types[$row['node_type_id']]) { + case 'testproject': + case 'testsuite': + $node['childNodes'] = null; + break; + + case 'testcase': + $node['leaf'] = true; + break; + } + break; + } + + // Basically we use this because: + // 1. Sometimes we don't want the children if the parent is a testcase, + // due to the version management + // + // 2. Sometime we want to exclude all descendants (branch) of a node. + // + // [franciscom]: + // I think ( but I have no figures to backup my thoughts) doing this check and + // avoiding the function call is better that passing a condition that will result + // in a null result set. + if (! isset( + $exclude_children_of[$this->node_types[$row['node_type_id']]]) && + ! isset($exclude_branches[$row['id']])) { + $this->getSubtreeRec($row['id'], $node, $my['filters'], + $my['options']); + } + + // Have added this logic, because when export test plan will be developed + // having a test spec tree where test suites that do not contribute to test plan + // are pruned/removed is very important, to avoid additional processing + $doRemove = is_null($node['childNodes']) && + $node['node_type_id'] == + $my['options']['remove_empty_nodes_of_type']; + + if (! $doRemove) { + $pnode['childNodes'][] = $node; + } + } + } + return $qnum; + } + + /** + * function: get_full_path_verbose + * when path can not be found instead of null, anyway a map will be returned, with key=itemID value=NULL + * + * @internal revisions + */ + public function get_full_path_verbose(&$items, $options = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; + $goto_root = null; + $path_to = null; + $all_nodes = array(); + $path_format = 'simple'; + $output_format = 'simple'; + + if (! is_null($options)) { + // not a good solution, but Quick & Dirty + $path_format = isset($options['path_format']) ? $options['path_format'] : $path_format; + if (! isset($options['path_format'])) { + $path_format = isset($options['include_starting_point']) ? 'points' : $path_format; + } + $output_format = isset($options['output_format']) ? $options['output_format'] : $output_format; + } + + // according to count($items) we will try to optimize, sorry for magic number + if (count((array) $items) > 200) { + $xitems = array_flip((array) $items); + $xsql = " SELECT parent_id,id " . + " FROM {$this->tables['nodes_hierarchy']} " . " WHERE id IN (" . + implode(',', array_keys($xitems)) . ")"; + + $xmen = $this->db->fetchRowsIntoMap($xsql, 'parent_id', + database::CUMULATIVE); + $all_nodes = array(); + foreach ($xmen as $parent_id => &$children) { + $paty = $this->get_path($parent_id, $goto_root, $path_format); + $paty[] = $parent_id; + + $all_nodes = array_merge($all_nodes, $paty); + foreach ($children as &$item) { + $path_to[$item['id']]['name'] = $stairway2heaven[$item['id']] = $paty; + $all_nodes[] = $item['id']; + } + } + unset($xmen); + } else { + foreach ((array) $items as $item_id) { + $stairway2heaven[$item_id] = $this->get_path($item_id, + $goto_root, $path_format); + $path_to[$item_id]['name'] = $stairway2heaven[$item_id]; + $all_nodes = array_merge($all_nodes, + (array) $path_to[$item_id]['name']); + } + } + + $status_ok = (! is_null($all_nodes) && ! empty($all_nodes)); + if ($status_ok) { + // get only different items, to get descriptions + $unique_nodes = implode(',', array_unique($all_nodes)); + + $sql = "/* $debugMsg */ " . + " SELECT id,name FROM {$this->tables['nodes_hierarchy']} WHERE id IN ({$unique_nodes})"; + $decode = $this->db->fetchRowsIntoMap($sql, 'id'); + + foreach ($path_to as $key => $elem) { + foreach ($elem['name'] as $idx => $node_id) { + $path_to[$key]['name'][$idx] = $decode[$node_id]['name']; + $path_to[$key]['node_id'][$idx] = $node_id; + } + } + unset($decode); + } else { + $path_to = null; + } + + if (! is_null($path_to)) { + switch ($output_format) { + case 'path_as_string': + case 'stairway2heaven': + $flat_path = null; + foreach ($path_to as $item_id => $pieces) { + // remove root node + unset($pieces['name'][0]); + $flat_path[$item_id] = implode('/', $pieces['name']); + } + if ($output_format == 'path_as_string') { + $path_to = $flat_path; + } else { + $path_to = null; + $path_to['flat'] = $flat_path; + $path_to['staircase'] = $stairway2heaven; + } + break; + + case 'id_name': + break; + + case 'simple': + default: + $keySet = array_keys($path_to); + foreach ($keySet as $key) { + $path_to[$key] = $path_to[$key]['name']; + } + break; + } + } + unset($stairway2heaven); + return $path_to; + } + + /** + * check if there is a sibbling node of same type that has same name + * + * @param + * string name: name to check + * @param + * int node_type_id: node types to check. + * @param + * int id: optional. exclude this node id from result set + * this is useful when you want to check for name + * existence during an update operation. + * Using id you get node parent, to get sibblings. + * If null parent_id argument must be present + * + * @param + * int parent_id: optional. Mandatory if id is null + * Used to get children nodes to check for + * name existence. + * + * @return array ret: ret['status']=1 if name exists + * 0 if name does not exist + * ret['msg']= localized message + * + */ + public function nodeNameExists($name, $node_type_id, $id = null, + $parent_id = null) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; + $ret['status'] = 0; + $ret['msg'] = ''; + if (is_null($id) && is_null($parent_id)) { + $msg = $debugMsg . + 'Error on call $id and $parent_id can not be both null'; + throw new Exception($msg); + } + + $additionalFilters = ''; + $parentNodeID = intval($parent_id); + if (! is_null($id)) { + // Try to get parent id if not provided on method call. + if (is_null($parentNodeID) || $parentNodeID <= 0) { + $sql = "/* {$debugMsg} */ " . + " SELECT parent_id FROM {$this->object_table} NHA " . + " WHERE NHA.id = " . $this->db->prepare_int($id); + $rs = $this->db->get_recordset($sql); + $parentNodeID = intval($rs[0]['parent_id']); + } + $additionalFilters = " AND NHA.id <> " . $this->db->prepare_int($id); + } + + if ($parentNodeID <= 0) { + $msg = $debugMsg . ' FATAL Error $parentNodeID can not be <= 0'; + throw new Exception($msg); + } + + $sql = "/* {$debugMsg} */ " . + " SELECT count(0) AS qty FROM {$this->object_table} NHA " . + " WHERE NHA.node_type_id = {$node_type_id} " . " AND NHA.name = '" . + $this->db->prepare_string($name) . "'" . " AND NHA.parent_id = " . + $this->db->prepare_int($parentNodeID) . " {$additionalFilters} "; + + $rs = $this->db->get_recordset($sql); + if ($rs[0]['qty'] > 0) { + $ret['status'] = 1; + $ret['msg'] = sprintf(lang_get('name_already_exists'), $name); + } + + return $ret; + } + + /** + * getTreeRoot() + */ + public function getTreeRoot($node_id) + { + $path = (array) $this->get_path($node_id); + $path_len = count($path); + return ($path_len > 0) ? $path[0]['parent_id'] : $node_id; + } + + /** + * delete_subtree_objects() + * + * ATTENTION: subtree root node ($node_id?? or root_id?) IS NOT DELETED. + */ + public function delete_subtree_objects($root_id, $node_id, + $additionalWhereClause = '', $exclude_children_of = null, + $exclude_branches = null) + { + static $debugMsg; + if (is_null($debugMsg)) { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + } + + $sql = "/* $debugMsg */ SELECT NH.* FROM {$this->object_table} NH " . + " WHERE NH.parent_id = " . $this->db->prepare_int($node_id) . + " {$additionalWhereClause} "; + $rs = $this->db->get_recordset($sql); + if (! is_null($rs)) { + foreach ($rs as $row) { + $rowID = $row['id']; + $nodeTypeID = $row['node_type_id']; + $nodeType = $this->node_types[$nodeTypeID]; + $nodeClassName = $this->class_name[$nodeTypeID]; + if (! isset($exclude_branches[$rowID])) { + // Basically we use this because: + // 1. Sometimes we don't want the children if the parent is a testcase, + // due to the version management + // + // 2. Sometime we want to exclude all descendants (branch) of a node. + if (! isset($exclude_children_of[$nodeType]) && + ! isset($exclude_branches[$rowID])) { + // I'm paying not having commented this well + // Why I've set root_id to null ? + // doing this when traversing a tree, containers under level of subtree root + // will not be deleted => and this seems to be wrong. + $this->delete_subtree_objects($root_id, $rowID, + $additionalWhereClause, $exclude_children_of, + $exclude_branches); + } else { + // For us in this method context this node is a leaf => just delete + if (! is_null($nodeClassName)) { + $item_mgr = new $nodeClassName($this->db); + $item_mgr->delete($rowID); + } + } + } + } + } + + // Must delete myself if I'm empty, only if I'm not subtree root. + // Done this way to avoid infinte recursion for some type of nodes + // that use this method as it's delete method. (example testproject). + + // Hmmm, need to recheck if this condition is ok + if (! is_null($root_id) && ($node_id != $root_id)) { + $children = (array) $this->db->get_recordset($sql); + if (count($children) == 0) { + $sql2 = "/* $debugMsg */ SELECT NH.* FROM {$this->object_table} NH " . + " WHERE NH.id = " . $this->db->prepare_int($node_id); + $node_info = $this->db->get_recordset($sql2); + if (isset($this->class_name[$node_info[0]['node_type_id']])) { + $className = $this->class_name[$node_info[0]['node_type_id']]; + if (! is_null($className)) { + $item_mgr = new $className($this->db); + $item_mgr->delete($node_id); + } + } + } + } + } + + /* + * + * [$mode]: dotted -> $level number of dot characters are appended to + * the left of item name to create an indent effect. + * Level indicates on what tree layer item is positioned. + * Example: + * + * null + * \ + * id=1 <--- Tree Root = Level 0 + * | + * + ------+ + * / \ \ + * id=9 id=2 id=8 <----- Level 1 + * \ + * id=3 <----- Level 2 + * \ + * id=4 <----- Level 3 + * + * + * key: item id (= node id on tree). + * value: every array element is an string, containing item name. + * + * Result example: + * + * 2 .TS1 + * 3 ..TS2 + * 9 .20071014-16:22:07 TS1 + * 10 ..TS2 + * + * + * array -> key: item id (= node id on tree). + * value: every array element is a map with the following keys + * 'name', 'level' + * + * 2 array(name => 'TS1',level => 1) + * 3 array(name => 'TS2',level => 2) + * 9 array(name => '20071014-16:22:07 TS1',level =>1) + * 10 array(name => 'TS2', level => 2) + * + */ + public function createHierarchyMap($array2map, $mode = 'dotted', + $field2add = null) + { + $hmap = array(); + $the_level = 1; + $level = array(); + $pivot = $array2map[0]; + $addField = ! is_null($field2add); + $mode = is_null($mode) ? 'dotted' : $mode; + + foreach ($array2map as $elem) { + $current = $elem; + if ($pivot['id'] == $current['parent_id']) { + $the_level ++; + $level[$current['parent_id']] = $the_level; + } elseif ($pivot['parent_id'] != $current['parent_id']) { + $the_level = $level[$current['parent_id']]; + } + + switch ($mode) { + case 'dotted': + $hmap[$current['id']] = str_repeat('.', $the_level); + if ($addField) { + $hmap[$current['id']] .= sprintf($field2add['format'], + $current[$field2add['field']]); + } + $hmap[$current['id']] .= $current['name']; + break; + + case 'array': + $str = ($addField ? $current[$field2add] : '') . + $current['name']; + $hmap[$current['id']] = array( + 'name' => $str, + 'level' => $the_level + ); + break; + } + + // update pivot + $level[$current['parent_id']] = $the_level; + $pivot = $elem; + } + + return $hmap; + } + + /** + * getAllItemsID + * + * @internal revisions + * based on code from testproject->get_all_testcases_id + * + */ + public function getAllItemsID($parentList, &$itemSet, $coupleTypes) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method:' . __FUNCTION__ . ' :: '; + $sql = "/* $debugMsg */ " . + " SELECT id,node_type_id from {$this->tables['nodes_hierarchy']} " . + " WHERE parent_id IN ({$parentList})"; + $sql .= " AND node_type_id IN ({$coupleTypes['target']},{$coupleTypes['container']}) "; + + $result = $this->db->exec_query($sql); + if ($result) { + $containerSet = array(); + while ($row = $this->db->fetch_array($result)) { + if ($row['node_type_id'] == $coupleTypes['target']) { + $itemSet[] = $row['id']; + } else { + $containerSet[] = $row['id']; + } + } + if (count($containerSet)) { + $containerSet = implode(",", $containerSet); + $this->getAllItemsID($containerSet, $itemSet, $coupleTypes); + } + } + } + + /** + */ + public function getNodeByAttributes($attr) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + + $addJoin = ''; + $sql = "/* $debugMsg */ "; + $sql .= " SELECT NH_MAIN.id,NH_MAIN.parent_id,NH_MAIN.name,NH_MAIN.node_type_id " . + " FROM {$this->object_table} AS NH_MAIN " . + " JOIN {$this->tables['node_types']} AS NT ON NT.id = NH_MAIN.node_type_id "; + + $where = " WHERE 1=1 "; + foreach ($attr as $key => $value) { + switch ($key) { + case 'type': + $where .= " AND NT.description = '" . + $this->db->prepare_string($value) . "'"; + break; + + case 'id': + $where .= " AND NH_MAIN.is = " . + $this->db->prepare_int($value); + break; + + case 'name': + $where .= " AND NH_MAIN.name = '" . + $this->db->prepare_string($value) . "'"; + break; + + case 'parent_id': + $where .= " AND NH_MAIN.parent_id = " . + $this->db->prepare_int($value); + $addJoin = " JOIN {$this->object_table} AS NH_PARENT ON NH_PARENT.id = NH_MAIN.parent_id "; + break; + } + } + + $sql .= $addJoin . $where; + return $this->db->fetchRowsIntoMap($sql, 'id'); + } + + /** + */ + private function getNodeType($id) + { + $sql = " SELECT node_type_id, NT.description AS node_type FROM {$this->tables['nodes_hierarchy']} NH - JOIN {$this->tables['node_types']} NT - ON NT.id = NH.node_type_id - WHERE NH.id = " . intval($id); - $rs = $this->db->get_recordset($sql); - - return null != $rs ? current($rs) : null; - } - - /** - * - */ - function getNameL2($node_id,$opt=null) - { - $options = array('l2CutFirst' => 0); - - $options = array_merge($options,(array)$opt); - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - - $concat = " CONCAT(NHL1.name,':',NHL2.name) "; - if ($options['l2CutFirst'] > 0) { - $where2cut = $options['l2CutFirst']; - $concat = " CONCAT(NHL1.name,':'," . - " SUBSTRING(NHL2.name,{$where2cut}) )"; - } - $sql = "SELECT $concat AS name + JOIN {$this->tables['node_types']} NT + ON NT.id = NH.node_type_id + WHERE NH.id = " . intval($id); + $rs = $this->db->get_recordset($sql); + + return null != $rs ? current($rs) : null; + } + + /** + */ + public function getNameL2($node_id, $opt = null) + { + $options = array( + 'l2CutFirst' => 0 + ); + + $options = array_merge($options, (array) $opt); + + $concat = " CONCAT(NHL1.name,':',NHL2.name) "; + if ($options['l2CutFirst'] > 0) { + $where2cut = $options['l2CutFirst']; + $concat = " CONCAT(NHL1.name,':'," . + " SUBSTRING(NHL2.name,{$where2cut}) )"; + } + $sql = "SELECT $concat AS name FROM {$this->tables['nodes_hierarchy']} NHL2 JOIN {$this->tables['nodes_hierarchy']} NHL1 ON NHL1.id = NHL2.parent_id - WHERE NHL2.id = " . intval($node_id); - $rs = $this->db->get_recordset($sql); - $result = !is_null($rs) ? $rs[0]['name'] : ''; - return $result; - } -}// end class \ No newline at end of file + WHERE NHL2.id = " . intval($node_id); + $rs = $this->db->get_recordset($sql); + return ! is_null($rs) ? $rs[0]['name'] : ''; + } +} diff --git a/lib/functions/treeMenu.inc.php b/lib/functions/treeMenu.inc.php index 363d1839d1..9909d1a947 100644 --- a/lib/functions/treeMenu.inc.php +++ b/lib/functions/treeMenu.inc.php @@ -1,2726 +1,2734 @@ - viewType='testSpecTree' - * - * planAddTCNavigator.php => viewType=testSpecTreeForTestPlan - * - * --> tlTestCaseFilterControl->build_tree_menu() - WHEN FILTER ADDED - */ -function generateTestSpecTree(&$db,$tproject_id, $tproject_name,$linkto,$filters=null,$options=null) -{ - - $chronos[] = microtime(true); - - $tables = tlObjectWithDB::getDBTables(array('tcversions','nodes_hierarchy')); - - $my = array(); - $my['options'] = array('forPrinting' => 0, 'hideTestCases' => 0, - 'tc_action_enabled' => 1, - 'viewType' => 'testSpecTree', - 'ignore_inactive_testcases' => null, - 'ignore_active_testcases' => null); - - // testplan => - // only used if opetions['viewType'] == 'testSpecTreeForTestPlan' - // - // 20120205 - franciscom - hmm seems this code is INCOMPLETE - // may be we can remove ? - - // keys -> filter_* come from tlTestCaseFilterControl.class.php - $my['filters'] = array('keywords' => null, - 'executionType' => null, - 'importance' => null, - 'testplan' => null, - 'filter_tc_id' => null, - 'filter_platforms' => null); - - $my['options'] = array_merge($my['options'], (array)$options); - $my['options']['showTestCaseID'] = config_get('treemenu_show_testcase_id'); - - $my['filters'] = array_merge($my['filters'], (array)$filters); - - if( $my['options']['viewType'] == 'testSpecTree' ) { - $rr = generateTestSpecTreeNew($db,$tproject_id,$tproject_name,$linkto,$filters,$options); - return $rr; - } - - // OK - Go ahead here we have other type of features - $treeMenu = new stdClass(); - $treeMenu->rootnode = null; - $treeMenu->menustring = ''; - - $resultsCfg = config_get('results'); - $glueChar = config_get('testcase_cfg')->glue_character; - $menustring = null; - - $tproject_mgr = new testproject($db); - $tree_manager = &$tproject_mgr->tree_manager; - - $hash_descr_id = $tree_manager->get_available_node_types(); - $hash_id_descr = array_flip($hash_descr_id); - $status_descr_code = $resultsCfg['status_code']; - $status_code_descr = $resultsCfg['code_status']; - - - // IMPORTANT NOTICE - // $filters['filter_toplevel_testsuite'] is managed in REVERSE form - // it contains NOT WHAT user wants, but all that we need to exclude - // in order provide what user WANTS. - // This is right way to go. - // - $exclude_branches = isset($filters['filter_toplevel_testsuite']) && - is_array($filters['filter_toplevel_testsuite']) ? - $filters['filter_toplevel_testsuite'] : null; - - $tcase_prefix = $tproject_mgr->getTestCasePrefix($tproject_id) . - $glueChar; - $test_spec = getTestSpecTree($tproject_id,$tproject_mgr,$filters); - - // where the Keyword filter will be applied? - - // Added root node for test specification -> testproject - $test_spec['name'] = $tproject_name; - $test_spec['id'] = $tproject_id; - $test_spec['node_type_id'] = $hash_descr_id['testproject']; - - - $map_node_tccount=array(); - $tplan_tcs=null; - $tc2show = null; - - // MORE FILTERS - if($test_spec) { - $attr_map['keywords'] = null; // means no filter - if(!is_null($my['filters']['filter_keywords'])) { - $attr_map['keywords'] = - $tproject_mgr->getKeywordsLatestTCV($tproject_id, - $my['filters']['filter_keywords'], - $my['filters']['filter_keywords_filter_type']); - - if( is_null($attr_map['keywords']) ) { - // means that tree will be EMPTY - $attr_map['keywords'] = array(); - } - } - - $attr_map['platforms'] = null; // means no filter - if(!is_null($my['filters']['filter_platforms'])) { - $attr_map['platforms'] = - $tproject_mgr->getPlatformsLatestTCV($tproject_id, - $my['filters']['filter_platforms']); - - if( is_null($attr_map['platforms']) ) { - // means that tree will be EMPTY - $attr_map['platforms'] = array(); - } - } - - // Important: prepareNode() will make changes to - // $test_spec like filtering by test case - // keywords using $attr_map['keywords']; - $pnFilters = null; - $keys2init = array('filter_testcase_name', - 'filter_execution_type','filter_priority', - 'filter_tc_id'); - foreach ($keys2init as $keyname) { - $pnFilters[$keyname] = isset($my['filters'][$keyname]) ? $my['filters'][$keyname] : null; - } - - $pnFilters['setting_testplan'] = $my['filters']['setting_testplan']; - if (isset($my['filters']['filter_custom_fields']) && isset($test_spec['childNodes'])) { - $test_spec['childNodes'] = filter_by_cf_values($db, - $test_spec['childNodes'], - $my['filters']['filter_custom_fields'],$hash_descr_id); - } - - // TICKET 4496: added inactive testcase filter - $pnOptions = array('hideTestCases' => $my['options']['hideTestCases'], - 'viewType' => $my['options']['viewType'], - 'ignoreInactiveTestCases' => - $my['options']['ignore_inactive_testcases'], - 'ignoreActiveTestCases' => - $my['options']['ignore_active_testcases']); - - $testcase_counters = prepareNode($db,$test_spec, - $map_node_tccount,$attr_map,$tplan_tcs,$pnFilters,$pnOptions); - - foreach($testcase_counters as $key => $value) { - $test_spec[$key] = $testcase_counters[$key]; - } - - $tc2show = renderTreeNode(1,$test_spec,$hash_id_descr, - $linkto,$tcase_prefix,$my['options']); - } - - $menustring =''; - $treeMenu->rootnode = new stdClass(); - $treeMenu->rootnode->name = $test_spec['text']; - $treeMenu->rootnode->id = $test_spec['id']; - $treeMenu->rootnode->leaf = isset($test_spec['leaf']) ? $test_spec['leaf'] : false; - $treeMenu->rootnode->text = $test_spec['text']; - $treeMenu->rootnode->position = $test_spec['position']; - $treeMenu->rootnode->href = $test_spec['href']; - - - // 20090328 - franciscom - BUGID 2299 - // More details about problem found on 20090308 and fixed IN WRONG WAY - // TPROJECT - // |______ TSA - // |__ TC1 - // |__ TC2 - // | - // |______ TSB - // |______ TSC - // - // Define Keyword K1,K2 - // - // NO TEST CASE HAS KEYWORD ASSIGNED - // Filter by K1 - // Tree will show root that spins Forever - // menustring before str_ireplace : [null,null] - // menustring AFTER [null] - // - // Now fixed. - // - // Some minor fix to do - // Il would be important exclude Top Level Test suites. - // - // - // 20090308 - franciscom - // Changed because found problem on: - // Test Specification tree when applying Keyword filter using a keyword NOT PRESENT - // in test cases => Tree root shows loading icon and spin never stops. - // - // Attention: do not know if in other situation this will generate a different bug - // - // Change key ('childNodes') to the one required by Ext JS tree. - if(isset($test_spec['childNodes'])) - { - $menustring = str_ireplace('childNodes', 'children', json_encode($test_spec['childNodes'])); - } - - if(!is_null($menustring)) - { - // Remove null elements (Ext JS tree do not like it ). - // :null happens on -> "children":null,"text" that must become "children":[],"text" - // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); - // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); - $menustring = str_ireplace(array(':' . REMOVEME, - ',"' . REMOVEME .'"', - '"' . REMOVEME . '",', - '"' . REMOVEME . '"'), - array(':[]','','',''), $menustring); - } - $treeMenu->menustring = $menustring; - - $tc2show = !is_null($tc2show) ? explode(",",trim($tc2show,",")) : null; - return array('menu' => $treeMenu, 'leaves' => $tc2show, 'tree' => $test_spec); -} - - -/** - * Prepares a Node to be displayed in a navigation tree. - * This function is used in the construction of: - * - Test project specification -> we want ALL test cases defined in test project. - * - Test execution -> we only want the test cases linked to a test plan. - * - * IMPORTANT: - * when analising a container node (Test Suite) if it is empty and we have requested - * some sort of filtering NODE WILL BE PRUNED. - * - * - * status: one of the possible execution status of a test case. - * - * - * tplan_tcases: map with testcase versions linked to test plan. - * due to the multiples uses of this function, null has several meanings - * - * When we want to build a Test Project specification tree, - * WE SET it to NULL, because we are not interested in a test plan. - * - * When we want to build a Test execution tree, we dont set it deliverately - * to null, but null can be the result of NO tcversion linked => EMPTY TEST PLAN - * - * - * status can be an array with multple values, to do OR search. - * added version info from test cases in return data structure. - * ignore_inactive_testcases: useful when building a Test Project Specification tree - * to be used in the add/link test case to Test Plan. - * - * attr_map['keywords']: Test Case Keyword map: - * null => no filter - * empty map => filter out ALL test case ALWAYS - * initialized map => filter out test case ONLY if NOT present in map. - * - * attr_map['platforms']: Test Case platforms map: - * null => no filter - * empty map => filter out ALL test case ALWAYS - * initialized map => filter out test case ONLY if NOT present in map. - * - * added argument: - * $map_node_tccount - * key => node_id - * values => node test case count - * node name (useful only for debug purpouses - * - * IMPORTANT: this new argument is not useful for tree rendering - * but to avoid duplicating logic to get test case count - * - * - * return: map with keys: - * 'total_count' - * 'passed' - * 'failed' - * 'blocked' - * 'not run' - * - * @internal revisions - */ -function prepareNode(&$db,&$node,&$map_node_tccount,$attr_map = null, - &$tplan_tcases = null,$filters=null, $options=null) -{ - static $status_descr_list; - static $debugMsg; - static $tables; - static $my; - static $enabledFiltersOn; - static $activeVersionClause; - static $filterOnTCVersionAttribute; - static $filtersApplied; - static $users2filter; - static $results2filter; - static $testPlanIsNotEmpty; - static $nodesTypeCode; - static $nodesCodeType; - - $tpNode = null; - if (!$tables) { - - $debugMsg = 'Class: ' . __CLASS__ . ' - ' . 'Method: ' . __FUNCTION__ . ' - '; - $tables = tlObjectWithDB::getDBTables(array('tcversions','nodes_hierarchy','node_types','testplan_tcversions')); - - $sql = " SELECT * FROM {$tables['node_types']} "; - $nodesTypeCode = $db->fetchColumnsIntoMap($sql,'description','id'); - $nodesCodeType = array_flip($nodesTypeCode); - - $resultsCfg = config_get('results'); - $status_descr_list = array_keys($resultsCfg['status_code']); - $status_descr_list[] = 'testcase_count'; - - $my = array(); - $my['options'] = array('hideTestCases' => 0, 'showTestCaseID' => 1, - 'viewType' => 'testSpecTree', - 'getExternalTestCaseID' => 1, - 'ignoreInactiveTestCases' => 0, - 'ignoreActiveTestCases' => 0, - 'setAssignedTo' => false); - - // added importance here because of "undefined" error in event log - $my['filters'] = array('status' => null, - 'assignedTo' => null, - 'importance' => null, 'executionType' => null, - 'filter_tc_id' => null, - 'filter_platforms' => null); - - $my['options'] = array_merge($my['options'], (array)$options); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $enabledFiltersOn['importance'] = isset($my['filters']['filter_priority']); - $enabledFiltersOn['testcase_id'] = isset($my['filters']['filter_tc_id']); - - $enabledFiltersOn['testcase_name'] = - isset($my['filters']['filter_testcase_name']); - $enabledFiltersOn['executionType'] = - isset($my['filters']['filter_execution_type']); - $enabledFiltersOn['custom_fields'] = - isset($my['filters']['filter_custom_fields']); - - - $enabledFiltersOn['keywords'] = - (null != $attr_map && isset($attr_map['keywords']) - && null != $attr_map['keywords'] - && count($attr_map['keywords']) > 0); - - $enabledFiltersOn['platforms'] = - (null != $attr_map - && isset($attr_map['platforms']) - && null != $attr_map['platforms'] - && count($attr_map['platforms']) > 0); - - - $filterOnTCVersionAttribute = $enabledFiltersOn['executionType'] || $enabledFiltersOn['importance']; - - $filtersApplied = false; - foreach($enabledFiltersOn as $filterValue) { - $filtersApplied = $filtersApplied || $filterValue; - } - - $activeVersionClause = $filterOnTCVersionAttribute ? " AND TCV.active=1 " : ''; - - $users2filter = isset($my['filters']['filter_assigned_user']) ? - $my['filters']['filter_assigned_user'] : null; - - $results2filter = isset($my['filters']['filter_result_result']) ? - $my['filters']['filter_result_result'] : null; - - - $testPlanIsNotEmpty = (!is_null($tplan_tcases) && count($tplan_tcases) > 0); - } - - $tcase_counters = array_fill_keys($status_descr_list, 0); - $nodeV = - $node_type = isset($node['node_type_id']) ? - $nodesCodeType[$node['node_type_id']] : null; - - if($node_type == 'testcase') { - // ABSOLUTELY First implicit filter to be applied when test plan is not empty. - // is our test case present on Test Spec linked to Test Plan ? - - if( $testPlanIsNotEmpty && !isset($tplan_tcases[$node['id']])) { - $node = null; - } - else if( - ($enabledFiltersOn['keywords'] && - !isset($attr_map['keywords'][$node['id']])) || - - ($enabledFiltersOn['platforms'] && - !isset($attr_map['platforms'][$node['id']])) || - - ($enabledFiltersOn['testcase_name'] && - stripos($node['name'], $my['filters']['filter_testcase_name']) === FALSE) || - ($enabledFiltersOn['testcase_id'] && ($node['id'] != $my['filters']['filter_tc_id'])) ) { - unset($tplan_tcases[$node['id']]); - $node = null; // OK - 20150129 - } - else { - if ($my['options']['viewType'] == 'executionTree') { - $tpNode = isset($tplan_tcases[$node['id']]) ? $tplan_tcases[$node['id']] : null; - if (!($delete_node=is_null($tpNode))) { - $delete_node = !is_null($results2filter) - && !isset($results2filter[$tpNode['exec_status']]); - - if (!$delete_node && !is_null($users2filter)) { - $somebody_wanted_but_nobody_there = - isset($users2filter[TL_USER_SOMEBODY]) - && !is_numeric($tpNode['user_id']); - - $unassigned_wanted_but_someone_assigned = - isset($users2filter[TL_USER_NOBODY]) - && !is_null($tpNode['user_id']); - - $wrong_user = !isset($users2filter[TL_USER_NOBODY]) - && !isset($users2filter[TL_USER_SOMEBODY]) - && !isset($users2filter[$tpNode['user_id']]); - - $delete_node = $unassigned_wanted_but_someone_assigned - || $wrong_user - || $somebody_wanted_but_nobody_there; - } - } - - if ($delete_node) { - unset($tplan_tcases[$node['id']]); - $node = null; - // $node = REMOVEME; - } else { - $externalID=''; - $node['tcversion_id'] = $tpNode['tcversion_id']; - $node['version'] = $tpNode['version']; - if ($my['options']['setAssignedTo']) { - $node['assigned_to'] = $tplan_tcases[$node['id']]['assigned_to']; - } - - if ($my['options']['getExternalTestCaseID']) { - if (!isset($tpNode['external_id'])) { - $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . - " SELECT TCV.tc_external_id AS external_id " . - " FROM {$tables['tcversions']} TCV " . - " WHERE TCV.id=" . $node['tcversion_id']; - - $result = $db->exec_query($sql); - $myrow = $db->fetch_array($result); - $externalID = $myrow['external_id']; - } else { - $externalID = $tpNode['external_id']; - } - } - $node['external_id'] = $externalID; - } - } - - if ($node != REMOVEME && $my['options']['ignoreInactiveTestCases']) - { - // there are active tcversions for this node ??? - // I'm doing this instead of creating a test case manager object, because - // I think is better for performance. - // - // ======================================================================================= - // 20070106 - franciscom - // Postgres Problems - // ======================================================================================= - // Problem 1 - SQL Syntax - // While testing with postgres - // SELECT count(TCV.id) NUM_ACTIVE_VERSIONS -> Error - // - // At least for what I remember using AS to create COLUMN ALIAS IS REQUIRED and Standard - // while AS is NOT REQUIRED (and with some DBMS causes errors) when you want to give a - // TABLE ALIAS - // - // Problem 2 - alias case - // At least in my installation the aliases column name is returned lower case, then - // PHP fails when: - // if($myrow['NUM_ACTIVE_VERSIONS'] == 0) - // - // - $sql=" /* $debugMsg - line:" . __LINE__ . " */ " . - " SELECT count(TCV.id) AS num_active_versions " . - " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . - " WHERE NH.parent_id=" . $node['id'] . - " AND NH.id = TCV.id AND TCV.active=1"; - - $result = $db->exec_query($sql); - $myrow = $db->fetch_array($result); - if($myrow['num_active_versions'] == 0) - { - $node = null; - //$node = REMOVEME; - } - } - - // TICKET 4496: added inactive testcase filter - if ($node !== REMOVEME && $my['options']['ignoreActiveTestCases']) - { - $sql=" /* $debugMsg - line:" . __LINE__ . " */ " . - " SELECT count(TCV.id) AS num_active_versions " . - " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . - " WHERE NH.parent_id=" . $node['id'] . - " AND NH.id = TCV.id AND TCV.active=1"; - - $result = $db->exec_query($sql); - $myrow = $db->fetch_array($result); - if($myrow['num_active_versions'] != 0) - { - $node = null; - //$node = REMOVEME; - } - } - } - // ------------------------------------------------------------------- - - // ------------------------------------------------------------------- - if (!is_null($node) && ($my['options']['viewType']=='testSpecTree' || - $my['options']['viewType'] =='testSpecTreeForTestPlan') ) - { - $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . - " SELECT COALESCE(MAX(TCV.id),0) AS targetid, TCV.tc_external_id AS external_id" . - " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . - " WHERE NH.id = TCV.id {$activeVersionClause} AND NH.parent_id={$node['id']} " . - " GROUP BY TCV.tc_external_id "; - - $rs = $db->get_recordset($sql); - if( is_null($rs) ) - { - $node = null; // OK 20150129 - } - else - { - $node['external_id'] = $rs[0]['external_id']; - $target_id = $rs[0]['targetid']; - - if( $filterOnTCVersionAttribute ) - { - switch ($my['options']['viewType']) - { - case 'testSpecTreeForTestPlan': - // Try to get info from linked tcversions - // Platform is not needed - $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . - " SELECT DISTINCT TPTCV.tcversion_id AS targetid " . - " FROM {$tables['tcversions']} TCV " . - " JOIN {$tables['nodes_hierarchy']} NH " . - " ON NH.id = TCV.id {$activeVersionClause} " . - " AND NH.parent_id={$node['id']} " . - " JOIN {$tables['testplan_tcversions']} TPTCV " . - " ON TPTCV.tcversion_id = TCV.id " . - " AND TPTCV.testplan_id = " . - " {$my['filters']['setting_testplan']}"; - $rs = $db->get_recordset($sql); - $target_id = !is_null($rs) ? $rs[0]['targetid'] : $target_id; - break; - } - - $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . - " SELECT TCV.execution_type " . - " FROM {$tables['tcversions']} TCV " . - " WHERE TCV.id = {$target_id} "; - - if( $enabledFiltersOn['executionType'] ) - { - $sql .= " AND TCV.execution_type = " . - " {$my['filters']['filter_execution_type']} "; - } - - if( $enabledFiltersOn['importance'] ) - { - $sql .= " AND TCV.importance = " . - " {$my['filters']['filter_priority']} "; - } - - $rs = $db->fetchRowsIntoMap($sql,'execution_type'); - if(is_null($rs)) - { - $node = null; // OK - 20150129 - } - } - } - - // if( !is_null($node) ) - if(!is_null($node) && $node != REMOVEME) - { - // needed to avoid problems when using json_encode with EXTJS - unset($node['childNodes']); - $node['leaf']=true; - } - } - // ------------------------------------------------------------------- - - // ======================================================================== - foreach($tcase_counters as $key => $value) - { - $tcase_counters[$key]=0; - } - - if(isset($tpNode['exec_status']) ) - { - $tc_status_descr = $decoding_info['status_code_descr'][$tpNode['exec_status']]; - } - else - { - $tc_status_descr = "not_run"; - } - - $init_value = $node ? 1 : 0; - $tcase_counters[$tc_status_descr] = $init_value; - $tcase_counters['testcase_count'] = $init_value; - if ( $my['options']['hideTestCases'] ) - { - $node = REMOVEME; - } - // ======================================================================== - } // if($node_type == 'testcase') - - - // ======================================================================== - if (isset($node['childNodes']) && is_array($node['childNodes'])) - { - // node has to be a Test Suite ? - $childNodes = &$node['childNodes']; - $childNodesQty = count($childNodes); - - for($idx = 0;$idx < $childNodesQty ;$idx++) - { - $current = &$childNodes[$idx]; - // I use set an element to null to filter out leaf menu items - if(is_null($current) || $current == REMOVEME) - { - $childNodes[$idx] = REMOVEME; - continue; - } - $counters_map = prepareNode($db,$current,$map_node_tccount, - $attr_map,$tplan_tcases,$my['filters'],$my['options']); - foreach($counters_map as $key => $value) - { - $tcase_counters[$key] += $counters_map[$key]; - } - } - foreach($tcase_counters as $key => $value) - { - $node[$key] = $tcase_counters[$key]; - } - - if (isset($node['id'])) - { - $map_node_tccount[$node['id']] = array( 'testcount' => $node['testcase_count'], - 'name' => $node['name']); - } - - // node must be destroyed if empty had we have using filtering conditions - if( ($filtersApplied || !is_null($tplan_tcases)) && - !$tcase_counters['testcase_count'] && ($node_type != 'testproject')) - { - $node = REMOVEME; // OK 20150129 - } - } - else if ($node_type == 'testsuite') - { - // does this means is an empty test suite ??? - franciscom 20080328 - $map_node_tccount[$node['id']] = array( 'testcount' => 0,'name' => $node['name']); - - // If is an EMPTY Test suite and we have added filtering conditions, - // We will destroy it. - if ($filtersApplied || !is_null($tplan_tcases) ) - { - $node = REMOVEME; // OK - 20150129 - } - } - - return $tcase_counters; -} - - -/** - * Create the string representation suitable to create a graphic visualization - * of a node, for the type of menu selected. - * - * Used when LAZY Rendering can not be used. - * - * @internal revisions - */ -function renderTreeNode($level,&$node,$hash_id_descr,$linkto,$testCasePrefix,$opt) -{ - - static $f2call; - static $forbidden_parents; - - $testCasesIDList=''; - - // ------------------------------------------------------------------------------- - // Choice for PERFORMANCE: - // Some pieces of code on TL < 1.9.4 has been wrapped in a function, but when working - // with BIG amount of testcases (> 5000) impact on performance was high. - if(!$f2call) - { - $f2call['testproject'] = 'EP'; - $f2call['testsuite'] = 'ETS'; - if( isset($opt['forPrinting']) && $opt['forPrinting'] ) - { - $f2call['testproject'] = 'TPROJECT_PTP'; - $f2call['testsuite'] = 'TPROJECT_PTS'; - } - - $f2call['testcase'] = $opt['tc_action_enabled'] ? 'ET' : 'void'; - - // Design allow JUST ONE forbidden, probably other measures - // like a leaf (test case) can not have other leaf as parent - // are already in place (need to check code better) - // - // IMPORTANT NOTICE: - // @20130407 - // this extended attribute need to be setted also on - // logic used when lazy tree build is used - // (gettprojectnodes.php) - // In addition tree config option useBeforeMoveNode must be set to true - $forbidden_parents['testproject'] = 'none'; - $forbidden_parents['testcase'] = 'testproject'; - $forbidden_parents['testsuite'] = 'testcase'; - } - - if( !isset($node['name']) ) - { - return $testCasesIDList; - } - - // custom Property that will be accessed by EXT-JS using node.attributes - // strip potential newlines and other unwanted chars from strings - // Mainly for stripping out newlines, carriage returns, and quotes that were - // causing problems in javascript using jtree - $node['testlink_node_name'] = str_replace(array("\n","\r"), array("",""), $node['name']); - $node['testlink_node_name'] = htmlspecialchars($node['testlink_node_name'], ENT_QUOTES); - - $node['testlink_node_type'] = $hash_id_descr[$node['node_type_id']]; - $node['forbidden_parent'] = $forbidden_parents[$node['testlink_node_type']]; - - $testcase_count = isset($node['testcase_count']) ? $node['testcase_count'] : 0; - $pfn = $f2call[$node['testlink_node_type']]; - - switch($node['testlink_node_type']) - { - case 'testproject': - case 'testsuite': - $node['text'] = $node['testlink_node_name'] . " (" . $testcase_count . ")"; - if(isset($opt['nodeHelpText'][$node['testlink_node_type']])) - { - $node['text'] = '' . - $node['text'] . ''; - } - break; - - case 'testcase': - $node['text'] = ""; - if($opt['showTestCaseID']) - { - $node['text'] .= "{$testCasePrefix}{$node['external_id']}:"; - } - $node['text'] .= $node['testlink_node_name']; - $testCasesIDList .= $node['id'] . ','; - break; - } // switch - - $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; - $node['href'] = "javascript:{$pfn}({$node['id']})"; - // ------------------------------------------------------------------------------- - - if (isset($node['childNodes']) && $node['childNodes']) - { - // need to work always original object - // in order to change it's values using reference . - // Can not assign anymore to intermediate variables. - // - $nChildren = sizeof($node['childNodes']); - for($idx = 0;$idx < $nChildren;$idx++) - { - // asimon - replaced is_null by !isset because of warnings in event log - if(!isset($node['childNodes'][$idx])) - { - continue; - } - $testCasesIDList .= renderTreeNode($level+1,$node['childNodes'][$idx],$hash_id_descr, - $linkto,$testCasePrefix,$opt); - } - } - - return $testCasesIDList; -} - - - -/** - * - * - * @param integer $level - * @param array &$node reference to recursive map - * @param array &$tcases_map reference to map that contains info about testcase exec status - * when node is of testcase type. - * - * @return datatype description - * @used-by execTreeMenu.inc.php - * - */ -function renderExecTreeNode($level,&$node,&$tcase_node,$hash_id_descr,$linkto,$testCasePrefix,$opt) -{ - static $resultsCfg; - static $l18n; - static $pf; - static $doColouringOn; - static $cssClasses; - - $node_type = $hash_id_descr[$node['node_type_id']]; - - if(!$resultsCfg) - { - $doColouringOn['testcase'] = 1; - $doColouringOn['counters'] = 1; - if( !is_null($opt['useColors']) ) - { - $doColouringOn['testcase'] = $opt['useColors']->testcases; - $doColouringOn['counters'] = $opt['useColors']->counters; - } - - $resultsCfg = config_get('results'); - $status_descr_code = $resultsCfg['status_code']; - - - foreach($resultsCfg['status_label'] as $key => $value) - { - $l18n[$status_descr_code[$key]] = lang_get($value); - - // here we use ONLY key - $cssClasses[$status_descr_code[$key]] = $doColouringOn['testcase'] ? ('class="light_' . $key . '"') : ''; - } - - // Very BAD CHOICE => SIDE EFFECT - $pf['testsuite'] = $opt['hideTestCases'] ? 'TPLAN_PTS' : ($opt['showTestSuiteContents'] ? 'STS' : null); - $pf['testproject'] = $opt['hideTestCases'] ? 'TPLAN_PTP' : 'SP'; - - if( isset($opt['actionJS']) ) { - $k2l = array('testproject','testsuite','testcase','testplan','default'); - foreach($k2l as $kiki) { - if( isset($opt['actionJS'][$kiki]) ){ - $pf[$kiki] = null; - if( '' != $opt['actionJS'][$kiki] ) { - $pf[$kiki] = $opt['actionJS'][$kiki]; - } - } - } - } - - // manage defaults - $opt['showTestCaseExecStatus'] = isset($opt['showTestCaseExecStatus']) ? $opt['showTestCaseExecStatus'] : true; - $opt['nodeHelpText'] = isset($opt['nodeHelpText']) ? $opt['nodeHelpText'] : array(); - } - - $name = htmlspecialchars($node['name'], ENT_QUOTES); - - - // custom Property that will be accessed by EXT-JS using node.attributes - $node['testlink_node_name'] = $name; - $node['testlink_node_type'] = $node_type; - - switch($node_type) { - case 'testproject': - case 'testsuite': - $node['leaf'] = false; - - $testcase_count = isset($node['testcase_count']) ? $node['testcase_count'] : 0; - $node['text'] = $name ." (" . $testcase_count . ")"; - if($opt['useCounters']) - { - $node['text'] .= create_counters_info($node,$doColouringOn['counters']); - } - - if( isset($opt['nodeHelpText'][$node_type]) ) - { - $node['text'] = '' . $node['text'] . ''; - } - - $pfn = !is_null($pf[$node_type]) ? $pf[$node_type] . "({$node['id']})" : null; - if( 'testsuite' == $node_type && ($opt['alertOnTestSuiteTCQty'] >0) ) { - if( $testcase_count > $opt['alertOnTestSuiteTCQty'] ) { - $jfn = config_get('jsAlertOnTestSuiteTCQty'); - $pfn = $jfn; - } - } - - - break; - - case 'testcase': - $node['leaf'] = true; - $pfn = null; - if($opt['tc_action_enabled']) { - $pfx = "ST"; - if(isset($pf[$node_type])) { - $pfx = "$pf[$node_type]"; - } - $pfn = $pfx . "({$node['id']},{$node['tcversion_id']})"; - } - - $node['text'] = "'; - } - } - - - if($opt['showTestCaseID']) - { - // optimizable - $node['text'] .= "" . htmlspecialchars($testCasePrefix . $node['external_id']) . ":"; - } - $node['text'] .= "{$name}"; - break; - - case 'testplan': - $pfn = "ST({$node['id']})"; - if( isset($pf[$node_type]) ){ - $pfn = null; - if( '' != $pf[$node_type] ) { - $pfn = $pf[$node_type] . "({$node['id']})"; - } - } - break; - - default: - $pfn = "ST({$node['id']})"; - if( isset($pf['default']) ){ - $pfn = null; - if( '' != $pf['default'] ) { - $pfn = $pf['default'] . "({$node['id']})"; - } - } - break; - } - - $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; - $node['href'] = is_null($pfn)? '' : "javascript:{$pfn}"; - - // ---------------------------------------------------------------------------------------------- - if( isset($tcase_node[$node['id']]) ) - { - unset($tcase_node[$node['id']]); // dam it NO COMMENT! - } - - if (isset($node['childNodes']) && $node['childNodes']) - { - // need to work always original object in order to change it's values using reference . - // Can not assign anymore to intermediate variables. - $nodes_qty = sizeof($node['childNodes']); - for($idx = 0;$idx <$nodes_qty ;$idx++) - { - if(is_null($node['childNodes'][$idx]) || $node['childNodes'][$idx]==REMOVEME) - { - continue; - } - renderExecTreeNode($level+1,$node['childNodes'][$idx],$tcase_node, - $hash_id_descr,$linkto,$testCasePrefix,$opt); - } - } - return; -} - - - -/** - * - * - * - */ -function create_counters_info(&$node,$useColors) -{ - static $keys2display; - static $labelCache; - - if(!$labelCache) - { - $resultsCfg = config_get('results'); - $status_label = $resultsCfg['status_label']; - - // I will add not_run if not exists - $keys2display = array('not_run' => 'not_run'); - foreach( $resultsCfg['status_label_for_exec_ui'] as $key => $value) - { - if( $key != 'not_run') - { - $keys2display[$key]=$key; - } - $labelCache[$key] = lang_get($status_label[$key]); - } - } - - $add_html=''; - foreach($keys2display as $key) - { - if( isset($node[$key]) ) - { - $css_class = $useColors ? (" class=\"light_{$key}\" ") : ''; - $add_html .= "' . $node[$key] . ","; - } - } - - $add_html = "(" . rtrim($add_html,",") . ")"; - return $add_html; -} - - - - -/** - * Filter out the testcases that don't have the given value - * in their custom field(s) from the tree. - * Recursive function. - * - * @author Andreas Simon - * @since 1.9 - * - * @param resource &$db reference to DB handler object - * @param array &$tcase_tree reference to test case set/tree to filter - * @param array &$cf_hash reference to selected custom field information - * @param int $node_types IDs of node types - * - * @return array $tcase_tree filtered tree structure - * - * @internal revisions - */ -function filter_by_cf_values(&$db, &$tcase_tree, &$cf_hash, $node_types) -{ - static $tables = null; - static $debugMsg = null; - - $rows = null; - if (!$debugMsg) - { - $tables = tlObject::getDBTables(array('cfield_design_values','nodes_hierarchy','tcversions')); - $debugMsg = 'Function: ' . __FUNCTION__; - } - - $node_deleted = false; - - // This code is in parts based on (NOT simply copy/pasted) - // some filter code used in testplan class. - // Implemented because we have a tree here, - // not simple one-dimensional array of testcases like in tplan class. - - foreach ($tcase_tree as $key => $node) - { - // TICKET 5186: Filtering by the value of custom fields on test specification is not working - if ($node['node_type_id'] == $node_types['testsuite']) - { - $delete_suite = false; - - if (isset($node['childNodes']) && is_array($node['childNodes'])) - { - // node is a suite and has children, so recurse one level deeper - $tcase_tree[$key]['childNodes'] = filter_by_cf_values($db,$tcase_tree[$key]['childNodes'], - $cf_hash,$node_types); - - // now remove testsuite node if it is empty after coming back from recursion - if (!count($tcase_tree[$key]['childNodes'])) - { - $delete_suite = true; - } - } - else - { - // nothing in here, suite was already empty - $delete_suite = true; - } - - if ($delete_suite) - { - unset($tcase_tree[$key]); - $node_deleted = true; - } - } - else if ($node['node_type_id'] == $node_types['testcase']) - { - // node is testcase, check if we need to delete it - $passed = false; - - // TICKET 5186: added "DISTINCT" to SQL clause, detailed explanation follows at the end of function - // Note: SQL statement has been adopted to filter by latest active tc version. - // That is a better solution for the explained problem than using the distinct keyword. - $latest_active_version_sql = " /* get latest active TC version ID */ " . - " SELECT MAX(TCVX.id) AS max_tcv_id, NHTCX.parent_id AS tc_id " . - " FROM {$tables['tcversions']} TCVX " . - " JOIN {$tables['nodes_hierarchy']} NHTCX " . - " ON NHTCX.id = TCVX.id AND TCVX.active = 1 " . - " WHERE NHTCX.parent_id = {$node['id']} " . - " GROUP BY NHTCX.parent_id, TCVX.tc_external_id "; - - $sql = " /* $debugMsg */ SELECT CFD.value " . - " FROM {$tables['cfield_design_values']} CFD, {$tables['nodes_hierarchy']} NH " . - " JOIN ( $latest_active_version_sql ) LAVSQL ON NH.id = LAVSQL.max_tcv_id " . - " WHERE CFD.node_id = NH.id "; - - // IMPORTANT DEV NOTES - // Query uses OR, but final processing makes that CF LOGIC work in AND MODE as expected - if (isset($cf_hash)) - { - $countmain = 1; - $cf_sql = ''; - foreach ($cf_hash as $cf_id => $cf_value) - { - if ( $countmain != 1 ) - { - $cf_sql .= " OR "; - } - - $safeID = intval($cf_id); - // single value or array? - if (is_array($cf_value)) - { - $count = 1; - foreach ($cf_value as $value) - { - if ($count > 1) - { - $cf_sql .= " AND "; - } - $safeValue = $db->prepare_string($value); - $cf_sql .= "( CFD.value LIKE '%{$safeValue}%' AND CFD.field_id = {$safeID} )"; - $count++; - } - } - else - { - $safeValue = $db->prepare_string($cf_value); - $cf_sql .= " ( CFD.value LIKE '%{$safeValue}%' AND CFD.field_id = {$safeID} ) "; - } - $countmain++; - } - $sql .= " AND ({$cf_sql}) "; - } - - $rows = $db->fetchColumnsIntoArray($sql,'value'); - - //if there exist as many rows as custom fields to be filtered by - //the tc does meet the criteria - - /* NOTE by asimon: This assumption was wrong! If there are multiple versions of a TC, - * then the row number here can be larger than the number of custom fields with the correct value. - * - * Example: - * Custom field "color" has possible values "red", "blue", "green", default empty. - * Custom field "status" has possible values "draft", "ready", "needs review", "needs rework", default empty. - * TC Version 1: cfield "color" has value "red", cfield "status" has no value yet. - * TC Version 2: cfield "color" has value "red", cfield "status" has no value yet. - * TC Version 3: cfield "color" and value "red", cfield "status" has value "ready". - * TC Version 4: cfield "color" has value "red", cfield "status" has value "ready". - * - * Filter by color GREEN and status READY, then $rows looks like this: Array ( [0] => red, [1] => red ) - * => count($rows) returns 2, which matches the number of custom fields we want to filter by. - * So TC lands in the result set instead of being filtered out. - * That is wrong, because TC matches only one of the fields we were filtering by! - * - * Because of this I extended the SQL statement above with the DISTINCT keyword, - * so that each custom field only is contained ONCE in the result set. - */ - - $passed = (count($rows) == count($cf_hash)) ? true : false; - // now delete node if no match was found - if (!$passed) - { - unset($tcase_tree[$key]); - $node_deleted = true; - } - } - } - - // 20100702 - asimon - // if we deleted a note, the numeric indexes of this array do have missing numbers, - // which causes problems in later loop constructs in other functions that assume numeric keys - // in these arrays without missing numbers in between - crashes JS tree! - // -> so I have to fix the array indexes here starting from 0 without missing a key - if ($node_deleted) { - $tcase_tree = array_values($tcase_tree); - } - - return $tcase_tree; -} - - -/** - * - * @param object &$tplan_mgr reference to test plan manager object - * @param array &$tcase_set reference to test case set to filter - * @param integer $tplan_id ID of test plan - * @param array $filters filters to apply to test case set - * @return array new tcase_set - */ -function filterStatusSetAtLeastOneOfActiveBuilds(&$tplan_mgr,&$tcase_set,$tplan_id,$filters) -{ - $safe_platform = intval($filters->setting_platform); - $buildSet = array_keys($tplan_mgr->get_builds($tplan_id, testplan::ACTIVE_BUILDS)); - if( !is_null($buildSet) ) - { - if( $safe_platform > 0 ) - { - tLog(basename(__FILE__) . __FUNCTION__ . ':: $tplan_mgr->getHitsSameStatusPartialOnPlatform', 'DEBUG'); - $hits = $tplan_mgr->getHitsSameStatusPartialOnPlatform($tplan_id,$safe_platform, - (array)$filters->filter_result_result); - } - else - { - tLog(basename(__FILE__) . __FUNCTION__ . ':: $tplan_mgr->getHitsSameStatusPartialALOP', 'DEBUG'); - $hits = $tplan_mgr->getHitsSameStatusPartialALOP($tplan_id,(array)$filters->filter_result_result); - } - - if( is_null($hits) ) - { - $tcase_set = array(); - } - else - { - helper_filter_cleanup($tcase_set,$hits); - } - } - - return $tcase_set; -} - - -/** - * filterStatusSetAllActiveBuilds() - * - * returns: - * - * test cases that has AT LEAST ONE of requested status - * or combinations of requested status - * ON LAST EXECUTION ON ALL ACTIVE builds, for a PLATFORM - * - * For examples and more info read documentation regarding - * getHits*() methods on testplan class. - * - * - * @param object &$tplan_mgr reference to test plan manager object - * @param array &$tcase_set reference to test case set to filter - * WILL BE MODIFIED HERE - * - * @param integer $tplan_id ID of test plan - * @param array $filters filters to apply to test case set - * - * @return array new tcase_set - */ -function filterStatusSetAllActiveBuilds(&$tplan_mgr,&$tcase_set,$tplan_id,$filters) { - $buildSet = array_keys($tplan_mgr->get_builds($tplan_id, testplan::ACTIVE_BUILDS)); - if( !is_null($buildSet) ) { - - $safe_platform = intval($filters->setting_platform); - if( $safe_platform > 0 ) { - tLog(basename(__FILE__) . __FUNCTION__ . ':: $tplan_mgr->getHitsSameStatusFullOnPlatform', 'DEBUG'); - $hits = $tplan_mgr->getHitsSameStatusFullOnPlatform($tplan_id,$safe_platform, - (array)$filters->filter_result_result,$buildSet); - } else { - tLog(basename(__FILE__) .__FUNCTION__ . ':: $tplan_mgr->getHitsSameStatusFullALOP', 'DEBUG'); - - $hits = $tplan_mgr->getHitsSameStatusFullALOP($tplan_id, - (array)$filters->filter_result_result,$buildSet); - } - - if( is_null($hits) ) { - $tcase_set = array(); - } else { - helper_filter_cleanup($tcase_set,$hits); - unset($hits); - } - } - return $tcase_set; -} - -/** - * used by filter options: - * result on specific build - * result on current build - * - * - * @param object &$tplan_mgr reference to test plan manager object - * @param array &$tcase_set reference to test case set to filter - * @param integer $tplan_id ID of test plan - * @param array $filters filters to apply to test case set - * - * @return array new tcase_set - */ -function filter_by_status_for_build(&$tplan_mgr,&$tcase_set,$tplan_id,$filters) -{ - $safe_platform = intval($filters->setting_platform); - $safe_build = intval($filters->filter_result_build); - if( $safe_platform > 0) - { - tLog(__FUNCTION__ . ':: $tplan_mgr->getHitsStatusSetOnBuildPlatform', 'DEBUG'); - $hits = $tplan_mgr->getHitsStatusSetOnBuildPlatform($tplan_id,$safe_platform,$safe_build, - (array)$filters->filter_result_result); - } - else - { - tLog(__FUNCTION__ . ':: $tplan_mgr->getHitsStatusSetOnBuildALOP', 'DEBUG'); - $hits = $tplan_mgr->getHitsStatusSetOnBuildALOP($tplan_id,$safe_build, - (array)$filters->filter_result_result); - } - - if( is_null($hits) ) - { - $tcase_set = array(); - } - else - { - helper_filter_cleanup($tcase_set,$hits); - } - - return $tcase_set; -} - -/** - * filter testcases by the result of their latest execution - * - * CAN NOT BE USED FOR NOT RUN because Not run is not saved on DB - * - * @param object &$db reference to database handler - * @param object &$tplan_mgr reference to test plan manager object - * @param array &$tcase_set reference to test case set to filter - * @param integer $tplan_id ID of test plan - * @param array $filters filters to apply to test case set - * @return array new tcase_set - */ -function filter_by_status_for_latest_execution(&$tplan_mgr,&$tcase_set,$tplan_id,$filters) -{ - - $safe_tplan = intval($tplan_id); - $safe_platform = intval($filters->setting_platform); - - if($safe_platform > 0) - { - $hits = $tplan_mgr->getHitsStatusSetOnLatestExecOnPlatform($safe_tplan,$safe_platform, - (array)$filters->filter_result_result); - } - else - { - $hits = $tplan_mgr->getHitsStatusSetOnLatestExecALOP($safe_tplan,(array)$filters->filter_result_result); - } - - if( is_null($hits) ) - { - $tcase_set = array(); - } - else - { - helper_filter_cleanup($tcase_set,$hits); - } - - return $tcase_set; -} - - -/** - * - * @param object &$tplan_mgr reference to test plan manager object - * @param array &$tcase_set reference to test case set to filter - * @param integer $tplan_id ID of test plan - * @param array $filters filters to apply to test case set - * @return array new tcase_set - */ -function filter_not_run_for_any_build(&$tplan_mgr,&$tcase_set,$tplan_id,$filters) -{ - - $safe_platform = intval($filters->setting_platform); - if( $safe_platform > 0) - { - $hits = $tplan_mgr->getHitsNotRunPartialOnPlatform($tplan_id,intval($filters->setting_platform)); - } - else - { - $hits = $tplan_mgr->getHitsNotRunPartialALOP($tplan_id); - } - - if( is_null($hits) ) - { - $tcase_set = array(); - } - else - { - helper_filter_cleanup($tcase_set,$hits); - } - - return $tcase_set; -} - - -/** - * generate array with Keywords for a filter - * - */ -function buildKeywordsFilter($keywordsId,&$guiObj) -{ - $keywordsFilter = null; - - if(!is_null($keywordsId)) - { - $items = array_flip((array)$keywordsId); - if(!isset($items[0])) - { - $keywordsFilter = new stdClass(); - $keywordsFilter->items = $keywordsId; - $keywordsFilter->type = isset($guiObj->keywordsFilterTypes) ? $guiObj->keywordsFilterTypes->selected: 'OR'; - } - } - - return $keywordsFilter; -} - - -/** - * generate object with test case execution type for a filter - * - */ -function buildExecTypeFilter($execTypeSet) -{ - $itemsFilter = null; - - if(!is_null($execTypeSet)) - { - $items = array_flip((array)$execTypeSet); - if(!isset($items[0])) - { - $itemsFilter = new stdClass(); - $itemsFilter->items = $execTypeSet; - } - } - - return $itemsFilter; -} - -/** - * generate object with test case importance for a filter - * - */ -function buildImportanceFilter($importance) -{ - $itemsFilter = null; - - if(!is_null($importance)) - { - $items = array_flip((array)$importance); - if(!isset($items[0])) - { - $itemsFilter = new stdClass(); - $itemsFilter->items = $importance; - } - } - - return $itemsFilter; -} - -/** - * Generate the necessary data object for the filtered requirement specification tree. - * - * @author Andreas Simon - * @param Database $db reference to database handler object - * @param testproject $testproject_mgr reference to testproject manager object - * @param int $testproject_id ID of the project for which the tree shall be generated - * @param string $testproject_name Name of the test project - * @param array $filters Filter settings which shall be applied to the tree, possible values are: - * 'filter_doc_id', - * 'filter_title', - * 'filter_status', - * 'filter_type', - * 'filter_spec_type', - * 'filter_coverage', - * 'filter_relation', - * 'filter_tc_id', - * 'filter_custom_fields' - * @param array $options Further options which shall be applied on generating the tree - * @return stdClass $treeMenu object with which ExtJS can generate the graphical tree - */ -function generate_reqspec_tree(&$db, &$testproject_mgr, $testproject_id, $testproject_name, - $filters = null, $options = null) -{ - - $tables = tlObjectWithDB::getDBTables(array('requirements', 'req_versions', - 'req_specs', 'req_relations', - 'req_specs_revisions', - 'req_coverage', 'nodes_hierarchy')); - - $tree_manager = &$testproject_mgr->tree_manager; - - $glue_char = config_get('testcase_cfg')->glue_character; - $tcase_prefix=$testproject_mgr->getTestCasePrefix($testproject_id) . $glue_char; - - $req_node_type = $tree_manager->node_descr_id['testcase']; - $req_spec_node_type = $tree_manager->node_descr_id['testsuite']; - - $map_nodetype_id = $tree_manager->get_available_node_types(); - $map_id_nodetype = array_flip($map_nodetype_id); - - $my = array(); - - $my['options'] = array('for_printing' => 0,'exclude_branches' => null, - 'recursive' => true,'order_cfg' => array('type' => 'spec_order')); - - $my['filters'] = array('exclude_node_types' => array('testplan' => 'exclude me', - 'testsuite' => 'exclude me', - 'testcase' => 'exclude me', - 'requirement_spec_revision' => 'exclude me'), - 'exclude_children_of' => array('testcase' => 'exclude my children', - 'requirement' => 'exclude my children', - 'testsuite' => 'exclude my children'), - 'filter_doc_id' => null, 'filter_title' => null, - 'filter_status' => null, 'filter_type' => null, - 'filter_spec_type' => null, 'filter_coverage' => null, - 'filter_relation' => null, 'filter_tc_id' => null, - 'filter_custom_fields' => null); - - // merge with given parameters - $my['options'] = array_merge($my['options'], (array) $options); - $my['filters'] = array_merge($my['filters'], (array) $filters); - - $req_spec = $tree_manager->get_subtree($testproject_id, $my['filters'], $my['options']); - - $req_spec['name'] = $testproject_name; - $req_spec['id'] = $testproject_id; - $req_spec['node_type_id'] = $map_nodetype_id['testproject']; - - $filtered_map = get_filtered_req_map($db, $testproject_id, $testproject_mgr, - $my['filters'], $my['options']); - - $level = 1; - $req_spec = prepare_reqspec_treenode($db, $level, $req_spec, $filtered_map, $map_id_nodetype, - $map_nodetype_id, $my['filters'], $my['options']); - - $menustring = null; - $treeMenu = new stdClass(); - $treeMenu->rootnode = new stdClass(); - $treeMenu->rootnode->total_req_count = $req_spec['total_req_count']; - $treeMenu->rootnode->name = $req_spec['name']; - $treeMenu->rootnode->id = $req_spec['id']; - $treeMenu->rootnode->leaf = isset($req_spec['leaf']) ? $req_spec['leaf'] : false; - $treeMenu->rootnode->position = $req_spec['position']; - $treeMenu->rootnode->href = $req_spec['href']; - - // replace key ('childNodes') to 'children' - if (isset($req_spec['childNodes'])) - { - $menustring = str_ireplace('childNodes', 'children', json_encode($req_spec['childNodes'])); - } - - if (!is_null($menustring)) - { - $menustring = str_ireplace(array(',"' . REMOVEME .'"', '"' . REMOVEME . '",'), - array('',''), $menustring); - - $menustring = str_ireplace(array(':' . REMOVEME, '"' . REMOVEME .'"'), - array(':[]',''), $menustring); - } - $treeMenu->menustring = $menustring; - - return $treeMenu; -} - -/** - * Generate the necessary data object for the filtered requirement specification tree. - * - * @author Andreas Simon - * @param Database $db reference to database handler object - * @param testproject $testproject_mgr reference to testproject manager object - * @param int $testproject_id ID of the project for which the tree shall be generated - * @param string $testproject_name Name of the test project - * @param array $filters Filter settings which shall be applied to the tree, possible values are: - * 'filter_doc_id', - * 'filter_title', - * 'filter_status', - * 'filter_type', - * 'filter_spec_type', - * 'filter_coverage', - * 'filter_relation', - * 'filter_tc_id', - * 'filter_custom_fields' - * @param array $options Further options which shall be applied on generating the tree - * @return stdClass $treeMenu object with which ExtJS can generate the graphical tree - */ -function generateTestReqCoverageTree(&$db,$tproject_id, $tproject_name,$linkto,$filters=null,$options=null) -{ - - $tables = tlObjectWithDB::getDBTables(array('requirements', 'req_versions', - 'req_specs', 'req_relations', - 'req_specs_revisions', - 'req_coverage', 'nodes_hierarchy')); - - $tproject_mgr = new testproject($db); - $tree_manager = &$tproject_mgr->tree_manager; - - $glue_char = config_get('testcase_cfg')->glue_character; - $tcase_prefix=$tproject_mgr->getTestCasePrefix($tproject_id) . $glue_char; - - $req_node_type = $tree_manager->node_descr_id['testcase']; - $req_spec_node_type = $tree_manager->node_descr_id['testsuite']; - - $map_nodetype_id = $tree_manager->get_available_node_types(); - $map_id_nodetype = array_flip($map_nodetype_id); - - $my = array(); - - $my['options'] = array('for_printing' => 0, - 'exclude_branches' => null, - 'recursive' => true, - 'order_cfg' => array('type' => 'spec_order')); - - $my['filters'] = array('exclude_node_types' => array('testplan' => 'exclude me', - 'testsuite' => 'exclude me', - 'testcase' => 'exclude me', - 'requirement_spec_revision' => 'exclude me'), - 'exclude_children_of' => array('testcase' => 'exclude my children', - 'requirement' => 'exclude my children', - 'testsuite' => 'exclude my children'), - 'filter_doc_id' => null, - 'filter_title' => null, - 'filter_status' => null, - 'filter_type' => null, - 'filter_spec_type' => null, - 'filter_coverage' => null, - 'filter_relation' => null, - 'filter_tc_id' => null, - 'filter_custom_fields' => null); - - // merge with given parameters - $my['options'] = array_merge($my['options'], (array) $options); - $my['filters'] = array_merge($my['filters'], (array) $filters); - - $req_spec = $tree_manager->get_subtree($tproject_id, $my['filters'], $my['options']); - - $req_spec['name'] = $tproject_name; - $req_spec['id'] = $tproject_id; - $req_spec['node_type_id'] = $map_nodetype_id['testproject']; - - $filtered_map = get_filtered_req_map($db, $tproject_id, $tproject_mgr, - $my['filters'], $my['options']); - - $level = 1; - $req_spec = prepare_reqspeccoverage_treenode($db, $level, $req_spec, $filtered_map, $map_id_nodetype, - $map_nodetype_id, $my['filters'], $my['options']); - - $menustring = null; - $treeMenu = new stdClass(); - $treeMenu->rootnode = new stdClass(); - $treeMenu->rootnode->total_req_count = $req_spec['total_req_count']; - $treeMenu->rootnode->name = $req_spec['name']; - $treeMenu->rootnode->id = $req_spec['id']; - $treeMenu->rootnode->leaf = isset($req_spec['leaf']) ? $req_spec['leaf'] : false; - //$treeMenu->rootnode->text = $req_spec['name']; //not needed, accidentally duplicated - $treeMenu->rootnode->position = $req_spec['position']; - $treeMenu->rootnode->href = $req_spec['href']; - - // replace key ('childNodes') to 'children' - if (isset($req_spec['childNodes'])) - { - $menustring = str_ireplace('childNodes', 'children', - json_encode($req_spec['childNodes'])); - } - - if (!is_null($menustring)) - { - // delete null elements for Ext JS - $menustring = str_ireplace(array(':null',',null','null,','null'), - array(':[]','','',''), - $menustring); - } - $treeMenu->menustring = $menustring; - - return $treeMenu; -} - -/** - * Generate a filtered map with all fitting requirements in it. - * - * @author Andreas Simon - * @param Database $db reference to database handler object - * @param int $testproject_id ID of the project for which the tree shall be generated - * @param testproject $testproject_mgr reference to testproject manager object - * @param array $filters Filter settings which shall be applied to the tree - * @param array $options Further options which shall be applied on generating the tree - * @return array $filtered_map map with all fitting requirements - * - * @internal revisions - * @since 1.9.4 - * 20120827 - franciscom - TICKET 5178: Requirement Specification->"Req. Spec. Type" Filter-> KO - * - */ -function get_filtered_req_map(&$db, $testproject_id, &$testproject_mgr, $filters, $options) { - $filtered_map = null; - $tables = tlObjectWithDB::getDBTables(array('nodes_hierarchy', 'requirements', 'req_specs', - 'req_relations', 'req_versions', 'req_coverage', - 'tcversions', 'cfield_design_values','req_specs_revisions')); - - $sql = " SELECT R.id, R.req_doc_id, NH_R.name AS title, R.srs_id, " . - " RS.doc_id AS req_spec_doc_id, NH_RS.name AS req_spec_title, " . - " RV.version, RV.id AS version_id, NH_R.node_order, " . - " RV.expected_coverage, RV.status, RV.type, RV.active, RV.is_open " . - " FROM {$tables['requirements']} R " . - " JOIN {$tables['nodes_hierarchy']} NH_R ON NH_R.id = R.id " . - " JOIN {$tables['nodes_hierarchy']} NH_RV ON NH_RV.parent_id = NH_R.id " . - " JOIN {$tables['req_versions']} RV ON RV.id = NH_RV.id " . - " JOIN {$tables['req_specs']} RS ON RS.id = R.srs_id " . - " JOIN {$tables['req_specs_revisions']} RSPECREV ON RSPECREV.parent_id = RS.id " . - " JOIN {$tables['nodes_hierarchy']} NH_RS ON NH_RS.id = RS.id "; - - if (isset($filters['filter_relation'])) - { - $sql .= " JOIN {$tables['req_relations']} RR " . - " ON (RR.destination_id = R.id OR RR.source_id = R.id) "; - } - - if (isset($filters['filter_tc_id'])) - { - $tc_cfg = config_get('testcase_cfg'); - $tc_prefix = $testproject_mgr->getTestCasePrefix($testproject_id); - $tc_prefix .= $tc_cfg->glue_character; - - $tc_ext_id = $db->prepare_int(str_replace($tc_prefix, '', $filters['filter_tc_id'])); - - $sql .= " JOIN {$tables['req_coverage']} RC ON RC.req_id = R.id " . - " JOIN {$tables['nodes_hierarchy']} NH_T ON NH_T.id = RC.testcase_id " . - " JOIN {$tables['nodes_hierarchy']} NH_TV on NH_TV.parent_id = NH_T.id " . - " JOIN {$tables['tcversions']} TV ON TV.id = NH_TV.id " . - " AND TV.tc_external_id = {$tc_ext_id} "; - } - - if (isset($filters['filter_custom_fields'])) - { - $suffix = 1; - - foreach ($filters['filter_custom_fields'] as $cf_id => $cf_value) - { - $sql .= " JOIN {$tables['cfield_design_values']} CF{$suffix} " . - " ON CF{$suffix}.node_id = RV.id " . - " AND CF{$suffix}.field_id = {$cf_id} "; - - // single value or array? - if (is_array($cf_value)) - { - $sql .= " AND ( "; - $count = 1; - foreach ($cf_value as $value) - { - if ($count > 1) - { - $sql .= " OR "; - } - $sql .= " CF{$suffix}.value LIKE '%{$value}%' "; - $count++; - } - $sql .= " ) "; - } - else - { - $sql .= " AND CF{$suffix}.value LIKE '%{$cf_value}%' "; - } - - $suffix ++; - } - } - - $sql .= " WHERE RS.testproject_id = {$testproject_id} "; - - if (isset($filters['filter_doc_id'])) - { - $doc_id = $db->prepare_string($filters['filter_doc_id']); - $sql .= " AND R.req_doc_id LIKE '%{$doc_id}%' OR RS.doc_id LIKE '%{$doc_id}%' "; - } - - if (isset($filters['filter_title'])) - { - $title = $db->prepare_string($filters['filter_title']); - $sql .= " AND NH_R.name LIKE '%{$title}%' "; - } - - if (isset($filters['filter_coverage'])) - { - $coverage = $db->prepare_int($filters['filter_coverage']); - $sql .= " AND expected_coverage = {$coverage} "; - } - - if (isset($filters['filter_status'])) - { - $statuses = (array) $filters['filter_status']; - foreach ($statuses as $key => $status) - { - $statuses[$key] = "'" . $db->prepare_string($status) . "'"; - } - $statuses = implode(",", $statuses); - $sql .= " AND RV.status IN ({$statuses}) "; - } - - if (isset($filters['filter_type'])) - { - $types = (array) $filters['filter_type']; - - foreach ($types as $key => $type) - { - $types[$key] = $db->prepare_string($type); - } - $types = implode("','", $types); - $sql .= " AND RV.type IN ('{$types}') "; - } - - if (isset($filters['filter_spec_type'])) - { - $spec_types = (array) $filters['filter_spec_type']; - - foreach ($spec_types as $key => $type) - { - $spec_types[$key] = $db->prepare_string($type); - } - $spec_types = implode("','", $spec_types); - $sql .= " AND RSPECREV.type IN ('{$spec_types}') "; - } - - if (isset($filters['filter_relation'])) - { - $sql .= " AND ( "; - $count = 1; - foreach ($filters['filter_relation'] as $key => $rel_filter) - { - $relation_info = explode('_', $rel_filter); - $relation_type = $db->prepare_int($relation_info[0]); - $relation_side = isset($relation_info[1]) ? $relation_info[1] : null; - $sql .= ($count == 1) ? " ( " : " OR ( "; - - if ($relation_side == "destination") - { - $sql .= " RR.destination_id = R.id "; - } - else if ($relation_side == "source") - { - $sql .= " RR.source_id = R.id "; - } - else - { - $sql .= " (RR.destination_id = R.id OR RR.source_id = R.id) "; - } - - $sql .= " AND RR.relation_type = {$relation_type} ) "; - $count++; - } - - $sql .= " ) "; - } - - $sql .= " ORDER BY RV.version DESC "; - $filtered_map = $db->fetchRowsIntoMap($sql, 'id'); - return $filtered_map; -} - -/** - * Prepares nodes for the filtered requirement tree. - * Filters out those nodes which are not in the given map and counts the remaining subnodes. - * @author Andreas Simn - * @param Database $db reference to database handler object - * @param int $level gets increased by one for each sublevel in recursion - * @param array $node the tree structure to traverse - * @param array $filtered_map a map of filtered requirements, req that are not in this map will be deleted - * @param array $map_id_nodetype array with node type IDs as keys, node type descriptions as values - * @param array $map_nodetype_id array with node type descriptions as keys, node type IDs as values - * @param array $filters - * @param array $options - * @return array tree structure after filtering out unneeded nodes - */ -function prepare_reqspec_treenode(&$db, $level, &$node, &$filtered_map, &$map_id_nodetype, - &$map_nodetype_id, &$filters, &$options) -{ - $child_req_count = 0; - if (isset($node['childNodes']) && is_array($node['childNodes'])) - { - // node has childs, must be a specification (or testproject) - foreach ($node['childNodes'] as $key => $childnode) - { - $current_childnode = &$node['childNodes'][$key]; - $current_childnode = prepare_reqspec_treenode($db, $level + 1, $current_childnode, - $filtered_map, $map_id_nodetype, - $map_nodetype_id, - $filters, $options); - - // now count childnodes that have not been deleted and are requirements - if(!is_null($current_childnode) && $current_childnode != REMOVEME) - { - switch($current_childnode['node_type_id']) - { - case $map_nodetype_id['requirement']: - $child_req_count ++; - break; - - case $map_nodetype_id['requirement_spec']: - $child_req_count += $current_childnode['child_req_count']; - break; - } - } - } - } - - $node_type = $map_id_nodetype[$node['node_type_id']]; - - $delete_node = false; - switch ($node_type) - { - case 'testproject': - $node['total_req_count'] = $child_req_count; - break; - - case 'requirement_spec': - // add requirement count - // delete empty specs - $node['child_req_count'] = $child_req_count; - $delete_node = !$child_req_count; - break; - - case 'requirement': - // delete node from tree if it is not in $filtered_map - $delete_node = (is_null($filtered_map) || !array_key_exists($node['id'], $filtered_map)); - break; - } - - if ($delete_node) - { - unset($node); - $node = REMOVEME; - } - else - { - $node = render_reqspec_treenode($db, $node, $filtered_map, $map_id_nodetype); - } - - return $node; -} - -/** - * Prepares nodes for the filtered requirement tree. - * Filters out those nodes which are not in the given map and counts the remaining subnodes. - * @author Andreas Simn - * @param Database $db reference to database handler object - * @param int $level gets increased by one for each sublevel in recursion - * @param array $node the tree structure to traverse - * @param array $filtered_map a map of filtered requirements, req that are not in this map will be deleted - * @param array $map_id_nodetype array with node type IDs as keys, node type descriptions as values - * @param array $map_nodetype_id array with node type descriptions as keys, node type IDs as values - * @param array $filters - * @param array $options - * @return array tree structure after filtering out unneeded nodes - */ -function prepare_reqspeccoverage_treenode(&$db, $level, &$node, &$filtered_map, &$map_id_nodetype, - &$map_nodetype_id, &$filters, &$options) { - $child_req_count = 0; - - if (isset($node['childNodes']) && is_array($node['childNodes'])) { - // node has childs, must be a specification (or testproject) - foreach ($node['childNodes'] as $key => $childnode) { - $current_childnode = &$node['childNodes'][$key]; - $current_childnode = prepare_reqspeccoverage_treenode($db, $level + 1, $current_childnode, - $filtered_map, $map_id_nodetype, - $map_nodetype_id, - $filters, $options); - - // now count childnodes that have not been deleted and are requirements - if (!is_null($current_childnode)) { - switch ($current_childnode['node_type_id']) { - case $map_nodetype_id['requirement']: - $child_req_count ++; - break; - - case $map_nodetype_id['requirement_spec']: - $child_req_count += $current_childnode['child_req_count']; - break; - } - } - } - } - - $node_type = $map_id_nodetype[$node['node_type_id']]; - - $delete_node = false; - - switch ($node_type) { - case 'testproject': - $node['total_req_count'] = $child_req_count; - break; - - case 'requirement_spec': - // add requirement count - $node['child_req_count'] = $child_req_count; - // delete empty specs - if (!$child_req_count) { - $delete_node = true; - } - break; - - case 'requirement': - // delete node from tree if it is not in $filtered_map - if (is_null($filtered_map) || !array_key_exists($node['id'], $filtered_map)) { - $delete_node = true; - } - break; - } - - if ($delete_node) { - unset($node); - $node = null; - } else { - $node = render_reqspeccoverage_treenode($db, $node, $filtered_map, $map_id_nodetype); - } - - return $node; -} - -/** - * Prepares nodes in the filtered requirement tree for displaying with ExtJS. - * @author Andreas Simon - * @param Database $db reference to database handler object - * @param array $node the object to prepare - * @param array $filtered_map a map of filtered requirements, req that are not in this map will be deleted - * @param array $map_id_nodetype array with node type IDs as keys, node type descriptions as values - * @return array tree object with all needed data for ExtJS tree - */ -function render_reqspec_treenode(&$db, &$node, &$filtered_map, &$map_id_nodetype) { - static $js_functions; - static $forbidden_parents; - - if (!$js_functions) { - $js_functions = array('testproject' => 'TPROJECT_REQ_SPEC_MGMT', - 'requirement_spec' =>'REQ_SPEC_MGMT', - 'requirement' => 'REQ_MGMT'); - - $req_cfg = config_get('req_cfg'); - $forbidden_parents['testproject'] = 'none'; - $forbidden_parents['requirement'] = 'testproject'; - - // Hmm is ok ? (see next lines, may be it's time to remove this code) - $forbidden_parents['requirement_spec'] = 'requirement_spec'; - if($req_cfg->child_requirements_mgmt) - { - $forbidden_parents['requirement_spec'] = 'none'; - } - } - - $node_type = $map_id_nodetype[$node['node_type_id']]; - $node_id = $node['id']; - - $node['href'] = "javascript:{$js_functions[$node_type]}({$node_id});"; - $node['text'] = htmlspecialchars($node['name']); - $node['leaf'] = false; // will be set to true later for requirement nodes - $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; - $node['cls'] = 'folder'; - - // custom Properties that will be accessed by EXT-JS using node.attributes - $node['testlink_node_type'] = $node_type; - $node['forbidden_parent'] = $forbidden_parents[$node_type]; - $node['testlink_node_name'] = $node['text']; - - switch ($node_type) { - case 'testproject': - break; - - case 'requirement_spec': - // get doc id from filtered array, it's already stored in there - $doc_id = ''; - foreach($node['childNodes'] as $child) { - if ( is_array($child) ) { - $child_id = $child['id']; - if (isset($filtered_map[$child_id])) { - $doc_id = htmlspecialchars($filtered_map[$child_id]['req_spec_doc_id']); - } - break; // only need to get one child for this - } - } - // BUGID 3765: load doc ID with if this req spec has no direct req child nodes. - // Reason: in these cases we do not have a parent doc ID in $filtered_map - if ($doc_id == '') { - static $req_spec_mgr = null; - if (!$req_spec_mgr) { - $req_spec_mgr = new requirement_spec_mgr($db); - } - $tmp_spec = $req_spec_mgr->get_by_id($node_id); - $doc_id = $tmp_spec['doc_id']; - unset($tmp_spec); - } - - $count = $node['child_req_count']; - $node['text'] = "{$doc_id}:{$node['text']} ({$count})"; - break; - - case 'requirement': - $node['leaf'] = true; - $doc_id = htmlspecialchars($filtered_map[$node_id]['req_doc_id']); - $node['text'] = "{$doc_id}:{$node['text']}"; - break; - } - - return $node; -} - - - - -/** - * Prepares nodes in the filtered requirement tree for displaying with ExtJS. - * @author Andreas Simon - * @param Database $db reference to database handler object - * @param array $node the object to prepare - * @param array $filtered_map a map of filtered requirements, req that are not in this map will be deleted - * @param array $map_id_nodetype array with node type IDs as keys, node type descriptions as values - * @return array tree object with all needed data for ExtJS tree - */ -function render_reqspeccoverage_treenode(&$db, &$node, &$filtered_map, &$map_id_nodetype) { - static $js_functions; - static $forbidden_parents; - - if (!$js_functions) - { - $js_functions = array('testproject' => 'EP', - 'requirement_spec' =>'ERS', - 'requirement' => 'ER'); - - $req_cfg = config_get('req_cfg'); - $forbidden_parents['testproject'] = 'none'; - $forbidden_parents['requirement'] = 'testproject'; - - // Hmm is ok ? (see next lines, may be it's time to remove this code) - $forbidden_parents['requirement_spec'] = 'requirement_spec'; - if($req_cfg->child_requirements_mgmt) - { - $forbidden_parents['requirement_spec'] = 'none'; - } - } - - $node_type = $map_id_nodetype[$node['node_type_id']]; - $node_id = $node['id']; - - $node['href'] = "javascript:{$js_functions[$node_type]}({$node_id});"; - $node['text'] = htmlspecialchars($node['name']); - $node['leaf'] = false; // will be set to true later for requirement nodes - $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; - $node['cls'] = 'folder'; - - // custom Properties that will be accessed by EXT-JS using node.attributes - $node['testlink_node_type'] = $node_type; - $node['forbidden_parent'] = $forbidden_parents[$node_type]; - $node['testlink_node_name'] = $node['text']; - - switch ($node_type) { - case 'testproject': - break; - - case 'requirement_spec': - // get doc id from filtered array, it's already stored in there - $doc_id = ''; - foreach($node['childNodes'] as $child) { - if (!is_null($child)) { - $child_id = $child['id']; - if (isset($filtered_map[$child_id])) { - $doc_id = htmlspecialchars($filtered_map[$child_id]['req_spec_doc_id']); - } - break; // only need to get one child for this - } - } - // BUGID 3765: load doc ID with if this req spec has no direct req child nodes. - // Reason: in these cases we do not have a parent doc ID in $filtered_map - if ($doc_id == '') { - static $req_spec_mgr = null; - if (!$req_spec_mgr) { - $req_spec_mgr = new requirement_spec_mgr($db); - } - $tmp_spec = $req_spec_mgr->get_by_id($node_id); - $doc_id = $tmp_spec['doc_id']; - unset($tmp_spec); - } - - $count = $node['child_req_count']; - $node['text'] = "{$doc_id}:{$node['text']} ({$count})"; - break; - - case 'requirement': - $node['leaf'] = true; - $doc_id = htmlspecialchars($filtered_map[$node_id]['req_doc_id']); - $node['text'] = "{$doc_id}:{$node['text']}"; - break; - } - - return $node; -} - - -/** - * - * - */ -function apply_status_filters($tplan_id,&$items,&$fobj,&$tplan_mgr,$statusCfg) -{ - $fm = config_get('execution_filter_methods'); - $methods = $fm['status_code']; - - - $ffn = array($methods['any_build'] => 'filterStatusSetAtLeastOneOfActiveBuilds', - $methods['all_builds'] => 'filterStatusSetAllActiveBuilds', - $methods['specific_build'] => 'filter_by_status_for_build', - $methods['current_build'] => 'filter_by_status_for_build', - $methods['latest_execution'] => 'filter_by_status_for_latest_execution'); - - $f_method = isset($fobj->filter_result_method) ? $fobj->filter_result_method : null; - $f_result = isset($fobj->filter_result_result) ? $fobj->filter_result_result : null; - $f_result = (array)$f_result; - - // if "any" was selected as filtering status, don't filter by status - if (in_array($statusCfg['all'], $f_result)) - { - $f_result = null; - } - - if (!is_null($f_method) && isset($ffn[$f_method])) - { - // special case: - // filtering by "not run" status in any build - // filtering by "not run" status in specific - // - // we change filter function - if (in_array($statusCfg['not_run'], $f_result)) - { - $ffn[$methods['any_build']] = 'filter_not_run_for_any_build'; - $ffn[$methods['specific_build']] = 'filter_by_status_for_build'; - } - - // special case: when filtering by "current build", we set the build to filter with - // to the build chosen in settings instead of the one in filters - if ($f_method == $methods['current_build']) - { - $fobj->filter_result_build = $fobj->setting_build; - } - - $items = $ffn[$f_method]($tplan_mgr, $items, $tplan_id, $fobj); - } - return $items; -} - -/** - * - * - */ -function update_status_for_colors(&$dbHandler,&$items,$context,$statusCfg) -{ - $tables = tlObject::getDBTables(array('executions','nodes_hierarchy')); - $dummy = current($items); - $key2scan = array_keys($items); - $keySet = null; - foreach($key2scan as $fx) - { - $keySet[] = $items[$fx]['tcversion_id']; - } - - extract($context); // magic to create single variables - - $sql = " SELECT E.status, NH_TCV.parent_id AS tcase_id " . - " FROM {$tables['executions']} E " . - " JOIN {$tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = E.tcversion_id " . - " JOIN " . - " ( SELECT MAX(E2.id) AS last_exec_id " . - " FROM {$tables['executions']} E2 " . - " WHERE testplan_id = {$tplanID} " . - " AND tcversion_id IN (" . implode(',', $keySet) . ") " . - " AND platform_id = {$dummy['platform_id']} " . - " AND build_id = {$buildID} " . - " GROUP BY testplan_id,tcversion_id,platform_id,build_id ) AS EY " . - " ON E.id = EY.last_exec_id "; - - $result = null; - $rs = $dbHandler->fetchRowsIntoMap($sql,'tcase_id'); - - if( !is_null($rs) ) - { - foreach($key2scan as $tcase_id) - { - $rr = isset($rs[$tcase_id]['status']) && !is_null($rs[$tcase_id]['status']) ? - $rs[$tcase_id]['status'] : $statusCfg['not_run']; - - if ($rr != $items[$tcase_id]['exec_status']) - { - $items[$tcase_id]['exec_status'] = $rr; - } - } - } - -} - - -/** - * Used when ['viewType'] == 'testSpecTree' - */ -function generateTestSpecTreeNew(&$db,$tproject_id, $tproject_name,$linkto,$filters=null,$options=null) -{ - $chronos[] = microtime(true); - $tables = tlObjectWithDB::getDBTables(array('tcversions','nodes_hierarchy')); - - $my = array(); - - $my['options'] = array('forPrinting' => 0, 'hideTestCases' => 0, - 'tc_action_enabled' => 1, - 'viewType' => 'testSpecTree'); - - - $my['filters'] = array('keywords' => null, - 'plaftorms' => null,'testplan' => null); - - $my['options'] = array_merge($my['options'], (array)$options); - $my['options']['showTestCaseID'] = config_get('treemenu_show_testcase_id'); - - $my['filters'] = array_merge($my['filters'], (array)$filters); - - - $treeMenu = new stdClass(); - $treeMenu->rootnode = null; - $treeMenu->menustring = ''; - - $resultsCfg = config_get('results'); - $glueChar = config_get('testcase_cfg')->glue_character; - $menustring = null; - - $tproject_mgr = new testproject($db); - $tree_manager = &$tproject_mgr->tree_manager; - - $hash_descr_id = $tree_manager->get_available_node_types(); - $hash_id_descr = array_flip($hash_descr_id); - $status_descr_code=$resultsCfg['status_code']; - $status_code_descr=$resultsCfg['code_status']; - - $tcase_prefix = $tproject_mgr->getTestCasePrefix($tproject_id) . $glueChar; - $test_spec = getTestSpecTree($tproject_id,$tproject_mgr,$filters); - - // Added root node for test specification -> testproject - $test_spec['name'] = $tproject_name; - $test_spec['id'] = $tproject_id; - $test_spec['node_type_id'] = $hash_descr_id['testproject']; - - $map_node_tccount=array(); - $tc2show = null; - - if($test_spec) { - if (isset($my['filters']['filter_custom_fields']) - && isset($test_spec['childNodes'])) { - $test_spec['childNodes'] = filter_by_cf_values($db, - $test_spec['childNodes'], - $my['filters']['filter_custom_fields'],$hash_descr_id); - } - - $pnFilters = array('keywords' => $my['filters']['filter_keywords'], - 'keywords_filter_type' => - $my['filters']['filter_keywords_filter_type'], - 'platforms' => $my['filters']['filter_platforms'], - ); - - $pnOptions = array('hideTestCases' => $my['options']['hideTestCases'], - 'ignoreInactiveTestCases' => - $my['options']['ignore_inactive_testcases'], - 'ignoreActiveTestCases' => - $my['options']['ignore_active_testcases']); - - // Important/CRITIC: - // prepareTestSpecNode() will make changes - // to $test_spec like filtering by test case keywords. - $testcase_counters = prepareTestSpecNode($db, $tproject_mgr,$tproject_id,$test_spec,$map_node_tccount,$pnFilters,$pnOptions); - - if( is_null($test_spec) ) { - $test_spec['name'] = $tproject_name; - $test_spec['id'] = $tproject_id; - $test_spec['node_type_id'] = $hash_descr_id['testproject']; - } - - foreach($testcase_counters as $key => $value) { - $test_spec[$key] = $testcase_counters[$key]; - } - - $tc2show = renderTreeNode(1,$test_spec,$hash_id_descr,$linkto,$tcase_prefix,$my['options']); - } - - $menustring =''; - $treeMenu->rootnode = new stdClass(); - $treeMenu->rootnode->name = $test_spec['text']; - $treeMenu->rootnode->id = $test_spec['id']; - $treeMenu->rootnode->leaf = isset($test_spec['leaf']) ? $test_spec['leaf'] : false; - $treeMenu->rootnode->text = $test_spec['text']; - $treeMenu->rootnode->position = $test_spec['position']; - $treeMenu->rootnode->href = $test_spec['href']; - - - - // 20090328 - franciscom - BUGID 2299 - // More details about problem found on 20090308 and fixed IN WRONG WAY - // TPROJECT - // |______ TSA - // |__ TC1 - // |__ TC2 - // | - // |______ TSB - // |______ TSC - // - // Define Keyword K1,K2 - // - // NO TEST CASE HAS KEYWORD ASSIGNED - // Filter by K1 - // Tree will show root that spins Forever - // menustring before str_ireplace : [null,null] - // menustring AFTER [null] - // - // Now fixed. - // - // Some minor fix to do - // Il would be important exclude Top Level Test suites. - // - // - // 20090308 - franciscom - // Changed because found problem on: - // Test Specification tree when applying Keyword filter using a keyword NOT PRESENT - // in test cases => Tree root shows loading icon and spin never stops. - // - // Attention: do not know if in other situation this will generate a different bug - // - // - // Change key ('childNodes') to the one required by Ext JS tree. - if(isset($test_spec['childNodes'])) - { - $menustring = str_ireplace('childNodes', 'children', json_encode($test_spec['childNodes'])); - } - - if(!is_null($menustring)) - { - // Remove null elements (Ext JS tree do not like it ). - // :null happens on -> "children":null,"text" that must become "children":[],"text" - // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); - // $menustring = str_ireplace(array(':null',',null','null,','null'),array(':[]','','',''), $menustring); - // $menustring = preg_replace('/,\s*"[^"]+":null|"[^"]+":null,?/', '', $menustring); - $menustring = str_ireplace(array(':' . REMOVEME, ',"' . REMOVEME .'"', '"' . REMOVEME . '",'), - array(':[]','',''), $menustring); - } - $treeMenu->menustring = $menustring; - - $tc2show = !is_null($tc2show) ? explode(",",trim($tc2show,",")) : null; - return array('menu' => $treeMenu, 'leaves' => $tc2show, 'tree' => $test_spec); -} - - -/** - * - * importance & status (workflow status) can be array - */ -function getTestSpecTree($tprojectID,&$tprojectMgr,&$fObj) { - - $flt = array(); - $flt['exclude_branches'] = - isset($fObj['filter_toplevel_testsuite']) && - is_array($fObj['filter_toplevel_testsuite']) ? - $fObj['filter_toplevel_testsuite'] : null; - - $flt['testcase_name'] = null; - $flt['testcase_id'] = null; - $flt['execution_type'] = null; - $flt['importance'] = null; - $flt['status'] = null; - $flt['keywords'] = null; - $flt['platforms'] = null; - - if( isset($fObj['filter_testcase_name']) && !is_null($fObj['filter_testcase_name']) ) { - if( ($dummy = trim($fObj['filter_testcase_name'])) != '' ) { - $flt['testcase_name'] = $dummy; - } - } - - if( isset($fObj['filter_tc_id']) && !is_null($fObj['filter_tc_id']) ) { - $flt['testcase_id'] = intval($fObj['filter_tc_id']); - } - - if( isset($fObj['filter_execution_type']) && !is_null($fObj['filter_execution_type']) ) { - $flt['execution_type'] = intval($fObj['filter_execution_type']); - } - - if( isset($fObj['filter_importance']) && !is_null($fObj['filter_importance']) ) { - $xx = (array)$fObj['filter_importance']; - if($xx[0] >0) { - $flt['importance'] = $xx; - } - } - - if( isset($fObj['filter_workflow_status']) && !is_null($fObj['filter_workflow_status']) ) { - $xx = (array)$fObj['filter_workflow_status']; - if($xx[0]>0) { - $flt['status'] = $xx; - } - } - - $piece = 'platforms'; - $full = 'filter_' . $piece; - if( isset($fObj[$full]) - && !is_null($fObj[$full]) ) { - $xx = (array)$fObj[$full]; - if($xx[0]>0) { - $flt[$piece] = $xx; - } - } - - $opt = array('recursive' => true,'exclude_testcases' => false); - $items = $tprojectMgr->getTestSpec($tprojectID,$flt,$opt); - - return $items; -} - - -/** - * - * - */ -function prepareTestSpecNode(&$db, &$tprojectMgr,$tprojectID,&$node,&$map_node_tccount,$filters=null,$options=null) { - - static $status_descr_list; - static $debugMsg; - static $tables; - static $my; - static $filtersApplied; - static $decoding_info; - static $tcFilterByKeywords; - static $doFilterOn; - static $tcFilterByPlatforms; - - if (!$tables) { - $debugMsg = 'Class: ' . __CLASS__ . ' - ' . 'Method: ' . __FUNCTION__ . ' - '; - $tables = tlObjectWithDB::getDBTables( - array('tcversions','nodes_hierarchy','testplan_tcversions')); - $decoding_info = array('node_id_descr' => - array_flip($tprojectMgr->tree_manager->get_available_node_types())); - $my = array(); - $my['options'] = array('hideTestCases' => 0); - $my['filters'] = array('keywords' => null, 'platforms' => null); - - $my['options'] = array_merge($my['options'], (array)$options); - $my['filters'] = array_merge($my['filters'], (array)$filters); - - $doFilterOn['keywords'] = !is_null($my['filters']['keywords']) - && (intval($my['filters']['keywords']) > 0); - - if ($doFilterOn['keywords']) { - $tcFilterByKeywords = $tprojectMgr->getTCLatestVersionFilteredByKeywords( - $tprojectID, - $my['filters']['keywords'], - $my['filters']['keywords_filter_type']); - - if( is_null($tcFilterByKeywords) ) { - // tree will be empty - $node = null; - $tcase_counters['testcase_count'] = 0; - return($tcase_counters); - } - } - - $doFilterOn['platforms'] = !is_null($my['filters']['platforms']) - && (intval($my['filters']['platforms']) > 0); - if ($doFilterOn['platforms']) { - $tcFilterByPlatforms = - $tprojectMgr->getTCLatestVersionFilteredByPlatforms( - $tprojectID,$my['filters']['platforms']); - - if( is_null($tcFilterByPlatforms) ) { - // tree will be empty - $node = null; - $tcase_counters['testcase_count'] = 0; - return($tcase_counters); - } - } - - - // Critic for logic that prune empty branches - // TICKET 4353: added active/inactive filter - $filtersApplied = $doFilterOn['keywords'] || - $my['options']['ignoreInactiveTestCases'] || - $my['options']['ignoreActiveTestCases'] || - $doFilterOn['platforms']; - } - - $tcase_counters['testcase_count'] = 0; - $node_type = isset($node['node_type_id']) ? $decoding_info['node_id_descr'][$node['node_type_id']] : null; - - if($node_type == 'testcase') { - $remove_node = false; - - if ($my['options']['ignoreInactiveTestCases']) { - $sql = " SELECT COUNT(TCV.id) AS count_active_versions " . - " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . - " WHERE NH.parent_id=" . $node['id'] . - " AND NH.id = TCV.id AND TCV.active=1"; - $result = $db->exec_query($sql); - $row = $db->fetch_array($result); - if ($row['count_active_versions'] == 0) { - $remove_node = true; - } - } - else if ($my['options']['ignoreActiveTestCases']) - { - $sql = " SELECT COUNT(TCV.id) AS count_active_versions " . - " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . - " WHERE NH.parent_id=" . $node['id'] . - " AND NH.id = TCV.id AND TCV.active=1"; - $result = $db->exec_query($sql); - $row = $db->fetch_array($result); - if ($row['count_active_versions'] != 0) { - $remove_node = true; - } - } - - if( $my['options']['hideTestCases'] || $remove_node || - ($doFilterOn['keywords'] && - !isset($tcFilterByKeywords[$node['id']])) || - ($doFilterOn['platforms'] && - !isset($tcFilterByPlatforms[$node['id']])) ) { - $node = REMOVEME; - } else { - // needed to avoid problems when using json_encode with EXTJS - unset($node['childNodes']); - $node['leaf']=true; - $tcase_counters['testcase_count'] = 1; - } - } // if($node_type == 'testcase') - - - // ================================================================ - if( !is_null($node) && isset($node['childNodes']) && is_array($node['childNodes']) ) { - - // node has to be a Test Suite ? - $childNodes = &$node['childNodes']; - $childNodesQty = count($childNodes); - - //$pos2unset = array(); - for($idx = 0;$idx < $childNodesQty ;$idx++) { - $current = &$childNodes[$idx]; - // I use set an element to null to filter out leaf menu items - if(is_null($current) || $current== REMOVEME) { - $childNodes[$idx] = REMOVEME; - continue; - } - - $counters_map = prepareTestSpecNode($db, $tprojectMgr,$tprojectID,$current,$map_node_tccount); - - // 20120831 - - // to be analized carefully, because this can be solution - // to null issue with json and ext-js - if( is_null($current) ) { - $childNodes[$idx] = REMOVEME; - } - - $tcase_counters['testcase_count'] += $counters_map['testcase_count']; - } - $node['testcase_count'] = $tcase_counters['testcase_count']; - - if (isset($node['id'])) { - $map_node_tccount[$node['id']] = array('testcount' => $node['testcase_count'], - 'name' => $node['name']); - } - - // node must be destroyed if empty had we have using filtering conditions - if( $filtersApplied && !$tcase_counters['testcase_count'] && ($node_type != 'testproject')) { - $node = null; - } - } else if ($node_type == 'testsuite') { - // does this means is an empty test suite ??? - franciscom 20080328 - $map_node_tccount[$node['id']] = array( 'testcount' => 0,'name' => $node['name']); - - // If is an EMPTY Test suite and we have added filtering conditions, - // We will destroy it. - if( $filtersApplied ) { - $node = null; - } - } - - return $tcase_counters; -} - -/** - * - * - */ -function helper_filter_cleanup(&$itemSet,$hits) -{ - $key2remove = null; - foreach($itemSet as $tcase_id => $dummy) - { - if( !isset($hits[$tcase_id]) ) - { - $key2remove[]=$tcase_id; - } - } - if( !is_null($key2remove) ) - { - foreach($key2remove as $key) - { - unset($itemSet[$key]); - } - } -} + viewType='testSpecTree' + * + * planAddTCNavigator.php => viewType=testSpecTreeForTestPlan + * + * --> tlTestCaseFilterControl->build_tree_menu() - WHEN FILTER ADDED + */ +function generateTestSpecTree(&$db, $tproject_id, $tproject_name, $linkto, + $filters = null, $options = null) +{ + $chronos[] = microtime(true); + + $my = array(); + $my['options'] = array( + 'forPrinting' => 0, + 'hideTestCases' => 0, + 'tc_action_enabled' => 1, + 'viewType' => 'testSpecTree', + 'ignore_inactive_testcases' => null, + 'ignore_active_testcases' => null + ); + + // testplan => + // only used if opetions['viewType'] == 'testSpecTreeForTestPlan' + // + // 20120205 - franciscom - hmm seems this code is INCOMPLETE + // may be we can remove ? + + // keys -> filter_* come from tlTestCaseFilterControl.class.php + $my['filters'] = [ + 'keywords' => null, + 'executionType' => null, + 'importance' => null, + 'testplan' => null, + 'filter_tc_id' => null, + 'filter_platforms' => null + ]; + + $my['options'] = array_merge($my['options'], (array) $options); + $my['options']['showTestCaseID'] = config_get('treemenu_show_testcase_id'); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + + // CRITIC: call with immediate return!!! + if ($my['options']['viewType'] == 'testSpecTree') { + + // Special processing for keywords + if ($filters['filter_keywords'] != null && + count($filters['filter_keywords']) == 1 && + $filters['filter_keywords'][0] == 0) { + // Get all available keywords on test project and apply these set + // will be affected by mode ? + // TODOD + $tproject_mgr = new testproject($db); + $usedKeywordsByKeyID = $tproject_mgr->getUsedKeywordsMap( + $tproject_id); + $filters['filter_keywords'] = array_keys($usedKeywordsByKeyID); + } + + return generateTestSpecTreeNew($db, $tproject_id, $tproject_name, + $linkto, $filters, $options); + } + + // OK - Go ahead here we have other type of features + $treeMenu = new stdClass(); + $treeMenu->rootnode = null; + $treeMenu->menustring = ''; + + $glueChar = config_get('testcase_cfg')->glue_character; + $menustring = null; + + $tproject_mgr = new testproject($db); + $tree_manager = &$tproject_mgr->tree_manager; + + $hash_descr_id = $tree_manager->get_available_node_types(); + $hash_id_descr = array_flip($hash_descr_id); + + // IMPORTANT NOTICE + // $filters['filter_toplevel_testsuite'] is managed in REVERSE form + // it contains NOT WHAT user wants, but all that we need to exclude + // in order provide what user WANTS. + // This is right way to go. + // $exclude_branches = isset($filters['filter_toplevel_testsuite']) && is_array($filters['filter_toplevel_testsuite']) ? $filters['filter_toplevel_testsuite'] : null; + + $tcase_prefix = $tproject_mgr->getTestCasePrefix($tproject_id) . $glueChar; + + $test_spec = getTestSpecTree($tproject_id, $tproject_mgr, $filters); + + // where the Keyword filter will be applied? + + // Added root node for test specification -> testproject + $test_spec['name'] = $tproject_name; + $test_spec['id'] = $tproject_id; + $test_spec['node_type_id'] = $hash_descr_id['testproject']; + + $map_node_tccount = array(); + $tplan_tcs = null; + $tc2show = null; + + // MORE FILTERS + if ($test_spec) { + $attr_map['keywords'] = null; // means no filter + if (! is_null($my['filters']['filter_keywords'])) { + + // if + $attr_map['keywords'] = $tproject_mgr->getKeywordsLatestTCV( + $tproject_id, $my['filters']['filter_keywords'], + $my['filters']['filter_keywords_filter_type']); + + if (is_null($attr_map['keywords'])) { + // means that tree will be EMPTY + $attr_map['keywords'] = array(); + } + } + + $attr_map['platforms'] = null; // means no filter + if (! is_null($my['filters']['filter_platforms'])) { + $attr_map['platforms'] = $tproject_mgr->getPlatformsLatestTCV( + $tproject_id, $my['filters']['filter_platforms']); + + if (is_null($attr_map['platforms'])) { + // means that tree will be EMPTY + $attr_map['platforms'] = array(); + } + } + + // Important: prepareNode() will make changes to + // $test_spec like filtering by test case + // keywords using $attr_map['keywords']; + $pnFilters = null; + $keys2init = array( + 'filter_testcase_name', + 'filter_execution_type', + 'filter_priority', + 'filter_tc_id' + ); + foreach ($keys2init as $keyname) { + $pnFilters[$keyname] = isset($my['filters'][$keyname]) ? $my['filters'][$keyname] : null; + } + + $pnFilters['setting_testplan'] = $my['filters']['setting_testplan']; + if (isset($my['filters']['filter_custom_fields']) && + isset($test_spec['childNodes'])) { + $test_spec['childNodes'] = filter_by_cf_values($db, + $test_spec['childNodes'], $my['filters']['filter_custom_fields'], + $hash_descr_id); + } + + // TICKET 4496: added inactive testcase filter + $pnOptions = array( + 'hideTestCases' => $my['options']['hideTestCases'], + 'viewType' => $my['options']['viewType'], + 'ignoreInactiveTestCases' => $my['options']['ignore_inactive_testcases'], + 'ignoreActiveTestCases' => $my['options']['ignore_active_testcases'] + ); + + $testcase_counters = prepareNode($db, $test_spec, $map_node_tccount, + $attr_map, $tplan_tcs, $pnFilters, $pnOptions); + + foreach ($testcase_counters as $key => $value) { + $test_spec[$key] = $testcase_counters[$key]; + } + + $tc2show = renderTreeNode(1, $test_spec, $hash_id_descr, $linkto, + $tcase_prefix, $my['options']); + } + + $menustring = ''; + $treeMenu->rootnode = new stdClass(); + $treeMenu->rootnode->name = $test_spec['text']; + $treeMenu->rootnode->id = $test_spec['id']; + $treeMenu->rootnode->leaf = isset($test_spec['leaf']) ? $test_spec['leaf'] : false; + $treeMenu->rootnode->text = $test_spec['text']; + $treeMenu->rootnode->position = $test_spec['position']; + $treeMenu->rootnode->href = $test_spec['href']; + + // 20090328 - franciscom - BUGID 2299 + // More details about problem found on 20090308 and fixed IN WRONG WAY + // TPROJECT + // |______ TSA + // |__ TC1 + // |__ TC2 + // | + // |______ TSB + // |______ TSC + // + // Define Keyword K1,K2 + // + // NO TEST CASE HAS KEYWORD ASSIGNED + // Filter by K1 + // Tree will show root that spins Forever + // menustring before str_ireplace : [null,null] + // menustring AFTER [null] + // + // Now fixed. + // + // Some minor fix to do + // Il would be important exclude Top Level Test suites. + // + // + // 20090308 - franciscom + // Changed because found problem on: + // Test Specification tree when applying Keyword filter using a keyword NOT PRESENT + // in test cases => Tree root shows loading icon and spin never stops. + // + // Attention: do not know if in other situation this will generate a different bug + // + // Change key ('childNodes') to the one required by Ext JS tree. + if (isset($test_spec['childNodes'])) { + $menustring = str_ireplace('childNodes', 'children', + json_encode($test_spec['childNodes'])); + } + + if (! is_null($menustring)) { + // Remove null elements (Ext JS tree do not like it ). + // :null happens on -> "children":null,"text" that must become "children":[],"text" + // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); + // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); + $menustring = str_ireplace( + array( + ':' . REMOVEME, + ',"' . REMOVEME . '"', + '"' . REMOVEME . '",', + '"' . REMOVEME . '"' + ), array( + ':[]', + '', + '', + '' + ), $menustring); + } + $treeMenu->menustring = $menustring; + + $tc2show = ! is_null($tc2show) ? explode(",", trim($tc2show, ",")) : null; + return array( + 'menu' => $treeMenu, + 'leaves' => $tc2show, + 'tree' => $test_spec + ); +} + +/** + * Prepares a Node to be displayed in a navigation tree. + * This function is used in the construction of: + * - Test project specification -> we want ALL test cases defined in test project. + * - Test execution -> we only want the test cases linked to a test plan. + * + * IMPORTANT: + * when analising a container node (Test Suite) if it is empty and we have requested + * some sort of filtering NODE WILL BE PRUNED. + * + * + * status: one of the possible execution status of a test case. + * + * + * tplan_tcases: map with testcase versions linked to test plan. + * due to the multiples uses of this function, null has several meanings + * + * When we want to build a Test Project specification tree, + * WE SET it to NULL, because we are not interested in a test plan. + * + * When we want to build a Test execution tree, we dont set it deliverately + * to null, but null can be the result of NO tcversion linked => EMPTY TEST PLAN + * + * + * status can be an array with multple values, to do OR search. + * added version info from test cases in return data structure. + * ignore_inactive_testcases: useful when building a Test Project Specification tree + * to be used in the add/link test case to Test Plan. + * + * attr_map['keywords']: Test Case Keyword map: + * null => no filter + * empty map => filter out ALL test case ALWAYS + * initialized map => filter out test case ONLY if NOT present in map. + * + * attr_map['platforms']: Test Case platforms map: + * null => no filter + * empty map => filter out ALL test case ALWAYS + * initialized map => filter out test case ONLY if NOT present in map. + * + * added argument: + * $map_node_tccount + * key => node_id + * values => node test case count + * node name (useful only for debug purpouses + * + * IMPORTANT: this new argument is not useful for tree rendering + * but to avoid duplicating logic to get test case count + * + * + * return: map with keys: + * 'total_count' + * 'passed' + * 'failed' + * 'blocked' + * 'not run' + * + * @internal revisions + */ +function prepareNode(&$db, &$node, &$map_node_tccount, $attr_map = null, + &$tplan_tcases = null, $filters = null, $options = null) +{ + static $status_descr_list; + static $debugMsg; + static $tables; + static $my; + static $enabledFiltersOn; + static $activeVersionClause; + static $filterOnTCVersionAttribute; + static $filtersApplied; + static $users2filter; + static $results2filter; + static $testPlanIsNotEmpty; + static $nodesTypeCode; + static $nodesCodeType; + + $tpNode = null; + if (! $tables) { + + $debugMsg = 'Class: ' . __CLASS__ . ' - ' . 'Method: ' . __FUNCTION__ . + ' - '; + $tables = tlObjectWithDB::getDBTables( + array( + 'tcversions', + 'nodes_hierarchy', + 'node_types', + 'testplan_tcversions' + )); + + $sql = " SELECT * FROM {$tables['node_types']} "; + $nodesTypeCode = $db->fetchColumnsIntoMap($sql, 'description', 'id'); + $nodesCodeType = array_flip($nodesTypeCode); + + $resultsCfg = config_get('results'); + $status_descr_list = array_keys($resultsCfg['status_code']); + $status_descr_list[] = 'testcase_count'; + + $my = array(); + $my['options'] = array( + 'hideTestCases' => 0, + 'showTestCaseID' => 1, + 'viewType' => 'testSpecTree', + 'getExternalTestCaseID' => 1, + 'ignoreInactiveTestCases' => 0, + 'ignoreActiveTestCases' => 0, + 'setAssignedTo' => false + ); + + // added importance here because of "undefined" error in event log + $my['filters'] = array( + 'status' => null, + 'assignedTo' => null, + 'importance' => null, + 'executionType' => null, + 'filter_tc_id' => null, + 'filter_platforms' => null + ); + + $my['options'] = array_merge($my['options'], (array) $options); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $enabledFiltersOn['importance'] = isset( + $my['filters']['filter_priority']); + $enabledFiltersOn['testcase_id'] = isset($my['filters']['filter_tc_id']); + + $enabledFiltersOn['testcase_name'] = isset( + $my['filters']['filter_testcase_name']); + $enabledFiltersOn['executionType'] = isset( + $my['filters']['filter_execution_type']); + $enabledFiltersOn['custom_fields'] = isset( + $my['filters']['filter_custom_fields']); + + $enabledFiltersOn['keywords'] = (null != $attr_map && + isset($attr_map['keywords']) && null != $attr_map['keywords'] && + count($attr_map['keywords']) > 0); + + $enabledFiltersOn['platforms'] = (null != $attr_map && + isset($attr_map['platforms']) && null != $attr_map['platforms'] && + count($attr_map['platforms']) > 0); + + $filterOnTCVersionAttribute = $enabledFiltersOn['executionType'] || + $enabledFiltersOn['importance']; + + $filtersApplied = false; + foreach ($enabledFiltersOn as $filterValue) { + $filtersApplied = $filtersApplied || $filterValue; + } + + $activeVersionClause = $filterOnTCVersionAttribute ? " AND TCV.active=1 " : ''; + + $users2filter = isset($my['filters']['filter_assigned_user']) ? $my['filters']['filter_assigned_user'] : null; + + $results2filter = isset($my['filters']['filter_result_result']) ? $my['filters']['filter_result_result'] : null; + + $testPlanIsNotEmpty = (! empty($tplan_tcases)); + } + + $tcase_counters = array_fill_keys($status_descr_list, 0); + $node_type = isset($node['node_type_id']) ? $nodesCodeType[$node['node_type_id']] : null; + + if ($node_type == 'testcase') { + // ABSOLUTELY First implicit filter to be applied when test plan is not empty. + // is our test case present on Test Spec linked to Test Plan ? + + if ($testPlanIsNotEmpty && ! isset($tplan_tcases[$node['id']])) { + $node = null; + } elseif (($enabledFiltersOn['keywords'] && + ! isset($attr_map['keywords'][$node['id']])) || + ($enabledFiltersOn['platforms'] && + ! isset($attr_map['platforms'][$node['id']])) || + ($enabledFiltersOn['testcase_name'] && + stripos($node['name'], $my['filters']['filter_testcase_name']) === + false) || + ($enabledFiltersOn['testcase_id'] && + ($node['id'] != $my['filters']['filter_tc_id']))) { + unset($tplan_tcases[$node['id']]); + $node = null; // OK - 20150129 + } else { + if ($my['options']['viewType'] == 'executionTree') { + $tpNode = isset($tplan_tcases[$node['id']]) ? $tplan_tcases[$node['id']] : null; + if (! ($delete_node = is_null($tpNode))) { + $delete_node = ! is_null($results2filter) && + ! isset($results2filter[$tpNode['exec_status']]); + + if (! $delete_node && ! is_null($users2filter)) { + $somebody_wanted_but_nobody_there = isset( + $users2filter[TL_USER_SOMEBODY]) && + ! is_numeric($tpNode['user_id']); + + $unassigned_wanted_but_someone_assigned = isset( + $users2filter[TL_USER_NOBODY]) && + ! is_null($tpNode['user_id']); + + $wrong_user = ! isset($users2filter[TL_USER_NOBODY]) && + ! isset($users2filter[TL_USER_SOMEBODY]) && + ! isset($users2filter[$tpNode['user_id']]); + + $delete_node = $unassigned_wanted_but_someone_assigned || + $wrong_user || $somebody_wanted_but_nobody_there; + } + } + + if ($delete_node) { + unset($tplan_tcases[$node['id']]); + $node = null; + } else { + $externalID = ''; + $node['tcversion_id'] = $tpNode['tcversion_id']; + $node['version'] = $tpNode['version']; + if ($my['options']['setAssignedTo']) { + $node['assigned_to'] = $tplan_tcases[$node['id']]['assigned_to']; + } + + if ($my['options']['getExternalTestCaseID']) { + if (! isset($tpNode['external_id'])) { + $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . + " SELECT TCV.tc_external_id AS external_id " . + " FROM {$tables['tcversions']} TCV " . + " WHERE TCV.id=" . $node['tcversion_id']; + + $result = $db->exec_query($sql); + $myrow = $db->fetch_array($result); + $externalID = $myrow['external_id']; + } else { + $externalID = $tpNode['external_id']; + } + } + $node['external_id'] = $externalID; + } + } + + if ($node != REMOVEME && $my['options']['ignoreInactiveTestCases']) { + // there are active tcversions for this node ??? + // I'm doing this instead of creating a test case manager object, because + // I think is better for performance. + // + // 20070106 - franciscom + // Postgres Problems + // Problem 1 - SQL Syntax + // While testing with postgres + // SELECT count(TCV.id) NUM_ACTIVE_VERSIONS -> Error + // + // At least for what I remember using AS to create COLUMN ALIAS IS REQUIRED and Standard + // while AS is NOT REQUIRED (and with some DBMS causes errors) when you want to give a + // TABLE ALIAS + // + // Problem 2 - alias case + // At least in my installation the aliases column name is returned lower case, then + // PHP fails when: + // if($myrow['NUM_ACTIVE_VERSIONS'] == 0) + $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . + " SELECT count(TCV.id) AS num_active_versions " . + " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . + " WHERE NH.parent_id=" . $node['id'] . + " AND NH.id = TCV.id AND TCV.active=1"; + + $result = $db->exec_query($sql); + $myrow = $db->fetch_array($result); + if ($myrow['num_active_versions'] == 0) { + $node = null; + } + } + + // TICKET 4496: added inactive testcase filter + if ($node !== REMOVEME && $my['options']['ignoreActiveTestCases']) { + $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . + " SELECT count(TCV.id) AS num_active_versions " . + " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . + " WHERE NH.parent_id=" . $node['id'] . + " AND NH.id = TCV.id AND TCV.active=1"; + + $result = $db->exec_query($sql); + $myrow = $db->fetch_array($result); + if ($myrow['num_active_versions'] != 0) { + $node = null; + } + } + } + + if (! is_null($node) && + ($my['options']['viewType'] == 'testSpecTree' || + $my['options']['viewType'] == 'testSpecTreeForTestPlan')) { + $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . + " SELECT COALESCE(MAX(TCV.id),0) AS targetid, TCV.tc_external_id AS external_id" . + " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . + " WHERE NH.id = TCV.id {$activeVersionClause} AND NH.parent_id={$node['id']} " . + " GROUP BY TCV.tc_external_id "; + + $rs = $db->get_recordset($sql); + if (is_null($rs)) { + $node = null; // OK 20150129 + } else { + $node['external_id'] = $rs[0]['external_id']; + $target_id = $rs[0]['targetid']; + + if ($filterOnTCVersionAttribute) { + switch ($my['options']['viewType']) { + case 'testSpecTreeForTestPlan': + // Try to get info from linked tcversions + // Platform is not needed + $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . + " SELECT DISTINCT TPTCV.tcversion_id AS targetid " . + " FROM {$tables['tcversions']} TCV " . + " JOIN {$tables['nodes_hierarchy']} NH " . + " ON NH.id = TCV.id {$activeVersionClause} " . + " AND NH.parent_id={$node['id']} " . + " JOIN {$tables['testplan_tcversions']} TPTCV " . + " ON TPTCV.tcversion_id = TCV.id " . + " AND TPTCV.testplan_id = " . + " {$my['filters']['setting_testplan']}"; + $rs = $db->get_recordset($sql); + $target_id = ! is_null($rs) ? $rs[0]['targetid'] : $target_id; + break; + } + + $sql = " /* $debugMsg - line:" . __LINE__ . " */ " . + " SELECT TCV.execution_type " . + " FROM {$tables['tcversions']} TCV " . + " WHERE TCV.id = {$target_id} "; + + if ($enabledFiltersOn['executionType']) { + $sql .= " AND TCV.execution_type = " . + " {$my['filters']['filter_execution_type']} "; + } + + if ($enabledFiltersOn['importance']) { + $sql .= " AND TCV.importance = " . + " {$my['filters']['filter_priority']} "; + } + + $rs = $db->fetchRowsIntoMap($sql, 'execution_type'); + if (is_null($rs)) { + $node = null; // OK - 20150129 + } + } + } + + // if( !is_null($node) ) + if (! is_null($node) && $node != REMOVEME) { + // needed to avoid problems when using json_encode with EXTJS + unset($node['childNodes']); + $node['leaf'] = true; + } + } + + foreach ($tcase_counters as $key => $value) { + $tcase_counters[$key] = 0; + } + + if (isset($decoding_info['status_code_descr'][$tpNode['exec_status']])) { + $tc_status_descr = $decoding_info['status_code_descr'][$tpNode['exec_status']]; + } else { + $tc_status_descr = "not_run"; + } + + $init_value = $node ? 1 : 0; + $tcase_counters[$tc_status_descr] = $init_value; + $tcase_counters['testcase_count'] = $init_value; + if ($my['options']['hideTestCases']) { + $node = REMOVEME; + } + } + + if (isset($node['childNodes']) && is_array($node['childNodes'])) { + // node has to be a Test Suite ? + $childNodes = &$node['childNodes']; + $childNodesQty = count($childNodes); + + for ($idx = 0; $idx < $childNodesQty; $idx ++) { + $current = &$childNodes[$idx]; + // I use set an element to null to filter out leaf menu items + if (is_null($current) || $current == REMOVEME) { + $childNodes[$idx] = REMOVEME; + continue; + } + $counters_map = prepareNode($db, $current, $map_node_tccount, + $attr_map, $tplan_tcases, $my['filters'], $my['options']); + foreach ($counters_map as $key => $value) { + $tcase_counters[$key] += $counters_map[$key]; + } + } + foreach ($tcase_counters as $key => $value) { + $node[$key] = $tcase_counters[$key]; + } + + if (isset($node['id'])) { + $map_node_tccount[$node['id']] = array( + 'testcount' => $node['testcase_count'], + 'name' => $node['name'] + ); + } + + // node must be destroyed if empty had we have using filtering conditions + if (($filtersApplied || ! is_null($tplan_tcases)) && + ! $tcase_counters['testcase_count'] && ($node_type != 'testproject')) { + $node = REMOVEME; // OK 20150129 + } + } elseif ($node_type == 'testsuite') { + // does this means is an empty test suite ??? - franciscom 20080328 + $map_node_tccount[$node['id']] = array( + 'testcount' => 0, + 'name' => $node['name'] + ); + + // If is an EMPTY Test suite and we have added filtering conditions, + // We will destroy it. + if ($filtersApplied || ! is_null($tplan_tcases)) { + $node = REMOVEME; // OK - 20150129 + } + } + + return $tcase_counters; +} + +/** + * Create the string representation suitable to create a graphic visualization + * of a node, for the type of menu selected. + * + * Used when LAZY Rendering can not be used. + * + * @internal revisions + */ +function renderTreeNode($level, &$node, $hash_id_descr, $linkto, $testCasePrefix, + $opt) +{ + static $f2call; + static $forbidden_parents; + + $testCasesIDList = ''; + + // Choice for PERFORMANCE: + // Some pieces of code on TL < 1.9.4 has been wrapped in a function, but when working + // with BIG amount of testcases (> 5000) impact on performance was high. + if (! $f2call) { + $f2call['testproject'] = 'EP'; + $f2call['testsuite'] = 'ETS'; + if (isset($opt['forPrinting']) && $opt['forPrinting']) { + $f2call['testproject'] = 'TPROJECT_PTP'; + $f2call['testsuite'] = 'TPROJECT_PTS'; + } + + $f2call['testcase'] = $opt['tc_action_enabled'] ? 'ET' : 'void'; + + // Design allow JUST ONE forbidden, probably other measures + // like a leaf (test case) can not have other leaf as parent + // are already in place (need to check code better) + // + // IMPORTANT NOTICE: + // @20130407 + // this extended attribute need to be setted also on + // logic used when lazy tree build is used + // (gettprojectnodes.php) + // In addition tree config option useBeforeMoveNode must be set to true + $forbidden_parents['testproject'] = 'none'; + $forbidden_parents['testcase'] = 'testproject'; + $forbidden_parents['testsuite'] = 'testcase'; + } + + if (! isset($node['name'])) { + return $testCasesIDList; + } + + // custom Property that will be accessed by EXT-JS using node.attributes + // strip potential newlines and other unwanted chars from strings + // Mainly for stripping out newlines, carriage returns, and quotes that were + // causing problems in javascript using jtree + $node['testlink_node_name'] = str_replace(array( + "\n", + "\r" + ), array( + "", + "" + ), $node['name']); + $node['testlink_node_name'] = htmlspecialchars($node['testlink_node_name'], + ENT_QUOTES); + + $node['testlink_node_type'] = $hash_id_descr[$node['node_type_id']]; + $node['forbidden_parent'] = $forbidden_parents[$node['testlink_node_type']]; + + $testcase_count = isset($node['testcase_count']) ? $node['testcase_count'] : 0; + $pfn = $f2call[$node['testlink_node_type']]; + + switch ($node['testlink_node_type']) { + case 'testproject': + case 'testsuite': + $node['text'] = $node['testlink_node_name'] . " (" . $testcase_count . + ")"; + if (isset($opt['nodeHelpText'][$node['testlink_node_type']])) { + $node['text'] = '' . + $node['text'] . ''; + } + break; + + case 'testcase': + $node['text'] = ""; + if ($opt['showTestCaseID']) { + $node['text'] .= "{$testCasePrefix}{$node['external_id']}:"; + } + $node['text'] .= $node['testlink_node_name']; + $testCasesIDList .= $node['id'] . ','; + break; + } // switch + + $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; + $node['href'] = "javascript:{$pfn}({$node['id']})"; + + if (isset($node['childNodes']) && $node['childNodes']) { + // need to work always original object + // in order to change it's values using reference . + // Can not assign anymore to intermediate variables. + // + $nChildren = count($node['childNodes']); + for ($idx = 0; $idx < $nChildren; $idx ++) { + // asimon - replaced is_null by !isset because of warnings in event log + if (! isset($node['childNodes'][$idx])) { + continue; + } + $testCasesIDList .= renderTreeNode($level + 1, + $node['childNodes'][$idx], $hash_id_descr, $linkto, + $testCasePrefix, $opt); + } + } + + return $testCasesIDList; +} + +/** + * + * @param integer $level + * @param + * array &$node reference to recursive map + * @param + * array &$tcases_node reference to map that contains info about testcase exec status + * when node is of testcase type. + * @param array $hash_id_descr + * @param string $linkto + * @param string $testCasePrefix + * @param array $opt + * @used-by execTreeMenu.inc.php + * + */ +function renderExecTreeNode($level, &$node, &$tcase_node, $hash_id_descr, + $linkto, $testCasePrefix, $opt) +{ + static $resultsCfg; + static $l18n; + static $pf; + static $doColouringOn; + static $cssClasses; + + $node_type = $hash_id_descr[$node['node_type_id']]; + + if (! $resultsCfg) { + $doColouringOn['testcase'] = 1; + $doColouringOn['counters'] = 1; + if (! is_null($opt['useColors'])) { + $doColouringOn['testcase'] = $opt['useColors']->testcases; + $doColouringOn['counters'] = $opt['useColors']->counters; + } + + $resultsCfg = config_get('results'); + $status_descr_code = $resultsCfg['status_code']; + + foreach ($resultsCfg['status_label'] as $key => $value) { + $l18n[$status_descr_code[$key]] = lang_get($value); + + // here we use ONLY key + $cssClasses[$status_descr_code[$key]] = $doColouringOn['testcase'] ? ('class="light_' . + $key . '"') : ''; + } + + // Very BAD CHOICE => SIDE EFFECT + $pf['testsuite'] = $opt['hideTestCases'] ? 'TPLAN_PTS' : ($opt['showTestSuiteContents'] ? 'STS' : null); + $pf['testproject'] = $opt['hideTestCases'] ? 'TPLAN_PTP' : 'SP'; + + if (isset($opt['actionJS'])) { + $k2l = array( + 'testproject', + 'testsuite', + 'testcase', + 'testplan', + 'default' + ); + foreach ($k2l as $kiki) { + if (isset($opt['actionJS'][$kiki])) { + $pf[$kiki] = null; + if ('' != $opt['actionJS'][$kiki]) { + $pf[$kiki] = $opt['actionJS'][$kiki]; + } + } + } + } + + // manage defaults + $opt['showTestCaseExecStatus'] = isset($opt['showTestCaseExecStatus']) ? $opt['showTestCaseExecStatus'] : true; + $opt['nodeHelpText'] = isset($opt['nodeHelpText']) ? $opt['nodeHelpText'] : array(); + } + + $name = htmlspecialchars($node['name'], ENT_QUOTES); + + // custom Property that will be accessed by EXT-JS using node.attributes + $node['testlink_node_name'] = $name; + $node['testlink_node_type'] = $node_type; + + switch ($node_type) { + case 'testproject': + case 'testsuite': + $node['leaf'] = false; + + $testcase_count = isset($node['testcase_count']) ? $node['testcase_count'] : 0; + $node['text'] = $name . " (" . $testcase_count . ")"; + if ($opt['useCounters']) { + $node['text'] .= create_counters_info($node, + $doColouringOn['counters']); + } + + if (isset($opt['nodeHelpText'][$node_type])) { + $node['text'] = '' . $node['text'] . + ''; + } + + $pfn = ! is_null($pf[$node_type]) ? $pf[$node_type] . + "({$node['id']})" : null; + if ('testsuite' == $node_type && ($opt['alertOnTestSuiteTCQty'] > 0) && + $testcase_count > $opt['alertOnTestSuiteTCQty']) { + $jfn = config_get('jsAlertOnTestSuiteTCQty'); + $pfn = $jfn; + } + + break; + + case 'testcase': + $node['leaf'] = true; + $pfn = null; + if ($opt['tc_action_enabled']) { + $pfx = "ST"; + if (isset($pf[$node_type])) { + $pfx = "$pf[$node_type]"; + } + $pfn = $pfx . "({$node['id']},{$node['tcversion_id']})"; + } + + $node['text'] = "'; + } + + if ($opt['showTestCaseID']) { + // optimizable + $node['text'] .= "" . + htmlspecialchars($testCasePrefix . $node['external_id']) . + ":"; + } + $node['text'] .= "{$name}"; + break; + + case 'testplan': + $pfn = "ST({$node['id']})"; + if (isset($pf[$node_type])) { + $pfn = null; + if ('' != $pf[$node_type]) { + $pfn = $pf[$node_type] . "({$node['id']})"; + } + } + break; + + default: + $pfn = "ST({$node['id']})"; + if (isset($pf['default'])) { + $pfn = null; + if ('' != $pf['default']) { + $pfn = $pf['default'] . "({$node['id']})"; + } + } + break; + } + + $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; + $node['href'] = is_null($pfn) ? '' : "javascript:{$pfn}"; + + if (isset($tcase_node[$node['id']])) { + unset($tcase_node[$node['id']]); // dam it NO COMMENT! + } + + if (isset($node['childNodes']) && $node['childNodes']) { + // need to work always original object in order to change it's values using reference . + // Can not assign anymore to intermediate variables. + $nodes_qty = count($node['childNodes']); + for ($idx = 0; $idx < $nodes_qty; $idx ++) { + if (is_null($node['childNodes'][$idx]) || + $node['childNodes'][$idx] == REMOVEME) { + continue; + } + renderExecTreeNode($level + 1, $node['childNodes'][$idx], + $tcase_node, $hash_id_descr, $linkto, $testCasePrefix, $opt); + } + } +} + +/** + */ +function create_counters_info(&$node, $useColors) +{ + static $keys2display; + static $labelCache; + + if (! $labelCache) { + $resultsCfg = config_get('results'); + $status_label = $resultsCfg['status_label']; + + // I will add not_run if not exists + $keys2display = array( + 'not_run' => 'not_run' + ); + foreach ($resultsCfg['status_label_for_exec_ui'] as $key => $value) { + if ($key != 'not_run') { + $keys2display[$key] = $key; + } + $labelCache[$key] = lang_get($status_label[$key]); + } + } + + $add_html = ''; + foreach ($keys2display as $key) { + if (isset($node[$key])) { + $css_class = $useColors ? (" class=\"light_{$key}\" ") : ''; + $add_html .= "' . $node[$key] . ","; + } + } + + $add_html = "(" . rtrim($add_html, ",") . ")"; + return $add_html; +} + +/** + * Filter out the testcases that don't have the given value + * in their custom field(s) from the tree. + * Recursive function. + * + * @author Andreas Simon + * @since 1.9 + * + * @param + * resource &$db reference to DB handler object + * @param + * array &$tcase_tree reference to test case set/tree to filter + * @param + * array &$cf_hash reference to selected custom field information + * @param int $node_types + * IDs of node types + * + * @return array $tcase_tree filtered tree structure + * + * @internal revisions + */ +function filter_by_cf_values(&$db, &$tcase_tree, &$cf_hash, $node_types) +{ + static $tables = null; + static $debugMsg = null; + + $rows = null; + if (! $debugMsg) { + $tables = tlObject::getDBTables( + array( + 'cfield_design_values', + 'nodes_hierarchy', + 'tcversions' + )); + $debugMsg = 'Function: ' . __FUNCTION__; + } + + $node_deleted = false; + + // This code is in parts based on (NOT simply copy/pasted) + // some filter code used in testplan class. + // Implemented because we have a tree here, + // not simple one-dimensional array of testcases like in tplan class. + + foreach ($tcase_tree as $key => $node) { + // TICKET 5186: Filtering by the value of custom fields on test specification is not working + if ($node['node_type_id'] == $node_types['testsuite']) { + $delete_suite = false; + + if (isset($node['childNodes']) && is_array($node['childNodes'])) { + // node is a suite and has children, so recurse one level deeper + $tcase_tree[$key]['childNodes'] = filter_by_cf_values($db, + $tcase_tree[$key]['childNodes'], $cf_hash, $node_types); + + // now remove testsuite node if it is empty after coming back from recursion + if (! count($tcase_tree[$key]['childNodes'])) { + $delete_suite = true; + } + } else { + // nothing in here, suite was already empty + $delete_suite = true; + } + + if ($delete_suite) { + unset($tcase_tree[$key]); + $node_deleted = true; + } + } elseif ($node['node_type_id'] == $node_types['testcase']) { + // node is testcase, check if we need to delete it + $passed = false; + + // TICKET 5186: added "DISTINCT" to SQL clause, detailed explanation follows at the end of function + // Note: SQL statement has been adopted to filter by latest active tc version. + // That is a better solution for the explained problem than using the distinct keyword. + $latest_active_version_sql = " /* get latest active TC version ID */ " . + " SELECT MAX(TCVX.id) AS max_tcv_id, NHTCX.parent_id AS tc_id " . + " FROM {$tables['tcversions']} TCVX " . + " JOIN {$tables['nodes_hierarchy']} NHTCX " . + " ON NHTCX.id = TCVX.id AND TCVX.active = 1 " . + " WHERE NHTCX.parent_id = {$node['id']} " . + " GROUP BY NHTCX.parent_id, TCVX.tc_external_id "; + + $sql = " /* $debugMsg */ SELECT CFD.value " . + " FROM {$tables['cfield_design_values']} CFD, {$tables['nodes_hierarchy']} NH " . + " JOIN ( $latest_active_version_sql ) LAVSQL ON NH.id = LAVSQL.max_tcv_id " . + " WHERE CFD.node_id = NH.id "; + + // IMPORTANT DEV NOTES + // Query uses OR, but final processing makes that CF LOGIC work in AND MODE as expected + if (isset($cf_hash)) { + $countmain = 1; + $cf_sql = ''; + foreach ($cf_hash as $cf_id => $cf_value) { + if ($countmain != 1) { + $cf_sql .= " OR "; + } + + $safeID = intval($cf_id); + // single value or array? + if (is_array($cf_value)) { + $count = 1; + foreach ($cf_value as $value) { + if ($count > 1) { + $cf_sql .= " AND "; + } + $safeValue = $db->prepare_string($value); + $cf_sql .= "( CFD.value LIKE '%{$safeValue}%' AND CFD.field_id = {$safeID} )"; + $count ++; + } + } else { + $safeValue = $db->prepare_string($cf_value); + $cf_sql .= " ( CFD.value LIKE '%{$safeValue}%' AND CFD.field_id = {$safeID} ) "; + } + $countmain ++; + } + $sql .= " AND ({$cf_sql}) "; + } + + $rows = (array) $db->fetchColumnsIntoArray($sql, 'value'); + + // if there exist as many rows as custom fields to be filtered by + // the tc does meet the criteria + + /* + * NOTE by asimon: This assumption was wrong! If there are multiple versions of a TC, + * then the row number here can be larger than the number of custom fields with the correct value. + * + * Example: + * Custom field "color" has possible values "red", "blue", "green", default empty. + * Custom field "status" has possible values "draft", "ready", "needs review", "needs rework", default empty. + * TC Version 1: cfield "color" has value "red", cfield "status" has no value yet. + * TC Version 2: cfield "color" has value "red", cfield "status" has no value yet. + * TC Version 3: cfield "color" and value "red", cfield "status" has value "ready". + * TC Version 4: cfield "color" has value "red", cfield "status" has value "ready". + * + * Filter by color GREEN and status READY, then $rows looks like this: Array ( [0] => red, [1] => red ) + * => count($rows) returns 2, which matches the number of custom fields we want to filter by. + * So TC lands in the result set instead of being filtered out. + * That is wrong, because TC matches only one of the fields we were filtering by! + * + * Because of this I extended the SQL statement above with the DISTINCT keyword, + * so that each custom field only is contained ONCE in the result set. + */ + + $passed = (count($rows) == count($cf_hash)) ? true : false; + // now delete node if no match was found + if (! $passed) { + unset($tcase_tree[$key]); + $node_deleted = true; + } + } + } + + // 20100702 - asimon + // if we deleted a note, the numeric indexes of this array do have missing numbers, + // which causes problems in later loop constructs in other functions that assume numeric keys + // in these arrays without missing numbers in between - crashes JS tree! + // -> so I have to fix the array indexes here starting from 0 without missing a key + if ($node_deleted) { + $tcase_tree = array_values($tcase_tree); + } + + return $tcase_tree; +} + +/** + * + * @param + * object &$tplan_mgr reference to test plan manager object + * @param + * array &$tcase_set reference to test case set to filter + * @param integer $tplan_id + * ID of test plan + * @param array $filters + * filters to apply to test case set + * @return array new tcase_set + */ +function filterStatusSetAtLeastOneOfActiveBuilds(&$tplan_mgr, &$tcase_set, + $tplan_id, $filters) +{ + $safe_platform = intval($filters->setting_platform); + $buildSet = array_keys( + $tplan_mgr->get_builds($tplan_id, testplan::ACTIVE_BUILDS)); + if (! is_null($buildSet)) { + if ($safe_platform > 0) { + tLog( + basename(__FILE__) . __FUNCTION__ . + ':: $tplan_mgr->getHitsSameStatusPartialOnPlatform', 'DEBUG'); + $hits = $tplan_mgr->getHitsSameStatusPartialOnPlatform($tplan_id, + $safe_platform, (array) $filters->filter_result_result); + } else { + tLog( + basename(__FILE__) . __FUNCTION__ . + ':: $tplan_mgr->getHitsSameStatusPartialALOP', 'DEBUG'); + $hits = $tplan_mgr->getHitsSameStatusPartialALOP($tplan_id, + (array) $filters->filter_result_result); + } + + if (is_null($hits)) { + $tcase_set = array(); + } else { + helper_filter_cleanup($tcase_set, $hits); + } + } + + return $tcase_set; +} + +/** + * filterStatusSetAllActiveBuilds() + * + * returns: + * + * test cases that has AT LEAST ONE of requested status + * or combinations of requested status + * ON LAST EXECUTION ON ALL ACTIVE builds, for a PLATFORM + * + * For examples and more info read documentation regarding + * getHits*() methods on testplan class. + * + * + * @param + * object &$tplan_mgr reference to test plan manager object + * @param + * array &$tcase_set reference to test case set to filter + * WILL BE MODIFIED HERE + * + * @param integer $tplan_id + * ID of test plan + * @param array $filters + * filters to apply to test case set + * + * @return array new tcase_set + */ +function filterStatusSetAllActiveBuilds(&$tplan_mgr, &$tcase_set, $tplan_id, + $filters) +{ + $buildSet = array_keys( + $tplan_mgr->get_builds($tplan_id, testplan::ACTIVE_BUILDS)); + if (! is_null($buildSet)) { + + $safe_platform = intval($filters->setting_platform); + if ($safe_platform > 0) { + tLog( + basename(__FILE__) . __FUNCTION__ . + ':: $tplan_mgr->getHitsSameStatusFullOnPlatform', 'DEBUG'); + $hits = $tplan_mgr->getHitsSameStatusFullOnPlatform($tplan_id, + $safe_platform, (array) $filters->filter_result_result, + $buildSet); + } else { + tLog( + basename(__FILE__) . __FUNCTION__ . + ':: $tplan_mgr->getHitsSameStatusFullALOP', 'DEBUG'); + + $hits = $tplan_mgr->getHitsSameStatusFullALOP($tplan_id, + (array) $filters->filter_result_result, $buildSet); + } + + if (is_null($hits)) { + $tcase_set = array(); + } else { + helper_filter_cleanup($tcase_set, $hits); + unset($hits); + } + } + return $tcase_set; +} + +/** + * used by filter options: + * result on specific build + * result on current build + * + * + * @param + * object &$tplan_mgr reference to test plan manager object + * @param + * array &$tcase_set reference to test case set to filter + * @param integer $tplan_id + * ID of test plan + * @param array $filters + * filters to apply to test case set + * + * @return array new tcase_set + */ +function filter_by_status_for_build(&$tplan_mgr, &$tcase_set, $tplan_id, + $filters) +{ + $safe_platform = intval($filters->setting_platform); + $safe_build = intval($filters->filter_result_build); + if ($safe_platform > 0) { + tLog(__FUNCTION__ . ':: $tplan_mgr->getHitsStatusSetOnBuildPlatform', + 'DEBUG'); + $hits = $tplan_mgr->getHitsStatusSetOnBuildPlatform($tplan_id, + $safe_platform, $safe_build, (array) $filters->filter_result_result); + } else { + tLog(__FUNCTION__ . ':: $tplan_mgr->getHitsStatusSetOnBuildALOP', + 'DEBUG'); + $hits = $tplan_mgr->getHitsStatusSetOnBuildALOP($tplan_id, $safe_build, + (array) $filters->filter_result_result); + } + + if (is_null($hits)) { + $tcase_set = array(); + } else { + helper_filter_cleanup($tcase_set, $hits); + } + + return $tcase_set; +} + +/** + * filter testcases by the result of their latest execution + * + * CAN NOT BE USED FOR NOT RUN because Not run is not saved on DB + * + * @param + * object &$db reference to database handler + * @param + * object &$tplan_mgr reference to test plan manager object + * @param + * array &$tcase_set reference to test case set to filter + * @param integer $tplan_id + * ID of test plan + * @param array $filters + * filters to apply to test case set + * @return array new tcase_set + */ +function filter_by_status_for_latest_execution(&$tplan_mgr, &$tcase_set, + $tplan_id, $filters) +{ + $safe_tplan = intval($tplan_id); + $safe_platform = intval($filters->setting_platform); + + if ($safe_platform > 0) { + $hits = $tplan_mgr->getHitsStatusSetOnLatestExecOnPlatform($safe_tplan, + $safe_platform, (array) $filters->filter_result_result); + } else { + $hits = $tplan_mgr->getHitsStatusSetOnLatestExecALOP($safe_tplan, + (array) $filters->filter_result_result); + } + + if (is_null($hits)) { + $tcase_set = array(); + } else { + helper_filter_cleanup($tcase_set, $hits); + } + + return $tcase_set; +} + +/** + * + * @param + * object &$tplan_mgr reference to test plan manager object + * @param + * array &$tcase_set reference to test case set to filter + * @param integer $tplan_id + * ID of test plan + * @param array $filters + * filters to apply to test case set + * @return array new tcase_set + */ +function filter_not_run_for_any_build(&$tplan_mgr, &$tcase_set, $tplan_id, + $filters) +{ + $safe_platform = intval($filters->setting_platform); + if ($safe_platform > 0) { + $hits = $tplan_mgr->getHitsNotRunPartialOnPlatform($tplan_id, + intval($filters->setting_platform)); + } else { + $hits = $tplan_mgr->getHitsNotRunPartialALOP($tplan_id); + } + + if (is_null($hits)) { + $tcase_set = array(); + } else { + helper_filter_cleanup($tcase_set, $hits); + } + + return $tcase_set; +} + +/** + * generate array with Keywords for a filter + */ +function buildKeywordsFilter($keywordsId, &$guiObj) +{ + $keywordsFilter = null; + + if (! is_null($keywordsId)) { + $items = array_flip((array) $keywordsId); + if (! isset($items[0])) { + $keywordsFilter = new stdClass(); + $keywordsFilter->items = $keywordsId; + $keywordsFilter->type = isset($guiObj->keywordsFilterTypes) ? $guiObj->keywordsFilterTypes->selected : 'OR'; + } + } + + return $keywordsFilter; +} + +/** + * generate object with test case execution type for a filter + */ +function buildExecTypeFilter($execTypeSet) +{ + $itemsFilter = null; + + if (! is_null($execTypeSet)) { + $items = array_flip((array) $execTypeSet); + if (! isset($items[0])) { + $itemsFilter = new stdClass(); + $itemsFilter->items = $execTypeSet; + } + } + + return $itemsFilter; +} + +/** + * generate object with test case importance for a filter + */ +function buildImportanceFilter($importance) +{ + $itemsFilter = null; + + if (! is_null($importance)) { + $items = array_flip((array) $importance); + if (! isset($items[0])) { + $itemsFilter = new stdClass(); + $itemsFilter->items = $importance; + } + } + + return $itemsFilter; +} + +/** + * Generate the necessary data object for the filtered requirement specification tree. + * + * @author Andreas Simon + * @param Database $db + * reference to database handler object + * @param testproject $testproject_mgr + * reference to testproject manager object + * @param int $testproject_id + * ID of the project for which the tree shall be generated + * @param string $testproject_name + * Name of the test project + * @param array $filters + * Filter settings which shall be applied to the tree, possible values are: + * 'filter_doc_id', + * 'filter_title', + * 'filter_status', + * 'filter_type', + * 'filter_spec_type', + * 'filter_coverage', + * 'filter_relation', + * 'filter_tc_id', + * 'filter_custom_fields' + * @param array $options + * Further options which shall be applied on generating the tree + * @return stdClass $treeMenu object with which ExtJS can generate the graphical tree + */ +function generate_reqspec_tree(&$db, &$testproject_mgr, $testproject_id, + $testproject_name, $filters = null, $options = null) +{ + $tree_manager = &$testproject_mgr->tree_manager; + + $map_nodetype_id = $tree_manager->get_available_node_types(); + $map_id_nodetype = array_flip($map_nodetype_id); + + $my = array(); + + $my['options'] = array( + 'for_printing' => 0, + 'exclude_branches' => null, + 'recursive' => true, + 'order_cfg' => array( + 'type' => 'spec_order' + ) + ); + + $my['filters'] = array( + 'exclude_node_types' => array( + 'testplan' => 'exclude me', + 'testsuite' => 'exclude me', + 'testcase' => 'exclude me', + 'requirement_spec_revision' => 'exclude me' + ), + 'exclude_children_of' => array( + 'testcase' => 'exclude my children', + 'requirement' => 'exclude my children', + 'testsuite' => 'exclude my children' + ), + 'filter_doc_id' => null, + 'filter_title' => null, + 'filter_status' => null, + 'filter_type' => null, + 'filter_spec_type' => null, + 'filter_coverage' => null, + 'filter_relation' => null, + 'filter_tc_id' => null, + 'filter_custom_fields' => null + ); + + // merge with given parameters + $my['options'] = array_merge($my['options'], (array) $options); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $req_spec = $tree_manager->get_subtree($testproject_id, $my['filters'], + $my['options']); + + $req_spec['name'] = $testproject_name; + $req_spec['id'] = $testproject_id; + $req_spec['node_type_id'] = $map_nodetype_id['testproject']; + + $filtered_map = get_filtered_req_map($db, $testproject_id, $testproject_mgr, + $my['filters']); + + $level = 1; + $req_spec = prepare_reqspec_treenode($db, $level, $req_spec, $filtered_map, + $map_id_nodetype, $map_nodetype_id, $my['filters'], $my['options']); + + $menustring = null; + $treeMenu = new stdClass(); + $treeMenu->rootnode = new stdClass(); + $treeMenu->rootnode->total_req_count = $req_spec['total_req_count']; + $treeMenu->rootnode->name = $req_spec['name']; + $treeMenu->rootnode->id = $req_spec['id']; + $treeMenu->rootnode->leaf = isset($req_spec['leaf']) ? $req_spec['leaf'] : false; + $treeMenu->rootnode->position = $req_spec['position']; + $treeMenu->rootnode->href = $req_spec['href']; + + // replace key ('childNodes') to 'children' + if (isset($req_spec['childNodes'])) { + $menustring = str_ireplace('childNodes', 'children', + json_encode($req_spec['childNodes'])); + } + + if (! is_null($menustring)) { + $menustring = str_ireplace( + array( + ',"' . REMOVEME . '"', + '"' . REMOVEME . '",' + ), array( + '', + '' + ), $menustring); + + $menustring = str_ireplace( + array( + ':' . REMOVEME, + '"' . REMOVEME . '"' + ), array( + ':[]', + '' + ), $menustring); + } + $treeMenu->menustring = $menustring; + + return $treeMenu; +} + +/** + * Generate the necessary data object for the filtered requirement specification tree. + * + * @author Andreas Simon + * @param Database $db + * reference to database handler object + * @param testproject $testproject_mgr + * reference to testproject manager object + * @param int $testproject_id + * ID of the project for which the tree shall be generated + * @param string $testproject_name + * Name of the test project + * @param array $filters + * Filter settings which shall be applied to the tree, possible values are: + * 'filter_doc_id', + * 'filter_title', + * 'filter_status', + * 'filter_type', + * 'filter_spec_type', + * 'filter_coverage', + * 'filter_relation', + * 'filter_tc_id', + * 'filter_custom_fields' + * @param array $options + * Further options which shall be applied on generating the tree + * @return stdClass $treeMenu object with which ExtJS can generate the graphical tree + */ +function generateTestReqCoverageTree(&$db, $tproject_id, $tproject_name, + $filters = null, $options = null) +{ + $tproject_mgr = new testproject($db); + $tree_manager = &$tproject_mgr->tree_manager; + + $map_nodetype_id = $tree_manager->get_available_node_types(); + $map_id_nodetype = array_flip($map_nodetype_id); + + $my = array(); + + $my['options'] = array( + 'for_printing' => 0, + 'exclude_branches' => null, + 'recursive' => true, + 'order_cfg' => array( + 'type' => 'spec_order' + ) + ); + + $my['filters'] = array( + 'exclude_node_types' => array( + 'testplan' => 'exclude me', + 'testsuite' => 'exclude me', + 'testcase' => 'exclude me', + 'requirement_spec_revision' => 'exclude me' + ), + 'exclude_children_of' => array( + 'testcase' => 'exclude my children', + 'requirement' => 'exclude my children', + 'testsuite' => 'exclude my children' + ), + 'filter_doc_id' => null, + 'filter_title' => null, + 'filter_status' => null, + 'filter_type' => null, + 'filter_spec_type' => null, + 'filter_coverage' => null, + 'filter_relation' => null, + 'filter_tc_id' => null, + 'filter_custom_fields' => null + ); + + // merge with given parameters + $my['options'] = array_merge($my['options'], (array) $options); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $req_spec = $tree_manager->get_subtree($tproject_id, $my['filters'], + $my['options']); + + $req_spec['name'] = $tproject_name; + $req_spec['id'] = $tproject_id; + $req_spec['node_type_id'] = $map_nodetype_id['testproject']; + + $filtered_map = get_filtered_req_map($db, $tproject_id, $tproject_mgr, + $my['filters']); + + $level = 1; + $req_spec = prepare_reqspeccoverage_treenode($db, $level, $req_spec, + $filtered_map, $map_id_nodetype, $map_nodetype_id, $my['filters'], + $my['options']); + + $menustring = null; + $treeMenu = new stdClass(); + $treeMenu->rootnode = new stdClass(); + $treeMenu->rootnode->total_req_count = $req_spec['total_req_count']; + $treeMenu->rootnode->name = $req_spec['name']; + $treeMenu->rootnode->id = $req_spec['id']; + $treeMenu->rootnode->leaf = isset($req_spec['leaf']) ? $req_spec['leaf'] : false; + $treeMenu->rootnode->position = $req_spec['position']; + $treeMenu->rootnode->href = $req_spec['href']; + + // replace key ('childNodes') to 'children' + if (isset($req_spec['childNodes'])) { + $menustring = str_ireplace('childNodes', 'children', + json_encode($req_spec['childNodes'])); + } + + if (! is_null($menustring)) { + // delete null elements for Ext JS + $menustring = str_ireplace(array( + ':null', + ',null', + 'null,', + 'null' + ), array( + ':[]', + '', + '', + '' + ), $menustring); + } + $treeMenu->menustring = $menustring; + + return $treeMenu; +} + +/** + * Generate a filtered map with all fitting requirements in it. + * + * @author Andreas Simon + * @param Database $db + * reference to database handler object + * @param int $testproject_id + * ID of the project for which the tree shall be generated + * @param testproject $testproject_mgr + * reference to testproject manager object + * @param array $filters + * Filter settings which shall be applied to the tree + * @param array $options + * Further options which shall be applied on generating the tree + * @return array $filtered_map map with all fitting requirements + * + * @internal revisions + * @since 1.9.4 + * 20120827 - franciscom - TICKET 5178: Requirement Specification->"Req. Spec. Type" Filter-> KO + * + */ +function get_filtered_req_map(&$db, $testproject_id, &$testproject_mgr, $filters) +{ + $tables = tlObjectWithDB::getDBTables( + array( + 'nodes_hierarchy', + 'requirements', + 'req_specs', + 'req_relations', + 'req_versions', + 'req_coverage', + 'tcversions', + 'cfield_design_values', + 'req_specs_revisions' + )); + + $sql = " SELECT R.id, R.req_doc_id, NH_R.name AS title, R.srs_id, " . + " RS.doc_id AS req_spec_doc_id, NH_RS.name AS req_spec_title, " . + " RV.version, RV.id AS version_id, NH_R.node_order, " . + " RV.expected_coverage, RV.status, RV.type, RV.active, RV.is_open " . + " FROM {$tables['requirements']} R " . + " JOIN {$tables['nodes_hierarchy']} NH_R ON NH_R.id = R.id " . + " JOIN {$tables['nodes_hierarchy']} NH_RV ON NH_RV.parent_id = NH_R.id " . + " JOIN {$tables['req_versions']} RV ON RV.id = NH_RV.id " . + " JOIN {$tables['req_specs']} RS ON RS.id = R.srs_id " . + " JOIN {$tables['req_specs_revisions']} RSPECREV ON RSPECREV.parent_id = RS.id " . + " JOIN {$tables['nodes_hierarchy']} NH_RS ON NH_RS.id = RS.id "; + + if (isset($filters['filter_relation'])) { + $sql .= " JOIN {$tables['req_relations']} RR " . + " ON (RR.destination_id = R.id OR RR.source_id = R.id) "; + } + + if (isset($filters['filter_tc_id'])) { + $tc_cfg = config_get('testcase_cfg'); + $tc_prefix = $testproject_mgr->getTestCasePrefix($testproject_id); + $tc_prefix .= $tc_cfg->glue_character; + + $tc_ext_id = $db->prepare_int( + str_replace($tc_prefix, '', $filters['filter_tc_id'])); + + $sql .= " JOIN {$tables['req_coverage']} RC ON RC.req_id = R.id " . + " JOIN {$tables['nodes_hierarchy']} NH_T ON NH_T.id = RC.testcase_id " . + " JOIN {$tables['nodes_hierarchy']} NH_TV on NH_TV.parent_id = NH_T.id " . + " JOIN {$tables['tcversions']} TV ON TV.id = NH_TV.id " . + " AND TV.tc_external_id = {$tc_ext_id} "; + } + + if (isset($filters['filter_custom_fields'])) { + $suffix = 1; + + foreach ($filters['filter_custom_fields'] as $cf_id => $cf_value) { + $sql .= " JOIN {$tables['cfield_design_values']} CF{$suffix} " . + " ON CF{$suffix}.node_id = RV.id " . + " AND CF{$suffix}.field_id = {$cf_id} "; + + // single value or array? + if (is_array($cf_value)) { + $sql .= " AND ( "; + $count = 1; + foreach ($cf_value as $value) { + if ($count > 1) { + $sql .= " OR "; + } + $sql .= " CF{$suffix}.value LIKE '%{$value}%' "; + $count ++; + } + $sql .= " ) "; + } else { + $sql .= " AND CF{$suffix}.value LIKE '%{$cf_value}%' "; + } + + $suffix ++; + } + } + + $sql .= " WHERE RS.testproject_id = {$testproject_id} "; + + if (isset($filters['filter_doc_id'])) { + $doc_id = $db->prepare_string($filters['filter_doc_id']); + $sql .= " AND R.req_doc_id LIKE '%{$doc_id}%' OR RS.doc_id LIKE '%{$doc_id}%' "; + } + + if (isset($filters['filter_title'])) { + $title = $db->prepare_string($filters['filter_title']); + $sql .= " AND NH_R.name LIKE '%{$title}%' "; + } + + if (isset($filters['filter_coverage'])) { + $coverage = $db->prepare_int($filters['filter_coverage']); + $sql .= " AND expected_coverage = {$coverage} "; + } + + if (isset($filters['filter_status'])) { + $statuses = (array) $filters['filter_status']; + foreach ($statuses as $key => $status) { + $statuses[$key] = "'" . $db->prepare_string($status) . "'"; + } + $statuses = implode(",", $statuses); + $sql .= " AND RV.status IN ({$statuses}) "; + } + + if (isset($filters['filter_type'])) { + $types = (array) $filters['filter_type']; + + foreach ($types as $key => $type) { + $types[$key] = $db->prepare_string($type); + } + $types = implode("','", $types); + $sql .= " AND RV.type IN ('{$types}') "; + } + + if (isset($filters['filter_spec_type'])) { + $spec_types = (array) $filters['filter_spec_type']; + + foreach ($spec_types as $key => $type) { + $spec_types[$key] = $db->prepare_string($type); + } + $spec_types = implode("','", $spec_types); + $sql .= " AND RSPECREV.type IN ('{$spec_types}') "; + } + + if (isset($filters['filter_relation'])) { + $sql .= " AND ( "; + $count = 1; + foreach ($filters['filter_relation'] as $rel_filter) { + $relation_info = explode('_', $rel_filter); + $relation_type = $db->prepare_int($relation_info[0]); + $relation_side = isset($relation_info[1]) ? $relation_info[1] : null; + $sql .= ($count == 1) ? " ( " : " OR ( "; + + if ($relation_side == "destination") { + $sql .= " RR.destination_id = R.id "; + } elseif ($relation_side == "source") { + $sql .= " RR.source_id = R.id "; + } else { + $sql .= " (RR.destination_id = R.id OR RR.source_id = R.id) "; + } + + $sql .= " AND RR.relation_type = {$relation_type} ) "; + $count ++; + } + + $sql .= " ) "; + } + + $sql .= " ORDER BY RV.version DESC "; + return $db->fetchRowsIntoMap($sql, 'id'); +} + +/** + * Prepares nodes for the filtered requirement tree. + * Filters out those nodes which are not in the given map and counts the remaining subnodes. + * + * @author Andreas Simn + * @param Database $db + * reference to database handler object + * @param int $level + * gets increased by one for each sublevel in recursion + * @param array $node + * the tree structure to traverse + * @param array $filtered_map + * a map of filtered requirements, req that are not in this map will be deleted + * @param array $map_id_nodetype + * array with node type IDs as keys, node type descriptions as values + * @param array $map_nodetype_id + * array with node type descriptions as keys, node type IDs as values + * @param array $filters + * @param array $options + * @return array tree structure after filtering out unneeded nodes + */ +function prepare_reqspec_treenode(&$db, $level, &$node, &$filtered_map, + &$map_id_nodetype, &$map_nodetype_id, &$filters, &$options) +{ + $child_req_count = 0; + if (isset($node['childNodes']) && is_array($node['childNodes'])) { + // node has childs, must be a specification (or testproject) + foreach ($node['childNodes'] as $key => $childnode) { + $current_childnode = &$node['childNodes'][$key]; + $current_childnode = prepare_reqspec_treenode($db, $level + 1, + $current_childnode, $filtered_map, $map_id_nodetype, + $map_nodetype_id, $filters, $options); + + // now count childnodes that have not been deleted and are requirements + if (! is_null($current_childnode) && $current_childnode != REMOVEME) { + switch ($current_childnode['node_type_id']) { + case $map_nodetype_id['requirement']: + $child_req_count ++; + break; + + case $map_nodetype_id['requirement_spec']: + $child_req_count += $current_childnode['child_req_count']; + break; + } + } + } + } + + $node_type = $map_id_nodetype[$node['node_type_id']]; + + $delete_node = false; + switch ($node_type) { + case 'testproject': + $node['total_req_count'] = $child_req_count; + break; + + case 'requirement_spec': + // add requirement count + // delete empty specs + $node['child_req_count'] = $child_req_count; + $delete_node = ! $child_req_count; + break; + + case 'requirement': + // delete node from tree if it is not in $filtered_map + $delete_node = (is_null($filtered_map) || + ! array_key_exists($node['id'], $filtered_map)); + break; + } + + if ($delete_node) { + unset($node); + $node = REMOVEME; + } else { + $node = render_reqspec_treenode($db, $node, $filtered_map, + $map_id_nodetype); + } + + return $node; +} + +/** + * Prepares nodes for the filtered requirement tree. + * Filters out those nodes which are not in the given map and counts the remaining subnodes. + * + * @author Andreas Simn + * @param Database $db + * reference to database handler object + * @param int $level + * gets increased by one for each sublevel in recursion + * @param array $node + * the tree structure to traverse + * @param array $filtered_map + * a map of filtered requirements, req that are not in this map will be deleted + * @param array $map_id_nodetype + * array with node type IDs as keys, node type descriptions as values + * @param array $map_nodetype_id + * array with node type descriptions as keys, node type IDs as values + * @param array $filters + * @param array $options + * @return array tree structure after filtering out unneeded nodes + */ +function prepare_reqspeccoverage_treenode(&$db, $level, &$node, &$filtered_map, + &$map_id_nodetype, &$map_nodetype_id, &$filters, &$options) +{ + $child_req_count = 0; + + if (isset($node['childNodes']) && is_array($node['childNodes'])) { + // node has childs, must be a specification (or testproject) + foreach ($node['childNodes'] as $key => $childnode) { + $current_childnode = &$node['childNodes'][$key]; + $current_childnode = prepare_reqspeccoverage_treenode($db, + $level + 1, $current_childnode, $filtered_map, $map_id_nodetype, + $map_nodetype_id, $filters, $options); + + // now count childnodes that have not been deleted and are requirements + if (! is_null($current_childnode)) { + switch ($current_childnode['node_type_id']) { + case $map_nodetype_id['requirement']: + $child_req_count ++; + break; + + case $map_nodetype_id['requirement_spec']: + $child_req_count += $current_childnode['child_req_count']; + break; + } + } + } + } + + $node_type = $map_id_nodetype[$node['node_type_id']]; + + $delete_node = false; + + switch ($node_type) { + case 'testproject': + $node['total_req_count'] = $child_req_count; + break; + + case 'requirement_spec': + // add requirement count + $node['child_req_count'] = $child_req_count; + // delete empty specs + if (! $child_req_count) { + $delete_node = true; + } + break; + + case 'requirement': + // delete node from tree if it is not in $filtered_map + if (is_null($filtered_map) || + ! array_key_exists($node['id'], $filtered_map)) { + $delete_node = true; + } + break; + } + + if ($delete_node) { + unset($node); + $node = null; + } else { + $node = render_reqspeccoverage_treenode($db, $node, $filtered_map, + $map_id_nodetype); + } + + return $node; +} + +/** + * Prepares nodes in the filtered requirement tree for displaying with ExtJS. + * + * @author Andreas Simon + * @param Database $db + * reference to database handler object + * @param array $node + * the object to prepare + * @param array $filtered_map + * a map of filtered requirements, req that are not in this map will be deleted + * @param array $map_id_nodetype + * array with node type IDs as keys, node type descriptions as values + * @return array tree object with all needed data for ExtJS tree + */ +function render_reqspec_treenode(&$db, &$node, &$filtered_map, &$map_id_nodetype) +{ + static $js_functions; + static $forbidden_parents; + + if (! $js_functions) { + $js_functions = array( + 'testproject' => 'TPROJECT_REQ_SPEC_MGMT', + 'requirement_spec' => 'REQ_SPEC_MGMT', + 'requirement' => 'REQ_MGMT' + ); + + $req_cfg = config_get('req_cfg'); + $forbidden_parents['testproject'] = 'none'; + $forbidden_parents['requirement'] = 'testproject'; + + // Hmm is ok ? (see next lines, may be it's time to remove this code) + $forbidden_parents['requirement_spec'] = 'requirement_spec'; + if ($req_cfg->child_requirements_mgmt) { + $forbidden_parents['requirement_spec'] = 'none'; + } + } + + $node_type = $map_id_nodetype[$node['node_type_id']]; + $node_id = $node['id']; + + $node['href'] = "javascript:{$js_functions[$node_type]}({$node_id});"; + $node['text'] = htmlspecialchars($node['name']); + $node['leaf'] = false; // will be set to true later for requirement nodes + $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; + $node['cls'] = 'folder'; + + // custom Properties that will be accessed by EXT-JS using node.attributes + $node['testlink_node_type'] = $node_type; + $node['forbidden_parent'] = $forbidden_parents[$node_type]; + $node['testlink_node_name'] = $node['text']; + + switch ($node_type) { + case 'testproject': + break; + + case 'requirement_spec': + // get doc id from filtered array, it's already stored in there + $doc_id = ''; + foreach ($node['childNodes'] as $child) { + if (is_array($child)) { + $child_id = $child['id']; + if (isset($filtered_map[$child_id])) { + $doc_id = htmlspecialchars( + $filtered_map[$child_id]['req_spec_doc_id']); + } + break; // only need to get one child for this + } + } + // BUGID 3765: load doc ID with if this req spec has no direct req child nodes. + // Reason: in these cases we do not have a parent doc ID in $filtered_map + if ($doc_id == '') { + static $req_spec_mgr = null; + if (! $req_spec_mgr) { + $req_spec_mgr = new requirement_spec_mgr($db); + } + $tmp_spec = $req_spec_mgr->get_by_id($node_id); + $doc_id = $tmp_spec['doc_id']; + unset($tmp_spec); + } + + $count = $node['child_req_count']; + $node['text'] = "{$doc_id}:{$node['text']} ({$count})"; + break; + + case 'requirement': + $node['leaf'] = true; + $doc_id = htmlspecialchars($filtered_map[$node_id]['req_doc_id']); + $node['text'] = "{$doc_id}:{$node['text']}"; + break; + } + + return $node; +} + +/** + * Prepares nodes in the filtered requirement tree for displaying with ExtJS. + * + * @author Andreas Simon + * @param Database $db + * reference to database handler object + * @param array $node + * the object to prepare + * @param array $filtered_map + * a map of filtered requirements, req that are not in this map will be deleted + * @param array $map_id_nodetype + * array with node type IDs as keys, node type descriptions as values + * @return array tree object with all needed data for ExtJS tree + */ +function render_reqspeccoverage_treenode(&$db, &$node, &$filtered_map, + &$map_id_nodetype) +{ + static $js_functions; + static $forbidden_parents; + + if (! $js_functions) { + $js_functions = array( + 'testproject' => 'EP', + 'requirement_spec' => 'ERS', + 'requirement' => 'ER' + ); + + $req_cfg = config_get('req_cfg'); + $forbidden_parents['testproject'] = 'none'; + $forbidden_parents['requirement'] = 'testproject'; + + // Hmm is ok ? (see next lines, may be it's time to remove this code) + $forbidden_parents['requirement_spec'] = 'requirement_spec'; + if ($req_cfg->child_requirements_mgmt) { + $forbidden_parents['requirement_spec'] = 'none'; + } + } + + $node_type = $map_id_nodetype[$node['node_type_id']]; + $node_id = $node['id']; + + $node['href'] = "javascript:{$js_functions[$node_type]}({$node_id});"; + $node['text'] = htmlspecialchars($node['name']); + $node['leaf'] = false; // will be set to true later for requirement nodes + $node['position'] = isset($node['node_order']) ? $node['node_order'] : 0; + $node['cls'] = 'folder'; + + // custom Properties that will be accessed by EXT-JS using node.attributes + $node['testlink_node_type'] = $node_type; + $node['forbidden_parent'] = $forbidden_parents[$node_type]; + $node['testlink_node_name'] = $node['text']; + + switch ($node_type) { + case 'testproject': + break; + + case 'requirement_spec': + // get doc id from filtered array, it's already stored in there + $doc_id = ''; + foreach ($node['childNodes'] as $child) { + if (! is_null($child)) { + $child_id = $child['id']; + if (isset($filtered_map[$child_id])) { + $doc_id = htmlspecialchars( + $filtered_map[$child_id]['req_spec_doc_id']); + } + break; // only need to get one child for this + } + } + // BUGID 3765: load doc ID with if this req spec has no direct req child nodes. + // Reason: in these cases we do not have a parent doc ID in $filtered_map + if ($doc_id == '') { + static $req_spec_mgr = null; + if (! $req_spec_mgr) { + $req_spec_mgr = new requirement_spec_mgr($db); + } + $tmp_spec = $req_spec_mgr->get_by_id($node_id); + $doc_id = $tmp_spec['doc_id']; + unset($tmp_spec); + } + + $count = $node['child_req_count']; + $node['text'] = "{$doc_id}:{$node['text']} ({$count})"; + break; + + case 'requirement': + $node['leaf'] = true; + $doc_id = htmlspecialchars($filtered_map[$node_id]['req_doc_id']); + $node['text'] = "{$doc_id}:{$node['text']}"; + break; + } + + return $node; +} + +/** + * + * @todo delete if necessary + * @deprecated + */ +function apply_status_filters($tplan_id, &$items, &$fobj, &$tplan_mgr, + $statusCfg) +{ + $fm = config_get('execution_filter_methods'); + $methods = $fm['status_code']; + + $ffn = array( + $methods['any_build'] => 'filterStatusSetAtLeastOneOfActiveBuilds', + $methods['all_builds'] => 'filterStatusSetAllActiveBuilds', + $methods['specific_build'] => 'filter_by_status_for_build', + $methods['current_build'] => 'filter_by_status_for_build', + $methods['latest_execution'] => 'filter_by_status_for_latest_execution' + ); + + $f_method = isset($fobj->filter_result_method) ? $fobj->filter_result_method : null; + $f_result = isset($fobj->filter_result_result) ? $fobj->filter_result_result : null; + $f_result = (array) $f_result; + + // if "any" was selected as filtering status, don't filter by status + if (in_array($statusCfg['all'], $f_result)) { + $f_result = null; + } + + if (! is_null($f_method) && isset($ffn[$f_method])) { + // special case: + // filtering by "not run" status in any build + // filtering by "not run" status in specific + // + // we change filter function + if (in_array($statusCfg['not_run'], $f_result)) { + $ffn[$methods['any_build']] = 'filter_not_run_for_any_build'; + $ffn[$methods['specific_build']] = 'filter_by_status_for_build'; + } + + // special case: when filtering by "current build", we set the build to filter with + // to the build chosen in settings instead of the one in filters + if ($f_method == $methods['current_build']) { + $fobj->filter_result_build = $fobj->setting_build; + } + + $items = $ffn[$f_method]($tplan_mgr, $items, $tplan_id, $fobj); + } + return $items; +} + +/** + */ +function update_status_for_colors(&$dbHandler, &$items, $context, $statusCfg) +{ + $tables = tlObject::getDBTables(array( + 'executions', + 'nodes_hierarchy' + )); + $dummy = current($items); + $key2scan = array_keys($items); + $keySet = null; + foreach ($key2scan as $fx) { + $keySet[] = $items[$fx]['tcversion_id']; + } + + extract($context); // magic to create single variables + + $sql = " SELECT E.status, NH_TCV.parent_id AS tcase_id " . + " FROM {$tables['executions']} E " . + " JOIN {$tables['nodes_hierarchy']} NH_TCV ON NH_TCV.id = E.tcversion_id " . + " JOIN " . " ( SELECT MAX(E2.id) AS last_exec_id " . + " FROM {$tables['executions']} E2 " . + " WHERE testplan_id = {$tplanID} " . " AND tcversion_id IN (" . + implode(',', $keySet) . ") " . + " AND platform_id = {$dummy['platform_id']} " . + " AND build_id = {$buildID} " . + " GROUP BY testplan_id,tcversion_id,platform_id,build_id ) AS EY " . + " ON E.id = EY.last_exec_id "; + + $rs = $dbHandler->fetchRowsIntoMap($sql, 'tcase_id'); + + if (! is_null($rs)) { + foreach ($key2scan as $tcase_id) { + $rr = isset($rs[$tcase_id]['status']) && + ! is_null($rs[$tcase_id]['status']) ? $rs[$tcase_id]['status'] : $statusCfg['not_run']; + + if ($rr != $items[$tcase_id]['exec_status']) { + $items[$tcase_id]['exec_status'] = $rr; + } + } + } +} + +/** + * Used when ['viewType'] == 'testSpecTree' + */ +function generateTestSpecTreeNew(&$db, $tproject_id, $tproject_name, $linkto, + $filters = null, $options = null) +{ + $chronos[] = microtime(true); + + $my = array(); + + $my['options'] = array( + 'forPrinting' => 0, + 'hideTestCases' => 0, + 'tc_action_enabled' => 1, + 'viewType' => 'testSpecTree' + ); + + $my['filters'] = array( + 'keywords' => null, + 'plaftorms' => null, + 'testplan' => null + ); + + $my['options'] = array_merge($my['options'], (array) $options); + $my['options']['showTestCaseID'] = config_get('treemenu_show_testcase_id'); + + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $treeMenu = new stdClass(); + $treeMenu->rootnode = null; + $treeMenu->menustring = ''; + + $glueChar = config_get('testcase_cfg')->glue_character; + $menustring = null; + + $tproject_mgr = new testproject($db); + $tree_manager = &$tproject_mgr->tree_manager; + + $hash_descr_id = $tree_manager->get_available_node_types(); + $hash_id_descr = array_flip($hash_descr_id); + + $tcase_prefix = $tproject_mgr->getTestCasePrefix($tproject_id) . $glueChar; + $test_spec = getTestSpecTree($tproject_id, $tproject_mgr, $filters); + + // Added root node for test specification -> testproject + $test_spec['name'] = $tproject_name; + $test_spec['id'] = $tproject_id; + $test_spec['node_type_id'] = $hash_descr_id['testproject']; + + $map_node_tccount = array(); + $tc2show = null; + + if ($test_spec) { + if (isset($my['filters']['filter_custom_fields']) && + isset($test_spec['childNodes'])) { + $test_spec['childNodes'] = filter_by_cf_values($db, + $test_spec['childNodes'], $my['filters']['filter_custom_fields'], + $hash_descr_id); + } + + $pnFilters = array( + 'keywords' => $my['filters']['filter_keywords'], + 'keywords_filter_type' => $my['filters']['filter_keywords_filter_type'], + 'platforms' => $my['filters']['filter_platforms'] + ); + + $pnOptions = array( + 'hideTestCases' => $my['options']['hideTestCases'], + 'ignoreInactiveTestCases' => $my['options']['ignore_inactive_testcases'], + 'ignoreActiveTestCases' => $my['options']['ignore_active_testcases'] + ); + + // Important/CRITIC: + // prepareTestSpecNode() will make changes + // to $test_spec like filtering by test case keywords. + $testcase_counters = prepareTestSpecNode($db, $tproject_mgr, + $tproject_id, $test_spec, $map_node_tccount, $pnFilters, $pnOptions); + + if (is_null($test_spec)) { + $test_spec['name'] = $tproject_name; + $test_spec['id'] = $tproject_id; + $test_spec['node_type_id'] = $hash_descr_id['testproject']; + } + + foreach ($testcase_counters as $key => $value) { + $test_spec[$key] = $testcase_counters[$key]; + } + + $tc2show = renderTreeNode(1, $test_spec, $hash_id_descr, $linkto, + $tcase_prefix, $my['options']); + } + + $menustring = ''; + $treeMenu->rootnode = new stdClass(); + $treeMenu->rootnode->name = $test_spec['text']; + $treeMenu->rootnode->id = $test_spec['id']; + $treeMenu->rootnode->leaf = isset($test_spec['leaf']) ? $test_spec['leaf'] : false; + $treeMenu->rootnode->text = $test_spec['text']; + $treeMenu->rootnode->position = $test_spec['position']; + $treeMenu->rootnode->href = $test_spec['href']; + + // 20090328 - franciscom - BUGID 2299 + // More details about problem found on 20090308 and fixed IN WRONG WAY + // TPROJECT + // |______ TSA + // |__ TC1 + // |__ TC2 + // | + // |______ TSB + // |______ TSC + // + // Define Keyword K1,K2 + // + // NO TEST CASE HAS KEYWORD ASSIGNED + // Filter by K1 + // Tree will show root that spins Forever + // menustring before str_ireplace : [null,null] + // menustring AFTER [null] + // + // Now fixed. + // + // Some minor fix to do + // Il would be important exclude Top Level Test suites. + // + // + // 20090308 - franciscom + // Changed because found problem on: + // Test Specification tree when applying Keyword filter using a keyword NOT PRESENT + // in test cases => Tree root shows loading icon and spin never stops. + // + // Attention: do not know if in other situation this will generate a different bug + // + // + // Change key ('childNodes') to the one required by Ext JS tree. + if (isset($test_spec['childNodes'])) { + $menustring = str_ireplace('childNodes', 'children', + json_encode($test_spec['childNodes'])); + } + + if (! is_null($menustring)) { + // Remove null elements (Ext JS tree do not like it ). + // :null happens on -> "children":null,"text" that must become "children":[],"text" + // $menustring = str_ireplace(array(':null',',null','null,'),array(':[]','',''), $menustring); + // $menustring = str_ireplace(array(':null',',null','null,','null'),array(':[]','','',''), $menustring); + // $menustring = preg_replace('/,\s*"[^"]+":null|"[^"]+":null,?/', '', $menustring); + $menustring = str_ireplace( + array( + ':' . REMOVEME, + ',"' . REMOVEME . '"', + '"' . REMOVEME . '",' + ), array( + ':[]', + '', + '' + ), $menustring); + } + $treeMenu->menustring = $menustring; + + $tc2show = ! is_null($tc2show) ? explode(",", trim($tc2show, ",")) : null; + return array( + 'menu' => $treeMenu, + 'leaves' => $tc2show, + 'tree' => $test_spec + ); +} + +/** + * importance & status (workflow status) can be array + */ +function getTestSpecTree($tprojectID, &$tprojectMgr, &$fObj) +{ + $flt = array(); + $flt['exclude_branches'] = isset($fObj['filter_toplevel_testsuite']) && + is_array($fObj['filter_toplevel_testsuite']) ? $fObj['filter_toplevel_testsuite'] : null; + + $flt['testcase_name'] = null; + $flt['testcase_id'] = null; + $flt['execution_type'] = null; + $flt['importance'] = null; + $flt['status'] = null; + $flt['keywords'] = null; + $flt['platforms'] = null; + + if (isset($fObj['filter_testcase_name']) && + ! is_null($fObj['filter_testcase_name']) && + ($dummy = trim($fObj['filter_testcase_name'])) != '') { + $flt['testcase_name'] = $dummy; + } + + if (isset($fObj['filter_tc_id']) && ! is_null($fObj['filter_tc_id'])) { + $flt['testcase_id'] = intval($fObj['filter_tc_id']); + } + + if (isset($fObj['filter_execution_type']) && + ! is_null($fObj['filter_execution_type'])) { + $flt['execution_type'] = intval($fObj['filter_execution_type']); + } + + if (isset($fObj['filter_importance']) && + ! is_null($fObj['filter_importance'])) { + $xx = (array) $fObj['filter_importance']; + if ($xx[0] > 0) { + $flt['importance'] = $xx; + } + } + + if (isset($fObj['filter_workflow_status']) && + ! is_null($fObj['filter_workflow_status'])) { + $xx = (array) $fObj['filter_workflow_status']; + if ($xx[0] > 0) { + $flt['status'] = $xx; + } + } + + $piece = 'platforms'; + $full = 'filter_' . $piece; + if (isset($fObj[$full]) && ! is_null($fObj[$full])) { + $xx = (array) $fObj[$full]; + if ($xx[0] > 0) { + $flt[$piece] = $xx; + } + } + + $opt = array( + 'recursive' => true, + 'exclude_testcases' => false + ); + return $tprojectMgr->getTestSpec($tprojectID, $flt, $opt); +} + +/** + */ +function prepareTestSpecNode(&$db, &$tprojectMgr, $tprojectID, &$node, + &$map_node_tccount, $filters = null, $options = null) +{ + static $tables; + static $my; + static $filtersApplied; + static $decoding_info; + static $tcFilterByKeywords; + static $doFilterOn; + static $tcFilterByPlatforms; + + if (! $tables) { + $tables = tlObjectWithDB::getDBTables( + array( + 'tcversions', + 'nodes_hierarchy', + 'testplan_tcversions' + )); + $decoding_info = array( + 'node_id_descr' => array_flip( + $tprojectMgr->tree_manager->get_available_node_types()) + ); + $my = array(); + $my['options'] = array( + 'hideTestCases' => 0 + ); + $my['filters'] = array( + 'keywords' => null, + 'platforms' => null + ); + + $my['options'] = array_merge($my['options'], (array) $options); + $my['filters'] = array_merge($my['filters'], (array) $filters); + + $doFilterOn['keywords'] = ! is_null($my['filters']['keywords']) && + (intval($my['filters']['keywords']) > 0); + + if ($doFilterOn['keywords']) { + $tcFilterByKeywords = $tprojectMgr->getTCLatestVersionFilteredByKeywords( + $tprojectID, $my['filters']['keywords'], + $my['filters']['keywords_filter_type']); + + if (is_null($tcFilterByKeywords)) { + // tree will be empty + $node = null; + $tcase_counters['testcase_count'] = 0; + return $tcase_counters; + } + } + + $doFilterOn['platforms'] = ! is_null($my['filters']['platforms']) && + (intval($my['filters']['platforms']) > 0); + if ($doFilterOn['platforms']) { + $tcFilterByPlatforms = $tprojectMgr->getTCLatestVersionFilteredByPlatforms( + $tprojectID, $my['filters']['platforms']); + + if (is_null($tcFilterByPlatforms)) { + // tree will be empty + $node = null; + $tcase_counters['testcase_count'] = 0; + return $tcase_counters; + } + } + + // Critic for logic that prune empty branches + // TICKET 4353: added active/inactive filter + $filtersApplied = $doFilterOn['keywords'] || + $my['options']['ignoreInactiveTestCases'] || + $my['options']['ignoreActiveTestCases'] || $doFilterOn['platforms']; + } + + $tcase_counters['testcase_count'] = 0; + $node_type = isset($node['node_type_id']) ? $decoding_info['node_id_descr'][$node['node_type_id']] : null; + + if ($node_type == 'testcase') { + $remove_node = false; + + if ($my['options']['ignoreInactiveTestCases']) { + $sql = " SELECT COUNT(TCV.id) AS count_active_versions " . + " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . + " WHERE NH.parent_id=" . $node['id'] . + " AND NH.id = TCV.id AND TCV.active=1"; + $result = $db->exec_query($sql); + $row = $db->fetch_array($result); + if ($row['count_active_versions'] == 0) { + $remove_node = true; + } + } elseif ($my['options']['ignoreActiveTestCases']) { + $sql = " SELECT COUNT(TCV.id) AS count_active_versions " . + " FROM {$tables['tcversions']} TCV, {$tables['nodes_hierarchy']} NH " . + " WHERE NH.parent_id=" . $node['id'] . + " AND NH.id = TCV.id AND TCV.active=1"; + $result = $db->exec_query($sql); + $row = $db->fetch_array($result); + if ($row['count_active_versions'] != 0) { + $remove_node = true; + } + } + + if ($my['options']['hideTestCases'] || $remove_node || + ($doFilterOn['keywords'] && + ! isset($tcFilterByKeywords[$node['id']])) || + ($doFilterOn['platforms'] && + ! isset($tcFilterByPlatforms[$node['id']]))) { + $node = REMOVEME; + } else { + // needed to avoid problems when using json_encode with EXTJS + unset($node['childNodes']); + $node['leaf'] = true; + $tcase_counters['testcase_count'] = 1; + } + } // if($node_type == 'testcase') + + if (! is_null($node) && isset($node['childNodes']) && + is_array($node['childNodes'])) { + + // node has to be a Test Suite ? + $childNodes = &$node['childNodes']; + $childNodesQty = count($childNodes); + + for ($idx = 0; $idx < $childNodesQty; $idx ++) { + $current = &$childNodes[$idx]; + // I use set an element to null to filter out leaf menu items + if (is_null($current) || $current == REMOVEME) { + $childNodes[$idx] = REMOVEME; + continue; + } + + $counters_map = prepareTestSpecNode($db, $tprojectMgr, $tprojectID, + $current, $map_node_tccount); + + // 20120831 - + // to be analized carefully, because this can be solution + // to null issue with json and ext-js + if (is_null($current)) { + $childNodes[$idx] = REMOVEME; + } + + $tcase_counters['testcase_count'] += $counters_map['testcase_count']; + } + $node['testcase_count'] = $tcase_counters['testcase_count']; + + if (isset($node['id'])) { + $map_node_tccount[$node['id']] = array( + 'testcount' => $node['testcase_count'], + 'name' => $node['name'] + ); + } + + // node must be destroyed if empty had we have using filtering conditions + if ($filtersApplied && ! $tcase_counters['testcase_count'] && + ($node_type != 'testproject')) { + $node = null; + } + } elseif ($node_type == 'testsuite') { + // does this means is an empty test suite ??? - franciscom 20080328 + $map_node_tccount[$node['id']] = array( + 'testcount' => 0, + 'name' => $node['name'] + ); + + // If is an EMPTY Test suite and we have added filtering conditions, + // We will destroy it. + if ($filtersApplied) { + $node = null; + } + } + + return $tcase_counters; +} + +/** + */ +function helper_filter_cleanup(&$itemSet, $hits) +{ + $key2remove = null; + foreach ($itemSet as $tcase_id => $dummy) { + if (! isset($hits[$tcase_id])) { + $key2remove[] = $tcase_id; + } + } + if (! is_null($key2remove)) { + foreach ($key2remove as $key) { + unset($itemSet[$key]); + } + } +} ?> diff --git a/lib/functions/users.inc.php b/lib/functions/users.inc.php index 4e46f6c9da..3db8f9937a 100644 --- a/lib/functions/users.inc.php +++ b/lib/functions/users.inc.php @@ -1,467 +1,471 @@ - 'map_name_with_inactive_mark', 'order_by' => $gui_cfg->tprojects_combo_order_by); - $arrProducts = $tproject_mgr->get_accessible_for_user($id,$opt); - - $tproject_cookie = config_get('cookie')->testProjectMemory . $id; - if (isset($_COOKIE[$tproject_cookie])) { - if (isset($arrProducts[$_COOKIE[$tproject_cookie]]) - && $arrProducts[$_COOKIE[$tproject_cookie]]) { - $_SESSION['testprojectID'] = $_COOKIE[$tproject_cookie]; - tLog('Cookie: {$tproject_cookie}='.$_SESSION['testprojectID']); - } - } - if (!$_SESSION['testprojectID']) { - $tpID = null; - if (sizeof($arrProducts)) - { - $tpID = key($arrProducts); - } - $_SESSION['testprojectID'] = $tpID; - } - // Validation is done in navBar.php - $tplan_cookie = 'TL_lastTestPlanForUserID_' . $id; - if (isset($_COOKIE[$tplan_cookie])) - { - $_SESSION['testplanID'] = $_COOKIE[$tplan_cookie]; - tLog("Cookie: {$tplan_cookie}=".$_SESSION['testplanID']); - } - - return 1; -} - -/* - function: getUsersForHtmlOptions - - args: db: reference to db object - [whereClause]: - [add_blank_option]: - [active_filter]: - - returns: map - - rev : -*/ -function getUsersForHtmlOptions(&$db,$whereClause = null,$additional_users = null, - $active_filter = null,$users = null, $opt=null) -{ - $users_map = null; - if (!$users) - { - $sqlWhere = $whereClause; - if(!is_null($active_filter)) - { - $whereClause .= ' AND active =' . ($active_filter > 0 ? 1 : 0) . ' '; - } - $users = tlUser::getAll($db,$sqlWhere,"id",null,tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); - } - return buildUserMap($users,!is_null($additional_users),$additional_users,$opt); -} - -/* - function: buildUserMap - - args: - $users: map of user objects - [add_options]: default false. - true, elements present on additional_options arguments - will be will added to result map. - - [additional_options]: default null - map with key=user id, value=verbose description - - returns: map ready to be used on a HTML select input. - -*/ -function buildUserMap($users,$add_options = false, $additional_options=null, $opt=null) -{ - $my['opt'] = array('userDisplayFormat' => null); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $usersMap = null; - $inactivePrefix = lang_get('tag_for_inactive_users'); - if ($users) - { - if($add_options) - { - $my_options = is_null($additional_options) ? array( 0 => '') : $additional_options; - foreach($my_options as $code => $verbose_code) - { - $usersMap[$code] = $verbose_code; - } - } - $userSet = array_keys($users); - $loops2do = count($userSet); - - for( $idx=0; $idx < $loops2do ; $idx++) - { - $userID = $userSet[$idx]; - $usersMap[$userID] = $users[$userID]->getDisplayName($my['opt']['userDisplayFormat']); - if($users[$userID]->isActive == 0) - { - $usersMap[$userID] = $inactivePrefix . ' ' . $usersMap[$userID]; - } - } - } - return $usersMap; -} - - -/** - * reset user password in DB - * - * @param resource &$db reference to database handler - * @param integer $userID - * @param string $newPasswordSendMethod, default 'send_password_by_mail' - * - * @return hash - * status: integer result status code - * password: new password - * msg: error message (if any) - */ -function resetPassword(&$db,$userID,$passwordSendMethod='send_password_by_mail') -{ - $doIt = false; - $retval = array('status' => tl::OK, 'password' => '', 'msg' => ''); - - $user = new tlUser($userID); - $retval['status'] = $user->readFromDB($db); - - if ($retval['status'] >= tl::OK) - { - // Reset can be done ONLY if user authentication method allows it. - $systemCfg = config_get('authentication'); - $userAuthMethod = trim($user->authentication); - if($userAuthMethod == '' || is_null($userAuthMethod)) - { - $userAuthMethod = $systemCfg['method']; - } - - $cfg = $systemCfg['domain']; - $doIt = isset($cfg[$userAuthMethod]) && - $cfg[$userAuthMethod]['allowPasswordManagement']; - } - - if ($doIt) - { - $retval['status'] = tlUser::E_EMAILLENGTH; - - if( trim($user->emailAddress) != "") - { - $newPassword = tlUser::generatePassword(8,4); - $retval['status'] = $user->setPassword($newPassword,$userAuthMethod); - if ($retval['status'] >= tl::OK) - { - $retval['password'] = $newPassword; - $mail_op = new stdClass(); - $mail_op->status_ok = false; - if( $passwordSendMethod == 'send_password_by_mail' ) - { - $msgBody = lang_get('your_password_is') . "\n\n" . $newPassword . "\n\n" . lang_get('contact_admin'); - $mail_op = @email_send(config_get('from_email'), - $user->emailAddress,lang_get('mail_passwd_subject'),$msgBody); - } - - if ($mail_op->status_ok || ($passwordSendMethod == 'display_on_screen') ) - { - $retval['status'] = $user->writePasswordToDB($db); - } - else - { - $retval['status'] = tl::ERROR; - $retval['msg'] = $mail_op->msg; - } - } - } - } - - $retval['msg'] = ($retval['msg'] != "") ? $retval['msg'] : getUserErrorMessage($retval['status']) ; - return $retval; -} - -/* - function: getUserErrorMessage - - args : - - returns: - -*/ -function getUserErrorMessage($code) -{ - $msg = 'ok'; - switch($code) - { - case tl::OK: - break; - - case tlUser::E_LOGINLENGTH: - $msg = lang_get('error_user_login_length_error'); - break; - - case tlUser::E_EMAILLENGTH: - $msg = lang_get('empty_email_address'); - break; - - case tlUser::E_EMAILFORMAT: - $msg = lang_get('no_good_email_address'); - break; - - case tlUser::E_NOTALLOWED: - $msg = lang_get('user_login_valid_regex'); - break; - - case tlUser::E_FIRSTNAMELENGTH: - $msg = lang_get('empty_first_name'); - break; - - case tlUser::E_LOGINALREADYEXISTS: - $msg = lang_get('user_name_exists'); - break; - - case tlUser::E_LASTNAMELENGTH: - $msg = lang_get('empty_last_name'); - break; - - case tlUser::E_PWDEMPTY: - $msg = lang_get('warning_empty_pwd'); - break; - - case tlUser::E_PWDDONTMATCH: - $msg = lang_get('wrong_old_password'); - break; - - case tlUser::S_PWDMGTEXTERNAL : - $msg = lang_get('password_mgmt_is_external'); - break; - - case ERROR: - case tlUser::E_DBERROR: - default: - $msg = lang_get('error_user_not_updated'); - break; - } - return $msg; -} - - -/* - function: getAllUsersRoles - - args: - - returns: - - -*/ -function getAllUsersRoles(&$db,$order_by = null) -{ - $tables = tlObject::getDBTables(array('users','roles')); - - $sql = "SELECT users.id FROM {$tables['users']} users " . - " LEFT OUTER JOIN {$tables['roles']} roles ON users.role_id = roles.id "; - $sql .= is_null($order_by) ? " ORDER BY login " : $order_by; - - $users = tlDBObject::createObjectsFromDBbySQL($db,$sql,"id","tlUser",false,tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); - - $loop2do = count($users); - $specialK = array_flip((array)config_get('demoSpecialUsers')); - $demoModeEnabled = config_get('demoMode'); - for($idx=0; $idx < $loop2do; $idx++) - { - $users[$idx]->isDemoSpecial = $demoModeEnabled ? isset($specialK[$users[$idx]->login]) : false; - } - return $users; -} - -/* - function: getTestersForHtmlOptions - - args: - - returns: - -*/ -/** - * getTestersForHtmlOptions - * returns users that have role on ($tplanID,$tprojectID) with right - * to execute a test case. - * - * @param resource &$db reference to database handler - * @param integer $tplanID test plan id - * @param integer $tprojectID test project id - * @param $users UNUSED - remove - * @param $additional_testers TBD - * @param string $activeStatus. values: 'active','inactive','any' - * - * @return array TBD - * @internal revisions - */ -function getTestersForHtmlOptions(&$db,$tplanID,$tproject,$users = null, - $additional_testers = null,$activeStatus = 'active') -{ - $orOperand = false; - $activeTarget = 1; - switch ($activeStatus) - { - case 'any': - $orOperand = true; - break; - - case 'inactive': - $activeTarget = 0; - break; - - case 'active': - default: - break; - } - - $users_roles = get_tplan_effective_role($db,$tplanID,$tproject,null,$users); - - $userFilter = array(); - foreach($users_roles as $keyUserID => $roleInfo) - { - if( is_object($roleInfo['effective_role']) ) - { - if( $roleInfo['effective_role']->hasRight('testplan_execute') && - ($orOperand || $roleInfo['user']->isActive == $activeTarget) ) - { - $userFilter[$keyUserID] = $roleInfo['user']; - } - } - } - return buildUserMap($userFilter,true,$additional_testers); -} - -/** - * - */ -function initialize_tabsmenu() -{ - $hl = new stdClass(); - $hl->view_roles = 0; - $hl->create_role = 0; - $hl->edit_role = 0; - - $hl->view_users = 0; - $hl->create_user = 0; - $hl->edit_user = 0; - - $hl->assign_users_tproject = 0; - $hl->assign_users_tplan = 0; - return $hl; -} - - -/* - function: getGrantsForUserMgmt - utility function used on all user and role pages - to pass grants to smarty templates. - Logic is: - if user has Global user management right => no control - on specific test project or test plan is done - - - args: - - returns: - -*/ -function getGrantsForUserMgmt(&$dbHandler,&$userObj,$tprojectID=null,$tplanID=null) -{ - $answers = new stdClass(); - $grants = new stdClass(); - $grants->user_mgmt = $userObj->hasRight($dbHandler,"mgt_users"); - $grants->role_mgmt = $userObj->hasRight($dbHandler,"role_management"); - - // in order to assign DEFAULT role to user, due to current implementation - // you need to access all user data => we request you can do mgt_users. - // A question arise: - // how user_role_assignment right has to be understood ? - // how it is used ? where ? - // $grants->user_role_assignment = $userObj->hasRight($dbHandler,"user_role_assignment"); - - $grants->tproject_user_role_assignment = "no"; - $grants->tplan_user_role_assignment = "no"; - - if($grants->user_mgmt == 'yes') - { - $grants->tplan_user_role_assignment = 'yes'; - $grants->tproject_user_role_assignment = 'yes'; - } - else - { - $grants->tplan_user_role_assignment = $userObj->hasRight($dbHandler,"testplan_user_role_assignment", - $tprojectID,$tplanID); - - - $answers->user_role_assignment = $userObj->hasRight($dbHandler,"user_role_assignment",null,-1); - $answers->testproject_user_role_assignment=$userObj->hasRight($dbHandler,"testproject_user_role_assignment",$tprojectID,-1); - if($answers->user_role_assignment == "yes" || $answers->testproject_user_role_assignment == "yes") - { - $grants->tproject_user_role_assignment = "yes"; - } - } - foreach($grants as $key => $value) - { - $grants->$key = $value == "yes" ? "yes" : "no"; - } - - return $grants; -} - - -/** - * just a wrapper - * - */ -function setUserSessionFromObj(&$db,$userObj) { - return setUserSession($db,$userObj->login,$userObj->dbID, - $userObj->globalRoleID,$userObj->emailAddress); + 'map_name_with_inactive_mark', + 'order_by' => $gui_cfg->tprojects_combo_order_by + ); + $arrProducts = $tproject_mgr->get_accessible_for_user($id, $opt); + + $tproject_cookie = config_get('cookie')->testProjectMemory . $id; + if (isset($_COOKIE[$tproject_cookie]) && + isset($arrProducts[$_COOKIE[$tproject_cookie]]) && + $arrProducts[$_COOKIE[$tproject_cookie]]) { + $_SESSION['testprojectID'] = $_COOKIE[$tproject_cookie]; + tLog('Cookie: {$tproject_cookie}=' . $_SESSION['testprojectID']); + } + if (! $_SESSION['testprojectID']) { + $tpID = null; + if (count($arrProducts)) { + $tpID = key($arrProducts); + } + $_SESSION['testprojectID'] = $tpID; + } + // Validation is done in navBar.php + $tplan_cookie = 'TL_lastTestPlanForUserID_' . $id; + if (isset($_COOKIE[$tplan_cookie])) { + $_SESSION['testplanID'] = $_COOKIE[$tplan_cookie]; + tLog("Cookie: {$tplan_cookie}=" . $_SESSION['testplanID']); + } + + return 1; +} + +/* + * function: getUsersForHtmlOptions + * + * args: db: reference to db object + * [whereClause]: + * [add_blank_option]: + * [active_filter]: + * + * returns: map + * + * rev : + */ +function getUsersForHtmlOptions(&$db, $whereClause = null, + $additional_users = null, $active_filter = null, $users = null, $opt = null) +{ + if (! $users) { + $sqlWhere = $whereClause; + if (! is_null($active_filter)) { + $whereClause .= ' AND active =' . ($active_filter > 0 ? 1 : 0) . ' '; + } + $users = tlUser::getAll($db, $sqlWhere, "id", null, + tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); + } + return buildUserMap($users, ! is_null($additional_users), $additional_users, + $opt); +} + +/* + * function: buildUserMap + * + * args: + * $users: map of user objects + * [add_options]: default false. + * true, elements present on additional_options arguments + * will be will added to result map. + * + * [additional_options]: default null + * map with key=user id, value=verbose description + * + * returns: map ready to be used on a HTML select input. + * + */ +function buildUserMap($users, $add_options = false, $additional_options = null, + $opt = null) +{ + $my['opt'] = array( + 'userDisplayFormat' => null + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $usersMap = null; + $inactivePrefix = lang_get('tag_for_inactive_users'); + if ($users) { + if ($add_options) { + $my_options = is_null($additional_options) ? array( + 0 => '' + ) : $additional_options; + foreach ($my_options as $code => $verbose_code) { + $usersMap[$code] = $verbose_code; + } + } + $userSet = array_keys($users); + $loops2do = count($userSet); + + for ($idx = 0; $idx < $loops2do; $idx ++) { + $userID = $userSet[$idx]; + $usersMap[$userID] = $users[$userID]->getDisplayName( + $my['opt']['userDisplayFormat']); + if ($users[$userID]->isActive == 0) { + $usersMap[$userID] = $inactivePrefix . ' ' . $usersMap[$userID]; + } + } + } + return $usersMap; +} + +/** + * reset user password in DB + * + * @param + * resource &$db reference to database handler + * @param integer $userID + * @param string $newPasswordSendMethod, + * default 'send_password_by_mail' + * + * @return array status: integer result status code + * password: new password + * msg: error message (if any) + */ +function resetPassword(&$db, $userID, + $passwordSendMethod = 'send_password_by_mail') +{ + $doIt = false; + $retval = array( + 'status' => tl::OK, + 'password' => '', + 'msg' => '' + ); + + $user = new tlUser($userID); + $retval['status'] = $user->readFromDB($db); + + if ($retval['status'] >= tl::OK) { + // Reset can be done ONLY if user authentication method allows it. + $systemCfg = config_get('authentication'); + $userAuthMethod = trim($user->authentication); + if ($userAuthMethod == '' || is_null($userAuthMethod)) { + $userAuthMethod = $systemCfg['method']; + } + + $cfg = $systemCfg['domain']; + $doIt = isset($cfg[$userAuthMethod]) && + $cfg[$userAuthMethod]['allowPasswordManagement']; + } + + if ($doIt) { + $retval['status'] = tlUser::E_EMAILLENGTH; + + if (trim($user->emailAddress) != "") { + $newPassword = tlUser::generatePassword(8, 4); + $retval['status'] = $user->setPassword($newPassword, $userAuthMethod); + if ($retval['status'] >= tl::OK) { + $retval['password'] = $newPassword; + $mail_op = new stdClass(); + $mail_op->status_ok = false; + if ($passwordSendMethod == 'send_password_by_mail') { + $msgBody = lang_get('your_password_is') . "\n\n" . + $newPassword . "\n\n" . lang_get('contact_admin'); + $mail_op = @email_send(config_get('from_email'), + $user->emailAddress, lang_get('mail_passwd_subject'), + $msgBody); + } + + if ($mail_op->status_ok || + ($passwordSendMethod == 'display_on_screen')) { + $retval['status'] = $user->writePasswordToDB($db); + } else { + $retval['status'] = tl::ERROR; + $retval['msg'] = $mail_op->msg; + } + } + } + } + + $retval['msg'] = ($retval['msg'] != "") ? $retval['msg'] : getUserErrorMessage( + $retval['status']); + return $retval; +} + +/* + * function: getUserErrorMessage + * + * args : + * + * returns: + * + */ +function getUserErrorMessage($code) +{ + $msg = 'ok'; + switch ($code) { + case tl::OK: + break; + + case tlUser::E_LOGINLENGTH: + $msg = lang_get('error_user_login_length_error'); + break; + + case tlUser::E_EMAILLENGTH: + $msg = lang_get('empty_email_address'); + break; + + case tlUser::E_EMAILFORMAT: + $msg = lang_get('no_good_email_address'); + break; + + case tlUser::E_NOTALLOWED: + $msg = lang_get('user_login_valid_regex'); + break; + + case tlUser::E_FIRSTNAMELENGTH: + $msg = lang_get('empty_first_name'); + break; + + case tlUser::E_LOGINALREADYEXISTS: + $msg = lang_get('user_name_exists'); + break; + + case tlUser::E_LASTNAMELENGTH: + $msg = lang_get('empty_last_name'); + break; + + case tlUser::E_PWDEMPTY: + $msg = lang_get('warning_empty_pwd'); + break; + + case tlUser::E_PWDDONTMATCH: + $msg = lang_get('wrong_old_password'); + break; + + case tlUser::S_PWDMGTEXTERNAL: + $msg = lang_get('password_mgmt_is_external'); + break; + + case ERROR: + case tlUser::E_DBERROR: + default: + $msg = lang_get('error_user_not_updated'); + break; + } + return $msg; +} + +/* + * function: getAllUsersRoles + * + * args: + * + * returns: + * + * + */ +function getAllUsersRoles(&$db, $order_by = null) +{ + $tables = tlObject::getDBTables(array( + 'users', + 'roles' + )); + + $sql = "SELECT users.id FROM {$tables['users']} users " . + " LEFT OUTER JOIN {$tables['roles']} roles ON users.role_id = roles.id "; + $sql .= is_null($order_by) ? " ORDER BY login " : $order_by; + + $users = tlDBObject::createObjectsFromDBbySQL($db, $sql, "id", "tlUser", + false, tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); + + $loop2do = count($users); + $specialK = array_flip((array) config_get('demoSpecialUsers')); + $demoModeEnabled = config_get('demoMode'); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $users[$idx]->isDemoSpecial = $demoModeEnabled ? isset( + $specialK[$users[$idx]->login]) : false; + } + return $users; +} + +/* + * function: getTestersForHtmlOptions + * + * args: + * + * returns: + * + */ +/** + * getTestersForHtmlOptions + * returns users that have role on ($tplanID,$tprojectID) with right + * to execute a test case. + * + * @param + * resource &$db reference to database handler + * @param integer $tplanID + * test plan id + * @param integer $tprojectID + * test project id + * @param array $users + * @param array $additional_testers + * TBD + * @param string $activeStatus. + * values: 'active','inactive','any' + * + * @return array TBD + * @internal revisions + */ +function getTestersForHtmlOptions(&$db, $tplanID, $tproject, $users = null, + $additional_testers = null, $activeStatus = 'active') +{ + $orOperand = false; + $activeTarget = 1; + switch ($activeStatus) { + case 'any': + $orOperand = true; + break; + + case 'inactive': + $activeTarget = 0; + break; + + case 'active': + default: + break; + } + + $users_roles = get_tplan_effective_role($db, $tplanID, $tproject, null, + $users); + + $userFilter = array(); + foreach ($users_roles as $keyUserID => $roleInfo) { + if (is_object($roleInfo['effective_role']) && + $roleInfo['effective_role']->hasRight('testplan_execute') && + ($orOperand || $roleInfo['user']->isActive == $activeTarget)) { + $userFilter[$keyUserID] = $roleInfo['user']; + } + } + return buildUserMap($userFilter, true, $additional_testers); +} + +/** + */ +function initializeTabsmenu() +{ + $hl = new stdClass(); + $hl->view_roles = 0; + $hl->create_role = 0; + $hl->edit_role = 0; + + $hl->view_users = 0; + $hl->create_user = 0; + $hl->edit_user = 0; + + $hl->assign_users_tproject = 0; + $hl->assign_users_tplan = 0; + return $hl; +} + +/* + * function: getGrantsForUserMgmt + * utility function used on all user and role pages + * to pass grants to smarty templates. + * Logic is: + * if user has Global user management right => no control + * on specific test project or test plan is done + * + * + * args: + * + * returns: + * + */ +function getGrantsForUserMgmt(&$dbHandler, &$userObj, $tprojectID = null, + $tplanID = null) +{ + $answers = new stdClass(); + $grants = new stdClass(); + $grants->user_mgmt = $userObj->hasRight($dbHandler, "mgt_users"); + $grants->role_mgmt = $userObj->hasRight($dbHandler, "role_management"); + + // in order to assign DEFAULT role to user, due to current implementation + // you need to access all user data => we request you can do mgt_users. + // A question arise: + // how user_role_assignment right has to be understood ? + // how it is used ? where ? + // $grants->user_role_assignment = $userObj->hasRight($dbHandler,"user_role_assignment"); + + $grants->tproject_user_role_assignment = "no"; + $grants->tplan_user_role_assignment = "no"; + + if ($grants->user_mgmt == 'yes') { + $grants->tplan_user_role_assignment = 'yes'; + $grants->tproject_user_role_assignment = 'yes'; + } else { + $grants->tplan_user_role_assignment = $userObj->hasRight($dbHandler, + "testplan_user_role_assignment", $tprojectID, $tplanID); + + $answers->user_role_assignment = $userObj->hasRight($dbHandler, + "user_role_assignment", null, - 1); + $answers->testproject_user_role_assignment = $userObj->hasRight( + $dbHandler, "testproject_user_role_assignment", $tprojectID, - 1); + if ($answers->user_role_assignment == "yes" || + $answers->testproject_user_role_assignment == "yes") { + $grants->tproject_user_role_assignment = "yes"; + } + } + foreach ($grants as $key => $value) { + $grants->$key = $value == "yes" ? "yes" : "no"; + } + + return $grants; +} + +/** + * just a wrapper + */ +function setUserSessionFromObj(&$db, $userObj) +{ + return setUserSession($db, $userObj->login, $userObj->dbID, + $userObj->globalRoleID, $userObj->emailAddress); } diff --git a/lib/functions/web_editor.php b/lib/functions/web_editor.php index d84b21bf82..760a7668e0 100644 --- a/lib/functions/web_editor.php +++ b/lib/functions/web_editor.php @@ -1,119 +1,154 @@ -Editor->basePath = $base_path . 'third_party/ckeditor/'; - $of->Editor->config['customConfig'] = $base_path . $webEditorCfg['configFile']; - $of->Editor->config['toolbar'] = $webEditorCfg['toolbar']; - $of->Editor->config['language'] = $ckeditorLang; - if (isset($webEditorCfg['height'])) { - $of->Editor->config['height'] = $webEditorCfg['height']; - } - - if (isset($webEditorCfg['width'])) { - $of->Editor->config['width'] = $webEditorCfg['width']; - } - break; - - case 'tinymce': - $of = new tinymce($html_input_id) ; - if (isset($webEditorCfg['rows'])) { - $of->rows = $webEditorCfg['rows']; - } - - if (isset($webEditorCfg['cols'])) { - $of->cols = $webEditorCfg['cols']; - } - break; - - case 'none': - default: - $of = new no_editor($html_input_id) ; - if (isset($webEditorCfg['rows'])) { - $of->rows = $webEditorCfg['rows']; - } - - if (isset($webEditorCfg['cols'])) { - $of->cols = $webEditorCfg['cols']; - } - break; - } - - return $of; -} \ No newline at end of file +Editor->basePath = $base_path . 'third_party/ckeditor/'; + $of->Editor->config['customConfig'] = $base_path . + $webEditorCfg['configFile']; + $of->Editor->config['toolbar'] = $webEditorCfg['toolbar']; + $of->Editor->config['language'] = $ckeditorLang; + if (isset($webEditorCfg['height'])) { + $of->Editor->config['height'] = $webEditorCfg['height']; + } + + if (isset($webEditorCfg['width'])) { + $of->Editor->config['width'] = $webEditorCfg['width']; + } + break; + + case 'tinymce': + $of = new tinymce($html_input_id); + if (isset($webEditorCfg['rows'])) { + $of->rows = $webEditorCfg['rows']; + } + + if (isset($webEditorCfg['cols'])) { + $of->cols = $webEditorCfg['cols']; + } + break; + + case 'none': + default: + $of = new no_editor($html_input_id); + if (isset($webEditorCfg['rows'])) { + $of->rows = $webEditorCfg['rows']; + } + + if (isset($webEditorCfg['cols'])) { + $of->cols = $webEditorCfg['cols']; + } + break; + } + + return $of; +} diff --git a/lib/functions/xml.inc.php b/lib/functions/xml.inc.php index b13cbd95ca..e8ee3a4dc4 100644 --- a/lib/functions/xml.inc.php +++ b/lib/functions/xml.inc.php @@ -1,180 +1,161 @@ - ]> - * - * - * &xxe; - * - * - * - */ -function simplexml_load_file_wrapper($filename) -{ - // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html - libxml_disable_entity_loader(true); - $zebra = file_get_contents($filename); - $xml = @simplexml_load_string($zebra); - return $xml; -} - - -/** - * - * - */ -function exportDataToXML($items,$rootTpl,$elemTpl,$elemInfo,$bNoXMLHeader = false) -{ - if (!$items) - { - return; - } - - $xmlCode = ''; - reset($items); - while($item = each($items)) - { - $item = $item[1]; - $xmlElemCode = $elemTpl; - - // REMEMBER YOU NEED TO USE XMP TO DEBUG - // echo '$xmlElemCode'; echo "$xmlElemCode)"; - foreach($elemInfo as $subject => $replacement) - { - $fm = substr($subject,0,2); - $content = isset($item[$replacement]) ? $item[$replacement] : null; - - switch($fm) - { - case '||': - break; - - case '{{': - default: - $content = htmlspecialchars($content); - break; - } - - $howMany = 0; - $xmlElemCode = str_replace($subject,$content,$xmlElemCode,$howMany); - } - - - $xmlCode .= $xmlElemCode; - } - reset($items); - - $result = null; - if (!$bNoXMLHeader) - { - $result .= TL_XMLEXPORT_HEADER."\n"; - } - - if($rootTpl != '' && !is_null($rootTpl)) - { - $result .= str_replace("{{XMLCODE}}",$xmlCode,$rootTpl); - return $result; - } - else - { - return $xmlCode; - } -} - - -/** - * $simpleXMLItems - * $itemStructure: keys elements, attributes - * - * both keys are maps: - * key: element/attribute type - * value: map - * key: attribute name - * value: options used to request special - * processing like trim(), intval(),etc. - * - * Example: - * $tcXML['elements'] = array('string' => array("summary" => null, - * "preconditions" => 'trim'), - * 'integer' => array("node_order" => 'intval', - * "externalid" => null, - * "execution_type" => null, - * "importance" => null)); - * - * $tcXML['attributes'] = array('string' => array("name" => 'trim'), - * 'integer' => array('internalid' => null)); - * - * - */ -function getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure) -{ - $items = null; - if($simpleXMLItems) - { - $items_counter=0; - $loop_qty = count($simpleXMLItems); - - // new dBug($loop_qty); - for($idx=0; $idx < $loop_qty; $idx++) - { - // echo "DEBUG - " . __FUNCTION__ . " \$idx:$idx
    "; - foreach($itemStructure['elements'] as $castType => $keyValues) - { - foreach($keyValues as $key => $fn2apply) - { - $dummy[$key] = null; - if( property_exists($simpleXMLItems[$idx],$key) ) - { - $dummy[$key] = $simpleXMLItems[$idx]->$key; - settype($dummy[$key],$castType); - if(!is_null($fn2apply)) - { - $dummy[$key] = $fn2apply($dummy[$key]); - } - } - } - } - - if( isset($itemStructure['attributes']) && !is_null($itemStructure['attributes']) ) - { - foreach($itemStructure['attributes'] as $castType => $keyValues) - { - foreach($keyValues as $key => $fn2apply) - { - $dummy[$key] = null; - if( isset($simpleXMLItems[$idx],$key) ) - { - $dummy[$key] = $simpleXMLItems[$idx][$key]; - settype($dummy[$key],$castType); - if(!is_null($fn2apply)) - { - $dummy[$key] = $fn2apply($dummy[$key]); - } - } - } - } - } - $items[$items_counter++] = $dummy; - } - } - - return $items; -} \ No newline at end of file + ]> + * + * + * &xxe; + * + * + * + * User contribution regarding XML External Entity (XXE) Processing Attacks + */ +function simplexml_load_file_wrapper($filename) +{ + // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + libxml_disable_entity_loader(true); + $zebra = file_get_contents($filename); + $xml = @simplexml_load_string($zebra); + if ($xml === false) { + libxml_use_internal_errors(true); + $xml = simplexml_load_string($zebra); + echo lang_get("simplexml_load_file_wrapper_error"); + foreach (libxml_get_errors() as $error) { + echo "
    ", $error->message; + } + die(); + } + return $xml; +} + +/** + */ +function exportDataToXML($items, $rootTpl, $elemTpl, $elemInfo, + $bNoXMLHeader = false) +{ + if (! $items) { + return; + } + + $xmlCode = ''; + reset($items); + foreach ($items as $item) { + + $xmlElemCode = $elemTpl; + + // REMEMBER YOU NEED TO USE XMP TO DEBUG + // echo '$xmlElemCode'; echo "$xmlElemCode)"; + foreach ($elemInfo as $subject => $replacement) { + $fm = substr($subject, 0, 2); + $content = isset($item[$replacement]) ? $item[$replacement] : null; + + switch ($fm) { + case '||': + break; + + case '{{': + default: + $content = htmlspecialchars($content); + break; + } + + $howMany = 0; + $xmlElemCode = str_replace($subject, $content, $xmlElemCode, + $howMany); + } + + $xmlCode .= $xmlElemCode; + } + reset($items); + + $result = null; + if (! $bNoXMLHeader) { + $result .= TL_XMLEXPORT_HEADER . "\n"; + } + + if ($rootTpl != '' && ! is_null($rootTpl)) { + $result .= str_replace("{{XMLCODE}}", $xmlCode, $rootTpl); + return $result; + } else { + return $xmlCode; + } +} + +/** + * $simpleXMLItems + * $itemStructure: keys elements, attributes + * + * both keys are maps: + * key: element/attribute type + * value: map + * key: attribute name + * value: options used to request special + * processing like trim(), intval(),etc. + * + * Example: + * $tcXML['elements'] = array('string' => array("summary" => null, + * "preconditions" => 'trim'), + * 'integer' => array("node_order" => 'intval', + * "externalid" => null, + * "execution_type" => null, + * "importance" => null)); + * + * $tcXML['attributes'] = array('string' => array("name" => 'trim'), + * 'integer' => array('internalid' => null)); + */ +function getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure) +{ + $items = null; + if ($simpleXMLItems) { + $items_counter = 0; + $loop_qty = count($simpleXMLItems); + + for ($idx = 0; $idx < $loop_qty; $idx ++) { + foreach ($itemStructure['elements'] as $castType => $keyValues) { + foreach ($keyValues as $key => $fn2apply) { + $dummy[$key] = null; + if (property_exists($simpleXMLItems[$idx], $key)) { + $dummy[$key] = $simpleXMLItems[$idx]->$key; + settype($dummy[$key], $castType); + if (! is_null($fn2apply)) { + $dummy[$key] = $fn2apply($dummy[$key]); + } + } + } + } + + if (isset($itemStructure['attributes']) && + ! is_null($itemStructure['attributes'])) { + foreach ($itemStructure['attributes'] as $castType => $keyValues) { + foreach ($keyValues as $key => $fn2apply) { + $dummy[$key] = null; + if (isset($simpleXMLItems[$idx], $key)) { + $dummy[$key] = $simpleXMLItems[$idx][$key]; + settype($dummy[$key], $castType); + if (! is_null($fn2apply)) { + $dummy[$key] = $fn2apply($dummy[$key]); + } + } + } + } + } + $items[$items_counter ++] = $dummy; + } + } + + return $items; +} diff --git a/lib/general/frmWorkArea.php b/lib/general/frmWorkArea.php index ab7b519016..1536d8e285 100644 --- a/lib/general/frmWorkArea.php +++ b/lib/general/frmWorkArea.php @@ -1,263 +1,259 @@ - Associative Array TreeFramePath) -// key : feature -// value: page to lauch -// -$req_cfg = config_get('req_cfg'); - -// more info here -// array(0) => left pane -// array(1) => right pane -$aa_tfp = array( - 'editTc' => array('lib/testcases/listTestCases.php?feature=edit_tc', - 'lib/testcases/archiveData.php?edit=testproject&id='), - - 'assignReqs' => 'lib/testcases/listTestCases.php?feature=assignReqs', - 'searchTc' => 'lib/testcases/tcSearchForm.php', - - 'searchReq' => 'lib/requirements/reqSearchForm.php', - 'searchReqSpec' => 'lib/requirements/reqSpecSearchForm.php', - - 'printTestSpec' => 'lib/results/printDocOptions.php?type=testspec', - 'printReqSpec' => 'lib/results/printDocOptions.php?type=reqspec', - 'keywordsAssign' => 'lib/testcases/listTestCases.php?feature=keywordsAssign', - 'planAddTC' => array('lib/plan/planAddTCNavigator.php?loadRightPaneAddTC=0', - 'lib/results/printDocOptions.php?activity=addTC'), - 'planRemoveTC' => 'lib/plan/planTCNavigator.php?feature=removeTC&help_topic=planRemoveTC', - 'planUpdateTC' => 'lib/plan/planTCNavigator.php?feature=planUpdateTC', - 'show_ve' => 'lib/plan/planTCNavigator.php?feature=show_ve', - 'newest_tcversions' => '../../lib/plan/newest_tcversions.php', - 'test_urgency' => 'lib/plan/planTCNavigator.php?feature=test_urgency', - 'tc_exec_assignment' => 'lib/plan/planTCNavigator.php?feature=tc_exec_assignment', - 'executeTest' => array('lib/execute/execNavigator.php?setting_testplan=', 'lib/execute/execDashboard.php?id='), - 'showMetrics' => 'lib/results/resultsNavigator.php', - 'reqSpecMgmt' => array('lib/requirements/reqSpecListTree.php','lib/project/project_req_spec_mgmt.php?id=') -); - -$full_screen = array('newest_tcversions' => 1); - -//cleanup session var -$_SESSION['currentSrsId'] = null; - -/** feature to display */ -$showFeature = $args->feature; -if (isset($aa_tfp[$showFeature]) === FALSE) -{ - // argument is wrong - tLog("Wrong page argument feature = ".$showFeature, 'ERROR'); - exit(); + Associative Array TreeFramePath) +// key : feature +// value: page to lauch +// +$req_cfg = config_get('req_cfg'); + +// more info here +// array(0) => left pane +// array(1) => right pane +$aa_tfp = array( + 'editTc' => array( + 'lib/testcases/listTestCases.php?feature=edit_tc', + 'lib/testcases/archiveData.php?edit=testproject&id=' + ), + + 'assignReqs' => 'lib/testcases/listTestCases.php?feature=assignReqs', + 'searchTc' => 'lib/testcases/tcSearchForm.php', + + 'searchReq' => 'lib/requirements/reqSearchForm.php', + 'searchReqSpec' => 'lib/requirements/reqSpecSearchForm.php', + + 'printTestSpec' => 'lib/results/printDocOptions.php?type=testspec', + 'printReqSpec' => 'lib/results/printDocOptions.php?type=reqspec', + 'keywordsAssign' => 'lib/testcases/listTestCases.php?feature=keywordsAssign', + 'planAddTC' => array( + 'lib/plan/planAddTCNavigator.php?loadRightPaneAddTC=0', + 'lib/plan/planAddTC.php?activity=addTC' + ), + 'planRemoveTC' => 'lib/plan/planTCNavigator.php?feature=removeTC&help_topic=planRemoveTC', + 'planUpdateTC' => 'lib/plan/planTCNavigator.php?feature=planUpdateTC', + 'show_ve' => 'lib/plan/planTCNavigator.php?feature=show_ve', + 'newest_tcversions' => '../../lib/plan/newest_tcversions.php', + 'test_urgency' => 'lib/plan/planTCNavigator.php?feature=test_urgency', + 'tc_exec_assignment' => 'lib/plan/planTCNavigator.php?feature=tc_exec_assignment', + 'executeTest' => array( + 'lib/execute/execNavigator.php?setting_testplan=', + 'lib/execute/execDashboard.php?id=' + ), + 'showMetrics' => 'lib/results/resultsNavigator.php', + 'reqSpecMgmt' => array( + 'lib/requirements/reqSpecListTree.php', + 'lib/project/project_req_spec_mgmt.php?id=' + ) +); + +$full_screen = array( + 'newest_tcversions' => 1 +); + +// cleanup session var +$_SESSION['currentSrsId'] = null; + +/** + * feature to display + */ +$showFeature = $args->feature; +if (isset($aa_tfp[$showFeature]) === false) { + // argument is wrong + tLog("Wrong page argument feature = " . $showFeature, 'ERROR'); + exit(); +} + +// features that need to run the validate build function +if (in_array($showFeature, + array( + 'executeTest', + 'showMetrics', + 'tc_exec_assignment' + ))) { + // Check if for test project selected at least a test plan exist + if (isset($_SESSION['testplanID']) || ! is_null($args->tplan_id)) { + // Filter on build attributes: ACTIVE,OPEN + switch ($showFeature) { + case 'executeTest': + $hasToBe['active'] = true; + $hasToBe['open'] = true; + $featureHint = lang_get('href_execute_test'); + break; + + case 'tc_exec_assignment': + $txcfg = config_get('tree_filter_cfg'); + $cfg = $txcfg->testcases->plan_mode; + $hasToBe['active'] = $cfg->setting_build_inactive_out ? true : null; + $hasToBe['open'] = $cfg->setting_build_close_out ? true : null; + $featureHint = lang_get('href_tc_exec_assignment'); + break; + + default: + $hasToBe['active'] = null; + $hasToBe['open'] = null; + $featureHint = lang_get('href_rep_and_metrics'); + break; + } + + $tplanIDCard = new stdClass(); + $tplanIDCard->id = intval($_SESSION['testplanID']); + $tplanIDCard->name = $_SESSION['testplanName']; + $tplanMgr = new testplan($db); + + if (! is_null($args->tplan_id)) { + $tplanIDCard->id = intval($args->tplan_id); + $dummy = $tplanMgr->tree_manager->get_node_hierarchy_info( + $tplanIDCard->id); + $tplanIDCard->name = $dummy['name']; + } + + $ctx = new stdClass(); + $ctx->tplanIDCard = $tplanIDCard; + $ctx->featureTitle = $featureHint; + + validateBuildAvailability($db, $tplanMgr, $ctx, $hasToBe); + } else { + redirect('../plan/planView.php'); + exit(); + } +} + +// / 1. get path from global var +// / 2. the URL made easier after setting some rules for help/instruction files +// / naming convention. +// / +$smarty = new TLSmarty(); + +// try to add context in order to avoid using global coupling via $_SESSION +// this will be useful to open different test projects on different browser TAB +if (is_array($aa_tfp[$showFeature])) { + $leftPane = $aa_tfp[$showFeature][0]; + $rightPane = $aa_tfp[$showFeature][1]; + + if ($rightPane[strlen($rightPane) - 1] == '=') { + $rightPane .= intval($_SESSION['testprojectID']); + } + + if ($showFeature == 'executeTest') { + $leftPane .= $args->tplan_id; + } +} else { + $leftPane = $aa_tfp[$showFeature]; + $rightPane = 'lib/general/staticPage.php?key=' . $showFeature; +} + +if (intval($args->tproject_id) > 0 || intval($args->tplan_id) > 0) { + $leftPane .= (strpos($leftPane, "?") === false) ? "?" : "&"; + $leftPane .= "tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}"; + + // for execDashboard is OK, need to understand if will be ok for other features + // or is going to create issues. + $rightPane .= (strpos($rightPane, "?") === false) ? "?" : "&"; + $rightPane .= "tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}"; +} + +if (isset($full_screen[$showFeature])) { + redirect($leftPane); +} else { + $smarty->assign('treewidth', TL_FRMWORKAREA_LEFT_FRAME_WIDTH); + $smarty->assign('treeframe', $leftPane); + $smarty->assign('workframe', $rightPane); + $smarty->display('frmInner.tpl'); +} + +/** + * validate that some build exists (for Test Plan related features). + * If no valid build is found give feedback to user and exit. + * + * check if user can create builds, then put a link on the message page + * to create link feature + */ +function validateBuildAvailability(&$db, &$tplanMgr, $context, $attrFilter) +{ + $tpID = $context->tplanIDCard->id; + $tpName = $context->tplanIDCard->name; + + if (! $tplanMgr->getNumberOfBuilds($tpID, $attrFilter['active'], + $attrFilter['open'])) { + $msx = []; + if ($attrFilter['active']) { + $msx[] = lang_get('active'); + } + + if ($attrFilter['open']) { + $msx[] = lang_get('open'); + } + + $mzx = ''; + if (! empty($msx)) { + $mzx = "(" . implode(' & ', $msx) . ")"; + } + + $message = "

    " . $context->featureTitle . "

    " . + sprintf(lang_get('no_good_build'), $mzx) . " " . + htmlspecialchars($tpName) . ""; + + $link_to_op = ''; + $hint_text = ''; + if (has_rights($db, "testplan_create_build") == 'yes') { + // final url will be composed adding to $basehref + // (one TL variable available on smarty templates) to $link_to_op + $link_to_op = "lib/plan/buildEdit.php?do_action=create&tplan_id=$tpID"; + $hint_text = lang_get('create_a_build'); + } else { + $message .= '

    ' . lang_get('no_build_warning_part2') . '

    '; + } + + // show info and exit + $smarty = new TLSmarty(); + $smarty->assign('content', $message); + $smarty->assign('link_to_op', $link_to_op); + $smarty->assign('hint_text', $hint_text); + $smarty->display('workAreaSimple.tpl'); + exit(); + } +} + +/** + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + $iParams = array( + "feature" => array( + tlInputParameter::STRING_N + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + R_PARAMS($iParams, $args); + + return $args; } - -// features that need to run the validate build function -if (in_array($showFeature,array('executeTest','showMetrics','tc_exec_assignment'))) -{ - // Check if for test project selected at least a test plan exist - if( isset($_SESSION['testplanID']) || !is_null($args->tplan_id)) - { - // Filter on build attributes: ACTIVE,OPEN - switch($showFeature) - { - case 'executeTest': - $hasToBe['active'] = true; - $hasToBe['open'] = true; - $featureHint = lang_get('href_execute_test'); - break; - - case 'tc_exec_assignment': - $txcfg = config_get('tree_filter_cfg'); - $cfg = $txcfg->testcases->plan_mode; - $hasToBe['active'] = $cfg->setting_build_inactive_out ? true : null; - $hasToBe['open'] = $cfg->setting_build_close_out ? true : null; - $featureHint = lang_get('href_tc_exec_assignment'); - break; - - default: - $hasToBe['active'] = null; - $hasToBe['open'] = null; - $featureHint = lang_get('href_rep_and_metrics'); - break; - } - - - $tplanIDCard = new stdClass(); - $tplanIDCard->id = intval($_SESSION['testplanID']); - $tplanIDCard->name = $_SESSION['testplanName']; - $tplanMgr = new testplan($db); - - if(!is_null($args->tplan_id)) - { - $tplanIDCard->id = intval($args->tplan_id); - $dummy = $tplanMgr->tree_manager->get_node_hierarchy_info($tplanIDCard->id); - $tplanIDCard->name = $dummy['name']; - } - - $ctx = new stdClass(); - $ctx->tplanIDCard = $tplanIDCard; - $ctx->featureTitle = $featureHint; - - validateBuildAvailability($db,$tplanMgr,$ctx,$hasToBe); - } - else - { - redirect('../plan/planView.php'); - exit(); - } -} - -/// 1. get path from global var -/// 2. the URL made easier after setting some rules for help/instruction files -/// naming convention. -/// -$smarty = new TLSmarty(); - -// try to add context in order to avoid using global coupling via $_SESSION -// this will be useful to open different test projects on different browser TAB -if( is_array($aa_tfp[$showFeature]) ) -{ - $leftPane = $aa_tfp[$showFeature][0]; - $rightPane = $aa_tfp[$showFeature][1]; - - if($rightPane[strlen($rightPane)-1] == '=') - { - $rightPane .= intval($_SESSION['testprojectID']); - } - - if($showFeature == 'executeTest') - { - $leftPane .= $args->tplan_id; - } - // new dBug($leftPane); - -} -else -{ - $leftPane = $aa_tfp[$showFeature]; - $rightPane = 'lib/general/staticPage.php?key=' . $showFeature; -} - -if( intval($args->tproject_id) > 0 || intval($args->tproject_id) > 0) -{ - $leftPane .= (strpos($leftPane,"?") === false) ? "?" : "&"; - $leftPane .= "tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}"; - - // for execDashboard is OK, need to understand if will be ok for other features - // or is going to create issues. - $rightPane .= (strpos($rightPane,"?") === false) ? "?" : "&"; - $rightPane .= "tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}"; -} - -if(isset($full_screen[$showFeature])) -{ - redirect($leftPane); -} -else -{ - $smarty->assign('treewidth', TL_FRMWORKAREA_LEFT_FRAME_WIDTH); - $smarty->assign('treeframe', $leftPane); - $smarty->assign('workframe', $rightPane); - $smarty->display('frmInner.tpl'); -} - - -/** - * validate that some build exists (for Test Plan related features). - * If no valid build is found give feedback to user and exit. - * - * check if user can create builds, then put a link on the message page - * to create link feature - * - * - * - * - **/ -function validateBuildAvailability(&$db,&$tplanMgr,$context,$attrFilter) -{ - $tpID = $context->tplanIDCard->id; - $tpName = $context->tplanIDCard->name; - - if (!$tplanMgr->getNumberOfBuilds($tpID, $attrFilter['active'], $attrFilter['open'])) - { - $msx = null; - if($attrFilter['active']) - { - $msx[] = lang_get('active'); - } - - if($attrFilter['open']) - { - $msx[] = lang_get('open'); - } - - $mzx = ''; - if(count($msx) > 0) - { - $mzx = "(" . implode(' & ',$msx) . ")"; - } - - - $message = "

    " . $context->featureTitle . - "

    " . sprintf(lang_get('no_good_build'),$mzx) . - " " . htmlspecialchars($tpName) . ""; - - $link_to_op = ''; - $hint_text = ''; - if(has_rights($db,"testplan_create_build") == 'yes') - { - // final url will be composed adding to $basehref - // (one TL variable available on smarty templates) to $link_to_op - $link_to_op = "lib/plan/buildEdit.php?do_action=create&tplan_id=$tpID"; - $hint_text = lang_get('create_a_build'); - } - else - { - $message .= '

    ' . lang_get('no_build_warning_part2') . '

    '; - } - - // show info and exit - $smarty = new TLSmarty; - $smarty->assign('content', $message); - $smarty->assign('link_to_op', $link_to_op); - $smarty->assign('hint_text', $hint_text); - $smarty->display('workAreaSimple.tpl'); - exit(); - } -} - -/** - * - */ -function init_args() -{ - $_REQUEST=strings_stripSlashes($_REQUEST); - $args = new stdClass(); - $iParams = array("feature" => array(tlInputParameter::STRING_N), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - R_PARAMS($iParams,$args); - - return $args; -} \ No newline at end of file diff --git a/lib/general/mainPage.php b/lib/general/mainPage.php index aaaf30c145..a37bdb9334 100644 --- a/lib/general/mainPage.php +++ b/lib/general/mainPage.php @@ -1,290 +1,312 @@ -get_accessible_for_user($user->dbID,array('output' => 'map_name_with_inactive_mark')); -$tprojectQty = $tproject_mgr->getItemCount(); -$userIsBlindFolded = (is_null($accessibleItems) || count($accessibleItems) == 0) && $tprojectQty > 0; - -if($userIsBlindFolded) { - $testprojectID = $testplanID = 0; - $_SESSION['testprojectTopMenu'] = ''; -} - -$tplan2check = null; -$currentUser = $_SESSION['currentUser']; -$userID = $currentUser->dbID; - -$gui = new stdClass(); -$gui->grants = getGrants($db,$user,$testprojectID,$userIsBlindFolded); -$gui->hasTestCases = false; - -if($gui->grants['view_tc']) { - $gui->hasTestCases = $tproject_mgr->count_testcases($testprojectID) > 0 ? 1 : 0; -} - -$gui->hasKeywords = false; -if($gui->hasTestCases) { - $gui->hasKeywords = $tproject_mgr->hasKeywords($testprojectID); -} - - -// ----- Test Plan Section -------------------------------- -/** - * @TODO - franciscom - we must understand if these two calls are really needed, - * or is enough just call to getAccessibleTestPlans() - */ -$filters = array('plan_status' => ACTIVE); -$gui->num_active_tplans = $tproject_mgr->getActiveTestPlansCount($testprojectID); - -// get Test Plans available for the user -$arrPlans = (array)$currentUser->getAccessibleTestPlans($db,$testprojectID); - -if($testplanID > 0) { - // if this test plan is present on $arrPlans - // OK we will set it on $arrPlans as selected one. - // else - // need to set test plan on session - // - $index=0; - $found=0; - $loop2do=count($arrPlans); - for($idx=0; $idx < $loop2do; $idx++) { - if( $arrPlans[$idx]['id'] == $testplanID ) { - $found = 1; - $index = $idx; - break; - } - } - if( $found == 0 ) { - // update test plan id - $index = 0; - $testplanID = $arrPlans[$index]['id']; - } - - setSessionTestPlan($arrPlans[$index]); - $arrPlans[$index]['selected']=1; -} - -$gui->testplanRole = null; -if ($testplanID) { - - $rd = null; - // Role can be configured or inherited - if( isset($currentUser->tplanRoles[$testplanID]) ) { - // Configured - $role = $currentUser->tplanRoles[$testplanID]; - $rd = $role->getDisplayName(); - } else { - if( config_get('testplan_role_inheritance_mode') == 'global' ) { - $rd = $currentUser->globalRole->name; - } - } - - if( null != $rd ) { - $gui->testplanRole = $tlCfg->gui->role_separator_open .$rd . $tlCfg->gui->role_separator_close; - } -} -$rights2check = array('testplan_execute','testplan_create_build', - 'testplan_metrics','testplan_planning', - 'testplan_user_role_assignment', - 'mgt_testplan_create', - 'cfield_view', 'cfield_management', - 'testplan_milestone_overview', - 'exec_testcases_assigned_to_me', - 'exec_assign_testcases','exec_ro_access', - 'testplan_add_remove_platforms', - 'testplan_update_linked_testcase_versions', - 'testplan_set_urgent_testcases', - 'testplan_show_testcases_newest_versions'); - -foreach($rights2check as $key => $the_right) { - $gui->grants[$the_right] = $userIsBlindFolded ? 'no' : $currentUser->hasRight($db,$the_right,$testprojectID,$testplanID); -} - -$gui->grants['tproject_user_role_assignment'] = "no"; -if( $currentUser->hasRight($db,"testproject_user_role_assignment",$testprojectID,-1) == "yes" || - $currentUser->hasRight($db,"user_role_assignment",null,-1) == "yes" ) -{ - $gui->grants['tproject_user_role_assignment'] = "yes"; -} - - -$gui->url = array('metrics_dashboard' => 'lib/results/metricsDashboard.php', - 'testcase_assignments' => 'lib/testcases/tcAssignedToUser.php'); -$gui->launcher = 'lib/general/frmWorkArea.php'; -$gui->arrPlans = $arrPlans; -$gui->countPlans = count($gui->arrPlans); - - -$gui->testprojectID = $testprojectID; -$gui->testplanID = $testplanID; - -$gui->docs = config_get('userDocOnDesktop') ? getUserDocumentation() : null; - -$secCfg = config_get('config_check_warning_frequence'); -$gui->securityNotes = ''; -if( (strcmp($secCfg, 'ALWAYS') == 0) || - (strcmp($secCfg, 'ONCE_FOR_SESSION') == 0 && !isset($_SESSION['getSecurityNotesOnMainPageDone'])) ) -{ - $_SESSION['getSecurityNotesOnMainPageDone'] = 1; - $gui->securityNotes = getSecurityNotes($db); -} - -$gui->opt_requirements = isset($_SESSION['testprojectOptions']->requirementsEnabled) ? - $_SESSION['testprojectOptions']->requirementsEnabled : null; - - -$gui->plugins = array(); -foreach(array('EVENT_LEFTMENU_TOP', - 'EVENT_LEFTMENU_BOTTOM', - 'EVENT_RIGHTMENU_TOP', - 'EVENT_RIGHTMENU_BOTTOM') as $menu_item) -{ - # to be compatible with PHP 5.4 - $menu_content = event_signal($menu_item); - if( !empty($menu_content) ) - { - $gui->plugins[$menu_item] = $menu_content; - } -} - -$tplKey = 'mainPage'; -$tpl = $tplKey . '.tpl'; -$tplCfg = config_get('tpl'); -if( null !== $tplCfg && isset($tplCfg[$tplKey]) ) { - $tpl = $tplCfg->$tplKey; -} - -$smarty->assign('gui',$gui); -$smarty->display($tpl); - - -/** - * Get User Documentation - * based on contribution by Eugenia Drosdezki - */ -function getUserDocumentation() -{ - $target_dir = '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'docs'; - $documents = null; - - if ($handle = opendir($target_dir)) - { - while (false !== ($file = readdir($handle))) - { - clearstatcache(); - if (($file != ".") && ($file != "..")) - { - if (is_file($target_dir . DIRECTORY_SEPARATOR . $file)) - { - $documents[] = $file; - } - } - } - closedir($handle); - } - return $documents; -} - -/** - * - */ -function getGrants($dbHandler,$user,$tproject_id,$forceToNo=false) -{ - // User has test project rights - // This talks about Default/Global - // - // key: more or less verbose - // value: string present on rights table - $right2check = - array('project_edit' => 'mgt_modify_product', - 'reqs_view' => "mgt_view_req", - 'monitor_req' => "monitor_requirement", - 'req_tcase_link_management' => "req_tcase_link_management", - 'reqs_edit' => "mgt_modify_req", - 'keywords_view' => "mgt_view_key", - 'keyword_assignment' => "keyword_assignment", - 'keywords_edit' => "mgt_modify_key", - 'platform_management' => "platform_management", - 'issuetracker_management' => "issuetracker_management", - 'issuetracker_view' => "issuetracker_view", - 'codetracker_management' => "codetracker_management", - 'codetracker_view' => "codetracker_view", - 'configuration' => "system_configuraton", - 'cfield_management' => 'cfield_management', - 'cfield_view' => 'cfield_view', - 'cfield_assignment' => 'cfield_assignment', - 'usergroups' => "mgt_view_usergroups", - 'view_tc' => "mgt_view_tc", - 'view_testcase_spec' => "mgt_view_tc", - 'project_inventory_view' => 'project_inventory_view', - 'project_inventory_management' => 'project_inventory_management', - 'modify_tc' => 'mgt_modify_tc', - 'exec_edit_notes' => 'exec_edit_notes', 'exec_delete' => 'exec_delete', - 'testplan_unlink_executed_testcases' => 'testplan_unlink_executed_testcases', - 'testproject_delete_executed_testcases' => 'testproject_delete_executed_testcases', - 'exec_ro_access' => 'exec_ro_access'); - if ($forceToNo) { - $grants = array_fill_keys(array_keys($right2check), 'no'); - return $grants; - } - - - $grants['project_edit'] = $user->hasRight($dbHandler,$right2check['project_edit'],$tproject_id); - - /** redirect admin to create testproject if not found */ - if ($grants['project_edit'] && !isset($_SESSION['testprojectID'])) { - redirect($_SESSION['basehref'] . 'lib/project/projectEdit.php?doAction=create'); - exit(); - } - - - foreach($right2check as $humankey => $right) { - $grants[$humankey] = $user->hasRight($dbHandler,$right,$tproject_id); - } - - - // check right ONLY if option is enables - if($_SESSION['testprojectOptions']->inventoryEnabled) { - $invr = array('project_inventory_view','project_inventory_management'); - foreach($invr as $r){ - $grants[$r] = ($user->hasRight($dbHandler,$r,$tproject_id) == 'yes') ? 1 : 0; - } - } - - return $grants; +get_accessible_for_user($user->dbID, + array( + 'output' => 'map_name_with_inactive_mark' + )); +$tprojectQty = $tproject_mgr->getItemCount(); +$userIsBlindFolded = (is_null($accessibleItems) || count($accessibleItems) == 0) && + $tprojectQty > 0; + +if ($userIsBlindFolded) { + $testprojectID = $testplanID = 0; + $_SESSION['testprojectTopMenu'] = ''; +} + +$tplan2check = null; +$currentUser = $_SESSION['currentUser']; +$userID = $currentUser->dbID; + +$gui = new stdClass(); +$gui->grants = getGrants($db, $user, $testprojectID, $userIsBlindFolded); + +$gui->hasTestCases = false; + +if ($gui->grants['view_tc']) { + $gui->hasTestCases = $tproject_mgr->count_testcases($testprojectID) > 0 ? 1 : 0; +} + +$gui->hasKeywords = false; +if ($gui->hasTestCases) { + $gui->hasKeywords = $tproject_mgr->hasKeywords($testprojectID); +} + +// ----- Test Plan Section -------------------------------- +/** + * + * @todo - franciscom - we must understand if these two calls are really needed, + * or is enough just call to getAccessibleTestPlans() + */ +$filters = array( + 'plan_status' => ACTIVE +); +$gui->num_active_tplans = $tproject_mgr->getActiveTestPlansCount($testprojectID); + +// get Test Plans available for the user +$arrPlans = (array) $currentUser->getAccessibleTestPlans($db, $testprojectID); + +if ($testplanID > 0) { + // if this test plan is present on $arrPlans + // OK we will set it on $arrPlans as selected one. + // else + // need to set test plan on session + // + $index = 0; + $found = 0; + $loop2do = count($arrPlans); + for ($idx = 0; $idx < $loop2do; $idx ++) { + if ($arrPlans[$idx]['id'] == $testplanID) { + $found = 1; + $index = $idx; + break; + } + } + if ($found == 0) { + // update test plan id + $index = 0; + $testplanID = $arrPlans[$index]['id']; + } + + setSessionTestPlan($arrPlans[$index]); + $arrPlans[$index]['selected'] = 1; +} + +$gui->testplanRole = null; +if ($testplanID) { + + $rd = null; + // Role can be configured or inherited + if (isset($currentUser->tplanRoles[$testplanID])) { + // Configured + $role = $currentUser->tplanRoles[$testplanID]; + $rd = $role->getDisplayName(); + } else { + if (config_get('testplan_role_inheritance_mode') == 'global') { + $rd = $currentUser->globalRole->name; + } + } + + if (null != $rd) { + $gui->testplanRole = $tlCfg->gui->role_separator_open . $rd . + $tlCfg->gui->role_separator_close; + } +} +$rights2check = array( + 'testplan_execute', + 'testplan_create_build', + 'testplan_metrics', + 'testplan_planning', + 'testplan_user_role_assignment', + 'mgt_testplan_create', + 'cfield_view', + 'cfield_management', + 'testplan_milestone_overview', + 'exec_testcases_assigned_to_me', + 'exec_assign_testcases', + 'exec_ro_access', + 'testplan_add_remove_platforms', + 'testplan_update_linked_testcase_versions', + 'testplan_set_urgent_testcases', + 'testplan_show_testcases_newest_versions' +); + +foreach ($rights2check as $the_right) { + $gui->grants[$the_right] = $userIsBlindFolded ? 'no' : $currentUser->hasRight( + $db, $the_right, $testprojectID, $testplanID); +} + +$gui->grants['tproject_user_role_assignment'] = "no"; +if ($currentUser->hasRight($db, "testproject_user_role_assignment", + $testprojectID, - 1) == "yes" || + $currentUser->hasRight($db, "user_role_assignment", null, - 1) == "yes") { + $gui->grants['tproject_user_role_assignment'] = "yes"; +} + +$gui->url = array( + 'metrics_dashboard' => 'lib/results/metricsDashboard.php', + 'testcase_assignments' => 'lib/testcases/tcAssignedToUser.php' +); +$gui->launcher = 'lib/general/frmWorkArea.php'; +$gui->arrPlans = $arrPlans; +$gui->countPlans = count($gui->arrPlans); + +$gui->testprojectID = $testprojectID; +$gui->testplanID = $testplanID; + +$gui->docs = config_get('userDocOnDesktop') ? getUserDocumentation() : null; + +$secCfg = config_get('config_check_warning_frequence'); +$gui->securityNotes = ''; +if ((strcmp($secCfg, 'ALWAYS') == 0) || + (strcmp($secCfg, 'ONCE_FOR_SESSION') == 0 && + ! isset($_SESSION['getSecurityNotesOnMainPageDone']))) { + $_SESSION['getSecurityNotesOnMainPageDone'] = 1; + $gui->securityNotes = getSecurityNotes($db); +} + +$gui->opt_requirements = isset( + $_SESSION['testprojectOptions']->requirementsEnabled) ? $_SESSION['testprojectOptions']->requirementsEnabled : null; + +$gui->plugins = array(); +foreach (array( + 'EVENT_LEFTMENU_TOP', + 'EVENT_LEFTMENU_BOTTOM', + 'EVENT_RIGHTMENU_TOP', + 'EVENT_RIGHTMENU_BOTTOM' +) as $menu_item) { + # to be compatible with PHP 5.4 + $menu_content = event_signal($menu_item); + if (! empty($menu_content)) { + $gui->plugins[$menu_item] = $menu_content; + } +} + +$tplKey = 'mainPage'; +$tpl = $tplKey . '.tpl'; +$tplCfg = config_get('tpl'); +if (null !== $tplCfg && isset($tplCfg[$tplKey])) { + $tpl = $tplCfg->$tplKey; +} + +$smarty->assign('gui', $gui); +$smarty->display($tpl); + +/** + * Get User Documentation + * based on contribution by Eugenia Drosdezki + * + * @return NULL|string|boolean + */ +function getUserDocumentation() +{ + $target_dir = '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . + 'docs'; + $documents = null; + + if ($handle = opendir($target_dir)) { + while (false !== ($file = readdir($handle))) { + clearstatcache(); + if (($file != ".") && ($file != "..") && + is_file($target_dir . DIRECTORY_SEPARATOR . $file)) { + $documents[] = $file; + } + } + closedir($handle); + } + return $documents; +} + +/** + * Get grants + * + * Returns an array with the corresponding permissions and shares + * + * @param database $dbHandler + * @param tlUser $user + * @param int $tproject_id + * @param boolean $forceToNo + * @return array|number + */ +function getGrants($dbHandler, $user, $tproject_id, $forceToNo = false) +{ + // User has test project rights + // This talks about Default/Global + // + // key: more or less verbose + // value: string present on rights table + $right2check = array( + 'project_edit' => 'mgt_modify_product', + 'reqs_view' => "mgt_view_req", + 'monitor_req' => "monitor_requirement", + 'req_tcase_link_management' => "req_tcase_link_management", + 'reqs_edit' => "mgt_modify_req", + 'keywords_view' => "mgt_view_key", + 'keyword_assignment' => "keyword_assignment", + 'keywords_edit' => "mgt_modify_key", + 'platform_management' => "platform_management", + 'platform_view' => "platform_view", + 'issuetracker_management' => "issuetracker_management", + 'issuetracker_view' => "issuetracker_view", + 'codetracker_management' => "codetracker_management", + 'codetracker_view' => "codetracker_view", + 'configuration' => "system_configuraton", + 'cfield_management' => 'cfield_management', + 'cfield_view' => 'cfield_view', + 'cfield_assignment' => 'cfield_assignment', + 'usergroups' => "mgt_view_usergroups", + 'view_tc' => "mgt_view_tc", + 'view_testcase_spec' => "mgt_view_tc", + 'project_inventory_view' => 'project_inventory_view', + 'project_inventory_management' => 'project_inventory_management', + 'modify_tc' => 'mgt_modify_tc', + 'exec_edit_notes' => 'exec_edit_notes', + 'exec_delete' => 'exec_delete', + 'testplan_unlink_executed_testcases' => 'testplan_unlink_executed_testcases', + 'testproject_delete_executed_testcases' => 'testproject_delete_executed_testcases', + 'exec_ro_access' => 'exec_ro_access' + ); + if ($forceToNo) { + return array_fill_keys(array_keys($right2check), 'no'); + } + + $grants['project_edit'] = $user->hasRight($dbHandler, + $right2check['project_edit'], $tproject_id); + + /** + * redirect admin to create testproject if not found + */ + if ($grants['project_edit'] && ! isset($_SESSION['testprojectID'])) { + redirect( + $_SESSION['basehref'] . 'lib/project/projectEdit.php?doAction=create'); + exit(); + } + + foreach ($right2check as $humankey => $right) { + $grants[$humankey] = $user->hasRight($dbHandler, $right, $tproject_id); + } + + // check right ONLY if option is enables + if ($_SESSION['testprojectOptions']->inventoryEnabled) { + $invr = array( + 'project_inventory_view', + 'project_inventory_management' + ); + foreach ($invr as $r) { + $grants[$r] = ($user->hasRight($dbHandler, $r, $tproject_id) == 'yes') ? 1 : 0; + } + } + + return $grants; } diff --git a/lib/general/navBar.php b/lib/general/navBar.php index 6cf62a7e70..3f07a632b1 100644 --- a/lib/general/navBar.php +++ b/lib/general/navBar.php @@ -1,212 +1,251 @@ -assign('gui',$gui); -$smarty->display('navBar.tpl'); - - -/** - * - */ -function getGrants(&$db,&$userObj) -{ - $grants = new stdClass(); - $grants->view_testcase_spec = $userObj->hasRight($db,"mgt_view_tc"); - return $grants; -} - -/** - * - */ -function init_args(&$dbH) -{ - $iParams = array("testproject" => array(tlInputParameter::INT_N), - "caller" => array(tlInputParameter::STRING_N,1,6), - "viewer" => array(tlInputParameter::STRING_N, 0, 3) - ); - $args = new stdClass(); - $pParams = G_PARAMS($iParams,$args); - - if( is_null($args->viewer) || $args->viewer == '' ) - { - $args->viewer = isset($_SESSION['viewer']) ? $_SESSION['viewer'] : null; - } - - $args->ssodisable = getSSODisable(); - $args->user = $_SESSION['currentUser']; - - // Check if any project exists to display error - $args->newInstallation = false; - if($args->testproject <= 0) { - $sch = tlObject::getDBTables(array('testprojects','nodes_hierarchy')); - $sql = " SELECT NH.id, NH.name FROM {$sch['nodes_hierarchy']} NH " . - " JOIN {$sch['testprojects']} TPRJ " . - " ON TPRJ.id = NH.id "; - $rs = (array)$dbH->get_recordset($sql); - - if(count($rs) == 0) { - $args->newInstallation = true; - } - } - - return $args; -} - -/** - * - */ -function initializeGui(&$db,&$args) { - $tproject_mgr = new testproject($db); - $guiCfg = config_get("gui"); - - $gui = new stdClass(); - - $opx = array('output' => 'map_name_with_inactive_mark', - 'field_set' => $guiCfg->tprojects_combo_format, - 'order_by' => $guiCfg->tprojects_combo_order_by); - - $gui->TestProjects = $tproject_mgr->get_accessible_for_user($args->user->dbID,$opx); - - $gui->TestProjectCount = sizeof($gui->TestProjects); - if($gui->TestProjectCount == 0) { - $gui->TestProjects = null; - } - - $gui->tprojectID = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - $gui->tproject_id = $gui->tprojectID; - - if($gui->tproject_id <= 0 ) { - $ckObj = new stdClass(); - $ckCfg = config_get('cookie'); - - // Try to get from Cookie - $ckObj->name = $ckCfg->testProjectMemory . - intval($_SESSION['userID']); - - if( isset($_COOKIE[$ckObj->name]) ) { - $gui->tproject_id = $gui->tprojectID = intval($_COOKIE[$ckObj->name]); - } - } - - - if($gui->tproject_id <= 0 && !$args->newInstallation) { - // Well instead of this, try to get the firts test project - // user is enabled to. - if( 0 == $gui->TestProjectCount ) { - throw new Exception("Can't work without Test Project ID", 1); - } - $theOne = current(array_keys($gui->TestProjects)); - $gui->tproject_id = $gui->tprojectID = $theOne; - } - - $gui->tcasePrefix = ''; - $gui->searchSize = 8; - $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($gui->tproject_id) . - config_get('testcase_cfg')->glue_character; - $gui->searchSize = tlStringLen($gui->tcasePrefix) + - $guiCfg->dynamic_quick_tcase_search_input_size; - - - - $gui->TestPlanCount = 0; - - $tprojectQty = $tproject_mgr->getItemCount(); - if($gui->TestProjectCount == 0 && $tprojectQty > 0) - { - // User rights configurations does not allow access to ANY test project - $_SESSION['testprojectTopMenu'] = ''; - $gui->tproject_id = 0; - } - - if($gui->tproject_id) { - $testPlanSet = (array)$args->user->getAccessibleTestPlans($db,$gui->tproject_id); - $gui->TestPlanCount = sizeof($testPlanSet); - - $tplanID = isset($_SESSION['testplanID']) ? intval($_SESSION['testplanID']) : null; - if( !is_null($tplanID) ) { - // Need to set this info on session with first Test Plan from $testPlanSet - // if this test plan is present on $testPlanSet - // OK we will set it on $testPlanSet as selected one. - // else - // need to set test plan on session - // - $index=0; - $testPlanFound=0; - $loop2do=count($testPlanSet); - for($idx=0; $idx < $loop2do; $idx++) { - if( $testPlanSet[$idx]['id'] == $tplanID ) { - $testPlanFound = 1; - $index = $idx; - break; - } - } - - if( $testPlanFound == 0 && is_array($testPlanSet) ) { - $tplanID = $testPlanSet[0]['id']; - setSessionTestPlan($testPlanSet[0]); - } - $testPlanSet[$index]['selected']=1; - } - } - - if ($gui->tproject_id && isset($args->user->tprojectRoles[$gui->tproject_id])) { - // test project specific role applied - $role = $args->user->tprojectRoles[$gui->tprojectID]; - $testprojectRole = $role->getDisplayName(); - } else { - // general role applied - $testprojectRole = $args->user->globalRole->getDisplayName(); - } - $gui->whoami = $args->user->getDisplayName() . ' ' . - $guiCfg->role_separator_open . - $testprojectRole . $guiCfg->role_separator_close; - - - // only when the user has changed project using the combo the _GET has this key. - // Use this clue to launch a refresh of other frames present on the screen - // using the onload HTML body attribute - $gui->updateMainPage = 0; - if ($args->testproject) { - - // set test project ID for the next session - $gui->updateMainPage = is_null($args->caller); - - $ckCfg = config_get('cookie'); - $ckObj = new stdClass(); - $ckObj->name = $ckCfg->testProjectMemory . $args->user->dbID; - $ckObj->value = $args->testproject; - tlSetCookie($ckObj); - } - - $gui->grants = getGrants($db,$args->user); - $gui->viewer = $args->viewer; - - $gui->plugins = array(); - foreach(array('EVENT_TITLE_BAR') as $menu_item) { - $menu_content = event_signal($menu_item); - $gui->plugins[$menu_item] = !empty($menu_content) ? $menu_content : null; - } - - $gui->ssodisable = $args->ssodisable; - $sso = ($args->ssodisable ? '&ssodisable' : ''); - $gui->logout = 'logout.php?viewer=' . $sso; - - // to do not break logic - $gui->testprojectID = $gui->tproject_id; - return $gui; +assign('gui', $gui); +$smarty->display('navBar.tpl'); + +/** + * Get grants + * + * @param database $db + * @param tlUser $userObj + * @return stdClass + */ +function getGrants(&$db, &$userObj) +{ + $grants = new stdClass(); + $grants->view_testcase_spec = $userObj->hasRightOnProj($db, "mgt_view_tc"); + return $grants; +} + +/** + * Get input from user and return it in some sort of namespace + * + * @param database $dbH + * @return stdClass object returns the arguments for the page + */ +function initArgs(&$dbH) +{ + $iParams = array( + "testproject" => array( + tlInputParameter::INT_N + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "caller" => array( + tlInputParameter::STRING_N, + 1, + 6 + ), + "viewer" => array( + tlInputParameter::STRING_N, + 0, + 3 + ) + ); + $args = new stdClass(); + G_PARAMS($iParams, $args); + + $args->ssodisable = getSSODisable(); + $args->user = $_SESSION['currentUser']; + + $args->testproject = intval($args->testproject); + $args->tproject_id = intval($args->tproject_id); + + // Check if any project exists to display error + $args->newInstallation = false; + if ($args->testproject <= 0 || $args->tproject_id <= 0) { + $sch = tlObject::getDBTables(array( + 'testprojects', + 'nodes_hierarchy' + )); + $sql = " SELECT NH.id, NH.name + FROM {$sch['nodes_hierarchy']} NH + JOIN {$sch['testprojects']} TPRJ + ON TPRJ.id = NH.id "; + $rs = (array) $dbH->get_recordset($sql); + + if (count($rs) == 0) { + $args->newInstallation = true; + } + } + + return $args; +} + +/** + * Initialize GUI + * + * @param database $db + * @param stdClass $args + * @return stdClass + */ +function initializeGui(&$db, &$args) +{ + $tproject_mgr = new testproject($db); + $guiCfg = config_get("gui"); + + $gui = new stdClass(); + + $opx = array( + 'output' => 'map_name_with_inactive_mark', + 'field_set' => $guiCfg->tprojects_combo_format, + 'order_by' => $guiCfg->tprojects_combo_order_by + ); + + $gui->TestProjects = $tproject_mgr->get_accessible_for_user( + $args->user->dbID, $opx); + + $gui->TestProjectCount = count($gui->TestProjects); + if ($gui->TestProjectCount == 0) { + $gui->TestProjects = null; + } + + if ($args->tproject_id > 0) { + $gui->tprojectID = $args->tproject_id; + } else { + $kiki = 'testprojectID'; + $gui->tprojectID = intval( + isset($_SESSION[$kiki]) ? $_SESSION[$kiki] : 0); + } + $gui->tproject_id = $gui->tprojectID; + + if ($gui->tproject_id <= 0) { + $ckObj = new stdClass(); + $ckCfg = config_get('cookie'); + + // Try to get from Cookie + $ckObj->name = $ckCfg->testProjectMemory . intval($_SESSION['userID']); + + if (isset($_COOKIE[$ckObj->name])) { + $gui->tproject_id = $gui->tprojectID = intval( + $_COOKIE[$ckObj->name]); + } + } + + if ($gui->tproject_id <= 0 && ! $args->newInstallation) { + // Well instead of this, try to get the firts test project + // user is enabled to. + if (0 == $gui->TestProjectCount) { + throw new Exception("Can't work without Test Project ID", 1); + } + $theOne = current(array_keys($gui->TestProjects)); + $gui->tproject_id = $gui->tprojectID = $theOne; + } + + $gui->tcasePrefix = ''; + $gui->searchSize = 8; + $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($gui->tproject_id) . + config_get('testcase_cfg')->glue_character; + $gui->searchSize = tlStringLen($gui->tcasePrefix) + + $guiCfg->dynamic_quick_tcase_search_input_size; + + $gui->TestPlanCount = 0; + + $tprojectQty = $tproject_mgr->getItemCount(); + if ($gui->TestProjectCount == 0 && $tprojectQty > 0) { + // User rights configurations does not allow access to ANY test project + $_SESSION['testprojectTopMenu'] = ''; + $gui->tproject_id = 0; + } + + if ($gui->tproject_id) { + $testPlanSet = (array) $args->user->getAccessibleTestPlans($db, + $gui->tproject_id); + $gui->TestPlanCount = count($testPlanSet); + + $tplanID = isset($_SESSION['testplanID']) ? intval( + $_SESSION['testplanID']) : null; + if (! is_null($tplanID)) { + // Need to set this info on session + // with first Test Plan from $testPlanSet + // if this test plan is present on $testPlanSet + // OK we will set it on $testPlanSet as selected one. + // else + // need to set test plan on session + // + $index = 0; + $testPlanFound = 0; + $loop2do = count($testPlanSet); + for ($idx = 0; $idx < $loop2do; $idx ++) { + if ($testPlanSet[$idx]['id'] == $tplanID) { + $testPlanFound = 1; + $index = $idx; + break; + } + } + + if ($testPlanFound == 0 && is_array($testPlanSet) && + ! empty($testPlanSet)) { + setSessionTestPlan($testPlanSet[0]); + } + $testPlanSet[$index]['selected'] = 1; + } + } + + if ($gui->tproject_id && + isset($args->user->tprojectRoles[$gui->tproject_id])) { + // test project specific role applied + $role = $args->user->tprojectRoles[$gui->tprojectID]; + $testprojectRole = $role->getDisplayName(); + } else { + // general role applied + $testprojectRole = $args->user->globalRole->getDisplayName(); + } + $gui->whoami = $args->user->getDisplayName() . ' ' . + $guiCfg->role_separator_open . $testprojectRole . + $guiCfg->role_separator_close; + + // only when the user has changed project + // using the combo the _GET has this key. + // Use this clue to launch a refresh of other + // frames present on the screen + // using the onload HTML body attribute + $gui->updateMainPage = 0; + // if ($args->testproject > 0 || $args->tproject_id) { + if ($gui->tproject_id > 0) { + // set test project ID for the next session + $gui->updateMainPage = is_null($args->caller); + + $ckCfg = config_get('cookie'); + $ckObj = new stdClass(); + $ckObj->name = $ckCfg->testProjectMemory . $args->user->dbID; + $ckObj->value = $gui->tproject_id; + tlSetCookie($ckObj); + } + + $gui->grants = getGrants($db, $args->user); + $gui->viewer = $args->viewer; + + $gui->plugins = array(); + foreach (array( + 'EVENT_TITLE_BAR' + ) as $menu_item) { + $menu_content = event_signal($menu_item); + $gui->plugins[$menu_item] = ! empty($menu_content) ? $menu_content : null; + } + + $gui->ssodisable = $args->ssodisable; + $sso = ($args->ssodisable ? '&ssodisable' : ''); + $gui->logout = 'logout.php?viewer=' . $sso; + + // to do not break logic + $gui->testprojectID = $gui->tproject_id; + return $gui; } diff --git a/lib/general/show_help.php b/lib/general/show_help.php index bcb29d917e..4f29c192a5 100644 --- a/lib/general/show_help.php +++ b/lib/general/show_help.php @@ -1,41 +1,52 @@ -locale; -$smarty->template_dir = $td; - -$smarty->clear_compiled_tpl($args->help . ".html"); -$smarty->display($args->help . ".html"); - -function init_args() -{ - $iParams = array( - "help" => array(tlInputParameter::STRING_N), - "locale" => array(tlInputParameter::STRING_N,0,10), - ); - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - - return $args; -} +locale; +$smarty->template_dir = $td; + +$smarty->clear_compiled_tpl($args->help . ".html"); +$smarty->display($args->help . ".html"); + +/** + * Initializes the arguments + * + * @return stdClass + */ +function initArgs() +{ + $iParams = array( + "help" => array( + tlInputParameter::STRING_N + ), + "locale" => array( + tlInputParameter::STRING_N, + 0, + 10 + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + + return $args; +} ?> diff --git a/lib/general/staticPage.php b/lib/general/staticPage.php index 81c526474a..388051dbc4 100644 --- a/lib/general/staticPage.php +++ b/lib/general/staticPage.php @@ -1,66 +1,68 @@ -pageTitle = ''; -$gui->pageContent = ''; -$gui->refreshTree = $args->refreshTree; - -$pageKey = htmlspecialchars($args->key); -if ($pageKey == "") -{ - exit ("Error: Invalid page parameter."); -} - -// link appropriate definition file and default to en_GB if not present in the current language -$locale = isset($_SESSION['locale']) ? $_SESSION['locale'] : $tlCfg->default_language; -$language = (file_exists('../../locale/' . $locale . '/texts.php')) ? $locale : 'en_GB'; -include('../../locale/'. $language .'/texts.php'); - -if (isset($TLS_htmltext[$pageKey])) -{ - $gui->pageTitle = $TLS_htmltext_title[$pageKey]; - $gui->pageContent = $TLS_htmltext[$pageKey]; -} -else -{ - $gui->pageContent = "Please, ask administrator to update localization file" . - "(<testlink_root>/locale/$locale/texts.php)" . - " - missing key: " . $pageKey; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display('staticPage.tpl'); - - -/** - * init_args() - * - */ -function init_args() -{ - $iParams = array("key" => array(tlInputParameter::STRING_N), - "refreshTree" => array(tlInputParameter::INT_N)); - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - return $args; -} +pageTitle = ''; +$gui->pageContent = ''; +$gui->refreshTree = $args->refreshTree; + +$pageKey = htmlspecialchars($args->key); +if ($pageKey == "") { + exit("Error: Invalid page parameter."); +} + +// link appropriate definition file and default to en_GB if not present in the current language +$locale = isset($_SESSION['locale']) ? $_SESSION['locale'] : $tlCfg->default_language; +$language = (file_exists('../../locale/' . $locale . '/texts.php')) ? $locale : 'en_GB'; +include_once '../../locale/' . $language . '/texts.php'; + +if (isset($TLS_htmltext[$pageKey])) { + $gui->pageTitle = $TLS_htmltext_title[$pageKey]; + $gui->pageContent = $TLS_htmltext[$pageKey]; +} else { + $gui->pageContent = "Please, ask administrator to update localization file" . + "(<testlink_root>/locale/$locale/texts.php)" . " - missing key: " . + $pageKey; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display('staticPage.tpl'); + +/** + * init_args() + * + * @return stdClass + */ +function initArgs() +{ + $iParams = array( + "key" => array( + tlInputParameter::STRING_N + ), + "refreshTree" => array( + tlInputParameter::INT_N + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + return $args; +} ?> diff --git a/lib/inventory/deleteInventory.php b/lib/inventory/deleteInventory.php index e25b2431fb..d04e8bc1d8 100644 --- a/lib/inventory/deleteInventory.php +++ b/lib/inventory/deleteInventory.php @@ -1,61 +1,68 @@ -hasRight($db,"project_inventory_management")) { - $tlIs = new tlInventory($args->testprojectId, $db); - $data['success'] = $tlIs->deleteInventory($args->machineID); - $data['success'] = ($data['success'] == 1 /*$tlIs->OK*/) ? true : false; - $data['userfeedback'] = $tlIs->getUserFeedback(); -} else { - tLog('User has not rights to set a device!','ERROR'); - $data['userfeedback'] = lang_get('inventory_msg_no_rights'); +hasRight($db, "project_inventory_management")) { + $tlIs = new tlInventory($args->testprojectId, $db); + $data['success'] = $tlIs->deleteInventory($args->machineID); + $data['success'] = ($data['success'] == 1 /* $tlIs->OK */) ? true : false; + $data['userfeedback'] = $tlIs->getUserFeedback(); +} else { + tLog('User has not rights to set a device!', 'ERROR'); + $data['userfeedback'] = lang_get('inventory_msg_no_rights'); +} + +echo json_encode($data); + +/** + * Get input from user and return it in some sort of namespace + * + * @return stdClass object returns the arguments for the page + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $iParams = array( + "machineID" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + // from session + $args->testprojectId = intval($_SESSION['testprojectID']); + $args->userId = intval($_SESSION['userID']); + + return $args; +} + +/** + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "project_inventory_management"); } - -echo json_encode($data); - -/** - * - */ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $iParams = array("machineID" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - // from session - $args->testprojectId = intval($_SESSION['testprojectID']); - $args->userId = intval($_SESSION['userID']); - - return $args; -} - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"project_inventory_management"); -} \ No newline at end of file diff --git a/lib/inventory/getInventory.php b/lib/inventory/getInventory.php index 6cd726bc71..fdcdf2094c 100644 --- a/lib/inventory/getInventory.php +++ b/lib/inventory/getInventory.php @@ -1,35 +1,34 @@ -getAll(); - -$tlUser = new tlUser(intval($_SESSION['userID'])); -$users = $tlUser->getNames($db); - -// fill login instead of user ID -if (!is_null($data)) { - foreach ($data as $k => $v) { - if ($v['owner_id'] != '0') { - $data[$k]['owner'] = $users[$v['owner_id']]['login']; - } else { - $data[$k]['owner'] = ''; - } - } -} -echo json_encode($data); \ No newline at end of file +getAll(); + +$tlUser = new tlUser(intval($_SESSION['userID'])); +$users = $tlUser->getNames($db); + +// fill login instead of user ID +if (! is_null($data)) { + foreach ($data as $k => $v) { + if ($v['owner_id'] != '0') { + $data[$k]['owner'] = $users[$v['owner_id']]['login']; + } else { + $data[$k]['owner'] = ''; + } + } +} +echo json_encode($data); diff --git a/lib/inventory/inventoryView.php b/lib/inventory/inventoryView.php index 3631c843f9..2f7f47265d 100644 --- a/lib/inventory/inventoryView.php +++ b/lib/inventory/inventoryView.php @@ -1,25 +1,24 @@ -rightEdit = has_rights($db,"project_inventory_management"); -$gui->rightView = has_rights($db,"project_inventory_view"); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); \ No newline at end of file +rightEdit = has_rights($db, "project_inventory_management"); +$gui->rightView = has_rights($db, "project_inventory_view"); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); diff --git a/lib/inventory/setInventory.php b/lib/inventory/setInventory.php index 4c5ccb2210..6ca16d55db 100644 --- a/lib/inventory/setInventory.php +++ b/lib/inventory/setInventory.php @@ -1,57 +1,81 @@ -hasRight($db,"project_inventory_management")) { - $tproj_id = intval($_SESSION['testprojectID']); - $tlIs = new tlInventory($tproj_id, $db); - $data['success'] = $tlIs->setInventory($args); - $data['success'] = ($data['success'] == 1 /*$tlIs->OK*/) ? true : false; - $data['userfeedback'] = $tlIs->getUserFeedback(); - $data['record'] = $tlIs->getCurrentData(); +hasRight($db, "project_inventory_management")) { + $tproj_id = intval($_SESSION['testprojectID']); + $tlIs = new tlInventory($tproj_id, $db); + $data['success'] = $tlIs->setInventory($args); + $data['success'] = ($data['success'] == 1 /* $tlIs->OK */) ? true : false; + $data['userfeedback'] = $tlIs->getUserFeedback(); + $data['record'] = $tlIs->getCurrentData(); +} else { + tLog('User has not rights to set a device!', 'ERROR'); + $data['userfeedback'] = lang_get('inventory_msg_no_rights'); +} + +echo json_encode($data); + +/** + * Get input from user and return it in some sort of namespace + * + * @return stdClass object returns the arguments for the page + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $iParams = array( + "machineID" => array( + tlInputParameter::INT_N + ), + "machineOwner" => array( + tlInputParameter::INT_N + ), + "machineName" => array( + tlInputParameter::STRING_N, + 0, + 255 + ), + "machineIp" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "machineNotes" => array( + tlInputParameter::STRING_N, + 0, + 2000 + ), + "machinePurpose" => array( + tlInputParameter::STRING_N, + 0, + 2000 + ), + "machineHw" => array( + tlInputParameter::STRING_N, + 0, + 2000 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + return $args; } -else { - tLog('User has not rights to set a device!','ERROR'); - $data['userfeedback'] = lang_get('inventory_msg_no_rights'); -} - -echo json_encode($data); - -/** - * - */ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $iParams = - array("machineID" => array(tlInputParameter::INT_N), - "machineOwner" => array(tlInputParameter::INT_N), - "machineName" => array(tlInputParameter::STRING_N,0,255), - "machineIp" => array(tlInputParameter::STRING_N,0,50), - "machineNotes" => array(tlInputParameter::STRING_N,0,2000), - "machinePurpose" => array(tlInputParameter::STRING_N,0,2000), - "machineHw" => array(tlInputParameter::STRING_N,0,2000), - ); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - return $args; -} \ No newline at end of file diff --git a/lib/issuetrackerintegration/bugzilladbInterface.class.php b/lib/issuetrackerintegration/bugzilladbInterface.class.php index 9d0409814a..d18c556e1a 100644 --- a/lib/issuetrackerintegration/bugzilladbInterface.class.php +++ b/lib/issuetrackerintegration/bugzilladbInterface.class.php @@ -1,157 +1,153 @@ -connected ) - { - // For bugzilla status code is not important. - // Design Choice make it equal to verbose. Important bugzilla uses UPPERCASE - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = array('code' => 'RESOLVED', 'verbose' => 'RESOLVED'); - $this->defaultResolvedStatus[] = array('code' => 'VERIFIED', 'verbose' => 'VERIFIED'); - $this->defaultResolvedStatus[] = array('code' => 'CLOSED', 'verbose' => 'CLOSED'); - - $this->setResolvedStatusCfg(); - - - $this->interfaceViaDB = true; - $this->guiCfg = array('use_decoration' => true); // add [] on summary - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => false); - } - } - - - - function getissue($id) - { - if (!$this->isConnected()) - { - return null; - } - - $sql = " SELECT bug_id AS id,short_desc AS summary,bug_status AS status" . - " FROM " . ( !is_null($this->cfg->dbschema) ? " {$this->cfg->dbschema}.bugs " : 'bugs') . - " WHERE bug_id = '{$id}' "; - $rs = $this->dbConnection->fetchRowsIntoMap($sql,'id'); - $issue = null; - - if( !is_null($rs) ) - { - $issue = new stdClass(); - - $issue->id = $id; // useful on spreadsheet export - $issue->summary = $rs[$id]['summary']; // useful on spreadsheet export - - $issue->IDHTMLString = "{$id} : "; - $issue->statusCode = $issue->statusVerbose = $rs[$id]['status']; - $issue->statusHTMLString = $this->buildStatusHTMLString($issue->statusVerbose); - $issue->statusColor = isset($this->status_color[$issue->statusVerbose]) ? - $this->status_color[$issue->statusVerbose] : 'white'; - - $issue->summaryHTMLString = $rs[$id]['summary']; - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - - - } - return $issue; - } - - - /** - * Returns the status of the bug with the given id - * this function is not directly called by TestLink. - * - * @return string returns the status of the given bug (if found in the db), or null else - **/ - function getBugStatus($id) - { - if (!$this->isConnected()) - { - return null; - } - $issue = $this->getIssue($id); - - return is_null($issue) ? $issue : $issue->statusVerbose; - } - - - /** - * checks is bug id is present on BTS - * - * @return integer returns 1 if the bug with the given id exists - **/ - function checkBugIDExistence($id) - { - $status_ok = 0; - $issue = $this->getIssue($id); - - return !is_null($issue) ? 1 : 0; - } - - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - function getMyInterface() - { - return $this->cfg->interfacePHP; - } - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - - $template = "\n" . - "\n" . - "DATABASE SERVER NAME\n" . - "DATABASE NAME\n" . - "DATABASE NAME\n" . - "mysql\n" . - "USER\n" . - "PASSWORD\n" . - "http://[bugzillaserver]/bugzilla/\n" . - "http://[bugzillaserver]/bugzilla/show_bug.cgi?id=\n" . - "\n"; - return $template; - } - - -} \ No newline at end of file +connected) { + // For bugzilla status code is not important. + // Design Choice make it equal to verbose. Important bugzilla uses UPPERCASE + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 'RESOLVED', + 'verbose' => 'RESOLVED' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 'VERIFIED', + 'verbose' => 'VERIFIED' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 'CLOSED', + 'verbose' => 'CLOSED' + ); + + $this->setResolvedStatusCfg(); + + $this->interfaceViaDB = true; + $this->guiCfg = array( + 'use_decoration' => true + ); // add [] on summary + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => false + ); + } + } + + public function getissue($id) + { + if (! $this->isConnected()) { + return null; + } + + $sql = " SELECT bug_id AS id,short_desc AS summary,bug_status AS status" . + " FROM " . + (! is_null($this->cfg->dbschema) ? " {$this->cfg->dbschema}.bugs " : 'bugs') . + " WHERE bug_id = '{$id}' "; + $rs = $this->dbConnection->fetchRowsIntoMap($sql, 'id'); + $issue = null; + + if (! is_null($rs)) { + $issue = new stdClass(); + + $issue->id = $id; // useful on spreadsheet export + $issue->summary = $rs[$id]['summary']; // useful on spreadsheet export + + $issue->IDHTMLString = "{$id} : "; + $issue->statusCode = $issue->statusVerbose = $rs[$id]['status']; + $issue->statusHTMLString = $this->buildStatusHTMLString( + $issue->statusVerbose); + $issue->statusColor = isset( + $this->status_color[$issue->statusVerbose]) ? $this->status_color[$issue->statusVerbose] : 'white'; + + $issue->summaryHTMLString = $rs[$id]['summary']; + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + } + return $issue; + } + + /** + * Returns the status of the bug with the given id + * this function is not directly called by TestLink. + * + * @return string returns the status of the given bug (if found in the db), or null else + */ + public function getBugStatus($id) + { + if (! $this->isConnected()) { + return null; + } + $issue = $this->getIssue($id); + + return is_null($issue) ? $issue : $issue->statusVerbose; + } + + /** + * checks is bug id is present on BTS + * + * @return integer returns 1 if the bug with the given id exists + */ + public function checkBugIDExistence($id) + { + $issue = $this->getIssue($id); + + return ! is_null($issue) ? 1 : 0; + } + + /** + * checks id for validity + * + * @param + * string issueID + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public function getMyInterface() + { + return $this->cfg->interfacePHP; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "DATABASE SERVER NAME\n" . + "DATABASE NAME\n" . + "DATABASE NAME\n" . "mysql\n" . + "USER\n" . "PASSWORD\n" . + "http://[bugzillaserver]/bugzilla/\n" . + "http://[bugzillaserver]/bugzilla/show_bug.cgi?id=\n" . + "\n"; + } +} diff --git a/lib/issuetrackerintegration/bugzillaxmlrpcInterface.class.php b/lib/issuetrackerintegration/bugzillaxmlrpcInterface.class.php index 64922032e0..bd63527fc9 100644 --- a/lib/issuetrackerintegration/bugzillaxmlrpcInterface.class.php +++ b/lib/issuetrackerintegration/bugzillaxmlrpcInterface.class.php @@ -1,473 +1,468 @@ -interfaceViaDB = false; - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => false); - $this->guiCfg = array('use_decoration' => true); // add [] on summary - - $this->name = $name; - if( !$this->setCfg($config) ) - { - return false; - } - - $this->completeCfg(); - $this->connect(); - - // For bugzilla status code is not important. - // Design Choice make it equal to verbose. Important bugzilla uses UPPERCASE - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = array('code' => 'RESOLVED', 'verbose' => 'RESOLVED'); - $this->defaultResolvedStatus[] = array('code' => 'VERIFIED', 'verbose' => 'VERIFIED'); - $this->defaultResolvedStatus[] = array('code' => 'CLOSED', 'verbose' => 'CLOSED'); - - $this->setResolvedStatusCfg(); - } - - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/'; // be sure no double // at end - if( !property_exists($this->cfg,'urixmlrpc') ) - { - $this->cfg->urixmlrpc = $base . 'xmlrpc.cgi'; - } - - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'show_bug.cgi?id='; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base; - } - - $this->issueDefaults = array('version' => 'unspecified', 'severity' => 'Trivial', - 'op_sys' => 'All', 'priority' => 'Normal','platform' => "All",); - foreach($this->issueDefaults as $prop => $default) - { - $this->cfg->$prop = (string)(property_exists($this->cfg,$prop) ? $this->cfg->$prop : $default); - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - try - { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $this->createAPIClient(); - $this->connected = true; - } - catch(Exception $e) - { - $logDetails = ''; - foreach(array('uribase','apikey') as $v) - { - $logDetails .= "$v={$this->cfg->$v} / "; - } - $logDetails = trim($logDetails,'/ '); - $this->connected = false; - tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - public function getIssue($issueID) - { - $issue = null; - - $resp = array(); - $login = $this->login(); - $resp = array_merge($resp,(array)$login['response']); - - - $method = 'Bug.get'; - $args = array(array('ids' => array(intval($issueID)), 'permissive' => true)); - if (isset($login['userToken'])) - { - $args[0]['Bugzilla_token'] = $login['userToken']; - } - $resp[$method] = $this->APIClient->call($method, $args); - - - $op = $this->logout($login['userToken']); - $resp = array_merge($resp,(array)$op['response']); - - - if(count($resp['Bug.get']['faults']) == 0) - { - $issue = new stdClass(); - $issue->id = $issueID; - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = $issue->statusVerbose = $resp['Bug.get']['bugs'][0]['status']; - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - - $issue->statusHTMLString = $this->buildStatusHTMLString($issue->statusVerbose); - $issue->summary = $issue->summaryHTMLString = $resp['Bug.get']['bugs'][0]['summary']; - } - else - { - tLog(__METHOD__ . ' :: ' . $resp['Bug.get']['faults'][0]['faultString'], 'ERROR'); - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->statusCode : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - return $this->getIssueStatusCode($issueID); - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) - { - $issue = $this->getIssue($issueID); - $str = $issue->summaryHTMLString; - if($this->guiCfg['use_decoration']) - { - $str = "[" . $str . "] "; - } - return $str; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = is_object($issue) && !is_null($issue); - } - return $status_ok; - } - - - /** - * - * - **/ - function createAPIClient() - { - // echo __METHOD__ .'
    '; - try - { - $this->APIClient = new Zend_XmlRpc_Client((string)$this->cfg->urixmlrpc); - $httpClient = new Zend_Http_Client(); - $httpClient->setCookieJar(); - $this->APIClient->setHttpClient($httpClient); - } - catch(Exception $e) - { - $this->connected = false; - tLog(__METHOD__ . $e->getMessage(), 'ERROR'); - } - } - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $template = "\n" . - "\n" . - "USERNAME\n" . - "PASSWORD\n" . - "http://bugzilla.mozilla.org/\n" . - "\n". - "BUGZILLA PRODUCT\n" . - "BUGZILLA PRODUCT\n" . - "\n". - " \n". - "\n"; - - return $template; - } - - - function getAccessibleProducts() - { - $issue = null; - - $resp = array(); - $login = $this->login(); - $resp = array_merge($resp,(array)$login['response']); - - $method = 'Product.get_accessible_products'; - $args = array(array()); - if (isset($login['userToken'])) - { - $args[0]['Bugzilla_token'] = $login['userToken']; - } - $itemSet = $this->APIClient->call($method, $args); - - $op = $this->logout($login['userToken']); - $resp = array_merge($resp,(array)$op['response']); - - return $itemSet; - } - - /** - * - */ - function getProduct($id) - { - $issue = null; - $resp = array(); - $login = $this->login(); - $resp = array_merge($resp,(array)$login['response']); - - $method = 'Product.get'; - $args = array(array('ids' => array(intval($id)))); - if (isset($login['userToken'])) - { - $args[0]['Bugzilla_token'] = $login['userToken']; - } - $itemSet = $this->APIClient->call($method,$args); - - $op = $this->logout($login['userToken']); - $resp = array_merge($resp,(array)$op['response']); - - return $itemSet; - } - - // good info from: - // http://petehowe.co.uk/2010/example-of-calling-the-bugzilla-api-using-php-zend-framework/ - // - // From BUGZILLA DOCS - // - // Returns - // A hash with one element, id. This is the id of the newly-filed bug. - // - // Errors - // - // 51 (Invalid Object) - // The component you specified is not valid for this Product. - // - // 103 (Invalid Alias) - // The alias you specified is invalid for some reason. See the error message for more details. - // - // 104 (Invalid Field) - // One of the drop-down fields has an invalid value, or a value entered in a text field is too long. - // The error message will have more detail. - // - // 105 (Invalid Component) - // You didn't specify a component. - // - // 106 (Invalid Product) - // Either you didn't specify a product, this product doesn't exist, or you don't have permission - // to enter bugs in this product. - // - // 107 (Invalid Summary) - // You didn't specify a summary for the bug. - // - // 504 (Invalid User) - // Either the QA Contact, Assignee, or CC lists have some invalid user in them. - // The error message will have more details. - // - - - function addIssue($summary,$description) - { - $issue = null; - $resp = array(); - $login = $this->login(); - $resp = array_merge($resp,(array)$login['response']); - - $method = 'Bug.create'; - $issue = array('product' => (string)$this->cfg->product, - 'component' => (string)$this->cfg->component, - 'summary' => $summary, - 'description' => $description); - - - foreach($this->issueDefaults as $prop => $default) - { - $issue[$prop] = (string)$this->cfg->$prop; - } - - $args = array($issue); - if (isset($login['userToken'])) - { - $args[0]['Bugzilla_token'] = $login['userToken']; - } - - - $op = $this->APIClient->call($method,$args); - if( ($op['status_ok'] = ($op['id'] > 0)) ) - { - $op['msg'] = sprintf(lang_get('bugzilla_bug_created'),$summary,$issue['product']); - } - else - { - $msg = "Create BUGZILLA Ticket FAILURE "; - $op= array('status_ok' => false, 'id' => -1, - 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - tLog($msg, 'WARNING'); - } - - $logout = $this->logout($login['userToken']); - $resp = array_merge($resp,(array)$logout['response']); - - return $op; - } - - /** - * - **/ - function canCreateViaAPI() - { - return (property_exists($this->cfg, 'product') && property_exists($this->cfg, 'component')); - } - - - - /** - * - **/ - private function login() - { - $args = array(array('login' => (string)$this->cfg->username, - 'password' => (string)$this->cfg->password,'remember' => 1)); - $ret = array(); - $ret['response']['User.login'] = $this->APIClient->call('User.login', $args); - $ret['userToken'] = $ret['response']['User.login']['token']; - return $ret; - } - - - /** - * - **/ - private function logout($userToken=null) - { - $args = array(array()); - if( !is_null($userToken) ) - { - $args[0]['Bugzilla_token'] = $userToken; - } - - $ret = array(); - $ret['response']['User.logout'] = $this->APIClient->call('User.logout', $args); - return $ret; - } - -} \ No newline at end of file +interfaceViaDB = false; + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => false + ); + $this->guiCfg = array( + 'use_decoration' => true + ); // add [] on summary + + $this->name = $name; + if (! $this->setCfg($config)) { + return false; + } + + $this->completeCfg(); + $this->connect(); + + // For bugzilla status code is not important. + // Design Choice make it equal to verbose. Important bugzilla uses UPPERCASE + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 'RESOLVED', + 'verbose' => 'RESOLVED' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 'VERIFIED', + 'verbose' => 'VERIFIED' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 'CLOSED', + 'verbose' => 'CLOSED' + ); + + $this->setResolvedStatusCfg(); + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; // be sure no double // at end + if (! property_exists($this->cfg, 'urixmlrpc')) { + $this->cfg->urixmlrpc = $base . 'xmlrpc.cgi'; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'show_bug.cgi?id='; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base; + } + + $this->issueDefaults = array( + 'version' => 'unspecified', + 'severity' => 'Trivial', + 'op_sys' => 'All', + 'priority' => 'Normal', + 'platform' => "All" + ); + foreach ($this->issueDefaults as $prop => $default) { + $this->cfg->$prop = (string) (property_exists($this->cfg, $prop) ? $this->cfg->$prop : $default); + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + * + */ + public function connect() + { + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $this->createAPIClient(); + $this->connected = true; + } catch (Exception $e) { + $logDetails = ''; + foreach (array( + 'uribase', + 'apikey' + ) as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getIssue($issueID) + { + $issue = null; + + $resp = array(); + $login = $this->login(); + $resp = array_merge($resp, (array) $login['response']); + + $method = 'Bug.get'; + $args = array( + array( + 'ids' => array( + intval($issueID) + ), + 'permissive' => true + ) + ); + if (isset($login['userToken'])) { + $args[0]['Bugzilla_token'] = $login['userToken']; + } + $resp[$method] = $this->APIClient->call($method, $args); + + $op = $this->logout($login['userToken']); + $resp = array_merge($resp, (array) $op['response']); + + if (count($resp['Bug.get']['faults']) == 0) { + $issue = new stdClass(); + $issue->id = $issueID; + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = $issue->statusVerbose = $resp['Bug.get']['bugs'][0]['status']; + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + + $issue->statusHTMLString = $this->buildStatusHTMLString( + $issue->statusVerbose); + $issue->summary = $issue->summaryHTMLString = $resp['Bug.get']['bugs'][0]['summary']; + } else { + tLog( + __METHOD__ . ' :: ' . + $resp['Bug.get']['faults'][0]['faultString'], 'ERROR'); + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * + * @return string + * + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * + * @param + * string issueID + * @return string + * + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + $str = $issue->summaryHTMLString; + if ($this->guiCfg['use_decoration']) { + $str = "[" . $str . "] "; + } + return $str; + } + + /** + * + * @param + * string issueID + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + /** + */ + private function createAPIClient() + { + try { + $this->APIClient = new Zend_XmlRpc_Client( + (string) $this->cfg->urixmlrpc); + $httpClient = new Zend_Http_Client(); + $httpClient->setCookieJar(); + $this->APIClient->setHttpClient($httpClient); + } catch (Exception $e) { + $this->connected = false; + tLog(__METHOD__ . $e->getMessage(), 'ERROR'); + } + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "USERNAME\n" . "PASSWORD\n" . + "http://bugzilla.mozilla.org/\n" . + "\n" . + "BUGZILLA PRODUCT\n" . + "BUGZILLA PRODUCT\n" . + "\n" . + " \n" . "\n"; + } + + private function getAccessibleProducts() + { + $resp = array(); + $login = $this->login(); + $resp = array_merge($resp, (array) $login['response']); + + $method = 'Product.get_accessible_products'; + $args = array( + array() + ); + if (isset($login['userToken'])) { + $args[0]['Bugzilla_token'] = $login['userToken']; + } + $itemSet = $this->APIClient->call($method, $args); + + $op = $this->logout($login['userToken']); + $resp = array_merge($resp, (array) $op['response']); + + return $itemSet; + } + + /** + */ + private function getProduct($id) + { + $resp = array(); + $login = $this->login(); + $resp = array_merge($resp, (array) $login['response']); + + $method = 'Product.get'; + $args = array( + array( + 'ids' => array( + intval($id) + ) + ) + ); + if (isset($login['userToken'])) { + $args[0]['Bugzilla_token'] = $login['userToken']; + } + $itemSet = $this->APIClient->call($method, $args); + + $op = $this->logout($login['userToken']); + $resp = array_merge($resp, (array) $op['response']); + + return $itemSet; + } + + // good info from: + // http://petehowe.co.uk/2010/example-of-calling-the-bugzilla-api-using-php-zend-framework/ + // + // From BUGZILLA DOCS + // + // Returns + // A hash with one element, id. This is the id of the newly-filed bug. + // + // Errors + // + // 51 (Invalid Object) + // The component you specified is not valid for this Product. + // + // 103 (Invalid Alias) + // The alias you specified is invalid for some reason. See the error message for more details. + // + // 104 (Invalid Field) + // One of the drop-down fields has an invalid value, or a value entered in a text field is too long. + // The error message will have more detail. + // + // 105 (Invalid Component) + // You didn't specify a component. + // + // 106 (Invalid Product) + // Either you didn't specify a product, this product doesn't exist, or you don't have permission + // to enter bugs in this product. + // + // 107 (Invalid Summary) + // You didn't specify a summary for the bug. + // + // 504 (Invalid User) + // Either the QA Contact, Assignee, or CC lists have some invalid user in them. + // The error message will have more details. + // + public function addIssue($summary, $description) + { + $issue = null; + $resp = array(); + $login = $this->login(); + $resp = array_merge($resp, (array) $login['response']); + + $method = 'Bug.create'; + $issue = array( + 'product' => (string) $this->cfg->product, + 'component' => (string) $this->cfg->component, + 'summary' => $summary, + 'description' => $description + ); + + foreach ($this->issueDefaults as $prop => $default) { + $issue[$prop] = (string) $this->cfg->$prop; + } + + $args = array( + $issue + ); + if (isset($login['userToken'])) { + $args[0]['Bugzilla_token'] = $login['userToken']; + } + + $op = $this->APIClient->call($method, $args); + if ($op['status_ok'] = ($op['id'] > 0)) { + $op['msg'] = sprintf(lang_get('bugzilla_bug_created'), $summary, + $issue['product']); + } else { + $msg = "Create BUGZILLA Ticket FAILURE "; + $op = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + tLog($msg, 'WARNING'); + } + + $logout = $this->logout($login['userToken']); + $resp = array_merge($resp, (array) $logout['response']); + + return $op; + } + + /** + */ + public function canCreateViaAPI() + { + return property_exists($this->cfg, 'product') && + property_exists($this->cfg, 'component'); + } + + /** + */ + private function login() + { + $args = array( + array( + 'login' => (string) $this->cfg->username, + 'password' => (string) $this->cfg->password, + 'remember' => 1 + ) + ); + $ret = array(); + $ret['response']['User.login'] = $this->APIClient->call('User.login', + $args); + $ret['userToken'] = $ret['response']['User.login']['token']; + return $ret; + } + + /** + */ + private function logout($userToken = null) + { + $args = array( + array() + ); + if (! is_null($userToken)) { + $args[0]['Bugzilla_token'] = $userToken; + } + + $ret = array(); + $ret['response']['User.logout'] = $this->APIClient->call('User.logout', + $args); + return $ret; + } +} diff --git a/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzilladb.php b/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzilladb.php index 0366a5903a..a22ef49887 100644 --- a/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzilladb.php +++ b/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzilladb.php @@ -1,71 +1,38 @@ - \n" . - "192.168.1.88\n" . - "bugzilla3 \n" . - "bugzilla3\n" . - "mysql \n" . - "root \n" . - "mysqlroot\n" . - "http://192.168.1.88/bugzilla/\n" . - "http://192.168.1.88/bugzilla/show_bug.cgi?id=\n" . - ""; - - - -echo '

    '; -echo "Testing BTS Integration - bugzilladbInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$its = new bugzilladbInterface(185,$cfg); -// var_dump($its); - -$bug=20; -echo '
    ';
    -var_dump($its->getIssue($bug));
    -echo '
    '; - -echo '
    Does issue ' . $bug . ' exist? ' . ($its->checkBugIDExistence($bug) ? 'YES!!!' : 'Oh No!!!'); -echo '
    Does issue 999999 exist? ' . ($its->checkBugIDExistence(999999) ? 'YES!!!' : 'Oh No!!!'); -/* */ - - -/* -if($op) -{ - $issue2check = array( array('issue' => 11776, 'exists' => true), - array('issue' => 99999, 'exists' => false)); - - $methods = array('getBugSummaryString','getBugStatus','getBugStatusString', - 'checkBugID_existence','buildViewBugLink'); - - $if = config_get('bugInterface'); - new dBug($if); - // $xx = $if->getIssue(281579); - - // echo 'Does issue 999999 exist? ' . ($if->checkBugID_existence(999999) ? 'YES!!!' : 'Oh No!!!'); - // echo 'Does issue 281579 exist? ' . ($if->checkBugID_existence(281579) ? 'YES!!!' : 'Oh No!!!'); - - //$xx = $if->getIssue(999999); - //new dBug($xx); - $xx = $if->getBugStatus(281579); - new dBug($xx); - - $xx = $if->getBugSummaryString(281579); - new dBug($xx); - -} -*/ - -?> \ No newline at end of file + \n" . "192.168.1.88\n" . + "bugzilla3 \n" . "bugzilla3\n" . + "mysql \n" . "root \n" . + "mysqlroot\n" . + "http://192.168.1.88/bugzilla/\n" . + "http://192.168.1.88/bugzilla/show_bug.cgi?id=\n" . + ""; + +echo '

    '; +echo "Testing BTS Integration - bugzilladbInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$its = new bugzilladbInterface(185, $cfg); + +$bug = 20; +echo '
    ';
    +var_dump($its->getIssue($bug));
    +echo '
    '; + +echo '
    Does issue ' . $bug . ' exist? ' . + ($its->checkBugIDExistence($bug) ? 'YES!!!' : 'Oh No!!!'); +echo '
    Does issue 999999 exist? ' . + ($its->checkBugIDExistence(999999) ? 'YES!!!' : 'Oh No!!!'); + +?> diff --git a/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzillaxmlrpc.php b/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzillaxmlrpc.php index a797439b7e..2b661114ee 100644 --- a/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzillaxmlrpc.php +++ b/lib/issuetrackerintegration/code_testing/bugzilla/test.bugzillaxmlrpc.php @@ -1,58 +1,28 @@ -\n" . - "testlink.helpme@gmail.com\n" . - "testlink.helpme\n" . - "http://bugzilla.mozilla.org/\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - bugzillaxmlrpcInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$its = new bugzillaxmlrpcInterface(185,$cfg); -// var_dump($its); -echo '
    Does issue 281579 exist? ' . ($its->checkBugIDExistence(281579) ? 'YES!!!' : 'Oh No!!!'); -echo '
    Does issue 999999 exist? ' . ($its->checkBugIDExistence(999999) ? 'YES!!!' : 'Oh No!!!'); - - - -/* -if($op) -{ - $issue2check = array( array('issue' => 11776, 'exists' => true), - array('issue' => 99999, 'exists' => false)); - - $methods = array('getBugSummaryString','getBugStatus','getBugStatusString', - 'checkBugID_existence','buildViewBugLink'); - - $if = config_get('bugInterface'); - new dBug($if); - // $xx = $if->getIssue(281579); - - // echo 'Does issue 999999 exist? ' . ($if->checkBugID_existence(999999) ? 'YES!!!' : 'Oh No!!!'); - // echo 'Does issue 281579 exist? ' . ($if->checkBugID_existence(281579) ? 'YES!!!' : 'Oh No!!!'); - - //$xx = $if->getIssue(999999); - //new dBug($xx); - $xx = $if->getBugStatus(281579); - new dBug($xx); - - $xx = $if->getBugSummaryString(281579); - new dBug($xx); - -} -*/ - -?> \ No newline at end of file +\n" . "testlink.helpme@gmail.com\n" . + "testlink.helpme\n" . + "http://bugzilla.mozilla.org/\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - bugzillaxmlrpcInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$its = new bugzillaxmlrpcInterface(185, $cfg); +echo '
    Does issue 281579 exist? ' . + ($its->checkBugIDExistence(281579) ? 'YES!!!' : 'Oh No!!!'); +echo '
    Does issue 999999 exist? ' . + ($its->checkBugIDExistence(999999) ? 'YES!!!' : 'Oh No!!!'); + +?> diff --git a/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugz.api.php b/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugz.api.php index 43e2f3bfeb..1826addde6 100644 --- a/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugz.api.php +++ b/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugz.api.php @@ -1,49 +1,36 @@ -logon(); - - // You can call any FogBugz API method directly by using it's - // name as a method name on the $fogbugz object. - // It will turn the method name in to the command, - // ?cmd={method_name} and it will add the array to the - // get request automatically - - /* - $xml = $fogbugz->listProjects(); - foreach ($xml->projects->children() as $item) - { - print "Project:" . (string)$item->sProject; - print "
    "; - var_dump($item); - print "
    ======================
    "; - } - */ - - // Go for an issue - $xml = $fogbugz->search(array('q' => 3,'cols' => 'sTitle,sStatus')); - //$xml = $fogbugz->search(array('q' => 3)); - - echo (string)$xml->description . '
    '; - echo (int)$xml->cases['count'] . '
    '; - // var_dump($xml->cases); - foreach($xml->cases->children() as $item) - { - echo (int)$item['ixBug'] . '
    '; - echo (string)$item->sTitle . '
    '; - echo (string)$item->sStatus . '
    '; - } - -} -catch (Exception $e) -{ - print sprintf("FogBugz Error : [Code %d] %s\n",$e->getCode(),$e->getMessage()); -} -?> \ No newline at end of file +logon(); + + // You can call any FogBugz API method directly by using it's + // name as a method name on the $fogbugz object. + // It will turn the method name in to the command, + // ?cmd={method_name} and it will add the array to the + // get request automatically + + // Go for an issue + $xml = $fogbugz->search(array( + 'q' => 3, + 'cols' => 'sTitle,sStatus' + )); + + echo (string) $xml->description . '
    '; + echo (int) $xml->cases['count'] . '
    '; + foreach ($xml->cases->children() as $item) { + echo (int) $item['ixBug'] . '
    '; + echo (string) $item->sTitle . '
    '; + echo (string) $item->sStatus . '
    '; + } +} catch (Exception $e) { + print + sprintf("FogBugz Error : [Code %d] %s\n", $e->getCode(), + $e->getMessage()); +} +?> diff --git a/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugzrestInterface.class.php b/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugzrestInterface.class.php index e9db6e33a1..d6de6da067 100644 --- a/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugzrestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/fogbugz/test.fogbugzrestInterface.class.php @@ -1,68 +1,48 @@ -getTypes(); - -$cfg = "\n" . - "francisco.mancardi@gmail.com\n" . - "qazwsxedc\n" . - "https://fman.fogbugz.com/\n" . - "TestLink Testing\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - fogbugzrestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$its = new fogbugzrestInterface(18,$cfg); -echo '
    ' . __FILE__ . '
    '; -echo '
    Dumping INTERFACE OBJECT
    '; -echo '
    ';
    -var_dump($its);
    -echo '
    '; - -$xx = $its->getCfg(); -//var_dump($xx); -// var_dump('<pre>' . $xx->asXML() . '</pre>'); - -if( $its->isConnected() ) -{ - $xx = $its->getIssue(3); - - echo '
    ' . __FILE__ . '
    '; - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - /* - $xx = $its->addIssue('ISSUE FROM PHP', 'Que miras bolu'); - echo '
    ' . __FILE__ . '
    '; - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - */ - -} - -/* -$xx->uriview = $xx->uribase . 'ffffff'; -var_dump($xx); -var_dump('<pre>' . $xx->asXML() . '</pre>'); -*/ - -?> \ No newline at end of file +getTypes(); + +$cfg = "\n" . "francisco.mancardi@gmail.com\n" . + "qazwsxedc\n" . + "https://fman.fogbugz.com/\n" . + "TestLink Testing\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - fogbugzrestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$its = new fogbugzrestInterface(18, $cfg); +echo '
    ' . __FILE__ . '
    '; +echo '
    Dumping INTERFACE OBJECT
    '; +echo '
    ';
    +var_dump($its);
    +echo '
    '; + +$xx = $its->getCfg(); + +if ($its->isConnected()) { + $xx = $its->getIssue(3); + + echo '
    ' . __FILE__ . '
    '; + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; +} + +?> diff --git a/lib/issuetrackerintegration/code_testing/gforge/test.gforgesoapInterface.class.php b/lib/issuetrackerintegration/code_testing/gforge/test.gforgesoapInterface.class.php index 790fd787c4..5faf6da6c0 100644 --- a/lib/issuetrackerintegration/code_testing/gforge/test.gforgesoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/gforge/test.gforgesoapInterface.class.php @@ -1,46 +1,41 @@ -getTypes(); - -$cfg = "\n" . - "testlink.api\n" . - "testlinkapi\n" . - "http://gforge.com/\n" . - "http://gforge.com/gf/xmlcompatibility/soap5/?wsdl\n" . - "http://gforge.com/\n" . - "http://gforge.com/\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - gforgesoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$its = new gforgesoapInterface(1,$cfg); -var_dump($its); - -if( $its->isConnected() ) -{ - echo 'Connected !
    '; - // $issue=$its->getIssue(7091); - $issue=$its->getIssue(8305); - - new dBug($issue); - -} -?> \ No newline at end of file +getTypes(); + +$cfg = "\n" . "testlink.api\n" . + "testlinkapi\n" . + "http://gforge.com/\n" . + "http://gforge.com/gf/xmlcompatibility/soap5/?wsdl\n" . + "http://gforge.com/\n" . + "http://gforge.com/\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - gforgesoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$its = new gforgesoapInterface(1, $cfg); +var_dump($its); + +if ($its->isConnected()) { + echo 'Connected !
    '; + $issue = $its->getIssue(8305); + + new dBug($issue); +} +?> diff --git a/lib/issuetrackerintegration/code_testing/gitlab/test.gitlabInterface.class.php b/lib/issuetrackerintegration/code_testing/gitlab/test.gitlabInterface.class.php index 83f45e0345..ca02640df3 100644 --- a/lib/issuetrackerintegration/code_testing/gitlab/test.gitlabInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/gitlab/test.gitlabInterface.class.php @@ -1,61 +1,60 @@ -\n" . - "REPLACE_ME\n". - "https://gitlab.com\n". - "REPLACE_ME\n". - "\n"; - -echo '

    '; -echo "Testing BST Integration - gitlabrestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$its = new gitlabrestInterface(18,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -{ - $today = date("Y-m-d H:i:s"); - $issue = array('summary' => 'Issue Via API' . $today,'description' => 'Some text'); - $resp = $its->addIssue($issue['summary'],$issue['description']); - echo '
    ' . __FILE__ . '
    '; - echo '
    ';
    -  var_dump($resp);
    -  echo '
    '; -} - -if( $its->isConnected() ) -{ - $resp = $its->getIssue(1); - - echo '
    ' . __FILE__ . '
    '; - echo '
    ';
    -  var_dump($resp);
    -  echo '
    '; - -} - +\n" . "REPLACE_ME\n" . + "https://gitlab.com\n" . + "REPLACE_ME\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - gitlabrestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$its = new gitlabrestInterface(18, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +{ + $today = date("Y-m-d H:i:s"); + $issue = array( + 'summary' => 'Issue Via API' . $today, + 'description' => 'Some text' + ); + $resp = $its->addIssue($issue['summary'], $issue['description']); + echo '
    ' . __FILE__ . '
    '; + echo '
    ';
    +    var_dump($resp);
    +    echo '
    '; +} + +if ($its->isConnected()) { + $resp = $its->getIssue(1); + + echo '
    ' . __FILE__ . '
    '; + echo '
    ';
    +    var_dump($resp);
    +    echo '
    '; +} + ?> diff --git a/lib/issuetrackerintegration/code_testing/jira/db/test.jiradbInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/db/test.jiradbInterface.class.php index 54c8801609..8f42b0bbe5 100644 --- a/lib/issuetrackerintegration/code_testing/jira/db/test.jiradbInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/db/test.jiradbInterface.class.php @@ -1,70 +1,58 @@ -getTypes(); -$systems = $it_mgr->getSystems(); -new dBug($itt); -new dBug($systems); - - - -// last test ok: -$cfg = "\n" . - "192.168.1.201\n" . - "jiradb\n" . - "mysql\n" . - "root\n" . - "mysqlroot\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "\n"; - -echo '

    '; -echo "Testing BTS Integration - jiradbInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -// $safe_cfg = str_replace("\n",'',$cfg); -// echo $safe_cfg; -echo 'Creating INTERFACE
    '; - -// @20121215 -> 6 => jiradbInterface -$its = new jiradbInterface(6,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - echo 'Connected !
    '; - - echo '
    ';
    -	var_dump($its->getStatusDomain());
    -	echo '
    '; - echo 'Get Issue
    '; - new dBug($its->getIssue('DEMO-2')); - - echo 'Get Issue Summary
    '; - - echo($its->getIssueSummary('DEMO-2')); - echo '
    '; - - // echo($its->getIssueSummary('ZOFF-8')); - -} -?> \ No newline at end of file +getTypes(); +$systems = $it_mgr->getSystems(); +new dBug($itt); +new dBug($systems); + +// last test ok: +$cfg = "\n" . "192.168.1.201\n" . + "jiradb\n" . "mysql\n" . + "root\n" . "mysqlroot\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/browse/\n" . + "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + "\n"; + +echo '

    '; +echo "Testing BTS Integration - jiradbInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; +echo 'Creating INTERFACE
    '; + +// @20121215 -> 6 => jiradbInterface +$its = new jiradbInterface(6, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + echo 'Connected !
    '; + + echo '
    ';
    +    var_dump($its->getStatusDomain());
    +    echo '
    '; + echo 'Get Issue
    '; + new dBug($its->getIssue('DEMO-2')); + + echo 'Get Issue Summary
    '; + + echo $its->getIssueSummary('DEMO-2'); + echo '
    '; +} +?> diff --git a/lib/issuetrackerintegration/code_testing/jira/rest/test.addIssueComment.jiraOnDemand.jirarestInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/rest/test.addIssueComment.jiraOnDemand.jirarestInterface.class.php index 1dffc692a9..0f8c2bf82d 100644 --- a/lib/issuetrackerintegration/code_testing/jira/rest/test.addIssueComment.jiraOnDemand.jirarestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/rest/test.addIssueComment.jiraOnDemand.jirarestInterface.class.php @@ -1,70 +1,60 @@ -getTypes(); - -// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin -$username = 'testlink.forum'; -$password = 'forum'; -$uribase = 'https://testlink.atlassian.net/'; -$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; -$projectkey = 'ZOFF'; - - -$cfg = "\n" . - "{$username}\n" . - "{$password}\n" . - "{$uribase}\n" . - "{$uriapi}\n" . - "{$projectkey}\n" . - "1\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirarestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirarestInterface(7,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - // RAW - // ATTENTION interface IS COMPLETELY DIFFERENT - $issueID = 'ZOFF-1337'; - $noteText = 'I want to rock'; - $zorro = $its->getAPIClient()->addComment($noteText,$issueID); - echo 'Test - ADD an ISSUE Comment VIA REST RAW
    '; - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - - // using TestLink Interface -$issueID = 'ZOFF-1337'; - $noteText = 'I want to rock VIA INTERFACE'; - $zorro = $its->addNote($issueID,$noteText); - echo 'Test - ADD an ISSUE Comment VIA REST RAW
    '; - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - - +getTypes(); + +// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin +$username = 'testlink.forum'; +$password = 'forum'; +$uribase = 'https://testlink.atlassian.net/'; +$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; +$projectkey = 'ZOFF'; + +$cfg = "\n" . "{$username}\n" . + "{$password}\n" . "{$uribase}\n" . + "{$uriapi}\n" . "{$projectkey}\n" . + "1\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - jirarestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirarestInterface(7, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + // RAW + // ATTENTION interface IS COMPLETELY DIFFERENT + $issueID = 'ZOFF-1337'; + $noteText = 'I want to rock'; + $zorro = $its->getAPIClient()->addComment($noteText, $issueID); + echo 'Test - ADD an ISSUE Comment VIA REST RAW
    '; + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; + + // using TestLink Interface + $issueID = 'ZOFF-1337'; + $noteText = 'I want to rock VIA INTERFACE'; + $zorro = $its->addNote($issueID, $noteText); + echo 'Test - ADD an ISSUE Comment VIA REST RAW
    '; + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; } diff --git a/lib/issuetrackerintegration/code_testing/jira/rest/test.createIssue.jiraOnDemand.jirarestInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/rest/test.createIssue.jiraOnDemand.jirarestInterface.class.php index 4b01ea5cc4..e09122754e 100644 --- a/lib/issuetrackerintegration/code_testing/jira/rest/test.createIssue.jiraOnDemand.jirarestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/rest/test.createIssue.jiraOnDemand.jirarestInterface.class.php @@ -1,77 +1,72 @@ -getTypes(); - -// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin -$username = 'testlink.forum'; -$password = 'forum'; -$uribase = 'https://testlink.atlassian.net/'; -$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; -$projectkey = 'ZOFF'; - - -$cfg = "\n" . - "{$username}\n" . - "{$password}\n" . - "{$uribase}\n" . - "{$uriapi}\n" . - "{$projectkey}\n" . - "1\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirarestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirarestInterface(7,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - - $summary = 'Will try to create via REST RAW'; - $description = 'I WAS ABLE to create via REST RAW!!!'; - $issue = array('fields' => - array('project' => array('key' => (string)$projectkey), - 'summary' => $summary, - 'description' => $description, - 'issuetype' => array( 'id' => 1) - ) - ); - - $zorro = $its->getAPIClient()->createIssue($issue); - echo 'Test - Create an ISSUE VIA REST RAW
    '; - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - // ==================================================================== - $summary = 'Will try to create via REST TestLink Interface'; - $description = 'I WAS ABLE to create via REST TestLink Interface ****'; - $zorro = $its->addIssue($summary,$description); - echo 'Test - Create an ISSUE VIA REST TestLink Interface
    '; - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - - - +getTypes(); + +// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin +$username = 'testlink.forum'; +$password = 'forum'; +$uribase = 'https://testlink.atlassian.net/'; +$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; +$projectkey = 'ZOFF'; + +$cfg = "\n" . "{$username}\n" . + "{$password}\n" . "{$uribase}\n" . + "{$uriapi}\n" . "{$projectkey}\n" . + "1\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - jirarestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirarestInterface(7, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + + $summary = 'Will try to create via REST RAW'; + $description = 'I WAS ABLE to create via REST RAW!!!'; + $issue = array( + 'fields' => array( + 'project' => array( + 'key' => (string) $projectkey + ), + 'summary' => $summary, + 'description' => $description, + 'issuetype' => array( + 'id' => 1 + ) + ) + ); + + $zorro = $its->getAPIClient()->createIssue($issue); + echo 'Test - Create an ISSUE VIA REST RAW
    '; + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; + + // ==================================================================== + $summary = 'Will try to create via REST TestLink Interface'; + $description = 'I WAS ABLE to create via REST TestLink Interface ****'; + $zorro = $its->addIssue($summary, $description); + echo 'Test - Create an ISSUE VIA REST TestLink Interface
    '; + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; } diff --git a/lib/issuetrackerintegration/code_testing/jira/rest/test.getIssue.jiraOnDemand.jirarestInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/rest/test.getIssue.jiraOnDemand.jirarestInterface.class.php index 885050545c..6009cbce8f 100644 --- a/lib/issuetrackerintegration/code_testing/jira/rest/test.getIssue.jiraOnDemand.jirarestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/rest/test.getIssue.jiraOnDemand.jirarestInterface.class.php @@ -1,76 +1,48 @@ -getTypes(); - -// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin -$username = 'testlink.forum'; -$password = 'forum'; -// $password = ''; -$uribase = 'https://testlink.atlassian.net/'; -$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; - -$cfg = "\n" . - "{$username}\n" . - "{$password}\n" . - "{$uribase}\n" . - "{$uriapi}\n" . - "ZOFF\n" . - "1\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirarestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirarestInterface(7,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - // Using RAW API - - /* - $api = $its->getAPIClient(); - $zorro = $its->getAPIClient()->getUser($username); - echo 'Test - Get Data about connected user
    '; - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - - $targetIssue = 'ZOFF-129'; - echo 'Test - Get Data about Issue:' . $targetIssue . '
    '; - $zorro = $its->getAPIClient()->getIssue($targetIssue); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - */ - - $targetIssue = 'ZOFF-185'; - echo 'Test - USING TL Interface - Get Data about Issue:' . $targetIssue . '
    '; - $zorro = $its->getIssue($targetIssue); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - - - +getTypes(); + +// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin +$username = 'testlink.forum'; +$password = 'forum'; +$uribase = 'https://testlink.atlassian.net/'; +$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; + +$cfg = "\n" . "{$username}\n" . + "{$password}\n" . "{$uribase}\n" . + "{$uriapi}\n" . "ZOFF\n" . + "1\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - jirarestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirarestInterface(7, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + $targetIssue = 'ZOFF-185'; + echo 'Test - USING TL Interface - Get Data about Issue:' . $targetIssue . + '
    '; + $zorro = $its->getIssue($targetIssue); + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; } diff --git a/lib/issuetrackerintegration/code_testing/jira/rest/test.getMetaData.jiraOnDemand.jirarestInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/rest/test.getMetaData.jiraOnDemand.jirarestInterface.class.php index 2596047027..463fa70ba1 100644 --- a/lib/issuetrackerintegration/code_testing/jira/rest/test.getMetaData.jiraOnDemand.jirarestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/rest/test.getMetaData.jiraOnDemand.jirarestInterface.class.php @@ -1,100 +1,68 @@ -getTypes(); - -// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin -$username = 'testlink.forum'; -$password = 'forum'; -// $password = ''; -$uribase = 'https://testlink.atlassian.net/'; -$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; - -$cfg = "\n" . - "{$username}\n" . - "{$password}\n" . - "{$uribase}\n" . - "{$uriapi}\n" . - "ZOFF\n" . - "1\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirarestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirarestInterface(7,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - // Using RAW API - - /* - $api = $its->getAPIClient(); - $zorro = $its->getAPIClient()->getUser($username); - echo 'Test - Get Data about connected user
    '; - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - - $targetIssue = 'ZOFF-129'; - echo 'Test - Get Data about Issue:' . $targetIssue . '
    '; - $zorro = $its->getAPIClient()->getIssue($targetIssue); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - */ - // $api = $its->getAPIClient(); - - - $zorro = $its->getIssueTypes(); - echo '
    ';
    -  echo 'ISSUE TYPES
    '; - var_dump($zorro); - echo '
    '; - - $zorro = $its->getIssueTypesForHTMLSelect(); - echo '
    ';
    -  echo 'ISSUE TYPES
    '; - var_dump($zorro); - echo '
    '; - - - - $zorro = $its->getPriorities(); - echo '
    ';
    -  echo 'getPriorities
    '; - var_dump($zorro); - echo '
    '; - - - $zorro = $its->getVersions('ZOFF'); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - $zorro = $its->getComponents('ZOFF'); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; - - +getTypes(); + +// http://testlink.atlassian.net/rest/api/latest/user/search/?username=admin +$username = 'testlink.forum'; +$password = 'forum'; +$uribase = 'https://testlink.atlassian.net/'; +$uriapi = 'https://testlink.atlassian.net/rest/api/latest/'; + +$cfg = "\n" . "{$username}\n" . + "{$password}\n" . "{$uribase}\n" . + "{$uriapi}\n" . "ZOFF\n" . + "1\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - jirarestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirarestInterface(7, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + $zorro = $its->getIssueTypes(); + echo '
    ';
    +    echo 'ISSUE TYPES
    '; + var_dump($zorro); + echo '
    '; + + $zorro = $its->getIssueTypesForHTMLSelect(); + echo '
    ';
    +    echo 'ISSUE TYPES
    '; + var_dump($zorro); + echo '
    '; + + $zorro = $its->getPriorities(); + echo '
    ';
    +    echo 'getPriorities
    '; + var_dump($zorro); + echo '
    '; + + $zorro = $its->getVersions('ZOFF'); + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; + + $zorro = $its->getComponents('ZOFF'); + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; } diff --git a/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssue.jiraOnDemand.jirasoapInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssue.jiraOnDemand.jirasoapInterface.class.php index c978c91d32..c8484a3609 100644 --- a/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssue.jiraOnDemand.jirasoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssue.jiraOnDemand.jirasoapInterface.class.php @@ -1,54 +1,52 @@ -getTypes(); - -// 10101 -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "ZOFF\n" . - "1\n" . - - "1010010101\n" . - "\n" . - - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirasoapInterface 'Do Androids Dream of Electric Sheep?' "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirasoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - $today = date("Y-m-d H:i:s"); - $issue = array('summary' => 'Issue Via API' . $today,'description' => 'Do Androids Dream of Electric Sheep?'); - $zorro = $its->addIssue($issue['summary'],$issue['description']); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; +getTypes(); + +// 10101 +$cfg = "\n" . "testlink.forum\n" . + "forum\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + "http://testlink.atlassian.net/browse/\n" . + "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + "ZOFF\n" . "1\n" . + + "1010010101\n" . + "\n" . + "\n"; + +echo '

    '; +echo "Testing BST Integration - jirasoapInterface 'Do Androids Dream of Electric Sheep?' "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirasoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + $today = date("Y-m-d H:i:s"); + $issue = array( + 'summary' => 'Issue Via API' . $today, + 'description' => 'Do Androids Dream of Electric Sheep?' + ); + $zorro = $its->addIssue($issue['summary'], $issue['description']); + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; } diff --git a/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithAffectedVersions.jiraOnDemand.jirasoapInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithAffectedVersions.jiraOnDemand.jirasoapInterface.class.php index 88692a1144..6861045940 100644 --- a/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithAffectedVersions.jiraOnDemand.jirasoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithAffectedVersions.jiraOnDemand.jirasoapInterface.class.php @@ -1,64 +1,59 @@ -getTypes(); - -// 10101 -$oneAffectedVersionSimpleValue = - "2.0 Fast Track\n" . - "1.5 Beta\n"; - - -$oneAffectedVersionSimpleValue = - "10000\n" . - "10002\n"; - -$af = $oneAffectedVersionSimpleValue; - - -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "ZOFF\n" . - "1\n" . - "1010010101\n" . - "\n" . $af . "\n" . - "\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirasoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirasoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - $today = date("Y-m-d H:i:s"); - $issue = array('summary' => 'Issue Via API' . $today,'description' => 'Do Androids Dream of Electric Sheep?'); - $zorro = $its->addIssue($issue['summary'],$issue['description']); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; -} \ No newline at end of file +getTypes(); + +// 10101 +$oneAffectedVersionSimpleValue = "2.0 Fast Track\n" . + "1.5 Beta\n"; + +$oneAffectedVersionSimpleValue = "10000\n" . + "10002\n"; + +$af = $oneAffectedVersionSimpleValue; + +$cfg = "\n" . "testlink.forum\n" . + "forum\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + "http://testlink.atlassian.net/browse/\n" . + "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + "ZOFF\n" . "1\n" . + "1010010101\n" . + "\n" . $af . "\n" . "\n" . + "\n"; + +echo '

    '; +echo "Testing BST Integration - jirasoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirasoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + $today = date("Y-m-d H:i:s"); + $issue = array( + 'summary' => 'Issue Via API' . $today, + 'description' => 'Do Androids Dream of Electric Sheep?' + ); + $zorro = $its->addIssue($issue['summary'], $issue['description']); + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; +} diff --git a/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithCustomFields.jiraOnDemand.jirasoapInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithCustomFields.jiraOnDemand.jirasoapInterface.class.php index 41cddc6331..d0a97f4d3f 100644 --- a/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithCustomFields.jiraOnDemand.jirasoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/soap/test.createIssueWithCustomFields.jiraOnDemand.jirasoapInterface.class.php @@ -1,142 +1,111 @@ -getTypes(); - -// 10101 -$oneCFSimpleValue = "\n" . - "\n" . - "customfield_10800\n" . - "\n" . - "12345\n" . - "\n" . - "\n"; - -$oneCFMultiListValue = "\n" . - "\n" . - "customfield_10900\n" . - "\n" . - "DucatiYamaha Factory Racing\n" . - "\n"; - - -$oneCFMultiListValue = "\n" . - "\n" . - "customfield_10900\n" . - "\n" . - "DucatiYamaha Factory Racing\n" . - "\n"; - -$cfmod03 = "\n" . - "\n" . - "customfield_10800\n" . - "\n" . - "111\n" . - "\n" . - - "\n" . // <<<< PAY ATTENTION - "\n" . - "customfield_10900\n" . - "\n" . - "Ducati\n" . - "\n"; - -// ELEM IN INPUTobject(SimpleXMLElement)#81 (1) { ["customField"]=> array(2) { [0]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(3) "111" } } [1]=> object(SimpleXMLElement)#79 (2) { ["customfieldId"]=> string(19) " customfield_10900 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(6) "Ducati" } } } } -// getCustomFieldsAttribute -// AFTER get obj vars ACCESS IN INPUT -// array(2) { [0]=> object(SimpleXMLElement)#79 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(3) "111" } } [1]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10900 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(6) "Ducati" } } } -// IO SONO ITEM -// object(SimpleXMLElement)#79 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(3) "111" } } ID: - - -// ELEM IN INPUTobject(SimpleXMLElement)#81 (1) { ["customField"]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } } -// getCustomFieldsAttribute -// AFTER get obj vars ACCESS IN INPUT -// object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } -//IO SONO ITEM -//object(SimpleXMLElement)#82 (0) { } ID: - -//AFTER get obj vars ACCESS IN INPUT -//array(1) { [0]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } } -//IO SONO ITEM -//object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } ID: - - -$cfmod04 = "\n" . - "\n" . - "customfield_10800\n" . - "\n" . - "111\n" . - "\n"; - -$oneOneCFMultiListValue = "\n" . - "\n" . - "customfield_10900\n" . - "\n" . - "Yamaha Factory Racing\n" . - "\n"; - -$cfmod05 = "\n" . - "\n" . - "customfield_10900\n" . - "\n" . - "Ducati\n" . - "\n"; - - -$cfmod06 = "\n" . - "\n" . - "customfield_10800\n" . - "\n" . - "111\n" . - "\n"; - -$cf = $oneCFMultiListValue; - - -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "ZOFF\n" . - "1\n" . - "1010010101\n" . - "\n" . $cf . "\n" . - "\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirasoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirasoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - $today = date("Y-m-d H:i:s"); - $issue = array('summary' => 'Issue Via API' . $today,'description' => 'Do Androids Dream of Electric Sheep?'); - $zorro = $its->addIssue($issue['summary'],$issue['description']); - echo '
    ';
    -  var_dump($zorro);
    -  echo '
    '; +getTypes(); + +// 10101 +$oneCFSimpleValue = "\n" . "\n" . + "customfield_10800\n" . "\n" . + "12345\n" . "\n" . + "\n"; + +$oneCFMultiListValue = "\n" . "\n" . + "customfield_10900\n" . "\n" . + "DucatiYamaha Factory Racing\n" . + "\n"; + +$oneCFMultiListValue = "\n" . "\n" . + "customfield_10900\n" . "\n" . + "DucatiYamaha Factory Racing\n" . + "\n"; + +$cfmod03 = "\n" . "\n" . "customfield_10800\n" . + "\n" . "111\n" . + "\n" . + "\n" . // <<<< PAY ATTENTION + "\n" . "customfield_10900\n" . "\n" . + "Ducati\n" . "\n"; + +// ELEM IN INPUTobject(SimpleXMLElement)#81 (1) { ["customField"]=> array(2) { [0]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(3) "111" } } [1]=> object(SimpleXMLElement)#79 (2) { ["customfieldId"]=> string(19) " customfield_10900 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(6) "Ducati" } } } } +// getCustomFieldsAttribute +// AFTER get obj vars ACCESS IN INPUT +// array(2) { [0]=> object(SimpleXMLElement)#79 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(3) "111" } } [1]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10900 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(6) "Ducati" } } } +// IO SONO ITEM +// object(SimpleXMLElement)#79 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#82 (1) { ["value"]=> string(3) "111" } } ID: + +// ELEM IN INPUTobject(SimpleXMLElement)#81 (1) { ["customField"]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } } +// getCustomFieldsAttribute +// AFTER get obj vars ACCESS IN INPUT +// object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } +// IO SONO ITEM +// object(SimpleXMLElement)#82 (0) { } ID: + +// AFTER get obj vars ACCESS IN INPUT +// array(1) { [0]=> object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } } +// IO SONO ITEM +// object(SimpleXMLElement)#78 (2) { ["customfieldId"]=> string(19) " customfield_10800 " ["values"]=> object(SimpleXMLElement)#79 (1) { ["value"]=> string(3) "111" } } ID: + +$cfmod04 = "\n" . "\n" . "customfield_10800\n" . + "\n" . "111\n" . + "\n"; + +$oneOneCFMultiListValue = "\n" . "\n" . + "customfield_10900\n" . "\n" . + "Yamaha Factory Racing\n" . + "\n"; + +$cfmod05 = "\n" . "\n" . "customfield_10900\n" . + "\n" . "Ducati\n" . + "\n"; + +$cfmod06 = "\n" . "\n" . "customfield_10800\n" . + "\n" . "111\n" . + "\n"; + +$cf = $oneCFMultiListValue; + +$cfg = "\n" . "testlink.forum\n" . + "forum\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + "http://testlink.atlassian.net/browse/\n" . + "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + "ZOFF\n" . "1\n" . + "1010010101\n" . + "\n" . $cf . "\n" . "\n" . + "\n"; + +echo '

    '; +echo "Testing BST Integration - jirasoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirasoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + $today = date("Y-m-d H:i:s"); + $issue = array( + 'summary' => 'Issue Via API' . $today, + 'description' => 'Do Androids Dream of Electric Sheep?' + ); + $zorro = $its->addIssue($issue['summary'], $issue['description']); + echo '
    ';
    +    var_dump($zorro);
    +    echo '
    '; } diff --git a/lib/issuetrackerintegration/code_testing/jira/soap/test.getIssue.jiraOnDemand.jirasoapInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/soap/test.getIssue.jiraOnDemand.jirasoapInterface.class.php index 021fe7e756..ff5cf650a0 100644 --- a/lib/issuetrackerintegration/code_testing/jira/soap/test.getIssue.jiraOnDemand.jirasoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/soap/test.getIssue.jiraOnDemand.jirasoapInterface.class.php @@ -1,49 +1,45 @@ -getTypes(); - -// last test ok: 20121117 -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirasoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; -echo 'Creating INTERFACE
    '; -$its = new jirasoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - echo 'Get Issue
    '; - // $zx = $its->getIssue('ZOFF-112'); - // $zx = $its->getIssue('ZOFF-135'); - $zx = $its->getIssue('ZOFF-166'); - echo '
    ';
    -  var_dump($zx);
    -  echo '
    '; - echo '
    '; -} \ No newline at end of file +getTypes(); + +// last test ok: 20121117 +$cfg = "\n" . "testlink.forum\n" . + "forum\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + "http://testlink.atlassian.net/browse/\n" . + "\n"; + +echo '

    '; +echo "Testing BST Integration - jirasoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; +echo 'Creating INTERFACE
    '; +$its = new jirasoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + echo 'Get Issue
    '; + $zx = $its->getIssue('ZOFF-166'); + echo '
    ';
    +    var_dump($zx);
    +    echo '
    '; + echo '
    '; +} diff --git a/lib/issuetrackerintegration/code_testing/jira/soap/test.jiraOnDemand.jirasoapInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/soap/test.jiraOnDemand.jirasoapInterface.class.php index d8f6b70125..1f46eb1679 100644 --- a/lib/issuetrackerintegration/code_testing/jira/soap/test.jiraOnDemand.jirasoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/soap/test.jiraOnDemand.jirasoapInterface.class.php @@ -1,219 +1,178 @@ -getTypes(); - -// last test ok: 20121117 -/* -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "ZOFF\n" . - "1\n" . - "1010010101\n" . - "\n" . - "\n" . - "\n" . - "customfield_10800\n" . - "\n" . - "12\n" . - "\n" . - "\n" . - "\n" . - "\n"; - -array(7) { ["project"]=> string(4) "ZOFF" ["type"]=> int(1) -["summary"]=> string(24) "Issue Via API 2013-02-04" ["description"]=> string(36) "Do Androids Dream of Electric Sheep?" ["components"]=> array(2) { [0]=> array(1) { ["id"]=> string(5) "10100" } [1]=> array(1) { ["id"]=> string(5) "10101" } } ["customFieldValues"]=> array(2) { [0]=> array(1) { ["remoteCustomFieldValue"]=> string(19) " customfield_10800 " } [1]=> array(1) { ["remoteCustomFieldValue"]=> string(0) "" } } ["issuetype"]=> int(1) } array(3) { ["status_ok"]=> bool(false) ["id"]=> int(-1) ["msg"]=> string(550) "Create JIRA Ticket FAILURE => com.atlassian.jira.rpc.exception.RemoteValidationException: Custom field ID 'null' is invalid. - serialized issue:a:7:{s:7:"project";s:4:"ZOFF";s:4:"type";i:1;s:7:"summary";s:24:"Issue Via API 2013-02-04";s:11:"description";s:36:"Do Androids Dream of Electric Sheep?";s:10:"components";a:2:{i:0;a:1:{s:2:"id";s:5:"10100";}i:1;a:1:{s:2:"id";s:5:"10101";}}s:17:"customFieldValues";a:2:{i:0;a:1:{s:22:"remoteCustomFieldValue";s:19:" customfield_10800 ";}i:1;a:1:{s:22:"remoteCustomFieldValue";s:0:"";}}s:9:"issuetype";i:1;}" } -*/ - -/* -- snip -- -$issue_attributes = array('project' => 'FEEDBACK', 'assignee' => 'simon', -'type' => '1', 'summary' => 'test', -'customFieldValues' => -array(array('customfieldId' => 'customfield_10011', 'value' => -'a little')) -); -*/ - -/* -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "ZOFF\n" . - "1\n" . - "\n"; -*/ - -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "ZOFF\n" . - "1\n" . - "1010010101\n" . - "\n" . - "\n" . - "\n" . - "customfield_10800\n" . - "\n" . - "12120\n" . - "\n" . - "\n" . - "\n" . - "\n"; - -/* -{ ["components"] => object(SimpleXMLElement)#84 (1) { ["id"]=> array(2) { [0]=> string(5) "10100" [1]=> string(5) "10101" } } - ["customFieldValues"]=> object(SimpleXMLElement)#83 (1) - { ["id"]=> object(SimpleXMLElement)#82 (2) - { ["customFieldID"]=> string(19) " customfield_10800 " - ["values"]=> object(SimpleXMLElement)#85 (1) - { ["value"]=> array(2) { [0]=> string(2) "12" [1]=> string(3) "120" } } } } } } - -This is an issue to allow people to test integration -array(7) { ["project"]=> string(4) "ZOFF" ["type"]=> int(1) - ["summary"]=> string(24) "Issue Via API 2013-02-04" - ["description"]=> string(36) "Do Androids Dream of Electric Sheep?" - ["components"]=> array(2) { [0]=> array(1) { ["id"]=> string(5) "10100" } [1]=> array(1) { ["id"]=> string(5) "10101" } } - ["customFieldValues"]=> array(2) { [0]=> array(1) { ["id"]=> string(19) " customfield_10800 " } - [1]=> array(1) { ["id"]=> string(0) "" } } - ["issuetype"]=> int(1) } array(3) { ["status_ok"]=> bool(false) ["id"]=> int(-1) - ["msg"]=> string(508) "Create JIRA Ticket FAILURE => com.atlassian.jira.rpc.exception.RemoteValidationException: - Custom field ID 'null' is invalid. - -serialized issue:a:7:{s:7:"project";s:4:"ZOFF";s:4:"type";i:1;s:7:"summary";s:24:"Issue Via API 2013-02-04"; -s:11:"description";s:36:"Do Androids Dream of Electric Sheep?"; -s:10:"components" ;a:2:{i:0;a:1:{s:2:"id";s:5:"10100";}i:1;a:1:{s:2:"id";s:5:"10101";}} -s:17:"customFieldValues";a:2:{i:0;a:1:{s:2:"id";s:19:" customfield_10800 ";}i:1;a:1:{s:2:"id";s:0:"";}}s:9:"issuetype";i:1;}" } - - - - bread - chicken - non-veg - burger - chicken - - Zinger Burger - _< !]]> - - - 10 - 450 - - - - - IT - - - Bob - - - Jim - - - Mel - - - - - -*/ - - -echo '

    '; -echo "Testing BST Integration - jirasoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -// $safe_cfg = str_replace("\n",'',$cfg); -// echo $safe_cfg; -echo 'Creating INTERFACE
    '; - -new dBug($itt); -$its = new jirasoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - echo 'Get Issue
    '; - $zx = $its->getIssue('ZOFF-112'); - var_dump($zx); - //echo $zx->asXML(); - - echo '
    '; - - // echo 'Connected !
    '; - // echo '
    ';
    -	// var_dump($its->getStatusDomain());
    -	// echo '
    '; - //echo 'Get Issue Summary
    '; - // echo($its->getIssueSummary('ZOFF-16')); - //echo '
    '; - - -// I've seen things you people wouldn't believe. -// Attack ships on fire off the shoulder of Orion. -// I watched C-beams glitter in the dark near the Tannhauser gate. -// All those moments will be lost in time... like tears in rain... Time to die. -// - //$issue = array('project' => 'ZOFF','summary' => 'My Firts ISSUE VIA API', - // 'description' => 'Do Androids Dream of Electric Sheep?', - // 'type' => 1, 'components' => array( array('id' => '10100'), array('id' => '10101'))); - // $zorro = $its->addIssueFromArray($issue); - // var_dump($zorro); - $issue = array('summary' => 'Issue Via API 2013-02-04','description' => 'Do Androids Dream of Electric Sheep?'); - // 'type' => 1, 'components' => array( array('id' => '10100'), array('id' => '10101'))); - - $zorro = $its->addIssue($issue['summary'],$issue['description']); - var_dump($zorro); - - - /* - $issue2check = array( array('issue' => 'TLJIRASOAPINTEGRATION-1', 'exists' => true), - array('issue' => 'TLJIRASOAPINTEGRATION-199999', 'exists' => false)); - - */ - - - -/* -["customFieldValues"]=> array(4) -{ [0]=> object(stdClass)#85 (3) - { ["customfieldId"]=> string(17) "customfield_10800" ["key"]=> NULL ["values"]=> array(1) { [0]=> string(3) "123" } } - [1]=> object(stdClass)#82 (3) - { ["customfieldId"]=> string(17) "customfield_10000" ["key"]=> NULL ["values"]=> array(1) { [0]=> string(3) "117" } } - [2]=> object(stdClass)#83 (3) - { ["customfieldId"]=> string(17) "customfield_10200" ["key"]=> NULL ["values"]=> array(1) { [0]=> string(3) "117" } } - [3]=> object(stdClass)#84 (3) { ["customfieldId"]=> string(17) "customfield_10607" ["key"]=> NULL ["values"]=> array(1) { [0]=> NULL } } } -*/ -} -?> \ No newline at end of file +getTypes(); + +// last test ok: 20121117 +/* + * $cfg = "\n" . + * "testlink.forum\n" . + * "forum\n" . + * "http://testlink.atlassian.net/\n" . + * "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + * "http://testlink.atlassian.net/browse/\n" . + * "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + * "ZOFF\n" . + * "1\n" . + * "1010010101\n" . + * "\n" . + * "\n" . + * "\n" . + * "customfield_10800\n" . + * "\n" . + * "12\n" . + * "\n" . + * "\n" . + * "\n" . + * "\n"; + * + * array(7) { ["project"]=> string(4) "ZOFF" ["type"]=> int(1) + * ["summary"]=> string(24) "Issue Via API 2013-02-04" ["description"]=> string(36) "Do Androids Dream of Electric Sheep?" ["components"]=> array(2) { [0]=> array(1) { ["id"]=> string(5) "10100" } [1]=> array(1) { ["id"]=> string(5) "10101" } } ["customFieldValues"]=> array(2) { [0]=> array(1) { ["remoteCustomFieldValue"]=> string(19) " customfield_10800 " } [1]=> array(1) { ["remoteCustomFieldValue"]=> string(0) "" } } ["issuetype"]=> int(1) } array(3) { ["status_ok"]=> bool(false) ["id"]=> int(-1) ["msg"]=> string(550) "Create JIRA Ticket FAILURE => com.atlassian.jira.rpc.exception.RemoteValidationException: Custom field ID 'null' is invalid. - serialized issue:a:7:{s:7:"project";s:4:"ZOFF";s:4:"type";i:1;s:7:"summary";s:24:"Issue Via API 2013-02-04";s:11:"description";s:36:"Do Androids Dream of Electric Sheep?";s:10:"components";a:2:{i:0;a:1:{s:2:"id";s:5:"10100";}i:1;a:1:{s:2:"id";s:5:"10101";}}s:17:"customFieldValues";a:2:{i:0;a:1:{s:22:"remoteCustomFieldValue";s:19:" customfield_10800 ";}i:1;a:1:{s:22:"remoteCustomFieldValue";s:0:"";}}s:9:"issuetype";i:1;}" } + */ + +/* + * - snip -- + * $issue_attributes = array('project' => 'FEEDBACK', 'assignee' => 'simon', + * 'type' => '1', 'summary' => 'test', + * 'customFieldValues' => + * array(array('customfieldId' => 'customfield_10011', 'value' => + * 'a little')) + * ); + */ + +$cfg = "\n" . "testlink.forum\n" . + "forum\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + "http://testlink.atlassian.net/browse/\n" . + "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + "ZOFF\n" . "1\n" . + "1010010101\n" . + "\n" . "\n" . "\n" . + "customfield_10800\n" . "\n" . + "12120\n" . "\n" . + "\n" . "\n" . "\n"; + +/* + * { ["components"] => object(SimpleXMLElement)#84 (1) { ["id"]=> array(2) { [0]=> string(5) "10100" [1]=> string(5) "10101" } } + * ["customFieldValues"]=> object(SimpleXMLElement)#83 (1) + * { ["id"]=> object(SimpleXMLElement)#82 (2) + * { ["customFieldID"]=> string(19) " customfield_10800 " + * ["values"]=> object(SimpleXMLElement)#85 (1) + * { ["value"]=> array(2) { [0]=> string(2) "12" [1]=> string(3) "120" } } } } } } + * + * This is an issue to allow people to test integration + * array(7) { ["project"]=> string(4) "ZOFF" ["type"]=> int(1) + * ["summary"]=> string(24) "Issue Via API 2013-02-04" + * ["description"]=> string(36) "Do Androids Dream of Electric Sheep?" + * ["components"]=> array(2) { [0]=> array(1) { ["id"]=> string(5) "10100" } [1]=> array(1) { ["id"]=> string(5) "10101" } } + * ["customFieldValues"]=> array(2) { [0]=> array(1) { ["id"]=> string(19) " customfield_10800 " } + * [1]=> array(1) { ["id"]=> string(0) "" } } + * ["issuetype"]=> int(1) } array(3) { ["status_ok"]=> bool(false) ["id"]=> int(-1) + * ["msg"]=> string(508) "Create JIRA Ticket FAILURE => com.atlassian.jira.rpc.exception.RemoteValidationException: + * Custom field ID 'null' is invalid. - + * serialized issue:a:7:{s:7:"project";s:4:"ZOFF";s:4:"type";i:1;s:7:"summary";s:24:"Issue Via API 2013-02-04"; + * s:11:"description";s:36:"Do Androids Dream of Electric Sheep?"; + * s:10:"components" ;a:2:{i:0;a:1:{s:2:"id";s:5:"10100";}i:1;a:1:{s:2:"id";s:5:"10101";}} + * s:17:"customFieldValues";a:2:{i:0;a:1:{s:2:"id";s:19:" customfield_10800 ";}i:1;a:1:{s:2:"id";s:0:"";}}s:9:"issuetype";i:1;}" } + * + * + * + * bread + * chicken + * non-veg + * burger + * chicken + * + * Zinger Burger + * _< !]]> + * + * + * 10 + * 450 + * + * + * + * + * IT + * + * + * Bob + * + * + * Jim + * + * + * Mel + * + * + * + * + * + */ + +echo '

    '; +echo "Testing BST Integration - jirasoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +echo 'Creating INTERFACE
    '; + +new dBug($itt); +$its = new jirasoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + echo 'Get Issue
    '; + $zx = $its->getIssue('ZOFF-112'); + var_dump($zx); + + echo '
    '; + + // I've seen things you people wouldn't believe. + // Attack ships on fire off the shoulder of Orion. + // I watched C-beams glitter in the dark near the Tannhauser gate. + // All those moments will be lost in time... like tears in rain... Time to die. + // + // $issue = array('project' => 'ZOFF','summary' => 'My Firts ISSUE VIA API', + // 'description' => 'Do Androids Dream of Electric Sheep?', + // 'type' => 1, 'components' => array( array('id' => '10100'), array('id' => '10101'))); + // $zorro = $its->addIssueFromArray($issue); + // var_dump($zorro); + $issue = array( + 'summary' => 'Issue Via API 2013-02-04', + 'description' => 'Do Androids Dream of Electric Sheep?' + ); + // 'type' => 1, 'components' => array( array('id' => '10100'), array('id' => '10101'))); + + $zorro = $its->addIssue($issue['summary'], $issue['description']); + var_dump($zorro); + + /* + * ["customFieldValues"]=> array(4) + * { [0]=> object(stdClass)#85 (3) + * { ["customfieldId"]=> string(17) "customfield_10800" ["key"]=> NULL ["values"]=> array(1) { [0]=> string(3) "123" } } + * [1]=> object(stdClass)#82 (3) + * { ["customfieldId"]=> string(17) "customfield_10000" ["key"]=> NULL ["values"]=> array(1) { [0]=> string(3) "117" } } + * [2]=> object(stdClass)#83 (3) + * { ["customfieldId"]=> string(17) "customfield_10200" ["key"]=> NULL ["values"]=> array(1) { [0]=> string(3) "117" } } + * [3]=> object(stdClass)#84 (3) { ["customfieldId"]=> string(17) "customfield_10607" ["key"]=> NULL ["values"]=> array(1) { [0]=> NULL } } } + */ +} +?> diff --git a/lib/issuetrackerintegration/code_testing/jira/soap/test.jirasoapInterface.class.php b/lib/issuetrackerintegration/code_testing/jira/soap/test.jirasoapInterface.class.php index ab160796a4..a31f08681e 100644 --- a/lib/issuetrackerintegration/code_testing/jira/soap/test.jirasoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/jira/soap/test.jirasoapInterface.class.php @@ -1,90 +1,57 @@ -getTypes(); - -/* -$cfg = "\n" . - "testlink.helpme\n" . - "jira\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "\n"; -*/ -// last test ok: 20121117 -$cfg = "\n" . - "testlink.forum\n" . - "forum\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "http://testlink.atlassian.net/browse/\n" . - "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - jirasoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -// $safe_cfg = str_replace("\n",'',$cfg); -// echo $safe_cfg; -echo 'Creating INTERFACE
    '; - -new dBug($itt); -$its = new jirasoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -if( $its->isConnected() ) -{ - // echo 'Connected !
    '; - // echo '
    ';
    -	// var_dump($its->getStatusDomain());
    -	// echo '
    '; - // echo 'Get Issue Summary
    '; - // echo($its->getIssueSummary('ZOFF-8')); - // echo '
    '; - - // echo($its->getIssueSummary('ZOFF-8')); - - -// I've seen things you people wouldn't believe. -// Attack ships on fire off the shoulder of Orion. -// I watched C-beams glitter in the dark near the Tannhauser gate. -// All those moments will be lost in time... like tears in rain... Time to die. -// - $issue = array('project' => 'ZOFF','summary' => 'My Firts ISSUE VIA API', - 'description' => 'Do Androids Dream of Electric Sheep?', - 'type' => 1); - $zorro = $its->addIssue($issue); - var_dump($zorro); - - - /* - $issue2check = array( array('issue' => 'TLJIRASOAPINTEGRATION-1', 'exists' => true), - array('issue' => 'TLJIRASOAPINTEGRATION-199999', 'exists' => false)); - - */ - - - - -} -?> \ No newline at end of file +getTypes(); + +// last test ok: 20121117 +$cfg = "\n" . "testlink.forum\n" . + "forum\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + "http://testlink.atlassian.net/browse/\n" . + "http://testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + "\n"; + +echo '

    '; +echo "Testing BST Integration - jirasoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +echo 'Creating INTERFACE
    '; + +new dBug($itt); +$its = new jirasoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +if ($its->isConnected()) { + // I've seen things you people wouldn't believe. + // Attack ships on fire off the shoulder of Orion. + // I watched C-beams glitter in the dark near the Tannhauser gate. + // All those moments will be lost in time... like tears in rain... Time to die. + // + $issue = array( + 'project' => 'ZOFF', + 'summary' => 'My Firts ISSUE VIA API', + 'description' => 'Do Androids Dream of Electric Sheep?', + 'type' => 1 + ); + $zorro = $its->addIssue($issue); + var_dump($zorro); +} +?> diff --git a/lib/issuetrackerintegration/code_testing/kaiten/test.kaitenInterface.class.php b/lib/issuetrackerintegration/code_testing/kaiten/test.kaitenInterface.class.php index 359a577cfd..809a306513 100644 --- a/lib/issuetrackerintegration/code_testing/kaiten/test.kaitenInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/kaiten/test.kaitenInterface.class.php @@ -1,62 +1,61 @@ -\n" . - "YOR APIKEY HERE\n". - "https://yourcompany.kaiten.io\n". - "REPLACE_ME\n". - "\n"; - -echo '

    '; -echo "Testing BST Integration - kaitenrestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$its = new kaitenrestInterface(23,$cfg,'KAITEN'); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -$issueId = null; -if( $its->isConnected() ) { - $today = date("Y-m-d H:i:s"); - $issue = array('summary' => 'New issue card Via API' . $today,'description' => 'Some text'); - $resp = $its->addIssue($issue['summary'],$issue['description']); - echo '
    ' . __FILE__ . '
    '; - echo '
    ';
    -  var_dump($resp);
    -  echo '
    '; - $issueId = isset($resp['id']) ? $resp['id'] : null; -} - -if( $its->isConnected() ) -{ - $resp = $its->getIssue($issueId); - - echo '
    ' . __FILE__ . '
    '; - echo '
    ';
    -  var_dump($resp);
    -  echo '
    '; - -} - +\n" . "YOR APIKEY HERE\n" . + "https://yourcompany.kaiten.io\n" . + "REPLACE_ME\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - kaitenrestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$its = new kaitenrestInterface(23, $cfg, 'KAITEN'); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +$issueId = null; +if ($its->isConnected()) { + $today = date("Y-m-d H:i:s"); + $issue = array( + 'summary' => 'New issue card Via API' . $today, + 'description' => 'Some text' + ); + $resp = $its->addIssue($issue['summary'], $issue['description']); + echo '
    ' . __FILE__ . '
    '; + echo '
    ';
    +    var_dump($resp);
    +    echo '
    '; + $issueId = isset($resp['id']) ? $resp['id'] : null; +} + +if ($its->isConnected()) { + $resp = $its->getIssue($issueId); + + echo '
    ' . __FILE__ . '
    '; + echo '
    ';
    +    var_dump($resp);
    +    echo '
    '; +} + ?> diff --git a/lib/issuetrackerintegration/code_testing/mantis/test.mantissoapInterface.class.php b/lib/issuetrackerintegration/code_testing/mantis/test.mantissoapInterface.class.php index 935c3a94f4..1e5c7d6b94 100644 --- a/lib/issuetrackerintegration/code_testing/mantis/test.mantissoapInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/mantis/test.mantissoapInterface.class.php @@ -1,68 +1,46 @@ -getTypes(); - -// last test ok: 20121210 -/* -$cfg = "\n" . - "\n" . - "u0113\n" . - "tesi\n" . - "http://localhost:8080/development/closet/mantisbt-1.2.11/\n" . - "\n" . - "\n" . - "80resolved\n" . - "90closed\n" . - "\n". - "\n"; -*/ - -$username='administrator'; -$password='root'; -$uribase='http://localhost/development/mantis/mantisbt-1.2.15/'; - - -$cfg = "\n" . - "\n" . - "$username\n" . - "$password\n" . - "$uribase\n" . - "Project ONE\n" . - "YUMO" . - "\n"; - - -echo '

    '; -echo "Testing Issue Tracker Integration - mantissoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; -echo 'Creating INTERFACE
    '; - -$its = new mantissoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); - -$issueID = 12; -$issueNote = 'SONO UNA NOTAwww!!!'; -if( $its->isConnected() ) -{ - echo 'Connected !
    '; - $xx = $its->addNote($issueID,$issueNote); - var_dump($xx); -} \ No newline at end of file +getTypes(); + +$username = 'administrator'; +$password = 'root'; +$uribase = 'http://localhost/development/mantis/mantisbt-1.2.15/'; + +$cfg = "\n" . "\n" . + "$username\n" . "$password\n" . + "$uribase\n" . "Project ONE\n" . + "YUMO" . "\n"; + +echo '

    '; +echo "Testing Issue Tracker Integration - mantissoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; +echo 'Creating INTERFACE
    '; + +$its = new mantissoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); + +$issueID = 12; +$issueNote = 'SONO UNA NOTAwww!!!'; +if ($its->isConnected()) { + echo 'Connected !
    '; + $xx = $its->addNote($issueID, $issueNote); + var_dump($xx); +} diff --git a/lib/issuetrackerintegration/code_testing/mantis/test.testlinkopensource.mantishub.com.class.php b/lib/issuetrackerintegration/code_testing/mantis/test.testlinkopensource.mantishub.com.class.php index 66aad73e38..c009865051 100644 --- a/lib/issuetrackerintegration/code_testing/mantis/test.testlinkopensource.mantishub.com.class.php +++ b/lib/issuetrackerintegration/code_testing/mantis/test.testlinkopensource.mantishub.com.class.php @@ -1,55 +1,50 @@ -getTypes(); - -// last test ok: 20121210 -$cfg = "\n" . - "\n" . - "administrator\n" . - "mantis\n" . - "http://testlinkopensource.mantishub.com\n" . - "\n" . - "\n" . - "80resolved\n" . - "90closed\n" . - "\n". - "\n"; - -echo '

    '; -echo "Testing Issue Tracker Integration - mantissoapInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; -echo 'Creating INTERFACE
    '; - -$its = new mantissoapInterface(5,$cfg); - -echo 'Connection OK?
    '; -var_dump($its->isConnected()); -if( $its->isConnected() ) -{ - echo 'Connected !
    '; - - echo 'get resolved status configuration
    '; - new dBug($its->getResolvedStatusCfg()); - - echo 'get issue
    '; - new dBug($its->getIssue(102575)); - new dBug($its->getIssue(102529)); - -} -?> \ No newline at end of file +getTypes(); + +// last test ok: 20121210 +$cfg = "\n" . "\n" . + "administrator\n" . "mantis\n" . + "http://testlinkopensource.mantishub.com\n" . + "\n" . + "\n" . + "80resolved\n" . + "90closed\n" . + "\n" . "\n"; + +echo '

    '; +echo "Testing Issue Tracker Integration - mantissoapInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; +echo 'Creating INTERFACE
    '; + +$its = new mantissoapInterface(5, $cfg); + +echo 'Connection OK?
    '; +var_dump($its->isConnected()); +if ($its->isConnected()) { + echo 'Connected !
    '; + + echo 'get resolved status configuration
    '; + new dBug($its->getResolvedStatusCfg()); + + echo 'get issue
    '; + new dBug($its->getIssue(102575)); + new dBug($its->getIssue(102529)); +} +?> diff --git a/lib/issuetrackerintegration/code_testing/mantis/test2_int_mantissoap.php b/lib/issuetrackerintegration/code_testing/mantis/test2_int_mantissoap.php index f681efb373..ad882447a4 100644 --- a/lib/issuetrackerintegration/code_testing/mantis/test2_int_mantissoap.php +++ b/lib/issuetrackerintegration/code_testing/mantis/test2_int_mantissoap.php @@ -1,78 +1,88 @@ - -testlink.helpme -testlink.helpme -http://www.mantisbt.org/ -http://www.mantisbt.org/bugs/api/soap/mantisconnect.php?wsdl -http://www.mantisbt.org/bugs/view.php?id= -http://www.mantisbt.org/bugs/ - - - -*/ - -echo '

    '; -echo "Testing BST Integration :{$g_interface_bugs} "; -echo '

    '; -echo "Configuration settings
    "; -echo "username:" . BUG_TRACK_USERNAME . '
    '; -echo "password:" . BUG_TRACK_PASSWORD . '
    '; -echo "BUG_TRACK_HREF:" . BUG_TRACK_HREF . '
    '; -echo "BUG_TRACK_SOAP_HREF:" . BUG_TRACK_SOAP_HREF . '
    '; -echo "BUG_TRACK_SHOW_ISSUE_HREF:" . BUG_TRACK_SHOW_ISSUE_HREF . '
    '; -echo "BUG_TRACK_ENTER_ISSUE_HREF:" . BUG_TRACK_ENTER_ISSUE_HREF .'
    '; -echo '


    '; - -$op = config_get('bugInterfaceOn'); -echo 'Connection Status:' . ( $op ? 'OK' : 'KO Oohhh!') . '

    '; - -if($op) -{ - $issue2check = array( array('issue' => 11776, 'exists' => true), - array('issue' => 99999, 'exists' => false)); - - $methods = array('getBugSummaryString','getBugStatus','getBugStatusString', - 'checkBugID_existence','buildViewBugLink'); - - $if = config_get('bugInterface'); - $tc=1; - foreach($issue2check as $elem) - { - $issue = $elem['issue']; - $msg = $elem['exists'] ? "
    Ask info about EXISTENT ISSUE:{$issue}
    " : - "
    Ask info about INEXISTENT ISSUE:{$issue}
    "; - - echo $msg; - foreach($methods as $call) - { - $x = $if->$call($issue); - echo '
    Test Case #' . $tc . '
    '; - echo "
    \$if->$call($issue) => " . $x .'

    '; - $tc++; - } - } -} -?> \ No newline at end of file + + * testlink.helpme + * testlink.helpme + * http://www.mantisbt.org/ + * http://www.mantisbt.org/bugs/api/soap/mantisconnect.php?wsdl + * http://www.mantisbt.org/bugs/view.php?id= + * http://www.mantisbt.org/bugs/ + * + * + * + */ + +echo '

    '; +echo "Testing BST Integration :{$g_interface_bugs} "; +echo '

    '; +echo "Configuration settings
    "; +echo "username:" . BUG_TRACK_USERNAME . '
    '; +echo "password:" . BUG_TRACK_PASSWORD . '
    '; +echo "BUG_TRACK_HREF:" . BUG_TRACK_HREF . '
    '; +echo "BUG_TRACK_SOAP_HREF:" . BUG_TRACK_SOAP_HREF . '
    '; +echo "BUG_TRACK_SHOW_ISSUE_HREF:" . BUG_TRACK_SHOW_ISSUE_HREF . '
    '; +echo "BUG_TRACK_ENTER_ISSUE_HREF:" . BUG_TRACK_ENTER_ISSUE_HREF . '
    '; +echo '


    '; + +$op = config_get('bugInterfaceOn'); +echo 'Connection Status:' . ($op ? 'OK' : 'KO Oohhh!') . '

    '; + +if ($op) { + $issue2check = array( + array( + 'issue' => 11776, + 'exists' => true + ), + array( + 'issue' => 99999, + 'exists' => false + ) + ); + + $methods = array( + 'getBugSummaryString', + 'getBugStatus', + 'getBugStatusString', + 'checkBugID_existence', + 'buildViewBugLink' + ); + + $if = config_get('bugInterface'); + $tc = 1; + foreach ($issue2check as $elem) { + $issue = $elem['issue']; + $msg = $elem['exists'] ? "
    Ask info about EXISTENT ISSUE:{$issue}
    " : "
    Ask info about INEXISTENT ISSUE:{$issue}
    "; + + echo $msg; + foreach ($methods as $call) { + $x = $if->$call($issue); + echo '
    Test Case #' . $tc . '
    '; + echo "
    \$if->$call($issue) => " . $x . '

    '; + $tc ++; + } + } +} +?> diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-redmine.php b/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-redmine.php index 7ecb0bef14..91169c3f7e 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-redmine.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-redmine.php @@ -1,30 +1,40 @@ - 'http://192.168.1.174', - 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', - 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922'), - array('url' => 'https://localhost:8443/redmine/', - 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd'), - array('url' => 'http://localhost:8888/redmine/', - 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd'), - array('url' => 'http://testlink01.m.redmine.org', - 'apiKey' => '058157d55d62b632a665491abcc003aa4554673d') - ); - -$siteID = 4; -$red = new redmine($site[$siteID]['url'],$site[$siteID]['apiKey']); - -$xml = new SimpleXMLElement(''); -$xml->addChild('subject', htmlentities('TEST SUBJECT - MAY 1')); - -// As you see we can use the project identifier (string) as project_id!!! Great -$xml->addChild('project_id', 'quantum-priv'); -$xml->addChild('tracker_id', 1); + 'http://192.168.1.174', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ), + array( + 'url' => 'https://localhost:8443/redmine/', + 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd' + ), + array( + 'url' => 'http://localhost:8888/redmine/', + 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd' + ), + array( + 'url' => 'http://testlink01.m.redmine.org', + 'apiKey' => '058157d55d62b632a665491abcc003aa4554673d' + ) +); + +$siteID = 4; +$red = new redmine($site[$siteID]['url'], $site[$siteID]['apiKey']); + +$xml = new SimpleXMLElement(''); +$xml->addChild('subject', htmlentities('TEST SUBJECT - MAY 1')); + +// As you see we can use the project identifier (string) as project_id!!! Great +$xml->addChild('project_id', 'quantum-priv'); +$xml->addChild('tracker_id', 1); $red->addIssueFromSimpleXML($xml); diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-with-cf-redmine.php b/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-with-cf-redmine.php index 7ecb0bef14..91169c3f7e 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-with-cf-redmine.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.add-issue-with-cf-redmine.php @@ -1,30 +1,40 @@ - 'http://192.168.1.174', - 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', - 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922'), - array('url' => 'https://localhost:8443/redmine/', - 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd'), - array('url' => 'http://localhost:8888/redmine/', - 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd'), - array('url' => 'http://testlink01.m.redmine.org', - 'apiKey' => '058157d55d62b632a665491abcc003aa4554673d') - ); - -$siteID = 4; -$red = new redmine($site[$siteID]['url'],$site[$siteID]['apiKey']); - -$xml = new SimpleXMLElement(''); -$xml->addChild('subject', htmlentities('TEST SUBJECT - MAY 1')); - -// As you see we can use the project identifier (string) as project_id!!! Great -$xml->addChild('project_id', 'quantum-priv'); -$xml->addChild('tracker_id', 1); + 'http://192.168.1.174', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ), + array( + 'url' => 'https://localhost:8443/redmine/', + 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd' + ), + array( + 'url' => 'http://localhost:8888/redmine/', + 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd' + ), + array( + 'url' => 'http://testlink01.m.redmine.org', + 'apiKey' => '058157d55d62b632a665491abcc003aa4554673d' + ) +); + +$siteID = 4; +$red = new redmine($site[$siteID]['url'], $site[$siteID]['apiKey']); + +$xml = new SimpleXMLElement(''); +$xml->addChild('subject', htmlentities('TEST SUBJECT - MAY 1')); + +// As you see we can use the project identifier (string) as project_id!!! Great +$xml->addChild('project_id', 'quantum-priv'); +$xml->addChild('tracker_id', 1); $red->addIssueFromSimpleXML($xml); diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.add-note-redmine.php b/lib/issuetrackerintegration/code_testing/redmine/test.add-note-redmine.php index 88e405f3ef..969e1b9541 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.add-note-redmine.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.add-note-redmine.php @@ -1,42 +1,47 @@ - 'http://192.168.1.174', - 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', - 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922'), - array('url' => 'https://localhost:8443/redmine/', - 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd'), - array('url' => 'http://localhost:8888/redmine/', - 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd'), - array('url' => 'http://testlink01.m.redmine.org', - 'apiKey' => '058157d55d62b632a665491abcc003aa4554673d') - ); - - -// $siteID = 2; -$siteID = 4; -$issueID = 4; -$red = new redmine($site[$siteID]['url'],$site[$siteID]['apiKey']); -$issueObj = $red->getIssue($issueID); -// echo '
    ';
    -// var_dump($issueObj);
    -// echo '
    '; -// die(); - -echo '
    '; -echo 'Summary(SUBJECT):' .(string)$issueObj->subject . '
    '; -echo 'Status: Name/ID' . (string)$issueObj->status['name'] . '/' . (int)$issueObj->status['id'] . '
    '; -echo '

    ';
    -echo '
    '; - -echo '
    '; -echo 'Trying to add a NOTE'; -$issueXmlObj = new SimpleXMLElement(''); -$issueXmlObj->addChild('notes', htmlspecialchars('ciao Bello!!!')); -$red->addIssueNoteFromSimpleXML($issueID,$issueXmlObj); \ No newline at end of file + 'http://192.168.1.174', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ), + array( + 'url' => 'https://localhost:8443/redmine/', + 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd' + ), + array( + 'url' => 'http://localhost:8888/redmine/', + 'apiKey' => '81538efac88d05a1dbf77b80e793526dbd4921dd' + ), + array( + 'url' => 'http://testlink01.m.redmine.org', + 'apiKey' => '058157d55d62b632a665491abcc003aa4554673d' + ) +); + +$siteID = 4; +$issueID = 4; +$red = new redmine($site[$siteID]['url'], $site[$siteID]['apiKey']); +$issueObj = $red->getIssue($issueID); + +echo '
    '; +echo 'Summary(SUBJECT):' . (string) $issueObj->subject . '
    '; +echo 'Status: Name/ID' . (string) $issueObj->status['name'] . '/' . + (int) $issueObj->status['id'] . '
    '; +echo '

    ';
    +echo '
    '; + +echo '
    '; +echo 'Trying to add a NOTE'; +$issueXmlObj = new SimpleXMLElement(''); +$issueXmlObj->addChild('notes', htmlspecialchars('ciao Bello!!!')); +$red->addIssueNoteFromSimpleXML($issueID, $issueXmlObj); diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-cfg.php b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-cfg.php index dc47fa517e..535a1069a6 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-cfg.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-cfg.php @@ -1,68 +1,61 @@ -getTypes(); - - - - - -//$site = array(array('url' => 'http://192.168.1.174','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), -// $site = array(array('url' => 'http://192.168.1.2','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), -$site = array(array('url' => 'http://192.168.1.74','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', 'project_id' => 'tl-rest', - 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922'), - array('url' => 'http://127.0.0.1:8085/redmine', 'project_id' => 'fedora-20', - 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d'), - array('url' => 'https://127.0.0.1:8443/redmine', 'project_id' => 'fedora-20', - 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d')); - -// e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8 -// curl -v -H "Content-Type: application/xml" -X POST --data "@issue.xml" -H "X-Redmine-API-Key: e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8" http://192.168.1.2/issues.xml - -$targetSite = 3; - -$cfg = '' . "\n" . -'' . "\n" . -'' . $site[$targetSite]['apiKey'] . '' . "\n" . -'' . $site[$targetSite]['url'] . '' . "\n" . -'' . $site[$targetSite]['project_id'] . -'' . "\n" . - '' . "\n" . - ' ' . "\n" . - ' SALAME' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' STRF' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ALFA' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ''; - -echo '
    ';var_dump($cfg);echo '
    '; - -// var_dump($itt); -$its = new redminerestInterface(15,$cfg); -var_dump($its->getCfg()); - -echo 'ADD?'; - -$its->addIssue('SUMMARY ','TEST DESCR'); +getTypes(); + +$site = array( + array( + 'url' => 'http://192.168.1.74', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'project_id' => 'tl-rest', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ), + array( + 'url' => 'http://127.0.0.1:8085/redmine', + 'project_id' => 'fedora-20', + 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d' + ), + array( + 'url' => 'https://127.0.0.1:8443/redmine', + 'project_id' => 'fedora-20', + 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d' + ) +); + +// e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8 +// curl -v -H "Content-Type: application/xml" -X POST --data "@issue.xml" -H "X-Redmine-API-Key: e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8" http://192.168.1.2/issues.xml + +$targetSite = 3; + +$cfg = '' . "\n" . '' . "\n" . '' . + $site[$targetSite]['apiKey'] . '' . "\n" . '' . + $site[$targetSite]['url'] . '' . "\n" . '' . + $site[$targetSite]['project_id'] . '' . "\n" . + '' . "\n" . + ' ' . "\n" . + ' SALAME' . "\n" . ' ' . + "\n" . ' ' . "\n" . + ' STRF' . "\n" . ' ' . + "\n" . ' ' . + "\n" . ' ' . "\n" . + ' ALFA' . "\n" . ' ' . + "\n" . ' ' . "\n" . ' ' . "\n" . + ''; + +echo '
    ';
    +var_dump($cfg);
    +echo '
    '; + +$its = new redminerestInterface(15, $cfg); +var_dump($its->getCfg()); + +echo 'ADD?'; + +$its->addIssue('SUMMARY ', 'TEST DESCR'); diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-fman.php b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-fman.php index 334300c081..ce2ffef44a 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-fman.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-fman.php @@ -1,94 +1,88 @@ - 'http://192.168.1.174','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), -// $site = array(array('url' => 'http://192.168.1.2','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - -$site = array(array('url' => 'http://192.168.1.74','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922')); - -// e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8 -// curl -v -H "Content-Type: application/xml" -X POST --data "@issue.xml" -H "X-Redmine-API-Key: e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8" http://192.168.1.2/issues.xml - -$red = new redmine($site[0]['url'],$site[0]['apiKey']); -$issueObj = $red->getIssue(3); - -var_dump($issueObj); -die(); - -echo '
    '; -echo 'Summary(SUBJECT):' .(string)$issueObj->subject . '
    '; -echo 'Status: Name/ID' . (string)$issueObj->status['name'] . '/' . (int)$issueObj->status['id'] . '
    '; -echo '

    ';
    -echo '
    '; - - -$xml = new SimpleXMLElement(''); -$xml->addChild('subject', htmlentities('TEST SUBJECT')); -$xml->addChild('project_id', 'tl-rest'); -$xml->addChild('tracker_id', 1); -$red->addIssueFromSimpleXML($xml); - -/* -What you get when things work ok - - - 11 - - - - - - API TEST FMAN - - 2012-11-18 - - 0 - - 0.0 - Sun Nov 18 10:26:02 +0000 2012 - Sun Nov 18 10:26:02 +0000 2012 - - - - - - - -*/ - -die(); - -/* -20120328 20:44 -object(redmine)#1 (4) { ["url"]=> string(25) "http://fman.m.redmine.org" -["apiKey"]=> string(40) "b956de40bf8baf6af7344b759cd9471832f33922" -["curl"]=> resource(3) of type (curl) ["headers":"redmine":private]=> array(0) { } } -/issues.xml?key=b956de40bf8baf6af7344b759cd9471832f33922 -http://fman.m.redmine.org/issues.xml?key=b956de40bf8baf6af7344b759cd9471832f33922object(SimpleXMLElement)#2 (2) -{ ["@attributes"]=> array(4) { ["limit"]=> string(2) "25" ["type"]=> string(5) - "array" ["total_count"]=> string(1) "1" ["offset"]=> string(1) "0" } - ["issue"]=> object(SimpleXMLElement)#3 (14) { ["id"]=> string(1) "1" - ["project"]=> object(SimpleXMLElement)#4 (1) { ["@attributes"]=> array(2) - { ["name"]=> string(11) "fman-prj001" ["id"]=> string(1) "1" } } - ["tracker"]=> object(SimpleXMLElement)#5 (1) { ["@attributes"]=> array(2) - { ["name"]=> string(3) "Bug" ["id"]=> string(1) "1" } } - ["status"]=> object(SimpleXMLElement)#6 (1) { ["@attributes"]=> array(2) - { ["name"]=> string(3) "New" ["id"]=> string(1) "1" } } ["priority"]=> object(SimpleXMLElement)#7 (1) - { ["@attributes"]=> array(2) { ["name"]=> string(6) "Urgent" ["id"]=> string(1) "6" } } - ["author"]=> object(SimpleXMLElement)#8 (1) { ["@attributes"]=> array(2) - { ["name"]=> string(13) "Redmine Admin" ["id"]=> string(1) "2" } } ["subject"]=> string(11) - "fman-prj001" ["description"]=> string(11) "fman-prj001" ["start_date"]=> string(10) "2012-03-28" - ["due_date"]=> object(SimpleXMLElement)#9 (0) { } ["done_ratio"]=> string(1) "0" - ["estimated_hours"]=> object(SimpleXMLElement)#10 (0) { } ["created_on"]=> string(25) - "2012-03-28T20:41:21+02:00" ["updated_on"]=> string(25) "2012-03-28T20:41:21+02:00" } } -*/ -?> \ No newline at end of file + 'http://192.168.1.74', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ) +); + +$red = new redmine($site[0]['url'], $site[0]['apiKey']); +$issueObj = $red->getIssue(3); + +var_dump($issueObj); +die(); + +echo '
    '; +echo 'Summary(SUBJECT):' . (string) $issueObj->subject . '
    '; +echo 'Status: Name/ID' . (string) $issueObj->status['name'] . '/' . + (int) $issueObj->status['id'] . '
    '; +echo '

    ';
    +echo '
    '; + +$xml = new SimpleXMLElement(''); +$xml->addChild('subject', htmlentities('TEST SUBJECT')); +$xml->addChild('project_id', 'tl-rest'); +$xml->addChild('tracker_id', 1); +$red->addIssueFromSimpleXML($xml); + +/* + * What you get when things work ok + * + * + * 11 + * + * + * + * + * + * API TEST FMAN + * + * 2012-11-18 + * + * 0 + * + * 0.0 + * Sun Nov 18 10:26:02 +0000 2012 + * Sun Nov 18 10:26:02 +0000 2012 + * + * + * + * + * + * + * + */ + +die(); + +/* + * 20120328 20:44 + * object(redmine)#1 (4) { ["url"]=> string(25) "http://fman.m.redmine.org" + * ["apiKey"]=> string(40) "b956de40bf8baf6af7344b759cd9471832f33922" + * ["curl"]=> resource(3) of type (curl) ["headers":"redmine":private]=> array(0) { } } + * /issues.xml?key=b956de40bf8baf6af7344b759cd9471832f33922 + * http://fman.m.redmine.org/issues.xml?key=b956de40bf8baf6af7344b759cd9471832f33922object(SimpleXMLElement)#2 (2) + * { ["@attributes"]=> array(4) { ["limit"]=> string(2) "25" ["type"]=> string(5) + * "array" ["total_count"]=> string(1) "1" ["offset"]=> string(1) "0" } + * ["issue"]=> object(SimpleXMLElement)#3 (14) { ["id"]=> string(1) "1" + * ["project"]=> object(SimpleXMLElement)#4 (1) { ["@attributes"]=> array(2) + * { ["name"]=> string(11) "fman-prj001" ["id"]=> string(1) "1" } } + * ["tracker"]=> object(SimpleXMLElement)#5 (1) { ["@attributes"]=> array(2) + * { ["name"]=> string(3) "Bug" ["id"]=> string(1) "1" } } + * ["status"]=> object(SimpleXMLElement)#6 (1) { ["@attributes"]=> array(2) + * { ["name"]=> string(3) "New" ["id"]=> string(1) "1" } } ["priority"]=> object(SimpleXMLElement)#7 (1) + * { ["@attributes"]=> array(2) { ["name"]=> string(6) "Urgent" ["id"]=> string(1) "6" } } + * ["author"]=> object(SimpleXMLElement)#8 (1) { ["@attributes"]=> array(2) + * { ["name"]=> string(13) "Redmine Admin" ["id"]=> string(1) "2" } } ["subject"]=> string(11) + * "fman-prj001" ["description"]=> string(11) "fman-prj001" ["start_date"]=> string(10) "2012-03-28" + * ["due_date"]=> object(SimpleXMLElement)#9 (0) { } ["done_ratio"]=> string(1) "0" + * ["estimated_hours"]=> object(SimpleXMLElement)#10 (0) { } ["created_on"]=> string(25) + * "2012-03-28T20:41:21+02:00" ["updated_on"]=> string(25) "2012-03-28T20:41:21+02:00" } } + */ +?> diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-localhost.php b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-localhost.php index d8755f8273..c77f106d82 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-localhost.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-localhost.php @@ -1,112 +1,110 @@ - 'http://192.168.1.174','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), -// $site = array(array('url' => 'http://192.168.1.2','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), -$site = array(array('url' => 'http://192.168.1.74','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', 'project_id' => 'tl-rest', - 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922'), - array('url' => 'http://127.0.0.1:8085/redmine', 'project_id' => 'fedora-20', - 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d'), - array('url' => 'https://127.0.0.1:8443/redmine', 'project_id' => 'fedora-20', - 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d')); - -// e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8 -// curl -v -H "Content-Type: application/xml" -X POST --data "@issue.xml" -H "X-Redmine-API-Key: e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8" http://192.168.1.2/issues.xml - -$targetSite = 3; -$red = new redmine($site[$targetSite]['url'],$site[$targetSite]['apiKey']); - -echo 'Target Installation:' . $site[$targetSite]['url'] . '
    '; - -//var_dump($red); - - -$issueObj = $red->getProjects(); -//var_dump($issueObj); - -$issueNumber = 10; -echo 'Getting ISSUE #' . $issueNumber . '
    '; -$issueObj = $red->getIssue($issueNumber); - -//var_dump($issueObj); - -echo '
    '; -echo 'Summary(SUBJECT):' .(string)$issueObj->subject . '
    '; -echo 'Status: Name/ID' . (string)$issueObj->status['name'] . '/' . (int)$issueObj->status['id'] . '
    '; -echo '

    ';
    -echo '
    '; - - -$xml = new SimpleXMLElement(''); -$xml->addChild('subject', htmlentities($site[$targetSite]['url'] . ' - TEST SUBJECT - rand:' . rand())); - -// $xml->addChild('subject', -// '/PG-TestProject-TR/TS100/QA-1:TC-A - Executed ON (ISO FORMAT): 2014-02-17 11:34:56Execution ID: 3 Tester: admin Test Plan: sdsd Build: sds Executed ON (ISO FORMAT): 2014-02-17 11:34:56 Execution Status: failed fedora-201'); - -$xml->addChild('project_id', $site[$targetSite]['project_id']); -$xml->addChild('tracker_id', 1); -$red->addIssueFromSimpleXML($xml); - -/* -What you get when things work ok - - - 11 - - - - - - API TEST FMAN - - 2012-11-18 - - 0 - - 0.0 - Sun Nov 18 10:26:02 +0000 2012 - Sun Nov 18 10:26:02 +0000 2012 - - - - - - - -*/ - + 'http://192.168.1.174','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), +// $site = array(array('url' => 'http://192.168.1.2','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), +$site = array( + array( + 'url' => 'http://192.168.1.74', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'project_id' => 'tl-rest', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ), + array( + 'url' => 'http://127.0.0.1:8085/redmine', + 'project_id' => 'fedora-20', + 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d' + ), + array( + 'url' => 'https://127.0.0.1:8443/redmine', + 'project_id' => 'fedora-20', + 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d' + ) +); + +// e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8 +// curl -v -H "Content-Type: application/xml" -X POST --data "@issue.xml" -H "X-Redmine-API-Key: e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8" http://192.168.1.2/issues.xml + +$targetSite = 3; +$red = new redmine($site[$targetSite]['url'], $site[$targetSite]['apiKey']); + +echo 'Target Installation:' . $site[$targetSite]['url'] . '
    '; + +$issueObj = $red->getProjects(); + +$issueNumber = 10; +echo 'Getting ISSUE #' . $issueNumber . '
    '; +$issueObj = $red->getIssue($issueNumber); + +echo '
    '; +echo 'Summary(SUBJECT):' . (string) $issueObj->subject . '
    '; +echo 'Status: Name/ID' . (string) $issueObj->status['name'] . '/' . + (int) $issueObj->status['id'] . '
    '; +echo '

    ';
    +echo '
    '; + +$xml = new SimpleXMLElement(''); +$xml->addChild('subject', + htmlentities($site[$targetSite]['url'] . ' - TEST SUBJECT - rand:' . rand())); + +$xml->addChild('project_id', $site[$targetSite]['project_id']); +$xml->addChild('tracker_id', 1); +$red->addIssueFromSimpleXML($xml); + +/* + * What you get when things work ok + * + * + * 11 + * + * + * + * + * + * API TEST FMAN + * + * 2012-11-18 + * + * 0 + * + * 0.0 + * Sun Nov 18 10:26:02 +0000 2012 + * Sun Nov 18 10:26:02 +0000 2012 + * + * + * + * + * + * + * + */ + die(); /* 20120328 20:44 -object(redmine)#1 (4) { ["url"]=> string(25) "http://fman.m.redmine.org" -["apiKey"]=> string(40) "b956de40bf8baf6af7344b759cd9471832f33922" -["curl"]=> resource(3) of type (curl) ["headers":"redmine":private]=> array(0) { } } +object(redmine)#1 (4) { ["url"]=> string(25) "http://fman.m.redmine.org" +["apiKey"]=> string(40) "b956de40bf8baf6af7344b759cd9471832f33922" +["curl"]=> resource(3) of type (curl) ["headers":"redmine":private]=> array(0) { } } /issues.xml?key=b956de40bf8baf6af7344b759cd9471832f33922 -http://fman.m.redmine.org/issues.xml?key=b956de40bf8baf6af7344b759cd9471832f33922object(SimpleXMLElement)#2 (2) +http://fman.m.redmine.org/issues.xml?key=b956de40bf8baf6af7344b759cd9471832f33922object(SimpleXMLElement)#2 (2) { ["@attributes"]=> array(4) { ["limit"]=> string(2) "25" ["type"]=> string(5) - "array" ["total_count"]=> string(1) "1" ["offset"]=> string(1) "0" } + "array" ["total_count"]=> string(1) "1" ["offset"]=> string(1) "0" } ["issue"]=> object(SimpleXMLElement)#3 (14) { ["id"]=> string(1) "1" ["project"]=> object(SimpleXMLElement)#4 (1) { ["@attributes"]=> array(2) { ["name"]=> string(11) "fman-prj001" ["id"]=> string(1) "1" } } - ["tracker"]=> object(SimpleXMLElement)#5 (1) { ["@attributes"]=> array(2) - { ["name"]=> string(3) "Bug" ["id"]=> string(1) "1" } } - ["status"]=> object(SimpleXMLElement)#6 (1) { ["@attributes"]=> array(2) - { ["name"]=> string(3) "New" ["id"]=> string(1) "1" } } ["priority"]=> object(SimpleXMLElement)#7 (1) + ["tracker"]=> object(SimpleXMLElement)#5 (1) { ["@attributes"]=> array(2) + { ["name"]=> string(3) "Bug" ["id"]=> string(1) "1" } } + ["status"]=> object(SimpleXMLElement)#6 (1) { ["@attributes"]=> array(2) + { ["name"]=> string(3) "New" ["id"]=> string(1) "1" } } ["priority"]=> object(SimpleXMLElement)#7 (1) { ["@attributes"]=> array(2) { ["name"]=> string(6) "Urgent" ["id"]=> string(1) "6" } } - ["author"]=> object(SimpleXMLElement)#8 (1) { ["@attributes"]=> array(2) - { ["name"]=> string(13) "Redmine Admin" ["id"]=> string(1) "2" } } ["subject"]=> string(11) - "fman-prj001" ["description"]=> string(11) "fman-prj001" ["start_date"]=> string(10) "2012-03-28" - ["due_date"]=> object(SimpleXMLElement)#9 (0) { } ["done_ratio"]=> string(1) "0" - ["estimated_hours"]=> object(SimpleXMLElement)#10 (0) { } ["created_on"]=> string(25) - "2012-03-28T20:41:21+02:00" ["updated_on"]=> string(25) "2012-03-28T20:41:21+02:00" } } -*/ \ No newline at end of file + ["author"]=> object(SimpleXMLElement)#8 (1) { ["@attributes"]=> array(2) + { ["name"]=> string(13) "Redmine Admin" ["id"]=> string(1) "2" } } ["subject"]=> string(11) + "fman-prj001" ["description"]=> string(11) "fman-prj001" ["start_date"]=> string(10) "2012-03-28" + ["due_date"]=> object(SimpleXMLElement)#9 (0) { } ["done_ratio"]=> string(1) "0" + ["estimated_hours"]=> object(SimpleXMLElement)#10 (0) { } ["created_on"]=> string(25) + "2012-03-28T20:41:21+02:00" ["updated_on"]=> string(25) "2012-03-28T20:41:21+02:00" } } +*/ diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-raw-api.01.php b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-raw-api.01.php index c56ccac841..7fb7800e1d 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redmine-raw-api.01.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redmine-raw-api.01.php @@ -1,17 +1,26 @@ - 'http://192.168.1.174','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922')); - -$red = new redmine($site[0]['url'],$site[0]['apiKey']); -$issueObj = $red->getIssue(3); -var_dump($issueObj); -die(); - -echo '
    '; -echo 'Summary(SUBJECT):' .(string)$issueObj->subject . '
    '; -echo 'Status: Name/ID' . (string)$issueObj->status['name'] . '/' . (int)$issueObj->status['id'] . '
    '; -echo '

    ';
    -echo '
    '; -?> \ No newline at end of file + 'http://192.168.1.174', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ) +); + +$red = new redmine($site[0]['url'], $site[0]['apiKey']); +$issueObj = $red->getIssue(3); +var_dump($issueObj); +die(); + +echo '
    '; +echo 'Summary(SUBJECT):' . (string) $issueObj->subject . '
    '; +echo 'Status: Name/ID' . (string) $issueObj->status['name'] . '/' . + (int) $issueObj->status['id'] . '
    '; +echo '

    ';
    +echo '
    '; +?> diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redmine.01.php b/lib/issuetrackerintegration/code_testing/redmine/test.redmine.01.php index c56ccac841..7fb7800e1d 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redmine.01.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redmine.01.php @@ -1,17 +1,26 @@ - 'http://192.168.1.174','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922')); - -$red = new redmine($site[0]['url'],$site[0]['apiKey']); -$issueObj = $red->getIssue(3); -var_dump($issueObj); -die(); - -echo '
    '; -echo 'Summary(SUBJECT):' .(string)$issueObj->subject . '
    '; -echo 'Status: Name/ID' . (string)$issueObj->status['name'] . '/' . (int)$issueObj->status['id'] . '
    '; -echo '

    ';
    -echo '
    '; -?> \ No newline at end of file + 'http://192.168.1.174', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ) +); + +$red = new redmine($site[0]['url'], $site[0]['apiKey']); +$issueObj = $red->getIssue(3); +var_dump($issueObj); +die(); + +echo '
    '; +echo 'Summary(SUBJECT):' . (string) $issueObj->subject . '
    '; +echo 'Status: Name/ID' . (string) $issueObj->status['name'] . '/' . + (int) $issueObj->status['id'] . '
    '; +echo '

    ';
    +echo '
    '; +?> diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redmine.getprojects.php b/lib/issuetrackerintegration/code_testing/redmine/test.redmine.getprojects.php index 00bbe693a9..1ad193014e 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redmine.getprojects.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redmine.getprojects.php @@ -1,39 +1,39 @@ - 'http://192.168.1.174','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), -// $site = array(array('url' => 'http://192.168.1.2','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), -$site = array(array('url' => 'http://192.168.1.74','apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'), - array('url' => 'http://tl.m.redmine.org', 'project_id' => 'tl-rest', - 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922'), - array('url' => 'http://127.0.0.1:8085/redmine', 'project_id' => 'fedora-20', - 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d'), - array('url' => 'https://127.0.0.1:8443/redmine', 'project_id' => 'fedora-20', - 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d')); - -// e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8 -// curl -v -H "Content-Type: application/xml" -X POST --data "@issue.xml" -H "X-Redmine-API-Key: e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8" http://192.168.1.2/issues.xml - -$targetSite = 1; - -$pxy = new stdClass(); -$pxy->proxy = config_get('proxy'); -var_dump($pxy); - -$red = new redmine($site[$targetSite]['url'], - $site[$targetSite]['apiKey'],$pxy); - -echo 'Target Installation:' . $site[$targetSite]['url'] . '
    '; -$result = $red->getProjects(); + 'http://192.168.1.74', + 'apiKey' => 'e6f1cbed7469528389554cffcb0e5aa4e0fa0bc8' + ), + array( + 'url' => 'http://tl.m.redmine.org', + 'project_id' => 'tl-rest', + 'apiKey' => 'b956de40bf8baf6af7344b759cd9471832f33922' + ), + array( + 'url' => 'http://127.0.0.1:8085/redmine', + 'project_id' => 'fedora-20', + 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d' + ), + array( + 'url' => 'https://127.0.0.1:8443/redmine', + 'project_id' => 'fedora-20', + 'apiKey' => '630e3a09b365757458c4039257f7ee57e87cec5d' + ) +); + +$targetSite = 1; + +$pxy = new stdClass(); +$pxy->proxy = config_get('proxy'); +var_dump($pxy); + +$red = new redmine($site[$targetSite]['url'], $site[$targetSite]['apiKey'], $pxy); + +echo 'Target Installation:' . $site[$targetSite]['url'] . '
    '; +$result = $red->getProjects(); var_dump($result); diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redmine.rest.php b/lib/issuetrackerintegration/code_testing/redmine/test.redmine.rest.php index b741e004c4..d9414271f5 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redmine.rest.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redmine.rest.php @@ -1,49 +1,37 @@ -find('all'); - -var_dump($x); - -//$issues = $issueMgr->find(d); -//echo '
    ' . var_dump($issues) . '
    '; -//$issuesQty = count($issues); -//for ($idx=0; $idx < $issuesQty; $idx++) -//{ -// echo $issues[$idx]->subject; -//} - -//// find and update an issue -//$issue->find (1); -//echo $issue->subject; -//$issue->set ('subject', 'This is the new subject')->save (); - -//// delete an issue -//$issue->find (1); -//$issue->destroy (); -?> \ No newline at end of file +find('all'); + +var_dump($x); + +?> diff --git a/lib/issuetrackerintegration/code_testing/redmine/test.redminerestInterface.class.php b/lib/issuetrackerintegration/code_testing/redmine/test.redminerestInterface.class.php index e3a1d493c6..7e7522284b 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test.redminerestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test.redminerestInterface.class.php @@ -1,112 +1,101 @@ -getTypes(); - -// 192.168.1.174 -$cfg = "\n" . - "AAe6f1cbed7469528389554cffcb0e5aa4e0fa0bc8\n" . - "public01\n" . - "http://192.168.1.2/\n" . - "\n"; - -echo '

    '; -echo "Testing Issue Tracker Integration - redminerestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; - -echo 'Creating INTERFACE
    '; - -$user = 'admin'; -$its[$user] = new redminerestInterface(15,$cfg); - -echo 'Connection OK?
    '; -var_dump($its[$user]->isConnected()); -if( $its[$user]->isConnected() ) -{ - - echo 'Try To Get ISSUE FROM PUBLIC PROJECT with ADMIN
    '; - $xx = $its[$user]->getIssue(23); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - echo 'Try To Get ISSUE FROM PUBLIC PROJECT with ADMIN
    '; - $xx = $its[$user]->getIssue(26); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - - echo 'Try To Get ISSUE FROM PPRIVATE PROJECT with ADMIN
    '; - $xx = $its[$user]->getIssue(3); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - echo 'Try To CREATE ISSUE ON PRIVATE PROJECT with ADMIN
    '; - - $xx = $its[$user]->addIssue('Inter', 'non vince'); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - - -} - -$cfg = "\n" . - "8530912c68e5dd52416452b0b3881acb7de94944\n" . - "public01\n" . - "http://192.168.1.174/\n" . - "\n"; - -echo '

    '; -echo "Testing BST Integration - redminerestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; - -$user= 'testlink.forum4git'; -$its[$user] = new redminerestInterface(15,$cfg); - -if( $its[$user]->isConnected() ) -{ - - echo "Try To Get ISSUE FROM PUBLIC PROJECT with $user
    "; - $xx = $its[$user]->getIssue(23); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - echo "Try To Get ISSUE FROM ***Private*** PROJECT with $user THAT HAS NO ACCESS
    "; - $xx = $its[$user]->getIssue(3); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - - echo "Try To Get ISSUE FROM ***Private*** PROJECT with $user THAT HAS ACCESS OK
    "; - $xx = $its[$user]->getIssue(24); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - -} - - -?> \ No newline at end of file +getTypes(); + +// 192.168.1.174 +$cfg = "\n" . + "AAe6f1cbed7469528389554cffcb0e5aa4e0fa0bc8\n" . + "public01\n" . + "http://192.168.1.2/\n" . "\n"; + +echo '

    '; +echo "Testing Issue Tracker Integration - redminerestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; + +echo 'Creating INTERFACE
    '; + +$user = 'admin'; +$its[$user] = new redminerestInterface(15, $cfg); + +echo 'Connection OK?
    '; +var_dump($its[$user]->isConnected()); +if ($its[$user]->isConnected()) { + + echo 'Try To Get ISSUE FROM PUBLIC PROJECT with ADMIN
    '; + $xx = $its[$user]->getIssue(23); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; + + echo 'Try To Get ISSUE FROM PUBLIC PROJECT with ADMIN
    '; + $xx = $its[$user]->getIssue(26); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; + + echo 'Try To Get ISSUE FROM PPRIVATE PROJECT with ADMIN
    '; + $xx = $its[$user]->getIssue(3); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; + + echo 'Try To CREATE ISSUE ON PRIVATE PROJECT with ADMIN
    '; + + $xx = $its[$user]->addIssue('Inter', 'non vince'); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; +} + +$cfg = "\n" . + "8530912c68e5dd52416452b0b3881acb7de94944\n" . + "public01\n" . + "http://192.168.1.174/\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - redminerestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; + +$user = 'testlink.forum4git'; +$its[$user] = new redminerestInterface(15, $cfg); + +if ($its[$user]->isConnected()) { + + echo "Try To Get ISSUE FROM PUBLIC PROJECT with $user
    "; + $xx = $its[$user]->getIssue(23); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; + + echo "Try To Get ISSUE FROM ***Private*** PROJECT with $user THAT HAS NO ACCESS
    "; + $xx = $its[$user]->getIssue(3); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; + + echo "Try To Get ISSUE FROM ***Private*** PROJECT with $user THAT HAS ACCESS OK
    "; + $xx = $its[$user]->getIssue(24); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; +} + +?> diff --git a/lib/issuetrackerintegration/code_testing/redmine/test2.redminerestInterface.class.php b/lib/issuetrackerintegration/code_testing/redmine/test2.redminerestInterface.class.php index 2e5b292ace..5822c1c29f 100644 --- a/lib/issuetrackerintegration/code_testing/redmine/test2.redminerestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/redmine/test2.redminerestInterface.class.php @@ -1,96 +1,72 @@ -getTypes(); - -new dBug($it_mgr); - - - -// 192.168.1.174 -// 20130406 - - -$apiKey['192.168.1.174']['admin'] = 'AAe6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'; -$apiKey['192.168.1.194']['user'] = 'a67dd9575e2b870c103ebb5aa04d5d85790069ca'; - -$projectidentifier['192.168.1.194'] = 'tl-one'; - -$user = 'user'; -$redmineHost = '192.168.1.194'; - -//$authKey = $apiKey[$redmineHost]['user']; - -// "2\n" . -// "12.6\n" . - -$cfg = "\n" . - "{$apiKey[$redmineHost]['user']}\n" . - "{$projectidentifier[$redmineHost]}\n" . - "21\n" . - "12.6\n" . - "\n" . - "http://$redmineHost/redmine/\n" . - "\n"; - -echo '

    '; -echo "Testing Issue Tracker Integration - redminerestInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; -echo '


    '; - -echo 'Creating INTERFACE
    '; - -$its[$user] = new redminerestInterface(15,$cfg); -// var_dump($its[$user]); - -echo "
    Connection OK? (using apikey for user: $user)
    "; -var_dump($its[$user]->isConnected()); -if( $its[$user]->isConnected() ) -{ - - echo 'Try To CREATE ISSUE
    '; - $xx = $its[$user]->addIssue('Internazionale di Milan', 'Who loves it?'); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - // @20130406 - // Without unset() working with bitnami redmine 2.3 - // Get AFTER create takes an eternity and FAILED - unset($its[$user]); - $its[$user] = new redminerestInterface(15,$cfg); - - echo 'Try To Get ISSUE
    '; - $xx = $its[$user]->getIssue(7); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - /* - echo 'Try To Get ISSUE FROM PUBLIC PROJECT with ADMIN
    '; - $xx = $its[$user]->getIssue(26); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - - echo 'Try To Get ISSUE FROM PPRIVATE PROJECT with ADMIN
    '; - $xx = $its[$user]->getIssue(3); - echo '
    ';
    -  var_dump($xx);
    -  echo '
    '; - */ -} -?> \ No newline at end of file +getTypes(); + +new dBug($it_mgr); + +// 192.168.1.174 +// 20130406 + +$apiKey['192.168.1.174']['admin'] = 'AAe6f1cbed7469528389554cffcb0e5aa4e0fa0bc8'; +$apiKey['192.168.1.194']['user'] = 'a67dd9575e2b870c103ebb5aa04d5d85790069ca'; + +$projectidentifier['192.168.1.194'] = 'tl-one'; + +$user = 'user'; +$redmineHost = '192.168.1.194'; + +// "2\n" . +// "12.6\n" . + +$cfg = "\n" . "{$apiKey[$redmineHost]['user']}\n" . + "{$projectidentifier[$redmineHost]}\n" . + "21\n" . + "12.6\n" . "\n" . + "http://$redmineHost/redmine/\n" . "\n"; + +echo '

    '; +echo "Testing Issue Tracker Integration - redminerestInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; +echo '


    '; + +echo 'Creating INTERFACE
    '; + +$its[$user] = new redminerestInterface(15, $cfg); + +echo "
    Connection OK? (using apikey for user: $user)
    "; +var_dump($its[$user]->isConnected()); +if ($its[$user]->isConnected()) { + + echo 'Try To CREATE ISSUE
    '; + $xx = $its[$user]->addIssue('Internazionale di Milan', 'Who loves it?'); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; + + // @20130406 + // Without unset() working with bitnami redmine 2.3 + // Get AFTER create takes an eternity and FAILED + unset($its[$user]); + $its[$user] = new redminerestInterface(15, $cfg); + + echo 'Try To Get ISSUE
    '; + $xx = $its[$user]->getIssue(7); + echo '
    ';
    +    var_dump($xx);
    +    echo '
    '; +} +?> diff --git a/lib/issuetrackerintegration/code_testing/trac/test.tracxmlrpc.php b/lib/issuetrackerintegration/code_testing/trac/test.tracxmlrpc.php index 4c8aaec6e4..fdddd53532 100644 --- a/lib/issuetrackerintegration/code_testing/trac/test.tracxmlrpc.php +++ b/lib/issuetrackerintegration/code_testing/trac/test.tracxmlrpc.php @@ -1,64 +1,35 @@ -\n" . - "admin\n" . - "admin\n" . - "http://192.168.1.183/hg-helloworld/\n" . - "\n"; - -echo '

    '; -echo "Testing BTS Integration - tracxmlrpcInterface "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$fakeTestProjectID = 185; -$its = new tracxmlrpcInterface($fakeTestProjectID,$cfg); -// var_dump($its); -$issueID = 1; -echo "
    Get issue id:{$issueID}"; -$xx = $its->getIssue($issueID); -new dBug($xx); -die(); -echo '
    Does issue 281579 exist? ' . ($its->checkBugIDExistence(281579) ? 'YES!!!' : 'Oh No!!!'); -echo '
    Does issue 999999 exist? ' . ($its->checkBugIDExistence(999999) ? 'YES!!!' : 'Oh No!!!'); - - - -/* -if($op) -{ - $issue2check = array( array('issue' => 11776, 'exists' => true), - array('issue' => 99999, 'exists' => false)); - - $methods = array('getBugSummaryString','getBugStatus','getBugStatusString', - 'checkBugID_existence','buildViewBugLink'); - - $if = config_get('bugInterface'); - new dBug($if); - // $xx = $if->getIssue(281579); - - // echo 'Does issue 999999 exist? ' . ($if->checkBugID_existence(999999) ? 'YES!!!' : 'Oh No!!!'); - // echo 'Does issue 281579 exist? ' . ($if->checkBugID_existence(281579) ? 'YES!!!' : 'Oh No!!!'); - - //$xx = $if->getIssue(999999); - //new dBug($xx); - $xx = $if->getBugStatus(281579); - new dBug($xx); - - $xx = $if->getBugSummaryString(281579); - new dBug($xx); - -} -*/ - -?> \ No newline at end of file +\n" . "admin\n" . + "admin\n" . + "http://192.168.1.183/hg-helloworld/\n" . + "\n"; + +echo '

    '; +echo "Testing BTS Integration - tracxmlrpcInterface "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$fakeTestProjectID = 185; +$its = new tracxmlrpcInterface($fakeTestProjectID, $cfg); +$issueID = 1; +echo "
    Get issue id:{$issueID}"; +$xx = $its->getIssue($issueID); +new dBug($xx); +die(); +echo '
    Does issue 281579 exist? ' . + ($its->checkBugIDExistence(281579) ? 'YES!!!' : 'Oh No!!!'); +echo '
    Does issue 999999 exist? ' . + ($its->checkBugIDExistence(999999) ? 'YES!!!' : 'Oh No!!!'); + +?> diff --git a/lib/issuetrackerintegration/code_testing/youtrack/test.youtrackrestInterface.class.php b/lib/issuetrackerintegration/code_testing/youtrack/test.youtrackrestInterface.class.php index d1847a2a75..87c33b0cda 100644 --- a/lib/issuetrackerintegration/code_testing/youtrack/test.youtrackrestInterface.class.php +++ b/lib/issuetrackerintegration/code_testing/youtrack/test.youtrackrestInterface.class.php @@ -1,112 +1,74 @@ -getTypes(); - -/* -$cfg = "\n" . -"\n" . -"testlink.youtrackincloud\n" . -"youtrackincloud2012\n" . -"http://testlink.myjetbrains.com/youtrack\n" . -"http://testlink.myjetbrains.com/youtrack/rest/\n" . -"http://testlink.myjetbrains.com/youtrack/issue/\n" . -"http://testlink.myjetbrains.com/youtrack/dashboard#newissue=yes\n" . -"\n"; - -$cfg = "\n" . -"\n" . -"testlink.integration\n" . -"integration.testlink\n" . -"http://testlink.myjetbrains.com/youtrack\n" . -"\n"; - -"http://fman.myjetbrains.com/youtrack/rest/\n" . -"http://fman.myjetbrains.com/youtrack/issue/\n" . -"http://fman.myjetbrains.com/youtrack/dashboard#newissue=yes\n" . - - -*/ -$cfg = "\n" . -"\n" . -"testlink.forum@gmail.com\n" . -"qazwsxedc\n" . -"http://fman.myjetbrains.com/youtrack\n" . -"fpo\n" . -"\n"; - -echo '

    '; -echo "Testing BST Integration - $itfName "; -echo '

    '; -echo "Configuration settings
    "; -echo "
    " . $cfg . "
    "; - -echo '


    '; - -$its = new $itfName($itfName,$cfg); - -$issueID = "fpo-1"; -$issue = $its->getIssue($issueID); -echo '
    ' . var_dump($issue) . '
    '; -new dBug($issue); - - -$status = $its->getIssueStatusCode($issueID); -echo 'status:' . $status . '
    '; - -// var_dump($its); -$client = $its->getAPIClient(); -$yy = $client->get_state_bundle('States'); -echo '<pre>' . var_dump($yy) . '</pre>'; - - -/* -$project = 'fpo'; -$assignee = ''; -$summary = 'Hope this will be issue number 2'; -$description = ' Hell'; -$priority = $type = $subsystem = $state = $affectsVersion = $fixedVersion=$fixedInBuild=''; - -$xx = $client->create_issue($project, $assignee, $summary, $description, $priority, - $type, $subsystem, $state, $affectsVersion, $fixedVersion, $fixedInBuild); - -*/ - -$xx = $its->addIssue('ROTOLO', 'non è asado'); -new dBug($xx); -die(); - - -//$zz = $client->get_project_custom_field('krm','Priority'); - -//$zz = $client->get_enum_bundle('Types'); - -// $zz = $client->get_project_custom_fields('krm'); -//echo '
    ' . var_dump($zz) . '
    '; -//die(); -//$zz = $client->get_project_custom_field('YS','State'); -//echo '
    ' . var_dump($zz) . '
    '; - -// die(); - - -//$yy = $client->get_project_issue_states('YS'); -//echo '
    ' . var_dump($yy) . '
    '; - -//$yykrm = $client->get_project_issue_states('krm'); -//echo '
    ' . var_dump($yykrm) . '
    '; - -?> \ No newline at end of file +getTypes(); + +/* + * $cfg = "\n" . + * "\n" . + * "testlink.youtrackincloud\n" . + * "youtrackincloud2012\n" . + * "http://testlink.myjetbrains.com/youtrack\n" . + * "http://testlink.myjetbrains.com/youtrack/rest/\n" . + * "http://testlink.myjetbrains.com/youtrack/issue/\n" . + * "http://testlink.myjetbrains.com/youtrack/dashboard#newissue=yes\n" . + * "\n"; + * + * $cfg = "\n" . + * "\n" . + * "testlink.integration\n" . + * "integration.testlink\n" . + * "http://testlink.myjetbrains.com/youtrack\n" . + * "\n"; + * + * "http://fman.myjetbrains.com/youtrack/rest/\n" . + * "http://fman.myjetbrains.com/youtrack/issue/\n" . + * "http://fman.myjetbrains.com/youtrack/dashboard#newissue=yes\n" . + * + * + */ +$cfg = "\n" . "\n" . + "testlink.forum@gmail.com\n" . + "qazwsxedc\n" . + "http://fman.myjetbrains.com/youtrack\n" . + "fpo\n" . "\n"; + +echo '

    '; +echo "Testing BST Integration - $itfName "; +echo '

    '; +echo "Configuration settings
    "; +echo "
    " . $cfg . "
    "; + +echo '


    '; + +$its = new $itfName($itfName, $cfg); + +$issueID = "fpo-1"; +$issue = $its->getIssue($issueID); +echo '
    ' . var_dump($issue) . '
    '; +new dBug($issue); + +$status = $its->getIssueStatusCode($issueID); +echo 'status:' . $status . '
    '; + +$client = $its->getAPIClient(); +$yy = $client->get_state_bundle('States'); +echo '<pre>' . var_dump($yy) . '</pre>'; + +$xx = $its->addIssue('ROTOLO', 'non è asado'); +new dBug($xx); +die(); + +?> diff --git a/lib/issuetrackerintegration/fogbugzdbInterface.class.php b/lib/issuetrackerintegration/fogbugzdbInterface.class.php index 346afb1443..4e76fead06 100644 --- a/lib/issuetrackerintegration/fogbugzdbInterface.class.php +++ b/lib/issuetrackerintegration/fogbugzdbInterface.class.php @@ -1,191 +1,171 @@ -interfaceViaDB = true; - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => true); - $this->guiCfg = array('use_decoration' => true); - } - - - - /** - * Return the URL to the bugtracking page for viewing - * the bug with the given id. - * - * @param int id the bug id - * - * @return string returns a complete URL to view the bug - **/ - function buildViewBugURL($id) - { - return $this->cfg->uriview.urlencode($id); - } - - /** - * - **/ - function getIssue($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - if (!$this->isConnected()) - { - return false; - } - $sql = "/* $debugMsg */ " . - " SELECT Bug.ixBug AS id, Bug.ixStatus AS status, Status.sStatus AS statusVerbose," . - " Bug.sTitle AS summary, Bug.fOpen AS openStatus " . - " FROM Bug JOIN Status ON Status.ixStatus = Bug.ixStatus " . - " WHERE Bug.ixBug=" . intval($id); - - $rs = $this->dbConnection->fetchRowsIntoMap($sql,'id'); - - $issue = null; - if( !is_null($rs) ) - { - $issue = new stdClass(); - $issue->id = $id; - $issue->openStatus = $rs[$id]['openStatus']; - $issue->IDHTMLString = "{$id} : "; - $issue->statusCode = $rs[$id]['status']; - $issue->statusVerbose = $rs[$id]['statusVerbose']; - - $issue->statusHTMLString = $this->buildStatusHTMLString($issue); - $issue->statusColor = isset($this->status_color[$issue->statusVerbose]) ? - $this->status_color[$issue->statusVerbose] : 'white'; - - $issue->summaryHTMLString = $rs[$id]['summary']; - } - return $issue; - } - - - /** - * Returns the status of the bug with the given id - * this function is not directly called by TestLink. - * - * @return string returns the status of the given bug (if found in the db), or false else - **/ - function getBugStatus($id) - { - if (!$this->isConnected()) - { - return false; - } - $issue = $this->getIssue($id); - return is_null($issue) ? $issue : $issue->statusVerbose; - } - - - /** - * checks is bug id is present on BTS - * - * @return integer returns 1 if the bug with the given id exists - **/ - function checkBugIDExistence($id) - { - $status_ok = 0; - $issue = $this->getIssue($id); - return !is_null($issue) ? 1 : 0; - } - - - function buildViewBugLink($bugID,$addSummary = false) - { - $linkVerbose = parent::buildViewBugLink($bugID, $addSummary); - $status = $this->getBugStatus($bugID); - $color = isset($this->status_color[$status]) ? $this->status_color[$status] : 'white'; - $title = lang_get('access_to_bts'); - return "
    $linkVerbose
    "; - } - - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * - * - **/ - function buildStatusHTMLString($issue) - { - // if the bug wasn't found the status is null and we simply display the bugID - $str = htmlspecialchars($issue->id); - if (!is_null($issue) ) - { - //strike through all bugs that have a closed status.. - if( $issue->statusCode > 1 ) - { - if( $issue->openStatus ) - { - // strike through and bold all bugs that have a resolved status - $str = "[resolv.] [$issue->statusVerbose] " . $id . ""; - } - else - { - $str = "[closed] [$issue->statusVerbose] " . $id . ""; - } - } - else - { - $str = "[$issue->statusVerbose] "; - } - } - return (!is_null($issue) && $issue) ? $str : null; - } - - - function getMyInterface() - { - return $this->cfg->interfacePHP; - } - - - - public static function getCfgTemplate() - { - - $template = "\n" . - "\n" . - "DATABASE SERVER NAME\n" . - "DATABASE NAME\n" . - "mysql\n" . - "USER\n" . - "PASSWORD\n" . - "\n" . - "http://[FOGBUGZ_HOST]/fogbugz/default.asp?pg=pgEditBug&command=view&ixbug=\n" . - "http://[FOGBUGZ_HOST]/fogbugz/default.asp?command=new&pg=pgEditBug\n" . - "\n"; - return $template; - } - -} -?> \ No newline at end of file +interfaceViaDB = true; + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => true + ); + $this->guiCfg = array( + 'use_decoration' => true + ); + } + + /** + * Return the URL to the bugtracking page for viewing + * the bug with the given id. + * + * @param + * int id the bug id + * @return string returns a complete URL to view the bug + */ + public function buildViewBugURL($id) + { + return $this->cfg->uriview . urlencode($id); + } + + /** + */ + public function getIssue($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + if (! $this->isConnected()) { + return false; + } + $sql = "/* $debugMsg */ " . + " SELECT Bug.ixBug AS id, Bug.ixStatus AS status, Status.sStatus AS statusVerbose," . + " Bug.sTitle AS summary, Bug.fOpen AS openStatus " . + " FROM Bug JOIN Status ON Status.ixStatus = Bug.ixStatus " . + " WHERE Bug.ixBug=" . intval($id); + + $rs = $this->dbConnection->fetchRowsIntoMap($sql, 'id'); + + $issue = null; + if (! is_null($rs)) { + $issue = new stdClass(); + $issue->id = $id; + $issue->openStatus = $rs[$id]['openStatus']; + $issue->IDHTMLString = "{$id} : "; + $issue->statusCode = $rs[$id]['status']; + $issue->statusVerbose = $rs[$id]['statusVerbose']; + + $issue->statusHTMLString = $this->buildStatusHTMLString($issue); + $issue->statusColor = isset( + $this->status_color[$issue->statusVerbose]) ? $this->status_color[$issue->statusVerbose] : 'white'; + + $issue->summaryHTMLString = $rs[$id]['summary']; + } + return $issue; + } + + /** + * Returns the status of the bug with the given id + * this function is not directly called by TestLink. + * + * @return string returns the status of the given bug (if found in the db), or false else + */ + public function getBugStatus($id) + { + if (! $this->isConnected()) { + return false; + } + $issue = $this->getIssue($id); + return is_null($issue) ? $issue : $issue->statusVerbose; + } + + /** + * checks is bug id is present on BTS + * + * @return integer returns 1 if the bug with the given id exists + */ + public function checkBugIDExistence($id) + { + $issue = $this->getIssue($id); + return ! is_null($issue) ? 1 : 0; + } + + public function buildViewBugLink($bugID, $addSummary = false) + { + $linkVerbose = parent::buildViewBugLink($bugID, $addSummary); + $status = $this->getBugStatus($bugID); + $color = isset($this->status_color[$status]) ? $this->status_color[$status] : 'white'; + $title = lang_get('access_to_bts'); + return "
    $linkVerbose
    "; + } + + /** + * checks id for validity + * + * @param + * string issueID + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + */ + private function buildStatusHTMLString($issue) + { + // if the bug wasn't found the status is null and we simply display the bugID + $str = htmlspecialchars($issue->id); + if (! is_null($issue)) { + // strike through all bugs that have a closed status.. + if ($issue->statusCode > 1) { + if ($issue->openStatus) { + // strike through and bold all bugs that have a resolved status + $str = "[resolv.] [$issue->statusVerbose] " . $id . + ""; + } else { + $str = "[closed] [$issue->statusVerbose] " . $id . + ""; + } + } else { + $str = "[$issue->statusVerbose] "; + } + } + return (! is_null($issue) && $issue) ? $str : null; + } + + public function getMyInterface() + { + return $this->cfg->interfacePHP; + } + + public static function getCfgTemplate() + { + return "\n" . "\n" . + "DATABASE SERVER NAME\n" . + "DATABASE NAME\n" . "mysql\n" . + "USER\n" . "PASSWORD\n" . + "\n" . + "http://[FOGBUGZ_HOST]/fogbugz/default.asp?pg=pgEditBug&command=view&ixbug=\n" . + "http://[FOGBUGZ_HOST]/fogbugz/default.asp?command=new&pg=pgEditBug\n" . + "\n"; + } +} +?> diff --git a/lib/issuetrackerintegration/fogbugzrestInterface.class.php b/lib/issuetrackerintegration/fogbugzrestInterface.class.php index c5b84440f6..175f1af263 100644 --- a/lib/issuetrackerintegration/fogbugzrestInterface.class.php +++ b/lib/issuetrackerintegration/fogbugzrestInterface.class.php @@ -1,279 +1,272 @@ -name = $name; - $this->interfaceViaDB = false; - $this->methodOpt = array('buildViewBugLink' => array('addSummary' => true, 'colorByStatus' => true)); - - if( !$this->setCfg($config) ) - { - return false; - } - $this->completeCfg(); - $this->connect(); - } - - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/'; // be sure no double // at end - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'default.asp?command=view&pg=pgEditBug&ixbug='; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base . 'default.asp?command=new&pg=pgEditBug'; - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - try - { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $this->APIClient = new FogBugz((string)trim($this->cfg->username),(string)trim($this->cfg->password), - (string)trim($this->cfg->uribase)); - $this->APIClient->logon(); - $this->connected = true; - } - catch(Exception $e) - { - $logDetails = ''; - foreach(array('uribase','username','password') as $v) - { - $logDetails .= "$v={$this->cfg->$v} / "; - } - $logDetails = trim($logDetails,'/ '); - $this->connected = false; - tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - public function getIssue($issueID) - { - if (!$this->isConnected()) - { - $msg = __METHOD__ . ' Not Connected '; - tLog($msg,'ERROR'); - return false; - } - - try - { - $target = array('q' => intval($issueID), 'cols' => 'sTitle,sStatus'); - $xml = $this->APIClient->search($target); - if( !is_null($xml) && is_object($xml) ) - { - $issue = new stdClass(); - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = (string)$xml->cases->case->sStatus; - $issue->statusVerbose = $issue->statusCode; - $issue->statusHTMLString = "[$issue->statusCode] "; - $issue->summary = $issue->summaryHTMLString = (string)$xml->cases->case->sTitle; - } - } - catch(Exception $e) - { - $msg = __METHOD__ . '/' . $e->getMessage(); - tLog($msg,'ERROR'); - $issue = null; - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return (!is_null($issue) && is_object($issue)) ? $issue->statusCode : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - return $this->getIssueStatusCode($issueID); - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) - { - $issue = $this->getIssue($issueID); - return (!is_null($issue) && is_object($issue)) ? $issue->summaryHTMLString : null; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = !is_null($issue) && is_object($issue); - } - return $status_ok; - } - - - /** - * - */ - public function addIssue($summary,$description) - { - try - { - $projectName = (string)$this->cfg->project; - $issue = array('sProject' => htmlentities($projectName), - 'sTitle' => htmlentities($summary), - 'sEvent' => htmlentities($description)); - - // just for the record APIClient->NAME OF FogBugz command - $op = $this->APIClient->new($issue); - $ret = array('status_ok' => true, 'id' => (string)$op->case['ixBug'], - 'msg' => sprintf(lang_get('fogbugz_bug_created'),$summary,$projectName)); - } - catch (Exception $e) - { - $msg = "Create FOGBUGZ Ticket FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - } - return $ret; - } - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - - // https://testlink.fogbugz.com/fogbugz/default.asp?pg=pgEditBug&command=view&ixbug= - $template = "\n" . - "\n" . - "FOGBUGZ LOGIN NAME\n" . - "FOGBUGZ PASSWORD\n" . - "https://testlink.fogbugz.com\n" . - "\n" . - "FOGBUGZ PROJECT NAME\n" . - "\n"; - return $template; - } - - /** - * - **/ - function canCreateViaAPI() - { - return (property_exists($this->cfg, 'project')); - } - -} \ No newline at end of file +name = $name; + $this->interfaceViaDB = false; + $this->methodOpt = array( + 'buildViewBugLink' => array( + 'addSummary' => true, + 'colorByStatus' => true + ) + ); + + if (! $this->setCfg($config)) { + return false; + } + $this->completeCfg(); + $this->connect(); + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; // be sure no double // at end + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . + 'default.asp?command=view&pg=pgEditBug&ixbug='; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base . + 'default.asp?command=new&pg=pgEditBug'; + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + * + */ + public function connect() + { + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $this->APIClient = new FogBugz((string) trim($this->cfg->username), + (string) trim($this->cfg->password), + (string) trim($this->cfg->uribase)); + $this->APIClient->logon(); + $this->connected = true; + } catch (Exception $e) { + $logDetails = ''; + foreach (array( + 'uribase', + 'username', + 'password' + ) as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + $msg = __METHOD__ . ' Not Connected '; + tLog($msg, 'ERROR'); + return false; + } + + try { + $target = array( + 'q' => intval($issueID), + 'cols' => 'sTitle,sStatus' + ); + $xml = $this->APIClient->search($target); + if (! is_null($xml) && is_object($xml)) { + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = (string) $xml->cases->case->sStatus; + $issue->statusVerbose = $issue->statusCode; + $issue->statusHTMLString = "[$issue->statusCode] "; + $issue->summary = $issue->summaryHTMLString = (string) $xml->cases->case->sTitle; + } + } catch (Exception $e) { + $msg = __METHOD__ . '/' . $e->getMessage(); + tLog($msg, 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return (! is_null($issue) && is_object($issue)) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + * + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * + * @param + * string issueID + * @return string + * + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return (! is_null($issue) && is_object($issue)) ? $issue->summaryHTMLString : null; + } + + /** + * + * @param + * string issueID + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = ! is_null($issue) && is_object($issue); + } + return $status_ok; + } + + /** + */ + public function addIssue($summary, $description) + { + try { + $projectName = (string) $this->cfg->project; + $issue = array( + 'sProject' => htmlentities($projectName), + 'sTitle' => htmlentities($summary), + 'sEvent' => htmlentities($description) + ); + + // just for the record APIClient->NAME OF FogBugz command + $op = $this->APIClient->new($issue); + $ret = array( + 'status_ok' => true, + 'id' => (string) $op->case['ixBug'], + 'msg' => sprintf(lang_get('fogbugz_bug_created'), $summary, + $projectName) + ); + } catch (Exception $e) { + $msg = "Create FOGBUGZ Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + + // https://testlink.fogbugz.com/fogbugz/default.asp?pg=pgEditBug&command=view&ixbug= + return "\n" . "\n" . + "FOGBUGZ LOGIN NAME\n" . + "FOGBUGZ PASSWORD\n" . + "https://testlink.fogbugz.com\n" . + "\n" . + "FOGBUGZ PROJECT NAME\n" . "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return property_exists($this->cfg, 'project'); + } +} diff --git a/lib/issuetrackerintegration/gforgesoapInterface.class.php b/lib/issuetrackerintegration/gforgesoapInterface.class.php index e3a7e7325a..299e37871a 100644 --- a/lib/issuetrackerintegration/gforgesoapInterface.class.php +++ b/lib/issuetrackerintegration/gforgesoapInterface.class.php @@ -1,420 +1,364 @@ - 'its_duedate_with_separator'); - - private $soapOpt = array("connection_timeout" => 1, 'exceptions' => 1); - - /** - * Construct and connect to BTS. - * - * @param str $type (see tlIssueTracker.class.php $systems property) - * @param xml $cfg - **/ - function __construct($type,$config,$name) - { - $this->name = $name; - $this->interfaceViaDB = false; - if( !$this->setCfg($config) ) - { - return false; - } - - $this->completeCfg(); - $this->connect(); - } - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/' ; - if( !property_exists($this->cfg,'uriwsdl') ) - { - $this->cfg->uriwsdl = $base . 'gf/xmlcompatibility/soap5/?wsdl'; - } - - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'browse/'; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base . 'gf/'; - } - } - - - function getAuthToken() - { - return $this->authToken; - } - - - /** - * status code (integer) for issueID - * - * - * @param string issueID - * - * @return - **/ - public function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->statusCode : false; - } - - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->statusVerbose : null; - } - - - /** - * - * @param string issueID - * - * @return string returns the bug summary if bug is found, else null - **/ - function getIssueSummary($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->summary : null; - } - - /** - * @internal precondition: TestLink has to be connected to BTS - * - * @param string issueID - * - **/ - function getIssue($issueID) - { - try - { - $issue = $this->APIClient->getTrackerItemFull($this->authToken, $issueID); - new dBug($issue); - - echo 'QTY extra_field_data:' . count($issue->extra_field_data) . '
    '; - $target = array(); - $dataID = array(); - foreach($issue->extra_field_data as $efd) - { - $target[] = $efd->tracker_extra_field_id; - $dataID[] = $efd->tracker_extra_field_data_id; - } - - $extraFields = $this->APIClient->getTrackerExtraFieldArray($this->authToken, - $issue->tracker_id, $target); - - new dBug($extraFields); - $idx=0; - foreach($extraFields as $ef) - { - if($ef->field_name == 'Status') - { - echo 'Status FOUND on idx=' . $idx . '
    '; - $ef->tracker_extra_field_data_id = $dataID[$idx]; - $statusObj = $ef; - break; - } - $idx++; - } - new dBug($statusObj); - - $zz = $this->APIClient->getTrackerExtraField($this->authToken, - $issue->tracker_id, - $statusObj->tracker_extra_field_id); - - new dBug($zz); - - //$yy = $this->APIClient->getTrackerExtraFieldElementArray($this->authToken, - // $issue->tracker_id, (array)$statusObj->tracker_extra_field_id); - - // new dBug($yy); - // echo $statusObj->tracker_extra_field_data_id; - // $yy = $this->APIClient->getTrackerExtraFieldData($this->authToken,$issue->tracker_item_id,87191); - // // $statusObj->tracker_extra_field_data_id); - - echo $this->authToken . '
    '; - // echo '$issue->tracker_item_id:' . $issue->tracker_item_id . '
    '; - // echo '$statusObj->tracker_extra_field_data_id:' . $statusObj->tracker_extra_field_data_id . '
    '; - //echo '$statusObj->tracker_extra_field_id:' . $statusObj->tracker_extra_field_id . '
    '; - //echo '
    '; - //$yy = $this->APIClient->getTrackerExtraFieldDatas($this->authToken,$issue->tracker_item_id, - // $statusObj->tracker_extra_field_id); - - // tracker_item_id:8305 - // tracker_extra_field_id:55108 - // tracker_extra_field_data_id:87191 - $yy = $this->APIClient->getTrackerExtraFieldDatas($this->authToken,8305,55108); - echo __LINE__; - new dBug($yy); - - // getTrackerExtraField(string sessionhash, int tracker_id, int tracker_extra_field_id) - - - // We are going to have a set of standard properties - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = $issue->status_id; - $issue->statusVerbose = array_search($issue->statusCode, $this->statusDomain); - $issue->statusHTMLString = $this->buildStatusHTMLString($issue->statusCode); - $issue->summaryHTMLString = $this->buildSummaryHTMLString($issue); - } - catch (Exception $e) - { - tLog("JIRA Ticket ID $issueID - " . $e->getMessage(), 'WARNING'); - $issue = null; - } - - return $issue; - } - - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxString($issueID); - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = !is_null($issue); - } - return $status_ok; - } - - /** - * establishes connection to the bugtracking system - * - * @return bool returns true if the soap connection was established and the - * wsdl could be downloaded, false else - * - **/ - function connect() - { - $this->interfaceViaDB = false; - $op = $this->getClient(array('log' => true)); - if( ($this->connected = $op['connected']) ) - { - // OK, we have got WSDL => server is up and we can do SOAP calls, but now we need - // to do a simple call with user/password only to understand if we are really connected - try - { - $this->APIClient = $op['client']; - $this->authToken = $this->APIClient->login((string)$this->cfg->username, (string)$this->cfg->password); - - $this->l18n = init_labels($this->labels); - } - catch (SoapFault $f) - { - $this->connected = false; - tLog(__CLASS__ . " - SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})","ERROR"); - } - } - return $this->connected; - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - function getClient($opt=null) - { - // IMPORTANT NOTICE - 2012-01-06 - If you are using XDEBUG, Soap Fault will not work - $res = array('client' => null, 'connected' => false, 'msg' => 'generic ko'); - $my['opt'] = array('log' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - try - { - // IMPORTANT NOTICE - // $this->cfg is a simpleXML object, then is ABSOLUTELY CRITICAL - // DO CAST any member before using it. - // If we do following call WITHOUT (string) CAST, SoapClient() fails - // complaining '... wsdl has to be an STRING or null ...' - $res['client'] = new SoapClient((string)$this->cfg->uriwsdl,$this->soapOpt); - $res['connected'] = true; - $res['msg'] = 'iupi!!!'; - } - catch (SoapFault $f) - { - $res['connected'] = false; - $res['msg'] = "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; - if($my['opt']['log']) - { - tLog("SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})","ERROR"); - } - } - return $res; - } - - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - private function helperParseDate($date2parse) - { - $ret = null; - if (!is_null($date2parse)) - { - $ret = date_parse($date2parse); - $ret = ((gmmktime(0, 0, 0, $ret['month'], $ret['day'], $ret['year']))); - $ret = $this->l18n['duedate'] . gmstrftime("%d %b %Y",($ret)); - } - return $ret ; - } - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $template = "\n" . - "\n" . - "GFORGE LOGIN NAME\n" . - "GFORGE PASSWORD\n" . - "http://gforge.org/\n" . - "\n"; - return $template; - } - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public function getStatusDomain() - { - return $this->statusDomain; - } - - /** - * - **/ - function buildStatusHTMLString($statusCode) - { - $str = array_search($statusCode, $this->statusDomain); - if (strcasecmp($str, 'closed') == 0 || strcasecmp($str, 'resolved') == 0 ) - { - $str = "" . $str . ""; - } - return "[{$str}] "; - } - - /** - * - **/ - function buildSummaryHTMLString($issue) - { - $summary = $issue->summary; - $strDueDate = $this->helperParseDate($issue->duedate); - if( !is_null($strDueDate) ) - { - $summary .= " [$strDueDate] "; - } - return $summary; - } - - public static function checkEnv() - { - $ret = array(); - $ret['status'] = extension_loaded('soap'); - $ret['msg'] = $ret['status'] ? 'OK' : 'You need to enable SOAP extension'; - return $ret; - } - - -/* -getTrackerItem() - -$issue (object) -tracker_id 371 -tracker_item_id 7091 -status_id 0 -priority 3 -submitted_by 14030 -open_date 2011-03-31 03:00:00 -close_date 2011-04-07 03:00:00 -summary Add support for Gforge6 -details Add support for Gforge As 6.0 (hierarchical tracker items) -last_modified_date 2011-05-26 20:31:40 -last_modified_by 14030 -sort_order 0 -parent_id 0 -has_subitems [empty string] -subitems_count 0 -*/ - -} -?> \ No newline at end of file + 'its_duedate_with_separator' + ); + + private $soapOpt = array( + "connection_timeout" => 1, + 'exceptions' => 1 + ); + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + $this->name = $name; + $this->interfaceViaDB = false; + if (! $this->setCfg($config)) { + return false; + } + + $this->completeCfg(); + $this->connect(); + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; + if (! property_exists($this->cfg, 'uriwsdl')) { + $this->cfg->uriwsdl = $base . 'gf/xmlcompatibility/soap5/?wsdl'; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'browse/'; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base . 'gf/'; + } + } + + public function getAuthToken() + { + return $this->authToken; + } + + /** + * status code (integer) for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusVerbose : null; + } + + /** + * + * @param + * string issueID + * @return string returns the bug summary if bug is found, else null + */ + public function getIssueSummary($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->summary : null; + } + + /** + * + * @internal precondition: TestLink has to be connected to BTS + * @param + * string issueID + */ + public function getIssue($issueID) + { + try { + $issue = $this->APIClient->getTrackerItemFull($this->authToken, + $issueID); + new dBug($issue); + + echo 'QTY extra_field_data:' . count($issue->extra_field_data) . + '
    '; + $target = array(); + $dataID = array(); + foreach ($issue->extra_field_data as $efd) { + $target[] = $efd->tracker_extra_field_id; + $dataID[] = $efd->tracker_extra_field_data_id; + } + + $extraFields = $this->APIClient->getTrackerExtraFieldArray( + $this->authToken, $issue->tracker_id, $target); + + new dBug($extraFields); + $idx = 0; + foreach ($extraFields as $ef) { + if ($ef->field_name == 'Status') { + echo 'Status FOUND on idx=' . $idx . '
    '; + $ef->tracker_extra_field_data_id = $dataID[$idx]; + $statusObj = $ef; + break; + } + $idx ++; + } + new dBug($statusObj); + + $zz = $this->APIClient->getTrackerExtraField($this->authToken, + $issue->tracker_id, $statusObj->tracker_extra_field_id); + + new dBug($zz); + + echo $this->authToken . '
    '; + + // tracker_item_id:8305 + // tracker_extra_field_id:55108 + // tracker_extra_field_data_id:87191 + $yy = $this->APIClient->getTrackerExtraFieldDatas($this->authToken, + 8305, 55108); + echo __LINE__; + new dBug($yy); + + // getTrackerExtraField(string sessionhash, int tracker_id, int tracker_extra_field_id) + + // We are going to have a set of standard properties + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = $issue->status_id; + $issue->statusVerbose = array_search($issue->statusCode, + $this->statusDomain); + $issue->statusHTMLString = $this->buildStatusHTMLString( + $issue->statusCode); + $issue->summaryHTMLString = $this->buildSummaryHTMLString($issue); + } catch (Exception $e) { + tLog("JIRA Ticket ID $issueID - " . $e->getMessage(), 'WARNING'); + $issue = null; + } + + return $issue; + } + + /** + * checks id for validity + * + * @param + * string issueID + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxString($issueID); + } + + /** + * + * @param + * string issueID + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = ! is_null($issue); + } + return $status_ok; + } + + /** + * establishes connection to the bugtracking system + * + * @return bool returns true if the soap connection was established and the + * wsdl could be downloaded, false else + */ + public function connect() + { + $this->interfaceViaDB = false; + $op = $this->getClient(array( + 'log' => true + )); + if ($this->connected = $op['connected']) { + // OK, we have got WSDL => server is up and we can do SOAP calls, but now we need + // to do a simple call with user/password only to understand if we are really connected + try { + $this->APIClient = $op['client']; + $this->authToken = $this->APIClient->login( + (string) $this->cfg->username, (string) $this->cfg->password); + + $this->l18n = init_labels($this->labels); + } catch (SoapFault $f) { + $this->connected = false; + tLog( + __CLASS__ . + " - SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})", + "ERROR"); + } + } + return $this->connected; + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + private function getClient($opt = null) + { + // IMPORTANT NOTICE - 2012-01-06 - If you are using XDEBUG, Soap Fault will not work + $res = array( + 'client' => null, + 'connected' => false, + 'msg' => 'generic ko' + ); + $my['opt'] = array( + 'log' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + try { + // IMPORTANT NOTICE + // $this->cfg is a simpleXML object, then is ABSOLUTELY CRITICAL + // DO CAST any member before using it. + // If we do following call WITHOUT (string) CAST, SoapClient() fails + // complaining '... wsdl has to be an STRING or null ...' + $res['client'] = new SoapClient((string) $this->cfg->uriwsdl, + $this->soapOpt); + $res['connected'] = true; + $res['msg'] = 'iupi!!!'; + } catch (SoapFault $f) { + $res['connected'] = false; + $res['msg'] = "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; + if ($my['opt']['log']) { + tLog( + "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})", + "ERROR"); + } + } + return $res; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + private function helperParseDate($date2parse) + { + $ret = null; + if (! is_null($date2parse)) { + $ret = date_parse($date2parse); + $ret = (gmmktime(0, 0, 0, $ret['month'], $ret['day'], $ret['year'])); + $ret = $this->l18n['duedate'] . @gmstrftime("%d %b %Y", ($ret)); + } + return $ret; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "GFORGE LOGIN NAME\n" . + "GFORGE PASSWORD\n" . + "http://gforge.org/\n" . "\n"; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public function getStatusDomain() + { + return $this->statusDomain; + } + + /** + */ + private function buildStatusHTMLString($statusCode) + { + $str = array_search($statusCode, $this->statusDomain); + if (strcasecmp($str, 'closed') == 0 || strcasecmp($str, 'resolved') == 0) { + $str = "" . $str . ""; + } + return "[{$str}] "; + } + + /** + */ + private function buildSummaryHTMLString($issue) + { + $summary = $issue->summary; + $strDueDate = $this->helperParseDate($issue->duedate); + if (! is_null($strDueDate)) { + $summary .= " [$strDueDate] "; + } + return $summary; + } + + public static function checkEnv() + { + $ret = array(); + $ret['status'] = extension_loaded('soap'); + $ret['msg'] = $ret['status'] ? 'OK' : 'You need to enable SOAP extension'; + return $ret; + } +} +?> diff --git a/lib/issuetrackerintegration/githubrestInterface.class.php b/lib/issuetrackerintegration/githubrestInterface.class.php new file mode 100644 index 0000000000..9bf59aa623 --- /dev/null +++ b/lib/issuetrackerintegration/githubrestInterface.class.php @@ -0,0 +1,350 @@ + + * file derived from GITlab integration done by jlguardi + * + * @internal revisions + * @since 1.9.20-fixed + * + **/ +require_once TL_ABS_PATH . '/third_party/github-php-api/lib/github-rest-api.php'; + +class githubrestInterface extends issueTrackerInterface +{ + + private $APIClient; + + private $issueDefaults; + + private $issueOtherAttr = null; + + private $translate = null; + + public $defaultResolvedStatus; + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + $this->name = $name; + $this->interfaceViaDB = false; + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => false + ); + + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 'open', + 'verbose' => 'open' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 'closed', + 'verbose' => 'closed' + ); + + if (! $this->setCfg($config)) { + return false; + } + + // http://www.github.org/issues/6843 + // "Target version" is the new display name for this property, + // but it's still named fixed_version internally and thus in the API. + // $issueXmlObj->addChild('fixed_version_id', (string)2); + $this->translate['targetversion'] = 'fixed_version_id'; + + $this->completeCfg(); + $this->setResolvedStatusCfg(); + $this->connect(); + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + if (property_exists($this->cfg, 'attributes')) { + $attr = get_object_vars($this->cfg->attributes); + foreach ($attr as $name => $elem) { + $name = (string) $name; + if (is_object($elem)) { + $elem = get_object_vars($elem); + $cc = current($elem); + $kk = key($elem); + foreach ($cc as $value) { + $this->issueOtherAttr[$name][] = array( + $kk => (string) $value + ); + } + } else { + $this->issueOtherAttr[$name] = (string) $elem; + } + } + } + + // All attributes that I do not consider mandatory + // are managed through the issueAdditionalAttributes + // + // On Redmine 1 seems to be standard for Issues/Bugs + $this->issueDefaults = array( + 'trackerid' => 1 + ); + foreach ($this->issueDefaults as $prop => $default) { + if (! isset($this->issueAttr[$prop])) { + $this->issueAttr[$prop] = $default; + } + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + $processCatch = false; + + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $url = (string) trim($this->cfg->url); + $user = (string) trim($this->cfg->user); + $apiKey = (string) trim($this->cfg->apikey); + $repo = (string) trim($this->cfg->repo); // TODO: check integer value + $owner = (string) trim($this->cfg->owner); // TODO: check integer value + $pxy = new stdClass(); + $pxy->proxy = config_get('proxy'); + $this->APIClient = new github($url, $user, $apiKey, $owner, $repo, + $pxy); + + // to undestand if connection is OK, I will ask for projects. + // I've tried to ask for users but get always ERROR from github (not able to understand why). + try { + $items = $this->APIClient->getRepo(); + $this->connected = count($items) > 0 ? true : false; + unset($items); + } catch (Exception $e) { + $processCatch = true; + } + } catch (Exception $e) { + $processCatch = true; + } + + if ($processCatch) { + $logDetails = ''; + foreach (array( + 'url', + 'user', + 'apikey', + 'owner', + 'repo' + ) as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + public function buildViewBugURL($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + return $this->APIClient->getIssueURL($issueID); + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + + $issue = null; + try { + $jsonObj = $this->APIClient->getIssue((int) $issueID); + + if (! is_null($jsonObj) && is_object($jsonObj)) { + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + $issue->id = $jsonObj->number; + $issue->url = $jsonObj->html_url; + $issue->statusCode = (string) $jsonObj->state; + $issue->statusVerbose = (string) $jsonObj->state; + $issue->statusHTMLString = "[$issue->statusVerbose] "; + $issue->summaryHTMLString = (string) $jsonObj->title . ":
    " . + (string) $jsonObj->body; + $issue->summary = (string) $jsonObj->title . ":\n" . + (string) $jsonObj->body; + $notes = $this->APIClient->getNotes((int) $issueID); + if (is_array($notes) && count($notes) > 0) { + foreach ($notes as $key => $note) { + $issue->summaryHTMLString .= "
    [Note $key]:$note->body"; + $issue->summary .= "\n[Note $key]: $note->body"; + } + } + $issue->isResolved = $this->state == 'closed'; + } + } catch (Exception $e) { + tLog(__METHOD__ . '/' . $e->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * + * @param + * string issueID + * @return string + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return $issue->summaryHTMLString; + } + + /** + * + * @param + * string issueID + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + public function addIssue($summary, $description, $opt = null) + { + try { + $op = $this->APIClient->addIssue($summary, $description); + if (is_null($op)) { + throw new Exception("Error creating issue", 1); + } + $ret = array( + 'status_ok' => true, + 'id' => (string) $op->number, + 'msg' => sprintf(lang_get('github_bug_created'), $summary, + $this->APIClient->repo) + ); + } catch (Exception $e) { + $msg = "Create github Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + */ + public function addNote($issueID, $noteText, $opt = null) + { + $op = $this->APIClient->addNote($issueID, $noteText); + if (is_null($op)) { + throw new Exception("Error setting note", 1); + } + return array( + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => sprintf(lang_get('github_bug_comment'), $op->body, + $this->APIClient->repo) + ); + } + + /** + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "github user\n" . "github TOKEN\n" . + "https://api.github.com\n" . + "GitHub Org or User\n" . + "github REPOSITORY\n" . "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return true; + } +} diff --git a/lib/issuetrackerintegration/gitlabrestInterface.class.php b/lib/issuetrackerintegration/gitlabrestInterface.class.php index f3d6556b2d..dd5ea98718 100644 --- a/lib/issuetrackerintegration/gitlabrestInterface.class.php +++ b/lib/issuetrackerintegration/gitlabrestInterface.class.php @@ -1,360 +1,348 @@ - - * - * @internal revisions - * @since 1.9.16 - * -**/ -require_once(TL_ABS_PATH . "/third_party/gitlab-php-api/lib/gitlab-rest-api.php"); -class gitlabrestInterface extends issueTrackerInterface -{ - private $APIClient; - private $issueDefaults; - private $issueOtherAttr = null; // see - private $translate = null; - - var $defaultResolvedStatus; - - /** - * Construct and connect to BTS. - * - * @param str $type (see tlIssueTracker.class.php $systems property) - * @param xml $cfg - **/ - function __construct($type,$config,$name) - { - $this->name = $name; - $this->interfaceViaDB = false; - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => false); - - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = array('code' => 3, 'verbose' => 'resolved'); - $this->defaultResolvedStatus[] = array('code' => 5, 'verbose' => 'closed'); - - if( !$this->setCfg($config) ) - { - return false; - } - - // http://www.gitlab.org/issues/6843 - // "Target version" is the new display name for this property, - // but it's still named fixed_version internally and thus in the API. - // $issueXmlObj->addChild('fixed_version_id', (string)2); - $this->translate['targetversion'] = 'fixed_version_id'; - - $this->completeCfg(); - $this->setResolvedStatusCfg(); - $this->connect(); - } - - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/'; // be sure no double // at end - if( property_exists($this->cfg,'attributes') ) - { - $attr = get_object_vars($this->cfg->attributes); - foreach ($attr as $name => $elem) - { - $name = (string)$name; - if( is_object($elem) ) - { - $elem = get_object_vars($elem); - $cc = current($elem); - $kk = key($elem); - foreach($cc as $value) - { - $this->issueOtherAttr[$name][] = array($kk => (string)$value); - } - } - else - { - $this->issueOtherAttr[$name] = (string)$elem; - } - } - } - - // All attributes that I do not consider mandatory - // are managed through the issueAdditionalAttributes - // - // On Redmine 1 seems to be standard for Issues/Bugs - $this->issueDefaults = array('trackerid' => 1); - foreach($this->issueDefaults as $prop => $default) - { - if(!isset($this->issueAttr[$prop])) - { - $this->issueAttr[$prop] = $default; - } - } - - if( property_exists($this->cfg,'custom_fields') ) - { - $cf = $this->cfg->custom_fields; - $this->cfg->custom_fields = (string)$cf->asXML(); - } - } - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - $processCatch = false; - - try - { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $redUrl = (string)trim($this->cfg->uribase); - $redAK = (string)trim($this->cfg->apikey); - $projectId = (string)trim($this->cfg->projectidentifier); //TODO: check integer value - $pxy = new stdClass(); - $pxy->proxy = config_get('proxy'); - $this->APIClient = new gitlab($redUrl,$redAK,$projectId, $pxy); - - // to undestand if connection is OK, I will ask for projects. - // I've tried to ask for users but get always ERROR from gitlab (not able to understand why). - try - { - $items = $this->APIClient->getProjects(); - $this->connected = count($items) > 0 ? true : false; - unset($items); - } - catch(Exception $e) - { - $processCatch = true; - } - } - catch(Exception $e) - { - $processCatch = true; - } - - if($processCatch) - { - $logDetails = ''; - foreach(array('uribase','apikey') as $v) - { - $logDetails .= "$v={$this->cfg->$v} / "; - } - $logDetails = trim($logDetails,'/ '); - $this->connected = false; - tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - function buildViewBugURL($issueID) - { - if (!$this->isConnected()) - { - tLog(__METHOD__ . '/Not Connected ', 'ERROR'); - return false; - } - return $this->APIClient->getIssueURL($issueID); - } - - /** - * - * - **/ - public function getIssue($issueID) - { - if (!$this->isConnected()) - { - tLog(__METHOD__ . '/Not Connected ', 'ERROR'); - return false; - } - - $issue = null; - try - { - $jsonObj = $this->APIClient->getIssue((int)$issueID); - - if( !is_null($jsonObj) && is_object($jsonObj)) - { - $issue = new stdClass(); - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = (string)$jsonObj->iid; - $issue->statusVerbose = (string)$jsonObj->state; - $issue->statusHTMLString = "[$issue->statusVerbose] "; - $issue->summary = $issue->summaryHTMLString = (string)$jsonObj->title; - $issue->gitlabProject = array('name' => (string)$jsonObj->project_id, - 'id' => (int)$jsonObj->project_id ); - - $issue->isResolved = isset($this->state); - } - } - catch(Exception $e) - { - tLog(__METHOD__ . '/' . $e->getMessage(),'ERROR'); - $issue = null; - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->statusCode : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - return $this->getIssueStatusCode($issueID); - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) - { - $issue = $this->getIssue($issueID); - return $issue->summaryHTMLString; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = is_object($issue) && !is_null($issue); - } - return $status_ok; - } - - public function addIssue($summary,$description,$opt=null) - { - try - { - $op = $this->APIClient->addIssue($summary, $description); - if(is_null($op)){ - throw new Exception("Error creating issue", 1); - } - $ret = array('status_ok' => true, 'id' => (string)$op->iid, - 'msg' => sprintf(lang_get('gitlab_bug_created'), - $summary, $this->APIClient->projectId)); - } - catch (Exception $e) - { - $msg = "Create GITLAB Ticket FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - } - return $ret; - } - - - /** - * - */ - public function addNote($issueID,$noteText,$opt=null) - { - $op = $this->APIClient->addNote($issueID, $noteText); - if(is_null($op)){ - throw new Exception("Error setting note", 1); - } - $ret = array('status_ok' => true, 'id' => (string)$op->iid, - 'msg' => sprintf(lang_get('gitlab_bug_comment'),$op->body, $this->APIClient->projectId)); - return $ret; - } - - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $tpl = "\n" . - "\n" . - "GITLAB API KEY\n" . - "https://gitlab.mydomain.com\n" . - "GITLAB PROJECT NUMERIC IDENTIFIER\n" . - "\n"; - return $tpl; - } - - /** - * - **/ - function canCreateViaAPI() - { - return true; - } - - + + * + * @internal revisions + * @since 1.9.16 + * + **/ +require_once TL_ABS_PATH . '/third_party/gitlab-php-api/lib/gitlab-rest-api.php'; + +class gitlabrestInterface extends issueTrackerInterface +{ + + private $APIClient; + + private $issueDefaults; + + private $issueOtherAttr = null; + + private $translate = null; + + public $defaultResolvedStatus; + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + $this->name = $name; + $this->interfaceViaDB = false; + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => false + ); + + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 3, + 'verbose' => 'resolved' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 5, + 'verbose' => 'closed' + ); + + if (! $this->setCfg($config)) { + return false; + } + + // http://www.gitlab.org/issues/6843 + // "Target version" is the new display name for this property, + // but it's still named fixed_version internally and thus in the API. + // $issueXmlObj->addChild('fixed_version_id', (string)2); + $this->translate['targetversion'] = 'fixed_version_id'; + + $this->completeCfg(); + $this->setResolvedStatusCfg(); + $this->connect(); + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; // be sure no double // at end + if (property_exists($this->cfg, 'attributes')) { + $attr = get_object_vars($this->cfg->attributes); + foreach ($attr as $name => $elem) { + $name = (string) $name; + if (is_object($elem)) { + $elem = get_object_vars($elem); + $cc = current($elem); + $kk = key($elem); + foreach ($cc as $value) { + $this->issueOtherAttr[$name][] = array( + $kk => (string) $value + ); + } + } else { + $this->issueOtherAttr[$name] = (string) $elem; + } + } + } + + // All attributes that I do not consider mandatory + // are managed through the issueAdditionalAttributes + // + // On Redmine 1 seems to be standard for Issues/Bugs + $this->issueDefaults = array( + 'trackerid' => 1 + ); + foreach ($this->issueDefaults as $prop => $default) { + if (! isset($this->issueAttr[$prop])) { + $this->issueAttr[$prop] = $default; + } + } + + if (property_exists($this->cfg, 'custom_fields')) { + $cf = $this->cfg->custom_fields; + $this->cfg->custom_fields = (string) $cf->asXML(); + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + $processCatch = false; + + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $redUrl = (string) trim($this->cfg->uribase); + $redAK = (string) trim($this->cfg->apikey); + $projectId = (string) trim($this->cfg->projectidentifier); // TODO: check integer value + $pxy = new stdClass(); + $pxy->proxy = config_get('proxy'); + $this->APIClient = new gitlab($redUrl, $redAK, $projectId, $pxy); + + // to undestand if connection is OK, I will ask for projects. + // I've tried to ask for users but get always ERROR from gitlab (not able to understand why). + try { + $items = $this->APIClient->getProjects(); + $this->connected = count($items) > 0 ? true : false; + unset($items); + } catch (Exception $e) { + $processCatch = true; + } + } catch (Exception $e) { + $processCatch = true; + } + + if ($processCatch) { + $logDetails = ''; + foreach (array( + 'uribase', + 'apikey' + ) as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + public function buildViewBugURL($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + return $this->APIClient->getIssueURL($issueID); + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + + $issue = null; + try { + $jsonObj = $this->APIClient->getIssue((int) $issueID); + + if (! is_null($jsonObj) && is_object($jsonObj)) { + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = (string) $jsonObj->iid; + $issue->statusVerbose = (string) $jsonObj->state; + $issue->statusHTMLString = "[$issue->statusVerbose] "; + $issue->summary = $issue->summaryHTMLString = (string) $jsonObj->title; + $issue->gitlabProject = array( + 'name' => (string) $jsonObj->project_id, + 'id' => (int) $jsonObj->project_id + ); + + $issue->isResolved = isset($this->state); + } + } catch (Exception $e) { + tLog(__METHOD__ . '/' . $e->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + * + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * + * @param + * string issueID + * @return string + * + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return $issue->summaryHTMLString; + } + + /** + * + * @param + * string issueID + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + public function addIssue($summary, $description, $opt = null) + { + try { + $op = $this->APIClient->addIssue($summary, $description); + if (is_null($op)) { + throw new Exception("Error creating issue", 1); + } + $ret = array( + 'status_ok' => true, + 'id' => (string) $op->iid, + 'msg' => sprintf(lang_get('gitlab_bug_created'), $summary, + $this->APIClient->projectId) + ); + } catch (Exception $e) { + $msg = "Create GITLAB Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + */ + public function addNote($issueID, $noteText, $opt = null) + { + $op = $this->APIClient->addNote($issueID, $noteText); + if (is_null($op)) { + throw new Exception("Error setting note", 1); + } + return array( + 'status_ok' => true, + 'id' => (string) $op->iid, + 'msg' => sprintf(lang_get('gitlab_bug_comment'), $op->body, + $this->APIClient->projectId) + ); + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "GITLAB API KEY\n" . + "https://gitlab.mydomain.com\n" . + "GITLAB PROJECT NUMERIC IDENTIFIER\n" . + "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return true; + } } diff --git a/lib/issuetrackerintegration/issueTrackerInterface.class.php b/lib/issuetrackerintegration/issueTrackerInterface.class.php index 0d6673acc6..80300c21d2 100644 --- a/lib/issuetrackerintegration/issueTrackerInterface.class.php +++ b/lib/issuetrackerintegration/issueTrackerInterface.class.php @@ -1,604 +1,607 @@ - - array('addSummary' => false, - 'colorByStatus' => false, - 'addReporter' => false, - 'addHandler' => false)); - - var $guiCfg = array(); - var $summaryLengthLimit = 120; // Mantis max is 128. - - /** - * Construct and connect to BTS. - * Can be overloaded in specialized class - * - * @param str $type (see tlIssueTracker.class.php $systems property) - **/ - function __construct($type,$config,$name) { - - $this->tlCharSet = config_get('charset'); - $this->guiCfg = array('use_decoration' => true); // add [] on summary and statusHTMLString - $this->name = $name; - - if( $this->setCfg($config) ) { - // useful only for integration via DB - if( !property_exists($this->cfg,'dbcharset') ) { - $this->cfg->dbcharset = $this->tlCharSet; - } - $this->connect(); - } - else - { - $this->connected = false; - } - } - - /** - * - **/ - function canCreateViaAPI() - { - return true; - } - - - /** - * - **/ - function getCfg() - { - return $this->cfg; - } - - /** - * - * - **/ - function setCfg($xmlString) { - $msg = null; - $signature = 'Source:' . __METHOD__; - - // check for empty string - if(strlen(trim($xmlString)) == 0) { - // Bye,Bye - $msg = " - Issue tracker:$this->name - XML Configuration seems to be empty - please check"; - tLog(__METHOD__ . $msg, 'ERROR'); - return false; - } - - $this->xmlCfg = " " . $xmlString; - libxml_use_internal_errors(true); - try { - $this->cfg = simplexml_load_string($this->xmlCfg); - if (!$this->cfg) { - $msg = $signature . " - Failure loading XML STRING\n"; - foreach(libxml_get_errors() as $error) - { - $msg .= "\t" . $error->message; - } - } - } - catch(Exception $e) - { - $msg = $signature . " - Exception loading XML STRING\n"; - $msg .= 'Message: ' .$e->getMessage(); - } - - if( !($retval = is_null($msg)) ) - { - tLog(__METHOD__ . $msg, 'ERROR'); - } - - // - if( !property_exists($this->cfg,'userinteraction') ) - { - $this->cfg->userinteraction = 0; - } - $this->cfg->userinteraction = intval($this->cfg->userinteraction) > 0 ? 1 : 0; - - // From - // http://php.net/manual/it/function.unserialize.php#112823 - // - // After PHP 5.3 an object made by - // SimpleXML_Load_String() cannot be serialized. - // An attempt to do so will result in a run-time - // failure, throwing an exception. - // - // If you store such an object in $_SESSION, - // you will get a post-execution error that says this: - // Fatal error: Uncaught exception 'Exception' - // with message 'Serialization of 'SimpleXMLElement' - // is not allowed' in [no active file]:0 - // Stack trace: #0 {main} thrown in [no active file] - // on line 0 - // - // !!!!! The entire contents of the session will be lost. - // http://stackoverflow.com/questions/1584725/quickly-convert-simplexmlobject-to-stdclass - $this->cfg = json_decode(json_encode($this->cfg)); - return $retval; - } - - /** - * - **/ - function getMyInterface() - { - return $this->cfg->interfacePHP; - } - - /** - * return the maximum length in chars of a issue id - * used on TestLink GUI - * - * @return int the maximum length of a bugID - */ - function getBugIDMaxLength() - { - // CRITIC: - // related to execution_bugs table, you can not make it - // greater WITHOUT changing table structure. - // - return 64; - } - - - /** - * establishes the database connection to the bugtracking system - * - * @return bool returns true if the db connection was established and the - * db could be selected, false else - * - **/ - function connect() - { - if (is_null($this->cfg->dbhost) || is_null($this->cfg->dbuser)) - { - return false; - } - - // cast everything to string in order to avoid issues - // @20140604 someone has been issues trying to connect to JIRA on MSSQL - $this->cfg->dbtype = strtolower((string)$this->cfg->dbtype); - $this->cfg->dbhost = (string)$this->cfg->dbhost; - $this->cfg->dbuser = (string)$this->cfg->dbuser; - $this->cfg->dbpassword = (string)$this->cfg->dbpassword; - $this->cfg->dbname = (string)$this->cfg->dbname; - - $this->dbConnection = new database($this->cfg->dbtype); - $result = $this->dbConnection->connect(false, $this->cfg->dbhost,$this->cfg->dbuser, - $this->cfg->dbpassword, $this->cfg->dbname); - - if (!$result['status']) - { - $this->dbConnection = null; - $connection_args = "(interface: - Host:$this->cfg->dbhost - " . - "DBName: $this->cfg->dbname - User: $this->cfg->dbuser) "; - $msg = sprintf(lang_get('BTS_connect_to_database_fails'),$connection_args); - tLog($msg . $result['dbms_msg'], 'ERROR'); - } - elseif ($this->cfg->dbtype == 'mysql') - { - if ($this->cfg->dbcharset == 'UTF-8') - { - $r = $this->dbConnection->exec_query("SET CHARACTER SET utf8"); - $r = $this->dbConnection->exec_query("SET NAMES utf8"); - $r = $this->dbConnection->exec_query("SET collation_connection = 'utf8_general_ci'"); - } - else - { - $r = $this->dbConnection->exec_query("SET CHARACTER SET " . $this->cfg->dbcharset); - $r = $this->dbConnection->exec_query("SET NAMES ". $this->cfg->dbcharset); - } - } - - $this->connected = $result['status'] ? true : false; - - return $this->connected; - } - - /** - * State of connection to BTS - * - * @return bool returns true if connection with BTS is established, false else - * - **/ - function isConnected() - { - - return ($this->connected && - ((!$this->interfaceViaDB ) || is_object($this->dbConnection)) ? 1 : 0); - } - - /** - * Closes the db connection (if any) - * - **/ - function disconnect() - { - if ($this->isConnected() && $this->interfaceViaDB) - { - $this->dbConnection->close(); - } - $this->connected = false; - $this->dbConnection = null; - } - - - - /** - * checks a issue id for validity (NUMERIC) - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntaxNumeric($issueID) - { - $valid = true; - $forbidden_chars = '/\D/i'; - if (preg_match($forbidden_chars, $issueID)) - { - $valid = false; - } - else - { - $valid = (intval($issueID) > 0); - } - return $valid; - } - - /** - * checks id for validity (STRING) - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntaxString($issueID) - { - $status_ok = !(trim($issueID) == ""); - if($status_ok) - { - $forbidden_chars = '/[!|�%&()\/=?]/'; - if (preg_match($forbidden_chars, $issueID)) - { - $status_ok = false; - } - } - return $status_ok; - } - - - /** - * default implementation for generating a link to the bugtracking page for viewing - * the bug with the given id in a new page - * - * @param int id the bug id - * - * @return string returns a complete HTML HREF to view the bug (if found in db) - * - **/ - function buildViewBugLink($issueID, $opt=null) { - - static $l10n; - - if( !$l10n ) { - $tg = array('issueReporter' => null, 'issueHandler' => null); - $l10n = init_labels($tg); - } - - $my['opt'] = $this->methodOpt[__FUNCTION__]; - $my['opt'] = array_merge($my['opt'],(array)$opt); - - $link = ""; - $issue = $this->getIssue($issueID); - - $ret = new stdClass(); - $ret->link = ''; - $ret->isResolved = false; - $ret->op = false; - - if( is_null($issue) || !is_object($issue) ) { - $ret->link = "TestLink Internal Message: getIssue($issueID) FAILURE on " . __METHOD__; - return $ret; - } - - $useIconv = property_exists($this->cfg,'dbcharset'); - if($useIconv) { - $link .= iconv((string)$this->cfg->dbcharset,$this->tlCharSet,$issue->IDHTMLString); - } - else { - $link .= $issue->IDHTMLString; - } - - if (!is_null($issue->statusHTMLString)) { - if($useIconv) { - $link .= iconv((string)$this->cfg->dbcharset,$this->tlCharSet,$issue->statusHTMLString); - } else { - $link .= $issue->statusHTMLString; - } - } else { - $link .= $issueID; - } - - if($my['opt']['addSummary']) { - if (!is_null($issue->summaryHTMLString)) { - $link .= " : "; - if($useIconv) { - $link .= iconv((string)$this->cfg->dbcharset,$this->tlCharSet,$issue->summaryHTMLString); - } - else { - $link .= (string)$issue->summaryHTMLString; - } - } - } - - if ($my['opt']['addReporter']) { - if( property_exists($issue, 'reportedBy') ) { - $link .= ""; - $who = trim((string)$issue->reportedBy); - if( '' != $who ) { - - $link .= '
    ' . $l10n['issueReporter'] . ': '; - if($useIconv) { - $link .= - iconv((string)$this->cfg->dbcharset,$this->tlCharSet,$who); - } - else { - $link .= $who; - } - } - } - } - - if($my['opt']['addHandler']) { - if( property_exists($issue, 'handledBy') ) { - $link .= ""; - $who = trim((string)$issue->handledBy); - if( '' != $who ) { - - $link .= '
    ' . $l10n['issueHandler'] . ': '; - if($useIconv) { - $link .= - iconv((string)$this->cfg->dbcharset,$this->tlCharSet,$who); - } - else { - $link .= $who; - } - } - } - } - - $link .= "
    "; - - if ($my['opt']['colorByStatus'] - && property_exists($issue,'statusColor') ) { - $title = lang_get('access_to_bts'); - $link = "
    statusColor;\">$link
    "; - } - - $ret = new stdClass(); - $ret->link = $link; - $ret->isResolved = $issue->isResolved; - $ret->op = true; - - if (isset($my['opt']['raw']) - && !is_null(isset($my['opt']['raw'])) ) { - foreach ($my['opt']['raw'] as $attr) { - if (property_exists($issue, $attr)) { - $ret->$attr = $issue->$attr; - } - } - } - return $ret; - } - - /** - * returns the URL which should be displayed for entering bugs - * - * @return string returns a complete URL - * - **/ - function getEnterBugURL() { - return $this->cfg->uricreate; - } - - - /** - * Returns URL to the bugtracking page for viewing ticket - * - * @param mixed issueID - * depending of BTS issueID can be a number (e.g. Mantis) - * or a string (e.g. JIRA) - * - * @return string - **/ - function buildViewBugURL($issueID) { - return $this->cfg->uriview . urlencode($issueID); - } - - - /** - * status code (always integer??) for issueID - * - * @param issueID according to BTS can be number or string - * - * @return - **/ - public function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return (!is_null($issue) && is_object($issue))? $issue->statusCode : false; - } - - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param issueID according to BTS can be number or string - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - $issue = $this->getIssue($issueID); - return (!is_null($issue) && is_object($issue))? $issue->statusVerbose : false; - } - - - - /** - * - * @param issueID according to BTS can be number or string - * - * @return string returns the bug summary if bug is found, else null - **/ - function getIssueSummary($issueID) - { - $issue = $this->getIssue($issueID); - return (!is_null($issue) && is_object($issue))? $issue->summary : null; - } - - - // How to Force Extending class to define this STATIC method ? - // KO abstract public static function getCfgTemplate(); - public static function getCfgTemplate() - { - throw new RuntimeException("Unimplemented - YOU must implement it in YOUR interface Class"); - } - - /** - * - **/ - public static function checkEnv() - { - $ret = array(); - $ret['status'] = true; - $ret['msg'] = 'OK'; - return $ret; - } - - - /** - * - **/ - public function setResolvedStatusCfg() - { - if (property_exists($this->cfg,'resolvedstatus')) { - $statusCfg = (array)$this->cfg->resolvedstatus; - } else { - $statusCfg['status'] = $this->defaultResolvedStatus; - } - $this->resolvedStatus = new stdClass(); - foreach ($statusCfg['status'] as $cfx) { - $e = (array)$cfx; - $this->resolvedStatus->byCode[$e['code']] = $e['verbose']; - } - $this->resolvedStatus->byName = array_flip($this->resolvedStatus->byCode); - } - - /** - * - **/ - public function getResolvedStatusCfg() - { - return $this->resolvedStatus; - } - - /** - * Returns the status of the bug with the given id - * this function is not directly called by TestLink. - * - * @return string returns the status of the given bug (if found in the db), or false else - **/ - function getBugStatus($id) - { - if (!$this->isConnected()) - { - return false; - } - $issue = $this->getIssue($id); - return (!is_null($issue) && $issue) ? $issue->statusVerbose : null; - } - - /** - * @param issueID (can be number of string according to specific BTS) - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = !is_null($issue) && is_object($issue); - } - return $status_ok; - } - - /** - * - **/ - function buildStatusHTMLString($statusCode) - { - $str = $statusCode; - if($this->guiCfg['use_decoration']) - { - $str = "[" . $str . "] "; - } - return $str; - } - - /** - * return the maximum length in chars of a issue summary - * used on TestLink GUI - * - * @return int - */ - function getBugSummaryMaxLength() { - return $this->summaryLengthLimit; - } - -} \ No newline at end of file + array( + 'addSummary' => false, + 'colorByStatus' => false, + 'addReporter' => false, + 'addHandler' => false + ) + ); + + private $guiCfg = array(); + + private $summaryLengthLimit = 120; + + // Mantis max is 128. + private $forbidden_chars = '/[!|�%&()\/=?]/'; + + /** + * Construct and connect to BTS. + * Can be overloaded in specialized class + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + */ + public function __construct($type, $config, $name) + { + $this->tlCharSet = config_get('charset'); + $this->guiCfg = array( + 'use_decoration' => true + ); // add [] on summary and statusHTMLString + $this->name = $name; + + if ($this->setCfg($config)) { + // useful only for integration via DB + if (! property_exists($this->cfg, 'dbcharset')) { + $this->cfg->dbcharset = $this->tlCharSet; + } + $this->connect(); + } else { + $this->connected = false; + } + } + + /** + */ + public function canCreateViaAPI() + { + return false; + } + + /** + */ + public function canAddNoteViaAPI() + { + return false; + } + + /** + */ + public function getCfg() + { + return $this->cfg; + } + + /** + */ + public function setCfg($xmlString) + { + $msg = null; + $signature = 'Source:' . __METHOD__; + + // check for empty string + if (strlen(trim($xmlString)) == 0) { + // Bye,Bye + $msg = " - Issue tracker:$this->name - XML Configuration seems to be empty - please check"; + tLog(__METHOD__ . $msg, 'ERROR'); + return false; + } + + $this->xmlCfg = " " . $xmlString; + libxml_use_internal_errors(true); + try { + $this->cfg = simplexml_load_string($this->xmlCfg); + if (! $this->cfg) { + $msg = $signature . " - Failure loading XML STRING\n"; + foreach (libxml_get_errors() as $error) { + $msg .= "\t" . $error->message; + } + } + } catch (Exception $e) { + $msg = $signature . " - Exception loading XML STRING\n"; + $msg .= 'Message: ' . $e->getMessage(); + } + + if (! ($retval = is_null($msg))) { + tLog(__METHOD__ . $msg, 'ERROR'); + } + + // + if (! property_exists($this->cfg, 'userinteraction')) { + $this->cfg->userinteraction = 0; + } + $this->cfg->userinteraction = intval($this->cfg->userinteraction) > 0 ? 1 : 0; + + // From + // http://php.net/manual/it/function.unserialize.php#112823 + // + // After PHP 5.3 an object made by + // SimpleXML_Load_String() cannot be serialized. + // An attempt to do so will result in a run-time + // failure, throwing an exception. + // + // If you store such an object in $_SESSION, + // you will get a post-execution error that says this: + // Fatal error: Uncaught exception 'Exception' + // with message 'Serialization of 'SimpleXMLElement' + // is not allowed' in [no active file]:0 + // Stack trace: #0 {main} thrown in [no active file] + // on line 0 + // + // !!!!! The entire contents of the session will be lost. + // http://stackoverflow.com/questions/1584725/quickly-convert-simplexmlobject-to-stdclass + $this->cfg = json_decode(json_encode($this->cfg)); + return $retval; + } + + /** + */ + public function getMyInterface() + { + return $this->cfg->interfacePHP; + } + + /** + * return the maximum length in chars of a issue id + * used on TestLink GUI + * + * @return int the maximum length of a bugID + */ + public function getBugIDMaxLength() + { + // CRITIC: + // related to execution_bugs table, you can not make it + // greater WITHOUT changing table structure. + // + return 64; + } + + /** + * establishes the database connection to the bugtracking system + * + * @return bool returns true if the db connection was established and the + * db could be selected, false else + * + */ + public function connect() + { + if (is_null($this->cfg->dbhost) || is_null($this->cfg->dbuser)) { + return false; + } + + // cast everything to string in order to avoid issues + // @20140604 someone has been issues trying to connect + // to JIRA on MSSQL + $this->cfg->dbtype = strtolower((string) $this->cfg->dbtype); + $this->cfg->dbhost = (string) $this->cfg->dbhost; + $this->cfg->dbuser = (string) $this->cfg->dbuser; + $this->cfg->dbpassword = (string) $this->cfg->dbpassword; + $this->cfg->dbname = (string) $this->cfg->dbname; + + $this->dbConnection = new database($this->cfg->dbtype); + $result = $this->dbConnection->connect(false, $this->cfg->dbhost, + $this->cfg->dbuser, $this->cfg->dbpassword, $this->cfg->dbname); + + if (! $result['status']) { + $this->dbConnection = null; + $cnn = "(interface: - Host:{$this->cfg->dbhost} - " . + "DBName: {$this->cfg->dbname} + - User: {$this->cfg->dbuser}) "; + $msg = sprintf(lang_get('BTS_connect_to_database_fails'), $cnn); + tLog($msg . $result['dbms_msg'], 'ERROR'); + } elseif ($this->cfg->dbtype == 'mysql') { + if ($this->cfg->dbcharset == 'UTF-8') { + $this->dbConnection->exec_query("SET CHARACTER SET utf8"); + $this->dbConnection->exec_query("SET NAMES utf8"); + $this->dbConnection->exec_query( + "SET collation_connection = 'utf8_general_ci'"); + } else { + $this->dbConnection->exec_query( + "SET CHARACTER SET " . $this->cfg->dbcharset); + $this->dbConnection->exec_query( + "SET NAMES " . $this->cfg->dbcharset); + } + } + + $this->connected = $result['status'] ? true : false; + + return $this->connected; + } + + /** + * State of connection to BTS + * + * @return bool returns true if connection with BTS is established, false else + * + */ + public function isConnected() + { + return $this->connected && + ((! $this->interfaceViaDB) || is_object($this->dbConnection)) ? 1 : 0; + } + + /** + * Closes the db connection (if any) + */ + public function disconnect() + { + if ($this->isConnected() && $this->interfaceViaDB) { + $this->dbConnection->close(); + } + $this->connected = false; + $this->dbConnection = null; + } + + /** + * checks a issue id for validity (NUMERIC) + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntaxNumeric($issueID) + { + $valid = true; + $blackList = '/\D/i'; + if (preg_match($blackList, $issueID)) { + $valid = false; + } else { + $valid = (intval($issueID) > 0); + } + return $valid; + } + + /** + * checks id for validity (STRING) + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntaxString($issueID) + { + $status_ok = (trim($issueID) != ""); + if ($status_ok && preg_match($this->forbidden_chars, $issueID)) { + $status_ok = false; + } + return $status_ok; + } + + /** + * default implementation for generating a link to the bugtracking page for viewing + * the bug with the given id in a new page + * + * @param + * int id the bug id + * + * @return string returns a complete HTML HREF to view the bug (if found in db) + * + */ + public function buildViewBugLink($issueID, $opt = null) + { + static $l10n; + + if (! $l10n) { + $tg = array( + 'issueReporter' => null, + 'issueHandler' => null + ); + $l10n = init_labels($tg); + } + + $my['opt'] = $this->methodOpt[__FUNCTION__]; + $my['opt'] = array_merge($my['opt'], (array) $opt); + + $link = ""; + $issue = $this->getIssue($issueID); + + $ret = new stdClass(); + $ret->link = ''; + $ret->isResolved = false; + $ret->op = false; + + if (is_null($issue) || ! is_object($issue)) { + $ret->link = "TestLink Internal Message: getIssue($issueID) FAILURE on " . + __METHOD__; + return $ret; + } + + $useIconv = property_exists($this->cfg, 'dbcharset'); + if ($useIconv) { + $link .= iconv((string) $this->cfg->dbcharset, $this->tlCharSet, + $issue->IDHTMLString); + } else { + $link .= $issue->IDHTMLString; + } + + if (! is_null($issue->statusHTMLString)) { + if ($useIconv) { + $link .= iconv((string) $this->cfg->dbcharset, $this->tlCharSet, + $issue->statusHTMLString); + } else { + $link .= $issue->statusHTMLString; + } + } else { + $link .= $issueID; + } + + if ($my['opt']['addSummary'] && ! is_null($issue->summaryHTMLString)) { + $link .= " : "; + if ($useIconv) { + $link .= iconv((string) $this->cfg->dbcharset, $this->tlCharSet, + $issue->summaryHTMLString); + } else { + $link .= (string) $issue->summaryHTMLString; + } + } + + if ($my['opt']['addReporter'] && property_exists($issue, 'reportedBy')) { + $link .= ""; + $who = trim((string) $issue->reportedBy); + if ('' != $who) { + + $link .= '
    ' . $l10n['issueReporter'] . ': '; + if ($useIconv) { + $link .= iconv((string) $this->cfg->dbcharset, + $this->tlCharSet, $who); + } else { + $link .= $who; + } + } + } + + if ($my['opt']['addHandler'] && property_exists($issue, 'handledBy')) { + $link .= ""; + $who = trim((string) $issue->handledBy); + if ('' != $who) { + + $link .= '
    ' . $l10n['issueHandler'] . ': '; + if ($useIconv) { + $link .= iconv((string) $this->cfg->dbcharset, + $this->tlCharSet, $who); + } else { + $link .= $who; + } + } + } + + $link .= "
    "; + + if ($my['opt']['colorByStatus'] && property_exists($issue, 'statusColor')) { + $title = lang_get('access_to_bts'); + $link = "
    statusColor;\">$link
    "; + } + + $ret = new stdClass(); + $ret->link = $link; + $ret->isResolved = $issue->isResolved; + $ret->op = true; + + if (isset($my['opt']['raw']) && ! is_null(isset($my['opt']['raw']))) { + foreach ($my['opt']['raw'] as $attr) { + if (property_exists($issue, $attr)) { + $ret->$attr = $issue->$attr; + } + } + } + return $ret; + } + + /** + * returns the URL which should be displayed for entering bugs + * + * @return string returns a complete URL + * + */ + public function getEnterBugURL() + { + return $this->cfg->uricreate; + } + + /** + * Returns URL to the bugtracking page for viewing ticket + * + * @param + * mixed issueID + * depending of BTS issueID can be a number (e.g. Mantis) + * or a string (e.g. JIRA) + * + * @return string + */ + public function buildViewBugURL($issueID) + { + return $this->cfg->uriview . urlencode($issueID); + } + + /** + * status code (always integer??) for issueID + * + * @param + * issueID according to BTS can be number or string + * + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return (! is_null($issue) && is_object($issue)) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * issueID according to BTS can be number or string + * + * @return string + * + */ + public function getIssueStatusVerbose($issueID) + { + $issue = $this->getIssue($issueID); + return (! is_null($issue) && is_object($issue)) ? $issue->statusVerbose : false; + } + + /** + * + * @param + * issueID according to BTS can be number or string + * + * @return string returns the bug summary if bug is found, else null + */ + public function getIssueSummary($issueID) + { + $issue = $this->getIssue($issueID); + return (! is_null($issue) && is_object($issue)) ? $issue->summary : null; + } + + // How to Force Extending class to define this STATIC method ? + // KO abstract public static function getCfgTemplate(); + public static function getCfgTemplate() + { + throw new RuntimeException( + "Unimplemented - YOU must implement it in YOUR interface Class"); + } + + /** + */ + public static function checkEnv() + { + $ret = array(); + $ret['status'] = true; + $ret['msg'] = 'OK'; + return $ret; + } + + /** + */ + public function setResolvedStatusCfg() + { + if (property_exists($this->cfg, 'resolvedstatus')) { + $statusCfg = (array) $this->cfg->resolvedstatus; + } else { + $statusCfg['status'] = $this->defaultResolvedStatus; + } + $this->resolvedStatus = new stdClass(); + $this->resolvedStatus->byCode = []; + $this->resolvedStatus->byName = []; + + foreach ($statusCfg['status'] as $cfx) { + $e = (array) $cfx; + $this->resolvedStatus->byCode[$e['code']] = $e['verbose']; + } + $this->resolvedStatus->byName = array_flip( + $this->resolvedStatus->byCode); + } + + /** + */ + public function getResolvedStatusCfg() + { + return $this->resolvedStatus; + } + + /** + * Returns the status of the bug with the given id + * this function is not directly called by TestLink. + * + * @return string returns the status of the given bug (if found in the db), or false else + */ + public function getBugStatus($id) + { + if (! $this->isConnected()) { + return false; + } + $issue = $this->getIssue($id); + return (! is_null($issue) && $issue) ? $issue->statusVerbose : null; + } + + /** + * + * @param + * issueID (can be number of string according to specific BTS) + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = ! is_null($issue) && is_object($issue); + } + return $status_ok; + } + + /** + */ + public function buildStatusHTMLString($statusCode) + { + $str = $statusCode; + if ($this->guiCfg['use_decoration']) { + $str = "[" . $str . "] "; + } + return $str; + } + + /** + * return the maximum length in chars of a issue summary + * used on TestLink GUI + * + * @return int + */ + public function getBugSummaryMaxLength() + { + return $this->summaryLengthLimit; + } + + /** + */ + public function normalizeBugID($issueID) + { + return $issueID; + } +} diff --git a/lib/issuetrackerintegration/jiraCommons.class.php b/lib/issuetrackerintegration/jiraCommons.class.php index e1692e4963..b07b690497 100644 --- a/lib/issuetrackerintegration/jiraCommons.class.php +++ b/lib/issuetrackerintegration/jiraCommons.class.php @@ -1,97 +1,101 @@ - 'its_duedate_with_separator'); - - var $defaultResolvedStatus; - var $guiCfg; - - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = !is_null($issue) && is_object($issue); - } - return $status_ok; - } - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - private function helperParseDate($date2parse) - { - $ret = null; - if (!is_null($date2parse)) - { - $ret = date_parse($date2parse); - $ret = ((gmmktime(0, 0, 0, $ret['month'], $ret['day'], $ret['year']))); - $ret = $this->l18n['duedate'] . gmstrftime("%d %b %Y",($ret)); - } - return $ret ; - } - - /** - * - **/ - function buildStatusHTMLString($statusCode) - { - $str = $statusCode; - if($this->guiCfg['use_decoration']) - { - $str = "[" . $str . "] "; - } - return $str; - } - - /** - * - **/ - function buildSummaryHTMLString($issue) - { - $summary = $issue->summary; - if(property_exists($issue, 'duedate')) - { - $strDueDate = $this->helperParseDate($issue->duedate); - if( !is_null($strDueDate) ) - { - $summary .= " [$strDueDate] "; - } - } - return $summary; - } - - /** - * - **/ - function initDefaultResolvedStatus($statusDomain) - { - $domain = array(); - $itemSet = array('Resolved','Closed'); // Unfortunately case is important - foreach($itemSet as $st) - { - $domain[] = array('code' => $statusDomain[$st], 'verbose' => $st); - } - return $domain; - } -} \ No newline at end of file + 'its_duedate_with_separator' + ); + + public $defaultResolvedStatus; + + public $guiCfg; + + /** + * + * @param + * string issueID + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = ! is_null($issue) && is_object($issue); + } + return $status_ok; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + private function helperParseDate($date2parse) + { + $ret = null; + if (! is_null($date2parse)) { + $ret = date_parse($date2parse); + $ret = (gmmktime(0, 0, 0, $ret['month'], $ret['day'], $ret['year'])); + $ret = $this->l18n['duedate'] . @gmstrftime("%d %b %Y", ($ret)); + } + return $ret; + } + + /** + */ + public function buildStatusHTMLString($statusCode) + { + $str = $statusCode; + if ($this->guiCfg['use_decoration']) { + $str = "[" . $str . "] "; + } + return $str; + } + + /** + */ + public function buildSummaryHTMLString($issue) + { + $summary = $issue->summary; + if (property_exists($issue, 'duedate')) { + $strDueDate = $this->helperParseDate($issue->duedate); + if (! is_null($strDueDate)) { + $summary .= " [$strDueDate] "; + } + } + return $summary; + } + + /** + */ + public function initDefaultResolvedStatus($statusDomain) + { + $domain = array(); + $itemSet = array( + 'Resolved', + 'Closed' + ); // Unfortunately case is important + foreach ($itemSet as $st) { + $domain[] = array( + 'code' => $statusDomain[$st], + 'verbose' => $st + ); + } + return $domain; + } +} diff --git a/lib/issuetrackerintegration/jiradbInterface.class.php b/lib/issuetrackerintegration/jiradbInterface.class.php index 6a645c4ac7..eef5ccd740 100644 --- a/lib/issuetrackerintegration/jiradbInterface.class.php +++ b/lib/issuetrackerintegration/jiradbInterface.class.php @@ -1,246 +1,222 @@ -isConnected() ) - { - return false; - } - - - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => true); - $this->interfaceViaDB = true; - - $this->support = new jiraCommons(); - $this->support->guiCfg = array('use_decoration' => true); - - // Tables used - $this->dbSchema = new stdClass(); - $this->dbSchema->issues = 'jiraissue'; - $this->dbSchema->status = 'issuestatus'; - $this->dbSchema->project = 'project'; - - - $this->getStatuses(); - if( property_exists($this->cfg, 'statuscfg') ) - { - $this->setStatusCfg(); - } - - if( !property_exists($this->cfg, 'jiraversion') ) - { - // throw new Exception("jiraversion is MANDATORY - Unable to continue"); - $msg = " - Issuetracker $this->name - jiraversion is MANDATORY - Unable to continue"; - tLog(__METHOD__ . $msg, 'ERROR'); - return false; - } - else - { - $this->completeCfg(); - } - - - $this->defaultResolvedStatus = $this->support->initDefaultResolvedStatus($this->statusDomain); - $this->setResolvedStatusCfg(); - - } - - /** - * - */ - function completeCfg() - { - // when working with simpleXML objects is better to use intermediate variables - $sz = (string)$this->cfg->jiraversion; - $pieces = explode('.',$sz); - $this->cfg->majorVersionNumber = (int)$pieces[0]; - - if($this->cfg->majorVersionNumber <= 0) - { - throw new Exception("Version has to be MAJOR.MINOR" . ' - got : ' . $sz , 1); - } - } - - - - /** - * - **/ - function getIssue($issueID) - { - - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - if (!$this->isConnected()) - { - return false; - } - - // ATTENTION: - // Field names on Jira tables seems to be sometimes on CAPITALS - // TICKET 6028: Integration with Jira 6.1 broken. - Due to JIRA schema changes - \Kint::dump($this->dbConnection); - if(intval($this->cfg->majorVersionNumber) >= 6) - { - $dummy = explode("-",$issueID); - $addFields = ",ISSUES.project, ISSUES.issuenum, PROJECT.originalkey, PROJECT.id "; - $addJoin = " JOIN {$this->dbSchema->project} PROJECT ON ISSUES.project = PROJECT.id "; - $where = " WHERE ISSUES.issuenum='{$this->dbConnection->prepare_string($dummy[1])}' " . - " AND PROJECT.originalkey='{$this->dbConnection->prepare_string($dummy[0])}'"; - } - else - { - $addFields = ",ISSUES.pkey "; - $addJoin = ''; - $where = " WHERE ISSUES.pkey='{$this->dbConnection->prepare_string($issueID)}'"; - } - - $sql = "/* $debugMsg */ " . - " SELECT ISSUES.ID AS id, ISSUES.summary,ISSUES.issuestatus AS status_code, " . - " ST.pname AS status_verbose " . $addFields . - " FROM {$this->dbSchema->issues} ISSUES " . - " JOIN {$this->dbSchema->status} ST ON ST.ID = ISSUES.issuestatus " . - $addJoin . $where; - - try - { - $rs = $this->dbConnection->fetchRowsIntoMap($sql,'id'); - } - catch (Exception $e) - { - $rs = null; - $msg = "JIRA DB - Ticket ID $issueID - " . $e->getMessage(); - tLog($msg, 'WARNING'); - } - - $issue = null; - if( !is_null($rs) ) - { - $issueOnDB = current($rs); - $issue = new stdClass(); - $issue->IDHTMLString = "{$issueID} : "; - - $issue->summary = $issueOnDB['summary']; - $issue->statusCode = $issueOnDB['status_code']; - $issue->statusVerbose = $issueOnDB['status_verbose']; - - $issue->statusHTMLString = $this->support->buildStatusHTMLString($issue->statusVerbose); - $issue->summaryHTMLString = $this->support->buildSummaryHTMLString($issue); - - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - } - return $issue; - } - - /** - * - */ - function getMyInterface() - { - return $this->cfg->interfacePHP; - } - - /** - * - */ - function getStatuses() - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - if (!$this->isConnected()) - { - return false; - } - - // ATTENTION: - // Field names on Jira tables seems to be sometimes on CAPITALS - $sql = "/* $debugMsg */ " . - " SELECT ST.ID AS id,ST.pname AS name FROM {$this->dbSchema->status} ST"; - try - { - $rs = $this->dbConnection->fetchRowsIntoMap($sql,'id'); - foreach ($rs as $id => $elem) - { - $this->statusDomain[$elem['name']]=$id; - } - } - catch (Exception $e) - { - tLog("JIRA DB " . __METHOD__ . $e->getMessage(), 'WARNING'); - } - } - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public function getStatusDomain() - { - return $this->statusDomain; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxString($issueID); - } - - - public static function getCfgTemplate() - { - - $template = "\n" . - "\n" . - "MANDATORY\n" . - "DATABASE SERVER NAME\n" . - "DATABASE NAME\n" . - "mysql\n" . - "USER\n" . - "PASSWORD\n" . - "http://localhost:8080/development/mantisbt-1.2.5/view.php?id=\n" . - "http://localhost:8080/development/mantisbt-1.2.5/\n" . - "\n" . - "\n" . - "80resolved\n" . - "90closed\n" . - "\n" . - "\n"; - return $template; - } - - /** - * - **/ - function canCreateViaAPI() - { - return false; - } +isConnected()) { + return false; + } + + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => true + ); + $this->interfaceViaDB = true; + + $this->support = new jiraCommons(); + $this->support->guiCfg = array( + 'use_decoration' => true + ); + + // Tables used + $this->dbSchema = new stdClass(); + $this->dbSchema->issues = 'jiraissue'; + $this->dbSchema->status = 'issuestatus'; + $this->dbSchema->project = 'project'; + + $this->getStatuses(); + if (property_exists($this->cfg, 'statuscfg')) { + $this->setStatusCfg(); + } + + if (! property_exists($this->cfg, 'jiraversion')) { + $msg = " - Issuetracker $this->name - jiraversion is MANDATORY - Unable to continue"; + tLog(__METHOD__ . $msg, 'ERROR'); + return false; + } else { + $this->completeCfg(); + } + + $this->defaultResolvedStatus = $this->support->initDefaultResolvedStatus( + $this->statusDomain); + $this->setResolvedStatusCfg(); + } + + /** + */ + private function completeCfg() + { + // when working with simpleXML objects is better to use intermediate variables + $sz = (string) $this->cfg->jiraversion; + $pieces = explode('.', $sz); + $this->cfg->majorVersionNumber = (int) $pieces[0]; + + if ($this->cfg->majorVersionNumber <= 0) { + throw new Exception( + "Version has to be MAJOR.MINOR" . ' - got : ' . $sz, 1); + } + } + + /** + */ + public function getIssue($issueID) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + if (! $this->isConnected()) { + return false; + } + + // ATTENTION: + // Field names on Jira tables seems to be sometimes on CAPITALS + // TICKET 6028: Integration with Jira 6.1 broken. - Due to JIRA schema changes + \Kint::dump($this->dbConnection); + if (intval($this->cfg->majorVersionNumber) >= 6) { + $dummy = explode("-", $issueID); + $addFields = ",ISSUES.project, ISSUES.issuenum, PROJECT.originalkey, PROJECT.id "; + $addJoin = " JOIN {$this->dbSchema->project} PROJECT ON ISSUES.project = PROJECT.id "; + $where = " WHERE ISSUES.issuenum='{$this->dbConnection->prepare_string($dummy[1])}' " . + " AND PROJECT.originalkey='{$this->dbConnection->prepare_string($dummy[0])}'"; + } else { + $addFields = ",ISSUES.pkey "; + $addJoin = ''; + $where = " WHERE ISSUES.pkey='{$this->dbConnection->prepare_string($issueID)}'"; + } + + $sql = "/* $debugMsg */ " . + " SELECT ISSUES.ID AS id, ISSUES.summary,ISSUES.issuestatus AS status_code, " . + " ST.pname AS status_verbose " . $addFields . + " FROM {$this->dbSchema->issues} ISSUES " . + " JOIN {$this->dbSchema->status} ST ON ST.ID = ISSUES.issuestatus " . + $addJoin . $where; + + try { + $rs = $this->dbConnection->fetchRowsIntoMap($sql, 'id'); + } catch (Exception $e) { + $rs = null; + $msg = "JIRA DB - Ticket ID $issueID - " . $e->getMessage(); + tLog($msg, 'WARNING'); + } + + $issue = null; + if (! is_null($rs)) { + $issueOnDB = current($rs); + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + + $issue->summary = $issueOnDB['summary']; + $issue->statusCode = $issueOnDB['status_code']; + $issue->statusVerbose = $issueOnDB['status_verbose']; + + $issue->statusHTMLString = $this->support->buildStatusHTMLString( + $issue->statusVerbose); + $issue->summaryHTMLString = $this->support->buildSummaryHTMLString( + $issue); + + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + } + return $issue; + } + + /** + */ + public function getMyInterface() + { + return $this->cfg->interfacePHP; + } + + /** + */ + public function getStatuses() + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + if (! $this->isConnected()) { + return false; + } + + // ATTENTION: + // Field names on Jira tables seems to be sometimes on CAPITALS + $sql = "/* $debugMsg */ " . + " SELECT ST.ID AS id,ST.pname AS name FROM {$this->dbSchema->status} ST"; + try { + $rs = $this->dbConnection->fetchRowsIntoMap($sql, 'id'); + foreach ($rs as $id => $elem) { + $this->statusDomain[$elem['name']] = $id; + } + } catch (Exception $e) { + tLog("JIRA DB " . __METHOD__ . $e->getMessage(), 'WARNING'); + } + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public function getStatusDomain() + { + return $this->statusDomain; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxString($issueID); + } + + public static function getCfgTemplate() + { + return "\n" . "\n" . + "MANDATORY\n" . + "DATABASE SERVER NAME\n" . + "DATABASE NAME\n" . "mysql\n" . + "USER\n" . "PASSWORD\n" . + "http://localhost:8080/development/mantisbt-1.2.5/view.php?id=\n" . + "http://localhost:8080/development/mantisbt-1.2.5/\n" . + "\n" . + "\n" . + "80resolved\n" . + "90closed\n" . + "\n" . "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return false; + } } diff --git a/lib/issuetrackerintegration/jirarestInterface.class.php b/lib/issuetrackerintegration/jirarestInterface.class.php index c8ac3c713b..7e61d1e948 100644 --- a/lib/issuetrackerintegration/jirarestInterface.class.php +++ b/lib/issuetrackerintegration/jirarestInterface.class.php @@ -1,911 +1,840 @@ -name = $name; - $this->interfaceViaDB = false; - $this->support = new jiraCommons(); - $this->support->guiCfg = array('use_decoration' => true); - - // This is the right way to overwrite ONLY - // the keys we want, and preserve the default - // configuration present in the issueTrackerInterface class - $this->methodOpt['buildViewBugLink'] = - array_merge($this->methodOpt['buildViewBugLink'], - array('addSummary' => true, - 'colorByStatus' => false) - ); - - if ($this->setCfg($config) && $this->checkCfg()) { - $this->completeCfg(); - $this->connect(); - $this->guiCfg = array('use_decoration' => true); - - if( $this->isConnected()) - { - $this->setResolvedStatusCfg(); - } - } - } - - /** - * - */ - function getIssueAttr() - { - return $this->issueAttr; - } - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/'; // be sure no double // at end - - if( !property_exists($this->cfg,'uriapi') ) - { - $this->cfg->uriapi = $base . 'rest/api/latest/'; - } - - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'browse/'; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base . ''; - } - - - if( property_exists($this->cfg,'attributes') ) - { - // echo __FUNCTION__ . "::Debug::Step#$step Going To Add attributes
    ";$step++; - $this->processAttributes(); - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxString($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - try - { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $this->jiraCfg = array('username' => (string)trim($this->cfg->username), - 'password' => (string)trim($this->cfg->password), - 'host' => (string)trim($this->cfg->uriapi)); - - $this->jiraCfg['proxy'] = config_get('proxy'); - if( !is_null($this->jiraCfg['proxy']) ) - { - if( is_null($this->jiraCfg['proxy']->host) ) - { - $this->jiraCfg['proxy'] = null; - } - } - - $this->APIClient = new JiraApi\Jira($this->jiraCfg); - - $this->connected = $this->APIClient->testLogin(); - if($this->connected && ($this->cfg->projectkey != self::NOPROJECTKEY)) - { - // Now check if can get info about the project, to understand - // if at least it exists. - $pk = trim((string)$this->cfg->projectkey); - $this->APIClient->getProject($pk); - - $statusSet = $this->APIClient->getStatuses(); - foreach ($statusSet as $statusID => $statusName) - { - $this->statusDomain[$statusName] = $statusID; - } - - $this->defaultResolvedStatus = - $this->support->initDefaultResolvedStatus($this->statusDomain); - } - } - catch(Exception $e) - { - $this->connected = false; - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - public function getIssue($issueID) - { - if (!$this->isConnected()) - { - tLog(__METHOD__ . '/Not Connected ', 'ERROR'); - return false; - } - - $issue = null; - try - { - - $issue = $this->APIClient->getIssue($issueID); - - // IMPORTANT NOTICE - // $issue->id do not contains ISSUE ID as displayed on GUI, but what seems to be an internal value. - // $issue->key has what we want. - // Very strange is how have this worked till today ?? (2015-01-24) - if(!is_null($issue) && is_object($issue) && !property_exists($issue,'errorMessages')) - { - - // We are going to have a set of standard properties - $issue->id = $issue->key; - $issue->summary = $issue->fields->summary; - $issue->statusCode = $issue->fields->status->id; - $issue->statusVerbose = $issue->fields->status->name; - - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusHTMLString = $this->support->buildStatusHTMLString($issue->statusVerbose); - $issue->summaryHTMLString = $this->support->buildSummaryHTMLString($issue); - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - - - /* - for debug porpouses - $tlIssue = new stdClass(); - $tlIssue->IDHTMLString = $issue->IDHTMLString; - $tlIssue->statusCode = $issue->statusCode; - $tlIssue->statusVerbose = $issue->statusVerbose; - $tlIssue->statusHTMLString = $issue->statusHTMLString; - $tlIssue->summaryHTMLString = $issue->summaryHTMLString; - $tlIssue->isResolved = $issue->isResolved; - - var_dump($tlIssue); - */ - } - else - { - $issue = null; - } - - } - catch(Exception $e) - { - tLog("JIRA Ticket ID $issueID - " . $e->getMessage(), 'WARNING'); - $issue = null; - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->statusCode : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - return $this->getIssueStatusCode($issueID); - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) - { - $issue = $this->getIssue($issueID); - return $issue->summaryHTMLString; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = is_object($issue) && !is_null($issue); - } - return $status_ok; - } - -/* -{ - "fields": { - "project": - { - "key": "TEST" - }, - "summary": "REST ye merry gentlemen.", - "description": "Creating of an issue using project keys and issue type names using the REST API", - "issuetype": { - "name": "Bug" - } - "priority": { - "id": 4 - } - - } -} -*/ - - /** - * - * - * JSON example: - * - * { - * "fields": { - * "project": { - * "key": "TEST" - * }, - * "summary": "REST ye merry gentlemen.", - * "description": "Creating of an issue using project keys and issue type names using the REST API", - * "issuetype": { - * "name": "Bug" - * } - * } - * } - * - * - */ - public function addIssue($summary,$description,$opt=null) - { - try - { - $issue = array('fields' => - array('project' => array('key' => (string)$this->cfg->projectkey), - 'summary' => $summary, - 'description' => $description, - 'issuetype' => array( 'id' => (int)$this->cfg->issuetype) - )); - - $prio = null; - if(property_exists($this->cfg, 'issuepriority')) - { - $prio = $this->cfg->issuepriority; - - } - if( !is_null($opt) && property_exists($opt, 'issuePriority') ) - { - $prio = $opt->issuePriority; - } - if( !is_null($prio) ) - { - // CRITIC: if not casted to string, you will get following error from JIRA - // "Could not find valid 'id' or 'name' in priority object." - $issue['fields']['priority'] = array('id' => (string)$prio); - } - - - if(!is_null($this->issueAttr)) - { - $issue['fields'] = array_merge($issue['fields'],$this->issueAttr); - } - - if(!is_null($opt)) - { - - // these can have multiple values - if(property_exists($opt, 'artifactComponent')) - { - // YES is plural!! - $issue['fields']['components'] = array(); - foreach( $opt->artifactComponent as $vv) - { - $issue['fields']['components'][] = array('id' => (string)$vv); - } - } - - if (property_exists($opt, 'artifactVersion')) { - // YES is plural!! - $issue['fields']['versions'] = array(); - foreach ( $opt->artifactVersion as $vv) { - $issue['fields']['versions'][] = array('id' => (string)$vv); - } - } - - - if (property_exists($opt, 'reporter')) { - $issue['fields']['reporter'] = array('name' => (string)$opt->reporter); - } - - if (property_exists($opt, 'issueType')) { - $issue['fields']['issuetype'] = array('id' => $opt->issueType); - } - - if (preg_grep("/(?:\/.*\/{1,})(.*) - Execution/", $summary, $matches)) { - $issue['fields']['customfield_10311'] = $matches[1]; - } - } - - $op = $this->APIClient->createIssue($issue); - $ret = array('status_ok' => false, 'id' => null, 'msg' => 'ko'); - if(!is_null($op)) - { - if(isset($op->errors)) - { - $ret['msg'] = __FUNCTION__ . ":Failure:JIRA Message:\n"; - foreach ($op->errors as $pk => $pv) - { - $ret['msg'] .= "$pk => $pv\n"; - } - } - else - { - $ret = array('status_ok' => true, 'id' => $op->key, - 'msg' => sprintf(lang_get('jira_bug_created'),$summary,$issue['fields']['project']['key'])); - } - } - } - catch (Exception $e) - { - $msg = "Create JIRA Ticket (REST) FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - } - return $ret; - } - - /** - * on JIRA notes is called comment - * - */ - public function addNote($issueID,$noteText,$opt=null) - { - try - { - $op = $this->APIClient->addComment($noteText,$issueID); - $ret = array('status_ok' => false, 'id' => null, 'msg' => 'ko'); - if(!is_null($op)) - { - if(isset($op->errors)) - { - $ret['msg'] = $op->errors; - } - else - { - $ret = array('status_ok' => true, 'id' => $op->key, - 'msg' => sprintf(lang_get('jira_comment_added'),$issueID)); - } - } - } - catch (Exception $e) - { - $msg = "Add JIRA Issue Comment (REST) FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - } - return $ret; - } - - /** - * - */ - public function getIssueTypes() - { - try - { - return $this->APIClient->getIssueTypes(); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getPriorities() - { - try - { - return $this->APIClient->getPriorities(); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - */ - public function getVersions() - { - $items = null; - try - { - $items = $this->APIClient->getVersions((string)$this->cfg->projectkey); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - return $items; - } - - /** - * - */ - public function getComponents() - { - try - { - return $this->APIClient->getComponents((string)$this->cfg->projectkey); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - - /** - * - */ - public function getCreateIssueFields() - { - try - { - return $this->APIClient->getCreateIssueFields((string)$this->cfg->projectkey); - } - catch(Exception $e) - { - tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); - } - } - - - - /** - * - */ - public function getIssueTypesForHTMLSelect() - { - return array('items' => $this->objectAttrToIDName($this->getIssueTypes()), - 'isMultiSelect' => false); - } - - /** - * - */ - public function getPrioritiesForHTMLSelect() - { - return array('items' => $this->objectAttrToIDName($this->getPriorities()), - 'isMultiSelect' => false); - } - - /** - * - */ - public function getVersionsForHTMLSelect() - { - $input = array('items' => null,'isMultiSelect' => true); - $items = $this->getVersions(); - if(!is_null($items)) - { - $input['items'] = $this->objectAttrToIDName($items); - } - else - { - $input = null; - } - return $input; - } - - /** - * - */ - public function getComponentsForHTMLSelect() - { - $items = $this->getComponents(); - $input = array('items' => null,'isMultiSelect' => true); - if(!is_null($items)) - { - $input['items'] = $this->objectAttrToIDName($items); - } - else - { - $input = null; - } - return $input; - } - - - /** - * - * - */ - private function objectAttrToIDName($attrSet) - { - $ret = null; - if(!is_null($attrSet)) - { - $ic = count($attrSet); - for($idx=0; $idx < $ic; $idx++) - { - $ret[$attrSet[$idx]->id] = $attrSet[$idx]->name; - } - } - return $ret; - } - - - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $tpl = "\n" . - "\n" . - "JIRA LOGIN NAME\n" . - "JIRA PASSWORD\n" . - "https://testlink.atlassian.net/\n" . - "\n" . - "https://testlink.atlassian.net/rest/api/latest/\n" . - "https://testlink.atlassian.net/browse/\n" . - "1/0\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "JIRA PROJECT KEY\n" . - "JIRA ISSUE TYPE ID\n" . - "JIRA ISSUE PRIORITY ID\n" . - "\n" . - "\n"; - return $tpl; - } - - - - /** - * - **/ - function canCreateViaAPI() - { - $status_ok = false; - - // The VERY Mandatory KEY - if( property_exists($this->cfg, 'projectkey') ) - { - $pk = trim((string)($this->cfg->projectkey)); - $status_ok = ($pk !== ''); - } - - if($status_ok && $this->cfg->userinteraction == 0) - { - $status_ok = property_exists($this->cfg, 'issuetype'); - } - - return $status_ok; - } - - /** - * - **/ - function processAttributes() - { - $attr = get_object_vars($this->cfg->attributes); - foreach ($attr as $name => $elem) - { - $name = (string)$name; - switch($name) - { - case 'customFieldValues': - $this->getCustomFieldsAttribute($name,$elem); - break; - } - } - } - - /** - * supported types: - * (see https://developer.atlassian.com/jiradev/api-reference/ - * jira-rest-apis/jira-rest-api-tutorials/ - * jira-rest-api-example-create-issue# - * JIRARESTAPIExample-CreateIssue-Exampleofcreatinganissueusingcustomfields) - * - * --------------------------------------------------------- - * Single Value (simple) Group: - * --------------------------------------------------------- - * - * DatePickerField - * "customfield_10002": "2011-10-03" - * - * DateTimeField - * "customfield_10003": "2011-10-19T10:29:29.908+1100" * - * - * FreeTextField - * "customfield_10004": "Free text goes here. Type away!" - * - * NumberField - * "customfield_10010": 42.07 - * - * --------------------------------------------------------- - * Pair Value (simple) Group: - * --------------------------------------------------------- - * - * RadioButtons - * "customfield_10012": { "value": "red" } - * - * SelectList - * "customfield_10013": { "value": "red" } - * - * --------------------------------------------------------- - * Multiple Values (simple) Group: - * --------------------------------------------------------- - * - * Labels (PHP Array of strings) - * "customfield_10006": ["examplelabelnumber1", "examplelabelnumber2"] - * - * - * --------------------------------------------------------- - * Multiple Values (COMPLEX) Group: - * --------------------------------------------------------- - * - * MultiGroupPicker (access key -> name) - * "customfield_10007": [{ "name": "admins" }, { "name": "jira-dev" }, - * { "name": "jira-users" }] - * - * MultiUserPicker (access key -> name) - * "customfield_10009": [ {"name": "jsmith" }, {"name": "bjones" }, {"name": "tdurden" }] - * - * MultiSelect (access key -> value) - * "customfield_10008": [ {"value": "red" }, {"value": "blue" }, {"value": "green" }] - * - * - - * - * - **/ - function getCustomFieldsAttribute($name,$objCFSet) - { - $cfSet = get_object_vars($objCFSet); - $cfSet = $cfSet['customField']; - - foreach ($cfSet as $cf) - { - $cf = (array)$cf; - $cfJIRAID = $cf['customfieldId']; - $valueSet = (array)$cf['values']; - $loop2do = count($valueSet); - - $dummy = null; - $cfType = strtolower((string)$cf['type']); - switch($cfType) - { - case 'numberfield': - $dummy = (float)$valueSet['value']; - break; - - case 'datepickerfield': - case 'datetimefield': - case 'freetextfield': - $dummy = (string)$valueSet['value']; - break; - - case 'radiobutton': - $dummy = array('value' => (string)$valueSet['value']); - break; - case 'selectlist': - // "customfield_10012": { "value": "red" } - $dummy = array('id' => (string)$valueSet['value']); - break; - - case 'userpicker': - // "customfield_10012": { "value": "admin" } - $dummy = array('name' => (string)$valueSet['value']); - break; - - case 'labels': - for($vdx=0; $vdx <= $loop2do; $vdx++) - { - $dummy[] = (string)$valueSet['value'][$vdx]; - } - break; - - case 'multigrouppicker': - case 'multiuserpicker': - // access key -> name - for($vdx=0; $vdx <= $loop2do; $vdx++) - { - $dummy[] = array('name' => (string)$valueSet['value'][$vdx]); - } - break; - - case 'multiselect': - // access key -> value) - for($vdx=0; $vdx <= $loop2do; $vdx++) - { - $dummy[] = array('value' => (string)$valueSet['value'][$vdx]); - } - break; - } - $this->issueAttr[$cfJIRAID] = $dummy; - } - } - - /** - * - * - **/ - function checkCfg() - { - $status_ok = true; - if( property_exists($this->cfg, 'projectkey') ) - { - $pk = trim((string)($this->cfg->projectkey)); - if($pk == '') - { - $status_ok = false; - $msg = __CLASS__ . ' - Empty configuration: '; - } - } - else - { - // this is oK if user only wants to LINK issues - $this->cfg->projectkey = self::NOPROJECTKEY; - } - - if(!$status_ok) - { - tLog(__METHOD__ . ' / ' . $msg , 'ERROR'); - } - return $status_ok; - } - - - /** - * - * If connection fails $this->defaultResolvedStatus is null - * - */ - public function setResolvedStatusCfg() - { - if( property_exists($this->cfg,'resolvedstatus') ) - { - $statusCfg = (array)$this->cfg->resolvedstatus; - } - else - { - $statusCfg['status'] = $this->defaultResolvedStatus; - } - - $this->resolvedStatus = new stdClass(); - $this->resolvedStatus->byCode = array(); - if(!is_null($statusCfg['status'])) - { - foreach($statusCfg['status'] as $cfx) - { - $e = (array)$cfx; - $this->resolvedStatus->byCode[$e['code']] = $e['verbose']; - } - } - $this->resolvedStatus->byName = array_flip($this->resolvedStatus->byCode); - } - - - +name = $name; + $this->interfaceViaDB = false; + $this->support = new jiraCommons(); + $this->support->guiCfg = array( + 'use_decoration' => true + ); + + // This is the right way to overwrite ONLY + // the keys we want, and preserve the default + // configuration present in the issueTrackerInterface class + $this->methodOpt['buildViewBugLink'] = array_merge( + $this->methodOpt['buildViewBugLink'], + array( + 'addSummary' => true, + 'colorByStatus' => false + )); + + if ($this->setCfg($config) && $this->checkCfg()) { + $this->completeCfg(); + $this->connect(); + $this->guiCfg = array( + 'use_decoration' => true + ); + + if ($this->isConnected()) { + $this->setResolvedStatusCfg(); + } + } + } + + /** + */ + public function getIssueAttr() + { + return $this->issueAttr; + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; // be sure no double // at end + + if (! property_exists($this->cfg, 'uriapi')) { + $this->cfg->uriapi = $base . 'rest/api/latest/'; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'browse/'; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base . ''; + } + + if (property_exists($this->cfg, 'attributes')) { + $this->processAttributes(); + } + + if (! property_exists($this->cfg, 'userinteraction')) { + $this->cfg->userinteraction = 0; + } + + if (! property_exists($this->cfg, 'createissueviaapi')) { + $this->cfg->createissueviaapi = 0; + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxString($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $this->jiraCfg = array( + 'username' => (string) trim($this->cfg->username), + 'password' => (string) trim($this->cfg->password), + 'host' => (string) trim($this->cfg->uriapi) + ); + + $this->jiraCfg['proxy'] = config_get('proxy'); + if (! is_null($this->jiraCfg['proxy']) && + is_null($this->jiraCfg['proxy']->host)) { + $this->jiraCfg['proxy'] = null; + } + + $this->APIClient = new JiraApi\Jira($this->jiraCfg); + + $this->connected = $this->APIClient->testLogin(); + if ($this->connected && + ($this->cfg->projectkey != self::NOPROJECTKEY)) { + // Now check if can get info about the project, to understand + // if at least it exists. + $pk = trim((string) $this->cfg->projectkey); + $this->APIClient->getProject($pk); + + $statusSet = $this->APIClient->getStatuses(); + foreach ($statusSet as $statusID => $statusName) { + $this->statusDomain[$statusName] = $statusID; + } + + $this->defaultResolvedStatus = $this->support->initDefaultResolvedStatus( + $this->statusDomain); + } + } catch (Exception $e) { + $this->connected = false; + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + + $issue = null; + try { + $issue = $this->APIClient->getIssue($issueID); + + // IMPORTANT NOTICE + // $issue->id do not contains ISSUE ID as displayed on GUI, but what seems to be an internal value. + // $issue->key has what we want. + // Very strange is how have this worked till today ?? (2015-01-24) + if (! is_null($issue) && is_object($issue) && + ! property_exists($issue, 'errorMessages')) { + + // We are going to have a set of standard properties + $issue->id = $issue->key; + $issue->summary = $issue->fields->summary; + $issue->statusCode = $issue->fields->status->id; + $issue->statusVerbose = $issue->fields->status->name; + + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusHTMLString = $this->support->buildStatusHTMLString( + $issue->statusVerbose); + $issue->summaryHTMLString = $this->support->buildSummaryHTMLString( + $issue); + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + + /* + * for debug porpouses + * $tlIssue = new stdClass(); + * $tlIssue->IDHTMLString = $issue->IDHTMLString; + * $tlIssue->statusCode = $issue->statusCode; + * $tlIssue->statusVerbose = $issue->statusVerbose; + * $tlIssue->statusHTMLString = $issue->statusHTMLString; + * $tlIssue->summaryHTMLString = $issue->summaryHTMLString; + * $tlIssue->isResolved = $issue->isResolved; + * + * var_dump($tlIssue); + */ + } else { + $issue = null; + } + } catch (Exception $e) { + tLog("JIRA Ticket ID $issueID - " . $e->getMessage(), 'WARNING'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * + * @param + * string issueID + * @return string + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return $issue->summaryHTMLString; + } + + /** + * + * @param + * string issueID + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + /** + * JSON example: + * + * { + * "fields": { + * "project": { + * "key": "TEST" + * }, + * "summary": "REST ye merry gentlemen.", + * "description": "Creating of an issue using project keys and issue type names using the REST API", + * "issuetype": { + * "name": "Bug" + * } + * } + * } + */ + public function addIssue($summary, $description, $opt = null) + { + try { + $issue = array( + 'fields' => array( + 'project' => array( + 'key' => (string) $this->cfg->projectkey + ), + 'summary' => $summary, + 'description' => $description, + 'issuetype' => 1 /* Bug */ + ) + ); + + if (property_exists($this->cfg, 'issuetype')) { + $issue['fields']['issuetype'] = array( + 'id' => (int) $this->cfg->issuetype + ); + } + + $prio = null; + if (property_exists($this->cfg, 'issuepriority')) { + $prio = $this->cfg->issuepriority; + } + if (! is_null($opt) && property_exists($opt, 'issuePriority')) { + $prio = $opt->issuePriority; + } + if (! is_null($prio)) { + // CRITIC: if not casted to string, you will get following error from JIRA + // "Could not find valid 'id' or 'name' in priority object." + $issue['fields']['priority'] = array( + 'id' => (string) $prio + ); + } + + if (! is_null($this->issueAttr)) { + $issue['fields'] = array_merge($issue['fields'], + $this->issueAttr); + } + + if (! is_null($opt)) { + // these can have multiple values + if (property_exists($opt, 'artifactComponent')) { + // YES is plural!! + $issue['fields']['components'] = array(); + foreach ($opt->artifactComponent as $vv) { + $issue['fields']['components'][] = array( + 'id' => (string) $vv + ); + } + } + + if (property_exists($opt, 'artifactVersion')) { + // YES is plural!! + $issue['fields']['versions'] = array(); + foreach ($opt->artifactVersion as $vv) { + $issue['fields']['versions'][] = array( + 'id' => (string) $vv + ); + } + } + + if (property_exists($opt, 'reporter')) { + $issue['fields']['reporter'] = array( + 'id' => (string) $opt->reporter + ); + } + + if (property_exists($opt, 'issueType')) { + $issue['fields']['issuetype'] = array( + 'id' => $opt->issueType + ); + } + } + + $op = $this->APIClient->createIssue($issue); + $ret = array( + 'status_ok' => false, + 'id' => null, + 'msg' => 'ko' + ); + if (! is_null($op)) { + if (isset($op->errors)) { + $ret['msg'] = __FUNCTION__ . ":Failure:JIRA Message:\n"; + foreach ($op->errors as $pk => $pv) { + $ret['msg'] .= "$pk => $pv\n"; + } + } else { + $ret = array( + 'status_ok' => true, + 'id' => $op->key, + 'msg' => sprintf(lang_get('jira_bug_created'), $summary, + $issue['fields']['project']['key']) + ); + } + } + } catch (Exception $e) { + $msg = "Create JIRA Ticket (REST) FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + * on JIRA notes is called comment + */ + public function addNote($issueID, $noteText, $opt = null) + { + try { + $op = $this->APIClient->addComment($noteText, $issueID); + $ret = array( + 'status_ok' => false, + 'id' => null, + 'msg' => 'ko' + ); + if (! is_null($op)) { + if (isset($op->errors)) { + $ret['msg'] = $op->errors; + } else { + $ret = array( + 'status_ok' => true, + 'id' => $op->key, + 'msg' => sprintf(lang_get('jira_comment_added'), + $issueID) + ); + } + } + } catch (Exception $e) { + $msg = "Add JIRA Issue Comment (REST) FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + */ + public function getIssueTypes() + { + try { + return $this->APIClient->getIssueTypes(); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getPriorities() + { + try { + return $this->APIClient->getPriorities(); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getVersions() + { + $items = null; + try { + $items = $this->APIClient->getVersions( + (string) $this->cfg->projectkey); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + return $items; + } + + /** + */ + public function getComponents() + { + try { + return $this->APIClient->getComponents( + (string) $this->cfg->projectkey); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getCreateIssueFields() + { + try { + return $this->APIClient->getCreateIssueFields( + (string) $this->cfg->projectkey); + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function getIssueTypesForHTMLSelect() + { + return array( + 'items' => $this->objectAttrToIDName($this->getIssueTypes()), + 'isMultiSelect' => false + ); + } + + /** + */ + public function getPrioritiesForHTMLSelect() + { + return array( + 'items' => $this->objectAttrToIDName($this->getPriorities()), + 'isMultiSelect' => false + ); + } + + /** + */ + public function getVersionsForHTMLSelect() + { + $input = array( + 'items' => null, + 'isMultiSelect' => true + ); + $items = $this->getVersions(); + if (! is_null($items)) { + $input['items'] = $this->objectAttrToIDName($items); + } else { + $input = null; + } + return $input; + } + + /** + */ + public function getComponentsForHTMLSelect() + { + $items = $this->getComponents(); + $input = array( + 'items' => null, + 'isMultiSelect' => true + ); + if (! is_null($items)) { + $input['items'] = $this->objectAttrToIDName($items); + } else { + $input = null; + } + return $input; + } + + /** + */ + private function objectAttrToIDName($attrSet) + { + $ret = null; + if (! is_null($attrSet)) { + $ic = count($attrSet); + for ($idx = 0; $idx < $ic; $idx ++) { + $ret[$attrSet[$idx]->id] = $attrSet[$idx]->name; + } + } + return $ret; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "JIRA LOGIN NAME\n" . + "JIRA PASSWORD\n" . + "https://testlink.atlassian.net/\n" . + "\n" . + "https://testlink.atlassian.net/rest/api/latest/\n" . + "https://testlink.atlassian.net/browse/\n" . + "1/0\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "JIRA PROJECT KEY\n" . + "JIRA ISSUE TYPE ID\n" . + "JIRA ISSUE PRIORITY ID\n" . "\n" . "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + $status_ok = false; + + // The VERY Mandatory KEY + if (property_exists($this->cfg, 'projectkey')) { + $pk = trim((string) ($this->cfg->projectkey)); + $status_ok = ($pk !== ''); + } + + if ($status_ok && $this->cfg->userinteraction == 0) { + $status_ok = property_exists($this->cfg, 'issuetype'); + } + + return $status_ok; + } + + /** + */ + private function processAttributes() + { + $attr = get_object_vars($this->cfg->attributes); + foreach ($attr as $name => $elem) { + $name = (string) $name; + switch ($name) { + case 'customFieldValues': + $this->getCustomFieldsAttribute($name, $elem); + break; + } + } + } + + /** + * supported types: + * (see https://developer.atlassian.com/jiradev/api-reference/ + * jira-rest-apis/jira-rest-api-tutorials/ + * jira-rest-api-example-create-issue# + * JIRARESTAPIExample-CreateIssue-Exampleofcreatinganissueusingcustomfields) + * + * --------------------------------------------------------- + * Single Value (simple) Group: + * --------------------------------------------------------- + * + * DatePickerField + * "customfield_10002": "2011-10-03" + * + * DateTimeField + * "customfield_10003": "2011-10-19T10:29:29.908+1100" * + * + * FreeTextField + * "customfield_10004": "Free text goes here. Type away!" + * + * NumberField + * "customfield_10010": 42.07 + * + * --------------------------------------------------------- + * Pair Value (simple) Group: + * --------------------------------------------------------- + * + * RadioButtons + * "customfield_10012": { "value": "red" } + * + * SelectList + * "customfield_10013": { "value": "red" } + * + * --------------------------------------------------------- + * Multiple Values (simple) Group: + * --------------------------------------------------------- + * + * Labels (PHP Array of strings) + * "customfield_10006": ["examplelabelnumber1", "examplelabelnumber2"] + * + * + * --------------------------------------------------------- + * Multiple Values (COMPLEX) Group: + * --------------------------------------------------------- + * + * MultiGroupPicker (access key -> name) + * "customfield_10007": [{ "name": "admins" }, { "name": "jira-dev" }, + * { "name": "jira-users" }] + * + * MultiUserPicker (access key -> name) + * "customfield_10009": [ {"name": "jsmith" }, {"name": "bjones" }, {"name": "tdurden" }] + * + * MultiSelect (access key -> value) + * "customfield_10008": [ {"value": "red" }, {"value": "blue" }, {"value": "green" }] + */ + private function getCustomFieldsAttribute($name, $objCFSet) + { + $cfSet = get_object_vars($objCFSet); + $cfSet = $cfSet['customField']; + + foreach ($cfSet as $cf) { + $cf = (array) $cf; + $cfJIRAID = $cf['customfieldId']; + $valueSet = (array) $cf['values']; + $loop2do = count($valueSet); + + $dummy = null; + $cfType = strtolower((string) $cf['type']); + switch ($cfType) { + case 'numberfield': + $dummy = (float) $valueSet['value']; + break; + + case 'datepickerfield': + case 'datetimefield': + case 'freetextfield': + $dummy = (string) $valueSet['value']; + break; + + case 'radiobutton': + $dummy = array( + 'value' => (string) $valueSet['value'] + ); + break; + case 'selectlist': + // "customfield_10012": { "value": "red" } + $dummy = array( + 'id' => (string) $valueSet['value'] + ); + break; + + case 'userpicker': + // "customfield_10012": { "value": "admin" } + $dummy = array( + 'name' => (string) $valueSet['value'] + ); + break; + + case 'labels': + for ($vdx = 0; $vdx <= $loop2do; $vdx ++) { + $dummy[] = (string) $valueSet['value'][$vdx]; + } + break; + + case 'multigrouppicker': + case 'multiuserpicker': + // access key -> name + for ($vdx = 0; $vdx <= $loop2do; $vdx ++) { + $dummy[] = array( + 'name' => (string) $valueSet['value'][$vdx] + ); + } + break; + + case 'multiselect': + // access key -> value) + for ($vdx = 0; $vdx <= $loop2do; $vdx ++) { + $dummy[] = array( + 'value' => (string) $valueSet['value'][$vdx] + ); + } + break; + } + $this->issueAttr[$cfJIRAID] = $dummy; + } + } + + /** + */ + private function checkCfg() + { + $status_ok = true; + if (property_exists($this->cfg, 'projectkey')) { + $pk = trim((string) ($this->cfg->projectkey)); + if ($pk == '') { + $status_ok = false; + $msg = __CLASS__ . ' - Empty configuration: '; + } + } else { + // this is oK if user only wants to LINK issues + $this->cfg->projectkey = self::NOPROJECTKEY; + } + + if (! $status_ok) { + tLog(__METHOD__ . ' / ' . $msg, 'ERROR'); + } + return $status_ok; + } + + /** + * If connection fails $this->defaultResolvedStatus is null + */ + public function setResolvedStatusCfg() + { + if (property_exists($this->cfg, 'resolvedstatus')) { + $statusCfg = (array) $this->cfg->resolvedstatus; + } else { + $statusCfg['status'] = $this->defaultResolvedStatus; + } + + $this->resolvedStatus = new stdClass(); + $this->resolvedStatus->byCode = array(); + if (! is_null($statusCfg['status'])) { + foreach ($statusCfg['status'] as $cfx) { + $e = (array) $cfx; + $this->resolvedStatus->byCode[$e['code']] = $e['verbose']; + } + } + $this->resolvedStatus->byName = array_flip( + $this->resolvedStatus->byCode); + } + + /** + */ + public function getUserAccountID($email) + { + try { + $u = $this->APIClient->getUserByEmail($email); + if (null != $u) { + return $u->accountId; + } + return null; + } catch (Exception $e) { + tLog(__METHOD__ . " " . $e->getMessage(), 'ERROR'); + } + } } diff --git a/lib/issuetrackerintegration/jirasoapInterface.class.php b/lib/issuetrackerintegration/jirasoapInterface.class.php index c7529f410a..838d3620ce 100644 --- a/lib/issuetrackerintegration/jirasoapInterface.class.php +++ b/lib/issuetrackerintegration/jirasoapInterface.class.php @@ -1,547 +1,543 @@ - 'its_duedate_with_separator'); - - private $soapOpt = array("connection_timeout" => 1, 'exceptions' => 1); - private $issueDefaults; - private $issueAttr = null; - - var $defaultResolvedStatus; - var $support; - - /** - * Construct and connect to BTS. - * - * @param str $type (see tlIssueTracker.class.php $systems property) - * @param xml $cfg - **/ - function __construct($type,$config,$name) - { - $this->name = $name; - $this->interfaceViaDB = false; - $this->support = new jiraCommons(); - $this->support->guiCfg = array('use_decoration' => true); - - $proxyCfg = config_get('proxy'); - if(!is_null($proxyCfg->host)) - { - $key2loop = array('host','port','login','password'); - foreach($key2loop as $fi) - { - if(!is_null($proxyCfg->$fi)) - { - $this->soapOpt['proxy_' . $fi] = $proxyCfg->$fi; - } - } - } - - $this->methodOpt = array('buildViewBugLink' => array('addSummary' => true, 'colorByStatus' => true)); - if( $this->setCfg($config) ) - { - $this->completeCfg(); - $this->connect(); - $this->guiCfg = array('use_decoration' => true); - - // Attention has to be done AFTER CONNECT OK, because we need info setted there - if( $this->isConnected()) - { - $this->setResolvedStatusCfg(); - } - } - } - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $step = 1; // just for debug - - $base = trim($this->cfg->uribase,"/") . '/' ; - if( !property_exists($this->cfg,'uriwsdl') ) - { - //DEBUG-echo __FUNCTION__ . "::Debug::Step#$step Going To Add uriwsdl
    ";$step++; - $this->cfg->uriwsdl = $base . 'rpc/soap/jirasoapservice-v2?wsdl'; - } - - if( !property_exists($this->cfg,'uriview') ) - { - //DEBUG-echo __FUNCTION__ . "::Debug::Step#$step Going To Add uriview
    ";$step++; - $this->cfg->uriview = $base . 'browse/'; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - //DEBUG-echo __FUNCTION__ . "::Debug::Step#$step Going To Add uricreate
    ";$step++; - $this->cfg->uricreate = $base . 'secure/CreateIssue!default.jspa'; - } - - - if( property_exists($this->cfg,'attributes') ) - { - // echo __FUNCTION__ . "::Debug::Step#$step Going To Add attributes
    ";$step++; - $this->processAttributes(); - } - - $this->issueDefaults = array('issuetype' => 1); - foreach($this->issueDefaults as $prop => $default) - { - if(!isset($this->issueAttr[$prop])) - { - $this->issueAttr[$prop] = $default; - } - // $this->cfg->$prop = (string)(property_exists($this->cfg,$prop) ? $this->cfg->$prop : $default); - } - } - - - - /** - * @internal precondition: TestLink has to be connected to Jira - * - * @param string issueID - * - **/ - function getIssue($issueID) - { - $issue = null; - try - { - $issue = $this->APIClient->getIssue($this->authToken, $issueID); - - if(!is_null($issue) && is_object($issue)) - { - // We are going to have a set of standard properties - $issue->id = $issueID; - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = $issue->status; - $issue->statusVerbose = array_search($issue->statusCode, $this->statusDomain); - $issue->statusHTMLString = $this->support->buildStatusHTMLString($issue->statusVerbose); - $issue->summaryHTMLString = $this->support->buildSummaryHTMLString($issue); - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - - } - } - catch (Exception $e) - { - tLog("JIRA Ticket ID $issueID - " . $e->getMessage(), 'WARNING'); - $issue = null; - } - - return $issue; - } - - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxString($issueID); - } - - - /** - * establishes connection to the bugtracking system - * - * @return bool returns true if the soap connection was established and the - * wsdl could be downloaded, false else - * - **/ - function connect() - { - $this->interfaceViaDB = false; - $op = $this->getClient(array('log' => true)); - // echo '
    OP
    ';var_dump($op); - - if( ($this->connected = $op['connected']) ) - { - // OK, we have got WSDL => server is up and we can do SOAP calls, but now we need - // to do a simple call with user/password only to understand if we are really connected - try - { - $this->APIClient = $op['client']; - $this->authToken = $this->APIClient->login($this->cfg->username, $this->cfg->password); - $statusSet = $op['client']->getStatuses($this->authToken); - foreach ($statusSet as $key => $pair) - { - $this->statusDomain[$pair->name]=$pair->id; - } - - $this->defaultResolvedStatus = $this->support->initDefaultResolvedStatus($this->statusDomain); - $this->l18n = init_labels($this->labels); - } - catch (SoapFault $f) - { - $this->connected = false; - $msg = __CLASS__ . " - SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; - // echo $msg; - tLog($msg,"ERROR"); - } - } - return $this->connected; - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - function getClient($opt=null) - { - // IMPORTANT NOTICE - 2012-01-06 - If you are using XDEBUG, Soap Fault will not work - $res = array('client' => null, 'connected' => false, 'msg' => 'generic ko'); - $my['opt'] = array('log' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - try - { - // IMPORTANT NOTICE - // $this->cfg is a simpleXML object, then is ABSOLUTELY CRITICAL - // DO CAST any member before using it. - // If we do following call WITHOUT (string) CAST, SoapClient() fails - // complaining '... wsdl has to be an STRING or null ...' - // - - $res['client'] = new SoapClient((string)$this->cfg->uriwsdl,$this->soapOpt); - $res['connected'] = true; - $res['msg'] = 'iupi!!!'; - } - catch (SoapFault $f) - { - $res['connected'] = false; - $res['msg'] = "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; - if($my['opt']['log']) - { - tLog("SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})","ERROR"); - } - } - return $res; - } - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $template = "\n" . - "\n" . - "JIRA LOGIN NAME\n" . - "JIRA PASSWORD\n" . - "http://testlink.atlassian.net/\n" . - "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . - "testlink.atlassian.net/browse/\n" . - "testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . - "\n" . - "JIRA PROJECT KEY\n" . - "JIRA ISSUE TYPE\n" . - "\n" . - "\n" . - " --> \n" . - " \n" . - " \n" . - " customfield_10800\n" . - " 111\n" . - " \n" . - " \n" . - " customfield_10900\n" . - " Yamaha Factory RacingDucati\n" . - " \n" . - " \n" . - " -->\n" . - "\n" . - "\n" . - "5Resolved\n" . - "6Closed\n" . - "\n" . - "\n"; - return $template; - } - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public function getStatusDomain() - { - return $this->statusDomain; - } - - public static function checkEnv() - { - $ret = array(); - $ret['status'] = extension_loaded('soap'); - $ret['msg'] = $ret['status'] ? 'OK' : 'You need to enable SOAP extension'; - return $ret; - } - - - // useful info - // https://github.com/ricardocasares/jira-soap-api - // - // CRITIC ISSUE TYPE IS MANDATORY. - // - public function addIssue($summary,$description) - { - try - { - $issue = array('project' => (string)$this->cfg->projectkey, - 'type' => (int)$this->cfg->issuetype, - 'summary' => $summary, - 'description' => $description); - - - if(!is_null($this->issueAttr)) - { - $issue = array_merge($issue,$this->issueAttr); - } - - //DEBUG-echo 'This Will Be Sent to JIRA
    ';echo '
    ';var_dump($issue);echo '
    '; - - $op = $this->APIClient->createIssue($this->authToken, $issue); - $ret = array('status_ok' => true, 'id' => $op->key, - 'msg' => sprintf(lang_get('jira_bug_created'),$summary,$issue['project'])); - } - catch (Exception $e) - { - $msg = "Create JIRA Ticket FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - } - return $ret; - } - - - /** - * - * If connection fails $this->defaultResolvedStatus is null - * - */ - public function setResolvedStatusCfg() - { - if( property_exists($this->cfg,'resolvedstatus') ) - { - $statusCfg = (array)$this->cfg->resolvedstatus; - } - else - { - $statusCfg['status'] = $this->defaultResolvedStatus; - } - - $this->resolvedStatus = new stdClass(); - $this->resolvedStatus->byCode = array(); - if(!is_null($statusCfg['status'])) - { - foreach($statusCfg['status'] as $cfx) - { - $e = (array)$cfx; - $this->resolvedStatus->byCode[$e['code']] = $e['verbose']; - } - } - $this->resolvedStatus->byName = array_flip($this->resolvedStatus->byCode); - } - - /** - * - */ - public function addIssueFromArray($issue) - { - try - { - - $op = $this->APIClient->createIssue($this->authToken, $issue); - $ret = array('status_ok' => true, 'id' => $op->key, - 'msg' => sprintf(lang_get('jira_bug_created'),$summary,$issue['project'])); - } - catch (Exception $e) - { - $msg = "Create JIRA Ticket FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - } - return $ret; - } - - /** - * - **/ - function canCreateViaAPI() - { - return (property_exists($this->cfg, 'projectkey') && - property_exists($this->cfg, 'issuetype')); - } - - - -/** - * - **/ - function processAttributes() - { - $attr = get_object_vars($this->cfg->attributes); - foreach ($attr as $name => $elem) - { - $name = (string)$name; - switch($name) - { - case 'customFieldValues': - $this->getCustomFieldsAttribute($name,$elem); - break; - - case 'affectsVersions': - $this->getAffectsVersionsAttribute($name,$elem); - break; - - default: - $this->getRelaxedAttribute($name,$elem); - break; - } - } - } - - - /** - * - **/ - function getRelaxedAttribute($name,$elem) - { - if( is_object($elem) ) - { - $ovars = get_object_vars($elem); - $cc = (array)current($ovars); - $kk = key($ovars); - foreach($cc as $value) - { - $this->issueAttr[$name][] = array($kk => (string)$value); - } - } - else - { - $this->issueAttr[$name] = (string)$elem; - } - } - - /** - * - * - **/ - function getCustomFieldsAttribute($name,$objCFSet) - { - // loop on fields of a Custom Field - // According to JIRA Documentation and some hands on examples - // customfieldId, key, values => has to be sent as an array - // - $elem = get_object_vars($objCFSet); - $elem = $elem['customField']; - - // Because how XML works, when we have ONLY one CF we do not get an array, - // but only when we have more. - // This forces us to do this kind of processing => cast always to an array, - // but paying special attention to complex elements. - // Remember we get data from simpleXML processing - // - if(is_object($elem)) - { - $elem = array($elem); - } - - foreach ($elem as $item) - { - // dev notes - // key attribute is not managed yet - // may be trim on each $item->values->value will be good - $this->issueAttr[$name][] = array('customfieldId' => trim((string)$item->customfieldId), - 'values' => (array)$item->values->value); - } - } - - - /** - * - * - * - * 10000 - * - * - * - * - * 10002 - * - * - * - * - * - * - **/ - function getAffectsVersionsAttribute($name,$objItemSet) - { - $elem = get_object_vars($objItemSet); - $elem = $elem['version']; - - // Because how XML works, when we have ONLY one CF we do not get an array, - // but only when we have more. - // This forces us to do this kind of processing => cast always to an array, - // but paying special attention to complex elements. - // Remember we get data from simpleXML processing - // - if(is_object($elem)) - { - $elem = array($elem); - } - - foreach ($elem as $item) - { - $this->issueAttr[$name][] = array('id' => trim((string)$item->id), - 'archived' => trim((string)$item->archived), - 'released' => trim((string)$item->released)); - } - } -} \ No newline at end of file + 'its_duedate_with_separator' + ); + + private $soapOpt = array( + "connection_timeout" => 1, + 'exceptions' => 1 + ); + + private $issueDefaults; + + private $issueAttr = null; + + public $defaultResolvedStatus; + + public $support; + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + $this->name = $name; + $this->interfaceViaDB = false; + $this->support = new jiraCommons(); + $this->support->guiCfg = array( + 'use_decoration' => true + ); + + $proxyCfg = config_get('proxy'); + if (! is_null($proxyCfg->host)) { + $key2loop = array( + 'host', + 'port', + 'login', + 'password' + ); + foreach ($key2loop as $fi) { + if (! is_null($proxyCfg->$fi)) { + $this->soapOpt['proxy_' . $fi] = $proxyCfg->$fi; + } + } + } + + $this->methodOpt = array( + 'buildViewBugLink' => array( + 'addSummary' => true, + 'colorByStatus' => true + ) + ); + if ($this->setCfg($config)) { + $this->completeCfg(); + $this->connect(); + $this->guiCfg = array( + 'use_decoration' => true + ); + + // Attention has to be done AFTER CONNECT OK, because we need info setted there + if ($this->isConnected()) { + $this->setResolvedStatusCfg(); + } + } + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; + if (! property_exists($this->cfg, 'uriwsdl')) { + // DEBUG-echo __FUNCTION__ . "::Debug::Step#$step Going To Add uriwsdl
    ";$step++; + $this->cfg->uriwsdl = $base . 'rpc/soap/jirasoapservice-v2?wsdl'; + } + + if (! property_exists($this->cfg, 'uriview')) { + // DEBUG-echo __FUNCTION__ . "::Debug::Step#$step Going To Add uriview
    ";$step++; + $this->cfg->uriview = $base . 'browse/'; + } + + if (! property_exists($this->cfg, 'uricreate')) { + // DEBUG-echo __FUNCTION__ . "::Debug::Step#$step Going To Add uricreate
    ";$step++; + $this->cfg->uricreate = $base . 'secure/CreateIssue!default.jspa'; + } + + if (property_exists($this->cfg, 'attributes')) { + $this->processAttributes(); + } + + $this->issueDefaults = array( + 'issuetype' => 1 + ); + foreach ($this->issueDefaults as $prop => $default) { + if (! isset($this->issueAttr[$prop])) { + $this->issueAttr[$prop] = $default; + } + } + + if (! property_exists($this->cfg, 'userinteraction')) { + $this->cfg->userinteraction = 0; + } + + if (! property_exists($this->cfg, 'createissueviaapi')) { + $this->cfg->createissueviaapi = 0; + } + } + + /** + * + * @internal precondition: TestLink has to be connected to Jira + * + * @param + * string issueID + * + */ + public function getIssue($issueID) + { + $issue = null; + try { + $issue = $this->APIClient->getIssue($this->authToken, $issueID); + + if (! is_null($issue) && is_object($issue)) { + // We are going to have a set of standard properties + $issue->id = $issueID; + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = $issue->status; + $issue->statusVerbose = array_search($issue->statusCode, + $this->statusDomain); + $issue->statusHTMLString = $this->support->buildStatusHTMLString( + $issue->statusVerbose); + $issue->summaryHTMLString = $this->support->buildSummaryHTMLString( + $issue); + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + } + } catch (Exception $e) { + tLog("JIRA Ticket ID $issueID - " . $e->getMessage(), 'WARNING'); + $issue = null; + } + + return $issue; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxString($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool returns true if the soap connection was established and the + * wsdl could be downloaded, false else + * + */ + public function connect() + { + $this->interfaceViaDB = false; + $op = $this->getClient(array( + 'log' => true + )); + + if ($this->connected = $op['connected']) { + // OK, we have got WSDL => server is up and we can do SOAP calls, but now we need + // to do a simple call with user/password only to understand if we are really connected + try { + $this->APIClient = $op['client']; + $this->authToken = $this->APIClient->login($this->cfg->username, + $this->cfg->password); + $statusSet = $op['client']->getStatuses($this->authToken); + foreach ($statusSet as $pair) { + $this->statusDomain[$pair->name] = $pair->id; + } + + $this->defaultResolvedStatus = $this->support->initDefaultResolvedStatus( + $this->statusDomain); + $this->l18n = init_labels($this->labels); + } catch (SoapFault $f) { + $this->connected = false; + $msg = __CLASS__ . + " - SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; + tLog($msg, "ERROR"); + } + } + return $this->connected; + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getClient($opt = null) + { + // IMPORTANT NOTICE - 2012-01-06 - If you are using XDEBUG, Soap Fault will not work + $res = array( + 'client' => null, + 'connected' => false, + 'msg' => 'generic ko' + ); + $my['opt'] = array( + 'log' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + try { + // IMPORTANT NOTICE + // $this->cfg is a simpleXML object, then is ABSOLUTELY CRITICAL + // DO CAST any member before using it. + // If we do following call WITHOUT (string) CAST, SoapClient() fails + // complaining '... wsdl has to be an STRING or null ...' + // + + $res['client'] = new SoapClient((string) $this->cfg->uriwsdl, + $this->soapOpt); + $res['connected'] = true; + $res['msg'] = 'iupi!!!'; + } catch (SoapFault $f) { + $res['connected'] = false; + $res['msg'] = "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; + if ($my['opt']['log']) { + tLog( + "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})", + "ERROR"); + } + } + return $res; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "JIRA LOGIN NAME\n" . + "JIRA PASSWORD\n" . + "http://testlink.atlassian.net/\n" . + "http://testlink.atlassian.net/rpc/soap/jirasoapservice-v2?wsdl\n" . + "testlink.atlassian.net/browse/\n" . + "testlink.atlassian.net/secure/CreateIssue!default.jspa\n" . + "\n" . + "JIRA PROJECT KEY\n" . + "JIRA ISSUE TYPE\n" . + "\n" . + "\n" . + " --> \n" . " \n" . + " \n" . + " customfield_10800\n" . + " 111\n" . + " \n" . " \n" . + " customfield_10900\n" . + " Yamaha Factory RacingDucati\n" . + " \n" . " \n" . + " -->\n" . + "\n" . + "\n" . + "5Resolved\n" . + "6Closed\n" . + "\n" . "\n"; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public function getStatusDomain() + { + return $this->statusDomain; + } + + public static function checkEnv() + { + $ret = array(); + $ret['status'] = extension_loaded('soap'); + $ret['msg'] = $ret['status'] ? 'OK' : 'You need to enable SOAP extension'; + return $ret; + } + + // useful info + // https://github.com/ricardocasares/jira-soap-api + // + // CRITIC ISSUE TYPE IS MANDATORY. + // + public function addIssue($summary, $description) + { + try { + $issue = array( + 'project' => (string) $this->cfg->projectkey, + 'type' => (int) $this->cfg->issuetype, + 'summary' => $summary, + 'description' => $description + ); + + if (! is_null($this->issueAttr)) { + $issue = array_merge($issue, $this->issueAttr); + } + + // DEBUG-echo 'This Will Be Sent to JIRA
    ';echo '
    ';var_dump($issue);echo '
    '; + + $op = $this->APIClient->createIssue($this->authToken, $issue); + $ret = array( + 'status_ok' => true, + 'id' => $op->key, + 'msg' => sprintf(lang_get('jira_bug_created'), $summary, + $issue['project']) + ); + } catch (Exception $e) { + $msg = "Create JIRA Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + * If connection fails $this->defaultResolvedStatus is null + */ + public function setResolvedStatusCfg() + { + if (property_exists($this->cfg, 'resolvedstatus')) { + $statusCfg = (array) $this->cfg->resolvedstatus; + } else { + $statusCfg['status'] = $this->defaultResolvedStatus; + } + + $this->resolvedStatus = new stdClass(); + $this->resolvedStatus->byCode = array(); + if (! is_null($statusCfg['status'])) { + foreach ($statusCfg['status'] as $cfx) { + $e = (array) $cfx; + $this->resolvedStatus->byCode[$e['code']] = $e['verbose']; + } + } + $this->resolvedStatus->byName = array_flip( + $this->resolvedStatus->byCode); + } + + /** + */ + public function addIssueFromArray($issue) + { + try { + + $op = $this->APIClient->createIssue($this->authToken, $issue); + $ret = array( + 'status_ok' => true, + 'id' => $op->key, + 'msg' => sprintf(lang_get('jira_bug_created'), $summary, + $issue['project']) + ); + } catch (Exception $e) { + $msg = "Create JIRA Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + */ + public function canCreateViaAPI() + { + return property_exists($this->cfg, 'projectkey') && + property_exists($this->cfg, 'issuetype'); + } + + /** + */ + private function processAttributes() + { + $attr = get_object_vars($this->cfg->attributes); + foreach ($attr as $name => $elem) { + $name = (string) $name; + switch ($name) { + case 'customFieldValues': + $this->getCustomFieldsAttribute($name, $elem); + break; + + case 'affectsVersions': + $this->getAffectsVersionsAttribute($name, $elem); + break; + + default: + $this->getRelaxedAttribute($name, $elem); + break; + } + } + } + + /** + */ + private function getRelaxedAttribute($name, $elem) + { + if (is_object($elem)) { + $ovars = get_object_vars($elem); + $cc = (array) current($ovars); + $kk = key($ovars); + foreach ($cc as $value) { + $this->issueAttr[$name][] = array( + $kk => (string) $value + ); + } + } else { + $this->issueAttr[$name] = (string) $elem; + } + } + + /** + */ + private function getCustomFieldsAttribute($name, $objCFSet) + { + // loop on fields of a Custom Field + // According to JIRA Documentation and some hands on examples + // customfieldId, key, values => has to be sent as an array + // + $elem = get_object_vars($objCFSet); + $elem = $elem['customField']; + + // Because how XML works, when we have ONLY one CF we do not get an array, + // but only when we have more. + // This forces us to do this kind of processing => cast always to an array, + // but paying special attention to complex elements. + // Remember we get data from simpleXML processing + // + if (is_object($elem)) { + $elem = array( + $elem + ); + } + + foreach ($elem as $item) { + // dev notes + // key attribute is not managed yet + // may be trim on each $item->values->value will be good + $this->issueAttr[$name][] = array( + 'customfieldId' => trim((string) $item->customfieldId), + 'values' => (array) $item->values->value + ); + } + } + + /** + * + * + * 10000 + * + * + * + * + * 10002 + * + * + * + * + */ + private function getAffectsVersionsAttribute($name, $objItemSet) + { + $elem = get_object_vars($objItemSet); + $elem = $elem['version']; + + // Because how XML works, when we have ONLY one CF we do not get an array, + // but only when we have more. + // This forces us to do this kind of processing => cast always to an array, + // but paying special attention to complex elements. + // Remember we get data from simpleXML processing + // + if (is_object($elem)) { + $elem = array( + $elem + ); + } + + foreach ($elem as $item) { + $this->issueAttr[$name][] = array( + 'id' => trim((string) $item->id), + 'archived' => trim((string) $item->archived), + 'released' => trim((string) $item->released) + ); + } + } +} diff --git a/lib/issuetrackerintegration/kaitenrestInterface.class.php b/lib/issuetrackerintegration/kaitenrestInterface.class.php index 59aa1fe8f7..5e743714ec 100644 --- a/lib/issuetrackerintegration/kaitenrestInterface.class.php +++ b/lib/issuetrackerintegration/kaitenrestInterface.class.php @@ -1,357 +1,388 @@ - 'archived', - '3' => 'deleted' - ]; - - public $defaultResolvedStatus; - - - /** - * Construct and connect to BTS. - * - * @param str $type (see tlIssueTracker.class.php $systems property) - * @param xml $cfg - **/ - function __construct($type,$config,$name) { - $this->name = $name; - $this->interfaceViaDB = false; - $this->methodOpt['buildViewBugLink'] = [ - 'addSummary' => true,'colorByStatus' => false, - 'addReporter' => false, 'addHandler' => false ]; - - $this->defaultResolvedStatus = []; - $this->defaultResolvedStatus[] = ['code' => 1, 'verbose' => 'queue']; - $this->defaultResolvedStatus[] = ['code' => 2, 'verbose' => 'in progress']; - $this->defaultResolvedStatus[] = ['code' => 3, 'verbose' => 'done']; - - $this->canSetReporter = true; - if( !$this->setCfg($config) ) { - return false; - } - - $this->completeCfg(); - $this->setResolvedStatusCfg(); - $this->connect(); - } - - /** - * - **/ - function completeCfg() { - $this->cfg->uribase = trim($this->cfg->uribase,"/"); - if(!property_exists($this->cfg, 'uricreate') ) { - $this->cfg->uricreate = $this->cfg->uribase; - } - - if( property_exists($this->cfg,'options') ) { - $option = get_object_vars($this->cfg->options); - foreach ($option as $name => $elem) { - $name = (string)$name; - $this->options[$name] = (string)$elem; - } - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() { - $processCatch = false; - - try { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $kaitenContext = [ - 'url' => (string)trim($this->cfg->uribase), - 'apikey' => (string)trim($this->cfg->apikey), - 'boardId' => (string)trim($this->cfg->boardid), - 'options' => $this->options ]; - - $tlContext = [ 'proxy' => config_get('proxy'), - 'cfg' => ['setcardowneremail' => - $this->cfg->setcardowneremail] ]; - $tlContext['cfg'] = (object)$tlContext['cfg']; - - $this->APIClient = new kaiten($kaitenContext,$tlContext); - // to undestand if connection is OK, I will ask for users. - try { - $items = $this->APIClient->getUsers(); - $this->connected = count($items) > 0 ? true : false; - unset($items); - } - catch(Exception $e) { - $processCatch = true; - } - } - catch(Exception $e) { - $processCatch = true; - } - - if($processCatch) { - $logDetails = ''; - foreach(['uribase'] as $v) { - $logDetails .= "$v={$this->cfg->$v} / "; - } - $logDetails = trim($logDetails,'/ '); - $this->connected = false; - tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() { - return $this->connected; - } - - /** - * - * - **/ - function buildViewBugURL($issueID) { - return $this->APIClient->getIssueURL($issueID); - } - - /** - * - * - **/ - public function getIssue($issueID) { - if (!$this->isConnected()) { - tLog(__METHOD__ . '/Not Connected ', 'ERROR'); - return false; - } - - $issue = null; - try { - $jsonObj = $this->APIClient->getIssue($issueID); - - if( !is_null($jsonObj) && is_object($jsonObj)) { - $conditionData = isset($this->conditionMap[$jsonObj->condition]) ? ' / '.$this->conditionMap[$jsonObj->condition] : ''; - $issue = new stdClass(); - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = (string)$jsonObj->state; - $issue->statusVerbose = $this->resolvedStatus->byCode[$issue->statusCode]; - $issue->statusHTMLString = "[{$issue->statusVerbose}{$conditionData}]"; - $issue->summary = $issue->summaryHTMLString = (string)$jsonObj->title; - $issue->isResolved = (int)$jsonObj->state == 3; - } - } - catch(Exception $e) { - tLog(__METHOD__ . '/' . $e->getMessage(),'ERROR'); - $issue = null; - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->state : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) { - $state = $this->getIssueStatusCode($issueID); - if ($state) { - return $this->resolvedStatus->byCode[$state]; - } - return false; - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) { - $issue = $this->getIssue($issueID); - return $issue->summaryHTMLString; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) { - if(($status_ok = $this->checkBugIDSyntax($issueID))) { - $issue = $this->getIssue($issueID); - $status_ok = is_object($issue) && !is_null($issue); - } - return $status_ok; - } - - /** - * - */ - function parseAddInfo($info) { - $result = [ 'descr' => $info, 'links' => [] ]; - - $pik = array('dl2tl' => lang_get('dl2tl'), - 'dl2tlpv' => lang_get('dl2tlpv')); - - $matches = array('dl2tl' => 0, 'dl2tlpv' => 0); - - foreach($pik as $ky => $vy ) { - preg_match('/^' . $vy . '(.+)$/imu', $info, $matches[$ky]); - if( count($matches[$ky]) > 1 ) { - $result['links'][] = [ - 'descr' => $vy, - 'url' => $matches[$ky][1] - ]; - } - } - - if (!empty($result['links'])) { - $result['descr'] = strstr($info, $result['links'][0]['descr'], true); - } - return $result; - } - - /** - * - */ - public function addIssue($summary,$moreInfo,$opt=null) { - $more = $this->parseAddInfo($moreInfo); - try { - $op = $this->APIClient->addIssue($summary, $more['descr'],$opt); - if(is_null($op)){ - throw new Exception("Error creating issue", 1); - } - - if (count($more['links']) > 0) { - $this->APIClient->addExternalLinks($op->id,$more['links']); - } - - $tags = null; - if (!empty($opt)) { - $tags = [ - ['name' => $opt->execContext['testplan_name']], - ['name' => $opt->execContext['build_name']] - ]; - } - if (null !== $tags) { - $this->APIClient->addTags($op->id,$tags); - } - - $ret = ['status_ok' => true, 'id' => (string)$op->id, - 'msg' => sprintf(lang_get('kaiten_bug_created'), - $summary, (string)$op->board_id)]; - } - catch (Exception $e) { - $msg = "Create KAITEN Card FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = ['status_ok' => false, 'id' => -1, 'msg' => $msg]; - } - return $ret; - } - - /** - * - */ - public function addNote($issueID,$noteText,$opt=null) { - $op = $this->APIClient->addNote($issueID, $noteText); - if(is_null($op)){ - throw new Exception("Error setting note", 1); - } - $ret = ['status_ok' => true, 'id' => (string)$op->iid, - 'msg' => sprintf(lang_get('kaiten_bug_comment'), - $op->body, $this->APIClient->projectId)]; - return $ret; - } - - /** - * - **/ - public static function getCfgTemplate() { - $tpl = "\n" . - "\n" . - "\n" . - "KAITEN API KEY\n" . - "https://company.kaiten.io\n" . - "BOARD IDENTIFICATOR\n" . - " \n" . - "0\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n"; - return $tpl; - } - - /** - * - **/ - function canCreateViaAPI() - { - return true; - } - - + 'archived', + '3' => 'deleted' + ]; + + public $defaultResolvedStatus; + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + $this->name = $name; + $this->interfaceViaDB = false; + $this->methodOpt['buildViewBugLink'] = [ + 'addSummary' => true, + 'colorByStatus' => false, + 'addReporter' => false, + 'addHandler' => false + ]; + + $this->defaultResolvedStatus = []; + $this->defaultResolvedStatus[] = [ + 'code' => 1, + 'verbose' => 'queue' + ]; + $this->defaultResolvedStatus[] = [ + 'code' => 2, + 'verbose' => 'in progress' + ]; + $this->defaultResolvedStatus[] = [ + 'code' => 3, + 'verbose' => 'done' + ]; + + $this->canSetReporter = true; + if (! $this->setCfg($config)) { + return false; + } + + $this->completeCfg(); + $this->setResolvedStatusCfg(); + $this->connect(); + } + + /** + */ + private function completeCfg() + { + $this->cfg->uribase = trim($this->cfg->uribase, "/"); + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $this->cfg->uribase; + } + + if (property_exists($this->cfg, 'options')) { + $option = get_object_vars($this->cfg->options); + foreach ($option as $name => $elem) { + $name = (string) $name; + $this->options[$name] = (string) $elem; + } + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + $processCatch = false; + + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $kaitenContext = [ + 'url' => (string) trim($this->cfg->uribase), + 'apikey' => (string) trim($this->cfg->apikey), + 'boardId' => (string) trim($this->cfg->boardid), + 'options' => $this->options + ]; + + $tlContext = [ + 'proxy' => config_get('proxy'), + 'cfg' => [ + 'setcardowneremail' => $this->cfg->setcardowneremail + ] + ]; + $tlContext['cfg'] = (object) $tlContext['cfg']; + + $this->APIClient = new kaiten($kaitenContext, $tlContext); + // to undestand if connection is OK, I will ask for users. + try { + $items = $this->APIClient->getUsers(); + $this->connected = count($items) > 0 ? true : false; + unset($items); + } catch (Exception $e) { + $processCatch = true; + } + } catch (Exception $e) { + $processCatch = true; + } + + if ($processCatch) { + $logDetails = ''; + foreach ([ + 'uribase' + ] as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function buildViewBugURL($issueID) + { + return $this->APIClient->getIssueURL($issueID); + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + + $issue = null; + try { + $jsonObj = $this->APIClient->getIssue($issueID); + + if (! is_null($jsonObj) && is_object($jsonObj)) { + $conditionData = isset($this->conditionMap[$jsonObj->condition]) ? ' / ' . + $this->conditionMap[$jsonObj->condition] : ''; + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = (string) $jsonObj->state; + $issue->statusVerbose = $this->resolvedStatus->byCode[$issue->statusCode]; + $issue->statusHTMLString = "[{$issue->statusVerbose}{$conditionData}]"; + $issue->summary = $issue->summaryHTMLString = (string) $jsonObj->title; + $issue->isResolved = (int) $jsonObj->state == 3; + } + } catch (Exception $e) { + tLog(__METHOD__ . '/' . $e->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->state : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + $state = $this->getIssueStatusCode($issueID); + if ($state) { + return $this->resolvedStatus->byCode[$state]; + } + return false; + } + + /** + * + * @param + * string issueID + * @return string + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return $issue->summaryHTMLString; + } + + /** + * + * @param + * string issueID + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + /** + */ + private function parseAddInfo($info) + { + $result = [ + 'descr' => $info, + 'links' => [] + ]; + + $pik = array( + 'dl2tl' => lang_get('dl2tl'), + 'dl2tlpv' => lang_get('dl2tlpv') + ); + + $matches = array( + 'dl2tl' => 0, + 'dl2tlpv' => 0 + ); + + foreach ($pik as $ky => $vy) { + preg_match('/^' . $vy . '(.+)$/imu', $info, $matches[$ky]); + if (count($matches[$ky]) > 1) { + $result['links'][] = [ + 'descr' => $vy, + 'url' => $matches[$ky][1] + ]; + } + } + + if (! empty($result['links'])) { + $result['descr'] = strstr($info, $result['links'][0]['descr'], true); + } + return $result; + } + + /** + */ + public function addIssue($summary, $moreInfo, $opt = null) + { + $more = $this->parseAddInfo($moreInfo); + try { + $op = $this->APIClient->addIssue($summary, $more['descr'], $opt); + if (is_null($op)) { + throw new Exception("Error creating issue", 1); + } + + if (count($more['links']) > 0) { + $this->APIClient->addExternalLinks($op->id, $more['links']); + } + + $tags = null; + if (! empty($opt)) { + $tags = [ + [ + 'name' => $opt->execContext['testplan_name'] + ], + [ + 'name' => $opt->execContext['build_name'] + ] + ]; + } + if (null !== $tags) { + $this->APIClient->addTags($op->id, $tags); + } + + $ret = [ + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => sprintf(lang_get('kaiten_bug_created'), $summary, + (string) $op->board_id) + ]; + } catch (Exception $e) { + $msg = "Create KAITEN Card FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = [ + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg + ]; + } + return $ret; + } + + /** + */ + public function addNote($issueID, $noteText, $opt = null) + { + $op = $this->APIClient->addNote($issueID, $noteText); + if (is_null($op)) { + throw new Exception("Error setting note", 1); + } + return [ + 'status_ok' => true, + 'id' => (string) $op->iid, + 'msg' => sprintf(lang_get('kaiten_bug_comment'), $op->body, + $this->APIClient->projectId) + ]; + } + + /** + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "\n" . + "KAITEN API KEY\n" . + "https://company.kaiten.io\n" . + "BOARD IDENTIFICATOR\n" . + " \n" . + "0\n" . + "\n" . + "\n" . "\n" . "\n" . + "\n" . "\n" . + "\n" . "\n" . + "\n" . "\n" . + "\n" . "\n" . + "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return true; + } } diff --git a/lib/issuetrackerintegration/mantisdbInterface.class.php b/lib/issuetrackerintegration/mantisdbInterface.class.php index 0d858cb6d4..37ce99ddea 100644 --- a/lib/issuetrackerintegration/mantisdbInterface.class.php +++ b/lib/issuetrackerintegration/mantisdbInterface.class.php @@ -1,254 +1,247 @@ - 'new',20 => 'feedback',30 => 'acknowledged', - 40 => 'confirmed',50 => 'assigned',80 => 'resolved',90 => 'closed'); - - private $status_color = array('new' => '#ffa0a0', # red, - 'feedback' => '#ff50a8', # purple - 'acknowledged' => '#ffd850', # orange - 'confirmed' => '#ffffb0', # yellow - 'assigned' => '#c8c8ff', # blue - 'resolved' => '#cceedd', # buish-green - 'closed' => '#e8e8e8'); # light gray - - var $defaultResolvedStatus; - - - /** - * Construct and connect to BTS. - * - * @param str $type (see tlIssueTracker.class.php $systems property) - * @param xml $cfg - **/ - function __construct($type,$config,$name) - { - - parent::__construct($type,$config,$name); - if( !$this->isConnected() ) - { - return false; - } - - $this->interfaceViaDB = true; - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = array('code' => 80, 'verbose' => 'resolved'); - $this->defaultResolvedStatus[] = array('code' => 90, 'verbose' => 'closed'); - - $this->setResolvedStatusCfg(); - - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => true); - $this->guiCfg = array('use_decoration' => true); - if( property_exists($this->cfg, 'statuscfg') ) - { - $this->setStatusCfg(); - } - } - - - /** - * Return the URL to the bugtracking page for viewing - * the bug with the given id. - * - * @param int id the bug id - * - * @return string returns a complete URL to view the bug - **/ - function buildViewBugURL($id) - { - return $this->cfg->uriview . urlencode($id); - } - - /** - * - **/ - function getIssue($id) - { - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - if (!$this->isConnected()) - { - return false; - } - $sql = "/* $debugMsg */ SELECT id,status,summary FROM mantis_bug_table " . - " WHERE id=" . intval($id); - - $rs = $this->dbConnection->fetchRowsIntoMap($sql,'id'); - $issue = null; - if( !is_null($rs) ) - { - $issueOnMantisDB = current($rs); - $issue = new stdClass(); - $issue->IDHTMLString = "{$id} : "; - $issue->summaryHTMLString = $issueOnMantisDB['summary']; - $issue->id = $issueOnMantisDB['id']; - $issue->summary = $issueOnMantisDB['summary']; - $issue->statusCode = $issueOnMantisDB['status']; - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - - if( isset($this->code_status[$issue->statusCode]) ) - { - $issue->statusVerbose = $this->code_status[$issue->statusCode]; - } - else - { - // give info to user on Event Viewer - $msg = lang_get('MANTIS_status_not_configured'); - $msg = sprintf($msg,$issueOnMantisDB['status']); - logWarningEvent($msg,"MANTIS INTEGRATION"); - $issue->statusVerbose = 'custom_undefined_on_tl'; - } - - $issue->statusHTMLString = $this->buildStatusHTMLString($issue->statusVerbose); - $issue->statusColor = isset($this->status_color[$issue->statusVerbose]) ? - $this->status_color[$issue->statusVerbose] : 'white'; - - } - return $issue; - } - - - - /** - * Returns the status of the bug with the given id - * this function is not directly called by TestLink. - * - * @return string returns the status of the given bug (if found in the db), or false else - **/ - function getBugStatus($id) - { - if (!$this->isConnected()) - { - return false; - } - $issue = $this->getIssue($id); - return (!is_null($issue) && $issue) ? $issue->statusVerbose : null; - } - - - /** - * checks is bug id is present on BTS - * - * @return integer returns 1 if the bug with the given id exists - **/ - function checkBugIDExistence($id) - { - $status_ok = 0; - $query = "SELECT status FROM mantis_bug_table WHERE id='" . $id ."'"; - $result = $this->dbConnection->exec_query($query); - if ($result && ($this->dbConnection->num_rows($result) == 1)) - { - $status_ok = 1; - } - return $status_ok; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * - * - **/ - function buildStatusHTMLString($statusVerbose) - { - $str = ''; - if ($statusVerbose !== false) - { - // status values depends on your mantis configuration at config_inc.php in $g_status_enum_string, - // below is the default: - //'10:new,20:feedback,30:acknowledged,40:confirmed,50:assigned,80:resolved,90:closed' - // With this replace if user configure status on mantis with blank we do not have problems - // - $tlStatus = str_replace(" ", "_", $statusVerbose); - $str = lang_get('issue_status_' . $tlStatus); - if($this->guiCfg['use_decoration']) - { - $str = "[" . $str . "] "; - } - } - return $str; - } - - - function getMyInterface() - { - return $this->cfg->interfacePHP; - } - - - - public static function getCfgTemplate() - { - - $template = "\n" . - "\n" . - "DATABASE SERVER NAME\n" . - "DATABASE NAME\n" . - "mysql\n" . - "USER\n" . - "PASSWORD\n" . - "http://localhost:8080/development/mantisbt-1.2.5/view.php?id=\n" . - "http://localhost:8080/development/mantisbt-1.2.5/\n" . - "\n" . - "\n" . - "10new#ffa0a0\n" . - "20feedback#ff50a8\n" . - "30acknowledged#ffd850\n" . - "40confirmed#ffffb0\n" . - "50assigned#c8c8ff\n" . - "80resolved#cceedd\n" . - "90closed#e8e8e8\n" . - "\n" . - "\n" . - "\n" . - "80resolved\n" . - "90closed\n" . - "\n" . - "\n"; - "\n"; - return $template; - } - - public function setStatusCfg() - { - $statusCfg = (array)$this->cfg->statuscfg; - foreach($statusCfg['status'] as $cfx) - { - $e = (array)$cfx; - $this->code_status[$e['code']] = $e['verbose']; - $this->status_color[$e['verbose']] = $e['color']; - } - } - - - public function getCodeStatus() - { - return $this->code_status; - } - - public function getStatusColor() - { - return $this->status_color; - } - -} -?> \ No newline at end of file + 'new', + 20 => 'feedback', + 30 => 'acknowledged', + 40 => 'confirmed', + 50 => 'assigned', + 80 => 'resolved', + 90 => 'closed' + ); + + private $status_color = array( + 'new' => '#ffa0a0', # red, + 'feedback' => '#ff50a8', # purple + 'acknowledged' => '#ffd850', # orange + 'confirmed' => '#ffffb0', # yellow + 'assigned' => '#c8c8ff', # blue + 'resolved' => '#cceedd', # buish-green + 'closed' => '#e8e8e8' + ); + + # light gray + public $defaultResolvedStatus; + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + parent::__construct($type, $config, $name); + if (! $this->isConnected()) { + return false; + } + + $this->interfaceViaDB = true; + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 80, + 'verbose' => 'resolved' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 90, + 'verbose' => 'closed' + ); + + $this->setResolvedStatusCfg(); + + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => true + ); + + $this->guiCfg = array( + 'use_decoration' => true + ); + if (property_exists($this->cfg, 'statuscfg')) { + $this->setStatusCfg(); + } + } + + /** + * Return the URL to the bugtracking page for viewing + * the bug with the given id. + * + * @param + * int id the bug id + * @return string returns a complete URL to view the bug + */ + public function buildViewBugURL($id) + { + return $this->cfg->uriview . urlencode($id); + } + + /** + */ + public function getIssue($id) + { + $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; + if (! $this->isConnected()) { + return false; + } + $sql = "/* $debugMsg */ SELECT id,status,summary FROM mantis_bug_table " . + " WHERE id=" . intval($id); + + $rs = $this->dbConnection->fetchRowsIntoMap($sql, 'id'); + $issue = null; + if (! is_null($rs)) { + $issueOnMantisDB = current($rs); + $issue = new stdClass(); + $issue->IDHTMLString = "{$id} : "; + $issue->summaryHTMLString = $issueOnMantisDB['summary']; + $issue->id = $issueOnMantisDB['id']; + $issue->summary = $issueOnMantisDB['summary']; + $issue->statusCode = $issueOnMantisDB['status']; + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + + if (isset($this->code_status[$issue->statusCode])) { + $issue->statusVerbose = $this->code_status[$issue->statusCode]; + } else { + // give info to user on Event Viewer + $msg = lang_get('MANTIS_status_not_configured'); + $msg = sprintf($msg, $issueOnMantisDB['status']); + logWarningEvent($msg, "MANTIS INTEGRATION"); + $issue->statusVerbose = 'custom_undefined_on_tl'; + } + + $issue->statusHTMLString = $this->buildStatusHTMLString( + $issue->statusVerbose); + $issue->statusColor = isset( + $this->status_color[$issue->statusVerbose]) ? $this->status_color[$issue->statusVerbose] : 'white'; + } + return $issue; + } + + /** + * Returns the status of the bug with the given id + * this function is not directly called by TestLink. + * + * @return string returns the status of the given bug (if found in the db), or false else + */ + public function getBugStatus($id) + { + if (! $this->isConnected()) { + return false; + } + $issue = $this->getIssue($id); + return (! is_null($issue) && $issue) ? $issue->statusVerbose : null; + } + + /** + * checks is bug id is present on BTS + * + * @return integer returns 1 if the bug with the given id exists + */ + public function checkBugIDExistence($id) + { + $status_ok = 0; + $query = "SELECT status FROM mantis_bug_table WHERE id='" . $id . "'"; + $result = $this->dbConnection->exec_query($query); + if ($result && ($this->dbConnection->num_rows($result) == 1)) { + $status_ok = 1; + } + return $status_ok; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + */ + private function buildStatusHTMLString($statusVerbose) + { + $str = ''; + if ($statusVerbose !== false) { + // status values depends on your mantis configuration at config_inc.php in $g_status_enum_string, + // below is the default: + // '10:new,20:feedback,30:acknowledged,40:confirmed,50:assigned,80:resolved,90:closed' + // With this replace if user configure status on mantis with blank we do not have problems + // + $tlStatus = str_replace(" ", "_", $statusVerbose); + $str = lang_get('issue_status_' . $tlStatus); + if ($this->guiCfg['use_decoration']) { + $str = "[" . $str . "] "; + } + } + return $str; + } + + public function getMyInterface() + { + return $this->cfg->interfacePHP; + } + + public static function getCfgTemplate() + { + return "\n" . "\n" . + "DATABASE SERVER NAME\n" . + "DATABASE NAME\n" . "mysql\n" . + "USER\n" . "PASSWORD\n" . + "http://localhost:8080/development/mantisbt-1.2.5/view.php?id=\n" . + "http://localhost:8080/development/mantisbt-1.2.5/\n" . + "\n" . "\n" . + "10new#ffa0a0\n" . + "20feedback#ff50a8\n" . + "30acknowledged#ffd850\n" . + "40confirmed#ffffb0\n" . + "50assigned#c8c8ff\n" . + "80resolved#cceedd\n" . + "90closed#e8e8e8\n" . + "\n" . + "\n" . + "\n" . + "80resolved\n" . + "90closed\n" . + "\n" . "\n"; + } + + public function setStatusCfg() + { + $statusCfg = (array) $this->cfg->statuscfg; + foreach ($statusCfg['status'] as $cfx) { + $e = (array) $cfx; + $this->code_status[$e['code']] = $e['verbose']; + $this->status_color[$e['verbose']] = $e['color']; + } + } + + public function getCodeStatus() + { + return $this->code_status; + } + + public function getStatusColor() + { + return $this->status_color; + } +} +?> diff --git a/lib/issuetrackerintegration/mantisrestInterface.class.php b/lib/issuetrackerintegration/mantisrestInterface.class.php index 84e56517f4..540e2dce3b 100644 --- a/lib/issuetrackerintegration/mantisrestInterface.class.php +++ b/lib/issuetrackerintegration/mantisrestInterface.class.php @@ -1,330 +1,483 @@ - '#ffa0a0', # red, - 'feedback' => '#ff50a8', # purple - 'acknowledged' => '#ffd850', # orange - 'confirmed' => '#ffffb0', # yellow - 'assigned' => '#c8c8ff', # blue - 'resolved' => '#cceedd', # buish-green - 'closed' => '#e8e8e8'); # light gray - - public $defaultResolvedStatus; - - - /** - * Construct and connect to BTS. - * - * @param str $type (see tlIssueTracker.class.php $systems property) - * @param xml $cfg - **/ - function __construct($type,$config,$name) { - $this->name = $name; - $this->interfaceViaDB = false; - $this->methodOpt['buildViewBugLink'] = [ - 'addSummary' => true,'colorByStatus' => false, - 'addReporter' => true, 'addHandler' => true ]; - - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = - array('code' => 80, 'verbose' => 'resolved'); - $this->defaultResolvedStatus[] = - array('code' => 90, 'verbose' => 'closed'); - - $this->canSetReporter = true; - if( !$this->setCfg($config) ) { - return false; - } - - $this->completeCfg(); - $this->setResolvedStatusCfg(); - $this->connect(); - } - - /** - * - **/ - function completeCfg() { - $this->cfg->uribase = trim($this->cfg->uribase,"/"); - if(!property_exists($this->cfg, 'uricreate') ) { - $this->cfg->uricreate = $this->cfg->uribase; - } - - if (!property_exists($this->cfg,'uriview')) { - $this->cfg->uriview = $this->cfg->uribase . '/view.php?id='; - } - - if( property_exists($this->cfg,'options') ) { - $option = get_object_vars($this->cfg->options); - foreach ($option as $name => $elem) { - $name = (string)$name; - $this->options[$name] = (string)$elem; - } - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() { - $processCatch = false; - - try { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $context = [ - 'url' => (string)trim($this->cfg->uribase), - 'apikey' => (string)trim($this->cfg->apikey) ]; - - $tlContext = [ 'proxy' => config_get('proxy') ]; - - $this->APIClient = new mantis($context,$tlContext); - - // to undestand if connection is OK, I will ask for users. - try { - $ValarMorghulis = $this->APIClient->getMyUserInfo(); - $this->connected = !is_null($ValarMorghulis); - } - catch(Exception $e) { - $processCatch = true; - } - } - catch(Exception $e) { - $processCatch = true; - } - - if($processCatch) { - $logDetails = ''; - foreach(['uribase'] as $v) { - $logDetails .= "$v={$this->cfg->$v} / "; - } - $logDetails = trim($logDetails,'/ '); - $this->connected = false; - tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() { - return $this->connected; - } - - /** - * Return the URL to the bugtracking page for viewing - * the bug with the given id. - * - * @param int id the bug id - * - * @return string returns a complete URL to view the bug - **/ - function buildViewBugURL($id) - { - return (string)($this->cfg->uriview . urlencode($id)); - } - - /** - * - * - **/ - public function getIssue($issueID) { - if (!$this->isConnected()) { - tLog(__METHOD__ . '/Not Connected ', 'ERROR'); - return false; - } - - $issue = null; - try { - $jsonObj = $this->APIClient->getIssue($issueID); - - if( !is_null($jsonObj) && is_object($jsonObj)) { - $item = $jsonObj->issues; - $item = $item[0]; - - /* - echo '
    ';
    -      var_dump($item);
    -      echo '
    '; - die(); - */ - $issue = new stdClass(); - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = intval($item->status->id); - $issue->statusVerbose = (string)$item->status->label; - $issue->statusHTMLString = "[{$issue->statusVerbose}]"; - $issue->reportedBy = (string)$item->reporter->real_name; - $issue->handledBy = (string)$item->handler->real_name; - $issue->summary = $issue->summaryHTMLString = (string)$item->summary; - - $issue->isResolved = false; - } - } - catch(Exception $e) { - tLog(__METHOD__ . '/' . $e->getMessage(),'ERROR'); - $issue = null; - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->state : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) { - $state = $this->getIssueStatusCode($issueID); - if ($state) { - return $this->resolvedStatus->byCode[$state]; - } - return false; - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) { - $issue = $this->getIssue($issueID); - return $issue->summaryHTMLString; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) { - if(($status_ok = $this->checkBugIDSyntax($issueID))) { - $issue = $this->getIssue($issueID); - $status_ok = is_object($issue) && !is_null($issue); - } - return $status_ok; - } - - /** - * - */ - public function addIssue($summary,$moreInfo,$opt=null) { - $more = $moreInfo; - try { - $op = $this->APIClient->addIssue($summary, $more['descr'],$opt); - if(is_null($op)){ - throw new Exception("Error creating issue", 1); - } - - if (count($more['links']) > 0) { - $this->APIClient->addExternalLinks($op->id,$more['links']); - } - - $ret = ['status_ok' => true, 'id' => (string)$op->id, - 'msg' => sprintf(lang_get('mantis_bug_created'), - $summary, (string)$op->board_id)]; - } - catch (Exception $e) { - $msg = "Create Mantis Issue Via REST FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = ['status_ok' => false, 'id' => -1, 'msg' => $msg]; - } - return $ret; - } - - /** - * - */ - public function addNote($issueID,$noteText,$opt=null) { - $op = $this->APIClient->addNote($issueID, $noteText); - if(is_null($op)){ - throw new Exception("Error setting note", 1); - } - $ret = ['status_ok' => true, 'id' => (string)$op->iid, - 'msg' => sprintf(lang_get('mantis_bug_comment'), - $op->body, $this->APIClient->projectId)]; - return $ret; - } - - /** - * - **/ - public static function getCfgTemplate() { - $tpl = "\n" . - "\n" . - "\n" . - "API KEY\n" . - "https://www.mantisbt.org/\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "https://www.mantisbt.org/view.php?id=\n" . - "https://www.mantisbt.org/\n" . - "\n"; - return $tpl; - } - - /** - * - **/ - function canCreateViaAPI() - { - return true; - } -} \ No newline at end of file + '#ffa0a0', # red, + 'feedback' => '#ff50a8', # purple + 'acknowledged' => '#ffd850', # orange + 'confirmed' => '#ffffb0', # yellow + 'assigned' => '#c8c8ff', # blue + 'resolved' => '#cceedd', # buish-green + 'closed' => '#e8e8e8' + ); + + # light gray + public $defaultResolvedStatus; + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + $this->name = $name; + $this->interfaceViaDB = false; + $this->methodOpt['buildViewBugLink'] = [ + 'addSummary' => true, + 'addReporter' => true, + 'addHandler' => true, + 'colorByStatus' => false + ]; + + $this->defaultResolvedStatus = [ + [ + 'code' => 80, + 'verbose' => 'resolved' + ], + [ + 'code' => 90, + 'verbose' => 'closed' + ] + ]; + + $this->canSetReporter = true; + if (! $this->setCfg($config)) { + return false; + } + + $this->completeCfg(); + $this->setResolvedStatusCfg(); + $this->connect(); + } + + /** + */ + private function completeCfg() + { + $this->cfg->uribase = trim($this->cfg->uribase, "/"); + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $this->cfg->uribase; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $this->cfg->uribase . '/view.php?id='; + } + + if (property_exists($this->cfg, 'options')) { + $option = get_object_vars($this->cfg->options); + foreach ($option as $name => $elem) { + $name = (string) $name; + $this->options[$name] = (string) $elem; + } + } + + if (! property_exists($this->cfg, 'userinteraction')) { + $this->cfg->userinteraction = 0; + } + + if (! property_exists($this->cfg, 'createissueviaapi')) { + $this->cfg->createissueviaapi = 0; + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + $processCatch = false; + + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $context = [ + 'url' => (string) trim($this->cfg->uribase), + 'apikey' => (string) trim($this->cfg->apikey) + ]; + + $tlContext = [ + 'proxy' => config_get('proxy') + ]; + + $this->APIClient = new mantis($context, $tlContext); + + // to undestand if connection is OK, I will ask for users. + try { + $valarMorghulis = $this->APIClient->getMyUserInfo(); + $this->connected = ! is_null($valarMorghulis); + } catch (Exception $e) { + $processCatch = true; + } + } catch (Exception $e) { + $processCatch = true; + } + + if ($processCatch) { + $logDetails = ''; + foreach ([ + 'uribase' + ] as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + * Return the URL to the bugtracking page for viewing + * the bug with the given id. + * + * @param + * int id the bug id + * @return string returns a complete URL to view the bug + */ + public function buildViewBugURL($id) + { + return (string) ($this->cfg->uriview . urlencode($id)); + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + + $issue = null; + try { + $jsonObj = $this->APIClient->getIssue($issueID); + + if (! is_null($jsonObj) && is_object($jsonObj)) { + + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + + if (property_exists($jsonObj, 'exception')) { + $issue->summary = (string) $jsonObj->reason; + $issue->summaryHTMLString = $issue->summary; + return $issue; + } + + // Normal processing + $item = $jsonObj->issues; + $item = $item[0]; + + $issue->statusCode = intval($item->status->id); + $issue->statusVerbose = (string) $item->status->label; + $issue->statusHTMLString = "[{$issue->statusVerbose}]"; + $issue->summary = $issue->summaryHTMLString = (string) $item->summary; + + // Actors - Begin + $issue->reportedBy = (string) $item->reporter->real_name; + + // Attention: when issue has not handler yet, property does not exist + $issue->handledBy = ''; + if (property_exists($item, 'handler')) { + $issue->handledBy = (string) $item->handler->real_name; + } + // Actors - End + + $cond = [ + 'version' => 'name', + 'fixed_in_version' => 'name', + 'target_version' => 'name' + ]; + $trans = [ + 'version' => 'version', + 'fixed_in_version' => 'fixedInVersion', + 'target_version' => 'targetVersion' + ]; + + foreach ($cond as $prop => $wtg) { + $ip = $trans[$prop]; + $issue->$ip = null; + if (property_exists($item, $prop)) { + $issue->$ip = (string) $item->$prop->$wtg; + } + } + + $issue->isResolved = false; + } + } catch (Exception $e) { + tLog(__METHOD__ . '/' . $e->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->state : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + * + */ + public function getIssueStatusVerbose($issueID) + { + $state = $this->getIssueStatusCode($issueID); + if ($state) { + return $this->resolvedStatus->byCode[$state]; + } + return false; + } + + /** + * + * @param + * string issueID + * @return string + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return $issue->summaryHTMLString; + } + + /** + * + * @param + * string issueID + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + /** + */ + /* + * NOT IMPLEMENTED YET 20211130 + * public function addIssue($summary,$moreInfo,$opt=null) { + * $more = $moreInfo; + * try { + * $op = $this->APIClient->addIssue($summary, $more['descr'],$opt); + * if(is_null($op)){ + * throw new Exception("Error creating issue", 1); + * } + * + * if (count($more['links']) > 0) { + * $this->APIClient->addExternalLinks($op->id,$more['links']); + * } + * + * $ret = ['status_ok' => true, 'id' => (string)$op->id, + * 'msg' => sprintf(lang_get('mantis_bug_created'), + * $summary, (string)$op->board_id)]; + * } + * catch (Exception $e) { + * $msg = "Create Mantis Issue Via REST FAILURE => " . $e->getMessage(); + * tLog($msg, 'WARNING'); + * $ret = ['status_ok' => false, 'id' => -1, 'msg' => $msg]; + * } + * return $ret; + * } + */ + + /** + */ + + /* + * NOT IMPLEMENTED YET 20211130 + * public function addNote($issueID,$noteText,$opt=null) { + * $op = $this->APIClient->addNote($issueID, $noteText); + * if(is_null($op)){ + * throw new Exception("Error setting note", 1); + * } + * $ret = ['status_ok' => true, 'id' => (string)$op->iid, + * 'msg' => sprintf(lang_get('mantis_bug_comment'), + * $op->body, $this->APIClient->projectId)]; + * return $ret; + * } + */ + + /** + * link->testCaseID + * link->testCaseName + * link->relation (verbose) + */ + public function addLink($issueID, $link) + { + try { + $op = $this->APIClient->addLink($issueID, $link); + if (is_null($op)) { + throw new Exception("Error creating link", 1); + } + $ret = [ + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => 'ok' + ]; + $msg = "Create Mantis Link Via REST OK => TICKET:" . $issueID . + ' >> link: ' . json_encode($link); + tLog($msg, 'WARNING'); + } catch (Exception $e) { + $msg = "Create Mantis Link Via REST FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + + $msg = "Create Mantis Link Via REST FAILURE => TICKET -> " . $issueID . + ' >> link: ' . json_encode($link); + tLog($msg, 'WARNING'); + + $ret = [ + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg + ]; + } + return $ret; + } + + /** + * link->testCaseID + */ + public function removeLink($issueID, $link) + { + try { + $op = $this->APIClient->removeLink($issueID, $link); + if (is_null($op)) { + throw new Exception("Error removing link", 1); + } + $ret = [ + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => 'ok' + ]; + } catch (Exception $e) { + $msg = "Remove Mantis Link Via REST FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = [ + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg + ]; + } + return $ret; + } + + /** + * link->testCaseID + * link->testCaseName + * link->relation (verbose) + * link->testPlanName": "TPLAN_A", + * link->buildName": "BUILD 1", + * link->platformName": "", + * link->tester": "Mauro", + * link->execStatus": "Passed", + * link->timeStamp": "20200101-23:00" + */ + public function addExecLink($issueID, $link) + { + try { + $this->APIClient->addExecLink($issueID, $link); + $ret = [ + 'status_ok' => true, + 'msg' => 'ok' + ]; + } catch (Exception $e) { + $msg = "Create Mantis Exec Link Via REST FAILURE => " . + $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = [ + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg + ]; + } + return $ret; + } + + /** + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "\n" . "API KEY\n" . + "https://www.mantisbt.org/\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "https://www.mantisbt.org/view.php?id=\n" . + "https://www.mantisbt.org/\n" . + "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return true; + } +} diff --git a/lib/issuetrackerintegration/mantissoapInterface.class.php b/lib/issuetrackerintegration/mantissoapInterface.class.php index 783e5a8285..786aa1c1d3 100644 --- a/lib/issuetrackerintegration/mantissoapInterface.class.php +++ b/lib/issuetrackerintegration/mantissoapInterface.class.php @@ -1,568 +1,554 @@ - '#ffa0a0', # red, - 'feedback' => '#ff50a8', # purple - 'acknowledged' => '#ffd850', # orange - 'confirmed' => '#ffffb0', # yellow - 'assigned' => '#c8c8ff', # blue - 'resolved' => '#cceedd', # buish-green - 'closed' => '#e8e8e8'); # light gray - - - private $soapOpt = array("connection_timeout" => 1, 'exceptions' => 1); - - var $defaultResolvedStatus; - - // field is nvarchar(128) at least on 1.2.14 - var $summaryLengthLimit = 120; - - /** - * Construct and connect to BTS. - * - * @param str $type (see tlIssueTracker.class.php $systems property) - * @param xml $cfg - **/ - function __construct($type,$config,$name) - { - $this->name = $name; - $this->interfaceViaDB = false; - - $this->methodOpt['buildViewBugLink'] = - array('addSummary' => true, 'colorByStatus' => true, - 'addReporter' => true, 'addHandler' => true); - - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = array('code' => 80, 'verbose' => 'resolved'); - $this->defaultResolvedStatus[] = array('code' => 90, 'verbose' => 'closed'); - - if( $this->setCfg($config) ) - { - $this->completeCfg(); - $this->setResolvedStatusCfg(); - $this->connect(); - $this->guiCfg = array('use_decoration' => true); - } - } - - - /** - * Return the URL to the bugtracking page for viewing - * the bug with the given id. - * - * @param int id the bug id - * - * @return string returns a complete URL to view the bug - **/ - function buildViewBugURL($id) - { - return (string)($this->cfg->uriview . urlencode($id)); - } - - - /** - * establishes the soap connection to the bugtracking system - * - * @return bool returns true if the soap connection was established and the - * wsdl could be downloaded, false else - * - **/ - function connect() - { - $op = $this->getClient(array('log' => true)); - if( ($this->connected = $op['connected']) ) - { - // OK, we have got WSDL => server is up and we can do SOAP calls, but now we need - // to do a simple call with user/password only to understand if we are really connected - try { - $x = $op['client']->mc_enum_status($this->cfg->username,$this->cfg->password); - } catch (SoapFault $f) { - $this->connected = false; - tLog("SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})","ERROR"); - } - } - return $this->connected; - } - - - /** - * - * - **/ - function getClient($opt=null) - { - // IMPORTANT NOTICE - 2012-01-06 - If you are using XDEBUG, Soap Fault will not work - $res = array('client' => null, 'connected' => false, 'msg' => 'generic ko'); - $my['opt'] = array('log' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - try - { - // IMPORTANT NOTICE - // $this->cfg is a simpleXML object, then is ABSOLUTELY CRITICAL - // DO CAST any member before using it. - // If we do following call WITHOUT (string) CAST, SoapClient() fails - // complaining '... wsdl has to be an STRING or null ...' - $res['client'] = new SoapClient((string)$this->cfg->uriwsdl,$this->soapOpt); - - // debug trying to use proxy (20140409) - // $res['client'] = new SoapClient('/tmp/mantisconnect.php.wsdl',$this->soapOpt); - - $res['connected'] = true; - $res['msg'] = 'iupi!!!'; - } - catch (SoapFault $f) - { - $res['connected'] = false; - $res['msg'] = "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; - if($my['opt']['log']) - { - tLog("SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})","ERROR"); - } - } - return $res; - } - - /** - * checks is bug id is present on BTS - * - * @return integer returns 1 if the bug with the given id exists - **/ - function checkBugIDExistence($id) - { - static $client; - - if (!$this->isConnected()) - { - return 0; // >>>---> bye! - } - - if(is_null($client)) - { - $dummy = $this->getClient(); - $client = $dummy['client']; - } - - $status_ok = 0; - - $safe = new stdClass(); - $safe->username = (string)$this->cfg->username; - $safe->password = (string)$this->cfg->password; - $safe->id = intval($id); - - try - { - $status_ok = $client->mc_issue_exists($safe->username,$safe->password,$safe->id) ? 1 : 0; - } - catch (SoapFault $f) - { - // from http://www.w3schools.com/soap/soap_fault.asp - // VersionMismatch - Found an invalid namespace for the SOAP Envelope element - // MustUnderstand - An immediate child element of the Header element, - // with the mustUnderstand attribute set to "1", was not understood - // Client - The message was incorrectly formed or contained incorrect information - // Server - There was a problem with the server so the message ... - - // @Å¢ODO - 20120106 - need to think how to manage this situation in a better way - } - return $status_ok; - } - - - /** - * - * - * - * - **/ - function getIssue($id) - { - static $client; - - if (!$this->isConnected()) - { - return false; - } - - if(is_null($client)) - { - $dummy = $this->getClient(); - $client = $dummy['client']; - } - - $status = false; - $issue = null; - try - { - $safe = new stdClass(); - $safe->username = (string)$this->cfg->username; - $safe->password = (string)$this->cfg->password; - $safe->id = intval($id); - - - if($client->mc_issue_exists($safe->username,$safe->password,$safe->id)) - { - $issue = $client->mc_issue_get($safe->username,$safe->password,$safe->id); - if( !is_null($issue) && is_object($issue) ) { - $issue->IDHTMLString = "{$id} : "; - $issue->statusCode = $issue->status->id; - $issue->statusVerbose = $issue->status->name; - $issue->statusHTMLString = $this->buildStatusHTMLString($issue->statusVerbose); - $issue->statusColor = isset($this->status_color[$issue->statusVerbose]) ? - $this->status_color[$issue->statusVerbose] : 'white'; - - $issue->summaryHTMLString = $issue->summary; - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - - $issue->reportedBy = (string)$issue->reporter->name; - $issue->handledBy = (string)$issue->handler->name; - } - } - } - catch (SoapFault $f) - { - // from http://www.w3schools.com/soap/soap_fault.asp - // VersionMismatch - Found an invalid namespace for the SOAP Envelope element - // MustUnderstand - An immediate child element of the Header element, - // with the mustUnderstand attribute set to "1", was not understood - // Client - The message was incorrectly formed or contained incorrect information - // Server - There was a problem with the server so the message ... - - // @Å¢ODO - 20120106 - need to think how to manage this situation in a better way - } - return $issue; - } - - - /** - * - * - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - * - * - **/ - public static function getCfgTemplate() - { - $template = "\n" . - "\n" . - "MANTIS LOGIN NAME\n" . - "MANTIS PASSWORD\n" . - "http://www.mantisbt.org/\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "http://www.mantisbt.org/api/soap/mantisconnect.php?wsdl\n" . - "http://www.mantisbt.org/view.php?id=\n" . - "http://www.mantisbt.org/\n" . - "\n" . - "MANTIS PROJECT NAME\n" . - "MANTIS CATEGORY NAME\n" . - "\n" . - "\n" . - "80resolved\n" . - "90closed\n" . - "\n" . - "\n"; - return $template; - } - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/' ; - if( !property_exists($this->cfg,'uriwsdl') ) - { - $this->cfg->uriwsdl = $base . 'api/soap/mantisconnect.php?wsdl'; - } - - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'view.php?id='; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base; - } - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - - - /** - * - * - **/ - function buildStatusHTMLString($statusVerbose) - { - $str = ''; - if ($statusVerbose !== false) - { - // status values depends on your mantis configuration at config_inc.php in $g_status_enum_string, - // below is the default: - //'10:new,20:feedback,30:acknowledged,40:confirmed,50:assigned,80:resolved,90:closed' - // With this replace if user configure status on mantis with blank we do not have problems - // - $tlStatus = str_replace(" ", "_", $statusVerbose); - $str = lang_get('issue_status_' . $tlStatus); - if($this->guiCfg['use_decoration']) - { - $str = "[" . $str . "] "; - } - } - return $str; - } - - public function setStatusCfg() - { - $statusCfg = (array)$this->cfg->statuscfg; - foreach($statusCfg['status'] as $cfx) - { - $e = (array)$cfx; - $this->status_color[$e['verbose']] = $e['color']; - } - } - - - public function getCodeStatus() - { - return $this->code_status; - } - - public function getStatusColor() - { - return $this->status_color; - } - - public static function checkEnv() - { - $ret = array(); - $ret['status'] = extension_loaded('soap'); - $ret['msg'] = $ret['status'] ? 'OK' : 'You need to enable SOAP extension'; - return $ret; - } - - - /** - * - * from mantisconnect.php - * - * ### AccountData - * $l_oServer->wsdl->addComplexType('AccountData','complexType','struct','all','', - * array( 'id' => array( 'name' => 'id', 'type' => 'xsd:integer', 'minOccurs' => '0'), - * 'name' => array( 'name' => 'name', 'type' => 'xsd:string', 'minOccurs' => '0'), - * 'real_name' => array( 'name' => 'real_name', 'type' => 'xsd:string', 'minOccurs' => '0'), - * 'email' => array( 'name' => 'email', 'type' => 'xsd:string', 'minOccurs' => '0') - * - * - * From IssueData I want to have the example for project to understand what data structure I need to use - * - * ### IssueData - * $l_oServer->wsdl->addComplexType('IssueData','complexType','struct','all','', - * array('id' => array( 'name' => 'id', 'type' => 'xsd:integer', 'minOccurs' => '0' ), - * 'view_state' => array( 'name' => 'view_state', 'type' => 'tns:ObjectRef', 'minOccurs' => '0' ), - * 'last_updated' => array( 'name' => 'last_updated', 'type' => 'xsd:dateTime', 'minOccurs' => '0' ), - * 'project' => array( 'name' => 'project', 'type' => 'tns:ObjectRef', 'minOccurs' => '0' ), - * - * - * ### ObjectRef - * $l_oServer->wsdl->addComplexType('ObjectRef','complexType','struct','all','', - * array('id' => array( 'name' => 'id', 'type' => 'xsd:integer', 'minOccurs' => '0'), - * 'name' => array( 'name' => 'name', 'type' => 'xsd:string', 'minOccurs' => '0') - * - */ - public function addIssue($summary,$description,$opt=null) - { - static $client; - $ret = array('status_ok' => false, 'id' => -1,'msg' => ''); - if (!$this->isConnected()) - { - return $ret; - } - - if(is_null($client)) - { - $dummy = $this->getClient(); - $client = $dummy['client']; - } - $safe = new stdClass(); - $safe->username = (string)$this->cfg->username; - $safe->password = (string)$this->cfg->password; - $safe->project = (string)$this->cfg->project; - - $mpid = $client->mc_project_get_id_from_name($safe->username,$safe->password,$safe->project); - if( $mpid > 0) - { - - $safeSummary = (strlen($summary) > $this->summaryLengthLimit) ? '...' . substr($summary, -($this->summaryLengthLimit)) : $summary; - $issue = array('summary' => $safeSummary,'description' => $description,'project' => array('id' => $mpid)); - - // check category - $nameCode = $client->mc_project_get_categories($safe->username,$safe->password,$mpid); - $codeName = array_flip($nameCode); - - $categoryName = (property_exists($this->cfg,'category')) ? (string)$this->cfg->category : null; - $issue['category'] = (is_null($categoryName) || !isset($codeName[$categoryName])) ? current($nameCode) : $categoryName; - - // user tester as Reporter - if(!is_null($opt)) - { - if(property_exists($opt, 'reporter')) - { - $issue['reporter'] = array('name' => $opt->reporter); - } - } - - - // because issue id on TestLink is considered a string, - // in order to make work ORDER BY, I will format it adding 0 to left as done on Mantis GUI - // example: 6845 => 0006845 - $ret['id'] = $client->mc_issue_add($safe->username,$safe->password,$issue); - $ret['id'] = sprintf('%07u',$ret['id']); - - $ret['status_ok'] = true; - $ret['msg'] = sprintf(lang_get('mantis_bug_created'), $ret['id'],$safeSummary,$safe->project); - } - else - { - $ret['msg'] = sprintf(lang_get('bts_project_does_not_exist'),(string)$this->cfg->project); - } - return $ret; - } - - - /** - * - * Mantis API Call mc_issue_note_addR - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - public function addNote($issueID,$noteText,$opt=null) { - - static $client; - $ret = array('status_ok' => false,'msg' => '', 'note_id' => -1); - if (!$this->isConnected()) { - $ret = array('status_ok' => false,'msg' => 'Connection KO', 'note_id' => -1); - return $ret; - } - - if(is_null($client)) { - $dummy = $this->getClient(); - $client = $dummy['client']; - } - - $safe = new stdClass(); - $safe->username = (string)$this->cfg->username; - $safe->password = (string)$this->cfg->password; - $safe->issueID = intval($issueID); - - - if($client->mc_issue_exists($safe->username,$safe->password,$safe->issueID)) { - - $issueNoteData = array('text' => $noteText); - if(!is_null($opt)) { - if(property_exists($opt, 'reporter')) { - $issueNoteData['reporter'] = array('name' => $opt->reporter); - } - } - - try { - $ret['note_id'] = - $client->mc_issue_note_add($safe->username,$safe->password, - $safe->issueID,$issueNoteData); - } catch (SoapFault $f) { - // "User id missing"; - // Have found no way to check code, then will check message - // MantisBT send messages only on English - $faultMsg = $f->getMessage(); - - switch( $faultMsg ) { - case "User id missing": - $ret['msg'] = - "Cannot create note, using TestLink logged user: " . - $issueNoteData['reporter']['name']; - break; - - default: - $ret['msg'] = - "Cannot create note, MantisBT message: $faultMsg"; - break; - } - } - } - else { - $ret['msg'] = "issue $safe->issueID does not exist"; - } - - return $ret; - } - - - - /** - * - **/ - function canCreateViaAPI() - { - return (property_exists($this->cfg, 'project') && property_exists($this->cfg, 'category')); - } - - -} \ No newline at end of file + '#ffa0a0', # red, + 'feedback' => '#ff50a8', # purple + 'acknowledged' => '#ffd850', # orange + 'confirmed' => '#ffffb0', # yellow + 'assigned' => '#c8c8ff', # blue + 'resolved' => '#cceedd', # buish-green + 'closed' => '#e8e8e8' + ); + + # light gray + private $soapOpt = array( + "connection_timeout" => 1, + 'exceptions' => 1 + ); + + public $defaultResolvedStatus; + + // field is nvarchar(128) at least on 1.2.14 + private $summaryLengthLimit = 120; + + /** + * Construct and connect to BTS. + * + * @param string $type + * (see tlIssueTracker.class.php $systems property) + * @param xml $cfg + */ + public function __construct($type, $config, $name) + { + $this->name = $name; + $this->interfaceViaDB = false; + + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => true, + 'addReporter' => true, + 'addHandler' => true + ); + + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 80, + 'verbose' => 'resolved' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 90, + 'verbose' => 'closed' + ); + + if ($this->setCfg($config)) { + $this->completeCfg(); + $this->setResolvedStatusCfg(); + $this->connect(); + $this->guiCfg = array( + 'use_decoration' => true + ); + } + } + + /** + * Return the URL to the bugtracking page for viewing + * the bug with the given id. + * + * @param + * int id the bug id + * @return string returns a complete URL to view the bug + */ + public function buildViewBugURL($id) + { + return (string) ($this->cfg->uriview . urlencode($id)); + } + + /** + * establishes the soap connection to the bugtracking system + * + * @return bool returns true if the soap connection was established and the + * wsdl could be downloaded, false else + * + */ + public function connect() + { + $op = $this->getClient(array( + 'log' => true + )); + if ($this->connected = $op['connected']) { + // OK, we have got WSDL => server is up and we can do SOAP calls, but now we need + // to do a simple call with user/password only to understand if we are really connected + try { + $op['client']->mc_enum_status($this->cfg->username, + $this->cfg->password); + } catch (SoapFault $f) { + $this->connected = false; + tLog( + "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})", + "ERROR"); + } + } + return $this->connected; + } + + /** + */ + public function getClient($opt = null) + { + // IMPORTANT NOTICE - 2012-01-06 - If you are using XDEBUG, Soap Fault will not work + $res = array( + 'client' => null, + 'connected' => false, + 'msg' => 'generic ko' + ); + $my['opt'] = array( + 'log' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + try { + // IMPORTANT NOTICE + // $this->cfg is a simpleXML object, then is ABSOLUTELY CRITICAL + // DO CAST any member before using it. + // If we do following call WITHOUT (string) CAST, SoapClient() fails + // complaining '... wsdl has to be an STRING or null ...' + $res['client'] = new SoapClient((string) $this->cfg->uriwsdl, + $this->soapOpt); + + // debug trying to use proxy (20140409) + // $res['client'] = new SoapClient('/tmp/mantisconnect.php.wsdl',$this->soapOpt); + + $res['connected'] = true; + $res['msg'] = 'iupi!!!'; + } catch (SoapFault $f) { + $res['connected'] = false; + $res['msg'] = "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})"; + if ($my['opt']['log']) { + tLog( + "SOAP Fault: (code: {$f->faultcode}, string: {$f->faultstring})", + "ERROR"); + } + } + return $res; + } + + /** + * checks is bug id is present on BTS + * + * @return integer returns 1 if the bug with the given id exists + */ + public function checkBugIDExistence($id) + { + static $client; + + if (! $this->isConnected()) { + return 0; // >>>---> bye! + } + + if (is_null($client)) { + $dummy = $this->getClient(); + $client = $dummy['client']; + } + + $status_ok = 0; + + $safe = new stdClass(); + $safe->username = (string) $this->cfg->username; + $safe->password = (string) $this->cfg->password; + $safe->id = intval($id); + + try { + $status_ok = $client->mc_issue_exists($safe->username, + $safe->password, $safe->id) ? 1 : 0; + } catch (SoapFault $f) { + // from http://www.w3schools.com/soap/soap_fault.asp + // VersionMismatch - Found an invalid namespace for the SOAP Envelope element + // MustUnderstand - An immediate child element of the Header element, + // with the mustUnderstand attribute set to "1", was not understood + // Client - The message was incorrectly formed or contained incorrect information + // Server - There was a problem with the server so the message ... + + // @Å¢ODO - 20120106 - need to think how to manage this situation in a better way + } + return $status_ok; + } + + /** + */ + public function getIssue($id) + { + static $client; + + if (! $this->isConnected()) { + return false; + } + + if (is_null($client)) { + $dummy = $this->getClient(); + $client = $dummy['client']; + } + + $issue = null; + try { + $safe = new stdClass(); + $safe->username = (string) $this->cfg->username; + $safe->password = (string) $this->cfg->password; + $safe->id = intval($id); + + if ($client->mc_issue_exists($safe->username, $safe->password, + $safe->id)) { + $issue = $client->mc_issue_get($safe->username, $safe->password, + $safe->id); + if (! is_null($issue) && is_object($issue)) { + $issue->IDHTMLString = "{$id} : "; + $issue->statusCode = $issue->status->id; + $issue->statusVerbose = $issue->status->name; + $issue->statusHTMLString = $this->buildStatusHTMLString( + $issue->statusVerbose); + $issue->statusColor = isset( + $this->status_color[$issue->statusVerbose]) ? $this->status_color[$issue->statusVerbose] : 'white'; + + $issue->summaryHTMLString = $issue->summary; + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + + $issue->reportedBy = (string) $issue->reporter->name; + $issue->handledBy = (string) $issue->handler->name; + } + } + } catch (SoapFault $f) { + // from http://www.w3schools.com/soap/soap_fault.asp + // VersionMismatch - Found an invalid namespace for the SOAP Envelope element + // MustUnderstand - An immediate child element of the Header element, + // with the mustUnderstand attribute set to "1", was not understood + // Client - The message was incorrectly formed or contained incorrect information + // Server - There was a problem with the server so the message ... + + // @Å¢ODO - 20120106 - need to think how to manage this situation in a better way + } + return $issue; + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "MANTIS LOGIN NAME\n" . + "MANTIS PASSWORD\n" . + "http://www.mantisbt.org/\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "http://www.mantisbt.org/api/soap/mantisconnect.php?wsdl\n" . + "http://www.mantisbt.org/view.php?id=\n" . + "http://www.mantisbt.org/\n" . + "\n" . + "MANTIS PROJECT NAME\n" . + "MANTIS CATEGORY NAME\n" . + "\n" . + "\n" . + "80resolved\n" . + "90closed\n" . + "\n" . "\n"; + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; + if (! property_exists($this->cfg, 'uriwsdl')) { + $this->cfg->uriwsdl = $base . 'api/soap/mantisconnect.php?wsdl'; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'view.php?id='; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base; + } + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + */ + private function buildStatusHTMLString($statusVerbose) + { + $str = ''; + if ($statusVerbose !== false) { + // status values depends on your mantis configuration at config_inc.php in $g_status_enum_string, + // below is the default: + // '10:new,20:feedback,30:acknowledged,40:confirmed,50:assigned,80:resolved,90:closed' + // With this replace if user configure status on mantis with blank we do not have problems + // + $tlStatus = str_replace(" ", "_", $statusVerbose); + $str = lang_get('issue_status_' . $tlStatus); + if ($this->guiCfg['use_decoration']) { + $str = "[" . $str . "] "; + } + } + return $str; + } + + public function setStatusCfg() + { + $statusCfg = (array) $this->cfg->statuscfg; + foreach ($statusCfg['status'] as $cfx) { + $e = (array) $cfx; + $this->status_color[$e['verbose']] = $e['color']; + } + } + + public function getCodeStatus() + { + return $this->code_status; + } + + public function getStatusColor() + { + return $this->status_color; + } + + public static function checkEnv() + { + $ret = array(); + $ret['status'] = extension_loaded('soap'); + $ret['msg'] = $ret['status'] ? 'OK' : 'You need to enable SOAP extension'; + return $ret; + } + + /** + * from mantisconnect.php + * + * ### AccountData + * $l_oServer->wsdl->addComplexType('AccountData','complexType','struct','all','', + * array( 'id' => array( 'name' => 'id', 'type' => 'xsd:integer', 'minOccurs' => '0'), + * 'name' => array( 'name' => 'name', 'type' => 'xsd:string', 'minOccurs' => '0'), + * 'real_name' => array( 'name' => 'real_name', 'type' => 'xsd:string', 'minOccurs' => '0'), + * 'email' => array( 'name' => 'email', 'type' => 'xsd:string', 'minOccurs' => '0') + * + * + * From IssueData I want to have the example for project to understand what data structure I need to use + * + * ### IssueData + * $l_oServer->wsdl->addComplexType('IssueData','complexType','struct','all','', + * array('id' => array( 'name' => 'id', 'type' => 'xsd:integer', 'minOccurs' => '0' ), + * 'view_state' => array( 'name' => 'view_state', 'type' => 'tns:ObjectRef', 'minOccurs' => '0' ), + * 'last_updated' => array( 'name' => 'last_updated', 'type' => 'xsd:dateTime', 'minOccurs' => '0' ), + * 'project' => array( 'name' => 'project', 'type' => 'tns:ObjectRef', 'minOccurs' => '0' ), + * + * + * ### ObjectRef + * $l_oServer->wsdl->addComplexType('ObjectRef','complexType','struct','all','', + * array('id' => array( 'name' => 'id', 'type' => 'xsd:integer', 'minOccurs' => '0'), + * 'name' => array( 'name' => 'name', 'type' => 'xsd:string', 'minOccurs' => '0') + */ + public function addIssue($summary, $description, $opt = null) + { + static $client; + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => '' + ); + if (! $this->isConnected()) { + return $ret; + } + + if (is_null($client)) { + $dummy = $this->getClient(); + $client = $dummy['client']; + } + $safe = new stdClass(); + $safe->username = (string) $this->cfg->username; + $safe->password = (string) $this->cfg->password; + $safe->project = (string) $this->cfg->project; + + $mpid = $client->mc_project_get_id_from_name($safe->username, + $safe->password, $safe->project); + if ($mpid > 0) { + + $safeSummary = (strlen($summary) > $this->summaryLengthLimit) ? '...' . + substr($summary, - ($this->summaryLengthLimit)) : $summary; + $issue = array( + 'summary' => $safeSummary, + 'description' => $description, + 'project' => array( + 'id' => $mpid + ) + ); + + // check category + $nameCode = $client->mc_project_get_categories($safe->username, + $safe->password, $mpid); + $codeName = array_flip($nameCode); + + $categoryName = (property_exists($this->cfg, 'category')) ? (string) $this->cfg->category : null; + $issue['category'] = (is_null($categoryName) || + ! isset($codeName[$categoryName])) ? current($nameCode) : $categoryName; + + // user tester as Reporter + if (! is_null($opt) && property_exists($opt, 'reporter')) { + $issue['reporter'] = array( + 'name' => $opt->reporter + ); + } + + // because issue id on TestLink is considered a string, + // in order to make work ORDER BY, I will format it adding 0 to left as done on Mantis GUI + // example: 6845 => 0006845 + $ret['id'] = $client->mc_issue_add($safe->username, $safe->password, + $issue); + $ret['id'] = sprintf('%07u', $ret['id']); + + $ret['status_ok'] = true; + $ret['msg'] = sprintf(lang_get('mantis_bug_created'), $ret['id'], + $safeSummary, $safe->project); + } else { + $ret['msg'] = sprintf(lang_get('bts_project_does_not_exist'), + (string) $this->cfg->project); + } + return $ret; + } + + /** + * Mantis API Call mc_issue_note_addR + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + public function addNote($issueID, $noteText, $opt = null) + { + static $client; + $ret = array( + 'status_ok' => false, + 'msg' => '', + 'note_id' => - 1 + ); + if (! $this->isConnected()) { + return array( + 'status_ok' => false, + 'msg' => 'Connection KO', + 'note_id' => - 1 + ); + } + + if (is_null($client)) { + $dummy = $this->getClient(); + $client = $dummy['client']; + } + + $safe = new stdClass(); + $safe->username = (string) $this->cfg->username; + $safe->password = (string) $this->cfg->password; + $safe->issueID = intval($issueID); + + if ($client->mc_issue_exists($safe->username, $safe->password, + $safe->issueID)) { + + $issueNoteData = array( + 'text' => $noteText + ); + if (! is_null($opt) && property_exists($opt, 'reporter')) { + $issueNoteData['reporter'] = array( + 'name' => $opt->reporter + ); + } + + try { + $ret['note_id'] = $client->mc_issue_note_add($safe->username, + $safe->password, $safe->issueID, $issueNoteData); + } catch (SoapFault $f) { + // "User id missing"; + // Have found no way to check code, then will check message + // MantisBT send messages only on English + $faultMsg = $f->getMessage(); + + switch ($faultMsg) { + case "User id missing": + $ret['msg'] = "Cannot create note, using TestLink logged user: " . + $issueNoteData['reporter']['name']; + break; + + default: + $ret['msg'] = "Cannot create note, MantisBT message: $faultMsg"; + break; + } + } + } else { + $ret['msg'] = "issue $safe->issueID does not exist"; + } + + return $ret; + } + + /** + */ + public function canCreateViaAPI() + { + return property_exists($this->cfg, 'project') && + property_exists($this->cfg, 'category'); + } +} diff --git a/lib/issuetrackerintegration/redminerestInterface.class.php b/lib/issuetrackerintegration/redminerestInterface.class.php index 7e4a7751d5..67d8f985b1 100644 --- a/lib/issuetrackerintegration/redminerestInterface.class.php +++ b/lib/issuetrackerintegration/redminerestInterface.class.php @@ -1,514 +1,501 @@ -name = $name; - $this->interfaceViaDB = false; - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => false); - - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = array('code' => 3, 'verbose' => 'resolved'); - $this->defaultResolvedStatus[] = array('code' => 5, 'verbose' => 'closed'); - - $this->canSetReporter = true; - if( !$this->setCfg($config) ) { - return false; - } - - // http://www.redmine.org/issues/6843 - // "Target version" is the new display name for this property, - // but it's still named fixed_version internally and thus in the API. - // $issueXmlObj->addChild('fixed_version_id', (string)2); - $this->translate['targetversion'] = 'fixed_version_id'; - - $this->completeCfg(); - $this->setResolvedStatusCfg(); - $this->connect(); - } - - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/'; // be sure no double // at end - if( !property_exists($this->cfg,'uriview') ) - { - // seems this is good only for redmine 1 and 2 ?? - // $this->cfg->uriview = $base . 'issues/show/'; - $this->cfg->uriview = $base . 'issues/'; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base; - } - - if( property_exists($this->cfg,'attributes') ) - { - $attr = get_object_vars($this->cfg->attributes); - foreach ($attr as $name => $elem) - { - $name = (string)$name; - if( is_object($elem) ) - { - $elem = get_object_vars($elem); - $cc = current($elem); - $kk = key($elem); - foreach($cc as $value) - { - $this->issueOtherAttr[$name][] = array($kk => (string)$value); - } - } - else - { - $this->issueOtherAttr[$name] = (string)$elem; - } - } - } - - // All attributes that I do not consider mandatory - // are managed through the issueAdditionalAttributes - // - // On Redmine 1 seems to be standard for Issues/Bugs - $this->issueDefaults = array('trackerid' => 1); - foreach($this->issueDefaults as $prop => $default) - { - if(!isset($this->issueAttr[$prop])) - { - $this->issueAttr[$prop] = $default; - } - } - - if( property_exists($this->cfg,'custom_fields') ) - { - libxml_use_internal_errors(true); - $xcfg = simplexml_load_string($this->xmlCfg); - $this->cfg->custom_fields = (string)$xcfg->custom_fields->asXML(); - } - } - - - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - $processCatch = false; - - try - { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $redUrl = (string)trim($this->cfg->uribase); - $redAK = (string)trim($this->cfg->apikey); - $pxy = new stdClass(); - $pxy->proxy = config_get('proxy'); - $this->APIClient = new redmine($redUrl,$redAK,$pxy); - - // to undestand if connection is OK, I will ask for projects. - // I've tried to ask for users but get always ERROR from redmine (not able to understand why). - try - { - $items = $this->APIClient->getProjects(); - $this->connected = !is_null($items); - unset($items); - } - catch(Exception $e) - { - $processCatch = true; - } - } - catch(Exception $e) - { - $processCatch = true; - } - - if($processCatch) - { - $logDetails = ''; - foreach(array('uribase','apikey') as $v) - { - $logDetails .= "$v={$this->cfg->$v} / "; - } - $logDetails = trim($logDetails,'/ '); - $this->connected = false; - tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - public function getIssue($issueID) - { - if (!$this->isConnected()) - { - tLog(__METHOD__ . '/Not Connected ', 'ERROR'); - return false; - } - - $issue = null; - try - { - $xmlObj = $this->APIClient->getIssue((int)$issueID); - - if( !is_null($xmlObj) && is_object($xmlObj)) - { - $issue = new stdClass(); - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = (string)$xmlObj->status['id']; - $issue->statusVerbose = (string)$xmlObj->status['name'];; - $issue->statusHTMLString = "[$issue->statusVerbose] "; - $issue->summary = $issue->summaryHTMLString = (string)$xmlObj->subject; - $issue->redmineProject = array('name' => (string)$xmlObj->project['name'], - 'id' => (int)$xmlObj->project['id'] ); - - $issue->isResolved = isset($this->resolvedStatus->byCode[$issue->statusCode]); - } - } - catch(Exception $e) - { - tLog(__METHOD__ . '/' . $e->getMessage(),'ERROR'); - $issue = null; - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->statusCode : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - return $this->getIssueStatusCode($issueID); - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) - { - $issue = $this->getIssue($issueID); - return $issue->summaryHTMLString; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = is_object($issue) && !is_null($issue); - } - return $status_ok; - } - - /** - * - * From Redmine API documentation (@20130406) - * Parameters: - * - * issue - A hash of the issue attributes: - * - subject - * - description - * - project_id - * - tracker_id - * - status_id - * - category_id - * - fixed_version_id - see http://www.redmine.org/issues/6843 - * - assigned_to_id - ID of the user to assign the issue to (currently no mechanism to assign by name) - * - parent_issue_id - ID of the parent issue <= aslo know as Parent Task - * - custom_fields - See Custom fields - * - watcher_user_ids - Array of user ids to add as watchers (since 2.3.0) - */ - public function addIssue($summary,$description,$opt=null) { - - $reporter = null; - if(!is_null($opt) && property_exists($opt, 'reporter')) { - $reporter = $opt->reporter; - } - - - // Check mandatory info - if( !property_exists($this->cfg,'projectidentifier') ) { - throw new exception(__METHOD__ . " project identifier is MANDATORY"); - } - - - try { - // needs json or xml - $issueXmlObj = new SimpleXMLElement(''); - - // according with user report is better to use htmlspecialchars - // TICKET 5703: Create issue with Portuguese characters produces mangled text - // - // $issueXmlObj->addChild('subject', htmlentities($summary)); - // $issueXmlObj->addChild('description', htmlentities($description)); - - // limit size to redmine max => 255 ? - $issueXmlObj->addChild('subject', substr(htmlspecialchars($summary),0,255) ); - $issueXmlObj->addChild('description', htmlspecialchars($description)); - - // Got from XML Configuration - // improvement - $pid = (string)$this->cfg->projectidentifier; - $issueXmlObj->addChild('project_id',$pid); - - if( property_exists($this->cfg,'trackerid') ) { - $issueXmlObj->addChild('tracker_id', (string)$this->cfg->trackerid); - } - - // try to be generic - if( property_exists($this->cfg,'parent_issue_id') ) { - $issueXmlObj->addChild('parent_issue_id', (string)$this->cfg->parent_issue_id); - } - - // Why issuesAttr is issue ? - // Idea was - // on XML config on TestLink provide direct access to a minimun set of MANDATORY - // attributes => without it issue can not be created. - // After first development/release of this feature people that knows better - // Redmine start asking for other attributes. - // Then to manage this other set of unknown attributes in a generic way idea was - // loop over an object property and blidly add it to request. - // - // Drawback/limitations - // I can not manage type (because I do not request this info) => will treat always as STRING - // - // * Special case Target Version - // http://www.redmine.org/issues/6843 - // "Target version" is the new display name for this property, - // but it's still named fixed_version internally and thus in the API. - // $issueXmlObj->addChild('fixed_version_id', (string)2); - // - if(!is_null($this->issueOtherAttr)) { - foreach($this->issueOtherAttr as $ka => $kv) { - // will treat everything as simple strings or can I check type - // see completeCfg() - $issueXmlObj->addChild((isset($this->translate[$ka]) ? $this->translate[$ka] : $ka), (string)$kv); - } - } - - // In order to manage custom fields in simple way, - // it seems that is better create here plain XML String - // - $xml = $issueXmlObj->asXML(); - if( property_exists($this->cfg,'custom_fields') ) { - $cf = (string)$this->cfg->custom_fields; - - // -- - // Management of Dynamic Values From XML Configuration - $safeVal = array(); - foreach($opt->tagValue->value as $val) { - array_push($safeVal, htmlentities($val, ENT_XML1)); - } - $cf = str_replace($opt->tagValue->tag,$safeVal,$cf); - // -- - - $xml = str_replace('', $cf . '', $xml); - } - - // $op = $this->APIClient->addIssueFromSimpleXML($issueXmlObj); - //file_put_contents('/var/testlink/' . __CLASS__ . '.log', $xml); - $op = $this->APIClient->addIssueFromXMLString($xml,$reporter); - - - if(is_null($op)) { - $msg = "Error Calling " . __CLASS__ . - "->APIClient->addIssueFromXMLString() " . - " check Communication TimeOut "; - throw new Exception($msg, 1); - } - - $ret = array('status_ok' => true, 'id' => (string)$op->id, - 'msg' => sprintf(lang_get('redmine_bug_created'), - $summary,$pid)); - } - catch (Exception $e) { - $msg = "Create REDMINE Ticket FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($xml)); - } - return $ret; - } - - - /** - * - */ - public function addNote($issueID,$noteText,$opt=null) - { - try - { - // needs json or xml - $issueXmlObj = new SimpleXMLElement(''); - $issueXmlObj->addChild('notes', htmlspecialchars($noteText)); - - $reporter = null; - if(!is_null($opt) && property_exists($opt, 'reporter')) - { - $reporter = $opt->reporter; - } - $op = $this->APIClient->addIssueNoteFromSimpleXML($issueID,$issueXmlObj,$reporter); - $ret = array('status_ok' => true, 'id' => (string)$op->id, - 'msg' => sprintf(lang_get('redmine_bug_created'),$summary,$issueXmlObj->project_id)); - } - catch (Exception $e) - { - $msg = "REDMINE Add Note to Ticket FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issueXmlObj)); - } - return $ret; - } - - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $tpl = "\n" . - "\n" . - "REDMINE API KEY\n" . - "http://tl.m.remine.org\n" . - "http://tl.m.remine.org/issues/ \n" . - "\n" . - "REDMINE PROJECT IDENTIFIER\n" . - " You can use numeric id or identifier string \n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - '' . "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n" . - "\n"; - return $tpl; - } - - /** - * - **/ - function canCreateViaAPI() - { - return (property_exists($this->cfg, 'projectidentifier')); - } - - +name = $name; + $this->interfaceViaDB = false; + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => false + ); + + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 3, + 'verbose' => 'resolved' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 5, + 'verbose' => 'closed' + ); + + $this->canSetReporter = true; + if (! $this->setCfg($config)) { + return false; + } + + // http://www.redmine.org/issues/6843 + // "Target version" is the new display name for this property, + // but it's still named fixed_version internally and thus in the API. + // $issueXmlObj->addChild('fixed_version_id', (string)2); + $this->translate['targetversion'] = 'fixed_version_id'; + + $this->completeCfg(); + $this->setResolvedStatusCfg(); + $this->connect(); + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; // be sure no double // at end + if (! property_exists($this->cfg, 'uriview')) { + // seems this is good only for redmine 1 and 2 ?? + // $this->cfg->uriview = $base . 'issues/show/'; + $this->cfg->uriview = $base . 'issues/'; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base; + } + + if (property_exists($this->cfg, 'attributes')) { + $attr = get_object_vars($this->cfg->attributes); + foreach ($attr as $name => $elem) { + $name = (string) $name; + if (is_object($elem)) { + $elem = get_object_vars($elem); + $cc = current($elem); + $kk = key($elem); + foreach ($cc as $value) { + $this->issueOtherAttr[$name][] = array( + $kk => (string) $value + ); + } + } else { + $this->issueOtherAttr[$name] = (string) $elem; + } + } + } + + // All attributes that I do not consider mandatory + // are managed through the issueAdditionalAttributes + // + // On Redmine 1 seems to be standard for Issues/Bugs + $this->issueDefaults = array( + 'trackerid' => 1 + ); + foreach ($this->issueDefaults as $prop => $default) { + if (! isset($this->issueAttr[$prop])) { + $this->issueAttr[$prop] = $default; + } + } + + if (property_exists($this->cfg, 'custom_fields')) { + libxml_use_internal_errors(true); + $xcfg = simplexml_load_string($this->xmlCfg); + $this->cfg->custom_fields = (string) $xcfg->custom_fields->asXML(); + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + * + */ + public function connect() + { + $processCatch = false; + + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $redUrl = (string) trim($this->cfg->uribase); + $redAK = (string) trim($this->cfg->apikey); + $pxy = new stdClass(); + $pxy->proxy = config_get('proxy'); + $this->APIClient = new redmine($redUrl, $redAK, $pxy); + + // to undestand if connection is OK, I will ask for projects. + // I've tried to ask for users but get always ERROR from redmine (not able to understand why). + try { + $items = $this->APIClient->getProjects(); + $this->connected = ! is_null($items); + unset($items); + } catch (Exception $e) { + $processCatch = true; + } + } catch (Exception $e) { + $processCatch = true; + } + + if ($processCatch) { + $logDetails = ''; + foreach (array( + 'uribase', + 'apikey' + ) as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + + $issue = null; + try { + $xmlObj = $this->APIClient->getIssue((int) $issueID); + + if (! is_null($xmlObj) && is_object($xmlObj)) { + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = (string) $xmlObj->status['id']; + $issue->statusVerbose = (string) $xmlObj->status['name']; + $issue->statusHTMLString = "[$issue->statusVerbose] "; + $issue->summary = $issue->summaryHTMLString = (string) $xmlObj->subject; + $issue->redmineProject = array( + 'name' => (string) $xmlObj->project['name'], + 'id' => (int) $xmlObj->project['id'] + ); + + $issue->isResolved = isset( + $this->resolvedStatus->byCode[$issue->statusCode]); + } + } catch (Exception $e) { + tLog(__METHOD__ . '/' . $e->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + * + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * + * @param + * string issueID + * @return string + * + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return $issue->summaryHTMLString; + } + + /** + * + * @param + * string issueID + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + /** + * From Redmine API documentation (@20130406) + * Parameters: + * + * issue - A hash of the issue attributes: + * - subject + * - description + * - project_id + * - tracker_id + * - status_id + * - category_id + * - fixed_version_id - see http://www.redmine.org/issues/6843 + * - assigned_to_id - ID of the user to assign the issue to (currently no mechanism to assign by name) + * - parent_issue_id - ID of the parent issue <= aslo know as Parent Task + * - custom_fields - See Custom fields + * - watcher_user_ids - Array of user ids to add as watchers (since 2.3.0) + */ + public function addIssue($summary, $description, $opt = null) + { + $reporter = null; + if (! is_null($opt) && property_exists($opt, 'reporter')) { + $reporter = $opt->reporter; + } + + // Check mandatory info + if (! property_exists($this->cfg, 'projectidentifier')) { + throw new exception(__METHOD__ . " project identifier is MANDATORY"); + } + + try { + // needs json or xml + $issueXmlObj = new SimpleXMLElement( + ''); + + // according with user report is better to use htmlspecialchars + // TICKET 5703: Create issue with Portuguese characters produces mangled text + // + // $issueXmlObj->addChild('subject', htmlentities($summary)); + // $issueXmlObj->addChild('description', htmlentities($description)); + + // limit size to redmine max => 255 ? + $issueXmlObj->addChild('subject', + substr(htmlspecialchars($summary), 0, 255)); + $issueXmlObj->addChild('description', htmlspecialchars($description)); + + // Got from XML Configuration + // improvement + $pid = (string) $this->cfg->projectidentifier; + $issueXmlObj->addChild('project_id', $pid); + + if (property_exists($this->cfg, 'trackerid')) { + $issueXmlObj->addChild('tracker_id', + (string) $this->cfg->trackerid); + } + + // try to be generic + if (property_exists($this->cfg, 'parent_issue_id')) { + $issueXmlObj->addChild('parent_issue_id', + (string) $this->cfg->parent_issue_id); + } + + // Why issuesAttr is issue ? + // Idea was + // on XML config on TestLink provide direct access to a minimun set of MANDATORY + // attributes => without it issue can not be created. + // After first development/release of this feature people that knows better + // Redmine start asking for other attributes. + // Then to manage this other set of unknown attributes in a generic way idea was + // loop over an object property and blidly add it to request. + // + // Drawback/limitations + // I can not manage type (because I do not request this info) => will treat always as STRING + // + // * Special case Target Version + // http://www.redmine.org/issues/6843 + // "Target version" is the new display name for this property, + // but it's still named fixed_version internally and thus in the API. + // $issueXmlObj->addChild('fixed_version_id', (string)2); + // + if (! is_null($this->issueOtherAttr)) { + foreach ($this->issueOtherAttr as $ka => $kv) { + // will treat everything as simple strings or can I check type + // see completeCfg() + $issueXmlObj->addChild( + (isset($this->translate[$ka]) ? $this->translate[$ka] : $ka), + (string) $kv); + } + } + + // In order to manage custom fields in simple way, + // it seems that is better create here plain XML String + // + $xml = $issueXmlObj->asXML(); + if (property_exists($this->cfg, 'custom_fields')) { + $cf = (string) $this->cfg->custom_fields; + + // Management of Dynamic Values From XML Configuration + $safeVal = array(); + foreach ($opt->tagValue->value as $val) { + array_push($safeVal, htmlentities($val, ENT_XML1)); + } + $cf = str_replace($opt->tagValue->tag, $safeVal, $cf); + + $xml = str_replace('', $cf . '', $xml); + } + + $op = $this->APIClient->addIssueFromXMLString($xml, $reporter); + + if (is_null($op)) { + $msg = "Error Calling " . __CLASS__ . + "->APIClient->addIssueFromXMLString() " . + " check Communication TimeOut "; + throw new Exception($msg, 1); + } + + $ret = array( + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => sprintf(lang_get('redmine_bug_created'), $summary, $pid) + ); + } catch (Exception $e) { + $msg = "Create REDMINE Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($xml) + ); + } + return $ret; + } + + /** + */ + public function addNote($issueID, $noteText, $opt = null) + { + try { + // needs json or xml + $issueXmlObj = new SimpleXMLElement( + ''); + $issueXmlObj->addChild('notes', htmlspecialchars($noteText)); + + $reporter = null; + if (! is_null($opt) && property_exists($opt, 'reporter')) { + $reporter = $opt->reporter; + } + $op = $this->APIClient->addIssueNoteFromSimpleXML($issueID, + $issueXmlObj, $reporter); + $ret = array( + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => sprintf(lang_get('redmine_bug_created'), $summary, + $issueXmlObj->project_id) + ); + } catch (Exception $e) { + $msg = "REDMINE Add Note to Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issueXmlObj) + ); + } + return $ret; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "REDMINE API KEY\n" . + "http://tl.m.remine.org\n" . + "http://tl.m.remine.org/issues/ \n" . + "\n" . + "REDMINE PROJECT IDENTIFIER\n" . + " You can use numeric id or identifier string \n" . + "\n" . "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + '' . "\n" . + '' . "\n" . + '' . "\n" . '' . + "\n" . + '' . + "\n" . '' . "\n" . + '' . "\n" . '' . + "\n" . '' . "\n" . + '' . "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n" . "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return property_exists($this->cfg, 'projectidentifier'); + } } diff --git a/lib/issuetrackerintegration/tracxmlrpcInterface.class.php b/lib/issuetrackerintegration/tracxmlrpcInterface.class.php index 07213d3f0e..57169f8f19 100644 --- a/lib/issuetrackerintegration/tracxmlrpcInterface.class.php +++ b/lib/issuetrackerintegration/tracxmlrpcInterface.class.php @@ -1,341 +1,311 @@ -name = $name; - $this->interfaceViaDB = false; - - if( !$this->setCfg($config) ) - { - return false; - } - - $this->methodOpt['buildViewBugLink'] = array('addSummary' => true, 'colorByStatus' => false); - - $this->defaultResolvedStatus = array(); - $this->defaultResolvedStatus[] = array('code' => 'r', 'verbose' => 'resolved'); - $this->defaultResolvedStatus[] = array('code' => 'v', 'verbose' => 'verified'); - $this->defaultResolvedStatus[] = array('code' => 'c', 'verbose' => 'closed'); - - $this->setResolvedStatusCfg(); - $this->completeCfg(); - $this->connect(); - $this->guiCfg = array('use_decoration' => true); // add [] on summary - } - - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - $base = trim($this->cfg->uribase,"/") . '/'; // be sure no double // at end - if( !property_exists($this->cfg,'urixmlrpc') ) - { - $this->cfg->urixmlrpc = $base . 'xmlrpc'; - } - - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'ticket/'; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base . 'newticket/'; - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxNumeric($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - // echo __METHOD__ . '

    '; - try - { - // CRITIC NOTICE for developers - // $this->cfg is a simpleXML Object, then seems very conservative and safe - // to cast properties BEFORE using it. - $this->createAPIClient(); - $this->connected = true; - - //var_dump($this->APIClient); - //echo '

    END ' . __METHOD__ . '

    '; - - } - catch(Exception $e) - { - $logDetails = ''; - foreach(array('uribase','apikey') as $v) - { - $logDetails .= "$v={$this->cfg->$v} / "; - } - $logDetails = trim($logDetails,'/ '); - $this->connected = false; - tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - public function getIssue($issueID) - { - // array ticket.get(int id) Fetch a ticket. - // Returns [id, time_created, time_changed, attributes]. - // attributes is following map (@20120826) - // - // ------------------------------------------ - // key | value - // ------------------------------------------ - // status | new - // description | MERCURIAL FIRST TICKET - // reporter | admin - // cc | [empty string] - // component | component1 - // summary | MERCURIAL FIRST TICKET - // priority | major - // keywords | [empty string] - // version | [empty string] - // milestone | [empty string] - // owner | somebody - // type | defect - // - - $resp = $this->sendCmd('ticket.get', $issueID); - if( $resp == false ) - { - $issue = null; - } - else - { - $attrib = $resp[self::TICKET_GET_ATTRIBUTES_IDX]; - $issue = new stdClass(); - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = 0; - $issue->statusVerbose = $attrib['status']; - $issue->statusHTMLString = "[$issue->statusVerbose] "; - $issue->summary = $issue->summaryHTMLString = $attrib['summary']; - $issue->isResolved = isset($this->resolvedStatus->byName[$issue->statusVerbose]); - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return !is_null($issue) ? $issue->statusCode : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - return $this->getIssueStatusCode($issueID); - } - - /** - * - * @param string issueID - * - * @return string - * - **/ - function getIssueSummaryHTMLString($issueID) - { - $issue = $this->getIssue($issueID); - $str = $issue->summaryHTMLString; - if($this->guiCfg['use_decoration']) - { - $str = "[" . $str . "] "; - } - return $str; - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - $dBugLabel = array('label' => __METHOD__); - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = is_object($issue) && !is_null($issue); - } - return $status_ok; - } - - - /** - * - * - **/ - function createAPIClient() - { - try - { - // Create a new connection with the TRAC-server. - $this->APIClient = new xmlrpc_client($this->cfg->urixmlrpc); - - // Set the credentials to use to log in. - $this->APIClient->setCredentials($this->cfg->username, $this->cfg->password); - - // Disable certificate checking. Don't need to check it. - $this->APIClient->verifyhost = false; - $this->APIClient->verifypeer = false; - - } - catch(Exception $e) - { - $this->connected = false; - tLog(__METHOD__ . $e->getMessage(), 'ERROR'); - } - } - - - /** - * - */ - function sendCmd($cmd, $id) - { - $param = new xmlrpcval(intval($id)); - $msg = new xmlrpcmsg($cmd); - $msg->addParam($param); - - // Send request with timeout disabled - $response = $this->APIClient->send($msg, 0); - if (!$response->errno) - { - $response = php_xmlrpc_decode($response->val); - } - else - { - tLog(__METHOD__ . (serialize($response)), 'ERROR'); - $response = false; - } - - return $response; - } - - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $tpl = "\n" . - "\n" . - "USERNAME\n" . - "PASSWORD\n" . - "'http:///\n" . - "\n"; - return $tpl; - } - -} \ No newline at end of file +name = $name; + $this->interfaceViaDB = false; + + if (! $this->setCfg($config)) { + return false; + } + + $this->methodOpt['buildViewBugLink'] = array( + 'addSummary' => true, + 'colorByStatus' => false + ); + + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = array( + 'code' => 'r', + 'verbose' => 'resolved' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 'v', + 'verbose' => 'verified' + ); + $this->defaultResolvedStatus[] = array( + 'code' => 'c', + 'verbose' => 'closed' + ); + + $this->setResolvedStatusCfg(); + $this->completeCfg(); + $this->connect(); + $this->guiCfg = array( + 'use_decoration' => true + ); // add [] on summary + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + $base = trim($this->cfg->uribase, "/") . '/'; // be sure no double // at end + if (! property_exists($this->cfg, 'urixmlrpc')) { + $this->cfg->urixmlrpc = $base . 'xmlrpc'; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'ticket/'; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base . 'newticket/'; + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $this->createAPIClient(); + $this->connected = true; + } catch (Exception $e) { + $logDetails = ''; + foreach (array( + 'uribase', + 'apikey' + ) as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getIssue($issueID) + { + // array ticket.get(int id) Fetch a ticket. + // Returns [id, time_created, time_changed, attributes]. + // attributes is following map (@20120826) + // + // ------------------------------------------ + // key | value + // ------------------------------------------ + // status | new + // description | MERCURIAL FIRST TICKET + // reporter | admin + // cc | [empty string] + // component | component1 + // summary | MERCURIAL FIRST TICKET + // priority | major + // keywords | [empty string] + // version | [empty string] + // milestone | [empty string] + // owner | somebody + // type | defect + // + $resp = $this->sendCmd('ticket.get', $issueID); + if (! $resp) { + $issue = null; + } else { + $attrib = $resp[self::TICKET_GET_ATTRIBUTES_IDX]; + $issue = new stdClass(); + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = 0; + $issue->statusVerbose = $attrib['status']; + $issue->statusHTMLString = "[$issue->statusVerbose] "; + $issue->summary = $issue->summaryHTMLString = $attrib['summary']; + $issue->isResolved = isset( + $this->resolvedStatus->byName[$issue->statusVerbose]); + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * + * @param + * string issueID + * @return string + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + $str = $issue->summaryHTMLString; + if ($this->guiCfg['use_decoration']) { + $str = "[" . $str . "] "; + } + return $str; + } + + /** + * + * @param + * string issueID + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + /** + */ + private function createAPIClient() + { + try { + // Create a new connection with the TRAC-server. + $this->APIClient = new xmlrpc_client($this->cfg->urixmlrpc); + + // Set the credentials to use to log in. + $this->APIClient->setCredentials($this->cfg->username, + $this->cfg->password); + + // Disable certificate checking. Don't need to check it. + $this->APIClient->verifyhost = false; + $this->APIClient->verifypeer = false; + } catch (Exception $e) { + $this->connected = false; + tLog(__METHOD__ . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + private function sendCmd($cmd, $id) + { + $param = new xmlrpcval(intval($id)); + $msg = new xmlrpcmsg($cmd); + $msg->addParam($param); + + // Send request with timeout disabled + $response = $this->APIClient->send($msg, 0); + if (! $response->errno) { + $response = php_xmlrpc_decode($response->val); + } else { + tLog(__METHOD__ . (serialize($response)), 'ERROR'); + $response = false; + } + + return $response; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "USERNAME\n" . "PASSWORD\n" . + "'http:///\n" . + "\n"; + } +} diff --git a/lib/issuetrackerintegration/trellorestInterface.class.php b/lib/issuetrackerintegration/trellorestInterface.class.php new file mode 100644 index 0000000000..036a6baeaf --- /dev/null +++ b/lib/issuetrackerintegration/trellorestInterface.class.php @@ -0,0 +1,316 @@ +name = $name; + $this->interfaceViaDB = false; + $this->methodOpt['buildViewBugLink'] = [ + 'addSummary' => true, + 'colorByStatus' => false, + 'addReporter' => false, + 'addHandler' => false + ]; + + $this->defaultResolvedStatus = []; + + /* + * @20201207 + * $this->defaultResolvedStatus[] = ['code' => 1, 'verbose' => 'queue']; + * $this->defaultResolvedStatus[] = ['code' => 2, 'verbose' => 'in progress']; + * $this->defaultResolvedStatus[] = ['code' => 3, 'verbose' => 'done']; + */ + + // @20201207 $this->canSetReporter = true; + if (! $this->setCfg($config)) { + return false; + } + + $this->completeCfg(); + $this->setResolvedStatusCfg(); + $this->connect(); + } + + /** + */ + private function completeCfg() + { + $this->cfg->implements = __CLASS__; + + $this->cfg->uribase = trim($this->cfg->uribase, "/"); + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $this->cfg->uribase; + } + + if (property_exists($this->cfg, 'options')) { + $option = get_object_vars($this->cfg->options); + foreach ($option as $name => $elem) { + $name = (string) $name; + $this->options[$name] = (string) $elem; + } + } + + if (! property_exists($this->cfg, 'userinteraction')) { + $this->cfg->userinteraction = 0; + } + + if (! property_exists($this->cfg, 'createissueviaapi')) { + $this->cfg->createissueviaapi = 0; + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * Two formats allowed + * https://trello.com/c/XZZftZ8A/12-backlog01-yy + * XZZftZ8A + */ + public function normalizeBugID($issueID) + { + $norm = $issueID; + $pieces = explode('/', $issueID); + $piecesQty = count($pieces); + if ($piecesQty > 1) { + // MAGIC + // 0 -> https: + // 1 -> /trello.com + // 2 -> c + // 3 -> XZZftZ8A + // 4 -> 12-backlog01-yy + $norm = $pieces[$piecesQty - 2]; + } + return $norm; + } + + /** + * checks id for validity + * + * @param + * string issueID + * Two formats allowed + * https://trello.com/c/XZZftZ8A/12-backlog01-yy + * XZZftZ8A + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + // Two formats allowed + // https://trello.com/c/XZZftZ8A/12-backlog01-yy + // XZZftZ8A + return $this->checkBugIDSyntaxString($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + $processCatch = false; + + try { + // CRITIC NOTICE for developers + // $this->cfg is a simpleXML Object, then seems very conservative and safe + // to cast properties BEFORE using it. + $myContext = [ + 'url' => (string) trim($this->cfg->uribase), + 'apikey' => (string) trim($this->cfg->apikey), + 'apitoken' => (string) trim($this->cfg->apitoken), + 'boardid' => (string) trim($this->cfg->boardid) + ]; + + $cfg = [ + 'proxy' => config_get('proxy') + ]; + + $this->APIClient = new trello($myContext, $cfg); + // to undestand if connection is OK, I will ask for users. + try { + $item = $this->APIClient->getBoard(); + $this->connected = ($item != null); + } catch (Exception $e) { + $processCatch = true; + } + } catch (Exception $e) { + $processCatch = true; + } + + if ($processCatch) { + $logDetails = ''; + foreach ([ + 'uribase' + ] as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function buildViewBugURL($issueID) + { + return $this->APIClient->getIssueURL($issueID); + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + $issue = $this->getIssue($issueID); + return ! is_null($issue) ? $issue->statusVerbose : false; + } + + /** + * + * @param + * string issueID + * @return string + */ + public function getIssueSummaryHTMLString($issueID) + { + $issue = $this->getIssue($issueID); + return $issue->summaryHTMLString; + } + + /** + * + * @param + * string issueID + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = is_object($issue) && ! is_null($issue); + } + return $status_ok; + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + tLog(__METHOD__ . '/Not Connected ', 'ERROR'); + return false; + } + + $issue = null; + try { + $jsonObj = $this->APIClient->getIssue($issueID); + if (! is_null($jsonObj) && is_object($jsonObj)) { + $issue = new stdClass(); + $issue->IDHTMLString = "{$jsonObj->idShort} : "; + + // dateLastActivity ? + // idList -> get the name + $silo = $this->APIClient->getList($jsonObj->idList); + + // we will use the list name as the status verbose + $issue->statusCode = (string) $jsonObj->idList; + $issue->statusVerbose = (string) $silo->name; + $issue->statusHTMLString = "[{$issue->statusVerbose}]"; + + $verbose = (string) $jsonObj->name; // . " {{$jsonObj->dateLastActivity}}"; + $issue->summary = $issue->summaryHTMLString = $verbose; + + $issue->isResolved = false; + } + } catch (Exception $e) { + tLog(__METHOD__ . '/' . $e->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "\n" . + "TRELLO API KEY\n" . + "TRELLO API TOKEN\n" . + "https://api.trello.com/1/\n" . + "BOARD IDENTIFICATOR\n" . "\n"; + } + + /** + */ + public function canCreateViaAPI() + { + return false; + } + + /** + */ + public function canAddNoteViaAPI() + { + return false; + } +} diff --git a/lib/issuetrackerintegration/tuleaprestInterface.class.php b/lib/issuetrackerintegration/tuleaprestInterface.class.php new file mode 100644 index 0000000000..63403d437b --- /dev/null +++ b/lib/issuetrackerintegration/tuleaprestInterface.class.php @@ -0,0 +1,514 @@ +name = $name; + $this->interfaceViaDB = false; + $this->defaultResolvedStatus = array(); + $this->defaultResolvedStatus[] = 'Invalid'; + $this->defaultResolvedStatus[] = 'Wont Fix'; + $this->defaultResolvedStatus[] = 'Fixed'; + $this->defaultResolvedStatus[] = 'Works for me'; + $this->defaultResolvedStatus[] = 'Duplicate'; + $this->methodOpt = array( + 'buildViewBugLink' => array( + 'addSummary' => true, + 'colorByStatus' => false + ) + ); + $this->connected = false; + + if (! $this->setCfg($config)) { + return false; + } else { + // check the tracker ID + if (property_exists($this->cfg, 'tracker')) { + $this->trackerID = trim((string) $this->cfg->tracker); + if (strlen($this->trackerID) > 0 && + ! $this->checkTrackerIDSyntax($this->trackerID)) { + return false; + } + } else { + // tracker ID may be absent (bug creation from Testlink will not be possible) + $this->trackerID = ""; + } + + // check the base URI + if (property_exists($this->cfg, 'uribase')) { + $this->URIBase = trim((string) $this->cfg->uribase); + if (strlen($this->URIBase) > 0 && + ! $this->checkURLSyntax($this->URIBase)) { + return false; + } + } else { + return false; + } + } + + $this->completeCfg(); + $this->connect(); + $this->setResolvedStatusCfg(); + } + + /** + * checks a tracker id for validity (a numeric value) + * + * @param + * string tracker ID + * @return bool returns true if the tracker id has the right format + */ + private function checkTrackerIDSyntax($trackerID) + { + $valid = true; + $blackList = '/\D/i'; + if (preg_match($blackList, $trackerID)) { + $valid = false; + } else { + $valid = (intval($trackerID) > 0); + } + + return $valid; + } + + /** + * checks a URL for validity + * + * @param + * string URL + * @return bool returns true if the param is an URL + */ + private function checkURLSyntax($url) + { + return filter_var($url, FILTER_VALIDATE_URL) && + stripos($url, "http") === 0; + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * Set resolved status + * + * @author Aurelien TISNE + */ + public function setResolvedStatusCfg() + { + $statusCfg = $this->getResolvedStatus(); + if (! $statusCfg) { + if (property_exists($this->cfg, 'resolvedstatus')) { + $statusCfg = (array) $this->cfg->resolvedstatus; + } else { + $statusCfg['status'] = $this->defaultResolvedStatus; + } + } + + $this->resolvedStatus = new stdClass(); + foreach ($statusCfg['status'] as $cfx) { + $this->resolvedStatus->byName[] = $cfx; + } + } + + /** + * Get resolved status from ITS + * + * @author Aurelien TISNE + */ + public function getResolvedStatus() + { + if (! $this->isConnected()) { + return null; + } + + if ($this->trackerID == '') { + return null; + } + + $ret = null; + try { + $tracker = $this->APIClient->getTrackerById($this->trackerID); + if ($tracker) { + // field ID containing the status semantic + $statusID = $tracker->semantics->status->field_id; + // opened values ID + $statusValuesID = $tracker->semantics->status->value_ids; + // $ret = array(); + // retrieve the field containing the status semantic + $status = $this->getField($tracker, $statusID); + if (! $status) { + throw new Exception( + 'The field ' . $statusID . + ' cannot be found in the tracker "' . $tracker->label . + '" (' . $tracker->id . ').'); + } + + // retrieve the labels of closed status + $ret['status'] = $this->getClosedLabels($status, $statusValuesID); + // check that all labels have been found + if (count($ret['status']) != + (count($status->values) - count($statusValuesID))) { + throw new Exception('Some labels was not found.'); + } + } else { + throw new Exception( + 'The tracker ' . $this->trackerID . ' was not found.'); + } + } catch (Exception $e) { + tLog($e->getMessage(), 'ERROR'); + $ret = null; + } + + return $ret; + } + + /** + * Retrieve a field from a tracker + * + * @param object $tracker + * A tracker + * @param string $fieldID + * A tracker item ID + * + * @author Aurelien TISNE + */ + private function getField($tracker, $fieldID) + { + $i = count($tracker->fields); + $field = null; + while ($i > 0 && ! $field) { + if ($tracker->fields[$i - 1]->field_id == $fieldID) { + $field = $tracker->fields[$i - 1]; + } else { + $i -= 1; + } + } + + return $field; + } + + /** + * Retrieve labels of closed status values ID from the status field + * + * @param object $statusField + * Tracker field containing the status semantic + * @param array $valuesID + * List of opened values ID + * @author Aurelien TISNE + */ + private function getClosedLabels($statusField, $openValuesID) + { + if (! property_exists($statusField, "values")) { + return null; + } + + $ret = array(); + foreach ($statusField->values as $value) { + if (! in_array($value->id, $openValuesID)) { + $ret[] = $value->label; + } + } + + return $ret; + } + + /** + * checks id for validity + * + * @param + * string issueID + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxNumeric($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + $processCatch = false; + + try { + + $this->APIClient = new tuleap((string) trim($this->cfg->uriapi), + (string) trim($this->cfg->username), + (string) trim($this->cfg->password)); + + try { + $this->connected = $this->APIClient->Connect(); + } catch (Exception $e) { + $processCatch = true; + } + } catch (Exception $e) { + $processCatch = true; + } + + if ($processCatch) { + $logDetails = ''; + foreach (array( + 'uribase', + 'username' + ) as $v) { + $logDetails .= "$v={$this->cfg->$v} / "; + } + $logDetails = trim($logDetails, '/ '); + $this->connected = false; + tLog(__METHOD__ . " [$logDetails] " . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + private function buildStatusHTMLString($status) + { + if (in_array($status, $this->resolvedStatus->byName)) // Closed type status + { + $str = "" . $status . ""; + } else { + $str = $status; + } + return "[{$str}] "; + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + return false; + } + + try { + $issue = $this->APIClient->getArtifactById((int) $issueID); + if (! is_null($issue) && is_object($issue)) { + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusVerbose = $issue->status; + $issue->statusHTMLString = $this->buildStatusHTMLString( + $issue->status); + $issue->summaryHTMLString = $issue->title; + + $issue->isResolved = isset( + $this->resolvedStatus->byName[$issue->statusVerbose]); + } + } catch (Exception $e) { + tLog($e->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return boolean + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return (! is_null($issue) && is_object($issue)) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + return $this->getIssueStatusCode($issueID); + } + + /** + * Returns a configuration template + * + * @return string + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "TULEAP LOGIN NAME\n" . + "TULEAP PASSWORD\n" . + "\n" . + "https://" . $_SERVER['SERVER_NAME'] . "\n" . + "\n" . + "\n" . + "\n" . + "https://" . $_SERVER['SERVER_NAME'] . "/api\n" . + "https://" . $_SERVER['SERVER_NAME'] . + "/plugins/tracker/?aid=\n" . "https://" . + $_SERVER['SERVER_NAME'] . + "/plugins/tracker/?func=new-artifact&tracker=TULEAP TRACKER ID\n" . + "\n" . + "\n" . + "\n" . + "TULEAP TRACKER ID\n" . + "\n" . + "\n" . + "\n" . "\n" . + "Fixed\n" . "Invalid\n" . + "Wont Fix\n" . "Works for me\n" . + "Duplicate\n" . "\n" . + "\n"; + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + // '/' at uribase name creates issue with API + $this->URIBase = trim($this->URIBase, "/"); + + $base = $this->URIBase . '/'; + + if (! property_exists($this->cfg, 'uriapi')) { + $this->cfg->uriapi = $base . 'api'; + } + + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'plugins/tracker/?aid='; + } + + if (! property_exists($this->cfg, 'uricreate')) { + if ($this->trackerID != "") { + $this->cfg->uricreate = $base . 'plugins/tracker/?tracker=' . + $this->trackerID . '&func=new-artifact'; + } else { + $this->cfg->uricreate = ''; + } + } + } + + /** + * + * @param + * string issueID + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = (! is_null($issue) && is_object($issue)); + } + return $status_ok; + } + + /** + */ + public function addIssue($summary, $description, $opt = null) + { + try { + $op = $this->APIClient->createIssue((int) $this->trackerID, $summary, + $description); + + if (is_null($op)) { + throw new Exception( + "Something's wrong when creating an artefact"); + } else { + $ret = array( + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => sprintf(lang_get('tuleap_bug_created'), $summary, + (string) $op->tracker->project->id) + ); + } + } catch (Exception $e) { + $msg = "Create artifact FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg + ); + } + return $ret; + } + + /** + */ + public function addNote($bugId, $noteText, $opt = null) + { + if (! $this->isConnected()) { + return null; + } + + try { + $noteText = "Reporter: " . $opt->reporter . " <" . + $opt->reporter_email . ">\n" . $noteText; + $this->APIClient->addTrackerArtifactMessage((int) $bugId, $noteText); + $ret = array( + 'status_ok' => true, + 'msg' => sprintf(lang_get('tuleap_bug_comment'), $noteText) + ); + } catch (Exception $e) { + $msg = "Add note FAILURE for bug " . $bugId . " => " . + $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'msg' => $msg + ); + } + + return $ret; + } + + /** + */ + public function canCreateViaAPI() + { + return $this->trackerID !== ''; + } +} +?> diff --git a/lib/issuetrackerintegration/youtrackrestInterface.class.php b/lib/issuetrackerintegration/youtrackrestInterface.class.php index 996e615f1b..e5ce3c508e 100644 --- a/lib/issuetrackerintegration/youtrackrestInterface.class.php +++ b/lib/issuetrackerintegration/youtrackrestInterface.class.php @@ -1,288 +1,269 @@ -name = $name; - $this->interfaceViaDB = false; - $this->methodOpt = array('buildViewBugLink' => array('addSummary' => true, 'colorByStatus' => true)); - $this->connected = false; - if( $this->setCfg($config) ) - { - $this->completeCfg(); - $this->connect(); - } - } - - /** - * useful for testing - * - * - **/ - function getAPIClient() - { - return $this->APIClient; - } - - /** - * checks id for validity - * - * @param string issueID - * - * @return bool returns true if the bugid has the right format, false else - **/ - function checkBugIDSyntax($issueID) - { - return $this->checkBugIDSyntaxString($issueID); - } - - /** - * establishes connection to the bugtracking system - * - * @return bool - * - **/ - function connect() - { - try - { - $this->APIClient = new \YouTrack\Connection($this->cfg->uribase, - $this->cfg->username, $this->cfg->password); - $this->connected = true; - } - catch(Exception $e) - { - $this->connected = false; - tLog(__METHOD__ . $e->getMessage(), 'ERROR'); - } - } - - /** - * - * - **/ - function isConnected() - { - return $this->connected; - } - - - /** - * - * - **/ - function getIssue($issueID) - { - if (!$this->isConnected()) - { - return false; - } - - try - { - $issue = $this->APIClient->get_issue($issueID); - if( !is_null($issue) && is_object($issue) ) - { - $issue->IDHTMLString = "{$issueID} : "; - $issue->statusCode = $issue->State; - $issue->statusVerbose = $issue->statusCode; - $issue->statusHTMLString = "[$issue->statusCode] "; - $issue->summaryHTMLString = $issue->summary; - } - - } - catch(\YouTrack\YouTrackException $yte) - { - tLog($yte->getMessage(),'ERROR'); - $issue = null; - } - return $issue; - } - - - /** - * Returns status for issueID - * - * @param string issueID - * - * @return - **/ - function getIssueStatusCode($issueID) - { - $issue = $this->getIssue($issueID); - return (!is_null($issue) && is_object($issue))? $issue->statusCode : false; - } - - /** - * Returns status in a readable form (HTML context) for the bug with the given id - * - * @param string issueID - * - * @return string - * - **/ - function getIssueStatusVerbose($issueID) - { - $str = "Ticket ID - " . $issueID . " - does not exist in BTS"; - $issue = $this->getBugStatus($issueID); - if (!is_null($issue) && is_object($issue)) - { - $str = array_search($issue->status, $this->statusDomain); - if (strcasecmp($str, 'closed') == 0 || strcasecmp($str, 'resolved') == 0 ) - { - $str = "" . $str . ""; - } - $str = "" . $issueID . ": [" . $str . "] " ; - } - return $str; - } - - - - /** - * - * @author francisco.mancardi@gmail.com> - **/ - public static function getCfgTemplate() - { - $template = "\n" . - "\n" . - "YOUTRACK LOGIN NAME\n" . - "YOUTRACK PASSWORD\n" . - "\n" . - "\n" . - "http://testlink.myjetbrains.com/youtrack\n". - "\n" . - "YOUTRACK PROJECT ID\n". - "\n"; - return $template; - } - - - /** - * - * check for configuration attributes than can be provided on - * user configuration, but that can be considered standard. - * If they are MISSING we will use 'these carved on the stone values' - * in order to simplify configuration. - * - * - **/ - function completeCfg() - { - // '/' at uribase name creates issue with API - $this->cfg->uribase = trim((string)$this->cfg->uribase,"/"); - - $base = $this->cfg->uribase . '/'; - if( !property_exists($this->cfg,'uriview') ) - { - $this->cfg->uriview = $base . 'issue/'; - } - - if( !property_exists($this->cfg,'uricreate') ) - { - $this->cfg->uricreate = $base . 'dashboard#newissue=yes'; - } - - $this->issueTemplate = array(); - $this->issueDefaults = array('assignee' => '', 'priority' => '', 'type' => '', - 'subsystem' => '', 'state' => '', 'affectsversion' => '', - 'fixedversion' => '', 'fixedinbuild' => ''); - foreach($this->issueDefaults as $prop => $default) - { - $this->cfg->$prop = (string)(property_exists($this->cfg,$prop) ? $this->cfg->$prop : $default); - $this->issueTemplate[$prop] = $this->cfg->$prop; - } - - - - } - - /** - * @param string issueID - * - * @return bool true if issue exists on BTS - **/ - function checkBugIDExistence($issueID) - { - if(($status_ok = $this->checkBugIDSyntax($issueID))) - { - $issue = $this->getIssue($issueID); - $status_ok = (!is_null($issue) && is_object($issue)); - } - return $status_ok; - } - - /** - * - */ - public function addIssue($summary,$description) - { - try - { - $issue = $this->issueTemplate; - $op = $this->APIClient->create_issue((string)$this->cfg->project, $issue['assignee'], - $summary, $description, $issue['priority'], - $issue['type'], $issue['subsystem'], $issue['state'], - $issue['affectsversion'], - $issue['fixedversion'], $issue['fixedinbuild']); - - $ret = array('status_ok' => true, 'id' => (string)$op->id, - 'msg' => sprintf(lang_get('youtrack_bug_created'),$summary,(string)$this->cfg->project)); - } - catch (Exception $e) - { - $msg = "Create YOUTRACK Ticket FAILURE => " . $e->getMessage(); - tLog($msg, 'WARNING'); - $ret = array('status_ok' => false, 'id' => -1, 'msg' => $msg . ' - serialized issue:' . serialize($issue)); - } - return $ret; - } - - - /** - * - **/ - function canCreateViaAPI() - { - return (property_exists($this->cfg, 'project')); - } - - -} \ No newline at end of file +name = $name; + $this->interfaceViaDB = false; + $this->methodOpt = array( + 'buildViewBugLink' => array( + 'addSummary' => true, + 'colorByStatus' => true + ) + ); + $this->connected = false; + if ($this->setCfg($config)) { + $this->completeCfg(); + $this->connect(); + } + } + + /** + * useful for testing + */ + public function getAPIClient() + { + return $this->APIClient; + } + + /** + * checks id for validity + * + * @param + * string issueID + * + * @return bool returns true if the bugid has the right format, false else + */ + public function checkBugIDSyntax($issueID) + { + return $this->checkBugIDSyntaxString($issueID); + } + + /** + * establishes connection to the bugtracking system + * + * @return bool + */ + public function connect() + { + try { + $this->APIClient = new \YouTrack\Connection($this->cfg->uribase, + $this->cfg->username, $this->cfg->password); + $this->connected = true; + } catch (Exception $e) { + $this->connected = false; + tLog(__METHOD__ . $e->getMessage(), 'ERROR'); + } + } + + /** + */ + public function isConnected() + { + return $this->connected; + } + + /** + */ + public function getIssue($issueID) + { + if (! $this->isConnected()) { + return false; + } + + try { + $issue = $this->APIClient->get_issue($issueID); + if (! is_null($issue) && is_object($issue)) { + $issue->IDHTMLString = "{$issueID} : "; + $issue->statusCode = $issue->State; + $issue->statusVerbose = $issue->statusCode; + $issue->statusHTMLString = "[$issue->statusCode] "; + $issue->summaryHTMLString = $issue->summary; + } + } catch (\YouTrack\YouTrackException $yte) { + tLog($yte->getMessage(), 'ERROR'); + $issue = null; + } + return $issue; + } + + /** + * Returns status for issueID + * + * @param + * string issueID + * @return + */ + public function getIssueStatusCode($issueID) + { + $issue = $this->getIssue($issueID); + return (! is_null($issue) && is_object($issue)) ? $issue->statusCode : false; + } + + /** + * Returns status in a readable form (HTML context) for the bug with the given id + * + * @param + * string issueID + * @return string + */ + public function getIssueStatusVerbose($issueID) + { + $str = "Ticket ID - " . $issueID . " - does not exist in BTS"; + $issue = $this->getBugStatus($issueID); + if (! is_null($issue) && is_object($issue)) { + $str = array_search($issue->status, $this->statusDomain); + if (strcasecmp($str, 'closed') == 0 || + strcasecmp($str, 'resolved') == 0) { + $str = "" . $str . ""; + } + $str = "" . $issueID . ": [" . $str . "] "; + } + return $str; + } + + /** + * + * @author francisco.mancardi@gmail.com> + */ + public static function getCfgTemplate() + { + return "\n" . "\n" . + "YOUTRACK LOGIN NAME\n" . + "YOUTRACK PASSWORD\n" . + "\n" . + "\n" . + "http://testlink.myjetbrains.com/youtrack\n" . + "\n" . + "YOUTRACK PROJECT ID\n" . "\n"; + } + + /** + * check for configuration attributes than can be provided on + * user configuration, but that can be considered standard. + * If they are MISSING we will use 'these carved on the stone values' + * in order to simplify configuration. + */ + private function completeCfg() + { + // '/' at uribase name creates issue with API + $this->cfg->uribase = trim((string) $this->cfg->uribase, "/"); + + $base = $this->cfg->uribase . '/'; + if (! property_exists($this->cfg, 'uriview')) { + $this->cfg->uriview = $base . 'issue/'; + } + + if (! property_exists($this->cfg, 'uricreate')) { + $this->cfg->uricreate = $base . 'dashboard#newissue=yes'; + } + + $this->issueTemplate = array(); + $this->issueDefaults = array( + 'assignee' => '', + 'priority' => '', + 'type' => '', + 'subsystem' => '', + 'state' => '', + 'affectsversion' => '', + 'fixedversion' => '', + 'fixedinbuild' => '' + ); + foreach ($this->issueDefaults as $prop => $default) { + $this->cfg->$prop = (string) (property_exists($this->cfg, $prop) ? $this->cfg->$prop : $default); + $this->issueTemplate[$prop] = $this->cfg->$prop; + } + } + + /** + * + * @param + * string issueID + * + * @return bool true if issue exists on BTS + */ + public function checkBugIDExistence($issueID) + { + if ($status_ok = $this->checkBugIDSyntax($issueID)) { + $issue = $this->getIssue($issueID); + $status_ok = (! is_null($issue) && is_object($issue)); + } + return $status_ok; + } + + /** + */ + public function addIssue($summary, $description) + { + try { + $issue = $this->issueTemplate; + $op = $this->APIClient->create_issue((string) $this->cfg->project, + $issue['assignee'], $summary, $description, $issue['priority'], + $issue['type'], $issue['subsystem'], $issue['state'], + $issue['affectsversion'], $issue['fixedversion'], + $issue['fixedinbuild']); + + $ret = array( + 'status_ok' => true, + 'id' => (string) $op->id, + 'msg' => sprintf(lang_get('youtrack_bug_created'), $summary, + (string) $this->cfg->project) + ); + } catch (Exception $e) { + $msg = "Create YOUTRACK Ticket FAILURE => " . $e->getMessage(); + tLog($msg, 'WARNING'); + $ret = array( + 'status_ok' => false, + 'id' => - 1, + 'msg' => $msg . ' - serialized issue:' . serialize($issue) + ); + } + return $ret; + } + + /** + */ + public function canCreateViaAPI() + { + return property_exists($this->cfg, 'project'); + } +} diff --git a/lib/issuetrackers/issueTrackerCommands.class.php b/lib/issuetrackers/issueTrackerCommands.class.php index 421e2bb75d..ae19e335e1 100644 --- a/lib/issuetrackers/issueTrackerCommands.class.php +++ b/lib/issuetrackers/issueTrackerCommands.class.php @@ -1,286 +1,286 @@ -db=$dbHandler; - $this->issueTrackerMgr = new tlIssueTracker($dbHandler); - $this->entitySpec = $this->issueTrackerMgr->getEntitySpec(); - - $this->grants=new stdClass(); - $this->grants->canManage = false; - - $this->guiOpWhiteList = array_flip(array('checkConnection','create','edit', - 'delete','doCreate', - 'doUpdate','doDelete')); - } - - /** - * - */ - function setTemplateCfg($cfg) { - $this->templateCfg = $cfg; - } - - /** - * - */ - function getGuiOpWhiteList() { - return $this->guiOpWhiteList; - } - - /** - * - * - */ - function initGuiBean(&$argsObj, $caller) { - $obj = new stdClass(); - $obj->action = $caller; - $obj->typeDomain = $this->issueTrackerMgr->getTypes(); - $obj->canManage = $argsObj->currentUser->hasRight($this->db,'issuetracker_management'); - $obj->user_feedback = array('type' => '', 'message' => ''); - - $obj->l18n = init_labels(array('issuetracker_management' => null, - 'btn_save' => null,'create' => null, - 'edit' => null, - 'checkConnection' => 'btn_check_connection', - 'issuetracker_deleted' => null)); - - // we experiment on way to get Action Description for GUI using __FUNCTION__ - $obj->l18n['doUpdate'] = $obj->l18n['edit']; - $obj->l18n['doCreate'] = $obj->l18n['create']; - $obj->l18n['doDelete'] = ''; - $obj->main_descr = $obj->l18n['issuetracker_management']; - $obj->action_descr = ucfirst($obj->l18n[$caller]); - - $obj->connectionStatus = ''; - - switch($caller) { - case 'delete': - case 'doDelete': - $obj->submit_button_label = ''; - break; - - default: - $obj->submit_button_label = $obj->l18n['btn_save']; - break; - } - - return $obj; - } - - /** - * - * - */ - function create(&$argsObj,$request,$caller=null) - { - $guiObj = $this->initGuiBean($argsObj,(is_null($caller) ? __FUNCTION__ : $caller)); - $templateCfg = templateConfiguration('issueTrackerEdit'); - $guiObj->template = $templateCfg->default_template; - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'issuetracker_management'); - - $guiObj->item = array('id' => 0); - $dummy = ''; - foreach($this->entitySpec as $property => $type) - { - $guiObj->item[$property] = ($type == 'int') ? 0 :''; - } - return $guiObj; - } - - /** - * - * - */ - function doCreate(&$argsObj,$request) - { - $guiObj = $this->create($argsObj,$request,__FUNCTION__); - - // Checks are centralized on create() - $it = new stdClass(); - foreach($this->entitySpec as $property => $type) - { - $it->$property = $argsObj->$property; - } - - // Save user input. - // This will be useful if create() will fail, to present values again on GUI - $guiObj->item = (array)$it; - - $op = $this->issueTrackerMgr->create($it); - if($op['status_ok']) - { - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->template = "issueTrackerView.php"; - } - else - { - $templateCfg = templateConfiguration('issueTrackerEdit'); - $guiObj->template=$templateCfg->default_template; - $guiObj->user_feedback['message'] = $op['msg']; - } - - return $guiObj; - } - - - - - /* - function: edit - - args: - - returns: - - */ - function edit(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - $templateCfg = templateConfiguration('issueTrackerEdit'); - $guiObj->template = $templateCfg->default_template; - - $guiObj->item = $this->issueTrackerMgr->getByID($argsObj->id); - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'issuetracker_management'); - return $guiObj; - } - - - /* - function: doUpdate - - args: - - returns: - - */ - function doUpdate(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - $it = new stdClass(); - $it->id = $argsObj->id; - foreach($this->entitySpec as $property => $type) - { - $it->$property = $argsObj->$property; - } - - // Save user input. - // This will be useful if create() will fail, to present values again on GUI - $guiObj->item = (array)$it; - - $op = $this->issueTrackerMgr->update($it); - if( $op['status_ok'] ) - { - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->template = "issueTrackerView.php"; - - if( isset($_SESSION['its'][$it->name]) ) - { - unset($_SESSION['its'][$it->name]); - } - } - else - { - $guiObj->user_feedback['message'] = $op['msg']; - $guiObj->template = null; - } - - return $guiObj; - } - - /** - * - * - */ - function doDelete(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - // get minimal info for user feedback before deleting - // $it = $this->issueTrackerMgr->getByID($argsObj->id); - $op = $this->issueTrackerMgr->delete($argsObj->id); - - // http://www.plus2net.com/php_tutorial/variables.php - //if($op['status_ok']) - //{ - // $msg = sprintf($this->guiObj->l18n['issuetracker_deleted'],$it['name']); - //} - //else - //{ - // $msg = $op['msg']; - //} - //$_SESSION['issueTrackerView.user_feedback'] = $msg; - - $guiObj->action = 'doDelete'; - $guiObj->template = "issueTrackerView.php?"; - - return $guiObj; - } - - - /** - * - */ - function checkConnection(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'issuetracker_management'); - - $tplCfg = templateConfiguration('issueTrackerEdit'); - $guiObj->template = $tplCfg->default_template; - - if( $argsObj->id > 0 ) - { - $ixx = $this->issueTrackerMgr->getByID($argsObj->id); - $guiObj->item['id'] = $ixx['id']; - } - else - { - $guiObj->operation = 'doCreate'; - $guiObj->item['id'] = 0; - } - - $guiObj->item['name'] = $argsObj->name; - $guiObj->item['type'] = $argsObj->type; - $guiObj->item['cfg'] = $argsObj->cfg; - $guiObj->item['implementation'] = - $this->issueTrackerMgr->getImplementationForType($argsObj->type); - - $class2create = $guiObj->item['implementation']; - - $its = new $class2create($argsObj->type,$argsObj->cfg,$argsObj->name); - $guiObj->connectionStatus = $its->isConnected() ? 'ok' : 'ko'; - - return $guiObj; - } - -} // end class +db = $dbHandler; + $this->issueTrackerMgr = new tlIssueTracker($dbHandler); + $this->entitySpec = $this->issueTrackerMgr->getEntitySpec(); + + $this->grants = new stdClass(); + $this->grants->canManage = false; + + $this->guiOpWhiteList = array_flip( + array( + 'checkConnection', + 'create', + 'edit', + 'delete', + 'doCreate', + 'doUpdate', + 'doDelete' + )); + } + + /** + */ + public function setTemplateCfg($cfg) + { + $this->templateCfg = $cfg; + } + + /** + */ + public function getGuiOpWhiteList() + { + return $this->guiOpWhiteList; + } + + /** + */ + public function initGuiBean(&$argsObj, $caller) + { + $obj = new stdClass(); + $obj->action = $caller; + $obj->typeDomain = $this->issueTrackerMgr->getTypes(); + $obj->canManage = $argsObj->currentUser->hasRight($this->db, + 'issuetracker_management'); + $obj->user_feedback = array( + 'type' => '', + 'message' => '' + ); + + $obj->l18n = init_labels( + array( + 'issuetracker_management' => null, + 'btn_save' => null, + 'create' => null, + 'edit' => null, + 'checkConnection' => 'btn_check_connection', + 'issuetracker_deleted' => null + )); + + // we experiment on way to get Action Description for GUI using __FUNCTION__ + $obj->l18n['doUpdate'] = $obj->l18n['edit']; + $obj->l18n['doCreate'] = $obj->l18n['create']; + $obj->l18n['doDelete'] = ''; + $obj->main_descr = $obj->l18n['issuetracker_management']; + $obj->action_descr = ucfirst($obj->l18n[$caller]); + + $obj->connectionStatus = ''; + + switch ($caller) { + case 'delete': + case 'doDelete': + $obj->submit_button_label = ''; + break; + + default: + $obj->submit_button_label = $obj->l18n['btn_save']; + break; + } + + return $obj; + } + + /** + */ + public function create(&$argsObj, $request, $caller = null) + { + $guiObj = $this->initGuiBean($argsObj, + (is_null($caller) ? __FUNCTION__ : $caller)); + $templateCfg = templateConfiguration('issueTrackerEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'issuetracker_management'); + + $guiObj->item = array( + 'id' => 0 + ); + foreach ($this->entitySpec as $property => $type) { + $guiObj->item[$property] = ($type == 'int') ? 0 : ''; + } + return $guiObj; + } + + /** + */ + public function doCreate(&$argsObj, $request) + { + $guiObj = $this->create($argsObj, $request, __FUNCTION__); + + // Checks are centralized on create() + $it = new stdClass(); + foreach ($this->entitySpec as $property => $type) { + $it->$property = $argsObj->$property; + } + + // Save user input. + // This will be useful if create() will fail, to present values again on GUI + $guiObj->item = (array) $it; + + $op = $this->issueTrackerMgr->create($it); + if ($op['status_ok']) { + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->template = "issueTrackerView.php"; + } else { + $templateCfg = templateConfiguration('issueTrackerEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->user_feedback['message'] = $op['msg']; + } + + return $guiObj; + } + + /* + * function: edit + * + * args: + * + * returns: + * + */ + public function edit(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $templateCfg = templateConfiguration('issueTrackerEdit'); + $guiObj->template = $templateCfg->default_template; + + $guiObj->item = $this->issueTrackerMgr->getByID($argsObj->id); + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'issuetracker_management'); + return $guiObj; + } + + /* + * function: doUpdate + * + * args: + * + * returns: + * + */ + public function doUpdate(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $it = new stdClass(); + $it->id = $argsObj->id; + foreach ($this->entitySpec as $property => $type) { + $it->$property = $argsObj->$property; + } + + // Save user input. + // This will be useful if create() will fail, to present values again on GUI + $guiObj->item = (array) $it; + + $op = $this->issueTrackerMgr->update($it); + if ($op['status_ok']) { + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->template = "issueTrackerView.php"; + + if (isset($_SESSION['its'][$it->name])) { + unset($_SESSION['its'][$it->name]); + } + } else { + $guiObj->user_feedback['message'] = $op['msg']; + $guiObj->template = null; + } + + return $guiObj; + } + + /** + */ + public function doDelete(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + // get minimal info for user feedback before deleting + // $it = $this->issueTrackerMgr->getByID($argsObj->id); + $this->issueTrackerMgr->delete($argsObj->id); + + // http://www.plus2net.com/php_tutorial/variables.php + // if($op['status_ok']) + // { + // $msg = sprintf($this->guiObj->l18n['issuetracker_deleted'],$it['name']); + // } + // else + // { + // $msg = $op['msg']; + // } + // $_SESSION['issueTrackerView.user_feedback'] = $msg; + + $guiObj->action = 'doDelete'; + $guiObj->template = "issueTrackerView.php?"; + + return $guiObj; + } + + /** + */ + public function checkConnection(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'issuetracker_management'); + + $tplCfg = templateConfiguration('issueTrackerEdit'); + $guiObj->template = $tplCfg->default_template; + + if ($argsObj->id > 0) { + $ixx = $this->issueTrackerMgr->getByID($argsObj->id); + $guiObj->item['id'] = $ixx['id']; + } else { + $guiObj->operation = 'doCreate'; + $guiObj->item['id'] = 0; + } + + $guiObj->item['name'] = $argsObj->name; + $guiObj->item['type'] = $argsObj->type; + $guiObj->item['cfg'] = $argsObj->cfg; + $guiObj->item['implementation'] = $this->issueTrackerMgr->getImplementationForType( + $argsObj->type); + + $class2create = $guiObj->item['implementation']; + + $its = new $class2create($argsObj->type, $argsObj->cfg, $argsObj->name); + $guiObj->connectionStatus = $its->isConnected() ? 'ok' : 'ko'; + + return $guiObj; + } +} // end class diff --git a/lib/issuetrackers/issueTrackerEdit.php b/lib/issuetrackers/issueTrackerEdit.php index 9f1541f1a3..9bb23d93c6 100644 --- a/lib/issuetrackers/issueTrackerEdit.php +++ b/lib/issuetrackers/issueTrackerEdit.php @@ -1,166 +1,217 @@ -doAction; -$op = null; -if(method_exists($commandMgr,$pFn)) { - $op = $commandMgr->$pFn($args,$_REQUEST); +doAction; +$op = null; +if (method_exists($commandMgr, $pFn)) { + $op = $commandMgr->$pFn($args, $_REQUEST); +} + +renderGui($args, $gui, $op, $templateCfg); + +/** + */ +function renderGui(&$argsObj, $guiObj, $opObj, $templateCfg) +{ + $smartyObj = new TLSmarty(); + $renderType = 'none'; + + // key: gui action + // value: next gui action (used to set value of action button on gui) + $actionOperation = array( + 'create' => 'doCreate', + 'edit' => 'doUpdate', + 'doDelete' => '', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate', + 'checkConnection' => 'doCreate' + ); + + if ($argsObj->id > 0) { + $actionOperation['checkConnection'] = 'doUpdate'; + } + + // Get rendering type and set variable for template + switch ($argsObj->doAction) { + case "edit": + case "create": + case "doDelete": + case "doCreate": + case "doUpdate": + case "checkConnection": + $key2loop = get_object_vars($opObj); + foreach ($key2loop as $key => $value) { + $guiObj->$key = $value; + } + $guiObj->operation = $actionOperation[$argsObj->doAction]; + + $renderType = 'redirect'; + $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; + $pos = strpos($tpl, '.php'); + if ($pos === false) { + $tplDir = (! isset($opObj->template_dir) || + is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; + $tpl = $tplDir . $tpl; + $renderType = 'template'; + } + break; + } + + // execute rendering + switch ($renderType) { + case 'template': + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($tpl); + break; + + case 'redirect': + header("Location: {$tpl}"); + exit(); + break; + + default: + break; + } +} + +/** + * Initializes the script + * + * @param database $dbHandler + * @return issueTrackerCommands[]|stdClass[]|issueTrackerCommands[] + */ +function initScript(&$dbHandler) +{ + $mgr = new issueTrackerCommands($dbHandler); + $args = initArgs(array( + 'doAction' => $mgr->getGuiOpWhiteList() + )); + $gui = initializeGui($dbHandler, $args, $mgr); + return array( + $args, + $gui, + $mgr + ); +} + +/** + * Get input from user and return it in some sort of namespace + * + * @param array $whiteList + * @return stdClass object returns the arguments for the page + */ +function initArgs($whiteList) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + + $iParams = array( + "id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + "name" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "cfg" => array( + tlInputParameter::STRING_N, + 0, + 2000 + ), + "type" => array( + tlInputParameter::INT_N + ) + ); + + R_PARAMS($iParams, $args); + + // sanitize via whitelist + foreach ($whiteList as $inputKey => $allowedValues) { + if (property_exists($args, $inputKey) && + ! isset($allowedValues[$args->$inputKey])) { + $msg = "Input parameter $inputKey - white list validation failure - " . + "Value:" . $args->$inputKey . " - " . "File: " . + basename(__FILE__) . " - Function: " . __FUNCTION__; + tLog($msg, 'ERROR'); + throw new Exception($msg); + } + } + + $args->currentUser = $_SESSION['currentUser']; + + return $args; +} + +/** + * Initializes the GUI + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param issueTrackerCommands $commandMgr + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj, &$commandMgr) +{ + $gui = new stdClass(); + $gui->main_descr = ''; + $gui->action_descr = ''; + $gui->user_feedback = array( + 'type' => '', + 'message' => '' + ); + $gui->mgt_view_events = $argsObj->currentUser->hasRight($dbHandler, + 'mgt_view_events'); + + // get affected test projects + $gui->testProjectSet = null; + if ($argsObj->id > 0) { + // just to fix erroneous test project delete + $dummy = $commandMgr->issueTrackerMgr->getLinks($argsObj->id, + array( + 'getDeadLinks' => true + )); + if (! is_null($dummy)) { + foreach ($dummy as $key => $elem) { + $commandMgr->issueTrackerMgr->unlink($argsObj->id, $key); + } + } + + // Now get good info + $gui->testProjectSet = $commandMgr->issueTrackerMgr->getLinks( + $argsObj->id); + } + return $gui; +} + +/** + * Checks the user rights for accessing the page + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'issuetracker_management'); } - -renderGui($db,$args,$gui,$op,$templateCfg); - - -/** - */ -function renderGui(&$dbHandler,&$argsObj,$guiObj,$opObj,$templateCfg) { - $smartyObj = new TLSmarty(); - $renderType = 'none'; - - // key: gui action - // value: next gui action (used to set value of action button on gui) - $actionOperation = array('create' => 'doCreate', 'edit' => 'doUpdate', - 'doDelete' => '', 'doCreate' => 'doCreate', - 'doUpdate' => 'doUpdate', - 'checkConnection' => 'doCreate'); - - if($argsObj->id > 0) { - $actionOperation['checkConnection'] = 'doUpdate'; - } - - // Get rendering type and set variable for template - switch($argsObj->doAction) { - case "edit": - case "create": - case "doDelete": - case "doCreate": - case "doUpdate": - case "checkConnection"; - $key2loop = get_object_vars($opObj); - foreach($key2loop as $key => $value) { - $guiObj->$key = $value; - } - $guiObj->operation = $actionOperation[$argsObj->doAction]; - - $renderType = 'redirect'; - $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; - $pos = strpos($tpl, '.php'); - if($pos === false) { - $tplDir = (!isset($opObj->template_dir) || is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; - $tpl = $tplDir . $tpl; - $renderType = 'template'; - } - break; - } - - // execute rendering - switch($renderType) { - case 'template': - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($tpl); - break; - - case 'redirect': - header("Location: {$tpl}"); - exit(); - break; - - default: - break; - } -} - -/** - * - */ -function initScript(&$dbHandler) { - $mgr = new issueTrackerCommands($dbHandler); - $args = init_args(array('doAction' => $mgr->getGuiOpWhiteList())); - $gui = initializeGui($dbHandler,$args,$mgr); - return array($args,$gui,$mgr); -} - -/** - * @return object returns the arguments for the page - */ -function init_args($whiteList) { - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - - $iParams = array("id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,20), - "name" => array(tlInputParameter::STRING_N,0,100), - "cfg" => array(tlInputParameter::STRING_N,0,2000), - "type" => array(tlInputParameter::INT_N)); - - R_PARAMS($iParams,$args); - - // sanitize via whitelist - foreach($whiteList as $inputKey => $allowedValues) { - if( property_exists($args,$inputKey) ) { - if( !isset($allowedValues[$args->$inputKey]) ) { - $msg = "Input parameter $inputKey - white list validation failure - " . - "Value:" . $args->$inputKey . " - " . - "File: " . basename(__FILE__) . " - Function: " . __FUNCTION__ ; - tLog($msg,'ERROR'); - throw new Exception($msg); - } - } - } - - $args->currentUser = $_SESSION['currentUser']; - - return $args; -} - - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,&$commandMgr) { - $gui = new stdClass(); - $gui->main_descr = ''; - $gui->action_descr = ''; - $gui->user_feedback = array('type' => '', 'message' => ''); - $gui->mgt_view_events = $argsObj->currentUser->hasRight($dbHandler,'mgt_view_events'); - - // get affected test projects - $gui->testProjectSet = null; - if($argsObj->id > 0) { - // just to fix erroneous test project delete - $dummy = $commandMgr->issueTrackerMgr->getLinks($argsObj->id,array('getDeadLinks' => true)); - if( !is_null($dummy) ) { - foreach($dummy as $key => $elem) { - $commandMgr->issueTrackerMgr->unlink($argsObj->id,$key); - } - } - - // Now get good info - $gui->testProjectSet = $commandMgr->issueTrackerMgr->getLinks($argsObj->id); - } - return $gui; -} - - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) { - return $user->hasRight($db,'issuetracker_management'); -} \ No newline at end of file diff --git a/lib/issuetrackers/issueTrackerView.php b/lib/issuetrackers/issueTrackerView.php index 899546eec2..e9638845e2 100644 --- a/lib/issuetrackers/issueTrackerView.php +++ b/lib/issuetrackers/issueTrackerView.php @@ -1,66 +1,82 @@ -items = $issueTrackerMgr->getAll(array('output' => 'add_link_count', 'checkEnv' => true)); -$gui->canManage = $args->currentUser->hasRight($db,"issuetracker_management"); -$gui->user_feedback = $args->user_feedback; - -if($args->id > 0) { - $gui->items[$args->id]['connection_status'] = $issueTrackerMgr->checkConnection($args->id) ? 'ok' : 'ko'; +items = $issueTrackerMgr->getAll( + array( + 'output' => 'add_link_count', + 'checkEnv' => true + )); +$gui->canManage = $args->currentUser->hasRight($db, "issuetracker_management"); +$gui->user_feedback = $args->user_feedback; + +if ($args->id > 0) { + $gui->items[$args->id]['connection_status'] = $issueTrackerMgr->checkConnection( + $args->id) ? 'ok' : 'ko'; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get input from user and return it in some sort of namespace + * + * @return stdClass $args object returns the arguments for the page + */ +function initArgs() +{ + $args = new stdClass(); + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + $args->currentUser = $_SESSION['currentUser']; + $args->user_feedback = array( + 'type' => '', + 'message' => '' + ); + + // only way I've found in order to give feedback for delete + // need to undertand if we really need/want to do all this mess + // $args->user_feedback = array('type' => '', 'message' => ''); + // if( isset($_SESSION['issueTrackerView.user_feedback']) ) + // { + // $args->user_feedback = array('type' => '', 'message' => $_SESSION['issueTrackerView.user_feedback']); + // unset($_SESSION['issueTrackerView.user_feedback']); + // } + + $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; + return $args; +} + +/** + * Checks the user rights for accessing the page + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "issuetracker_view") || + $user->hasRight($db, "issuetracker_management"); } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/** - * @return object returns the arguments for the page - */ -function init_args() { - $args = new stdClass(); - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - - $args->currentUser = $_SESSION['currentUser']; - - $args->user_feedback = array('type' => '', 'message' => ''); - - // only way I've found in order to give feedback for delete - // need to undertand if we really need/want to do all this mess - // $args->user_feedback = array('type' => '', 'message' => ''); - // if( isset($_SESSION['issueTrackerView.user_feedback']) ) - // { - // $args->user_feedback = array('type' => '', 'message' => $_SESSION['issueTrackerView.user_feedback']); - // unset($_SESSION['issueTrackerView.user_feedback']); - // } - - $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; - return $args; -} - -/** - * - */ -function checkRights(&$db,&$user) { - return $user->hasRight($db,"issuetracker_view") || $user->hasRight($db,"issuetracker_management"); -} \ No newline at end of file diff --git a/lib/keywords/keywordsAssign.php b/lib/keywords/keywordsAssign.php index 3b081bfe30..3373f9e11c 100644 --- a/lib/keywords/keywordsAssign.php +++ b/lib/keywords/keywordsAssign.php @@ -1,239 +1,297 @@ -js_ot_name = 'ot'; -$args = init_args($opt_cfg); - -$gui = initializeGui($args); - -if ($args->edit == 'testproject') { - // We can NOT assign/remove keywords on a whole test project - show_instructions('keywordsAssign'); - exit(); +js_ot_name = 'ot'; +$args = initArgs($opt_cfg); + +$gui = initializeGui($args); + +if ($args->edit == 'testproject') { + // We can NOT assign/remove keywords on a whole test project + show_instructions('keywordsAssign'); + exit(); +} + +$smarty = new TLSmarty(); +$tproject_mgr = new testproject($db); +$tcaseMgr = new testcase($db); + +$result = null; + +// Important Development Notice +// option transfer do the magic on GUI, +// analizing content of from->map and to->map, is able to populate +// each side as expected. +// +$opt_cfg->global_lbl = ''; +$opt_cfg->additional_global_lbl = null; +$opt_cfg->from->lbl = lang_get('available_kword'); +$opt_cfg->from->map = $tproject_mgr->get_keywords_map($args->testproject_id); + +switch ($args->edit) { + + case 'testsuite': + $opt_cfg->to->lbl = lang_get('target_kword'); + $opt_cfg->to->map = null; + + // We are going to walk all test suites contained + // in the selected container, and assign/remove keywords on each test case. + $tsuite_mgr = new testsuite($db); + $testsuite = $tsuite_mgr->get_by_id($args->id); + $gui->keyword_assignment_subtitle = lang_get('test_suite') . TITLE_SEP . + $testsuite['name']; + + if ($args->onlyDirectChildren) { + $tsChildren = $tsuite_mgr->get_children_testcases($args->id, + 'only_id'); + } else { + if ($args->useFilteredSet) { + $filteredTC = $args->tcaseSet; + } else { + $deepTC = $tsuite_mgr->get_testcases_deep($args->id, 'only_id'); + } + } + + if ($args->onlyDirectChildren && $args->useFilteredSet && + ! empty($tsChildren) && ! empty($filteredTC)) { + $tcs = array_intersect($tsChildren, $filteredTC); + } else { + if ($args->useFilteredSet && ! empty($filteredTC)) { + $tcs = &$filteredTC; + } elseif ($args->onlyDirectChildren) { + $tcs = &$tsChildren; + } else { + $tcs = &$deepTC; + } + } + + if ($loop2do = count($tcs)) { + $gui->can_do = 1; + + $method = null; + if ($args->assignToTestSuite && null != $args->keywordArray) { + $method = 'addKeywords'; + } + + if ($args->removeFromTestSuite && null != $args->keywordArray) { + $method = 'deleteKeywords'; + } + + if ($args->removeAllFromTestSuite) { + $method = 'deleteKeywords'; + } + + if (null != $method) { + $result = 'ok'; + $glOpt = array( + 'output' => 'thin', + 'active' => 1 + ); + + for ($idx = 0; $idx < $loop2do; $idx ++) { + $ltcv = $tcaseMgr->getLastVersionInfo($tcs[$idx], $glOpt); + $latestActiveVersionID = $ltcv['tcversion_id']; + $statusQuo = current( + $tcaseMgr->getVersionsStatusQuo($tcs[$idx], + $latestActiveVersionID)); + + $hasBeenExecuted = intval($statusQuo['executed']) > 0; + if ($gui->canAddRemoveKWFromExecuted || ! $hasBeenExecuted) { + $tcaseMgr->$method($tcs[$idx], $latestActiveVersionID, + $args->keywordArray); + } + } + } + } + break; + + case 'testcase': + $doRecall = true; + $gui->can_do = 1; + + $tcName = $tcaseMgr->getName($args->id); + $gui->keyword_assignment_subtitle = lang_get('test_case') . TITLE_SEP . + $tcName; + + // Now we work only on latest active version. + // We also need to check if has been executed + $glOpt = array( + 'output' => 'thin', + 'active' => 1 + ); + $ltcv = $tcaseMgr->getLastVersionInfo($args->id, $glOpt); + $latestActiveVersionID = $ltcv['tcversion_id']; + + $statusQuo = current( + $tcaseMgr->getVersionsStatusQuo($args->id, $latestActiveVersionID)); + $gui->hasBeenExecuted = intval($statusQuo['executed']) > 0; + + if ($gui->canAddRemoveKWFromExecuted || ! $gui->hasBeenExecuted) { + $kwQty = ! is_null($args->keywordArray) ? count($args->keywordArray) : 0; + if ($args->assignToTestCase && $kwQty > 0) { + $result = 'ok'; + $tcaseMgr->setKeywords($args->id, $latestActiveVersionID, + $args->keywordArray); + $doRecall = ! is_null($args->keywordArray); + } + } + + $opt_cfg->to->lbl = lang_get('assigned_kword'); + $opt_cfg->to->map = $doRecall ? $tcaseMgr->get_keywords_map($args->id, + $latestActiveVersionID, + array( + 'orderByClause' => " ORDER BY keyword ASC " + )) : null; + break; +} + +keywords_opt_transf_cfg($opt_cfg, $args->keywordList); + +$smarty->assign('gui', $gui); +$smarty->assign('sqlResult', $result); +$smarty->assign('opt_cfg', $opt_cfg); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get input from user and return it in some sort of namespace + * + * @param stdClass $opt_cfg + * @return stdClass object returns the arguments for the page + */ +function initArgs(&$opt_cfg) +{ + $rl_html_name = $opt_cfg->js_ot_name . "_newRight"; + + $iParams = array( + "id" => array( + tlInputParameter::INT_N + ), + "edit" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "assigntestcase" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "assigntestsuite" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "removetestcase" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "removetestsuite" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "removealltestcase" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "removealltestsuite" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + $rl_html_name => array( + tlInputParameter::STRING_N + ) + ); + + $args = new stdClass(); + $pParams = R_PARAMS($iParams, $args); + + $args->id = $pParams["id"]; + $args->edit = $pParams["edit"]; + + $args->assignToTestCase = ($pParams["assigntestcase"] != "") ? 1 : 0; + $args->assignToTestSuite = ($pParams["assigntestsuite"] != "") ? 1 : 0; + + $args->removeFromTestCase = ($pParams["removetestcase"] != "") ? 1 : 0; + $args->removeFromTestSuite = ($pParams["removetestsuite"] != "") ? 1 : 0; + + $args->removeAllFromTestCase = ($pParams["removealltestcase"] != "") ? 1 : 0; + $args->removeAllFromTestSuite = ($pParams["removealltestsuite"] != "") ? 1 : 0; + + $args->useFilteredSet = isset($_REQUEST['useFilteredSet']) ? 1 : 0; + $args->onlyDirectChildren = isset($_REQUEST['onlyDirectChildren']) ? 1 : 0; + + $args->testproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + $args->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $args->tcaseSet = isset($_SESSION['edit_mode']) && + isset($_SESSION['edit_mode'][$args->form_token]['testcases_to_show']) ? $_SESSION['edit_mode'][$args->form_token]['testcases_to_show'] : null; + + $args->keywordArray = null; + $args->keywordList = $pParams[$rl_html_name]; + if ($args->keywordList != "") { + $args->keywordArray = explode(",", $args->keywordList); + } + + $args->user = $_SESSION['currentUser']; + return $args; +} + +/** + * Initializes the GUI + * + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->can_do = 0; + $guiObj->form_token = $argsObj->form_token; + $guiObj->useFilteredSet = $argsObj->useFilteredSet; + $guiObj->onlyDirectChildren = $argsObj->onlyDirectChildren; + $guiObj->id = $argsObj->id; + $guiObj->level = $argsObj->edit; + $guiObj->keyword_assignment_subtitle = null; + + $guiObj->canAddRemoveKWFromExecuted = $argsObj->user->hasRightOnProj($db, + 'testproject_add_remove_keywords_executed_tcversions') || + $argsObj->user->hasRightOnProj($db, + 'testproject_edit_executed_testcases'); + + return $guiObj; +} + +/** + * Checks the user rights for accessing the page + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'keyword_assignment'); } - -$smarty = new TLSmarty(); -$tproject_mgr = new testproject($db); -$tcase_mgr = new testcase($db); - -$result = null; - -// Important Development Notice -// option transfer do the magic on GUI, -// analizing content of from->map and to->map, is able to populate -// each side as expected. -// -$opt_cfg->global_lbl = ''; -$opt_cfg->additional_global_lbl = null; -$opt_cfg->from->lbl = lang_get('available_kword'); -$opt_cfg->from->map = $tproject_mgr->get_keywords_map($args->testproject_id); - -switch($args->edit) { - - case 'testsuite': - $opt_cfg->to->lbl = lang_get('target_kword'); - $opt_cfg->to->map = null; - - // We are going to walk all test suites contained - // in the selected container, and assign/remove keywords on each test case. - $tsuite_mgr = new testsuite($db); - $testsuite = $tsuite_mgr->get_by_id($args->id); - $gui->keyword_assignment_subtitle = lang_get('test_suite') . TITLE_SEP . $testsuite['name']; - - if ($args->onlyDirectChildren) { - $tsChildren = $tsuite_mgr->get_children_testcases($args->id,'only_id'); - } else { - if($args->useFilteredSet) { - $filteredTC = $args->tcaseSet; - } else { - $deepTC = $tsuite_mgr->get_testcases_deep($args->id,'only_id'); - } - } - - if ($args->onlyDirectChildren && $args->useFilteredSet) { - // intersect - $tcs = array_intersect($tsChildren, $filteredTC); - } else { - if ($args->useFilteredSet) { - $tcs = &$filteredTC; - } else if ($args->onlyDirectChildren) { - $tcs = &$tsChildren; - } else { - $tcs = &$deepTC; - } - } - - if( ($loop2do = sizeof($tcs)) ) { - $gui->can_do = 1; - - $method = null; - if ($args->assignToTestSuite && null != $args->keywordArray) { - $method = 'addKeywords'; - } - - if ($args->removeFromTestSuite && null != $args->keywordArray) { - $method = 'deleteKeywords'; - } - - if ($args->removeAllFromTestSuite) { - $method = 'deleteKeywords'; - } - - if (null != $method) { - $result = 'ok'; - $glOpt = array('output' => 'thin', 'active' => 1); - - for($idx = 0; $idx < $loop2do; $idx++) { - $ltcv = $tcase_mgr->get_last_version_info($tcs[$idx],$glOpt); - $latestActiveVersionID = $ltcv['tcversion_id']; - $statusQuo = current($tcase_mgr->get_versions_status_quo($tcs[$idx],$latestActiveVersionID)); - - $hasBeenExecuted = intval($statusQuo['executed']) > 0; - if( $gui->canAddRemoveKWFromExecuted || - $hasBeenExecuted == false ) { - $tcase_mgr->$method($tcs[$idx],$latestActiveVersionID,$args->keywordArray); - } - } - } - } - break; - - - case 'testcase': - $doRecall = true; - $gui->can_do = 1; - - $tcName = $tcase_mgr->getName($args->id); - $gui->keyword_assignment_subtitle = lang_get('test_case') . TITLE_SEP . - $tcName; - - // Now we work only on latest active version. - // We also need to check if has been executed - $glOpt = array('output' => 'thin', 'active' => 1); - $ltcv = $tcase_mgr->get_last_version_info($args->id,$glOpt); - $latestActiveVersionID = $ltcv['tcversion_id']; - - $statusQuo = current($tcase_mgr->get_versions_status_quo($args->id,$latestActiveVersionID)); - $gui->hasBeenExecuted = intval($statusQuo['executed']) > 0; - - if ($gui->canAddRemoveKWFromExecuted || !$gui->hasBeenExecuted) { - $kwQty = !is_null($args->keywordArray) ? count($args->keywordArray) : 0; - if ($args->assignToTestCase && $kwQty >0) { - $result = 'ok'; - $tcase_mgr->setKeywords($args->id,$latestActiveVersionID,$args->keywordArray); - $doRecall = !is_null($args->keywordArray); - } - } - - $opt_cfg->to->lbl = lang_get('assigned_kword'); - $opt_cfg->to->map = $doRecall ? - $tcase_mgr->get_keywords_map($args->id,$latestActiveVersionID, - array('orderByClause' =>" ORDER BY keyword ASC ")) : null; - break; -} - - -keywords_opt_transf_cfg($opt_cfg, $args->keywordList); - - -$smarty->assign('gui', $gui); -$smarty->assign('sqlResult', $result); -$smarty->assign('opt_cfg', $opt_cfg); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - */ -function init_args(&$opt_cfg) { - $rl_html_name = $opt_cfg->js_ot_name . "_newRight"; - - $iParams = array("id" => array(tlInputParameter::INT_N), - "edit" => array(tlInputParameter::STRING_N,0,100), - "assigntestcase" => array(tlInputParameter::STRING_N,0,1), - "assigntestsuite" => array(tlInputParameter::STRING_N,0,1), - "removetestcase" => array(tlInputParameter::STRING_N,0,1), - "removetestsuite" => array(tlInputParameter::STRING_N,0,1), - "removealltestcase" => array(tlInputParameter::STRING_N,0,1), - "removealltestsuite" => array(tlInputParameter::STRING_N,0,1), - $rl_html_name => array(tlInputParameter::STRING_N) ); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - - $args->id = $pParams["id"]; - $args->edit = $pParams["edit"]; - - $args->assignToTestCase = ($pParams["assigntestcase"] != "") ? 1 : 0; - $args->assignToTestSuite = ($pParams["assigntestsuite"] != "") ? 1 : 0; - - $args->removeFromTestCase = ($pParams["removetestcase"] != "") ? 1 : 0; - $args->removeFromTestSuite = ($pParams["removetestsuite"] != "") ? 1 : 0; - - $args->removeAllFromTestCase = ($pParams["removealltestcase"] != "") ? 1 : 0; - $args->removeAllFromTestSuite = ($pParams["removealltestsuite"] != "") ? 1 : 0; - - $args->useFilteredSet = isset($_REQUEST['useFilteredSet']) ? 1 : 0; - $args->onlyDirectChildren = isset($_REQUEST['onlyDirectChildren']) ? 1 : 0; - - $args->testproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - - $args->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $args->tcaseSet = isset($_SESSION['edit_mode']) - && isset($_SESSION['edit_mode'][$args->form_token]['testcases_to_show']) ? - $_SESSION['edit_mode'][$args->form_token]['testcases_to_show'] : null; - - - $args->keywordArray = null; - $args->keywordList = $pParams[$rl_html_name]; - if ($args->keywordList != "") { - $args->keywordArray = explode(",",$args->keywordList); - } - - $args->user = $_SESSION['currentUser']; - return $args; -} - -/** - * - */ -function initializeGui(&$argsObj) { - $guiObj = new stdClass(); - $guiObj->can_do = 0; - $guiObj->form_token = $argsObj->form_token; - $guiObj->useFilteredSet = $argsObj->useFilteredSet; - $guiObj->onlyDirectChildren = $argsObj->onlyDirectChildren; - $guiObj->id = $argsObj->id; - $guiObj->level = $argsObj->edit; - $guiObj->keyword_assignment_subtitle = null; - - $guiObj->canAddRemoveKWFromExecuted = - $argsObj->user->hasRight($db, - 'testproject_add_remove_keywords_executed_tcversions') || - $argsObj->user->hasRight($db,'testproject_edit_executed_testcases'); - - return $guiObj; -} - - -function checkRights(&$db,&$user) { - return $user->hasRight($db,'keyword_assignment'); -} \ No newline at end of file diff --git a/lib/keywords/keywordsEdit.php b/lib/keywords/keywordsEdit.php index 42ea19afd7..f188e1fdc2 100644 --- a/lib/keywords/keywordsEdit.php +++ b/lib/keywords/keywordsEdit.php @@ -1,345 +1,426 @@ -status = 0; - -$args = initEnv($db); -$gui = initializeGui($db,$args); - -$tprojectMgr = new testproject($db); - -$action = $args->doAction; - -switch ($action) { - case "do_create": - case "do_update": - case "do_delete": - case "edit": - case "create": - case "cfl": - case "do_cfl": - $op = $action($args,$gui,$tprojectMgr); - break; -} - - -if($op->status == 1) { - $tpl = $op->template; -} else { - $tpl = $tplCfg->default_template; - $gui->user_feedback = getKeywordErrorMessage($op->status); -} - -$gui->keywords = null; -$gui->submitCode = ""; -if ($tpl != $tplCfg->default_template) { - // I'm going to return to screen that display all keywords - $kwe = getKeywordsEnv($db,$args->user,$args->tproject_id); - foreach($kwe as $prop => $val) { - $gui->$prop = $val; - } - $setUpDialog = $gui->openByOther; -} else { - $setUpDialog = $gui->directAccess; - $gui->submitCode="return dialog_onSubmit($gui->dialogName)"; -} - -if( $setUpDialog ) { - $gui->dialogName = 'kw_dialog'; - $gui->bodyOnLoad = "dialog_onLoad($gui->dialogName)"; - $gui->bodyOnUnload = "dialog_onUnload($gui->dialogName)"; - - if( $gui->directAccess ) { - $gui->submitCode = "return dialog_onSubmit($gui->dialogName)"; - } -} - -$tplEngine->assign('gui',$gui); -$tplEngine->display($tplCfg->template_dir . $tpl); - - -/** - * @return object returns the arguments for the page - */ -function initEnv(&$dbHandler) { - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - $source = sizeof($_POST) ? "POST" : "GET"; - - $ipcfg = - array( "doAction" => array($source,tlInputParameter::STRING_N,0,50), - "id" => array($source, tlInputParameter::INT_N), - "keyword" => array($source, tlInputParameter::STRING_N,0,100), - "notes" => array($source, tlInputParameter::STRING_N), - "tproject_id" => array($source, tlInputParameter::INT_N), - "openByOther" => array($source, tlInputParameter::INT_N), - "directAccess" => array($source, tlInputParameter::INT_N), - "tcversion_id" => array($source, tlInputParameter::INT_N)); - - $ip = I_PARAMS($ipcfg); - - $args = new stdClass(); - $args->doAction = $ip["doAction"]; - $args->notes = $ip["notes"]; - $args->keyword = $ip["keyword"]; - $args->keyword_id = $ip["id"]; - $args->tproject_id = $ip["tproject_id"]; - $args->openByOther = intval($ip["openByOther"]); - $args->directAccess = intval($ip["directAccess"]); - $args->tcversion_id = intval($ip["tcversion_id"]); - - - if( $args->tproject_id <= 0 ) { - throw new Exception("Error Invalid Test Project ID", 1); - } - - // Check rights before doing anything else - // Abort if rights are not enough - $args->user = $_SESSION['currentUser']; - $env['tproject_id'] = $args->tproject_id; - $env['tplan_id'] = 0; - - $check = new stdClass(); - $check->items = array('mgt_modify_key','mgt_view_key'); - $check->mode = 'and'; - checkAccess($dbHandler,$args->user,$env,$check); - - // OK Go ahead - $args->canManage = true; - $args->mgt_view_events = $args->user->hasRight($dbHandler,"mgt_view_events",$args->tproject_id); - - $treeMgr = new tree($dbHandler); - $dummy = $treeMgr->get_node_hierarchy_info($args->tproject_id); - $args->tproject_name = $dummy['name']; - - return $args; -} - -/* - * initialize variables to launch user interface (smarty template) - * to get information to accomplish create task. -*/ -function create(&$argsObj,&$guiObj) { - $guiObj->submit_button_action = 'do_create'; - $guiObj->submit_button_label = lang_get('btn_save'); - $guiObj->main_descr = lang_get('keyword_management'); - $guiObj->action_descr = lang_get('create_keyword'); - - $ret = new stdClass(); - $ret->template = 'keywordsEdit.tpl'; - $ret->status = 1; - return $ret; -} - -/* - * initialize variables to launch user interface (smarty template) - * to get information to accomplish edit task. -*/ -function edit(&$argsObj,&$guiObj,&$tproject_mgr) { - $guiObj->submit_button_action = 'do_update'; - $guiObj->submit_button_label = lang_get('btn_save'); - $guiObj->main_descr = lang_get('keyword_management'); - $guiObj->action_descr = lang_get('edit_keyword'); - - $ret = new stdClass(); - $ret->template = 'keywordsEdit.tpl'; - $ret->status = 1; - - $keyword = $tproject_mgr->getKeyword($argsObj->keyword_id); - if ($keyword) { - $guiObj->keyword = $argsObj->keyword = $keyword->name; - $guiObj->notes = $argsObj->notes = $keyword->notes; - $guiObj->action_descr .= TITLE_SEP . $guiObj->keyword; - } - - return $ret; -} - -/* - * Creates the keyword - */ -function do_create(&$args,&$guiObj,&$tproject_mgr) { - $guiObj->submit_button_action = 'do_create'; - $guiObj->submit_button_label = lang_get('btn_save'); - $guiObj->main_descr = lang_get('keyword_management'); - $guiObj->action_descr = lang_get('create_keyword'); - - $op = $tproject_mgr->addKeyword($args->tproject_id,$args->keyword,$args->notes); - $ret = new stdClass(); - $ret->template = 'keywordsView.tpl'; - $ret->status = $op['status']; - return $ret; -} - -/* - * Updates the keyword - */ -function do_update(&$argsObj,&$guiObj,&$tproject_mgr) { - $guiObj->submit_button_action = 'do_update'; - $guiObj->submit_button_label = lang_get('btn_save'); - $guiObj->main_descr = lang_get('keyword_management'); - $guiObj->action_descr = lang_get('edit_keyword'); - - $keyword = $tproject_mgr->getKeyword($argsObj->keyword_id); - if ($keyword) { - $guiObj->action_descr .= TITLE_SEP . $keyword->name; - } - - $ret = new stdClass(); - $ret->template = 'keywordsView.tpl'; - $ret->status = $tproject_mgr->updateKeyword($argsObj->tproject_id, - $argsObj->keyword_id,$argsObj->keyword,$argsObj->notes); - return $ret; -} - -/* - * Deletes the keyword - */ -function do_delete(&$args,&$guiObj,&$tproject_mgr) { - $guiObj->submit_button_action = 'do_update'; - $guiObj->submit_button_label = lang_get('btn_save'); - $guiObj->main_descr = lang_get('keyword_management'); - $guiObj->action_descr = lang_get('delete_keyword'); - - $ret = new stdClass(); - $ret->template = 'keywordsView.tpl'; - - $dko = array('context' => 'getTestProjectName', - 'tproject_id' => $args->tproject_id); - $ret->status = $tproject_mgr->deleteKeyword($args->keyword_id,$dko); - - return $ret; -} - -/* - * initialize variables to launch user interface (smarty template) - * to get information to accomplish create task. -*/ -function cfl(&$argsObj,&$guiObj) { - $guiObj->submit_button_action = 'do_cfl'; - $guiObj->submit_button_label = lang_get('btn_create_and_link'); - $guiObj->main_descr = lang_get('keyword_management'); - $guiObj->action_descr = lang_get('create_keyword_and_link'); - - $ret = new stdClass(); - $ret->template = 'keywordsEdit.tpl'; - $ret->status = 1; - return $ret; -} - -/* - * Creates the keyword - */ -function do_cfl(&$args,&$guiObj,&$tproject_mgr) { - $guiObj->submit_button_action = 'do_cfl'; - $guiObj->submit_button_label = lang_get('btn_save'); - $guiObj->main_descr = lang_get('keyword_management'); - $guiObj->action_descr = lang_get('create_keyword'); - - $op = $tproject_mgr->addKeyword($args->tproject_id,$args->keyword,$args->notes); - - if( $op['status'] ) { - $tcaseMgr = new testcase($tproject_mgr->db); - $tbl = tlObject::getDBTables('nodes_hierarchy'); - $sql = "SELECT parent_id FROM {$tbl['nodes_hierarchy']} - WHERE id=" . intval($args->tcversion_id); - $rs = $tproject_mgr->db->get_recordset($sql); - $tcase_id = intval($rs[0]['parent_id']); - $tcaseMgr->addKeywords($tcase_id,$args->tcversion_id, - array($op['id'])); - } - - $ret = new stdClass(); - $ret->template = 'keywordsView.tpl'; - $ret->status = $op['status']; - return $ret; -} - - - -/** - * - */ -function getKeywordErrorMessage($code) { - - switch($code) { - case tlKeyword::E_NAMENOTALLOWED: - $msg = lang_get('keywords_char_not_allowed'); - break; - - case tlKeyword::E_NAMELENGTH: - $msg = lang_get('empty_keyword_no'); - break; - - case tlKeyword::E_DBERROR: - case ERROR: - $msg = lang_get('kw_update_fails'); - break; - - case tlKeyword::E_NAMEALREADYEXISTS: - $msg = lang_get('keyword_already_exists'); - break; - - default: - $msg = 'ok'; - } - return $msg; -} - -/** - * - * - */ -function initializeGui(&$dbH,&$args) { - - $gui = new stdClass(); - $gui->openByOther = $args->openByOther; - $gui->directAccess = $args->directAccess; - $gui->tcversion_id = $args->tcversion_id; - - $gui->user_feedback = ''; - - // Needed by the smarty template to be launched - $kr = array('canManage' => "mgt_modify_key", 'canAssign' => "keyword_assignment"); - foreach( $kr as $vk => $rk ) { - $gui->$vk = - $args->user->hasRight($dbH,$rk,$args->tproject_id); - } - - $gui->tproject_id = $args->tproject_id; - $gui->canManage = $args->canManage; - $gui->mgt_view_events = $args->mgt_view_events; - $gui->notes = $args->notes; - $gui->name = $args->keyword; - $gui->keyword = $args->keyword; - $gui->keywordID = $args->keyword_id; - - $gui->editUrl = $_SESSION['basehref'] . "lib/keywords/keywordsEdit.php?" . - "tproject_id={$gui->tproject_id}"; - - return $gui; +status = 0; + +$args = initEnv($db); +$gui = initializeGui($db, $args); + +$tprojectMgr = new testproject($db); + +$action = $args->doAction; + +switch ($action) { + case "do_create": + case "do_update": + case "do_delete": + case "edit": + case "create": + case "cfl": + case "do_cfl": + $op = $action($args, $gui, $tprojectMgr); + break; +} + +if ($op->status == 1) { + $tpl = $op->template; +} else { + $tpl = (property_exists($op, 'template') && null != $op->template) ? $op->template : $tplCfg->default_template; + $gui->user_feedback = getKeywordErrorMessage($op->status); +} + +$gui->keywords = null; +$gui->submitCode = ""; +if ($tpl != $tplCfg->default_template) { + // I'm going to return to screen that display all keywords + $kwe = getKeywordsEnv($db, $args->user, $args->tproject_id); + foreach ($kwe as $prop => $val) { + $gui->$prop = $val; + } + $setUpDialog = $gui->openByOther; +} else { + $setUpDialog = $gui->directAccess; + $gui->submitCode = "return dialog_onSubmit($gui->dialogName)"; +} + +if ($setUpDialog) { + $gui->dialogName = 'kw_dialog'; + $gui->bodyOnLoad = "dialog_onLoad($gui->dialogName)"; + $gui->bodyOnUnload = "dialog_onUnload($gui->dialogName)"; + + if ($gui->directAccess) { + $gui->submitCode = "return dialog_onSubmit($gui->dialogName)"; + } +} + +$tplEngine->assign('gui', $gui); +$tplEngine->display($tplCfg->template_dir . $tpl); + +/** + * Initializes the environment + * + * @param database $dbHandler + * @return stdClass object returns the arguments for the page + */ +function initEnv(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $source = count($_POST) ? "POST" : "GET"; + + $ipcfg = array( + "doAction" => array( + $source, + tlInputParameter::STRING_N, + 0, + 50 + ), + "id" => array( + $source, + tlInputParameter::INT_N + ), + "keyword" => array( + $source, + tlInputParameter::STRING_N, + 0, + 100 + ), + "notes" => array( + $source, + tlInputParameter::STRING_N + ), + "tproject_id" => array( + $source, + tlInputParameter::INT_N + ), + "openByOther" => array( + $source, + tlInputParameter::INT_N + ), + "directAccess" => array( + $source, + tlInputParameter::INT_N + ), + "tcversion_id" => array( + $source, + tlInputParameter::INT_N + ) + ); + + $ip = I_PARAMS($ipcfg); + + $args = new stdClass(); + $args->doAction = $ip["doAction"]; + $args->notes = $ip["notes"]; + $args->keyword = $ip["keyword"]; + $args->keyword_id = $ip["id"]; + $args->tproject_id = $ip["tproject_id"]; + $args->openByOther = intval($ip["openByOther"]); + $args->directAccess = intval($ip["directAccess"]); + $args->tcversion_id = intval($ip["tcversion_id"]); + + if ($args->tproject_id <= 0) { + throw new Exception("Error Invalid Test Project ID", 1); + } + + // Check rights before doing anything else + // Abort if rights are not enough + $args->user = $_SESSION['currentUser']; + $env['tproject_id'] = $args->tproject_id; + $env['tplan_id'] = 0; + + $check = new stdClass(); + $check->items = array( + 'mgt_modify_key', + 'mgt_view_key' + ); + $check->mode = 'and'; + checkAccess($dbHandler, $args->user, $env, $check); + + // OK Go ahead + $args->canManage = true; + $args->mgt_view_events = $args->user->hasRight($dbHandler, "mgt_view_events", + $args->tproject_id); + + $treeMgr = new tree($dbHandler); + $dummy = $treeMgr->get_node_hierarchy_info($args->tproject_id); + $args->tproject_name = $dummy['name']; + + return $args; +} + +/** + * initialize variables to launch user interface (smarty template) + * to get information to accomplish create task. + * + * @param stdClass $argsObj + * @param stdClass $guiObj + * @return stdClass + */ +function create(&$argsObj, &$guiObj) +{ + $guiObj->submit_button_action = 'do_create'; + $guiObj->submit_button_label = lang_get('btn_save'); + $guiObj->main_descr = lang_get('keyword_management'); + $guiObj->action_descr = lang_get('create_keyword'); + + $ret = new stdClass(); + $ret->template = 'keywordsEdit.tpl'; + $ret->status = 1; + return $ret; +} + +/** + * + * @param stdClass $argsObj + * @param stdClass $guiObj + * @param testproject $tproject_mgr + * @return stdClass + */ +function edit(&$argsObj, &$guiObj, &$tproject_mgr) +{ + $guiObj->submit_button_action = 'do_update'; + $guiObj->submit_button_label = lang_get('btn_save'); + $guiObj->main_descr = lang_get('keyword_management'); + $guiObj->action_descr = lang_get('edit_keyword'); + + $ret = new stdClass(); + $ret->template = 'keywordsEdit.tpl'; + $ret->status = 1; + + $keyword = $tproject_mgr->getKeyword($argsObj->keyword_id); + if ($keyword) { + $guiObj->keyword = $argsObj->keyword = $keyword->name; + $guiObj->notes = $argsObj->notes = $keyword->notes; + $guiObj->action_descr .= TITLE_SEP . $guiObj->keyword; + } + + return $ret; +} + +/** + * Creates the keyword + * + * @param stdClass $args + * @param stdClass $guiObj + * @param testproject $tproject_mgr + * @return stdClass + */ +function do_create(&$args, &$guiObj, &$tproject_mgr) +{ + $guiObj->submit_button_action = 'do_create'; + $guiObj->submit_button_label = lang_get('btn_save'); + $guiObj->main_descr = lang_get('keyword_management'); + $guiObj->action_descr = lang_get('create_keyword'); + + $op = $tproject_mgr->addKeyword($args->tproject_id, $args->keyword, + $args->notes); + $ret = new stdClass(); + $ret->template = 'keywordsView.tpl'; + $ret->status = $op['status']; + return $ret; +} + +/** + * Updates the keyword + * + * @param stdClass $argsObj + * @param stdClass $guiObj + * @param testproject $tproject_mgr + * @return stdClass + */ +function do_update(&$argsObj, &$guiObj, &$tproject_mgr) +{ + $guiObj->submit_button_action = 'do_update'; + $guiObj->submit_button_label = lang_get('btn_save'); + $guiObj->main_descr = lang_get('keyword_management'); + $guiObj->action_descr = lang_get('edit_keyword'); + + $keyword = $tproject_mgr->getKeyword($argsObj->keyword_id); + if ($keyword) { + $guiObj->action_descr .= TITLE_SEP . $keyword->name; + } + + $ret = new stdClass(); + $ret->template = 'keywordsView.tpl'; + $ret->status = $tproject_mgr->updateKeyword($argsObj->tproject_id, + $argsObj->keyword_id, $argsObj->keyword, $argsObj->notes); + return $ret; +} + +/** + * Deletes the keyword + * + * @param stdClass $args + * @param stdClass $guiObj + * @param testproject $tproject_mgr + * @return stdClass + */ +function do_delete(&$args, &$guiObj, &$tproject_mgr) +{ + $guiObj->submit_button_action = 'do_update'; + $guiObj->submit_button_label = lang_get('btn_save'); + $guiObj->main_descr = lang_get('keyword_management'); + $guiObj->action_descr = lang_get('delete_keyword'); + + $ret = new stdClass(); + $ret->template = 'keywordsView.tpl'; + + $dko = array( + 'context' => 'getTestProjectName', + 'tproject_id' => $args->tproject_id + ); + $ret->status = $tproject_mgr->deleteKeyword($args->keyword_id, $dko); + + return $ret; +} + +/** + * initialize variables to launch user interface (smarty template) + * to get information to accomplish create and link task. + * + * @param stdClass $argsObj + * @param stdClass $guiObj + * @return stdClass + */ +function cfl(&$argsObj, &$guiObj) +{ + $guiObj->submit_button_action = 'do_cfl'; + $guiObj->submit_button_label = lang_get('btn_create_and_link'); + $guiObj->main_descr = lang_get('keyword_management'); + $guiObj->action_descr = lang_get('create_keyword_and_link'); + + $ret = new stdClass(); + $ret->template = 'keywordsEdit.tpl'; + $ret->status = 1; + return $ret; +} + +/** + * Creates & Link the keyword + * + * @param stdClass $args + * @param stdClass $guiObj + * @param testproject $tproject_mgr + * @return stdClass + */ +function do_cfl(&$args, &$guiObj, &$tproject_mgr) +{ + $guiObj->submit_button_action = 'do_cfl'; + $guiObj->submit_button_label = lang_get('btn_create_and_link'); + $guiObj->main_descr = lang_get('keyword_management'); + $guiObj->action_descr = lang_get('create_keyword_and_link'); + + $op = $tproject_mgr->addKeyword($args->tproject_id, $args->keyword, + $args->notes); + + $ret = new stdClass(); + if ($op['status'] >= tl::OK) { + $ret->template = 'keywordsView.tpl'; + $tcaseMgr = new testcase($tproject_mgr->db); + $tbl = tlObject::getDBTables('nodes_hierarchy'); + $sql = "SELECT parent_id FROM {$tbl['nodes_hierarchy']} + WHERE id=" . intval($args->tcversion_id); + $rs = $tproject_mgr->db->get_recordset($sql); + $tcase_id = intval($rs[0]['parent_id']); + $tcaseMgr->addKeywords($tcase_id, $args->tcversion_id, + array( + $op['id'] + )); + } + $ret->status = $op['status']; + return $ret; +} + +/** + * Get the error message + * + * @param string $code + * the local language + * @return string of the error message + */ +function getKeywordErrorMessage($code) +{ + switch ($code) { + case tlKeyword::E_NAMENOTALLOWED: + $msg = lang_get('keywords_char_not_allowed'); + break; + + case tlKeyword::E_NAMELENGTH: + $msg = lang_get('empty_keyword_no'); + break; + + case tlKeyword::E_DBERROR: + case ERROR: + $msg = lang_get('kw_update_fails'); + break; + + case tlKeyword::E_NAMEALREADYEXISTS: + $msg = lang_get('keyword_already_exists'); + break; + + default: + $msg = 'ok'; + break; + } + return $msg; +} + +/** + */ +function initializeGui(&$dbH, &$args) +{ + $gui = new stdClass(); + $gui->dialogName = 'keywordsEdit_dialog'; + $gui->openByOther = $args->openByOther; + $gui->directAccess = $args->directAccess; + $gui->tcversion_id = $args->tcversion_id; + + $gui->user_feedback = ''; + + // Needed by the smarty template to be launched + $kr = array( + 'canManage' => "mgt_modify_key", + 'canAssign' => "keyword_assignment" + ); + foreach ($kr as $vk => $rk) { + $gui->$vk = $args->user->hasRight($dbH, $rk, $args->tproject_id); + } + + $gui->tproject_id = $args->tproject_id; + $gui->canManage = $args->canManage; + $gui->mgt_view_events = $args->mgt_view_events; + $gui->notes = $args->notes; + $gui->name = $args->keyword; + $gui->keyword = $args->keyword; + $gui->keywordID = $args->keyword_id; + + $gui->editUrl = $_SESSION['basehref'] . "lib/keywords/keywordsEdit.php?" . + "tproject_id={$gui->tproject_id}"; + + return $gui; } diff --git a/lib/keywords/keywordsEnv.php b/lib/keywords/keywordsEnv.php index 08ff28b0d8..94a16f90fc 100644 --- a/lib/keywords/keywordsEnv.php +++ b/lib/keywords/keywordsEnv.php @@ -1,11 +1,11 @@ dbID; if( $more ) { $kwNames[$kwo->dbID] = $kwo->name; - $kwNotes[$kwo->dbID] = $kwo->notes; + $kwNotes[$kwo->dbID] = $kwo->notes; } } @@ -42,20 +42,18 @@ function getKeywordsEnv(&$dbHandler,&$user,$tproject_id,$opt=null) { if( $more && count($kwEnv->kwOnTCV) > 0) { foreach($kwEnv->kwOnTCV as $kk => $dummy) { $kwEnv->kwOnTCV[$kk]['keyword'] = $kwNames[$kk]; - $kwEnv->kwOnTCV[$kk]['notes'] = $kwNotes[$kk]; + $kwEnv->kwOnTCV[$kk]['notes'] = $kwNotes[$kk]; } } $kwCfg = config_get('keywords'); if( $kwCfg->onDeleteCheckExecutedTCVersions ) { - $kwEnv->kwExecStatus = - $tproject->getKeywordsExecStatus($kws,$tproject_id); + $kwEnv->kwExecStatus = $tproject->getKeywordsExecStatus($kws,$tproject_id); } if( $kwCfg->onDeleteCheckFrozenTCVersions ) { - $kwEnv->kwFreshStatus = - $tproject->getKeywordsFreezeStatus($kws,$tproject_id); + $kwEnv->kwFreshStatus = $tproject->getKeywordsFreezeStatus($kws,$tproject_id); } } @@ -64,6 +62,6 @@ function getKeywordsEnv(&$dbHandler,&$user,$tproject_id,$opt=null) { $kwEnv->canAssign = $user->hasRight($dbHandler,"keyword_assignment",$tproject_id); $kwEnv->editUrl = $_SESSION['basehref'] . "lib/keywords/keywordsEdit.php?" . - "tproject_id={$tproject_id}"; + "tproject_id={$tproject_id}"; return $kwEnv; -} \ No newline at end of file +} diff --git a/lib/keywords/keywordsExport.php b/lib/keywords/keywordsExport.php index 6f7fca6202..0bc13da84f 100644 --- a/lib/keywords/keywordsExport.php +++ b/lib/keywords/keywordsExport.php @@ -1,134 +1,175 @@ -doAction) { - case "do_export": - $op = do_export($db,$smarty,$args); - break; +doAction) { + case "do_export": + do_export($db, $smarty, $args); + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get input from user and return it in some sort of namespace + * + * @param database $dbHandler + * @return stdClass object returns the arguments for the page + */ +function initArgs(&$dbHandler) +{ + $ipcfg = array( + "doAction" => array( + "GET", + tlInputParameter::STRING_N, + 0, + 50 + ), + "tproject_id" => array( + "GET", + tlInputParameter::INT_N + ), + "export_filename" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 255 + ), + "exportType" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 255 + ) + ); + + $args = new stdClass(); + I_PARAMS($ipcfg, $args); + + if ($args->tproject_id <= 0) { + throw new Exception("Error Invalid Test Project ID", 1); + } + + // Check rights before doing anything else + // Abort if rights are not enough + $args->user = $_SESSION['currentUser']; + $env['tproject_id'] = $args->tproject_id; + $env['tplan_id'] = 0; + + $check = new stdClass(); + $check->items = array( + 'mgt_view_key' + ); + $check->mode = 'and'; + checkAccess($dbHandler, $args->user, $env, $check); + + $tproj_mgr = new testproject($dbHandler); + $dm = $tproj_mgr->get_by_id($args->tproject_id, array( + 'output' => 'name' + )); + $args->tproject_name = $dm['name']; + + return $args; +} + +/** + * do_export + * generate export file + * + * @param database $db + * @param TLSmarty $smarty + * @param stdClass $args + */ +function do_export(&$db, &$smarty, &$args) +{ + $pfn = null; + $pfx = null; + switch ($args->exportType) { + case 'iSerializationToCSV': + $pfn = null; + $pfx = "exportKeywordsToCSV"; + break; + + case 'iSerializationToXML': + $pfn = "exportKeywordsToXML"; + break; + } + + if (null != $pfn) { + $tprojectMgr = new testproject($db); + $content = $tprojectMgr->$pfn($args->tproject_id); + downloadContentsToFile($content, $args->export_filename); + exit(); + } + + if (null != $pfx) { + $cu = getKeywordsEnv($db, $args->user, $args->tproject_id, + array( + 'usage' => 'csvExport' + )); + + $content = exportKeywordsToCSV($cu->kwOnTCV); + downloadContentsToFile($content, $args->export_filename); + exit(); + } +} + +/** + * Initialisiert die GUI + * + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$argsObj) +{ + $kw = new tlKeyword(); + $gui = new stdClass(); + $gui->tproject_id = $argsObj->tproject_id; + $gui->exportTypes = $kw->getSupportedSerializationInterfaces(); + $gui->main_descr = lang_get('testproject') . TITLE_SEP . + $argsObj->tproject_name; + $gui->export_filename = is_null($argsObj->export_filename) ? 'keywords.xml' : $argsObj->export_filename; + $gui->action_descr = lang_get('export_keywords'); + + $gui->actionUrl = "lib/keywords/keywordsExport.php?doAction=do_export&tproject_id={$gui->tproject_id}"; + $gui->cancelUrl = "lib/keywords/keywordsView.php?tproject_id={$gui->tproject_id}"; + return $gui; +} + +/** + * Export keywords to CSV + * + * @param array $kwSet + * @return string in csv format + */ +function exportKeywordsToCSV($kwSet) +{ + $keys = array( + "keyword", + "notes", + "tcv_qty" + ); + return exportDataToCSV($kwSet, $keys, $keys, array( + 'addHeader' => 1 + )); } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - */ -function init_args(&$dbHandler) { - $ipcfg = array("doAction" => array("GET",tlInputParameter::STRING_N,0,50), - "tproject_id" => array("GET",tlInputParameter::INT_N), - "export_filename" => array("POST", tlInputParameter::STRING_N,0,255), - "exportType" => array("POST", tlInputParameter::STRING_N,0,255)); - - $args = new stdClass(); - $pps = I_PARAMS($ipcfg,$args); - - if( $args->tproject_id <= 0 ) { - throw new Exception("Error Invalid Test Project ID", 1); - } - - // Check rights before doing anything else - // Abort if rights are not enough - $args->user = $_SESSION['currentUser']; - $env['tproject_id'] = $args->tproject_id; - $env['tplan_id'] = 0; - - $check = new stdClass(); - $check->items = array('mgt_view_key'); - $check->mode = 'and'; - checkAccess($dbHandler,$args->user,$env,$check); - - $tproj_mgr = new testproject($dbHandler); - $dm = $tproj_mgr->get_by_id($args->tproject_id,array('output' => 'name')); - $args->tproject_name = $dm['name']; - - return $args; -} - - -/* - function: do_export - generate export file - - args : - - returns: - -*/ -function do_export(&$db,&$smarty,&$args) { - $pfn = null; - $pfx = null; - switch($args->exportType) { - case 'iSerializationToCSV': - $pfn = null; - $pfx = "exportKeywordsToCSV"; - break; - - case 'iSerializationToXML': - $pfn = "exportKeywordsToXML"; - break; - } - - if (null != $pfn) { - $tprojectMgr = new testproject($db); - $content = $tprojectMgr->$pfn($args->tproject_id); - downloadContentsToFile($content,$args->export_filename); - exit(); - } - - if (null != $pfx) { - $cu = getKeywordsEnv($db,$args->user,$args->tproject_id, - array('usage' => 'csvExport')); - - $content = exportKeywordsToCSV($cu->kwOnTCV); - downloadContentsToFile($content,$args->export_filename); - exit(); - } - -} - -/** - * - */ -function initializeGui(&$argsObj) { - $kw = new tlKeyword(); - $gui = new stdClass(); - $gui->tproject_id = $argsObj->tproject_id; - $gui->exportTypes = $kw->getSupportedSerializationInterfaces(); - $gui->main_descr = lang_get('testproject') . TITLE_SEP . $argsObj->tproject_name; - $gui->export_filename = is_null($argsObj->export_filename) ? 'keywords.xml' : $argsObj->export_filename; - $gui->action_descr = lang_get('export_keywords'); - - $gui->actionUrl = "lib/keywords/keywordsExport.php?doAction=do_export&tproject_id={$gui->tproject_id}"; - $gui->cancelUrl = "lib/keywords/keywordsView.php?tproject_id={$gui->tproject_id}"; - return $gui; -} - - -/** - * - */ -function exportKeywordsToCSV($kwSet) { - $keys = array( "keyword","notes","tcv_qty" ); - $csv = exportDataToCSV($kwSet,$keys,$keys,array('addHeader' => 1)); - return $csv; -} \ No newline at end of file diff --git a/lib/keywords/keywordsImport.php b/lib/keywords/keywordsImport.php index 71c7a13eed..8452a5f94d 100644 --- a/lib/keywords/keywordsImport.php +++ b/lib/keywords/keywordsImport.php @@ -1,139 +1,160 @@ -msg && $args->UploadFile) -{ - if(($args->source != 'none') && ($args->source != '')) - { - if (move_uploaded_file($args->source, $args->dest)) - { - $pfn = null; - switch($args->importType) - { - case 'iSerializationToCSV': - $pfn = "importKeywordsFromCSV"; - break; - - case 'iSerializationToXML': - $pfn = "importKeywordsFromXMLFile"; - break; - } - - if($pfn) - { - $tproject = new testproject($db); - $result = $tproject->$pfn($args->tproject_id,$args->dest); - if ($result != tl::OK) - { - $gui->msg = lang_get('wrong_keywords_file'); - } - else - { - header("Location: keywordsView.php?tproject_id={$gui->tproject_id}"); - exit(); - } - } - @unlink($args->dest); - } - } - else - { - $gui->msg = lang_get('please_choose_keywords_file'); - } +msg && $args->UploadFile) { + + if (($args->source != 'none') && ($args->source != '')) { + if (move_uploaded_file($args->source, $args->dest)) { + $pfn = null; + switch ($args->importType) { + case 'iSerializationToCSV': + $pfn = "importKeywordsFromCSV"; + break; + + case 'iSerializationToXML': + $pfn = "importKeywordsFromXMLFile"; + break; + } + + if ($pfn) { + $tproject = new testproject($db); + $result = $tproject->$pfn($args->tproject_id, $args->dest); + if ($result != tl::OK) { + $gui->msg = lang_get('wrong_keywords_file'); + } else { + header( + "Location: keywordsView.php?tproject_id={$gui->tproject_id}"); + exit(); + } + } + @unlink($args->dest); + } + } else { + $gui->msg = lang_get('please_choose_keywords_file'); + } +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->tpl); + +/** + * Get input from user and return it in some sort of namespace + * + * @return object returns the arguments for the page + */ +function initArgs(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $ipcfg = array( + "UploadFile" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "importType" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($ipcfg, $args); + + if ($args->tproject_id <= 0) { + throw new Exception(" Error Invalid Test Project ID", 1); + } + + // Check rights before doing anything else + // Abort if rights are not enough + $user = $_SESSION['currentUser']; + $env['tproject_id'] = $args->tproject_id; + $env['tplan_id'] = 0; + + $check = new stdClass(); + $check->items = array( + 'mgt_modify_key' + ); + $check->mode = 'and'; + checkAccess($dbHandler, $user, $env, $check); + + $tproj_mgr = new testproject($dbHandler); + $dm = $tproj_mgr->get_by_id($args->tproject_id, array( + 'output' => 'name' + )); + $args->tproject_name = $dm['name']; + + $args->UploadFile = ($args->UploadFile != "") ? 1 : 0; + $args->fInfo = isset($_FILES['uploadedFile']) ? $_FILES['uploadedFile'] : null; + $args->source = isset($args->fInfo['tmp_name']) ? $args->fInfo['tmp_name'] : null; + + // whitelist + switch ($args->importType) { + case 'iSerializationToCSV': + case 'iSerializationToXML': + break; + + default: + $args->importType = 'iSerializationToXML'; + break; + } + + $tlkw = new tlKeyword(); + $args->importTypes = $tlkw->getSupportedSerializationInterfaces(); + $args->keywordFormatStrings = $tlkw->getSupportedSerializationFormatDescriptions(); + + $args->dest = TL_TEMP_PATH . session_id() . "-importkeywords." . + $args->importTypes[$args->importType]; + + return $args; +} + +/** + */ +function initializeGui(&$argsObj) +{ + $gui = new stdClass(); + $gui->tproject_id = $argsObj->tproject_id; + $gui->tproject_name = $argsObj->tproject_name; + + $gui->main_descr = lang_get('testproject') . TITLE_SEP . $gui->tproject_name; + $gui->viewUrl = "lib/keywords/keywordsView.php?tproject_id={$gui->tproject_id}"; + $gui->import_type_selected = $argsObj->importType; + $gui->msg = getFileUploadErrorMessage($argsObj->fInfo); + + $gui->importTypes = $argsObj->importTypes; + $gui->keywordFormatStrings = $argsObj->keywordFormatStrings; + + $fslimit = config_get('import_file_max_size_bytes'); + $gui->fileSizeLimitMsg = sprintf(lang_get('max_file_size_is'), + $fslimit / 1024 . ' KB '); + $gui->importLimit = $fslimit; + + return $gui; } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * @return object returns the arguments for the page - */ -function init_args(&$dbHandler) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $ipcfg = array("UploadFile" => array(tlInputParameter::STRING_N,0,1), - "importType" => array(tlInputParameter::STRING_N,0,100), - "tproject_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($ipcfg,$args); - - if( $args->tproject_id <= 0 ) - { - throw new Exception(" Error Invalid Test Project ID", 1); - } - - // Check rights before doing anything else - // Abort if rights are not enough - $user = $_SESSION['currentUser']; - $env['tproject_id'] = $args->tproject_id; - $env['tplan_id'] = 0; - - $check = new stdClass(); - $check->items = array('mgt_modify_key'); - $check->mode = 'and'; - checkAccess($dbHandler,$user,$env,$check); - - $tproj_mgr = new testproject($dbHandler); - $dm = $tproj_mgr->get_by_id($args->tproject_id,array('output' => 'name')); - $args->tproject_name = $dm['name']; - - $args->UploadFile = ($args->UploadFile != "") ? 1 : 0; - $args->fInfo = isset($_FILES['uploadedFile']) ? $_FILES['uploadedFile'] : null; - $args->source = isset($args->fInfo['tmp_name']) ? $args->fInfo['tmp_name'] : null; - $args->dest = TL_TEMP_PATH . session_id() . "-importkeywords." . $args->importType; - - return $args; -} - -/** - * - */ -function initializeGui(&$argsObj) -{ - $gui = new stdClass(); - $gui->tproject_id = $argsObj->tproject_id; - $gui->tproject_name = $argsObj->tproject_name; - - $gui->main_descr = lang_get('testproject') . TITLE_SEP . $gui->tproject_name; - $gui->viewUrl = "lib/keywords/keywordsView.php?tproject_id={$gui->tproject_id}"; - $gui->import_type_selected = $argsObj->importType; - $gui->msg = getFileUploadErrorMessage($argsObj->fInfo); - - $tlkw = new tlKeyword(); - $gui->importTypes = $tlkw->getSupportedSerializationInterfaces(); - $gui->keywordFormatStrings = $tlkw->getSupportedSerializationFormatDescriptions(); - - $fslimit = config_get('import_file_max_size_bytes'); - $gui->fileSizeLimitMsg = - sprintf(lang_get('max_file_size_is'), $fslimit/1024 . ' KB '); - $gui->importLimit = $fslimit; - - - - - return $gui; -} \ No newline at end of file diff --git a/lib/keywords/keywordsView.php b/lib/keywords/keywordsView.php index 6efdf40fbc..9c1fb379f0 100644 --- a/lib/keywords/keywordsView.php +++ b/lib/keywords/keywordsView.php @@ -1,65 +1,70 @@ -assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * @return object returns the arguments for the page - */ -function init_args(&$dbHandler) { - $args = new stdClass(); - $tproject_id = isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : 0; - $tproject_id = intval($tproject_id); - - if( $tproject_id <= 0 ) { - throw new Exception("Error Invalid Test Project ID", 1); - } - - // Check rights before doing anything else - // Abort if rights are not enough - $user = $_SESSION['currentUser']; - $env['tproject_id'] = $tproject_id; - $env['tplan_id'] = 0; - - $check = new stdClass(); - $check->items = array('mgt_view_key'); - $check->mode = 'and'; - checkAccess($dbHandler,$user,$env,$check); - - // OK, go ahead - $args = getKeywordsEnv($dbHandler,$user,$tproject_id); - $args->tproject_id = $tproject_id; - - $args->dialogName = ''; - $args->bodyOnLoad = $args->bodyOnUnload = ''; - if(isset($_REQUEST['openByKWInc'])) { - $args->openByOther = 1; - } else { - // Probably useless - $args->openByOther = - isset($_REQUEST['openByOther']) ? intval($_REQUEST['openByOther']) : 0; - if( $args->openByOther ) { - $args->dialogName = 'kw_dialog'; - $args->bodyOnLoad = "dialog_onLoad($args->dialogName)"; - $args->bodyOnUnload = "dialog_onUnload($args->dialogName)"; - } - } - - return $args; -} \ No newline at end of file +assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get input from user and return it in some sort of namespace + * + * @param database $dbHandler + * @return stdClass object returns the arguments for the page + */ +function initArgs(&$dbHandler) +{ + $tproject_id = isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : 0; + $tproject_id = intval($tproject_id); + + if ($tproject_id <= 0) { + throw new Exception("Error Invalid Test Project ID", 1); + } + + // Check rights before doing anything else + // Abort if rights are not enough + $user = $_SESSION['currentUser']; + $env['tproject_id'] = $tproject_id; + $env['tplan_id'] = 0; + + $check = new stdClass(); + $check->items = array( + 'mgt_view_key' + ); + $check->mode = 'and'; + checkAccess($dbHandler, $user, $env, $check); + + // OK, go ahead + $args = getKeywordsEnv($dbHandler, $user, $tproject_id); + $args->tproject_id = $tproject_id; + + $args->dialogName = ''; + $args->bodyOnLoad = $args->bodyOnUnload = ''; + if (isset($_REQUEST['openByKWInc'])) { + $args->openByOther = 1; + } else { + // Probably useless + $args->openByOther = isset($_REQUEST['openByOther']) ? intval( + $_REQUEST['openByOther']) : 0; + if ($args->openByOther) { + $args->dialogName = 'kw_dialog'; + $args->bodyOnLoad = "dialog_onLoad($args->dialogName)"; + $args->bodyOnUnload = "dialog_onUnload($args->dialogName)"; + } + } + + return $args; +} diff --git a/lib/plan/buildCopyExecTaskAssignment.php b/lib/plan/buildCopyExecTaskAssignment.php index 47b51a7a46..132e6fb9f7 100644 --- a/lib/plan/buildCopyExecTaskAssignment.php +++ b/lib/plan/buildCopyExecTaskAssignment.php @@ -1,175 +1,185 @@ -assignment_mgr; -$build_mgr = new build_mgr($db); - -$templateCfg = templateConfiguration(); - -$args = init_args($build_mgr); -$gui = init_gui($db, $args, $tplan_mgr); - -switch( $args->doAction ) -{ - case 'copy': - // Step 1 - remove old assignments - $assignment_mgr->delete_by_build_id($args->build_id); - - // Step 2 - copy assignments - $assignment_mgr->copy_assignments($args->source_build_id,$args->build_id, - $args->user_id); - $gui->message = lang_get('copy_done'); - break; - - default: - break; -} - - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - */ -function init_args(&$buildMgr) -{ - $args = new stdClass(); - - $_REQUEST = strings_stripSlashes($_REQUEST); - - $k2g = array('build_id','source_build_id'); - foreach($k2g as $key) - { - $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; - } - - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; - switch( $args->doAction ) - { - case 'copy': - break; - - default: - $args->doAction = null; - break; - } - - // Fatal checks - if( $args->build_id <= 0 ) - { - throw new Exception("Error Processing Request - Target build is not set", 1); - } - - // Get test plan id from build - $bi = $buildMgr->get_by_id($args->build_id); - $args->tplan_id = $bi['testplan_id']; - - $args->confirmed = isset($_REQUEST['confirmed']) && $_REQUEST['confirmed'] == 'yes' ? true : false; - - - $args->refreshTree = false; - $args->currentUser = $_SESSION['currentUser']; - $args->user_id = $_SESSION['userID']; - - return $args; -} - - -/** - * - */ -function init_gui(&$dbHandler, &$argsObj, &$tplanMgr) -{ - $gui = new stdClass(); - - $gui->build_id = $argsObj->build_id; - $gui->source_build_id = $argsObj->source_build_id; - $gui->source_build = getBuildDomainForGUI($tplanMgr, $argsObj); - $gui->message = ""; - - if( is_null($gui->source_build->items) ) - { - $gui->message = lang_get('no_builds_available_for_tester_copy'); - } - - $gui->draw_tc_unassign_button = false; - $gui->refreshTree = false; - - $gui->title = lang_get('copy_tester_assignments_title'); - - - $gui->popup_title = ""; - $gui->popup_message = ""; - - return $gui; -} - -/** - * Initialize the HTML select box for selection of a source build when - * user wants to copy the user assignments. - * @internal revisions - */ -function getBuildDomainForGUI(&$tplanMgr, &$argsObj) -{ - - $htmlMenu = array('items' => null, 'selected' => null, 'build_count' => 0, 'testers' => null); - $opt = array('orderByDir' => 'id:DESC', 'excludeBuild' => $argsObj->build_id); - - $htmlMenu['items'] = $tplanMgr->get_builds_for_html_options($argsObj->tplan_id,testplan::ACTIVE_BUILDS,testplan::OPEN_BUILDS,$opt); - - // get the number of existing execution assignments with each build - if( !is_null($htmlMenu['items']) ) - { - $lblCount = lang_get('assignments'); - $htmlMenu['build_count'] = count($htmlMenu['items']); - foreach ($htmlMenu['items'] as $key => $name) - { - $count = $tplanMgr->assignment_mgr->get_count_of_assignments_for_build_id($key); - $htmlMenu['items'][$key] = $name . " ($lblCount" . $count . ")"; - $htmlMenu['testers'][$key] = $count; - } - - // if no build has been chosen yet, select the newest build by default - reset($htmlMenu['items']); - if( !$argsObj->source_build_id ) - { - $htmlMenu['selected'] = key($htmlMenu['items']); - } - } - - return $htmlMenu; -} - -/** - * - */ -function checkRights(&$dbHandler,&$user) -{ - if( !$user->hasRight($dbHandler, 'testplan_planning') ) - { - exit(); - } +assignment_mgr; +$build_mgr = new build_mgr($db); + +$templateCfg = templateConfiguration(); + +$args = initArgs($build_mgr); +$gui = initGui($db, $args, $tplan_mgr); + +$context = new stdClass(); +$context->tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +switch ($args->doAction) { + case 'copy': + // Step 1 - remove old assignments + $assignment_mgr->delete_by_build_id($args->build_id); + + // Step 2 - copy assignments + $assignment_mgr->copy_assignments($args->source_build_id, + $args->build_id, $args->user_id); + $gui->message = lang_get('copy_done'); + break; + + default: + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initArgs(&$buildMgr) +{ + $args = new stdClass(); + + $_REQUEST = strings_stripSlashes($_REQUEST); + + $k2g = array( + 'build_id', + 'source_build_id' + ); + foreach ($k2g as $key) { + $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; + } + + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; + switch ($args->doAction) { + case 'copy': + break; + + default: + $args->doAction = null; + break; + } + + // Fatal checks + if ($args->build_id <= 0) { + throw new Exception( + "Error Processing Request - Target build is not set", 1); + } + + // Get test plan id from build + $bi = $buildMgr->get_by_id($args->build_id); + $args->tplan_id = $bi['testplan_id']; + + $info = $buildMgr->tree_manager->get_node_hierarchy_info($args->tplan_id, + null, array( + 'nodeType' => 'testplan' + )); + + $args->tproject_id = intval($info['testproject_id']); + + $args->confirmed = isset($_REQUEST['confirmed']) && + $_REQUEST['confirmed'] == 'yes' ? true : false; + + $args->refreshTree = false; + $args->currentUser = $_SESSION['currentUser']; + $args->user_id = $_SESSION['userID']; + + return $args; +} + +/** + */ +function initGui(&$dbHandler, &$argsObj, &$tplanMgr) +{ + $gui = new stdClass(); + + $gui->build_id = $argsObj->build_id; + $gui->source_build_id = $argsObj->source_build_id; + $gui->source_build = getBuildDomainForGUI($tplanMgr, $argsObj); + $gui->message = ""; + + if (is_null($gui->source_build->items)) { + $gui->message = lang_get('no_builds_available_for_tester_copy'); + } + + $gui->draw_tc_unassign_button = false; + $gui->refreshTree = false; + + $gui->title = lang_get('copy_tester_assignments_title'); + + $gui->popup_title = ""; + $gui->popup_message = ""; + + return $gui; +} + +/** + * Initialize the HTML select box for selection of a source build when + * user wants to copy the user assignments. + * + * @internal revisions + */ +function getBuildDomainForGUI(&$tplanMgr, &$argsObj) +{ + $htmlMenu = array( + 'items' => null, + 'selected' => null, + 'build_count' => 0, + 'testers' => null + ); + $opt = array( + 'orderByDir' => 'id:DESC', + 'excludeBuild' => $argsObj->build_id + ); + + $htmlMenu['items'] = $tplanMgr->get_builds_for_html_options( + $argsObj->tplan_id, testplan::ACTIVE_BUILDS, testplan::OPEN_BUILDS, $opt); + + // get the number of existing execution assignments with each build + if (! is_null($htmlMenu['items'])) { + $lblCount = lang_get('assignments'); + $htmlMenu['build_count'] = count($htmlMenu['items']); + foreach ($htmlMenu['items'] as $key => $name) { + $count = $tplanMgr->assignment_mgr->get_count_of_assignments_for_build_id( + $key); + $htmlMenu['items'][$key] = $name . " ($lblCount" . $count . ")"; + $htmlMenu['testers'][$key] = $count; + } + + // if no build has been chosen yet, select the newest build by default + reset($htmlMenu['items']); + if (! $argsObj->source_build_id) { + $htmlMenu['selected'] = key($htmlMenu['items']); + } + } + + return $htmlMenu; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_planning" + ]; + pageAccessCheck($db, $user, $context); } diff --git a/lib/plan/buildEdit.php b/lib/plan/buildEdit.php index b4d0e2d518..16641e33d5 100644 --- a/lib/plan/buildEdit.php +++ b/lib/plan/buildEdit.php @@ -1,679 +1,823 @@ -user_feedback = ''; -$op->buttonCfg = new stdClass(); -$op->buttonCfg->name = ""; -$op->buttonCfg->value = ""; - -$smarty = new TLSmarty(); -$tplan_mgr = new testplan($db); -$build_mgr = new build_mgr($db); - -$args = init_args($_REQUEST,$_SESSION,$date_format_cfg,$tplan_mgr); -$gui = initializeGui($args,$build_mgr); - - -$of = web_editor('notes',$_SESSION['basehref'],$editorCfg); -$of->Value = getItemTemplateContents('build_template', $of->InstanceName, $args->notes); - - -$op = new stdClass(); -$op->operation_descr = ''; -$op->user_feedback = ''; -$op->buttonCfg = ''; -$op->status_ok = 1; - -switch($args->do_action) -{ - case 'edit': - $op = edit($args,$build_mgr,$date_format_cfg); - $gui->closed_on_date = $args->closed_on_date; - $of->Value = $op->notes; - break; - - case 'create': - $op = create($args); - $gui->closed_on_date = $args->closed_on_date; - break; - - case 'do_delete': - $op = doDelete($db,$args,$build_mgr,$tplan_mgr); - break; - - case 'do_update': - $op = doUpdate($args,$build_mgr,$tplan_mgr,$date_format_cfg); - $of->Value = $op->notes; - $templateCfg->template = $op->template; - break; - - case 'do_create': - $op = doCreate($args,$build_mgr,$tplan_mgr,$date_format_cfg); - $of->Value = $op->notes; - $templateCfg->template = $op->template; - break; - - case 'setActive': - $build_mgr->setActive($args->build_id); - break; - - case 'setInactive': - $build_mgr->setInactive($args->build_id); - break; - - case 'open': - $build_mgr->setOpen($args->build_id); - break; - - case 'close': - $build_mgr->setClosed($args->build_id); - break; - +user_feedback = ''; +$op->buttonCfg = new stdClass(); +$op->buttonCfg->name = ""; +$op->buttonCfg->value = ""; + +$smarty = new TLSmarty(); +$tplan_mgr = new testplan($db); +$build_mgr = new build_mgr($db); + +$args = initArgs($_REQUEST, $_SESSION, $date_format_cfg, $tplan_mgr); +$gui = initializeGui($args, $build_mgr); + +$context = new stdClass(); +$context->tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$of = web_editor('notes', $_SESSION['basehref'], $editorCfg); +$of->Value = getItemTemplateContents('build_template', $of->InstanceName, + $args->notes); + +$op = new stdClass(); +$op->operation_descr = ''; +$op->user_feedback = ''; +$op->buttonCfg = ''; +$op->status_ok = 1; + +switch ($args->do_action) { + case 'edit': + $op = edit($args, $build_mgr); + $gui->closed_on_date = $args->closed_on_date; + $of->Value = $op->notes; + break; + + case 'create': + $op = create($args); + $gui->closed_on_date = $args->closed_on_date; + break; + + case 'do_delete': + $op = doDelete($db, $args, $build_mgr, $tplan_mgr); + break; + + case 'do_update': + $op = doUpdate($args, $build_mgr, $tplan_mgr, $date_format_cfg); + $of->Value = $op->notes; + $templateCfg->template = $op->template; + break; + + case 'do_create': + $op = doCreate($args, $build_mgr, $tplan_mgr, $date_format_cfg); + $of->Value = $op->notes; + $templateCfg->template = $op->template; + break; + + case 'setActive': + $build_mgr->setActive($args->build_id); + break; + + case 'setInactive': + $build_mgr->setInactive($args->build_id); + break; + + case 'open': + $build_mgr->setOpen($args->build_id); + break; + + case 'close': + $build_mgr->setClosed($args->build_id); + break; +} + +$dummy = null; +$gui->release_date = (isset($op->status_ok) && $op->status_ok && + $args->release_date != "") ? localize_dateOrTimeStamp(null, $dummy, + 'date_format', $args->release_date) : $args->release_date_original; +$gui->closed_on_date = $args->closed_on_date; +$gui->operation_descr = $op->operation_descr; +$gui->user_feedback = $op->user_feedback; +$gui->buttonCfg = $op->buttonCfg; + +$gui->mgt_view_events = $args->user->hasRight($db, "mgt_view_events"); +$gui->editorType = $editorCfg['type']; + +renderGui($smarty, $args, $tplan_mgr, $build_mgr, $templateCfg, $of, $gui); + +/** + * INITialize page ARGuments, using the $_REQUEST and $_SESSION + * super-global hashes. + * Important: changes in HTML input elements on the Smarty template + * must be reflected here. + * + * @param array $request_hash + * hash the $_REQUEST + * @param array $session_hash + * hash the the $_SESSION + * @param string $date_format + * @param testplan $tplanMgr + * @return stdClass object with html values tranformed and other generated variables. + * @internal revisions + */ +function initArgs($request_hash, $session_hash, $date_format, &$tplanMgr) +{ + $args = new stdClass(); + $request_hash = strings_stripSlashes($request_hash); + + $nullable_keys = array( + 'notes', + 'do_action', + 'build_name', + 'commit_id', + 'tag', + 'branch', + 'release_candidate' + ); + foreach ($nullable_keys as $value) { + $args->$value = isset($request_hash[$value]) ? $request_hash[$value] : null; + } + + $intval_keys = array( + 'build_id' => 0, + 'source_build_id' => 0 + ); + foreach ($intval_keys as $key => $value) { + $args->$key = isset($request_hash[$key]) ? intval($request_hash[$key]) : $value; + } + + $bool_keys = array( + 'is_active' => 0, + 'is_open' => 0, + 'copy_to_all_tplans' => 0, + 'copy_tester_assignments' => 0 + ); + foreach ($bool_keys as $key => $value) { + $args->$key = isset($request_hash[$key]) ? 1 : $value; + } + + // convert start date to iso format to write to db + $args->release_date = null; + if (isset($request_hash['release_date']) && + $request_hash['release_date'] != '') { + $date_array = split_localized_date($request_hash['release_date'], + $date_format); + if ($date_array != null) { + // set date in iso format + $args->release_date = $date_array['year'] . "-" . + $date_array['month'] . "-" . $date_array['day']; + } + } + + $args->release_date_original = isset($request_hash['release_date']) && + $request_hash['release_date'] != '' ? $request_hash['release_date'] : null; + + $args->closed_on_date = isset($request_hash['closed_on_date']) ? $request_hash['closed_on_date'] : null; + + if (isset($request_hash['tplan_id']) && intval($request_hash['tplan_id']) > 0) { + $args->tplan_id = intval($_REQUEST['tplan_id']); + $dp = $tplanMgr->get_by_id($args->tplan_id); + $args->tplan_name = $dp['name']; + } else { + $args->tplan_id = isset($session_hash['testplanID']) ? intval( + $session_hash['testplanID']) : 0; + $args->tplan_name = isset($session_hash['testplanName']) ? $session_hash['testplanName'] : ''; + } + + $args->testprojectID = intval($session_hash['testprojectID']); + $args->tproject_id = intval($session_hash['testprojectID']); + + $args->testprojectName = $session_hash['testprojectName']; + $args->userID = intval($session_hash['userID']); + + $args->exec_status_filter = isset($request_hash['exec_status_filter']) ? $request_hash['exec_status_filter'] : null; + + $args->user = $_SESSION['currentUser']; + return $args; +} + +/** + * Initialize the GUI + * + * @param stdClass $argsObj + * @param stdClass $buildMgr + * @return stdClass + */ +function initializeGui(&$argsObj, &$buildMgr) +{ + $guiObj = new stdClass(); + $guiObj->main_descr = lang_get('title_build_2') . + config_get('gui_title_separator_2') . lang_get('test_plan') . + config_get('gui_title_separator_1') . $argsObj->tplan_name; + + $guiObj->cfields = $buildMgr->html_custom_field_inputs($argsObj->build_id, + $argsObj->testprojectID, 'design', '', $_REQUEST); + + $dummy = config_get('results'); + foreach ($dummy['status_label_for_exec_ui'] as $kv => $vl) { + $guiObj->exec_status_filter['items'][$dummy['status_code'][$kv]] = lang_get( + $vl); + } + $guiObj->exec_status_filter['selected'] = $argsObj->exec_status_filter; + + $guiObj->tplan_id = $argsObj->tplan_id; + $guiObj->tproject_id = $argsObj->tproject_id; + return $guiObj; +} + +/** + * This action can be used to edit a build + * + * @param stdClass $argsObj + * @param build_mgr $buildMgr + * @return stdClass + */ +function edit(&$argsObj, &$buildMgr) +{ + $binfo = $buildMgr->get_by_id($argsObj->build_id); + $op = new stdClass(); + $op->buttonCfg = new stdClass(); + $op->buttonCfg->name = "do_update"; + $op->buttonCfg->value = lang_get('btn_save'); + $op->notes = $binfo['notes']; + $op->user_feedback = ''; + $op->status_ok = 1; + + $argsObj->build_name = $binfo['name']; + $argsObj->is_active = $binfo['active']; + + $k2l = array( + 'is_open', + 'commit_id', + 'tag', + 'branch', + 'release_candidate', + 'release_date' + ); + foreach ($k2l as $pp) { + $argsObj->$pp = $binfo[$pp]; + } + + if ($binfo['closed_on_date'] == '') { + $argsObj->closed_on_date = mktime(0, 0, 0, date("m"), date("d"), + date("Y")); + } else { + $datePieces = explode("-", $binfo['closed_on_date']); + $argsObj->closed_on_date = mktime(0, 0, 0, $datePieces[1], + $datePieces[2], $datePieces[0]); + } + + $op->operation_descr = lang_get('title_build_edit') . TITLE_SEP_TYPE3 . + $argsObj->build_name; + + return $op; +} + +/** + * This action creates a new build + * + * prepares environment to manage user interaction on a create operations + * + * @param stdClass $argsObj + * reference to input values received by page. + * @return stdClass object with part of gui configuration + */ +function create(&$argsObj) +{ + $op = new stdClass(); + $op->operation_descr = lang_get('title_build_create'); + $op->buttonCfg = new stdClass(); + $op->buttonCfg->name = "do_create"; + $op->buttonCfg->value = lang_get('btn_create'); + $op->user_feedback = ''; + $argsObj->is_active = 1; + $argsObj->is_open = 1; + $argsObj->closed_on_date = mktime(0, 0, 0, date("m"), date("d"), date("Y")); + + return $op; +} + +/** + * This action deletes an existing build + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param build_mgr $buildMgr + * @param testplan $tplanMgr + * @return stdClass + */ +function doDelete(&$dbHandler, &$argsObj, &$buildMgr, &$tplanMgr) +{ + $op = new stdClass(); + $op->user_feedback = ''; + $op->operation_descr = ''; + $op->buttonCfg = null; + + $build = $buildMgr->get_by_id($argsObj->build_id); + + $qty = $tplanMgr->getExecCountOnBuild($argsObj->tplan_id, $argsObj->build_id); + if ($qty > 0 && ! $argsObj->user->hasRightOnProj($dbHandler, 'exec_delete')) { + // Need to check if user has rigth to delete executions + $op->user_feedback = sprintf( + lang_get("cannot_delete_build_no_exec_delete"), $build['name']); + return $op; + } + + if (! $buildMgr->delete($argsObj->build_id)) { + $op->user_feedback = lang_get("cannot_delete_build"); + } else { + logAuditEvent( + TLS("audit_build_deleted", $argsObj->testprojectName, + $argsObj->tplan_name, $build['name']), "DELETE", + $argsObj->build_id, "builds"); + } + return $op; +} + +/** + * Renders the GUI + * + * @param TLSmarty $smartyObj + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @param build_mgr $buildMgr + * @param stdClass $templateCfg + * @param ckeditorInterface $owebeditor + * @param stdClass $guiObj + */ +function renderGui(&$smartyObj, &$argsObj, &$tplanMgr, &$buildMgr, $templateCfg, + $owebeditor, &$guiObj) +{ + $doRender = false; + switch ($argsObj->do_action) { + case "do_create": + case "do_delete": + case "do_update": + case "setActive": + case "setInactive": + case "open": + case "close": + $doRender = true; + $tpl = is_null($templateCfg->template) ? 'buildView.tpl' : $templateCfg->template; + + // ----- + // To create the CF columns we need to get the linked CF + // Attention this is affected by changes in templates + $guiObj->buildSet = $tplanMgr->get_builds($argsObj->tplan_id); + $availableCF = (array) $buildMgr->get_linked_cfields_at_design( + $guiObj->build, $guiObj->tproject_id); + + $hasCF = count($availableCF); + $guiObj->cfieldsColumns = null; + $guiObj->cfieldsType = null; + + // get CF used to configure HIDE COLS + // We want different configurations for different test projects + // then will do two steps algorithm + // 1. get test project prefix PPFX + // 2. look for TL_BUILDVIEW_HIDECOL_PPFX + // 3. if found proceed + // 4. else look for TL_BUILDVIEW_HIDECOL + // + $ppfx = $tplanMgr->tproject_mgr->getTestCasePrefix( + $guiObj->tproject_id); + $suffixSet = [ + '_' . $ppfx, + '' + ]; + foreach ($suffixSet as $suf) { + $gopt['name'] = 'TL_BUILDVIEW_HIDECOL' . $suf; + $col2hideCF = $tplanMgr->cfield_mgr->get_linked_to_testproject( + $guiObj->tproject_id, null, $gopt); + if ($col2hideCF != null) { + $col2hideCF = current($col2hideCF); + $col2hide = array_flip( + explode('|', $col2hideCF['possible_values'])); + $col2hide[$gopt['name']] = ''; + break; + } + } + + $localeDateFormat = config_get('locales_date_format'); + $localeDateFormat = $localeDateFormat[$args->user->locale]; + $initCFCol = true; + + foreach ($guiObj->buildSet as $elemBuild) { + $idk = current($elemBuild); + if ($hasCF) { + $cfields = (array) $buildMgr->getCustomFieldsValues($idk, + $guiObj->tproject_id); + foreach ($cfields as $cfd) { + if ($initCFCol && ! isset($col2hide[$cfd['name']])) { + $guiObj->cfieldsColumns[] = $cfd['label']; + $guiObj->cfieldsType[] = $cfd['type']; + } + $guiObj->buildSet[$idk][$cfd['label']] = [ + 'value' => $cfd['value'], + 'data-order' => $cfd['value'] + ]; + if ($cfd['type'] == 'date') { + $guiObj->buildSet[$idk][$cfd['label']]['data-order'] = locateDateToISO( + $cfd['value'], $localeDateFormat); + } + } + $initCFCol = false; + } + } + break; + + case "edit": + case "create": + $doRender = true; + $tpl = is_null($templateCfg->template) ? $templateCfg->default_template : $templateCfg->template; + break; + } + + if ($doRender) { + + // Attention this is affected by changes in templates + $guiObj->enable_copy = ($argsObj->do_action == 'create' || + $argsObj->do_action == 'do_create') ? 1 : 0; + $guiObj->notes = $owebeditor->CreateHTML(); + $guiObj->source_build = initSourceBuildSelector($tplanMgr, $argsObj); + + $k2c = array( + 'tplan_name', + 'build_id', + 'build_name', + 'is_active', + 'is_open', + 'copy_tester_assignments', + 'commit_id', + 'tag', + 'branch', + 'release_candidate' + ); + + foreach ($k2c as $pp) { + $guiObj->$pp = $argsObj->$pp; + } + + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($templateCfg->template_dir . $tpl); + } +} + +/** + * create action + * + * @param stdClass $argsObj + * @param build_mgr $buildMgr + * @param testplan $tplanMgr + * @param string $dateFormat + * @return stdClass + */ +function doCreate(&$argsObj, &$buildMgr, &$tplanMgr, $dateFormat) +{ + $op = new stdClass(); + $op->operation_descr = ''; + $op->user_feedback = ''; + $op->template = "buildEdit.tpl"; + $op->notes = $argsObj->notes; + $op->status_ok = 0; + $op->buttonCfg = null; + $check = crossChecks($argsObj, $tplanMgr, $dateFormat); + $targetDate = null; + if ($check->status_ok) { + $oBuild = new stdClass(); + // 'creation_ts' + $prop = array( + 'tplan_id', + 'release_date', + 'notes', + 'commit_id', + 'tag', + 'branch', + 'release_candidate', + 'is_active', + 'is_open' + ); + + $oBuild->name = $argsObj->build_name; + foreach ($prop as $pp) { + $oBuild->$pp = $argsObj->$pp; + } + + $buildID = $buildMgr->createFromObject($oBuild); + + if ($buildID) { + $cf_map = $buildMgr->get_linked_cfields_at_design($buildID, + $argsObj->testprojectID); + $buildMgr->cfield_mgr->design_values_to_db($_REQUEST, $buildID, + $cf_map, null, 'build'); + + if ($argsObj->is_open == 1) { + $targetDate = null; + } else { + $targetDate = date("Y-m-d", $argsObj->closed_on_date); + } + $buildMgr->setClosedOnDate($buildID, $targetDate); + + if ($argsObj->copy_tester_assignments && $argsObj->source_build_id) { + if (! is_null($argsObj->exec_status_filter) && + is_array($argsObj->exec_status_filter)) { + $buildSet[] = $argsObj->source_build_id; + + $execVerboseDomain = config_get('results'); + $execVerboseDomain = array_flip( + $execVerboseDomain['status_code']); + + // remember that assignment is done at platform + build + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'addIfNull' => true, + 'outputDetails' => 'name' + ); + $platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id, + $getOpt); + + $caOpt['keep_old_assignments'] = true; + foreach ($platformSet as $platform_id => $pname) { + $glf['filters']['platform_id'] = $platform_id; + foreach ($argsObj->exec_status_filter as $ec) { + switch ($execVerboseDomain[$ec]) { + case 'not_run': + $tcaseSet = $tplanMgr->getHitsNotRunForBuildAndPlatform( + $argsObj->tplan_id, $platform_id, + $argsObj->source_build_id); + + break; + + default: + $tcaseSet = $tplanMgr->getHitsSingleStatusFull( + $argsObj->tplan_id, $platform_id, $ec, + $buildSet); + break; + } + + if (! is_null($tcaseSet)) { + $targetSet = array_keys($tcaseSet); + $features = $tplanMgr->getLinkedFeatures( + $argsObj->tplan_id, $glf['filters']); + $caOpt['feature_set'] = null; + foreach ($targetSet as $tcase_id) { + $caOpt['feature_set'][] = $features[$tcase_id][$glf['filters']['platform_id']]['feature_id']; + } + $tplanMgr->assignment_mgr->copy_assignments( + $argsObj->source_build_id, $buildID, + $argsObj->userID, $caOpt); + } + } + } + } else { + $tplanMgr->assignment_mgr->copy_assignments( + $argsObj->source_build_id, $buildID, $argsObj->userID); + } + } + + $op->user_feedback = ''; + $op->notes = ''; + $op->template = null; + $op->status_ok = 1; + logAuditEvent( + TLS("audit_build_created", $argsObj->testprojectName, + $argsObj->tplan_name, $argsObj->build_name), "CREATE", + $buildID, "builds"); + } + } + + if (! $op->status_ok) { + $op->buttonCfg = new stdClass(); + $op->buttonCfg->name = "do_create"; + $op->buttonCfg->value = lang_get('btn_create'); + $op->user_feedback = $check->user_feedback; + } elseif ($argsObj->copy_to_all_tplans) { + doCopyToTestPlans($argsObj, $buildMgr, $tplanMgr); + } + return $op; +} + +/** + * This action updates a build + * + * @param stdClass $argsObj + * @param build_mgr $buildMgr + * @param testplan $tplanMgr + * @param string $dateFormat + * @return stdClass + */ +function doUpdate(&$argsObj, &$buildMgr, &$tplanMgr, $dateFormat) +{ + $op = new stdClass(); + $op->operation_descr = ''; + $op->user_feedback = ''; + $op->template = "buildEdit.tpl"; + $op->notes = $argsObj->notes; + $op->status_ok = 0; + $op->buttonCfg = null; + + $oldObjData = $buildMgr->get_by_id($argsObj->build_id); + $oldname = $oldObjData['name']; + + $check = crossChecks($argsObj, $tplanMgr, $dateFormat); + if ($check->status_ok) { + $attr = array(); + $k2c = array( + 'release_date', + 'release_candidate', + 'is_active', + 'is_open', + 'copy_tester_assignments', + 'commit_id', + 'tag', + 'branch' + ); + foreach ($k2c as $pp) { + $attr[$pp] = $argsObj->$pp; + } + + if ($buildMgr->update($argsObj->build_id, $argsObj->build_name, + $argsObj->notes, $attr)) { + $cf_map = $buildMgr->get_linked_cfields_at_design( + $argsObj->build_id, $argsObj->testprojectID); + $buildMgr->cfield_mgr->design_values_to_db($_REQUEST, + $argsObj->build_id, $cf_map, null, 'build'); + + if ($argsObj->closed_on_date == '') { + $argsObj->closed_on_date = mktime(0, 0, 0, date("m"), date("d"), + date("Y")); + } + + if ($argsObj->is_open == 1) { + $targetDate = null; + } else { + $targetDate = date("Y-m-d", $argsObj->closed_on_date); + } + $buildMgr->setClosedOnDate($argsObj->build_id, $targetDate); + + $op->user_feedback = ''; + $op->notes = ''; + $op->template = null; + $op->status_ok = 1; + logAuditEvent( + TLS("audit_build_saved", $argsObj->testprojectName, + $argsObj->tplan_name, $argsObj->build_name), "SAVE", + $argsObj->build_id, "builds"); + } + } + + if (! $op->status_ok) { + $op->operation_descr = lang_get('title_build_edit') . TITLE_SEP_TYPE3 . + $oldname; + $op->buttonCfg = new stdClass(); + $op->buttonCfg->name = "do_update"; + $op->buttonCfg->value = lang_get('btn_save'); + $op->user_feedback = $check->user_feedback; + } + return $op; +} + +/** + * crossChecks + * + * do checks that are common to create and update operations + * - name already exists in this testplan? + * + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @param string $dateFormat + * @return stdClass + * @internal revision + * 20100706 - franciscom - BUGID 3581 + */ +function crossChecks($argsObj, &$tplanMgr, $dateFormat) +{ + $op = new stdClass(); + $op->user_feedback = ''; + $op->status_ok = 1; + $buildID = ($argsObj->do_action == 'do_update') ? $argsObj->build_id : null; + if ($tplanMgr->check_build_name_existence($argsObj->tplan_id, + $argsObj->build_name, $buildID)) { + $op->user_feedback = lang_get("warning_duplicate_build") . + TITLE_SEP_TYPE3 . $argsObj->build_name; + $op->status_ok = 0; + } + + // check is date is valid + if ($op->status_ok) { + + // BUGID 3716 + $rdate = trim($argsObj->release_date_original); + + $date_array = split_localized_date($rdate, $dateFormat); + + if ($date_array != null) { + $status_ok = checkdate($date_array['month'], $date_array['day'], + $date_array['year']); + $op->status_ok = $status_ok ? 1 : 0; + } else { + $op->status_ok = 0; + } + + // release date is optional + if ($rdate == "") { + $op->status_ok = 1; + } + + if ($op->status_ok == 0) { + $op->user_feedback = lang_get("invalid_release_date"); + } + } + + return $op; +} + +/** + * Copy do checks that are common to create and update operations + * - name already exists in this testplan? + * + * @param stdClass $argsObj + * @param build_mgr $buildMgr + * @param testplan $tplanMgr + */ +function doCopyToTestPlans(&$argsObj, &$buildMgr, &$tplanMgr) +{ + $tprojectMgr = new testproject($tplanMgr->db); + + // exclude this testplan + $filters = array( + 'tplan2exclude' => $argsObj->tplan_id + ); + $tplanset = $tprojectMgr->get_all_testplans($argsObj->testprojectID, + $filters); + + if (! is_null($tplanset)) { + foreach ($tplanset as $id => $info) { + if (! $tplanMgr->check_build_name_existence($id, + $argsObj->build_name)) { + $buildMgr->create($id, $argsObj->build_name, $argsObj->notes, + $argsObj->is_active, $argsObj->is_open); + } + } + } +} + +/** + * Check the rights + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_create_build" + ]; + pageAccessCheck($db, $user, $context); +} + +/** + * Initialize the HTML select box for selection of a source build when + * user wants to copy the user assignments on creation of a new build. + * + * @author Andreas Simon + * @param testplan $testplan_mgr + * reference to testplan manager object + * @param object $argsObj + * reference to user input object + * @return array $htmlMenu array structure with all information needed for the menu + * + * @internal revisions + */ +function initSourceBuildSelector(&$testplan_mgr, &$argsObj) +{ + $htmlMenu = array( + 'items' => null, + 'selected' => null, + 'build_count' => 0 + ); + $htmlMenu['items'] = $testplan_mgr->get_builds_for_html_options( + $argsObj->tplan_id, null, null, array( + 'orderByDir' => 'id:DESC' + )); + + // get the number of existing execution assignments with each build + if (! is_null($htmlMenu['items'])) { + $htmlMenu['build_count'] = count($htmlMenu['items']); + foreach ($htmlMenu['items'] as $key => $name) { + $count = $testplan_mgr->assignment_mgr->get_count_of_assignments_for_build_id( + $key); + $htmlMenu['items'][$key] = $name . " (" . $count . ")"; + } + + // if no build has been chosen yet, select the newest build by default + reset($htmlMenu['items']); + if (! $argsObj->source_build_id) { + $htmlMenu['selected'] = key($htmlMenu['items']); + } + } + + return $htmlMenu; } - -$dummy = null; -$gui->release_date = (isset($op->status_ok) && $op->status_ok && $args->release_date != "") ? - localize_dateOrTimeStamp(null, $dummy, 'date_format',$args->release_date) : - $args->release_date_original; -$gui->closed_on_date = $args->closed_on_date; -$gui->operation_descr = $op->operation_descr; -$gui->user_feedback = $op->user_feedback; -$gui->buttonCfg = $op->buttonCfg; - -$gui->mgt_view_events = $args->user->hasRight($db,"mgt_view_events"); -$gui->editorType = $editorCfg['type']; - -renderGui($smarty,$args,$tplan_mgr,$templateCfg,$of,$gui); - -/* - * INITialize page ARGuments, using the $_REQUEST and $_SESSION - * super-global hashes. - * Important: changes in HTML input elements on the Smarty template - * must be reflected here. - * - * - * @parameter hash request_hash the $_REQUEST - * @parameter hash session_hash the $_SESSION - * @return object with html values tranformed and other - * generated variables. - * @internal revisions - * - */ -function init_args($request_hash, $session_hash,$date_format,&$tplanMgr) { - $args = new stdClass(); - $request_hash = strings_stripSlashes($request_hash); - - $nullable_keys = array('notes','do_action','build_name', - 'commit_id','tag','branch','release_candidate'); - foreach($nullable_keys as $value) { - $args->$value = isset($request_hash[$value]) ? $request_hash[$value] : null; - } - - $intval_keys = array('build_id' => 0, 'source_build_id' => 0); - foreach($intval_keys as $key => $value) { - $args->$key = isset($request_hash[$key]) ? intval($request_hash[$key]) : $value; - } - - $bool_keys = array('is_active' => 0, 'is_open' => 0, - 'copy_to_all_tplans' => 0, - 'copy_tester_assignments' => 0); - foreach($bool_keys as $key => $value) { - $args->$key = isset($request_hash[$key]) ? 1 : $value; - } - - // convert start date to iso format to write to db - $args->release_date = null; - if (isset($request_hash['release_date']) && $request_hash['release_date'] != '') - { - $date_array = split_localized_date($request_hash['release_date'], $date_format); - if ($date_array != null) { - // set date in iso format - $args->release_date = $date_array['year'] . "-" . $date_array['month'] . "-" . $date_array['day']; - } - } - - $args->release_date_original = isset($request_hash['release_date']) && $request_hash['release_date'] != '' ? - $request_hash['release_date'] : null; - - $args->closed_on_date = isset($request_hash['closed_on_date']) ? $request_hash['closed_on_date'] : null; - - - if(isset($request_hash['tplan_id']) && intval($request_hash['tplan_id']) > 0) { - $args->tplan_id = intval($_REQUEST['tplan_id']); - $dp = $tplanMgr->get_by_id($args->tplan_id); - $args->tplan_name = $dp['name']; - } else { - $args->tplan_id = isset($session_hash['testplanID']) ? intval($session_hash['testplanID']) : 0; - $args->tplan_name = isset($session_hash['testplanName']) ? $session_hash['testplanName']: ''; - } - - $args->testprojectID = intval($session_hash['testprojectID']); - $args->testprojectName = $session_hash['testprojectName']; - $args->userID = intval($session_hash['userID']); - - $args->exec_status_filter = isset($request_hash['exec_status_filter']) ? - $request_hash['exec_status_filter'] : null; - - $args->user = $_SESSION['currentUser']; - return $args; -} - -/** - * - * - */ -function initializeGui(&$argsObj,&$buildMgr) { - $guiObj = new stdClass(); - $guiObj->main_descr = lang_get('title_build_2') . - config_get('gui_title_separator_2') . - lang_get('test_plan') . config_get('gui_title_separator_1') . - $argsObj->tplan_name; - - $guiObj->cfields = $buildMgr->html_custom_field_inputs($argsObj->build_id,$argsObj->testprojectID,'design','',$_REQUEST); - - $dummy = config_get('results'); - foreach($dummy['status_label_for_exec_ui'] as $kv => $vl) { - $guiObj->exec_status_filter['items'][$dummy['status_code'][$kv]] = lang_get($vl); - } - - $guiObj->tplan_id = $argsObj->tplan_id; - return $guiObj; -} - - -/* - function: edit - edit action - - args : - - returns: - -*/ -function edit(&$argsObj,&$buildMgr,$dateFormat) { - - $binfo = $buildMgr->get_by_id($argsObj->build_id); - $op = new stdClass(); - $op->buttonCfg = new stdClass(); - $op->buttonCfg->name = "do_update"; - $op->buttonCfg->value = lang_get('btn_save'); - $op->notes = $binfo['notes']; - $op->user_feedback = ''; - $op->status_ok = 1; - - $argsObj->build_name = $binfo['name']; - $argsObj->is_active = $binfo['active']; - - $k2l = array('is_open','commit_id','tag', - 'branch','release_candidate', - 'release_date'); - foreach( $k2l as $pp ) { - $argsObj->$pp = $binfo[$pp]; - } - - if( $binfo['closed_on_date'] == '') { - $argsObj->closed_on_date = mktime(0, 0, 0, date("m") , date("d"), date("Y")); - } else { - $datePieces = explode("-",$binfo['closed_on_date']); - $argsObj->closed_on_date = mktime(0,0,0,$datePieces[1],$datePieces[2],$datePieces[0]); - } - - $op->operation_descr=lang_get('title_build_edit') . TITLE_SEP_TYPE3 . $argsObj->build_name; - - return $op; -} - -/* - function: create - prepares environment to manage user interaction on a create operations - - args: $argsObj: reference to input values received by page. - - returns: object with part of gui configuration - -*/ -function create(&$argsObj) { - - $op = new stdClass(); - $op->operation_descr = lang_get('title_build_create'); - $op->buttonCfg = new stdClass(); - $op->buttonCfg->name = "do_create"; - $op->buttonCfg->value = lang_get('btn_create'); - $op->user_feedback = ''; - $argsObj->is_active = 1; - $argsObj->is_open = 1; - $argsObj->closed_on_date = mktime(0, 0, 0, date("m") , date("d"), date("Y")); - - return $op; -} - -/* - function: doDelete - - args : - - returns: - -*/ -function doDelete(&$dbHandler,&$argsObj,&$buildMgr,&$tplanMgr) { - $op = new stdClass(); - $op->user_feedback = ''; - $op->operation_descr = ''; - $op->buttonCfg = null; - - $build = $buildMgr->get_by_id($argsObj->build_id); - - $qty = $tplanMgr->getExecCountOnBuild($argsObj->tplan_id,$argsObj->build_id); - if($qty > 0 && !$argsObj->user->hasRight($dbHandler,'exec_delete')) - { - // Need to check if user has rigth to delete executions - $op->user_feedback = sprintf(lang_get("cannot_delete_build_no_exec_delete"),$build['name']); - return $op; - } - - - if (!$buildMgr->delete($argsObj->build_id)) - { - $op->user_feedback = lang_get("cannot_delete_build"); - } - else - { - logAuditEvent(TLS("audit_build_deleted",$argsObj->testprojectName,$argsObj->tplan_name,$build['name']), - "DELETE",$argsObj->build_id,"builds"); - } - return $op; -} - -/* - function: - - args : - - returns: - -*/ -function renderGui(&$smartyObj,&$argsObj,&$tplanMgr,$templateCfg,$owebeditor,&$guiObj) -{ - $doRender = false; - switch($argsObj->do_action) - { - case "do_create": - case "do_delete": - case "do_update": - case "setActive": - case "setInactive": - case "open": - case "close": - $doRender = true; - $tpl = is_null($templateCfg->template) ? 'buildView.tpl' : $templateCfg->template; - break; - - case "edit": - case "create": - $doRender = true; - $tpl = is_null($templateCfg->template) ? $templateCfg->default_template : $templateCfg->template; - break; - } - - if($doRender) { - - // Attention this is affected by changes in templates - $guiObj->buildSet=$tplanMgr->get_builds($argsObj->tplan_id); - - $guiObj->enable_copy = ($argsObj->do_action == 'create' || $argsObj->do_action == 'do_create') ? 1 : 0; - $guiObj->notes = $owebeditor->CreateHTML(); - $guiObj->source_build = init_source_build_selector($tplanMgr, $argsObj); - - $k2c = array('tplan_name','build_id','build_name', - 'is_active','is_open','copy_tester_assignments', - 'commit_id','tag','branch','release_candidate'); - - foreach( $k2c as $pp ) { - $guiObj->$pp=$argsObj->$pp; - } - - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($templateCfg->template_dir . $tpl); - } - -} - - -/* - function: doCreate - - args : - - returns: - - @internal revisions -*/ -function doCreate(&$argsObj,&$buildMgr,&$tplanMgr,$dateFormat) { - $op = new stdClass(); - $op->operation_descr = ''; - $op->user_feedback = ''; - $op->template = "buildEdit.tpl"; - $op->notes = $argsObj->notes; - $op->status_ok = 0; - $op->buttonCfg = null; - $check = crossChecks($argsObj,$tplanMgr,$dateFormat); - $targetDate = null; - if($check->status_ok) { - $user_feedback = lang_get("cannot_add_build"); - - $oBuild = new stdClass(); - $prop = array('tplan_id','release_date','notes', - 'commit_id', 'tag', - 'branch', 'release_candidate', - 'is_active','is_open','creation_ts'); - - $oBuild->name = $argsObj->build_name; - foreach( $prop as $pp ) { - $oBuild->$pp = $argsObj->$pp; - } - - /* - $buildID = $buildMgr->create($argsObj->tplan_id,$argsObj->build_name,$argsObj->notes, - $argsObj->is_active,$argsObj->is_open,$argsObj->release_date); - */ - $buildID = $buildMgr->createFromObject($oBuild); - - if ($buildID) { - $cf_map = $buildMgr->get_linked_cfields_at_design($buildID,$argsObj->testprojectID); - $buildMgr->cfield_mgr->design_values_to_db($_REQUEST,$buildID,$cf_map,null,'build'); - - if($argsObj->is_open == 1) { - $targetDate = null; - } else { - $targetDate=date("Y-m-d",$argsObj->closed_on_date); - } - $buildMgr->setClosedOnDate($buildID,$targetDate); - - if ($argsObj->copy_tester_assignments && - $argsObj->source_build_id) { - if(!is_null($argsObj->exec_status_filter) && - is_array($argsObj->exec_status_filter)) { - $buildSet[] = $argsObj->source_build_id; - - $execVerboseDomain = config_get('results'); - $execVerboseDomain = array_flip($execVerboseDomain['status_code']); - - // remember that assignment is done at platform + build - $getOpt = array('outputFormat' => 'mapAccessByID' , 'addIfNull' => true, - 'outputDetails' => 'name'); - $platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,$getOpt); - - $caOpt['keep_old_assignments'] = true; - foreach($platformSet as $platform_id => $pname) { - $glf['filters']['platform_id'] = $platform_id; - foreach($argsObj->exec_status_filter as $ec) { - switch($execVerboseDomain[$ec]) { - case 'not_run': - $tcaseSet = $tplanMgr->getHitsNotRunForBuildAndPlatform( - $argsObj->tplan_id,$platform_id,$argsObj->source_build_id); - - break; - - default: - $tcaseSet = $tplanMgr->getHitsSingleStatusFull( - $argsObj->tplan_id,$platform_id,$ec,$buildSet); - break; - } - - if(!is_null($tcaseSet)){ - $targetSet = array_keys($tcaseSet); - $features = $tplanMgr->getLinkedFeatures($argsObj->tplan_id,$glf['filters']); - $caOpt['feature_set'] = null; - foreach($targetSet as $tcase_id) { - $caOpt['feature_set'][] = - $features[$tcase_id][$glf['filters']['platform_id']]['feature_id']; - } - $tplanMgr->assignment_mgr->copy_assignments( - $argsObj->source_build_id, $buildID, $argsObj->userID,$caOpt); - } - } - } - } else { - $tplanMgr->assignment_mgr->copy_assignments($argsObj->source_build_id, - $buildID, $argsObj->userID); - } - } - - $op->user_feedback = ''; - $op->notes = ''; - $op->template = null; - $op->status_ok = 1; - logAuditEvent(TLS("audit_build_created",$argsObj->testprojectName, - $argsObj->tplan_name,$argsObj->build_name), - "CREATE",$buildID,"builds"); - } - } - - if(!$op->status_ok) { - $op->buttonCfg = new stdClass(); - $op->buttonCfg->name = "do_create"; - $op->buttonCfg->value = lang_get('btn_create'); - $op->user_feedback = $check->user_feedback; - } elseif($argsObj->copy_to_all_tplans) { - doCopyToTestPlans($argsObj,$buildMgr,$tplanMgr); - } - return $op; -} - - -/* - function: doUpdate - - args : - - returns: - -*/ -function doUpdate(&$argsObj,&$buildMgr,&$tplanMgr,$dateFormat) -{ - $op = new stdClass(); - $op->operation_descr = ''; - $op->user_feedback = ''; - $op->template = "buildEdit.tpl"; - $op->notes = $argsObj->notes; - $op->status_ok = 0; - $op->buttonCfg = null; - - $oldObjData = $buildMgr->get_by_id($argsObj->build_id); - $oldname = $oldObjData['name']; - - $check = crossChecks($argsObj,$tplanMgr,$dateFormat); - if($check->status_ok) { - $user_feedback = lang_get("cannot_update_build"); - - $attr = array(); - $k2c = array('release_date','release_candidate', - 'is_active','is_open','copy_tester_assignments', - 'commit_id','tag','branch'); - foreach( $k2c as $pp ) { - $attr[$pp] = $argsObj->$pp; - } - - if ($buildMgr->update($argsObj->build_id,$argsObj->build_name,$argsObj->notes,$attr) ) { - $cf_map = $buildMgr->get_linked_cfields_at_design($argsObj->build_id,$argsObj->testprojectID); - $buildMgr->cfield_mgr->design_values_to_db($_REQUEST,$argsObj->build_id,$cf_map,null,'build'); - - if( $argsObj->closed_on_date == '') { - $argsObj->closed_on_date = mktime(0, 0, 0, date("m") , date("d"), date("Y")); - } - - if($argsObj->is_open == 1) { - $targetDate=null; - } else { - $targetDate=date("Y-m-d",$argsObj->closed_on_date); - } - $buildMgr->setClosedOnDate($argsObj->build_id,$targetDate); - - $op->user_feedback = ''; - $op->notes = ''; - $op->template = null; - $op->status_ok = 1; - logAuditEvent(TLS("audit_build_saved",$argsObj->testprojectName,$argsObj->tplan_name,$argsObj->build_name), - "SAVE",$argsObj->build_id,"builds"); - } - } - - if(!$op->status_ok) - { - $op->operation_descr = lang_get('title_build_edit') . TITLE_SEP_TYPE3 . $oldname; - $op->buttonCfg = new stdClass(); - $op->buttonCfg->name = "do_update"; - $op->buttonCfg->value = lang_get('btn_save'); - $op->user_feedback = $check->user_feedback; - } - return $op; -} - -/* - function: crossChecks - do checks that are common to create and update operations - - name already exists in this testplan? - args: - - returns: - - - @internal revision - 20100706 - franciscom - BUGID 3581 -*/ -function crossChecks($argsObj,&$tplanMgr,$dateFormat) -{ - $op = new stdClass(); - $op->user_feedback = ''; - $op->status_ok = 1; - $buildID = ($argsObj->do_action == 'do_update') ? $argsObj->build_id : null; - if( $tplanMgr->check_build_name_existence($argsObj->tplan_id,$argsObj->build_name,$buildID) ) - { - $op->user_feedback = lang_get("warning_duplicate_build") . TITLE_SEP_TYPE3 . $argsObj->build_name; - $op->status_ok = 0; - } - - // check is date is valid - if( $op->status_ok ) - { - - // BUGID 3716 - $rdate = trim($argsObj->release_date_original); - - // TODO: comment - $date_array = split_localized_date($rdate,$dateFormat); - - if( $date_array != null ) - { - $status_ok = checkdate($date_array['month'],$date_array['day'],$date_array['year']); - $op->status_ok = $status_ok ? 1 : 0; - } else { - $op->status_ok = 0; - } - - // release date is optional - if ( $rdate == "") { - $op->status_ok = 1; - } - - if( $op->status_ok == 0 ) - { - $op->user_feedback = lang_get("invalid_release_date"); - } - } - - return $op; -} - -/* - function: doCopyToTestPlans - copy do checks that are common to create and update operations - - name already exists in this testplan? - args: - - returns: - - -*/ -function doCopyToTestPlans(&$argsObj,&$buildMgr,&$tplanMgr) -{ - $tprojectMgr = new testproject($tplanMgr->db); - - // exclude this testplan - $filters = array('tplan2exclude' => $argsObj->tplan_id); - $tplanset = $tprojectMgr->get_all_testplans($argsObj->testprojectID,$filters); - - if(!is_null($tplanset)) - { - foreach($tplanset as $id => $info) - { - if(!$tplanMgr->check_build_name_existence($id,$argsObj->build_name)) - { - $buildMgr->create($id,$argsObj->build_name,$argsObj->notes, - $argsObj->is_active,$argsObj->is_open); - } - } - } -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_create_build'); -} - -/** - * Initialize the HTML select box for selection of a source build when - * user wants to copy the user assignments on creation of a new build. - * - * @author Andreas Simon - * @param testplan $testplan_mgr reference to testplan manager object - * @param object $argsObj reference to user input object - * @return array $htmlMenu array structure with all information needed for the menu - * - * @internal revisions - */ -function init_source_build_selector(&$testplan_mgr, &$argsObj) -{ - - $htmlMenu = array('items' => null, 'selected' => null, 'build_count' => 0); - $htmlMenu['items'] = $testplan_mgr->get_builds_for_html_options($argsObj->tplan_id,null,null, - array('orderByDir' => 'id:DESC')); - - - // get the number of existing execution assignments with each build - if( !is_null($htmlMenu['items']) ) - { - $htmlMenu['build_count'] = count($htmlMenu['items']); - foreach ($htmlMenu['items'] as $key => $name) - { - $count = $testplan_mgr->assignment_mgr->get_count_of_assignments_for_build_id($key); - $htmlMenu['items'][$key] = $name . " (" . $count . ")"; - } - - // if no build has been chosen yet, select the newest build by default - reset($htmlMenu['items']); - if( !$argsObj->source_build_id ) - { - $htmlMenu['selected'] = key($htmlMenu['items']); - } - } - - return $htmlMenu; -} // end of method -?> \ No newline at end of file diff --git a/lib/plan/buildView.php b/lib/plan/buildView.php index 0f3c1b7818..8ecddaa6d6 100644 --- a/lib/plan/buildView.php +++ b/lib/plan/buildView.php @@ -1,65 +1,146 @@ -tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : 0; - if( $gui->tplan_id == 0 ) - { - throw new Exception("Abort Test Plan ID == 0", 1); - } - - $tplan_mgr = new testplan($dbHandler); - $info = $tplan_mgr->tree_manager-> - get_node_hierarchy_info($gui->tplan_id,null,array('nodeType' => 'testplan')); - - if( !is_null($info) ) - { - $gui->tplan_name = $info['name']; - } - else - { - throw new Exception("Invalid Test Plan ID", 1); - } - - $gui->buildSet = $tplan_mgr->get_builds($gui->tplan_id); - $gui->user_feedback = null; - - $cfg = getWebEditorCfg('build'); - $gui->editorType = $cfg['type']; - - return $gui; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_create_build'); +tproject_id = $gui->tproject_id; +$context->tplan_id = $gui->tplan_id; + +checkRights($db, $_SESSION['currentUser'], $context); + +/** + * initialize the environment + * + * @param database $dbHandler + * @return StdClass + */ +function initEnv(&$dbHandler) +{ + $gui = new StdClass(); + + $_REQUEST = strings_stripSlashes($_REQUEST); + $gui->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : 0; + if ($gui->tplan_id == 0) { + throw new Exception("Abort Test Plan ID == 0", 1); + } + + $tplan_mgr = new testplan($dbHandler); + $build_mgr = new build_mgr($dbHandler); + $info = $tplan_mgr->tree_manager->get_node_hierarchy_info($gui->tplan_id, + null, array( + 'nodeType' => 'testplan' + )); + + if (! is_null($info)) { + $gui->tplan_name = $info['name']; + } else { + throw new Exception("Invalid Test Plan ID", 1); + } + + $gui->tproject_id = intval($info['parent_id']); + + $gui->buildSet = $tplan_mgr->get_builds($gui->tplan_id); + $gui->user_feedback = null; + + // To create the CF columns we need to get the linked CF + $availableCF = []; + if (! is_null($gui->buildSet)) { + $availableCF = (array) $build_mgr->get_linked_cfields_at_design( + current($gui->buildSet), $gui->tproject_id); + } + $hasCF = count($availableCF); + $gui->cfieldsColumns = null; + $gui->cfieldsType = null; + $initCFCol = true; + + // get CF used to configure HIDE COLS + // We want different configurations for different test projects + // then will do two steps algorithm + // 1. get test project prefix PPFX + // 2. look for TL_BUILDVIEW_HIDECOL_PPFX + // 3. if found proceed + // 4. else look for TL_BUILDVIEW_HIDECOL + // + $ppfx = $tplan_mgr->tproject_mgr->getTestCasePrefix($gui->tproject_id); + $suffixSet = [ + '_' . $ppfx, + '' + ]; + foreach ($suffixSet as $suf) { + $gopt['name'] = 'TL_BUILDVIEW_HIDECOL' . $suf; + $col2hideCF = $tplan_mgr->cfield_mgr->get_linked_to_testproject( + $gui->tproject_id, null, $gopt); + + if ($col2hideCF != null) { + $col2hideCF = current($col2hideCF); + $col2hide = array_flip(explode('|', $col2hideCF['possible_values'])); + $col2hide[$gopt['name']] = ''; + break; + } + } + $localeDateFormat = config_get('locales_date_format'); + $localeDateFormat = $localeDateFormat[$_SESSION['currentUser']->locale]; + + foreach ($gui->buildSet as $elemBuild) { + $idk = current($elemBuild); + if ($hasCF) { + $cfields = (array) $build_mgr->getCustomFieldsValues($idk, + $gui->tproject_id); + foreach ($cfields as $cfd) { + if ($initCFCol && ! isset($col2hide[$cfd['name']])) { + $gui->cfieldsColumns[] = $cfd['label']; + $gui->cfieldsType[] = $cfd['type']; + } + $gui->buildSet[$idk][$cfd['label']] = [ + 'value' => $cfd['value'], + 'data-order' => $cfd['value'] + ]; + if ($cfd['type'] == 'date') { + $gui->buildSet[$idk][$cfd['label']]['data-order'] = locateDateToISO( + $cfd['value'], $localeDateFormat); + } + } + $initCFCol = false; + } + } + + $cfg = getWebEditorCfg('build'); + $gui->editorType = $cfg['type']; + + return $gui; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($tplCfg->template_dir . $tplCfg->default_template); + +/** + * checks the rights + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_create_build" + ]; + pageAccessCheck($db, $user, $context); } diff --git a/lib/plan/newest_tcversions.php b/lib/plan/newest_tcversions.php index 8079e52d5f..03cfc2fb3d 100644 --- a/lib/plan/newest_tcversions.php +++ b/lib/plan/newest_tcversions.php @@ -1,111 +1,111 @@ -can_manage_testplans=$_SESSION['currentUser']->hasRight($db,"mgt_testplan_create"); -$gui->tplans = array(); -$gui->show_details = 0; -$gui->user_feedback = ''; -$gui->tcasePrefix = $tcase_mgr->tproject_mgr->getTestCasePrefix($args->tproject_id) . - $testcase_cfg->glue_character; - -$tplan_info = $tcase_mgr->get_by_id($args->tplan_id); -$gui->tplan_name = $tplan_info['name']; -$gui->tplan_id=$args->tplan_id; -$gui->tproject_name = $args->tproject_name; - -$linked_tcases = $tplan_mgr->get_linked_items_id($args->tplan_id); -$qty_linked = count($linked_tcases); -$gui->testcases = $tplan_mgr->get_linked_and_newest_tcversions($args->tplan_id); - -if($qty_linked) -{ - $qty_newest = count($gui->testcases); - if($qty_newest) - { - $gui->show_details = 1; - - // get path - $tcaseSet=array_keys($gui->testcases); - $path_info=$tree_mgr->get_full_path_verbose($tcaseSet); - foreach($gui->testcases as $tcase_id => $value) - { - $path=$path_info[$tcase_id]; - unset($path[0]); - $path[]=''; - $gui->testcases[$tcase_id]['path']=implode(' / ',$path); - } - } - else - { - $gui->user_feedback = lang_get('no_newest_version_of_linked_tcversions'); - } -} -else -{ - $gui->user_feedback = lang_get('no_linked_tcversions'); +tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$gui = new stdClass(); +$gui->can_manage_testplans = $_SESSION['currentUser']->hasRight($db, + "mgt_testplan_create", $context->tproject_id); +$gui->tplans = array(); +$gui->show_details = 0; +$gui->user_feedback = ''; +$gui->tcasePrefix = $tcaseMgr->tproject_mgr->getTestCasePrefix( + $args->tproject_id) . $testcase_cfg->glue_character; + +$tplan_info = $tcaseMgr->get_by_id($args->tplan_id); +$gui->tplan_name = $tplan_info['name']; +$gui->tplan_id = $args->tplan_id; +$gui->tproject_name = $args->tproject_name; + +$linked_tcases = $tplan_mgr->get_linked_items_id($args->tplan_id); +$qty_linked = count($linked_tcases); +$gui->testcases = $tplan_mgr->get_linked_and_newest_tcversions($args->tplan_id); + +if ($qty_linked) { + $qty_newest = count($gui->testcases); + if ($qty_newest) { + $gui->show_details = 1; + + // get path + $tcaseSet = array_keys($gui->testcases); + $path_info = $tree_mgr->get_full_path_verbose($tcaseSet); + foreach ($gui->testcases as $tcase_id => $value) { + $path = $path_info[$tcase_id]; + unset($path[0]); + $path[] = ''; + $gui->testcases[$tcase_id]['path'] = implode(' / ', $path); + } + } else { + $gui->user_feedback = lang_get('no_newest_version_of_linked_tcversions'); + } +} else { + $gui->user_feedback = lang_get('no_linked_tcversions'); +} + +$tplans = $_SESSION['currentUser']->getAccessibleTestPlans($db, + $args->tproject_id); +foreach ($tplans as $value) { + $gui->tplans[$value['id']] = $value['name']; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * init_args + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->user_id = $_SESSION['userID']; + $args->tproject_id = intval($_SESSION['testprojectID']); + $args->tproject_name = $_SESSION['testprojectName']; + + $args->tplan_id = isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']; + $args->tplan_id = intval($args->tplan_id); + + $args->id = isset($_REQUEST['id']) ? $_REQUEST['id'] : null; + $args->version_id = isset($_REQUEST['version_id']) ? $_REQUEST['version_id'] : 0; + $args->level = isset($_REQUEST['level']) ? $_REQUEST['level'] : null; + + // Can be a list (string with , (comma) has item separator), + $args->keyword_id = isset($_REQUEST['keyword_id']) ? $_REQUEST['keyword_id'] : 0; + + return $args; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_planning" + ]; + pageAccessCheck($db, $user, $context); } - -$tplans = $_SESSION['currentUser']->getAccessibleTestPlans($db,$args->tproject_id); -foreach($tplans as $key => $value) -{ - $gui->tplans[$value['id']] = $value['name']; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/** - * init_args - * - */ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->user_id = $_SESSION['userID']; - $args->tproject_id = intval($_SESSION['testprojectID']); - $args->tproject_name = $_SESSION['testprojectName']; - - $args->tplan_id = isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']; - $args->tplan_id = intval($args->tplan_id); - - $args->id = isset($_REQUEST['id']) ? $_REQUEST['id'] : null; - $args->version_id = isset($_REQUEST['version_id']) ? $_REQUEST['version_id'] : 0; - $args->level = isset($_REQUEST['level']) ? $_REQUEST['level'] : null; - - // Can be a list (string with , (comma) has item separator), - $args->keyword_id = isset($_REQUEST['keyword_id']) ? $_REQUEST['keyword_id'] : 0; - - return $args; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_planning'); -} -?> \ No newline at end of file diff --git a/lib/plan/planAddTC.php b/lib/plan/planAddTC.php index c1c712dc9c..764675a9ea 100644 --- a/lib/plan/planAddTC.php +++ b/lib/plan/planAddTC.php @@ -1,1137 +1,1110 @@ -keyword_id)) { - $keywordsFilter = new stdClass(); - $keywordsFilter->items = $args->keyword_id; - $keywordsFilter->type = $gui->keywordsFilterType->selected; -} - -$do_display = 0; -$do_display_coverage = 0; - -switch($args->item_level) { - case 'testsuite': - case 'req': - case 'req_spec': - $do_display = 1; - break; - - case 'reqcoverage': - case 'reqspeccoverage': - $do_display_coverage = 1; - break; - - case 'testproject': - redirect($_SESSION['basehref'] . - "lib/results/printDocOptions.php?activity=$args->activity"); - exit(); - break; -} - - -switch($args->doAction) { - case 'doAddRemove': - // Remember: checkboxes exist only if are checked - $gui->itemQty = count((array)$args->testcases2add); - - if( !is_null($args->testcases2add) ) { - addToTestPlan($db,$args,$gui,$tplan_mgr,$tcase_mgr); - } - - if(!is_null($args->testcases2remove)) { - // remove without warning - $items_to_unlink=null; - foreach ($args->testcases2remove as $tcase_id => $info) { - foreach ($info as $platform_id => $tcversion_id) { - $items_to_unlink['tcversion'][$tcase_id] = $tcversion_id; - $items_to_unlink['platform'][$platform_id] = $platform_id; - $items_to_unlink['items'][$tcase_id][$platform_id] = $tcversion_id; - } - } - $tplan_mgr->unlink_tcversions($args->tplan_id,$items_to_unlink); - } - - doReorder($args,$tplan_mgr); - break; - - case 'doReorder': - doReorder($args,$tplan_mgr); - break; - - case 'doSavePlatforms': - doSavePlatforms($args,$tplan_mgr); - break; - - case 'doSaveCustomFields': - doSaveCustomFields($args,$_REQUEST,$tplan_mgr,$tcase_mgr); - break; - - default: - break; -} - -$smarty = new TLSmarty(); - - -if($do_display) { - $tsuite_data = $tsuite_mgr->get_by_id($args->object_id); - // see development documentation on [INSTALL DIR]/docs/development/planAddTC.php.txt - $tplan_linked_tcversions = getFilteredLinkedVersions($db,$args,$tplan_mgr,$tcase_mgr,array('addImportance' => true)); - - // Add Test Cases to Test plan - Right pane does not honor custom field filter - $testCaseSet = $args->control_panel['filter_tc_id']; - if(!is_null($keywordsFilter) ) { - - // With this pieces we implement the AND type of keyword filter. - $keywordsTestCases = - $tproject_mgr->getKeywordsLatestTCV($args->tproject_id, - $keywordsFilter->items,$keywordsFilter->type); - - if (sizeof($keywordsTestCases)) { - $testCaseSet = array_keys($keywordsTestCases); - } - } - - // Choose enable/disable display of custom fields, analysing if this kind of custom fields - // exists on this test project. - $cfields = (array)$tsuite_mgr->cfield_mgr->get_linked_cfields_at_testplan_design($args->tproject_id,1,'testcase'); - $opt = array('write_button_only_if_linked' => 0, 'add_custom_fields' => 0); - $opt['add_custom_fields'] = count($cfields) > 0 ? 1 : 0; - - // Add Test Cases to Test plan - Right pane does not honor custom field filter - // filter by test case execution type - $filters = array('keywords' => $args->keyword_id, - 'testcases' => $testCaseSet, - 'exec_type' => $args->executionType, - 'importance' => $args->importance, - 'workflow_status' => $args->workflow_status, - 'cfields' => null, 'tcase_name' => null, - 'platforms' => null); - - if( isset($args->control_panel['filter_custom_fields']) ) { - $filters['cfields'] = $args->control_panel['filter_custom_fields']; - } - - if( isset($args->control_panel['filter_testcase_name']) ) { - $filters['tcase_name'] = - $args->control_panel['filter_testcase_name']; - } - - if( isset($args->control_panel['filter_platforms']) ) { - $filters['platforms'] = - $args->control_panel['filter_platforms']; - } - - - $out = gen_spec_view($db,'testPlanLinking', - $args->tproject_id,$args->object_id, - $tsuite_data['name'], - $tplan_linked_tcversions,null,$filters,$opt); - - $gui->has_tc = ($out['num_tc'] > 0 ? 1 : 0); - $gui->items = $out['spec_view']; - $gui->has_linked_items = $out['has_linked_items']; - $gui->add_custom_fields = $opt['add_custom_fields']; - $gui->drawSavePlatformsButton = false; - $gui->drawSaveCFieldsButton = false; - - if( !is_null($gui->items) ) - { - initDrawSaveButtons($gui); - } - - // This has to be done ONLY AFTER has all data needed => after gen_spec_view() call - setAdditionalGuiData($gui); - - // refresh tree only when action is done - switch ($args->doAction) - { - case 'doReorder': - case 'doSavePlatforms': - case 'doSaveCustomFields': - case 'doAddRemove': - $gui->refreshTree = $args->refreshTree; - break; - - default: - $gui->refreshTree = false; - break; - } - - $smarty->assign('gui', $gui); - $smarty->display($templateCfg->template_dir . 'planAddTC_m1.tpl'); -} elseif ($do_display_coverage) { - if($args->item_level == 'reqcoverage') { - // Select coverage - - $requirement_data = $req_mgr->get_by_id($args->object_id, requirement_mgr::LATEST_VERSION); - $requirement_data_name = $requirement_data[0]['req_doc_id'] . ' : ' . $requirement_data[0]['title']; - // get chekbox value : setting_get_parent_child_relation. - if($_SESSION['setting_get_parent_child_relation']){ - // if checkbox is checked. - $requirements_child = $req_spec_mgr->get_requirement_child_by_id($args->object_id, requirement_mgr::LATEST_VERSION); - } else { - $requirements_child = null; - } - } - elseif($args->item_level == 'reqspeccoverage') { - - // Select folder coverage - $getOptions = array('order_by' => " ORDER BY id"); - //$getFilters = array('status' => VALID_REQ); - $requirements = $req_spec_mgr->get_requirements($args->object_id,'all',null,$getOptions); - } - - // This does filter on keywords ALWAYS in OR mode. - // - // CRITIC: - // We have arrived after clicking in a node of Test Spec Tree where we have two classes of filters - // 1. filters on attribute COMMON to all test case versions => TEST CASE attribute like keyword_id - // 2. filters on attribute that can change on each test case version => execution type. - // - // For attributes at Version Level, filter is done ON LAST ACTIVE version, that can be NOT the VERSION - // already linked to test plan. - // This can produce same weird effects like this: - // - // 1. Test Suite A - create TC1 - Version 1 - exec type MANUAL - // 2. Test Suite A - create TC2 - Version 1 - exec type AUTO - // 3. Test Suite A - create TC3 - Version 1 - exec type MANUAL - // 4. Use feature ADD/REMOVE test cases from test plan. - // 5. Add TC1 - Version 1 to test plan - // 6. Apply filter on exec type AUTO - // 7. Tree will display (Folder) Test Suite A with 1 element - // 8. click on folder, then on RIGHT pane: - // TC2 - Version 1 NOT ASSIGNED TO TEST PLAN is displayed - // 9. Use feature edits test cases, to create a new version for TC1 -> Version 2 - exec type AUTO - // 10. Use feature ADD/REMOVE test cases from test plan. - // 11. Apply filter on exec type AUTO - // 12. Tree will display (Folder) Test Suite A with 2 elements - // 13. click on folder, then on RIGHT pane: - // TC2 - Version 1 NOT ASSIGNED TO TEST PLAN is displayed - // TC1 - Version 2 NOT ASSIGNED TO TEST PLAN is displayed ----> THIS IS RIGHT but WRONG - // Only one TC version can be linked to test plan, and TC1 already is LINKED BUT with VERSION 1. - // Version 2 is displayed because it has EXEC TYPE AUTO - // - // How to solve ? - // Filters regarding this kind of attributes WILL BE NOT APPLIEDED to get linked items - // In this way counters on Test Spec Tree and amount of TC displayed on right pane will be coherent. - // - // 20130426 - // Hmm. But if I do as explained in ' How to solve ?' - // I need to apply this filters on a second step or this filters will not work - // Need to check what I've done - // - $tplan_linked_tcversions = getFilteredLinkedVersions($db,$args,$tplan_mgr,$tcase_mgr,null,false); - - // Add Test Cases to Test plan - Right pane does not honor custom field filter - $testCaseSet = $args->control_panel['filter_tc_id']; - if(!is_null($keywordsFilter) ) { - - // With this pieces we implement the AND type of keyword filter. - $keywordsTestCases = - $tproject_mgr->getKeywordsLatestTCV($args->tproject_id, - $keywordsFilter->items,$keywordsFilter->type); - - if (sizeof($keywordsTestCases)) { - $testCaseSet = array_keys($keywordsTestCases); - } - } - - // Choose enable/disable display of custom fields, analysing if this kind of custom fields - // exists on this test project. - $cfields = - (array)$tsuite_mgr->cfield_mgr->get_linked_cfields_at_testplan_design($args->tproject_id,1,'testcase'); - - $opt = array('write_button_only_if_linked' => 0, 'add_custom_fields' => 0); - $opt['add_custom_fields'] = count($cfields) > 0 ? 1 : 0; - - // Add Test Cases to Test plan - Right pane does not honor custom field filter - // filter by test case execution type - $filters = array('keywords' => $args->keyword_id, 'testcases' => null, - 'exec_type' => $args->executionType, 'importance' => $args->importance, - 'cfields' => $args->control_panel['filter_custom_fields'], - 'tcase_name' => $args->control_panel['filter_testcase_name']); - - if($args->item_level == 'reqcoverage') { - $out = array(); - $out = gen_coverage_view($db,'testPlanLinking',$args->tproject_id,$args->object_id,$requirement_data_name, - $tplan_linked_tcversions,null,$filters,$opt); - - // if requirement, has a child requirement. - if(!is_null($requirements_child)){ - - // get parent name. - $parentName = $requirement_data_name; - - foreach($requirements_child as $key => $req){ - $requirement_data_name = $req['req_doc_id'] . ' : ' . $req['name'] . " " . lang_get('req_rel_is_child_of') . " " . $parentName; - $tmp = gen_coverage_view($db,'testPlanLinking',$args->tproject_id,$req['destination_id'],$requirement_data_name, - $tplan_linked_tcversions,null,$filters,$opt); - // update parent name. - $parentName = $req['req_doc_id'] . ' : ' . $req['name']; - // First requirement without test cases - if (empty($tmp['spec_view'])) - continue; - - if(empty($out)) - { - $out = $tmp; - } - else - { - $tmp['spec_view'][1]["testsuite"] = $tmp['spec_view'][0]['testsuite']; - array_push($out['spec_view'], $tmp['spec_view'][1]); - } - } - } - } - elseif($args->item_level == 'reqspeccoverage') - { - - $out = array(); - foreach($requirements as $key => $req) - { - if(empty($req['req_doc_id'])){ - $coverage_name = $req['doc_id'] . " : " . $req['title']; - } else { - $coverage_name = $req['req_doc_id'] . " : " . $req['title']; - } - $tmp = gen_coverage_view($db,'testPlanLinking',$args->tproject_id,$req['id'], $coverage_name, - $tplan_linked_tcversions,null,$filters,$opt); - - // First requirement without test cases - if (empty($tmp['spec_view'])) - continue; - - if(empty($out)) - { - $out = $tmp; - } - else - { - $tmp['spec_view'][1]["testsuite"] = $tmp['spec_view'][0]['testsuite']; - array_push($out['spec_view'], $tmp['spec_view'][1]); - } - - } - - } - - // count nb testcases selected in view. - $nbTestCaseSelected = 0; - foreach($out['spec_view'][1]['testcases'] as $key => $value) - { - if($value['linked_version_id'] != 0){ - $nbTestCaseSelected++; - } - } - - $out['spec_view'][1]['linked_testcase_qty'] = $nbTestCaseSelected; - $gui->has_tc = ($out['num_tc'] > 0 ? 1 : 0); - $gui->items = $out['spec_view']; - $gui->has_linked_items = $out['has_linked_items']; - $gui->add_custom_fields = $opt['add_custom_fields']; - $gui->drawSavePlatformsButton = false; - $gui->drawSaveCFieldsButton = false; - - if( !is_null($gui->items) ) - { - initDrawSaveButtons($gui); - } - - // This has to be done ONLY AFTER has all data needed => after gen_spec_view() call - setAdditionalGuiData($gui); - - // refresh tree only when action is done - switch ($args->doAction) - { - case 'doReorder': - case 'doSavePlatforms': - case 'doSaveCustomFields': - case 'doAddRemove': - $gui->refreshTree = $args->refreshTree; - break; - - default: - $gui->refreshTree = false; - break; - } - - $smarty->assign('gui', $gui); - $smarty->display($templateCfg->template_dir . 'planAddTC_m1.tpl'); -} - - - - - -/* - function: init_args - creates a sort of namespace - - args: - - returns: object with some REQUEST and SESSION values as members - -*/ -function init_args(&$tproject_mgr) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - - $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : 0; - $args->userID = intval($args->user->dbID); - - $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : intval($_SESSION['testplanID']); - $args->tproject_id = intval($_SESSION['testprojectID']); - $args->tproject_name = $_SESSION['testprojectName']; - - $args->object_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; - $args->item_level = isset($_REQUEST['edit']) ? trim($_REQUEST['edit']) : null; - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : "default"; - $args->testcases2add = isset($_REQUEST['achecked_tc']) ? $_REQUEST['achecked_tc'] : null; - $args->tcversion_for_tcid = isset($_REQUEST['tcversion_for_tcid']) ? $_REQUEST['tcversion_for_tcid'] : null; - $args->testcases2remove = isset($_REQUEST['remove_checked_tc']) ? $_REQUEST['remove_checked_tc'] : null; - - $args->testcases2order = isset($_REQUEST['exec_order']) ? $_REQUEST['exec_order'] : null; - $args->linkedOrder = isset($_REQUEST['linked_exec_order']) ? $_REQUEST['linked_exec_order'] : null; - $args->linkedVersion = isset($_REQUEST['linked_version']) ? $_REQUEST['linked_version'] : null; - $args->linkedWithCF = isset($_REQUEST['linked_with_cf']) ? $_REQUEST['linked_with_cf'] : null; - - $args->feature2fix = isset($_REQUEST['feature2fix']) ? $_REQUEST['feature2fix'] : null; - $args->testerID = isset($_REQUEST['testerID']) ? intval($_REQUEST['testerID']) : 0; - $args->send_mail = isset($_REQUEST['send_mail']) ? $_REQUEST['send_mail'] : false; - - // For more information about the data accessed in session here, see the comment - // in the file header of lib/functions/tlTestCaseFilterControl.class.php. - $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $mode = 'plan_add_mode'; - $pageCache = isset($_SESSION[$mode]) - && isset($_SESSION[$mode][$args->treeFormToken]) ? - $_SESSION[$mode][$args->treeFormToken] : null; - - // need to comunicate with left frame, will do via $_SESSION and form_token - if( $args->treeFormToken > 0 && ($args->item_level == 'testsuite' || $args->item_level == 'testcase')) { - // do not understand why this do not works OK - $_SESSION['loadRightPaneAddTC'][$args->treeFormToken] = false; - } - - - // to be able to pass filters to functions present on specview.php - $args->control_panel = $pageCache; - $getFromSession = !is_null($pageCache); - - $booleankeys = array('refreshTree' => 'setting_refresh_tree_on_action', - 'importance' => 'filter_importance', - 'executionType' => 'filter_execution_type'); - - foreach($booleankeys as $key => $value) { - $args->$key = ($getFromSession && isset($pageCache[$value])) ? $pageCache[$value] : 0; - } - $args->importance = ($args->importance > 0) ? $args->importance : null; - - - // Filter Top level testsuite is implemented in an strange way: - // contains WHAT TO REMOVE - $args->topLevelTestSuite = 0; - if( $getFromSession && isset($pageCache['filter_toplevel_testsuite']) - && count($pageCache['filter_toplevel_testsuite']) > 0) - { - // get all - $first_level_suites = $tproject_mgr->get_first_level_test_suites($args->tproject_id,'simple',array('accessKey' => 'id')); - - - // remove unneeded - $hit = array_diff_key($first_level_suites, $pageCache['filter_toplevel_testsuite']); - $args->topLevelTestSuite = intval(key($hit)); - } - - // This has effect when 'show full (on right pane)' button is used - if($args->tproject_id == $args->object_id && $args->topLevelTestSuite > 0) { - $args->object_id = $args->topLevelTestSuite; - } - - - - $args->keyword_id = 0; - $ak = 'filter_keywords'; - if (isset($pageCache[$ak])) { - $args->keyword_id = $pageCache[$ak]; - if (is_array($args->keyword_id) && count($args->keyword_id) == 1) { - $args->keyword_id = $args->keyword_id[0]; - } - } - - $args->keywordsFilterType = null; - $ak = 'filter_keywords_filter_type'; - if (isset($pageCache[$ak])) { - $args->keywordsFilterType = $pageCache[$ak]; - } - - $args->platform_id = 0; - $ak = 'filter_platforms'; - if (isset($pageCache[$ak])) { - $args->platform_id = $pageCache[$ak]; - } - - $args->keywordsFilterType = null; - $ak = 'filter_keywords_filter_type'; - if (isset($pageCache[$ak])) { - $args->keywordsFilterType = $pageCache[$ak]; - } - - - $ak = 'filter_workflow_status'; - $args->workflow_status = isset($pageCache[$ak]) ? $pageCache[$ak] : null; - - $args->build_id = isset($_REQUEST['build_id']) ? intval($_REQUEST['build_id']) : 0; - $args->activity = isset($_REQUEST['activity']) ? $_REQUEST['activity'] : ''; - - return $args; -} - -/* - function: doReorder - writes to DB execution order of test case versions - linked to testplan. - - args: argsObj: user input data collected via HTML inputs - tplanMgr: testplan manager object - - returns: - - -*/ -function doReorder(&$argsObj,&$tplanMgr) -{ - $mapo = null; - - // Do this to avoid update if order has not been changed on already linked items - if(!is_null($argsObj->linkedVersion)) - { - // Using memory of linked test case, try to get order - foreach($argsObj->linkedVersion as $tcid => $tcversion_id) - { - if($argsObj->linkedOrder[$tcid] != $argsObj->testcases2order[$tcid] ) - { - $mapo[$tcversion_id] = $argsObj->testcases2order[$tcid]; - } - } - } - - // Now add info for new liked test cases if any - if(!is_null($argsObj->testcases2add)) - { - $tcaseSet = array_keys($argsObj->testcases2add); - foreach($tcaseSet as $tcid) - { - // This check is needed because, after we have added test case - // for a platform, this will not be present anymore - // in tcversion_for_tcid, but it's present in linkedVersion. - // IMPORTANT: - // We do not allow link of different test case version on a - // testplan no matter we are using or not platform feature. - // - $tcversion_id=null; - if( isset($argsObj->tcversion_for_tcid[$tcid]) ) - { - $tcversion_id = $argsObj->tcversion_for_tcid[$tcid]; - //$mapo[$tcversion_id] = $argsObj->testcases2order[$tcid]; - } - else if( isset($argsObj->linkedVersion[$tcid]) && - !isset($mapo[$argsObj->linkedVersion[$tcid]])) - { - $tcversion_id = $argsObj->linkedVersion[$tcid]; - } - if( !is_null($tcversion_id)) - { - $mapo[$tcversion_id] = $argsObj->testcases2order[$tcid]; - } - } - } - - if(!is_null($mapo)) - { - $tplanMgr->setExecutionOrder($argsObj->tplan_id,$mapo); - } - -} - - -/* - function: initializeGui - - args : - - returns: - -*/ -function initializeGui(&$dbHandler,$argsObj,&$tplanMgr,&$tcaseMgr) -{ - - $tcase_cfg = config_get('testcase_cfg'); - $title_separator = config_get('gui_title_separator_1'); - - $gui = new stdClass(); - $gui->status_feedback = buildStatusFeedbackMsg(); - - $gui->testCasePrefix = $tcaseMgr->tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - $gui->testCasePrefix .= $tcase_cfg->glue_character; - - $gui->can_remove_executed_testcases = $argsObj->user->hasRight($dbHandler, - "testplan_unlink_executed_testcases", - $argsObj->tproject_id,$argsObj->tplan_id); - - $tprojectInfo = $tcaseMgr->tproject_mgr->get_by_id($argsObj->tproject_id); - $gui->priorityEnabled = $tprojectInfo['opt']->testPriorityEnabled; - - // $gui->keywordsFilterType = $argsObj->keywordsFilterType; - // $gui->keywords_filter = ''; - $gui->has_tc = 0; - $gui->items = null; - $gui->has_linked_items = false; - - $gui->keywordsFilterType = new stdClass(); - $gui->keywordsFilterType->options = array('OR' => 'Or' , 'AND' =>'And'); - $gui->keywordsFilterType->selected=$argsObj->keywordsFilterType; - - $gui->keyword_id = $argsObj->keyword_id; - $gui->keywords_filter_feedback = ''; - if( !is_null($gui->keyword_id) && $gui->keyword_id != 0 ) { - $gui->keywords_filter_feedback = - buildKeywordsFeedbackMsg($dbHandler,$argsObj,$gui); - } - - $gui->platform_id = $argsObj->platform_id; - $gui->platforms_filter_feedback = ''; - if( !is_null($gui->platform_id) && $gui->platform_id != 0 ) { - $gui->platforms_filter_feedback = - buildPlatformsFeedbackMsg($dbHandler,$argsObj,$gui); - } - - - // full_control, controls the operations planAddTC_m1.tpl will allow - // 1 => add/remove - // 0 => just remove - $gui->full_control = 1; - - $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->testPlanName = $tplan_info['name']; - $gui->pageTitle = lang_get('test_plan') . $title_separator . $gui->testPlanName; - $gui->refreshTree = $argsObj->refreshTree; - $gui->canAssignExecTask = $argsObj->user->hasRight($dbHandler,"exec_assign_testcases",$argsObj->tproject_id,$argsObj->tplan_id); - - $tproject_mgr = new testproject($dbHandler); - $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); - - $gui->testers = getTestersForHtmlOptions($dbHandler,$argsObj->tplan_id, $tproject_info); - $gui->testerID = $argsObj->testerID; - $gui->send_mail = $argsObj->send_mail; - $gui->send_mail_checked = ''; - if($gui->send_mail) { - $gui->send_mail_checked = ' checked="checked" '; - } - - $platform_mgr = new tlPlatform($dbHandler, $argsObj->tproject_id); - $gui->platforms = $platform_mgr->getLinkedToTestplan($argsObj->tplan_id); - $gui->platformsForHtmlOptions = null; - $gui->usePlatforms = $platform_mgr->platformsActiveForTestplan($argsObj->tplan_id); - if($gui->usePlatforms) { - // Create options for two different select boxes. $bulk_platforms - // has "All platforms" on top and "$platformsForHtmlOptions" has an - // empty item - $gui->platformsForHtmlOptions[0]=''; - foreach($gui->platforms as $elem) - { - $gui->platformsForHtmlOptions[$elem['id']] =$elem['name']; - } - - $optLTT = null; - $gui->bulk_platforms = $platform_mgr->getLinkedToTestplanAsMap($argsObj->tplan_id,$optLTT); - $gui->bulk_platforms[0] = lang_get("all_platforms"); - ksort($gui->bulk_platforms); - } - - // - $gui->warning_msg = new stdClass(); - $gui->warning_msg->executed = lang_get('executed_can_not_be_removed'); - if( $gui->can_remove_executed_testcases ) - { - $gui->warning_msg->executed = lang_get('has_been_executed'); - } - - $gui->build = init_build_selector($tplanMgr, $argsObj); - - return $gui; -} - - -/* - function: doSaveCustomFields - writes to DB value of custom fields displayed - for test case versions linked to testplan. - - args: argsObj: user input data collected via HTML inputs - tplanMgr: testplan manager object - - returns: - - -*/ -function doSaveCustomFields(&$argsObj,&$userInput,&$tplanMgr,&$tcaseMgr) -{ - // N.B.: I've use this piece of code also on write_execution(), think is time to create - // a method on cfield_mgr class. - // One issue: find a good method name - $cf_prefix = $tcaseMgr->cfield_mgr->get_name_prefix(); - $len_cfp = tlStringLen($cf_prefix); - $cf_nodeid_pos=4; - - $nodeid_array_cfnames=null; - - // Example: two test cases (21 and 19 are testplan_tcversions.id => FEATURE_ID) - // with 3 custom fields - // - // custom_field_[TYPE]_[CFIELD_ID]_[FEATURE_ID] - // - // ( - // [21] => Array - // ( - // [0] => custom_field_0_3_21 - // [1] => custom_field_0_7_21 - // [5] => custom_field_6_9_21 - // ) - // - // [19] => Array - // ( - // [0] => custom_field_0_3_19 - // [1] => custom_field_0_7_19 - // [5] => custom_field_6_9_19 - // ) - // ) - // - foreach($userInput as $input_name => $value) - { - if( strncmp($input_name,$cf_prefix,$len_cfp) == 0 ) - { - $dummy=explode('_',$input_name); - $nodeid_array_cfnames[$dummy[$cf_nodeid_pos]][]=$input_name; - } - } - - // foreach($argsObj->linkedWithCF as $key => $link_id) - foreach( $nodeid_array_cfnames as $link_id => $customFieldsNames) - { - - - // Create a SubSet of userInput just with inputs regarding CF for a link_id - // Example for link_id=21: - // - // $cfvalues=( 'custom_field_0_3_21' => A - // 'custom_field_0_7_21' => - // 'custom_field_8_8_21_day' => 0 - // 'custom_field_8_8_21_month' => 0 - // 'custom_field_8_8_21_year' => 0 - // 'custom_field_6_9_21_' => Every day) - // - $cfvalues=null; - foreach($customFieldsNames as $cf) - { - $cfvalues[$cf]=$userInput[$cf]; - } - $tcaseMgr->cfield_mgr->testplan_design_values_to_db($cfvalues,null,$link_id); - } -} - - -/* - function: doSavePlatforms - writes to DB execution ... of test case versions linked to testplan. - - args: argsObj: user input data collected via HTML inputs - tplanMgr: testplan manager object - - returns: - - -*/ -function doSavePlatforms(&$argsObj,&$tplanMgr) -{ - foreach($argsObj->feature2fix as $feature_id => $tcversion_platform) { - $tcversion_id = key($tcversion_platform); - $platform_id = current($tcversion_platform); - if( $platform_id != 0 ) { - $tplanMgr->changeLinkedTCVersionsPlatform($argsObj->tplan_id,0,$platform_id,$tcversion_id); - } - } -} - - -/** - * send_mail_to_testers - * - * - * @return void - */ -function send_mail_to_testers(&$dbHandler,&$tcaseMgr,&$guiObj,&$argsObj,$features,$operation) -{ - $testers['new']=null; - $mail_details['new']=lang_get('mail_testcase_assigned') . "

    "; - $mail_subject['new']=lang_get('mail_subject_testcase_assigned'); - $use_testers['new']= true ; - - $tcaseSet=null; - $tcnames=null; - $email=array(); - - $userSet[]=$argsObj->userID; - $userSet[]=$argsObj->testerID; - - $userData=tlUser::getByIDs($dbHandler,$userSet); - $assigner=$userData[$argsObj->userID]->firstName . ' ' . $userData[$argsObj->userID]->lastName ; - - $email['from_address']=config_get('from_email'); - $body_first_lines = lang_get('testproject') . ': ' . $argsObj->tproject_name . '
    ' . - lang_get('testplan') . ': ' . $guiObj->testPlanName .'

    '; - - // Get testers id - foreach($features as $feature_id => $value) - { - if($use_testers['new']) - { - $testers['new'][$value['user_id']][$value['tcase_id']]=$value['tcase_id']; - } - $tcaseSet[$value['tcase_id']]=$value['tcase_id']; - $tcversionSet[$value['tcversion_id']]=$value['tcversion_id']; - } - - $infoSet=$tcaseMgr->get_by_id_bulk($tcaseSet,$tcversionSet); - foreach($infoSet as $value) - { - $tcnames[$value['testcase_id']] = $guiObj->testCasePrefix . $value['tc_external_id'] . ' ' . $value['name']; - } - - $path_info = $tcaseMgr->tree_manager->get_full_path_verbose($tcaseSet,array('output_format' => 'simple')); - $flat_path=null; - foreach($path_info as $tcase_id => $pieces) - { - $flat_path[$tcase_id]=implode('/',$pieces) . '/' . $tcnames[$tcase_id]; - } - - $validator = new Zend_Validate_EmailAddress(); - foreach($testers as $tester_type => $tester_set) - { - if( !is_null($tester_set) ) - { - $email['subject'] = $mail_subject[$tester_type] . ' ' . $guiObj->testPlanName; - foreach($tester_set as $user_id => $value) - { - $userObj=$userData[$user_id]; - $email['to_address'] = trim($userObj->emailAddress); - if($email['to_address'] == '' || !$validator->isValid($email['to_address'])) - { - continue; - } - - $email['body'] = $body_first_lines; - $email['body'] .= sprintf($mail_details[$tester_type], - $userObj->firstName . ' ' .$userObj->lastName,$assigner); - - foreach($value as $tcase_id) - { - $email['body'] .= $flat_path[$tcase_id] . '
    '; - } - $email['body'] .= '
    ' . date(DATE_RFC1123); - - $email['cc'] = ''; - $email['attachment'] = null; - $email['exit_on_error'] = true; - $email['htmlFormat'] = true; - - $email_op = email_send($email['from_address'], $email['to_address'], - $email['subject'], $email['body'], - $email['cc'],$email['attachment'], - $email['exit_on_error'],$email['htmlFormat']); - } - } - } -} - - -/** - * initDrawSaveButtons - * - */ -function initDrawSaveButtons(&$guiObj) -{ - $keySet = array_keys($guiObj->items); - - // 20100225 - eloff - BUGID 3205 - check only when platforms are active - // Logic to initialize drawSavePlatformsButton. - if ($guiObj->usePlatforms) - { - // Looks for a platform with id = 0 - foreach($keySet as $key) - { - $breakLoop = false; - $testSuite = &$guiObj->items[$key]; - if($testSuite['linked_testcase_qty'] > 0) - { - $tcaseSet = array_keys($testSuite['testcases']); - foreach($tcaseSet as $tcaseKey) - { - if( isset($testSuite['testcases'][$tcaseKey]['feature_id'][0]) ) - { - $breakLoop = true; - $guiObj->drawSavePlatformsButton = true; - break; - } - } - } - if( $breakLoop ) - { - break; - } - } - } - - // Logic to initialize drawSaveCFieldsButton - reset($keySet); - foreach($keySet as $key) - { - $breakLoop = false; - $tcaseSet = &$guiObj->items[$key]['testcases']; - if( !is_null($tcaseSet) ) - { - $tcversionSet = array_keys($tcaseSet); - foreach($tcversionSet as $tcversionID) - { - if( isset($tcaseSet[$tcversionID]['custom_fields']) && - !is_null($tcaseSet[$tcversionID]['custom_fields'])) - { - $breakLoop = true; - $guiObj->drawSaveCFieldsButton = true; - break; - } - } - } - if( $breakLoop ) - { - break; - } - } -} - - -/** - * - * - */ -function setAdditionalGuiData($guiObj) -{ - $actionTitle = 'title_remove_test_from_plan'; - $buttonValue = 'btn_remove_selected_tc'; - $guiObj->exec_order_input_disabled = 'disabled="disabled"'; - - if( $guiObj->full_control ) - { - $actionTitle = 'title_add_test_to_plan'; - $buttonValue = 'btn_add_selected_tc'; - if( $guiObj->has_linked_items ) - { - $actionTitle = 'title_add_remove_test_to_plan'; - $buttonValue = 'btn_add_remove_selected_tc'; - } - $guiObj->exec_order_input_disabled = ' '; - } - $guiObj->actionTitle = lang_get($actionTitle); - $guiObj->buttonValue = lang_get($buttonValue); -} - -/** - * Initialize the HTML select box for selection of a build to which - * user wants to assign testers which are added to testplan. - * - * @author Andreas Simon - * @param testplan $testplan_mgr reference to testplan manager object - * @param object $argsObj reference to user input object - * @return array $html_menu array structure with all information needed for the menu - */ -function init_build_selector(&$testplan_mgr, &$argsObj) { - - // init array - $menu = array('items' => null, 'selected' => null, 'count' => 0); - - $menu['items'] = - (array)$testplan_mgr->get_builds_for_html_options($argsObj->tplan_id, - testplan::GET_ACTIVE_BUILD, - testplan::GET_OPEN_BUILD); - $menu['count'] = count($menu['items']); - - // if no build has been chosen yet, select the newest build by default - $build_id = $argsObj->build_id; - if (!$build_id && $menu['count']) { - $keys = array_keys($menu['items']); - $build_id = end($keys); - } - $menu['selected'] = $build_id; - - return $menu; -} // end of method - -/** - * - */ -function addToTestPlan(&$dbHandler,&$argsObj,&$guiObj,&$tplanMgr,&$tcaseMgr) { - // items_to_link structure: - // key: test case id , value: map - // key: platform_id value: test case VERSION ID - $items_to_link = null; - foreach ($argsObj->testcases2add as $tcase_id => $info) { - foreach ($info as $platform_id => $tcase_id) { - if( isset($argsObj->tcversion_for_tcid[$tcase_id]) ){ - $tcversion_id = $argsObj->tcversion_for_tcid[$tcase_id]; - } else { - $tcversion_id = $argsObj->linkedVersion[$tcase_id]; - } - $items_to_link['tcversion'][$tcase_id] = $tcversion_id; - $items_to_link['platform'][$platform_id] = $platform_id; - $items_to_link['items'][$tcase_id][$platform_id] = $tcversion_id; - } - } - - $linked_features=$tplanMgr->link_tcversions($argsObj->tplan_id,$items_to_link,$argsObj->userID); - - if( $argsObj->testerID > 0 ) { - $features2add = null; - $status_map = $tplanMgr->assignment_mgr->get_available_status(); - $types_map = $tplanMgr->assignment_mgr->get_available_types(); - $db_now = $dbHandler->db_now(); - $tcversion_tcase = array_flip($items_to_link['tcversion']); - - $getOpt = array('outputFormat' => 'map', 'addIfNull' => true); - $platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,$getOpt); - - foreach($linked_features as $platform_id => $tcversion_info) - { - foreach($tcversion_info as $tcversion_id => $feature_id) - { - $features2['add'][$feature_id]['user_id'] = $argsObj->testerID; - $features2['add'][$feature_id]['type'] = $types_map['testcase_execution']['id']; - $features2['add'][$feature_id]['status'] = $status_map['open']['id']; - $features2['add'][$feature_id]['assigner_id'] = $argsObj->userID; - $features2['add'][$feature_id]['tcase_id'] = $tcversion_tcase[$tcversion_id]; - $features2['add'][$feature_id]['tcversion_id'] = $tcversion_id; - $features2['add'][$feature_id]['creation_ts'] = $db_now; - $features2['add'][$feature_id]['platform_name'] = $platformSet[$platform_id]; - $features2['add'][$feature_id]['build_id'] = $argsObj->build_id; - } - } - - foreach($features2 as $key => $values) - { - $tplanMgr->assignment_mgr->assign($values); - $called[$key]=true; - } - - if($argsObj->send_mail) - { - foreach($called as $ope => $ope_status) - { - if($ope_status) - { - send_mail_to_testers($dbHandler,$tcaseMgr,$guiObj,$argsObj,$features2['add'],$ope); - } - } - } - } -} - -/** - * - * - */ -function buildStatusFeedbackMsg() -{ - $ret = ''; - $hideStatusSet = config_get('tplanDesign')->hideTestCaseWithStatusIn; - if( !is_null($hideStatusSet) ) - { - $cfx = getConfigAndLabels('testCaseStatus'); - $sc = array_flip($cfx['cfg']); - $msg = array(); - foreach( $hideStatusSet as $code => $verbose) - { - $msg[] = $cfx['lbl'][$sc[$code]]; - } - $ret = - sprintf(lang_get('hint_add_testcases_to_testplan_status'),implode(',',$msg)); - } - - return $ret; -} - -/** - * - */ -function buildKeywordsFeedbackMsg(&$dbHandler,&$argsObj,&$gui) -{ - $opx = array('tproject_id' => $argsObj->tproject_id, - 'cols' => 'id,keyword', 'accessKey' => 'id'); - - $kwSet = tlKeyword::getSimpleSet($dbHandler, $opx); - $msg = array(); - $k2s = (array)$gui->keyword_id; - foreach( $k2s as $idt ) { - $msg[] = $kwSet[$idt]['keyword']; - } - return implode(',',$msg); -} - -/** - * - */ -function buildPlatformsFeedbackMsg(&$dbHandler,&$argsObj,&$gui) -{ - $opx = array('fields' => 'id,name', 'accessKey' => 'id'); - - $platMgr = new tlPlatform($dbHandler, $argsObj->tproject_id); - $k2s = (array)$gui->platform_id; - $ixSet = $platMgr->getByID($k2s, $opx); - $msg = array(); - foreach( $k2s as $idt ) { - $msg[] = $ixSet[$idt]['name']; - } - return implode(',',$msg); +keyword_id)) { + $keywordsFilter = new stdClass(); + $keywordsFilter->items = $args->keyword_id; + $keywordsFilter->type = $gui->keywordsFilterType->selected; +} + +$do_display = 0; +$do_display_coverage = 0; + +switch ($args->item_level) { + case 'testsuite': + case 'req': + case 'req_spec': + case 'testproject': + $do_display = 1; + break; + + case 'reqcoverage': + case 'reqspeccoverage': + $do_display_coverage = 1; + break; + + default: + break; +} + +switch ($args->doAction) { + case 'doAddRemove': + // Remember: checkboxes exist only if are checked + $gui->itemQty = count((array) $args->testcases2add); + + if (! is_null($args->testcases2add)) { + addToTestPlan($db, $args, $gui, $tplan_mgr, $tcaseMgr); + } + + if (! is_null($args->testcases2remove)) { + // remove without warning + $items_to_unlink = null; + foreach ($args->testcases2remove as $tcase_id => $info) { + foreach ($info as $platform_id => $tcversion_id) { + $items_to_unlink['tcversion'][$tcase_id] = $tcversion_id; + $items_to_unlink['platform'][$platform_id] = $platform_id; + $items_to_unlink['items'][$tcase_id][$platform_id] = $tcversion_id; + } + } + $tplan_mgr->unlink_tcversions($args->tplan_id, $items_to_unlink); + } + + doReorder($args, $tplan_mgr); + break; + + case 'doReorder': + doReorder($args, $tplan_mgr); + break; + + case 'doSavePlatforms': + doSavePlatforms($args, $tplan_mgr); + break; + + case 'doSaveCustomFields': + doSaveCustomFields($args, $_REQUEST, $tplan_mgr, $tcaseMgr); + break; + + default: + break; +} + +$smarty = new TLSmarty(); + +if ($do_display) { + $tsuite_data = $tsuite_mgr->get_by_id($args->object_id); + // see development documentation on [INSTALL DIR]/docs/development/planAddTC.php.txt + $tplan_linked_tcversions = getFilteredLinkedVersions($db, $args, $tplan_mgr, + $tcaseMgr, array( + 'addImportance' => true + )); + + // Add Test Cases to Test plan - Right pane does not honor custom field filter + $testCaseSet = $args->control_panel['filter_tc_id']; + if (! is_null($keywordsFilter)) { + + // With this pieces we implement the AND type of keyword filter. + $keywordsTestCases = $tproject_mgr->getKeywordsLatestTCV( + $args->tproject_id, $keywordsFilter->items, $keywordsFilter->type); + + if (count($keywordsTestCases)) { + $testCaseSet = array_keys($keywordsTestCases); + } + } + + // Choose enable/disable display of custom fields, analysing if this kind of custom fields + // exists on this test project. + $cfields = (array) $tsuite_mgr->cfield_mgr->get_linked_cfields_at_testplan_design( + $args->tproject_id, 1, 'testcase'); + $opt = array( + 'write_button_only_if_linked' => 0, + 'add_custom_fields' => 0 + ); + $opt['add_custom_fields'] = count($cfields) > 0 ? 1 : 0; + + // Add Test Cases to Test plan - Right pane does not honor custom field filter + // filter by test case execution type + $filters = array( + 'keywords' => $args->keyword_id, + 'testcases' => $testCaseSet, + 'exec_type' => $args->executionType, + 'importance' => $args->importance, + 'workflow_status' => $args->workflow_status, + 'cfields' => null, + 'tcase_name' => null, + 'platforms' => null + ); + + if (isset($args->control_panel['filter_custom_fields'])) { + $filters['cfields'] = $args->control_panel['filter_custom_fields']; + } + + if (isset($args->control_panel['filter_testcase_name'])) { + $filters['tcase_name'] = $args->control_panel['filter_testcase_name']; + } + + if (isset($args->control_panel['filter_platforms'])) { + $filters['platforms'] = $args->control_panel['filter_platforms']; + } + + $out = gen_spec_view($db, 'testPlanLinking', $args->tproject_id, + $args->object_id, $tsuite_data['name'], $tplan_linked_tcversions, null, + $filters, $opt); + + $gui->has_tc = ($out['num_tc'] > 0 ? 1 : 0); + $gui->items = $out['spec_view']; + $gui->has_linked_items = $out['has_linked_items']; + $gui->add_custom_fields = $opt['add_custom_fields']; + $gui->drawSavePlatformsButton = false; + $gui->drawSaveCFieldsButton = false; + + if (! is_null($gui->items)) { + initDrawSaveButtons($gui); + } + + // This has to be done ONLY AFTER has all data needed => after gen_spec_view() call + setAdditionalGuiData($gui); + + // refresh tree only when action is done + switch ($args->doAction) { + case 'doReorder': + case 'doSavePlatforms': + case 'doSaveCustomFields': + case 'doAddRemove': + $gui->refreshTree = $args->refreshTree; + break; + + default: + $gui->refreshTree = false; + break; + } + + $smarty->assign('gui', $gui); + $smarty->display($templateCfg->template_dir . 'planAddTC_m1.tpl'); +} elseif ($do_display_coverage) { + if ($args->item_level == 'reqcoverage') { + // Select coverage + + $requirement_data = $req_mgr->get_by_id($args->object_id, + requirement_mgr::LATEST_VERSION); + $requirement_data_name = $requirement_data[0]['req_doc_id'] . ' : ' . + $requirement_data[0]['title']; + // get chekbox value : setting_get_parent_child_relation. + if ($_SESSION['setting_get_parent_child_relation']) { + // if checkbox is checked. + $requirements_child = $req_spec_mgr->get_requirement_child_by_id( + $args->object_id, requirement_mgr::LATEST_VERSION); + } else { + $requirements_child = null; + } + } elseif ($args->item_level == 'reqspeccoverage') { + + // Select folder coverage + $getOptions = array( + 'order_by' => " ORDER BY id" + ); + $requirements = $req_spec_mgr->get_requirements($args->object_id, 'all', + null, $getOptions); + } + + // This does filter on keywords ALWAYS in OR mode. + // + // CRITIC: + // We have arrived after clicking in a node of Test Spec Tree where we have two classes of filters + // 1. filters on attribute COMMON to all test case versions => TEST CASE attribute like keyword_id + // 2. filters on attribute that can change on each test case version => execution type. + // + // For attributes at Version Level, filter is done ON LAST ACTIVE version, that can be NOT the VERSION + // already linked to test plan. + // This can produce same weird effects like this: + // + // 1. Test Suite A - create TC1 - Version 1 - exec type MANUAL + // 2. Test Suite A - create TC2 - Version 1 - exec type AUTO + // 3. Test Suite A - create TC3 - Version 1 - exec type MANUAL + // 4. Use feature ADD/REMOVE test cases from test plan. + // 5. Add TC1 - Version 1 to test plan + // 6. Apply filter on exec type AUTO + // 7. Tree will display (Folder) Test Suite A with 1 element + // 8. click on folder, then on RIGHT pane: + // TC2 - Version 1 NOT ASSIGNED TO TEST PLAN is displayed + // 9. Use feature edits test cases, to create a new version for TC1 -> Version 2 - exec type AUTO + // 10. Use feature ADD/REMOVE test cases from test plan. + // 11. Apply filter on exec type AUTO + // 12. Tree will display (Folder) Test Suite A with 2 elements + // 13. click on folder, then on RIGHT pane: + // TC2 - Version 1 NOT ASSIGNED TO TEST PLAN is displayed + // TC1 - Version 2 NOT ASSIGNED TO TEST PLAN is displayed ----> THIS IS RIGHT but WRONG + // Only one TC version can be linked to test plan, and TC1 already is LINKED BUT with VERSION 1. + // Version 2 is displayed because it has EXEC TYPE AUTO + // + // How to solve ? + // Filters regarding this kind of attributes WILL BE NOT APPLIEDED to get linked items + // In this way counters on Test Spec Tree and amount of TC displayed on right pane will be coherent. + // + // 20130426 + // Hmm. But if I do as explained in ' How to solve ?' + // I need to apply this filters on a second step or this filters will not work + // Need to check what I've done + // + $tplan_linked_tcversions = getFilteredLinkedVersions($db, $args, $tplan_mgr, + $tcaseMgr, null, false); + + // Add Test Cases to Test plan - Right pane does not honor custom field filter + $testCaseSet = $args->control_panel['filter_tc_id']; + if (! is_null($keywordsFilter)) { + + // With this pieces we implement the AND type of keyword filter. + $keywordsTestCases = $tproject_mgr->getKeywordsLatestTCV( + $args->tproject_id, $keywordsFilter->items, $keywordsFilter->type); + + if (count($keywordsTestCases)) { + $testCaseSet = array_keys($keywordsTestCases); + } + } + + // Choose enable/disable display of custom fields, analysing if this kind of custom fields + // exists on this test project. + $cfields = (array) $tsuite_mgr->cfield_mgr->get_linked_cfields_at_testplan_design( + $args->tproject_id, 1, 'testcase'); + + $opt = array( + 'write_button_only_if_linked' => 0, + 'add_custom_fields' => 0 + ); + $opt['add_custom_fields'] = count($cfields) > 0 ? 1 : 0; + + // Add Test Cases to Test plan - Right pane does not honor custom field filter + // filter by test case execution type + $filters = array( + 'keywords' => $args->keyword_id, + 'testcases' => null, + 'exec_type' => $args->executionType, + 'importance' => $args->importance, + 'cfields' => $args->control_panel['filter_custom_fields'], + 'tcase_name' => $args->control_panel['filter_testcase_name'] + ); + + if ($args->item_level == 'reqcoverage') { + $out = array(); + $out = gen_coverage_view($db, 'testPlanLinking', $args->tproject_id, + $args->object_id, $requirement_data_name, $tplan_linked_tcversions, + null, $filters, $opt); + + // if requirement, has a child requirement. + if (! is_null($requirements_child)) { + + // get parent name. + $parentName = $requirement_data_name; + + foreach ($requirements_child as $req) { + $requirement_data_name = $req['req_doc_id'] . ' : ' . + $req['name'] . " " . lang_get('req_rel_is_child_of') . " " . + $parentName; + $tmp = gen_coverage_view($db, 'testPlanLinking', + $args->tproject_id, $req['destination_id'], + $requirement_data_name, $tplan_linked_tcversions, null, + $filters, $opt); + // update parent name. + $parentName = $req['req_doc_id'] . ' : ' . $req['name']; + // First requirement without test cases + if (empty($tmp['spec_view'])) { + continue; + } + if (empty($out)) { + $out = $tmp; + } else { + $tmp['spec_view'][1]["testsuite"] = $tmp['spec_view'][0]['testsuite']; + array_push($out['spec_view'], $tmp['spec_view'][1]); + } + } + } + } elseif ($args->item_level == 'reqspeccoverage') { + + $out = array(); + foreach ($requirements as $req) { + if (empty($req['req_doc_id'])) { + $coverage_name = $req['doc_id'] . " : " . $req['title']; + } else { + $coverage_name = $req['req_doc_id'] . " : " . $req['title']; + } + $tmp = gen_coverage_view($db, 'testPlanLinking', $args->tproject_id, + $req['id'], $coverage_name, $tplan_linked_tcversions, null, + $filters, $opt); + + // First requirement without test cases + if (empty($tmp['spec_view'])) { + continue; + } + + if (empty($out)) { + $out = $tmp; + } else { + $tmp['spec_view'][1]["testsuite"] = $tmp['spec_view'][0]['testsuite']; + array_push($out['spec_view'], $tmp['spec_view'][1]); + } + } + } + + // count nb testcases selected in view. + $nbTestCaseSelected = 0; + foreach ($out['spec_view'][1]['testcases'] as $value) { + if ($value['linked_version_id'] != 0) { + $nbTestCaseSelected ++; + } + } + + $out['spec_view'][1]['linked_testcase_qty'] = $nbTestCaseSelected; + $gui->has_tc = ($out['num_tc'] > 0 ? 1 : 0); + $gui->items = $out['spec_view']; + $gui->has_linked_items = $out['has_linked_items']; + $gui->add_custom_fields = $opt['add_custom_fields']; + $gui->drawSavePlatformsButton = false; + $gui->drawSaveCFieldsButton = false; + + if (! is_null($gui->items)) { + initDrawSaveButtons($gui); + } + + // This has to be done ONLY AFTER has all data needed => after gen_spec_view() call + setAdditionalGuiData($gui); + + // refresh tree only when action is done + switch ($args->doAction) { + case 'doReorder': + case 'doSavePlatforms': + case 'doSaveCustomFields': + case 'doAddRemove': + $gui->refreshTree = $args->refreshTree; + break; + + default: + $gui->refreshTree = false; + break; + } + + $smarty->assign('gui', $gui); + $smarty->display($templateCfg->template_dir . 'planAddTC_m1.tpl'); +} + +/** + * Get input from user and return it in some sort of namespace + * + * @param testproject $tproject_mgr + * @return stdClass object with some REQUEST and SESSION values as members + */ +function initArgs(&$tproject_mgr) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + + $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : 0; + $args->userID = intval($args->user->dbID); + + $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : intval($_SESSION['testplanID']); + $args->tproject_id = intval($_SESSION['testprojectID']); + $args->tproject_name = $_SESSION['testprojectName']; + + $args->object_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; + $args->item_level = isset($_REQUEST['edit']) ? trim($_REQUEST['edit']) : null; + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : "default"; + $args->testcases2add = isset($_REQUEST['achecked_tc']) ? $_REQUEST['achecked_tc'] : null; + $args->tcversion_for_tcid = isset($_REQUEST['tcversion_for_tcid']) ? $_REQUEST['tcversion_for_tcid'] : null; + $args->testcases2remove = isset($_REQUEST['remove_checked_tc']) ? $_REQUEST['remove_checked_tc'] : null; + + $args->testcases2order = isset($_REQUEST['exec_order']) ? $_REQUEST['exec_order'] : null; + $args->linkedOrder = isset($_REQUEST['linked_exec_order']) ? $_REQUEST['linked_exec_order'] : null; + $args->linkedVersion = isset($_REQUEST['linked_version']) ? $_REQUEST['linked_version'] : null; + $args->linkedWithCF = isset($_REQUEST['linked_with_cf']) ? $_REQUEST['linked_with_cf'] : null; + + $args->feature2fix = isset($_REQUEST['feature2fix']) ? $_REQUEST['feature2fix'] : null; + $args->testerID = isset($_REQUEST['testerID']) ? intval( + $_REQUEST['testerID']) : 0; + $args->send_mail = isset($_REQUEST['send_mail']) ? $_REQUEST['send_mail'] : false; + + // For more information about the data accessed in session here, see the comment + // in the file header of lib/functions/tlTestCaseFilterControl.class.php. + $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $mode = 'plan_add_mode'; + $pageCache = isset($_SESSION[$mode]) && + isset($_SESSION[$mode][$args->treeFormToken]) ? $_SESSION[$mode][$args->treeFormToken] : null; + + // need to comunicate with left frame, will do via $_SESSION and form_token + if ($args->treeFormToken > 0 && + ($args->item_level == 'testsuite' || $args->item_level == 'testcase')) { + // do not understand why this do not works OK + $_SESSION['loadRightPaneAddTC'][$args->treeFormToken] = false; + } + + // to be able to pass filters to functions present on specview.php + $args->control_panel = $pageCache; + $getFromSession = ! is_null($pageCache); + + $booleankeys = array( + 'refreshTree' => 'setting_refresh_tree_on_action', + 'importance' => 'filter_importance', + 'executionType' => 'filter_execution_type' + ); + + foreach ($booleankeys as $key => $value) { + $args->$key = ($getFromSession && isset($pageCache[$value])) ? $pageCache[$value] : 0; + } + $args->importance = ($args->importance > 0) ? $args->importance : null; + + // Filter Top level testsuite is implemented in an strange way: + // contains WHAT TO REMOVE + $args->topLevelTestSuite = 0; + if ($getFromSession && isset($pageCache['filter_toplevel_testsuite']) && + count($pageCache['filter_toplevel_testsuite']) > 0) { + // get all + $first_level_suites = $tproject_mgr->get_first_level_test_suites( + $args->tproject_id, 'simple', array( + 'accessKey' => 'id' + )); + + // remove unneeded + $hit = array_diff_key($first_level_suites, + $pageCache['filter_toplevel_testsuite']); + $args->topLevelTestSuite = intval(key($hit)); + } + + // This has effect when 'show full (on right pane)' button is used + if ($args->tproject_id == $args->object_id && $args->topLevelTestSuite > 0) { + $args->object_id = $args->topLevelTestSuite; + } + + $args->keyword_id = 0; + $ak = 'filter_keywords'; + if (isset($pageCache[$ak])) { + $args->keyword_id = $pageCache[$ak]; + if (is_array($args->keyword_id) && count($args->keyword_id) == 1) { + $args->keyword_id = $args->keyword_id[0]; + } + } + + $args->keywordsFilterType = null; + $ak = 'filter_keywords_filter_type'; + if (isset($pageCache[$ak])) { + $args->keywordsFilterType = $pageCache[$ak]; + } + + $args->platform_id = 0; + $ak = 'filter_platforms'; + if (isset($pageCache[$ak])) { + $args->platform_id = $pageCache[$ak]; + } + + $args->keywordsFilterType = null; + $ak = 'filter_keywords_filter_type'; + if (isset($pageCache[$ak])) { + $args->keywordsFilterType = $pageCache[$ak]; + } + + $ak = 'filter_workflow_status'; + $args->workflow_status = isset($pageCache[$ak]) ? $pageCache[$ak] : null; + + $args->build_id = isset($_REQUEST['build_id']) ? intval( + $_REQUEST['build_id']) : 0; + $args->activity = isset($_REQUEST['activity']) ? $_REQUEST['activity'] : ''; + + return $args; +} + +/* + * function: doReorder + * writes to DB execution order of test case versions + * linked to testplan. + * + * args: argsObj: user input data collected via HTML inputs + * tplanMgr: testplan manager object + * + * returns: - + * + */ +function doReorder(&$argsObj, &$tplanMgr) +{ + $mapo = null; + + // Do this to avoid update if order has not been changed on already linked items + if (! is_null($argsObj->linkedVersion)) { + // Using memory of linked test case, try to get order + foreach ($argsObj->linkedVersion as $tcid => $tcversion_id) { + if ($argsObj->linkedOrder[$tcid] != $argsObj->testcases2order[$tcid]) { + $mapo[$tcversion_id] = $argsObj->testcases2order[$tcid]; + } + } + } + + // Now add info for new liked test cases if any + if (! is_null($argsObj->testcases2add)) { + $tcaseSet = array_keys($argsObj->testcases2add); + foreach ($tcaseSet as $tcid) { + // This check is needed because, after we have added test case + // for a platform, this will not be present anymore + // in tcversion_for_tcid, but it's present in linkedVersion. + // IMPORTANT: + // We do not allow link of different test case version on a + // testplan no matter we are using or not platform feature. + // + $tcversion_id = null; + if (isset($argsObj->tcversion_for_tcid[$tcid])) { + $tcversion_id = $argsObj->tcversion_for_tcid[$tcid]; + } elseif (isset($argsObj->linkedVersion[$tcid]) && + ! isset($mapo[$argsObj->linkedVersion[$tcid]])) { + $tcversion_id = $argsObj->linkedVersion[$tcid]; + } + if (! is_null($tcversion_id)) { + $mapo[$tcversion_id] = $argsObj->testcases2order[$tcid]; + } + } + } + + if (! is_null($mapo)) { + $tplanMgr->setExecutionOrder($argsObj->tplan_id, $mapo); + } +} + +/* + * function: initializeGui + * + * args : + * + * returns: + * + */ +function initializeGui(&$dbHandler, $argsObj, &$tplanMgr, &$tcaseMgr) +{ + $tcase_cfg = config_get('testcase_cfg'); + $title_separator = config_get('gui_title_separator_1'); + + $gui = new stdClass(); + $gui->status_feedback = buildStatusFeedbackMsg(); + + $gui->testCasePrefix = $tcaseMgr->tproject_mgr->getTestCasePrefix( + $argsObj->tproject_id); + $gui->testCasePrefix .= $tcase_cfg->glue_character; + + $gui->can_remove_executed_testcases = $argsObj->user->hasRight($dbHandler, + "testplan_unlink_executed_testcases", $argsObj->tproject_id, + $argsObj->tplan_id); + + $tprojectInfo = $tcaseMgr->tproject_mgr->get_by_id($argsObj->tproject_id); + $gui->priorityEnabled = $tprojectInfo['opt']->testPriorityEnabled; + + $gui->has_tc = 0; + $gui->items = null; + $gui->has_linked_items = false; + + $gui->keywordsFilterType = new stdClass(); + $gui->keywordsFilterType->options = array( + 'OR' => 'Or', + 'AND' => 'And' + ); + $gui->keywordsFilterType->selected = $argsObj->keywordsFilterType; + + $gui->keyword_id = $argsObj->keyword_id; + $gui->keywords_filter_feedback = ''; + if (! is_null($gui->keyword_id) && $gui->keyword_id != 0) { + $gui->keywords_filter_feedback = buildKeywordsFeedbackMsg($dbHandler, + $argsObj, $gui); + } + + $gui->platform_id = $argsObj->platform_id; + $gui->platforms_filter_feedback = ''; + if (! is_null($gui->platform_id) && $gui->platform_id != 0) { + $gui->platforms_filter_feedback = buildPlatformsFeedbackMsg($dbHandler, + $argsObj, $gui); + } + + // full_control, controls the operations planAddTC_m1.tpl will allow + // 1 => add/remove + // 0 => just remove + $gui->full_control = 1; + + $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->testPlanName = $tplan_info['name']; + $gui->pageTitle = lang_get('test_plan') . $title_separator . + $gui->testPlanName; + $gui->refreshTree = $argsObj->refreshTree; + $gui->canAssignExecTask = $argsObj->user->hasRight($dbHandler, + "exec_assign_testcases", $argsObj->tproject_id, $argsObj->tplan_id); + + $tproject_mgr = new testproject($dbHandler); + $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); + + $gui->testers = getTestersForHtmlOptions($dbHandler, $argsObj->tplan_id, + $tproject_info); + $gui->testerID = $argsObj->testerID; + $gui->send_mail = $argsObj->send_mail; + $gui->send_mail_checked = ''; + if ($gui->send_mail) { + $gui->send_mail_checked = ' checked="checked" '; + } + + $platform_mgr = new tlPlatform($dbHandler, $argsObj->tproject_id); + $gui->platforms = $platform_mgr->getLinkedToTestplan($argsObj->tplan_id); + $gui->platformsForHtmlOptions = null; + $gui->usePlatforms = $platform_mgr->platformsActiveForTestplan( + $argsObj->tplan_id); + if ($gui->usePlatforms) { + // Create options for two different select boxes. $bulk_platforms + // has "All platforms" on top and "$platformsForHtmlOptions" has an + // empty item + $gui->platformsForHtmlOptions[0] = ''; + foreach ($gui->platforms as $elem) { + $gui->platformsForHtmlOptions[$elem['id']] = $elem['name']; + } + + $optLTT = null; + $gui->bulk_platforms = $platform_mgr->getLinkedToTestplanAsMap( + $argsObj->tplan_id, $optLTT); + $gui->bulk_platforms[0] = lang_get("all_platforms"); + ksort($gui->bulk_platforms); + } + + $gui->warning_msg = new stdClass(); + $gui->warning_msg->executed = lang_get('executed_can_not_be_removed'); + if ($gui->can_remove_executed_testcases) { + $gui->warning_msg->executed = lang_get('has_been_executed'); + } + + $gui->build = initBuildSelector($tplanMgr, $argsObj); + + return $gui; +} + +/* + * function: doSaveCustomFields + * writes to DB value of custom fields displayed + * for test case versions linked to testplan. + * + * args: argsObj: user input data collected via HTML inputs + * tplanMgr: testplan manager object + * + * returns: - + * + */ +function doSaveCustomFields(&$argsObj, &$userInput, &$tplanMgr, &$tcaseMgr) +{ + // N.B.: I've use this piece of code also on write_execution(), think is time to create + // a method on cfield_mgr class. + // One issue: find a good method name + $cf_prefix = $tcaseMgr->cfield_mgr->get_name_prefix(); + $len_cfp = tlStringLen($cf_prefix); + $cf_nodeid_pos = 4; + + $nodeid_array_cfnames = null; + + // Example: two test cases (21 and 19 are testplan_tcversions.id => FEATURE_ID) + // with 3 custom fields + // + // custom_field_[TYPE]_[CFIELD_ID]_[FEATURE_ID] + // + // ( + // [21] => Array + // ( + // [0] => custom_field_0_3_21 + // [1] => custom_field_0_7_21 + // [5] => custom_field_6_9_21 + // ) + // + // [19] => Array + // ( + // [0] => custom_field_0_3_19 + // [1] => custom_field_0_7_19 + // [5] => custom_field_6_9_19 + // ) + // ) + // + foreach ($userInput as $input_name => $value) { + if (strncmp($input_name, $cf_prefix, $len_cfp) == 0) { + $dummy = explode('_', $input_name); + $nodeid_array_cfnames[$dummy[$cf_nodeid_pos]][] = $input_name; + } + } + + // foreach($argsObj->linkedWithCF as $key => $link_id) + foreach ($nodeid_array_cfnames as $link_id => $customFieldsNames) { + + // Create a SubSet of userInput just with inputs regarding CF for a link_id + // Example for link_id=21: + // + // $cfvalues=( 'custom_field_0_3_21' => A + // 'custom_field_0_7_21' => + // 'custom_field_8_8_21_day' => 0 + // 'custom_field_8_8_21_month' => 0 + // 'custom_field_8_8_21_year' => 0 + // 'custom_field_6_9_21_' => Every day) + // + $cfvalues = null; + foreach ($customFieldsNames as $cf) { + $cfvalues[$cf] = $userInput[$cf]; + } + $tcaseMgr->cfield_mgr->testplan_design_values_to_db($cfvalues, null, + $link_id); + } +} + +/* + * function: doSavePlatforms + * writes to DB execution ... of test case versions linked to testplan. + * + * args: argsObj: user input data collected via HTML inputs + * tplanMgr: testplan manager object + * + * returns: - + * + */ +function doSavePlatforms(&$argsObj, &$tplanMgr) +{ + foreach ($argsObj->feature2fix as $tcversion_platform) { + $tcversion_id = key($tcversion_platform); + $platform_id = current($tcversion_platform); + if ($platform_id != 0) { + $tplanMgr->changeLinkedTCVersionsPlatform($argsObj->tplan_id, 0, + $platform_id, $tcversion_id); + } + } +} + +/** + * send_mail_to_testers + * + * + * @return void + */ +function sendMailToTesters(&$dbHandler, &$tcaseMgr, &$guiObj, &$argsObj, + $features, $operation) +{ + $testers['new'] = null; + $mail_details['new'] = lang_get('mail_testcase_assigned') . "

    "; + $mail_subject['new'] = lang_get('mail_subject_testcase_assigned'); + $use_testers['new'] = true; + + $tcaseSet = null; + $tcnames = null; + $email = array(); + + $userSet[] = $argsObj->userID; + $userSet[] = $argsObj->testerID; + + $userData = tlUser::getByIDs($dbHandler, $userSet); + $assigner = $userData[$argsObj->userID]->firstName . ' ' . + $userData[$argsObj->userID]->lastName; + + $email['from_address'] = config_get('from_email'); + $body_first_lines = lang_get('testproject') . ': ' . $argsObj->tproject_name . + '
    ' . lang_get('testplan') . ': ' . $guiObj->testPlanName . + '

    '; + + // Get testers id + foreach ($features as $value) { + if ($use_testers['new']) { + $testers['new'][$value['user_id']][$value['tcase_id']] = $value['tcase_id']; + } + $tcaseSet[$value['tcase_id']] = $value['tcase_id']; + $tcversionSet[$value['tcversion_id']] = $value['tcversion_id']; + } + + $infoSet = $tcaseMgr->get_by_id_bulk($tcaseSet, $tcversionSet); + foreach ($infoSet as $value) { + $tcnames[$value['testcase_id']] = $guiObj->testCasePrefix . + $value['tc_external_id'] . ' ' . $value['name']; + } + + $path_info = $tcaseMgr->tree_manager->get_full_path_verbose($tcaseSet, + array( + 'output_format' => 'simple' + )); + $flat_path = null; + foreach ($path_info as $tcase_id => $pieces) { + $flat_path[$tcase_id] = implode('/', $pieces) . '/' . $tcnames[$tcase_id]; + } + + $validator = new Zend_Validate_EmailAddress(); + foreach ($testers as $tester_type => $tester_set) { + if (! is_null($tester_set)) { + $email['subject'] = $mail_subject[$tester_type] . ' ' . + $guiObj->testPlanName; + foreach ($tester_set as $user_id => $value) { + $userObj = $userData[$user_id]; + $email['to_address'] = trim($userObj->emailAddress); + if ($email['to_address'] == '' || + ! $validator->isValid($email['to_address'])) { + continue; + } + + $email['body'] = $body_first_lines; + $email['body'] .= sprintf($mail_details[$tester_type], + $userObj->firstName . ' ' . $userObj->lastName, $assigner); + + foreach ($value as $tcase_id) { + $email['body'] .= $flat_path[$tcase_id] . '
    '; + } + $email['body'] .= '
    ' . date(DATE_RFC1123); + + $email['cc'] = ''; + $email['attachment'] = null; + $email['exit_on_error'] = true; + $email['htmlFormat'] = true; + + $email_op = email_send($email['from_address'], + $email['to_address'], $email['subject'], $email['body'], + $email['cc'], $email['attachment'], $email['exit_on_error'], + $email['htmlFormat']); + } + } + } +} + +/** + * initDrawSaveButtons + */ +function initDrawSaveButtons(&$guiObj) +{ + $keySet = array_keys($guiObj->items); + + // 20100225 - eloff - BUGID 3205 - check only when platforms are active + // Logic to initialize drawSavePlatformsButton. + if ($guiObj->usePlatforms) { + // Looks for a platform with id = 0 + foreach ($keySet as $key) { + $breakLoop = false; + $testSuite = &$guiObj->items[$key]; + if ($testSuite['linked_testcase_qty'] > 0) { + $tcaseSet = array_keys($testSuite['testcases']); + foreach ($tcaseSet as $tcaseKey) { + if (isset( + $testSuite['testcases'][$tcaseKey]['feature_id'][0])) { + $breakLoop = true; + $guiObj->drawSavePlatformsButton = true; + break; + } + } + } + if ($breakLoop) { + break; + } + } + } + + // Logic to initialize drawSaveCFieldsButton + reset($keySet); + foreach ($keySet as $key) { + $breakLoop = false; + $tcaseSet = &$guiObj->items[$key]['testcases']; + if (! is_null($tcaseSet)) { + $tcversionSet = array_keys($tcaseSet); + foreach ($tcversionSet as $tcversionID) { + if (isset($tcaseSet[$tcversionID]['custom_fields']) && + ! is_null($tcaseSet[$tcversionID]['custom_fields'])) { + $breakLoop = true; + $guiObj->drawSaveCFieldsButton = true; + break; + } + } + } + if ($breakLoop) { + break; + } + } +} + +/** + */ +function setAdditionalGuiData($guiObj) +{ + $actionTitle = 'title_remove_test_from_plan'; + $buttonValue = 'btn_remove_selected_tc'; + $guiObj->exec_order_input_disabled = 'disabled="disabled"'; + + if ($guiObj->full_control) { + $actionTitle = 'title_add_test_to_plan'; + $buttonValue = 'btn_add_selected_tc'; + if ($guiObj->has_linked_items) { + $actionTitle = 'title_add_remove_test_to_plan'; + $buttonValue = 'btn_add_remove_selected_tc'; + } + $guiObj->exec_order_input_disabled = ' '; + } + $guiObj->actionTitle = lang_get($actionTitle); + $guiObj->buttonValue = lang_get($buttonValue); +} + +/** + * Initialize the HTML select box for selection of a build to which + * user wants to assign testers which are added to testplan. + * + * @author Andreas Simon + * @param testplan $testplan_mgr + * reference to testplan manager object + * @param object $argsObj + * reference to user input object + * @return array $html_menu array structure with all information needed for the menu + */ +function initBuildSelector(&$testplan_mgr, &$argsObj) +{ + + // init array + $menu = array( + 'items' => null, + 'selected' => null, + 'count' => 0 + ); + + $menu['items'] = (array) $testplan_mgr->get_builds_for_html_options( + $argsObj->tplan_id, testplan::GET_ACTIVE_BUILD, testplan::GET_OPEN_BUILD); + $menu['count'] = count($menu['items']); + + // if no build has been chosen yet, select the newest build by default + $build_id = $argsObj->build_id; + if (! $build_id && $menu['count']) { + $keys = array_keys($menu['items']); + $build_id = end($keys); + } + $menu['selected'] = $build_id; + + return $menu; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param stdClass $guiObj + * @param testplan $tplanMgr + * @param testcase $tcaseMgr + */ +function addToTestPlan(&$dbHandler, &$argsObj, &$guiObj, &$tplanMgr, &$tcaseMgr) +{ + // items_to_link structure: + // key: test case id , value: map + // key: platform_id value: test case VERSION ID + $items_to_link = null; + foreach ($argsObj->testcases2add as $tcase_id => $info) { + foreach ($info as $platform_id => $tcase_id) { + if (isset($argsObj->tcversion_for_tcid[$tcase_id])) { + $tcversion_id = $argsObj->tcversion_for_tcid[$tcase_id]; + } else { + $tcversion_id = $argsObj->linkedVersion[$tcase_id]; + } + $items_to_link['tcversion'][$tcase_id] = $tcversion_id; + $items_to_link['platform'][$platform_id] = $platform_id; + $items_to_link['items'][$tcase_id][$platform_id] = $tcversion_id; + } + } + + $linked_features = $tplanMgr->link_tcversions($argsObj->tplan_id, + $items_to_link, $argsObj->userID); + + if ($argsObj->testerID > 0) { + $features2 = null; + $status_map = $tplanMgr->assignment_mgr->get_available_status(); + $types_map = $tplanMgr->assignment_mgr->get_available_types(); + $db_now = $dbHandler->db_now(); + $tcversion_tcase = array_flip($items_to_link['tcversion']); + + $getOpt = array( + 'outputFormat' => 'map', + 'addIfNull' => true + ); + $platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id, $getOpt); + + foreach ($linked_features as $platform_id => $tcversion_info) { + foreach ($tcversion_info as $tcversion_id => $feature_id) { + $features2['add'][$feature_id]['user_id'] = $argsObj->testerID; + $features2['add'][$feature_id]['type'] = $types_map['testcase_execution']['id']; + $features2['add'][$feature_id]['status'] = $status_map['open']['id']; + $features2['add'][$feature_id]['assigner_id'] = $argsObj->userID; + $features2['add'][$feature_id]['tcase_id'] = $tcversion_tcase[$tcversion_id]; + $features2['add'][$feature_id]['tcversion_id'] = $tcversion_id; + $features2['add'][$feature_id]['creation_ts'] = $db_now; + $features2['add'][$feature_id]['platform_name'] = $platformSet[$platform_id]; + $features2['add'][$feature_id]['build_id'] = $argsObj->build_id; + } + } + + foreach ($features2 as $key => $values) { + $tplanMgr->assignment_mgr->assign($values); + $called[$key] = true; + } + + if ($argsObj->send_mail) { + foreach ($called as $ope => $ope_status) { + if ($ope_status) { + sendMailToTesters($dbHandler, $tcaseMgr, $guiObj, $argsObj, + $features2['add'], $ope); + } + } + } + } +} + +/** + */ +function buildStatusFeedbackMsg() +{ + $ret = ''; + $hideStatusSet = config_get('tplanDesign')->hideTestCaseWithStatusIn; + if (! is_null($hideStatusSet)) { + $cfx = getConfigAndLabels('testCaseStatus'); + $sc = array_flip($cfx['cfg']); + $msg = array(); + foreach ($hideStatusSet as $code => $verbose) { + $msg[] = $cfx['lbl'][$sc[$code]]; + } + $ret = sprintf(lang_get('hint_add_testcases_to_testplan_status'), + implode(',', $msg)); + } + + return $ret; +} + +/** + */ +function buildKeywordsFeedbackMsg(&$dbHandler, &$argsObj, &$gui) +{ + $opx = array( + 'tproject_id' => $argsObj->tproject_id, + 'cols' => 'id,keyword', + 'accessKey' => 'id' + ); + + $kwSet = tlKeyword::getSimpleSet($dbHandler, $opx); + $msg = array(); + $k2s = (array) $gui->keyword_id; + foreach ($k2s as $idt) { + $msg[] = $kwSet[$idt]['keyword']; + } + return implode(',', $msg); +} + +/** + */ +function buildPlatformsFeedbackMsg(&$dbHandler, &$argsObj, &$gui) +{ + $opx = array( + 'fields' => 'id,name', + 'accessKey' => 'id' + ); + + $platMgr = new tlPlatform($dbHandler, $argsObj->tproject_id); + $k2s = (array) $gui->platform_id; + $ixSet = $platMgr->getByID($k2s, $opx); + $msg = array(); + foreach ($k2s as $idt) { + $msg[] = $ixSet[$idt]['name']; + } + return implode(',', $msg); } diff --git a/lib/plan/planAddTCNavigator.php b/lib/plan/planAddTCNavigator.php index c28b477edc..ea6dd6c13d 100644 --- a/lib/plan/planAddTCNavigator.php +++ b/lib/plan/planAddTCNavigator.php @@ -1,90 +1,86 @@ -build_tree_menu($gui); +$control->formAction = $_SESSION['basehref'] . "lib/plan/planAddTCNavigator.php"; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('control', $control); +$smarty->assign('args', $gui->args); +$smarty->assign('menuUrl', $gui->menuUrl); + +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Initialize gui object for use in templates. + * + * @param tlTestCaseFilterControl $control + * @return object $gui + */ +function initializeGui($control) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $gui = new stdClass(); + $gui->formAction = ''; + + $gui->req_spec_manager_url = "lib/requirements/reqSpecView.php"; + $gui->req_manager_url = "lib/requirements/reqView.php"; + + // This logic is managed from frmWorkArea.php and planAddTC.php + $gui->loadRightPaneAddTC = isset($_REQUEST['loadRightPaneAddTC']) ? $_REQUEST['loadRightPaneAddTC'] : true; + if (isset($_SESSION['loadRightPaneAddTC'][$control->form_token])) { + $gui->loadRightPaneAddTC = false; + unset($_SESSION['loadRightPaneAddTC'][$control->form_token]); + } + + $gui->menuUrl = 'lib/plan/planAddTC.php'; + + // DEV NOTES - CRITIC + // activity has to be coherent with login on frmWorkArea.php and printDocOptions.php + $gui->args = $control->get_argument_string() . '&activity=addTC'; + $gui->additional_string = ''; + $gui->src_workframe = $control->args->basehref . $gui->menuUrl . + "?edit=testproject&id={$control->args->testproject_id}" . $gui->args; + + $gui->title_navigator = lang_get('navigator_add_remove_tcase_to_tplan'); + return $gui; } - -$gui = initializeGui($control); -$control->build_tree_menu($gui); -$control->formAction = $_SESSION['basehref'] . "lib/plan/planAddTCNavigator.php"; - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->assign('control', $control); -$smarty->assign('args', $gui->args); -$smarty->assign('menuUrl', $gui->menuUrl); - -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * Initialize gui object for use in templates. - * @param tlTestCaseFilterControl $control - * @return object $gui - */ -function initializeGui($control) -{ - - $_REQUEST = strings_stripSlashes($_REQUEST); - - $gui = new stdClass(); - $gui->formAction = ''; - - $gui->req_spec_manager_url = "lib/requirements/reqSpecView.php"; - $gui->req_manager_url = "lib/requirements/reqView.php"; - - // This logic is managed from frmWorkArea.php and planAddTC.php - $gui->loadRightPaneAddTC = isset($_REQUEST['loadRightPaneAddTC']) ? $_REQUEST['loadRightPaneAddTC'] : true; - if( isset($_SESSION['loadRightPaneAddTC'][$control->form_token]) ) - { - $gui->loadRightPaneAddTC = false; - unset($_SESSION['loadRightPaneAddTC'][$control->form_token]); - } - - $gui->menuUrl = 'lib/plan/planAddTC.php'; - - // DEV NOTES - CRITIC - // activity has to be coherent with login on frmWorkArea.php and printDocOptions.php - $gui->args = $control->get_argument_string() . '&activity=addTC'; - $gui->additional_string = ''; - $gui->src_workframe = $control->args->basehref . $gui->menuUrl . - "?edit=testproject&id={$control->args->testproject_id}" . $gui->args; - - $gui->title_navigator = lang_get('navigator_add_remove_tcase_to_tplan'); - return $gui; -} \ No newline at end of file diff --git a/lib/plan/planEdit.php b/lib/plan/planEdit.php index f2aee651f6..255d409da5 100644 --- a/lib/plan/planEdit.php +++ b/lib/plan/planEdit.php @@ -1,453 +1,553 @@ -tproject_id) { - $smarty->assign('title', lang_get('fatal_page_title')); - $smarty->assign('content', lang_get('error_no_testprojects_present')); - $smarty->display('workAreaSimple.tpl'); - exit(); -} - -if (!checkRights($db,$args->user,$args->tproject_id)) { - $smarty->assign('title', lang_get('fatal_page_title')); - $smarty->assign('content', lang_get('not_enough_rights')); - $smarty->display('workAreaSimple.tpl'); - exit(); +tproject_id) { + $smarty->assign('title', lang_get('fatal_page_title')); + $smarty->assign('content', lang_get('error_no_testprojects_present')); + $smarty->display('workAreaSimple.tpl'); + exit(); +} + +if (! checkRights($db, $args->user, $args->tproject_id)) { + $smarty->assign('title', lang_get('fatal_page_title')); + $smarty->assign('content', lang_get('not_enough_rights')); + $smarty->display('workAreaSimple.tpl'); + exit(); +} + +$of = web_editor('notes', $_SESSION['basehref'], $editorCfg); +$of->Value = getItemTemplateContents('testplan_template', $of->InstanceName, + $args->notes); + +// Checks on testplan name, and testplan name<=>testplan id +if ($args->do_action == "do_create" || $args->do_action == "do_update") { + $gui->testplan_name = $args->testplan_name; + $name_exists = $tproject_mgr->check_tplan_name_existence($args->tproject_id, + $args->testplan_name); + $name_id_rel_ok = (isset($gui->tplans[$args->tplan_id]) && + $gui->tplans[$args->tplan_id]['name'] == $args->testplan_name); +} + +// interface changes to be able to do not loose CF values if some problem arise on User Interface +$gui->cfields = $tplan_mgr->html_table_of_custom_field_inputs($args->tplan_id, + $args->tproject_id, 'design', '', $_REQUEST); + +switch ($args->do_action) { + case 'fileUpload': + $gui->uploadOp = fileUploadManagement($db, $args->tplan_id, + $args->fileTitle, $tplan_mgr->getAttachmentTableName()); + getItemData($tplan_mgr, $gui, $of, $args->tplan_id, true); + break; + + case 'deleteFile': + deleteAttachment($db, $args->file_id); + getItemData($tplan_mgr, $gui, $of, $args->tplan_id, true); + break; + + case 'edit': + getItemData($tplan_mgr, $gui, $of, $args->tplan_id); + break; + + case 'do_delete': + $tplanInfo = $tplan_mgr->get_by_id($args->tplan_id); + if ($tplanInfo) { + $tplan_mgr->delete($args->tplan_id); + logAuditEvent( + TLS("audit_testplan_deleted", $args->tproject_name, + $tplanInfo['name']), "DELETE", $args->tplan_id, "testplan"); + } + + // unset the session test plan if it is deleted + if (isset($_SESSION['testplanID']) && + ($_SESSION['testplanID'] = $args->tplan_id)) { + $_SESSION['testplanID'] = 0; + $_SESSION['testplanName'] = null; + } + break; + + case 'do_update': + $of->Value = $args->notes; + $gui->testplan_name = $args->testplan_name; + $gui->is_active = ($args->active == 'on') ? 1 : 0; + $gui->is_public = ($args->is_public == 'on') ? 1 : 0; + + $template = 'planEdit.tpl'; + $status_ok = false; + + if (! $name_exists || $name_id_rel_ok) { + if (! $tplan_mgr->update($args->tplan_id, $args->testplan_name, + $args->notes, $args->active, $args->is_public)) { + $gui->user_feedback = lang_get('update_tp_failed1') . + $gui->testplan_name . lang_get('update_tp_failed2') . ": " . + $db->error_msg() . "
    "; + } else { + logAuditEvent( + TLS("audit_testplan_saved", $args->tproject_name, + $args->testplan_name), "SAVE", $args->tplan_id, + "testplans"); + $cf_map = $tplan_mgr->get_linked_cfields_at_design( + $args->tplan_id); + $tplan_mgr->cfield_mgr->design_values_to_db($_REQUEST, + $args->tplan_id, $cf_map); + + if (isset($_SESSION['testplanID']) && + ($args->tplan_id == $_SESSION['testplanID'])) { + $_SESSION['testplanName'] = $args->testplan_name; + } + $status_ok = true; + $template = null; + + if (! $args->is_public) { + $tprojectEffectiveRole = $args->user->getEffectiveRole($db, + $args->tproject_id, null); + + // does user have an SPECIFIC role on TestPlan ? + // if answer is yes => do nothing + if (! tlUser::hasRoleOnTestPlan($db, $args->user_id, + $args->tplan_id)) { + $tplan_mgr->addUserRole($args->user_id, $args->tplan_id, + $tprojectEffectiveRole->dbID); + } + } + } + } else { + $gui->user_feedback = lang_get("warning_duplicate_tplan_name"); + } + + if (! $status_ok) { + $gui->tplan_id = $args->tplan_id; + $gui->tproject_name = $args->tproject_name; + $gui->notes = $of->CreateHTML(); + } + break; + + case 'do_create': + $template = 'planEdit.tpl'; + $status_ok = false; + + $of->Value = $args->notes; + $gui->testplan_name = $args->testplan_name; + $gui->is_active = ($args->active == 'on') ? 1 : 0; + $gui->is_public = ($args->is_public == 'on') ? 1 : 0; + + if (! $name_exists) { + $new_tplan_id = $tplan_mgr->create($args->testplan_name, + $args->notes, $args->tproject_id, $args->active, + $args->is_public); + if ($new_tplan_id == 0) { + $gui->user_feedback = $db->error_msg(); + } else { + logAuditEvent( + TLS("audit_testplan_created", $args->tproject_name, + $args->testplan_name), "CREATED", $new_tplan_id, + "testplans"); + $cf_map = $tplan_mgr->get_linked_cfields_at_design( + $new_tplan_id, $args->tproject_id); + $tplan_mgr->cfield_mgr->design_values_to_db($_REQUEST, + $new_tplan_id, $cf_map); + + $status_ok = true; + $template = null; + $gui->user_feedback = ''; + + // Operations Order is CRITIC + if ($args->copy) { + $options = array( + 'items2copy' => $args->copy_options, + 'copy_assigned_to' => $args->copy_assigned_to, + 'tcversion_type' => $args->tcversion_type + ); + $tplan_mgr->copy_as($args->source_tplanid, $new_tplan_id, + $args->testplan_name, $args->tproject_id, $args->user_id, + $options); + } + + // does user have an SPECIFIC role on TestPlan ? + // if answer is yes => do nothing + if (! $args->is_public && + ! tlUser::hasRoleOnTestPlan($db, $args->user_id, + $new_tplan_id)) { + $effectiveRole = $args->user->getEffectiveRole($db, + $args->tproject_id, null); + $tplan_mgr->addUserRole($args->user_id, $new_tplan_id, + $effectiveRole->dbID); + } + } + } else { + $gui->user_feedback = lang_get("warning_duplicate_tplan_name"); + } + + if (! $status_ok) { + $gui->tproject_name = $args->tproject_name; + $gui->notes = $of->CreateHTML(); + } + break; + + case 'setActive': + $tplan_mgr->setActive($args->tplan_id); + break; + + case 'setInactive': + $tplan_mgr->setInactive($args->tplan_id); + break; +} + +switch ($args->do_action) { + case "do_create": + case "do_delete": + case "do_update": + case "list": + case 'setActive': + case 'setInactive': + $do_display = true; + $template = is_null($template) ? 'planView.tpl' : $template; + $gui->tplans = $args->user->getAccessibleTestPlans($db, + $args->tproject_id, null, + array( + 'output' => 'mapfull', + 'active' => null + )); + $gui->drawPlatformQtyColumn = false; + + if (! is_null($gui->tplans)) { + // do this test project has platform definitions ? + $tplan_mgr->platform_mgr->setTestProjectID($args->tproject_id); + $dummy = $tplan_mgr->platform_mgr->testProjectCount(); + $gui->drawPlatformQtyColumn = $dummy[$args->tproject_id]['platform_qty'] > + 0; + + $tplanSet = array_keys($gui->tplans); + $dummy = $tplan_mgr->count_testcases($tplanSet, null, + array( + 'output' => 'groupByTestPlan' + )); + $buildQty = $tplan_mgr->get_builds($tplanSet, null, null, + array( + 'getCount' => true + )); + + $rightSet = array( + 'testplan_user_role_assignment' + ); + + $availableCF = (array) $tplan_mgr->get_linked_cfields_at_design( + current($tplanSet), $gui->tproject_id); + $hasCF = count($availableCF); + $gui->cfieldsColumns = null; + $initCFCol = true; + + $localeDateFormat = config_get('locales_date_format'); + $localeDateFormat = $localeDateFormat[$args->user->locale]; + + // get CF used to configure HIDE COLS + // We want different configurations for different test projects + // then will do two steps algorithm + // 1. get test project prefix PPFX + // 2. look for TL_TPLANVIEW_HIDECOL_PPFX + // 3. if found proceed + // 4. else look for TL_TPLANVIEW_HIDECOL + // + $ppfx = $tproject_mgr->getTestCasePrefix($gui->tproject_id); + $suffixSet = [ + '_' . $ppfx, + '' + ]; + foreach ($suffixSet as $suf) { + $gopt['name'] = 'TL_TPLANVIEW_HIDECOL' . $suf; + $col2hideCF = $tplan_mgr->cfield_mgr->get_linked_to_testproject( + $gui->tproject_id, null, $gopt); + + if ($col2hideCF != null) { + $col2hideCF = current($col2hideCF); + $col2hide = array_flip( + explode('|', $col2hideCF['possible_values'])); + $col2hide[$gopt['name']] = ''; + break; + } + } + + foreach ($tplanSet as $idk) { + if ($hasCF) { + $cfields = (array) $tplan_mgr->getCustomFieldsValues($idk, + $gui->tproject_id); + foreach ($cfields as $cfd) { + if ($initCFCol && ! isset($col2hide[$cfd['name']])) { + $gui->cfieldsColumns[] = $cfd['label']; + $gui->cfieldsType[] = $cfd['type']; + } + $gui->tplans[$idk][$cfd['label']] = [ + 'value' => $cfd['value'], + 'data-order' => $cfd['value'] + ]; + + if ($cfd['type'] == 'date') { + $gui->tplans[$idk][$cfd['label']]['data-order'] = locateDateToISO( + $cfd['value'], $localeDateFormat); + } + } + $initCFCol = false; + } + + $gui->tplans[$idk]['tcase_qty'] = isset($dummy[$idk]['qty']) ? intval( + $dummy[$idk]['qty']) : 0; + $gui->tplans[$idk]['build_qty'] = isset( + $buildQty[$idk]['build_qty']) ? intval( + $buildQty[$idk]['build_qty']) : 0; + if ($gui->drawPlatformQtyColumn) { + $plat = $tplan_mgr->getPlatforms($idk); + $gui->tplans[$idk]['platform_qty'] = is_null($plat) ? 0 : count( + $plat); + } + + // Get rights for each test plan + foreach ($rightSet as $target) { + // DEV NOTE - CRITIC + // I've made a theorically good performance choice to + // assign to $roleObj a reference to different roleObj + // UNFORTUNATELLY this choice was responsible to destroy point object + // since second LOOP + $roleObj = null; + if ($gui->tplans[$idk]['has_role'] > 0) { + if (isset( + $args->user->tplanRoles[$gui->tplans[$idk]['has_role']])) { + $roleObj = $args->user->tplanRoles[$gui->tplans[$idk]['has_role']]; + } else { + // Need To review this comment + // session cache has not still updated => get from DB ? + $roleObj = $args->user->getEffectiveRole($db, + $args->tproject_id, $idk); + } + } elseif (! is_null($args->user->tprojectRoles) && + isset($args->user->tprojectRoles[$args->tproject_id])) { + $roleObj = $args->user->tprojectRoles[$args->tproject_id]; + } + + if (is_null($roleObj)) { + $roleObj = $args->user->globalRole; + } + $gui->tplans[$idk]['rights'][$target] = $roleObj->hasRight( + $target); + } + } + } + break; + + case "edit": + case "create": + case 'fileUpload': + case 'deleteFile': + $do_display = true; + $template = is_null($template) ? 'planEdit.tpl' : $template; + $gui->notes = $of->CreateHTML(); + break; +} + +if ($do_display) { + $smarty->assign('gui', $gui); + $smarty->display($templateCfg->template_dir . $template); +} + +/** + * INITialize page ARGuments, using the $_REQUEST and $_SESSION + * super-global hashes. + * Important: changes in HTML input elements on the Smarty template must be reflected here. + * + * @param array $request_hash + * hash the $_REQUEST + * @return stdClass object with html values tranformed and other generated variables. + */ +function initArgs($request_hash) +{ + $session_hash = $_SESSION; + $args = new stdClass(); + $request_hash = strings_stripSlashes($request_hash); + + $nullable_keys = array( + 'testplan_name', + 'notes', + 'rights', + 'active', + 'do_action' + ); + foreach ($nullable_keys as $value) { + $args->$value = isset($request_hash[$value]) ? trim( + $request_hash[$value]) : null; + } + + $checkboxes_keys = array( + 'is_public' => 0, + 'active' => 0 + ); + foreach ($checkboxes_keys as $key => $value) { + $args->$key = isset($request_hash[$key]) ? 1 : 0; + } + + $intval_keys = array( + 'copy_from_tplan_id' => 0, + 'tplan_id' => 0 + ); + foreach ($intval_keys as $key => $value) { + $args->$key = isset($request_hash[$key]) ? intval($request_hash[$key]) : $value; + } + $args->source_tplanid = $args->copy_from_tplan_id; + $args->copy = ($args->copy_from_tplan_id > 0) ? true : false; + + $args->copy_options = array(); + $boolean_keys = array( + 'copyTcases' => 0, + 'copyPriorities' => 0, + 'copyMilestones' => 0, + 'copyUserRoles' => 0, + 'copyBuilds' => 0, + 'copyPlatformsLinks' => 0, + 'copyAttachments' => 0 + ); + + foreach ($boolean_keys as $key => $value) { + $args->copy_options[$key] = isset($request_hash[$key]) ? 1 : 0; + } + + $args->copy_assigned_to = isset($request_hash['copy_assigned_to']) ? 1 : 0; + $args->tcversion_type = isset($request_hash['tcversion_type']) ? $request_hash['tcversion_type'] : null; + $args->tproject_id = intval($session_hash['testprojectID']); + $args->tproject_name = $session_hash['testprojectName']; + $args->user_id = intval($session_hash['userID']); + $args->user = $session_hash['currentUser']; + + // all has to be refactored this way + $iParams = array( + "file_id" => array( + tlInputParameter::INT_N + ), + "fileTitle" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + R_PARAMS($iParams, $args); + + return $args; +} + +/** + * Checks the user rights for accessing the page + * + * @param database $db + * @param tlUser $user + * @param int $tproject_id + * @return boolean + */ +function checkRights(&$db, &$user, $tproject_id) +{ + return $user->hasRight($db, 'mgt_testplan_create', $tproject_id); +} + +/** + * Initializes the GUI + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param array $editorCfg + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj, &$editorCfg) +{ + $tplan_mgr = new testplan($dbHandler); + + $guiObj = new stdClass(); + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->editorType = $editorCfg['type']; + $guiObj->tplans = $argsObj->user->getAccessibleTestPlans($dbHandler, + $argsObj->tproject_id, null, + array( + 'output' => 'mapfull', + 'active' => null + )); + $guiObj->tproject_name = $argsObj->tproject_name; + $guiObj->main_descr = lang_get('testplan_title_tp_management') . " - " . + lang_get('testproject') . ' ' . $argsObj->tproject_name; + $guiObj->testplan_name = null; + $guiObj->tplan_id = intval($argsObj->tplan_id); + $guiObj->is_active = 0; + $guiObj->is_public = 0; + $guiObj->cfields = ''; + $guiObj->user_feedback = ''; + + $guiObj->grants = new stdClass(); + $guiObj->grants->testplan_create = $argsObj->user->hasRight($dbHandler, + "mgt_testplan_create", $argsObj->tproject_id); + $guiObj->grants->mgt_view_events = $argsObj->user->hasRight($dbHandler, + "mgt_view_events"); + $guiObj->notes = ''; + + $guiObj->attachments[$guiObj->tplan_id] = getAttachmentInfosFrom($tplan_mgr, + $guiObj->tplan_id); + $guiObj->attachmentTableName = $tplan_mgr->getAttachmentTableName(); + + $guiObj->fileUploadURL = $_SESSION['basehref'] . + $tplan_mgr->getFileUploadRelativeURL($guiObj->tplan_id); + $guiObj->delAttachmentURL = $_SESSION['basehref'] . + $tplan_mgr->getDeleteAttachmentRelativeURL($guiObj->tplan_id); + + $guiObj->fileUploadMsg = ''; + $guiObj->import_limit = TL_REPOSITORY_MAXFILESIZE; + + return $guiObj; +} + +/** + * + * @param testplan $itemMgr + * @param stdClass $guiObj + * @param ckeditorInterface $ofObj + * @param int $itemID + * @param boolean $updateAttachments + */ +function getItemData(&$itemMgr, &$guiObj, &$ofObj, $itemID, + $updateAttachments = false) +{ + $dummy = $itemMgr->get_by_id($itemID); + if (count($dummy)) { + $ofObj->Value = $dummy['notes']; + $guiObj->testplan_name = $dummy['name']; + $guiObj->is_active = $dummy['active']; + $guiObj->is_public = $dummy['is_public']; + $guiObj->api_key = $dummy['api_key']; + $guiObj->tplan_id = $itemID; + + if ($updateAttachments) { + $guiObj->attachments[$guiObj->tplan_id] = getAttachmentInfosFrom( + $itemMgr, $guiObj->tplan_id); + } + } } - - - -$of = web_editor('notes',$_SESSION['basehref'],$editorCfg); -$of->Value = getItemTemplateContents('testplan_template', $of->InstanceName, $args->notes); - - -// Checks on testplan name, and testplan name<=>testplan id -if($args->do_action == "do_create" || $args->do_action == "do_update") -{ - $gui->testplan_name = $args->testplan_name; - $name_exists = $tproject_mgr->check_tplan_name_existence($args->tproject_id,$args->testplan_name); - $name_id_rel_ok = (isset($gui->tplans[$args->tplan_id]) && - $gui->tplans[$args->tplan_id]['name'] == $args->testplan_name); -} - -// interface changes to be able to do not loose CF values if some problem arise on User Interface -$gui->cfields = $tplan_mgr->html_table_of_custom_field_inputs($args->tplan_id,$args->tproject_id,'design','',$_REQUEST); - -switch($args->do_action) -{ - case 'fileUpload': - $gui->uploadOp = fileUploadManagement($db,$args->tplan_id,$args->fileTitle,$tplan_mgr->getAttachmentTableName()); - getItemData($tplan_mgr,$gui,$of,$args->tplan_id,true); - break; - - case 'deleteFile': - deleteAttachment($db,$args->file_id); - getItemData($tplan_mgr,$gui,$of,$args->tplan_id,true); - break; - - case 'edit': - getItemData($tplan_mgr,$gui,$of,$args->tplan_id); - break; - - case 'do_delete': - $tplanInfo = $tplan_mgr->get_by_id($args->tplan_id); - if ($tplanInfo) - { - $tplan_mgr->delete($args->tplan_id); - logAuditEvent(TLS("audit_testplan_deleted",$args->tproject_name,$tplanInfo['name']), - "DELETE",$args->tplan_id,"testplan"); - } - - //unset the session test plan if it is deleted - if (isset($_SESSION['testplanID']) && ($_SESSION['testplanID'] = $args->tplan_id)) - { - $_SESSION['testplanID'] = 0; - $_SESSION['testplanName'] = null; - } - break; - - case 'do_update': - $of->Value = $args->notes; - $gui->testplan_name = $args->testplan_name; - $gui->is_active = ($args->active == 'on') ? 1 :0 ; - $gui->is_public = ($args->is_public == 'on') ? 1 :0 ; - - $template = 'planEdit.tpl'; - $status_ok = false; - - if(!$name_exists || $name_id_rel_ok) - { - if(!$tplan_mgr->update($args->tplan_id,$args->testplan_name,$args->notes, - $args->active,$args->is_public)) - { - $gui->user_feedback = lang_get('update_tp_failed1'). $gui->testplan_name . - lang_get('update_tp_failed2').": " . $db->error_msg() . "
    "; - } - else - { - logAuditEvent(TLS("audit_testplan_saved",$args->tproject_name,$args->testplan_name),"SAVE", - $args->tplan_id,"testplans"); - $cf_map = $tplan_mgr->get_linked_cfields_at_design($args->tplan_id); - $tplan_mgr->cfield_mgr->design_values_to_db($_REQUEST,$args->tplan_id,$cf_map); - - if(isset($_SESSION['testplanID']) && ($args->tplan_id == $_SESSION['testplanID'])) - { - $_SESSION['testplanName'] = $args->testplan_name; - } - $status_ok = true; - $template = null; - - if(!$args->is_public) - { - $tprojectEffectiveRole = $args->user->getEffectiveRole($db,$args->tproject_id,null); - - // does user have an SPECIFIC role on TestPlan ? - // if answer is yes => do nothing - if(!tlUser::hasRoleOnTestPlan($db,$args->user_id,$args->tplan_id)) - { - $tplan_mgr->addUserRole($args->user_id,$args->tplan_id,$tprojectEffectiveRole->dbID); - } - } - } - } - else - { - $gui->user_feedback = lang_get("warning_duplicate_tplan_name"); - } - - if(!$status_ok) - { - $gui->tplan_id=$args->tplan_id; - $gui->tproject_name=$args->tproject_name; - $gui->notes=$of->CreateHTML(); - } - break; - - case 'do_create': - $template = 'planEdit.tpl'; - $status_ok = false; - - $of->Value = $args->notes; - $gui->testplan_name = $args->testplan_name; - $gui->is_active = ($args->active == 'on') ? 1 :0 ; - $gui->is_public = ($args->is_public == 'on') ? 1 :0 ; - - if(!$name_exists) - { - $new_tplan_id = $tplan_mgr->create($args->testplan_name,$args->notes, - $args->tproject_id,$args->active,$args->is_public); - if ($new_tplan_id == 0) - { - $gui->user_feedback = $db->error_msg(); - } - else - { - logAuditEvent(TLS("audit_testplan_created",$args->tproject_name,$args->testplan_name), - "CREATED",$new_tplan_id,"testplans"); - $cf_map = $tplan_mgr->get_linked_cfields_at_design($new_tplan_id,$args->tproject_id); - $tplan_mgr->cfield_mgr->design_values_to_db($_REQUEST,$new_tplan_id,$cf_map); - - $status_ok = true; - $template = null; - $gui->user_feedback =''; - - // Operations Order is CRITIC - if($args->copy) - { - $options = array('items2copy' => $args->copy_options,'copy_assigned_to' => $args->copy_assigned_to, - 'tcversion_type' => $args->tcversion_type); - $tplan_mgr->copy_as($args->source_tplanid, $new_tplan_id,$args->testplan_name, - $args->tproject_id,$args->user_id,$options); - } - - if(!$args->is_public) - { - // does user have an SPECIFIC role on TestPlan ? - // if answer is yes => do nothing - if(!tlUser::hasRoleOnTestPlan($db,$args->user_id,$new_tplan_id)) - { - $effectiveRole = $args->user->getEffectiveRole($db,$args->tproject_id,null); - $tplan_mgr->addUserRole($args->user_id,$new_tplan_id,$effectiveRole->dbID); - } - } - // End critic block - - } - } - else - { - $gui->user_feedback = lang_get("warning_duplicate_tplan_name"); - } - - if(!$status_ok) - { - // $gui->tplan_id=$new_tplan_id; - $gui->tproject_name=$args->tproject_name; - $gui->notes=$of->CreateHTML(); - } - break; - - case 'setActive': - $tplan_mgr->setActive($args->tplan_id); - break; - - case 'setInactive': - $tplan_mgr->setInactive($args->tplan_id); - break; - -} - -switch($args->do_action) -{ - case "do_create": - case "do_delete": - case "do_update": - case "list": - case 'setActive': - case 'setInactive': - $do_display=true; - $template = is_null($template) ? 'planView.tpl' : $template; - $gui->tplans = $args->user->getAccessibleTestPlans($db,$args->tproject_id,null, - array('output' =>'mapfull','active' => null)); - $gui->drawPlatformQtyColumn = false; - if( !is_null($gui->tplans) ) - { - // do this test project has platform definitions ? - $tplan_mgr->platform_mgr->setTestProjectID($args->tproject_id); - $dummy = $tplan_mgr->platform_mgr->testProjectCount(); - $gui->drawPlatformQtyColumn = $dummy[$args->tproject_id]['platform_qty'] > 0; - - $tplanSet = array_keys($gui->tplans); - $dummy = $tplan_mgr->count_testcases($tplanSet,null,array('output' => 'groupByTestPlan')); - $buildQty = $tplan_mgr->get_builds($tplanSet,null,null,array('getCount' => true)); - - $rightSet = array('testplan_user_role_assignment'); - foreach($tplanSet as $idk) - { - $gui->tplans[$idk]['tcase_qty'] = isset($dummy[$idk]['qty']) ? intval($dummy[$idk]['qty']) : 0; - $gui->tplans[$idk]['build_qty'] = isset($buildQty[$idk]['build_qty']) ? intval($buildQty[$idk]['build_qty']) : 0; - if( $gui->drawPlatformQtyColumn ) - { - $plat = $tplan_mgr->getPlatforms($idk); - $gui->tplans[$idk]['platform_qty'] = is_null($plat) ? 0 : count($plat); - } - - // Get rights for each test plan - foreach($rightSet as $target) - { - // DEV NOTE - CRITIC - // I've made a theorically good performance choice to - // assign to $roleObj a reference to different roleObj - // UNFORTUNATELLY this choice was responsible to destroy point object - // since second LOOP - $roleObj = null; - if($gui->tplans[$idk]['has_role'] > 0) - { - if( isset($args->user->tplanRoles[ $gui->tplans[$idk]['has_role'] ]) ) - { - $roleObj = $args->user->tplanRoles[ $gui->tplans[$idk]['has_role'] ]; - } - else - { - // Need To review this comment - // session cache has not still updated => get from DB ? - $roleObj = $args->user->getEffectiveRole($db,$args->tproject_id,$idk); - } - } - else if (!is_null($args->user->tprojectRoles) && - isset($args->user->tprojectRoles[$args->tproject_id]) ) - { - $roleObj = $args->user->tprojectRoles[$args->tproject_id]; - } - - if(is_null($roleObj)) - { - $roleObj = $args->user->globalRole; - } - $gui->tplans[$idk]['rights'][$target] = $roleObj->hasRight($target); - } - } - } - break; - - case "edit": - case "create": - case 'fileUpload': - case 'deleteFile': - $do_display=true; - $template = is_null($template) ? 'planEdit.tpl' : $template; - $gui->notes=$of->CreateHTML(); - break; -} - -if($do_display) -{ - $smarty->assign('gui',$gui); - $smarty->display($templateCfg->template_dir . $template); -} - - -/* - * INITialize page ARGuments, using the $_REQUEST and $_SESSION - * super-global hashes. - * Important: changes in HTML input elements on the Smarty template - * must be reflected here. - * - * - * @parameter hash request_hash the $_REQUEST - * @return object with html values tranformed and other - * generated variables. - * - */ -function init_args($request_hash) -{ - $session_hash = $_SESSION; - $args = new stdClass(); - $request_hash = strings_stripSlashes($request_hash); - - $nullable_keys = array('testplan_name','notes','rights','active','do_action'); - foreach($nullable_keys as $value) - { - $args->$value = isset($request_hash[$value]) ? trim($request_hash[$value]) : null; - } - - $checkboxes_keys = array('is_public' => 0,'active' => 0); - foreach($checkboxes_keys as $key => $value) - { - $args->$key = isset($request_hash[$key]) ? 1 : 0; - } - - $intval_keys = array('copy_from_tplan_id' => 0,'tplan_id' => 0); - foreach($intval_keys as $key => $value) - { - $args->$key = isset($request_hash[$key]) ? intval($request_hash[$key]) : $value; - } - $args->source_tplanid = $args->copy_from_tplan_id; - $args->copy = ($args->copy_from_tplan_id > 0) ? TRUE : FALSE; - - $args->copy_options=array(); - $boolean_keys = array('copy_tcases' => 0,'copy_priorities' => 0, - 'copy_milestones' => 0, 'copy_user_roles' => 0, - 'copy_builds' => 0, 'copy_platforms_links' => 0, - 'copy_attachments' => 0); - - foreach($boolean_keys as $key => $value) - { - $args->copy_options[$key]=isset($request_hash[$key]) ? 1 : 0; - } - - $args->copy_assigned_to = isset($request_hash['copy_assigned_to']) ? 1 : 0; - $args->tcversion_type = isset($request_hash['tcversion_type']) ? $request_hash['tcversion_type'] : null; - $args->tproject_id = intval($session_hash['testprojectID']); - $args->tproject_name = $session_hash['testprojectName']; - $args->user_id = intval($session_hash['userID']); - $args->user = $session_hash['currentUser']; - - - // all has to be refactored this way - $iParams = array("file_id" => array(tlInputParameter::INT_N), - "fileTitle" => array(tlInputParameter::STRING_N,0,100)); - R_PARAMS($iParams,$args); - - return $args; -} - -/** - * checkRights - * - */ -function checkRights(&$db,&$user,$tproject_id) -{ - return $user->hasRight($db,'mgt_testplan_create',$tproject_id); -} - -/** - * initializeGui - * - */ -function initializeGui(&$dbHandler,&$argsObj,&$editorCfg,&$tprojectMgr) -{ - $tplan_mgr = new testplan($dbHandler); - - $guiObj = new stdClass(); - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->editorType = $editorCfg['type']; - $guiObj->tplans = $argsObj->user->getAccessibleTestPlans($dbHandler,$argsObj->tproject_id, - null,array('output' =>'mapfull','active' => null)); - $guiObj->tproject_name = $argsObj->tproject_name; - $guiObj->main_descr = lang_get('testplan_title_tp_management'). " - " . - lang_get('testproject') . ' ' . $argsObj->tproject_name; - $guiObj->testplan_name = null; - $guiObj->tplan_id = intval($argsObj->tplan_id); - $guiObj->is_active = 0; - $guiObj->is_public = 0; - $guiObj->cfields = ''; - $guiObj->user_feedback = ''; - - $guiObj->grants = new stdClass(); - $guiObj->grants->testplan_create = $argsObj->user->hasRight($dbHandler,"mgt_testplan_create",$argsObj->tproject_id); - $guiObj->grants->mgt_view_events = $argsObj->user->hasRight($dbHandler,"mgt_view_events"); - $guiObj->notes = ''; - - $guiObj->attachments[$guiObj->tplan_id] = getAttachmentInfosFrom($tplan_mgr,$guiObj->tplan_id); - $guiObj->attachmentTableName = $tplan_mgr->getAttachmentTableName(); - - - $guiObj->fileUploadURL = $_SESSION['basehref'] . $tplan_mgr->getFileUploadRelativeURL($guiObj->tplan_id); - $guiObj->delAttachmentURL = $_SESSION['basehref'] . $tplan_mgr->getDeleteAttachmentRelativeURL($guiObj->tplan_id); - - $guiObj->fileUploadMsg = ''; - $guiObj->import_limit = TL_REPOSITORY_MAXFILESIZE; - - return $guiObj; -} - -/** - * - */ -function getItemData(&$itemMgr,&$guiObj,&$ofObj,$itemID,$updateAttachments=false) -{ - $dummy = $itemMgr->get_by_id($itemID); - if (sizeof($dummy)) - { - $ofObj->Value = $dummy['notes']; - $guiObj->testplan_name = $dummy['name']; - $guiObj->is_active = $dummy['active']; - $guiObj->is_public = $dummy['is_public']; - $guiObj->api_key = $dummy['api_key']; - $guiObj->tplan_id = $itemID; - - if($updateAttachments) - { - $guiObj->attachments[$guiObj->tplan_id] = getAttachmentInfosFrom($itemMgr,$guiObj->tplan_id); - } - } -} \ No newline at end of file diff --git a/lib/plan/planExport.php b/lib/plan/planExport.php index 860a0b370f..3b14714775 100644 --- a/lib/plan/planExport.php +++ b/lib/plan/planExport.php @@ -1,203 +1,218 @@ -exportContent - * - * 'linkedItem' just linked elements - * linked platforms - * linked test cases (minimal information) - * - * 'tree' - * complete plan contents: - * to be defined - * - * '4results' - * generates file format that can be used to import results - * - * @filesource planExport.php - * @package TestLink - * @author Francisco Mancardi - * @copyright 2003-2014, TestLink community - * @link http://www.testlink.org/ - * - * @internal revisions - * @since 1.9.12 - * - **/ -require_once("../../config.inc.php"); -require_once("../functions/common.php"); -require_once("../functions/xml.inc.php"); -testlinkInitPage($db); -$templateCfg = templateConfiguration(); - -$tplan_mgr = new testplan($db); - -$args = init_args(); -$gui = initializeGui($args,$tplan_mgr); - -if ($args->doExport) -{ - $tLogMsg = 'basename(__FILE__) : ' . basename(__FILE__) . ' : $args->exportContent : ' . $args->exportContent; - switch ($args->exportContent) - { - case 'tree': - $context = array('platform_id' => $args->platform_id, 'build_id' => $args->build_id, - 'tproject_id' => $args->tproject_id); - $content = $tplan_mgr->exportTestPlanDataToXML($args->tplan_id,$context); - $tLogMsg .= ' : exportTestPlanDataToXML()'; - break; - - case '4results': - $context = array('platform_id' => $args->platform_id, 'build_id' => $args->build_id, - 'tproject_id' => $args->tproject_id); - - $content = $tplan_mgr->exportForResultsToXML($args->tplan_id,$context,null, - array('tcaseSet' => $args->testCaseSet)); - $tLogMsg .= ' : exportForResultsToXML()'; - break; - - case 'linkedItems': - default: - $args->exportContent = 'linkedItems'; - $content = $tplan_mgr->exportLinkedItemsToXML($args->tplan_id); - $tLogMsg .= ' : exportLinkedItemsToXML()'; - break; - } - - tLog($tLogMsg,'DEBUG'); - downloadContentsToFile($content,$gui->export_filename); - exit(); +exportContent + * + * 'linkedItem' just linked elements + * linked platforms + * linked test cases (minimal information) + * + * 'tree' + * complete plan contents: + * to be defined + * + * '4results' + * generates file format that can be used to import results + * + * @filesource planExport.php + * @package TestLink + * @author Francisco Mancardi + * @copyright 2003-2014, TestLink community + * @link http://www.testlink.org/ + * + * @internal revisions + * @since 1.9.12 + * + **/ +require_once '../../config.inc.php'; +require_once '../functions/common.php'; +require_once '../functions/xml.inc.php'; +testlinkInitPage($db); +$templateCfg = templateConfiguration(); + +$tplan_mgr = new testplan($db); + +$args = initArgs(); +$gui = initializeGui($args, $tplan_mgr); + +if ($args->doExport) { + $tLogMsg = 'basename(__FILE__) : ' . basename(__FILE__) . + ' : $args->exportContent : ' . $args->exportContent; + switch ($args->exportContent) { + case 'tree': + $context = array( + 'platform_id' => $args->platform_id, + 'build_id' => $args->build_id, + 'tproject_id' => $args->tproject_id + ); + $content = $tplan_mgr->exportTestPlanDataToXML($args->tplan_id, + $context); + $tLogMsg .= ' : exportTestPlanDataToXML()'; + break; + + case '4results': + $context = array( + 'platform_id' => $args->platform_id, + 'build_id' => $args->build_id, + 'tproject_id' => $args->tproject_id + ); + + $content = $tplan_mgr->exportForResultsToXML($args->tplan_id, + $context, null, array( + 'tcaseSet' => $args->testCaseSet + )); + $tLogMsg .= ' : exportForResultsToXML()'; + break; + + case 'linkedItems': + default: + $args->exportContent = 'linkedItems'; + $content = $tplan_mgr->exportLinkedItemsToXML($args->tplan_id); + $tLogMsg .= ' : exportLinkedItemsToXML()'; + break; + } + + tLog($tLogMsg, 'DEBUG'); + downloadContentsToFile($content, $gui->export_filename); + exit(); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: init_args + * + * args: + * + * returns: + * + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->doExport = isset($_REQUEST['export']) ? $_REQUEST['export'] : null; + $args->exportType = isset($_REQUEST['exportType']) ? $_REQUEST['exportType'] : null; + $args->closeOnCancel = isset($_REQUEST['closeOnCancel']) ? $_REQUEST['closeOnCancel'] : 0; + + // ------------------------------------------------------------------------------------------------ + // IMPORTANT NOTICE - 20101101 - franciscom + // This page is called (@20101101) from two places + // + // From test plan management to export linked test cases & platforms + // From execution to export test plan contents + // I've found problems when using in 'execution feature' when I've choose to name hidden inputs + // on tpl with a name different to that used on execSetResults.php. + // This resulted on weird effects on execNavigator.tpl + // Propably one option can be to save 'form_token'. + // I've used a simple (and may be more suggest to new bugs in future): + // maintain same names -> build_id instead of buildID, and so on. + // A change was also needed on JS support function openExportTestPlan(). + // ------------------------------------------------------------------------------------------------ + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + $args->build_id = isset($_REQUEST['build_id']) ? intval( + $_REQUEST['build_id']) : 0; + $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : 0; + $args->platform_id = isset($_REQUEST['platform_id']) ? intval( + $_REQUEST['platform_id']) : 0; + + $args->export_filename = isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : null; + $args->export_filename = trim($args->export_filename); + + // replace blank on name with _ + if (! is_null($args->export_filename)) { + $args->export_filename = str_replace(' ', '_', $args->export_filename); + } + + $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; + + // TICKET 6498: Cross-Site Scripting on /lib/plan/planExport.php (CWE-80) + $default = 'linkedItems'; + $args->exportContent = isset($_REQUEST['exportContent']) ? substr( + $_REQUEST['exportContent'], 0, strlen($default)) : $default; + switch ($args->exportContent) { + case 'tree': + case '4results': + case 'linkedItems': + break; + + default: + $args->exportContent = $default; + break; + } + + // Vulnerable ? + $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $args->testCaseSet = null; + if ($args->treeFormToken > 0) { + $mode = 'execution_mode'; + $session_data = isset($_SESSION[$mode]) && + isset($_SESSION[$mode][$args->treeFormToken]) ? $_SESSION[$mode][$args->treeFormToken] : null; + + $args->testCaseSet = $session_data['testcases_to_show']; + } + return $args; +} + +/** + */ +function initializeGui(&$argsObj, &$tplanMgr) +{ + $info = $tplanMgr->get_by_id($argsObj->tplan_id, + array( + 'output' => 'minimun', + 'caller' => __LINE__ + )); + $add2name = ''; + + $guiObj = new stdClass(); + $guiObj->do_it = 1; + $guiObj->nothing_todo_msg = ''; + $guiObj->closeOnCancel = $argsObj->closeOnCancel; + + // If there is a platform setted -> use in name. + if ($argsObj->platform_id > 0) { + $dummy = $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'mapAccessByID' + )); + $add2name .= '_' . + str_replace(' ', '_', $dummy[$argsObj->platform_id]['name']); + } + // If there is a build setted -> use in name. + if ($argsObj->build_id > 0) { + $dummy = $tplanMgr->get_builds($argsObj->tplan_id); + $add2name .= '_' . + str_replace(' ', '_', $dummy[$argsObj->build_id]['name']); + } + + // TICKET 4996: Ignores change of XML export file name + $guiObj->export_filename = $argsObj->export_filename; + if (trim($argsObj->export_filename) == '') { + $guiObj->export_filename = $argsObj->exportContent . '_' . + str_replace(' ', '_', $info['name']) . $add2name . '.xml'; + } + + $guiObj->exportTypes = array( + 'XML' => 'XML' + ); + $guiObj->page_title = lang_get('export_test_plan'); + $guiObj->object_name = $info['name']; + $guiObj->goback_url = ! is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; + + $guiObj->tplan_id = intval($argsObj->tplan_id); + $guiObj->tproject_id = intval($argsObj->tproject_id); + $guiObj->platform_id = intval($argsObj->platform_id); + $guiObj->build_id = intval($argsObj->build_id); + $guiObj->exportContent = $argsObj->exportContent; + $guiObj->treeFormToken = $argsObj->treeFormToken; + + return $guiObj; } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: init_args - - args: - - returns: - -*/ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->doExport = isset($_REQUEST['export']) ? $_REQUEST['export'] : null; - $args->exportType = isset($_REQUEST['exportType']) ? $_REQUEST['exportType'] : null; - $args->closeOnCancel = isset($_REQUEST['closeOnCancel']) ? $_REQUEST['closeOnCancel'] : 0; - - // ------------------------------------------------------------------------------------------------ - // IMPORTANT NOTICE - 20101101 - franciscom - // This page is called (@20101101) from two places - // - // From test plan management to export linked test cases & platforms - // From execution to export test plan contents - // I've found problems when using in 'execution feature' when I've choose to name hidden inputs - // on tpl with a name different to that used on execSetResults.php. - // This resulted on weird effects on execNavigator.tpl - // Propably one option can be to save 'form_token'. - // I've used a simple (and may be more suggest to new bugs in future): - // maintain same names -> build_id instead of buildID, and so on. - // A change was also needed on JS support function openExportTestPlan(). - // ------------------------------------------------------------------------------------------------ - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - $args->build_id = isset($_REQUEST['build_id']) ? intval($_REQUEST['build_id']) : 0; - $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : 0; - $args->platform_id = isset($_REQUEST['platform_id']) ? intval($_REQUEST['platform_id']) : 0; - - $args->export_filename = isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : null; - $args->export_filename = trim($args->export_filename); - - // replace blank on name with _ - if( !is_null($args->export_filename) ) - { - $args->export_filename = str_replace(' ','_',$args->export_filename); - } - - $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; - - // TICKET 6498: Cross-Site Scripting on /lib/plan/planExport.php (CWE-80) - $default = 'linkedItems'; - $args->exportContent = isset($_REQUEST['exportContent']) ? substr($_REQUEST['exportContent'],0,strlen($default)) : $default; - switch ($args->exportContent) - { - case 'tree': - case '4results': - case 'linkedItems': - break; - - default: - $args->exportContent = $default; - break; - } - - // Vulnerable ? - $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $args->testCaseSet = null; - if($args->treeFormToken >0) - { - $mode = 'execution_mode'; - $session_data = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$args->treeFormToken]) ? - $_SESSION[$mode][$args->treeFormToken] : null; - - $args->testCaseSet = $session_data['testcases_to_show']; - } - return $args; -} - - -/** - * - * - */ -function initializeGui(&$argsObj,&$tplanMgr) -{ - $info = $tplanMgr->get_by_id($argsObj->tplan_id, array('output' => 'minimun','caller' => __LINE__)); - $add2name = ''; - - $guiObj = new stdClass(); - $guiObj->do_it = 1; - $guiObj->nothing_todo_msg = ''; - $guiObj->closeOnCancel = $argsObj->closeOnCancel; - - // If there is a platform setted -> use in name. - if( $argsObj->platform_id > 0 ) - { - $dummy = $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'mapAccessByID')); - $add2name .= '_' . str_replace(' ','_',$dummy[$argsObj->platform_id]['name']); - } - // If there is a build setted -> use in name. - if( $argsObj->build_id > 0 ) - { - $dummy = $tplanMgr->get_builds($argsObj->tplan_id); - $add2name .= '_' . str_replace(' ','_',$dummy[$argsObj->build_id]['name']); - } - - // TICKET 4996: Ignores change of XML export file name - $guiObj->export_filename = $argsObj->export_filename; - if( trim($argsObj->export_filename) == '' ) - { - $guiObj->export_filename = $argsObj->exportContent . '_' . str_replace(' ','_',$info['name']) . $add2name . '.xml'; - } - - $guiObj->exportTypes = array('XML' => 'XML'); - $guiObj->page_title = lang_get('export_test_plan'); - $guiObj->object_name = $info['name']; - $guiObj->goback_url = !is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; - - $guiObj->tplan_id = intval($argsObj->tplan_id); - $guiObj->tproject_id = intval($argsObj->tproject_id); - $guiObj->platform_id = intval($argsObj->platform_id); - $guiObj->build_id = intval($argsObj->build_id); - $guiObj->exportContent = $argsObj->exportContent; - $guiObj->treeFormToken = $argsObj->treeFormToken; - - return $guiObj; -} \ No newline at end of file diff --git a/lib/plan/planImport.php b/lib/plan/planImport.php index 891df1fc37..3b616c2a76 100644 --- a/lib/plan/planImport.php +++ b/lib/plan/planImport.php @@ -1,512 +1,544 @@ - $dest_common . ".xml"); -$input_file = $dest_files['XML']; - -if(!is_null($args->importType)) -{ - $input_file = $dest_files[$args->importType]; -} - -$gui->file_check = array('status_ok' => 1, 'msg' => 'ok'); -$gui->import_title = lang_get('title_import_testplan_links'); - -// This check is done againg, also on importTestPlanLinksFromXML(), just to avoid surprises -$tproject_mgr = new testproject($db); -$dummy = $tproject_mgr->get_by_id($args->tproject_id); -$tprojectHasTC = $tproject_mgr->count_testcases($args->tproject_id) > 0; -if(!$tprojectHasTC) -{ - $gui->resultMap[] = array('',sprintf(lang_get('tproject_has_zero_testcases'),$dummy['name'])); -} - - -if ($args->do_upload) -{ - - // check the uploaded file - $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; - - $doIt = false; - $gui->file_check = null; - if (($source != 'none') && ($source != '')) - { - // ATTENTION: - // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using - // Firefox and Chrome. - if( !($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes) ) - { - $gui->file_check['status_ok'] = 0; - $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'),$_FILES['uploadedFile']['size'],$gui->importLimitBytes); - } - } - if($doIt) - { - $gui->file_check['status_ok'] = 1; - if (move_uploaded_file($source, $input_file)) - { - switch($args->importType) - { - case 'XML': - $pimport_fn = "importTestPlanLinksFromXML"; - break; - } - } - if($gui->file_check['status_ok'] && $pimport_fn) - { - $context = new stdClass(); - $context->tproject_id = $args->tproject_id; - $context->tplan_id = $args->tplan_id; - $context->userID = $args->userID; - $gui->resultMap = $pimport_fn($db,$tplan_mgr,$input_file,$context); - } - } - else if(is_null($gui->file_check)) - { - $gui->file_check = array('status_ok' => 0, 'msg' => lang_get('please_choose_file_to_import')); - $args->importType = null; - } -} - -$gui->testprojectName = $_SESSION['testprojectName']; -$gui->importTypes = $tplan_mgr->get_import_file_types(); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * called magically by TL to check if user trying to use this feature - * has enough rights. - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'mgt_testplan_create'); -} - -/** - * process input data, creating a kind of namespace - * - * @global array _REQUEST - * - * @internal Revisions - * 20101017 - franciscom - creation - */ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; - $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; - $args->do_upload = isset($_REQUEST['uploadFile']) ? 1 : 0; - - $args->userID = intval($_SESSION['userID']); - $args->tproject_id = $_SESSION['testprojectID']; - $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : 0; - - return $args; -} - - -/** - * - */ -function initializeGui(&$argsObj,&$tplanMgr) -{ - $guiObj = new stdClass(); - $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); - $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); - $guiObj->resultMap = null; - - $info = $tplanMgr->get_by_id($argsObj->tplan_id); - $guiObj->main_descr = lang_get('testplan') . ' ' . $info['name']; - $guiObj->tplan_id = intval($argsObj->tplan_id); - $guiObj->import_done = false; - return $guiObj; -} - - -/** - * @internal revisions - */ -function importTestPlanLinksFromXML(&$dbHandler,&$tplanMgr,$targetFile,$contextObj) -{ - // - // - // - // - // - // - // - // - // ... - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // ... - // - // - // - // - $msg = array(); - $labels = init_labels(array('link_without_required_platform' => null, 'ok' => null, - 'link_without_platform_element' => null, - 'no_platforms_on_tproject' => null, 'tcase_link_updated' => null, - 'link_with_platform_not_needed' => null, - 'tproject_has_zero_testcases' => null, - 'platform_not_on_tproject' => null, 'platform_linked' => null, - 'platform_not_linked' => null, 'tcase_doesnot_exist' => null, - 'tcversion_doesnot_exist' => null, 'not_imported' => null, - 'link_to_tplan_feedback' => null, 'link_to_platform' => null, - 'tcversion_status_forbidden' => null, - 'cant_link_to_tplan_feedback' => null)); - - // Double Check - // Check if Test Plan Parent (Test Project) has testcases, if not abort - $tprojectMgr = new testproject($dbHandler); - $tprojectInfo = $tprojectMgr->get_by_id($contextObj->tproject_id); - $tcasePrefix = $tprojectInfo['prefix'] . config_get('testcase_cfg')->glue_character; - - $tprojectHasTC = $tprojectMgr->count_testcases($contextObj->tproject_id) > 0; - if(!$tprojectHasTC) - { - $msg[] = array(sprintf($labels['tproject_has_zero_testcases'],$tprojectInfo['name']),$labels['not_imported']); - return $msg; // >>>-----> Bye - } - - $xml = @simplexml_load_file_wrapper($targetFile); - if($xml !== FALSE) - { - $tcaseMgr = new testcase($dbHandler); - $tcaseSet = array(); - $tprojectMgr->get_all_testcases_id($contextObj->tproject_id,$tcaseSet,array('output' => 'external_id')); - $tcaseSet = array_flip($tcaseSet); - - // Test Plan name will not be used - // - // - // Platform definition info will not be used - // - // I will try to link the platforms if are defined - $status_ok = true; - if (property_exists($xml,'platforms')) { - $platformMgr = new tlPlatform($dbHandler,$contextObj->tproject_id); - $platformUniverse = $platformMgr->getAllAsMap(); - if (is_null($platformUniverse)) { - $status_ok = false; - $msg[] = array($labels['no_platforms_on_tproject'],$labels['not_imported']); - } else { - $platformUniverse = array_flip($platformUniverse); - $op = processPlatforms($platformMgr,$tplanMgr,$platformUniverse,$xml->platforms, - $labels,$contextObj->tplan_id); - $status_ok = $op['status_ok']; - $msg = $op['msg']; - } - } - - if( $status_ok && $xml->xpath('//executables') ) - { - $tables = tlObjectWithDB::getDBTables(array('testplan_tcversions')); - $platformSet = $tplanMgr->getPlatforms($contextObj->tplan_id,array('outputFormat' => 'mapAccessByName')); - $targetHasPlatforms = (count($platformSet) > 0); - - $xmlLinks = $xml->executables->children(); - $loops2do = count($xmlLinks); - - // new dBug($platformSet); - $tplanDesignCfg = config_get('tplanDesign'); - - for($idx = 0; $idx < $loops2do; $idx++) - { - // if Target Test Plan has platforms and importing file NO => Fatal Error - $targetName = null; - $platformID = -1; - $linkWithPlatform = false; - $status_ok = false; - $dummy_msg = null; - $import_status = $labels['ok'];; - - if( ($platformElementExists = property_exists($xmlLinks[$idx],'platform')) ) - { - $targetName = trim((string)$xmlLinks[$idx]->platform->name); - $linkWithPlatform = ($targetName != ''); - } - - // echo "\$targetHasPlatforms:$targetHasPlatforms
    "; - // echo "\$linkWithPlatform:$linkWithPlatform
    "; - if($targetHasPlatforms) - { - // each link need to have platform or will not be imported - if( $linkWithPlatform && isset($platformSet[$targetName])) - { - $platformID = $platformSet[$targetName]['id']; - $status_ok = true; - $dummy_msg = null; - } - else - { - $import_status = $labels['not_imported']; - if( !$platformElementExists ) - { - $dummy_msg = sprintf($labels['link_without_platform_element'],$idx+1); - } - else if(!$linkWithPlatform) - { - $dummy_msg = sprintf($labels['link_without_required_platform'],$idx+1); - } - else - { - $dummy_msg = sprintf($labels['platform_not_linked'],$idx+1,$targetName,$contextObj->tplan_name); - } - } - } - else - { - if( $linkWithPlatform ) - { - $import_status = $labels['not_imported']; - $dummy_msg = sprintf($labels['link_with_platform_not_needed'],$idx+1); - } - else - { - $platformID = 0; - $status_ok = true; - } - } - if( !is_null($dummy_msg) ) - { - $msg[] = array($dummy_msg,$import_status); - } - - // echo '$status_ok' . $status_ok . ' ' . __LINE__ . '
    ' ; - if( $status_ok ) - { - $createLink = false; - $updateLink = false; - - // Link passed ok check on platform - // Now we need to understand if requested Test case is present on Test Project - $externalID = (int)$xmlLinks[$idx]->testcase->externalid; - $tcaseName = (string)$xmlLinks[$idx]->testcase->name; - $execOrder = (int)$xmlLinks[$idx]->testcase->execution_order; - $version = (int)$xmlLinks[$idx]->testcase->version; - - if( isset($tcaseSet[$externalID] ) ) - { - // now need to check if requested version exists - $dummy = $tcaseMgr->get_basic_info($tcaseSet[$externalID], - array('number' => $version)); - - if( count($dummy) > 0 ) - { - // Check : - // for same test plan there is a different version already linked ? - // if YES => error. - // - $lvFilters = array('tplan_id' => $contextObj->tplan_id); - $linkedVersions = $tcaseMgr->get_linked_versions($dummy[0]['id'],$lvFilters); - $updateLink = false; - $doUpdateFeedBack = true; - - if( !($createLink = is_null($linkedVersions)) ) - { - // Now need to understand if is already linked with this signature. - if( !isset($linkedVersions[$dummy[0]['tcversion_id']]) ) - { - // need to check if tc version status allows link to test plan - $createLink = !isset($tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]); - if($createLink == FALSE) - { - // see const.inc.php - $rogue = 'testCaseStatus_' . - $tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]; - - $dummy_msg = sprintf($labels['cant_link_to_tplan_feedback'], $externalID, $version); - - $msg[] = array($dummy_msg, - sprintf($labels['tcversion_status_forbidden'],lang_get($rogue))); - } - } - else - { - // linked platforms - $createLink = false; - $updateLink = false; - $plat_keys = array_keys($linkedVersions[$dummy[0]['tcversion_id']][$contextObj->tplan_id]); - $plat_keys = array_flip($plat_keys); - - if( isset($plat_keys[$platformID]) ) - { - $updateLink = true; - } - else if ($platformID == 0 ) - { - // User request to add without platform, but platforms exist => SKIP - $msg[] = array('platform 0 missing messages',$labels['not_imported']); - } - else - { - $createLink = true; - } - } - } - - - if( $createLink ) - { - $createLink = !isset($tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]); - if($createLink == FALSE) - { - // see const.inc.php - $rogue = 'testCaseStatus_' . - $tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]; - - $dummy_msg = sprintf($labels['cant_link_to_tplan_feedback'], $externalID, $version); - - $msg[] = array($dummy_msg, - sprintf($labels['tcversion_status_forbidden'],lang_get($rogue))); - } - } - - if( $createLink ) - { - $item2link['items'] = array($dummy[0]['id'] => array($platformID => $dummy[0]['tcversion_id'])); - $item2link['tcversion'] = array($dummy[0]['id'] => $dummy[0]['tcversion_id']); - $tplanMgr->link_tcversions($contextObj->tplan_id,$item2link,$contextObj->userID); - $dummy_msg = sprintf($labels['link_to_tplan_feedback'], $externalID, $version); - - if( $platformID > 0 ) - { - $dummy_msg .= sprintf($labels['link_to_platform'],$targetName); - } - $msg[] = array($dummy_msg,$labels['ok']); - - // TICKET 5189: Import a test plan does not import test cases execution order - $updateLink = true; - $doUpdateFeedBack = false; - } - - if( $updateLink ) - { - $newOrder = array( $dummy[0]['tcversion_id'] => $execOrder); - $tplanMgr->setExecutionOrder($contextObj->tplan_id,$newOrder); - - if( $doUpdateFeedBack ) - { - $dummy_msg = sprintf($labels['tcase_link_updated'],$tcasePrefix . $externalID . ' ' . - $tcaseName,$version); - $msg[] = array($dummy_msg,$labels['ok']); - } - } - } - else - { - $msg[] = array(sprintf($labels['tcversion_doesnot_exist'],$externalID,$version,$tprojectInfo['name'])); - } - } - else - { - $msg[] = array(sprintf($labels['tcase_doesnot_exist'],$externalID,$tprojectInfo['name'])); - } - //$tcaseMgr->get_by_external - - // echo '
    ';
    -          // var_dump($xmlLinks[$idx]->testcase);
    -          // echo 'TCBAME' . (string)$xmlLinks[$idx]->testcase->name;     
    -          // echo '
    '; - } - - } - } - } - return $msg; -} - -/** - * - */ -function processPlatforms(&$platMgr,&$tplanMgr,$universe,$xmlSubset,$lbl,$tplanID) -{ - $ret = array('status_ok' => true, 'msg' => null); - $children = $xmlSubset->children(); - $msg_ok = array(); - $loops2do = count($children); - $status_ok = true; - $idSet = null; - for($idx = 0; $idx < $loops2do; $idx++) - { - $targetName = trim((string)$children[$idx]->name); - if( isset($universe[$targetName]) ) - { - $status_ok = $status_ok && true; - // $msg_ok[] = array(sprintf($lbl['platform_linked'],$targetName),$lbl['ok']); - $idSet[$universe[$targetName]] = $targetName; - } - else - { - $status_ok = false; - $ret['msg'][] = array(sprintf($lbl['platform_not_on_tproject'],$targetName),$lbl['not_imported']); - - } - } - if( $status_ok ) - { - - // Now Link only if Platform is not already linked to test plan - $currentPlatformSet = $tplanMgr->getPlatforms($tplanID,array('outputFormat' => 'mapAccessByID')); - foreach($idSet as $platformID => $platformName) - { - if( !isset($currentPlatformSet[$platformID]) ) - { - $platMgr->linkToTestplan($platformID, $tplanID); - $msg_ok[] = array(sprintf($lbl['platform_linked'],$platformName),$lbl['ok']); - } - } - $ret['msg'] = $msg_ok; - } - return $ret; + $dest_common . ".xml" +); +$input_file = $dest_files['XML']; + +if (! is_null($args->importType)) { + $input_file = $dest_files[$args->importType]; +} + +$gui->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' +); +$gui->import_title = lang_get('title_import_testplan_links'); + +// This check is done againg, also on importTestPlanLinksFromXML(), just to avoid surprises +$tproject_mgr = new testproject($db); +$dummy = $tproject_mgr->get_by_id($args->tproject_id); +$tprojectHasTC = $tproject_mgr->count_testcases($args->tproject_id) > 0; +if (! $tprojectHasTC) { + $gui->resultMap[] = array( + '', + sprintf(lang_get('tproject_has_zero_testcases'), $dummy['name']) + ); +} + +if ($args->do_upload) { + + // check the uploaded file + $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; + + $doIt = false; + $gui->file_check = null; + // ATTENTION: + // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using + // Firefox and Chrome. + if (($source != 'none') && ($source != '') && + ! ($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes)) { + $gui->file_check['status_ok'] = 0; + $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'), + $_FILES['uploadedFile']['size'], $gui->importLimitBytes); + } + if ($doIt) { + $gui->file_check['status_ok'] = 1; + if (move_uploaded_file($source, $input_file)) { + switch ($args->importType) { + case 'XML': + $pimport_fn = "importTestPlanLinksFromXML"; + break; + } + } + if ($gui->file_check['status_ok'] && $pimport_fn) { + $context = new stdClass(); + $context->tproject_id = $args->tproject_id; + $context->tplan_id = $args->tplan_id; + $context->userID = $args->userID; + $gui->resultMap = $pimport_fn($db, $tplan_mgr, $input_file, $context); + } + } elseif (is_null($gui->file_check)) { + $gui->file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('please_choose_file_to_import') + ); + $args->importType = null; + } +} + +$gui->testprojectName = $_SESSION['testprojectName']; +$gui->importTypes = $tplan_mgr->get_import_file_types(); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * called magically by TL to check if user trying to use this feature + * has enough rights. + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'mgt_testplan_create'); +} + +/** + * process input data, creating a kind of namespace + * + * @global array _REQUEST + * + * @internal Revisions + * 20101017 - franciscom - creation + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; + $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; + $args->do_upload = isset($_REQUEST['uploadFile']) ? 1 : 0; + + $args->userID = intval($_SESSION['userID']); + $args->tproject_id = $_SESSION['testprojectID']; + $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : 0; + + return $args; +} + +/** + */ +function initializeGui(&$argsObj, &$tplanMgr) +{ + $guiObj = new stdClass(); + $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); + $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); + $guiObj->resultMap = null; + + $info = $tplanMgr->get_by_id($argsObj->tplan_id); + $guiObj->main_descr = lang_get('testplan') . ' ' . $info['name']; + $guiObj->tplan_id = intval($argsObj->tplan_id); + $guiObj->import_done = false; + return $guiObj; +} + +/** + * + * @internal revisions + */ +function importTestPlanLinksFromXML(&$dbHandler, &$tplanMgr, $targetFile, + $contextObj) +{ + // + // + // + // + // + // + // + // + // ... + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // ... + // + // + // + // + $msg = array(); + $labels = init_labels( + array( + 'link_without_required_platform' => null, + 'ok' => null, + 'link_without_platform_element' => null, + 'no_platforms_on_tproject' => null, + 'tcase_link_updated' => null, + 'link_with_platform_not_needed' => null, + 'tproject_has_zero_testcases' => null, + 'platform_not_on_tproject' => null, + 'platform_linked' => null, + 'platform_not_linked' => null, + 'tcase_doesnot_exist' => null, + 'tcversion_doesnot_exist' => null, + 'not_imported' => null, + 'link_to_tplan_feedback' => null, + 'link_to_platform' => null, + 'tcversion_status_forbidden' => null, + 'cant_link_to_tplan_feedback' => null + )); + + // Double Check + // Check if Test Plan Parent (Test Project) has testcases, if not abort + $tprojectMgr = new testproject($dbHandler); + $tprojectInfo = $tprojectMgr->get_by_id($contextObj->tproject_id); + $tcasePrefix = $tprojectInfo['prefix'] . + config_get('testcase_cfg')->glue_character; + + $tprojectHasTC = $tprojectMgr->count_testcases($contextObj->tproject_id) > 0; + if (! $tprojectHasTC) { + $msg[] = array( + sprintf($labels['tproject_has_zero_testcases'], + $tprojectInfo['name']), + $labels['not_imported'] + ); + return $msg; + } + + $xml = @simplexml_load_file_wrapper($targetFile); + if ($xml !== false) { + $tcaseMgr = new testcase($dbHandler); + $tcaseSet = array(); + $tprojectMgr->get_all_testcases_id($contextObj->tproject_id, $tcaseSet, + array( + 'output' => 'external_id' + )); + $tcaseSet = array_flip($tcaseSet); + + // Test Plan name will not be used + // + // + // Platform definition info will not be used + // + // I will try to link the platforms if are defined + $status_ok = true; + if (property_exists($xml, 'platforms')) { + $platformMgr = new tlPlatform($dbHandler, $contextObj->tproject_id); + $platformUniverse = $platformMgr->getAllAsMap(); + if (is_null($platformUniverse)) { + $status_ok = false; + $msg[] = array( + $labels['no_platforms_on_tproject'], + $labels['not_imported'] + ); + } else { + $platformUniverse = array_flip($platformUniverse); + $op = processPlatforms($platformMgr, $tplanMgr, + $platformUniverse, $xml->platforms, $labels, + $contextObj->tplan_id); + $status_ok = $op['status_ok']; + $msg = $op['msg']; + } + } + + if ($status_ok && $xml->xpath('//executables')) { + $platformSet = $tplanMgr->getPlatforms($contextObj->tplan_id, + array( + 'outputFormat' => 'mapAccessByName' + )); + $targetHasPlatforms = (! empty($platformSet)); + + $xmlLinks = $xml->executables->children(); + $loops2do = count($xmlLinks); + + $tplanDesignCfg = config_get('tplanDesign'); + + for ($idx = 0; $idx < $loops2do; $idx ++) { + // if Target Test Plan has platforms and importing file NO => Fatal Error + $targetName = null; + $platformID = - 1; + $linkWithPlatform = false; + $status_ok = false; + $dummy_msg = null; + $import_status = $labels['ok']; + + if ($platformElementExists = property_exists($xmlLinks[$idx], + 'platform')) { + $targetName = trim((string) $xmlLinks[$idx]->platform->name); + $linkWithPlatform = ($targetName != ''); + } + + if ($targetHasPlatforms) { + // each link need to have platform or will not be imported + if ($linkWithPlatform && isset($platformSet[$targetName])) { + $platformID = $platformSet[$targetName]['id']; + $status_ok = true; + $dummy_msg = null; + } else { + $import_status = $labels['not_imported']; + if (! $platformElementExists) { + $dummy_msg = sprintf( + $labels['link_without_platform_element'], + $idx + 1); + } elseif (! $linkWithPlatform) { + $dummy_msg = sprintf( + $labels['link_without_required_platform'], + $idx + 1); + } else { + $dummy_msg = sprintf($labels['platform_not_linked'], + $idx + 1, $targetName, $contextObj->tplan_name); + } + } + } else { + if ($linkWithPlatform) { + $import_status = $labels['not_imported']; + $dummy_msg = sprintf( + $labels['link_with_platform_not_needed'], $idx + 1); + } else { + $platformID = 0; + $status_ok = true; + } + } + if (! is_null($dummy_msg)) { + $msg[] = array( + $dummy_msg, + $import_status + ); + } + + if ($status_ok) { + $createLink = false; + $updateLink = false; + + // Link passed ok check on platform + // Now we need to understand if requested Test case is present on Test Project + $externalID = (int) $xmlLinks[$idx]->testcase->externalid; + $tcaseName = (string) $xmlLinks[$idx]->testcase->name; + $execOrder = (int) $xmlLinks[$idx]->testcase->execution_order; + $version = (int) $xmlLinks[$idx]->testcase->version; + + if (isset($tcaseSet[$externalID])) { + // now need to check if requested version exists + $dummy = $tcaseMgr->get_basic_info( + $tcaseSet[$externalID], + array( + 'number' => $version + )); + + if (! empty($dummy)) { + // Check : + // for same test plan there is a different version already linked ? + // if YES => error. + // + $lvFilters = array( + 'tplan_id' => $contextObj->tplan_id + ); + $linkedVersions = $tcaseMgr->get_linked_versions( + $dummy[0]['id'], $lvFilters); + $updateLink = false; + $doUpdateFeedBack = true; + + if (! ($createLink = is_null($linkedVersions))) { + // Now need to understand if is already linked with this signature. + if (! isset( + $linkedVersions[$dummy[0]['tcversion_id']])) { + // need to check if tc version status allows link to test plan + $createLink = ! isset( + $tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]); + if (! $createLink) { + // see const.inc.php + $rogue = 'testCaseStatus_' . + $tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]; + + $dummy_msg = sprintf( + $labels['cant_link_to_tplan_feedback'], + $externalID, $version); + + $msg[] = array( + $dummy_msg, + sprintf( + $labels['tcversion_status_forbidden'], + lang_get($rogue)) + ); + } + } else { + // linked platforms + $createLink = false; + $updateLink = false; + $plat_keys = array_keys( + $linkedVersions[$dummy[0]['tcversion_id']][$contextObj->tplan_id]); + $plat_keys = array_flip($plat_keys); + + if (isset($plat_keys[$platformID])) { + $updateLink = true; + } elseif ($platformID == 0) { + // User request to add without platform, but platforms exist => SKIP + $msg[] = array( + 'platform 0 missing messages', + $labels['not_imported'] + ); + } else { + $createLink = true; + } + } + } + + if ($createLink) { + $createLink = ! isset( + $tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]); + if (! $createLink) { + // see const.inc.php + $rogue = 'testCaseStatus_' . + $tplanDesignCfg->hideTestCaseWithStatusIn[$dummy[0]['status']]; + + $dummy_msg = sprintf( + $labels['cant_link_to_tplan_feedback'], + $externalID, $version); + + $msg[] = array( + $dummy_msg, + sprintf( + $labels['tcversion_status_forbidden'], + lang_get($rogue)) + ); + } + } + + if ($createLink) { + $item2link['items'] = array( + $dummy[0]['id'] => array( + $platformID => $dummy[0]['tcversion_id'] + ) + ); + $item2link['tcversion'] = array( + $dummy[0]['id'] => $dummy[0]['tcversion_id'] + ); + $tplanMgr->link_tcversions( + $contextObj->tplan_id, $item2link, + $contextObj->userID); + $dummy_msg = sprintf( + $labels['link_to_tplan_feedback'], + $externalID, $version); + + if ($platformID > 0) { + $dummy_msg .= sprintf( + $labels['link_to_platform'], $targetName); + } + $msg[] = array( + $dummy_msg, + $labels['ok'] + ); + + // TICKET 5189: Import a test plan does not import test cases execution order + $updateLink = true; + $doUpdateFeedBack = false; + } + + if ($updateLink) { + $newOrder = array( + $dummy[0]['tcversion_id'] => $execOrder + ); + $tplanMgr->setExecutionOrder( + $contextObj->tplan_id, $newOrder); + + if ($doUpdateFeedBack) { + $dummy_msg = sprintf( + $labels['tcase_link_updated'], + $tcasePrefix . $externalID . ' ' . + $tcaseName, $version); + $msg[] = array( + $dummy_msg, + $labels['ok'] + ); + } + } + } else { + $msg[] = array( + sprintf($labels['tcversion_doesnot_exist'], + $externalID, $version, $tprojectInfo['name']) + ); + } + } else { + $msg[] = array( + sprintf($labels['tcase_doesnot_exist'], $externalID, + $tprojectInfo['name']) + ); + } + } + } + } + } + return $msg; +} + +/** + */ +function processPlatforms(&$platMgr, &$tplanMgr, $universe, $xmlSubset, $lbl, + $tplanID) +{ + $ret = array( + 'status_ok' => true, + 'msg' => null + ); + $children = $xmlSubset->children(); + $msg_ok = array(); + $loops2do = count($children); + $status_ok = true; + $idSet = null; + for ($idx = 0; $idx < $loops2do; $idx ++) { + $targetName = trim((string) $children[$idx]->name); + if (isset($universe[$targetName])) { + $status_ok = true; + $idSet[$universe[$targetName]] = $targetName; + } else { + $status_ok = false; + $ret['msg'][] = array( + sprintf($lbl['platform_not_on_tproject'], $targetName), + $lbl['not_imported'] + ); + } + } + if ($status_ok) { + + // Now Link only if Platform is not already linked to test plan + $currentPlatformSet = $tplanMgr->getPlatforms($tplanID, + array( + 'outputFormat' => 'mapAccessByID' + )); + foreach ($idSet as $platformID => $platformName) { + if (! isset($currentPlatformSet[$platformID])) { + $platMgr->linkToTestplan($platformID, $tplanID); + $msg_ok[] = array( + sprintf($lbl['platform_linked'], $platformName), + $lbl['ok'] + ); + } + } + $ret['msg'] = $msg_ok; + } + return $ret; } diff --git a/lib/plan/planMilestonesCommands.class.php b/lib/plan/planMilestonesCommands.class.php index f08cf34677..88fa7de8c6 100644 --- a/lib/plan/planMilestonesCommands.class.php +++ b/lib/plan/planMilestonesCommands.class.php @@ -1,316 +1,325 @@ -db = $db; - $this->milestone_mgr = new milestone_mgr($db); - $this->submit_button_label = lang_get('btn_save'); - } - - function setAuditContext($auditContext) - { - $this->auditContext = $auditContext; - } - - /* - function: create - - args: - - returns: - - */ - function create(&$argsObj) - { - $guiObj = new stdClass(); - $guiObj->main_descr = lang_get('testplan') . TITLE_SEP; - $guiObj->action_descr = lang_get('create_milestone'); - $guiObj->template = $this->defaultTemplate; - $guiObj->submit_button_label = $this->submit_button_label; - $guiObj->milestone = array('id' => 0, 'name' => '', 'target_date' => '', - 'start_date' => '', - 'high_percentage' => '', 'medium_percentage' => '', - 'low_percentage' => '', - 'testplan_id' => $argsObj->tplan_id, - 'testplan_name' => $argsObj->tplan_name,); - return $guiObj; - } - - /* - function: edit - - args: - - returns: - - */ - function edit(&$argsObj) - { - $guiObj = new stdClass(); - $dummy = $this->milestone_mgr->get_by_id($argsObj->id); - $guiObj->milestone = $dummy[$argsObj->id]; - - // $dummyy necessary because localize_dateOrTimeStamp wants second parameter to be passed by reference - $dummy = null; - - // localize target date (is always set on edit) - $guiObj->milestone['target_date'] = localize_dateOrTimeStamp(null, $dummy, 'date_format',$guiObj->milestone['target_date']); - - // as start date is optional it can be "0000-00-00" (default timestamp) - if ($guiObj->milestone['start_date'] != "0000-00-00") - { - $guiObj->milestone['start_date'] = localize_dateOrTimeStamp(null, $dummy, 'date_format',$guiObj->milestone['start_date']); - } - else - { - $guiObj->milestone['start_date'] = ""; - } - - $guiObj->main_descr = lang_get('testplan') . TITLE_SEP; - $guiObj->action_descr = sprintf(lang_get('edit_milestone'),$guiObj->milestone['name']); - $guiObj->template = $this->defaultTemplate; - $guiObj->submit_button_label = $this->submit_button_label; - return $guiObj; - } - - - /* - function: doCreate - - args: - - returns: - - */ - function doCreate(&$argsObj,$basehref) - { - $date_format_cfg = config_get('date_format'); - $guiObj = new stdClass(); - $guiObj->main_descr = lang_get('Milestone') . TITLE_SEP; - $guiObj->action_descr = lang_get('create_milestone'); - $guiObj->submit_button_label=$this->submit_button_label; - $guiObj->template = null; - $op_ok = 1; - - // Check name do not exists - $name_exists = $this->milestone_mgr->check_name_existence($argsObj->tplan_id,$argsObj->name); - if($name_exists) - { - $guiObj->user_feedback = sprintf(lang_get('milestone_name_already_exists'),$argsObj->name); - $op_ok=0; - } - - // BUGID 3716 - // are the dates valid? - if ($op_ok) { - // start date is optional - $op_ok = is_valid_date($argsObj->target_date_original, $date_format_cfg) && - ($argsObj->start_date_original == '' || is_valid_date($argsObj->start_date_original, $date_format_cfg)); - if (!$op_ok) { - $guiObj->user_feedback = sprintf(lang_get('warning_invalid_date')); - } - } - - // check target date - if($op_ok) - { - $timestamp=array(); - $timestamp['target'] = strtotime($argsObj->target_date . " 23:59:59"); - $timestamp['now'] = strtotime("now"); - - if( $timestamp['target'] < $timestamp['now'] ) - { - $op_ok=0; - $guiObj->user_feedback = lang_get('warning_milestone_date'); - } - } - - // BUGID 3829 - check target date > start date - if($op_ok && isset($argsObj->start_date)) { - $timestamp['target'] = strtotime($argsObj->target_date . " 23:59:59"); - $timestamp['start'] = strtotime($argsObj->start_date . " 23:59:59"); - - // target must be chronologically after start - if( $timestamp['target'] < $timestamp['start'] ) - { - $op_ok=0; - $guiObj->user_feedback = lang_get('warning_target_before_start'); - } - } - - if($op_ok) - { - // avoid warning on event viewer - if (!isset($argsObj->start_date)) { - $argsObj->start_date = ""; - } - /* - $argsObj->id = $this->milestone_mgr->create($argsObj->tplan_id,$argsObj->name, - $argsObj->target_date,$argsObj->start_date, - $argsObj->low_priority_tcases, - $argsObj->medium_priority_tcases, - $argsObj->high_priority_tcases); - */ - $argsObj->low_priority = $argsObj->low_priority_tcases; - $argsObj->medium_priority = $argsObj->medium_priority_tcases; - $argsObj->high_priority = $argsObj->high_priority_tcases; - - $argsObj->id = $this->milestone_mgr->create($argsObj); - - $guiObj->user_feedback = 'ok'; - if($argsObj->id > 0) - { - logAuditEvent(TLS("audit_milestone_created",$argsObj->tplan_name,$argsObj->name), - "CREATE",$argsObj->id,"milestones"); - $guiObj->user_feedback = sprintf(lang_get('milestone_created'), $argsObj->name); - $guiObj->template = $basehref . $this->viewAction; - } - } - return $guiObj; - } - - - /* - function: doUpdate - - args: - - returns: - - */ - function doUpdate(&$argsObj,$basehref) - { - $date_format_cfg = config_get('date_format'); - $obj=new stdClass(); - $descr_prefix = lang_get('Milestone') . TITLE_SEP; - $obj=$this->edit($argsObj); - $obj->user_feedback = 'ok'; - $obj->template = null; - $dummy = $this->milestone_mgr->get_by_id($argsObj->id); - $originalMilestone = $dummy[$argsObj->id]; - - $op_ok=1; - - // Check name do not exists - $name_exists = $this->milestone_mgr->check_name_existence($originalMilestone['testplan_id'], - $argsObj->name,$argsObj->id); - if($name_exists) - { - $obj->user_feedback = sprintf(lang_get('milestone_name_already_exists'),$argsObj->name); - $op_ok=0; - } - - // BUGID 3716 - // are the dates valid? - if ($op_ok) { - // start date is optional - $op_ok = is_valid_date($argsObj->target_date_original, $date_format_cfg) && - ($argsObj->start_date_original == '' || is_valid_date($argsObj->start_date_original, $date_format_cfg)); - - if (!$op_ok) { - $obj->user_feedback = lang_get('warning_invalid_date'); - } - } - - // target date changed ? - if($op_ok) - { - $timestamp=array(); - $timestamp['target'] = strtotime($argsObj->target_date ." 23:59:59"); - $timestamp['original_target'] = strtotime($originalMilestone['target_date'] ." 23:59:59"); - $timestamp['now'] = strtotime("now"); - - if( ($timestamp['target'] != $timestamp['original_target']) && $timestamp['target'] < $timestamp['now'] ) - { - $op_ok=0; - $obj->user_feedback = lang_get('warning_milestone_date'); - } - } - - // BUGID 3829 - check target date > start date - if($op_ok && isset($argsObj->start_date)) { - $timestamp['target'] = strtotime($argsObj->target_date . " 23:59:59"); - $timestamp['start'] = strtotime($argsObj->start_date . " 23:59:59"); - - // target must be chronologically after start - if( $timestamp['target'] < $timestamp['start'] ) - { - $op_ok=0; - $obj->user_feedback = lang_get('warning_target_before_start'); - } - } - - if($op_ok) - { - // BUGID 3907 - start date is optional -> if empty set to default date - if (!isset($argsObj->start_date) || $argsObj->start_date == "") { - $argsObj->start_date = "0000-00-00"; - } - - $op_ok = $this->milestone_mgr->update($argsObj->id,$argsObj->name,$argsObj->target_date, - $argsObj->start_date,$argsObj->low_priority_tcases,$argsObj->medium_priority_tcases, - $argsObj->high_priority_tcases); - } - if($op_ok) - { - $obj->main_descr = ''; - $obj->action_descr=''; - $obj->template = "planMilestonesView.php"; - logAuditEvent(TLS("audit_milestone_saved",$argsObj->tplan_name,$argsObj->name), - "SAVE",$argsObj->id,"milestones"); - } - else - { - // Action has failed => no change done on DB. - $obj->main_descr = $descr_prefix . $originalMilestone['name']; - } - - return $obj; - } - - - /* - function: doDelete - - args: - - returns: object with info useful to manage user interface - - */ - function doDelete(&$argsObj,$basehref) - { - $dummy = $this->milestone_mgr->get_by_id($argsObj->id); - $milestone = $dummy[$argsObj->id]; - - $this->milestone_mgr->delete($argsObj->id); - logAuditEvent(TLS("audit_milestone_deleted",$milestone['testplan_name'],$milestone['name']), - "DELETE",$argsObj->id,"milestones"); - - $obj = new stdClass(); - $obj->template = $basehref . $this->viewAction; - $obj->user_feedback = sprintf(lang_get('milestone_deleted'),$milestone['name']); - $obj->main_descr = null; - $obj->title = lang_get('delete_milestone'); - - return $obj; - } -} -?> \ No newline at end of file +db = $db; + $this->milestone_mgr = new milestone_mgr($db); + $this->submit_button_label = lang_get('btn_save'); + } + + private function setAuditContext($auditContext) + { + $this->auditContext = $auditContext; + } + + /* + * function: create + * + * args: + * + * returns: + * + */ + public function create(&$argsObj) + { + $guiObj = new stdClass(); + $guiObj->main_descr = lang_get('testplan') . TITLE_SEP; + $guiObj->action_descr = lang_get('create_milestone'); + $guiObj->template = $this->defaultTemplate; + $guiObj->submit_button_label = $this->submit_button_label; + $guiObj->milestone = array( + 'id' => 0, + 'name' => '', + 'target_date' => '', + 'start_date' => '', + 'high_percentage' => '', + 'medium_percentage' => '', + 'low_percentage' => '', + 'testplan_id' => $argsObj->tplan_id, + 'testplan_name' => $argsObj->tplan_name + ); + return $guiObj; + } + + /* + * function: edit + * + * args: + * + * returns: + * + */ + public function edit(&$argsObj) + { + $guiObj = new stdClass(); + $dummy = $this->milestone_mgr->get_by_id($argsObj->id); + $guiObj->milestone = $dummy[$argsObj->id]; + + // $dummyy necessary because localize_dateOrTimeStamp wants second parameter to be passed by reference + $dummy = null; + + // localize target date (is always set on edit) + $guiObj->milestone['target_date'] = localize_dateOrTimeStamp(null, + $dummy, 'date_format', $guiObj->milestone['target_date']); + + // as start date is optional it can be "0000-00-00" (default timestamp) + if ($guiObj->milestone['start_date'] != "0000-00-00") { + $guiObj->milestone['start_date'] = localize_dateOrTimeStamp(null, + $dummy, 'date_format', $guiObj->milestone['start_date']); + } else { + $guiObj->milestone['start_date'] = ""; + } + + $guiObj->main_descr = lang_get('testplan') . TITLE_SEP; + $guiObj->action_descr = sprintf(lang_get('edit_milestone'), + $guiObj->milestone['name']); + $guiObj->template = $this->defaultTemplate; + $guiObj->submit_button_label = $this->submit_button_label; + return $guiObj; + } + + /* + * function: doCreate + * + * args: + * + * returns: + * + */ + public function doCreate(&$argsObj, $basehref) + { + $date_format_cfg = config_get('date_format'); + $guiObj = new stdClass(); + $guiObj->main_descr = lang_get('Milestone') . TITLE_SEP; + $guiObj->action_descr = lang_get('create_milestone'); + $guiObj->submit_button_label = $this->submit_button_label; + $guiObj->template = null; + $op_ok = 1; + + // Check name do not exists + $name_exists = $this->milestone_mgr->check_name_existence( + $argsObj->tplan_id, $argsObj->name); + if ($name_exists) { + $guiObj->user_feedback = sprintf( + lang_get('milestone_name_already_exists'), $argsObj->name); + $op_ok = 0; + } + + // BUGID 3716 + // are the dates valid? + if ($op_ok) { + // start date is optional + $op_ok = is_valid_date($argsObj->target_date_original, + $date_format_cfg) && + ($argsObj->start_date_original == '' || + is_valid_date($argsObj->start_date_original, $date_format_cfg)); + if (! $op_ok) { + $guiObj->user_feedback = sprintf( + lang_get('warning_invalid_date')); + } + } + + // check target date + if ($op_ok) { + $timestamp = array(); + $timestamp['target'] = strtotime( + $argsObj->target_date . " 23:59:59"); + $timestamp['now'] = strtotime("now"); + + if ($timestamp['target'] < $timestamp['now']) { + $op_ok = 0; + $guiObj->user_feedback = lang_get('warning_milestone_date'); + } + } + + // BUGID 3829 - check target date > start date + if ($op_ok && isset($argsObj->start_date)) { + $timestamp['target'] = strtotime( + $argsObj->target_date . " 23:59:59"); + $timestamp['start'] = strtotime($argsObj->start_date . " 23:59:59"); + + // target must be chronologically after start + if ($timestamp['target'] < $timestamp['start']) { + $op_ok = 0; + $guiObj->user_feedback = lang_get('warning_target_before_start'); + } + } + + if ($op_ok) { + // avoid warning on event viewer + if (! isset($argsObj->start_date)) { + $argsObj->start_date = ""; + } + + $argsObj->low_priority = $argsObj->low_priority_tcases; + $argsObj->medium_priority = $argsObj->medium_priority_tcases; + $argsObj->high_priority = $argsObj->high_priority_tcases; + + $argsObj->id = $this->milestone_mgr->create($argsObj); + + $guiObj->user_feedback = 'ok'; + if ($argsObj->id > 0) { + logAuditEvent( + TLS("audit_milestone_created", $argsObj->tplan_name, + $argsObj->name), "CREATE", $argsObj->id, "milestones"); + $guiObj->user_feedback = sprintf(lang_get('milestone_created'), + $argsObj->name); + $guiObj->template = $basehref . $this->viewAction; + } + } + return $guiObj; + } + + /* + * function: doUpdate + * + * args: + * + * returns: + * + */ + public function doUpdate(&$argsObj, $basehref) + { + $date_format_cfg = config_get('date_format'); + $descr_prefix = lang_get('Milestone') . TITLE_SEP; + $obj = $this->edit($argsObj); + $obj->user_feedback = 'ok'; + $obj->template = null; + $dummy = $this->milestone_mgr->get_by_id($argsObj->id); + $originalMilestone = $dummy[$argsObj->id]; + + $op_ok = 1; + + // Check name do not exists + $name_exists = $this->milestone_mgr->check_name_existence( + $originalMilestone['testplan_id'], $argsObj->name, $argsObj->id); + if ($name_exists) { + $obj->user_feedback = sprintf( + lang_get('milestone_name_already_exists'), $argsObj->name); + $op_ok = 0; + } + + // BUGID 3716 + // are the dates valid? + if ($op_ok) { + // start date is optional + $op_ok = is_valid_date($argsObj->target_date_original, + $date_format_cfg) && + ($argsObj->start_date_original == '' || + is_valid_date($argsObj->start_date_original, $date_format_cfg)); + + if (! $op_ok) { + $obj->user_feedback = lang_get('warning_invalid_date'); + } + } + + // target date changed ? + if ($op_ok) { + $timestamp = array(); + $timestamp['target'] = strtotime( + $argsObj->target_date . " 23:59:59"); + $timestamp['original_target'] = strtotime( + $originalMilestone['target_date'] . " 23:59:59"); + $timestamp['now'] = strtotime("now"); + + if (($timestamp['target'] != $timestamp['original_target']) && + $timestamp['target'] < $timestamp['now']) { + $op_ok = 0; + $obj->user_feedback = lang_get('warning_milestone_date'); + } + } + + // BUGID 3829 - check target date > start date + if ($op_ok && isset($argsObj->start_date)) { + $timestamp['target'] = strtotime( + $argsObj->target_date . " 23:59:59"); + $timestamp['start'] = strtotime($argsObj->start_date . " 23:59:59"); + + // target must be chronologically after start + if ($timestamp['target'] < $timestamp['start']) { + $op_ok = 0; + $obj->user_feedback = lang_get('warning_target_before_start'); + } + } + + if ($op_ok) { + // BUGID 3907 - start date is optional -> if empty set to default date + if (! isset($argsObj->start_date) || $argsObj->start_date == "") { + $argsObj->start_date = "0000-00-00"; + } + + $op_ok = $this->milestone_mgr->update($argsObj->id, $argsObj->name, + $argsObj->target_date, $argsObj->start_date, + $argsObj->low_priority_tcases, $argsObj->medium_priority_tcases, + $argsObj->high_priority_tcases); + } + if ($op_ok) { + $obj->main_descr = ''; + $obj->action_descr = ''; + $obj->template = "planMilestonesView.php"; + logAuditEvent( + TLS("audit_milestone_saved", $argsObj->tplan_name, + $argsObj->name), "SAVE", $argsObj->id, "milestones"); + } else { + // Action has failed => no change done on DB. + $obj->main_descr = $descr_prefix . $originalMilestone['name']; + } + + return $obj; + } + + /* + * function: doDelete + * + * args: + * + * returns: object with info useful to manage user interface + * + */ + public function doDelete(&$argsObj, $basehref) + { + $dummy = $this->milestone_mgr->get_by_id($argsObj->id); + $milestone = $dummy[$argsObj->id]; + + $this->milestone_mgr->delete($argsObj->id); + logAuditEvent( + TLS("audit_milestone_deleted", $milestone['testplan_name'], + $milestone['name']), "DELETE", $argsObj->id, "milestones"); + + $obj = new stdClass(); + $obj->template = $basehref . $this->viewAction; + $obj->user_feedback = sprintf(lang_get('milestone_deleted'), + $milestone['name']); + $obj->main_descr = null; + $obj->title = lang_get('delete_milestone'); + + return $obj; + } +} +?> diff --git a/lib/plan/planMilestonesEdit.php b/lib/plan/planMilestonesEdit.php index 86faea310d..d7e0cd446a 100644 --- a/lib/plan/planMilestonesEdit.php +++ b/lib/plan/planMilestonesEdit.php @@ -1,201 +1,208 @@ -doAction; -$op = null; -if(method_exists($commandMgr,$pFn)) -{ - $op = $commandMgr->$pFn($args,$_SESSION['basehref']); +tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$commandMgr = new planMilestonesCommands($db); + +$pFn = $args->doAction; +$op = null; +if (method_exists($commandMgr, $pFn)) { + $op = $commandMgr->$pFn($args, $_SESSION['basehref']); +} + +renderGui($args, $gui, $op, $templateCfg); + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs(&$dbHandler, $dateFormat) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + + $args->target_date_original = isset($_REQUEST['target_date']) ? $_REQUEST['target_date'] : null; + $args->start_date_original = isset($_REQUEST['start_date']) ? $_REQUEST['start_date'] : null; + + // convert target date to iso format to write to db + if (isset($_REQUEST['target_date']) && $_REQUEST['target_date'] != '') { + $date_array = split_localized_date($_REQUEST['target_date'], $dateFormat); + if ($date_array != null) { + // set date in iso format + $args->target_date = $date_array['year'] . "-" . $date_array['month'] . + "-" . $date_array['day']; + } + } + + // convert start date to iso format to write to db + if (isset($_REQUEST['start_date']) && $_REQUEST['start_date'] != '') { + $date_array = split_localized_date($_REQUEST['start_date'], $dateFormat); + if ($date_array != null) { + // set date in iso format + $args->start_date = $date_array['year'] . "-" . $date_array['month'] . + "-" . $date_array['day']; + } + } + + $key2loop = array( + 'low_priority_tcases', + 'medium_priority_tcases', + 'high_priority_tcases' + ); + foreach ($key2loop as $key) { + $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; + } + + $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; + $args->name = isset($_REQUEST['milestone_name']) ? $_REQUEST['milestone_name'] : null; + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; + + $args->basehref = $_SESSION['basehref']; + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ""; + + $args->tplan_name = ''; + $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : 0; + if ($args->tplan_id == 0) { + $args->tplan_id = isset($_SESSION['testplanID']) ? intval( + $_SESSION['testplanID']) : 0; + } + if ($args->tplan_id > 0) { + $tplan_mgr = new testplan($dbHandler); + $info = $tplan_mgr->get_by_id($args->tplan_id); + $args->tplan_name = $info['name']; + } + + return $args; +} + +/* + * function: renderGui + * + * args: + * + * returns: + * + */ +function renderGui(&$argsObj, $guiObj, $opObj, $templateCfg) +{ + $smartyObj = new TLSmarty(); + // + // key: operation requested (normally received from GUI on doAction) + // value: operation value to set on doAction HTML INPUT + // This is useful when you use same template (example xxEdit.tpl), for create and edit. + // When template is used for create -> operation: doCreate. + // When template is used for edit -> operation: doUpdate. + // + // used to set value of: $guiObj->operation + // + $actionOperation = array( + 'create' => 'doCreate', + 'edit' => 'doUpdate', + 'doDelete' => '', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate' + ); + + $renderType = 'none'; + switch ($argsObj->doAction) { + case "edit": + case "create": + case "doDelete": + case "doCreate": + case "doUpdate": + $renderType = 'template'; + $key2loop = get_object_vars($opObj); + foreach ($key2loop as $key => $value) { + $guiObj->$key = $value; + } + $guiObj->operation = $actionOperation[$argsObj->doAction]; + + $tplDir = (! isset($opObj->template_dir) || + is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; + $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; + + $pos = strpos($tpl, '.php'); + if ($pos === false) { + $tpl = $tplDir . $tpl; + } else { + $renderType = 'redirect'; + } + break; + } + + switch ($renderType) { + case 'template': + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($tpl); + break; + + case 'redirect': + header("Location: {$tpl}"); + exit(); + break; + + default: + break; + } +} + +/* + * function: initialize_gui + * + * args : - + * + * returns: + * + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $gui = new stdClass(); + + $gui->user_feedback = null; + $gui->main_descr = lang_get('req_spec'); + $gui->action_descr = null; + + $gui->grants = new stdClass(); + $gui->grants->milestone_mgmt = has_rights($dbHandler, "testplan_planning"); + $gui->grants->mgt_view_events = has_rights($dbHandler, "mgt_view_events"); + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_planning" + ]; + pageAccessCheck($db, $user, $context); } - -renderGui($args,$gui,$op,$templateCfg); - - -/* - function: - - args : - - returns: - -*/ -function init_args(&$dbHandler,$dateFormat) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - - $args->target_date_original = isset($_REQUEST['target_date']) ? $_REQUEST['target_date'] : null; - $args->start_date_original = isset($_REQUEST['start_date']) ? $_REQUEST['start_date'] : null; - - // convert target date to iso format to write to db - if (isset($_REQUEST['target_date']) && $_REQUEST['target_date'] != '') { - $date_array = split_localized_date($_REQUEST['target_date'], $dateFormat); - if ($date_array != null) { - // set date in iso format - $args->target_date = $date_array['year'] . "-" . $date_array['month'] . "-" . $date_array['day']; - } - } - - // convert start date to iso format to write to db - if (isset($_REQUEST['start_date']) && $_REQUEST['start_date'] != '') { - $date_array = split_localized_date($_REQUEST['start_date'], $dateFormat); - if ($date_array != null) { - // set date in iso format - $args->start_date = $date_array['year'] . "-" . $date_array['month'] . "-" . $date_array['day']; - } - } - - $key2loop = array('low_priority_tcases','medium_priority_tcases','high_priority_tcases'); - foreach($key2loop as $key) - { - $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; - } - - $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; - $args->name = isset($_REQUEST['milestone_name']) ? $_REQUEST['milestone_name'] : null; - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; - - $args->basehref=$_SESSION['basehref']; - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ""; - - $args->tplan_name = ''; - $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : 0; - if( $args->tplan_id == 0 ) - { - $args->tplan_id = isset($_SESSION['testplanID']) ? intval($_SESSION['testplanID']) : 0; - } - if( $args->tplan_id > 0 ) - { - $tplan_mgr = new testplan($dbHandler); - $info = $tplan_mgr->get_by_id($args->tplan_id); - $args->tplan_name = $info['name']; - } - - return $args; -} - - -/* - function: renderGui - - args: - - returns: - -*/ -function renderGui(&$argsObj,$guiObj,$opObj,$templateCfg) -{ - $smartyObj = new TLSmarty(); - // - // key: operation requested (normally received from GUI on doAction) - // value: operation value to set on doAction HTML INPUT - // This is useful when you use same template (example xxEdit.tpl), for create and edit. - // When template is used for create -> operation: doCreate. - // When template is used for edit -> operation: doUpdate. - // - // used to set value of: $guiObj->operation - // - $actionOperation=array('create' => 'doCreate', 'edit' => 'doUpdate', - 'doDelete' => '', 'doCreate' => 'doCreate', - 'doUpdate' => 'doUpdate'); - - $renderType = 'none'; - switch($argsObj->doAction) - { - case "edit": - case "create": - case "doDelete": - case "doCreate": - case "doUpdate": - $renderType = 'template'; - $key2loop = get_object_vars($opObj); - foreach($key2loop as $key => $value) - { - $guiObj->$key = $value; - } - $guiObj->operation = $actionOperation[$argsObj->doAction]; - - $tplDir = (!isset($opObj->template_dir) || is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; - $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; - - $pos = strpos($tpl, '.php'); - if($pos === false) - { - $tpl = $tplDir . $tpl; - } - else - { - $renderType = 'redirect'; - } - break; - } - - switch($renderType) - { - case 'template': - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($tpl); - break; - - case 'redirect': - header("Location: {$tpl}"); - exit(); - break; - - default: - break; - } - -} - -/* - function: initialize_gui - - args : - - - returns: - -*/ -function initialize_gui(&$dbHandler,&$argsObj) -{ - $req_spec_mgr = new requirement_spec_mgr($dbHandler); - $gui = new stdClass(); - - $gui->user_feedback = null; - $gui->main_descr = lang_get('req_spec'); - $gui->action_descr = null; - - $gui->grants = new stdClass(); - $gui->grants->milestone_mgmt = has_rights($dbHandler,"testplan_planning"); - $gui->grants->mgt_view_events = has_rights($dbHandler,"mgt_view_events"); - - return $gui; -} - - -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,"testplan_planning")); -} -?> \ No newline at end of file diff --git a/lib/plan/planMilestonesView.php b/lib/plan/planMilestonesView.php index 08a020cdc8..5f46fe2085 100644 --- a/lib/plan/planMilestonesView.php +++ b/lib/plan/planMilestonesView.php @@ -1,93 +1,97 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/* - function: - - args : - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ""; - $args->tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0; - $args->tplan_name = isset($_SESSION['testplanName']) ? $_SESSION['testplanName'] : ""; - - return $args; +tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs() +{ + $args = new stdClass(); + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ""; + $args->tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0; + $args->tplan_name = isset($_SESSION['testplanName']) ? $_SESSION['testplanName'] : ""; + + return $args; +} + +/* + * function: initialize_gui + * + * args : - + * + * returns: + * + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $manager = new milestone_mgr($dbHandler); + $gui = new stdClass(); + + $gui->user_feedback = null; + $gui->main_descr = lang_get('title_milestones') . " " . $argsObj->tplan_name; + $gui->action_descr = null; + $gui->tplan_name = $argsObj->tplan_name; + $gui->tplan_id = $argsObj->tplan_id; + $gui->items = $manager->get_all_by_testplan($argsObj->tplan_id); + $gui->itemsLive = null; + + if (! is_null($gui->items)) { + $metrics = new tlTestPlanMetrics($dbHandler); + $gui->itemsLive = $metrics->getMilestonesMetrics($argsObj->tplan_id, + $gui->items); + } + + $gui->grants = new stdClass(); + $gui->grants->milestone_mgmt = has_rights($dbHandler, "testplan_planning"); + $gui->grants->mgt_view_events = has_rights($dbHandler, "mgt_view_events"); + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_planning" + ]; + pageAccessCheck($db, $user, $context); } - -/* - function: initialize_gui - - args : - - - returns: - -*/ -function initialize_gui(&$dbHandler,&$argsObj) -{ - $manager = new milestone_mgr($dbHandler); - $gui = new stdClass(); - - $gui->user_feedback = null; - $gui->main_descr = lang_get('title_milestones') . " " . $argsObj->tplan_name; - $gui->action_descr = null; - $gui->tplan_name = $argsObj->tplan_name; - $gui->tplan_id = $argsObj->tplan_id; - $gui->items = $manager->get_all_by_testplan($argsObj->tplan_id); - $gui->itemsLive = null; - - if(!is_null($gui->items)) - { - $metrics = new tlTestPlanMetrics($dbHandler); - $gui->itemsLive = $metrics->getMilestonesMetrics($argsObj->tplan_id,$gui->items); - } - - - $gui->grants = new stdClass(); - $gui->grants->milestone_mgmt = has_rights($dbHandler,"testplan_planning"); - $gui->grants->mgt_view_events = has_rights($dbHandler,"mgt_view_events"); - - return $gui; -} - - -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,"testplan_planning")); -} -?> \ No newline at end of file diff --git a/lib/plan/planTCNavigator.php b/lib/plan/planTCNavigator.php index 88fb551689..614971a7a0 100644 --- a/lib/plan/planTCNavigator.php +++ b/lib/plan/planTCNavigator.php @@ -16,11 +16,11 @@ * **/ -require('../../config.inc.php'); -require_once("common.php"); -require_once("users.inc.php"); -require_once("treeMenu.inc.php"); -require_once('exec.inc.php'); +require_once '../../config.inc.php'; +require_once 'common.php'; +require_once 'users.inc.php'; +require_once 'treeMenu.inc.php'; +require_once 'exec.inc.php'; testlinkInitPage($db); $templateCfg = templateConfiguration(); @@ -43,13 +43,13 @@ /** - * @param unknown_type $dbHandler - * @param unknown_type $control + * @param database $dbHandler + * @param tlTestCaseFilterControl $control * @return stdClass - * + * * @internal revisions: */ -function initializeGui(&$dbHandler, &$control, &$assignmentMgr) +function initializeGui(&$dbHandler, &$control, &$assignmentMgr) { $gui = new stdClass(); @@ -60,7 +60,7 @@ function initializeGui(&$dbHandler, &$control, &$assignmentMgr) $gui->additional_string = ''; // configure target URLs and clickable buttons - switch($control->args->feature) + switch($control->args->feature) { case 'planUpdateTC': $gui->menuUrl = "lib/plan/planUpdateTC.php"; @@ -76,7 +76,7 @@ function initializeGui(&$dbHandler, &$control, &$assignmentMgr) case 'tc_exec_assignment': $gui->title_navigator = lang_get('navigator_tc_exec_assignment'); $gui->menuUrl = "lib/plan/tc_exec_assignment.php"; - $build_id = $control->settings['setting_build']['selected']; + $control->settings['setting_build']['selected']; $control->draw_tc_unassign_button = true; $control->draw_tc_assignment_bulk_copy_button = true; @@ -84,4 +84,4 @@ function initializeGui(&$dbHandler, &$control, &$assignmentMgr) } return $gui; -} \ No newline at end of file +} diff --git a/lib/plan/planUpdateTC.php b/lib/plan/planUpdateTC.php index 063ceeec5f..0bcfb3fe82 100644 --- a/lib/plan/planUpdateTC.php +++ b/lib/plan/planUpdateTC.php @@ -1,430 +1,432 @@ -keyword_id)) -{ - $keywordsFilter = new stdClass(); - $keywordsFilter->items = $args->keyword_id; - $keywordsFilter->type = $gui->keywordsFilterType->selected; +tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$keywordsFilter = null; +if (is_array($args->keyword_id)) { + $keywordsFilter = new stdClass(); + $keywordsFilter->items = $args->keyword_id; + $keywordsFilter->type = $gui->keywordsFilterType->selected; +} + +switch ($args->doAction) { + case "doUpdate": + case "doBulkUpdateToLatest": + $gui->user_feedback = doUpdate($db, $args); + break; + + default: + break; +} + +$out = null; +$gui->show_details = 0; +$gui->operationType = 'standard'; +$gui->hasItems = 0; + +switch ($args->level) { + case 'testcase': + $out = processTestCase($db, $args, $tplan_mgr, $tree_mgr); + break; + + case 'testsuite': + $out = processTestSuite($db, $args, $keywordsFilter, $tplan_mgr, + $tcaseMgr); + break; + + case 'testplan': + $itemSet = processTestPlan($db, $args, $tplan_mgr); + $gui->testcases = $itemSet['items']; + $gui->user_feedback = $itemSet['msg']; + $gui->instructions = lang_get('update2latest'); + $gui->buttonAction = "doBulkUpdateToLatest"; + $gui->operationType = 'bulk'; + if (! is_null($gui->testcases)) { + $gui->hasItems = 1; + $gui->show_details = 1; + } + break; + + default: + // show instructions + redirect( + $_SESSION['basehref'] . + "/lib/general/staticPage.php?key=planUpdateTC"); + break; +} + +if (! is_null($out)) { + $gui->hasItems = $out['num_tc'] > 0 ? 1 : 0; + $gui->items = $out['spec_view']; +} + +if ($gui->buttonAction == 'doUpdate') { + $gui->action_descr = lang_get('update_testcase_versions'); +} else { + $gui->action_descr = lang_get('update_all_testcase_versions'); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get input from user and return it in some sort of namespace + * + * @param testplan $tplanMgr + * @return stdClass object with some REQUEST and SESSION values as members + */ +function initArgs(&$tplanMgr) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null; + $args->level = isset($_REQUEST['level']) ? $_REQUEST['level'] : null; + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; + + // Maps with key: test case ID value: tcversion_id + $args->fullTestCaseSet = isset($_REQUEST['a_tcid']) ? $_REQUEST['a_tcid'] : null; + $args->checkedTestCaseSet = isset($_REQUEST['achecked_tc']) ? $_REQUEST['achecked_tc'] : null; + $args->newVersionSet = isset($_REQUEST['new_tcversion_for_tcid']) ? $_REQUEST['new_tcversion_for_tcid'] : null; + $args->version_id = isset($_REQUEST['version_id']) ? $_REQUEST['version_id'] : 0; + + $args->tproject_id = $_SESSION['testprojectID']; + $args->tproject_name = $_SESSION['testprojectName']; + + // For more information about the data accessed in session here, see the comment + // in the file header of lib/functions/tlTestCaseFilterControl.class.php. + $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + + $mode = 'plan_mode'; + + $session_data = isset($_SESSION[$mode]) && + isset($_SESSION[$mode][$form_token]) ? $_SESSION[$mode][$form_token] : null; + + $args->tplan_id = isset($session_data['setting_testplan']) ? $session_data['setting_testplan'] : 0; + if ($args->tplan_id == 0) { + $args->tplan_id = isset($_SESSION['testplanID']) ? intval( + $_SESSION['testplanID']) : 0; + $args->tplan_name = $_SESSION['testplanName']; + } else { + $tpi = $tplanMgr->get_by_id($args->tplan_id); + $args->tplan_name = $tpi['name']; + } + + $args->refreshTree = isset($session_data['setting_refresh_tree_on_action']) ? $session_data['setting_refresh_tree_on_action'] : 0; + + $args->keyword_id = 0; + $fk = 'filter_keywords'; + if (isset($session_data[$fk])) { + $args->keyword_id = $session_data[$fk]; + if (is_array($args->keyword_id) && count($args->keyword_id) == 1) { + $args->keyword_id = $args->keyword_id[0]; + } + } + + $args->keywordsFilterType = null; + $ft = 'filter_keywords_filter_type'; + if (isset($session_data[$ft])) { + $args->keywordsFilterType = $session_data[$ft]; + } + + return $args; +} + +/** + * doUpdate + * + * @param database $dbObj + * @param stdClass $argsObj + * @return string + */ +function doUpdate(&$dbObj, &$argsObj) +{ + $debugMsg = 'File:' . __FILE__ . ' - Function: ' . __FUNCTION__; + $tables = tlObject::getDBTables( + array( + 'testplan_tcversions', + 'executions', + 'cfield_execution_values' + )); + $msg = ""; + if (! is_null($argsObj->checkedTestCaseSet)) { + foreach ($argsObj->checkedTestCaseSet as $tcaseID => $tcversionID) { + $newtcversion = $argsObj->newVersionSet[$tcaseID]; + foreach ($tables as $table2update) { + $sql = "/* $debugMsg */ UPDATE $table2update " . + " SET tcversion_id={$newtcversion} " . + " WHERE tcversion_id={$tcversionID} " . + " AND testplan_id={$argsObj->tplan_id}"; + $dbObj->exec_query($sql); + } + } + $msg = lang_get("tplan_updated"); + } + return $msg; +} + +/** + * Initializes the GUI + * + * @param stdClass $argsObj + * @param testcase $tcaseMgr + * @return stdClass + */ +function initializeGui($argsObj, &$tcaseMgr) +{ + $tcase_cfg = config_get('testcase_cfg'); + $gui = new stdClass(); + $gui->refreshTree = false; + $gui->instructions = ''; + $gui->buttonAction = "doUpdate"; + $gui->testCasePrefix = $tcaseMgr->tproject_mgr->getTestCasePrefix( + $argsObj->tproject_id); + $gui->testCasePrefix .= $tcase_cfg->glue_character; + $gui->user_feedback = ''; + $gui->testPlanName = $argsObj->tplan_name; + $gui->items = null; + $gui->has_tc = 1; + + return $gui; +} + +/** + * processTestSuite + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param + * $keywordsFilter + * @param testplan $tplanMgr + * @param testcase $tcaseMgr + * @return array + */ +function processTestSuite(&$dbHandler, &$argsObj, $keywordsFilter, &$tplanMgr, + &$tcaseMgr) +{ + // hmm need to document why we use ONLY $keywordsFilter + $out = getFilteredSpecView($dbHandler, $argsObj, $tplanMgr, $tcaseMgr, + array( + 'keywordsFilter' => $keywordsFilter + )); + tideUpForGUI($out); + return $out; +} + +/** + * doUpdateAllToLatest + * + * @param database $dbObj + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @return array + */ +function doUpdateAllToLatest(&$dbObj, $argsObj, &$tplanMgr) +{ + $qty = 0; + $linkedItems = $tplanMgr->get_linked_items_id($argsObj->tplan_id); + if (is_null($linkedItems)) { + return lang_get('no_testcase_available'); + } + + $items = $tplanMgr->get_linked_and_newest_tcversions($argsObj->tplan_id); + if (! is_null($items)) { + foreach ($items as $value) { + if ($value['newest_tcversion_id'] != $value['tcversion_id']) { + $newtcversion = $value['newest_tcversion_id']; + $tcversionID = $value['tcversion_id']; + $qty ++; + + // Update link to testplan + $sql = "UPDATE testplan_tcversions " . + " SET tcversion_id={$newtcversion} " . + " WHERE tcversion_id={$tcversionID} " . + " AND testplan_id={$argsObj->tplan_id}"; + $dbObj->exec_query($sql); + + // Update link in executions + $sql = "UPDATE executions " . + " SET tcversion_id={$newtcversion} " . + " WHERE tcversion_id={$tcversionID}" . + " AND testplan_id={$argsObj->tplan_id}"; + $dbObj->exec_query($sql); + + // Update link in cfields values + $sql = "UPDATE cfield_execution_values " . + " SET tcversion_id={$newtcversion} " . + " WHERE tcversion_id={$tcversionID}" . + " AND testplan_id={$argsObj->tplan_id}"; + $dbObj->exec_query($sql); + } + } + } + $qty == 0 ? $msg = lang_get('all_versions_where_latest') : $msg = sprintf( + lang_get('num_of_updated'), $qty); + + return $msg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @param tree $treeMgr + * @return array|array[]|number[] + */ +function processTestCase(&$dbHandler, &$argsObj, &$tplanMgr, &$treeMgr) +{ + $xx = $tplanMgr->getLinkInfo($argsObj->tplan_id, $argsObj->id, null, + array( + 'output' => 'tcase_info', + 'collapse' => true + )); + $linked_items[$xx['tc_id']][0] = $xx; // adapt data structure to gen_spec_view() desires + + $my_path = $treeMgr->get_path($argsObj->id); + $idx_ts = count($my_path) - 1; + $tsuite_data = $my_path[$idx_ts - 1]; + + // Again here need to understand why we seems to consider ONLY keywords filter. + $filters = array( + 'keywords' => $argsObj->keyword_id, + 'testcases' => $argsObj->id + ); + $opt = array( + 'write_button_only_if_linked' => 1, + 'prune_unlinked_tcversions' => 1 + ); + $out = gen_spec_view($dbHandler, 'testplan', $argsObj->tplan_id, + $tsuite_data['id'], $tsuite_data['name'], $linked_items, null, $filters, + $opt); + + // need new processing + tideUpForGUI($out); + return $out; +} + +/** + * + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @return string|NULL[]|string[] + * @internal revisions: + */ +function processTestPlan(&$argsObj, &$tplanMgr) +{ + $set2update = array( + 'items' => null, + 'msg' => '' + ); + $check = $tplanMgr->getLinkedCount($argsObj->tplan_id); + $set2update['msg'] = $check == 0 ? lang_get('testplan_seems_empty') : lang_get( + 'no_newest_version_of_linked_tcversions'); + + $set2update['items'] = $tplanMgr->get_linked_and_newest_tcversions( + $argsObj->tplan_id); + if (! empty($set2update['items']) && ! is_null($set2update['items'])) { + $set2update['msg'] = ''; + $itemSet = array_keys($set2update['items']); + $path_info = $tplanMgr->tree_manager->get_full_path_verbose($itemSet); + foreach ($set2update['items'] as $tcase_id => $value) { + $path = $path_info[$tcase_id]; + unset($path[0]); + $path[] = ''; + $set2update['items'][$tcase_id]['path'] = implode(' / ', $path); + } + } + return $set2update; +} + +/** + * + * @param array $output + */ +function tideUpForGUI(&$output) +{ + // We are going to loop over test suites + $loop2do = count($output['spec_view']); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $itemSet = &$output['spec_view'][$idx]['testcases']; + if (! empty($itemSet)) { + $key2loop = array_keys($itemSet); + foreach ($key2loop as $tcaseID) { + // want to understand + // How many active test case versions exist + // if we have ONLY one active test case version + // is this version the ALREADY LINKED one ? + // if we have ZERO ACTIVE VERSIONS + // + $active = 0; + foreach ($itemSet[$tcaseID]['tcversions_active_status'] as $status) { + if ($status) { + $active ++; + } + } + + $itemSet[$tcaseID]['updateTarget'] = $itemSet[$tcaseID]['tcversions']; + $lnItem = $itemSet[$tcaseID]['linked_version_id']; + $itemSet[$tcaseID]['canUpdateVersion'] = ($active != 0); + if ($active == 1 && + $lnItem == key($itemSet[$tcaseID]['tcversions'])) { + $itemSet[$tcaseID]['canUpdateVersion'] = false; + } + if (! is_null($lnItem) && + isset($itemSet[$tcaseID]['tcversions'][$lnItem])) { + unset($itemSet[$tcaseID]['updateTarget'][$lnItem]); + if (count($itemSet[$tcaseID]['updateTarget']) == 0) { + $itemSet[$tcaseID]['updateTarget'] = null; + } + } + } + } + } +} + +/** + * Checks the user rights for accessing the page + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_planning" + ]; + pageAccessCheck($db, $user, $context); } - -switch ($args->doAction) -{ - case "doUpdate": - case "doBulkUpdateToLatest": - $gui->user_feedback = doUpdate($db,$args); - break; - - default: - break; -} - -$out = null; -$gui->show_details = 0; -$gui->operationType = 'standard'; -$gui->hasItems = 0; - -switch($args->level) -{ - case 'testcase': - $out = processTestCase($db,$args,$keywordsFilter,$tplan_mgr,$tree_mgr); - break; - - case 'testsuite': - $out = processTestSuite($db,$args,$keywordsFilter,$tplan_mgr,$tcase_mgr); - break; - - case 'testplan': - $itemSet = processTestPlan($db,$args,$tplan_mgr); - $gui->testcases = $itemSet['items']; - $gui->user_feedback = $itemSet['msg']; - $gui->instructions = lang_get('update2latest'); - $gui->buttonAction = "doBulkUpdateToLatest"; - $gui->operationType = 'bulk'; - if( !is_null($gui->testcases) ) - { - $gui->hasItems = 1; - $gui->show_details = 1; - } - break; - - default: - // show instructions - redirect($_SESSION['basehref'] . "/lib/general/staticPage.php?key=planUpdateTC"); - break; -} - -if(!is_null($out)) -{ - $gui->hasItems = $out['num_tc'] > 0 ? 1 : 0; - $gui->items = $out['spec_view']; -} - -if($gui->buttonAction == 'doUpdate') -{ - $gui->action_descr = lang_get('update_testcase_versions'); -} -else -{ - $gui->action_descr = lang_get('update_all_testcase_versions'); -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/* - function: init_args - - args : - - returns: - -*/ -function init_args(&$tplanMgr) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null; - $args->level = isset($_REQUEST['level']) ? $_REQUEST['level'] : null; - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; - - // Maps with key: test case ID value: tcversion_id - $args->fullTestCaseSet = isset($_REQUEST['a_tcid']) ? $_REQUEST['a_tcid'] : null; - $args->checkedTestCaseSet = isset($_REQUEST['achecked_tc']) ? $_REQUEST['achecked_tc'] : null; - $args->newVersionSet = isset($_REQUEST['new_tcversion_for_tcid']) ? $_REQUEST['new_tcversion_for_tcid'] : null; - $args->version_id = isset($_REQUEST['version_id']) ? $_REQUEST['version_id'] : 0; - - $args->tproject_id = $_SESSION['testprojectID']; - $args->tproject_name = $_SESSION['testprojectName']; - - // For more information about the data accessed in session here, see the comment - // in the file header of lib/functions/tlTestCaseFilterControl.class.php. - $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - - $mode = 'plan_mode'; - - $session_data = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$form_token]) - ? $_SESSION[$mode][$form_token] : null; - - $args->tplan_id = isset($session_data['setting_testplan']) ? $session_data['setting_testplan'] : 0; - if($args->tplan_id == 0) - { - $args->tplan_id = isset($_SESSION['testplanID']) ? intval($_SESSION['testplanID']) : 0; - $args->tplan_name = $_SESSION['testplanName']; - } - else - { - $tpi = $tplanMgr->get_by_id($args->tplan_id); - $args->tplan_name = $tpi['name']; - } - - $args->refreshTree = isset($session_data['setting_refresh_tree_on_action']) ? - $session_data['setting_refresh_tree_on_action'] : 0; - - $args->keyword_id = 0; - $fk = 'filter_keywords'; - if (isset($session_data[$fk])) - { - $args->keyword_id = $session_data[$fk]; - if (is_array($args->keyword_id) && count($args->keyword_id) == 1) - { - $args->keyword_id = $args->keyword_id[0]; - } - } - - $args->keywordsFilterType = null; - $ft = 'filter_keywords_filter_type'; - if (isset($session_data[$ft])) - { - $args->keywordsFilterType = $session_data[$ft]; - } - - return $args; -} - -/* - function: doUpdate - - args: - - returns: message - -*/ -function doUpdate(&$dbObj,&$argsObj) -{ - $debugMsg = 'File:' . __FILE__ . ' - Function: ' . __FUNCTION__; - $tables = tlObject::getDBTables(array('testplan_tcversions','executions', - 'cfield_execution_values')); - $msg = ""; - if(!is_null($argsObj->checkedTestCaseSet)) - { - foreach($argsObj->checkedTestCaseSet as $tcaseID => $tcversionID) - { - $newtcversion=$argsObj->newVersionSet[$tcaseID]; - foreach($tables as $table2update) - { - $sql = "/* $debugMsg */ UPDATE $table2update " . - " SET tcversion_id={$newtcversion} " . - " WHERE tcversion_id={$tcversionID} " . - " AND testplan_id={$argsObj->tplan_id}"; - $dbObj->exec_query($sql); - } - } - $msg = lang_get("tplan_updated"); - } - return $msg; -} - - -/* - function: initializeGui - - args : - - returns: - -*/ -function initializeGui(&$dbHandler,$argsObj,&$tplanMgr,&$tcaseMgr) -{ - $tcase_cfg = config_get('testcase_cfg'); - $gui = new stdClass(); - $gui->refreshTree=false; - $gui->instructions=''; - $gui->buttonAction="doUpdate"; - $gui->testCasePrefix = $tcaseMgr->tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - $gui->testCasePrefix .= $tcase_cfg->glue_character; - $gui->user_feedback = ''; - $gui->testPlanName = $argsObj->tplan_name; - $gui->items = null; - $gui->has_tc = 1; - - return $gui; -} - - -/* - function: processTestSuite - - args : - - returns: - -*/ -function processTestSuite(&$dbHandler,&$argsObj,$keywordsFilter,&$tplanMgr,&$tcaseMgr) -{ - // hmm need to document why we use ONLY $keywordsFilter - $out = getFilteredSpecView($dbHandler,$argsObj,$tplanMgr,$tcaseMgr,array('keywordsFilter' => $keywordsFilter)); - tideUpForGUI($out); - return $out; -} - - -/* - function: doUpdateAllToLatest - - args: - - returns: message - -*/ -function doUpdateAllToLatest(&$dbObj,$argsObj,&$tplanMgr) -{ - $qty=0; - $linkedItems = $tplanMgr->get_linked_items_id($argsObj->tplan_id); - if( is_null($linkedItems) ) - { - return lang_get('no_testcase_available'); - } - - $items=$tplanMgr->get_linked_and_newest_tcversions($argsObj->tplan_id); - if( !is_null($items) ) - { - foreach($items as $key => $value) - { - if( $value['newest_tcversion_id'] != $value['tcversion_id'] ) - { - $newtcversion=$value['newest_tcversion_id']; - $tcversionID=$value['tcversion_id']; - $qty++; - - // Update link to testplan - $sql = "UPDATE testplan_tcversions " . - " SET tcversion_id={$newtcversion} " . - " WHERE tcversion_id={$tcversionID} " . - " AND testplan_id={$argsObj->tplan_id}"; - $dbObj->exec_query($sql); - - // Update link in executions - $sql = "UPDATE executions " . - " SET tcversion_id={$newtcversion} " . - " WHERE tcversion_id={$tcversionID}" . - " AND testplan_id={$argsObj->tplan_id}"; - $dbObj->exec_query($sql); - - // Update link in cfields values - $sql = "UPDATE cfield_execution_values " . - " SET tcversion_id={$newtcversion} " . - " WHERE tcversion_id={$tcversionID}" . - " AND testplan_id={$argsObj->tplan_id}"; - $dbObj->exec_query($sql); - } - } - } - if( $qty == 0 ) - { - $msg=lang_get('all_versions_where_latest'); - } - else - { - $msg=sprintf(lang_get('num_of_updated'),$qty); - } - - return $msg; -} - - -/** - * - * - */ -function processTestCase(&$dbHandler,&$argsObj,$keywordsFilter,&$tplanMgr,&$treeMgr) -{ - $xx = $tplanMgr->getLinkInfo($argsObj->tplan_id,$argsObj->id,null, - array('output' => 'tcase_info', 'collapse' => true)); - $linked_items[$xx['tc_id']][0] = $xx; // adapt data structure to gen_spec_view() desires - - $my_path = $treeMgr->get_path($argsObj->id); - $idx_ts = count($my_path)-1; - $tsuite_data = $my_path[$idx_ts-1]; - - // Again here need to understand why we seems to consider ONLY keywords filter. - $filters = array('keywords' => $argsObj->keyword_id, 'testcases' => $argsObj->id); - $opt = array('write_button_only_if_linked' => 1, 'prune_unlinked_tcversions' => 1); - $out = gen_spec_view($dbHandler,'testplan',$argsObj->tplan_id,$tsuite_data['id'],$tsuite_data['name'], - $linked_items,null,$filters,$opt); - - // need new processing - tideUpForGUI($out); - return $out; -} - -/** - * - * - * @internal revisions: - */ -function processTestPlan(&$dbHandler,&$argsObj,&$tplanMgr) -{ - $set2update = array('items' => null, 'msg' => ''); - $check = $tplanMgr->getLinkedCount($argsObj->tplan_id); - $set2update['msg'] = $check == 0 ? lang_get('testplan_seems_empty') : - lang_get('no_newest_version_of_linked_tcversions'); - - $set2update['items'] = $tplanMgr->get_linked_and_newest_tcversions($argsObj->tplan_id); - if( count($set2update['items']) > 0 ) - { - if( !is_null($set2update['items']) && count($set2update['items']) > 0 ) - { - $set2update['msg'] = ''; - $itemSet=array_keys($set2update['items']); - $path_info=$tplanMgr->tree_manager->get_full_path_verbose($itemSet); - foreach($set2update['items'] as $tcase_id => $value) - { - $path=$path_info[$tcase_id]; - unset($path[0]); - $path[]=''; - $set2update['items'][$tcase_id]['path']=implode(' / ',$path); - } - } - } - return $set2update; -} - - -function tideUpForGUI(&$output) -{ - // We are going to loop over test suites - $loop2do = count($output['spec_view']); - for($idx=0; $idx < $loop2do; $idx++) - { - $itemSet = &$output['spec_view'][$idx]['testcases']; - if( count($itemSet) > 0) - { - $key2loop = array_keys($itemSet); - foreach($key2loop as $tcaseID) - { - // want to understand - // How many active test case versions exist - // if we have ONLY one active test case version - // is this version the ALREADY LINKED one ? - // if we have ZERO ACTIVE VERSIONS - // - $active = 0; - $total = count($itemSet[$tcaseID]['tcversions_active_status']); - foreach($itemSet[$tcaseID]['tcversions_active_status'] as $status) - { - if($status) - { - $active++; - } - } - - $itemSet[$tcaseID]['updateTarget'] = $itemSet[$tcaseID]['tcversions']; - $lnItem = $itemSet[$tcaseID]['linked_version_id']; - $itemSet[$tcaseID]['canUpdateVersion'] = ($active != 0); - if($active == 1) - { - // linked_version_id - if( $lnItem == key($itemSet[$tcaseID]['tcversions']) ) - { - $itemSet[$tcaseID]['canUpdateVersion'] = FALSE; - } - } - if( !is_null($lnItem) && isset($itemSet[$tcaseID]['tcversions'][$lnItem]) ) - { - unset($itemSet[$tcaseID]['updateTarget'][$lnItem]); - if(count($itemSet[$tcaseID]['updateTarget']) == 0) - { - $itemSet[$tcaseID]['updateTarget'] = null; - } - } - } - } - - } -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_planning'); -} \ No newline at end of file diff --git a/lib/plan/planUrgency.php b/lib/plan/planUrgency.php index d65714cb6d..654d417b85 100644 --- a/lib/plan/planUrgency.php +++ b/lib/plan/planUrgency.php @@ -1,170 +1,167 @@ -show_help) -{ - show_instructions('test_urgency'); - exit(); +tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +if ($args->show_help) { + show_instructions('test_urgency'); + exit(); +} + +$templateCfg = templateConfiguration(); +$tplan_mgr = new testPlanUrgency($db); +$gui = initializeGui($args, $tplan_mgr->tree_manager); + +if ($args->urgency != OFF || isset($args->urgency_tc)) { + $gui->user_feedback = doProcess($args, $tplan_mgr); +} + +// get the current urgency for child test cases +$context = new stdClass(); +$context->tplan_id = $args->tplan_id; +$context->tsuite_id = $args->node_id; +$context->tproject_id = $args->tproject_id; +$context->platform_id = $args->platform_id; + +$gui->listTestCases = $tplan_mgr->getSuiteUrgency($context, + array( + 'build4testers' => $args->build4testers + ), array( + 'testcases' => $args->testCaseSet + )); + +foreach ($gui->listTestCases as $tcversion_id => $tcaseSet) { + foreach ($tcaseSet as $idx => $tcase) { + $gui->listTestCases[$tcversion_id][$idx]['priority'] = priority_to_level( + $tcase['priority']); + } +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: init_args() + * + * args: - + * + * returns: object with user input. + * + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->show_help = (isset($_REQUEST['level']) && + $_REQUEST['level'] == 'testproject'); + + $args->tproject_id = intval( + isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']); + $args->tplan_id = intval( + isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); + $args->tplan_name = $_SESSION['testplanName']; + $args->node_type = isset($_REQUEST['level']) ? $_REQUEST['level'] : OFF; + $args->node_id = isset($_REQUEST['id']) ? $_REQUEST['id'] : ERROR; + + // Sets urgency for suite + + if (isset($_REQUEST['high_urgency'])) { + $args->urgency = HIGH; + } elseif (isset($_REQUEST['medium_urgency'])) { + $args->urgency = MEDIUM; + } elseif (isset($_REQUEST['low_urgency'])) { + $args->urgency = LOW; + } else { + $args->urgency = OFF; + } + + // Sets urgency for every single tc + if (isset($_REQUEST['urgency'])) { + $args->urgency_tc = $_REQUEST['urgency']; + } + + // For more information about the data accessed in session here, see the comment + // in the file header of lib/functions/tlTestCaseFilterControl.class.php. + $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $mode = 'plan_mode'; + $session_data = isset($_SESSION[$mode]) && + isset($_SESSION[$mode][$args->treeFormToken]) ? $_SESSION[$mode][$args->treeFormToken] : null; + + $args->testCaseSet = $session_data['testcases_to_show']; + $args->build4testers = intval($session_data['setting_build']); + $args->platform_id = intval($session_data['setting_platform']); + + return $args; +} + +/** + */ +function initializeGui(&$argsObj, &$treeMgr) +{ + $guiObj = new stdClass(); + + $ni = $treeMgr->get_node_hierarchy_info($argsObj->node_id); + $guiObj->node_name = $ni['name']; + $guiObj->user_feedback = null; + $guiObj->node_id = $argsObj->node_id; + $guiObj->tplan_id = $argsObj->tplan_id; + $guiObj->tplan_name = $argsObj->tplan_name; + $guiObj->formToken = $argsObj->treeFormToken; + return $guiObj; +} + +/** + */ +function doProcess(&$argsObj, &$tplanMgr) +{ + $userFeedback = null; + + // Set urgency for test suite + if ($argsObj->urgency != OFF) { + $userFeedback['type'] = $tplanMgr->setSuiteUrgency($argsObj->tplan_id, + $argsObj->node_id, $argsObj->urgency); + $userFeedback['message'] = lang_get( + ($userFeedback['type'] == OK) ? "feedback_urgency_ok" : "feedback_urgency_fail"); + } + + // Set urgency for individual testcases + if (isset($argsObj->urgency_tc)) { + foreach ($argsObj->urgency_tc as $id => $urgency) { + $tplanMgr->setTestUrgency($argsObj->tplan_id, intval($id), + intval($urgency)); + } + } + + return $userFeedback; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_planning" + ]; + pageAccessCheck($db, $user, $context); } -$templateCfg = templateConfiguration(); -$tplan_mgr = new testPlanUrgency($db); -$gui = initializeGui($args,$tplan_mgr->tree_manager); - -if( $args->urgency != OFF || isset($args->urgency_tc) ) -{ - $gui->user_feedback = doProcess($args,$tplan_mgr); -} - - -// get the current urgency for child test cases -$context = new stdClass(); -$context->tplan_id = $args->tplan_id; -$context->tsuite_id = $args->node_id; -$context->tproject_id = $args->tproject_id; -$context->platform_id = $args->platform_id; - -$gui->listTestCases = $tplan_mgr->getSuiteUrgency($context,array('build4testers' => $args->build4testers), - array('testcases' => $args->testCaseSet)); - -foreach($gui->listTestCases as $tcversion_id => $tcaseSet) -{ - foreach($tcaseSet as $idx => $tcase) - { - $gui->listTestCases[$tcversion_id][$idx]['priority'] = priority_to_level($tcase['priority']); - } -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: init_args() - - args: - - - returns: object with user input. - -*/ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->show_help = (isset($_REQUEST['level']) && $_REQUEST['level']=='testproject'); - - $args->tproject_id = intval(isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']); - $args->tplan_id = intval(isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); - $args->tplan_name = $_SESSION['testplanName']; - $args->node_type = isset($_REQUEST['level']) ? $_REQUEST['level'] : OFF; - $args->node_id = isset($_REQUEST['id']) ? $_REQUEST['id'] : ERROR; - - // Sets urgency for suite - - if (isset($_REQUEST['high_urgency'])) - { - $args->urgency = HIGH; - } - elseif (isset($_REQUEST['medium_urgency'])) - { - $args->urgency = MEDIUM; - } - elseif (isset($_REQUEST['low_urgency'])) - { - $args->urgency = LOW; - } - else - { - $args->urgency = OFF; - } - - // Sets urgency for every single tc - if (isset($_REQUEST['urgency'])) - { - $args->urgency_tc = $_REQUEST['urgency']; - } - - // For more information about the data accessed in session here, see the comment - // in the file header of lib/functions/tlTestCaseFilterControl.class.php. - $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $mode = 'plan_mode'; - $session_data = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$args->treeFormToken]) ? - $_SESSION[$mode][$args->treeFormToken] : null; - - - $args->testCaseSet = $session_data['testcases_to_show']; - $args->build4testers = intval($session_data['setting_build']); - $args->platform_id = intval($session_data['setting_platform']); - - return $args; -} - -/** - * - */ -function initializeGui(&$argsObj,&$treeMgr) -{ - $guiObj = new stdClass(); - - $ni = $treeMgr->get_node_hierarchy_info($argsObj->node_id); - $guiObj->node_name = $ni['name']; - $guiObj->user_feedback = null; - $guiObj->node_id = $argsObj->node_id; - $guiObj->tplan_id = $argsObj->tplan_id; - $guiObj->tplan_name = $argsObj->tplan_name; - $guiObj->formToken = $argsObj->treeFormToken; - return $guiObj; -} - - -/** - * - */ -function doProcess(&$argsObj,&$tplanMgr) -{ - $userFeedback = null; - - // Set urgency for test suite - if($argsObj->urgency != OFF) - { - $userFeedback['type'] = $tplanMgr->setSuiteUrgency($argsObj->tplan_id, $argsObj->node_id, $argsObj->urgency); - $userFeedback['message'] = lang_get(($userFeedback['type'] == OK) ? "feedback_urgency_ok" : "feedback_urgency_fail"); - } - - // Set urgency for individual testcases - if(isset($argsObj->urgency_tc)) - { - foreach ($argsObj->urgency_tc as $id => $urgency) - { - $tplanMgr->setTestUrgency($argsObj->tplan_id, $id, $urgency); - } - } - - return $userFeedback; -} - - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_planning'); -} \ No newline at end of file diff --git a/lib/plan/planView.php b/lib/plan/planView.php index 6238fc8ff3..68302863d3 100644 --- a/lib/plan/planView.php +++ b/lib/plan/planView.php @@ -1,123 +1,204 @@ -tproject_id - && checkRights($db,$args->user,$args->tproject_id)) { - $tproject_mgr = new testproject($db); - $gui->tplans = - $args->user->getAccessibleTestPlans($db,$args->tproject_id,null, - array('output' =>'mapfull', 'active' => null)); - $gui->drawPlatformQtyColumn = false; - - if( !is_null($gui->tplans) && count($gui->tplans) > 0 ) - { - // do this test project has platform definitions ? - $tplan_mgr = new testplan($db); - $tplan_mgr->platform_mgr->setTestProjectID($args->tproject_id); - $dummy = $tplan_mgr->platform_mgr->testProjectCount(); - $gui->drawPlatformQtyColumn = $dummy[$args->tproject_id]['platform_qty'] > 0; - - $tplanSet = array_keys($gui->tplans); - $dummy = $tplan_mgr->count_testcases($tplanSet,null,array('output' => 'groupByTestPlan')); - $buildQty = $tplan_mgr->get_builds($tplanSet,null,null,array('getCount' => true)); - $rightSet = array('testplan_user_role_assignment'); - - foreach($tplanSet as $idk) - { - $gui->tplans[$idk]['tcase_qty'] = isset($dummy[$idk]['qty']) ? intval($dummy[$idk]['qty']) : 0; - $gui->tplans[$idk]['build_qty'] = isset($buildQty[$idk]['build_qty']) ? intval($buildQty[$idk]['build_qty']) : 0; - if( $gui->drawPlatformQtyColumn ) - { - $plat = $tplan_mgr->getPlatforms($idk); - $gui->tplans[$idk]['platform_qty'] = is_null($plat) ? 0 : count($plat); - } - - - // Get rights for each test plan - foreach($rightSet as $target) - { - // DEV NOTE - CRITIC - // I've made a theorically good performance choice to - // assign to $roleObj a reference to different roleObj - // UNFORTUNATELLY this choice was responsible to destroy point object - // since second LOOP - $roleObj = null; - if($gui->tplans[$idk]['has_role'] > 0) - { - $roleObj = $args->user->tplanRoles[$gui->tplans[$idk]['has_role']]; - } - else if (!is_null($args->user->tprojectRoles) && - isset($args->user->tprojectRoles[$args->tproject_id]) ) - { - $roleObj = $args->user->tprojectRoles[$args->tproject_id]; - } - - if(is_null($roleObj)) - { - $roleObj = $args->user->globalRole; - } - $gui->tplans[$idk]['rights'][$target] = $roleObj->hasRight($target); - } - } - unset($tplan_mgr); - } - unset($tproject_mgr); +tproject_id && checkRights($db, $args->user, $args->tproject_id)) { + $tproject_mgr = new testproject($db); + $gui->tplans = $args->user->getAccessibleTestPlans($db, $args->tproject_id, + null, array( + 'output' => 'mapfull', + 'active' => null + )); + $gui->drawPlatformQtyColumn = false; + + if (! is_null($gui->tplans) && count($gui->tplans) > 0) { + // do this test project has platform definitions ? + $tplan_mgr = new testplan($db); + $tplan_mgr->platform_mgr->setTestProjectID($args->tproject_id); + $dummy = $tplan_mgr->platform_mgr->testProjectCount(); + $gui->drawPlatformQtyColumn = $dummy[$args->tproject_id]['platform_qty'] > + 0; + + $tplanSet = array_keys($gui->tplans); + $dummy = $tplan_mgr->count_testcases($tplanSet, null, + array( + 'output' => 'groupByTestPlan' + )); + $buildQty = $tplan_mgr->get_builds($tplanSet, null, null, + array( + 'getCount' => true + )); + $rightSet = array( + 'testplan_user_role_assignment' + ); + + // To create the CF columns we need to get the linked CF + $availableCF = (array) $tplan_mgr->get_linked_cfields_at_design( + current($tplanSet), $gui->tproject_id); + $hasCF = count($availableCF); + $gui->cfieldsColumns = null; + $gui->cfieldsType = null; + $initCFCol = true; + + // get CF used to configure HIDE COLS + // We want different configurations for different test projects + // then will do two steps algorithm + // 1. get test project prefix PPFX + // 2. look for TL_TPLANVIEW_HIDECOL_PPFX + // 3. if found proceed + // 4. else look for TL_TPLANVIEW_HIDECOL + // + $ppfx = $tproject_mgr->getTestCasePrefix($gui->tproject_id); + $suffixSet = [ + '_' . $ppfx, + '' + ]; + foreach ($suffixSet as $suf) { + $gopt['name'] = 'TL_TPLANVIEW_HIDECOL' . $suf; + $col2hideCF = $tplan_mgr->cfield_mgr->get_linked_to_testproject( + $gui->tproject_id, null, $gopt); + + if ($col2hideCF != null) { + $col2hideCF = current($col2hideCF); + $col2hide = array_flip( + explode('|', $col2hideCF['possible_values'])); + $col2hide[$gopt['name']] = ''; + break; + } + } + + $localeDateFormat = config_get('locales_date_format'); + $localeDateFormat = $localeDateFormat[$args->user->locale]; + + foreach ($tplanSet as $idk) { + if ($hasCF) { + $cfields = (array) $tplan_mgr->getCustomFieldsValues($idk, + $gui->tproject_id); + foreach ($cfields as $cfd) { + if ($initCFCol && ! isset($col2hide[$cfd['name']])) { + $gui->cfieldsColumns[] = $cfd['label']; + $gui->cfieldsType[] = $cfd['type']; + } + $gui->tplans[$idk][$cfd['label']] = [ + 'value' => $cfd['value'], + 'data-order' => $cfd['value'] + ]; + + if ($cfd['type'] == 'date') { + $gui->tplans[$idk][$cfd['label']]['data-order'] = locateDateToISO( + $cfd['value'], $localeDateFormat); + } + } + $initCFCol = false; + } + + $gui->tplans[$idk]['tcase_qty'] = isset($dummy[$idk]['qty']) ? intval( + $dummy[$idk]['qty']) : 0; + + $gui->tplans[$idk]['tcase_qty'] = isset($dummy[$idk]['qty']) ? intval( + $dummy[$idk]['qty']) : 0; + $gui->tplans[$idk]['build_qty'] = isset( + $buildQty[$idk]['build_qty']) ? intval( + $buildQty[$idk]['build_qty']) : 0; + if ($gui->drawPlatformQtyColumn) { + $plat = $tplan_mgr->getPlatforms($idk); + $gui->tplans[$idk]['platform_qty'] = is_null($plat) ? 0 : count( + $plat); + } + + // Get rights for each test plan + foreach ($rightSet as $target) { + // DEV NOTE - CRITIC + // I've made a theorically good performance choice to + // assign to $roleObj a reference to different roleObj + // UNFORTUNATELLY this choice was responsible to destroy point object + // since second LOOP + $roleObj = null; + if ($gui->tplans[$idk]['has_role'] > 0) { + $roleObj = $args->user->tplanRoles[$gui->tplans[$idk]['has_role']]; + } elseif (! is_null($args->user->tprojectRoles) && + isset($args->user->tprojectRoles[$args->tproject_id])) { + $roleObj = $args->user->tprojectRoles[$args->tproject_id]; + } + + if (is_null($roleObj)) { + $roleObj = $args->user->globalRole; + } + $gui->tplans[$idk]['rights'][$target] = $roleObj->hasRight( + $target); + } + } + unset($tplan_mgr); + } + unset($tproject_mgr); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get input from user and return it in some sort of namespace + * + * @return stdClass object with some REQUEST and SESSION values as members + */ +function initArgs() +{ + $args = new stdClass(); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? trim( + $_SESSION['testprojectName']) : ''; + + $args->user = $_SESSION['currentUser']; + return $args; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$dbHandler, $argsObj) +{ + $gui = new stdClass(); + $gui->tproject_id = $argsObj->tproject_id; + $gui->tplans = null; + $gui->user_feedback = ''; + $gui->grants = new stdClass(); + $gui->grants->testplan_create = $argsObj->user->hasRight($dbHandler, + "mgt_testplan_create", $argsObj->tproject_id); + $gui->main_descr = lang_get('testplan_title_tp_management') . " - " . + lang_get('testproject') . ' ' . $argsObj->tproject_name; + $cfg = getWebEditorCfg('testplan'); + $gui->editorType = $cfg['type']; + + return $gui; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param int $tproject_id + * @return boolean + */ +function checkRights(&$db, &$user, $tproject_id) +{ + return $user->hasRight($db, 'mgt_testplan_create', $tproject_id); } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * init_args - * - */ -function init_args() -{ - $args = new stdClass(); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0 ; - $args->tproject_name = isset($_SESSION['testprojectName']) ? trim($_SESSION['testprojectName']) : '' ; - - $args->user = $_SESSION['currentUser']; - return $args; -} - -function initializeGui(&$dbHandler,$argsObj) -{ - $gui = new stdClass(); - $gui->tproject_id = $argsObj->tproject_id; - $gui->tplans = null; - $gui->user_feedback = ''; - $gui->grants = new stdClass(); - $gui->grants->testplan_create = $argsObj->user->hasRight($dbHandler,"mgt_testplan_create",$argsObj->tproject_id); - $gui->main_descr = lang_get('testplan_title_tp_management'). " - " . - lang_get('testproject') . ' ' . $argsObj->tproject_name; - $cfg = getWebEditorCfg('testplan'); - $gui->editorType = $cfg['type']; - - return $gui; -} - - -/** - * checkRights - * - */ -function checkRights(&$db,&$user,$tproject_id) -{ - return $user->hasRight($db,'mgt_testplan_create',$tproject_id); -} \ No newline at end of file diff --git a/lib/plan/tc_exec_assignment.php b/lib/plan/tc_exec_assignment.php index 4d6d3dca11..d20a458afd 100644 --- a/lib/plan/tc_exec_assignment.php +++ b/lib/plan/tc_exec_assignment.php @@ -1,634 +1,667 @@ -items = null; -$keywordsFilter->type = null; -if(is_array($args->keyword_id)) -{ - $keywordsFilter->items = $args->keyword_id; - $keywordsFilter->type = $gui->keywordsFilterType; +tproject_id = $args->tproject_id; +$context->tplan_id = $args->tplan_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$keywordsFilter = new stdClass(); +$keywordsFilter->items = null; +$keywordsFilter->type = null; +if (is_array($args->keyword_id)) { + $keywordsFilter->items = $args->keyword_id; + $keywordsFilter->type = $gui->keywordsFilterType; +} +$arrData = array(); + +$status_map = $assignment_mgr->get_available_status(); +$types_map = $assignment_mgr->get_available_types(); +$cfg['task_test_execution'] = $types_map['testcase_execution']['id']; +$task_test_execution = $cfg['task_test_execution']; + +switch ($args->doAction) { + case 'std': + if (! is_null($args->achecked_tc)) { + $open = $status_map['open']['id']; + $db_now = $db->db_now(); + $features2 = array( + 'upd' => array(), + 'ins' => array(), + 'del' => array() + ); + $method2call = array( + 'upd' => 'update', + 'ins' => 'assign', + 'del' => 'delete_by_feature_id_and_build_id' + ); + $called = array( + 'upd' => false, + 'ins' => false, + 'del' => false + ); + + foreach ($args->achecked_tc as $key_tc => $platform_tcversion) { + foreach ($platform_tcversion as $platform_id => $tcversion_id) { + $feature_id = $args->feature_id[$key_tc][$platform_id]; + + $op = 'ins'; + $features2[$op][$platform_id][$feature_id]['user_id'] = $args->tester_for_tcid[$key_tc][$platform_id]; + $features2[$op][$platform_id][$feature_id]['type'] = $task_test_execution; + $features2[$op][$platform_id][$feature_id]['status'] = $open; + $features2[$op][$platform_id][$feature_id]['creation_ts'] = $db_now; + $features2[$op][$platform_id][$feature_id]['assigner_id'] = $args->user_id; + $features2[$op][$platform_id][$feature_id]['tcase_id'] = $key_tc; + $features2[$op][$platform_id][$feature_id]['tcversion_id'] = $tcversion_id; + $features2[$op][$platform_id][$feature_id]['build_id'] = $args->build_id; + } + } + + foreach ($features2 as $key => $featByPlatform) { + if (count($features2[$key]) > 0) { + foreach ($featByPlatform as $values) { + $assignment_mgr->assign($values); + } + $called[$key] = true; + } + } + + if ($args->send_mail) { + foreach ($called as $ope => $ope_status) { + if ($ope_status) { + sendMailToTesters($db, $tcaseMgr, $gui, $args, + $features2[$ope], $ope); + } + } + } + } + break; + + case 'doRemoveAll': + if (! is_null($args->achecked_tc)) { + doRemoveAll($db, $args, $gui, $cfg, $objMgr); + } + break; + + case 'doRemove': + $signature[] = array( + 'type' => $task_test_execution, + 'user_id' => $args->targetUser, + 'feature_id' => $args->targetFeature, + 'build_id' => $args->build_id + ); + $assignment_mgr->deleteBySignature($signature); + + if ($args->send_mail) { + // In order to send mail to tester we need info about test case, test case version + // and build, and we need to use feature_id to get this info + $feature = current($tplan_mgr->getFeatureByID($args->targetFeature)); + + $items = array(); + $lnk[$args->targetFeature] = array(); + $lnk[$args->targetFeature]['previous_user_id'] = array( + $args->targetUser + ); + $lnk[$args->targetFeature]['tcase_id'] = intval( + $feature['tcase_id']); + $lnk[$args->targetFeature]['tcversion_id'] = intval( + $feature['tcversion_id']); + $items[intval($feature['platform_id'])] = $lnk; + + sendMailToTesters($db, $tcaseMgr, $gui, $args, $items, 'del'); + } + break; + + case 'linkByMail': + $context = array( + 'tplan_id' => $args->tplan_id, + 'build_id' => $args->build_id + ); + $assignment_mgr->emailLinkToExecPlanning($context, $args->userSet); + break; + + case 'doBulkUserRemove': + if (! is_null($args->achecked_tc) && ! is_null($args->userSet)) { + doBulkUserRemove($db, $args, $gui, $cfg, $objMgr); + } + break; +} + +switch ($args->level) { + case 'testcase': + // build the data need to call gen_spec_view + $xx = $tcaseMgr->getPathLayered(array( + $args->id + )); + $yy = array_keys($xx); // done to silence warning on end() + $tsuite_data['id'] = end($yy); + $tsuite_data['name'] = $xx[$tsuite_data['id']]['value']; + + $xx = $tplan_mgr->getLinkInfo($args->tplan_id, $args->id, + $args->control_panel['setting_platform'], + array( + 'output' => 'assignment_info', + 'build4assignment' => $args->build_id + )); + + $linked_items[$args->id] = $xx; + $opt = array( + 'write_button_only_if_linked' => 1, + 'user_assignments_per_build' => $args->build_id, + 'useOptionalArrayFields' => true + ); + + $filters = array( + 'keywords' => $keywordsFilter->items, + 'testcases' => $args->id + ); + + $my_out = gen_spec_view($db, 'testplan', $args->tplan_id, + $tsuite_data['id'], $tsuite_data['name'], $linked_items, null, + $filters, $opt); + + // index 0 contains data for the parent test suite of this test case, + // other elements are not needed. + $out = array(); + $out['spec_view'][0] = $my_out['spec_view'][0]; + $out['num_tc'] = 1; + break; + + case 'testsuite': + $filters = array(); + $filters['keywordsFilter'] = $keywordsFilter; + $filters['testcaseFilter'] = (isset($args->testcases_to_show)) ? $args->testcases_to_show : null; + $filters['assignedToFilter'] = property_exists($args, + 'filter_assigned_to') ? $args->filter_assigned_to : null; + $filters['executionTypeFilter'] = $args->control_panel['filter_execution_type']; + $filters['cfieldsFilter'] = $args->control_panel['filter_custom_fields']; + + // ORDER IS CRITIC - Attention in refactoring + $opt = array( + 'assigned_on_build' => $args->build_id, + 'addPriority' => true, + 'addExecInfo' => false + ); + $filters += $opt; + $opt['accessKeyType'] = 'tcase+platform+stackOnUser'; + $opt['useOptionalArrayFields'] = true; + $opt['tlFeature'] = 'testCaseExecTaskAssignment'; + + // platform filter is generated inside getFilteredSpecView() using $args->control_panel['setting_platform']; + // $out = getFilteredSpecView($db, $args, $tplan_mgr, $tcaseMgr, $filters, $opt); + + $out = getFilteredSpecViewFlat($db, $args, $tplan_mgr, $tcaseMgr, + $filters, $opt); + break; + + default: + show_instructions('tc_exec_assignment'); + break; +} + +$gui->items = $out['spec_view']; + +// useful to avoid error messages on smarty template. +$gui->items_qty = is_null($gui->items) ? 0 : count($gui->items); +$gui->has_tc = $out['num_tc'] > 0 ? 1 : 0; +$gui->support_array = array_keys($gui->items); + +if ($_SESSION['testprojectOptions']->testPriorityEnabled) { + $cfg = config_get('priority'); + $gui->priority_labels = init_labels($cfg["code_label"]); +} + +// Changing to _flat template +$tplCfg = templateConfiguration(); +$tpl = $tplCfg->tpl; +$tpl = str_replace('.tpl', '_flat.tpl', $tpl); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($tpl); + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + $args->user_id = intval($_SESSION['userID']); + $args->tproject_id = intval($_SESSION['testprojectID']); + $args->tproject_name = $_SESSION['testprojectName']; + + $key2loop = array( + 'doActionButton' => null, + 'doAction' => null, + 'level' => null, + 'achecked_tc' => null, + 'version_id' => 0, + 'has_prev_assignment' => null, + 'send_mail' => false, + 'tester_for_tcid' => null, + 'feature_id' => null, + 'id' => 0 + ); + + foreach ($key2loop as $key => $value) { + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $value; + } + + $args->userSet = null; + $target = $_REQUEST['bulk_tester_div']; + if (isset($target) && count($target) > 0) { + foreach ($target as $uid) { + if ($uid > 0) { + $args->userSet[$uid] = $uid; + } + } + } + + // For more information about the data accessed in session here, see the comment + // in the file header of lib/functions/tlTestCaseFilterControl.class.php. + $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $mode = 'plan_mode'; + $session_data = isset($_SESSION[$mode]) && + isset($_SESSION[$mode][$form_token]) ? $_SESSION[$mode][$form_token] : null; + + $args->control_panel = $session_data; + + $key2loop = array( + 'refreshTree' => array( + 'key' => 'setting_refresh_tree_on_action', + 'value' => 0 + ), + 'filter_assigned_to' => array( + 'key' => 'filter_assigned_user', + 'value' => null + ) + ); + + foreach ($key2loop as $key => $info) { + $args->$key = isset($session_data[$info['key']]) ? $session_data[$info['key']] : $info['value']; + } + + $args->keyword_id = 0; + $fk = 'filter_keywords'; + if (isset($session_data[$fk])) { + $args->keyword_id = $session_data[$fk]; + if (is_array($args->keyword_id) && count($args->keyword_id) == 1) { + $args->keyword_id = $args->keyword_id[0]; + } + } + + $args->keywordsFilterType = null; + $fk = 'filter_keywords_filter_type'; + if (isset($session_data[$fk])) { + $args->keywordsFilterType = $session_data[$fk]; + } + + $args->testcases_to_show = null; + if (isset($session_data['testcases_to_show'])) { + $args->testcases_to_show = $session_data['testcases_to_show']; + } + + $args->build_id = intval( + isset($session_data['setting_build']) ? $session_data['setting_build'] : 0); + $args->platform_id = intval( + isset($session_data['setting_platform']) ? $session_data['setting_platform'] : 0); + + $args->tplan_id = intval( + isset($session_data['setting_testplan']) ? $session_data['setting_testplan'] : 0); + if ($args->tplan_id) { + $args->tplan_id = intval( + isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); + } + + $args->targetFeature = intval( + isset($_REQUEST['targetFeature']) ? $_REQUEST['targetFeature'] : 0); + $args->targetUser = intval( + isset($_REQUEST['targetUser']) ? $_REQUEST['targetUser'] : 0); + + $key = 'doRemoveAll'; + if ($args->$key = isset($_REQUEST[$key]) ? 1 : 0) { + $args->doAction = $key; + } + + return $args; +} + +/* + * function: initializeGui + * + * args : + * + * returns: + * + */ +function initializeGui(&$dbHandler, $argsObj, &$tplanMgr, &$tcaseMgr) +{ + $platform_mgr = new tlPlatform($dbHandler, $argsObj->tproject_id); + + $tcase_cfg = config_get('testcase_cfg'); + $gui = new stdClass(); + + $optLTT = null; + $gui->platforms = $platform_mgr->getLinkedToTestplanAsMap( + $argsObj->tplan_id, $optLTT); + + $gui->usePlatforms = $platform_mgr->platformsActiveForTestplan( + $argsObj->tplan_id); + $gui->bulk_platforms = $platform_mgr->getLinkedToTestplanAsMap( + $argsObj->tplan_id); + $gui->bulk_platforms[0] = lang_get("all_platforms"); + ksort($gui->bulk_platforms); + + $gui->send_mail = $argsObj->send_mail; + $gui->send_mail_checked = ""; + if ($gui->send_mail) { + $gui->send_mail_checked = ' checked="checked" '; + } + + $gui->glueChar = $tcase_cfg->glue_character; + + if ($argsObj->level != 'testproject') { + $gui->testCasePrefix = $tcaseMgr->tproject_mgr->getTestCasePrefix( + $argsObj->tproject_id); + $gui->testCasePrefix .= $tcase_cfg->glue_character; + $gui->keywordsFilterType = $argsObj->keywordsFilterType; + $gui->build_id = $argsObj->build_id; + $gui->tplan_id = $argsObj->tplan_id; + + $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->testPlanName = $tplan_info['name']; + + $build_info = $tplanMgr->get_build_by_id($argsObj->tplan_id, + $argsObj->build_id); + $gui->buildName = $build_info['name']; + $gui->main_descr = sprintf(lang_get('title_tc_exec_assignment'), + $gui->buildName, $gui->testPlanName); + + $tproject_mgr = new testproject($dbHandler); + $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); + + $gui->all_users = tlUser::getAll($dbHandler, null, "id", null); + $gui->users = getUsersForHtmlOptions($dbHandler, null, null, null, + $gui->all_users); + $gui->testers = getTestersForHtmlOptions($dbHandler, $argsObj->tplan_id, + $tproject_info, $gui->all_users); + } + + return $gui; +} + +/** + * send_mail_to_testers + * + * @param array $features + * main key platform_id + * @param string $operation + * + * @return void + */ +function sendMailToTesters(&$dbHandler, &$tcaseMgr, &$guiObj, &$argsObj, + $features, $operation) +{ + $testers['new'] = null; + $testers['old'] = null; + $lb = array( + 'platform' => null, + 'testplan' => null, + 'testproject' => null, + 'build' => null + ); + $lbl = init_labels($lb); + + $mail_details['new'] = lang_get('mail_testcase_assigned') . "

    "; + $mail_details['old'] = lang_get('mail_testcase_assignment_removed') . + "

    "; + $mail_subject['new'] = lang_get('mail_subject_testcase_assigned'); + $mail_subject['old'] = lang_get('mail_subject_testcase_assignment_removed'); + $use_testers['new'] = ($operation == 'del') ? false : true; + $use_testers['old'] = ($operation == 'ins') ? false : true; + + $tcaseSet = null; + $tcnames = null; + + $assigner = $guiObj->all_users[$argsObj->user_id]->firstName . ' ' . + $guiObj->all_users[$argsObj->user_id]->lastName; + + $email = array(); + $email['from_address'] = config_get('from_email'); + $email['attachment'] = null; + $email['cc'] = null; + $email['exit_on_error'] = true; + $email['htmlFormat'] = true; + + $body_header = $lbl['testproject'] . ': ' . $argsObj->tproject_name . + '
    ' . $lbl['testplan'] . ': ' . $guiObj->testPlanName . '
    ' . + $lbl['build'] . ': ' . $guiObj->buildName . '

    '; + + // Do we really have platforms? + $pset = array_flip(array_keys($features)); + if (! isset($pset[0])) { + $platMgr = new tlPlatform($dbHandler, $argsObj->tproject_id); + $platSet = $platMgr->getAllAsMap(); + } + + // Get testers id & item set with test case & test case version + foreach ($features as $platform_id => $items) { + $plat[$platform_id] = $platform_id; + foreach ($items as $feature_id => $value) { + if ($use_testers['new'] || $use_testers['old']) { + if ($use_testers['new']) { + $ty = (array) $value['user_id']; + $accessKey = 'new'; + } + + if ($use_testers['old']) { + $ty = (array) $value['previous_user_id']; + $accessKey = 'old'; + } + + foreach ($ty as $user_id) { + $testers[$accessKey][$user_id][$platform_id][$feature_id] = $value['tcase_id']; + } + } + + $tcaseSet[$value['tcase_id']] = $value['tcase_id']; + $tcversionSet[$value['tcversion_id']] = $value['tcversion_id']; + } + } + + $infoSet = $tcaseMgr->get_by_id_bulk($tcaseSet, $tcversionSet); + foreach ($infoSet as $value) { + $tcnames[$value['testcase_id']] = $guiObj->testCasePrefix . + $value['tc_external_id'] . ' ' . $value['name']; + } + + $path_info = $tcaseMgr->tree_manager->get_full_path_verbose($tcaseSet); + $flat_path = null; + foreach ($path_info as $tcase_id => $pieces) { + $flat_path[$tcase_id] = implode('/', $pieces) . '/' . $tcnames[$tcase_id]; + } + + $validator = new Zend_Validate_EmailAddress(); + foreach ($testers as $tester_type => $tester_set) { + if (! is_null($tester_set)) { + $email['subject'] = $mail_subject[$tester_type] . ' ' . + $guiObj->testPlanName; + foreach ($tester_set as $user_id => $set2work) { + // workaround till solution will be found + if ($user_id <= 0) { + continue; + } + + $userObj = $guiObj->all_users[$user_id]; + $email['to_address'] = trim($userObj->emailAddress); + if ($email['to_address'] == '' || + ! $validator->isValid($email['to_address'])) { + continue; + } + + $email['body'] = $body_header; + $email['body'] .= sprintf($mail_details[$tester_type], + $userObj->firstName . ' ' . $userObj->lastName, $assigner); + + foreach ($set2work as $pid => $value) { + if ($pid != 0) { + $email['body'] .= $lbl['platform'] . ': ' . + $platSet[$pid] . '
    '; + } + + foreach ($value as $tcase_id) { + $email['body'] .= $flat_path[$tcase_id] . '
    '; + $wl = $tcaseMgr->buildDirectWebLink( + $_SESSION['basehref'], $tcase_id, + $argsObj->testproject_id); + + $email['body'] .= '' . + 'direct link to test case spec ' . '' . + '

    '; + } + } + + $email['body'] .= '
    ' . date(DATE_RFC1123); + + email_send($email['from_address'], $email['to_address'], + $email['subject'], $email['body'], $email['cc'], + $email['attachment'], $email['exit_on_error'], + $email['htmlFormat']); + } + } + } +} + +/** + */ +function doRemoveAll(&$dbH, &$argsObj, &$guiObj, $cfg, $oMgr) +{ + $op = 'del'; + $features2[$op] = array(); + + foreach ($argsObj->achecked_tc as $key_tc => $ptc) { + foreach ($ptc as $platform_id => $tcversion_id) { + $fid = $argsObj->feature_id[$key_tc][$platform_id]; + $features2[$op][$fid]['type'] = $cfg['task_test_execution']; + $features2[$op][$fid]['build_id'] = $argsObj->build_id; + } + } + + // Must be done before delete + if ($argsObj->send_mail) { + $fSet = array_keys($features2[$op]); + $items = $oMgr['tplan']->getFeatureByID($fSet); + $testers = $oMgr['assign']->getUsersByFeatureBuild($fSet, + $argsObj->build_id, $cfg['task_test_execution']); + + $f4mail = array(); + foreach ($items as $fid => $value) { + $pid = $value['platform_id']; + $f4mail[$pid][$fid]['previous_user_id'] = array_keys($testers[$fid]); + $f4mail[$pid][$fid]['tcase_id'] = $items[$fid]['tcase_id']; + $f4mail[$pid][$fid]['tcversion_id'] = $items[$fid]['tcversion_id']; + } + } + + foreach ($features2 as $key => $values) { + if (count($features2[$key]) > 0) { + $oMgr['assign']->delete_by_feature_id_and_build_id($values); + } + } + + if ($argsObj->send_mail) { + sendMailToTesters($dbH, $oMgr['tcase'], $guiObj, $argsObj, $f4mail, + 'del'); + } +} + +/** + */ +function doBulkUserRemove(&$dbH, &$argsObj, &$guiObj, $cfg, $oMgr) +{ + $feat = null; + if (! is_null($argsObj->achecked_tc)) { + foreach ($argsObj->achecked_tc as $key_tc => $ptc) { + foreach ($ptc as $platform_id => $tcversion_id) { + foreach ($argsObj->userSet as $user2remove) { + $fid = $argsObj->feature_id[$key_tc][$platform_id]; + $feat[$fid]['type'] = $cfg['task_test_execution']; + $feat[$fid]['feature_id'] = $fid; + $feat[$fid]['build_id'] = $argsObj->build_id; + $feat[$fid]['user_id'] = $user2remove; + } + } + } + + // Must be done before delete + if ($argsObj->send_mail) { + $fSet = array_keys($feat); + $items = $oMgr['tplan']->getFeatureByID($fSet); + $testers = $oMgr['assign']->getUsersByFeatureBuild($fSet, + $argsObj->build_id, $cfg['task_test_execution']); + + $f4mail = array(); + foreach ($items as $fid => $value) { + $pid = $value['platform_id']; + $f4mail[$pid][$fid]['previous_user_id'] = array_keys( + $testers[$fid]); + $f4mail[$pid][$fid]['tcase_id'] = $items[$fid]['tcase_id']; + $f4mail[$pid][$fid]['tcversion_id'] = $items[$fid]['tcversion_id']; + } + } + + $oMgr['assign']->deleteBySignature($feat); + + if ($argsObj->send_mail) { + sendMailToTesters($dbH, $oMgr['tcase'], $guiObj, $argsObj, $f4mail, + 'del'); + } + } +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "exec_assign_testcases" + ]; + pageAccessCheck($db, $user, $context); } -$arrData = array(); - -$status_map = $assignment_mgr->get_available_status(); -$types_map = $assignment_mgr->get_available_types(); -$cfg['task_test_execution'] = $types_map['testcase_execution']['id']; -$task_test_execution = $cfg['task_test_execution']; - -switch($args->doAction) -{ - case 'std': - if(!is_null($args->achecked_tc)) - { - $open = $status_map['open']['id']; - $db_now = $db->db_now(); - $features2 = array( 'upd' => array(), 'ins' => array(), 'del' => array()); - $method2call = array( 'upd' => 'update', 'ins' => 'assign', 'del' => 'delete_by_feature_id_and_build_id'); - $called = array( 'upd' => false, 'ins' => false, 'del' => false); - - foreach($args->achecked_tc as $key_tc => $platform_tcversion) - { - foreach($platform_tcversion as $platform_id => $tcversion_id) - { - $feature_id = $args->feature_id[$key_tc][$platform_id]; - - $op='ins'; - $features2[$op][$platform_id][$feature_id]['user_id'] = $args->tester_for_tcid[$key_tc][$platform_id]; - $features2[$op][$platform_id][$feature_id]['type'] = $task_test_execution; - $features2[$op][$platform_id][$feature_id]['status'] = $open; - $features2[$op][$platform_id][$feature_id]['creation_ts'] = $db_now; - $features2[$op][$platform_id][$feature_id]['assigner_id'] = $args->user_id; - $features2[$op][$platform_id][$feature_id]['tcase_id'] = $key_tc; - $features2[$op][$platform_id][$feature_id]['tcversion_id'] = $tcversion_id; - $features2[$op][$platform_id][$feature_id]['build_id'] = $args->build_id; - } - - } - - foreach($features2 as $key => $featByPlatform) - { - if( count($features2[$key]) > 0 ) - { - foreach($featByPlatform as $plat => $values) - { - $assignment_mgr->assign($values); - } - $called[$key]=true; - } - } - - if($args->send_mail) - { - foreach($called as $ope => $ope_status) - { - if($ope_status) - { - send_mail_to_testers($db,$tcase_mgr,$gui,$args,$features2[$ope],$ope); - } - } - } // if($args->send_mail) - } - break; - - - case 'doRemoveAll': - if(!is_null($args->achecked_tc)) { - doRemoveAll($db,$args,$gui,$cfg,$objMgr); - } - break; - - case 'doRemove': - $signature[] = array('type' => $task_test_execution, - 'user_id' => $args->targetUser, - 'feature_id' => $args->targetFeature, - 'build_id' => $args->build_id); - $assignment_mgr->deleteBySignature($signature); - - if($args->send_mail) - { - // In order to send mail to tester we need info about test case, test case version - // and build, and we need to use feature_id to get this info - $feature = current($tplan_mgr->getFeatureByID($args->targetFeature)); - - $items = array(); - $lnk[$args->targetFeature] = array(); - $lnk[$args->targetFeature]['previous_user_id'] = array($args->targetUser); - $lnk[$args->targetFeature]['tcase_id'] = intval($feature['tcase_id']); - $lnk[$args->targetFeature]['tcversion_id'] = intval($feature['tcversion_id']); - $items[intval($feature['platform_id'])] = $lnk; - - send_mail_to_testers($db,$tcase_mgr,$gui,$args,$items,'del'); - } - break; - - case 'linkByMail': - $context = array('tplan_id' => $args->tplan_id, - 'build_id' => $args->build_id); - $assignment_mgr->emailLinkToExecPlanning($context,$args->userSet); - break; - - case 'doBulkUserRemove': - if(!is_null($args->achecked_tc) && !is_null($args->userSet)) { - doBulkUserRemove($db,$args,$gui,$cfg,$objMgr); - } - break; - - -} - - - - - -switch($args->level) { - case 'testcase': - // build the data need to call gen_spec_view - $xx = $tcase_mgr->getPathLayered(array($args->id)); - $yy = array_keys($xx); // done to silence warning on end() - $tsuite_data['id'] = end($yy); - $tsuite_data['name'] = $xx[$tsuite_data['id']]['value']; - - $xx = $tplan_mgr->getLinkInfo($args->tplan_id,$args->id,$args->control_panel['setting_platform'], - array('output' => 'assignment_info','build4assignment' => $args->build_id)); - - $linked_items[$args->id] = $xx; - $opt = array('write_button_only_if_linked' => 1, 'user_assignments_per_build' => $args->build_id, - 'useOptionalArrayFields' => true); - - $filters = array('keywords' => $keywordsFilter->items, 'testcases' => $args->id); - - $my_out = gen_spec_view($db,'testplan',$args->tplan_id,$tsuite_data['id'],$tsuite_data['name'], - $linked_items,null,$filters,$opt); - - // index 0 contains data for the parent test suite of this test case, - // other elements are not needed. - $out = array(); - $out['spec_view'][0] = $my_out['spec_view'][0]; - $out['num_tc'] = 1; - break; - - case 'testsuite': - $filters = array(); - $filters['keywordsFilter'] = $keywordsFilter; - $filters['testcaseFilter'] = (isset($args->testcases_to_show)) ? $args->testcases_to_show : null; - $filters['assignedToFilter'] = property_exists($args,'filter_assigned_to') ? $args->filter_assigned_to : null; - $filters['executionTypeFilter'] = $args->control_panel['filter_execution_type']; - $filters['cfieldsFilter'] = $args->control_panel['filter_custom_fields']; - - // ORDER IS CRITIC - Attention in refactoring - $opt = array('assigned_on_build' => $args->build_id, 'addPriority' => true, - 'addExecInfo' => false); - $filters += $opt; - $opt['accessKeyType'] = 'tcase+platform+stackOnUser'; - $opt['useOptionalArrayFields'] = true; - $opt['tlFeature'] = 'testCaseExecTaskAssignment'; - - // platform filter is generated inside getFilteredSpecView() using $args->control_panel['setting_platform']; - // $out = getFilteredSpecView($db, $args, $tplan_mgr, $tcase_mgr, $filters, $opt); - - $out = getFilteredSpecViewFlat($db, $args, $tplan_mgr, $tcase_mgr, $filters, $opt); - break; - - default: - show_instructions('tc_exec_assignment'); - break; -} - - -$gui->items = $out['spec_view']; - -// useful to avoid error messages on smarty template. -$gui->items_qty = is_null($gui->items) ? 0 : count($gui->items); -$gui->has_tc = $out['num_tc'] > 0 ? 1:0; -$gui->support_array = array_keys($gui->items); - -if ($_SESSION['testprojectOptions']->testPriorityEnabled) -{ - $cfg = config_get('priority'); - $gui->priority_labels = init_labels($cfg["code_label"]); -} - -// Changing to _flat template -$tplCfg = templateConfiguration(); -$tpl = $tplCfg->tpl; -$tpl = str_replace('.tpl', '_flat.tpl', $tpl); - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($tpl); - -/* - function: - - args : - - returns: - -*/ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - $args->user_id = intval($_SESSION['userID']); - $args->tproject_id = intval($_SESSION['testprojectID']); - $args->tproject_name = $_SESSION['testprojectName']; - - $key2loop = array('doActionButton' => null, 'doAction' => null,'level' => null , 'achecked_tc' => null, - 'version_id' => 0, 'has_prev_assignment' => null, 'send_mail' => false, - 'tester_for_tcid' => null, 'feature_id' => null, 'id' => 0); - - foreach($key2loop as $key => $value) - { - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $value; - } - - $args->userSet = null; - $target = $_REQUEST['bulk_tester_div']; - if(isset($target) && count($target) > 0) { - foreach($target as $uid) { - if($uid > 0) { - $args->userSet[$uid] = $uid; - } - } - } - - // For more information about the data accessed in session here, see the comment - // in the file header of lib/functions/tlTestCaseFilterControl.class.php. - $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $mode = 'plan_mode'; - $session_data = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$form_token]) ? $_SESSION[$mode][$form_token] : null; - - $args->control_panel = $session_data; - - $key2loop = array('refreshTree' => array('key' => 'setting_refresh_tree_on_action', 'value' => 0), - 'filter_assigned_to' => array('key' => 'filter_assigned_user', 'value' => null)); - - foreach($key2loop as $key => $info) - { - $args->$key = isset($session_data[$info['key']]) ? $session_data[$info['key']] : $info['value']; - } - - - $args->keyword_id = 0; - $fk = 'filter_keywords'; - if (isset($session_data[$fk])) - { - $args->keyword_id = $session_data[$fk]; - if (is_array($args->keyword_id) && count($args->keyword_id) == 1) - { - $args->keyword_id = $args->keyword_id[0]; - } - } - - $args->keywordsFilterType = null; - $fk = 'filter_keywords_filter_type'; - if (isset($session_data[$fk])) - { - $args->keywordsFilterType = $session_data[$fk]; - } - - - $args->testcases_to_show = null; - if (isset($session_data['testcases_to_show'])) - { - $args->testcases_to_show = $session_data['testcases_to_show']; - } - - $args->build_id = intval(isset($session_data['setting_build']) ? $session_data['setting_build'] : 0); - $args->platform_id = intval(isset($session_data['setting_platform']) ? - $session_data['setting_platform'] : 0); - - $args->tplan_id = intval(isset($session_data['setting_testplan']) ? $session_data['setting_testplan'] : 0); - if ($args->tplan_id) - { - $args->tplan_id = intval(isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']); - } - - - $args->targetFeature = intval(isset($_REQUEST['targetFeature']) ? $_REQUEST['targetFeature'] : 0); - $args->targetUser = intval(isset($_REQUEST['targetUser']) ? $_REQUEST['targetUser'] : 0); - - - $key = 'doRemoveAll'; - if( ($args->$key = isset($_REQUEST[$key]) ? 1 : 0) ) - { - $args->doAction = $key; - } - - return $args; -} - -/* - function: initializeGui - - args : - - returns: - -*/ -function initializeGui(&$dbHandler,$argsObj,&$tplanMgr,&$tcaseMgr) -{ - $platform_mgr = new tlPlatform($dbHandler,$argsObj->tproject_id); - - $tcase_cfg = config_get('testcase_cfg'); - $gui = new stdClass(); - - $optLTT = null; - $gui->platforms = $platform_mgr->getLinkedToTestplanAsMap($argsObj->tplan_id,$optLTT); - - $gui->usePlatforms = $platform_mgr->platformsActiveForTestplan($argsObj->tplan_id); - $gui->bulk_platforms = $platform_mgr->getLinkedToTestplanAsMap($argsObj->tplan_id); - $gui->bulk_platforms[0] = lang_get("all_platforms"); - ksort($gui->bulk_platforms); - - $gui->send_mail = $argsObj->send_mail; - $gui->send_mail_checked = ""; - if($gui->send_mail) - { - $gui->send_mail_checked = ' checked="checked" '; - } - - $gui->glueChar=$tcase_cfg->glue_character; - - if ($argsObj->level != 'testproject') - { - $gui->testCasePrefix = $tcaseMgr->tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - $gui->testCasePrefix .= $tcase_cfg->glue_character; - $gui->keywordsFilterType = $argsObj->keywordsFilterType; - $gui->build_id = $argsObj->build_id; - $gui->tplan_id = $argsObj->tplan_id; - - $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->testPlanName = $tplan_info['name']; - - $build_info = $tplanMgr->get_build_by_id($argsObj->tplan_id, $argsObj->build_id); - $gui->buildName = $build_info['name']; - $gui->main_descr = sprintf(lang_get('title_tc_exec_assignment'),$gui->buildName, $gui->testPlanName); - - $tproject_mgr = new testproject($dbHandler); - $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); - - $gui->all_users = tlUser::getAll($dbHandler,null,"id",null); - $gui->users = getUsersForHtmlOptions($dbHandler,null,null,null,$gui->all_users); - $gui->testers = getTestersForHtmlOptions($dbHandler,$argsObj->tplan_id,$tproject_info,$gui->all_users); - } - - return $gui; -} - - -/** - * send_mail_to_testers - * - * @param hash $features main key platform_id - * @param string $operation - * - * @return void - */ -function send_mail_to_testers(&$dbHandler,&$tcaseMgr,&$guiObj,&$argsObj,$features,$operation) -{ - $testers['new']=null; - $testers['old']=null; - $lb = array('platform' => null, 'testplan' => null, 'testproject' => null, - 'build' =>null); - $lbl = init_labels($lb); - - $mail_details['new']=lang_get('mail_testcase_assigned') . "

    "; - $mail_details['old']=lang_get('mail_testcase_assignment_removed'). "

    "; - $mail_subject['new']=lang_get('mail_subject_testcase_assigned'); - $mail_subject['old']=lang_get('mail_subject_testcase_assignment_removed'); - $use_testers['new']= ($operation == 'del') ? false : true ; - $use_testers['old']= ($operation == 'ins') ? false : true ; - - $tcaseSet=null; - $tcnames=null; - - $assigner=$guiObj->all_users[$argsObj->user_id]->firstName . ' ' . - $guiObj->all_users[$argsObj->user_id]->lastName ; - - - $email=array(); - $email['from_address']=config_get('from_email'); - $email['attachment'] = null; - $email['cc'] = null; - $email['exit_on_error'] = true; - $email['htmlFormat'] = true; - - $body_header = $lbl['testproject'] . ': ' . $argsObj->tproject_name . '
    ' . - $lbl['testplan'] . ': ' . $guiObj->testPlanName .'
    ' . - $lbl['build'] . ': ' . $guiObj->buildName .'

    '; - - - // Do we really have platforms? - $pset = array_flip(array_keys($features)); - if ($hasPlat = !isset($pset[0])) { - $platMgr = new tlPlatform($dbHandler,$argsObj->tproject_id); - $platSet = $platMgr->getAllAsMap(); - } - - // Get testers id & item set with test case & test case version - foreach($features as $platform_id => $items) - { - $plat[$platform_id] = $platform_id; - foreach( $items as $feature_id => $value ) - { - if( $use_testers['new'] || $use_testers['old'] ) - { - if( $use_testers['new'] ) - { - $ty = (array)$value['user_id']; - $accessKey = 'new'; - } - - if( $use_testers['old'] ) - { - $ty = (array)$value['previous_user_id']; - $accessKey = 'old'; - } - - foreach( $ty as $user_id ) - { - $testers[$accessKey][$user_id][$platform_id][$feature_id]=$value['tcase_id']; - } - } - - $tcaseSet[$value['tcase_id']]=$value['tcase_id']; - $tcversionSet[$value['tcversion_id']]=$value['tcversion_id']; - } - } - - $infoSet = $tcaseMgr->get_by_id_bulk($tcaseSet,$tcversionSet); - foreach($infoSet as $value) - { - $tcnames[$value['testcase_id']] = $guiObj->testCasePrefix . $value['tc_external_id'] . ' ' . $value['name']; - } - - $path_info = $tcaseMgr->tree_manager->get_full_path_verbose($tcaseSet); - $flat_path=null; - foreach($path_info as $tcase_id => $pieces) - { - $flat_path[$tcase_id]=implode('/',$pieces) . '/' . $tcnames[$tcase_id]; - } - - $validator = new Zend_Validate_EmailAddress(); - foreach($testers as $tester_type => $tester_set) - { - if( !is_null($tester_set) ) - { - $email['subject'] = $mail_subject[$tester_type] . ' ' . $guiObj->testPlanName; - foreach($tester_set as $user_id => $set2work) - { - // workaround till solution will be found - if($user_id <= 0) - { - continue; - } - - $userObj=$guiObj->all_users[$user_id]; - $email['to_address'] = trim($userObj->emailAddress); - if($email['to_address'] == '' || !$validator->isValid($email['to_address'])) - { - continue; - } - - $email['body'] = $body_header; - $email['body'] .= sprintf($mail_details[$tester_type], - $userObj->firstName . ' ' .$userObj->lastName,$assigner); - - foreach ($set2work as $pid => $value) - { - if( $pid != 0 ) - { - $email['body'] .= $lbl['platform'] . ': ' . $platSet[$pid] . '
    '; - } - - foreach($value as $tcase_id) - { - $email['body'] .= $flat_path[$tcase_id] . '
    '; - $wl = $tcaseMgr->buildDirectWebLink($_SESSION['basehref'],$tcase_id, - $argsObj->testproject_id); - - $email['body'] .= '' . - 'direct link to test case spec ' . - '' . - '

    '; - - } - } - - - $email['body'] .= '
    ' . date(DATE_RFC1123); - - $email_op = email_send($email['from_address'], $email['to_address'], - $email['subject'], $email['body'], $email['cc'], - $email['attachment'],$email['exit_on_error'], - $email['htmlFormat']); - } // foreach($tester_set as $user_id => $value) - } - } -} - -/** - * - */ -function doRemoveAll(&$dbH,&$argsObj,&$guiObj,$cfg,$oMgr) { - $op='del'; - $features2[$op] = array(); - - foreach($argsObj->achecked_tc as $key_tc => $ptc) { - foreach($ptc as $platform_id => $tcversion_id) { - $fid = $argsObj->feature_id[$key_tc][$platform_id]; - $features2[$op][$fid]['type'] = $cfg['task_test_execution']; - $features2[$op][$fid]['build_id'] = $argsObj->build_id; - } - } - - // Must be done before delete - if($argsObj->send_mail) { - $fSet = array_keys($features2[$op]); - $items = $oMgr['tplan']->getFeatureByID($fSet); - $testers = $oMgr['assign']->getUsersByFeatureBuild($fSet,$argsObj->build_id,$cfg['task_test_execution']); - - $f4mail = array(); - foreach($items as $fid => $value) - { - $pid = $value['platform_id']; - $f4mail[$pid][$fid]['previous_user_id'] = array_keys($testers[$fid]); - $f4mail[$pid][$fid]['tcase_id'] = $items[$fid]['tcase_id']; - $f4mail[$pid][$fid]['tcversion_id'] = $items[$fid]['tcversion_id']; - } - } - - - foreach($features2 as $key => $values) { - if( count($features2[$key]) > 0 ) { - $oMgr['assign']->delete_by_feature_id_and_build_id($values); - } - } - - if($argsObj->send_mail) { - send_mail_to_testers($dbH,$oMgr['tcase'],$guiObj,$argsObj,$f4mail,'del'); - } -} - -/** - * - */ -function doBulkUserRemove(&$dbH,&$argsObj,&$guiObj,$cfg,$oMgr) { - - $feat = null; - if(!is_null($argsObj->achecked_tc)) { - foreach($argsObj->achecked_tc as $key_tc => $ptc) { - foreach($ptc as $platform_id => $tcversion_id) { - foreach($argsObj->userSet as $user2remove) { - $fid = $argsObj->feature_id[$key_tc][$platform_id]; - $feat[$fid]['type'] = $cfg['task_test_execution']; - $feat[$fid]['feature_id'] = $fid; - $feat[$fid]['build_id'] = $argsObj->build_id; - $feat[$fid]['user_id'] = $user2remove; - } - } - } - - // Must be done before delete - if($argsObj->send_mail) { - $fSet = array_keys($feat); - $items = $oMgr['tplan']->getFeatureByID($fSet); - $testers = $oMgr['assign']->getUsersByFeatureBuild($fSet,$argsObj->build_id,$cfg['task_test_execution']); - - $f4mail = array(); - foreach($items as $fid => $value) { - $pid = $value['platform_id']; - $f4mail[$pid][$fid]['previous_user_id'] = array_keys($testers[$fid]); - $f4mail[$pid][$fid]['tcase_id'] = $items[$fid]['tcase_id']; - $f4mail[$pid][$fid]['tcversion_id'] = $items[$fid]['tcversion_id']; - } - } - - $oMgr['assign']->deleteBySignature($feat); - - if($argsObj->send_mail) { - send_mail_to_testers($dbH,$oMgr['tcase'],$guiObj,$argsObj,$f4mail,'del'); - } - - } -} - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'exec_assign_testcases'); -} \ No newline at end of file diff --git a/lib/plan/tc_exec_unassign_all.php b/lib/plan/tc_exec_unassign_all.php index b258d57fc4..57ae3c4f5c 100644 --- a/lib/plan/tc_exec_unassign_all.php +++ b/lib/plan/tc_exec_unassign_all.php @@ -1,110 +1,116 @@ -build_id) { - $assignment_count = $assignment_mgr->get_count_of_assignments_for_build_id($args->build_id); - $build_info = $build_mgr->get_by_id($args->build_id); - $build_name = $build_info['name']; -} - - -if ($assignment_count) { - // there are assignments - if ($args->confirmed) { - // their deletion has been confirmed, so delete them - $assignment_mgr->delete_by_build_id($args->build_id); - $gui->message = sprintf(lang_get('unassigned_all_tcs_msg'), $build_name); - $gui->refreshTree = $args->refreshTree ? true : false; - } else { - // there are assignments, but their deletion has still to be confirmed - $gui->draw_tc_unassign_button = true; - $gui->popup_title = lang_get('unassign_all_tcs_msgbox_title'); - $gui->popup_message = sprintf(lang_get('unassign_all_tcs_warning_msg'), $build_name); - $gui->message = sprintf(lang_get('number_of_assignments_per_build'), $assignment_count, $build_name); - } -} else { - // there are no assignments for this build - $gui->message = lang_get('no_testers_assigned_to_build'); -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - */ -function init_args() { - - $args = new stdClass(); - - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args->build_id = isset($_REQUEST['build_id']) ? - intval($_REQUEST['build_id']) : 0; - $args->confirmed = isset($_REQUEST['confirmed']) && $_REQUEST['confirmed'] == 'yes' ? true : false; - - $args->user_id = $_SESSION['userID']; - $args->testproject_id = intval($_SESSION['testprojectID']); - $args->testproject_name = $_SESSION['testprojectName']; - - $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? - $_SESSION['setting_refresh_tree_on_action'] : false; - - return $args; -} - - -/** - * - */ -function init_gui(&$dbHandler, &$argsObj) { - - $gui = new stdClass(); - - $gui->build_id = $argsObj->build_id; - $gui->draw_tc_unassign_button = false; - $gui->refreshTree = false; - - $gui->title = lang_get('remove_all_tester_assignments_title'); - $gui->message = ""; - - $gui->popup_title = ""; - $gui->popup_message = ""; - - return $gui; -} - - -/** - * - */ -function checkRights(&$dbHandler,&$user) { - return $user->hasRight($dbHandler, 'testplan_planning'); +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$assignment_count = 0; + +$build_name = ""; +if ($args->build_id) { + $assignment_count = $assignment_mgr->get_count_of_assignments_for_build_id( + $args->build_id); + $build_info = $build_mgr->get_by_id($args->build_id); + $build_name = $build_info['name']; +} + +if ($assignment_count) { + // there are assignments + if ($args->confirmed) { + // their deletion has been confirmed, so delete them + $assignment_mgr->delete_by_build_id($args->build_id); + $gui->message = sprintf(lang_get('unassigned_all_tcs_msg'), $build_name); + $gui->refreshTree = $args->refreshTree ? true : false; + } else { + // there are assignments, but their deletion has still to be confirmed + $gui->draw_tc_unassign_button = true; + $gui->popup_title = lang_get('unassign_all_tcs_msgbox_title'); + $gui->popup_message = sprintf(lang_get('unassign_all_tcs_warning_msg'), + $build_name); + $gui->message = sprintf(lang_get('number_of_assignments_per_build'), + $assignment_count, $build_name); + } +} else { + // there are no assignments for this build + $gui->message = lang_get('no_testers_assigned_to_build'); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get input from user and return it in some sort of namespace + * + * @return stdClass object returns the arguments for the page + */ +function initArgs() +{ + $args = new stdClass(); + + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->build_id = isset($_REQUEST['build_id']) ? intval( + $_REQUEST['build_id']) : 0; + $args->confirmed = isset($_REQUEST['confirmed']) && + $_REQUEST['confirmed'] == 'yes' ? true : false; + + $args->user_id = $_SESSION['userID']; + $args->testproject_id = intval($_SESSION['testprojectID']); + $args->testproject_name = $_SESSION['testprojectName']; + + $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? $_SESSION['setting_refresh_tree_on_action'] : false; + + return $args; +} + +/** + */ +function initGui(&$dbHandler, &$argsObj) +{ + $gui = new stdClass(); + + $gui->build_id = $argsObj->build_id; + $gui->draw_tc_unassign_button = false; + $gui->refreshTree = false; + + $gui->title = lang_get('remove_all_tester_assignments_title'); + $gui->message = ""; + + $gui->popup_title = ""; + $gui->popup_message = ""; + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "testplan_planning" + ]; + pageAccessCheck($db, $user, $context); } -?> \ No newline at end of file diff --git a/lib/platforms/platformsAssign.php b/lib/platforms/platformsAssign.php index d4c475f4f8..2f7918aba9 100644 --- a/lib/platforms/platformsAssign.php +++ b/lib/platforms/platformsAssign.php @@ -1,168 +1,180 @@ -js_ot_name = 'ot'; -$args = init_args($opt_cfg); - -if ($args->edit == 'testproject') { - show_instructions('platformAssign'); - exit(); -} - - -$smarty = new TLSmarty(); -$tplan_mgr = new testplan($db); -$platform_mgr = new tlPlatform($db, $args->tproject_id); - -$gui = new stdClass(); -$gui->platform_assignment_subtitle = null; -$gui->tplan_id = $args->tplan_id; -$gui->can_do = isset($args->tplan_id); -$gui->mainTitle = lang_get('add_remove_platforms'); -$gui->warning = ''; - -if (isset($args->tplan_id)) { - // do following check to give warning to user - // if test plan has test case versions with platform_id=0 - // this means that right now there are not platforms linked to test plan. - // Give message to user with following info: - // Till you are not going to assign a platform to this linked tcversions - // and it's execution results he/she will not be able to execute - // - $qtyByPlatform = $tplan_mgr->countLinkedTCVersionsByPlatform($args->tplan_id); - $qtyLinked2Unknown = isset($qtyByPlatform[0]['qty']) ? $qtyByPlatform[0]['qty'] : 0; - if( ($fix_needed = ($qtyLinked2Unknown > 0)) ) - { - - $gui->warning = lang_get('unknown_platform'); - } - $opt_cfg->global_lbl = ''; - $opt_cfg->additional_global_lbl = null; - $opt_cfg->from->lbl = lang_get('available_platforms'); - $opt_cfg->to->lbl = lang_get('assigned_platforms'); - $gui->platform_count_js = init_option_panels($tplan_mgr, $platform_mgr, $opt_cfg, $args); - - $tplanData = $tplan_mgr->get_by_id($args->tplan_id); - if (isset($tplanData)) - { - $gui->mainTitle = sprintf($gui->mainTitle,$tplanData['name']); - } - - - if($args->doAction == 'doAssignPlatforms') - { - $platform_mgr->linkToTestplan($args->platformsToAdd,$args->tplan_id); - $platform_mgr->unlinkFromTestplan($args->platformsToRemove,$args->tplan_id); - if( $fix_needed && count($args->platformsToAdd) == 1) - { - reset($args->platformsToAdd); - $tplan_mgr->changeLinkedTCVersionsPlatform($args->tplan_id,0,current($args->platformsToAdd)); - } - // Update option panes with newly updated config - $gui->platform_count_js = init_option_panels($tplan_mgr, $platform_mgr, $opt_cfg, $args); - } -} - - -$opt_cfg->from->desc_field = 'platform'; -$opt_cfg->to->desc_field = 'platform'; -item_opt_transf_cfg($opt_cfg, null); - -$smarty->assign('gui', $gui); -$smarty->assign('opt_cfg', $opt_cfg); - -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * Initializes option transfer items, by appending a text with number linked TC:s - * for every assigned platform. - * It also builds a js map platform_name => linked_count. - * This map is used to show warning dialog only when trying to unlink - * platforms with assigned TCs - */ -function init_option_panels(&$tplan_mgr, &$platform_mgr, &$opt_cfg, &$args) -{ - $opx = array('enable_on_design' => false, 'enable_on_execution' => true); - $opt_cfg->from->map = $platform_mgr->getAllAsMap($opx); - - $optLTT = null; - $map = $platform_mgr->getLinkedToTestplanAsMap($args->tplan_id,$optLTT); - $platform_count_js = "platform_count_map = new Array();\n"; - if (!is_null($map)) { - foreach ($map as $platform_id => &$platform_name) - { - $count = $tplan_mgr->count_testcases($args->tplan_id,$platform_id); - $platform_name .= sprintf(lang_get('platform_linked_count'), $count); - $platform_count_js .= "platform_count_map['$platform_name'] = $count;\n"; - - // Removal of duplicates is NOT handles automatically since we just - // modified their names. - unset($opt_cfg->from->map[$platform_id]); - } - } - $opt_cfg->to->map = $map; - return $platform_count_js; -} - -/** - * - * - */ -function init_args(&$opt_cfg) -{ - $added = $opt_cfg->js_ot_name . "_addedRight"; - $removed = $opt_cfg->js_ot_name . "_removedRight"; - - $iParams = array( "tplan_id" => array(tlInputParameter::INT_N), - "edit" => array(tlInputParameter::STRING_N,0,100), - "doAction" => array(tlInputParameter::STRING_N,0,20), - $added => array(tlInputParameter::STRING_N), - $removed => array(tlInputParameter::STRING_N)); - - $pParams = R_PARAMS($iParams); - - $args = new stdClass(); - $args->tplan_id = $pParams["tplan_id"]; - $args->platformsToAdd = null; - $args->platformsToRemove = null; - $args->edit = $pParams["edit"]; - $args->doAction = $pParams["doAction"]; - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - if ($pParams[$added] != "") { - $args->platformsToAdd = explode(",", $pParams[$added]); - } - - if( $pParams[$removed] != "" ) - { - $args->platformsToRemove = explode(",", $pParams[$removed]); - } - - return $args; -} - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_add_remove_platforms'); +js_ot_name = 'ot'; +$args = initArgs($opt_cfg); + +if ($args->edit == 'testproject') { + show_instructions('platformAssign'); + exit(); +} + +$smarty = new TLSmarty(); +$tplan_mgr = new testplan($db); +$platform_mgr = new tlPlatform($db, $args->tproject_id); + +$gui = new stdClass(); +$gui->platform_assignment_subtitle = null; +$gui->tplan_id = $args->tplan_id; +$gui->can_do = isset($args->tplan_id); +$gui->mainTitle = lang_get('add_remove_platforms'); +$gui->warning = ''; + +if (isset($args->tplan_id)) { + // do following check to give warning to user + // if test plan has test case versions with platform_id=0 + // this means that right now there are not platforms linked to test plan. + // Give message to user with following info: + // Till you are not going to assign a platform to this linked tcversions + // and it's execution results he/she will not be able to execute + // + $qtyByPlatform = $tplan_mgr->countLinkedTCVersionsByPlatform( + $args->tplan_id); + + $qtyLinked2Unknown = isset($qtyByPlatform[0]['qty']) ? $qtyByPlatform[0]['qty'] : 0; + + if ($fix_needed = ($qtyLinked2Unknown > 0)) { + $gui->warning = lang_get('unknown_platform'); + } + $opt_cfg->global_lbl = ''; + $opt_cfg->additional_global_lbl = null; + $opt_cfg->from->lbl = lang_get('available_platforms'); + $opt_cfg->to->lbl = lang_get('assigned_platforms'); + $gui->platform_count_js = initOptionPanels($tplan_mgr, $platform_mgr, + $opt_cfg, $args); + + $tplanData = $tplan_mgr->get_by_id($args->tplan_id); + if (isset($tplanData)) { + $gui->mainTitle = sprintf($gui->mainTitle, $tplanData['name']); + } + + if ($args->doAction == 'doAssignPlatforms') { + $platform_mgr->linkToTestplan($args->platformsToAdd, $args->tplan_id); + $platform_mgr->unlinkFromTestplan($args->platformsToRemove, + $args->tplan_id); + if ($fix_needed && count($args->platformsToAdd) == 1) { + reset($args->platformsToAdd); + $tplan_mgr->changeLinkedTCVersionsPlatform($args->tplan_id, 0, + current($args->platformsToAdd)); + } + // Update option panes with newly updated config + $gui->platform_count_js = initOptionPanels($tplan_mgr, $platform_mgr, + $opt_cfg, $args); + } +} + +$opt_cfg->from->desc_field = 'platform'; +$opt_cfg->to->desc_field = 'platform'; +item_opt_transf_cfg($opt_cfg, null); + +$smarty->assign('gui', $gui); +$smarty->assign('opt_cfg', $opt_cfg); + +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Initializes option transfer items, by appending a text with number linked TC:s + * for every assigned platform. + * It also builds a js map platform_name => linked_count. + * This map is used to show warning dialog only when trying to unlink + * platforms with assigned TCs + */ +function initOptionPanels(&$tplan_mgr, &$platform_mgr, &$opt_cfg, &$args) +{ + $opt_cfg->from->map = $platform_mgr->getAllAsMap( + config_get('platforms')->allowedOnAssign); + + $optLTT = null; + $map = $platform_mgr->getLinkedToTestplanAsMap($args->tplan_id, $optLTT); + $platform_count_js = "platform_count_map = new Array();\n"; + if (! is_null($map)) { + foreach ($map as $plat_id => &$plat_name) { + $count = $tplan_mgr->count_testcases($args->tplan_id, $plat_id); + $plat_name .= sprintf(lang_get('platform_linked_count'), $count); + $platform_count_js .= "platform_count_map['$plat_name'] = $count;\n"; + + // Removal of duplicates is NOT handled + // automatically since we just have modified + // their names adding a usage counter. + unset($opt_cfg->from->map[$plat_id]); + } + } + + $opt_cfg->to->map = $map; + return $platform_count_js; +} + +/** + */ +function initArgs(&$opt_cfg) +{ + $added = $opt_cfg->js_ot_name . "_addedRight"; + $removed = $opt_cfg->js_ot_name . "_removedRight"; + + $iParams = array( + "tplan_id" => array( + tlInputParameter::INT_N + ), + "edit" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + $added => array( + tlInputParameter::STRING_N + ), + $removed => array( + tlInputParameter::STRING_N + ) + ); + + $pParams = R_PARAMS($iParams); + + $args = new stdClass(); + $args->tplan_id = $pParams["tplan_id"]; + $args->platformsToAdd = null; + $args->platformsToRemove = null; + $args->edit = $pParams["edit"]; + $args->doAction = $pParams["doAction"]; + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + if ($pParams[$added] != "") { + $args->platformsToAdd = explode(",", $pParams[$added]); + } + + if ($pParams[$removed] != "") { + $args->platformsToRemove = explode(",", $pParams[$removed]); + } + + return $args; +} + +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_add_remove_platforms'); } diff --git a/lib/platforms/platformsEdit.php b/lib/platforms/platformsEdit.php index 0e81bd95b3..63df7b4da1 100644 --- a/lib/platforms/platformsEdit.php +++ b/lib/platforms/platformsEdit.php @@ -1,355 +1,414 @@ - exit() -list($args,$gui,$platform_mgr) = initEnv($db); - -$templateCfg = templateConfiguration(); -$smarty = new TLSmarty(); -$default_template = $templateCfg->default_template; - -$op = new stdClass(); -$op->status = 0; - -$of = web_editor('notes',$_SESSION['basehref'],$editorCfg); -$of->Value = getItemTemplateContents('platform_template', $of->InstanceName, $args->notes); - -$method = $args->doAction; -switch ($args->doAction) { - case "do_create": - case "do_update": - case "do_delete": - if (!$gui->canManage) { - break; - } - - case "edit": - case "create": - $op = $method($args,$gui,$platform_mgr); - $of->Value = $gui->notes; - break; - - case "disableDesign": - case "enableDesign": - case "disableExec": - case "enableExec": - $platform_mgr->$method($args->platform_id); - - $op = new stdClass(); - $op->status = 1; - $op->template = 'platformsView.tpl'; - break; -} - -if ($op->status == 1) { - $default_template = $op->template; - $gui->user_feedback['message'] = $op->user_feedback; -} else { - $gui->user_feedback['message'] = getErrorMessage($op->status, $args->name); - $gui->user_feedback['type'] = 'ERROR'; -} - -// refresh -$guiX = $platform_mgr->initViewGui($args->currentUser); -$gui->platforms = $guiX->platforms; - -$gui->notes = $of->CreateHTML(); - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $default_template); - -/** - * - * - */ -function initEnv(&$dbHandler) { - testlinkInitPage($dbHandler); - $argsObj = init_args($dbHandler); - checkPageAccess($dbHandler,$argsObj); // Will exit if check failed - - $platMgr = new tlPlatform($dbHandler, $argsObj->tproject_id); - - $guiObj = init_gui($dbHandler,$argsObj,$platMgr); - - return array($argsObj,$guiObj,$platMgr); -} - - - -/** - * - * - */ -function init_args( &$dbH ) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $iParams = - array("doAction" => array(tlInputParameter::STRING_N,0,50), - "id" => array(tlInputParameter::INT_N), - "platform_id" => array(tlInputParameter::INT_N), - "name" => array(tlInputParameter::STRING_N,0,100), - "notes" => array(tlInputParameter::STRING_N), - 'tproject_id' => array(tlInputParameter::INT_N), - "enable_on_execution" => array(tlInputParameter::CB_BOOL), - "enable_on_design" => array(tlInputParameter::CB_BOOL)); - - R_PARAMS($iParams,$args); - if (null == $args->platform_id || $args->platform_id <= 0) { - $args->platform_id = $args->id; - } - - $tables = tlDBObject::getDBTables(array('nodes_hierarchy','platforms')); - - if( 0 != $args->platform_id ) { - $sql = "SELECT testproject_id FROM {$tables['platforms']} - WHERE id={$args->platform_id}"; - $info = $dbH->get_recordset($sql); - - $args->tproject_id = $info[0]['testproject_id']; - } - - if( 0 == $args->tproject_id ) { - throw new Exception("Unable to Get Test Project ID, Aborting", 1); - } - - $args->testproject_name = ''; - $sql = "SELECT name FROM {$tables['nodes_hierarchy']} - WHERE id={$args->tproject_id}"; - $info = $dbH->get_recordset($sql); - if( null != $info ) { - $args->testproject_name = $info[0]['name']; - } - - $args->currentUser = $_SESSION['currentUser']; - - if (null == $args->enable_on_design) { - $args->enable_on_design = 0; - } - - if (null == $args->enable_on_execution) { - $args->enable_on_execution = 1; - } - return $args; -} - -/* - function: create - initialize variables to launch user interface (smarty template) - to get information to accomplish create task. - - args: - - returns: - - -*/ -function create(&$args,&$gui) { - $ret = new stdClass(); - $ret->template = 'platformsEdit.tpl'; - $ret->status = 1; - $ret->user_feedback = ''; - $gui->submit_button_label = lang_get('btn_save'); - $gui->submit_button_action = 'do_create'; - $gui->action_descr = lang_get('create_platform'); - - return $ret; -} - - -/* - function: edit - initialize variables to launch user interface (smarty template) - to get information to accomplish edit task. - - args: - - returns: - - -*/ -function edit(&$args,&$gui,&$platform_mgr) { - $ret = new stdClass(); - $ret->template = 'platformsEdit.tpl'; - $ret->status = 1; - $ret->user_feedback = ''; - - $gui->action_descr = lang_get('edit_platform'); - $platform = $platform_mgr->getById($args->platform_id); - - if ($platform) { - $args->enable_on_design = $platform['enable_on_design']; - $args->enable_on_execution = $platform['enable_on_execution']; - $args->name = $platform['name']; - $args->notes = $platform['notes']; - - $gui->enable_on_design = $args->enable_on_design; - $gui->enable_on_execution = $args->enable_on_execution; - $gui->name = $args->name; - $gui->notes = $args->notes; - - $gui->action_descr .= TITLE_SEP . $platform['name']; - } - - $gui->submit_button_label = lang_get('btn_save'); - $gui->submit_button_action = 'do_update'; - $gui->main_descr = lang_get('platform_management'); - - return $ret; -} - -/* - function: do_create - do operations on db - - args : - - returns: - -*/ -function do_create(&$args,&$gui,&$platform_mgr) { - $gui->main_descr = lang_get('platform_management'); - $gui->action_descr = lang_get('create_platform'); - $gui->submit_button_label = lang_get('btn_save'); - $gui->submit_button_action = 'do_create'; - - $ret = new stdClass(); - $ret->template = 'platformsView.tpl'; - $plat = new stdClass(); - $plat->name = $args->name; - $k2c = array('notes' => null,'enable_on_design' => 0, - 'enable_on_execution' => 1); - foreach ($k2c as $prop => $defa) { - $plat->$prop = property_exists($args, $prop) ? $args->$prop : $defa; - } - - $op = $platform_mgr->create($plat); - - $ret->status = $op['status']; - $ret->user_feedback = sprintf(lang_get('platform_created'), $args->name); - - return $ret; -} - -/** - * - * - */ -function do_update(&$args,&$gui,&$platform_mgr) { - $action_descr = lang_get('edit_platform'); - $platform = $platform_mgr->getPlatform($args->platform_id); - if ($platform) { - $action_descr .= TITLE_SEP . $platform['name']; - } - - $gui->submit_button_label = lang_get('btn_save'); - $gui->submit_button_action = 'do_update'; - $gui->main_descr = lang_get('platform_management'); - $gui->action_descr = $action_descr; - - $ret = new stdClass(); - $ret->template = 'platformsView.tpl'; - $ret->status = - $platform_mgr->update($args->platform_id,$args->name,$args->notes, - $args->enable_on_design, - $args->enable_on_execution); - $ret->user_feedback = sprintf(lang_get('platform_updated'), $args->name); - - return $ret; -} - -/* - function: do_delete - do operations on db - - args : - - returns: - -*/ -function do_delete(&$args,&$gui,&$platform_mgr) { - $gui->main_descr = lang_get('testproject') . TITLE_SEP . $args->testproject_name; - - $gui->submit_button_label = lang_get('btn_save'); - $gui->submit_button_action = 'do_update'; - $gui->action_descr = lang_get('edit_platform'); - - $ret = new stdClass(); - $ret->template = 'platformsView.tpl'; - // This also removes all exec data on this platform - $ret->status = $platform_mgr->delete($args->platform_id,true); - $ret->user_feedback = sprintf(lang_get('platform_deleted'), $args->name); - - return $ret; -} - -/** - * - */ -function getErrorMessage($code,$platform_name) { - switch($code) { - case tlPlatform::E_NAMENOTALLOWED: - $msg = lang_get('platforms_char_not_allowed'); - break; - - case tlPlatform::E_NAMELENGTH: - $msg = lang_get('empty_platform_no'); - break; - - case tlPlatform::E_DBERROR: - case ERROR: - $msg = lang_get('platform_update_failed'); - break; - - case tlPlatform::E_NAMEALREADYEXISTS: - $msg = sprintf(lang_get('platform_name_already_exists'),$platform_name); - break; - - default: - $msg = 'ok'; - } - return $msg; -} - -/** - * - */ -function init_gui(&$db,&$args,&$platMgr) { - $gui = $platMgr->initViewGui($args->currentUser); - - $gui->name = $args->name; - $gui->notes = $args->notes; - $gui->platform_id = $args->platform_id; - - return $gui; -} - -/** - * - */ -function checkPageAccess(&$db,&$argsObj) { - $env['script'] = basename(__FILE__); - $env['tproject_id'] = isset($argsObj->tproject_id) ? - $argsObj->tproject_id : 0; - $env['tplan_id'] = isset($argsObj->tplan_id) ? $argsObj->tplan_id : 0; - $argsObj->currentUser->checkGUISecurityClearance($db,$env,array('platform_management'),'and'); + exit() +list ($args, $gui, $platform_mgr) = initEnv($db); + +$templateCfg = templateConfiguration(); +$smarty = new TLSmarty(); +$default_template = $templateCfg->default_template; + +$op = new stdClass(); +$op->status = 0; + +$of = web_editor('notes', $_SESSION['basehref'], $editorCfg); +$of->Value = getItemTemplateContents('platform_template', $of->InstanceName, + $args->notes); + +$method = $args->doAction; +switch ($args->doAction) { + case "do_create": + case "do_update": + case "do_delete": + if (! $gui->canManage) { + break; + } + + case "edit": + case "create": + $op = $method($args, $gui, $platform_mgr); + $of->Value = $gui->notes; + break; + + case "disableDesign": + case "enableDesign": + case "disableExec": + case "enableExec": + case "openForExec": + case "closeForExec": + $platform_mgr->$method($args->platform_id); + + // optimistic + $op = new stdClass(); + $op->status = 1; + $op->user_feedback = ''; + $op->template = 'platformsView.tpl'; + break; +} + +if ($op->status == 1) { + $default_template = $op->template; + $gui->user_feedback['message'] = $op->user_feedback; +} else { + $gui->user_feedback['message'] = getErrorMessage($op->status, $args->name); + $gui->user_feedback['type'] = 'ERROR'; +} + +// refresh +$guiX = $platform_mgr->initViewGui($args->currentUser, $args); +$gui->platforms = $guiX->platforms; + +$gui->notes = $of->CreateHTML(); + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $default_template); + +/** + */ +function initEnv(&$dbHandler) +{ + testlinkInitPage($dbHandler); + $argsObj = initArgs($dbHandler); + + checkPageAccess($dbHandler, $argsObj); // Will exit if check failed + + $platMgr = new tlPlatform($dbHandler, $argsObj->tproject_id); + + $guiObj = initGui($dbHandler, $argsObj, $platMgr); + + return array( + $argsObj, + $guiObj, + $platMgr + ); +} + +/** + */ +function initArgs(&$dbH) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $iParams = [ + "doAction" => [ + tlInputParameter::STRING_N, + 0, + 50 + ], + "id" => [ + tlInputParameter::INT_N + ], + "platform_id" => [ + tlInputParameter::INT_N + ], + "name" => [ + tlInputParameter::STRING_N, + 0, + 100 + ], + "notes" => [ + tlInputParameter::STRING_N + ], + "tproject_id" => [ + tlInputParameter::INT_N + ], + "tplan_id" => [ + tlInputParameter::INT_N + ], + "enable_on_execution" => [ + tlInputParameter::CB_BOOL + ], + "enable_on_design" => [ + tlInputParameter::CB_BOOL + ], + "is_open" => [ + tlInputParameter::CB_BOOL + ] + ]; + + R_PARAMS($iParams, $args); + if (null == $args->platform_id || $args->platform_id <= 0) { + $args->platform_id = $args->id; + } + + $tables = tlDBObject::getDBTables(array( + 'nodes_hierarchy', + 'platforms' + )); + + if (0 != $args->platform_id) { + $sql = "SELECT testproject_id FROM {$tables['platforms']} + WHERE id={$args->platform_id}"; + $info = $dbH->get_recordset($sql); + + $args->tproject_id = $info[0]['testproject_id']; + } + + if (0 == $args->tproject_id) { + throw new Exception("Unable to Get Test Project ID, Aborting", 1); + } + + $args->testproject_name = ''; + $sql = "SELECT name FROM {$tables['nodes_hierarchy']} + WHERE id={$args->tproject_id}"; + $info = $dbH->get_recordset($sql); + if (null != $info) { + $args->testproject_name = $info[0]['name']; + } + + $args->currentUser = $_SESSION['currentUser']; + + // Checkboxes + if (null == $args->enable_on_design) { + $args->enable_on_design = 0; + } + + if (null == $args->enable_on_execution) { + $args->enable_on_execution = 0; + } + + if (null == $args->is_open) { + $args->is_open = 0; + } + + return $args; +} + +/* + * function: create + * initialize variables to launch user interface (smarty template) + * to get information to accomplish create task. + * + * args: + * + * returns: - + * + */ +function create(&$args, &$gui) +{ + $ret = new stdClass(); + $ret->template = 'platformsEdit.tpl'; + $ret->status = 1; + $ret->user_feedback = ''; + $gui->submit_button_label = lang_get('btn_save'); + $gui->submit_button_action = 'do_create'; + $gui->action_descr = lang_get('create_platform'); + + return $ret; +} + +/* + * function: edit + * initialize variables to launch user interface (smarty template) + * to get information to accomplish edit task. + * + * args: + * + * returns: - + * + */ +function edit(&$args, &$gui, &$platform_mgr) +{ + $ret = new stdClass(); + $ret->template = 'platformsEdit.tpl'; + $ret->status = 1; + $ret->user_feedback = ''; + + $gui->action_descr = lang_get('edit_platform'); + $platform = $platform_mgr->getById($args->platform_id); + + if ($platform) { + $args->enable_on_design = $platform['enable_on_design']; + $args->enable_on_execution = $platform['enable_on_execution']; + $args->is_open = $platform['is_open']; + + $args->name = $platform['name']; + $args->notes = $platform['notes']; + + // Copy from args into $gui + $gui->enable_on_design = $args->enable_on_design; + $gui->enable_on_execution = $args->enable_on_execution; + $gui->is_open = $args->is_open; + + $gui->name = $args->name; + $gui->notes = $args->notes; + + $gui->action_descr .= TITLE_SEP . $platform['name']; + } + + $gui->submit_button_label = lang_get('btn_save'); + $gui->submit_button_action = 'do_update'; + $gui->main_descr = lang_get('platform_management'); + + return $ret; +} + +/** + * function: do_create + * do operations on db + */ +function do_create(&$args, &$gui, &$platform_mgr) +{ + $gui->main_descr = lang_get('platform_management'); + $gui->action_descr = lang_get('create_platform'); + $gui->submit_button_label = lang_get('btn_save'); + $gui->submit_button_action = 'do_create'; + + $ret = new stdClass(); + $ret->template = 'platformsView.tpl'; + $plat = new stdClass(); + $plat->name = $args->name; + $k2c = [ + 'notes' => null, + 'enable_on_design' => 0, + 'enable_on_execution' => 0, + 'is_open' => 0 + ]; + + foreach ($k2c as $prop => $defa) { + $plat->$prop = property_exists($args, $prop) ? $args->$prop : $defa; + } + $op = $platform_mgr->create($plat); + + $ret->status = $op['status']; + $ret->user_feedback = sprintf(lang_get('platform_created'), $args->name); + + return $ret; +} + +/** + */ +function do_update(&$args, &$gui, &$platform_mgr) +{ + $action_descr = lang_get('edit_platform'); + $platform = $platform_mgr->getPlatform($args->platform_id); + if ($platform) { + $action_descr .= TITLE_SEP . $platform['name']; + } + + $gui->submit_button_label = lang_get('btn_save'); + $gui->submit_button_action = 'do_update'; + $gui->main_descr = lang_get('platform_management'); + $gui->action_descr = $action_descr; + + $ret = new stdClass(); + $ret->template = 'platformsView.tpl'; + $ret->status = $platform_mgr->update($args->platform_id, $args->name, + $args->notes, $args->enable_on_design, $args->enable_on_execution, + $args->is_open); + $ret->user_feedback = sprintf(lang_get('platform_updated'), $args->name); + + return $ret; +} + +/* + * function: do_delete + * do operations on db + * + * args : + * + * returns: + * + */ +function do_delete(&$args, &$gui, &$platform_mgr) +{ + $gui->main_descr = lang_get('testproject') . TITLE_SEP . + $args->testproject_name; + + $gui->submit_button_label = lang_get('btn_save'); + $gui->submit_button_action = 'do_update'; + $gui->action_descr = lang_get('edit_platform'); + + $ret = new stdClass(); + $ret->template = 'platformsView.tpl'; + // This also removes all exec data on this platform + $ret->status = $platform_mgr->delete($args->platform_id, true); + $ret->user_feedback = sprintf(lang_get('platform_deleted'), $args->name); + + return $ret; +} + +/** + */ +function getErrorMessage($code, $platform_name) +{ + switch ($code) { + case tlPlatform::E_NAMENOTALLOWED: + $msg = lang_get('platforms_char_not_allowed'); + break; + + case tlPlatform::E_NAMELENGTH: + $msg = lang_get('empty_platform_no'); + break; + + case tlPlatform::E_DBERROR: + case ERROR: + $msg = lang_get('platform_update_failed'); + break; + + case tlPlatform::E_NAMEALREADYEXISTS: + $msg = sprintf(lang_get('platform_name_already_exists'), + $platform_name); + break; + + default: + $msg = 'ok'; + } + return $msg; +} + +/** + */ +function initGui(&$db, &$args, &$platMgr) +{ + $gui = $platMgr->initViewGui($args->currentUser, $args); + + $gui->name = $args->name; + $gui->notes = $args->notes; + $gui->platform_id = $args->platform_id; + $gui->tproject_id = $args->tproject_id; + $gui->tplan_id = $args->tplan_id; + + $gui->enable_on_design = 0; + $gui->enable_on_execution = 0; + $gui->is_open = 0; + + return $gui; +} + +/** + */ +function checkPageAccess(&$db, &$argsObj) +{ + $env['script'] = basename(__FILE__); + $env['tproject_id'] = isset($argsObj->tproject_id) ? $argsObj->tproject_id : 0; + $env['tplan_id'] = isset($argsObj->tplan_id) ? $argsObj->tplan_id : 0; + $argsObj->currentUser->checkGUISecurityClearance($db, $env, + array( + 'platform_management' + ), 'and'); } diff --git a/lib/platforms/platformsExport.php b/lib/platforms/platformsExport.php index 2836643b7d..92f858834f 100644 --- a/lib/platforms/platformsExport.php +++ b/lib/platforms/platformsExport.php @@ -1,119 +1,134 @@ -doAction) { - case 'doExport': - doExport($db,$gui->export_filename,$args->tproject_id); - break; - - default: - break; +doAction) { + case 'doExport': + doExport($db, $gui->export_filename, $args->tproject_id); + break; + + default: + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initArgs(&$dbH) +{ + $args = new stdClass(); + $iParams = array( + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "export_filename" => array( + tlInputParameter::STRING_N, + 0, + 255 + ), + "tproject_id" => array( + tlInputParameter::INT + ) + ); + + R_PARAMS($iParams, $args); + if (0 == $args->tproject_id) { + throw new Exception("Unable to Get Test Project ID, Aborting", 1); + } + + $args->testproject_name = ''; + $tables = tlDBObject::getDBTables(array( + 'nodes_hierarchy' + )); + $sql = "SELECT name FROM {$tables['nodes_hierarchy']} + WHERE id={$args->tproject_id}"; + $info = $dbH->get_recordset($sql); + if (null != $info) { + $args->testproject_name = $info[0]['name']; + } + + if (is_null($args->export_filename)) { + $args->export_filename = $args->testproject_name . "-platforms.xml"; + } + $args->export_filename = trim(str_ireplace(" ", "", $args->export_filename)); + return $args; +} + +/** + */ +function initializeGui(&$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->export_filename = trim($argsObj->export_filename); + $guiObj->page_title = lang_get('export_platforms'); + $guiObj->do_it = 1; + $guiObj->nothing_todo_msg = ''; + $guiObj->exportTypes = array( + 'XML' => 'XML' + ); + + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->goback_url = $_SESSION['basehref'] . + 'lib/platforms/platformsView.php?tproject_id=' . $guiObj->tproject_id; + + return $guiObj; +} + +/* + * function: doExport() + * + * args: dbHandler + * filename: where to export + * + * returns: - + * + */ +function doExport(&$db, $filename, $tproject_id) +{ + $debugMsg = 'File:' . __FILE__ . ' - Function: ' . __FUNCTION__; + $tables = tlObjectWithDB::getDBTables(array( + 'platforms' + )); + $adodbXML = new ADODB_XML("1.0", "UTF-8"); + + $sql = "/* $debugMsg */ + SELECT name,notes,enable_on_design,enable_on_execution,is_open + FROM {$tables['platforms']} PLAT + WHERE PLAT.testproject_id=" . intval($tproject_id); + + $adodbXML->setRootTagName('platforms'); + $adodbXML->setRowTagName('platform'); + $content = $adodbXML->ConvertToXMLString($db->db, $sql); + downloadContentsToFile($content, $filename); + exit(); +} + +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, "platform_view"); } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - */ -function init_args( &$dbH ) { - $args = new stdClass(); - $iParams = - array("doAction" => array(tlInputParameter::STRING_N,0,50), - "export_filename" => array(tlInputParameter::STRING_N,0,255), - "tproject_id" => array(tlInputParameter::INT)); - - R_PARAMS($iParams,$args); - if (0 == $args->tproject_id) { - throw new Exception("Unable to Get Test Project ID, Aborting", 1); - } - - $args->testproject_name = ''; - $tables = tlDBObject::getDBTables(array('nodes_hierarchy')); - $sql = "SELECT name FROM {$tables['nodes_hierarchy']} - WHERE id={$args->tproject_id}"; - $info = $dbH->get_recordset($sql); - if( null != $info ) { - $args->testproject_name = $info[0]['name']; - } - - if(is_null($args->export_filename)) { - $args->export_filename = $args->testproject_name . "-platforms.xml"; - } - $args->export_filename = trim(str_ireplace(" ", "",$args->export_filename)); - return $args; -} - -/** - * - */ -function initializeGui(&$argsObj) { - $guiObj = new stdClass(); - $guiObj->export_filename = trim($argsObj->export_filename); - $guiObj->page_title = lang_get('export_platforms'); - $guiObj->do_it = 1; - $guiObj->nothing_todo_msg = ''; - $guiObj->exportTypes = array('XML' => 'XML'); - - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->goback_url = $_SESSION['basehref'] . - 'lib/platforms/platformsView.php?tproject_id=' . $guiObj->tproject_id; - - return $guiObj; -} - -/* - function: doExport() - - args: dbHandler - filename: where to export - - returns: - - -*/ -function doExport(&$db,$filename,$tproject_id) -{ - $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__; - $tables = tlObjectWithDB::getDBTables(array('platforms')); - $adodbXML = new ADODB_XML("1.0", "UTF-8"); - - $sql = "/* $debugMsg */ - SELECT name,notes,enable_on_design, - enable_on_execution - FROM {$tables['platforms']} PLAT - WHERE PLAT.testproject_id=" . intval($tproject_id); - - $adodbXML->setRootTagName('platforms'); - $adodbXML->setRowTagName('platform'); - $content = $adodbXML->ConvertToXMLString($db->db, $sql); - downloadContentsToFile($content,$filename); - exit(); -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"platform_view"); -} \ No newline at end of file diff --git a/lib/platforms/platformsImport.php b/lib/platforms/platformsImport.php index ac7ee10016..0f09619d88 100644 --- a/lib/platforms/platformsImport.php +++ b/lib/platforms/platformsImport.php @@ -1,174 +1,211 @@ -doAction) { - case 'doImport': - $gui->file_check = doImport($db,$args->tproject_id); - break; - - default: - break; +doAction) { + case 'doImport': + $gui->file_check = doImport($db, $args->tproject_id); + break; + + default: + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initArgs(&$dbH) +{ + $args = new stdClass(); + $iParams = [ + "doAction" => [ + tlInputParameter::STRING_N, + 0, + 50 + ], + "tproject_id" => [ + tlInputParameter::INT + ], + "tplan_id" => [ + tlInputParameter::INT + ] + ]; + + R_PARAMS($iParams, $args); + $args->userID = $_SESSION['userID']; + + if (0 == $args->tproject_id) { + throw new Exception("Unable to Get Test Project ID, Aborting", 1); + } + + $args->testproject_name = ''; + $tables = tlDBObject::getDBTables(array( + 'nodes_hierarchy' + )); + $sql = "SELECT name FROM {$tables['nodes_hierarchy']} + WHERE id={$args->tproject_id}"; + $info = $dbH->get_recordset($sql); + if (null != $info) { + $args->testproject_name = $info[0]['name']; + } + + return $args; +} + +/** + */ +function initializeGui(&$argsObj) +{ + $guiObj = new stdClass(); + + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->tplan_id = $argsObj->tplan_id; + + $guiObj->goback_url = $_SESSION['basehref'] . + 'lib/platforms/platformsView.php?tproject_id=' . $guiObj->tproject_id . + 'tplan_id=' . $guiObj->tplan_id; + + $guiObj->page_title = lang_get('import_platforms'); + $guiObj->file_check = [ + 'show_results' => 0, + 'status_ok' => 1, + 'msg' => 'ok', + 'filename' => '' + ]; + + $guiObj->importTypes = array( + 'XML' => 'XML' + ); + + $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); + $guiObj->max_size_import_file_msg = sprintf(lang_get('max_size_file_msg'), + $guiObj->importLimitBytes / 1024); + + return $guiObj; +} + +/** + * + * @param + * object dbHandler reference to db handler + * + */ +function doImport(&$dbHandler, $testproject_id) +{ + $import_msg = array( + 'ok' => array(), + 'ko' => array() + ); + $file_check = array( + 'show_results' => 0, + 'status_ok' => 0, + 'msg' => '', + 'filename' => '', + 'import_msg' => $import_msg + ); + + $key = 'targetFilename'; + $dest = TL_TEMP_PATH . session_id() . "-import_platforms.tmp"; + $fInfo = $_FILES[$key]; + $source = isset($fInfo['tmp_name']) ? $fInfo['tmp_name'] : null; + if (($source != 'none') && ($source != '')) { + $file_check['filename'] = $fInfo['name']; + $xml = false; + if (move_uploaded_file($source, $dest)) { + // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + $xml = @simplexml_load_file_wrapper($dest); + } + if ($xml !== false) { + $file_check['status_ok'] = 1; + $file_check['show_results'] = 1; + $platform_mgr = new tlPlatform($dbHandler, $testproject_id); + + $platformsOnSystem = $platform_mgr->getAllAsMap( + [ + 'accessKey' => 'name', + 'output' => 'rows', + 'enable_on_design' => null, + 'enable_on_execution' => null, + 'is_open' => null + ]); + + foreach ($xml as $platform) { + if (property_exists($platform, 'name')) { + // Check if platform with this name already exists on test Project + // if answer is yes => update fields + $name = trim((string) $platform->name); + if (isset($platformsOnSystem[$name])) { + $import_msg['ok'][] = sprintf( + lang_get('platform_updated'), $name); + + $platform_mgr->update($platformsOnSystem[$name]['id'], + $name, (string) $platform->notes, + intval($platform->enable_on_design), + intval($platform->enable_on_execution), + intval($platform->is_open)); + } else { + $import_msg['ok'][] = sprintf( + lang_get('platform_imported'), $name); + $item = new stdClass(); + $item->name = $name; + $item->notes = (string) $platform->notes; + $item->enable_on_design = intval( + $platform->enable_on_design); + $item->enable_on_execution = intval( + $platform->enable_on_execution); + $item->is_open = intval($platform->is_open); + $platform_mgr->create($item); + } + } else { + $import_msg['ko'][] = lang_get('bad_line_skipped'); + } + } + } else { + $file_check['msg'] = lang_get('problems_loading_xml_content'); + } + } else { + $msg = getFileUploadErrorMessage($fInfo); + $file_check = array( + 'show_results' => 0, + 'status_ok' => 0, + 'msg' => $msg + ); + } + + if (count($import_msg['ko']) == 0) { + $import_msg['ko'] = null; + } + $file_check['import_msg'] = $import_msg; + return $file_check; +} + +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, "platform_management"); } - - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - */ -function init_args(&$dbH) { - $args = new stdClass(); - $iParams = array("doAction" => array(tlInputParameter::STRING_N,0,50), - "tproject_id" => array(tlInputParameter::INT)); - - R_PARAMS($iParams,$args); - $args->userID = $_SESSION['userID']; - - if( 0 == $args->tproject_id ) { - throw new Exception("Unable to Get Test Project ID, Aborting", 1); - } - - $args->testproject_name = ''; - $tables = tlDBObject::getDBTables(array('nodes_hierarchy')); - $sql = "SELECT name FROM {$tables['nodes_hierarchy']} - WHERE id={$args->tproject_id}"; - $info = $dbH->get_recordset($sql); - if( null != $info ) { - $args->testproject_name = $info[0]['name']; - } - - return $args; -} - -/** - * - */ -function initializeGui(&$argsObj) { - $guiObj = new stdClass(); - - $guiObj->tproject_id = $argsObj->tproject_id; - - $guiObj->goback_url = - $_SESSION['basehref'] . 'lib/platforms/platformsView.php?tproject_id=' . - $guiObj->tproject_id; - - $guiObj->page_title = lang_get('import_platforms'); - $guiObj->file_check = array('show_results' => 0, 'status_ok' => 1, - 'msg' => 'ok', 'filename' => ''); - - $guiObj->importTypes = array('XML' => 'XML'); - - $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); - $guiObj->max_size_import_file_msg = - sprintf(lang_get('max_size_file_msg'), $guiObj->importLimitBytes/1024); - - return $guiObj; -} - - -/** - * @param object dbHandler reference to db handler - * - */ -function doImport(&$dbHandler,$testproject_id) -{ - - $import_msg = array('ok' => array(), 'ko' => array()); - $file_check = array('show_results' => 0, 'status_ok' => 0, 'msg' => '', - 'filename' => '', 'import_msg' => $import_msg); - - $key = 'targetFilename'; - $dest = TL_TEMP_PATH . session_id(). "-import_platforms.tmp"; - $fInfo = $_FILES[$key]; - $source = isset($fInfo['tmp_name']) ? $fInfo['tmp_name'] : null; - if (($source != 'none') && ($source != '')) - { - $file_check['filename'] = $fInfo['name']; - $xml = false; - if (move_uploaded_file($source, $dest)) - { - // http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html - $xml = @simplexml_load_file_wrapper($dest); - } - - if ($xml !== FALSE) { - $file_check['status_ok'] = 1; - $file_check['show_results'] = 1; - $platform_mgr = new tlPlatform($dbHandler,$testproject_id); - - $opx = array('accessKey' => 'name', 'output' => 'rows'); - $platformsOnSystem = $platform_mgr->getAllAsMap($opx); - - foreach($xml as $platform) { - if (property_exists($platform, 'name')) { - // Check if platform with this name already exists on test Project - // if answer is yes => update fields - $name = trim($platform->name); - if(isset($platformsOnSystem[$name])) - { - $import_msg['ok'][] = sprintf(lang_get('platform_updated'),$platform->name); - $platform_mgr->update($platformsOnSystem[$name]['id'],$name,$platform->notes); - } - else - { - $import_msg['ok'][] = sprintf(lang_get('platform_imported'),$platform->name); - $platform_mgr->create($name,$platform->notes); - } - } - else - { - $import_msg['ko'][] = lang_get('bad_line_skipped'); - } - } - } - else - { - $file_check['msg'] = lang_get('problems_loading_xml_content'); - } - - } - else - { - $msg = getFileUploadErrorMessage($fInfo); - $file_check = array('show_results' => 0, 'status_ok' => 0,'msg' => $msg); - } - - if( count($import_msg['ko']) == 0 ) - { - $import_msg['ko'] = null; - } - $file_check['import_msg'] = $import_msg; - return $file_check; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"platform_management"); -} \ No newline at end of file diff --git a/lib/platforms/platformsView.php b/lib/platforms/platformsView.php index ca0e799e40..a5285b4166 100644 --- a/lib/platforms/platformsView.php +++ b/lib/platforms/platformsView.php @@ -1,48 +1,48 @@ -tproject_id); -$gui = $platform_mgr->initViewGui($args->currentUser); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -function init_args() { - $args = new stdClass(); - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - - if( 0 == $args->tproject_id ) { - throw new Exception("Unable Get Test Project ID => Can Not Proceed", 1); - } - - $args->currentUser = $_SESSION['currentUser']; - - return $args; +tproject_id); +$gui = $platform_mgr->initViewGui($args->currentUser, $args); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initArgs() +{ + $args = new stdClass(); + $args->currentUser = $_SESSION['currentUser']; + + list ($context,) = initContext(); + $args->tproject_id = $context->tproject_id; + $args->tplan_id = $context->tplan_id; + + if (0 == $args->tproject_id) { + throw new Exception("Unable Get Test Project ID => Can Not Proceed", 1); + } + + return $args; +} + +/** + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'platform_management') || + $user->hasRightOnProj($db, 'platform_view'); } - -/** - * - * - */ -function checkRights(&$db,&$user) { - return ($user->hasRight($db,'platform_management') || $user->hasRight($db,'platform_view')); -} \ No newline at end of file diff --git a/lib/plugins/pluginView.php b/lib/plugins/pluginView.php index 9d60854bab..5e6bb7cdb1 100644 --- a/lib/plugins/pluginView.php +++ b/lib/plugins/pluginView.php @@ -1,89 +1,99 @@ -operation) -{ - case 'install': - if ($args->pluginName) - { - $p_plugin = plugin_register($args->pluginName, true); - plugin_init($args->pluginName); - plugin_install($p_plugin); - $feedback = sprintf(lang_get('plugin_installed'), $args->pluginName); - } - break; - - case 'uninstall': - if ($args->pluginId) - { - $t_basename = plugin_uninstall($args->pluginId); - $feedback = sprintf(lang_get('plugin_uninstalled'), $t_basename); - } - break; - - default: - break; -} - -$gui->main_title = lang_get('title_plugin_mgmt'); -$gui->installed_plugins = get_all_installed_plugins(); -$gui->available_plugins = get_all_available_plugins($gui->installed_plugins); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->assign('user_feedback', $feedback); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -function initEnv(&$dbHandler) -{ - $_REQUEST=strings_stripSlashes($_REQUEST); - - $iParams = array("operation" => array(tlInputParameter::STRING_N,0,50), - "pluginId" => array(tlInputParameter::INT_N), - "pluginName" => array(tlInputParameter::STRING_N,0,50)); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - - $args->currentUser = $_SESSION['currentUser']; - $args->currentUserID = $_SESSION['currentUser']->dbID; - $args->basehref = $_SESSION['basehref']; - - $gui = new stdClass(); - $gui->grants = getGrantsForUserMgmt($dbHandler,$args->currentUser); - $gui->feedback = ''; - $gui->basehref = $args->basehref; - - return array($args,$gui); +operation) { + case 'install': + if ($args->pluginName) { + $p_plugin = plugin_register($args->pluginName, true); + plugin_init($args->pluginName); + plugin_install($p_plugin); + $feedback = sprintf(lang_get('plugin_installed'), $args->pluginName); + } + break; + + case 'uninstall': + if ($args->pluginId) { + $t_basename = plugin_uninstall($args->pluginId); + $feedback = sprintf(lang_get('plugin_uninstalled'), $t_basename); + } + break; + + default: + break; +} + +$gui->main_title = lang_get('title_plugin_mgmt'); +$gui->installed_plugins = get_all_installed_plugins(); +$gui->available_plugins = get_all_available_plugins($gui->installed_plugins); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('user_feedback', $feedback); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +function initEnv(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "operation" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "pluginId" => array( + tlInputParameter::INT_N + ), + "pluginName" => array( + tlInputParameter::STRING_N, + 0, + 50 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->currentUser = $_SESSION['currentUser']; + $args->currentUserID = $_SESSION['currentUser']->dbID; + $args->basehref = $_SESSION['basehref']; + + $gui = new stdClass(); + $gui->grants = getGrantsForUserMgmt($dbHandler, $args->currentUser); + $gui->feedback = ''; + $gui->basehref = $args->basehref; + + return array( + $args, + $gui + ); +} + +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'mgt_plugins'); } - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'mgt_plugins'); -} \ No newline at end of file diff --git a/lib/project/fix_tplans.php b/lib/project/fix_tplans.php index ce0d9e5048..f70e2e7621 100644 --- a/lib/project/fix_tplans.php +++ b/lib/project/fix_tplans.php @@ -1,64 +1,56 @@ - $testProject) - { - if ($testProject != "none") - { - echo "
    changing test plan $testPlan to go with test project $testProject"; - changeTestProjectForTestPlan($db, $testPlan, $testProject); - } - } - echo "
    "; - } - - $testPlans = getTestPlansWithoutProject($db); - $testPlansCount = count($testPlans); - - $tpObj = new testproject($db); - $testProjects = $tpObj->get_all(); - - $smarty = new TLSmarty(); - $smarty->assign('testPlans', $testPlans); - $smarty->assign('testProjects', $testProjects); - $smarty->assign('count', $testPlansCount); - $smarty->display($template_dir . 'fix_tplans.tpl'); -} -else -{ - echo "

    ".lang_get('fix_tplans_no_rights')."

    "; -} - - -function changeTestProjectForTestPlan(&$db, $testPlan, $testProject) -{ - $query = "UPDATE testplans SET testproject_id={$testProject} WHERE id={$testPlan}"; - $db->exec_query($query); - echo "
    Done changing test project"; -} - -?> \ No newline at end of file + $testProject) { + if ($testProject != "none") { + echo "
    changing test plan $testPlan to go with test project $testProject"; + changeTestProjectForTestPlan($db, $testPlan, $testProject); + } + } + echo "
    "; + } + + $testPlans = getTestPlansWithoutProject($db); + $testPlansCount = count($testPlans); + + $tpObj = new testproject($db); + $testProjects = $tpObj->get_all(); + + $smarty = new TLSmarty(); + $smarty->assign('testPlans', $testPlans); + $smarty->assign('testProjects', $testProjects); + $smarty->assign('count', $testPlansCount); + $smarty->display($template_dir . 'fix_tplans.tpl'); +} else { + echo "

    " . lang_get('fix_tplans_no_rights') . "

    "; +} + +function changeTestProjectForTestPlan(&$db, $testPlan, $testProject) +{ + $query = "UPDATE testplans SET testproject_id={$testProject} WHERE id={$testPlan}"; + $db->exec_query($query); + echo "
    Done changing test project"; +} + +?> diff --git a/lib/project/projectEdit.php b/lib/project/projectEdit.php index 96b9609434..5439dd39b8 100644 --- a/lib/project/projectEdit.php +++ b/lib/project/projectEdit.php @@ -1,734 +1,771 @@ -doActionValue = ''; -$ui->buttonValue = ''; -$ui->caption = ''; -$ui->main_descr = lang_get('title_testproject_management'); - -$user_feedback = ''; -$reloadType = 'none'; // domain 'none','reloadNavBar' - -$tproject_mgr = new testproject($db); -$args = init_args($tproject_mgr, $_REQUEST, $session_tproject_id); - -$gui = initializeGui($db,$args); -$of = web_editor('notes',$_SESSION['basehref'],$editorCfg) ; -$status_ok = 1; - -switch($args->doAction) { - case 'create': - $template = $templateCfg->default_template; - $ui = create($args,$tproject_mgr); - $gui->testprojects = $ui->testprojects; - break; - - case 'edit': - $template = $templateCfg->default_template; - $ui = edit($args,$tproject_mgr); - break; - - case 'doCreate': - $op = doCreate($args,$tproject_mgr); - $template= $op->status_ok ? null : $templateCfg->default_template; - $ui = $op->ui; - $status_ok = $op->status_ok; - $user_feedback = $op->msg; - $reloadType = $op->reloadType; - break; - - case 'doUpdate': - $op = doUpdate($args,$tproject_mgr,$session_tproject_id); - $template= $op->status_ok ? null : $templateCfg->default_template; - $ui = $op->ui; - $status_ok = $op->status_ok; - $user_feedback = $op->msg; - $reloadType = $op->reloadType; - break; - - case 'doDelete': - $op = doDelete($args,$tproject_mgr,$session_tproject_id); - $status_ok = $op->status_ok; - $user_feedback = $op->msg; - $reloadType = $op->reloadType; - break; - - case 'setActive': - case 'setInactive': - case 'enableRequirements': - case 'disableRequirements': - $m2c = $args->doAction; - $tproject_mgr->$m2c($args->tprojectID); - $template= null; - $ui = new stdClass(); - $status_ok = 1; - $user_feedback = ''; - $reloadType = 'reloadNavBar'; - break; - -} - -$ui->main_descr = lang_get('title_testproject_management'); -$smarty = new TLSmarty(); -$smarty->assign('gui_cfg',$gui_cfg); -$smarty->assign('editorType',$editorCfg['type']); -$smarty->assign('mgt_view_events',$_SESSION['currentUser']->hasRight($db,"mgt_view_events")); - -$feedback_type = ''; -if(!$status_ok) { - $feedback_type = 'error'; - $args->doAction = "ErrorOnAction"; -} - -switch($args->doAction) { - case "doCreate": - case "doDelete": - case "doUpdate": - case "setActive": - case "setInactive": - case 'enableRequirements': - case 'disableRequirements': - if( ($addIssueTracker = $addCodeTracker = $addReqMgrSystem = is_null($template)) ) { - $template = 'projectView.tpl'; - // needed after addition of search function on test project view - $gui->name = ''; - $gui->feedback = ''; - } - - $gui->doAction = $reloadType; - $opt = array('output' => 'array_of_map', 'order_by' => " ORDER BY nodes_hierarchy.name ", - 'add_issuetracker' => $addIssueTracker, 'add_codetracker' => $addCodeTracker, - 'add_reqmgrsystem' => $addReqMgrSystem); - $gui->tprojects = (array)$tproject_mgr->get_accessible_for_user($args->userID,$opt); - - $gui->pageTitle = lang_get('title_testproject_management'); - $gui->itemQty = $tprojQty = count($gui->tprojects); - - if($gui->itemQty > 0) { - $gui->pageTitle .= ' ' . sprintf(lang_get('available_test_projects'),$gui->itemQty); - } - $imgSet = $smarty->getImages(); - - - if($addIssueTracker) { - $labels = init_labels(array('active_integration' => null, 'inactive_integration' => null)); - - for($idx=0; $idx < $tprojQty; $idx++) { - $gui->tprojects[$idx]['itstatusImg'] = ''; - if($gui->tprojects[$idx]['itname'] != '') { - $ak = ($gui->tprojects[$idx]['issue_tracker_enabled']) ? 'active' : 'inactive'; - $gui->tprojects[$idx]['itstatusImg'] = ' ' . $labels[$ak . '_integration'] . ''; - } - } - } - - if($addCodeTracker) { - $labels = init_labels(array('active_integration' => null, 'inactive_integration' => null)); - - for($idx=0; $idx < $tprojQty; $idx++) { - $gui->tprojects[$idx]['ctstatusImg'] = ''; - if($gui->tprojects[$idx]['ctname'] != '') { - $ak = ($gui->tprojects[$idx]['code_tracker_enabled']) ? 'active' : 'inactive'; - $gui->tprojects[$idx]['ctstatusImg'] = ' ' . $labels[$ak . '_integration'] . ''; - } - } - } - - if($addReqMgrSystem) { - $labels = init_labels(array('active_integration' => null, 'inactive_integration' => null)); - - for($idx=0; $idx < $tprojQty; $idx++) { - $gui->tprojects[$idx]['rmsstatusImg'] = ''; - if($gui->tprojects[$idx]['rmsname'] != '') { - $ak = ($gui->tprojects[$idx]['reqmgr_integration_enabled']) ? 'active' : 'inactive'; - $gui->tprojects[$idx]['rmsstatusImg'] = ' ' . $labels[$ak . '_integration'] . ''; - } - } - } - - $gui->editorType = $editorCfg['type']; - $smarty->assign('gui',$gui); - $smarty->display($templateCfg->template_dir . $template); - break; - - - case "ErrorOnAction": - default: - if( $args->doAction != "edit" && $args->doAction != "ErrorOnAction") { - $of->Value = getItemTemplateContents('project_template', $of->InstanceName, $args->notes); - } else { - $of->Value = $args->notes; - } - - foreach($ui as $prop => $value) { - $smarty->assign($prop,$value); - } - - $smarty->assign('gui', $args); - $smarty->assign('notes', $of->CreateHTML()); - $smarty->assign('user_feedback', $user_feedback); - $smarty->assign('feedback_type', $feedback_type); - $smarty->display($templateCfg->template_dir . $template); - break; -} - - - -/** - * INITialize page ARGuments, using the $_REQUEST and $_SESSION - * super-global hashes. - * Important: changes in HTML input elements on the Smarty template - * must be reflected here. - * - * @param array $request_hash the $_REQUEST - * @param hash session_hash the $_SESSION - * @return singleton object with html values tranformed and other - * generated variables. - * @internal - */ -function init_args($tprojectMgr,$request_hash, $session_tproject_id) { - $args = new stdClass(); - $request_hash = strings_stripSlashes($request_hash); - - $nullable_keys = array('tprojectName','color','notes','doAction','tcasePrefix','api_key'); - foreach ($nullable_keys as $value) - { - $args->$value = isset($request_hash[$value]) ? trim($request_hash[$value]) : null; - } - - $intval_keys = array('tprojectID' => 0, 'copy_from_tproject_id' => 0); - foreach ($intval_keys as $key => $value) - { - $args->$key = isset($request_hash[$key]) ? intval($request_hash[$key]) : $value; - } - - // get input from the project edit/create page - $checkbox_keys = array('is_public' => 0,'active' => 0, - 'optPriority' => 0,'optAutomation' => 0, - 'optReq' => 0,'optInventory' => 0, - 'issue_tracker_enabled' => 0, - 'code_tracker_enabled' => 0, - 'reqmgr_integration_enabled' => 0); - foreach ($checkbox_keys as $key => $value) - { - $args->$key = isset($request_hash[$key]) ? 1 : $value; - } - - $args->issue_tracker_id = isset($request_hash['issue_tracker_id']) ? intval($request_hash['issue_tracker_id']) : 0; - $args->code_tracker_id = isset($request_hash['code_tracker_id']) ? intval($request_hash['code_tracker_id']) : 0; - $args->reqmgrsystem_id = isset($request_hash['reqmgrsystem_id']) ? intval($request_hash['reqmgrsystem_id']) : 0; - - // This way we are safe - if($args->issue_tracker_id == 0) - { - $args->issue_tracker_enabled = 0; - } - - if($args->code_tracker_id == 0) { - $args->code_tracker_enabled = 0; - } - - if($args->doAction != 'doUpdate' && $args->doAction != 'doCreate') { - if ($args->tprojectID > 0) { - $the_data = $tprojectMgr->get_by_id($args->tprojectID); - $args->notes = $the_data['notes']; - - $args->issue_tracker_enabled = intval($the_data['issue_tracker_enabled']); - $args->issue_tracker_id = 0; - $itMgr = new tlIssueTracker($tprojectMgr->db); - $issueT = $itMgr->getLinkedTo($args->tprojectID); - if( !is_null($issueT) ) { - $args->issue_tracker_id = $issueT['issuetracker_id']; - } - - $args->code_tracker_enabled = intval($the_data['code_tracker_enabled']); - $args->code_tracker_id = 0; - $ctMgr = new tlCodeTracker($tprojectMgr->db); - $codeT = $ctMgr->getLinkedTo($args->tprojectID); - if( !is_null($codeT) ) { - $args->code_tracker_id = $codeT['codetracker_id']; - } - - $args->reqmgr_integration_enabled = intval($the_data['reqmgr_integration_enabled']); - $args->reqmgrsystem_id = 0; - $mgr = new tlReqMgrSystem($tprojectMgr->db); - $et = $mgr->getLinkedTo($args->tprojectID); - if( !is_null($et) ) { - $args->reqmgrsystem_id = $et['reqmgrsystem_id']; - } - - if ($args->doAction == 'doDelete') { - $args->tprojectName = $the_data['name']; - } - - } else { - $args->notes = ''; - } - } - - // sanitize output via black list - if($args->notes != '') { - // The Black List - Jon Bokenkamp - $bl = array(''); - foreach($bl as $tg) { - $cl[] = htmlentities($tg); - } - $args->notes = str_replace($bl,$cl,$args->notes); - } - - $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; - $args->userID = intval(isset($_SESSION['userID']) ? intval($_SESSION['userID']) : 0); - $args->testprojects = null; - $args->projectOptions = prepareOptions($args); - - return $args; -} - -/** - * Collect a test project options (input from form) to a singleton - * - * @param array $argsObj the page input - * @return singleton data to be stored - */ -function prepareOptions($argsObj) { - $opts = new stdClass(); - $opts->requirementsEnabled = $argsObj->optReq; - $opts->testPriorityEnabled = $argsObj->optPriority; - $opts->automationEnabled = $argsObj->optAutomation; - $opts->inventoryEnabled = $argsObj->optInventory; - - return $opts; -} - -/** - * - * ATTENTION: logEvent() is done on testproject->create() - * - */ -function doCreate($argsObj,&$tprojectMgr) { - $key2get=array('status_ok','msg'); - - $op = new stdClass(); - $op->ui = new stdClass(); - - $op->status_ok = 0; - $op->template = null; - $op->msg = ''; - $op->id = 0; - $op->reloadType = 'none'; - - $check_op = crossChecks($argsObj,$tprojectMgr); - foreach($key2get as $key) { - $op->$key=$check_op[$key]; - } - - if($op->status_ok) { - try { - $shazam = false; - $item = $argsObj; - $item->name = $argsObj->tprojectName; - $item->prefix = $argsObj->tcasePrefix; - $item->options = prepareOptions($argsObj); - $new_id = $tprojectMgr->create($item, array('doChecks' => true, 'setSessionProject' => true)); - } catch (Exception $e) { - $new_id = -1; - $op->status_ok = false; - $op->msg = $e->getMessage(); - $shazam = true; - } - - if ($new_id <= 0) { - if(!$shazam) { - $op->msg = lang_get('refer_to_log'); - } - } else { - $op->template = 'projectView.tpl'; - $op->id = $new_id; - - if($argsObj->issue_tracker_enabled) { - $tprojectMgr->enableIssueTracker($new_id); - } else { - $tprojectMgr->disableIssueTracker($new_id); - } - - - $itMgr = new tlIssueTracker($tprojectMgr->db); - if($argsObj->issue_tracker_id > 0) { - $itMgr->link($argsObj->issue_tracker_id,$new_id); - } - - if($argsObj->code_tracker_enabled) { - $tprojectMgr->enableCodeTracker($new_id); - } else { - $tprojectMgr->disableCodeTracker($new_id); - } - - - $ctMgr = new tlCodeTracker($tprojectMgr->db); - if($argsObj->code_tracker_id > 0) { - $ctMgr->link($argsObj->code_tracker_id,$new_id); - } - - if( !$argsObj->is_public) { - // Need to add specific role on test project in order to not make - // it invisible for me!!! - $tprojectMgr->addUserRole($argsObj->userID,$new_id,$argsObj->user->globalRole->dbID); - } - } - } - - if( $op->status_ok ) { - $op->reloadType = 'reloadNavBar'; - if($argsObj->copy_from_tproject_id > 0) { - $options = array('copy_requirements' => $argsObj->optReq); - $tprojectMgr->copy_as($argsObj->copy_from_tproject_id,$new_id, - $argsObj->userID,trim($argsObj->tprojectName),$options); - } - } - else - { - $op->ui->doActionValue = 'doCreate'; - $op->ui->buttonValue = lang_get('btn_create'); - $op->ui->caption = lang_get('caption_new_tproject'); - } - - return $op; -} - -/* - function: doUpdate - - args: - - returns: - -*/ -function doUpdate($argsObj,&$tprojectMgr,$sessionTprojectID) -{ - $key2get = array('status_ok','msg'); - - $op = new stdClass(); - $op->ui = new stdClass(); - $op->status_ok = 0; - $op->msg = ''; - $op->template = null; - $op->reloadType = 'none'; - - $oldObjData = $tprojectMgr->get_by_id($argsObj->tprojectID); - $op->oldName = $oldObjData['name']; - - $check_op = crossChecks($argsObj,$tprojectMgr); - foreach($key2get as $key) - { - $op->$key=$check_op[$key]; - } - - if($op->status_ok) - { - $options = prepareOptions($argsObj); - if( $tprojectMgr->update($argsObj->tprojectID,trim($argsObj->tprojectName), - $argsObj->color, $argsObj->notes, $options, $argsObj->active, - $argsObj->tcasePrefix, $argsObj->is_public) ) - { - $op->msg = ''; - $tprojectMgr->activate($argsObj->tprojectID,$argsObj->active); - - $tprojectMgr->setIssueTrackerEnabled($argsObj->tprojectID,$argsObj->issue_tracker_enabled); - $itMgr = new tlIssueTracker($tprojectMgr->db); - if( ($doLink = $argsObj->issue_tracker_id > 0) ) - { - $itMgr->link($argsObj->issue_tracker_id,$argsObj->tprojectID); - } - else - { - $issueT = $itMgr->getLinkedTo($argsObj->tprojectID); - if( !is_null($issueT) ) - { - $itMgr->unlink($issueT['issuetracker_id'],$issueT['testproject_id']); - } - } - - $tprojectMgr->setCodeTrackerEnabled($argsObj->tprojectID,$argsObj->code_tracker_enabled); - $ctMgr = new tlCodeTracker($tprojectMgr->db); - if( ($doLink = $argsObj->code_tracker_id > 0) ) - { - $ctMgr->link($argsObj->code_tracker_id,$argsObj->tprojectID); - } - else - { - $codeT = $ctMgr->getLinkedTo($argsObj->tprojectID); - if( !is_null($codeT) ) - { - $ctMgr->unlink($codeT['codetracker_id'],$codeT['testproject_id']); - } - } - - $tprojectMgr->setReqMgrIntegrationEnabled($argsObj->tprojectID,$argsObj->reqmgr_integration_enabled); - $mgr = new tlReqMgrSystem($tprojectMgr->db); - if( ($doLink = $argsObj->reqmgrsystem_id > 0) ) - { - $mgr->link($argsObj->reqmgrsystem_id,$argsObj->tprojectID); - } - else - { - $et = $mgr->getLinkedTo($argsObj->tprojectID); - if( !is_null($et) ) - { - $mgr->unlink($et['reqmgrsystem_id'],$et['testproject_id']); - } - } - - if( !$argsObj->is_public) - { - // does user have an SPECIFIC role on Test Project ? - // if answer is yes => do nothing - if(!tlUser::hasRoleOnTestProject($tprojectMgr->db,$argsObj->userID,$argsObj->tprojectID)) - { - $tprojectMgr->addUserRole($argsObj->userID,$argsObj->tprojectID,$argsObj->user->globalRole->dbID); - } - } - - $event = new stdClass(); - $event->message = TLS("audit_testproject_saved",$argsObj->tprojectName); - $event->logLevel = "AUDIT"; - $event->source = "GUI"; - $event->objectID = $argsObj->tprojectID; - $event->objectType = "testprojects"; - $event->code = "UPDATE"; - logEvent($event); - } - else - { - $op->status_ok=0; - } - } - if($op->status_ok) - { - if($sessionTprojectID == $argsObj->tprojectID) - { - $op->reloadType = 'reloadNavBar'; - } - } - else - { - $op->ui->doActionValue = 'doUpdate'; - $op->ui->buttonValue = lang_get('btn_save'); - $op->ui->caption = sprintf(lang_get('caption_edit_tproject'),$op->oldName); - } - - return $op; -} - - -/* - function: edit - initialize variables to launch user interface (smarty template) - to get information to accomplish edit task. - - args: - - returns: - - -*/ -function edit(&$argsObj,&$tprojectMgr) -{ - $tprojectInfo = $tprojectMgr->get_by_id($argsObj->tprojectID); - - $argsObj->tprojectName = $tprojectInfo['name']; - $argsObj->projectOptions = $tprojectInfo['opt']; - $argsObj->tcasePrefix = $tprojectInfo['prefix']; - - $k2l = array('color','notes', 'active','is_public','issue_tracker_enabled', - 'code_tracker_enabled','reqmgr_integration_enabled','api_key'); - foreach($k2l as $key) - { - $argsObj->$key = $tprojectInfo[$key]; - } - - $ui = new stdClass(); - $ui->main_descr=lang_get('title_testproject_management'); - $ui->doActionValue = 'doUpdate'; - $ui->buttonValue = lang_get('btn_save'); - $ui->caption = sprintf(lang_get('caption_edit_tproject'),$argsObj->tprojectName); - return $ui; -} - -/* - function: crossChecks - do checks that are common to create and update operations - - name is valid ? - - name already exists ? - - prefix already exits ? - args: - - returns: - - - -*/ -function crossChecks($argsObj,&$tprojectMgr) -{ - $op = new stdClass(); - $updateAdditionalSQLFilter = null ; - $op = $tprojectMgr->checkName($argsObj->tprojectName); - - $check_op = array(); - $check_op['msg'] = array(); - $check_op['status_ok'] = $op['status_ok']; - - if($argsObj->doAction == 'doUpdate') - { - $updateAdditionalSQLFilter = " testprojects.id <> {$argsObj->tprojectID}"; - } - - if($check_op['status_ok']) - { - if($tprojectMgr->get_by_name($argsObj->tprojectName,$updateAdditionalSQLFilter)) - { - $check_op['msg'][] = sprintf(lang_get('error_product_name_duplicate'),$argsObj->tprojectName); - $check_op['status_ok'] = 0; - } - - // Check prefix no matter what has happen with previous check - $rs = $tprojectMgr->get_by_prefix($argsObj->tcasePrefix,$updateAdditionalSQLFilter); - if(!is_null($rs)) - { - $check_op['msg'][] = sprintf(lang_get('error_tcase_prefix_exists'),$argsObj->tcasePrefix); - $check_op['status_ok'] = 0; - } - } - else - { - $check_op['msg'][] = $op['msg']; - } - return $check_op; -} - -/* - function: create - - args : - - returns: - -*/ -function create(&$argsObj,&$tprojectMgr) -{ - $gui = new stdClass(); - - // Set defaults here - $argsObj->active = 1; - $argsObj->is_public = 1; - $argsObj->optPriority = 1; - $argsObj->optAutomation = 1; - - $gui->active = $argsObj->active; - $gui->is_public = $argsObj->is_public; - $gui->projectOptions = $argsObj->projectOptions = prepareOptions($argsObj); - $gui->doActionValue = 'doCreate'; - $gui->buttonValue = lang_get('btn_create'); - $gui->caption = lang_get('caption_new_tproject'); - - - - new dBug($gui); - - $gui->testprojects = $tprojectMgr->get_all(null,array('access_key' => 'id')); - return $gui; -} - - -/* - function: doDelete - - args : - - returns: - -*/ -function doDelete($argsObj,&$tprojectMgr,$sessionTprojectID) -{ - $tprojectMgr->setAuditLogOn(); - $ope_status = $tprojectMgr->delete($argsObj->tprojectID); - - $op = new stdClass(); - $op->status_ok = $ope_status['status_ok']; - $op->reloadType = 'none'; - - if ($ope_status['status_ok']) - { - $op->reloadType = 'reloadNavBar'; - $op->msg = sprintf(lang_get('test_project_deleted'),$argsObj->tprojectName); - } - else - { - $op->msg = lang_get('info_product_not_deleted_check_log') . ' ' . $ope_status['msg']; - } - - return $op; -} - - - -/* - * - * @internal revisions - * - */ -function initializeGui(&$dbHandler,$argsObj) -{ - - $guiObj = $argsObj; - $guiObj->canManage = $argsObj->user->hasRight($dbHandler,"mgt_modify_product"); - $guiObj->found = 'yes'; - - $ent2loop = array('tlIssueTracker' => 'issueTrackers', 'tlCodeTracker' => 'codeTrackers', - 'tlReqMgrSystem' => 'reqMgrSystems'); - - foreach($ent2loop as $cl => $pr) - { - $mgr = new $cl($dbHandler); - $guiObj->$pr = $mgr->getAll(); - unset($mgr); - } - return $guiObj; -} - - -function checkRights(&$db,&$user) -{ - csrfguard_start(); - return $user->hasRight($db,'mgt_modify_product'); +doActionValue = ''; +$ui->buttonValue = ''; +$ui->caption = ''; +$ui->main_descr = lang_get('title_testproject_management'); + +$user_feedback = ''; +$reloadType = 'none'; +$tproject_mgr = new testproject($db); +$args = initArgs($tproject_mgr, $_REQUEST); + +$gui = initializeGui($db, $args); +$of = web_editor('notes', $_SESSION['basehref'], $editorCfg); +$status_ok = 1; + +switch ($args->doAction) { + case 'create': + $ui = create($args, $tproject_mgr); + $template = $templateCfg->default_template; + $gui->testprojects = $ui->testprojects; + break; + + case 'edit': + $ui = edit($args, $tproject_mgr); + $template = $templateCfg->default_template; + break; + + case 'doCreate': + $op = doCreate($args, $tproject_mgr); + $template = $op->status_ok ? null : $templateCfg->default_template; + $ui = $op->ui; + $status_ok = $op->status_ok; + $user_feedback = $op->msg; + $reloadType = 'projectView'; + break; + + case 'doUpdate': + $op = doUpdate($args, $tproject_mgr, $session_tproject_id); + $template = $op->status_ok ? null : $templateCfg->default_template; + $ui = $op->ui; + $status_ok = $op->status_ok; + $user_feedback = $op->msg; + $reloadType = 'projectView'; + break; + + case 'doDelete': + $op = doDelete($args, $tproject_mgr); + $status_ok = $op->status_ok; + $user_feedback = $op->msg; + $reloadType = 'projectView'; + break; + + case 'setActive': + case 'setInactive': + case 'enableRequirements': + case 'disableRequirements': + $m2c = $args->doAction; + $tproject_mgr->$m2c($args->tprojectID); + $template = null; + $ui = new stdClass(); + $status_ok = tl::OK; + $user_feedback = ''; + $reloadType = 'projectView'; + break; +} + +$ui->main_descr = lang_get('title_testproject_management'); +$smarty = new TLSmarty(); +$smarty->assign('gui_cfg', $gui_cfg); +$smarty->assign('editorType', $editorCfg['type']); +$smarty->assign('mgt_view_events', + $_SESSION['currentUser']->hasRight($db, "mgt_view_events")); + +$feedback_type = ''; +if (! $status_ok) { + $feedback_type = 'error'; + $args->doAction = "ErrorOnAction"; +} + +switch ($args->doAction) { + case "doCreate": + case "doDelete": + case "doUpdate": + case "setActive": + case "setInactive": + case 'enableRequirements': + case 'disableRequirements': + if ($addIssueTracker = $addCodeTracker = $addReqMgrSystem = is_null( + $template)) { + $template = 'projectView.tpl'; + // needed after addition of search function on test project view + $gui->name = ''; + $gui->feedback = ''; + } + + $gui->doAction = $reloadType; + $opt = array( + 'output' => 'array_of_map', + 'order_by' => " ORDER BY nodes_hierarchy.name ", + 'add_issuetracker' => $addIssueTracker, + 'add_codetracker' => $addCodeTracker, + 'add_reqmgrsystem' => $addReqMgrSystem + ); + $gui->tprojects = (array) $tproject_mgr->get_accessible_for_user( + $args->userID, $opt); + + $gui->pageTitle = lang_get('title_testproject_management'); + $gui->itemQty = $tprojQty = count($gui->tprojects); + + if ($gui->itemQty > 0) { + $gui->pageTitle .= ' ' . + sprintf(lang_get('available_test_projects'), $gui->itemQty); + } + $imgSet = $smarty->getImages(); + + if ($addIssueTracker) { + $labels = init_labels( + array( + 'active_integration' => null, + 'inactive_integration' => null + )); + + for ($idx = 0; $idx < $tprojQty; $idx ++) { + $gui->tprojects[$idx]['itstatusImg'] = ''; + if ($gui->tprojects[$idx]['itname'] != '') { + $ak = ($gui->tprojects[$idx]['issue_tracker_enabled']) ? 'active' : 'inactive'; + $gui->tprojects[$idx]['itstatusImg'] = ' ' .
+                        $labels[$ak . '_integration'] . ''; + } + } + } + + if ($addCodeTracker) { + $labels = init_labels( + array( + 'active_integration' => null, + 'inactive_integration' => null + )); + + for ($idx = 0; $idx < $tprojQty; $idx ++) { + $gui->tprojects[$idx]['ctstatusImg'] = ''; + if ($gui->tprojects[$idx]['ctname'] != '') { + $ak = ($gui->tprojects[$idx]['code_tracker_enabled']) ? 'active' : 'inactive'; + $gui->tprojects[$idx]['ctstatusImg'] = ' ' .
+                        $labels[$ak . '_integration'] . ''; + } + } + } + + if ($addReqMgrSystem) { + $labels = init_labels( + array( + 'active_integration' => null, + 'inactive_integration' => null + )); + + for ($idx = 0; $idx < $tprojQty; $idx ++) { + $gui->tprojects[$idx]['rmsstatusImg'] = ''; + if ($gui->tprojects[$idx]['rmsname'] != '') { + $ak = ($gui->tprojects[$idx]['reqmgr_integration_enabled']) ? 'active' : 'inactive'; + $gui->tprojects[$idx]['rmsstatusImg'] = ' ' .
+                        $labels[$ak . '_integration'] . ''; + } + } + } + + $gui->editorType = $editorCfg['type']; + $smarty->assign('gui', $gui); + $smarty->display($templateCfg->template_dir . $template); + break; + + case "ErrorOnAction": + default: + if ($args->doAction != "edit" && $args->doAction != "ErrorOnAction") { + $of->Value = getItemTemplateContents('project_template', + $of->InstanceName, $args->notes); + } else { + $of->Value = $args->notes; + } + + foreach ($ui as $prop => $value) { + $smarty->assign($prop, $value); + } + + $smarty->assign('gui', $args); + $smarty->assign('notes', $of->CreateHTML()); + $smarty->assign('user_feedback', $user_feedback); + $smarty->assign('feedback_type', $feedback_type); + $smarty->display($templateCfg->template_dir . $template); + break; +} + +/** + * INITialize page ARGuments, using the $_REQUEST and $_SESSION + * super-global hashes. + * Important: changes in HTML input elements on the Smarty template + * must be reflected here. + * + * @param array $request_hash + * the $_REQUEST + * @param + * hash session_hash the $_SESSION + * @return stdClass object with html values tranformed and other + * generated variables. + * @internal + */ +function initArgs($tprojectMgr, $request_hash) +{ + $args = new stdClass(); + $request_hash = strings_stripSlashes($request_hash); + + $nullable_keys = array( + 'tprojectName', + 'color', + 'notes', + 'doAction', + 'tcasePrefix', + 'api_key' + ); + foreach ($nullable_keys as $value) { + $args->$value = isset($request_hash[$value]) ? trim( + $request_hash[$value]) : null; + } + + $intval_keys = array( + 'tprojectID' => 0, + 'copy_from_tproject_id' => 0 + ); + foreach ($intval_keys as $key => $value) { + $args->$key = isset($request_hash[$key]) ? intval($request_hash[$key]) : $value; + } + + // get input from the project edit/create page + $checkbox_keys = array( + 'is_public' => 0, + 'active' => 0, + 'optPriority' => 0, + 'optAutomation' => 0, + 'optReq' => 0, + 'optInventory' => 0, + 'issue_tracker_enabled' => 0, + 'code_tracker_enabled' => 0, + 'reqmgr_integration_enabled' => 0 + ); + foreach ($checkbox_keys as $key => $value) { + $args->$key = isset($request_hash[$key]) ? 1 : $value; + } + + $args->issue_tracker_id = isset($request_hash['issue_tracker_id']) ? intval( + $request_hash['issue_tracker_id']) : 0; + $args->code_tracker_id = isset($request_hash['code_tracker_id']) ? intval( + $request_hash['code_tracker_id']) : 0; + $args->reqmgrsystem_id = isset($request_hash['reqmgrsystem_id']) ? intval( + $request_hash['reqmgrsystem_id']) : 0; + + // This way we are safe + if ($args->issue_tracker_id == 0) { + $args->issue_tracker_enabled = 0; + } + + if ($args->code_tracker_id == 0) { + $args->code_tracker_enabled = 0; + } + + if ($args->doAction != 'doUpdate' && $args->doAction != 'doCreate') { + if ($args->tprojectID > 0) { + $the_data = $tprojectMgr->get_by_id($args->tprojectID); + $args->notes = $the_data['notes']; + + $args->issue_tracker_enabled = intval( + $the_data['issue_tracker_enabled']); + $args->issue_tracker_id = 0; + $itMgr = new tlIssueTracker($tprojectMgr->db); + $issueT = $itMgr->getLinkedTo($args->tprojectID); + if (! is_null($issueT)) { + $args->issue_tracker_id = $issueT['issuetracker_id']; + } + + $args->code_tracker_enabled = intval( + $the_data['code_tracker_enabled']); + $args->code_tracker_id = 0; + $ctMgr = new tlCodeTracker($tprojectMgr->db); + $codeT = $ctMgr->getLinkedTo($args->tprojectID); + if (! is_null($codeT)) { + $args->code_tracker_id = $codeT['codetracker_id']; + } + + $args->reqmgr_integration_enabled = intval( + $the_data['reqmgr_integration_enabled']); + $args->reqmgrsystem_id = 0; + $mgr = new tlReqMgrSystem($tprojectMgr->db); + $et = $mgr->getLinkedTo($args->tprojectID); + if (! is_null($et)) { + $args->reqmgrsystem_id = $et['reqmgrsystem_id']; + } + + if ($args->doAction == 'doDelete') { + $args->tprojectName = $the_data['name']; + } + } else { + $args->notes = ''; + } + } + + // sanitize output via black list + if ($args->notes != '') { + // The Black List - Jon Bokenkamp + $bl = array( + '' + ); + foreach ($bl as $tg) { + $cl[] = htmlentities($tg); + } + $args->notes = str_replace($bl, $cl, $args->notes); + } + + $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; + $args->userID = intval( + isset($_SESSION['userID']) ? intval($_SESSION['userID']) : 0); + $args->testprojects = null; + $args->projectOptions = prepareOptions($args); + + return $args; +} + +/** + * Collect a test project options (input from form) to a singleton + * + * @param array $argsObj + * the page input + * @return stdClass data to be stored + */ +function prepareOptions($argsObj) +{ + $opts = new stdClass(); + $opts->requirementsEnabled = $argsObj->optReq; + $opts->testPriorityEnabled = $argsObj->optPriority; + $opts->automationEnabled = $argsObj->optAutomation; + $opts->inventoryEnabled = $argsObj->optInventory; + + return $opts; +} + +/** + * ATTENTION: logEvent() is done on testproject->create() + */ +function doCreate($argsObj, &$tprojectMgr) +{ + $key2get = array( + 'status_ok', + 'msg' + ); + + $op = new stdClass(); + $op->ui = new stdClass(); + + $op->status_ok = 0; + $op->template = null; + $op->msg = ''; + $op->id = 0; + $op->reloadType = 'none'; + + $check_op = crossChecks($argsObj, $tprojectMgr); + foreach ($key2get as $key) { + $op->$key = $check_op[$key]; + } + + if ($op->status_ok) { + try { + $shazam = false; + $item = $argsObj; + $item->name = $argsObj->tprojectName; + $item->prefix = $argsObj->tcasePrefix; + $item->options = prepareOptions($argsObj); + $new_id = $tprojectMgr->create($item, + array( + 'doChecks' => true, + 'setSessionProject' => true + )); + } catch (Exception $e) { + $new_id = - 1; + $op->status_ok = false; + $op->msg = $e->getMessage(); + $shazam = true; + } + + if ($new_id <= 0) { + if (! $shazam) { + $op->msg = lang_get('refer_to_log'); + } + } else { + $op->template = 'projectView.tpl'; + $op->id = $new_id; + + if ($argsObj->issue_tracker_enabled) { + $tprojectMgr->enableIssueTracker($new_id); + } else { + $tprojectMgr->disableIssueTracker($new_id); + } + + $itMgr = new tlIssueTracker($tprojectMgr->db); + if ($argsObj->issue_tracker_id > 0) { + $itMgr->link($argsObj->issue_tracker_id, $new_id); + } + + if ($argsObj->code_tracker_enabled) { + $tprojectMgr->enableCodeTracker($new_id); + } else { + $tprojectMgr->disableCodeTracker($new_id); + } + + $ctMgr = new tlCodeTracker($tprojectMgr->db); + if ($argsObj->code_tracker_id > 0) { + $ctMgr->link($argsObj->code_tracker_id, $new_id); + } + + if (! $argsObj->is_public) { + // Need to add specific role on test project in order to not make + // it invisible for me!!! + $tprojectMgr->addUserRole($argsObj->userID, $new_id, + $argsObj->user->globalRole->dbID); + } + } + } + + if ($op->status_ok) { + $op->reloadType = 'reloadNavBar'; + if ($argsObj->copy_from_tproject_id > 0) { + $options = array( + 'copy_requirements' => $argsObj->optReq + ); + $tprojectMgr->copy_as($argsObj->copy_from_tproject_id, $new_id, + $argsObj->userID, trim($argsObj->tprojectName), $options); + } + } else { + $op->ui->doActionValue = 'doCreate'; + $op->ui->buttonValue = lang_get('btn_create'); + $op->ui->caption = lang_get('caption_new_tproject'); + } + + return $op; +} + +/* + * function: doUpdate + * + * args: + * + * returns: + * + */ +function doUpdate($argsObj, &$tprojectMgr, $sessionTprojectID) +{ + $key2get = array( + 'status_ok', + 'msg' + ); + + $op = new stdClass(); + $op->ui = new stdClass(); + $op->status_ok = 0; + $op->msg = ''; + $op->template = null; + $op->reloadType = 'none'; + + $oldObjData = $tprojectMgr->get_by_id($argsObj->tprojectID); + $op->oldName = $oldObjData['name']; + + $check_op = crossChecks($argsObj, $tprojectMgr); + foreach ($key2get as $key) { + $op->$key = $check_op[$key]; + } + + if ($op->status_ok) { + $options = prepareOptions($argsObj); + if ($tprojectMgr->update($argsObj->tprojectID, + trim($argsObj->tprojectName), $argsObj->color, $argsObj->notes, + $options, $argsObj->active, $argsObj->tcasePrefix, + $argsObj->is_public)) { + $op->msg = ''; + $tprojectMgr->activate($argsObj->tprojectID, $argsObj->active); + + $tprojectMgr->setIssueTrackerEnabled($argsObj->tprojectID, + $argsObj->issue_tracker_enabled); + $itMgr = new tlIssueTracker($tprojectMgr->db); + if ($argsObj->issue_tracker_id > 0) { + $itMgr->link($argsObj->issue_tracker_id, $argsObj->tprojectID); + } else { + $issueT = $itMgr->getLinkedTo($argsObj->tprojectID); + if (! is_null($issueT)) { + $itMgr->unlink($issueT['issuetracker_id'], + $issueT['testproject_id']); + } + } + + $tprojectMgr->setCodeTrackerEnabled($argsObj->tprojectID, + $argsObj->code_tracker_enabled); + $ctMgr = new tlCodeTracker($tprojectMgr->db); + if ($argsObj->code_tracker_id > 0) { + $ctMgr->link($argsObj->code_tracker_id, $argsObj->tprojectID); + } else { + $codeT = $ctMgr->getLinkedTo($argsObj->tprojectID); + if (! is_null($codeT)) { + $ctMgr->unlink($codeT['codetracker_id'], + $codeT['testproject_id']); + } + } + + $tprojectMgr->setReqMgrIntegrationEnabled($argsObj->tprojectID, + $argsObj->reqmgr_integration_enabled); + $mgr = new tlReqMgrSystem($tprojectMgr->db); + if ($argsObj->reqmgrsystem_id > 0) { + $mgr->link($argsObj->reqmgrsystem_id, $argsObj->tprojectID); + } else { + $et = $mgr->getLinkedTo($argsObj->tprojectID); + if (! is_null($et)) { + $mgr->unlink($et['reqmgrsystem_id'], $et['testproject_id']); + } + } + + // does user have an SPECIFIC role on Test Project ? + // if answer is yes => do nothing + if (! $argsObj->is_public && + ! tlUser::hasRoleOnTestProject($tprojectMgr->db, + $argsObj->userID, $argsObj->tprojectID)) { + $tprojectMgr->addUserRole($argsObj->userID, $argsObj->tprojectID, + $argsObj->user->globalRole->dbID); + } + + $event = new stdClass(); + $event->message = TLS("audit_testproject_saved", + $argsObj->tprojectName); + $event->logLevel = "AUDIT"; + $event->source = "GUI"; + $event->objectID = $argsObj->tprojectID; + $event->objectType = "testprojects"; + $event->code = "UPDATE"; + logEvent($event); + } else { + $op->status_ok = 0; + } + } + if ($op->status_ok) { + if ($sessionTprojectID == $argsObj->tprojectID) { + $op->reloadType = 'reloadNavBar'; + } + } else { + $op->ui->doActionValue = 'doUpdate'; + $op->ui->buttonValue = lang_get('btn_save'); + $op->ui->caption = sprintf(lang_get('caption_edit_tproject'), + $op->oldName); + } + + return $op; +} + +/* + * function: edit + * initialize variables to launch user interface (smarty template) + * to get information to accomplish edit task. + * + * args: + * + * returns: - + * + */ +function edit(&$argsObj, &$tprojectMgr) +{ + $tprojectInfo = $tprojectMgr->get_by_id($argsObj->tprojectID); + + $argsObj->tprojectName = $tprojectInfo['name']; + $argsObj->projectOptions = $tprojectInfo['opt']; + $argsObj->tcasePrefix = $tprojectInfo['prefix']; + + $k2l = array( + 'color', + 'notes', + 'active', + 'is_public', + 'issue_tracker_enabled', + 'code_tracker_enabled', + 'reqmgr_integration_enabled', + 'api_key' + ); + foreach ($k2l as $key) { + $argsObj->$key = $tprojectInfo[$key]; + } + + $ui = new stdClass(); + $ui->main_descr = lang_get('title_testproject_management'); + $ui->doActionValue = 'doUpdate'; + $ui->buttonValue = lang_get('btn_save'); + $ui->caption = sprintf(lang_get('caption_edit_tproject'), + $argsObj->tprojectName); + return $ui; +} + +/* + * function: crossChecks + * do checks that are common to create and update operations + * - name is valid ? + * - name already exists ? + * - prefix already exits ? + * args: + * + * returns: - + * + * + */ +function crossChecks($argsObj, &$tprojectMgr) +{ + $updateAdditionalSQLFilter = null; + $op = $tprojectMgr->checkName($argsObj->tprojectName); + + $check_op = array(); + $check_op['msg'] = array(); + $check_op['status_ok'] = $op['status_ok']; + + if ($argsObj->doAction == 'doUpdate') { + $updateAdditionalSQLFilter = " testprojects.id <> {$argsObj->tprojectID}"; + } + + if ($check_op['status_ok']) { + if ($tprojectMgr->get_by_name($argsObj->tprojectName, + $updateAdditionalSQLFilter)) { + $check_op['msg'][] = sprintf( + lang_get('error_product_name_duplicate'), $argsObj->tprojectName); + $check_op['status_ok'] = 0; + } + + // Check prefix no matter what has happen with previous check + $rs = $tprojectMgr->get_by_prefix($argsObj->tcasePrefix, + $updateAdditionalSQLFilter); + if (! is_null($rs)) { + $check_op['msg'][] = sprintf(lang_get('error_tcase_prefix_exists'), + $argsObj->tcasePrefix); + $check_op['status_ok'] = 0; + } + } else { + $check_op['msg'][] = $op['msg']; + } + return $check_op; +} + +/* + * function: create + * + * args : + * + * returns: + * + */ +function create(&$argsObj, &$tprojectMgr) +{ + $gui = new stdClass(); + + // Set defaults here + $argsObj->active = 1; + $argsObj->is_public = 1; + $argsObj->optPriority = 1; + $argsObj->optAutomation = 1; + + $gui->active = $argsObj->active; + $gui->is_public = $argsObj->is_public; + $gui->projectOptions = $argsObj->projectOptions = prepareOptions($argsObj); + $gui->doActionValue = 'doCreate'; + $gui->buttonValue = lang_get('btn_create'); + $gui->caption = lang_get('caption_new_tproject'); + + $gui->testprojects = $tprojectMgr->get_all(null, + array( + 'access_key' => 'id' + )); + return $gui; +} + +/* + * function: doDelete + * + * args : + * + * returns: + * + */ +function doDelete($argsObj, &$tprojectMgr) +{ + $tprojectMgr->setAuditLogOn(); + $ope_status = $tprojectMgr->delete($argsObj->tprojectID); + + $op = new stdClass(); + $op->status_ok = $ope_status['status_ok']; + $op->reloadType = 'none'; + + if ($ope_status['status_ok']) { + $op->reloadType = 'reloadNavBar'; + $op->msg = sprintf(lang_get('test_project_deleted'), + $argsObj->tprojectName); + } else { + $op->msg = lang_get('info_product_not_deleted_check_log') . ' ' . + $ope_status['msg']; + } + + return $op; +} + +/* + * + * @internal revisions + * + */ +function initializeGui(&$dbHandler, $argsObj) +{ + $guiObj = $argsObj; + $guiObj->canManage = $argsObj->user->hasRight($dbHandler, + "mgt_modify_product"); + $guiObj->found = 'yes'; + + $ent2loop = array( + 'tlIssueTracker' => 'issueTrackers', + 'tlCodeTracker' => 'codeTrackers', + 'tlReqMgrSystem' => 'reqMgrSystems' + ); + + foreach ($ent2loop as $cl => $pr) { + $mgr = new $cl($dbHandler); + $guiObj->$pr = $mgr->getAll(); + unset($mgr); + } + return $guiObj; +} + +function checkRights(&$db, &$user) +{ + csrfguard_start(); + return $user->hasRight($db, 'mgt_modify_product'); } diff --git a/lib/project/projectView.php b/lib/project/projectView.php index d900078b8a..6bca34bfae 100644 --- a/lib/project/projectView.php +++ b/lib/project/projectView.php @@ -1,146 +1,153 @@ -default_template; -if(!is_null($gui->tprojects) || $args->doAction=='list') { - if( $gui->itemQty == 0 ) { - $template2launch = "projectEdit.tpl"; - $gui->doAction = "create"; - } -} - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $template2launch); - - -/** - * - * - */ -function init_args() { - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0 ; - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : 'list' ; - $args->userID = isset($_SESSION['userID']) ? intval($_SESSION['userID']) : 0; - $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; - $args->name = isset($_REQUEST['name']) ? trim($_REQUEST['name']) : null ; - - - - if(!is_null($args->name)) - { - $args->name = trim($args->name); - if(strlen($args->name) == 0) - { - $args->name = null; - } - else - { - $args->name = substr($args->name,0,100); - } - } - return $args; -} - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj) { - - $tplEngine = new TLSmarty(); - - $guiObj = new stdClass(); - $guiObj->doAction = $argsObj->doAction; - $guiObj->canManage = $argsObj->user->hasRight($dbHandler,"mgt_modify_product"); - $guiObj->name = is_null($argsObj->name) ? '' : $argsObj->name; - $guiObj->feedback = ''; - - switch($argsObj->doAction) { - case 'list': - $filters = null; - break; - - case 'search': - default: - $filters = array('name' => array('op' => 'like', 'value' => $argsObj->name)); - $guiObj->feedback = lang_get('no_records_found'); - break; - } - - $tproject_mgr = new testproject($dbHandler); - $opt = array('output' => 'array_of_map', 'order_by' => " ORDER BY name ", - 'add_issuetracker' => true, - 'add_codetracker' => true, 'add_reqmgrsystem' => true); - $guiObj->tprojects = $tproject_mgr->get_accessible_for_user($argsObj->userID,$opt,$filters); - $guiObj->pageTitle = lang_get('title_testproject_management'); - - $cfg = getWebEditorCfg('testproject'); - $guiObj->editorType = $cfg['type']; - - $guiObj->itemQty = count($guiObj->tprojects); - - if($guiObj->itemQty > 0) { - $guiObj->pageTitle .= ' ' . sprintf(lang_get('available_test_projects'),$guiObj->itemQty); - - initIntegrations($guiObj->tprojects,$guiObj->itemQty,$tplEngine); - } - - return array($guiObj,$tplEngine); -} - -/** - * - */ -function initIntegrations(&$tprojSet,$tprojQty,&$tplEngine) { - $labels = init_labels(array('active_integration' => null, - 'inactive_integration' => null)); - - $imgSet = $tplEngine->getImages(); - - $intk = array('it' => 'issue', 'ct' => 'code'); - for($idx=0; $idx < $tprojQty; $idx++) { - foreach( $intk as $short => $item ) { - $tprojSet[$idx][$short . 'statusImg'] = ''; - if($tprojSet[$idx][$short . 'name'] != '') { - $ak = ($tprojSet[$idx][$item . '_tracker_enabled']) ? - 'active' : 'inactive'; - $tprojSet[$idx][$short . 'statusImg'] = - ' ' . $labels[$ak . '_integration'] . ''; - } - } - } -} - - -/** - * - */ -function checkRights(&$db,&$user) { - return $user->hasRight($db,'mgt_modify_product'); +default_template; +if ((! is_null($gui->tprojects) || $args->doAction == 'list') && + $gui->itemQty == 0) { + $template2launch = "projectEdit.tpl"; + $gui->doAction = "create"; +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $template2launch); + +/** + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : 'list'; + $args->userID = isset($_SESSION['userID']) ? intval($_SESSION['userID']) : 0; + $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; + $args->name = isset($_REQUEST['name']) ? trim($_REQUEST['name']) : null; + + if (! is_null($args->name)) { + $args->name = trim($args->name); + if (strlen($args->name) == 0) { + $args->name = null; + } else { + $args->name = substr($args->name, 0, 100); + } + } + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $tplEngine = new TLSmarty(); + + $guiObj = new stdClass(); + $guiObj->doAction = $argsObj->doAction; + $guiObj->canManage = $argsObj->user->hasRight($dbHandler, + "mgt_modify_product"); + $guiObj->name = is_null($argsObj->name) ? '' : $argsObj->name; + $guiObj->feedback = ''; + + switch ($argsObj->doAction) { + case 'list': + $filters = null; + break; + + case 'search': + default: + $filters = array( + 'name' => array( + 'op' => 'like', + 'value' => $argsObj->name + ) + ); + $guiObj->feedback = lang_get('no_records_found'); + break; + } + + $tproject_mgr = new testproject($dbHandler); + $opt = array( + 'output' => 'array_of_map', + 'order_by' => " ORDER BY name ", + 'add_issuetracker' => true, + 'add_codetracker' => true, + 'add_reqmgrsystem' => true + ); + $guiObj->tprojects = $tproject_mgr->get_accessible_for_user( + $argsObj->userID, $opt, $filters); + $guiObj->pageTitle = lang_get('title_testproject_management'); + + $cfg = getWebEditorCfg('testproject'); + $guiObj->editorType = $cfg['type']; + + $guiObj->itemQty = count($guiObj->tprojects); + + if ($guiObj->itemQty > 0) { + $guiObj->pageTitle .= ' ' . + sprintf(lang_get('available_test_projects'), $guiObj->itemQty); + + initIntegrations($guiObj->tprojects, $guiObj->itemQty, $tplEngine); + } + + return array( + $guiObj, + $tplEngine + ); +} + +/** + */ +function initIntegrations(&$tprojSet, $tprojQty, &$tplEngine) +{ + $labels = init_labels( + array( + 'active_integration' => null, + 'inactive_integration' => null + )); + + $imgSet = $tplEngine->getImages(); + + $intk = array( + 'it' => 'issue', + 'ct' => 'code' + ); + for ($idx = 0; $idx < $tprojQty; $idx ++) { + foreach ($intk as $short => $item) { + $tprojSet[$idx][$short . 'statusImg'] = ''; + if ($tprojSet[$idx][$short . 'name'] != '') { + $ak = ($tprojSet[$idx][$item . '_tracker_enabled']) ? 'active' : 'inactive'; + $tprojSet[$idx][$short . 'statusImg'] = ' ' .
+                    $labels[$ak . '_integration'] . ''; + } + } + } +} + +/** + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'mgt_modify_product'); } diff --git a/lib/project/project_req_spec_mgmt.php b/lib/project/project_req_spec_mgmt.php index 62a80dda26..5640dacd02 100644 --- a/lib/project/project_req_spec_mgmt.php +++ b/lib/project/project_req_spec_mgmt.php @@ -1,40 +1,49 @@ -main_descr = lang_get('testproject') . TITLE_SEP . $tproject_name . TITLE_SEP . lang_get('title_req_spec'); -$gui->tproject_id = $tproject_id; -$gui->refresh_tree = 'no'; - -$uo = $_SESSION['currentUser']; -$gui->grants = new stdClass(); -$gui->grants->modify = $uo->hasRight($db,'mgt_modify_req'); -$gui->grants->ro = $uo->hasRight($db,'mgt_view_req'); - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display('requirements/project_req_spec_mgmt.tpl'); - -/** - * - */ -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,'mgt_view_req') || - $user->hasRight($db,'mgt_modify_req')); -} \ No newline at end of file +tproject_id = $tproject_id; +checkRights($db, $uo, $context); + +$gui = new stdClass(); +$gui->main_descr = lang_get('testproject') . TITLE_SEP . $tproject_name . + TITLE_SEP . lang_get('title_req_spec'); +$gui->tproject_id = $tproject_id; +$gui->refresh_tree = 'no'; + +$gui->grants = new stdClass(); +$gui->grants->modify = $uo->hasRight($db, 'mgt_modify_req', + $context->tproject_id); +$gui->grants->ro = $uo->hasRight($db, 'mgt_view_req', $context->tproject_id); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display('requirements/project_req_spec_mgmt.tpl'); + +/** + */ +function checkRights(&$db, &$user, $context) +{ + $context->rightsOr = [ + "mgt_view_req", + "mgt_modify_req" + ]; + $context->rightsAnd = []; + pageAccessCheck($db, $user, $context); +} diff --git a/lib/reqmgrsystemintegration/reqMgrSystemInterface.class.php b/lib/reqmgrsystemintegration/reqMgrSystemInterface.class.php index 83aa68101c..2d5bf91cc0 100644 --- a/lib/reqmgrsystemintegration/reqMgrSystemInterface.class.php +++ b/lib/reqmgrsystemintegration/reqMgrSystemInterface.class.php @@ -1,4 +1,5 @@ setCfg($config) ) - { - $this->connect(); - } - else - { - $this->connected = false; - } - } - - - /** - * - **/ - function getCfg() - { - return $this->cfg; - } - - /** - * - **/ - function setCfg($xmlString) - { - $msg = null; - $signature = 'Source:' . __METHOD__; - - $xmlCfg = " " . $xmlString; - libxml_use_internal_errors(true); - try - { - $this->cfg = simplexml_load_string($xmlCfg); - if (!$this->cfg) - { - $msg = $signature . " - Failure loading XML STRING\n"; - foreach(libxml_get_errors() as $error) - { - $msg .= "\t" . $error->message; - } - } - } - catch(Exception $e) - { - $msg = $signature . " - Exception loading XML STRING\n"; - $msg .= 'Message: ' .$e->getMessage(); - } - - return is_null($msg); - } - /** - * - **/ - function getMyInterface() - { - return $this->cfg->interfacePHP; - } + private $connected; + + private $cfg = null; + + // simpleXML object + private $interfaceViaDB = false; + + // Variables related to establishing the connection + private $serverConnection = null; + private $server = null; - function isConnected() - { - return $this->connected; - } + private $user = null; + private $password = null; - // =========================================================== - function connect() - { - if (is_null($type)) + // Variables related to retrieving and caching the requirements + private $projects = array(); + + private $lastproject = null; + + private $baselines = array(); + + private $lastbaseline = null; + + private $requirements = array(); + + private $type = null; + + // Variables related to requirement modifications during import. + private $prefix = ""; + + /** + * Will follow same approach used for issue tracking integration, + * connection will be done when constructing. + */ + public function __construct($type, $config) { - return false; + if ($this->setCfg($config)) { + $this->connect(); + } else { + $this->connected = false; + } } - - $this->server = $server; - $this->user = $user; - $this->password = $password; - - return true; - } - - function disconnect($url) - { - if (!is_null($this->server)) + + /** + */ + public function getCfg() { - // Need to disconnect from the server somehow + return $this->cfg; } - - $this->server = null; - $this->user = null; - $this->password = null; - - return true; - } - - function getProjects() - { - if (is_null($server)) + + /** + */ + public function setCfg($xmlString) { - // There is no connection with the requirement management server. - return false; + $msg = null; + $signature = 'Source:' . __METHOD__; + + $xmlCfg = " " . $xmlString; + libxml_use_internal_errors(true); + try { + $this->cfg = simplexml_load_string($xmlCfg); + if (! $this->cfg) { + $msg = $signature . " - Failure loading XML STRING\n"; + foreach (libxml_get_errors() as $error) { + $msg .= "\t" . $error->message; + } + } + } catch (Exception $e) { + $msg = $signature . " - Exception loading XML STRING\n"; + $msg .= 'Message: ' . $e->getMessage(); + } + + return is_null($msg); } - $this->projects = array(); - $this->lastproject = null; - $this->lastbaseline = null; - - if (count($this->projects) == 0) + /** + */ + public function getMyInterface() { - // No projects were found. - return false; + return $this->cfg->interfacePHP; } - return $this->projects; - } - - function getBaselines($project, $refresh = false) - { - if (is_null($serverConnection)) + + public function isConnected() { - // There is no connection with the requirement management server. - return false ; + return $this->connected; } - - if (($project != $this->lastproject) || (count($this->baselines) == 0) || $refresh) + + private function connect() { - // Retrieve baselines for the specified project. - $this->lastproject = $project; - $this->baselines = array(); + if (is_null($type)) { + return false; + } + + $this->server = $server; + $this->user = $user; + $this->password = $password; + + return true; } - else + + private function disconnect($url) { - // Baselines are already available. + if (! is_null($this->server)) { + // Need to disconnect from the server somehow + } + + $this->server = null; + $this->user = null; + $this->password = null; + + return true; + } + + public function getProjects() + { + if (is_null($server)) { + // There is no connection with the requirement management server. + return false; + } + + $this->projects = array(); + $this->lastproject = null; + $this->lastbaseline = null; + + if (count($this->projects) == 0) { + // No projects were found. + return false; + } + return $this->projects; } - - return $this->baselines; - } - - function getRequirements($project, $baseline, $refresh = false) - { - if ($project != $this->lastproject) + + public function getBaselines($project, $refresh = false) { - if (!$this->getBaselines($project)) - { - // Baselines for specified projects could not be retrieved. - return false; - } + if (is_null($serverConnection)) { + // There is no connection with the requirement management server. + return false; + } + + if (($project != $this->lastproject) || (count($this->baselines) == 0) || + $refresh) { + // Retrieve baselines for the specified project. + $this->lastproject = $project; + $this->baselines = array(); + } else { + // Baselines are already available. + } + + return $this->baselines; } - - if (($baseline != $this->lastbaseline) || $refresh) + + public function getRequirements($project, $baseline, $refresh = false) { - // Retrieve the set of requirements in case it is a different baseline as last retrieved - // or the list needs to be refreshed + if ($project != $this->lastproject && ! $this->getBaselines($project)) { + // Baselines for specified projects could not be retrieved. + return false; + } + + if (($baseline != $this->lastbaseline) || $refresh) { + // Retrieve the set of requirements in case it is a different baseline as last retrieved + // or the list needs to be refreshed + } + + return $this->requirements; } - - return $this->requirements; - } - } -?> \ No newline at end of file +?> diff --git a/lib/reqmgrsystems/reqMgrSystemCommands.class.php b/lib/reqmgrsystems/reqMgrSystemCommands.class.php index 333b4ba0ba..c6cba62c70 100644 --- a/lib/reqmgrsystems/reqMgrSystemCommands.class.php +++ b/lib/reqmgrsystems/reqMgrSystemCommands.class.php @@ -1,238 +1,240 @@ -db = $dbHandler; - $this->mgr = new tlReqMgrSystem($dbHandler); - $this->entitySpec = $this->mgr->getEntitySpec(); - - $this->grants=new stdClass(); - $this->grants->canManage = false; - - $this->guiOpWhiteList = array_flip(array('checkConnection','create','edit','delete','doCreate', - 'doUpdate','doDelete')); - } - - function setTemplateCfg($cfg) - { - $this->templateCfg = $cfg; - } - - function getGuiOpWhiteList() - { - return $this->guiOpWhiteList; - } - - /** - * - * - */ - function initGuiBean(&$argsObj, $caller) - { - $obj = new stdClass(); - $obj->action = $caller; - $obj->typeDomain = $this->mgr->getTypes(); - $obj->canManage = $argsObj->currentUser->hasRight($this->db,'reqmgrsystem_management'); - $obj->user_feedback = array('type' => '', 'message' => ''); - - $obj->l18n = init_labels(array('reqmgrsystem_management' => null, 'btn_save' => null, - 'create' => null, 'edit' => null, 'reqmgrsystem_deleted' => null)); - - // we experiment on way to get Action Description for GUI using __FUNCTION__ - $obj->l18n['doUpdate'] = $obj->l18n['edit']; - $obj->l18n['doCreate'] = $obj->l18n['create']; - $obj->l18n['doDelete'] = ''; - $obj->main_descr = $obj->l18n['reqmgrsystem_management']; - $obj->action_descr = ucfirst($obj->l18n[$caller]); - - switch($caller) - { - case 'delete': - case 'doDelete': - $obj->submit_button_label = ''; - break; - - default: - $obj->submit_button_label = $obj->l18n['btn_save']; - break; - } - - return $obj; - } - - /** - * - * - */ - function create(&$argsObj,$request,$caller=null) - { - $guiObj = $this->initGuiBean($argsObj,(is_null($caller) ? __FUNCTION__ : $caller)); - $templateCfg = templateConfiguration('reqMgrSystemEdit'); - $guiObj->template = $templateCfg->default_template; - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'reqmgrsystem_management'); - - $guiObj->item = array('id' => 0); - $dummy = ''; - foreach($this->entitySpec as $property => $type) - { - $guiObj->item[$property] = ($type == 'int') ? 0 :''; - } - return $guiObj; - } - - /** - * - * - */ - function doCreate(&$argsObj,$request) - { - $guiObj = $this->create($argsObj,$request,__FUNCTION__); - - // Checks are centralized on create() - $it = new stdClass(); - foreach($this->entitySpec as $property => $type) - { - $it->$property = $argsObj->$property; - - } - - // Save user input. - // This will be useful if create() will fail, to present values again on GUI - $guiObj->item = (array)$it; - - $op = $this->mgr->create($it); - if($op['status_ok']) - { - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->template = "reqMgrSystemView.php"; - } - else - { - $templateCfg = templateConfiguration('reqMgrSystemEdit'); - $guiObj->template=$templateCfg->default_template; - $guiObj->user_feedback['message'] = $op['msg']; - } - - return $guiObj; - } - - - - - /* - function: edit - - args: - - returns: - - */ - function edit(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - $templateCfg = templateConfiguration('reqMgrSystemEdit'); - $guiObj->template = $templateCfg->default_template; - - $guiObj->item = $this->mgr->getByID($argsObj->id); - $guiObj->canManage = $argsObj->currentUser->hasRight($this->db,'reqmgrsystem_management'); - return $guiObj; - } - - - /* - function: doUpdate - - args: - - returns: - - */ - function doUpdate(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - $it = new stdClass(); - $it->id = $argsObj->id; - foreach($this->entitySpec as $property => $type) - { - $it->$property = $argsObj->$property; - } - - // Save user input. - // This will be useful if create() will fail, to present values again on GUI - $guiObj->item = (array)$it; - - $op = $this->mgr->update($it); - if( $op['status_ok'] ) - { - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->template = "reqMgrSystemView.php"; - } - else - { - $guiObj->user_feedback['message'] = $op['msg']; - $guiObj->template = null; - } - - return $guiObj; - } - - /** - * - * - */ - function doDelete(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - $op = $this->mgr->delete($argsObj->id); - $guiObj->action = 'doDelete'; - $guiObj->template = "reqMgrSystemView.php?"; - return $guiObj; - } - - - function checkConnection(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj,__FUNCTION__); - - $xx = $this->mgr->getByID($argsObj->id); - $class2create = $xx['implementation']; - $its = new $class2create($xx['type'],$xx['cfg']); - - - $guiObj->template = "reqMgrSystemView.php?"; - $guiObj->connectionStatus = $its->isConnected() ? 'ok' : 'ko'; - return $guiObj; - } - -} // end class -?> \ No newline at end of file +db = $dbHandler; + $this->mgr = new tlReqMgrSystem($dbHandler); + $this->entitySpec = $this->mgr->getEntitySpec(); + + $this->grants = new stdClass(); + $this->grants->canManage = false; + + $this->guiOpWhiteList = array_flip( + array( + 'checkConnection', + 'create', + 'edit', + 'delete', + 'doCreate', + 'doUpdate', + 'doDelete' + )); + } + + public function setTemplateCfg($cfg) + { + $this->templateCfg = $cfg; + } + + public function getGuiOpWhiteList() + { + return $this->guiOpWhiteList; + } + + /** + */ + public function initGuiBean(&$argsObj, $caller) + { + $obj = new stdClass(); + $obj->action = $caller; + $obj->typeDomain = $this->mgr->getTypes(); + $obj->canManage = $argsObj->currentUser->hasRight($this->db, + 'reqmgrsystem_management'); + $obj->user_feedback = array( + 'type' => '', + 'message' => '' + ); + + $obj->l18n = init_labels( + array( + 'reqmgrsystem_management' => null, + 'btn_save' => null, + 'create' => null, + 'edit' => null, + 'reqmgrsystem_deleted' => null + )); + + // we experiment on way to get Action Description for GUI using __FUNCTION__ + $obj->l18n['doUpdate'] = $obj->l18n['edit']; + $obj->l18n['doCreate'] = $obj->l18n['create']; + $obj->l18n['doDelete'] = ''; + $obj->main_descr = $obj->l18n['reqmgrsystem_management']; + $obj->action_descr = ucfirst($obj->l18n[$caller]); + + switch ($caller) { + case 'delete': + case 'doDelete': + $obj->submit_button_label = ''; + break; + + default: + $obj->submit_button_label = $obj->l18n['btn_save']; + break; + } + + return $obj; + } + + /** + */ + public function create(&$argsObj, $request, $caller = null) + { + $guiObj = $this->initGuiBean($argsObj, + (is_null($caller) ? __FUNCTION__ : $caller)); + $templateCfg = templateConfiguration('reqMgrSystemEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'reqmgrsystem_management'); + + $guiObj->item = array( + 'id' => 0 + ); + foreach ($this->entitySpec as $property => $type) { + $guiObj->item[$property] = ($type == 'int') ? 0 : ''; + } + return $guiObj; + } + + /** + */ + public function doCreate(&$argsObj, $request) + { + $guiObj = $this->create($argsObj, $request, __FUNCTION__); + + // Checks are centralized on create() + $it = new stdClass(); + foreach ($this->entitySpec as $property => $type) { + $it->$property = $argsObj->$property; + } + + // Save user input. + // This will be useful if create() will fail, to present values again on GUI + $guiObj->item = (array) $it; + + $op = $this->mgr->create($it); + if ($op['status_ok']) { + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->template = "reqMgrSystemView.php"; + } else { + $templateCfg = templateConfiguration('reqMgrSystemEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->user_feedback['message'] = $op['msg']; + } + + return $guiObj; + } + + /* + * function: edit + * + * args: + * + * returns: + * + */ + public function edit(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $templateCfg = templateConfiguration('reqMgrSystemEdit'); + $guiObj->template = $templateCfg->default_template; + + $guiObj->item = $this->mgr->getByID($argsObj->id); + $guiObj->canManage = $argsObj->currentUser->hasRight($this->db, + 'reqmgrsystem_management'); + return $guiObj; + } + + /* + * function: doUpdate + * + * args: + * + * returns: + * + */ + public function doUpdate(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $it = new stdClass(); + $it->id = $argsObj->id; + foreach ($this->entitySpec as $property => $type) { + $it->$property = $argsObj->$property; + } + + // Save user input. + // This will be useful if create() will fail, to present values again on GUI + $guiObj->item = (array) $it; + + $op = $this->mgr->update($it); + if ($op['status_ok']) { + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->template = "reqMgrSystemView.php"; + } else { + $guiObj->user_feedback['message'] = $op['msg']; + $guiObj->template = null; + } + + return $guiObj; + } + + /** + */ + public function doDelete(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + $this->mgr->delete($argsObj->id); + $guiObj->action = 'doDelete'; + $guiObj->template = "reqMgrSystemView.php?"; + return $guiObj; + } + + public function checkConnection(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj, __FUNCTION__); + + $xx = $this->mgr->getByID($argsObj->id); + $class2create = $xx['implementation']; + $its = new $class2create($xx['type'], $xx['cfg']); + + $guiObj->template = "reqMgrSystemView.php?"; + $guiObj->connectionStatus = $its->isConnected() ? 'ok' : 'ko'; + return $guiObj; + } +} +?> diff --git a/lib/reqmgrsystems/reqMgrSystemEdit.php b/lib/reqmgrsystems/reqMgrSystemEdit.php index b7a4142762..8784244d0f 100644 --- a/lib/reqmgrsystems/reqMgrSystemEdit.php +++ b/lib/reqmgrsystems/reqMgrSystemEdit.php @@ -1,181 +1,201 @@ -doAction; -$op = null; -if(method_exists($commandMgr,$pFn)) -{ - $op = $commandMgr->$pFn($args,$_REQUEST); -} -renderGui($db,$args,$gui,$op,$templateCfg); - - - - -/** - */ -function renderGui(&$dbHandler,&$argsObj,$guiObj,$opObj,$templateCfg) -{ - $smartyObj = new TLSmarty(); - $renderType = 'none'; - - // key: gui action - // value: next gui action (used to set value of action button on gui) - $actionOperation = array('create' => 'doCreate', 'edit' => 'doUpdate', - 'doDelete' => '', 'doCreate' => 'doCreate', - 'doUpdate' => 'doUpdate'); - - // Get rendering type and set variable for template - switch($argsObj->doAction) - { - case "edit": - case "create": - case "doDelete": - case "doCreate": - case "doUpdate": - $key2loop = get_object_vars($opObj); - foreach($key2loop as $key => $value) - { - $guiObj->$key = $value; - } - $guiObj->operation = $actionOperation[$argsObj->doAction]; - - $renderType = 'redirect'; - $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; - $pos = strpos($tpl, '.php'); - if($pos === false) - { - $tplDir = (!isset($opObj->template_dir) || is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; - $tpl = $tplDir . $tpl; - $renderType = 'template'; - } - break; - } - - switch($renderType) - { - case 'template': - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($tpl); - break; - - case 'redirect': - header("Location: {$tpl}"); - exit(); - break; - - default: - break; - } -} - -/** - * - */ -function initScript(&$dbHandler) -{ - $mgr = new reqMgrSystemCommands($dbHandler); - $args = init_args(array('doAction' => $mgr->getGuiOpWhiteList())); - $gui = initializeGui($dbHandler,$args,$mgr); - return array($args,$gui,$mgr); -} - -/** - * @return object returns the arguments for the page - */ -function init_args($whiteLists) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - - $iParams = array("id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,20), - "name" => array(tlInputParameter::STRING_N,0,100), - "cfg" => array(tlInputParameter::STRING_N,0,2000), - "type" => array(tlInputParameter::INT_N)); - - R_PARAMS($iParams,$args); - - // sanitize via whitelist - foreach($whiteLists as $inputKey => $allowedValues) - { - if( property_exists($args,$inputKey) ) - { - if( !isset($allowedValues[$args->$inputKey]) ) - { - $msg = "Input parameter $inputKey - white list validation failure - " . - "Value:" . $args->$inputKey . " - " . - "File: " . basename(__FILE__) . " - Function: " . __FUNCTION__ ; - tLog($msg,'ERROR'); - throw new Exception($msg); - } - } - } - - $args->currentUser = $_SESSION['currentUser']; - - return $args; -} - - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,&$commandMgr) -{ - $gui = new stdClass(); - $gui->main_descr = ''; - $gui->action_descr = ''; - $gui->user_feedback = array('type' => '', 'message' => ''); - $gui->mgt_view_events = $argsObj->currentUser->hasRight($dbHandler,'mgt_view_events'); - - // get affected test projects - $gui->testProjectSet = null; - if($argsObj->id > 0) - { - - // just to fix erroneous test project delete - $dummy = $commandMgr->mgr->getLinks($argsObj->id,array('getDeadLinks' => true)); - if( !is_null($dummy) ) - { - foreach($dummy as $key => $elem) - { - $commandMgr->mgr->unlink($argsObj->id,$key); - } - } - - // Now get good info - $gui->testProjectSet = $commandMgr->mgr->getLinks($argsObj->id); - } - return $gui; -} - - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'reqmgrsystem_management'); -} -?> \ No newline at end of file +doAction; +$op = null; +if (method_exists($commandMgr, $pFn)) { + $op = $commandMgr->$pFn($args, $_REQUEST); +} +renderGui($db, $args, $gui, $op, $templateCfg); + +/** + */ +function renderGui(&$dbHandler, &$argsObj, $guiObj, $opObj, $templateCfg) +{ + $smartyObj = new TLSmarty(); + $renderType = 'none'; + + // key: gui action + // value: next gui action (used to set value of action button on gui) + $actionOperation = array( + 'create' => 'doCreate', + 'edit' => 'doUpdate', + 'doDelete' => '', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate' + ); + + // Get rendering type and set variable for template + switch ($argsObj->doAction) { + case "edit": + case "create": + case "doDelete": + case "doCreate": + case "doUpdate": + $key2loop = get_object_vars($opObj); + foreach ($key2loop as $key => $value) { + $guiObj->$key = $value; + } + $guiObj->operation = $actionOperation[$argsObj->doAction]; + + $renderType = 'redirect'; + $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; + $pos = strpos($tpl, '.php'); + if ($pos === false) { + $tplDir = (! isset($opObj->template_dir) || + is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; + $tpl = $tplDir . $tpl; + $renderType = 'template'; + } + break; + } + + switch ($renderType) { + case 'template': + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($tpl); + break; + + case 'redirect': + header("Location: {$tpl}"); + exit(); + break; + + default: + break; + } +} + +/** + */ +function initScript(&$dbHandler) +{ + $mgr = new reqMgrSystemCommands($dbHandler); + $args = initArgs(array( + 'doAction' => $mgr->getGuiOpWhiteList() + )); + $gui = initializeGui($dbHandler, $args, $mgr); + return array( + $args, + $gui, + $mgr + ); +} + +/** + * + * @return object returns the arguments for the page + */ +function initArgs($whiteLists) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + + $iParams = array( + "id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + "name" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "cfg" => array( + tlInputParameter::STRING_N, + 0, + 2000 + ), + "type" => array( + tlInputParameter::INT_N + ) + ); + + R_PARAMS($iParams, $args); + + // sanitize via whitelist + foreach ($whiteLists as $inputKey => $allowedValues) { + if (property_exists($args, $inputKey) && + ! isset($allowedValues[$args->$inputKey])) { + $msg = "Input parameter $inputKey - white list validation failure - " . + "Value:" . $args->$inputKey . " - " . "File: " . + basename(__FILE__) . " - Function: " . __FUNCTION__; + tLog($msg, 'ERROR'); + throw new Exception($msg); + } + } + + $args->currentUser = $_SESSION['currentUser']; + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj, &$commandMgr) +{ + $gui = new stdClass(); + $gui->main_descr = ''; + $gui->action_descr = ''; + $gui->user_feedback = array( + 'type' => '', + 'message' => '' + ); + $gui->mgt_view_events = $argsObj->currentUser->hasRight($dbHandler, + 'mgt_view_events'); + + // get affected test projects + $gui->testProjectSet = null; + if ($argsObj->id > 0) { + + // just to fix erroneous test project delete + $dummy = $commandMgr->mgr->getLinks($argsObj->id, + array( + 'getDeadLinks' => true + )); + if (! is_null($dummy)) { + foreach ($dummy as $key => $elem) { + $commandMgr->mgr->unlink($argsObj->id, $key); + } + } + + // Now get good info + $gui->testProjectSet = $commandMgr->mgr->getLinks($argsObj->id); + } + return $gui; +} + +/** + * + * @param $db resource + * the database connection handle + * @param tlUser $user + * the current active user + * + * @return boolean returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'reqmgrsystem_management'); +} +?> diff --git a/lib/reqmgrsystems/reqMgrSystemView.php b/lib/reqmgrsystems/reqMgrSystemView.php index c719ab350b..4774914fa1 100644 --- a/lib/reqmgrsystems/reqMgrSystemView.php +++ b/lib/reqmgrsystems/reqMgrSystemView.php @@ -1,72 +1,79 @@ -items = $mgr->getAll(array('output' => 'add_link_count', 'checkEnv' => true)); -$gui->canManage = $args->currentUser->hasRight($db,"reqmgrsystem_management"); -$gui->user_feedback = $args->user_feedback; - -if($args->id > 0) -{ - $gui->items[$args->id]['connection_status'] = $mgr->checkConnection($args->id) ? 'ok' : 'ko'; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - - - - - - -*/ - - -/** - * @return object returns the arguments for the page - */ -function init_args() -{ - $args = new stdClass(); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - - if( $args->tproject_id == 0 ) - { - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_SESSION['tproject_id']) : 0; - } - $args->currentUser = $_SESSION['currentUser']; - - $args->user_feedback = array('type' => '', 'message' => ''); - $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; - return $args; -} - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"reqmgrsystem_view") || $user->hasRight($db,"reqmgrsystem_management"); -} -?> \ No newline at end of file +items = $mgr->getAll( + array( + 'output' => 'add_link_count', + 'checkEnv' => true + )); +$gui->canManage = $args->currentUser->hasRight($db, "reqmgrsystem_management"); +$gui->user_feedback = $args->user_feedback; + +if ($args->id > 0) { + $gui->items[$args->id]['connection_status'] = $mgr->checkConnection( + $args->id) ? 'ok' : 'ko'; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * + * + * + * + * + * + */ + +/** + * + * @return object returns the arguments for the page + */ +function initArgs() +{ + $args = new stdClass(); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + + if ($args->tproject_id == 0) { + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_SESSION['tproject_id']) : 0; + } + $args->currentUser = $_SESSION['currentUser']; + + $args->user_feedback = array( + 'type' => '', + 'message' => '' + ); + $args->id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0; + return $args; +} + +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "reqmgrsystem_view") || + $user->hasRight($db, "reqmgrsystem_management"); +} +?> diff --git a/lib/requirements/reqCommands.class.php b/lib/requirements/reqCommands.class.php index d722310e20..a2e6fe935b 100644 --- a/lib/requirements/reqCommands.class.php +++ b/lib/requirements/reqCommands.class.php @@ -1,1035 +1,1088 @@ -db=$db; - $this->reqSpecMgr = new requirement_spec_mgr($db); - $this->reqMgr = new requirement_mgr($db); - - $this->reqCfg = config_get('req_cfg'); - $this->reqStatusDomain = init_labels($this->reqCfg->status_labels); - $this->reqTypeDomain = init_labels($this->reqCfg->type_labels); - $this->reqRelationTypeDescr = init_labels($this->reqCfg->rel_type_description); - - $type_ec = $this->reqCfg->type_expected_coverage; - $this->attrCfg = array(); - $this->attrCfg['expected_coverage'] = array(); - foreach($this->reqTypeDomain as $type_code => $dummy) { - // Because it has to be used on Smarty Template, I choose to transform - // TRUE -> 1, FALSE -> 0, because I've had problems using true/false - $value = isset($type_ec[$type_code]) ? ($type_ec[$type_code] ? 1 : 0) : 1; - $this->attrCfg['expected_coverage'][$type_code] = $value; - } - } - - /** - * common properties needed on gui - * - */ - function initGuiBean( $argsObj = null ) { - - $obj = new stdClass(); - $obj->pageTitle = ''; - $obj->bodyOnLoad = ''; - $obj->bodyOnUnload = "storeWindowSize('ReqPopup');"; - $obj->hilite_item_name = false; - $obj->display_path = false; - $obj->show_match_count = false; - $obj->match_count = 0; - $obj->main_descr = ''; - $obj->action_descr = ''; - $obj->cfields = null; - $obj->template = ''; - $obj->submit_button_label = ''; - $obj->reqStatusDomain = $this->reqStatusDomain; - $obj->reqTypeDomain = $this->reqTypeDomain; - $obj->attrCfg = $this->attrCfg; - - $obj->reqHasBeenDeleted = false; - $obj->req_spec_id = null; - $obj->req_id = null; - $obj->req_version_id = null; - $obj->req = null; - $obj->expected_coverage = 0; - - $obj->suggest_revision = false; - $obj->prompt_for_log = false; - // do not do this -> will desctroy webeditor - // $obj->scope = ''; - // $obj->refreshTree = 0; - - $obj->req_cfg = config_get('req_cfg'); - $obj->glueChar = config_get('testcase_cfg')->glue_character; - $obj->pieceSep = config_get('gui_title_separator_1'); - - $obj->req_id = 0; - $obj->canAddCoverage = true; - if( null != $argsObj ) { - $obj->refreshTree = $argsObj->refreshTree; - $obj->tproject_name = $argsObj->tproject_name; - $obj->showAllVersions = $argsObj->showAllVersions; - $obj->user_feedback = $argsObj->user_feedback; - $obj->req_version_id = $argsObj->req_version_id; - - $obj->reqVersionIDFromCaller = $obj->req_version_id; - - if( property_exists($argsObj, 'req_id') ) { - $obj->req_id = $argsObj->req_id; - } - - /* if wanted, show only the given version */ - if( $obj->showAllVersions ) { - $obj->version_option = requirement_mgr::ALL_VERSIONS; - } else { - $obj->version_option = $argsObj->req_version_id ? $argsObj->req_version_id : requirement_mgr::ALL_VERSIONS; - $obj->version_option = intval($obj->version_option); - } - - // In order to enable/disable Coverage Manage for version - // we need to understand if this is latest version. - $obj->canAddCoverage = true; - if( $obj->version_option != requirement_mgr::ALL_VERSIONS ) { - $nuOpt = array('output' => 'id'); - $nu = $this->reqMgr->get_last_version_info($obj->req_id, $nuOpt); - $obj->canAddCoverage = ($nu['id'] == $obj->req_version_id); - } - } - $obj->requirement_id = $obj->req_id; - - - - $obj->fileUploadMsg = ''; - $obj->import_limit = TL_REPOSITORY_MAXFILESIZE; - - $reqEdCfg = getWebEditorCfg('requirement'); - $obj->reqEditorType = $reqEdCfg['type']; - - - return $obj; - } - - /* - function: create - - args: - - returns: - - */ - function create(&$argsObj,$request) - { - $obj = $this->initGuiBean(); - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - - $obj->main_descr = lang_get('req_spec_short') . TITLE_SEP . $req_spec['title']; - $obj->action_descr = lang_get('create_req'); - - $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id); - $obj->template = 'reqEdit.tpl'; - $obj->submit_button_label = lang_get('btn_save'); - $obj->reqStatusDomain = $this->reqStatusDomain; - $obj->reqTypeDomain = $this->reqTypeDomain; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->req_id = null; - $obj->req = null; - $obj->expected_coverage = 1; - - // set a default value other than informational for type, - // so the "expected coverage" field is showing for new req - $obj->preSelectedType = 0; - if (defined('TL_REQ_TYPE_USE_CASE') && isset($obj->reqTypeDomain[TL_REQ_TYPE_USE_CASE])) { - $obj->preSelectedType = TL_REQ_TYPE_USE_CASE; - } - - $obj->display_path = false; - return $obj; - } - - /* - function: edit - - args: - - @param boolean $overwriteArgs - - returns: - - */ - function edit(&$argsObj,$request,$overwriteArgs=true) - { - $obj = $this->initGuiBean(); - $obj->display_path = false; - $obj->req = $this->reqMgr->get_by_id($argsObj->req_id,$argsObj->req_version_id); - $obj->req = $obj->req[0]; - if( $overwriteArgs ) - { - $argsObj->scope = $obj->req['scope']; - } - - $obj->main_descr = lang_get('req_short') . TITLE_SEP . $obj->req['req_doc_id'] . " (" . - lang_get('version') . ' ' . $obj->req['version'] . " " . - lang_get('revision') . ' ' . $obj->req['revision'] . - ")" . TITLE_SEP . TITLE_SEP . $obj->req['title']; - - $obj->action_descr = lang_get('edit_req'); - - $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs($argsObj->req_id,$argsObj->req_version_id, - $argsObj->tproject_id); - - - $obj->template = 'reqEdit.tpl'; - $obj->submit_button_label = lang_get('btn_save'); - $obj->reqStatusDomain = $this->reqStatusDomain; - $obj->reqTypeDomain = $this->reqTypeDomain; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->req_id = $argsObj->req_id; - $obj->req_version_id = $argsObj->req_version_id; - $obj->expected_coverage = $argsObj->expected_coverage; - - return $obj; - } - - /* - function: doCreate - - args: - - returns: - - */ - function doCreate(&$argsObj,$request) - { - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - $obj = $this->initGuiBean(); - $obj->display_path = false; - $obj->req = null; - $obj->main_descr = lang_get('req_spec_short') . TITLE_SEP . $req_spec['title']; - $obj->action_descr = lang_get('create_req'); - $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id, null, $request); - - $obj->submit_button_label=lang_get('btn_save'); - $obj->template = null; - $obj->reqStatusDomain=$this->reqStatusDomain; - $obj->reqTypeDomain = $this->reqTypeDomain; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->expected_coverage = $argsObj->expected_coverage; - - // manage new order - $order = 0; - $nt2exclude = array('testplan' => 'exclude_me','testsuite'=> 'exclude_me', - 'testcase'=> 'exclude_me'); - $siblings = $this->reqMgr->tree_mgr->get_children($argsObj->req_spec_id,$nt2exclude); - if( !is_null($siblings) ) - { - $dummy = end($siblings); - $order = $dummy['node_order']+1; - } - $ret = $this->reqMgr->create($argsObj->req_spec_id,$argsObj->reqDocId,$argsObj->title, - $argsObj->scope,$argsObj->user_id,$argsObj->reqStatus, - $argsObj->reqType,$argsObj->expected_coverage,$order); - - $obj->user_feedback = $ret['msg']; - if($ret['status_ok']) - { - logAuditEvent(TLS("audit_requirement_created",$argsObj->reqDocId),"CREATE",$ret['id'],"requirements"); - $obj->user_feedback = sprintf(lang_get('req_created'),$argsObj->reqDocId,$argsObj->title); - - $cf_map = $this->reqMgr->get_linked_cfields(null,null,$argsObj->tproject_id); - $this->reqMgr->values_to_db($request,$ret['version_id'],$cf_map); - if($argsObj->stay_here) - { - $obj->template = 'reqEdit.tpl'; - } - else - { - $obj->template = "reqView.php?refreshTree={$argsObj->refreshTree}&requirement_id={$ret['id']}"; - } - $obj->req_id = $ret['id']; - $argsObj->scope = ''; - } - else - { - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->req_version_id = $argsObj->req_version_id; - - $obj->req = array(); - $obj->req['expected_coverage'] = $argsObj->expected_coverage; - $obj->req['title'] = $argsObj->title; - $obj->req['status'] = $argsObj->reqStatus; - $obj->req['type'] = $argsObj->reqType; - $obj->req['req_doc_id'] = $argsObj->reqDocId; - } - return $obj; - } - - - /* - function: doUpdate - - args: - - returns: - - */ - function doUpdate(&$argsObj,$request) - { - $obj = $this->initGuiBean(); - $descr_prefix = lang_get('req') . TITLE_SEP; - $ret['msg'] = null; - - // Before Update want to understand what has changed regarding previous version/revision - $oldData = $this->reqMgr->get_by_id($argsObj->req_id,$argsObj->req_version_id); - $oldCFields = $this->reqMgr->get_linked_cfields(null,$argsObj->req_version_id,$argsObj->tproject_id); - - - $cf_map = $this->reqMgr->get_linked_cfields(null,null,$argsObj->tproject_id); - $newCFields = $this->reqMgr->cfield_mgr->_build_cfield($request,$cf_map); - - $diff = $this->simpleCompare($oldData[0],$argsObj,$oldCFields,$newCFields); - - $obj = $this->edit($argsObj,null,!self::OVERWRITESCOPE); - $obj->user_feedback = ''; - $obj->template = null; - $obj->suggest_revision = false; - - $createRev = false; - if($diff['force'] && !$argsObj->do_save) - { - $obj->prompt_for_log = true; - - // Need Change several values with user input data, to match logic on - // reqEdit.php - renderGui() - $map = array('status' => 'reqStatus', 'type' => 'reqType','scope' => 'scope', - 'expected_coverage' => 'expected_coverage', - 'req_doc_id'=> 'reqDocId', 'title' => 'title'); - - foreach($map as $k => $w) - { - $obj->req[$k] = $argsObj->$w; - } - - // Need to preserve Custom Fields values filled in by user - $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id, null, $request); - - } - else if( $diff['nochange'] || ( ($createRev = $diff['force'] && !$obj->prompt_for_log) || $argsObj->do_save ) ) - { - if( $argsObj->do_save == 1) - { - $createRev = ($argsObj->save_rev == 1); - } - - $ret = $this->reqMgr->update($argsObj->req_id,$argsObj->req_version_id, - trim($argsObj->reqDocId),$argsObj->title, - $argsObj->scope,$argsObj->user_id,$argsObj->reqStatus, - $argsObj->reqType,$argsObj->expected_coverage, - null,null,0,$createRev,$argsObj->log_message); - - $obj->user_feedback = $ret['msg']; - $obj->template = null; - - if($ret['status_ok']) - { - $obj->main_descr = ''; - $obj->action_descr = ''; - $obj->template = "reqView.php?refreshTree={$argsObj->refreshTree}&requirement_id={$argsObj->req_id}"; - - $this->reqMgr->values_to_db($request,$argsObj->req_version_id,$cf_map); - - logAuditEvent(TLS("audit_requirement_saved",$argsObj->reqDocId),"SAVE",$argsObj->req_id,"requirements"); - - $obj->refreshTree = $argsObj->refreshTree; - } - else - { - // Action has failed => no change done on DB. - $old = $this->reqMgr->get_by_id($argsObj->req_id,$argsObj->req_version_id); - $obj->main_descr = $descr_prefix . $old['title']; - $obj->cfields = $this->reqMgr->html_table_of_custom_field_values($argsObj->req_id,$argsObj->req_version_id, - $argsObj->tproject_id); - } - } - else if( $diff['suggest'] ) - { - $obj->suggest_revision = true; - } - return $obj; - } - - /** - * - * - */ - function doDelete(&$argsObj,$request) { - $obj = $this->initGuiBean(); - $obj->display_path = false; - $reqVersionSet = $this->reqMgr->get_by_id($argsObj->req_id); - $req = current($reqVersionSet); - - $this->reqMgr->setNotifyOn(array('delete'=> true) ); - $this->reqMgr->delete($argsObj->req_id,requirement_mgr::ALL_VERSIONS,$argsObj->user_id); - - logAuditEvent(TLS("audit_requirement_deleted",$req['req_doc_id']),"DELETE",$argsObj->req_id,"requirements"); - - $obj->template = 'show_message.tpl'; - $obj->template_dir = ''; - $obj->user_feedback = sprintf(lang_get('req_deleted'),$req['req_doc_id'],$req['title']); - $obj->main_descr=lang_get('requirement') . TITLE_SEP . $req['title']; - $obj->title=lang_get('delete_req'); - $obj->refreshTree = 1; - $obj->result = 'ok'; // needed to enable refresh_tree logic - $obj->refreshTree = $argsObj->refreshTree; - return $obj; - } - - - /** - * - * - */ - function doUnfreezeVersion(&$argsObj,$request) - { - $obj = $this->initGuiBean(); - $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info($argsObj->req_version_id); - $req_version = $this->reqMgr->get_by_id($node['parent_id'],$argsObj->req_version_id); - $req_version = $req_version[0]; - - $this->reqMgr->updateOpen($req_version['version_id'], true); - logAuditEvent(TLS("audit_req_version_unfrozen",$req_version['version'], - $req_version['req_doc_id'],$req_version['title']), - "UNFREEZE",$argsObj->req_version_id,"req_version"); - - $obj->template = 'show_message.tpl'; - $obj->template_dir = ''; - - $obj->user_feedback = sprintf(lang_get('req_version_unfrozen'),$req_version['req_doc_id'], - $req_version['title'],$req_version['version']); - - $obj->main_descr = lang_get('requirement') . TITLE_SEP . $req_version['title']; - $obj->title = lang_get('unfreeze_req'); - $obj->refreshTree = 0; - $obj->result = 'ok'; // needed to enable refresh_tree logic - return $obj; - } - - - /** - * - */ - function reorder(&$argsObj,$request) - { - $obj = $this->initGuiBean(); - - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - $all_reqs = $this->reqSpecMgr->get_requirements($argsObj->req_spec_id); - - $obj->template = 'reqReorder.tpl'; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->req_spec_name = $req_spec['title']; - $obj->all_reqs = $all_reqs; - $obj->main_descr = lang_get('req') . TITLE_SEP . $obj->req_spec_name; - - return $obj; - } - - - /** - * - */ - function doReorder(&$argsObj,$request) - { - $obj = $this->initGuiBean(); - $obj->template = 'reqSpecView.tpl'; - $nodes_in_order = transform_nodes_order($argsObj->nodes_order); - - // need to remove first element, is req_spec_id - $req_spec_id = array_shift($nodes_in_order); - $this->reqMgr->set_order($nodes_in_order); - - $obj->req_spec = $this->reqSpecMgr->get_by_id($req_spec_id); - $obj->refreshTree = 1; - - return $obj; - } - - /** - * - * - */ - function createTestCases(&$argsObj,$request) - { - $guiObj = $this->initGuiBean(); - $guiObj->template = 'reqCreateTestCases.tpl'; - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - $guiObj->main_descr = lang_get('req_spec_short') . TITLE_SEP . $req_spec['title']; - $guiObj->action_descr = lang_get('create_testcase_from_req'); - - $guiObj->req_spec_id = $argsObj->req_spec_id; - $guiObj->req_spec_name = $req_spec['title']; - $guiObj->array_of_msg = ''; - - $guiObj->all_reqs = $this->reqSpecMgr->get_requirements($argsObj->req_spec_id); - - foreach($guiObj->all_reqs as $key => $req) - { - $count = count($this->reqMgr->get_coverage($req['id'])); - $guiObj->all_reqs[$key]['coverage_percent'] = - round(100 / $guiObj->all_reqs[$key]['expected_coverage'] * $count, 2); - $guiObj->all_reqs[$key]['coverage'] = $count; - } - return $guiObj; - } - - /** - * - * - */ - function doCreateTestCases(&$argsObj,$request) - { - $guiObj = $this->initGuiBean(); - $guiObj = $this->createTestCases($argsObj,$request); - $msg = $this->reqMgr->create_tc_from_requirement($argsObj->arrReqIds,$argsObj->req_spec_id, - $argsObj->user_id,$argsObj->tproject_id, - $argsObj->testcase_count); - // need to update results - $guiObj = $this->createTestCases($argsObj,$request); - $guiObj->array_of_msg = $msg; - return $guiObj; - } - - - /** - * - * - */ - function copy(&$argsObj,$request=NULL) - { - $obj = $this->initGuiBean(); - $reqVersionSet = $this->reqMgr->get_by_id($argsObj->req_id); - $req = current($reqVersionSet); - - $obj->items = array($req); - $obj->main_descr = lang_get('req') . TITLE_SEP . $req['title']; - $obj->action_descr = lang_get('copy_one_req'); - $obj->template = 'reqCopy.tpl'; - $obj->containers = null; - $obj->page2call = 'lib/requirements/reqEdit.php'; - $obj->array_of_msg = ''; - $obj->doActionButton = 'doCopy'; - $obj->req_spec_id = $argsObj->req_spec_id; - - $exclude_node_types=array('testplan' => 'exclude_me','testsuite' => 'exclude_me', - 'testcase'=> 'exclude_me','requirement' => 'exclude_me', - 'requirement_spec_revision'=> 'exclude_me'); - - $my['filters'] = array('exclude_node_types' => $exclude_node_types); - $my['options']['order_cfg']['type'] = $my['options']['output'] = 'rspec'; - $subtree = $this->reqMgr->tree_mgr->get_subtree($argsObj->tproject_id,$my['filters'],$my['options']); - if(count($subtree)) - { - $obj->containers = $this->reqMgr->tree_mgr->createHierarchyMap($subtree,'dotted',array('field' => 'doc_id','format' => '%s:')); - } - return $obj; - } - - /** - * - * - */ - function doCopy(&$argsObj,$request) - { - $obj = $this->initGuiBean(); - - $target_req_spec = $this->reqSpecMgr->get_by_id($argsObj->containerID); - $itemID = current($argsObj->itemSet); - $argsObj->req_id = $itemID; - $obj = $this->copy($argsObj); - $obj->req = null; - $obj->req_spec_id = $argsObj->req_spec_id; - - $copyOptions = array('copy_also' => - array('testcase_assignment' => $argsObj->copy_testcase_assignment)); - - $ret = $this->reqMgr->copy_to($itemID,$argsObj->containerID,$argsObj->user_id,$argsObj->tproject_id, - $copyOptions); - $obj->user_feedback = $ret['msg']; - $obj->array_of_msg = ''; - - if($ret['status_ok']) - { - $new_req_version_set = $this->reqMgr->get_by_id($ret['id']); - $new_req = current($new_req_version_set); - - $source_req_version_set = $this->reqMgr->get_by_id($itemID); - $source_req = current($source_req_version_set); - $logMsg = TLS("audit_requirement_copy",$new_req['req_doc_id'],$source_req['req_doc_id']); - logAuditEvent($logMsg,"COPY",$ret['id'],"requirements"); - - - $obj->user_feedback = sprintf(lang_get('req_created'), $new_req['req_doc_id'],$new_req['title']); - $obj->template = 'reqCopy.tpl'; - $obj->req_id = $ret['id']; - $obj->array_of_msg = array($logMsg); - $obj->refreshTree = $argsObj->refreshTree; - } - return $obj; - } - - - /** - * doCreateVersion - * - */ - function doCreateVersion(&$argsObj,$request) { - - $freezeSourceVersion = $this->reqCfg->freezeREQVersionOnNewREQVersion; - - $opt = array('reqVersionID' => $argsObj->req_version_id, - 'log_msg' => $argsObj->log_message, - 'notify' => true, - 'freezeSourceVersion' => $freezeSourceVersion); - - $ret = $this->reqMgr->create_new_version($argsObj->req_id,$argsObj->user_id,$opt); - $obj = $this->initGuiBean(); - $obj->user_feedback = $ret['msg']; - $obj->template = "reqView.php?requirement_id={$argsObj->req_id}"; - $obj->req = null; - $obj->req_id = $argsObj->req_id; - return $obj; - } - - - /** - * - * - */ - function doDeleteVersion(&$argsObj,$request) { - $obj = $this->initGuiBean(); - $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info($argsObj->req_version_id); - $req_version = $this->reqMgr->get_by_id($node['parent_id'],$argsObj->req_version_id); - $req_version = $req_version[0]; - - $this->reqMgr->setNotifyOn(array('delete'=> true) ); - - $this->reqMgr->delete($node['parent_id'],$argsObj->req_version_id,$argsObj->user_id); - - logAuditEvent(TLS("audit_req_version_deleted",$req_version['version'], - $req_version['req_doc_id'],$req_version['title']), - "DELETE",$argsObj->req_version_id,"req_version"); - - $obj->template = 'show_message.tpl'; - $obj->template_dir = ''; - - $obj->user_feedback = sprintf(lang_get('req_version_deleted'),$req_version['req_doc_id'], - $req_version['title'],$req_version['version']); - - $obj->main_descr=lang_get('requirement') . TITLE_SEP . $req_version['title']; - $obj->title=lang_get('delete_req'); - $obj->refreshTree = 0; - $obj->result = 'ok'; // needed to enable refresh_tree logic - return $obj; - } - - - /** - * Add a relation from one requirement to another. - * - * @param stdClass $argsObj input parameters - * @return stdClass $obj - */ - public function doAddRelation($argsObj,$request) - { - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $op = array('ok' => true, 'msg' => lang_get('new_rel_add_success')); - $own_id = $argsObj->relation_source_req_id; - $authorID = $argsObj->user_id; - $tproject_id = $argsObj->tproject_id; - - if (isset($argsObj->relation_destination_testproject_id)) { - // relation destination belongs to another project - $tproject_id = $argsObj->relation_destination_testproject_id; - } - - $other_req = $this->reqMgr->getByDocID($argsObj->relation_destination_req_doc_id, $tproject_id); - if (count($other_req) < 1) { - // req doc ID was not ok - $op['ok'] = false; - $op['msg'] = lang_get('rel_add_error_dest_id'); - } - - if ($op['ok']) { - // are all the IDs we have ok? - $other_req = current($other_req); - - $other_id = $other_req['id']; - $source_id = $own_id; - $destination_id = $other_id; - $relTypeID = (int)current((explode('_',$argsObj->relation_type))); - if( strpos($argsObj->relation_type, "_destination") ) - { - $source_id = $other_id; - $destination_id = $own_id; - } - - if (!is_numeric($authorID) || !is_numeric($source_id) || !is_numeric($destination_id)) { - $op['ok'] = false; - $op['msg'] = lang_get('rel_add_error'); - } - - if ( $op['ok'] && ($source_id == $destination_id)) { - $op['ok'] = false; - $op['msg'] = lang_get('rel_add_error_self'); - } - } - - if ($op['ok']) { - $exists = $this->reqMgr->check_if_relation_exists($source_id, $destination_id, $relTypeID); - if ($exists) { - $op['ok'] = false; - $op['msg'] = sprintf(lang_get('rel_add_error_exists_already'),$this->reqRelationTypeDescr[$relTypeID]); - } - $dest_last_version_info = $this->reqMgr->get_last_version_info($destination_id); - if (!$dest_last_version_info['is_open']) { - $op['ok'] = false; - $op['msg'] = sprintf(lang_get('rel_add_error_dest_frozen')); - } - } - - if ($op['ok']) { - $this->reqMgr->add_relation($source_id, $destination_id, $relTypeID, $authorID); - } - - $obj = $this->initGuiBean(); - $op['msg'] = ($op['ok'] ? '
    ' : '
    ') . $op['msg'] . '
    '; - $obj->template = "reqView.php?requirement_id={$own_id}&relation_add_result_msg=" . $op['msg']; - - return $obj; - } - - - /** - * delete a relation to another requirement - * - * @author Andreas Simon - * - * @param stcClass $argsObj user input data - * - * @return stdClass $object data for template to display - */ - public function doDeleteRelation($argsObj,$request) - { - - $debugMsg = '/* Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__ . ' */'; - $ok_msg = '
    ' . lang_get('delete_rel_success') . '
    '; - $op = array('ok' => true, 'msg' => $ok_msg); - - $relation_id = $argsObj->relation_id; - $requirement_id = $argsObj->requirement_id; - - if (is_null($relation_id) || !is_numeric($relation_id) - || is_null($requirement_id) || !is_numeric($requirement_id)) { - $op['ok'] = false; - $op['msg'] = '
    ' . lang_get('error_deleting_rel') . '
    '; - } - - if ($op['ok']) { - $this->reqMgr->delete_relation($relation_id); - } - - $obj = $this->initGuiBean(); - $obj->template = "reqView.php?requirement_id=$requirement_id&relation_add_result_msg=" . $op['msg']; - - return $obj; - } - - /* - function: doCreateRevision - - args: - - returns: - - @internal revisions - - */ - function doCreateRevision(&$argsObj,$request) - { - $req = $this->reqMgr->get_by_id($argsObj->req_id,$argsObj->req_version_id); - $req = $req[0]; - $ret = $this->reqMgr->create_new_revision($argsObj->req_version_id,$argsObj->user_id, - $argsObj->tproject_id,$req,$argsObj->log_message); - - $obj = $this->initGuiBean(); - $obj->user_feedback = $ret['msg']; - $obj->template = "reqView.php?requirement_id={$argsObj->req_id}"; - $obj->req = null; - $obj->req_id = $argsObj->req_id; - return $obj; - } - - - - - - /** - * - * - */ - function simpleCompare($old,$new,$oldCF,$newCF) - { - - $suggest_revision = array('scope' => 'scope'); - - $force_revision = array('status' => 'reqStatus', 'type' => 'reqType', - 'expected_coverage' => 'expected_coverage', - 'req_doc_id'=> 'reqDocId', 'title' => 'title'); - - - $ret = array('force' => false, 'suggest' => false, 'nochange' => false, 'changeon' => null); - foreach($force_revision as $access_key => $access_prop) - { - if( $ret['force'] = ($old[$access_key] != $new->$access_prop) ) - { - $ret['changeon'] = 'attribute:' . $access_key; - break; - } - } - - if( !$ret['force'] ) - { - if( !is_null($newCF) ) - { - foreach($newCF as $cf_key => $cf) - { - if( $ret['force'] = ($oldCF[$cf_key]['value'] != $cf['cf_value']) ) - { - $ret['changeon'] = 'custom field:' . $oldCF[$cf_key]['name']; - break; - } - } - } - } - - if( !$ret['force'] ) - { - - foreach($suggest_revision as $access_key => $access_prop) - { - if( $ret['suggest'] = ($old[$access_key] != $new->$access_prop) ) - { - $ret['changeon'] = 'attribute:' . $access_key; - break; - } - } - - } - $ret['nochange'] = ($ret['force'] == false && $ret['suggest'] == false); - return $ret; - } - - - /** - * - * - */ - function doFreezeVersion(&$argsObj,$request) - { - $obj = $this->initGuiBean(); - $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info($argsObj->req_version_id); - $req_version = $this->reqMgr->get_by_id($node['parent_id'],$argsObj->req_version_id); - $req_version = $req_version[0]; - - $this->reqMgr->updateOpen($req_version['version_id'], false); - - // BUGID 3312 - logAuditEvent(TLS("audit_req_version_frozen",$req_version['version'], - $req_version['req_doc_id'],$req_version['title']), - "FREEZE",$argsObj->req_version_id,"req_version"); - - $obj->template = 'show_message.tpl'; - $obj->template_dir = ''; - - $obj->user_feedback = sprintf(lang_get('req_version_frozen'),$req_version['req_doc_id'], - $req_version['title'],$req_version['version']); - - $obj->main_descr=lang_get('requirement') . TITLE_SEP . $req_version['title']; - $obj->title=lang_get('freeze_req'); - $obj->refreshTree = 0; - $obj->result = 'ok'; // needed to enable refresh_tree logic - return $obj; - } - - /** - * - */ - function addTestCase(&$argsObj,$request) { - - $obj = $this->initGuiBean(); - $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info($argsObj->req_version_id); - $dummy = $this->reqMgr->get_by_id($node['parent_id'],$argsObj->req_version_id); - $req_version = $dummy[0]; - - $obj->req = $req_version; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->req_version_id = $argsObj->req_version_id; - $obj->template = "reqView.php?refreshTree=0&requirement_id={$argsObj->req_id}"; - - // Analise test case identity - $tcaseCfg = config_get('testcase_cfg'); - - $status_ok = false; - $msg = sprintf(lang_get('provide_full_external_tcase_id'),$argsObj->tcasePrefix, $tcaseCfg->glue_character); - $gluePos = strrpos($argsObj->tcaseIdentity, $tcaseCfg->glue_character); - - $isFullExternal = ($gluePos !== false); - if($isFullExternal) { - $status_ok = true; - $rawTestCasePrefix = substr($argsObj->tcaseIdentity, 0, $gluePos); - - $status_ok = (strcmp($rawTestCasePrefix,$argsObj->tcasePrefix) == 0); - if(!$status_ok) { - $msg = sprintf(lang_get('seems_to_belong_to_other_tproject'),$rawTestCasePrefix,$argsObj->tcasePrefix); - } - } - - if($status_ok) { - // IMPORTANT NOTICE: audit info is managed on reqMgr method - $alienMgr = new testcase($this->db); - $tcase_id = $alienMgr->getInternalID($argsObj->tcaseIdentity,array('tproject_id' => $argsObj->tproject_id)); - - // Design Choice - // 1. Only latest test case version will be added - // 2. Only if not executed - if($tcase_id > 0) { - - $doLink = true; - if( $tcaseCfg->reqLinkingDisabledAfterExec ) { - if( $alienMgr->latestVersionHasBeenExecuted($tcase_id) == 0) { - $doLink = true; - } else { - $doLink = false; - $status_ok = false; - $msg = sprintf(lang_get('cannot_link_latest_version_reason_has_been_exec'), - $argsObj->tcaseIdentity); - } - } - if( $doLink ) { - $this->reqMgr->assign_to_tcase($argsObj->req_id,$tcase_id,intval($argsObj->user_id)); - } - - } else { - $status_ok = false; - $msg = sprintf(lang_get('tcase_doesnot_exist'),$argsObj->tcaseIdentity); - } - } - - - if(!$status_ok) { - $obj->user_feedback = $msg; - $obj->template .= "&user_feedback=" . urlencode($obj->user_feedback); - } - - return $obj; - } - - /** - * - */ - function removeTestCase(&$argsObj,$request) { - // IMPORTANT NOTICE: audit info is managed on reqMgr method - $obj = $this->initGuiBean(); - $bond = array('req' => $argsObj->req_version_id, 'tc' => $argsObj->tcaseIdentity); - - $this->reqMgr->delReqVersionTCVersionLink($bond,__METHOD__); - - $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info($argsObj->req_version_id); - $dummy = $this->reqMgr->get_by_id($node['parent_id'],$argsObj->req_version_id); - $req_version = $dummy[0]; - - $obj->req = $req_version; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->req_version_id = $argsObj->req_version_id; - $obj->template = "reqView.php?refreshTree=0&requirement_id={$argsObj->req_id}"; - return $obj; - } - - /** - * - */ - function fileUpload(&$argsObj,$request) { - $argsObj->uploadOp = fileUploadManagement($this->db,$argsObj->req_version_id, - $argsObj->fileTitle,$this->reqMgr->getAttachmentTableName()); - - return $this->initGuiObjForAttachmentOperations($argsObj); - } - - /** - * - */ - function deleteFile(&$argsObj) { - $fileInfo = deleteAttachment($this->db,$argsObj->file_id,false); - if( $argsObj->req_version_id == 0 ) { - $argsObj->req_version_id = $fileInfo['fk_id']; - } - - return $this->initGuiObjForAttachmentOperations($argsObj); - } - - - /** - * - */ - private function initGuiObjForAttachmentOperations($argsObj) { - $guiObj = new stdClass(); - $guiObj->reqHasBeenDeleted = false; - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->req_id = $argsObj->req_id; - $guiObj->suggest_revision = $guiObj->prompt_for_log = false; - $guiObj->template = "reqView.php?refreshTree=0&requirement_id={$argsObj->req_id}"; - $guiObj->uploadOp = $argsObj->uploadOp; - - return $guiObj; - } - - /** - * - */ - function stopMonitoring(&$argsObj,$request) - { - $this->reqMgr->monitorOff($argsObj->req_id,$argsObj->user_id,$argsObj->tproject_id); - - return $this->initGuiObjForAttachmentOperations($argsObj); - } - - /** - * - */ - function startMonitoring(&$argsObj,$request) - { - $this->reqMgr->monitorOn($argsObj->req_id,$argsObj->user_id,$argsObj->tproject_id); - - return $this->initGuiObjForAttachmentOperations($argsObj); - } - -} \ No newline at end of file +db = $db; + $this->reqSpecMgr = new requirement_spec_mgr($db); + $this->reqMgr = new requirement_mgr($db); + + $this->reqCfg = config_get('req_cfg'); + $this->reqStatusDomain = init_labels($this->reqCfg->status_labels); + $this->reqTypeDomain = init_labels($this->reqCfg->type_labels); + $this->reqRelationTypeDescr = init_labels( + $this->reqCfg->rel_type_description); + + $type_ec = $this->reqCfg->type_expected_coverage; + $this->attrCfg = array(); + $this->attrCfg['expected_coverage'] = array(); + foreach ($this->reqTypeDomain as $type_code => $dummy) { + // Because it has to be used on Smarty Template, I choose to transform + // TRUE -> 1, FALSE -> 0, because I've had problems using true/false + $value = isset($type_ec[$type_code]) ? ($type_ec[$type_code] ? 1 : 0) : 1; + $this->attrCfg['expected_coverage'][$type_code] = $value; + } + } + + /** + * common properties needed on gui + */ + public function initGuiBean($argsObj = null) + { + $obj = new stdClass(); + $obj->pageTitle = ''; + $obj->bodyOnLoad = ''; + $obj->bodyOnUnload = "storeWindowSize('ReqPopup');"; + $obj->hilite_item_name = false; + $obj->display_path = false; + $obj->show_match_count = false; + $obj->match_count = 0; + $obj->main_descr = ''; + $obj->action_descr = ''; + $obj->cfields = null; + $obj->template = ''; + $obj->submit_button_label = ''; + $obj->reqStatusDomain = $this->reqStatusDomain; + $obj->reqTypeDomain = $this->reqTypeDomain; + $obj->attrCfg = $this->attrCfg; + + $obj->reqHasBeenDeleted = false; + $obj->req_spec_id = null; + $obj->req_id = null; + $obj->req_version_id = null; + $obj->req = null; + $obj->expected_coverage = 0; + + $obj->suggest_revision = false; + $obj->prompt_for_log = false; + + $obj->req_cfg = config_get('req_cfg'); + $obj->glueChar = config_get('testcase_cfg')->glue_character; + $obj->pieceSep = config_get('gui_title_separator_1'); + + $obj->req_id = 0; + $obj->canAddCoverage = true; + if (! empty($argsObj)) { + $obj->refreshTree = $argsObj->refreshTree; + $obj->tproject_name = $argsObj->tproject_name; + $obj->showAllVersions = $argsObj->showAllVersions; + $obj->user_feedback = $argsObj->user_feedback; + $obj->req_version_id = $argsObj->req_version_id; + + $obj->reqVersionIDFromCaller = $obj->req_version_id; + + if (property_exists($argsObj, 'req_id')) { + $obj->req_id = $argsObj->req_id; + } + + /* if wanted, show only the given version */ + if ($obj->showAllVersions) { + $obj->version_option = requirement_mgr::ALL_VERSIONS; + } else { + $obj->version_option = $argsObj->req_version_id ? $argsObj->req_version_id : requirement_mgr::ALL_VERSIONS; + $obj->version_option = intval($obj->version_option); + } + + // In order to enable/disable Coverage Manage for version + // we need to understand if this is latest version. + $obj->canAddCoverage = true; + if ($obj->version_option != requirement_mgr::ALL_VERSIONS) { + $nuOpt = array( + 'output' => 'id' + ); + $nu = $this->reqMgr->getLastVersionInfo($obj->req_id, $nuOpt); + $obj->canAddCoverage = ($nu['id'] == $obj->req_version_id); + } + } + $obj->requirement_id = $obj->req_id; + + $obj->fileUploadMsg = ''; + $obj->import_limit = TL_REPOSITORY_MAXFILESIZE; + + $reqEdCfg = getWebEditorCfg('requirement'); + $obj->reqEditorType = $reqEdCfg['type']; + + return $obj; + } + + /* + * function: create + * + * args: + * + * returns: + * + */ + public function create(&$argsObj) + { + $obj = $this->initGuiBean(); + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + + $obj->main_descr = lang_get('req_spec_short') . TITLE_SEP . + $req_spec['title']; + $obj->action_descr = lang_get('create_req'); + + $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs(null, + null, $argsObj->tproject_id); + $obj->template = 'reqEdit.tpl'; + $obj->submit_button_label = lang_get('btn_save'); + $obj->reqStatusDomain = $this->reqStatusDomain; + $obj->reqTypeDomain = $this->reqTypeDomain; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->req_id = null; + $obj->req = null; + $obj->expected_coverage = 1; + + // set a default value other than informational for type, + // so the "expected coverage" field is showing for new req + $obj->preSelectedType = 0; + if (defined('TL_REQ_TYPE_USE_CASE') && + isset($obj->reqTypeDomain[TL_REQ_TYPE_USE_CASE])) { + $obj->preSelectedType = TL_REQ_TYPE_USE_CASE; + } + + $obj->display_path = false; + return $obj; + } + + /* + * function: edit + * + * args: + * + * @param boolean $overwriteArgs + * + * returns: + * + */ + public function edit(&$argsObj, $overwriteArgs = true) + { + $obj = $this->initGuiBean(); + $obj->display_path = false; + $obj->req = $this->reqMgr->get_by_id($argsObj->req_id, + $argsObj->req_version_id); + $obj->req = $obj->req[0]; + if ($overwriteArgs) { + $argsObj->scope = $obj->req['scope']; + } + + $obj->main_descr = lang_get('req_short') . TITLE_SEP . + $obj->req['req_doc_id'] . " (" . lang_get('version') . ' ' . + $obj->req['version'] . " " . lang_get('revision') . ' ' . + $obj->req['revision'] . ")" . TITLE_SEP . TITLE_SEP . + $obj->req['title']; + + $obj->action_descr = lang_get('edit_req'); + + $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs( + $argsObj->req_id, $argsObj->req_version_id, $argsObj->tproject_id); + + $obj->template = 'reqEdit.tpl'; + $obj->submit_button_label = lang_get('btn_save'); + $obj->reqStatusDomain = $this->reqStatusDomain; + $obj->reqTypeDomain = $this->reqTypeDomain; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->req_id = $argsObj->req_id; + $obj->req_version_id = $argsObj->req_version_id; + $obj->expected_coverage = $argsObj->expected_coverage; + + return $obj; + } + + /* + * function: doCreate + * + * args: + * + * returns: + * + */ + public function doCreate(&$argsObj, $request) + { + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + $obj = $this->initGuiBean(); + $obj->display_path = false; + $obj->req = null; + $obj->main_descr = lang_get('req_spec_short') . TITLE_SEP . + $req_spec['title']; + $obj->action_descr = lang_get('create_req'); + $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs(null, + null, $argsObj->tproject_id, null, $request); + + $obj->submit_button_label = lang_get('btn_save'); + $obj->template = null; + $obj->reqStatusDomain = $this->reqStatusDomain; + $obj->reqTypeDomain = $this->reqTypeDomain; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->expected_coverage = $argsObj->expected_coverage; + + // manage new order + $order = 0; + $nt2exclude = array( + 'testplan' => 'exclude_me', + 'testsuite' => 'exclude_me', + 'testcase' => 'exclude_me' + ); + $siblings = $this->reqMgr->tree_mgr->get_children($argsObj->req_spec_id, + $nt2exclude); + if (! is_null($siblings)) { + $dummy = end($siblings); + $order = $dummy['node_order'] + 1; + } + $ret = $this->reqMgr->create($argsObj->req_spec_id, $argsObj->reqDocId, + $argsObj->title, $argsObj->scope, $argsObj->user_id, + $argsObj->reqStatus, $argsObj->reqType, $argsObj->expected_coverage, + $order); + + $obj->user_feedback = $ret['msg']; + if ($ret['status_ok']) { + logAuditEvent(TLS("audit_requirement_created", $argsObj->reqDocId), + "CREATE", $ret['id'], "requirements"); + $obj->user_feedback = sprintf(lang_get('req_created'), + $argsObj->reqDocId, $argsObj->title); + + $cf_map = $this->reqMgr->get_linked_cfields(null, null, + $argsObj->tproject_id); + $this->reqMgr->values_to_db($request, $ret['version_id'], $cf_map); + if ($argsObj->stay_here) { + $obj->template = 'reqEdit.tpl'; + } else { + $obj->template = "reqView.php?refreshTree={$argsObj->refreshTree}&requirement_id={$ret['id']}"; + } + $obj->req_id = $ret['id']; + $argsObj->scope = ''; + } else { + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->req_version_id = $argsObj->req_version_id; + + $obj->req = array(); + $obj->req['expected_coverage'] = $argsObj->expected_coverage; + $obj->req['title'] = $argsObj->title; + $obj->req['status'] = $argsObj->reqStatus; + $obj->req['type'] = $argsObj->reqType; + $obj->req['req_doc_id'] = $argsObj->reqDocId; + } + return $obj; + } + + /* + * function: doUpdate + * + * args: + * + * returns: + * + */ + public function doUpdate(&$argsObj, $request) + { + $this->initGuiBean(); + $descr_prefix = lang_get('req') . TITLE_SEP; + $ret['msg'] = null; + + // Before Update want to understand what has changed regarding previous version/revision + $oldData = $this->reqMgr->get_by_id($argsObj->req_id, + $argsObj->req_version_id); + $oldCFields = $this->reqMgr->get_linked_cfields(null, + $argsObj->req_version_id, $argsObj->tproject_id); + + $cf_map = $this->reqMgr->get_linked_cfields(null, null, + $argsObj->tproject_id); + $newCFields = $this->reqMgr->cfield_mgr->_build_cfield($request, $cf_map); + + $diff = $this->simpleCompare($oldData[0], $argsObj, $oldCFields, + $newCFields); + + $obj = $this->edit($argsObj, ! self::OVERWRITESCOPE); + $obj->user_feedback = ''; + $obj->template = null; + $obj->suggest_revision = false; + + $createRev = false; + if ($diff['force'] && ! $argsObj->do_save) { + $obj->prompt_for_log = true; + + // Need Change several values with user input data, to match logic on + // reqEdit.php - renderGui() + $map = array( + 'status' => 'reqStatus', + 'type' => 'reqType', + 'scope' => 'scope', + 'expected_coverage' => 'expected_coverage', + 'req_doc_id' => 'reqDocId', + 'title' => 'title' + ); + + foreach ($map as $k => $w) { + $obj->req[$k] = $argsObj->$w; + } + + // Need to preserve Custom Fields values filled in by user + $obj->cfields = $this->reqMgr->html_table_of_custom_field_inputs( + null, null, $argsObj->tproject_id, null, $request); + } elseif ($diff['nochange'] || + (($createRev = $diff['force'] && ! $obj->prompt_for_log) || + $argsObj->do_save)) { + if ($argsObj->do_save == 1) { + $createRev = ($argsObj->save_rev == 1); + } + + $ret = $this->reqMgr->update($argsObj->req_id, + $argsObj->req_version_id, trim($argsObj->reqDocId), + $argsObj->title, $argsObj->scope, $argsObj->user_id, + $argsObj->reqStatus, $argsObj->reqType, + $argsObj->expected_coverage, null, null, 0, $createRev, + $argsObj->log_message); + + $obj->user_feedback = $ret['msg']; + $obj->template = null; + + if ($ret['status_ok']) { + $obj->main_descr = ''; + $obj->action_descr = ''; + $obj->template = "reqView.php?refreshTree={$argsObj->refreshTree}&requirement_id={$argsObj->req_id}"; + + $this->reqMgr->values_to_db($request, $argsObj->req_version_id, + $cf_map); + + logAuditEvent( + TLS("audit_requirement_saved", $argsObj->reqDocId), "SAVE", + $argsObj->req_id, "requirements"); + + $obj->refreshTree = $argsObj->refreshTree; + } else { + // Action has failed => no change done on DB. + $old = $this->reqMgr->get_by_id($argsObj->req_id, + $argsObj->req_version_id); + $obj->main_descr = $descr_prefix . $old['title']; + $obj->cfields = $this->reqMgr->html_table_of_custom_field_values( + $argsObj->req_id, $argsObj->req_version_id, + $argsObj->tproject_id); + } + } elseif ($diff['suggest']) { + $obj->suggest_revision = true; + } + return $obj; + } + + /** + */ + public function doDelete(&$argsObj) + { + $obj = $this->initGuiBean(); + $obj->display_path = false; + $reqVersionSet = $this->reqMgr->get_by_id($argsObj->req_id); + $req = current($reqVersionSet); + + $this->reqMgr->setNotifyOn(array( + 'delete' => true + )); + $this->reqMgr->delete($argsObj->req_id, requirement_mgr::ALL_VERSIONS, + $argsObj->user_id); + + logAuditEvent(TLS("audit_requirement_deleted", $req['req_doc_id']), + "DELETE", $argsObj->req_id, "requirements"); + + $obj->template = 'show_message.tpl'; + $obj->template_dir = ''; + $obj->user_feedback = sprintf(lang_get('req_deleted'), + $req['req_doc_id'], $req['title']); + $obj->main_descr = lang_get('requirement') . TITLE_SEP . $req['title']; + $obj->title = lang_get('delete_req'); + $obj->refreshTree = 1; + $obj->result = 'ok'; // needed to enable refresh_tree logic + $obj->refreshTree = $argsObj->refreshTree; + return $obj; + } + + /** + */ + public function doUnfreezeVersion(&$argsObj) + { + $obj = $this->initGuiBean(); + $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info( + $argsObj->req_version_id); + $req_version = $this->reqMgr->get_by_id($node['parent_id'], + $argsObj->req_version_id); + $req_version = $req_version[0]; + + $this->reqMgr->updateOpen($req_version['version_id'], true); + logAuditEvent( + TLS("audit_req_version_unfrozen", $req_version['version'], + $req_version['req_doc_id'], $req_version['title']), "UNFREEZE", + $argsObj->req_version_id, "req_version"); + + $obj->template = 'show_message.tpl'; + $obj->template_dir = ''; + + $obj->user_feedback = sprintf(lang_get('req_version_unfrozen'), + $req_version['req_doc_id'], $req_version['title'], + $req_version['version']); + + $obj->main_descr = lang_get('requirement') . TITLE_SEP . + $req_version['title']; + $obj->title = lang_get('unfreeze_req'); + $obj->refreshTree = 0; + $obj->result = 'ok'; // needed to enable refresh_tree logic + return $obj; + } + + /** + */ + private function reorder(&$argsObj) + { + $obj = $this->initGuiBean(); + + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + $all_reqs = $this->reqSpecMgr->get_requirements($argsObj->req_spec_id); + + $obj->template = 'reqReorder.tpl'; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->req_spec_name = $req_spec['title']; + $obj->all_reqs = $all_reqs; + $obj->main_descr = lang_get('req') . TITLE_SEP . $obj->req_spec_name; + + return $obj; + } + + /** + */ + public function doReorder(&$argsObj) + { + $obj = $this->initGuiBean(); + $obj->template = 'reqSpecView.tpl'; + $nodes_in_order = transform_nodes_order($argsObj->nodes_order); + + // need to remove first element, is req_spec_id + $req_spec_id = array_shift($nodes_in_order); + $this->reqMgr->set_order($nodes_in_order); + + $obj->req_spec = $this->reqSpecMgr->get_by_id($req_spec_id); + $obj->refreshTree = 1; + + return $obj; + } + + /** + */ + private function createTestCases(&$argsObj) + { + $guiObj = $this->initGuiBean(); + $guiObj->template = 'reqCreateTestCases.tpl'; + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + $guiObj->main_descr = lang_get('req_spec_short') . TITLE_SEP . + $req_spec['title']; + $guiObj->action_descr = lang_get('create_testcase_from_req'); + + $guiObj->req_spec_id = $argsObj->req_spec_id; + $guiObj->req_spec_name = $req_spec['title']; + $guiObj->array_of_msg = ''; + + $guiObj->all_reqs = $this->reqSpecMgr->get_requirements( + $argsObj->req_spec_id); + + foreach ($guiObj->all_reqs as $key => $req) { + $count = count($this->reqMgr->get_coverage($req['id'])); + $guiObj->all_reqs[$key]['coverage_percent'] = round( + 100 / $guiObj->all_reqs[$key]['expected_coverage'] * $count, 2); + $guiObj->all_reqs[$key]['coverage'] = $count; + } + return $guiObj; + } + + /** + */ + public function doCreateTestCases(&$argsObj) + { + $this->initGuiBean(); + $this->createTestCases($argsObj); + $msg = $this->reqMgr->create_tc_from_requirement($argsObj->arrReqIds, + $argsObj->req_spec_id, $argsObj->user_id, $argsObj->tproject_id, + $argsObj->testcase_count); + // need to update results + $guiObj = $this->createTestCases($argsObj); + $guiObj->array_of_msg = $msg; + return $guiObj; + } + + /** + */ + public function copy(&$argsObj) + { + $obj = $this->initGuiBean(); + $reqVersionSet = $this->reqMgr->get_by_id($argsObj->req_id); + $req = current($reqVersionSet); + + $obj->items = array( + $req + ); + $obj->main_descr = lang_get('req') . TITLE_SEP . $req['title']; + $obj->action_descr = lang_get('copy_one_req'); + $obj->template = 'reqCopy.tpl'; + $obj->containers = null; + $obj->page2call = 'lib/requirements/reqEdit.php'; + $obj->array_of_msg = ''; + $obj->doActionButton = 'doCopy'; + $obj->req_spec_id = $argsObj->req_spec_id; + + $exclude_node_types = array( + 'testplan' => 'exclude_me', + 'testsuite' => 'exclude_me', + 'testcase' => 'exclude_me', + 'requirement' => 'exclude_me', + 'requirement_spec_revision' => 'exclude_me' + ); + + $my['filters'] = array( + 'exclude_node_types' => $exclude_node_types + ); + $my['options']['order_cfg']['type'] = $my['options']['output'] = 'rspec'; + $subtree = $this->reqMgr->tree_mgr->get_subtree($argsObj->tproject_id, + $my['filters'], $my['options']); + if (count($subtree)) { + $obj->containers = $this->reqMgr->tree_mgr->createHierarchyMap( + $subtree, 'dotted', + array( + 'field' => 'doc_id', + 'format' => '%s:' + )); + } + return $obj; + } + + /** + */ + public function doCopy(&$argsObj) + { + $this->initGuiBean(); + + $this->reqSpecMgr->get_by_id($argsObj->containerID); + $itemID = current($argsObj->itemSet); + $argsObj->req_id = $itemID; + $obj = $this->copy($argsObj); + $obj->req = null; + $obj->req_spec_id = $argsObj->req_spec_id; + + $copyOptions = array( + 'copy_also' => array( + 'testcase_assignment' => $argsObj->copy_testcase_assignment + ) + ); + + $ret = $this->reqMgr->copy_to($itemID, $argsObj->containerID, + $argsObj->user_id, $argsObj->tproject_id, $copyOptions); + $obj->user_feedback = $ret['msg']; + $obj->array_of_msg = ''; + + if ($ret['status_ok']) { + $new_req_version_set = $this->reqMgr->get_by_id($ret['id']); + $new_req = current($new_req_version_set); + + $source_req_version_set = $this->reqMgr->get_by_id($itemID); + $source_req = current($source_req_version_set); + $logMsg = TLS("audit_requirement_copy", $new_req['req_doc_id'], + $source_req['req_doc_id']); + logAuditEvent($logMsg, "COPY", $ret['id'], "requirements"); + + $obj->user_feedback = sprintf(lang_get('req_created'), + $new_req['req_doc_id'], $new_req['title']); + $obj->template = 'reqCopy.tpl'; + $obj->req_id = $ret['id']; + $obj->array_of_msg = array( + $logMsg + ); + $obj->refreshTree = $argsObj->refreshTree; + } + return $obj; + } + + /** + * doCreateVersion + */ + public function doCreateVersion(&$argsObj) + { + $freezeSourceVersion = $this->reqCfg->freezeREQVersionOnNewREQVersion; + + $opt = array( + 'reqVersionID' => $argsObj->req_version_id, + 'log_msg' => $argsObj->log_message, + 'notify' => true, + 'freezeSourceVersion' => $freezeSourceVersion + ); + + $ret = $this->reqMgr->create_new_version($argsObj->req_id, + $argsObj->user_id, $opt); + $obj = $this->initGuiBean(); + $obj->user_feedback = $ret['msg']; + $obj->template = "reqView.php?requirement_id={$argsObj->req_id}"; + $obj->req = null; + $obj->req_id = $argsObj->req_id; + return $obj; + } + + /** + */ + public function doDeleteVersion(&$argsObj) + { + $obj = $this->initGuiBean(); + $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info( + $argsObj->req_version_id); + $req_version = $this->reqMgr->get_by_id($node['parent_id'], + $argsObj->req_version_id); + $req_version = $req_version[0]; + + $this->reqMgr->setNotifyOn(array( + 'delete' => true + )); + + $this->reqMgr->delete($node['parent_id'], $argsObj->req_version_id, + $argsObj->user_id); + + logAuditEvent( + TLS("audit_req_version_deleted", $req_version['version'], + $req_version['req_doc_id'], $req_version['title']), "DELETE", + $argsObj->req_version_id, "req_version"); + + $obj->template = 'show_message.tpl'; + $obj->template_dir = ''; + + $obj->user_feedback = sprintf(lang_get('req_version_deleted'), + $req_version['req_doc_id'], $req_version['title'], + $req_version['version']); + + $obj->main_descr = lang_get('requirement') . TITLE_SEP . + $req_version['title']; + $obj->title = lang_get('delete_req'); + $obj->refreshTree = 0; + $obj->result = 'ok'; // needed to enable refresh_tree logic + return $obj; + } + + /** + * Add a relation from one requirement to another. + * + * @param stdClass $argsObj + * input parameters + * @return stdClass $obj + */ + public function doAddRelation($argsObj) + { + $op = array( + 'ok' => true, + 'msg' => lang_get('new_rel_add_success') + ); + $own_id = $argsObj->relation_source_req_id; + $authorID = $argsObj->user_id; + $tproject_id = $argsObj->tproject_id; + + if (isset($argsObj->relation_destination_testproject_id)) { + // relation destination belongs to another project + $tproject_id = $argsObj->relation_destination_testproject_id; + } + + $other_req = $this->reqMgr->getByDocID( + $argsObj->relation_destination_req_doc_id, $tproject_id); + if (empty($other_req)) { + // req doc ID was not ok + $op['ok'] = false; + $op['msg'] = lang_get('rel_add_error_dest_id'); + } + + if ($op['ok']) { + // are all the IDs we have ok? + $other_req = current($other_req); + + $other_id = $other_req['id']; + $source_id = $own_id; + $destination_id = $other_id; + $relTypeID = (int) current((explode('_', $argsObj->relation_type))); + if (strpos($argsObj->relation_type, "_destination")) { + $source_id = $other_id; + $destination_id = $own_id; + } + + if (! is_numeric($authorID) || ! is_numeric($source_id) || + ! is_numeric($destination_id)) { + $op['ok'] = false; + $op['msg'] = lang_get('rel_add_error'); + } + + if ($op['ok'] && ($source_id == $destination_id)) { + $op['ok'] = false; + $op['msg'] = lang_get('rel_add_error_self'); + } + } + + if ($op['ok']) { + $exists = $this->reqMgr->check_if_relation_exists($source_id, + $destination_id, $relTypeID); + if ($exists) { + $op['ok'] = false; + $op['msg'] = sprintf(lang_get('rel_add_error_exists_already'), + $this->reqRelationTypeDescr[$relTypeID]); + } + $dest_last_version_info = $this->reqMgr->getLastVersionInfo( + $destination_id); + if (! $dest_last_version_info['is_open']) { + $op['ok'] = false; + $op['msg'] = sprintf(lang_get('rel_add_error_dest_frozen')); + } + } + + if ($op['ok']) { + $this->reqMgr->add_relation($source_id, $destination_id, $relTypeID, + $authorID); + } + + $obj = $this->initGuiBean(); + $op['msg'] = ($op['ok'] ? '
    ' : '
    ') . + $op['msg'] . '
    '; + $obj->template = "reqView.php?requirement_id={$own_id}&relation_add_result_msg=" . + $op['msg']; + + return $obj; + } + + /** + * delete a relation to another requirement + * + * @author Andreas Simon + * + * @param stdClass $argsObj + * user input data + * + * @return stdClass $object data for template to display + */ + public function doDeleteRelation($argsObj) + { + $ok_msg = '
    ' . lang_get('delete_rel_success') . + '
    '; + $op = array( + 'ok' => true, + 'msg' => $ok_msg + ); + + $relation_id = $argsObj->relation_id; + $requirement_id = $argsObj->requirement_id; + + if (is_null($relation_id) || ! is_numeric($relation_id) || + is_null($requirement_id) || ! is_numeric($requirement_id)) { + $op['ok'] = false; + $op['msg'] = '
    ' . lang_get('error_deleting_rel') . + '
    '; + } + + if ($op['ok']) { + $this->reqMgr->delete_relation($relation_id); + } + + $obj = $this->initGuiBean(); + $obj->template = "reqView.php?requirement_id=$requirement_id&relation_add_result_msg=" . + $op['msg']; + + return $obj; + } + + /* + * function: doCreateRevision + * + * args: + * + * returns: + * + * @internal revisions + * + */ + public function doCreateRevision(&$argsObj) + { + $req = $this->reqMgr->get_by_id($argsObj->req_id, + $argsObj->req_version_id); + $req = $req[0]; + $ret = $this->reqMgr->create_new_revision($argsObj->req_version_id, + $argsObj->user_id, $argsObj->tproject_id, $req, + $argsObj->log_message); + + $obj = $this->initGuiBean(); + $obj->user_feedback = $ret['msg']; + $obj->template = "reqView.php?requirement_id={$argsObj->req_id}"; + $obj->req = null; + $obj->req_id = $argsObj->req_id; + return $obj; + } + + /** + */ + private function simpleCompare($old, $new, $oldCF, $newCF) + { + $suggest_revision = array( + 'scope' => 'scope' + ); + + $force_revision = array( + 'status' => 'reqStatus', + 'type' => 'reqType', + 'expected_coverage' => 'expected_coverage', + 'req_doc_id' => 'reqDocId', + 'title' => 'title' + ); + + $ret = array( + 'force' => false, + 'suggest' => false, + 'nochange' => false, + 'changeon' => null + ); + foreach ($force_revision as $access_key => $access_prop) { + if ($ret['force'] = ($old[$access_key] != $new->$access_prop)) { + $ret['changeon'] = 'attribute:' . $access_key; + break; + } + } + + if (! $ret['force'] && ! is_null($newCF)) { + foreach ($newCF as $cf_key => $cf) { + if ($ret['force'] = ($oldCF[$cf_key]['value'] != $cf['cf_value'])) { + $ret['changeon'] = 'custom field:' . $oldCF[$cf_key]['name']; + break; + } + } + } + + if (! $ret['force']) { + + foreach ($suggest_revision as $access_key => $access_prop) { + if ($ret['suggest'] = ($old[$access_key] != $new->$access_prop)) { + $ret['changeon'] = 'attribute:' . $access_key; + break; + } + } + } + $ret['nochange'] = (! $ret['force'] && ! $ret['suggest']); + return $ret; + } + + /** + */ + public function doFreezeVersion(&$argsObj) + { + $obj = $this->initGuiBean(); + $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info( + $argsObj->req_version_id); + $req_version = $this->reqMgr->get_by_id($node['parent_id'], + $argsObj->req_version_id); + $req_version = $req_version[0]; + + $this->reqMgr->updateOpen($req_version['version_id'], false); + + // BUGID 3312 + logAuditEvent( + TLS("audit_req_version_frozen", $req_version['version'], + $req_version['req_doc_id'], $req_version['title']), "FREEZE", + $argsObj->req_version_id, "req_version"); + + $obj->template = 'show_message.tpl'; + $obj->template_dir = ''; + + $obj->user_feedback = sprintf(lang_get('req_version_frozen'), + $req_version['req_doc_id'], $req_version['title'], + $req_version['version']); + + $obj->main_descr = lang_get('requirement') . TITLE_SEP . + $req_version['title']; + $obj->title = lang_get('freeze_req'); + $obj->refreshTree = 0; + $obj->result = 'ok'; // needed to enable refresh_tree logic + return $obj; + } + + /** + */ + public function addTestCase(&$argsObj) + { + $obj = $this->initGuiBean(); + $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info( + $argsObj->req_version_id); + $dummy = $this->reqMgr->get_by_id($node['parent_id'], + $argsObj->req_version_id); + $req_version = $dummy[0]; + + $obj->req = $req_version; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->req_version_id = $argsObj->req_version_id; + $obj->template = "reqView.php?refreshTree=0&requirement_id={$argsObj->req_id}"; + + // Analise test case identity + $tcaseCfg = config_get('testcase_cfg'); + + $status_ok = false; + $msg = sprintf(lang_get('provide_full_external_tcase_id'), + $argsObj->tcasePrefix, $tcaseCfg->glue_character); + $gluePos = strrpos($argsObj->tcaseIdentity, $tcaseCfg->glue_character); + + $isFullExternal = ($gluePos !== false); + if ($isFullExternal) { + $status_ok = true; + $rawTestCasePrefix = substr($argsObj->tcaseIdentity, 0, $gluePos); + + $status_ok = (strcmp($rawTestCasePrefix, $argsObj->tcasePrefix) == 0); + if (! $status_ok) { + $msg = sprintf(lang_get('seems_to_belong_to_other_tproject'), + $rawTestCasePrefix, $argsObj->tcasePrefix); + } + } + + if ($status_ok) { + // IMPORTANT NOTICE: audit info is managed on reqMgr method + $alienMgr = new testcase($this->db); + $tcase_id = $alienMgr->getInternalID($argsObj->tcaseIdentity, + array( + 'tproject_id' => $argsObj->tproject_id + )); + + // Design Choice + // 1. Only latest test case version will be added + // 2. Only if not executed + if ($tcase_id > 0) { + $doLink = true; + if ($tcaseCfg->reqLinkingDisabledAfterExec) { + if ($alienMgr->latestVersionHasBeenExecuted($tcase_id) == 0) { + $doLink = true; + } else { + $doLink = false; + $status_ok = false; + $msg = sprintf( + lang_get( + 'cannot_link_latest_version_reason_has_been_exec'), + $argsObj->tcaseIdentity); + } + } + if ($doLink) { + $this->reqMgr->assign_to_tcase($argsObj->req_id, $tcase_id, + intval($argsObj->user_id)); + } + } else { + $status_ok = false; + $msg = sprintf(lang_get('tcase_doesnot_exist'), + $argsObj->tcaseIdentity); + } + } + + if (! $status_ok) { + $obj->user_feedback = $msg; + $obj->template .= "&user_feedback=" . urlencode($obj->user_feedback); + } + + return $obj; + } + + /** + */ + public function removeTestCase(&$argsObj) + { + // IMPORTANT NOTICE: audit info is managed on reqMgr method + $obj = $this->initGuiBean(); + $bond = array( + 'req' => $argsObj->req_version_id, + 'tc' => $argsObj->tcaseIdentity + ); + + $this->reqMgr->delReqVersionTCVersionLink($bond, __METHOD__); + + $node = $this->reqMgr->tree_mgr->get_node_hierarchy_info( + $argsObj->req_version_id); + $dummy = $this->reqMgr->get_by_id($node['parent_id'], + $argsObj->req_version_id); + $req_version = $dummy[0]; + + $obj->req = $req_version; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->req_version_id = $argsObj->req_version_id; + $obj->template = "reqView.php?refreshTree=0&requirement_id={$argsObj->req_id}"; + return $obj; + } + + /** + */ + public function fileUpload(&$argsObj) + { + $argsObj->uploadOp = fileUploadManagement($this->db, + $argsObj->req_version_id, $argsObj->fileTitle, + $this->reqMgr->getAttachmentTableName()); + + return $this->initGuiObjForAttachmentOperations($argsObj); + } + + /** + */ + public function deleteFile(&$argsObj) + { + $fileInfo = deleteAttachment($this->db, $argsObj->file_id, false); + if ($argsObj->req_version_id == 0) { + $argsObj->req_version_id = $fileInfo['fk_id']; + } + + return $this->initGuiObjForAttachmentOperations($argsObj); + } + + /** + */ + private function initGuiObjForAttachmentOperations($argsObj) + { + $guiObj = new stdClass(); + $guiObj->reqHasBeenDeleted = false; + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->req_id = $argsObj->req_id; + $guiObj->suggest_revision = $guiObj->prompt_for_log = false; + $guiObj->template = "reqView.php?refreshTree=0&requirement_id={$argsObj->req_id}"; + $guiObj->uploadOp = $argsObj->uploadOp; + + return $guiObj; + } + + /** + */ + public function stopMonitoring(&$argsObj) + { + $this->reqMgr->monitorOff($argsObj->req_id, $argsObj->user_id, + $argsObj->tproject_id); + + return $this->initGuiObjForAttachmentOperations($argsObj); + } + + /** + */ + public function startMonitoring(&$argsObj) + { + $this->reqMgr->monitorOn($argsObj->req_id, $argsObj->user_id, + $argsObj->tproject_id); + + return $this->initGuiObjForAttachmentOperations($argsObj); + } +} diff --git a/lib/requirements/reqCompareVersions.php b/lib/requirements/reqCompareVersions.php index 88ec0d8be3..126ad86da5 100644 --- a/lib/requirements/reqCompareVersions.php +++ b/lib/requirements/reqCompareVersions.php @@ -1,344 +1,356 @@ - null,"no_changes" => null, - "diff_subtitle_req" => null, "version_short" => null, - "diff_details_req" => null,"type" => null, "status" => null, - "expected_coverage" => null, - "revision_short" => null, "version_revision" => null) ); - - - -$reqMgr = new requirement_mgr($db); -$differ = new diff(); -$args = init_args(); -$gui = initializeGui($db,$args,$labels,$reqMgr); - - -// if already two versions are selected, display diff -// else display template with versions to select -if ($args->compare_selected_versions) -{ - // Side By Side - $sbs = getItemsToCompare($args->left_item_id,$args->right_item_id,$gui->items); - prepareUserFeedback($db,$gui,$args->req_id,$labels,$sbs); - - $gui->attrDiff = getAttrDiff($sbs['left_item'],$sbs['right_item'],$labels); - - $cfields = getCFToCompare($sbs,$args->tproject_id,$reqMgr); - $gui->cfieldsDiff = null; - if( !is_null($cfields) ) - { - $gui->cfieldsDiff = getCFDiff($cfields,$reqMgr); - } - - $gui->diff = array("scope" => array()); - foreach($gui->diff as $key => $val) - { - if ($args->use_daisydiff) - { - // using daisydiff as diffing engine - $diff = new HTMLDiffer(); - if ($gui->reqType == 'none'){ - list($differences, $diffcount) = $diff->htmlDiff(nl2br($sbs['left_item'][$key]), nl2br($sbs['right_item'][$key])); - } - else{ - list($differences, $diffcount) = $diff->htmlDiff($sbs['left_item'][$key], $sbs['right_item'][$key]); - } - $gui->diff[$key]["diff"] = $differences; - $gui->diff[$key]["count"] = $diffcount; - } - else - { - // insert line endings so diff is better readable and makes sense (not everything in one line) - // then cast to array with \n as separating character, differ needs that - $gui->diff[$key]["left"] = explode("\n", str_replace("

    ", "

    \n", $sbs['left_item'][$key])); - $gui->diff[$key]["right"] = explode("\n", str_replace("

    ", "

    \n", $sbs['right_item'][$key])); - $gui->diff[$key]["diff"] = $differ->inline($gui->diff[$key]["left"], $gui->leftID, - $gui->diff[$key]["right"], $gui->rightID,$args->context); - $gui->diff[$key]["count"] = count($differ->changes); - } - - $gui->diff[$key]["heading"] = lang_get($key); - - // are there any changes? then display! if not, nothing to show here - $additional = ''; - $msg_key = "no_changes"; - if ($gui->diff[$key]["count"] > 0) - { - $msg_key = "num_changes"; - $additional = $gui->diff[$key]["count"]; - } - $gui->diff[$key]["message"] = (sprintf($labels[$msg_key], $key, $additional)); - } + null, + "no_changes" => null, + "diff_subtitle_req" => null, + "version_short" => null, + "diff_details_req" => null, + "type" => null, + "status" => null, + "expected_coverage" => null, + "revision_short" => null, + "version_revision" => null + )); + +$reqMgr = new requirement_mgr($db); +$differ = new diff(); +$args = initArgs(); +$gui = initializeGui($db, $args, $labels, $reqMgr); + +// if already two versions are selected, display diff +// else display template with versions to select +if ($args->compare_selected_versions) { + // Side By Side + $sbs = getItemsToCompare($args->left_item_id, $args->right_item_id, + $gui->items); + prepareUserFeedback($db, $gui, $args->req_id, $labels, $sbs); + + $gui->attrDiff = getAttrDiff($sbs['left_item'], $sbs['right_item'], $labels); + + $cfields = getCFToCompare($sbs, $args->tproject_id, $reqMgr); + $gui->cfieldsDiff = null; + if (! is_null($cfields)) { + $gui->cfieldsDiff = getCFDiff($cfields, $reqMgr); + } + + $gui->diff = array( + "scope" => array() + ); + foreach ($gui->diff as $key => $val) { + if ($args->use_daisydiff) { + // using daisydiff as diffing engine + $diff = new HTMLDiffer(); + if ($gui->reqType == 'none') { + list ($differences, $diffcount) = $diff->htmlDiff( + nl2br($sbs['left_item'][$key]), + nl2br($sbs['right_item'][$key])); + } else { + list ($differences, $diffcount) = $diff->htmlDiff( + $sbs['left_item'][$key], $sbs['right_item'][$key]); + } + $gui->diff[$key]["diff"] = $differences; + $gui->diff[$key]["count"] = $diffcount; + } else { + // insert line endings so diff is better readable and makes sense (not everything in one line) + // then cast to array with \n as separating character, differ needs that + $gui->diff[$key]["left"] = explode("\n", + str_replace("

    ", "

    \n", $sbs['left_item'][$key])); + $gui->diff[$key]["right"] = explode("\n", + str_replace("

    ", "

    \n", $sbs['right_item'][$key])); + $gui->diff[$key]["diff"] = $differ->inline($gui->diff[$key]["left"], + $gui->leftID, $gui->diff[$key]["right"], $gui->rightID, + $args->context); + $gui->diff[$key]["count"] = count($differ->changes); + } + + $gui->diff[$key]["heading"] = lang_get($key); + + // are there any changes? then display! if not, nothing to show here + $additional = ''; + $msg_key = "no_changes"; + if ($gui->diff[$key]["count"] > 0) { + $msg_key = "num_changes"; + $additional = $gui->diff[$key]["count"]; + } + $gui->diff[$key]["message"] = (sprintf($labels[$msg_key], $key, + $additional)); + } +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function getBareBonesReq($dbHandler, $reqID) +{ + $debugMsg = ' Function: ' . __FUNCTION__; + $tables = tlObjectWithDB::getDBTables( + array( + 'requirements', + 'nodes_hierarchy' + )); + $sql = " /* $debugMsg */ SELECT REQ.req_doc_id, NH_REQ.name " . + " FROM {$tables['requirements']} REQ " . + " JOIN {$tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . + " WHERE REQ.id = " . intval($reqID); + + $bones = $dbHandler->get_recordset($sql); + + return $bones[0]; +} + +/** + */ +function getItemsToCompare($leftSideID, $rightSideID, &$itemSet) +{ + $ret = array(); + foreach ($itemSet as $item) { + if ($item['item_id'] == $leftSideID) { + $ret['left_item'] = $item; + } + if ($item['item_id'] == $rightSideID) { + $ret['right_item'] = $item; + } + + if (count($ret) == 2) { + break; + } + } + return $ret; +} + +/** + */ +function getCFToCompare($sides, $tprojectID, &$reqMgr) +{ + $cfields = array( + 'left_side' => array( + 'key' => 'left_item', + 'value' => null + ), + 'right_side' => array( + 'key' => 'right_item', + 'value' => null + ) + ); + + foreach ($cfields as $item_side => $dummy) { + $target_id = $sides[$dummy['key']]; + $target_id = $target_id['item_id']; + $cfields[$item_side]['value'] = $reqMgr->get_linked_cfields(null, + $target_id, $tprojectID); + } + return $cfields; +} + +/** + */ +function getCFDiff($cfields, &$reqMgr) +{ + $cmp = null; + + // Development Note + // All versions + revisions (i.e. child items) have the same qty of linked CF + // => both arrays will have same size() + // + // This is because to get cfields we look only to CF enabled for node type. + $cfieldsLeft = $cfields['left_side']['value']; + $cfieldsRight = $cfields['right_side']['value']; + + if (! is_null($cfieldsLeft)) { + $key2loop = array_keys($cfieldsLeft); + $cmp = array(); + $type_code = $reqMgr->cfield_mgr->get_available_types(); + $key2convert = array( + 'lvalue', + 'rvalue' + ); + + $cfg = config_get('gui'); + $cfCfg = config_get('custom_fields'); + + $formats = array( + 'date' => config_get('date_format') + ); + $t_date_format = str_replace("%", "", $formats['date']); // must remove % + $t_datetime_format = $t_date_format . ' ' . + $cfg->custom_fields->time_format; + + foreach ($key2loop as $cf_key) { + $dt_format = $t_date_format; + + // $cfg->show_custom_fields_without_value + // false => At least one value has to be <> NULL to include on comparsion results + if ($cfCfg->show_custom_fields_without_value || + (! $cfCfg->show_custom_fields_without_value && + ((! is_null($cfieldsRight) && + ! is_null($cfieldsRight[$cf_key]['value'])) || + (! is_null($cfieldsLeft) && + ! is_null($cfieldsLeft[$cf_key]['value']))))) { + $cmp[$cf_key] = array( + 'label' => htmlspecialchars($cfieldsLeft[$cf_key]['label']), + 'lvalue' => $cfieldsLeft[$cf_key]['value'], + 'rvalue' => ! is_null($cfieldsRight) ? $cfieldsRight[$cf_key]['value'] : null, + 'changed' => $cfieldsLeft[$cf_key]['value'] != + $cfieldsRight[$cf_key]['value'] + ); + + if ($type_code[$cfieldsLeft[$cf_key]['type']] == 'date' || + $type_code[$cfieldsLeft[$cf_key]['type']] == 'datetime') { + foreach ($key2convert as $fx) { + if ($doIt = ($cmp[$cf_key][$fx] != null)) { + switch ($type_code[$cfieldsLeft[$cf_key]['type']]) { + case 'datetime': + $dt_format = $t_datetime_format; + break; + } + } + if ($doIt) { + $cmp[$cf_key][$fx] = date($dt_format, + $cmp[$cf_key][$fx]); + } + } + } + } + } + } + + return (null != $cmp && ! empty($cmp)) ? $cmp : null; +} + +/** + */ +function initArgs() +{ + $args = new stdClass(); + + $args->req_id = isset($_REQUEST['requirement_id']) ? intval( + $_REQUEST['requirement_id']) : 0; + + $args->compare_selected_versions = isset( + $_REQUEST['compare_selected_versions']); + $args->left_item_id = isset($_REQUEST['left_item_id']) ? intval( + $_REQUEST['left_item_id']) : - 1; + $args->right_item_id = isset($_REQUEST['right_item_id']) ? intval( + $_REQUEST['right_item_id']) : - 1; + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + + $args->use_daisydiff = isset($_REQUEST['use_html_comp']); + + $diffEngineCfg = config_get("diffEngine"); + $args->context = null; + if (! isset($_REQUEST['context_show_all'])) { + $args->context = (isset($_REQUEST['context']) && + is_numeric($_REQUEST['context'])) ? $_REQUEST['context'] : $diffEngineCfg->context; + } + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj, $lbl, &$reqMgr) +{ + $reqCfg = config_get('req_cfg'); + $guiObj = new stdClass(); + $guiObj->items = $reqMgr->get_history($argsObj->req_id, + array( + 'output' => 'array', + 'decode_user' => true + )); + + // Truncate log message + if ($reqCfg->log_message_len > 0) { + $loop2do = count($guiObj->items); + for ($idx = 0; $idx < $loop2do; $idx ++) { + if (strlen($guiObj->items[$idx]['log_message']) > + $reqCfg->log_message_len) { + $guiObj->items[$idx]['log_message'] = substr( + $guiObj->items[$idx]['log_message'], 0, + $reqCfg->log_message_len) . '...'; + } + $guiObj->items[$idx]['log_message'] = htmlspecialchars( + $guiObj->items[$idx]['log_message']); + } + } + $guiObj->req_id = $argsObj->req_id; + $guiObj->compare_selected_versions = $argsObj->compare_selected_versions; + $guiObj->context = $argsObj->context; + $guiObj->version_short = $lbl['version_short']; + $guiObj->diff = null; + $reqCfg = getWebEditorCfg('requirement'); + $guiObj->reqType = $reqCfg['type']; + + return $guiObj; +} + +/** + */ +function prepareUserFeedback(&$dbHandler, &$guiObj, $reqID, $labels, $sbs) +{ + $guiObj->leftID = sprintf($labels['version_revision'], + $sbs['left_item']['version'], $sbs['left_item']['revision']); + $guiObj->rightID = sprintf($labels['version_revision'], + $sbs['right_item']['version'], $sbs['right_item']['revision']); + $mini_me = getBareBonesReq($dbHandler, $reqID); + $guiObj->subtitle = sprintf($labels['diff_details_req'], + $sbs['left_item']['version'], $sbs['left_item']['revision'], + $sbs['left_item']['version'], $sbs['left_item']['revision'], + $sbs['right_item']['version'], $sbs['right_item']['revision'], + $sbs['right_item']['version'], $sbs['right_item']['revision'], + $mini_me['req_doc_id'] . config_get('gui_title_separator_1') . + $mini_me['name']); +} + +/** + */ +function getAttrDiff($leftSide, $rightSide, $labels) +{ + $req_cfg = config_get('req_cfg'); + $key2loop = array( + 'status' => 'status_labels', + 'type' => 'type_labels', + 'expected_coverage' => null + ); + foreach ($key2loop as $fkey => $lkey) { + // Need to decode + $cmp[$fkey] = array( + 'label' => htmlspecialchars($labels[$fkey]), + 'lvalue' => $leftSide[$fkey], + 'rvalue' => $rightSide[$fkey], + 'changed' => $leftSide[$fkey] != $rightSide[$fkey] + ); + + if (! is_null($lkey)) { + $decode = $req_cfg->$lkey; + + $cmp[$fkey]['lvalue'] = lang_get($decode[$cmp[$fkey]['lvalue']]); + $cmp[$fkey]['rvalue'] = lang_get($decode[$cmp[$fkey]['rvalue']]); + } + } + return $cmp; } - -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -function getBareBonesReq($dbHandler,$reqID) -{ - $debugMsg = ' Function: ' . __FUNCTION__; - $tables = tlObjectWithDB::getDBTables(array('requirements','nodes_hierarchy')); - $sql = " /* $debugMsg */ SELECT REQ.req_doc_id, NH_REQ.name " . - " FROM {$tables['requirements']} REQ " . - " JOIN {$tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id = REQ.id " . - " WHERE REQ.id = " . intval($reqID); - - $bones = $dbHandler->get_recordset($sql); - - return $bones[0]; -} - -/** - * - * - */ -function getItemsToCompare($leftSideID,$rightSideID,&$itemSet) -{ - - $ret = array(); - foreach($itemSet as $item) - { - if ($item['item_id'] == $leftSideID) - { - $ret['left_item'] = $item; - } - if ($item['item_id'] == $rightSideID) - { - $ret['right_item'] = $item; - } - - if( count($ret) == 2 ) - { - break; - } - } - return $ret; -} - - -/** - * - * - */ -function getCFToCompare($sides,$tprojectID,&$reqMgr) -{ - $cfields = array('left_side' => array('key' => 'left_item', 'value' => null), - 'right_side' => array('key' => 'right_item', 'value' => null)); - - foreach($cfields as $item_side => $dummy) - { - $target_id = $sides[$dummy['key']]; - $target_id = $target_id['item_id']; - $cfields[$item_side]['value'] = $reqMgr->get_linked_cfields(null,$target_id,$tprojectID); - } - return $cfields; -} - - -/** - * - */ -function getCFDiff($cfields,&$reqMgr) -{ - $cmp = null; - - // Development Note - // All versions + revisions (i.e. child items) have the same qty of linked CF - // => both arrays will have same size() - // - // This is because to get cfields we look only to CF enabled for node type. - $cfieldsLeft = $cfields['left_side']['value']; - $cfieldsRight = $cfields['right_side']['value']; - - if( !is_null($cfieldsLeft) ) - { - $key2loop = array_keys($cfieldsLeft); - $cmp = array(); - $type_code = $reqMgr->cfield_mgr->get_available_types(); - $key2convert = array('lvalue','rvalue'); - - - $cfg = config_get('gui'); - $cfCfg = config_get('custom_fields'); - - $formats = array('date' => config_get( 'date_format')); - $t_date_format = str_replace("%","",$formats['date']); // must remove % - $t_datetime_format = $t_date_format . ' ' . $cfg->custom_fields->time_format; - - foreach($key2loop as $cf_key) - { - $dt_format = $t_date_format; - - // $cfg->show_custom_fields_without_value - // false => At least one value has to be <> NULL to include on comparsion results - // - if( $cfCfg->show_custom_fields_without_value == true || - ($cfCfg->show_custom_fields_without_value == false && - ( (!is_null($cfieldsRight) && !is_null($cfieldsRight[$cf_key]['value'])) || - (!is_null($cfieldsLeft) && !is_null($cfieldsLeft[$cf_key]['value'])) ) - ) - ) - { - $cmp[$cf_key] = array('label' => htmlspecialchars($cfieldsLeft[$cf_key]['label']), - 'lvalue' => $cfieldsLeft[$cf_key]['value'], - 'rvalue' => !is_null($cfieldsRight) ? $cfieldsRight[$cf_key]['value'] : null, - 'changed' => $cfieldsLeft[$cf_key]['value'] != $cfieldsRight[$cf_key]['value']); - - if($type_code[$cfieldsLeft[$cf_key]['type']] == 'date' || - $type_code[$cfieldsLeft[$cf_key]['type']] == 'datetime') - { - foreach($key2convert as $fx) - { - if( ($doIt = ($cmp[$cf_key][$fx] != null)) ) - { - switch($type_code[$cfieldsLeft[$cf_key]['type']]) - { - case 'datetime': - $dt_format = $t_datetime_format; - break ; - } - } - if( $doIt ) - { - $cmp[$cf_key][$fx] = date($dt_format,$cmp[$cf_key][$fx]); - } - } - } - } // mega if - } // foraeach - } - - return (null != $cmp && count($cmp) > 0) ? $cmp : null; -} - - - -/** - * - * - */ -function init_args() { - $args = new stdClass(); - - $args->req_id = isset($_REQUEST['requirement_id']) ? intval($_REQUEST['requirement_id']) : 0; - - $args->compare_selected_versions = isset($_REQUEST['compare_selected_versions']); - $args->left_item_id = isset($_REQUEST['left_item_id']) ? intval($_REQUEST['left_item_id']) : -1; - $args->right_item_id = isset($_REQUEST['right_item_id']) ? intval($_REQUEST['right_item_id']) : -1; - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - - $args->use_daisydiff = isset($_REQUEST['use_html_comp']); - - $diffEngineCfg = config_get("diffEngine"); - $args->context = null; - if( !isset($_REQUEST['context_show_all'])) { - $args->context = (isset($_REQUEST['context']) && is_numeric($_REQUEST['context'])) ? $_REQUEST['context'] : $diffEngineCfg->context; - } - - return $args; -} - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,$lbl,&$reqMgr) -{ - $reqCfg = config_get('req_cfg'); - $guiObj = new stdClass(); - $guiObj->items = $reqMgr->get_history($argsObj->req_id,array('output' => 'array','decode_user' => true)); - - - // Truncate log message - if( $reqCfg->log_message_len > 0 ) - { - $loop2do = count($guiObj->items); - for($idx=0; $idx < $loop2do; $idx++) - { - if( strlen($guiObj->items[$idx]['log_message']) > $reqCfg->log_message_len ) - { - $guiObj->items[$idx]['log_message'] = substr($guiObj->items[$idx]['log_message'],0,$reqCfg->log_message_len) . '...'; - } - $guiObj->items[$idx]['log_message'] = htmlspecialchars($guiObj->items[$idx]['log_message']); - } - } - $guiObj->req_id = $argsObj->req_id; - $guiObj->compare_selected_versions = $argsObj->compare_selected_versions; - $guiObj->context = $argsObj->context; - $guiObj->version_short = $lbl['version_short']; - $guiObj->diff = null; - $reqCfg = getWebEditorCfg('requirement'); - $guiObj->reqType = $reqCfg['type']; - - return $guiObj; -} - -/** - * - * - */ -function prepareUserFeedback(&$dbHandler,&$guiObj,$reqID,$labels,$sbs) -{ - $guiObj->leftID = sprintf($labels['version_revision'],$sbs['left_item']['version'],$sbs['left_item']['revision']); - $guiObj->rightID = sprintf($labels['version_revision'],$sbs['right_item']['version'],$sbs['right_item']['revision']); - $mini_me = getBareBonesReq($dbHandler,$reqID); - $guiObj->subtitle = sprintf($labels['diff_details_req'], - $sbs['left_item']['version'],$sbs['left_item']['revision'], - $sbs['left_item']['version'],$sbs['left_item']['revision'], - $sbs['right_item']['version'],$sbs['right_item']['revision'], - $sbs['right_item']['version'],$sbs['right_item']['revision'], - $mini_me['req_doc_id'] . config_get('gui_title_separator_1') . $mini_me['name']); -} - -/** - * - * - */ -function getAttrDiff($leftSide,$rightSide,$labels) -{ - $req_cfg = config_get('req_cfg'); - $key2loop = array('status' => 'status_labels','type' => 'type_labels','expected_coverage' => null); - foreach($key2loop as $fkey => $lkey) - { - // Need to decode - $cmp[$fkey] = array('label' => htmlspecialchars($labels[$fkey]), - 'lvalue' => $leftSide[$fkey],'rvalue' => $rightSide[$fkey], - 'changed' => $leftSide[$fkey] != $rightSide[$fkey]); - - if( !is_null($lkey) ) - { - $decode = $req_cfg->$lkey; - - $cmp[$fkey]['lvalue'] = lang_get($decode[$cmp[$fkey]['lvalue']]); - $cmp[$fkey]['rvalue'] = lang_get($decode[$cmp[$fkey]['rvalue']]); - } - } - return $cmp; -} \ No newline at end of file diff --git a/lib/requirements/reqCreateFromIssueMantisXML.php b/lib/requirements/reqCreateFromIssueMantisXML.php index 329cb181eb..e5732db3c2 100644 --- a/lib/requirements/reqCreateFromIssueMantisXML.php +++ b/lib/requirements/reqCreateFromIssueMantisXML.php @@ -1,268 +1,270 @@ - - * - * - * 21 - * testlink-test - * administrator - * normal - * minor - * have not tried - * new - * open - * none - * FromTestLink - * 1365184242 - * 1365184242 - * none - * V1 - * public - * ISSUE-V1 - * 1 - * ISSUE-V1 - * - * - * 20 - * testlink-test - * - * @internal revisions - * @since 1.9.10 - * - */ -require('../../config.inc.php'); -require_once('common.php'); -require_once('xml.inc.php'); - -testlinkInitPage($db,false,false,"checkRights"); -$templateCfg = templateConfiguration(); -$req_spec_mgr = new requirement_spec_mgr($db); -$req_mgr = new requirement_mgr($db); -$args = init_args(); -$gui = initializeGui($db,$args); - -new dBug($args); -new dBug($gui); - -switch($args->doAction) -{ - case 'uploadFile': - $dummy = doExecuteImport($gui->fileName,$args,$req_spec_mgr,$req_mgr); - $gui->items = $dummy->items; - $gui->file_check = $dummy->file_check; - $gui->userFeedback = (array)$dummy->userFeedback; - if(array_key_exists("syntaxError", $gui->userFeedback) && count($gui->userFeedback['syntaxError']) > 0) - { - $gui->importResult = lang_get('import_syntax_error'); - } - else - { - $gui->importResult = lang_get('import_done'); - } - $gui->refreshTree = $args->refreshTree && $gui->file_check['status_ok']; - break; -} - - -// new dBug($gui); -$smarty = new TLSmarty; -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/** - * - */ -function init_args() -{ - $argsObj = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $iParams = array("importType" => array(tlInputParameter::STRING_N,0,5), - "req_spec_id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,20)); - - R_PARAMS($iParams,$argsObj); - - $argsObj->doAction = ($argsObj->doAction == '') ? 'askFileName' : $argsObj->doAction; - $argsObj->userID = intval($_SESSION['userID']); - $argsObj->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $argsObj->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - return $argsObj; -} - - - - -/** - * - * - * - **/ -function initializeGui(&$dbHandler,&$argsObj) -{ - $guiObj = new stdClass(); - $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); - $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); - $guiObj->importTypes = array('XML' => 'Mantis XML'); - - - $guiObj->req_spec_id = $argsObj->req_spec_id; - $guiObj->refreshTree = $guiObj->doImport = tlStringLen($argsObj->importType); - $guiObj->resultMap = null; - $guiObj->req_spec_name = ''; - $guiObj->file_check = array('status_ok' => 1, 'msg' => 'ok'); - $guiObj->import_title = lang_get('title_req_import'); - - $guiObj->fileName = TL_TEMP_PATH . session_id(). "-import_req_from_issue"; - - if($argsObj->req_spec_id) - { - $tree_mgr = new tree($dbHandler); - $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->req_spec_id); - unset($tree_mgr); - $guiObj->req_spec_name = $node_info['name']; - } - - - return $guiObj; -} - - -/** - * doExecuteImport - * - */ -function doExecuteImport($fileName,&$argsObj,&$reqSpecMgr,&$reqMgr) -{ - $retval = new stdClass(); - $retval->items = array(); - $retval->msg = ''; - $retval->file_check=array('status_ok' => 1, 'msg' => 'ok'); - $retval->userFeedback = null; - - $context = new stdClass(); - $context->tproject_id = $argsObj->tproject_id; - $context->req_spec_id = $argsObj->req_spec_id; - $context->user_id = $argsObj->userID; - $context->importType = $argsObj->importType; - - new dBug($context); - // manage file upload process - $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; - - - if (($source != 'none') && ($source != '' )) - { - if (move_uploaded_file($source, $fileName)) - { - if( strcasecmp($argsObj->importType,'XML') == 0 ) - { - $retval->file_check['status_ok']=!(($xml=simplexml_load_file_wrapper($fileName)) === FALSE); - } - } - } - else - { - $retval->file_check=array('status_ok' => 0, 'msg' => lang_get('please_choose_req_file')); - } - // ---------------------------------------------------------------------------------------------- - - if($retval->file_check['status_ok']) - { - //var_dump($xml); - //die(); - - $retval->items = doReqImportFromMantisXML($reqMgr,$xml,$context); - $retval->msg = lang_get('req_import_finished'); - unlink($fileName); - } - return $retval; -} - - -/** - * - */ -function doReqImportFromMantisXML(&$reqMgr,&$simpleXMLObj,$importContext) -{ - - $inputItems = getFromMantisIssueSimpleXMLObj($simpleXMLObj); - $loop2do = count($inputItems); - for($kdx=0; $kdx < $loop2do; $kdx++) - { - $dummy = $reqMgr->createFromMap($inputItems[$kdx],$importContext->tproject_id, - $importContext->req_spec_id,$importContext->user_id); - $items = array_merge($items,$dummy); - } - return $items; -} - -/** - * - */ -function getFromMantisIssueSimpleXMLObj($xmlObj) -{ - $itemSet = null; - if (!$xmlObj) - { - return $itemSet; - } - - - $l18n = init_labels( array('issue_issue' => null, 'issue_steps_to_reproduce' => null, 'issue_summary' => null, - 'issue_target_version' => null,'issue_description' => null, - 'issue_additional_information' => null)); - - $jdx = 0; - $xmlIssue = $xmlObj->issue; - $loops2do=sizeof($xmlIssue); - - $XMLDef['elements'] = array('string' => array('summary' => null,'description' => null, - 'additional_information' => null, - 'steps_to_reproduce' => null, - 'target_version' => null, 'id' => null)); - $itemSet = array(); - $nl = "

    "; - for($idx = 0; $idx < $loops2do; $idx++) - { - $dummy = getItemsFromSimpleXMLObj(array($xmlIssue[$idx]),$XMLDef); - $dummy = $dummy[0]; - - $isum = $l18n['issue_description'] . $nl . $dummy['description']; - if(!is_null($dummy['steps_to_reproduce'])) - { - $isum .= $nl . $l18n['issue_steps_to_reproduce'] . $nl . $dummy['steps_to_reproduce']; - } - if(!is_null($dummy['additional_information'])) - { - $isum .= $nl . $l18n['issue_additional_information'] . $nl . $dummy['additional_information']; - } - - $itemSet[$jdx++] = array('docid' => 'Mantis Task ID:' .$dummy['id'], - 'title' => ($l18n['issue_issue'] . ':' . $dummy['id'] . ' - ' . $dummy['summary']), - 'description' => $isum,'node_order' => $idx, - 'status' => '', 'type' => '', 'expected_coverage' => 1); - } - return $itemSet; -} - -/** - * - * - */ -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,'mgt_view_req') && $user->hasRight($db,'mgt_modify_req')); + + * + * + * 21 + * testlink-test + * administrator + * normal + * minor + * have not tried + * new + * open + * none + * FromTestLink + * 1365184242 + * 1365184242 + * none + * V1 + * public + *

    ISSUE-V1 + * 1 + * ISSUE-V1 + *
    + * + * 20 + * testlink-test + * + * @internal revisions + * @since 1.9.10 + * + */ +require_once '../../config.inc.php'; +require_once 'common.php'; +require_once 'xml.inc.php'; + +testlinkInitPage($db, false, false, "checkRights"); +$templateCfg = templateConfiguration(); +$req_spec_mgr = new requirement_spec_mgr($db); +$req_mgr = new requirement_mgr($db); +$args = initArgs(); +$gui = initializeGui($db, $args); + +switch ($args->doAction) { + case 'uploadFile': + $dummy = doExecuteImport($gui->fileName, $args, $req_spec_mgr, $req_mgr); + $gui->items = $dummy->items; + $gui->file_check = $dummy->file_check; + $gui->userFeedback = (array) $dummy->userFeedback; + if (array_key_exists("syntaxError", $gui->userFeedback) && + ! empty($gui->userFeedback['syntaxError'])) { + $gui->importResult = lang_get('import_syntax_error'); + } else { + $gui->importResult = lang_get('import_done'); + } + $gui->refreshTree = $args->refreshTree && $gui->file_check['status_ok']; + break; +} +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initArgs() +{ + $argsObj = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "importType" => array( + tlInputParameter::STRING_N, + 0, + 5 + ), + "req_spec_id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 20 + ) + ); + + R_PARAMS($iParams, $argsObj); + + $argsObj->doAction = ($argsObj->doAction == '') ? 'askFileName' : $argsObj->doAction; + $argsObj->userID = intval($_SESSION['userID']); + $argsObj->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $argsObj->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + return $argsObj; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); + $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); + $guiObj->importTypes = array( + 'XML' => 'Mantis XML' + ); + + $guiObj->req_spec_id = $argsObj->req_spec_id; + $guiObj->refreshTree = $guiObj->doImport = tlStringLen($argsObj->importType); + $guiObj->resultMap = null; + $guiObj->req_spec_name = ''; + $guiObj->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $guiObj->import_title = lang_get('title_req_import'); + + $guiObj->fileName = TL_TEMP_PATH . session_id() . "-import_req_from_issue"; + + if ($argsObj->req_spec_id) { + $tree_mgr = new tree($dbHandler); + $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->req_spec_id); + unset($tree_mgr); + $guiObj->req_spec_name = $node_info['name']; + } + + return $guiObj; +} + +/** + * doExecuteImport + */ +function doExecuteImport($fileName, &$argsObj, &$reqSpecMgr, &$reqMgr) +{ + $retval = new stdClass(); + $retval->items = array(); + $retval->msg = ''; + $retval->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $retval->userFeedback = null; + + $context = new stdClass(); + $context->tproject_id = $argsObj->tproject_id; + $context->req_spec_id = $argsObj->req_spec_id; + $context->user_id = $argsObj->userID; + $context->importType = $argsObj->importType; + + // manage file upload process + $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; + + if (($source != 'none') && ($source != '')) { + if (move_uploaded_file($source, $fileName) && + strcasecmp($argsObj->importType, 'XML') == 0) { + $retval->file_check['status_ok'] = (($xml = simplexml_load_file_wrapper( + $fileName)) !== false); + } + } else { + $retval->file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('please_choose_req_file') + ); + } + + if ($retval->file_check['status_ok']) { + + $retval->items = doReqImportFromMantisXML($reqMgr, $xml, $context); + $retval->msg = lang_get('req_import_finished'); + unlink($fileName); + } + return $retval; +} + +/** + */ +function doReqImportFromMantisXML(&$reqMgr, &$simpleXMLObj, $importContext) +{ + $inputItems = getFromMantisIssueSimpleXMLObj($simpleXMLObj); + $loop2do = count($inputItems); + for ($kdx = 0; $kdx < $loop2do; $kdx ++) { + $dummy = $reqMgr->createFromMap($inputItems[$kdx], + $importContext->tproject_id, $importContext->req_spec_id, + $importContext->user_id); + $items = array_merge($items, $dummy); + } + return $items; +} + +/** + */ +function getFromMantisIssueSimpleXMLObj($xmlObj) +{ + $itemSet = null; + if (! $xmlObj) { + return $itemSet; + } + + $l18n = init_labels( + array( + 'issue_issue' => null, + 'issue_steps_to_reproduce' => null, + 'issue_summary' => null, + 'issue_target_version' => null, + 'issue_description' => null, + 'issue_additional_information' => null + )); + + $jdx = 0; + $xmlIssue = $xmlObj->issue; + $loops2do = count($xmlIssue); + + $xmlDef['elements'] = array( + 'string' => array( + 'summary' => null, + 'description' => null, + 'additional_information' => null, + 'steps_to_reproduce' => null, + 'target_version' => null, + 'id' => null + ) + ); + $itemSet = array(); + $nl = "

    "; + for ($idx = 0; $idx < $loops2do; $idx ++) { + $dummy = getItemsFromSimpleXMLObj(array( + $xmlIssue[$idx] + ), $xmlDef); + $dummy = $dummy[0]; + + $isum = $l18n['issue_description'] . $nl . $dummy['description']; + if (! is_null($dummy['steps_to_reproduce'])) { + $isum .= $nl . $l18n['issue_steps_to_reproduce'] . $nl . + $dummy['steps_to_reproduce']; + } + if (! is_null($dummy['additional_information'])) { + $isum .= $nl . $l18n['issue_additional_information'] . $nl . + $dummy['additional_information']; + } + + $itemSet[$jdx ++] = array( + 'docid' => 'Mantis Task ID:' . $dummy['id'], + 'title' => ($l18n['issue_issue'] . ':' . $dummy['id'] . ' - ' . + $dummy['summary']), + 'description' => $isum, + 'node_order' => $idx, + 'status' => '', + 'type' => '', + 'expected_coverage' => 1 + ); + } + return $itemSet; +} + +/** + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'mgt_view_req') && + $user->hasRight($db, 'mgt_modify_req'); } diff --git a/lib/requirements/reqEdit.php b/lib/requirements/reqEdit.php index 186e5f1b73..85387f7262 100644 --- a/lib/requirements/reqEdit.php +++ b/lib/requirements/reqEdit.php @@ -1,307 +1,389 @@ -doAction; -$op = null; -if(method_exists($commandMgr,$pFn)) { - $op = $commandMgr->$pFn($args,$_REQUEST); +tproject_id = $args->tproject_id; +checkRights($db, $args->user, $context); + +$pFn = $args->doAction; +$op = null; +if (method_exists($commandMgr, $pFn)) { + $op = $commandMgr->$pFn($args, $_REQUEST); +} + +renderGui($args, $gui, $op, $templateCfg, $editorCfg, $db); + +/** + * init_args + */ +function initArgs(&$dbHandler) +{ + $reqTitleSize = config_get('field_size')->requirement_title; + $iParams = array( + "requirement_id" => array( + tlInputParameter::INT_N + ), + "req_version_id" => array( + tlInputParameter::INT_N + ), + "req_spec_id" => array( + tlInputParameter::INT_N + ), + "req_title" => array( + tlInputParameter::STRING_N, + 0, + $reqTitleSize + ), + "req_id_cbox" => array( + tlInputParameter::ARRAY_INT + ), + "reqDocId" => array( + tlInputParameter::STRING_N, + 0, + 64 + ), + "reqStatus" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "reqType" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "containerID" => array( + tlInputParameter::INT_N + ), + "scope" => array( + tlInputParameter::STRING_N + ), + "countReq" => array( + tlInputParameter::INT_N + ), + "expected_coverage" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + "itemSet" => array( + tlInputParameter::ARRAY_INT + ), + "testcase_count" => array( + tlInputParameter::ARRAY_INT + ), + "copy_testcase_assignment" => array( + tlInputParameter::CB_BOOL + ), + "relation_id" => array( + tlInputParameter::INT_N + ), + "relation_source_req_id" => array( + tlInputParameter::INT_N + ), + "relation_type" => array( + tlInputParameter::STRING_N + ), + "relation_destination_req_doc_id" => array( + tlInputParameter::STRING_N, + 0, + 64 + ), + "relation_destination_testproject_id" => array( + tlInputParameter::INT_N + ), + "save_rev" => array( + tlInputParameter::INT_N + ), + "do_save" => array( + tlInputParameter::INT_N + ), + "log_message" => array( + tlInputParameter::STRING_N + ), + "tcaseIdentity" => array( + tlInputParameter::STRING_N + ), + "file_id" => array( + tlInputParameter::INT_N + ), + "fileTitle" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->user = $_SESSION['currentUser']; + $args->req_id = $args->requirement_id; + $args->title = $args->req_title; + $args->arrReqIds = $args->req_id_cbox; + + $args->basehref = $_SESSION['basehref']; + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + if ($args->tproject_id <= 0) { + throw new Exception( + __FILE__ . '::' . __FUNCTION__ . " Test project ID can not be <= 0 "); + } + + $mgr = new testproject($dbHandler); + $info = $mgr->get_by_id($args->tproject_id); + if (is_null($info)) { + throw new Exception( + __FILE__ . '::' . __FUNCTION__ . " Unable to get test project data "); + } + + $args->tproject_name = $info['name']; + $args->tcasePrefix = $info['prefix']; + + $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; + + if (! is_numeric($args->expected_coverage)) { + $args->expected_coverage = 0; + } + + $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? $_SESSION['setting_refresh_tree_on_action'] : 0; + + $args->stay_here = isset($_REQUEST['stay_here']) ? 1 : 0; + return $args; +} + +/** + */ +function renderGui(&$argsObj, $guiObj, $opObj, $templateCfg, $editorCfg, + &$dbHandler) +{ + $smartyObj = new TLSmarty(); + $renderType = 'none'; + // @TODO document + $actionOpe = array( + 'create' => 'doCreate', + 'edit' => 'doUpdate', + 'doDelete' => '', + 'doReorder' => '', + 'reorder' => '', + 'createTestCases' => 'doCreateTestCases', + 'doCreateTestCases' => 'doCreateTestCases', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate', + 'copy' => 'doCopy', + 'doCopy' => 'doCopy', + 'doCreateVersion' => 'doCreateVersion', + 'doCreateRevision' => 'doCreateRevision', + 'doDeleteVersion' => '', + 'doFreezeVersion' => 'doFreezeVersion', + 'doAddRelation' => 'doAddRelation', + 'doDeleteRelation' => 'doDeleteRelation', + 'doUnfreezeVersion' => 'doUnfreezeVersion', + 'fileUpload' => '', + 'deleteFile' => '', + 'startMonitoring' => '', + 'stopMonitoring' => '' + ); + + $owebEditor = web_editor('scope', $argsObj->basehref, $editorCfg); + switch ($argsObj->doAction) { + case "edit": + case "doCreate": + $owebEditor->Value = $argsObj->scope; + break; + + case "fileUpload": + case "deleteFile": + case "startMonitoring": + case "stopMonitoring": + break; + + default: + if ($opObj->suggest_revision || $opObj->prompt_for_log) { + $owebEditor->Value = $argsObj->scope; + } else { + $owebEditor->Value = getItemTemplateContents( + 'requirement_template', $owebEditor->InstanceName, + $argsObj->scope); + } + break; + } + + $guiObj->askForRevision = $opObj->suggest_revision ? 1 : 0; + $guiObj->askForLog = $opObj->prompt_for_log ? 1 : 0; + + $guiObj->scope = $owebEditor->CreateHTML(); + $guiObj->editorType = $editorCfg['type']; + switch ($argsObj->doAction) { + case "doDelete": + $guiObj->refreshTree = 1; // has to be forced + break; + + case "doCreate": + $guiObj->refreshTree = $argsObj->refreshTree; + break; + + case "doUpdate": + // IMPORTANT NOTICE + // we do not set tree refresh here, because on this situation + // tree update has to be done when reqView page is called. + // If we ask for tree refresh here we are going to do double refresh (useless and time consuming) + break; + } + + switch ($argsObj->doAction) { + case "addTestCase": + case "edit": + case "create": + case "reorder": + case "doDelete": + case "doReorder": + case "createTestCases": + case "doCreateTestCases": + case "doCreate": + case "doFreezeVersion": + case "doUnfreezeVersion": + case "doUpdate": + case "copy": + case "doCopy": + case "doCreateVersion": + case "doDeleteVersion": + case "doAddRelation": + case "doDeleteRelation": + case "doCreateRevision": + case "removeTestCase": + case "fileUpload": + case "deleteFile": + case "stopMonitoring": + case "startMonitoring": + $renderType = 'template'; + $key2loop = get_object_vars($opObj); + foreach ($key2loop as $key => $value) { + $guiObj->$key = $value; + } + + // exceptions + $guiObj->askForRevision = $opObj->suggest_revision ? 1 : 0; + $guiObj->askForLog = $opObj->prompt_for_log ? 1 : 0; + $guiObj->operation = isset($actionOpe[$argsObj->doAction]) ? $actionOpe[$argsObj->doAction] : $argsObj->doAction; + + $tplDir = (! isset($opObj->template_dir) || + is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; + $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; + + $pos = strpos($tpl, '.php'); + if ($pos === false) { + $tpl = $tplDir . $tpl; + } else { + $renderType = 'redirect'; + if (! empty($guiObj->uploadOp) && ! $guiObj->uploadOp->statusOK) { + $tpl .= "&uploadOPStatusCode=" . + $guiObj->uploadOp->statusCode; + } + } + break; + } + + $req_mgr = new requirement_mgr($dbHandler); + $guiObj->last_doc_id = $req_mgr->get_last_doc_id_for_testproject( + $argsObj->tproject_id); + $guiObj->doAction = $argsObj->doAction; + + switch ($renderType) { + case 'template': + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($tpl); + break; + + case 'redirect': + header("Location: {$tpl}"); + exit(); + break; + + default: + break; + } +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj, &$commandMgr) +{ + $req_spec_mgr = new requirement_spec_mgr($dbHandler); + + $gui = $commandMgr->initGuiBean(); + $gui->req_cfg = config_get('req_cfg'); + + $gui->req_spec_id = $argsObj->req_spec_id; + if ($argsObj->req_spec_id) { + $gui->requirements_count = $req_spec_mgr->get_requirements_count( + $gui->req_spec_id); + $gui->req_spec = $req_spec_mgr->get_by_id($gui->req_spec_id); + } + $gui->user_feedback = null; + $gui->main_descr = lang_get('req_spec_short'); + if (isset($gui->req_spec)) { + $gui->main_descr .= config_get('gui_title_separator_1') . + $gui->req_spec['title']; + } + $gui->action_descr = null; + + $gui->grants = new stdClass(); + $gui->grants->req_mgmt = $argsObj->user->hasRightOnProj($dbHandler, + "mgt_modify_req"); + $gui->grants->mgt_view_events = $argsObj->user->hasRightOnProj($dbHandler, + "mgt_view_events"); + + $gui->req_version_id = $argsObj->req_version_id; + $gui->preSelectedType = TL_REQ_TYPE_USE_CASE; + + $gui->stay_here = $argsObj->stay_here; + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req", + "mgt_modify_req" + ]; + pageAccessCheck($db, $user, $context); } - -renderGui($args,$gui,$op,$templateCfg,$editorCfg,$db); - - -/** - * init_args - * - */ -function init_args(&$dbHandler) -{ - $reqTitleSize = config_get('field_size')->requirement_title; - $iParams = array("requirement_id" => array(tlInputParameter::INT_N), - "req_version_id" => array(tlInputParameter::INT_N), - "req_spec_id" => array(tlInputParameter::INT_N), - "req_title" => array(tlInputParameter::STRING_N,0,$reqTitleSize), - "req_id_cbox" => array(tlInputParameter::ARRAY_INT), - "reqDocId" => array(tlInputParameter::STRING_N,0,64), - "reqStatus" => array(tlInputParameter::STRING_N,0,1), - "reqType" => array(tlInputParameter::STRING_N,0,1), - "containerID" => array(tlInputParameter::INT_N), - "scope" => array(tlInputParameter::STRING_N), - "countReq" => array(tlInputParameter::INT_N), - "expected_coverage" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,20), - "itemSet" => array(tlInputParameter::ARRAY_INT), - "testcase_count" => array(tlInputParameter::ARRAY_INT), - "copy_testcase_assignment" => array(tlInputParameter::CB_BOOL), - "relation_id" => array(tlInputParameter::INT_N), - "relation_source_req_id" => array(tlInputParameter::INT_N), - "relation_type" => array(tlInputParameter::STRING_N), - "relation_destination_req_doc_id" => array(tlInputParameter::STRING_N,0,64), - "relation_destination_testproject_id" => array(tlInputParameter::INT_N), - "save_rev" => array(tlInputParameter::INT_N), - "do_save" => array(tlInputParameter::INT_N), - "log_message" => array(tlInputParameter::STRING_N), - "tcaseIdentity" => array(tlInputParameter::STRING_N), - "file_id" => array(tlInputParameter::INT_N), - "fileTitle" => array(tlInputParameter::STRING_N,0,100)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - $_REQUEST=strings_stripSlashes($_REQUEST); - - $args->req_id = $args->requirement_id; - $args->title = $args->req_title; - $args->arrReqIds = $args->req_id_cbox; - - $args->basehref = $_SESSION['basehref']; - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - if($args->tproject_id <= 0) - { - throw new Exception(__FILE__ . '::' . __FUNCTION__ . " Test project ID can not be <= 0 "); - } - - $mgr = new testproject($dbHandler); - $info = $mgr->get_by_id($args->tproject_id); - if(is_null($info)) - { - throw new Exception(__FILE__ . '::' . __FUNCTION__ . " Unable to get test project data "); - } - - $args->tproject_name = $info['name']; - $args->tcasePrefix = $info['prefix']; - - - $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; - - if (!is_numeric($args->expected_coverage)) { - $args->expected_coverage = 0; - } - - $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) - ? $_SESSION['setting_refresh_tree_on_action'] : 0; - - $args->stay_here = isset($_REQUEST['stay_here']) ? 1 : 0; - return $args; -} - -/** - * - * - */ -function renderGui(&$argsObj,$guiObj,$opObj,$templateCfg,$editorCfg,&$dbHandler) -{ - $smartyObj = new TLSmarty(); - $renderType = 'none'; - // @TODO document - $actionOpe = array('create' => 'doCreate', 'edit' => 'doUpdate', - 'doDelete' => '', 'doReorder' => '', 'reorder' => '', - 'createTestCases' => 'doCreateTestCases', - 'doCreateTestCases' => 'doCreateTestCases', - 'doCreate' => 'doCreate', 'doUpdate' => 'doUpdate', - 'copy' => 'doCopy', 'doCopy' => 'doCopy', - 'doCreateVersion' => 'doCreateVersion','doCreateRevision' => 'doCreateRevision', - 'doDeleteVersion' => '', 'doFreezeVersion' => 'doFreezeVersion', - 'doAddRelation' => 'doAddRelation', 'doDeleteRelation' => 'doDeleteRelation', - 'doUnfreezeVersion' => 'doUnfreezeVersion', - 'fileUpload' => '', 'deleteFile' => '', - 'startMonitoring' => '','stopMonitoring' => ''); - - $owebEditor = web_editor('scope',$argsObj->basehref,$editorCfg) ; - switch($argsObj->doAction) { - case "edit": - case "doCreate": - $owebEditor->Value = $argsObj->scope; - break; - - case "fileUpload": - case "deleteFile": - case "startMonitoring": - case "stopMonitoring": - break; - - default: - if($opObj->suggest_revision || $opObj->prompt_for_log) - { - $owebEditor->Value = $argsObj->scope; - } - else - { - $owebEditor->Value = getItemTemplateContents('requirement_template',$owebEditor->InstanceName, - $argsObj->scope); - } - break; - } - - $guiObj->askForRevision = $opObj->suggest_revision ? 1 : 0; - $guiObj->askForLog = $opObj->prompt_for_log ? 1 : 0; - - - $guiObj->scope = $owebEditor->CreateHTML(); - $guiObj->editorType = $editorCfg['type']; - switch($argsObj->doAction) - { - case "doDelete": - $guiObj->refreshTree = 1; // has to be forced - break; - - case "doCreate": - $guiObj->refreshTree = $argsObj->refreshTree; - break; - - case "doUpdate": - // IMPORTANT NOTICE - // we do not set tree refresh here, because on this situation - // tree update has to be done when reqView page is called. - // If we ask for tree refresh here we are going to do double refresh (useless and time consuming) - break; - } - - switch($argsObj->doAction) - { - case "addTestCase": - case "edit": - case "create": - case "reorder": - case "doDelete": - case "doReorder": - case "createTestCases": - case "doCreateTestCases": - case "doCreate": - case "doFreezeVersion": - case "doUnfreezeVersion": - case "doUpdate": - case "copy": - case "doCopy": - case "doCreateVersion": - case "doDeleteVersion": - case "doAddRelation": - case "doDeleteRelation": - case "doCreateRevision": - case "removeTestCase": - case "fileUpload": - case "deleteFile": - case "stopMonitoring": - case "startMonitoring": - $renderType = 'template'; - $key2loop = get_object_vars($opObj); - foreach($key2loop as $key => $value) - { - $guiObj->$key = $value; - } - - // exceptions - $guiObj->askForRevision = $opObj->suggest_revision ? 1 : 0; - $guiObj->askForLog = $opObj->prompt_for_log ? 1 : 0; - $guiObj->operation = isset($actionOpe[$argsObj->doAction]) ? $actionOpe[$argsObj->doAction] : $argsObj->doAction; - - $tplDir = (!isset($opObj->template_dir) || is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; - $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; - - $pos = strpos($tpl, '.php'); - if($pos === false) { - $tpl = $tplDir . $tpl; - } else { - $renderType = 'redirect'; - if (null != $guiObj->uploadOp && $guiObj->uploadOp->statusOK == false) { - $tpl .= "&uploadOPStatusCode=" . $guiObj->uploadOp->statusCode; - } - } - break; - } - - $req_mgr = new requirement_mgr($dbHandler); - $guiObj->last_doc_id = $req_mgr->get_last_doc_id_for_testproject($argsObj->tproject_id); - $guiObj->doAction = $argsObj->doAction; - - switch($renderType) - { - case 'template': - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($tpl); - break; - - case 'redirect': - header("Location: {$tpl}"); - exit(); - break; - - default: - break; - } -} - -/** - * - * - */ -function initialize_gui(&$dbHandler,&$argsObj,&$commandMgr) -{ - $req_spec_mgr = new requirement_spec_mgr($dbHandler); - - // new dBug($argsObj); - - $gui = $commandMgr->initGuiBean(); - $gui->req_cfg = config_get('req_cfg'); - - $gui->req_spec_id = $argsObj->req_spec_id; - if ($argsObj->req_spec_id) - { - $gui->requirements_count = $req_spec_mgr->get_requirements_count($gui->req_spec_id); - $gui->req_spec = $req_spec_mgr->get_by_id($gui->req_spec_id); - } - $gui->user_feedback = null; - $gui->main_descr = lang_get('req_spec_short'); - if (isset($gui->req_spec)) - { - $gui->main_descr .= config_get('gui_title_separator_1') . $gui->req_spec['title']; - } - $gui->action_descr = null; - - $gui->grants = new stdClass(); - $gui->grants->req_mgmt = has_rights($dbHandler,"mgt_modify_req"); - $gui->grants->mgt_view_events = has_rights($dbHandler,"mgt_view_events"); - - $gui->req_version_id = $argsObj->req_version_id; - $gui->preSelectedType = TL_REQ_TYPE_USE_CASE; - - $gui->stay_here = $argsObj->stay_here; - - return $gui; -} - - -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,'mgt_view_req') && $user->hasRight($db,'mgt_modify_req')); -} -?> \ No newline at end of file diff --git a/lib/requirements/reqExport.php b/lib/requirements/reqExport.php index 9461faa439..f13a025737 100644 --- a/lib/requirements/reqExport.php +++ b/lib/requirements/reqExport.php @@ -1,173 +1,170 @@ -doAction) -{ - case 'export': - $smarty = new TLSmarty(); - $smarty->assign('gui', $gui); - $smarty->display($templateCfg->template_dir . $templateCfg->default_template); - break; - - case 'doExport': - doExport($args,$req_spec_mgr); - break; +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +switch ($args->doAction) { + case 'export': + $smarty = new TLSmarty(); + $smarty->assign('gui', $gui); + $smarty->display($tplCfg->template_dir . $tplCfg->default_template); + break; + + case 'doExport': + doExport($args, $req_spec_mgr); + break; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req" + ]; + pageAccessCheck($db, $user, $context); +} + +/** + * init_args + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : 'export'; + $args->exportType = isset($_REQUEST['exportType']) ? $_REQUEST['exportType'] : null; + $args->req_spec_id = isset($_REQUEST['req_spec_id']) ? intval( + $_REQUEST['req_spec_id']) : null; + $args->export_filename = isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : ""; + $args->export_attachments = isset($_REQUEST['exportAttachments']) ? $_REQUEST['exportAttachments'] : ""; + + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + + if ($args->tproject_id == 0) { + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + $args->scope = isset($_REQUEST['scope']) ? $_REQUEST['scope'] : 'items'; + + return $args; +} + +/** + * initializeGui + */ +function initializeGui(&$argsObj, &$req_spec_mgr) +{ + $gui = new stdClass(); + $gui->exportTypes = $req_spec_mgr->get_export_file_types(); + $gui->exportType = $argsObj->exportType; + $gui->scope = $argsObj->scope; + $gui->tproject_id = $argsObj->tproject_id; + + switch ($argsObj->scope) { + case 'tree': + $gui->req_spec['title'] = lang_get('all_reqspecs_in_tproject'); + $gui->req_spec_id = 0; + $exportFileName = 'all-req.xml'; + break; + + case 'branch': + $gui->req_spec = $req_spec_mgr->get_by_id($argsObj->req_spec_id); + $gui->req_spec_id = $argsObj->req_spec_id; + $exportFileName = $gui->req_spec['title'] . '-req-spec.xml'; + break; + + case 'items': + $gui->req_spec = $req_spec_mgr->get_by_id($argsObj->req_spec_id); + $gui->req_spec_id = $argsObj->req_spec_id; + $exportFileName = $gui->req_spec['title'] . '-child_req.xml'; + break; + } + + $gui->export_filename = trim($argsObj->export_filename); + if ($gui->export_filename == "") { + $gui->export_filename = $exportFileName; + } + return $gui; +} + +/** + * doExport + */ +function doExport(&$argsObj, &$req_spec_mgr) +{ + $pfn = null; + switch ($argsObj->exportType) { + case 'csv': + $requirements_map = $req_spec_mgr->get_requirements( + $argsObj->req_spec_id); + $pfn = "exportReqDataToCSV"; + $fileName = 'reqs.csv'; + $content = $pfn($requirements_map); + break; + + case 'XML': + $pfn = "exportReqSpecToXML"; + $fileName = 'reqs.xml'; + $content = TL_XMLEXPORT_HEADER; + $optionsForExport['RECURSIVE'] = $argsObj->scope == 'items' ? false : true; + $optionsForExport['ATTACHMENTS'] = $argsObj->export_attachments; + + $openTag = $argsObj->scope == 'items' ? "requirements>" : 'requirement-specification>'; + + switch ($argsObj->scope) { + case 'tree': + $reqSpecSet = $req_spec_mgr->getFirstLevelInTestProject( + $argsObj->tproject_id); + $reqSpecSet = array_keys($reqSpecSet); + break; + + case 'branch': + case 'items': + $reqSpecSet = array( + $argsObj->req_spec_id + ); + break; + } + + $content .= "<" . $openTag . "\n"; + if (! is_null($reqSpecSet)) { + foreach ($reqSpecSet as $reqSpecID) { + $content .= $req_spec_mgr->$pfn($reqSpecID, + $argsObj->tproject_id, $optionsForExport); + } + } + $content .= "export_filename) ? $fileName : $argsObj->export_filename; + downloadContentsToFile($content, $fileName); + exit(); + } } - - -/** - * checkRights - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'mgt_view_req'); -} - - -/** - * init_args - * - */ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : 'export'; - $args->exportType = isset($_REQUEST['exportType']) ? $_REQUEST['exportType'] : null; - $args->req_spec_id = isset($_REQUEST['req_spec_id']) ? intval($_REQUEST['req_spec_id']) : null; - $args->export_filename = isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : ""; - $args->export_attachments = isset($_REQUEST['exportAttachments']) ? $_REQUEST['exportAttachments'] : ""; - - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - if( $args->tproject_id == 0 ) - { - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - $args->scope = isset($_REQUEST['scope']) ? $_REQUEST['scope'] : 'items'; - - return $args; -} - - -/** - * initializeGui - * - */ -function initializeGui(&$argsObj,&$req_spec_mgr) -{ - $gui = new stdClass(); - $gui->exportTypes = $req_spec_mgr->get_export_file_types(); - $gui->exportType = $argsObj->exportType; - $gui->scope = $argsObj->scope; - $gui->tproject_id = $argsObj->tproject_id; - - switch($argsObj->scope) - { - case 'tree': - $gui->req_spec['title'] = lang_get('all_reqspecs_in_tproject'); - $gui->req_spec_id = 0; - $exportFileName = 'all-req.xml'; - break; - - case 'branch': - $gui->req_spec = $req_spec_mgr->get_by_id($argsObj->req_spec_id); - $gui->req_spec_id = $argsObj->req_spec_id; - $exportFileName = $gui->req_spec['title'] . '-req-spec.xml'; - break; - - case 'items': - $gui->req_spec = $req_spec_mgr->get_by_id($argsObj->req_spec_id); - $gui->req_spec_id = $argsObj->req_spec_id; - $exportFileName = $gui->req_spec['title'] . '-child_req.xml'; - break; - - } - - $gui->export_filename = trim($argsObj->export_filename); - if($gui->export_filename == "") - { - $gui->export_filename = $exportFileName; - } - return $gui; -} - - - -/** - * doExport - * - */ -function doExport(&$argsObj,&$req_spec_mgr) -{ - $pfn = null; - switch($argsObj->exportType) - { - case 'csv': - $requirements_map = $req_spec_mgr->get_requirements($argsObj->req_spec_id); - $pfn = "exportReqDataToCSV"; - $fileName = 'reqs.csv'; - $content = $pfn($requirements_map); - break; - - case 'XML': - $pfn = "exportReqSpecToXML"; - $fileName = 'reqs.xml'; - $content = TL_XMLEXPORT_HEADER; - $optionsForExport['RECURSIVE'] = $argsObj->scope == 'items' ? false : true; - $optionsForExport['ATTACHMENTS'] = $argsObj->export_attachments; - - $openTag = $argsObj->scope == 'items' ? "requirements>" : 'requirement-specification>'; - - switch($argsObj->scope) - { - case 'tree': - $reqSpecSet = $req_spec_mgr->getFirstLevelInTestProject($argsObj->tproject_id); - $reqSpecSet = array_keys($reqSpecSet); - break; - - case 'branch': - case 'items': - $reqSpecSet = array($argsObj->req_spec_id); - break; - } - - $content .= "<" . $openTag . "\n"; - if(!is_null($reqSpecSet)) - { - foreach($reqSpecSet as $reqSpecID) - { - $content .= $req_spec_mgr->$pfn($reqSpecID,$argsObj->tproject_id,$optionsForExport); - } - } - $content .= "export_filename) ? $fileName : $argsObj->export_filename; - downloadContentsToFile($content,$fileName); - exit(); - } -} \ No newline at end of file diff --git a/lib/requirements/reqImport.php b/lib/requirements/reqImport.php index 5fc6d64c95..0eb7974d66 100644 --- a/lib/requirements/reqImport.php +++ b/lib/requirements/reqImport.php @@ -1,332 +1,322 @@ -doAction) -{ - case 'uploadFile': - $dummy = doExecuteImport($gui->fileName,$args,$req_spec_mgr,$req_mgr); - $gui->items = $dummy->items; - $gui->file_check = $dummy->file_check; - $gui->userFeedback = (array)$dummy->userFeedback; - if(array_key_exists("syntaxError", $gui->userFeedback) && count($gui->userFeedback['syntaxError']) > 0) - { - $gui->importResult = lang_get('import_syntax_error'); - } - else - { - $gui->importResult = lang_get('import_done'); - } - $gui->refreshTree = $args->refreshTree && $gui->file_check['status_ok']; - break; +tproject_id = $args->tproject_id; +checkRights($db, $args->user, $context); + +switch ($args->doAction) { + case 'uploadFile': + $dummy = doExecuteImport($gui->fileName, $args, $req_spec_mgr, $req_mgr); + $gui->items = $dummy->items; + $gui->file_check = $dummy->file_check; + $gui->userFeedback = (array) $dummy->userFeedback; + $gui->importResult = lang_get('import_done'); + if (array_key_exists("syntaxError", $gui->userFeedback) && + count($gui->userFeedback['syntaxError']) > 0) { + $gui->importResult = lang_get('import_syntax_error'); + } + $gui->refreshTree = $args->refreshTree && $gui->file_check['status_ok']; + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * doExecuteImport + */ +function doExecuteImport($fileName, &$argsObj, &$reqSpecMgr, &$reqMgr) +{ + $retval = new stdClass(); + $retval->items = array(); + $retval->msg = ''; + $retval->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $retval->userFeedback = null; + + $context = new stdClass(); + $context->tproject_id = $argsObj->tproject_id; + $context->req_spec_id = $argsObj->req_spec_id; + $context->user_id = $argsObj->user_id; + $context->importType = $argsObj->importType; + + $opts = array(); + $opts['skipFrozenReq'] = ($argsObj->skip_frozen_req ? true : false); + $opts['hitCriteria'] = $argsObj->hitCriteria; + $opts['actionOnHit'] = $argsObj->actionOnHit; + + // manage file upload process + $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; + if (($source != 'none') && ($source != '')) { + if (move_uploaded_file($source, $fileName) && + $argsObj->importType == 'XML') { + $retval->file_check['status_ok'] = (($xml = simplexml_load_file_wrapper( + $fileName)) !== false); + if (! $retval->file_check['status_ok']) { + $retval->file_check['msg'] = lang_get( + 'import_failed_xml_load_failed'); + } + } + } else { + $retval->file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('please_choose_req_file') + ); + } + + if ($retval->file_check['status_ok']) { + if ($argsObj->importType == 'XML') { + // If there is no req_spec in XML, and req_spec_id + // from context is null, we must raise an error, to avoid ghots requirements in DB + $isReqSpec = property_exists($xml, 'req_spec'); + if (! $isReqSpec && $argsObj->req_spec_id <= 0) { + $retval->file_check = array( + 'status_ok' => false, + 'msg' => lang_get('please_create_req_spec_first') + ); + } else { + $retval->items = doReqImportFromXML($reqSpecMgr, $reqMgr, $xml, + $context, $opts); + } + } else { + echo __LINE__; + die(); + $dummy = doReqImportOther($reqMgr, $fileName, $context, $opts); + $retval->items = $dummy['items']; + $retval->userFeedback = $dummy['userFeedback']; + } + unlink($fileName); + $retval->msg = lang_get('req_import_finished'); + } + + return $retval; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs() +{ + $args = new stdClass(); + $request = strings_stripSlashes($_REQUEST); + + $key = 'actionOnHit'; + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'update_last_version'; + + $key = 'hitCriteria'; + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'docid'; + + $args->req_spec_id = isset($request['req_spec_id']) ? intval( + $request['req_spec_id']) : null; + $args->importType = isset($request['importType']) ? $request['importType'] : null; + $args->emptyScope = isset($request['noEmpty']) ? $request['noEmpty'] : null; + $args->conflictSolution = isset($request['conflicts']) ? $request['conflicts'] : null; + $args->bUpload = isset($request['uploadFile']) ? 1 : 0; + + // useRecursion: used when you want to work on test project or req. spec + $args->useRecursion = isset($request['useRecursion']) ? 1 : 0; + $args->skip_frozen_req = isset($request['skip_frozen_req']) ? 1 : 0; + + $args->doAction = 'askFileName'; + $action_keys = array( + 'uploadFile', + 'executeImport' + ); + foreach ($action_keys as $action) { + if (isset($request[$action])) { + $args->doAction = $action; + break; + } + } + + $args->achecked_req = isset($request['achecked_req']) ? $request['achecked_req'] : null; + $args->tproject_id = intval($_SESSION['testprojectID']); + $args->tproject_name = $_SESSION['testprojectName']; + $args->user_id = intval( + isset($_SESSION['userID']) ? $_SESSION['userID'] : 0); + + $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; + + $args->scope = isset($_REQUEST['scope']) ? $_REQUEST['scope'] : 'items'; + + $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? $_SESSION['setting_refresh_tree_on_action'] : 0; + + return $args; +} + +/** + * initializeGui() + * create object that will be used by Smarty template + */ +function initializeGui(&$dbHandler, &$argsObj, $session, &$reqSpecMgr, &$reqMgr) +{ + $gui = new stdClass(); + $gui->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $gui->items = null; + $gui->try_upload = $argsObj->bUpload; + $gui->importResult = null; + $gui->refreshTree = false; + + $gui->doAction = $argsObj->doAction; + $gui->scope = $argsObj->scope; + $gui->req_spec = null; + $gui->req_spec_id = $argsObj->req_spec_id; + $gui->hitCriteria = $argsObj->hitCriteria; + $gui->actionOnHit = $argsObj->actionOnHit; + + switch ($gui->scope) { + case 'tree': + $gui->main_descr = sprintf(lang_get('tproject_import_req_spec'), + $argsObj->tproject_name); + $gui->importTypes = $reqSpecMgr->get_import_file_types(); + break; + + case 'branch': + $gui->req_spec = $reqSpecMgr->get_by_id($argsObj->req_spec_id); + $gui->main_descr = sprintf(lang_get('reqspec_import_req_spec'), + $gui->req_spec['title']); + $gui->importTypes = $reqSpecMgr->get_import_file_types(); + break; + + case 'items': + $gui->req_spec = $reqSpecMgr->get_by_id($argsObj->req_spec_id); + $gui->main_descr = sprintf(lang_get('reqspec_import_requirements'), + $gui->req_spec['title']); + $gui->importTypes = $reqMgr->get_import_file_types(); + break; + } + + $gui->importType = $argsObj->importType; + $gui->fileName = TL_TEMP_PATH . "importReq-" . session_id() . ".tmp"; + + $gui->importFileGui = new stdClass(); + $gui->importFileGui->importTypes = $gui->importTypes; + $gui->importFileGui->importType = $argsObj->importType; + + $file_size_limit = config_get('import_file_max_size_bytes'); + $gui->importFileGui->maxFileSize = round(strval($file_size_limit) / 1024); + $gui->importFileGui->fileSizeLimitMsg = sprintf( + lang_get('max_file_size_is'), $gui->importFileGui->maxFileSize . ' KB '); + + $gui->importFileGui->skip_frozen_req_checked = $argsObj->skip_frozen_req ? ' checked="checked" ' : ''; + + $gui->importFileGui->return_to_url = $session['basehref']; + if (is_null($argsObj->req_spec_id)) { + $gui->importFileGui->return_to_url .= "lib/project/project_req_spec_mgmt.php?id=$argsObj->tproject_id"; + } else { + $gui->importFileGui->return_to_url .= "lib/requirements/reqSpecView.php?req_spec_id=$argsObj->req_spec_id"; + } + + $gui->actionOptions = array( + 'update_last_version' => lang_get('update_last_requirement_version'), + 'create_new_version' => lang_get('create_new_requirement_version') + ); + + $gui->hitOptions = array( + 'docid' => lang_get('same_docid'), + 'title' => lang_get('same_title') + ); + + $gui->duplicate_criteria_verbose = lang_get('duplicate_req_criteria'); + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req", + "mgt_modify_req" + ]; + pageAccessCheck($db, $user, $context); +} + +/** + */ +function doReqImportFromXML(&$reqSpecMgr, &$reqMgr, &$simpleXMLObj, + $importContext, $importOptions) +{ + $items = array(); + $isReqSpec = property_exists($simpleXMLObj, 'req_spec'); + if ($isReqSpec) { + foreach ($simpleXMLObj->req_spec as $xkm) { + $dummy = $reqSpecMgr->createFromXML($xkm, + $importContext->tproject_id, $importContext->req_spec_id, + $importContext->user_id, null, $importOptions); + $items = array_merge($items, $dummy); + } + } else { + $loop2do = count($simpleXMLObj->requirement); + for ($kdx = 0; $kdx < $loop2do; $kdx ++) { + $dummy = $reqMgr->createFromXML($simpleXMLObj->requirement[$kdx], + $importContext->tproject_id, $importContext->req_spec_id, + $importContext->user_id, null, $importOptions); + $items = array_merge($items, $dummy); + } + } + return $items; +} + +/** + */ +function doReqImportOther(&$reqMgr, $fileName, $importContext, $importOptions) +{ + $impSet = loadImportedReq($fileName, $importContext->importType); + $items = array(); + + if (! is_null($impSet)) { + $reqSet = $impSet['info']; + if ($loop2do = count($reqSet)) { + for ($kdx = 0; $kdx < $loop2do; $kdx ++) { + $dummy = $reqMgr->createFromMap($reqSet[$kdx], + $importContext->tproject_id, $importContext->req_spec_id, + $importContext->user_id, null, $importOptions); + $items = array_merge($items, $dummy); + } + } + } + return array( + 'items' => $items, + 'userFeedback' => $impSet['userFeedback'] + ); } - - -$smarty = new TLSmarty; -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/** - * doExecuteImport - * - */ -function doExecuteImport($fileName,&$argsObj,&$reqSpecMgr,&$reqMgr) -{ - $retval = new stdClass(); - $retval->items = array(); - $retval->msg = ''; - $retval->file_check=array('status_ok' => 1, 'msg' => 'ok'); - $retval->userFeedback = null; - - $context = new stdClass(); - $context->tproject_id = $argsObj->tproject_id; - $context->req_spec_id = $argsObj->req_spec_id; - $context->user_id = $argsObj->user_id; - $context->importType = $argsObj->importType; - - $opts = array(); - $opts['skipFrozenReq'] = ($argsObj->skip_frozen_req ? true : false); - $opts['hitCriteria'] = $argsObj->hitCriteria; - $opts['actionOnHit'] = $argsObj->actionOnHit; - - // manage file upload process - $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; - if (($source != 'none') && ($source != '' )) - { - if (move_uploaded_file($source, $fileName)) - { - if( $argsObj->importType == 'XML' ) - { - $retval->file_check['status_ok']=!(($xml=simplexml_load_file_wrapper($fileName)) === FALSE); - if( !$retval->file_check['status_ok'] ) - { - $retval->file_check['msg'] = lang_get('import_failed_xml_load_failed'); - } - } - } - } - else - { - $retval->file_check=array('status_ok' => 0, 'msg' => lang_get('please_choose_req_file')); - } - // ---------------------------------------------------------------------------------------------- - - if($retval->file_check['status_ok']) - { - if($argsObj->importType == 'XML') - { - // If there is no req_spec in XML, and req_spec_id - // from context is null, we must raise an error, to avoid ghots requirements in DB - $isReqSpec = property_exists($xml,'req_spec'); - if(!$isReqSpec && $argsObj->req_spec_id <= 0) - { - $retval->file_check = array('status_ok' => FALSE, 'msg' => lang_get('please_create_req_spec_first')); - } - else - { - $retval->items = doReqImportFromXML($reqSpecMgr,$reqMgr,$xml,$context,$opts); - } - } - else - { - $dummy = doReqImportOther($reqMgr,$fileName,$context,$opts); - $retval->items = $dummy['items']; - $retval->userFeedback = $dummy['userFeedback']; - } - unlink($fileName); - $retval->msg = lang_get('req_import_finished'); - } - - return $retval; -} - -/* - function: - - args : - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $request = strings_stripSlashes($_REQUEST); - - - $key='actionOnHit'; - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'update_last_version'; - - $key='hitCriteria'; - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'docid'; - - - $args->req_spec_id = isset($request['req_spec_id']) ? intval($request['req_spec_id']) : null; - $args->importType = isset($request['importType']) ? $request['importType'] : null; - $args->emptyScope = isset($request['noEmpty']) ? $request['noEmpty'] : null; - $args->conflictSolution = isset($request['conflicts']) ? $request['conflicts'] : null; - $args->bUpload = isset($request['uploadFile']) ? 1 : 0; - - // useRecursion: used when you want to work on test project or req. spec - $args->useRecursion = isset($request['useRecursion']) ? 1 : 0; - $args->skip_frozen_req = isset($request['skip_frozen_req']) ? 1 : 0; - - - $args->doAction='askFileName'; - $action_keys = array('uploadFile','executeImport'); - foreach($action_keys as $action) - { - if( isset($request[$action]) ) - { - $args->doAction=$action; - break; - } - } - - $args->achecked_req = isset($request['achecked_req']) ? $request['achecked_req'] : null; - $args->tproject_id = intval($_SESSION['testprojectID']); - $args->tproject_name = $_SESSION['testprojectName']; - $args->user_id = intval(isset($_SESSION['userID']) ? $_SESSION['userID'] : 0); - $args->scope = isset($_REQUEST['scope']) ? $_REQUEST['scope'] : 'items'; - - $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? - $_SESSION['setting_refresh_tree_on_action'] : 0; - - return $args; -} - - - - - -/** - * initializeGui() - * create object that will be used by Smarty template - * - */ -function initializeGui(&$dbHandler,&$argsObj,$session,&$reqSpecMgr,&$reqMgr) -{ - $gui=new stdClass(); - $gui->file_check = array('status_ok' => 1, 'msg' => 'ok'); - $gui->items=null; - $gui->try_upload = $argsObj->bUpload; - $gui->importResult = null; - $gui->refreshTree = false; - - $gui->doAction=$argsObj->doAction; - $gui->scope = $argsObj->scope; - $gui->req_spec = null; - $gui->req_spec_id = $argsObj->req_spec_id; - $gui->hitCriteria = $argsObj->hitCriteria; - $gui->actionOnHit = $argsObj->actionOnHit; - - switch($gui->scope) - { - case 'tree': - $gui->main_descr = sprintf(lang_get('tproject_import_req_spec'),$argsObj->tproject_name); - $gui->importTypes = $reqSpecMgr->get_import_file_types(); - break; - - case 'branch': - $gui->req_spec = $reqSpecMgr->get_by_id($argsObj->req_spec_id); - $gui->main_descr = sprintf(lang_get('reqspec_import_req_spec'),$gui->req_spec['title']); - $gui->importTypes = $reqSpecMgr->get_import_file_types(); - break; - - case 'items': - $gui->req_spec = $reqSpecMgr->get_by_id($argsObj->req_spec_id); - $gui->main_descr = sprintf(lang_get('reqspec_import_requirements'),$gui->req_spec['title']); - $gui->importTypes = $reqMgr->get_import_file_types(); - break; - } - - - $gui->importType = $argsObj->importType; - $gui->fileName = TL_TEMP_PATH . "importReq-" . session_id() . ".tmp"; - - $gui->importFileGui = new stdClass(); - $gui->importFileGui->importTypes = $gui->importTypes; - $gui->importFileGui->importType = $argsObj->importType; - - $file_size_limit = config_get('import_file_max_size_bytes'); - $gui->importFileGui->maxFileSize=round(strval($file_size_limit)/1024); - $gui->importFileGui->fileSizeLimitMsg=sprintf(lang_get('max_file_size_is'), $gui->importFileGui->maxFileSize . ' KB '); - - - $gui->importFileGui->skip_frozen_req_checked = $argsObj->skip_frozen_req ? ' checked="checked" ' : ''; - - - $gui->importFileGui->return_to_url=$session['basehref']; - if( is_null($argsObj->req_spec_id) ) - { - $gui->importFileGui->return_to_url .= "lib/project/project_req_spec_mgmt.php?id=$argsObj->tproject_id"; - } - else - { - $gui->importFileGui->return_to_url .= "lib/requirements/reqSpecView.php?req_spec_id=$argsObj->req_spec_id"; - } - - $gui->actionOptions=array('update_last_version' => lang_get('update_last_requirement_version'), - 'create_new_version' => lang_get('create_new_requirement_version')); - - $gui->hitOptions=array('docid' => lang_get('same_docid'),'title' => lang_get('same_title')); - - $gui->duplicate_criteria_verbose = lang_get('duplicate_req_criteria'); - - return $gui; -} - - -/** - * - * - */ -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,'mgt_view_req') && $user->hasRight($db,'mgt_modify_req')); -} - - -/** - * - * - */ -function doReqImportFromXML(&$reqSpecMgr,&$reqMgr,&$simpleXMLObj,$importContext,$importOptions) -{ - $items = array(); - $isReqSpec = property_exists($simpleXMLObj,'req_spec'); - if($isReqSpec) - { - foreach($simpleXMLObj->req_spec as $xkm) - { - $dummy = $reqSpecMgr->createFromXML($xkm,$importContext->tproject_id,$importContext->req_spec_id, - $importContext->user_id,null,$importOptions); - $items = array_merge($items,$dummy); - } - } - else - { - $loop2do = count($simpleXMLObj->requirement); - for($kdx=0; $kdx < $loop2do; $kdx++) - { - $dummy = $reqMgr->createFromXML($simpleXMLObj->requirement[$kdx],$importContext->tproject_id, - $importContext->req_spec_id,$importContext->user_id,null,$importOptions); - $items = array_merge($items,$dummy); - } - } - return $items; -} - - -/** - * - * - */ -function doReqImportOther(&$reqMgr,$fileName,$importContext,$importOptions) -{ - $impSet = loadImportedReq($fileName, $importContext->importType); - $items = array(); - - if( !is_null($impSet) ) - { - $reqSet = $impSet['info']; - if( ($loop2do=count($reqSet)) ) - { - for($kdx=0; $kdx < $loop2do; $kdx++) - { - $dummy = $reqMgr->createFromMap($reqSet[$kdx],$importContext->tproject_id, - $importContext->req_spec_id, - $importContext->user_id,null,$importOptions); - $items = array_merge($items,$dummy); - } - } - } - return array('items' => $items, 'userFeedback' => $impSet['userFeedback']); -} \ No newline at end of file diff --git a/lib/requirements/reqMonitorOverview.php b/lib/requirements/reqMonitorOverview.php index 489537f1a3..9703ccc9c8 100644 --- a/lib/requirements/reqMonitorOverview.php +++ b/lib/requirements/reqMonitorOverview.php @@ -1,284 +1,316 @@ -reqIDSet) > 0) -{ - $pathCache = null; - $imgSet = $smarty->getImages(); - - // get type and status labels - $lbl = getLabels($cfg->req); - $reqSet = $req_mgr->getByIDBulkLatestVersionRevision($gui->reqIDSet,array('outputFormat' => 'mapOfArray')); - $onClick = buildOnClick($args,$lbl['mixed'],$imgSet); - - if( $args->req_id > 0 ) - { - $vk = array_flip(array('on','off')); - if( isset($vk[$args->action]) ) - { - $m2c = 'monitor' . ucfirst($args->action); - $req_mgr->$m2c($args->req_id,$args->userID,$args->tproject_id); - } - } - - // array to gather table data row per row - $rows = array(); - - $monitoredSet = $req_mgr->getMonitoredByUser($args->userID,$args->tproject_id); - - foreach($gui->reqIDSet as $id) - { - $req = $reqSet[$id][0]; - - // create the link to display - $title = htmlentities($req['req_doc_id'], ENT_QUOTES, $cfg->charset) . $cfg->glue_char . - htmlentities($req['title'], ENT_QUOTES, $cfg->charset); - - // reqspec-"path" to requirement - if( !isset($pathCache[$req['srs_id']]) ) - { - $path = $req_mgr->tree_mgr->get_path($req['srs_id']); - foreach ($path as $key => $p) - { - $path[$key] = $p['name']; - } - $pathCache[$req['srs_id']] = htmlentities(implode("/", $path), ENT_QUOTES, $cfg->charset); - } - - // get content for each row to display - $result = array(); - $result[] = $pathCache[$req['srs_id']]; - - $edit_link = '' . - ' '; - - $result[] = '' . $edit_link . $title; - - // use html comment to sort properly by this columns (extjs) - $result[] = "" . - localizeTimeStamp($req['creation_ts'],$cfg->datetime) . " ({$req['author']})"; - - $action = 'on'; - foreach($monitoredSet as $monReqID => $dummy) - { - if($req["id"] == $monReqID) - { - $action = 'off'; - break; - } - } - $result[] = $onClick[$action]['open'] . $req["id"] . - $onClick[$action]['close']; - - $rows[] = $result; - } - - - - - - // -------------------------------------------------------------------------------------------------- - // Construction of EXT-JS table starts here - if(($gui->row_qty = count($rows)) > 0 ) - { - - /** - * get column header titles for the table - * - * IMPORTANT: - * the order of following items in this array has to be - * the same as row content above!!! - * - * should be: - * 1. path, 2. title, 3. created_on, 4. monitor - */ - $columns = array(); - $columns[] = array('title_key' => 'req_spec_short', 'width' => 150); - $columns[] = array('title_key' => 'title', 'width' => 150); - $columns[] = array('title_key' => 'created_on', 'width' => 100); - $columns[] = array('title_key' => 'monitor', 'width' => 100); - - // create table object, fill it with columns and row data and give it a title - $matrix = new tlExtTable($columns, $rows, 'tl_table_req_overview'); - $matrix->title = $lbl['mixed']['requirements']; - - // group by Req Spec - $matrix->setGroupByColumnName($lbl['mixed']['req_spec_short']); - - // sort by coverage descending if enabled, otherwise by status - $sort_name = ($cfg->req->expected_coverage_management) ?$lbl['mixed']['th_coverage'] : $lbl['mixed']['status']; - $matrix->setSortByColumnName($sort_name); - $matrix->sortDirection = 'DESC'; - - // define toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - $matrix->toolbarRefreshButton = true; - $matrix->showGroupItemsCount = true; - - // show custom field content in multiple lines - $matrix->addCustomBehaviour('text', array('render' => 'columnWrap')); - $gui->tableSet= array($matrix); - } - -} - - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * initialize user input - * - * @param resource &$tproject_mgr reference to testproject manager - * @return array $args array with user input information - */ -function init_args(&$tproject_mgr) -{ - $args = new stdClass(); - - $i2get = array("tproject_id" => array(tlInputParameter::INT_N), - "req_id" => array(tlInputParameter::INT_N), - "action" => array(tlInputParameter::STRING_N,2,3)); - - $args = new stdClass(); - R_PARAMS($i2get,$args); - - if( $args->tproject_id <= 0 ) - { - throw new Exception("Test project is mandatory", 1); - } - - $item = $tproject_mgr->get_by_id($args->tproject_id); - $args->tproject_name = $item['name']; - - - $args->req_id = intval($args->req_id); - $args->userID = $_SESSION['currentUser']->dbID; - - return $args; +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$cfg = getCfg(); + +$smarty = new TLSmarty(); + +if (! empty($gui->reqIDSet)) { + $pathCache = null; + $imgSet = $smarty->getImages(); + + // get type and status labels + $lbl = getLabels($cfg->req); + $reqSet = $req_mgr->getByIDBulkLatestVersionRevision($gui->reqIDSet, + array( + 'outputFormat' => 'mapOfArray' + )); + $onClick = buildOnClick($args, $lbl['mixed'], $imgSet); + + if ($args->req_id > 0) { + $vk = array_flip(array( + 'on', + 'off' + )); + if (isset($vk[$args->action])) { + $m2c = 'monitor' . ucfirst($args->action); + $req_mgr->$m2c($args->req_id, $args->userID, $args->tproject_id); + } + } + + // array to gather table data row per row + $rows = array(); + + $monitoredSet = $req_mgr->getMonitoredByUser($args->userID, + $args->tproject_id); + + foreach ($gui->reqIDSet as $id) { + $req = $reqSet[$id][0]; + + // create the link to display + $title = htmlentities($req['req_doc_id'], ENT_QUOTES, $cfg->charset) . + $cfg->glue_char . + htmlentities($req['title'], ENT_QUOTES, $cfg->charset); + + // reqspec-"path" to requirement + if (! isset($pathCache[$req['srs_id']])) { + $path = $req_mgr->tree_mgr->get_path($req['srs_id']); + foreach ($path as $key => $p) { + $path[$key] = $p['name']; + } + $pathCache[$req['srs_id']] = htmlentities(implode("/", $path), + ENT_QUOTES, $cfg->charset); + } + + // get content for each row to display + $result = array(); + $result[] = $pathCache[$req['srs_id']]; + + $edit_link = '' . ' '; + + $result[] = '' . $edit_link . $title; + + // use html comment to sort properly by this columns (extjs) + $result[] = "" . + localizeTimeStamp($req['creation_ts'], $cfg->datetime) . + " ({$req['author']})"; + + $action = 'on'; + if (! empty($monitoredSet)) { + foreach ($monitoredSet as $monReqID => $dummy) { + if ($req["id"] == $monReqID) { + $action = 'off'; + break; + } + } + } + $result[] = $onClick[$action]['open'] . $req["id"] . + $onClick[$action]['close']; + + $rows[] = $result; + } + + // Construction of EXT-JS table starts here + if (($gui->row_qty = count($rows)) > 0) { + + /** + * get column header titles for the table + * + * IMPORTANT: + * the order of following items in this array has to be + * the same as row content above!!! + * + * should be: + * 1. path, 2. title, 3. created_on, 4. monitor + */ + $columns = array(); + $columns[] = array( + 'title_key' => 'req_spec_short', + 'width' => 150 + ); + $columns[] = array( + 'title_key' => 'title', + 'width' => 150 + ); + $columns[] = array( + 'title_key' => 'created_on', + 'width' => 100 + ); + $columns[] = array( + 'title_key' => 'monitor', + 'width' => 100 + ); + + // create table object, fill it with columns and row data and give it a title + $matrix = new tlExtTable($columns, $rows, 'tl_table_req_overview'); + $matrix->title = $lbl['mixed']['requirements']; + + // group by Req Spec + $matrix->setGroupByColumnName($lbl['mixed']['req_spec_short']); + + // sort by coverage descending if enabled, otherwise by status + $sort_name = ($cfg->req->expected_coverage_management) ? $lbl['mixed']['th_coverage'] : $lbl['mixed']['status']; + $matrix->setSortByColumnName($sort_name); + $matrix->sortDirection = 'DESC'; + + // define toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + $matrix->toolbarRefreshButton = true; + $matrix->showGroupItemsCount = true; + + // show custom field content in multiple lines + $matrix->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + $gui->tableSet = array( + $matrix + ); + } +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * initialize user input + * + * @param + * resource &$tproject_mgr reference to testproject manager + * @return array $args array with user input information + */ +function initArgs(&$tproject_mgr) +{ + $i2get = array( + "tproject_id" => array( + tlInputParameter::INT_N + ), + "req_id" => array( + tlInputParameter::INT_N + ), + "action" => array( + tlInputParameter::STRING_N, + 2, + 3 + ) + ); + + $args = new stdClass(); + R_PARAMS($i2get, $args); + + if ($args->tproject_id <= 0) { + throw new Exception("Test project is mandatory", 1); + } + + $item = $tproject_mgr->get_by_id($args->tproject_id); + $args->tproject_name = $item['name']; + + $args->req_id = intval($args->req_id); + $args->userID = $_SESSION['currentUser']->dbID; + + return $args; +} + +/** + * initialize GUI + * + * @param stdClass $argsObj + * reference to user input + * @return stdClass $gui gui data + */ +function initializeGui(&$argsObj, &$tprojectMgr) +{ + $gui = new stdClass(); + + $gui->pageTitle = lang_get('caption_req_monitor_overview'); + $gui->tproject_name = $argsObj->tproject_name; + $gui->tableSet = null; + $gui->reqIDSet = $tprojectMgr->get_all_requirement_ids( + $argsObj->tproject_id); + + return $gui; +} + +/** + */ +function getCfg() +{ + $cfg = new stdClass(); + $cfg->glue_char = config_get('gui_title_separator_1'); + $cfg->charset = config_get('charset'); + $cfg->req = config_get('req_cfg'); + $cfg->date = config_get('date_format'); + $cfg->datetime = config_get('timestamp_format'); + + return $cfg; +} + +/** + */ +function getLabels($reqCfg) +{ + $lbl = array(); + + $l2get = array( + 'no' => 'No', + 'yes' => 'Yes', + 'not_aplicable' => null, + 'never' => null, + 'req_spec_short' => null, + 'title' => null, + 'version' => null, + 'th_coverage' => null, + 'frozen' => null, + 'type' => null, + 'status' => null, + 'th_relations' => null, + 'requirements' => null, + 'number_of_reqs' => null, + 'number_of_versions' => null, + 'requirement' => null, + 'monitor' => null, + 'version_revision_tag' => null, + 'week_short' => 'calendar_week_short', + 'on2off' => 'on_turn_off', + 'off2on' => 'off_turn_on' + ); + + $lbl['mixed'] = init_labels($l2get); + $lbl['type'] = init_labels($reqCfg->type_labels); + $lbl['status'] = init_labels($reqCfg->status_labels); + + return $lbl; +} + +/** + */ +function buildOnClick($args, $lbl, $imgSet) +{ + $ret = array(); + $ret['off']['open'] = '

    tproject_id}&req_id="; + $ret['off']['close'] = '">
    '; + + $ret['on']['open'] = str_replace('=off', '=on', $ret['off']['open']); + $ret['on']['close'] = '">'; + + return $ret; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req" + ]; + pageAccessCheck($db, $user, $context); } -/** - * initialize GUI - * - * @param stdClass $argsObj reference to user input - * @return stdClass $gui gui data - */ -function initializeGui(&$argsObj,&$tprojectMgr) -{ - $gui = new stdClass(); - - $gui->pageTitle = lang_get('caption_req_monitor_overview'); - $gui->tproject_name = $argsObj->tproject_name; - $gui->tableSet = null; - $gui->reqIDSet = $tprojectMgr->get_all_requirement_ids($argsObj->tproject_id); - - return $gui; -} - - -/** - * - */ -function getCfg() -{ - $cfg = new stdClass(); - $cfg->glue_char = config_get('gui_title_separator_1'); - $cfg->charset = config_get('charset'); - $cfg->req = config_get('req_cfg'); - $cfg->date = config_get('date_format'); - $cfg->datetime = config_get('timestamp_format'); - - return $cfg; -} - - -/** - * - */ -function getLabels($reqCfg) -{ - $lbl = array(); - - $l2get = array('no' => 'No', 'yes' => 'Yes', - 'not_aplicable' => null,'never' => null, - 'req_spec_short' => null,'title' => null, - 'version' => null, 'th_coverage' => null, - 'frozen' => null, 'type'=> null, - 'status' => null,'th_relations' => null, - 'requirements' => null,'number_of_reqs' => null, - 'number_of_versions' => null, - 'requirement' => null, 'monitor' => null, - 'version_revision_tag' => null, - 'week_short' => 'calendar_week_short', - 'on2off' => 'on_turn_off', 'off2on' => 'off_turn_on'); - - $lbl['mixed'] = init_labels($l2get); - $lbl['type'] = init_labels($reqCfg->type_labels); - $lbl['status'] = init_labels($reqCfg->status_labels); - - return $lbl; -} - -/** - * - */ -function buildOnClick($args,$lbl,$imgSet) -{ - $ret = array(); - $ret['off']['open'] = '
    tproject_id}&req_id="; - $ret['off']['close'] = '">
    '; - - $ret['on']['open'] = str_replace('=off','=on',$ret['off']['open']); - $ret['on']['close'] = '">'; - - - return $ret; -} - -/* - * rights check function for testlinkInitPage() - */ -function checkRights(&$db, &$user) -{ - return $user->hasRight($db,'mgt_view_req'); -} - diff --git a/lib/requirements/reqOverview.php b/lib/requirements/reqOverview.php index 3ff1167526..7c2e1d26d4 100644 --- a/lib/requirements/reqOverview.php +++ b/lib/requirements/reqOverview.php @@ -1,405 +1,480 @@ -reqIDs = $tproject_mgr->get_all_requirement_ids($args->tproject_id); - -$smarty = new TLSmarty(); -if(count($gui->reqIDs) > 0) { - $chronoStart = microtime(true); - - $pathCache = null; - $imgSet = $smarty->getImages(); - $gui->warning_msg = ''; - - // get type and status labels - $type_labels = init_labels($cfg->req->type_labels); - $status_labels = init_labels($cfg->req->status_labels); - - $labels2get = array('no' => 'No', 'yes' => 'Yes', - 'not_aplicable' => null,'never' => null, - 'req_spec_short' => null,'title' => null, - 'version' => null, 'th_coverage' => null, - 'frozen' => null, 'type'=> null,'status' => null, - 'th_relations' => null, 'requirements' => null, - 'number_of_reqs' => null, 'number_of_versions' => null, - 'requirement' => null, - 'version_revision_tag' => null, - 'week_short' => 'calendar_week_short'); - - $labels = init_labels($labels2get); - - $gui->cfields4req = (array)$cfield_mgr->get_linked_cfields_at_design($args->tproject_id, 1, null, 'requirement', null, 'name'); - $gui->processCF = count($gui->cfields4req) > 0; - - - $coverageSet = null; - $relationCounters = null; - - $version_option = $args->all_versions ? requirement_mgr::ALL_VERSIONS : requirement_mgr::LATEST_VERSION; - if( $version_option == requirement_mgr::LATEST_VERSION ) { - $reqSet = $req_mgr->getByIDBulkLatestVersionRevision($gui->reqIDs,array('outputFormat' => 'mapOfArray')); - } - else { - $reqSet = $req_mgr->get_by_id($gui->reqIDs, $version_option,null,array('output_format' => 'mapOfArray')); - } - - // conditions to generate reqVersion Set - // having this set, is useful to try to improve performance - // to get Custom Fields when req qty > 2000 - // - if ( $cfg->req->expected_coverage_management || - $gui->processCF ) { - $xSet = array_keys($reqSet); - - foreach($xSet as $rqID) { - $reqVersionSet[] = $reqSet[$rqID][0]['version_id']; - } - } - - if($cfg->req->expected_coverage_management) { - - $coverageSet = $req_mgr->getLatestReqVersionCoverageCounterSet($reqVersionSet); - } - - if($cfg->req->relations->enable) { - $relationCounters = $req_mgr->getRelationsCounters($gui->reqIDs); - } - - if ($gui->processCF) { - // get custom field values bulk - $cfByReqVer = (array)$req_mgr->get_linked_cfields(null,$reqVersionSet,$args->tproject_id,array('access_key' => 'node_id')); - } - - - // array to gather table data row per row - $rows = array(); - - foreach($gui->reqIDs as $id) { - - $req = $reqSet[$id]; - - // create the link to display - $title = htmlentities($req[0]['req_doc_id'], ENT_QUOTES, $cfg->charset) . - $cfg->glue_char . - htmlentities($req[0]['title'], ENT_QUOTES, $cfg->charset); - - // reqspec-"path" to requirement - if( !isset($pathCache[$req[0]['srs_id']]) ) { - $path = $req_mgr->tree_mgr->get_path($req[0]['srs_id']); - foreach ($path as $key => $p) { - $path[$key] = $p['name']; - } - $pathCache[$req[0]['srs_id']] = htmlentities(implode("/", $path), ENT_QUOTES, $cfg->charset); - } - - - # get all cfield ids we have columns for in the req overview - $cfield_ids = array(); - foreach ($gui->cfields4req as $cf){ - $cfield_ids[] = $cf['id']; - } - - - foreach($req as $version) { - // get content for each row to display - $result = array(); - - /** - * IMPORTANT: - * the order of following items in this array has to be - * the same as column headers are below!!! - * - * should be: - * 1. path - * 2. title - * 3. version - * 4. frozen (is_open attribute) - * 5. coverage (if enabled) - * 6. type - * 7. status - * 8. relations (if enabled) - * 9. all custom fields in order of $fields - */ - - $result[] = $pathCache[$req[0]['srs_id']]; - - $edit_link = '' . - ' '; - - $result[] = '' . $edit_link . $title; - - // version and revision number - // $version_revision = sprintf($labels['version_revision_tag'],$version['version'],$version['revision']); - // $padded_data = sprintf("%05d%05d", $version['version'], $version['revision']); - - // use html comment to sort properly by this column (extjs) - // USE CARVED IN THE STONE [vxxsyy] to save function calls. - $result[] = "" . - "[v{$version['version']}r{$version['revision']}]"; - - // use html comment to sort properly by this columns (extjs) - $result[] = "" . localizeTimeStamp($version['creation_ts'],$cfg->datetime) . - " ({$version['author']})"; - - // 20140914 - - // Because we can do this logic thoundands of times, I suppose it will cost less - // to do not use my other approach of firts assigning instead of using else. - // - // use html comment to sort properly by this column (extjs) - if( !is_null($version['modification_ts']) && ($version['modification_ts'] != $cfg->neverModifiedTS) ) { - $result[] = "" . localizeTimeStamp($version['modification_ts'],$cfg->datetime) . - " ({$version['modifier']})"; - } - else { - $result[] = "" . $labels['never']; - } - - - // is it frozen? - $result[] = ($version['is_open']) ? $labels['no'] : $labels['yes']; - - // coverage - // use html comment to sort properly by this columns (extjs) - if($cfg->req->expected_coverage_management) { - $tc_coverage = isset($coverageSet[$id]) ? $coverageSet[$id]['qty'] : 0; - $expected = $version['expected_coverage']; - $coverage_string = "" . $labels['not_aplicable'] . " ($tc_coverage/0)"; - if ($expected > 0) { - $percentage = round(100 / $expected * $tc_coverage, 2); - $padded_data = sprintf("%010d", $percentage); //bring all percentages to same length - $coverage_string = " {$percentage}% ({$tc_coverage}/{$expected})"; - } - $result[] = $coverage_string; - } - - $result[] = isset($type_labels[$version['type']]) ? $type_labels[$version['type']] : ''; - $result[] = isset($status_labels[$version['status']]) ? $status_labels[$version['status']] : ''; - - if ($cfg->req->relations->enable) { - $rx = isset($relationCounters[$id]) ? $relationCounters[$id] : 0; - $result[] = "" . $rx; - } - - #8792: append one item to $result for every displayed column (no content?: append empty string) - if($gui->processCF) { - $linkedCFWithContent = array(); - if ( isset($cfByReqVer[$version['version_id']])) { - $linkedCFWithContent = $cfByReqVer[$version['version_id']]; - } - - foreach ($cfield_ids as $cf_id) { - if (isset($linkedCFWithContent[$cf_id])) { - $cf = $linkedCFWithContent[$cf_id]; - $verbose_type = $req_mgr->cfield_mgr->custom_field_types[$cf['type']]; - $value = preg_replace('!\s+!', ' ', htmlspecialchars($cf['value'], ENT_QUOTES, $cfg->charset)); - if( ($verbose_type == 'date' || $verbose_type == 'datetime') && is_numeric($value) && $value != 0 ) { - $value = strftime( $cfg->$verbose_type . " ({$labels['week_short']} %W)" , $value); #fix typo: missing 's' in labels - } - $result[] = $value; - } - else { - $result[] = ''; - continue; - } - } - } - - $rows[] = $result; - } - } - - // -------------------------------------------------------------------- - // Construction of EXT-JS table starts here - if(($gui->row_qty = count($rows)) > 0 ) { - $version_string = ($args->all_versions) ? $labels['number_of_versions'] : $labels['number_of_reqs']; - $gui->pageTitle .= " - " . $version_string . ": " . $gui->row_qty; - - /** - * get column header titles for the table - * - * IMPORTANT: - * the order of following items in this array has to be - * the same as row content above!!! - * - * should be: - * 1. path - * 2. title - * 3. version - * 4. frozen - * 5. coverage (if enabled) - * 6. type - * 7. status - * 8. relations (if enabled) - * 9. then all custom fields in order of $fields - */ - $columns = array(); - $columns[] = array('title_key' => 'req_spec_short', 'width' => 200); - $columns[] = array('title_key' => 'title', 'width' => 150); - $columns[] = array('title_key' => 'version', 'width' => 30); - $columns[] = array('title_key' => 'created_on', 'width' => 55); - $columns[] = array('title_key' => 'modified_on','width' => 55); - - $frozen_for_filter = array($labels['yes'],$labels['no']); - $columns[] = array('title_key' => 'frozen', 'width' => 30, 'filter' => 'list', - 'filterOptions' => $frozen_for_filter); - - if($cfg->req->expected_coverage_management) { - $columns[] = array('title_key' => 'th_coverage', 'width' => 80); - } - - $columns[] = array('title_key' => 'type', 'width' => 60, 'filter' => 'list', - 'filterOptions' => $type_labels); - $columns[] = array('title_key' => 'status', 'width' => 60, 'filter' => 'list', - 'filterOptions' => $status_labels); - - if ($cfg->req->relations->enable) { - $columns[] = array('title_key' => 'th_relations', 'width' => 50, 'filter' => 'numeric'); - } - - foreach($gui->cfields4req as $cf) { - $columns[] = array('title' => htmlentities($cf['label'], ENT_QUOTES, $cfg->charset), 'type' => 'text', - 'col_id' => 'id_cf_' .$cf['name']); - } - - // create table object, fill it with columns and row data and give it a title - $matrix = new tlExtTable($columns, $rows, 'tl_table_req_overview'); - $matrix->title = $labels['requirements']; - - // group by Req Spec - $matrix->setGroupByColumnName($labels['req_spec_short']); - - // sort by coverage descending if enabled, otherwise by status - $sort_name = ($cfg->req->expected_coverage_management) ? $labels['th_coverage'] : $labels['status']; - $matrix->setSortByColumnName($sort_name); - $matrix->sortDirection = 'DESC'; - - // define toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - $matrix->toolbarRefreshButton = true; - $matrix->showGroupItemsCount = true; - - // show custom field content in multiple lines - $matrix->addCustomBehaviour('text', array('render' => 'columnWrap')); - $gui->tableSet= array($matrix); - } - - $chronoStop = microtime(true); - $gui->elapsedSeconds = round($chronoStop - $chronoStart); -} - - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * initialize user input - * - * @param resource &$tproject_mgr reference to testproject manager - * @return array $args array with user input information - */ -function init_args(&$tproject_mgr) { - $args = new stdClass(); - - $all_versions = isset($_REQUEST['all_versions']) ? true : false; - $all_versions_hidden = isset($_REQUEST['all_versions_hidden']) ? true : false; - if ($all_versions) { - $selection = true; - } else if ($all_versions_hidden) { - $selection = false; - } else if (isset($_SESSION['all_versions'])) { - $selection = $_SESSION['all_versions']; - } else { - $selection = false; - } - $args->all_versions = $_SESSION['all_versions'] = $selection; - - $args->tproject_id = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; - - if($args->tproject_id > 0) { - $tproject_info = $tproject_mgr->get_by_id($args->tproject_id); - $args->tproject_name = $tproject_info['name']; - $args->tproject_description = $tproject_info['notes']; - } - - return $args; +tproject_id = $args->tproject_id; +checkRights($db, $args->user, $ctx); + +$gui->reqIDs = $tproject_mgr->get_all_requirement_ids($args->tproject_id); + +$smarty = new TLSmarty(); +if (count($gui->reqIDs) > 0) { + $chronoStart = microtime(true); + + $pathCache = null; + $imgSet = $smarty->getImages(); + $gui->warning_msg = ''; + + // get type and status labels + $type_labels = init_labels($cfg->req->type_labels); + $status_labels = init_labels($cfg->req->status_labels); + + $labels2get = array( + 'no' => 'No', + 'yes' => 'Yes', + 'not_aplicable' => null, + 'never' => null, + 'req_spec_short' => null, + 'title' => null, + 'version' => null, + 'th_coverage' => null, + 'frozen' => null, + 'type' => null, + 'status' => null, + 'th_relations' => null, + 'requirements' => null, + 'number_of_reqs' => null, + 'number_of_versions' => null, + 'requirement' => null, + 'version_revision_tag' => null, + 'week_short' => 'calendar_week_short' + ); + + $labels = init_labels($labels2get); + + $gui->cfields4req = (array) $cfield_mgr->get_linked_cfields_at_design( + $args->tproject_id, 1, null, 'requirement', null, 'name'); + $gui->processCF = count($gui->cfields4req) > 0; + + $coverageSet = null; + $relationCounters = null; + + $version_option = $args->all_versions ? requirement_mgr::ALL_VERSIONS : requirement_mgr::LATEST_VERSION; + if ($version_option == requirement_mgr::LATEST_VERSION) { + $reqSet = $req_mgr->getByIDBulkLatestVersionRevision($gui->reqIDs, + array( + 'outputFormat' => 'mapOfArray' + )); + } else { + $reqSet = $req_mgr->get_by_id($gui->reqIDs, $version_option, null, + array( + 'output_format' => 'mapOfArray' + )); + } + + // conditions to generate reqVersion Set + // having this set, is useful to try to improve performance + // to get Custom Fields when req qty > 2000 + // + if ($cfg->req->expected_coverage_management || $gui->processCF) { + $xSet = array_keys($reqSet); + + foreach ($xSet as $rqID) { + $reqVersionSet[] = $reqSet[$rqID][0]['version_id']; + } + } + + if ($cfg->req->expected_coverage_management) { + + $coverageSet = $req_mgr->getLatestReqVersionCoverageCounterSet( + $reqVersionSet); + } + + if ($cfg->req->relations->enable) { + $relationCounters = $req_mgr->getRelationsCounters($gui->reqIDs); + } + + if ($gui->processCF) { + // get custom field values bulk + $cfByReqVer = (array) $req_mgr->get_linked_cfields(null, $reqVersionSet, + $args->tproject_id, array( + 'access_key' => 'node_id' + )); + } + + // array to gather table data row per row + $rows = array(); + + foreach ($gui->reqIDs as $id) { + + $req = $reqSet[$id]; + + // create the link to display + $title = htmlentities($req[0]['req_doc_id'], ENT_QUOTES, $cfg->charset) . + $cfg->glue_char . + htmlentities($req[0]['title'], ENT_QUOTES, $cfg->charset); + + // reqspec-"path" to requirement + if (! isset($pathCache[$req[0]['srs_id']])) { + $path = $req_mgr->tree_mgr->get_path($req[0]['srs_id']); + foreach ($path as $key => $p) { + $path[$key] = $p['name']; + } + $pathCache[$req[0]['srs_id']] = htmlentities(implode("/", $path), + ENT_QUOTES, $cfg->charset); + } + + # get all cfield ids we have columns for in the req overview + $cfield_ids = array(); + foreach ($gui->cfields4req as $cf) { + $cfield_ids[] = $cf['id']; + } + + foreach ($req as $version) { + // get content for each row to display + $result = array(); + + /** + * IMPORTANT: + * the order of following items in this array has to be + * the same as column headers are below!!! + * + * should be: + * 1. path + * 2. title + * 3. version + * 4. frozen (is_open attribute) + * 5. coverage (if enabled) + * 6. type + * 7. status + * 8. relations (if enabled) + * 9. all custom fields in order of $fields + */ + + $result[] = $pathCache[$req[0]['srs_id']]; + + $edit_link = '' . ' '; + + $result[] = '' . $edit_link . $title; + + // version and revision number + // $version_revision = sprintf($labels['version_revision_tag'],$version['version'],$version['revision']); + // $padded_data = sprintf("%05d%05d", $version['version'], $version['revision']); + + // use html comment to sort properly by this column (extjs) + // USE CARVED IN THE STONE [vxxsyy] to save function calls. + $result[] = "" . "[v{$version['version']}r{$version['revision']}]"; + + // use html comment to sort properly by this columns (extjs) + $result[] = "" . + localizeTimeStamp($version['creation_ts'], $cfg->datetime) . + " ({$version['author']})"; + + // 20140914 - + // Because we can do this logic thoundands of times, I suppose it will cost less + // to do not use my other approach of firts assigning instead of using else. + // + // use html comment to sort properly by this column (extjs) + if (! is_null($version['modification_ts']) && + ($version['modification_ts'] != $cfg->neverModifiedTS)) { + $result[] = "" . + localizeTimeStamp($version['modification_ts'], + $cfg->datetime) . " ({$version['modifier']})"; + } else { + $result[] = "" . $labels['never']; + } + + // is it frozen? + $result[] = ($version['is_open']) ? $labels['no'] : $labels['yes']; + + // coverage + // use html comment to sort properly by this columns (extjs) + if ($cfg->req->expected_coverage_management) { + $tc_coverage = isset($coverageSet[$id]) ? $coverageSet[$id]['qty'] : 0; + $expected = $version['expected_coverage']; + $coverage_string = "" . $labels['not_aplicable'] . + " ($tc_coverage/0)"; + if ($expected > 0) { + $percentage = round(100 / $expected * $tc_coverage, 2); + $padded_data = sprintf("%010d", $percentage); // bring all percentages to same length + $coverage_string = " {$percentage}% ({$tc_coverage}/{$expected})"; + } + $result[] = $coverage_string; + } + + $result[] = isset($type_labels[$version['type']]) ? $type_labels[$version['type']] : ''; + $result[] = isset($status_labels[$version['status']]) ? $status_labels[$version['status']] : ''; + + if ($cfg->req->relations->enable) { + $rx = isset($relationCounters[$id]) ? $relationCounters[$id] : 0; + $result[] = "" . $rx; + } + + # 8792: append one item to $result for every displayed column (no content?: append empty string) + if ($gui->processCF) { + $linkedCFWithContent = array(); + if (isset($cfByReqVer[$version['version_id']])) { + $linkedCFWithContent = $cfByReqVer[$version['version_id']]; + } + + foreach ($cfield_ids as $cf_id) { + if (isset($linkedCFWithContent[$cf_id])) { + $cf = $linkedCFWithContent[$cf_id]; + $verbose_type = $req_mgr->cfield_mgr->custom_field_types[$cf['type']]; + $value = preg_replace('!\s+!', ' ', + htmlspecialchars($cf['value'], ENT_QUOTES, + $cfg->charset)); + if (($verbose_type == 'date' || + $verbose_type == 'datetime') && is_numeric($value) && + $value != 0) { + $value = @strftime( + $cfg->$verbose_type . + " ({$labels['week_short']} %W)", $value); # fix typo: missing 's' in labels + } + $result[] = $value; + } else { + $result[] = ''; + } + } + } + + $rows[] = $result; + } + } + + // Construction of EXT-JS table starts here + if (($gui->row_qty = count($rows)) > 0) { + $version_string = ($args->all_versions) ? $labels['number_of_versions'] : $labels['number_of_reqs']; + $gui->pageTitle .= " - " . $version_string . ": " . $gui->row_qty; + + /** + * get column header titles for the table + * + * IMPORTANT: + * the order of following items in this array has to be + * the same as row content above!!! + * + * should be: + * 1. path + * 2. title + * 3. version + * 4. frozen + * 5. coverage (if enabled) + * 6. type + * 7. status + * 8. relations (if enabled) + * 9. then all custom fields in order of $fields + */ + $columns = array(); + $columns[] = array( + 'title_key' => 'req_spec_short', + 'width' => 200 + ); + $columns[] = array( + 'title_key' => 'title', + 'width' => 150 + ); + $columns[] = array( + 'title_key' => 'version', + 'width' => 30 + ); + $columns[] = array( + 'title_key' => 'created_on', + 'width' => 55 + ); + $columns[] = array( + 'title_key' => 'modified_on', + 'width' => 55 + ); + + $frozen_for_filter = array( + $labels['yes'], + $labels['no'] + ); + $columns[] = array( + 'title_key' => 'frozen', + 'width' => 30, + 'filter' => 'list', + 'filterOptions' => $frozen_for_filter + ); + + if ($cfg->req->expected_coverage_management) { + $columns[] = array( + 'title_key' => 'th_coverage', + 'width' => 80 + ); + } + + $columns[] = array( + 'title_key' => 'type', + 'width' => 60, + 'filter' => 'list', + 'filterOptions' => $type_labels + ); + $columns[] = array( + 'title_key' => 'status', + 'width' => 60, + 'filter' => 'list', + 'filterOptions' => $status_labels + ); + + if ($cfg->req->relations->enable) { + $columns[] = array( + 'title_key' => 'th_relations', + 'width' => 50, + 'filter' => 'numeric' + ); + } + + foreach ($gui->cfields4req as $cf) { + $columns[] = array( + 'title' => htmlentities($cf['label'], ENT_QUOTES, $cfg->charset), + 'type' => 'text', + 'col_id' => 'id_cf_' . $cf['name'] + ); + } + + // create table object, fill it with columns and row data and give it a title + $matrix = new tlExtTable($columns, $rows, 'tl_table_req_overview'); + $matrix->title = $labels['requirements']; + + // group by Req Spec + $matrix->setGroupByColumnName($labels['req_spec_short']); + + // sort by coverage descending if enabled, otherwise by status + $sort_name = ($cfg->req->expected_coverage_management) ? $labels['th_coverage'] : $labels['status']; + $matrix->setSortByColumnName($sort_name); + $matrix->sortDirection = 'DESC'; + + // define toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + $matrix->toolbarRefreshButton = true; + $matrix->showGroupItemsCount = true; + + // show custom field content in multiple lines + $matrix->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + $gui->tableSet = array( + $matrix + ); + } + + $chronoStop = microtime(true); + $gui->elapsedSeconds = round($chronoStop - $chronoStart); +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * initialize user input + * + * @param + * resource &$tproject_mgr reference to testproject manager + * @return array $args array with user input information + */ +function initArgs(&$tproject_mgr) +{ + $args = new stdClass(); + $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; + + $all_versions = isset($_REQUEST['all_versions']) ? true : false; + $all_versions_hidden = isset($_REQUEST['all_versions_hidden']) ? true : false; + if ($all_versions) { + $selection = true; + } elseif ($all_versions_hidden) { + $selection = false; + } elseif (isset($_SESSION['all_versions'])) { + $selection = $_SESSION['all_versions']; + } else { + $selection = false; + } + $args->all_versions = $_SESSION['all_versions'] = $selection; + + $args->tproject_id = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; + + if ($args->tproject_id > 0) { + $tproject_info = $tproject_mgr->get_by_id($args->tproject_id); + $args->tproject_name = $tproject_info['name']; + $args->tproject_description = $tproject_info['notes']; + } + + return $args; +} + +/** + * initialize GUI + * + * @param stdClass $argsObj + * reference to user input + * @return stdClass $gui gui data + */ +function initGui(&$argsObj) +{ + $gui = new stdClass(); + + $gui->pageTitle = lang_get('caption_req_overview'); + $gui->warning_msg = lang_get('no_linked_req'); + $gui->tproject_name = $argsObj->tproject_name; + $gui->all_versions = $argsObj->all_versions; + $gui->tableSet = null; + + return $gui; +} + +/** + */ +function getCfg() +{ + $cfg = new stdClass(); + $cfg->glue_char = config_get('gui_title_separator_1'); + $cfg->charset = config_get('charset'); + $cfg->req = config_get('req_cfg'); + $cfg->date = config_get('date_format'); + $cfg->datetime = config_get('timestamp_format'); + + // on requirement creation motification timestamp is set to default value "0000-00-00 00:00:00" + $cfg->neverModifiedTS = "0000-00-00 00:00:00"; + + // $cfg->req->expected_coverage_management = FALSE; // FORCED FOR TEST + + return $cfg; +} + +/** + */ +function checkRights(&$db, &$user, $context) +{ + $context->rightsOr = [ + "mgt_view_req" + ]; + $context->rightsAnd = []; + pageAccessCheck($db, $user, $context); } - - -/** - * initialize GUI - * - * @param stdClass $argsObj reference to user input - * @return stdClass $gui gui data - */ -function init_gui(&$argsObj) { - $gui = new stdClass(); - - $gui->pageTitle = lang_get('caption_req_overview'); - $gui->warning_msg = lang_get('no_linked_req'); - $gui->tproject_name = $argsObj->tproject_name; - $gui->all_versions = $argsObj->all_versions; - $gui->tableSet = null; - - return $gui; -} - - -/** - * - */ -function getCfg() { - $cfg = new stdClass(); - $cfg->glue_char = config_get('gui_title_separator_1'); - $cfg->charset = config_get('charset'); - $cfg->req = config_get('req_cfg'); - $cfg->date = config_get('date_format'); - $cfg->datetime = config_get('timestamp_format'); - - // on requirement creation motification timestamp is set to default value "0000-00-00 00:00:00" - $cfg->neverModifiedTS = "0000-00-00 00:00:00"; - - // $cfg->req->expected_coverage_management = FALSE; // FORCED FOR TEST - - return $cfg; -} - - -/* - * rights check function for testlinkInitPage() - */ -function checkRights(&$db, &$user) { - return $user->hasRight($db,'mgt_view_req'); -} - diff --git a/lib/requirements/reqPrint.php b/lib/requirements/reqPrint.php index 0a238c4b98..3dda190d84 100644 --- a/lib/requirements/reqPrint.php +++ b/lib/requirements/reqPrint.php @@ -1,78 +1,87 @@ -get_node_hierarchy_info($args->req_id); -$node['version_id'] = $args->req_version_id; -$node['revision'] = $args->req_revision; - -$gui = new stdClass(); -$gui->object_name=''; -$gui->object_name = $node['name']; -$gui->page_title = sprintf(lang_get('print_requirement'),$node['name']); -$gui->tproject_name=$args->tproject_name; -$gui->tproject_id=$args->tproject_id; -$gui->req_id=$args->req_id; -$gui->req_version_id=$args->req_version_id; -$gui->req_revision=$args->req_revision; - - -// Struture defined in printDocument.php -$options = array('toc' => 0, - 'req_linked_tcs' => 1, 'req_cf' => 1, - 'req_scope' => 1, 'req_relations' => 1, 'req_coverage' => 1, - 'req_status' => 1, 'req_type' => 1,'req_author'=> 1, - 'displayVersion' => 1, 'displayDates' => 1, - 'displayLastEdit' => 1, 'docType' => SINGLE_REQ); - -$text2print = ''; -$text2print .= - renderHTMLHeader($gui->page_title,$_SESSION['basehref'],SINGLE_REQ); - -$text2print .= - renderReqForPrinting($db,$node,$options,null,0,$args->tproject_id); - -echo $text2print; - -/* - function: init_args - - args: - - returns: - -*/ -function init_args() { - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->req_id = isset($_REQUEST['req_id']) ? intval($_REQUEST['req_id']) : 0; - $args->req_version_id = isset($_REQUEST['req_version_id']) ? intval($_REQUEST['req_version_id']) : 0; - $args->req_revision = isset($_REQUEST['req_revision']) ? intval($_REQUEST['req_revision']) : 0; - - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tproject_name = $_SESSION['testprojectName']; - - return $args; +get_node_hierarchy_info($args->req_id); +$node['version_id'] = $args->req_version_id; +$node['revision'] = $args->req_revision; + +$gui = new stdClass(); +$gui->object_name = ''; +$gui->object_name = $node['name']; +$gui->page_title = sprintf(lang_get('print_requirement'), $node['name']); +$gui->tproject_name = $args->tproject_name; +$gui->tproject_id = $args->tproject_id; +$gui->req_id = $args->req_id; +$gui->req_version_id = $args->req_version_id; +$gui->req_revision = $args->req_revision; + +// Struture defined in printDocument.php +$options = array( + 'toc' => 0, + 'req_linked_tcs' => 1, + 'req_cf' => 1, + 'req_scope' => 1, + 'req_relations' => 1, + 'req_coverage' => 1, + 'req_status' => 1, + 'req_type' => 1, + 'req_author' => 1, + 'displayVersion' => 1, + 'displayDates' => 1, + 'displayLastEdit' => 1, + 'docType' => SINGLE_REQ +); + +$text2print = ''; +$text2print .= renderHTMLHeader($gui->page_title, $_SESSION['basehref'], + SINGLE_REQ); +$text2print .= renderReqForPrinting($db, $node, $options, 0, $args->tproject_id); + +echo $text2print; + +/* + * function: init_args + * + * args: + * + * returns: + * + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->req_id = isset($_REQUEST['req_id']) ? intval($_REQUEST['req_id']) : 0; + $args->req_version_id = isset($_REQUEST['req_version_id']) ? intval( + $_REQUEST['req_version_id']) : 0; + $args->req_revision = isset($_REQUEST['req_revision']) ? intval( + $_REQUEST['req_revision']) : 0; + + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tproject_name = $_SESSION['testprojectName']; + + return $args; } diff --git a/lib/requirements/reqSearch.php b/lib/requirements/reqSearch.php index 5ecdb832d8..2cad00e4cf 100644 --- a/lib/requirements/reqSearch.php +++ b/lib/requirements/reqSearch.php @@ -1,461 +1,519 @@ -initGuiBean(); - -$gui->main_descr = lang_get('caption_search_form_req'); -$gui->warning_msg = ''; -$gui->path_info = null; -$gui->resultSet = null; -$gui->tableSet = null; - -$map = null; -$args = init_args($date_format_cfg); - -$gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tprojectID); -$gui->tcasePrefix .= $tcase_cfg->glue_character; - -if ($args->tprojectID) { - $sql = build_search_sql($db,$args,$gui); - - // key: req id (db id) - // value: array of versions and revisions - // - $map = $db->fetchRowsIntoMap($sql,'id',database::CUMULATIVE); - - // dont show requirements from different testprojects than the selected one - if (count($map)) { - $reqIDSet = array_keys($map); - foreach ($reqIDSet as $item) { - $pid = $tproject_mgr->tree_manager->getTreeRoot($item); - if ($pid != $args->tprojectID) { - unset($map[$item]); - } - } - } +initGuiBean(); + +$gui->main_descr = lang_get('caption_search_form_req'); +$gui->warning_msg = ''; +$gui->path_info = null; +$gui->tableSet = null; +$gui->resultSet = []; + +$map = []; +$args = initArgs($date_format_cfg); + +$gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tprojectID); +$gui->tcasePrefix .= $tcase_cfg->glue_character; + +if ($args->tprojectID) { + $sql = build_search_sql($db, $args, $gui); + + // key: req id (db id) + // value: array of versions and revisions + $map = (array) $db->fetchRowsIntoMap($sql, 'id', database::CUMULATIVE); + + // dont show requirements from different testprojects than the selected one + if (count($map)) { + $reqIDSet = array_keys($map); + foreach ($reqIDSet as $item) { + $pid = $tproject_mgr->tree_manager->getTreeRoot($item); + if ($pid != $args->tprojectID) { + unset($map[$item]); + } + } + } +} + +$smarty = new TLSmarty(); +$gui->row_qty = count($map); +if ($gui->row_qty > 0) { + $gui->resultSet = $map; + if ($gui->row_qty <= $req_cfg->search->max_qty_for_display) { + $req_set = array_keys($map); + $options = array( + 'output_format' => 'path_as_string' + ); + $gui->path_info = $tproject_mgr->tree_manager->get_full_path_verbose( + $req_set, $options); + } else { + $gui->warning_msg = lang_get('too_wide_search_criteria'); + } +} else { + $gui->warning_msg = lang_get('no_records_found'); +} + +$table = buildExtTable($gui, $charset); + +if (! is_null($table)) { + $gui->tableSet[] = $table; +} + +$gui->pageTitle = $gui->main_descr . " - " . lang_get('match_count') . ": " . + $gui->row_qty; + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $tpl); + +/** + */ +function buildExtTable($gui, $charset) +{ + $table = null; + $lbl = array( + 'edit' => 'requirement', + 'rev' => 'revision_short', + 'ver' => 'version_short', + 'req_spec' => 'req_spec', + 'requirement' => 'requirement', + 'version_revision_tag' => 'version_revision_tag' + ); + + $labels = init_labels($lbl); + $edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; + + // $gui->resultSet - + // key: reqspec_id + // value: array of matches + // array + // { + // [4][0]=>{"name" => "QAZ MNNN","id" => "4","req_doc_id" => "QAZ", + // "version_id" => 5, "version" => 1, + // "revision_id" => -1, "revision" => 2} -> revisio_id < 0 => lives on REQ VERSIONS TABLE + // + // [1]=>{"name" => "QAZ MNNN","id" => "4","req_doc_id" => "QAZ", + // "version_id" => 5, "version" => 1, + // "revision_id" => 6, "revision" => 1} + // ... + // } + // + // + if (count($gui->resultSet) > 0) { + $columns = array(); + + $columns[] = array( + 'title_key' => 'req_spec' + ); + $columns[] = array( + 'title_key' => 'requirement', + 'type' => 'text' + ); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + $key2loop = array_keys($gui->resultSet); + $img = ""; + // req_id, req_version_id + $reqVerHref = '' . + $labels['version_revision_tag'] . ' '; + // req_revision_id + $reqRevHref = '' . + $labels['version_revision_tag'] . ' '; + + foreach ($key2loop as $req_id) { + $rowData = array(); + $itemSet = $gui->resultSet[$req_id]; + $rfx = &$itemSet[0]; + + // We Group by Requirement path + $rowData[] = htmlentities($gui->path_info[$rfx['id']], ENT_QUOTES, + $charset); + + $edit_link = "" . "{$img} "; + $title = htmlentities($rfx['req_doc_id'], ENT_QUOTES, $charset) . ":" . + htmlentities($rfx['name'], ENT_QUOTES, $charset); + + $matches = ''; + foreach ($itemSet as $rx) { + if ($rx['revision_id'] > 0) { + $dummy = sprintf($reqRevHref, $rx['revision_id'], + $rx['version'], $rx['revision']); + } else { + $dummy = sprintf($reqVerHref, $req_id, $rx['version_id'], + $rx['version'], $rx['revision']); + } + $matches .= $dummy; + } + $rowData[] = $edit_link . $title . ' ' . $matches; + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, 'tl_table_req_search'); + + $table->setGroupByColumnName($labels['req_spec']); + $table->setSortByColumnName($labels['requirement']); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->allowMultiSort = false; + $table->toolbarRefreshButton = false; + $table->toolbarShowAllColumnsButton = false; + $table->storeTableState = false; + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + } + return $table; +} + +/* + * function: + * + * args: + * + * returns: + * + */ +function initArgs($dateFormat) +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $strnull = array( + 'requirement_document_id', + 'name', + 'scope', + 'reqStatus', + 'custom_field_value', + 'targetRequirement', + 'creation_date_from', + 'creation_date_to', + 'log_message', + 'modification_date_from', + 'modification_date_to' + ); + + foreach ($strnull as $keyvar) { + $args->$keyvar = isset($_REQUEST[$keyvar]) ? trim($_REQUEST[$keyvar]) : null; + $args->$keyvar = ! is_null($args->$keyvar) && strlen($args->$keyvar) > 0 ? trim( + $args->$keyvar) : null; + } + + $intcheck = array( + 'version', + 'tcid', + 'reqType', + 'relation_type' + ); + foreach ($intcheck as $keyvar) { + $args->$keyvar = isset($_REQUEST[$keyvar]) ? intval($_REQUEST[$keyvar]) : null; + } + + $int0 = array( + 'custom_field_id', + 'coverage' + ); + foreach ($int0 as $keyvar) { + $args->$keyvar = isset($_REQUEST[$keyvar]) ? intval($_REQUEST[$keyvar]) : 0; + } + + // convert "creation date from" to iso format for database usage + $dk = array( + 'creation_date_from' => ' 00:00:00', + 'creation_date_to' => ' 23:59:59', + 'modification_date_from' => ' 00:00:00', + 'modification_date_to' => ' 23:59:59' + ); + foreach ($dk as $tdk => $hhmmss) { + if (isset($args->$tdk) && trim($args->$tdk) != '') { + $l10ndate = split_localized_date($args->$tdk, $dateFormat); + $args->$tdk = null; + if ($l10ndate != null && is_array($l10ndate)) { + // set date in iso format + $args->$tdk = $l10ndate['year'] . "-" . $l10ndate['month'] . "-" . + $l10ndate['day'] . $hhmmss; + } + } + } + + $args->userID = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; + $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + return $args; +} + +/** + */ +function build_search_sql(&$dbHandler, &$argsObj, &$guiObj) +{ + $tables = tlObjectWithDB::getDBTables( + array( + 'cfield_design_values', + 'nodes_hierarchy', + 'req_specs', + 'req_relations', + 'req_versions', + 'req_revisions', + 'requirements', + 'req_coverage', + 'tcversions' + )); + + // ver => REQ Versions + // rev => REQ Revisions + // Some filters can be applied on REQ Versions & REQ Revisions + // other ONLY on REQ Versions and other ONLY REQ Revisions + // that's why we have developer logic using UNION. + // Using just UNION and not UNION ALL, we will try to remove duplicates + // if possible. + // + // That's why to certain extent filter seems to work in OR mode. + // May be this is a BUG, that was never reported. + $filter = array(); + $filter['ver'] = null; + $filter['rev'] = null; + + // date filters can be build using algorithm + // Need to sanitize!!! 2019 + $date_fields = array( + 'creation_ts' => 'ts', + 'modification_ts' => 'ts' + ); + $date_keys = array( + 'date_from' => '>=', + 'date_to' => '<=' + ); + foreach ($date_fields as $fx => $needle) { + foreach ($date_keys as $fk => $op) { + $fkey = str_replace($needle, $fk, $fx); + if ($argsObj->$fkey) { + $filter['ver'][$fkey] = " AND REQV.$fx $op '{$argsObj->$fkey}' "; + $filter['rev'][$fkey] = " AND REQR.$fx $op '{$argsObj->$fkey}' "; + } + } + } + + // key: args key + // value: map + // key: table field + // value: map + // key: filter scope, will identify with part of SQL affects + // value: table alias + $likeKeys = array( + 'name' => array( + 'name' => array( + 'ver' => "NH_REQ", + 'rev' => "REQR" + ) + ), + 'requirement_document_id' => array( + 'req_doc_id' => array( + 'ver' => 'REQ', + 'rev' => 'REQR' + ) + ), + 'scope' => array( + 'scope' => array( + 'ver' => 'REQV', + 'rev' => 'REQR' + ) + ), + 'log_message' => array( + 'log_message' => array( + 'ver' => 'REQV', + 'rev' => 'REQR' + ) + ) + ); + + foreach ($likeKeys as $key => $fcfg) { + if ($argsObj->$key) { + $value = $dbHandler->prepare_string($argsObj->$key); + $field = key($fcfg); + foreach ($fcfg[$field] as $table => $alias) { + $filter[$table][$field] = " AND {$alias}.{$field} like '%{$value}%' "; + } + } + } + + $char_keys = array( + 'reqType' => array( + 'type' => array( + 'ver' => "REQV", + 'rev' => "REQR" + ) + ), + 'reqStatus' => array( + 'status' => array( + 'ver' => 'REQV', + 'rev' => 'REQR' + ) + ) + ); + + foreach ($char_keys as $key => $fcfg) { + if ($argsObj->$key) { + $value = $dbHandler->prepare_string($argsObj->$key); + $field = key($fcfg); + foreach ($fcfg[$field] as $table => $alias) { + $filter[$table][$field] = " AND {$alias}.{$field} = '{$value}' "; + } + } + } + + if ($argsObj->version) { + $version = $dbHandler->prepare_int($argsObj->version); + $filter['ver']['version'] = " AND REQV.version = {$version} "; + $filter['rev']['version'] = $filter['ver']['version']; + } + + if ($argsObj->coverage) { + // search by expected coverage of testcases + $coverage = $dbHandler->prepare_int($argsObj->coverage); + $filter['ver']['coverage'] = " AND REQV.expected_coverage = {$coverage} "; + $filter['rev']['coverage'] = " AND REQR.expected_coverage = {$coverage} "; + } + + // Complex processing + if (! is_null($argsObj->relation_type) && intval($argsObj->relation_type) > 0) { + // search by relation type + // $argsObj->relation_type is a string in following form + // e.g. 3_destination or 2_source or only 4 + // must be treated different + $dummy = explode('_', $argsObj->relation_type); + $rel_type = $dummy[0]; + $side = isset($dummy[1]) ? " RR.{$dummy[1]}_id = NH_REQ.id " : " RR.source_id = NH_REQ.id OR RR.destination_id = NH_REQ.id "; + + $from['ver']['relation_type'] = " JOIN {$tables['req_relations']} RR " . + " ON ($side) AND RR.relation_type = {$rel_type} "; + $from['rev']['relation_type'] = $from['ver']['relation_type']; + } + + if ($argsObj->custom_field_id > 0) { + $cfield_id = $dbHandler->prepare_string($argsObj->custom_field_id); + $cfield_value = $dbHandler->prepare_string($argsObj->custom_field_value); + $from['ver']['custom_field'] = " JOIN {$tables['cfield_design_values']} CFD " . + " ON CFD.node_id = REQV.id "; + + $from['rev']['custom_field'] = " JOIN {$tables['cfield_design_values']} CFD " . + " ON CFD.node_id = REQR.id "; + + $filter['ver']['custom_field'] = " AND CFD.field_id = {$cfield_id} " . + " AND CFD.value like '%{$cfield_value}%' "; + + $filter['rev']['custom_field'] = $filter['ver']['custom_field']; + } + + if ($argsObj->tcid != "" && strcmp($argsObj->tcid, $guiObj->tcasePrefix) != 0) { + // search for reqs linked to this testcase + $tcid = $dbHandler->prepare_string($argsObj->tcid); + $tcid = str_replace($guiObj->tcasePrefix, "", $tcid); + + $filter['ver']['tcid'] = " AND TCV.tc_external_id = '$tcid' "; + $filter['rev']['tcid'] = $filter['ver']['tcid']; + + $from['ver']['tcid'] = " /* 1.9.18 Changed */ " . + " /* Look for Req Coverage info */ " . + " JOIN {$tables['req_coverage']} RC ON RC.req_version_id = NH_REQV.id " . + + " /* 1.9.18 Changed */ " . + " /* Need Test case children => test case versions */ " . + " JOIN {$tables['nodes_hierarchy']} NH_TCV + ON NH_TCV.id = RC.tcversion_id " . + + " /* Needed to search using External ID */ " . + " JOIN {$tables['tcversions']} TCV ON TCV.id = NH_TCV.id "; + + $from['rev']['tcid'] = $from['ver']['tcid']; + } + + // We will search on two steps + // STEP 1 + // Search on REQ Versions + // + $common = " SELECT NH_REQ.name, REQ.id, REQ.req_doc_id,"; + $sql = $common . + " REQV.id as version_id, REQV.version, REQV.revision, -1 AS revision_id " . + " /* */" . " /* Main table to get Last Version REQ_DOC_ID */" . + " FROM {$tables['requirements']} REQ " . + " JOIN {$tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id=REQ.id " . + " /* */ " . " /* Need to get all REQ children => REQ Versions */ " . + " JOIN {$tables['nodes_hierarchy']} + NH_REQV ON NH_REQV.parent_id = NH_REQ.id " . " /* */ " . + " /* Go for REQ REV data */ " . + " JOIN {$tables['req_versions']} REQV ON REQV.id=NH_REQV.id " . " /* */ "; + + $map2use = array( + 'from', + 'filter' + ); // ORDER IS CRITIC to build SQL statement + foreach ($map2use as $vv) { + $ref = &$$vv; + if (! is_null($ref['ver'])) { + $sql .= ($vv == 'filter') ? ' WHERE 1=1 ' : ''; + $sql .= implode("", $ref['ver']); + } + } + $stm['ver'] = $sql; + + // STEP 1 + // Search on REQ Revisions + // + $sql4Union = $common . + " REQR.parent_id AS version_id, REQV.version, REQR.revision, REQR.id AS revision_id " . + " /* SQL For Req REVISIONS - */ " . + " /* SQL For Req REVISIONS - Main table to get Last Version REQ_DOC_ID */" . + " FROM {$tables['requirements']} REQ " . + " JOIN {$tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id=REQ.id " . + " /* SQL For Req REVISIONS - */ " . + " /* SQL For Req REVISIONS - Need to get all REQ children => REQ Versions because they are parent of REVISIONS */ " . + " JOIN {$tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id " . + " /* SQL For Req REVISIONS - */ " . + " /* SQL For Req REVISIONS - Go for REQ REVISION DATA */" . + " JOIN {$tables['req_versions']} REQV ON REQV.id=NH_REQV.id " . + " /* SQL For Req REVISIONS - */ " . + " /* SQL For Req REVISIONS - Now need to go for revisions */ " . + " JOIN {$tables['req_revisions']} REQR ON REQR.parent_id=REQV.id "; + + foreach ($map2use as $vv) { + $ref = &$$vv; + if (! is_null($ref['rev'])) { + $sql4Union .= ($vv == 'filter') ? ' WHERE 1=1 ' : ''; + $sql4Union .= implode("", $ref['rev']); + } + } + + // add additional joins that depends on user search criteria + return $stm['ver'] . + " UNION ({$sql4Union}) ORDER BY id ASC, version DESC, revision DESC "; } - -$smarty = new TLSmarty(); -$gui->row_qty = count($map); -if($gui->row_qty > 0) { - $gui->resultSet = $map; - if($gui->row_qty <= $req_cfg->search->max_qty_for_display) { - $req_set = array_keys($map); - $options = array('output_format' => 'path_as_string'); - $gui->path_info = - $tproject_mgr->tree_manager->get_full_path_verbose($req_set,$options); - } else { - $gui->warning_msg = lang_get('too_wide_search_criteria'); - } -} else { - $gui->warning_msg = lang_get('no_records_found'); -} - -$table = buildExtTable($gui, $charset); - -if (!is_null($table)) { - $gui->tableSet[] = $table; -} - -$gui->pageTitle = - $gui->main_descr . " - " . lang_get('match_count') . ": " . $gui->row_qty; - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $tpl); - -/** - * - * - */ -function buildExtTable($gui, $charset) { - $table = null; - $lbl = array('edit' => 'requirement', 'rev' => 'revision_short', - 'ver' => 'version_short', - 'req_spec' => 'req_spec', 'requirement' => 'requirement', - 'version_revision_tag' => 'version_revision_tag'); - - $labels = init_labels($lbl); - $edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; - - // $gui->resultSet - - // key: reqspec_id - // value: array of matches - // array - // { - // [4][0]=>{"name" => "QAZ MNNN","id" => "4","req_doc_id" => "QAZ", - // "version_id" => 5, "version" => 1, - // "revision_id" => -1, "revision" => 2} -> revisio_id < 0 => lives on REQ VERSIONS TABLE - // - // [1]=>{"name" => "QAZ MNNN","id" => "4","req_doc_id" => "QAZ", - // "version_id" => 5, "version" => 1, - // "revision_id" => 6, "revision" => 1} - // ... - // } - // - // - - if(count($gui->resultSet) > 0) { - $columns = array(); - - $columns[] = array('title_key' => 'req_spec'); - $columns[] = array('title_key' => 'requirement', 'type' => 'text'); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - $key2loop = array_keys($gui->resultSet); - $img = ""; - // req_id, req_version_id - $reqVerHref = '' . $labels['version_revision_tag'] . ' '; - // req_revision_id - $reqRevHref = '' . $labels['version_revision_tag'] . ' '; - - foreach($key2loop as $req_id) { - $rowData = array(); - $itemSet = $gui->resultSet[$req_id]; - $rfx = &$itemSet[0]; - - // We Group by Requirement path - $rowData[] = htmlentities($gui->path_info[$rfx['id']], ENT_QUOTES, $charset); - - $edit_link = "" . "{$img} "; - $title = htmlentities($rfx['req_doc_id'], ENT_QUOTES, $charset) . ":" . - htmlentities($rfx['name'], ENT_QUOTES, $charset); - - $matches = ''; - foreach($itemSet as $rx) { - if($rx['revision_id'] > 0) { - $dummy = sprintf($reqRevHref,$rx['revision_id'],$rx['version'], - $rx['revision']); - } else { - $dummy = sprintf($reqVerHref,$req_id,$rx['version_id'],$rx['version'], - $rx['revision']); - } - $matches .= $dummy; - } - $rowData[] = $edit_link . $title . ' ' . $matches; - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_req_search'); - - $table->setGroupByColumnName($labels['req_spec']); - $table->setSortByColumnName($labels['requirement']); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->allowMultiSort = false; - $table->toolbarRefreshButton = false; - $table->toolbarShowAllColumnsButton = false; - $table->storeTableState = false; - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - } - return $table; -} - -/* - function: - - args: - - returns: - - */ -function init_args($dateFormat) { - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $strnull = array('requirement_document_id', 'name','scope', - 'reqStatus', - 'custom_field_value', 'targetRequirement', - 'creation_date_from','creation_date_to', - 'log_message', - 'modification_date_from','modification_date_to'); - - foreach($strnull as $keyvar) { - $args->$keyvar = isset($_REQUEST[$keyvar]) ? trim($_REQUEST[$keyvar]) : null; - $args->$keyvar = !is_null($args->$keyvar) && strlen($args->$keyvar) > 0 ? trim($args->$keyvar) : null; - } - - $intcheck = array('version', 'tcid', 'reqType', 'relation_type'); - foreach($intcheck as $keyvar) { - $args->$keyvar = isset($_REQUEST[$keyvar]) ? intval($_REQUEST[$keyvar]) : null; - } - - $int0 = array('custom_field_id', 'coverage'); - foreach($int0 as $keyvar) { - $args->$keyvar = isset($_REQUEST[$keyvar]) ? intval($_REQUEST[$keyvar]) : 0; - } - - // convert "creation date from" to iso format for database usage - $dk = array('creation_date_from' => ' 00:00:00', - 'creation_date_to' => ' 23:59:59', - 'modification_date_from' => ' 00:00:00', - 'modification_date_to' => ' 23:59:59'); - foreach( $dk as $tdk => $hhmmss ) { - if (isset($args->$tdk) && trim($args->$tdk) != '') { - $l10ndate = split_localized_date($args->$tdk, $dateFormat); - $args->$tdk = null; - if ($l10ndate != null && is_array($l10ndate)) { - // set date in iso format - $args->$tdk = $l10ndate['year'] . "-" . - $l10ndate['month'] . "-" . - $l10ndate['day'] . $hhmmss; - } - } - } - - $args->userID = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; - $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - return $args; -} - - - -/** - * - * - */ -function build_search_sql(&$dbHandler,&$argsObj,&$guiObj) { - $tables = tlObjectWithDB::getDBTables(array('cfield_design_values', - 'nodes_hierarchy', 'req_specs', 'req_relations', 'req_versions', - 'req_revisions','requirements', 'req_coverage', 'tcversions')); - - // ver => REQ Versions - // rev => REQ Revisions - // Some filters can be applied on REQ Versions & REQ Revisions - // other ONLY on REQ Versions and other ONLY REQ Revisions - // that's why we have developer logic using UNION. - // Using just UNION and not UNION ALL, we will try to remove duplicates - // if possible. - // - // That's why to certain extent filter seems to work in OR mode. - // May be this is a BUG, that was never reported. - // - $filter = array(); - $filter['ver'] = null; - $filter['rev'] = null; - - // ----------------------------------------------------------------------- - // date filters can be build using algorithm - // Need to sanitize!!! 2019 - $date_fields = array('creation_ts' => 'ts' ,'modification_ts' => 'ts'); - $date_keys = array('date_from' => '>=' ,'date_to' => '<='); - foreach($date_fields as $fx => $needle) { - foreach($date_keys as $fk => $op) { - $fkey = str_replace($needle,$fk,$fx); - if($argsObj->$fkey) { - $filter['ver'][$fkey] = " AND REQV.$fx $op '{$argsObj->$fkey}' "; - $filter['rev'][$fkey] = " AND REQR.$fx $op '{$argsObj->$fkey}' "; - } - } - } - // ----------------------------------------------------------------------- - - // key: args key - // value: map - // key: table field - // value: map - // key: filter scope, will identify with part of SQL affects - // value: table alias - // - $likeKeys = array('name' => - array('name' => array('ver' => "NH_REQ", 'rev' => "REQR")), - 'requirement_document_id' => - array('req_doc_id' => array('ver' => 'REQ', 'rev' => 'REQR')), - 'scope' => - array('scope' => array('ver' => 'REQV', 'rev' => 'REQR')), - 'log_message' - => array('log_message'=> array('ver' => 'REQV','rev' =>'REQR'))); - - foreach($likeKeys as $key => $fcfg) { - if($argsObj->$key) { - $value = $dbHandler->prepare_string($argsObj->$key); - $field = key($fcfg); - foreach($fcfg[$field] as $table => $alias) { - $filter[$table][$field] = " AND {$alias}.{$field} like '%{$value}%' "; - } - } - } - - $char_keys = array( 'reqType' => - array('type' => array('ver' => "REQV", 'rev' => "REQR")), - 'reqStatus' => - array('status' => array('ver' => 'REQV', 'rev' => 'REQR'))); - - foreach($char_keys as $key => $fcfg) { - if($argsObj->$key) { - $value = $dbHandler->prepare_string($argsObj->$key); - $field = key($fcfg); - foreach($fcfg[$field] as $table => $alias) { - $filter[$table][$field] = " AND {$alias}.{$field} = '{$value}' "; - } - } - } - - if ($argsObj->version) { - $version = $dbHandler->prepare_int($argsObj->version); - $filter['ver']['version'] = " AND REQV.version = {$version} "; - $filter['rev']['version'] = $filter['versions']['by_version']; - } - - if ($argsObj->coverage) { - //search by expected coverage of testcases - $coverage=$dbHandler->prepare_int($argsObj->coverage); - $filter['ver']['coverage'] = " AND REQV.expected_coverage = {$coverage} "; - $filter['rev']['coverage'] = " AND REQR.expected_coverage = {$coverage} "; - } - - - // Complex processing - if(!is_null($argsObj->relation_type)) { - // search by relation type - // $argsObj->relation_type is a string in following form - // e.g. 3_destination or 2_source or only 4 - // must be treated different - $dummy = explode('_',$argsObj->relation_type); - $rel_type = $dummy[0]; - $side = isset($dummy[1]) ? " RR.{$dummy[1]}_id = NH_REQ.id " : - " RR.source_id = NH_REQ.id OR RR.destination_id = NH_REQ.id "; - - $from['ver']['relation_type'] = " JOIN {$tables['req_relations']} RR " . - " ON ($side) AND RR.relation_type = {$rel_type} "; - $from['rev']['relation_type'] = $from['ver']['relation_type']; - - } - - if($argsObj->custom_field_id > 0) { - $cfield_id = $dbHandler->prepare_string($argsObj->custom_field_id); - $cfield_value = $dbHandler->prepare_string($argsObj->custom_field_value); - $from['ver']['custom_field'] = - " JOIN {$tables['cfield_design_values']} CFD " . - " ON CFD.node_id = REQV.id "; - - $from['rev']['custom_field'] = - " JOIN {$tables['cfield_design_values']} CFD " . - " ON CFD.node_id = REQR.id "; - - $filter['ver']['custom_field'] = " AND CFD.field_id = {$cfield_id} " . - " AND CFD.value like '%{$cfield_value}%' "; - - $filter['rev']['custom_field'] = $filter['ver']['custom_field']; - } - - if ($argsObj->tcid != "" && strcmp($argsObj->tcid, $guiObj->tcasePrefix) != 0) { - // search for reqs linked to this testcase - $tcid = $dbHandler->prepare_string($argsObj->tcid); - $tcid = str_replace($guiObj->tcasePrefix, "", $tcid); - - $filter['ver']['tcid'] = " AND TCV.tc_external_id = '$tcid' "; - $filter['rev']['tcid'] = $filter['ver']['by_tcid']; - - $from['ver']['tcid'] = - - " /* 1.9.18 Changed */ " . - " /* Look for Req Coverage info */ " . - " JOIN {$tables['req_coverage']} RC ON RC.req_version_id = NH_REQV.id " . - - " /* 1.9.18 Changed */ " . - " /* Need Test case children => test case versions */ ". - " JOIN {$tables['nodes_hierarchy']} NH_TCV - ON NH_TCV.id = RC.tcversion_id " . - - " /* Needed to search using External ID */ ". - " JOIN {$tables['tcversions']} TCV ON TCV.id = NH_TCV.id "; - - $from['rev']['tcid'] = $from['ver']['tcid']; - } - - // We will search on two steps - // STEP 1 - // Search on REQ Versions - // - $common = " SELECT NH_REQ.name, REQ.id, REQ.req_doc_id,"; - $sql = $common . - " REQV.id as version_id, REQV.version, REQV.revision, -1 AS revision_id " . - " /* */" . - " /* Main table to get Last Version REQ_DOC_ID */" . - " FROM {$tables['requirements']} REQ " . - " JOIN {$tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id=REQ.id " . - " /* */ " . - " /* Need to get all REQ children => REQ Versions */ " . - " JOIN {$tables['nodes_hierarchy']} - NH_REQV ON NH_REQV.parent_id = NH_REQ.id " . - " /* */ " . - " /* Go for REQ REV data */ " . - " JOIN {$tables['req_versions']} REQV ON REQV.id=NH_REQV.id " . - " /* */ "; - - $map2use = array('from','filter'); // ORDER IS CRITIC to build SQL statement - foreach($map2use as $vv) { - $ref = &$$vv; - if(!is_null($ref['ver'])) { - $sql .= ($vv == 'filter') ? ' WHERE 1=1 ' : ''; - $sql .= implode("",$ref['ver']); - } - } - $stm['ver'] = $sql; - - - // STEP 1 - // Search on REQ Revisions - // - $sql4Union = $common . - " REQR.parent_id AS version_id, REQV.version, REQR.revision, REQR.id AS revision_id " . - " /* SQL For Req REVISIONS - */ " . - " /* SQL For Req REVISIONS - Main table to get Last Version REQ_DOC_ID */" . - " FROM {$tables['requirements']} REQ " . - " JOIN {$tables['nodes_hierarchy']} NH_REQ ON NH_REQ.id=REQ.id " . - " /* SQL For Req REVISIONS - */ " . - " /* SQL For Req REVISIONS - Need to get all REQ children => REQ Versions because they are parent of REVISIONS */ " . - " JOIN {$tables['nodes_hierarchy']} NH_REQV ON NH_REQV.parent_id = NH_REQ.id " . - " /* SQL For Req REVISIONS - */ " . - " /* SQL For Req REVISIONS - Go for REQ REVISION DATA */" . - " JOIN {$tables['req_versions']} REQV ON REQV.id=NH_REQV.id " . - " /* SQL For Req REVISIONS - */ " . - " /* SQL For Req REVISIONS - Now need to go for revisions */ " . - " JOIN {$tables['req_revisions']} REQR ON REQR.parent_id=REQV.id "; - - foreach($map2use as $vv) { - $ref = &$$vv; - if(!is_null($ref['rev'])) { - $sql4Union .= ($vv == 'filter') ? ' WHERE 1=1 ' : ''; - $sql4Union .= implode("",$ref['rev']); - } - } - - - // add additional joins that depends on user search criteria - $sql = $stm['ver'] . " UNION ({$sql4Union}) ORDER BY id ASC, version DESC, revision DESC "; - return $sql; -} \ No newline at end of file diff --git a/lib/requirements/reqSearchForm.php b/lib/requirements/reqSearchForm.php index ce3160b05d..5c36d0daa8 100644 --- a/lib/requirements/reqSearchForm.php +++ b/lib/requirements/reqSearchForm.php @@ -1,90 +1,87 @@ -tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tprojectID); -$gui->tcasePrefix .= $tcase_cfg->glue_character; -$gui->mainCaption = lang_get('testproject') . " " . $args->tprojectName; - -$enabled = 1; -$no_filters = null; -$gui->creation_date_from = null; -$gui->creation_date_to = null; -$gui->modification_date_from = null; -$gui->modification_date_to = null; - -$gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design($args->tprojectID,$enabled, - $no_filters,'requirement'); - -$gui->keywords = $tproject_mgr->getKeywords($args->tprojectID); -$reqSpecSet = $tproject_mgr->getOptionReqSpec($args->tprojectID,testproject::GET_NOT_EMPTY_REQSPEC); - -$gui->filter_by['design_scope_custom_fields'] = !is_null($gui->design_cf); -$gui->filter_by['keyword'] = !is_null($gui->keywords); -$gui->filter_by['requirement_doc_id'] = !is_null($reqSpecSet); - -$reqCfg = config_get('req_cfg'); -$gui->types = init_labels($reqCfg->type_labels); -$coverageManagement = $reqCfg->expected_coverage_management; -$gui->filter_by['expected_coverage'] = !is_null($coverageManagement); - -$gui->reqStatus = init_labels($reqCfg->status_labels); - -$gui->filter_by['relation_type'] = $reqCfg->relations->enable; -$gui->req_relation_select = $req_mgr->init_relation_type_select(); -foreach ($gui->req_relation_select['equal_relations'] as $key => $oldkey) { - // set new key in array and delete old one - $new_key = (int)str_replace("_source", "", $oldkey); - $gui->req_relation_select['items'][$new_key] = $gui->req_relation_select['items'][$oldkey]; - unset($gui->req_relation_select['items'][$oldkey]); -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . 'reqSearchForm.tpl'); - - - -function init_args() -{ - $args = new stdClass(); - $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; - - return $args; -} - -?> \ No newline at end of file +tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tprojectID); +$gui->tcasePrefix .= $tcase_cfg->glue_character; +$gui->mainCaption = lang_get('testproject') . " " . $args->tprojectName; + +$enabled = 1; +$no_filters = null; +$gui->creation_date_from = null; +$gui->creation_date_to = null; +$gui->modification_date_from = null; +$gui->modification_date_to = null; + +$gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design( + $args->tprojectID, $enabled, $no_filters, 'requirement'); + +$gui->keywords = $tproject_mgr->getKeywords($args->tprojectID); +$reqSpecSet = $tproject_mgr->getOptionReqSpec($args->tprojectID, + testproject::GET_NOT_EMPTY_REQSPEC); + +$gui->filter_by['design_scope_custom_fields'] = ! is_null($gui->design_cf); +$gui->filter_by['keyword'] = ! is_null($gui->keywords); +$gui->filter_by['requirement_doc_id'] = ! is_null($reqSpecSet); + +$reqCfg = config_get('req_cfg'); +$gui->types = init_labels($reqCfg->type_labels); +$coverageManagement = $reqCfg->expected_coverage_management; +$gui->filter_by['expected_coverage'] = ! is_null($coverageManagement); + +$gui->reqStatus = init_labels($reqCfg->status_labels); + +$gui->filter_by['relation_type'] = $reqCfg->relations->enable; +$gui->req_relation_select = $req_mgr->init_relation_type_select(); +foreach ($gui->req_relation_select['equal_relations'] as $oldkey) { + // set new key in array and delete old one + $new_key = (int) str_replace("_source", "", $oldkey); + $gui->req_relation_select['items'][$new_key] = $gui->req_relation_select['items'][$oldkey]; + unset($gui->req_relation_select['items'][$oldkey]); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . 'reqSearchForm.tpl'); + +function initArgs() +{ + $args = new stdClass(); + $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; + + return $args; +} + +?> diff --git a/lib/requirements/reqSpecCommands.class.php b/lib/requirements/reqSpecCommands.class.php index af89f42fa2..61c588cea5 100644 --- a/lib/requirements/reqSpecCommands.class.php +++ b/lib/requirements/reqSpecCommands.class.php @@ -1,944 +1,981 @@ -db=$db; - $this->reqSpecMgr = new requirement_spec_mgr($db); - $this->reqMgr = new requirement_mgr($db); - $this->treeMgr = $this->reqMgr->tree_mgr; - - $req_spec_cfg = config_get('req_spec_cfg'); - $this->reqSpecTypeDomain = init_labels($req_spec_cfg->type_labels); - $this->commandMgr = new reqCommands($db); - $this->submit_button_label=lang_get('btn_save'); - $this->getRequirementsOptions = array('order_by' => " ORDER BY NH_REQ.node_order "); - - $tproject_mgr = new testproject($this->db); - $info = $tproject_mgr->get_by_id($tproject_id); - if($info['reqmgr_integration_enabled']) - { - $sysmgr = new tlReqMgrSystem($this->db); - $rms = $sysmgr->getInterfaceObject($tproject_id); - $this->reqMgrSystem = $sysmgr->getLinkedTo($tproject_id); - unset($sysmgr); - } - } - - function setAuditContext($auditContext) - { - $this->auditContext=$auditContext; - } - - function getReqMgrSystem() - { - return $this->reqMgrSystem; - } - - - /** - * common properties needed on gui - * - */ - function initGuiBean($options=null) - { - $obj = new stdClass(); - $obj->pageTitle = ''; - $obj->bodyOnLoad = ''; - $obj->bodyOnUnload = ''; - $obj->hilite_item_name = false; - $obj->display_path = false; - $obj->show_match_count = false; - $obj->main_descr = ''; - $obj->action_descr = ''; - $obj->cfields = null; - $obj->template = ''; - $obj->submit_button_label = ''; - $obj->action_status_ok = true; - - $obj->req_spec_id = null; - $obj->req_spec_revision_id = null; - $obj->req_spec = null; - - $obj->expected_coverage = null; - $obj->total_req_counter=null; - $obj->reqSpecTypeDomain = $this->reqSpecTypeDomain; - - $obj->askForRevision = false; - $obj->askForLog = false; - $obj->req_spec = null; - if(!is_null($options)) - { - if(isset($options['getReqSpec'])) - { - $ref = &$options['getReqSpec']; - $obj->req_spec = $this->reqSpecMgr->get_by_id($ref['id'],$ref['options']); - } - } - - return $obj; - } - - - - - /* - function: create - - args: - - returns: - - */ - function create(&$argsObj) - { - // echo __CLASS__ . '.' . __FUNCTION__ . '()
    '; - $guiObj = $this->initGuiBean(); - $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . $argsObj->tproject_name; - $guiObj->action_descr = lang_get('create_req_spec'); - - $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id); - $guiObj->template = $this->defaultTemplate; - $guiObj->submit_button_label=$this->submit_button_label; - $guiObj->req_spec_id=null; - $guiObj->req_spec_title=null; - $guiObj->req_spec_doc_id=null; - $guiObj->total_req_counter=null; - - return $guiObj; - } - - /* - function: edit - - args: - - returns: - - */ - // following req command model - function edit(&$argsObj,$request,$overwriteArgs=true) - { - // echo __CLASS__ . '.' . __FUNCTION__ . '()
    '; - - $guiObj = $this->initGuiBean(); - - $guiObj->req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - $guiObj->main_descr = lang_get('req_spec_short') . TITLE_SEP . $guiObj->req_spec['title']; - - $guiObj->req_spec_doc_id = $guiObj->req_spec['doc_id']; - $guiObj->req_spec_title = $guiObj->req_spec['title']; - $guiObj->total_req_counter = $guiObj->req_spec['total_req']; - - $guiObj->req_spec_id = $argsObj->req_spec_id; - $guiObj->req_spec_revision_id = $argsObj->req_spec_revision_id; - - $guiObj->action_descr = lang_get('edit_req_spec'); - $guiObj->template = $this->defaultTemplate; - $guiObj->submit_button_label=$this->submit_button_label; - - $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs($argsObj->req_spec_id, - $argsObj->req_spec_revision_id, - $argsObj->tproject_id); - - // not really clear - if( $overwriteArgs ) - { - $argsObj->scope = $guiObj->req_spec['scope']; - } - - return $guiObj; - } - - /* - function: doCreate - - args: - - returns: - - */ - function doCreate(&$argsObj,$request) - { - // echo __CLASS__ . '.' . __FUNCTION__ . '()
    '; - - $guiObj = $this->initGuiBean(); - $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . $argsObj->tproject_name; - $guiObj->action_descr = lang_get('create_req_spec'); - $guiObj->submit_button_label=$this->submit_button_label; - $guiObj->template = $this->defaultTemplate; - $guiObj->req_spec_id=null; - $guiObj->req_spec_doc_id=null; - $guiObj->req_spec_title=null; - $guiObj->total_req_counter=null; - - $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id, - $request); - // manage new order - $order = 0; - $nt2exclude = array('testplan' => 'exclude_me','testsuite'=> 'exclude_me', - 'testcase'=> 'exclude_me'); - $siblings = $this->treeMgr->get_children($argsObj->parentID,$nt2exclude); - if( !is_null($siblings) ) - { - $dummy = end($siblings); - $order = $dummy['node_order']+1; - } - - $ret = $this->reqSpecMgr->create($argsObj->tproject_id,$argsObj->parentID, - $argsObj->doc_id,$argsObj->title,$argsObj->scope, - $argsObj->countReq,$argsObj->user_id,$argsObj->reqSpecType,$order); - - $guiObj->user_feedback = $ret['msg']; - if($ret['status_ok']) - { - $argsObj->scope = ""; - $guiObj->user_feedback = sprintf(lang_get('req_spec_created'),$argsObj->title); - $idCard = array('tproject_id' => $argsObj->tproject_id); - $cf_map = $this->reqSpecMgr->get_linked_cfields($idCard); - - $this->reqSpecMgr->values_to_db($request,$ret['revision_id'],$cf_map); - logAuditEvent(TLS("audit_req_spec_created",$this->auditContext->tproject,$argsObj->title), - "CREATE",$ret['id'],"req_specs"); - } - else - { - $guiObj->req_spec_doc_id=$argsObj->doc_id; - $guiObj->req_spec_title=$argsObj->title; - $guiObj->total_req_counter=$argsObj->countReq; - } - return $guiObj; - } - - - /* - function: doUpdate - - args: - - returns: - - */ - function doUpdate(&$argsObj,$request) - { - $descr_prefix = lang_get('req_spec_short') . TITLE_SEP; - - $guiObj = $this->initGuiBean(); - $guiObj->submit_button_label=$this->submit_button_label; - $guiObj->template = null; - $guiObj->req_spec_id = $argsObj->req_spec_id; - - $guiObj = $this->edit($argsObj,null,!self::OVERWRITESCOPE); - $guiObj->user_feedback = ''; - $guiObj->template = null; - $guiObj->askForRevision = false; - - // why can not do the check now ? 20110730 - $chk = $this->reqSpecMgr->check_main_data($argsObj->title,$argsObj->doc_id, - $argsObj->tproject_id,$argsObj->parentID, - $argsObj->req_spec_id); - - if( $chk['status_ok'] ) - { - $guiObj = $this->process_revision($guiObj,$argsObj,$request); - } - else - { - // need to manage things in order to NOT LOOSE user input - $user_inputs = array('title' => array('prefix' => 'req_spec_'), - 'scope' => array('prefix' => ''), - 'doc_id' => array('prefix' => 'req_spec_'), - 'reqSpecType' => array(prefix => '', 'item_key' => 'type') ); - - foreach($user_inputs as $from => $convert_to) - { - $prefix_to = isset($convert_to['prefix_to']) ? $convert_to['prefix_to'] : ''; - $to = $prefix_to . $from; - $guiObj->$to = $argsObj->$from; - - $item_key = isset($convert_to['item_key']) ? $convert_to['item_key'] : $from; - $guiObj->req_spec[$item_key] = $argsObj->$from; - } - - $guiObj->action_status_ok = false; - $guiObj->user_feedback = $chk['msg']; - $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id, - null, null,$request); - } - - return $guiObj; - } - - - /* - function: doDelete - - args: - - returns: - - */ - function doDelete(&$argsObj) - { - $guiObj = $this->initGuiBean(); - - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - $this->reqSpecMgr->delete_deep($argsObj->req_spec_id); - logAuditEvent(TLS("audit_req_spec_deleted",$this->auditContext->tproject,$req_spec['title']), - "DELETE",$argsObj->req_spec_id,"req_specs"); - - $guiObj->template = 'show_message.tpl'; - $guiObj->template_dir = ''; - $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . $argsObj->tproject_name; - $guiObj->title=lang_get('delete_req_spec'); - - $guiObj->user_feedback = sprintf(lang_get('req_spec_deleted'),$req_spec['title']); - $guiObj->refreshTree = 1; // needed to enable refresh_tree logic - $guiObj->result = 'ok'; - - return $guiObj; - } - - - /* - function: reorder - - args: - - returns: - - */ - function reorder(&$argsObj) - { - $guiObj = $this->initGuiBean(); - $guiObj->template = 'reqSpecReorder.tpl'; - $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . $argsObj->tproject_name; - $guiObj->action_descr = lang_get('title_change_req_spec_order'); - - $order_by = ' ORDER BY NH.node_order,REQ_SPEC.id '; - $guiObj->all_req_spec = $this->reqSpecMgr->get_all_in_testproject($argsObj->tproject_id,$order_by); - $guiObj->tproject_name=$argsObj->tproject_name; - $guiObj->tproject_id=$argsObj->tproject_id; - return $guiObj; - } - - - - /* - function: doReorder - - args: - - returns: - - */ - function doReorder(&$argsObj) - { - $guiObj = $this->initGuiBean(); - $guiObj->tproject_name=$argsObj->tproject_name; - $guiObj->tproject_id=$argsObj->tproject_id; - $guiObj->template = 'project_req_spec_mgmt.tpl'; - $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . $argsObj->tproject_name; - - $nodes_in_order = transform_nodes_order($argsObj->nodes_order); - - // need to remove first element, is testproject - array_shift($nodes_in_order); - $this->reqSpecMgr->set_order($nodes_in_order); - $guiObj->refreshTree=1; - return $guiObj; - } - - - /* - function: createChild - - args: - - returns: - - */ - function createChild(&$argsObj) - { - $reqParent=$this->reqSpecMgr->get_by_id($argsObj->parentID); - $guiObj = $this->initGuiBean(); - $guiObj->main_descr = lang_get('req_spec_short') . TITLE_SEP . $reqParent['title']; - $guiObj->action_descr = lang_get('create_child_req_spec'); - - $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id); - $guiObj->template = $this->defaultTemplate; - $guiObj->submit_button_label=$this->submit_button_label; - $guiObj->req_spec_id=null; - $guiObj->req_spec_doc_id=null; - $guiObj->req_spec_title=null; - $guiObj->total_req_counter=null; - - return $guiObj; - } - - - /* - function: copyRequirements - - args: - - returns: - - */ - function copyRequirements(&$argsObj,$options=null) - { - $obj = $this->initGuiBean(); - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - - $my['options'] = array( 'get_items' => true); - $my['options'] = array_merge($my['options'], (array)$options); - if( $my['options']['get_items'] ) - { - $opt = $this->getRequirementsOptions + array('output' => 'minimal'); - $obj->items = $this->reqSpecMgr->get_requirements($argsObj->req_spec_id,'all',null,$opt); - } - $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; - $obj->action_descr = lang_get('copy_several_reqs'); - $obj->template = 'reqCopy.tpl'; - $obj->containers = null; - $obj->page2call = 'lib/requirements/reqSpecEdit.php'; - $obj->array_of_msg = ''; - $obj->doActionButton = 'doCopyRequirements'; - $obj->req_spec_id = $argsObj->req_spec_id; - - $exclude_node_types=array('testplan' => 'exclude_me','testsuite' => 'exclude_me', - 'testcase' => 'exclude_me','requirement' => 'exclude_me', - 'requirement_spec_revision' => 'exclude_me'); - - $my['filters'] = array('exclude_node_types' => $exclude_node_types); - $my['options']['order_cfg']['type'] = $my['options']['output'] = 'rspec'; - $subtree = $this->reqMgr->tree_mgr->get_subtree($argsObj->tproject_id,$my['filters'],$my['options']); - if(count($subtree)) - { - $obj->containers = $this->reqMgr->tree_mgr->createHierarchyMap($subtree,'dotted',array('field' => 'doc_id','format' => '%s:')); - } - return $obj; - } - - /** - * - * - */ - function doCopyRequirements(&$argsObj) - { - $obj = $this->initGuiBean(); - $obj = $this->copyRequirements($argsObj, array( 'get_items' => false)); - $obj->req = null; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->array_of_msg = array(); - - $copyOptions = array('copy_also' => array('testcase_assignment' => $argsObj->copy_testcase_assignment)); - - foreach($argsObj->itemSet as $itemID) - { - $ret = $this->reqMgr->copy_to($itemID,$argsObj->containerID,$argsObj->user_id, - $argsObj->tproject_id,$copyOptions); - $obj->user_feedback = $ret['msg']; - if($ret['status_ok']) - { - $new_req = $this->reqMgr->get_by_id($ret['id'],requirement_mgr::LATEST_VERSION); - $source_req = $this->reqMgr->get_by_id($itemID,requirement_mgr::LATEST_VERSION); - $new_req = $new_req[0]; - $source_req = $source_req[0]; - - $logMsg = TLS("audit_requirement_copy",$new_req['req_doc_id'],$source_req['req_doc_id']); - logAuditEvent($logMsg,"COPY",$ret['id'],"requirements"); - $obj->user_feedback = $logMsg; // sprintf(lang_get('req_created'), $new_req['req_doc_id']); - $obj->template = 'reqCopy.tpl'; - $obj->req_id = $ret['id']; - $obj->array_of_msg[] = $logMsg; - } - } - $obj->items = $this->reqSpecMgr->get_requirements($obj->req_spec_id, - 'all',null,$this->getRequirementsOptions); - - return $obj; - } - - - /* - function: copy - copy req. spec - - args: - - returns: - - */ - function copy(&$argsObj,$options=null) - { - $obj = $this->initGuiBean(); - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - - $my['options'] = array( 'get_items' => true); - $my['options'] = array_merge($my['options'], (array)$options); - - $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; - $obj->action_descr = lang_get('copy_req_spec'); - $obj->template = 'reqSpecCopy.tpl'; - $obj->containers = null; - $obj->page2call = 'lib/requirements/reqSpecEdit.php'; - $obj->array_of_msg = ''; - $obj->doActionButton = 'doCopy'; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->top_checked = ' checked = "checked" '; - $obj->bottom_checked = ' '; - - - $exclude_node_types=array('testplan' => 'exclude_me','testsuite' => 'exclude_me', - 'testcase'=> 'exclude_me','requirement' => 'exclude_me'); - - $my['filters'] = array('exclude_node_types' => $exclude_node_types); - $root = $this->treeMgr->get_node_hierarchy_info($argsObj->tproject_id); - $subtree = array_merge(array($root),$this->treeMgr->get_subtree($argsObj->tproject_id,$my['filters'])); - - if(count($subtree)) - { - $obj->containers = $this->treeMgr->createHierarchyMap($subtree); - } - return $obj; - } - - - - /* - function: doCopy - copy req. spec - - args: - - returns: - - */ - function doCopy(&$argsObj) - { - $obj = $this->initGuiBean(); - $obj = $this->copy($argsObj); - $obj->req = null; - $obj->req_spec_id = $argsObj->req_spec_id; - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - - - $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; - $obj->action_descr = lang_get('copy_req_spec'); - $obj->template = 'reqSpecCopy.tpl'; - $obj->containers = null; - $obj->page2call = 'lib/requirements/reqSpecEdit.php'; - $obj->array_of_msg = array(); - $obj->doActionButton = 'doCopy'; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->top_checked = ' checked = "checked" '; - $obj->bottom_checked = ' '; - - $op = $this->reqSpecMgr->copy_to($argsObj->req_spec_id,$argsObj->containerID, - $argsObj->tproject_id, $argsObj->user_id); - - if( $op['status_ok'] ) - { - $new_req_spec = $this->reqSpecMgr->get_by_id($op['id']); - $obj->array_of_msg[] = sprintf(lang_get('req_spec_copy_done'),$req_spec['doc_id'], - $req_spec['title'],$new_req_spec['doc_id']); - } - - $exclude_node_types=array('testplan' => 'exclude_me','testsuite' => 'exclude_me', - 'testcase'=> 'exclude_me','requirement' => 'exclude_me'); - - $my['filters'] = array('exclude_node_types' => $exclude_node_types); - $root = $this->treeMgr->get_node_hierarchy_info($argsObj->tproject_id); - $subtree = array_merge(array($root),$this->treeMgr->get_subtree($argsObj->tproject_id,$my['filters'])); - - if(count($subtree)) - { - $obj->containers = $this->treeMgr->createHierarchyMap($subtree); - } - return $obj; - } - - /** - * - */ - public function doFreeze(&$argsObj,$request) - { - $req_spec_id = $request["req_spec_id"]; - $req_spec = $this->reqSpecMgr->getReqTree($req_spec_id); - $req_spec_info = $this->reqSpecMgr->get_by_id($req_spec_id); - - $childNodes = isset($req_spec['childNodes']) ? $req_spec['childNodes'] : null ; - if( !is_null($childNodes)) - { - $loop_qty=sizeof($childNodes); - for($idx = 0;$idx < $loop_qty;$idx++) - { - $cNode = $childNodes[$idx]; - $nTable = $cNode['node_table']; - if($cNode['node_table'] == 'req_specs') - { - $request["req_spec_id"]=$cNode['id']; - $this->doFreeze($argsObj,$request); - } - else if ($cNode['node_table'] == 'requirements') - { - $req = $this->reqMgr->get_by_id($cNode['id'],requirement_mgr::LATEST_VERSION); - $req_freeze_version = new stdClass(); - $req_freeze_version->req_version_id = $req[0]['version_id']; - $this->commandMgr->doFreezeVersion($req_freeze_version); - } - } - } - - $obj = $this->initGuiBean(); - $obj->template = 'show_message.tpl'; - $obj->template_dir = ''; - $obj->user_feedback = lang_get('req_frozen'); - $obj->main_descr=lang_get('req_spec') . TITLE_SEP . $req_spec_info['title']; - $obj->title=lang_get('freeze_req'); - $obj->refreshTree = 0; - $obj->result = 'ok'; // needed to enable refresh_tree logic - return $obj; - } - - - /** - * THIS METHOD NEED to be moved to common class because is also used - * on reqCommand.class.php - * - */ - function simpleCompare($old,$new,$oldCF,$newCF) - { - // - log message is only forced to be entered when a custom field, title or document ID is changed - // - when only changes where made to scope user is free to create a new revision or - // overwrite the old revision (Cancel -> overwrite) - $ret = array('force' => false, 'suggest' => false, 'nochange' => false, 'changeon' => null); - - // key: var name to be used on $old - // value: var name to be used on $new - // Then to compare old and new - // $old[$key] compare to $new[$value] - // - $suggest_revision = array('scope' => 'scope'); - $force_revision = array('type' => 'reqSpecType', 'doc_id'=> 'doc_id', 'title' => 'title'); - - foreach($force_revision as $access_key => $access_prop) - { - if( $ret['force'] = ($old[$access_key] != $new->$access_prop) ) - { - $ret['changeon'] = 'attribute:' . $access_key; - break; - } - } - - if( !$ret['force'] ) - { - if( !is_null($newCF) ) - { - foreach($newCF as $cf_key => $cf) - { - if( $ret['force'] = ($oldCF[$cf_key]['value'] != $cf['cf_value']) ) - { - $ret['changeon'] = 'custom field:' . $oldCF[$cf_key]['name']; - break; - } - } - } - } - - if( !$ret['force'] ) - { - foreach($suggest_revision as $access_key => $access_prop) - { - if( $ret['suggest'] = ($old[$access_key] != $new->$access_prop) ) - { - $ret['changeon'] = 'attribute:' . $access_key; - break; - } - } - - } - $ret['nochange'] = ($ret['force'] == false && $ret['suggest'] == false); - return $ret; - } - - /* - function: doCreateRevision - - args: - - returns: - - @internal revisions - - */ - function doCreateRevision(&$argsObj,$request) - { - $item = array('log_message' => $argsObj->log_message, 'author_id' => $argsObj->user_id); - $ret = $this->reqSpecMgr->clone_revision($argsObj->req_spec_id,$item); - - $obj = $this->initGuiBean(); - $obj->user_feedback = $ret['msg']; - $obj->template = "reqSpecView.php?req_spec_id={$argsObj->req_spec_id}"; - $obj->req_spec = null; - $obj->req_spec_id=$argsObj->req_spec_id; - $obj->req_spec_revision_id = $ret['id']; - return $obj; - } - - - - /** - * - */ - function process_revision(&$guiObj,&$argsObj,&$userInput) - { - - // TICKET 4661 - $itemOnDB = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - $who = array('tproject_id' => $argsObj->tproject_id); - $cf_map = $this->reqSpecMgr->get_linked_cfields($who); - $newCFields = $this->reqSpecMgr->cfield_mgr->_build_cfield($userInput,$cf_map); - - $who['item_id'] = $argsObj->req_spec_revision_id; - $oldCFields = $this->reqSpecMgr->get_linked_cfields($who); - $diff = $this->simpleCompare($itemOnDB,$argsObj,$oldCFields,$newCFields); - - - $createRev = false; - if($diff['force'] && !$argsObj->do_save) - { - $guiObj->askForLog = true; - $guiObj->refreshTree = false; - - // Need Change several values with user input data, to match logic on - // edit php page on function renderGui() - // $map = array('status' => 'reqStatus', 'type' => 'reqSpecType','scope' => 'scope', - $map = array('type' => 'reqSpecType','scope' => 'scope', - 'doc_id'=> 'doc_id', 'title' => 'title'); - - foreach($map as $k => $w) - { - $guiObj->req_spec[$k] = $argsObj->$w; - } - $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs(null,null,$argsObj->tproject_id, - null, null,$userInput); - - } - else if( $diff['nochange'] || ( ($createRev = $diff['force'] && !$guiObj->askForLog) || $argsObj->do_save ) ) - { - - if( $argsObj->do_save == 1) - { - $createRev = ($argsObj->save_rev == 1); - } - - $item = array(); - $item['id'] = $argsObj->req_spec_id; - $item['revision_id'] = $createRev ? -1 : $argsObj->req_spec_revision_id; - $item['doc_id'] = $argsObj->doc_id; - $item['name'] = $argsObj->title; - $item['scope'] = $argsObj->scope; - $item['countReq'] = $argsObj->countReq; - $item['type'] = $argsObj->reqSpecType; - - $user_key = $createRev ? 'author_id' : 'modifier_id'; - $item[$user_key] = $argsObj->user_id; - - $opt = array('skip_controls' => true, 'create_rev' => $createRev, 'log_message' => $argsObj->log_message); - $ret = $this->reqSpecMgr->update($item,$opt); - - $guiObj->user_feedback = $ret['msg']; - $guiObj->template = null; - - if($ret['status_ok']) - { - // custom fields update - $this->reqSpecMgr->values_to_db($userInput,$ret['revision_id'],$cf_map); - - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->template = "reqSpecView.php?refreshTree={$argsObj->refreshTree}&" . - "req_spec_id={$guiObj->req_spec_id}"; - - // TODO - // logAuditEvent(TLS("audit_requirement_saved",$argsObj->reqDocId),"SAVE",$argsObj->req_id,"requirements"); - } - else - { - // Action has failed => no change done on DB. - $old = $this->reqSpecMgr->get_by_id($argsObj->req_id); - $guiObj->main_descr = $descr_prefix . $old['title']; - $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_values($argsObj->req_spec_id, - $argsObj->req_spec_revision_id, - $argsObj->tproject_id); - } - } - else if( $diff['suggest'] ) - { - $guiObj->askForRevision = true; - } - - return $guiObj; - } - - - /** - * - */ - function fileUpload(&$argsObj,$request) - { - $argsObj->uploadOp = fileUploadManagement($this->db,$argsObj->req_spec_id,$argsObj->fileTitle,$this->reqSpecMgr->getAttachmentTableName()); - return $this->initGuiObjForAttachmentOperations($argsObj); - } - - /** - * - */ - function deleteFile(&$argsObj) - { - deleteAttachment($this->db,$argsObj->file_id); - return $this->initGuiObjForAttachmentOperations($argsObj); - } - - - /** - * - */ - private function initGuiObjForAttachmentOperations($argsObj) - { - $guiObj = new stdClass(); - $guiObj->main_descr = ''; - $guiObj->action_descr = ''; - $guiObj->askForRevision = $guiObj->askForLog = false; - $guiObj->action_status_ok = true; - $guiObj->req_spec_id = $argsObj->req_spec_id; - $guiObj->template = "reqSpecView.php?refreshTree=0&req_spec_id={$argsObj->req_spec_id}"; - - $guiObj->uploadOp = $argsObj->uploadOp; - - return $guiObj; - } - - /* - function: copyRequirements - - args: - - returns: - - */ - function bulkReqMon(&$argsObj,$options=null) - { - $obj = $this->initGuiBean(); - $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); - - $my['options'] = array( 'get_items' => true); - $my['options'] = array_merge($my['options'], (array)$options); - - - if( $my['options']['get_items'] ) - { - $opt = $this->getRequirementsOptions + - array('outputLevel' => 'minimal', 'decodeUsers' => false); - $obj->items = $this->reqSpecMgr->get_requirements($argsObj->req_spec_id,'all',null,$opt); - } - - $opx = array('reqSpecID' => $argsObj->req_spec_id); - $monSet = $this->reqMgr->getMonitoredByUser($argsObj->user_id,$argsObj->tproject_id,$opx); - - $obj->enable_start_btn = false; - $obj->enable_stop_btn = false; - foreach($obj->items as $xdx => &$itx) - { - $onOff = isset($monSet[$itx['id']]) ? true : false; - $itx['monitor'] = $onOff ? 'On' : 'Off'; - $obj->enable_start_btn |= !$onOff; - $obj->enable_stop_btn |= $onOff; - } - - $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; - $obj->action_descr = lang_get('bulk_monitoring'); - $obj->template = 'reqBulkMon.tpl'; - $obj->containers = null; - $obj->page2call = 'lib/requirements/reqSpecEdit.php'; - $obj->array_of_msg = ''; - $obj->doActionButton = 'do' . ucfirst(__FUNCTION__); - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->refreshTree = 0; - - return $obj; - } - - /** - * - * - */ - function doBulkReqMon(&$argsObj) - { - $obj = $this->initGuiBean(); - $obj->req = null; - $obj->req_spec_id = $argsObj->req_spec_id; - $obj->array_of_msg = ''; - - $m2r = null; - switch($argsObj->op) - { - case 'toogleMon': - $opx = array('reqSpecID' => $argsObj->req_spec_id); - $monSet = $this->reqMgr->getMonitoredByUser($argsObj->user_id,$argsObj->tproject_id,$opx); - - foreach($argsObj->itemSet as $req_id) - { - $f2r = isset($monSet[$req_id]) ? 'monitorOff' : 'monitorOn'; - $this->reqMgr->$f2r($req_id,$argsObj->user_id,$argsObj->tproject_id); - } - break; - - case 'startMon': - $m2r = 'monitorOn'; - break; - - case 'stopMon': - $m2r = 'monitorOff'; - break; - } - - if( !is_null($m2r) ) - { - foreach($argsObj->itemSet as $req_id) - { - $this->reqMgr->$m2r($req_id,$argsObj->user_id,$argsObj->tproject_id); - } - } - - return $this->bulkReqMon($argsObj); - } - - - +db = $db; + $this->reqSpecMgr = new requirement_spec_mgr($db); + $this->reqMgr = new requirement_mgr($db); + $this->treeMgr = $this->reqMgr->tree_mgr; + + $req_spec_cfg = config_get('req_spec_cfg'); + $this->reqSpecTypeDomain = init_labels($req_spec_cfg->type_labels); + $this->commandMgr = new reqCommands($db); + $this->submit_button_label = lang_get('btn_save'); + $this->getRequirementsOptions = array( + 'order_by' => " ORDER BY NH_REQ.node_order " + ); + + $tproject_mgr = new testproject($this->db); + $info = $tproject_mgr->get_by_id($tproject_id); + if ($info['reqmgr_integration_enabled']) { + $sysmgr = new tlReqMgrSystem($this->db); + $this->reqMgrSystem = $sysmgr->getLinkedTo($tproject_id); + unset($sysmgr); + } + } + + public function setAuditContext($auditContext) + { + $this->auditContext = $auditContext; + } + + public function getReqMgrSystem() + { + return $this->reqMgrSystem; + } + + /** + * common properties needed on gui + */ + public function initGuiBean($options = null) + { + $obj = new stdClass(); + $obj->pageTitle = ''; + $obj->bodyOnLoad = ''; + $obj->bodyOnUnload = ''; + $obj->hilite_item_name = false; + $obj->display_path = false; + $obj->show_match_count = false; + $obj->main_descr = ''; + $obj->action_descr = ''; + $obj->cfields = null; + $obj->template = ''; + $obj->submit_button_label = ''; + $obj->action_status_ok = true; + + $obj->req_spec_id = null; + $obj->req_spec_revision_id = null; + $obj->req_spec = null; + + $obj->expected_coverage = null; + $obj->total_req_counter = null; + $obj->reqSpecTypeDomain = $this->reqSpecTypeDomain; + + $obj->askForRevision = false; + $obj->askForLog = false; + $obj->req_spec = null; + if (! is_null($options) && isset($options['getReqSpec'])) { + $ref = &$options['getReqSpec']; + $obj->req_spec = $this->reqSpecMgr->get_by_id($ref['id'], + $ref['options']); + } + + return $obj; + } + + /* + * function: create + * + * args: + * + * returns: + * + */ + public function create(&$argsObj) + { + $guiObj = $this->initGuiBean(); + $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . + $argsObj->tproject_name; + $guiObj->action_descr = lang_get('create_req_spec'); + + $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs( + null, null, $argsObj->tproject_id); + $guiObj->template = $this->defaultTemplate; + $guiObj->submit_button_label = $this->submit_button_label; + $guiObj->req_spec_id = null; + $guiObj->req_spec_title = null; + $guiObj->req_spec_doc_id = null; + $guiObj->total_req_counter = null; + + return $guiObj; + } + + /* + * function: edit + * + * args: + * + * returns: + * + */ + // following req command model + public function edit(&$argsObj, $request, $overwriteArgs = true) + { + $guiObj = $this->initGuiBean(); + + $guiObj->req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + $guiObj->main_descr = lang_get('req_spec_short') . TITLE_SEP . + $guiObj->req_spec['title']; + + $guiObj->req_spec_doc_id = $guiObj->req_spec['doc_id']; + $guiObj->req_spec_title = $guiObj->req_spec['title']; + $guiObj->total_req_counter = $guiObj->req_spec['total_req']; + + $guiObj->req_spec_id = $argsObj->req_spec_id; + $guiObj->req_spec_revision_id = $argsObj->req_spec_revision_id; + + $guiObj->action_descr = lang_get('edit_req_spec'); + $guiObj->template = $this->defaultTemplate; + $guiObj->submit_button_label = $this->submit_button_label; + + $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs( + $argsObj->req_spec_id, $argsObj->req_spec_revision_id, + $argsObj->tproject_id); + + // not really clear + if ($overwriteArgs) { + $argsObj->scope = $guiObj->req_spec['scope']; + } + + return $guiObj; + } + + /* + * function: doCreate + * + * args: + * + * returns: + * + */ + public function doCreate(&$argsObj, $request) + { + $guiObj = $this->initGuiBean(); + $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . + $argsObj->tproject_name; + $guiObj->action_descr = lang_get('create_req_spec'); + $guiObj->submit_button_label = $this->submit_button_label; + $guiObj->template = $this->defaultTemplate; + $guiObj->req_spec_id = null; + $guiObj->req_spec_doc_id = null; + $guiObj->req_spec_title = null; + $guiObj->total_req_counter = null; + + $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs( + null, null, $argsObj->tproject_id, $request); + // manage new order + $order = 0; + $nt2exclude = array( + 'testplan' => 'exclude_me', + 'testsuite' => 'exclude_me', + 'testcase' => 'exclude_me' + ); + $siblings = $this->treeMgr->get_children($argsObj->parentID, $nt2exclude); + if (! is_null($siblings)) { + $dummy = end($siblings); + $order = $dummy['node_order'] + 1; + } + + $ret = $this->reqSpecMgr->create($argsObj->tproject_id, + $argsObj->parentID, $argsObj->doc_id, $argsObj->title, + $argsObj->scope, $argsObj->countReq, $argsObj->user_id, + $argsObj->reqSpecType, $order); + + $guiObj->user_feedback = $ret['msg']; + if ($ret['status_ok']) { + $argsObj->scope = ""; + $guiObj->user_feedback = sprintf(lang_get('req_spec_created'), + $argsObj->title); + $idCard = array( + 'tproject_id' => $argsObj->tproject_id + ); + $cf_map = $this->reqSpecMgr->get_linked_cfields($idCard); + + $this->reqSpecMgr->values_to_db($request, $ret['revision_id'], + $cf_map); + logAuditEvent( + TLS("audit_req_spec_created", $this->auditContext->tproject, + $argsObj->title), "CREATE", $ret['id'], "req_specs"); + } else { + $guiObj->req_spec_doc_id = $argsObj->doc_id; + $guiObj->req_spec_title = $argsObj->title; + $guiObj->total_req_counter = $argsObj->countReq; + } + return $guiObj; + } + + /* + * function: doUpdate + * + * args: + * + * returns: + * + */ + public function doUpdate(&$argsObj, $request) + { + $guiObj = $this->initGuiBean(); + $guiObj->submit_button_label = $this->submit_button_label; + $guiObj->template = null; + $guiObj->req_spec_id = $argsObj->req_spec_id; + + $guiObj = $this->edit($argsObj, null, ! self::OVERWRITESCOPE); + $guiObj->user_feedback = ''; + $guiObj->template = null; + $guiObj->askForRevision = false; + + // why can not do the check now ? 20110730 + $chk = $this->reqSpecMgr->check_main_data($argsObj->title, + $argsObj->doc_id, $argsObj->tproject_id, $argsObj->parentID, + $argsObj->req_spec_id); + + if ($chk['status_ok']) { + $guiObj = $this->processRevision($guiObj, $argsObj, $request); + } else { + // need to manage things in order to NOT LOOSE user input + $user_inputs = array( + 'title' => array( + 'prefix' => 'req_spec_' + ), + 'scope' => array( + 'prefix' => '' + ), + 'doc_id' => array( + 'prefix' => 'req_spec_' + ), + 'reqSpecType' => array( + prefix => '', + 'item_key' => 'type' + ) + ); + + foreach ($user_inputs as $from => $convert_to) { + $prefix_to = isset($convert_to['prefix_to']) ? $convert_to['prefix_to'] : ''; + $to = $prefix_to . $from; + $guiObj->$to = $argsObj->$from; + + $item_key = isset($convert_to['item_key']) ? $convert_to['item_key'] : $from; + $guiObj->req_spec[$item_key] = $argsObj->$from; + } + + $guiObj->action_status_ok = false; + $guiObj->user_feedback = $chk['msg']; + $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs( + null, null, $argsObj->tproject_id, null, null, $request); + } + + return $guiObj; + } + + /* + * function: doDelete + * + * args: + * + * returns: + * + */ + public function doDelete(&$argsObj) + { + $guiObj = $this->initGuiBean(); + + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + $this->reqSpecMgr->delete_deep($argsObj->req_spec_id); + logAuditEvent( + TLS("audit_req_spec_deleted", $this->auditContext->tproject, + $req_spec['title']), "DELETE", $argsObj->req_spec_id, + "req_specs"); + + $guiObj->template = 'show_message.tpl'; + $guiObj->template_dir = ''; + $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . + $argsObj->tproject_name; + $guiObj->title = lang_get('delete_req_spec'); + + $guiObj->user_feedback = sprintf(lang_get('req_spec_deleted'), + $req_spec['title']); + $guiObj->refreshTree = 1; // needed to enable refresh_tree logic + $guiObj->result = 'ok'; + + return $guiObj; + } + + /* + * function: reorder + * + * args: + * + * returns: + * + */ + public function reorder(&$argsObj) + { + $guiObj = $this->initGuiBean(); + $guiObj->template = 'reqSpecReorder.tpl'; + $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . + $argsObj->tproject_name; + $guiObj->action_descr = lang_get('title_change_req_spec_order'); + + $order_by = ' ORDER BY NH.node_order,REQ_SPEC.id '; + $guiObj->all_req_spec = $this->reqSpecMgr->get_all_in_testproject( + $argsObj->tproject_id, $order_by); + $guiObj->tproject_name = $argsObj->tproject_name; + $guiObj->tproject_id = $argsObj->tproject_id; + return $guiObj; + } + + /* + * function: doReorder + * + * args: + * + * returns: + * + */ + public function doReorder(&$argsObj) + { + $guiObj = $this->initGuiBean(); + $guiObj->tproject_name = $argsObj->tproject_name; + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->template = 'project_req_spec_mgmt.tpl'; + $guiObj->main_descr = lang_get('testproject') . TITLE_SEP . + $argsObj->tproject_name; + + $nodes_in_order = transform_nodes_order($argsObj->nodes_order); + + // need to remove first element, is testproject + array_shift($nodes_in_order); + $this->reqSpecMgr->set_order($nodes_in_order); + $guiObj->refreshTree = 1; + return $guiObj; + } + + /* + * function: createChild + * + * args: + * + * returns: + * + */ + public function createChild(&$argsObj) + { + $reqParent = $this->reqSpecMgr->get_by_id($argsObj->parentID); + $guiObj = $this->initGuiBean(); + $guiObj->main_descr = lang_get('req_spec_short') . TITLE_SEP . + $reqParent['title']; + $guiObj->action_descr = lang_get('create_child_req_spec'); + + $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs( + null, null, $argsObj->tproject_id); + $guiObj->template = $this->defaultTemplate; + $guiObj->submit_button_label = $this->submit_button_label; + $guiObj->req_spec_id = null; + $guiObj->req_spec_doc_id = null; + $guiObj->req_spec_title = null; + $guiObj->total_req_counter = null; + + return $guiObj; + } + + /* + * function: copyRequirements + * + * args: + * + * returns: + * + */ + public function copyRequirements(&$argsObj, $options = null) + { + $obj = $this->initGuiBean(); + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + + $my['options'] = array( + 'get_items' => true + ); + $my['options'] = array_merge($my['options'], (array) $options); + if ($my['options']['get_items']) { + $opt = $this->getRequirementsOptions + array( + 'output' => 'minimal' + ); + $obj->items = $this->reqSpecMgr->get_requirements( + $argsObj->req_spec_id, 'all', null, $opt); + } + $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; + $obj->action_descr = lang_get('copy_several_reqs'); + $obj->template = 'reqCopy.tpl'; + $obj->containers = null; + $obj->page2call = 'lib/requirements/reqSpecEdit.php'; + $obj->array_of_msg = ''; + $obj->doActionButton = 'doCopyRequirements'; + $obj->req_spec_id = $argsObj->req_spec_id; + + $exclude_node_types = array( + 'testplan' => 'exclude_me', + 'testsuite' => 'exclude_me', + 'testcase' => 'exclude_me', + 'requirement' => 'exclude_me', + 'requirement_spec_revision' => 'exclude_me' + ); + + $my['filters'] = array( + 'exclude_node_types' => $exclude_node_types + ); + $my['options']['order_cfg']['type'] = $my['options']['output'] = 'rspec'; + $subtree = $this->reqMgr->tree_mgr->get_subtree($argsObj->tproject_id, + $my['filters'], $my['options']); + if (count($subtree)) { + $obj->containers = $this->reqMgr->tree_mgr->createHierarchyMap( + $subtree, 'dotted', + array( + 'field' => 'doc_id', + 'format' => '%s:' + )); + } + return $obj; + } + + /** + */ + public function doCopyRequirements(&$argsObj) + { + $this->initGuiBean(); + $obj = $this->copyRequirements($argsObj, array( + 'get_items' => false + )); + $obj->req = null; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->array_of_msg = array(); + + $copyOptions = array( + 'copy_also' => array( + 'testcase_assignment' => $argsObj->copy_testcase_assignment + ) + ); + + foreach ($argsObj->itemSet as $itemID) { + $ret = $this->reqMgr->copy_to($itemID, $argsObj->containerID, + $argsObj->user_id, $argsObj->tproject_id, $copyOptions); + $obj->user_feedback = $ret['msg']; + if ($ret['status_ok']) { + $new_req = $this->reqMgr->get_by_id($ret['id'], + requirement_mgr::LATEST_VERSION); + $source_req = $this->reqMgr->get_by_id($itemID, + requirement_mgr::LATEST_VERSION); + $new_req = $new_req[0]; + $source_req = $source_req[0]; + + $logMsg = TLS("audit_requirement_copy", $new_req['req_doc_id'], + $source_req['req_doc_id']); + logAuditEvent($logMsg, "COPY", $ret['id'], "requirements"); + $obj->user_feedback = $logMsg; + $obj->template = 'reqCopy.tpl'; + $obj->req_id = $ret['id']; + $obj->array_of_msg[] = $logMsg; + } + } + $obj->items = $this->reqSpecMgr->get_requirements($obj->req_spec_id, + 'all', null, $this->getRequirementsOptions); + + return $obj; + } + + /* + * function: copy + * copy req. spec + * + * args: + * + * returns: + * + */ + public function copy(&$argsObj, $options = null) + { + $obj = $this->initGuiBean(); + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + + $my['options'] = array( + 'get_items' => true + ); + $my['options'] = array_merge($my['options'], (array) $options); + + $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; + $obj->action_descr = lang_get('copy_req_spec'); + $obj->template = 'reqSpecCopy.tpl'; + $obj->containers = null; + $obj->page2call = 'lib/requirements/reqSpecEdit.php'; + $obj->array_of_msg = ''; + $obj->doActionButton = 'doCopy'; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->top_checked = ' checked = "checked" '; + $obj->bottom_checked = ' '; + + $exclude_node_types = array( + 'testplan' => 'exclude_me', + 'testsuite' => 'exclude_me', + 'testcase' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + + $my['filters'] = array( + 'exclude_node_types' => $exclude_node_types + ); + $root = $this->treeMgr->get_node_hierarchy_info($argsObj->tproject_id); + $subtree = array_merge(array( + $root + ), $this->treeMgr->get_subtree($argsObj->tproject_id, $my['filters'])); + + if (count($subtree)) { + $obj->containers = $this->treeMgr->createHierarchyMap($subtree); + } + return $obj; + } + + /* + * function: doCopy + * copy req. spec + * + * args: + * + * returns: + * + */ + public function doCopy(&$argsObj) + { + $this->initGuiBean(); + $obj = $this->copy($argsObj); + $obj->req = null; + $obj->req_spec_id = $argsObj->req_spec_id; + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + + $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; + $obj->action_descr = lang_get('copy_req_spec'); + $obj->template = 'reqSpecCopy.tpl'; + $obj->containers = null; + $obj->page2call = 'lib/requirements/reqSpecEdit.php'; + $obj->array_of_msg = array(); + $obj->doActionButton = 'doCopy'; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->top_checked = ' checked = "checked" '; + $obj->bottom_checked = ' '; + + $op = $this->reqSpecMgr->copy_to($argsObj->req_spec_id, + $argsObj->containerID, $argsObj->tproject_id, $argsObj->user_id); + + if ($op['status_ok']) { + $new_req_spec = $this->reqSpecMgr->get_by_id($op['id']); + $obj->array_of_msg[] = sprintf(lang_get('req_spec_copy_done'), + $req_spec['doc_id'], $req_spec['title'], $new_req_spec['doc_id']); + } + + $exclude_node_types = array( + 'testplan' => 'exclude_me', + 'testsuite' => 'exclude_me', + 'testcase' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + + $my['filters'] = array( + 'exclude_node_types' => $exclude_node_types + ); + $root = $this->treeMgr->get_node_hierarchy_info($argsObj->tproject_id); + $subtree = array_merge(array( + $root + ), $this->treeMgr->get_subtree($argsObj->tproject_id, $my['filters'])); + + if (count($subtree)) { + $obj->containers = $this->treeMgr->createHierarchyMap($subtree); + } + return $obj; + } + + /** + */ + public function doFreeze(&$argsObj, $request) + { + $req_spec_id = $request["req_spec_id"]; + $req_spec = $this->reqSpecMgr->getReqTree($req_spec_id); + $req_spec_info = $this->reqSpecMgr->get_by_id($req_spec_id); + + $childNodes = isset($req_spec['childNodes']) ? $req_spec['childNodes'] : null; + if (! is_null($childNodes)) { + $loop_qty = count($childNodes); + for ($idx = 0; $idx < $loop_qty; $idx ++) { + $cNode = $childNodes[$idx]; + if ($cNode['node_table'] == 'req_specs') { + $request["req_spec_id"] = $cNode['id']; + $this->doFreeze($argsObj, $request); + } elseif ($cNode['node_table'] == 'requirements') { + $req = $this->reqMgr->get_by_id($cNode['id'], + requirement_mgr::LATEST_VERSION); + $req_freeze_version = new stdClass(); + $req_freeze_version->req_version_id = $req[0]['version_id']; + $this->commandMgr->doFreezeVersion($req_freeze_version); + } + } + } + + $obj = $this->initGuiBean(); + $obj->template = 'show_message.tpl'; + $obj->template_dir = ''; + $obj->user_feedback = lang_get('req_frozen'); + $obj->main_descr = lang_get('req_spec') . TITLE_SEP . + $req_spec_info['title']; + $obj->title = lang_get('freeze_req'); + $obj->refreshTree = 0; + $obj->result = 'ok'; // needed to enable refresh_tree logic + return $obj; + } + + /** + * THIS METHOD NEED to be moved to common class because is also used + * on reqCommand.class.php + */ + private function simpleCompare($old, $new, $oldCF, $newCF) + { + // - log message is only forced to be entered when a custom field, title or document ID is changed + // - when only changes where made to scope user is free to create a new revision or + // overwrite the old revision (Cancel -> overwrite) + $ret = array( + 'force' => false, + 'suggest' => false, + 'nochange' => false, + 'changeon' => null + ); + + // key: var name to be used on $old + // value: var name to be used on $new + // Then to compare old and new + // $old[$key] compare to $new[$value] + // + $suggest_revision = array( + 'scope' => 'scope' + ); + $force_revision = array( + 'type' => 'reqSpecType', + 'doc_id' => 'doc_id', + 'title' => 'title' + ); + + foreach ($force_revision as $access_key => $access_prop) { + if ($ret['force'] = ($old[$access_key] != $new->$access_prop)) { + $ret['changeon'] = 'attribute:' . $access_key; + break; + } + } + + if (! $ret['force'] && ! is_null($newCF)) { + foreach ($newCF as $cf_key => $cf) { + if ($ret['force'] = ($oldCF[$cf_key]['value'] != $cf['cf_value'])) { + $ret['changeon'] = 'custom field:' . $oldCF[$cf_key]['name']; + break; + } + } + } + + if (! $ret['force']) { + foreach ($suggest_revision as $access_key => $access_prop) { + if ($ret['suggest'] = ($old[$access_key] != $new->$access_prop)) { + $ret['changeon'] = 'attribute:' . $access_key; + break; + } + } + } + $ret['nochange'] = (! $ret['force'] && ! $ret['suggest']); + return $ret; + } + + /* + * function: doCreateRevision + * + * args: + * + * returns: + * + * @internal revisions + * + */ + public function doCreateRevision(&$argsObj, $request) + { + $item = array( + 'log_message' => $argsObj->log_message, + 'author_id' => $argsObj->user_id + ); + $ret = $this->reqSpecMgr->clone_revision($argsObj->req_spec_id, $item); + + $obj = $this->initGuiBean(); + $obj->user_feedback = $ret['msg']; + $obj->template = "reqSpecView.php?req_spec_id={$argsObj->req_spec_id}"; + $obj->req_spec = null; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->req_spec_revision_id = $ret['id']; + return $obj; + } + + /** + */ + private function processRevision(&$guiObj, &$argsObj, &$userInput) + { + // TICKET 4661 + $itemOnDB = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + $who = array( + 'tproject_id' => $argsObj->tproject_id + ); + $cf_map = $this->reqSpecMgr->get_linked_cfields($who); + $newCFields = $this->reqSpecMgr->cfield_mgr->_build_cfield($userInput, + $cf_map); + + $who['item_id'] = $argsObj->req_spec_revision_id; + $oldCFields = $this->reqSpecMgr->get_linked_cfields($who); + $diff = $this->simpleCompare($itemOnDB, $argsObj, $oldCFields, + $newCFields); + + $createRev = false; + if ($diff['force'] && ! $argsObj->do_save) { + $guiObj->askForLog = true; + $guiObj->refreshTree = false; + + // Need Change several values with user input data, to match logic on + // edit php page on function renderGui() + // $map = array('status' => 'reqStatus', 'type' => 'reqSpecType','scope' => 'scope', + $map = array( + 'type' => 'reqSpecType', + 'scope' => 'scope', + 'doc_id' => 'doc_id', + 'title' => 'title' + ); + + foreach ($map as $k => $w) { + $guiObj->req_spec[$k] = $argsObj->$w; + } + $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_inputs( + null, null, $argsObj->tproject_id, null, null, $userInput); + } elseif ($diff['nochange'] || + (($createRev = $diff['force'] && ! $guiObj->askForLog) || + $argsObj->do_save)) { + + if ($argsObj->do_save == 1) { + $createRev = ($argsObj->save_rev == 1); + } + + $item = array(); + $item['id'] = $argsObj->req_spec_id; + $item['revision_id'] = $createRev ? - 1 : $argsObj->req_spec_revision_id; + $item['doc_id'] = $argsObj->doc_id; + $item['name'] = $argsObj->title; + $item['scope'] = $argsObj->scope; + $item['countReq'] = $argsObj->countReq; + $item['type'] = $argsObj->reqSpecType; + + $user_key = $createRev ? 'author_id' : 'modifier_id'; + $item[$user_key] = $argsObj->user_id; + + $opt = array( + 'skip_controls' => true, + 'create_rev' => $createRev, + 'log_message' => $argsObj->log_message + ); + $ret = $this->reqSpecMgr->update($item, $opt); + + $guiObj->user_feedback = $ret['msg']; + $guiObj->template = null; + + if ($ret['status_ok']) { + // custom fields update + $this->reqSpecMgr->values_to_db($userInput, $ret['revision_id'], + $cf_map); + + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->template = "reqSpecView.php?refreshTree={$argsObj->refreshTree}&" . + "req_spec_id={$guiObj->req_spec_id}"; + + // TODO + // logAuditEvent(TLS("audit_requirement_saved",$argsObj->reqDocId),"SAVE",$argsObj->req_id,"requirements"); + } else { + // Action has failed => no change done on DB. + $old = $this->reqSpecMgr->get_by_id($argsObj->req_id); + $guiObj->main_descr = $descr_prefix . $old['title']; + $guiObj->cfields = $this->reqSpecMgr->html_table_of_custom_field_values( + $argsObj->req_spec_id, $argsObj->req_spec_revision_id, + $argsObj->tproject_id); + } + } elseif ($diff['suggest']) { + $guiObj->askForRevision = true; + } + + return $guiObj; + } + + /** + */ + public function fileUpload(&$argsObj, $request) + { + $argsObj->uploadOp = fileUploadManagement($this->db, + $argsObj->req_spec_id, $argsObj->fileTitle, + $this->reqSpecMgr->getAttachmentTableName()); + return $this->initGuiObjForAttachmentOperations($argsObj); + } + + /** + */ + public function deleteFile(&$argsObj) + { + deleteAttachment($this->db, $argsObj->file_id); + return $this->initGuiObjForAttachmentOperations($argsObj); + } + + /** + */ + private function initGuiObjForAttachmentOperations($argsObj) + { + $guiObj = new stdClass(); + $guiObj->main_descr = ''; + $guiObj->action_descr = ''; + $guiObj->askForRevision = $guiObj->askForLog = false; + $guiObj->action_status_ok = true; + $guiObj->req_spec_id = $argsObj->req_spec_id; + $guiObj->template = "reqSpecView.php?refreshTree=0&req_spec_id={$argsObj->req_spec_id}"; + + $guiObj->uploadOp = $argsObj->uploadOp; + + return $guiObj; + } + + /* + * function: copyRequirements + * + * args: + * + * returns: + * + */ + public function bulkReqMon(&$argsObj, $options = null) + { + $obj = $this->initGuiBean(); + $req_spec = $this->reqSpecMgr->get_by_id($argsObj->req_spec_id); + + $my['options'] = array( + 'get_items' => true + ); + $my['options'] = array_merge($my['options'], (array) $options); + + if ($my['options']['get_items']) { + $opt = $this->getRequirementsOptions + + array( + 'outputLevel' => 'minimal', + 'decodeUsers' => false + ); + $obj->items = $this->reqSpecMgr->get_requirements( + $argsObj->req_spec_id, 'all', null, $opt); + } + + $opx = array( + 'reqSpecID' => $argsObj->req_spec_id + ); + $monSet = $this->reqMgr->getMonitoredByUser($argsObj->user_id, + $argsObj->tproject_id, $opx); + + $obj->enable_start_btn = false; + $obj->enable_stop_btn = false; + foreach ($obj->items as &$itx) { + $onOff = isset($monSet[$itx['id']]) ? true : false; + $itx['monitor'] = $onOff ? 'On' : 'Off'; + $obj->enable_start_btn |= ! $onOff; + $obj->enable_stop_btn |= $onOff; + } + + $obj->main_descr = lang_get('req_spec') . TITLE_SEP . $req_spec['title']; + $obj->action_descr = lang_get('bulk_monitoring'); + $obj->template = 'reqBulkMon.tpl'; + $obj->containers = null; + $obj->page2call = 'lib/requirements/reqSpecEdit.php'; + $obj->array_of_msg = ''; + $obj->doActionButton = 'do' . ucfirst(__FUNCTION__); + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->refreshTree = 0; + + return $obj; + } + + /** + */ + public function doBulkReqMon(&$argsObj) + { + $obj = $this->initGuiBean(); + $obj->req = null; + $obj->req_spec_id = $argsObj->req_spec_id; + $obj->array_of_msg = ''; + + $m2r = null; + switch ($argsObj->op) { + case 'toogleMon': + $opx = array( + 'reqSpecID' => $argsObj->req_spec_id + ); + $monSet = $this->reqMgr->getMonitoredByUser($argsObj->user_id, + $argsObj->tproject_id, $opx); + + foreach ($argsObj->itemSet as $req_id) { + $f2r = isset($monSet[$req_id]) ? 'monitorOff' : 'monitorOn'; + $this->reqMgr->$f2r($req_id, $argsObj->user_id, + $argsObj->tproject_id); + } + break; + + case 'startMon': + $m2r = 'monitorOn'; + break; + + case 'stopMon': + $m2r = 'monitorOff'; + break; + } + + if (! is_null($m2r)) { + foreach ($argsObj->itemSet as $req_id) { + $this->reqMgr->$m2r($req_id, $argsObj->user_id, + $argsObj->tproject_id); + } + } + + return $this->bulkReqMon($argsObj); + } } diff --git a/lib/requirements/reqSpecCompareRevisions.php b/lib/requirements/reqSpecCompareRevisions.php index c9cf22a501..97652a9b96 100644 --- a/lib/requirements/reqSpecCompareRevisions.php +++ b/lib/requirements/reqSpecCompareRevisions.php @@ -1,327 +1,324 @@ - null,"no_changes" => null, - "version_short" => null,"diff_details_rev" => null, - "type" => null, "status" => null, "name" => "title", - "doc_id" => null,"revision_short" => null, "revision" => null) ); - - - -$itemMgr = new requirement_spec_mgr($db); -$differ = new diff(); -$args = init_args(); -$gui = initializeGui($db,$args,$labels,$itemMgr); - -// if already two revisions are selected, display diff -// else display template with versions to select -if ($args->doCompare) -{ - // Side By Side - $sbs = getItemsToCompare($args->left_item_id,$args->right_item_id,$gui->items); - prepareUserFeedback($db,$gui,$args->req_spec_id,$labels,$sbs); - - $gui->attrDiff = getAttrDiff($sbs['left_item'],$sbs['right_item'],$labels); - - $cfields = getCFToCompare($sbs,$args->tproject_id,$itemMgr); - $gui->cfieldsDiff = null; - if( !is_null($cfields) ) - { - $gui->cfieldsDiff = getCFDiff($cfields,$itemMgr); - } - - $gui->diff = array("scope" => array()); - foreach($gui->diff as $key => $val) - { - if ($args->useDaisyDiff) - { - $diff = new HTMLDiffer(); - list($differences, $diffcount) = $diff->htmlDiff($sbs['left_item'][$key], $sbs['right_item'][$key]); - $gui->diff[$key]["diff"] = $differences; - $gui->diff[$key]["count"] = $diffcount; - } else { - // insert line endings so diff is better readable and makes sense (not everything in one line) - // then cast to array with \n as separating character, differ needs that - $gui->diff[$key]["left"] = explode("\n", str_replace("

    ", "

    \n", $sbs['left_item'][$key])); - $gui->diff[$key]["right"] = explode("\n", str_replace("

    ", "

    \n", $sbs['right_item'][$key])); - - $gui->diff[$key]["diff"] = $differ->inline($gui->diff[$key]["left"], $gui->leftID, - $gui->diff[$key]["right"], $gui->rightID,$args->context); - $gui->diff[$key]["count"] = count($differ->changes); - } - - $gui->diff[$key]["heading"] = lang_get($key); - - // are there any changes? then display! if not, nothing to show here - $additional = ''; - $msg_key = "no_changes"; - if ($gui->diff[$key]["count"] > 0) - { - $msg_key = "num_changes"; - $additional = $gui->diff[$key]["count"]; - } - $gui->diff[$key]["message"] = sprintf($labels[$msg_key], $key, $additional); - } - -} - -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -/** - * - * - */ -function getItemsToCompare($leftSideID,$rightSideID,&$itemSet) -{ - - $ret = array(); - foreach($itemSet as $item) - { - if ($item['item_id'] == $leftSideID) - { - $ret['left_item'] = $item; - } - if ($item['item_id'] == $rightSideID) - { - $ret['right_item'] = $item; - } - - if( count($ret) == 2 ) - { - break; - } - } - return $ret; -} - - -/** - * - * - */ -function getCFToCompare($sides,$tprojectID,&$itemMgr) -{ - $cfields = array('left_side' => array('key' => 'left_item', 'value' => null), - 'right_side' => array('key' => 'right_item', 'value' => null)); - - - $who = array('parent_id' => null, 'item_id' => 0, 'tproject_id' => $tprojectID); - foreach($cfields as $item_side => $dummy) - { - $target_id = $sides[$dummy['key']]; - // $target_id = $target_id['item_id']; - // $cfields[$item_side]['value'] = $itemMgr->get_linked_cfields(null,$target_id,$tprojectID); - $who['item_id'] = $target_id['item_id']; - $cfields[$item_side]['value'] = $itemMgr->get_linked_cfields($who); - } - return $cfields; -} - - -/** - * - * - * @internal revisions - * 20101211 - franciscom - use show_custom_fields_without_value - */ -function getCFDiff($cfields,&$itemMgr) -{ - $cmp = null; - - // Development Note - // All versions + revisions (i.e. child items) have the same qty of linked CF - // => both arrays will have same size() - // - // This is because to get cfields we look only to CF enabled for node type. - $cfieldsLeft = $cfields['left_side']['value']; - $cfieldsRight = $cfields['right_side']['value']; - if( !is_null($cfieldsLeft) ) - { - $key2loop = array_keys($cfieldsLeft); - $cmp = array(); - $type_code = $itemMgr->cfield_mgr->get_available_types(); - $key2convert = array('lvalue','rvalue'); - - $formats = array('date' => config_get( 'date_format')); - $cfg = config_get('gui'); - $cfCfg = config_get('custom_fields'); - foreach($key2loop as $cf_key) - { - // $cfg->show_custom_fields_without_value - // false => At least one value has to be <> NULL to include on comparsion results - // - if( $cfCfg->show_custom_fields_without_value == true || - ($cfCfg->show_custom_fields_without_value == false && - ( (!is_null($cfieldsRight) && !is_null($cfieldsRight[$cf_key]['value'])) || - (!is_null($cfieldsLeft) && !is_null($cfieldsLeft[$cf_key]['value'])) ) - ) - ) - { - $cmp[$cf_key] = array('label' => htmlspecialchars($cfieldsLeft[$cf_key]['label']), - 'lvalue' => $cfieldsLeft[$cf_key]['value'], - 'rvalue' => !is_null($cfieldsRight) ? $cfieldsRight[$cf_key]['value'] : null, - 'changed' => $cfieldsLeft[$cf_key]['value'] != $cfieldsRight[$cf_key]['value']); - - if($type_code[$cfieldsLeft[$cf_key]['type']] == 'date' || - $type_code[$cfieldsLeft[$cf_key]['type']] == 'datetime') - { - $t_date_format = str_replace("%","",$formats['date']); // must remove % - foreach($key2convert as $fx) - { - if( ($doIt = ($cmp[$cf_key][$fx] != null)) ) - { - switch($type_code[$cfieldsLeft[$cf_key]['type']]) - { - case 'datetime': - $t_date_format .= " " . $cfg->custom_fields->time_format; - break ; - } - } - if( $doIt ) - { - $cmp[$cf_key][$fx] = date($t_date_format,$cmp[$cf_key][$fx]); - } - } - } - } // mega if - } // foraeach - } - return count($cmp) > 0 ? $cmp : null; -} - - - -/** - * - * - */ -function init_args() -{ - $_REQUEST=strings_stripSlashes($_REQUEST); - - - $args = new stdClass(); - $args->req_spec_id = isset($_REQUEST['req_spec_id']) ? intval($_REQUEST['req_spec_id']) : 0; - $args->doCompare = isset($_REQUEST['doCompare']) ? true : false; - $args->left_item_id = isset($_REQUEST['left_item_id']) ? intval($_REQUEST['left_item_id']) : -1; - $args->right_item_id = isset($_REQUEST['right_item_id']) ? intval($_REQUEST['right_item_id']) : -1; - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->useDaisyDiff = (isset($_REQUEST['diff_method']) && ($_REQUEST['diff_method'] == 'htmlCompare')) ? 1 : 0; - - - - $diffEngineCfg = config_get("diffEngine"); - $args->context = null; - if( !isset($_REQUEST['context_show_all'])) - { - $args->context = (isset($_REQUEST['context']) && is_numeric($_REQUEST['context'])) ? $_REQUEST['context'] : $diffEngineCfg->context; - } - - return $args; -} - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,$lbl,&$itemMgr) -{ - $reqSpecCfg = config_get('req_spec_cfg'); - $guiObj = new stdClass(); - $guiObj->items = $itemMgr->get_history($argsObj->req_spec_id,array('output' => 'array','decode_user' => true)); - - // Truncate log message - if( $reqSpecCfg->log_message_len > 0 ) - { - $loop2do = count($guiObj->items); - for($idx=0; $idx < $loop2do; $idx++) - { - if( strlen($guiObj->items[$idx]['log_message']) > $reqSpecCfg->log_message_len ) - { - $guiObj->items[$idx]['log_message'] = substr($guiObj->items[$idx]['log_message'],0,$reqSpecCfg->log_message_len) . '...'; - } - $guiObj->items[$idx]['log_message'] = htmlspecialchars($guiObj->items[$idx]['log_message']); - } - } - $guiObj->req_spec_id = $argsObj->req_spec_id; - $guiObj->doCompare = $argsObj->doCompare; - $guiObj->context = $argsObj->context; - $guiObj->version_short = $lbl['version_short']; - $guiObj->diff = null; - return $guiObj; -} - -/** - * - * - */ -function prepareUserFeedback(&$dbHandler,&$guiObj,$itemID,$labels,$sbs) -{ - $guiObj->leftID = $labels['revision'] . ':' . $sbs['left_item']['revision']; - $guiObj->rightID = $labels['revision'] . ':' . $sbs['right_item']['revision']; - $guiObj->subtitle = sprintf($labels['diff_details_rev'], - $sbs['left_item']['revision'],$sbs['left_item']['revision'], - $sbs['right_item']['revision'],$sbs['right_item']['revision']); - - -} - - - -/** - * - * - */ -function getAttrDiff($leftSide,$rightSide,$labels) -{ - $req_spec_cfg = config_get('req_spec_cfg'); - - // attribute => label definition on TL configuration (just if NOT NULL) - // order in this array will drive display order - // $key2loop = array('doc_id' => null,'status' => 'status_labels','type' => 'type_labels'); - $key2loop = array('doc_id' => null,'name' => null,'type' => 'type_labels'); - foreach($key2loop as $fkey => $lkey) - { - // Need to decode - $cmp[$fkey] = array('label' => htmlspecialchars($labels[$fkey]), - 'lvalue' => $leftSide[$fkey],'rvalue' => $rightSide[$fkey], - 'changed' => $leftSide[$fkey] != $rightSide[$fkey]); - - if( !is_null($lkey) ) - { - $decode = $req_spec_cfg->$lkey; - $cmp[$fkey]['lvalue'] = lang_get($decode[$cmp[$fkey]['lvalue']]); - $cmp[$fkey]['rvalue'] = lang_get($decode[$cmp[$fkey]['rvalue']]); - } - } - return $cmp; -} -?> \ No newline at end of file + null, + "no_changes" => null, + "version_short" => null, + "diff_details_rev" => null, + "type" => null, + "status" => null, + "name" => "title", + "doc_id" => null, + "revision_short" => null, + "revision" => null + )); + +$itemMgr = new requirement_spec_mgr($db); +$differ = new diff(); +$args = initArgs(); +$gui = initializeGui($db, $args, $labels, $itemMgr); + +// if already two revisions are selected, display diff +// else display template with versions to select +if ($args->doCompare) { + // Side By Side + $sbs = getItemsToCompare($args->left_item_id, $args->right_item_id, + $gui->items); + prepareUserFeedback($db, $gui, $labels, $sbs); + + $gui->attrDiff = getAttrDiff($sbs['left_item'], $sbs['right_item'], $labels); + + $cfields = getCFToCompare($sbs, $args->tproject_id, $itemMgr); + $gui->cfieldsDiff = null; + if (! is_null($cfields)) { + $gui->cfieldsDiff = getCFDiff($cfields, $itemMgr); + } + + $gui->diff = array( + "scope" => array() + ); + foreach ($gui->diff as $key => $val) { + if ($args->useDaisyDiff) { + $diff = new HTMLDiffer(); + list ($differences, $diffcount) = $diff->htmlDiff( + $sbs['left_item'][$key], $sbs['right_item'][$key]); + $gui->diff[$key]["diff"] = $differences; + $gui->diff[$key]["count"] = $diffcount; + } else { + // insert line endings so diff is better readable and makes sense (not everything in one line) + // then cast to array with \n as separating character, differ needs that + $gui->diff[$key]["left"] = explode("\n", + str_replace("

    ", "

    \n", $sbs['left_item'][$key])); + $gui->diff[$key]["right"] = explode("\n", + str_replace("

    ", "

    \n", $sbs['right_item'][$key])); + + $gui->diff[$key]["diff"] = $differ->inline($gui->diff[$key]["left"], + $gui->leftID, $gui->diff[$key]["right"], $gui->rightID, + $args->context); + $gui->diff[$key]["count"] = count($differ->changes); + } + + $gui->diff[$key]["heading"] = lang_get($key); + + // are there any changes? then display! if not, nothing to show here + $additional = ''; + $msg_key = "no_changes"; + if ($gui->diff[$key]["count"] > 0) { + $msg_key = "num_changes"; + $additional = $gui->diff[$key]["count"]; + } + $gui->diff[$key]["message"] = sprintf($labels[$msg_key], $key, + $additional); + } +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function getItemsToCompare($leftSideID, $rightSideID, &$itemSet) +{ + $ret = array(); + foreach ($itemSet as $item) { + if ($item['item_id'] == $leftSideID) { + $ret['left_item'] = $item; + } + if ($item['item_id'] == $rightSideID) { + $ret['right_item'] = $item; + } + + if (count($ret) == 2) { + break; + } + } + return $ret; +} + +/** + */ +function getCFToCompare($sides, $tprojectID, &$itemMgr) +{ + $cfields = array( + 'left_side' => array( + 'key' => 'left_item', + 'value' => null + ), + 'right_side' => array( + 'key' => 'right_item', + 'value' => null + ) + ); + + $who = array( + 'parent_id' => null, + 'item_id' => 0, + 'tproject_id' => $tprojectID + ); + foreach ($cfields as $item_side => $dummy) { + $target_id = $sides[$dummy['key']]; + $who['item_id'] = $target_id['item_id']; + $cfields[$item_side]['value'] = $itemMgr->get_linked_cfields($who); + } + return $cfields; +} + +/** + * + * @internal revisions + * 20101211 - franciscom - use show_custom_fields_without_value + */ +function getCFDiff($cfields, &$itemMgr) +{ + $cmp = null; + + // Development Note + // All versions + revisions (i.e. child items) have the same qty of linked CF + // => both arrays will have same size() + // + // This is because to get cfields we look only to CF enabled for node type. + $cfieldsLeft = $cfields['left_side']['value']; + $cfieldsRight = $cfields['right_side']['value']; + if (! is_null($cfieldsLeft)) { + $key2loop = array_keys($cfieldsLeft); + $cmp = array(); + $type_code = $itemMgr->cfield_mgr->get_available_types(); + $key2convert = array( + 'lvalue', + 'rvalue' + ); + + $formats = array( + 'date' => config_get('date_format') + ); + $cfg = config_get('gui'); + $cfCfg = config_get('custom_fields'); + foreach ($key2loop as $cf_key) { + // $cfg->show_custom_fields_without_value + // false => At least one value has to be <> NULL to include on comparsion results + if ($cfCfg->show_custom_fields_without_value || + (! $cfCfg->show_custom_fields_without_value && + ((! is_null($cfieldsRight) && + ! is_null($cfieldsRight[$cf_key]['value'])) || + (! is_null($cfieldsLeft) && + ! is_null($cfieldsLeft[$cf_key]['value']))))) { + $cmp[$cf_key] = array( + 'label' => htmlspecialchars($cfieldsLeft[$cf_key]['label']), + 'lvalue' => $cfieldsLeft[$cf_key]['value'], + 'rvalue' => ! is_null($cfieldsRight) ? $cfieldsRight[$cf_key]['value'] : null, + 'changed' => $cfieldsLeft[$cf_key]['value'] != + $cfieldsRight[$cf_key]['value'] + ); + + if ($type_code[$cfieldsLeft[$cf_key]['type']] == 'date' || + $type_code[$cfieldsLeft[$cf_key]['type']] == 'datetime') { + $t_date_format = str_replace("%", "", $formats['date']); // must remove % + foreach ($key2convert as $fx) { + if ($doIt = ($cmp[$cf_key][$fx] != null)) { + switch ($type_code[$cfieldsLeft[$cf_key]['type']]) { + case 'datetime': + $t_date_format .= " " . + $cfg->custom_fields->time_format; + break; + } + } + if ($doIt) { + $cmp[$cf_key][$fx] = date($t_date_format, + $cmp[$cf_key][$fx]); + } + } + } + } + } + } + return ! empty($cmp) ? $cmp : null; +} + +/** + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->req_spec_id = isset($_REQUEST['req_spec_id']) ? intval( + $_REQUEST['req_spec_id']) : 0; + $args->doCompare = isset($_REQUEST['doCompare']) ? true : false; + $args->left_item_id = isset($_REQUEST['left_item_id']) ? intval( + $_REQUEST['left_item_id']) : - 1; + $args->right_item_id = isset($_REQUEST['right_item_id']) ? intval( + $_REQUEST['right_item_id']) : - 1; + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->useDaisyDiff = (isset($_REQUEST['diff_method']) && + ($_REQUEST['diff_method'] == 'htmlCompare')) ? 1 : 0; + + $diffEngineCfg = config_get("diffEngine"); + $args->context = null; + if (! isset($_REQUEST['context_show_all'])) { + $args->context = (isset($_REQUEST['context']) && + is_numeric($_REQUEST['context'])) ? $_REQUEST['context'] : $diffEngineCfg->context; + } + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj, $lbl, &$itemMgr) +{ + $reqSpecCfg = config_get('req_spec_cfg'); + $guiObj = new stdClass(); + $guiObj->items = $itemMgr->get_history($argsObj->req_spec_id, + array( + 'output' => 'array', + 'decode_user' => true + )); + + // Truncate log message + if ($reqSpecCfg->log_message_len > 0) { + $loop2do = count($guiObj->items); + for ($idx = 0; $idx < $loop2do; $idx ++) { + if (strlen($guiObj->items[$idx]['log_message']) > + $reqSpecCfg->log_message_len) { + $guiObj->items[$idx]['log_message'] = substr( + $guiObj->items[$idx]['log_message'], 0, + $reqSpecCfg->log_message_len) . '...'; + } + $guiObj->items[$idx]['log_message'] = htmlspecialchars( + $guiObj->items[$idx]['log_message']); + } + } + $guiObj->req_spec_id = $argsObj->req_spec_id; + $guiObj->doCompare = $argsObj->doCompare; + $guiObj->context = $argsObj->context; + $guiObj->version_short = $lbl['version_short']; + $guiObj->diff = null; + return $guiObj; +} + +/** + */ +function prepareUserFeedback(&$dbHandler, &$guiObj, $labels, $sbs) +{ + $guiObj->leftID = $labels['revision'] . ':' . $sbs['left_item']['revision']; + $guiObj->rightID = $labels['revision'] . ':' . $sbs['right_item']['revision']; + $guiObj->subtitle = sprintf($labels['diff_details_rev'], + $sbs['left_item']['revision'], $sbs['left_item']['revision'], + $sbs['right_item']['revision'], $sbs['right_item']['revision']); +} + +/** + */ +function getAttrDiff($leftSide, $rightSide, $labels) +{ + $req_spec_cfg = config_get('req_spec_cfg'); + + // attribute => label definition on TL configuration (just if NOT NULL) + // order in this array will drive display order + $key2loop = array( + 'doc_id' => null, + 'name' => null, + 'type' => 'type_labels' + ); + foreach ($key2loop as $fkey => $lkey) { + // Need to decode + $cmp[$fkey] = array( + 'label' => htmlspecialchars($labels[$fkey]), + 'lvalue' => $leftSide[$fkey], + 'rvalue' => $rightSide[$fkey], + 'changed' => $leftSide[$fkey] != $rightSide[$fkey] + ); + + if (! is_null($lkey)) { + $decode = $req_spec_cfg->$lkey; + $cmp[$fkey]['lvalue'] = lang_get($decode[$cmp[$fkey]['lvalue']]); + $cmp[$fkey]['rvalue'] = lang_get($decode[$cmp[$fkey]['rvalue']]); + } + } + return $cmp; +} +?> diff --git a/lib/requirements/reqSpecEdit.php b/lib/requirements/reqSpecEdit.php index f630c389d3..bf3a68e0d9 100644 --- a/lib/requirements/reqSpecEdit.php +++ b/lib/requirements/reqSpecEdit.php @@ -1,265 +1,317 @@ -tproject_id); - -$gui = initialize_gui($db,$args,$req_cfg,$commandMgr); - -$auditContext = new stdClass(); -$auditContext->tproject = $args->tproject_name; -$commandMgr->setAuditContext($auditContext); - -$pFn = $args->doAction; -$op = null; -if(method_exists($commandMgr,$pFn)) -{ - $op = $commandMgr->$pFn($args,$_REQUEST); +tproject_id); +$gui = initializeGui($db, $args, $req_cfg, $commandMgr); + +$context = new stdClass(); +$context->tproject_id = $args->tproject_id; +checkRights($db, $args->user, $context); + +$auditContext = new stdClass(); +$auditContext->tproject = $args->tproject_name; +$commandMgr->setAuditContext($auditContext); + +$pFn = $args->doAction; +$op = null; +if (method_exists($commandMgr, $pFn)) { + $op = $commandMgr->$pFn($args, $_REQUEST); +} + +renderGui($args, $gui, $op, $templateCfg, $editorCfg); + +/** + */ +function initArgs() +{ + $iParams = array( + "countReq" => array( + tlInputParameter::INT_N, + 99999 + ), + "req_spec_id" => array( + tlInputParameter::INT_N + ), + "req_spec_revision_id" => array( + tlInputParameter::INT_N + ), + "parentID" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 250 + ), + "title" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "scope" => array( + tlInputParameter::STRING_N + ), + "doc_id" => array( + tlInputParameter::STRING_N, + 1, + 32 + ), + "nodes_order" => array( + tlInputParameter::ARRAY_INT + ), + "containerID" => array( + tlInputParameter::INT_N + ), + "itemSet" => array( + tlInputParameter::ARRAY_INT + ), + "reqSpecType" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "copy_testcase_assignment" => array( + tlInputParameter::CB_BOOL + ), + "save_rev" => array( + tlInputParameter::INT_N + ), + "do_save" => array( + tlInputParameter::INT_N + ), + "log_message" => array( + tlInputParameter::STRING_N + ), + "file_id" => array( + tlInputParameter::INT_N + ), + "fileTitle" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + // i guess due to required revison log it is necessary to strip slashes + // after R_PARAMS call - at least this fixed the problem + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ""; + $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; + $args->basehref = $_SESSION['basehref']; + + $args->parentID = is_null($args->parentID) ? $args->tproject_id : $args->parentID; + + $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? $_SESSION['setting_refresh_tree_on_action'] : 0; + + $args->countReq = is_null($args->countReq) ? 0 : intval($args->countReq); + + // Process buttons + $args->op = null; + $btnSet = array( + 'toogleMon', + 'startMon', + 'stopMon' + ); + foreach ($btnSet as $btn) { + if (isset($_REQUEST[$btn])) { + $args->op = $btn; + break; + } + } + + $args->user = $_SESSION['currentUser']; + + return $args; +} + +/** + * renderGui + */ +function renderGui(&$argsObj, $guiObj, $opObj, $templateCfg, $editorCfg) +{ + $smartyObj = new TLSmarty(); + $renderType = 'none'; + $tpl = $tpd = null; + + $actionOperation = array( + 'create' => 'doCreate', + 'edit' => 'doUpdate', + 'doDelete' => '', + 'doReorder' => '', + 'reorder' => '', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate', + 'createChild' => 'doCreate', + 'copy' => 'doCopy', + 'doCopy' => 'doCopy', + 'doFreeze' => 'doFreeze', + 'copyRequirements' => 'doCopyRequirements', + 'doCopyRequirements' => 'doCopyRequirements', + 'doCreateRevision' => 'doCreateRevision', + 'fileUpload' => '', + 'deleteFile' => '', + 'bulkReqMon' => 'doBulkReqMon', + 'doBulkReqMon' => 'doBulkReqMon' + ); + // ------------------------------------------------------------------------------------------------ + // Web Editor Processing + $owebEditor = web_editor('scope', $argsObj->basehref, $editorCfg); + switch ($argsObj->doAction) { + case "edit": + case "doCreate": + $owebEditor->Value = $argsObj->scope; + break; + + case "fileUpload": + case "deleteFile": + break; + + default: + if ($opObj->askForRevision || $opObj->askForLog || + ! $opObj->action_status_ok) { + $owebEditor->Value = $argsObj->scope; + } else { + $owebEditor->Value = getItemTemplateContents( + 'req_spec_template', $owebEditor->InstanceName, + $argsObj->scope); + } + break; + } + $guiObj->scope = $owebEditor->CreateHTML(); + $guiObj->editorType = $editorCfg['type']; + + // Tree refresh Processing + switch ($argsObj->doAction) { + case "doCreate": + case "doUpdate": + case "doCopyRequirements": + case "doCopy": + case "doFreeze": + case "doDelete": + case "doBulkReqMon": + $guiObj->refreshTree = $argsObj->refreshTree; + break; + } + + // GUI rendering Processing + switch ($argsObj->doAction) { + case "edit": + case "create": + case "createChild": + case "reorder": + case "doDelete": + case "doReorder": + case "doCreate": + case "doUpdate": + case "copyRequirements": + case "doCopyRequirements": + case "copy": + case "doCopy": + case "doFreeze": + case "doCreateRevision": + case "fileUpload": + case "deleteFile": + case "bulkReqMon": + case "doBulkReqMon": + $renderType = 'template'; + $key2loop = get_object_vars($opObj); + + if (! $opObj->action_status_ok) { + // Remember that scope normally is a WebRichEditor, and that + // we have already processed WebRichEditor + // Need to understand if remove of scope key can be done always + // no matter action_status_ok + unset($key2loop['scope']); + } + foreach ($key2loop as $key => $value) { + $guiObj->$key = $value; + } + + $guiObj->operation = $actionOperation[$argsObj->doAction]; + $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; + $tpd = isset($key2loop['template_dir']) ? $opObj->template_dir : $templateCfg->template_dir; + + $pos = strpos($tpl, '.php'); + if ($pos === false) { + $tpl = $tpd . $tpl; + } else { + $renderType = 'redirect'; + if (! is_null($guiObj->uploadOp) && ! $guiObj->uploadOp->statusOK) { + $tpl .= "&uploadOPStatusCode=" . + $guiObj->uploadOp->statusCode; + } + } + break; + } + + switch ($renderType) { + case 'template': + $smartyObj->assign('mgt_view_events', + has_rights($db, "mgt_view_events")); + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($tpl); + break; + + case 'redirect': + header("Location: {$tpl}"); + exit(); + break; + + default: + echo 'Can not process RENDERING!!!'; + break; + } +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj, &$req_cfg, &$commandMgr) +{ + $gui = $commandMgr->initGuiBean(); + $gui->parentID = $argsObj->parentID; + $gui->user_feedback = null; + $gui->main_descr = null; + $gui->action_descr = null; + $gui->refreshTree = 0; + $gui->external_req_management = ($req_cfg->external_req_management == ENABLED) ? 1 : 0; + $gui->grants = new stdClass(); + $gui->grants->req_mgmt = has_rights($dbHandler, "mgt_modify_req"); + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req", + "mgt_modify_req" + ]; + pageAccessCheck($db, $user, $context); } - -renderGui($args,$gui,$op,$templateCfg,$editorCfg); - - -/** - * - * - */ -function init_args() -{ - $args = new stdClass(); - - $iParams = array("countReq" => array(tlInputParameter::INT_N,99999), - "req_spec_id" => array(tlInputParameter::INT_N), - "req_spec_revision_id" => array(tlInputParameter::INT_N), - "parentID" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,250), - "title" => array(tlInputParameter::STRING_N,0,100), - "scope" => array(tlInputParameter::STRING_N), - "doc_id" => array(tlInputParameter::STRING_N,1,32), - "nodes_order" => array(tlInputParameter::ARRAY_INT), - "containerID" => array(tlInputParameter::INT_N), - "itemSet" => array(tlInputParameter::ARRAY_INT), - "reqSpecType" => array(tlInputParameter::STRING_N,0,1), - "copy_testcase_assignment" => array(tlInputParameter::CB_BOOL), - "save_rev" => array(tlInputParameter::INT_N), - "do_save" => array(tlInputParameter::INT_N), - "log_message" => array(tlInputParameter::STRING_N), - "file_id" => array(tlInputParameter::INT_N), - "fileTitle" => array(tlInputParameter::STRING_N,0,100)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - // i guess due to required revison log it is necessary to strip slashes - // after R_PARAMS call - at least this fixed the problem - $_REQUEST=strings_stripSlashes($_REQUEST); - - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ""; - $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; - $args->basehref = $_SESSION['basehref']; - - $args->parentID = is_null($args->parentID) ? $args->tproject_id : $args->parentID; - - $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) - ? $_SESSION['setting_refresh_tree_on_action'] : 0; - - $args->countReq = is_null($args->countReq) ? 0 : intval($args->countReq); - - // Process buttons - $args->op = null; - $btnSet = array('toogleMon','startMon','stopMon'); - foreach( $btnSet as $btn ) - { - if( isset($_REQUEST[$btn]) ) - { - $args->op = $btn; - break; - } - } - - return $args; -} - - -/** - * renderGui - * - */ -function renderGui(&$argsObj,$guiObj,$opObj,$templateCfg,$editorCfg) -{ - $smartyObj = new TLSmarty(); - $renderType = 'none'; - $tpl = $tpd = null; - - $actionOperation = array('create' => 'doCreate', 'edit' => 'doUpdate', - 'doDelete' => '', 'doReorder' => '', 'reorder' => '', - 'doCreate' => 'doCreate', 'doUpdate' => 'doUpdate', - 'createChild' => 'doCreate', 'copy' => 'doCopy', - 'doCopy' => 'doCopy', - 'doFreeze' => 'doFreeze', - 'copyRequirements' => 'doCopyRequirements', - 'doCopyRequirements' => 'doCopyRequirements', - 'doCreateRevision' => 'doCreateRevision', - 'fileUpload' => '', 'deleteFile' => '', - 'bulkReqMon' => 'doBulkReqMon', - 'doBulkReqMon' => 'doBulkReqMon'); - // ------------------------------------------------------------------------------------------------ - // Web Editor Processing - $owebEditor = web_editor('scope',$argsObj->basehref,$editorCfg) ; - switch($argsObj->doAction) - { - case "edit": - case "doCreate": - $owebEditor->Value = $argsObj->scope; - break; - - case "fileUpload": - case "deleteFile": - break; - - default: - if($opObj->askForRevision || $opObj->askForLog || !$opObj->action_status_ok) - { - $owebEditor->Value = $argsObj->scope; - } - else - { - $owebEditor->Value = getItemTemplateContents('req_spec_template',$owebEditor->InstanceName,$argsObj->scope); - } - break; - } - $guiObj->scope = $owebEditor->CreateHTML(); - $guiObj->editorType = $editorCfg['type']; - - // Tree refresh Processing - switch($argsObj->doAction) - { - case "doCreate": - case "doUpdate": - case "doCopyRequirements": - case "doCopy": - case "doFreeze": - case "doDelete": - case "doBulkReqMon": - $guiObj->refreshTree = $argsObj->refreshTree; - break; - } - - // GUI rendering Processing - switch($argsObj->doAction) - { - case "edit": - case "create": - case "createChild": - case "reorder": - case "doDelete": - case "doReorder": - case "doCreate": - case "doUpdate": - case "copyRequirements": - case "doCopyRequirements": - case "copy": - case "doCopy": - case "doFreeze": - case "doCreateRevision": - case "fileUpload": - case "deleteFile": - case "bulkReqMon": - case "doBulkReqMon": - $renderType = 'template'; - $key2loop = get_object_vars($opObj); - - if($opObj->action_status_ok == false) - { - // Remember that scope normally is a WebRichEditor, and that - // we have already processed WebRichEditor - // Need to understand if remove of scope key can be done always - // no matter action_status_ok - unset($key2loop['scope']); - } - foreach($key2loop as $key => $value) - { - $guiObj->$key = $value; - } - - $guiObj->operation = $actionOperation[$argsObj->doAction]; - $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; - $tpd = isset($key2loop['template_dir']) ? $opObj->template_dir : $templateCfg->template_dir; - - $pos = strpos($tpl, '.php'); - if ($pos === false) { - $tpl = $tpd . $tpl; - } else { - $renderType = 'redirect'; - if (null != $guiObj->uploadOp && $guiObj->uploadOp->statusOK == false) { - $tpl .= "&uploadOPStatusCode=" . $guiObj->uploadOp->statusCode; - } - } - break; - } - - switch($renderType) - { - case 'template': - $smartyObj->assign('mgt_view_events',has_rights($db,"mgt_view_events")); - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($tpl); - break; - - case 'redirect': - header("Location: {$tpl}"); - exit(); - break; - - default: - echo 'Can not process RENDERING!!!'; - break; - } -} - -/** - * - * - */ -function initialize_gui(&$dbHandler, &$argsObj, &$req_cfg, &$commandMgr) -{ - $gui = $commandMgr->initGuiBean(); - $gui->parentID = $argsObj->parentID; - $gui->user_feedback = null; - $gui->main_descr = null; - $gui->action_descr = null; - $gui->refreshTree = 0; - $gui->external_req_management = ($req_cfg->external_req_management == ENABLED) ? 1 : 0; - $gui->grants = new stdClass(); - $gui->grants->req_mgmt = has_rights($dbHandler,"mgt_modify_req"); - - return $gui; -} - -/** - * - * - */ -function checkRights(&$db,&$user) -{ - return ($user->hasRight($db,'mgt_view_req') && $user->hasRight($db,'mgt_modify_req')); -} \ No newline at end of file diff --git a/lib/requirements/reqSpecListTree.php b/lib/requirements/reqSpecListTree.php index 4084ee04ae..a69325c334 100644 --- a/lib/requirements/reqSpecListTree.php +++ b/lib/requirements/reqSpecListTree.php @@ -1,75 +1,86 @@ -build_tree_menu($gui); -$control->formAction = ''; - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->assign('control', $control); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - */ -function init_args() -{ - $args = new stdClass(); - $args->tproject_id = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 'undefned'; - $args->basehref = $_SESSION['basehref']; - - return $args; +tproject_id = $args->tproject_id; +checkRights($db, $args->user, $ctx); + +$control = new tlRequirementFilterControl($db); +$control->build_tree_menu($gui); +$control->formAction = ''; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('control', $control); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initArgs() +{ + $args = new stdClass(); + $args->tproject_id = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 'undefned'; + $args->basehref = $_SESSION['basehref']; + + $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; + + return $args; +} + +/* + * function: initializeGui + * initialize gui (stdClass) object that will be used as argument + * in call to Template Engine. + * + * args: argsObj: object containing User Input and some session values + * basehref: URL to web home of your testlink installation. + * + * returns: stdClass object + * + * rev: + * + */ +function initializeGui($argsObj) +{ + $gui = new stdClass(); + $gui->tree_title = lang_get('title_navigator') . ' - ' . + lang_get('title_req_spec'); + + $gui->req_spec_manager_url = "lib/requirements/reqSpecView.php"; + $gui->req_manager_url = "lib/requirements/reqView.php"; + $gui->basehref = $argsObj->basehref; + + return $gui; +} + +/* + * rights check + */ +function checkRights(&$db, &$user, $context) +{ + $context->rightsOr = [ + "mgt_view_req", + "mgt_modify_req" + ]; + $context->rightsAnd = []; + pageAccessCheck($db, $user, $context); } - -/* - function: initializeGui - initialize gui (stdClass) object that will be used as argument - in call to Template Engine. - - args: argsObj: object containing User Input and some session values - basehref: URL to web home of your testlink installation. - - returns: stdClass object - - rev: - -*/ -function initializeGui($argsObj) -{ - $gui = new stdClass(); - $gui->tree_title = lang_get('title_navigator'). ' - ' . lang_get('title_req_spec'); - - $gui->req_spec_manager_url = "lib/requirements/reqSpecView.php"; - $gui->req_manager_url = "lib/requirements/reqView.php"; - $gui->basehref = $argsObj->basehref; - - return $gui; -} - -/** - * - */ -function checkRights(&$db,&$user) -{ - return ( $user->hasRight($db,'mgt_view_req') || $user->hasRight($db,'mgt_modify_req') ) ; -} \ No newline at end of file diff --git a/lib/requirements/reqSpecPrint.php b/lib/requirements/reqSpecPrint.php index 4d5a8e90f1..ecd86b5299 100644 --- a/lib/requirements/reqSpecPrint.php +++ b/lib/requirements/reqSpecPrint.php @@ -1,101 +1,112 @@ -reqspec_revision_id; -$target_id = ($target_id <= 0) ? $args->reqspec_id : $target_id; - -// $node = $tree_mgr->get_node_hierarchy_info($args->reqspec_id); -$node = $tree_mgr->get_node_hierarchy_info($target_id); - -$gui = new stdClass(); -$gui->object_name=''; -$gui->object_name = $node['name']; -$gui->page_title = sprintf(lang_get('print_requirement_specification'),$node['name']); -$gui->tproject_name=$args->tproject_name; -$gui->tproject_id=$args->tproject_id; -$gui->reqspec_id=$args->reqspec_id; - - -// Struture defined in printDocument.php -$options = array('toc' => 0, 'req_spec_scope' => 1, 'req_spec_author' => 1,'req_spec_type' =>1, - 'req_spec_cf' => 1,'req_spec_overwritten_count_reqs' => 1, - 'headerNumbering' => 0, 'docType' => SINGLE_REQSPEC); - -$text2print = ''; -$text2print .= renderHTMLHeader($gui->page_title,$_SESSION['basehref'],SINGLE_REQSPEC) . '' ; -//$text2print .= '

    ' . lang_get('req_specification') . '

    '; - -$text2print .= renderReqSpecNodeForPrinting($db, $node, $options,null,0,$args->tproject_id); - -// now get all it's children (just requirements). -$childrenReq = $reqspec_mgr->get_requirements($args->reqspec_id); -if( !is_null($childrenReq) && $req_cfg->show_child_reqs_on_reqspec_print_view) -{ - // IMPORTANT NOTICE: - // 'docType' => 'SINGLE_REQ' among other things remove the indent on req table - // that is present by default. - // That's why we need to pass any other value. - $reqPrintOpts = array('toc' => 0, 'req_linked_tcs' => 1, 'req_cf' => 1, - 'req_scope' => 1, 'req_relations' => 1, 'req_coverage' => 1, - 'req_status' => 1, 'req_type' => 1,'req_author'=> 1, - 'displayVersion' => 1, 'displayDates' => 1, - 'displayLastEdit' => 1, 'docType' => SINGLE_REQ); - - $text2print .= '

    ' . lang_get('reqs') . '

    '; - $loop2do = count($childrenReq); - for($rdx=0; $rdx < $loop2do; $rdx++) - { - $text2print .= renderReqForPrinting($db,$childrenReq[$rdx],$reqPrintOpts, - null,0,$args->tproject_id); - } -} -$text2print .= renderEOF(); -echo $text2print; - -/* - function: init_args - - args: - - returns: - -*/ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->reqspec_id = isset($_REQUEST['reqspec_id']) ? intval($_REQUEST['reqspec_id']) : 0; - $args->reqspec_revision_id = isset($_REQUEST['reqspec_revision_id']) ? intval($_REQUEST['reqspec_revision_id']) : 0; - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tproject_name = $_SESSION['testprojectName']; - - return $args; -} -?> \ No newline at end of file +reqspec_revision_id; +$target_id = ($target_id <= 0) ? $args->reqspec_id : $target_id; + +$node = $tree_mgr->get_node_hierarchy_info($target_id); + +$gui = new stdClass(); +$gui->object_name = ''; +$gui->object_name = $node['name']; +$gui->page_title = sprintf(lang_get('print_requirement_specification'), + $node['name']); +$gui->tproject_name = $args->tproject_name; +$gui->tproject_id = $args->tproject_id; +$gui->reqspec_id = $args->reqspec_id; + +// Struture defined in printDocument.php +$options = array( + 'toc' => 0, + 'req_spec_scope' => 1, + 'req_spec_author' => 1, + 'req_spec_type' => 1, + 'req_spec_cf' => 1, + 'req_spec_overwritten_count_reqs' => 1, + 'headerNumbering' => 0, + 'docType' => SINGLE_REQSPEC +); + +$text2print = ''; +$text2print .= renderHTMLHeader($gui->page_title, $_SESSION['basehref'], + SINGLE_REQSPEC) . ''; +$text2print .= renderReqSpecNodeForPrinting($db, $node, $options, null, 0); + +// now get all it's children (just requirements). +$childrenReq = $reqspec_mgr->get_requirements($args->reqspec_id); +if (! is_null($childrenReq) && $req_cfg->show_child_reqs_on_reqspec_print_view) { + // IMPORTANT NOTICE: + // 'docType' => 'SINGLE_REQ' among other things remove the indent on req table + // that is present by default. + // That's why we need to pass any other value. + $reqPrintOpts = array( + 'toc' => 0, + 'req_linked_tcs' => 1, + 'req_cf' => 1, + 'req_scope' => 1, + 'req_relations' => 1, + 'req_coverage' => 1, + 'req_status' => 1, + 'req_type' => 1, + 'req_author' => 1, + 'displayVersion' => 1, + 'displayDates' => 1, + 'displayLastEdit' => 1, + 'docType' => SINGLE_REQ + ); + + $text2print .= '

    ' . lang_get('reqs') . '

    '; + $loop2do = count($childrenReq); + for ($rdx = 0; $rdx < $loop2do; $rdx ++) { + $text2print .= renderReqForPrinting($db, $childrenReq[$rdx], + $reqPrintOpts, 0, $args->tproject_id); + } +} +$text2print .= renderEOF(); +echo $text2print; + +/** + * + * @return stdClass + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->reqspec_id = isset($_REQUEST['reqspec_id']) ? intval( + $_REQUEST['reqspec_id']) : 0; + $args->reqspec_revision_id = isset($_REQUEST['reqspec_revision_id']) ? intval( + $_REQUEST['reqspec_revision_id']) : 0; + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tproject_name = $_SESSION['testprojectName']; + + return $args; +} +?> diff --git a/lib/requirements/reqSpecSearch.php b/lib/requirements/reqSpecSearch.php index 3ab8b830f4..64ac7e11d7 100644 --- a/lib/requirements/reqSpecSearch.php +++ b/lib/requirements/reqSpecSearch.php @@ -1,243 +1,254 @@ -tprojectID); -$gui = $commandMgr->initGuiBean(); - -$edit_label = lang_get('requirement_spec'); -$edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; - -$gui->main_descr = lang_get('caption_search_form_req_spec'); -$gui->warning_msg = ''; -$gui->path_info = null; -$gui->resultSet = null; -$gui->tableSet = null; - -$itemSet = null; -if ($args->tprojectID) -{ - $tables = tlObjectWithDB::getDBTables(array('cfield_design_values', 'nodes_hierarchy', - 'req_specs','req_specs_revisions')); - $filter = null; - $join = null; - - - - // we use same approach used on requirements search => search on revisions - if ($args->requirement_document_id) { - $id=$db->prepare_string($args->requirement_document_id); - $filter['by_id'] = " AND RSPECREV.doc_id like '%{$id}%' "; - } - - if ($args->name) { - $title=$db->prepare_string($args->name); - $filter['by_name'] = " AND NHRSPEC.name like '%{$title}%' "; - } - - if ($args->reqSpecType != "notype") { - $type=$db->prepare_string($args->reqSpecType); - $filter['by_type'] = " AND RSPECREV.type='{$type}' "; - } - - if ($args->scope) { - $scope=$db->prepare_string($args->scope); - $filter['by_scope'] = " AND RSPECREV.scope like '%{$scope}%' "; - } - - if ($args->log_message) { - $log_message = $db->prepare_string($args->log_message); - $filter['by_log_message'] = " AND RSPECREV.log_message like '%{$log_message}%' "; - } - - - if($args->custom_field_id > 0) { - $args->custom_field_id = $db->prepare_int($args->custom_field_id); - $args->custom_field_value = $db->prepare_string($args->custom_field_value); - $join['by_custom_field'] = " JOIN {$tables['cfield_design_values']} CFD " . - " ON CFD.node_id=RSPECREV.id "; - $filter['by_custom_field'] = " AND CFD.field_id={$args->custom_field_id} " . - " AND CFD.value like '%{$args->custom_field_value}%' "; - } - - $sql = " SELECT NHRSPEC.name, NHRSPEC.id, RSPEC.doc_id, RSPECREV.id AS revision_id, RSPECREV.revision " . - " FROM {$tables['req_specs']} RSPEC JOIN {$tables['req_specs_revisions']} RSPECREV " . - " ON RSPEC.id=RSPECREV.parent_id " . - " JOIN {$tables['nodes_hierarchy']} NHRSPEC " . - " ON NHRSPEC.id = RSPEC.id "; - - if(!is_null($join)) - { - $sql .= implode("",$join); - } - - $sql .= " AND RSPEC.testproject_id = {$args->tprojectID} "; - - if(!is_null($filter)) - { - $sql .= implode("",$filter); - } - - $sql .= ' ORDER BY id ASC, revision DESC '; - $itemSet = $db->fetchRowsIntoMap($sql,'id',database::CUMULATIVE); - -} - -$smarty = new TLSmarty(); -$gui->row_qty=count($itemSet); -if($gui->row_qty > 0) -{ - $gui->resultSet = $itemSet; - if($gui->row_qty <= $req_cfg->search->max_qty_for_display) - { - $req_set=array_keys($itemSet); - $options = array('output_format' => 'path_as_string'); - $gui->path_info=$tproject_mgr->tree_manager->get_full_path_verbose($req_set, $options); - } - else - { - $gui->warning_msg=lang_get('too_wide_search_criteria'); - } -} -else -{ - $gui->warning_msg=lang_get('no_records_found'); -} - -$table = buildExtTable($gui, $charset); -if (!is_null($table)) { - $gui->tableSet[] = $table; -} - -$gui->pageTitle = $gui->main_descr . " - " . lang_get('match_count') . ": " . $gui->row_qty; -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $tpl); - - -function buildExtTable($gui, $charset) -{ - $lbl = array('edit' => 'requirement_spec', 'rev' => 'revision_short','req_spec' => 'req_spec', - 'revision_tag' => 'revision_tag', 'open_on_new_window' => 'open_on_new_window'); - $labels = init_labels($lbl); - $edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; - $table = null; - - // $gui->resultSet - - // key: reqspec_id - // value: array of matches - // array - // { - // [232][0]=>{"name" => "QA","id" => "232","doc_id" => "QA", - // "revision_id" => "251", "revision" => "4"} - // [1]=>{"name" => "QA","id" => "232","doc_id" => "QA", - // "revision_id" => "251", "revision" => "3"} - // ... - // } - // - // - if(count($gui->resultSet) > 0) - { - $matrixData = array(); - $columns = array(); - $columns[] = array('title_key' => 'req_spec', 'type' => 'text', 'groupable' => 'false', - 'hideable' => 'false'); - - $key2loop = array_keys($gui->resultSet); - foreach($key2loop as $rspec_id) - { - $rowData = array(); - - $itemSet = $gui->resultSet[$rspec_id]; - $rfx = &$itemSet[0]; - $path = ($gui->path_info[$rfx['id']]) ? $gui->path_info[$rfx['id']] . " / " : ""; - $edit_link = "" . - " "; - - $title = htmlentities($rfx['doc_id'], ENT_QUOTES, $charset) . ":" . - htmlentities($rfx['name'], ENT_QUOTES, $charset); - $cm = '' . - $labels['revision_tag'] . ' '; - // $link = $edit_link; - $matches = ''; - foreach($itemSet as $rx) - { - $matches .= sprintf($cm,$rx['revision_id'],$rx['revision']); - } - $rowData[] = $edit_link . $title . ' ' . $matches; - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_req_spec_search'); - $table->setSortByColumnName($labels['req_spec']); - $table->sortDirection = 'ASC'; - - $table->showToolbar = false; - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - $table->storeTableState = false; - } - return($table); -} - -/* - function: - - args: - - returns: - - */ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $strnull = array('requirement_document_id', 'name', 'scope', 'coverage', - 'custom_field_value', 'reqSpecType', 'log_message'); - - foreach($strnull as $keyvar) - { - $args->$keyvar = isset($_REQUEST[$keyvar]) ? trim($_REQUEST[$keyvar]) : null; - $args->$keyvar = !is_null($args->$keyvar) && strlen($args->$keyvar) > 0 ? trim($args->$keyvar) : null; - } - - $int0 = array('custom_field_id'); - foreach($int0 as $keyvar) - { - $args->$keyvar = isset($_REQUEST[$keyvar]) ? intval($_REQUEST[$keyvar]) : 0; - } - - $args->userID = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; - $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - return $args; -} -?> \ No newline at end of file +tprojectID); +$gui = $commandMgr->initGuiBean(); + +$edit_label = lang_get('requirement_spec'); +$edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; + +$gui->main_descr = lang_get('caption_search_form_req_spec'); +$gui->warning_msg = ''; +$gui->path_info = null; +$gui->resultSet = null; +$gui->tableSet = null; + +$itemSet = null; +if ($args->tprojectID) { + $tables = tlObjectWithDB::getDBTables( + array( + 'cfield_design_values', + 'nodes_hierarchy', + 'req_specs', + 'req_specs_revisions' + )); + $filter = null; + $join = null; + + // we use same approach used on requirements search => search on revisions + if ($args->requirement_document_id) { + $id = $db->prepare_string($args->requirement_document_id); + $filter['by_id'] = " AND RSPECREV.doc_id like '%{$id}%' "; + } + + if ($args->name) { + $title = $db->prepare_string($args->name); + $filter['by_name'] = " AND NHRSPEC.name like '%{$title}%' "; + } + + if ($args->reqSpecType != "notype") { + $type = $db->prepare_string($args->reqSpecType); + $filter['by_type'] = " AND RSPECREV.type='{$type}' "; + } + + if ($args->scope) { + $scope = $db->prepare_string($args->scope); + $filter['by_scope'] = " AND RSPECREV.scope like '%{$scope}%' "; + } + + if ($args->log_message) { + $log_message = $db->prepare_string($args->log_message); + $filter['by_log_message'] = " AND RSPECREV.log_message like '%{$log_message}%' "; + } + + if ($args->custom_field_id > 0) { + $args->custom_field_id = $db->prepare_int($args->custom_field_id); + $args->custom_field_value = $db->prepare_string( + $args->custom_field_value); + $join['by_custom_field'] = " JOIN {$tables['cfield_design_values']} CFD " . + " ON CFD.node_id=RSPECREV.id "; + $filter['by_custom_field'] = " AND CFD.field_id={$args->custom_field_id} " . + " AND CFD.value like '%{$args->custom_field_value}%' "; + } + + $sql = " SELECT NHRSPEC.name, NHRSPEC.id, RSPEC.doc_id, RSPECREV.id AS revision_id, RSPECREV.revision " . + " FROM {$tables['req_specs']} RSPEC JOIN {$tables['req_specs_revisions']} RSPECREV " . + " ON RSPEC.id=RSPECREV.parent_id " . + " JOIN {$tables['nodes_hierarchy']} NHRSPEC " . + " ON NHRSPEC.id = RSPEC.id "; + + if (! is_null($join)) { + $sql .= implode("", $join); + } + + $sql .= " AND RSPEC.testproject_id = {$args->tprojectID} "; + + if (! is_null($filter)) { + $sql .= implode("", $filter); + } + + $sql .= ' ORDER BY id ASC, revision DESC '; + $itemSet = $db->fetchRowsIntoMap($sql, 'id', database::CUMULATIVE); +} + +$smarty = new TLSmarty(); +if (! empty($itemSet)) { + $gui->row_qty = count($itemSet); + $gui->resultSet = $itemSet; + if ($gui->row_qty <= $req_cfg->search->max_qty_for_display) { + $req_set = array_keys($itemSet); + $options = array( + 'output_format' => 'path_as_string' + ); + $gui->path_info = $tproject_mgr->tree_manager->get_full_path_verbose( + $req_set, $options); + } else { + $gui->warning_msg = lang_get('too_wide_search_criteria'); + } +} else { + $gui->warning_msg = lang_get('no_records_found'); +} + +$table = buildExtTable($gui, $charset); +if (! is_null($table)) { + $gui->tableSet[] = $table; +} + +$gui->pageTitle = $gui->main_descr . " - " . lang_get('match_count') . ": " . + $gui->row_qty; +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $tpl); + +function buildExtTable($gui, $charset) +{ + $lbl = array( + 'edit' => 'requirement_spec', + 'rev' => 'revision_short', + 'req_spec' => 'req_spec', + 'revision_tag' => 'revision_tag', + 'open_on_new_window' => 'open_on_new_window' + ); + $labels = init_labels($lbl); + $edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; + $table = null; + + // $gui->resultSet - + // key: reqspec_id + // value: array of matches + // array + // { + // [232][0]=>{"name" => "QA","id" => "232","doc_id" => "QA", + // "revision_id" => "251", "revision" => "4"} + // [1]=>{"name" => "QA","id" => "232","doc_id" => "QA", + // "revision_id" => "251", "revision" => "3"} + // ... + // } + if (! empty($gui->resultSet)) { + $matrixData = array(); + $columns = array(); + $columns[] = array( + 'title_key' => 'req_spec', + 'type' => 'text', + 'groupable' => 'false', + 'hideable' => 'false' + ); + + $key2loop = array_keys($gui->resultSet); + foreach ($key2loop as $rspec_id) { + $rowData = array(); + + $itemSet = $gui->resultSet[$rspec_id]; + $rfx = &$itemSet[0]; + $edit_link = "" . + " "; + + $title = htmlentities($rfx['doc_id'], ENT_QUOTES, $charset) . ":" . + htmlentities($rfx['name'], ENT_QUOTES, $charset); + $cm = '' . $labels['revision_tag'] . + ' '; + $matches = ''; + foreach ($itemSet as $rx) { + $matches .= sprintf($cm, $rx['revision_id'], $rx['revision']); + } + $rowData[] = $edit_link . $title . ' ' . $matches; + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, + 'tl_table_req_spec_search'); + $table->setSortByColumnName($labels['req_spec']); + $table->sortDirection = 'ASC'; + + $table->showToolbar = false; + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + $table->storeTableState = false; + } + return $table; +} + +/* + * function: + * + * args: + * + * returns: + * + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $strnull = array( + 'requirement_document_id', + 'name', + 'scope', + 'coverage', + 'custom_field_value', + 'reqSpecType', + 'log_message' + ); + + foreach ($strnull as $keyvar) { + $args->$keyvar = isset($_REQUEST[$keyvar]) ? trim($_REQUEST[$keyvar]) : null; + $args->$keyvar = ! is_null($args->$keyvar) && strlen($args->$keyvar) > 0 ? trim( + $args->$keyvar) : null; + } + + $int0 = array( + 'custom_field_id' + ); + foreach ($int0 as $keyvar) { + $args->$keyvar = isset($_REQUEST[$keyvar]) ? intval($_REQUEST[$keyvar]) : 0; + } + + $args->userID = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; + $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + return $args; +} +?> diff --git a/lib/requirements/reqSpecSearchForm.php b/lib/requirements/reqSpecSearchForm.php index 5660179f31..bd0ebb724f 100644 --- a/lib/requirements/reqSpecSearchForm.php +++ b/lib/requirements/reqSpecSearchForm.php @@ -1,66 +1,65 @@ -tcasePrefix = ''; - -$gui->mainCaption = lang_get('testproject') . " " . $args->tprojectName; - -$enabled = 1; -$no_filters = null; - -$gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design($args->tprojectID,$enabled, - $no_filters,'requirement_spec'); - -$reqSpecSet = $tproject_mgr->getOptionReqSpec($args->tprojectID,testproject::GET_NOT_EMPTY_REQSPEC); - -$gui->filter_by['design_scope_custom_fields'] = !is_null($gui->design_cf); -$gui->filter_by['requirement_doc_id'] = !is_null($reqSpecSet); - -$reqSpecCfg = config_get('req_spec_cfg'); -$gui->types = init_labels($reqSpecCfg->type_labels); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . 'reqSpecSearchForm.tpl'); - -/* - function: - - args: - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; - - return $args; -} -?> \ No newline at end of file +tcasePrefix = ''; + +$gui->mainCaption = lang_get('testproject') . " " . $args->tprojectName; + +$enabled = 1; +$no_filters = null; + +$gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design( + $args->tprojectID, $enabled, $no_filters, 'requirement_spec'); + +$reqSpecSet = $tproject_mgr->getOptionReqSpec($args->tprojectID, + testproject::GET_NOT_EMPTY_REQSPEC); + +$gui->filter_by['design_scope_custom_fields'] = ! is_null($gui->design_cf); +$gui->filter_by['requirement_doc_id'] = ! is_null($reqSpecSet); + +$reqSpecCfg = config_get('req_spec_cfg'); +$gui->types = init_labels($reqSpecCfg->type_labels); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . 'reqSpecSearchForm.tpl'); + +/* + * function: + * + * args: + * + * returns: + * + */ +function initArgs() +{ + $args = new stdClass(); + $args->tprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; + + return $args; +} +?> diff --git a/lib/requirements/reqSpecView.php b/lib/requirements/reqSpecView.php index 2ebb0f4d29..34c0d09571 100644 --- a/lib/requirements/reqSpecView.php +++ b/lib/requirements/reqSpecView.php @@ -1,127 +1,147 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - */ -function init_args() -{ - $iParams = array("req_spec_id" => array(tlInputParameter::INT_N), - "refreshTree" => array(tlInputParameter::INT_N), - "uploadOPStatusCode" => array(tlInputParameter::STRING_N,0,30) ); - $args = new stdClass(); - R_PARAMS($iParams,$args); - $args->refreshTree = intval($args->refreshTree); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - - return $args; +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($tplCfg->template_dir . $tplCfg->default_template); + +/** + */ +function initArgs() +{ + $iParams = array( + "req_spec_id" => array( + tlInputParameter::INT_N + ), + "refreshTree" => array( + tlInputParameter::INT_N + ), + "uploadOPStatusCode" => array( + tlInputParameter::STRING_N, + 0, + 30 + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + $args->refreshTree = intval($args->refreshTree); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $req_spec_mgr = new requirement_spec_mgr($dbHandler); + $tproject_mgr = new testproject($dbHandler); + $commandMgr = new reqSpecCommands($dbHandler, $argsObj->tproject_id); + + $gui = $commandMgr->initGuiBean(); + $gui->refreshTree = $argsObj->refreshTree; + $gui->req_spec_cfg = config_get('req_spec_cfg'); + $gui->req_cfg = config_get('req_cfg'); + $gui->external_req_management = ($gui->req_cfg->external_req_management == + ENABLED) ? 1 : 0; + + $gui->grants = new stdClass(); + $gui->grants->req_mgmt = has_rights($db, "mgt_modify_req", + $argsObj->tproject_id); + + $gui->req_spec = $req_spec_mgr->get_by_id($argsObj->req_spec_id); + $gui->revCount = $req_spec_mgr->getRevisionsCount($argsObj->req_spec_id); + + $gui->req_spec_id = intval($argsObj->req_spec_id); + $gui->parentID = $argsObj->req_spec_id; + + $gui->req_spec_revision_id = $gui->req_spec['revision_id']; + $gui->name = $gui->req_spec['title']; + + $gui->tproject_name = $argsObj->tproject_name; + $gui->main_descr = lang_get('req_spec_short') . + config_get('gui_title_separator_1') . "[{$gui->req_spec['doc_id']}] :: " . + $gui->req_spec['title']; + + $gui->refresh_tree = 'no'; + + $gui->cfields = $req_spec_mgr->html_table_of_custom_field_values( + $gui->req_spec_id, $gui->req_spec_revision_id, $argsObj->tproject_id); + + $gui->attachments = getAttachmentInfosFrom($req_spec_mgr, + $argsObj->req_spec_id); + $gui->requirements_count = $req_spec_mgr->get_requirements_count( + $argsObj->req_spec_id); + + $gui->reqSpecTypeDomain = init_labels($gui->req_spec_cfg->type_labels); + + $prefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); + $gui->direct_link = $_SESSION['basehref'] . 'linkto.php?tprojectPrefix=' . + urlencode($prefix) . '&item=reqspec&id=' . + urlencode($gui->req_spec['doc_id']); + + $gui->fileUploadURL = $_SESSION['basehref'] . + $req_spec_mgr->getFileUploadRelativeURL($gui->req_spec_id); + $gui->delAttachmentURL = $_SESSION['basehref'] . + $req_spec_mgr->getDeleteAttachmentRelativeURL($gui->req_spec_id); + $gui->fileUploadMsg = ''; + $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; + + $cfg = new stdClass(); + $cfg->reqSpecCfg = getWebEditorCfg('requirement_spec'); + $gui->reqSpecEditorType = $cfg->reqSpecCfg['type']; + + $gui->btn_import_req_spec = ''; + $gui->reqMgrSystemEnabled = 0; + if (! is_null($reqMgrSystem = $commandMgr->getReqMgrSystem())) { + $gui->btn_import_req_spec = sprintf(lang_get('importViaAPI'), + $reqMgrSystem['reqmgrsystem_name']); + $gui->reqMgrSystemEnabled = 1; + } + + $gui->uploadOp = null; + if (trim($argsObj->uploadOPStatusCode) != '') { + $gui->uploadOp = new stdClass(); + $gui->uploadOp->statusOK = false; + $gui->uploadOp->statusCode = $argsObj->uploadOPStatusCode; + $gui->uploadOp->msg = lang_get($argsObj->uploadOPStatusCode); + } + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req" + ]; + pageAccessCheck($db, $user, $context); } - -/** - * - * - */ -function initialize_gui(&$dbHandler,&$argsObj) -{ - $req_spec_mgr = new requirement_spec_mgr($dbHandler); - $tproject_mgr = new testproject($dbHandler); - $commandMgr = new reqSpecCommands($dbHandler,$argsObj->tproject_id); - - - $gui = $commandMgr->initGuiBean(); - $gui->refreshTree = $argsObj->refreshTree; - $gui->req_spec_cfg = config_get('req_spec_cfg'); - $gui->req_cfg = config_get('req_cfg'); - $gui->external_req_management = ($gui->req_cfg->external_req_management == ENABLED) ? 1 : 0; - - $gui->grants = new stdClass(); - $gui->grants->req_mgmt = has_rights($db,"mgt_modify_req"); - - $gui->req_spec = $req_spec_mgr->get_by_id($argsObj->req_spec_id); - $gui->revCount = $req_spec_mgr->getRevisionsCount($argsObj->req_spec_id); - - $gui->req_spec_id = intval($argsObj->req_spec_id); - $gui->parentID = $argsObj->req_spec_id; - - $gui->req_spec_revision_id = $gui->req_spec['revision_id']; - $gui->name = $gui->req_spec['title']; - - - $gui->tproject_name = $argsObj->tproject_name; - $gui->main_descr = lang_get('req_spec_short') . config_get('gui_title_separator_1') . - "[{$gui->req_spec['doc_id']}] :: " .$gui->req_spec['title']; - - $gui->refresh_tree = 'no'; - - $gui->cfields = $req_spec_mgr->html_table_of_custom_field_values($gui->req_spec_id, - $gui->req_spec_revision_id, - $argsObj->tproject_id); - - $gui->attachments = getAttachmentInfosFrom($req_spec_mgr,$argsObj->req_spec_id); - $gui->requirements_count = $req_spec_mgr->get_requirements_count($argsObj->req_spec_id); - - $gui->reqSpecTypeDomain = init_labels($gui->req_spec_cfg->type_labels); - - $prefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - $gui->direct_link = $_SESSION['basehref'] . 'linkto.php?tprojectPrefix=' . urlencode($prefix) . - '&item=reqspec&id=' . urlencode($gui->req_spec['doc_id']); - - - $gui->fileUploadURL = $_SESSION['basehref'] . $req_spec_mgr->getFileUploadRelativeURL($gui->req_spec_id); - $gui->delAttachmentURL = $_SESSION['basehref'] . $req_spec_mgr->getDeleteAttachmentRelativeURL($gui->req_spec_id); - $gui->fileUploadMsg = ''; - $gui->import_limit = TL_REPOSITORY_MAXFILESIZE; - - $cfg = new stdClass(); - $cfg->reqSpecCfg = getWebEditorCfg('requirement_spec'); - $gui->reqSpecEditorType = $cfg->reqSpecCfg['type']; - - $gui->btn_import_req_spec = ''; - $gui->reqMgrSystemEnabled = 0; - if( !is_null($reqMgrSystem = $commandMgr->getReqMgrSystem()) ) - { - $gui->btn_import_req_spec = sprintf(lang_get('importViaAPI'),$reqMgrSystem['reqmgrsystem_name']); - $gui->reqMgrSystemEnabled = 1; - } - - $gui->uploadOp = null; - if (trim($argsObj->uploadOPStatusCode) != '') { - $gui->uploadOp = new stdClass(); - $gui->uploadOp->statusOK = false; - $gui->uploadOp->statusCode = $argsObj->uploadOPStatusCode; - $gui->uploadOp->msg = lang_get($argsObj->uploadOPStatusCode); - } - - return $gui; -} - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'mgt_view_req'); -} \ No newline at end of file diff --git a/lib/requirements/reqSpecViewRevision.php b/lib/requirements/reqSpecViewRevision.php index 7b68217c9d..d1d92c8847 100644 --- a/lib/requirements/reqSpecViewRevision.php +++ b/lib/requirements/reqSpecViewRevision.php @@ -1,98 +1,107 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . 'reqSpecViewRevision.tpl'); - -/** - * - */ -function init_args() -{ - $iParams = array("item_id" => array(tlInputParameter::INT_N), - "showContextInfo" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - $user = $_SESSION['currentUser']; - $args->userID = $user->dbID; - - return $args; +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$smarty = new TLSmarty(); + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . 'reqSpecViewRevision.tpl'); + +/** + */ +function initArgs() +{ + $iParams = array( + "item_id" => array( + tlInputParameter::INT_N + ), + "showContextInfo" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + $user = $_SESSION['currentUser']; + $args->userID = $user->dbID; + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, $argsObj) +{ + $tproject_mgr = new testproject($dbHandler); + $itemMgr = new requirement_spec_mgr($dbHandler); + $commandMgr = new reqSpecCommands($dbHandler, $argsObj->tproject_id); + + $gui = $commandMgr->initGuiBean(); + $gui->itemCfg = config_get('req_spec_cfg'); + $gui->tproject_name = $argsObj->tproject_name; + + $gui->grants = new stdClass(); + $gui->grants->req_mgmt = has_rights($dbHandler, "mgt_modify_req"); + + $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); + $gui->glueChar = config_get('testcase_cfg')->glue_character; + $gui->pieceSep = config_get('gui_title_separator_1'); + + $gui->item_id = $argsObj->item_id; + $info = $itemMgr->getRevisionByID($gui->item_id, + array( + 'decode_user' => true + )); + $gui->item = $info; + + $gui->cfields = $itemMgr->html_table_of_custom_field_values(null, + $gui->item_id, $argsObj->tproject_id); + $gui->show_title = false; + $gui->main_descr = lang_get('req_spec') . $gui->pieceSep . $gui->item['name']; + + $gui->showContextInfo = $argsObj->showContextInfo; + if ($gui->showContextInfo) { + $gui->parent_descr = lang_get('req_spec_short') . $gui->pieceSep . + $gui->item['name']; + } + + $gui->itemSpecStatus = null; + $gui->itemTypeDomain = init_labels($gui->itemCfg->type_labels); + + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req" + ]; + pageAccessCheck($db, $user, $context); } - -/** - * - * - */ -function initialize_gui(&$dbHandler,$argsObj) -{ - $tproject_mgr = new testproject($dbHandler); - $itemMgr = new requirement_spec_mgr($dbHandler); - $commandMgr = new reqSpecCommands($dbHandler,$argsObj->tproject_id); - - $gui = $commandMgr->initGuiBean(); - $gui->itemCfg = config_get('req_spec_cfg'); - $gui->tproject_name = $argsObj->tproject_name; - - $gui->grants = new stdClass(); - $gui->grants->req_mgmt = has_rights($dbHandler,"mgt_modify_req"); - - $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - $gui->glueChar = config_get('testcase_cfg')->glue_character; - $gui->pieceSep = config_get('gui_title_separator_1'); - - $gui->item_id = $argsObj->item_id; - $info = $itemMgr->getRevisionByID($gui->item_id,array('decode_user' => true)); - $gui->item = $info; - - $gui->cfields = $itemMgr->html_table_of_custom_field_values(null,$gui->item_id,$argsObj->tproject_id); - $gui->show_title = false; - $gui->main_descr = lang_get('req_spec') . $gui->pieceSep . $gui->item['name']; - - $gui->showContextInfo = $argsObj->showContextInfo; - if($gui->showContextInfo) - { - $gui->parent_descr = lang_get('req_spec_short') . $gui->pieceSep . $gui->item['name']; - } - - $gui->itemSpecStatus = null; - $gui->itemTypeDomain = init_labels($gui->itemCfg->type_labels); - - return $gui; -} - - -/** - * - * - */ -function checkRights(&$dbHandler,&$user) -{ - return $user->hasRight($dbHandler,'mgt_view_req'); -} -?> \ No newline at end of file diff --git a/lib/requirements/reqTcAssign.php b/lib/requirements/reqTcAssign.php index a5371d9b7c..65d43c566b 100644 --- a/lib/requirements/reqTcAssign.php +++ b/lib/requirements/reqTcAssign.php @@ -1,396 +1,459 @@ -doAction) { - case 'assign': - $pfn = "assign_to_tcase"; - break; - - case 'unassign': - $pfn = "delReqVersionTCVersionLinkByID"; - break; - - case 'bulkassign': - // need to check if we have test cases to work on - $tcase_set = getTargetTestCases($db,$args); - $bulkCounter = 0; - $bulkDone = true; - $args->edit = 'testsuite'; - if( !is_null($tcase_set) && count($tcase_set) > 0 ) { - $bulkCounter = doBulkAssignment($db,$args,$tcase_set); - } - break; - - case 'switchspec': - $args->edit = 'testsuite'; - break; -} - -if(!is_null($pfn)) { - $gui = doSingleTestCaseOperation($db,$args,$gui,$pfn); -} - -switch($args->edit) { - case 'testproject': - show_instructions('assignReqs'); - exit(); - break; - - case 'testsuite': - $gui = processTestSuite($db,$args,$gui); - $templateCfg->default_template = 'reqTcBulkAssignment.tpl'; - if($bulkDone) { - $gui->user_feedback = sprintf(lang_get('bulk_assigment_done'),$bulkCounter); - } - break; - - case 'testcase': - $gui = processTestCase($db,$args,$gui); - break; - - default: - tlog("Wrong GET/POST arguments.", 'ERROR'); - exit(); - break; +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$bulkCounter = 0; +$bulkDone = false; +$bulkMsg = null; +$pfn = null; + +switch ($args->doAction) { + case 'assign': + $pfn = "assign_to_tcase"; + break; + + case 'unassign': + $pfn = "delReqVersionTCVersionLinkByID"; + break; + + case 'bulkassign': + // need to check if we have test cases to work on + $tcase_set = getTargetTestCases($db, $args); + $bulkCounter = 0; + $bulkDone = true; + $args->edit = 'testsuite'; + if (! is_null($tcase_set) && ! empty($tcase_set)) { + $bulkCounter = doBulkAssignment($db, $args, $tcase_set); + } + break; + + case 'switchspec': + $args->edit = 'testsuite'; + break; +} + +if (! is_null($pfn)) { + $gui = doSingleTestCaseOperation($db, $args, $gui, $pfn); +} + +switch ($args->edit) { + case 'testproject': + show_instructions('assignReqs'); + exit(); + break; + + case 'testsuite': + $gui = processTestSuite($db, $args, $gui); + $templateCfg->default_template = 'reqTcBulkAssignment.tpl'; + if ($bulkDone) { + $gui->user_feedback = sprintf(lang_get('bulk_assigment_done'), + $bulkCounter); + } + break; + + case 'testcase': + $gui = processTestCase($db, $args, $gui); + break; + + default: + tlog("Wrong GET/POST arguments.", 'ERROR'); + exit(); + break; +} + +$tpl = $templateCfg->template_dir . $templateCfg->default_template; +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($tpl); + +/** + */ +function initArgs() +{ + $iParams = array( + "id" => array( + tlInputParameter::INT_N + ), + "req_id" => array( + tlInputParameter::ARRAY_INT + ), + "link_id" => array( + tlInputParameter::ARRAY_INT + ), + + "req" => array( + tlInputParameter::INT_N + ), + "showCloseButton" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "edit" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "unassign" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "assign" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "form_token" => array( + tlInputParameter::INT_N + ), + "callback" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "idSRS" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + // take care of proper escaping when magic_quotes_gpc is enabled + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->idReqSpec = null; + $args->idReq = $args->req; + $args->reqIdSet = $args->req_id; + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + + $args->tcaseSet = null; + if (isset($_SESSION['edit_mode'][$args->form_token]['testcases_to_show'])) { + $args->tcaseSet = $_SESSION['edit_mode'][$args->form_token]['testcases_to_show']; + } + + if (is_null($args->doAction)) { + $args->doAction = ($args->unassign != "") ? "unassign" : null; + } + + if (is_null($args->doAction)) { + $args->doAction = ($args->assign != "") ? "assign" : null; + } + + if ($args->idSRS) { + $args->idReqSpec = $args->idSRS; + $_SESSION['currentSrsId'] = $args->idReqSpec; + } elseif (isset($_SESSION['currentSrsId']) && + intval($_SESSION['currentSrsId']) > 0) { + $args->idReqSpec = intval($_SESSION['currentSrsId']); + } + + $args->user = $_SESSION['currentUser']; + return $args; +} + +/** + */ +function processTestSuite(&$dbHandler, &$argsObj, &$guiObj) +{ + $tproject_mgr = new testproject($dbHandler); + + $guiObj->bulkassign_warning_msg = ''; + $guiObj->tsuite_id = $argsObj->id; + + $tsuite_info = $tproject_mgr->tree_manager->get_node_hierarchy_info( + $guiObj->tsuite_id); + $guiObj->pageTitle = lang_get('test_suite') . + config_get('gui_title_separator_1') . $tsuite_info['name']; + + $guiObj->req_specs = $tproject_mgr->genComboReqSpec($argsObj->tproject_id, + 'dotted', " "); + + $guiObj->selectedReqSpec = $argsObj->idReqSpec; + $guiObj->selectedReqSpecName = ''; + + $guiObj->tcase_number = 0; + $guiObj->has_req_spec = false; + + if (! is_null($guiObj->req_specs) && count($guiObj->req_specs)) { + $guiObj->has_req_spec = true; + + if (is_null($argsObj->idReqSpec)) { + $guiObj->selectedReqSpec = key($guiObj->req_specs); + } + $guiObj->selectedReqSpecName = trim( + $guiObj->req_specs[$guiObj->selectedReqSpec], ' '); + + $req_spec_mgr = new requirement_spec_mgr($dbHandler); + + $getOpt = array( + 'output' => 'array' + ); + $guiObj->requirements = $req_spec_mgr->getAllLatestRQVOnReqSpec( + $guiObj->selectedReqSpec, $getOpt); + + $guiObj->reqCountOnReqSpec = count((array) $guiObj->requirements); + + $guiObj->reqCountFeedback = sprintf(lang_get('req_on_req_spec'), + $guiObj->reqCountOnReqSpec, $guiObj->selectedReqSpecName); + + $tcase_set = getTargetTestCases($dbHandler, $argsObj); + $guiObj->tcase_number = count($tcase_set); + if ($guiObj->tcase_number > 0) { + $guiObj->bulkassign_warning_msg = sprintf( + lang_get('bulk_req_assign_msg'), $guiObj->tcase_number, + $tsuite_info['name']); + } else { + $guiObj->bulkassign_warning_msg = lang_get( + 'bulk_req_assign_no_test_cases'); + } + } + return $guiObj; +} + +/** + */ +function doBulkAssignment(&$dbHandler, &$argsObj, $targetTestCaseSet = null) +{ + $req_mgr = new requirement_mgr($dbHandler); + $assignmentCounter = 0; + $requirements = array_keys($argsObj->reqIdSet); + if (! is_null($requirements) && count($requirements) > 0) { + $tcase_set = $targetTestCaseSet; + if (is_null($tcase_set)) { + $tsuite_mgr = new testsuite($dbHandler); + $tcase_set = $tsuite_mgr->get_testcases_deep($argsObj->id, 'only_id'); + } + + if (! is_null($tcase_set) && count($tcase_set)) { + $assignmentCounter = $req_mgr->bulkAssignLatestREQVTCV( + $requirements, $tcase_set, $argsObj->user->dbID); + } + } + return $assignmentCounter; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param stdClass $guiObj + * @param string $pfn + * @return stdClass + */ +function doSingleTestCaseOperation(&$dbHandler, &$argsObj, &$guiObj, $pfn) +{ + $msg = ''; + + switch ($pfn) { + case 'assign_to_tcase': + $items = array_keys($argsObj->reqIdSet); + break; + + case 'delReqVersionTCVersionLinkByID': + $items = array_keys($argsObj->link_id); + break; + } + + if (count($items) == 0) { + $guiObj->user_feedback = lang_get('req_msg_noselect'); + return $guiObj; + } + + $req_mgr = new requirement_mgr($dbHandler); + + switch ($pfn) { + case 'assign_to_tcase': + foreach ($items as $idOneReq) { + $res = $req_mgr->$pfn($idOneReq, $argsObj->id, + $argsObj->user->dbID); + if (! $res) { + $msg .= $idOneReq . ', '; + } + } + if (! empty($msg)) { + $guiObj->user_feedback = lang_get('req_msg_notupdated_coverage') . + $msg; + } + break; + + case 'delReqVersionTCVersionLinkByID': + foreach ($items as $idLink) { + $res = $req_mgr->$pfn($idLink); + if (! $res) { + $msg .= $idLink . ', '; + } + } + if (! empty($msg)) { + $guiObj->user_feedback = lang_get('req_msg_notupdated_coverage') . + $msg; + } + break; + } + + return $guiObj; +} + +/** + */ +function arrayDiffById($arrAll, $arrPart) +{ + if (is_null($arrAll) || ! count($arrAll)) { + return null; + } + + if (is_null($arrPart) || ! count($arrPart)) { + return $arrAll; + } + + foreach ($arrAll as $penny) { + $highLander[$penny['id']] = $penny; + } + + foreach ($arrPart as $penny) { + if (isset($highLander[$penny['id']])) { + unset($highLander[$penny['id']]); + } + } + + return array_values($highLander); +} + +/** + * processTestCase + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param stdClass $guiObj + * @return stdClass + */ +function processTestCase(&$dbHandler, &$argsObj, &$guiObj) +{ + $tproject_mgr = new testproject($dbHandler); + $guiObj->arrReqSpec = $tproject_mgr->genComboReqSpec($argsObj->tproject_id, + 'dotted', " "); + $srs_qty = count($guiObj->arrReqSpec); + + if ($srs_qty > 0) { + $tc_mgr = new testcase($dbHandler); + $tcase = $tc_mgr->get_by_id($argsObj->id, testcase::LATEST_VERSION); + $tcase = current($tcase); + if ($tcase) { + $guiObj->tcTitle = $tcase['name']; + $guiObj->tcVersion = $tcase['version']; + + // get test case version execution status + $tcversion_id = $tcase['id']; + $statusQuo = $tc_mgr->getVersionsStatusQuo($argsObj->id, + $tcversion_id); + + $statusQuo = current($statusQuo); + $guiObj->tcaseHasBeenExecuted = (intval($statusQuo['executed']) > 0); + + // get first ReqSpec if not defined + if (is_null($argsObj->idReqSpec)) { + reset($guiObj->arrReqSpec); + $argsObj->idReqSpec = key($guiObj->arrReqSpec); + } + + if ($argsObj->idReqSpec) { + $req_spec_mgr = new requirement_spec_mgr($dbHandler); + $fx = array( + 'link_status' => array( + LINK_TC_REQ_OPEN, + LINK_TC_REQ_CLOSED_BY_EXEC + ) + ); + $theAssigned = $req_spec_mgr->getReqsOnSpecForLatestTCV( + $argsObj->idReqSpec, $argsObj->id, null, $fx); + + $guiObj->assignedReq = $theAssigned; + $guiObj->allReq = $req_spec_mgr->get_requirements( + $argsObj->idReqSpec); + + $guiObj->unassignedReq = arrayDiffById($guiObj->allReq, + $guiObj->assignedReq); + } + } + } + return $guiObj; +} + +/** + */ +function initializeGui(&$dbH, $argsObj) +{ + $guiObj = new stdClass(); + $guiObj->user_feedback = ''; + $guiObj->tcTitle = $guiObj->assignedReq = null; + $guiObj->unassignedReq = $guiObj->arrReqSpec = null; + + $guiObj->showCloseButton = $argsObj->showCloseButton; + $guiObj->selectedReqSpec = $argsObj->idReqSpec; + $guiObj->form_token = $argsObj->form_token; + + $guiObj->tcase_id = $argsObj->id; + $guiObj->callback = $argsObj->callback; + + $reqCfg = getWebEditorCfg('requirement'); + $guiObj->reqEditorType = $reqCfg['type']; + $reqCfg = getWebEditorCfg('requirement_spec'); + $guiObj->reqSpecEditorType = $reqCfg['type']; + + $guiObj->req_tcase_link_management = $argsObj->user->hasRightOnProj($dbH, + 'req_tcase_link_management'); + + return $guiObj; +} + +/** + */ +function getTargetTestCases(&$dbHandler, &$argsObj) +{ + $mgr = new testsuite($dbHandler); + $items = $mgr->get_testcases_deep($argsObj->id, 'only_id'); + + if (! is_null($argsObj->tcaseSet)) { + $rr = array_intersect($items, $argsObj->tcaseSet); + $items = $rr; + } + + return $items; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "req_tcase_link_management" + ]; + pageAccessCheck($db, $user, $context); } -$tpl = $templateCfg->template_dir . $templateCfg->default_template; -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($tpl); - - -/** - * - */ -function init_args() { - $iParams = array("id" => array(tlInputParameter::INT_N), - "req_id" => array(tlInputParameter::ARRAY_INT), - "link_id" => array(tlInputParameter::ARRAY_INT), - - "req" => array(tlInputParameter::INT_N), - "showCloseButton" => array(tlInputParameter::STRING_N,0,1), - "doAction" => array(tlInputParameter::STRING_N,0,100), - "edit" => array(tlInputParameter::STRING_N,0,100), - "unassign" => array(tlInputParameter::STRING_N,0,1), - "assign" => array(tlInputParameter::STRING_N,0,1), - "form_token" => array(tlInputParameter::INT_N), - "callback" => array(tlInputParameter::STRING_N,0,1), - "idSRS" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - // take care of proper escaping when magic_quotes_gpc is enabled - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args->idReqSpec = null; - $args->idReq = $args->req; - $args->reqIdSet = $args->req_id; - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - - $args->tcaseSet = null; - if(isset($_SESSION['edit_mode'][$args->form_token]['testcases_to_show'])) { - $args->tcaseSet = $_SESSION['edit_mode'][$args->form_token]['testcases_to_show']; - } - - if(is_null($args->doAction)) { - $args->doAction = ($args->unassign != "") ? "unassign" : null; - } - - if(is_null($args->doAction)){ - $args->doAction = ($args->assign != "") ? "assign" : null; - } - - if ($args->idSRS) { - $args->idReqSpec = $args->idSRS; - $_SESSION['currentSrsId'] = $args->idReqSpec; - } - else if(isset($_SESSION['currentSrsId']) && intval($_SESSION['currentSrsId']) > 0) { - $args->idReqSpec = intval($_SESSION['currentSrsId']); - } - - - $args->user = $_SESSION['currentUser']; - return $args; -} - -/** - * - * - */ -function processTestSuite(&$dbHandler,&$argsObj,&$guiObj) { - $tproject_mgr = new testproject($dbHandler); - - $guiObj->bulkassign_warning_msg = ''; - $guiObj->tsuite_id = $argsObj->id; - - $tsuite_info = $tproject_mgr->tree_manager->get_node_hierarchy_info($guiObj->tsuite_id); - $guiObj->pageTitle = lang_get('test_suite') . config_get('gui_title_separator_1') . $tsuite_info['name']; - - $guiObj->req_specs = $tproject_mgr->genComboReqSpec($argsObj->tproject_id,'dotted'," "); - - $guiObj->selectedReqSpec = $argsObj->idReqSpec; - $guiObj->selectedReqSpecName = ''; - - $guiObj->tcase_number = 0; - $guiObj->has_req_spec = false; - - if(!is_null($guiObj->req_specs) && count($guiObj->req_specs)) { - $guiObj->has_req_spec = true; - - if(is_null($argsObj->idReqSpec)) { - $guiObj->selectedReqSpec = key($guiObj->req_specs); - } - $guiObj->selectedReqSpecName = - trim($guiObj->req_specs[$guiObj->selectedReqSpec],' '); - - $req_spec_mgr = new requirement_spec_mgr($dbHandler); - - $getOpt = array('output' => 'array'); - $guiObj->requirements = - $req_spec_mgr->getAllLatestRQVOnReqSpec($guiObj->selectedReqSpec,$getOpt); - - $guiObj->reqCountOnReqSpec = count((array)$guiObj->requirements); - - $guiObj->reqCountFeedback = - sprintf(lang_get('req_on_req_spec'),$guiObj->reqCountOnReqSpec, - $guiObj->selectedReqSpecName); - - - $tcase_set = getTargetTestCases($dbHandler,$argsObj); - $guiObj->tcase_number = count($tcase_set); - if( $guiObj->tcase_number > 0 ) { - $guiObj->bulkassign_warning_msg = - sprintf(lang_get('bulk_req_assign_msg'),$guiObj->tcase_number,$tsuite_info['name']); - } else { - $guiObj->bulkassign_warning_msg = - lang_get('bulk_req_assign_no_test_cases'); - } - } - return $guiObj; -} - -/** - * - * - */ -function doBulkAssignment(&$dbHandler,&$argsObj,$targetTestCaseSet = null) -{ - $req_mgr = new requirement_mgr($dbHandler); - $assignmentCounter = 0; - $requirements = array_keys($argsObj->reqIdSet); - if(!is_null($requirements) && count($requirements) > 0) - { - $tcase_set = $targetTestCaseSet; - if( is_null($tcase_set) ) - { - $tsuite_mgr = new testsuite($dbHandler); - $tcase_set = $tsuite_mgr->get_testcases_deep($argsObj->id,'only_id'); - } - - if( !is_null($tcase_set) && count($tcase_set) ) - { - // $assignmentCounter = $req_mgr->bulk_assignment($requirements,$tcase_set,$argsObj->user->dbID); - - $assignmentCounter = - $req_mgr->bulkAssignLatestREQVTCV($requirements,$tcase_set,$argsObj->user->dbID); - - } - - } - return $assignmentCounter; -} - -/** - * - */ -function doSingleTestCaseOperation(&$dbHandler,&$argsObj,&$guiObj,$pfn) { - $msg = ''; - - switch($pfn) { - case 'assign_to_tcase': - $items = array_keys($argsObj->reqIdSet); - break; - - case 'delReqVersionTCVersionLinkByID': - $items = array_keys($argsObj->link_id); - break; - } - - if( count($items) == 0 ) { - $guiObj->user_feedback = lang_get('req_msg_noselect'); - return $guiObj; - } - - $req_mgr = new requirement_mgr($dbHandler); - - - switch($pfn) { - case 'assign_to_tcase': - foreach ($items as $idOneReq) { - $res = $req_mgr->$pfn($idOneReq,$argsObj->id,$argsObj->user->dbID); - if (!$res) { - $msg .= $idOneReq . ', '; - } - } - if (!empty($msg)) { - $guiObj->user_feedback = lang_get('req_msg_notupdated_coverage') . $msg; - } - break; - - case 'delReqVersionTCVersionLinkByID': - foreach ($items as $idLink) { - $res = $req_mgr->$pfn($idLink); - if (!$res) { - $msg .= $idOneReq . ', '; - } - } - if (!empty($msg)) { - $guiObj->user_feedback = lang_get('req_msg_notupdated_coverage') . $msg; - } - break; - } - - return $guiObj; -} - - -/** - * - * - */ -function array_diff_byId($arrAll, $arrPart) { - - if (is_null($arrAll) || !count($arrAll)) { - return null; - } - - if (is_null($arrPart) || !count($arrPart)) { - return $arrAll; - } - - $arrTempAll = array(); - foreach ($arrAll as $penny) { - $highLander[$penny['id']] = $penny; - } - - foreach ($arrPart as $penny) { - if(isset($highLander[$penny['id']])) { - unset($highLander[$penny['id']]); - } - } - - return array_values($highLander); -} - - -/** - * processTestCase - * - */ -function processTestCase(&$dbHandler,&$argsObj,&$guiObj) { - $tproject_mgr = new testproject($dbHandler); - $guiObj->arrReqSpec = $tproject_mgr->genComboReqSpec($argsObj->tproject_id,'dotted'," "); - $SRS_qty = count($guiObj->arrReqSpec); - - if($SRS_qty > 0) { - $tc_mgr = new testcase($dbHandler); - $tcase = $tc_mgr->get_by_id($argsObj->id,testcase::LATEST_VERSION); - $tcase = current($tcase); - if($tcase) { - $guiObj->tcTitle = $tcase['name']; - $guiObj->tcVersion = $tcase['version']; - - // get test case version execution status - $tcversion_id = $tcase['id']; - $statusQuo = $tc_mgr->get_versions_status_quo($argsObj->id,$tcversion_id); - - $statusQuo = current($statusQuo); - $guiObj->tcaseHasBeenExecuted = (intval($statusQuo['executed']) > 0); - - // get first ReqSpec if not defined - if(is_null($argsObj->idReqSpec)) { - reset($guiObj->arrReqSpec); - $argsObj->idReqSpec = key($guiObj->arrReqSpec); - } - - if($argsObj->idReqSpec) { - $req_spec_mgr = new requirement_spec_mgr($dbHandler); - $fx = array('link_status' => array(LINK_TC_REQ_OPEN,LINK_TC_REQ_CLOSED_BY_EXEC)); - $theAssigned = $req_spec_mgr->getReqsOnSpecForLatestTCV($argsObj->idReqSpec,$argsObj->id,null,$fx); - - $guiObj->assignedReq = $theAssigned; - $guiObj->allReq = $req_spec_mgr->get_requirements($argsObj->idReqSpec); - - $guiObj->unassignedReq = array_diff_byId($guiObj->allReq, $guiObj->assignedReq); - } - } - } - return $guiObj; -} - -/** - * - */ -function initializeGui(&$dbH,$argsObj) { - $guiObj = new stdClass(); - $guiObj->user_feedback = ''; - $guiObj->tcTitle = $guiObj->assignedReq = null; - $guiObj->unassignedReq = $guiObj->arrReqSpec = null; - - $guiObj->showCloseButton = $argsObj->showCloseButton; - $guiObj->selectedReqSpec = $argsObj->idReqSpec; - $guiObj->form_token = $argsObj->form_token; - - $guiObj->tcase_id = $argsObj->id; - $guiObj->callback = $argsObj->callback; - - $reqCfg = getWebEditorCfg('requirement'); - $guiObj->reqEditorType = $reqCfg['type']; - $reqCfg = getWebEditorCfg('requirement_spec'); - $guiObj->reqSpecEditorType = $reqCfg['type']; - - $guiObj->req_tcase_link_management = - $argsObj->user->hasRight($dbH,'req_tcase_link_management'); - - return $guiObj; -} - -/** - * - */ -function getTargetTestCases(&$dbHandler,&$argsObj) { - $mgr = new testsuite($dbHandler); - $items = $mgr->get_testcases_deep($argsObj->id,'only_id'); - - if(!is_null($argsObj->tcaseSet)) { - $rr = array_intersect($items,$argsObj->tcaseSet); - $items = $rr; - } - - return $items; -} -function checkRights(&$db,&$user) { - return ($user->hasRight($db,'req_tcase_link_management')); -} \ No newline at end of file diff --git a/lib/requirements/reqView.php b/lib/requirements/reqView.php index b088bf64ca..e25fe4edc5 100644 --- a/lib/requirements/reqView.php +++ b/lib/requirements/reqView.php @@ -1,273 +1,300 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . 'reqViewVersions.tpl'); - -/** - * - */ -function init_args( &$reqMgr ) { - $_REQUEST=strings_stripSlashes($_REQUEST); - $iParams = array("req_id" => array(tlInputParameter::INT_N), - "requirement_id" => array(tlInputParameter::INT_N), - "req_version_id" => array(tlInputParameter::INT_N), - "showReqSpecTitle" => array(tlInputParameter::INT_N), - "refreshTree" => array(tlInputParameter::INT_N), - "relation_add_result_msg" => array(tlInputParameter::STRING_N), - "user_feedback" => array(tlInputParameter::STRING_N), - "uploadOPStatusCode" => array(tlInputParameter::STRING_N,0,30)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - if($args->req_id <= 0) { - $args->req_id = $args->requirement_id; - } - - $args->reqVersionIDFromCaller = $args->req_version_id; - $args->showAllVersions = false; - - if( $args->req_version_id == 0 ) { - $args->showAllVersions = true; - $lv = $reqMgr->get_last_version_info($args->req_id); - $args->req_version_id = intval($lv['id']); - } - - $args->refreshTree = intval($args->refreshTree); - $args->tproject_id = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - $args->user = $_SESSION['currentUser']; - $args->userID = $args->user->dbID; - - return $args; +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . 'reqViewVersions.tpl'); + +/** + */ +function initArgs(&$reqMgr) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $iParams = array( + "req_id" => array( + tlInputParameter::INT_N + ), + "requirement_id" => array( + tlInputParameter::INT_N + ), + "req_version_id" => array( + tlInputParameter::INT_N + ), + "showReqSpecTitle" => array( + tlInputParameter::INT_N + ), + "refreshTree" => array( + tlInputParameter::INT_N + ), + "relation_add_result_msg" => array( + tlInputParameter::STRING_N + ), + "user_feedback" => array( + tlInputParameter::STRING_N + ), + "uploadOPStatusCode" => array( + tlInputParameter::STRING_N, + 0, + 30 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + if ($args->req_id <= 0) { + $args->req_id = $args->requirement_id; + } + + $args->reqVersionIDFromCaller = $args->req_version_id; + $args->showAllVersions = false; + + if ($args->req_version_id == 0) { + $args->showAllVersions = true; + $lv = $reqMgr->getLastVersionInfo($args->req_id); + $args->req_version_id = intval($lv['id']); + } + + $args->refreshTree = intval($args->refreshTree); + $args->tproject_id = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + $args->user = $_SESSION['currentUser']; + $args->userID = $args->user->dbID; + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, $argsObj, &$tproject_mgr, &$req_mgr) +{ + $commandMgr = new reqCommands($dbHandler); + + $gui = $commandMgr->initGuiBean($argsObj); + + $opt = array( + 'renderImageInline' => true + ); + $gui->req_versions = $req_mgr->get_by_id($gui->req_id, $gui->version_option, + 1, $opt); + + $gui->reqHasBeenDeleted = false; + if (is_null($gui->req_versions)) { + // this means that requirement does not exist anymore. + // We have to give just that info to user + $gui->reqHasBeenDeleted = true; + $gui->main_descr = lang_get('req_does_not_exist'); + unset($gui->show_match_count); + return $gui; // >>>----> Bye! + } + + // Everything OK, go ahead + $tproject_id = $req_mgr->getTestProjectID($argsObj->requirement_id); + $target_id = $argsObj->tproject_id; + if ($isAlien = ($tproject_id != $argsObj->tproject_id)) { + $target_id = $tproject_id; + } + + $gui->grants = getGrants($dbHandler, $argsObj->user, $target_id); + $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); + + $gui->reqMonitors = $req_mgr->getReqMonitors($gui->req_id); + $gui->btn_monitor_mgmt = lang_get('btn_start_mon'); + $gui->btn_monitor_action = 'startMonitoring'; + if (isset($gui->reqMonitors[$argsObj->userID])) { + $gui->btn_monitor_mgmt = lang_get('btn_stop_mon'); + $gui->btn_monitor_action = 'stopMonitoring'; + } + + $gui->req = current($gui->req_versions); + + // 2018 $gui->req_coverage = $req_mgr->get_coverage($gui->req_id); + // This need to become an array. + $loop2do = count($gui->req_versions); + $gui->current_req_coverage = array(); + $gui->other_req_coverage = array(); + for ($cvx = 0; $cvx < $loop2do; $cvx ++) { + $bebe = $gui->req_versions[$cvx]['version_id']; + + if ($cvx == 0) { + $gui->current_req_coverage = $req_mgr->getActiveForReqVersion($bebe); + } else { + $gui->other_req_coverage[0][] = $req_mgr->getActiveForReqVersion( + $bebe); + } + } + + $gui->direct_link = $_SESSION['basehref'] . 'linkto.php?tprojectPrefix=' . + urlencode($gui->tcasePrefix) . '&item=req&id=' . + urlencode($gui->req['req_doc_id']); + + // Same for all versions because we only use the FILE ID + // need to be refactored + $gui->delAttachmentURL = $_SESSION['basehref'] . + $req_mgr->getDeleteAttachmentRelativeURL($gui->req_id, 0); + + $gui->fileUploadURL = array(); + $gui->fileUploadURL[$gui->req_version_id] = $_SESSION['basehref'] . + $req_mgr->getFileUploadRelativeURL($gui->req_id, $gui->req_version_id); + + $gui->log_target = null; + $loop2do = count($gui->req_versions); + for ($rqx = 0; $rqx < $loop2do; $rqx ++) { + $gui->log_target[] = ($gui->req_versions[$rqx]['revision_id'] > 0) ? $gui->req_versions[$rqx]['revision_id'] : $gui->req_versions[$rqx]['version_id']; + } + + $gui->req_has_history = count( + $req_mgr->get_history($gui->req_id, array( + 'output' => 'array' + ))) > 1; + + // This seems weird but is done to adapt template than can + // display multiple requirements. + // This logic has been borrowed from test case versions management + $gui->current_version[0] = array( + $gui->req + ); + $gui->cfields_current_version[0] = $req_mgr->html_table_of_custom_field_values( + $gui->req_id, $gui->req['version_id'], $argsObj->tproject_id); + + // Now CF for other Versions + $gui->other_versions[0] = null; + $gui->cfields_other_versions[] = null; + if (count($gui->req_versions) > 1) { + $gui->other_versions[0] = array_slice($gui->req_versions, 1); + $loop2do = count($gui->other_versions[0]); + for ($qdx = 0; $qdx < $loop2do; $qdx ++) { + $target_version = $gui->other_versions[0][$qdx]['version_id']; + $gui->cfields_other_versions[0][$qdx] = $req_mgr->html_table_of_custom_field_values( + $gui->req_id, $target_version, $argsObj->tproject_id); + + // File Upload Management + $gui->fileUploadURL[$target_version] = $_SESSION['basehref'] . + $req_mgr->getFileUploadRelativeURL($gui->req_id, $target_version); + } + } + + $gui->show_title = false; + $gui->main_descr = lang_get('req') . $gui->pieceSep . $gui->req['title']; + + $gui->showReqSpecTitle = $argsObj->showReqSpecTitle; + if ($gui->showReqSpecTitle) { + $gui->parent_descr = lang_get('req_spec_short') . $gui->pieceSep . + $gui->req['req_spec_title']; + } + + if ($gui->showAllVersions) { + $versionSet = array(); + $loop2do = count($gui->req_versions); + for ($ggx = 0; $ggx < $loop2do; $ggx ++) { + $versionSet[] = intval($gui->req_versions[$ggx]['version_id']); + } + } else { + $versionSet = array( + $gui->req_version_id + ); + } + + foreach ($versionSet as $kiwi) { + $gui->attachments[$kiwi] = getAttachmentInfosFrom($req_mgr, $kiwi); + } + + $gui->reqStatus = init_labels($gui->req_cfg->status_labels); + $gui->reqTypeDomain = init_labels($gui->req_cfg->type_labels); + + $gui->req_relations = false; + $gui->req_relation_select = false; + $gui->testproject_select = false; + $gui->req_add_result_msg = isset($argsObj->relation_add_result_msg) ? $argsObj->relation_add_result_msg : ""; + + if ($gui->req_cfg->relations->enable) { + $gui->req_relations = $req_mgr->get_relations($gui->req_id); + $gui->req_relations['rw'] = ! $isAlien; + $gui->req_relation_select = $req_mgr->init_relation_type_select(); + if ($gui->req_cfg->relations->interproject_linking) { + $gui->testproject_select = initTestprojectSelect($argsObj->userID, + $argsObj->tproject_id, $tproject_mgr); + } + } + + $gui->uploadOp = null; + if (trim($argsObj->uploadOPStatusCode) != '') { + $gui->uploadOp = new stdClass(); + $gui->uploadOp->statusOK = false; + $gui->uploadOp->statusCode = $argsObj->uploadOPStatusCode; + $gui->uploadOp->msg = lang_get($argsObj->uploadOPStatusCode); + } + + return $gui; +} + +/** + */ +function getGrants(&$dbH, &$userObj, $tproject_id) +{ + $grants = new stdClass(); + $gk = array( + 'req_mgmt' => "mgt_modify_req", + 'monitor_req' => "monitor_requirement", + 'req_tcase_link_management' => 'req_tcase_link_management', + 'unfreeze_req' => 'mgt_unfreeze_req' + ); + + foreach ($gk as $p => $g) { + $grants->$p = $userObj->hasRight($dbH, $g, $tproject_id); + } + return $grants; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req" + ]; + pageAccessCheck($db, $user, $context); +} + +/** + * Initializes the select field for the testprojects. + * + * @return array $htmlSelect array with info, needed to create testproject select box on template + */ +function initTestprojectSelect($userID, $tprojectID, &$tprojectMgr) +{ + $opt = array( + 'output' => 'map_name_with_inactive_mark', + 'order_by' => config_get('gui')->tprojects_combo_order_by + ); + $testprojects = $tprojectMgr->get_accessible_for_user($userID, $opt); + return array( + 'items' => $testprojects, + 'selected' => $tprojectID + ); } - -/** - * - * - */ -function initialize_gui(&$dbHandler,$argsObj,&$tproject_mgr,&$req_mgr) { - $commandMgr = new reqCommands($dbHandler); - - $gui = $commandMgr->initGuiBean( $argsObj ); - - $opt = array('renderImageInline' => true); - $gui->req_versions = - $req_mgr->get_by_id($gui->req_id, $gui->version_option,1,$opt); - - $gui->reqHasBeenDeleted = false; - if( is_null($gui->req_versions) ) { - // this means that requirement does not exist anymore. - // We have to give just that info to user - $gui->reqHasBeenDeleted = true; - $gui->main_descr = lang_get('req_does_not_exist'); - unset($gui->show_match_count); - return $gui; // >>>----> Bye! - } - - // Everything OK, go ahead - $tproject_id = $req_mgr->getTestProjectID($argsObj->requirement_id); - $target_id = $argsObj->tproject_id; - if( ($isAlien = ($tproject_id != $argsObj->tproject_id)) ) { - $target_id = $tproject_id; - } - - $gui->grants = getGrants($dbHandler,$argsObj->user,$target_id); - $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - - - $gui->reqMonitors = $req_mgr->getReqMonitors($gui->req_id); - $gui->btn_monitor_mgmt = lang_get('btn_start_mon'); - $gui->btn_monitor_action = 'startMonitoring'; - if(isset($gui->reqMonitors[$argsObj->userID])) { - $gui->btn_monitor_mgmt = lang_get('btn_stop_mon'); - $gui->btn_monitor_action = 'stopMonitoring'; - } - - $gui->req = current($gui->req_versions); - - // 2018 $gui->req_coverage = $req_mgr->get_coverage($gui->req_id); - // This need to become an array. - $loop2do = count($gui->req_versions); - $gui->current_req_coverage = array(); - $gui->other_req_coverage = array(); - for($cvx = 0 ; $cvx < $loop2do; $cvx++) { - $bebe = $gui->req_versions[$cvx]['version_id']; - - if( $cvx == 0 ) { - $gui->current_req_coverage = $req_mgr->getActiveForReqVersion($bebe); - } else { - $gui->other_req_coverage[0][] = $req_mgr->getActiveForReqVersion($bebe); - } - } - - $gui->direct_link = $_SESSION['basehref'] . 'linkto.php?tprojectPrefix=' . - urlencode($gui->tcasePrefix) . '&item=req&id=' . urlencode($gui->req['req_doc_id']); - - - /* - $gui->fileUploadURL = $gui->delAttachmentURL = $_SESSION['basehref']; - $gui->fileUploadURL .= - $req_mgr->getFileUploadRelativeURL($gui->req_id, $gui->req_version_id); - - $gui->delAttachmentURL .= - $req_mgr->getDeleteAttachmentRelativeURL($gui->req_id, $gui->req_version_id); - */ - - // Same for all versions because we only use the FILE ID - // need to be refactored - $gui->delAttachmentURL = $_SESSION['basehref'] . - $req_mgr->getDeleteAttachmentRelativeURL($gui->req_id,0); - - $gui->fileUploadURL = array(); - $gui->fileUploadURL[$gui->req_version_id] = $_SESSION['basehref'] . - $req_mgr->getFileUploadRelativeURL($gui->req_id, $gui->req_version_id); - - $gui->log_target = null; - $loop2do = count($gui->req_versions); - for($rqx = 0; $rqx < $loop2do; $rqx++) { - $gui->log_target[] = ($gui->req_versions[$rqx]['revision_id'] > 0) ? $gui->req_versions[$rqx]['revision_id'] : - $gui->req_versions[$rqx]['version_id']; - } - - $gui->req_has_history = count($req_mgr->get_history($gui->req_id, array('output' => 'array'))) > 1; - - - // This seems weird but is done to adapt template than can - // display multiple requirements. - // This logic has been borrowed from test case versions management - $gui->current_version[0] = array($gui->req); - $gui->cfields_current_version[0] = - $req_mgr->html_table_of_custom_field_values($gui->req_id,$gui->req['version_id'], - $argsObj->tproject_id); - - // Now CF for other Versions - $gui->other_versions[0] = null; - $gui->cfields_other_versions[] = null; - if( count($gui->req_versions) > 1 ) { - $gui->other_versions[0] = array_slice($gui->req_versions,1); - $loop2do = count($gui->other_versions[0]); - for($qdx=0; $qdx < $loop2do; $qdx++) { - $target_version = $gui->other_versions[0][$qdx]['version_id']; - $gui->cfields_other_versions[0][$qdx]= - $req_mgr->html_table_of_custom_field_values($gui->req_id,$target_version,$argsObj->tproject_id); - - // File Upload Management - $gui->fileUploadURL[$target_version] = $_SESSION['basehref'] . - $req_mgr->getFileUploadRelativeURL($gui->req_id, $target_version); - } - } - - $gui->show_title = false; - $gui->main_descr = lang_get('req') . $gui->pieceSep . $gui->req['title']; - - $gui->showReqSpecTitle = $argsObj->showReqSpecTitle; - if($gui->showReqSpecTitle) { - $gui->parent_descr = lang_get('req_spec_short') . $gui->pieceSep . $gui->req['req_spec_title']; - } - - - if( $gui->showAllVersions ) { - $versionSet = array(); - $loop2do = count($gui->req_versions); - for( $ggx=0; $ggx < $loop2do; $ggx++ ) { - $versionSet[] = intval($gui->req_versions[$ggx]['version_id']); - } - } else { - $versionSet = array($gui->req_version_id); - } - - foreach ($versionSet as $kiwi) { - $gui->attachments[$kiwi] = getAttachmentInfosFrom($req_mgr,$kiwi); - } - - $gui->reqStatus = init_labels($gui->req_cfg->status_labels); - $gui->reqTypeDomain = init_labels($gui->req_cfg->type_labels); - - $gui->req_relations = FALSE; - $gui->req_relation_select = FALSE; - $gui->testproject_select = FALSE; - $gui->req_add_result_msg = isset($argsObj->relation_add_result_msg) ? - $argsObj->relation_add_result_msg : ""; - - if ($gui->req_cfg->relations->enable) { - $gui->req_relations = $req_mgr->get_relations($gui->req_id); - $gui->req_relations['rw'] = !$isAlien; - $gui->req_relation_select = $req_mgr->init_relation_type_select(); - if ($gui->req_cfg->relations->interproject_linking) { - $gui->testproject_select = initTestprojectSelect($argsObj->userID, $argsObj->tproject_id,$tproject_mgr); - } - } - - $gui->uploadOp = null; - if (trim($argsObj->uploadOPStatusCode) != '') { - $gui->uploadOp = new stdClass(); - $gui->uploadOp->statusOK = false; - $gui->uploadOp->statusCode = $argsObj->uploadOPStatusCode; - $gui->uploadOp->msg = lang_get($argsObj->uploadOPStatusCode); - } - - return $gui; -} - - -/** - * - * - */ -function getGrants( &$dbH, &$userObj, $tproject_id ) { - - $grants = new stdClass(); - $gk = array('req_mgmt' => "mgt_modify_req", 'monitor_req' => "monitor_requirement", - 'req_tcase_link_management' => 'req_tcase_link_management', - 'unfreeze_req' => 'mgt_unfreeze_req'); - - foreach($gk as $p => $g) { - $grants->$p = $userObj->hasRight($dbH,$g,$tproject_id); - } - return $grants; -} - - -/** - * - * - */ -function checkRights(&$dbHandler,&$user) { - return $user->hasRight($dbHandler,'mgt_view_req'); -} - - -/** - * Initializes the select field for the testprojects. - * - * @return array $htmlSelect array with info, needed to create testproject select box on template - */ -function initTestprojectSelect($userID, $tprojectID, &$tprojectMgr) { - $opt = array('output' => 'map_name_with_inactive_mark', 'order_by' => config_get('gui')->tprojects_combo_order_by); - $testprojects = $tprojectMgr->get_accessible_for_user($userID,$opt); - $htmlSelect = array('items' => $testprojects, 'selected' => $tprojectID); - return $htmlSelect; -} \ No newline at end of file diff --git a/lib/requirements/reqViewRevision.php b/lib/requirements/reqViewRevision.php index 4c5dee552b..e5edfca602 100644 --- a/lib/requirements/reqViewRevision.php +++ b/lib/requirements/reqViewRevision.php @@ -1,118 +1,130 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . 'reqViewRevisionRO.tpl'); - -/** - * - */ -function init_args() { - $iParams = array("item_id" => array(tlInputParameter::INT_N), - "showReqSpecTitle" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $args->tproject_id = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - $user = $_SESSION['currentUser']; - $args->userID = $user->dbID; - - return $args; +tproject_id = $args->tproject_id; +checkRights($db, $_SESSION['currentUser'], $context); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . 'reqViewRevisionRO.tpl'); + +/** + */ +function initArgs() +{ + $iParams = array( + "item_id" => array( + tlInputParameter::INT_N + ), + "showReqSpecTitle" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->tproject_id = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + $user = $_SESSION['currentUser']; + $args->userID = $user->dbID; + + return $args; +} + +/** + */ +function initializeGui(&$dbHandler, $argsObj) +{ + $tproject_mgr = new testproject($dbHandler); + $req_mgr = new requirement_mgr($dbHandler); + $commandMgr = new reqCommands($dbHandler); + + $gui = $commandMgr->initGuiBean(); + $gui->req_cfg = config_get('req_cfg'); + $gui->tproject_name = $argsObj->tproject_name; + + $gui->grants = new stdClass(); + $gui->grants->req_mgmt = has_rights($dbHandler, "mgt_modify_req"); + + $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); + $gui->glueChar = config_get('testcase_cfg')->glue_character; + $gui->pieceSep = config_get('gui_title_separator_1'); + + $gui->item_id = $argsObj->item_id; + + // identify item is version or revision ? + $node_type_id = $req_mgr->tree_mgr->get_available_node_types(); + $node_id_type = array_flip($node_type_id); + $item = $req_mgr->tree_mgr->get_node_hierarchy_info($gui->item_id); + + // target_is is db id of item, item['id'] is the REQ ID. + // for several logics we need to DB id (target_id) + $info = null; + $getOpt = array( + 'renderImageInline' => true + ); + switch ($node_id_type[$item['node_type_id']]) { + case 'requirement_version': + $info = $req_mgr->get_version($gui->item_id, $getOpt); + $info['revision_id'] = - 1; + $info['target_id'] = $reqVersionID = $info['version_id']; + + break; + + case 'requirement_revision': + $info = $req_mgr->get_revision($gui->item_id, $getOpt); + $info['target_id'] = $info['revision_id']; + + $reqVersionID = $info['req_version_id']; + break; + } + $gui->reqCoverage = $req_mgr->getActiveForReqVersion($reqVersionID); + + $gui->item = $info; + $gui->cfields = $req_mgr->html_table_of_custom_field_values(null, + $gui->item_id, $argsObj->tproject_id); + $gui->show_title = false; + $gui->main_descr = lang_get('req') . $gui->pieceSep . $gui->item['title']; + + $gui->showReqSpecTitle = $argsObj->showReqSpecTitle; + if ($gui->showReqSpecTitle) { + $gui->parent_descr = lang_get('req_spec_short') . $gui->pieceSep . + $gui->item['req_spec_title']; + } + + $gui->reqStatus = init_labels($gui->req_cfg->status_labels); + $gui->reqTypeDomain = init_labels($gui->req_cfg->type_labels); + return $gui; +} + +/** + */ +function checkRights(&$db, &$user, &$context) +{ + $context->rightsOr = []; + $context->rightsAnd = [ + "mgt_view_req" + ]; + pageAccessCheck($db, $user, $context); } - -/** - * - * - */ -function initialize_gui(&$dbHandler,$argsObj) { - $tproject_mgr = new testproject($dbHandler); - $req_mgr = new requirement_mgr($dbHandler); - $commandMgr = new reqCommands($dbHandler); - - $gui = $commandMgr->initGuiBean(); - $gui->req_cfg = config_get('req_cfg'); - $gui->tproject_name = $argsObj->tproject_name; - - $gui->grants = new stdClass(); - $gui->grants->req_mgmt = has_rights($dbHandler,"mgt_modify_req"); - - $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - $gui->glueChar = config_get('testcase_cfg')->glue_character; - $gui->pieceSep = config_get('gui_title_separator_1'); - - $gui->item_id = $argsObj->item_id; - - // identify item is version or revision ? - $node_type_id = $req_mgr->tree_mgr->get_available_node_types(); - $node_id_type = array_flip($node_type_id); - $item = $req_mgr->tree_mgr->get_node_hierarchy_info($gui->item_id); - - // target_is is db id of item, item['id'] is the REQ ID. - // for several logics we need to DB id (target_id) - $info = null; - $getOpt = array('renderImageInline' => true); - switch ($node_id_type[$item['node_type_id']]) { - case 'requirement_version': - $info = $req_mgr->get_version($gui->item_id,$getOpt); - $info['revision_id'] = -1; - $info['target_id'] = $reqVersionID = $info['version_id']; - - break; - - case 'requirement_revision': - $info = $req_mgr->get_revision($gui->item_id,$getOpt); - $info['target_id'] = $info['revision_id']; - - $reqVersionID = $info['req_version_id']; - break; - } - $gui->reqCoverage = $req_mgr->getActiveForReqVersion($reqVersionID); - - $gui->item = $info; - $gui->cfields = $req_mgr->html_table_of_custom_field_values(null,$gui->item_id,$argsObj->tproject_id); - $gui->show_title = false; - $gui->main_descr = lang_get('req') . $gui->pieceSep . $gui->item['title']; - - $gui->showReqSpecTitle = $argsObj->showReqSpecTitle; - if($gui->showReqSpecTitle) { - $gui->parent_descr = lang_get('req_spec_short') . $gui->pieceSep . $gui->item['req_spec_title']; - } - - $gui->reqStatus = init_labels($gui->req_cfg->status_labels); - $gui->reqTypeDomain = init_labels($gui->req_cfg->type_labels); - return $gui; -} - - -/** - * - * - */ -function checkRights(&$dbHandler,&$user) -{ - return $user->hasRight($dbHandler,'mgt_view_req'); -} -?> \ No newline at end of file diff --git a/lib/results/baselinel1l2.php b/lib/results/baselinel1l2.php index 130704875d..901a245414 100644 --- a/lib/results/baselinel1l2.php +++ b/lib/results/baselinel1l2.php @@ -1,597 +1,680 @@ -do_report['status_ok'] = 1; - $gui->do_report['msg'] = ''; - -$tables = tlObject::getDBTables(array('baseline_l1l2_context', - 'baseline_l1l2_details', - 'nodes_hierarchy')); - -// Get virtual columns -$sql = "SELECT distinct(status) as status_col - FROM {$tables['baseline_l1l2_context']} BLC - JOIN {$tables['baseline_l1l2_details']} BLDT - ON BLDT.context_id = BLC.id - WHERE BLC.testplan_id = $args->tplan_id"; - -$statusRS = $db->fetchRowsIntoMap($sql,'status_col'); -$cfg = config_get('results'); -$codeToStatus = array_flip($cfg['status_code']); -$statusToLabel = $cfg['status_label']; -$statusDisplayOrder = $cfg['status_order']; -$statusCols = array(); -$gui->columnsDefinition = array(); - -foreach ($statusDisplayOrder as $x => $code) { - $statusCols[$code] = $statusToLabel[$codeToStatus[$code]]; - $gui->columnsDefinition[$codeToStatus[$code]] = - array('qty' => lang_get($statusCols[$code]), - 'percentage' => "[%]"); - - $data_tpl[$codeToStatus[$code]] = array('qty' => 0, 'percentage' => 0); -} - - -//foreach ($gui->platformSet as $plat_id => $plat_name) { - $sql = "SELECT context_id,BLDT.id AS detail_id, - testplan_id,platform_id, - begin_exec_ts,end_exec_ts,creation_ts, - top_tsuite_id,child_tsuite_id,status,qty,total_tc, - TS_TOP.name AS top_name, TS_CHI.name AS child_name - FROM {$tables['baseline_l1l2_context']} BLC - JOIN {$tables['baseline_l1l2_details']} BLDT - ON BLDT.context_id = BLC.id - JOIN {$tables['nodes_hierarchy']} AS TS_TOP - ON TS_TOP.id = top_tsuite_id - JOIN {$tables['nodes_hierarchy']} AS TS_CHI - ON TS_CHI.id = child_tsuite_id - WHERE BLC.testplan_id = $args->tplan_id - ORDER BY BLC.creation_ts DESC, top_name ASC,child_name ASC"; - - - $keyCols = array('platform_id','context_id', - 'top_tsuite_id','child_tsuite_id'); - $rsu = $db->fetchRowsIntoMap4l($sql,$keyCols,true); - - - -// Generate statistics for each platform -// Platforms are ordered by name -foreach ($rsu as $plat_id => $dataByContext) { - $gui->statistics = array(); - - $gui->statistics[$plat_id] = array(); - $gui->span[$plat_id] = array(); - - $rx = 0; - foreach ($dataByContext as $context_id => $dataByTop) { - $gui->statistics[$plat_id][$rx] = array(); - $gui->span[$plat_id][$rx] = null; - - $rrr = current(current($dataByTop))[0]; - reset($dataByTop); - $gui->span[$plat_id][$rx] = - array('begin' => $rrr['begin_exec_ts'], - 'end' => $rrr['end_exec_ts'], - 'baseline_ts' => $rrr['creation_ts']); - - foreach ($dataByTop as $top_id => $dataByChild) { - foreach ($dataByChild as $child_id => $dataX) { - $gui->statistics[$plat_id][$rx][$child_id] = array(); - $hand = &$gui->statistics[$plat_id][$rx][$child_id]; - - $dfx = $dataX[0]; - $hand['name'] = $dfx['top_name'] . ':' . $dfx['child_name']; - $hand['total_tc'] = $dfx['total_tc']; - $hand['percentage_completed'] = -1; - $hand['details'] = $data_tpl; - $hand['parent_id'] = $top_id; - - foreach ($dataX as $xx => $xmen) { - $pp = ($hand['total_tc'] > 0) ? - (round(($xmen['qty']/$hand['total_tc']) * 100,1)) : 0; - $hand['details'][$codeToStatus[$xmen['status']]] = - array('qty' => $xmen['qty'],'percentage' => $pp); - } - - // Calculate percentage completed, using all exec status - // other than not run - if ($hand['total_tc'] > 0) { - $hand['percentage_completed'] = - $hand['total_tc'] - $hand['details']['not_run']['qty']; - $hand['percentage_completed'] = - round(($hand['percentage_completed']/$hand['total_tc']) * 100,1); - } - } - } - $rx++; - } -} - - -/* -array(1) { - [187]=> - array(10) { - [33984]=> - array(6) { - ["type"]=> string(6) "tsuite" - ["name"]=> - string(55) "PT/08/TMS/Costing:PT/08.01/Price List & Rate Management" - ["parent_id"]=> string(5) "33953" - ["total_tc"]=> int(218) - ["percentage_completed"]=> string(4) "95.4" - ["details"]=> - array(4) { - ["not_run"]=> array(2) {["qty"]=>int(10) - ["percentage"]=> string(3) "4.6"} - ["passed"]=> - } - } -*/ - - - -$timerOff = microtime(true); -$gui->elapsed_time = round($timerOff - $timerOn,2); - -if ($args->spreadsheet) { - createSpreadsheet($gui,$args,$tplan_mgr); -} - -$smarty = new TLSmarty; -$smarty->assign('gui', $gui); -displayReport($tplCfg->tpl, $smarty, $args->format,$mailCfg); - - -/** - * - * - */ -function buildMailCfg(&$guiObj) { - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . - $labels['testproject'] . ' : ' . - $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * - */ -function initializeGui(&$dbHandler,$argsObj,&$tplanMgr) { - - list($add2args,$gui) = initUserEnv($dbHandler,$argsObj); - - $gui->fakePlatform = array(''); - $gui->title = lang_get('baseline_l1l2'); - $gui->do_report = array(); - $gui->showPlatforms = true; - $gui->columnsDefinition = new stdClass(); - $gui->statistics = new stdClass(); - $gui->elapsed_time = 0; - $gui->displayBuildMetrics = false; - $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); - - $gui->tproject_name = testproject::getName($dbHandler,$argsObj->tproject_id); - - $info = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->tplan_name = $info['name']; - $gui->tplan_id = intval($argsObj->tplan_id); - - $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'map')); - if( is_null($gui->platformSet) ) { - $gui->platformSet = array(''); - $gui->showPlatforms = false; - } else { - natsort($gui->platformSet); - } - - $gui->hasPlatforms = count($gui->platformSet) >= 1 && - !isset($gui->platformSet[0]); - - return $gui; -} - - -/** - * - * - */ -function createSpreadsheet($gui,$args,&$tplanMgr) { - - // N sections - // Always same format - // Platform - // Build Assigned Not Run [%] Passed [%] Failed [%] Blocked [%] - // Completed [%] - - // Results by Platform - // Overall Build Status - // Results by Build - // Results by Top Level Test Suite - // Results by priority - // Results by Keyword - - - $lbl = initLblSpreadsheet(); - $cellRange = setCellRangeSpreadsheet(); - $style = initStyleSpreadsheet(); - - // Common - $execStatusDomain = $tplanMgr->getStatusForReports(); - $dataHeaderMetrics = array(); - // $cellPosForExecStatus = array(); - $ccc = 0; - foreach( $execStatusDomain as $code => $human ) { - $dataHeaderMetrics[] = lang_get('test_status_' . $human); - $ccc++; - $dataHeaderMetrics[] = '[%]'; - $ccc++; - //$cellPosForExecStatus[$human] = $ccc; - } - $dataHeaderMetrics[] = $lbl['completed_perc']; - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - - $oneLevel = array(); - - // NO PLATFORM => ID=0 - if( $gui->hasPlatforms ) { - $oneLevel[] = array('entity' => 'platform', - 'dimension' => 'testcase_qty', - 'nameKey' => 'name', 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->platform); - } - - $oneLevel[] = array('entity' => 'build', 'dimension' => 'testcase_qty', - 'nameKey' => 'build_name', - 'tcQtyKey' => 'total_assigned', - 'source' => &$gui->statistics->overallBuildStatus); - - $startingRow = count($lines2write); // MAGIC - foreach( $oneLevel as $target ) { - $entity = $target['entity']; - $dimension = $target['dimension']; - $dataHeader = array($lbl[$entity],$lbl[$dimension]); - - // intermediate column qty is dynamic because it depends - // of status configuration. - foreach( $dataHeaderMetrics as $val ) { - $dataHeader[] = $val; - } - - $startingRow++; - $startingRow++; - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - - $startingRow++; - $infoSet = $target['source']; - $nameKey = $target['nameKey']; - $tcQtyKey = $target['tcQtyKey']; - - foreach($infoSet as $itemID => $fieldSet) { - - $whatCell = 0; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$nameKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$tcQtyKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - foreach($fieldSet['details'] as $human => $metrics) { - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['qty']); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['percentage']); - } - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, - $fieldSet['percentage_completed']); - $startingRow++; - } - } - - // The first column will be the platform - $twoLevels = array(); - - if( $gui->hasPlatforms ) { - $twoLevels[] = - array('entity' => 'build', 'dimension' => 'testcase_qty', - 'nameKey' => 'build_name', - 'tcQtyKey' => 'total_assigned', - 'source' => &$gui->statistics->buildByPlatMetrics); - } - - $twoLevels[] = - array('entity' => 'testsuite', 'dimension' => 'testcase_qty', - 'nameKey' => 'name', - 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->testsuites); - - $twoLevels[] = array('entity' => 'priority', - 'dimension' => 'testcase_qty', - 'nameKey' => 'name', 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->priorities); - - $twoLevels[] = - array('entity' => 'keyword', 'dimension' => 'testcase_qty', - 'nameKey' => 'name', - 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->keywords); - - foreach( $twoLevels as $target ) { - $startingRow++; - $startingRow++; - - $entity = $target['entity']; - $dimension = $target['dimension']; - $nameKey = $target['nameKey']; - $tcQtyKey = $target['tcQtyKey']; - - if( count($target['source']) == 0 ) { - continue; - } - - // Just ONE HEADER ? - $dataHeader = array($lbl['platform'],$lbl[$entity],$lbl[$dimension]); - if( $gui->hasPlatforms == false ) { - array_shift($dataHeader); - } - - // intermediate column qty is dynamic because it depends - // of status configuration. - foreach( $dataHeaderMetrics as $val ) { - $dataHeader[] = $val; - } - - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - // END ONE HEADER - $startingRow++; - - $idr = ''; - foreach( $gui->platformSet as $platID => $platName ) { - $idr = ('' == $idr || 'rowB' == $idr ) ? 'rowA' : 'rowB'; - - $infoSet = isset($target['source'][$platID]) ? - $target['source'][$platID] : array(); - - foreach($infoSet as $itemID => $fieldSet) { - $whatCell=0; - - if( $gui->hasPlatforms ) { - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $platName; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - } - - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$nameKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$tcQtyKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - foreach($fieldSet['details'] as $human => $metrics) { - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['qty']); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['percentage']); - } - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, - $fieldSet['percentage_completed']); - - $cellZone = "A{$startingRow}:" . $cellRange[$whatCell] . - "$startingRow"; - - $objPHPExcel->getActiveSheet() - ->getStyle($cellZone) - ->applyFromArray($style[$idr]); - - $startingRow++; - } - } - } // on container ? - - // Just to add some final empty row - $cellID = $cellRange[0] . $startingRow; - $field = ''; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - - - // Final step - $tmpfname = tempnam(config_get('temp_dir'),"TestLink_GTMP.tmp"); - $objPHPExcel->setActiveSheetIndex(0); - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - $objWriter->save($tmpfname); - - downloadXls($tmpfname,$xlsType,$gui,'TestLink_GTMP_'); -} - - -/** - * - */ -function xlsStepOne(&$oj,$style,&$lbl,&$gui) { - $dummy = ''; - $lines2write = array(array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time()))); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0) - ->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['ReportContext']); - - return $lines2write; -} - -/** - * - */ -function initLblSpreadsheet() { - $lbl = init_labels( - array('testsuite' => null, - 'testcase_qty' => null,'keyword' => null, - 'platform' => null,'priority' => null, - 'priority_level' => null, - 'build' => null,'testplan' => null, - 'testproject' => null,'not_run' => null, - 'completed_perc' => 'trep_comp_perc', - 'generated_by_TestLink_on' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() { - $style = array(); - $style['ReportContext'] = array('font' => array('bold' => true)); - $style['DataHeader'] = - array('font' => array('bold' => true), - 'borders' => - array('outline' => - array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - - $style['rowA'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FFFFFFFF')) - ); - - $style['rowB'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'DCDCDCDC')) - ); - - return $style; -} - -/** - * - */ -function setCellRangeSpreadsheet() { - $cr = range('A','Z'); - $crLen = count($cr); - for($idx = 0; $idx < $crLen; $idx++) { - for($jdx = 0; $jdx < $crLen; $jdx++) { - $cr[] = $cr[$idx] . $cr[$jdx]; - } - } - return $cr; -} - - - -/** - * - */ -function checkRights(&$db,&$user,$context = null) { - if(is_null($context)) { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; +do_report['status_ok'] = 1; +$gui->do_report['msg'] = ''; + +$tables = tlObject::getDBTables( + array( + 'baseline_l1l2_context', + 'baseline_l1l2_details', + 'nodes_hierarchy' + )); + +// Get virtual columns +$sql = "SELECT distinct(status) as status_col " . + " FROM {$tables['baseline_l1l2_context']} BLC " . + " JOIN {$tables['baseline_l1l2_details']} BLDT " . + " ON BLDT.context_id = BLC.id " . " WHERE BLC.testplan_id = $args->tplan_id"; + +$statusRS = $db->fetchRowsIntoMap($sql, 'status_col'); +$cfg = config_get('results'); +$codeToStatus = array_flip($cfg['status_code']); +$statusToLabel = $cfg['status_label']; +$statusDisplayOrder = $cfg['status_order']; +$statusCols = array(); +$gui->columnsDefinition = array(); + +foreach ($statusDisplayOrder as $code) { + $statusCols[$code] = $statusToLabel[$codeToStatus[$code]]; + $gui->columnsDefinition[$codeToStatus[$code]] = array( + 'qty' => lang_get($statusCols[$code]), + 'percentage' => "[%]" + ); + + $data_tpl[$codeToStatus[$code]] = array( + 'qty' => 0, + 'percentage' => 0 + ); +} + +// foreach ($gui->platformSet as $plat_id => $plat_name) { +$sql = "SELECT context_id,BLDT.id AS detail_id,testplan_id,platform_id," . + " begin_exec_ts, end_exec_ts, creation_ts," . + " top_tsuite_id, child_tsuite_id, status, qty, total_tc," . + " TS_TOP.name AS top_name, TS_CHI.name AS child_name " . + " FROM {$tables['baseline_l1l2_context']} BLC " . + " JOIN {$tables['baseline_l1l2_details']} BLDT " . + " ON BLDT.context_id = BLC.id " . + " JOIN {$tables['nodes_hierarchy']} AS TS_TOP " . + " ON TS_TOP.id = top_tsuite_id " . + " JOIN {$tables['nodes_hierarchy']} AS TS_CHI " . + " ON TS_CHI.id = child_tsuite_id " . + " WHERE BLC.testplan_id = $args->tplan_id " . + " ORDER BY BLC.creation_ts DESC, top_name ASC, child_name ASC"; + +$keyCols = array( + 'platform_id', + 'context_id', + 'top_tsuite_id', + 'child_tsuite_id' +); +$rsu = $db->fetchRowsIntoMap4l($sql, $keyCols, true); + +// Generate statistics for each platform +// Platforms are ordered by name +if (! empty($rsu)) { + foreach ($rsu as $plat_id => $dataByContext) { + $gui->statistics = array(); + + $gui->statistics[$plat_id] = array(); + $gui->span[$plat_id] = array(); + + $rx = 0; + foreach ($dataByContext as $dataByTop) { + $gui->statistics[$plat_id][$rx] = array(); + $gui->span[$plat_id][$rx] = null; + + $rrr = current(current($dataByTop))[0]; + reset($dataByTop); + $gui->span[$plat_id][$rx] = array( + 'begin' => $rrr['begin_exec_ts'], + 'end' => $rrr['end_exec_ts'], + 'baseline_ts' => $rrr['creation_ts'] + ); + + foreach ($dataByTop as $top_id => $dataByChild) { + foreach ($dataByChild as $child_id => $dataX) { + $gui->statistics[$plat_id][$rx][$child_id] = array(); + $hand = &$gui->statistics[$plat_id][$rx][$child_id]; + + $dfx = $dataX[0]; + $hand['name'] = $dfx['top_name'] . ':' . $dfx['child_name']; + $hand['total_tc'] = $dfx['total_tc']; + $hand['percentage_completed'] = - 1; + $hand['details'] = $data_tpl; + $hand['parent_id'] = $top_id; + + foreach ($dataX as $xmen) { + $pp = ($hand['total_tc'] > 0) ? (round( + ($xmen['qty'] / $hand['total_tc']) * 100, 1)) : 0; + $hand['details'][$codeToStatus[$xmen['status']]] = array( + 'qty' => $xmen['qty'], + 'percentage' => $pp + ); + } + + // Calculate percentage completed, using all exec status + // other than not run + if ($hand['total_tc'] > 0) { + $hand['percentage_completed'] = $hand['total_tc'] - + $hand['details']['not_run']['qty']; + $hand['percentage_completed'] = round( + ($hand['percentage_completed'] / $hand['total_tc']) * + 100, 1); + } + } + } + $rx ++; + } + } +} + +/* + * array(1) { + * [187]=> + * array(10) { + * [33984]=> + * array(6) { + * ["type"]=> string(6) "tsuite" + * ["name"]=> + * string(55) "PT/08/TMS/Costing:PT/08.01/Price List & Rate Management" + * ["parent_id"]=> string(5) "33953" + * ["total_tc"]=> int(218) + * ["percentage_completed"]=> string(4) "95.4" + * ["details"]=> + * array(4) { + * ["not_run"]=> array(2) {["qty"]=>int(10) + * ["percentage"]=> string(3) "4.6"} + * ["passed"]=> + * } + * } + */ + +$timerOff = microtime(true); +$gui->elapsed_time = round($timerOff - $timerOn, 2); + +if ($args->spreadsheet) { + createSpreadsheet($gui, $tplan_mgr); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +displayReport($tplCfg->tpl, $smarty, $args->format, $mailCfg); + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @return array + */ +function initializeGui(&$dbHandler, $argsObj, &$tplanMgr) +{ + list (, $gui) = initUserEnv($dbHandler, $argsObj); + + $gui->fakePlatform = array( + '' + ); + $gui->title = lang_get('baseline_l1l2'); + $gui->do_report = array(); + $gui->showPlatforms = true; + $gui->columnsDefinition = new stdClass(); + $gui->statistics = new stdClass(); + $gui->elapsed_time = 0; + $gui->displayBuildMetrics = false; + $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); + + $gui->tproject_name = testproject::getName($dbHandler, $argsObj->tproject_id); + + $info = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->tplan_name = $info['name']; + $gui->tplan_id = intval($argsObj->tplan_id); + + $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'map' + )); + if (is_null($gui->platformSet)) { + $gui->platformSet = array( + '' + ); + $gui->showPlatforms = false; + } else { + natsort($gui->platformSet); + } + + $gui->hasPlatforms = count($gui->platformSet) >= 1 && + ! isset($gui->platformSet[0]); + + return $gui; +} + +/** + * + * @param stdClass $gui + * @param tlTestPlanMetrics $tplanMgr + */ +function createSpreadsheet($gui, &$tplanMgr) +{ + + // N sections + // Always same format + // Platform + // Build Assigned Not Run [%] Passed [%] Failed [%] Blocked [%] + // Completed [%] + + // Results by Platform + // Overall Build Status + // Results by Build + // Results by Top Level Test Suite + // Results by priority + // Results by Keyword + $lbl = initLblSpreadsheet(); + $cellRange = setCellRangeSpreadsheet(); + $style = initStyleSpreadsheet(); + + // Common + $execStatusDomain = $tplanMgr->getStatusForReports(); + $dataHeaderMetrics = array(); + $ccc = 0; + foreach ($execStatusDomain as $human) { + $dataHeaderMetrics[] = lang_get('test_status_' . $human); + $ccc ++; + $dataHeaderMetrics[] = '[%]'; + $ccc ++; + } + $dataHeaderMetrics[] = $lbl['completed_perc']; + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + + $oneLevel = array(); + + // NO PLATFORM => ID=0 + if ($gui->hasPlatforms) { + $oneLevel[] = array( + 'entity' => 'platform', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->platform + ); + } + + $oneLevel[] = array( + 'entity' => 'build', + 'dimension' => 'testcase_qty', + 'nameKey' => 'build_name', + 'tcQtyKey' => 'total_assigned', + 'source' => &$gui->statistics->overallBuildStatus + ); + + $startingRow = count($lines2write); // MAGIC + foreach ($oneLevel as $target) { + $entity = $target['entity']; + $dimension = $target['dimension']; + $dataHeader = array( + $lbl[$entity], + $lbl[$dimension] + ); + + // intermediate column qty is dynamic because it depends + // of status configuration. + foreach ($dataHeaderMetrics as $val) { + $dataHeader[] = $val; + } + + $startingRow ++; + $startingRow ++; + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + $startingRow ++; + $infoSet = $target['source']; + $nameKey = $target['nameKey']; + $tcQtyKey = $target['tcQtyKey']; + + foreach ($infoSet as $fieldSet) { + + $whatCell = 0; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$nameKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$tcQtyKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + foreach ($fieldSet['details'] as $metrics) { + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['qty']); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['percentage']); + } + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $fieldSet['percentage_completed']); + $startingRow ++; + } + } + + // The first column will be the platform + $twoLevels = array(); + + if ($gui->hasPlatforms) { + $twoLevels[] = array( + 'entity' => 'build', + 'dimension' => 'testcase_qty', + 'nameKey' => 'build_name', + 'tcQtyKey' => 'total_assigned', + 'source' => &$gui->statistics->buildByPlatMetrics + ); + } + + $twoLevels[] = array( + 'entity' => 'testsuite', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->testsuites + ); + + $twoLevels[] = array( + 'entity' => 'priority', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->priorities + ); + + $twoLevels[] = array( + 'entity' => 'keyword', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->keywords + ); + + foreach ($twoLevels as $target) { + $startingRow ++; + $startingRow ++; + + $entity = $target['entity']; + $dimension = $target['dimension']; + $nameKey = $target['nameKey']; + $tcQtyKey = $target['tcQtyKey']; + + if (count($target['source']) == 0) { + continue; + } + + // Just ONE HEADER ? + $dataHeader = array( + $lbl['platform'], + $lbl[$entity], + $lbl[$dimension] + ); + if (! $gui->hasPlatforms) { + array_shift($dataHeader); + } + + // intermediate column qty is dynamic because it depends + // of status configuration. + foreach ($dataHeaderMetrics as $val) { + $dataHeader[] = $val; + } + + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + // END ONE HEADER + $startingRow ++; + + $idr = ''; + foreach ($gui->platformSet as $platID => $platName) { + $idr = ('' == $idr || 'rowB' == $idr) ? 'rowA' : 'rowB'; + + $infoSet = isset($target['source'][$platID]) ? $target['source'][$platID] : array(); + + foreach ($infoSet as $fieldSet) { + $whatCell = 0; + + if ($gui->hasPlatforms) { + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $platName; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + $whatCell ++; + } + + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$nameKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$tcQtyKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + foreach ($fieldSet['details'] as $metrics) { + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['qty']); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['percentage']); + } + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $fieldSet['percentage_completed']); + + $cellZone = "A{$startingRow}:" . $cellRange[$whatCell] . + "$startingRow"; + + $objPHPExcel->getActiveSheet() + ->getStyle($cellZone) + ->applyFromArray($style[$idr]); + + $startingRow ++; + } + } + } + + // Just to add some final empty row + $cellID = $cellRange[0] . $startingRow; + $field = ''; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + // Final step + $tmpfname = tempnam(config_get('temp_dir'), "TestLink_GTMP.tmp"); + $objPHPExcel->setActiveSheetIndex(0); + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + $objWriter->save($tmpfname); + + downloadXls($tmpfname, $xlsType, $gui, 'TestLink_GTMP_'); +} + +/** + */ +function xlsStepOne(&$oj, $style, &$lbl, &$gui) +{ + $dummy = ''; + $lines2write = array( + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + + $cellArea .= "A{$cdx}"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; +} + +/** + * + * @return array + */ +function initLblSpreadsheet() +{ + return init_labels( + array( + 'testsuite' => null, + 'testcase_qty' => null, + 'keyword' => null, + 'platform' => null, + 'priority' => null, + 'priority_level' => null, + 'build' => null, + 'testplan' => null, + 'testproject' => null, + 'not_run' => null, + 'completed_perc' => 'trep_comp_perc', + 'generated_by_TestLink_on' => null + )); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $style = array(); + $style['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $style['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + + $style['rowA'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FFFFFFFF' + ) + ) + ); + + $style['rowB'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'DCDCDCDC' + ) + ) + ); + + return $style; +} + +/** + * + * @return string|array + */ +function setCellRangeSpreadsheet() +{ + $cr = range('A', 'Z'); + $crLen = count($cr); + for ($idx = 0; $idx < $crLen; $idx ++) { + for ($jdx = 0; $jdx < $crLen; $jdx ++) { + $cr[] = $cr[$idx] . $cr[$jdx]; + } + } + return $cr; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); } diff --git a/lib/results/charts.inc.php b/lib/results/charts.inc.php index 6e12a2e0f6..91daf7d632 100644 --- a/lib/results/charts.inc.php +++ b/lib/results/charts.inc.php @@ -1,138 +1,155 @@ - 255, 'G' => 255, 'B' => 254); - $chartCfg=new stdClass(); - $chartCfg->XSize = $info->canDraw ? $cfg->XSize : 600; - $chartCfg->YSize = $info->canDraw ? $cfg->YSize : 50; - - $chartCfg->border = new stdClass(); - $chartCfg->border->width = 1; - $chartCfg->border->color = array('R' => 0, 'G' => 0, 'B' => 0); - - $chartCfg->graphArea = new stdClass(); - $chartCfg->graphArea->color=array('R' => 213, 'G' => 217, 'B' => 221); - - $chartCfg->graphArea->beginX = property_exists($cfg,'beginX') ? $cfg->beginX : 40; - $chartCfg->graphArea->beginY = property_exists($cfg,'beginY') ? $cfg->beginY : 100; - - $chartCfg->graphArea->endX = $chartCfg->XSize - $chartCfg->graphArea->beginX; - $chartCfg->graphArea->endY = $chartCfg->YSize - $chartCfg->graphArea->beginY; - - $chartCfg->scale=new stdClass(); - - // 20100914 - franciscom - // After reading documentation - // drawScale - // Today there is four way of computing scales : - // - // - Getting Max & Min values per serie : ScaleMode = SCALE_NORMAL - // - Like the previous one but setting the min value to 0 : ScaleMode = SCALE_START0 - // - Getting the series cumulative Max & Min values : ScaleMode = SCALE_ADDALL - // - Like the previous one but setting the min value to 0 : ScaleMode = SCALE_ADDALLSTART0 - // - // This will depends on the kind of graph you are drawing, today only the stacked bar chart - // can use the SCALE_ADDALL mode. - // Drawing graphs were you want to fix the min value to 0 you must use the SCALE_START0 option. - // - $chartCfg->scale->mode = SCALE_ADDALLSTART0; - $chartCfg->scale->color = array('R' => 0, 'G' => 0, 'B' => 0); - $chartCfg->scale->drawTicks = TRUE; - $chartCfg->scale->angle=$cfg->scale->legendXAngle; - $chartCfg->scale->decimals=1; - $chartCfg->scale->withMargin=TRUE; - - $chartCfg->legend=new stdClass(); - $chartCfg->legend->X=$chartCfg->XSize-80; - $chartCfg->legend->Y=15; - $chartCfg->legend->color=array('R' => 236, 'G' => 238, 'B' => 240); - - $chartCfg->title=new stdClass(); - $chartCfg->title->value=$cfg->chartTitle; - - $chartCfg->title->X=2*$chartCfg->graphArea->beginX; - $chartCfg->title->Y=$chartCfg->legend->Y; - $chartCfg->title->color=array('R' => 0, 'G' => 0, 'B' => 255); - - $Test = new pChart($chartCfg->XSize,$chartCfg->YSize); - $Test->reportWarnings("GD"); - $Test->drawBackground($backgndColor['R'],$backgndColor['G'],$backgndColor['B']); - $Test->drawGraphArea($chartCfg->graphArea->color['R'], - $chartCfg->graphArea->color['G'],$chartCfg->graphArea->color['B']); - $Test->setGraphArea($chartCfg->graphArea->beginX,$chartCfg->graphArea->beginY, - $chartCfg->graphArea->endX,$chartCfg->graphArea->endY); - - $Test->setFontProperties(config_get('charts_font_path'),config_get('charts_font_size')); - - if($info->canDraw) - { - $DataSet = new pData; - foreach($info->chart_data as $key => $values) - { - $id=$key+1; - $DataSet->AddPoint($values,"Serie{$id}"); - $DataSet->SetSerieName($info->series_label[$key],"Serie{$id}"); - - } - $DataSet->AddPoint($info->xAxis->values,$info->xAxis->serieName); - $DataSet->AddAllSeries(); - $DataSet->RemoveSerie($info->xAxis->serieName); - $DataSet->SetAbsciseLabelSerie($info->xAxis->serieName); - $chartData=$DataSet->GetData(); - $chartLegend=$DataSet->GetDataDescription(); - - - foreach( $info->series_color as $key => $hexrgb) - { - $rgb=str_split($hexrgb,2); - $Test->setColorPalette($key,hexdec($rgb[0]),hexdec($rgb[1]),hexdec($rgb[2])); - } - // $Test->setFixedScale($info->scale->minY,$info->scale->maxY,$info->scale->divisions); - $Test->drawScale($chartData,$chartLegend,$chartCfg->scale->mode, - $chartCfg->scale->color['R'],$chartCfg->scale->color['G'],$chartCfg->scale->color['B'], - $chartCfg->scale->drawTicks,$chartCfg->scale->angle,$chartCfg->scale->decimals, - $chartCfg->scale->withMargin); - - $Test->drawStackedBarGraph($chartData,$chartLegend,70); - - // Draw the legend - $Test->setFontProperties(config_get('charts_font_path'),config_get('charts_font_size')); - $Test->drawLegend($chartCfg->legend->X,$chartCfg->legend->Y,$chartLegend, - $chartCfg->legend->color['R'],$chartCfg->legend->color['G'], - $chartCfg->legend->color['B']); - - $Test->addBorder($chartCfg->border->width, - $chartCfg->border->color['R'],$chartCfg->border->color['G'], - $chartCfg->border->color['B']); - } - else - { - $chartCfg->title->value .= '/' . lang_get('no_data_available'); - } - - $Test->drawTitle($chartCfg->title->X,$chartCfg->title->Y,$chartCfg->title->value, - $chartCfg->title->color['R'],$chartCfg->title->color['G'],$chartCfg->title->color['B']); - $Test->Stroke(); -} + 255, + 'G' => 255, + 'B' => 254 + ); + $chartCfg = new stdClass(); + $chartCfg->XSize = $info->canDraw ? $cfg->XSize : 600; + $chartCfg->YSize = $info->canDraw ? $cfg->YSize : 50; + + $chartCfg->border = new stdClass(); + $chartCfg->border->width = 1; + $chartCfg->border->color = array( + 'R' => 0, + 'G' => 0, + 'B' => 0 + ); + + $chartCfg->graphArea = new stdClass(); + $chartCfg->graphArea->color = array( + 'R' => 213, + 'G' => 217, + 'B' => 221 + ); + + $chartCfg->graphArea->beginX = property_exists($cfg, 'beginX') ? $cfg->beginX : 40; + $chartCfg->graphArea->beginY = property_exists($cfg, 'beginY') ? $cfg->beginY : 100; + + $chartCfg->graphArea->endX = $chartCfg->XSize - $chartCfg->graphArea->beginX; + $chartCfg->graphArea->endY = $chartCfg->YSize - $chartCfg->graphArea->beginY; + + $chartCfg->scale = new stdClass(); + + // 20100914 - franciscom + // After reading documentation + // drawScale + // Today there is four way of computing scales : + // + // - Getting Max & Min values per serie : ScaleMode = SCALE_NORMAL + // - Like the previous one but setting the min value to 0 : ScaleMode = SCALE_START0 + // - Getting the series cumulative Max & Min values : ScaleMode = SCALE_ADDALL + // - Like the previous one but setting the min value to 0 : ScaleMode = SCALE_ADDALLSTART0 + // + // This will depends on the kind of graph you are drawing, today only the stacked bar chart + // can use the SCALE_ADDALL mode. + // Drawing graphs were you want to fix the min value to 0 you must use the SCALE_START0 option. + // + $chartCfg->scale->mode = SCALE_ADDALLSTART0; + $chartCfg->scale->color = array( + 'R' => 0, + 'G' => 0, + 'B' => 0 + ); + $chartCfg->scale->drawTicks = true; + $chartCfg->scale->angle = $cfg->scale->legendXAngle; + $chartCfg->scale->decimals = 1; + $chartCfg->scale->withMargin = true; + + $chartCfg->legend = new stdClass(); + $chartCfg->legend->X = 15; + $chartCfg->legend->Y = 20; + $chartCfg->legend->color = array( + 'R' => 236, + 'G' => 238, + 'B' => 240 + ); + + $chartCfg->title = new stdClass(); + $chartCfg->title->value = $cfg->chartTitle; + $chartCfg->title->X = ($chartCfg->XSize / 2) - + (strlen($chartCfg->title->value) * 2.5); + $chartCfg->title->Y = 15; + $chartCfg->title->color = array( + 'R' => 0, + 'G' => 0, + 'B' => 255 + ); + + $test = new pChart($chartCfg->XSize, $chartCfg->YSize); + $test->drawBackground($backgndColor['R'], $backgndColor['G'], + $backgndColor['B']); + $test->drawGraphArea($chartCfg->graphArea->color['R'], + $chartCfg->graphArea->color['G'], $chartCfg->graphArea->color['B']); + $test->setGraphArea($chartCfg->graphArea->beginX, + $chartCfg->graphArea->beginY, $chartCfg->graphArea->endX, + $chartCfg->graphArea->endY); + + $test->setFontProperties(config_get('charts_font_path'), + config_get('charts_font_size')); + + if ($info->canDraw) { + $dataSet = new pData(); + foreach ($info->chart_data as $key => $values) { + $id = $key + 1; + $dataSet->AddPoint($values, "Serie{$id}"); + $dataSet->SetSerieName($info->series_label[$key], "Serie{$id}"); + } + $dataSet->AddPoint($info->xAxis->values, $info->xAxis->serieName); + $dataSet->AddAllSeries(); + $dataSet->RemoveSerie($info->xAxis->serieName); + $dataSet->SetAbsciseLabelSerie($info->xAxis->serieName); + $chartData = $dataSet->GetData(); + $chartLegend = $dataSet->GetDataDescription(); + + foreach ($info->series_color as $key => $hexrgb) { + $rgb = str_split($hexrgb, 2); + $test->setColorPalette($key, hexdec($rgb[0]), hexdec($rgb[1]), + hexdec($rgb[2])); + } + $test->drawScale($chartData, $chartLegend, $chartCfg->scale->mode, + $chartCfg->scale->color['R'], $chartCfg->scale->color['G'], + $chartCfg->scale->color['B'], $chartCfg->scale->drawTicks, + $chartCfg->scale->angle, $chartCfg->scale->decimals, + $chartCfg->scale->withMargin); + $test->drawStackedBarGraph($chartData, $chartLegend, 70); + + // Draw the legend + $test->setFontProperties(config_get('charts_font_path'), + config_get('charts_font_size')); + $test->drawLegend($chartCfg->legend->X, $chartCfg->legend->Y, + $chartLegend, $chartCfg->legend->color['R'], + $chartCfg->legend->color['G'], $chartCfg->legend->color['B']); + + $test->addBorder($chartCfg->border->width, $chartCfg->border->color['R'], + $chartCfg->border->color['G'], $chartCfg->border->color['B']); + } else { + $chartCfg->title->value .= '/' . lang_get('no_data_available'); + } + + $test->drawTitle($chartCfg->title->X, $chartCfg->title->Y, + $chartCfg->title->value, $chartCfg->title->color['R'], + $chartCfg->title->color['G'], $chartCfg->title->color['B']); + $test->Stroke(); +} ?> diff --git a/lib/results/charts.php b/lib/results/charts.php index d24f8c5b09..520118b79c 100644 --- a/lib/results/charts.php +++ b/lib/results/charts.php @@ -1,158 +1,184 @@ - null,'overall_metrics_for_platform' => null, - 'results_by_keyword' => null,'results_top_level_suites' => null)); - -list($args,$tproject_mgr,$tplan_mgr) = init_args($db); - -$tplan_info = $tplan_mgr->get_by_id($args->tplan_id); -$tproject_info = $tproject_mgr->get_by_id($args->tproject_id); - -$gui = initializeGui($args); -if($gui->can_use_charts == 'OK') -{ - $gui->tplan_name = $tplan_info['name']; - $gui->tproject_name = $tproject_info['name']; - - $resultsCfg = config_get('results'); - - $pathToScripts = "lib/results/"; - $chartsUrl=new stdClass(); - $chartsUrl->overallPieChart = $pathToScripts . "overallPieChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}"; - $chartsUrl->keywordBarChart = $pathToScripts . "keywordBarChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}" . - "&tproject_id=$args->tproject_id"; - $chartsUrl->topLevelSuitesBarChart = $pathToScripts . - "topLevelSuitesBarChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}" . - "&tproject_id=$args->tproject_id"; - - $platformSet = $tplan_mgr->getPlatforms($gui->tplan_id,array('outputFormat' => 'map')); - $platformIDSet = is_null($platformSet) ? array(0) : array_keys($platformSet); - - $gui->charts = array($l18n['overall_metrics'] => $chartsUrl->overallPieChart); - if(!is_null($platformSet)) - { - foreach($platformIDSet as $platform_id) - { - $description = $l18n['overall_metrics_for_platform'] . ' ' . $platformSet[$platform_id]; - $gui->charts[$description] = $pathToScripts . - "platformPieChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}&platform_id={$platform_id}"; - } - } - - $gui->charts = array_merge( $gui->charts, - array($l18n['results_by_keyword'] => $chartsUrl->keywordBarChart, - $l18n['results_top_level_suites'] => $chartsUrl->topLevelSuitesBarChart) ); -} - -$smarty = new TLSmarty(); -$smarty->assign("gui",$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - - -/** - * initialize user input - * - * @param resource dbHandler - * @return array $args array with user input information - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,0,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - if( !is_null($args->apikey) ) - { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - $args->addOpAccess = false; - $cerbero->method = null; - $cerbero->args->getAccessAttr = false; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } - else - { - testlinkInitPage($dbHandler,false,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - $tproject_mgr = new testproject($dbHandler); - $tplan_mgr = new testplan($dbHandler); - if($args->tproject_id > 0) - { - $args->tproject_info = $tproject_mgr->get_by_id($args->tproject_id); - $args->tproject_name = $args->tproject_info['name']; - $args->tproject_description = $args->tproject_info['notes']; - } - - if ($args->tplan_id > 0) - { - $args->tplan_info = $tplan_mgr->get_by_id($args->tplan_id); - } - - return array($args,$tproject_mgr,$tplan_mgr); + null, + 'overall_metrics_for_platform' => null, + 'results_by_keyword' => null, + 'results_top_level_suites' => null + )); + +list ($args, $tproject_mgr, $tplan_mgr) = initArgs($db); + +$tplan_info = $tplan_mgr->get_by_id($args->tplan_id); +$tproject_info = $tproject_mgr->get_by_id($args->tproject_id); + +$gui = initializeGui($args); +if ($gui->can_use_charts == 'OK') { + $gui->tplan_name = $tplan_info['name']; + $gui->tproject_name = $tproject_info['name']; + + $resultsCfg = config_get('results'); + + $pathToScripts = "lib/results/"; + $chartsUrl = new stdClass(); + $chartsUrl->overallPieChart = $pathToScripts . + "overallPieChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}"; + $chartsUrl->keywordBarChart = $pathToScripts . + "keywordBarChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}" . + "&tproject_id=$args->tproject_id"; + $chartsUrl->topLevelSuitesBarChart = $pathToScripts . + "topLevelSuitesBarChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}" . + "&tproject_id=$args->tproject_id"; + + $platformSet = $tplan_mgr->getPlatforms($gui->tplan_id, + array( + 'outputFormat' => 'map' + )); + $platformIDSet = is_null($platformSet) ? array( + 0 + ) : array_keys($platformSet); + + $gui->charts = array( + $l18n['overall_metrics'] => $chartsUrl->overallPieChart + ); + if (! is_null($platformSet)) { + foreach ($platformIDSet as $platform_id) { + $description = $l18n['overall_metrics_for_platform'] . ' ' . + $platformSet[$platform_id]; + $gui->charts[$description] = $pathToScripts . + "platformPieChart.php?apikey={$args->apikey}&tplan_id={$gui->tplan_id}&platform_id={$platform_id}"; + } + } + + $gui->charts = array_merge($gui->charts, + array( + $l18n['results_by_keyword'] => $chartsUrl->keywordBarChart, + $l18n['results_top_level_suites'] => $chartsUrl->topLevelSuitesBarChart + )); +} + +$smarty = new TLSmarty(); +$smarty->assign("gui", $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * initialize user input + * + * @param + * database dbHandler + * @return array $args array with user input information + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 0, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + $cerbero->args->getAccessAttr = false; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + $tproject_mgr = new testproject($dbHandler); + $tplan_mgr = new testplan($dbHandler); + if ($args->tproject_id > 0) { + $args->tproject_info = $tproject_mgr->get_by_id($args->tproject_id); + $args->tproject_name = $args->tproject_info['name']; + $args->tproject_description = $args->tproject_info['notes']; + } + + if ($args->tplan_id > 0) { + $args->tplan_info = $tplan_mgr->get_by_id($args->tplan_id); + } + + return array( + $args, + $tproject_mgr, + $tplan_mgr + ); +} + +/** + * + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui($argsObj) +{ + $gui = new stdClass(); + $gui->tplan_id = $argsObj->tplan_id; + $gui->can_use_charts = checkLibGd(); + return $gui; +} + +/** + * rights check function for testlinkInitPage() + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); } - -/** - * - */ -function initializeGui($argsObj) -{ - $gui=new stdClass(); - $gui->tplan_id = $argsObj->tplan_id; - $gui->can_use_charts = checkLibGd(); - return $gui; -} - - - -/* - * rights check function for testlinkInitPage() - */ -function checkRights(&$db,&$user,$context = null) -{ - if(is_null($context)) - { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; -} \ No newline at end of file diff --git a/lib/results/displayMgr.php b/lib/results/displayMgr.php index 6d76b3416f..d00a8cd2d3 100644 --- a/lib/results/displayMgr.php +++ b/lib/results/displayMgr.php @@ -1,238 +1,267 @@ - array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N), - "type" => array(tlInputParameter::STRING_N,0,1), - "sendByMail" => array(tlInputParameter::INT_N), - "spreadsheet" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,5,15), - "platSet" => array(tlInputParameter::ARRAY_INT), - "build_set" => array(tlInputParameter::ARRAY_INT), - "buildListForExcel" => array(tlInputParameter::STRING_N,0,100)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $rCfg = config_get('results'); - $args->statusCode = $rCfg['status_code']; - - $args->spreadsheet = intval($args->spreadsheet); - $args->accessType = 'gui'; - $args->addOpAccess = true; - - $args->getSpreadsheetBy = - isset($_REQUEST['sendSpreadSheetByMail_x']) ? 'email' : null; - - if( is_null($args->getSpreadsheetBy) ) { - $args->getSpreadsheetBy = isset($_REQUEST['exportSpreadSheet_x']) ? - 'download' : null; - } - - if( !is_null($args->apikey) ) { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - $args->accessType = 'remote'; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } else { - $args->addOpAccess = false; - $cerbero->method = null; - $args->accessType = 'anonymous'; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } else { - testlinkInitPage($dbHandler,true,false,"checkRights"); - - $tplanMgr = new testplan($dbHandler); - $tplan = $tplanMgr->get_by_id($args->tplan_id); - $args->tproject_id = $tplan['testproject_id']; - } - - if ($args->tproject_id <= 0) { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid Test Project ID ({$args->tproject_id})"; - throw new Exception($msg); - } - - if (is_null($args->format)) { - tlog("Parameter 'format' is not defined", 'ERROR'); - exit(); - } - - switch ($args->format) { - case FORMAT_XLS: - if($args->buildListForExcel != '') { - $args->build_set = explode(',',$args->buildListForExcel); - } - break; - } - - $args->format = $args->sendByMail ? FORMAT_MAIL_HTML : $args->format; - - $args->user = $_SESSION['currentUser']; - $args->basehref = $_SESSION['basehref']; - - return array($tplanMgr,$args); -} - - -/** - * - * - */ -function generateHtmlEmail(&$smarty, $template_file, $mailCfg) { - // same objet that is returned by email_send - $op = new stdClass(); - $op->status_ok = true; - $op->msg = 'ok'; - - $html_report = $smarty->fetch($template_file); - if( ! property_exists($mailCfg,'from') ) { - $mailCfg->from = $_SESSION['currentUser']->emailAddress; - } - - if( ! property_exists($mailCfg,'to') ) { - $mailCfg->to = $mailCfg->from; - } - - if($mailCfg->to == ""){ - $op->status_ok = false; - $op->msg = lang_get("error_sendreport_no_email_credentials"); - } else { - // Link to test case is still raw link (no title) in email(HTML) type of test report - $op = email_send( $mailCfg->from, $mailCfg->to, $mailCfg->subject, - $html_report, $mailCfg->cc, null,false,true, - array('strip_email_links' => false)); - - if($op->status_ok) { - $op->msg = sprintf(lang_get('mail_sent_to'), $mailCfg->to); - } - } - return $op; -} - - -/** - * - * - */ -function displayReport($template_file, &$smarty, $doc_format, $mailCfg = null) -{ - - $doc_format = intval($doc_format); - switch($doc_format) - { - case FORMAT_HTML: - case FORMAT_ODT: - case FORMAT_ODS: - case FORMAT_XLS: - case FORMAT_MSWORD: - case FORMAT_PDF: - flushHttpHeader($doc_format, $doc_kind = 0); - break; - - case FORMAT_MAIL_HTML: - $op = generateHtmlEmail($smarty, $template_file, $mailCfg); - - switch($template_file) - { - case 'results/resultsGeneral.tpl'; - flushHttpHeader(FORMAT_HTML, $doc_kind = 0); - $mf->msg = $op->status_ok ? '' : lang_get('send_mail_ko'); - $mf->msg .= ' ' . $op->msg; - $mf->title = ''; //$mailCfg->subject; - $smarty->assign('mailFeedBack',$mf); - break; - - default: - $message = $op->status_ok ? '' : lang_get('send_mail_ko'); - $smarty = new TLSmarty(); - $smarty->assign('message', $message . ' ' . $op->msg); - $smarty->assign('title', $mailCfg->subject); - $template_file = "emailSent.tpl"; - break; - } - break; - } - - $smarty->display($template_file); -} - - -/** - * Generate HTML header and send it to browser - * @param string $format identifier of document format; value must be in $tlCfg->reports_formats - * @param integer $doc_kind Magic number of document kind; see consts.inc.php for list - * (for example: DOC_TEST_PLAN_DESIGN) - * @author havlatm - */ -function flushHttpHeader($format, $doc_kind = 0) -{ - $file_extensions = config_get('reports_file_extension'); - $reports_applications = config_get('reports_applications'); - - switch($doc_kind) { - case DOC_TEST_SPEC: - $kind_acronym = '_test_spec'; - break; - - case DOC_TEST_PLAN_DESIGN: - $kind_acronym = '_test_plan'; - break; - - case DOC_TEST_PLAN_EXECUTION: - $kind_acronym = '_test_report'; - break; - - case DOC_REQ_SPEC: - $kind_acronym = '_req_spec'; - break; - - default: - $kind_acronym = ''; - break; - } - - - if ($format == FORMAT_MAIL_HTML) { - tLog('flushHttpHeader> Invalid format: '.$format, 'ERROR'); - } - - $filename = isset($_SESSION['testprojectPrefix']) ? $_SESSION['testprojectPrefix'] : ''; - $filename .= $kind_acronym . '-' . date('Y-m-d') . '.' . $file_extensions[$format]; - tLog('Flush HTTP header for '.$format); - - - $contentType = isset($reports_applications[$format]) ? $reports_applications[$format] : 'text/html'; - $contentType .= (is_null($format) || $format=='') ? '' : ("; name='Testlink_" . $format ."'") ; - header("Content-type: {$contentType}"); - header("Content-Description: TestLink - Generated Document (see " . __FUNCTION__ . ")" ); - if( (!is_null($format) && $format != '') && $format != FORMAT_HTML ) - { - header("Content-Disposition: attachment; filename=$filename"); - } - flush(); + array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N + ), + "type" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "sendByMail" => array( + tlInputParameter::INT_N + ), + "spreadsheet" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 5, + 15 + ), + "platSet" => array( + tlInputParameter::ARRAY_INT + ), + "build_set" => array( + tlInputParameter::ARRAY_INT + ), + "buildListForExcel" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $rCfg = config_get('results'); + $args->statusCode = $rCfg['status_code']; + + $args->spreadsheet = intval($args->spreadsheet); + $args->accessType = 'gui'; + $args->addOpAccess = true; + + $args->getSpreadsheetBy = isset($_REQUEST['sendSpreadSheetByMail_x']) ? 'email' : null; + + if (is_null($args->getSpreadsheetBy)) { + $args->getSpreadsheetBy = isset($_REQUEST['exportSpreadSheet_x']) ? 'download' : null; + } + + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + $args->accessType = 'remote'; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + $args->accessType = 'anonymous'; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, true, false, "checkRights"); + + $tplanMgr = new testplan($dbHandler); + $tplan = $tplanMgr->get_by_id($args->tplan_id); + $args->tproject_id = $tplan['testproject_id']; + } + + if ($args->tproject_id <= 0) { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid Test Project ID ({$args->tproject_id})"; + throw new Exception($msg); + } + + if (is_null($args->format)) { + tlog("Parameter 'format' is not defined", 'ERROR'); + exit(); + } + + switch ($args->format) { + case FORMAT_XLS: + if ($args->buildListForExcel != '') { + $args->build_set = explode(',', $args->buildListForExcel); + } + break; + } + + $args->format = $args->sendByMail ? FORMAT_MAIL_HTML : $args->format; + + $args->user = $_SESSION['currentUser']; + $args->basehref = $_SESSION['basehref']; + + return array( + $tplanMgr, + $args + ); +} + +/** + */ +function generateHtmlEmail(&$smarty, $template_file, $mailCfg) +{ + // same objet that is returned by email_send + $op = new stdClass(); + $op->status_ok = true; + $op->msg = 'ok'; + + $html_report = $smarty->fetch($template_file); + if (! property_exists($mailCfg, 'from')) { + $mailCfg->from = $_SESSION['currentUser']->emailAddress; + } + + if (! property_exists($mailCfg, 'to')) { + $mailCfg->to = $mailCfg->from; + } + + if ($mailCfg->to == "") { + $op->status_ok = false; + $op->msg = lang_get("error_sendreport_no_email_credentials"); + } else { + // Link to test case is still raw link (no title) in email(HTML) type of test report + $op = email_send($mailCfg->from, $mailCfg->to, $mailCfg->subject, + $html_report, $mailCfg->cc, null, false, true, + array( + 'strip_email_links' => false + )); + + if ($op->status_ok) { + $op->msg = sprintf(lang_get('mail_sent_to'), $mailCfg->to); + } + } + return $op; +} + +/** + */ +function displayReport($template_file, &$smarty, $doc_format, $mailCfg = null) +{ + $doc_format = intval($doc_format); + switch ($doc_format) { + case FORMAT_HTML: + case FORMAT_ODT: + case FORMAT_ODS: + case FORMAT_XLS: + case FORMAT_MSWORD: + case FORMAT_PDF: + flushHttpHeader($doc_format); + break; + + case FORMAT_MAIL_HTML: + $op = generateHtmlEmail($smarty, $template_file, $mailCfg); + + switch ($template_file) { + case 'results/resultsGeneral.tpl': + flushHttpHeader(FORMAT_HTML); + $mf->msg = $op->status_ok ? '' : lang_get('send_mail_ko'); + $mf->msg .= ' ' . $op->msg; + $mf->title = ''; + $smarty->assign('mailFeedBack', $mf); + break; + + default: + $message = $op->status_ok ? '' : lang_get('send_mail_ko'); + $smarty = new TLSmarty(); + $smarty->assign('message', $message . ' ' . $op->msg); + $smarty->assign('title', $mailCfg->subject); + $template_file = "emailSent.tpl"; + break; + } + break; + } + + $smarty->display($template_file); +} + +/** + * Generate HTML header and send it to browser + * + * @param string $format + * identifier of document format; value must be in $tlCfg->reports_formats + * @param integer $doc_kind + * Magic number of document kind; see consts.inc.php for list + * (for example: DOC_TEST_PLAN_DESIGN) + * @author havlatm + */ +function flushHttpHeader($format, $doc_kind = 0) +{ + $file_extensions = config_get('reports_file_extension'); + $reports_applications = config_get('reports_applications'); + + switch ($doc_kind) { + case DOC_TEST_SPEC: + $kind_acronym = '_test_spec'; + break; + + case DOC_TEST_PLAN_DESIGN: + $kind_acronym = '_test_plan'; + break; + + case DOC_TEST_PLAN_EXECUTION: + $kind_acronym = '_test_report'; + break; + + case DOC_REQ_SPEC: + $kind_acronym = '_req_spec'; + break; + + default: + $kind_acronym = ''; + break; + } + + if ($format == FORMAT_MAIL_HTML) { + tLog('flushHttpHeader> Invalid format: ' . $format, 'ERROR'); + } + + $filename = isset($_SESSION['testprojectPrefix']) ? $_SESSION['testprojectPrefix'] : ''; + $filename .= $kind_acronym . '-' . date('Y-m-d') . '.' . + $file_extensions[$format]; + tLog('Flush HTTP header for ' . $format); + + $contentType = isset($reports_applications[$format]) ? $reports_applications[$format] : 'text/html'; + $contentType .= (is_null($format) || $format == '') ? '' : ("; name='Testlink_" . + $format . "'"); + header("Content-type: {$contentType}"); + header( + "Content-Description: TestLink - Generated Document (see " . __FUNCTION__ . + ")"); + if ((! is_null($format) && $format != '') && $format != FORMAT_HTML) { + header("Content-Disposition: attachment; filename=$filename"); + } + flush(); } diff --git a/lib/results/execTimelineStats.php b/lib/results/execTimelineStats.php index df6bcd1c8d..8e930e95d4 100644 --- a/lib/results/execTimelineStats.php +++ b/lib/results/execTimelineStats.php @@ -1,348 +1,424 @@ - 'month', 'workforce' => true); -$statsBy['day'] = array('timeline' => 'day', 'workforce' => true); -$statsBy['day_hour'] = array('timeline' => 'day_hour', 'workforce' => true); - -$gui->statsBy = $statsBy; -$gui->group = $group = 'day'; -$stats = $mgr->getExecTimelineStats($args->tplan_id,null,$statsBy[$group]); - -if ($stats != null) { - $gui->do_report['status_ok'] = 1; - $gui->do_report['msg'] = ''; - $gui->statistics->exec = $stats[0]; - - if( !is_null($gui->statistics->exec) ) { - switch ($group) { - case 'day': - $gui->columnsDefinition->exec = - array(lang_get('qty'),lang_get('yyyy_mm_dd')); - break; - - case 'month': - $gui->columnsDefinition->exec = - array(lang_get('qty'),lang_get('yyyy_mm')); - break; - - case 'day_hour': - $gui->columnsDefinition->exec = - array(lang_get('qty'),lang_get('yyyy_mm_dd'),lang_get('hh')); - break; - } - - if ($statsBy[$group]['workforce']) { - $gui->columnsDefinition->exec[] = lang_get('testers_qty'); - } - } -} - -if( $args->spreadsheet ) { - createSpreadsheet($gui,$args,$tplan_mgr); -} - -$smarty = new TLSmarty; -$smarty->assign('gui', $gui); -displayReport($tplCfg->tpl, $smarty, $args->format,$mailCfg); - - - -/** - * - * - */ -function buildMailCfg(&$guiObj) { - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . - $labels['testproject'] . ' : ' . - $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * - */ -function initializeGui(&$dbHandler,$argsObj,&$tplanMgr) -{ - - $gui = new stdClass(); - $gui->tproject_id = $argsObj->tproject_id; - - if ($argsObj->accessType == 'gui') { - list($add2args,$gui) = initUserEnv($dbHandler,$argsObj); - } - - $gui->apikey = $argsObj->apikey; - $gui->accessType = $argsObj->accessType; - $gui->fakePlatform = array(''); - $gui->title = lang_get('execTimelineStats_report'); - $gui->do_report = array(); - $gui->showPlatforms=true; - $gui->columnsDefinition = new stdClass(); - $gui->columnsDefinition->keywords = null; - $gui->columnsDefinition->testers = null; - $gui->columnsDefinition->platform = null; - $gui->statistics = new stdClass(); - $gui->statistics->keywords = null; - $gui->statistics->testers = null; - $gui->statistics->milestones = null; - $gui->statistics->overalBuildStatus = null; - $gui->elapsed_time = 0; - $gui->displayBuildMetrics = false; - $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); - - $gui->tproject_name = testproject::getName($dbHandler,$argsObj->tproject_id); - - $info = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->tplan_name = $info['name']; - $gui->tplan_id = intval($argsObj->tplan_id); - - $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'map')); - if( is_null($gui->platformSet) ) { - $gui->platformSet = array(''); - $gui->showPlatforms = false; - } else { - natsort($gui->platformSet); - } - - $gui->basehref = $_SESSION['basehref']; - $gui->actionSendMail = $gui->basehref . - "lib/results/execTimelineStats.php?format=" . - FORMAT_MAIL_HTML . "&tplan_id={$gui->tplan_id}" . - "&tproject_id={$gui->tproject_id}"; - - $gui->actionSpreadsheet = $gui->basehref . - "lib/results/execTimelineStats.php?format=" . - FORMAT_XLS . "&tplan_id={$gui->tplan_id}&spreadsheet=1". - "&tproject_id={$gui->tproject_id}"; - - $gui->mailFeedBack = new stdClass(); - $gui->mailFeedBack->msg = ''; - return $gui; -} - - -/** - * - * - */ -function createSpreadsheet($gui,$args,&$tplanMgr) -{ - $lbl = initLblSpreadsheet(); - $cellRange = setCellRangeSpreadsheet(); - $style = initStyleSpreadsheet(); - - // Common - $dataHeaderMetrics = array(); - $dataHeaderMetrics[] = $lbl['qty_of_executions']; - switch ($gui->group) { - case 'day': - $dataHeaderMetrics[] = $lbl['yyyy_mm_dd']; - break; - - case 'month': - $dataHeaderMetrics[] = $lbl['yyyy_mm']; - break; - - case 'day_hour': - $dataHeaderMetrics[] = $lbl['yyyy_mm_dd']; - $dataHeaderMetrics[] = $lbl['hh']; - break; - } - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - $startingRow = count($lines2write); // MAGIC - $dataHeader = array(); - foreach( $dataHeaderMetrics as $val ) { - $dataHeader[] = $val; - } - - $startingRow++; - $startingRow++; - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - - $startingRow++; - - // 'The meat!!' - foreach ($gui->statistics->exec as $timestamp => $elem) { - $ldx = 0; - foreach ($elem as $field) { - $cellID = $cellRange[$ldx++] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - } - $startingRow++; - } - $startingRow++; - - // Just to add some final empty row - $cellID = $cellRange[0] . $startingRow; - $field = ''; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - // Final step - $tmpfname = tempnam(config_get('temp_dir'),"TL_ExecTimelineStats.tmp"); - $objPHPExcel->setActiveSheetIndex(0); - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - $objWriter->save($tmpfname); - - downloadXls($tmpfname,$xlsType,$gui,'TL_ExecTimelineStats_'); -} - - -/** - * - */ -function xlsStepOne(&$oj,$style,&$lbl,&$gui) { - $dummy = ''; - $lines2write = array(array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time()))); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0) - ->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['ReportContext']); - - return $lines2write; -} - -/** - * - */ -function initLblSpreadsheet() { - $lbl = init_labels( - array('qty' => null,'yyyy_mm_dd' => null, - 'qty_of_executions' => null, - 'yyyy_mm' => null, 'hh' => null, - 'platform' => null, - 'testplan' => null, - 'testproject' => null, - 'generated_by_TestLink_on' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() { - $style = array(); - $style['ReportContext'] = array('font' => array('bold' => true)); - $style['DataHeader'] = - array('font' => array('bold' => true), - 'borders' => - array('outline' => - array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - - $style['rowA'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FFFFFFFF')) - ); - - $style['rowB'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'DCDCDCDC')) - ); - - return $style; -} - -/** - * - */ -function setCellRangeSpreadsheet() { - $cr = range('A','Z'); - $crLen = count($cr); - for($idx = 0; $idx < $crLen; $idx++) { - for($jdx = 0; $jdx < $crLen; $jdx++) { - $cr[] = $cr[$idx] . $cr[$jdx]; - } - } - return $cr; -} - - - -/** - * - */ -function checkRights(&$db,&$user,$context = null) { - if(is_null($context)) { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; + 'month', + 'workforce' => true +); +$statsBy['day'] = array( + 'timeline' => 'day', + 'workforce' => true +); +$statsBy['day_hour'] = array( + 'timeline' => 'day_hour', + 'workforce' => true +); + +$gui->statsBy = $statsBy; +$gui->group = $group = 'day'; +$stats = $mgr->getExecTimelineStats($args->tplan_id, null, $statsBy[$group]); + +if ($stats != null) { + $gui->do_report['status_ok'] = 1; + $gui->do_report['msg'] = ''; + $gui->statistics->exec = $stats[0]; + + if (! is_null($gui->statistics->exec)) { + switch ($group) { + case 'day': + $gui->columnsDefinition->exec = array( + lang_get('qty'), + lang_get('yyyy_mm_dd') + ); + break; + + case 'month': + $gui->columnsDefinition->exec = array( + lang_get('qty'), + lang_get('yyyy_mm') + ); + break; + + case 'day_hour': + $gui->columnsDefinition->exec = array( + lang_get('qty'), + lang_get('yyyy_mm_dd'), + lang_get('hh') + ); + break; + } + + if ($statsBy[$group]['workforce']) { + $gui->columnsDefinition->exec[] = lang_get('testers_qty'); + } + } +} + +if ($args->spreadsheet) { + createSpreadsheet($gui, $tplan_mgr); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +displayReport($tplCfg->tpl, $smarty, $args->format, $mailCfg); + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param tlTestPlanMetrics $tplanMgr + * @return stdClass|stdClass[]|testproject[] + */ +function initializeGui(&$dbHandler, $argsObj, &$tplanMgr) +{ + $gui = new stdClass(); + $gui->tproject_id = $argsObj->tproject_id; + + if ($argsObj->accessType == 'gui') { + list (, $gui) = initUserEnv($dbHandler, $argsObj); + } + + $gui->apikey = $argsObj->apikey; + $gui->accessType = $argsObj->accessType; + $gui->fakePlatform = array( + '' + ); + $gui->title = lang_get('execTimelineStats_report'); + $gui->do_report = array(); + $gui->showPlatforms = true; + $gui->columnsDefinition = new stdClass(); + $gui->columnsDefinition->keywords = null; + $gui->columnsDefinition->testers = null; + $gui->columnsDefinition->platform = null; + $gui->statistics = new stdClass(); + $gui->statistics->keywords = null; + $gui->statistics->testers = null; + $gui->statistics->milestones = null; + $gui->statistics->overalBuildStatus = null; + $gui->elapsed_time = 0; + $gui->displayBuildMetrics = false; + $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); + + $gui->tproject_name = testproject::getName($dbHandler, $argsObj->tproject_id); + + $info = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->tplan_name = $info['name']; + $gui->tplan_id = intval($argsObj->tplan_id); + + $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'map' + )); + if (is_null($gui->platformSet)) { + $gui->platformSet = array( + '' + ); + $gui->showPlatforms = false; + } else { + natsort($gui->platformSet); + } + + $gui->basehref = $_SESSION['basehref']; + $gui->actionSendMail = $gui->basehref . + "lib/results/execTimelineStats.php?format=" . FORMAT_MAIL_HTML . + "&tplan_id={$gui->tplan_id}" . "&tproject_id={$gui->tproject_id}"; + + $gui->actionSpreadsheet = $gui->basehref . + "lib/results/execTimelineStats.php?format=" . FORMAT_XLS . + "&tplan_id={$gui->tplan_id}&spreadsheet=1" . + "&tproject_id={$gui->tproject_id}"; + + $gui->mailFeedBack = new stdClass(); + $gui->mailFeedBack->msg = ''; + return $gui; +} + +/** + * + * @param stdClass $gui + * @param stdClass $args + * @param tlTestPlanMetrics $tplanMgr + */ +function createSpreadsheet($gui, &$tplanMgr) +{ + $lbl = initLblSpreadsheet(); + $cellRange = setCellRangeSpreadsheet(); + $style = initStyleSpreadsheet(); + + // Common + $dataHeaderMetrics = array(); + $dataHeaderMetrics[] = $lbl['qty_of_executions']; + switch ($gui->group) { + case 'day': + $dataHeaderMetrics[] = $lbl['yyyy_mm_dd']; + break; + + case 'month': + $dataHeaderMetrics[] = $lbl['yyyy_mm']; + break; + + case 'day_hour': + $dataHeaderMetrics[] = $lbl['yyyy_mm_dd']; + $dataHeaderMetrics[] = $lbl['hh']; + break; + } + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + $startingRow = count($lines2write); // MAGIC + $dataHeader = array(); + foreach ($dataHeaderMetrics as $val) { + $dataHeader[] = $val; + } + + $startingRow ++; + $startingRow ++; + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + $startingRow ++; + + // 'The meat!!' + foreach ($gui->statistics->exec as $elem) { + $ldx = 0; + foreach ($elem as $field) { + $cellID = $cellRange[$ldx ++] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + } + $startingRow ++; + } + $startingRow ++; + + // Just to add some final empty row + $cellID = $cellRange[0] . $startingRow; + $field = ''; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + // Final step + $tmpfname = tempnam(config_get('temp_dir'), "TL_ExecTimelineStats.tmp"); + $objPHPExcel->setActiveSheetIndex(0); + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + $objWriter->save($tmpfname); + + downloadXls($tmpfname, $xlsType, $gui, 'TL_ExecTimelineStats_'); +} + +/** + * + * @param PHPExcel $oj + * @param array $style + * @param array $lbl + * @param stdClass $gui + * @return array + */ +function xlsStepOne(&$oj, $style, &$lbl, &$gui) +{ + $dummy = ''; + $lines2write = array( + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + + $cellArea .= "A{$cdx}"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; +} + +/** + * + * @return array + */ +function initLblSpreadsheet() +{ + return init_labels( + array( + 'qty' => null, + 'yyyy_mm_dd' => null, + 'qty_of_executions' => null, + 'yyyy_mm' => null, + 'hh' => null, + 'platform' => null, + 'testplan' => null, + 'testproject' => null, + 'generated_by_TestLink_on' => null + )); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $style = array(); + $style['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $style['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + + $style['rowA'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FFFFFFFF' + ) + ) + ); + + $style['rowB'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'DCDCDCDC' + ) + ) + ); + + return $style; +} + +/** + * + * @return string|array + */ +function setCellRangeSpreadsheet() +{ + $cr = range('A', 'Z'); + $crLen = count($cr); + for ($idx = 0; $idx < $crLen; $idx ++) { + for ($jdx = 0; $jdx < $crLen; $jdx ++) { + $cr[] = $cr[$idx] . $cr[$jdx]; + } + } + return $cr; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); } diff --git a/lib/results/freeTestCases.php b/lib/results/freeTestCases.php index afd4974ade..b4bd1e149f 100644 --- a/lib/results/freeTestCases.php +++ b/lib/results/freeTestCases.php @@ -1,179 +1,210 @@ -testPriorityEnabled; - -$msg_key = 'all_testcases_has_testplan'; -$edit_img = TL_THEME_IMG_DIR . "edit_icon.png"; - -// Time tracking -//$tstart = microtime(true); -//$chronos[] = $tstart; $tnow = end($chronos);reset($chronos); -// Memory metrics -//$mem['usage'][] = memory_get_usage(true); $mem['peak'][] = memory_get_peak_usage(true); - -$gui = new stdClass(); -$gui->path_info = null; -$gui->tableSet = null; -$gui->freeTestCases = $tproject_mgr->getFreeTestCases($args->tproject_id); - -// Time tracking -//$chronos[] = microtime(true);$tnow = end($chronos);$tprev = prev($chronos); -//$t_elapsed_abs = number_format( $tnow - $tstart, 4); -//$t_elapsed = number_format( $tnow - $tprev, 4); -//echo '
    ' . __FUNCTION__ . ' Elapsed relative (sec):' . $t_elapsed . ' Elapsed ABSOLUTE (sec):' . $t_elapsed_abs .'
    '; -//reset($chronos); - - -if(!is_null($gui->freeTestCases['items'])) -{ - - $l18n = init_labels(array('low_importance' => null,'medium_importance' => null, - 'high_importance' => null, 'test_suite' => null, 'design' => null)); - $il = config_get('importance_levels'); - $impCols = array(); - $impCols[$il[LOW]] = "" . $l18n['low_importance']; - $impCols[$il[MEDIUM]] = "" . $l18n['medium_importance']; - $impCols[$il[HIGH]] = "" . $l18n['high_importance']; - - if($gui->freeTestCases['allfree']) - { - // has no sense display all test cases => display just message. - $msg_key = 'all_testcases_are_free'; - } - else - { - $msg_key = ''; - $tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tproject_id) . $tcase_cfg->glue_character; - $tcaseSet = array_keys($gui->freeTestCases['items']); - $tsuites = $tproject_mgr->tree_manager->get_full_path_verbose($tcaseSet, - array('output_format' => 'path_as_string')); - unset($tcaseSet); - - // Time tracking - //$chronos[] = microtime(true);$tnow = end($chronos);$tprev = prev($chronos); - //$t_elapsed_abs = number_format( $tnow - $tstart, 4); - //$t_elapsed = number_format( $tnow - $tprev, 4); - //echo '
    ' . __FUNCTION__ . ' Elapsed relative (sec):' . $t_elapsed . ' Elapsed ABSOLUTE AFTER get_full_path_verbose(sec):' . $t_elapsed_abs .'
    '; - //reset($chronos); - - $columns = getColumnsDefinition($priorityMgmtEnabled); - - // Extract the relevant data and build a matrix - $matrixData = array(); - foreach($gui->freeTestCases['items'] as &$tcases) - { - $rowData = array(); - $rowData[] = strip_tags($tsuites[$tcases['id']]); - $rowData[] = "" . - "" . - " " . - $tcasePrefix . $tcases['tc_external_id'] . ':' . strip_tags($tcases['name']); - - // only add importance column if - if($priorityMgmtEnabled) - { - $rowData[] = $impCols[$tcases['importance']]; - } - - $matrixData[] = $rowData; - } - // Time tracking - //$chronos[] = microtime(true);$tnow = end($chronos);$tprev = prev($chronos); - //$t_elapsed_abs = number_format( $tnow - $tstart, 4); - //$t_elapsed = number_format( $tnow - $tprev, 4); - //echo '
    ' . __FUNCTION__ . ' Elapsed relative (sec):' . $t_elapsed . ' Elapsed ABSOLUTE (sec):' . $t_elapsed_abs .'
    '; - //reset($chronos); - - $table = new tlExtTable($columns, $matrixData, 'tl_table_test_cases_not_assigned_to_any_test_plan'); - $table->setGroupByColumnName($l18n['test_suite']); - $table->setSortByColumnName(lang_get(($priorityMgmtEnabled) ? 'importance' : 'test_case')); - $table->sortDirection = 'DESC'; - $table->showToolbar = true; - $table->toolbarExpandCollapseGroupsButton = true; - $table->toolbarShowAllColumnsButton = true; - - $gui->tableSet = array($table); - } -} - - -$gui->tproject_name = $args->tproject_name; -$gui->pageTitle = lang_get('report_free_testcases_on_testproject'); -$gui->warning_msg = lang_get($msg_key); - - - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * get Columns definition for table to display - * - */ -function getColumnsDefinition($priorityMgmtEnabled) -{ - $colDef = array(); - - $colDef[] = array('title_key' => 'test_suite', 'type' => 'text'); - $colDef[] = array('title_key' => 'test_case', 'type' => 'text'); - if ($priorityMgmtEnabled) { - $urgencies_for_filter = array(lang_get('urgency_low'),lang_get('urgency_medium'),lang_get('urgency_high')); - $colDef[] = array('title_key' => 'importance', 'width' => 20, 'filter' => 'ListSimpleMatch', 'filterOptions' => $urgencies_for_filter); - } - - return $colDef; -} - -/** - * init_args - * - * Collect all inputs (arguments) to page, that can be arrived via $_REQUEST,$_SESSION - * and creates an stdClass() object where each property is result of mapping page inputs. - * We have created some sort of 'namespace', thi way we can easy understand which variables - * has been created for local use, and which have arrived on call. - * - */ -function init_args() -{ - $iParams = array( - "tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N), - ); - - $args = new stdClass(); - $pParams = G_PARAMS($iParams,$args); - - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; - - return $args; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); +testPriorityEnabled; + +$msg_key = 'all_testcases_has_testplan'; +$edit_img = TL_THEME_IMG_DIR . "edit_icon.png"; + +// Time tracking +// $tstart = microtime(true); +// $chronos[] = $tstart; $tnow = end($chronos);reset($chronos); +// Memory metrics +// $mem['usage'][] = memory_get_usage(true); $mem['peak'][] = memory_get_peak_usage(true); + +$gui = new stdClass(); +$gui->path_info = null; +$gui->tableSet = null; +$gui->freeTestCases = $tproject_mgr->getFreeTestCases($args->tproject_id); + +// Time tracking +// $chronos[] = microtime(true);$tnow = end($chronos);$tprev = prev($chronos); +// $t_elapsed_abs = number_format( $tnow - $tstart, 4); +// $t_elapsed = number_format( $tnow - $tprev, 4); +// echo '
    ' . __FUNCTION__ . ' Elapsed relative (sec):' . $t_elapsed . ' Elapsed ABSOLUTE (sec):' . $t_elapsed_abs .'
    '; +// reset($chronos); + +if (! is_null($gui->freeTestCases['items'])) { + + $l18n = init_labels( + array( + 'low_importance' => null, + 'medium_importance' => null, + 'high_importance' => null, + 'test_suite' => null, + 'design' => null + )); + $il = config_get('importance_levels'); + $impCols = array(); + $impCols[$il[LOW]] = "" . $l18n['low_importance']; + $impCols[$il[MEDIUM]] = "" . $l18n['medium_importance']; + $impCols[$il[HIGH]] = "" . $l18n['high_importance']; + + if ($gui->freeTestCases['allfree']) { + // has no sense display all test cases => display just message. + $msg_key = 'all_testcases_are_free'; + } else { + $msg_key = ''; + $tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tproject_id) . + $tcase_cfg->glue_character; + $tcaseSet = array_keys($gui->freeTestCases['items']); + $tsuites = $tproject_mgr->tree_manager->get_full_path_verbose($tcaseSet, + array( + 'output_format' => 'path_as_string' + )); + unset($tcaseSet); + + // Time tracking + // $chronos[] = microtime(true);$tnow = end($chronos);$tprev = prev($chronos); + // $t_elapsed_abs = number_format( $tnow - $tstart, 4); + // $t_elapsed = number_format( $tnow - $tprev, 4); + // echo '
    ' . __FUNCTION__ . ' Elapsed relative (sec):' . $t_elapsed . ' Elapsed ABSOLUTE AFTER get_full_path_verbose(sec):' . $t_elapsed_abs .'
    '; + // reset($chronos); + + $columns = getColumnsDefinition($priorityMgmtEnabled); + + // Extract the relevant data and build a matrix + $matrixData = array(); + foreach ($gui->freeTestCases['items'] as &$tcases) { + $rowData = array(); + $rowData[] = strip_tags($tsuites[$tcases['id']]); + $rowData[] = "" . + "" . + " " . + $tcasePrefix . $tcases['tc_external_id'] . ':' . + strip_tags($tcases['name']); + + // only add importance column if + if ($priorityMgmtEnabled) { + $rowData[] = $impCols[$tcases['importance']]; + } + + $matrixData[] = $rowData; + } + // Time tracking + // $chronos[] = microtime(true);$tnow = end($chronos);$tprev = prev($chronos); + // $t_elapsed_abs = number_format( $tnow - $tstart, 4); + // $t_elapsed = number_format( $tnow - $tprev, 4); + // echo '
    ' . __FUNCTION__ . ' Elapsed relative (sec):' . $t_elapsed . ' Elapsed ABSOLUTE (sec):' . $t_elapsed_abs .'
    '; + // reset($chronos); + + $table = new tlExtTable($columns, $matrixData, + 'tl_table_test_cases_not_assigned_to_any_test_plan'); + $table->setGroupByColumnName($l18n['test_suite']); + $table->setSortByColumnName( + lang_get(($priorityMgmtEnabled) ? 'importance' : 'test_case')); + $table->sortDirection = 'DESC'; + $table->showToolbar = true; + $table->toolbarExpandCollapseGroupsButton = true; + $table->toolbarShowAllColumnsButton = true; + + $gui->tableSet = array( + $table + ); + } +} + +$gui->tproject_name = $args->tproject_name; +$gui->pageTitle = lang_get('report_free_testcases_on_testproject'); +$gui->warning_msg = lang_get($msg_key); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * get Columns definition for table to display + * + * @param int $priorityMgmtEnabled + * @return array + */ +function getColumnsDefinition($priorityMgmtEnabled) +{ + $colDef = array(); + + $colDef[] = array( + 'title_key' => 'test_suite', + 'type' => 'text' + ); + $colDef[] = array( + 'title_key' => 'test_case', + 'type' => 'text' + ); + if ($priorityMgmtEnabled) { + $urgencies_for_filter = array( + lang_get('urgency_low'), + lang_get('urgency_medium'), + lang_get('urgency_high') + ); + $colDef[] = array( + 'title_key' => 'importance', + 'width' => 20, + 'filter' => 'ListSimpleMatch', + 'filterOptions' => $urgencies_for_filter + ); + } + + return $colDef; +} + +/** + * init_args + * + * Collect all inputs (arguments) to page, that can be arrived via $_REQUEST,$_SESSION + * and creates an stdClass() object where each property is result of mapping page inputs. + * We have created some sort of 'namespace', thi way we can easy understand which variables + * has been created for local use, and which have arrived on call. + * + * @return stdClass + */ +function initArgs() +{ + $iParams = array( + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + G_PARAMS($iParams, $args); + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; + + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_metrics'); } diff --git a/lib/results/keywordBarChart.php b/lib/results/keywordBarChart.php index 3650984046..d6a18f5b60 100644 --- a/lib/results/keywordBarChart.php +++ b/lib/results/keywordBarChart.php @@ -1,165 +1,161 @@ -scale = new stdClass(); - -$chart_cfg = config_get('results'); -$chart_cfg = $chart_cfg['charts']['dimensions']['keywordBarChart']; - -$cfg->chartTitle = lang_get($chart_cfg['chartTitle']); -$cfg->XSize = $chart_cfg['XSize']; -$cfg->YSize = $chart_cfg['YSize']; -$cfg->beginX = $chart_cfg['beginX']; -$cfg->beginY = $chart_cfg['beginY']; -$cfg->scale->legendXAngle = $chart_cfg['legendXAngle']; - - -$args = init_args($db); -$info = getDataAndScale($db,$args); - -createChart($info,$cfg); - - -/* - function: getDataAndScale - - args: dbHandler - - returns: object - -*/ -function getDataAndScale(&$dbHandler,$argsObj) -{ - $resultsCfg = config_get('results'); - $obj = new stdClass(); - $items = array(); - $totals = null; - - $metricsMgr = new tlTestPlanMetrics($dbHandler); - $dummy = $metricsMgr->getStatusTotalsByKeywordForRender($argsObj->tplan_id); - - $obj->canDraw = false; - if( !is_null($dummy) ) - { - $dataSet = $dummy->info; - $obj->canDraw = !is_null($dataSet) && (count($dataSet) > 0); - } - - if($obj->canDraw) - { - // Process to enable alphabetical order - foreach($dataSet as $keyword_id => $elem) - { - $item_descr[$elem['name']] = $keyword_id; - } - ksort($item_descr); - - foreach($item_descr as $name => $keyword_id) - { - $items[] = htmlspecialchars($name); - foreach($dataSet[$keyword_id]['details'] as $status => $value) - { - $totals[$status][] = $value['qty']; - } - } - } - - $obj->xAxis = new stdClass(); - $obj->xAxis->values = $items; - $obj->xAxis->serieName = 'Serie8'; - - $obj->series_color = null; - $obj->scale = new stdClass(); - $obj->scale->maxY = 0; - $obj->scale->minY = 0; - $obj->scale->divisions = 0; - - if(!is_null($totals)) - { - // in this array position we will find minimun value after an rsort - $minPos = count($dataSet)-1; - $obj->scale->maxY = 0; - $obj->scale->minY = 0; - - foreach($totals as $status => $values) - { - $obj->chart_data[] = $values; - $obj->series_label[] = lang_get($resultsCfg['status_label'][$status]); - if( isset($resultsCfg['charts']['status_colour'][$status]) ) - { - $obj->series_color[] = $resultsCfg['charts']['status_colour'][$status]; - } - } - } - - return $obj; -} - -/** - * - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,0,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - if( !is_null($args->apikey) ) - { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - $args->addOpAccess = false; - $cerbero->method = null; - $cerbero->args->getAccessAttr = false; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } - else - { - testlinkInitPage($dbHandler,false,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - if( isset($_REQUEST['debug']) ) - { - $args->debug = 'yes'; - } - return $args; -} - -/** - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file +scale = new stdClass(); + +$chart_cfg = config_get('results'); +$chart_cfg = $chart_cfg['charts']['dimensions']['keywordBarChart']; + +$cfg->chartTitle = lang_get($chart_cfg['chartTitle']); +$cfg->XSize = $chart_cfg['XSize']; +$cfg->YSize = $chart_cfg['YSize']; +$cfg->beginX = $chart_cfg['beginX']; +$cfg->beginY = $chart_cfg['beginY']; +$cfg->scale->legendXAngle = $chart_cfg['legendXAngle']; + +$args = initArgs($db); +$info = getDataAndScale($db, $args); + +createChart($info, $cfg); + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function getDataAndScale(&$dbHandler, $argsObj) +{ + $resultsCfg = config_get('results'); + $obj = new stdClass(); + $items = array(); + $totals = null; + + $metricsMgr = new tlTestPlanMetrics($dbHandler); + $dummy = $metricsMgr->getStatusTotalsByKeywordForRender($argsObj->tplan_id); + + $obj->canDraw = false; + if (! is_null($dummy)) { + $dataSet = $dummy->info; + $obj->canDraw = ! is_null($dataSet) && (! empty($dataSet)); + } + + if ($obj->canDraw) { + // Process to enable alphabetical order + foreach ($dataSet as $keyword_id => $elem) { + $item_descr[$elem['name']] = $keyword_id; + } + ksort($item_descr); + + foreach ($item_descr as $name => $keyword_id) { + $items[] = htmlspecialchars($name); + foreach ($dataSet[$keyword_id]['details'] as $status => $value) { + $totals[$status][] = $value['qty']; + } + } + } + + $obj->xAxis = new stdClass(); + $obj->xAxis->values = $items; + $obj->xAxis->serieName = 'Serie8'; + + $obj->series_color = null; + $obj->scale = new stdClass(); + $obj->scale->maxY = 0; + $obj->scale->minY = 0; + $obj->scale->divisions = 0; + + if (! is_null($totals)) { + $obj->scale->maxY = 0; + $obj->scale->minY = 0; + + foreach ($totals as $status => $values) { + $obj->chart_data[] = $values; + $obj->series_label[] = lang_get( + $resultsCfg['status_label'][$status]); + if (isset($resultsCfg['charts']['status_colour'][$status])) { + $obj->series_color[] = $resultsCfg['charts']['status_colour'][$status]; + } + } + } + + return $obj; +} + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 0, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + $cerbero->args->getAccessAttr = false; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + if (isset($_REQUEST['debug'])) { + $args->debug = 'yes'; + } + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); +} diff --git a/lib/results/metricsDashboard.php b/lib/results/metricsDashboard.php index 0e3e978906..d66b1b902e 100644 --- a/lib/results/metricsDashboard.php +++ b/lib/results/metricsDashboard.php @@ -1,473 +1,517 @@ -show_test_plan_status; -$round_precision = config_get('dashboard_precision'); - -$labels = init_labels(array('overall_progress' => null, 'test_plan' => null, 'progress' => null, - 'href_metrics_dashboard' => null, 'progress_absolute' => null, - 'no_testplans_available' => null, 'not_aplicable' => null, - 'platform' => null, 'th_active_tc' => null, 'in_percent' => null)); - -list($gui->tplan_metrics,$gui->show_platforms, $platforms) = getMetrics($db,$_SESSION['currentUser'],$args,$result_cfg, $labels); - - -// new dBug($gui->tplan_metrics); -if(count($gui->tplan_metrics) > 0) -{ - $statusSetForDisplay = $result_cfg['status_label_for_exec_ui']; - $gui->warning_msg = ''; - $columns = getColumnsDefinition($gui->show_platforms, $statusSetForDisplay, $labels, $platforms); - - $matrixData = array(); - if(isset($gui->tplan_metrics['testplans'])) - { - foreach ($gui->tplan_metrics['testplans'] as $tplan_metrics) - { - foreach($tplan_metrics['platforms'] as $key => $platform_metric) - { - $rowData = array(); - - // if test plan does not use platforms a overall status is not necessary - $tplan_string = strip_tags($platform_metric['tplan_name']); - if ($show_all_status_details) - { - // add information for all exec statuses - $tplan_string .= "
    "; - foreach( $statusSetForDisplay as $status_verbose => &$status_label) - { - $tplan_string .= lang_get($status_label). ": " . - $tplan_metrics['overall'][$status_verbose] . - " [" . getPercentage($tplan_metrics['overall'][$status_verbose], - $tplan_metrics['overall']['active'], - $round_precision) . "%], "; - } - } - else - { - $tplan_string .= " - "; - } - - $tplan_string .= $labels['overall_progress'] . ": " . - getPercentage($tplan_metrics['overall']['executed'], - $tplan_metrics['overall']['active'], - $round_precision) . "%"; - - $rowData[] = $tplan_string; - if ($gui->show_platforms) - { - $rowData[] = strip_tags($platform_metric['platform_name']); - } - - if( isset($platform_metric['total']) ) - { - $rowData[] = $platform_metric['total']; - } - else - { - $rowData[] = 0; - } - - foreach ($statusSetForDisplay as $status_verbose => $status_label) - { - if( isset($platform_metric[$status_verbose]) ) - { - $rowData[] = $platform_metric[$status_verbose]; - $rowData[] = getPercentage($platform_metric[$status_verbose], $platform_metric['active'], - $round_precision); - } - else - { - $rowData[] = 0; - $rowData[] = 0; - } - } - - $rowData[] = getPercentage($platform_metric['executed'], $platform_metric['active'], - $round_precision); - - $matrixData[] = $rowData; - } - } - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_metrics_dashboard'); - - // if platforms are to be shown -> group by test plan - // if no platforms are to be shown -> no grouping - if($gui->show_platforms) - { - $table->setGroupByColumnName($labels['test_plan']); - } - - $table->setSortByColumnName($labels['progress']); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->toolbarExpandCollapseGroupsButton = true; - $table->toolbarShowAllColumnsButton = true; - $table->toolbarResetFiltersButton = true; - $table->title = $labels['href_metrics_dashboard']; - $table->showGroupItemsCount = true; - - $gui->tableSet = array($table); - - // get overall progress, collect test project metrics - $gui->project_metrics = collectTestProjectMetrics($gui->tplan_metrics, - array('statusSetForDisplay' => $statusSetForDisplay, - 'round_precision' => $round_precision)); -} - - -$smarty = new TLSmarty; -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * only active builds has to be used - * - * @internal revisions - * - * - */ -function getMetrics(&$db,$userObj,$args, $result_cfg, $labels) -{ - $user_id = $args->currentUserID; - $tproject_id = $args->tproject_id; - $linked_tcversions = array(); - $metrics = array(); - $tplan_mgr = new testplan($db); - $show_platforms = false; - $platforms = array(); - - // get all tesplans accessibles for user, for $tproject_id - $options = array('output' => 'map'); - $options['active'] = $args->show_only_active ? ACTIVE : TP_ALL_STATUS; - $test_plans = $userObj->getAccessibleTestPlans($db,$tproject_id,null,$options); - - // Get count of testcases linked to every testplan - // Hmm Count active and inactive ? - $linkedItemsQty = $tplan_mgr->count_testcases(array_keys($test_plans),null,array('output' => 'groupByTestPlan')); - - - $metricsMgr = new tlTestPlanMetrics($db); - $show_platforms = false; - - $metrics = array('testplans' => null, 'total' => null); - $mm = &$metrics['testplans']; - $metrics['total'] = array('active' => 0,'total' => 0, 'executed' => 0); - foreach($result_cfg['status_label_for_exec_ui'] as $status_code => &$dummy) - { - $metrics['total'][$status_code] = 0; - } - - $codeStatusVerbose = array_flip($result_cfg['status_code']); - foreach($test_plans as $key => &$dummy) - { - // We need to know if test plan has builds, if not we can not call any method - // that try to get exec info, because you can only execute if you have builds. - // - // 20130909 - added active filter - $buildSet = $tplan_mgr->get_builds($key,testplan::ACTIVE_BUILDS); - if( is_null($buildSet) ) - { - continue; - } - - $platformSet = $tplan_mgr->getPlatforms($key); - if (isset($platformSet)) - { - $platforms = array_merge($platforms, $platformSet); - } - $show_platforms_for_tplan = !is_null($platformSet); - $show_platforms = $show_platforms || $show_platforms_for_tplan; - if( !is_null($platformSet) ) - { - $neurus = $metricsMgr->getExecCountersByPlatformExecStatus($key,null, - array('getPlatformSet' => true, - 'getOnlyActiveTCVersions' => true)); - - $mm[$key]['overall']['active'] = $mm[$key]['overall']['executed'] = 0; - foreach($neurus['with_tester'] as $platform_id => &$pinfo) - { - $xd = &$mm[$key]['platforms'][$platform_id]; - $xd['tplan_name'] = $dummy['name']; - $xd['platform_name'] = $neurus['platforms'][$platform_id]; - $xd['total'] = $xd['active'] = $neurus['total'][$platform_id]['qty']; - $xd['executed'] = 0; - - foreach($pinfo as $code => &$elem) - { - $xd[$codeStatusVerbose[$code]] = $elem['exec_qty']; - if($codeStatusVerbose[$code] != 'not_run') - { - $xd['executed'] += $elem['exec_qty']; - } - if( !isset($mm[$key]['overall'][$codeStatusVerbose[$code]]) ) - { - $mm[$key]['overall'][$codeStatusVerbose[$code]] = 0; - } - $mm[$key]['overall'][$codeStatusVerbose[$code]] += $elem['exec_qty']; - $metrics['total'][$codeStatusVerbose[$code]] += $elem['exec_qty']; - } - $mm[$key]['overall']['executed'] += $xd['executed']; - $mm[$key]['overall']['active'] += $xd['active']; - } - unset($neurus); - $mm[$key]['overall']['total'] = $mm[$key]['overall']['active']; - $metrics['total']['executed'] += $mm[$key]['overall']['executed']; - $metrics['total']['active'] += $mm[$key]['overall']['active']; - } - else - { - $mm[$key]['overall'] = $metricsMgr->getExecCountersByExecStatus($key,null, - array('getOnlyActiveTCVersions' => true)); - - $mm[$key]['overall']['active'] = $mm[$key]['overall']['total']; - - // compute executed - $mm[$key]['overall']['executed'] = 0; - foreach($mm[$key]['overall'] as $status_code => $qty) - { - if( $status_code != 'not_run' && $status_code != 'total' && $status_code != 'active' ) - { - $mm[$key]['overall']['executed'] += $qty; - } - - if( $status_code != 'total' && $status_code != 'active' ) - { - if(!isset($metrics['total'][$status_code])) - { - $metrics['total'][$status_code] = 0; - } - $metrics['total'][$status_code] += $qty; - } - } - $metrics['total']['executed'] += $mm[$key]['overall']['executed']; - $metrics['total']['active'] += $mm[$key]['overall']['active']; - - $mm[$key]['platforms'][0] = $mm[$key]['overall']; - $mm[$key]['platforms'][0]['tplan_name'] = $dummy['name']; - $mm[$key]['platforms'][0]['platform_name'] = $labels['not_aplicable']; - } - } - - // remove duplicate platform names - $platformsUnique = array(); - foreach($platforms as $platform) - { - if(!in_array($platform['name'], $platformsUnique)) - { - $platformsUnique[] = $platform['name']; - } - } - - return array($metrics, $show_platforms, $platformsUnique); -} - -/** - * - * - */ -function getPercentage($denominator, $numerator, $round_precision) -{ - $percentage = ($numerator > 0) ? (round(($denominator / $numerator) * 100,$round_precision)) : 0; - - return $percentage; -} - -/** - * get Columns definition for table to display - * - */ -function getColumnsDefinition($showPlatforms, $statusLbl, $labels, $platforms) -{ - $colDef = array(); - - $colDef[] = array('title_key' => 'test_plan', 'width' => 60, 'type' => 'text', 'sortType' => 'asText', - 'filter' => 'string'); - - if ($showPlatforms) - { - $colDef[] = array('title_key' => 'platform', 'width' => 60, 'sortType' => 'asText', - 'filter' => 'list', 'filterOptions' => $platforms); - } - - $colDef[] = array('title_key' => 'th_active_tc', 'width' => 40, 'sortType' => 'asInt', - 'filter' => 'numeric'); - - // create 2 columns for each defined status - foreach($statusLbl as $lbl) - { - $colDef[] = array('title_key' => $lbl, 'width' => 40, 'hidden' => true, 'type' => 'int', - 'sortType' => 'asInt', 'filter' => 'numeric'); - - $colDef[] = array('title' => lang_get($lbl) . " " . $labels['in_percent'], 'width' => 40, - 'col_id' => 'id_'. $lbl .'_percent', 'type' => 'float', 'sortType' => 'asFloat', - 'filter' => 'numeric'); - } - - $colDef[] = array('title_key' => 'progress', 'width' => 40, 'sortType' => 'asFloat', 'filter' => 'numeric'); - - return $colDef; -} - -function initEnv(&$dbHandler) -{ - $args = new stdClass(); - $gui = new stdClass(); - - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "show_only_active" => array(tlInputParameter::CB_BOOL), - "show_only_active_hidden" => array(tlInputParameter::CB_BOOL)); - - R_PARAMS($iParams,$args); - - if( !is_null($args->apikey) ) - { - - $args->show_only_active = true; - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->redirect_target = "../../login.php?note=logout"; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - // Have got OBJECT KEY - $cerbero->method = null; - $cerbero->args->getAccessAttr = false; - - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - - // if we are here => everything seems OK - $tprojMgr = new testproject($dbHandler); - $dj = $tprojMgr->getByAPIKey($args->apikey); - $args->tproject_id = $dj['id']; - } - } - else - { - testlinkInitPage($dbHandler,false,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? - intval($_SESSION['testprojectID']) : 0; - } - - if($args->tproject_id <= 0) - { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid Test Project ID ({$args->tproject_id})"; - throw new Exception($msg); - } - $mgr = new tree($dbHandler); - $dummy = $mgr->get_node_hierarchy_info($args->tproject_id); - $args->tproject_name = $dummy['name']; - - $args->user = $_SESSION['currentUser']; - $args->currentUserID = $args->user->dbID; - - // I'm sorry for MAGIC - $args->direct_link_ok = true; - $ak = testproject::getAPIkey($dbHandler,$args->tproject_id); - $args->direct_link = $_SESSION['basehref'] . - "lnl.php?type=metricsdashboard&" . - "apikey={$ak}"; - - if ($args->show_only_active) - { - $selection = true; - } - else if ($args->show_only_active_hidden) - { - $selection = false; - } - else if (isset($_SESSION['show_only_active'])) - { - $selection = $_SESSION['show_only_active']; - } - else - { - $selection = true; - } - $args->show_only_active = $_SESSION['show_only_active'] = $selection; - - - $gui->tproject_name = $args->tproject_name; - $gui->show_only_active = $args->show_only_active; - $gui->direct_link = $args->direct_link; - $gui->direct_link_ok = $args->direct_link_ok; - $gui->warning_msg = lang_get('no_testplans_available'); - - return array($args,$gui); -} - - -/** - * - */ -function collectTestProjectMetrics($tplanMetrics,$cfg) -{ - $mm = array(); - $mm['executed']['value'] = getPercentage($tplanMetrics['total']['executed'], - $tplanMetrics['total']['active'], $cfg['round_precision']); - $mm['executed']['label_key'] = 'progress_absolute'; - - foreach ($cfg['statusSetForDisplay'] as $status_verbose => $label_key) - { - $mm[$status_verbose]['value'] = getPercentage($tplanMetrics['total'][$status_verbose], - $tplanMetrics['total']['active'], $cfg['round_precision']); - $mm[$status_verbose]['label_key'] = $label_key; - } - return $mm; -} - -/** - * - */ -function checkRights(&$db,&$user,$context = null) -{ - if(is_null($context)) - { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - $checkOrMode = array('testplan_metrics','testplan_execute'); - foreach($checkOrMode as $right) - { - if( $user->hasRight($db,$right,$context->tproject_id,$context->tplan_id,$context->getAccessAttr) ) - { - return true; - } - } - return false; -} +show_test_plan_status; +$round_precision = config_get('dashboard_precision'); + +$labels = init_labels( + array( + 'overall_progress' => null, + 'test_plan' => null, + 'progress' => null, + 'href_metrics_dashboard' => null, + 'progress_absolute' => null, + 'no_testplans_available' => null, + 'not_aplicable' => null, + 'platform' => null, + 'th_active_tc' => null, + 'in_percent' => null + )); + +list ($gui->tplan_metrics, $gui->show_platforms, $platforms) = getMetrics($db, + $_SESSION['currentUser'], $args, $result_cfg, $labels); + +if (! empty($gui->tplan_metrics)) { + $statusSetForDisplay = $result_cfg['status_label_for_exec_ui']; + $gui->warning_msg = ''; + $columns = getColumnsDefinition($gui->show_platforms, $statusSetForDisplay, + $labels, $platforms); + + $matrixData = array(); + if (isset($gui->tplan_metrics['testplans'])) { + foreach ($gui->tplan_metrics['testplans'] as $tplan_metrics) { + foreach ($tplan_metrics['platforms'] as $platform_metric) { + $rowData = array(); + + // if test plan does not use platforms a overall status is not necessary + $tplan_string = strip_tags($platform_metric['tplan_name']); + if ($show_all_status_details) { + // add information for all exec statuses + $tplan_string .= "
    "; + foreach ($statusSetForDisplay as $status_verbose => &$status_label) { + $tplan_string .= lang_get($status_label) . ": " . + $tplan_metrics['overall'][$status_verbose] . " [" . + getPercentage( + $tplan_metrics['overall'][$status_verbose], + $tplan_metrics['overall']['active'], + $round_precision) . "%], "; + } + } else { + $tplan_string .= " - "; + } + + $tplan_string .= $labels['overall_progress'] . ": " . + getPercentage($tplan_metrics['overall']['executed'], + $tplan_metrics['overall']['active'], $round_precision) . + "%"; + + $rowData[] = $tplan_string; + if ($gui->show_platforms) { + $rowData[] = strip_tags($platform_metric['platform_name']); + } + + if (isset($platform_metric['total'])) { + $rowData[] = $platform_metric['total']; + } else { + $rowData[] = 0; + } + + foreach ($statusSetForDisplay as $status_verbose => $status_label) { + if (isset($platform_metric[$status_verbose])) { + $rowData[] = $platform_metric[$status_verbose]; + $rowData[] = getPercentage( + $platform_metric[$status_verbose], + $platform_metric['active'], $round_precision); + } else { + $rowData[] = 0; + $rowData[] = 0; + } + } + + $rowData[] = getPercentage($platform_metric['executed'], + $platform_metric['active'], $round_precision); + + $matrixData[] = $rowData; + } + } + } + + $table = new tlExtTable($columns, $matrixData, 'tl_table_metrics_dashboard'); + + // if platforms are to be shown -> group by test plan + // if no platforms are to be shown -> no grouping + if ($gui->show_platforms) { + $table->setGroupByColumnName($labels['test_plan']); + } + + $table->setSortByColumnName($labels['progress']); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->toolbarExpandCollapseGroupsButton = true; + $table->toolbarShowAllColumnsButton = true; + $table->toolbarResetFiltersButton = true; + $table->title = $labels['href_metrics_dashboard']; + $table->showGroupItemsCount = true; + + $gui->tableSet = array( + $table + ); + + // get overall progress, collect test project metrics + $gui->project_metrics = collectTestProjectMetrics($gui->tplan_metrics, + array( + 'statusSetForDisplay' => $statusSetForDisplay, + 'round_precision' => $round_precision + )); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * only active builds has to be used + * + * @param database $db + * @param tlUser $userObj + * @param stdClass $args + * @param array $result_cfg + * @param array $labels + * @return array + * @internal revisions + */ +function getMetrics(&$db, $userObj, $args, $result_cfg, $labels) +{ + $tproject_id = $args->tproject_id; + $metrics = array(); + $tplan_mgr = new testplan($db); + $show_platforms = false; + $platforms = array(); + + // get all tesplans accessibles for user, for $tproject_id + $options = array( + 'output' => 'map' + ); + $options['active'] = $args->show_only_active ? ACTIVE : TP_ALL_STATUS; + $test_plans = $userObj->getAccessibleTestPlans($db, $tproject_id, null, + $options); + + $metricsMgr = new tlTestPlanMetrics($db); + $show_platforms = false; + + $metrics = array( + 'testplans' => null, + 'total' => null + ); + $mm = &$metrics['testplans']; + $metrics['total'] = array( + 'active' => 0, + 'total' => 0, + 'executed' => 0 + ); + foreach ($result_cfg['status_label_for_exec_ui'] as $status_code => &$dummy) { + $metrics['total'][$status_code] = 0; + } + + $codeStatusVerbose = array_flip($result_cfg['status_code']); + foreach ($test_plans as $key => &$dummy) { + // We need to know if test plan has builds, if not we can not call any method + // that try to get exec info, because you can only execute if you have builds. + // + // 20130909 - added active filter + $buildSet = $tplan_mgr->get_builds($key, testplan::ACTIVE_BUILDS); + if (is_null($buildSet)) { + continue; + } + + $platformSet = $tplan_mgr->getPlatforms($key); + if (isset($platformSet)) { + $platforms = array_merge($platforms, $platformSet); + } + $show_platforms_for_tplan = ! is_null($platformSet); + $show_platforms = $show_platforms || $show_platforms_for_tplan; + if (! is_null($platformSet)) { + $neurus = $metricsMgr->getExecCountersByPlatformExecStatus($key, + null, + array( + 'getPlatformSet' => true, + 'getOnlyActiveTCVersions' => true + )); + + $mm[$key]['overall']['active'] = $mm[$key]['overall']['executed'] = 0; + foreach ($neurus['with_tester'] as $platform_id => &$pinfo) { + $xd = &$mm[$key]['platforms'][$platform_id]; + $xd['tplan_name'] = $dummy['name']; + $xd['platform_name'] = $neurus['platforms'][$platform_id]; + $xd['total'] = $xd['active'] = $neurus['total'][$platform_id]['qty']; + $xd['executed'] = 0; + + foreach ($pinfo as $code => &$elem) { + $xd[$codeStatusVerbose[$code]] = $elem['exec_qty']; + if ($codeStatusVerbose[$code] != 'not_run') { + $xd['executed'] += $elem['exec_qty']; + } + if (! isset($mm[$key]['overall'][$codeStatusVerbose[$code]])) { + $mm[$key]['overall'][$codeStatusVerbose[$code]] = 0; + } + $mm[$key]['overall'][$codeStatusVerbose[$code]] += $elem['exec_qty']; + $metrics['total'][$codeStatusVerbose[$code]] += $elem['exec_qty']; + } + $mm[$key]['overall']['executed'] += $xd['executed']; + $mm[$key]['overall']['active'] += $xd['active']; + } + unset($neurus); + $mm[$key]['overall']['total'] = $mm[$key]['overall']['active']; + $metrics['total']['executed'] += $mm[$key]['overall']['executed']; + $metrics['total']['active'] += $mm[$key]['overall']['active']; + } else { + $mm[$key]['overall'] = $metricsMgr->getExecCountersByExecStatus( + $key, null, array( + 'getOnlyActiveTCVersions' => true + )); + + $mm[$key]['overall']['active'] = $mm[$key]['overall']['total']; + + // compute executed + $mm[$key]['overall']['executed'] = 0; + foreach ($mm[$key]['overall'] as $status_code => $qty) { + if ($status_code != 'not_run' && $status_code != 'total' && + $status_code != 'active') { + $mm[$key]['overall']['executed'] += $qty; + } + + if ($status_code != 'total' && $status_code != 'active') { + if (! isset($metrics['total'][$status_code])) { + $metrics['total'][$status_code] = 0; + } + $metrics['total'][$status_code] += $qty; + } + } + $metrics['total']['executed'] += $mm[$key]['overall']['executed']; + $metrics['total']['active'] += $mm[$key]['overall']['active']; + + $mm[$key]['platforms'][0] = $mm[$key]['overall']; + $mm[$key]['platforms'][0]['tplan_name'] = $dummy['name']; + $mm[$key]['platforms'][0]['platform_name'] = $labels['not_aplicable']; + } + } + + // remove duplicate platform names + $platformsUnique = array(); + foreach ($platforms as $platform) { + if (! in_array($platform['name'], $platformsUnique)) { + $platformsUnique[] = $platform['name']; + } + } + + return array( + $metrics, + $show_platforms, + $platformsUnique + ); +} + +/** + * + * @param int $denominator + * @param int $numerator + * @param int $round_precision + * @return number + */ +function getPercentage($denominator, $numerator, $round_precision) +{ + return ($numerator > 0) ? (round(($denominator / $numerator) * 100, + $round_precision)) : 0; +} + +/** + * get Columns definition for table to display + * + * @param boolean $showPlatforms + * @param array $statusLbl + * @param array $labels + * @param array $platforms + * @return array + */ +function getColumnsDefinition($showPlatforms, $statusLbl, $labels, $platforms) +{ + $colDef = array(); + + $colDef[] = array( + 'title_key' => 'test_plan', + 'width' => 60, + 'type' => 'text', + 'sortType' => 'asText', + 'filter' => 'string' + ); + + if ($showPlatforms) { + $colDef[] = array( + 'title_key' => 'platform', + 'width' => 60, + 'sortType' => 'asText', + 'filter' => 'list', + 'filterOptions' => $platforms + ); + } + + $colDef[] = array( + 'title_key' => 'th_active_tc', + 'width' => 40, + 'sortType' => 'asInt', + 'filter' => 'numeric' + ); + + // create 2 columns for each defined status + foreach ($statusLbl as $lbl) { + $colDef[] = array( + 'title_key' => $lbl, + 'width' => 40, + 'hidden' => true, + 'type' => 'int', + 'sortType' => 'asInt', + 'filter' => 'numeric' + ); + + $colDef[] = array( + 'title' => lang_get($lbl) . " " . $labels['in_percent'], + 'width' => 40, + 'col_id' => 'id_' . $lbl . '_percent', + 'type' => 'float', + 'sortType' => 'asFloat', + 'filter' => 'numeric' + ); + } + + $colDef[] = array( + 'title_key' => 'progress', + 'width' => 40, + 'sortType' => 'asFloat', + 'filter' => 'numeric' + ); + + return $colDef; +} + +/** + * + * @param database $dbHandler + * @return stdClass[] + */ +function initEnv(&$dbHandler) +{ + $args = new stdClass(); + $gui = new stdClass(); + + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "show_only_active" => array( + tlInputParameter::CB_BOOL + ), + "show_only_active_hidden" => array( + tlInputParameter::CB_BOOL + ) + ); + + R_PARAMS($iParams, $args); + + if (! is_null($args->apikey)) { + + $args->show_only_active = true; + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->redirect_target = "../../login.php?note=logout"; + + if (strlen($args->apikey) == 32) { + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + // Have got OBJECT KEY + $cerbero->method = null; + $cerbero->args->getAccessAttr = false; + + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + + // if we are here => everything seems OK + $tprojMgr = new testproject($dbHandler); + $dj = $tprojMgr->getByAPIKey($args->apikey); + $args->tproject_id = $dj['id']; + } + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + if ($args->tproject_id <= 0) { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid Test Project ID ({$args->tproject_id})"; + throw new Exception($msg); + } + $mgr = new tree($dbHandler); + $dummy = $mgr->get_node_hierarchy_info($args->tproject_id); + $args->tproject_name = $dummy['name']; + + $args->user = $_SESSION['currentUser']; + $args->currentUserID = $args->user->dbID; + + // I'm sorry for MAGIC + $args->direct_link_ok = true; + $ak = testproject::getAPIkey($dbHandler, $args->tproject_id); + $args->direct_link = $_SESSION['basehref'] . "lnl.php?type=metricsdashboard&" . + "apikey={$ak}"; + + if ($args->show_only_active) { + $selection = true; + } elseif ($args->show_only_active_hidden) { + $selection = false; + } elseif (isset($_SESSION['show_only_active'])) { + $selection = $_SESSION['show_only_active']; + } else { + $selection = true; + } + $args->show_only_active = $_SESSION['show_only_active'] = $selection; + + $gui->tproject_name = $args->tproject_name; + $gui->show_only_active = $args->show_only_active; + $gui->direct_link = $args->direct_link; + $gui->direct_link_ok = $args->direct_link_ok; + $gui->warning_msg = lang_get('no_testplans_available'); + + return array( + $args, + $gui + ); +} + +/** + * + * @param array $tplanMetrics + * @param array $cfg + * @return array + */ +function collectTestProjectMetrics($tplanMetrics, $cfg) +{ + $mm = array(); + $mm['executed']['value'] = getPercentage($tplanMetrics['total']['executed'], + $tplanMetrics['total']['active'], $cfg['round_precision']); + $mm['executed']['label_key'] = 'progress_absolute'; + + foreach ($cfg['statusSetForDisplay'] as $status_verbose => $label_key) { + $mm[$status_verbose]['value'] = getPercentage( + $tplanMetrics['total'][$status_verbose], + $tplanMetrics['total']['active'], $cfg['round_precision']); + $mm[$status_verbose]['label_key'] = $label_key; + } + return $mm; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return boolean + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + $checkOrMode = array( + 'testplan_metrics', + 'testplan_execute' + ); + foreach ($checkOrMode as $right) { + if ($user->hasRightOnProj($db, $right, $context->tproject_id, + $context->tplan_id, $context->getAccessAttr)) { + return true; + } + } + return false; +} ?> diff --git a/lib/results/neverRunByPP.php b/lib/results/neverRunByPP.php index 3348b0ba11..1a758094ca 100644 --- a/lib/results/neverRunByPP.php +++ b/lib/results/neverRunByPP.php @@ -1,537 +1,658 @@ -labels; - -$testCaseCfg = config_get('testcase_cfg'); - -// done here in order to get some config about images -$smarty = new TLSmarty(); - -$doIt = false; -$doChoice = true; - -$metrics = null; - -/* -file_put_contents('/development/tmp/ty.txt', - json_encode(array('$args->platSet' => $args->platSet)) . "\n", - FILE_APPEND); -*/ -if( $args->doAction == 'result' ) { - $metrics = getMetrics($db,$args,$gui); -} - -if( $args->doAction == 'result' && - !is_null($metrics) and count($metrics) > 0 ) { - - $doIt = true; - $doChoice = false; - - $tpl = $tplCfg->default_template; - - $urlSafeString = array(); - $urlSafeString['tprojectPrefix'] = urlencode($gui->tproject_info['prefix']); - $urlSafeString['basehref'] = str_replace(" ", "%20", $args->basehref); - - $out = array(); - $pathCache = $topCache = $levelCache = null; - $nameCache = initNameCache($gui); - - $odx = 0; - foreach($metrics as &$elem) { - // ------------------------------------------- - // do some decode work, using caches - if( !isset($pathCache[$elem['tcase_id']]) ) { - $du = $tcase_mgr->getPathLayered(array($elem['tcase_id'])); - $pathCache[$elem['tcase_id']] = $du[$elem['tsuite_id']]['value']; - $levelCache[$elem['tcase_id']] = $du[$elem['tsuite_id']]['level']; - $ky = current(array_keys($du)); - $topCache[$elem['tcase_id']] = $ky; - } - - // ----------------------------------------------------------- - // IMPORTANT NOTICE: - // - // Column ORDER IS CRITIC - // testTitle CCA-15708: RSRSR-150 - // platformName XXXX <<< ONlY is platforms have been used on - // Test plan under analisys - // - // $out[$odx]['suiteName'] = $pathCache[$exec['tcase_id']]; - - // ------------------------------------------------------------- - $zipper = ''; - switch($args->format) { - case FORMAT_HTML: - $out[$odx]['testTitle'] = ""; - $zipper = ''; - break; - - case FORMAT_XLS: - $out[$odx]['testTitle'] = ''; - break; - - default: - $out[$odx]['testTitle'] = ''; - $zipper = ''; - break; - } - - // See IMPORTANT NOTICE/WARNING about XLS generation - $out[$odx]['testTitle'] .= $elem['full_external_id'] . ':' . - $elem['name'] . $zipper; - - // Insert order on out is CRITIC, because order is used on buildMatrix - if($gui->show_platforms) { - $out[$odx]['platformName'] = - $nameCache['platform'][$elem['platform_id']]; - } - // --------------------------------------------------------- - $odx++; - } - $gui->dataSet = $out; - unset($out); -} - -$gui->urlSendExcelByEmail = $args->basehref . - "lib/results/neverRunByPP.php?" . - "format=" . FORMAT_XLS . "&tplan_id=$gui->tplan_id" . - "&tproject_id=$gui->tproject_id&doAction=result"; - -if( $doIt ) { - switch($args->format) { - case FORMAT_XLS: - createSpreadsheet($gui,$args,$args->getSpreadsheetBy,$cfSet); - break; - - default: - $tableOpt = - array('format' => $args->format, - 'show_platforms' => $gui->show_platforms); - - $gui->tableSet[] = buildMatrix($gui->dataSet, $args, $tableOpt , - $gui->platformSet,$cfSet); - break; - } -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui ); - -if( $doChoice ) { - $tpl = 'neverRunByPPLauncher.tpl'; - $gui->url2call = $args->basehref . - "lib/results/neverRunByPP.php?tplan_id=$gui->tplan_id" . - "&tproject_id=$gui->tproject_id&doAction=result"; -} - -displayReport($tplCfg->template_dir . $tpl, - $smarty, $args->format, $gui->mailCfg); - - -/** - * - * - */ -function init_args(&$dbHandler) { - $iP = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N), - "type" => array(tlInputParameter::STRING_N,0,1), - "platSet" => array(tlInputParameter::ARRAY_INT), - "doAction" => array(tlInputParameter::STRING_N,5,10)); - - $args = new stdClass(); - R_PARAMS($iP,$args); - - $cx = 'sendSpreadSheetByMail_x'; - $args->getSpreadsheetBy = isset($_REQUEST[$cx]) ? 'email' : null; - if( is_null($args->getSpreadsheetBy) ) { - $cx = 'exportSpreadSheet_x'; - $args->getSpreadsheetBy = isset($_REQUEST[$cx]) ? 'download' : null; - } - - $args->addOpAccess = true; - if( !is_null($args->apikey) ) { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } else { - $args->addOpAccess = false; - $cerbero->method = null; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } else { - testlinkInitPage($dbHandler,true,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - $args->user = $_SESSION['currentUser']; - $args->basehref = $_SESSION['basehref']; - - return $args; -} - -/** - * initializeGui - * - */ -function initializeGui(&$dbh,&$argsObj,&$tplanMgr) { - $tprojectMgr = new testproject($dbh); - - $guiObj = new stdClass(); - - $guiObj->labels = init_labels( - array('deleted_user' => null, 'design' => null, - 'execution' => null,'nobody' => null, - 'execution_history' => null, - 'info_notrun_tc_report' => null, - 'title' => 'neverRunByPP_title')); - - $guiObj->title = $guiObj->labels['title']; - $guiObj->pageTitle = $guiObj->title; - $guiObj->report_context = ''; - $guiObj->info_msg = ''; - - $guiObj->tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); - $guiObj->tproject_info = $tprojectMgr->get_by_id($argsObj->tproject_id); - $guiObj->tplan_name = $guiObj->tplan_info['name']; - $guiObj->tproject_name = $guiObj->tproject_info['name']; - - $guiObj->format = $argsObj->format; - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->tplan_id = $argsObj->tplan_id; - $guiObj->apikey = $argsObj->apikey; - - $guiObj->dataSet = null; - $guiObj->type = $argsObj->type; - $guiObj->warning_msg = ''; - - $reportCfg = config_get('reports_list'); - - // needed to decode - $getOpt = array('outputFormat' => 'map', 'addIfNull' => true); - $guiObj->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,$getOpt); - - $guiObj->show_platforms = true; - $pqy = count($guiObj->platformSet); - if( $pqy == 0 || ($pqy == 1) && isset($guiObj->platformSet[0])){ - $guiObj->show_platforms = false; - } - - // will be used when sending mail o creating spreadsheet - $guiObj->platSet = array(); - $pp = (array)array_flip($argsObj->platSet); - if( !isset($pp[0]) ) { - // we have platforms - foreach( $argsObj->platSet as $pk ) { - $guiObj->platSet[$pk] = $pk; - } - } - - $guiObj->mailCfg = buildMailCfg($guiObj); - - return $guiObj; -} - - -/** - * - */ -function checkRights(&$db,&$user,$context = null) { - if(is_null($context)) { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - $check = $user->hasRight($db,'testplan_metrics', - $context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; -} - - -/** - * - * - */ -function buildMailCfg(&$guiObj) { - $labels = array('testplan' => lang_get('testplan'), - 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * Builds ext-js rich table to display matrix results - * - * @param map dataSet: data to be displayed on matrix - * - * return tlExtTable - * - */ -function buildMatrix($dataSet, &$args, $options = array(), $platforms,$customFieldColumns=null) { - $default_options = - array('show_platforms' => false,'format' => FORMAT_HTML); - $options = array_merge($default_options, $options); - - $l18n = init_labels(array('platform' => null)); - $columns = array(); - $columns[] = array('title_key' => 'title_test_case_title', 'width' => 80, 'type' => 'text'); - - if ($options['show_platforms']) { - $columns[] = array('title_key' => 'platform', 'width' => 60, 'filter' => 'list', 'filterOptions' => $platforms); - } - - if ($options['format'] == FORMAT_HTML) { - - // IMPORTANT DEVELOPMENT NOTICE - // columns and dataSet are deeply related this means that inside - // dataSet order has to be identical that on columns or table will be a disaster - // - $matrix = new tlExtTable($columns, $dataSet, 'tl_table_results_by_status'); - - //if not run report: sort by test suite - //blocked, failed report: sort by platform (if enabled) else sort by date - $sort_name = 0; - $sort_name = $options['show_platforms'] ? $l18n['platform'] : ''; - - $matrix->setSortByColumnName($sort_name); - $matrix->addCustomBehaviour('text', array('render' => 'columnWrap')); - - //define table toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - } else { - $matrix = new tlHTMLTable($columns, $dataSet, 'tl_table_results_by_status'); - } - return $matrix; -} - - -/** - * - */ -function initNameCache($guiObj) { - $safeItems = array('platform' => null); - - if($guiObj->show_platforms) { - foreach($guiObj->platformSet as $id => $name) { - $safeItems['platform'][$id] = htmlspecialchars($name); - } - } - - return $safeItems; -} - -/** - * - */ -function createSpreadsheet($gui,$args,$media) { - $lbl = initLblSpreadsheet(); - $cellRange = range('A','Z'); - $style = initStyleSpreadsheet(); - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - - // Step 2 - // data is organized with following columns$dataHeader[] - // Test case - // [Platform] - // - // This is HOW gui->dataSet is organized - // THIS IS CRITIC ?? - // - // testTitle PTRJ-76:Create issue tracker - no conflict - // [platformName] - // - $dataHeader = array($lbl['title_test_case_title']); - if( $showPlatforms = ( property_exists($gui,'platformSet') && - !is_null($gui->platformSet) && !isset($gui->platformSet[0])) ) { - $dataHeader[] = $lbl['platform']; - } - - $startingRow = count($lines2write) + 2; // MAGIC - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet()->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - - // Now process data - $colorChangeCol = 1; - $startingRow++; - $qta_loops = count($gui->dataSet); - $val4color = $gui->dataSet[0][$colorChangeCol]; - for($idx = 0; $idx < $qta_loops; $idx++) { - $line2write = $gui->dataSet[$idx]; - $colCounter = 0; - foreach($line2write as $ldx => $field) { - $cellID = $cellRange[$colCounter] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, html_entity_decode($field) ); - $colCounter++; - } - $cellEnd = $cellRange[$colCounter-1] . $startingRow; - $startingRow++; - } - - // Final step - $objPHPExcel->setActiveSheetIndex(0); - - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - - $codex = 'neverRunByPP'; - $tmpfname = tempnam(config_get('temp_dir'),"$codex.tmp"); - $objWriter->save($tmpfname); - - if($args->getSpreadsheetBy == 'email') { - require_once('email_api.php'); - - $ema = new stdClass(); - $ema->from_address = config_get('from_email'); - $ema->to_address = $args->user->emailAddress;; - $ema->subject = $gui->mailCfg->subject; - $ema->message = $gui->mailCfg->subject; - - $dum = uniqid("$codex_") . '.xls'; - $oops = array('attachment' => - array('file' => $tmpfname, 'newname' => $dum), - 'exit_on_error' => true, 'htmlFormat' => true); - $email_op = email_send_wrapper($ema,$oops); - unlink($tmpfname); - exit(); - } else { - downloadXls($tmpfname,$xlsType,$gui,"$codex_"); - } -} - - -/** - * - */ -function getMetrics(&$dbh,&$args,&$gui) { - $metricsMgr = new tlTestPlanMetrics($dbh); - - $opt = array('output' => 'array'); - $met = $metricsMgr->getNeverRunByPlatform($args->tplan_id,$args->platSet); - - $gui->notRunReport = true; - $gui->info_msg = $gui->labels['info_notrun_tc_report']; - $gui->notesAccessKey = 'summary'; - $gui->userAccessKey = 'user_id'; - - return $met; -} - -/** - * - */ -function initLblSpreadsheet() { - $lbl = init_labels(array('title_test_suite_name' => null,'platform' => null,'build' => null,'th_bugs_id_summary' => null, - 'title_test_case_title' => null,'version' => null, - 'testproject' => null,'generated_by_TestLink_on' => null,'testplan' => null, - 'title_execution_notes' => null, 'th_date' => null, 'th_run_by' => null, - 'assigned_to' => null,'summary' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() { - $sty = array(); - $sty['ReportContext'] = array('font' => array('bold' => true)); - $sty['DataHeader'] = array('font' => array('bold' => true), - 'borders' => array('outline' => array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - - return $sty; -} - -/** - * - */ -function xlsStepOne($oj,$style,$lbl,$gui) { - $dummy = ''; - $lines2write = array(array($gui->title,''), - array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time())), - array($gui->report_context,'')); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0)->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet()->getStyle($cellArea) - ->applyFromArray($style['ReportContext']); - - return $lines2write; -} \ No newline at end of file +labels; + +$testCaseCfg = config_get('testcase_cfg'); + +// done here in order to get some config about images +$smarty = new TLSmarty(); + +$doIt = false; +$doChoice = true; + +$metrics = null; + +if ($args->doAction == 'result') { + $metrics = getMetrics($db, $args, $gui); +} + +if ($args->doAction == 'result' && ! empty($metrics)) { + + $doIt = true; + $doChoice = false; + + $tpl = $tplCfg->default_template; + + $urlSafeString = array(); + $urlSafeString['tprojectPrefix'] = urlencode($gui->tproject_info['prefix']); + $urlSafeString['basehref'] = str_replace(" ", "%20", $args->basehref); + + $out = array(); + $pathCache = $topCache = $levelCache = null; + $nameCache = initNameCache($gui); + + $odx = 0; + foreach ($metrics as &$elem) { + // do some decode work, using caches + if (! isset($pathCache[$elem['tcase_id']])) { + $du = $tcaseMgr->getPathLayered(array( + $elem['tcase_id'] + )); + $pathCache[$elem['tcase_id']] = $du[$elem['tsuite_id']]['value']; + $levelCache[$elem['tcase_id']] = $du[$elem['tsuite_id']]['level']; + $ky = current(array_keys($du)); + $topCache[$elem['tcase_id']] = $ky; + } + + // IMPORTANT NOTICE: + // + // Column ORDER IS CRITIC + // testTitle CCA-15708: RSRSR-150 + // platformName XXXX <<< ONlY is platforms have been used on + // Test plan under analisys + // + // $out[$odx]['suiteName'] = $pathCache[$exec['tcase_id']]; + $zipper = ''; + switch ($args->format) { + case FORMAT_HTML: + $out[$odx]['testTitle'] = ""; + $zipper = ''; + break; + + case FORMAT_XLS: + $out[$odx]['testTitle'] = ''; + break; + + default: + $out[$odx]['testTitle'] = ''; + $zipper = ''; + break; + } + + // See IMPORTANT NOTICE/WARNING about XLS generation + $out[$odx]['testTitle'] .= $elem['full_external_id'] . ':' . + $elem['name'] . $zipper; + + // Insert order on out is CRITIC, because order is used on buildMatrix + if ($gui->show_platforms) { + $out[$odx]['platformName'] = $nameCache['platform'][$elem['platform_id']]; + } + + $odx ++; + } + $gui->dataSet = $out; + unset($out); +} + +$gui->urlSendExcelByEmail = $args->basehref . "lib/results/neverRunByPP.php?" . + "format=" . FORMAT_XLS . "&tplan_id=$gui->tplan_id" . + "&tproject_id=$gui->tproject_id&doAction=result"; + +if ($doIt) { + switch ($args->format) { + case FORMAT_XLS: + createSpreadsheet($gui, $args, $args->getSpreadsheetBy, $cfSet); + break; + + default: + $tableOpt = array( + 'format' => $args->format, + 'show_platforms' => $gui->show_platforms + ); + + $gui->tableSet[] = buildMatrix($gui->dataSet, $args, + $gui->platformSet, $tableOpt); + break; + } +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); + +if ($doChoice) { + $tpl = 'neverRunByPPLauncher.tpl'; + $gui->url2call = $args->basehref . + "lib/results/neverRunByPP.php?tplan_id=$gui->tplan_id" . + "&tproject_id=$gui->tproject_id&format=$gui->format&doAction=result"; +} + +displayReport($tplCfg->template_dir . $tpl, $smarty, $args->format, + $gui->mailCfg); + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iP = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N + ), + "type" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "platSet" => array( + tlInputParameter::ARRAY_INT + ), + "doAction" => array( + tlInputParameter::STRING_N, + 5, + 10 + ) + ); + + $args = new stdClass(); + R_PARAMS($iP, $args); + + $cx = 'sendSpreadSheetByMail_x'; + $args->getSpreadsheetBy = isset($_REQUEST[$cx]) ? 'email' : null; + if (is_null($args->getSpreadsheetBy)) { + $cx = 'exportSpreadSheet_x'; + $args->getSpreadsheetBy = isset($_REQUEST[$cx]) ? 'download' : null; + } + + $args->addOpAccess = true; + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, true, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + $args->user = $_SESSION['currentUser']; + $args->basehref = $_SESSION['basehref']; + + return $args; +} + +/** + * initializeGui + * + * @param database $dbh + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @return stdClass + */ +function initializeGui(&$dbh, &$argsObj, &$tplanMgr) +{ + $tprojectMgr = new testproject($dbh); + + $guiObj = new stdClass(); + + $guiObj->labels = init_labels( + array( + 'deleted_user' => null, + 'design' => null, + 'execution' => null, + 'nobody' => null, + 'execution_history' => null, + 'info_notrun_tc_report' => null, + 'title' => 'neverRunByPP_title' + )); + + $guiObj->title = $guiObj->labels['title']; + $guiObj->pageTitle = $guiObj->title; + $guiObj->report_context = ''; + $guiObj->info_msg = ''; + + $guiObj->tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); + $guiObj->tproject_info = $tprojectMgr->get_by_id($argsObj->tproject_id); + $guiObj->tplan_name = $guiObj->tplan_info['name']; + $guiObj->tproject_name = $guiObj->tproject_info['name']; + + $guiObj->format = $argsObj->format; + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->tplan_id = $argsObj->tplan_id; + $guiObj->apikey = $argsObj->apikey; + + $guiObj->dataSet = null; + $guiObj->type = $argsObj->type; + $guiObj->warning_msg = ''; + + // needed to decode + $getOpt = array( + 'outputFormat' => 'map', + 'addIfNull' => true + ); + $guiObj->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id, $getOpt); + + $guiObj->show_platforms = true; + $pqy = count($guiObj->platformSet); + if ($pqy == 0 || ($pqy == 1) && isset($guiObj->platformSet[0])) { + $guiObj->show_platforms = false; + } + + // will be used when sending mail o creating spreadsheet + $guiObj->platSet = array(); + if (! empty($argsObj->platSet)) { + $pp = (array) array_flip($argsObj->platSet); + } + if (! isset($pp[0])) { + // we have platforms + foreach ($argsObj->platSet as $pk) { + $guiObj->platSet[$pk] = $pk; + } + } + + $guiObj->mailCfg = buildMailCfg($guiObj); + + return $guiObj; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); +} + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * Builds ext-js rich table to display matrix results + * + * @param array $dataSet + * data to be displayed on matrix + * @param stdClass $args + * @param array $options + * @param array $platforms + * @param array $customFieldColumns + * @return tlExtTable|tlHTMLTable + */ +function buildMatrix($dataSet, &$args, $platforms, $options = array()) +{ + $default_options = array( + 'show_platforms' => false, + 'format' => FORMAT_HTML + ); + $options = array_merge($default_options, $options); + + $l18n = init_labels(array( + 'platform' => null + )); + $columns = array(); + $columns[] = array( + 'title_key' => 'title_test_case_title', + 'width' => 80, + 'type' => 'text' + ); + + if ($options['show_platforms']) { + $columns[] = array( + 'title_key' => 'platform', + 'width' => 60, + 'filter' => 'list', + 'filterOptions' => $platforms + ); + } + + if ($options['format'] == FORMAT_HTML) { + + // IMPORTANT DEVELOPMENT NOTICE + // columns and dataSet are deeply related this means that inside + // dataSet order has to be identical that on columns or table will be a disaster + // + $matrix = new tlExtTable($columns, $dataSet, + 'tl_table_results_by_status'); + + // if not run report: sort by test suite + // blocked, failed report: sort by platform (if enabled) else sort by date + $sort_name = 0; + $sort_name = $options['show_platforms'] ? $l18n['platform'] : ''; + + $matrix->setSortByColumnName($sort_name); + $matrix->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + + // define table toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + } else { + $matrix = new tlHTMLTable($columns, $dataSet, + 'tl_table_results_by_status'); + } + return $matrix; +} + +/** + * + * @param stdClass $guiObj + * @return NULL[]|string + */ +function initNameCache($guiObj) +{ + $safeItems = array( + 'platform' => null + ); + + if ($guiObj->show_platforms) { + foreach ($guiObj->platformSet as $id => $name) { + $safeItems['platform'][$id] = htmlspecialchars($name); + } + } + + return $safeItems; +} + +/** + * + * @param stdClass $gui + * @param stdClass $args + * @param string $media + */ +function createSpreadsheet($gui, $args, $media) +{ + $lbl = initLblSpreadsheet(); + $cellRange = range('A', 'Z'); + $style = initStyleSpreadsheet(); + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + + // Step 2 + // data is organized with following columns$dataHeader[] + // Test case + // [Platform] + // + // This is HOW gui->dataSet is organized + // THIS IS CRITIC ?? + // + // testTitle PTRJ-76:Create issue tracker - no conflict + // [platformName] + // + $dataHeader = array( + $lbl['title_test_case_title'] + ); + if (property_exists($gui, 'platformSet') && ! is_null($gui->platformSet) && + ! isset($gui->platformSet[0])) { + $dataHeader[] = $lbl['platform']; + } + + $startingRow = count($lines2write) + 2; + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + // Now process data + $startingRow ++; + $qta_loops = count($gui->dataSet); + for ($idx = 0; $idx < $qta_loops; $idx ++) { + $line2write = $gui->dataSet[$idx]; + $colCounter = 0; + foreach ($line2write as $field) { + $cellID = $cellRange[$colCounter] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + html_entity_decode($field)); + $colCounter ++; + } + $startingRow ++; + } + + // Final step + $objPHPExcel->setActiveSheetIndex(0); + + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + + $codex = 'neverRunByPP'; + $tmpfname = tempnam(config_get('temp_dir'), "$codex.tmp"); + $objWriter->save($tmpfname); + + if ($args->getSpreadsheetBy == 'email') { + require_once 'email_api.php'; + + $ema = new stdClass(); + $ema->from_address = config_get('from_email'); + $ema->to_address = $args->user->emailAddress; + $ema->subject = $gui->mailCfg->subject; + $ema->message = $gui->mailCfg->subject; + + $dum = uniqid("$codex_") . '.xls'; + $oops = array( + 'attachment' => array( + 'file' => $tmpfname, + 'newname' => $dum + ), + 'exit_on_error' => true, + 'htmlFormat' => true + ); + $email_op = email_send_wrapper($ema, $oops); + unlink($tmpfname); + exit(); + } else { + downloadXls($tmpfname, $xlsType, $gui, "$codex_"); + } +} + +/** + * + * @param database $dbh + * @param stdClass $args + * @param stdClass $gui + * @return array + */ +function getMetrics(&$dbh, &$args, &$gui) +{ + $metricsMgr = new tlTestPlanMetrics($dbh); + + $met = $metricsMgr->getNeverRunByPlatform($args->tplan_id, $args->platSet); + + $gui->notRunReport = true; + $gui->info_msg = $gui->labels['info_notrun_tc_report']; + $gui->notesAccessKey = 'summary'; + $gui->userAccessKey = 'user_id'; + + return $met; +} + +/** + */ +function initLblSpreadsheet() +{ + return init_labels( + array( + 'title_test_suite_name' => null, + 'platform' => null, + 'build' => null, + 'th_bugs_id_summary' => null, + 'title_test_case_title' => null, + 'version' => null, + 'testproject' => null, + 'generated_by_TestLink_on' => null, + 'testplan' => null, + 'title_execution_notes' => null, + 'th_date' => null, + 'th_run_by' => null, + 'assigned_to' => null, + 'summary' => null + )); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $sty = array(); + $sty['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $sty['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + + return $sty; +} + +/** + * + * @param PHPExcel $oj + * @param array $style + * @param array $lbl + * @param stdClass $gui + * @return array + */ +function xlsStepOne($oj, $style, $lbl, $gui) +{ + $dummy = ''; + $lines2write = array( + array( + $gui->title, + '' + ), + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ), + array( + $gui->report_context, + '' + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + $cellArea .= "A{$cdx}"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; +} diff --git a/lib/results/overallPieChart.php b/lib/results/overallPieChart.php index d9fa294601..789d1eb3a3 100644 --- a/lib/results/overallPieChart.php +++ b/lib/results/overallPieChart.php @@ -1,135 +1,141 @@ -getExecCountersByExecStatus($args->tplan_id); -unset($totals['total']); - -$values = array(); -$labels = array(); -foreach($totals as $key => $value) -{ - $values[] = $value; - $labels[] = lang_get($resultsCfg['status_label'][$key]) . " ($value)"; - if( isset($resultsCfg['charts']['status_colour'][$key]) ) - { - $series_color[] = $resultsCfg['charts']['status_colour'][$key]; - } +getExecCountersByExecStatus($args->tplan_id); +unset($totals['total']); + +$values = array(); +$labels = array(); +foreach ($totals as $key => $value) { + $values[] = $value; + $labels[] = lang_get($resultsCfg['status_label'][$key]) . " ($value)"; + if (isset($resultsCfg['charts']['status_colour'][$key])) { + $series_color[] = $resultsCfg['charts']['status_colour'][$key]; + } +} + +// Dataset definition +$DataSet = new pData(); +$DataSet->AddPoint($values, "Serie1"); +$DataSet->AddPoint($labels, "Serie8"); +$DataSet->AddAllSeries(); +$DataSet->SetAbsciseLabelSerie("Serie8"); + +// Initialise the graph +$pChartCfg = new stdClass(); +$pChartCfg->XSize = $chart_cfg['XSize'] + 200; +$pChartCfg->YSize = $chart_cfg['YSize']; +$pChartCfg->radius = $chart_cfg['radius']; +$pChartCfg->legendX = $chart_cfg['legendX']; +$pChartCfg->legendY = $chart_cfg['legendY']; + +$pChartCfg->centerX = intval($pChartCfg->XSize / 2); +$pChartCfg->centerY = intval($pChartCfg->YSize / 2); + +$graph = new stdClass(); +$graph->data = $DataSet->GetData(); +$graph->description = $DataSet->GetDataDescription(); + +$Test = new pChart($pChartCfg->XSize, $pChartCfg->YSize); +foreach ($series_color as $key => $hexrgb) { + $rgb = str_split($hexrgb, 2); + $Test->setColorPalette($key, hexdec($rgb[0]), hexdec($rgb[1]), + hexdec($rgb[2])); +} + +// Draw the pie chart +$Test->setFontProperties(config_get('charts_font_path'), + config_get('charts_font_size')); +$Test->AntialiasQuality = 0; +$Test->drawBasicPieGraph($graph->data, $graph->description, $pChartCfg->centerX, + $pChartCfg->centerY, $pChartCfg->radius, PIE_PERCENTAGE, 255, 255, 218); +$Test->drawPieLegend($pChartCfg->legendX, $pChartCfg->legendY, $graph->data, + $graph->description, 250, 250, 250); +$Test->Stroke(); + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); +} + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 0, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + $cerbero->args->getAccessAttr = false; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, true, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + return $args; } - -// Dataset definition -$DataSet = new pData; -$DataSet->AddPoint($values,"Serie1"); -$DataSet->AddPoint($labels,"Serie8"); -$DataSet->AddAllSeries(); -$DataSet->SetAbsciseLabelSerie("Serie8"); - -// Initialise the graph -$pChartCfg = new stdClass(); -$pChartCfg->XSize = $chart_cfg['XSize']; -$pChartCfg->YSize = $chart_cfg['YSize']; -$pChartCfg->radius = $chart_cfg['radius']; -$pChartCfg->legendX = $chart_cfg['legendX']; -$pChartCfg->legendY = $chart_cfg['legendY']; - -$pChartCfg->centerX = intval($pChartCfg->XSize/2); -$pChartCfg->centerY = intval($pChartCfg->YSize/2); - - -$graph = new stdClass(); -$graph->data = $DataSet->GetData(); -$graph->description = $DataSet->GetDataDescription(); - -$Test = new pChart($pChartCfg->XSize,$pChartCfg->YSize); -foreach($series_color as $key => $hexrgb) -{ - $rgb = str_split($hexrgb,2); - $Test->setColorPalette($key,hexdec($rgb[0]),hexdec($rgb[1]),hexdec($rgb[2])); -} - -// Draw the pie chart -$Test->setFontProperties(config_get('charts_font_path'),config_get('charts_font_size')); -$Test->AntialiasQuality = 0; -$Test->drawBasicPieGraph($graph->data,$graph->description, - $pChartCfg->centerX,$pChartCfg->centerY,$pChartCfg->radius,PIE_PERCENTAGE,255,255,218); -$Test->drawPieLegend($pChartCfg->legendX,$pChartCfg->legendY,$graph->data,$graph->description,250,250,250); -$Test->Stroke(); - - -/** - * - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} - - -/** - * - * - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,0,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - $args = new stdClass(); - R_PARAMS($iParams,$args); - - if( !is_null($args->apikey) ) - { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - $args->addOpAccess = false; - $cerbero->method = null; - $cerbero->args->getAccessAttr = false; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } - else - { - testlinkInitPage($dbHandler,true,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - return $args; -} \ No newline at end of file diff --git a/lib/results/platformPieChart.php b/lib/results/platformPieChart.php index 4d1975e096..b557d34766 100644 --- a/lib/results/platformPieChart.php +++ b/lib/results/platformPieChart.php @@ -1,154 +1,161 @@ -getStatusTotalsByPlatformForRender($args->tplan_id); - -// if platform has no test case assigned $dummy->info[$args->platform_id] does not exists -if( isset($dummy->info[$args->platform_id]) ) -{ - $totals = $dummy->info[$args->platform_id]['details']; +getStatusTotalsByPlatformForRender($args->tplan_id); + +// if platform has no test case assigned $dummy->info[$args->platform_id] does not exists +if (isset($dummy->info[$args->platform_id])) { + $totals = $dummy->info[$args->platform_id]['details']; +} else { + // create empty set + $status = $metricsMgr->getStatusForReports(); + foreach ($status as $statusVerbose) { + $totals[$statusVerbose] = array( + 'qty' => 0, + 'percentage' => 0 + ); + } + unset($status); +} + +unset($dummy); +unset($metricsMgr); + +$values = array(); +$labels = array(); +$series_color = array(); +foreach ($totals as $key => $value) { + $value = $value['qty']; + $values[] = $value; + $labels[] = lang_get($resultsCfg['status_label'][$key]) . " ($value)"; + if (isset($resultsCfg['charts']['status_colour'][$key])) { + $series_color[] = $resultsCfg['charts']['status_colour'][$key]; + } +} + +// Dataset definition +$DataSet = new pData(); +$DataSet->AddPoint($values, "Serie1"); +$DataSet->AddPoint($labels, "Serie8"); +$DataSet->AddAllSeries(); +$DataSet->SetAbsciseLabelSerie("Serie8"); + +// Initialise the graph +$pChartCfg = new stdClass(); +$pChartCfg->XSize = $chart_cfg['XSize'] + 200; +$pChartCfg->YSize = $chart_cfg['YSize']; +$pChartCfg->radius = $chart_cfg['radius']; +$pChartCfg->legendX = $chart_cfg['legendX']; +$pChartCfg->legendY = $chart_cfg['legendY']; + +$pChartCfg->centerX = intval($pChartCfg->XSize / 2); +$pChartCfg->centerY = intval($pChartCfg->YSize / 2); + +$graph = new stdClass(); +$graph->data = $DataSet->GetData(); +$graph->description = $DataSet->GetDataDescription(); + +$Test = new pChart($pChartCfg->XSize, $pChartCfg->YSize); +foreach ($series_color as $key => $hexrgb) { + $rgb = str_split($hexrgb, 2); + $Test->setColorPalette($key, hexdec($rgb[0]), hexdec($rgb[1]), + hexdec($rgb[2])); +} + +// Draw the pie chart +$Test->setFontProperties(config_get('charts_font_path'), + config_get('charts_font_size')); +$Test->AntialiasQuality = 0; +$Test->drawBasicPieGraph($graph->data, $graph->description, $pChartCfg->centerX, + $pChartCfg->centerY, $pChartCfg->radius, PIE_PERCENTAGE, 255, 255, 218); +$Test->drawPieLegend($pChartCfg->legendX, $pChartCfg->legendY, $graph->data, + $graph->description, 250, 250, 250); +$Test->Stroke(); + +/** + * + * @param database $db + * @param tlUser $user + * @return unknown + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); +} + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 0, + 64 + ), + "platform_id" => array( + tlInputParameter::INT_N + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + $cerbero->args->getAccessAttr = false; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, true, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + return $args; } -else -{ - // create empty set - $status = $metricsMgr->getStatusForReports(); - foreach($status as $statusVerbose) - { - $totals[$statusVerbose] = array('qty' => 0, 'percentage' => 0); - } - unset($status); -} - -unset($dummy); -unset($metricsMgr); - -$values = array(); -$labels = array(); -$series_color = array(); -foreach($totals as $key => $value) -{ - $value = $value['qty']; - $values[] = $value; - $labels[] = lang_get($resultsCfg['status_label'][$key]) . " ($value)"; - if( isset($resultsCfg['charts']['status_colour'][$key]) ) - { - $series_color[] = $resultsCfg['charts']['status_colour'][$key]; - } -} - -// Dataset definition -$DataSet = new pData; -$DataSet->AddPoint($values,"Serie1"); -$DataSet->AddPoint($labels,"Serie8"); -$DataSet->AddAllSeries(); -$DataSet->SetAbsciseLabelSerie("Serie8"); - -// Initialise the graph -$pChartCfg = new stdClass(); -$pChartCfg->XSize = $chart_cfg['XSize']; -$pChartCfg->YSize = $chart_cfg['YSize']; -$pChartCfg->radius = $chart_cfg['radius']; -$pChartCfg->legendX = $chart_cfg['legendX']; -$pChartCfg->legendY = $chart_cfg['legendY']; - -$pChartCfg->centerX = intval($pChartCfg->XSize/2); -$pChartCfg->centerY = intval($pChartCfg->YSize/2); - -$graph = new stdClass(); -$graph->data = $DataSet->GetData(); -$graph->description = $DataSet->GetDataDescription(); - -$Test = new pChart($pChartCfg->XSize,$pChartCfg->YSize); -foreach($series_color as $key => $hexrgb) -{ - $rgb = str_split($hexrgb,2); - $Test->setColorPalette($key,hexdec($rgb[0]),hexdec($rgb[1]),hexdec($rgb[2])); -} - -// Draw the pie chart -$Test->setFontProperties(config_get('charts_font_path'),config_get('charts_font_size')); -$Test->AntialiasQuality = 0; -$Test->drawBasicPieGraph($graph->data,$graph->description, - $pChartCfg->centerX,$pChartCfg->centerY,$pChartCfg->radius,PIE_PERCENTAGE,255,255,218); -$Test->drawPieLegend($pChartCfg->legendX,$pChartCfg->legendY,$graph->data,$graph->description,250,250,250); -$Test->Stroke(); - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} - - -/** - * - * - */ -function init_args(&$dbHandler) -{ - // $_REQUEST = strings_stripSlashes($_REQUEST); - // $args = new stdClass(); - // $args->tplan_id = $_REQUEST['tplan_id']; - // $args->tproject_id = $_SESSION['testprojectID']; - // $args->platform_id = $_REQUEST['platform_id']; - $iParams = array("apikey" => array(tlInputParameter::STRING_N,0,64), - "platform_id" => array(tlInputParameter::INT_N), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - if( !is_null($args->apikey) ) - { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - $args->addOpAccess = false; - $cerbero->method = null; - $cerbero->args->getAccessAttr = false; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } - else - { - testlinkInitPage($dbHandler,true,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - - return $args; -} \ No newline at end of file diff --git a/lib/results/printDocOptions.php b/lib/results/printDocOptions.php index d2e80afcc3..cd50a3edcc 100644 --- a/lib/results/printDocOptions.php +++ b/lib/results/printDocOptions.php @@ -1,354 +1,373 @@ -doc_type) { - case DOC_TEST_SPEC: - case DOC_REQ_SPEC: - $gui->buildInfoSet = null; - break; - - case DOC_TEST_PLAN_DESIGN: - case DOC_TEST_PLAN_EXECUTION: - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - $tplan_mgr = new testplan($db); - $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); - $testplan_name = htmlspecialchars($tplan_info['name']); - - // 20131201 - do we really need this ? - // $filters = new stdClass(); - // $filters->build_id = $tplan_mgr->get_max_build_id($args->tplan_id); - $gui->buildInfoSet = null; - if( $args->doc_type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { - $gui->buildInfoSet = $tplan_mgr->get_builds($args->tplan_id); - - if( null != $gui->buildInfoSet ) { - $gui->buildRptLinkSet = array(); - $dl = $args->basehref . - "lnl.php?apikey=" . $args->tplan_info['api_key'] . - "&tproject_id=$args->tproject_id" . - "&tplan_id=$args->tplan_id" . - "&type=testreport_onbuild"; - - foreach( $gui->buildInfoSet as $bid => $nunu ) { - $gui->buildRptLinkSet[$bid] = $dl . "&build_id=$bid"; - } - } - } - - $additionalInfo = new stdClass(); - $additionalInfo->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; - $additionalInfo->useColours = COLOR_BY_TC_STATUS_OFF; - - $opt_etree = new stdClass(); - $opt_etree->tc_action_enabled = 0; - $opt_etree->allow_empty_build = 1; - $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; - $opt_etree->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; - - $opt_etree->useColours = new stdClass(); - $opt_etree->useColours->testcases = COLOR_BY_TC_STATUS_OFF; - $opt_etree->useColours->counters = COLOR_BY_TC_STATUS_OFF; - - switch($args->activity) { - case 'addTC': - $opt_etree->hideTestCases = SHOW_TESTCASES; - $opt_etree->tc_action_enabled = false; - $opt_etree->showTestCaseExecStatus = false; - $opt_etree->nodeHelpText = array(); - $opt_etree->nodeHelpText['testproject'] = lang_get('gen_test_plan_design_report'); - $opt_etree->nodeHelpText['testsuite'] = $opt_etree->nodeHelpText['testproject']; - - $opt_etree->actionJS['testproject'] = 'TPLAN_PTP'; - $opt_etree->actionJS['testsuite'] = 'TPLAN_PTS'; - break; - - default: - $opt_etree->hideTestCases = HIDE_TESTCASES; - break; - } - - $filters = null; - $treeContents = null; - list($treeContents, $testcases_to_show) = - testPlanTree($db,$rightPaneAction,$args->tproject_id, - $args->tproject_name,$args->tplan_id, - $testplan_name,$filters,$opt_etree); - - - $gui->ajaxTree = new stdClass(); - $gui->ajaxTree->cookiePrefix = "{$args->doc_type}_tplan_id_{$args->tplan_id}_"; - $gui->ajaxTree->loadFromChildren = true; - $gui->ajaxTree->root_node = $treeContents->rootnode; - $gui->ajaxTree->children = trim($treeContents->menustring); - - if($gui->ajaxTree->children == ''){ - $gui->ajaxTree->children = '{}'; // generate valid JSON - $gui->ajaxTree->root_node->href = ''; - } - break; - - default: - tLog("Argument _REQUEST['type'] has invalid value", 'ERROR'); - exit(); - break; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->assign('args', $gui->getArguments); -$smarty->assign('selFormat', $args->format); -$smarty->assign('docType', $args->doc_type); -$smarty->assign('docTestPlanId', $args->tplan_id); -$smarty->assign('menuUrl', $rightPaneAction); -$smarty->assign('additionalArgs',$additionalArgs); - -$optCfg = new printDocOptions(); -$smarty->assign('printPreferences', $optCfg->getJSPrintPreferences()); - -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * get user input and create an object with properties representing this inputs. - * @return stdClass object - */ -function init_args(&$dbHandler) { - $args = new stdClass(); - $iParams = array("tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N,999), - "type" => array(tlInputParameter::STRING_N,0,100), - "activity" => array(tlInputParameter::STRING_N,1,10)); - - $l18n = array(); - $l18n['addTC'] = lang_get('navigator_add_remove_tcase_to_tplan'); - $l18n['test_plan'] = lang_get('test_plan'); - - - R_PARAMS($iParams,$args); - $args->tproject_id = intval(isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0); - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; - - $args->basehref = $_SESSION['basehref']; - $args->testprojectOptReqs = $_SESSION['testprojectOptions']->requirementsEnabled; - - - $args->format = is_null($args->format) ? FORMAT_HTML : $args->format; - $args->type = is_null($args->type) ? DOC_TEST_PLAN_DESIGN : $args->type; - $args->doc_type = $args->type; - - // Changes to call this page also in add/remove test cases feature - $args->showOptions = true; - $args->showHelpIcon = true; - $args->tplan_info = null; - $args->mainTitle = ''; - - if( ($args->tplan_id = intval($args->tplan_id)) <= 0 || $args->activity != '') { - $args->showOptions = false; - $args->showHelpIcon = false; - $args->tplan_id = intval(isset($_SESSION['testplanID']) ? intval($_SESSION['testplanID']) : 0); - - } - - if($args->tplan_id > 0) { - $tplan_mgr = new testplan($dbHandler); - $args->tplan_info = $tplan_mgr->get_by_id($args->tplan_id); - $args->mainTitle = $l18n['test_plan'] . ': ' . $args->tplan_info['name']; - } - - return $args; -} - - -/** - * Initialize gui (stdClass) object that will be used as argument - * in call to Template Engine. - * - * @param class pointer args: object containing User Input and some session values - * TBD structure - * - * ? tprojectMgr: test project manager object. - * ? treeDragDropEnabled: true/false. Controls Tree drag and drop behaivor. - * - * @return stdClass TBD structure - */ -function initializeGui(&$db,$args) { - $tcaseCfg = config_get('testcase_cfg'); - $reqCfg = config_get('req_cfg'); - - $gui = new stdClass(); - $gui->showOptionsCheckBoxes = $gui->showOptions = $args->showOptions; - - $gui->showHelpIcon = $args->showHelpIcon; - - $gui->mainTitle = ''; - $gui->outputFormat = array(FORMAT_HTML => lang_get('format_html'), - FORMAT_MSWORD => lang_get('format_pseudo_msword')); - - $gui->outputOptions = init_checkboxes($args); - if($gui->showOptions == false) { - $loop2do = count($gui->outputOptions); - for($idx = 0; $idx < $loop2do; $idx++) { - $gui->outputOptions[$idx]['checked'] = 'y'; - } - } - - $tprojectMgr = new testproject($db); - $tcasePrefix = $tprojectMgr->getTestCasePrefix($args->tproject_id); - - $gui->tree_title = ''; - $gui->ajaxTree = new stdClass(); - $gui->ajaxTree->root_node = new stdClass(); - $gui->ajaxTree->dragDrop = new stdClass(); - $gui->ajaxTree->dragDrop->enabled = false; - $gui->ajaxTree->dragDrop->BackEndUrl = null; - $gui->ajaxTree->children = ''; - - // improved cookie prefix for test spec doc and req spec doc - $gui->ajaxTree->cookiePrefix = $args->doc_type . '_doc_'; - $gui->doc_type = $args->doc_type; - - $addTestPlanID = false; - switch($args->doc_type) { - case DOC_REQ_SPEC: - $gui->showOptions = true; - $gui->showOptionsCheckBoxes = false; - $gui->tree_title = lang_get('title_req_print_navigator'); - $gui->ajaxTree->loader = $args->basehref . 'lib/ajax/getrequirementnodes.php?' . - "root_node={$args->tproject_id}&show_children=0&operation=print"; - - $gui->ajaxTree->loadFromChildren = 0; - $gui->ajaxTree->root_node->href = "javascript:TPROJECT_PTP_RS({$args->tproject_id})"; - $gui->ajaxTree->root_node->id = $args->tproject_id; - - $req_qty = $tprojectMgr->count_all_requirements($args->tproject_id); - $gui->ajaxTree->root_node->name = htmlspecialchars($args->tproject_name) . " ($req_qty)"; - $gui->ajaxTree->cookiePrefix .= "tproject_id_" . $gui->ajaxTree->root_node->id . "_" ; - $gui->mainTitle = lang_get('requirement_specification_report'); - break; - - case DOC_TEST_SPEC: - $gui->tree_title = lang_get('title_tc_print_navigator'); - $gui->ajaxTree->loader = $args->basehref . 'lib/ajax/gettprojectnodes.php?' . - "root_node={$args->tproject_id}&" . - "show_tcases=0&operation=print&" . - "tcprefix=". urlencode($tcasePrefix.$tcaseCfg->glue_character) ."}"; - - $gui->ajaxTree->loadFromChildren = 0; - $gui->ajaxTree->root_node->href = "javascript:TPROJECT_PTP({$args->tproject_id})"; - $gui->ajaxTree->root_node->id = $args->tproject_id; - - $tcase_qty = $tprojectMgr->count_testcases($args->tproject_id); - $gui->ajaxTree->root_node->name = htmlspecialchars($args->tproject_name) . " ($tcase_qty)"; - $gui->ajaxTree->cookiePrefix .= "tproject_id_" . $gui->ajaxTree->root_node->id . "_" ; - $gui->mainTitle = lang_get('testspecification_report'); - break; - - case DOC_TEST_PLAN_EXECUTION: - $addTestPlanID = true; - $gui->mainTitle = lang_get('test_report'); - break; - - case DOC_TEST_PLAN_DESIGN: - $addTestPlanID = true; - $gui->tree_title = lang_get('title_tp_print_navigator'); - $gui->ajaxTree->loadFromChildren = 1; - $gui->ajaxTree->loader = ''; - $gui->mainTitle = lang_get('report_test_plan_design'); - break; - - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - $addTestPlanID = true; - $gui->mainTitle = lang_get('test_report_on_build'); - break; - - } - - // Do not move - if($args->mainTitle == '') { - $gui->mainTitle .= ' - ' . lang_get('doc_opt_title'); - } else { - $gui->mainTitle = $args->mainTitle; - } - - $gui->getArguments = "&type=" . $args->doc_type; - if ($addTestPlanID) { - $gui->getArguments .= '&docTestPlanId=' . $args->tplan_id; - } - return $gui; -} - -/** - * Initializes the checkbox options. - * Made this a function to simplify handling of differences - * between printing for requirements and testcases and to make code more readable. - * - * ATTENTION if you add somethin here, you need also to work on javascript function - * tree_getPrintPreferences() - * - * @author Andreas Simon - * - * @param stdClass $args reference to user input parameters - * - * @return array $cbSet - */ -function init_checkboxes(&$args) { - // Important Notice: - // If you want to add or remove elements in this array, you must also update - // $printingOptions in printDocument.php and tree_getPrintPreferences() in testlink_library.js - - $execCfg = config_get('exec_cfg'); - - $optCfg = new printDocOptions(); - - // Check Box Set - $cbSet = array(); - - $cbSet += $optCfg->getDocOpt(); - switch($args->doc_type) { - case 'reqspec': - $cbSet = array_merge($cbSet,$optCfg->getReqSpecOpt()); - break; - - default: - $cbSet = array_merge($cbSet,$optCfg->getTestSpecOpt()); - break; - } - - if( $args->doc_type == DOC_TEST_PLAN_EXECUTION || - $args->doc_type == DOC_TEST_PLAN_EXECUTION_ON_BUILD ) { - $cbSet = array_merge($cbSet,$optCfg->getExecOpt()); - } - - foreach ($cbSet as $key => $elem) { - $cbSet[$key]['description'] = lang_get($elem['description']); - if( !isset($cbSet[$key]['checked']) ) { - $cbSet[$key]['checked'] = 'n'; - } - } - - return $cbSet; +doc_type) { + case DOC_TEST_SPEC: + case DOC_REQ_SPEC: + $gui->buildInfoSet = null; + break; + + case DOC_TEST_PLAN_DESIGN: + case DOC_TEST_PLAN_EXECUTION: + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + $tplan_mgr = new testplan($db); + $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); + $testplan_name = htmlspecialchars($tplan_info['name']); + + // 20131201 - do we really need this ? + // $filters = new stdClass(); + // $filters->build_id = $tplan_mgr->get_max_build_id($args->tplan_id); + $gui->buildInfoSet = null; + if ($args->doc_type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { + $gui->buildInfoSet = $tplan_mgr->get_builds($args->tplan_id); + + if (null != $gui->buildInfoSet) { + $gui->buildRptLinkSet = array(); + $dl = $args->basehref . "lnl.php?apikey=" . + $args->tplan_info['api_key'] . + "&tproject_id=$args->tproject_id" . + "&tplan_id=$args->tplan_id" . "&type=testreport_onbuild"; + + foreach ($gui->buildInfoSet as $bid => $nunu) { + $gui->buildRptLinkSet[$bid] = $dl . "&build_id=$bid"; + } + } + } + + $additionalInfo = new stdClass(); + $additionalInfo->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; + $additionalInfo->useColours = COLOR_BY_TC_STATUS_OFF; + + $opt_etree = new stdClass(); + $opt_etree->tc_action_enabled = 0; + $opt_etree->allow_empty_build = 1; + $opt_etree->getTreeMethod = 'getLinkedForTesterAssignmentTree'; + $opt_etree->useCounters = CREATE_TC_STATUS_COUNTERS_OFF; + + $opt_etree->useColours = new stdClass(); + $opt_etree->useColours->testcases = COLOR_BY_TC_STATUS_OFF; + $opt_etree->useColours->counters = COLOR_BY_TC_STATUS_OFF; + + switch ($args->activity) { + case 'addTC': + $opt_etree->hideTestCases = SHOW_TESTCASES; + $opt_etree->tc_action_enabled = false; + $opt_etree->showTestCaseExecStatus = false; + $opt_etree->nodeHelpText = array(); + $opt_etree->nodeHelpText['testproject'] = lang_get( + 'gen_test_plan_design_report'); + $opt_etree->nodeHelpText['testsuite'] = $opt_etree->nodeHelpText['testproject']; + + $opt_etree->actionJS['testproject'] = 'TPLAN_PTP'; + $opt_etree->actionJS['testsuite'] = 'TPLAN_PTS'; + break; + + default: + $opt_etree->hideTestCases = HIDE_TESTCASES; + break; + } + + $filters = null; + $treeContents = null; + list ($treeContents, $testcases_to_show) = testPlanTree($db, + $rightPaneAction, $args->tproject_id, $args->tproject_name, + $args->tplan_id, $testplan_name, $filters, $opt_etree); + + $gui->ajaxTree = new stdClass(); + $gui->ajaxTree->cookiePrefix = "{$args->doc_type}_tplan_id_{$args->tplan_id}_"; + $gui->ajaxTree->loadFromChildren = true; + $gui->ajaxTree->root_node = $treeContents->rootnode; + $gui->ajaxTree->children = trim($treeContents->menustring); + + if ($gui->ajaxTree->children == '') { + $gui->ajaxTree->children = '{}'; // generate valid JSON + $gui->ajaxTree->root_node->href = ''; + } + break; + + default: + tLog("Argument _REQUEST['type'] has invalid value", 'ERROR'); + exit(); + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('args', $gui->getArguments); +$smarty->assign('selFormat', $args->format); +$smarty->assign('docType', $args->doc_type); +$smarty->assign('docTestPlanId', $args->tplan_id); +$smarty->assign('menuUrl', $rightPaneAction); +$smarty->assign('additionalArgs', $additionalArgs); + +$optCfg = new printDocOptions(); +$smarty->assign('printPreferences', $optCfg->getJSPrintPreferences()); + +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get user input and create an object with properties representing this inputs. + * + * @param database $dbHandler + * @return stdClass object + */ +function initArgs(&$dbHandler) +{ + $args = new stdClass(); + $iParams = array( + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N, + 999 + ), + "type" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "activity" => array( + tlInputParameter::STRING_N, + 1, + 10 + ) + ); + + $l18n = array(); + $l18n['addTC'] = lang_get('navigator_add_remove_tcase_to_tplan'); + $l18n['test_plan'] = lang_get('test_plan'); + + R_PARAMS($iParams, $args); + $args->tproject_id = intval( + isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0); + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; + + $args->basehref = $_SESSION['basehref']; + $args->testprojectOptReqs = $_SESSION['testprojectOptions']->requirementsEnabled; + + $args->format = is_null($args->format) ? FORMAT_HTML : $args->format; + $args->type = is_null($args->type) ? DOC_TEST_PLAN_DESIGN : $args->type; + $args->doc_type = $args->type; + + // Changes to call this page also in add/remove test cases feature + $args->showOptions = true; + $args->showHelpIcon = true; + $args->tplan_info = null; + $args->mainTitle = ''; + + if (($args->tplan_id = intval($args->tplan_id)) <= 0 || $args->activity != '') { + $args->showOptions = false; + $args->showHelpIcon = false; + $args->tplan_id = intval( + isset($_SESSION['testplanID']) ? intval($_SESSION['testplanID']) : 0); + } + + if ($args->tplan_id > 0) { + $tplan_mgr = new testplan($dbHandler); + $args->tplan_info = $tplan_mgr->get_by_id($args->tplan_id); + $args->mainTitle = $l18n['test_plan'] . ': ' . $args->tplan_info['name']; + } + + return $args; +} + +/** + * Initialize gui (stdClass) object that will be used as argument + * in call to Template Engine. + * + * @param database $db + * @param stdClass $args + * object containing User Input and some session values + * TBD structure + * + * ? tprojectMgr: test project manager object. + * ? treeDragDropEnabled: true/false. Controls Tree drag and drop behaivor. + * + * @return stdClass TBD structure + */ +function initializeGui(&$db, $args) +{ + $tcaseCfg = config_get('testcase_cfg'); + + $gui = new stdClass(); + $gui->showOptionsCheckBoxes = $gui->showOptions = $args->showOptions; + + $gui->showHelpIcon = $args->showHelpIcon; + + $gui->mainTitle = ''; + $gui->outputFormat = array( + FORMAT_HTML => lang_get('format_html'), + FORMAT_MSWORD => lang_get('format_pseudo_msword') + ); + + $gui->outputOptions = init_checkboxes($args); + if (! $gui->showOptions) { + $loop2do = count($gui->outputOptions); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $gui->outputOptions[$idx]['checked'] = 'y'; + } + } + + $tprojectMgr = new testproject($db); + $tcasePrefix = $tprojectMgr->getTestCasePrefix($args->tproject_id); + + $gui->tree_title = ''; + $gui->ajaxTree = new stdClass(); + $gui->ajaxTree->root_node = new stdClass(); + $gui->ajaxTree->dragDrop = new stdClass(); + $gui->ajaxTree->dragDrop->enabled = false; + $gui->ajaxTree->dragDrop->BackEndUrl = null; + $gui->ajaxTree->children = ''; + + // improved cookie prefix for test spec doc and req spec doc + $gui->ajaxTree->cookiePrefix = $args->doc_type . '_doc_'; + $gui->doc_type = $args->doc_type; + + $addTestPlanID = false; + switch ($args->doc_type) { + case DOC_REQ_SPEC: + $gui->showOptions = true; + $gui->showOptionsCheckBoxes = false; + $gui->tree_title = lang_get('title_req_print_navigator'); + $gui->ajaxTree->loader = $args->basehref . + 'lib/ajax/getrequirementnodes.php?' . + "root_node={$args->tproject_id}&show_children=0&operation=print"; + + $gui->ajaxTree->loadFromChildren = 0; + $gui->ajaxTree->root_node->href = "javascript:TPROJECT_PTP_RS({$args->tproject_id})"; + $gui->ajaxTree->root_node->id = $args->tproject_id; + + $req_qty = $tprojectMgr->count_all_requirements($args->tproject_id); + $gui->ajaxTree->root_node->name = htmlspecialchars( + $args->tproject_name) . " ($req_qty)"; + $gui->ajaxTree->cookiePrefix .= "tproject_id_" . + $gui->ajaxTree->root_node->id . "_"; + $gui->mainTitle = lang_get('requirement_specification_report'); + break; + + case DOC_TEST_SPEC: + $gui->tree_title = lang_get('title_tc_print_navigator'); + $gui->ajaxTree->loader = $args->basehref . + 'lib/ajax/gettprojectnodes.php?' . + "root_node={$args->tproject_id}&" . + "show_tcases=0&operation=print&" . "tcprefix=" . + urlencode($tcasePrefix . $tcaseCfg->glue_character) . "}"; + + $gui->ajaxTree->loadFromChildren = 0; + $gui->ajaxTree->root_node->href = "javascript:TPROJECT_PTP({$args->tproject_id})"; + $gui->ajaxTree->root_node->id = $args->tproject_id; + + $tcase_qty = $tprojectMgr->count_testcases($args->tproject_id); + $gui->ajaxTree->root_node->name = htmlspecialchars( + $args->tproject_name) . " ($tcase_qty)"; + $gui->ajaxTree->cookiePrefix .= "tproject_id_" . + $gui->ajaxTree->root_node->id . "_"; + $gui->mainTitle = lang_get('testspecification_report'); + break; + + case DOC_TEST_PLAN_EXECUTION: + $addTestPlanID = true; + $gui->mainTitle = lang_get('test_report'); + break; + + case DOC_TEST_PLAN_DESIGN: + $addTestPlanID = true; + $gui->tree_title = lang_get('title_tp_print_navigator'); + $gui->ajaxTree->loadFromChildren = 1; + $gui->ajaxTree->loader = ''; + $gui->mainTitle = lang_get('report_test_plan_design'); + break; + + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + $addTestPlanID = true; + $gui->mainTitle = lang_get('test_report_on_build'); + break; + } + + // Do not move + if ($args->mainTitle == '') { + $gui->mainTitle .= ' - ' . lang_get('doc_opt_title'); + } else { + $gui->mainTitle = $args->mainTitle; + } + + $gui->getArguments = "&type=" . $args->doc_type; + if ($addTestPlanID) { + $gui->getArguments .= '&docTestPlanId=' . $args->tplan_id; + } + return $gui; +} + +/** + * Initializes the checkbox options. + * Made this a function to simplify handling of differences + * between printing for requirements and testcases and to make code more readable. + * + * ATTENTION if you add somethin here, you need also to work on javascript function + * tree_getPrintPreferences() + * + * @author Andreas Simon + * @param stdClass $args + * reference to user input parameters + * @return array $cbSet + */ +function init_checkboxes(&$args) +{ + // Important Notice: + // If you want to add or remove elements in this array, you must also update + // $printingOptions in printDocument.php and tree_getPrintPreferences() in testlink_library.js + $optCfg = new printDocOptions(); + + // Check Box Set + $cbSet = array(); + + $cbSet += $optCfg->getDocOpt(); + switch ($args->doc_type) { + case 'reqspec': + $cbSet = array_merge($cbSet, $optCfg->getReqSpecOpt()); + break; + + default: + $cbSet = array_merge($cbSet, $optCfg->getTestSpecOpt()); + break; + } + + if ($args->doc_type == DOC_TEST_PLAN_EXECUTION || + $args->doc_type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { + $cbSet = array_merge($cbSet, $optCfg->getExecOpt()); + } + + foreach ($cbSet as $key => $elem) { + $cbSet[$key]['description'] = lang_get($elem['description']); + if (! isset($cbSet[$key]['checked'])) { + $cbSet[$key]['checked'] = 'n'; + } + } + + return $cbSet; } diff --git a/lib/results/printDocument.php b/lib/results/printDocument.php index 7a50a9ca39..a99c2235e5 100644 --- a/lib/results/printDocument.php +++ b/lib/results/printDocument.php @@ -1,743 +1,838 @@ -tree_manager; -list($doc_info,$my) = initEnv($db,$args,$tproject_mgr,$args->user_id); - -$printingOptions = initPrintOpt($_REQUEST,$doc_info); - -$subtree = $tree_manager->get_subtree($args->itemID,$my['filters'],$my['options']); - -$treeForPlatform[0] = &$subtree; -$doc_info->title = $doc_info->tproject_name; -$doc_info->outputFormat = $printingOptions['outputFormat'] = $args->format; - -switch ($doc_info->type) { - case DOC_REQ_SPEC: - switch($doc_info->content_range) { - case 'reqspec': - $spec_mgr = new requirement_spec_mgr($db); - $spec = $spec_mgr->get_by_id($args->itemID); - unset($spec_mgr); - - $spec['childNodes'] = isset($subtree['childNodes']) ? $subtree['childNodes'] : null; - $spec['node_type_id'] = $decode['node_descr_id']['requirement_spec']; - - unset($treeForPlatform[0]['childNodes']); - $treeForPlatform[0]['childNodes'][0] = &$spec; - - $doc_info->title = htmlspecialchars($args->tproject_name . - $tlCfg->gui_title_separator_2 . $spec['title']); - break; - } // $doc_info->content_range - break; - - case DOC_TEST_SPEC: - $printingOptions['importance'] = $doc_info->test_priority_enabled; - - switch($doc_info->content_range) { - case 'testsuite': - $tsuite = new testsuite($db); - $tInfo = $tsuite->get_by_id($args->itemID); - $tInfo['childNodes'] = isset($subtree['childNodes']) ? $subtree['childNodes'] : null; - - $treeForPlatform[0]['childNodes'] = array($tInfo); - - $doc_info->title = htmlspecialchars(isset($tInfo['name']) ? $args->tproject_name . - $tlCfg->gui_title_separator_2.$tInfo['name'] : $args->tproject_name); - break; - } - break; - - case DOC_TEST_PLAN_DESIGN: - $printingOptions['metrics'] = true; // FORCE - - case DOC_TEST_PLAN_EXECUTION: - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - $tplan_mgr = new testplan($db); - $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); - - if($args->build_id > 0) { - $xx = $tplan_mgr->get_builds($args->tplan_id,null,null,array('buildID' => $args->build_id)); - $doc_info->build_name = htmlspecialchars($xx[$args->build_id]['name']); - $doc_info->build_notes = $xx[$args->build_id]['notes']; - } - - $doc_info->testplan_name = htmlspecialchars($tplan_info['name']); - $doc_info->testplan_scope = $tplan_info['notes']; - $doc_info->title = $doc_info->testplan_name; - - // Changed to get ALL platform attributes. - $getOpt = array('outputFormat' => 'mapAccessByID', 'addIfNull' => true); - $platforms = $tplan_mgr->getPlatforms($args->tplan_id,$getOpt); - $platformIDSet = array_keys($platforms); - - $printingOptions['priority'] = $doc_info->test_priority_enabled; - $items2use = (object) array('estimatedExecTime' => null,'realExecTime' => null); - $treeForPlatform = array(); - - $filters = null; - $ctx = new stdClass(); - $ctx->tplan_id = $args->tplan_id; - $ctx->platformIDSet = $platformIDSet; - $opx = null; - - if( $doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD ) { - $ctx->build_id = ($args->build_id > 0) ? $args->build_id : null; - - $opx = array('setAssignedTo' => false); - $ctx->with_user_assignment = $args->with_user_assignment; - if( $ctx->build_id > 0 ) { - if( $args->with_user_assignment ) { - $opx = array('setAssignedTo' => true); - } - } - } - - // var_dump($opx); die(); - - switch($doc_info->content_range) { - case 'testproject': - $treeForPlatform = buildContentForTestPlan($db,$subtree,$ctx,$decode, - $tplan_mgr,$filters,$opx); - break; - - case 'testsuite': - $ctx->branchRoot = $args->itemID; - $opx = array_merge((array)$opx,(array)$my['options']['prepareNode']); - list($treeForPlatform,$items2use) = - buildContentForTestPlanBranch($db,$subtree,$ctx,$doc_info,$decode,$tplan_mgr,$opx); - break; - } - - // Create list of execution id, that will be used to compute execution time if - // CF_EXEC_TIME custom field exists and is linked to current testproject - $doc_data->statistics = null; - if ($printingOptions['metrics']) { - $target = new stdClass(); - $target->tplan_id = $args->tplan_id; - $target->build_id = $args->build_id; - $target->platform_id = isset($args->platform_id) ? $args->platform_id : null; - $doc_data->statistics = timeStatistics($items2use,$target,$decode,$tplan_mgr); - } - break; +tree_manager; +list ($doc_info, $my) = initEnv($db, $args, $tproject_mgr, $args->user_id); + +$printingOptions = initPrintOpt($_REQUEST, $doc_info); + +$subtree = $tree_manager->get_subtree($args->itemID, $my['filters'], + $my['options']); + +$treeForPlatform[0] = &$subtree; +$doc_info->title = $doc_info->tproject_name; +$doc_info->outputFormat = $printingOptions['outputFormat'] = $args->format; + +switch ($doc_info->type) { + case DOC_REQ_SPEC: + switch ($doc_info->content_range) { + case 'reqspec': + $spec_mgr = new requirement_spec_mgr($db); + $spec = $spec_mgr->get_by_id($args->itemID); + unset($spec_mgr); + + $spec['childNodes'] = isset($subtree['childNodes']) ? $subtree['childNodes'] : null; + $spec['node_type_id'] = $decode['node_descr_id']['requirement_spec']; + + unset($treeForPlatform[0]['childNodes']); + $treeForPlatform[0]['childNodes'][0] = &$spec; + + $doc_info->title = htmlspecialchars( + $args->tproject_name . $tlCfg->gui_title_separator_2 . + $spec['title']); + break; + } + break; + + case DOC_TEST_SPEC: + $printingOptions['importance'] = $doc_info->test_priority_enabled; + + switch ($doc_info->content_range) { + case 'testsuite': + $tsuite = new testsuite($db); + $tInfo = $tsuite->get_by_id($args->itemID); + $tInfo['childNodes'] = isset($subtree['childNodes']) ? $subtree['childNodes'] : null; + + $treeForPlatform[0]['childNodes'] = array( + $tInfo + ); + + $doc_info->title = htmlspecialchars( + isset($tInfo['name']) ? $args->tproject_name . + $tlCfg->gui_title_separator_2 . $tInfo['name'] : $args->tproject_name); + break; + } + break; + + case DOC_TEST_PLAN_DESIGN: + $printingOptions['metrics'] = true; // FORCE + + case DOC_TEST_PLAN_EXECUTION: + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + $tplan_mgr = new testplan($db); + $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); + + if ($args->build_id > 0) { + $xx = $tplan_mgr->get_builds($args->tplan_id, null, null, + array( + 'buildID' => $args->build_id + )); + $doc_info->build_name = htmlspecialchars( + $xx[$args->build_id]['name']); + $doc_info->build_notes = $xx[$args->build_id]['notes']; + } + + $doc_info->testplan_name = htmlspecialchars($tplan_info['name']); + $doc_info->testplan_scope = $tplan_info['notes']; + $doc_info->title = $doc_info->testplan_name; + + // Changed to get ALL platform attributes. + $getOpt = array( + 'outputFormat' => 'mapAccessByID', + 'addIfNull' => true + ); + $platforms = $tplan_mgr->getPlatforms($args->tplan_id, $getOpt); + $platformIDSet = array_keys($platforms); + + $printingOptions['priority'] = $doc_info->test_priority_enabled; + $items2use = (object) array( + 'estimatedExecTime' => null, + 'realExecTime' => null + ); + $treeForPlatform = array(); + + $filters = null; + $ctx = new stdClass(); + $ctx->tplan_id = $args->tplan_id; + $ctx->platformIDSet = $platformIDSet; + $opx = null; + + if ($doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { + $ctx->build_id = ($args->build_id > 0) ? $args->build_id : null; + + $opx = array( + 'setAssignedTo' => false + ); + $ctx->with_user_assignment = $args->with_user_assignment; + if ($ctx->build_id > 0 && $args->with_user_assignment) { + $opx = array( + 'setAssignedTo' => true + ); + } + } + + switch ($doc_info->content_range) { + case 'testproject': + $treeForPlatform = buildContentForTestPlan($db, $subtree, $ctx, + $tplan_mgr, $filters, $opx); + break; + + case 'testsuite': + $ctx->branchRoot = $args->itemID; + $opx = array_merge((array) $opx, + (array) $my['options']['prepareNode']); + list ($treeForPlatform, $items2use) = buildContentForTestPlanBranch( + $db, $subtree, $ctx, $doc_info, $decode, $tplan_mgr, $opx); + break; + } + + // Create list of execution id, that will be used to compute execution time if + // CF_EXEC_TIME custom field exists and is linked to current testproject + $doc_data->statistics = null; + if ($printingOptions['metrics']) { + $target = new stdClass(); + $target->tplan_id = $args->tplan_id; + $target->build_id = $args->build_id; + $target->platform_id = isset($args->platform_id) ? $args->platform_id : null; + $doc_data->statistics = timeStatistics($items2use, $target, $decode, + $tplan_mgr); + } + break; +} + +// ----- rendering logic ----- +$topText = renderHTMLHeader($doc_info->type . ' ' . $doc_info->title, + $_SESSION['basehref'], $doc_info->type); +$topText .= renderFirstPage($doc_info); + +// Init table of content (TOC) data +renderTOC($printingOptions); // @TODO check if is really useful + +$tocPrefix = null; +if ($showPlatforms = ! isset($treeForPlatform[0]) ? true : false) { + $tocPrefix = 0; +} + +if ($treeForPlatform) { + // Things that have to be printed just once + switch ($doc_info->type) { + case DOC_TEST_PLAN_DESIGN: + $printingOptions['metrics'] = true; // FORCED + + case DOC_TEST_PLAN_EXECUTION: + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + $docText .= renderTestProjectItem($doc_info); + $docText .= renderTestPlanItem($doc_info); + + if ($doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { + $docText .= renderBuildItem($doc_info); + } + + $cfieldFormatting = array( + 'table_css_style' => 'class="cf"' + ); + if ($printingOptions['cfields']) { + $cfields = $tplan_mgr->html_table_of_custom_field_values( + $args->tplan_id, 'design', null, $cfieldFormatting); + $docText .= '

    ' . $cfields . '

    '; + } + break; + } + + $actionContext = (array) $args; + foreach ($treeForPlatform as $platform_id => $tree2work) { + $actionContext['platform_id'] = $platform_id; + + if (isset($tree2work['childNodes']) && + count($tree2work['childNodes']) > 0) { + $tree2work['name'] = $args->tproject_name; + $tree2work['id'] = $args->tproject_id; + $tree2work['node_type_id'] = $decode['node_descr_id']['testproject']; + switch ($doc_info->type) { + case DOC_REQ_SPEC: + $docText .= renderReqSpecTreeForPrinting($db, $tree2work, + $printingOptions, null, 0, 1, $args->user_id, 0, + $args->tproject_id); + break; + + case DOC_TEST_SPEC: + $docText .= renderSimpleChapter(lang_get('scope'), + $doc_info->tproject_scope); + + $env = new stdClass(); + $env->base_href = $_SESSION['basehref']; + $env->item_type = $doc_info->content_range; + $env->tocPrefix = null; + $env->tocCounter = 0; + $env->user_id = $args->user_id; + $env->reportType = $doc_info->type; + + // force hidding of execution related info + $printingOptions['passfail'] = false; + $printingOptions['step_exec_notes'] = false; + $printingOptions['step_exec_status'] = false; + + $actionContext['level'] = 0; + $indentLevelStart = 1; + $docText .= renderTestSpecTreeForPrinting($db, $tree2work, + $printingOptions, $env, $actionContext, $env->tocPrefix, + $indentLevelStart); + break; + + case DOC_TEST_PLAN_DESIGN: + case DOC_TEST_PLAN_EXECUTION: + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + + $tocPrefix ++; + $env = new stdClass(); + $env->base_href = $_SESSION['basehref']; + $env->item_type = $doc_info->content_range; + $env->tocPrefix = $tocPrefix; + $env->user_id = $args->user_id; + $env->testCounter = 1; + $env->reportType = $doc_info->type; + + if ($showPlatforms) { + $printingOptions['showPlatformNotes'] = true; + $docText .= renderPlatformHeading($tocPrefix, + $platforms[$platform_id], $printingOptions); + } + + $actionContext['level'] = 0; + $docText .= renderTestPlanForPrinting($db, $tree2work, + $printingOptions, $env, $actionContext); + + if ($printingOptions['metrics']) { + $docText .= buildTestPlanMetrics($doc_data->statistics, + $platform_id); + } + break; + } + } + } +} +$docText .= renderEOF(); + +// Needed for platform feature +if ($printingOptions['toc']) { + $printingOptions['tocCode'] .= '
    '; + $topText .= $printingOptions['tocCode']; +} +$docText = $topText . $docText; + +// add application header to HTTP +if (($args->format == FORMAT_ODT) || ($args->format == FORMAT_MSWORD)) { + flushHttpHeader($args->format, $doc_info->type); +} + +// send out the data +echo $docText; + +/** + * Process input data + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "build_id" => array( + tlInputParameter::INT_N + ), + "docTestPlanId" => array( + tlInputParameter::INT_N + ), + "id" => array( + tlInputParameter::INT_N + ), + "type" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + "format" => array( + tlInputParameter::INT_N + ), + "level" => array( + tlInputParameter::STRING_N, + 0, + 32 + ), + "with_user_assignment" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + // really UGLY HACK + $typeDomain = array( + 'test_plan' => 'testplan', + 'test_report' => 'testreport' + ); + $args->type = isset($typeDomain[$args->type]) ? $typeDomain[$args->type] : $args->type; + + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + $args->itemID = $args->tproject_id; + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tplan_id = isset($_REQUEST['docTestPlanId']) ? intval( + $_REQUEST['docTestPlanId']) : 0; + $args->itemID = $args->id; + } + + $tproject_mgr = new testproject($dbHandler); + + if ($args->tproject_id > 0) { + $dummy = $tproject_mgr->get_by_id($args->tproject_id); + $args->tproject_name = $dummy['name']; + } else { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid Test Project ID ({$args->tproject_id})"; + throw new Exception($msg); + } + + $args->doc_type = $args->type; + $args->user_id = isset($_SESSION['userID']) ? intval($_SESSION['userID']) : null; + + $resultsCfg = config_get('results'); + $dcd = array(); + $dcd['node_descr_id'] = $tproject_mgr->tree_manager->get_available_node_types(); + $dcd['node_id_descr'] = array_flip($dcd['node_descr_id']); + + $dcd['status_descr_code'] = $resultsCfg['status_code']; + $dcd['status_code_descr'] = array_flip($dcd['status_descr_code']); + + return array( + $args, + $tproject_mgr, + $dcd + ); +} + +/** + * + * @uses init_checkboxes() - printDocOptions.php + * + */ +function initPrintOpt(&$uiHash, &$docInfo) +{ + $optObj = new printDocOptions(); + $pOpt = $optObj->getAllOptVars(); + + $lightOn = isset($uiHash['allOptionsOn']); + foreach ($pOpt as $opt => $val) { + $pOpt[$opt] = $lightOn || + (isset($uiHash[$opt]) && ($uiHash[$opt] == 'y')); + } + $pOpt['docType'] = $docInfo->type; + $pOpt['tocCode'] = ''; // to avoid warning because of undefined index + + return $pOpt; +} + +/** + */ +function getDecode(&$treeMgr) +{ + $resultsCfg = config_get('results'); + + $dcd = array(); + $dcd['node_descr_id'] = $treeMgr->get_available_node_types(); + $dcd['node_id_descr'] = array_flip($dcd['node_descr_id']); + + $dcd['status_descr_code'] = $resultsCfg['status_code']; + $dcd['status_code_descr'] = array_flip($dcd['status_descr_code']); + + return $dcd; +} + +/** + * + * @internal revisions: + * + */ +function initEnv(&$dbHandler, &$argsObj, &$tprojectMgr, $userID) +{ + $my = array(); + $doc = new stdClass(); + + $my['options'] = array( + 'recursive' => true, + 'prepareNode' => null, + 'order_cfg' => array( + "type" => 'spec_order' + ) + ); + $my['filters'] = array( + 'exclude_node_types' => array( + 'testplan' => 'exclude me', + 'requirement_spec' => 'exclude me', + 'requirement' => 'exclude me' + ), + 'exclude_children_of' => array( + 'testcase' => 'exclude my children', + 'requirement_spec' => 'exclude my children' + ) + ); + + $lblKey = array( + DOC_TEST_SPEC => 'title_test_spec', + DOC_TEST_PLAN_DESIGN => 'report_test_plan_design', + DOC_TEST_PLAN_EXECUTION => 'report_test_plan_execution', + DOC_TEST_PLAN_EXECUTION_ON_BUILD => 'report_test_plan_execution_on_build', + DOC_REQ_SPEC => 'req_spec' + ); + + $doc->content_range = $argsObj->level; + $doc->type = $argsObj->doc_type; + $doc->type_name = lang_get($lblKey[$doc->type]); + $doc->additional_info = $argsObj->with_user_assignment ? lang_get( + 'only_test_cases_wta') : ''; + $doc->author = ''; + $doc->title = ''; + + switch ($doc->type) { + case DOC_TEST_PLAN_DESIGN: + $my['options']['order_cfg'] = array( + "type" => 'exec_order', + "tplan_id" => $argsObj->tplan_id + ); + break; + + case DOC_TEST_PLAN_EXECUTION: + case DOC_TEST_PLAN_EXECUTION_ON_BUILD: + $my['options']['order_cfg'] = array( + "type" => 'exec_order', + "tplan_id" => $argsObj->tplan_id + ); + $my['options']['prepareNode'] = array( + 'viewType' => 'executionTree' + ); + break; + + case DOC_REQ_SPEC: + $my['filters'] = array( + 'exclude_node_types' => array( + 'testplan' => 'exclude me', + 'testsuite' => 'exclude me', + 'testcase' => 'exclude me' + ), + 'exclude_children_of' => array( + 'testcase' => 'exclude my children', + 'testsuite' => 'exclude my children', + 'requirement' => 'exclude my children' + ) + ); + break; + } + + $user = tlUser::getById($dbHandler, $userID); + if ($user) { + $doc->author = htmlspecialchars($user->getDisplayName()); + } + unset($user); + + $dummy = $tprojectMgr->get_by_id($argsObj->tproject_id); + $doc->tproject_name = htmlspecialchars($dummy['name']); + $doc->tproject_scope = $dummy['notes']; + + $doc->test_priority_enabled = $dummy['opt']->testPriorityEnabled; + + return array( + $doc, + $my + ); +} + +/** + */ +function getStatsEstimatedExecTime(&$tplanMgr, &$items2use, $tplanID) +{ + $min = array(); + $stat = null; + if (is_null($items2use)) { + // will work on all test cases present on Test Plan. + // these IDs will be searched inside get_estimated_execution_time() + $min = $tplanMgr->get_estimated_execution_time($tplanID); + } else { + $min['totalMinutes'] = 0; + $min['totalTestCases'] = 0; + $min['platform'] = array(); + foreach ($items2use as $platID => $itemsForPlat) { + if (! is_null($itemsForPlat)) { + $tmp = $tplanMgr->get_estimated_execution_time($tplanID, + $itemsForPlat, $platID); + $min['platform'][$platID] = $tmp['platform'][$platID]; + $min['totalMinutes'] += $tmp['totalMinutes']; + $min['totalTestCases'] += $tmp['totalTestCases']; + } + } + } + + if ($min['totalMinutes'] != "0") { + $stat['minutes'] = $min['totalMinutes']; + $stat['tcase_qty'] = $min['totalTestCases']; + + foreach ($min['platform'] as $platformID => $elem) { + $stat['platform'][$platformID] = $elem; + } + } + return $stat; +} + +/** + */ +function getStatsRealExecTime(&$tplanMgr, &$lastExecBy, $context, $decode) +{ + $min = array(); + $stat = null; + $executed_qty = 0; + $items2use = array(); + + if (! is_null($lastExecBy) && ! empty($lastExecBy)) { + // divide execution by Platform ID + $p2loop = array_keys($lastExecBy); + foreach ($p2loop as $platfID) { + if (! is_null($lastExecBy[$platfID])) { + $i2loop = array_keys($lastExecBy[$platfID]); + $items2use[$platfID] = null; + foreach ($i2loop as $xdx) { + $info = &$lastExecBy[$platfID][$xdx]; + if ($info['exec_status'] != + $decode['status_descr_code']['not_run']) { + $items2use[$platfID][] = $info['exec_id']; + $executed_qty ++; + } + } + } + } + + if ($executed_qty > 0) { + $min['totalMinutes'] = 0; + $min['totalTestCases'] = 0; + $min['platform'] = array(); + $ecx = $context; + + foreach ($items2use as $platID => $itemsForPlat) { + $min['platform'][$platID] = null; + if (! is_null($itemsForPlat)) { + $ecx->platform_id = $platID; + + $tmp = $tplanMgr->getExecutionTime($context, $itemsForPlat); + + $min['platform'][$platID] = $tmp['platform'][$platID]; + $min['totalMinutes'] += isset($tmp['totalMinutes']) ? $tmp['totalMinutes'] : 0; + $min['totalTestCases'] += $tmp['totalTestCases']; + } + } + } + } else { + $min = $tplanMgr->getExecutionTime($context); + } + + // Arrange data for caller + if (isset($min['totalMinutes']) && $min['totalMinutes'] != 0) { + $stat['minutes'] = $min['totalMinutes']; + $stat['tcase_qty'] = $min['totalTestCases']; + + foreach ($min['platform'] as $platformID => $elem) { + $stat['platform'][$platformID] = $elem; + } + } + return $stat; +} + +/** + */ +function buildContentForTestPlan(&$dbHandler, $itemsTree, $ctx, &$tplanMgr, + $pnFilters = null, $opt = null) +{ + $linkedBy = array(); + $contentByPlatform = array(); + + $tplanID = $ctx->tplan_id; + $platformIDSet = $ctx->platformIDSet; + + $my['opt'] = array( + 'setAssignedTo' => false + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + // due to Platforms we need to use 'viewType' => 'executionTree', + // if not we get ALWAYS the same set of test cases linked to test plan + // for each platform -> WRONG + $pnOptions = array( + 'hideTestCases' => 0, + 'showTestCaseID' => 1, + 'viewType' => 'executionTree', + 'getExternalTestCaseID' => 0, + 'ignoreInactiveTestCases' => 0 + ); + + $pnOptions['setAssignedTo'] = $my['opt']['setAssignedTo']; + + $filters = null; + $px = 'with_user_assignment'; + if (property_exists($ctx, $px) && $ctx->$px == 0) { + $filters = null; + } else { + $px = 'build_id'; + if (property_exists($ctx, $px)) { + $filters = array( + $px => $ctx->$px + ); + } + } + + foreach ($platformIDSet as $platform_id) { + $filters['platform_id'] = $platform_id; + $linkedBy[$platform_id] = $tplanMgr->getLinkedStaticView($tplanID, + $filters); + + // IMPORTANT NOTE: + // We are in a loop and we use tree on prepareNode, that changes it, + // then we can not use anymore a reference BUT WE NEED A COPY. + $tree2work = $itemsTree; + if (! $linkedBy[$platform_id]) { + $tree2work['childNodes'] = null; + } + + $dummy4reference = null; + prepareNode($dbHandler, $tree2work, $dummy4reference, $dummy4reference, + $linkedBy[$platform_id], $pnFilters, $pnOptions); + + $contentByPlatform[$platform_id] = $tree2work; + } + + return $contentByPlatform; +} + +/** + */ +function buildContentForTestPlanBranch(&$dbHandler, $itemsTree, $ctx, &$docInfo, + $decode, &$tplanMgr, $options = null) +{ + $linkedBy = array(); + $branch_tsuites = null; + $contentByPlatform = array(); + + $branchRoot = &$ctx->branchRoot; + $tplanID = &$ctx->tplan_id; + $platformIDSet = &$ctx->platformIDSet; + + $pnOptions = array( + 'hideTestCases' => 0, + 'setAssignedTo' => false + ); + $pnOptions = array_merge($pnOptions, (array) $options); + + $tsuite = new testsuite($dbHandler); + $tInfo = $tsuite->get_by_id($branchRoot); + $tInfo['node_type_id'] = $decode['node_descr_id']['testsuite']; + $docInfo->title = htmlspecialchars( + isset($tInfo['name']) ? $tInfo['name'] : $docInfo->testplan_name); + + $children_tsuites = $tsuite->tree_manager->get_subtree_list($branchRoot, + $decode['node_descr_id']['testsuite']); + if (! is_null($children_tsuites) && trim($children_tsuites) != "") { + $branch_tsuites = explode(',', $children_tsuites); + } + $branch_tsuites[] = $branchRoot; + + $metrics = (object) array( + 'estimatedExecTime' => null, + 'realExecTime' => null + ); + $filters = array( + 'tsuites_id' => $branch_tsuites + ); + + $getLTCVOpt['addExecInfo'] = true; + if ($docInfo->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { + $getLTCVOpt['addExecInfo'] = true; + $getLTCVOpt['ua_user_alias'] = ' AS assigned_to '; + $getLTCVOpt['ua_force_join'] = true; + + $getLTCVOpt['assigned_on_build'] = $ctx->build_id; + $filters['build_id'] = $ctx->build_id; + } + + foreach ($platformIDSet as $platform_id) { + // IMPORTANTE NOTICE: + // This need to be initialized on each iteration because prepareNode() make changes on it. + $tInfo['childNodes'] = isset($itemsTree['childNodes']) ? $itemsTree['childNodes'] : null; + + $filters['platform_id'] = $platform_id; + $metrics->estimatedExecTime[$platform_id] = null; + $metrics->realExecTime[$platform_id] = null; + + $avalon = $tplanMgr->getLTCVNewGeneration($tplanID, $filters, + $getLTCVOpt); + if (! is_null($avalon)) { + $k2l = array_keys($avalon); + foreach ($k2l as $key) { + $linkedBy[$platform_id][$key] = $avalon[$key][$platform_id]; + } + } else { + $linkedBy[$platform_id] = null; + } + + // After architecture changes on how CF design values for Test Cases are + // managed, we need the test case version ID and not test case ID + // In addition if we loop over Platforms we need to save this set each time!!! + $items2loop = ! is_null($linkedBy[$platform_id]) ? array_keys( + $linkedBy[$platform_id]) : null; + if (! is_null($items2loop)) { + foreach ($items2loop as $rdx) { + $metrics->estimatedExecTime[$platform_id][] = $linkedBy[$platform_id][$rdx]['tcversion_id']; + } + } + + // Prepare Node -> pn + $pnFilters = null; + $dummy4reference = null; + $contentByPlatform[$platform_id]['childNodes'] = array(); + + if (! is_null($linkedBy[$platform_id])) { + prepareNode($dbHandler, $tInfo, $dummy4reference, $dummy4reference, + $linkedBy[$platform_id], $pnFilters, $pnOptions); + + $contentByPlatform[$platform_id]['childNodes'] = array( + $tInfo + ); + } + } + + $metrics->realExecTime = $linkedBy; + return array( + $contentByPlatform, + $metrics + ); +} + +/** + */ +function timeStatistics($items, $context, $decode, $tplanMgr) +{ + $stats = array(); + $stats['estimated_execution'] = getStatsEstimatedExecTime($tplanMgr, + $items->estimatedExecTime, $context->tplan_id); + + $stats['real_execution'] = getStatsRealExecTime($tplanMgr, + $items->realExecTime, $context, $decode); + return $stats; +} + +/** + * rights check function for testlinkInitPage() + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); } - - -// ----- rendering logic ----- -$topText = renderHTMLHeader($doc_info->type . ' ' . $doc_info->title,$_SESSION['basehref'],$doc_info->type); -$topText .= renderFirstPage($doc_info); - -// Init table of content (TOC) data -renderTOC($printingOptions); // @TODO check if is really useful - -$tocPrefix = null; -if( ($showPlatforms = !isset($treeForPlatform[0]) ? true : false) ) { - $tocPrefix = 0; -} - -if ($treeForPlatform) { - // Things that have to be printed just once - // - switch ($doc_info->type) { - case DOC_TEST_PLAN_DESIGN: - $printingOptions['metrics'] = true; // FORCED - - case DOC_TEST_PLAN_EXECUTION: - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - $docText .= renderTestProjectItem($doc_info); - $docText .= renderTestPlanItem($doc_info); - - if($doc_info->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { - $docText .= renderBuildItem($doc_info); - } - - $cfieldFormatting=array('table_css_style' => 'class="cf"'); - if ($printingOptions['cfields']) { - $cfields = $tplan_mgr->html_table_of_custom_field_values($args->tplan_id,'design',null,$cfieldFormatting); - $docText .= '

    ' . $cfields . '

    '; - } - break; - } - - - $actionContext = (array)$args; - foreach ($treeForPlatform as $platform_id => $tree2work) { - $actionContext['platform_id'] = $platform_id; - - if(isset($tree2work['childNodes']) && sizeof($tree2work['childNodes']) > 0) { - $tree2work['name'] = $args->tproject_name; - $tree2work['id'] = $args->tproject_id; - $tree2work['node_type_id'] = $decode['node_descr_id']['testproject']; - switch ($doc_info->type) { - case DOC_REQ_SPEC: - $docText .= renderReqSpecTreeForPrinting($db, $tree2work, $printingOptions, - null, 0, 1, $args->user_id,0,$args->tproject_id); - break; - - case DOC_TEST_SPEC: - $docText .= renderSimpleChapter(lang_get('scope'), $doc_info->tproject_scope); - - $env = new stdClass(); - $env->base_href = $_SESSION['basehref']; - $env->item_type = $doc_info->content_range; - $env->tocPrefix = null; - $env->tocCounter = 0; - $env->user_id = $args->user_id; - $env->reportType = $doc_info->type; - - // force hidding of execution related info - $printingOptions['passfail'] = false; - $printingOptions['step_exec_notes'] = false; - $printingOptions['step_exec_status'] = false; - - - $actionContext['level'] = 0; - $indentLevelStart = 1; - $docText .= renderTestSpecTreeForPrinting($db,$tree2work,$printingOptions,$env,$actionContext, - $env->tocPrefix,$indentLevelStart); - break; - - case DOC_TEST_PLAN_DESIGN: - case DOC_TEST_PLAN_EXECUTION: - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - - $tocPrefix++; - $env = new stdClass(); - $env->base_href = $_SESSION['basehref']; - $env->item_type = $doc_info->content_range; - $env->tocPrefix = $tocPrefix; - $env->user_id = $args->user_id; - $env->testCounter = 1; - $env->reportType = $doc_info->type; - - if ($showPlatforms) { - $printingOptions['showPlatformNotes'] = true; - $docText .= renderPlatformHeading($tocPrefix,$platforms[$platform_id],$printingOptions); - } - - $actionContext['level'] = 0; - $docText .= renderTestPlanForPrinting($db,$tree2work,$printingOptions,$env,$actionContext); - - if( $printingOptions['metrics'] ) { - $docText .= buildTestPlanMetrics($doc_data->statistics,$platform_id); - } - break; - } - } - } -} -$docText .= renderEOF(); - -// Needed for platform feature -if ($printingOptions['toc']) { - $printingOptions['tocCode'] .= '
    '; - $topText .= $printingOptions['tocCode']; -} -$docText = $topText . $docText; - - -// add application header to HTTP -if (($args->format == FORMAT_ODT) || ($args->format == FORMAT_MSWORD)) -{ - flushHttpHeader($args->format, $doc_info->type); -} - -// send out the data -echo $docText; - - -/** - * Process input data - * - **/ -function init_args(&$dbHandler) { - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "build_id" => array(tlInputParameter::INT_N), - "docTestPlanId" => array(tlInputParameter::INT_N), - "id" => array(tlInputParameter::INT_N), - "type" => array(tlInputParameter::STRING_N,0,20), - "format" => array(tlInputParameter::INT_N), - "level" => array(tlInputParameter::STRING_N,0,32), - "with_user_assignment" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - - // really UGLY HACK - $typeDomain = array('test_plan' => 'testplan','test_report' => 'testreport'); - $args->type = isset($typeDomain[$args->type]) ? $typeDomain[$args->type] : $args->type; - - if( !is_null($args->apikey) ) { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } else { - $args->addOpAccess = false; - $cerbero->method = null; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - $args->itemID = $args->tproject_id; - } - else { - testlinkInitPage($dbHandler,false,false,"checkRights"); - - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tplan_id = isset($_REQUEST['docTestPlanId']) ? intval($_REQUEST['docTestPlanId']) : 0; - $args->itemID = $args->id; - } - - $tproject_mgr = new testproject($dbHandler); - - if($args->tproject_id > 0) { - $dummy = $tproject_mgr->get_by_id($args->tproject_id); - $args->tproject_name = $dummy['name']; - } else { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid Test Project ID ({$args->tproject_id})"; - throw new Exception($msg); - } - - $args->doc_type = $args->type; - $args->user_id = isset($_SESSION['userID']) ? intval($_SESSION['userID']) : null; - - - $resultsCfg = config_get('results'); - $dcd = array(); - $dcd['node_descr_id'] = $tproject_mgr->tree_manager->get_available_node_types(); - $dcd['node_id_descr'] = array_flip($dcd['node_descr_id']); - - $dcd['status_descr_code'] = $resultsCfg['status_code']; - $dcd['status_code_descr'] = array_flip($dcd['status_descr_code']); - - return array($args,$tproject_mgr,$dcd); -} - - -/** - * @uses init_checkboxes() - printDocOptions.php - * - **/ -function initPrintOpt(&$UIhash,&$docInfo) { - - $optObj = new printDocOptions(); - $pOpt = $optObj->getAllOptVars(); - - $lightOn = isset($UIhash['allOptionsOn']); - foreach($pOpt as $opt => $val) { - $pOpt[$opt] = $lightOn || (isset($UIhash[$opt]) && ($UIhash[$opt] == 'y')); - } - $pOpt['docType'] = $docInfo->type; - $pOpt['tocCode'] = ''; // to avoid warning because of undefined index - - return $pOpt; -} - - -/** - * - * - **/ -function getDecode(&$treeMgr) -{ - - $resultsCfg = config_get('results'); - - $dcd = array(); - $dcd['node_descr_id'] = $treeMgr->get_available_node_types(); - $dcd['node_id_descr'] = array_flip($dcd['node_descr_id']); - - $dcd['status_descr_code'] = $resultsCfg['status_code']; - $dcd['status_code_descr'] = array_flip($dcd['status_descr_code']); - - return $dcd; -} - -/** - * - * @internal revisions: - * - **/ -function initEnv(&$dbHandler,&$argsObj,&$tprojectMgr,$userID) { - - $my = array(); - $doc = new stdClass(); - - $my['options'] = array('recursive' => true, 'prepareNode' => null, - 'order_cfg' => array("type" =>'spec_order') ); - $my['filters'] = array('exclude_node_types' => array('testplan'=>'exclude me', - 'requirement_spec'=>'exclude me', - 'requirement'=>'exclude me'), - 'exclude_children_of' => array('testcase'=>'exclude my children', - 'requirement_spec'=> 'exclude my children')); - - $lblKey = array(DOC_TEST_SPEC => 'title_test_spec', DOC_TEST_PLAN_DESIGN => 'report_test_plan_design', - DOC_TEST_PLAN_EXECUTION => 'report_test_plan_execution', - DOC_TEST_PLAN_EXECUTION_ON_BUILD => 'report_test_plan_execution_on_build', - DOC_REQ_SPEC => 'req_spec'); - - - $doc->content_range = $argsObj->level; - $doc->type = $argsObj->doc_type; - $doc->type_name = lang_get($lblKey[$doc->type]); - $doc->additional_info = $argsObj->with_user_assignment ? - lang_get('only_test_cases_wta') : ''; - $doc->author = ''; - $doc->title = ''; - - switch ($doc->type) { - case DOC_TEST_PLAN_DESIGN: - $my['options']['order_cfg'] = array("type" =>'exec_order',"tplan_id" => $argsObj->tplan_id); - break; - - case DOC_TEST_PLAN_EXECUTION: - case DOC_TEST_PLAN_EXECUTION_ON_BUILD: - $my['options']['order_cfg'] = array("type" =>'exec_order', - "tplan_id" => $argsObj->tplan_id); - $my['options']['prepareNode'] = array('viewType' => 'executionTree'); - break; - - case DOC_REQ_SPEC: - $my['filters'] = array('exclude_node_types' => array('testplan'=>'exclude me', - 'testsuite'=>'exclude me', - 'testcase'=>'exclude me'), - 'exclude_children_of' => array('testcase'=>'exclude my children', - 'testsuite'=> 'exclude my children', - 'requirement'=>'exclude my children')); - break; - } - - - $user = tlUser::getById($dbHandler,$userID); - if ($user) { - $doc->author = htmlspecialchars($user->getDisplayName()); - } - unset($user); - - $dummy = $tprojectMgr->get_by_id($argsObj->tproject_id); - $doc->tproject_name = htmlspecialchars($dummy['name']); - $doc->tproject_scope = $dummy['notes']; - - $doc->test_priority_enabled = $dummy['opt']->testPriorityEnabled; - - return array($doc,$my); -} - - -/** - * - * - **/ -function getStatsEstimatedExecTime(&$tplanMgr,&$items2use,$tplanID) -{ - - $min = array(); - $stat = null; - if( is_null($items2use) ) - { - // will work on all test cases present on Test Plan. - // these IDs will be searched inside get_estimated_execution_time() - $min = $tplanMgr->get_estimated_execution_time($tplanID); - } - else - { - $min['totalMinutes'] = 0; - $min['totalTestCases'] = 0; - $min['platform'] = array(); - foreach( $items2use as $platID => $itemsForPlat ) - { - if( !is_null($itemsForPlat) ) - { - $tmp = $tplanMgr->get_estimated_execution_time($tplanID,$itemsForPlat,$platID); - $min['platform'][$platID] = $tmp['platform'][$platID]; - $min['totalMinutes'] += $tmp['totalMinutes']; - $min['totalTestCases'] += $tmp['totalTestCases']; - } - } - } - - if ($min['totalMinutes'] != "0") - { - $stat['minutes'] = $min['totalMinutes']; - $stat['tcase_qty'] = $min['totalTestCases']; - - foreach($min['platform'] as $platformID => $elem) - { - $stat['platform'][$platformID] = $elem; - } - } - return $stat; -} - - -/** - * - * - **/ -function getStatsRealExecTime(&$tplanMgr,&$lastExecBy,$context,$decode) { - $min = array(); - $stat = null; - $executed_qty = 0; - $items2use = array(); - - if( !is_null($lastExecBy) && count($lastExecBy) > 0 ) { - // divide execution by Platform ID - $p2loop = array_keys($lastExecBy); - foreach($p2loop as $platfID) { - if( !is_null($lastExecBy[$platfID]) ) { - $i2loop = array_keys($lastExecBy[$platfID]); - $items2use[$platfID] = null; - foreach($i2loop as $xdx) { - $info = &$lastExecBy[$platfID][$xdx]; - if( $info['exec_status'] != $decode['status_descr_code']['not_run'] ) { - $items2use[$platfID][] = $info['exec_id']; - $executed_qty++; - } - } - } - } - - if( $executed_qty > 0) { - $min['totalMinutes'] = 0; - $min['totalTestCases'] = 0; - $min['platform'] = array(); - $ecx = new stdClass(); - $ecx = $context; - - foreach( $items2use as $platID => $itemsForPlat ) { - $min['platform'][$platID] = null; - if( !is_null($itemsForPlat) ) { - $ecx->platform_id = $platID; - - $tmp = $tplanMgr->getExecutionTime($context,$itemsForPlat); - - $min['platform'][$platID] = $tmp['platform'][$platID]; - $min['totalMinutes'] += isset($tmp['totalMinutes']) ? $tmp['totalMinutes'] : 0; - $min['totalTestCases'] += $tmp['totalTestCases']; - } - } - } - } else { - $min = $tplanMgr->getExecutionTime($context); - } - - // Arrange data for caller - if (isset($min['totalMinutes']) && $min['totalMinutes'] != 0) { - $stat['minutes'] = $min['totalMinutes']; - $stat['tcase_qty'] = $min['totalTestCases']; - - foreach($min['platform'] as $platformID => $elem) { - $stat['platform'][$platformID] = $elem; - } - } - return $stat; -} - - -/** - * - */ -function buildContentForTestPlan(&$dbHandler,$itemsTree,$ctx,$decode, - &$tplanMgr,$pnFilters=null,$opt=null) { - - $linkedBy = array(); - $contentByPlatform = array(); - - $tplanID = $ctx->tplan_id; - $platformIDSet = $ctx->platformIDSet; - - $my['opt'] = array('setAssignedTo' => false); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - // due to Platforms we need to use 'viewType' => 'executionTree', - // if not we get ALWAYS the same set of test cases linked to test plan - // for each platform -> WRONG - $pnOptions = array('hideTestCases' => 0, 'showTestCaseID' => 1, - 'viewType' => 'executionTree', - 'getExternalTestCaseID' => 0, 'ignoreInactiveTestCases' => 0); - - $pnOptions['setAssignedTo'] = $my['opt']['setAssignedTo']; - - $filters = null; - $px = 'with_user_assignment'; - if( property_exists($ctx, $px) && $ctx->$px == 0 ){ - $filters = null; - } else { - $px = 'build_id'; - if( property_exists($ctx, $px) ) { - $filters = array($px => $ctx->$px); - } - } - - foreach($platformIDSet as $platform_id) { - $filters['platform_id'] = $platform_id; - $linkedBy[$platform_id] = $tplanMgr->getLinkedStaticView($tplanID,$filters); - - // IMPORTANT NOTE: - // We are in a loop and we use tree on prepareNode, that changes it, - // then we can not use anymore a reference BUT WE NEED A COPY. - $tree2work = $itemsTree; - if (!$linkedBy[$platform_id]) { - $tree2work['childNodes'] = null; - } - - $dummy4reference = null; - prepareNode($dbHandler,$tree2work,$dummy4reference,$dummy4reference,$linkedBy[$platform_id],$pnFilters,$pnOptions); - - $contentByPlatform[$platform_id] = $tree2work; - } - - return $contentByPlatform; -} - - -/** - * - */ -function buildContentForTestPlanBranch(&$dbHandler,$itemsTree,$ctx,&$docInfo,$decode, - &$tplanMgr,$options=null) -{ - $linkedBy = array(); - $branch_tsuites = null; - $contentByPlatform = array(); - - $branchRoot = &$ctx->branchRoot; - $tplanID = &$ctx->tplan_id; - $platformIDSet = &$ctx->platformIDSet; - - $pnOptions = array('hideTestCases' => 0,'setAssignedTo' => false); - $pnOptions = array_merge($pnOptions, (array)$options); - - $tsuite = new testsuite($dbHandler); - $tInfo = $tsuite->get_by_id($branchRoot); - $tInfo['node_type_id'] = $decode['node_descr_id']['testsuite']; - $docInfo->title = htmlspecialchars(isset($tInfo['name']) ? $tInfo['name'] : $docInfo->testplan_name); - - $children_tsuites = $tsuite->tree_manager->get_subtree_list($branchRoot,$decode['node_descr_id']['testsuite']); - if( !is_null($children_tsuites) and trim($children_tsuites) != "") - { - $branch_tsuites = explode(',',$children_tsuites); - } - $branch_tsuites[]=$branchRoot; - - $metrics = (object) array('estimatedExecTime' => null,'realExecTime' => null); - $filters = array( 'tsuites_id' => $branch_tsuites); - - $getLTCVOpt['addExecInfo'] = true; - if($docInfo->type == DOC_TEST_PLAN_EXECUTION_ON_BUILD) { - $getLTCVOpt['addExecInfo'] = true; - $getLTCVOpt['ua_user_alias'] = ' AS assigned_to '; - $getLTCVOpt['ua_force_join'] = true; - - $getLTCVOpt['assigned_on_build'] = $ctx->build_id; - $filters['build_id'] = $ctx->build_id; - } - - foreach($platformIDSet as $platform_id) { - // IMPORTANTE NOTICE: - // This need to be initialized on each iteration because prepareNode() make changes on it. - $tInfo['childNodes'] = isset($itemsTree['childNodes']) ? $itemsTree['childNodes'] : null; - - $filters['platform_id'] = $platform_id; - $metrics->estimatedExecTime[$platform_id] = null; - $metrics->realExecTime[$platform_id] = null; - - $avalon = $tplanMgr->getLTCVNewGeneration($tplanID, $filters, $getLTCVOpt); - if(!is_null($avalon)) { - $k2l = array_keys($avalon); - foreach($k2l as $key) { - $linkedBy[$platform_id][$key] = $avalon[$key][$platform_id]; - } - } else { - $linkedBy[$platform_id] = null; - } - - // After architecture changes on how CF design values for Test Cases are - // managed, we need the test case version ID and not test case ID - // In addition if we loop over Platforms we need to save this set each time!!! - $items2loop = !is_null($linkedBy[$platform_id]) ? array_keys($linkedBy[$platform_id]) : null; - if( !is_null($items2loop) ) { - foreach($items2loop as $rdx) { - $metrics->estimatedExecTime[$platform_id][] = $linkedBy[$platform_id][$rdx]['tcversion_id']; - } - } - - // Prepare Node -> pn - $pnFilters = null; - $dummy4reference = null; - $contentByPlatform[$platform_id]['childNodes'] = array(); - - if(!is_null($linkedBy[$platform_id])) { - prepareNode($dbHandler,$tInfo,$dummy4reference,$dummy4reference, - $linkedBy[$platform_id],$pnFilters,$pnOptions); - - $contentByPlatform[$platform_id]['childNodes'] = array($tInfo); - } - } - - $metrics->realExecTime = $linkedBy; - return array($contentByPlatform,$metrics); -} - -/** - * - */ -function timeStatistics($items,$context,$decode,$tplanMgr) { - $stats = array(); - $stats['estimated_execution'] = - getStatsEstimatedExecTime($tplanMgr,$items->estimatedExecTime,$context->tplan_id); - - $stats['real_execution'] = getStatsRealExecTime($tplanMgr,$items->realExecTime,$context,$decode); - return $stats; -} - - - -/* - * rights check function for testlinkInitPage() - */ -function checkRights(&$db,&$user,$context = null) -{ - if(is_null($context)) - { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; -} \ No newline at end of file diff --git a/lib/results/priorityBarChart.php b/lib/results/priorityBarChart.php index 1ec19c8f52..d706c95fa1 100644 --- a/lib/results/priorityBarChart.php +++ b/lib/results/priorityBarChart.php @@ -1,100 +1,200 @@ -get_by_id($tplan_id); -$tproject_info = $tproject_mgr->get_by_id($tproject_id); - -$re = new results($db, $tplan_mgr, $tproject_info, $tplan_info, - ALL_TEST_SUITES,ALL_BUILDS,ALL_PLATFORMS); - -/** -* KEYWORDS REPORT -*/ -$arrDataKeys = $re->getAggregateKeywordResults(); -$i = 0; -$arrDataKeys2 = null; - -if ($arrDataKeys != null) { - while ($keywordId = key($arrDataKeys)) { - $arr = $arrDataKeys[$keywordId]; - $arrDataKeys2[$i] = $arr; - $i++; - next($arrDataKeys); - } -} - - - -$namesOfKeywordsArray = array(); -$namesOfKeywordsArray[0] = ""; - -$passArray = array(); -$passArray[0] = "pass"; - -$failArray = array(); -$failArray[0] = "fail"; - -$blockedArray = array(); -$blockedArray[0] = "blocked"; - -$notRunArray = array(); -$notRunArray[0] = "not run"; - -for ($i = 0 ; $i < sizeOf($arrDataKeys); $i++) { - $keywordArr = $arrDataKeys2[$i]; - $namesOfKeywordsArray[$i + 1] = $keywordArr[0]; - // $total = $keywordArr[1]; - $passArray[$i + 1] = $keywordArr[2]; - $failArray[$i + 1] = $keywordArr[3]; - $blockedArray[$i + 1] = $keywordArr[4]; - $notRunArray[$i + 1] = $keywordArr[5]; - // $percentComplete = $keywordArr[6]; -} - -$chart[ 'chart_data' ] = array ($namesOfKeywordsArray, $passArray,$failArray, $blockedArray,$notRunArray); - -/** -END NEW STUFF -*/ - - - -$chart[ 'axis_value' ] = array ( 'font'=>"arial", 'bold'=>true, 'size'=>10, 'color'=>"000000", 'alpha'=>50, 'steps'=>6, 'prefix'=>"", 'suffix'=>"", 'decimals'=>0, 'separator'=>"", 'show_min'=>true ); - -$chart[ 'chart_border' ] = array ( 'color'=>"000000", 'top_thickness'=>0, 'bottom_thickness'=>3, 'left_thickness'=>0, 'right_thickness'=>0 ); - -// $chart[ 'chart_data' ] = array ( array ( "", "P1", "P2", "P3" ), array ( "pass", 1, 5, 10), array ( "fail", 1, 5, 10), array ("blocked", 1, 5, 10), array ("not run", 1, 5, 10)); - - - - - - -$chart[ 'chart_grid_h' ] = array ( 'alpha'=>20, 'color'=>"000000", 'thickness'=>1, 'type'=>"solid" ); -$chart[ 'chart_grid_v' ] = array ( 'alpha'=>20, 'color'=>"000000", 'thickness'=>1, 'type'=>"dashed" ); -$chart[ 'chart_rect' ] = array ( 'x'=>125, 'y'=>65, 'width'=>250, 'height'=>200, 'positive_color'=>"ffffff", 'negative_color'=>"000000", 'positive_alpha'=>75, 'negative_alpha'=>15 ); -$chart[ 'chart_transition' ] = array ( 'type'=>"drop", 'delay'=>0, 'duration'=>2, 'order'=>"series" ); -$chart[ 'chart_type' ] = "stacked column"; - -$chart[ 'draw' ] = array ( array ( 'transition'=>"slide_up", 'delay'=>1, 'duration'=>.5, 'type'=>"text", 'color'=>"000033", 'alpha'=>15, 'font'=>"arial", 'rotation'=>-90, 'bold'=>true, 'size'=>64, 'x'=>0, 'y'=>295, 'width'=>300, 'height'=>50, 'text'=>"Keywords", 'h_align'=>"right", 'v_align'=>"middle" ), - array ( 'transition'=>"slide_up", 'delay'=>1, 'duration'=>.5, 'type'=>"text", 'color'=>"ffffff", 'alpha'=>40, 'font'=>"arial", 'rotation'=>-90, 'bold'=>true, 'size'=>25, 'x'=>30, 'y'=>300, 'width'=>300, 'height'=>50, 'text'=>"report", 'h_align'=>"right", 'v_align'=>"middle" ) ); - -$chart[ 'legend_label' ] = array ( 'layout'=>"horizontal", 'font'=>"arial", 'bold'=>true, 'size'=>13, 'color'=>"444466", 'alpha'=>90 ); -$chart[ 'legend_rect' ] = array ( 'x'=>125, 'y'=>10, 'width'=>250, 'height'=>10, 'margin'=>5, 'fill_color'=>"ffffff", 'fill_alpha'=>35, 'line_color'=>"000000", 'line_alpha'=>0, 'line_thickness'=>0 ); -$chart[ 'legend_transition' ] = array ( 'type'=>"slide_left", 'delay'=>0, 'duration'=>1 ); - -$chart[ 'series_color' ] = array ("00FF00", "FF0000", "0000FF", "000000"); - -SendChartData ( $chart ); +get_by_id($tplan_id); +$tproject_info = $tproject_mgr->get_by_id($tproject_id); + +$re = new results($db, $tplan_mgr, $tproject_info, $tplan_info, ALL_TEST_SUITES, + ALL_BUILDS, ALL_PLATFORMS); + +/** + * KEYWORDS REPORT + */ +$arrDataKeys = $re->getAggregateKeywordResults(); +$i = 0; +$arrDataKeys2 = null; + +if ($arrDataKeys != null) { + while ($keywordId = key($arrDataKeys)) { + $arr = $arrDataKeys[$keywordId]; + $arrDataKeys2[$i] = $arr; + $i ++; + next($arrDataKeys); + } +} + +$namesOfKeywordsArray = array(); +$namesOfKeywordsArray[0] = ""; + +$passArray = array(); +$passArray[0] = "pass"; + +$failArray = array(); +$failArray[0] = "fail"; + +$blockedArray = array(); +$blockedArray[0] = "blocked"; + +$notRunArray = array(); +$notRunArray[0] = "not run"; + +for ($i = 0; $i < count($arrDataKeys); $i ++) { + $keywordArr = $arrDataKeys2[$i]; + $namesOfKeywordsArray[$i + 1] = $keywordArr[0]; + $passArray[$i + 1] = $keywordArr[2]; + $failArray[$i + 1] = $keywordArr[3]; + $blockedArray[$i + 1] = $keywordArr[4]; + $notRunArray[$i + 1] = $keywordArr[5]; +} + +$chart['chart_data'] = array( + $namesOfKeywordsArray, + $passArray, + $failArray, + $blockedArray, + $notRunArray +); + +/** + * END NEW STUFF + */ + +$chart['axis_value'] = array( + 'font' => "arial", + 'bold' => true, + 'size' => 10, + 'color' => "000000", + 'alpha' => 50, + 'steps' => 6, + 'prefix' => "", + 'suffix' => "", + 'decimals' => 0, + 'separator' => "", + 'show_min' => true +); + +$chart['chart_border'] = array( + 'color' => "000000", + 'top_thickness' => 0, + 'bottom_thickness' => 3, + 'left_thickness' => 0, + 'right_thickness' => 0 +); + +$chart['chart_grid_h'] = array( + 'alpha' => 20, + 'color' => "000000", + 'thickness' => 1, + 'type' => "solid" +); +$chart['chart_grid_v'] = array( + 'alpha' => 20, + 'color' => "000000", + 'thickness' => 1, + 'type' => "dashed" +); +$chart['chart_rect'] = array( + 'x' => 125, + 'y' => 65, + 'width' => 250, + 'height' => 200, + 'positive_color' => "ffffff", + 'negative_color' => "000000", + 'positive_alpha' => 75, + 'negative_alpha' => 15 +); +$chart['chart_transition'] = array( + 'type' => "drop", + 'delay' => 0, + 'duration' => 2, + 'order' => "series" +); +$chart['chart_type'] = "stacked column"; + +$chart['draw'] = array( + array( + 'transition' => "slide_up", + 'delay' => 1, + 'duration' => .5, + 'type' => "text", + 'color' => "000033", + 'alpha' => 15, + 'font' => "arial", + 'rotation' => - 90, + 'bold' => true, + 'size' => 64, + 'x' => 0, + 'y' => 295, + 'width' => 300, + 'height' => 50, + 'text' => "Keywords", + 'h_align' => "right", + 'v_align' => "middle" + ), + array( + 'transition' => "slide_up", + 'delay' => 1, + 'duration' => .5, + 'type' => "text", + 'color' => "ffffff", + 'alpha' => 40, + 'font' => "arial", + 'rotation' => - 90, + 'bold' => true, + 'size' => 25, + 'x' => 30, + 'y' => 300, + 'width' => 300, + 'height' => 50, + 'text' => "report", + 'h_align' => "right", + 'v_align' => "middle" + ) +); + +$chart['legend_label'] = array( + 'layout' => "horizontal", + 'font' => "arial", + 'bold' => true, + 'size' => 13, + 'color' => "444466", + 'alpha' => 90 +); +$chart['legend_rect'] = array( + 'x' => 125, + 'y' => 10, + 'width' => 250, + 'height' => 10, + 'margin' => 5, + 'fill_color' => "ffffff", + 'fill_alpha' => 35, + 'line_color' => "000000", + 'line_alpha' => 0, + 'line_thickness' => 0 +); +$chart['legend_transition'] = array( + 'type' => "slide_left", + 'delay' => 0, + 'duration' => 1 +); + +$chart['series_color'] = array( + "00FF00", + "FF0000", + "0000FF", + "000000" +); + +SendChartData($chart); ?> diff --git a/lib/results/resultsBugs.php b/lib/results/resultsBugs.php index c491de60b6..b54dab7a52 100644 --- a/lib/results/resultsBugs.php +++ b/lib/results/resultsBugs.php @@ -1,268 +1,285 @@ -warning_msg = ''; -$gui->tableSet = null; - -$templateCfg = templateConfiguration(); -$args = init_args(); - -// get issue tracker config and object to manage TestLink - BTS integration -$its = null; -$tproject_mgr = new testproject($db); -$info = $tproject_mgr->get_by_id($args->tproject_id); -$gui->bugInterfaceOn = $info['issue_tracker_enabled']; -if( $info['issue_tracker_enabled']) { - $it_mgr = new tlIssueTracker($db); - $its = $it_mgr->getInterfaceObject($args->tproject_id); - unset($it_mgr); -} - -$smarty = new TLSmarty; -$img = $smarty->getImages(); -$openBugs = array(); -$resolvedBugs = array(); -$arrData = array(); - -$tplan_mgr = new testplan($db); -$metricsMgr = new tlTestPlanMetrics($db); -$tproject_mgr = new testproject($db); - -$tplan_info = $tplan_mgr->get_by_id($args->tplan_id); -$tproject_info = $tproject_mgr->get_by_id($args->tproject_id); -unset($tproject_mgr); - -// $filters = array(); -// $options = array('output' => 'array', 'only_executed' => true, 'details' => 'full'); -// $execSet = $tplan_mgr->get_linked_tcversions($args->tplan_id, $filters, $options); - -switch($args->verboseType) -{ - case 'all': - $execSet = $tplan_mgr->getAllExecutionsWithBugs($args->tplan_id); - break; - - case 'latest': - default: - $execSet = (array)$metricsMgr->getLTCVNewGeneration($args->tplan_id,null, - array('addExecInfo' => true, 'accessKeyType' => 'index', - 'specViewFields' => true, 'testSuiteInfo' => true, - 'includeNotRun' => false)); - break; - - +warning_msg = ''; +$gui->tableSet = null; + +$templateCfg = templateConfiguration(); +$args = initArgs(); + +// get issue tracker config and object to manage TestLink - BTS integration +$its = null; +$tproject_mgr = new testproject($db); +$info = $tproject_mgr->get_by_id($args->tproject_id); +$gui->bugInterfaceOn = $info['issue_tracker_enabled']; +if ($info['issue_tracker_enabled']) { + $it_mgr = new tlIssueTracker($db); + $its = $it_mgr->getInterfaceObject($args->tproject_id); + unset($it_mgr); +} + +$smarty = new TLSmarty(); +$img = $smarty->getImages(); +$openBugs = array(); +$resolvedBugs = array(); +$arrData = array(); + +$tplan_mgr = new testplan($db); +$metricsMgr = new tlTestPlanMetrics($db); +$tproject_mgr = new testproject($db); + +$tplan_info = $tplan_mgr->get_by_id($args->tplan_id); +$tproject_info = $tproject_mgr->get_by_id($args->tproject_id); +unset($tproject_mgr); + +switch ($args->verboseType) { + case 'all': + $execSet = $tplan_mgr->getAllExecutionsWithBugs($args->tplan_id); + break; + + case 'latest': + default: + $execSet = (array) $metricsMgr->getLTCVNewGeneration($args->tplan_id, + null, + array( + 'addExecInfo' => true, + 'accessKeyType' => 'index', + 'specViewFields' => true, + 'testSuiteInfo' => true, + 'includeNotRun' => false + )); + break; +} + +$testcase_bugs = array(); +$mine = array(); + +$l18n = init_labels( + array( + 'execution_history' => null, + 'design' => null, + 'no_linked_bugs' => null + )); +foreach ($execSet as $execution) { + $tc_id = $execution['tc_id']; + $mine[] = $execution['exec_id']; + + $bug_urls = buildBugString($db, $execution['exec_id'], $its, $openBugs, + $resolvedBugs); + if ($bug_urls) { + // First bug found for this tc + if (! isset($testcase_bugs[$tc_id])) { + // This is ONLY PARENT TEST SUITE !!! + $suiteName = $execution['tsuite_name']; + $tc_name = $execution['full_external_id'] . ":" . $execution['name']; + + // add linked icons + $exec_history_link = "" . + " "; + $edit_link = "" . + " "; + + $tc_name = "" . $exec_history_link . $edit_link . $tc_name; + + $testcase_bugs[$tc_id] = array( + $suiteName, + $tc_name, + array() + ); + } + foreach ($bug_urls as $url) { + if (! in_array($url, $testcase_bugs[$tc_id][2])) { + $testcase_bugs[$tc_id][2][] = $url; + } + } + } +} +foreach ($testcase_bugs as &$row) { + $row[2] = implode("
    ", $row[2]); +} +$arrData = array_values($testcase_bugs); + +if (! empty($arrData)) { + // Create column headers + $columns = getColumnsDefinition(); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + foreach ($arrData as $bugs) { + $rowData = array(); + $rowData[] = $bugs[0]; + $rowData[] = $bugs[1]; + $rowData[] = $bugs[2]; + + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, 'tl_table_bugs_per_test_case'); + + $table->setGroupByColumnName(lang_get('title_test_suite_name')); + + $table->setSortByColumnName(lang_get('title_test_case_title')); + $table->sortDirection = 'ASC'; + + $table->showToolbar = true; + $table->toolbarExpandCollapseGroupsButton = true; + $table->toolbarShowAllColumnsButton = true; + + $gui->tableSet = array( + $table + ); +} else { + $gui->warning_msg = $l18n['no_linked_bugs']; +} + +$totalOpenBugs = count($openBugs); +$totalResolvedBugs = count($resolvedBugs); +$totalBugs = $totalOpenBugs + $totalResolvedBugs; +$totalCasesWithBugs = count($arrData); + +$gui->user = $args->user; +$gui->printDate = ''; +$gui->tproject_name = $tproject_info['name']; +$gui->tplan_name = $tplan_info['name']; +$gui->title = $args->title; +$gui->totalOpenBugs = $totalOpenBugs; +$gui->totalResolvedBugs = $totalResolvedBugs; +$gui->totalBugs = $totalBugs; +$gui->totalCasesWithBugs = $totalCasesWithBugs; +$gui->hint = $args->hint; + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Get links to bugs related to execution. + * + * @param database $db + * @param integer $execID + * execution id + * @param array $openBugsArray + * array to count open bugs + * @param array $resolvedBugsArray + * array to count resolved bugs + * @return array List of links to related bugs + */ +function buildBugString(&$db, $execID, &$bugInterface, &$openBugsArray, + &$resolvedBugsArray) +{ + $bugUrls = array(); + if ($bugInterface) { + $bugs = get_bugs_for_exec($db, $bugInterface, $execID); + if ($bugs) { + foreach ($bugs as $bugID => $bugInfo) { + if ($bugInfo['isResolved']) { + if (! in_array($bugID, $resolvedBugsArray)) { + $resolvedBugsArray[] = $bugID; + } + } else { + if (! in_array($bugID, $openBugsArray)) { + $openBugsArray[] = $bugID; + } + } + $bugUrls[] = $bugInfo['link_to_bts']; + } + } + } + return $bugUrls; +} + +/** + * get Columns definition for table to display + * + * @return array + */ +function getColumnsDefinition() +{ + $colDef = array(); + + $colDef[] = array( + 'title_key' => 'title_test_suite_name', + 'width' => 30, + 'type' => 'text' + ); + $colDef[] = array( + 'title_key' => 'title_test_case_title', + 'width' => 30, + 'type' => 'text' + ); + $colDef[] = array( + 'title_key' => 'title_test_case_bugs', + 'width' => 40, + 'type' => 'text' + ); + + return $colDef; +} + +/** + * + * @return stdClass + */ +function initArgs() +{ + $iParams = array( + "format" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "type" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->tproject_id = intval($_SESSION['testprojectID']); + $args->user = $_SESSION['currentUser']; + + switch ($args->type) { + case 1: + $args->verboseType = 'all'; + $args->title = lang_get('link_report_total_bugs_all_exec'); + $args->hint = lang_get('link_report_total_bugs_all_exec'); + break; + + case 0: + default: + $args->verboseType = 'latest'; + $args->title = lang_get('link_report_total_bugs'); + $args->hint = ''; + break; + } + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_metrics'); } - -$testcase_bugs = array(); -$mine = array(); - -$l18n = init_labels(array('execution_history' => null,'design' => null,'no_linked_bugs' => null)); -foreach ($execSet as $execution) -{ - $tc_id = $execution['tc_id']; - $mine[] = $execution['exec_id']; - - $bug_urls = buildBugString($db, $execution['exec_id'],$its, $openBugs, $resolvedBugs); - if ($bug_urls) - { - // First bug found for this tc - if (!isset($testcase_bugs[$tc_id])) - { - // This is ONLY PARENT TEST SUITE !!! - $suiteName = $execution['tsuite_name']; - $tc_name = $execution['full_external_id'] . ":" . $execution['name']; - - // add linked icons - $exec_history_link = "" . - " "; - $edit_link = "" . - " "; - - $tc_name = "" . $exec_history_link . - $edit_link . $tc_name; - - $testcase_bugs[$tc_id] = array($suiteName, $tc_name, array()); - } - foreach ($bug_urls as $url) - { - if (!in_array($url, $testcase_bugs[$tc_id][2])) - { - $testcase_bugs[$tc_id][2][] = $url; - } - } - } -} -foreach ($testcase_bugs as &$row) -{ - $row[2] = implode("
    ", $row[2]); -} -$arrData = array_values($testcase_bugs); - -if(count($arrData) > 0) -{ - // Create column headers - $columns = getColumnsDefinition(); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - foreach($arrData as $bugs) - { - $rowData = array(); - $rowData[] = $bugs[0]; - $rowData[] = $bugs[1]; - $rowData[] = $bugs[2]; - - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_bugs_per_test_case'); - - $table->setGroupByColumnName(lang_get('title_test_suite_name')); - - $table->setSortByColumnName(lang_get('title_test_case_title')); - $table->sortDirection = 'ASC'; - - $table->showToolbar = true; - $table->toolbarExpandCollapseGroupsButton = true; - $table->toolbarShowAllColumnsButton = true; - - $gui->tableSet = array($table); -} -else -{ - $gui->warning_msg = $l18n['no_linked_bugs']; -} - -$totalOpenBugs = count($openBugs); -$totalResolvedBugs = count($resolvedBugs); -$totalBugs = $totalOpenBugs + $totalResolvedBugs; -$totalCasesWithBugs = count($arrData); - -$gui->user = $args->user; -$gui->printDate = ''; -$gui->tproject_name = $tproject_info['name']; -$gui->tplan_name = $tplan_info['name']; -$gui->title = $args->title; -$gui->totalOpenBugs = $totalOpenBugs; -$gui->totalResolvedBugs = $totalResolvedBugs; -$gui->totalBugs = $totalBugs; -$gui->totalCasesWithBugs = $totalCasesWithBugs; -$gui->hint = $args->hint; - -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * Get links to bugs related to execution. - * @param $db - * @param $execID execution id - * @param $openBugsArray array to count open bugs - * @param $resolvedBugsArray array to count resolved bugs - * - * @return array List of links to related bugs - */ -function buildBugString(&$db,$execID,&$bugInterface,&$openBugsArray,&$resolvedBugsArray) -{ - $bugUrls = array(); - if ($bugInterface) - { - $bugs = get_bugs_for_exec($db,$bugInterface,$execID); - if ($bugs) - { - foreach($bugs as $bugID => $bugInfo) - { - if($bugInfo['isResolved']) - { - if(!in_array($bugID, $resolvedBugsArray)) - { - $resolvedBugsArray[] = $bugID; - } - } - else - { - if(!in_array($bugID, $openBugsArray)) - { - $openBugsArray[] = $bugID; - } - } - $bugUrls[] = $bugInfo['link_to_bts']; - } - } - } - return $bugUrls; -} - -/** - * get Columns definition for table to display - * - */ -function getColumnsDefinition() -{ - $colDef = array(); - - $colDef[] = array('title_key' => 'title_test_suite_name', 'width' => 30, 'type' => 'text'); - $colDef[] = array('title_key' => 'title_test_case_title', 'width' => 30, 'type' => 'text'); - $colDef[] = array('title_key' => 'title_test_case_bugs', 'width' => 40, 'type' => 'text'); - - return $colDef; -} - - -/* - function: init_args() - - args : - - returns: - -*/ -function init_args() -{ - $iParams = array("format" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "type" => array(tlInputParameter::INT_N) ); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - - $args->tproject_id = intval($_SESSION['testprojectID']); - $args->user = $_SESSION['currentUser']; - - switch($args->type) - { - case 1: - $args->verboseType = 'all'; - $args->title = lang_get('link_report_total_bugs_all_exec'); - $args->hint = lang_get('link_report_total_bugs_all_exec'); - break; - - default: - case 0: - $args->verboseType = 'latest'; - $args->title = lang_get('link_report_total_bugs'); - $args->hint = ''; - break; - } - return $args; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file diff --git a/lib/results/resultsByStatus.php b/lib/results/resultsByStatus.php index 2a19acd70b..8fe79cb62e 100644 --- a/lib/results/resultsByStatus.php +++ b/lib/results/resultsByStatus.php @@ -1,908 +1,1026 @@ -statusCode; - -$tplan_mgr = new testplan($db); -$tcase_mgr = new testcase($db); - -$gui = initializeGui($db,$args,$tplan_mgr); -$its = &$gui->its; -$labels = &$gui->labels; - -$testCaseCfg = config_get('testcase_cfg'); -$metrics = getMetrics($db,$args,$gui); - -$cfOnExec = $cfSet = null; - -// done here in order to get some config about images -$smarty = new TLSmarty(); -if (!is_null($metrics) and count($metrics) > 0) { - if ($args->addOpAccess) { - $links = featureLinks($labels,$smarty->getImages()); - } - - - $userAccessKey = $gui->userAccessKey; - $notesAccessKey = $gui->notesAccessKey; - - $urlSafeString = array(); - $urlSafeString['tprojectPrefix'] = urlencode($gui->tproject_info['prefix']); - $urlSafeString['basehref'] = str_replace(" ", "%20", $args->basehref); - - $out = array(); - $users = getUsersForHtmlOptions($db); - $pathCache = $topCache = $levelCache = null; - $nameCache = initNameCache($gui); - - $odx = 0; - - if( $args->type != $statusCode['not_run'] ) { - // get Custom fields definition to understand columns to be added - $cfSet = $tcase_mgr->cfield_mgr->get_linked_cfields_at_execution($args->tproject_id,true,'testcase'); - $execSet = array_keys($metrics); - - - // go for Custom fields values of all executions on ONE SHOT! - $cfOnExec = $tcase_mgr->cfield_mgr->get_linked_cfields_at_execution($args->tproject_id,true,'testcase',null,$execSet); - } - - - foreach($metrics as $execID => &$exec) { - // --------------------------------------------------------------------- - // do some decode work, using caches - if (!isset($pathCache[$exec['tcase_id']])) { - $dummy = $tcase_mgr->getPathLayered(array($exec['tcase_id'])); - $pathCache[$exec['tcase_id']] = $dummy[$exec['tsuite_id']]['value']; - $levelCache[$exec['tcase_id']] = $dummy[$exec['tsuite_id']]['level']; - $ky = current(array_keys($dummy)); - $topCache[$exec['tcase_id']] = $ky; - } - - - // ------------------------------------------------------------------------- - // IMPORTANT NOTICE: - // When test case has been runned, version must be get from - // executions.tcversion_number - // - // Column ORDER IS CRITIC - // suiteName - // testTitle CCA-15708: RSRSR-150 - // testVersion 1 - - // @20191128 - // We will adde test version summary ONLY if OUTPUT is - // Spreadsheet - - // platformName XXXX <<< ONlY is platforms have been used on - // Test plan under analisys - // - // buildName 2.0 <<< At least when platforms ARE NOT USED, - // <<< BY DEFAULT build is not displayed as - // column but used to group results. - // testerName yyyyyy - // localizedTS 2012-04-25 12:14:55 <<<< ONLY if executed - // notes [empty string] (execution notes) - // bugString [empty string] <<<< ONLY if executed - // - $out[$odx]['suiteName'] = $pathCache[$exec['tcase_id']]; - - // ------------------------------------------------------------------------- - $zipper = ''; - switch ($args->format) { - case FORMAT_HTML: - $out[$odx]['testTitle'] = ""; - $zipper = ''; - if ($args->addOpAccess) { - $out[$odx]['testTitle'] .= sprintf($links['full'], - $exec['tcase_id'],$exec['tcase_id'],$exec['tcversion_id'], - $exec['build_id'],$args->tplan_id,$exec['platform_id'],$exec['tcase_id']); - $zipper = ''; - } - break; - - case FORMAT_XLS: - $out[$odx]['testTitle'] = ''; - break; - - default: - $out[$odx]['testTitle'] = ''; - $zipper = ''; - break; - - } - - // See IMPORTANT NOTICE/WARNING about XLS generation - $out[$odx]['testTitle'] .= $exec['full_external_id'] . ':' . - $exec['name'] .$zipper; - - $out[$odx]['testVersion'] = $exec['tcversion_number']; - - switch ($args->format) { - case FORMAT_XLS: - $out[$odx]['summary'] = $exec['summary']; - break; - - case FORMAT_HTML: - default: - break; - } - - // Insert order on out is CRITIC, because order is used on buildMatrix - if($gui->show_platforms) { - $out[$odx]['platformName'] = $nameCache['platform'][$exec['platform_id']]; - } - - $out[$odx]['buildName'] = $nameCache['build'][$exec['build_id']]; - - // ------------------------------------------------------------------------ - // verbose user - if( $args->type == $statusCode['not_run'] ) - { - natsort($exec[$userAccessKey]); - $zux = array(); - foreach ($exec[$userAccessKey] as $vux) - { - if(isset($users,$vux)) - { - $zux[] = htmlspecialchars($users[$vux]); - } - else - { - // user id has been disable/deleted - $zux[] = sprintf($labels['deleted_user'],$vux); - } - } - $out[$odx]['testerName'] = implode(',',$zux); - } - else - { - if($exec[$userAccessKey] == 0 ) - { - $out[$odx]['testerName'] = $labels['nobody']; - } - else - { - if(isset($users,$exec[$userAccessKey])) - { - $out[$odx]['testerName'] = htmlspecialchars($users[$exec[$userAccessKey]]); - } - else - { - // user id has been disable/deleted - $out[$odx]['testerName'] = sprintf($labels['deleted_user'],$exec[$userAccessKey]); - } - } - } - $out[$odx]['testerName'] = htmlspecialchars($out[$odx]['testerName']); - - // ------------------------------------------------------------------------- - if( $args->type != $statusCode['not_run'] ) - { - $out[$odx]['localizedTS'] = $exec['execution_ts']; - } - $out[$odx]['notes'] = strip_tags($exec[$notesAccessKey]); - - if( $args->type != $statusCode['not_run'] ) - { - if(!is_null($cfSet)) - { - // Need to document how important is value of second index on - // $out[$odx][SECOND INDEX] - foreach($cfSet as $cfID => $cfValue) - { - if(isset($cfOnExec[$execID][$cfID]) && !is_null($cfOnExec[$execID][$cfID])) - { - $out[$odx][$cfID] = $tcase_mgr->cfield_mgr->string_custom_field_value($cfOnExec[$execID][$cfID],null); - } - else - { - $out[$odx][$cfID] = ''; - } - } - } - - // ------------------------------------------------------------------------ - // Bug processing. - // Remember that bugs are linked to executions NOT test case. - // When using Platforms a Test Case can have multiple executions - // (N on each platform). - // ------------------------------------------------------------------------ - $bugString = ''; - if($gui->bugInterfaceOn && $exec['status'] != $statusCode['not_run']) - { - $bugSet = get_bugs_for_exec($db, $its, $exec['executions_id'],array('id','summary')); - if (count($bugSet) == 0) - { - $gui->without_bugs_counter += 1; - } - - switch($args->format) - { - case FORMAT_XLS: - // See IMPORTANT NOTICE/WARNING about XLS generation - foreach($bugSet as $bug) - { - $bugString .= $bug['id'] . ':' . $bug['summary'] . "\r"; - } - break; - - default: - foreach($bugSet as $bug) - { - $bugString .= $bug['link_to_bts'] . '
    '; - } - break; - } - unset($bugSet); - } - $out[$odx]['bugString'] = $bugString; - } - $odx++; - } - $gui->dataSet = $out; - unset($out); -} else { - $gui->warning_msg = getWarning($args->type,$statusCode); -} - -switch ($args->format) { - case FORMAT_XLS: - createSpreadsheet($gui,$args,$args->getSpreadsheetBy,$cfSet); - break; - - default: - $tableOpt = array('status_not_run' => ($args->type == $statusCode['not_run']), - 'bugInterfaceOn' => $gui->bugInterfaceOn, - 'format' => $args->format, - 'show_platforms' => $gui->show_platforms); - - $gui->tableSet[] = buildMatrix($gui->dataSet, $args, $tableOpt , - $gui->platformSet,$cfSet); - break; -} - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui ); -displayReport($tplCfg->template_dir . $tplCfg->default_template, - $smarty, $args->format, $gui->mailCfg); - - -/** - * - * - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N), - "type" => array(tlInputParameter::STRING_N,0,1)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $args->getSpreadsheetBy = isset($_REQUEST['sendSpreadSheetByMail_x']) ? 'email' : null; - if( is_null($args->getSpreadsheetBy) ) { - $args->getSpreadsheetBy = isset($_REQUEST['exportSpreadSheet_x']) ? 'download' : null; - } - - $args->addOpAccess = true; - if( !is_null($args->apikey) ) { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } else { - $args->addOpAccess = false; - $cerbero->method = null; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } else { - testlinkInitPage($dbHandler,true,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - - $rCfg = config_get('results'); - $args->statusCode = $rCfg['status_code']; - - $args->user = $_SESSION['currentUser']; - $args->basehref = $_SESSION['basehref']; - return $args; +statusCode; + +$tplan_mgr = new testplan($db); +$tcaseMgr = new testcase($db); + +$gui = initializeGui($db, $args, $tplan_mgr); +$its = &$gui->its; +$labels = &$gui->labels; + +$testCaseCfg = config_get('testcase_cfg'); +$metrics = getMetrics($db, $args, $gui); + +$cfOnExec = $cfSet = null; + +// done here in order to get some config about images +$smarty = new TLSmarty(); +if (! is_null($metrics) && ! empty($metrics)) { + if ($args->addOpAccess) { + $links = featureLinks($labels, $smarty->getImages()); + } + + $userAccessKey = $gui->userAccessKey; + $notesAccessKey = $gui->notesAccessKey; + + $urlSafeString = array(); + $urlSafeString['tprojectPrefix'] = urlencode($gui->tproject_info['prefix']); + $urlSafeString['basehref'] = str_replace(" ", "%20", $args->basehref); + + $out = array(); + $users = getUsersForHtmlOptions($db); + $pathCache = null; + $nameCache = initNameCache($gui); + + $odx = 0; + + if ($args->type != $statusCode['not_run']) { + // get Custom fields definition to understand columns to be added + $cfSet = $tcaseMgr->cfield_mgr->get_linked_cfields_at_execution( + $args->tproject_id, true, 'testcase'); + $execSet = array_keys($metrics); + + // go for Custom fields values of all executions on ONE SHOT! + $cfOnExec = $tcaseMgr->cfield_mgr->get_linked_cfields_at_execution( + $args->tproject_id, true, 'testcase', null, $execSet); + } + + foreach ($metrics as $execID => &$exec) { + // --------------------------------------------------------------------- + // do some decode work, using caches + if (! isset($pathCache[$exec['tcase_id']])) { + $dummy = $tcaseMgr->getPathLayered(array( + $exec['tcase_id'] + )); + $pathCache[$exec['tcase_id']] = $dummy[$exec['tsuite_id']]['value']; + $levelCache[$exec['tcase_id']] = $dummy[$exec['tsuite_id']]['level']; + $ky = current(array_keys($dummy)); + $topCache[$exec['tcase_id']] = $ky; + } + + // ------------------------------------------------------------------------- + // IMPORTANT NOTICE: + // When test case has been runned, version must be get from + // executions.tcversion_number + // + // Column ORDER IS CRITIC + // suiteName + // testTitle CCA-15708: RSRSR-150 + // testVersion 1 + + // @20191128 + // We will adde test version summary ONLY if OUTPUT is + // Spreadsheet + + // platformName XXXX <<< ONlY is platforms have been used on + // Test plan under analisys + // + // buildName 2.0 <<< At least when platforms ARE NOT USED, + // <<< BY DEFAULT build is not displayed as + // column but used to group results. + // testerName yyyyyy + // localizedTS 2012-04-25 12:14:55 <<<< ONLY if executed + // notes [empty string] (execution notes) + // bugString [empty string] <<<< ONLY if executed + // + $out[$odx]['suiteName'] = $pathCache[$exec['tcase_id']]; + + // ------------------------------------------------------------------------- + $zipper = ''; + switch ($args->format) { + case FORMAT_HTML: + $out[$odx]['testTitle'] = ""; + $zipper = ''; + if ($args->addOpAccess) { + $out[$odx]['testTitle'] .= sprintf($links['full'], + $exec['tcase_id'], $exec['tcase_id'], + $exec['tcversion_id'], $exec['build_id'], + $args->tplan_id, $exec['platform_id'], $exec['tcase_id']); + $zipper = ''; + } + break; + + case FORMAT_XLS: + $out[$odx]['testTitle'] = ''; + break; + + default: + $out[$odx]['testTitle'] = ''; + $zipper = ''; + break; + } + + // See IMPORTANT NOTICE/WARNING about XLS generation + $out[$odx]['testTitle'] .= $exec['full_external_id'] . ':' . + $exec['name'] . $zipper; + + $out[$odx]['testVersion'] = $exec['tcversion_number']; + + switch ($args->format) { + case FORMAT_XLS: + $out[$odx]['summary'] = $exec['summary']; + break; + + case FORMAT_HTML: + default: + break; + } + + // Insert order on out is CRITIC, because order is used on buildMatrix + if ($gui->show_platforms) { + $out[$odx]['platformName'] = $nameCache['platform'][$exec['platform_id']]; + } + + $out[$odx]['buildName'] = $nameCache['build'][$exec['build_id']]; + + // ------------------------------------------------------------------------ + // verbose user + if ($args->type == $statusCode['not_run']) { + natsort($exec[$userAccessKey]); + $zux = array(); + foreach ($exec[$userAccessKey] as $vux) { + if (isset($users, $vux)) { + $zux[] = htmlspecialchars($users[$vux]); + } else { + // user id has been disable/deleted + $zux[] = sprintf($labels['deleted_user'], $vux); + } + } + $out[$odx]['testerName'] = implode(',', $zux); + } else { + if ($exec[$userAccessKey] == 0) { + $out[$odx]['testerName'] = $labels['nobody']; + } else { + if (isset($users, $exec[$userAccessKey])) { + $out[$odx]['testerName'] = htmlspecialchars( + $users[$exec[$userAccessKey]]); + } else { + // user id has been disable/deleted + $out[$odx]['testerName'] = sprintf($labels['deleted_user'], + $exec[$userAccessKey]); + } + } + } + $out[$odx]['testerName'] = htmlspecialchars($out[$odx]['testerName']); + + // ------------------------------------------------------------------------- + if ($args->type != $statusCode['not_run']) { + $out[$odx]['localizedTS'] = $exec['execution_ts']; + } + $out[$odx]['notes'] = strip_tags($exec[$notesAccessKey]); + + if ($args->type != $statusCode['not_run']) { + if (! is_null($cfSet)) { + // Need to document how important is value of second index on + // $out[$odx][SECOND INDEX] + foreach ($cfSet as $cfID => $cfValue) { + if (isset($cfOnExec[$execID][$cfID]) && + ! is_null($cfOnExec[$execID][$cfID])) { + $out[$odx][$cfID] = $tcaseMgr->cfield_mgr->string_custom_field_value( + $cfOnExec[$execID][$cfID], null); + } else { + $out[$odx][$cfID] = ''; + } + } + } + + // ------------------------------------------------------------------------ + // Bug processing. + // Remember that bugs are linked to executions NOT test case. + // When using Platforms a Test Case can have multiple executions + // (N on each platform). + // ------------------------------------------------------------------------ + $bugString = ''; + if ($gui->bugInterfaceOn && $exec['status'] != $statusCode['not_run']) { + $bugSet = get_bugs_for_exec($db, $its, $exec['executions_id'], + array( + 'id', + 'summary' + )); + if (count($bugSet) == 0) { + $gui->without_bugs_counter += 1; + } + + switch ($args->format) { + case FORMAT_XLS: + // See IMPORTANT NOTICE/WARNING about XLS generation + foreach ($bugSet as $bug) { + $bugString .= $bug['id'] . ':' . $bug['summary'] . + "\r"; + } + break; + + default: + foreach ($bugSet as $bug) { + $bugString .= $bug['link_to_bts'] . '
    '; + } + break; + } + unset($bugSet); + } + $out[$odx]['bugString'] = $bugString; + } + $odx ++; + } + $gui->dataSet = $out; + unset($out); +} else { + $gui->warning_msg = getWarning($args->type, $statusCode); +} + +switch ($args->format) { + case FORMAT_XLS: + createSpreadsheet($gui, $args, $cfSet); + break; + + default: + $tableOpt = [ + 'status_not_run' => ($args->type == $statusCode['not_run']), + 'bugInterfaceOn' => $gui->bugInterfaceOn, + 'format' => $args->format, + 'show_platforms' => $gui->show_platforms + ]; + + $gui->tableSet[] = buildMatrix($gui->dataSet, $args, $tableOpt, + $gui->platformSet, $cfSet); + break; +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +displayReport($tplCfg->template_dir . $tplCfg->default_template, $smarty, + $args->format, $gui->mailCfg); + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N + ), + "type" => array( + tlInputParameter::STRING_N, + 0, + 1 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->getSpreadsheetBy = isset($_REQUEST['sendSpreadSheetByMail_x']) ? 'email' : null; + if (is_null($args->getSpreadsheetBy)) { + $args->getSpreadsheetBy = isset($_REQUEST['exportSpreadSheet_x']) ? 'download' : null; + } + + $args->addOpAccess = true; + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, true, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + $rCfg = config_get('results'); + $args->statusCode = $rCfg['status_code']; + + $args->user = $_SESSION['currentUser']; + $args->basehref = $_SESSION['basehref']; + return $args; +} + +/** + * initializeGui + */ +function initializeGui(&$dbh, &$argsObj, &$tplanMgr) +{ + $tprojectMgr = new testproject($dbh); + + $guiObj = new stdClass(); + + $guiObj->labels = init_labels( + array( + 'deleted_user' => null, + 'design' => null, + 'execution' => null, + 'nobody' => null, + 'execution_history' => null, + 'info_only_with_tester_assignment' => null, + 'th_bugs_not_linked' => null, + 'info_notrun_tc_report' => null, + 'info_xls_report_results_by_status' => null + )); + + $guiObj->report_context = $guiObj->labels['info_only_with_tester_assignment']; + $guiObj->info_xls_report = $guiObj->labels['info_xls_report_results_by_status']; + + $guiObj->info_msg = ''; + $guiObj->bugs_msg = ''; + + $guiObj->tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); + $guiObj->tproject_info = $tprojectMgr->get_by_id($argsObj->tproject_id); + $guiObj->tplan_name = $guiObj->tplan_info['name']; + $guiObj->tproject_name = $guiObj->tproject_info['name']; + + $guiObj->format = $argsObj->format; + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->tplan_id = $argsObj->tplan_id; + $guiObj->apikey = $argsObj->apikey; + + // Count for the Failed Issues whose bugs have to be raised/not linked. + $guiObj->without_bugs_counter = 0; + $guiObj->dataSet = null; + $guiObj->title = null; + $guiObj->type = $argsObj->type; + $guiObj->warning_msg = ''; + + // Implementation based on convention, will use only keys starting with 'list_tc_' + // see reports.cfg.php + $reportCfg = config_get('reports_list'); + + $lbl_th_bugs_not_linked = lang_get('th_bugs_not_linked'); + $needle = 'list_tc_'; + $nl = strlen($needle); + foreach ($reportCfg as $key => $val) { + $checkIt = false; + if ($checkIt = (strpos($key, $needle) !== false)) { + // now get the verbose status + // list_tc_[verbose_status], example list_tc_not_run + $verbose_status = substr($key, $nl); + + // if( $verbose_status != 'not_run' || $verbose_status != 'passed' ) + $guiObj->bugs_msg = $lbl_th_bugs_not_linked; + if (isset($reportCfg[$key]['misc']) && + isset($reportCfg[$key]['misc']['bugs_not_linked']) && + ! $reportCfg[$key]['misc']['bugs_not_linked']) { + $guiObj->bugs_msg = ''; + } + } + + if ($checkIt && $argsObj->type == $argsObj->statusCode[$verbose_status]) { + $guiObj->title = lang_get('list_of_' . $verbose_status); + break; + } + } + + if (is_null($guiObj->title)) { + tlog('wrong value of GET type'); + exit(); + } + + $guiObj->buildSet = (array) $tplanMgr->get_builds_for_html_options( + $argsObj->tplan_id); + + // needed to decode + $getOpt = [ + 'outputFormat' => 'map' + ]; + $guiObj->platformSet = (array) $tplanMgr->getPlatforms($argsObj->tplan_id, + $getOpt); + $guiObj->show_platforms = count($guiObj->platformSet); + + $guiObj->its = null; // Issue Tracker System + $info = $tprojectMgr->get_by_id($argsObj->tproject_id); + $guiObj->bugInterfaceOn = $info['issue_tracker_enabled']; + if ($info['issue_tracker_enabled']) { + $it_mgr = new tlIssueTracker($dbh); + $guiObj->its = $it_mgr->getInterfaceObject($argsObj->tproject_id); + unset($it_mgr); + } + + $guiObj->mailCfg = buildMailCfg($guiObj); + + return $guiObj; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); +} + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * Builds ext-js rich table to display matrix results + * + * @param array $dataSet + * data to be displayed on matrix + * @param stdClass $args + * @param array $options + * @param array $platforms + * @param array $customFieldColumns + * @return tlExtTable|tlHTMLTable + */ +function buildMatrix($dataSet, &$args, $options = [], $platforms = null, + $customFieldColumns = null) +{ + $default_options = [ + 'bugInterfaceOn' => false, + 'show_platforms' => false, + 'status_not_run' => false, + 'format' => FORMAT_HTML + ]; + + $options = array_merge($default_options, $options); + + $l18n = init_labels( + [ + 'assigned_to' => null, + 'platform' => null, + 'th_date' => null, + 'th_build' => null + ]); + + $columns = []; + $columns[] = [ + 'title_key' => 'title_test_suite_name', + 'width' => 80 + ]; + $columns[] = [ + 'title_key' => 'title_test_case_title', + 'width' => 80 + ]; + $columns[] = [ + 'title_key' => 'version', + 'width' => 30 + ]; + + if ($options['show_platforms']) { + $columns[] = [ + 'title_key' => 'platform', + 'width' => 60, + 'filter' => 'list', + 'filterOptions' => $platforms + ]; + } + + $columns[] = [ + 'title_key' => 'th_build', + 'width' => 35 + ]; + if ($options['status_not_run']) { + $columns[] = [ + 'title_key' => 'assigned_to', + 'width' => 60 + ]; + $columns[] = [ + 'title_key' => 'summary', + 'width' => 150, + 'type' => 'textArea' // This will attach a custom behaivour + // defined in exttable.class.php + ]; + } else { + $columns[] = [ + 'title_key' => 'th_run_by', + 'width' => 60 + ]; + $columns[] = [ + 'title_key' => 'th_date', + 'width' => 60 + ]; + $columns[] = [ + 'title_key' => 'title_execution_notes', + 'width' => 150, + 'type' => 'notes' // This will attach a custom behaivour + // defined in exttable.class.php + ]; + + if (! is_null($customFieldColumns)) { + foreach ($customFieldColumns as $def) { + $columns[] = [ + 'title' => $def['label'], + 'width' => 60 + ]; + } + } + + if ($options['bugInterfaceOn']) { + $columns[] = [ + 'title_key' => 'th_bugs_id_summary', + 'type' => 'issueSummary' + ]; + } + } + + if ($options['format'] == FORMAT_HTML) { + + // IMPORTANT DEVELOPMENT NOTICE + // columns and dataSet are deeply related this means that inside + // dataSet order has to be identical that on columns or table will be a disaster + // + $matrix = new tlExtTable($columns, $dataSet, + 'tl_table_results_by_status'); + + // if not run report: sort by test suite + // blocked, failed report: sort by platform (if enabled) else sort by date + $sort_name = 0; + if ($options['status_not_run']) { + $sort_name = $l18n['assigned_to']; + } else { + $sort_name = $options['show_platforms'] ? $l18n['platform'] : $l18n['th_date']; + } + + $matrix->setSortByColumnName($sort_name); + $matrix->setGroupByColumnName($l18n['th_build']); + + // define table toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + } else { + $matrix = new tlHTMLTable($columns, $dataSet, + 'tl_table_results_by_status'); + } + return $matrix; +} + +/** + * + * @param array $lbl + * @param array $img + * @return string[] + */ +function featureLinks($lbl, $img) +{ + $links = array(); + + // %s => test case id + $links['exec_history'] = '' . + ' '; + + // tcase_id,tcversion_id,build_id,tplan_id,platform_id + $links['exec'] = '' . + ' '; + + // %s => test case id + $links['edit'] = '' . + ' '; + + $links['full'] = $links['exec_history'] . $links['exec'] . $links['edit']; + + return $links; +} + +/** + * + * @param stdClass $guiObj + * @return array + */ +function initNameCache($guiObj) +{ + $safeItems = array( + 'build' => null, + 'platform' => null + ); + + foreach ($guiObj->buildSet as $id => $name) { + $safeItems['build'][$id] = htmlspecialchars($name); + } + + if ($guiObj->show_platforms) { + foreach ($guiObj->platformSet as $id => $name) { + $safeItems['platform'][$id] = htmlspecialchars($name); + } + } + + return $safeItems; +} + +/** + * + * @param String $targetStatus + * @param array $statusCfg + * @return array + */ +function getWarning($targetStatus, $statusCfg) +{ + $msg = ''; + $key2check = array( + 'not_run', + 'failed', + 'blocked' + ); + foreach ($key2check as $statusVerbose) { + if ($targetStatus == $statusCfg[$statusVerbose]) { + $msg = lang_get('no_' . $statusVerbose . '_with_tester'); + break; + } + } + return $msg; +} + +/** + * + * @param stdClass $gui + * @param stdClass $args + * @param string $media + * @param array $customFieldColumns + */ +function createSpreadsheet($gui, $args, $customFieldColumns = null) +{ + $lbl = initLblSpreadsheet(); + $cellRange = range('A', 'Z'); + $style = initStyleSpreadsheet(); + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + + // Step 2 + // data is organized with following columns$dataHeader[] + // Test suite + // Test case + // Version + // Test case summary + // [Platform] + // Build + // Tester + // Date + // Execution notes + // [Custom Field ENABLED ON EXEC 1] + // [Custom Field ENABLED ON EXEC 1] + // [Custom Field ENABLED ON EXEC 1] + // [bugString] only if bugtracking integration exists for this test project + + // This is HOW gui->dataSet is organized + // THIS IS CRITIC ?? + // + // suiteName Issue Tracker Management + // testTitle PTRJ-76:Create issue tracker - no conflict + // testVersion 1 + // bla,bla,bla,.... + // [platformName] + // buildName 1.0 + // testerName admin + // localizedTS 2013-03-28 20:15:06 + // notes [empty string] + // bugString [empty string] + + // + $dataHeader = array( + $lbl['title_test_suite_name'], + $lbl['title_test_case_title'], + $lbl['version'], + $lbl['summary'] + ); + + // if( $showPlatforms = ( property_exists($gui,'platformSet') && !is_null($gui->platformSet) && !isset($gui->platformSet[0])) ) + if (property_exists($gui, 'platformSet') && ! is_null($gui->platformSet) && + ! isset($gui->platformSet[0])) { + $dataHeader[] = $lbl['platform']; + } + + $dataHeader[] = $lbl['build']; + + if ($gui->notRunReport) { + $dataHeader[] = $lbl['assigned_to']; + $dataHeader[] = $lbl['summary']; + } else { + $dataHeader[] = $lbl['th_run_by']; + $dataHeader[] = $lbl['th_date']; + $dataHeader[] = $lbl['title_execution_notes']; + } + + if (! is_null($customFieldColumns)) { + foreach ($customFieldColumns as $def) { + $dataHeader[] = $def['label']; + } + } + + // ATTENTION logic regarding NOT RUN IS MISSING + // For not run this column and also columns regarding CF on exec are not displayed + if ($gui->bugInterfaceOn && ! $gui->notRunReport) { + $dataHeader[] = $lbl['th_bugs_id_summary']; + } + + $startingRow = count($lines2write) + 2; // MAGIC + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + // Now process data + $startingRow ++; + $qta_loops = count($gui->dataSet); + for ($idx = 0; $idx < $qta_loops; $idx ++) { + $colCounter = 0; + foreach ($gui->dataSet[$idx] as $ldx => $field) { + if ($ldx != 'bugString' || + ($ldx == 'bugString' && $gui->bugInterfaceOn)) { + $cellID = $cellRange[$colCounter] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + html_entity_decode($field)); + $colCounter ++; + } + + // May be same processing can be applied to execution otes + if ($ldx == 'bugString' && $gui->bugInterfaceOn) { + // To manage new line + // http://stackoverflow.com/questions/5960242/how-to-make-new-lines-in-a-cell-using-phpexcel + // http://stackoverflow.com/questions/6054444/how-to-set-auto-height-in-phpexcel + $objPHPExcel->setActiveSheetIndex(0) + ->getStyle($cellID) + ->getAlignment() + ->setWrapText(true); + } + } + $startingRow ++; + } + + // Final step + $objPHPExcel->setActiveSheetIndex(0); + + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + + $tmpfname = tempnam(config_get('temp_dir'), "resultsByStatus.tmp"); + $objWriter->save($tmpfname); + + if ($args->getSpreadsheetBy == 'email') { + require_once 'email_api.php'; + $ema = new stdClass(); + $ema->from_address = config_get('from_email'); + $ema->to_address = $args->user->emailAddress; + $ema->subject = $gui->mailCfg->subject; + $ema->message = $gui->mailCfg->subject; + + $dum = uniqid("resultsByStatus_") . '.xls'; + $oops = array( + 'attachment' => array( + 'file' => $tmpfname, + 'newname' => $dum + ), + 'exit_on_error' => true, + 'htmlFormat' => true + ); + $email_op = email_send_wrapper($ema, $oops); + unlink($tmpfname); + exit(); + } else { + downloadXls($tmpfname, $xlsType, $gui, 'resultsByStatus_'); + } +} + +/** + * + * @param database $dbh + * @param stdClass $args + * @param stdClass $gui + * @return array + */ +function getMetrics(&$dbh, &$args, &$gui) +{ + $resultsCfg = config_get('results'); + $statusCode = $resultsCfg['status_code']; + $metricsMgr = new tlTestPlanMetrics($dbh); + + if ($args->type == $statusCode['not_run']) { + $opt = array( + 'output' => 'array' + ); + if ($args->format == FORMAT_XLS) { + $opt['add2fields'] = 'TCV.summary'; + } + + $met = $metricsMgr->getNotRunWithTesterAssigned($args->tplan_id, null, + $opt); + + $gui->notRunReport = true; + $gui->info_msg = $gui->labels['info_notrun_tc_report']; + $gui->notesAccessKey = 'summary'; + $gui->userAccessKey = 'user_id'; + } else { + $opt = array( + 'output' => 'mapByExecID', + 'getOnlyAssigned' => true + ); + if ($args->format == FORMAT_XLS) { + $opt['add2fields'] = 'TCV.summary'; + } + + $met = $metricsMgr->getExecutionsByStatus($args->tplan_id, $args->type, + null, $opt); + + $gui->notRunReport = false; + $gui->info_msg = lang_get( + 'info_' . $resultsCfg['code_status'][$args->type] . '_tc_report'); + + $gui->notesAccessKey = 'execution_notes'; + $gui->userAccessKey = 'tester_id'; + } + + return $met; +} + +/** + * + * @return array + */ +function initLblSpreadsheet() +{ + return init_labels( + array( + 'title_test_suite_name' => null, + 'platform' => null, + 'build' => null, + 'th_bugs_id_summary' => null, + 'title_test_case_title' => null, + 'version' => null, + 'testproject' => null, + 'generated_by_TestLink_on' => null, + 'testplan' => null, + 'title_execution_notes' => null, + 'th_date' => null, + 'th_run_by' => null, + 'assigned_to' => null, + 'summary' => null + )); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $sty = array(); + $sty['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $sty['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + + return $sty; +} + +/** + * + * @param PHPExcel $oj + * @param array $style + * @param array $lbl + * @param stdClass $gui + * @return array + */ +function xlsStepOne($oj, $style, $lbl, $gui) +{ + $dummy = ''; + $lines2write = array( + array( + $gui->title, + '' + ), + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ), + array( + $gui->report_context, + '' + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + $cellArea .= "A[$cdx]"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; } - -/** - * initializeGui - * - */ -function initializeGui(&$dbh,&$argsObj,&$tplanMgr) -{ - $tprojectMgr = new testproject($dbh); - - $guiObj = new stdClass(); - - $guiObj->labels = init_labels( - array('deleted_user' => null, 'design' => null, - 'execution' => null,'nobody' => null, - 'execution_history' => null, - 'info_only_with_tester_assignment' => null, - 'th_bugs_not_linked' => null, - 'info_notrun_tc_report' => null, - 'info_xls_report_results_by_status' => null)); - - $guiObj->report_context = $guiObj->labels['info_only_with_tester_assignment']; - $guiObj->info_xls_report = - $guiObj->labels['info_xls_report_results_by_status']; - - $guiObj->info_msg = ''; - $guiObj->bugs_msg = ''; - - $guiObj->tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); - $guiObj->tproject_info = $tprojectMgr->get_by_id($argsObj->tproject_id); - $guiObj->tplan_name = $guiObj->tplan_info['name']; - $guiObj->tproject_name = $guiObj->tproject_info['name']; - - - $guiObj->format = $argsObj->format; - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->tplan_id = $argsObj->tplan_id; - $guiObj->apikey = $argsObj->apikey; - - // Count for the Failed Issues whose bugs have to be raised/not linked. - $guiObj->without_bugs_counter = 0; - $guiObj->dataSet = null; - $guiObj->title = null; - $guiObj->type = $argsObj->type; - $guiObj->warning_msg = ''; - - - // Implementation based on convention, will use only keys starting with 'list_tc_' - // see reports.cfg.php - $reportCfg = config_get('reports_list'); - - $lbl_th_bugs_not_linked = lang_get('th_bugs_not_linked'); - $needle = 'list_tc_'; - $nl = strlen($needle); - foreach( $reportCfg as $key => $val ) - { - $checkIt = false; - if( $checkIt = (strpos($key,$needle) !== FALSE) ) - { - // now get the verbose status - // list_tc_[verbose_status], example list_tc_not_run - $verbose_status = substr($key, $nl); - - // if( $verbose_status != 'not_run' || $verbose_status != 'passed' ) - $guiObj->bugs_msg = $lbl_th_bugs_not_linked; - if( isset($reportCfg[$key]['misc']) ) - { - if( isset($reportCfg[$key]['misc']['bugs_not_linked']) && - $reportCfg[$key]['misc']['bugs_not_linked'] == false ) - { - $guiObj->bugs_msg = ''; - } - } - } - - if( $checkIt ) - { - if($argsObj->type == $argsObj->statusCode[$verbose_status]) - { - $guiObj->title = lang_get('list_of_' . $verbose_status); - break; - } - } - } - - if(is_null($guiObj->title)) - { - tlog('wrong value of GET type'); - exit(); - } - - - // needed to decode - $getOpt = array('outputFormat' => 'map'); - $guiObj->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,$getOpt); - if( !($guiObj->show_platforms = !is_null($guiObj->platformSet)) ) - { - $guiObj->platformSet = array(''); - } - - $guiObj->buildSet = $tplanMgr->get_builds_for_html_options($argsObj->tplan_id); - - - $guiObj->its = null; - $info = $tprojectMgr->get_by_id($argsObj->tproject_id); - $guiObj->bugInterfaceOn = $info['issue_tracker_enabled']; - if($info['issue_tracker_enabled']) - { - $it_mgr = new tlIssueTracker($dbh); - $guiObj->its = $it_mgr->getInterfaceObject($argsObj->tproject_id); - unset($it_mgr); - } - - $guiObj->mailCfg = buildMailCfg($guiObj); - - return $guiObj; - -} - - - -function checkRights(&$db,&$user,$context = null) -{ - if(is_null($context)) - { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; -} - - -/** - * - * - */ -function buildMailCfg(&$guiObj) -{ - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * Builds ext-js rich table to display matrix results - * - * @param map dataSet: data to be displayed on matrix - * - * return tlExtTable - * - */ -function buildMatrix($dataSet, &$args, $options = array(), $platforms,$customFieldColumns=null) -{ - $default_options = array('bugInterfaceOn' => false,'show_platforms' => false, - 'status_not_run' => false,'format' => FORMAT_HTML); - $options = array_merge($default_options, $options); - - $l18n = init_labels(array('assigned_to' => null,'platform' => null, 'th_date' => null, - 'th_build' => null)); - - - - $columns = array(); - $columns[] = array('title_key' => 'title_test_suite_name', 'width' => 80, 'type' => 'text'); - $columns[] = array('title_key' => 'title_test_case_title', 'width' => 80, 'type' => 'text'); - $columns[] = array('title_key' => 'version', 'width' => 30); - if ($options['show_platforms']) - { - $columns[] = array('title_key' => 'platform', 'width' => 60, 'filter' => 'list', 'filterOptions' => $platforms); - } - if( $options['status_not_run'] ) - { - $columns[] = array('title_key' => 'th_build', 'width' => 35); - $columns[] = array('title_key' => 'assigned_to', 'width' => 60); - $columns[] = array('title_key' => 'summary', 'width' => 150, 'type' => 'text'); - } - else - { - $columns[] = array('title_key' => 'th_build', 'width' => 35); - $columns[] = array('title_key' => 'th_run_by', 'width' => 60); - $columns[] = array('title_key' => 'th_date', 'width' => 60); - $columns[] = array('title_key' => 'title_execution_notes', 'width' => 150, 'type' => 'text'); - - // 20130325 - if(!is_null($customFieldColumns)) - { - foreach($customFieldColumns as $id => $def) - { - $columns[] = array('title' => $def['label'], 'width' => 60); - } - } - - if ($options['bugInterfaceOn']) - { - $columns[] = array('title_key' => 'th_bugs_id_summary', 'type' => 'text'); - } - } - - - if ($options['format'] == FORMAT_HTML) - { - - // IMPORTANT DEVELOPMENT NOTICE - // columns and dataSet are deeply related this means that inside - // dataSet order has to be identical that on columns or table will be a disaster - // - $matrix = new tlExtTable($columns, $dataSet, 'tl_table_results_by_status'); - - //if not run report: sort by test suite - //blocked, failed report: sort by platform (if enabled) else sort by date - $sort_name = 0; - if ($options['status_not_run']) - { - $sort_name = $l18n['assigned_to']; - } - else - { - $sort_name = $options['show_platforms'] ? $l18n['platform'] : $l18n['th_date']; - } - - $matrix->setSortByColumnName($sort_name); - $matrix->setGroupByColumnName($l18n['th_build']); - - $matrix->addCustomBehaviour('text', array('render' => 'columnWrap')); - - //define table toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - } - else - { - $matrix = new tlHTMLTable($columns, $dataSet, 'tl_table_results_by_status'); - } - return $matrix; -} - - -/** - * - */ -function featureLinks($lbl,$img) -{ - $links = array(); - - // %s => test case id - $links['exec_history'] = '' . - ' '; - - // tcase_id,tcversion_id,build_id,tplan_id,platform_id - $links['exec'] = '' . - ' '; - - // %s => test case id - $links['edit'] = '' . - ' '; - - - $links['full'] = $links['exec_history'] . $links['exec'] . $links['edit']; - - return $links; -} - - -/** - * - */ -function initNameCache($guiObj) -{ - $safeItems = array('build' => null, 'platform' => null); - - foreach($guiObj->buildSet as $id => $name) - { - $safeItems['build'][$id] = htmlspecialchars($name); - } - - if($guiObj->show_platforms) - { - foreach($guiObj->platformSet as $id => $name) - { - $safeItems['platform'][$id] = htmlspecialchars($name); - } - } - - return $safeItems; -} - -/** - * - */ -function getWarning($targetStatus,$statusCfg) -{ - $msg = ''; - $key2check = array('not_run','failed','blocked'); - foreach($key2check as $statusVerbose) - { - if( $targetStatus == $statusCfg[$statusVerbose] ) - { - $msg = lang_get('no_' . $statusVerbose . '_with_tester'); - break; - } - } - return $msg; -} - -/** - * - */ -function createSpreadsheet($gui,$args,$media,$customFieldColumns=null) -{ - $lbl = initLblSpreadsheet(); - $cellRange = range('A','Z'); - $style = initStyleSpreadsheet(); - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - - // Step 2 - // data is organized with following columns$dataHeader[] - // Test suite - // Test case - // Version - // Test case summary - // [Platform] - // Build - // Tester - // Date - // Execution notes - // [Custom Field ENABLED ON EXEC 1] - // [Custom Field ENABLED ON EXEC 1] - // [Custom Field ENABLED ON EXEC 1] - // [bugString] only if bugtracking integration exists for this test project - - // This is HOW gui->dataSet is organized - // THIS IS CRITIC ?? - // - // suiteName Issue Tracker Management - // testTitle PTRJ-76:Create issue tracker - no conflict - // testVersion 1 - // bla,bla,bla,.... - // [platformName] - // buildName 1.0 - // testerName admin - // localizedTS 2013-03-28 20:15:06 - // notes [empty string] - // bugString [empty string] - - // - $dataHeader = - array($lbl['title_test_suite_name'],$lbl['title_test_case_title'], - $lbl['version'],$lbl['summary']); - - if( $showPlatforms = ( property_exists($gui,'platformSet') && !is_null($gui->platformSet) && - !isset($gui->platformSet[0])) ) { - $dataHeader[] = $lbl['platform']; - } - - $dataHeader[] = $lbl['build']; - - if ($gui->notRunReport) { - $dataHeader[] = $lbl['assigned_to']; - $dataHeader[] = $lbl['summary']; - } else { - $dataHeader[] = $lbl['th_run_by']; - $dataHeader[] = $lbl['th_date']; - $dataHeader[] = $lbl['title_execution_notes']; - } - - - if(!is_null($customFieldColumns)) { - foreach($customFieldColumns as $id => $def) { - $dataHeader[] = $def['label']; - } - } - - // ATTENTION logic regarding NOT RUN IS MISSING - // For not run this column and also columns regarding CF on exec are not displayed - if( $gui->bugInterfaceOn && !$gui->notRunReport) { - $dataHeader[] = $lbl['th_bugs_id_summary']; - } - - $startingRow = count($lines2write) + 2; // MAGIC - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet()->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - - // Now process data - $startingRow++; - $qta_loops = count($gui->dataSet); - for ($idx = 0; $idx < $qta_loops; $idx++) { - $line2write = $gui->dataSet[$idx]; - $colCounter = 0; - foreach($gui->dataSet[$idx] as $ldx => $field) { - if( $ldx != 'bugString' || ($ldx == 'bugString' && $gui->bugInterfaceOn) ) - { - $cellID = $cellRange[$colCounter] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, html_entity_decode($field) ); - $colCounter++; - } - - // May be same processing can be applied to execution otes - if(($ldx == 'bugString' && $gui->bugInterfaceOn)) { - // To manage new line - // http://stackoverflow.com/questions/5960242/how-to-make-new-lines-in-a-cell-using-phpexcel - // http://stackoverflow.com/questions/6054444/how-to-set-auto-height-in-phpexcel - $objPHPExcel->setActiveSheetIndex(0)->getStyle($cellID)->getAlignment()->setWrapText(true); - } - } - $cellEnd = $cellRange[$colCounter-1] . $startingRow; - $startingRow++; - } - - // Final step - $objPHPExcel->setActiveSheetIndex(0); - - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - - $tmpfname = tempnam(config_get('temp_dir'),"resultsByStatus.tmp"); - $objWriter->save($tmpfname); - - if($args->getSpreadsheetBy == 'email') { - require_once('email_api.php'); - $ema = new stdClass(); - $ema->from_address = config_get('from_email'); - $ema->to_address = $args->user->emailAddress;; - $ema->subject = $gui->mailCfg->subject; - $ema->message = $gui->mailCfg->subject; - - $dum = uniqid("resultsByStatus_") . '.xls'; - $oops = array('attachment' => - array('file' => $tmpfname, 'newname' => $dum), - 'exit_on_error' => true, 'htmlFormat' => true); - $email_op = email_send_wrapper($ema,$oops); - unlink($tmpfname); - exit(); - } else { - downloadXls($tmpfname,$xlsType,$gui,'resultsByStatus_'); - } -} - - -/** - * - */ -function getMetrics(&$dbh,&$args,&$gui) -{ - $resultsCfg = config_get('results'); - $statusCode = $resultsCfg['status_code']; - $metricsMgr = new tlTestPlanMetrics($dbh); - - if( $args->type == $statusCode['not_run'] ) { - $opt = array('output' => 'array'); - if ($args->format == FORMAT_XLS) { - $opt['add2fields'] = 'TCV.summary'; - } - - $met = $metricsMgr->getNotRunWithTesterAssigned($args->tplan_id,null,$opt); - - $gui->notRunReport = true; - $gui->info_msg = $gui->labels['info_notrun_tc_report']; - $gui->notesAccessKey = 'summary'; - $gui->userAccessKey = 'user_id'; - } else { - $opt = array('output' => 'mapByExecID', - 'getOnlyAssigned' => true); - if ($args->format == FORMAT_XLS) { - $opt['add2fields'] = 'TCV.summary'; - } - - $met = $metricsMgr->getExecutionsByStatus($args->tplan_id,$args->type,null,$opt); - - $gui->notRunReport = false; - $gui->info_msg = lang_get('info_' . $resultsCfg['code_status'][$args->type] .'_tc_report'); - - $gui->notesAccessKey = 'execution_notes'; - $gui->userAccessKey='tester_id'; - } - - return $met; -} - -/** - * - */ -function initLblSpreadsheet() -{ - $lbl = init_labels(array('title_test_suite_name' => null,'platform' => null,'build' => null,'th_bugs_id_summary' => null, - 'title_test_case_title' => null,'version' => null, - 'testproject' => null,'generated_by_TestLink_on' => null,'testplan' => null, - 'title_execution_notes' => null, 'th_date' => null, 'th_run_by' => null, - 'assigned_to' => null,'summary' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() -{ - $sty = array(); - $sty['ReportContext'] = array('font' => array('bold' => true)); - $sty['DataHeader'] = array('font' => array('bold' => true), - 'borders' => array('outline' => array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - - return $sty; -} - -/** - * - */ -function xlsStepOne($oj,$style,$lbl,$gui) -{ - - $dummy = ''; - $lines2write = array(array($gui->title,''), - array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time())), - array($gui->report_context,'')); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) - { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0)->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet()->getStyle($cellArea) - ->applyFromArray($style['ReportContext']); - - return $lines2write; -} - diff --git a/lib/results/resultsByTSuite.php b/lib/results/resultsByTSuite.php index aa1063131c..4526aa914a 100644 --- a/lib/results/resultsByTSuite.php +++ b/lib/results/resultsByTSuite.php @@ -1,571 +1,665 @@ -getStatusTotalsTSuiteDepth2ForRender($args->tplan_id,null,array('groupByPlatform' => 1)); - -if(is_null($tsInf)) { - // no test cases -> no report - $gui->do_report['status_ok'] = 0; - $gui->do_report['msg'] = lang_get('report_tspec_has_no_tsuites'); - tLog('Overall Metrics page: no test cases defined'); -} else { - // do report - $gui->statistics->testsuites = $tsInf->infoL2; - $gui->do_report['status_ok'] = 1; - $gui->do_report['msg'] = ''; - - $doubleItemToLoop = array('testsuites'); - foreach( $doubleItemToLoop as $item ) { - if( !is_null($gui->statistics->$item) ) { - $gui->columnsDefinition->$item = array(); - - // Get labels - // !!double current because main key is PLATFORM - $dummy = current(current($gui->statistics->$item)); - if(isset($dummy['details'])) { - foreach($dummy['details'] as $status_verbose => $value) { - $dummy['details'][$status_verbose]['qty'] = lang_get($tlCfg->results['status_label'][$status_verbose]); - $dummy['details'][$status_verbose]['percentage'] = "[%]"; - } - $gui->columnsDefinition->$item = $dummy['details']; - } - } - } - - // Get First & Latest Execution - $execContext = array('testplan_id'); - if ($gui->hasPlatforms) { - $execContext[] = 'platform_id'; - } - $span = $metricsMgr->getExecTimeSpan($args->tplan_id,$execContext); - $gui->spanByPlatform = $span[$args->tplan_id]; - - // reorder data according test suite name - natcasesort($tsInf->idNameMap); - $sortedKeys = array_keys($tsInf->idNameMap); - $gui->dataByPlatform = new stdClass(); - $gui->dataByPlatform->testsuites = array(); - foreach ($gui->statistics->testsuites as $platId => $elem) { - foreach($sortedKeys as $itemID) { - $gui->dataByPlatform->testsuites[$platId][$itemID] = $elem[$itemID]; - } - } -} - -if ($args->doAction == 'saveForBaseline') { - foreach ($gui->dataByPlatform->testsuites as $platID => $elem) { - // Context - $span = $gui->spanByPlatform[$platID]; - $tables = tlObject::getDBTables(array('baseline_l1l2_context', - 'baseline_l1l2_details')); - $sql = "INSERT INTO {$tables['baseline_l1l2_context']} - (testplan_id,platform_id,begin_exec_ts,end_exec_ts) - VALUES({$span['testplan_id']}, - {$span['platform_id']}," . - "'" . $span['begin'] . "'," . - "'" . $span['end'] . "')"; - $db->exec_query($sql); - $context_id = $db->insert_id($tables['baseline_l1l2_context']); - - $cfg = config_get('results'); - $verboseCode = $cfg['status_code']; - - foreach ($elem as $l2_id => $info) { - foreach ($info['details'] as $verbose => $figures) { - $exec_status = "'" . $verboseCode[$verbose] . "'"; - - $sql = "INSERT INTO {$tables['baseline_l1l2_details']} - (context_id,top_tsuite_id,child_tsuite_id, - status,qty,total_tc) - VALUES($context_id,{$info['parent_id']},$l2_id, - $exec_status,{$figures['qty']}, - {$info['total_tc']})"; - $db->exec_query($sql); - } - } - } - $gui->baselineSaved = true; -} - -$timerOff = microtime(true); -$gui->elapsed_time = round($timerOff - $timerOn,2); - -if ($args->spreadsheet) { - createSpreadsheet($gui,$args,$tplan_mgr); -} - -$smarty = new TLSmarty; -$smarty->assign('gui', $gui); -displayReport($tplCfg->tpl, $smarty, $args->format,$mailCfg); - - -/** - * - * - */ -function buildMailCfg(&$guiObj) { - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . - $labels['testproject'] . ' : ' . - $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * - */ -function initializeGui(&$dbHandler,$argsObj,&$tplanMgr) { - - list($add2args,$gui) = initUserEnv($dbHandler,$argsObj); - - $gui->baselineSaved = false; - $gui->fakePlatform = array(''); - $gui->title = lang_get('metrics_by_l1l2_testsuite'); - $gui->do_report = array(); - $gui->showPlatforms=true; - $gui->columnsDefinition = new stdClass(); - $gui->columnsDefinition->keywords = null; - $gui->columnsDefinition->testers = null; - $gui->columnsDefinition->platform = null; - $gui->statistics = new stdClass(); - $gui->statistics->keywords = null; - $gui->statistics->testers = null; - $gui->statistics->milestones = null; - $gui->statistics->overalBuildStatus = null; - $gui->elapsed_time = 0; - $gui->displayBuildMetrics = false; - $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); - - $gui->tproject_name = testproject::getName($dbHandler,$argsObj->tproject_id); - - $info = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->tplan_name = $info['name']; - $gui->tplan_id = intval($argsObj->tplan_id); - - $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'map')); - if( is_null($gui->platformSet) ) { - $gui->platformSet = array(''); - $gui->showPlatforms = false; - } else { - natsort($gui->platformSet); - } - - $base = 'lib/results/resultsByTSuite.php'; - $gui->basehref = $_SESSION['basehref']; - $common = $gui->basehref . $base . "?tplan_id={$gui->tplan_id}" . - "&tproject_id={$gui->tproject_id}&format="; - - $gui->actionSendMail = $common . FORMAT_MAIL_HTML; - $gui->actionSpreadsheet = $common . FORMAT_XLS . "&spreadsheet=1"; - $gui->actionSaveForBaseline = $common . "fake" . "&doAction=saveForBaseline"; - - $gui->mailFeedBack = new stdClass(); - $gui->mailFeedBack->msg = ''; - - $gui->hasPlatforms = count($gui->platformSet) >= 1 && - !isset($gui->platformSet[0]); - - return $gui; -} - - -/** - * - * - */ -function createSpreadsheet($gui,$args,&$tplanMgr) { - - // N sections - // Always same format - // Platform - // Build Assigned Not Run [%] Passed [%] Failed [%] Blocked [%] - // Completed [%] - - // Results by Platform - // Overall Build Status - // Results by Build - // Results by Top Level Test Suite - // Results by priority - // Results by Keyword - - - $lbl = initLblSpreadsheet(); - $cellRange = setCellRangeSpreadsheet(); - $style = initStyleSpreadsheet(); - - // Common - $execStatusDomain = $tplanMgr->getStatusForReports(); - $dataHeaderMetrics = array(); - // $cellPosForExecStatus = array(); - $ccc = 0; - foreach( $execStatusDomain as $code => $human ) { - $dataHeaderMetrics[] = lang_get('test_status_' . $human); - $ccc++; - $dataHeaderMetrics[] = '[%]'; - $ccc++; - //$cellPosForExecStatus[$human] = $ccc; - } - $dataHeaderMetrics[] = $lbl['completed_perc']; - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - - $oneLevel = array(); - - // NO PLATFORM => ID=0 - if( $gui->hasPlatforms ) { - $oneLevel[] = array('entity' => 'platform', - 'dimension' => 'testcase_qty', - 'nameKey' => 'name', 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->platform); - } - - $oneLevel[] = array('entity' => 'build', 'dimension' => 'testcase_qty', - 'nameKey' => 'build_name', - 'tcQtyKey' => 'total_assigned', - 'source' => &$gui->statistics->overallBuildStatus); - - $startingRow = count($lines2write); // MAGIC - foreach( $oneLevel as $target ) { - $entity = $target['entity']; - $dimension = $target['dimension']; - $dataHeader = array($lbl[$entity],$lbl[$dimension]); - - // intermediate column qty is dynamic because it depends - // of status configuration. - foreach( $dataHeaderMetrics as $val ) { - $dataHeader[] = $val; - } - - $startingRow++; - $startingRow++; - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - - $startingRow++; - $infoSet = $target['source']; - $nameKey = $target['nameKey']; - $tcQtyKey = $target['tcQtyKey']; - - foreach($infoSet as $itemID => $fieldSet) { - - $whatCell = 0; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$nameKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$tcQtyKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - foreach($fieldSet['details'] as $human => $metrics) { - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['qty']); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['percentage']); - } - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, - $fieldSet['percentage_completed']); - $startingRow++; - } - } - - // The first column will be the platform - $twoLevels = array(); - - if( $gui->hasPlatforms ) { - $twoLevels[] = - array('entity' => 'build', 'dimension' => 'testcase_qty', - 'nameKey' => 'build_name', - 'tcQtyKey' => 'total_assigned', - 'source' => &$gui->statistics->buildByPlatMetrics); - } - - $twoLevels[] = - array('entity' => 'testsuite', 'dimension' => 'testcase_qty', - 'nameKey' => 'name', - 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->testsuites); - - $twoLevels[] = array('entity' => 'priority', - 'dimension' => 'testcase_qty', - 'nameKey' => 'name', 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->priorities); - - $twoLevels[] = - array('entity' => 'keyword', 'dimension' => 'testcase_qty', - 'nameKey' => 'name', - 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->keywords); - - foreach( $twoLevels as $target ) { - $startingRow++; - $startingRow++; - - $entity = $target['entity']; - $dimension = $target['dimension']; - $nameKey = $target['nameKey']; - $tcQtyKey = $target['tcQtyKey']; - - if( count($target['source']) == 0 ) { - continue; - } - - // Just ONE HEADER ? - $dataHeader = array($lbl['platform'],$lbl[$entity],$lbl[$dimension]); - if( $gui->hasPlatforms == false ) { - array_shift($dataHeader); - } - - // intermediate column qty is dynamic because it depends - // of status configuration. - foreach( $dataHeaderMetrics as $val ) { - $dataHeader[] = $val; - } - - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - // END ONE HEADER - $startingRow++; - - $idr = ''; - foreach( $gui->platformSet as $platID => $platName ) { - $idr = ('' == $idr || 'rowB' == $idr ) ? 'rowA' : 'rowB'; - - $infoSet = isset($target['source'][$platID]) ? - $target['source'][$platID] : array(); - - foreach($infoSet as $itemID => $fieldSet) { - $whatCell=0; - - if( $gui->hasPlatforms ) { - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $platName; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - } - - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$nameKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$tcQtyKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - foreach($fieldSet['details'] as $human => $metrics) { - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['qty']); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['percentage']); - } - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, - $fieldSet['percentage_completed']); - - $cellZone = "A{$startingRow}:" . $cellRange[$whatCell] . - "$startingRow"; - - $objPHPExcel->getActiveSheet() - ->getStyle($cellZone) - ->applyFromArray($style[$idr]); - - $startingRow++; - } - } - } // on container ? - - // Just to add some final empty row - $cellID = $cellRange[0] . $startingRow; - $field = ''; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - - - // Final step - $tmpfname = tempnam(config_get('temp_dir'),"TestLink_GTMP.tmp"); - $objPHPExcel->setActiveSheetIndex(0); - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - $objWriter->save($tmpfname); - - downloadXls($tmpfname,$xlsType,$gui,'TestLink_GTMP_'); -} - - -/** - * - */ -function xlsStepOne(&$oj,$style,&$lbl,&$gui) { - $dummy = ''; - $lines2write = array(array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time()))); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0) - ->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['ReportContext']); - - return $lines2write; -} - -/** - * - */ -function initLblSpreadsheet() { - $lbl = init_labels( - array('testsuite' => null, - 'testcase_qty' => null,'keyword' => null, - 'platform' => null,'priority' => null, - 'priority_level' => null, - 'build' => null,'testplan' => null, - 'testproject' => null,'not_run' => null, - 'completed_perc' => 'trep_comp_perc', - 'generated_by_TestLink_on' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() { - $style = array(); - $style['ReportContext'] = array('font' => array('bold' => true)); - $style['DataHeader'] = - array('font' => array('bold' => true), - 'borders' => - array('outline' => - array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - - $style['rowA'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FFFFFFFF')) - ); - - $style['rowB'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'DCDCDCDC')) - ); - - return $style; -} - -/** - * - */ -function setCellRangeSpreadsheet() { - $cr = range('A','Z'); - $crLen = count($cr); - for($idx = 0; $idx < $crLen; $idx++) { - for($jdx = 0; $jdx < $crLen; $jdx++) { - $cr[] = $cr[$idx] . $cr[$jdx]; - } - } - return $cr; -} - - - -/** - * - */ -function checkRights(&$db,&$user,$context = null) { - if(is_null($context)) { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; +getStatusTotalsTSuiteDepth2ForRender($args->tplan_id, null, + array( + 'groupByPlatform' => 1 + )); + +if (is_null($tsInf)) { + // no test cases -> no report + $gui->do_report['status_ok'] = 0; + $gui->do_report['msg'] = lang_get('report_tspec_has_no_tsuites'); + tLog('Overall Metrics page: no test cases defined'); +} else { + // do report + $gui->statistics->testsuites = $tsInf->infoL2; + $gui->do_report['status_ok'] = 1; + $gui->do_report['msg'] = ''; + + $doubleItemToLoop = array( + 'testsuites' + ); + foreach ($doubleItemToLoop as $item) { + if (! is_null($gui->statistics->$item)) { + $gui->columnsDefinition->$item = array(); + + // Get labels + // !!double current because main key is PLATFORM + if ($gui->statistics->$item) { + $dummy = current(current($gui->statistics->$item)); + } + if (isset($dummy['details'])) { + foreach ($dummy['details'] as $status_verbose => $value) { + $dummy['details'][$status_verbose]['qty'] = lang_get( + $tlCfg->results['status_label'][$status_verbose]); + $dummy['details'][$status_verbose]['percentage'] = "[%]"; + } + $gui->columnsDefinition->$item = $dummy['details']; + } + } + } + + // Get First & Latest Execution + $execContext = array( + 'testplan_id' + ); + if ($gui->hasPlatforms) { + $execContext[] = 'platform_id'; + } + $span = $metricsMgr->getExecTimeSpan($args->tplan_id, $execContext); + + if ($gui->hasPlatforms) { + $gui->spanByPlatform = $span[$args->tplan_id]; + } else { + $gui->spanByPlatform[0] = $span[$args->tplan_id]; + } + + // reorder data according test suite name + natcasesort($tsInf->idNameMap); + $sortedKeys = array_keys($tsInf->idNameMap); + $gui->dataByPlatform = new stdClass(); + $gui->dataByPlatform->testsuites = array(); + foreach ($gui->statistics->testsuites as $platId => $elem) { + foreach ($sortedKeys as $itemID) { + $gui->dataByPlatform->testsuites[$platId][$itemID] = $elem[$itemID]; + } + } +} + +if ($args->doAction == 'saveForBaseline') { + foreach ($gui->dataByPlatform->testsuites as $platID => $elem) { + // Context + $span = $gui->spanByPlatform[$platID]; + $tables = tlObject::getDBTables( + array( + 'baseline_l1l2_context', + 'baseline_l1l2_details' + )); + $sql = "INSERT INTO {$tables['baseline_l1l2_context']} " . + " (testplan_id,platform_id,begin_exec_ts,end_exec_ts) " . + " VALUES({$span['testplan_id']}, {$platID}, '" . $span['begin'] . + "', '" . $span['end'] . "')"; + $db->exec_query($sql); + $context_id = $db->insert_id($tables['baseline_l1l2_context']); + + $cfg = config_get('results'); + $verboseCode = $cfg['status_code']; + + foreach ($elem as $l2_id => $info) { + foreach ($info['details'] as $verbose => $figures) { + $exec_status = "'" . $verboseCode[$verbose] . "'"; + $sql = "INSERT INTO {$tables['baseline_l1l2_details']} " . + " (context_id,top_tsuite_id,child_tsuite_id,status,qty,total_tc) " . + " VALUES($context_id,{$info['parent_id']},$l2_id,$exec_status,{$figures['qty']}, " . + " {$info['total_tc']})"; + $db->exec_query($sql); + } + } + } + $gui->baselineSaved = true; +} + +$timerOff = microtime(true); +$gui->elapsed_time = round($timerOff - $timerOn, 2); + +if ($args->spreadsheet) { + createSpreadsheet($gui, $tplan_mgr); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +displayReport($tplCfg->tpl, $smarty, $args->format, $mailCfg); + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @return stdClass + */ +function initializeGui(&$dbHandler, $argsObj, &$tplanMgr) +{ + list (, $gui) = initUserEnv($dbHandler, $argsObj); + + $gui->baselineSaved = false; + $gui->fakePlatform = array( + '' + ); + $gui->title = lang_get('metrics_by_l1l2_testsuite'); + $gui->do_report = array(); + $gui->showPlatforms = true; + $gui->columnsDefinition = new stdClass(); + $gui->columnsDefinition->keywords = null; + $gui->columnsDefinition->testers = null; + $gui->columnsDefinition->platform = null; + $gui->statistics = new stdClass(); + $gui->statistics->keywords = null; + $gui->statistics->testers = null; + $gui->statistics->milestones = null; + $gui->statistics->overalBuildStatus = null; + $gui->elapsed_time = 0; + $gui->displayBuildMetrics = false; + $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); + + $gui->tproject_name = testproject::getName($dbHandler, $argsObj->tproject_id); + + $info = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->tplan_name = $info['name']; + $gui->tplan_id = intval($argsObj->tplan_id); + + $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'map' + )); + if (is_null($gui->platformSet)) { + $gui->platformSet = array( + '' + ); + $gui->showPlatforms = false; + } else { + natsort($gui->platformSet); + } + + $base = 'lib/results/resultsByTSuite.php'; + $gui->basehref = $_SESSION['basehref']; + $common = $gui->basehref . $base . "?tplan_id={$gui->tplan_id}" . + "&tproject_id={$gui->tproject_id}&format="; + + $gui->actionSendMail = $common . FORMAT_MAIL_HTML; + $gui->actionSpreadsheet = $common . FORMAT_XLS . "&spreadsheet=1"; + $gui->actionSaveForBaseline = $common . "fake" . "&doAction=saveForBaseline"; + + $gui->mailFeedBack = new stdClass(); + $gui->mailFeedBack->msg = ''; + + $gui->hasPlatforms = count($gui->platformSet) >= 1 && + ! isset($gui->platformSet[0]); + + return $gui; +} + +/** + * + * @param stdClass $gui + * @param testplan $tplanMgr + */ +function createSpreadsheet($gui, &$tplanMgr) +{ + // N sections + // Always same format + // Platform + // Build Assigned Not Run [%] Passed [%] Failed [%] Blocked [%] + // Completed [%] + + // Results by Platform + // Overall Build Status + // Results by Build + // Results by Top Level Test Suite + // Results by priority + // Results by Keyword + $lbl = initLblSpreadsheet(); + $cellRange = setCellRangeSpreadsheet(); + $style = initStyleSpreadsheet(); + + // Common + $execStatusDomain = $tplanMgr->getStatusForReports(); + $dataHeaderMetrics = array(); + $ccc = 0; + foreach ($execStatusDomain as $human) { + $dataHeaderMetrics[] = lang_get('test_status_' . $human); + $ccc ++; + $dataHeaderMetrics[] = '[%]'; + $ccc ++; + } + $dataHeaderMetrics[] = $lbl['completed_perc']; + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + + $oneLevel = array(); + + // NO PLATFORM => ID=0 + if ($gui->hasPlatforms) { + $oneLevel[] = array( + 'entity' => 'platform', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->platform + ); + } + + $oneLevel[] = array( + 'entity' => 'build', + 'dimension' => 'testcase_qty', + 'nameKey' => 'build_name', + 'tcQtyKey' => 'total_assigned', + 'source' => &$gui->statistics->overallBuildStatus + ); + + $startingRow = count($lines2write); // MAGIC + foreach ($oneLevel as $target) { + $entity = $target['entity']; + $dimension = $target['dimension']; + $dataHeader = array( + $lbl[$entity], + $lbl[$dimension] + ); + + // intermediate column qty is dynamic because it depends + // of status configuration. + foreach ($dataHeaderMetrics as $val) { + $dataHeader[] = $val; + } + + $startingRow ++; + $startingRow ++; + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + $startingRow ++; + $infoSet = $target['source']; + $nameKey = $target['nameKey']; + $tcQtyKey = $target['tcQtyKey']; + + foreach ($infoSet as $fieldSet) { + + $whatCell = 0; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$nameKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$tcQtyKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + foreach ($fieldSet['details'] as $metrics) { + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['qty']); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['percentage']); + } + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $fieldSet['percentage_completed']); + $startingRow ++; + } + } + + // The first column will be the platform + $twoLevels = array(); + + if ($gui->hasPlatforms) { + $twoLevels[] = array( + 'entity' => 'build', + 'dimension' => 'testcase_qty', + 'nameKey' => 'build_name', + 'tcQtyKey' => 'total_assigned', + 'source' => &$gui->statistics->buildByPlatMetrics + ); + } + + $twoLevels[] = array( + 'entity' => 'testsuite', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->testsuites + ); + + $twoLevels[] = array( + 'entity' => 'priority', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->priorities + ); + + $twoLevels[] = array( + 'entity' => 'keyword', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->keywords + ); + + foreach ($twoLevels as $target) { + $startingRow ++; + $startingRow ++; + + $entity = $target['entity']; + $dimension = $target['dimension']; + $nameKey = $target['nameKey']; + $tcQtyKey = $target['tcQtyKey']; + + if (empty($target['source'])) { + continue; + } + + // Just ONE HEADER ? + $dataHeader = array( + $lbl['platform'], + $lbl[$entity], + $lbl[$dimension] + ); + if (! $gui->hasPlatforms) { + array_shift($dataHeader); + } + + // intermediate column qty is dynamic because it depends + // of status configuration. + foreach ($dataHeaderMetrics as $val) { + $dataHeader[] = $val; + } + + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + // END ONE HEADER + $startingRow ++; + + $idr = ''; + foreach ($gui->platformSet as $platID => $platName) { + $idr = ('' == $idr || 'rowB' == $idr) ? 'rowA' : 'rowB'; + + $infoSet = isset($target['source'][$platID]) ? $target['source'][$platID] : array(); + + foreach ($infoSet as $fieldSet) { + $whatCell = 0; + + if ($gui->hasPlatforms) { + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $platName; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + $whatCell ++; + } + + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$nameKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$tcQtyKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + foreach ($fieldSet['details'] as $metrics) { + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['qty']); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['percentage']); + } + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $fieldSet['percentage_completed']); + + $cellZone = "A{$startingRow}:" . $cellRange[$whatCell] . + "$startingRow"; + + $objPHPExcel->getActiveSheet() + ->getStyle($cellZone) + ->applyFromArray($style[$idr]); + + $startingRow ++; + } + } + } + + // Just to add some final empty row + $cellID = $cellRange[0] . $startingRow; + $field = ''; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + // Final step + $tmpfname = tempnam(config_get('temp_dir'), "TestLink_GTMP.tmp"); + $objPHPExcel->setActiveSheetIndex(0); + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + $objWriter->save($tmpfname); + + downloadXls($tmpfname, $xlsType, $gui, 'TestLink_GTMP_'); +} + +/** + * + * @param PHPExcel $oj + * @param array $style + * @param array $lbl + * @param stdClass $gui + * @return array + */ +function xlsStepOne(&$oj, $style, &$lbl, &$gui) +{ + $dummy = ''; + $lines2write = array( + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + + $cellArea .= "A{$cdx}"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; +} + +/** + * + * @return array + */ +function initLblSpreadsheet() +{ + return init_labels( + array( + 'testsuite' => null, + 'testcase_qty' => null, + 'keyword' => null, + 'platform' => null, + 'priority' => null, + 'priority_level' => null, + 'build' => null, + 'testplan' => null, + 'testproject' => null, + 'not_run' => null, + 'completed_perc' => 'trep_comp_perc', + 'generated_by_TestLink_on' => null + )); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $style = array(); + $style['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $style['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + + $style['rowA'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FFFFFFFF' + ) + ) + ); + + $style['rowB'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'DCDCDCDC' + ) + ) + ); + + return $style; +} + +/** + * + * @return array + */ +function setCellRangeSpreadsheet() +{ + $cr = range('A', 'Z'); + $crLen = count($cr); + for ($idx = 0; $idx < $crLen; $idx ++) { + for ($jdx = 0; $jdx < $crLen; $jdx ++) { + $cr[] = $cr[$idx] . $cr[$jdx]; + } + } + return $cr; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); } diff --git a/lib/results/resultsByTesterPerBuild.php b/lib/results/resultsByTesterPerBuild.php index bcf4044fd5..81f76028d0 100644 --- a/lib/results/resultsByTesterPerBuild.php +++ b/lib/results/resultsByTesterPerBuild.php @@ -1,327 +1,376 @@ -getNumberOfBuilds($args->tplan_id,null,testplan::OPEN_BUILDS); - -// not too wise duplicated code, but effective => Quick & Dirty -if( $openBuildsQty <= 0 && !$args->show_closed_builds) -{ - $gui->warning_message = lang_get('no_open_builds'); - $gui->tableSet = null; - $smarty = new TLSmarty(); - $smarty->assign('gui',$gui); - $smarty->display($templateCfg->template_dir . $templateCfg->default_template); - exit(); +getNumberOfBuilds($args->tplan_id, null, + testplan::OPEN_BUILDS); + +// not too wise duplicated code, but effective => Quick & Dirty +if ($openBuildsQty <= 0 && ! $args->show_closed_builds) { + $gui->warning_message = lang_get('no_open_builds'); + $gui->tableSet = null; + $smarty = new TLSmarty(); + $smarty->assign('gui', $gui); + $smarty->display( + $templateCfg->template_dir . $templateCfg->default_template); + exit(); +} + +$metricsMgr = new tlTestPlanMetrics($db); +$statusCfg = $metricsMgr->getStatusConfig(); +$metrics = $metricsMgr->getStatusTotalsByBuildUAForRender($args->tplan_id, + array( + 'processClosedBuilds' => $args->show_closed_builds + )); +$matrix = $metrics->info; + +// Here need to work, because all queries consider ONLY ACTIVE STATUS +$option = $args->show_closed_builds ? null : testplan::GET_OPEN_BUILD; +$build_set = $metricsMgr->get_builds($args->tplan_id, testplan::GET_ACTIVE_BUILD, + $option); +$names = $user->getNames($db); + +// get the progress of the whole build based on executions of single users +$build_statistics = array(); +foreach ($matrix as $build_id => $build_execution_map) { + $build_statistics[$build_id]['total'] = 0; + $build_statistics[$build_id]['executed'] = 0; + $build_statistics[$build_id]['total_time'] = 0; + + foreach ($build_execution_map as $statistics) { + // total assigned test cases + $build_statistics[$build_id]['total'] += $statistics['total']; + + // total executed testcases + $executed = $statistics['total'] - $statistics['not_run']['count']; + $build_statistics[$build_id]['executed'] += $executed; + + $build_statistics[$build_id]['total_time'] += $statistics['total_time']; + } + + // build progress + $build_statistics[$build_id]['progress'] = round( + $build_statistics[$build_id]['executed'] / + $build_statistics[$build_id]['total'] * 100, 2); + + // We have to fill this if we want time at BUILD LEVEL + $build_statistics[$build_id]['total_time'] = minutes2HHMMSS( + $build_statistics[$build_id]['total_time']); +} + +// build the content of the table +$rows = array(); + +$lblx = array( + 'progress_absolute' => lang_get('progress_absolute'), + 'total_time_hhmmss' => lang_get('total_time_hhmmss') +); + +foreach ($matrix as $build_id => $build_execution_map) { + + $first_row = $build_set[$build_id]['name'] . " - " . + $lblx['progress_absolute'] . + " {$build_statistics[$build_id]['progress']}%" . " - " . + $lblx['total_time_hhmmss'] . + " {$build_statistics[$build_id]['total_time']}"; + + foreach ($build_execution_map as $user_id => $statistics) { + $current_row = array(); + $current_row[] = $first_row; + + // add username and link it to tcAssignedToUser.php + // $username = $names[$user_id]['login']; + $name = "tplan_id});\">{$names[$user_id]['login']}"; + $current_row[] = $name; + + // total count of testcases assigned to this user on this build + $current_row[] = $statistics['total']; + + // add count and percentage for each possible status + foreach ($statusCfg as $status => $code) { + $current_row[] = $statistics[$status]['count']; + $current_row[] = $statistics[$status]['percentage']; + } + + $current_row[] = $statistics['progress']; + + $current_row[] = minutes2HHMMSS($statistics['total_time']); + + // add this row to the others + $rows[] = $current_row; + } +} + +$columns = getTableHeader($statusCfg); +$smartTable = new tlExtTable($columns, $rows, + 'tl_table_results_by_tester_per_build'); +$smartTable->title = lang_get('results_by_tester_per_build'); +$smartTable->setGroupByColumnName(lang_get('build')); + +// enable default sorting by progress column +$smartTable->setSortByColumnName(lang_get('progress')); + +// define toolbar +$smartTable->showToolbar = true; +$smartTable->toolbarExpandCollapseGroupsButton = true; +$smartTable->toolbarShowAllColumnsButton = true; + +$gui->tableSet = array( + $smartTable +); + +// show warning message instead of table if table is empty +$gui->warning_message = ! empty($rows > 0) ? '' : lang_get( + 'no_testers_per_build'); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * initialize user input + * + * @param + * database dbHandler + * @return array $args array with user input information + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N + ), + "show_closed_builds" => array( + tlInputParameter::CB_BOOL + ), + "show_closed_builds_hidden" => array( + tlInputParameter::CB_BOOL + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + $tproject_mgr = new testproject($dbHandler); + $tplan_mgr = new testplan($dbHandler); + if ($args->tproject_id > 0) { + $args->tproject_info = $tproject_mgr->get_by_id($args->tproject_id); + $args->tproject_name = $args->tproject_info['name']; + $args->tproject_description = $args->tproject_info['notes']; + } + + if ($args->tplan_id > 0) { + $args->tplan_info = $tplan_mgr->get_by_id($args->tplan_id); + } + + $selection = false; + if ($args->show_closed_builds) { + $selection = true; + } elseif ($args->show_closed_builds_hidden) { + $selection = false; + } elseif (isset($_SESSION['reports_show_closed_builds'])) { + $selection = $_SESSION['reports_show_closed_builds']; + } + $args->show_closed_builds = $_SESSION['reports_show_closed_builds'] = $selection; + + return array( + $args, + $tproject_mgr, + $tplan_mgr + ); +} + +/** + * initialize GUI + * + * @param stdClass $argsObj + * reference to user input + * @return stdClass $gui gui data + */ +function initGui(&$argsObj) +{ + $gui = new stdClass(); + $gui->pageTitle = lang_get('caption_results_by_tester_per_build'); + $gui->warning_msg = ''; + $gui->tproject_name = $argsObj->tproject_name; + $gui->tplan_name = $argsObj->tplan_info['name']; + $gui->show_closed_builds = $argsObj->show_closed_builds; + + return $gui; +} + +/** + * + * @param array $statusCfg + * @return array + */ +function getTableHeader($statusCfg) +{ + $resultsCfg = config_get('results'); + + $colCfg = array(); + $colCfg[] = array( + 'title_key' => 'build', + 'width' => 50, + 'type' => 'text', + 'sortType' => 'asText', + 'filter' => 'string' + ); + $colCfg[] = array( + 'title_key' => 'user', + 'width' => 50, + 'type' => 'text', + 'sortType' => 'asText', + 'filter' => 'string' + ); + $colCfg[] = array( + 'title_key' => 'th_tc_assigned', + 'width' => 50, + 'sortType' => 'asFloat', + 'filter' => 'numeric' + ); + + foreach ($statusCfg as $status => $code) { + $label = $resultsCfg['status_label'][$status]; + $colCfg[] = array( + 'title_key' => $label, + 'width' => 20, + 'sortType' => 'asInt', + 'filter' => 'numeric' + ); + $colCfg[] = array( + 'title' => lang_get($label) . ' ' . lang_get('in_percent'), + 'col_id' => 'id_' . $label . '_percent', + 'width' => 30, + 'type' => 'float', + 'sortType' => 'asFloat', + 'filter' => 'numeric' + ); + } + + $colCfg[] = array( + 'title_key' => 'progress', + 'width' => 30, + 'type' => 'float', + 'sortType' => 'asFloat', + 'filter' => 'numeric' + ); + + $colCfg[] = array( + 'title' => lang_get('total_time_hhmmss'), + 'width' => 30, + 'type' => 'text', + 'sortType' => 'asText', + 'filter' => 'string' + ); + + return $colCfg; +} + +/** + * + * ATTENTION: + * because minutes can be a decimal (i.e 131.95) if I use standard operations i can get + * wrong results + * + * @param int $minutes + * @return string + */ +function minutes2HHMMSS($minutes) +{ + // Attention: + // $min2sec = $minutes * 60; + // doing echo provide expected result, but when using to do more math op + // result was wrong, 1 second loss. + // Example with 131.95 as input + // $min2sec = sprintf('%d',($minutes * 60)); + $min2sec = bcmul($minutes, 60); + + // From here number will not have decimal => will return to normal operators. + // do not know perfomance impacts related to BC* functions + $hh = floor($min2sec / 3600); + $mmss = ($min2sec % 3600); + + $mm = floor($mmss / 60); + $ss = $mmss % 60; + + return sprintf('%02d:%02d:%02d', $hh, $mm, $ss); +} + +/** + * rights check function for testlinkInitPage() + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); } - - -$metricsMgr = new tlTestPlanMetrics($db); -$statusCfg = $metricsMgr->getStatusConfig(); -$metrics = $metricsMgr->getStatusTotalsByBuildUAForRender($args->tplan_id, - array('processClosedBuilds' => $args->show_closed_builds)); -$matrix = $metrics->info; - -// Here need to work, because all queries consider ONLY ACTIVE STATUS -$option = $args->show_closed_builds ? null : testplan::GET_OPEN_BUILD; -$build_set = $metricsMgr->get_builds($args->tplan_id, testplan::GET_ACTIVE_BUILD, $option); -$names = $user->getNames($db); - -// get the progress of the whole build based on executions of single users -$build_statistics = array(); -foreach($matrix as $build_id => $build_execution_map) -{ - $build_statistics[$build_id]['total'] = 0; - $build_statistics[$build_id]['executed'] = 0; - $build_statistics[$build_id]['total_time'] = 0; - - foreach ($build_execution_map as $user_id => $statistics) - { - // total assigned test cases - $build_statistics[$build_id]['total'] += $statistics['total']; - - // total executed testcases - $executed = $statistics['total'] - $statistics['not_run']['count']; - $build_statistics[$build_id]['executed'] += $executed; - - $build_statistics[$build_id]['total_time'] += $statistics['total_time']; - } - - // build progress - $build_statistics[$build_id]['progress'] = round($build_statistics[$build_id]['executed'] / - $build_statistics[$build_id]['total'] * 100,2); - - // We have to fill this if we want time at BUILD LEVEL - $build_statistics[$build_id]['total_time'] = minutes2HHMMSS($build_statistics[$build_id]['total_time']); -} - -// build the content of the table -$rows = array(); - -$lblx = array('progress_absolute' => lang_get('progress_absolute'), - 'total_time_hhmmss' => lang_get('total_time_hhmmss') ); - -foreach ($matrix as $build_id => $build_execution_map) -{ - - $first_row = $build_set[$build_id]['name'] . " - " . - $lblx['progress_absolute'] . " {$build_statistics[$build_id]['progress']}%" ." - " . - $lblx['total_time_hhmmss']. " {$build_statistics[$build_id]['total_time']}"; - - foreach ($build_execution_map as $user_id => $statistics) - { - $current_row = array(); - $current_row[] = $first_row; - - // add username and link it to tcAssignedToUser.php - // $username = $names[$user_id]['login']; - $name = "tplan_id});\">{$names[$user_id]['login']}"; - $current_row[] = $name; - - // total count of testcases assigned to this user on this build - $current_row[] = $statistics['total']; - - // add count and percentage for each possible status - foreach ($statusCfg as $status => $code) - { - $current_row[] = $statistics[$status]['count']; - $current_row[] = $statistics[$status]['percentage']; - } - - $current_row[] = $statistics['progress']; - - $current_row[] = minutes2HHMMSS($statistics['total_time']); - - // add this row to the others - $rows[] = $current_row; - } -} - -$columns = getTableHeader($statusCfg); -$smartTable = new tlExtTable($columns, $rows, 'tl_table_results_by_tester_per_build'); -$smartTable->title = lang_get('results_by_tester_per_build'); -$smartTable->setGroupByColumnName(lang_get('build')); - -// enable default sorting by progress column -$smartTable->setSortByColumnName(lang_get('progress')); - -//define toolbar -$smartTable->showToolbar = true; -$smartTable->toolbarExpandCollapseGroupsButton = true; -$smartTable->toolbarShowAllColumnsButton = true; - -$gui->tableSet = array($smartTable); - -// show warning message instead of table if table is empty -$gui->warning_message = (count($rows) > 0) ? '' : lang_get('no_testers_per_build'); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * initialize user input - * - * @param resource dbHandler - * @return array $args array with user input information - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N), - "show_closed_builds" => array(tlInputParameter::CB_BOOL), - "show_closed_builds_hidden" => array(tlInputParameter::CB_BOOL)); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - if( !is_null($args->apikey) ) - { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - $args->addOpAccess = false; - $cerbero->method = null; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } - else - { - testlinkInitPage($dbHandler,false,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - $tproject_mgr = new testproject($dbHandler); - $tplan_mgr = new testplan($dbHandler); - if($args->tproject_id > 0) - { - $args->tproject_info = $tproject_mgr->get_by_id($args->tproject_id); - $args->tproject_name = $args->tproject_info['name']; - $args->tproject_description = $args->tproject_info['notes']; - } - - if ($args->tplan_id > 0) - { - $args->tplan_info = $tplan_mgr->get_by_id($args->tplan_id); - } - - $selection = false; - if($args->show_closed_builds) - { - $selection = true; - } - else if ($args->show_closed_builds_hidden) - { - $selection = false; - } - else if (isset($_SESSION['reports_show_closed_builds'])) - { - $selection = $_SESSION['reports_show_closed_builds']; - } - $args->show_closed_builds = $_SESSION['reports_show_closed_builds'] = $selection; - - return array($args,$tproject_mgr,$tplan_mgr); -} - - -/** - * initialize GUI - * - * @param stdClass $argsObj reference to user input - * @return stdClass $gui gui data - */ -function init_gui(&$argsObj) -{ - $gui = new stdClass(); - - $gui->pageTitle = lang_get('caption_results_by_tester_per_build'); - $gui->warning_msg = ''; - $gui->tproject_name = $argsObj->tproject_name; - $gui->tplan_name = $argsObj->tplan_info['name']; - $gui->show_closed_builds = $argsObj->show_closed_builds; - return $gui; -} - -/** - * - * - */ -function getTableHeader($statusCfg) -{ - $resultsCfg = config_get('results'); - - $colCfg = array(); - $colCfg[] = array('title_key' => 'build', 'width' => 50, - 'type' => 'text', 'sortType' => 'asText','filter' => 'string'); - $colCfg[] = array('title_key' => 'user', 'width' => 50, - 'type' => 'text', 'sortType' => 'asText','filter' => 'string'); - $colCfg[] = array('title_key' => 'th_tc_assigned', - 'width' => 50, 'sortType' => 'asFloat','filter' => 'numeric'); - - foreach ($statusCfg as $status => $code) - { - $label = $resultsCfg['status_label'][$status]; - $colCfg[] = array('title_key' => $label, 'width' => 20, 'sortType' => 'asInt','filter' => 'numeric'); - $colCfg[] = array('title' => lang_get($label).' '.lang_get('in_percent'), - 'col_id' => 'id_'.$label.'_percent', 'width' => 30, - 'type' => 'float', 'sortType' => 'asFloat', 'filter' => 'numeric'); - } - - $colCfg[] = array('title_key' => 'progress', 'width' => 30, - 'type' => 'float','sortType' => 'asFloat', 'filter' => 'numeric'); - - $colCfg[] = array('title' => lang_get('total_time_hhmmss'), 'width' => 30, - 'type' => 'text','sortType' => 'asText', 'filter' => 'string'); - - return $colCfg; -} - -/** - * - * ATTENTION: - * because minutes can be a decimal (i.e 131.95) if I use standard operations i can get - * wrong results - * - * - */ -function minutes2HHMMSS($minutes) -{ - // Attention: - // $min2sec = $minutes * 60; - // doing echo provide expected result, but when using to do more math op - // result was wrong, 1 second loss. - // Example with 131.95 as input - // $min2sec = sprintf('%d',($minutes * 60)); - $min2sec = bcmul($minutes, 60); - - // From here number will not have decimal => will return to normal operators. - // do not know perfomance impacts related to BC* functions - $hh = floor($min2sec/3600); - $mmss = ($min2sec%3600); - - $mm = floor($mmss/60); - $ss = $mmss%60; - - return sprintf('%02d:%02d:%02d', $hh, $mm, $ss); -} - - - - -/* - * rights check function for testlinkInitPage() - */ -function checkRights(&$db,&$user,$context = null) -{ - if(is_null($context)) - { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; -} - - diff --git a/lib/results/resultsGeneral.php b/lib/results/resultsGeneral.php index 66de14926d..d4a4f6058e 100644 --- a/lib/results/resultsGeneral.php +++ b/lib/results/resultsGeneral.php @@ -1,651 +1,764 @@ -getStatusTotalsByTopLevelTestSuiteForRender($args->tplan_id,null,array('groupByPlatform' => 1)); - -if(is_null($tsInf)) { - // no test cases -> no report - $gui->do_report['status_ok'] = 0; - $gui->do_report['msg'] = lang_get('report_tspec_has_no_tsuites'); - tLog('Overall Metrics page: no test cases defined'); -} else { - - // do report - $gui->statistics->testsuites = $tsInf->info; - $gui->do_report['status_ok'] = 1; - $gui->do_report['msg'] = ''; - - - $keywordsMetrics = - $metricsMgr->getStatusTotalsByKeywordForRender($args->tplan_id,null, array('groupByPlatform' => 1) ); - - $gui->statistics->keywords = !is_null($keywordsMetrics) ? $keywordsMetrics->info : null; - - if( $gui->showPlatforms ) { - $items2loop[] = 'platform'; - $platformMetrics = $metricsMgr->getStatusTotalsByPlatformForRender($args->tplan_id); - $gui->statistics->platform = !is_null($platformMetrics) ? $platformMetrics->info : null; - } - - if($gui->testprojectOptions->testPriorityEnabled) { - $filters = null; - $opt = array('getOnlyAssigned' => false, - 'groupByPlatform' => 1); - $priorityMetrics = $metricsMgr->getStatusTotalsByPriorityForRender($args->tplan_id,$filters,$opt); - $gui->statistics->priorities = !is_null($priorityMetrics) ? $priorityMetrics->info : null; - } - - - foreach($items2loop as $item) { - if( !is_null($gui->statistics->$item) ) { - $gui->columnsDefinition->$item = array(); - - // Get labels - $dummy = current($gui->statistics->$item); - if(isset($dummy['details'])) { - foreach($dummy['details'] as $status_verbose => $value) { - $dummy['details'][$status_verbose]['qty'] = lang_get($tlCfg->results['status_label'][$status_verbose]); - $dummy['details'][$status_verbose]['percentage'] = "[%]"; - } - $gui->columnsDefinition->$item = $dummy['details']; - } - } - } - - $doubleItemToLoop = array('priorities','keywords','testsuites'); - foreach( $doubleItemToLoop as $item ) { - if( !is_null($gui->statistics->$item) ) { - $gui->columnsDefinition->$item = array(); - - // Get labels - // !!double current because main key is PLATFORM - $dummy = current(current($gui->statistics->$item)); - if(isset($dummy['details'])) { - foreach($dummy['details'] as $status_verbose => $value) { - $dummy['details'][$status_verbose]['qty'] = lang_get($tlCfg->results['status_label'][$status_verbose]); - $dummy['details'][$status_verbose]['percentage'] = "[%]"; - } - $gui->columnsDefinition->$item = $dummy['details']; - } - } - } - - - /* BUILDS REPORT */ - $colDefinition = null; - $results = null; - if($gui->do_report['status_ok']) { - - $o = $metricsMgr->getOverallBuildStatusForRender($args->tplan_id); - $gui->statistics->overallBuildStatus = $o->info; - $gui->columnsDefinition->overallBuildStatus = $o->colDefinition; - $gui->displayBuildMetrics = !is_null($gui->statistics->overallBuildStatus); - } - - // Build by Platform - $colDefinition = null; - $results = null; - if($gui->do_report['status_ok']) { - $o = $metricsMgr->getBuildByPlatStatusForRender($args->tplan_id); - - $gui->statistics->buildByPlatMetrics = new stdClass(); - $gui->statistics->buildByPlatMetrics = $o->info; - $gui->columnsDefinition->buildByPlatMetrics = $o->colDefinition; - - $gui->displayBuildByPlatMetrics = - !is_null($gui->statistics->buildByPlatMetrics); - } - - - - /* MILESTONE & PRIORITY REPORT */ - // Need to be refactored ??? - $milestonesList = $tplan_mgr->get_milestones($args->tplan_id); - if (!empty($milestonesList)) { - $gui->statistics->milestones = $metricsMgr->getMilestonesMetrics($args->tplan_id,$milestonesList); - } - -} - -$timerOff = microtime(true); -$gui->elapsed_time = round($timerOff - $timerOn,2); - -if( $args->spreadsheet ) { - createSpreadsheet($gui,$args,$tplan_mgr); -} - - -$smarty = new TLSmarty; -$smarty->assign('gui', $gui); -displayReport($tplCfg->tpl, $smarty, $args->format,$mailCfg); - - - -/* - function: init_args - args: none - returns: array -*/ -function init_args(&$dbHandler) { - $tplanMgr = null; - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "format" => array(tlInputParameter::INT_N), - "sendByMail" => array(tlInputParameter::INT_N), - "spreadsheet" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - $pParams = G_PARAMS($iParams,$args); - - $args->spreadsheet = intval($args->spreadsheet); - if( !is_null($args->apikey) ) { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } else { - $args->addOpAccess = false; - $cerbero->method = null; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } else { - testlinkInitPage($dbHandler,true,false,"checkRights"); - - $tplanMgr = new testplan($dbHandler); - $tplan = $tplanMgr->get_by_id($args->tplan_id); - $args->tproject_id = $tplan['testproject_id']; - } - - if($args->tproject_id <= 0) { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid Test Project ID ({$args->tproject_id})"; - throw new Exception($msg); - } - - if (is_null($args->format)) { - tlog("Parameter 'format' is not defined", 'ERROR'); - exit(); - } - - $args->user = $_SESSION['currentUser']; - $args->format = $args->sendByMail ? FORMAT_MAIL_HTML : $args->format; - - return array($tplanMgr,$args); -} - - -/** - * - * - */ -function buildMailCfg(&$guiObj) { - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * - */ -function initializeGui(&$dbHandler,$argsObj,&$tplanMgr) { - $gui = new stdClass(); - $gui->fakePlatform = array(''); - $gui->title = lang_get('title_gen_test_rep'); - $gui->do_report = array(); - $gui->showPlatforms=true; - $gui->columnsDefinition = new stdClass(); - $gui->columnsDefinition->keywords = null; - $gui->columnsDefinition->testers = null; - $gui->columnsDefinition->platform = null; - $gui->statistics = new stdClass(); - $gui->statistics->keywords = null; - $gui->statistics->testers = null; - $gui->statistics->milestones = null; - $gui->statistics->overalBuildStatus = null; - $gui->elapsed_time = 0; - $gui->displayBuildMetrics = false; - $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); - - $mgr = new testproject($dbHandler); - $dummy = $mgr->get_by_id($argsObj->tproject_id); - $gui->testprojectOptions = new stdClass(); - $gui->testprojectOptions->testPriorityEnabled = $dummy['opt']->testPriorityEnabled; - $gui->tproject_name = $dummy['name']; - - $info = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->tplan_name = $info['name']; - $gui->tplan_id = intval($argsObj->tplan_id); - - $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'map')); - if( is_null($gui->platformSet) ) { - $gui->platformSet = array(''); - $gui->showPlatforms = false; - } else { - natsort($gui->platformSet); - } - - $gui->basehref = $_SESSION['basehref']; - $gui->actionSendMail = $gui->basehref . - "lib/results/resultsGeneral.php?format=" . - FORMAT_MAIL_HTML . "&tplan_id={$gui->tplan_id}"; - - $gui->actionSpreadsheet = $gui->basehref . - "lib/results/resultsGeneral.php?format=" . - FORMAT_XLS . "&tplan_id={$gui->tplan_id}&spreadsheet=1"; - - - $gui->mailFeedBack = new stdClass(); - $gui->mailFeedBack->msg = ''; - return $gui; -} - - -/** - * - * - */ -function createSpreadsheet($gui,$args,&$tplanMgr) { - - // N sections - // Always same format - // Platform - // Build Assigned Not Run [%] Passed [%] Failed [%] Blocked [%] - // Completed [%] - - // Results by Platform - // Overall Build Status - // Results by Build - // Results by Top Level Test Suite - // Results by priority - // Results by Keyword - - - $lbl = initLblSpreadsheet(); - $cellRange = setCellRangeSpreadsheet(); - $style = initStyleSpreadsheet(); - - // Common - $execStatusDomain = $tplanMgr->getStatusForReports(); - $dataHeaderMetrics = array(); - // $cellPosForExecStatus = array(); - $ccc = 0; - foreach( $execStatusDomain as $code => $human ) { - $dataHeaderMetrics[] = lang_get('test_status_' . $human); - $ccc++; - $dataHeaderMetrics[] = '[%]'; - $ccc++; - //$cellPosForExecStatus[$human] = $ccc; - } - $dataHeaderMetrics[] = $lbl['completed_perc']; - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - - $oneLevel = array(); - - // NO PLATFORM => ID=0 - $hasPlatforms = count($gui->platformSet) >= 1 && - !isset($gui->platformSet[0]); - if( $hasPlatforms ) { - $oneLevel[] = array('entity' => 'platform', - 'dimension' => 'testcase_qty', - 'nameKey' => 'name', 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->platform); - } - - $oneLevel[] = array('entity' => 'build', 'dimension' => 'testcase_qty', - 'nameKey' => 'build_name', - 'tcQtyKey' => 'total_assigned', - 'source' => &$gui->statistics->overallBuildStatus); - - $startingRow = count($lines2write); // MAGIC - foreach( $oneLevel as $target ) { - $entity = $target['entity']; - $dimension = $target['dimension']; - $dataHeader = array($lbl[$entity],$lbl[$dimension]); - - // intermediate column qty is dynamic because it depends - // of status configuration. - foreach( $dataHeaderMetrics as $val ) { - $dataHeader[] = $val; - } - - $startingRow++; - $startingRow++; - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - - $startingRow++; - $infoSet = $target['source']; - $nameKey = $target['nameKey']; - $tcQtyKey = $target['tcQtyKey']; - - foreach($infoSet as $itemID => $fieldSet) { - - $whatCell = 0; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$nameKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$tcQtyKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - foreach($fieldSet['details'] as $human => $metrics) { - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['qty']); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['percentage']); - } - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, - $fieldSet['percentage_completed']); - $startingRow++; - } - } - - // The first column will be the platform - $twoLevels = array(); - - if( $hasPlatforms ) { - $twoLevels[] = - array('entity' => 'build', 'dimension' => 'testcase_qty', - 'nameKey' => 'build_name', - 'tcQtyKey' => 'total_assigned', - 'source' => &$gui->statistics->buildByPlatMetrics); - } - - $twoLevels[] = - array('entity' => 'testsuite', 'dimension' => 'testcase_qty', - 'nameKey' => 'name', - 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->testsuites); - - $twoLevels[] = array('entity' => 'priority', - 'dimension' => 'testcase_qty', - 'nameKey' => 'name', 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->priorities); - - $twoLevels[] = - array('entity' => 'keyword', 'dimension' => 'testcase_qty', - 'nameKey' => 'name', - 'tcQtyKey' => 'total_tc', - 'source' => &$gui->statistics->keywords); - - foreach( $twoLevels as $target ) { - $startingRow++; - $startingRow++; - - $entity = $target['entity']; - $dimension = $target['dimension']; - $nameKey = $target['nameKey']; - $tcQtyKey = $target['tcQtyKey']; - - if( count($target['source']) == 0 ) { - continue; - } - - // Just ONE HEADER ? - $dataHeader = array($lbl['platform'],$lbl[$entity],$lbl[$dimension]); - if( $hasPlatforms == false ) { - array_shift($dataHeader); - } - - // intermediate column qty is dynamic because it depends - // of status configuration. - foreach( $dataHeaderMetrics as $val ) { - $dataHeader[] = $val; - } - - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['DataHeader']); - // END ONE HEADER - $startingRow++; - - $idr = ''; - foreach( $gui->platformSet as $platID => $platName ) { - $idr = ('' == $idr || 'rowB' == $idr ) ? 'rowA' : 'rowB'; - - $infoSet = isset($target['source'][$platID]) ? - $target['source'][$platID] : array(); - - foreach($infoSet as $itemID => $fieldSet) { - $whatCell=0; - - if( $hasPlatforms ) { - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $platName; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - } - - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$nameKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $field = $fieldSet[$tcQtyKey]; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - foreach($fieldSet['details'] as $human => $metrics) { - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['qty']); - - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, $metrics['percentage']); - } - $whatCell++; - $cellID = $cellRange[$whatCell] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0) - ->setCellValue($cellID, - $fieldSet['percentage_completed']); - - $cellZone = "A{$startingRow}:" . $cellRange[$whatCell] . - "$startingRow"; - - $objPHPExcel->getActiveSheet() - ->getStyle($cellZone) - ->applyFromArray($style[$idr]); - - $startingRow++; - } - } - } // on container ? - - // Just to add some final empty row - $cellID = $cellRange[0] . $startingRow; - $field = ''; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - - - - // Final step - $tmpfname = tempnam(config_get('temp_dir'),"TestLink_GTMP.tmp"); - $objPHPExcel->setActiveSheetIndex(0); - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - $objWriter->save($tmpfname); - - downloadXls($tmpfname,$xlsType,$gui,'TestLink_GTMP_'); -} - - -/** - * - */ -function xlsStepOne(&$oj,$style,&$lbl,&$gui) { - $dummy = ''; - $lines2write = array(array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time()))); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0) - ->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet() - ->getStyle($cellArea) - ->applyFromArray($style['ReportContext']); - - return $lines2write; -} - -/** - * - */ -function initLblSpreadsheet() { - $lbl = init_labels( - array('testsuite' => null, - 'testcase_qty' => null,'keyword' => null, - 'platform' => null,'priority' => null, - 'priority_level' => null, - 'build' => null,'testplan' => null, - 'testproject' => null,'not_run' => null, - 'completed_perc' => 'trep_comp_perc', - 'generated_by_TestLink_on' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() { - $style = array(); - $style['ReportContext'] = array('font' => array('bold' => true)); - $style['DataHeader'] = - array('font' => array('bold' => true), - 'borders' => - array('outline' => - array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - - $style['rowA'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FFFFFFFF')) - ); - - $style['rowB'] = - array('borders' => - array( - 'outline' => - array('style' => PHPExcel_Style_Border::BORDER_THIN), - 'vertical' => - array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'DCDCDCDC')) - ); - - return $style; -} - -/** - * - */ -function setCellRangeSpreadsheet() { - $cr = range('A','Z'); - $crLen = count($cr); - for($idx = 0; $idx < $crLen; $idx++) { - for($jdx = 0; $jdx < $crLen; $jdx++) { - $cr[] = $cr[$idx] . $cr[$jdx]; - } - } - return $cr; -} - - - -/** - * - */ -function checkRights(&$db,&$user,$context = null) { - if(is_null($context)) { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; +getStatusTotalsByTopLevelTestSuiteForRender( + $args->tplan_id, null, array( + 'groupByPlatform' => 1 + )); + +if (is_null($tsInf)) { + // no test cases -> no report + $gui->do_report['status_ok'] = 0; + $gui->do_report['msg'] = lang_get('report_tspec_has_no_tsuites'); + tLog('Overall Metrics page: no test cases defined'); +} else { + + // do report + $gui->statistics->testsuites = $tsInf->info; + $gui->do_report['status_ok'] = 1; + $gui->do_report['msg'] = ''; + + $keywordsMetrics = $metricsMgr->getStatusTotalsByKeywordForRender( + $args->tplan_id, null, array( + 'groupByPlatform' => 1 + )); + + $gui->statistics->keywords = ! is_null($keywordsMetrics) ? $keywordsMetrics->info : null; + + if ($gui->showPlatforms) { + $items2loop[] = 'platform'; + $platformMetrics = $metricsMgr->getStatusTotalsByPlatformForRender( + $args->tplan_id); + $gui->statistics->platform = ! is_null($platformMetrics) ? $platformMetrics->info : null; + } + + if ($gui->testprojectOptions->testPriorityEnabled) { + $filters = null; + $opt = array( + 'getOnlyAssigned' => false, + 'groupByPlatform' => 1 + ); + $priorityMetrics = $metricsMgr->getStatusTotalsByPriorityForRender( + $args->tplan_id, $filters, $opt); + $gui->statistics->priorities = ! is_null($priorityMetrics) ? $priorityMetrics->info : null; + } + + foreach ($items2loop as $item) { + if (! is_null($gui->statistics->$item)) { + $gui->columnsDefinition->$item = array(); + + // Get labels + $dummy = current($gui->statistics->$item); + if (isset($dummy['details'])) { + foreach ($dummy['details'] as $status_verbose => $value) { + $dummy['details'][$status_verbose]['qty'] = lang_get( + $tlCfg->results['status_label'][$status_verbose]); + $dummy['details'][$status_verbose]['percentage'] = "[%]"; + } + $gui->columnsDefinition->$item = $dummy['details']; + } + } + } + + $doubleItemToLoop = array( + 'priorities', + 'keywords', + 'testsuites' + ); + foreach ($doubleItemToLoop as $item) { + if (! empty($gui->statistics->$item)) { + $gui->columnsDefinition->$item = array(); + + // Get labels + // !!double current because main key is PLATFORM + $dummy = current(current($gui->statistics->$item)); + if (isset($dummy['details'])) { + foreach ($dummy['details'] as $status_verbose => $value) { + $dummy['details'][$status_verbose]['qty'] = lang_get( + $tlCfg->results['status_label'][$status_verbose]); + $dummy['details'][$status_verbose]['percentage'] = "[%]"; + } + $gui->columnsDefinition->$item = $dummy['details']; + } + } + } + + /* BUILDS REPORT */ + $colDefinition = null; + $results = null; + if ($gui->do_report['status_ok']) { + + $o = $metricsMgr->getOverallBuildStatusForRender($args->tplan_id); + $gui->statistics->overallBuildStatus = $o->info; + $gui->columnsDefinition->overallBuildStatus = $o->colDefinition; + $gui->displayBuildMetrics = ! is_null( + $gui->statistics->overallBuildStatus); + } + + // Build by Platform + $colDefinition = null; + $results = null; + if ($gui->do_report['status_ok']) { + $o = $metricsMgr->getBuildByPlatStatusForRender($args->tplan_id); + + $gui->statistics->buildByPlatMetrics = new stdClass(); + $gui->statistics->buildByPlatMetrics = $o->info; + $gui->columnsDefinition->buildByPlatMetrics = $o->colDefinition; + + $gui->displayBuildByPlatMetrics = ! is_null( + $gui->statistics->buildByPlatMetrics); + } + + /* MILESTONE & PRIORITY REPORT */ + // Need to be refactored ??? + $milestonesList = $tplan_mgr->get_milestones($args->tplan_id); + if (! empty($milestonesList)) { + $gui->statistics->milestones = $metricsMgr->getMilestonesMetrics( + $args->tplan_id, $milestonesList); + } +} + +$timerOff = microtime(true); +$gui->elapsed_time = round($timerOff - $timerOn, 2); + +if ($args->spreadsheet) { + createSpreadsheet($gui, $tplan_mgr); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +displayReport($tplCfg->tpl, $smarty, $args->format, $mailCfg); + +/** + * + * @param database $dbHandler + * @return array + */ +function initArgs(&$dbHandler) +{ + $tplanMgr = null; + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "format" => array( + tlInputParameter::INT_N + ), + "sendByMail" => array( + tlInputParameter::INT_N + ), + "spreadsheet" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + G_PARAMS($iParams, $args); + + $args->spreadsheet = intval($args->spreadsheet); + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, true, false, "checkRights"); + + $tplanMgr = new testplan($dbHandler); + $tplan = $tplanMgr->get_by_id($args->tplan_id); + $args->tproject_id = $tplan['testproject_id']; + } + + if ($args->tproject_id <= 0) { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid Test Project ID ({$args->tproject_id})"; + throw new Exception($msg); + } + + if (is_null($args->format)) { + tlog("Parameter 'format' is not defined", 'ERROR'); + exit(); + } + + $args->user = $_SESSION['currentUser']; + $args->format = $args->sendByMail ? FORMAT_MAIL_HTML : $args->format; + + return array( + $tplanMgr, + $args + ); +} + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param testplan $tplanMgr + * @return stdClass + */ +function initializeGui(&$dbHandler, $argsObj, &$tplanMgr) +{ + $gui = new stdClass(); + $gui->fakePlatform = array( + '' + ); + $gui->title = lang_get('title_gen_test_rep'); + $gui->do_report = array(); + $gui->showPlatforms = true; + $gui->columnsDefinition = new stdClass(); + $gui->columnsDefinition->keywords = null; + $gui->columnsDefinition->testers = null; + $gui->columnsDefinition->platform = null; + $gui->statistics = new stdClass(); + $gui->statistics->keywords = null; + $gui->statistics->testers = null; + $gui->statistics->milestones = null; + $gui->statistics->overalBuildStatus = null; + $gui->elapsed_time = 0; + $gui->displayBuildMetrics = false; + $gui->buildMetricsFeedback = lang_get('buildMetricsFeedback'); + + $mgr = new testproject($dbHandler); + $dummy = $mgr->get_by_id($argsObj->tproject_id); + $gui->testprojectOptions = new stdClass(); + $gui->testprojectOptions->testPriorityEnabled = $dummy['opt']->testPriorityEnabled; + $gui->tproject_name = $dummy['name']; + + $info = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->tplan_name = $info['name']; + $gui->tplan_id = intval($argsObj->tplan_id); + + $gui->platformSet = $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'map' + )); + if (is_null($gui->platformSet)) { + $gui->platformSet = array( + '' + ); + $gui->showPlatforms = false; + } else { + natsort($gui->platformSet); + } + + $gui->basehref = $_SESSION['basehref']; + $gui->actionSendMail = $gui->basehref . + "lib/results/resultsGeneral.php?format=" . FORMAT_MAIL_HTML . + "&tplan_id={$gui->tplan_id}"; + + $gui->actionSpreadsheet = $gui->basehref . + "lib/results/resultsGeneral.php?format=" . FORMAT_XLS . + "&tplan_id={$gui->tplan_id}&spreadsheet=1"; + + $gui->mailFeedBack = new stdClass(); + $gui->mailFeedBack->msg = ''; + return $gui; +} + +/** + * + * @param stdClass $gui + * @param stdClass $args + * @param testplan $tplanMgr + */ +function createSpreadsheet($gui, &$tplanMgr) +{ + + // N sections + // Always same format + // Platform + // Build Assigned Not Run [%] Passed [%] Failed [%] Blocked [%] + // Completed [%] + + // Results by Platform + // Overall Build Status + // Results by Build + // Results by Top Level Test Suite + // Results by priority + // Results by Keyword + $lbl = initLblSpreadsheet(); + $cellRange = setCellRangeSpreadsheet(); + $style = initStyleSpreadsheet(); + + // Common + $execStatusDomain = $tplanMgr->getStatusForReports(); + $dataHeaderMetrics = array(); + $ccc = 0; + foreach ($execStatusDomain as $human) { + $dataHeaderMetrics[] = lang_get('test_status_' . $human); + $ccc ++; + $dataHeaderMetrics[] = '[%]'; + $ccc ++; + } + $dataHeaderMetrics[] = $lbl['completed_perc']; + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + + $oneLevel = array(); + + // NO PLATFORM => ID=0 + $hasPlatforms = count($gui->platformSet) >= 1 && + ! isset($gui->platformSet[0]); + if ($hasPlatforms) { + $oneLevel[] = array( + 'entity' => 'platform', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->platform + ); + } + + $oneLevel[] = array( + 'entity' => 'build', + 'dimension' => 'testcase_qty', + 'nameKey' => 'build_name', + 'tcQtyKey' => 'total_assigned', + 'source' => &$gui->statistics->overallBuildStatus + ); + + $startingRow = count($lines2write); // MAGIC + foreach ($oneLevel as $target) { + $entity = $target['entity']; + $dimension = $target['dimension']; + $dataHeader = array( + $lbl[$entity], + $lbl[$dimension] + ); + + // intermediate column qty is dynamic because it depends + // of status configuration. + foreach ($dataHeaderMetrics as $val) { + $dataHeader[] = $val; + } + + $startingRow ++; + $startingRow ++; + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + $startingRow ++; + $infoSet = $target['source']; + $nameKey = $target['nameKey']; + $tcQtyKey = $target['tcQtyKey']; + + foreach ($infoSet as $fieldSet) { + + $whatCell = 0; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$nameKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$tcQtyKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + foreach ($fieldSet['details'] as $metrics) { + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['qty']); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['percentage']); + } + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $fieldSet['percentage_completed']); + $startingRow ++; + } + } + + // The first column will be the platform + $twoLevels = array(); + + if ($hasPlatforms) { + $twoLevels[] = array( + 'entity' => 'build', + 'dimension' => 'testcase_qty', + 'nameKey' => 'build_name', + 'tcQtyKey' => 'total_assigned', + 'source' => &$gui->statistics->buildByPlatMetrics + ); + } + + $twoLevels[] = array( + 'entity' => 'testsuite', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->testsuites + ); + + $twoLevels[] = array( + 'entity' => 'priority', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->priorities + ); + + $twoLevels[] = array( + 'entity' => 'keyword', + 'dimension' => 'testcase_qty', + 'nameKey' => 'name', + 'tcQtyKey' => 'total_tc', + 'source' => &$gui->statistics->keywords + ); + + foreach ($twoLevels as $target) { + $startingRow ++; + $startingRow ++; + + $entity = $target['entity']; + $dimension = $target['dimension']; + $nameKey = $target['nameKey']; + $tcQtyKey = $target['tcQtyKey']; + + if (count($target['source']) == 0) { + continue; + } + + // Just ONE HEADER ? + $dataHeader = array( + $lbl['platform'], + $lbl[$entity], + $lbl[$dimension] + ); + if (! $hasPlatforms) { + array_shift($dataHeader); + } + + // intermediate column qty is dynamic because it depends + // of status configuration. + foreach ($dataHeaderMetrics as $val) { + $dataHeader[] = $val; + } + + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + // END ONE HEADER + $startingRow ++; + + $idr = ''; + foreach ($gui->platformSet as $platID => $platName) { + $idr = ('' == $idr || 'rowB' == $idr) ? 'rowA' : 'rowB'; + + $infoSet = isset($target['source'][$platID]) ? $target['source'][$platID] : array(); + + foreach ($infoSet as $fieldSet) { + $whatCell = 0; + + if ($hasPlatforms) { + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $platName; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + $whatCell ++; + } + + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$nameKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $field = $fieldSet[$tcQtyKey]; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $field); + + foreach ($fieldSet['details'] as $metrics) { + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['qty']); + + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $metrics['percentage']); + } + $whatCell ++; + $cellID = $cellRange[$whatCell] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, + $fieldSet['percentage_completed']); + + $cellZone = "A{$startingRow}:" . $cellRange[$whatCell] . + "$startingRow"; + + $objPHPExcel->getActiveSheet() + ->getStyle($cellZone) + ->applyFromArray($style[$idr]); + + $startingRow ++; + } + } + } + + // Just to add some final empty row + $cellID = $cellRange[0] . $startingRow; + $field = ''; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + + // Final step + $tmpfname = tempnam(config_get('temp_dir'), "TestLink_GTMP.tmp"); + $objPHPExcel->setActiveSheetIndex(0); + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + $objWriter->save($tmpfname); + + downloadXls($tmpfname, $xlsType, $gui, 'TestLink_GTMP_'); +} + +/** + * + * @param PHPExcel $oj + * @param array $style + * @param array $lbl + * @param stdClass $gui + * @return array + */ +function xlsStepOne(&$oj, $style, &$lbl, &$gui) +{ + $dummy = ''; + $lines2write = array( + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + + $cellArea .= "A{$cdx}"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; +} + +/** + * + * @return array + */ +function initLblSpreadsheet() +{ + return init_labels( + array( + 'testsuite' => null, + 'testcase_qty' => null, + 'keyword' => null, + 'platform' => null, + 'priority' => null, + 'priority_level' => null, + 'build' => null, + 'testplan' => null, + 'testproject' => null, + 'not_run' => null, + 'completed_perc' => 'trep_comp_perc', + 'generated_by_TestLink_on' => null + )); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $style = array(); + $style['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $style['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + + $style['rowA'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FFFFFFFF' + ) + ) + ); + + $style['rowB'] = array( + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'DCDCDCDC' + ) + ) + ); + + return $style; +} + +/** + * + * @return array + */ +function setCellRangeSpreadsheet() +{ + $cr = range('A', 'Z'); + $crLen = count($cr); + for ($idx = 0; $idx < $crLen; $idx ++) { + for ($jdx = 0; $jdx < $crLen; $jdx ++) { + $cr[] = $cr[$idx] . $cr[$jdx]; + } + } + return $cr; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); } diff --git a/lib/results/resultsImport.php b/lib/results/resultsImport.php index 8e2370c5fb..e132d3025b 100644 --- a/lib/results/resultsImport.php +++ b/lib/results/resultsImport.php @@ -1,742 +1,866 @@ -doUpload) { - // check the uploaded file - $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; - - if (($source != 'none') && ($source != '')) { - $gui->file_check['status_ok']=1; - if($gui->file_check['status_ok']) { - if (move_uploaded_file($source, $dest)) { - switch($args->importType) { - case 'XML': - $pcheck_fn="check_xml_execution_results"; - $pimport_fn="importExecutionResultsFromXML"; - break; - } - - if ($pcheck_fn) { - $gui->file_check=$pcheck_fn($dest); - if($gui->file_check['status_ok']) { - if ($pimport_fn) { - $resultMap=$pimport_fn($db,$dest,$args); - } - } - } - } - } - } else { - $gui->file_check=array('status_ok' => 0, 'msg' => lang_get('please_choose_file_to_import')); - $args->importType=null; - } +doUpload) { + // check the uploaded file + $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; + + if (($source != 'none') && ($source != '')) { + $gui->file_check['status_ok'] = 1; + if ($gui->file_check['status_ok'] && move_uploaded_file($source, $dest)) { + switch ($args->importType) { + case 'XML': + $pcheck_fn = "check_xml_execution_results"; + $pimport_fn = "importExecutionResultsFromXML"; + break; + } + + if ($pcheck_fn) { + $gui->file_check = $pcheck_fn($dest); + if ($gui->file_check['status_ok'] && $pimport_fn) { + $resultMap = $pimport_fn($db, $dest, $args); + } + } + } + } else { + $gui->file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('please_choose_file_to_import') + ); + $args->importType = null; + } +} + +$gui->resultMap = $resultMap; +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: + * + * args : + * + * returns: + * + */ +function importExecutionResultsFromXML(&$db, $fileName, $context) +{ + $resultMap = null; + + $xml = @simplexml_load_file_wrapper($fileName); + if ($xml !== false) { + $resultMap = importResults($db, $xml, $context); + } + return $resultMap; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function importResults(&$db, &$xml, $context) +{ + $resultMap = null; + + if ($xml->getName() == 'results') { + // check if additional data (context execution) has been provided, + // if yes overwrite GUI selection with value get from file + // + $executionContext = $context; + $contextKeys = array( + 'testproject' => array( + 'id' => 'tprojectID', + 'name' => 'tprojectName' + ), + 'testplan' => array( + 'id' => 'tplanID', + 'name' => 'tplanName' + ), + 'build' => array( + 'id' => 'buildID', + 'name' => 'buildName' + ), + 'platform' => array( + 'id' => 'platformID', + 'name' => 'platformName' + ) + ); + + foreach ($contextKeys as $xmlkey => $execElem) { + if ($joker = $xml->$xmlkey) { + // IMPORTANT NOTICE: name has precedence over id + if (isset($joker['name'])) { + $executionContext->$execElem['name'] = (string) $joker['name']; + $executionContext->$execElem['id'] = null; // get rid of id passed from GUI + continue; + } + + if (isset($joker['id'])) { + $executionContext->$execElem['id'] = (int) $joker['id']; + $executionContext->$execElem['name'] = null; + } + } + } + + $xmlTCExec = $xml->xpath("//testcase"); + $resultData = importExecutionsFromXML($xmlTCExec); + if ($resultData) { + $resultMap = saveImportedResultData($db, $resultData, + $executionContext, $context); + } + } + return $resultMap; +} + +/* + * function: saveImportedResultData + * + * args : + * + * returns: + * + * rev: + */ +function saveImportedResultData(&$db, $resultData, $context, $options) +{ + if (! $resultData) { + return; + } + + $debugMsg = ' FUNCTION: ' . __FUNCTION__; + $tables = tlObjectWithDB::getDBTables( + array( + 'executions', + 'execution_bugs' + )); + + $tcaseCfg = config_get('testcase_cfg'); + + $l10n = array( + 'import_results_tc_not_found' => '', + 'import_results_invalid_result' => '', + 'tproject_id_not_found' => '', + 'import_results_ok' => '', + 'invalid_cf' => '', + 'import_results_skipped' => '' + ); + + foreach ($l10n as $key => $value) { + $l10n[$key] = lang_get($key); + } + + $resultsCfg = config_get('results'); + foreach ($resultsCfg['status_label'] as $ks => $lbl) { + $key = $resultsCfg['status_code'][$ks]; + $l10n[$key] = lang_get($lbl); + } + + // Get Column definitions to get size dinamically instead of create constants + $columnDef = array(); + $adodbObj = $db->get_dbmgr_object(); + $columnDef['execution_bugs'] = $adodbObj->MetaColumns( + $tables['execution_bugs']); + $keySet = array_keys($columnDef['execution_bugs']); + foreach ($keySet as $keyName) { + if (($keylow = strtolower($keyName)) != $keyName) { + $columnDef['execution_bugs'][$keylow] = $columnDef['execution_bugs'][$keyName]; + unset($columnDef['execution_bugs'][$keyName]); + } + } + $user = new tlUser($context->userID); + $user->readFromDB($db); + + $tcaseMgr = new testcase($db); + + $resultMap = array(); + $tplan_mgr = null; + + $tc_qty = count($resultData); + if ($tc_qty) { + $tplan_mgr = new testplan($db); + $tproject_mgr = new testproject($db); + $build_mgr = new build_mgr($db); + } + + // Need to do checks on common settings + // + // test project exists + // + // test plan id: + // belongs to target test project + // is active + // build id: + // belongs to target test plan + // is open + // + // platform id: + // is linked to target test plan + // + // execution type if not present -> set to MANUAL + // if presente is valid i.e. inside the TL domain + $checks = array(); + $checks['msg'] = null; + $dummy = null; + + if (! is_null($context->tprojectID) && intval($context->tprojectID) > 0) { + $dummy = array( + $tproject_mgr->get_by_id($context->tprojectID, + array( + 'output' => 'existsByID' + )) + ); + } elseif (! is_null($context->tprojectName)) { + $dummy = $tproject_mgr->get_by_name($context->tprojectName, null, + array( + 'output' => 'existsByName' + )); + } + + $checks['status_ok'] = ! is_null($dummy); + if (! $checks['status_ok']) { + $checks['msg'][] = sprintf($l10n['tproject_id_not_found'], + $context->tprojectID); + } + + if (! $checks['status_ok']) { + foreach ($checks['msg'] as $warning) { + $resultMap[] = array( + $warning + ); + } + } + + if ($doIt = $checks['status_ok']) { + $context->tprojectID = $dummy[0]['id']; + } + + $dummy = null; + if (! is_null($context->tplanID) && intval($context->tplanID) > 0) { + $dummy = $tplan_mgr->get_by_id($context->tplanID, + array( + 'output' => 'minimun' + )); + if (! is_null($dummy)) { + $dummy['id'] = $context->tplanID; + } + } elseif (! is_null($context->tplanName)) { + $dummy = $tplan_mgr->get_by_name($context->tplanName, + $context->tprojectID, array( + 'output' => 'minimun' + )); + if (! is_null($dummy)) { + $dummy = $dummy[0]; + } + } + + if (! is_null($dummy)) { + $context->tplanID = $dummy['id']; + } + + if ((intval($context->tprojectID) <= 0) && intval($context->tplanID) > 0) { + $dummy = $tplan_mgr->tree_manager->get_node_hierarchy_info( + $context->tplanID); + $context->tprojectID = $dummy['parent_id']; + } + + $dummy = null; + $tplan_mgr->platform_mgr->setTestProjectID($context->tprojectID); + if (! is_null($context->platformID) && intval($context->platformID) > 0) { + $dummy = array( + $tplan_mgr->platform_mgr->getByID($context->platformID) + ); + } elseif (property_exists($context, 'platformName') && + ! is_null($context->platformName)) { + if (! is_null( + $xx = $tplan_mgr->platform_mgr->getID($context->platformName))) { + $dummy = array( + 0 => array( + 'id' => $xx + ) + ); + } + } + if (! is_null($dummy)) { + $context->platformID = $dummy[0]['id']; + } + + $optGB = array( + 'tplan_id' => $context->tplanID, + 'output' => 'minimun' + ); + $dummy = null; + if (! is_null($context->buildID) && intval($context->buildID) > 0) { + $dummy = array( + $build_mgr->get_by_id($context->buildID, $optGB) + ); + } elseif (! is_null($context->buildName)) { + $dummy = $build_mgr->get_by_name($context->buildName, $optGB); + } + + if (! is_null($dummy)) { + $context->buildID = $dummy[0]['id']; + } + + for ($idx = 0; $doIt && $idx < $tc_qty; $idx ++) { + + $tester_id = 0; + $tester_name = ''; + $using_external_id = false; + $message = null; + $status_ok = true; + $tcase_exec = $resultData[$idx]; + + // New attribute "execution type" makes old XML import files incompatible + // Important NOTICE: + // tcase_exec is passed BY REFERENCE to allow check_exec_values()change execution type if needed + $checks = checkExecValues($db, $tcaseMgr, $user_mgr, $tcaseCfg, + $tcase_exec, $columnDef['execution_bugs']); + $status_ok = $checks['status_ok']; + if ($status_ok) { + $tcase_id = $checks['tcase_id']; + $tcase_external_id = trim($tcase_exec['tcase_external_id']); + $tester_id = $checks['tester_id']; + + // external_id has precedence over internal id + $using_external_id = ($tcase_external_id != ""); + } else { + foreach ($checks['msg'] as $warning) { + $resultMap[] = array( + $warning + ); + } + } + + if ($status_ok) { + $tcase_identity = $using_external_id ? $tcase_external_id : $tcase_id; + $result_code = strtolower($tcase_exec['result']); + $result_is_acceptable = isset( + $resultsCfg['code_status'][$result_code]) ? true : false; + $notes = $tcase_exec['notes']; + $message = null; + + $info_on_case = $tplan_mgr->getLinkInfo($context->tplanID, $tcase_id, + $context->platformID); + if (is_null($info_on_case)) { + $message = sprintf($l10n['import_results_tc_not_found'], + $tcase_identity); + } elseif (! $result_is_acceptable) { + $message = sprintf($l10n['import_results_invalid_result'], + $tcase_identity, $tcase_exec['result']); + } else { + $info_on_case = current($info_on_case); + $tcversion_id = $info_on_case['tcversion_id']; + $version = $info_on_case['version']; + $notes = $db->prepare_string(trim($notes)); + + // N.B.: db_now() returns an string ready to be used in an SQL insert + // example '2008-09-04', while $tcase_exec["timestamp"] => 2008-09-04 + $execution_ts = ($tcase_exec['timestamp'] != '') ? "'" . + $tcase_exec["timestamp"] . "'" : $db->db_now(); + + if ($tester_id != 0) { + $tester_name = $tcase_exec['tester']; + } else { + $tester_name = $user->login; + $tester_id = $context->userID; + } + + $addExecDuration = (strlen($tcase_exec['execution_duration']) > 0 && + is_numeric($tcase_exec['execution_duration'])); + + $lexid = 0; + if ($options->copyIssues) { + $lexid = $tcaseMgr->getSystemWideLastestExecutionID( + $tcversion_id); + } + + $idCard = array( + 'id' => $tcase_id, + 'version_id' => $tcversion_id + ); + $exco = array( + 'tplan_id' => $context->tplanID, + 'platform_id' => $context->platformID, + 'build_id' => $context->buildID + ); + $lexInfo = $tcaseMgr->getLatestExecSingleContext($idCard, $exco, + array( + 'output' => 'timestamp' + )); + $doInsert = true; + if (! is_null($lexInfo)) { + $doInsert = ($lexInfo[$tcase_id][0]['execution_ts'] != + trim($execution_ts, "'")); + $msgTxt = $l10n['import_results_skipped']; + } + + if ($doInsert) { + $sql = " /* $debugMsg */ " . + " INSERT INTO {$tables['executions']} (build_id,tester_id,status,testplan_id," . + " tcversion_id,execution_ts,notes,tcversion_number,platform_id,execution_type" . + ($addExecDuration ? ',execution_duration' : '') . ")" . + " VALUES ({$context->buildID}, {$tester_id},'{$result_code}',{$context->tplanID}, " . + " {$tcversion_id},{$execution_ts},'{$notes}', {$version}, " . + " {$context->platformID}, {$tcase_exec['execution_type']}" . + ($addExecDuration ? ",{$tcase_exec['execution_duration']}" : '') . + ")"; + + $db->exec_query($sql); + $execution_id = $db->insert_id($tables['executions']); + + if ($lexid > 0 && $options->copyIssues) { + copyIssues($db, $lexid, $execution_id); + } + + if (isset($tcase_exec['steps']) && + ! is_null($tcase_exec['steps']) && $execution_id > 0) { + $stepSet = $tcaseMgr->getStepsSimple($tcversion_id, 0, + array( + 'fields2get' => 'TCSTEPS.step_number,TCSTEPS.id', + 'accessKey' => 'step_number' + )); + $sc = count($tcase_exec['steps']); + + for ($sx = 0; $sx < $sc; $sx ++) { + $snum = $tcase_exec['steps'][$sx]['step_number']; + + if (isset($stepSet[$snum])) { + $tcstep_id = $stepSet[$snum]['id']; + $target = DB_TABLE_PREFIX . 'execution_tcsteps'; + + $doIt = (! is_null( + $tcase_exec['steps'][$sx]['result']) && + trim($tcase_exec['steps'][$sx]['result']) != + '') || + $tcase_exec['steps'][$sx]['result'] != + $resultsCfg['status_code']['not_run']; + + if ($doIt) { + $sql = " INSERT INTO {$target} (execution_id,tcstep_id,notes"; + $values = " VALUES ( {$execution_id}, {$tcstep_id} , " . + "'" . + $db->prepare_string( + $tcase_exec['steps'][$sx]['notes']) . + "'"; + + $status = strtolower( + trim( + $tcase_exec['steps'][$sx]['result'])); + $status = $status[0]; + $sql .= ",status"; + $values .= ",'" . + $db->prepare_string( + $tcase_exec['steps'][$sx]['result']) . + "'"; + + $sql .= ") " . $values . ")"; + $db->exec_query($sql); + + $db->insert_id($target); + } + } + } + } + + if (isset($tcase_exec['bug_id']) && + ! is_null($tcase_exec['bug_id']) && + is_array($tcase_exec['bug_id'])) { + + foreach ($tcase_exec['bug_id'] as $bug_id) { + $bug_id = trim($bug_id); + $sql = " /* $debugMsg */ " . + " SELECT execution_id AS check_qty FROM {$tables['execution_bugs']} " . + " WHERE bug_id = '{$bug_id}' AND execution_id={$execution_id} "; + $rs = $db->get_recordset($sql); + if (is_null($rs)) { + $sql = " /* $debugMsg */ " . + " INSERT INTO {$tables['execution_bugs']} (bug_id,execution_id)" . + " VALUES ('" . $db->prepare_string($bug_id) . + "', {$execution_id} )"; + $db->exec_query($sql); + } + } + } + + if (isset($tcase_exec['custom_fields']) && + ! is_null($tcase_exec['custom_fields']) && + is_array($tcase_exec['custom_fields'])) { + + // Get linked custom fields to this test project, for test case on execution + // $context->tprojectID + $cfieldMgr = new cfield_mgr($db); + $cfSetByName = $cfieldMgr->get_linked_cfields_at_execution( + $context->tprojectID, 1, 'testcase', null, null, + null, 'name'); + + foreach ($tcase_exec['custom_fields'] as $cf) { + $ak = null; + if (isset($cfSetByName[$cf['name']])) { + // write to db blind + $ak[$cfSetByName[$cf['name']]['id']]['cf_value'] = $cf['value']; + } else { + $message = sprintf($l10n['invalid_cf'], + $tcase_identity, $cf['name']); + } + + if (! is_null($ak)) { + $cfieldMgr->execution_values_to_db($ak, + $tcversion_id, $execution_id, + $context->tplanID, null, 'plain'); + } + } + } + + if (! is_null($message)) { + $resultMap[] = array( + $message + ); + } + $msgTxt = $l10n['import_results_ok']; + } + $message = sprintf($msgTxt, $tcase_identity, $version, + $tester_name, $l10n[$result_code], $execution_ts); + } + } + + if (! is_null($message)) { + $resultMap[] = array( + $message + ); + } + } + return $resultMap; +} + +/* + * function: importExecutionsFromXML + * + * args : + * + * returns: + * + */ +function importExecutionsFromXML($xmlTCExecSet) +{ + $execInfoSet = null; + if ($xmlTCExecSet) { + $jdx = 0; + $exec_qty = count($xmlTCExecSet); + for ($idx = 0; $idx < $exec_qty; $idx ++) { + $xmlTCExec = $xmlTCExecSet[$idx]; + $execInfo = importExecutionFromXML($xmlTCExec); + if ($execInfo) { + $execInfoSet[$jdx ++] = $execInfo; + } + } + } + + return $execInfoSet; +} + +/* + * function: importExecutionFromXML() + * + * args : + * + * returns: + * + */ +function importExecutionFromXML(&$xmlTCExec) +{ + if (! $xmlTCExec) { + return null; + } + + $execInfo = array(); + $execInfo['tcase_id'] = isset($xmlTCExec["id"]) ? (int) $xmlTCExec["id"] : 0; + $execInfo['tcase_external_id'] = (string) $xmlTCExec["external_id"]; + + // Developer Note - 20100328 - franciscom: + // seems that no PHP error is generated when trying to access an undefined + // property. Do not know if will not be better anyway to use property_exists() + $execInfo['tcase_name'] = (string) $xmlTCExec->name; + $execInfo['result'] = (string) trim($xmlTCExec->result); + $execInfo['notes'] = (string) trim($xmlTCExec->notes); + $execInfo['timestamp'] = (string) trim($xmlTCExec->timestamp); + $execInfo['tester'] = (string) trim($xmlTCExec->tester); + $execInfo['execution_type'] = intval((int) trim($xmlTCExec->execution_type)); + $execInfo['execution_duration'] = trim($xmlTCExec->execution_duration); + + if (! empty($xmlTCExec->bug_id)) { + foreach ($xmlTCExec->bug_id as $bug) { + $execInfo['bug_id'][] = (string) $bug; + } + } + + $execInfo['steps'] = null; + if (property_exists($xmlTCExec, 'steps') && + property_exists($xmlTCExec->steps, 'step')) { + $itemStructure['elements'] = array( + 'integer' => array( + "step_number" => 'intval' + ), + 'string' => array( + "result" => 'trim', + "notes" => 'trim' + ) + ); + $execInfo['steps'] = getItemsFromSimpleXMLObj($xmlTCExec->steps->step, + $itemStructure); + } + + $execInfo['custom_fields'] = null; + if (property_exists($xmlTCExec, 'custom_fields') && + property_exists($xmlTCExec->custom_fields, 'custom_field')) { + $itemStructure['elements'] = array( + 'string' => array( + "name" => 'trim', + "value" => 'trim' + ) + ); + $execInfo['custom_fields'] = getItemsFromSimpleXMLObj( + $xmlTCExec->custom_fields->custom_field, $itemStructure); + } + + return $execInfo; +} + +/* + * function: + * + * Check if at least the file starts seems OK + * + */ +function checkXMLExecutionResults($fileName) +{ + $file_check = array( + 'status_ok' => 0, + 'msg' => 'xml_ko' + ); + $xml = @simplexml_load_file_wrapper($fileName); + if ($xml !== false) { + $file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $elementName = $xml->getName(); + if ($elementName != 'results') { + $file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('wrong_results_import_format') + ); + } + } + return $file_check; +} + +/* + * function: init_args(&$dbHandler) + * + * args : + * + * returns: + * + */ +function initArgs(&$dbHandler) +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; + $args->copyIssues = isset($_REQUEST['copyIssues']) ? 1 : 0; + + // Need to use REQUEST because sometimes data arrives on GET and other on POST (has hidden fields) + $args->buildID = isset($_REQUEST['buildID']) ? intval($_REQUEST['buildID']) : null; + $args->platformID = isset($_REQUEST['platformID']) ? intval( + $_REQUEST['platformID']) : null; + $args->tplanID = isset($_REQUEST['tplanID']) ? intval($_REQUEST['tplanID']) : null; + $args->tplanID = ! is_null($args->tplanID) ? $args->tplanID : intval( + $_SESSION['testplanID']); + + $args->tprojectID = isset($_REQUEST['tprojectID']) ? intval( + $_REQUEST['tprojectID']) : null; + + if (is_null($args->tprojectID)) { + $args->tprojectID = intval($_SESSION['testprojectID']); + $args->testprojectName = $_SESSION['testprojectName']; + } else { + $tproject_mgr = new testproject($dbHandler); + $dummy = $tproject_mgr->get_by_id($args->tprojectID); + $args->testprojectName = $dummy['name']; + } + + $args->doUpload = isset($_REQUEST['UploadFile']) ? 1 : 0; + $args->userID = intval($_SESSION['userID']); + + return $args; +} + +/* + * function: check_exec_values() + * + * args : + * + * returns: map + * keys: + * status_ok -> value=true / false + * tcase_id: test case id if controls OK + * tester_id: tester_id if controls OK + * msg -> array with localized messages + * + * @internal revisions + */ +function checkExecValues(&$db, &$tcaseMgr, &$user_mgr, $tcaseCfg, &$execValues, + &$columnDef) +{ + $tables = tlObjectWithDB::getDBTables(array( + 'users', + 'execution_bugs' + )); + $checks = array( + 'status_ok' => false, + 'tcase_id' => 0, + 'tester_id' => 0, + 'msg' => array() + ); + $tcase_id = $execValues['tcase_id']; + $tcase_external_id = trim($execValues['tcase_external_id']); + $using_external_id = ($tcase_external_id != ""); // external_id has precedence over internal id + + if ($using_external_id) { + // need to get internal id + $checks['tcase_id'] = $tcaseMgr->getInternalID($tcase_external_id); + $checks['status_ok'] = intval($checks['tcase_id']) > 0 ? true : false; + if (! $checks['status_ok']) { + $checks['msg'][] = sprintf( + lang_get('tcase_external_id_do_not_exists'), $tcase_external_id); + } + } else { + // before using internal id, I want to check it's a number + $checks['tcase_id'] = $tcase_id; + $checks['status_ok'] = intval($checks['tcase_id']) > 0 ? true : false; + if (! $checks['status_ok']) { + $checks['msg'][] = sprintf(lang_get('tcase_id_is_not_number'), + $tcase_id); + } + } + + if ($checks['status_ok']) { + // useful for user feedback + $identity = $using_external_id ? $tcase_external_id : $checks['tcase_id']; + } + + if ($checks['status_ok'] && $execValues['timestamp'] != '') { + $checks['status_ok'] = isValidISODateTime($execValues['timestamp']); + if (! $checks['status_ok']) { + $checks['msg'][] = sprintf(lang_get('invalid_execution_timestamp'), + $identity, $execValues['timestamp']); + } + } + + if ($checks['status_ok'] && $execValues['tester'] != '') { + $sql = "SELECT id,login FROM {$tables['users']} WHERE login ='" . + $db->prepare_string($execValues['tester']) . "'"; + $userInfo = $db->get_recordset($sql); + + if (! is_null($userInfo) && isset($userInfo[0]['id'])) { + $checks['tester_id'] = $userInfo[0]['id']; + } else { + $checks['status_ok'] = false; + $checks['msg'][] = sprintf(lang_get('invalid_tester'), $identity, + $execValues['tester']); + } + } + + $execValues['bug_id'] = isset($execValues['bug_id']) ? $execValues['bug_id'] : null; + if ($checks['status_ok'] && ! is_null($execValues['bug_id']) && + is_array($execValues['bug_id'])) { + foreach ($execValues['bug_id'] as $bug_id) { + if (($field_len = strlen(trim($bug_id))) > + $columnDef['bug_id']->max_length) { + $checks['msg'][] = sprintf(lang_get('bug_id_invalid_len'), + $field_len, $columnDef['bug_id']->max_length); + $checks['status_ok'] = false; + break; + } + } + } + + if ($checks['status_ok'] && isset($execValues['execution_type'])) { + $execValues['execution_type'] = intval($execValues['execution_type']); + $execDomain = $tcaseMgr->get_execution_types(); + if ($execValues['execution_type'] == 0) { + $execValues['execution_type'] = TESTCASE_EXECUTION_TYPE_MANUAL; + // right now this is useless, but may be in future can be used, then I choose to leave it. + $checks['msg'][] = sprintf(lang_get('missing_exec_type'), + $execValues['execution_type'], + $execDomain[$execValues['execution_type']]); + } else { + $checks['status_ok'] = isset( + $execDomain[$execValues['execution_type']]); + if (! $checks['status_ok']) { + $checks['msg'][] = sprintf(lang_get('invalid_exec_type'), + $execValues['execution_type']); + } + } + } + + return $checks; +} + +/** + */ +function initializeGui(&$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->import_title = lang_get('title_results_import_to'); + $guiObj->buildID = $argsObj->buildID; + $guiObj->platformID = $argsObj->platformID; + $guiObj->tplanID = $argsObj->tplanID; + + $guiObj->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $guiObj->importTypes = array( + "XML" => "XML" + ); + $guiObj->importLimit = config_get('import_file_max_size_bytes'); + $guiObj->doImport = ($argsObj->importType != ""); + $guiObj->testprojectName = $argsObj->testprojectName; + $guiObj->copyIssues = $argsObj->copyIssues; + return $guiObj; } - -$gui->resultMap=$resultMap; -$smarty=new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/* - function: - - args : - - returns: - -*/ -function importExecutionResultsFromXML(&$db,$fileName,$context) { - $resultMap=null; - - $xml = @simplexml_load_file_wrapper($fileName); - if($xml !== FALSE) { - $resultMap = importResults($db,$xml,$context); - } - return $resultMap; -} - - -/* - function: - - args : - - returns: - -*/ -function importResults(&$db,&$xml,$context) { - $resultMap = null; - - if($xml->getName() == 'results') { - // check if additional data (context execution) has been provided, - // if yes overwrite GUI selection with value get from file - // - $executionContext = $context; - $contextKeys = - array('testproject' => array('id' => 'tprojectID', 'name' => 'tprojectName'), - 'testplan' => array('id' => 'tplanID', 'name' => 'tplanName'), - 'build' => array('id' => 'buildID', 'name' => 'buildName'), - 'platform' => array('id' => 'platformID', 'name' => 'platformName')); - - - foreach( $contextKeys as $xmlkey => $execElem) { - if( ($joker = $xml->$xmlkey) ) { - // IMPORTANT NOTICE: name has precedence over id - if( isset($joker['name']) ) { - $executionContext->$execElem['name'] = (string) $joker['name']; - $executionContext->$execElem['id'] = null; // get rid of id passed from GUI - continue; - } - - if( isset($joker['id']) ) { - $executionContext->$execElem['id'] = (int) $joker['id']; - $executionContext->$execElem['name'] = null; - } - } - } - - $xmlTCExec = $xml->xpath("//testcase"); - $resultData = importExecutionsFromXML($xmlTCExec); - if ($resultData) { - $resultMap = saveImportedResultData($db,$resultData,$executionContext,$context); - } - } - return $resultMap; -} - - - -/* - function: saveImportedResultData - - args : - - returns: - - rev: -*/ -function saveImportedResultData(&$db,$resultData,$context,$options) { - - if (!$resultData) { - return; - } - - $debugMsg = ' FUNCTION: ' . __FUNCTION__; - $tables = tlObjectWithDB::getDBTables(array('executions','execution_bugs')); - - $tcaseCfg = config_get('testcase_cfg'); - - // -------------------------------------------------------------------------------- - $l10n = - array('import_results_tc_not_found' => '' ,'import_results_invalid_result' => '', - 'tproject_id_not_found' => '', 'import_results_ok' => '', - 'invalid_cf' => '', 'import_results_skipped' => ''); - - foreach($l10n as $key => $value) { - $l10n[$key] = lang_get($key); - } - - $resultsCfg=config_get('results'); - foreach($resultsCfg['status_label'] as $ks => $lbl) { - $key = $resultsCfg['status_code'][$ks]; - $l10n[$key] = lang_get($lbl); - } - // --------------------------------------------------------------------------------- - - // Get Column definitions to get size dinamically instead of create constants - $columnDef = array(); - $adodbObj = $db->get_dbmgr_object(); - $columnDef['execution_bugs'] = $adodbObj->MetaColumns($tables['execution_bugs']); - $keySet = array_keys($columnDef['execution_bugs']); - foreach($keySet as $keyName) { - if( ($keylow=strtolower($keyName)) != $keyName ) { - $columnDef['execution_bugs'][$keylow] = $columnDef['execution_bugs'][$keyName]; - unset($columnDef['execution_bugs'][$keyName]); - } - } - $user = new tlUser($context->userID); - $user->readFromDB($db); - - $tcase_mgr = new testcase($db); - - $resultMap = array(); - $tplan_mgr = null; - - $tc_qty = sizeof($resultData); - if($tc_qty) { - $tplan_mgr=new testplan($db); - $tproject_mgr=new testproject($db); - $build_mgr=new build_mgr($db); - } - - // Need to do checks on common settings - // - // test project exists - // - // test plan id: - // belongs to target test project - // is active - // build id: - // belongs to target test plan - // is open - // - // platform id: - // is linked to target test plan - // - // execution type if not present -> set to MANUAL - // if presente is valid i.e. inside the TL domain - // - $checks = array(); - $checks['status_ok'] = true; - $checks['msg'] = null; - $dummy = null; - - if( !is_null($context->tprojectID) && intval($context->tprojectID) > 0) { - $dummy = array($tproject_mgr->get_by_id($context->tprojectID,array('output' => 'existsByID'))); - } else if( !is_null($context->tprojectName) ) { - $dummy = $tproject_mgr->get_by_name($context->tprojectName,null,array('output' => 'existsByName')); - } - - $checks['status_ok'] = !is_null($dummy); - if( !$checks['status_ok'] ) { - $checks['msg'][] = sprintf($l10n['tproject_id_not_found'],$context->tprojectID); - } - - if( !$checks['status_ok'] ) { - foreach($checks['msg'] as $warning ) { - $resultMap[]=array($warning); - } - } - - if( ($doIt = $checks['status_ok']) ) { - $context->tprojectID = $dummy[0]['id']; - } - - // -------------------------------------------------------------------- - $dummy = null; - if( !is_null($context->tplanID) && intval($context->tplanID) > 0 ) { - $dummy = $tplan_mgr->get_by_id($context->tplanID,array('output' => 'minimun')); - if( !is_null($dummy) ) { - $dummy['id'] = $context->tplanID; - } - } else if( !is_null($context->tplanName) ) { - $dummy = $tplan_mgr->get_by_name($context->tplanName,$context->tprojectID,array('output' => 'minimun')); - if( !is_null($dummy) ) { - $dummy = $dummy[0]; - } - } - - if( !is_null($dummy) ) { - $context->tplanID = $dummy['id']; - } - - if( (intval($context->tprojectID) <= 0) && intval($context->tplanID) > 0) { - $dummy = $tplan_mgr->tree_manager->get_node_hierarchy_info($context->tplanID); - $context->tprojectID = $dummy['parent_id']; - } - // -------------------------------------------------------------------- - - // -------------------------------------------------------------------- - $dummy = null; - $tplan_mgr->platform_mgr->setTestProjectID($context->tprojectID); - if( !is_null($context->platformID) && intval($context->platformID) > 0 ) { - $dummy = array($tplan_mgr->platform_mgr->getByID($context->platformID)); - } else if( property_exists($context,'platformName') && !is_null($context->platformName) ) { - if( !is_null($xx = $tplan_mgr->platform_mgr->getID($context->platformName) ) ) { - $dummy = array(0 => array('id' => $xx)); - } - } - if( !is_null($dummy) ) { - $context->platformID = $dummy[0]['id']; - } - // -------------------------------------------------------------------- - - // -------------------------------------------------------------------- - $optGB = array('tplan_id' => $context->tplanID, 'output' => 'minimun'); - $dummy = null; - if( !is_null($context->buildID) && intval($context->buildID) > 0 ) { - $dummy = array($build_mgr->get_by_id($context->buildID,$optGB)); - } else if( !is_null($context->buildName) ) { - $dummy = $build_mgr->get_by_name($context->buildName,$optGB); - } - - if( !is_null($dummy) ) { - $context->buildID = $dummy[0]['id']; - } - // -------------------------------------------------------------------- - - // -------------------------------------------------------------------- - for($idx=0; $doIt && $idx < $tc_qty;$idx++) { - - $tester_id = 0; - $tester_name = ''; - $using_external_id = false; - $message = null; - $status_ok = true; - $tcase_exec = $resultData[$idx]; - - // New attribute "execution type" makes old XML import files incompatible - // Important NOTICE: - // tcase_exec is passed BY REFERENCE to allow check_exec_values()change execution type if needed - // - $checks = check_exec_values($db,$tcase_mgr,$user_mgr,$tcaseCfg,$tcase_exec,$columnDef['execution_bugs']); - $status_ok = $checks['status_ok']; - if($status_ok) { - $tcase_id = $checks['tcase_id']; - $tcase_external_id = trim($tcase_exec['tcase_external_id']); - $tester_id = $checks['tester_id']; - - // external_id has precedence over internal id - $using_external_id = ($tcase_external_id != ""); - } else { - foreach($checks['msg'] as $warning ) { - $resultMap[]=array($warning); - } - } - - if( $status_ok ) { - $tcase_identity = $using_external_id ? $tcase_external_id : $tcase_id; - $result_code = strtolower($tcase_exec['result']); - $result_is_acceptable = isset($resultsCfg['code_status'][$result_code]) ? true : false; - $notes = $tcase_exec['notes']; - $message = null; - - $info_on_case = $tplan_mgr->getLinkInfo($context->tplanID,$tcase_id,$context->platformID); - if(is_null($info_on_case)) { - $message=sprintf($l10n['import_results_tc_not_found'],$tcase_identity); - } else if (!$result_is_acceptable) { - $message=sprintf($l10n['import_results_invalid_result'],$tcase_identity,$tcase_exec['result']); - } else { - $info_on_case = current($info_on_case); - $tcversion_id = $info_on_case['tcversion_id']; - $version = $info_on_case['version']; - $notes = $db->prepare_string(trim($notes)); - - // N.B.: db_now() returns an string ready to be used in an SQL insert - // example '2008-09-04', while $tcase_exec["timestamp"] => 2008-09-04 - // - $execution_ts=($tcase_exec['timestamp'] != '') ? "'" . $tcase_exec["timestamp"] . "'": $db->db_now(); - - if($tester_id != 0) { - $tester_name=$tcase_exec['tester']; - } else { - $tester_name=$user->login; - $tester_id=$context->userID; - } - - $addExecDuration = (strlen($tcase_exec['execution_duration']) > 0 && is_numeric($tcase_exec['execution_duration'])); - - $lexid = 0; - if($options->copyIssues) { - $lexid = $tcase_mgr->getSystemWideLastestExecutionID($tcversion_id); - } - - $idCard = array('id' => $tcase_id,'version_id' => $tcversion_id); - $exco = array('tplan_id' => $context->tplanID, - 'platform_id' => $context->platformID, - 'build_id' => $context->buildID); - $lexInfo = $tcase_mgr->getLatestExecSingleContext($idCard,$exco,array('output' => 'timestamp')); - $doInsert = true; - if(!is_null($lexInfo)) { - $tts = $lexInfo[$tcase_id][0]['execution_ts']; - $doInsert = ($lexInfo[$tcase_id][0]['execution_ts'] != trim($execution_ts,"'")); - $msgTxt = $l10n['import_results_skipped']; - } - - if( $doInsert ) { - $sql = " /* $debugMsg */ " . - " INSERT INTO {$tables['executions']} (build_id,tester_id,status,testplan_id," . - " tcversion_id,execution_ts,notes,tcversion_number,platform_id,execution_type" . - ($addExecDuration ? ',execution_duration':'') . ")" . - " VALUES ({$context->buildID}, {$tester_id},'{$result_code}',{$context->tplanID}, ". - " {$tcversion_id},{$execution_ts},'{$notes}', {$version}, " . - " {$context->platformID}, {$tcase_exec['execution_type']}" . - ($addExecDuration ? ",{$tcase_exec['execution_duration']}" : '') . ")"; - - $db->exec_query($sql); - $execution_id = $db->insert_id($tables['executions']); - - if($lexid > 0 && $options->copyIssues) { - copyIssues($db,$lexid,$execution_id); - } - - if(isset($tcase_exec['steps']) && !is_null($tcase_exec['steps']) && - $execution_id > 0 ) { - $stepSet = $tcase_mgr->getStepsSimple($tcversion_id,0, - array('fields2get' => 'TCSTEPS.step_number,TCSTEPS.id', - 'accessKey' => 'step_number')); - $sc = count($tcase_exec['steps']); - - for($sx=0; $sx < $sc; $sx++) { - $snum = $tcase_exec['steps'][$sx]['step_number']; - - if(isset($stepSet[$snum])) { - $tcstep_id = $stepSet[$snum]['id']; - $target = DB_TABLE_PREFIX . 'execution_tcsteps'; - - $doIt = (!is_null($tcase_exec['steps'][$sx]['result']) && - trim($tcase_exec['steps'][$sx]['result']) != '') || - $tcase_exec['steps'][$sx]['result'] != $resultsCfg['status_code']['not_run']; - - if( $doIt ) { - $sql = " INSERT INTO {$target} (execution_id,tcstep_id,notes"; - $values = " VALUES ( {$execution_id}, {$tcstep_id} , " . - "'" . $db->prepare_string($tcase_exec['steps'][$sx]['notes']) . "'"; - - $status = strtolower(trim($tcase_exec['steps'][$sx]['result'])); - $status = $status[0]; - $sql .= ",status"; - $values .= ",'" . $db->prepare_string($tcase_exec['steps'][$sx]['result']) . "'"; - - $sql .= ") " . $values . ")"; - $db->exec_query($sql); - - $execution_tcsteps_id = $db->insert_id($target); - } - } - } - } - - if( isset($tcase_exec['bug_id']) && !is_null($tcase_exec['bug_id']) && is_array($tcase_exec['bug_id']) ) { - - foreach($tcase_exec['bug_id'] as $bug_id) { - $bug_id = trim($bug_id); - $sql = " /* $debugMsg */ " . - " SELECT execution_id AS check_qty FROM {$tables['execution_bugs']} " . - " WHERE bug_id = '{$bug_id}' AND execution_id={$execution_id} "; - $rs = $db->get_recordset($sql); - if( is_null($rs) ) { - $sql = " /* $debugMsg */ " . - " INSERT INTO {$tables['execution_bugs']} (bug_id,execution_id)" . - " VALUES ('" . $db->prepare_string($bug_id) . "', {$execution_id} )"; - $db->exec_query($sql); - } - } - } - - if( isset($tcase_exec['custom_fields']) && !is_null($tcase_exec['custom_fields']) && is_array($tcase_exec['custom_fields']) ) { - - // Get linked custom fields to this test project, for test case on execution - // $context->tprojectID - $cfieldMgr = new cfield_mgr($db); - $cfSetByName = $cfieldMgr->get_linked_cfields_at_execution($context->tprojectID,1,'testcase',null,null,null,'name'); - - foreach($tcase_exec['custom_fields'] as $cf) { - $ak = null; - if( isset($cfSetByName[$cf['name']]) ) { - // write to db blind - $ak[$cfSetByName[$cf['name']]['id']]['cf_value'] = $cf['value']; - } else { - $message=sprintf($l10n['invalid_cf'],$tcase_identity,$cf['name']); - } - - if(!is_null($ak)) { - $cfieldMgr->execution_values_to_db($ak,$tcversion_id,$execution_id,$context->tplanID,null,'plain'); - } - } - } - - if( !is_null($message) ) { - $resultMap[]=array($message); - } - $msgTxt = $l10n['import_results_ok']; - - } - $message = sprintf($msgTxt,$tcase_identity,$version,$tester_name, - $l10n[$result_code],$execution_ts); - } - } - - if( !is_null($message) ) { - $resultMap[]=array($message); - } - } - return $resultMap; -} - -/* - function: importExecutionsFromXML - - args : - - returns: - -*/ -function importExecutionsFromXML($xmlTCExecSet) { - $execInfoSet=null; - if($xmlTCExecSet) { - $jdx=0; - $exec_qty=sizeof($xmlTCExecSet); - for($idx=0; $idx < $exec_qty ; $idx++) { - $xmlTCExec=$xmlTCExecSet[$idx]; - $execInfo = importExecutionFromXML($xmlTCExec); - if ($execInfo) { - $execInfoSet[$jdx++]=$execInfo; - } - } - } - - return $execInfoSet; -} - -/* - function: importExecutionFromXML() - - args : - - returns: - -*/ -function importExecutionFromXML(&$xmlTCExec) { - if (!$xmlTCExec) { - return null; - } - - $execInfo=array();; - $execInfo['tcase_id'] = isset($xmlTCExec["id"]) ? (int)$xmlTCExec["id"] : 0; - $execInfo['tcase_external_id'] = (string) $xmlTCExec["external_id"]; - - // Developer Note - 20100328 - franciscom: - // seems that no PHP error is generated when trying to access an undefined - // property. Do not know if will not be better anyway to use property_exists() - // - $execInfo['tcase_name'] = (string) $xmlTCExec->name; - $execInfo['result'] = (string) trim($xmlTCExec->result); - $execInfo['notes'] = (string) trim($xmlTCExec->notes); - $execInfo['timestamp'] = (string) trim($xmlTCExec->timestamp); - $execInfo['tester'] = (string) trim($xmlTCExec->tester); - $execInfo['execution_type'] = intval((int) trim($xmlTCExec->execution_type)); - $execInfo['execution_duration'] = trim($xmlTCExec->execution_duration); - - $bugQty = count($xmlTCExec->bug_id); - if( ($bugQty = count($xmlTCExec->bug_id)) > 0 ) { - foreach($xmlTCExec->bug_id as $bug) { - $execInfo['bug_id'][] = (string) $bug; - } - } - - $execInfo['steps'] = null; - if(property_exists($xmlTCExec, 'steps') && - property_exists($xmlTCExec->steps, 'step')){ - $itemStructure['elements'] = array('integer' => array("step_number" => 'intval'), - 'string' => array("result" => 'trim',"notes" => 'trim')); - $execInfo['steps'] = getItemsFromSimpleXMLObj($xmlTCExec->steps->step,$itemStructure); - } - - $execInfo['custom_fields'] = null; - if(property_exists($xmlTCExec, 'custom_fields') && property_exists($xmlTCExec->custom_fields, 'custom_field')) { - $itemStructure['elements'] = array('string' => array("name" => 'trim',"value" => 'trim')); - $execInfo['custom_fields'] = getItemsFromSimpleXMLObj($xmlTCExec->custom_fields->custom_field,$itemStructure); - } - - return $execInfo; -} - - -/* - function: - - Check if at least the file starts seems OK - -*/ -function check_xml_execution_results($fileName) { - - $file_check=array('status_ok' => 0, 'msg' => 'xml_ko'); - $xml = @simplexml_load_file_wrapper($fileName); - if($xml !== FALSE) { - $file_check=array('status_ok' => 1, 'msg' => 'ok'); - $elementName = $xml->getName(); - if($elementName != 'results') { - $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_results_import_format')); - } - } - return $file_check; -} - - -/* - function: init_args(&$dbHandler) - - args : - - returns: - -*/ -function init_args(&$dbHandler) { - $args=new stdClass(); - $_REQUEST=strings_stripSlashes($_REQUEST); - - $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; - $args->copyIssues = isset($_REQUEST['copyIssues']) ? 1 : 0; - - - // Need to use REQUEST because sometimes data arrives on GET and other on POST (has hidden fields) - $args->buildID = isset($_REQUEST['buildID']) ? intval($_REQUEST['buildID']) : null; - $args->platformID = isset($_REQUEST['platformID']) ? intval($_REQUEST['platformID']) : null; - $args->tplanID = isset($_REQUEST['tplanID']) ? intval($_REQUEST['tplanID']) : null; - $args->tplanID = !is_null($args->tplanID) ? $args->tplanID : intval($_SESSION['testplanID']); - - $args->tprojectID = isset($_REQUEST['tprojectID']) ? intval($_REQUEST['tprojectID']) : null; - - if( is_null($args->tprojectID)) { - $args->tprojectID = intval($_SESSION['testprojectID']); - $args->testprojectName = $_SESSION['testprojectName']; - } else { - $tproject_mgr = new testproject($dbHandler); - $dummy = $tproject_mgr->get_by_id($args->tprojectID); - $args->testprojectName = $dummy['name']; - } - - $args->doUpload=isset($_REQUEST['UploadFile']) ? 1 : 0; - $args->userID=intval($_SESSION['userID']); - - return $args; -} - -/* - function: check_exec_values() - - args : - - returns: map - keys: - status_ok -> value=true / false - tcase_id: test case id if controls OK - tester_id: tester_id if controls OK - msg -> array with localized messages - - @internal revisions -*/ -function check_exec_values(&$db,&$tcase_mgr,&$user_mgr,$tcaseCfg,&$execValues,&$columnDef) { - $tables = tlObjectWithDB::getDBTables(array('users','execution_bugs')); - $checks=array('status_ok' => false, 'tcase_id' => 0, 'tester_id' => 0, 'msg' => array()); - $tcase_id=$execValues['tcase_id']; - $tcase_external_id=trim($execValues['tcase_external_id']); - $using_external_id = ($tcase_external_id != ""); // external_id has precedence over internal id - - if($using_external_id) { - // need to get internal id - $checks['tcase_id'] = $tcase_mgr->getInternalID($tcase_external_id); - $checks['status_ok'] = intval($checks['tcase_id']) > 0 ? true : false; - if(!$checks['status_ok']) { - $checks['msg'][]=sprintf(lang_get('tcase_external_id_do_not_exists'),$tcase_external_id); - } - } else { - // before using internal id, I want to check it's a number - $checks['tcase_id'] = $tcase_id; - $checks['status_ok'] = intval($checks['tcase_id']) > 0 ? true : false; - if(!$checks['status_ok']) { - $checks['msg'][]=sprintf(lang_get('tcase_id_is_not_number'),$tcase_id); - } - } - - if($checks['status_ok']) { - // useful for user feedback - $identity=$using_external_id ? $tcase_external_id : $checks['tcase_id']; - } - - if($checks['status_ok'] && $execValues['timestamp'] != '' ) { - $checks['status_ok']=isValidISODateTime($execValues['timestamp']); - if(!$checks['status_ok']) { - $checks['msg'][]=sprintf(lang_get('invalid_execution_timestamp'),$identity,$execValues['timestamp']); - } - } - - if($checks['status_ok'] && $execValues['tester'] != '' ) { - $sql = "SELECT id,login FROM {$tables['users']} WHERE login ='" . - $db->prepare_string($execValues['tester']) . "'"; - $userInfo=$db->get_recordset($sql); - - if(!is_null($userInfo) && isset($userInfo[0]['id']) ) { - $checks['tester_id']=$userInfo[0]['id']; - } else { - $checks['status_ok']=false; - $checks['msg'][]=sprintf(lang_get('invalid_tester'),$identity,$execValues['tester']); - } - } - - $execValues['bug_id'] = isset($execValues['bug_id']) ? $execValues['bug_id'] : null; - if($checks['status_ok'] && !is_null($execValues['bug_id']) && is_array($execValues['bug_id']) ) { - foreach($execValues['bug_id'] as $bug_id ) { - if( ($field_len = strlen(trim($bug_id))) > $columnDef['bug_id']->max_length ) { - $checks['msg'][]=sprintf(lang_get('bug_id_invalid_len'),$field_len,$columnDef['bug_id']->max_length); - $checks['status_ok']=false; - break; - } - } - } - - if($checks['status_ok'] && isset($execValues['execution_type']) ) { - $execValues['execution_type'] = intval($execValues['execution_type']); - $execDomain = $tcase_mgr->get_execution_types(); - if( $execValues['execution_type'] == 0 ) { - $execValues['execution_type'] = TESTCASE_EXECUTION_TYPE_MANUAL; - // right now this is useless, but may be in future can be used, then I choose to leave it. - $checks['msg'][]=sprintf(lang_get('missing_exec_type'), - $execValues['execution_type'],$execDomain[$execValues['execution_type']]); - } else { - $checks['status_ok'] = isset($execDomain[$execValues['execution_type']]); - if( !$checks['status_ok'] ) { - $checks['msg'][]=sprintf(lang_get('invalid_exec_type'),$execValues['execution_type']); - } - } - } - - - if($checks['status_ok'] && isset($execValues['steps']) ) { - // To Be done - } - - return $checks; -} - - -/** - * - * - */ -function initializeGui(&$argsObj) { - $guiObj = new stdClass(); - $guiObj->import_title = lang_get('title_results_import_to'); - $guiObj->buildID = $argsObj->buildID; - $guiObj->platformID = $argsObj->platformID; - $guiObj->tplanID = $argsObj->tplanID; - - $guiObj->file_check = array('status_ok' => 1, 'msg' => 'ok'); - $guiObj->importTypes = array("XML" => "XML"); - $guiObj->importLimit = config_get('import_file_max_size_bytes'); - $guiObj->doImport = ($argsObj->importType != ""); - $guiObj->testprojectName = $argsObj->testprojectName; - $guiObj->copyIssues = $argsObj->copyIssues; - return $guiObj; -} \ No newline at end of file diff --git a/lib/results/resultsMoreBuilds.php b/lib/results/resultsMoreBuilds.php index 31fef6d461..bab3e61e9f 100644 --- a/lib/results/resultsMoreBuilds.php +++ b/lib/results/resultsMoreBuilds.php @@ -1,316 +1,315 @@ - - * @copyright 2009,2012 TestLink community - * - * @internal revisions - * @since 1.9.4 - * - **/ -require_once('../../config.inc.php'); -require_once('common.php'); -require_once('users.inc.php'); -require_once('displayMgr.php'); - -testlinkInitPage($db,false,false,"checkRights"); -$templateCfg = templateConfiguration(); -$date_format_cfg = config_get('date_format'); - -$args = init_args(); -$gui = initializeGui($db,$args,$date_format_cfg); -$mailCfg = buildMailCfg($gui); - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->assign('report_type', $args->report_type); -displayReport($templateCfg->template_dir . $templateCfg->default_template, $smarty, $args->report_type,$mailCfg); - - -/** - * initialize Gui - */ -function initializeGui(&$dbHandler,&$argsObj,$dateFormat) -{ - - new dBug($argsObj); - -/* $my['filters'] = array('exec_ts_from' => null, 'exec_ts_to' => null, - 'assigned_to' => null, 'tester_id' => null, - 'keywords' => null, 'builds' => null, - 'plaforms' => null, 'top_level_tsuites' => null); - -*/ - $reports_cfg = config_get('reportsCfg'); - $tplan_mgr = new tlTestPlanMetrics($dbHandler); - $tproject_mgr = new testproject($dbHandler); - - $gui = new stdClass(); - $gui->resultsCfg = config_get('results'); - $gui->title = lang_get('query_metrics_report'); - $gui->tplan_id = $argsObj->tplan_id; - $gui->tproject_id = $argsObj->tproject_id; - - $tplan_info = $tplan_mgr->get_by_id($gui->tplan_id); - $tproject_info = $tproject_mgr->get_by_id($gui->tproject_id); - $gui->tplan_name = $tplan_info['name']; - $gui->tproject_name = $tproject_info['name']; - - $getOpt = array('outputFormat' => 'map'); - $gui->platformSet = $tplan_mgr->getPlatforms($argsObj->tplan_id,$getOpt); - $gui->showPlatforms = true; - if( is_null($gui->platformSet) ) - { - $gui->platformSet = null; - $gui->showPlatforms = false; - } - else - { - $filters['platforms'] = array_keys($gui->platformSet); - } - - // convert starttime to iso format for database usage - list($gui->startTime,$gui->endTime) = helper2ISO($_REQUEST); - - //die(); - - - $gui_open = config_get('gui_separator_open'); - $gui_close = config_get('gui_separator_close'); - $gui->str_option_any = $gui_open . lang_get('any') . $gui_close; - $gui->str_option_none = $gui_open . lang_get('nobody') . $gui_close; - - $gui->search_notes_string = $argsObj->search_notes_string; - - $testsuiteNames = null; - $everest = $tplan_mgr->getRootTestSuites($gui->tplan_id,$gui->tproject_id,array('output' => 'plain')); - $tsuites_qty = sizeOf($argsObj->testsuitesSelected); - - $userWantsAll = ($tsuits_qty == 0 || $tsuits_qty == count($everest)); - $filters['top_level_tsuites'] = ($tsuites_qty == 0 || $tsuites_qty == count($everest)) ? null : $argsObj->testsuitesSelected; - $gui->testsuitesSelected = array(); - foreach($argsObj->testsuitesSelected as $dmy) - { - $gui->testsuitesSelected[$dmy] = $everest[$dmy]['name']; - } - - $filters['builds'] = null; - if (sizeof($argsObj->buildsSelected)) - { - $filters['builds'] = implode(",", $argsObj->buildsSelected); - } - - $filters['keywords'] = (array)$argsObj->keywordSelected; - if(in_array(0,$filters['keywords'])) // Sorry for MAGIC 0 => ANY - { - $filters['keywords'] = null; - } - - // statusForClass is used for results.class.php - // lastStatus is used to be displayed - $statusForClass = 'a'; - - // amitkhullar - added this parameter to get the latest results. - $latest_resultset = $argsObj->display->latest_results; - - $assignee = $argsObj->ownerSelected > 0 ? $argsObj->ownerSelected : TL_USER_ANYBODY; - $tester = $argsObj->executorSelected > 0 ? $argsObj->executorSelected : TL_USER_ANYBODY ; - - - - - //$rs = $tplan_mgr->queryMetrics($gui->tplan_id,$filters); - //new dBug($rs); - // die(); - - //$re = new newResults($dbHandler, $tplan_mgr,$tproject_info,$tplan_info, - // $testsuiteIds, $buildsToQuery, - // $argsObj->platformsSelected, $statusForClass, - // $latest_resultset, $argsObj->keywordSelected, - // $assignee, $gui->startTime, - // $gui->endTime, $tester, - // $argsObj->search_notes_string, null); - // - //$gui->suiteList = $re->getSuiteList(); // test executions results - //// Filter test cases on selected platforms - //foreach ($gui->suiteList as $suiteid => $tcases) - //{ - // $filtered = array(); - // foreach ($tcases as $index => $tcase) { - // if ($tcase['platform_id'] == 0 || - // $argsObj->platformsSelected[0] == ALL_PLATFORMS || - // array_search($tcase['platform_id'], $argsObj->platformsSelected) !== false) { - // array_push($filtered, $tcase); - // } - // } - // unset($gui->suiteList[$suiteid]); - // $gui->suiteList[$suiteid] = $filtered; - //} - //$gui->flatArray = $re->getFlatArray(); - //$gui->mapOfSuiteSummary = $re->getAggregateMap(); - // - - - // Prepare User Feedback - $gui->totals = new stdClass(); - $gui->totals->items = 0; - $gui->totals->labels = array(); - - foreach($gui->totals->items as $key => $value) - { - $l18n = $key == 'total' ? 'th_total_cases' : $gui->resultsCfg['status_label'][$key]; - $gui->totals->labels[$key] = lang_get($l18n); - } - - $gui->keywords = new stdClass(); - $gui->keywords->items[0] = $gui->str_option_any; // Sorry MAGIC 0 - if(!is_null($tplan_keywords_map = $tplan_mgr->get_keywords_map($gui->tplan_id))) - { - $gui->keywords->items += $tplan_keywords_map; - } - $gui->keywords->qty = count($gui->keywords->items); - $gui->keywordSelected = $gui->keywords->items[$argsObj->keywordSelected]; - - $gui->builds_html = $tplan_mgr->get_builds_for_html_options($gui->tplan_id); - $gui->users = getUsersForHtmlOptions($dbHandler,ALL_USERS_FILTER, - array(TL_USER_ANYBODY => $gui->str_option_any)); - - $gui->ownerSelected = $gui->users[$argsObj->ownerSelected]; - $gui->executorSelected = $gui->users[$argsObj->executorSelected]; - $gui->buildsSelected = $argsObj->buildsSelected; - $gui->platformsSelected = $argsObj->platformsSelected; - $gui->display = $argsObj->display; - - // init display rows attribute and some status localized labels - $gui->displayResults = array(); - $gui->lastStatus = array(); - foreach($reports_cfg->exec_status as $verbose => $label) - { - $gui->displayResults[$gui->resultsCfg['status_code'][$verbose]]=false; - } - - foreach($gui->resultsCfg['status_label'] as $status_verbose => $label_key) - { - $gui->statusLabels[$gui->resultsCfg['status_code'][$status_verbose]] = lang_get($label_key); - } - - $lastStatus_localized = null; - foreach($argsObj->lastStatus as $key => $status_code) - { - $verbose = $gui->resultsCfg['code_status'][$status_code]; - $gui->displayResults[$status_code] = true; - $lastStatus_localized[] = lang_get($gui->resultsCfg['status_label'][$verbose]); - } - $gui->lastStatus = $lastStatus_localized; - - return $gui; -} - -/** - * Initialize input data - */ -function init_args() -{ - $iParams = array("format" => array(tlInputParameter::INT_N), - "report_type" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "build" => array(tlInputParameter::ARRAY_INT), - "platform" => array(tlInputParameter::ARRAY_INT), - "keyword" => array(tlInputParameter::INT_N), - "owner" => array(tlInputParameter::INT_N), - "executor" => array(tlInputParameter::INT_N), - "display_totals" => array(tlInputParameter::INT_N,1), - "display_query_params" => array(tlInputParameter::INT_N,1), - "display_test_cases" => array(tlInputParameter::INT_N,1), - "display_latest_results" => array(tlInputParameter::INT_N,1), - "display_suite_summaries" => array(tlInputParameter::INT_N,1), - "lastStatus" => array(tlInputParameter::ARRAY_STRING_N), - "testsuite" => array(tlInputParameter::ARRAY_STRING_N), - "search_notes_string" => array(tlInputParameter::STRING_N)); - $args = new stdClass(); - - $_REQUEST=strings_stripSlashes($_REQUEST); - $pParams = R_PARAMS($iParams); - - $args->format = $pParams["format"]; - $args->report_type = $pParams["report_type"]; - $args->tplan_id = $pParams["tplan_id"]; - - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - $args->display = new stdClass(); - $args->display->suite_summaries = $pParams["display_suite_summaries"]; - $args->display->totals = $pParams["display_totals"]; - $args->display->query_params = $pParams["display_query_params"]; - $args->display->test_cases = $pParams["display_test_cases"]; - $args->display->latest_results = $pParams["display_latest_results"]; - - $args->lastStatus = $pParams["lastStatus"] ? $pParams["lastStatus"] : array(); - $args->keywordSelected = $pParams["keyword"]; - $args->ownerSelected = $pParams["owner"]; - $args->executorSelected = $pParams["executor"]; - $args->buildsSelected = $pParams["build"] ? $pParams["build"] : array(); - $args->platformsSelected = $pParams["platform"] ? $pParams["platform"] : array(); - $args->testsuitesSelected = $pParams["testsuite"] ? $pParams["testsuite"] : array(); - $args->search_notes_string = $pParams['search_notes_string']; - - return $args; -} - - -/** - * - * - */ -function buildMailCfg(&$guiObj) -{ - $labels = init_labels(array('testplan' => null, 'testproject' => null)); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . - $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - return $cfg; -} - -function helper2ISO($userInput) -{ - $dateFormatMask = config_get('date_format'); - $zy = array(); - $key2loop = array('selected_start_date' => 'startTime','selected_end_date' => 'endTime'); - foreach($key2loop as $target => $prop) - { - if (isset($userInput[$target]) && $userInput[$target] != '') - { - $dummy = split_localized_date($userInput[$target], $dateFormatMask); - if($dummy != null) - { - $zy[$prop] = $dummy['year'] . "-" . $dummy['month'] . "-" . $dummy['day']; - } - } - } - - $dummy = isset($userInput['start_Hour']) ? $userInput['start_Hour'] : "00"; - $zy['startTime'] .= " " . $dummy . ":00:00"; - $dummy = isset($userInput['end_Hour']) ? $userInput['end_Hour'] : "00"; - $zy['endTime'] .= " " . $dummy . ":59:59"; - - return(array($zy['startTime'],$zy['endTime'])); -} - - - -/** - * - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} -?> \ No newline at end of file + + * @copyright 2009,2012 TestLink community + * + * @internal revisions + * @since 1.9.4 + * + **/ +require_once '../../config.inc.php'; +require_once 'common.php'; +require_once 'users.inc.php'; +require_once 'displayMgr.php'; + +testlinkInitPage($db, false, false, "checkRights"); +$templateCfg = templateConfiguration(); +$date_format_cfg = config_get('date_format'); + +$args = initArgs(); +$gui = initializeGui($db, $args); +$mailCfg = buildMailCfg($gui); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('report_type', $args->report_type); +displayReport($templateCfg->template_dir . $templateCfg->default_template, + $smarty, $args->report_type, $mailCfg); + +/** + * initialize Gui + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param string $dateFormat + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $reports_cfg = config_get('reportsCfg'); + $tplan_mgr = new tlTestPlanMetrics($dbHandler); + $tproject_mgr = new testproject($dbHandler); + + $gui = new stdClass(); + $gui->resultsCfg = config_get('results'); + $gui->title = lang_get('query_metrics_report'); + $gui->tplan_id = $argsObj->tplan_id; + $gui->tproject_id = $argsObj->tproject_id; + + $tplan_info = $tplan_mgr->get_by_id($gui->tplan_id); + $tproject_info = $tproject_mgr->get_by_id($gui->tproject_id); + $gui->tplan_name = $tplan_info['name']; + $gui->tproject_name = $tproject_info['name']; + + $getOpt = array( + 'outputFormat' => 'map' + ); + $gui->platformSet = $tplan_mgr->getPlatforms($argsObj->tplan_id, $getOpt); + $gui->showPlatforms = true; + if (is_null($gui->platformSet)) { + $gui->platformSet = null; + $gui->showPlatforms = false; + } else { + $filters['platforms'] = array_keys($gui->platformSet); + } + + // convert starttime to iso format for database usage + list ($gui->startTime, $gui->endTime) = helper2ISO($_REQUEST); + + $gui_open = config_get('gui_separator_open'); + $gui_close = config_get('gui_separator_close'); + $gui->str_option_any = $gui_open . lang_get('any') . $gui_close; + $gui->str_option_none = $gui_open . lang_get('nobody') . $gui_close; + + $gui->search_notes_string = $argsObj->search_notes_string; + + $everest = $tplan_mgr->getRootTestSuites($gui->tplan_id, $gui->tproject_id, + array( + 'output' => 'plain' + )); + $tsuites_qty = count($argsObj->testsuitesSelected); + + $filters['top_level_tsuites'] = ($tsuites_qty == 0 || + $tsuites_qty == count($everest)) ? null : $argsObj->testsuitesSelected; + $gui->testsuitesSelected = array(); + foreach ($argsObj->testsuitesSelected as $dmy) { + $gui->testsuitesSelected[$dmy] = $everest[$dmy]['name']; + } + + $filters['builds'] = null; + if (count($argsObj->buildsSelected)) { + $filters['builds'] = implode(",", $argsObj->buildsSelected); + } + + $filters['keywords'] = (array) $argsObj->keywordSelected; + if (in_array(0, $filters['keywords'])) // Sorry for MAGIC 0 => ANY + { + $filters['keywords'] = null; + } + + // Prepare User Feedback + $gui->totals = new stdClass(); + $gui->totals->items = 0; + $gui->totals->labels = array(); + + foreach ($gui->totals->items as $key => $value) { + $l18n = $key == 'total' ? 'th_total_cases' : $gui->resultsCfg['status_label'][$key]; + $gui->totals->labels[$key] = lang_get($l18n); + } + + $gui->keywords = new stdClass(); + $gui->keywords->items[0] = $gui->str_option_any; // Sorry MAGIC 0 + if (! is_null( + $tplan_keywords_map = $tplan_mgr->get_keywords_map($gui->tplan_id))) { + $gui->keywords->items += $tplan_keywords_map; + } + $gui->keywords->qty = count($gui->keywords->items); + $gui->keywordSelected = $gui->keywords->items[$argsObj->keywordSelected]; + + $gui->builds_html = $tplan_mgr->get_builds_for_html_options($gui->tplan_id); + $gui->users = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, + array( + TL_USER_ANYBODY => $gui->str_option_any + )); + + $gui->ownerSelected = $gui->users[$argsObj->ownerSelected]; + $gui->executorSelected = $gui->users[$argsObj->executorSelected]; + $gui->buildsSelected = $argsObj->buildsSelected; + $gui->platformsSelected = $argsObj->platformsSelected; + $gui->display = $argsObj->display; + + // init display rows attribute and some status localized labels + $gui->displayResults = array(); + $gui->lastStatus = array(); + foreach ($reports_cfg->exec_status as $verbose => $label) { + $gui->displayResults[$gui->resultsCfg['status_code'][$verbose]] = false; + } + + foreach ($gui->resultsCfg['status_label'] as $status_verbose => $label_key) { + $gui->statusLabels[$gui->resultsCfg['status_code'][$status_verbose]] = lang_get( + $label_key); + } + + $lastStatus_localized = null; + foreach ($argsObj->lastStatus as $status_code) { + $verbose = $gui->resultsCfg['code_status'][$status_code]; + $gui->displayResults[$status_code] = true; + $lastStatus_localized[] = lang_get( + $gui->resultsCfg['status_label'][$verbose]); + } + $gui->lastStatus = $lastStatus_localized; + + return $gui; +} + +/** + * Initialize input data + */ +function initArgs() +{ + $iParams = array( + "format" => array( + tlInputParameter::INT_N + ), + "report_type" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "build" => array( + tlInputParameter::ARRAY_INT + ), + "platform" => array( + tlInputParameter::ARRAY_INT + ), + "keyword" => array( + tlInputParameter::INT_N + ), + "owner" => array( + tlInputParameter::INT_N + ), + "executor" => array( + tlInputParameter::INT_N + ), + "display_totals" => array( + tlInputParameter::INT_N, + 1 + ), + "display_query_params" => array( + tlInputParameter::INT_N, + 1 + ), + "display_test_cases" => array( + tlInputParameter::INT_N, + 1 + ), + "display_latest_results" => array( + tlInputParameter::INT_N, + 1 + ), + "display_suite_summaries" => array( + tlInputParameter::INT_N, + 1 + ), + "lastStatus" => array( + tlInputParameter::ARRAY_STRING_N + ), + "testsuite" => array( + tlInputParameter::ARRAY_STRING_N + ), + "search_notes_string" => array( + tlInputParameter::STRING_N + ) + ); + $args = new stdClass(); + + $_REQUEST = strings_stripSlashes($_REQUEST); + $pParams = R_PARAMS($iParams); + + $args->format = $pParams["format"]; + $args->report_type = $pParams["report_type"]; + $args->tplan_id = $pParams["tplan_id"]; + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + $args->display = new stdClass(); + $args->display->suite_summaries = $pParams["display_suite_summaries"]; + $args->display->totals = $pParams["display_totals"]; + $args->display->query_params = $pParams["display_query_params"]; + $args->display->test_cases = $pParams["display_test_cases"]; + $args->display->latest_results = $pParams["display_latest_results"]; + + $args->lastStatus = $pParams["lastStatus"] ? $pParams["lastStatus"] : array(); + $args->keywordSelected = $pParams["keyword"]; + $args->ownerSelected = $pParams["owner"]; + $args->executorSelected = $pParams["executor"]; + $args->buildsSelected = $pParams["build"] ? $pParams["build"] : array(); + $args->platformsSelected = $pParams["platform"] ? $pParams["platform"] : array(); + $args->testsuitesSelected = $pParams["testsuite"] ? $pParams["testsuite"] : array(); + $args->search_notes_string = $pParams['search_notes_string']; + + return $args; +} + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = init_labels(array( + 'testplan' => null, + 'testproject' => null + )); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + return $cfg; +} + +/** + * + * @param string $userInput + * @return array + */ +function helper2ISO($userInput) +{ + $dateFormatMask = config_get('date_format'); + $zy = array(); + $key2loop = array( + 'selected_start_date' => 'startTime', + 'selected_end_date' => 'endTime' + ); + foreach ($key2loop as $target => $prop) { + if (isset($userInput[$target]) && $userInput[$target] != '') { + $dummy = split_localized_date($userInput[$target], $dateFormatMask); + if ($dummy != null) { + $zy[$prop] = $dummy['year'] . "-" . $dummy['month'] . "-" . + $dummy['day']; + } + } + } + + $dummy = isset($userInput['start_Hour']) ? $userInput['start_Hour'] : "00"; + $zy['startTime'] .= " " . $dummy . ":00:00"; + $dummy = isset($userInput['end_Hour']) ? $userInput['end_Hour'] : "00"; + $zy['endTime'] .= " " . $dummy . ":59:59"; + + return array( + $zy['startTime'], + $zy['endTime'] + ); +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); +} +?> diff --git a/lib/results/resultsMoreBuildsGUI.php b/lib/results/resultsMoreBuildsGUI.php index ce284875d3..5622f15e8f 100644 --- a/lib/results/resultsMoreBuildsGUI.php +++ b/lib/results/resultsMoreBuildsGUI.php @@ -1,155 +1,172 @@ - - * - * @internal revisions - * @since 1.9.4 - * - **/ -require_once('../../config.inc.php'); -require_once('common.php'); -require_once('exec.inc.php'); -require_once('users.inc.php'); -testlinkInitPage($db,true,false,"checkRights"); - -$templateCfg = templateConfiguration(); - -$args = init_args(); -$gui = initializeGui($db,$args); - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/* - function: get_status_for_reports_html_options - generates map useful for smarty html_options - - args : - - returns: map key -> status code, value -> localized status description - -*/ -function get_status_for_reports_html_options() -{ - $reports_cfg = config_get('reportsCfg'); - $results = config_get('results'); - - foreach($reports_cfg->exec_status as $verbose_status => $status_label) - { - $code = $results['status_code'][$verbose_status]; - $html_options[$code] = lang_get($status_label); - } - - return $html_options; -} - - -/* - function: initializeGui - - args : - - returns: - -*/ -function initializeGui(&$dbHandler,$args) -{ - - $gui = new stdClass(); - $tplan_mgr = new testplan($dbHandler); - - $gui_open = config_get('gui_separator_open'); - $gui_close = config_get('gui_separator_close'); - $gui->str_option_any = $gui_open . lang_get('any') . $gui_close; - $gui->str_option_none = $gui_open . lang_get('nobody') . $gui_close; - - $gui->tplan_id = $args->tplan_id; - $gui->tproject_id = $args->tproject_id; - - $tplan_info = $tplan_mgr->get_by_id($gui->tplan_id); - $gui->tplan_name = $tplan_info['name']; - unset($tplan_info); - - $ni = $tplan_mgr->tree_manager->get_node_hierarchy_info($gui->tproject_id); - $gui->tproject_name = $ni['name']; - unset($ni); - - $gui->assigned_users = new stdClass(); - $gui->keywords = new stdClass(); - $gui->builds = new stdClass(); - $gui->platforms = new stdClass(); - $gui->testsuites = new stdClass(); - - // 20090107 - franciscom - // Show only users that are able to execute test cases ? - // What happens if a user that has loose right to execute, but - // before loosing this right has been assigned some tests, or have executed it? - // - // $gui->assigned_users->items = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, ADD_BLANK_OPTION); - // $gui->assigned_users->items = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, - // array(TL_USER_ANYBODY => $gui->str_option_any, - // TL_USER_NOBODY => $gui->str_option_none) ); - // - $gui->assigned_users->items = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, - array(TL_USER_ANYBODY => $gui->str_option_any) ); - - $gui->builds->items = $tplan_mgr->get_builds($gui->tplan_id,testplan::ACTIVE_BUILDS); - $gui->platforms->items = $tplan_mgr->getPlatforms($gui->tplan_id); - $gui->testsuites->items = $tplan_mgr->getRootTestSuites($gui->tplan_id,$gui->tproject_id, - array('output' => 'plain')); - - $gui->keywords->items[0]=$gui->str_option_any; - if(!is_null($tplan_keywords_map=$tplan_mgr->get_keywords_map($gui->tplan_id)) ) - { - $gui->keywords->items += $tplan_keywords_map; - } - - - $key2loop = array('keywords','builds','platforms','testsuites','assigned_users'); - foreach($key2loop as $kx) - { - $gui->$kx->qty = count($gui->$kx->items); - - } - $gui->status_code_label = get_status_for_reports_html_options(); - $gui->report_type = $args->format; - - $reports_cfg = config_get('reportsCfg'); - $ldf = config_get('locales_date_format'); - $date_format = $ldf[((isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB')]; - $gui->selected_start_date = strftime($date_format, time() - ($reports_cfg->start_date_offset)); - $gui->selected_start_time = $reports_cfg->start_time; - $gui->selected_end_date = strftime($date_format, time()); - $gui->selected_end_time = null; - - return $gui; -} - - - -function init_args() -{ - $iParams = array("format" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - - return $args; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} -?> \ No newline at end of file + + * + * @internal revisions + * @since 1.9.4 + * + **/ +require_once '../../config.inc.php'; +require_once 'common.php'; +require_once 'exec.inc.php'; +require_once 'users.inc.php'; +testlinkInitPage($db, true, false, "checkRights"); + +$templateCfg = templateConfiguration(); + +$args = initArgs(); +$gui = initializeGui($db, $args); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: get_status_for_reports_html_options + * generates map useful for smarty html_options + * + * args : + * + * returns: map key -> status code, value -> localized status description + * + */ +function getStatusForReportsHTMLOptions() +{ + $reports_cfg = config_get('reportsCfg'); + $results = config_get('results'); + + foreach ($reports_cfg->exec_status as $verbose_status => $status_label) { + $code = $results['status_code'][$verbose_status]; + $html_options[$code] = lang_get($status_label); + } + + return $html_options; +} + +/** + * + * @param database $dbHandler + * @param unknown $args + * @return stdClass + */ +function initializeGui(&$dbHandler, $args) +{ + $gui = new stdClass(); + $tplan_mgr = new testplan($dbHandler); + + $gui_open = config_get('gui_separator_open'); + $gui_close = config_get('gui_separator_close'); + $gui->str_option_any = $gui_open . lang_get('any') . $gui_close; + $gui->str_option_none = $gui_open . lang_get('nobody') . $gui_close; + + $gui->tplan_id = $args->tplan_id; + $gui->tproject_id = $args->tproject_id; + + $tplan_info = $tplan_mgr->get_by_id($gui->tplan_id); + $gui->tplan_name = $tplan_info['name']; + unset($tplan_info); + + $ni = $tplan_mgr->tree_manager->get_node_hierarchy_info($gui->tproject_id); + $gui->tproject_name = $ni['name']; + unset($ni); + + $gui->assigned_users = new stdClass(); + $gui->keywords = new stdClass(); + $gui->builds = new stdClass(); + $gui->platforms = new stdClass(); + $gui->testsuites = new stdClass(); + + // 20090107 - franciscom + // Show only users that are able to execute test cases ? + // What happens if a user that has loose right to execute, but + // before loosing this right has been assigned some tests, or have executed it? + // + // $gui->assigned_users->items = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, ADD_BLANK_OPTION); + // $gui->assigned_users->items = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, + // array(TL_USER_ANYBODY => $gui->str_option_any, + // TL_USER_NOBODY => $gui->str_option_none) ); + $gui->assigned_users->items = getUsersForHtmlOptions($dbHandler, + ALL_USERS_FILTER, array( + TL_USER_ANYBODY => $gui->str_option_any + )); + + $gui->builds->items = $tplan_mgr->get_builds($gui->tplan_id, + testplan::ACTIVE_BUILDS); + $gui->platforms->items = $tplan_mgr->getPlatforms($gui->tplan_id); + $gui->testsuites->items = $tplan_mgr->getRootTestSuites($gui->tplan_id, + $gui->tproject_id, array( + 'output' => 'plain' + )); + + $gui->keywords->items[0] = $gui->str_option_any; + if (! is_null( + $tplan_keywords_map = $tplan_mgr->get_keywords_map($gui->tplan_id))) { + $gui->keywords->items += $tplan_keywords_map; + } + + $key2loop = array( + 'keywords', + 'builds', + 'platforms', + 'testsuites', + 'assigned_users' + ); + foreach ($key2loop as $kx) { + $gui->$kx->qty = count($gui->$kx->items); + } + $gui->status_code_label = getStatusForReportsHTMLOptions(); + $gui->report_type = $args->format; + + $reports_cfg = config_get('reportsCfg'); + $ldf = config_get('locales_date_format'); + $date_format = $ldf[((isset($_SESSION['locale'])) ? $_SESSION['locale'] : 'en_GB')]; + $gui->selected_start_date = @strftime($date_format, + time() - ($reports_cfg->start_date_offset)); + $gui->selected_start_time = $reports_cfg->start_time; + $gui->selected_end_date = @strftime($date_format, time()); + $gui->selected_end_time = null; + + return $gui; +} + +/** + * + * @return stdClass + */ +function initArgs() +{ + $iParams = array( + "format" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); +} +?> diff --git a/lib/results/resultsNavigator.php b/lib/results/resultsNavigator.php index e0fdf40937..991f9cf0a6 100644 --- a/lib/results/resultsNavigator.php +++ b/lib/results/resultsNavigator.php @@ -1,140 +1,157 @@ - - * - * - **/ -require('../../config.inc.php'); -require_once('common.php'); -require_once('reports.class.php'); -testlinkInitPage($db,true,false,"checkRights"); - -$smarty = new TLSmarty(); -$templateCfg = templateConfiguration(); -$args = init_args(); -$gui = initializeGui($db,$args); -$reports_mgr = new tlReports($db, $gui->tplan_id); - -// ----------------------------------------------------------------------------- -// Do some checks to understand if reports make sense - -// Check if there are linked test cases to the choosen test plan. -$tc4tp_count = $reports_mgr->get_count_testcase4testplan(); -tLog('TC in TP count = ' . $tc4tp_count); -if( $tc4tp_count == 0) { - // Test plan without test cases - $gui->do_report['status_ok'] = 0; - $gui->do_report['msg'] = lang_get('report_tplan_has_no_tcases'); + + * + * + **/ +require_once '../../config.inc.php'; +require_once 'common.php'; +require_once 'reports.class.php'; +testlinkInitPage($db, true, false, "checkRights"); + +$smarty = new TLSmarty(); +$templateCfg = templateConfiguration(); +$args = initArgs(); +$gui = initializeGui($db, $args); +$reports_mgr = new tlReports($db, $gui->tplan_id); + +// ----------------------------------------------------------------------------- +// Do some checks to understand if reports make sense + +// Check if there are linked test cases to the choosen test plan. +$tc4tp_count = $reports_mgr->get_count_testcase4testplan(); +tLog('TC in TP count = ' . $tc4tp_count); +if ($tc4tp_count == 0) { + // Test plan without test cases + $gui->do_report['status_ok'] = 0; + $gui->do_report['msg'] = lang_get('report_tplan_has_no_tcases'); +} + +// Build qty +$build_count = $reports_mgr->get_count_builds(); +tLog('Active Builds count = ' . $build_count); +if ($build_count == 0) { + // Test plan without builds can have execution data + $gui->do_report['status_ok'] = 0; + $gui->do_report['msg'] = lang_get('report_tplan_has_no_build'); +} + +// ----------------------------------------------------------------------------- +// get navigation data +$gui->menuItems = array(); +if ($gui->do_report['status_ok']) { + // create a list or reports + $context = new stdClass(); + $context->tproject_id = $args->tproject_id; + $context->tplan_id = $args->tplan_id; + + $tplan_mgr = new testplan($db); + $dmy = $tplan_mgr->get_by_id($context->tplan_id); + unset($tplan_mgr); + + $context->apikey = $dmy['api_key']; + $context->imgSet = $smarty->getImages(); + $gui->menuItems = $reports_mgr->get_list_reports($context, $gui->btsEnabled, + $args->optReqs, $tlCfg->reports_formats[$args->format]); +} + +$gui->selectedReportType = $args->format; +$gui->reportTypes = localize_array($tlCfg->reports_formats); + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $iParams = array( + "format" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "show_inactive_tplans" => array( + tlInputParameter::CB_BOOL + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + + if (is_null($args->format)) { + $reports_formats = config_get('reports_formats'); + $args->format = count($reports_formats) ? key($reports_formats) : null; + } + + if (is_null($args->tplan_id)) { + $args->tplan_id = $_SESSION['testplanID']; + } + + $_SESSION['resultsNavigator_testplanID'] = $args->tplan_id; + $_SESSION['resultsNavigator_format'] = $args->format; + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + $args->userID = $_SESSION['userID']; + $args->user = $_SESSION['currentUser']; + $args->optReqs = $_SESSION['testprojectOptions']->requirementsEnabled; + $args->checked_show_inactive_tplans = $args->show_inactive_tplans ? 'checked="checked"' : 0; + $args->show_only_active_tplans = ! $args->show_inactive_tplans; + + return $args; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$dbHandler, $argsObj) +{ + $gui = new stdClass(); + + $gui->workframe = $_SESSION['basehref'] . + "lib/general/staticPage.php?key=showMetrics"; + $gui->do_report = array( + 'status_ok' => 1, + 'msg' => '' + ); + $gui->tplan_id = $argsObj->tplan_id; + $gui->tproject_id = $argsObj->tproject_id; + $gui->checked_show_inactive_tplans = $argsObj->checked_show_inactive_tplans; + + $tproject_mgr = new testproject($dbHandler); + $gui->btsEnabled = $tproject_mgr->isIssueTrackerEnabled($gui->tproject_id); + + // get Accessible Test Plans for combobox + $activeAttr = $argsObj->show_only_active_tplans ? 1 : null; + $gui->tplans = $argsObj->user->getAccessibleTestPlans($dbHandler, + $argsObj->tproject_id, null, + array( + 'output' => 'combo', + 'active' => $activeAttr + )); + + return $gui; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return boolean + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_metrics'); } - -// Build qty -$build_count = $reports_mgr->get_count_builds(); -tLog('Active Builds count = ' . $build_count); -if( $build_count == 0) { - // Test plan without builds can have execution data - $gui->do_report['status_ok'] = 0; - $gui->do_report['msg'] = lang_get('report_tplan_has_no_build'); -} - -// ----------------------------------------------------------------------------- -// get navigation data -$gui->menuItems = array(); -if($gui->do_report['status_ok']) { - // create a list or reports - $context = new stdClass(); - $context->tproject_id = $args->tproject_id; - $context->tplan_id = $args->tplan_id; - - $tplan_mgr = new testplan($db); - $dmy = $tplan_mgr->get_by_id($context->tplan_id); - unset($tplan_mgr); - - $context->apikey = $dmy['api_key']; - $context->imgSet = $smarty->getImages(); - $gui->menuItems = - $reports_mgr->get_list_reports($context,$gui->btsEnabled,$args->optReqs, - $tlCfg->reports_formats[$args->format]); -} - -$gui->selectedReportType = $args->format; -$gui->reportTypes = localize_array($tlCfg->reports_formats); - - -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - * - */ -function init_args() { - $iParams = array("format" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "show_inactive_tplans" => array(tlInputParameter::CB_BOOL)); - $args = new stdClass(); - R_PARAMS($iParams,$args); - - if (is_null($args->format)) { - $reports_formats = config_get('reports_formats'); - $args->format = sizeof($reports_formats) ? key($reports_formats) : null; - } - - if (is_null($args->tplan_id)) { - $args->tplan_id = $_SESSION['testplanID']; - } - - $_SESSION['resultsNavigator_testplanID'] = $args->tplan_id; - $_SESSION['resultsNavigator_format'] = $args->format; - - $args->tproject_id = - isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - $args->userID = $_SESSION['userID']; - $args->user = $_SESSION['currentUser']; - $args->optReqs = $_SESSION['testprojectOptions']->requirementsEnabled; - $args->checked_show_inactive_tplans = - $args->show_inactive_tplans ? 'checked="checked"' : 0; - $args->show_only_active_tplans = !$args->show_inactive_tplans; - - return $args; -} - -/** - * - */ -function initializeGui(&$dbHandler,$argsObj) { - $gui = new stdClass(); - - $gui->workframe = $_SESSION['basehref'] . "lib/general/staticPage.php?key=showMetrics"; - $gui->do_report = array('status_ok' => 1, 'msg' => ''); - $gui->tplan_id = $argsObj->tplan_id; - $gui->tproject_id = $argsObj->tproject_id; - $gui->checked_show_inactive_tplans = $argsObj->checked_show_inactive_tplans; - - $tproject_mgr = new testproject($dbHandler); - $gui->btsEnabled = $tproject_mgr->isIssueTrackerEnabled($gui->tproject_id); - - // get Accessible Test Plans for combobox - $activeAttr = $argsObj->show_only_active_tplans ? 1 : null; - $gui->tplans = - $argsObj->user->getAccessibleTestPlans($dbHandler,$argsObj->tproject_id,null, - array('output' =>'combo', 'active' => $activeAttr)); - - return $gui; -} - - - -/** - * - * - */ -function checkRights(&$db,&$user) { - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file diff --git a/lib/results/resultsReqs.php b/lib/results/resultsReqs.php index f496ba7e8f..bbff4aeaca 100644 --- a/lib/results/resultsReqs.php +++ b/lib/results/resultsReqs.php @@ -1,870 +1,928 @@ -getImages(); -$gui = init_gui($args,$tplan_mgr); -$i2u = array('edit_icon','exec_icon','history_small'); - - -$reqContext = array('tproject_id' => $args->tproject_id, 'tplan_id' => $args->tplan_id, - 'platform_id' => $args->platform); - -$reqSetX = (array)$req_mgr->getAllByContext($reqContext); -$req_ids = array_keys($reqSetX); - -$prefix = $tproject_mgr->getTestCasePrefix($args->tproject_id) . (config_get('testcase_cfg')->glue_character); - -$rspecSet = array(); -$testcases = array(); - -// first step: get the requirements and linked testcases with which we have to work, -// order them into $rspecSet by spec -$gui->total_reqs = 0; -if (count($req_ids)) -{ - list($gui->total_reqs,$rspecSet,$testcases) = buildReqSpecMap($req_ids,$req_mgr,$req_spec_mgr,$tplan_mgr, - $args->states_to_show->selected,$args); - if (!count($rspecSet)) - { - $gui->warning_msg = $labels['no_matching_reqs']; - } +getImages(); +$gui = initGui($args, $tplan_mgr); +$i2u = array( + 'edit_icon', + 'exec_icon', + 'history_small' +); + +$reqContext = array( + 'tproject_id' => $args->tproject_id, + 'tplan_id' => $args->tplan_id, + 'platform_id' => $args->platform +); + +$reqSetX = (array) $req_mgr->getAllByContext($reqContext); +$req_ids = array_keys($reqSetX); + +$prefix = $tproject_mgr->getTestCasePrefix($args->tproject_id) . + (config_get('testcase_cfg')->glue_character); + +$rspecSet = array(); +$testcases = array(); + +// first step: get the requirements and linked testcases with which we have to work, +// order them into $rspecSet by spec +$gui->total_reqs = 0; +if (count($req_ids)) { + list ($gui->total_reqs, $rspecSet, $testcases) = buildReqSpecMap($req_ids, + $req_mgr, $req_spec_mgr, $tplan_mgr, $args->states_to_show->selected, + $args); + if (! count($rspecSet)) { + $gui->warning_msg = $labels['no_matching_reqs']; + } +} else { + $gui->warning_msg = $labels['no_srs_defined']; +} + +// second step: walk through req spec map, count/calculate, store results +if (count($rspecSet)) { + + foreach ($rspecSet as $rspec_id => $req_spec_info) { + $rspecSet[$rspec_id]['req_counters'] = array( + 'total' => 0 + ); + foreach ($req_spec_info['requirements'] as $req_id => $req_info) { + // Test Plan Test Case Version (TPTCV) + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'] = array( + 'total' => 0, + 'totalTPTCV' => 0 + ); + + // add coverage for more detailed evaluation + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']['expected_coverage'] = $rspecSet[$rspec_id]['requirements'][$req_id]['expected_coverage']; + + foreach ($req_info['linked_testcases'] as $tc_info) { + $tc_id = $tc_info['id']; + $plat2loop = array_keys($testcases[$tc_id]); + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']['total'] ++; + + foreach ($plat2loop as $plat_id) { + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']['totalTPTCV'] ++; + if (isset($testcases[$tc_id][$plat_id]['exec_status'])) { + $status = $testcases[$tc_id][$plat_id]['exec_status']; + + // if the counters for this status don't exist yet, initialize them with 0 + if (! isset( + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'][$status])) { + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'][$status] = 0; + } + + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'][$status] ++; + } + } + } + + // evaluate this requirement by configured coverage algorithm + $eval = evaluateReq($status_code_map, + $req_cfg->coverageStatusAlgorithm, + $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']); + + $rspecSet[$rspec_id]['requirements'][$req_id]['evaluation'] = $eval; + + if (! isset($rspecSet[$rspec_id]['req_counters'][$eval])) { + $rspecSet[$rspec_id]['req_counters'][$eval] = 0; + } + + $rspecSet[$rspec_id]['req_counters'][$eval] ++; + $rspecSet[$rspec_id]['req_counters']['total'] ++; + } + } +} + +// last step: build the table +if (count($rspecSet)) { + $allStatusCode = config_get('results'); + + // headers + $columns = array(); + $columns[] = array( + 'title_key' => 'req_spec_short', + 'groupable' => 'true', + 'hideable' => 'false', + 'hidden' => 'true' + ); + $columns[] = array( + 'title_key' => 'title', + 'width' => 100, + 'groupable' => 'false', + 'type' => 'text' + ); + $columns[] = array( + 'title_key' => 'version', + 'width' => 20, + 'groupable' => 'false' + ); + + if ($req_cfg->expected_coverage_management) { + $columns[] = array( + 'title_key' => 'th_coverage', + 'width' => 60, + 'groupable' => 'false' + ); + } + + $evaluation_for_filter = array(); + foreach ($eval_status_map as $eval) { + $evaluation_for_filter[] = $eval['label']; + } + $columns[] = array( + 'title_key' => 'evaluation', + 'width' => 80, + 'groupable' => 'false', + 'filter' => 'ListSimpleMatch', + 'filterOptions' => $evaluation_for_filter + ); + $columns[] = array( + 'title_key' => 'type', + 'width' => 60, + 'groupable' => 'false', + 'filter' => 'list', + 'filterOptions' => $req_type_labels + ); + $columns[] = array( + 'title_key' => 'status', + 'width' => 60, + 'groupable' => 'false', + 'filter' => 'list', + 'filterOptions' => $status_labels + ); + + foreach ($code_status_map as $status) { + $columns[] = array( + 'title_key' => $results_cfg['status_label'][$status['status']], + 'width' => 60, + 'groupable' => 'false' + ); + } + + // complete progress + $columns[] = array( + 'title_key' => 'progress', + 'width' => 60, + 'groupable' => 'false' + ); + $columns[] = array( + 'title_key' => 'linked_tcs', + 'groupable' => 'false', + 'width' => 250, + 'type' => 'text' + ); + + // data for rows + $rows = array(); + foreach ($rspecSet as $req_spec_info) { + + // build the evaluation data string and attache it to req spec name for table group feature + $req_spec_description = buildReqSpecDescription($eval_status_map, + $req_spec_info, $req_cfg->external_req_management, $labels, + $req_spec_type_labels); + + foreach ($req_spec_info['requirements'] as $req_id => $req_info) { + $single_row = array(); + + // first column (grouped, not shown) is req spec information + $path = $req_mgr->tree_mgr->get_path($req_info['srs_id']); + foreach ($path as $key => $val) { + $path[$key] = $val['name']; + } + $path = implode("/", $path); + $single_row[] = htmlentities($path, ENT_QUOTES, $charset) . + $req_spec_description; + + // create the linked title to display + $edit_link = ' "; + + $single_row[] = $edit_link . + htmlentities($req_info['req_doc_id'], ENT_QUOTES, $charset) . + $title_sep . + htmlentities($req_info['title'], ENT_QUOTES, $charset); + + $single_row[] = "" . $req_info['version']; + + // coverage + if ($req_cfg->expected_coverage_management) { + $expected_coverage = $req_info['expected_coverage']; + $current = count($req_info['linked_testcases']); + if ($expected_coverage) { + $coverage_string = "" . $labels['na'] . + " ($current/0)"; + if ($expected_coverage) { + $percentage = 100 / $expected_coverage * $current; + $coverage_string = commentPercentage($percentage) . + " ({$current}/{$expected_coverage})"; + } + $single_row[] = $coverage_string; + } else { + // no expected value, no percentage, just absolute number + $single_row[] = $current; + } + } + + $eval = $req_info['evaluation']; + + // add the count of each evaluation + $eval_status_map[$eval]['count'] += 1; + + $single_row[] = '' . + $eval_status_map[$eval]['label'] . ''; + + $single_row[] = isset($req_type_labels[$req_info['type']]) ? $req_type_labels[$req_info['type']] : sprintf( + $labels['no_label_for_req_type'], $req_info['type']); + + $single_row[] = $status_labels[$req_info['status']]; + + // add count and percentage for each possible status and progress + $progress_percentage = 0; + + $total_count = ($req_cfg->expected_coverage_management && + $expected_coverage > 0) ? $expected_coverage : $req_info['tc_counters']['total']; + + foreach ($status_code_map as $status => $code) { + $count = isset($req_info['tc_counters'][$code]) ? $req_info['tc_counters'][$code] : 0; + $value = 0; + + if ($total_count) { + $percentage = (100 / $total_count) * $count; + $percentage_string = commentPercentage($percentage) . + " ({$count}/{$total_count})"; + + $value = $percentage_string; + + // if status is not "not run", add it to progress percentage + if ($code != $status_code_map['not_run']) { + $progress_percentage += $percentage; + } + } else { + $value = $labels['na']; + } + + $single_row[] = $value; + } + + // complete progress + $single_row[] = $total_count ? commentPercentage( + $progress_percentage) : $labels['na']; + + // show all linked tcversions incl exec result + $linked_tcs_with_status = ''; + if (! empty($req_info['linked_testcases'])) { + // ATTENTION HERE IS WHERE PLATFORMS AFFECTS + foreach ($req_info['linked_testcases'] as $ltcase) { + $tc_id = $ltcase['id']; + foreach ($testcases[$tc_id] as $pelem) { + $status = $status_code_map['not_run']; + if (isset($pelem['exec_status'])) { + $status = $pelem['exec_status']; + $status_l10n = $eval_status_map[$status]['label']; + } else { + $not_run = $allStatusCode['status_code']['not_run']; + $status_l10n = $labels['not_in_testplan']; + } + $pname = ($pelem['platform_id'] > 0 ? ($pelem['platform_name'] . + '/') : ''); + $colored_status = '' . '[' . + $pname . $status_l10n . ']'; + + $tc_name = $prefix . $ltcase['tc_external_id'] . + $title_sep . $ltcase['name'] . " v" . + $ltcase['version']; + + $exec_history_link = "" . + " "; + $edit_link = "" . + " "; + + $exec_link = ""; + + if (isset($pelem['exec_status']) && + $pelem['exec_status'] != $status_code_map['not_run']) { + $exec_link = "" . + " "; + } + + $linked_tcs_with_status .= "{$exec_history_link} {$edit_link} {$exec_link} {$colored_status} {$tc_name}
    "; + } + } + } else { + $linked_tcs_with_status = $labels['no_linked_tcs']; + } + + $single_row[] = $linked_tcs_with_status; + + $rows[] = $single_row; + } + } + + $matrix = new tlExtTable($columns, $rows, 'tl_table_results_reqs'); + $matrix->title = $gui->pageTitle; + + // group by Req Spec and hide that column + $matrix->setGroupByColumnName($labels['req_spec_short']); + + $matrix->setSortByColumnName($labels['progress']); + $matrix->sortDirection = 'DESC'; + + // show long text content in multiple lines + $matrix->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + + // define toolbar + $matrix->toolbarShowAllColumnsButton = true; + $matrix->showGroupItemsCount = false; + + $gui->tableSet = array( + $matrix + ); +} + +$gui->summary = $eval_status_map; + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Builds a descriptive string which will be added to the grouping column of the ExtJS table + * for each req spec to see information about the requirements in this spec and their status. + * + * @author Andreas Simon + * @param array $evalcode_status_map + * @param array $spec_info + * @param bool $ext_mgmt_enabled + * @param array $labels + * @param array $spec_types + * @return string description + */ +function buildReqSpecDescription(&$evalcode_status_map, &$spec_info, + $ext_mgmt_enabled, &$labels, &$spec_types) +{ + $description = ""; + $space = "    "; + + $req_count = $spec_info['req_counters']['total']; + $external_count = isset($spec_info['total_req']) ? $spec_info['total_req'] : 0; + $description .= "
    " . $space . $labels['type'] . ": " . + $spec_types[$spec_info['type']]; + + if ($ext_mgmt_enabled && $external_count) { + $description .= "
    {$space}{$labels['req_availability']}: " . + "({$req_count}/{$external_count})"; + } else { + $description .= "
    " . $space . $labels['requirements'] . ": " . + $req_count; + } + + foreach ($evalcode_status_map as $status_code => $status_info) { + $count = isset($spec_info['req_counters'][$status_code]) ? $spec_info['req_counters'][$status_code] : 0; + if ($count) { + $description .= "
    " . $space . $status_info['long_label'] . ": " . + $count; + } + } + + return $description; +} + +/** + * Get the evaluation status of a single requirement. + * + * @author Andreas Simon + * @param array $status_code + * @param array $algorithm_cfg + * @param array $counters + * @return string evaluation + */ +function evaluateReq(&$status_code, &$algorithm_cfg, &$counters) +{ + // init + $evaluation = null; + $is_fully_covered = ($counters['total'] >= $counters['expected_coverage']) ? true : false; + + if (! isset($counters[$status_code['not_run']])) { + $counters[$status_code['not_run']] = 0; + } + + $doIt = true; + if ($counters['total'] == 0) { + // Zero test cases linked to Requirement => uncovered + // Do we really display this req on report? + $evaluation = 'uncovered'; + $doIt = false; + } + + // because we can have a situation where NOT ALL test cases assigned to req + // are linked to test plan, we need to compute how many results we have + // because this figure is equal to qty of test cases linked to test plan + // ATTENTION: recheck logic when there are PLATFORMS. + + // if there are linked test cases and ALL are not run => Req. takes status 'not run' + // how many status counters are set ? + $hmc = 0; + foreach ($status_code as $code) { + $hmc += isset($counters[$code]); + } + + if ($counters['total'] > 0) { + list ($evaluation, $doIt) = doNotRunAnalysis($hmc, $counters, + $status_code['not_run']); + if (! $doIt) { + $evaluation .= ($is_fully_covered ? '' : '_nfc'); + } + } + + if ($doIt) { + $evaluation = null; + $analisysDone = false; + foreach ($algorithm_cfg['checkOrder'] as $checkKey) { + $analisysDone = true; + $doOuterBreak = false; + foreach ($algorithm_cfg['checkType'][$checkKey] as $status2check) { + $code = $status_code[$status2check]; + $count = isset($counters[$code]) ? $counters[$code] : 0; + if ($checkKey == 'atLeastOne' && $count) { + $evaluation = $is_fully_covered ? $code : $code . "_nfc"; + $doOuterBreak = true; + break; + } + + if ($checkKey == 'all' && ($count == $counters['totalTPTCV'])) { + $evaluation = $is_fully_covered ? $code : $code . "_nfc"; + $doOuterBreak = true; + break; + } + } + + if ($doOuterBreak) { + break; + } + } + + if ($analisysDone && is_null($evaluation)) { + $evaluation = 'partially_passed'; + if ($counters[$status_code['not_run']] == 0) { + $evaluation = $status_code['passed']; + } + $evaluation .= ($is_fully_covered ? '' : '_nfc'); + } + } + return $evaluation; +} + +/** + * Transform a numerical value to a string with its value as a padded html comment + * to make sorting on ExtJS table object easier + * + * @author Andreas Simon + * @param int $percentage + * @return string + */ +function commentPercentage($percentage) +{ + $percentage = round($percentage, 2); + $padded_percentage = sprintf("%010d", $percentage); + return " {$percentage}% "; +} + +/** + * initialize user input + * + * @author Andreas Simon + * @param + * resource &$tproject_mgr reference to testproject manager + * @return array $args array with user input information + */ +function initArgs(&$tproject_mgr, &$tplan_mgr, &$req_cfg) +{ + $args = new stdClass(); + + $states_to_show = array( + 0 => "0" + ); + if (isset($_REQUEST['states_to_show'])) { + $states_to_show = $_REQUEST['states_to_show']; + } elseif (isset($_SESSION['states_to_show'])) { + $states_to_show = $_SESSION['states_to_show']; + } + + $args->states_to_show = new stdClass(); + $args->states_to_show->selected = $_SESSION['states_to_show'] = $states_to_show; + + // get configured statuses and add "any" string to menu + $args->states_to_show->items = array( + 0 => "[" . lang_get('any') . "]" + ) + (array) init_labels($req_cfg->status_labels); + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + + $args->tplan_id = intval($_SESSION['resultsNavigator_testplanID']); + + $args->format = $_SESSION['resultsNavigator_format']; + + // remember platform selection too + // + $platform = 0; + $gui_open = config_get('gui_separator_open'); + $gui_close = config_get('gui_separator_close'); + + $optLTT = null; + $dummy = $tplan_mgr->platform_mgr->getLinkedToTestplanAsMap($args->tplan_id, + $optLTT); + $args->platformSet = $dummy ? array( + 0 => $gui_open . lang_get('any') . $gui_close + ) + $dummy : null; + + if (isset($_REQUEST['platform'])) { + $platform = $_REQUEST['platform']; + } elseif (isset($_SESSION['platform']) && + isset($args->platforms[$_SESSION['platform']])) { + // ATTENTION: + // This can be ONLY done if: + // the platform we remember linked to current test plan + $platform = intval($_SESSION['platform']); + } + + $args->platform = $_SESSION['platform'] = $platform; + + // $dummy = $tplan_mgr->get_builds_for_html_options($id,$active=null,$open=null,$opt=null) + $dummy = $tplan_mgr->get_builds_for_html_options($args->tplan_id, 1); // Only active builds should be available to choose + $args->buildSet = $dummy ? array( + 0 => $gui_open . lang_get('any') . $gui_close + ) + $dummy : null; + $args->build = 0; + if (isset($_REQUEST['build'])) { + $args->build = $_REQUEST['build']; + } + + return $args; +} + +/** + * initialize GUI object + * + * @author Andreas Simon + * @param stdClass $argsObj + * reference to user input + * @return stdClass $gui gui data + */ +function initGui(&$argsObj, &$tplanMgr) +{ + $gui = new stdClass(); + + $gui->pageTitle = lang_get('title_result_req_testplan'); + $gui->warning_msg = ''; + $gui->tproject_name = $argsObj->tproject_name; + $gui->states_to_show = $argsObj->states_to_show; + $gui->tableSet = null; + $gui->selected_platform = $argsObj->platform; + $gui->platforms = $argsObj->platformSet; + + $gui->builds = $argsObj->buildSet; + $gui->selected_build = $argsObj->build; + + $dummy = $tplanMgr->get_by_id($argsObj->tplan_id); + $gui->tplan_name = $dummy['name']; + + $gui->baseHref = $_SESSION['basehref']; + + return $gui; +} + +function setUpLabels($reqCfg) +{ + $dummy = config_get('req_spec_cfg'); + $rsptlbl = init_labels($dummy->type_labels); + + $rtlbl = init_labels($reqCfg->type_labels); + $slbl = init_labels($reqCfg->status_labels); + + $labels = init_labels( + array( + 'requirement' => null, + 'requirements' => null, + 'type' => null, + 'req_availability' => null, + 'linked_tcs' => null, + 'no_linked_tcs' => null, + 'goto_testspec' => null, + 'design' => null, + 'no_label_for_req_type' => null, + 'progress' => null, + 'na' => 'not_aplicable', + 'no_matching_reqs' => null, + 'execution' => null, + 'no_srs_defined' => null, + 'not_in_testplan' => null, + 'platform' => null, + 'execution_history' => null, + 'req_spec_short' => null + )); + + return array( + $rsptlbl, + $rtlbl, + $slbl, + $labels + ); +} + +/** + */ +function setUpReqStatusCfg() +{ + $results_cfg = config_get('results'); + + $status_code_map = array(); + foreach ($results_cfg['status_label_for_exec_ui'] as $status => $label) { + $status_code_map[$status] = $results_cfg['status_code'][$status]; + } + + $code_status_map = array_flip($status_code_map); + foreach ($code_status_map as $code => $status) { + $code_status_map[$code] = array( + 'label' => lang_get($results_cfg['status_label'][$status]), + 'long_label' => lang_get("req_title_" . $status), + 'status' => $status, + 'css_class' => $status . '_text' + ); + } + + $eva = $code_status_map; + + // add additional states for requirement evaluation + $evalbl = init_labels( + array( + 'partially_passed' => null, + 'partially_passed_reqs' => null, + 'uncovered' => null, + 'uncovered_reqs' => null, + 'passed_nfc' => null, + 'passed_nfc_reqs' => null, + 'failed_nfc' => null, + 'failed_nfc_reqs' => null, + 'blocked_nfc' => null, + 'blocked_nfc_reqs' => null, + 'not_run_nfc' => null, + 'not_run_nfc_reqs' => null, + 'passed' => null, + 'partially_passed_nfc' => null, + 'partially_passed_nfc_reqs' => null + )); + + $eva['partially_passed'] = array( + 'label' => $evalbl['partially_passed'], + 'long_label' => $evalbl['partially_passed_reqs'], + 'css_class' => 'passed_text' + ); + + $eva['uncovered'] = array( + 'label' => $evalbl['uncovered'], + 'long_label' => $evalbl['uncovered_reqs'], + 'css_class' => 'not_run_text' + ); + + $eva['p_nfc'] = array( + 'label' => $evalbl['passed_nfc'], + 'long_label' => $evalbl['passed_nfc_reqs'], + 'css_class' => 'passed_text' + ); + + $eva['f_nfc'] = array( + 'label' => $evalbl['failed_nfc'], + 'long_label' => $evalbl['failed_nfc_reqs'], + 'css_class' => 'failed_text' + ); + + $eva['b_nfc'] = array( + 'label' => $evalbl['blocked_nfc'], + 'long_label' => $evalbl['blocked_nfc_reqs'], + 'css_class' => 'blocked_text' + ); + + $eva['n_nfc'] = array( + 'label' => $evalbl['not_run_nfc'], + 'long_label' => $evalbl['not_run_nfc_reqs'], + 'css_class' => 'not_run_text' + ); + + $eva['partially_passed_nfc'] = array( + 'label' => $evalbl['partially_passed_nfc'], + 'long_label' => $evalbl['partially_passed_nfc_reqs'], + 'css_class' => 'passed_text' + ); + + // add count for each status to show test progress + foreach ($eva as $key => $status) { + $eva[$key]['count'] = 0; + } + + return array( + $results_cfg, + $status_code_map, + $code_status_map, + $eva + ); +} + +/** + */ +function buildReqSpecMap($reqSet, &$reqMgr, &$reqSpecMgr, &$tplanMgr, + $reqStatusFilter, &$argsObj) +{ + $rspec = array(); + $total = 0; + $tc_ids = array(); + + $coverageContext = null; + if ($argsObj->platform != 0) { + $coverageContext['tplan_id'] = $argsObj->tplan_id; + $coverageContext['platform_id'] = $argsObj->platform; + } + + // Test case linked to test plan + // $itemsInTestPlan = $tplanMgr->get_linked_items_id($argsObj->tplan_id); + $itemsInTestPlan = $tplanMgr->getLinkedItems($argsObj->tplan_id); + foreach ($reqSet as $id) { + // get the information for this requirement + $req = $reqMgr->get_by_id($id, requirement_mgr::LATEST_VERSION); + $req = $req[0]; + + // if req is "usable" (has one of the selected states) add it + if (in_array($req['status'], $reqStatusFilter, true) || + in_array("0", $reqStatusFilter, true)) { + + // some sort of Caching + if (! isset($rspec[$req['srs_id']])) { + $rspec[$req['srs_id']] = $reqSpecMgr->get_by_id($req['srs_id']); + $rspec[$req['srs_id']]['requirements'] = array(); + } + + $req['linked_testcases'] = (array) $reqMgr->getActiveForReqVersion( + $req['version_id']); + + // Exclude obsolete TC or TC not linked to test plan under analysis + foreach ($req['linked_testcases'] as $itemID => $dummy) { + if ($dummy['is_obsolete'] == "1" || + ! isset($itemsInTestPlan[$dummy['id']])) { + unset($req['linked_testcases'][$itemID]); + } + } + + // if there is linked (active) test case + if (count($req['linked_testcases']) > 0) { + $total ++; + $rspec[$req['srs_id']]['requirements'][$id] = $req; + + foreach ($req['linked_testcases'] as $tc) { + $tc_ids[] = $tc['id']; + } + } + } + } + + // Get test case data from test case version LINKED TO TEST PLAN, + // using as FILTER test cases ASSIGNED (linked) TO requirements + // + // ATTENTION: + // What can happens is this + // Test spec has TC1,TC2,TC3,TC4 + // REQ 1 has TC1,TC2.TC3 assigned + // TEST PLAN A has only TC1, TC2 + // + // It will be impossibile to provide a CLEAR INDICATION of + // relation between REQ status and Test case exec status, because + // TC3 is NOT PART OF TEST PLAN under analisys + // + $tcaseSet = array(); + if (count($tc_ids)) { + $filters = array( + 'tcase_id' => $tc_ids + ); + $f2a = array( + 'platform', + 'build' + ); + foreach ($f2a as $fk) { + if ($argsObj->$fk != 0) { + $filters[$fk . '_id'] = $argsObj->$fk; + } + } + + $filterOnly = array(); + $filterOnly['platform_id'] = isset($filters['platform_id']) && + ! isset($filters['build_id']); + $filterOnly['build_id'] = ! isset($filters['platform_id']) && + isset($filters['build_id']); + $noFilter = ! isset($filters['platform_id']) && + ! isset($filters['build_id']); + $allFilters = isset($filters['platform_id']) && + isset($filters['build_id']); + + $options = array( + 'addExecInfo' => true, + 'accessKeyType' => 'tcase+platform', + 'build_is_active' => true + ); + + if ($noFilter || $filterOnly['platform_id']) { + $tcaseSet = $tplanMgr->getLTCVOnTestPlanPlatform($argsObj->tplan_id, + $filters, $options); + } elseif ($allFilters || $filterOnly['build_id']) { + $tcaseSet = $tplanMgr->getLTCVNewGeneration($argsObj->tplan_id, + $filters, $options); + } + } + + return array( + $total, + $rspec, + $tcaseSet + ); +} + +/** + */ +function doNotRunAnalysis($tcaseQty, $execStatusCounter, $notRunCode) +{ + $evaluation = null; + $doIt = true; + + if ($tcaseQty == 1) { + if ($execStatusCounter[$notRunCode] != 0) { + $evaluation = $notRunCode; + $doIt = false; + } + } else { + if ($execStatusCounter['totalTPTCV'] == $execStatusCounter[$notRunCode]) { + $evaluation = $notRunCode; + $doIt = false; + } + } + return array( + $evaluation, + $doIt + ); +} + +/** + * Check if the user has the needed rights to view this page (testplan metrics). + * + * @author Andreas Simon + * @param Database $db + * reference to database object + * @param tlUser $user + * reference to user object + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_metrics'); } -else -{ - $gui->warning_msg = $labels['no_srs_defined']; -} - - -// second step: walk through req spec map, count/calculate, store results -if(count($rspecSet)) -{ - - foreach ($rspecSet as $rspec_id => $req_spec_info) - { - $rspecSet[$rspec_id]['req_counters'] = array('total' => 0); - foreach ($req_spec_info['requirements'] as $req_id => $req_info) - { - // Test Plan Test Case Version (TPTCV) - $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'] = array('total' => 0,'totalTPTCV' => 0); - - // add coverage for more detailed evaluation - $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']['expected_coverage'] = - $rspecSet[$rspec_id]['requirements'][$req_id]['expected_coverage']; - - foreach ($req_info['linked_testcases'] as $key => $tc_info) - { - $tc_id = $tc_info['id']; - $plat2loop = array_keys($testcases[$tc_id]); - $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']['total']++; - - foreach($plat2loop as $plat_id) - { - $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']['totalTPTCV']++; - if (isset($testcases[$tc_id][$plat_id]['exec_status'])) - { - $status = $testcases[$tc_id][$plat_id]['exec_status']; - - // if the counters for this status don't exist yet, initialize them with 0 - if (!isset($rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'][$status])) - { - $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'][$status] = 0; - } - - $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters'][$status] ++; - } - } - } - - // evaluate this requirement by configured coverage algorithm - $eval = evaluate_req($status_code_map, $req_cfg->coverageStatusAlgorithm, - $rspecSet[$rspec_id]['requirements'][$req_id]['tc_counters']); - - $rspecSet[$rspec_id]['requirements'][$req_id]['evaluation'] = $eval; - - if (!isset($rspecSet[$rspec_id]['req_counters'][$eval])) - { - $rspecSet[$rspec_id]['req_counters'][$eval] = 0; - } - - $rspecSet[$rspec_id]['req_counters'][$eval] ++; - $rspecSet[$rspec_id]['req_counters']['total'] ++; - } - } -} - -// last step: build the table -if (count($rspecSet)) -{ - $allStatusCode = config_get('results'); - - // headers - $columns = array(); - $columns[] = array('title_key' => 'req_spec_short', - 'groupable' => 'true', 'hideable' => 'false', 'hidden' => 'true'); - $columns[] = array('title_key' => 'title', 'width' => 100, - 'groupable' => 'false', 'type' => 'text'); - $columns[] = array('title_key' => 'version', 'width' => 20, 'groupable' => 'false'); - - if ($req_cfg->expected_coverage_management) - { - $columns[] = array('title_key' => 'th_coverage', 'width' => 60, 'groupable' => 'false'); - } - - $evaluation_for_filter = array(); - foreach($eval_status_map as $eval) - { - $evaluation_for_filter[] = $eval['label']; - } - $columns[] = array('title_key' => 'evaluation', 'width' => 80, 'groupable' => 'false', - 'filter' => 'ListSimpleMatch', 'filterOptions' => $evaluation_for_filter); - $columns[] = array('title_key' => 'type', 'width' => 60, 'groupable' => 'false', - 'filter' => 'list', 'filterOptions' => $req_type_labels); - $columns[] = array('title_key' => 'status', 'width' => 60, 'groupable' => 'false', - 'filter' => 'list', 'filterOptions' => $status_labels); - - foreach ($code_status_map as $status) - { - $columns[] = array('title_key' => $results_cfg['status_label'][$status['status']], - 'width' => 60, 'groupable' => 'false'); - } - - // complete progress - $columns[] = array('title_key' => 'progress', 'width' => 60, 'groupable' => 'false'); - $columns[] = array('title_key' => 'linked_tcs', 'groupable' => 'false', 'width' => 250, 'type' => 'text'); - - // data for rows - $rows = array(); - foreach ($rspecSet as $rspec_id => $req_spec_info) - { - - // build the evaluation data string and attache it to req spec name for table group feature - $req_spec_description = build_req_spec_description($eval_status_map, $req_spec_info, - $req_cfg->external_req_management, $labels, - $req_spec_type_labels); - - - foreach ($req_spec_info['requirements'] as $req_id => $req_info) - { - $single_row = array(); - - // first column (grouped, not shown) is req spec information - $path = $req_mgr->tree_mgr->get_path($req_info['srs_id']); - foreach ($path as $key => $val) - { - $path[$key] = $val['name']; - } - $path = implode("/", $path); - $single_row[] = htmlentities($path, ENT_QUOTES, $charset) . $req_spec_description; - - // create the linked title to display - $edit_link = ' "; - - $single_row[] = $edit_link . - htmlentities($req_info['req_doc_id'], ENT_QUOTES, $charset) . $title_sep . - htmlentities($req_info['title'], ENT_QUOTES, $charset); - - $single_row[] = "" . $req_info['version']; - - // coverage - if ($req_cfg->expected_coverage_management) - { - $expected_coverage = $req_info['expected_coverage']; - $current = count($req_info['linked_testcases']); - if ($expected_coverage) - { - $coverage_string = "" . $labels['na'] . " ($current/0)"; - if ($expected_coverage) - { - $percentage = 100 / $expected_coverage * $current; - $coverage_string = comment_percentage($percentage) . - " ({$current}/{$expected_coverage})"; - } - $single_row[] = $coverage_string; - } - else - { - // no expected value, no percentage, just absolute number - $single_row[] = $current; - } - } - - $eval = $req_info['evaluation']; - - // add the count of each evaluation - $eval_status_map[$eval]['count'] += 1; - - $single_row[] = '' . - $eval_status_map[$eval]['label'] . ''; - - $single_row[] = isset($req_type_labels[$req_info['type']]) ? $req_type_labels[$req_info['type']] : - sprintf($labels['no_label_for_req_type'],$req_info['type']); - - $single_row[] = $status_labels[$req_info['status']]; - - // add count and percentage for each possible status and progress - $progress_percentage = 0; - - $total_count = ($req_cfg->expected_coverage_management && $expected_coverage > 0) ? - $expected_coverage : $req_info['tc_counters']['total']; - - foreach ($status_code_map as $status => $code) - { - $count = isset($req_info['tc_counters'][$code]) ? $req_info['tc_counters'][$code] : 0; - $value = 0; - - if ($total_count) - { - $percentage = (100 / $total_count) * $count; - $percentage_string = comment_percentage($percentage) . " ({$count}/{$total_count})"; - - $value = $percentage_string; - - // if status is not "not run", add it to progress percentage - if ($code != $status_code_map['not_run']) - { - $progress_percentage += $percentage; - } - } - else - { - $value = $labels['na']; - } - - $single_row[] = $value; - } - - // complete progress - $single_row[] = $total_count ? comment_percentage($progress_percentage) : $labels['na']; - - // show all linked tcversions incl exec result - $linked_tcs_with_status = ''; - if (count($req_info['linked_testcases']) > 0 ) - { - // ATTENTION HERE IS WHERE PLATFORMS AFFECTS - foreach($req_info['linked_testcases'] as $ltcase) - { - $tc_id = $ltcase['id']; - foreach($testcases[$tc_id] as $pelem) - { - $status = $status_code_map['not_run']; - if(isset($pelem['exec_status'])) - { - $status = $pelem['exec_status']; - $status_l10n = $eval_status_map[$status]['label']; - } - else - { - $not_run = $allStatusCode['status_code']['not_run']; - $status_l10n = $labels['not_in_testplan']; - } - $pname = ($pelem['platform_id'] > 0 ? ($pelem['platform_name'] . '/') : '' ); - $colored_status = '' . - '[' . $pname . $status_l10n . ']'; - - - $tc_name = $prefix . $ltcase['tc_external_id'] . $title_sep . $ltcase['name']; - - $exec_history_link = "" . - " "; - $edit_link = "" . - " "; - - $exec_link = ""; - - - if(isset($pelem['exec_status']) && $pelem['exec_status'] != $status_code_map['not_run']) - { - $exec_link = "" . - " "; - } - - $linked_tcs_with_status .= "{$exec_history_link} {$edit_link} {$exec_link} {$colored_status} {$tc_name}
    "; - } - - } - } - else - { - $linked_tcs_with_status = $labels['no_linked_tcs']; - } - - $single_row[] = $linked_tcs_with_status; - - $rows[] = $single_row; - } - } - - $matrix = new tlExtTable($columns, $rows, 'tl_table_results_reqs'); - $matrix->title = $gui->pageTitle; - - // group by Req Spec and hide that column - $matrix->setGroupByColumnName($labels['req_spec_short']); - - $matrix->setSortByColumnName($labels['progress']); - $matrix->sortDirection = 'DESC'; - - // show long text content in multiple lines - $matrix->addCustomBehaviour('text', array('render' => 'columnWrap')); - - // define toolbar - $matrix->toolbarShowAllColumnsButton = true; - $matrix->showGroupItemsCount = false; - - $gui->tableSet = array($matrix); -} - -$gui->summary = $eval_status_map; - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * Builds a descriptive string which will be added to the grouping column of the ExtJS table - * for each req spec to see information about the requirements in this spec and their status. - * - * @author Andreas Simon - * @param array $evalcode_status_map - * @param array $spec_info - * @param bool $ext_mgmt_enabled - * @param array $labels - * @param array $spec_types - * @return string description - */ -function build_req_spec_description(&$evalcode_status_map, &$spec_info, $ext_mgmt_enabled, - &$labels, &$spec_types) -{ - - $description = ""; - $space = "    "; - - $req_count = $spec_info['req_counters']['total']; - $external_count = isset($spec_info['total_req']) ? $spec_info['total_req'] : 0; - $description .= "
    " . $space . $labels['type'] . ": " . $spec_types[$spec_info['type']]; - - if ($ext_mgmt_enabled && $external_count) - { - $description .= "
    {$space}{$labels['req_availability']}: " . - "({$req_count}/{$external_count})"; - } - else - { - $description .= "
    " . $space . $labels['requirements'] . ": " . $req_count; - } - - foreach ($evalcode_status_map as $status_code => $status_info) - { - $count = isset($spec_info['req_counters'][$status_code]) ? - $spec_info['req_counters'][$status_code] : 0; - if ($count) - { - $description .= "
    " . $space . $status_info['long_label'] . ": " . $count; - } - } - - return $description; -} - - -/** - * Get the evaluation status of a single requirement. - * - * @author Andreas Simon - * @param array $status_code - * @param array $algorithm_cfg - * @param array $counters - * @return string evaluation - */ -function evaluate_req(&$status_code, &$algorithm_cfg, &$counters) -{ - // init - $evaluation = null; - $is_fully_covered = ($counters['total'] >= $counters['expected_coverage']) ? true : false; - - if( !isset($counters[$status_code['not_run']]) ) - { - $counters[$status_code['not_run']] = 0; - } - - $doIt = true; - if ($counters['total'] == 0) - { - // Zero test cases linked to Requirement => uncovered - // Do we really display this req on report? - $evaluation = 'uncovered'; - $doIt = false; - } - - - // because we can have a situation where NOT ALL test cases assigned to req - // are linked to test plan, we need to compute how many results we have - // because this figure is equal to qty of test cases linked to test plan - // ATTENTION: recheck logic when there are PLATFORMS. - - // if there are linked test cases and ALL are not run => Req. takes status 'not run' - // how many status counters are set ? - $hmc = 0; - foreach($status_code as $verbose => $code) - { - $hmc += isset($counters[$code]); - } - - if( ($counters['total'] > 0) ) - { - list($evaluation,$doIt) = doNotRunAnalysis($hmc,$counters,$status_code['not_run']); - if(!$doIt) - { - $evaluation .= ($is_fully_covered ? '' : '_nfc'); - } - } - - if($doIt) - { - $evaluation = null; - $analisysDone = false; - foreach($algorithm_cfg['checkOrder'] as $checkKey) - { - $analisysDone = true; - $doOuterBreak = false; - foreach($algorithm_cfg['checkType'][$checkKey] as $status2check) - { - $code = $status_code[$status2check]; - $count = isset($counters[$code]) ? $counters[$code] : 0; - if ($checkKey == 'atLeastOne' && $count) - { - $evaluation = $is_fully_covered ? $code : $code . "_nfc"; - $doOuterBreak = true; - break; - } - - if($checkKey == 'all' && ($count == $counters['totalTPTCV']) ) - { - $evaluation = $is_fully_covered ? $code : $code . "_nfc"; - $doOuterBreak = true; - break; - } - } - - if($doOuterBreak) - { - break; - } - } - - if($analisysDone && is_null($evaluation)) - { - $evaluation = 'partially_passed'; - if($counters[$status_code['not_run']] == 0) - { - $evaluation = $status_code['passed']; - } - $evaluation .= ($is_fully_covered ? '' : '_nfc'); - } - } - return $evaluation; -} - - -/** - * Transform a numerical value to a string with its value as a padded html comment - * to make sorting on ExtJS table object easier - * - * @author Andreas Simon - * @param int $percentage - * @return string - */ -function comment_percentage($percentage) -{ - $percentage = round($percentage, 2); - $padded_percentage = sprintf("%010d", $percentage); - $string = " {$percentage}% "; - return $string; -} - - -/** - * initialize user input - * - * @author Andreas Simon - * @param resource &$tproject_mgr reference to testproject manager - * @return array $args array with user input information - */ -function init_args(&$tproject_mgr, &$tplan_mgr, &$req_cfg) -{ - $args = new stdClass(); - - $states_to_show = array(0 => "0"); - if (isset($_REQUEST['states_to_show'])) - { - $states_to_show = $_REQUEST['states_to_show']; - } - else if (isset($_SESSION['states_to_show'])) - { - $states_to_show = $_SESSION['states_to_show']; - } - - $args->states_to_show = new stdClass(); - $args->states_to_show->selected = $_SESSION['states_to_show'] = $states_to_show; - - // get configured statuses and add "any" string to menu - $args->states_to_show->items = array(0 => "[" . lang_get('any') . "]") + - (array) init_labels($req_cfg->status_labels); - - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - - $args->tplan_id = intval($_SESSION['resultsNavigator_testplanID']); - - $args->format = $_SESSION['resultsNavigator_format']; - - // remember platform selection too - // - $platform = 0; - $gui_open = config_get('gui_separator_open'); - $gui_close = config_get('gui_separator_close'); - - $optLTT = null; - $dummy = $tplan_mgr->platform_mgr->getLinkedToTestplanAsMap($args->tplan_id,$optLTT); - $args->platformSet = $dummy ? array(0 => $gui_open . lang_get('any') . $gui_close) + $dummy : null; - - if (isset($_REQUEST['platform'])) - { - $platform = $_REQUEST['platform']; - } - else if ( isset($_SESSION['platform']) && isset($args->platforms[$_SESSION['platform']]) ) - { - // ATTENTION: - // This can be ONLY done if: - // the platform we remember linked to current test plan - $platform = intval($_SESSION['platform']); - } - - $args->platform = $_SESSION['platform'] = $platform; - - - // $dummy = $tplan_mgr->get_builds_for_html_options($id,$active=null,$open=null,$opt=null) - $dummy = $tplan_mgr->get_builds_for_html_options($args->tplan_id, 1); //Only active builds should be available to choose - $args->buildSet = $dummy ? array(0 => $gui_open . lang_get('any') . $gui_close) + $dummy : null; - $args->build = 0; - if (isset($_REQUEST['build'])) - { - $args->build = $_REQUEST['build']; - } - - return $args; -} - - -/** - * initialize GUI object - * - * @author Andreas Simon - * @param stdClass $argsObj reference to user input - * @return stdClass $gui gui data - */ -function init_gui(&$argsObj,&$tplanMgr) -{ - $gui = new stdClass(); - - $gui->pageTitle = lang_get('title_result_req_testplan'); - $gui->warning_msg = ''; - $gui->tproject_name = $argsObj->tproject_name; - $gui->states_to_show = $argsObj->states_to_show; - $gui->tableSet = null; - $gui->selected_platform = $argsObj->platform; - $gui->platforms = $argsObj->platformSet; - - $gui->builds = $argsObj->buildSet; - $gui->selected_build = $argsObj->build; - - $dummy = $tplanMgr->get_by_id($argsObj->tplan_id); - $gui->tplan_name = $dummy['name']; - - $gui->baseHref = $_SESSION['basehref']; - - return $gui; -} - - -function setUpLabels($reqCfg) -{ - $dummy = config_get('req_spec_cfg'); - $rsptlbl = init_labels($dummy->type_labels); - - $rtlbl = init_labels($reqCfg->type_labels); - $slbl = init_labels($reqCfg->status_labels); - - $labels = init_labels( array('requirement' => null,'requirements' => null, - 'type' => null,'req_availability' => null, - 'linked_tcs' => null,'no_linked_tcs' => null, - 'goto_testspec' => null,'design' => null, - 'no_label_for_req_type' => null, 'progress' => null, - 'na' => 'not_aplicable', 'no_matching_reqs' => null, - 'execution' => null,'no_srs_defined' => null, - 'not_in_testplan' => null,'platform' => null, - 'execution_history' => null, 'req_spec_short' => null)); - - return array($rsptlbl,$rtlbl,$slbl,$labels); -} - -/** - * - */ -function setUpReqStatusCfg() -{ - $results_cfg = config_get('results'); - - $status_code_map = array(); - foreach ($results_cfg['status_label_for_exec_ui'] as $status => $label) - { - $status_code_map[$status] = $results_cfg['status_code'][$status]; - } - - $code_status_map = array_flip($status_code_map); - foreach ($code_status_map as $code => $status) - { - $code_status_map[$code] = array('label' => lang_get($results_cfg['status_label'][$status]), - 'long_label' => lang_get("req_title_" . $status), - 'status' => $status, - 'css_class' => $status . '_text'); - } - - - $eva = $code_status_map; - - // add additional states for requirement evaluation - $evalbl = init_labels(array('partially_passed' => null, 'partially_passed_reqs' => null, - 'uncovered' => null, 'uncovered_reqs' => null, - 'passed_nfc' => null, 'passed_nfc_reqs' => null, - 'failed_nfc' => null, 'failed_nfc_reqs' => null, - 'blocked_nfc' => null, 'blocked_nfc_reqs' => null, - 'not_run_nfc' => null, 'not_run_nfc_reqs' => null, - 'passed' => null, - 'partially_passed_nfc' => null, 'partially_passed_nfc_reqs' => null)); - - $eva['partially_passed'] = array('label' => $evalbl['partially_passed'], - 'long_label' => $evalbl['partially_passed_reqs'], - 'css_class' => 'passed_text'); - - $eva['uncovered'] = array('label' => $evalbl['uncovered'], - 'long_label' => $evalbl['uncovered_reqs'], - 'css_class' => 'not_run_text'); - - $eva['p_nfc'] = array('label' => $evalbl['passed_nfc'], - 'long_label' => $evalbl['passed_nfc_reqs'], - 'css_class' => 'passed_text'); - - $eva['f_nfc'] = array('label' => $evalbl['failed_nfc'], - 'long_label' => $evalbl['failed_nfc_reqs'], - 'css_class' => 'failed_text'); - - $eva['b_nfc'] = array('label' => $evalbl['blocked_nfc'], - 'long_label' => $evalbl['blocked_nfc_reqs'], - 'css_class' => 'blocked_text'); - - $eva['n_nfc'] = array('label' => $evalbl['not_run_nfc'], - 'long_label' => $evalbl['not_run_nfc_reqs'], - 'css_class' => 'not_run_text'); - - $eva['partially_passed_nfc'] = array('label' => $evalbl['partially_passed_nfc'], - 'long_label' => $evalbl['partially_passed_nfc_reqs'], - 'css_class' => 'passed_text'); - - - // add count for each status to show test progress - foreach ($eva as $key => $status) - { - $eva[$key]['count'] = 0; - } - - return array($results_cfg,$status_code_map,$code_status_map,$eva); -} - - -/** - * - */ -function buildReqSpecMap($reqSet,&$reqMgr,&$reqSpecMgr,&$tplanMgr,$reqStatusFilter,&$argsObj) -{ - $rspec = array(); - $total = 0; - $tc_ids = array(); - - $coverageContext = null; - if ($argsObj->platform != 0) - { - $coverageContext['tplan_id'] = $argsObj->tplan_id; - $coverageContext['platform_id'] = $argsObj->platform; - } - - // Test case linked to test plan - // $itemsInTestPlan = $tplanMgr->get_linked_items_id($argsObj->tplan_id); - $itemsInTestPlan = $tplanMgr->getLinkedItems($argsObj->tplan_id); - foreach($reqSet as $id) - { - // get the information for this requirement - $req = $reqMgr->get_by_id($id, requirement_mgr::LATEST_VERSION); - $req = $req[0]; - - // if req is "usable" (has one of the selected states) add it - if( in_array($req['status'], $reqStatusFilter, true) || - in_array("0", $reqStatusFilter, true) ) - { - $total++; - - // some sort of Caching - if (!isset($rspec[$req['srs_id']])) - { - $rspec[$req['srs_id']] = $reqSpecMgr->get_by_id($req['srs_id']); - $rspec[$req['srs_id']]['requirements'] = array(); - } - - $req['linked_testcases'] = (array)$reqMgr->get_coverage($id,$coverageContext,array('accessKey' => 'tcase_id')); - - // Now loop to mark test cases ASSIGNED to requirements as LINKED OR NOT to Test plan under analisys. - foreach($req['linked_testcases'] as $itemID => $dummy) - { - $req['linked_testcases'][$itemID]['in_testplan'] = isset($itemsInTestPlan[$itemID]); - } - - $rspec[$req['srs_id']]['requirements'][$id] = $req; - - foreach ($req['linked_testcases'] as $tc) - { - $tc_ids[] = $tc['id']; - } - } - } - - // Get test case data from test case version LINKED TO TEST PLAN, - // using as FILTER test cases ASSIGNED (linked) TO requirements - // - // ATTENTION: - // What can happens is this - // Test spec has TC1,TC2,TC3,TC4 - // REQ 1 has TC1,TC2.TC3 assigned - // TEST PLAN A has only TC1, TC2 - // - // It will be impossibile to provide a CLEAR INDICATION of - // relation between REQ status and Test case exec status, because - // TC3 is NOT PART OF TEST PLAN under analisys - // - $tcaseSet = array(); - if (count($tc_ids)) - { - $filters = array('tcase_id' => $tc_ids); - $f2a = array('platform','build'); - foreach($f2a as $fk) - { - if ($argsObj->$fk != 0) - { - $filters[$fk . '_id'] = $argsObj->$fk; - } - - } - - $filterOnly = array(); - $filterOnly['platform_id'] = isset($filters['platform_id']) && !isset($filters['build_id']); - $filterOnly['build_id'] = !isset($filters['platform_id']) && isset($filters['build_id']); - $noFilter = !isset($filters['platform_id']) && !isset($filters['build_id']); - $allFilters = isset($filters['platform_id']) && isset($filters['build_id']); - - // $options = array('addExecInfo' => true,'accessKeyType' => 'tcase'); - $options = array('addExecInfo' => true,'accessKeyType' => 'tcase+platform', 'build_is_active' => true); - - if($noFilter || $filterOnly['platform_id']) - { - $tcaseSet = $tplanMgr->getLTCVOnTestPlanPlatform($argsObj->tplan_id, $filters, $options); - } - else if ($allFilters || $filterOnly['build_id']) - { - $tcaseSet = $tplanMgr->getLTCVNewGeneration($argsObj->tplan_id, $filters, $options); - } - } - - return array($total,$rspec,$tcaseSet); -} - -/** - * - */ -function doNotRunAnalysis($tcaseQty,$execStatusCounter,$notRunCode) -{ - $evaluation = null; - $doIt = true; - - if($tcaseQty == 1) - { - if( $execStatusCounter[$notRunCode] != 0 ) - { - $evaluation = $notRunCode; - $doIt = false; - } - } - else - { - if(($execStatusCounter['totalTPTCV'] == $execStatusCounter[$notRunCode])) - { - $evaluation = $notRunCode; - $doIt = false; - } - } - return array($evaluation,$doIt); -} - - - -/** - * Check if the user has the needed rights to view this page (testplan metrics). - * - * @author Andreas Simon - * @param Database $db reference to database object - * @param tlUser $user reference to user object - */ -function checkRights(&$db, &$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file diff --git a/lib/results/resultsTC.php b/lib/results/resultsTC.php index 4a1aee1c47..266e5c9009 100644 --- a/lib/results/resultsTC.php +++ b/lib/results/resultsTC.php @@ -1,791 +1,882 @@ - -* -* Test Results Matrix -* -*/ -require('../../config.inc.php'); - -// Must be included BEFORE common.php -require_once('../../third_party/codeplex/PHPExcel.php'); - -require_once('common.php'); -require_once('displayMgr.php'); -require_once('exttable.class.php'); - -$timerOn = microtime(true); // will be used to compute elapsed time -$templateCfg = templateConfiguration(); - -$smarty = new TLSmarty; - -list($tplan_mgr,$args) = initArgsForReports($db); -$metricsMgr = new tlTestPlanMetrics($db); -$tplan_mgr = &$metricsMgr; - -list($gui,$tproject_info,$labels,$cfg) = initializeGui($db,$args,$smarty->getImages(),$tplan_mgr); -$args->cfg = $cfg; - -$renderHTML = true; - -// Because we will try to send via email xls, we need to be careful -// with logic regarding args->format. -// may be we need to add in logic the media => email, download, etc -// -// We have faced a performance block due to an environment with -// 700 Builds and 1300 Test Cases on Test Plan -// This created a block on NOT RUN QUERY, but anyway will produce an enormous and -// unmanageable matrix on screen -// -// New way to process: -// ACTIVE Build Qty > 20 => Ask user to select builds he/she wants to use -// Cell Qty = (ACTIVE Build Qty x Test Cases on Test plan) > 2000 => said user I'm sorry -// - -setUpBuilds($args,$gui); -$buildSet = array('buildSet' => $args->builds->idSet); - -if( ($gui->activeBuildsQty <= $gui->matrixCfg->buildQtyLimit) || - ($args->do_action == 'result' && count($args->builds->idSet) <= $gui->matrixCfg->buildQtyLimit) ) -{ - //setUpBuilds($args,$gui); - - $tpl = $templateCfg->default_template; - //$buildSet = array('buildSet' => $args->builds->idSet); - - $opt = array('getExecutionNotes' => true); - if($args->format == FORMAT_XLS) { - $opt = array('getExecutionNotes' => true, 'getTester' => true, - 'getUserAssignment' => true, - 'getExecutionTimestamp' => true, 'getExecutionDuration' => true); - } - $execStatus = $metricsMgr->getExecStatusMatrix($args->tplan_id,$buildSet,$opt); - $metrics = $execStatus['metrics']; - $latestExecution = $execStatus['latestExec']; - - // Every Test suite a row on matrix to display will be created - // One matrix will be created for every platform that has testcases - $args->cols = initCols($gui->show_platforms); - if( !is_null($execStatus['metrics']) ) - { - buildDataSet($db,$args,$gui,$execStatus,$labels); - } - - $renderHTML = false; - - switch($args->format) { - case FORMAT_XLS: - createSpreadsheet($gui,$args,$args->getSpreadsheetBy); - break; - - default: - $renderHTML = true; - $gui->tableSet[] = buildMatrix($gui, $args); - break; - } -} else { - // We need to ask user to do a choice - $tpl = 'resultsTCLauncher.tpl'; - $gui->url2call = "lib/results/resultsTC.php?tplan_id=$gui->tplan_id" . - "&tproject_id=$gui->tproject_id&do_action=result"; - - $gui->pageTitle = $labels['test_result_matrix_filters']; - if($gui->matrixCfg->buildQtyLimit > 0) { - $gui->userFeedback = $labels['too_much_data'] . '
    ' . - sprintf($labels['too_much_builds'],$gui->activeBuildsQty,$gui->matrixCfg->buildQtyLimit); - } -} - - -$timerOff = microtime(true); -$gui->elapsed_time = round($timerOff - $timerOn,2); - -$smarty->assign('gui',$gui); -displayReport($templateCfg->template_dir . $tpl, $smarty, $args->format, - $gui->mailCfg,$renderHTML); - - -/** - * - * - */ -function checkRights(&$db,&$user,$context = null) -{ - if (is_null($context)) { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; + + * + * Test Results Matrix + * + */ +require_once '../../config.inc.php'; +require_once '../../third_party/codeplex/PHPExcel.php'; // Must be included BEFORE common.php +require_once 'common.php'; +require_once 'displayMgr.php'; +require_once 'exttable.class.php'; + +$timerOn = microtime(true); // will be used to compute elapsed time +$templateCfg = templateConfiguration(); + +$smarty = new TLSmarty(); + +list ($tplan_mgr, $args) = initArgsForReports($db); +$metricsMgr = new tlTestPlanMetrics($db); +$tplan_mgr = &$metricsMgr; + +list ($gui, $tproject_info, $labels, $cfg) = initializeGui($db, $args, + $smarty->getImages(), $tplan_mgr); +$args->cfg = $cfg; + +$renderHTML = true; + +// Because we will try to send via email xls, we need to be careful +// with logic regarding args->format. +// may be we need to add in logic the media => email, download, etc +// +// We have faced a performance block due to an environment with +// 700 Builds and 1300 Test Cases on Test Plan +// This created a block on NOT RUN QUERY, but anyway will produce an enormous and +// unmanageable matrix on screen +// +// New way to process: +// ACTIVE Build Qty > 20 => Ask user to select builds he/she wants to use +// Cell Qty = (ACTIVE Build Qty x Test Cases on Test plan) > 2000 => said user I'm sorry +// + +setUpBuilds($args, $gui); +$buildSet = array( + 'buildSet' => $args->builds->idSet +); + +if (($gui->activeBuildsQty <= $gui->matrixCfg->buildQtyLimit) || + ($args->doAction == 'result' && + count($args->builds->idSet) <= $gui->matrixCfg->buildQtyLimit)) { + + $tpl = $templateCfg->default_template; + + $opt = array( + 'getExecutionNotes' => true + ); + if ($args->format == FORMAT_XLS) { + $opt = array( + 'getExecutionNotes' => true, + 'getTester' => true, + 'getUserAssignment' => true, + 'getExecutionTimestamp' => true, + 'getExecutionDuration' => true + ); + } + $execStatus = $metricsMgr->getExecStatusMatrix($args->tplan_id, $buildSet, + $opt); + + $metrics = $execStatus['metrics']; + $latestExecution = $execStatus['latestExec']; + + // Every Test suite a row on matrix to display will be created + // One matrix will be created for every platform that has testcases + $args->cols = initCols($gui->show_platforms); + if (! is_null($execStatus['metrics'])) { + buildDataSet($db, $args, $gui, $execStatus, $labels); + } + + $renderHTML = false; + + switch ($args->format) { + case FORMAT_XLS: + createSpreadsheet($gui, $args, $args->getSpreadsheetBy); + break; + + default: + $renderHTML = true; + $gui->tableSet[] = buildMatrix($gui, $args); + break; + } +} else { + // We need to ask user to do a choice + $tpl = 'resultsTCLauncher.tpl'; + $gui->url2call = "lib/results/resultsTC.php?tplan_id=$gui->tplan_id" . + "&tproject_id=$gui->tproject_id&doAction=result&format="; + + $gui->pageTitle = $labels['test_result_matrix_filters']; + if ($gui->matrixCfg->buildQtyLimit > 0) { + $gui->userFeedback = $labels['too_much_data'] . '
    ' . + sprintf($labels['too_much_builds'], $gui->activeBuildsQty, + $gui->matrixCfg->buildQtyLimit); + } +} + +$timerOff = microtime(true); +$gui->elapsed_time = round($timerOff - $timerOn, 2); + +$smarty->assign('gui', $gui); +displayReport($templateCfg->template_dir . $tpl, $smarty, $args->format, + $gui->mailCfg, $renderHTML); + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); +} + +/** + * Builds ext-js rich table to display matrix results + * + * + * return tlExtTable + */ +function buildMatrix(&$guiObj, &$argsObj, $forceFormat = null) +{ + $buildIDSet = $argsObj->builds->idSet; + $latestBuild = $argsObj->builds->latest; + + $lbl = init_labels( + [ + 'title_test_suite_name' => null, + 'platform' => null, + 'priority' => null, + 'result_on_last_build' => null, + 'title_test_case_title' => null, + 'latest_exec_notes' => null + ]); + $group_name = $lbl['title_test_suite_name']; + + // Column order is CRITIC, because is used in the build data logic + $columns = [ + [ + 'title_key' => 'title_test_suite_name', + 'width' => 100 + ], + [ + 'title_key' => 'title_test_case_title', + 'width' => 150 + ] + ]; + + if (! is_null($guiObj->platforms) && (count($guiObj->platforms) > 0)) { + $columns[] = [ + 'title_key' => 'platform', + 'width' => 60, + 'filter' => 'list', + 'filterOptions' => $guiObj->platforms + ]; + $group_name = $lbl['platform']; + } + + if ($guiObj->options->testPriorityEnabled) { + $columns[] = [ + 'title_key' => 'priority', + 'type' => 'priority', + 'width' => 40 + ]; + } + + // -------------------------------------------------------------------- + $guiObj->filterFeedback = null; + foreach ($buildIDSet as $iix) { + $buildSet[] = $guiObj->buildInfoSet[$iix]; + if ($guiObj->filterApplied) { + $guiObj->filterFeedback[] = $guiObj->buildInfoSet[$iix]['name']; + } + } + + if ($guiObj->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild']) { + $buildSet[] = [ + 'name' => $lbl['result_on_last_build'] . ' ' . $latestBuild->name + ]; + } + + foreach ($buildSet as $build) { + $columns[] = [ + 'title' => $build['name'], + 'type' => 'status', // OK because we display status + 'width' => 100 + ]; + } + + if ($guiObj->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild']) { + $columns[] = [ + 'title_key' => 'test_exec_notes_latest_created_build', + 'type' => 'notes', + 'width' => 100 + ]; + } + + // -------------------------------------------------------------------- + $columns[] = [ + 'title_key' => 'last_execution', + 'type' => 'status', // OK because we display status + 'width' => 100 + ]; + + $columns[] = [ + 'title_key' => 'latest_exec_notes', + 'type' => 'notes', + 'width' => 100 + ]; + + $fo = ! is_null($forceFormat) ? $forceFormat : $argsObj->format; + if ($fo == FORMAT_HTML) { + + // 20221231 - having a differente name for the table it's critic + // because it seems that column ID, of the column used for sorting are + // saves in a cookie that is not rested. + // This has created an issue when using the Test Results matrix + // in a Test Plan with platforms, and then request for a Test Plan + // without platforms, because the EXTJS code was trying to access + // sort info from a column named id_platform that does not exist + // obvioulsy in the data to be displayed + // That's why I've added the $group_name to the name + // I was able to fix this using the ext-all-debug-w-comments.js + // + $matrix = new tlExtTable($columns, $guiObj->matrix, + 'tlTestResultMatrix' . $group_name); + + // if platforms feature is enabled group by platform otherwise group by test suite + $matrix->setGroupByColumnName($group_name); + + $matrix->sortDirection = 'DESC'; + + if ($guiObj->options->testPriorityEnabled) { + // Developer Note: + // To understand 'filter' => 'Priority' => see exttable.class.php => buildColumns() + $matrix->addCustomBehaviour('priority', + [ + 'render' => 'priorityRenderer', + 'filter' => 'Priority' + ]); + $matrix->setSortByColumnName($lbl['priority']); + } else { + $matrix->setSortByColumnName($lbl['title_test_case_title']); + } + + // define table toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + } else { + $matrix = new tlHTMLTable($columns, $guiObj->matrix, + 'tl_table_results_tc'); + } + + return $matrix; +} + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param array $imgSet + * @param tlTestPlanMetrics $tplanMgr + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj, $imgSet, &$tplanMgr) +{ + $cfg = array( + 'results' => config_get('results'), + 'urgency' => config_get('urgency'), + 'tcase' => config_get('testcase_cfg') + ); + + $guiObj = new stdClass(); + $guiObj->map_status_css = null; + $guiObj->title = lang_get('title_test_report_all_builds'); + $guiObj->printDate = ''; + $guiObj->matrix = []; + + $guiObj->platforms = (array) $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'map' + )); + $guiObj->show_platforms = (count($guiObj->platforms) > 0); + + $guiObj->img = new stdClass(); + $guiObj->img->exec = $imgSet['exec_icon']; + $guiObj->img->edit = $imgSet['edit_icon']; + $guiObj->img->history = $imgSet['history_small']; + + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->tplan_id = $argsObj->tplan_id; + + $guiObj->apikey = $argsObj->apikey; + + $tproject_mgr = new testproject($dbHandler); + $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); + $argsObj->prefix = $tproject_info['prefix']; + $argsObj->tcPrefix = $tproject_info['prefix'] . $cfg['tcase']->glue_character; + $argsObj->tprojectOpt = $tproject_info['opt']; + + $guiObj->options = new stdClass(); + $guiObj->options->testPriorityEnabled = $tproject_info['opt']->testPriorityEnabled; + unset($tproject_mgr); + + $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); + $guiObj->tplan_name = $tplan_info['name']; + $guiObj->tproject_name = $tproject_info['name']; + + $l18n = init_labels( + array( + 'design' => null, + 'execution' => null, + 'history' => 'execution_history', + 'test_result_matrix_filters' => null, + 'too_much_data' => null, + 'too_much_builds' => null, + 'result_on_last_build' => null, + 'versionTag' => 'tcversion_indicator' + )); + + $l18n['not_run'] = lang_get($cfg['results']['status_label']['not_run']); + + $guiObj->matrixCfg = config_get('resultMatrixReport'); + $guiObj->buildInfoSet = $tplanMgr->get_builds($argsObj->tplan_id, + testplan::ACTIVE_BUILDS, null, + array( + 'orderBy' => $guiObj->matrixCfg->buildOrderByClause + )); + $guiObj->activeBuildsQty = count($guiObj->buildInfoSet); + + // hmm need to understand if this can be removed + if ($guiObj->matrixCfg->buildColumns['latestBuildOnLeft']) { + $guiObj->buildInfoSet = array_reverse($guiObj->buildInfoSet); + } + // ------------------------------------------------------------------------------- + + foreach ($cfg['results']['code_status'] as $code => $verbose) { + if (isset($cfg['results']['status_label'][$verbose])) { + $l18n[$code] = lang_get($cfg['results']['status_label'][$verbose]); + $guiObj->map_status_css[$code] = $cfg['results']['code_status'][$code] . + '_text'; + } + } + + $xxx = config_get('urgency'); + foreach ($xxx['code_label'] as $code => $label) { + $cfg['priority'][$code] = lang_get($label); + } + + $guiObj->mailCfg = buildMailCfg($guiObj); + + return array( + $guiObj, + $tproject_info, + $l18n, + $cfg + ); +} + +/** + * + * @param stdClass $gui + * @param stdClass $args + */ +function createSpreadsheet($gui, $args) +{ + $buildIDSet = $args->builds->idSet; + $latestBuild = $args->builds->latest; + + $lbl = initLblSpreadsheet(); + $cellRange = setCellRangeSpreadsheet(); + $style = initStyleSpreadsheet(); + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + + // Step 2 + // data is organized with following columns $dataHeader[] + // Test suite + // Test case + // [Platform] => if any exists + // + // Priority ===> Just discovered that we have choosen to make this column + // displayabled or not according test project configuration + // IMHO has no sense work without priority + // + // Exec result on Build 1 + // Assigned To + // Date + // Tester + // Notes + // Duration + // + // Exec result on Build 2 + // Assigned To + // ... + // ... + // Exec result on Build N + // + // + // Exec result ON LATEST CREATED Build + // Exec notes ON LATEST CREATED Build + // Latest Execution result (Hmm need to explain better) + // Latest Execution notes + // + $dataHeader = [ + $lbl['title_test_suite_name'], + $lbl['title_test_case_title'] + ]; + + if (! empty($gui->platforms)) { + $dataHeader[] = $lbl['platform']; + } + + if ($gui->options->testPriorityEnabled) { + $dataHeader[] = $lbl['priority']; + } + + $gui->filterFeedback = null; + foreach ($buildIDSet as $iix) { + $dataHeader[] = $lbl['build'] . ' ' . $gui->buildInfoSet[$iix]['name']; + $dataHeader[] = $lbl['assigned_to']; + $dataHeader[] = $lbl['date_time_run']; + $dataHeader[] = $lbl['test_exec_by']; + $dataHeader[] = $lbl['notes']; + $dataHeader[] = $lbl['execution_duration']; + + if ($gui->filterApplied) { + $gui->filterFeedback[] = $gui->buildInfoSet[$iix]['name']; + } + } + + // Now the magic + if ($gui->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild']) { + $dataHeader[] = $lbl['result_on_last_build'] . ' ' . $latestBuild->name; + } + + if ($gui->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild']) { + $dataHeader[] = $lbl['test_exec_notes_latest_created_build']; + } + + $dataHeader[] = $lbl['last_execution']; + $dataHeader[] = $lbl['latest_exec_notes']; + + $startingRow = count($lines2write) + 2; // MAGIC + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + $startingRow ++; + $qta_loops = count($gui->matrix); + for ($idx = 0; $idx < $qta_loops; $idx ++) { + foreach ($gui->matrix[$idx] as $ldx => $field) { + $cellID = $cellRange[$ldx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + } + $startingRow ++; + } + + // Final step + $tmpfname = tempnam(config_get('temp_dir'), "resultsTC.tmp"); + $objPHPExcel->setActiveSheetIndex(0); + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + $objWriter->save($tmpfname); + + if ($args->getSpreadsheetBy == 'email') { + require_once 'email_api.php'; + + $ema = new stdClass(); + $ema->from_address = config_get('from_email'); + $ema->to_address = $args->user->emailAddress; + $ema->subject = $gui->mailCfg->subject; + $ema->message = $gui->mailCfg->subject; + + $dum = uniqid("resultsTC_") . '.xls'; + $oops = array( + 'attachment' => array( + 'file' => $tmpfname, + 'newname' => $dum + ), + 'exit_on_error' => true, + 'htmlFormat' => true + ); + $email_op = email_send_wrapper($ema, $oops); + unlink($tmpfname); + exit(); + } else { + downloadXls($tmpfname, $xlsType, $gui, 'resultsTC_'); + } +} + +/** + */ +function setUpBuilds(&$args, &$gui) +{ + $args->builds = new stdClass(); + + if (is_null($args->build_set)) { + $args->builds->idSet = null; + + $gui->buildListForExcel = ''; + $gui->filterApplied = false; + if (! is_null($gui->buildInfoSet)) { + $args->builds->idSet = array_keys($gui->buildInfoSet); + } + } else { + $args->builds->idSet = array_keys(array_flip($args->build_set)); + $gui->filterApplied = true; + $gui->buildListForExcel = implode(',', $args->builds->idSet); + } + + $args->builds->latest = new stdClass(); + $args->builds->latest->id = end($args->builds->idSet); + $args->builds->latest->name = $gui->buildInfoSet[$args->builds->latest->id]['name']; +} + +/** + */ +function buildDataSet(&$db, &$args, &$gui, &$exec, $labels, $forceFormat = null) +{ + $userSet = getUsersForHtmlOptions($db, null, null, null, null, + array( + 'userDisplayFormat' => '%first% %last%' + )); + + // invariant pieces => avoid wasting time on loops + // $dlink = 'basehref) . + // 'linkto.php?tprojectPrefix=' . urlencode($args->prefix) . '&item=testcase&id='; + + $hist_img_tag = ' '; + $edit_img_tag = ' '; + + $metrics = $exec['metrics']; + $latestExecution = $exec['latestExec']; + + $cols = $args->cols; + + $tsuiteSet = array_keys($metrics); + foreach ($tsuiteSet as $tsuiteID) { + + $tcaseSet = array_keys($metrics[$tsuiteID]); + + foreach ($tcaseSet as $tcaseID) { + + // If there are NO PLATFORMS anyway we have the platformID=0!!! + $platformSet = array_keys($metrics[$tsuiteID][$tcaseID]); + foreach ($platformSet as $platformID) { + $rf = &$metrics[$tsuiteID][$tcaseID][$platformID]; + $rows = null; + + // some info does not change on different executions + $build2loop = array_keys($rf); + $top = current($build2loop); + $external_id = $args->tcPrefix . $rf[$top]['external_id']; + $rows[$cols['tsuite']] = $rf[$top]['suiteName']; + + $name = htmlspecialchars("{$external_id}:{$rf[$top]['name']}", + ENT_QUOTES); + + $fo = ! is_null($forceFormat) ? $forceFormat : $args->format; + if ($fo == FORMAT_HTML) { + $rows[$cols['link']] = ""; + if ($args->addOpAccess) { + $rows[$cols['link']] .= "" . + $hist_img_tag . + "" . + $edit_img_tag; + } + $rows[$cols['link']] .= $name; + } else { + $rows[$cols['link']] = "{$external_id}:{$rf[$top]['name']}"; + } + + if ($gui->show_platforms) { + $rows[$cols['platform']] = $gui->platforms[$platformID]; + } + + if ($gui->options->testPriorityEnabled) { + switch ($fo) { + case FORMAT_XLS: + $rows[$cols['priority']] = $args->cfg['priority'][$rf[$top]['priority_level']]; + break; + + default: + // is better to use code to do reorder instead of localized string ??? + $rows[$cols['priority']] = $rf[$top]['priority_level']; + break; + } + } + + // Now loop on result on each build, but following order + $buildExecStatus = null; + $execOnLatestCreatedBuild = null; + $execNoteLatestCreatedBuild = ''; + + foreach ($args->builds->idSet as $buildID) { + $r4build['text'] = ""; + + if ($fo == FORMAT_XLS) { + $r4build = $labels[$rf[$buildID]['status']] . + sprintf($labels['versionTag'], + $rf[$buildID]['version']); + + $tester = ''; + if (isset($userSet, $rf[$buildID]['tester_id'])) { + $tester = $userSet[$rf[$buildID]['tester_id']]; + } + + $assignee = ''; + if (isset($userSet, $rf[$buildID]['user_id'])) { + $assignee = $userSet[$rf[$buildID]['user_id']]; + } + + $bella = array( + $r4build, + $assignee, + $rf[$buildID]['execution_ts'], + $tester, + $rf[$buildID]['execution_notes'], + $rf[$buildID]['execution_duration'] + ); + $buildExecStatus = array_merge((array) $buildExecStatus, + $bella); + } else { + $r4build['text'] = ""; + } + + if ($fo == FORMAT_HTML) { + if ($args->addOpAccess) { + $r4build['text'] = "tplan_id}, {$platformID});\">" . + "img->exec}\" /> "; + } + + $r4build['text'] .= $labels[$rf[$buildID]['status']] . + sprintf($labels['versionTag'], + $rf[$buildID]['version']); + + $r4build['value'] = $rf[$buildID]['status']; + $r4build['cssClass'] = $gui->map_status_css[$rf[$buildID]['status']]; + $buildExecStatus[] = $r4build; + } + + if ($gui->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild'] && + $args->builds->latest->id == $buildID) { + $execOnLatestCreatedBuild = $r4build; + } + + if ($gui->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild'] && + $args->builds->latest->id == $buildID && + $rf[$buildID]['execution_notes']) { + $execNoteLatestCreatedBuild = $rf[$buildID]['execution_notes']; + } + + // why we do special reasoning on NOT RUN ??? + if (($latestExecution[$platformID][$tcaseID]['status'] == + $args->cfg['results']['status_code']['not_run']) || + (($latestExecution[$platformID][$tcaseID]['build_id'] == + $buildID) && + ($latestExecution[$platformID][$tcaseID]['id'] == + $rf[$buildID]['executions_id']))) { + $lexec = $r4build; + } + } // foreach buildIDSet + + // Ok, now the specials + // If configured, add column with Exec result on Latest Created Build + if ($gui->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild']) { + $buildExecStatus[] = $execOnLatestCreatedBuild; + } + + if ($gui->matrixCfg->buildColumns['latestBuildOnLeft']) { + $buildExecStatus = array_reverse($buildExecStatus); + } + + $rows = array_merge($rows, $buildExecStatus); + + if ($gui->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild']) { + $rows[] = $execNoteLatestCreatedBuild; + } + + // Always righmost column will display lastest execution result + $rows[] = $lexec; + + // @see lib/functions/tlTestPlanMetrics.class.php + // getExecStatusMatrix($id, $filters=null, $opt=null) + // + $dfx = $latestExecution[$platformID][$tcaseID]; + $nv = ''; + if (isset($dfx['execution_notes'])) { + $nv = is_null($dfx['execution_notes']) ? '' : $dfx['execution_notes']; + } + $rows[] = $nv; + // ---------------------------------------------------------------------- + + $gui->matrix[] = $rows; + unset($r4build); + unset($rows); + unset($buildExecStatus); + } + } + } +} + +/** + * + * @return array + */ +function initLblSpreadsheet() +{ + return init_labels( + [ + 'title_test_suite_name' => null, + 'platform' => null, + 'priority' => null, + 'build' => null, + 'title_test_case_title' => null, + 'test_exec_by' => null, + 'notes' => null, + 'date_time_run' => null, + 'execution_duration' => null, + 'testproject' => null, + 'generated_by_TestLink_on' => null, + 'testplan' => null, + 'result_on_last_build' => null, + 'last_execution' => null, + 'assigned_to' => null, + 'latest_exec_notes' => null, + 'test_exec_notes_latest_created_build' => null + ]); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $style = array(); + $style['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $style['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + return $style; +} + +/** + */ +function setCellRangeSpreadsheet() +{ + $cr = range('A', 'Z'); + $crLen = count($cr); + for ($idx = 0; $idx < $crLen; $idx ++) { + for ($jdx = 0; $jdx < $crLen; $jdx ++) { + $cr[] = $cr[$idx] . $cr[$jdx]; + } + } + return $cr; +} + +/** + */ +function xlsStepOne(&$oj, $style, &$lbl, &$gui) +{ + $dummy = ''; + $lines2write = array( + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + $cellArea .= "A{$cdx}"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; +} + +/** + */ +function initCols($showPlat) +{ + $tcols = array( + 'tsuite', + 'link' + ); + if ($showPlat) { + $tcols[] = 'platform'; + } + $tcols[] = 'priority'; + return array_flip($tcols); } - - -/** - * Builds ext-js rich table to display matrix results - * - * - * return tlExtTable - * - */ -function buildMatrix(&$guiObj,&$argsObj,$forceFormat=null) { - $buildIDSet = $argsObj->builds->idSet; - $latestBuild = $argsObj->builds->latest; - - $columns = array(array('title_key' => 'title_test_suite_name', 'width' => 100), - array('title_key' => 'title_test_case_title', 'width' => 150)); - - $lbl = init_labels(array('title_test_suite_name' => null,'platform' => null, - 'priority' => null, 'result_on_last_build' => null, - 'title_test_case_title' => null, - 'latest_exec_notes' => null)); - - $group_name = $lbl['title_test_suite_name']; - - if(!is_null($guiObj->platforms)) - { - $columns[] = array('title_key' => 'platform', 'width' => 60, 'filter' => 'list', - 'filterOptions' => $guiObj->platforms); - $group_name = $lbl['platform']; - } - if($guiObj->options->testPriorityEnabled) - { - $columns[] = array('title_key' => 'priority', 'type' => 'priority', 'width' => 40); - } - - // -------------------------------------------------------------------- - $guiObj->filterFeedback = null; - foreach($buildIDSet as $iix) { - $buildSet[] = $guiObj->buildInfoSet[$iix]; - if($guiObj->filterApplied) { - $guiObj->filterFeedback[] = $guiObj->buildInfoSet[$iix]['name']; - } - } - - if( $guiObj->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild'] ) { - $buildSet[] = array('name' => $lbl['result_on_last_build'] . ' ' . $latestBuild->name); - } - - foreach($buildSet as $build) - { - $columns[] = array('title' => $build['name'], 'type' => 'status', 'width' => 100); - } - - if ($guiObj->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild']) { - $columns[] = array('title_key' => 'test_exec_notes_latest_created_build', - 'type' => 'status', 'width' => 100); - } - - // -------------------------------------------------------------------- - $columns[] = array('title_key' => 'last_execution', 'type' => 'status', 'width' => 100); - - $columns[] = array('title_key' => 'latest_exec_notes', 'type' => 'status', 'width' => 100); - - $fo = !is_null($forceFormat) ? $forceFormat : $argsObj->format; - if ($fo == FORMAT_HTML) - { - $matrix = new tlExtTable($columns, $guiObj->matrix, 'tl_table_results_tc'); - - //if platforms feature is enabled group by platform otherwise group by test suite - $matrix->setGroupByColumnName($group_name); - $matrix->sortDirection = 'DESC'; - - if($guiObj->options->testPriorityEnabled) - { - // Developer Note: - // To understand 'filter' => 'Priority' => see exttable.class.php => buildColumns() - $matrix->addCustomBehaviour('priority', array('render' => 'priorityRenderer', 'filter' => 'Priority')); - $matrix->setSortByColumnName($lbl['priority']); - } - else - { - $matrix->setSortByColumnName($lbl['title_test_case_title']); - } - - // define table toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - - } - else - { - $matrix = new tlHTMLTable($columns, $guiObj->matrix, 'tl_table_results_tc'); - } - unset($columns); - - return $matrix; -} - - -/** - * - * - */ -function buildMailCfg(&$guiObj) -{ - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,$imgSet,&$tplanMgr) -{ - - $cfg = array('results' => config_get('results'), 'urgency' => config_get('urgency'), - 'tcase' => config_get('testcase_cfg')); - - $guiObj = new stdClass(); - $guiObj->map_status_css = null; - $guiObj->title = lang_get('title_test_report_all_builds'); - $guiObj->printDate = ''; - $guiObj->matrix = array(); - - $guiObj->platforms = $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'map')); - $guiObj->show_platforms = !is_null($guiObj->platforms); - - $guiObj->img = new stdClass(); - $guiObj->img->exec = $imgSet['exec_icon']; - $guiObj->img->edit = $imgSet['edit_icon']; - $guiObj->img->history = $imgSet['history_small']; - - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->tplan_id = $argsObj->tplan_id; - - $guiObj->apikey = $argsObj->apikey; - - - $tproject_mgr = new testproject($dbHandler); - $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); - $argsObj->prefix = $tproject_info['prefix']; - $argsObj->tcPrefix = $tproject_info['prefix'] . $cfg['tcase']->glue_character; - $argsObj->tprojectOpt = $tproject_info['opt']; - - $guiObj->options = new stdClass(); - $guiObj->options->testPriorityEnabled = $tproject_info['opt']->testPriorityEnabled; - unset($tproject_mgr); - - $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); - $guiObj->tplan_name = $tplan_info['name']; - $guiObj->tproject_name = $tproject_info['name']; - - $l18n = init_labels(array('design' => null, 'execution' => null, 'history' => 'execution_history', - 'test_result_matrix_filters' => null, 'too_much_data' => null,'too_much_builds' => null, - 'result_on_last_build' => null, 'versionTag' => 'tcversion_indicator') ); - - $l18n['not_run']=lang_get($cfg['results']['status_label']['not_run']); - - - $guiObj->matrixCfg = config_get('resultMatrixReport'); - $guiObj->buildInfoSet = $tplanMgr->get_builds($argsObj->tplan_id, testplan::ACTIVE_BUILDS,null, - array('orderBy' => $guiObj->matrixCfg->buildOrderByClause)); - $guiObj->activeBuildsQty = count($guiObj->buildInfoSet); - - - // hmm need to understand if this can be removed - if ($guiObj->matrixCfg->buildColumns['latestBuildOnLeft']) - { - $guiObj->buildInfoSet = array_reverse($guiObj->buildInfoSet); - } - // ------------------------------------------------------------------------------- - - - foreach($cfg['results']['code_status'] as $code => $verbose) - { - if( isset($cfg['results']['status_label'][$verbose])) - { - $l18n[$code] = lang_get($cfg['results']['status_label'][$verbose]); - $guiObj->map_status_css[$code] = $cfg['results']['code_status'][$code] . '_text'; - } - } - - $xxx = config_get('urgency'); - foreach ($xxx['code_label'] as $code => $label) - { - $cfg['priority'][$code] = lang_get($label); - } - - $guiObj->mailCfg = buildMailCfg($guiObj); - - return array($guiObj,$tproject_info,$l18n,$cfg); -} - -/** - * - * - */ -function createSpreadsheet($gui,$args,$media) { - $buildIDSet = $args->builds->idSet; - $latestBuild = $args->builds->latest; - - $lbl = initLblSpreadsheet(); - $cellRange = setCellRangeSpreadsheet(); - $style = initStyleSpreadsheet(); - - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - - // Step 2 - // data is organized with following columns $dataHeader[] - // Test suite - // Test case - // [Platform] => if any exists - // - // Priority ===> Just discovered that we have choosen to make this column - // displayabled or not according test project configuration - // IMHO has no sense work without priority - // - // Exec result on Build 1 - // Assigned To - // Date - // Tester - // Notes - // Duration - // - // Exec result on Build 2 - // Assigned To - // ... - // ... - // Exec result on Build N - // - // - // Exec result ON LATEST CREATED Build - // Exec notes ON LATEST CREATED Build - // Latest Execution result (Hmm need to explain better) - // Latest Execution notes - // - $dataHeader = array($lbl['title_test_suite_name'],$lbl['title_test_case_title']); - - if( $showPlatforms = !is_null($gui->platforms) ) - { - $dataHeader[] = $lbl['platform']; - } - - if($gui->options->testPriorityEnabled) - { - $dataHeader[] = $lbl['priority']; - } - - - $gui->filterFeedback = null; - foreach($buildIDSet as $iix) - { - $dataHeader[] = $lbl['build'] . ' ' . $gui->buildInfoSet[$iix]['name']; - $dataHeader[] = $lbl['assigned_to']; - $dataHeader[] = $lbl['date_time_run']; - $dataHeader[] = $lbl['test_exec_by']; - $dataHeader[] = $lbl['notes']; - $dataHeader[] = $lbl['execution_duration']; - - if($gui->filterApplied) - { - $gui->filterFeedback[] = $gui->buildInfoSet[$iix]['name']; - } - } - - - // Now the magic - if( $gui->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild'] ) - { - $dataHeader[] = $lbl['result_on_last_build'] . ' ' . $latestBuild->name; - } - - if( $gui->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild'] ) - { - $dataHeader[] = $lbl['test_exec_notes_latest_created_build']; - } - - - $dataHeader[] = $lbl['last_execution']; - $dataHeader[] = $lbl['latest_exec_notes']; - - $startingRow = count($lines2write) + 2; // MAGIC - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) - { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet()->getStyle($cellArea)->applyFromArray($style['DataHeader']); - - $startingRow++; - $qta_loops = count($gui->matrix); - for($idx = 0; $idx < $qta_loops; $idx++) - { - foreach($gui->matrix[$idx] as $ldx => $field) - { - $cellID = $cellRange[$ldx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - } - $startingRow++; - } - - // Final step - $tmpfname = tempnam(config_get('temp_dir'),"resultsTC.tmp"); - $objPHPExcel->setActiveSheetIndex(0); - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - $objWriter->save($tmpfname); - - if($args->getSpreadsheetBy == 'email') - { - require_once('email_api.php'); - - $ema = new stdClass(); - $ema->from_address = config_get('from_email'); - $ema->to_address = $args->user->emailAddress;; - $ema->subject = $gui->mailCfg->subject; - $ema->message = $gui->mailCfg->subject; - - $dum = uniqid("resultsTC_") . '.xls'; - $oops = array('attachment' => - array('file' => $tmpfname, 'newname' => $dum), - 'exit_on_error' => true, 'htmlFormat' => true); - $email_op = email_send_wrapper($ema,$oops); - unlink($tmpfname); - exit(); - } - else - { - downloadXls($tmpfname,$xlsType,$gui,'resultsTC_'); - } -} - - -/** - * - */ -function setUpBuilds(&$args,&$gui) { - $args->builds = new stdClass(); - - if( is_null($args->build_set) ) { - $args->builds->idSet = null; - - $gui->buildListForExcel = ''; - $gui->filterApplied = false; - if( !is_null($gui->buildInfoSet) ) { - $args->builds->idSet = array_keys($gui->buildInfoSet); - } - } else { - $args->builds->idSet = array_keys(array_flip($args->build_set)); - $gui->filterApplied = true; - $gui->buildListForExcel = implode(',',$args->builds->idSet); - } - - $args->builds->latest = new stdClass(); - $args->builds->latest->id = end($args->builds->idSet); - $args->builds->latest->name = $gui->buildInfoSet[$args->builds->latest->id]['name']; -} - - - - -/** - * - * - */ -function buildDataSet(&$db,&$args,&$gui,&$exec,$labels,$forceFormat=null) -{ - $userSet = getUsersForHtmlOptions($db,null,null,null,null, - array('userDisplayFormat' => '%first% %last%')); - - // invariant pieces => avoid wasting time on loops - $dlink = 'basehref) . - 'linkto.php?tprojectPrefix=' . urlencode($args->prefix) . '&item=testcase&id='; - - $hist_img_tag = ' '; - $edit_img_tag = ' '; - - $metrics = $exec['metrics']; - $latestExecution = $exec['latestExec']; - - $cols = $args->cols; - - $tsuiteSet = array_keys($metrics); - foreach($tsuiteSet as $tsuiteID) { - - $tcaseSet = array_keys($metrics[$tsuiteID]); - - foreach($tcaseSet as $tcaseID) { - - $platformSet = array_keys($metrics[$tsuiteID][$tcaseID]); - foreach($platformSet as $platformID) { - $rf = &$metrics[$tsuiteID][$tcaseID][$platformID]; - $rows = null; - - // some info does not change on different executions - $build2loop = array_keys($rf); - $top = current($build2loop); - $external_id = $args->tcPrefix . $rf[$top]['external_id']; - $rows[$cols['tsuite']] = $rf[$top]['suiteName']; - - - $name = htmlspecialchars("{$external_id}:{$rf[$top]['name']}",ENT_QUOTES); - - $fo = !is_null($forceFormat) ? $forceFormat : $args->format; - if($fo == FORMAT_HTML) - { - $rows[$cols['link']] = ""; - if($args->addOpAccess) - { - $rows[$cols['link']] .= "" . - $hist_img_tag . - "" . - $edit_img_tag; - } - $rows[$cols['link']] .= $name; - } - else - { - $rows[$cols['link']] = "{$external_id}:{$rf[$top]['name']}"; - } - - if ($gui->show_platforms) - { - $rows[$cols['platform']] = $gui->platforms[$platformID]; - } - - if($gui->options->testPriorityEnabled) - { - switch($fo) - { - case FORMAT_XLS: - $rows[$cols['priority']] = $args->cfg['priority'][$rf[$top]['priority_level']]; - break; - - default: - // is better to use code to do reorder instead of localized string ??? - $rows[$cols['priority']] = $rf[$top]['priority_level']; - break; - } - } - - // Now loop on result on each build, but following order - $buildExecStatus = null; - $execOnLatestCreatedBuild = null; - $execNoteLatestCreatedBuild = ['text' => '']; - foreach($args->builds->idSet as $buildID) - { - $r4build['text'] = ""; - - if( $fo == FORMAT_XLS) - { - $r4build = $labels[$rf[$buildID]['status']] . - sprintf($labels['versionTag'],$rf[$buildID]['version']); - - $tester = ''; - if(isset($userSet,$rf[$buildID]['tester_id'])) - { - $tester = $userSet[$rf[$buildID]['tester_id']]; - } - - $assignee = ''; - if(isset($userSet,$rf[$buildID]['user_id'])) - { - $assignee = $userSet[$rf[$buildID]['user_id']]; - } - - $bella = array($r4build,$assignee, - $rf[$buildID]['execution_ts'],$tester, - $rf[$buildID]['execution_notes'], - $rf[$buildID]['execution_duration']); - $buildExecStatus = array_merge((array)$buildExecStatus, $bella); - } - else - { - $r4build['text'] = ""; - } - - if ($fo == FORMAT_HTML ) - { - if ($args->addOpAccess) - { - $r4build['text'] = "tplan_id}, {$platformID});\">" . - "img->exec}\" /> "; - } - - $r4build['text'] .= $labels[$rf[$buildID]['status']] . - sprintf($labels['versionTag'],$rf[$buildID]['version']); - - $r4build['value'] = $rf[$buildID]['status']; - $r4build['cssClass'] = $gui->map_status_css[$rf[$buildID]['status']]; - $buildExecStatus[] = $r4build; - } - - - if($gui->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild'] && - $args->builds->latest->id == $buildID) - { - $execOnLatestCreatedBuild = $r4build; - } - - if ($gui->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild'] && - $args->builds->latest->id == $buildID && - $rf[$buildID]['execution_notes']) - { - $execNoteLatestCreatedBuild['text'] = $rf[$buildID]['execution_notes']; - } - - // why we do special reasoning on NOT RUN ??? - if( ($latestExecution[$platformID][$tcaseID]['status'] == - $args->cfg['results']['status_code']['not_run']) || - ( ($latestExecution[$platformID][$tcaseID]['build_id'] == $buildID) && - ($latestExecution[$platformID][$tcaseID]['id'] == $rf[$buildID]['executions_id']) ) - ) - { - $lexec = $r4build; - } - } // foreach buildIDSet - - // Ok, now the specials - // If configured, add column with Exec result on Latest Created Build - if ($gui->matrixCfg->buildColumns['showExecutionResultLatestCreatedBuild']) - { - $buildExecStatus[] = $execOnLatestCreatedBuild; - } - - if ($gui->matrixCfg->buildColumns['latestBuildOnLeft']) - { - $buildExecStatus = array_reverse($buildExecStatus); - } - - $rows = array_merge($rows, $buildExecStatus); - - if ($gui->matrixCfg->buildColumns['showExecutionNoteLatestCreatedBuild']) - { - if( $fo == FORMAT_XLS) { - $rows[] = $execNoteLatestCreatedBuild['text']; - } - if( $fo == FORMAT_HTML) { - $rows[] = $execNoteLatestCreatedBuild; - } - } - - // Always righmost column will display lastest execution result - $rows[] = $lexec; - - // @see lib/functions/tlTestPlanMetrics.class.php - // getExecStatusMatrix($id, $filters=null, $opt=null) - // - $dfx = $latestExecution[$platformID][$tcaseID]; - - $nv = ''; - if( isset($dfx['execution_notes']) ) { - $nv = is_null($dfx['execution_notes']) ? '' : $dfx['execution_notes']; - } - - - if( $fo == FORMAT_XLS) { - $rows[] = $nv; - } - if( $fo == FORMAT_HTML) { - $rows[] = ['text' => $nv]; - } - - $gui->matrix[] = $rows; - unset($r4build); - unset($rows); - unset($buildExecStatus); - } // $platformSet - } // $tcaseSet - } // $tsuiteSet -} - - -/** - * - */ -function initLblSpreadsheet() { - $lbl = init_labels(array('title_test_suite_name' => null, - 'platform' => null,'priority' => null, - 'build' => null, 'title_test_case_title' => null,'test_exec_by' => null, - 'notes' => null, 'date_time_run' => null, 'execution_duration' => null, - 'testproject' => null,'generated_by_TestLink_on' => null,'testplan' => null, - 'result_on_last_build' => null,'last_execution' => null,'assigned_to' => null,'latest_exec_notes' => null, - 'test_exec_notes_latest_created_build' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() { - $style = array(); - $style['ReportContext'] = array('font' => array('bold' => true)); - $style['DataHeader'] = array('font' => array('bold' => true), - 'borders' => array('outline' => array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - return $style; -} - -/** - * - */ -function setCellRangeSpreadsheet() { - $cr = range('A','Z'); - $crLen = count($cr); - for($idx = 0; $idx < $crLen; $idx++) { - for($jdx = 0; $jdx < $crLen; $jdx++) { - $cr[] = $cr[$idx] . $cr[$jdx]; - } - } - return $cr; -} - -/** - * - */ -function xlsStepOne(&$oj,$style,&$lbl,&$gui) { - $dummy = ''; - $lines2write = array(array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time()))); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0)->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet()->getStyle($cellArea)->applyFromArray($style['ReportContext']); - - return $lines2write; -} - -/** - * - */ -function initCols($showPlat) -{ - $tcols = array('tsuite', 'link'); - if($showPlat) - { - $tcols[] = 'platform'; - } - $tcols[] = 'priority'; - $cols = array_flip($tcols); - return $cols; -} diff --git a/lib/results/resultsTCAbsoluteLatest.php b/lib/results/resultsTCAbsoluteLatest.php index 7755daebf7..7dcb79a4a6 100644 --- a/lib/results/resultsTCAbsoluteLatest.php +++ b/lib/results/resultsTCAbsoluteLatest.php @@ -1,703 +1,830 @@ - -* -* Absolute Latest Execution Results on Test Plan & ONE Platform -* Builds ARE IGNORED -* -*/ -require('../../config.inc.php'); - -// Must be included BEFORE common.php -require_once('../../third_party/codeplex/PHPExcel.php'); - -require_once('common.php'); -require_once('displayMgr.php'); -require_once('exttable.class.php'); - -$timerOn = microtime(true); // will be used to compute elapsed time -$templateCfg = templateConfiguration(); - -// to init $db -testlinkInitPage($db,false,false); -$smarty = new TLSmarty; -$metricsMgr = new tlTestPlanMetrics($db); -$tplan_mgr = &$metricsMgr; - - -$args = init_args($db); - -list($gui,$tproject_info,$labels,$cfg) = - initializeGui($db,$args,$smarty->getImages(),$tplan_mgr); -$args->cfg = $cfg; - - -$renderHTML = true; - -// Because we will try to send via email xls, we need to be careful -// with logic regarding args->format. -// may be we need to add in logic the media => email, download, etc -// -// We have faced a performance block due to an environment with -// 700 Builds and 1300 Test Cases on Test Plan -// This created a block on NOT RUN QUERY, -// but anyway will produce an enormous and unmanageable matrix on screen -// - -switch ($args->doAction) { - case 'result': - $tpl = $templateCfg->default_template; - doProcess($db,$args,$gui,$metricsMgr); - break; - - case 'choose': - default: - $tpl = 'resultsTCAbsoluteLatestLauncher.tpl'; - $gui->url2call = $args->basehref . - "lib/results/resultsTCAbsoluteLatest.php?tplan_id=$gui->tplan_id" . - "&tproject_id=$gui->tproject_id&doAction=result"; - break; + + * + * Absolute Latest Execution Results on Test Plan & ONE Platform + * Builds ARE IGNORED + * + */ +require_once '../../config.inc.php'; +require_once '../../third_party/codeplex/PHPExcel.php'; // Must be included BEFORE common.php +require_once 'common.php'; +require_once 'displayMgr.php'; +require_once 'exttable.class.php'; + +$timerOn = microtime(true); // will be used to compute elapsed time +$templateCfg = templateConfiguration(); + +// to init $db +testlinkInitPage($db, false, false); +$smarty = new TLSmarty(); +$metricsMgr = new tlTestPlanMetrics($db); +$tplan_mgr = &$metricsMgr; + +$args = initArgs($db); + +list ($gui, $tproject_info, $labels, $cfg) = initializeGui($db, $args, + $smarty->getImages(), $tplan_mgr); +$args->cfg = $cfg; + +$renderHTML = true; + +// Because we will try to send via email xls, we need to be careful +// with logic regarding args->format. +// may be we need to add in logic the media => email, download, etc +// +// We have faced a performance block due to an environment with +// 700 Builds and 1300 Test Cases on Test Plan +// This created a block on NOT RUN QUERY, +// but anyway will produce an enormous and unmanageable matrix on screen +// + +switch ($args->doAction) { + case 'result': + $tpl = $templateCfg->default_template; + doProcess($db, $args, $gui, $metricsMgr); + break; + + case 'choose': + default: + $tpl = 'resultsTCAbsoluteLatestLauncher.tpl'; + $gui->url2call = $args->basehref . + "lib/results/resultsTCAbsoluteLatest.php?tplan_id=$gui->tplan_id" . + "&tproject_id=$gui->tproject_id&doAction=result"; + break; +} + +$smarty->assign('gui', $gui); +displayReport($templateCfg->template_dir . $tpl, $smarty, $args->format, + $gui->mailCfg, $renderHTML); + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "platform_id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 5, + 10 + ), + "format" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->format = intval($args->format); + + $args->getSpreadsheetBy = isset($_REQUEST['sendSpreadSheetByMail_x']) ? 'email' : null; + + if (is_null($args->getSpreadsheetBy)) { + $args->getSpreadsheetBy = isset($_REQUEST['exportSpreadSheet_x']) ? 'download' : null; + } + + $args->addOpAccess = true; + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + if ($args->tproject_id <= 0) { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid Test Project ID ({$args->tproject_id})"; + throw new Exception($msg); + } + + if ($args->tplan_id <= 0) { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid Test PLAN ID ({$args->tproject_id})"; + throw new Exception($msg); + } + + if ($args->doAction == 'result' && $args->platform_id <= 0) { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid PLATFORM ID ({$args->tproject_id})"; + throw new Exception($msg); + } + + $args->user = $_SESSION['currentUser']; + $args->basehref = $_SESSION['basehref']; + + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); +} + +/** + * Builds ext-js rich table to display matrix results + * + * @param stdClass $guiObj + * @param stdClass $argsObj + * @param int $forceFormat + * @return tlExtTable|tlHTMLTable + */ +function buildMatrix(&$guiObj, &$argsObj, $forceFormat = null) +{ + $columns = array( + array( + 'title_key' => 'title_test_suite_name', + 'width' => 100 + ), + array( + 'title_key' => 'title_test_case_title', + 'width' => 150 + ) + ); + + $lbl = init_labels( + array( + 'title_test_suite_name' => null, + 'platform' => null, + 'priority' => null, + 'result_on_last_build' => null, + 'title_test_case_title' => null, + 'latest_exec_notes' => null + )); + + $group_name = $lbl['title_test_suite_name']; + + if (! is_null($guiObj->platforms)) { + $columns[] = array( + 'title_key' => 'platform', + 'width' => 60, + 'filter' => 'list', + 'filterOptions' => $guiObj->platforms + ); + $group_name = $lbl['platform']; + } + + if ($guiObj->options->testPriorityEnabled) { + $columns[] = array( + 'title_key' => 'priority', + 'type' => 'priority', + 'width' => 40 + ); + } + + // -------------------------------------------------------------------- + $columns[] = array( + 'title_key' => 'latest_execution', + 'type' => 'status', + 'width' => 100 + ); + + $columns[] = array( + 'title_key' => 'latest_exec_notes', + 'type' => 'status', + 'width' => 100 + ); + + $fo = ! is_null($forceFormat) ? $forceFormat : $argsObj->format; + if ($fo == FORMAT_HTML) { + $matrix = new tlExtTable($columns, $guiObj->matrix, + 'tl_table_results_tc'); + + // if platforms feature is enabled group by platform + // otherwise group by test suite + $matrix->setGroupByColumnName($group_name); + $matrix->sortDirection = 'DESC'; + + if ($guiObj->options->testPriorityEnabled) { + // Developer Note: + // To understand 'filter' => 'Priority' => + // see exttable.class.php => buildColumns() + $matrix->addCustomBehaviour('priority', + array( + 'render' => 'priorityRenderer', + 'filter' => 'Priority' + )); + $matrix->setSortByColumnName($lbl['priority']); + } else { + $matrix->setSortByColumnName($lbl['title_test_case_title']); + } + + // define table toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + } else { + $matrix = new tlHTMLTable($columns, $guiObj->matrix, + 'tl_table_results_tc'); + } + unset($columns); + + return $matrix; +} + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param array $imgSet + * @param tlTestPlanMetrics $tplanMgr + * @return array + */ +function initializeGui(&$dbHandler, &$argsObj, $imgSet, &$tplanMgr) +{ + $cfg = array( + 'results' => config_get('results'), + 'urgency' => config_get('urgency'), + 'tcase' => config_get('testcase_cfg') + ); + + $guiObj = new stdClass(); + $guiObj->map_status_css = null; + $guiObj->title = lang_get('resultsTCAbsoluteLatest_title'); + $guiObj->pageTitle = $guiObj->title; + + $guiObj->printDate = ''; + $guiObj->matrix = array(); + $guiObj->platform_id = $argsObj->platform_id; + + $guiObj->platforms = $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'map' + )); + $guiObj->show_platforms = ! is_null($guiObj->platforms); + + $guiObj->img = new stdClass(); + $guiObj->img->exec = $imgSet['exec_icon']; + $guiObj->img->edit = $imgSet['edit_icon']; + $guiObj->img->history = $imgSet['history_small']; + + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->tplan_id = $argsObj->tplan_id; + + $guiObj->apikey = $argsObj->apikey; + + $tproject_mgr = new testproject($dbHandler); + $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); + $argsObj->prefix = $tproject_info['prefix']; + $argsObj->tcPrefix = $tproject_info['prefix'] . $cfg['tcase']->glue_character; + $argsObj->tprojectOpt = $tproject_info['opt']; + + $guiObj->options = new stdClass(); + $guiObj->options->testPriorityEnabled = $tproject_info['opt']->testPriorityEnabled; + unset($tproject_mgr); + + $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); + $guiObj->tplan_name = $tplan_info['name']; + $guiObj->tproject_name = $tproject_info['name']; + + $L10N = init_labels( + array( + 'design' => null, + 'execution' => null, + 'latest_execution' => null, + 'history' => 'execution_history', + 'test_result_matrix_filters' => null, + 'too_much_data' => null, + 'too_much_builds' => null, + 'result_on_last_build' => null, + 'versionTag' => 'tcversion_indicator' + )); + + $L10N['not_run'] = lang_get($cfg['results']['status_label']['not_run']); + + $guiObj->report_details = lang_get(basename(__FILE__, '.php')); + + $guiObj->matrixCfg = config_get('resultMatrixReport'); + $guiObj->buildInfoSet = $tplanMgr->get_builds($argsObj->tplan_id, + testplan::ACTIVE_BUILDS, null, + array( + 'orderBy' => $guiObj->matrixCfg->buildOrderByClause + )); + $guiObj->activeBuildsQty = count($guiObj->buildInfoSet); + + foreach ($cfg['results']['code_status'] as $code => $verbose) { + if (isset($cfg['results']['status_label'][$verbose])) { + $L10N[$code] = lang_get($cfg['results']['status_label'][$verbose]); + $guiObj->map_status_css[$code] = $cfg['results']['code_status'][$code] . + '_text'; + } + } + + $xxx = config_get('urgency'); + foreach ($xxx['code_label'] as $code => $label) { + $cfg['priority'][$code] = lang_get($label); + } + + $guiObj->mailCfg = buildMailCfg($guiObj); + + $guiObj->labels = $L10N; + return array( + $guiObj, + $tproject_info, + $L10N, + $cfg + ); +} + +/** + * + * @param stdClass $gui + * @param stdClass $args + */ +function createSpreadsheet($gui, $args) +{ + $lbl = initLblSpreadsheet(); + $cellRange = setCellRangeSpreadsheet(); + $style = initStyleSpreadsheet(); + + $objPHPExcel = new PHPExcel(); + $lines2write = xlsStepOne($objPHPExcel, $style, $lbl, $gui); + + // Step 2 + // data is organized with following columns $dataHeader[] + // Test suite + // Test case + // [Platform] => if any exists + // + // Priority ===> Just discovered that we have choosen to make this column + // displayabled or not according test project configuration + // IMHO has no sense work without priority + // + // Latest Execution result (Hmm need to explain better) + // Latest Execution notes + // + $dataHeader = array( + $lbl['title_test_suite_name'], + $lbl['title_test_case_title'] + ); + + if (! is_null($gui->platforms)) { + $dataHeader[] = $lbl['platform']; + } + + if ($gui->options->testPriorityEnabled) { + $dataHeader[] = $lbl['priority']; + } + + $dataHeader[] = $lbl['latest_execution']; + $dataHeader[] = $lbl['latest_exec_notes']; + + $startingRow = count($lines2write) + 2; // MAGIC + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['DataHeader']); + + $startingRow ++; + $qta_loops = count($gui->matrix); + for ($idx = 0; $idx < $qta_loops; $idx ++) { + foreach ($gui->matrix[$idx] as $ldx => $field) { + $cellID = $cellRange[$ldx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + } + $startingRow ++; + } + + // Final step + $fname = basename(__FILE__, '.php') . '_'; + $tmpfname = tempnam(config_get('temp_dir'), $fname . ".tmp"); + $objPHPExcel->setActiveSheetIndex(0); + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + $objWriter->save($tmpfname); + + if ($args->getSpreadsheetBy == 'email') { + require_once 'email_api.php'; + + $ema = new stdClass(); + $ema->from_address = config_get('from_email'); + $ema->to_address = $args->user->emailAddress; + $ema->subject = $gui->mailCfg->subject; + $ema->message = $gui->mailCfg->subject; + + $dum = uniqid($fname) . '.xls'; + $oops = array( + 'attachment' => array( + 'file' => $tmpfname, + 'newname' => $dum + ), + 'exit_on_error' => true, + 'htmlFormat' => true + ); + $email_op = email_send_wrapper($ema, $oops); + unlink($tmpfname); + exit(); + } else { + downloadXls($tmpfname, $xlsType, $gui, $fname); + } +} + +/** + * + * @param stdClass $args + * @param stdClass $gui + */ +function setUpBuilds(&$args, &$gui) +{ + $args->builds = new stdClass(); + + if (is_null($args->build_set)) { + $args->builds->idSet = null; + + $gui->buildListForExcel = ''; + $gui->filterApplied = false; + if (! is_null($gui->buildInfoSet)) { + $args->builds->idSet = array_keys($gui->buildInfoSet); + } + } else { + $args->builds->idSet = array_keys(array_flip($args->build_set)); + $gui->filterApplied = true; + $gui->buildListForExcel = implode(',', $args->builds->idSet); + } + + $args->builds->latest = new stdClass(); + $args->builds->latest->id = end($args->builds->idSet); + $args->builds->latest->name = $gui->buildInfoSet[$args->builds->latest->id]['name']; +} + +/** + * + * @param database $db + * @param stdClass $args + * @param stdClass $gui + * @param array $metrics + * @param array $labels + * @param int $forceFormat + */ +function buildDataSet(&$db, &$args, &$gui, &$metrics, $labels, + $forceFormat = null) +{ + $hist_img_tag = ' '; + $edit_img_tag .= '" /> '; + + $cols = $args->cols; + $priorityCfg = config_get('urgencyImportance'); + $execVerboseCode = config_get('results'); + $execVerboseCode = $execVerboseCode['status_code']; + + $itemSet = array_keys($metrics); + $tsuiteCache = array(); + $treeMgr = new tree($db); + + foreach ($itemSet as $iidx) { + $tcaseSet = array_keys($metrics[$iidx]); + foreach ($tcaseSet as $tcaseID) { + $platformSet = array_keys($metrics[$iidx][$tcaseID]); + foreach ($platformSet as $platformID) { + $rf = &$metrics[$iidx][$tcaseID][$platformID][0]; + + $tsuiteID = $rf['tsuite_id']; + if (! isset($tsuiteCache[$tsuiteID])) { + // Get full path + $tsuiteCache[$tsuiteID] = implode("/", + $treeMgr->get_path($tsuiteID, null, 'name')); + } + + $rows = null; + $external_id = $args->tcPrefix . $rf['external_id']; + $rows[$cols['tsuite']] = $tsuiteCache[$tsuiteID]; + + $name = htmlspecialchars("{$external_id}:{$rf['name']}", + ENT_QUOTES); + + $fo = ! is_null($forceFormat) ? $forceFormat : $args->format; + if ($fo == FORMAT_HTML) { + $rows[$cols['link']] = ""; + + if ($args->addOpAccess) { + $rows[$cols['link']] .= "" . + $hist_img_tag . + "" . + $edit_img_tag; + } + $rows[$cols['link']] .= $name; + } else { + $rows[$cols['link']] = "{$external_id}:{$rf['name']}"; + } + + $rows[$cols['platform']] = $gui->platforms[$platformID]; + + if ($gui->options->testPriorityEnabled) { + if ($rf['urg_imp'] >= $priorityCfg->threshold['high']) { + $rf['priority_level'] = HIGH; + } elseif ($rf['urg_imp'] < $priorityCfg->threshold['low']) { + $rf['priority_level'] = LOW; + } else { + $rf['priority_level'] = MEDIUM; + } + + switch ($fo) { + case FORMAT_XLS: + // We need the human readable value, not the code + $rows[$cols['priority']] = $args->cfg['priority'][$rf['priority_level']]; + break; + + default: + // Raw Code the human readable value will be + // constructed while rendering. + $rows[$cols['priority']] = $rf['priority_level']; + break; + } + } + + $statusVerbose = $labels[$rf['status']] . + sprintf($labels['versionTag'], $rf['version']); + + if ($fo == FORMAT_HTML) { + $execOut = array( + 'text' => '', + 'value' => '', + 'cssClass' => '' + ); + $execOut['text'] = $statusVerbose; + $execOut['value'] = $rf['status']; + $execOut['cssClass'] = $gui->map_status_css[$rf['status']]; + $rows[] = $execOut; + } else { + $rows[] = $statusVerbose; + } + $nv = ''; + if (isset($rf['execution_notes'])) { + $nv = is_null($rf['execution_notes']) ? '' : $rf['execution_notes']; + } + if ($fo == FORMAT_XLS) { + $rows[] = $nv; + } + if ($fo == FORMAT_HTML) { + $rows[] = [ + 'text' => $nv + ]; + } + $gui->matrix[] = $rows; + } // $platformSet + } // $tcaseSet + } // $tsuiteSet +} + +/** + * + * @return array + */ +function initLblSpreadsheet() +{ + return init_labels( + array( + 'title_test_suite_name' => null, + 'platform' => null, + 'priority' => null, + 'title_test_case_title' => null, + 'test_exec_by' => null, + 'notes' => null, + 'date_time_run' => null, + 'execution_duration' => null, + 'testproject' => null, + 'generated_by_TestLink_on' => null, + 'testplan' => null, + 'result_on_last_build' => null, + 'latest_execution' => null, + 'assigned_to' => null, + 'latest_exec_notes' => null, + 'important_notice' => null + )); +} + +/** + * + * @return array + */ +function initStyleSpreadsheet() +{ + $style = array(); + $style['ReportContext'] = array( + 'font' => array( + 'bold' => true + ) + ); + $style['DataHeader'] = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + return $style; +} + +/** + * + * @return string|array + */ +function setCellRangeSpreadsheet() +{ + $cr = range('A', 'Z'); + $crLen = count($cr); + for ($idx = 0; $idx < $crLen; $idx ++) { + for ($jdx = 0; $jdx < $crLen; $jdx ++) { + $cr[] = $cr[$idx] . $cr[$jdx]; + } + } + return $cr; +} + +/** + * + * @param PHPExcel $oj + * @param array $style + * @param array $lbl + * @param stdClass $gui + * @return array + */ +function xlsStepOne(&$oj, $style, &$lbl, &$gui) +{ + $dummy = ''; + $lines2write = array( + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['important_notice'], + $gui->report_details + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ) + ); + + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $oj->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + $cellArea .= "A{$cdx}"; + $oj->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($style['ReportContext']); + + return $lines2write; +} + +/** + * + * @return array + */ +function initCols() +{ + $tcols = array( + 'tsuite', + 'link', + 'platform', + 'priority', + 'latest_exec', + 'latest_exec_notes' + ); + return array_flip($tcols); +} + +/** + * + * @param database $dbH + * @param stdClass $args + * @param stdClass $gui + * @param tlTestPlanMetrics $metricsMgr + */ +function doProcess(&$dbH, &$args, &$gui, &$metricsMgr) +{ + $opt = array( + 'output' => 'array' + ); + $neverRunOnPP = (array) $metricsMgr->getNeverRunOnSinglePlatform( + $args->tplan_id, $args->platform_id); + + $execStatus = (array) $metricsMgr->getLatestExecOnSinglePlatformMatrix( + $args->tplan_id, $args->platform_id, $opt); + + $allExec = array(); + foreach ($neverRunOnPP as $elem) { + $allExec[] = $elem; + } + foreach ($execStatus as $elem) { + $allExec[] = $elem; + } + + // Every Test suite a row on matrix to display will be created + $args->cols = initCols(); + if (! is_null($neverRunOnPP) || ! is_null($execStatus)) { + buildDataSet($dbH, $args, $gui, $allExec, $gui->labels); + } + + switch ($args->format) { + case FORMAT_XLS: + createSpreadsheet($gui, $args); + break; + + default: + $gui->tableSet[] = buildMatrix($gui, $args); + break; + } } - -$smarty->assign('gui',$gui); -displayReport($templateCfg->template_dir . $tpl, $smarty, $args->format, - $gui->mailCfg,$renderHTML); - - -/** - * - * - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "platform_id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,5,10), - "format" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $args->format = intval($args->format); - - $args->getSpreadsheetBy = isset($_REQUEST['sendSpreadSheetByMail_x']) ? 'email' : null; - - if( is_null($args->getSpreadsheetBy) ) { - $args->getSpreadsheetBy = isset($_REQUEST['exportSpreadSheet_x']) ? 'download' : null; - } - - - $args->addOpAccess = true; - if( !is_null($args->apikey) ) { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if( strlen($args->apikey) == 32) { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } else { - $args->addOpAccess = false; - $cerbero->method = null; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } else { - testlinkInitPage($dbHandler,false,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - if ($args->tproject_id <= 0) { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid Test Project ID ({$args->tproject_id})"; - throw new Exception($msg); - } - - if ($args->tplan_id <= 0) { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid Test PLAN ID ({$args->tproject_id})"; - throw new Exception($msg); - } - - if ($args->doAction == 'result') { - if ($args->platform_id <= 0) { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid PLATFORM ID ({$args->tproject_id})"; - throw new Exception($msg); - } - } - - $args->user = $_SESSION['currentUser']; - $args->basehref = $_SESSION['basehref']; - - return $args; -} - -/** - * - * - */ -function checkRights(&$db,&$user,$context = null) -{ - if (is_null($context)) { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; -} - - -/** - * Builds ext-js rich table to display matrix results - * - * - * return tlExtTable - * - */ -function buildMatrix(&$guiObj,&$argsObj,$forceFormat=null) { - - $columns = array(array('title_key' => 'title_test_suite_name', - 'width' => 100), - array('title_key' => 'title_test_case_title', - 'width' => 150)); - - $lbl = init_labels(array('title_test_suite_name' => null, - 'platform' => null, - 'priority' => null, - 'result_on_last_build' => null, - 'title_test_case_title' => null, - 'latest_exec_notes' => null)); - - $group_name = $lbl['title_test_suite_name']; - - if (!is_null($guiObj->platforms)) { - $columns[] = array('title_key' => 'platform', - 'width' => 60, 'filter' => 'list', - 'filterOptions' => $guiObj->platforms); - $group_name = $lbl['platform']; - } - - if ($guiObj->options->testPriorityEnabled) { - $columns[] = array('title_key' => 'priority', - 'type' => 'priority', 'width' => 40); - } - - // -------------------------------------------------------------------- - $columns[] = array('title_key' => 'latest_execution', - 'type' => 'status', 'width' => 100); - - $columns[] = array('title_key' => 'latest_exec_notes', - 'type' => 'status', 'width' => 100); - - $fo = !is_null($forceFormat) ? $forceFormat : $argsObj->format; - if ($fo == FORMAT_HTML) { - $matrix = new tlExtTable($columns, $guiObj->matrix, 'tl_table_results_tc'); - - // if platforms feature is enabled group by platform - // otherwise group by test suite - $matrix->setGroupByColumnName($group_name); - $matrix->sortDirection = 'DESC'; - - if ($guiObj->options->testPriorityEnabled) { - // Developer Note: - // To understand 'filter' => 'Priority' => - // see exttable.class.php => buildColumns() - $matrix->addCustomBehaviour('priority', array('render' => 'priorityRenderer', 'filter' => 'Priority')); - $matrix->setSortByColumnName($lbl['priority']); - } else { - $matrix->setSortByColumnName($lbl['title_test_case_title']); - } - - // define table toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - } else { - $matrix = new tlHTMLTable($columns, $guiObj->matrix, 'tl_table_results_tc'); - } - unset($columns); - - return $matrix; -} - - -/** - * - * - */ -function buildMailCfg(&$guiObj) -{ - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,$imgSet,&$tplanMgr) -{ - - $cfg = array('results' => config_get('results'), - 'urgency' => config_get('urgency'), - 'tcase' => config_get('testcase_cfg')); - - $guiObj = new stdClass(); - $guiObj->map_status_css = null; - $guiObj->title = lang_get('resultsTCAbsoluteLatest_title'); - $guiObj->pageTitle = $guiObj->title; - - $guiObj->printDate = ''; - $guiObj->matrix = array(); - $guiObj->platform_id = $argsObj->platform_id; - - $guiObj->platforms = - $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'map')); - $guiObj->show_platforms = !is_null($guiObj->platforms); - - $guiObj->img = new stdClass(); - $guiObj->img->exec = $imgSet['exec_icon']; - $guiObj->img->edit = $imgSet['edit_icon']; - $guiObj->img->history = $imgSet['history_small']; - - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->tplan_id = $argsObj->tplan_id; - - $guiObj->apikey = $argsObj->apikey; - - - $tproject_mgr = new testproject($dbHandler); - $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); - $argsObj->prefix = $tproject_info['prefix']; - $argsObj->tcPrefix = $tproject_info['prefix'] . $cfg['tcase']->glue_character; - $argsObj->tprojectOpt = $tproject_info['opt']; - - $guiObj->options = new stdClass(); - $guiObj->options->testPriorityEnabled = $tproject_info['opt']->testPriorityEnabled; - unset($tproject_mgr); - - $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); - $guiObj->tplan_name = $tplan_info['name']; - $guiObj->tproject_name = $tproject_info['name']; - - $L10N = init_labels(array('design' => null, - 'execution' => null, - 'latest_execution' => null, - 'history' => 'execution_history', - 'test_result_matrix_filters' => null, - 'too_much_data' => null, - 'too_much_builds' => null, - 'result_on_last_build' => null, - 'versionTag' => 'tcversion_indicator') ); - - $L10N['not_run'] = lang_get($cfg['results']['status_label']['not_run']); - - $guiObj->report_details = lang_get(basename(__FILE__, '.php')); - - $guiObj->matrixCfg = config_get('resultMatrixReport'); - $guiObj->buildInfoSet = - $tplanMgr->get_builds($argsObj->tplan_id, testplan::ACTIVE_BUILDS,null, - array('orderBy' => $guiObj->matrixCfg->buildOrderByClause)); - $guiObj->activeBuildsQty = count($guiObj->buildInfoSet); - - - foreach($cfg['results']['code_status'] as $code => $verbose) { - if( isset($cfg['results']['status_label'][$verbose])) { - $L10N[$code] = lang_get($cfg['results']['status_label'][$verbose]); - $guiObj->map_status_css[$code] = $cfg['results']['code_status'][$code] . '_text'; - } - } - - $xxx = config_get('urgency'); - foreach ($xxx['code_label'] as $code => $label) { - $cfg['priority'][$code] = lang_get($label); - } - - $guiObj->mailCfg = buildMailCfg($guiObj); - - $guiObj->labels = $L10N; - return array($guiObj,$tproject_info,$L10N,$cfg); -} - -/** - * - * - */ -function createSpreadsheet($gui,$args,$media) { - $lbl = initLblSpreadsheet(); - $cellRange = setCellRangeSpreadsheet(); - $style = initStyleSpreadsheet(); - - $objPHPExcel = new PHPExcel(); - $lines2write = xlsStepOne($objPHPExcel,$style,$lbl,$gui); - - // Step 2 - // data is organized with following columns $dataHeader[] - // Test suite - // Test case - // [Platform] => if any exists - // - // Priority ===> Just discovered that we have choosen to make this column - // displayabled or not according test project configuration - // IMHO has no sense work without priority - // - // Latest Execution result (Hmm need to explain better) - // Latest Execution notes - // - $dataHeader = array($lbl['title_test_suite_name'], - $lbl['title_test_case_title']); - - if( $showPlatforms = !is_null($gui->platforms) ) { - $dataHeader[] = $lbl['platform']; - } - - if ($gui->options->testPriorityEnabled) { - $dataHeader[] = $lbl['priority']; - } - - - $dataHeader[] = $lbl['latest_execution']; - $dataHeader[] = $lbl['latest_exec_notes']; - - $startingRow = count($lines2write) + 2; // MAGIC - $cellArea = "A{$startingRow}:"; - foreach ($dataHeader as $zdx => $field) { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet()->getStyle($cellArea)->applyFromArray($style['DataHeader']); - - $startingRow++; - $qta_loops = count($gui->matrix); - for($idx = 0; $idx < $qta_loops; $idx++) { - foreach($gui->matrix[$idx] as $ldx => $field) { - $cellID = $cellRange[$ldx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - } - $startingRow++; - } - - // Final step - $fname = basename(__FILE__, '.php') . '_'; - $tmpfname = tempnam(config_get('temp_dir'), $fname . ".tmp"); - $objPHPExcel->setActiveSheetIndex(0); - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - $objWriter->save($tmpfname); - - if ($args->getSpreadsheetBy == 'email') { - require_once('email_api.php'); - - $ema = new stdClass(); - $ema->from_address = config_get('from_email'); - $ema->to_address = $args->user->emailAddress;; - $ema->subject = $gui->mailCfg->subject; - $ema->message = $gui->mailCfg->subject; - - $dum = uniqid($fname) . '.xls'; - $oops = array('attachment' => - array('file' => $tmpfname, 'newname' => $dum), - 'exit_on_error' => true, 'htmlFormat' => true); - $email_op = email_send_wrapper($ema,$oops); - unlink($tmpfname); - exit(); - } else { - downloadXls($tmpfname,$xlsType,$gui,$fname); - } -} - - -/** - * - */ -function setUpBuilds(&$args,&$gui) { - $args->builds = new stdClass(); - - if( is_null($args->build_set) ) { - $args->builds->idSet = null; - - $gui->buildListForExcel = ''; - $gui->filterApplied = false; - if( !is_null($gui->buildInfoSet) ) { - $args->builds->idSet = array_keys($gui->buildInfoSet); - } - } else { - $args->builds->idSet = array_keys(array_flip($args->build_set)); - $gui->filterApplied = true; - $gui->buildListForExcel = implode(',',$args->builds->idSet); - } - - $args->builds->latest = new stdClass(); - $args->builds->latest->id = end($args->builds->idSet); - $args->builds->latest->name = $gui->buildInfoSet[$args->builds->latest->id]['name']; -} - - - - -/** - * - * - */ -function buildDataSet(&$db,&$args,&$gui,&$metrics,$labels,$forceFormat=null) -{ - $userSet = getUsersForHtmlOptions($db,null,null,null,null, - array('userDisplayFormat' => '%first% %last%')); - - // invariant pieces => avoid wasting time on loops - $dlink = 'basehref) . - 'linkto.php?tprojectPrefix=' . urlencode($args->prefix) . '&item=testcase&id='; - - $hist_img_tag = ' '; - $edit_img_tag .= '" /> '; - - - $cols = $args->cols; - $priorityCfg = config_get('urgencyImportance'); - $execVerboseCode = config_get('results'); - $execVerboseCode = $execVerboseCode['status_code']; - $execCodeVerbose = array_flip($execVerboseCode); - - $itemSet = array_keys($metrics); - $tsuiteCache = array(); - $treeMgr = new tree($db); - - foreach($itemSet as $iidx) { - $tcaseSet = array_keys($metrics[$iidx]); - foreach($tcaseSet as $tcaseID) { - $platformSet = array_keys($metrics[$iidx][$tcaseID]); - foreach($platformSet as $platformID) { - $rf = &$metrics[$iidx][$tcaseID][$platformID][0]; - - $tsuiteID = $rf['tsuite_id']; - if (!isset($tsuiteCache[$tsuiteID])) { - // Get full path - $tsuiteCache[$tsuiteID] = - implode("/",$treeMgr->get_path($tsuiteID,null,'name')); - } - - $rows = null; - $external_id = $args->tcPrefix . $rf['external_id']; - $rows[$cols['tsuite']] = $tsuiteCache[$tsuiteID]; - - $name = htmlspecialchars("{$external_id}:{$rf['name']}",ENT_QUOTES); - - $fo = !is_null($forceFormat) ? $forceFormat : $args->format; - if ($fo == FORMAT_HTML) { - $rows[$cols['link']] = ""; - - if($args->addOpAccess) { - $rows[$cols['link']] .= - "" . - $hist_img_tag . - "" . - $edit_img_tag; - } - $rows[$cols['link']] .= $name; - } else { - $rows[$cols['link']] = "{$external_id}:{$rf['name']}"; - } - - $rows[$cols['platform']] = $gui->platforms[$platformID]; - - if ($gui->options->testPriorityEnabled) { - if ($rf['urg_imp'] >= $priorityCfg->threshold['high']) { - $rf['priority_level'] = HIGH; - } else if( $rf['urg_imp'] < $priorityCfg->threshold['low']) { - $rf['priority_level'] = LOW; - } else { - $rf['priority_level'] = MEDIUM; - } - - switch($fo) { - case FORMAT_XLS: - // We need the human readable value, not the code - $rows[$cols['priority']] = - $args->cfg['priority'][$rf['priority_level']]; - break; - - default: - // Raw Code the human readable value will be - // constructed while rendering. - $rows[$cols['priority']] = $rf['priority_level']; - break; - } - } - - $statusVerbose = $labels[$rf['status']] . - sprintf($labels['versionTag'],$rf['version']); - - if ($fo == FORMAT_HTML) { - $execOut = array('text' => '', 'value' => '', 'cssClass' => ''); - $execOut['text'] = $statusVerbose; - $execOut['value'] = $rf['status']; - $execOut['cssClass'] = $gui->map_status_css[$rf['status']]; - $rows[] = $execOut; - } - else { - $rows[] = $statusVerbose; - } - $nv = ''; - if (isset($rf['execution_notes'])) { - $nv = is_null($rf['execution_notes']) ? '' : - $rf['execution_notes']; - } - if( $fo == FORMAT_XLS) { - $rows[] = $nv; - } - if( $fo == FORMAT_HTML) { - $rows[] = ['text' => $nv]; - } - $gui->matrix[] = $rows; - } // $platformSet - } // $tcaseSet - } // $tsuiteSet -} - - -/** - * - */ -function initLblSpreadsheet() { - $lbl = init_labels(array('title_test_suite_name' => null, - 'platform' => null, - 'priority' => null, - 'title_test_case_title' => null, - 'test_exec_by' => null, - 'notes' => null, - 'date_time_run' => null, - 'execution_duration' => null, - 'testproject' => null, - 'generated_by_TestLink_on' => null, - 'testplan' => null, - 'result_on_last_build' => null, - 'latest_execution' => null, - 'assigned_to' => null, - 'latest_exec_notes' => null, - 'important_notice' => null)); - return $lbl; -} - -/** - * - */ -function initStyleSpreadsheet() { - $style = array(); - $style['ReportContext'] = array('font' => array('bold' => true)); - $style['DataHeader'] = array('font' => array('bold' => true), - 'borders' => array('outline' => array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - return $style; -} - -/** - * - */ -function setCellRangeSpreadsheet() { - $cr = range('A','Z'); - $crLen = count($cr); - for($idx = 0; $idx < $crLen; $idx++) { - for($jdx = 0; $jdx < $crLen; $jdx++) { - $cr[] = $cr[$idx] . $cr[$jdx]; - } - } - return $cr; -} - -/** - * - */ -function xlsStepOne(&$oj,$style,&$lbl,&$gui) { - $dummy = ''; - $lines2write = array(array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['important_notice'],$gui->report_details), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time()))); - - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) { - $cdx = $zdx+1; - $oj->setActiveSheetIndex(0)->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - $cellArea .= "A{$cdx}"; - $oj->getActiveSheet()->getStyle($cellArea)->applyFromArray($style['ReportContext']); - - return $lines2write; -} - -/** - * - */ -function initCols() -{ - $tcols = array('tsuite','link','platform','priority', - 'latest_exec','latest_exec_notes'); - $cols = array_flip($tcols); - return $cols; -} - -/** - * - */ -function doProcess(&$dbH,&$args,&$gui,&$metricsMgr) -{ - $opt = array('getExecutionNotes' => true); - - $opt = array('output' => 'array'); - $neverRunOnPP = (array)$metricsMgr->getNeverRunOnSinglePlatform($args->tplan_id,$args->platform_id); - - $execStatus = (array)$metricsMgr->getLatestExecOnSinglePlatformMatrix($args->tplan_id,$args->platform_id,$opt); - - $allExec = array(); - foreach ($neverRunOnPP as $elem) { - $allExec[] = $elem; - } - foreach ($execStatus as $elem) { - $allExec[] = $elem; - } - - // Every Test suite a row on matrix to display will be created - $args->cols = initCols(); - if( !is_null($neverRunOnPP) || !is_null($execStatus)) { - buildDataSet($dbH,$args,$gui,$allExec,$gui->labels); - } - - $renderHTML = false; - - switch($args->format) { - case FORMAT_XLS: - createSpreadsheet($gui,$args,$args->getSpreadsheetBy); - break; - - default: - $renderHTML = true; - $gui->tableSet[] = buildMatrix($gui, $args); - break; - } -} - diff --git a/lib/results/resultsTCFlat.php b/lib/results/resultsTCFlat.php index a7da8bab4d..a5c318a7d3 100644 --- a/lib/results/resultsTCFlat.php +++ b/lib/results/resultsTCFlat.php @@ -1,559 +1,629 @@ - -* -* Test Results on simple spreadsheet format -* -* -* @internal revisions -* @since 1.9.15 -*/ -require('../../config.inc.php'); -require_once('../../third_party/codeplex/PHPExcel.php'); // Must be included BEFORE common.php -require_once('common.php'); -require_once('displayMgr.php'); - -$timerOn = microtime(true); // will be used to compute elapsed time -$templateCfg = templateConfiguration(); - -$smarty = new TLSmarty; -$args = init_args($db); - -$metricsMgr = new tlTestPlanMetrics($db); -$tplan_mgr = &$metricsMgr; // displayMemUsage('START' . __FILE__); - -list($gui,$labels,$cfg) = initializeGui($db,$args,$smarty->getImages(),$tplan_mgr); -$args->cfg = $cfg; -$mailCfg = buildMailCfg($gui); - - -// We have faced a performance block due to an environment with -// 700 Builds and 1300 Test Cases on Test Plan -// This created a block on NOT RUN QUERY, but anyway will produce an enormous and -// unmanageable matrix on screen -// -// New way to process: -// ACTIVE Build Qty > 20 => Ask user to select builds he/she wants to use -// Cell Qty = (ACTIVE Build Qty x Test Cases on Test plan) > 2000 => said user I'm sorry -// -if( ($gui->activeBuildsQty <= $gui->matrixCfg->buildQtyLimit) || - $args->do_action == 'result') -{ - setUpBuilds($args,$gui); - - $tpl = $templateCfg->default_template; - $opt = null; - $buildSet = array('buildSet' => $args->builds->idSet); - - $opt = array('getExecutionNotes' => true, 'getTester' => true, - 'getUserAssignment' => true, 'output' => 'cumulative', - 'getExecutionTimestamp' => true, 'getExecutionDuration' => true); - - $execStatus = $metricsMgr->getExecStatusMatrixFlat($args->tplan_id,$buildSet,$opt); - - - $metrics = $execStatus['metrics']; - $latestExecution = $execStatus['latestExec']; - - // Every Test suite a row on matrix to display will be created - // One matrix will be created for every platform that has testcases - $tcols = array('tsuite', 'tcase','version'); - if($gui->show_platforms) - { - $tcols[] = 'platform'; - } - $tcols[] = 'priority'; - $cols = array_flip($tcols); - $args->cols = $cols; - - if( !is_null($execStatus['metrics']) ) - { - buildSpreadsheetData($db,$args,$gui,$execStatus,$labels); - } - createSpreadsheet($gui,$args); - $args->format = FORMAT_XLS; -} -else -{ - // We need to ask user to do a choice - $tpl = 'resultsTCFlatLauncher.tpl'; - $gui->pageTitle = $labels['test_result_flat_filters']; - if($gui->matrixCfg->buildQtyLimit > 0) - { - $gui->userFeedback = $labels['too_much_data'] . '
    ' . - sprintf($labels['too_much_builds'],$gui->activeBuildsQty,$gui->matrixCfg->buildQtyLimit); - } - $args->format = FORMAT_HTML; -} - - -$timerOff = microtime(true); -$gui->elapsed_time = round($timerOff - $timerOn,2); - -$smarty->assign('gui',$gui); -displayReport($templateCfg->template_dir . $tpl, $smarty, $args->format, $mailCfg); - -/** - * - * - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "do_action" => array(tlInputParameter::STRING_N,5,10), - "build_set" => array(tlInputParameter::ARRAY_INT), - "buildListForExcel" => array(tlInputParameter::STRING_N,0,100), - "format" => array(tlInputParameter::INT_N)); - - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $args->addOpAccess = true; - if( !is_null($args->apikey) ) - { - //var_dump($args); - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - $args->addOpAccess = false; - $cerbero->method = null; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } - else - { - testlinkInitPage($dbHandler,false,false,"checkRights"); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - if($args->tproject_id <= 0) - { - $msg = __FILE__ . '::' . __FUNCTION__ . " :: Invalid Test Project ID ({$args->tproject_id})"; - throw new Exception($msg); - } - - switch($args->format) - { - case FORMAT_XLS: - if($args->buildListForExcel != '') - { - $args->build_set = explode(',',$args->buildListForExcel); - } - break; - } - - - $args->user = $_SESSION['currentUser']; - $args->basehref = $_SESSION['basehref']; - - return $args; + + * + * Test Results on simple spreadsheet format + * + * + * @internal revisions + * @since 1.9.15 + */ +require_once '../../config.inc.php'; +require_once '../../third_party/codeplex/PHPExcel.php'; // Must be included BEFORE common.php +require_once 'common.php'; +require_once 'displayMgr.php'; + +$timerOn = microtime(true); // will be used to compute elapsed time +$templateCfg = templateConfiguration(); + +$smarty = new TLSmarty(); +$args = initArgs($db); + +$metricsMgr = new tlTestPlanMetrics($db); +$tplan_mgr = &$metricsMgr; + +list ($gui, $labels, $cfg) = initializeGui($db, $args, $smarty->getImages(), + $tplan_mgr); +$args->cfg = $cfg; +$mailCfg = buildMailCfg($gui); + +// We have faced a performance block due to an environment with +// 700 Builds and 1300 Test Cases on Test Plan +// This created a block on NOT RUN QUERY, but anyway will produce an enormous and +// unmanageable matrix on screen +// +// New way to process: +// ACTIVE Build Qty > 20 => Ask user to select builds he/she wants to use +// Cell Qty = (ACTIVE Build Qty x Test Cases on Test plan) > 2000 => said user I'm sorry +// +if (($gui->activeBuildsQty <= $gui->matrixCfg->buildQtyLimit) || + $args->do_action == 'result') { + setUpBuilds($args, $gui); + + $tpl = $templateCfg->default_template; + $opt = null; + $buildSet = array( + 'buildSet' => $args->builds->idSet + ); + + $opt = array( + 'getExecutionNotes' => true, + 'getTester' => true, + 'getUserAssignment' => true, + 'output' => 'cumulative', + 'getExecutionTimestamp' => true, + 'getExecutionDuration' => true + ); + + $execStatus = $metricsMgr->getExecStatusMatrixFlat($args->tplan_id, + $buildSet, $opt); + + $metrics = $execStatus['metrics']; + $latestExecution = $execStatus['latestExec']; + + // Every Test suite a row on matrix to display will be created + // One matrix will be created for every platform that has testcases + $tcols = array( + 'tsuite', + 'tcase', + 'version' + ); + if ($gui->show_platforms) { + $tcols[] = 'platform'; + } + $tcols[] = 'priority'; + $cols = array_flip($tcols); + $args->cols = $cols; + + if (! is_null($execStatus['metrics'])) { + buildSpreadsheetData($db, $args, $gui, $execStatus, $labels); + } + createSpreadsheet($gui); + $args->format = FORMAT_XLS; +} else { + // We need to ask user to do a choice + $tpl = 'resultsTCFlatLauncher.tpl'; + $gui->pageTitle = $labels['test_result_flat_filters']; + if ($gui->matrixCfg->buildQtyLimit > 0) { + $gui->userFeedback = $labels['too_much_data'] . '
    ' . + sprintf($labels['too_much_builds'], $gui->activeBuildsQty, + $gui->matrixCfg->buildQtyLimit); + } + $args->format = FORMAT_HTML; +} + +$timerOff = microtime(true); +$gui->elapsed_time = round($timerOff - $timerOn, 2); + +$smarty->assign('gui', $gui); +displayReport($templateCfg->template_dir . $tpl, $smarty, $args->format, + $mailCfg); + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "do_action" => array( + tlInputParameter::STRING_N, + 5, + 10 + ), + "build_set" => array( + tlInputParameter::ARRAY_INT + ), + "buildListForExcel" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "format" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->addOpAccess = true; + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + + if ($args->tproject_id <= 0) { + $msg = __FILE__ . '::' . __FUNCTION__ . + " :: Invalid Test Project ID ({$args->tproject_id})"; + throw new Exception($msg); + } + + switch ($args->format) { + case FORMAT_XLS: + if ($args->buildListForExcel != '') { + $args->build_set = explode(',', $args->buildListForExcel); + } + break; + } + + $args->user = $_SESSION['currentUser']; + $args->basehref = $_SESSION['basehref']; + + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @param stdClass $context + * @return string + */ +function checkRights(&$db, &$user, $context = null) +{ + if (is_null($context)) { + $context = new stdClass(); + $context->tproject_id = $context->tplan_id = null; + $context->getAccessAttr = false; + } + + return $user->hasRightOnProj($db, 'testplan_metrics', $context->tproject_id, + $context->tplan_id, $context->getAccessAttr); +} + +/** + * + * @param stdClass $guiObj + * @return stdClass + */ +function buildMailCfg(&$guiObj) +{ + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param array $imgSet + * @param tlTestPlanMetrics $tplanMgr + * @return array + */ +function initializeGui(&$dbHandler, &$argsObj, $imgSet, &$tplanMgr) +{ + $cfg = array( + 'results' => config_get('results'), + 'urgency' => config_get('urgency'), + 'tcase' => config_get('testcase_cfg') + ); + + $guiObj = new stdClass(); + $guiObj->map_status_css = null; + $guiObj->title = lang_get('title_test_report_all_builds'); + $guiObj->printDate = ''; + $guiObj->matrix = array(); + + $guiObj->platforms = (array) $tplanMgr->getPlatforms($argsObj->tplan_id, + array( + 'outputFormat' => 'map' + )); + $guiObj->show_platforms = (count($guiObj->platforms) > 0); + $guiObj->img = new stdClass(); + $guiObj->img->exec = $imgSet['exec_icon']; + $guiObj->img->edit = $imgSet['edit_icon']; + $guiObj->img->history = $imgSet['history_small']; + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->tplan_id = $argsObj->tplan_id; + $guiObj->apikey = $argsObj->apikey; + + $tproject_mgr = new testproject($dbHandler); + $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); + $argsObj->prefix = $tproject_info['prefix']; + $argsObj->tcPrefix = $tproject_info['prefix'] . $cfg['tcase']->glue_character; + $argsObj->tprojectOpt = $tproject_info['opt']; + + $guiObj->options = new stdClass(); + $guiObj->options->testPriorityEnabled = $tproject_info['opt']->testPriorityEnabled; + unset($tproject_mgr); + + $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); + $guiObj->tplan_name = $tplan_info['name']; + $guiObj->tproject_name = $tproject_info['name']; + + $l18n = init_labels( + array( + 'design' => null, + 'execution' => null, + 'history' => 'execution_history', + 'test_result_flat_filters' => null, + 'too_much_data' => null, + 'too_much_builds' => null, + 'result_on_last_build' => null, + 'versionTag' => 'tcversion_indicator', + 'execution_type_manual' => null, + 'execution_type_auto' => null + )); + + $l18n['not_run'] = lang_get($cfg['results']['status_label']['not_run']); + + $guiObj->matrixCfg = config_get('resultMatrixReport'); + $guiObj->buildInfoSet = $tplanMgr->get_builds($argsObj->tplan_id, + testplan::ACTIVE_BUILDS, null, + array( + 'orderBy' => $guiObj->matrixCfg->buildOrderByClause + )); + $guiObj->activeBuildsQty = count($guiObj->buildInfoSet); + + // hmm need to understand if this can be removed + if ($guiObj->matrixCfg->buildColumns['latestBuildOnLeft']) { + $guiObj->buildInfoSet = array_reverse($guiObj->buildInfoSet); + } + + foreach ($cfg['results']['code_status'] as $code => $verbose) { + if (isset($cfg['results']['status_label'][$verbose])) { + $l18n[$code] = lang_get($cfg['results']['status_label'][$verbose]); + $guiObj->map_status_css[$code] = $cfg['results']['code_status'][$code] . + '_text'; + } + } + + $xxx = config_get('urgency'); + foreach ($xxx['code_label'] as $code => $label) { + $cfg['priority'][$code] = lang_get($label); + } + + return array( + $guiObj, + $l18n, + $cfg + ); +} + +/** + * + * @param stdClass $gui + * @param stdClass $args + */ +function createSpreadsheet($gui) +{ + $lbl = init_labels( + array( + 'title_test_suite_name' => null, + 'platform' => null, + 'priority' => null, + 'build' => null, + 'title_test_case_title' => null, + 'test_exec_by' => null, + 'notes' => null, + 'date_time_run' => null, + 'execution_duration' => null, + 'testproject' => null, + 'generated_by_TestLink_on' => null, + 'testplan' => null, + 'result_on_last_build' => null, + 'last_execution' => null, + 'assigned_to' => null, + 'tcexec_latest_exec_result' => null, + 'version' => null, + 'execution_type' => null + )); + + // contribution to have more than 26 columns + $cellRange = range('A', 'Z'); + $cellRangeLen = count($cellRange); + for ($idx = 0; $idx < $cellRangeLen; $idx ++) { + for ($jdx = 0; $jdx < $cellRangeLen; $jdx ++) { + $cellRange[] = $cellRange[$idx] . $cellRange[$jdx]; + } + } + + $styleReportContext = array( + 'font' => array( + 'bold' => true + ) + ); + $styleDataHeader = array( + 'font' => array( + 'bold' => true + ), + 'borders' => array( + 'outline' => array( + 'style' => PHPExcel_Style_Border::BORDER_MEDIUM + ), + 'vertical' => array( + 'style' => PHPExcel_Style_Border::BORDER_THIN + ) + ), + 'fill' => array( + 'type' => PHPExcel_Style_Fill::FILL_SOLID, + 'startcolor' => array( + 'argb' => 'FF9999FF' + ) + ) + ); + $dummy = ''; + $lines2write = array( + array( + $lbl['testproject'], + $gui->tproject_name + ), + array( + $lbl['testplan'], + $gui->tplan_name + ), + array( + $lbl['generated_by_TestLink_on'], + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', time()) + ) + ); + + $objPHPExcel = new PHPExcel(); + $cellArea = "A1:"; + foreach ($lines2write as $zdx => $fields) { + $cdx = $zdx + 1; + $objPHPExcel->setActiveSheetIndex(0) + ->setCellValue("A{$cdx}", current($fields)) + ->setCellValue("B{$cdx}", end($fields)); + } + $cellArea .= "A{$cdx}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($styleReportContext); + + // Step 2 + // data is organized with following columns $dataHeader[] + // Test suite + // Test case + // Test case version (for humans) + // [Platform] => if any exists + // + // Priority ===> Just discovered that we have choosen to make this column + // displayabled or not according test project configuration + // IMHO has no sense work without priority + // + // Build + // Assigned To + // Exec result + // Date + // Tester + // Notes + // Duration + + // + // ?? Exec result on ON LATEST CREATED Build + // ?? Latest Execution result (Hmm need to explain better) + // + $dataHeader = array( + $lbl['title_test_suite_name'], + $lbl['title_test_case_title'], + $lbl['version'] + ); + + if (! is_null($gui->platforms)) { + $dataHeader[] = $lbl['platform']; + } + + if ($gui->options->testPriorityEnabled) { + $dataHeader[] = $lbl['priority']; + } + + $gui->filterFeedback = null; + $dataHeader[] = $lbl['build']; + $dataHeader[] = $lbl['assigned_to']; + $dataHeader[] = $lbl['tcexec_latest_exec_result']; + $dataHeader[] = $lbl['date_time_run']; + $dataHeader[] = $lbl['test_exec_by']; + $dataHeader[] = $lbl['notes']; + $dataHeader[] = $lbl['execution_duration']; + $dataHeader[] = $lbl['execution_type']; + + $startingRow = count($lines2write) + 2; // MAGIC + $cellArea = "A{$startingRow}:"; + foreach ($dataHeader as $zdx => $field) { + $cellID = $cellRange[$zdx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + $cellAreaEnd = $cellRange[$zdx]; + } + + $cellArea .= "{$cellAreaEnd}{$startingRow}"; + $objPHPExcel->getActiveSheet() + ->getStyle($cellArea) + ->applyFromArray($styleDataHeader); + + $startingRow ++; + + $qta_loops = count($gui->matrix); + + for ($idx = 0; $idx < $qta_loops; $idx ++) { + foreach ($gui->matrix[$idx] as $ldx => $field) { + $cellID = $cellRange[$ldx] . $startingRow; + $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); + } + $startingRow ++; + } + + // Final step + $objPHPExcel->setActiveSheetIndex(0); + $settings = array(); + $settings['Excel2007'] = array( + 'ext' => '.xlsx', + 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ); + $settings['Excel5'] = array( + 'ext' => '.xls', + 'Content-Type' => 'application/vnd.ms-excel' + ); + + $xlsType = 'Excel5'; + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); + + $tmpfname = tempnam(config_get('temp_dir'), "resultsTCFlat.tmp"); + $objWriter->save($tmpfname); + + $content = file_get_contents($tmpfname); + unlink($tmpfname); + $f2d = 'resultsTCFlat_' . $gui->tproject_name . '_' . $gui->tplan_name . + $settings[$xlsType]['ext']; + downloadContentsToFile($content, $f2d, + array( + 'Content-Type' => $settings[$xlsType]['Content-Type'] + )); + exit(); +} + +/** + * + * @param stdClass $args + * @param stdClass $gui + */ +function setUpBuilds(&$args, &$gui) +{ + $args->builds = new stdClass(); + + if (is_null($args->build_set)) { + $args->builds->idSet = null; + + $gui->buildListForExcel = ''; + $gui->filterApplied = false; + if (! is_null($gui->buildInfoSet)) { + $args->builds->idSet = array_keys($gui->buildInfoSet); + } + } else { + $args->builds->idSet = array_keys(array_flip($args->build_set)); + $gui->filterApplied = true; + $gui->buildListForExcel = implode(',', $args->builds->idSet); + } + + $args->builds->latest = new stdClass(); + $args->builds->latest->id = end($args->builds->idSet); + $args->builds->latest->name = $gui->buildInfoSet[$args->builds->latest->id]['name']; +} + +/** + * + * @param database $db + * @param stdClass $args + * @param stdClass $gui + * @param array $exec + * @param array $labels + */ +function buildSpreadsheetData(&$db, &$args, &$gui, &$exec, $labels) +{ + $userSet = getUsersForHtmlOptions($db, null, null, null, null, + array( + 'userDisplayFormat' => '%first% %last%' + )); + + $det = array( + TESTCASE_EXECUTION_TYPE_MANUAL => $labels['execution_type_manual'], + TESTCASE_EXECUTION_TYPE_AUTO => $labels['execution_type_auto'] + ); + + $metrics = $exec['metrics']; + + $cols = $args->cols; + + /* + * tsuite_id 741 + * tcase_id 742 => name TC-1A + * tcversion_id 743 + * platform_id 16 => NEED TO DECODE + * build_id 19 => NEED TO DECODE + * version 1 + * external_id 1 + * executions_id 64 + * status f => NEED TO DECODE + * execution_notes [empty string] + * tester_id 1 => NEED TO DECODE + * execution_ts 2015-05-23 16:38:22 + * execution_duration NULL + * user_id 1 => NEED TO DECODE + * urg_imp 4 => NEED TO DECODE + * execution_type => NEED TO DECODE + */ + + $loop2do = count($metrics); + + for ($ix = 0; $ix < $loop2do; $ix ++) { + $rows = array(); + + $rows[$cols['tsuite']] = $metrics[$ix]['suiteName']; + $eid = $args->tcPrefix . $metrics[$ix]['external_id']; + $rows[$cols['tcase']] = htmlspecialchars( + "{$eid}:{$metrics[$ix]['name']}", ENT_QUOTES); + + $rows[$cols['version']] = $metrics[$ix]['version']; + if ($gui->show_platforms) { + $rows[$cols['platform']] = $gui->platforms[$metrics[$ix]['platform_id']]; + } + + if ($gui->options->testPriorityEnabled) { + $rows[$cols['priority']] = $args->cfg['priority'][$metrics[$ix]['priority_level']]; + } + + // build,assigned to,exec result,data,tested by,notes,duration + $rows[] = $gui->buildInfoSet[$metrics[$ix]['build_id']]['name']; + + $u = ""; + if (isset($userSet, $metrics[$ix]['user_id'])) { + $u = $userSet[$metrics[$ix]['user_id']]; + } + $rows[] = $u; + $rows[] = $labels[$metrics[$ix]['status']]; + $rows[] = $metrics[$ix]['execution_ts']; + + $u = ""; + if (isset($userSet, $metrics[$ix]['tester_id'])) { + $u = $userSet[$metrics[$ix]['tester_id']]; + } + $rows[] = $u; + $rows[] = $metrics[$ix]['execution_notes']; + $rows[] = $metrics[$ix]['execution_duration']; + $rows[] = isset($det[$metrics[$ix]['exec_type']]) ? $det[$metrics[$ix]['exec_type']] : 'not configured'; + + $gui->matrix[] = $rows; + } } - -/** - * - * - */ -function checkRights(&$db,&$user,$context = null) -{ - if(is_null($context)) - { - $context = new stdClass(); - $context->tproject_id = $context->tplan_id = null; - $context->getAccessAttr = false; - } - - $check = $user->hasRight($db,'testplan_metrics',$context->tproject_id,$context->tplan_id,$context->getAccessAttr); - return $check; -} - -/** - * - * - */ -function buildMailCfg(&$guiObj) -{ - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; -} - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,$imgSet,&$tplanMgr) -{ - - $cfg = array('results' => config_get('results'), 'urgency' => config_get('urgency'), - 'tcase' => config_get('testcase_cfg')); - - $guiObj = new stdClass(); - $guiObj->map_status_css = null; - $guiObj->title = lang_get('title_test_report_all_builds'); - $guiObj->printDate = ''; - $guiObj->matrix = array(); - - $guiObj->platforms = $tplanMgr->getPlatforms($argsObj->tplan_id,array('outputFormat' => 'map')); - $guiObj->show_platforms = !is_null($guiObj->platforms); - - $guiObj->img = new stdClass(); - $guiObj->img->exec = $imgSet['exec_icon']; - $guiObj->img->edit = $imgSet['edit_icon']; - $guiObj->img->history = $imgSet['history_small']; - - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->tplan_id = $argsObj->tplan_id; - - $guiObj->apikey = $argsObj->apikey; - - - $tproject_mgr = new testproject($dbHandler); - $tproject_info = $tproject_mgr->get_by_id($argsObj->tproject_id); - $argsObj->prefix = $tproject_info['prefix']; - $argsObj->tcPrefix = $tproject_info['prefix'] . $cfg['tcase']->glue_character; - $argsObj->tprojectOpt = $tproject_info['opt']; - - $guiObj->options = new stdClass(); - $guiObj->options->testPriorityEnabled = $tproject_info['opt']->testPriorityEnabled; - unset($tproject_mgr); - - $tplan_info = $tplanMgr->get_by_id($argsObj->tplan_id); - $guiObj->tplan_name = $tplan_info['name']; - $guiObj->tproject_name = $tproject_info['name']; - - $l18n = init_labels(array('design' => null, 'execution' => null, 'history' => 'execution_history', - 'test_result_flat_filters' => null, 'too_much_data' => null,'too_much_builds' => null, - 'result_on_last_build' => null, 'versionTag' => 'tcversion_indicator', - 'execution_type_manual' => null, - 'execution_type_auto' => null) ); - - $l18n['not_run']=lang_get($cfg['results']['status_label']['not_run']); - - - $guiObj->matrixCfg = config_get('resultMatrixReport'); - $guiObj->buildInfoSet = $tplanMgr->get_builds($argsObj->tplan_id, testplan::ACTIVE_BUILDS,null, - array('orderBy' => $guiObj->matrixCfg->buildOrderByClause)); - $guiObj->activeBuildsQty = count($guiObj->buildInfoSet); - - - // hmm need to understand if this can be removed - if ($guiObj->matrixCfg->buildColumns['latestBuildOnLeft']) - { - $guiObj->buildInfoSet = array_reverse($guiObj->buildInfoSet); - } - // ------------------------------------------------------------------------------- - - - foreach($cfg['results']['code_status'] as $code => $verbose) - { - if( isset($cfg['results']['status_label'][$verbose])) - { - $l18n[$code] = lang_get($cfg['results']['status_label'][$verbose]); - $guiObj->map_status_css[$code] = $cfg['results']['code_status'][$code] . '_text'; - } - } - - $xxx = config_get('urgency'); - foreach ($xxx['code_label'] as $code => $label) - { - $cfg['priority'][$code] = lang_get($label); - } - - return array($guiObj,$l18n,$cfg); -} - -/** - * - * - */ -function createSpreadsheet($gui,$args) -{ - - $lbl = init_labels(array('title_test_suite_name' => null,'platform' => null,'priority' => null, - 'build' => null, 'title_test_case_title' => null,'test_exec_by' => null, - 'notes' => null, 'date_time_run' => null, 'execution_duration' => null, - 'testproject' => null,'generated_by_TestLink_on' => null,'testplan' => null, - 'result_on_last_build' => null,'last_execution' => null, - 'assigned_to' => null,'tcexec_latest_exec_result' => null, - 'version' => null,'execution_type' => null)); - - $buildIDSet = $args->builds->idSet; - - // contribution to have more than 26 columns - $cellRange = range('A','Z'); - $cellRangeLen = count($cellRange); - for($idx = 0; $idx < $cellRangeLen; $idx++) - { - for($jdx = 0; $jdx < $cellRangeLen; $jdx++) - { - $cellRange[] = $cellRange[$idx] . $cellRange[$jdx]; - } - } - - $styleReportContext = array('font' => array('bold' => true)); - $styleDataHeader = array('font' => array('bold' => true), - 'borders' => array('outline' => array('style' => PHPExcel_Style_Border::BORDER_MEDIUM), - 'vertical' => array('style' => PHPExcel_Style_Border::BORDER_THIN)), - 'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID, - 'startcolor' => array( 'argb' => 'FF9999FF')) - ); - $dummy = ''; - $lines2write = array(array($lbl['testproject'],$gui->tproject_name), - array($lbl['testplan'],$gui->tplan_name), - array($lbl['generated_by_TestLink_on'], - localize_dateOrTimeStamp(null,$dummy,'timestamp_format',time()))); - - $objPHPExcel = new PHPExcel(); - $cellArea = "A1:"; - foreach($lines2write as $zdx => $fields) - { - $cdx = $zdx+1; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue("A{$cdx}", current($fields)) - ->setCellValue("B{$cdx}", end($fields)); - } - $cellArea .= "A{$cdx}"; - $objPHPExcel->getActiveSheet()->getStyle($cellArea)->applyFromArray($styleReportContext); - - - // Step 2 - // data is organized with following columns $dataHeader[] - // Test suite - // Test case - // Test case version (for humans) - // [Platform] => if any exists - // - // Priority ===> Just discovered that we have choosen to make this column - // displayabled or not according test project configuration - // IMHO has no sense work without priority - // - // Build - // Assigned To - // Exec result - // Date - // Tester - // Notes - // Duration - - // - // ?? Exec result on ON LATEST CREATED Build - // ?? Latest Execution result (Hmm need to explain better) - // - $dataHeader = array($lbl['title_test_suite_name'], - $lbl['title_test_case_title'], - $lbl['version']); - - if( $showPlatforms = !is_null($gui->platforms) ) - { - $dataHeader[] = $lbl['platform']; - } - - if($gui->options->testPriorityEnabled) - { - $dataHeader[] = $lbl['priority']; - } - - $gui->filterFeedback = null; - $dataHeader[] = $lbl['build']; - $dataHeader[] = $lbl['assigned_to']; - $dataHeader[] = $lbl['tcexec_latest_exec_result']; - $dataHeader[] = $lbl['date_time_run']; - $dataHeader[] = $lbl['test_exec_by']; - $dataHeader[] = $lbl['notes']; - $dataHeader[] = $lbl['execution_duration']; - $dataHeader[] = $lbl['execution_type']; - - $startingRow = count($lines2write) + 2; // MAGIC - $cellArea = "A{$startingRow}:"; - foreach($dataHeader as $zdx => $field) - { - $cellID = $cellRange[$zdx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - $cellAreaEnd = $cellRange[$zdx]; - } - - $cellArea .= "{$cellAreaEnd}{$startingRow}"; - $objPHPExcel->getActiveSheet()->getStyle($cellArea)->applyFromArray($styleDataHeader); - - $startingRow++; - - $qta_loops = count($gui->matrix); - - for($idx = 0; $idx < $qta_loops; $idx++) - { - foreach($gui->matrix[$idx] as $ldx => $field) - { - $cellID = $cellRange[$ldx] . $startingRow; - $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellID, $field); - } - $startingRow++; - } - - - - // Final step - $objPHPExcel->setActiveSheetIndex(0); - $settings = array(); - $settings['Excel2007'] = array('ext' => '.xlsx', - 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); - $settings['Excel5'] = array('ext' => '.xls', - 'Content-Type' => 'application/vnd.ms-excel'); - - $xlsType = 'Excel5'; - $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, $xlsType); - - $tmpfname = tempnam(config_get('temp_dir'),"resultsTCFlat.tmp"); - $objWriter->save($tmpfname); - - $content = file_get_contents($tmpfname); - unlink($tmpfname); - $f2d = 'resultsTCFlat_'. $gui->tproject_name . '_' . $gui->tplan_name . $settings[$xlsType]['ext']; - downloadContentsToFile($content,$f2d,array('Content-Type' => $settings[$xlsType]['Content-Type'])); - exit(); -} - - -/** - * - */ -function setUpBuilds(&$args,&$gui) -{ - $args->builds = new stdClass(); - - if( is_null($args->build_set) ) - { - $args->builds->idSet = null; - - $gui->buildListForExcel = ''; - $gui->filterApplied = false; - if( !is_null($gui->buildInfoSet) ) - { - $args->builds->idSet = array_keys($gui->buildInfoSet); - } - } - else - { - $args->builds->idSet = array_keys(array_flip($args->build_set)); - $gui->filterApplied = true; - $gui->buildListForExcel = implode(',',$args->builds->idSet); - } - - $args->builds->latest = new stdClass(); - $args->builds->latest->id = end($args->builds->idSet); - $args->builds->latest->name = $gui->buildInfoSet[$args->builds->latest->id]['name']; -} - - -/** - * - * - */ -function buildSpreadsheetData(&$db,&$args,&$gui,&$exec,$labels) -{ - $userSet = getUsersForHtmlOptions($db,null,null,null,null, - array('userDisplayFormat' => '%first% %last%')); - - $det = array(TESTCASE_EXECUTION_TYPE_MANUAL => - $labels['execution_type_manual'], - TESTCASE_EXECUTION_TYPE_AUTO => - $labels['execution_type_auto']); - - $metrics = $exec['metrics']; - $latestExecution = $exec['latestExec']; - $cols = $args->cols; - -/* -tsuite_id 741 -tcase_id 742 => name TC-1A -tcversion_id 743 -platform_id 16 => NEED TO DECODE -build_id 19 => NEED TO DECODE -version 1 -external_id 1 -executions_id 64 -status f => NEED TO DECODE -execution_notes [empty string] -tester_id 1 => NEED TO DECODE -execution_ts 2015-05-23 16:38:22 -execution_duration NULL -user_id 1 => NEED TO DECODE -urg_imp 4 => NEED TO DECODE -execution_type => NEED TO DECODE -*/ - - $loop2do = count($metrics); - - $uk2 = array('user_id','tester_id'); - - for($ix=0; $ix < $loop2do; $ix++) - { - $rows = array(); - - $rows[$cols['tsuite']] = $metrics[$ix]['suiteName']; - $eid = $args->tcPrefix . $metrics[$ix]['external_id']; - $rows[$cols['tcase']] = - htmlspecialchars("{$eid}:{$metrics[$ix]['name']}",ENT_QUOTES); - - $rows[$cols['version']] = $metrics[$ix]['version']; - - if ($gui->show_platforms) - { - $rows[$cols['platform']] = $gui->platforms[$metrics[$ix]['platform_id']]; - } - - if($gui->options->testPriorityEnabled) - { - $rows[$cols['priority']] = $args->cfg['priority'][$metrics[$ix]['priority_level']]; - } - - // build,assigned to,exec result,data,tested by,notes,duration - $rows[] = $gui->buildInfoSet[$metrics[$ix]['build_id']]['name']; - - $u = ""; - if(isset($userSet,$metrics[$ix]['user_id'])) - { - $u = $userSet[$metrics[$ix]['user_id']]; - } - $rows[] = $u; - - // $rows[] = $args->cfg['results']['code_status'][$metrics[$ix]['status']]; - $rows[] = $labels[$metrics[$ix]['status']]; - $rows[] = $metrics[$ix]['execution_ts']; - - $u = ""; - if(isset($userSet,$metrics[$ix]['tester_id'])) - { - $u = $userSet[$metrics[$ix]['tester_id']]; - } - $rows[] = $u; - - $rows[] = $metrics[$ix]['execution_notes']; - $rows[] = $metrics[$ix]['execution_duration']; - - $rows[] = - isset($det[$metrics[$ix]['exec_type']]) ? - $det[$metrics[$ix]['exec_type']] : 'not configured'; - - $gui->matrix[] = $rows; - } -} \ No newline at end of file diff --git a/lib/results/tcCreatedPerUserOnTestProject.php b/lib/results/tcCreatedPerUserOnTestProject.php index 42dfceba7d..58471ab34d 100644 --- a/lib/results/tcCreatedPerUserOnTestProject.php +++ b/lib/results/tcCreatedPerUserOnTestProject.php @@ -1,472 +1,570 @@ -getImages(); -$templateCfg = templateConfiguration(); -$args = init_args($db); -$gui = initializeGui($db,$args,$imgSet); -$tpl = $templateCfg->default_template; - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $tpl); - -/** -* initialize Gui -*/ -function initializeGui(&$dbHandler,&$args,$images) { - $gui = new stdClass(); - $gui->images = $images; - $gui->glueChar = config_get('testcase_cfg')->glue_character; - $gui->tproject_id = $args->tproject_id; - $gui->tproject_name = $args->tproject_name; - $gui->warning_msg = ''; - $gui->tableSet = null; - - $gui->l18n = init_labels( - array('tcversion_indicator' => null,'goto_testspec' => null, - 'version' => null, - 'testplan' => null, 'assigned_tc_overview' => null, - 'testcases_created_per_user' => null, - 'design' => null, 'execution' => null, - 'execution_history' => null, - 'testproject' => null,'generated_by_TestLink_on' => null, - 'no_records_found' => null, - 'low' => null, 'medium' => null, 'high' => null)); - - $gui->pageTitle = sprintf($gui->l18n['testcases_created_per_user'],$gui->tproject_name); - $gui->context = $gui->l18n['testproject'] . ': ' . $args->tproject_name; - - switch($args->do_action) { - case 'uinput': - default: - initializeGuiForInput($dbHandler,$args,$gui); - break; - - case 'result': - initializeGuiForInput($dbHandler,$args,$gui); - initializeGuiForResult($dbHandler,$args,$gui); - break; - - case 'csv': - initializeGuiForInput($dbHandler,$args,$gui); - initGuiForCSVDownload($dbHandler,$args,$gui); - break; - - } - - return $gui; +getImages(); +$templateCfg = templateConfiguration(); +$args = initArgs($db); +$gui = initializeGui($db, $args, $imgSet); +$tpl = $templateCfg->default_template; + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $tpl); + +/** + * initialize Gui + */ +function initializeGui(&$dbHandler, &$args, $images) +{ + $gui = new stdClass(); + $gui->images = $images; + $gui->glueChar = config_get('testcase_cfg')->glue_character; + $gui->tproject_id = $args->tproject_id; + $gui->tproject_name = $args->tproject_name; + $gui->warning_msg = ''; + $gui->tableSet = null; + + $gui->l18n = init_labels( + array( + 'tcversion_indicator' => null, + 'goto_testspec' => null, + 'version' => null, + 'testplan' => null, + 'assigned_tc_overview' => null, + 'testcases_created_per_user' => null, + 'design' => null, + 'execution' => null, + 'execution_history' => null, + 'testproject' => null, + 'generated_by_TestLink_on' => null, + 'no_records_found' => null, + 'low' => null, + 'medium' => null, + 'high' => null + )); + + $gui->pageTitle = sprintf($gui->l18n['testcases_created_per_user'], + $gui->tproject_name); + $gui->context = $gui->l18n['testproject'] . ': ' . $args->tproject_name; + + switch ($args->do_action) { + case 'result': + initializeGuiForInput($dbHandler, $args, $gui); + initializeGuiForResult($dbHandler, $args, $gui); + break; + + case 'csv': + initializeGuiForInput($dbHandler, $args, $gui); + initGuiForCSVDownload($dbHandler, $args, $gui); + break; + + case 'uinput': + default: + initializeGuiForInput($dbHandler, $args, $gui); + break; + } + + return $gui; +} + +/** + */ +function initializeGuiForResult(&$dbHandler, $argsObj, &$guiObj) +{ + $rcfg = config_get('results'); + $map_code_status = $rcfg['code_status']; + $map_status_label = $rcfg['status_label']; + $map_statuscode_css = array(); + + foreach ($map_code_status as $code => $status) { + if (isset($map_status_label[$status])) { + $label = $map_status_label[$status]; + $map_statuscode_css[$code] = array(); + $map_statuscode_css[$code]['translation'] = lang_get($label); + $map_statuscode_css[$code]['css_class'] = $map_code_status[$code] . + '_text'; + } + } + + $options = array(); + + // convert starttime to iso format for database usage + $dateFormat = config_get('date_format'); + $k2l = array( + 'selected_start_date' => 'startTime', + 'selected_end_date' => 'endTime' + ); + foreach ($k2l as $in => $opt) { + if (isset($argsObj->$in) && count($argsObj->$in) > 0) { + $dd = split_localized_date(current($argsObj->$in), $dateFormat); + if ($dd != null) { + $options[$opt] = $dd['year'] . "-" . $dd['month'] . "-" . + $dd['day']; + } + } + } + + $options['startTime'] .= " " . + (isset($argsObj->start_Hour) ? $argsObj->start_Hour : "00") . ":00:00"; + $options['endTime'] .= " " . + (isset($argsObj->end_Hour) ? $argsObj->end_Hour : "00") . ":59:59"; + + $mgr = new testproject($dbHandler); + $guiObj->searchDone = 1; + $guiObj->resultSet = $mgr->getTestCasesCreatedByUser($argsObj->tproject_id, + $argsObj->user_id, $options); + + if (! is_null($guiObj->resultSet)) { + // test case can exist multiple times, due to versions + $rows = array(); + list ($columns, $sortByColumn) = getColumnsDefinition(); + foreach ($guiObj->resultSet as $itemInfo) { + foreach ($itemInfo as $tcase) { + $cuRow = array(); + $tcase_id = $tcase['tcase_id']; + $tcversion_id = $tcase['tcversion_id']; + $cuRow[] = htmlspecialchars($tcase['login']); + $cuRow[] = htmlspecialchars($tcase['path']); + + // Create linked icons + $edit_link = "
    " . + "l18n['design']}\" src=\"{$guiObj->images['edit']}\" /> "; + + $cuRow[] = "" . $edit_link . + htmlspecialchars($tcase['external_id']) . " : " . + htmlspecialchars($tcase['tcase_name']) . + sprintf($guiObj->l18n['tcversion_indicator'], + $tcase['version']); + + $cuRow[] = $tcase['importance']; + $cuRow[] = $tcase['creation_ts']; + $cuRow[] = $tcase['modification_ts']; + $rows[] = $cuRow; + } + } + + // Different table ID for different reports: + $table_id = "tl_table_tc_created_per_user_"; + + // Add test plan ID to table ID + $table_id .= $guiObj->tproject_id; + + $matrix = new tlExtTable($columns, $rows, $table_id); + $matrix->title = $guiObj->l18n['testproject'] . ": " . + htmlspecialchars($guiObj->tproject_name); + // + // @TODO how this work ? + // $matrix->addCustomBehaviour(arg1, arg2) + // arg1: type that can be user defined, here we use 'importance'. + // arg2: array with methods + // 'render' => javascript render method (has to be present on inc_ext_table.tpl). + // 'filter' => piece of name used on several files + // 1. on exttable.class.php is used on buildColumns() to call build{piece}FilterOptions() + // 2. on ext_extensions a method named Ext.ux.grid.filter.{piece}Filter + // has to exists or rendering will fail + // + $matrix->addCustomBehaviour('importance', + array( + 'render' => 'importanceRenderer', + 'filter' => 'Importance' + )); + + // Default grouping by first column, which is user for overview, build otherwise + $matrix->setGroupByColumnName(lang_get($columns[0]['title_key'])); + + // Define toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + + $matrix->toolbarDefaultStateButton = false; + $matrix->toolbarRefreshButton = false; + + $matrix->setSortByColumnName($sortByColumn); + $matrix->sortDirection = 'DESC'; + + $guiObj->tableSet[$guiObj->tproject_id] = $matrix; + } +} + +/** + */ +function initGuiForCSVDownload(&$dbHandler, $argsObj, &$guiObj) +{ + $fromDate = current($argsObj->selected_start_date); + $toDate = current($argsObj->selected_end_date); + + $impCfg = config_get('importance'); + $impL10N = $impCfg['code_label']; + foreach ($impL10N as $ci => $lc) { + $impL10N[$ci] = lang_get($lc); + } + + $colHeaders = getCSVColumnsDefinition(); + + $options = array(); + + // convert starttime to iso format for database usage + $dateFormat = config_get('date_format'); + $k2l = array( + 'selected_start_date' => 'startTime', + 'selected_end_date' => 'endTime' + ); + foreach ($k2l as $in => $opt) { + if (isset($argsObj->$in) && count($argsObj->$in) > 0) { + $dd = split_localized_date(current($argsObj->$in), $dateFormat); + if ($dd != null) { + $options[$opt] = $dd['year'] . "-" . $dd['month'] . "-" . + $dd['day']; + } + } + } + + $options['startTime'] .= " " . + (isset($argsObj->start_Hour) ? $argsObj->start_Hour : "00") . ":00:00"; + $options['endTime'] .= " " . + (isset($argsObj->end_Hour) ? $argsObj->end_Hour : "00") . ":59:59"; + + $mgr = new testproject($dbHandler); + $guiObj->searchDone = 1; + $guiObj->resultSet = $mgr->getTestCasesCreatedByUser($argsObj->tproject_id, + $argsObj->user_id, $options); + + if (! is_null($guiObj->resultSet)) { + // test case can exist multiple times, due to versions + $rows = array(); + foreach ($guiObj->resultSet as $itemInfo) { + foreach ($itemInfo as $tcase) { + $cuRow = array(); + $cuRow[] = htmlspecialchars($tcase['login']); + $cuRow[] = htmlspecialchars($tcase['path']); + + $cuRow[] = htmlspecialchars($tcase['external_id']) . " : " . + htmlspecialchars($tcase['tcase_name']) . + sprintf($guiObj->l18n['tcversion_indicator'], + $tcase['version']); + + $cuRow[] = '(' . $tcase['importance'] . ') ' . + $impL10N[$tcase['importance']]; + + $cuRow[] = $tcase['creation_ts']; + $cuRow[] = $tcase['modification_ts']; + $cuRow[] = $fromDate; + $cuRow[] = $toDate; + + $rows[] = $cuRow; + } + } + + if (empty($rows)) { + return; + } + + $tmpfname = tempnam(sys_get_temp_dir(), "nuwow"); + unlink($tmpfname); + $csvfile = $tmpfname . '.csv'; + $fp = fopen($csvfile, 'w'); + fputcsv($fp, $colHeaders); + foreach ($rows as $fields) { + fputcsv($fp, $fields); + } + fclose($fp); + $fcont = file_get_contents($csvfile); + unlink($csvfile); + $f2d = __FILE__ . '.csv'; + $cty = array( + 'Content-Type' => 'text/csv' + ); + downloadContentsToFile($fcont, $f2d, $cty); + exit(); + } +} + +/** + */ +function initializeGuiForInput(&$dbHandler, $argsObj, &$guiObj) +{ + $room = config_get('gui_room'); + $guiObj->str_option_any = sprintf($room, lang_get('any')); + $guiObj->str_option_none = sprintf($room, lang_get('nobody')); + $guiObj->warning_msg = ''; + $guiObj->searchDone = 0; + + $guiObj->users = new stdClass(); + $guiObj->users->items = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, + array( + TL_USER_ANYBODY => $guiObj->str_option_any + )); + + $guiObj->user_id = intval($argsObj->user_id); + + $dateFormat = config_get('date_format'); + $cfg = config_get('reportsCfg'); + $now = time(); + + if (is_null($argsObj->selected_start_date)) { + $guiObj->selected_start_date = @strftime($dateFormat, + $now - ($cfg->start_date_offset)); + $guiObj->selected_start_time = $cfg->start_time; + + $guiObj->selected_end_date = @strftime($dateFormat, $now); + $guiObj->selected_end_time = null; + } else { + $guiObj->selected_start_date = $argsObj->selected_start_date[0]; + $guiObj->selected_end_date = $argsObj->selected_end_date[0]; + + // we are using html_select_time (provided by Smarty Templates) + // then we need to provide selected in a format she likes. + $guiObj->selected_start_time = sprintf('%02d:00', $argsObj->start_Hour); + $guiObj->selected_end_time = sprintf('%02d:59', $argsObj->end_Hour); + } +} + +/** + * Gets the arguments used to create the report. + * + * Some of these arguments are set in the $_REQUEST, and some in $_SESSION. + * Having these arguments in hand, the init_args method will use TestLink objects, + * such as a Test Project Manager (testproject class) to retrieve other information + * that is displayed on the screen (e.g.: project name). + * + * @param database $dbHandler + * handler to TestLink database + * + * @return object of stdClass + */ +function initArgs(&$dbHandler) +{ + $args = new stdClass(); + + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 32, + 32 + ), + "do_action" => array( + tlInputParameter::STRING_N, + 3, + 6 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "user_id" => array( + tlInputParameter::INT_N + ), + "selected_start_date" => array( + tlInputParameter::ARRAY_STRING_N + ), + "selected_end_date" => array( + tlInputParameter::ARRAY_STRING_N + ), + "start_Hour" => array( + tlInputParameter::INT_N + ), + "end_Hour" => array( + tlInputParameter::INT_N + ) + ); + + $_REQUEST = strings_stripSlashes($_REQUEST); + R_PARAMS($iParams, $args); + + if (! is_null($args->apikey)) { + $args->show_only_active = true; + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = null; + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + } + + if ($args->tproject_id < 0) { + throw new Exception('Test project id can not be empty'); + } + $mgr = new testproject($dbHandler); + $info = $mgr->get_by_id($args->tproject_id); + $args->tproject_name = $info['name']; + + // Sanitize a little bit better + sanitizeDates($args); + + return $args; +} + +/** + * + * @link http://stackoverflow.com/questions/ + * 9293483/regular-expression-help-for-date-validation-dd-mm-yyyy-php + */ +function sanitizeDates(&$obj) +{ + $validLenght = strlen('MM/DD/YYYY'); + + // [./-] + // . russian,pl + // - nl + $validFormat = '#^\d{1,2}[./-][[0-9]{1,2}[./-][[0-9]{4}$#'; + + $p2check = array( + 'selected_end_date', + 'selected_start_date' + ); + foreach ($p2check as $prop) { + if (! is_null($obj->$prop)) { + // lenght check + $val = $obj->$prop; + $val = $val[0]; + + if (strlen($val) != $validLenght) { + $obj->$prop = null; + } else { + // check if format is valid + if (preg_match($validFormat, $val) === 0) { + $obj->$prop = null; + } + } + } + } // foreach +} + +/** + * Gets the columns definitions used in the report table. + * + * @return array containing columns and sort information + */ +function getColumnsDefinition() +{ + static $labels; + if (is_null($labels)) { + $lbl2get = array( + 'user' => null, + 'testsuite' => null, + 'testcase' => null, + 'importance' => null, + 'status' => null, + 'version' => null, + 'title_created' => null, + 'low' => null, + 'medium' => null, + 'high' => null + ); + $labels = init_labels($lbl2get); + } + + $colDef = array(); + $sortByCol = $labels['testsuite']; + $colDef[] = array( + 'title_key' => '', + 'width' => 80 + ); + $colDef[] = array( + 'title_key' => 'testsuite', + 'width' => 130 + ); + $colDef[] = array( + 'title_key' => 'testcase', + 'width' => 130 + ); + + // render and filter will be managed using customBehaviour (see $matrix->addCustomBehaviour()) + $colDef[] = array( + 'title_key' => 'importance', + 'width' => 50, + 'type' => 'importance' + ); + + $colDef[] = array( + 'title_key' => 'title_created', + 'width' => 75 + ); + $colDef[] = array( + 'title_key' => 'title_last_mod', + 'width' => 75 + ); + + return array( + $colDef, + $sortByCol + ); +} + +/** + * Gets the columns definitions used in the report table. + * + * @return array containing columns + */ +function getCSVColumnsDefinition() +{ + $lbl2get = array( + 'user' => null, + 'testsuite' => null, + 'testcase' => null, + 'importance' => null, + 'status' => null, + 'version' => null, + 'title_created' => null, + 'title_last_mod' => null, + 'th_start_time' => null, + 'th_end_time' => null, + 'low' => null, + 'medium' => null, + 'high' => null + ); + $lbl = init_labels($lbl2get); + + // this is the row layout + return array( + $lbl['user'], + $lbl['testsuite'], + $lbl['testcase'], + $lbl['importance'], + $lbl['title_created'], + $lbl['title_last_mod'], + $lbl['th_start_time'], + $lbl['th_end_time'] + ); +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); } - - -/** - * - */ -function initializeGuiForResult(&$dbHandler,$argsObj,&$guiObj) { - $rcfg = config_get('results'); - $map_status_code = $rcfg['status_code']; - $map_code_status = $rcfg['code_status']; - $map_status_label = $rcfg['status_label']; - $map_statuscode_css = array(); - - foreach($map_code_status as $code => $status) { - if (isset($map_status_label[$status])) { - $label = $map_status_label[$status]; - $map_statuscode_css[$code] = array(); - $map_statuscode_css[$code]['translation'] = lang_get($label); - $map_statuscode_css[$code]['css_class'] = $map_code_status[$code] . '_text'; - } - } - - $options = array(); - - // convert starttime to iso format for database usage - $dateFormat = config_get('date_format'); - $k2l = array('selected_start_date' => 'startTime','selected_end_date' => 'endTime'); - foreach($k2l as $in => $opt) { - if (isset($argsObj->$in) && sizeof($argsObj->$in) > 0) { - $dd = split_localized_date(current($argsObj->$in), $dateFormat); - if ($dd != null) { - $options[$opt] = $dd['year'] . "-" . $dd['month'] . "-" . $dd['day']; - } - } - } - - $options['startTime'] .= " " . (isset($argsObj->start_Hour) ? $argsObj->start_Hour : "00") . ":00:00"; - $options['endTime'] .= " " . (isset($argsObj->end_Hour) ? $argsObj->end_Hour : "00") . ":59:59"; - - $mgr = new testproject($dbHandler); - $guiObj->searchDone = 1; - $guiObj->resultSet = $mgr->getTestCasesCreatedByUser($argsObj->tproject_id,$argsObj->user_id,$options); - - if(!is_null($guiObj->resultSet)) { - // test case can exist multiple times, due to versions - $rows = array(); - list($columns, $sortByColumn) = getColumnsDefinition(); - foreach ($guiObj->resultSet as $idx => $itemInfo) { - foreach($itemInfo as $tcase) { - $cuRow = array(); - $tcase_id = $tcase['tcase_id']; - $tcversion_id = $tcase['tcversion_id']; - $cuRow[] = htmlspecialchars($tcase['login']); - $cuRow[] = htmlspecialchars($tcase['path']); - - // Create linked icons - $edit_link = "" . - "l18n['design']}\" src=\"{$guiObj->images['edit']}\" /> "; - - $cuRow[] = "" . - $edit_link . htmlspecialchars($tcase['external_id']) . " : " . - htmlspecialchars($tcase['tcase_name']) . - sprintf($guiObj->l18n['tcversion_indicator'],$tcase['version']); - - $cuRow[] = $tcase['importance']; - $cuRow[] = $tcase['creation_ts']; - $cuRow[] = $tcase['modification_ts']; - $rows[] = $cuRow; - } - } - - // Different table ID for different reports: - $table_id = "tl_table_tc_created_per_user_"; - - // Add test plan ID to table ID - $table_id .= $guiObj->tproject_id; - - $matrix = new tlExtTable($columns, $rows, $table_id); - $matrix->title = $guiObj->l18n['testproject'] . ": " . htmlspecialchars($guiObj->tproject_name); - // - // @TODO how this work ? - // $matrix->addCustomBehaviour(arg1, arg2) - // arg1: type that can be user defined, here we use 'importance'. - // arg2: array with methods - // 'render' => javascript render method (has to be present on inc_ext_table.tpl). - // 'filter' => piece of name used on several files - // 1. on exttable.class.php is used on buildColumns() to call build{piece}FilterOptions() - // 2. on ext_extensions a method named Ext.ux.grid.filter.{piece}Filter - // has to exists or rendering will fail - // - $matrix->addCustomBehaviour('importance', array('render' => 'importanceRenderer', 'filter' => 'Importance')); - - // Default grouping by first column, which is user for overview, build otherwise - $matrix->setGroupByColumnName(lang_get($columns[0]['title_key'])); - - // Define toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - - $matrix->toolbarDefaultStateButton = false; - $matrix->toolbarRefreshButton = false; - - $matrix->setSortByColumnName($sortByColumn); - $matrix->sortDirection = 'DESC'; - - $guiObj->tableSet[$guiObj->tproject_id] = $matrix; - } -} - -/** - * - */ -function initGuiForCSVDownload(&$dbHandler,$argsObj,&$guiObj) { - - $fromDate = current($argsObj->selected_start_date); - $toDate = current($argsObj->selected_end_date); - - $impCfg = config_get('importance'); - $impL10N = $impCfg['code_label']; - foreach( $impL10N as $ci => $lc ) { - $impL10N[$ci] = lang_get($lc); - } - - $colHeaders = getCSVColumnsDefinition(); - - $options = array(); - - // convert starttime to iso format for database usage - $dateFormat = config_get('date_format'); - $k2l = array('selected_start_date' => 'startTime','selected_end_date' => 'endTime'); - foreach($k2l as $in => $opt) { - if (isset($argsObj->$in) && sizeof($argsObj->$in) > 0) { - $dd = split_localized_date(current($argsObj->$in), $dateFormat); - if ($dd != null) { - $options[$opt] = $dd['year'] . "-" . $dd['month'] . "-" . $dd['day']; - } - } - } - - $options['startTime'] .= " " . (isset($argsObj->start_Hour) ? $argsObj->start_Hour : "00") . ":00:00"; - $options['endTime'] .= " " . (isset($argsObj->end_Hour) ? $argsObj->end_Hour : "00") . ":59:59"; - - $mgr = new testproject($dbHandler); - $guiObj->searchDone = 1; - $guiObj->resultSet = $mgr->getTestCasesCreatedByUser($argsObj->tproject_id,$argsObj->user_id,$options); - - if(!is_null($guiObj->resultSet)) { - // test case can exist multiple times, due to versions - $rows = array(); - foreach ($guiObj->resultSet as $idx => $itemInfo) { - foreach($itemInfo as $tcase) { - $cuRow = array(); - $tcase_id = $tcase['tcase_id']; - $tcversion_id = $tcase['tcversion_id']; - $cuRow[] = htmlspecialchars($tcase['login']); - $cuRow[] = htmlspecialchars($tcase['path']); - - $cuRow[] = htmlspecialchars($tcase['external_id']) . - " : " . - htmlspecialchars($tcase['tcase_name']) . - sprintf($guiObj->l18n['tcversion_indicator'],$tcase['version']); - - $cuRow[] = '(' . $tcase['importance'] . ') ' . - $impL10N[$tcase['importance']]; - - $cuRow[] = $tcase['creation_ts']; - $cuRow[] = $tcase['modification_ts']; - $cuRow[] = $fromDate; - $cuRow[] = $toDate; - - $rows[] = $cuRow; - } - } - - if( count($rows) == 0 ) { - return; - } - - $tmpfname = tempnam(sys_get_temp_dir(), "nuwow"); - unlink($tmpfname); - $csvfile = $tmpfname . '.csv'; - $fp = fopen($csvfile, 'w'); - fputcsv($fp, $colHeaders); - foreach ($rows as $fields) { - fputcsv($fp, $fields); - } - fclose($fp); - $fcont = file_get_contents($csvfile); - unlink($csvfile); - $f2d = __FILE__ . '.csv'; - $cty = array('Content-Type' => 'text/csv'); - downloadContentsToFile($fcont,$f2d,$cty); - exit(); - } - -} - - - -/** - * - */ -function initializeGuiForInput(&$dbHandler,$argsObj,&$guiObj) { - $room = config_get('gui_room'); - $guiObj->str_option_any = sprintf($room,lang_get('any')); - $guiObj->str_option_none = sprintf($room,lang_get('nobody')); - $guiObj->warning_msg = ''; - $guiObj->searchDone = 0; - - $guiObj->users = new stdClass(); - $guiObj->users->items = getUsersForHtmlOptions($dbHandler, ALL_USERS_FILTER, - array(TL_USER_ANYBODY => $guiObj->str_option_any) ); - - $guiObj->user_id = intval($argsObj->user_id); - - $dateFormat = config_get('date_format'); - $cfg = config_get('reportsCfg'); - $now = time(); - - if(is_null($argsObj->selected_start_date)) { - $guiObj->selected_start_date = strftime($dateFormat, $now - ($cfg->start_date_offset)); - $guiObj->selected_start_time = $cfg->start_time; - - $guiObj->selected_end_date = strftime($dateFormat, $now); - $guiObj->selected_end_time = null; - } else { - $guiObj->selected_start_date = $argsObj->selected_start_date[0]; - $guiObj->selected_end_date = $argsObj->selected_end_date[0]; - - // we are using html_select_time (provided by Smarty Templates) - // then we need to provide selected in a format she likes. - $guiObj->selected_start_time = sprintf('%02d:00',$argsObj->start_Hour); - $guiObj->selected_end_time = sprintf('%02d:59',$argsObj->end_Hour); - } -} - -/** - * Gets the arguments used to create the report. - * - * Some of these arguments are set in the $_REQUEST, and some in $_SESSION. - * Having these arguments in hand, the init_args method will use TestLink objects, - * such as a Test Project Manager (testproject class) to retrieve other information - * that is displayed on the screen (e.g.: project name). - * - * @param $dbHandler handler to TestLink database - * - * @return object of stdClass - */ -function init_args(&$dbHandler) { - $args = new stdClass(); - - $iParams = array("apikey" => array(tlInputParameter::STRING_N,32,32), - "do_action" => array(tlInputParameter::STRING_N,3,6), - "tproject_id" => array(tlInputParameter::INT_N), - "user_id" => array(tlInputParameter::INT_N), - "selected_start_date" => array(tlInputParameter::ARRAY_STRING_N), - "selected_end_date" => array(tlInputParameter::ARRAY_STRING_N), - "start_Hour" => array(tlInputParameter::INT_N), - "end_Hour" => array(tlInputParameter::INT_N)); - - $_REQUEST=strings_stripSlashes($_REQUEST); - R_PARAMS($iParams,$args); - - if( !is_null($args->apikey) ) { - $args->show_only_active = true; - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = null; - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } else { - testlinkInitPage($dbHandler,false,false,"checkRights"); - } - - if($args->tproject_id < 0) { - throw new Exception('Test project id can not be empty'); - } - $mgr = new testproject($dbHandler); - $info = $mgr->get_by_id($args->tproject_id); - $args->tproject_name = $info['name']; - - - // Sanitize a little bit better - sanitizeDates($args); - - return $args; -} - -/** - * - * @link http://stackoverflow.com/questions/ - * 9293483/regular-expression-help-for-date-validation-dd-mm-yyyy-php - */ -function sanitizeDates(&$obj) { - $validLenght = strlen('MM/DD/YYYY'); - - // [./-] - // . russian,pl - // - nl - $validFormat = '#^[0-9]{1,2}[./-][[0-9]{1,2}[./-][[0-9]{4}$#'; - - $p2check = array('selected_end_date','selected_start_date'); - foreach($p2check as $prop) { - if(!is_null($obj->$prop)) { - // lenght check - $val = $obj->$prop; - $val = $val[0]; - - if( strlen($val) != $validLenght) { - $obj->$prop = null; - } else { - // check if format is valid - if(preg_match($validFormat, $val) === 0) { - $obj->$prop = null; - } - } - } - } // foreach -} - -/** - * Gets the columns definitions used in the report table. - * - * @return array containing columns and sort information - */ -function getColumnsDefinition() { - - static $labels; - if( is_null($labels) ) { - $lbl2get = array('user' => null, 'testsuite' => null, - 'testcase' => null,'importance' => null,'status' => null, - 'version' => null,'title_created' => null, - 'low' => null,'medium' => null, 'high' => null); - $labels = init_labels($lbl2get); - } - - $colDef = array(); - $sortByCol = $labels['testsuite']; - $colDef[] = array('title_key' => '', 'width' => 80); - $colDef[] = array('title_key' => 'testsuite', 'width' => 130); - $colDef[] = array('title_key' => 'testcase', 'width' => 130); - - // render and filter will be managed using customBehaviour (see $matrix->addCustomBehaviour()) - $colDef[] = array('title_key' => 'importance', 'width' => 50, 'type' => 'importance'); - - $colDef[] = array('title_key' => 'title_created', 'width' => 75); - $colDef[] = array('title_key' => 'title_last_mod', 'width' => 75); - - return array($colDef, $sortByCol); -} - - -/** - * Gets the columns definitions used in the report table. - * - * @return array containing columns - */ -function getCSVColumnsDefinition() { - - $lbl2get = array('user' => null, 'testsuite' => null, - 'testcase' => null,'importance' => null,'status' => null, - 'version' => null,'title_created' => null, - 'title_last_mod' => null, - 'th_start_time' => null, 'th_end_time' => null, - 'low' => null,'medium' => null, 'high' => null); - $lbl = init_labels($lbl2get); - - - // this is the row layout - $colDef = array($lbl['user'],$lbl['testsuite'], - $lbl['testcase'],$lbl['importance'], - $lbl['title_created'],$lbl['title_last_mod'], - $lbl['th_start_time'],$lbl['th_end_time'] - ); - - return $colDef; -} - - -/** - * - */ -function checkRights(&$db,&$user) { - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file diff --git a/lib/results/tcNotRunAnyPlatform.php b/lib/results/tcNotRunAnyPlatform.php index 9e955a273d..9e02266322 100644 --- a/lib/results/tcNotRunAnyPlatform.php +++ b/lib/results/tcNotRunAnyPlatform.php @@ -1,32 +1,36 @@ title = lang_get('title_test_report_not_run_on_any_platform'); $gui->printDate = ''; $gui->matrixData = array(); -$labels = init_labels(array('design' => null, 'execution' => null, 'execution_history' => null)); +$labels = init_labels( + array( + 'design' => null, + 'execution' => null, + 'execution_history' => null + )); $edit_img = TL_THEME_IMG_DIR . "edit_icon.png"; $history_img = TL_THEME_IMG_DIR . "history_small.png"; @@ -42,17 +46,16 @@ $testCasePrefix = $tproject_info['prefix'] . $testCaseCfg->glue_character; $mailCfg = buildMailCfg($gui); -$getOpt = array('outputFormat' => 'map'); -$gui->platforms = $tplan_mgr->getPlatforms($args->tplan_id,$getOpt); -$platforms_active = !is_null($gui->platforms); - -// $re = new results($db, $tplan_mgr, $tproject_info, $tplan_info,ALL_TEST_SUITES,ALL_BUILDS,ALL_PLATFORMS); +$getOpt = array( + 'outputFormat' => 'map' +); +$gui->platforms = $tplan_mgr->getPlatforms($args->tplan_id, $getOpt); +$platforms_active = ! is_null($gui->platforms); $gui->buildInfoSet = $tplan_mgr->get_builds($args->tplan_id, 1); // only active builds -if ($gui->buildInfoSet) -{ - $buildIDSet = array_keys($gui->buildInfoSet); - $buildQty = sizeOf($buildIDSet); +if ($gui->buildInfoSet) { + $buildIDSet = array_keys($gui->buildInfoSet); + $buildQty = count($buildIDSet); } // Get Results on map with access key = test case's parent test suite id @@ -72,195 +75,213 @@ $gui->matrix = array(); $gui->tableSet = array(); -$cols = array_flip(array('tsuite', 'link', 'priority')); - -if ($lastResultMap != null && $platforms_active) -{ - $versionTag = lang_get('tcversion_indicator'); - foreach ($lastResultMap as $suiteId => $tsuite) - { - foreach ($tsuite as $testCaseId => $platform) - { - - $any_result_found = false; - $rowArray = null; - $gui->number_of_testcases ++; - - foreach($platform as $platformId => $tcase) - { - - if (!$any_result_found) - { - $suiteName = $tcase['suiteName']; - $name = $tcase['name']; - $linkedTCVersion = $tcase['version']; - $external_id = $testCasePrefix . $tcase['external_id']; - $tc_name = htmlspecialchars("{$external_id}:{$name}",ENT_QUOTES); - - // create linked icons - $exec_history_link = "" . - " "; - $edit_link = "" . - " "; - - $dl = str_replace(" ", "%20", $args->basehref) . 'linkto.php?tprojectPrefix=' . urlencode($tproject_info['prefix']) . - '&item=testcase&id=' . urlencode($external_id); - $mail_link = "{$tc_name} "; - - $tcLink = "" . - $exec_history_link .$edit_link . $tc_name; - - $rowArray[$cols['tsuite']] = $suiteName; - $rowArray[$cols['link']] = $args->format != FORMAT_HTML ? $mail_link : $tcLink; - - if($_SESSION['testprojectOptions']->testPriorityEnabled) - { - $dummy = $tplan_mgr->getPriority($args->tplan_id, array('tcversion_id' => $tcase['tcversion_id'])); - $rowArray[$cols['priority']] = $dummy[$tcase['tcversion_id']]['priority_level']; - } - - $suiteExecutions = $executionsMap[$suiteId]; - - foreach ($buildIDSet as $idx => $buildId) { - $resultsForBuild = null; - $lastStatus = $resultsCfg['status_code']['not_run']; - - // iterate over executions for this suite, look for - // entries that match current: - // test case id,build id ,platform id - $qta_suites=sizeOf($suiteExecutions); - - foreach ($suiteExecutions as $jdx => $execution_array) { - if (($execution_array['testcaseID'] == $testCaseId) && - ($execution_array['build_id'] == $buildId) && - ($execution_array['platform_id'] == $platformId) && - isset($execution_array['status'])) - { - $any_result_found = true; - } - } - } - } - } // end of inner foreach() - - if (!$any_result_found) { - $gui->matrix[] = $rowArray; - $gui->number_of_not_run_testcases++; - } - } +$cols = array_flip(array( + 'tsuite', + 'link', + 'priority' +)); + +if ($lastResultMap != null && $platforms_active) { + $versionTag = lang_get('tcversion_indicator'); + foreach ($lastResultMap as $suiteId => $tsuite) { + foreach ($tsuite as $testCaseId => $platform) { + + $any_result_found = false; + $rowArray = null; + $gui->number_of_testcases ++; + + foreach ($platform as $platformId => $tcase) { + + if (! $any_result_found) { + $suiteName = $tcase['suiteName']; + $name = $tcase['name']; + $linkedTCVersion = $tcase['version']; + $external_id = $testCasePrefix . $tcase['external_id']; + $tc_name = htmlspecialchars("{$external_id}:{$name}", + ENT_QUOTES); + + // create linked icons + $exec_history_link = "" . + " "; + $edit_link = "" . + " "; + + $dl = str_replace(" ", "%20", $args->basehref) . + 'linkto.php?tprojectPrefix=' . + urlencode($tproject_info['prefix']) . + '&item=testcase&id=' . urlencode($external_id); + $mail_link = "{$tc_name} "; + + $tcLink = "" . $exec_history_link . $edit_link . $tc_name; + + $rowArray[$cols['tsuite']] = $suiteName; + $rowArray[$cols['link']] = $args->format != FORMAT_HTML ? $mail_link : $tcLink; + + if ($_SESSION['testprojectOptions']->testPriorityEnabled) { + $dummy = $tplan_mgr->getPriority($args->tplan_id, + array( + 'tcversion_id' => $tcase['tcversion_id'] + )); + $rowArray[$cols['priority']] = $dummy[$tcase['tcversion_id']]['priority_level']; + } + + $suiteExecutions = $executionsMap[$suiteId]; + + foreach ($buildIDSet as $buildId) { + $resultsForBuild = null; + $lastStatus = $resultsCfg['status_code']['not_run']; + + // iterate over executions for this suite, look for + // entries that match current: + // test case id,build id ,platform id + $qta_suites = count($suiteExecutions); + + foreach ($suiteExecutions as $execution_array) { + if (($execution_array['testcaseID'] == $testCaseId) && + ($execution_array['build_id'] == $buildId) && + ($execution_array['platform_id'] == $platformId) && + isset($execution_array['status'])) { + $any_result_found = true; + } + } + } + } + } + + if (! $any_result_found) { + $gui->matrix[] = $rowArray; + $gui->number_of_not_run_testcases ++; + } + } } } // create and show the table only if we have data to display -if ($gui->number_of_not_run_testcases) -{ - $gui->tableSet[] = buildMatrix($gui->matrix, $args->format); +if ($gui->number_of_not_run_testcases) { + $gui->tableSet[] = buildMatrix($gui->matrix, $args->format); } -if ($platforms_active) -{ - $gui->status_message = sprintf(lang_get('not_run_any_platform_status_msg'), - $gui->number_of_testcases, - $gui->number_of_not_run_testcases); -} -else -{ - $gui->warning_msg = lang_get('not_run_any_platform_no_platforms'); +if ($platforms_active) { + $gui->status_message = sprintf(lang_get('not_run_any_platform_status_msg'), + $gui->number_of_testcases, $gui->number_of_not_run_testcases); +} else { + $gui->warning_msg = lang_get('not_run_any_platform_no_platforms'); } -$smarty = new TLSmarty; -$smarty->assign('gui',$gui); -displayReport($templateCfg->template_dir . $templateCfg->default_template, $smarty, $args->format, $mailCfg); - +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +displayReport($templateCfg->template_dir . $templateCfg->default_template, + $smarty, $args->format, $mailCfg); /** - * * + * @return stdClass */ -function init_args() +function initArgs() { - $iParams = array("format" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); + $iParams = array( + "format" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->basehref = $_SESSION['basehref']; - + $args->basehref = $_SESSION['basehref']; + return $args; } /** - * * + * @param database $db + * @param tlUser $user + * @return string */ -function checkRights(&$db,&$user) +function checkRights(&$db, &$user) { - return $user->hasRight($db,'testplan_metrics'); + return $user->hasRight($db, 'testplan_metrics'); } /** * Builds ext-js rich table or static HTML table to display matrix results * - * @param $dataSet - * @param $format + * @param unknown $dataSet + * @param unknown $format * @return tlExtTable|tlHTMLTable - * - * */ function buildMatrix($dataSet, $format) { - $columns = array(array('title_key' => 'title_test_suite_name', 'width' => 100), - array('title_key' => 'title_test_case_title', 'width' => 150)); - - if($_SESSION['testprojectOptions']->testPriorityEnabled) - { - $columns[] = array('title_key' => 'priority', 'type' => 'priority', 'width' => 40); - } - - if ($format == FORMAT_HTML) - { - - $matrix = new tlExtTable($columns, $dataSet, 'tl_table_results_tc'); - $matrix->setGroupByColumnName(lang_get('title_test_suite_name')); - $matrix->sortDirection = 'DESC'; - - if($_SESSION['testprojectOptions']->testPriorityEnabled) - { - $matrix->addCustomBehaviour('priority', array('render' => 'priorityRenderer', 'filter' => 'Priority')); - //sort by priority - $matrix->setSortByColumnName(lang_get('priority')); - } else { - //sort by test case - $matrix->setSortByColumnName(lang_get('title_test_case_title')); - } - - //define table toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - - } - else - { - $matrix = new tlHTMLTable($columns, $dataSet, 'tl_table_results_tc'); - } - return $matrix; -} + $columns = array( + array( + 'title_key' => 'title_test_suite_name', + 'width' => 100 + ), + array( + 'title_key' => 'title_test_case_title', + 'width' => 150 + ) + ); + + if ($_SESSION['testprojectOptions']->testPriorityEnabled) { + $columns[] = array( + 'title_key' => 'priority', + 'type' => 'priority', + 'width' => 40 + ); + } + if ($format == FORMAT_HTML) { + + $matrix = new tlExtTable($columns, $dataSet, 'tl_table_results_tc'); + $matrix->setGroupByColumnName(lang_get('title_test_suite_name')); + $matrix->sortDirection = 'DESC'; + + if ($_SESSION['testprojectOptions']->testPriorityEnabled) { + $matrix->addCustomBehaviour('priority', + array( + 'render' => 'priorityRenderer', + 'filter' => 'Priority' + )); + // sort by priority + $matrix->setSortByColumnName(lang_get('priority')); + } else { + // sort by test case + $matrix->setSortByColumnName(lang_get('title_test_case_title')); + } + + // define table toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + } else { + $matrix = new tlHTMLTable($columns, $dataSet, 'tl_table_results_tc'); + } + return $matrix; +} /** - * * + * @param stdClass $guiObj + * @return stdClass */ function buildMailCfg(&$guiObj) { - $labels = array('testplan' => lang_get('testplan'), 'testproject' => lang_get('testproject')); - $cfg = new stdClass(); - $cfg->cc = ''; - $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . $guiObj->tproject_name . - ' : ' . $labels['testplan'] . ' : ' . $guiObj->tplan_name; - - return $cfg; + $labels = array( + 'testplan' => lang_get('testplan'), + 'testproject' => lang_get('testproject') + ); + $cfg = new stdClass(); + $cfg->cc = ''; + $cfg->subject = $guiObj->title . ' : ' . $labels['testproject'] . ' : ' . + $guiObj->tproject_name . ' : ' . $labels['testplan'] . ' : ' . + $guiObj->tplan_name; + + return $cfg; } ?> diff --git a/lib/results/testCasesWithCF.php b/lib/results/testCasesWithCF.php index c150169ec0..b5258b15ca 100644 --- a/lib/results/testCasesWithCF.php +++ b/lib/results/testCasesWithCF.php @@ -1,314 +1,360 @@ -getImages(); - -$templateCfg = templateConfiguration(); -$charset = config_get('charset'); -$labels = init_labels(array('design' => null, 'execution' => null, 'no_linked_tc_cf' => null, - 'execution_history' => null)); - - -$tcase_mgr = new testcase($db); -$args = init_args($db); -$gui = initializeGui($db,$args); - -if( $args->doIt ) -{ - // Get executions with custom field values - buildResultSet($db,$gui,$args->tproject_id,$args->tplan_id); - - // Create column headers - $columns = getColumnsDefinition($args->showPlatforms,$gui->cfields,$args->platforms); - - // Extract the relevant data and build a matrix - $matrixData = array(); - foreach ($gui->resultSet as $item) - { - $rowData = array(); - - // Get test suite path - $dummy = $tcase_mgr->getPathLayered(array($item['tcase_id'])); - $dummy = end($dummy); - $rowData[] = $dummy['value']; - - // create linked icons - $exec_history_link = "" . - " "; - - $exec_link = "tplan_id}, {$item['platform_id']});\">" . - " "; - - $edit_link = "" . - " "; - - $tcaseName = buildExternalIdString($gui->tcasePrefix, $item['tc_external_id']) . ' : ' . $item['tcase_name']; - - $tcLink = "" . $exec_history_link . - $exec_link . $edit_link . $tcaseName; - $rowData[] = $tcLink; - - $rowData[] = $item['tcversion_number']; - if ($args->showPlatforms) - { - $rowData[] = $item['platform_name']; - } - $rowData[] = $item['build_name']; - $rowData[] = $item['tester']; - - // use html comment to be able to sort table by timestamp and not by link - // only link is visible in table but comment is used for sorting - $dummy = null; - $rowData[] = "" . - localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', $item['execution_ts']); - - // Use array for status to get correct rendering and sorting - $rowData[] = array( - 'value' => $item['exec_status'], - 'text' => $gui->status_code_labels[$item['exec_status']], - 'cssClass' => $gui->code_status[$item['exec_status']] . '_text', - ); - - $hasValue = false; - - $rowData[] = strip_tags($item['exec_notes']); - - if($item['exec_notes']) { - $hasValue = true; - } - - foreach ($item['cfields'] as $cf_value) - { - $rowData[] = preg_replace('!\s+!', ' ', htmlspecialchars($cf_value, ENT_QUOTES, $charset));; - if ($cf_value) { - $hasValue = true; - } - } - if ($hasValue) { - $matrixData[] = $rowData; - } - } - - if (count($matrixData) > 0) - { - $table = new tlExtTable($columns, $matrixData, 'tl_table_tc_with_cf'); - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - - $table->setGroupByColumnName(lang_get('build')); - $table->setSortByColumnName(lang_get('date')); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->toolbarExpandCollapseGroupsButton = true; - $table->toolbarShowAllColumnsButton = true; - - $gui->tableSet = array($table); - } - else - { - $gui->warning_msg = $labels['no_linked_tc_cf']; - } +getImages(); + +$templateCfg = templateConfiguration(); +$charset = config_get('charset'); +$labels = init_labels( + array( + 'design' => null, + 'execution' => null, + 'no_linked_tc_cf' => null, + 'execution_history' => null + )); + +$tcaseMgr = new testcase($db); +$args = initArgs($db); +$gui = initializeGui($db, $args); + +if ($args->doIt) { + // Get executions with custom field values + buildResultSet($db, $gui, $args->tproject_id, $args->tplan_id); + + // Create column headers + $columns = getColumnsDefinition($args->showPlatforms, $gui->cfields, + $args->platforms); + + // Extract the relevant data and build a matrix + $matrixData = array(); + foreach ($gui->resultSet as $item) { + $rowData = array(); + + // Get test suite path + $dummy = $tcaseMgr->getPathLayered(array( + $item['tcase_id'] + )); + $dummy = end($dummy); + $rowData[] = $dummy['value']; + + // create linked icons + $exec_history_link = "" . + " "; + + $exec_link = "tplan_id}, {$item['platform_id']});\">" . + " "; + + $edit_link = "" . + " "; + + $tcaseName = buildExternalIdString($gui->tcasePrefix, + $item['tc_external_id']) . ' : ' . $item['tcase_name']; + + $tcLink = "" . + $exec_history_link . $exec_link . $edit_link . $tcaseName; + $rowData[] = $tcLink; + + $rowData[] = $item['tcversion_number']; + if ($args->showPlatforms) { + $rowData[] = $item['platform_name']; + } + $rowData[] = $item['build_name']; + $rowData[] = $item['tester']; + + // use html comment to be able to sort table by timestamp and not by link + // only link is visible in table but comment is used for sorting + $dummy = null; + $rowData[] = "" . + localize_dateOrTimeStamp(null, $dummy, 'timestamp_format', + $item['execution_ts']); + + // Use array for status to get correct rendering and sorting + $rowData[] = array( + 'value' => $item['exec_status'], + 'text' => $gui->status_code_labels[$item['exec_status']], + 'cssClass' => $gui->code_status[$item['exec_status']] . '_text' + ); + + $hasValue = false; + + $rowData[] = strip_tags($item['exec_notes']); + + if ($item['exec_notes']) { + $hasValue = true; + } + + foreach ($item['cfields'] as $cf_value) { + $rowData[] = preg_replace('!\s+!', ' ', + htmlspecialchars($cf_value, ENT_QUOTES, $charset)); + if ($cf_value) { + $hasValue = true; + } + } + if ($hasValue) { + $matrixData[] = $rowData; + } + } + + if (! empty($matrixData)) { + $table = new tlExtTable($columns, $matrixData, 'tl_table_tc_with_cf'); + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + + $table->setGroupByColumnName(lang_get('build')); + $table->setSortByColumnName(lang_get('date')); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->toolbarExpandCollapseGroupsButton = true; + $table->toolbarShowAllColumnsButton = true; + + $gui->tableSet = array( + $table + ); + } else { + $gui->warning_msg = $labels['no_linked_tc_cf']; + } +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs(&$dbHandler) +{ + $argsObj = new stdClass(); + $argsObj->doIt = false; + $argsObj->showPlatforms = false; + $argsObj->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $argsObj->tproject_id = intval($argsObj->tproject_id); + + $argsObj->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; + + $argsObj->tplan_name = ''; + $argsObj->tplan_id = isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : 0; + $argsObj->tplan_id = intval($argsObj->tplan_id); + + if ($argsObj->tplan_id == 0) { + $argsObj->tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0; + } + + if ($argsObj->tplan_id > 0) { + $tplan_mgr = new testplan($dbHandler); + $tplan_info = $tplan_mgr->get_by_id($argsObj->tplan_id); + $argsObj->tplan_name = $tplan_info['name']; + + $argsObj->doIt = $tplan_mgr->count_testcases($argsObj->tplan_id) > 0; + $argsObj->showPlatforms = $tplan_mgr->hasLinkedPlatforms( + $argsObj->tplan_id); + $getOpt = array( + 'outputFormat' => 'map' + ); + $argsObj->platforms = $tplan_mgr->getPlatforms($argsObj->tplan_id, + $getOpt); + unset($tplan_mgr); + } + + return $argsObj; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->pageTitle = lang_get('caption_testCasesWithCF'); + $guiObj->warning_msg = $guiObj->tcasePrefix = ''; + $guiObj->path_info = $guiObj->resultSet = $guiObj->tableSet = null; + + $guiObj->tproject_name = $argsObj->tproject_name; + $guiObj->tplan_name = $argsObj->tplan_name; + $guiObj->tplan_id = $argsObj->tplan_id; + + $tproject_mgr = new testproject($dbHandler); + $guiObj->tcasePrefix = $tproject_mgr->getTestCasePrefix( + $argsObj->tproject_id); + unset($tproject_mgr); + + // Get the mapping for the Verbose Status Description of Test Case Status + $resultsCfg = config_get('results'); + $guiObj->code_status = $resultsCfg['code_status']; + foreach ($guiObj->code_status as $code => $verbose) { + if (isset($resultsCfg['status_label'][$verbose])) { + $guiObj->status_code_labels[$code] = lang_get( + $resultsCfg['status_label'][$verbose]); + } + } + return $guiObj; +} + +/** + * + * @param database $dbHandler + * @param stdClass $guiObj + * @param int $tproject_id + * @param int $tplan_id + */ +function buildResultSet(&$dbHandler, &$guiObj, $tproject_id, $tplan_id) +{ + $cfieldMgr = new cfield_mgr($dbHandler); + + // Get the custom fields linked/enabled on execution to a test project + // This will be used on report to give name to header of columns that hold custom field value + $guiObj->cfields = $cfieldMgr->get_linked_cfields_at_execution($tproject_id, + 1, 'testcase', null, null, null, 'name'); + + // this way on caller can be used on array operations, without warnings + $guiObj->cfields = (array) $guiObj->cfields; + if (count($guiObj->cfields) > 0) { + foreach ($guiObj->cfields as $key => $values) { + $cf_place_holder['cfields'][$key] = ''; + } + } + + $cf_map = $cfieldMgr->get_linked_cfields_at_execution($tproject_id, 1, + 'testcase', null, null, $tplan_id, 'exec_id'); + + // need to transform in structure that allow easy display + // Every row is an execution with exec data plus a column that contains following map: + // 'cfields' => CFNAME1 => value + // CFNAME2 => value + $guiObj->resultSet = array(); + + if (! is_null($cf_map)) { + foreach ($cf_map as $exec_id => $exec_info) { + // Get common exec info and remove useless keys + $guiObj->resultSet[$exec_id] = $exec_info[0]; + unset($guiObj->resultSet[$exec_id]['name']); + unset($guiObj->resultSet[$exec_id]['label']); + unset($guiObj->resultSet[$exec_id]['display_order']); + unset($guiObj->resultSet[$exec_id]['id']); + unset($guiObj->resultSet[$exec_id]['value']); + + // Collect custom fields values + $guiObj->resultSet[$exec_id] += $cf_place_holder; + foreach ($exec_info as $cfield_data) { + $guiObj->resultSet[$exec_id]['cfields'][$cfield_data['name']] = $cfieldMgr->string_custom_field_value( + $cfield_data, null); + } + } + } + + if (! empty($cf_map) && ($guiObj->row_qty = count($cf_map)) == 0) { + $guiObj->warning_msg = lang_get('no_linked_tc_cf'); + } +} + +/** + * get Columns definition for table to display + * + * @param boolean $showPlatforms + * @param array $customFields + * @param array $platforms + * @return array + */ +function getColumnsDefinition($showPlatforms, $customFields, $platforms) +{ + $colDef = array( + array( + 'title_key' => 'test_suite', + 'width' => 80, + 'type' => 'text' + ), + array( + 'title_key' => 'test_case', + 'width' => 80, + 'type' => 'text' + ), + array( + 'title_key' => 'version', + 'width' => 20 + ) + ); + + if ($showPlatforms) { + $colDef[] = array( + 'title_key' => 'platform', + 'width' => 40, + 'filter' => 'list', + 'filterOptions' => $platforms + ); + } + array_push($colDef, array( + 'title_key' => 'build', + 'width' => 35 + ), array( + 'title_key' => 'th_owner', + 'width' => 60 + ), array( + 'title_key' => 'date', + 'width' => 60 + ), array( + 'title_key' => 'status', + 'type' => 'status', + 'width' => 30 + )); + + $colDef[] = array( + 'title_key' => 'title_execution_notes', + 'type' => 'text' + ); + + foreach ($customFields as $cfield) { + // if custom field is time for computing execution time do not waste space + // $cfield['id'] is used instead of $cfield['name'] to fix the issue regarding dot on CF name + // 20130324 - need to understand if col_id is really needed + // + $dummy = array( + 'title' => $cfield['label'], + 'col_id' => 'id_cf_' . $cfield['id'] + ); + if ($cfield['name'] == 'CF_EXEC_TIME') { + $dummy['width'] = 20; + } else { + $dummy['type'] = 'text'; + } + $colDef[] = $dummy; + } + + return $colDef; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_metrics'); } - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/* - function: - - args : - - returns: - - */ -function init_args(&$dbHandler) -{ - $argsObj = new stdClass(); - $argsObj->doIt = false; - $argsObj->showPlatforms = false; - $argsObj->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $argsObj->tproject_id = intval($argsObj->tproject_id); - - - $argsObj->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; - - $argsObj->tplan_name = ''; - $argsObj->tplan_id = isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : 0; - $argsObj->tplan_id = intval($argsObj->tplan_id); - - if ($argsObj->tplan_id == 0) { - $argsObj->tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0; - } - - if($argsObj->tplan_id > 0) { - $tplan_mgr = new testplan($dbHandler); - $tplan_info = $tplan_mgr->get_by_id($argsObj->tplan_id); - $argsObj->tplan_name = $tplan_info['name']; - - $argsObj->doIt = $tplan_mgr->count_testcases($argsObj->tplan_id) > 0; - $argsObj->showPlatforms = $tplan_mgr->hasLinkedPlatforms($argsObj->tplan_id); - $getOpt = array('outputFormat' => 'map'); - $argsObj->platforms = $tplan_mgr->getPlatforms($argsObj->tplan_id,$getOpt); - unset($tplan_mgr); - } - - return $argsObj; -} - - - -function initializeGui(&$dbHandler,&$argsObj) -{ - $guiObj = new stdClass(); - $guiObj->pageTitle = lang_get('caption_testCasesWithCF'); - $guiObj->warning_msg = $guiObj->tcasePrefix = ''; - $guiObj->path_info = $guiObj->resultSet = $guiObj->tableSet = null; - - $guiObj->tproject_name = $argsObj->tproject_name; - $guiObj->tplan_name = $argsObj->tplan_name; - $guiObj->tplan_id = $argsObj->tplan_id; - - $tproject_mgr = new testproject($dbHandler); - $guiObj->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tproject_id); - unset($tproject_mgr); - - // Get the mapping for the Verbose Status Description of Test Case Status - $resultsCfg = config_get('results'); - $guiObj->code_status = $resultsCfg['code_status']; - foreach($guiObj->code_status as $code => $verbose) - { - if(isset($resultsCfg['status_label'][$verbose])) - { - $guiObj->status_code_labels[$code] = lang_get($resultsCfg['status_label'][$verbose]); - } - } - return $guiObj; -} - - -/** - * - * - */ -function buildResultSet(&$dbHandler,&$guiObj,$tproject_id,$tplan_id) -{ - - $cfieldMgr = new cfield_mgr($dbHandler); - - // Get the custom fields linked/enabled on execution to a test project - // This will be used on report to give name to header of columns that hold custom field value - $guiObj->cfields = $cfieldMgr->get_linked_cfields_at_execution($tproject_id,1,'testcase',null,null,null,'name'); - - // this way on caller can be used on array operations, without warnings - $guiObj->cfields = (array)$guiObj->cfields; - if( count($guiObj->cfields) > 0 ) - { - foreach($guiObj->cfields as $key => $values) - { - $cf_place_holder['cfields'][$key]=''; - } - } - - $cf_map = $cfieldMgr->get_linked_cfields_at_execution($tproject_id,1,'testcase',null,null,$tplan_id,'exec_id'); - - // need to transform in structure that allow easy display - // Every row is an execution with exec data plus a column that contains following map: - // 'cfields' => CFNAME1 => value - // CFNAME2 => value - $guiObj->resultSet = array(); - - if(!is_null($cf_map)) - { - foreach($cf_map as $exec_id => $exec_info) - { - // Get common exec info and remove useless keys - $guiObj->resultSet[$exec_id] = $exec_info[0]; - unset($guiObj->resultSet[$exec_id]['name']); - unset($guiObj->resultSet[$exec_id]['label']); - unset($guiObj->resultSet[$exec_id]['display_order']); - unset($guiObj->resultSet[$exec_id]['id']); - unset($guiObj->resultSet[$exec_id]['value']); - - // Collect custom fields values - $guiObj->resultSet[$exec_id] += $cf_place_holder; - foreach($exec_info as $cfield_data) - { - $guiObj->resultSet[$exec_id]['cfields'][$cfield_data['name']] = $cfieldMgr->string_custom_field_value($cfield_data,null); - } - } - } - - if(($guiObj->row_qty=count($cf_map)) == 0 ) - { - $guiObj->warning_msg = lang_get('no_linked_tc_cf'); - } -} - - -/** - * get Columns definition for table to display - * - */ -function getColumnsDefinition($showPlatforms,$customFields,$platforms) -{ - - $colDef = array(array('title_key' => 'test_suite', 'width' => 80, 'type' => 'text'), - array('title_key' => 'test_case', 'width' => 80, 'type' => 'text'), - array('title_key' => 'version', 'width' => 20)); - - if ($showPlatforms) - { - $colDef[] = array('title_key' => 'platform', 'width' => 40, 'filter' => 'list', 'filterOptions' => $platforms); - } - array_push( $colDef, - array('title_key' => 'build', 'width' => 35), - array('title_key' => 'th_owner', 'width' => 60), - array('title_key' => 'date', 'width' => 60), - array('title_key' => 'status', 'type' => 'status', 'width' => 30)); - - $colDef[] = array('title_key' => 'title_execution_notes', 'type' => 'text'); - - - foreach ($customFields as $cfield) - { - // if custom field is time for computing execution time do not waste space - // $cfield['id'] is used instead of $cfield['name'] to fix the issue regarding dot on CF name - // 20130324 - need to understand if col_id is really needed - // - $dummy = array('title' => $cfield['label'], 'col_id' => 'id_cf_' . $cfield['id']); - if($cfield['name'] == 'CF_EXEC_TIME') - { - $dummy['width'] = 20; - } - else - { - $dummy['type'] = 'text'; - } - $colDef[] = $dummy; - } - - return $colDef; -} - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file diff --git a/lib/results/testCasesWithoutTester.php b/lib/results/testCasesWithoutTester.php index 25fb9e8a0e..8355f6fcd9 100644 --- a/lib/results/testCasesWithoutTester.php +++ b/lib/results/testCasesWithoutTester.php @@ -1,224 +1,270 @@ -count_testcases($args->tplan_id) > 0) -{ - $platformCache = null; - $msg_key = 'all_testcases_have_tester'; - $cfg = config_get('results'); - - $metricsMgr = new tlTestPlanMetrics($db); - $metrics = $metricsMgr->getNotRunWoTesterAssigned($args->tplan_id,null,null, - array('output' => 'array', 'ignoreBuild' => true)); - - if(($gui->row_qty = count($metrics)) > 0) - { - $msg_key = ''; - $links = featureLinks($gui->labels,$smarty->getImages()); - $gui->pageTitle .= " - " . $gui->labels['match_count'] . ":" . $gui->row_qty; - - - if ($args->show_platforms) - { - $platformCache = $tplan_mgr->getPlatforms($args->tplan_id,array('outputFormat' => 'mapAccessByID')); - } - - // Collect all tcases id and get all test suite paths - $targetSet = array(); - - foreach ($metrics as &$item) - { - $targetSet[] = $item['tcase_id']; - } - $tree_mgr = new tree($db); - $path_info = $tree_mgr->get_full_path_verbose($targetSet); - unset($tree_mgr); - unset($targetSet); - - $data = array(); - foreach ($metrics as &$item) - { - $row = array(); - $row[] = join(" / ", $path_info[$item['tcase_id']]); - - $row[] = "" . - sprintf($links['full'],$item['tcase_id'],$item['tcase_id']) . - $item['full_external_id'] . ': ' . $item['name']; - - if ($args->show_platforms) - { - $row[] = $platformCache[$item['platform_id']]['name']; - } - - if($gui->options->testPriorityEnabled) - { - // THIS HAS TO BE REFACTORED, because we can no do lot of calls - // because performance will be BAD - $row[] = $tplan_mgr->urgencyImportanceToPriorityLevel($item['urg_imp']); - } - - $row[] = strip_tags($item['summary']); - $data[] = $row; - } - - $gui->tableSet[] = buildTable($data, $args->tproject_id, $args->show_platforms, - $gui->options->testPriorityEnabled); - } +count_testcases($args->tplan_id) > 0) { + $platformCache = null; + $msg_key = 'all_testcases_have_tester'; + $cfg = config_get('results'); + + $metricsMgr = new tlTestPlanMetrics($db); + $metrics = $metricsMgr->getNotRunWoTesterAssigned($args->tplan_id, null, + null, array( + 'output' => 'array', + 'ignoreBuild' => true + )); + + if (($gui->row_qty = count($metrics)) > 0) { + $msg_key = ''; + $links = featureLinks($gui->labels, $smarty->getImages()); + $gui->pageTitle .= " - " . $gui->labels['match_count'] . ":" . + $gui->row_qty; + + if ($args->show_platforms) { + $platformCache = $tplan_mgr->getPlatforms($args->tplan_id, + array( + 'outputFormat' => 'mapAccessByID' + )); + } + + // Collect all tcases id and get all test suite paths + $targetSet = array(); + + foreach ($metrics as &$item) { + $targetSet[] = $item['tcase_id']; + } + $tree_mgr = new tree($db); + $path_info = $tree_mgr->get_full_path_verbose($targetSet); + unset($tree_mgr); + unset($targetSet); + + $data = array(); + foreach ($metrics as &$item) { + $row = array(); + $row[] = implode(" / ", $path_info[$item['tcase_id']]); + + $row[] = "" . + sprintf($links['full'], $item['tcase_id'], $item['tcase_id']) . + $item['full_external_id'] . ': ' . $item['name']; + + if ($args->show_platforms) { + $row[] = $platformCache[$item['platform_id']]['name']; + } + + if ($gui->options->testPriorityEnabled) { + // THIS HAS TO BE REFACTORED, because we can no do lot of calls + // because performance will be BAD + $row[] = $tplan_mgr->urgencyImportanceToPriorityLevel( + $item['urg_imp']); + } + + $row[] = strip_tags($item['summary']); + $data[] = $row; + } + + $gui->tableSet[] = buildTable($data, $args->tproject_id, + $args->show_platforms, $gui->options->testPriorityEnabled); + } +} + +$gui->warning_msg = lang_get($msg_key); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @param unknown $data + * @param unknown $tproject_id + * @param unknown $show_platforms + * @param unknown $priorityMgmtEnabled + * @return tlExtTable + */ +function buildTable($data, $tproject_id, $show_platforms, $priorityMgmtEnabled) +{ + $key2search = array( + 'testsuite', + 'testcase', + 'platform', + 'priority', + 'summary' + ); + foreach ($key2search as $key) { + $labels[$key] = lang_get($key); + } + $columns[] = array( + 'title_key' => 'testsuite', + 'width' => 20 + ); + + $columns[] = array( + 'title_key' => 'testcase', + 'width' => 25 + ); + + if ($show_platforms) { + $columns[] = array( + 'title_key' => 'platform', + 'width' => 10 + ); + } + + if ($priorityMgmtEnabled) { + $columns[] = array( + 'title_key' => 'priority', + 'type' => 'priority', + 'width' => 5 + ); + } + + $columns[] = array( + 'title_key' => 'summary', + 'type' => 'text', + 'width' => 40 + ); + + $matrix = new tlExtTable($columns, $data, 'tl_table_tc_without_tester'); + + $matrix->setGroupByColumnName($labels['testsuite']); + $matrix->setSortByColumnName($labels['testcase']); + $matrix->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + + if ($priorityMgmtEnabled) { + $matrix->addCustomBehaviour('priority', + array( + 'render' => 'priorityRenderer', + 'filter' => 'Priority' + )); + $matrix->setSortByColumnName($labels['priority']); + } + return $matrix; +} + +/** + * + * @param testplan $tplan_mgr + * @return stdClass + */ +function initArgs(&$tplan_mgr) +{ + $iParams = array( + "format" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->show_platforms = false; + $args->tproject_id = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + + $args->tplan_name = ''; + if (! $args->tplan_id) { + $args->tplan_id = intval( + isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0); + } + + if ($args->tplan_id > 0) { + $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); + $args->tplan_name = $tplan_info['name']; + $args->show_platforms = $tplan_mgr->hasLinkedPlatforms($args->tplan_id); + } + + return $args; +} + +/** + * + * @param unknown $lbl + * @param unknown $img + * @return string[] + */ +function featureLinks($lbl, $img) +{ + $links = array(); + + // %s => test case id + $links['exec_history'] = '' . + ' '; + + // %s => test case id + $links['edit'] = '' . + ' '; + + $links['full'] = $links['exec_history'] . $links['edit']; + return $links; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $gui = new stdClass(); + $gui->pageTitle = lang_get('caption_testCasesWithoutTester'); + $gui->warning_msg = ''; + $gui->tplan_name = $argsObj->tplan_name; + + $mgr = new testproject($dbHandler); + $dummy = $mgr->get_by_id($argsObj->tproject_id); + + $gui->tproject_name = $argsObj->tproject_name = $dummy['name']; + + $gui->options = new stdClass(); + $gui->options->testPriorityEnabled = $dummy['opt']->testPriorityEnabled; + $gui->labels = init_labels( + array( + 'design' => null, + 'execution' => null, + 'execution_history' => null, + 'match_count' => null + )); + + $gui->tableSet = null; + return $gui; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_metrics'); } - -$gui->warning_msg = lang_get($msg_key); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -function buildTable($data, $tproject_id, $show_platforms, $priorityMgmtEnabled) -{ - $key2search = array('testsuite','testcase','platform','priority','summary'); - foreach($key2search as $key) - { - $labels[$key] = lang_get($key); - } - $columns[] = array('title_key' => 'testsuite', 'width' => 20); - - $columns[] = array('title_key' => 'testcase', 'width' => 25); - - if ($show_platforms){ - $columns[] = array('title_key' => 'platform', 'width' => 10); - } - - if ($priorityMgmtEnabled) { - $columns[] = array('title_key' => 'priority', 'type' => 'priority', 'width' => 5); - } - - $columns[] = array('title_key' => 'summary', 'type' => 'text', 'width' => 40); - - $matrix = new tlExtTable($columns, $data, 'tl_table_tc_without_tester'); - - $matrix->setGroupByColumnName($labels['testsuite']); - $matrix->setSortByColumnName($labels['testcase']); - $matrix->addCustomBehaviour('text', array('render' => 'columnWrap')); - - if($priorityMgmtEnabled) - { - $matrix->addCustomBehaviour('priority', array('render' => 'priorityRenderer', 'filter' => 'Priority')); - $matrix->setSortByColumnName($labels['priority']); - } - return $matrix; -} - -/* - function: - - args : - - returns: - -*/ -function init_args(&$tplan_mgr) -{ - $iParams = array("format" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $args->show_platforms = false; - $args->tproject_id = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - - $args->tplan_name = ''; - if(!$args->tplan_id) - { - $args->tplan_id = intval(isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0); - } - - if($args->tplan_id > 0) - { - $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); - $args->tplan_name = $tplan_info['name']; - $args->show_platforms = $tplan_mgr->hasLinkedPlatforms($args->tplan_id); - } - - return $args; -} - -/** - * - * - */ -function featureLinks($lbl,$img) -{ - $links = array(); - - // %s => test case id - $links['exec_history'] = '' . - ' '; - - // %s => test case id - $links['edit'] = '' . - ' '; - - - $links['full'] = $links['exec_history'] . $links['edit']; - return $links; -} - -/** - * - */ -function initializeGui(&$dbHandler,&$argsObj) -{ - $gui = new stdClass(); - $gui->pageTitle = lang_get('caption_testCasesWithoutTester'); - $gui->warning_msg = ''; - $gui->tplan_name = $argsObj->tplan_name; - - $mgr = new testproject($dbHandler); - $dummy = $mgr->get_by_id($argsObj->tproject_id); - - $gui->tproject_name = $argsObj->tproject_name = $dummy['name']; - - $gui->options = new stdClass(); - $gui->options->testPriorityEnabled = $dummy['opt']->testPriorityEnabled; - $gui->labels = init_labels(array('design' => null, 'execution' => null, 'execution_history' => null, - 'match_count' => null)); - - $gui->tableSet = null; - return $gui; -} - - - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file diff --git a/lib/results/testPlanWithCF.php b/lib/results/testPlanWithCF.php index d513435c91..fd2f22d6a8 100644 --- a/lib/results/testPlanWithCF.php +++ b/lib/results/testPlanWithCF.php @@ -1,208 +1,222 @@ -getImages(); - -$cfield_mgr = new cfield_mgr($db); -$templateCfg = templateConfiguration(); -$tproject_mgr = new testproject($db); -$tplan_mgr = new testplan($db); -$tcase_mgr = new testcase($db); -$args = init_args($tplan_mgr); - -$gui = new stdClass(); -$gui->warning_msg = ''; -$gui->path_info = $gui->resultSet = $gui->tableSet = null; -$gui->pageTitle = lang_get('caption_testPlanWithCF'); -$gui->tproject_name = $args->tproject_name; -$gui->tplan_name = $args->tplan_name; -$gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tproject_id); - - -$labels = init_labels(array('design' => null)); -$testCaseSet = array(); - -if($tplan_mgr->count_testcases($args->tplan_id) > 0) -{ - $resultsCfg = config_get('results'); - $tcase_cfg = config_get('testcase_cfg'); - - // ----------------------------------------------------------------------------------- - $gui->code_status = $resultsCfg['code_status']; - - // Get the custom fields linked/enabled on execution to a test project - // This will be used on report to give name to header of columns that hold custom field value - $gui->cfields = $cfield_mgr->get_linked_cfields_at_testplan_design($args->tproject_id,1,'testcase', - null,null,null,'name'); - - if(!is_null($gui->cfields)) - { - foreach($gui->cfields as $key => $values) - { - $cf_place_holder['cfields'][$key] = ''; - } - } - // Now get TPlan -> Test Cases with custom field values - $cf_map = $cfield_mgr->get_linked_cfields_at_testplan_design($args->tproject_id,1,'testcase', - null,null,$args->tplan_id); - // need to transform in structure that allow easy display - // Every row is an execution with exec data plus a column that contains following map: - // 'cfields' => CFNAME1 => value - // CFNAME2 => value - $result = array(); - if(!is_null($cf_map)) - { - foreach($cf_map as $exec_id => $exec_info) - { - // Get common exec info and remove useless keys - $result[$exec_id] = $exec_info[0]; - // Collect custom fields values - $result[$exec_id] += $cf_place_holder; - foreach($exec_info as $cfield_data) - { - $result[$exec_id]['cfields'][$cfield_data['name']]=$cfield_data['value']; - } - } - } - if(($gui->row_qty = count($cf_map)) > 0 ) - { - $gui->warning_msg = ''; - $gui->resultSet = $result; - } else { - $gui->warning_msg = lang_get('no_linked_tplan_cf'); - } +getImages(); + +$cfield_mgr = new cfield_mgr($db); +$templateCfg = templateConfiguration(); +$tproject_mgr = new testproject($db); +$tplan_mgr = new testplan($db); +$tcaseMgr = new testcase($db); +$args = initArgs($tplan_mgr); + +$gui = new stdClass(); +$gui->warning_msg = ''; +$gui->path_info = $gui->resultSet = $gui->tableSet = null; +$gui->pageTitle = lang_get('caption_testPlanWithCF'); +$gui->tproject_name = $args->tproject_name; +$gui->tplan_name = $args->tplan_name; +$gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tproject_id); + +$labels = init_labels(array( + 'design' => null +)); +$testCaseSet = array(); + +if ($tplan_mgr->count_testcases($args->tplan_id) > 0) { + $resultsCfg = config_get('results'); + $tcase_cfg = config_get('testcase_cfg'); + + // ----------------------------------------------------------------------------------- + $gui->code_status = $resultsCfg['code_status']; + + // Get the custom fields linked/enabled on execution to a test project + // This will be used on report to give name to header of columns that hold custom field value + $gui->cfields = $cfield_mgr->get_linked_cfields_at_testplan_design( + $args->tproject_id, 1, 'testcase', null, null, null, 'name'); + + if (! is_null($gui->cfields)) { + foreach ($gui->cfields as $key => $values) { + $cf_place_holder['cfields'][$key] = ''; + } + } + // Now get TPlan -> Test Cases with custom field values + $cf_map = $cfield_mgr->get_linked_cfields_at_testplan_design( + $args->tproject_id, 1, 'testcase', null, null, $args->tplan_id); + // need to transform in structure that allow easy display + // Every row is an execution with exec data plus a column that contains following map: + // 'cfields' => CFNAME1 => value + // CFNAME2 => value + $result = array(); + if (! is_null($cf_map)) { + foreach ($cf_map as $exec_id => $exec_info) { + // Get common exec info and remove useless keys + $result[$exec_id] = $exec_info[0]; + // Collect custom fields values + $result[$exec_id] += $cf_place_holder; + foreach ($exec_info as $cfield_data) { + $result[$exec_id]['cfields'][$cfield_data['name']] = $cfield_data['value']; + } + } + } + if ($gui->row_qty = ! empty($cf_map)) { + $gui->warning_msg = ''; + $gui->resultSet = $result; + } else { + $gui->warning_msg = lang_get('no_linked_tplan_cf'); + } +} + +$table = buildExtTable($gui, $tcaseMgr, $labels, $imgSet['edit_icon']); + +if (! is_null($table)) { + $gui->tableSet[] = $table; +} +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @param stdClass $gui + * @param testcase $tcaseMgr + * @param array $labels + * @param string $edit_icon + * @return tlExtTable + */ +function buildExtTable($gui, $tcaseMgr, $labels, $edit_icon) +{ + $charset = config_get('charset'); + $title_sep = config_get('gui_title_separator_1'); + + $table = null; + if (! empty($gui->resultSet)) { + $columns = array(); + $columns[] = array( + 'title_key' => 'test_suite' + ); + $columns[] = array( + 'title_key' => 'test_case', + 'width' => 80, + 'type' => 'text' + ); + + foreach ($gui->cfields as $cfield) { + $dummy = array( + 'title' => $cfield['label'], + 'col_id' => 'id_cf_' . $cfield['name'], + 'type' => 'text' + ); + $columns[] = $dummy; + } + + // Extract the relevant data and build a matrix + $matrixData = array(); + + foreach ($gui->resultSet as $item) { + $rowData = array(); + + // Get test suite path + $dummy = $tcaseMgr->getPathLayered(array( + $item['tcase_id'] + )); + $dummy = end($dummy); + $rowData[] = $dummy['value']; + + $name = buildExternalIdString($gui->tcasePrefix, + $item['tc_external_id']) . $title_sep . $item['tcase_name']; + + // create linked icons + $edit_link = "" . + " "; + + $rowData[] = "" . $edit_link . $name; + $hasValue = false; + foreach ($item['cfields'] as $cf_value) { + $rowData[] = preg_replace('!\s+!', ' ', + htmlentities($cf_value, ENT_QUOTES, $charset)); + $hasValue = $cf_value ? true : false; + } + + if ($hasValue) { + $matrixData[] = $rowData; + } + } + + $table = new tlExtTable($columns, $matrixData, 'tl_table_tplan_with_cf'); + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + + $table->setGroupByColumnName(lang_get('test_suite')); + $table->setSortByColumnName(lang_get('test_case')); + $table->sortDirection = 'ASC'; + + $table->showToolbar = true; + $table->toolbarExpandCollapseGroupsButton = true; + $table->toolbarShowAllColumnsButton = true; + } + + return $table; +} + +/** + * + * @param testplan $tplan_mgr + * @return stdClass + */ +function initArgs(&$tplan_mgr) +{ + $iParams = array( + "format" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; + + $args->tplan_name = ''; + if (! $args->tplan_id) { + $args->tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0; + } + + if ($args->tplan_id > 0) { + $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); + $args->tplan_name = $tplan_info['name']; + } + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRightOnProj($db, 'testplan_metrics'); } - -$table = buildExtTable($gui,$tcase_mgr, $tplan_mgr, $args->tplan_id,$labels, $imgSet['edit_icon']); - -if (!is_null($table)) -{ - $gui->tableSet[] = $table; -} -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - * - */ -function buildExtTable($gui,$tcase_mgr,$tplan_mgr, $tplan_id, $labels, $edit_icon) -{ - - $charset = config_get('charset'); - $title_sep = config_get('gui_title_separator_1'); - - $table = null; - if(count($gui->resultSet) > 0) - { - $columns = array(); - $columns[] = array('title_key' => 'test_suite'); - $columns[] = array('title_key' => 'test_case', 'width' => 80, 'type' => 'text'); - - foreach ($gui->cfields as $cfield) - { - $dummy = array('title' => $cfield['label'], 'col_id' => 'id_cf_' . $cfield['name'], 'type' => 'text'); - $columns[] = $dummy; - } - - // Extract the relevant data and build a matrix - $matrixData = array(); - - foreach ($gui->resultSet as $item) - { - $rowData = array(); - - // Get test suite path - $dummy = $tcase_mgr->getPathLayered(array($item['tcase_id'])); - $dummy = end($dummy); - $rowData[] = $dummy['value']; - - $name = buildExternalIdString($gui->tcasePrefix, $item['tc_external_id']) . - $title_sep . $item['tcase_name']; - - // create linked icons - $edit_link = "" . - " "; - - $rowData[] = "" . $edit_link . $name;; - $hasValue = false; - foreach ($item['cfields'] as $cf_value) - { - $rowData[] = preg_replace('!\s+!', ' ', htmlentities($cf_value, ENT_QUOTES, $charset)); - $hasValue = $cf_value ? true : false; - } - - if ($hasValue) - { - $matrixData[] = $rowData; - } - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_tplan_with_cf'); - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - - $table->setGroupByColumnName(lang_get('test_suite')); - $table->setSortByColumnName(lang_get('test_case')); - $table->sortDirection = 'ASC'; - - $table->showToolbar = true; - $table->toolbarExpandCollapseGroupsButton = true; - $table->toolbarShowAllColumnsButton = true; - } - return($table); -} - -/* - function: - - args : - - returns: - - */ -function init_args(&$tplan_mgr) -{ - $iParams = array("format" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; - - $args->tplan_name = ''; - if(!$args->tplan_id) - { - $args->tplan_id = isset($_SESSION['testplanID']) ? $_SESSION['testplanID'] : 0; - } - - if($args->tplan_id > 0) - { - $tplan_info = $tplan_mgr->get_by_id($args->tplan_id); - $args->tplan_name = $tplan_info['name']; - } - return $args; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} -?> \ No newline at end of file diff --git a/lib/results/topLevelSuitesBarChart.php b/lib/results/topLevelSuitesBarChart.php index 7dd6bce27e..66d541dd1c 100644 --- a/lib/results/topLevelSuitesBarChart.php +++ b/lib/results/topLevelSuitesBarChart.php @@ -1,162 +1,165 @@ -scale = new stdClass(); - -$chart_cfg = config_get('results'); -$chart_cfg = $chart_cfg['charts']['dimensions']['topLevelSuitesBarChart']; - -$cfg->chartTitle = lang_get($chart_cfg['chartTitle']); -$cfg->XSize = $chart_cfg['XSize']; -$cfg->YSize = $chart_cfg['YSize']; -$cfg->beginX = $chart_cfg['beginX']; -$cfg->beginY = $chart_cfg['beginY']; -$cfg->scale->legendXAngle = $chart_cfg['legendXAngle']; - -$args = init_args($db); -$info = getDataAndScale($db,$args); -if( property_exists($args,'debug') ) -{ - new dBug($info); - die(); +scale = new stdClass(); + +$chart_cfg = config_get('results'); +$chart_cfg = $chart_cfg['charts']['dimensions']['topLevelSuitesBarChart']; + +$cfg->chartTitle = lang_get($chart_cfg['chartTitle']); +$cfg->XSize = $chart_cfg['XSize']; +$cfg->YSize = $chart_cfg['YSize']; +$cfg->beginX = $chart_cfg['beginX']; +$cfg->beginY = $chart_cfg['beginY']; +$cfg->scale->legendXAngle = $chart_cfg['legendXAngle']; + +$args = initArgs($db); +$info = getDataAndScale($db, $args); +if (property_exists($args, 'debug')) { + new dBug($info); + die(); +} +createChart($info, $cfg); + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function getDataAndScale(&$dbHandler, $argsObj) +{ + $obj = new stdClass(); + $totals = null; + $resultsCfg = config_get('results'); + $metricsMgr = new tlTestPlanMetrics($dbHandler); + + $dataSet = $metricsMgr->getRootTestSuites($argsObj->tplan_id, + $argsObj->tproject_id); + $dummy = $metricsMgr->getStatusTotalsByTopLevelTestSuiteForRender( + $argsObj->tplan_id); + $obj->canDraw = ! is_null($dummy->info); + + if (property_exists($argsObj, 'debug')) { + new dBug($dummy->info); + } + + if ($obj->canDraw) { + // // Process to enable alphabetical order + $item_descr = array_flip($dataSet); + ksort($item_descr); + foreach ($item_descr as $name => $tsuite_id) { + if (isset($dummy->info[$tsuite_id])) { + $items[] = htmlspecialchars($name); + $rmap = $dummy->info[$tsuite_id]['details']; + foreach ($rmap as $key => $value) { + $totals[$key][] = $value['qty']; + } + } else { + // make things work, but create log this is not ok + tlog( + __FILE__ . '::' . __FUNCTION__ . 'Missing item: name/id:' . + "$name/$tsuite_id", 'DEBUG'); + } + } + } + + $obj->xAxis = new stdClass(); + $obj->xAxis->values = $items; + $obj->xAxis->serieName = 'Serie8'; + $obj->series_color = null; + + foreach ($totals as $status => $values) { + $obj->chart_data[] = $values; + $obj->series_label[] = lang_get($resultsCfg['status_label'][$status]); + if (isset($resultsCfg['charts']['status_colour'][$status])) { + $obj->series_color[] = $resultsCfg['charts']['status_colour'][$status]; + } + } + + return $obj; +} + +/** + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + 0, + 64 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + if (! is_null($args->apikey)) { + $cerbero = new stdClass(); + $cerbero->args = new stdClass(); + $cerbero->args->tproject_id = $args->tproject_id; + $cerbero->args->tplan_id = $args->tplan_id; + + if (strlen($args->apikey) == 32) { + $cerbero->args->getAccessAttr = true; + $cerbero->method = 'checkRights'; + $cerbero->redirect_target = "../../login.php?note=logout"; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, $cerbero); + } else { + $args->addOpAccess = false; + $cerbero->method = null; + $cerbero->args->getAccessAttr = false; + setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $cerbero); + } + } else { + testlinkInitPage($dbHandler, false, false, "checkRights"); + } + + if (isset($_REQUEST['debug'])) { + $args->debug = 'yes'; + } + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); } -createChart($info,$cfg); - - -/* - function: getDataAndScale - - args : - - returns: - -*/ -function getDataAndScale(&$dbHandler,$argsObj) -{ - $obj = new stdClass(); - $totals = null; - $resultsCfg = config_get('results'); - $metricsMgr = new tlTestPlanMetrics($dbHandler); - - $dataSet = $metricsMgr->getRootTestSuites($argsObj->tplan_id,$argsObj->tproject_id); - $dummy = $metricsMgr->getStatusTotalsByTopLevelTestSuiteForRender($argsObj->tplan_id); - $obj->canDraw = !is_null($dummy->info); - - if( property_exists($argsObj,'debug') ) - { - new dBug($dummy->info); - } - - if($obj->canDraw) - { - //// Process to enable alphabetical order - $item_descr = array_flip($dataSet); - ksort($item_descr); - foreach($item_descr as $name => $tsuite_id) - { - if( isset($dummy->info[$tsuite_id]) ) - { - $items[]=htmlspecialchars($name); - $rmap = $dummy->info[$tsuite_id]['details']; - foreach($rmap as $key => $value) - { - $totals[$key][]=$value['qty']; - } - } - else - { - // make things work, but create log this is not ok - tlog(__FILE__ . '::' . __FUNCTION__ . 'Missing item: name/id:' . - "$name/$tsuite_id", 'DEBUG'); - } - } - } - - $obj->xAxis = new stdClass(); - $obj->xAxis->values = $items; - $obj->xAxis->serieName = 'Serie8'; - $obj->series_color = null; - - foreach($totals as $status => $values) - { - $obj->chart_data[] = $values; - $obj->series_label[] = lang_get($resultsCfg['status_label'][$status]); - if( isset($resultsCfg['charts']['status_colour'][$status]) ) - { - $obj->series_color[] = $resultsCfg['charts']['status_colour'][$status]; - } - } - - return $obj; -} - -/** - * - */ -function init_args(&$dbHandler) -{ - $iParams = array("apikey" => array(tlInputParameter::STRING_N,0,64), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - if( !is_null($args->apikey) ) - { - $cerbero = new stdClass(); - $cerbero->args = new stdClass(); - $cerbero->args->tproject_id = $args->tproject_id; - $cerbero->args->tplan_id = $args->tplan_id; - - if(strlen($args->apikey) == 32) - { - $cerbero->args->getAccessAttr = true; - $cerbero->method = 'checkRights'; - $cerbero->redirect_target = "../../login.php?note=logout"; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,$cerbero); - } - else - { - $args->addOpAccess = false; - $cerbero->method = null; - $cerbero->args->getAccessAttr = false; - setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$cerbero); - } - } - else - { - testlinkInitPage($dbHandler,false,false,"checkRights"); - // $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - - if( isset($_REQUEST['debug']) ) - { - $args->debug = 'yes'; - } - return $args; -} - -/** - * - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} \ No newline at end of file diff --git a/lib/results/uncoveredTestCases.php b/lib/results/uncoveredTestCases.php index ae43798223..bfb2d929bf 100644 --- a/lib/results/uncoveredTestCases.php +++ b/lib/results/uncoveredTestCases.php @@ -1,123 +1,135 @@ -getOptionReqSpec($args->tproject_id); -$reqSpec = $tproject_mgr->genComboReqSpec($args->tproject_id); -$uncovered = null; -$gui = new stdClass(); -$gui->items = null; -$gui->tproject_name = $args->tproject_name; -$gui->has_reqspec = count($reqSpec) > 0; -$gui->has_requirements = false; -$gui->has_tc = false; - -if($gui->has_reqspec) -{ - // Check if at least one of these requirement spec are not empty. - $reqSpecMgr = new requirement_spec_mgr($db); - foreach($reqSpec as $reqSpecID => $name) - { - if($gui->has_requirements = ($reqSpecMgr->get_requirements_count($reqSpecID) > 0)) - break; - } - unset($reqSpecMgr); -} -if($gui->has_requirements) -{ - // get all test cases id (active/inactive) in test project - $tcasesID = null; - $tproject_mgr->get_all_testcases_id($args->tproject_id,$tcasesID); - - if(!is_null($tcasesID) && count($tcasesID) > 0) - { - $debugMsg = 'File: ' . basename(__FILE__) . ' - Line: ' . __LINE__ . ' - '; - $sql = " /* $debugMsg */ " . - " SELECT NHA.id AS tc_id, NHA.name, NHA.parent_id AS testsuite_id," . - " NT.description, REQC.req_id " . - " FROM {$tables['nodes_hierarchy']} NHA " . - " JOIN {$tables['node_types']} NT ON NHA.node_type_id=NT.id " . - " LEFT OUTER JOIN {$tables['req_coverage']} REQC on REQC.testcase_id=NHA.id " . - " WHERE NT.description='testcase' AND NHA.id IN (" . implode(",",$tcasesID) . ") " . - " and REQC.req_id IS NULL " ; - $uncovered = $db->fetchRowsIntoMap($sql,'tc_id'); - } -} - - -if($gui->has_tc = (!is_null($uncovered) && count($uncovered) > 0) ) -{ - // Get external ID - $testSet = array_keys($uncovered); - $inClause = implode(',',$testSet); - $debugMsg = 'File: ' . basename(__FILE__) . ' - Line: ' . __LINE__ . ' - '; - $sql = "/* $debugMsg */ " . - " SELECT distinct NHA.id AS tc_id, TCV.tc_external_id " . - " FROM {$tables['nodes_hierarchy']} NHA, " . - " {$tables['nodes_hierarchy']} NHB, " . - " {$tables['tcversions']} TCV, {$tables['node_types']} NT " . - " WHERE NHA.node_type_id=NT.id AND NHA.id=NHB.parent_id AND NHB.id=TCV.id " . - " AND NHA.id IN ({$inClause}) AND NT.description='testcase' "; - $external_id = $db->fetchRowsIntoMap($sql,'tc_id'); - foreach($external_id as $key => $value) - { - $uncovered[$key]['external_id'] = $value['tc_external_id']; - } - // $out = gen_spec_view($db,'uncoveredtestcases',$args->tproject_id,$args->tproject_id,null, - // $uncovered,null,null,$testSet,1,0,0); - $opt = array('write_button_only_if_linked' => 1); - $filters = array('testcases' => $testSet); - $out = gen_spec_view($db,'uncoveredtestcases',$args->tproject_id,$args->tproject_id,null, - $uncovered,null,$filters,$opt); - - $gui->items = $out['spec_view']; -} - -$tcase_cfg = config_get('testcase_cfg'); -$gui->pageTitle = lang_get('report_testcases_without_requirement'); -$gui->testCasePrefix = $tproject_mgr->getTestCasePrefix($args->tproject_id); -$gui->testCasePrefix .= $tcase_cfg->glue_character; - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -function init_args() -{ - $args = new stdClass(); - $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; - - return $args; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'testplan_metrics'); -} -?> \ No newline at end of file +getOptionReqSpec($args->tproject_id); +$reqSpec = $tproject_mgr->genComboReqSpec($args->tproject_id); +$uncovered = null; +$gui = new stdClass(); +$gui->items = null; +$gui->tproject_name = $args->tproject_name; +$gui->has_reqspec = count($reqSpec) > 0; +$gui->has_requirements = false; +$gui->has_tc = false; + +if ($gui->has_reqspec) { + // Check if at least one of these requirement spec are not empty. + $reqSpecMgr = new requirement_spec_mgr($db); + foreach ($reqSpec as $reqSpecID => $name) { + if ($gui->has_requirements = ($reqSpecMgr->get_requirements_count( + $reqSpecID) > 0)) { + break; + } + } + unset($reqSpecMgr); +} +if ($gui->has_requirements) { + // get all test cases id (active/inactive) in test project + $tcasesID = null; + $tproject_mgr->get_all_testcases_id($args->tproject_id, $tcasesID); + + if (! is_null($tcasesID) && count($tcasesID) > 0) { + $debugMsg = 'File: ' . basename(__FILE__) . ' - Line: ' . __LINE__ . + ' - '; + $sql = " /* $debugMsg */ " . + " SELECT NHA.id AS tc_id, NHA.name, NHA.parent_id AS testsuite_id," . + " NT.description, REQC.req_id " . + " FROM {$tables['nodes_hierarchy']} NHA " . + " JOIN {$tables['node_types']} NT ON NHA.node_type_id=NT.id " . + " LEFT OUTER JOIN {$tables['req_coverage']} REQC on REQC.testcase_id=NHA.id " . + " WHERE NT.description='testcase' AND NHA.id IN (" . + implode(",", $tcasesID) . ") " . " and REQC.req_id IS NULL "; + $uncovered = $db->fetchRowsIntoMap($sql, 'tc_id'); + } +} + +if ($gui->has_tc = (! is_null($uncovered) && ! empty($uncovered))) { + // Get external ID + $testSet = array_keys($uncovered); + $inClause = implode(',', $testSet); + $debugMsg = 'File: ' . basename(__FILE__) . ' - Line: ' . __LINE__ . ' - '; + $sql = "/* $debugMsg */ " . + " SELECT distinct NHA.id AS tc_id, TCV.tc_external_id " . + " FROM {$tables['nodes_hierarchy']} NHA, " . + " {$tables['nodes_hierarchy']} NHB, " . + " {$tables['tcversions']} TCV, {$tables['node_types']} NT " . + " WHERE NHA.node_type_id=NT.id AND NHA.id=NHB.parent_id AND NHB.id=TCV.id " . + " AND NHA.id IN ({$inClause}) AND NT.description='testcase' "; + $external_id = $db->fetchRowsIntoMap($sql, 'tc_id'); + foreach ($external_id as $key => $value) { + $uncovered[$key]['external_id'] = $value['tc_external_id']; + } + $opt = array( + 'write_button_only_if_linked' => 1 + ); + $filters = array( + 'testcases' => $testSet + ); + $out = gen_spec_view($db, 'uncoveredtestcases', $args->tproject_id, + $args->tproject_id, null, $uncovered, null, $filters, $opt); + + $gui->items = $out['spec_view']; +} + +$tcase_cfg = config_get('testcase_cfg'); +$gui->pageTitle = lang_get('report_testcases_without_requirement'); +$gui->testCasePrefix = $tproject_mgr->getTestCasePrefix($args->tproject_id); +$gui->testCasePrefix .= $tcase_cfg->glue_character; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $args = new stdClass(); + $args->tproject_id = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : ''; + + return $args; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'testplan_metrics'); +} +?> diff --git a/lib/search/search.php b/lib/search/search.php index 875d6c9679..a11845d86a 100644 --- a/lib/search/search.php +++ b/lib/search/search.php @@ -1,438 +1,476 @@ -initEnv(); - -$args = $cmdMgr->getArgs(); -$gui = $cmdMgr->getGui(); -$cmdMgr->initSchema(); -$treeMgr = new tree($db); -$cfieldMgr = new cfield_mgr($db); - - -$targetSet = cleanUpTarget($db,$args->target); -$canUseTarget = (count($targetSet) > 0); - -if($args->oneCheck == false) { - $gui->caller = 'search'; - $smarty->assign('gui',$gui); - $smarty->display($templateCfg->template_dir . $tpl); - exit(); -} - -if($canUseTarget == false && $args->oneValueOK == false) { - $smarty->assign('gui',$gui); - $smarty->display($templateCfg->template_dir . $tpl); - exit(); -} - -// Processing -$map = null; - -// CF belongs to ? -$tc_cf_id = null; -$req_cf_id = null; -if( $args->custom_field_id > 0) { - if ( isset( $gui->design_cf_tc[$args->custom_field_id] ) ) { - $tc_cf_id = $args->custom_field_id; - } - - if ( isset( $gui->design_cf_req[$args->custom_field_id] ) ) { - $req_cf_id = $args->custom_field_id; - } -} - -$args->reqType = null; -if($args->reqType != '') { - $args->reqType = str_replace('RQ','', $args->reqTypes); -} - -if( ($args->tproject_id > 0) && $args->doAction == 'doSearch') { - $tables = $cmdMgr->getTables(); - $views = $cmdMgr->getViews(); - - $from = array('by_keyword_id' => ' ', 'by_custom_field' => ' ', - 'by_requirement_doc_id' => '', 'users' => ''); - $tcaseID = null; - - $emptyTestProject = true; - - // Need to get all test cases to filter - $tcaseSet = $cmdMgr->getTestCaseIDSet($args->tproject_id); -} - - -$mapTC = null; -$mapTS = null; -$mapRS = null; -$mapRQ = null; - -// Search on Test Suites -if( $canUseTarget && ($args->ts_summary || $args->ts_title) ) { - $mapTS = $cmdMgr->searchTestSuites($targetSet,$canUseTarget); -} - -// Requirment SPECification -if( $canUseTarget && ($args->rs_scope || $args->rs_title) ) { - $mapRS = $cmdMgr->searchReqSpec($targetSet,$canUseTarget); -} - -// REQuirements -if( $args->rq_scope || $args->rq_title || $args->rq_doc_id || ($req_cf_id > 0) ) { - $mapRQ = $cmdMgr->searchReq($targetSet,$canUseTarget,$req_cf_id); -} - - -$hasTestCases = (!is_null($tcaseSet) && count($tcaseSet) > 0); -if( $hasTestCases ) { - $emptyTestProject = false; - $mapTC = $cmdMgr->searchTestCases($tcaseSet,$targetSet,$canUseTarget,$tc_cf_id); -} - -// Render Results -if( !is_null($mapTC) ) { - $tcase_mgr = new testcase($db); - $tcase_set = array_keys($mapTC); - $options = array('output_format' => 'path_as_string'); - $gui->path_info = $treeMgr->get_full_path_verbose($tcase_set, $options); - $gui->resultSet = $mapTC; -} else if ($emptyTestProject) { - $gui->warning_msg = lang_get('empty_testproject'); -} else { - $gui->warning_msg = lang_get('no_records_found'); -} - -$img = $smarty->getImages(); -$table = buildTCExtTable($gui, $charset, $img['edit_icon'], $img['history_small']); - -if (!is_null($table)) { - $gui->tableSet[] = $table; -} - -$table = null; -if( !is_null($mapTS)) { - $gui->resultTestSuite = $mapTS; - $table = buildTSExtTable($gui, $charset, $img['edit_icon'], $img['history_small']); -} - -$gui->warning_msg = ''; -if(!is_null($table)) { - $gui->tableSet[] = $table; -} - -$table = null; -if( !is_null($mapRS)) { - $gui->resultReqSpec = $mapRS; - $table = buildRSExtTable($gui, $charset, $img['edit_icon'], $img['history_small']); -} - -$gui->warning_msg = ''; -if(!is_null($table)) { - $gui->tableSet[] = $table; -} - -$table = null; -if( !is_null($mapRQ)) { - $gui->resultReq = $mapRQ; - $req_set = array_keys($mapRQ); - $options = array('output_format' => 'path_as_string'); - $gui->path_info = $treeMgr->get_full_path_verbose($req_set,$options); - - $table = buildRQExtTable($gui, $charset); -} - -$gui->warning_msg = ''; -if(!is_null($table)) { - $gui->tableSet[] = $table; -} - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $tpl); - - -/** - * - * - */ -function buildTCExtTable($gui, $charset, $edit_icon, $history_icon) { - $table = null; - $designCfg = getWebEditorCfg('design'); - $designType = $designCfg['type']; - - if(count((array)$gui->resultSet) > 0) { - $labels = array('test_suite' => lang_get('test_suite'), 'test_case' => lang_get('test_case')); - $columns = array(); - - $columns[] = array('title_key' => 'test_suite'); - $columns[] = array('title_key' => 'test_case', 'type' => 'text'); - - $columns[] = array('title_key' => 'summary'); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - $titleSeparator = config_get('gui_title_separator_1'); - - foreach($gui->resultSet as $result) - { - $rowData = array(); - $rowData[] = htmlentities($gui->path_info[$result['testcase_id']], ENT_QUOTES, $charset); - - // build test case link - $history_link = "" . - " "; - $edit_link = "" . - " "; - $tcaseName = htmlentities($gui->tcasePrefix, ENT_QUOTES, $charset) . $result['tc_external_id'] . - " [v" . $result['version'] . "]" . $titleSeparator . - htmlentities($result['name'], ENT_QUOTES, $charset); - - $rowData[] = $history_link . $edit_link . $tcaseName; - $rowData[] = ($designType == 'none' ? nl2br($result['summary']) : $result['summary']); - - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_test_case_search'); - - $table->setGroupByColumnName($labels['test_suite']); - $table->setSortByColumnName($labels['test_case']); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->allowMultiSort = false; - $table->toolbarRefreshButton = false; - $table->toolbarShowAllColumnsButton = false; - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - $table->storeTableState = false; - } - return($table); -} - -/** - * - * - */ -function buildTSExtTable($gui, $charset, $edit_icon, $history_icon) -{ - $table = null; - $designCfg = getWebEditorCfg('design'); - $designType = $designCfg['type']; - - if(count($gui->resultTestSuite) > 0) - { - $labels = array('test_suite' => lang_get('test_suite'), - 'details' => lang_get('details')); - $columns = array(); - - $columns[] = array('title_key' => 'test_suite', 'type' => 'text'); - $columns[] = array('title_key' => 'details'); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - foreach($gui->resultTestSuite as $result) - { - $edit_link = "" . - " "; - - $rowData = array(); - - $rowData[] = $edit_link . htmlentities($result['name'], ENT_QUOTES, $charset); - - $rowData[] = ($designType == 'none' ? nl2br($result['details']) : $result['details']); - - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_test_suite_search'); - - $table->setSortByColumnName($labels['test_suite']); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->allowMultiSort = false; - $table->toolbarRefreshButton = false; - $table->toolbarShowAllColumnsButton = false; - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - $table->storeTableState = false; - } - return $table; -} - -/** - * - * - */ -function buildRSExtTable($gui, $charset, $edit_icon, $history_icon) -{ - $table = null; - $designCfg = getWebEditorCfg('design'); - $designType = $designCfg['type']; - - if(count($gui->resultReqSpec) > 0) - { - $labels = array('req_spec' => lang_get('req_spec'), - 'scope' => lang_get('scope')); - $columns = array(); - - $columns[] = array('title_key' => 'req_spec', 'type' => 'text'); - $columns[] = array('title_key' => 'scope'); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - foreach($gui->resultReqSpec as $result) - { - $edit_link = "" . - " "; - - $rowData = array(); - - $rowData[] = $edit_link . - htmlentities($result['name'] . "[r{$result['revision']}]", ENT_QUOTES, $charset); - - $rowData[] = ($designType == 'none' ? nl2br($result['scope']) : $result['scope']); - - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_req_spec_search'); - - $table->setSortByColumnName($labels['req_spec']); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->allowMultiSort = false; - $table->toolbarRefreshButton = false; - $table->toolbarShowAllColumnsButton = false; - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - $table->storeTableState = false; - } - return $table; -} - - - -/** - * - * - */ -function buildRQExtTable($gui, $charset) -{ - $table = null; - $designCfg = getWebEditorCfg('design'); - $designType = $designCfg['type']; - - $lbl = array('edit' => 'requirement', 'req_spec' => 'req_spec', - 'requirement' => 'requirement','scope' => 'scope', - 'version_revision_tag' => 'version_revision_tag'); - - $labels = init_labels($lbl); - $edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; - - if(count($gui->resultReq) > 0) - { - $columns = array(); - - $columns[] = array('title_key' => 'req_spec'); - $columns[] = array('title_key' => 'requirement', 'type' => 'text'); - - $columns[] = array('title_key' => 'scope'); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - $key2loop = array_keys($gui->resultReq); - $img = ""; - $reqVerHref = '' . $labels['version_revision_tag'] . ' '; - $reqRevHref = '' . $labels['version_revision_tag'] . ' '; - - foreach($key2loop as $req_id) - { - $rowData = array(); - $itemSet = $gui->resultReq[$req_id]; - $rfx = $itemSet; - - // We Group by Requirement path - $rowData[] = htmlentities($gui->path_info[$rfx['req_id']], ENT_QUOTES, $charset); - - $edit_link = "" . "{$img} "; - $title = htmlentities($rfx['req_doc_id'], ENT_QUOTES, $charset) . ":" . - htmlentities($rfx['name'], ENT_QUOTES, $charset); - - $matches = ''; - $rowData[] = $edit_link . $title . ' ' . $matches; - $rowData[] = ($designType == 'none' ? nl2br($rfx['scope']) : $rfx['scope']); - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_req_search'); - - $table->setGroupByColumnName($labels['req_spec']); - $table->setSortByColumnName($labels['requirement']); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->allowMultiSort = false; - $table->toolbarRefreshButton = false; - $table->toolbarShowAllColumnsButton = false; - $table->storeTableState = false; - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - } - return($table); -} - - - - -/** - * - */ -function cleanUpTarget(&$dbHandler,$target) -{ - $s = preg_replace("/ {2,}/", " ", $target); - $theSet = explode(' ',$s); - $targetSet = array(); - foreach($theSet as $idx => $val) - { - if(trim($val) != '') - { - $targetSet[] = $dbHandler->prepare_string($val); - } - } - return $targetSet; +initEnv(); + +$args = $cmdMgr->getArgs(); +$gui = $cmdMgr->getGui(); +$cmdMgr->initSchema(); +$treeMgr = new tree($db); +$cfieldMgr = new cfield_mgr($db); + +$targetSet = cleanUpTarget($db, $args->target); +$canUseTarget = (count($targetSet) > 0); + +if (! $args->oneCheck) { + $gui->caller = 'search'; + $smarty->assign('gui', $gui); + $smarty->display($templateCfg->template_dir . $tpl); + exit(); +} + +if (! $canUseTarget && ! $args->oneValueOK) { + $smarty->assign('gui', $gui); + $smarty->display($templateCfg->template_dir . $tpl); + exit(); +} + +// Processing +$map = null; + +// CF belongs to ? +$tc_cf_id = null; +$req_cf_id = null; +if ($args->custom_field_id > 0) { + if (isset($gui->design_cf_tc[$args->custom_field_id])) { + $tc_cf_id = $args->custom_field_id; + } + + if (isset($gui->design_cf_req[$args->custom_field_id])) { + $req_cf_id = $args->custom_field_id; + } +} + +$args->reqType = null; +if ($args->reqType != '') { + $args->reqType = str_replace('RQ', '', $args->reqTypes); +} + +if (($args->tproject_id > 0) && $args->doAction == 'doSearch') { + $tables = $cmdMgr->getTables(); + $views = $cmdMgr->getViews(); + + $from = array( + 'by_keyword_id' => ' ', + 'by_custom_field' => ' ', + 'by_requirement_doc_id' => '', + 'users' => '' + ); + $tcaseID = null; + + $emptyTestProject = true; + + // Need to get all test cases to filter + $tcaseSet = $cmdMgr->getTestCaseIDSet($args->tproject_id); +} + +$mapTC = null; +$mapTS = null; +$mapRS = null; +$mapRQ = null; + +// Search on Test Suites +if ($canUseTarget && ($args->ts_summary || $args->ts_title)) { + $mapTS = $cmdMgr->searchTestSuites($targetSet, $canUseTarget); +} + +// Requirment SPECification +if ($canUseTarget && ($args->rs_scope || $args->rs_title)) { + $mapRS = $cmdMgr->searchReqSpec($targetSet, $canUseTarget); +} + +// REQuirements +if ($args->rq_scope || $args->rq_title || $args->rq_doc_id || ($req_cf_id > 0)) { + $mapRQ = $cmdMgr->searchReq($targetSet, $canUseTarget, $req_cf_id); +} + +$hasTestCases = (! is_null($tcaseSet) && count($tcaseSet) > 0); +if ($hasTestCases) { + $emptyTestProject = false; + $mapTC = $cmdMgr->searchTestCases($tcaseSet, $targetSet, $canUseTarget, + $tc_cf_id); +} + +// Render Results +if (! is_null($mapTC)) { + $tcaseMgr = new testcase($db); + $tcase_set = array_keys($mapTC); + $options = array( + 'output_format' => 'path_as_string' + ); + $gui->path_info = $treeMgr->get_full_path_verbose($tcase_set, $options); + $gui->resultSet = $mapTC; +} elseif ($emptyTestProject) { + $gui->warning_msg = lang_get('empty_testproject'); +} else { + $gui->warning_msg = lang_get('no_records_found'); +} + +$img = $smarty->getImages(); +$table = buildTCExtTable($gui, $charset, $img['edit_icon'], + $img['history_small']); + +if (! is_null($table)) { + $gui->tableSet[] = $table; +} + +$table = null; +if (! is_null($mapTS)) { + $gui->resultTestSuite = $mapTS; + $table = buildTSExtTable($gui, $charset, $img['edit_icon']); +} + +$gui->warning_msg = ''; +if (! is_null($table)) { + $gui->tableSet[] = $table; +} + +$table = null; +if (! is_null($mapRS)) { + $gui->resultReqSpec = $mapRS; + $table = buildRSExtTable($gui, $charset, $img['edit_icon']); +} + +$gui->warning_msg = ''; +if (! is_null($table)) { + $gui->tableSet[] = $table; +} + +$table = null; +if (! is_null($mapRQ)) { + $gui->resultReq = $mapRQ; + $req_set = array_keys($mapRQ); + $options = array( + 'output_format' => 'path_as_string' + ); + $gui->path_info = $treeMgr->get_full_path_verbose($req_set, $options); + + $table = buildRQExtTable($gui, $charset); +} + +$gui->warning_msg = ''; +if (! is_null($table)) { + $gui->tableSet[] = $table; +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $tpl); + +/** + */ +function buildTCExtTable($gui, $charset, $edit_icon, $history_icon) +{ + $table = null; + $designCfg = getWebEditorCfg('design'); + $designType = $designCfg['type']; + + if (count((array) $gui->resultSet) > 0) { + $labels = array( + 'test_suite' => lang_get('test_suite'), + 'test_case' => lang_get('test_case') + ); + $columns = array(); + + $columns[] = array( + 'title_key' => 'test_suite' + ); + $columns[] = array( + 'title_key' => 'test_case', + 'type' => 'text' + ); + + $columns[] = array( + 'title_key' => 'summary' + ); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + $titleSeparator = config_get('gui_title_separator_1'); + + foreach ($gui->resultSet as $result) { + $rowData = array(); + $rowData[] = htmlentities($gui->path_info[$result['testcase_id']], + ENT_QUOTES, $charset); + + // build test case link + $history_link = "" . + " "; + $edit_link = "" . + " "; + $tcaseName = htmlentities($gui->tcasePrefix, ENT_QUOTES, $charset) . + $result['tc_external_id'] . " [v" . $result['version'] . "]" . + $titleSeparator . + htmlentities($result['name'], ENT_QUOTES, $charset); + + $rowData[] = $history_link . $edit_link . $tcaseName; + $rowData[] = ($designType == 'none' ? nl2br($result['summary']) : $result['summary']); + + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, + 'tl_table_test_case_search'); + + $table->setGroupByColumnName($labels['test_suite']); + $table->setSortByColumnName($labels['test_case']); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->allowMultiSort = false; + $table->toolbarRefreshButton = false; + $table->toolbarShowAllColumnsButton = false; + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + $table->storeTableState = false; + } + return $table; +} + +/** + */ +function buildTSExtTable($gui, $charset, $edit_icon) +{ + $table = null; + $designCfg = getWebEditorCfg('design'); + $designType = $designCfg['type']; + + if (! empty($gui->resultTestSuite)) { + $labels = array( + 'test_suite' => lang_get('test_suite'), + 'details' => lang_get('details') + ); + $columns = array(); + + $columns[] = array( + 'title_key' => 'test_suite', + 'type' => 'text' + ); + $columns[] = array( + 'title_key' => 'details' + ); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + foreach ($gui->resultTestSuite as $result) { + $edit_link = "" . + " "; + + $rowData = array(); + + $rowData[] = $edit_link . + htmlentities($result['name'], ENT_QUOTES, $charset); + + $rowData[] = ($designType == 'none' ? nl2br($result['details']) : $result['details']); + + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, + 'tl_table_test_suite_search'); + + $table->setSortByColumnName($labels['test_suite']); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->allowMultiSort = false; + $table->toolbarRefreshButton = false; + $table->toolbarShowAllColumnsButton = false; + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + $table->storeTableState = false; + } + return $table; +} + +/** + */ +function buildRSExtTable($gui, $charset, $edit_icon) +{ + $table = null; + $designCfg = getWebEditorCfg('design'); + $designType = $designCfg['type']; + + if (! empty($gui->resultReqSpec)) { + $labels = array( + 'req_spec' => lang_get('req_spec'), + 'scope' => lang_get('scope') + ); + $columns = array(); + + $columns[] = array( + 'title_key' => 'req_spec', + 'type' => 'text' + ); + $columns[] = array( + 'title_key' => 'scope' + ); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + foreach ($gui->resultReqSpec as $result) { + $edit_link = "" . + " "; + + $rowData = array(); + + $rowData[] = $edit_link . + htmlentities($result['name'] . "[r{$result['revision']}]", + ENT_QUOTES, $charset); + + $rowData[] = ($designType == 'none' ? nl2br($result['scope']) : $result['scope']); + + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, + 'tl_table_req_spec_search'); + + $table->setSortByColumnName($labels['req_spec']); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->allowMultiSort = false; + $table->toolbarRefreshButton = false; + $table->toolbarShowAllColumnsButton = false; + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + $table->storeTableState = false; + } + return $table; +} + +/** + */ +function buildRQExtTable($gui, $charset) +{ + $table = null; + $designCfg = getWebEditorCfg('design'); + $designType = $designCfg['type']; + + $lbl = array( + 'edit' => 'requirement', + 'req_spec' => 'req_spec', + 'requirement' => 'requirement', + 'scope' => 'scope', + 'version_revision_tag' => 'version_revision_tag' + ); + + $labels = init_labels($lbl); + $edit_icon = TL_THEME_IMG_DIR . "edit_icon.png"; + + if (! empty($gui->resultReq)) { + $columns = array(); + + $columns[] = array( + 'title_key' => 'req_spec' + ); + $columns[] = array( + 'title_key' => 'requirement', + 'type' => 'text' + ); + + $columns[] = array( + 'title_key' => 'scope' + ); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + $key2loop = array_keys($gui->resultReq); + $img = ""; + + foreach ($key2loop as $req_id) { + $rowData = array(); + $itemSet = $gui->resultReq[$req_id]; + $rfx = $itemSet; + + // We Group by Requirement path + $rowData[] = htmlentities($gui->path_info[$rfx['req_id']], + ENT_QUOTES, $charset); + + $edit_link = "" . "{$img} "; + $title = htmlentities($rfx['req_doc_id'], ENT_QUOTES, $charset) . ":" . + htmlentities($rfx['name'], ENT_QUOTES, $charset); + + $matches = ''; + $rowData[] = $edit_link . $title . ' ' . $matches; + $rowData[] = ($designType == 'none' ? nl2br($rfx['scope']) : $rfx['scope']); + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, 'tl_table_req_search'); + + $table->setGroupByColumnName($labels['req_spec']); + $table->setSortByColumnName($labels['requirement']); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->allowMultiSort = false; + $table->toolbarRefreshButton = false; + $table->toolbarShowAllColumnsButton = false; + $table->storeTableState = false; + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + } + return $table; +} + +/** + */ +function cleanUpTarget(&$dbHandler, $target) +{ + $s = preg_replace("/ {2,}/", " ", $target); + $theSet = explode(' ', $s); + $targetSet = array(); + foreach ($theSet as $val) { + if (trim($val) != '') { + $targetSet[] = $dbHandler->prepare_string($val); + } + } + return $targetSet; } @@ -440,22 +478,22 @@ function cleanUpTarget(&$dbHandler,$target) /* create view latest_tcase_version_number AS SELECT NH_TC.id AS testcase_id,max(TCV.version) AS version -FROM nodes_hierarchy NH_TC -JOIN nodes_hierarchy NH_TCV ON NH_TCV.parent_id = NH_TC.id -JOIN tcversions TCV ON NH_TCV.id = TCV.id +FROM nodes_hierarchy NH_TC +JOIN nodes_hierarchy NH_TCV ON NH_TCV.parent_id = NH_TC.id +JOIN tcversions TCV ON NH_TCV.id = TCV.id group by testcase_id =========== -SELECT LVN.testcase_id, TCV.id,TCV.version -FROM latest_tcase_version_number LVN -JOIN nodes_hierarchy NH_TCV ON NH_TCV.parent_id = LVN.testcase_id +SELECT LVN.testcase_id, TCV.id,TCV.version +FROM latest_tcase_version_number LVN +JOIN nodes_hierarchy NH_TCV ON NH_TCV.parent_id = LVN.testcase_id JOIN tcversions TCV ON NH_TCV.id = TCV.id AND LVN.version = TCV.version WHERE 1=1 AND NH_TCV.parent_id IN (7945) AND ( 1=1 AND TCV.summary like '%three%' ) create view latest_rspec_revision AS SELECT parent_id AS req_spec_id,testproject_id, max(revision) AS revision FROM req_specs_revisions RSR -JOIN req_specs RS ON RS.id = RSR.parent_id +JOIN req_specs RS ON RS.id = RSR.parent_id group by parent_id,testproject_id CREATE VIEW latest_req_version AS @@ -469,6 +507,6 @@ function cleanUpTarget(&$dbHandler,$target) JOIN latest_req_version LV on LV.req_id = NHRQV.parent_id JOIN req_versions RQV on NHRQV.id = RQV.id AND RQV.version = LV.version JOIN nodes_hierarchy NHRQ on NHRQ.id = LV.req_id -JOIN requirements RQ on RQ.id = LV.req_id +JOIN requirements RQ on RQ.id = LV.req_id */ diff --git a/lib/search/searchCommands.class.php b/lib/search/searchCommands.class.php index f3a156b4dd..313e772758 100644 --- a/lib/search/searchCommands.class.php +++ b/lib/search/searchCommands.class.php @@ -1,1025 +1,1054 @@ -db = $db; - $this->tcaseMgr = new testcase($this->db); - $this->tprojectMgr = new testproject($this->db); - $this->cfieldMgr = &$this->tprojectMgr->cfield_mgr; - $this->reqSpecMgr = new requirement_spec_mgr($this->db); - - $this->tcaseCfg = config_get('testcase_cfg'); - $dbt = strtolower($this->db->db->databaseType); - - $this->likeOp = 'LIKE'; - if(stristr($dbt, 'postgres') !== FALSE) - { - $this->likeOp = 'I' . $this->likeOp; - } - } - - - /** - * - */ - function isReqFeatureEnabled($tproject_id) - { - $info = $this->tprojectMgr->get_by_id($tproject_id); - return isset($info['opt']->requirementsEnabled) - ? $info['opt']->requirementsEnabled : 0; - } - - - /** - * - */ - function getTestCaseIDSet($tproject_id) - { - $items = array(); - $this->tprojectMgr->get_all_testcases_id($tproject_id,$items); - return $items; - } - - /** - * - */ - function getTestSuiteIDSet($tproject_id) { - $nt2ex = array('testcase' => 'exclude_me', - 'testplan' => 'exclude_me', - 'requirement_spec'=> 'exclude_me', - 'requirement'=> 'exclude_me'); - - $nt2exchi = array('testcase' => 'exclude_my_children', - 'requirement_spec'=> 'exclude_my_children'); - - $opt = array('recursive' => 0, 'output' => 'id'); - $filters = array('exclude_node_types' => $nt2ex, - 'exclude_children_of' => $nt2exchi); - - $items = $this->tprojectMgr->tree_manager->get_subtree($tproject_id,$filters,$opt); - - return $items; - } - - /** - * - */ - function getReqSpecIDSet($tproject_id) - { - $items = array(); - - $opt = array('output' => 'id'); - $items = $this->reqSpecMgr->get_all_id_in_testproject($tproject_id); - return $items; - } - - /** - * - */ - function getReqIDSet($tproject_id) - { - $items = array(); - $items = $this->tprojectMgr->get_all_requirement_ids($tproject_id); - return $items; - } - - - /** - * - */ - function getArgs() - { - return $this->args; - } - - /** - * - */ - function getGui() - { - return $this->gui; - } - - /** - * - */ - function getFilters() - { - return $this->filters; - } - - /** - * - */ - function getTables() - { - return $this->tables; - } - - /** - * - */ - function getViews() - { - return $this->views; - } - - - - /** - * - */ - function initEnv() - { - $this->initArgs(); - $this->initGui(); - $this->initSearch(); - } - - - /** - * - */ - function initSchema() { - $this->tables = tlObjectWithDB::getDBTables( - array('cfield_design_values','nodes_hierarchy', - 'requirements','tcsteps','testcase_keywords', - 'req_specs_revisions','req_versions', - 'testsuites','tcversions','users', - 'object_keywords')); - - $this->views = tlObjectWithDB::getDBViews( - array('latest_rspec_revision','latest_req_version', - 'latest_tcase_version_number')); - } - - - /** - * - */ - function initArgs() - { - $cb = array("rq_scope" => array(tlInputParameter::CB_BOOL), - "rq_title" => array(tlInputParameter::CB_BOOL), - "rq_doc_id" => array(tlInputParameter::CB_BOOL), - "rs_scope" => array(tlInputParameter::CB_BOOL), - "rs_title" => array(tlInputParameter::CB_BOOL), - "tc_summary" => array(tlInputParameter::CB_BOOL), - "tc_title" => array(tlInputParameter::CB_BOOL), - "tc_steps" => array(tlInputParameter::CB_BOOL), - "tc_expected_results" => array(tlInputParameter::CB_BOOL), - "tc_preconditions" => array(tlInputParameter::CB_BOOL), - "tc_id" => array(tlInputParameter::CB_BOOL), - "ts_summary" => array(tlInputParameter::CB_BOOL), - "ts_title" => array(tlInputParameter::CB_BOOL)); - - - $strIn = array("tcWKFStatus" => array(tlInputParameter::STRING_N,0,1), - "reqStatus" => array(tlInputParameter::STRING_N,0,1), - "reqType" => array(tlInputParameter::STRING_N), - "created_by" => array(tlInputParameter::STRING_N,0,50), - "edited_by" => array(tlInputParameter::STRING_N,0,50), - "creation_date_from" => array(tlInputParameter::STRING_N), - "creation_date_to" => array(tlInputParameter::STRING_N), - "modification_date_from" => array(tlInputParameter::STRING_N), - "modification_date_to" => array(tlInputParameter::STRING_N), - "and_or" => array(tlInputParameter::STRING_N,2,3) ); - - $numIn = array("keyword_id" => array(tlInputParameter::INT_N), - "custom_field_id" => array(tlInputParameter::INT_N)); - - $iParams = array("target" => array(tlInputParameter::STRING_N), - "doAction" => array(tlInputParameter::STRING_N,0,10), - "custom_field_value" => array(tlInputParameter::STRING_N,0,20), - "tproject_id" => array(tlInputParameter::INT_N)); - - $this->args = new stdClass(); - $args = &$this->args; - - $iParams = $iParams + $cb + $strIn + $numIn; - - R_PARAMS($iParams,$this->args); - - // At least one checkbox need to be checked - $args->oneCheck = false; - foreach($cb as $key => $vx) - { - $args->oneCheck = $args->$key; - if($args->oneCheck) - { - break; - } - } - - $args->oneValueOK = false; - foreach($numIn as $key => $vx) - { - $args->oneValueOK = (intval($args->$key) > 0); - if($args->oneValueOK) - { - break; - } - } - - if($args->oneValueOK == false) - { - foreach($strIn as $key => $vx) - { - $args->oneValueOK = (trim($args->$key) != ''); - if($args->oneValueOK) - { - break; - } - } - } - - // try to sanitize target against XSS - // remove all blanks - // remove some html entities - // remove () - // Need to give a look - //$tt = array('<','>','(',')'); - //$args->target = str_replace($tt,'',$args->target); - $ts = preg_replace("/ {2,}/", " ", $args->target); - $args->target = trim($ts); - - $args->userID = intval(isset($_SESSION['userID']) ? $_SESSION['userID'] : 0); - - if(is_null($args->tproject_id) || intval($args->tproject_id) <= 0) - { - $args->tprojectID = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; - } - else - { - $args->tprojectID = intval($args->tproject_id); - $info = $this->tprojectMgr->get_by_id($args->tprojectID); - $args->tprojectName = $info['name']; - } - - if($args->tprojectID <= 0) - { - throw new Exception("Error Processing Request - Invalid Test project id " . __FILE__); - } - - // convert according local - - // convert "creation date from" to iso format for database usage - $k2w = array('creation_date_from' => '','creation_date_to' => " 23:59:59", - 'modification_date_from' => '', 'modification_date_to' => " 23:59:59"); - - $k2f = array('creation_date_from' => ' creation_ts >= ', - 'creation_date_to' => 'creation_ts <= ', - 'modification_date_from' => ' modification_ts >= ', - 'modification_date_to' => ' modification_ts <= '); - - - $dateFormat = config_get('date_format'); - $filter['dates4tc'] = null; - $filter['dates4rq'] = null; - foreach($k2w as $key => $value) - { - $lk = 'loc_' . $key; - $args->$lk = ''; - - if (isset($args->$key) && $args->$key != '') - { - $da = split_localized_date($args->$key, $dateFormat); - if ($da != null) - { - $args->$key = $da['year'] . "-" . $da['month'] . "-" . $da['day'] . $value; // set date in iso format - $this->filters['dates4tc'][$key] = " AND TCV.{$k2f[$key]} '{$args->$key}' "; - $this->filters['dates4rq'][$key] = " AND RQV.{$k2f[$key]} '{$args->$key}' "; - - $args->$lk = implode("/",$da); - } - } - } - - // $args->and_or = isset($_REQUEST['and_or']) ? $_REQUEST['and_or'] : 'or'; - $args->user = $_SESSION['currentUser']; - - $args->canAccessTestSpec = $args->user->hasRight($this->db,'mgt_view_tc',$args->tproject_id); - - $args->canAccessReqSpec = $args->user->hasRight($this->db,'mgt_view_req',$args->tproject_id); - - } - - - /** - * - * - */ - function initGui() - { - $this->gui = new stdClass(); - - $this->gui->caller = 'search.php'; - - $this->gui->tcasePrefix = $this->tprojectMgr->getTestCasePrefix($this->args->tprojectID); - $this->gui->tcasePrefix .= $this->tcaseCfg->glue_character; - - - - $this->gui->reqType = $this->args->reqType; - $this->gui->reqStatus = $this->args->reqStatus; - $this->gui->tcWKFStatus = $this->args->tcWKFStatus; - - $this->gui->pageTitle = lang_get('multiple_entities_search'); - $this->gui->warning_msg = ''; - $this->gui->path_info = null; - $this->gui->resultSet = null; - $this->gui->tableSet = null; - $this->gui->bodyOnLoad = null; - $this->gui->bodyOnUnload = null; - $this->gui->refresh_tree = false; - $this->gui->hilite_testcase_name = false; - $this->gui->show_match_count = false; - $this->gui->row_qty = 0; - $this->gui->doSearch = ($this->args->doAction == 'doSearch'); - $this->gui->tproject_id = intval($this->args->tprojectID); - - // ---------------------------------------------------- - $this->gui->mainCaption = lang_get('testproject') . " " . $this->args->tprojectName; - - $this->gui->search_important_notice = sprintf(lang_get('search_important_notice'),$this->args->tprojectName); - - // need to set values that where used on latest search (if any was done) - // $this->gui->importance = config_get('testcase_importance_default'); - - $this->gui->tc_steps = $this->args->tc_steps; - $this->gui->tc_title = $this->args->tc_title; - $this->gui->tc_summary = $this->args->tc_summary; - $this->gui->tc_preconditions = $this->args->tc_preconditions; - $this->gui->tc_expected_results = $this->args->tc_expected_results; - $this->gui->tc_id = $this->args->tc_id; - - $this->gui->ts_title = $this->args->ts_title; - $this->gui->ts_summary = $this->args->ts_summary; - - $this->gui->rs_title = $this->args->rs_title; - $this->gui->rs_scope = $this->args->rs_scope; - - $this->gui->rq_title = $this->args->rq_title; - $this->gui->rq_scope = $this->args->rq_scope; - $this->gui->rq_doc_id = $this->args->rq_doc_id; - - - $this->gui->custom_field_id = $this->args->custom_field_id; - $this->gui->custom_field_value = $this->args->custom_field_value; - $this->gui->creation_date_from = $this->args->loc_creation_date_from; - $this->gui->creation_date_to = $this->args->loc_creation_date_to; - $this->gui->modification_date_from = $this->args->loc_modification_date_from; - $this->gui->modification_date_to = $this->args->loc_modification_date_to; - - $this->gui->created_by = trim($this->args->created_by); - $this->gui->edited_by = trim($this->args->edited_by); - $this->gui->keyword_id = intval($this->args->keyword_id); - - - $this->gui->forceSearch = false; - - $this->gui->and_selected = $this->gui->or_selected = ''; - switch($this->args->and_or) - { - case 'and': - $this->gui->and_selected = ' selected '; - break; - - case 'or': - default: - $this->gui->or_selected = ' selected '; - break; - } - - $reqCfg = config_get('req_cfg'); - $this->gui->reqStatusDomain = init_labels($reqCfg->status_labels); - - $this->gui->reqTypes = array_flip(init_labels($reqCfg->type_labels)); - foreach ($this->gui->reqTypes as $key => $value) - { - $this->gui->reqTypes[$key] = 'RQ' . $value; - } - $this->gui->reqTypes = array_flip($this->gui->reqTypes); - $this->gui->tcWKFStatusDomain = $this->getTestCaseWKFStatusDomain(); - } - - - /** - * - */ - function initSearch() - { - - $this->gui->reqEnabled = $this->isReqFeatureEnabled($this->args->tproject_id); - - - $this->gui->cf = null; - $this->gui->design_cf_req = null; - - $this->gui->design_cf_tc = $this->cfieldMgr->get_linked_cfields_at_design( - $this->args->tproject_id,cfield_mgr::ENABLED,null,'testcase'); - - if($this->gui->reqEnabled) - { - $this->gui->design_cf_req = $this->cfieldMgr->get_linked_cfields_at_design( - $this->args->tproject_id, - cfield_mgr::ENABLED,null,'requirement'); - } - - if(!is_null($this->gui->design_cf_tc)) - { - $this->gui->cf = $this->gui->design_cf_tc; - } - - if(!is_null($this->gui->design_cf_req)) - { - if(is_null($this->gui->cf)) - { - $this->gui->cf = $this->gui->design_cf_req; - } - else - { - $this->gui->cf += $this->gui->design_cf_req; - } - } - - $this->gui->filter_by['custom_fields'] = !is_null($this->gui->cf) && count($this->gui->cf) > 0; - - $this->gui->keywords = $this->tprojectMgr->getKeywordSet($this->args->tproject_id); - $this->gui->filter_by['keyword'] = !is_null($this->gui->keywords); - - $reqSpecSet = $this->tprojectMgr->genComboReqSpec($this->args->tprojectID); - $this->gui->filter_by['requirement_doc_id'] = !is_null($reqSpecSet); - $reqSpecSet = null; - - $this->gui->status = isset($this->args->status) ? intval($this->args->status) : ''; - $this->gui->target = $this->args->target; - } - - /** - * - */ - function searchReqSpec($targetSet,$canUseTarget) { - // shortcuts - $args = &$this->args; - $db = &$this->db; - - $cfg = config_get('UDFStripHTMLTags'); - $udf = $cfg ? 'UDFStripHTMLTags' : ''; - - $mapRSpec = null; - $sql = "SELECT RSRV.name, RSRV.scope, LRSR.req_spec_id, RSRV.id," . - "LRSR.revision " . - "FROM {$this->views['latest_rspec_revision']} LRSR " . - "JOIN {$this->tables['req_specs_revisions']} RSRV " . - "ON RSRV.parent_id = LRSR.req_spec_id " . - "AND RSRV.revision = LRSR.revision " . - "WHERE LRSR.testproject_id = " . $args->tproject_id; - - $doFilter = true; - - - $filterRS = null; - if( $canUseTarget ) { - $doFilter = true; - $filterRS['tricky'] = " 1=0 "; - - $filterRS['scope'] = ' OR ( '; - $filterRS['scope'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - foreach($targetSet as $target) { - $filterRS['scope'] .= $args->and_or . " $udf(RSRV.scope) $this->likeOp '%{$target}%' "; - } - $filterRS['scope'] .= ')'; - - $filterRS['name'] = ' OR ( '; - $filterRS['name'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - foreach($targetSet as $trgt) { - $target = trim($trgt); - $filterRS['name'] .= $args->and_or . " RSRV.name $this->likeOp '%{$target}%' "; - } - $filterRS['name'] .= ')'; - } - - $otherFRS = ''; - if(!is_null($filterRS)) { - $otherFRS = " AND (" . implode("",$filterRS) . ")"; - } - - $sql .= $otherFRS; - if($doFilter) { - $mapRSpec = $db->fetchRowsIntoMap($sql,'req_spec_id'); - } - return $mapRSpec; - } - - /** - * - */ - function searchReq($targetSet,$canUseTarget,$req_cf_id) { - // shortcuts - $args = &$this->args; - $gui = &$this->gui; - $db = &$this->db; - $tables = &$this->tables; - $views = &$this->views; - - $cfg = config_get('UDFStripHTMLTags'); - $udf = $cfg ? 'UDFStripHTMLTags' : ''; - - - $reqSet = $this->getReqIDSet($args->tproject_id); - - $noItems = is_null($reqSet) || count($reqSet) == 0; - $bye = $noItems || (!$canUseTarget && $req_cf_id <= 0); - if( $bye ) - { - return null; - } - - // OK go ahead - $doSql = true; - $doFilter = false; - $fi = null; - $from['by_custom_field'] = ''; - - - if($req_cf_id >0) - { - $cf_def = $gui->design_cf_rq[$req_cf_id]; - - $from['by_custom_field']= " JOIN {$tables['cfield_design_values']} CFD " . - " ON CFD.node_id=RQV.id "; - $fi['by_custom_field'] = " AND CFD.field_id=" . intval($req_cf_id); - - switch($gui->cf_types[$cf_def['type']]) - { - case 'date': - $args->custom_field_value = $this->cfieldMgr->cfdate2mktime($args->custom_field_value); - - $fi['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; - break; - - default: - $args->custom_field_value = $db->prepare_string($args->custom_field_value); - $fi['by_custom_field'] .= " AND CFD.value $this->likeOp '%{$args->custom_field_value}%' "; - break; - } - } - - $args->created_by = trim($args->created_by); - $from['users'] = ''; - if($args->created_by != '' ) - { - $doFilter = true; - $from['users'] .= " JOIN {$tables['users']} RQAUTHOR ON RQAUTHOR.id = RQV.author_id "; - $fi['author'] = " AND ( RQAUTHOR.login $this->likeOp '%{$args->created_by}%' OR " . - " RQAUTHOR.first $this->likeOp '%{$args->created_by}%' OR " . - " RQAUTHOR.last $this->likeOp '%{$args->created_by}%') "; - } - - $args->edited_by = trim($args->edited_by); - if( $args->edited_by != '' ) - { - $doFilter = true; - $from['users'] .= " JOIN {$tables['users']} UPDATER ON UPDATER.id = RQV.modifier_id "; - $fi['modifier'] = " AND ( UPDATER.login $this->likeOp '%{$args->edited_by}%' OR " . - " UPDATER.first $this->likeOp '%{$args->edited_by}%' OR " . - " UPDATER.last $this->likeOp '%{$args->edited_by}%') "; - } - - if( $doSql ) { - $doFilter = true; - - $sql = " /* " . __LINE__ . " */ " . - " SELECT RQ.id AS req_id, RQV.scope,RQ.req_doc_id,NHRQ.name " . - " FROM {$tables['nodes_hierarchy']} NHRQV " . - " JOIN {$views['latest_req_version']} LV on LV.req_id = NHRQV.parent_id " . - " JOIN {$tables['req_versions']} RQV on NHRQV.id = RQV.id AND RQV.version = LV.version " . - " JOIN {$tables['nodes_hierarchy']} NHRQ on NHRQ.id = LV.req_id " . - " JOIN {$tables['requirements']} RQ on RQ.id = LV.req_id " . - $from['users'] . $from['by_custom_field'] . - " WHERE RQ.id IN(" . implode(',', $reqSet) . ")"; - - if(!is_null($args->reqType)) { - $doFilter = true; - $sql .= " AND RQV.type ='" . $db->prepare_string($args->reqType) . "' "; - } - - if($args->reqStatus != '') { - $doFilter = true; - $sql .= " AND RQV.status='" . $db->prepare_string($args->reqStatus) . "' "; - } - - $filterRQ = null; - if( $canUseTarget ) { - $doFilter = true; - $filterRQ['tricky'] = " 1=0 "; - - if( $args->rq_scope ) { - $filterRQ['scope'] = ' OR ( '; - $filterRQ['scope'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - foreach($targetSet as $target) { - $filterRQ['scope'] .= $args->and_or . " $udf(RQV.scope) $this->likeOp '%{$target}%' "; - } - $filterRQ['scope'] .= ')'; - } - - if( $args->rq_title ) - { - $filterRQ['name'] = ' OR ( '; - $filterRQ['name'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - - foreach($targetSet as $target) - { - $filterRQ['name'] .= $args->and_or . " NHRQ.name $this->likeOp '%{$target}%' "; - } - $filterRQ['name'] .= ')'; - } - - if( $args->rq_doc_id ) - { - $filterRQ['req_doc_id'] = ' OR ( '; - $filterRQ['req_doc_id'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - foreach($targetSet as $target) - { - $filterRQ['req_doc_id'] .= $args->and_or . " RQ.req_doc_id $this->likeOp '%{$target}%' "; - } - $filterRQ['req_doc_id'] .= ')'; - } - } - - $otherFRQ = ''; - if(!is_null($filterRQ)) - { - $otherFRQ = " AND (" . implode("",$filterRQ) . ")"; - } - - $xfil = ''; - if(!is_null($fi)) - { - $xfil = implode("",$fi); - } - - $sql .= $xfil . $otherFRQ; - if( $doFilter ) - { - //DEBUGecho __FUNCTION__ . ' SQL Line:' . __LINE__ . $sql .'
    '; - $mapRQ = $db->fetchRowsIntoMap($sql,'req_id'); - } - - return $mapRQ; - } - } - - - /** - * - */ - function searchTestSuites($targetSet,$canUseTarget) { - - // shortcuts - $args = &$this->args; - $gui = &$this->gui; - $db = &$this->db; - $tables = &$this->tables; - $views = &$this->views; - $cfg = config_get('UDFStripHTMLTags'); - $udf = $cfg ? 'UDFStripHTMLTags' : ''; - - $mapTS = null; - $tsuiteSet = $this->getTestSuiteIDSet($args->tproject_id); - if(is_null($tsuiteSet) || count($tsuiteSet) == 0) - { - return null; - } - - $filterSpecial = null; - $filterSpecial['tricky'] = " 1=0 "; - - if( ($doIt = $args->ts_summary && $canUseTarget) ) { - $filterSpecial['ts_summary'] = ' OR ( '; - $filterSpecial['ts_summary'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - - foreach($targetSet as $target) { - $filterSpecial['ts_summary'] .= $args->and_or . - " $udf(TS.details) $this->likeOp '%{$target}%' "; - } - $filterSpecial['ts_summary'] .= ')'; - } - - if( ($doIt = $args->ts_title && $canUseTarget) ) { - $filterSpecial['ts_title'] = ' OR ( '; - $filterSpecial['ts_title'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - - foreach($targetSet as $target) { - $filterSpecial['ts_title'] .= $args->and_or . " NH_TS.name $this->likeOp '%{$target}%' "; - } - $filterSpecial['ts_title'] .= ')'; - } - - $otherFilters = ''; - if(!is_null($filterSpecial)) - { - $otherFilters = " AND (" . implode("",$filterSpecial) . ")"; - } - - if($args->ts_title || $args->ts_summary) - { - $fromTS['by_keyword_id']= ''; - $filterTS['by_keyword_id']=''; - if($args->keyword_id) - { - $fromTS['by_keyword_id'] = " JOIN {$tables['object_keywords']} KW ON KW.fk_id = NH_TS.id "; - $filterTS['by_keyword_id'] = " AND KW.keyword_id = " . $args->keyword_id; - } - - $sqlFields = " SELECT NH_TS.name, TS.id, TS.details " . - " FROM {$tables['nodes_hierarchy']} NH_TS " . - " JOIN {$tables['testsuites']} TS ON TS.id = NH_TS.id " . - $fromTS['by_keyword_id'] . - " WHERE TS.id IN (" . implode(',', $tsuiteSet) . ")"; - - $sql = $sqlFields . $filterTS['by_keyword_id'] . $otherFilters; - $mapTS = $db->fetchRowsIntoMap($sql,'id'); - - //DEBUGecho 'DEBUG===' . $sql; - } - - return $mapTS; - } - - - /** - * - */ - function searchTestCases($tcaseSet,$targetSet,$canUseTarget,$tc_cf_id) { - // shortcuts - $args = &$this->args; - $gui = &$this->gui; - $db = &$this->db; - $tables = &$this->tables; - $views = &$this->views; - $cfg = config_get('UDFStripHTMLTags'); - $udf = $cfg ? 'UDFStripHTMLTags' : ''; - - - $from['tc_steps'] = ""; - $from['users'] = ""; - $from['by_keyword_id'] = ""; - $from['by_custom_field'] = ""; - - $filter = null; - $filterSpecial = null; - - - if( is_null($tcaseSet) || count($tcaseSet) == 0) - { - return null; - } - - - $filter['by_tc_internal_id'] = " AND NH_TCV.parent_id IN (" . - implode(",",$tcaseSet) . ") "; - - - $filterSpecial['tricky'] = " 1=0 "; - - if($args->tc_id) - { - $filterSpecial['by_tc_id'] = ''; - - // Remember that test case id is a number! - foreach($targetSet as $tgx) - { - $target = trim($tgx); - if( is_numeric($target) ) - { - $filterSpecial['by_tc_id'] .= $args->and_or . - " TCV.tc_external_id = $target "; - } - } - } - - $doFilter = false; - $doFilter = ($args->tc_summary || $args->tc_title || $args->tc_id); - - if($tc_cf_id > 0) - { - $cf_def = $gui->design_cf_tc[$tc_cf_id]; - - $from['by_custom_field']= " JOIN {$tables['cfield_design_values']} CFD " . - " ON CFD.node_id=NH_TCV.id "; - $filter['by_custom_field'] = " AND CFD.field_id=" . intval($tc_cf_id); - - switch($gui->cf_types[$cf_def['type']]) - { - case 'date': - $args->custom_field_value = $cfieldMgr->cfdate2mktime($args->custom_field_value); - - $filter['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; - break; - - default: - $args->custom_field_value = $db->prepare_string($args->custom_field_value); - $filter['by_custom_field'] .= " AND CFD.value $this->likeOp '%{$args->custom_field_value}%' "; - break; - } - } - - if($args->tc_steps || $args->tc_expected_results) - { - $doFilter = true; - $from['tc_steps'] = " LEFT OUTER JOIN {$tables['nodes_hierarchy']} " . - " NH_TCSTEPS ON NH_TCSTEPS.parent_id = NH_TCV.id " . - " LEFT OUTER JOIN {$tables['tcsteps']} TCSTEPS " . - " ON NH_TCSTEPS.id = TCSTEPS.id "; - } - - if($args->tc_steps && $canUseTarget) { - $filterSpecial['by_steps'] = ' OR ( '; - $filterSpecial['by_steps'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - - foreach($targetSet as $target) { - $filterSpecial['by_steps'] .= $args->and_or . - " $udf(TCSTEPS.actions) $this->likeOp '%{$target}%' "; - } - $filterSpecial['by_steps'] .= ')'; - } - - if($args->tc_expected_results && $canUseTarget) { - $filterSpecial['by_expected_results'] = ' OR ( '; - $filterSpecial['by_expected_results'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - - foreach($targetSet as $target) { - $filterSpecial['by_expected_results'] .= $args->and_or . - " $udf(TCSTEPS.expected_results) $this->likeOp '%{$target}%' "; - } - $filterSpecial['by_expected_results'] .= ')'; - } - - if($canUseTarget) - { - $k2w = array('name' => 'NH_TC', 'summary' => 'TCV', 'preconditions' => 'TCV'); - $i2s = array('name' => 'tc_title', 'summary' => 'tc_summary', - 'preconditions' => 'tc_preconditions'); - foreach($k2w as $kf => $alias) - { - $in = $i2s[$kf]; - if($args->$in) - { - $doFilter = true; - - $filterSpecial[$kf] = ' OR ( '; - $filterSpecial[$kf] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; - - foreach($targetSet as $target) { - $filterSpecial[$kf] .= " {$args->and_or} "; - $xx = "{$alias}.{$kf}"; - switch($kf) { - case 'summary': - case 'preconditions': - $xx = " $udf(" . $xx . ") "; - break; - } - $filterSpecial[$kf] .= "{$xx} {$this->likeOp} '%{$target}%' "; - } - $filterSpecial[$kf] .= ' )'; - } - } - } - - - $otherFilters = ''; - if(!is_null($filterSpecial) && count($filterSpecial) > 1) - { - $otherFilters = " AND (/* filterSpecial */ " . - implode("",$filterSpecial) . ")"; - } - - // Search on latest test case version using view - $sqlFields = " SELECT LVN.testcase_id, NH_TC.name, TCV.id AS tcversion_id," . - " TCV.summary, TCV.version, TCV.tc_external_id "; - - if($doFilter) - { - if($args->tcWKFStatus > 0) - { - $tg = intval($args->tcWKFStatus); - $filter['by_tcWKFStatus'] = " AND TCV.status = {$tg} "; - } - - if($args->keyword_id) - { - $from['by_keyword_id'] = " JOIN {$tables['testcase_keywords']} KW ON KW.testcase_id = NH_TC.id "; - $filter['by_keyword_id'] = " AND KW.keyword_id = " . $args->keyword_id; - } - - $created_by_on_tc = $args->created_by = trim($args->created_by); - $from['users'] = ''; - if( $created_by_on_tc != '' ) - { - $doFilter = true; - $from['users'] .= " JOIN {$tables['users']} AUTHOR ON AUTHOR.id = TCV.author_id "; - $filter['author'] = " AND ( AUTHOR.login $this->likeOp '%{$args->created_by}%' OR " . - " AUTHOR.first $this->likeOp '%{$args->created_by}%' OR " . - " AUTHOR.last $this->likeOp '%{$args->created_by}%') "; - } - - $edited_by_on_tc = $args->edited_by = trim($args->edited_by); - if( $edited_by_on_tc != '' ) - { - $doFilter = true; - $from['users'] .= " JOIN {$tables['users']} UPDATER ON UPDATER.id = TCV.updater_id "; - $filter['modifier'] = " AND ( UPDATER.login $this->likeOp '%{$args->edited_by}%' OR " . - " UPDATER.first $this->likeOp '%{$args->edited_by}%' OR " . - " UPDATER.last $this->likeOp '%{$args->edited_by}%') "; - } - } - - - // search fails if test case has 0 steps - Added LEFT OUTER - $sqlPart2 = " FROM {$views['latest_tcase_version_number']} LVN " . - " JOIN {$tables['nodes_hierarchy']} NH_TC ON NH_TC.id = LVN.testcase_id " . - " JOIN {$tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = NH_TC.id " . - " JOIN {$tables['tcversions']} TCV ON NH_TCV.id = TCV.id " . - " AND TCV.version = LVN.version " . - $from['tc_steps'] . $from['users'] . $from['by_keyword_id'] . - $from['by_custom_field'] . - " WHERE LVN.testcase_id IN (" . implode(',', $tcaseSet) . ")"; - - - $mapTC = NULL; - if($doFilter) - { - $mixedFilter = $this->getFilters(); - if ($filter) - { - $sqlPart2 .= implode("",$filter); - } - - if ($mixedFilter['dates4tc']) - { - $sqlPart2 .= implode("",$mixedFilter['dates4tc']); - } - - $sql = $sqlFields . $sqlPart2 . $otherFilters; - - //DEBUGecho __FUNCTION__ . '-' . __LINE__ . '-' . $sql .'
    '; - $mapTC = $db->fetchRowsIntoMap($sql,'testcase_id'); - } - - return $mapTC; - } - - /** - * - */ - static function getTestCaseWKFStatusDomain() { - $cv = array_flip(config_get('testCaseStatus')); - foreach($cv as $cc => $vv) { - $lbl = lang_get('testCaseStatus_' . $vv); - $cv[$cc] = lang_get('testCaseStatus_' . $vv); - } - return $cv; - } - - -} // end class +db = $db; + $this->tcaseMgr = new testcase($this->db); + $this->tprojectMgr = new testproject($this->db); + $this->cfieldMgr = &$this->tprojectMgr->cfield_mgr; + $this->reqSpecMgr = new requirement_spec_mgr($this->db); + + $this->tcaseCfg = config_get('testcase_cfg'); + $dbt = strtolower($this->db->db->databaseType); + + $this->likeOp = 'LIKE'; + if (stristr($dbt, 'postgres') !== false) { + $this->likeOp = 'I' . $this->likeOp; + } + } + + /** + */ + private function isReqFeatureEnabled($tproject_id) + { + $info = $this->tprojectMgr->get_by_id($tproject_id); + return isset($info['opt']->requirementsEnabled) ? $info['opt']->requirementsEnabled : 0; + } + + /** + */ + public function getTestCaseIDSet($tproject_id) + { + $items = array(); + $this->tprojectMgr->get_all_testcases_id($tproject_id, $items); + return $items; + } + + /** + */ + private function getTestSuiteIDSet($tproject_id) + { + $nt2ex = array( + 'testcase' => 'exclude_me', + 'testplan' => 'exclude_me', + 'requirement_spec' => 'exclude_me', + 'requirement' => 'exclude_me' + ); + + $nt2exchi = array( + 'testcase' => 'exclude_my_children', + 'requirement_spec' => 'exclude_my_children' + ); + + $opt = array( + 'recursive' => 0, + 'output' => 'id' + ); + $filters = array( + 'exclude_node_types' => $nt2ex, + 'exclude_children_of' => $nt2exchi + ); + + return $this->tprojectMgr->tree_manager->get_subtree($tproject_id, + $filters, $opt); + } + + /** + */ + private function getReqSpecIDSet($tproject_id) + { + return $this->reqSpecMgr->get_all_id_in_testproject($tproject_id); + } + + /** + */ + private function getReqIDSet($tproject_id) + { + return $this->tprojectMgr->get_all_requirement_ids($tproject_id); + } + + /** + */ + public function getArgs() + { + return $this->args; + } + + /** + */ + public function getGui() + { + return $this->gui; + } + + /** + */ + private function getFilters() + { + return $this->filters; + } + + /** + */ + public function getTables() + { + return $this->tables; + } + + /** + */ + public function getViews() + { + return $this->views; + } + + /** + */ + public function initEnv() + { + $this->initArgs(); + $this->initGui(); + $this->initSearch(); + } + + /** + */ + public function initSchema() + { + $this->tables = tlObjectWithDB::getDBTables( + array( + 'cfield_design_values', + 'nodes_hierarchy', + 'requirements', + 'tcsteps', + 'testcase_keywords', + 'req_specs_revisions', + 'req_versions', + 'testsuites', + 'tcversions', + 'users', + 'object_keywords' + )); + + $this->views = tlObjectWithDB::getDBViews( + array( + 'latest_rspec_revision', + 'latest_req_version', + 'latest_tcase_version_number' + )); + } + + /** + */ + private function initArgs() + { + $cb = array( + "rq_scope" => array( + tlInputParameter::CB_BOOL + ), + "rq_title" => array( + tlInputParameter::CB_BOOL + ), + "rq_doc_id" => array( + tlInputParameter::CB_BOOL + ), + "rs_scope" => array( + tlInputParameter::CB_BOOL + ), + "rs_title" => array( + tlInputParameter::CB_BOOL + ), + "tc_summary" => array( + tlInputParameter::CB_BOOL + ), + "tc_title" => array( + tlInputParameter::CB_BOOL + ), + "tc_steps" => array( + tlInputParameter::CB_BOOL + ), + "tc_expected_results" => array( + tlInputParameter::CB_BOOL + ), + "tc_preconditions" => array( + tlInputParameter::CB_BOOL + ), + "tc_id" => array( + tlInputParameter::CB_BOOL + ), + "ts_summary" => array( + tlInputParameter::CB_BOOL + ), + "ts_title" => array( + tlInputParameter::CB_BOOL + ) + ); + + $strIn = array( + "tcWKFStatus" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "reqStatus" => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + "reqType" => array( + tlInputParameter::STRING_N + ), + "created_by" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "edited_by" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "creation_date_from" => array( + tlInputParameter::STRING_N + ), + "creation_date_to" => array( + tlInputParameter::STRING_N + ), + "modification_date_from" => array( + tlInputParameter::STRING_N + ), + "modification_date_to" => array( + tlInputParameter::STRING_N + ), + "and_or" => array( + tlInputParameter::STRING_N, + 2, + 3 + ) + ); + + $numIn = array( + "keyword_id" => array( + tlInputParameter::INT_N + ), + "custom_field_id" => array( + tlInputParameter::INT_N + ) + ); + + $iParams = array( + "target" => array( + tlInputParameter::STRING_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 10 + ), + "custom_field_value" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ) + ); + + $this->args = new stdClass(); + $args = &$this->args; + + $iParams = $iParams + $cb + $strIn + $numIn; + + R_PARAMS($iParams, $this->args); + + // At least one checkbox need to be checked + $args->oneCheck = false; + foreach ($cb as $key => $vx) { + $args->oneCheck = $args->$key; + if ($args->oneCheck) { + break; + } + } + + $args->oneValueOK = false; + foreach ($numIn as $key => $vx) { + $args->oneValueOK = (intval($args->$key) > 0); + if ($args->oneValueOK) { + break; + } + } + + if (! $args->oneValueOK) { + foreach ($strIn as $key => $vx) { + $args->oneValueOK = (trim($args->$key) != ''); + if ($args->oneValueOK) { + break; + } + } + } + + // try to sanitize target against XSS + // remove all blanks + // remove some html entities + // remove () + // Need to give a look + // $tt = array('<','>','(',')'); + // $args->target = str_replace($tt,'',$args->target); + $ts = preg_replace("/ {2,}/", " ", $args->target); + $args->target = trim($ts); + + $args->userID = intval( + isset($_SESSION['userID']) ? $_SESSION['userID'] : 0); + + if (is_null($args->tproject_id) || intval($args->tproject_id) <= 0) { + $args->tprojectID = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; + } else { + $args->tprojectID = intval($args->tproject_id); + $info = $this->tprojectMgr->get_by_id($args->tprojectID); + $args->tprojectName = $info['name']; + } + + if ($args->tprojectID <= 0) { + throw new Exception( + "Error Processing Request - Invalid Test project id " . __FILE__); + } + + // convert according local + + // convert "creation date from" to iso format for database usage + $k2w = array( + 'creation_date_from' => '', + 'creation_date_to' => " 23:59:59", + 'modification_date_from' => '', + 'modification_date_to' => " 23:59:59" + ); + + $k2f = array( + 'creation_date_from' => ' creation_ts >= ', + 'creation_date_to' => 'creation_ts <= ', + 'modification_date_from' => ' modification_ts >= ', + 'modification_date_to' => ' modification_ts <= ' + ); + + $dateFormat = config_get('date_format'); + $filter['dates4tc'] = null; + $filter['dates4rq'] = null; + foreach ($k2w as $key => $value) { + $lk = 'loc_' . $key; + $args->$lk = ''; + + if (isset($args->$key) && $args->$key != '') { + $da = split_localized_date($args->$key, $dateFormat); + if ($da != null) { + $args->$key = $da['year'] . "-" . $da['month'] . "-" . + $da['day'] . $value; // set date in iso format + $this->filters['dates4tc'][$key] = " AND TCV.{$k2f[$key]} '{$args->$key}' "; + $this->filters['dates4rq'][$key] = " AND RQV.{$k2f[$key]} '{$args->$key}' "; + + $args->$lk = implode("/", $da); + } + } + } + + $args->user = $_SESSION['currentUser']; + $args->canAccessTestSpec = $args->user->hasRight($this->db, + 'mgt_view_tc', $args->tproject_id); + $args->canAccessReqSpec = $args->user->hasRight($this->db, + 'mgt_view_req', $args->tproject_id); + } + + /** + */ + private function initGui() + { + $this->gui = new stdClass(); + + $this->gui->caller = 'search.php'; + + $this->gui->tcasePrefix = $this->tprojectMgr->getTestCasePrefix( + $this->args->tprojectID); + $this->gui->tcasePrefix .= $this->tcaseCfg->glue_character; + + $this->gui->reqType = $this->args->reqType; + $this->gui->reqStatus = $this->args->reqStatus; + $this->gui->tcWKFStatus = $this->args->tcWKFStatus; + + $this->gui->pageTitle = lang_get('multiple_entities_search'); + $this->gui->warning_msg = ''; + $this->gui->path_info = null; + $this->gui->resultSet = null; + $this->gui->tableSet = null; + $this->gui->bodyOnLoad = null; + $this->gui->bodyOnUnload = null; + $this->gui->refresh_tree = false; + $this->gui->hilite_testcase_name = false; + $this->gui->show_match_count = false; + $this->gui->row_qty = 0; + $this->gui->doSearch = ($this->args->doAction == 'doSearch'); + $this->gui->tproject_id = intval($this->args->tprojectID); + + $this->gui->mainCaption = lang_get('testproject') . " " . + $this->args->tprojectName; + + $this->gui->search_important_notice = sprintf( + lang_get('search_important_notice'), $this->args->tprojectName); + + // need to set values that where used on latest search (if any was done) + // $this->gui->importance = config_get('testcase_importance_default'); + + $this->gui->tc_steps = $this->args->tc_steps; + $this->gui->tc_title = $this->args->tc_title; + $this->gui->tc_summary = $this->args->tc_summary; + $this->gui->tc_preconditions = $this->args->tc_preconditions; + $this->gui->tc_expected_results = $this->args->tc_expected_results; + $this->gui->tc_id = $this->args->tc_id; + + $this->gui->ts_title = $this->args->ts_title; + $this->gui->ts_summary = $this->args->ts_summary; + + $this->gui->rs_title = $this->args->rs_title; + $this->gui->rs_scope = $this->args->rs_scope; + + $this->gui->rq_title = $this->args->rq_title; + $this->gui->rq_scope = $this->args->rq_scope; + $this->gui->rq_doc_id = $this->args->rq_doc_id; + + $this->gui->custom_field_id = $this->args->custom_field_id; + $this->gui->custom_field_value = $this->args->custom_field_value; + $this->gui->creation_date_from = $this->args->loc_creation_date_from; + $this->gui->creation_date_to = $this->args->loc_creation_date_to; + $this->gui->modification_date_from = $this->args->loc_modification_date_from; + $this->gui->modification_date_to = $this->args->loc_modification_date_to; + + $this->gui->created_by = trim($this->args->created_by); + $this->gui->edited_by = trim($this->args->edited_by); + $this->gui->keyword_id = intval($this->args->keyword_id); + + $this->gui->forceSearch = false; + + $this->gui->and_selected = $this->gui->or_selected = ''; + switch ($this->args->and_or) { + case 'and': + $this->gui->and_selected = ' selected '; + break; + + case 'or': + default: + $this->gui->or_selected = ' selected '; + break; + } + + $reqCfg = config_get('req_cfg'); + $this->gui->reqStatusDomain = init_labels($reqCfg->status_labels); + + $this->gui->reqTypes = array_flip(init_labels($reqCfg->type_labels)); + foreach ($this->gui->reqTypes as $key => $value) { + $this->gui->reqTypes[$key] = 'RQ' . $value; + } + $this->gui->reqTypes = array_flip($this->gui->reqTypes); + $this->gui->tcWKFStatusDomain = $this->getTestCaseWKFStatusDomain(); + } + + /** + */ + private function initSearch() + { + $this->gui->reqEnabled = $this->isReqFeatureEnabled( + $this->args->tproject_id); + + $this->gui->cf = null; + $this->gui->design_cf_req = null; + + $this->gui->design_cf_tc = $this->cfieldMgr->get_linked_cfields_at_design( + $this->args->tproject_id, cfield_mgr::ENABLED, null, 'testcase'); + + if ($this->gui->reqEnabled) { + $this->gui->design_cf_req = $this->cfieldMgr->get_linked_cfields_at_design( + $this->args->tproject_id, cfield_mgr::ENABLED, null, + 'requirement'); + } + + if (! is_null($this->gui->design_cf_tc)) { + $this->gui->cf = $this->gui->design_cf_tc; + } + + if (! is_null($this->gui->design_cf_req)) { + if (is_null($this->gui->cf)) { + $this->gui->cf = $this->gui->design_cf_req; + } else { + $this->gui->cf += $this->gui->design_cf_req; + } + } + + $this->gui->filter_by['custom_fields'] = ! is_null($this->gui->cf) && + count($this->gui->cf) > 0; + + $this->gui->keywords = $this->tprojectMgr->getKeywordSet( + $this->args->tproject_id); + $this->gui->filter_by['keyword'] = ! is_null($this->gui->keywords); + + $reqSpecSet = $this->tprojectMgr->genComboReqSpec( + $this->args->tprojectID); + $this->gui->filter_by['requirement_doc_id'] = ! is_null($reqSpecSet); + $reqSpecSet = null; + + $this->gui->status = isset($this->args->status) ? intval( + $this->args->status) : ''; + $this->gui->target = $this->args->target; + } + + /** + */ + public function searchReqSpec($targetSet, $canUseTarget) + { + // shortcuts + $args = &$this->args; + $db = &$this->db; + + $cfg = config_get('UDFStripHTMLTags'); + $udf = $cfg ? 'UDFStripHTMLTags' : ''; + + $mapRSpec = null; + $sql = "SELECT RSRV.name, RSRV.scope, LRSR.req_spec_id, RSRV.id," . + "LRSR.revision " . + "FROM {$this->views['latest_rspec_revision']} LRSR " . + "JOIN {$this->tables['req_specs_revisions']} RSRV " . + "ON RSRV.parent_id = LRSR.req_spec_id " . + "AND RSRV.revision = LRSR.revision " . "WHERE LRSR.testproject_id = " . + $args->tproject_id; + + $doFilter = true; + + $filterRS = null; + if ($canUseTarget) { + $doFilter = true; + $filterRS['tricky'] = " 1=0 "; + + $filterRS['scope'] = ' OR ( '; + $filterRS['scope'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + foreach ($targetSet as $target) { + $filterRS['scope'] .= $args->and_or . + " $udf(RSRV.scope) $this->likeOp '%{$target}%' "; + } + $filterRS['scope'] .= ')'; + + $filterRS['name'] = ' OR ( '; + $filterRS['name'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + foreach ($targetSet as $trgt) { + $target = trim($trgt); + $filterRS['name'] .= $args->and_or . + " RSRV.name $this->likeOp '%{$target}%' "; + } + $filterRS['name'] .= ')'; + } + + $otherFRS = ''; + if (! is_null($filterRS)) { + $otherFRS = " AND (" . implode("", $filterRS) . ")"; + } + + $sql .= $otherFRS; + if ($doFilter) { + $mapRSpec = $db->fetchRowsIntoMap($sql, 'req_spec_id'); + } + return $mapRSpec; + } + + /** + */ + public function searchReq($targetSet, $canUseTarget, $req_cf_id) + { + // shortcuts + $args = &$this->args; + $gui = &$this->gui; + $db = &$this->db; + $tables = &$this->tables; + $views = &$this->views; + + $cfg = config_get('UDFStripHTMLTags'); + $udf = $cfg ? 'UDFStripHTMLTags' : ''; + + $reqSet = $this->getReqIDSet($args->tproject_id); + + $noItems = is_null($reqSet) || count($reqSet) == 0; + $bye = $noItems || (! $canUseTarget && $req_cf_id <= 0); + if ($bye) { + return null; + } + + // OK go ahead + $doSql = true; + $doFilter = false; + $fi = null; + $from['by_custom_field'] = ''; + + if ($req_cf_id > 0) { + $cf_def = $gui->design_cf_rq[$req_cf_id]; + + $from['by_custom_field'] = " JOIN {$tables['cfield_design_values']} CFD " . + " ON CFD.node_id=RQV.id "; + $fi['by_custom_field'] = " AND CFD.field_id=" . intval($req_cf_id); + + switch ($gui->cf_types[$cf_def['type']]) { + case 'date': + $args->custom_field_value = $this->cfieldMgr->cfdate2mktime( + $args->custom_field_value); + + $fi['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; + break; + + default: + $args->custom_field_value = $db->prepare_string( + $args->custom_field_value); + $fi['by_custom_field'] .= " AND CFD.value $this->likeOp '%{$args->custom_field_value}%' "; + break; + } + } + + $args->created_by = trim($args->created_by); + $from['users'] = ''; + if ($args->created_by != '') { + $doFilter = true; + $from['users'] .= " JOIN {$tables['users']} RQAUTHOR ON RQAUTHOR.id = RQV.author_id "; + $fi['author'] = " AND ( RQAUTHOR.login $this->likeOp '%{$args->created_by}%' OR " . + " RQAUTHOR.first $this->likeOp '%{$args->created_by}%' OR " . + " RQAUTHOR.last $this->likeOp '%{$args->created_by}%') "; + } + + $args->edited_by = trim($args->edited_by); + if ($args->edited_by != '') { + $doFilter = true; + $from['users'] .= " JOIN {$tables['users']} UPDATER ON UPDATER.id = RQV.modifier_id "; + $fi['modifier'] = " AND ( UPDATER.login $this->likeOp '%{$args->edited_by}%' OR " . + " UPDATER.first $this->likeOp '%{$args->edited_by}%' OR " . + " UPDATER.last $this->likeOp '%{$args->edited_by}%') "; + } + + if ($doSql) { + $doFilter = true; + + $sql = " /* " . __LINE__ . " */ " . + " SELECT RQ.id AS req_id, RQV.scope,RQ.req_doc_id,NHRQ.name " . + " FROM {$tables['nodes_hierarchy']} NHRQV " . + " JOIN {$views['latest_req_version']} LV on LV.req_id = NHRQV.parent_id " . + " JOIN {$tables['req_versions']} RQV on NHRQV.id = RQV.id AND RQV.version = LV.version " . + " JOIN {$tables['nodes_hierarchy']} NHRQ on NHRQ.id = LV.req_id " . + " JOIN {$tables['requirements']} RQ on RQ.id = LV.req_id " . + $from['users'] . $from['by_custom_field'] . " WHERE RQ.id IN(" . + implode(',', $reqSet) . ")"; + + if (! is_null($args->reqType)) { + $doFilter = true; + $sql .= " AND RQV.type ='" . $db->prepare_string($args->reqType) . + "' "; + } + + if ($args->reqStatus != '') { + $doFilter = true; + $sql .= " AND RQV.status='" . + $db->prepare_string($args->reqStatus) . "' "; + } + + $filterRQ = null; + if ($canUseTarget) { + $doFilter = true; + $filterRQ['tricky'] = " 1=0 "; + + if ($args->rq_scope) { + $filterRQ['scope'] = ' OR ( '; + $filterRQ['scope'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + foreach ($targetSet as $target) { + $filterRQ['scope'] .= $args->and_or . + " $udf(RQV.scope) $this->likeOp '%{$target}%' "; + } + $filterRQ['scope'] .= ')'; + } + + if ($args->rq_title) { + $filterRQ['name'] = ' OR ( '; + $filterRQ['name'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + + foreach ($targetSet as $target) { + $filterRQ['name'] .= $args->and_or . + " NHRQ.name $this->likeOp '%{$target}%' "; + } + $filterRQ['name'] .= ')'; + } + + if ($args->rq_doc_id) { + $filterRQ['req_doc_id'] = ' OR ( '; + $filterRQ['req_doc_id'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + foreach ($targetSet as $target) { + $filterRQ['req_doc_id'] .= $args->and_or . + " RQ.req_doc_id $this->likeOp '%{$target}%' "; + } + $filterRQ['req_doc_id'] .= ')'; + } + } + + $otherFRQ = ''; + if (! is_null($filterRQ)) { + $otherFRQ = " AND (" . implode("", $filterRQ) . ")"; + } + + $xfil = ''; + if (! is_null($fi)) { + $xfil = implode("", $fi); + } + + $sql .= $xfil . $otherFRQ; + if ($doFilter) { + // DEBUGecho __FUNCTION__ . ' SQL Line:' . __LINE__ . $sql .'
    '; + $mapRQ = $db->fetchRowsIntoMap($sql, 'req_id'); + } + + return $mapRQ; + } + } + + /** + */ + public function searchTestSuites($targetSet, $canUseTarget) + { + + // shortcuts + $args = &$this->args; + $db = &$this->db; + $tables = &$this->tables; + $cfg = config_get('UDFStripHTMLTags'); + $udf = $cfg ? 'UDFStripHTMLTags' : ''; + + $mapTS = null; + $tsuiteSet = $this->getTestSuiteIDSet($args->tproject_id); + if (is_null($tsuiteSet) || count($tsuiteSet) == 0) { + return null; + } + + $filterSpecial = null; + $filterSpecial['tricky'] = " 1=0 "; + + if ($args->ts_summary && $canUseTarget) { + $filterSpecial['ts_summary'] = ' OR ( '; + $filterSpecial['ts_summary'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + + foreach ($targetSet as $target) { + $filterSpecial['ts_summary'] .= $args->and_or . + " $udf(TS.details) $this->likeOp '%{$target}%' "; + } + $filterSpecial['ts_summary'] .= ')'; + } + + if ($args->ts_title && $canUseTarget) { + $filterSpecial['ts_title'] = ' OR ( '; + $filterSpecial['ts_title'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + + foreach ($targetSet as $target) { + $filterSpecial['ts_title'] .= $args->and_or . + " NH_TS.name $this->likeOp '%{$target}%' "; + } + $filterSpecial['ts_title'] .= ')'; + } + + $otherFilters = ''; + if (! is_null($filterSpecial)) { + $otherFilters = " AND (" . implode("", $filterSpecial) . ")"; + } + + if ($args->ts_title || $args->ts_summary) { + $fromTS['by_keyword_id'] = ''; + $filterTS['by_keyword_id'] = ''; + if ($args->keyword_id) { + $fromTS['by_keyword_id'] = " JOIN {$tables['object_keywords']} KW ON KW.fk_id = NH_TS.id "; + $filterTS['by_keyword_id'] = " AND KW.keyword_id = " . + $args->keyword_id; + } + + $sqlFields = " SELECT NH_TS.name, TS.id, TS.details " . + " FROM {$tables['nodes_hierarchy']} NH_TS " . + " JOIN {$tables['testsuites']} TS ON TS.id = NH_TS.id " . + $fromTS['by_keyword_id'] . " WHERE TS.id IN (" . + implode(',', $tsuiteSet) . ")"; + + $sql = $sqlFields . $filterTS['by_keyword_id'] . $otherFilters; + $mapTS = $db->fetchRowsIntoMap($sql, 'id'); + + // DEBUGecho 'DEBUG===' . $sql; + } + + return $mapTS; + } + + /** + */ + public function searchTestCases($tcaseSet, $targetSet, $canUseTarget, + $tc_cf_id) + { + // shortcuts + $args = &$this->args; + $gui = &$this->gui; + $db = &$this->db; + $tables = &$this->tables; + $views = &$this->views; + $cfg = config_get('UDFStripHTMLTags'); + $udf = $cfg ? 'UDFStripHTMLTags' : ''; + + $from['tc_steps'] = ""; + $from['users'] = ""; + $from['by_keyword_id'] = ""; + $from['by_custom_field'] = ""; + + $filter = null; + $filterSpecial = null; + + if (is_null($tcaseSet) || count($tcaseSet) == 0) { + return null; + } + + $filter['by_tc_internal_id'] = " AND NH_TCV.parent_id IN (" . + implode(",", $tcaseSet) . ") "; + + $filterSpecial['tricky'] = " 1=0 "; + + if ($args->tc_id) { + $filterSpecial['by_tc_id'] = ''; + + // Remember that test case id is a number! + foreach ($targetSet as $tgx) { + $target = trim($tgx); + if (is_numeric($target)) { + $filterSpecial['by_tc_id'] .= $args->and_or . + " TCV.tc_external_id = $target "; + } + } + } + + $doFilter = false; + $doFilter = ($args->tc_summary || $args->tc_title || $args->tc_id); + + if ($tc_cf_id > 0) { + $cf_def = $gui->design_cf_tc[$tc_cf_id]; + + $from['by_custom_field'] = " JOIN {$tables['cfield_design_values']} CFD " . + " ON CFD.node_id=NH_TCV.id "; + $filter['by_custom_field'] = " AND CFD.field_id=" . intval( + $tc_cf_id); + + switch ($gui->cf_types[$cf_def['type']]) { + case 'date': + $args->custom_field_value = $cfieldMgr->cfdate2mktime( + $args->custom_field_value); + + $filter['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; + break; + + default: + $args->custom_field_value = $db->prepare_string( + $args->custom_field_value); + $filter['by_custom_field'] .= " AND CFD.value $this->likeOp '%{$args->custom_field_value}%' "; + break; + } + } + + if ($args->tc_steps || $args->tc_expected_results) { + $doFilter = true; + $from['tc_steps'] = " LEFT OUTER JOIN {$tables['nodes_hierarchy']} " . + " NH_TCSTEPS ON NH_TCSTEPS.parent_id = NH_TCV.id " . + " LEFT OUTER JOIN {$tables['tcsteps']} TCSTEPS " . + " ON NH_TCSTEPS.id = TCSTEPS.id "; + } + + if ($args->tc_steps && $canUseTarget) { + $filterSpecial['by_steps'] = ' OR ( '; + $filterSpecial['by_steps'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + + foreach ($targetSet as $target) { + $filterSpecial['by_steps'] .= $args->and_or . + " $udf(TCSTEPS.actions) $this->likeOp '%{$target}%' "; + } + $filterSpecial['by_steps'] .= ')'; + } + + if ($args->tc_expected_results && $canUseTarget) { + $filterSpecial['by_expected_results'] = ' OR ( '; + $filterSpecial['by_expected_results'] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + + foreach ($targetSet as $target) { + $filterSpecial['by_expected_results'] .= $args->and_or . + " $udf(TCSTEPS.expected_results) $this->likeOp '%{$target}%' "; + } + $filterSpecial['by_expected_results'] .= ')'; + } + + if ($canUseTarget) { + $k2w = array( + 'name' => 'NH_TC', + 'summary' => 'TCV', + 'preconditions' => 'TCV' + ); + $i2s = array( + 'name' => 'tc_title', + 'summary' => 'tc_summary', + 'preconditions' => 'tc_preconditions' + ); + foreach ($k2w as $kf => $alias) { + $in = $i2s[$kf]; + if ($args->$in) { + $doFilter = true; + + $filterSpecial[$kf] = ' OR ( '; + $filterSpecial[$kf] .= $args->and_or == 'or' ? ' 1=0 ' : ' 1=1 '; + + foreach ($targetSet as $target) { + $filterSpecial[$kf] .= " {$args->and_or} "; + $xx = "{$alias}.{$kf}"; + switch ($kf) { + case 'summary': + case 'preconditions': + $xx = " $udf(" . $xx . ") "; + break; + } + $filterSpecial[$kf] .= "{$xx} {$this->likeOp} '%{$target}%' "; + } + $filterSpecial[$kf] .= ' )'; + } + } + } + + $otherFilters = ''; + if (! is_null($filterSpecial) && count($filterSpecial) > 1) { + $otherFilters = " AND (/* filterSpecial */ " . + implode("", $filterSpecial) . ")"; + } + + // Search on latest test case version using view + $sqlFields = " SELECT LVN.testcase_id, NH_TC.name, TCV.id AS tcversion_id," . + " TCV.summary, TCV.version, TCV.tc_external_id "; + + if ($doFilter) { + if ($args->tcWKFStatus > 0) { + $tg = intval($args->tcWKFStatus); + $filter['by_tcWKFStatus'] = " AND TCV.status = {$tg} "; + } + + if ($args->keyword_id) { + $from['by_keyword_id'] = " JOIN {$tables['testcase_keywords']} KW ON KW.testcase_id = NH_TC.id "; + $filter['by_keyword_id'] = " AND KW.keyword_id = " . + $args->keyword_id; + } + + $created_by_on_tc = $args->created_by = trim($args->created_by); + $from['users'] = ''; + if ($created_by_on_tc != '') { + $doFilter = true; + $from['users'] .= " JOIN {$tables['users']} AUTHOR ON AUTHOR.id = TCV.author_id "; + $filter['author'] = " AND ( AUTHOR.login $this->likeOp '%{$args->created_by}%' OR " . + " AUTHOR.first $this->likeOp '%{$args->created_by}%' OR " . + " AUTHOR.last $this->likeOp '%{$args->created_by}%') "; + } + + $edited_by_on_tc = $args->edited_by = trim($args->edited_by); + if ($edited_by_on_tc != '') { + $doFilter = true; + $from['users'] .= " JOIN {$tables['users']} UPDATER ON UPDATER.id = TCV.updater_id "; + $filter['modifier'] = " AND ( UPDATER.login $this->likeOp '%{$args->edited_by}%' OR " . + " UPDATER.first $this->likeOp '%{$args->edited_by}%' OR " . + " UPDATER.last $this->likeOp '%{$args->edited_by}%') "; + } + } + + // search fails if test case has 0 steps - Added LEFT OUTER + $sqlPart2 = " FROM {$views['latest_tcase_version_number']} LVN " . + " JOIN {$tables['nodes_hierarchy']} NH_TC ON NH_TC.id = LVN.testcase_id " . + " JOIN {$tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = NH_TC.id " . + " JOIN {$tables['tcversions']} TCV ON NH_TCV.id = TCV.id " . + " AND TCV.version = LVN.version " . $from['tc_steps'] . + $from['users'] . $from['by_keyword_id'] . $from['by_custom_field'] . + " WHERE LVN.testcase_id IN (" . implode(',', $tcaseSet) . ")"; + + $mapTC = null; + if ($doFilter) { + $mixedFilter = $this->getFilters(); + if ($filter) { + $sqlPart2 .= implode("", $filter); + } + + if ($mixedFilter['dates4tc']) { + $sqlPart2 .= implode("", $mixedFilter['dates4tc']); + } + + $sql = $sqlFields . $sqlPart2 . $otherFilters; + + // DEBUGecho __FUNCTION__ . '-' . __LINE__ . '-' . $sql .'
    '; + $mapTC = $db->fetchRowsIntoMap($sql, 'testcase_id'); + } + + return $mapTC; + } + + /** + */ + public static function getTestCaseWKFStatusDomain() + { + $cv = array_flip(config_get('testCaseStatus')); + foreach ($cv as $cc => $vv) { + $cv[$cc] = lang_get('testCaseStatus_' . $vv); + } + return $cv; + } +} diff --git a/lib/search/searchForm.php b/lib/search/searchForm.php index a9bc7bb4a8..eac6f6e969 100644 --- a/lib/search/searchForm.php +++ b/lib/search/searchForm.php @@ -1,81 +1,87 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . 'tcSearchForm.tpl'); -/** - * - * - */ -function init_args() -{ - $args = new stdClass(); - $args->tprojectID = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; - - if($args->tprojectID <= 0) - { - throw new Exception("Error Processing Request - Invalid Test project id " . __FILE__); - } - - return $args; +assign('gui', $gui); +$smarty->display($templateCfg->template_dir . 'tcSearchForm.tpl'); + +/** + */ +function initArgs() +{ + $args = new stdClass(); + $args->tprojectID = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; + + if ($args->tprojectID <= 0) { + throw new Exception( + "Error Processing Request - Invalid Test project id " . __FILE__); + } + + return $args; +} + +function initializeGui(&$dbHandler, &$argsObj) +{ + $tproject_mgr = new testproject($dbHandler); + + $gui = new stdClass(); + $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tprojectID) . + config_get('testcase_cfg')->glue_character; + $gui->mainCaption = lang_get('testproject') . " " . $argsObj->tprojectName; + $gui->importance = config_get('testcase_importance_default'); + $gui->creation_date_from = null; + $gui->creation_date_to = null; + $gui->modification_date_from = null; + $gui->modification_date_to = null; + $gui->search_important_notice = sprintf(lang_get('search_important_notice'), + $argsObj->tprojectName); + + $gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design( + $argsObj->tprojectID, cfield_mgr::ENABLED, null, 'testcase'); + + $gui->keywords = $tproject_mgr->getKeywords($argsObj->tprojectID); + + $gui->filter_by['design_scope_custom_fields'] = ! is_null($gui->design_cf); + $gui->filter_by['keyword'] = ! is_null($gui->keywords); + + $reqSpecSet = $tproject_mgr->genComboReqSpec($argsObj->tprojectID); + $gui->filter_by['requirement_doc_id'] = ! is_null($reqSpecSet); + + $gui->option_importance = array( + 0 => '', + HIGH => lang_get('high_importance'), + MEDIUM => lang_get('medium_importance'), + LOW => lang_get('low_importance') + ); + + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + $gui->domainTCStatus = array( + 0 => '' + ) + $dummy['lbl']; + return $gui; } - -function initializeGui(&$dbHandler,&$argsObj) -{ - - $tproject_mgr = new testproject($dbHandler); - - $gui = new stdClass(); - $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tprojectID) . config_get('testcase_cfg')->glue_character; - $gui->mainCaption = lang_get('testproject') . " " . $argsObj->tprojectName; - $gui->importance = config_get('testcase_importance_default'); - $gui->creation_date_from = null; - $gui->creation_date_to = null; - $gui->modification_date_from = null; - $gui->modification_date_to = null; - $gui->search_important_notice = sprintf(lang_get('search_important_notice'),$argsObj->tprojectName); - - $gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design($argsObj->tprojectID,cfield_mgr::ENABLED,null,'testcase'); - - $gui->keywords = $tproject_mgr->getKeywords($argsObj->tprojectID); - - $gui->filter_by['design_scope_custom_fields'] = !is_null($gui->design_cf); - $gui->filter_by['keyword'] = !is_null($gui->keywords); - - $reqSpecSet = $tproject_mgr->genComboReqSpec($argsObj->tprojectID); - $gui->filter_by['requirement_doc_id'] = !is_null($reqSpecSet); - - $gui->option_importance = array(0 => '',HIGH => lang_get('high_importance'),MEDIUM => lang_get('medium_importance'), - LOW => lang_get('low_importance')); - - - $dummy = getConfigAndLabels('testCaseStatus','code'); - $gui->domainTCStatus = array(0 => '') + $dummy['lbl']; - return $gui; -} \ No newline at end of file diff --git a/lib/search/searchMgmt.php b/lib/search/searchMgmt.php index 3c42c7b5fb..4054e26aa8 100644 --- a/lib/search/searchMgmt.php +++ b/lib/search/searchMgmt.php @@ -1,137 +1,128 @@ - array(tlInputParameter::STRING_N,0,200), - "caller" => array(tlInputParameter::STRING_N,0,20)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $tprojectMgr = new testproject($dbHandler); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; - $args->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - - if(is_null($args->tcaseTestProject)) - { - $args->tcaseTestProject = $tprojectMgr->get_by_id($args->tproject_id); - } - return $args; -} - - - -/** - * - * - */ -function initializeEnv($dbHandler) -{ - $args = init_args($dbHandler); - $gui = new stdClass(); - - $grant2check = array('mgt_modify_tc','mgt_view_req','testplan_planning', - 'mgt_modify_product', - 'mgt_modify_req','testcase_freeze', - 'testproject_edit_executed_testcases','testproject_delete_executed_testcases'); - $grants = new stdClass(); - foreach($grant2check as $right) - { - $grants->$right = $_SESSION['currentUser']->hasRight($dbHandler,$right,$args->tproject_id); - $gui->$right = $grants->$right; - } - - $gui->target = $args->target; - $gui->form_token = $args->form_token; - $gui->tproject_id = $args->tproject_id; - $gui->page_title = lang_get('container_title_' . $args->feature); - $gui->caller = trim($args->caller); - return array($args,$gui,$grants); -} - - -/** - * - * - */ -function processSearch(&$dbHandler) -{ - //$cfg = array('testcase' => config_get('testcase_cfg'), - // 'testcase_reorder_by' => config_get('testcase_reorder_by'), - // 'spec' => config_get('spec_cfg')); - - // list($args,$gui,$grants) = initializeEnv($dbHandler); - - $tplEngine = new TLSmarty(); - $tplEngine->tlTemplateCfg = $templateCfg = templateConfiguration(); - - $cmdMgr = new searchCommands($dbHandler); - $cmdMgr->initEnv(); - - $args = $cmdMgr->getArgs(); - - - // need to initialize search fields - $xbm = $cmdMgr->getGui(); - $xbm->warning_msg = lang_get('no_records_found'); - - $xbm->forceSearch = (strlen(trim($args->target)) > 0); - $xbm->caller = basename(__FILE__); - - $xbm->tc_summary = $xbm->tc_title = 1; - $xbm->tc_steps = $xbm->tc_expected_results = $xbm->tc_id = 1; - $xbm->tc_preconditions = $xbm->ts_summary = $xbm->ts_title = 1; - - if($xbm->reqEnabled) - { - $xbm->rs_scope = $xbm->rs_title = 1; - $xbm->rq_scope = $xbm->rq_title = $xbm->rq_doc_id = 1; - } - - - /* - $xbm->filter_by['custom_fields'] = !is_null($args->cf); - $xbm->cf = $args->cf; - $xbm->filter_by['keyword'] = !is_null($args->keywords); - $xbm->keywords = $args->keywords; - $xbm->tcWKFStatus = 0; - $xbm->tcWKFStatusDomain = searchCommands::getTestCaseWKFStatusDomain(); - */ - - $tplEngine->assign('gui',$xbm); - $tplEngine->display($templateCfg->template_dir . 'searchResults.tpl'); -} - -/** - * - */ -function getSearchSkeleton($userInput=null) -{ - $sk = new stdClass(); - - $sk->searchText = null; - - return $sk; + array( + tlInputParameter::STRING_N, + 0, + 200 + ), + "caller" => array( + tlInputParameter::STRING_N, + 0, + 20 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $tprojectMgr = new testproject($dbHandler); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; + $args->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + + if (is_null($args->tcaseTestProject)) { + $args->tcaseTestProject = $tprojectMgr->get_by_id($args->tproject_id); + } + return $args; +} + +/** + */ +function initializeEnv($dbHandler) +{ + $args = initArgs($dbHandler); + $gui = new stdClass(); + + $grant2check = array( + 'mgt_modify_tc', + 'mgt_view_req', + 'testplan_planning', + 'mgt_modify_product', + 'mgt_modify_req', + 'testcase_freeze', + 'testproject_edit_executed_testcases', + 'testproject_delete_executed_testcases' + ); + $grants = new stdClass(); + foreach ($grant2check as $right) { + $grants->$right = $_SESSION['currentUser']->hasRight($dbHandler, $right, + $args->tproject_id); + $gui->$right = $grants->$right; + } + + $gui->target = $args->target; + $gui->form_token = $args->form_token; + $gui->tproject_id = $args->tproject_id; + $gui->page_title = lang_get('container_title_' . $args->feature); + $gui->caller = trim($args->caller); + return array( + $args, + $gui, + $grants + ); +} + +/** + */ +function processSearch(&$dbHandler) +{ + $tplEngine = new TLSmarty(); + $tplEngine->tlTemplateCfg = $templateCfg = templateConfiguration(); + + $cmdMgr = new searchCommands($dbHandler); + $cmdMgr->initEnv(); + + $args = $cmdMgr->getArgs(); + + // need to initialize search fields + $xbm = $cmdMgr->getGui(); + $xbm->warning_msg = lang_get('no_records_found'); + + $xbm->forceSearch = (strlen(trim($args->target)) > 0); + $xbm->caller = basename(__FILE__); + + $xbm->tc_summary = $xbm->tc_title = 1; + $xbm->tc_steps = $xbm->tc_expected_results = $xbm->tc_id = 1; + $xbm->tc_preconditions = $xbm->ts_summary = $xbm->ts_title = 1; + + if ($xbm->reqEnabled) { + $xbm->rs_scope = $xbm->rs_title = 1; + $xbm->rq_scope = $xbm->rq_title = $xbm->rq_doc_id = 1; + } + + $tplEngine->assign('gui', $xbm); + $tplEngine->display($templateCfg->template_dir . 'searchResults.tpl'); +} + +/** + */ +function getSearchSkeleton($userInput = null) +{ + $sk = new stdClass(); + + $sk->searchText = null; + + return $sk; } diff --git a/lib/testcases/archiveData.php b/lib/testcases/archiveData.php index e72fd27166..e341f24b37 100644 --- a/lib/testcases/archiveData.php +++ b/lib/testcases/archiveData.php @@ -1,440 +1,515 @@ -tlTemplateCfg = $templateCfg = templateConfiguration(); - -$cfg = array('testcase' => config_get('testcase_cfg'),'testcase_reorder_by' => config_get('testcase_reorder_by'), - 'spec' => config_get('spec_cfg')); - -list($args,$gui,$grants) = initializeEnv($db); - - -// User right at test project level has to be done -// Because this script can be called requesting an item that CAN BELONG -// to a test project DIFFERENT that value present on SESSION, -// we need to use requested item to get its right Test Project -// We will start with Test Cases ONLY -switch($args->feature) { - case 'testproject': - case 'testsuite': - $item_mgr = new $args->feature($db); - $gui->id = $args->id; - $gui->user = $args->user; - if($args->feature == 'testproject') { - $gui->id = $args->id = $args->tproject_id; - $item_mgr->show($smarty,$gui,$templateCfg->template_dir,$args->id); - } - else { - $gui->direct_link = $item_mgr->buildDirectWebLink($_SESSION['basehref'],$args->id,$args->tproject_id); - $gui->attachments = getAttachmentInfosFrom($item_mgr,$args->id); - $item_mgr->show($smarty,$gui,$templateCfg->template_dir,$args->id, - array('show_mode' => $args->show_mode)); - } - break; - - case 'testcase': - try { - processTestCase($db,$smarty,$args,$gui,$grants,$cfg); - } - catch (Exception $e) { - echo $e->getMessage(); - } - break; - - default: - tLog('Argument "edit" has invalid value: ' . $args->feature , 'ERROR'); - trigger_error($_SESSION['currentUser']->login.'> Argument "edit" has invalid value.', E_USER_ERROR); - break; -} - - - -/** - * - * - */ -function init_args(&$dbHandler) { - $_REQUEST=strings_stripSlashes($_REQUEST); - - $iParams = array("edit" => array(tlInputParameter::STRING_N,0,50), - "id" => array(tlInputParameter::INT_N), - "tcase_id" => array(tlInputParameter::INT_N), - "tcversion_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "targetTestCase" => array(tlInputParameter::STRING_N,0,24), - "show_path" => array(tlInputParameter::INT_N), - "show_mode" => array(tlInputParameter::STRING_N,0,50), - "tcasePrefix" => array(tlInputParameter::STRING_N,0,16), - "tcaseExternalID" => array(tlInputParameter::STRING_N,0,16), - "tcaseVersionNumber" => array(tlInputParameter::INT_N), - "add_relation_feedback_msg" => array(tlInputParameter::STRING_N,0,255), - "caller" => array(tlInputParameter::STRING_N,0,10)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $tprojectMgr = new testproject($dbHandler); - - $cfg = config_get('testcase_cfg'); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; - $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; - - // --------------------------- - // whitelist - $wl = array_flip(array('testcase','testproject','testsuite')); - $args->edit = trim($args->edit); - - if (!isset($wl[$args->edit])) { - tLog('Argument "edit" has invalid value: ' . $args->edit , 'ERROR'); - trigger_error($_SESSION['currentUser']->login . - '> Argument "edit" has invalid value.', E_USER_ERROR); - } - // --------------------------- - - $args->feature = $args->edit; - $args->tcaseTestProject = null; - $args->viewerArgs = null; - - $args->automationEnabled = 0; - $args->requirementsEnabled = 0; - $args->testPriorityEnabled = 0; - $args->tcasePrefix = trim($args->tcasePrefix); - $args->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - - - - // For more information about the data accessed in session here, see the comment - // in the file header of lib/functions/tlTestCaseFilterControl.class.php. - $args->refreshTree = getSettingFromFormNameSpace('edit_mode','setting_refresh_tree_on_action'); - - // Try to understan how this script was called. - switch($args->caller) { - case 'navBar': - systemWideTestCaseSearch($dbHandler,$args,$cfg->glue_character); - break; - - case 'openTCW': - // all data come in - // tcaseExternalID DOM-22 - // tcaseVersionNumber 1 - $args->targetTestCase = $args->tcaseExternalID; // trick for systemWideTestCaseSearch - systemWideTestCaseSearch($dbHandler,$args,$cfg->glue_character); - break; - - default: - if (!$args->tcversion_id) { - $args->tcversion_id = testcase::ALL_VERSIONS; - } - break; - } - - - // used to manage goback - if(intval($args->tcase_id) > 0) { - $args->feature = 'testcase'; - $args->id = intval($args->tcase_id); - } - - switch($args->feature) { - case 'testsuite': - $args->viewerArgs = null; - $_SESSION['setting_refresh_tree_on_action'] = ($args->refreshTree) ? 1 : 0; - break; - - case 'testcase': - $args->viewerArgs = array('action' => '', 'msg_result' => '', - 'user_feedback' => '', - 'disable_edit' => 0, 'refreshTree' => 0, - 'add_relation_feedback_msg' => $args->add_relation_feedback_msg); - - $args->id = is_null($args->id) ? 0 : $args->id; - $args->tcase_id = $args->id; - - if( is_null($args->tcaseTestProject) && $args->id > 0 ) { - $args->tcaseTestProject = $tprojectMgr->getByChildID($args->id); - } - break; - } - - if(is_null($args->tcaseTestProject)) { - $args->tcaseTestProject = $tprojectMgr->get_by_id($args->tproject_id); - } - $args->requirementsEnabled = $args->tcaseTestProject['opt']->requirementsEnabled; - $args->automationEnabled = $args->tcaseTestProject['opt']->automationEnabled; - $args->testPriorityEnabled = $args->tcaseTestProject['opt']->testPriorityEnabled; - - // get code tracker config and object to manage TestLink - CTS integration - $args->ctsCfg = null; - $args->cts = null; - - unset($tprojectMgr); - if( ($args->codeTrackerEnabled = intval($args->tcaseTestProject['code_tracker_enabled'])) ) { - $ct_mgr = new tlCodeTracker($dbHandler); - $args->ctsCfg = $ct_mgr->getLinkedTo($args->tproject_id); - $args->cts = $ct_mgr->getInterfaceObject($args->tproject_id); - - unset($ct_mgr); - } - - return $args; -} - - - -/** - * - * - */ -function initializeEnv($dbHandler) { - $args = init_args($dbHandler); - $gui = new stdClass(); - - $grant2check = - array('mgt_modify_tc','mgt_view_req','testplan_planning', - 'mgt_modify_product','mgt_modify_req','testcase_freeze', - 'keyword_assignment','req_tcase_link_management', - 'testproject_edit_executed_testcases', - 'testproject_delete_executed_testcases', - 'testproject_add_remove_keywords_executed_tcversions', - 'delete_frozen_tcversion'); - - $grants = new stdClass(); - foreach($grant2check as $right) { - $grants->$right = $_SESSION['currentUser']->hasRight($dbHandler,$right,$args->tproject_id); - $gui->$right = $grants->$right; - } - - $gui->modify_tc_rights = $gui->mgt_modify_tc; - - $gui->form_token = $args->form_token; - $gui->tproject_id = $args->tproject_id; - $gui->tplan_id = $args->tplan_id; - - $gui->page_title = lang_get('container_title_' . $args->feature); - $gui->requirementsEnabled = $args->requirementsEnabled; - $gui->automationEnabled = $args->automationEnabled; - $gui->testPriorityEnabled = $args->testPriorityEnabled; - $gui->codeTrackerEnabled = $args->codeTrackerEnabled; - $gui->cts = $args->cts; - $gui->show_mode = $args->show_mode; - $lblkey = config_get('testcase_reorder_by') == 'NAME' ? '_alpha' : '_externalid'; - $gui->btn_reorder_testcases = lang_get('btn_reorder_testcases' . $lblkey); - - // has sense only when we work on test case - $dummy = testcase::getLayout(); - $gui->tableColspan = $dummy->tableToDisplayTestCaseSteps->colspan; - - $gui->platforms = null; - $gui->loadOnCancelURL = ''; - $gui->attachments = null; - $gui->direct_link = null; - $gui->steps_results_layout = config_get('spec_cfg')->steps_results_layout; - $gui->bodyOnUnload = "storeWindowSize('TCEditPopup')"; - $gui->viewerArgs = $args->viewerArgs; - - - return array($args,$gui,$grants); -} - - -/** - * - * - */ -function systemWideTestCaseSearch(&$dbHandler,&$argsObj,$glue) -{ - // Attention: - // this algorithm has potential flaw (IMHO) because we can find the glue character - // in situation where it's role is not this. - // Anyway i will work on this in the future (if I've time) - // - if (strpos($argsObj->targetTestCase,$glue) === false) { - // We suppose user was lazy enough to do not provide prefix, - // then we will try to help him/her - $argsObj->targetTestCase = $argsObj->tcasePrefix . $argsObj->targetTestCase; - } - - if( !is_null($argsObj->targetTestCase) ) { - // parse to get JUST prefix, find the last glue char. - // This useful because from navBar, user can request search of test cases that belongs - // to test project DIFFERENT to test project setted in environment - if( ($gluePos = strrpos($argsObj->targetTestCase, $glue)) !== false) { - $tcasePrefix = substr($argsObj->targetTestCase, 0, $gluePos); - } - - $tprojectMgr = new testproject($dbHandler); - $argsObj->tcaseTestProject = $tprojectMgr->get_by_prefix($tcasePrefix); - - $tcaseMgr = new testcase($dbHandler); - $argsObj->tcase_id = $tcaseMgr->getInternalID($argsObj->targetTestCase); - $dummy = $tcaseMgr->get_basic_info($argsObj->tcase_id,array('number' => $argsObj->tcaseVersionNumber)); - if(!is_null($dummy)) { - $argsObj->tcversion_id = $dummy[0]['tcversion_id']; - } - } -} - -/** - * - */ -function getSettingFromFormNameSpace($mode,$setting) -{ - $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $sd = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$form_token]) ? $_SESSION[$mode][$form_token] : null; - - $rtSetting = isset($sd[$setting]) ? $sd[$setting] : 0; - return $rtSetting; -} - -/** - * - * - */ -function processTestCase(&$dbHandler,$tplEngine,$args,&$gui,$grants,$cfg) { - $get_path_info = false; - $item_mgr = new testcase($dbHandler); - - - // has sense only when we work on test case - $dummy = testcase::getLayout(); - - $gui->showAllVersions = true; - $gui->tableColspan = $dummy->tableToDisplayTestCaseSteps->colspan; - $gui->viewerArgs['refresh_tree'] = 'no'; - $gui->path_info = null; - $gui->platforms = null; - $gui->loadOnCancelURL = ''; - $gui->attachments = null; - $gui->direct_link = null; - $gui->steps_results_layout = $cfg['spec']->steps_results_layout; - $gui->bodyOnUnload = "storeWindowSize('TCEditPopup')"; - - if( ($args->caller == 'navBar') && !is_null($args->targetTestCase) && strcmp($args->targetTestCase,$args->tcasePrefix) != 0) { - - $args->id = $item_mgr->getInternalID($args->targetTestCase); - $args->tcversion_id = testcase::ALL_VERSIONS; - - // I've added $args->caller, in order to make clear the logic, - // because some actions need to be done ONLY - // when we have arrived to this script because user has requested - // a search from navBar. - // Before we have trusted the existence of certain variables - // (do not think this old kind of approach is good). - // - // why strcmp($args->targetTestCase,$args->tcasePrefix) ? - // because in navBar targetTestCase is initialized with testcase prefix - // to provide some help to user - // then if user request search without adding nothing, - // we will not be able to search. - // - // From navBar we want to allow ONLY to search for ONE and ONLY ONE test case ID. - // - $gui->showAllVersions = true; - $gui->viewerArgs['show_title'] = 'no'; - $gui->viewerArgs['display_testproject'] = 1; - $gui->viewerArgs['display_parent_testsuite'] = 1; - if( !($get_path_info = ($args->id > 0)) ) { - $gui->warning_msg = $args->id == 0 ? lang_get('testcase_does_not_exists') : lang_get('prefix_does_not_exists'); - } - } - - // because we can arrive here from a User Search Request, - // if args->id == 0 => nothing found - if( $args->id > 0 ) { - if( $get_path_info || $args->show_path ) { - $gui->path_info = $item_mgr->tree_manager->get_full_path_verbose($args->id); - } - $platform_mgr = new tlPlatform($dbHandler,$args->tproject_id); - - $opx = array(); - $gui->platforms = $platform_mgr->getAllAsMap(); - $gui->direct_link = $item_mgr->buildDirectWebLink($_SESSION['basehref'],$args->id); - - $gui->id = $args->id; - - $identity = new stdClass(); - $identity->id = $args->id; - $identity->tproject_id = $args->tproject_id; - $identity->version_id = intval($args->tcversion_id); - - $gui->showAllVersions = ($identity->version_id == 0); - - // Since 1.9.18, other entities (attachments, keywords, etc) - // are related to test case versions, then the choice is to provide - // in identity an specific test case version. - // If nothing has been received on args, we will get latest active. - // - $latestTCVersionID = $identity->version_id; - if( $latestTCVersionID == 0 ) { - $tcvSet = $item_mgr->getAllVersionsID($args->id); - } else { - $tcvSet = array( $latestTCVersionID ); - } - - foreach( $tcvSet as $tcvx ) { - $gui->attachments[$tcvx] = - getAttachmentInfosFrom($item_mgr,$tcvx); - } - - try { - $item_mgr->show($tplEngine,$gui,$identity,$grants); - } - catch (Exception $e) { - echo $e->getMessage(); - } - exit(); - } - else { - $templateCfg = templateConfiguration(); - - // need to initialize search fields - $xbm = $item_mgr->getTcSearchSkeleton(); - $xbm->warning_msg = lang_get('no_records_found'); - $xbm->pageTitle = lang_get('caption_search_form'); - $xbm->tableSet = null; - $xbm->doSearch = false; - $xbm->tproject_id = $args->tproject_id; - - - $tprj = new testproject($dbHandler); - $oo = $tprj->getOptions($args->tproject_id); - $xbm->filter_by['requirement_doc_id'] = $oo->requirementsEnabled; - $xbm->keywords = $tprj->getKeywords($args->tproject_id); - $xbm->filter_by['keyword'] = !is_null($xbm->keywords); - - // - $cfMgr = new cfield_mgr($dbHandler); - $xbm->design_cf = $cfMgr->get_linked_cfields_at_design($args->tproject_id, - cfield_mgr::ENABLED,null,'testcase'); - - $xbm->filter_by['design_scope_custom_fields'] = !is_null($xbm->design_cf); - - $tplEngine->assign('gui',$xbm); - $tplEngine->display($templateCfg->template_dir . 'tcSearchResults.tpl'); - } +tlTemplateCfg = $templateCfg = templateConfiguration(); + +$cfg = array( + 'testcase' => config_get('testcase_cfg'), + 'testcase_reorder_by' => config_get('testcase_reorder_by'), + 'spec' => config_get('spec_cfg') +); + +list ($args, $gui, $grants) = initializeEnv($db); + +// User right at test project level has to be done +// Because this script can be called requesting an item that CAN BELONG +// to a test project DIFFERENT that value present on SESSION, +// we need to use requested item to get its right Test Project +// We will start with Test Cases ONLY +switch ($args->feature) { + case 'testproject': + case 'testsuite': + $item_mgr = new $args->feature($db); + $gui->id = $args->id; + $gui->user = $args->user; + if ($args->feature == 'testproject') { + $gui->id = $args->id = $args->tproject_id; + $item_mgr->show($smarty, $gui, $templateCfg->template_dir, $args->id); + } else { + $gui->direct_link = $item_mgr->buildDirectWebLink( + $_SESSION['basehref'], $args->id, $args->tproject_id); + $gui->attachments = getAttachmentInfosFrom($item_mgr, $args->id); + $item_mgr->show($smarty, $gui, $templateCfg->template_dir, $args->id, + array( + 'show_mode' => $args->show_mode + )); + } + break; + + case 'testcase': + try { + processTestCase($db, $smarty, $args, $gui, $grants, $cfg); + } catch (Exception $e) { + echo $e->getMessage(); + } + break; + + default: + tLog('Argument "edit" has invalid value: ' . $args->feature, 'ERROR'); + trigger_error( + $_SESSION['currentUser']->login . + '> Argument "edit" has invalid value.', E_USER_ERROR); + break; +} + +/** + * Initialize arguments + * + * @param database $dbHandler + * @return stdClass + */ +function initArgs(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "edit" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "id" => array( + tlInputParameter::INT_N + ), + "tcase_id" => array( + tlInputParameter::INT_N + ), + "tcversion_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "targetTestCase" => array( + tlInputParameter::STRING_N, + 0, + 24 + ), + "show_path" => array( + tlInputParameter::INT_N + ), + "show_mode" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "tcasePrefix" => array( + tlInputParameter::STRING_N, + 0, + 16 + ), + "tcaseExternalID" => array( + tlInputParameter::STRING_N, + 0, + 16 + ), + "tcaseVersionNumber" => array( + tlInputParameter::INT_N + ), + "add_relation_feedback_msg" => array( + tlInputParameter::STRING_N, + 0, + 255 + ), + "caller" => array( + tlInputParameter::STRING_N, + 0, + 10 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $tprojectMgr = new testproject($dbHandler); + + $cfg = config_get('testcase_cfg'); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->user_id = isset($_SESSION['userID']) ? $_SESSION['userID'] : 0; + $args->user = isset($_SESSION['currentUser']) ? $_SESSION['currentUser'] : null; + + // whitelist + $wl = array_flip(array( + 'testcase', + 'testproject', + 'testsuite' + )); + $args->edit = trim($args->edit); + if (! isset($wl[$args->edit])) { + tLog('Argument "edit" has invalid value: ' . $args->edit, 'ERROR'); + trigger_error( + $_SESSION['currentUser']->login . + '> Argument "edit" has invalid value.', E_USER_ERROR); + } + + $args->feature = $args->edit; + $args->tcaseTestProject = null; + $args->viewerArgs = null; + + $args->automationEnabled = 0; + $args->requirementsEnabled = 0; + $args->testPriorityEnabled = 0; + $args->tcasePrefix = trim($args->tcasePrefix); + $args->form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + + // For more information about the data accessed in session here, see the comment + // in the file header of lib/functions/tlTestCaseFilterControl.class.php. + $args->refreshTree = getSettingFromFormNameSpace('edit_mode', + 'setting_refresh_tree_on_action'); + + // Try to understan how this script was called. + switch ($args->caller) { + case 'navBar': + systemWideTestCaseSearch($dbHandler, $args, $cfg->glue_character); + break; + + case 'openTCW': + // all data come in + // tcaseExternalID DOM-22 + // tcaseVersionNumber 1 + $args->targetTestCase = $args->tcaseExternalID; // trick for systemWideTestCaseSearch + systemWideTestCaseSearch($dbHandler, $args, $cfg->glue_character); + break; + + default: + if (! $args->tcversion_id) { + $args->tcversion_id = testcase::ALL_VERSIONS; + } + break; + } + + // used to manage goback + if (intval($args->tcase_id) > 0) { + $args->feature = 'testcase'; + $args->id = intval($args->tcase_id); + } + + switch ($args->feature) { + case 'testsuite': + $args->viewerArgs = null; + $_SESSION['setting_refresh_tree_on_action'] = ($args->refreshTree) ? 1 : 0; + break; + + case 'testcase': + $args->viewerArgs = array( + 'action' => '', + 'msg_result' => '', + 'user_feedback' => '', + 'disable_edit' => 0, + 'refreshTree' => 0, + 'add_relation_feedback_msg' => $args->add_relation_feedback_msg + ); + + $args->id = is_null($args->id) ? 0 : $args->id; + $args->tcase_id = $args->id; + + if (is_null($args->tcaseTestProject) && $args->id > 0) { + $args->tcaseTestProject = $tprojectMgr->getByChildID($args->id); + } + break; + } + + if (is_null($args->tcaseTestProject)) { + $args->tcaseTestProject = $tprojectMgr->get_by_id($args->tproject_id); + } + $args->requirementsEnabled = $args->tcaseTestProject['opt']->requirementsEnabled; + $args->automationEnabled = $args->tcaseTestProject['opt']->automationEnabled; + $args->testPriorityEnabled = $args->tcaseTestProject['opt']->testPriorityEnabled; + + // get code tracker config and object to manage TestLink - CTS integration + $args->ctsCfg = null; + $args->cts = null; + + unset($tprojectMgr); + if ($args->codeTrackerEnabled = intval( + $args->tcaseTestProject['code_tracker_enabled'])) { + $ct_mgr = new tlCodeTracker($dbHandler); + $args->ctsCfg = $ct_mgr->getLinkedTo($args->tproject_id); + $args->cts = $ct_mgr->getInterfaceObject($args->tproject_id); + + unset($ct_mgr); + } + + return $args; +} + +/** + * Initialize the environment + * + * @param database $dbHandler + * @return stdClass[] + */ +function initializeEnv($dbHandler) +{ + $args = initArgs($dbHandler); + $gui = new stdClass(); + + $grant2check = array( + 'mgt_modify_tc', + 'mgt_view_req', + 'testplan_planning', + 'mgt_modify_product', + 'mgt_modify_req', + 'testcase_freeze', + 'keyword_assignment', + 'req_tcase_link_management', + 'testproject_edit_executed_testcases', + 'testproject_delete_executed_testcases', + 'testproject_add_remove_keywords_executed_tcversions', + 'delete_frozen_tcversion' + ); + + $grants = new stdClass(); + foreach ($grant2check as $right) { + $grants->$right = $_SESSION['currentUser']->hasRight($dbHandler, $right, + $args->tproject_id); + $gui->$right = $grants->$right; + } + + $gui->modify_tc_rights = $gui->mgt_modify_tc; + + $gui->form_token = $args->form_token; + $gui->tproject_id = $args->tproject_id; + $gui->tplan_id = $args->tplan_id; + + $gui->page_title = lang_get('container_title_' . $args->feature); + $gui->requirementsEnabled = $args->requirementsEnabled; + $gui->automationEnabled = $args->automationEnabled; + $gui->testPriorityEnabled = $args->testPriorityEnabled; + $gui->codeTrackerEnabled = $args->codeTrackerEnabled; + $gui->cts = $args->cts; + $gui->show_mode = $args->show_mode; + $lblkey = config_get('testcase_reorder_by') == 'NAME' ? '_alpha' : '_externalid'; + $gui->btn_reorder_testcases = lang_get('btn_reorder_testcases' . $lblkey); + + // has sense only when we work on test case + $dummy = testcase::getLayout(); + $gui->tableColspan = $dummy->tableToDisplayTestCaseSteps->colspan; + + $gui->platforms = null; + $gui->loadOnCancelURL = ''; + $gui->attachments = null; + $gui->direct_link = null; + $gui->steps_results_layout = config_get('spec_cfg')->steps_results_layout; + $gui->bodyOnUnload = "storeWindowSize('TCEditPopup')"; + $gui->viewerArgs = $args->viewerArgs; + + return array( + $args, + $gui, + $grants + ); +} + +/** + */ +function systemWideTestCaseSearch(&$dbHandler, &$argsObj, $glue) +{ + // Attention: + // this algorithm has potential flaw (IMHO) because we can find the glue character + // in situation where it's role is not this. + // Anyway i will work on this in the future (if I've time) + // + if (strpos($argsObj->targetTestCase, $glue) === false) { + // We suppose user was lazy enough to do not provide prefix, + // then we will try to help him/her + $argsObj->targetTestCase = $argsObj->tcasePrefix . + $argsObj->targetTestCase; + } + + if (! is_null($argsObj->targetTestCase)) { + // parse to get JUST prefix, find the last glue char. + // This useful because from navBar, user can request search of test cases that belongs + // to test project DIFFERENT to test project setted in environment + if (($gluePos = strrpos($argsObj->targetTestCase, $glue)) !== false) { + $tcasePrefix = substr($argsObj->targetTestCase, 0, $gluePos); + } + + $tprojectMgr = new testproject($dbHandler); + $argsObj->tcaseTestProject = $tprojectMgr->get_by_prefix($tcasePrefix); + + $tcaseMgr = new testcase($dbHandler); + $argsObj->tcase_id = $tcaseMgr->getInternalID($argsObj->targetTestCase); + $dummy = $tcaseMgr->get_basic_info($argsObj->tcase_id, + array( + 'number' => $argsObj->tcaseVersionNumber + )); + if (! is_null($dummy)) { + $argsObj->tcversion_id = $dummy[0]['tcversion_id']; + } + } +} + +/** + * getSettingFromFormNameSpace + * + * @param string $mode + * @param string $setting + * @return number + */ +function getSettingFromFormNameSpace($mode, $setting) +{ + $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $sd = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$form_token]) ? $_SESSION[$mode][$form_token] : null; + + return isset($sd[$setting]) ? $sd[$setting] : 0; +} + +/** + * + * @param database $dbHandler + * @param TLSmarty $tplEngine + * @param stdClass $args + * @param stdClass $gui + * @param stdClass $grants + * @param array $cfg + */ +function processTestCase(&$dbHandler, $tplEngine, $args, &$gui, $grants, $cfg) +{ + $get_path_info = false; + $item_mgr = new testcase($dbHandler); + + // has sense only when we work on test case + $dummy = testcase::getLayout(); + + $gui->showAllVersions = true; + $gui->tableColspan = $dummy->tableToDisplayTestCaseSteps->colspan; + $gui->viewerArgs['refresh_tree'] = 'no'; + $gui->path_info = null; + $gui->platforms = null; + $gui->loadOnCancelURL = ''; + $gui->attachments = null; + $gui->direct_link = null; + $gui->steps_results_layout = $cfg['spec']->steps_results_layout; + $gui->bodyOnUnload = "storeWindowSize('TCEditPopup')"; + + if (($args->caller == 'navBar') && ! is_null($args->targetTestCase) && + strcmp($args->targetTestCase, $args->tcasePrefix) != 0) { + + $args->id = $item_mgr->getInternalID($args->targetTestCase); + $args->tcversion_id = testcase::ALL_VERSIONS; + + // I've added $args->caller, in order to make clear the logic, + // because some actions need to be done ONLY + // when we have arrived to this script because user has requested + // a search from navBar. + // Before we have trusted the existence of certain variables + // (do not think this old kind of approach is good). + // + // why strcmp($args->targetTestCase,$args->tcasePrefix) ? + // because in navBar targetTestCase is initialized with testcase prefix + // to provide some help to user + // then if user request search without adding nothing, + // we will not be able to search. + // + // From navBar we want to allow ONLY to search for ONE and ONLY ONE test case ID. + // + $gui->showAllVersions = true; + $gui->viewerArgs['show_title'] = 'no'; + $gui->viewerArgs['display_testproject'] = 1; + $gui->viewerArgs['display_parent_testsuite'] = 1; + if (! ($get_path_info = ($args->id > 0))) { + $gui->warning_msg = $args->id == 0 ? lang_get( + 'testcase_does_not_exists') : lang_get('prefix_does_not_exists'); + } + } + + // because we can arrive here from a User Search Request, + // if args->id == 0 => nothing found + if ($args->id > 0) { + if ($get_path_info || $args->show_path) { + $gui->path_info = $item_mgr->tree_manager->get_full_path_verbose( + $args->id); + } + $platform_mgr = new tlPlatform($dbHandler, $args->tproject_id); + + $gui->platforms = $platform_mgr->getAllAsMap(); + $gui->direct_link = $item_mgr->buildDirectWebLink($_SESSION['basehref'], + $args->id); + + $gui->id = $args->id; + + $identity = new stdClass(); + $identity->id = $args->id; + $identity->tproject_id = $args->tproject_id; + $identity->version_id = intval($args->tcversion_id); + + $gui->showAllVersions = ($identity->version_id == 0); + + // Since 1.9.18, other entities (attachments, keywords, etc) + // are related to test case versions, then the choice is to provide + // in identity an specific test case version. + // If nothing has been received on args, we will get latest active. + // + $latestTCVersionID = $identity->version_id; + if ($latestTCVersionID == 0) { + $tcvSet = $item_mgr->getAllVersionsID($args->id); + } else { + $tcvSet = array( + $latestTCVersionID + ); + } + + foreach ($tcvSet as $tcvx) { + $gui->attachments[$tcvx] = getAttachmentInfosFrom($item_mgr, $tcvx); + } + + try { + $item_mgr->show($tplEngine, $gui, $identity, $grants); + } catch (Exception $e) { + echo $e->getMessage(); + } + exit(); + } else { + $templateCfg = templateConfiguration(); + + // need to initialize search fields + $xbm = $item_mgr->getTcSearchSkeleton(); + $xbm->warning_msg = lang_get('no_records_found'); + $xbm->pageTitle = lang_get('caption_search_form'); + $xbm->tableSet = null; + $xbm->doSearch = false; + $xbm->tproject_id = $args->tproject_id; + + $tprj = new testproject($dbHandler); + $oo = $tprj->getOptions($args->tproject_id); + $xbm->filter_by['requirement_doc_id'] = $oo->requirementsEnabled; + $xbm->keywords = $tprj->getKeywords($args->tproject_id); + $xbm->filter_by['keyword'] = ! is_null($xbm->keywords); + + // + $cfMgr = new cfield_mgr($dbHandler); + $xbm->design_cf = $cfMgr->get_linked_cfields_at_design( + $args->tproject_id, cfield_mgr::ENABLED, null, 'testcase'); + + $xbm->filter_by['design_scope_custom_fields'] = ! is_null( + $xbm->design_cf); + + $tplEngine->assign('gui', $xbm); + $tplEngine->display($templateCfg->template_dir . 'tcSearchResults.tpl'); + } } diff --git a/lib/testcases/containerEdit.php b/lib/testcases/containerEdit.php index 5b6009161d..3779b63f5a 100644 --- a/lib/testcases/containerEdit.php +++ b/lib/testcases/containerEdit.php @@ -1,1522 +1,1581 @@ -js_ot_name = 'ot'; - -$args = init_args($db,$tproject_mgr,$tsuite_mgr,$opt_cfg); -$level = isset($args->level) ? $args->level : $args->containerType; - -$gui_cfg = config_get('gui'); -$smarty = new TLSmarty(); -$smarty->assign('editorType',$editorCfg['type']); - - -list($a_tpl, $a_actions) = initTPLActions(); - -$a_init_opt_transfer = array('edit_testsuite' => 1,'new_testsuite' => 1, - 'add_testsuite' => 1,'update_testsuite' => 1); - -$the_tpl = null; -$action = null; -$get_c_data = null; -$init_opt_transfer = null; - - -// 20121222 -franciscom -// Need this trick because current implementation of Ext.ux.requireSessionAndSubmit() -// discards the original submit button -if( isset($_REQUEST['doAction']) ) { - $_POST[$_REQUEST['doAction']] = $_REQUEST['doAction']; -} -$action = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; - -foreach ($a_actions as $the_key => $the_val) { - if (isset($_POST[$the_key]) ) { - $the_tpl = isset($a_tpl[$the_key]) ? $a_tpl[$the_key] : null; - $init_opt_transfer = isset($a_init_opt_transfer[$the_key])?1:0; - - $action = $the_key; - $get_c_data = $the_val; - $level = is_null($level) ? 'testsuite' : $level; - break; - } -} -$args->action = $action; - -$smarty->assign('level', $level); -$smarty->assign('page_title',lang_get('container_title_' . $level)); - -if($init_opt_transfer) { - $opt_cfg = initializeOptionTransfer($tproject_mgr,$tsuite_mgr,$args,$action); -} - -// create web editor objects -list($oWebEditor,$webEditorHtmlNames,$webEditorTemplateKey) = initWebEditors($action,$level,$editorCfg); -if($get_c_data) { - $name_ok = 1; - $c_data = getValuesFromPost($webEditorHtmlNames); - if($name_ok && !check_string($c_data['container_name'],$g_ereg_forbidden)) { - $msg = $args->l10n['string_contains_bad_chars']; - $name_ok = 0; - } - - if($name_ok && ($c_data['container_name'] == "")) { - $msg = $args->l10n['warning_empty_testsuite_name']; - $name_ok = 0; - } -} - -$doIt = true; -switch($action) { - case 'fileUpload': - case 'deleteFile': - case 'edit_testsuite': - case 'new_testsuite': - case 'delete_testsuite': - case 'move_testsuite_viewer': - case 'reorder_testsuites': - case 'update_testsuite': - case 'do_move': - case 'do_copy': - case 'do_move_tcase_set': - case 'do_copy_tcase_set': - case 'do_copy_tcase_set_ghost': - case 'add_testsuite': - case 'delete_testcases': - case 'do_delete_testcases': - case 'reorder_testcases': - case 'reorder_testsuites_alpha': - case 'reorder_testproject_testsuites_alpha': - case 'doBulkSet': - case 'addKeyword': - case 'addKeywordTSDeep': - case 'removeKeyword': - $doIt = ('yes' == $args->grants->testcase_mgmt); - break; -} - -if( $doIt ) { - switch($action) { - case 'fileUpload': - switch($level) { - case 'testsuite': - $uploadOp = fileUploadManagement($db,$args->testsuiteID,$args->fileTitle,$tsuite_mgr->getAttachmentTableName()); - $gui = initializeGui($tsuite_mgr,$args->testsuiteID,$args); - $gui->refreshTree = 0; - $gui->uploadOp = $uploadOp; - $tsuite_mgr->show($smarty,$gui,$template_dir,$args->testsuiteID,null,null); - break; - - case 'testproject': - $uploadOp = fileUploadManagement($db,$args->tprojectID,$args->fileTitle,$tproject_mgr->getAttachmentTableName()); - $gui = initializeGui($tproject_mgr,$args->tprojectID,$args); - $gui->refreshTree = 0; - $gui->uploadOp = $uploadOp; - - $tproject_mgr->show($smarty,$gui,$template_dir,$args->tprojectID,null,null); - break; - } - break; - - case 'deleteFile': - deleteAttachment($db,$args->file_id); - switch($level) { - case 'testsuite': - $gui = initializeGui($tsuite_mgr,$args->testsuiteID,$args); - $gui->refreshTree = 0; - $tsuite_mgr->show($smarty,$gui,$template_dir,$args->testsuiteID,null,null); - break; - - case 'testproject': - $gui = initializeGui($tproject_mgr,$args->tprojectID,$args); - $gui->refreshTree = 0; - $tproject_mgr->show($smarty,$gui,$template_dir,$args->tprojectID,null,null); - break; - } - break; - - case 'edit_testsuite': - case 'new_testsuite': - keywords_opt_transf_cfg($opt_cfg, $args->assigned_keyword_list); - - $smarty->assign('opt_cfg', $opt_cfg); - - $gui = new stdClass(); - $gui->tproject_id = $args->tprojectID; - $gui->containerType = $level; - $gui->refreshTree = $args->refreshTree; - $gui->hasKeywords = (count($opt_cfg->from->map) > 0) || (count($opt_cfg->to->map) > 0); - - $gui->cancelActionJS = 'location.href=fRoot+' . - "'lib/testcases/archiveData.php?id=" . intval($args->containerID); - switch($level) { - case 'testproject': - $gui->cancelActionJS .= "&edit=testproject&level=testproject'"; - break; - - case 'testsuite': - $gui->cancelActionJS .= "&edit=testsuite&level=testsuite&containerType=testsuite'"; - break; - } - - $smarty->assign('level', $level); - $smarty->assign('gui', $gui); - $tsuite_mgr->viewer_edit_new($smarty,$template_dir,$webEditorHtmlNames, - $oWebEditor,$action,$args->containerID, - $args->testsuiteID,null, - $webEditorTemplateKey); - break; - - case 'delete_testsuite': - $refreshTree = deleteTestSuite($smarty,$args,$tsuite_mgr,$tree_mgr,$tcase_mgr,$level); - break; - - case 'move_testsuite_viewer': - moveTestSuiteViewer($smarty,$tproject_mgr,$args); - break; - - case 'move_testcases_viewer': - moveTestCasesViewer($db,$smarty,$tproject_mgr,$tree_mgr,$args); - break; - - case 'testcases_table_view': - $cf = null; - $cf_map = $tcase_mgr->get_linked_cfields_at_design(0,null,null,null,$args->tprojectID); - if(!is_null($cf_map)) { - $cfOpt = array('addCheck' => true, 'forceOptional' => true); - $cf = $tcase_mgr->cfield_mgr->html_table_inputs($cf_map,'',null,$cfOpt); - } - - moveTestCasesViewer($db,$smarty,$tproject_mgr,$tree_mgr,$args,null,$cf); - break; - - - case 'reorder_testsuites': - $ret = reorderTestSuiteViewer($smarty,$tree_mgr,$args); - $level = is_null($ret) ? $level : $ret; - break; - - case 'do_move': - moveTestSuite($smarty,$template_dir,$tproject_mgr,$args); - break; - - case 'do_copy': - copyTestSuite($smarty,$template_dir,$tsuite_mgr,$args); - break; - - case 'update_testsuite': - if ($name_ok) { - $msg = updateTestSuite($tsuite_mgr,$args,$c_data,$_REQUEST); - } - $gui = initializeGui($tsuite_mgr,$args->testsuiteID,$args); - $tsuite_mgr->show($smarty,$gui,$template_dir,$args->testsuiteID,null,$msg); - break; - - case 'add_testsuite': - $messages = null; - $op['status'] = 0; - if ($name_ok) { - $op = addTestSuite($tsuite_mgr,$args,$c_data,$_REQUEST); - $messages = array( 'result_msg' => $op['messages']['msg'], - 'user_feedback' => $op['messages']['user_feedback']); - } - - // $userInput is used to maintain data filled by user if there is - // a problem with test suite name. - $userInput = $op['status'] ? null : $_REQUEST; - $assignedKeywords = $op['status'] ? "" : $args->assigned_keyword_list; - keywords_opt_transf_cfg($opt_cfg, $assignedKeywords); - $smarty->assign('opt_cfg', $opt_cfg); - - $gui = new stdClass(); - $gui->tproject_id = $args->tprojectID; - $gui->containerType = $level; - $gui->refreshTree = $args->refreshTree; - $gui->cancelActionJS = 'location.href=fRoot+' . - "'lib/testcases/archiveData.php?id=" . intval($args->containerID); - - switch($level) { - case 'testproject': - $gui->cancelActionJS .= "&edit=testproject&level=testproject'"; - break; - - case 'testsuite': - $gui->cancelActionJS .= "&edit=testsuite&level=testsuite&containerType=testsuite'"; - break; - } - - $smarty->assign('level', $level); - $smarty->assign('gui', $gui); - - $tsuite_mgr->viewer_edit_new($smarty,$template_dir,$webEditorHtmlNames, - $oWebEditor, $action,$args->containerID, null, - $messages,$webEditorTemplateKey,$userInput); - break; - - - case 'do_move_tcase_set': - moveTestCases($smarty,$template_dir,$tsuite_mgr,$tree_mgr,$args); - break; - - case 'do_copy_tcase_set': - case 'do_copy_tcase_set_ghost': - $args->stepAsGhost = ($action == 'do_copy_tcase_set_ghost'); - $op = copyTestCases($smarty,$template_dir,$tsuite_mgr,$tcase_mgr,$args); - - $refreshTree = $op['refreshTree']; - moveTestCasesViewer($db,$smarty,$tproject_mgr,$tree_mgr,$args,$op['userfeedback']); - break; - - - case 'delete_testcases': - $args->refreshTree = false; - deleteTestCasesViewer($db,$smarty,$tproject_mgr,$tree_mgr,$tsuite_mgr,$tcase_mgr,$args); - break; - - case 'do_delete_testcases': - $args->refreshTree = true; - doDeleteTestCases($db,$args->tcaseSet,$tcase_mgr); - deleteTestCasesViewer($db,$smarty,$tproject_mgr,$tree_mgr,$tsuite_mgr, - $tcase_mgr,$args, - lang_get('all_testcases_have_been_deleted')); - break; - - case 'reorder_testcases': - reorderTestCasesByCriteria($args,$tsuite_mgr,$tree_mgr); - $gui = initializeGui($tsuite_mgr,$args->testsuiteID,$args); - $gui->refreshTree = true; - $tsuite_mgr->show($smarty,$gui,$template_dir,$args->testsuiteID,null,null); - break; - - - case 'reorder_testsuites_alpha': - reorderTestSuitesDictionary($args,$tree_mgr,$args->testsuiteID); - $gui = initializeGui($tsuite_mgr,$args->testsuiteID,$args); - $gui->refreshTree = true; - $tsuite_mgr->show($smarty,$gui,$template_dir,$args->testsuiteID,null,null); - break; - - case 'reorder_testproject_testsuites_alpha': - reorderTestSuitesDictionary($args,$tree_mgr,$args->tprojectID); - $gui = initializeGui($tproject_mgr,$args->tprojectID,$args); - $gui->refreshTree = true; - $tproject_mgr->show($smarty,$gui,$template_dir,$args->tprojectID,null,null); - break; - - case 'doBulkSet': - $args->refreshTree = true; - doBulkSet($db,$args,$args->tcaseSet,$tcase_mgr); - - $cf = null; - $cf_map = $tcase_mgr->get_linked_cfields_at_design(0,null,null,null,$args->tprojectID); - if(!is_null($cf_map)) { - $cfOpt = array('addCheck' => true, 'forceOptional' => true); - $cf = $tcase_mgr->cfield_mgr->html_table_inputs($cf_map,'',null,$cfOpt); - } - - moveTestCasesViewer($db,$smarty,$tproject_mgr,$tree_mgr,$args,null,$cf); - break; - - case 'addKeyword': - $tsuite_mgr->addKeywords($args->item_id,$args->free_keywords); - showTestSuite($smarty,$args,$tsuite_mgr,$template_dir); - exit(); - break; - - case 'addKeywordTSDeep': - $tsuite_mgr->addKeywordsDeep($args->item_id,$args->free_keywords); - showTestSuite($smarty,$args,$tsuite_mgr,$template_dir); - exit(); - break; - - case 'removeKeyword': - $tsuite_mgr->deleteKeywordByLinkID($args->kw_link_id); - showTestSuite($smarty,$args,$tsuite_mgr,$template_dir); - exit(); - break; - - - default: - trigger_error("containerEdit.php - No correct GET/POST data", E_USER_ERROR); - break; - } -} - - -if($the_tpl) { - $smarty->assign('refreshTree',$refreshTree && $args->refreshTree); - $smarty->display($template_dir . $the_tpl); -} - - -/** - * - * - */ -function getValuesFromPost($akeys2get) { - $akeys2get[] = 'container_name'; - $c_data = array(); - foreach($akeys2get as $key) { - $c_data[$key] = isset($_POST[$key]) ? strings_stripSlashes($_POST[$key]) : null; - } - return $c_data; -} - -/* - function: - -args : - -returns: - -*/ -function build_del_testsuite_warning_msg(&$tree_mgr,&$tcase_mgr,&$testcases,$tsuite_id) -{ - $msg = null; - $msg['warning'] = null; - $msg['link_msg'] = null; - $msg['delete_msg'] = null; - - if(!is_null($testcases)) { - $show_warning = 0; - $delete_msg = ''; - $verbose = array(); - $msg['link_msg'] = array(); - - $status_warning = array('linked_and_executed' => 1, - 'linked_but_not_executed' => 1, - 'no_links' => 0); - - $delete_notice = array('linked_and_executed' => lang_get('delete_notice'), - 'linked_but_not_executed' => '', - 'no_links' => ''); - - $getOptions = array('addExecIndicator' => true); - foreach($testcases as $the_key => $elem) { - $verbose[] = $tree_mgr->get_path($elem['id'],$tsuite_id); - $xx = $tcase_mgr->get_exec_status($elem['id'],null,$getOptions); - $status = 'no_links'; - if(!is_null($xx)) { - $status = $xx['executed'] ? 'linked_and_executed' : 'linked_but_not_executed'; - } - $msg['link_msg'][] = $status; - - if($status_warning[$status]) { - $show_warning = 1; - $msg['delete_msg'] = $delete_notice[$status]; - } - } - - $idx = 0; - if($show_warning) { - $msg['warning'] = array(); - foreach($verbose as $the_key => $elem) { - $msg['warning'][$idx] = ''; - $bSlash = false; - foreach($elem as $tkey => $telem) { - if ($bSlash) { - $msg['warning'][$idx] .= "\\"; - } - $msg['warning'][$idx] .= $telem['name']; - $bSlash = true; - } - $idx++; - } - } - else { - $msg['link_msg'] = null; - $msg['warning'] = null; - } - } - return $msg; -} - - -/* - function: - -args : - -returns: - -*/ -function init_args(&$dbHandler,&$tprojectMgr,&$tsuiteMgr,$optionTransferCfg) { - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args->kw_link_id = isset($_REQUEST['kw_link_id']) ? - intval($_REQUEST['kw_link_id']) : null; - - $args->item_id = isset($_REQUEST['item_id']) ? - intval($_REQUEST['item_id']) : null; - - $args->free_keywords = isset($_REQUEST['free_keywords']) ? - $_REQUEST['free_keywords'] : null; - - $args->containerID = isset($_REQUEST['containerID']) ? - $_REQUEST['containerID'] : null; - - // check againts whitelist - $args->objectType = isset($_REQUEST['objectType']) ? - $_REQUEST['objectType'] : null; - - $args->containerType = isset($_REQUEST['containerType']) ? - $_REQUEST['containerType'] : null; - - if( null != $args->containerType ) { - $ctWhiteList = array('testproject' => 'OK','testsuite' => 'OK'); - if(!is_null($args->containerType) && - !isset($ctWhiteList[$args->containerType])) { - $args->containerType = null; - } - } - - // When Deleting Test suite - container ID is not set - if( is_null($args->containerID) ) { - $args->containerType = is_null($args->containerType) ? 'testproject' : - $args->containerType; - } - - if( null == $args->containerType ) { - throw new Exception("Error No Container Type", 1); - } - - - $args->testsuiteID = isset($_REQUEST['testsuiteID']) ? intval($_REQUEST['testsuiteID']) : null; - $args->tsuite_name = isset($_REQUEST['testsuiteName']) ? $_REQUEST['testsuiteName'] : null; - - // Order is critic - $args->objectID = isset($_REQUEST['objectID']) ? intval($_REQUEST['objectID']) : null; - $args->containerID = isset($_REQUEST['containerID']) ? - intval($_REQUEST['containerID']) : $args->objectID; - - switch( $args->containerType ) { - case 'testproject': - $args->tprojectID = $args->containerID; - break; - - case 'testsuite': - $nodeID = !is_null($args->testsuiteID) ? $args->testsuiteID : - $args->containerID; - $args->tprojectID = $tsuiteMgr->getTestProjectFromTestSuite($nodeID,null); - break; - } - - if( intval($args->tprojectID) == 0 ) { - if( isset($_REQUEST['tproject_id']) ) { - $args->tprojectID = intval($_REQUEST['tproject_id']); - } - - if( isset($_REQUEST['tprojectID']) ) { - $args->tprojectID = intval($_REQUEST['tprojectID']); - } - } - - // very ugly - $args->level = null; - if( isset($_REQUEST['delete_testsuite']) ) { - $args->level = $args->objectType; - } - - - $info = $tprojectMgr->get_by_id($args->tprojectID, - array('output' => 'name')); - $args->tprojectName = $info['name']; - - $args->userID = isset($_SESSION['userID']) ? intval($_SESSION['userID']) : 0; - $args->file_id = isset($_REQUEST['file_id']) ? intval($_REQUEST['file_id']) : 0; - $args->fileTitle = isset($_REQUEST['fileTitle']) ? trim($_REQUEST['fileTitle']) : ''; - - - $k2l = array('tc_status','importance','execution_type'); - foreach($k2l as $kv) { - $args->$kv = isset($_REQUEST[$kv]) ? intval($_REQUEST[$kv]) : -1; - } - - $args->user = $_SESSION['currentUser']; - - $args->grants = new stdClass(); - $args->grants->delete_executed_testcases = - $args->user->hasRight($dbHandler,'testproject_delete_executed_testcases',$args->tprojectID); - - $args->grants->testcase_mgmt = - $args->user->hasRight($dbHandler,'mgt_modify_tc',$args->tprojectID); - - - $keys2loop=array('nodes_order' => null, 'tcaseSet' => null, - 'target_position' => 'bottom', 'doAction' => ''); - foreach($keys2loop as $key => $value) { - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $value; - } - - $args->bSure = (isset($_REQUEST['sure']) && ($_REQUEST['sure'] == 'yes')); - $rl_html_name = $optionTransferCfg->js_ot_name . "_newRight"; - $args->assigned_keyword_list = isset($_REQUEST[$rl_html_name])? $_REQUEST[$rl_html_name] : ""; - - - $keys2loop = array('copyKeywords' => 0,'copyRequirementAssignments' => 0); - - foreach($keys2loop as $key => $value) { - $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : $value; - } - - - $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? - $_SESSION['setting_refresh_tree_on_action'] : 0; - $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; - $args->testCaseSet = null; - - if($args->treeFormToken >0) { - $mode = 'edit_mode'; - $sdata = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$args->treeFormToken]) ? - $_SESSION[$mode][$args->treeFormToken] : null; - $args->testCaseSet = isset($sdata['testcases_to_show']) ? $sdata['testcases_to_show'] : null; - } - - $dummy = ($sortCriteria = config_get('testcase_reorder_by')) == 'NAME' ? '_alpha' : - '_externalid'; - $lbl2init = array('warning_empty_testsuite_name' => null, - 'string_contains_bad_chars' => null, - 'container_title_testsuite' => null, - 'btn_reorder_testcases' => 'btn_reorder_testcases' . $dummy); - $args->l10n = init_labels($lbl2init); - - return $args; -} - - -/* - function: - -args: - -returns: - -*/ -function writeCustomFieldsToDB(&$db,$tprojectID,$tsuiteID,&$hash) -{ - $ENABLED = 1; - $NO_FILTERS = null; - - $cfield_mgr = new cfield_mgr($db); - $cf_map = $cfield_mgr->get_linked_cfields_at_design($tprojectID,$ENABLED, - $NO_FILTERS,'testsuite'); - $cfield_mgr->design_values_to_db($hash,$tsuiteID,$cf_map); -} - - -/* - function: deleteTestSuite - -args: - -returns: true -> refresh tree -false -> do not refresh - -*/ -function deleteTestSuite(&$smartyObj,&$argsObj,&$tsuiteMgr,&$treeMgr,&$tcaseMgr,$level) -{ - - $feedback_msg = ''; - $system_message = ''; - $testcase_cfg = config_get('testcase_cfg'); - $can_delete = 1; - - if($argsObj->bSure) { - $tsuite = $tsuiteMgr->get_by_id($argsObj->objectID); - $tsuiteMgr->delete_deep($argsObj->objectID); - $tsuiteMgr->deleteKeywords($argsObj->objectID); - $smartyObj->assign('objectName', $tsuite['name']); - $doRefreshTree = true; - $feedback_msg = 'ok'; - $smartyObj->assign('user_feedback',lang_get('testsuite_successfully_deleted')); - } - else { - $doRefreshTree = false; - - // Get test cases present in this testsuite and all children - $testcases = $tsuiteMgr->get_testcases_deep($argsObj->testsuiteID); - $map_msg['warning'] = null; - $map_msg['link_msg'] = null; - $map_msg['delete_msg'] = null; - - if(is_null($testcases) || count($testcases) == 0) { - $can_delete = 1; - } - else { - $map_msg = build_del_testsuite_warning_msg($treeMgr,$tcaseMgr,$testcases,$argsObj->testsuiteID); - if( in_array('linked_and_executed', (array)$map_msg['link_msg']) ) { - $can_delete = $argsObj->grants->delete_executed_testcases; - } - } - - $system_message = ''; - if(!$can_delete && !$argsObj->grants->delete_executed_testcases) { - $system_message = lang_get('system_blocks_tsuite_delete_due_to_exec_tc'); - } - - // prepare to show the delete confirmation page - $smartyObj->assign('can_delete',$can_delete); - $smartyObj->assign('objectID',$argsObj->testsuiteID); - $smartyObj->assign('objectType','testsuite'); - $smartyObj->assign('objectName', $argsObj->tsuite_name); - $smartyObj->assign('containerType', $argsObj->containerType); - $smartyObj->assign('delete_msg',$map_msg['delete_msg']); - $smartyObj->assign('warning', $map_msg['warning']); - $smartyObj->assign('link_msg', $map_msg['link_msg']); - } - $smartyObj->assign('system_message', $system_message); - $smartyObj->assign('page_title', lang_get('delete') . " " . lang_get('container_title_' . $level)); - $smartyObj->assign('sqlResult',$feedback_msg); - - return $doRefreshTree; -} - -/* - function: addTestSuite - -args: - -returns: map with messages and status - -revision: -20101012 - franciscom - BUGID 3890 -when creating action on duplicate is setted to BLOCK without using -config_get('action_on_duplicate_name'). -This is because this config option has to be used ONLY when copying/moving not when creating. - -20091206 - franciscom - new items are created as last element of tree branch - -*/ -function addTestSuite(&$tsuiteMgr,&$argsObj,$container,&$hash) { - $new_order = null; - - // compute order - // - // $nt2exclude=array('testplan' => 'exclude_me','requirement_spec'=> 'exclude_me','requirement'=> 'exclude_me'); - // $siblings = $tsuiteMgr->tree_manager->get_children($argsObj->containerID,$nt2exclude); - // if( !is_null($siblings) ) - //{ - // $dummy = end($siblings); - // $new_order = $dummy['node_order']+1; - //} - $ret = $tsuiteMgr->create($argsObj->containerID,$container['container_name'], - $container['details'], - $new_order,config_get('check_names_for_duplicates'),'block'); - - $op['messages']= array('msg' => $ret['msg'], 'user_feedback' => ''); - $op['status']=$ret['status_ok']; - - if($ret['status_ok']) - { - $op['messages']['user_feedback'] = lang_get('testsuite_created'); - if($op['messages']['msg'] != 'ok') { - $op['messages']['user_feedback'] = $op['messages']['msg']; - } - - if(trim($argsObj->assigned_keyword_list) != "") { - $tsuiteMgr->addKeywords($ret['id'],explode(",",$argsObj->assigned_keyword_list)); - } - writeCustomFieldsToDB($tsuiteMgr->db,$argsObj->tprojectID,$ret['id'],$hash); - - // Send Events to plugins - $ctx = array('id' => $ret['id'],'name' => $container['container_name'],'details' => $container['details']); - event_signal('EVENT_TEST_SUITE_CREATE', $ctx); - } - return $op; -} - -/* - function: moveTestSuiteViewer -prepares smarty variables to display move testsuite viewer - -args: - -returns: - - -*/ -function moveTestSuiteViewer(&$smartyObj,&$tprojectMgr,$argsObj) { - $testsuites = $tprojectMgr->gen_combo_test_suites($argsObj->tprojectID, - array($argsObj->testsuiteID => 'exclude')); - // Added the Test Project as the FIRST Container where is possible to copy - $testsuites = array($argsObj->tprojectID => $argsObj->tprojectName) + $testsuites; - - // original container (need to comment this better) - $smartyObj->assign('old_containerID', $argsObj->tprojectID); - $smartyObj->assign('containers', $testsuites); - $smartyObj->assign('objectID', $argsObj->testsuiteID); - $smartyObj->assign('object_name', $argsObj->tsuite_name); - $smartyObj->assign('top_checked','checked=checked'); - $smartyObj->assign('bottom_checked',''); -} - - -/* - function: reorderTestSuiteViewer -prepares smarty variables to display reorder testsuite viewer - -args: - -returns: - - -*/ -function reorderTestSuiteViewer(&$smartyObj,&$treeMgr,$argsObj) -{ - $level = null; - $oid = is_null($argsObj->testsuiteID) ? $argsObj->containerID : $argsObj->testsuiteID; - $children = $treeMgr->get_children($oid, - array("testplan" => "exclude_me","requirement_spec" => "exclude_me")); - $object_info = $treeMgr->get_node_hierarchy_info($oid); - $object_name = $object_info['name']; - - - if (!sizeof($children)) - { - $children = null; - } - - $smartyObj->assign('arraySelect', $children); - $smartyObj->assign('objectID', $oid); - $smartyObj->assign('object_name', $object_name); - - if($oid == $argsObj->tprojectID) - { - $level = 'testproject'; - $smartyObj->assign('level', $level); - $smartyObj->assign('page_title',lang_get('container_title_' . $level)); - } - - return $level; -} - - -/* - function: updateTestSuite - -args: - -returns: - -*/ -function updateTestSuite(&$tsuiteMgr,&$argsObj,$container,&$hash) { - $msg = 'ok'; - $ret = $tsuiteMgr->update($argsObj->testsuiteID,$container['container_name'],$container['details']); - if($ret['status_ok']) { - $tsuiteMgr->deleteKeywords($argsObj->testsuiteID); - if(trim($argsObj->assigned_keyword_list) != "") { - $tsuiteMgr->addKeywords($argsObj->testsuiteID,explode(",",$argsObj->assigned_keyword_list)); - } - writeCustomFieldsToDB($tsuiteMgr->db,$argsObj->tprojectID,$argsObj->testsuiteID,$hash); - - /* Send events to plugins */ - $ctx = array('id' => $argsObj->testsuiteID,'name' => $container['container_name'],'details' => $container['details']); - event_signal('EVENT_TEST_SUITE_UPDATE', $ctx); - } - else - { - $msg = $ret['msg']; - } - return $msg; -} - -/* - function: copyTestSuite - -args: - -returns: - -*/ -function copyTestSuite(&$smartyObj,$template_dir,&$tsuiteMgr,$argsObj) { - $guiObj = new stdClass(); - $guiObj->btn_reorder_testcases = $argsObj->l10n['btn_reorder_testcases'];; - - $exclude_node_types=array('testplan' => 1, 'requirement' => 1, 'requirement_spec' => 1); - - $options = array(); - $options['check_duplicate_name'] = config_get('check_names_for_duplicates'); - $options['action_on_duplicate_name'] = config_get('action_on_duplicate_name'); - $options['copyKeywords'] = $argsObj->copyKeywords; - $options['copyRequirements'] = $argsObj->copyRequirementAssignments; - - - // copy_to($source,$destination,...) - $op = $tsuiteMgr->copy_to($argsObj->objectID, - $argsObj->containerID, $argsObj->userID,$options); - - if( $op['status_ok'] ) { - $tsuiteMgr->tree_manager->change_child_order($argsObj->containerID,$op['id'], - $argsObj->target_position,$exclude_node_types); - - - // get info to provide feedback - $dummy = $tsuiteMgr->tree_manager->get_node_hierarchy_info(array($argsObj->objectID, $argsObj->containerID)); - - $msgk = $op['name_changed'] ? 'tsuite_copied_ok_name_changed' : 'tsuite_copied_ok'; - $guiObj->user_feedback = sprintf(lang_get($msgk),$dummy[$argsObj->objectID]['name'], - $dummy[$argsObj->containerID]['name'],$op['name']); - } - $guiObj->refreshTree = $op['status_ok'] && $argsObj->refreshTree; - $guiObj->attachments = getAttachmentInfosFrom($tsuiteMgr,$argsObj->objectID); - $guiObj->id = $argsObj->objectID; - $guiObj->treeFormToken = $guiObj->form_token = $argsObj->treeFormToken; - - $guiObj->direct_link = $tsuiteMgr->buildDirectWebLink($_SESSION['basehref'], - $guiObj->id,$argsObj->tprojectID); - - $tsuiteMgr->show($smartyObj,$guiObj,$template_dir,$argsObj->objectID,null,'ok'); - - return $op['status_ok']; -} - -/* - function: moveTestSuite - -args: - -returns: - -*/ -function moveTestSuite(&$smartyObj,$template_dir,&$tprojectMgr,$argsObj) -{ - $exclude_node_types=array('testplan' => 1, 'requirement' => 1, 'requirement_spec' => 1); - - $tprojectMgr->tree_manager->change_parent($argsObj->objectID,$argsObj->containerID); - $tprojectMgr->tree_manager->change_child_order($argsObj->containerID,$argsObj->objectID, - $argsObj->target_position,$exclude_node_types); - - $guiObj = new stdClass(); - $guiObj->id = $argsObj->tprojectID; - $guiObj->refreshTree = $argsObj->refreshTree; - - $tprojectMgr->show($smartyObj,$guiObj,$template_dir,$argsObj->tprojectID,null,'ok'); -} - - -/* - function: initializeOptionTransfer - -args: - -returns: option transfer configuration - -*/ -function initializeOptionTransfer(&$tprojectMgr,&$tsuiteMgr,$argsObj,$doAction) { - $opt_cfg = opt_transf_empty_cfg(); - $opt_cfg->js_ot_name='ot'; - $opt_cfg->global_lbl=''; - $opt_cfg->from->lbl=lang_get('available_kword'); - $opt_cfg->from->map = $tprojectMgr->get_keywords_map($argsObj->tprojectID); - $opt_cfg->to->lbl=lang_get('assigned_kword'); - - if($doAction=='edit_testsuite') { - $opt_cfg->to->map = $tsuiteMgr->get_keywords_map($argsObj->testsuiteID," ORDER BY keyword ASC "); - } - return $opt_cfg; -} - - -/* - function: moveTestCasesViewer -prepares smarty variables to display move testcases viewer - -args: - -returns: - - -*/ -function moveTestCasesViewer(&$dbHandler,&$smartyObj,&$tprojectMgr,&$treeMgr, - $argsObj,$feedback='',$cf=null) -{ - $tables = $tprojectMgr->getDBTables(array('nodes_hierarchy','node_types','tcversions')); - $testcase_cfg = config_get('testcase_cfg'); - $glue = $testcase_cfg->glue_character; - - $containerID = isset($argsObj->testsuiteID) ? $argsObj->testsuiteID : $argsObj->objectID; - $containerName = $argsObj->tsuite_name; - if( is_null($containerName) ) { - $dummy = $treeMgr->get_node_hierarchy_info($argsObj->objectID); - $containerName = $dummy['name']; - } - - // I'have discovered that exclude selected testsuite branch is not good - // when you want to move lots of testcases from one testsuite to it's children - // testsuites. (in this situation tree drag & drop is not ergonomic). - $testsuites = $tprojectMgr->gen_combo_test_suites($argsObj->tprojectID); - $tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tprojectID) . $glue; - - // While testing with PostGres have found this behaivour: - // No matter is UPPER CASE has used on field aliases, keys on hash returned by - // ADODB are lower case. - // Accessing this keys on Smarty template using UPPER CASE fails. - // Solution: have changed case on Smarty to lower case. - // - $sqlA = " SELECT MAX(TCV.version) AS lvnum, NHTC.node_order, NHTC.name," . - " NHTC.id, TCV.tc_external_id AS tcexternalid" . - " FROM {$tables['nodes_hierarchy']} NHTC " . - " JOIN {$tables['nodes_hierarchy']} NHTCV " . - " ON NHTCV.parent_id = NHTC.id " . - " JOIN {$tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - " JOIN {$tables['node_types']} NT " . - " ON NT.id = NHTC.node_type_id AND NT.description='testcase'" . - " WHERE NHTC.parent_id = " . intval($containerID); - - if( !is_null($argsObj->testCaseSet) ) { - $sqlA .= " AND NHTC.id IN (" . implode(',', $argsObj->testCaseSet). ")"; - } - - $sqlA .=" GROUP BY NHTC.id,TCV.tc_external_id,NHTC.name,NHTC.node_order "; - - $sqlB = " SELECT SQLA.id AS tcid, SQLA.name AS tcname," . - " SQLA.node_order AS tcorder, SQLA.tcexternalid," . - " MTCV.summary,MTCV.status,MTCV.importance,MTCV.execution_type," . - " MTCV.id AS tcversion_id FROM ($sqlA) SQLA " . - " JOIN {$tables['nodes_hierarchy']} MNHTCV ON MNHTCV.parent_id = SQLA.id " . - " JOIN {$tables['tcversions']} MTCV ON MTCV.id = MNHTCV.id AND MTCV.version = SQLA.lvnum"; - $orderClause = " ORDER BY TCORDER,TCNAME"; - - $children = $dbHandler->get_recordset($sqlB . $orderClause); - - // check if operation can be done - $user_feedback = $feedback; - if(!is_null($children) && (sizeof($children) > 0) && sizeof($testsuites)) { - $op_ok = true; - } else { - $children = null; - $op_ok = false; - $user_feedback = lang_get('no_testcases_available_or_tsuite'); - } - - $gui = new stdClass(); - $gui->treeFormToken = $gui->form_token = $argsObj->treeFormToken; - - $dummy = getConfigAndLabels('testCaseStatus','code'); - $gui->domainTCStatus = array(-1 => '') + $dummy['lbl']; - $gui->domainTCImportance = array(-1 => '', - HIGH => lang_get('high_importance'), - MEDIUM => lang_get('medium_importance'), - LOW => lang_get('low_importance')); - - $dummy = getConfigAndLabels('execution_type','code'); - $gui->domainTCExecType = array(-1 => '') + $dummy['lbl']; - - $gui->testCasesTableView = 0; - if(($argsObj->action == 'testcases_table_view') || - ($argsObj->action == 'doBulkSet')) { - $gui->testCasesTableView = 1; - } - $gui->cf = $cf; - - $gui->tcprefix = $tcasePrefix; - $gui->testcases = $children; - - $smartyObj->assign('gui', $gui); - $smartyObj->assign('op_ok', $op_ok); - $smartyObj->assign('user_feedback', $user_feedback); - - //check if is needed - $smartyObj->assign('old_containerID', $argsObj->tprojectID); - $smartyObj->assign('containers', $testsuites); - $smartyObj->assign('objectID', $containerID); - $smartyObj->assign('object_name', $containerName); - $smartyObj->assign('top_checked','checked=checked'); - $smartyObj->assign('bottom_checked',''); -} - - -/* - function: copyTestCases -copy a set of choosen test cases. - -args: - -returns: - - -*/ -function copyTestCases(&$smartyObj,$template_dir,&$tsuiteMgr,&$tcaseMgr,$argsObj) -{ - $op = array('refreshTree' => false, 'userfeedback' => ''); - if( ($qty=sizeof($argsObj->tcaseSet)) > 0) - { - $msg_id = $qty == 1 ? 'one_testcase_copied' : 'testcase_set_copied'; - $op['userfeedback'] = sprintf(lang_get($msg_id),$qty); - - $copyOpt = array( - 'check_duplicate_name' => config_get('check_names_for_duplicates'), - 'action_on_duplicate_name' => config_get('action_on_duplicate_name'), - 'stepAsGhost' => $argsObj->stepAsGhost); - $copyOpt['copy_also'] = - array('keyword_assignments' => $argsObj->copyKeywords, - 'requirement_assignments' => - $argsObj->copyRequirementAssignments); - - $copy_op =array(); - foreach($argsObj->tcaseSet as $key => $tcaseid) { - $copy_op[$tcaseid] = $tcaseMgr->copy_to($tcaseid, $argsObj->containerID, $argsObj->userID, $copyOpt); - } - $guiObj = new stdClass(); - $guiObj->attachments = getAttachmentInfosFrom($tsuiteMgr,$argsObj->objectID); - $guiObj->id = $argsObj->objectID; - $guiObj->refreshTree = true; - $op['refreshTree'] = true; - } - return $op; -} - - -/* - function: moveTestCases -move a set of choosen test cases. - -args: - -returns: - - -*/ -function moveTestCases(&$smartyObj,$template_dir,&$tsuiteMgr,&$treeMgr,$argsObj) -{ - $lbl = $argsObj->l10n; - if (sizeof($argsObj->tcaseSet) > 0) { - $status_ok = $treeMgr->change_parent($argsObj->tcaseSet,$argsObj->containerID); - $user_feedback= $status_ok ? '' : lang_get('move_testcases_failed'); - - // objectID - original container - $guiObj = new stdClass(); - $guiObj->attachments = getAttachmentInfosFrom($tsuiteMgr,$argsObj->objectID); - $guiObj->id = $argsObj->objectID; - $guiObj->refreshTree = true; - $guiObj->btn_reorder_testcases = $lbl['btn_reorder_testcases']; - - $tsuiteMgr->show($smartyObj,$guiObj,$template_dir,$argsObj->objectID,null,$user_feedback); - } -} - - -/** - * initWebEditors - * - */ -function initWebEditors($action,$itemType,$editorCfg) { - $webEditorKeys = array('testsuite' => array('details')); - $itemTemplateKey=null; - - switch($action) { - case 'new_testsuite': - case 'add_testsuite': - case 'edit_testsuite': - $accessKey = 'testsuite'; - break; - - default: - $accessKey = ''; - break; - } - - - switch($itemType) { - case 'testproject': - case 'testsuite': - $itemTemplateKey = 'testsuite_template'; - $accessKey = 'testsuite'; - break; - } - - $oWebEditor = array(); - $htmlNames = ''; - if( isset($webEditorKeys[$accessKey]) ) { - $htmlNames = $webEditorKeys[$accessKey]; - foreach ($htmlNames as $key) { - $oWebEditor[$key] = web_editor($key,$_SESSION['basehref'],$editorCfg); - } - } - return array($oWebEditor,$htmlNames,$itemTemplateKey); -} - - - - -/* - function: deleteTestCasesViewer -prepares smarty variables to display move testcases viewer - -args: - -returns: - - -@internal revisions -20110402 - franciscom - BUGID 4322: New Option to block delete of executed test cases. -*/ -function deleteTestCasesViewer(&$dbHandler,&$smartyObj,&$tprojectMgr,&$treeMgr,&$tsuiteMgr, - &$tcaseMgr,$argsObj,$feedback = null) -{ - - $guiObj = new stdClass(); - $guiObj->main_descr = lang_get('delete_testcases'); - $guiObj->system_message = ''; - - - $tables = $tprojectMgr->getDBTables(array('nodes_hierarchy','node_types','tcversions')); - $testcase_cfg = config_get('testcase_cfg'); - $glue = $testcase_cfg->glue_character; - - $containerID = isset($argsObj->testsuiteID) ? $argsObj->testsuiteID : $argsObj->objectID; - $containerName = $argsObj->tsuite_name; - if( is_null($containerName) ) - { - $dummy = $treeMgr->get_node_hierarchy_info($argsObj->objectID); - $containerName = $dummy['name']; - } - - $guiObj->testCaseSet = $tsuiteMgr->get_children_testcases($containerID); - $guiObj->exec_status_quo = null; - $tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tprojectID); - $hasExecutedTC = false; - - if( !is_null($guiObj->testCaseSet) && count($guiObj->testCaseSet) > 0) - { - foreach($guiObj->testCaseSet as &$child) - { - $external = $tcaseMgr->getExternalID($child['id'],null,$tcasePrefix); - $child['external_id'] = $external[0]; - - // key level 1 : Test Case Version ID - // key level 2 : Test Plan ID - // key level 3 : Platform ID - $getOptions = array('addExecIndicator' => true); - $dummy = $tcaseMgr->get_exec_status($child['id'],null,$getOptions); - $child['draw_check'] = $argsObj->grants->delete_executed_testcases || (!$dummy['executed']); - - $hasExecutedTC = $hasExecutedTC || $dummy['executed']; - unset($dummy['executed']); - $guiObj->exec_status_quo[] = $dummy; - } - } - // Need to understand if platform column has to be displayed on GUI - if( !is_null($guiObj->exec_status_quo) ) - { - // key level 1 : Test Case Version ID - // key level 2 : Test Plan ID - // key level 3 : Platform ID - - $itemSet = array_keys($guiObj->exec_status_quo); - foreach($itemSet as $mainKey) - { - $guiObj->display_platform[$mainKey] = false; - if(!is_null($guiObj->exec_status_quo[$mainKey]) ) - { - $versionSet = array_keys($guiObj->exec_status_quo[$mainKey]); - $stop = false; - foreach($versionSet as $version_id) - { - $tplanSet = array_keys($guiObj->exec_status_quo[$mainKey][$version_id]); - foreach($tplanSet as $tplan_id) - { - if( ($guiObj->display_platform[$mainKey] = !isset($guiObj->exec_status_quo[$mainKey][$version_id][$tplan_id][0])) ) - { - $stop = true; - break; - } - } - - if($stop) - { - break; - } - } - } - } - } - // check if operation can be done - $guiObj->user_feedback = $feedback; - if(!is_null($guiObj->testCaseSet) && (sizeof($guiObj->testCaseSet) > 0) ) - { - $guiObj->op_ok = true; - $guiObj->user_feedback = ''; - } - else - { - $guiObj->children = null; - $guiObj->op_ok = false; - $guiObj->user_feedback = is_null($guiObj->user_feedback) ? lang_get('no_testcases_available') : $guiObj->user_feedback; - } - - if (!$argsObj->grants->delete_executed_testcases && $hasExecutedTC) { - $guiObj->system_message = lang_get('system_blocks_delete_executed_tc'); - } - - $guiObj->objectID = $containerID; - $guiObj->object_name = $containerName; - $guiObj->refreshTree = $argsObj->refreshTree; - - $smartyObj->assign('gui', $guiObj); -} - - -/* - function: doDeleteTestCasesViewer -prepares smarty variables to display move testcases viewer - -args: - -returns: - - -*/ -function doDeleteTestCases(&$dbHandler,$tcaseSet,&$tcaseMgr) -{ - if( count($tcaseSet) > 0 ) - { - foreach($tcaseSet as $victim) - { - $tcaseMgr->delete($victim); - } - } -} - - -/** - * - * - */ -function reorderTestCasesByCriteria($argsObj,&$tsuiteMgr,&$treeMgr) { - $sortCriteria = config_get('testcase_reorder_by'); - $pfn = ($sortCriteria == 'NAME') ? 'reorderTestCasesDictionary' : 'reorderTestCasesByExtID'; - $pfn($argsObj,$tsuiteMgr,$treeMgr); -} - - -/** - * - * - */ -function reorderTestCasesDictionary($argsObj,&$tsuiteMgr,&$treeMgr) -{ - $tcaseSet = (array)$tsuiteMgr->get_children_testcases($argsObj->testsuiteID,'simple'); - if( ($loop2do = count($tcaseSet)) > 0 ) - { - for($idx=0; $idx < $loop2do; $idx++) - { - $a2sort[$tcaseSet[$idx]['id']] = strtolower($tcaseSet[$idx]['name']); - } - natsort($a2sort); - $a2sort = array_keys($a2sort); - $treeMgr->change_order_bulk($a2sort); - } -} - - -/** - * - * - */ -function reorderTestCasesByExtID($argsObj,&$tsuiteMgr,&$treeMgr) -{ - $tables = $tsuiteMgr->getDBTables(array('nodes_hierarchy','testsuites','tcversions')); - - $sql = " SELECT DISTINCT NHTC.id,TCV.tc_external_id " . - " FROM {$tables['nodes_hierarchy']} NHTC " . - " JOIN {$tables['nodes_hierarchy']} NHTCV ON NHTCV.parent_id = NHTC.id " . - " JOIN {$tables['tcversions']} TCV ON TCV.id = NHTCV.id " . - " JOIN {$tables['testsuites']} TS ON NHTC.parent_id = TS.id " . - " WHERE TS.id = " . intval($argsObj->testsuiteID) . - " ORDER BY tc_external_id ASC"; - - $tcaseSet = $tsuiteMgr->db->fetchColumnsIntoMap($sql,'tc_external_id','id'); - $treeMgr->change_order_bulk($tcaseSet); -} - - - -/** - * - * - */ -function reorderTestSuitesDictionary($args,$treeMgr,$parent_id) -{ - $exclude_node_types = array('testplan' => 1, 'requirement' => 1, 'testcase' => 1, 'requirement_spec' => 1); - $itemSet = (array)$treeMgr->get_children($parent_id,$exclude_node_types); - if( ($loop2do = count($itemSet)) > 0 ) - { - for($idx=0; $idx < $loop2do; $idx++) - { - $a2sort[$itemSet[$idx]['id']] = strtolower($itemSet[$idx]['name']); - } - natsort($a2sort); - $a2sort = array_keys($a2sort); - $treeMgr->change_order_bulk($a2sort); - } -} - -/** - * - */ -function initializeGui(&$objMgr,$id,$argsObj,$lbl=null) { - $guiObj = new stdClass(); - - $labels = $lbl; - if( null == $labels ) { - $labels = $argsObj->l10n; - } - - $guiObj->id = $id; - $guiObj->user = $argsObj->user; - $guiObj->tproject_id = $argsObj->tprojectID; - $guiObj->refreshTree = $argsObj->refreshTree; - $guiObj->btn_reorder_testcases = $labels['btn_reorder_testcases']; - $guiObj->page_title = $labels['container_title_testsuite']; - $guiObj->attachments = getAttachmentInfosFrom($objMgr,$id); - $guiObj->form_token = $argsObj->treeFormToken; - - $guiObj->fileUploadURL = $_SESSION['basehref'] . $objMgr->getFileUploadRelativeURL($id); - - if( $objMgr->my_node_type == $objMgr->node_types_descr_id['testsuite'] ) { - $guiObj->direct_link = - $objMgr->buildDirectWebLink($_SESSION['basehref'], - $guiObj->id,$argsObj->tprojectID); - } - - - $guiObj->modify_tc_rights = $argsObj->grants->testcase_mgmt; - return $guiObj; -} - -/** - * - * - */ -function doBulkSet(&$dbHandler,$argsObj,$tcaseSet,&$tcaseMgr) -{ - if( count($tcaseSet) > 0 ) - { - $k2s = array('tc_status' => 'setStatus', - 'importance' => 'setImportance', - 'execution_type' => 'setExecutionType'); - foreach($tcaseSet as $tcversion_id => $tcase_id) - { - foreach($k2s as $attr => $m2c) - { - if($argsObj->$attr >0) - { - $tcaseMgr->$m2c($tcversion_id,$argsObj->$attr); - } - } - - /* - if($argsObj->tc_status >0) - { - $tcaseMgr->setStatus($tcversion_id,$argsObj->tc_status); - } - - if($argsObj->importance >0) - { - $tcaseMgr->setImportance($tcversion_id,$argsObj->importance); - } - - if($argsObj->execution_type >0) - { - $tcaseMgr->setStatus($tcversion_id,$argsObj->tc_status); - } - */ - } - - // second round, on Custom Fields - $cf_map = $tcaseMgr->cfield_mgr->get_linked_cfields_at_design($argsObj->tprojectID,ENABLED, - NO_FILTER_SHOW_ON_EXEC,'testcase'); - if( !is_null($cf_map) ) - { - // get checkboxes from $_REQUEST - $k2i = array_keys($_REQUEST); - $cfval = null; - foreach($k2i as $val) - { - if(strpos($val,'check_custom_field_') !== FALSE) - { - $cfid = explode('_',$val); - $cfid = end($cfid); - $cfval[$cfid] = $cf_map[$cfid]; - } - } - if(!is_null($cfval)) - { - foreach($tcaseSet as $tcversion_id => $tcase_id) - { - $tcaseMgr->cfield_mgr->design_values_to_db($_REQUEST,$tcversion_id,$cfval); - } - } - } - - } -} - - -/** - * - */ -function initTPLActions() { - - $tpl = array( 'move_testsuite_viewer' => 'containerMove.tpl', - 'delete_testsuite' => 'containerDelete.tpl', - 'move_testcases_viewer' => 'containerMoveTC.tpl', - 'testcases_table_view' => 'containerMoveTC.tpl', - 'do_copy_tcase_set' => 'containerMoveTC.tpl', - 'do_copy_tcase_set_ghost' => 'containerMoveTC.tpl', - 'delete_testcases' => 'containerDeleteTC.tpl', - 'do_delete_testcases' => 'containerDeleteTC.tpl', - 'doBulkSet' => 'containerMoveTC.tpl'); - - $actions = array('edit_testsuite' => 0,'new_testsuite' => 0, - 'delete_testsuite' => 0, - 'do_move' => 0,'do_copy' => 0,'reorder_testsuites' => 1, - 'do_testsuite_reorder' => 0,'add_testsuite' => 1, - 'move_testsuite_viewer' => 0,'update_testsuite' => 1, - 'move_testcases_viewer' => 0,'do_move_tcase_set' => 0, - 'testcases_table_view' => 0,'do_copy_tcase_set' => 0, - 'do_copy_tcase_set_ghost' => 0, 'del_testsuites_bulk' => 0, - 'delete_testcases' => 0,'do_delete_testcases' => 0, - 'reorder_testcases' => 0,'reorder_testsuites_alpha' => 0, - 'reorder_testproject_testsuites_alpha' => 0, - 'doBulkSet' => 0); - - return array($tpl,$actions); -} - -/** - * - */ -function showTestSuite(&$tplEngine,&$argsO,&$tsuiteMgr,$tplDir) { - if( null == $argsO->tprojectID ) { - $argsO->tprojectID = $tsuiteMgr->getTestproject($argsO->item_id); - } - $gui = initializeGui($tsuiteMgr,$argsO->item_id,$argsO,$argsO->l10n); - $gui->refreshTree = 0; - $tsuiteMgr->show($tplEngine,$gui,$tplDir,$argsO->item_id,null,null); - exit(); +js_ot_name = 'ot'; + +$args = initArgs($db, $tproject_mgr, $tsuite_mgr, $opt_cfg); +$level = isset($args->level) ? $args->level : $args->containerType; + +$gui_cfg = config_get('gui'); +$smarty = new TLSmarty(); +$smarty->assign('editorType', $editorCfg['type']); + +list ($a_tpl, $a_actions) = initTPLActions(); + +$a_init_opt_transfer = array( + 'edit_testsuite' => 1, + 'new_testsuite' => 1, + 'add_testsuite' => 1, + 'update_testsuite' => 1 +); + +$the_tpl = null; +$action = null; +$get_c_data = null; +$init_opt_transfer = null; + +// 20121222 -franciscom +// Need this trick because current implementation of Ext.ux.requireSessionAndSubmit() +// discards the original submit button +if (isset($_REQUEST['doAction'])) { + $_POST[$_REQUEST['doAction']] = $_REQUEST['doAction']; +} +$action = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; + +foreach ($a_actions as $the_key => $the_val) { + if (isset($_POST[$the_key])) { + $the_tpl = isset($a_tpl[$the_key]) ? $a_tpl[$the_key] : null; + $init_opt_transfer = isset($a_init_opt_transfer[$the_key]) ? 1 : 0; + + $action = $the_key; + $get_c_data = $the_val; + $level = is_null($level) ? 'testsuite' : $level; + break; + } +} +$args->action = $action; + +$smarty->assign('level', $level); +$smarty->assign('page_title', lang_get('container_title_' . $level)); + +if ($init_opt_transfer) { + $opt_cfg = initializeOptionTransfer($tproject_mgr, $tsuite_mgr, $args, + $action); +} + +// create web editor objects +list ($oWebEditor, $webEditorHtmlNames, $webEditorTemplateKey) = initWebEditors( + $action, $level, $editorCfg); +if ($get_c_data) { + $name_ok = 1; + $c_data = getValuesFromPost($webEditorHtmlNames); + if ($name_ok && ! checkString($c_data['container_name'], $g_ereg_forbidden)) { + $msg = $args->l10n['string_contains_bad_chars']; + $name_ok = 0; + } + + if ($name_ok && ($c_data['container_name'] == "")) { + $msg = $args->l10n['warning_empty_testsuite_name']; + $name_ok = 0; + } +} + +$doIt = true; +switch ($action) { + case 'fileUpload': + case 'deleteFile': + case 'edit_testsuite': + case 'new_testsuite': + case 'delete_testsuite': + case 'move_testsuite_viewer': + case 'reorder_testsuites': + case 'update_testsuite': + case 'do_move': + case 'do_copy': + case 'do_move_tcase_set': + case 'do_copy_tcase_set': + case 'do_copy_tcase_set_ghost': + case 'add_testsuite': + case 'delete_testcases': + case 'do_delete_testcases': + case 'reorder_testcases': + case 'reorder_testsuites_alpha': + case 'reorder_testproject_testsuites_alpha': + case 'doBulkSet': + case 'addKeyword': + case 'addKeywordTSDeep': + case 'removeKeyword': + $doIt = ('yes' == $args->grants->testcase_mgmt); + break; +} + +if ($doIt) { + switch ($action) { + case 'fileUpload': + switch ($level) { + case 'testsuite': + $uploadOp = fileUploadManagement($db, $args->testsuiteID, + $args->fileTitle, $tsuite_mgr->getAttachmentTableName()); + $gui = initializeGui($tsuite_mgr, $args->testsuiteID, $args); + $gui->refreshTree = 0; + $gui->uploadOp = $uploadOp; + $tsuite_mgr->show($smarty, $gui, $template_dir, + $args->testsuiteID, null, null); + break; + + case 'testproject': + $uploadOp = fileUploadManagement($db, $args->tprojectID, + $args->fileTitle, + $tproject_mgr->getAttachmentTableName()); + $gui = initializeGui($tproject_mgr, $args->tprojectID, $args); + $gui->refreshTree = 0; + $gui->uploadOp = $uploadOp; + + $tproject_mgr->show($smarty, $gui, $template_dir, + $args->tprojectID, null, null); + break; + } + break; + + case 'deleteFile': + deleteAttachment($db, $args->file_id); + switch ($level) { + case 'testsuite': + $gui = initializeGui($tsuite_mgr, $args->testsuiteID, $args); + $gui->refreshTree = 0; + $tsuite_mgr->show($smarty, $gui, $template_dir, + $args->testsuiteID, null, null); + break; + + case 'testproject': + $gui = initializeGui($tproject_mgr, $args->tprojectID, $args); + $gui->refreshTree = 0; + $tproject_mgr->show($smarty, $gui, $template_dir, + $args->tprojectID, null, null); + break; + } + break; + + case 'edit_testsuite': + case 'new_testsuite': + keywords_opt_transf_cfg($opt_cfg, $args->assigned_keyword_list); + + $smarty->assign('opt_cfg', $opt_cfg); + + $gui = new stdClass(); + $gui->tproject_id = $args->tprojectID; + $gui->containerType = $level; + $gui->refreshTree = $args->refreshTree; + $gui->hasKeywords = (count($opt_cfg->from->map) > 0) || + (count($opt_cfg->to->map) > 0); + + $gui->cancelActionJS = 'location.href=fRoot+' . + "'lib/testcases/archiveData.php?id=" . + intval($args->containerID); + switch ($level) { + case 'testproject': + $gui->cancelActionJS .= "&edit=testproject&level=testproject'"; + break; + + case 'testsuite': + $gui->cancelActionJS .= "&edit=testsuite&level=testsuite&containerType=testsuite'"; + break; + } + + $smarty->assign('level', $level); + $smarty->assign('gui', $gui); + $tsuite_mgr->viewer_edit_new($smarty, $template_dir, + $webEditorHtmlNames, $oWebEditor, $action, $args->containerID, + $args->testsuiteID, null, $webEditorTemplateKey); + break; + + case 'delete_testsuite': + $refreshTree = deleteTestSuite($smarty, $args, $tsuite_mgr, + $tree_mgr, $tcaseMgr, $level); + break; + + case 'move_testsuite_viewer': + moveTestSuiteViewer($smarty, $tproject_mgr, $args); + break; + + case 'move_testcases_viewer': + moveTestCasesViewer($db, $smarty, $tproject_mgr, $tree_mgr, $args); + break; + + case 'testcases_table_view': + $cf = null; + $cf_map = $tcaseMgr->get_linked_cfields_at_design(0, null, null, + null, $args->tprojectID); + if (! is_null($cf_map)) { + $cfOpt = array( + 'addCheck' => true, + 'forceOptional' => true + ); + $cf = $tcaseMgr->cfield_mgr->html_table_inputs($cf_map, '', null, + $cfOpt); + } + + moveTestCasesViewer($db, $smarty, $tproject_mgr, $tree_mgr, $args, + null, $cf); + break; + + case 'reorder_testsuites': + $ret = reorderTestSuiteViewer($smarty, $tree_mgr, $args); + $level = is_null($ret) ? $level : $ret; + break; + + case 'do_move': + moveTestSuite($smarty, $template_dir, $tproject_mgr, $args); + break; + + case 'do_copy': + copyTestSuite($smarty, $template_dir, $tsuite_mgr, $args); + break; + + case 'update_testsuite': + if ($name_ok) { + $msg = updateTestSuite($tsuite_mgr, $args, $c_data, $_REQUEST); + } + $gui = initializeGui($tsuite_mgr, $args->testsuiteID, $args); + $tsuite_mgr->show($smarty, $gui, $template_dir, $args->testsuiteID, + null, $msg); + break; + + case 'add_testsuite': + $messages = null; + $op['status'] = 0; + if ($name_ok) { + $op = addTestSuite($tsuite_mgr, $args, $c_data, $_REQUEST); + $messages = array( + 'result_msg' => $op['messages']['msg'], + 'user_feedback' => $op['messages']['user_feedback'] + ); + } + + // $userInput is used to maintain data filled by user if there is + // a problem with test suite name. + $userInput = $op['status'] ? null : $_REQUEST; + $assignedKeywords = $op['status'] ? "" : $args->assigned_keyword_list; + keywords_opt_transf_cfg($opt_cfg, $assignedKeywords); + $smarty->assign('opt_cfg', $opt_cfg); + + $gui = new stdClass(); + $gui->tproject_id = $args->tprojectID; + $gui->containerType = $level; + $gui->refreshTree = $args->refreshTree; + $gui->cancelActionJS = 'location.href=fRoot+' . + "'lib/testcases/archiveData.php?id=" . + intval($args->containerID); + + switch ($level) { + case 'testproject': + $gui->cancelActionJS .= "&edit=testproject&level=testproject'"; + break; + + case 'testsuite': + $gui->cancelActionJS .= "&edit=testsuite&level=testsuite&containerType=testsuite'"; + break; + } + + $smarty->assign('level', $level); + $smarty->assign('gui', $gui); + + $tsuite_mgr->viewer_edit_new($smarty, $template_dir, + $webEditorHtmlNames, $oWebEditor, $action, $args->containerID, + null, $messages, $webEditorTemplateKey, $userInput); + break; + + case 'do_move_tcase_set': + moveTestCases($smarty, $template_dir, $tsuite_mgr, $tree_mgr, $args); + break; + + case 'do_copy_tcase_set': + case 'do_copy_tcase_set_ghost': + $args->stepAsGhost = ($action == 'do_copy_tcase_set_ghost'); + $op = copyTestCases($smarty, $template_dir, $tsuite_mgr, $tcaseMgr, + $args); + + $refreshTree = $op['refreshTree']; + moveTestCasesViewer($db, $smarty, $tproject_mgr, $tree_mgr, $args, + $op['userfeedback']); + break; + + case 'delete_testcases': + $args->refreshTree = false; + deleteTestCasesViewer($db, $smarty, $tproject_mgr, $tree_mgr, + $tsuite_mgr, $tcaseMgr, $args); + break; + + case 'do_delete_testcases': + $args->refreshTree = true; + doDeleteTestCases($db, $args->tcaseSet, $tcaseMgr); + deleteTestCasesViewer($db, $smarty, $tproject_mgr, $tree_mgr, + $tsuite_mgr, $tcaseMgr, $args, + lang_get('all_testcases_have_been_deleted')); + break; + + case 'reorder_testcases': + reorderTestCasesByCriteria($args, $tsuite_mgr, $tree_mgr); + $gui = initializeGui($tsuite_mgr, $args->testsuiteID, $args); + $gui->refreshTree = true; + $tsuite_mgr->show($smarty, $gui, $template_dir, $args->testsuiteID, + null, null); + break; + + case 'reorder_testsuites_alpha': + reorderTestSuitesDictionary($tree_mgr, $args->testsuiteID); + $gui = initializeGui($tsuite_mgr, $args->testsuiteID, $args); + $gui->refreshTree = true; + $tsuite_mgr->show($smarty, $gui, $template_dir, $args->testsuiteID, + null, null); + break; + + case 'reorder_testproject_testsuites_alpha': + reorderTestSuitesDictionary($tree_mgr, $args->tprojectID); + $gui = initializeGui($tproject_mgr, $args->tprojectID, $args); + $gui->refreshTree = true; + $tproject_mgr->show($smarty, $gui, $template_dir, $args->tprojectID, + null, null); + break; + + case 'doBulkSet': + $args->refreshTree = true; + doBulkSet($db, $args, $args->tcaseSet, $tcaseMgr); + + $cf = null; + $cf_map = $tcaseMgr->get_linked_cfields_at_design(0, null, null, + null, $args->tprojectID); + if (! is_null($cf_map)) { + $cfOpt = array( + 'addCheck' => true, + 'forceOptional' => true + ); + $cf = $tcaseMgr->cfield_mgr->html_table_inputs($cf_map, '', null, + $cfOpt); + } + + moveTestCasesViewer($db, $smarty, $tproject_mgr, $tree_mgr, $args, + null, $cf); + break; + + case 'addKeyword': + $tsuite_mgr->addKeywords($args->item_id, $args->free_keywords); + showTestSuite($smarty, $args, $tsuite_mgr, $template_dir); + exit(); + break; + + case 'addKeywordTSDeep': + $tsuite_mgr->addKeywordsDeep($args->item_id, $args->free_keywords); + showTestSuite($smarty, $args, $tsuite_mgr, $template_dir); + exit(); + break; + + case 'removeKeyword': + $tsuite_mgr->deleteKeywordByLinkID($args->kw_link_id); + showTestSuite($smarty, $args, $tsuite_mgr, $template_dir); + exit(); + break; + + default: + trigger_error("containerEdit.php - No correct GET/POST data", + E_USER_ERROR); + break; + } +} + +if ($the_tpl) { + $smarty->assign('refreshTree', $refreshTree && $args->refreshTree); + $smarty->display($template_dir . $the_tpl); +} + +/** + */ +function getValuesFromPost($akeys2get) +{ + $akeys2get[] = 'container_name'; + $c_data = array(); + foreach ($akeys2get as $key) { + $c_data[$key] = isset($_POST[$key]) ? strings_stripSlashes($_POST[$key]) : null; + } + return $c_data; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function build_del_testsuite_warning_msg(&$tree_mgr, &$tcaseMgr, &$testcases, + $tsuite_id) +{ + $msg = null; + $msg['warning'] = null; + $msg['link_msg'] = null; + $msg['delete_msg'] = null; + + if (! is_null($testcases)) { + $show_warning = 0; + $verbose = array(); + $msg['link_msg'] = array(); + + $status_warning = array( + 'linked_and_executed' => 1, + 'linked_but_not_executed' => 1, + 'no_links' => 0 + ); + + $delete_notice = array( + 'linked_and_executed' => lang_get('delete_notice'), + 'linked_but_not_executed' => '', + 'no_links' => '' + ); + + $getOptions = array( + 'addExecIndicator' => true + ); + foreach ($testcases as $elem) { + $verbose[] = $tree_mgr->get_path($elem['id'], $tsuite_id); + $xx = $tcaseMgr->get_exec_status($elem['id'], null, $getOptions); + $status = 'no_links'; + if (! is_null($xx)) { + $status = $xx['executed'] ? 'linked_and_executed' : 'linked_but_not_executed'; + } + $msg['link_msg'][] = $status; + + if ($status_warning[$status]) { + $show_warning = 1; + $msg['delete_msg'] = $delete_notice[$status]; + } + } + + $idx = 0; + if ($show_warning) { + $msg['warning'] = array(); + foreach ($verbose as $elem) { + $msg['warning'][$idx] = ''; + $bSlash = false; + foreach ($elem as $telem) { + if ($bSlash) { + $msg['warning'][$idx] .= "\\"; + } + $msg['warning'][$idx] .= $telem['name']; + $bSlash = true; + } + $idx ++; + } + } else { + $msg['link_msg'] = null; + $msg['warning'] = null; + } + } + return $msg; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs(&$dbHandler, &$tprojectMgr, &$tsuiteMgr, $optionTransferCfg) +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->kw_link_id = isset($_REQUEST['kw_link_id']) ? intval( + $_REQUEST['kw_link_id']) : null; + $args->item_id = isset($_REQUEST['item_id']) ? intval($_REQUEST['item_id']) : null; + $args->free_keywords = isset($_REQUEST['free_keywords']) ? $_REQUEST['free_keywords'] : null; + $args->containerID = isset($_REQUEST['containerID']) ? $_REQUEST['containerID'] : null; + $args->objectType = isset($_REQUEST['objectType']) ? $_REQUEST['objectType'] : null; // check againts whitelist + $args->containerType = isset($_REQUEST['containerType']) ? $_REQUEST['containerType'] : null; + + if (null != $args->containerType) { + $ctWhiteList = array( + 'testproject' => 'OK', + 'testsuite' => 'OK' + ); + if (! is_null($args->containerType) && + ! isset($ctWhiteList[$args->containerType])) { + $args->containerType = null; + } + } + + // When Deleting Test suite - container ID is not set + if (is_null($args->containerID)) { + $args->containerType = is_null($args->containerType) ? 'testproject' : $args->containerType; + } + + if (null == $args->containerType) { + throw new Exception("Error No Container Type", 1); + } + + $args->testsuiteID = isset($_REQUEST['testsuiteID']) ? intval( + $_REQUEST['testsuiteID']) : null; + $args->tsuite_name = isset($_REQUEST['testsuiteName']) ? $_REQUEST['testsuiteName'] : null; + + // Order is critic + $args->objectID = isset($_REQUEST['objectID']) ? intval( + $_REQUEST['objectID']) : null; + $args->containerID = isset($_REQUEST['containerID']) ? intval( + $_REQUEST['containerID']) : $args->objectID; + + switch ($args->containerType) { + case 'testproject': + $args->tprojectID = $args->containerID; + break; + + case 'testsuite': + $nodeID = ! is_null($args->testsuiteID) ? $args->testsuiteID : $args->containerID; + $args->tprojectID = $tsuiteMgr->getTestProjectFromTestSuite($nodeID, + null); + break; + } + + if (intval($args->tprojectID) == 0) { + if (isset($_REQUEST['tproject_id'])) { + $args->tprojectID = intval($_REQUEST['tproject_id']); + } + + if (isset($_REQUEST['tprojectID'])) { + $args->tprojectID = intval($_REQUEST['tprojectID']); + } + } + + // very ugly + $args->level = null; + if (isset($_REQUEST['delete_testsuite'])) { + $args->level = $args->objectType; + } + + $info = $tprojectMgr->get_by_id($args->tprojectID, + array( + 'output' => 'name' + )); + $args->tprojectName = $info['name']; + + $args->userID = isset($_SESSION['userID']) ? intval($_SESSION['userID']) : 0; + $args->file_id = isset($_REQUEST['file_id']) ? intval($_REQUEST['file_id']) : 0; + $args->fileTitle = isset($_REQUEST['fileTitle']) ? trim( + $_REQUEST['fileTitle']) : ''; + + $k2l = array( + 'tc_status', + 'importance', + 'execution_type' + ); + foreach ($k2l as $kv) { + $args->$kv = isset($_REQUEST[$kv]) ? intval($_REQUEST[$kv]) : - 1; + } + + $args->user = $_SESSION['currentUser']; + + $args->grants = new stdClass(); + $args->grants->delete_executed_testcases = $args->user->hasRight($dbHandler, + 'testproject_delete_executed_testcases', $args->tprojectID); + + $args->grants->testcase_mgmt = $args->user->hasRight($dbHandler, + 'mgt_modify_tc', $args->tprojectID); + + $keys2loop = array( + 'nodes_order' => null, + 'tcaseSet' => null, + 'target_position' => 'bottom', + 'doAction' => '' + ); + foreach ($keys2loop as $key => $value) { + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $value; + } + + $args->bSure = (isset($_REQUEST['sure']) && ($_REQUEST['sure'] == 'yes')); + $rl_html_name = $optionTransferCfg->js_ot_name . "_newRight"; + $args->assigned_keyword_list = isset($_REQUEST[$rl_html_name]) ? $_REQUEST[$rl_html_name] : ""; + + $keys2loop = array( + 'copyKeywords' => 0, + 'copyRequirementAssignments' => 0 + ); + + foreach ($keys2loop as $key => $value) { + $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : $value; + } + + $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? $_SESSION['setting_refresh_tree_on_action'] : 0; + $args->treeFormToken = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0; + $args->testCaseSet = null; + + if ($args->treeFormToken > 0) { + $mode = 'edit_mode'; + $sdata = isset($_SESSION[$mode]) && + isset($_SESSION[$mode][$args->treeFormToken]) ? $_SESSION[$mode][$args->treeFormToken] : null; + $args->testCaseSet = isset($sdata['testcases_to_show']) ? $sdata['testcases_to_show'] : null; + } + + $dummy = (config_get('testcase_reorder_by')) == 'NAME' ? '_alpha' : '_externalid'; + $lbl2init = array( + 'warning_empty_testsuite_name' => null, + 'string_contains_bad_chars' => null, + 'container_title_testsuite' => null, + 'btn_reorder_testcases' => 'btn_reorder_testcases' . $dummy + ); + $args->l10n = init_labels($lbl2init); + + return $args; +} + +/* + * function: + * + * args: + * + * returns: + * + */ +function writeCustomFieldsToDB(&$db, $tprojectID, $tsuiteID, &$hash) +{ + $NO_FILTERS = null; + + $cfield_mgr = new cfield_mgr($db); + $cf_map = $cfield_mgr->get_linked_cfields_at_design($tprojectID, + cfield_mgr::ENABLED, $NO_FILTERS, 'testsuite'); + $cfield_mgr->design_values_to_db($hash, $tsuiteID, $cf_map); +} + +/* + * function: deleteTestSuite + * + * args: + * + * returns: true -> refresh tree + * false -> do not refresh + * + */ +function deleteTestSuite(&$smartyObj, &$argsObj, &$tsuiteMgr, &$treeMgr, + &$tcaseMgr, $level) +{ + $feedback_msg = ''; + $system_message = ''; + $can_delete = 1; + + if ($argsObj->bSure) { + $tsuite = $tsuiteMgr->get_by_id($argsObj->objectID); + $tsuiteMgr->delete_deep($argsObj->objectID); + $tsuiteMgr->deleteKeywords($argsObj->objectID); + $smartyObj->assign('objectName', $tsuite['name']); + $doRefreshTree = true; + $feedback_msg = 'ok'; + $smartyObj->assign('user_feedback', + lang_get('testsuite_successfully_deleted')); + } else { + $doRefreshTree = false; + + // Get test cases present in this testsuite and all children + $testcases = $tsuiteMgr->get_testcases_deep($argsObj->testsuiteID); + $map_msg['warning'] = null; + $map_msg['link_msg'] = null; + $map_msg['delete_msg'] = null; + + if (is_null($testcases) || count($testcases) == 0) { + $can_delete = 1; + } else { + $map_msg = build_del_testsuite_warning_msg($treeMgr, $tcaseMgr, + $testcases, $argsObj->testsuiteID); + if (in_array('linked_and_executed', (array) $map_msg['link_msg'])) { + $can_delete = $argsObj->grants->delete_executed_testcases; + } + } + + $system_message = ''; + if (! $can_delete && ! $argsObj->grants->delete_executed_testcases) { + $system_message = lang_get( + 'system_blocks_tsuite_delete_due_to_exec_tc'); + } + + // prepare to show the delete confirmation page + $smartyObj->assign('can_delete', $can_delete); + $smartyObj->assign('objectID', $argsObj->testsuiteID); + $smartyObj->assign('objectType', 'testsuite'); + $smartyObj->assign('objectName', $argsObj->tsuite_name); + $smartyObj->assign('containerType', $argsObj->containerType); + $smartyObj->assign('delete_msg', $map_msg['delete_msg']); + $smartyObj->assign('warning', $map_msg['warning']); + $smartyObj->assign('link_msg', $map_msg['link_msg']); + } + $smartyObj->assign('system_message', $system_message); + $smartyObj->assign('page_title', + lang_get('delete') . " " . lang_get('container_title_' . $level)); + $smartyObj->assign('sqlResult', $feedback_msg); + + return $doRefreshTree; +} + +/* + * function: addTestSuite + * + * args: + * + * returns: map with messages and status + * + * revision: + * 20101012 - franciscom - BUGID 3890 + * when creating action on duplicate is setted to BLOCK without using + * config_get('action_on_duplicate_name'). + * This is because this config option has to be used ONLY when copying/moving not when creating. + * + * 20091206 - franciscom - new items are created as last element of tree branch + * + */ +function addTestSuite(&$tsuiteMgr, &$argsObj, $container, &$hash) +{ + $new_order = null; + + // compute order + // + // $nt2exclude=array('testplan' => 'exclude_me','requirement_spec'=> 'exclude_me','requirement'=> 'exclude_me'); + // $siblings = $tsuiteMgr->tree_manager->get_children($argsObj->containerID,$nt2exclude); + // if( !is_null($siblings) ) + // { + // $dummy = end($siblings); + // $new_order = $dummy['node_order']+1; + // } + $ret = $tsuiteMgr->create($argsObj->containerID, + $container['container_name'], $container['details'], $new_order, + config_get('check_names_for_duplicates'), 'block'); + + $op['messages'] = array( + 'msg' => $ret['msg'], + 'user_feedback' => '' + ); + $op['status'] = $ret['status_ok']; + + if ($ret['status_ok']) { + $op['messages']['user_feedback'] = lang_get('testsuite_created'); + if ($op['messages']['msg'] != 'ok') { + $op['messages']['user_feedback'] = $op['messages']['msg']; + } + + if (trim($argsObj->assigned_keyword_list) != "") { + $tsuiteMgr->addKeywords($ret['id'], + explode(",", $argsObj->assigned_keyword_list)); + } + writeCustomFieldsToDB($tsuiteMgr->db, $argsObj->tprojectID, $ret['id'], + $hash); + + // Send Events to plugins + $ctx = array( + 'id' => $ret['id'], + 'name' => $container['container_name'], + 'details' => $container['details'] + ); + event_signal('EVENT_TEST_SUITE_CREATE', $ctx); + } + return $op; +} + +/* + * function: moveTestSuiteViewer + * prepares smarty variables to display move testsuite viewer + * + * args: + * + * returns: - + * + */ +function moveTestSuiteViewer(&$smartyObj, &$tprojectMgr, $argsObj) +{ + $testsuites = $tprojectMgr->gen_combo_test_suites($argsObj->tprojectID, + array( + $argsObj->testsuiteID => 'exclude' + )); + // Added the Test Project as the FIRST Container where is possible to copy + $testsuites = array( + $argsObj->tprojectID => $argsObj->tprojectName + ) + $testsuites; + + // original container (need to comment this better) + $smartyObj->assign('old_containerID', $argsObj->tprojectID); + $smartyObj->assign('containers', $testsuites); + $smartyObj->assign('objectID', $argsObj->testsuiteID); + $smartyObj->assign('object_name', $argsObj->tsuite_name); + $smartyObj->assign('top_checked', 'checked=checked'); + $smartyObj->assign('bottom_checked', ''); +} + +/* + * function: reorderTestSuiteViewer + * prepares smarty variables to display reorder testsuite viewer + * + * args: + * + * returns: - + * + */ +function reorderTestSuiteViewer(&$smartyObj, &$treeMgr, $argsObj) +{ + $level = null; + $oid = is_null($argsObj->testsuiteID) ? $argsObj->containerID : $argsObj->testsuiteID; + $children = $treeMgr->get_children($oid, + array( + "testplan" => "exclude_me", + "requirement_spec" => "exclude_me" + )); + $object_info = $treeMgr->get_node_hierarchy_info($oid); + $object_name = $object_info['name']; + + if (! count($children)) { + $children = null; + } + + $smartyObj->assign('arraySelect', $children); + $smartyObj->assign('objectID', $oid); + $smartyObj->assign('object_name', $object_name); + + if ($oid == $argsObj->tprojectID) { + $level = 'testproject'; + $smartyObj->assign('level', $level); + $smartyObj->assign('page_title', lang_get('container_title_' . $level)); + } + + return $level; +} + +/* + * function: updateTestSuite + * + * args: + * + * returns: + * + */ +function updateTestSuite(&$tsuiteMgr, &$argsObj, $container, &$hash) +{ + $msg = 'ok'; + $ret = $tsuiteMgr->update($argsObj->testsuiteID, + $container['container_name'], $container['details']); + if ($ret['status_ok']) { + $tsuiteMgr->deleteKeywords($argsObj->testsuiteID); + if (trim($argsObj->assigned_keyword_list) != "") { + $tsuiteMgr->addKeywords($argsObj->testsuiteID, + explode(",", $argsObj->assigned_keyword_list)); + } + writeCustomFieldsToDB($tsuiteMgr->db, $argsObj->tprojectID, + $argsObj->testsuiteID, $hash); + + /* Send events to plugins */ + $ctx = array( + 'id' => $argsObj->testsuiteID, + 'name' => $container['container_name'], + 'details' => $container['details'] + ); + event_signal('EVENT_TEST_SUITE_UPDATE', $ctx); + } else { + $msg = $ret['msg']; + } + return $msg; +} + +/* + * function: copyTestSuite + * + * args: + * + * returns: + * + */ +function copyTestSuite(&$smartyObj, $template_dir, &$tsuiteMgr, $argsObj) +{ + $guiObj = new stdClass(); + $guiObj->btn_reorder_testcases = $argsObj->l10n['btn_reorder_testcases']; + + $exclude_node_types = array( + 'testplan' => 1, + 'requirement' => 1, + 'requirement_spec' => 1 + ); + + $options = array(); + $options['check_duplicate_name'] = config_get('check_names_for_duplicates'); + $options['action_on_duplicate_name'] = config_get( + 'action_on_duplicate_name'); + $options['copyKeywords'] = $argsObj->copyKeywords; + $options['copyRequirements'] = $argsObj->copyRequirementAssignments; + + // copy_to($source,$destination,...) + $op = $tsuiteMgr->copy_to($argsObj->objectID, $argsObj->containerID, + $argsObj->userID, $options); + + if ($op['status_ok']) { + $tsuiteMgr->tree_manager->change_child_order($argsObj->containerID, + $op['id'], $argsObj->target_position, $exclude_node_types); + + // get info to provide feedback + $dummy = $tsuiteMgr->tree_manager->get_node_hierarchy_info( + array( + $argsObj->objectID, + $argsObj->containerID + )); + + $msgk = $op['name_changed'] ? 'tsuite_copied_ok_name_changed' : 'tsuite_copied_ok'; + $guiObj->user_feedback = sprintf(lang_get($msgk), + $dummy[$argsObj->objectID]['name'], + $dummy[$argsObj->containerID]['name'], $op['name']); + } + $guiObj->refreshTree = $op['status_ok'] && $argsObj->refreshTree; + $guiObj->attachments = getAttachmentInfosFrom($tsuiteMgr, $argsObj->objectID); + $guiObj->id = $argsObj->objectID; + $guiObj->treeFormToken = $guiObj->form_token = $argsObj->treeFormToken; + + $guiObj->direct_link = $tsuiteMgr->buildDirectWebLink($_SESSION['basehref'], + $guiObj->id, $argsObj->tprojectID); + + $tsuiteMgr->show($smartyObj, $guiObj, $template_dir, $argsObj->objectID, + null, 'ok'); + + return $op['status_ok']; +} + +/* + * function: moveTestSuite + * + * args: + * + * returns: + * + */ +function moveTestSuite(&$smartyObj, $template_dir, &$tprojectMgr, $argsObj) +{ + $exclude_node_types = array( + 'testplan' => 1, + 'requirement' => 1, + 'requirement_spec' => 1 + ); + + $tprojectMgr->tree_manager->change_parent($argsObj->objectID, + $argsObj->containerID); + $tprojectMgr->tree_manager->change_child_order($argsObj->containerID, + $argsObj->objectID, $argsObj->target_position, $exclude_node_types); + + $guiObj = new stdClass(); + $guiObj->id = $argsObj->tprojectID; + $guiObj->refreshTree = $argsObj->refreshTree; + + $tprojectMgr->show($smartyObj, $guiObj, $template_dir, $argsObj->tprojectID, + null, 'ok'); +} + +/* + * function: initializeOptionTransfer + * + * args: + * + * returns: option transfer configuration + * + */ +function initializeOptionTransfer(&$tprojectMgr, &$tsuiteMgr, $argsObj, + $doAction) +{ + $opt_cfg = opt_transf_empty_cfg(); + $opt_cfg->js_ot_name = 'ot'; + $opt_cfg->global_lbl = ''; + $opt_cfg->from->lbl = lang_get('available_kword'); + $opt_cfg->from->map = $tprojectMgr->get_keywords_map($argsObj->tprojectID); + $opt_cfg->to->lbl = lang_get('assigned_kword'); + + if ($doAction == 'edit_testsuite') { + $opt_cfg->to->map = $tsuiteMgr->get_keywords_map($argsObj->testsuiteID, + " ORDER BY keyword ASC "); + } + return $opt_cfg; +} + +/* + * function: moveTestCasesViewer + * prepares smarty variables to display move testcases viewer + * + * args: + * + * returns: - + * + */ +function moveTestCasesViewer(&$dbHandler, &$smartyObj, &$tprojectMgr, &$treeMgr, + $argsObj, $feedback = '', $cf = null) +{ + $tables = $tprojectMgr->getDBTables( + array( + 'nodes_hierarchy', + 'node_types', + 'tcversions' + )); + $testcase_cfg = config_get('testcase_cfg'); + $glue = $testcase_cfg->glue_character; + + $containerID = isset($argsObj->testsuiteID) ? $argsObj->testsuiteID : $argsObj->objectID; + $containerName = $argsObj->tsuite_name; + if (is_null($containerName)) { + $dummy = $treeMgr->get_node_hierarchy_info($argsObj->objectID); + $containerName = $dummy['name']; + } + + // I'have discovered that exclude selected testsuite branch is not good + // when you want to move lots of testcases from one testsuite to it's children + // testsuites. (in this situation tree drag & drop is not ergonomic). + $testsuites = $tprojectMgr->gen_combo_test_suites($argsObj->tprojectID); + $tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tprojectID) . $glue; + + // While testing with PostGres have found this behaivour: + // No matter is UPPER CASE has used on field aliases, keys on hash returned by + // ADODB are lower case. + // Accessing this keys on Smarty template using UPPER CASE fails. + // Solution: have changed case on Smarty to lower case. + // + $sqlA = " SELECT MAX(TCV.version) AS lvnum, NHTC.node_order, NHTC.name," . + " NHTC.id, TCV.tc_external_id AS tcexternalid" . + " FROM {$tables['nodes_hierarchy']} NHTC " . + " JOIN {$tables['nodes_hierarchy']} NHTCV " . + " ON NHTCV.parent_id = NHTC.id " . + " JOIN {$tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " JOIN {$tables['node_types']} NT " . + " ON NT.id = NHTC.node_type_id AND NT.description='testcase'" . + " WHERE NHTC.parent_id = " . intval($containerID); + + if (! is_null($argsObj->testCaseSet)) { + $sqlA .= " AND NHTC.id IN (" . implode(',', $argsObj->testCaseSet) . ")"; + } + + $sqlA .= " GROUP BY NHTC.id,TCV.tc_external_id,NHTC.name,NHTC.node_order "; + + $sqlB = " SELECT SQLA.id AS tcid, SQLA.name AS tcname," . + " SQLA.node_order AS tcorder, SQLA.tcexternalid," . + " MTCV.summary,MTCV.status,MTCV.importance,MTCV.execution_type," . + " MTCV.id AS tcversion_id FROM ($sqlA) SQLA " . + " JOIN {$tables['nodes_hierarchy']} MNHTCV ON MNHTCV.parent_id = SQLA.id " . + " JOIN {$tables['tcversions']} MTCV ON MTCV.id = MNHTCV.id AND MTCV.version = SQLA.lvnum"; + $orderClause = " ORDER BY TCORDER,TCNAME"; + + $children = $dbHandler->get_recordset($sqlB . $orderClause); + + // check if operation can be done + $user_feedback = $feedback; + if (! is_null($children) && (count($children) > 0) && count($testsuites)) { + $op_ok = true; + } else { + $children = null; + $op_ok = false; + $user_feedback = lang_get('no_testcases_available_or_tsuite'); + } + + $gui = new stdClass(); + $gui->treeFormToken = $gui->form_token = $argsObj->treeFormToken; + + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + $gui->domainTCStatus = array( + - 1 => '' + ) + $dummy['lbl']; + $gui->domainTCImportance = array( + - 1 => '', + HIGH => lang_get('high_importance'), + MEDIUM => lang_get('medium_importance'), + LOW => lang_get('low_importance') + ); + + $dummy = getConfigAndLabels('execution_type', 'code'); + $gui->domainTCExecType = array( + - 1 => '' + ) + $dummy['lbl']; + + $gui->testCasesTableView = 0; + if (($argsObj->action == 'testcases_table_view') || + ($argsObj->action == 'doBulkSet')) { + $gui->testCasesTableView = 1; + } + $gui->cf = $cf; + + $gui->tcprefix = $tcasePrefix; + $gui->testcases = $children; + + $smartyObj->assign('gui', $gui); + $smartyObj->assign('op_ok', $op_ok); + $smartyObj->assign('user_feedback', $user_feedback); + + // check if is needed + $smartyObj->assign('old_containerID', $argsObj->tprojectID); + $smartyObj->assign('containers', $testsuites); + $smartyObj->assign('objectID', $containerID); + $smartyObj->assign('object_name', $containerName); + $smartyObj->assign('top_checked', 'checked=checked'); + $smartyObj->assign('bottom_checked', ''); +} + +/* + * function: copyTestCases + * copy a set of choosen test cases. + * + * args: + * + * returns: - + * + */ +function copyTestCases(&$smartyObj, $template_dir, &$tsuiteMgr, &$tcaseMgr, + $argsObj) +{ + $op = array( + 'refreshTree' => false, + 'userfeedback' => '' + ); + if (($qty = count($argsObj->tcaseSet)) > 0) { + $msg_id = $qty == 1 ? 'one_testcase_copied' : 'testcase_set_copied'; + $op['userfeedback'] = sprintf(lang_get($msg_id), $qty); + + $copyOpt = array( + 'check_duplicate_name' => config_get('check_names_for_duplicates'), + 'action_on_duplicate_name' => config_get('action_on_duplicate_name'), + 'stepAsGhost' => $argsObj->stepAsGhost + ); + $copyOpt['copy_also'] = array( + 'keyword_assignments' => $argsObj->copyKeywords, + 'requirement_assignments' => $argsObj->copyRequirementAssignments + ); + + $copy_op = array(); + foreach ($argsObj->tcaseSet as $tcaseid) { + $copy_op[$tcaseid] = $tcaseMgr->copy_to($tcaseid, + $argsObj->containerID, $argsObj->userID, $copyOpt); + } + $guiObj = new stdClass(); + $guiObj->attachments = getAttachmentInfosFrom($tsuiteMgr, + $argsObj->objectID); + $guiObj->id = $argsObj->objectID; + $guiObj->refreshTree = true; + $op['refreshTree'] = true; + } + return $op; +} + +/* + * function: moveTestCases + * move a set of choosen test cases. + * + * args: + * + * returns: - + * + */ +function moveTestCases(&$smartyObj, $template_dir, &$tsuiteMgr, &$treeMgr, + $argsObj) +{ + $lbl = $argsObj->l10n; + if (count($argsObj->tcaseSet) > 0) { + $status_ok = $treeMgr->change_parent($argsObj->tcaseSet, + $argsObj->containerID); + $user_feedback = $status_ok ? '' : lang_get('move_testcases_failed'); + + // objectID - original container + $guiObj = new stdClass(); + $guiObj->attachments = getAttachmentInfosFrom($tsuiteMgr, + $argsObj->objectID); + $guiObj->id = $argsObj->objectID; + $guiObj->refreshTree = true; + $guiObj->btn_reorder_testcases = $lbl['btn_reorder_testcases']; + + $tsuiteMgr->show($smartyObj, $guiObj, $template_dir, $argsObj->objectID, + null, $user_feedback); + } +} + +/** + * initWebEditors + */ +function initWebEditors($action, $itemType, $editorCfg) +{ + $webEditorKeys = array( + 'testsuite' => array( + 'details' + ) + ); + $itemTemplateKey = null; + + switch ($action) { + case 'new_testsuite': + case 'add_testsuite': + case 'edit_testsuite': + $accessKey = 'testsuite'; + break; + + default: + $accessKey = ''; + break; + } + + switch ($itemType) { + case 'testproject': + case 'testsuite': + $itemTemplateKey = 'testsuite_template'; + $accessKey = 'testsuite'; + break; + } + + $oWebEditor = array(); + $htmlNames = ''; + if (isset($webEditorKeys[$accessKey])) { + $htmlNames = $webEditorKeys[$accessKey]; + foreach ($htmlNames as $key) { + $oWebEditor[$key] = web_editor($key, $_SESSION['basehref'], + $editorCfg); + } + } + return array( + $oWebEditor, + $htmlNames, + $itemTemplateKey + ); +} + +/* + * function: deleteTestCasesViewer + * prepares smarty variables to display move testcases viewer + * + * args: + * + * returns: - + * + * @internal revisions + * 20110402 - franciscom - BUGID 4322: New Option to block delete of executed test cases. + */ +function deleteTestCasesViewer(&$dbHandler, &$smartyObj, &$tprojectMgr, + &$treeMgr, &$tsuiteMgr, &$tcaseMgr, $argsObj, $feedback = null) +{ + $guiObj = new stdClass(); + $guiObj->main_descr = lang_get('delete_testcases'); + $guiObj->system_message = ''; + + $containerID = isset($argsObj->testsuiteID) ? $argsObj->testsuiteID : $argsObj->objectID; + $containerName = $argsObj->tsuite_name; + if (is_null($containerName)) { + $dummy = $treeMgr->get_node_hierarchy_info($argsObj->objectID); + $containerName = $dummy['name']; + } + + $guiObj->testCaseSet = $tsuiteMgr->get_children_testcases($containerID); + $guiObj->exec_status_quo = null; + $tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tprojectID); + $hasExecutedTC = false; + + if (! is_null($guiObj->testCaseSet) && count($guiObj->testCaseSet) > 0) { + foreach ($guiObj->testCaseSet as &$child) { + $external = $tcaseMgr->getExternalID($child['id'], null, + $tcasePrefix); + $child['external_id'] = $external[0]; + + // key level 1 : Test Case Version ID + // key level 2 : Test Plan ID + // key level 3 : Platform ID + $getOptions = array( + 'addExecIndicator' => true + ); + $dummy = $tcaseMgr->get_exec_status($child['id'], null, $getOptions); + $child['draw_check'] = $argsObj->grants->delete_executed_testcases || + (! $dummy['executed']); + + $hasExecutedTC = $hasExecutedTC || $dummy['executed']; + unset($dummy['executed']); + $guiObj->exec_status_quo[] = $dummy; + } + } + // Need to understand if platform column has to be displayed on GUI + if (! is_null($guiObj->exec_status_quo)) { + // key level 1 : Test Case Version ID + // key level 2 : Test Plan ID + // key level 3 : Platform ID + + $itemSet = array_keys($guiObj->exec_status_quo); + foreach ($itemSet as $mainKey) { + $guiObj->display_platform[$mainKey] = false; + if (! is_null($guiObj->exec_status_quo[$mainKey])) { + $versionSet = array_keys($guiObj->exec_status_quo[$mainKey]); + $stop = false; + foreach ($versionSet as $version_id) { + $tplanSet = array_keys( + $guiObj->exec_status_quo[$mainKey][$version_id]); + foreach ($tplanSet as $tplan_id) { + if ($guiObj->display_platform[$mainKey] = ! isset( + $guiObj->exec_status_quo[$mainKey][$version_id][$tplan_id][0])) { + $stop = true; + break; + } + } + + if ($stop) { + break; + } + } + } + } + } + // check if operation can be done + $guiObj->user_feedback = $feedback; + if (! is_null($guiObj->testCaseSet) && (count($guiObj->testCaseSet) > 0)) { + $guiObj->op_ok = true; + $guiObj->user_feedback = ''; + } else { + $guiObj->children = null; + $guiObj->op_ok = false; + $guiObj->user_feedback = is_null($guiObj->user_feedback) ? lang_get( + 'no_testcases_available') : $guiObj->user_feedback; + } + + if (! $argsObj->grants->delete_executed_testcases && $hasExecutedTC) { + $guiObj->system_message = lang_get('system_blocks_delete_executed_tc'); + } + + $guiObj->objectID = $containerID; + $guiObj->object_name = $containerName; + $guiObj->refreshTree = $argsObj->refreshTree; + + $smartyObj->assign('gui', $guiObj); +} + +/* + * function: doDeleteTestCasesViewer + * prepares smarty variables to display move testcases viewer + * + * args: + * + * returns: - + * + */ +function doDeleteTestCases(&$dbHandler, $tcaseSet, &$tcaseMgr) +{ + if (! empty($tcaseSet)) { + foreach ($tcaseSet as $victim) { + $tcaseMgr->delete($victim); + } + } +} + +/** + */ +function reorderTestCasesByCriteria($argsObj, &$tsuiteMgr, &$treeMgr) +{ + $sortCriteria = config_get('testcase_reorder_by'); + $pfn = ($sortCriteria == 'NAME') ? 'reorderTestCasesDictionary' : 'reorderTestCasesByExtID'; + $pfn($argsObj, $tsuiteMgr, $treeMgr); +} + +/** + */ +function reorderTestCasesDictionary($argsObj, &$tsuiteMgr, &$treeMgr) +{ + $tcaseSet = (array) $tsuiteMgr->get_children_testcases( + $argsObj->testsuiteID, 'simple'); + if (($loop2do = count($tcaseSet)) > 0) { + for ($idx = 0; $idx < $loop2do; $idx ++) { + $a2sort[$tcaseSet[$idx]['id']] = strtolower($tcaseSet[$idx]['name']); + } + natsort($a2sort); + $a2sort = array_keys($a2sort); + $treeMgr->change_order_bulk($a2sort); + } +} + +/** + */ +function reorderTestCasesByExtID($argsObj, &$tsuiteMgr, &$treeMgr) +{ + $tables = $tsuiteMgr->getDBTables( + array( + 'nodes_hierarchy', + 'testsuites', + 'tcversions' + )); + + $sql = " SELECT DISTINCT NHTC.id,TCV.tc_external_id " . + " FROM {$tables['nodes_hierarchy']} NHTC " . + " JOIN {$tables['nodes_hierarchy']} NHTCV ON NHTCV.parent_id = NHTC.id " . + " JOIN {$tables['tcversions']} TCV ON TCV.id = NHTCV.id " . + " JOIN {$tables['testsuites']} TS ON NHTC.parent_id = TS.id " . + " WHERE TS.id = " . intval($argsObj->testsuiteID) . + " ORDER BY tc_external_id ASC"; + + $tcaseSet = $tsuiteMgr->db->fetchColumnsIntoMap($sql, 'tc_external_id', 'id'); + $treeMgr->change_order_bulk($tcaseSet); +} + +/** + */ +function reorderTestSuitesDictionary($treeMgr, $parent_id) +{ + $exclude_node_types = array( + 'testplan' => 1, + 'requirement' => 1, + 'testcase' => 1, + 'requirement_spec' => 1 + ); + $itemSet = (array) $treeMgr->get_children($parent_id, $exclude_node_types); + if (($loop2do = count($itemSet)) > 0) { + for ($idx = 0; $idx < $loop2do; $idx ++) { + $a2sort[$itemSet[$idx]['id']] = strtolower($itemSet[$idx]['name']); + } + natsort($a2sort); + $a2sort = array_keys($a2sort); + $treeMgr->change_order_bulk($a2sort); + } +} + +/** + */ +function initializeGui(&$objMgr, $id, $argsObj, $lbl = null) +{ + $guiObj = new stdClass(); + + $labels = $lbl; + if (null == $labels) { + $labels = $argsObj->l10n; + } + + $guiObj->id = $id; + $guiObj->user = $argsObj->user; + $guiObj->tproject_id = $argsObj->tprojectID; + $guiObj->refreshTree = $argsObj->refreshTree; + $guiObj->btn_reorder_testcases = $labels['btn_reorder_testcases']; + $guiObj->page_title = $labels['container_title_testsuite']; + $guiObj->attachments = getAttachmentInfosFrom($objMgr, $id); + $guiObj->form_token = $argsObj->treeFormToken; + + $guiObj->fileUploadURL = $_SESSION['basehref'] . + $objMgr->getFileUploadRelativeURL($id); + + if ($objMgr->my_node_type == $objMgr->node_types_descr_id['testsuite']) { + $guiObj->direct_link = $objMgr->buildDirectWebLink( + $_SESSION['basehref'], $guiObj->id, $argsObj->tprojectID); + } + + $guiObj->modify_tc_rights = $argsObj->grants->testcase_mgmt; + return $guiObj; +} + +/** + */ +function doBulkSet(&$dbHandler, $argsObj, $tcaseSet, &$tcaseMgr) +{ + if (! empty($tcaseSet)) { + $k2s = array( + 'tc_status' => 'setStatus', + 'importance' => 'setImportance', + 'execution_type' => 'setExecutionType' + ); + foreach ($tcaseSet as $tcversion_id => $tcase_id) { + foreach ($k2s as $attr => $m2c) { + if ($argsObj->$attr > 0) { + $tcaseMgr->$m2c($tcversion_id, $argsObj->$attr); + } + } + } + + // second round, on Custom Fields + $cf_map = $tcaseMgr->cfield_mgr->get_linked_cfields_at_design( + $argsObj->tprojectID, cfield_mgr::ENABLED, NO_FILTER_SHOW_ON_EXEC, + 'testcase'); + if (! is_null($cf_map)) { + // get checkboxes from $_REQUEST + $k2i = array_keys($_REQUEST); + $cfval = null; + foreach ($k2i as $val) { + if (strpos($val, 'check_custom_field_') !== false) { + $cfid = explode('_', $val); + $cfid = end($cfid); + $cfval[$cfid] = $cf_map[$cfid]; + } + } + if (! is_null($cfval)) { + foreach ($tcaseSet as $tcversion_id => $tcase_id) { + $tcaseMgr->cfield_mgr->design_values_to_db($_REQUEST, + $tcversion_id, $cfval); + } + } + } + } +} + +/** + */ +function initTPLActions() +{ + $tpl = array( + 'move_testsuite_viewer' => 'containerMove.tpl', + 'delete_testsuite' => 'containerDelete.tpl', + 'move_testcases_viewer' => 'containerMoveTC.tpl', + 'testcases_table_view' => 'containerMoveTC.tpl', + 'do_copy_tcase_set' => 'containerMoveTC.tpl', + 'do_copy_tcase_set_ghost' => 'containerMoveTC.tpl', + 'delete_testcases' => 'containerDeleteTC.tpl', + 'do_delete_testcases' => 'containerDeleteTC.tpl', + 'doBulkSet' => 'containerMoveTC.tpl' + ); + + $actions = array( + 'edit_testsuite' => 0, + 'new_testsuite' => 0, + 'delete_testsuite' => 0, + 'do_move' => 0, + 'do_copy' => 0, + 'reorder_testsuites' => 1, + 'do_testsuite_reorder' => 0, + 'add_testsuite' => 1, + 'move_testsuite_viewer' => 0, + 'update_testsuite' => 1, + 'move_testcases_viewer' => 0, + 'do_move_tcase_set' => 0, + 'testcases_table_view' => 0, + 'do_copy_tcase_set' => 0, + 'do_copy_tcase_set_ghost' => 0, + 'del_testsuites_bulk' => 0, + 'delete_testcases' => 0, + 'do_delete_testcases' => 0, + 'reorder_testcases' => 0, + 'reorder_testsuites_alpha' => 0, + 'reorder_testproject_testsuites_alpha' => 0, + 'doBulkSet' => 0 + ); + + return array( + $tpl, + $actions + ); +} + +/** + */ +function showTestSuite(&$tplEngine, &$argsO, &$tsuiteMgr, $tplDir) +{ + if (null == $argsO->tprojectID) { + $argsO->tprojectID = $tsuiteMgr->getTestproject($argsO->item_id); + } + $gui = initializeGui($tsuiteMgr, $argsO->item_id, $argsO, $argsO->l10n); + $gui->refreshTree = 0; + $tsuiteMgr->show($tplEngine, $gui, $tplDir, $argsO->item_id, null, null); + exit(); } diff --git a/lib/testcases/listTestCases.php b/lib/testcases/listTestCases.php index 6d78f3fd07..1ac7ada134 100644 --- a/lib/testcases/listTestCases.php +++ b/lib/testcases/listTestCases.php @@ -1,59 +1,64 @@ -build_tree_menu($gui); -$control->formAction = ''; - -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->assign('control', $control); -$smarty->assign('args', $control->get_argument_string()); -$smarty->assign('menuUrl', $gui->menuUrl); - -$smarty->display($templateCfg->template_dir . 'tcTree.tpl'); - - -/** - * Initialize object with information for graphical user interface. - * - * @param tlTestCaseFilterControl $control - * @return stdClass $gui - */ -function initializeGui(&$dbHandler, &$control) -{ - $gui = new stdClass(); - $gui->feature = $control->args->feature; - $gui->treeHeader = lang_get('title_navigator'). ' - ' . lang_get('title_test_spec'); - - $lblkey = (config_get('testcase_reorder_by') == 'NAME') ? '_alpha' : '_externalid'; - $gui->btn_reorder_testcases = lang_get('btn_reorder_testcases' . $lblkey); - - $feature_path = array('edit_tc' => "lib/testcases/archiveData.php", - 'keywordsAssign' => "lib/keywords/keywordsAssign.php", - 'assignReqs' => "lib/requirements/reqTcAssign.php"); - - $gui->tree_drag_and_drop_enabled = array('edit_tc' => (has_rights($dbHandler, "mgt_modify_tc") == 'yes'), - 'keywordsAssign' => false, - 'assignReqs' => false); - - $gui->menuUrl = $feature_path[$gui->feature]; - return $gui; -} \ No newline at end of file +build_tree_menu($gui); +$control->formAction = ''; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('control', $control); +$smarty->assign('args', $control->get_argument_string()); +$smarty->assign('menuUrl', $gui->menuUrl); + +$smarty->display($templateCfg->template_dir . 'tcTree.tpl'); + +/** + * Initialize object with information for graphical user interface. + * + * @param tlTestCaseFilterControl $control + * @return stdClass $gui + */ +function initializeGui(&$dbHandler, &$control) +{ + $gui = new stdClass(); + $gui->feature = $control->args->feature; + $gui->treeHeader = lang_get('title_navigator') . ' - ' . + lang_get('title_test_spec'); + + $lblkey = (config_get('testcase_reorder_by') == 'NAME') ? '_alpha' : '_externalid'; + $gui->btn_reorder_testcases = lang_get('btn_reorder_testcases' . $lblkey); + + $feature_path = array( + 'edit_tc' => "lib/testcases/archiveData.php", + 'keywordsAssign' => "lib/keywords/keywordsAssign.php", + 'assignReqs' => "lib/requirements/reqTcAssign.php" + ); + + $gui->tree_drag_and_drop_enabled = array( + 'edit_tc' => ($_SESSION['currentUser']->hasRightOnProj($dbHandler, + "mgt_modify_tc") == 'yes'), + 'keywordsAssign' => false, + 'assignReqs' => false + ); + + $gui->menuUrl = $feature_path[$gui->feature]; + return $gui; +} diff --git a/lib/testcases/scriptAdd.php b/lib/testcases/scriptAdd.php index e64839f08a..798c08b5a2 100755 --- a/lib/testcases/scriptAdd.php +++ b/lib/testcases/scriptAdd.php @@ -1,424 +1,480 @@ -codeTrackerMetaData = array('projects' => null, 'repos' => null, 'files' => null, - 'branches' => null, 'commits' => null); -if(!is_null($args->projectKey)) -{ - $gui->codeTrackerMetaData['projects'] = $args->projectKey; -} -else -{ - $gui->codeTrackerMetaData['projects'] = $cts->getProjectsForHTMLSelect(); -} - -if(is_null($gui->project_key)) -{ - $gui->project_key = isset($_SESSION['testscript_projectKey']) ? $_SESSION['testscript_projectKey'] : $cts->cfg->projectkey; -} -if(is_null($gui->repository_name) && isset($_SESSION['testscript_repositoryName']) && $args->user_action == 'link') -{ - $gui->repository_name = $_SESSION['testscript_repositoryName']; -} - -if(!is_null($args->repositoryName) && $args->user_action != 'projectSelected') -{ - $gui->codeTrackerMetaData['repos'] = $args->repositoryName; -} -else -{ - $gui->codeTrackerMetaData['repos'] = $cts->getReposForHTMLSelect($gui->project_key); -} - -if(!is_null($args->branchName) && $args->user_action != 'projectSelected' && $args->user_action != 'repoSelected') -{ - $gui->codeTrackerMetaData['branches'] = $args->branchName; -} -else -{ - $gui->codeTrackerMetaData['branches'] = $cts->getBranchesForHTMLSelect($gui->project_key, $gui->repository_name); -} - -if(!is_null($args->commits) && $args->user_action != 'projectSelected' && $args->user_action != 'repoSelected' && - $args->user_action != 'branchSelected') -{ - $gui->codeTrackerMetaData['commits'] = $args->commits; -} -else if(($args->user_action == 'branchSelected') || - (!is_null($gui->branch_name) && ($args->user_action == 'expand' || $args->user_action == 'collapse'))) -{ - $gui->codeTrackerMetaData['commits'] = $cts->getCommitsForHTMLSelect($gui->project_key, $gui->repository_name, - $gui->branch_name); -} - -if(!is_null($args->files) && ($args->user_action == 'expand' || $args->user_action == 'collapse')) -{ - $gui->codeTrackerMetaData['files'] = $args->files; -} -else if($args->user_action == 'repoSelected' || $args->user_action == 'branchSelected' || -$args->user_action == 'expand' || $args->user_action == 'collapse' || ($args->user_action == 'link' && !is_null($gui->repository_name))) -{ - $gui->codeTrackerMetaData['files'] = $cts->getRepoContentForHTMLSelect($gui->project_key, $gui->repository_name, '', $gui->branch_name, $gui->commit_id); -} -if($args->user_action == 'expand') -{ - $expandArray = explode("/", $args->expand_item); - $tmpFileArray = &$gui->codeTrackerMetaData['files']; - $tmpPath = ""; - foreach($expandArray as $item) - { - $tmpPath .= $item . "/"; - $tmpFileArray[$item][0] = $cts->getRepoContentForHTMLSelect($gui->project_key, $gui->repository_name, $tmpPath, $gui->branch_name, $gui->commit_id); - $tmpFileArray = &$tmpFileArray[$item][0]; - } -} -else if($args->user_action == 'collapse') -{ - if(substr($args->collapse_item,-1) == "/") - { - $args->collapse_item = substr($args->collapse_item,0,-1); - } - $collapseArray = explode("/", $args->collapse_item); - array_pop($collapseArray); - - $tmpFileArray = &$gui->codeTrackerMetaData['files']; - $tmpPath = ""; - foreach($collapseArray as $item) - { - $tmpPath .= $item . "/"; - $tmpFileArray[$item][0] = $cts->getRepoContentForHTMLSelect($gui->project_key, $gui->repository_name, $tmpPath, $gui->branch_name, $gui->commit_id); - $tmpFileArray = &$tmpFileArray[$item][0]; - } -} - -if($args->user_action == 'create') -{ - if(!is_null($codeT) && $args->project_key != "" && $args->repository_name != "" && $args->code_path != "") - { - $l18n = init_labels(array("error_code_does_not_exist_on_cts" => null)); - - $gui->msg = ""; - - $baseURL = $cts->buildViewCodeURL($args->project_key, $args->repository_name, ''); - /* if code_path was entered manually it may contain the complete URL - * in this case we have to strip the base URL from the string - */ - if (substr($args->code_path, 0, strlen($baseURL)) === $baseURL) - { - $args->code_path = substr($args->code_path, strlen($baseURL)); - } - // if code_path contains reference to certain branch, delete it - $refPos = strpos($args->code_path, '?at='); - if ($refPos !== false) - { - // in case no branch was seleted try to extract branch name from reference if possible - $refStr = substr($args->code_path, $refPos); - if (strpos($refStr, '?at=refs%2Fheads%2F') !== false) - { - $refStr = str_replace("?at=refs%2Fheads%2F", "", $refStr); - if (is_null($args->branch_name) && (isset($args->branchName[$refStr]) || array_key_exists($refStr, $args->branchName))) - { - $args->branch_name = $refStr; - } - } - else //no branch given, but commit_id -> try to extract this - { - $refStr = str_replace("?at=", "", $refStr); - if (is_null($args->commit_id)) - { - $args->commit_id = $refStr; - } - } - - $args->code_path = substr($args->code_path, 0, $refPos); - } - - $scriptWritten = write_testcase_script($db,$cts,$args->tcversion_id, $args->project_key, $args->repository_name, - $args->code_path, $args->branch_name, $args->commit_id); - - - $auditMsg = "audit_testcasescript_added"; - $item_id = $args->tcversion_id; - $objectType = "testcase_script_links"; - $args->direct_link = $cts->buildViewCodeURL($args->project_key,$args->repository_name,$args->code_path,$args->branch_name); - if ($scriptWritten) - { - $gui->msg = lang_get("script_added"); - - //save user selection of project_key and repository_name to current session - $_SESSION['testscript_projectKey'] = $args->project_key; - $_SESSION['testscript_repositoryName'] = $args->repository_name; - - //update value of Custom Field "Test Script" if possible - $tScriptFieldID = null; - $tScriptFieldValue = null; - - $tcase_mgr = new testcase($db); - $linked_cfields = $tcase_mgr->cfield_mgr->get_linked_cfields_at_design($args->tproject_id, - 1,null,'testcase',$args->tcversion_id); - unset($tcase_mgr); - foreach($linked_cfields as $cfieldID => $cfieldValue) - { - if (is_null($tScriptFieldValue) && strpos(strtolower(str_replace(" ", "", $cfieldValue['name'])), 'testscript') !== false) - { - $tScriptFieldID = $cfieldID; - $tScriptFieldValue = $cfieldValue; - break; - } - } - - if (!is_null($tScriptFieldID)) - { - $cfieldWritten = write_cfield_testscript($db, $args->user, $tScriptFieldID, $args->tcversion_id, $args->code_path); - } - else - { - $cfieldWritten = true; - } - - if (!$cfieldWritten) - { - $gui->msg .= " - But Custom Field 'Test Script' could not be updated"; - } - - logAuditEvent(TLS($auditMsg,$args->direct_link),"CREATE",$item_id,$objectType); - } - else - { - $gui->msg = sprintf($l18n["error_code_does_not_exist_on_cts"],$args->direct_link); - } - } - else - { - $gui->msg = lang_get("error_script_not_added"); - } -} -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -function initEnv(&$dbHandler) -{ - $uaWhiteList = array(); - $uaWhiteList['elements'] = array('link','create','projectSelected','repoSelected', - 'branchSelected','expand','collapse'); - $uaWhiteList['length'] = array(); - foreach($uaWhiteList['elements'] as $xmen) - { - $uaWhiteList['length'][] = strlen($xmen); - } - $user_action['maxLength'] = max($uaWhiteList['length']); - $user_action['minLength'] = min($uaWhiteList['length']); - - $iParams = array("script_id" => array("REQUEST",tlInputParameter::STRING_N), - "tproject_id" => array("REQUEST",tlInputParameter::INT_N), - "tplan_id" => array("REQUEST",tlInputParameter::INT_N), - "tcversion_id" => array("REQUEST",tlInputParameter::INT_N), - "project_key" => array("POST",tlInputParameter::STRING_N), - "repository_name" => array("POST",tlInputParameter::STRING_N), - "code_path" => array("POST",tlInputParameter::STRING_N), - "branch_name" => array("POST",tlInputParameter::STRING_N), - "commit_id" => array("POST",tlInputParameter::STRING_N), - "projectKey" => array("POST",tlInputParameter::ARRAY_STRING_N), - "repositoryName" => array("POST",tlInputParameter::ARRAY_STRING_N), - "branchName" => array("POST",tlInputParameter::ARRAY_STRING_N), - "commits" => array("POST",tlInputParameter::ARRAY_STRING_N), - "files" => array("POST",tlInputParameter::ARRAY_STRING_N), - "expand_item" => array("POST",tlInputParameter::STRING_N), - "collapse_item" => array("POST",tlInputParameter::STRING_N), - "user_action" => array("REQUEST",tlInputParameter::STRING_N, - $user_action['minLength'],$user_action['maxLength'])); - - $args = new stdClass(); - I_PARAMS($iParams,$args); - - $args->user = $_SESSION['currentUser']; - - $gui = new stdClass(); - $gui->pageTitle = lang_get('title_script_add'); - - $gui->msg = ''; - $gui->tproject_id = $args->tproject_id; - $gui->tplan_id = $args->tplan_id; - $gui->tcversion_id = $args->tcversion_id; - $gui->user_action = $args->user_action; - - $gui->project_key = $args->project_key; - $gui->repository_name = $args->repository_name; - $gui->code_path = $args->code_path; - $gui->branch_name = $args->branch_name; - $gui->commit_id = $args->commit_id; - - // ----------------------------------------------------------------------- - // Special processing - list($ctObj,$ctCfg) = getCodeTracker($dbHandler,$args,$gui); - - $args->script_id = trim($args->script_id); - - return array($args,$gui,$ctObj,$ctCfg); -} - - -/** - * - */ -function getCodeTracker(&$dbHandler,$argsObj,&$guiObj) -{ - $cts = null; - $tprojectMgr = new testproject($dbHandler); - $info = $tprojectMgr->get_by_id($argsObj->tproject_id); - unset($tprojectMgr); - - $guiObj->codeTrackerCfg = new stdClass(); - $guiObj->codeTrackerCfg->createCodeURL = null; - $guiObj->codeTrackerCfg->VerboseID = ''; - $guiObj->codeTrackerCfg->VerboseType = ''; - - if($info['code_tracker_enabled']) - { - $ct_mgr = new tlCodeTracker($dbHandler); - $codeTrackerCfg = $ct_mgr->getLinkedTo($argsObj->tproject_id); - - if( !is_null($codeTrackerCfg) ) - { - $cts = $ct_mgr->getInterfaceObject($argsObj->tproject_id); - $guiObj->codeTrackerCfg->VerboseType = $codeTrackerCfg['verboseType']; - $guiObj->codeTrackerCfg->VerboseID = $codeTrackerCfg['codetracker_name']; - $guiObj->codeTrackerCfg->createCodeURL = $cts->getEnterCodeURL(); - } - } - return array($cts,$codeTrackerCfg); -} - -/** - * - */ -function write_testcase_script(&$dbHandler, &$cts, $tcversion_id, $project_key, - $repo_name, $code_path, $branch_name, $commit_id) -{ - $result = true; - - $repoCont = $cts->getRepoContent($project_key,$repo_name,$code_path,$branch_name,$commit_id); - if(property_exists($repoCont, 'errors')) - { - return false; - } - - $tbk = array('testcase_script_links'); - $tbl = tlObjectWithDB::getDBTables($tbk); - - //check if entry already exists in DB - $sql = " SELECT * FROM `{$tbl['testcase_script_links']}` " . - " WHERE `tcversion_id` = " .intval($tcversion_id) . - " AND `project_key` = '{$project_key}' " . - " AND `repository_name` = '{$repo_name}' " . - " AND `code_path` = '{$code_path}'"; - - $rs = $dbHandler->get_recordset($sql); - if (is_null($rs)) - { - $fields = "(`tcversion_id`,`project_key`,`repository_name`,`code_path`"; - $values = "(".intval($tcversion_id).",'{$project_key}'," . - "'{$repo_name}','{$code_path}'"; - if (!is_null($branch_name)) - { - $fields .= ",`branch_name`"; - $values .= ",'{$branch_name}'"; - } - if (!is_null($commit_id)) - { - $fields .= ",`commit_id`"; - $values .= ",'{$commit_id}'"; - } - $fields .= ")"; - $values .= ")"; - - //no entry found, i.e. add new values to DB - $sql = " INSERT INTO `{$tbl['testcase_script_links']}` {$fields} " . - " VALUES {$values}"; - - $result = $dbHandler->exec_query($sql); - } - - return $result; -} - -function write_cfield_testscript(&$dbHandler, &$user, $field_id, $node_id, $value) -{ - $tbk = array('cfield_design_values', 'tcversions','executions'); - $tbl = tlObjectWithDB::getDBTables($tbk); - - $result = false; - //check if tcversion can be edited - $sql = " SELECT id, active, is_open, baseline, reviewer_id " . - " FROM `{$tbl['tcversions']}` " . - " WHERE `id` = '{$node_id}'"; - - $rs = $dbHandler->get_recordset($sql); - - if(!is_null($rs)) - { - $rs = $rs[0]; - /* only update custom field value if test case can be - * modified, i.e. has no baseline, is not status "Accepted" - * and is active and open */ - if($rs['active'] == 1 && $rs['is_open'] == 1 && - is_null($rs['baseline']) && is_null($rs['reviewer_id'])) - { - $sql = " SELECT id FROM `{$tbl['executions']}` " . - " WHERE `tcversion_id` = '{$node_id}'"; - - $rsExec = $dbHandler->get_recordset($sql); - - /* only update if test case was not executed yet or - * corresponding modification right is given */ - if(is_null($rsExec) || - $user->hasRight($dbHandler,"testproject_edit_executed_testcases")) - { - //check if entry already exists in DB - $sql = " UPDATE `{$tbl['cfield_design_values']}` " . - " SET `value` = '{$value}' " . - " WHERE `field_id` = '{$field_id}' " . - " AND `node_id` = '{$node_id}'"; - - $result = $dbHandler->exec_query($sql); - } - } - } - - return $result; - -} - -/** - * Checks the user rights for viewing the page - * - * @param $db resource the database connection handle - * @param $user tlUser the object of the current user - * - * @return boolean return true if the page can be viewed, false if not - */ -function checkRights(&$db,&$user) -{ - $hasRights = $user->hasRight($db,"mgt_modify_tc"); - return $hasRights; +codeTrackerMetaData = array( + 'projects' => null, + 'repos' => null, + 'files' => null, + 'branches' => null, + 'commits' => null +); +if (! is_null($args->projectKey)) { + $gui->codeTrackerMetaData['projects'] = $args->projectKey; +} else { + $gui->codeTrackerMetaData['projects'] = $cts->getProjectsForHTMLSelect(); +} + +if (is_null($gui->project_key)) { + $gui->project_key = isset($_SESSION['testscript_projectKey']) ? $_SESSION['testscript_projectKey'] : $cts->cfg->projectkey; +} +if (is_null($gui->repository_name) && + isset($_SESSION['testscript_repositoryName']) && $args->user_action == 'link') { + $gui->repository_name = $_SESSION['testscript_repositoryName']; +} + +if (! is_null($args->repositoryName) && $args->user_action != 'projectSelected') { + $gui->codeTrackerMetaData['repos'] = $args->repositoryName; +} else { + $gui->codeTrackerMetaData['repos'] = $cts->getReposForHTMLSelect( + $gui->project_key); +} + +if (! is_null($args->branchName) && $args->user_action != 'projectSelected' && + $args->user_action != 'repoSelected') { + $gui->codeTrackerMetaData['branches'] = $args->branchName; +} else { + $gui->codeTrackerMetaData['branches'] = $cts->getBranchesForHTMLSelect( + $gui->project_key, $gui->repository_name); +} + +if (! is_null($args->commits) && $args->user_action != 'projectSelected' && + $args->user_action != 'repoSelected' && + $args->user_action != 'branchSelected') { + $gui->codeTrackerMetaData['commits'] = $args->commits; +} elseif (($args->user_action == 'branchSelected') || + (! is_null($gui->branch_name) && + ($args->user_action == 'expand' || $args->user_action == 'collapse'))) { + $gui->codeTrackerMetaData['commits'] = $cts->getCommitsForHTMLSelect( + $gui->project_key, $gui->repository_name, $gui->branch_name); +} + +if (! is_null($args->files) && + ($args->user_action == 'expand' || $args->user_action == 'collapse')) { + $gui->codeTrackerMetaData['files'] = $args->files; +} elseif ($args->user_action == 'repoSelected' || + $args->user_action == 'branchSelected' || $args->user_action == 'expand' || + $args->user_action == 'collapse' || + ($args->user_action == 'link' && ! is_null($gui->repository_name))) { + $gui->codeTrackerMetaData['files'] = $cts->getRepoContentForHTMLSelect( + $gui->project_key, $gui->repository_name, '', $gui->branch_name, + $gui->commit_id); +} +if ($args->user_action == 'expand') { + $expandArray = explode("/", $args->expand_item); + $tmpFileArray = &$gui->codeTrackerMetaData['files']; + $tmpPath = ""; + foreach ($expandArray as $item) { + $tmpPath .= $item . "/"; + $tmpFileArray[$item][0] = $cts->getRepoContentForHTMLSelect( + $gui->project_key, $gui->repository_name, $tmpPath, + $gui->branch_name, $gui->commit_id); + $tmpFileArray = &$tmpFileArray[$item][0]; + } +} elseif ($args->user_action == 'collapse') { + if (substr($args->collapse_item, - 1) == "/") { + $args->collapse_item = substr($args->collapse_item, 0, - 1); + } + $collapseArray = explode("/", $args->collapse_item); + array_pop($collapseArray); + + $tmpFileArray = &$gui->codeTrackerMetaData['files']; + $tmpPath = ""; + foreach ($collapseArray as $item) { + $tmpPath .= $item . "/"; + $tmpFileArray[$item][0] = $cts->getRepoContentForHTMLSelect( + $gui->project_key, $gui->repository_name, $tmpPath, + $gui->branch_name, $gui->commit_id); + $tmpFileArray = &$tmpFileArray[$item][0]; + } +} + +if ($args->user_action == 'create') { + if (! is_null($codeT) && $args->project_key != "" && + $args->repository_name != "" && $args->code_path != "") { + $l18n = init_labels(array( + "error_code_does_not_exist_on_cts" => null + )); + + $gui->msg = ""; + + $baseURL = $cts->buildViewCodeURL($args->project_key, + $args->repository_name, ''); + /* + * if code_path was entered manually it may contain the complete URL + * in this case we have to strip the base URL from the string + */ + if (substr($args->code_path, 0, strlen($baseURL)) === $baseURL) { + $args->code_path = substr($args->code_path, strlen($baseURL)); + } + // if code_path contains reference to certain branch, delete it + $refPos = strpos($args->code_path, '?at='); + if ($refPos !== false) { + // in case no branch was seleted try to extract branch name from reference if possible + $refStr = substr($args->code_path, $refPos); + if (strpos($refStr, '?at=refs%2Fheads%2F') !== false) { + $refStr = str_replace("?at=refs%2Fheads%2F", "", $refStr); + if (is_null($args->branch_name) && + (isset($args->branchName[$refStr]) || + array_key_exists($refStr, $args->branchName))) { + $args->branch_name = $refStr; + } + } else // no branch given, but commit_id -> try to extract this + { + $refStr = str_replace("?at=", "", $refStr); + if (is_null($args->commit_id)) { + $args->commit_id = $refStr; + } + } + + $args->code_path = substr($args->code_path, 0, $refPos); + } + + $scriptWritten = writeTestcaseScript($db, $cts, $args->tcversion_id, + $args->project_key, $args->repository_name, $args->code_path, + $args->branch_name, $args->commit_id); + + $auditMsg = "audit_testcasescript_added"; + $item_id = $args->tcversion_id; + $objectType = "testcase_script_links"; + $args->direct_link = $cts->buildViewCodeURL($args->project_key, + $args->repository_name, $args->code_path, $args->branch_name); + if ($scriptWritten) { + $gui->msg = lang_get("script_added"); + + // save user selection of project_key and repository_name to current session + $_SESSION['testscript_projectKey'] = $args->project_key; + $_SESSION['testscript_repositoryName'] = $args->repository_name; + + // update value of Custom Field "Test Script" if possible + $tScriptFieldID = null; + $tScriptFieldValue = null; + + $tcaseMgr = new testcase($db); + $linked_cfields = $tcaseMgr->cfield_mgr->get_linked_cfields_at_design( + $args->tproject_id, 1, null, 'testcase', $args->tcversion_id); + unset($tcaseMgr); + foreach ($linked_cfields as $cfieldID => $cfieldValue) { + if (is_null($tScriptFieldValue) && + strpos( + strtolower(str_replace(" ", "", $cfieldValue['name'])), + 'testscript') !== false) { + $tScriptFieldID = $cfieldID; + $tScriptFieldValue = $cfieldValue; + break; + } + } + + if (! is_null($tScriptFieldID)) { + $cfieldWritten = writeCfieldTestscript($db, $args->user, + $tScriptFieldID, $args->tcversion_id, $args->code_path); + } else { + $cfieldWritten = true; + } + + if (! $cfieldWritten) { + $gui->msg .= " - But Custom Field 'Test Script' could not be updated"; + } + + logAuditEvent(TLS($auditMsg, $args->direct_link), "CREATE", $item_id, + $objectType); + } else { + $gui->msg = sprintf($l18n["error_code_does_not_exist_on_cts"], + $args->direct_link); + } + } else { + $gui->msg = lang_get("error_script_not_added"); + } +} +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initEnv(&$dbHandler) +{ + $uaWhiteList = array(); + $uaWhiteList['elements'] = array( + 'link', + 'create', + 'projectSelected', + 'repoSelected', + 'branchSelected', + 'expand', + 'collapse' + ); + $uaWhiteList['length'] = array(); + foreach ($uaWhiteList['elements'] as $xmen) { + $uaWhiteList['length'][] = strlen($xmen); + } + $user_action['maxLength'] = max($uaWhiteList['length']); + $user_action['minLength'] = min($uaWhiteList['length']); + + $iParams = array( + "script_id" => array( + "REQUEST", + tlInputParameter::STRING_N + ), + "tproject_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "tplan_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "tcversion_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "project_key" => array( + "POST", + tlInputParameter::STRING_N + ), + "repository_name" => array( + "POST", + tlInputParameter::STRING_N + ), + "code_path" => array( + "POST", + tlInputParameter::STRING_N + ), + "branch_name" => array( + "POST", + tlInputParameter::STRING_N + ), + "commit_id" => array( + "POST", + tlInputParameter::STRING_N + ), + "projectKey" => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + "repositoryName" => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + "branchName" => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + "commits" => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + "files" => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ), + "expand_item" => array( + "POST", + tlInputParameter::STRING_N + ), + "collapse_item" => array( + "POST", + tlInputParameter::STRING_N + ), + "user_action" => array( + "REQUEST", + tlInputParameter::STRING_N, + $user_action['minLength'], + $user_action['maxLength'] + ) + ); + + $args = new stdClass(); + I_PARAMS($iParams, $args); + + $args->user = $_SESSION['currentUser']; + + $gui = new stdClass(); + $gui->pageTitle = lang_get('title_script_add'); + + $gui->msg = ''; + $gui->tproject_id = $args->tproject_id; + $gui->tplan_id = $args->tplan_id; + $gui->tcversion_id = $args->tcversion_id; + $gui->user_action = $args->user_action; + + $gui->project_key = $args->project_key; + $gui->repository_name = $args->repository_name; + $gui->code_path = $args->code_path; + $gui->branch_name = $args->branch_name; + $gui->commit_id = $args->commit_id; + + // ----------------------------------------------------------------------- + // Special processing + list ($ctObj, $ctCfg) = getCodeTracker($dbHandler, $args, $gui); + + $args->script_id = trim($args->script_id); + + return array( + $args, + $gui, + $ctObj, + $ctCfg + ); +} + +/** + */ +function getCodeTracker(&$dbHandler, $argsObj, &$guiObj) +{ + $cts = null; + $tprojectMgr = new testproject($dbHandler); + $info = $tprojectMgr->get_by_id($argsObj->tproject_id); + unset($tprojectMgr); + + $guiObj->codeTrackerCfg = new stdClass(); + $guiObj->codeTrackerCfg->createCodeURL = null; + $guiObj->codeTrackerCfg->VerboseID = ''; + $guiObj->codeTrackerCfg->VerboseType = ''; + + if ($info['code_tracker_enabled']) { + $ct_mgr = new tlCodeTracker($dbHandler); + $codeTrackerCfg = $ct_mgr->getLinkedTo($argsObj->tproject_id); + + if (! is_null($codeTrackerCfg)) { + $cts = $ct_mgr->getInterfaceObject($argsObj->tproject_id); + $guiObj->codeTrackerCfg->VerboseType = $codeTrackerCfg['verboseType']; + $guiObj->codeTrackerCfg->VerboseID = $codeTrackerCfg['codetracker_name']; + $guiObj->codeTrackerCfg->createCodeURL = $cts->getEnterCodeURL(); + } + } + return array( + $cts, + $codeTrackerCfg + ); +} + +/** + */ +function writeTestcaseScript(&$dbHandler, &$cts, $tcversion_id, $project_key, + $repo_name, $code_path, $branch_name, $commit_id) +{ + $result = true; + + $repoCont = $cts->getRepoContent($project_key, $repo_name, $code_path, + $branch_name, $commit_id); + if (property_exists($repoCont, 'errors')) { + return false; + } + + $tbk = array( + 'testcase_script_links' + ); + $tbl = tlObjectWithDB::getDBTables($tbk); + + // check if entry already exists in DB + $sql = " SELECT * FROM `{$tbl['testcase_script_links']}` " . + " WHERE `tcversion_id` = " . intval($tcversion_id) . + " AND `project_key` = '{$project_key}' " . + " AND `repository_name` = '{$repo_name}' " . + " AND `code_path` = '{$code_path}'"; + + $rs = $dbHandler->get_recordset($sql); + if (is_null($rs)) { + $fields = "(`tcversion_id`,`project_key`,`repository_name`,`code_path`"; + $values = "(" . intval($tcversion_id) . ",'{$project_key}'," . + "'{$repo_name}','{$code_path}'"; + if (! is_null($branch_name)) { + $fields .= ",`branch_name`"; + $values .= ",'{$branch_name}'"; + } + if (! is_null($commit_id)) { + $fields .= ",`commit_id`"; + $values .= ",'{$commit_id}'"; + } + $fields .= ")"; + $values .= ")"; + + // no entry found, i.e. add new values to DB + $sql = " INSERT INTO `{$tbl['testcase_script_links']}` {$fields} " . + " VALUES {$values}"; + + $result = $dbHandler->exec_query($sql); + } + + return $result; +} + +function writeCfieldTestscript(&$dbHandler, &$user, $field_id, $node_id, $value) +{ + $tbk = array( + 'cfield_design_values', + 'tcversions', + 'executions' + ); + $tbl = tlObjectWithDB::getDBTables($tbk); + + $result = false; + // check if tcversion can be edited + $sql = " SELECT id, active, is_open, baseline, reviewer_id " . + " FROM `{$tbl['tcversions']}` " . " WHERE `id` = '{$node_id}'"; + + $rs = $dbHandler->get_recordset($sql); + + if (! is_null($rs)) { + $rs = $rs[0]; + /* + * only update custom field value if test case can be + * modified, i.e. has no baseline, is not status "Accepted" + * and is active and open + */ + if ($rs['active'] == 1 && $rs['is_open'] == 1 && + is_null($rs['baseline']) && is_null($rs['reviewer_id'])) { + $sql = " SELECT id FROM `{$tbl['executions']}` " . + " WHERE `tcversion_id` = '{$node_id}'"; + + $rsExec = $dbHandler->get_recordset($sql); + + /* + * only update if test case was not executed yet or + * corresponding modification right is given + */ + if (is_null($rsExec) || + $user->hasRight($dbHandler, + "testproject_edit_executed_testcases")) { + // check if entry already exists in DB + $sql = " UPDATE `{$tbl['cfield_design_values']}` " . + " SET `value` = '{$value}' " . + " WHERE `field_id` = '{$field_id}' " . + " AND `node_id` = '{$node_id}'"; + + $result = $dbHandler->exec_query($sql); + } + } + } + + return $result; +} + +/** + * Checks the user rights for viewing the page + * + * @param $db resource + * the database connection handle + * @param $user tlUser + * the object of the current user + * + * @return boolean return true if the page can be viewed, false if not + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "mgt_modify_tc"); } diff --git a/lib/testcases/scriptDelete.php b/lib/testcases/scriptDelete.php index f54020ebad..9be6acacaa 100755 --- a/lib/testcases/scriptDelete.php +++ b/lib/testcases/scriptDelete.php @@ -1,167 +1,170 @@ -project_key != "" && $args->repository_name != "" && $args->code_path != "") -{ - $l18n = init_labels(array("error_code_does_not_exist_on_cts" => null)); - - $gui->msg = ""; - $scriptDeleted = del_testcase_script($db,$args->tcversion_id, $args->project_key, - $args->repository_name, $args->code_path); - $auditMsg = "audit_testcasescript_deleted"; - $item_id = $args->tcversion_id; - $objectType = "testcase_script_links"; - $args->direct_link = $cts->buildViewCodeURL($args->project_key,$args->repository_name,$args->code_path,$args->branch_name); - if ($scriptDeleted) - { - $gui->msg = lang_get("scriptdeleting_was_ok"); - logAuditEvent(TLS($auditMsg,$args->script_id),"DELETE",$item_id,$objectType); - } - else - { - $gui->msg = sprintf($l18n["error_code_does_not_exist_on_cts"],$args->direct_link); - } -} -else -{ - $gui->msg = lang_get("error_script_not_deleted"); -} -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -function initEnv(&$dbHandler) -{ - $iParams = array("script_id" => array("REQUEST",tlInputParameter::STRING_N), - "tproject_id" => array("REQUEST",tlInputParameter::INT_N), - "tcversion_id" => array("REQUEST",tlInputParameter::INT_N), - "project_key" => null, - "repository_name" => null, - "code_path" => null); - - $args = new stdClass(); - I_PARAMS($iParams,$args); - - $args->user = $_SESSION['currentUser']; - - $args->script_id = trim($args->script_id); - - $scriptArray = explode("&&", $args->script_id); - if(count($scriptArray) > 0) - { - $args->project_key = $scriptArray[0]; - $args->repository_name = $scriptArray[1]; - $args->code_path = $scriptArray[2]; - } - - if (is_null($args->tproject_id)) - { - $args->tproject_id = $_SESSION['testprojectID']; - } - $gui = new stdClass(); - $gui->pageTitle = lang_get('title_delete_script'); - - $gui->msg = ''; - $gui->tcversion_id = $args->tcversion_id; - - $gui->project_key = $args->project_key; - $gui->repository_name = $args->repository_name; - $gui->code_path = $args->code_path; - - // ----------------------------------------------------------------------- - // Special processing - list($ctObj,$ctCfg) = getCodeTracker($dbHandler,$args,$gui); - - $args->basehref = $_SESSION['basehref']; - - return array($args,$gui,$ctObj,$ctCfg); -} - - -/** - * - */ -function getCodeTracker(&$dbHandler,$argsObj,&$guiObj) -{ - $cts = null; - $tprojectMgr = new testproject($dbHandler); - $info = $tprojectMgr->get_by_id($argsObj->tproject_id); - - $guiObj->codeTrackerCfg = new stdClass(); - $guiObj->codeTrackerCfg->createCodeURL = null; - $guiObj->codeTrackerCfg->VerboseID = ''; - $guiObj->codeTrackerCfg->VerboseType = ''; - - if($info['code_tracker_enabled']) - { - $ct_mgr = new tlCodeTracker($dbHandler); - $codeTrackerCfg = $ct_mgr->getLinkedTo($argsObj->tproject_id); - - if( !is_null($codeTrackerCfg) ) - { - $cts = $ct_mgr->getInterfaceObject($argsObj->tproject_id); - $guiObj->codeTrackerCfg->VerboseType = $codeTrackerCfg['verboseType']; - $guiObj->codeTrackerCfg->VerboseID = $codeTrackerCfg['codetracker_name']; - $guiObj->codeTrackerCfg->createCodeURL = $cts->getEnterCodeURL(); - } - } - return array($cts,$codeTrackerCfg); -} - -/** - * - */ -function del_testcase_script(&$dbHandler, $tcversion_id, $project_key, - $repo_name, $code_path) -{ - $fields = "(`tcversion_id`,`project_key`,`repository_name`,`code_path`"; - $values = "(".intval($tcversion_id).",'{$project_key}'," . - "'{$repo_name}','{$code_path}'"; - $fields .= ")"; - $values .= ")"; - - $tbk = array('testcase_script_links'); - $tbl = tlObjectWithDB::getDBTables($tbk); - $sql = " DELETE FROM `{$tbl['testcase_script_links']}` " . - " WHERE `tcversion_id` = " . intval($tcversion_id) . - " AND `project_key` = '{$project_key}' " . - " AND `repository_name` = '{$repo_name}' " . - " AND `code_path` = '{$code_path}' "; - - $result = $dbHandler->exec_query($sql); - - return $result; -} - -/** - * Checks the user rights for viewing the page - * - * @param $db resource the database connection handle - * @param $user tlUser the object of the current user - * - * @return boolean return true if the page can be viewed, false if not - */ -function checkRights(&$db,&$user) -{ - $hasRights = $user->hasRight($db,"mgt_modify_tc"); - return $hasRights; +project_key != "" && $args->repository_name != "" && + $args->code_path != "") { + $l18n = init_labels(array( + "error_code_does_not_exist_on_cts" => null + )); + + $gui->msg = ""; + $scriptDeleted = delTestcaseScript($db, $args->tcversion_id, + $args->project_key, $args->repository_name, $args->code_path); + $auditMsg = "audit_testcasescript_deleted"; + $item_id = $args->tcversion_id; + $objectType = "testcase_script_links"; + $args->direct_link = $cts->buildViewCodeURL($args->project_key, + $args->repository_name, $args->code_path, $args->branch_name); + if ($scriptDeleted) { + $gui->msg = lang_get("scriptdeleting_was_ok"); + logAuditEvent(TLS($auditMsg, $args->script_id), "DELETE", $item_id, + $objectType); + } else { + $gui->msg = sprintf($l18n["error_code_does_not_exist_on_cts"], + $args->direct_link); + } +} else { + $gui->msg = lang_get("error_script_not_deleted"); +} +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + */ +function initEnv(&$dbHandler) +{ + $iParams = array( + "script_id" => array( + "REQUEST", + tlInputParameter::STRING_N + ), + "tproject_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "tcversion_id" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "project_key" => null, + "repository_name" => null, + "code_path" => null + ); + + $args = new stdClass(); + I_PARAMS($iParams, $args); + + $args->user = $_SESSION['currentUser']; + + $args->script_id = trim($args->script_id); + + $scriptArray = explode("&&", $args->script_id); + if (! empty($scriptArray)) { + $args->project_key = $scriptArray[0]; + $args->repository_name = $scriptArray[1]; + $args->code_path = $scriptArray[2]; + } + + if (is_null($args->tproject_id)) { + $args->tproject_id = $_SESSION['testprojectID']; + } + $gui = new stdClass(); + $gui->pageTitle = lang_get('title_delete_script'); + + $gui->msg = ''; + $gui->tcversion_id = $args->tcversion_id; + + $gui->project_key = $args->project_key; + $gui->repository_name = $args->repository_name; + $gui->code_path = $args->code_path; + + // Special processing + list ($ctObj, $ctCfg) = getCodeTracker($dbHandler, $args, $gui); + + $args->basehref = $_SESSION['basehref']; + + return array( + $args, + $gui, + $ctObj, + $ctCfg + ); +} + +/** + */ +function getCodeTracker(&$dbHandler, $argsObj, &$guiObj) +{ + $cts = null; + $tprojectMgr = new testproject($dbHandler); + $info = $tprojectMgr->get_by_id($argsObj->tproject_id); + + $guiObj->codeTrackerCfg = new stdClass(); + $guiObj->codeTrackerCfg->createCodeURL = null; + $guiObj->codeTrackerCfg->VerboseID = ''; + $guiObj->codeTrackerCfg->VerboseType = ''; + + if ($info['code_tracker_enabled']) { + $ct_mgr = new tlCodeTracker($dbHandler); + $codeTrackerCfg = $ct_mgr->getLinkedTo($argsObj->tproject_id); + + if (! is_null($codeTrackerCfg)) { + $cts = $ct_mgr->getInterfaceObject($argsObj->tproject_id); + $guiObj->codeTrackerCfg->VerboseType = $codeTrackerCfg['verboseType']; + $guiObj->codeTrackerCfg->VerboseID = $codeTrackerCfg['codetracker_name']; + $guiObj->codeTrackerCfg->createCodeURL = $cts->getEnterCodeURL(); + } + } + return array( + $cts, + $codeTrackerCfg + ); +} + +/** + */ +function delTestcaseScript(&$dbHandler, $tcversion_id, $project_key, $repo_name, + $code_path) +{ + $tbk = array( + 'testcase_script_links' + ); + $tbl = tlObjectWithDB::getDBTables($tbk); + $sql = " DELETE FROM `{$tbl['testcase_script_links']}` " . + " WHERE `tcversion_id` = " . intval($tcversion_id) . + " AND `project_key` = '{$project_key}' " . + " AND `repository_name` = '{$repo_name}' " . + " AND `code_path` = '{$code_path}' "; + + return $dbHandler->exec_query($sql); +} + +/** + * Checks the user rights for viewing the page + * + * @param $db resource + * the database connection handle + * @param $user tlUser + * the object of the current user + * + * @return boolean return true if the page can be viewed, false if not + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "mgt_modify_tc"); } diff --git a/lib/testcases/tcAssign2Tplan.php b/lib/testcases/tcAssign2Tplan.php index acfd3e664f..468da66872 100644 --- a/lib/testcases/tcAssign2Tplan.php +++ b/lib/testcases/tcAssign2Tplan.php @@ -1,211 +1,201 @@ -glue_character; -$args = init_args(); -$gui = initializeGui($args); -$getOpt = array('outputFormat' => 'map', 'addIfNull' => true); -$gui->platformSet = $tplan_mgr->getPlatforms($args->tplan_id,$getOpt); - -$options['output'] = 'essential'; -$tcase_all_info = $tcase_mgr->get_by_id($args->tcase_id,testcase::ALL_VERSIONS,null,$options); - -if( !is_null($tcase_all_info) ) -{ - foreach($tcase_all_info as $tcversion_info) - { - if($tcversion_info['id'] == $args->tcversion_id ) - { - $version = $tcversion_info['version']; - $gui->pageTitle=lang_get('test_case') . ':' . $tcversion_info['name']; - $gui->tcaseIdentity = $tproject_mgr->getTestCasePrefix($args->tproject_id); - $gui->tcaseIdentity .= $glue . $tcversion_info['tc_external_id'] . ':' . $tcversion_info['name']; - break; - } - } +glue_character; +$args = initArgs(); +$gui = initializeGui($args); +$getOpt = array( + 'outputFormat' => 'map', + 'addIfNull' => true +); +$gui->platformSet = $tplan_mgr->getPlatforms($args->tplan_id, $getOpt); + +$options['output'] = 'essential'; +$tcase_all_info = $tcaseMgr->get_by_id($args->tcase_id, testcase::ALL_VERSIONS, + null, $options); + +if (! is_null($tcase_all_info)) { + foreach ($tcase_all_info as $tcversion_info) { + if ($tcversion_info['id'] == $args->tcversion_id) { + $version = $tcversion_info['version']; + $gui->pageTitle = lang_get('test_case') . ':' . + $tcversion_info['name']; + $gui->tcaseIdentity = $tproject_mgr->getTestCasePrefix( + $args->tproject_id); + $gui->tcaseIdentity .= $glue . $tcversion_info['tc_external_id'] . + ':' . $tcversion_info['name']; + break; + } + } +} + +$link_info = $tcaseMgr->get_linked_versions($args->tcase_id); +if (! is_null( + $tplanSet = $tproject_mgr->get_all_testplans($args->tproject_id, + array( + 'plan_status' => 1 + )))) { + $has_links = array_fill_keys(array_keys($tplanSet), false); + $linked_tplans = null; + if (! is_null($link_info)) { + foreach ($link_info as $info) { + foreach ($info as $tplan_id => $platform_info) { + $has_links[$tplan_id] = true; + foreach ($platform_info as $platform_id => $value) { + $linked_tplans[$tplan_id][$platform_id]['tcversion_id'] = $value['tcversion_id']; + $linked_tplans[$tplan_id][$platform_id]['version'] = $value['version']; + $linked_tplans[$tplan_id][$platform_id]['draw_checkbox'] = false; + } + } + } + } + + // Initial situation, enable link of target test case version to all test plans + $getOpt = array( + 'outputFormat' => 'map', + 'addIfNull' => true + ); + foreach ($tplanSet as $tplan_id => $value) { + $gui->tplans[$tplan_id] = array(); + $platformSet = $tplan_mgr->getPlatforms($tplan_id, $getOpt); + + $target_version_number = $version; + $target_version_id = $args->tcversion_id; + $linked_platforms = null; + + // if a version of this Test Case has been linked to test plan, get it. + if ($has_links[$tplan_id]) { + $linked_platforms = array_flip( + array_keys($linked_tplans[$tplan_id])); + $dummy = current($linked_tplans[$tplan_id]); + $target_version_number = $dummy['version']; + $target_version_id = $dummy['tcversion_id']; + } + + // do logic on test plan linked platforms to understand what to display + // For situation like + // Test Plan TPX - Platforms: P1,P2,P3 + // Test Case A - version 1 -> Test Plan TPX - Platform P1 + // + // Create Test Case A - version 2 + // + // Add to test plan on version 2 + // We CAN NOT DISPLAY Platforms P2 and P3, because P1 has been linked to version 1 + // and we DO NOT ALLOW different test case versions to be linked to ONE TEST PLAN. + // Then we need to display only + // [x](read only) version 1 - test plan TPX - platform P1 + // + // But if we go to version 1 and choose add to test plan, will display: + // [x](read only) version 1 - test plan TPX - platform P1 + // [ ] version 1 - test plan TPX - platform P2 + // [ ] version 1 - test plan TPX - platform P3 + // + // Then we can add version 1 to other platform + // Following logic try to implement this. + // + foreach ($platformSet as $platform_id => $platform_info) { + $doAdd = true; + $draw_checkbox = true; + if ($has_links[$tplan_id]) { + if (isset($linked_platforms[$platform_id])) { + $draw_checkbox = false; + } elseif ($target_version_number == $version) { + $draw_checkbox = true; + } else { + $doAdd = false; + } + } + if ($doAdd) { + $gui->tplans[$tplan_id][$platform_id] = $value; + $gui->tplans[$tplan_id][$platform_id]['tcversion_id'] = $target_version_id; + $gui->tplans[$tplan_id][$platform_id]['version'] = $target_version_number; + $gui->tplans[$tplan_id][$platform_id]['draw_checkbox'] = $draw_checkbox; + $gui->tplans[$tplan_id][$platform_id]['platform'] = $platform_info; + } + } + } + + // Check if submit button can be displayed. + // Condition there is at least one test plan where no version of + // target test cases has been linked. + $gui->can_do = false; // because an OR logic will be used + foreach ($gui->tplans as $tplan_id => $platform_info) { + foreach ($platform_info as $platform_id => $value) { + $gui->can_do = $gui->can_do || + $gui->tplans[$tplan_id][$platform_id]['draw_checkbox']; + } + } +} +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * init_args + * creates a sort of namespace + * + * @return stdClass object with some REQUEST and SESSION values as members. + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + // if any piece of context is missing => we will display nothing instead of crashing WORK TO BE DONE + $args = new stdClass(); + $args->tplan_id = isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']; + $args->tproject_id = isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']; + + $args->tproject_id = intval($args->tproject_id); + $args->tplan_id = intval($args->tplan_id); + + $args->tcase_id = isset($_REQUEST['tcase_id']) ? $_REQUEST['tcase_id'] : 0; + $args->tcase_id = intval($args->tcase_id); + + $args->tcversion_id = isset($_REQUEST['tcversion_id']) ? $_REQUEST['tcversion_id'] : 0; + $args->tcversion_id = intval($args->tcversion_id); + + return $args; +} + +/** + * + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui($argsObj) +{ + $guiObj = new stdClass(); + $guiObj->pageTitle = ''; + $guiObj->tcaseIdentity = ''; + $guiObj->mainDescription = lang_get('add_tcversion_to_plans'); + $guiObj->tcase_id = $argsObj->tcase_id; + $guiObj->tcversion_id = $argsObj->tcversion_id; + $guiObj->can_do = false; + $guiObj->item_sep = config_get('gui')->title_separator_2; + $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . + "lib/testcases/archiveData.php?" . 'edit=testcase&id=' . + intval($argsObj->tcase_id) . "'"; + return $guiObj; } - -$link_info = $tcase_mgr->get_linked_versions($args->tcase_id); -if( !is_null($tplanSet = $tproject_mgr->get_all_testplans($args->tproject_id,array('plan_status' => 1))) ) -{ - $has_links = array_fill_keys(array_keys($tplanSet),false); - $linked_tplans = null; - if( !is_null($link_info) ) - { - foreach($link_info as $tcversion_id => $info) - { - foreach($info as $tplan_id => $platform_info) - { - $has_links[$tplan_id] = true; - foreach($platform_info as $platform_id => $value) - { - $linked_tplans[$tplan_id][$platform_id]['tcversion_id']=$value['tcversion_id']; - $linked_tplans[$tplan_id][$platform_id]['version']=$value['version']; - $linked_tplans[$tplan_id][$platform_id]['draw_checkbox'] = false; - } - } - } - } - - // Initial situation, enable link of target test case version to all test plans - $getOpt = array('outputFormat' => 'map', 'addIfNull' => true); - foreach($tplanSet as $tplan_id => $value) - { - $gui->tplans[$tplan_id] = array(); - $platformSet = $tplan_mgr->getPlatforms($tplan_id,$getOpt); - - // $target_version_number = 0; - // $target_version_id = 0; - $target_version_number = $version; - $target_version_id = $args->tcversion_id; - $linked_platforms = null; - - // if a version of this Test Case has been linked to test plan, get it. - if( $has_links[$tplan_id] ) - { - $linked_platforms = array_flip(array_keys($linked_tplans[$tplan_id])); - $dummy = current($linked_tplans[$tplan_id]); - $target_version_number = $dummy['version']; - $target_version_id = $dummy['tcversion_id']; - } - - // do logic on test plan linked platforms to understand what to display - // For situation like - // Test Plan TPX - Platforms: P1,P2,P3 - // Test Case A - version 1 -> Test Plan TPX - Platform P1 - // - // Create Test Case A - version 2 - // - // Add to test plan on version 2 - // We CAN NOT DISPLAY Platforms P2 and P3, because P1 has been linked to version 1 - // and we DO NOT ALLOW different test case versions to be linked to ONE TEST PLAN. - // Then we need to display only - // [x](read only) version 1 - test plan TPX - platform P1 - // - // But if we go to version 1 and choose add to test plan, will display: - // [x](read only) version 1 - test plan TPX - platform P1 - // [ ] version 1 - test plan TPX - platform P2 - // [ ] version 1 - test plan TPX - platform P3 - // - // Then we can add version 1 to other platform - // Following logic try to implement this. - // - foreach($platformSet as $platform_id => $platform_info) - { - $doAdd = true; - $draw_checkbox = true; - if( $has_links[$tplan_id] ) - { - if( isset($linked_platforms[$platform_id]) ) - { - $draw_checkbox = false; - } - else if($target_version_number == $version) - { - $draw_checkbox = true; - } - else - { - $doAdd = false; - } - } - if( $doAdd ) - { - $gui->tplans[$tplan_id][$platform_id] = $value; - $gui->tplans[$tplan_id][$platform_id]['tcversion_id'] = $target_version_id; - $gui->tplans[$tplan_id][$platform_id]['version'] = $target_version_number; - $gui->tplans[$tplan_id][$platform_id]['draw_checkbox'] = $draw_checkbox; - $gui->tplans[$tplan_id][$platform_id]['platform'] = $platform_info; - } - - } - } - - // Check if submit button can be displayed. - // Condition there is at least one test plan where no version of - // target test cases has been linked. - $gui->can_do=false; // because an OR logic will be used - foreach($gui->tplans as $tplan_id => $platform_info) - { - foreach($platform_info as $platform_id => $value) - { - $gui->can_do = $gui->can_do || $gui->tplans[$tplan_id][$platform_id]['draw_checkbox']; - } - - } -} -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * init_args - * creates a sort of namespace - * - * @return object with some REQUEST and SESSION values as members. - */ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - // if any piece of context is missing => we will display nothing instead of crashing WORK TO BE DONE - $args = new stdClass(); - $args->tplan_id = isset($_REQUEST['tplan_id']) ? $_REQUEST['tplan_id'] : $_SESSION['testplanID']; - $args->tproject_id = isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : $_SESSION['testprojectID']; - - $args->tproject_id = intval($args->tproject_id); - $args->tplan_id = intval($args->tplan_id); - - - $args->tcase_id = isset($_REQUEST['tcase_id']) ? $_REQUEST['tcase_id'] : 0; - $args->tcase_id = intval($args->tcase_id); - - $args->tcversion_id = isset($_REQUEST['tcversion_id']) ? $_REQUEST['tcversion_id'] : 0; - $args->tcversion_id = intval($args->tcversion_id); - - return $args; -} - - -/** - * - * - */ -function initializeGui($argsObj) -{ - $guiObj = new stdClass(); - $guiObj->pageTitle=''; - $guiObj->tcaseIdentity=''; - $guiObj->mainDescription=lang_get('add_tcversion_to_plans');; - $guiObj->tcase_id=$argsObj->tcase_id; - $guiObj->tcversion_id=$argsObj->tcversion_id; - $guiObj->can_do=false; - $guiObj->item_sep=config_get('gui')->title_separator_2; - $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . "lib/testcases/archiveData.php?" . - 'edit=testcase&id=' . intval($argsObj->tcase_id) . "'"; - return $guiObj; -} \ No newline at end of file diff --git a/lib/testcases/tcAssignedToUser.php b/lib/testcases/tcAssignedToUser.php index 3bf85dea72..67acb5a0da 100644 --- a/lib/testcases/tcAssignedToUser.php +++ b/lib/testcases/tcAssignedToUser.php @@ -1,514 +1,573 @@ -getImages(); - -$args = init_args($db); -$gui = initializeGui($db,$args); -$statusGui = getStatusGuiCfg(); - - -// Get all test cases assigned to user without filtering by execution status -$opt = array('mode' => 'full_path'); -$filters = initFilters($args); -$tplan_param = ($args->tplan_id) ? array($args->tplan_id) : testcase::ALL_TESTPLANS; - -$tcase_mgr = new testcase($db); -$gui->resultSet = $tcase_mgr->get_assigned_to_user($args->user_id, $args->tproject_id, - $tplan_param, $opt, $filters); - -$doIt = !is_null($gui->resultSet); - -// will work only on standard exec status -$exec = getQuickExecCfg($gui,$imgSet,$statusGui->status_code); - -$tables = tlObjectWithDB::getDBTables(array('nodes_hierarchy','executions','tcversions')); - -if($args->result != '' && $args->tcvx > 0) -{ - - // get version number - $sql = " SELECT TCV.version FROM {$tables['tcversions']} TCV WHERE TCV.id = " . $args->tcvx; - $xx = $db->get_recordset($sql); - $version_number = $xx[0]['version']; - - $sql = " INSERT INTO {$tables['executions']} ". - " (status,tester_id,execution_ts,tcversion_id,tcversion_number,testplan_id,platform_id,build_id)". - " VALUES ('{$args->result}', {$args->executedBy}, " . $db->db_now() . "," . - " {$args->tcvx}, {$version_number}, {$args->tpx}, {$args->pxi},{$args->bxi})"; - - $db->exec_query($sql); -} - - -if( $doIt ) -{ - $execCfg = config_get('exec_cfg'); - - // has logged user right to execute test cases on this test plan? - $hasExecRight = - $_SESSION['currentUser']->hasRight($db,'testplan_execute',null,$args->tplan_id); - - $tables = tlObjectWithDB::getDBTables(array('nodes_hierarchy')); - $tplanSet=array_keys($gui->resultSet); - $sql="SELECT name,id FROM {$tables['nodes_hierarchy']} " . - "WHERE id IN (" . implode(',',$tplanSet) . ")"; - $gui->tplanNames=$db->fetchRowsIntoMap($sql,'id'); - $optColumns = array('user' => $args->show_user_column, 'priority' => $args->priority_enabled); - - $whoiam = $args->show_all_users ? 'tcAssignedToUser': 'tcAssignedToMe'; - - foreach ($gui->resultSet as $tplan_id => $tcase_set) - { - list($columns,$sortByColumn,$show_platforms) = getColumnsDefinition($db,$tplan_id,$optColumns); - - $rows = array(); - - foreach ($tcase_set as $tcase_platform) - { - - foreach ($tcase_platform as $tcase) - { - $current_row = array(); - $tcase_id = $tcase['testcase_id']; - $tcversion_id = $tcase['tcversion_id']; - - if ($args->show_user_column) - { - if($tcase['user_id'] > 0 && isset($args->userSet[$tcase['user_id']])) - { - $current_row[] = htmlspecialchars($args->userSet[$tcase['user_id']]['login']); - } - else - { - $current_row[] = ''; - } - } - - $current_row[] = htmlspecialchars($tcase['build_name']); - $current_row[] = htmlspecialchars($tcase['tcase_full_path']); - - // create linked icons - $ekk = $elk = $exec_link = ''; - $canExec = $hasExecRight == 'yes'; - if($execCfg->exec_mode->tester == 'assigned_to_me') - { - $canExec = $canExec && ($tcase['user_id'] == $_SESSION['userID']); - } - - if($canExec) - { - $ekk = sprintf($exec['common'],$tplan_id,$tcase['platform_id'],$tplan_id,$tcase['build_id'], - $tplan_id,$tcversion_id,$tplan_id); - - $elk = sprintf($exec['passed'],$tplan_id) . $ekk . ' ' . sprintf($exec['failed'],$tplan_id ) . $ekk . ' ' . - sprintf($exec['blocked'],$tplan_id) . $ekk; - - $exec_link = "" . - "l18n['execution']}\" src=\"{$imgSet['exec_icon']}\" /> "; - } - - $exec_history_link = "" . - "l18n['execution_history']}\" src=\"{$imgSet['history_small']}\" /> "; - - - $edit_link = "" . - "l18n['design']}\" src=\"{$imgSet['edit_icon']}\" /> "; - - $current_row[] = "" . $elk . $exec_history_link . - $exec_link . $edit_link . htmlspecialchars($tcase['prefix']) . $gui->glueChar . - $tcase['tc_external_id'] . " : " . htmlspecialchars($tcase['name']) . - sprintf($gui->l18n['tcversion_indicator'],$tcase['version']); - - if ($show_platforms) - { - $current_row[] = htmlspecialchars($tcase['platform_name']); - } - - if ($args->priority_enabled) - { - $current_row[] = "" . $gui->priority[priority_to_level($tcase['priority'])]; - } - - $leOptions = array('getSteps' => 0); - $lexec = $tcase_mgr->get_last_execution($tcase_id, $tcversion_id, $tplan_id, - $tcase['build_id'],$tcase['platform_id'], - $leOptions); - $status = $lexec[$tcversion_id]['status']; - if (!$status) - { - $status = $statusGui->status_code['not_run']; - } - $current_row[] = $statusGui->definition[$status]; - - if ($args->show_user_column) - { - $current_row[] = htmlspecialchars($lexec[$tcversion_id]['tester_login']); - } - - - - // need to check if we are using the right timestamp - $current_row[] = htmlspecialchars($tcase['creation_ts']) . - " (" . get_date_diff($tcase['creation_ts']) . ")"; - - $rows[] = $current_row; - } - } - - /* different table id for different reports: - * - Assignment Overview if $args->show_all_users is set - * - Test Cases assigned to user if $args->build_id > 0 - * - Test Cases assigned to me else - */ - $table_id = "tl_table_tc_assigned_to_me_for_tplan_"; - if($args->show_all_users) { - $table_id = "tl_table_tc_assignment_overview_for_tplan_"; - } - if($args->build_id) { - $table_id = "tl_table_tc_assigned_to_user_for_tplan_"; - } - - // add test plan id to table id - $table_id .= $tplan_id; - - $matrix = new tlExtTable($columns, $rows, $table_id); - $matrix->title = $gui->l18n['testplan'] . ": " . htmlspecialchars($gui->tplanNames[$tplan_id]['name']); - - // default grouping by first column, which is user for overview, build otherwise - $matrix->setGroupByColumnName(lang_get($columns[0]['title_key'])); - - // make table collapsible if more than 1 table is shown and surround by frame - if (count($tplanSet) > 1) { - $matrix->collapsible = true; - $matrix->frame = true; - } - - // define toolbar - $matrix->showToolbar = true; - $matrix->toolbarExpandCollapseGroupsButton = true; - $matrix->toolbarShowAllColumnsButton = true; - - $matrix->setSortByColumnName($sortByColumn); - $matrix->sortDirection = 'DESC'; - $gui->tableSet[$tplan_id] = $matrix; - } -} - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * Replacement for the smarty helper function to get that functionality outside of templates. - * Returns difference between a given date and the current time in days. - * @author Andreas Simon - * @param $date - */ -function get_date_diff($date) -{ - $date = (is_string($date)) ? strtotime($date) : $date; - $i = 1/60/60/24; - return floor((time() - $date) * $i); -} - - -/** - * init_args() - * Get in an object all data that has arrived to page through _REQUEST or _SESSION. - * If you think this page as a function, you can consider this data arguments (args) - * to a function call. - * Using all this data as one object property will help developer to understand - * if data is received or produced on page. - * - * @author franciscom - francisco.mancardi@gmail.com - * @args - used global coupling accessing $_REQUEST and $_SESSION - * - * @return object of stdClass - * - * @internal revisions - */ -function init_args(&$dbHandler) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - - $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval($_REQUEST['tproject_id']) : 0; - if( $args->tproject_id == 0) - { - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - } - if( $args->tproject_id == 0) - { - throw new Exception(__FILE__ . ' Can not work without Test project ID => Aborting'); - } - $mgr = new testproject($dbHandler); - $info = $mgr->get_by_id($args->tproject_id); - $args->tproject_name = $info['name']; - $args->testprojectOptions = $info['opt']; - unset($info); - - $args->user_id = isset($_REQUEST['user_id']) ? intval($_REQUEST['user_id']) : 0; - - if( $args->user_id != 0) - { - $args->user = new tlUser($args->user_id); - $args->user->readFromDB($dbHandler); - } - else - { - $args->user_id = isset($_SESSION['userID']) ? intval($_SESSION['userID']) : 0; - if( $args->user_id == 0) - { - throw new Exception(__FILE__ . ' Can not work without User ID => Aborting'); - } - $args->user = $_SESSION['currentUser']; - } - - - $args->executedBy = $args->user_id; - $args->user_name = $args->user->login; - $args->userSet = $args->user->getNames($dbHandler); - - $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : 0; - $args->build_id = isset($_REQUEST['build_id']) && is_numeric($_REQUEST['build_id']) ? intval($_REQUEST['build_id']) : 0; - - $args->show_inactive_tplans = isset($_REQUEST['show_inactive_tplans']) ? true : false; - - $args->show_all_users = false; - if(isset($_REQUEST['show_all_users'])) - { - $args->show_all_users = (intval($_REQUEST['show_all_users']) == 1); - } - $args->show_user_column = $args->show_all_users; - - - $show_closed_builds = isset($_REQUEST['show_closed_builds']) ? true : false; - $show_closed_builds_hidden = isset($_REQUEST['show_closed_builds_hidden']) ? true : false; - if ($show_closed_builds) - { - $selection = true; - } - else if ($show_closed_builds_hidden) - { - $selection = false; - } - else if (isset($_SESSION['show_closed_builds'])) - { - $selection = intval($_SESSION['show_closed_builds']); - } - else - { - $selection = false; - } - $args->show_closed_builds = $_SESSION['show_closed_builds'] = $selection; - - if ($args->show_all_users) - { - $args->user_id = TL_USER_ANYBODY; - } - - $args->show_inactive_and_closed = false; - if( isset($_REQUEST['show_inactive_and_closed']) ) - { - $args->show_inactive_and_closed = (intval($_REQUEST['show_inactive_and_closed']) != 0); - } - - $args->priority_enabled = $_SESSION['testprojectOptions']->testPriorityEnabled ? true : false; - - - // quick & dirty execution - $args->tpx = isset($_REQUEST['tpx']) ? intval($_REQUEST['tpx']) : 0; - $dirtyHarry = array('pxi','bxi','tcvx'); - foreach($dirtyHarry as $tg) - { - $key = $tg . '_' . $args->tpx; - $args->$tg = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; - } - $args->result = isset($_REQUEST['result_' . $args->tpx]) ? $_REQUEST['result_' . $args->tpx][0] : ''; - - return $args; -} - - -/** - * get Columns definition for table to display - * - */ -function getColumnsDefinition($dbHandler,$tplan_id,$optionalColumns) -{ - static $labels; - static $tplan_mgr; - if( is_null($labels) ) - { - $tplan_mgr = new testplan($dbHandler); - - $lbl2get = array('build' => null,'testsuite' => null,'testcase' => null,'platform' => null, - 'user' => null, 'priority' => null,'status' => null, 'version' => null, - 'low_priority' => null,'medium_priority' => null,'high_priority' => null, - 'due_since' => null); - $labels = init_labels($lbl2get); - } - - $colDef = array(); - $sortByCol = $labels['testsuite']; - - // user column is only shown for assignment overview - if ($optionalColumns['user']) - { - $colDef[] = array('title_key' => 'user', 'width' => 80); - $sortByCol = $labels['build']; - } - - $colDef[] = array('title_key' => 'build', 'width' => 80); - $colDef[] = array('title_key' => 'testsuite', 'width' => 130); - $colDef[] = array('title_key' => 'testcase', 'width' => 130); - - $platforms = $tplan_mgr->getPlatforms($tplan_id,array('outputFormat' => 'map')); - if( ($show_plat = !is_null($platforms)) ) - { - $colDef[] = array('title_key' => 'platform', 'width' => 50, 'filter' => 'list', 'filterOptions' => $platforms); - } - - if ($optionalColumns['priority']) - { - $sortByCol = $labels['priority']; - $colDef[] = array('title_key' => 'priority', 'width' => 50, 'filter' => 'ListSimpleMatch', - 'filterOptions' => array($labels['low_priority'],$labels['medium_priority'],$labels['high_priority'])); - } - - $colDef[] = array('title_key' => 'status', 'width' => 50, 'type' => 'status'); - if($optionalColumns['user']) - { - $colDef[] = array('title_key' => 'tester', 'width' => 80); - } - - - $colDef[] = array('title_key' => 'due_since', 'width' => 100); - - return array($colDef, $sortByCol, $show_plat); -} - - -function initializeGui(&$dbHandler,$argsObj) -{ - $gui = new stdClass(); - $gui->tproject_name = $argsObj->tproject_name; - - // disable "show also closed builds" checkbox when a specific build is selected - $gui->show_build_selector = ($argsObj->build_id == 0); - $gui->show_closed_builds = $argsObj->show_closed_builds; - - $gui->glueChar = config_get('testcase_cfg')->glue_character; - $gui->warning_msg = ''; - $gui->tableSet = null; - $gui->l18n = init_labels(array('tcversion_indicator' => null,'goto_testspec' => null, 'version' => null, - 'testplan' => null, 'assigned_tc_overview' => null, - 'testcases_assigned_to_user' => null, - 'quick_passed' => null, 'quick_failed' => null,'quick_blocked' => null, - 'low_priority' => null,'medium_priority' => null,'high_priority' => null, - 'design' => null, 'execution' => null, 'execution_history' => null)); - - $gui->priority = array(LOW => $gui->l18n['low_priority'],MEDIUM => $gui->l18n['medium_priority'], - HIGH => $gui->l18n['high_priority']); - - if ($argsObj->show_all_users) - { - $gui->pageTitle=sprintf($gui->l18n['assigned_tc_overview'], $gui->tproject_name); - } - else - { - $gui->pageTitle=sprintf($gui->l18n['testcases_assigned_to_user'],$gui->tproject_name, $argsObj->user_name); - } - - $gui->user_id = $argsObj->user_id; - $gui->tplan_id = $argsObj->tplan_id; - - $gui->directLink = $_SESSION['basehref'] . - 'ltx.php?item=xta2m&user_id=' . $gui->user_id . - '&tplan_id=' . $gui->tplan_id; - - return $gui; -} - - -function initFilters($argsObj) -{ - $filters = array(); - - $filters['tplan_status'] = $argsObj->show_inactive_tplans ? 'all' : 'active'; - $filters['build_status'] = $argsObj->show_closed_builds ? 'all' : 'open'; - - if ($argsObj->build_id) - { - $filters['build_id'] = $argsObj->build_id; - - // show assignments regardless of build and tplan status - $filters['build_status'] = 'all'; - $filters['tplan_status'] = 'all'; - } - return $filters; -} - -function getStatusGuiCfg() -{ - $cfg = config_get('results'); - - $ret = new stdClass(); - $ret->status_code = $cfg['status_code']; - $ret->code_css = array(); - $ret->definition = array(); - - foreach($cfg['code_status'] as $code => $status) - { - if (isset($cfg['status_label'][$status])) - { - $label = $cfg['status_label'][$status]; - $ret->code_css[$code] = array(); - $ret->code_css[$code]['translation'] = lang_get($label); - $ret->code_css[$code]['css_class'] = $cfg['code_status'][$code] . '_text'; - $ret->definition[$code] = array("value" => $code, - "text" => $ret->code_css[$code]['translation'], - "cssClass" => $ret->code_css[$code]['css_class']); - } - } - return $ret; -} - -/** - * ATTENTION: xx.value is strongly related to HTML input names on tcAssignedToUser.tpl - */ -function getQuickExecCfg($gui,$imgSet,$statusCode) -{ - $qexe['passed'] = "l18n['quick_passed']}\" src=\"{$imgSet['exec_passed']}\" " . - " onclick=\"result_%s.value='{$statusCode['passed']}';"; - - - $qexe['failed'] = "l18n['quick_failed']}\" src=\"{$imgSet['exec_failed']}\" " . - " onclick=\"result_%s.value='{$statusCode['failed']}';"; - - $qexe['blocked'] = "l18n['quick_blocked']}\" src=\"{$imgSet['exec_blocked']}\" " . - " onclick=\"result_%s.value='{$statusCode['blocked']}';"; - - $qexe['common'] = 'pxi_%s.value=%s;bxi_%s.value=%s;tcvx_%s.value=%s;fog_%s.submit();" /> '; - - return $qexe; +getImages(); + +$args = initArgs($db); +$gui = initializeGui($db, $args); +$statusGui = getStatusGuiCfg(); + +// Get all test cases assigned to user without filtering by execution status +$opt = array( + 'mode' => 'full_path' +); +$filters = initFilters($args); +$tplan_param = ($args->tplan_id) ? array( + $args->tplan_id +) : testcase::ALL_TESTPLANS; + +$tcaseMgr = new testcase($db); +$gui->resultSet = $tcaseMgr->getAssignedToUser($args->user_id, + $args->tproject_id, $tplan_param, $opt, $filters); + +$doIt = ! is_null($gui->resultSet); + +// will work only on standard exec status +$exec = getQuickExecCfg($gui, $imgSet, $statusGui->status_code); + +$tables = tlObjectWithDB::getDBTables( + array( + 'nodes_hierarchy', + 'executions', + 'tcversions' + )); + +if ($args->result != '' && $args->tcvx > 0) { + + // get version number + $sql = " SELECT TCV.version FROM {$tables['tcversions']} TCV WHERE TCV.id = " . + $args->tcvx; + $xx = $db->get_recordset($sql); + $version_number = $xx[0]['version']; + + $sql = " INSERT INTO {$tables['executions']} " . + " (status,tester_id,execution_ts,tcversion_id,tcversion_number,testplan_id,platform_id,build_id)" . + " VALUES ('{$args->result}', {$args->executedBy}, " . $db->db_now() . "," . + " {$args->tcvx}, {$version_number}, {$args->tpx}, {$args->pxi},{$args->bxi})"; + + $db->exec_query($sql); +} + +if ($doIt) { + $execCfg = config_get('exec_cfg'); + + $tables = tlObjectWithDB::getDBTables(array( + 'nodes_hierarchy' + )); + $tplanSet = array_keys($gui->resultSet); + $sql = "SELECT name,id FROM {$tables['nodes_hierarchy']} " . "WHERE id IN (" . + implode(',', $tplanSet) . ")"; + $gui->tplanNames = $db->fetchRowsIntoMap($sql, 'id'); + $optColumns = array( + 'user' => $args->show_user_column, + 'priority' => $args->priority_enabled + ); + + $whoiam = $args->show_all_users ? 'tcAssignedToUser' : 'tcAssignedToMe'; + + foreach ($gui->resultSet as $tplan_id => $tcase_set) { + list ($columns, $sortByColumn, $show_platforms) = getColumnsDefinition( + $db, $tplan_id, $optColumns); + + $rows = array(); + + // has logged user right to execute test cases on this (test project,test plan)? + $hasExecRight = $_SESSION['currentUser']->hasRight($db, + 'testplan_execute', $args->tproject_id, $tplan_id, true); + + foreach ($tcase_set as $tcase_platform) { + foreach ($tcase_platform as $tcase) { + $current_row = array(); + $tcase_id = $tcase['testcase_id']; + $tcversion_id = $tcase['tcversion_id']; + + if ($args->show_user_column) { + if ($tcase['user_id'] > 0 && + isset($args->userSet[$tcase['user_id']])) { + $current_row[] = htmlspecialchars( + $args->userSet[$tcase['user_id']]['login']); + } else { + $current_row[] = ''; + } + } + + $current_row[] = htmlspecialchars($tcase['build_name']); + $current_row[] = htmlspecialchars($tcase['tcase_full_path']); + + // create linked icons + $ekk = $elk = $exec_link = ''; + $canExec = ($hasExecRight == 'yes'); + + if ($execCfg->exec_mode->tester == 'assigned_to_me') { + $canExec = $canExec && + ($tcase['user_id'] == $_SESSION['userID']); + } + + if ($canExec) { + $ekk = sprintf($exec['common'], $tplan_id, + $tcase['platform_id'], $tplan_id, $tcase['build_id'], + $tplan_id, $tcversion_id, $tplan_id); + + $elk = sprintf($exec['passed'], $tplan_id) . $ekk . ' ' . + sprintf($exec['failed'], $tplan_id) . $ekk . ' ' . + sprintf($exec['blocked'], $tplan_id) . $ekk; + + $exec_link = "" . + "l18n['execution']}\" src=\"{$imgSet['exec_icon']}\" /> "; + } + + $exec_history_link = "" . + "l18n['execution_history']}\" src=\"{$imgSet['history_small']}\" /> "; + + $edit_link = "" . + "l18n['design']}\" src=\"{$imgSet['edit_icon']}\" /> "; + + $current_row[] = "" . $elk . + $exec_history_link . $exec_link . $edit_link . + htmlspecialchars($tcase['prefix']) . $gui->glueChar . + $tcase['tc_external_id'] . " : " . + htmlspecialchars($tcase['name']) . + sprintf($gui->l18n['tcversion_indicator'], $tcase['version']); + + if ($show_platforms) { + $current_row[] = htmlspecialchars($tcase['platform_name']); + } + + if ($args->priority_enabled) { + $current_row[] = "" . + $gui->priority[priority_to_level($tcase['priority'])]; + } + + $leOptions = array( + 'getSteps' => 0 + ); + $lexec = $tcaseMgr->getLastExecution($tcase_id, $tcversion_id, + $tplan_id, $tcase['build_id'], $tcase['platform_id'], + $leOptions); + if (isset($lexec[$tcversion_id]['status'])) { + $status = $lexec[$tcversion_id]['status']; + } + if (! $status) { + $status = $statusGui->status_code['not_run']; + } + $current_row[] = $statusGui->definition[$status]; + + if ($args->show_user_column) { + $current_row[] = htmlspecialchars( + $lexec[$tcversion_id]['tester_login']); + } + + // need to check if we are using the right timestamp + $current_row[] = htmlspecialchars($tcase['creation_ts']) . " (" . + getDateDiff($tcase['creation_ts']) . ")"; + + $rows[] = $current_row; + } + } + + /* + * different table id for different reports: + * - Assignment Overview if $args->show_all_users is set + * - Test Cases assigned to user if $args->build_id > 0 + * - Test Cases assigned to me else + */ + $table_id = "tl_table_tc_assigned_to_me_for_tplan_"; + if ($args->show_all_users) { + $table_id = "tl_table_tc_assignment_overview_for_tplan_"; + } + if ($args->build_id) { + $table_id = "tl_table_tc_assigned_to_user_for_tplan_"; + } + + // add test plan id to table id + $table_id .= $tplan_id; + + $matrix = new tlExtTable($columns, $rows, $table_id); + $matrix->title = $gui->l18n['testplan'] . ": " . + htmlspecialchars($gui->tplanNames[$tplan_id]['name']); + + // default grouping by first column, which is user for overview, build otherwise + $matrix->setGroupByColumnName(lang_get($columns[0]['title_key'])); + + // make table collapsible if more than 1 table is shown and surround by frame + if (count($tplanSet) > 1) { + $matrix->collapsible = true; + $matrix->frame = true; + } + + // define toolbar + $matrix->showToolbar = true; + $matrix->toolbarExpandCollapseGroupsButton = true; + $matrix->toolbarShowAllColumnsButton = true; + + $matrix->setSortByColumnName($sortByColumn); + $matrix->sortDirection = 'DESC'; + $gui->tableSet[$tplan_id] = $matrix; + } +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * Replacement for the smarty helper function to get that functionality outside of templates. + * Returns difference between a given date and the current time in days. + * + * @author Andreas Simon + * @param + * $date + */ +function getDateDiff($date) +{ + $date = (is_string($date)) ? strtotime($date) : $date; + $i = 1 / 60 / 60 / 24; + return floor((time() - $date) * $i); +} + +/** + * init_args() + * Get in an object all data that has arrived to page through _REQUEST or _SESSION. + * If you think this page as a function, you can consider this data arguments (args) + * to a function call. + * Using all this data as one object property will help developer to understand + * if data is received or produced on page. + * + * @author franciscom - francisco.mancardi@gmail.com + * @args - used global coupling accessing $_REQUEST and $_SESSION + * + * @return object of stdClass + * + * @internal revisions + */ +function initArgs(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + + $args->tproject_id = isset($_REQUEST['tproject_id']) ? intval( + $_REQUEST['tproject_id']) : 0; + if ($args->tproject_id == 0) { + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + } + if ($args->tproject_id == 0) { + throw new Exception( + __FILE__ . ' Can not work without Test project ID => Aborting'); + } + $mgr = new testproject($dbHandler); + $info = $mgr->get_by_id($args->tproject_id); + $args->tproject_name = $info['name']; + $args->testprojectOptions = $info['opt']; + unset($info); + + $args->user_id = isset($_REQUEST['user_id']) ? intval($_REQUEST['user_id']) : 0; + + if ($args->user_id != 0) { + $args->user = new tlUser($args->user_id); + $args->user->readFromDB($dbHandler); + } else { + $args->user_id = isset($_SESSION['userID']) ? intval( + $_SESSION['userID']) : 0; + if ($args->user_id == 0) { + throw new Exception( + __FILE__ . ' Can not work without User ID => Aborting'); + } + $args->user = $_SESSION['currentUser']; + } + + $args->executedBy = $args->user_id; + $args->user_name = $args->user->login; + $args->userSet = $args->user->getNames($dbHandler); + + $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : 0; + $args->build_id = isset($_REQUEST['build_id']) && + is_numeric($_REQUEST['build_id']) ? intval($_REQUEST['build_id']) : 0; + + $args->show_inactive_tplans = isset($_REQUEST['show_inactive_tplans']) ? true : false; + + $args->show_all_users = false; + if (isset($_REQUEST['show_all_users'])) { + $args->show_all_users = (intval($_REQUEST['show_all_users']) == 1); + } + $args->show_user_column = $args->show_all_users; + + $show_closed_builds = isset($_REQUEST['show_closed_builds']) ? true : false; + $show_closed_builds_hidden = isset($_REQUEST['show_closed_builds_hidden']) ? true : false; + if ($show_closed_builds) { + $selection = true; + } elseif ($show_closed_builds_hidden) { + $selection = false; + } elseif (isset($_SESSION['show_closed_builds'])) { + $selection = intval($_SESSION['show_closed_builds']); + } else { + $selection = false; + } + $args->show_closed_builds = $_SESSION['show_closed_builds'] = $selection; + + if ($args->show_all_users) { + $args->user_id = TL_USER_ANYBODY; + } + + $args->show_inactive_and_closed = false; + if (isset($_REQUEST['show_inactive_and_closed'])) { + $args->show_inactive_and_closed = (intval( + $_REQUEST['show_inactive_and_closed']) != 0); + } + + $args->priority_enabled = $_SESSION['testprojectOptions']->testPriorityEnabled ? true : false; + + // quick & dirty execution + $args->tpx = isset($_REQUEST['tpx']) ? intval($_REQUEST['tpx']) : 0; + $dirtyHarry = array( + 'pxi', + 'bxi', + 'tcvx' + ); + foreach ($dirtyHarry as $tg) { + $key = $tg . '_' . $args->tpx; + $args->$tg = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; + } + $args->result = isset($_REQUEST['result_' . $args->tpx]) ? $_REQUEST['result_' . + $args->tpx][0] : ''; + + return $args; +} + +/** + * get Columns definition for table to display + */ +function getColumnsDefinition($dbHandler, $tplan_id, $optionalColumns) +{ + static $labels; + static $tplan_mgr; + if (is_null($labels)) { + $tplan_mgr = new testplan($dbHandler); + + $lbl2get = array( + 'build' => null, + 'testsuite' => null, + 'testcase' => null, + 'platform' => null, + 'user' => null, + 'priority' => null, + 'status' => null, + 'version' => null, + 'low_priority' => null, + 'medium_priority' => null, + 'high_priority' => null, + 'due_since' => null + ); + $labels = init_labels($lbl2get); + } + + $colDef = array(); + $sortByCol = $labels['testsuite']; + + // user column is only shown for assignment overview + if ($optionalColumns['user']) { + $colDef[] = array( + 'title_key' => 'user', + 'width' => 80 + ); + $sortByCol = $labels['build']; + } + + $colDef[] = array( + 'title_key' => 'build', + 'width' => 80 + ); + $colDef[] = array( + 'title_key' => 'testsuite', + 'width' => 130 + ); + $colDef[] = array( + 'title_key' => 'testcase', + 'width' => 130 + ); + + $platforms = $tplan_mgr->getPlatforms($tplan_id, + array( + 'outputFormat' => 'map' + )); + if ($show_plat = ! is_null($platforms)) { + $colDef[] = array( + 'title_key' => 'platform', + 'width' => 50, + 'filter' => 'list', + 'filterOptions' => $platforms + ); + } + + if ($optionalColumns['priority']) { + $sortByCol = $labels['priority']; + $colDef[] = array( + 'title_key' => 'priority', + 'width' => 50, + 'filter' => 'ListSimpleMatch', + 'filterOptions' => array( + $labels['low_priority'], + $labels['medium_priority'], + $labels['high_priority'] + ) + ); + } + + $colDef[] = array( + 'title_key' => 'status', + 'width' => 50, + 'type' => 'status' + ); + if ($optionalColumns['user']) { + $colDef[] = array( + 'title_key' => 'tester', + 'width' => 80 + ); + } + + $colDef[] = array( + 'title_key' => 'due_since', + 'width' => 100 + ); + + return array( + $colDef, + $sortByCol, + $show_plat + ); +} + +function initializeGui(&$dbHandler, $argsObj) +{ + $gui = new stdClass(); + $gui->tproject_name = $argsObj->tproject_name; + + // disable "show also closed builds" checkbox when a specific build is selected + $gui->show_build_selector = ($argsObj->build_id == 0); + $gui->show_closed_builds = $argsObj->show_closed_builds; + + $gui->glueChar = config_get('testcase_cfg')->glue_character; + $gui->warning_msg = ''; + $gui->tableSet = null; + $gui->l18n = init_labels( + array( + 'tcversion_indicator' => null, + 'goto_testspec' => null, + 'version' => null, + 'testplan' => null, + 'assigned_tc_overview' => null, + 'testcases_assigned_to_user' => null, + 'quick_passed' => null, + 'quick_failed' => null, + 'quick_blocked' => null, + 'low_priority' => null, + 'medium_priority' => null, + 'high_priority' => null, + 'design' => null, + 'execution' => null, + 'execution_history' => null + )); + + $gui->priority = array( + LOW => $gui->l18n['low_priority'], + MEDIUM => $gui->l18n['medium_priority'], + HIGH => $gui->l18n['high_priority'] + ); + + if ($argsObj->show_all_users) { + $gui->pageTitle = sprintf($gui->l18n['assigned_tc_overview'], + $gui->tproject_name); + } else { + $gui->pageTitle = sprintf($gui->l18n['testcases_assigned_to_user'], + $gui->tproject_name, $argsObj->user_name); + } + + $gui->user_id = $argsObj->user_id; + $gui->tplan_id = $argsObj->tplan_id; + + $gui->directLink = $_SESSION['basehref'] . 'ltx.php?item=xta2m&user_id=' . + $gui->user_id . '&tplan_id=' . $gui->tplan_id; + + return $gui; +} + +function initFilters($argsObj) +{ + $filters = array(); + + $filters['tplan_status'] = $argsObj->show_inactive_tplans ? 'all' : 'active'; + $filters['build_status'] = $argsObj->show_closed_builds ? 'all' : 'open'; + + if ($argsObj->build_id) { + $filters['build_id'] = $argsObj->build_id; + + // show assignments regardless of build and tplan status + $filters['build_status'] = 'all'; + $filters['tplan_status'] = 'all'; + } + return $filters; +} + +function getStatusGuiCfg() +{ + $cfg = config_get('results'); + + $ret = new stdClass(); + $ret->status_code = $cfg['status_code']; + $ret->code_css = array(); + $ret->definition = array(); + + foreach ($cfg['code_status'] as $code => $status) { + if (isset($cfg['status_label'][$status])) { + $label = $cfg['status_label'][$status]; + $ret->code_css[$code] = array(); + $ret->code_css[$code]['translation'] = lang_get($label); + $ret->code_css[$code]['css_class'] = $cfg['code_status'][$code] . + '_text'; + $ret->definition[$code] = array( + "value" => $code, + "text" => $ret->code_css[$code]['translation'], + "cssClass" => $ret->code_css[$code]['css_class'] + ); + } + } + return $ret; +} + +/** + * ATTENTION: xx.value is strongly related to HTML input names on tcAssignedToUser.tpl + */ +function getQuickExecCfg($gui, $imgSet, $statusCode) +{ + $qexe['passed'] = "l18n['quick_passed']}\" src=\"{$imgSet['exec_passed']}\" " . + " onclick=\"result_%s.value='{$statusCode['passed']}';"; + + $qexe['failed'] = "l18n['quick_failed']}\" src=\"{$imgSet['exec_failed']}\" " . + " onclick=\"result_%s.value='{$statusCode['failed']}';"; + + $qexe['blocked'] = "l18n['quick_blocked']}\" src=\"{$imgSet['exec_blocked']}\" " . + " onclick=\"result_%s.value='{$statusCode['blocked']}';"; + + $qexe['common'] = 'pxi_%s.value=%s;bxi_%s.value=%s;tcvx_%s.value=%s;fog_%s.submit();" /> '; + + return $qexe; } diff --git a/lib/testcases/tcBulkOp.php b/lib/testcases/tcBulkOp.php index 948c3e837b..0e8a81c69e 100644 --- a/lib/testcases/tcBulkOp.php +++ b/lib/testcases/tcBulkOp.php @@ -1,107 +1,113 @@ -doAction == 'apply') -{ - foreach($args->uchoice as $key => $val) - { - if($val > 0) - { - $tcaseMgr->setIntAttrForAllVersions($args->tcase_id,$key,$val,$args->forceFrozenVersions); - } - } -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: init_args - - args: - - returns: - -*/ -function init_args(&$tcaseMgr) -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; - switch($args->doAction) - { - case 'apply'; - break; - - default: - $args->doAction = 'init'; - break; - } - - $args->tcase_id = isset($_REQUEST['tcase_id']) ? intval($_REQUEST['tcase_id']) : 0; - $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; - - $args->uchoice = array(); - $k2s = array('importance','status','execution_type'); - foreach($k2s as $tg) - { - $args->uchoice[$tg] = intval(isset($_REQUEST[$tg]) ? $_REQUEST[$tg] : -1); - } - - $dummy = getConfigAndLabels('testCaseStatus','code'); - $args->tcStatusCfg['status_code'] = $dummy['cfg']; - $args->tcStatusCfg['code_label'] = $dummy['lbl']; - - $args->domainTCExecType = $tcaseMgr->get_execution_types(); - - $dummy = config_get('importance'); - foreach ($dummy['code_label'] as $code => $label) - { - $args->domainTCImportance[$code] = lang_get($label); - } - $args->forceFrozenVersions = isset($_REQUEST['forceFrozenTestcasesVersions']) ? intval($_REQUEST['forceFrozenTestcasesVersions']) : 0; - - return $args; -} - -/** - * - * - */ -function initializeGui(&$argsObj) -{ - $guiObj = new stdClass(); - $guiObj->page_title = lang_get('bulk_op'); - $guiObj->uchoice = $argsObj->uchoice; - $guiObj->tcase_id = $argsObj->tcase_id; - - $guiObj->domainTCStatus = array(-1 => '') + $argsObj->tcStatusCfg['code_label']; - $guiObj->domainTCExecType = array(-1 => '') + $argsObj->domainTCExecType; - $guiObj->domainTCImportance = array(-1 => '') + $argsObj->domainTCImportance; - - $guiObj->goback_url = !is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; - - return $guiObj; +doAction == 'apply') { + foreach ($args->uchoice as $key => $val) { + if ($val > 0) { + $tcaseMgr->setIntAttrForAllVersions($args->tcase_id, $key, $val, + $args->forceFrozenVersions); + } + } +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: init_args + * + * args: + * + * returns: + * + */ +function initArgs(&$tcaseMgr) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : null; + switch ($args->doAction) { + case 'apply': + break; + + default: + $args->doAction = 'init'; + break; + } + + $args->tcase_id = isset($_REQUEST['tcase_id']) ? intval( + $_REQUEST['tcase_id']) : 0; + $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; + + $args->uchoice = array(); + $k2s = array( + 'importance', + 'status', + 'execution_type' + ); + foreach ($k2s as $tg) { + $args->uchoice[$tg] = intval( + isset($_REQUEST[$tg]) ? $_REQUEST[$tg] : - 1); + } + + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + $args->tcStatusCfg['status_code'] = $dummy['cfg']; + $args->tcStatusCfg['code_label'] = $dummy['lbl']; + + $args->domainTCExecType = $tcaseMgr->get_execution_types(); + + $dummy = config_get('importance'); + foreach ($dummy['code_label'] as $code => $label) { + $args->domainTCImportance[$code] = lang_get($label); + } + $args->forceFrozenVersions = isset( + $_REQUEST['forceFrozenTestcasesVersions']) ? intval( + $_REQUEST['forceFrozenTestcasesVersions']) : 0; + + return $args; +} + +/** + */ +function initializeGui(&$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->page_title = lang_get('bulk_op'); + $guiObj->uchoice = $argsObj->uchoice; + $guiObj->tcase_id = $argsObj->tcase_id; + + $guiObj->domainTCStatus = array( + - 1 => '' + ) + $argsObj->tcStatusCfg['code_label']; + $guiObj->domainTCExecType = array( + - 1 => '' + ) + $argsObj->domainTCExecType; + $guiObj->domainTCImportance = array( + - 1 => '' + ) + $argsObj->domainTCImportance; + + $guiObj->goback_url = ! is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; + + return $guiObj; } diff --git a/lib/testcases/tcCompareVersions.php b/lib/testcases/tcCompareVersions.php index 0cf2a3ebca..9d182ad4a7 100644 --- a/lib/testcases/tcCompareVersions.php +++ b/lib/testcases/tcCompareVersions.php @@ -1,189 +1,186 @@ -compare_selected_versions) -{ - $diffEngine = $args->use_daisydiff ? new HTMLDiffer() : new diff(); - $attributes = buildDiff($gui->tc_versions,$args); - foreach($attributes as $key => $val) - { - $gui->diff[$key]['count'] = 0; - $gui->diff[$key]['heading'] = lang_get($key); - - $val['left'] = isset($val['left']) ? $val['left'] : ''; - $val['right'] = isset($val['right']) ? $val['right'] : ''; - - if($args->use_daisydiff) - { - if ($gui->tcType == 'none') - { - list($gui->diff[$key]['diff'], $gui->diff[$key]['count']) = $diffEngine->htmlDiff(nl2br($val['left']), nl2br($val['right'])); - } - else - { - list($gui->diff[$key]['diff'], $gui->diff[$key]['count']) = $diffEngine->htmlDiff($val['left'], $val['right']); - } - } - else - { - // insert line endings so diff is better readable and makes sense (not everything in one line) - // then transform into array with \n as separator => diffEngine needs that. - // - $gui->diff[$key]['left'] = explode("\n", str_replace("

    ", "

    \n", $val['left'])); - $gui->diff[$key]['right'] = explode("\n", str_replace("

    ", "

    \n", $val['right'])); - - $gui->diff[$key]['diff'] = $diffEngine->inline($gui->diff[$key]['left'], $gui->leftID, - $gui->diff[$key]['right'], $gui->rightID,$args->context); - $gui->diff[$key]['count'] = count($diffEngine->changes); - } - - // are there any changes? then display! if not, nothing to show here - $msgKey = ($gui->diff[$key]['count'] > 0) ? 'num_changes' : 'no_changes'; - $gui->diff[$key]['message'] = sprintf($gui->labels[$msgKey], $gui->diff[$key]['heading'], - $gui->diff[$key]['count']); - } -} -$smarty = new TLSmarty(); -$smarty->assign('gui', $gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -function init_args() -{ - $args = new stdClass(); - $args->use_daisydiff = isset($_REQUEST['use_html_comp']); - - - $args->tcase_id = isset($_REQUEST['testcase_id']) ? $_REQUEST['testcase_id'] : 0; - $args->tcase_id = intval($args->tcase_id); - - $key2set = array('compare_selected_versions' => 0,'version_left' => '','version_right' => ''); - foreach($key2set as $tk => $value) - { - $args->$tk = isset($_REQUEST[$tk]) ? $_REQUEST[$tk] : $value; - } - - - if (isset($_REQUEST['context_show_all'])) - { - $args->context = null; - } - else - { - $diffEngineCfg = config_get("diffEngine"); - $args->context = (isset($_REQUEST['context']) && is_numeric($_REQUEST['context'])) ? - $_REQUEST['context'] : $diffEngineCfg->context; - } - - return $args; -} - -function initializeGUI(&$dbHandler,$argsObj) -{ - $gui = new stdClass(); - - $gui->tc_id = $argsObj->tcase_id; - $gui->compare_selected_versions = $argsObj->compare_selected_versions; - $gui->context = $argsObj->context; - - $tcaseMgr = new testcase($dbHandler); - $gui->tc_versions = $tcaseMgr->get_by_id($argsObj->tcase_id); - $gui->tcaseName = $gui->tc_versions[0]['name']; - unset($tcaseMgr); - - $lblkeys = array('num_changes' => null,'no_changes' => null, 'version_short' => null,'diff_subtitle_tc' => null); - $gui->labels = init_labels($lblkeys); - $gui->version_short = $gui->labels['version_short']; - - - $gui->subtitle = sprintf($gui->labels['diff_subtitle_tc'], - $argsObj->version_left,$argsObj->version_left, - $argsObj->version_right,$argsObj->version_right, $gui->tcaseName); - - $gui->leftID = "v{$argsObj->version_left}"; - $gui->rightID = "v{$argsObj->version_right}"; - $tcCfg = getWebEditorCfg('design'); - $gui->tcType = $tcCfg['type']; - return $gui; -} - -function buildDiff($items,$argsObj) -{ - - $panel = array('left','right'); - - $attrKeys = array(); - $attrKeys['simple'] = array('summary','preconditions'); - $attrKeys['complex'] = array('steps' => 'actions', 'expected_results' => 'expected_results'); - $dummy = array_merge($attrKeys['simple'],array_keys($attrKeys['complex'])); - foreach($dummy as $gx) - { - foreach($panel as $side) - { - $diff[$gx][$side] = null; - } - } - - foreach($items as $tcase) - { - foreach($panel as $side) - { - $tk = 'version_' . $side; - if ($tcase['version'] == $argsObj->$tk) - { - foreach($attrKeys['simple'] as $attr) - { - $diff[$attr][$side] = $tcase[$attr]; - } - - // Steps & Expected results, have evolved from ONE FIXED SET of two simple fields - // to a dynamic SET (array?) of two simple fields. - // Our choice in order to find differences, is to transform the dynamic set - // again on a ONE FIXED SET. - // That's why we need to do this kind of special processing. - if(is_array($tcase['steps'])) // some magic, I'm Sorry - { - foreach($tcase['steps'] as $step) - { - foreach($attrKeys['complex'] as $attr => $key2read) - { - $diff[$attr][$side] .= str_replace("

    ", "

    \n", $step[$key2read])."
    "."
    "; // insert lines between each steps and between each expected results so diff is better readable and makes sense (not everything in one line) - } - } - } - } - } // foreach panel - } - return $diff; -} -?> \ No newline at end of file +compare_selected_versions) { + $diffEngine = $args->use_daisydiff ? new HTMLDiffer() : new diff(); + $attributes = buildDiff($gui->tc_versions, $args); + foreach ($attributes as $key => $val) { + $gui->diff[$key]['count'] = 0; + $gui->diff[$key]['heading'] = lang_get($key); + + $val['left'] = isset($val['left']) ? $val['left'] : ''; + $val['right'] = isset($val['right']) ? $val['right'] : ''; + + if ($args->use_daisydiff) { + if ($gui->tcType == 'none') { + list ($gui->diff[$key]['diff'], $gui->diff[$key]['count']) = $diffEngine->htmlDiff( + nl2br($val['left']), nl2br($val['right'])); + } else { + list ($gui->diff[$key]['diff'], $gui->diff[$key]['count']) = $diffEngine->htmlDiff( + $val['left'], $val['right']); + } + } else { + // insert line endings so diff is better readable and makes sense (not everything in one line) + // then transform into array with \n as separator => diffEngine needs that. + // + $gui->diff[$key]['left'] = explode("\n", + str_replace("

    ", "

    \n", $val['left'])); + $gui->diff[$key]['right'] = explode("\n", + str_replace("

    ", "

    \n", $val['right'])); + + $gui->diff[$key]['diff'] = $diffEngine->inline( + $gui->diff[$key]['left'], $gui->leftID, + $gui->diff[$key]['right'], $gui->rightID, $args->context); + $gui->diff[$key]['count'] = count($diffEngine->changes); + } + + // are there any changes? then display! if not, nothing to show here + $msgKey = ($gui->diff[$key]['count'] > 0) ? 'num_changes' : 'no_changes'; + $gui->diff[$key]['message'] = sprintf($gui->labels[$msgKey], + $gui->diff[$key]['heading'], $gui->diff[$key]['count']); + } +} +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +function initArgs() +{ + $args = new stdClass(); + $args->use_daisydiff = isset($_REQUEST['use_html_comp']); + + $args->tcase_id = isset($_REQUEST['testcase_id']) ? $_REQUEST['testcase_id'] : 0; + $args->tcase_id = intval($args->tcase_id); + + $key2set = array( + 'compare_selected_versions' => 0, + 'version_left' => '', + 'version_right' => '' + ); + foreach ($key2set as $tk => $value) { + $args->$tk = isset($_REQUEST[$tk]) ? $_REQUEST[$tk] : $value; + } + + if (isset($_REQUEST['context_show_all'])) { + $args->context = null; + } else { + $diffEngineCfg = config_get("diffEngine"); + $args->context = (isset($_REQUEST['context']) && + is_numeric($_REQUEST['context'])) ? $_REQUEST['context'] : $diffEngineCfg->context; + } + + return $args; +} + +function initializeGUI(&$dbHandler, $argsObj) +{ + $gui = new stdClass(); + + $gui->tc_id = $argsObj->tcase_id; + $gui->compare_selected_versions = $argsObj->compare_selected_versions; + $gui->context = $argsObj->context; + + $tcaseMgr = new testcase($dbHandler); + $gui->tc_versions = $tcaseMgr->get_by_id($argsObj->tcase_id); + $gui->tcaseName = $gui->tc_versions[0]['name']; + unset($tcaseMgr); + + $lblkeys = array( + 'num_changes' => null, + 'no_changes' => null, + 'version_short' => null, + 'diff_subtitle_tc' => null + ); + $gui->labels = init_labels($lblkeys); + $gui->version_short = $gui->labels['version_short']; + + $gui->subtitle = sprintf($gui->labels['diff_subtitle_tc'], + $argsObj->version_left, $argsObj->version_left, $argsObj->version_right, + $argsObj->version_right, $gui->tcaseName); + + $gui->leftID = "v{$argsObj->version_left}"; + $gui->rightID = "v{$argsObj->version_right}"; + $tcCfg = getWebEditorCfg('design'); + $gui->tcType = $tcCfg['type']; + return $gui; +} + +function buildDiff($items, $argsObj) +{ + $panel = array( + 'left', + 'right' + ); + + $attrKeys = array(); + $attrKeys['simple'] = array( + 'summary', + 'preconditions' + ); + $attrKeys['complex'] = array( + 'steps' => 'actions', + 'expected_results' => 'expected_results' + ); + $dummy = array_merge($attrKeys['simple'], array_keys($attrKeys['complex'])); + foreach ($dummy as $gx) { + foreach ($panel as $side) { + $diff[$gx][$side] = null; + } + } + + foreach ($items as $tcase) { + foreach ($panel as $side) { + $tk = 'version_' . $side; + if ($tcase['version'] == $argsObj->$tk) { + foreach ($attrKeys['simple'] as $attr) { + $diff[$attr][$side] = $tcase[$attr]; + } + + // Steps & Expected results, have evolved from ONE FIXED SET of two simple fields + // to a dynamic SET (array?) of two simple fields. + // Our choice in order to find differences, is to transform the dynamic set + // again on a ONE FIXED SET. + // That's why we need to do this kind of special processing. + if (is_array($tcase['steps'])) // some magic, I'm Sorry + { + foreach ($tcase['steps'] as $step) { + foreach ($attrKeys['complex'] as $attr => $key2read) { + $diff[$attr][$side] .= str_replace("

    ", "

    \n", + $step[$key2read]) . "
    " . "
    "; // insert lines between each steps and between each expected results so diff is better readable and makes sense (not everything in one line) + } + } + } + } + } + } + return $diff; +} +?> diff --git a/lib/testcases/tcCreateFromIssue.php b/lib/testcases/tcCreateFromIssue.php index fc3a226732..2686bca54e 100644 --- a/lib/testcases/tcCreateFromIssue.php +++ b/lib/testcases/tcCreateFromIssue.php @@ -1,1069 +1,1095 @@ -do_upload) -{ - - // check the uploaded file - $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; - - tLog('Uploaded file: '.$source); - $doIt = false; - $gui->file_check = null; - if (($source != 'none') && ($source != '')) - { - // ATTENTION: - // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using - // Firefox and Chrome. - if( !($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes) ) - { - $gui->file_check['status_ok'] = 0; - $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'),$_FILES['uploadedFile']['size'],$gui->importLimitBytes); - } - } - if($doIt) - { - $gui->file_check['status_ok'] = 1; - if (move_uploaded_file($source, $gui->dest)) - { - tLog('Renamed uploaded file: ' . $source); - switch($args->importType) - { - case 'XML': - $pcheck_fn = "check_xml_tc_tsuite"; - $pimport_fn = "importTestCaseDataFromXML"; - break; - } - if(!is_null($pcheck_fn)) - { - $gui->file_check = $pcheck_fn($gui->dest,$args->useRecursion); - } - } - if($gui->file_check['status_ok'] && $pimport_fn) - { - tLog('Check is Ok.'); - $opt = array(); - $opt['useRecursion'] = $args->useRecursion; - $opt['importIntoProject'] = $args->bIntoProject; - $opt['duplicateLogic'] = array('hitCriteria' => $args->hit_criteria, - 'actionOnHit' => $args->action_on_duplicated_name); - $gui->resultMap = $pimport_fn($db,$gui->dest,intval($args->container_id), - intval($args->tproject_id),intval($args->userID),$opt); - } - } - else if(is_null($gui->file_check)) - { - - tLog('Missing upload file','WARNING'); - $gui->file_check = array('status_ok' => 0, 'msg' => lang_get('please_choose_file_to_import')); - $args->importType = null; - } -} - -if($args->useRecursion) -{ - $obj_mgr = new testsuite($db); - $gui->actionOptions=array('update_last_version' => lang_get('update_last_testcase_version'), - 'generate_new' => lang_get('generate_new_testcase'), - 'create_new_version' => lang_get('create_new_testcase_version')); - - $gui->hitOptions=array('name' => lang_get('same_name'), - 'internalID' => lang_get('same_internalID'), - 'externalID' => lang_get('same_externalID')); -} -else -{ - $obj_mgr = new testcase($db); - $gui->actionOptions=array('update_last_version' => lang_get('update_last_testcase_version'), - 'generate_new' => lang_get('generate_new_testcase'), - 'create_new_version' => lang_get('create_new_testcase_version')); - - $gui->hitOptions=array('name' => lang_get('same_name'), - 'internalID' => lang_get('same_internalID'), - 'externalID' => lang_get('same_externalID')); - -} - -$gui->testprojectName = $_SESSION['testprojectName']; -$gui->importTypes = $obj_mgr->get_import_file_types(); -$gui->action_on_duplicated_name=$args->action_on_duplicated_name; - - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -// -------------------------------------------------------------------------------------- -/* - function: importTestCaseDataFromXML - args : - returns: -*/ -function importTestCaseDataFromXML(&$db,$fileName,$parentID,$tproject_id,$userID,$options=null) -{ - tLog('importTestCaseDataFromXML called for file: '. $fileName); - $xmlTCs = null; - $resultMap = null; - $my = array(); - $my['options'] = array('useRecursion' => false, 'importIntoProject' => 0, - 'duplicateLogic' => array('hitCriteria' => 'name', 'actionOnHit' => null)); - $my['options'] = array_merge($my['options'], (array)$options); - foreach($my['options'] as $varname => $value) - { - $$varname = $value; - } - - if (file_exists($fileName)) - { - $xml = @simplexml_load_file_wrapper($fileName); - if($xml !== FALSE) - { - $xmlKeywords = $xml->xpath('//keywords'); - $kwMap = null; - if ($xmlKeywords) - { - $tproject = new testproject($db); - $loop2do = sizeof($xmlKeywords); - for($idx = 0; $idx < $loop2do ;$idx++) - { - $tproject->importKeywordsFromSimpleXML($tproject_id,$xmlKeywords[$idx]); - } - $kwMap = $tproject->get_keywords_map($tproject_id); - $kwMap = is_null($kwMap) ? null : array_flip($kwMap); - } - - if (!$useRecursion && ($xml->getName() == 'testcases') ) - { - $resultMap = importTestCasesFromSimpleXML($db,$xml,$parentID,$tproject_id,$userID,$kwMap,$duplicateLogic); - } - - if ($useRecursion && ($xml->getName() == 'testsuite')) - { - $resultMap = importTestSuitesFromSimpleXML($db,$xml,intval($parentID),intval($tproject_id),$userID, - $kwMap,$importIntoProject,$duplicateLogic); - } - - } - } - return $resultMap; -} - - -// -------------------------------------------------------------------------------------- -/* - function: saveImportedTCData - args : - returns: -*/ -function saveImportedTCData(&$db,$tcData,$tproject_id,$container_id, - $userID,$kwMap,$duplicatedLogic = array('hitCriteria' => 'name', 'actionOnHit' => null)) -{ - static $messages; - static $fieldSizeCfg; - static $feedbackMsg; - static $tcase_mgr; - static $tproject_mgr; - static $req_spec_mgr; - static $req_mgr; - static $safeSizeCfg; - static $linkedCustomFields; - static $tprojectHas; - static $reqSpecSet; - static $getVersionOpt; - static $userObj; - - if (!$tcData) - { - return; - } - - // $tprojectHas = array('customFields' => false, 'reqSpec' => false); - $hasCustomFieldsInfo = false; - $hasRequirements = false; - - if(is_null($messages)) - { - $feedbackMsg = array(); - $messages = array(); - $fieldSizeCfg = config_get('field_size'); - - $tcase_mgr = new testcase($db); - $tcase_mgr->setTestProject($tproject_id); - - - $tproject_mgr = new testproject($db); - $req_spec_mgr = new requirement_spec_mgr($db); - $req_mgr = new requirement_mgr($db); - $userObj = new tlUser(); - - $k2l = array('already_exists_updated','original_name','testcase_name_too_long', - 'start_warning','end_warning','testlink_warning','hit_with_same_external_ID'); - foreach($k2l as $k) - { - $messages[$k] = lang_get($k); - } - - $messages['start_feedback'] = $messages['start_warning'] . "\n" . $messages['testlink_warning'] . "\n"; - $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); - $messages['reqspec_warning'] = lang_get('no_reqspec_defined_can_not_import'); - - - - $feedbackMsg['cfield']=lang_get('cf_value_not_imported_missing_cf_on_testproject'); - $feedbackMsg['tcase'] = lang_get('testcase'); - $feedbackMsg['req'] = lang_get('req_not_in_req_spec_on_tcimport'); - $feedbackMsg['req_spec'] = lang_get('req_spec_ko_on_tcimport'); - - - // because name can be changed automatically during item creation - // to avoid name conflict adding a suffix automatically generated, - // is better to use a max size < max allowed size - $safeSizeCfg = new stdClass(); - $safeSizeCfg->testcase_name=($fieldSizeCfg->testcase_name) * 0.8; - - - // Get CF with scope design time and allowed for test cases linked to this test project - $linkedCustomFields = $tcase_mgr->cfield_mgr->get_linked_cfields_at_design($tproject_id,1,null,'testcase',null,'name'); - $tprojectHas['customFields']=!is_null($linkedCustomFields); - - $reqSpecSet = $tproject_mgr->getReqSpec($tproject_id,null,array('RSPEC.id','NH.name AS title','RSPEC.doc_id as rspec_doc_id', 'REQ.req_doc_id'),'req_doc_id'); - $tprojectHas['reqSpec'] = (!is_null($reqSpecSet) && count($reqSpecSet) > 0); - - $getVersionOpt = array('output' => 'minimun'); - $tcasePrefix = $tproject_mgr->getTestCasePrefix($tproject_id); - } - - $resultMap = array(); - $tc_qty = sizeof($tcData); - $userIDCache = array(); - - for($idx = 0; $idx <$tc_qty ; $idx++) - { - $tc = $tcData[$idx]; - $name = $tc['name']; - $summary = $tc['summary']; - $steps = $tc['steps']; - - // I've changed value to use when order has not been provided - // from testcase:DEFAULT_ORDER to a counter, because with original solution - // an issue arise with 'save execution and go next' - // if use has not provided order I think is OK TestLink make any choice. - $node_order = isset($tc['node_order']) ? intval($tc['node_order']) : ($idx+1); - $internalid = $tc['internalid']; - $preconditions = $tc['preconditions']; - $exec_type = isset($tc['execution_type']) ? $tc['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; - $importance = isset($tc['importance']) ? $tc['importance'] : MEDIUM; - - $externalid = $tc['externalid']; - if( intval($externalid) <= 0 ) - { - $externalid = null; - } - - $personID = $userID; - if( !is_null($tc['author_login']) ) - { - if( isset($userIDCache[$tc['author_login']]) ) - { - $personID = $userIDCache[$tc['author_login']]; - } - else - { - $userObj->login = $tc['author_login']; - if( $userObj->readFromDB($db,tlUser::USER_O_SEARCH_BYLOGIN) == tl::OK ) - { - $personID = $userObj->dbID; - } - - // I will put always a valid userID on this cache, - // this way if author_login does not exit, and is used multiple times - // i will do check for existence JUST ONCE. - $userIDCache[$tc['author_login']] = $personID; - } - } - - $name_len = tlStringLen($name); - if($name_len > $fieldSizeCfg->testcase_name) - { - // Will put original name inside summary - $xx = $messages['start_feedback']; - $xx .= sprintf($messages['testcase_name_too_long'],$name_len, $fieldSizeCfg->testcase_name) . "\n"; - $xx .= $messages['original_name'] . "\n" . $name. "\n" . $messages['end_warning'] . "\n"; - $summary = nl2br($xx) . $summary; - $name = tlSubStr($name, 0, $safeSizeCfg->testcase_name); - } - - - $kwIDs = null; - if (isset($tc['keywords']) && $tc['keywords']) - { - $kwIDs = implode(",",buildKeywordList($kwMap,$tc['keywords'])); - } - - $doCreate=true; - if( $duplicatedLogic['actionOnHit'] == 'update_last_version' ) - { - switch($duplicatedLogic['hitCriteria']) - { - case 'name': - $info = $tcase_mgr->getDuplicatesByName($name,$container_id); - break; - - case 'internalID': - $dummy = $tcase_mgr->tree_manager->get_node_hierarchy_info($internalid,$container_id); - if( !is_null($dummy) ) - { - $info = null; // TICKET 4925 - $info[$internalid] = $dummy; - } - break; - - case 'externalID': - $info = $tcase_mgr->get_by_external($externalid,$container_id); - break; - - - } - - if( !is_null($info) ) - { - $tcase_qty = count($info); - switch($tcase_qty) - { - case 1: - $doCreate=false; - $tcase_id = key($info); - $last_version = $tcase_mgr->get_last_version_info($tcase_id,$getVersionOpt); - $tcversion_id = $last_version['id']; - $ret = $tcase_mgr->update($tcase_id,$tcversion_id,$name,$summary, - $preconditions,$steps,$personID,$kwIDs, - $node_order,$exec_type,$importance); - - $ret['id'] = $tcase_id; - $ret['tcversion_id'] = $tcversion_id; - $resultMap[] = array($name,$messages['already_exists_updated']); - break; - - case 0: - $doCreate=true; - break; - - default: - $doCreate=false; - break; - } - } - } - - if( $doCreate ) - { - // Want to block creation of with existent EXTERNAL ID, if containers ARE DIFFERENT. - $item_id = intval($tcase_mgr->getInternalID($externalid, array('tproject_id' => $tproject_id))); - if( $item_id > 0) - { - // who is his parent ? - $owner = $tcase_mgr->getTestSuite($item_id); - if( $owner != $container_id) - { - // Get full path of existent Test Cases - $stain = $tcase_mgr->tree_manager->get_path($item_id,null, 'name'); - $n = count($stain); - $stain[$n-1] = $tcasePrefix . config_get('testcase_cfg')->glue_character . $externalid . ':' . $stain[$n-1]; - $stain = implode('/',$stain); - - $resultMap[] = array($name,$messages['hit_with_same_external_ID'] . $stain); - $doCreate = false; - } - } - } - if( $doCreate ) - { - $createOptions = array('check_duplicate_name' => testcase::CHECK_DUPLICATE_NAME, - 'action_on_duplicate_name' => $duplicatedLogic['actionOnHit'], - 'external_id' => $externalid); - - if ($ret = $tcase_mgr->create($container_id,$name,$summary,$preconditions,$steps, - $personID,$kwIDs,$node_order,testcase::AUTOMATIC_ID, - $exec_type,$importance,$createOptions)) - { - $resultMap[] = array($name,$ret['msg']); - } - } - - // Custom Fields Management - // Check if CF with this name and that can be used on Test Cases is defined in current Test Project. - // If Check fails => give message to user. - // Else Import CF data - // - $hasCustomFieldsInfo = (isset($tc['customfields']) && !is_null($tc['customfields'])); - if($hasCustomFieldsInfo && !is_null($ret)) - { - if($tprojectHas['customFields']) - { - $msg = processCustomFields($tcase_mgr,$name,$ret['id'],$ret['tcversion_id'],$tc['customfields'], - $linkedCustomFields,$feedbackMsg); - if( !is_null($msg) ) - { - $resultMap = array_merge($resultMap,$msg); - } - } - else - { - // Can not import Custom Fields Values, give feedback - $msg[]=array($name,$messages['cf_warning']); - $resultMap = array_merge($resultMap,$msg); - } - } - - // BUGID - 20090205 - franciscom - // Requirements Management - // Check if Requirement ... - // If Check fails => give message to user. - // Else Import - // - $hasRequirements=(isset($tc['requirements']) && !is_null($tc['requirements'])); - if($hasRequirements) - { - if( $tprojectHas['reqSpec'] ) - { - $msg = processRequirements($db,$req_mgr,$name,$ret['id'],$tc['requirements'],$reqSpecSet,$feedbackMsg); - if( !is_null($msg) ) - { - $resultMap = array_merge($resultMap,$msg); - } - } - else - { - $msg[]=array($name,$messages['reqspec_warning']); - $resultMap = array_merge($resultMap,$msg); - } - } - - } - return $resultMap; -} - - -// -------------------------------------------------------------------------------------- -/* - function: buildKeywordList - args : - returns: -*/ -function buildKeywordList($kwMap,$keywords) -{ - $items = array(); - $loop2do = sizeof($keywords); - for($jdx = 0; $jdx <$loop2do ; $jdx++) - { - $items[] = $kwMap[trim($keywords[$jdx]['name'])]; - } - return $items; -} - - -// -------------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------------- - -/* - function: Check if at least the file starts seems OK -*/ -function check_xml_tc_tsuite($fileName,$recursiveMode) -{ - $xml = @simplexml_load_file_wrapper($fileName); - $file_check = array('status_ok' => 0, 'msg' => 'xml_load_ko'); - if($xml !== FALSE) - { - $file_check = array('status_ok' => 1, 'msg' => 'ok'); - $elementName = $xml->getName(); - if($recursiveMode) - { - if($elementName != 'testsuite') - { - $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_xml_tsuite_file')); - } - } - else - { - if($elementName != 'testcases' && $elementName != 'testcase') - { - $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_xml_tcase_file')); - } - } - } - return $file_check; -} - - - -/* contribution by mirosvad - - Convert new line characters from XLS to HTML -*/ -function nl2p($str) -{ - return str_replace('

    ', '', '

    ' . preg_replace('#\n|\r#', '

    $0

    ', $str) . '

    '); //MS -} - - -/* - function: - - args : - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $key='action_on_duplicated_name'; - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'generate_new'; - - $key='hit_criteria'; - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'name'; - - - $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; - $args->useRecursion = isset($_REQUEST['useRecursion']) ? $_REQUEST['useRecursion'] : 0; - $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; - $args->container_id = isset($_REQUEST['containerID']) ? intval($_REQUEST['containerID']) : 0; - $args->bIntoProject = isset($_REQUEST['bIntoProject']) ? intval($_REQUEST['bIntoProject']) : 0; - - $args->containerType = isset($_REQUEST['containerType']) ? intval($_REQUEST['containerType']) : 0; - $args->do_upload = isset($_REQUEST['UploadFile']) ? 1 : 0; - - $args->userID = $_SESSION['userID']; - $args->tproject_id = $_SESSION['testprojectID']; - - return $args; -} - - -/** - * processCustomFields - * - * Analise custom field info related to test case being imported. - * If everything OK, assign to test case. - * Else return an array of messages. - * - * - * @internal revisions - * 20100905 - franciscom - BUGID 3431 - Custom Field values at Test Case VERSION Level - */ -function processCustomFields(&$tcaseMgr,$tcaseName,$tcaseId,$tcversionId,$cfValues,$cfDefinition,$messages) -{ - static $missingCfMsg; - $cf2insert=null; - $resultMsg=null; - - foreach($cfValues as $value) - { - if( isset($cfDefinition[$value['name']]) ) - { - $cf2insert[$cfDefinition[$value['name']]['id']]=array('type_id' => $cfDefinition[$value['name']]['type'], - 'cf_value' => $value['value']); - } - else - { - if( !isset($missingCfMsg[$value['name']]) ) - { - $missingCfMsg[$value['name']] = sprintf($messages['cfield'],$value['name'],$messages['tcase']); - } - $resultMsg[] = array($tcaseName,$missingCfMsg[$value['name']]); - } - } - - new dBug($cf2insert); - new dBug($tcversionId); - $tcaseMgr->cfield_mgr->design_values_to_db($cf2insert,$tcversionId,null,'simple'); - return $resultMsg; -} - -/** - * processRequirements - * - * Analise requirements info related to test case being imported. - * If everything OK, assign to test case. - * Else return an array of messages. - * - * 20100911 - amitkhullar - BUGID 3764 - */ -function processRequirements(&$dbHandler,&$reqMgr,$tcaseName,$tcaseId,$tcReq,$reqSpecSet,$messages) -{ - static $missingReqMsg; - static $missingReqSpecMsg; - static $cachedReqSpec; - $resultMsg=null; - $tables = tlObjectWithDB::getDBTables(array('requirements')); - - - foreach($tcReq as $ydx => $value) - { - $cachedReqSpec=array(); - $doit=false; - if( ($doit=isset($reqSpecSet[$value['doc_id']])) ) - { - if( !(isset($cachedReqSpec[$value['req_spec_title']])) ) - { - // $cachedReqSpec - // key: Requirement Specification Title - // value: map with follogin keys - // id => requirement specification id - // req => map with key: requirement document id - $cachedReqSpec[$value['req_spec_title']]['id']=$reqSpecSet[$value['doc_id']]['id']; - $cachedReqSpec[$value['req_spec_title']]['req']=null; - } - } - - if($doit) - { - $useit=false; - $req_spec_id=$cachedReqSpec[$value['req_spec_title']]['id']; - - // Check if requirement with desired document id exists on requirement specification. - // If not => create message for user feedback. - if( !($useit=isset($cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']])) ) - { - $sql = " SELECT REQ.id from {$tables['requirements']} REQ " . - " WHERE REQ.req_doc_id='{$dbHandler->prepare_string($value['doc_id'])}' " . - " AND REQ.srs_id={$req_spec_id} "; - - $rsx=$dbHandler->get_recordset($sql); - if( $useit=((!is_null($rsx) && count($rsx) > 0) ? true : false) ) - { - $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']]=$rsx[0]['id']; - } - } - - - if($useit) - { - $reqMgr->assign_to_tcase($cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']],$tcaseId); - } - else - { - if( !isset($missingReqMsg[$value['doc_id']]) ) - { - $missingReqMsg[$value['doc_id']]=sprintf($messages['req'], - $value['doc_id'],$value['req_spec_title']); - } - $resultMsg[] = array($tcaseName,$missingReqMsg[$value['doc_id']]); - } - } - else - { - // Requirement Specification not found - if( !isset($missingReqSpecMsg[$value['req_spec_title']]) ) - { - $missingReqSpecMsg[$value['req_spec_title']]=sprintf($messages['req_spec'],$value['req_spec_title']); - } - $resultMsg[] = array($tcaseName,$missingReqSpecMsg[$value['req_spec_title']]); - } - - } //foreach - - return $resultMsg; -} - - - -/** - * - * - */ -function importTestCasesFromSimpleXML(&$db,&$simpleXMLObj,$parentID,$tproject_id,$userID,$kwMap,$duplicateLogic) -{ - $resultMap = null; - $xmlTCs = $simpleXMLObj->xpath('//testcase'); - $tcData = getTestCaseSetFromSimpleXMLObj($xmlTCs); - if ($tcData) - { - $resultMap = saveImportedTCData($db,$tcData,$tproject_id,$parentID,$userID,$kwMap,$duplicateLogic); - } - return $resultMap; -} - -/** - * - * - * @internal revisions - * 20100317 - added internalid - BUGID 3236 - */ -function getTestCaseSetFromSimpleXMLObj($xmlTCs) -{ - $tcSet = null; - if (!$xmlTCs) - { - return $tcSet; - } - - $jdx = 0; - $loops2do=sizeof($xmlTCs); - $tcaseSet = array(); - - // $tcXML['elements'] = array('string' => array("summary","preconditions"), - // 'integer' => array("node_order","externalid","execution_type","importance")); - // $tcXML['attributes'] = array('string' => array("name"), 'integer' =>array('internalid')); - - // TICKET 4963: Test case / Tes suite XML format, new element to set author - $tcXML['elements'] = array('string' => array("summary" => null,"preconditions" => null, - "author_login" => null), - 'integer' => array("node_order" => null,"externalid" => null, - "execution_type" => null ,"importance" => null)); - $tcXML['attributes'] = array('string' => array("name" => 'trim'), - 'integer' =>array('internalid' => null)); - - for($idx = 0; $idx < $loops2do; $idx++) - { - $dummy = getItemsFromSimpleXMLObj(array($xmlTCs[$idx]),$tcXML); - $tc = $dummy[0]; - - if ($tc) - { - // Test Case Steps - $steps = getStepsFromSimpleXMLObj($xmlTCs[$idx]->steps->step); - $tc['steps'] = $steps; - - $keywords = getKeywordsFromSimpleXMLObj($xmlTCs[$idx]->keywords->keyword); - if ($keywords) - { - $tc['keywords'] = $keywords; - } - - $cf = getCustomFieldsFromSimpleXMLObj($xmlTCs[$idx]->custom_fields->custom_field); - if($cf) - { - $tc['customfields'] = $cf; - } - - $requirements = getRequirementsFromSimpleXMLObj($xmlTCs[$idx]->requirements->requirement); - if($requirements) - { - $tc['requirements'] = $requirements; - } - } - $tcaseSet[$jdx++] = $tc; - } - return $tcaseSet; -} - - -/** - * - * - * @internal revisions - * 20100821 - franciscom - BUGID 3695 - added "execution_type" - */ -function getStepsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("actions"=>null,"expectedresults" => null), - 'integer' => array("step_number" => null,"execution_type" => null)); - - // 20110205 - franciscom - seems key 'transformations' is not managed on - // getItemsFromSimpleXMLObj(), then ??? is useless??? - $itemStructure['transformations'] = array("expectedresults" => "expected_results"); - - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - - // need to do this due to (maybe) a wrong name choice for XML element - if( !is_null($items) ) - { - $loop2do = count($items); - for($idx=0; $idx < $loop2do; $idx++) - { - $items[$idx]['expected_results'] = ''; - if( isset($items[$idx]['expectedresults']) ) - { - $items[$idx]['expected_results'] = $items[$idx]['expectedresults']; - unset($items[$idx]['expectedresults']); - } - } - } - return $items; -} - -function getCustomFieldsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("name" => 'trim',"value" => 'trim')); - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - return $items; - -} - -function getRequirementsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("req_spec_title" => 'trim', - "doc_id" => 'trim' ,"title" => 'trim' )); - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - return $items; -} - -function getKeywordsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("notes" => null)); - $itemStructure['attributes'] = array('string' => array("name" => 'trim')); - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - return $items; -} - - -/* - function: importTestSuite - args : - returns: - - @internal revisions - 20120623 - franciscom - TICKET 5070 - test suite custom fields import - -*/ -function importTestSuitesFromSimpleXML(&$dbHandler,&$xml,$parentID,$tproject_id, - $userID,$kwMap,$importIntoProject = 0,$duplicateLogic) -{ - static $tsuiteXML; - static $tsuiteMgr; - static $myself; - static $callCounter = 0; - static $cfSpec; - static $doCF; - - $resultMap = array(); - if(is_null($tsuiteXML) ) - { - $myself = __FUNCTION__; - $tsuiteXML = array(); - $tsuiteXML['elements'] = array('string' => array("details" => null), - 'integer' => array("node_order" => null)); - $tsuiteXML['attributes'] = array('string' => array("name" => 'trim')); - - $tsuiteMgr = new testsuite($dbHandler); - $doCF = !is_null(($cfSpec = $tsuiteMgr->get_linked_cfields_at_design(null,null,null, - $tproject_id,'name'))); - } - - if($xml->getName() == 'testsuite') - { - - - // getItemsFromSimpleXMLObj() first argument must be an array - $dummy = getItemsFromSimpleXMLObj(array($xml),$tsuiteXML); - $tsuite = current($dummy); - $tsuiteID = $parentID; // hmmm, not clear - - if ($tsuite['name'] != "") - { - // Check if Test Suite with this name exists on this container - // if yes -> update instead of create - $info = $tsuiteMgr->get_by_name($tsuite['name'],$parentID); - if( is_null($info) ) - { - $ret = $tsuiteMgr->create($parentID,$tsuite['name'],$tsuite['details'],$tsuite['node_order']); - $tsuite['id'] = $ret['id']; - } - else - { - $ret = $tsuiteMgr->update(($tsuite['id'] = $info[0]['id']),$tsuite['name'],$tsuite['details'], - null,$tsuite['node_order']); - - } - unset($dummy); - - $tsuiteID = $tsuite['id']; // $tsuiteID is needed on more code pieces => DO NOT REMOVE - if (!$tsuite['id']) - { - return null; - } - - if($doCF) - { - $cf = getCustomFieldsFromSimpleXMLObj($xml->custom_fields->custom_field); - if(!is_null($cf)) - { - processTestSuiteCF($tsuiteMgr,$xml,$cfSpec,$cf,$tsuite,$tproject_id); - } - } - - if( $keywords = getKeywordsFromSimpleXMLObj($xml->keywords->keyword) ) - { - $kwIDs = buildKeywordList($kwMap,$keywords); - $tsuiteMgr->addKeywords($tsuite['id'],$kwIDs); - } - - unset($tsuite); - } - else if($importIntoProject) - { - $tsuiteID = intval($tproject_id); - } - - $childrenNodes = $xml->children(); - $loop2do = sizeof($childrenNodes); - - for($idx = 0; $idx < $loop2do; $idx++) - { - $target = $childrenNodes[$idx]; - switch($target->getName()) - { - case 'testcase': - // getTestCaseSetFromSimpleXMLObj() first argument must be an array - $tcData = getTestCaseSetFromSimpleXMLObj(array($target)); - $resultMap = array_merge($resultMap, - saveImportedTCData($dbHandler,$tcData,$tproject_id, - $tsuiteID,$userID,$kwMap,$duplicateLogic)); - unset($tcData); - break; - - case 'testsuite': - $resultMap = array_merge($resultMap, - $myself($dbHandler,$target,$tsuiteID,$tproject_id, - $userID,$kwMap,$importIntoProject,$duplicateLogic)); - break; - - - // Important Development Notice - // Due to XML file structure, while looping - // we will find also this children: - // node_order,keywords,custom_fields,details - // - // It's processing to get and save values is done - // on other pieces of this code. - // - // Form a logical point of view seems the better - // to consider and process here testcase and testsuite as children. - // - } - } - } - return $resultMap; -} - - -/** - * - * - * - **/ -function initializeGui(&$dbHandler,&$argsObj) -{ - $guiObj = new stdClass(); - $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); - $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); - $guiObj->hitCriteria = $argsObj->hit_criteria; - $guiObj->useRecursion = $argsObj->useRecursion; - $guiObj->containerID = $argsObj->container_id; - $guiObj->bImport = tlStringLen($argsObj->importType); - $guiObj->bIntoProject = $argsObj->bIntoProject; - $guiObj->resultMap = null; - $guiObj->container_name = ''; - - - $dest_common = TL_TEMP_PATH . session_id(). "-importtcs"; - $dest_files = array('XML' => $dest_common . ".xml"); - $guiObj->dest = $dest_files['XML']; - if(!is_null($argsObj->importType)) - { - $guiObj->dest = $dest_files[$argsObj->importType]; - } - - $guiObj->file_check = array('status_ok' => 1, 'msg' => 'ok'); - - if($argsObj->useRecursion) - { - $guiObj->import_title = lang_get('title_tsuite_import_to'); - $guiObj->container_description = lang_get('test_suite'); - } - else - { - $guiObj->import_title = lang_get('title_tc_import_to'); - $guiObj->container_description = lang_get('test_case'); - } - - if($argsObj->container_id) - { - $tree_mgr = new tree($dbHandler); - $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->container_id); - unset($tree_mgr); - $guiObj->container_name = $node_info['name']; - if($argsObj->container_id == $argsObj->tproject_id) - { - $guiObj->container_description = lang_get('testproject'); - } - } - - return $guiObj; -} - -/** - * - * - * @internal revisions - * @since 1.9.4 - * - **/ -function processTestSuiteCF(&$tsuiteMgr,$xmlObj,&$cfDefinition,&$cfValues,$tsuite,$tproject_id) -{ - - static $messages; - static $missingCfMsg; - - if(is_null($messages)) - { - $messages = array(); - $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); - $messages['start_warning'] = lang_get('start_warning'); - $messages['end_warning'] = lang_get('end_warning'); - $messages['testlink_warning'] = lang_get('testlink_warning'); - $messages['start_feedback'] = $messages['start_warning'] . "\n" . $messages['testlink_warning'] . "\n"; - $messages['cfield'] = lang_get('cf_value_not_imported_missing_cf_on_testproject'); - $messages['tsuite'] = lang_get('testsuite'); - } - - $cf2insert=null; - $resultMsg=null; - foreach($cfValues as $value) - { - if( isset($cfDefinition[$value['name']]) ) - { - $cf2insert[$cfDefinition[$value['name']]['id']]=array('type_id' => $cfDefinition[$value['name']]['type'], - 'cf_value' => $value['value']); - } - else - { - if( !isset($missingCfMsg[$value['name']]) ) - { - $missingCfMsg[$value['name']] = sprintf($messages['cfield'],$value['name'],$messages['tsuite']); - } - $resultMsg[] = array($tsuite['name'],$missingCfMsg[$value['name']]); - } - } - $tsuiteMgr->cfield_mgr->design_values_to_db($cf2insert,$tsuite['id'],null,'simple'); - return $resultMsg; -} -?> \ No newline at end of file +do_upload) { + + // check the uploaded file + $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; + + tLog('Uploaded file: ' . $source); + $doIt = false; + $gui->file_check = null; + if (($source != 'none') && ($source != '')) { + // ATTENTION: + // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using + // Firefox and Chrome. + if (! ($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes)) { + $gui->file_check['status_ok'] = 0; + $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'), + $_FILES['uploadedFile']['size'], $gui->importLimitBytes); + } + } + if ($doIt) { + $gui->file_check['status_ok'] = 1; + if (move_uploaded_file($source, $gui->dest)) { + tLog('Renamed uploaded file: ' . $source); + switch ($args->importType) { + case 'XML': + $pcheck_fn = "check_xml_tc_tsuite"; + $pimport_fn = "importTestCaseDataFromXML"; + break; + } + if (! is_null($pcheck_fn)) { + $gui->file_check = $pcheck_fn($gui->dest, $args->useRecursion); + } + } + if ($gui->file_check['status_ok'] && $pimport_fn) { + tLog('Check is Ok.'); + $opt = array(); + $opt['useRecursion'] = $args->useRecursion; + $opt['importIntoProject'] = $args->bIntoProject; + $opt['duplicateLogic'] = array( + 'hitCriteria' => $args->hit_criteria, + 'actionOnHit' => $args->action_on_duplicated_name + ); + $gui->resultMap = $pimport_fn($db, $gui->dest, + intval($args->container_id), intval($args->tproject_id), + intval($args->userID), $opt); + } + } elseif (is_null($gui->file_check)) { + + tLog('Missing upload file', 'WARNING'); + $gui->file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('please_choose_file_to_import') + ); + $args->importType = null; + } +} + +if ($args->useRecursion) { + $obj_mgr = new testsuite($db); + $gui->actionOptions = array( + 'update_last_version' => lang_get('update_last_testcase_version'), + 'generate_new' => lang_get('generate_new_testcase'), + 'create_new_version' => lang_get('create_new_testcase_version') + ); + + $gui->hitOptions = array( + 'name' => lang_get('same_name'), + 'internalID' => lang_get('same_internalID'), + 'externalID' => lang_get('same_externalID') + ); +} else { + $obj_mgr = new testcase($db); + $gui->actionOptions = array( + 'update_last_version' => lang_get('update_last_testcase_version'), + 'generate_new' => lang_get('generate_new_testcase'), + 'create_new_version' => lang_get('create_new_testcase_version') + ); + + $gui->hitOptions = array( + 'name' => lang_get('same_name'), + 'internalID' => lang_get('same_internalID'), + 'externalID' => lang_get('same_externalID') + ); +} + +$gui->testprojectName = $_SESSION['testprojectName']; +$gui->importTypes = $obj_mgr->get_import_file_types(); +$gui->action_on_duplicated_name = $args->action_on_duplicated_name; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: importTestCaseDataFromXML + * args : + * returns: + */ +function importTestCaseDataFromXML(&$db, $fileName, $parentID, $tproject_id, + $userID, $options = null) +{ + tLog('importTestCaseDataFromXML called for file: ' . $fileName); + $resultMap = null; + $my = array(); + $my['options'] = array( + 'useRecursion' => false, + 'importIntoProject' => 0, + 'duplicateLogic' => array( + 'hitCriteria' => 'name', + 'actionOnHit' => null + ) + ); + $my['options'] = array_merge($my['options'], (array) $options); + foreach ($my['options'] as $varname => $value) { + $$varname = $value; + } + + if (file_exists($fileName)) { + $xml = @simplexml_load_file_wrapper($fileName); + if ($xml !== false) { + $xmlKeywords = $xml->xpath('//keywords'); + $kwMap = null; + if ($xmlKeywords) { + $tproject = new testproject($db); + $loop2do = count($xmlKeywords); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $tproject->importKeywordsFromSimpleXML($tproject_id, + $xmlKeywords[$idx]); + } + $kwMap = $tproject->get_keywords_map($tproject_id); + $kwMap = is_null($kwMap) ? null : array_flip($kwMap); + } + + if (! $useRecursion && ($xml->getName() == 'testcases')) { + $resultMap = importTestCasesFromSimpleXML($db, $xml, $parentID, + $tproject_id, $userID, $kwMap, $duplicateLogic); + } + + if ($useRecursion && ($xml->getName() == 'testsuite')) { + $resultMap = importTestSuitesFromSimpleXML($db, $xml, + intval($parentID), intval($tproject_id), $userID, $kwMap, + $duplicateLogic, $importIntoProject); + } + } + } + return $resultMap; +} + +/* + * function: saveImportedTCData + * args : + * returns: + */ +function saveImportedTCData(&$db, $tcData, $tproject_id, $container_id, $userID, + $kwMap, + $duplicatedLogic = array( + 'hitCriteria' => 'name', + 'actionOnHit' => null + )) +{ + static $messages; + static $fieldSizeCfg; + static $feedbackMsg; + static $tcaseMgr; + static $tproject_mgr; + static $req_mgr; + static $safeSizeCfg; + static $linkedCustomFields; + static $tprojectHas; + static $reqSpecSet; + static $getVersionOpt; + static $userObj; + + if (! $tcData) { + return; + } + + $hasCustomFieldsInfo = false; + $hasRequirements = false; + + if (is_null($messages)) { + $feedbackMsg = array(); + $messages = array(); + $fieldSizeCfg = config_get('field_size'); + + $tcaseMgr = new testcase($db); + $tcaseMgr->setTestProject($tproject_id); + + $tproject_mgr = new testproject($db); + $req_mgr = new requirement_mgr($db); + $userObj = new tlUser(); + + $k2l = array( + 'already_exists_updated', + 'original_name', + 'testcase_name_too_long', + 'start_warning', + 'end_warning', + 'testlink_warning', + 'hit_with_same_external_ID' + ); + foreach ($k2l as $k) { + $messages[$k] = lang_get($k); + } + + $messages['start_feedback'] = $messages['start_warning'] . "\n" . + $messages['testlink_warning'] . "\n"; + $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); + $messages['reqspec_warning'] = lang_get( + 'no_reqspec_defined_can_not_import'); + + $feedbackMsg['cfield'] = lang_get( + 'cf_value_not_imported_missing_cf_on_testproject'); + $feedbackMsg['tcase'] = lang_get('testcase'); + $feedbackMsg['req'] = lang_get('req_not_in_req_spec_on_tcimport'); + $feedbackMsg['req_spec'] = lang_get('req_spec_ko_on_tcimport'); + + // because name can be changed automatically during item creation + // to avoid name conflict adding a suffix automatically generated, + // is better to use a max size < max allowed size + $safeSizeCfg = new stdClass(); + $safeSizeCfg->testcase_name = ($fieldSizeCfg->testcase_name) * 0.8; + + // Get CF with scope design time and allowed for test cases linked to this test project + $linkedCustomFields = $tcaseMgr->cfield_mgr->get_linked_cfields_at_design( + $tproject_id, 1, null, 'testcase', null, 'name'); + $tprojectHas['customFields'] = ! is_null($linkedCustomFields); + + $reqSpecSet = $tproject_mgr->getReqSpec($tproject_id, null, + array( + 'RSPEC.id', + 'NH.name AS title', + 'RSPEC.doc_id as rspec_doc_id', + 'REQ.req_doc_id' + ), 'req_doc_id'); + $tprojectHas['reqSpec'] = (! is_null($reqSpecSet) && + count($reqSpecSet) > 0); + + $getVersionOpt = array( + 'output' => 'minimun' + ); + $tcasePrefix = $tproject_mgr->getTestCasePrefix($tproject_id); + } + + $resultMap = array(); + $tc_qty = count($tcData); + $userIDCache = array(); + + for ($idx = 0; $idx < $tc_qty; $idx ++) { + $tc = $tcData[$idx]; + $name = $tc['name']; + $summary = $tc['summary']; + $steps = $tc['steps']; + + // I've changed value to use when order has not been provided + // from testcase:DEFAULT_ORDER to a counter, because with original solution + // an issue arise with 'save execution and go next' + // if use has not provided order I think is OK TestLink make any choice. + $node_order = isset($tc['node_order']) ? intval($tc['node_order']) : ($idx + + 1); + $internalid = $tc['internalid']; + $preconditions = $tc['preconditions']; + $exec_type = isset($tc['execution_type']) ? $tc['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; + $importance = isset($tc['importance']) ? $tc['importance'] : MEDIUM; + + $externalid = $tc['externalid']; + if (intval($externalid) <= 0) { + $externalid = null; + } + + $personID = $userID; + if (! is_null($tc['author_login'])) { + if (isset($userIDCache[$tc['author_login']])) { + $personID = $userIDCache[$tc['author_login']]; + } else { + $userObj->login = $tc['author_login']; + if ($userObj->readFromDB($db, tlUser::USER_O_SEARCH_BYLOGIN) == + tl::OK) { + $personID = $userObj->dbID; + } + + // I will put always a valid userID on this cache, + // this way if author_login does not exit, and is used multiple times + // i will do check for existence JUST ONCE. + $userIDCache[$tc['author_login']] = $personID; + } + } + + $name_len = tlStringLen($name); + if ($name_len > $fieldSizeCfg->testcase_name) { + // Will put original name inside summary + $xx = $messages['start_feedback']; + $xx .= sprintf($messages['testcase_name_too_long'], $name_len, + $fieldSizeCfg->testcase_name) . "\n"; + $xx .= $messages['original_name'] . "\n" . $name . "\n" . + $messages['end_warning'] . "\n"; + $summary = nl2br($xx) . $summary; + $name = tlSubStr($name, 0, $safeSizeCfg->testcase_name); + } + + $kwIDs = null; + if (isset($tc['keywords']) && $tc['keywords']) { + $kwIDs = implode(",", buildKeywordList($kwMap, $tc['keywords'])); + } + + $doCreate = true; + if ($duplicatedLogic['actionOnHit'] == 'update_last_version') { + switch ($duplicatedLogic['hitCriteria']) { + case 'name': + $info = $tcaseMgr->getDuplicatesByName($name, $container_id); + break; + + case 'internalID': + $dummy = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $internalid, $container_id); + if (! is_null($dummy)) { + $info = null; // TICKET 4925 + $info[$internalid] = $dummy; + } + break; + + case 'externalID': + $info = $tcaseMgr->get_by_external($externalid, + $container_id); + break; + } + + if (! is_null($info)) { + $tcase_qty = count($info); + switch ($tcase_qty) { + case 1: + $doCreate = false; + $tcase_id = key($info); + $last_version = $tcaseMgr->getLastVersionInfo($tcase_id, + $getVersionOpt); + $tcversion_id = $last_version['id']; + $ret = $tcaseMgr->update($tcase_id, $tcversion_id, $name, + $summary, $preconditions, $steps, $personID, $kwIDs, + $node_order, $exec_type, $importance); + + $ret['id'] = $tcase_id; + $ret['tcversion_id'] = $tcversion_id; + $resultMap[] = array( + $name, + $messages['already_exists_updated'] + ); + break; + + case 0: + $doCreate = true; + break; + + default: + $doCreate = false; + break; + } + } + } + + if ($doCreate) { + // Want to block creation of with existent EXTERNAL ID, if containers ARE DIFFERENT. + $item_id = intval( + $tcaseMgr->getInternalID($externalid, + array( + 'tproject_id' => $tproject_id + ))); + if ($item_id > 0) { + // who is his parent ? + $owner = $tcaseMgr->getTestSuite($item_id); + if ($owner != $container_id) { + // Get full path of existent Test Cases + $stain = $tcaseMgr->tree_manager->get_path($item_id, null, + 'name'); + $n = count($stain); + $stain[$n - 1] = $tcasePrefix . + config_get('testcase_cfg')->glue_character . $externalid . + ':' . $stain[$n - 1]; + $stain = implode('/', $stain); + + $resultMap[] = array( + $name, + $messages['hit_with_same_external_ID'] . $stain + ); + $doCreate = false; + } + } + } + if ($doCreate) { + $createOptions = array( + 'check_duplicate_name' => testcase::CHECK_DUPLICATE_NAME, + 'action_on_duplicate_name' => $duplicatedLogic['actionOnHit'], + 'external_id' => $externalid + ); + + if ($ret = $tcaseMgr->create($container_id, $name, $summary, + $preconditions, $steps, $personID, $kwIDs, $node_order, + testcase::AUTOMATIC_ID, $exec_type, $importance, $createOptions)) { + $resultMap[] = array( + $name, + $ret['msg'] + ); + } + } + + // Custom Fields Management + // Check if CF with this name and that can be used on Test Cases is defined in current Test Project. + // If Check fails => give message to user. + // Else Import CF data + $hasCustomFieldsInfo = (isset($tc['customfields']) && + ! is_null($tc['customfields'])); + if ($hasCustomFieldsInfo && ! is_null($ret)) { + if ($tprojectHas['customFields']) { + $msg = processCustomFields($tcaseMgr, $name, + $ret['tcversion_id'], $tc['customfields'], + $linkedCustomFields, $feedbackMsg); + if (! is_null($msg)) { + $resultMap = array_merge($resultMap, $msg); + } + } else { + // Can not import Custom Fields Values, give feedback + $msg[] = array( + $name, + $messages['cf_warning'] + ); + $resultMap = array_merge($resultMap, $msg); + } + } + + // BUGID - 20090205 - franciscom + // Requirements Management + // Check if Requirement ... + // If Check fails => give message to user. + // Else Import + $hasRequirements = (isset($tc['requirements']) && + ! is_null($tc['requirements'])); + if ($hasRequirements) { + if ($tprojectHas['reqSpec']) { + $msg = processRequirements($db, $req_mgr, $name, $ret['id'], + $tc['requirements'], $reqSpecSet, $feedbackMsg); + if (! is_null($msg)) { + $resultMap = array_merge($resultMap, $msg); + } + } else { + $msg[] = array( + $name, + $messages['reqspec_warning'] + ); + $resultMap = array_merge($resultMap, $msg); + } + } + } + return $resultMap; +} + +/* + * function: buildKeywordList + * args : + * returns: + */ +function buildKeywordList($kwMap, $keywords) +{ + $items = array(); + $loop2do = count($keywords); + for ($jdx = 0; $jdx < $loop2do; $jdx ++) { + $items[] = $kwMap[trim($keywords[$jdx]['name'])]; + } + return $items; +} + +/* + * function: Check if at least the file starts seems OK + */ +function checkXMLTCTsuite($fileName, $recursiveMode) +{ + $xml = @simplexml_load_file_wrapper($fileName); + $file_check = array( + 'status_ok' => 0, + 'msg' => 'xml_load_ko' + ); + if ($xml !== false) { + $file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $elementName = $xml->getName(); + if ($recursiveMode) { + if ($elementName != 'testsuite') { + $file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('wrong_xml_tsuite_file') + ); + } + } else { + if ($elementName != 'testcases' && $elementName != 'testcase') { + $file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('wrong_xml_tcase_file') + ); + } + } + } + return $file_check; +} + +/* + * contribution by mirosvad - + * Convert new line characters from XLS to HTML + */ +function nl2p($str) +{ + return str_replace('

    ', '', + '

    ' . preg_replace('#\n|\r#', '

    $0

    ', $str) . '

    '); // MS +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $key = 'action_on_duplicated_name'; + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'generate_new'; + + $key = 'hit_criteria'; + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'name'; + + $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; + $args->useRecursion = isset($_REQUEST['useRecursion']) ? $_REQUEST['useRecursion'] : 0; + $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; + $args->container_id = isset($_REQUEST['containerID']) ? intval( + $_REQUEST['containerID']) : 0; + $args->bIntoProject = isset($_REQUEST['bIntoProject']) ? intval( + $_REQUEST['bIntoProject']) : 0; + + $args->containerType = isset($_REQUEST['containerType']) ? intval( + $_REQUEST['containerType']) : 0; + $args->do_upload = isset($_REQUEST['UploadFile']) ? 1 : 0; + + $args->userID = $_SESSION['userID']; + $args->tproject_id = $_SESSION['testprojectID']; + + return $args; +} + +/** + * processCustomFields + * + * Analise custom field info related to test case being imported. + * If everything OK, assign to test case. + * Else return an array of messages. + * + * + * @internal revisions + * 20100905 - franciscom - BUGID 3431 - Custom Field values at Test Case VERSION Level + */ +function processCustomFields(&$tcaseMgr, $tcaseName, $tcversionId, $cfValues, + $cfDefinition, $messages) +{ + static $missingCfMsg; + $cf2insert = null; + $resultMsg = null; + + foreach ($cfValues as $value) { + if (isset($cfDefinition[$value['name']])) { + $cf2insert[$cfDefinition[$value['name']]['id']] = array( + 'type_id' => $cfDefinition[$value['name']]['type'], + 'cf_value' => $value['value'] + ); + } else { + if (! isset($missingCfMsg[$value['name']])) { + $missingCfMsg[$value['name']] = sprintf($messages['cfield'], + $value['name'], $messages['tcase']); + } + $resultMsg[] = array( + $tcaseName, + $missingCfMsg[$value['name']] + ); + } + } + + new dBug($cf2insert); + new dBug($tcversionId); + $tcaseMgr->cfield_mgr->design_values_to_db($cf2insert, $tcversionId, null, + 'simple'); + return $resultMsg; +} + +/** + * processRequirements + * + * Analise requirements info related to test case being imported. + * If everything OK, assign to test case. + * Else return an array of messages. + * + * 20100911 - amitkhullar - BUGID 3764 + */ +function processRequirements(&$dbHandler, &$reqMgr, $tcaseName, $tcaseId, $tcReq, + $reqSpecSet, $messages) +{ + static $missingReqMsg; + static $missingReqSpecMsg; + static $cachedReqSpec; + $resultMsg = null; + $tables = tlObjectWithDB::getDBTables(array( + 'requirements' + )); + + foreach ($tcReq as $value) { + $cachedReqSpec = array(); + $doit = false; + if ($doit = isset($reqSpecSet[$value['doc_id']]) && + ! (isset($cachedReqSpec[$value['req_spec_title']]))) { + // $cachedReqSpec + // key: Requirement Specification Title + // value: map with follogin keys + // id => requirement specification id + // req => map with key: requirement document id + $cachedReqSpec[$value['req_spec_title']]['id'] = $reqSpecSet[$value['doc_id']]['id']; + $cachedReqSpec[$value['req_spec_title']]['req'] = null; + } + + if ($doit) { + $useit = false; + $req_spec_id = $cachedReqSpec[$value['req_spec_title']]['id']; + + // Check if requirement with desired document id exists on requirement specification. + // If not => create message for user feedback. + if (! ($useit = isset( + $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']]))) { + $sql = " SELECT REQ.id from {$tables['requirements']} REQ " . + " WHERE REQ.req_doc_id='{$dbHandler->prepare_string($value['doc_id'])}' " . + " AND REQ.srs_id={$req_spec_id} "; + + $rsx = $dbHandler->get_recordset($sql); + if ($useit = ((! empty($rsx)) ? true : false)) { + $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']] = $rsx[0]['id']; + } + } + + if ($useit) { + $reqMgr->assign_to_tcase( + $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']], + $tcaseId); + } else { + if (! isset($missingReqMsg[$value['doc_id']])) { + $missingReqMsg[$value['doc_id']] = sprintf($messages['req'], + $value['doc_id'], $value['req_spec_title']); + } + $resultMsg[] = array( + $tcaseName, + $missingReqMsg[$value['doc_id']] + ); + } + } else { + // Requirement Specification not found + if (! isset($missingReqSpecMsg[$value['req_spec_title']])) { + $missingReqSpecMsg[$value['req_spec_title']] = sprintf( + $messages['req_spec'], $value['req_spec_title']); + } + $resultMsg[] = array( + $tcaseName, + $missingReqSpecMsg[$value['req_spec_title']] + ); + } + } // foreach + + return $resultMsg; +} + +/** + */ +function importTestCasesFromSimpleXML(&$db, &$simpleXMLObj, $parentID, + $tproject_id, $userID, $kwMap, $duplicateLogic) +{ + $resultMap = null; + $xmlTCs = $simpleXMLObj->xpath('//testcase'); + $tcData = getTestCaseSetFromSimpleXMLObj($xmlTCs); + if ($tcData) { + $resultMap = saveImportedTCData($db, $tcData, $tproject_id, $parentID, + $userID, $kwMap, $duplicateLogic); + } + return $resultMap; +} + +/** + * + * @internal revisions + * 20100317 - added internalid - BUGID 3236 + */ +function getTestCaseSetFromSimpleXMLObj($xmlTCs) +{ + $tcSet = null; + if (! $xmlTCs) { + return $tcSet; + } + + $jdx = 0; + $loops2do = count($xmlTCs); + $tcaseSet = array(); + + // TICKET 4963: Test case / Tes suite XML format, new element to set author + $tcXML['elements'] = array( + 'string' => array( + "summary" => null, + "preconditions" => null, + "author_login" => null + ), + 'integer' => array( + "node_order" => null, + "externalid" => null, + "execution_type" => null, + "importance" => null + ) + ); + $tcXML['attributes'] = array( + 'string' => array( + "name" => 'trim' + ), + 'integer' => array( + 'internalid' => null + ) + ); + + for ($idx = 0; $idx < $loops2do; $idx ++) { + $dummy = getItemsFromSimpleXMLObj(array( + $xmlTCs[$idx] + ), $tcXML); + $tc = $dummy[0]; + + if ($tc) { + // Test Case Steps + $steps = getStepsFromSimpleXMLObj($xmlTCs[$idx]->steps->step); + $tc['steps'] = $steps; + + $keywords = getKeywordsFromSimpleXMLObj( + $xmlTCs[$idx]->keywords->keyword); + if ($keywords) { + $tc['keywords'] = $keywords; + } + + $cf = getCustomFieldsFromSimpleXMLObj( + $xmlTCs[$idx]->custom_fields->custom_field); + if ($cf) { + $tc['customfields'] = $cf; + } + + $requirements = getRequirementsFromSimpleXMLObj( + $xmlTCs[$idx]->requirements->requirement); + if ($requirements) { + $tc['requirements'] = $requirements; + } + } + $tcaseSet[$jdx ++] = $tc; + } + return $tcaseSet; +} + +/** + * + * @internal revisions + * 20100821 - franciscom - BUGID 3695 - added "execution_type" + */ +function getStepsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "actions" => null, + "expectedresults" => null + ), + 'integer' => array( + "step_number" => null, + "execution_type" => null + ) + ); + + // 20110205 - franciscom - seems key 'transformations' is not managed on + // getItemsFromSimpleXMLObj(), then ??? is useless??? + $itemStructure['transformations'] = array( + "expectedresults" => "expected_results" + ); + + $items = getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); + + // need to do this due to (maybe) a wrong name choice for XML element + if (! is_null($items)) { + $loop2do = count($items); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $items[$idx]['expected_results'] = ''; + if (isset($items[$idx]['expectedresults'])) { + $items[$idx]['expected_results'] = $items[$idx]['expectedresults']; + unset($items[$idx]['expectedresults']); + } + } + } + return $items; +} + +function getCustomFieldsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "name" => 'trim', + "value" => 'trim' + ) + ); + return getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); +} + +function getRequirementsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "req_spec_title" => 'trim', + "doc_id" => 'trim', + "title" => 'trim' + ) + ); + return getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); +} + +function getKeywordsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "notes" => null + ) + ); + $itemStructure['attributes'] = array( + 'string' => array( + "name" => 'trim' + ) + ); + return getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); +} + +/* + * function: importTestSuite + * args : + * returns: + * + * @internal revisions + * 20120623 - franciscom - TICKET 5070 - test suite custom fields import + * + */ +function importTestSuitesFromSimpleXML(&$dbHandler, &$xml, $parentID, + $tproject_id, $userID, $kwMap, $duplicateLogic, $importIntoProject = 0) +{ + static $tsuiteXML; + static $tsuiteMgr; + static $myself; + static $cfSpec; + static $doCF; + + $resultMap = array(); + if (is_null($tsuiteXML)) { + $myself = __FUNCTION__; + $tsuiteXML = array(); + $tsuiteXML['elements'] = array( + 'string' => array( + "details" => null + ), + 'integer' => array( + "node_order" => null + ) + ); + $tsuiteXML['attributes'] = array( + 'string' => array( + "name" => 'trim' + ) + ); + + $tsuiteMgr = new testsuite($dbHandler); + $doCF = ! is_null( + ($cfSpec = $tsuiteMgr->get_linked_cfields_at_design(null, null, null, + $tproject_id, 'name'))); + } + + if ($xml->getName() == 'testsuite') { + + // getItemsFromSimpleXMLObj() first argument must be an array + $dummy = getItemsFromSimpleXMLObj(array( + $xml + ), $tsuiteXML); + $tsuite = current($dummy); + $tsuiteID = $parentID; // hmmm, not clear + + if ($tsuite['name'] != "") { + // Check if Test Suite with this name exists on this container + // if yes -> update instead of create + $info = $tsuiteMgr->get_by_name($tsuite['name'], $parentID); + if (is_null($info)) { + $ret = $tsuiteMgr->create($parentID, $tsuite['name'], + $tsuite['details'], $tsuite['node_order']); + $tsuite['id'] = $ret['id']; + } else { + $tsuiteMgr->update(($tsuite['id'] = $info[0]['id']), + $tsuite['name'], $tsuite['details'], null, + $tsuite['node_order']); + } + unset($dummy); + + $tsuiteID = $tsuite['id']; // $tsuiteID is needed on more code pieces => DO NOT REMOVE + if (! $tsuite['id']) { + return null; + } + + if ($doCF) { + $cf = getCustomFieldsFromSimpleXMLObj( + $xml->custom_fields->custom_field); + if (! is_null($cf)) { + processTestSuiteCF($tsuiteMgr, $xml, $cfSpec, $cf, $tsuite, + $tproject_id); + } + } + + if ($keywords = getKeywordsFromSimpleXMLObj($xml->keywords->keyword)) { + $kwIDs = buildKeywordList($kwMap, $keywords); + $tsuiteMgr->addKeywords($tsuite['id'], $kwIDs); + } + + unset($tsuite); + } elseif ($importIntoProject) { + $tsuiteID = intval($tproject_id); + } + + $childrenNodes = $xml->children(); + $loop2do = count($childrenNodes); + + for ($idx = 0; $idx < $loop2do; $idx ++) { + $target = $childrenNodes[$idx]; + switch ($target->getName()) { + case 'testcase': + // getTestCaseSetFromSimpleXMLObj() first argument must be an array + $tcData = getTestCaseSetFromSimpleXMLObj(array( + $target + )); + $resultMap = array_merge($resultMap, + saveImportedTCData($dbHandler, $tcData, $tproject_id, + $tsuiteID, $userID, $kwMap, $duplicateLogic)); + unset($tcData); + break; + + case 'testsuite': + $resultMap = array_merge($resultMap, + $myself($dbHandler, $target, $tsuiteID, $tproject_id, + $userID, $kwMap, $importIntoProject, $duplicateLogic)); + break; + + // Important Development Notice + // Due to XML file structure, while looping + // we will find also this children: + // node_order,keywords,custom_fields,details + // + // It's processing to get and save values is done + // on other pieces of this code. + // + // Form a logical point of view seems the better + // to consider and process here testcase and testsuite as children. + } + } + } + return $resultMap; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); + $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); + $guiObj->hitCriteria = $argsObj->hit_criteria; + $guiObj->useRecursion = $argsObj->useRecursion; + $guiObj->containerID = $argsObj->container_id; + $guiObj->bImport = tlStringLen($argsObj->importType); + $guiObj->bIntoProject = $argsObj->bIntoProject; + $guiObj->resultMap = null; + $guiObj->container_name = ''; + + $dest_common = TL_TEMP_PATH . session_id() . "-importtcs"; + $dest_files = array( + 'XML' => $dest_common . ".xml" + ); + $guiObj->dest = $dest_files['XML']; + if (! is_null($argsObj->importType)) { + $guiObj->dest = $dest_files[$argsObj->importType]; + } + + $guiObj->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + + if ($argsObj->useRecursion) { + $guiObj->import_title = lang_get('title_tsuite_import_to'); + $guiObj->container_description = lang_get('test_suite'); + } else { + $guiObj->import_title = lang_get('title_tc_import_to'); + $guiObj->container_description = lang_get('test_case'); + } + + if ($argsObj->container_id) { + $tree_mgr = new tree($dbHandler); + $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->container_id); + unset($tree_mgr); + $guiObj->container_name = $node_info['name']; + if ($argsObj->container_id == $argsObj->tproject_id) { + $guiObj->container_description = lang_get('testproject'); + } + } + + return $guiObj; +} + +/** + * + * @internal revisions + * @since 1.9.4 + * + */ +function processTestSuiteCF(&$tsuiteMgr, $xmlObj, &$cfDefinition, &$cfValues, + $tsuite, $tproject_id) +{ + static $messages; + static $missingCfMsg; + + if (is_null($messages)) { + $messages = array(); + $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); + $messages['start_warning'] = lang_get('start_warning'); + $messages['end_warning'] = lang_get('end_warning'); + $messages['testlink_warning'] = lang_get('testlink_warning'); + $messages['start_feedback'] = $messages['start_warning'] . "\n" . + $messages['testlink_warning'] . "\n"; + $messages['cfield'] = lang_get( + 'cf_value_not_imported_missing_cf_on_testproject'); + $messages['tsuite'] = lang_get('testsuite'); + } + + $cf2insert = null; + $resultMsg = null; + foreach ($cfValues as $value) { + if (isset($cfDefinition[$value['name']])) { + $cf2insert[$cfDefinition[$value['name']]['id']] = array( + 'type_id' => $cfDefinition[$value['name']]['type'], + 'cf_value' => $value['value'] + ); + } else { + if (! isset($missingCfMsg[$value['name']])) { + $missingCfMsg[$value['name']] = sprintf($messages['cfield'], + $value['name'], $messages['tsuite']); + } + $resultMsg[] = array( + $tsuite['name'], + $missingCfMsg[$value['name']] + ); + } + } + $tsuiteMgr->cfield_mgr->design_values_to_db($cf2insert, $tsuite['id'], null, + 'simple'); + return $resultMsg; +} +?> diff --git a/lib/testcases/tcCreateFromIssueMantisXML.php b/lib/testcases/tcCreateFromIssueMantisXML.php index 630ca8f252..f8797a49b9 100644 --- a/lib/testcases/tcCreateFromIssueMantisXML.php +++ b/lib/testcases/tcCreateFromIssueMantisXML.php @@ -1,526 +1,546 @@ - - * - * - * 21 - * testlink-test - * administrator - * normal - * minor - * have not tried - * new - * open - * none - * FromTestLink - * 1365184242 - * 1365184242 - * none - * V1 - * public - * ISSUE-V1 - * 1 - * ISSUE-V1 - * - * - * 20 - * testlink-test - * - * @internal revisions - * @since 1.9.7 - * - */ -require('../../config.inc.php'); -require_once('common.php'); -require_once('xml.inc.php'); - - -testlinkInitPage($db); -$templateCfg = templateConfiguration(); -$pcheck_fn=null; -$args = init_args(); -$gui = initializeGui($db,$args); - -if ($args->do_upload) -{ - $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; - tLog('Uploaded file: '.$source); - $doIt = false; - $gui->file_check = null; - if (($source != 'none') && ($source != '')) - { - // ATTENTION: - // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using - // Firefox and Chrome. - if( !($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes) ) - { - $gui->file_check['status_ok'] = 0; - $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'),$_FILES['uploadedFile']['size'],$gui->importLimitBytes); - } - } - if($doIt) - { - $gui->file_check['status_ok'] = 1; - if (move_uploaded_file($source, $gui->dest)) - { - tLog('Renamed uploaded file: ' . $source); - } - tLog('Check is Ok.'); - $opt = array(); - $gui->resultMap = importIssueFromXML($db,$gui->dest,intval($args->container_id), - intval($args->tproject_id),intval($args->userID),$opt); - } - else if(is_null($gui->file_check)) - { - - tLog('Missing upload file','WARNING'); - $gui->file_check = array('status_ok' => 0, 'msg' => lang_get('please_choose_file_to_import')); - $args->importType = null; - } -} - - -$gui->testprojectName = $args->tproject_name; -$gui->importTypes = array('XML' => 'Mantis XML'); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -// -------------------------------------------------------------------------------------- -/* - function: - args : - returns: -*/ -function importIssueFromXML(&$db,$fileName,$parentID,$tproject_id,$userID,$options=null) -{ - $xmlTCs = null; - $resultMap = null; - $my = array(); - $my['options'] = array('useRecursion' => false, 'importIntoProject' => 0, - 'duplicateLogic' => array('hitCriteria' => 'name', 'actionOnHit' => null)); - $my['options'] = array_merge($my['options'], (array)$options); - foreach($my['options'] as $varname => $value) - { - $$varname = $value; - } - - if (file_exists($fileName)) - { - $xml = @simplexml_load_file_wrapper($fileName); - if($xml !== FALSE) - { - $resultMap = importTestCasesFromIssueSimpleXML($db,$xml,$parentID,$tproject_id,$userID,null,$duplicateLogic); - } - } - return $resultMap; -} - - -// -------------------------------------------------------------------------------------- -/* - function: saveImportedTCData - args : - returns: -*/ -function saveImportedTCData(&$db,$tcData,$tproject_id,$container_id, - $userID,$kwMap,$duplicatedLogic = array('hitCriteria' => 'name', 'actionOnHit' => null)) -{ - static $messages; - static $fieldSizeCfg; - static $feedbackMsg; - static $tcase_mgr; - static $tproject_mgr; - static $req_spec_mgr; - static $req_mgr; - static $safeSizeCfg; - static $linkedCustomFields; - static $tprojectHas; - static $reqSpecSet; - static $getVersionOpt; - static $userObj; - - if (!$tcData) - { - return; - } - - // $tprojectHas = array('customFields' => false, 'reqSpec' => false); - $hasCustomFieldsInfo = false; - $hasRequirements = false; - - if(is_null($messages)) - { - $feedbackMsg = array(); - $messages = array(); - $fieldSizeCfg = config_get('field_size'); - - $tcase_mgr = new testcase($db); - $tproject_mgr = new testproject($db); - $req_spec_mgr = new requirement_spec_mgr($db); - $req_mgr = new requirement_mgr($db); - $userObj = new tlUser(); - - $k2l = array('already_exists_updated','original_name','testcase_name_too_long', - 'start_warning','end_warning','testlink_warning','hit_with_same_external_ID'); - foreach($k2l as $k) - { - $messages[$k] = lang_get($k); - } - - $messages['start_feedback'] = $messages['start_warning'] . "\n" . $messages['testlink_warning'] . "\n"; - $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); - $messages['reqspec_warning'] = lang_get('no_reqspec_defined_can_not_import'); - - - - $feedbackMsg['cfield']=lang_get('cf_value_not_imported_missing_cf_on_testproject'); - $feedbackMsg['tcase'] = lang_get('testcase'); - $feedbackMsg['req'] = lang_get('req_not_in_req_spec_on_tcimport'); - $feedbackMsg['req_spec'] = lang_get('req_spec_ko_on_tcimport'); - - - // because name can be changed automatically during item creation - // to avoid name conflict adding a suffix automatically generated, - // is better to use a max size < max allowed size - $safeSizeCfg = new stdClass(); - $safeSizeCfg->testcase_name=($fieldSizeCfg->testcase_name) * 0.8; - - - // Get CF with scope design time and allowed for test cases linked to this test project - $linkedCustomFields = $tcase_mgr->cfield_mgr->get_linked_cfields_at_design($tproject_id,1,null,'testcase',null,'name'); - $tprojectHas['customFields']=!is_null($linkedCustomFields); - - $reqSpecSet = $tproject_mgr->getReqSpec($tproject_id,null,array('RSPEC.id','NH.name AS title','RSPEC.doc_id as rspec_doc_id', 'REQ.req_doc_id'),'req_doc_id'); - $tprojectHas['reqSpec'] = (!is_null($reqSpecSet) && count($reqSpecSet) > 0); - - $getVersionOpt = array('output' => 'minimun'); - $tcasePrefix = $tproject_mgr->getTestCasePrefix($tproject_id); - } - - $resultMap = array(); - $tc_qty = sizeof($tcData); - $userIDCache = array(); - - for($idx = 0; $idx <$tc_qty ; $idx++) - { - $tc = $tcData[$idx]; - $name = $tc['name']; - $summary = $tc['summary']; - $steps = $tc['steps']; - - // I've changed value to use when order has not been provided - // from testcase:DEFAULT_ORDER to a counter, because with original solution - // an issue arise with 'save execution and go next' - // if use has not provided order I think is OK TestLink make any choice. - $node_order = isset($tc['node_order']) ? intval($tc['node_order']) : ($idx+1); - $internalid = $tc['internalid']; - $preconditions = $tc['preconditions']; - $exec_type = isset($tc['execution_type']) ? $tc['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; - $importance = isset($tc['importance']) ? $tc['importance'] : MEDIUM; - - $externalid = $tc['externalid']; - if( intval($externalid) <= 0 ) - { - $externalid = null; - } - - $personID = $userID; - if( !is_null($tc['author_login']) ) - { - if( isset($userIDCache[$tc['author_login']]) ) - { - $personID = $userIDCache[$tc['author_login']]; - } - else - { - $userObj->login = $tc['author_login']; - if( $userObj->readFromDB($db,tlUser::USER_O_SEARCH_BYLOGIN) == tl::OK ) - { - $personID = $userObj->dbID; - } - - // I will put always a valid userID on this cache, - // this way if author_login does not exit, and is used multiple times - // i will do check for existence JUST ONCE. - $userIDCache[$tc['author_login']] = $personID; - } - } - - $name_len = tlStringLen($name); - if($name_len > $fieldSizeCfg->testcase_name) - { - // Will put original name inside summary - $xx = $messages['start_feedback']; - $xx .= sprintf($messages['testcase_name_too_long'],$name_len, $fieldSizeCfg->testcase_name) . "\n"; - $xx .= $messages['original_name'] . "\n" . $name. "\n" . $messages['end_warning'] . "\n"; - $summary = nl2br($xx) . $summary; - $name = tlSubStr($name, 0, $safeSizeCfg->testcase_name); - } - - - $kwIDs = null; - if (isset($tc['keywords']) && $tc['keywords']) - { - $kwIDs = implode(",",buildKeywordList($kwMap,$tc['keywords'])); - } - - $doCreate=true; - if( $duplicatedLogic['actionOnHit'] == 'update_last_version' ) - { - switch($duplicatedLogic['hitCriteria']) - { - case 'name': - $info = $tcase_mgr->getDuplicatesByName($name,$container_id); - break; - - case 'internalID': - $dummy = $tcase_mgr->tree_manager->get_node_hierarchy_info($internalid,$container_id); - if( !is_null($dummy) ) - { - $info = null; - $info[$internalid] = $dummy; - } - break; - - case 'externalID': - $info = $tcase_mgr->get_by_external($externalid,$container_id); - break; - - - } - - if( !is_null($info) ) - { - $tcase_qty = count($info); - switch($tcase_qty) - { - case 1: - $doCreate=false; - $tcase_id = key($info); - $last_version = $tcase_mgr->get_last_version_info($tcase_id,$getVersionOpt); - $tcversion_id = $last_version['id']; - $ret = $tcase_mgr->update($tcase_id,$tcversion_id,$name,$summary, - $preconditions,$steps,$personID,$kwIDs, - $node_order,$exec_type,$importance); - - $ret['id'] = $tcase_id; - $ret['tcversion_id'] = $tcversion_id; - $resultMap[] = array($name,$messages['already_exists_updated']); - break; - - case 0: - $doCreate=true; - break; - - default: - $doCreate=false; - break; - } - } - } - - if( $doCreate ) - { - // Want to block creation of with existent EXTERNAL ID, if containers ARE DIFFERENT. - $item_id = intval($tcase_mgr->getInternalID($externalid, array('tproject_id' => $tproject_id))); - if( $item_id > 0) - { - // who is his parent ? - $owner = $tcase_mgr->getTestSuite($item_id); - if( $owner != $container_id) - { - // Get full path of existent Test Cases - $stain = $tcase_mgr->tree_manager->get_path($item_id,null, 'name'); - $n = count($stain); - $stain[$n-1] = $tcasePrefix . config_get('testcase_cfg')->glue_character . $externalid . ':' . $stain[$n-1]; - $stain = implode('/',$stain); - - $resultMap[] = array($name,$messages['hit_with_same_external_ID'] . $stain); - $doCreate = false; - } - } - } - if( $doCreate ) - { - $createOptions = array('check_duplicate_name' => testcase::CHECK_DUPLICATE_NAME, - 'action_on_duplicate_name' => $duplicatedLogic['actionOnHit'], - 'external_id' => $externalid); - - if ($ret = $tcase_mgr->create($container_id,$name,$summary,$preconditions,$steps, - $personID,$kwIDs,$node_order,testcase::AUTOMATIC_ID, - $exec_type,$importance,$createOptions)) - { - $resultMap[] = array($name,$ret['msg']); - } - } - - } - return $resultMap; -} - - - -/* contribution by mirosvad - - Convert new line characters from XLS to HTML -*/ -function nl2p($str) -{ - return str_replace('

    ', '', '

    ' . preg_replace('#\n|\r#', '

    $0

    ', $str) . '

    '); //MS -} - - -/* - function: - - args : - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; - $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; - $args->container_id = isset($_REQUEST['containerID']) ? intval($_REQUEST['containerID']) : 0; - $args->containerType = isset($_REQUEST['containerType']) ? intval($_REQUEST['containerType']) : 0; - $args->do_upload = isset($_REQUEST['UploadFile']) ? 1 : 0; - - $args->userID = intval($_SESSION['userID']); - $args->tproject_id = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; - - - return $args; -} - - - -/** - * - * - */ -function importTestCasesFromIssueSimpleXML(&$db,&$simpleXMLObj,$parentID,$tproject_id,$userID,$kwMap,$duplicateLogic) -{ - $resultMap = null; - $tcData = getTestCaseSetFromIssueSimpleXMLObj($simpleXMLObj); - if ($tcData) - { - $resultMap = saveImportedTCData($db,$tcData,$tproject_id,$parentID,$userID,$kwMap,$duplicateLogic); - } - return $resultMap; -} - -/** - * - * - * @internal revisions - * 20100317 - added internalid - BUGID 3236 - */ -function getTestCaseSetFromIssueSimpleXMLObj($xmlObj) -{ - $itemSet = null; - if (!$xmlObj) - { - return $itemSet; - } - - - $l18n = init_labels( array('issue_issue' => null, 'issue_steps_to_reproduce' => null, 'issue_summary' => null, - 'issue_target_version' => null,'issue_description' => null, - 'issue_additional_information' => null)); - - $jdx = 0; - $xmlIssue = $xmlObj->issue; - $loops2do=sizeof($xmlIssue); - - $XMLDef['elements'] = array('string' => array('summary' => null,'description' => null, - 'additional_information' => null, - 'steps_to_reproduce' => null, - 'target_version' => null, 'id' => null)); - $itemSet = array(); - $nl = "

    "; - for($idx = 0; $idx < $loops2do; $idx++) - { - $dummy = getItemsFromSimpleXMLObj(array($xmlIssue[$idx]),$XMLDef); - $dummy = $dummy[0]; - - $isum = $l18n['issue_description'] . $nl . $dummy['description']; - if(!is_null($dummy['steps_to_reproduce'])) - { - $isum .= $nl . $l18n['issue_steps_to_reproduce'] . $nl . $dummy['steps_to_reproduce']; - } - if(!is_null($dummy['additional_information'])) - { - $isum .= $nl . $l18n['issue_additional_information'] . $nl . $dummy['additional_information']; - } - - $itemSet[$jdx++] = array('name' => ($l18n['issue_issue'] . ':' . $dummy['id'] . ' - ' . $dummy['summary']), - 'summary' => $isum, 'steps' => null, 'internalid' => null, 'externalid' => null, - 'author_login' => null, 'preconditions' => null); - } - return $itemSet; -} - - - - -/** - * - * - * - **/ -function initializeGui(&$dbHandler,&$argsObj) -{ - $guiObj = new stdClass(); - $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); - $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); - $guiObj->containerID = $argsObj->container_id; - $guiObj->refreshTree = $guiObj->doImport = tlStringLen($argsObj->importType); - $guiObj->resultMap = null; - $guiObj->container_name = ''; - $guiObj->file_check = array('status_ok' => 1, 'msg' => 'ok'); - $guiObj->import_title = lang_get('title_tc_import_to'); - $guiObj->container_description = lang_get('test_case'); - - $dest_common = TL_TEMP_PATH . session_id(). "-importtcs"; - $dest_files = array('XML' => $dest_common . ".xml"); - $guiObj->dest = $dest_files['XML']; - if(!is_null($argsObj->importType)) - { - $guiObj->dest = $dest_files[$argsObj->importType]; - } - - if($argsObj->container_id) - { - $tree_mgr = new tree($dbHandler); - $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->container_id); - unset($tree_mgr); - $guiObj->container_name = $node_info['name']; - if($argsObj->container_id == $argsObj->tproject_id) - { - $guiObj->container_description = lang_get('testproject'); - } - } - - return $guiObj; -} - -?> \ No newline at end of file + + * + * + * 21 + * testlink-test + * administrator + * normal + * minor + * have not tried + * new + * open + * none + * FromTestLink + * 1365184242 + * 1365184242 + * none + * V1 + * public + *

    ISSUE-V1 + * 1 + * ISSUE-V1 + *
    + * + * 20 + * testlink-test + * + * @internal revisions + * @since 1.9.7 + * + */ +require_once '../../config.inc.php'; +require_once 'common.php'; +require_once 'xml.inc.php'; + +testlinkInitPage($db); +$templateCfg = templateConfiguration(); +$pcheck_fn = null; +$args = initArgs(); +$gui = initializeGui($db, $args); + +if ($args->do_upload) { + $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; + tLog('Uploaded file: ' . $source); + $doIt = false; + $gui->file_check = null; + + // ATTENTION: + // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using + // Firefox and Chrome. + if (($source != 'none') && ($source != '') && + ! ($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes)) { + $gui->file_check['status_ok'] = 0; + $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'), + $_FILES['uploadedFile']['size'], $gui->importLimitBytes); + } + if ($doIt) { + $gui->file_check['status_ok'] = 1; + if (move_uploaded_file($source, $gui->dest)) { + tLog('Renamed uploaded file: ' . $source); + } + tLog('Check is Ok.'); + $opt = array(); + $gui->resultMap = importIssueFromXML($db, $gui->dest, + intval($args->container_id), intval($args->tproject_id), + intval($args->userID), $opt); + } elseif (is_null($gui->file_check)) { + + tLog('Missing upload file', 'WARNING'); + $gui->file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('please_choose_file_to_import') + ); + $args->importType = null; + } +} + +$gui->testprojectName = $args->tproject_name; +$gui->importTypes = array( + 'XML' => 'Mantis XML' +); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: + * args : + * returns: + */ +function importIssueFromXML(&$db, $fileName, $parentID, $tproject_id, $userID, + $options = null) +{ + $resultMap = null; + $my = array(); + $my['options'] = array( + 'useRecursion' => false, + 'importIntoProject' => 0, + 'duplicateLogic' => array( + 'hitCriteria' => 'name', + 'actionOnHit' => null + ) + ); + $my['options'] = array_merge($my['options'], (array) $options); + foreach ($my['options'] as $varname => $value) { + $$varname = $value; + } + + if (file_exists($fileName)) { + $xml = @simplexml_load_file_wrapper($fileName); + if ($xml !== false) { + $resultMap = importTestCasesFromIssueSimpleXML($db, $xml, $parentID, + $tproject_id, $userID, null, $duplicateLogic); + } + } + return $resultMap; +} + +/* + * function: saveImportedTCData + * args : + * returns: + */ +function saveImportedTCData(&$db, $tcData, $tproject_id, $container_id, $userID, + $kwMap, + $duplicatedLogic = array( + 'hitCriteria' => 'name', + 'actionOnHit' => null + )) +{ + static $messages; + static $fieldSizeCfg; + static $feedbackMsg; + static $tcaseMgr; + static $tproject_mgr; + static $safeSizeCfg; + static $linkedCustomFields; + static $tprojectHas; + static $reqSpecSet; + static $getVersionOpt; + static $userObj; + + if (! $tcData) { + return; + } + + if (is_null($messages)) { + $feedbackMsg = array(); + $messages = array(); + $fieldSizeCfg = config_get('field_size'); + + $tcaseMgr = new testcase($db); + $tproject_mgr = new testproject($db); + $userObj = new tlUser(); + + $k2l = array( + 'already_exists_updated', + 'original_name', + 'testcase_name_too_long', + 'start_warning', + 'end_warning', + 'testlink_warning', + 'hit_with_same_external_ID' + ); + foreach ($k2l as $k) { + $messages[$k] = lang_get($k); + } + + $messages['start_feedback'] = $messages['start_warning'] . "\n" . + $messages['testlink_warning'] . "\n"; + $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); + $messages['reqspec_warning'] = lang_get( + 'no_reqspec_defined_can_not_import'); + + $feedbackMsg['cfield'] = lang_get( + 'cf_value_not_imported_missing_cf_on_testproject'); + $feedbackMsg['tcase'] = lang_get('testcase'); + $feedbackMsg['req'] = lang_get('req_not_in_req_spec_on_tcimport'); + $feedbackMsg['req_spec'] = lang_get('req_spec_ko_on_tcimport'); + + // because name can be changed automatically during item creation + // to avoid name conflict adding a suffix automatically generated, + // is better to use a max size < max allowed size + $safeSizeCfg = new stdClass(); + $safeSizeCfg->testcase_name = ($fieldSizeCfg->testcase_name) * 0.8; + + // Get CF with scope design time and allowed for test cases linked to this test project + $linkedCustomFields = $tcaseMgr->cfield_mgr->get_linked_cfields_at_design( + $tproject_id, 1, null, 'testcase', null, 'name'); + $tprojectHas['customFields'] = ! is_null($linkedCustomFields); + + $reqSpecSet = $tproject_mgr->getReqSpec($tproject_id, null, + array( + 'RSPEC.id', + 'NH.name AS title', + 'RSPEC.doc_id as rspec_doc_id', + 'REQ.req_doc_id' + ), 'req_doc_id'); + $tprojectHas['reqSpec'] = (! is_null($reqSpecSet) && + count($reqSpecSet) > 0); + + $getVersionOpt = array( + 'output' => 'minimun' + ); + $tcasePrefix = $tproject_mgr->getTestCasePrefix($tproject_id); + } + + $resultMap = array(); + $tc_qty = count($tcData); + $userIDCache = array(); + + for ($idx = 0; $idx < $tc_qty; $idx ++) { + $tc = $tcData[$idx]; + $name = $tc['name']; + $summary = $tc['summary']; + $steps = $tc['steps']; + + // I've changed value to use when order has not been provided + // from testcase:DEFAULT_ORDER to a counter, because with original solution + // an issue arise with 'save execution and go next' + // if use has not provided order I think is OK TestLink make any choice. + $node_order = isset($tc['node_order']) ? intval($tc['node_order']) : ($idx + + 1); + $internalid = $tc['internalid']; + $preconditions = $tc['preconditions']; + $exec_type = isset($tc['execution_type']) ? $tc['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; + $importance = isset($tc['importance']) ? $tc['importance'] : MEDIUM; + + $externalid = $tc['externalid']; + if (intval($externalid) <= 0) { + $externalid = null; + } + + $personID = $userID; + if (! is_null($tc['author_login'])) { + if (isset($userIDCache[$tc['author_login']])) { + $personID = $userIDCache[$tc['author_login']]; + } else { + $userObj->login = $tc['author_login']; + if ($userObj->readFromDB($db, tlUser::USER_O_SEARCH_BYLOGIN) == + tl::OK) { + $personID = $userObj->dbID; + } + + // I will put always a valid userID on this cache, + // this way if author_login does not exit, and is used multiple times + // i will do check for existence JUST ONCE. + $userIDCache[$tc['author_login']] = $personID; + } + } + + $name_len = tlStringLen($name); + if ($name_len > $fieldSizeCfg->testcase_name) { + // Will put original name inside summary + $xx = $messages['start_feedback']; + $xx .= sprintf($messages['testcase_name_too_long'], $name_len, + $fieldSizeCfg->testcase_name) . "\n"; + $xx .= $messages['original_name'] . "\n" . $name . "\n" . + $messages['end_warning'] . "\n"; + $summary = nl2br($xx) . $summary; + $name = tlSubStr($name, 0, $safeSizeCfg->testcase_name); + } + + $kwIDs = null; + if (isset($tc['keywords']) && $tc['keywords']) { + $kwIDs = implode(",", buildKeywordList($kwMap, $tc['keywords'])); + } + + $doCreate = true; + if ($duplicatedLogic['actionOnHit'] == 'update_last_version') { + switch ($duplicatedLogic['hitCriteria']) { + case 'name': + $info = $tcaseMgr->getDuplicatesByName($name, $container_id); + break; + + case 'internalID': + $dummy = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $internalid, $container_id); + if (! is_null($dummy)) { + $info = null; + $info[$internalid] = $dummy; + } + break; + + case 'externalID': + $info = $tcaseMgr->get_by_external($externalid, + $container_id); + break; + } + + if (! is_null($info)) { + $tcase_qty = count($info); + switch ($tcase_qty) { + case 1: + $doCreate = false; + $tcase_id = key($info); + $last_version = $tcaseMgr->getLastVersionInfo($tcase_id, + $getVersionOpt); + $tcversion_id = $last_version['id']; + $ret = $tcaseMgr->update($tcase_id, $tcversion_id, $name, + $summary, $preconditions, $steps, $personID, $kwIDs, + $node_order, $exec_type, $importance); + + $ret['id'] = $tcase_id; + $ret['tcversion_id'] = $tcversion_id; + $resultMap[] = array( + $name, + $messages['already_exists_updated'] + ); + break; + + case 0: + $doCreate = true; + break; + + default: + $doCreate = false; + break; + } + } + } + + if ($doCreate) { + // Want to block creation of with existent EXTERNAL ID, if containers ARE DIFFERENT. + $item_id = intval( + $tcaseMgr->getInternalID($externalid, + array( + 'tproject_id' => $tproject_id + ))); + if ($item_id > 0) { + // who is his parent ? + $owner = $tcaseMgr->getTestSuite($item_id); + if ($owner != $container_id) { + // Get full path of existent Test Cases + $stain = $tcaseMgr->tree_manager->get_path($item_id, null, + 'name'); + $n = count($stain); + $stain[$n - 1] = $tcasePrefix . + config_get('testcase_cfg')->glue_character . $externalid . + ':' . $stain[$n - 1]; + $stain = implode('/', $stain); + + $resultMap[] = array( + $name, + $messages['hit_with_same_external_ID'] . $stain + ); + $doCreate = false; + } + } + } + if ($doCreate) { + $createOptions = array( + 'check_duplicate_name' => testcase::CHECK_DUPLICATE_NAME, + 'action_on_duplicate_name' => $duplicatedLogic['actionOnHit'], + 'external_id' => $externalid + ); + + if ($ret = $tcaseMgr->create($container_id, $name, $summary, + $preconditions, $steps, $personID, $kwIDs, $node_order, + testcase::AUTOMATIC_ID, $exec_type, $importance, $createOptions)) { + $resultMap[] = array( + $name, + $ret['msg'] + ); + } + } + } + return $resultMap; +} + +/* + * contribution by mirosvad - + * Convert new line characters from XLS to HTML + */ +function nl2p($str) +{ + return str_replace('

    ', '', + '

    ' . preg_replace('#\n|\r#', '

    $0

    ', $str) . '

    '); // MS +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; + $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; + $args->container_id = isset($_REQUEST['containerID']) ? intval( + $_REQUEST['containerID']) : 0; + $args->containerType = isset($_REQUEST['containerType']) ? intval( + $_REQUEST['containerType']) : 0; + $args->do_upload = isset($_REQUEST['UploadFile']) ? 1 : 0; + + $args->userID = intval($_SESSION['userID']); + $args->tproject_id = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tproject_name = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : null; + + return $args; +} + +/** + */ +function importTestCasesFromIssueSimpleXML(&$db, &$simpleXMLObj, $parentID, + $tproject_id, $userID, $kwMap, $duplicateLogic) +{ + $resultMap = null; + $tcData = getTestCaseSetFromIssueSimpleXMLObj($simpleXMLObj); + if ($tcData) { + $resultMap = saveImportedTCData($db, $tcData, $tproject_id, $parentID, + $userID, $kwMap, $duplicateLogic); + } + return $resultMap; +} + +/** + * + * @internal revisions + * 20100317 - added internalid - BUGID 3236 + */ +function getTestCaseSetFromIssueSimpleXMLObj($xmlObj) +{ + $itemSet = null; + if (! $xmlObj) { + return $itemSet; + } + + $l18n = init_labels( + array( + 'issue_issue' => null, + 'issue_steps_to_reproduce' => null, + 'issue_summary' => null, + 'issue_target_version' => null, + 'issue_description' => null, + 'issue_additional_information' => null + )); + + $jdx = 0; + $xmlIssue = $xmlObj->issue; + $loops2do = count($xmlIssue); + + $xmlDef['elements'] = array( + 'string' => array( + 'summary' => null, + 'description' => null, + 'additional_information' => null, + 'steps_to_reproduce' => null, + 'target_version' => null, + 'id' => null + ) + ); + $itemSet = array(); + $nl = "

    "; + for ($idx = 0; $idx < $loops2do; $idx ++) { + $dummy = getItemsFromSimpleXMLObj(array( + $xmlIssue[$idx] + ), $xmlDef); + $dummy = $dummy[0]; + + $isum = $l18n['issue_description'] . $nl . $dummy['description']; + if (! is_null($dummy['steps_to_reproduce'])) { + $isum .= $nl . $l18n['issue_steps_to_reproduce'] . $nl . + $dummy['steps_to_reproduce']; + } + if (! is_null($dummy['additional_information'])) { + $isum .= $nl . $l18n['issue_additional_information'] . $nl . + $dummy['additional_information']; + } + + $itemSet[$jdx ++] = array( + 'name' => ($l18n['issue_issue'] . ':' . $dummy['id'] . ' - ' . + $dummy['summary']), + 'summary' => $isum, + 'steps' => null, + 'internalid' => null, + 'externalid' => null, + 'author_login' => null, + 'preconditions' => null + ); + } + return $itemSet; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); + $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); + $guiObj->containerID = $argsObj->container_id; + $guiObj->refreshTree = $guiObj->doImport = tlStringLen($argsObj->importType); + $guiObj->resultMap = null; + $guiObj->container_name = ''; + $guiObj->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $guiObj->import_title = lang_get('title_tc_import_to'); + $guiObj->container_description = lang_get('test_case'); + + $dest_common = TL_TEMP_PATH . session_id() . "-importtcs"; + $dest_files = array( + 'XML' => $dest_common . ".xml" + ); + $guiObj->dest = $dest_files['XML']; + if (! is_null($argsObj->importType)) { + $guiObj->dest = $dest_files[$argsObj->importType]; + } + + if ($argsObj->container_id) { + $tree_mgr = new tree($dbHandler); + $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->container_id); + unset($tree_mgr); + $guiObj->container_name = $node_info['name']; + if ($argsObj->container_id == $argsObj->tproject_id) { + $guiObj->container_description = lang_get('testproject'); + } + } + + return $guiObj; +} + +?> diff --git a/lib/testcases/tcEdit.php b/lib/testcases/tcEdit.php index ce9728dfae..9e965134c7 100644 --- a/lib/testcases/tcEdit.php +++ b/lib/testcases/tcEdit.php @@ -1,856 +1,951 @@ -webEditorCfg['type'])); - -$templateCfg = templateConfiguration('tcEdit'); - -$commandMgr = new testcaseCommands($db,$args->user,$args->tproject_id); -$commandMgr->setTemplateCfg(templateConfiguration()); - -$testCaseEditorKeys = array('summary' => 'summary','preconditions' => 'preconditions'); -$init_inputs = true; -$opt_cfg = initializeOptionTransferCfg($optionTransferName,$args,$tproject_mgr); -$gui = initializeGui($db,$args,$cfg,$tcase_mgr,$tproject_mgr); - -$smarty = new TLSmarty(); - -$name_ok = 1; -$doRender = false; -$pfn = $args->doAction; - - -$testCaseEditorKeys = null; -switch($args->doAction) { - case "create": - case "edit": - case "doCreate": - $testCaseEditorKeys = array('summary' => 'summary','preconditions' => 'preconditions'); - break; - - - case "createStep": - case "editStep": - case "doCreateStep": - case "doCreateStepAndExit": - case "doCopyStep": - case "doUpdateStep": - case "doUpdateStepAndExit": - case "doUpdateStepAndInsert": - case "doDeleteStep": - case "doReorderSteps": - case "doInsertStep": - case "doResequenceSteps": - case "doStepOperationExit": - $testCaseEditorKeys = array('steps' => 'steps', 'expected_results' => 'expected_results'); - break; - -} - -switch($args->doAction) { - case "doUpdate": - case "doAdd2testplan": - case 'updateTPlanLinkToTCV': - $op = $commandMgr->$pfn($args,$_REQUEST); - break; - - case "create": - case "edit": - case "doCreate": - $op = $commandMgr->$pfn($args,$opt_cfg,array_keys($testCaseEditorKeys),$_REQUEST); - $doRender = true; - break; - - - case "delete": - case "doDelete": - case "createStep": - case "editStep": - case "doCreateStep": - case "doCreateStepAndExit": - case "doCopyStep": - case "doUpdateStep": - case "doUpdateStepAndExit": - case "doUpdateStepAndInsert": - case "doDeleteStep": - case "doReorderSteps": - case "doInsertStep": - case "doResequenceSteps": - case "setImportance": - case "setStatus": - case "setExecutionType": - case "setEstimatedExecDuration": - case "removeKeyword": - case "addKeyword": - case "freeze": - case "unfreeze": - case "doStepOperationExit": - case "removePlatform": - case "addPlatform": - -/* -var_dump($_REQUEST); -die(); - die(); -*/ - $op = $commandMgr->$pfn($args,$_REQUEST); - $doRender = true; - break; - - case "fileUpload": - $args->uploadOp = fileUploadManagement($db,$args->tcversion_id,$args->fileTitle,$tcase_mgr->getAttachmentTableName()); - $commandMgr->show($args,$_REQUEST,array('status_ok' => true),false); - break; - - case "deleteFile": - $fileInfo = deleteAttachment($db,$args->file_id,false); - if( $args->tcversion_id == 0 && null != $fileInfo ) { - $args->tcversion_id = $fileInfo['fk_id']; - } - $commandMgr->show($args,$_REQUEST,array('status_ok' => true),false); - break; - - - case "doAddRelation": - case "doDeleteRelation": - $op = $commandMgr->$pfn($args,$_REQUEST); - $doRender = true; - break; - -} - - -if( $doRender ) -{ - renderGui($args,$gui,$op,$templateCfg,$cfg,$testCaseEditorKeys); - exit(); -} - -// Things that one day will be managed by command file -if($args->delete_tc_version) { - $status_quo_map = $tcase_mgr->get_versions_status_quo($args->tcase_id); - $exec_status_quo = $tcase_mgr->get_exec_status($args->tcase_id); - $gui->delete_mode = 'single'; - $gui->delete_enabled = 1; - - $msg = ''; - $sq = null; - if(!is_null($exec_status_quo)) { - if(isset($exec_status_quo[$args->tcversion_id])) { - $sq = array($args->tcversion_id => $exec_status_quo[$args->tcversion_id]); - } - } - - if(intval($status_quo_map[$args->tcversion_id]['executed'])) { - $msg = lang_get('warning') . TITLE_SEP . lang_get('delete_linked_and_exec'); - } - else if(intval($status_quo_map[$args->tcversion_id]['linked'])) - { - $msg = lang_get('warning') . TITLE_SEP . lang_get('delete_linked'); - } - - $tcinfo = $tcase_mgr->get_by_id($args->tcase_id,$args->tcversion_id); - - $gui->main_descr = lang_get('title_del_tc') . - TITLE_SEP_TYPE3 . lang_get('version') . " " . $tcinfo[0]['version']; - $gui->testcase_name = $tcinfo[0]['name']; - $gui->testcase_id = $args->tcase_id; - $gui->tcversion_id = $args->tcversion_id; - $gui->delete_message = $msg; - $gui->exec_status_quo = $sq; - $gui->refreshTree = 0; - - $smarty->assign('gui',$gui); - $templateCfg = templateConfiguration('tcDelete'); - $smarty->display($templateCfg->template_dir . $templateCfg->default_template); -} else if($args->move_copy_tc) { - // need to get the testproject for the test case - $tproject_id = $tcase_mgr->get_testproject($args->tcase_id); - $the_tc_node = $tree_mgr->get_node_hierarchy_info($args->tcase_id); - $tc_parent_id = $the_tc_node['parent_id']; - $the_xx = $tproject_mgr->gen_combo_test_suites($tproject_id); - - $the_xx[$the_tc_node['parent_id']] .= ' (' . lang_get('current') . ')'; - $tc_info = $tcase_mgr->get_by_id($args->tcase_id); - - $container_qty = count($the_xx); - $gui->move_enabled = 1; - if ($container_qty == 1) { - // move operation is nonsense - $gui->move_enabled = 0; - } - - $gui->top_checked = 'checked=checked'; - $gui->bottom_checked = ''; - - $gui->array_container = $the_xx; - $gui->old_container = $the_tc_node['parent_id']; // original container - $gui->testsuite_id = $the_tc_node['parent_id']; - $gui->testcase_id = $args->tcase_id; - $gui->name = $tc_info[0]['name']; - $gui->testcase_name = $tcase_mgr->generateTimeStampName($gui->name); - - - $smarty->assign('gui', $gui); - $templateCfg = templateConfiguration('tcMove'); - $smarty->display($templateCfg->template_dir . $templateCfg->default_template); -} -else if($args->do_move) -{ - $result = $tree_mgr->change_parent($args->tcase_id,$args->new_container_id); - $tree_mgr->change_child_order($args->new_container_id,$args->tcase_id, - $args->target_position,$cfg->exclude_node_types); - - $gui->refreshTree = $args->refreshTree; - $tsuite_mgr->show($smarty,$gui,$templateCfg->template_dir,$args->old_container_id); -} else if($args->do_copy || $args->do_copy_ghost_zone) { - $args->stepAsGhost = $args->do_copy_ghost_zone; - $user_feedback=''; - $msg = ''; - $action_result = 'copied'; - $options = array('check_duplicate_name' => - config_get('check_names_for_duplicates'), - 'action_on_duplicate_name' => - config_get('action_on_duplicate_name'), - 'copy_also' => $args->copy, - 'stepAsGhost' => $args->do_copy_ghost_zone, - 'use_this_name' => $args->name, - 'copyOnlyLatest' => $args->copyOnlyLatestVersion); - - $result = $tcase_mgr->copy_to($args->tcase_id,$args->new_container_id,$args->user_id,$options); - $msg = $result['msg']; - if($result['status_ok']) - { - $tree_mgr->change_child_order($args->new_container_id,$result['id'], - $args->target_position,$cfg->exclude_node_types); - - $ts_sep = config_get('testsuite_sep'); - $tc_info = $tcase_mgr->get_by_id($args->tcase_id); - $container_info = $tree_mgr->get_node_hierarchy_info($args->new_container_id); - $container_path = $tree_mgr->get_path($args->new_container_id); - $path = ''; - - foreach($container_path as $key => $value) - { - $path .= $value['name'] . $ts_sep; - } - $path = trim($path,$ts_sep); - $user_feedback = sprintf(lang_get('tc_copied'),$tc_info[0]['name'],$path); - } - - $gui->refreshTree = $args->refreshTree; - $gui->viewerArgs['action'] = $action_result; - $gui->viewerArgs['refreshTree']=$args->refreshTree? 1 : 0; - $gui->viewerArgs['msg_result'] = $msg; - $gui->viewerArgs['user_feedback'] = $user_feedback; - $gui->path_info = null; - - $identity = new stdClass(); - $identity->id = $args->tcase_id; - $identity->tproject_id = $args->tproject_id; - $identity->version_id = $args->tcversion_id; - - $tcase_mgr->show($smarty,$gui,$identity,$gui->grants); - -} -else if($args->do_create_new_version) { - createNewVersion($smarty,$args,$gui,$tcase_mgr,$args->tcversion_id); -} -else if($args->do_create_new_version_from_latest) { - $ltcv = $tcase_mgr->getLatestVersionID($args->tcase_id); - createNewVersion($smarty,$args,$gui,$tcase_mgr,$ltcv); - -} -else if($args->do_activate_this || $args->do_deactivate_this) { - $commandMgr->setActiveAttr($args,$_REQUEST); - exit(); -} - -// ----------------------------------------------------------------------- - -/* - function: - - args: - - returns: - -*/ -function init_args(&$cfgObj,$otName,&$tcaseMgr) { - $tc_importance_default = config_get('testcase_importance_default'); - - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $rightlist_html_name = $otName . "_newRight"; - $args->assigned_keywords_list = isset($_REQUEST[$rightlist_html_name])? $_REQUEST[$rightlist_html_name] : ""; - $args->container_id = isset($_REQUEST['containerID']) ? intval($_REQUEST['containerID']) : 0; - - $args->file_id = isset($_REQUEST['file_id']) ? intval($_REQUEST['file_id']) : 0; - - $args->tcase_id = isset($_REQUEST['testcase_id']) ? intval($_REQUEST['testcase_id']) : 0; - if($args->tcase_id == 0) { - $args->tcase_id = isset($_REQUEST['tcase_id']) ? intval($_REQUEST['tcase_id']) : 0; - } - if($args->tcase_id == 0) { - $args->tcase_id = intval(isset($_REQUEST['relation_source_tcase_id']) ? - $_REQUEST['relation_source_tcase_id'] : 0); - } - - $args->tcversion_id = isset($_REQUEST['tcversion_id']) ? intval($_REQUEST['tcversion_id']) : 0; - - if( $args->tcversion_id == 0 && $args->tcase_id > 0 ) { - // get latest active version - $nu = key($tcaseMgr->get_last_active_version($args->tcase_id)); - } - - - $args->name = isset($_REQUEST['testcase_name']) ? $_REQUEST['testcase_name'] : null; - - // Normally Rich Web Editors - $args->summary = isset($_REQUEST['summary']) ? $_REQUEST['summary'] : null; - $args->preconditions = isset($_REQUEST['preconditions']) ? $_REQUEST['preconditions'] : null; - $args->steps = isset($_REQUEST['steps']) ? $_REQUEST['steps'] : null; - $args->expected_results = isset($_REQUEST['expected_results']) ? $_REQUEST['expected_results'] : null; - - $args->new_container_id = isset($_REQUEST['new_container']) ? intval($_REQUEST['new_container']) : 0; - $args->old_container_id = isset($_REQUEST['old_container']) ? intval($_REQUEST['old_container']) : 0; - $args->has_been_executed = isset($_REQUEST['has_been_executed']) ? intval($_REQUEST['has_been_executed']) : 0; - $args->exec_type = isset($_REQUEST['exec_type']) ? $_REQUEST['exec_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; - $args->importance = isset($_REQUEST['importance']) ? $_REQUEST['importance'] : $tc_importance_default; - $args->status = isset($_REQUEST['status']) ? $_REQUEST['status'] : 1; // sorry for the magic - - $args->estimatedExecDuration = isset($_REQUEST['estimated_execution_duration']) ? - $_REQUEST['estimated_execution_duration'] : null; - - - $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : ''; - - $key2loop = array('edit_tc' => 'edit', 'delete_tc' => 'delete','do_delete' => 'doDelete', - 'create_tc' => 'create','do_create' => 'doCreate'); - - foreach($key2loop as $key => $action) { - if( isset($_REQUEST[$key]) ) { - $args->doAction = $action; - break; - } - } - - - $key2loop = array('move_copy_tc','delete_tc_version','do_move','do_copy', - 'do_copy_ghost_zone','do_delete_tc_version', - 'do_create_new_version','do_create_new_version_from_latest'); - foreach($key2loop as $key) { - $args->$key = isset($_REQUEST[$key]) ? 1 : 0; - } - - $args->do_activate_this = isset($_REQUEST['activate_this_tcversion']) ? 1 : 0; - $args->do_deactivate_this = isset($_REQUEST['deactivate_this_tcversion']) ? 1 : 0; - $args->activeAttr = 0; - if( $args->do_activate_this ) { - $args->activeAttr = 1; - } - - - $args->target_position = isset($_REQUEST['target_position']) ? $_REQUEST['target_position'] : 'bottom'; - - $key2loop=array("keyword_assignments","requirement_assignments"); - foreach($key2loop as $key) { - $args->copy[$key] = isset($_REQUEST[$key])?true:false; - } - - - $args->show_mode = (isset($_REQUEST['show_mode']) && $_REQUEST['show_mode'] != '') ? $_REQUEST['show_mode'] : null; - - // Multiple Test Case Steps Feature - $args->step_number = isset($_REQUEST['step_number']) ? intval($_REQUEST['step_number']) : 0; - $args->step_id = isset($_REQUEST['step_id']) ? intval($_REQUEST['step_id']) : 0; - $args->step_set = isset($_REQUEST['step_set']) ? $_REQUEST['step_set'] : null; - $args->tcaseSteps = isset($_REQUEST['tcaseSteps']) ? $_REQUEST['tcaseSteps'] : null; - - - // from session - $args->testproject_id = $args->tproject_id = intval($_SESSION['testprojectID']); - - $args->user = $_SESSION['currentUser']; - $args->user_id = intval($_SESSION['userID']); - - $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? intval($_SESSION['setting_refresh_tree_on_action']) : 0; - - $args->opt_requirements = null; - if( isset($_SESSION['testprojectOptions']) ) { - $args->opt_requirements = $_SESSION['testprojectOptions']->requirementsEnabled; - $args->requirementsEnabled = $_SESSION['testprojectOptions']->requirementsEnabled; - } - - $args->basehref = $_SESSION['basehref']; - $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; - - - // Specialized webEditorConfiguration - $action2check = array("editStep" => true,"createStep" => true, - "doCreateStep" => true, - "doUpdateStep" => true, "doInsertStep" => true, - "doCopyStep" => true, - "doUpdateStepAndInsert" => true); - if( isset($action2check[$args->doAction]) ) { - $cfgObj->webEditorCfg = getWebEditorCfg('steps_design'); - } - - $args->stay_here = isset($_REQUEST['stay_here']) ? 1 : 0; - - - $dummy = getConfigAndLabels('testCaseStatus','code'); - $args->tcStatusCfg['status_code'] = $dummy['cfg']; - $args->tcStatusCfg['code_label'] = $dummy['lbl']; - $args->tc_status = isset($_REQUEST['tc_status']) ? intval($_REQUEST['tc_status']) : - $args->tcStatusCfg['status_code']['draft']; - - $dk = 'estimated_execution_duration'; - $args->$dk = trim(isset($_REQUEST[$dk]) ? $_REQUEST[$dk] : ''); - - - $args->fileTitle = isset($_REQUEST['fileTitle'])? $_REQUEST['fileTitle'] : ""; - - - - $args->relation_type = isset($_REQUEST['relation_type']) ? $_REQUEST['relation_type'] : null; - $args->relation_id = intval(isset($_REQUEST['relation_id']) ? $_REQUEST['relation_id'] : 0); - - $args->relation_destination_tcase = isset($_REQUEST['relation_destination_tcase']) ? - $_REQUEST['relation_destination_tcase'] : null; - - $args->relation_destination_tcase = str_replace(' ','',$args->relation_destination_tcase); - $getOpt = array('tproject_id' => null, 'output' => 'map'); - if( is_numeric($args->relation_destination_tcase) ) { - $getOpt['tproject_id'] = $args->tproject_id; - } - $args->dummy = $tcaseMgr->getInternalID($args->relation_destination_tcase,$getOpt); - - $args->destination_tcase_id = $args->dummy['id']; - - - $args->keyword_id = isset($_GET['keyword_id']) ? intval($_GET['keyword_id']) : 0; - - - $args->tckw_link_id = isset($_GET['tckw_link_id']) ? intval($_GET['tckw_link_id']) : 0; - - $args->tcplat_link_id = isset($_GET['tcplat_link_id']) ? intval($_GET['tcplat_link_id']) : 0; - - - $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval($_REQUEST['tplan_id']) : 0; - $args->platform_id = isset($_REQUEST['platform_id']) ? intval($_REQUEST['platform_id']) : 0; - - - $cbk = 'changeExecTypeOnSteps'; - $args->applyExecTypeChangeToAllSteps = isset($_REQUEST[$cbk]); - - $k2c = array('free_keywords','free_platforms'); - foreach ($k2c as $kv) { - $args->$kv = isset($_REQUEST[$kv]) ? $_REQUEST[$kv] : null; - } - - $args->copyOnlyLatestVersion = - isset($_REQUEST['copy_latest_version']) ? 1 : 0; - - $tcaseMgr->setTestProject($args->tproject_id); - - return $args; -} - - -/* - function: initializeOptionTransferCfg - args : - returns: -*/ -function initializeOptionTransferCfg($otName,&$argsObj,&$tprojectMgr) -{ - $otCfg = new stdClass(); - switch($argsObj->doAction) - { - case 'create': - case 'edit': - case 'doCreate': - $otCfg = opt_transf_empty_cfg(); - $otCfg->global_lbl = ''; - $otCfg->from->lbl = lang_get('available_kword'); - $otCfg->from->map = $tprojectMgr->get_keywords_map($argsObj->testproject_id); - $otCfg->to->lbl = lang_get('assigned_kword'); - break; - } - - $otCfg->js_ot_name = $otName; - return $otCfg; -} - -/* - function: createWebEditors - - When using tinymce or none as web editor, we need to set rows and cols - to appropriate values, to avoid an ugly ui. - null => use default values defined on editor class file - Rows and Cols values are useless for FCKeditor - - args : - - returns: object - -*/ -function createWebEditors($basehref,$editorCfg,$editorSet=null) -{ - $specGUICfg=config_get('spec_cfg'); - $layout=$specGUICfg->steps_results_layout; - - // Rows and Cols configuration - $owe = new stdClass(); - - $cols = array('steps' => array('horizontal' => 38, 'vertical' => 44), - 'expected_results' => array('horizontal' => 38, 'vertical' => 44)); - - $owe->cfg = array('summary' => array('rows'=> null,'cols' => null), - 'preconditions' => array('rows'=> null,'cols' => null) , - 'steps' => array('rows'=> null,'cols' => $cols['steps'][$layout]) , - 'expected_results' => array('rows'=> null, 'cols' => $cols['expected_results'][$layout])); - - $owe->editor = array(); - $force_create = is_null($editorSet); - foreach ($owe->cfg as $key => $value) - { - if( $force_create || isset($editorSet[$key]) ) - { - $owe->editor[$key] = web_editor($key,$basehref,$editorCfg); - } - else - { - unset($owe->cfg[$key]); - } - } - - return $owe; -} - -/* - function: getCfg - args : - returns: object -*/ -function getCfg() -{ - $cfg=new stdClass(); - $cfg->treemenu_default_testcase_order = config_get('treemenu_default_testcase_order'); - $cfg->spec = config_get('spec_cfg'); - $cfg->exclude_node_types = array('testplan' => 1, 'requirement' => 1, 'requirement_spec' => 1); - $cfg->tcase_template = config_get('testcase_template'); - $cfg->webEditorCfg=getWebEditorCfg('design'); - - $cfg->editorKeys = new stdClass(); - $cfg->editorKeys->testcase = array('summary' => true, 'preconditions' => true); - $cfg->editorKeys->step = array('steps' => true, 'expected_results' => true); - - return $cfg; -} - -/* - function: getGrants - args : - returns: object -*/ -function getGrants(&$dbHandler) { - $grants = new stdClass(); - $grants->requirement_mgmt = has_rights($dbHandler,"mgt_modify_req"); - return $grants; -} - - -/** - * - * - */ -function initializeGui(&$dbHandler,&$argsObj,$cfgObj,&$tcaseMgr,&$tprojMgr) { - $guiObj = new stdClass(); - $guiObj->uploadOp = null; - $guiObj->tplan_id = $argsObj->tplan_id; - $guiObj->tproject_id = $argsObj->tproject_id; - $guiObj->editorType = $cfgObj->webEditorCfg['type']; - $guiObj->grants = getGrants($dbHandler); - $guiObj->opt_requirements = $argsObj->opt_requirements; - $guiObj->action_on_duplicated_name = 'generate_new'; - $guiObj->show_mode = $argsObj->show_mode; - $guiObj->has_been_executed = $argsObj->has_been_executed; - $guiObj->attachments = null; - $guiObj->parent_info = null; - $guiObj->user_feedback = ''; - $guiObj->stay_here = $argsObj->stay_here; - $guiObj->steps_results_layout = $cfgObj->spec->steps_results_layout; - $guiObj->btn_reorder_testcases = lang_get('btn_reorder_testcases_externalid'); - $guiObj->import_limit = TL_REPOSITORY_MAXFILESIZE; - $guiObj->msg = ''; - - $guiObj->loadOnCancelURL = $_SESSION['basehref'] . - "/lib/testcases/archiveData.php?edit=testcase&id=" . $argsObj->tcase_id . - "&show_mode={$argsObj->show_mode}"; - - $guiObj->fileUploadURL = $_SESSION['basehref'] . $tcaseMgr->getFileUploadRelativeURL($argsObj); - - - if($argsObj->container_id > 0) { - $pnode_info = $tcaseMgr->tree_manager->get_node_hierarchy_info($argsObj->container_id); - $node_descr = array_flip($tcaseMgr->tree_manager->get_available_node_types()); - $guiObj->parent_info['name'] = $pnode_info['name']; - $guiObj->parent_info['description'] = lang_get($node_descr[$pnode_info['node_type_id']]); - } - - $guiObj->direct_link = $tcaseMgr->buildDirectWebLink($_SESSION['basehref'],$argsObj->tcase_id, - $argsObj->testproject_id); - - $guiObj->domainTCStatus = $argsObj->tcStatusCfg['code_label']; - - $grant2check = array('mgt_modify_tc','mgt_view_req','testplan_planning', - 'mgt_modify_product','keyword_assignment', - 'req_tcase_link_management', - 'testproject_edit_executed_testcases', - 'testproject_delete_executed_testcases'); - $guiObj->grants = new stdClass(); - foreach($grant2check as $right) { - $guiObj->$right = $guiObj->grants->$right = $argsObj->user->hasRight($dbHandler,$right,$argsObj->tproject_id); - } - - $guiObj->codeTrackerEnabled = $tprojMgr->isCodeTrackerEnabled($guiObj->tproject_id); - - return $guiObj; -} - -/** - * manage GUI rendering - * - */ -function renderGui(&$argsObj,$guiObj,$opObj,$templateCfg,$cfgObj,$editorKeys) { - $smartyObj = new TLSmarty(); - - // needed by webeditor loading logic present on inc_head.tpl - $smartyObj->assign('editorType',$guiObj->editorType); - - $renderType = 'none'; - - // - // key: operation requested (normally received from GUI on doAction) - // value: operation value to set on doAction HTML INPUT - // This is useful when you use same template (example xxEdit.tpl), - // for create and edit. - // When template is used for create -> operation: doCreate. - // When template is used for edit -> operation: doUpdate. - // - // used to set value of: $guiObj->operation - // - $actionOperation = array('create' => 'doCreate', 'doCreate' => 'doCreate', - 'edit' => 'doUpdate','delete' => 'doDelete', - 'createStep' => 'doCreateStep', - 'doCreateStep' => 'doCreateStep', - 'doCopyStep' => 'doUpdateStep', - 'editStep' => 'doUpdateStep', - 'doUpdateStep' => 'doUpdateStep', - 'doInsertStep' => 'doUpdateStep', - 'doUpdateStepAndInsert' => 'doUpdateStep'); - - $nak = array('doDelete','doDeleteStep','doReorderSteps','doResequenceSteps', - 'setImportance','setStatus','setExecutionType', - 'setEstimatedExecDuration','doAddRelation','doDeleteRelation', - 'removeKeyword','freeze','unfreeze','addKeyword', - 'removePlatform','addPlatform'); - - foreach($nak as $ak) { - $actionOperation[$ak] = ''; - } - - $key2work = 'initWebEditorFromTemplate'; - $initWebEditorFromTemplate = property_exists($opObj,$key2work) ? $opObj->$key2work : false; - $key2work = 'cleanUpWebEditor'; - $cleanUpWebEditor = property_exists($opObj,$key2work) ? $opObj->$key2work : false; - - $oWebEditor = createWebEditors($argsObj->basehref,$cfgObj->webEditorCfg,$editorKeys); - - foreach ($oWebEditor->cfg as $key => $value) { - $of = &$oWebEditor->editor[$key]; - $rows = $oWebEditor->cfg[$key]['rows']; - $cols = $oWebEditor->cfg[$key]['cols']; - - switch($argsObj->doAction) { - case "edit": - case "delete": - case "editStep": - $initWebEditorFromTemplate = false; - $of->Value = $argsObj->$key; - break; - - case "doCreate": - $initWebEditorFromTemplate = $opObj->actionOK; - $of->Value = $argsObj->$key; - break; - - case "doDelete": - case "doCopyStep": - case "doUpdateStep": - $initWebEditorFromTemplate = false; - $of->Value = $argsObj->$key; - break; - - case "create": - case "doCreateStep": - case "doInsertStep": - case "doUpdateStepAndInsert": - default: - $initWebEditorFromTemplate = true; - break; - } - $guiObj->operation = $actionOperation[$argsObj->doAction]; - - if($initWebEditorFromTemplate) { - $of->Value = getItemTemplateContents('testcase_template', $of->InstanceName, ''); - } else if( $cleanUpWebEditor ) { - $of->Value = ''; - } - $smartyObj->assign($key, $of->CreateHTML($rows,$cols)); - } - - switch($argsObj->doAction) { - case "doDelete": - $guiObj->refreshTree = $argsObj->refreshTree; - break; - } - - switch($argsObj->doAction) { - case "edit": - case "create": - case "delete": - case "createStep": - case "editStep": - case "doCreate": - case "doDelete": - case "doCreateStep": - case "doUpdateStep": - case "doDeleteStep": - case "doReorderSteps": - case "doCopyStep": - case "doInsertStep": - case "doResequenceSteps": - case "setImportance": - case "setStatus": - case "setExecutionType": - case "setEstimatedExecDuration": - case "doAddRelation": - case "doDeleteRelation": - case "doUpdateStepAndInsert": - case "removeKeyword": - case "addKeyword": - case "freeze": - case "unfreeze": - case "removePlatform": - case "addPlatform": - $renderType = 'template'; - - // Document this !!!! - $key2loop = get_object_vars($opObj); - foreach($key2loop as $key => $value) { - $guiObj->$key = $value; - } - $guiObj->operation = $actionOperation[$argsObj->doAction]; - - $tplDir = (!isset($opObj->template_dir) || is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; - $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; - - $pos = strpos($tpl, '.php'); - if($pos === false) { - $tpl = $tplDir . $tpl; - } else { - $renderType = 'redirect'; - } - break; - } - - switch($renderType) { - case 'template': - $smartyObj->assign('gui',$guiObj); - $smartyObj->display($tpl); - break; - - case 'redirect': - header("Location: {$tpl}"); - exit(); - break; - - default: - break; - } - -} - - -/** - * - */ -function createNewVersion(&$tplEng,&$argsObj,&$guiObj,&$tcaseMgr,$sourceTCVID) { - $user_feedback = ''; - $msg = lang_get('error_tc_add'); - - $op = $tcaseMgr->create_new_version($argsObj->tcase_id, - $argsObj->user_id,$sourceTCVID); - - $candidate = $sourceTCVID; - if ($op['msg'] == "ok") { - $candidate = $op['id']; - $user_feedback = sprintf(lang_get('tc_new_version'),$op['version']); - $msg = 'ok'; - $tcCfg = config_get('testcase_cfg'); - $isOpen = !$tcCfg->freezeTCVersionOnNewTCVersion; - $tcaseMgr->setIsOpen($argsObj->tcase_id,$sourceTCVID,$isOpen); - } - $identity = new stdClass(); - $identity->id = $argsObj->tcase_id; - $identity->tproject_id = $argsObj->tproject_id; - $identity->version_id = !is_null($argsObj->show_mode) ? $candidate : testcase::ALL_VERSIONS; - - $guiObj->viewerArgs['action'] = "do_update"; - $guiObj->viewerArgs['refreshTree'] = DONT_REFRESH; - $guiObj->viewerArgs['msg_result'] = $msg; - $guiObj->viewerArgs['user_feedback'] = $user_feedback; - $guiObj->path_info = null; - - // used to implement go back ?? - $guiObj->loadOnCancelURL = $_SESSION['basehref'] . - '/lib/testcases/archiveData.php?edit=testcase&id=' . $argsObj->tcase_id . - "&show_mode={$argsObj->show_mode}"; - - - $tcaseMgr->show($tplEng,$guiObj,$identity,$guiObj->grants, - array('getAttachments' => true)); - exit(); +webEditorCfg['type']); + +$templateCfg = templateConfiguration('tcEdit'); + +$commandMgr = new testcaseCommands($db, $args->user, $args->tproject_id); +$commandMgr->setTemplateCfg(templateConfiguration()); + +$testCaseEditorKeys = array( + 'summary' => 'summary', + 'preconditions' => 'preconditions' +); +$init_inputs = true; +$opt_cfg = initializeOptionTransferCfg($optionTransferName, $args, $tproject_mgr); +$gui = initializeGui($db, $args, $cfg, $tcaseMgr, $tproject_mgr); + +$smarty = new TLSmarty(); + +$name_ok = 1; +$doRender = false; +$pfn = $args->doAction; + +$testCaseEditorKeys = null; +switch ($args->doAction) { + case "create": + case "edit": + case "doCreate": + $testCaseEditorKeys = array( + 'summary' => 'summary', + 'preconditions' => 'preconditions' + ); + break; + + case "createStep": + case "editStep": + case "doCreateStep": + case "doCreateStepAndExit": + case "doCopyStep": + case "doUpdateStep": + case "doUpdateStepAndExit": + case "doUpdateStepAndInsert": + case "doDeleteStep": + case "doReorderSteps": + case "doInsertStep": + case "doResequenceSteps": + case "doStepOperationExit": + $testCaseEditorKeys = array( + 'steps' => 'steps', + 'expected_results' => 'expected_results' + ); + break; +} + +switch ($args->doAction) { + case "doUpdate": + case "doAdd2testplan": + case 'updateTPlanLinkToTCV': + $op = $commandMgr->$pfn($args, $_REQUEST); + break; + + case "create": + case "edit": + case "doCreate": + $op = $commandMgr->$pfn($args, $opt_cfg, array_keys($testCaseEditorKeys), + $_REQUEST); + $doRender = true; + break; + + case "delete": + case "doDelete": + case "createStep": + case "editStep": + case "doCreateStep": + case "doCreateStepAndExit": + case "doCopyStep": + case "doUpdateStep": + case "doUpdateStepAndExit": + case "doUpdateStepAndInsert": + case "doDeleteStep": + case "doReorderSteps": + case "doInsertStep": + case "doResequenceSteps": + case "setImportance": + case "setStatus": + case "setExecutionType": + case "setEstimatedExecDuration": + case "removeKeyword": + case "addKeyword": + case "freeze": + case "unfreeze": + case "doStepOperationExit": + case "removePlatform": + case "addPlatform": + case "doAddRelation": + case "doDeleteRelation": + $op = $commandMgr->$pfn($args, $_REQUEST); + $doRender = true; + break; + + case "fileUpload": + $args->uploadOp = fileUploadManagement($db, $args->tcversion_id, + $args->fileTitle, $tcaseMgr->getAttachmentTableName()); + $commandMgr->show($args, $_REQUEST, array( + 'status_ok' => true + ), [ + 'updateCFOnDB' => false + ]); + break; + + case "deleteFile": + $fileInfo = deleteAttachment($db, $args->file_id, false); + if ($args->tcversion_id == 0 && null != $fileInfo) { + $args->tcversion_id = $fileInfo['fk_id']; + } + $commandMgr->show($args, $_REQUEST, array( + 'status_ok' => true + ), [ + 'updateCFOnDB' => false + ]); + break; +} + +if ($doRender) { + renderGui($args, $gui, $op, $templateCfg, $cfg, $testCaseEditorKeys); + exit(); +} + +// Things that one day will be managed by command file +if ($args->delete_tc_version) { + $status_quo_map = $tcaseMgr->getVersionsStatusQuo($args->tcase_id); + $exec_status_quo = $tcaseMgr->getExecStatus($args->tcase_id); + $gui->delete_mode = 'single'; + $gui->delete_enabled = 1; + + $msg = ''; + $sq = null; + if (! is_null($exec_status_quo) && + isset($exec_status_quo[$args->tcversion_id])) { + $sq = array( + $args->tcversion_id => $exec_status_quo[$args->tcversion_id] + ); + } + + if (intval($status_quo_map[$args->tcversion_id]['executed'])) { + $msg = lang_get('warning') . TITLE_SEP . + lang_get('delete_linked_and_exec'); + } elseif (intval($status_quo_map[$args->tcversion_id]['linked'])) { + $msg = lang_get('warning') . TITLE_SEP . lang_get('delete_linked'); + } + + $tcinfo = $tcaseMgr->get_by_id($args->tcase_id, $args->tcversion_id); + + $gui->main_descr = lang_get('title_del_tc') . TITLE_SEP_TYPE3 . + lang_get('version') . " " . $tcinfo[0]['version']; + $gui->testcase_name = $tcinfo[0]['name']; + $gui->testcase_id = $args->tcase_id; + $gui->tcversion_id = $args->tcversion_id; + $gui->delete_message = $msg; + $gui->exec_status_quo = $sq; + $gui->refreshTree = 0; + + $smarty->assign('gui', $gui); + $templateCfg = templateConfiguration('tcDelete'); + $smarty->display( + $templateCfg->template_dir . $templateCfg->default_template); +} elseif ($args->move_copy_tc) { + // need to get the testproject for the test case + $tproject_id = $tcaseMgr->get_testproject($args->tcase_id); + $the_tc_node = $tree_mgr->get_node_hierarchy_info($args->tcase_id); + $tc_parent_id = $the_tc_node['parent_id']; + $the_xx = $tproject_mgr->gen_combo_test_suites($tproject_id); + + $the_xx[$the_tc_node['parent_id']] .= ' (' . lang_get('current') . ')'; + $tc_info = $tcaseMgr->get_by_id($args->tcase_id); + + $container_qty = count($the_xx); + $gui->move_enabled = 1; + if ($container_qty == 1) { + // move operation is nonsense + $gui->move_enabled = 0; + } + + $gui->top_checked = 'checked=checked'; + $gui->bottom_checked = ''; + + $gui->array_container = $the_xx; + $gui->old_container = $the_tc_node['parent_id']; // original container + $gui->testsuite_id = $the_tc_node['parent_id']; + $gui->testcase_id = $args->tcase_id; + $gui->name = $tc_info[0]['name']; + $gui->testcase_name = $tcaseMgr->generateTimeStampName($gui->name); + + $smarty->assign('gui', $gui); + $templateCfg = templateConfiguration('tcMove'); + $smarty->display( + $templateCfg->template_dir . $templateCfg->default_template); +} elseif ($args->do_move) { + $result = $tree_mgr->change_parent($args->tcase_id, $args->new_container_id); + $tree_mgr->change_child_order($args->new_container_id, $args->tcase_id, + $args->target_position, $cfg->exclude_node_types); + + $gui->refreshTree = $args->refreshTree; + $tsuite_mgr->show($smarty, $gui, $templateCfg->template_dir, + $args->old_container_id); +} elseif ($args->do_copy || $args->do_copy_ghost_zone) { + $args->stepAsGhost = $args->do_copy_ghost_zone; + $user_feedback = ''; + $msg = ''; + $action_result = 'copied'; + $options = array( + 'check_duplicate_name' => config_get('check_names_for_duplicates'), + 'action_on_duplicate_name' => config_get('action_on_duplicate_name'), + 'copy_also' => $args->copy, + 'stepAsGhost' => $args->do_copy_ghost_zone, + 'use_this_name' => $args->name, + 'copyOnlyLatest' => $args->copyOnlyLatestVersion + ); + + $result = $tcaseMgr->copy_to($args->tcase_id, $args->new_container_id, + $args->user_id, $options); + $msg = $result['msg']; + if ($result['status_ok']) { + $tree_mgr->change_child_order($args->new_container_id, $result['id'], + $args->target_position, $cfg->exclude_node_types); + + $ts_sep = config_get('testsuite_sep'); + $tc_info = $tcaseMgr->get_by_id($args->tcase_id); + $container_info = $tree_mgr->get_node_hierarchy_info( + $args->new_container_id); + $container_path = $tree_mgr->get_path($args->new_container_id); + $path = ''; + + foreach ($container_path as $value) { + $path .= $value['name'] . $ts_sep; + } + $path = trim($path, $ts_sep); + $user_feedback = sprintf(lang_get('tc_copied'), $tc_info[0]['name'], + $path); + } + + $gui->refreshTree = $args->refreshTree; + $gui->viewerArgs['action'] = $action_result; + $gui->viewerArgs['refreshTree'] = $args->refreshTree ? 1 : 0; + $gui->viewerArgs['msg_result'] = $msg; + $gui->viewerArgs['user_feedback'] = $user_feedback; + $gui->path_info = null; + + $identity = new stdClass(); + $identity->id = $args->tcase_id; + $identity->tproject_id = $args->tproject_id; + $identity->version_id = $args->tcversion_id; + + $tcaseMgr->show($smarty, $gui, $identity, $gui->grants); +} elseif ($args->do_create_new_version) { + createNewVersion($smarty, $args, $gui, $tcaseMgr, $args->tcversion_id); +} elseif ($args->do_create_new_version_from_latest) { + $ltcv = $tcaseMgr->getLatestVersionID($args->tcase_id); + createNewVersion($smarty, $args, $gui, $tcaseMgr, $ltcv); +} elseif ($args->do_activate_this || $args->do_deactivate_this) { + $commandMgr->setActiveAttr($args, $_REQUEST); + exit(); +} + +/** + * Initialize arguments + * + * @param stdClass $cfgObj + * @param string $otName + * @param testcase $tcaseMgr + * @return stdClass + */ +function initArgs(&$cfgObj, $otName, &$tcaseMgr) +{ + $tc_importance_default = config_get('testcase_importance_default'); + + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args->stepSeq = isset($_REQUEST["stepSeq"]) ? $_REQUEST["stepSeq"] : ""; + + $rightlist_html_name = $otName . "_newRight"; + $args->assigned_keywords_list = isset($_REQUEST[$rightlist_html_name]) ? $_REQUEST[$rightlist_html_name] : ""; + $args->container_id = isset($_REQUEST['containerID']) ? intval( + $_REQUEST['containerID']) : 0; + + $args->file_id = isset($_REQUEST['file_id']) ? intval($_REQUEST['file_id']) : 0; + + $args->tcase_id = isset($_REQUEST['testcase_id']) ? intval( + $_REQUEST['testcase_id']) : 0; + if ($args->tcase_id == 0) { + $args->tcase_id = isset($_REQUEST['tcase_id']) ? intval( + $_REQUEST['tcase_id']) : 0; + } + if ($args->tcase_id == 0) { + $args->tcase_id = intval( + isset($_REQUEST['relation_source_tcase_id']) ? $_REQUEST['relation_source_tcase_id'] : 0); + } + + $args->tcversion_id = isset($_REQUEST['tcversion_id']) ? intval( + $_REQUEST['tcversion_id']) : 0; + + $args->name = isset($_REQUEST['testcase_name']) ? $_REQUEST['testcase_name'] : null; + + // Normally Rich Web Editors + $args->summary = isset($_REQUEST['summary']) ? $_REQUEST['summary'] : null; + $args->preconditions = isset($_REQUEST['preconditions']) ? $_REQUEST['preconditions'] : null; + $args->steps = isset($_REQUEST['steps']) ? $_REQUEST['steps'] : null; + $args->expected_results = isset($_REQUEST['expected_results']) ? $_REQUEST['expected_results'] : null; + + $args->new_container_id = isset($_REQUEST['new_container']) ? intval( + $_REQUEST['new_container']) : 0; + $args->old_container_id = isset($_REQUEST['old_container']) ? intval( + $_REQUEST['old_container']) : 0; + $args->has_been_executed = isset($_REQUEST['has_been_executed']) ? intval( + $_REQUEST['has_been_executed']) : 0; + $args->exec_type = isset($_REQUEST['exec_type']) ? $_REQUEST['exec_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; + $args->importance = isset($_REQUEST['importance']) ? $_REQUEST['importance'] : $tc_importance_default; + $args->status = isset($_REQUEST['status']) ? $_REQUEST['status'] : 1; // sorry for the magic + + $args->estimatedExecDuration = isset( + $_REQUEST['estimated_execution_duration']) ? $_REQUEST['estimated_execution_duration'] : null; + + $args->doAction = isset($_REQUEST['doAction']) ? $_REQUEST['doAction'] : ''; + + $key2loop = array( + 'edit_tc' => 'edit', + 'delete_tc' => 'delete', + 'do_delete' => 'doDelete', + 'create_tc' => 'create', + 'do_create' => 'doCreate' + ); + + foreach ($key2loop as $key => $action) { + if (isset($_REQUEST[$key])) { + $args->doAction = $action; + break; + } + } + + $key2loop = array( + 'move_copy_tc', + 'delete_tc_version', + 'do_move', + 'do_copy', + 'do_copy_ghost_zone', + 'do_delete_tc_version', + 'do_create_new_version', + 'do_create_new_version_from_latest' + ); + foreach ($key2loop as $key) { + $args->$key = isset($_REQUEST[$key]) ? 1 : 0; + } + + $args->do_activate_this = isset($_REQUEST['activate_this_tcversion']) ? 1 : 0; + $args->do_deactivate_this = isset($_REQUEST['deactivate_this_tcversion']) ? 1 : 0; + $args->activeAttr = 0; + if ($args->do_activate_this) { + $args->activeAttr = 1; + } + + $args->target_position = isset($_REQUEST['target_position']) ? $_REQUEST['target_position'] : 'bottom'; + + $key2loop = array( + "keyword_assignments", + "requirement_assignments" + ); + foreach ($key2loop as $key) { + $args->copy[$key] = isset($_REQUEST[$key]) ? true : false; + } + + $args->show_mode = (isset($_REQUEST['show_mode']) && + $_REQUEST['show_mode'] != '') ? $_REQUEST['show_mode'] : null; + + // Multiple Test Case Steps Feature + $args->step_number = isset($_REQUEST['step_number']) ? intval( + $_REQUEST['step_number']) : 0; + $args->step_id = isset($_REQUEST['step_id']) ? intval($_REQUEST['step_id']) : 0; + $args->step_set = isset($_REQUEST['step_set']) ? $_REQUEST['step_set'] : null; + $args->tcaseSteps = isset($_REQUEST['tcaseSteps']) ? $_REQUEST['tcaseSteps'] : null; + + // from session + $args->testproject_id = $args->tproject_id = intval( + $_SESSION['testprojectID']); + + $args->user = $_SESSION['currentUser']; + $args->user_id = intval($_SESSION['userID']); + + $args->refreshTree = isset($_SESSION['setting_refresh_tree_on_action']) ? intval( + $_SESSION['setting_refresh_tree_on_action']) : 0; + + $args->opt_requirements = null; + if (isset($_SESSION['testprojectOptions'])) { + $args->opt_requirements = $_SESSION['testprojectOptions']->requirementsEnabled; + $args->requirementsEnabled = $_SESSION['testprojectOptions']->requirementsEnabled; + } + + $args->basehref = $_SESSION['basehref']; + $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; + + // Specialized webEditorConfiguration + $action2check = array( + "editStep" => true, + "createStep" => true, + "doCreateStep" => true, + "doUpdateStep" => true, + "doInsertStep" => true, + "doCopyStep" => true, + "doUpdateStepAndInsert" => true + ); + if (isset($action2check[$args->doAction])) { + $cfgObj->webEditorCfg = getWebEditorCfg('steps_design'); + } + + $args->stay_here = isset($_REQUEST['stay_here']) ? 1 : 0; + + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + $args->tcStatusCfg['status_code'] = $dummy['cfg']; + $args->tcStatusCfg['code_label'] = $dummy['lbl']; + $args->tc_status = isset($_REQUEST['tc_status']) ? intval( + $_REQUEST['tc_status']) : $args->tcStatusCfg['status_code']['draft']; + + $dk = 'estimated_execution_duration'; + $args->$dk = trim(isset($_REQUEST[$dk]) ? $_REQUEST[$dk] : ''); + + $args->fileTitle = isset($_REQUEST['fileTitle']) ? $_REQUEST['fileTitle'] : ""; + + $args->relation_type = isset($_REQUEST['relation_type']) ? $_REQUEST['relation_type'] : null; + $args->relation_id = intval( + isset($_REQUEST['relation_id']) ? $_REQUEST['relation_id'] : 0); + + $args->relation_destination_tcase = isset( + $_REQUEST['relation_destination_tcase']) ? $_REQUEST['relation_destination_tcase'] : null; + + $args->relation_destination_tcase = str_replace(' ', '', + $args->relation_destination_tcase); + $getOpt = array( + 'tproject_id' => null, + 'output' => 'map' + ); + if (is_numeric($args->relation_destination_tcase)) { + $getOpt['tproject_id'] = $args->tproject_id; + } + $args->dummy = $tcaseMgr->getInternalID($args->relation_destination_tcase, + $getOpt); + + $args->destination_tcase_id = $args->dummy['id']; + + $args->keyword_id = isset($_GET['keyword_id']) ? intval($_GET['keyword_id']) : 0; + + $args->tckw_link_id = isset($_GET['tckw_link_id']) ? intval( + $_GET['tckw_link_id']) : 0; + + $args->tcplat_link_id = isset($_GET['tcplat_link_id']) ? intval( + $_GET['tcplat_link_id']) : 0; + + $args->tplan_id = isset($_REQUEST['tplan_id']) ? intval( + $_REQUEST['tplan_id']) : 0; + $args->platform_id = isset($_REQUEST['platform_id']) ? intval( + $_REQUEST['platform_id']) : 0; + + $cbk = 'changeExecTypeOnSteps'; + $args->applyExecTypeChangeToAllSteps = isset($_REQUEST[$cbk]); + + $k2c = array( + 'free_keywords', + 'free_platforms' + ); + foreach ($k2c as $kv) { + $args->$kv = isset($_REQUEST[$kv]) ? $_REQUEST[$kv] : null; + } + + $args->copyOnlyLatestVersion = isset($_REQUEST['copy_latest_version']) ? 1 : 0; + + $tcaseMgr->setTestProject($args->tproject_id); + + return $args; +} + +/** + * initializeOptionTransferCfg + * + * @param string $otName + * @param stdClass $argsObj + * @param testproject $tprojectMgr + * @return stdClass + */ +function initializeOptionTransferCfg($otName, &$argsObj, &$tprojectMgr) +{ + $otCfg = new stdClass(); + switch ($argsObj->doAction) { + case 'create': + case 'edit': + case 'doCreate': + $otCfg = opt_transf_empty_cfg(); + $otCfg->global_lbl = ''; + $otCfg->from->lbl = lang_get('available_kword'); + $otCfg->from->map = $tprojectMgr->get_keywords_map( + $argsObj->testproject_id); + $otCfg->to->lbl = lang_get('assigned_kword'); + break; + } + + $otCfg->js_ot_name = $otName; + return $otCfg; +} + +/** + * Create web editors + * + * When using tinymce or none as web editor, we need to set rows and cols + * to appropriate values, to avoid an ugly ui. + * null => use default values defined on editor class file + * Rows and Cols values are useless for FCKeditor + * + * @param string $basehref + * @param array $editorCfg + * @param array $editorSet + * @return stdClass + */ +function createWebEditors($basehref, $editorCfg, $editorSet = null) +{ + $specGUICfg = config_get('spec_cfg'); + $layout = $specGUICfg->steps_results_layout; + + // Rows and Cols configuration + $owe = new stdClass(); + + $cols = array( + 'steps' => array( + 'horizontal' => 38, + 'vertical' => 44 + ), + 'expected_results' => array( + 'horizontal' => 38, + 'vertical' => 44 + ) + ); + + $editorsCfg = config_get('gui')->text_editor; + $owe->cfg = [ + 'summary' => [ + 'height' => $editorsCfg['summary']['height'] + ], + 'preconditions' => [ + 'height' => $editorsCfg['preconditions']['height'] + ], + 'steps' => [ + 'rows' => null, + 'cols' => $cols['steps'][$layout] + ], + 'expected_results' => [ + 'rows' => null, + 'cols' => $cols['expected_results'][$layout] + ] + ]; + + $owe->editor = array(); + $force_create = is_null($editorSet); + foreach ($owe->cfg as $key => $value) { + if ($force_create || isset($editorSet[$key])) { + $owe->editor[$key] = web_editor($key, $basehref, $editorCfg); + } else { + unset($owe->cfg[$key]); + } + } + + return $owe; +} + +/** + * Get the configuration + * + * @return stdClass + */ +function getCfg() +{ + $cfg = new stdClass(); + $cfg->treemenu_default_testcase_order = config_get( + 'treemenu_default_testcase_order'); + $cfg->spec = config_get('spec_cfg'); + $cfg->exclude_node_types = array( + 'testplan' => 1, + 'requirement' => 1, + 'requirement_spec' => 1 + ); + $cfg->tcase_template = config_get('testcase_template'); + $cfg->webEditorCfg = getWebEditorCfg('design'); + + $cfg->editorKeys = new stdClass(); + $cfg->editorKeys->testcase = array( + 'summary' => true, + 'preconditions' => true + ); + $cfg->editorKeys->step = array( + 'steps' => true, + 'expected_results' => true + ); + + return $cfg; +} + +/** + * Get the grants + * + * @param database $dbHandler + * @return stdClass object + */ +function getGrants(&$dbHandler) +{ + $grants = new stdClass(); + $grants->requirement_mgmt = has_rights($dbHandler, "mgt_modify_req"); + return $grants; +} + +/** + * Initialize the GUI + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param stdClass $cfgObj + * @param testcase $tcaseMgr + * @param testproject $tprojMgr + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj, $cfgObj, &$tcaseMgr, &$tprojMgr) +{ + $guiObj = new stdClass(); + $guiObj->uploadOp = null; + $guiObj->tplan_id = $argsObj->tplan_id; + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->editorType = $cfgObj->webEditorCfg['type']; + $guiObj->grants = getGrants($dbHandler); + $guiObj->opt_requirements = $argsObj->opt_requirements; + $guiObj->action_on_duplicated_name = 'generate_new'; + $guiObj->show_mode = $argsObj->show_mode; + $guiObj->has_been_executed = $argsObj->has_been_executed; + $guiObj->attachments = null; + $guiObj->parent_info = null; + $guiObj->user_feedback = ''; + $guiObj->stay_here = $argsObj->stay_here; + $guiObj->steps_results_layout = $cfgObj->spec->steps_results_layout; + $guiObj->btn_reorder_testcases = lang_get( + 'btn_reorder_testcases_externalid'); + $guiObj->import_limit = TL_REPOSITORY_MAXFILESIZE; + $guiObj->msg = ''; + + $guiObj->loadOnCancelURL = $_SESSION['basehref'] . + "/lib/testcases/archiveData.php?edit=testcase&id=" . $argsObj->tcase_id . + "&show_mode={$argsObj->show_mode}"; + + $guiObj->fileUploadURL = $_SESSION['basehref'] . + $tcaseMgr->getFileUploadRelativeURL($argsObj); + + if ($argsObj->container_id > 0) { + $pnode_info = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $argsObj->container_id); + $node_descr = array_flip( + $tcaseMgr->tree_manager->get_available_node_types()); + $guiObj->parent_info['name'] = $pnode_info['name']; + $guiObj->parent_info['description'] = lang_get( + $node_descr[$pnode_info['node_type_id']]); + } + + $guiObj->direct_link = $tcaseMgr->buildDirectWebLink($_SESSION['basehref'], + $argsObj->tcase_id, $argsObj->testproject_id); + + $guiObj->domainTCStatus = $argsObj->tcStatusCfg['code_label']; + + $grant2check = array( + 'mgt_modify_tc', + 'mgt_view_req', + 'testplan_planning', + 'mgt_modify_product', + 'keyword_assignment', + 'req_tcase_link_management', + 'testproject_edit_executed_testcases', + 'testproject_delete_executed_testcases' + ); + $guiObj->grants = new stdClass(); + foreach ($grant2check as $right) { + $guiObj->$right = $guiObj->grants->$right = $argsObj->user->hasRight( + $dbHandler, $right, $argsObj->tproject_id); + } + + $guiObj->codeTrackerEnabled = $tprojMgr->isCodeTrackerEnabled( + $guiObj->tproject_id); + + return $guiObj; +} + +/** + * manage GUI rendering + * + * @param stdClass $argsObj + * @param stdClass $guiObj + * @param stdClass $opObj + * @param stdClass $templateCfg + * @param stdClass $cfgObj + * @param array $editorKeys + */ +function renderGui(&$argsObj, $guiObj, $opObj, $templateCfg, $cfgObj, + $editorKeys) +{ + $smartyObj = new TLSmarty(); + + // needed by webeditor loading logic present on inc_head.tpl + $smartyObj->assign('editorType', $guiObj->editorType); + + $renderType = 'none'; + + // + // key: operation requested (normally received from GUI on doAction) + // value: operation value to set on doAction HTML INPUT + // This is useful when you use same template (example xxEdit.tpl), + // for create and edit. + // When template is used for create -> operation: doCreate. + // When template is used for edit -> operation: doUpdate. + // + // used to set value of: $guiObj->operation + // + $actionOperation = array( + 'create' => 'doCreate', + 'doCreate' => 'doCreate', + 'edit' => 'doUpdate', + 'delete' => 'doDelete', + 'createStep' => 'doCreateStep', + 'doCreateStep' => 'doCreateStep', + 'doCopyStep' => 'doUpdateStep', + 'editStep' => 'doUpdateStep', + 'doUpdateStep' => 'doUpdateStep', + 'doInsertStep' => 'doUpdateStep', + 'doUpdateStepAndInsert' => 'doUpdateStep' + ); + + $nak = array( + 'doDelete', + 'doDeleteStep', + 'doReorderSteps', + 'doResequenceSteps', + 'setImportance', + 'setStatus', + 'setExecutionType', + 'setEstimatedExecDuration', + 'doAddRelation', + 'doDeleteRelation', + 'removeKeyword', + 'freeze', + 'unfreeze', + 'addKeyword', + 'removePlatform', + 'addPlatform' + ); + + foreach ($nak as $ak) { + $actionOperation[$ak] = ''; + } + + $key2work = 'cleanUpWebEditor'; + $cleanUpWebEditor = property_exists($opObj, $key2work) ? $opObj->$key2work : false; + + $oWebEditor = createWebEditors($argsObj->basehref, $cfgObj->webEditorCfg, + $editorKeys); + + foreach ($oWebEditor->cfg as $key => $value) { + $of = &$oWebEditor->editor[$key]; + + switch ($argsObj->doAction) { + case "edit": + case "delete": + case "editStep": + case "doDelete": + case "doCopyStep": + case "doUpdateStep": + $initWebEditorFromTemplate = false; + $of->Value = $argsObj->$key; + break; + + case "doCreate": + $initWebEditorFromTemplate = $opObj->actionOK; + $of->Value = $argsObj->$key; + break; + + case "create": + case "doCreateStep": + case "doInsertStep": + case "doUpdateStepAndInsert": + default: + $initWebEditorFromTemplate = true; + break; + } + $guiObj->operation = $actionOperation[$argsObj->doAction]; + + if ($initWebEditorFromTemplate) { + $of->Value = getItemTemplateContents('testcase_template', + $of->InstanceName, ''); + } elseif ($cleanUpWebEditor) { + $of->Value = ''; + } + $smartyObj->assign($key, $of->CreateHTML($oWebEditor->cfg[$key])); + } + + switch ($argsObj->doAction) { + case "doDelete": + $guiObj->refreshTree = $argsObj->refreshTree; + break; + } + + switch ($argsObj->doAction) { + case "edit": + case "create": + case "delete": + case "createStep": + case "editStep": + case "doCreate": + case "doDelete": + case "doCreateStep": + case "doUpdateStep": + case "doDeleteStep": + case "doReorderSteps": + case "doCopyStep": + case "doInsertStep": + case "doResequenceSteps": + case "setImportance": + case "setStatus": + case "setExecutionType": + case "setEstimatedExecDuration": + case "doAddRelation": + case "doDeleteRelation": + case "doUpdateStepAndInsert": + case "removeKeyword": + case "addKeyword": + case "freeze": + case "unfreeze": + case "removePlatform": + case "addPlatform": + $renderType = 'template'; + + // Document this !!!! + $key2loop = get_object_vars($opObj); + foreach ($key2loop as $key => $value) { + $guiObj->$key = $value; + } + $guiObj->operation = $actionOperation[$argsObj->doAction]; + + $tplDir = (! isset($opObj->template_dir) || + is_null($opObj->template_dir)) ? $templateCfg->template_dir : $opObj->template_dir; + $tpl = is_null($opObj->template) ? $templateCfg->default_template : $opObj->template; + + $pos = strpos($tpl, '.php'); + if ($pos === false) { + $tpl = $tplDir . $tpl; + } else { + $renderType = 'redirect'; + } + break; + } + + switch ($renderType) { + case 'template': + $smartyObj->assign('gui', $guiObj); + $smartyObj->display($tpl); + break; + + case 'redirect': + header("Location: {$tpl}"); + exit(); + break; + + default: + break; + } +} + +/** + * Create a new version of a test case + * + * @param TLSmarty $tplEng + * @param stdClass $argsObj + * @param stdClass $guiObj + * @param testcase $tcaseMgr + * @param int $sourceTCVID + */ +function createNewVersion(&$tplEng, &$argsObj, &$guiObj, &$tcaseMgr, + $sourceTCVID) +{ + $user_feedback = ''; + $msg = lang_get('error_tc_add'); + + $op = $tcaseMgr->create_new_version($argsObj->tcase_id, $argsObj->user_id, + $sourceTCVID); + + $candidate = $sourceTCVID; + if ($op['msg'] == "ok") { + $candidate = $op['id']; + $user_feedback = sprintf(lang_get('tc_new_version'), $op['version']); + $msg = 'ok'; + $tcCfg = config_get('testcase_cfg'); + $isOpen = ! $tcCfg->freezeTCVersionOnNewTCVersion; + $tcaseMgr->setIsOpen($argsObj->tcase_id, $sourceTCVID, $isOpen); + } + $identity = new stdClass(); + $identity->id = $argsObj->tcase_id; + $identity->tproject_id = $argsObj->tproject_id; + $identity->version_id = ! is_null($argsObj->show_mode) ? $candidate : testcase::ALL_VERSIONS; + + $guiObj->viewerArgs['action'] = "do_update"; + $guiObj->viewerArgs['refreshTree'] = DONT_REFRESH; + $guiObj->viewerArgs['msg_result'] = $msg; + $guiObj->viewerArgs['user_feedback'] = $user_feedback; + $guiObj->path_info = null; + + // used to implement go back ?? + $guiObj->loadOnCancelURL = $_SESSION['basehref'] . + '/lib/testcases/archiveData.php?edit=testcase&id=' . $argsObj->tcase_id . + "&show_mode={$argsObj->show_mode}"; + + $tcaseMgr->show($tplEng, $guiObj, $identity, $guiObj->grants, + array( + 'getAttachments' => true + )); + exit(); } diff --git a/lib/testcases/tcExecute.php b/lib/testcases/tcExecute.php index dec2ef19f7..7b28e9a896 100644 --- a/lib/testcases/tcExecute.php +++ b/lib/testcases/tcExecute.php @@ -1,158 +1,157 @@ -" . lang_get("check_test_automation_server") . ""; - -switch($args->level) -{ - case "testcase": - $xmlResponse = remote_exec_testcase($db,$args->testcase_id,$msg); - break; - case "testsuite": - case "testproject": - //@TODO schlundus, investigate this! - $tcase_parent_id = $_REQUEST[$args->level . "_id"]; - $xmlResponse = remote_exec_testcase_set($db,$tcase_parent_id,$msg); - break; - default: - echo "" . lang_get("service_not_supported") . ""; - break; -} - -if(!is_null($xmlResponse)) -{ - $xmlResponse = '' . $xmlResponse . - '
    '; - echo $xmlResponse; -} - -function remote_exec_testcase(&$db,$tcase_id,$msg) -{ - $cfield_manager = new cfield_mgr($db); - $tree_manager = new tree($db); - $xmlResponse = null; - $executionResults = array(); - - $executionResults[$tcase_id] = executeTestCase($tcase_id,$tree_manager,$cfield_manager); - $myResult = $executionResults[$tcase_id]['result']; - $myNotes = $executionResults[$tcase_id]['notes']; - $myMessage = $executionResults[$tcase_id]['message']; - - $xmlResponse = '' . lang_get('result_after_exec') . " {$myMessage}"; - - if($myResult != -1 and $myNotes != -1) - { - $xmlResponse .= "" . lang_get('tcexec_result') . "" . - "{$myResult}" . - "" . lang_get('tcexec_notes'). "" . - " {$myNotes}"; - } - else - { - $xmlResponse .= $msg['check_server_setting']; - } - - return $xmlResponse; -} - - -/* - function: - - args : - - returns: - -*/ -function remote_exec_testcase_set(&$db,$parent_id,$msg) -{ - $cfield_manager = new cfield_mgr($db); - $tree_manager = new tree($db); - $xmlResponse = null; - $executionResults = array(); - $node_type = $tree_manager->get_available_node_types(); - $subtree_list = $tree_manager->get_subtree($parent_id); - - foreach($subtree_list as $_key => $_value){ - if (is_array($_value)){ - if($_value['node_type_id'] == $node_type['testcase']) { - $executionResults[$_value['id']] = executeTestCase($_value['id'],$tree_manager,$cfield_manager); - } - else{ - //Can add some logic here. If required. - continue; - } - } - } - if($executionResults){ - foreach($executionResults as $key => $value){ - - $node_info=$tree_manager->get_node_hierarchy_info($key); - - $xmlResponse .= '' . lang_get('tcexec_results_for') . - $node_info['name'] . ""; - $serverTest = 1; - foreach($value as $_key => $_value){ - if($_value != -1){ - $xmlResponse .= "" . $_key . ":" . $_value . ""; - } - else - $serverTest = $serverTest+1; - - } - if($serverTest != 1){ - $xmlResponse .= $xmlResponse .= $msg['check_server_setting']; - } - } - } - return $xmlResponse; -} - -/** - * - * - */ -function init_args() -{ - $iParams = array("testcase_id" => array(tlInputParameter::INT_N,0), - "level" => array(tlInputParameter::STRING_N,0,50)); - $args = new stdClass(); - R_PARAMS($iParams,$args); - return $args; -} -?> \ No newline at end of file +" . + lang_get("check_test_automation_server") . ""; + +switch ($args->level) { + case "testcase": + $xmlResponse = remoteExecTestcase($db, $args->testcase_id, $msg); + break; + case "testsuite": + case "testproject": + // @TODO schlundus, investigate this! + $tcase_parent_id = $_REQUEST[$args->level . "_id"]; + $xmlResponse = remoteExecTestcaseSet($db, $tcase_parent_id, $msg); + break; + default: + echo "" . lang_get("service_not_supported") . ""; + break; +} + +if (! is_null($xmlResponse)) { + $xmlResponse = '' . $xmlResponse . + '
    '; + echo $xmlResponse; +} + +function remoteExecTestcase(&$db, $tcase_id, $msg) +{ + $cfield_manager = new cfield_mgr($db); + $tree_manager = new tree($db); + $xmlResponse = null; + $executionResults = array(); + + $executionResults[$tcase_id] = executeTestCase($tcase_id, $tree_manager, + $cfield_manager); + $myResult = $executionResults[$tcase_id]['result']; + $myNotes = $executionResults[$tcase_id]['notes']; + $myMessage = $executionResults[$tcase_id]['message']; + + $xmlResponse = '' . lang_get('result_after_exec') . + " {$myMessage}"; + + if ($myResult != - 1 && $myNotes != - 1) { + $xmlResponse .= "" . lang_get('tcexec_result') . "" . + "{$myResult}" . "" . lang_get('tcexec_notes') . + "" . " {$myNotes}"; + } else { + $xmlResponse .= $msg['check_server_setting']; + } + + return $xmlResponse; +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function remoteExecTestcaseSet(&$db, $parent_id, $msg) +{ + $cfield_manager = new cfield_mgr($db); + $tree_manager = new tree($db); + $xmlResponse = null; + $executionResults = array(); + $node_type = $tree_manager->get_available_node_types(); + $subtree_list = $tree_manager->get_subtree($parent_id); + + foreach ($subtree_list as $_key => $_value) { + if (is_array($_value) && + $_value['node_type_id'] == $node_type['testcase']) { + $executionResults[$_value['id']] = executeTestCase($_value['id'], + $tree_manager, $cfield_manager); + } + } + if ($executionResults) { + foreach ($executionResults as $key => $value) { + $node_info = $tree_manager->get_node_hierarchy_info($key); + + $xmlResponse .= '' . + lang_get('tcexec_results_for') . $node_info['name'] . + ""; + $serverTest = 1; + foreach ($value as $_key => $_value) { + if ($_value != - 1) { + $xmlResponse .= "" . $_key . ":" . $_value . + ""; + } else { + $serverTest += 1; + } + } + if ($serverTest != 1) { + $xmlResponse .= $xmlResponse .= $msg['check_server_setting']; + } + } + } + return $xmlResponse; +} + +/** + */ +function initArgs() +{ + $iParams = array( + "testcase_id" => array( + tlInputParameter::INT_N, + 0 + ), + "level" => array( + tlInputParameter::STRING_N, + 0, + 50 + ) + ); + $args = new stdClass(); + R_PARAMS($iParams, $args); + return $args; +} +?> diff --git a/lib/testcases/tcExport.php b/lib/testcases/tcExport.php index 20dbcdab0a..4f356c8425 100644 --- a/lib/testcases/tcExport.php +++ b/lib/testcases/tcExport.php @@ -1,233 +1,251 @@ -container_id; -$check_children = 0; - - - -if($args->useRecursion) { - // Exporting situations: - // All test suites in test project - // One test suite - // $dummy = array_flip($tree_mgr->get_available_node_types()); - $node_info = $tree_mgr->get_node_hierarchy_info($node_id); - $gui->export_filename = $node_info['name']; - - $gui->page_title=lang_get('title_tsuite_export'); - - $dummy = '.testsuite-deep.xml'; - if($node_id == $args->tproject_id) { - $gui->page_title = lang_get('title_tsuite_export_all'); - $dummy = '.testproject-deep.xml'; - $check_children=1; - $gui->nothing_todo_msg=lang_get('no_testsuites_to_export'); - } - $gui->export_filename .= $dummy; - -} else { - // Exporting situations: - // All test cases in test suite. - // One test case. - if($gui->oneTestCaseExport) { - $tcase_mgr = new testcase($db); - $tcinfo = $tcase_mgr->get_by_id($args->tcase_id,$args->tcversion_id,null,array('output' => 'essential')); - $tcinfo = $tcinfo[0]; - $node_id = $args->tcase_id; - $gui->export_filename = $tcinfo['name'] . '.version' . $tcinfo['version'] . '.testcase.xml'; - $gui->page_title = lang_get('title_tc_export'); - } else { - $check_children = 1; - $node_info = $tree_mgr->get_node_hierarchy_info($args->container_id); - $gui->export_filename = $node_info['name'] . '.testsuite-children-testcases.xml'; - $gui->page_title = lang_get('title_tc_export_all'); - $gui->nothing_todo_msg = lang_get('no_testcases_to_export'); - } -} -$gui->export_filename = is_null($args->export_filename) ? $gui->export_filename : $args->export_filename; - - -if( $check_children ) { - // Check if there is something to export - $children=$tree_mgr->get_children($node_id, - array("testplan" => "exclude_me", - "requirement_spec" => "exclude_me", - "requirement" => "exclude_me")); - - $gui->nothing_todo_msg=''; - if(count($children)==0) { - $gui->do_it = 0 ; - } -} -$node = $tree_mgr->get_node_hierarchy_info($node_id); - -if( $args->doExport || ( $args->doExportSkel && !$gui->oneTestCaseExport ) ) { - if( is_null($tcase_mgr) ) { - $tcase_mgr = new testcase($db); - } - $tsuite_mgr = new testsuite($db); - - $pfn = null; - switch($args->exportType) { - case 'XML': - $pfn = 'exportTestSuiteDataToXML'; - if ($gui->oneTestCaseExport) { - $pfn = 'exportTestCaseDataToXML'; - } - break; - } - - if ($pfn) { - if ($gui->oneTestCaseExport) { - $args->optExport['RELATIONS'] = true; - $args->optExport['ROOTELEM'] = "{{XMLCODE}}"; - $content = $tcase_mgr->$pfn($args->tcase_id,$args->tcversion_id,$args->tproject_id,null,$args->optExport); - } else { - - $opt = $args->optExport; - if( $args->doExportSkel ) { - $opt['skeleton'] = 1; - } - $content = TL_XMLEXPORT_HEADER; - $content .= $tsuite_mgr->$pfn($args->container_id,$args->tproject_id,$opt); - } - - downloadContentsToFile($content,$gui->export_filename); - exit(); - } -} - -$gui->object_name=$node['name']; -$gui->tproject_name=$args->tproject_name; -$gui->tproject_id=$args->tproject_id; -$gui->tcID=$args->tcase_id; -$gui->useRecursion=$args->useRecursion ? 1 : 0; -$gui->tcVersionID=$args->tcversion_id; -$gui->containerID=$args->container_id; - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/* - function: init_args - - args: - - returns: - -*/ -function init_args(&$dbHandler) { - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->doExport = isset($_REQUEST['export']) ? 1 : 0; - $args->doExportSkel = isset($_REQUEST['exportSkel']) ? 1 : 0; - - $k2l = array('useRecursion','exportReqs','exportCFields', - 'exportKeywords','exportTestCaseExternalID','exportTCSummary', - 'exportTCPreconditions','exportTCSteps', 'exportAttachments'); - - foreach ($k2l as $key) { - $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; - } - - $args->addPrefix = 0; - if($args->exportTestCaseExternalID) { - $args->addPrefix = isset($_REQUEST['addPrefix']) ? 1 : 0; - } - - $args->optExport = array('REQS' => $args->exportReqs, - 'CFIELDS' => $args->exportCFields, - 'KEYWORDS' => $args->exportKeywords, - 'EXTERNALID' => $args->exportTestCaseExternalID, - 'ADDPREFIX' => $args->addPrefix, - 'RECURSIVE' => $args->useRecursion, - 'TCSUMMARY' => $args->exportTCSummary, - 'TCPRECONDITIONS' => $args->exportTCPreconditions, - 'ATTACHMENTS' => $args->exportAttachments, - 'TCSTEPS' => $args->exportTCSteps); - - - $omgr = $args->useRecursion ? new testsuite($dbHandler) : new testcase($dbHandler); - $args->exportTypes = $omgr->get_export_file_types(); - $args->exportType = null; - if( isset($_REQUEST['exportType']) ) { - $xd = strtoupper(trim($_REQUEST['exportType'])); - $args->exportType = isset($args->exportTypes[$xd]) ? $args->exportTypes[$xd] : null; - } - - $args->export_filename=isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : null; - - $args->tcase_id = isset($_REQUEST['testcase_id']) ? intval($_REQUEST['testcase_id']) : 0; - $args->tcversion_id = isset($_REQUEST['tcversion_id']) ? intval($_REQUEST['tcversion_id']) : 0; - $args->container_id = isset($_REQUEST['containerID']) ? intval($_REQUEST['containerID']) : 0; - - // To be replaced with $_REQUEST value - $args->tproject_id = intval(isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : 0); - if($args->tproject_id > 0) { - $dummy = $omgr->tree_manager->get_node_hierarchy_info($args->tproject_id); - if(!is_null($dummy)) { - $args->tproject_name = $dummy['name']; - } else { - throw new Exception("BAD Test Project ID={$args->tproject_id}", 1); - } - } else { - throw new Exception("Test Project ID=0", 1); - } - - $args->goback_url=isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; - - unset($omgr); - return $args; -} - - -/** - * - */ -function initializeGui($argsObj) { - $guiObj = new stdClass(); - $guiObj->do_it = 1; - $guiObj->nothing_todo_msg = ''; - $guiObj->export_filename = ''; - $guiObj->page_title = ''; - $guiObj->object_name = ''; - $guiObj->exportTypes = $argsObj->exportTypes; - $guiObj->tproject_id = $argsObj->tproject_id; - - - $guiObj->goback_url = !is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; - $guiObj->oneTestCaseExport = ($argsObj->tcase_id && $argsObj->tcversion_id); - - $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . - "lib/testcases/archiveData.php?"; - if($argsObj->useRecursion || !$guiObj->oneTestCaseExport) { - $guiObj->cancelActionJS .= 'edit=testsuite&id=' . - intval($argsObj->container_id) . "'"; - } else { - $guiObj->cancelActionJS .= 'edit=testcase&id=' . - intval($argsObj->tcase_id) . "'"; - } - return $guiObj; +container_id; +$check_children = 0; + +if ($args->useRecursion) { + // Exporting situations: + // All test suites in test project + // One test suite + // $dummy = array_flip($tree_mgr->get_available_node_types()); + $node_info = $tree_mgr->get_node_hierarchy_info($node_id); + $gui->export_filename = $node_info['name']; + + $gui->page_title = lang_get('title_tsuite_export'); + + $dummy = '.testsuite-deep.xml'; + if ($node_id == $args->tproject_id) { + $gui->page_title = lang_get('title_tsuite_export_all'); + $dummy = '.testproject-deep.xml'; + $check_children = 1; + $gui->nothing_todo_msg = lang_get('no_testsuites_to_export'); + } + $gui->export_filename .= $dummy; +} else { + // Exporting situations: + // All test cases in test suite. + // One test case. + if ($gui->oneTestCaseExport) { + $tcaseMgr = new testcase($db); + $tcinfo = $tcaseMgr->get_by_id($args->tcase_id, $args->tcversion_id, + null, array( + 'output' => 'essential' + )); + $tcinfo = $tcinfo[0]; + $node_id = $args->tcase_id; + $gui->export_filename = $tcinfo['name'] . '.version' . $tcinfo['version'] . + '.testcase.xml'; + $gui->page_title = lang_get('title_tc_export'); + } else { + $check_children = 1; + $node_info = $tree_mgr->get_node_hierarchy_info($args->container_id); + $gui->export_filename = $node_info['name'] . + '.testsuite-children-testcases.xml'; + $gui->page_title = lang_get('title_tc_export_all'); + $gui->nothing_todo_msg = lang_get('no_testcases_to_export'); + } +} +$gui->export_filename = is_null($args->export_filename) ? $gui->export_filename : $args->export_filename; + +if ($check_children) { + // Check if there is something to export + $children = $tree_mgr->get_children($node_id, + array( + "testplan" => "exclude_me", + "requirement_spec" => "exclude_me", + "requirement" => "exclude_me" + )); + + $gui->nothing_todo_msg = ''; + if (count($children) == 0) { + $gui->do_it = 0; + } +} +$node = $tree_mgr->get_node_hierarchy_info($node_id); + +if ($args->doExport || ($args->doExportSkel && ! $gui->oneTestCaseExport)) { + if (is_null($tcaseMgr)) { + $tcaseMgr = new testcase($db); + } + $tsuite_mgr = new testsuite($db); + + $pfn = null; + switch ($args->exportType) { + case 'XML': + $pfn = 'exportTestSuiteDataToXML'; + if ($gui->oneTestCaseExport) { + $pfn = 'exportTestCaseDataToXML'; + } + break; + } + + if ($pfn) { + if ($gui->oneTestCaseExport) { + $args->optExport['RELATIONS'] = true; + $args->optExport['ROOTELEM'] = "{{XMLCODE}}"; + $content = $tcaseMgr->$pfn($args->tcase_id, $args->tcversion_id, + $args->tproject_id, null, $args->optExport); + } else { + + $opt = $args->optExport; + if ($args->doExportSkel) { + $opt['skeleton'] = 1; + } + $content = TL_XMLEXPORT_HEADER; + $content .= $tsuite_mgr->$pfn($args->container_id, + $args->tproject_id, $opt); + } + + downloadContentsToFile($content, $gui->export_filename); + exit(); + } +} + +$gui->object_name = $node['name']; +$gui->tproject_name = $args->tproject_name; +$gui->tproject_id = $args->tproject_id; +$gui->tcID = $args->tcase_id; +$gui->useRecursion = $args->useRecursion ? 1 : 0; +$gui->tcVersionID = $args->tcversion_id; +$gui->containerID = $args->container_id; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: init_args + * + * args: + * + * returns: + * + */ +function initArgs(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->doExport = isset($_REQUEST['export']) ? 1 : 0; + $args->doExportSkel = isset($_REQUEST['exportSkel']) ? 1 : 0; + + $k2l = array( + 'useRecursion', + 'exportReqs', + 'exportCFields', + 'exportKeywords', + 'exportTestCaseExternalID', + 'exportTCSummary', + 'exportTCPreconditions', + 'exportTCSteps', + 'exportAttachments' + ); + + foreach ($k2l as $key) { + $args->$key = isset($_REQUEST[$key]) ? intval($_REQUEST[$key]) : 0; + } + + $args->addPrefix = 0; + if ($args->exportTestCaseExternalID) { + $args->addPrefix = isset($_REQUEST['addPrefix']) ? 1 : 0; + } + + $args->optExport = array( + 'REQS' => $args->exportReqs, + 'CFIELDS' => $args->exportCFields, + 'KEYWORDS' => $args->exportKeywords, + 'EXTERNALID' => $args->exportTestCaseExternalID, + 'ADDPREFIX' => $args->addPrefix, + 'RECURSIVE' => $args->useRecursion, + 'TCSUMMARY' => $args->exportTCSummary, + 'TCPRECONDITIONS' => $args->exportTCPreconditions, + 'ATTACHMENTS' => $args->exportAttachments, + 'TCSTEPS' => $args->exportTCSteps + ); + + $omgr = $args->useRecursion ? new testsuite($dbHandler) : new testcase( + $dbHandler); + $args->exportTypes = $omgr->get_export_file_types(); + $args->exportType = null; + if (isset($_REQUEST['exportType'])) { + $xd = strtoupper(trim($_REQUEST['exportType'])); + $args->exportType = isset($args->exportTypes[$xd]) ? $args->exportTypes[$xd] : null; + } + + $args->export_filename = isset($_REQUEST['export_filename']) ? $_REQUEST['export_filename'] : null; + + $args->tcase_id = isset($_REQUEST['testcase_id']) ? intval( + $_REQUEST['testcase_id']) : 0; + $args->tcversion_id = isset($_REQUEST['tcversion_id']) ? intval( + $_REQUEST['tcversion_id']) : 0; + $args->container_id = isset($_REQUEST['containerID']) ? intval( + $_REQUEST['containerID']) : 0; + + // To be replaced with $_REQUEST value + $args->tproject_id = intval( + isset($_REQUEST['tproject_id']) ? $_REQUEST['tproject_id'] : 0); + if ($args->tproject_id > 0) { + $dummy = $omgr->tree_manager->get_node_hierarchy_info( + $args->tproject_id); + if (! is_null($dummy)) { + $args->tproject_name = $dummy['name']; + } else { + throw new Exception("BAD Test Project ID={$args->tproject_id}", 1); + } + } else { + throw new Exception("Test Project ID=0", 1); + } + + $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; + + unset($omgr); + return $args; +} + +/** + */ +function initializeGui($argsObj) +{ + $guiObj = new stdClass(); + $guiObj->do_it = 1; + $guiObj->nothing_todo_msg = ''; + $guiObj->export_filename = ''; + $guiObj->page_title = ''; + $guiObj->object_name = ''; + $guiObj->exportTypes = $argsObj->exportTypes; + $guiObj->tproject_id = $argsObj->tproject_id; + + $guiObj->goback_url = ! is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; + $guiObj->oneTestCaseExport = ($argsObj->tcase_id && $argsObj->tcversion_id); + + $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . + "lib/testcases/archiveData.php?"; + if ($argsObj->useRecursion || ! $guiObj->oneTestCaseExport) { + $guiObj->cancelActionJS .= 'edit=testsuite&id=' . + intval($argsObj->container_id) . "'"; + } else { + $guiObj->cancelActionJS .= 'edit=testcase&id=' . + intval($argsObj->tcase_id) . "'"; + } + return $guiObj; } diff --git a/lib/testcases/tcImport.php b/lib/testcases/tcImport.php index a4ef3b311b..4ed9cf45ed 100644 --- a/lib/testcases/tcImport.php +++ b/lib/testcases/tcImport.php @@ -1,1249 +1,1410 @@ -do_upload) { - - // check the uploaded file - $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; - - tLog('Uploaded file: '.$source); - $doIt = false; - $gui->file_check = null; - if (($source != 'none') && ($source != '')) { - // ATTENTION: - // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using - // Firefox and Chrome. - if( !($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes) ) { - $gui->file_check['status_ok'] = 0; - $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'),$_FILES['uploadedFile']['size'],$gui->importLimitBytes); - } - } - - if($doIt) { - $gui->file_check['status_ok'] = 1; - if (move_uploaded_file($source, $gui->dest)) { - tLog('Renamed uploaded file: ' . $source); - switch($args->importType) { - case 'XML': - $pcheck_fn = "check_xml_tc_tsuite"; - $pimport_fn = "importTestCaseDataFromXML"; - break; - } - - if(!is_null($pcheck_fn)) { - $gui->file_check = $pcheck_fn($gui->dest,$args->useRecursion); - } - } - - if($gui->file_check['status_ok'] && $pimport_fn) { - tLog('Check is Ok.'); - $opt = array(); - $opt['useRecursion'] = $args->useRecursion; - $opt['importIntoProject'] = $args->bIntoProject; - $opt['duplicateLogic'] = array('hitCriteria' => $args->hit_criteria, - 'actionOnHit' => $args->action_on_duplicated_name); - $gui->resultMap = $pimport_fn($db,$gui->dest, - intval($args->container_id),intval($args->tproject_id),intval($args->userID), - $opt); - } - } else if(is_null($gui->file_check)) { - - tLog('Missing upload file','WARNING'); - $gui->file_check = array('status_ok' => 0, 'msg' => lang_get('please_choose_file_to_import')); - $args->importType = null; - } +do_upload) { + + // check the uploaded file + $source = isset($_FILES['uploadedFile']['tmp_name']) ? $_FILES['uploadedFile']['tmp_name'] : null; + + tLog('Uploaded file: ' . $source); + $doIt = false; + $gui->file_check = null; + // ATTENTION: + // MAX_FILE_SIZE hidden input is defined on form, but anyway we do not get error at least using + // Firefox and Chrome. + if (($source != 'none') && ($source != '') && + ! ($doIt = $_FILES['uploadedFile']['size'] <= $gui->importLimitBytes)) { + $gui->file_check['status_ok'] = 0; + $gui->file_check['msg'] = sprintf(lang_get('file_size_exceeded'), + $_FILES['uploadedFile']['size'], $gui->importLimitBytes); + } + + if ($doIt) { + $gui->file_check['status_ok'] = 1; + if (move_uploaded_file($source, $gui->dest)) { + tLog('Renamed uploaded file: ' . $source); + switch ($args->importType) { + case 'XML': + $pcheck_fn = "check_xml_tc_tsuite"; + $pimport_fn = "importTestCaseDataFromXML"; + break; + } + + if (! is_null($pcheck_fn)) { + $gui->file_check = $pcheck_fn($gui->dest, $args->useRecursion); + } + } + + if ($gui->file_check['status_ok'] && $pimport_fn) { + tLog('Check is Ok.'); + $opt = array(); + $opt['useRecursion'] = $args->useRecursion; + $opt['importIntoProject'] = $args->bIntoProject; + $opt['duplicateLogic'] = array( + 'hitCriteria' => $args->hit_criteria, + 'actionOnHit' => $args->action_on_duplicated_name + ); + $gui->resultMap = $pimport_fn($db, $gui->dest, + intval($args->container_id), intval($args->tproject_id), + intval($args->userID), $opt); + } + } elseif (is_null($gui->file_check)) { + + tLog('Missing upload file', 'WARNING'); + $gui->file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('please_choose_file_to_import') + ); + $args->importType = null; + } +} + +if ($args->useRecursion) { + $obj_mgr = new testsuite($db); + $gui->actionOptions = array( + 'update_last_version' => lang_get('update_last_testcase_version'), + 'generate_new' => lang_get('generate_new_testcase'), + 'create_new_version' => lang_get('create_new_testcase_version') + ); + + $gui->hitOptions = array( + 'name' => lang_get('same_name'), + 'internalID' => lang_get('same_internalID'), + 'externalID' => lang_get('same_externalID') + ); +} else { + $obj_mgr = new testcase($db); + $obj_mgr->setTestProject($args->tproject_id); +} + +$gui->actionOptions = array( + 'skip' => lang_get('skip_testcase_import'), + 'update_last_version' => lang_get('update_last_testcase_version'), + 'generate_new' => lang_get('generate_new_testcase'), + 'create_new_version' => lang_get('create_new_testcase_version') +); + +$gui->hitOptions = array( + 'name' => lang_get('same_name'), + 'internalID' => lang_get('same_internalID'), + 'externalID' => lang_get('same_externalID') +); + +$gui->testprojectName = $_SESSION['testprojectName']; +$gui->importTypes = $obj_mgr->get_import_file_types(); +$gui->action_on_duplicated_name = $args->action_on_duplicated_name; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/* + * function: importTestCaseDataFromXML + * args : + * returns: + */ +function importTestCaseDataFromXML(&$db, $fileName, $parentID, $tproject_id, + $userID, $options = null) +{ + tLog('importTestCaseDataFromXML called for file: ' . $fileName); + $resultMap = null; + $my = array(); + $my['options'] = array( + 'useRecursion' => false, + 'importIntoProject' => 0, + 'duplicateLogic' => array( + 'hitCriteria' => 'name', + 'actionOnHit' => null + ) + ); + $my['options'] = array_merge($my['options'], (array) $options); + foreach ($my['options'] as $varname => $value) { + $$varname = $value; + } + + if (file_exists($fileName)) { + $xml = @simplexml_load_file_wrapper($fileName); + if ($xml) { + $xmlKeywords = $xml->xpath('//keywords'); + $kwMap = null; + if ($xmlKeywords) { + $tproject = new testproject($db); + $loop2do = count($xmlKeywords); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $tproject->importKeywordsFromSimpleXML($tproject_id, + $xmlKeywords[$idx]); + } + $kwMap = $tproject->get_keywords_map($tproject_id); + // change keywords to lowercase to be case insensitive + $kwMap = is_null($kwMap) ? null : array_change_key_case( + array_flip($kwMap), CASE_LOWER); + } + + if (! $useRecursion && ($xml->getName() == 'testcases')) { + $resultMap = importTestCasesFromSimpleXML($db, $xml, $parentID, + $tproject_id, $userID, $kwMap, $duplicateLogic); + } + + if ($useRecursion && ($xml->getName() == 'testsuite')) { + $resultMap = importTestSuitesFromSimpleXML($db, $xml, + intval($parentID), intval($tproject_id), $userID, $kwMap, + $duplicateLogic, $importIntoProject); + } + } + } + return $resultMap; +} + +/* + * function: saveImportedTCData + * args : + * returns: + */ +function saveImportedTCData(&$db, $tcData, $tproject_id, $container_id, $userID, + $kwMap, + $duplicatedLogic = array( + 'hitCriteria' => 'name', + 'actionOnHit' => null + )) +{ + static $messages; + static $fieldSizeCfg; + static $feedbackMsg; + static $tcaseMgr; + static $tproject_mgr; + static $req_mgr; + static $safeSizeCfg; + static $linkedCustomFields; + static $tprojectHas; + static $reqSpecSet; + static $getVersionOpt; + static $userObj; + static $tcasePrefix; + static $glueChar; + static $userRights; + + $ret = null; + + if (! $tcData) { + return; + } + + $hasCFieldsInfo = false; + $hasRequirements = false; + $hasAttachments = false; + + if (is_null($messages)) { + $feedbackMsg = array(); + $messages = array(); + $fieldSizeCfg = config_get('field_size'); + + $tcaseMgr = new testcase($db); + $tcaseMgr->setTestProject($tproject_id); + + $tproject_mgr = new testproject($db); + $req_mgr = new requirement_mgr($db); + $userObj = new tlUser($userID); + $userObj->readFromDB($db, tlUser::TLOBJ_O_SEARCH_BY_ID); + + $userRights['can_edit_executed'] = $userObj->hasRight($db, + 'testproject_edit_executed_testcases', $tproject_id); + $userRights['can_link_to_req'] = $userObj->hasRight($db, + 'req_tcase_link_management', $tproject_id); + $userRights['can_assign_keywords'] = $userObj->hasRight($db, + 'keyword_assignment', $tproject_id); + + $k2l = array( + 'already_exists_updated', + 'original_name', + 'testcase_name_too_long', + 'already_exists_not_updated', + 'already_exists_skipped', + 'start_warning', + 'end_warning', + 'testlink_warning', + 'hit_with_same_external_ID', + 'keywords_assignment_skipped_during_import', + 'req_assignment_skipped_during_import' + ); + + foreach ($k2l as $k) { + $messages[$k] = lang_get($k); + } + + $messages['start_feedback'] = $messages['start_warning'] . "\n" . + $messages['testlink_warning'] . "\n"; + $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); + $messages['reqspec_warning'] = lang_get( + 'no_reqspec_defined_can_not_import'); + + $feedbackMsg['cfield'] = lang_get( + 'cf_value_not_imported_missing_cf_on_testproject'); + $feedbackMsg['tcase'] = lang_get('testcase'); + $feedbackMsg['req'] = lang_get('req_not_in_req_spec_on_tcimport'); + $feedbackMsg['req_spec'] = lang_get('req_spec_ko_on_tcimport'); + $feedbackMsg['reqNotInDB'] = lang_get('req_not_in_DB_on_tcimport'); + $feedbackMsg['attachment'] = lang_get( + 'attachment_skipped_during_import'); + + // because name can be changed automatically during item creation + // to avoid name conflict adding a suffix automatically generated, + // is better to use a max size < max allowed size + $safeSizeCfg = new stdClass(); + $safeSizeCfg->testcase_name = ($fieldSizeCfg->testcase_name) * 0.8; + + // Get CF with scope design time and allowed for test cases linked to this test project + $linkedCustomFields = $tcaseMgr->cfield_mgr->get_linked_cfields_at_design( + $tproject_id, 1, null, 'testcase', null, 'name'); + $tprojectHas['customFields'] = ! is_null($linkedCustomFields); + + $reqSpecSet = getReqSpecSet($db, $tproject_id); + + $tprojectHas['reqSpec'] = (! is_null($reqSpecSet) && + count($reqSpecSet) > 0); + + $getVersionOpt = array( + 'output' => 'minimun' + ); + $tcasePrefix = $tproject_mgr->getTestCasePrefix($tproject_id); + $glueChar = config_get('testcase_cfg')->glue_character; + } + + $resultMap = array(); + $tc_qty = count($tcData); + $userIDCache = array(); + + for ($idx = 0; $idx < $tc_qty; $idx ++) { + $tc = $tcData[$idx]; + $name = $tc['name']; + + $summary = $tc['summary']; + $steps = $tc['steps']; + $internalid = $tc['internalid']; + $externalid = $tc['externalid']; + + $doCreate = true; + if ($duplicatedLogic['actionOnHit'] == 'update_last_version' || + $duplicatedLogic['actionOnHit'] == 'skip') { + + $updOpt['blockIfExecuted'] = ! $userRights['can_edit_executed']; + + switch ($duplicatedLogic['hitCriteria']) { + case 'name': + $dupInfo = $tcaseMgr->getDuplicatesByName($name, + $container_id); + break; + + case 'internalID': + $dummy = $tcaseMgr->tree_manager->get_node_hierarchy_info( + $internalid, $container_id); + if (! is_null($dummy)) { + $dupInfo = null; + $dupInfo[$internalid] = $dummy; + } + break; + + case 'externalID': + $dupInfo = $tcaseMgr->get_by_external($externalid, + $container_id); + break; + } + } + + // Check for skip, to avoid useless processing + if ($duplicatedLogic['actionOnHit'] == 'skip' && ! empty($dupInfo)) { + $resultMap[] = array( + $name, + $messages['already_exists_skipped'] + ); + continue; + } + + // I've changed value to use when order has not been provided + // from testcase:DEFAULT_ORDER to a counter, because with original solution + // an issue arise with 'save execution and go next' + // if use has not provided order I think is OK TestLink make any choice. + $node_order = isset($tc['node_order']) ? intval($tc['node_order']) : ($idx + + 1); + $internalid = $tc['internalid']; + + $preconditions = $tc['preconditions']; + + $exec_type = isset($tc['execution_type']) ? $tc['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; + $importance = isset($tc['importance']) ? $tc['importance'] : MEDIUM; + + $attr = null; + if (isset($tc['estimated_exec_duration']) && + ! is_null($tc['estimated_exec_duration'])) { + $attr['estimatedExecDuration'] = trim( + $tc['estimated_exec_duration']); + $attr['estimatedExecDuration'] = $attr['estimatedExecDuration'] == '' ? null : floatval( + $attr['estimatedExecDuration']); + } + + if (isset($tc['is_open'])) { + $attr['is_open'] = trim($tc['is_open']); + } + + if (isset($tc['active'])) { + $attr['active'] = trim($tc['active']); + } + + if (isset($tc['status'])) { + $attr['status'] = trim($tc['status']); + } + + $externalid = $tc['externalid']; + if (intval($externalid) <= 0) { + $externalid = null; + } + + $personID = $userID; + if (! is_null($tc['author_login'])) { + if (isset($userIDCache[$tc['author_login']])) { + $personID = $userIDCache[$tc['author_login']]; + } else { + $userObj->login = $tc['author_login']; + if ($userObj->readFromDB($db, tlUser::USER_O_SEARCH_BYLOGIN) == + tl::OK) { + $personID = $userObj->dbID; + } + + // I will put always a valid userID on this cache, + // this way if author_login does not exit, and is used multiple times + // i will do check for existence JUST ONCE. + $userIDCache[$tc['author_login']] = $personID; + } + } + + $name_len = tlStringLen($name); + if ($name_len > $fieldSizeCfg->testcase_name) { + // Will put original name inside summary + $xx = $messages['start_feedback']; + $xx .= sprintf($messages['testcase_name_too_long'], $name_len, + $fieldSizeCfg->testcase_name) . "\n"; + $xx .= $messages['original_name'] . "\n" . $name . "\n" . + $messages['end_warning'] . "\n"; + $tcCfg = getWebEditorCfg('design'); + $tcType = $tcCfg['type']; + if ($tcType == 'none') { + $summary = $xx . $summary; + } else { + $summary = nl2br($xx) . $summary; + } + $name = tlSubStr($name, 0, $safeSizeCfg->testcase_name); + } + + $kwIDs = null; + if (isset($tc['keywords']) && $tc['keywords']) { + if (! $userRights['can_assign_keywords']) { + $resultMap[] = array( + $name, + $messages['keywords_assignment_skipped_during_import'] + ); + } else { + $kwIDs = implode(",", buildKeywordList($kwMap, $tc['keywords'])); + } + } + + // More logic regarding Action on Duplicate + if ($duplicatedLogic['actionOnHit'] == 'update_last_version' && + ! is_null($dupInfo)) { + + $tcase_qty = count($dupInfo); + + switch ($tcase_qty) { + case 1: + $doCreate = false; + $tcase_id = key($dupInfo); + $last_version = $tcaseMgr->getLastVersionInfo($tcase_id, + $getVersionOpt); + $tcversion_id = $last_version['id']; + $ret = $tcaseMgr->update($tcase_id, $tcversion_id, $name, + $summary, $preconditions, $steps, $personID, $kwIDs, + $node_order, $exec_type, $importance, $attr, $updOpt); + + $ret['id'] = $tcase_id; + $ret['tcversion_id'] = $tcversion_id; + if ($ret['status_ok']) { + $resultMap[] = array( + $name, + $messages['already_exists_updated'] + ); + } else { + if ($ret['reason'] == '') { + $resultMap[] = array( + $name, + sprintf($messages['already_exists_not_updated'], + $tcasePrefix . $glueChar . $externalid, + $tcasePrefix . $glueChar . + $ret['hit_on']['tc_external_id']) + ); + } else { + $resultMap[] = array( + $name, + $ret['msg'] + ); + } + } + break; + + case 0: + $doCreate = true; + break; + + default: + $doCreate = false; + break; + } + } + + if ($doCreate) { + // Want to block creation of with existent EXTERNAL ID, if containers ARE DIFFERENT. + $item_id = intval( + $tcaseMgr->getInternalID($externalid, + array( + 'tproject_id' => $tproject_id + ))); + + if ($item_id > 0) { + // who is his parent ? + $owner = $tcaseMgr->getTestSuite($item_id); + if ($owner != $container_id) { + // Get full path of existent Test Cases + $stain = $tcaseMgr->tree_manager->get_path($item_id, null, + 'name'); + $n = count($stain); + $stain[$n - 1] = $tcasePrefix . + config_get('testcase_cfg')->glue_character . $externalid . + ':' . $stain[$n - 1]; + $stain = implode('/', $stain); + + $resultMap[] = array( + $name, + $messages['hit_with_same_external_ID'] . $stain + ); + $doCreate = false; + } + } + } + + if ($doCreate) { + $createOptions = array( + 'check_duplicate_name' => testcase::CHECK_DUPLICATE_NAME, + 'action_on_duplicate_name' => $duplicatedLogic['actionOnHit'], + 'external_id' => $externalid, + 'importLogic' => $duplicatedLogic + ); + + if (! is_null($attr)) { + $createOptions += $attr; + } + + if ($ret = $tcaseMgr->create($container_id, $name, $summary, + $preconditions, $steps, $personID, $kwIDs, $node_order, + testcase::AUTOMATIC_ID, $exec_type, $importance, $createOptions)) { + $resultMap[] = array( + $name, + $ret['msg'] + ); + } + } + + // Custom Fields Management + // Check if CF with this name and that can be used on Test Cases + // is defined in current Test Project. + // If Check fails => give message to user. + // Else Import CF data + $hasCFieldsInfo = (isset($tc['customfields']) && + ! is_null($tc['customfields'])); + + if ($hasCFieldsInfo && ! is_null($ret)) { + if ($tprojectHas['customFields']) { + $msg = processCustomFields($tcaseMgr, $name, + $ret['tcversion_id'], $tc['customfields'], + $linkedCustomFields, $feedbackMsg); + if (! is_null($msg)) { + $resultMap = array_merge($resultMap, $msg); + } + } else { + // Can not import Custom Fields Values, give feedback + $msg[] = array( + $name, + $messages['cf_warning'] + ); + $resultMap = array_merge($resultMap, $msg); + } + } + + $hasRequirements = (isset($tc['requirements']) && + ! is_null($tc['requirements'])); + + if ($hasRequirements) { + if ($tprojectHas['reqSpec']) { + + if (! $userRights['can_link_to_req']) { + $msg[] = array( + $name, + $messages['req_assignment_skipped_during_import'] + ); + } else { + $msg = processRequirements($db, $req_mgr, $name, $ret, + $tc['requirements'], $reqSpecSet, $feedbackMsg, $userID); + } + + if (! is_null($msg)) { + $resultMap = array_merge($resultMap, $msg); + } + } else { + $msg[] = array( + $name, + $messages['reqspec_warning'] + ); + $resultMap = array_merge($resultMap, $msg); + } + } + + $hasAttachments = (isset($tc['attachments']) && + ! is_null($tc['attachments'])); + if ($hasAttachments) { + $fk_id = $doCreate ? $ret['tcversion_id'] : $internalid; + + if ($internalid == "" && $item_id > 0) { + $internalid = $item_id; + } + + $msg = processAttachments($db, true, $name, $internalid, $fk_id, + $tc['attachments'], $feedbackMsg); + if (! is_null($msg)) { + $resultMap = array_merge($resultMap, $msg); + } + } + } + + return $resultMap; +} + +/* + * function: buildKeywordList + * + * Build the list of DB ID of the keywords used in the XML. + * + * args : + * returns: + */ +function buildKeywordList($kwMap, $keywords) +{ + $items = array(); + $loop2do = count($keywords); + for ($jdx = 0; $jdx < $loop2do; $jdx ++) { + // change Map keys (keyword) to lowercase to be case insensitive + $items[] = $kwMap[strtolower(trim($keywords[$jdx]['name']))]; + } + return $items; +} + +/* + * function: Check if at least the file starts seems OK + */ +function checkXMLTCTsuite($fileName, $recursiveMode) +{ + $xml = @simplexml_load_file_wrapper($fileName); + $file_check = array( + 'status_ok' => 0, + 'msg' => 'xml_load_ko' + ); + if ($xml) { + $file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + $elementName = $xml->getName(); + if ($recursiveMode) { + if ($elementName != 'testsuite') { + $file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('wrong_xml_tsuite_file') + ); + } + } else { + if ($elementName != 'testcases' && $elementName != 'testcase') { + $file_check = array( + 'status_ok' => 0, + 'msg' => lang_get('wrong_xml_tcase_file') + ); + } + } + } + return $file_check; +} + +/* + * contribution by mirosvad - + * Convert new line characters from XLS to HTML + */ +function nl2p($str) +{ + return str_replace('

    ', '', + '

    ' . preg_replace('#\n|\r#', '

    $0

    ', $str) . '

    '); // MS +} + +/* + * function: + * + * args : + * + * returns: + * + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $key = 'action_on_duplicated_name'; + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'generate_new'; + + $key = 'hit_criteria'; + $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'name'; + + $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; + $args->useRecursion = isset($_REQUEST['useRecursion']) ? $_REQUEST['useRecursion'] : 0; + $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; + $args->container_id = isset($_REQUEST['containerID']) ? intval( + $_REQUEST['containerID']) : 0; + $args->bIntoProject = isset($_REQUEST['bIntoProject']) ? intval( + $_REQUEST['bIntoProject']) : 0; + + $args->containerType = isset($_REQUEST['containerType']) ? intval( + $_REQUEST['containerType']) : 0; + $args->do_upload = isset($_REQUEST['UploadFile']) ? 1 : 0; + + $args->userID = $_SESSION['userID']; + $args->tproject_id = $_SESSION['testprojectID']; + + return $args; +} + +/** + * processCustomFields + * + * Analise custom field info related to test case being imported. + * If everything OK, assign to test case. + * Else return an array of messages. + * + * + * @internal revisions + * 20100905 - franciscom - BUGID 3431 - Custom Field values at Test Case VERSION Level + */ +function processCustomFields(&$tcaseMgr, $tcaseName, $tcversionId, $cfValues, + $cfDefinition, $messages) +{ + static $missingCfMsg; + $cf2insert = null; + $resultMsg = null; + + foreach ($cfValues as $value) { + if (isset($cfDefinition[$value['name']])) { + $cf2insert[$cfDefinition[$value['name']]['id']] = array( + 'type_id' => $cfDefinition[$value['name']]['type'], + 'cf_value' => $value['value'] + ); + } else { + if (! isset($missingCfMsg[$value['name']])) { + $missingCfMsg[$value['name']] = sprintf($messages['cfield'], + $value['name'], $messages['tcase']); + } + $resultMsg[] = array( + $tcaseName, + $missingCfMsg[$value['name']] + ); + } + } + + $tcaseMgr->cfield_mgr->design_values_to_db($cf2insert, $tcversionId, null, + 'simple'); + return $resultMsg; +} + +/** + * processRequirements + * + * Analise requirements info related to test case being imported. + * If everything OK, assign to test case. + * Else return an array of messages. + */ +function processRequirements(&$dbHandler, &$reqMgr, $tcaseName, $tcIDCard, + $tcReq, $reqSpecSet, $messages, $userID) +{ + static $missingReqMsg; + static $missingReqInDBMsg; + static $cachedReqSpec; + + $resultMsg = null; + $tables = tlObjectWithDB::getDBTables(array( + 'requirements' + )); + + // Since 1.9.18, links are between req version e test case version + // We will work on latest test case version and lates req version + $tcaseId = $tcIDCard['id']; + + foreach ($tcReq as $value) { + $cachedReqSpec = array(); + $doit = false; + + // Look for req doc id we get from file, inside Req Spec Set + // we got from DB + if ($doit = isset($reqSpecSet[$value['doc_id']]) && + ! (isset($cachedReqSpec[$value['req_spec_title']]))) { + // $cachedReqSpec + // key: Requirement Specification Title get from file + // value: map with follogin keys + // id => requirement specification id from DB + // req => map with key: requirement document id + $cachedReqSpec[$value['req_spec_title']]['id'] = $reqSpecSet[$value['doc_id']]['id']; + + $cachedReqSpec[$value['req_spec_title']]['req'] = null; + } + + if ($doit) { + $useit = false; + $req_spec_id = $cachedReqSpec[$value['req_spec_title']]['id']; + + // Check if requirement with desired document id exists + // on requirement specification on DB. + // If not => create message for user feedback. + if (! ($useit = isset( + $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']]))) { + + $sql = " SELECT REQ.id from {$tables['requirements']} REQ " . + " WHERE REQ.req_doc_id='{$dbHandler->prepare_string($value['doc_id'])}' " . + " AND REQ.srs_id={$req_spec_id} "; + + $rsx = $dbHandler->get_recordset($sql); + if ($useit = (! empty($rsx) ? true : false)) { + $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']] = $rsx[0]['id']; + } + } + + if ($useit) { + $reqMgr->assignToTCaseUsingLatestVersions( + $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']], + $tcaseId, $userID); + } else { + if (! isset($missingReqMsg[$value['doc_id']])) { + $missingReqMsg[$value['doc_id']] = sprintf($messages['req'], + $value['doc_id'], $value['req_spec_title']); + } + $resultMsg[] = array( + $tcaseName, + $missingReqMsg[$value['doc_id']] + ); + } + } else { + // We didnt find Req Doc ID in Req Spec Set got from DB + if (! isset($missingReqInDBMsg[$value['doc_id']])) { + $missingReqInDBMsg[$value['doc_id']] = sprintf( + $messages['reqNotInDB'], $value['doc_id'], ''); + } + $resultMsg[] = array( + $tcaseName, + $missingReqInDBMsg[$value['doc_id']] + ); + } + } // foreach + + return $resultMsg; +} + +/** + * processAttachments + * + * Analyze attachments info related to testcase or testsuite to define if the the attachment has to be saved. + * If attachment format is OK and attachment is not already in database for the target, save the attachment. + * Else return an array of messages. + */ +function processAttachments(&$dbHandler, $isTestCase, $tcaseName, $xmlInternalID, + $fk_Id, $tcAtt, $messages) +{ + static $duplicateAttachment; + $resultMsg = null; + $tables = tlObjectWithDB::getDBTables( + array( + 'nodes_hierarchy', + 'attachments', + 'tcversions' + )); + + foreach ($tcAtt as $value) { + $addAttachment = false; + + // Is it a CREATION or an UPDATE? + if ($xmlInternalID == $fk_Id) // internalID matches, seems to be an update + { + // try to bypass the importation of already known attachments. + // Check in database if the attachment with the same ID is linked to the testcase/testsuite with the same internal ID + // The couple attachment ID + InternalID is used as a kind of signature to avoid duplicates. + // If signature is not precise enough, could add the use of attachment timestamp (date_added in XML file). + $sql = " SELECT ATT.id from {$tables['attachments']} ATT " . + " WHERE ATT.id='{$dbHandler->prepare_string($value['id'])}' " . + " AND ATT.fk_id={$fk_Id} "; + + $rsx = $dbHandler->get_recordset($sql); + // allow attachment import only if no record with the same signature have been found in database + $addAttachment = (is_null($rsx) || count($rsx) < 1); + if ($addAttachment === false) { // inform user that the attachment has been skipped + if (! isset($duplicateAttachment[$value['id']])) { + $duplicateAttachment[$value['id']] = sprintf( + $messages['attachment'], $value['name']); + } + $resultMsg[] = array( + $tcaseName, + $duplicateAttachment[$value['id']] + ); + } + } else { + // Creation + $addAttachment = true; + } + + if ($addAttachment) { + $attachRepo = tlAttachmentRepository::create($dbHandler); + + $fileInfo = $attachRepo->createAttachmentTempFile($value['content']); + $fileInfo['name'] = $value['name']; + $fileInfo['type'] = $value['file_type']; + $tableRef = $tables['nodes_hierarchy']; + if ($isTestCase) { + $tableRef = $tables['tcversions']; + } + $iaOp = $attachRepo->insertAttachment($fk_Id, $tableRef, + $value['title'], $fileInfo); + } + } + + return $resultMsg; +} + +/** + */ +function importTestCasesFromSimpleXML(&$db, &$simpleXMLObj, $parentID, + $tproject_id, $userID, $kwMap, $duplicateLogic) +{ + $resultMap = null; + $xmlTCs = $simpleXMLObj->xpath('//testcase'); + $tcData = getTestCaseSetFromSimpleXMLObj($xmlTCs); + + if ($tcData) { + $resultMap = saveImportedTCData($db, $tcData, $tproject_id, $parentID, + $userID, $kwMap, $duplicateLogic); + } + return $resultMap; +} + +/** + * + * @internal revisions + */ +function getTestCaseSetFromSimpleXMLObj($xmlTCs) +{ + static $cfg; + if (! $cfg) { + $cfg = config_get('testcase_cfg')->import; + } + + $tcSet = null; + if (! $xmlTCs) { + return $tcSet; + } + + $jdx = 0; + $loops2do = count($xmlTCs); + $tcaseSet = array(); + + // TICKET 4963: Test case / Tes suite XML format, new element to set author + $tcXML['elements'] = array( + 'string' => array( + "summary" => null, + "preconditions" => null, + "author_login" => null, + "estimated_exec_duration" => null + ), + 'integer' => array( + "node_order" => null, + "externalid" => null, + "is_open" => null, + "active" => null, + "status" => null, + "execution_type" => null, + "importance" => null + ) + ); + $tcXML['attributes'] = array( + 'string' => array( + "name" => 'trim' + ), + 'integer' => array( + 'internalid' => null + ) + ); + + for ($idx = 0; $idx < $loops2do; $idx ++) { + $dummy = getItemsFromSimpleXMLObj(array( + $xmlTCs[$idx] + ), $tcXML); + $tc = $dummy[0]; + if ($tc) { + + if ($cfg->wordwrap->summary > 0) { + $tc['summary'] = wordwrap($tc['summary'], + $cfg->wordwrap->summary); + } + if ($cfg->wordwrap->preconditions > 0) { + $tc['preconditions'] = wordwrap($tc['preconditions'], + $cfg->wordwrap->preconditions); + } + + // Test Case Steps + $steps = getStepsFromSimpleXMLObj($xmlTCs[$idx]->steps->step); + $tc['steps'] = $steps; + + if ($cfg->wordwrap->actions > 0 || + $cfg->wordwrap->expected_results > 0) { + foreach ($tc['steps'] as $sdx => $elem) { + + if ($cfg->wordwrap->actions > 0) { + $tc['steps'][$sdx]['actions'] = wordwrap( + $tc['steps'][$sdx]['actions'], + $cfg->wordwrap->actions); + } + + if ($cfg->wordwrap->expected_results > 0) { + $tc['steps'][$sdx]['expected_results'] = wordwrap( + $tc['steps'][$sdx]['expected_results'], + $cfg->wordwrap->expected_results); + } + } + } + + $keywords = getKeywordsFromSimpleXMLObj( + $xmlTCs[$idx]->keywords->keyword); + if ($keywords) { + $tc['keywords'] = $keywords; + } + + $cf = getCustomFieldsFromSimpleXMLObj( + $xmlTCs[$idx]->custom_fields->custom_field); + if ($cf) { + $tc['customfields'] = $cf; + } + + $requirements = getRequirementsFromSimpleXMLObj( + $xmlTCs[$idx]->requirements->requirement); + if ($requirements) { + $tc['requirements'] = $requirements; + } + + $attachments = getAttachmentsFromSimpleXMLObj( + $xmlTCs[$idx]->attachments->attachment); + if ($attachments) { + $tc['attachments'] = $attachments; + } + } + $tcaseSet[$jdx ++] = $tc; + } + return $tcaseSet; +} + +/** + * + * @internal revisions + */ +function getStepsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "actions" => null, + "expectedresults" => null + ), + 'integer' => array( + "step_number" => null, + "execution_type" => null + ) + ); + + // 20110205 - franciscom - seems key 'transformations' is not managed on + // getItemsFromSimpleXMLObj(), then ??? is useless??? + $itemStructure['transformations'] = array( + "expectedresults" => "expected_results" + ); + + $items = getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); + + // need to do this due to (maybe) a wrong name choice for XML element + if (! is_null($items)) { + $loop2do = count($items); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $items[$idx]['expected_results'] = ''; + if (isset($items[$idx]['expectedresults'])) { + $items[$idx]['expected_results'] = $items[$idx]['expectedresults']; + unset($items[$idx]['expectedresults']); + } + } + } + return $items; +} + +function getCustomFieldsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "name" => 'trim', + "value" => 'trim' + ) + ); + return getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); +} + +function getRequirementsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "req_spec_title" => 'trim', + "doc_id" => 'trim', + "title" => 'trim' + ) + ); + return getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); +} + +function getAttachmentsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "id" => 'trim', + "name" => 'trim', + "file_type" => 'trim', + "title" => 'trim', + "date_added" => 'trim', + "content" => 'trim' + ) + ); + return getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); +} + +function getKeywordsFromSimpleXMLObj($simpleXMLItems) +{ + $itemStructure['elements'] = array( + 'string' => array( + "notes" => null + ) + ); + $itemStructure['attributes'] = array( + 'string' => array( + "name" => 'trim' + ) + ); + return getItemsFromSimpleXMLObj($simpleXMLItems, $itemStructure); +} + +/* + * function: importTestSuite + * args : + * returns: + * + * @internal revisions + * 20120623 - franciscom - TICKET 5070 - test suite custom fields import + * + */ +function importTestSuitesFromSimpleXML(&$dbHandler, &$xml, $parentID, + $tproject_id, $userID, $kwMap, $duplicateLogic, $importIntoProject = 0) +{ + static $tsuiteXML; + static $tsuiteMgr; + static $myself; + static $cfSpec; + static $doCF; + static $feedbackMsg; + + $feedbackMsg['attachment'] = lang_get('attachment_skipped_during_import'); + + $resultMap = array(); + if (is_null($tsuiteXML)) { + $myself = __FUNCTION__; + $tsuiteXML = array(); + $tsuiteXML['elements'] = array( + 'string' => array( + "details" => null + ), + 'integer' => array( + "node_order" => null + ) + ); + $tsuiteXML['attributes'] = array( + 'string' => array( + "name" => 'trim' + ), + 'integer' => array( + 'id' => null + ) + ); + + $tsuiteMgr = new testsuite($dbHandler); + $doCF = ! is_null( + ($cfSpec = $tsuiteMgr->get_linked_cfields_at_design(null, null, null, + $tproject_id, 'name'))); + } + + if ($xml->getName() == 'testsuite') { + // getItemsFromSimpleXMLObj() first argument must be an array + $dummy = getItemsFromSimpleXMLObj(array( + $xml + ), $tsuiteXML); + $tsuite = current($dummy); + $tsuiteXMLID = $dummy[0]['id']; + $tsuiteID = $parentID; // hmmm, not clear + + if ($tsuite['name'] != "") { + // Check if Test Suite with this name exists on this container + // if yes -> update instead of create + $info = $tsuiteMgr->get_by_name($tsuite['name'], $parentID); + if (is_null($info)) { + $ret = $tsuiteMgr->create($parentID, $tsuite['name'], + $tsuite['details'], $tsuite['node_order']); + $tsuite['id'] = $ret['id']; + } else { + $tsuiteMgr->update(($tsuite['id'] = $info[0]['id']), + $tsuite['name'], $tsuite['details'], null, + $tsuite['node_order']); + } + unset($dummy); + + $tsuiteID = $tsuite['id']; // $tsuiteID is needed on more code pieces => DO NOT REMOVE + if (! $tsuite['id']) { + return null; + } + + if ($doCF) { + $cf = getCustomFieldsFromSimpleXMLObj( + $xml->custom_fields->custom_field); + if (! is_null($cf)) { + processTestSuiteCF($tsuiteMgr, $cfSpec, $cf, $tsuite); + } + } + + if ($keywords = getKeywordsFromSimpleXMLObj($xml->keywords->keyword)) { + $kwIDs = buildKeywordList($kwMap, $keywords); + $tsuiteMgr->addKeywords($tsuite['id'], $kwIDs); + } + + if ($attachments = getAttachmentsFromSimpleXMLObj( + $xml->attachments->attachment) && ! is_null($attachments)) { + if ($tsuiteXMLID == "" && $info[0]['id'] > 0) { // testsuite id is optionnal in XML schema, id may has been retrieved from name during update + $tsuiteXMLID = $info[0]['id']; + } + $msg = processAttachments($dbHandler, false, $tsuite['name'], + $tsuiteXMLID, $tsuite['id'], $attachments, $feedbackMsg); + if (! is_null($msg)) { + $resultMap = array_merge($resultMap, $msg); + } + } + + unset($tsuite); + } elseif ($importIntoProject) { + $tsuiteID = intval($tproject_id); + } + + $childrenNodes = $xml->children(); + $loop2do = count($childrenNodes); + + for ($idx = 0; $idx < $loop2do; $idx ++) { + + $target = $childrenNodes[$idx]; + switch ($target->getName()) { + case 'testcase': + // getTestCaseSetFromSimpleXMLObj() first argument must be an array + $tcData = getTestCaseSetFromSimpleXMLObj(array( + $target + )); + if (trim($tcData[0]['name']) == '') { + $xx = array( + lang_get('testcase_has_no_name'), + lang_get('testcase_has_no_name') + ); + $resultMap = array_merge($resultMap, array( + $xx + )); + } else { + $resultMap = array_merge($resultMap, + saveImportedTCData($dbHandler, $tcData, $tproject_id, + $tsuiteID, $userID, $kwMap, $duplicateLogic)); + } + unset($tcData); + break; + + case 'testsuite': + $resultMap = array_merge($resultMap, + $myself($dbHandler, $target, $tsuiteID, $tproject_id, + $userID, $kwMap, $importIntoProject, $duplicateLogic)); + break; + + // Important Development Notice + // Due to XML file structure, while looping + // we will find also this children: + // node_order,keywords,custom_fields,details + // + // It's processing to get and save values is done + // on other pieces of this code. + // + // Form a logical point of view seems the better + // to consider and process here testcase and testsuite as children. + } + } + } + return $resultMap; +} + +/** + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $guiObj = new stdClass(); + $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); + $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); + $guiObj->hitCriteria = $argsObj->hit_criteria; + $guiObj->useRecursion = $argsObj->useRecursion; + $guiObj->containerID = $argsObj->container_id; + $guiObj->bImport = tlStringLen($argsObj->importType); + $guiObj->bIntoProject = $argsObj->bIntoProject; + $guiObj->resultMap = null; + $guiObj->container_name = ''; + + $dest_common = TL_TEMP_PATH . session_id() . "-importtcs"; + $dest_files = array( + 'XML' => $dest_common . ".xml" + ); + $guiObj->dest = $dest_files['XML']; + if (! is_null($argsObj->importType)) { + $guiObj->dest = $dest_files[$argsObj->importType]; + } + + $guiObj->file_check = array( + 'status_ok' => 1, + 'msg' => 'ok' + ); + + if ($argsObj->useRecursion) { + $guiObj->import_title = lang_get('title_tsuite_import_to'); + $guiObj->container_description = lang_get('test_suite'); + } else { + $guiObj->import_title = lang_get('title_tc_import_to'); + $guiObj->container_description = lang_get('test_case'); + } + + if ($argsObj->container_id) { + $tree_mgr = new tree($dbHandler); + $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->container_id); + unset($tree_mgr); + $guiObj->container_name = $node_info['name']; + if ($argsObj->container_id == $argsObj->tproject_id) { + $guiObj->container_description = lang_get('testproject'); + } + } + + return $guiObj; +} + +/** + * + * @internal revisions + * @since 1.9.4 + * + */ +function processTestSuiteCF(&$tsuiteMgr, &$cfDefinition, &$cfValues, $tsuite) +{ + static $messages; + static $missingCfMsg; + + if (is_null($messages)) { + $messages = array(); + $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); + $messages['start_warning'] = lang_get('start_warning'); + $messages['end_warning'] = lang_get('end_warning'); + $messages['testlink_warning'] = lang_get('testlink_warning'); + $messages['start_feedback'] = $messages['start_warning'] . "\n" . + $messages['testlink_warning'] . "\n"; + $messages['cfield'] = lang_get( + 'cf_value_not_imported_missing_cf_on_testproject'); + $messages['tsuite'] = lang_get('testsuite'); + } + + $cf2insert = null; + $resultMsg = null; + foreach ($cfValues as $value) { + if (isset($cfDefinition[$value['name']])) { + $cf2insert[$cfDefinition[$value['name']]['id']] = array( + 'type_id' => $cfDefinition[$value['name']]['type'], + 'cf_value' => $value['value'] + ); + } else { + if (! isset($missingCfMsg[$value['name']])) { + $missingCfMsg[$value['name']] = sprintf($messages['cfield'], + $value['name'], $messages['tsuite']); + } + $resultMsg[] = array( + $tsuite['name'], + $missingCfMsg[$value['name']] + ); + } + } + $tsuiteMgr->cfield_mgr->design_values_to_db($cf2insert, $tsuite['id'], null, + 'simple'); + return $resultMsg; +} + +/** + */ +function getReqSpecSet(&$dbHandler, $tproject_id) +{ + $debugMsg = __FUNCTION__; + + $tables = tlObjectWithDB::getDBTables( + array( + 'req_specs', + 'nodes_hierarchy', + 'requirements' + )); + + // get always Latest Revision Req. Spec Title + $sql = "/* $debugMsg */ " . + " SELECT RSPEC.id, NHRSPEC.name AS title, RSPEC.doc_id AS rspec_doc_id, REQ.req_doc_id " . + " FROM {$tables['req_specs']} RSPEC " . + " JOIN {$tables['nodes_hierarchy']} NHRSPEC ON NHRSPEC.id = RSPEC.id " . + " JOIN {$tables['requirements']} REQ ON REQ.srs_id = RSPEC.id " . + " WHERE RSPEC.testproject_id = " . intval($tproject_id) . + " ORDER BY RSPEC.id,title"; + + return $dbHandler->fetchRowsIntoMap($sql, 'req_doc_id'); } - -if($args->useRecursion) { - $obj_mgr = new testsuite($db); - $gui->actionOptions = - array('update_last_version' => lang_get('update_last_testcase_version'), - 'generate_new' => lang_get('generate_new_testcase'), - 'create_new_version' => lang_get('create_new_testcase_version')); - - $gui->hitOptions=array('name' => lang_get('same_name'), - 'internalID' => lang_get('same_internalID'), - 'externalID' => lang_get('same_externalID')); -} else { - $obj_mgr = new testcase($db); - $obj_mgr->setTestProject($args->tproject_id); -} - -$gui->actionOptions = -array('skip' => lang_get('skip_testcase_import'), - 'update_last_version' => lang_get('update_last_testcase_version'), - 'generate_new' => lang_get('generate_new_testcase'), - 'create_new_version' => lang_get('create_new_testcase_version')); - -$gui->hitOptions = array('name' => lang_get('same_name'), - 'internalID' => lang_get('same_internalID'), - 'externalID' => lang_get('same_externalID')); - - -$gui->testprojectName = $_SESSION['testprojectName']; -$gui->importTypes = $obj_mgr->get_import_file_types(); -$gui->action_on_duplicated_name=$args->action_on_duplicated_name; - - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -// -------------------------------------------------------------------------------------- -/* - function: importTestCaseDataFromXML - args : - returns: -*/ -function importTestCaseDataFromXML(&$db,$fileName,$parentID,$tproject_id,$userID,$options=null) { - - tLog('importTestCaseDataFromXML called for file: '. $fileName); - $xmlTCs = null; - $resultMap = null; - $my = array(); - $my['options'] = array('useRecursion' => false, 'importIntoProject' => 0, - 'duplicateLogic' => array('hitCriteria' => 'name', 'actionOnHit' => null)); - $my['options'] = array_merge($my['options'], (array)$options); - foreach($my['options'] as $varname => $value) { - $$varname = $value; - } - - if (file_exists($fileName)) { - $xml = @simplexml_load_file_wrapper($fileName); - if($xml !== FALSE) { - $xmlKeywords = $xml->xpath('//keywords'); - $kwMap = null; - if ($xmlKeywords) { - $tproject = new testproject($db); - $loop2do = sizeof($xmlKeywords); - for($idx = 0; $idx < $loop2do ;$idx++) { - $tproject->importKeywordsFromSimpleXML($tproject_id,$xmlKeywords[$idx]); - } - $kwMap = $tproject->get_keywords_map($tproject_id); - $kwMap = is_null($kwMap) ? null : array_flip($kwMap); - } - - if (!$useRecursion && ($xml->getName() == 'testcases') ) { - $resultMap = importTestCasesFromSimpleXML($db,$xml,$parentID,$tproject_id,$userID,$kwMap,$duplicateLogic); - } - - if ($useRecursion && ($xml->getName() == 'testsuite')) { - $resultMap = importTestSuitesFromSimpleXML($db,$xml,intval($parentID),intval($tproject_id),$userID,$kwMap,$importIntoProject,$duplicateLogic); - } - } - } - return $resultMap; -} - - -// -------------------------------------------------------------------------------------- -/* - function: saveImportedTCData - args : - returns: -*/ -function saveImportedTCData(&$db,$tcData,$tproject_id,$container_id, - $userID,$kwMap,$duplicatedLogic = array('hitCriteria' => 'name', 'actionOnHit' => null)) -{ - static $messages; - static $fieldSizeCfg; - static $feedbackMsg; - static $tcase_mgr; - static $tproject_mgr; - static $req_spec_mgr; - static $req_mgr; - static $safeSizeCfg; - static $linkedCustomFields; - static $tprojectHas; - static $reqSpecSet; - static $getVersionOpt; - static $userObj; - static $tcasePrefix; - static $glueChar; - static $userRights; - - $ret = null; - - if (!$tcData) { - return; - } - - // $tprojectHas = array('customFields' => false, 'reqSpec' => false); - $hasCFieldsInfo = false; - $hasRequirements = false; - $hasAttachments = false; - - if(is_null($messages)) { - $feedbackMsg = array(); - $messages = array(); - $fieldSizeCfg = config_get('field_size'); - - $tcase_mgr = new testcase($db); - $tcase_mgr->setTestProject($tproject_id); - - $tproject_mgr = new testproject($db); - $req_spec_mgr = new requirement_spec_mgr($db); - $req_mgr = new requirement_mgr($db); - $userObj = new tlUser($userID); - $userObj->readFromDB($db,tlUser::TLOBJ_O_SEARCH_BY_ID); - - $userRights['can_edit_executed'] = - $userObj->hasRight($db,'testproject_edit_executed_testcases',$tproject_id); - $userRights['can_link_to_req'] = - $userObj->hasRight($db,'req_tcase_link_management',$tproject_id); - $userRights['can_assign_keywords'] = - $userObj->hasRight($db,'keyword_assignment',$tproject_id); - - $k2l = array('already_exists_updated','original_name','testcase_name_too_long', - 'already_exists_not_updated','already_exists_skipped', - 'start_warning','end_warning','testlink_warning', - 'hit_with_same_external_ID', - 'keywords_assignment_skipped_during_import', - 'req_assignment_skipped_during_import'); - - foreach($k2l as $k) { - $messages[$k] = lang_get($k); - } - - $messages['start_feedback'] = $messages['start_warning'] . "\n" . $messages['testlink_warning'] . "\n"; - $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); - $messages['reqspec_warning'] = lang_get('no_reqspec_defined_can_not_import'); - - - $feedbackMsg['cfield']=lang_get('cf_value_not_imported_missing_cf_on_testproject'); - $feedbackMsg['tcase'] = lang_get('testcase'); - $feedbackMsg['req'] = lang_get('req_not_in_req_spec_on_tcimport'); - $feedbackMsg['req_spec'] = lang_get('req_spec_ko_on_tcimport'); - $feedbackMsg['reqNotInDB'] = lang_get('req_not_in_DB_on_tcimport'); - $feedbackMsg['attachment'] = lang_get('attachment_skipped_during_import'); - - - // because name can be changed automatically during item creation - // to avoid name conflict adding a suffix automatically generated, - // is better to use a max size < max allowed size - $safeSizeCfg = new stdClass(); - $safeSizeCfg->testcase_name=($fieldSizeCfg->testcase_name) * 0.8; - - - // Get CF with scope design time and allowed for test cases linked to this test project - $linkedCustomFields = $tcase_mgr->cfield_mgr->get_linked_cfields_at_design($tproject_id,1,null,'testcase',null,'name'); - $tprojectHas['customFields']=!is_null($linkedCustomFields); - - $reqSpecSet = getReqSpecSet($db,$tproject_id); - - $tprojectHas['reqSpec'] = (!is_null($reqSpecSet) && count($reqSpecSet) > 0); - - $getVersionOpt = array('output' => 'minimun'); - $tcasePrefix = $tproject_mgr->getTestCasePrefix($tproject_id); - $glueChar = config_get('testcase_cfg')->glue_character; - } - - $resultMap = array(); - $tc_qty = sizeof($tcData); - $userIDCache = array(); - - for($idx = 0; $idx <$tc_qty ; $idx++) { - $tc = $tcData[$idx]; - $name = $tc['name']; - $summary = $tc['summary']; - $steps = $tc['steps']; - $internalid = $tc['internalid']; - $externalid = $tc['externalid']; - - $doCreate = true; - if( $duplicatedLogic['actionOnHit'] == 'update_last_version' || - $duplicatedLogic['actionOnHit'] == 'skip' ) { - - $updOpt['blockIfExecuted'] = !$userRights['can_edit_executed']; - - switch($duplicatedLogic['hitCriteria']) { - case 'name': - $dupInfo = $tcase_mgr->getDuplicatesByName($name,$container_id); - break; - - case 'internalID': - $dummy = $tcase_mgr->tree_manager->get_node_hierarchy_info($internalid,$container_id); - if( !is_null($dummy) ) { - $dupInfo = null; - $dupInfo[$internalid] = $dummy; - } - break; - - case 'externalID': - $dupInfo = $tcase_mgr->get_by_external($externalid,$container_id); - break; - } - } - - // Check for skip, to avoid useless processing - if( $duplicatedLogic['actionOnHit'] == 'skip' && !is_null($dupInfo) && - count($dupInfo) > 0 ) { - $resultMap[] = array($name,$messages['already_exists_skipped']); - continue; - } - - // I've changed value to use when order has not been provided - // from testcase:DEFAULT_ORDER to a counter, because with original solution - // an issue arise with 'save execution and go next' - // if use has not provided order I think is OK TestLink make any choice. - $node_order = isset($tc['node_order']) ? intval($tc['node_order']) : ($idx+1); - $internalid = $tc['internalid']; - $preconditions = $tc['preconditions']; - $exec_type = isset($tc['execution_type']) ? $tc['execution_type'] : TESTCASE_EXECUTION_TYPE_MANUAL; - $importance = isset($tc['importance']) ? $tc['importance'] : MEDIUM; - - $attr = null; - if(isset($tc['estimated_exec_duration']) && !is_null($tc['estimated_exec_duration'])) { - $attr['estimatedExecDuration'] = trim($tc['estimated_exec_duration']); - $attr['estimatedExecDuration'] = $attr['estimatedExecDuration']=='' ? null : floatval($attr['estimatedExecDuration']); - } - - if(isset($tc['is_open'])) { - $attr['is_open'] = trim($tc['is_open']); - } - - if(isset($tc['active'])) { - $attr['active'] = trim($tc['active']); - } - - if(isset($tc['status'])) { - $attr['status'] = trim($tc['status']); - } - - $externalid = $tc['externalid']; - if( intval($externalid) <= 0 ) { - $externalid = null; - } - - $personID = $userID; - if( !is_null($tc['author_login']) ) { - if( isset($userIDCache[$tc['author_login']]) ) { - $personID = $userIDCache[$tc['author_login']]; - } else { - $userObj->login = $tc['author_login']; - if( $userObj->readFromDB($db,tlUser::USER_O_SEARCH_BYLOGIN) == tl::OK ) { - $personID = $userObj->dbID; - } - - // I will put always a valid userID on this cache, - // this way if author_login does not exit, and is used multiple times - // i will do check for existence JUST ONCE. - $userIDCache[$tc['author_login']] = $personID; - } - } - - $name_len = tlStringLen($name); - if($name_len > $fieldSizeCfg->testcase_name) { - // Will put original name inside summary - $xx = $messages['start_feedback']; - $xx .= sprintf($messages['testcase_name_too_long'],$name_len, $fieldSizeCfg->testcase_name) . "\n"; - $xx .= $messages['original_name'] . "\n" . $name. "\n" . $messages['end_warning'] . "\n"; - $tcCfg = getWebEditorCfg('design'); - $tcType = $tcCfg['type']; - if ($tcType == 'none'){ - $summary = $xx . $summary ; - } else{ - $summary = nl2br($xx) . $summary ; - } - $name = tlSubStr($name, 0, $safeSizeCfg->testcase_name); - } - - - $kwIDs = null; - if (isset($tc['keywords']) && $tc['keywords']) { - if(!$userRights['can_assign_keywords']){ - $resultMap[] = - array($name,$messages['keywords_assignment_skipped_during_import']); - } else{ - $kwIDs = implode(",",buildKeywordList($kwMap,$tc['keywords'])); - } - } - - // More logic regarding Action on Duplicate - if( $duplicatedLogic['actionOnHit'] == 'update_last_version' && - !is_null($dupInfo) ) { - - $tcase_qty = count($dupInfo); - - switch($tcase_qty) { - case 1: - $doCreate=false; - $tcase_id = key($dupInfo); - $last_version = $tcase_mgr->get_last_version_info($tcase_id, - $getVersionOpt); - $tcversion_id = $last_version['id']; - $ret = $tcase_mgr->update($tcase_id,$tcversion_id,$name, - $summary, - $preconditions,$steps,$personID, - $kwIDs, - $node_order,$exec_type,$importance, - $attr,$updOpt); - - $ret['id'] = $tcase_id; - $ret['tcversion_id'] = $tcversion_id; - if( $ret['status_ok'] ) { - $resultMap[] = array($name,$messages['already_exists_updated']); - } else { - if($ret['reason'] == '') { - $resultMap[] = array($name, - sprintf($messages['already_exists_not_updated'], - $tcasePrefix . $glueChar . $externalid, - $tcasePrefix . $glueChar . $ret['hit_on']['tc_external_id'])); - } else { - $resultMap[] = array($name,$ret['msg']); - } - } - break; - - case 0: - $doCreate=true; - break; - - default: - $doCreate=false; - break; - } - } - - if( $doCreate ) { - // Want to block creation of with existent EXTERNAL ID, if containers ARE DIFFERENT. - $item_id = intval($tcase_mgr->getInternalID($externalid, array('tproject_id' => $tproject_id))); - - if( $item_id > 0) { - // who is his parent ? - $owner = $tcase_mgr->getTestSuite($item_id); - if( $owner != $container_id) { - // Get full path of existent Test Cases - $stain = $tcase_mgr->tree_manager->get_path($item_id,null, 'name'); - $n = count($stain); - $stain[$n-1] = $tcasePrefix . config_get('testcase_cfg')->glue_character . $externalid . ':' . $stain[$n-1]; - $stain = implode('/',$stain); - - $resultMap[] = array($name,$messages['hit_with_same_external_ID'] . $stain); - $doCreate = false; - } - } - } - - if( $doCreate ) { - $createOptions = - array('check_duplicate_name' => testcase::CHECK_DUPLICATE_NAME, - 'action_on_duplicate_name' => $duplicatedLogic['actionOnHit'], - 'external_id' => $externalid, 'importLogic' => $duplicatedLogic); - - if(!is_null($attr) ) { - $createOptions += $attr; - } - - if ( $ret = $tcase_mgr->create($container_id,$name,$summary,$preconditions, - $steps,$personID,$kwIDs,$node_order, - testcase::AUTOMATIC_ID, - $exec_type,$importance,$createOptions) ) { - $resultMap[] = array($name,$ret['msg']); - } - } - - // Custom Fields Management - // Check if CF with this name and that can be used on Test Cases - // is defined in current Test Project. - // If Check fails => give message to user. - // Else Import CF data - // - $hasCFieldsInfo = (isset($tc['customfields']) && - !is_null($tc['customfields'])); - - - if($hasCFieldsInfo && !is_null($ret)) { - if($tprojectHas['customFields']) { - $msg = processCustomFields($tcase_mgr,$name,$ret['id'],$ret['tcversion_id'], - $tc['customfields'],$linkedCustomFields,$feedbackMsg); - if( !is_null($msg) ) { - $resultMap = array_merge($resultMap,$msg); - } - } else { - // Can not import Custom Fields Values, give feedback - $msg[]=array($name,$messages['cf_warning']); - $resultMap = array_merge($resultMap,$msg); - } - } - - $hasRequirements=(isset($tc['requirements']) && !is_null($tc['requirements'])); - - if($hasRequirements) { - if( $tprojectHas['reqSpec'] ) { - - if(!$userRights['can_link_to_req']){ - $msg[]=array($name,$messages['req_assignment_skipped_during_import']); - } else{ - $msg = processRequirements($db,$req_mgr,$name,$ret,$tc['requirements'], - $reqSpecSet,$feedbackMsg,$userID); - } - - if( !is_null($msg) ) { - $resultMap = array_merge($resultMap,$msg); - } - } else { - $msg[]=array($name,$messages['reqspec_warning']); - $resultMap = array_merge($resultMap,$msg); - } - } - - - $hasAttachments=(isset($tc['attachments']) && !is_null($tc['attachments'])); - if($hasAttachments) { - $fk_id = $doCreate ? $ret['id'] : $internalid; - if ($internalid == "" && $item_id>0) { - $internalid = $item_id; - } - $msg = processAttachments( $db, $name, $internalid, $fk_id, $tc['attachments'], - $feedbackMsg ); - if( !is_null($msg) ) { - $resultMap = array_merge($resultMap,$msg); - } - } - - } - - return $resultMap; -} - - -// ------------------------------------------------------------------------------- -/* - function: buildKeywordList - args : - returns: -*/ -function buildKeywordList($kwMap,$keywords) { - $items = array(); - $loop2do = sizeof($keywords); - for($jdx = 0; $jdx <$loop2do ; $jdx++) { - $items[] = $kwMap[trim($keywords[$jdx]['name'])]; - } - return $items; -} - - -// ---------------------------------------------------------------------------------- - -// ------------------------------------------------------------------------------------ - -/* - function: Check if at least the file starts seems OK -*/ -function check_xml_tc_tsuite($fileName,$recursiveMode) { - $xml = @simplexml_load_file_wrapper($fileName); - $file_check = array('status_ok' => 0, 'msg' => 'xml_load_ko'); - if($xml !== FALSE) - { - $file_check = array('status_ok' => 1, 'msg' => 'ok'); - $elementName = $xml->getName(); - if($recursiveMode) - { - if($elementName != 'testsuite') - { - $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_xml_tsuite_file')); - } - } - else - { - if($elementName != 'testcases' && $elementName != 'testcase') - { - $file_check=array('status_ok' => 0, 'msg' => lang_get('wrong_xml_tcase_file')); - } - } - } - return $file_check; -} - - - -/* contribution by mirosvad - - Convert new line characters from XLS to HTML -*/ -function nl2p($str) -{ - return str_replace('

    ', '', '

    ' . preg_replace('#\n|\r#', '

    $0

    ', $str) . '

    '); //MS -} - - -/* - function: - - args : - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $key='action_on_duplicated_name'; - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'generate_new'; - - $key='hit_criteria'; - $args->$key = isset($_REQUEST[$key]) ? $_REQUEST[$key] : 'name'; - - - $args->importType = isset($_REQUEST['importType']) ? $_REQUEST['importType'] : null; - $args->useRecursion = isset($_REQUEST['useRecursion']) ? $_REQUEST['useRecursion'] : 0; - $args->location = isset($_REQUEST['location']) ? $_REQUEST['location'] : null; - $args->container_id = isset($_REQUEST['containerID']) ? intval($_REQUEST['containerID']) : 0; - $args->bIntoProject = isset($_REQUEST['bIntoProject']) ? intval($_REQUEST['bIntoProject']) : 0; - - $args->containerType = isset($_REQUEST['containerType']) ? intval($_REQUEST['containerType']) : 0; - $args->do_upload = isset($_REQUEST['UploadFile']) ? 1 : 0; - - $args->userID = $_SESSION['userID']; - $args->tproject_id = $_SESSION['testprojectID']; - - return $args; -} - - -/** - * processCustomFields - * - * Analise custom field info related to test case being imported. - * If everything OK, assign to test case. - * Else return an array of messages. - * - * - * @internal revisions - * 20100905 - franciscom - BUGID 3431 - Custom Field values at Test Case VERSION Level - */ -function processCustomFields(&$tcaseMgr,$tcaseName,$tcaseId,$tcversionId,$cfValues,$cfDefinition,$messages) -{ - static $missingCfMsg; - $cf2insert=null; - $resultMsg=null; - - foreach($cfValues as $value) - { - if( isset($cfDefinition[$value['name']]) ) - { - $cf2insert[$cfDefinition[$value['name']]['id']]=array('type_id' => $cfDefinition[$value['name']]['type'], - 'cf_value' => $value['value']); - } - else - { - if( !isset($missingCfMsg[$value['name']]) ) - { - $missingCfMsg[$value['name']] = sprintf($messages['cfield'],$value['name'],$messages['tcase']); - } - $resultMsg[] = array($tcaseName,$missingCfMsg[$value['name']]); - } - } - - $tcaseMgr->cfield_mgr->design_values_to_db($cf2insert,$tcversionId,null,'simple'); - return $resultMsg; -} - -/** - * processRequirements - * - * Analise requirements info related to test case being imported. - * If everything OK, assign to test case. - * Else return an array of messages. - * - */ -function processRequirements(&$dbHandler,&$reqMgr,$tcaseName,$tcIDCard, - $tcReq,$reqSpecSet,$messages,$userID) { - - static $missingReqMsg; - static $missingReqSpecMsg; - static $missingReqInDBMsg; - static $cachedReqSpec; - - $resultMsg = null; - $tables = tlObjectWithDB::getDBTables(array('requirements')); - - // Since 1.9.18, links are between req version e test case version - // We will work on latest test case version and lates req version - // - $tcaseId = $tcIDCard['id']; - $latestTCVersionID = $tcIDCard['tcversion_id']; - - foreach($tcReq as $ydx => $value) { - $cachedReqSpec=array(); - $doit=false; - - // Look for req doc id we get from file, inside Req Spec Set - // we got from DB - if( ($doit=isset($reqSpecSet[$value['doc_id']])) ) { - if( !(isset($cachedReqSpec[$value['req_spec_title']])) ) { - // $cachedReqSpec - // key: Requirement Specification Title get from file - // value: map with follogin keys - // id => requirement specification id from DB - // req => map with key: requirement document id - $cachedReqSpec[$value['req_spec_title']]['id'] = - $reqSpecSet[$value['doc_id']]['id']; - - $cachedReqSpec[$value['req_spec_title']]['req']=null; - } - } - - if($doit) { - $useit = false; - $req_spec_id = $cachedReqSpec[$value['req_spec_title']]['id']; - - // Check if requirement with desired document id exists - // on requirement specification on DB. - // If not => create message for user feedback. - if( !($useit=isset($cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']])) ) { - - $sql = " SELECT REQ.id from {$tables['requirements']} REQ " . - " WHERE REQ.req_doc_id='{$dbHandler->prepare_string($value['doc_id'])}' " . - " AND REQ.srs_id={$req_spec_id} "; - - $rsx = $dbHandler->get_recordset($sql); - if( $useit=((!is_null($rsx) && count($rsx) > 0) ? true : false) ) { - $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']]=$rsx[0]['id']; - } - } - - if($useit) { - // $reqMgr->assign_to_tcase($cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']],$tcaseId,$userID); - $reqMgr->assignToTCaseUsingLatestVersions( - $cachedReqSpec[$value['req_spec_title']]['req'][$value['doc_id']], - $tcaseId,$userID); - - } - else { - if( !isset($missingReqMsg[$value['doc_id']]) ) { - $missingReqMsg[$value['doc_id']]=sprintf($messages['req'], - $value['doc_id'],$value['req_spec_title']); - } - $resultMsg[] = array($tcaseName,$missingReqMsg[$value['doc_id']]); - } - } else { - // We didnt find Req Doc ID in Req Spec Set got from DB - if( !isset($missingReqInDBMsg[$value['doc_id']]) ) { - $missingReqInDBMsg[$value['doc_id']]=sprintf($messages['reqNotInDB'], - $value['doc_id'],''); - } - $resultMsg[] = array($tcaseName,$missingReqInDBMsg[$value['doc_id']]); - } - - } //foreach - - return $resultMsg; -} - -/** - * processAttachments - * - * Analyze attachments info related to testcase or testsuite to define if the the attachment has to be saved. - * If attachment format is OK and attachment is not already in database for the target, save the attachment. - * Else return an array of messages. - * - */ -function processAttachments( &$dbHandler, $tcaseName, $xmlInternalID, $fk_Id, $tcAtt, $messages ) -{ - static $duplicateAttachment; - $resultMsg=null; - $tables = tlObjectWithDB::getDBTables(array('nodes_hierarchy','attachments')); - - foreach( $tcAtt as $ydx => $value ) - { - $addAttachment = false; - - // Is it a CREATION or an UPDATE? - if( $xmlInternalID == $fk_Id ) // internalID matches, seems to be an update - { - // try to bypass the importation of already known attachments. - // Check in database if the attachment with the same ID is linked to the testcase/testsuite with the same internal ID - // The couple attachment ID + InternalID is used as a kind of signature to avoid duplicates. - // If signature is not precise enough, could add the use of attachment timestamp (date_added in XML file). - $sql = " SELECT ATT.id from {$tables['attachments']} ATT " . - " WHERE ATT.id='{$dbHandler->prepare_string($value['id'])}' " . - " AND ATT.fk_id={$fk_Id} "; - - $rsx=$dbHandler->get_recordset($sql); - // allow attachment import only if no record with the same signature have been found in database - $addAttachment = ( is_null($rsx) || count($rsx) < 1 ); - if( $addAttachment === false ){ // inform user that the attachment has been skipped - if( !isset($duplicateAttachment[$value['id']]) ) - { - $duplicateAttachment[$value['id']]=sprintf($messages['attachment'],$value['name']); - } - $resultMsg[] = array($tcaseName,$duplicateAttachment[$value['id']]); - } - - }else{ - // Creation - $addAttachment = true; - } - - if( $addAttachment ) - { - $attachRepo = tlAttachmentRepository::create($dbHandler); - - $fileInfo = $attachRepo->createAttachmentTempFile( $value['content'] ); - $fileInfo['name'] = $value['name']; - $fileInfo['type'] = $value['file_type']; - - $attachRepo->insertAttachment( $fk_Id, $tables['nodes_hierarchy'], $value['title'], $fileInfo); - } - } //foreach - - return $resultMsg; -} - - - -/** - * - * - */ -function importTestCasesFromSimpleXML(&$db,&$simpleXMLObj,$parentID,$tproject_id,$userID,$kwMap,$duplicateLogic) { - $resultMap = null; - $xmlTCs = $simpleXMLObj->xpath('//testcase'); - $tcData = getTestCaseSetFromSimpleXMLObj($xmlTCs); - - if ($tcData) { - $resultMap = saveImportedTCData($db,$tcData,$tproject_id,$parentID,$userID,$kwMap,$duplicateLogic); - } - return $resultMap; -} - -/** - * - * - * @internal revisions - */ -function getTestCaseSetFromSimpleXMLObj($xmlTCs) -{ - $tcSet = null; - if (!$xmlTCs) - { - return $tcSet; - } - - $jdx = 0; - $loops2do=sizeof($xmlTCs); - $tcaseSet = array(); - - // $tcXML['elements'] = array('string' => array("summary","preconditions"), - // 'integer' => array("node_order","externalid","execution_type","importance")); - // $tcXML['attributes'] = array('string' => array("name"), 'integer' =>array('internalid')); - - // TICKET 4963: Test case / Tes suite XML format, new element to set author - $tcXML['elements'] = array('string' => array("summary" => null,"preconditions" => null, - "author_login" => null,"estimated_exec_duration" => null), - 'integer' => array("node_order" => null,"externalid" => null,"is_open" => null,"active" => null,"status" => null, - "execution_type" => null ,"importance" => null)); - $tcXML['attributes'] = array('string' => array("name" => 'trim'), - 'integer' =>array('internalid' => null)); - - for($idx = 0; $idx < $loops2do; $idx++) - { - $dummy = getItemsFromSimpleXMLObj(array($xmlTCs[$idx]),$tcXML); - $tc = $dummy[0]; - - if ($tc) - { - // Test Case Steps - $steps = getStepsFromSimpleXMLObj($xmlTCs[$idx]->steps->step); - $tc['steps'] = $steps; - - $keywords = getKeywordsFromSimpleXMLObj($xmlTCs[$idx]->keywords->keyword); - if ($keywords) - { - $tc['keywords'] = $keywords; - } - - $cf = getCustomFieldsFromSimpleXMLObj($xmlTCs[$idx]->custom_fields->custom_field); - if($cf) - { - $tc['customfields'] = $cf; - } - - $requirements = getRequirementsFromSimpleXMLObj($xmlTCs[$idx]->requirements->requirement); - if($requirements) - { - $tc['requirements'] = $requirements; - } - - $attachments = getAttachmentsFromSimpleXMLObj($xmlTCs[$idx]->attachments->attachment); - if($attachments) - { - $tc['attachments'] = $attachments; - } - } - $tcaseSet[$jdx++] = $tc; - } - return $tcaseSet; -} - - -/** - * - * - * @internal revisions - */ -function getStepsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("actions"=>null,"expectedresults" => null), - 'integer' => array("step_number" => null,"execution_type" => null)); - - // 20110205 - franciscom - seems key 'transformations' is not managed on - // getItemsFromSimpleXMLObj(), then ??? is useless??? - $itemStructure['transformations'] = array("expectedresults" => "expected_results"); - - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - - // need to do this due to (maybe) a wrong name choice for XML element - if( !is_null($items) ) - { - $loop2do = count($items); - for($idx=0; $idx < $loop2do; $idx++) - { - $items[$idx]['expected_results'] = ''; - if( isset($items[$idx]['expectedresults']) ) - { - $items[$idx]['expected_results'] = $items[$idx]['expectedresults']; - unset($items[$idx]['expectedresults']); - } - } - } - return $items; -} - -function getCustomFieldsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("name" => 'trim',"value" => 'trim')); - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - return $items; - -} - -function getRequirementsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("req_spec_title" => 'trim', - "doc_id" => 'trim' ,"title" => 'trim' )); - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - return $items; -} - -function getAttachmentsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("id" => 'trim', "name" => 'trim', - "file_type" => 'trim' ,"title" => 'trim', - "date_added" => 'trim' ,"content" => 'trim' )); - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - return $items; -} - -function getKeywordsFromSimpleXMLObj($simpleXMLItems) -{ - $itemStructure['elements'] = array('string' => array("notes" => null)); - $itemStructure['attributes'] = array('string' => array("name" => 'trim')); - $items = getItemsFromSimpleXMLObj($simpleXMLItems,$itemStructure); - return $items; -} - - -/* - function: importTestSuite - args : - returns: - - @internal revisions - 20120623 - franciscom - TICKET 5070 - test suite custom fields import - -*/ -function importTestSuitesFromSimpleXML(&$dbHandler,&$xml,$parentID,$tproject_id, - $userID,$kwMap,$importIntoProject = 0,$duplicateLogic) -{ - static $tsuiteXML; - static $tsuiteMgr; - static $myself; - static $callCounter = 0; - static $cfSpec; - static $doCF; - static $feedbackMsg; - - $feedbackMsg['attachment'] = lang_get('attachment_skipped_during_import'); - - $resultMap = array(); - if(is_null($tsuiteXML) ) - { - $myself = __FUNCTION__; - $tsuiteXML = array(); - $tsuiteXML['elements'] = array('string' => array("details" => null), - 'integer' => array("node_order" => null)); - $tsuiteXML['attributes'] = array('string' => array("name" => 'trim'), - 'integer' =>array('id' => null)); - - $tsuiteMgr = new testsuite($dbHandler); - $doCF = !is_null(($cfSpec = $tsuiteMgr->get_linked_cfields_at_design(null,null,null, - $tproject_id,'name'))); - } - - if($xml->getName() == 'testsuite') - { - - - // getItemsFromSimpleXMLObj() first argument must be an array - $dummy = getItemsFromSimpleXMLObj(array($xml),$tsuiteXML); - $tsuite = current($dummy); - $tsuiteXMLID = $dummy[0]['id']; - $tsuiteID = $parentID; // hmmm, not clear - - if ($tsuite['name'] != "") - { - // Check if Test Suite with this name exists on this container - // if yes -> update instead of create - $info = $tsuiteMgr->get_by_name($tsuite['name'],$parentID); - if( is_null($info) ) - { - $ret = $tsuiteMgr->create($parentID,$tsuite['name'],$tsuite['details'],$tsuite['node_order']); - $tsuite['id'] = $ret['id']; - } - else - { - $ret = $tsuiteMgr->update(($tsuite['id'] = $info[0]['id']),$tsuite['name'],$tsuite['details'], - null,$tsuite['node_order']); - - } - unset($dummy); - - $tsuiteID = $tsuite['id']; // $tsuiteID is needed on more code pieces => DO NOT REMOVE - if (!$tsuite['id']) - { - return null; - } - - if($doCF) - { - $cf = getCustomFieldsFromSimpleXMLObj($xml->custom_fields->custom_field); - if(!is_null($cf)) - { - processTestSuiteCF($tsuiteMgr,$xml,$cfSpec,$cf,$tsuite,$tproject_id); - } - } - - if( $keywords = getKeywordsFromSimpleXMLObj($xml->keywords->keyword) ) - { - $kwIDs = buildKeywordList($kwMap,$keywords); - $tsuiteMgr->addKeywords($tsuite['id'],$kwIDs); - } - - if( $attachments = getAttachmentsFromSimpleXMLObj($xml->attachments->attachment) ) - { - if(!is_null($attachments)) - { - if ($tsuiteXMLID == "" && $info[0]['id']>0){ // testsuite id is optionnal in XML schema, id may has been retrieved from name during update - $tsuiteXMLID = $info[0]['id']; - } - $msg = processAttachments( $dbHandler, $tsuite['name'], $tsuiteXMLID, $tsuite['id'], $attachments, $feedbackMsg ); - if( !is_null($msg) ) - { - $resultMap = array_merge($resultMap,$msg); - } - } - } - - unset($tsuite); - } - else if($importIntoProject) - { - $tsuiteID = intval($tproject_id); - } - - $childrenNodes = $xml->children(); - $loop2do = sizeof($childrenNodes); - - for($idx = 0; $idx < $loop2do; $idx++) { - - $target = $childrenNodes[$idx]; - switch($target->getName()) { - case 'testcase': - // getTestCaseSetFromSimpleXMLObj() first argument must be an array - $tcData = getTestCaseSetFromSimpleXMLObj(array($target)); - if( trim($tcData[0]['name']) == '' ) { - $xx = array(lang_get('testcase_has_no_name'),lang_get('testcase_has_no_name')); - $resultMap = array_merge($resultMap,array($xx)); - } else { - $resultMap = array_merge($resultMap, - saveImportedTCData($dbHandler,$tcData,$tproject_id, - $tsuiteID,$userID,$kwMap,$duplicateLogic)); - } - unset($tcData); - break; - - case 'testsuite': - $resultMap = array_merge($resultMap, - $myself($dbHandler,$target,$tsuiteID,$tproject_id, - $userID,$kwMap,$importIntoProject,$duplicateLogic)); - break; - - - // Important Development Notice - // Due to XML file structure, while looping - // we will find also this children: - // node_order,keywords,custom_fields,details - // - // It's processing to get and save values is done - // on other pieces of this code. - // - // Form a logical point of view seems the better - // to consider and process here testcase and testsuite as children. - // - } - } - } - return $resultMap; -} - - -/** - * - * - * - **/ -function initializeGui(&$dbHandler,&$argsObj) -{ - $guiObj = new stdClass(); - $guiObj->importLimitBytes = config_get('import_file_max_size_bytes'); - $guiObj->importLimitKB = ($guiObj->importLimitBytes / 1024); - $guiObj->hitCriteria = $argsObj->hit_criteria; - $guiObj->useRecursion = $argsObj->useRecursion; - $guiObj->containerID = $argsObj->container_id; - $guiObj->bImport = tlStringLen($argsObj->importType); - $guiObj->bIntoProject = $argsObj->bIntoProject; - $guiObj->resultMap = null; - $guiObj->container_name = ''; - - - $dest_common = TL_TEMP_PATH . session_id(). "-importtcs"; - $dest_files = array('XML' => $dest_common . ".xml"); - $guiObj->dest = $dest_files['XML']; - if(!is_null($argsObj->importType)) - { - $guiObj->dest = $dest_files[$argsObj->importType]; - } - - $guiObj->file_check = array('status_ok' => 1, 'msg' => 'ok'); - - if($argsObj->useRecursion) - { - $guiObj->import_title = lang_get('title_tsuite_import_to'); - $guiObj->container_description = lang_get('test_suite'); - } - else - { - $guiObj->import_title = lang_get('title_tc_import_to'); - $guiObj->container_description = lang_get('test_case'); - } - - if($argsObj->container_id) - { - $tree_mgr = new tree($dbHandler); - $node_info = $tree_mgr->get_node_hierarchy_info($argsObj->container_id); - unset($tree_mgr); - $guiObj->container_name = $node_info['name']; - if($argsObj->container_id == $argsObj->tproject_id) - { - $guiObj->container_description = lang_get('testproject'); - } - } - - return $guiObj; -} - -/** - * - * - * @internal revisions - * @since 1.9.4 - * - **/ -function processTestSuiteCF(&$tsuiteMgr,$xmlObj,&$cfDefinition,&$cfValues,$tsuite,$tproject_id) -{ - - static $messages; - static $missingCfMsg; - - if(is_null($messages)) { - $messages = array(); - $messages['cf_warning'] = lang_get('no_cf_defined_can_not_import'); - $messages['start_warning'] = lang_get('start_warning'); - $messages['end_warning'] = lang_get('end_warning'); - $messages['testlink_warning'] = lang_get('testlink_warning'); - $messages['start_feedback'] = $messages['start_warning'] . "\n" . $messages['testlink_warning'] . "\n"; - $messages['cfield'] = lang_get('cf_value_not_imported_missing_cf_on_testproject'); - $messages['tsuite'] = lang_get('testsuite'); - } - - $cf2insert=null; - $resultMsg=null; - foreach($cfValues as $value) - { - if( isset($cfDefinition[$value['name']]) ) - { - $cf2insert[$cfDefinition[$value['name']]['id']]=array('type_id' => $cfDefinition[$value['name']]['type'], - 'cf_value' => $value['value']); - } - else - { - if( !isset($missingCfMsg[$value['name']]) ) - { - $missingCfMsg[$value['name']] = sprintf($messages['cfield'],$value['name'],$messages['tsuite']); - } - $resultMsg[] = array($tsuite['name'],$missingCfMsg[$value['name']]); - } - } - $tsuiteMgr->cfield_mgr->design_values_to_db($cf2insert,$tsuite['id'],null,'simple'); - return $resultMsg; -} - -/** - * - */ -function getReqSpecSet(&$dbHandler,$tproject_id) -{ - $debugMsg = __FUNCTION__; - - $tables = tlObjectWithDB::getDBTables(array('req_specs','nodes_hierarchy','requirements')); - - // get always Latest Revision Req. Spec Title - $sql = "/* $debugMsg */ " . - " SELECT RSPEC.id, NHRSPEC.name AS title, RSPEC.doc_id AS rspec_doc_id, REQ.req_doc_id " . - " FROM {$tables['req_specs']} RSPEC " . - " JOIN {$tables['nodes_hierarchy']} NHRSPEC ON NHRSPEC.id = RSPEC.id " . - " JOIN {$tables['requirements']} REQ ON REQ.srs_id = RSPEC.id " . - " WHERE RSPEC.testproject_id = " . intval($tproject_id) . - " ORDER BY RSPEC.id,title"; - - $rs = $dbHandler->fetchRowsIntoMap($sql,'req_doc_id'); - - return $rs; -} \ No newline at end of file diff --git a/lib/testcases/tcPrint.php b/lib/testcases/tcPrint.php index ff9f989a09..b2793ab6f5 100644 --- a/lib/testcases/tcPrint.php +++ b/lib/testcases/tcPrint.php @@ -1,101 +1,123 @@ -get_node_hierarchy_info($args->tcase_id); -$node['tcversion_id'] = $args->tcversion_id; -$gui = initializeGui($args,$node); - - -// Struture defined in printDocument.php -$printingOptions = array('toc' => 0,'body' => 1,'summary' => 1, - 'header' => 0,'headerNumbering' => 0, - 'passfail' => 0, 'author' => 1, 'notes' => 0, 'requirement' => 1, - 'keyword' => 1, 'cfields' => 1, 'displayVersion' => 1, - 'displayDates' => 1, - 'docType' => SINGLE_TESTCASE, 'importance' => 1,'platform' => 1); - -$level = 0; -$tplanID = 0; -$prefix = null; -$text2print = ''; -$text2print .= renderHTMLHeader($gui->page_title,$_SESSION['basehref'], - SINGLE_TESTCASE,array('gui/javascript/testlink_library.js')); - -$env = new stdClass(); -$env->base_href = $_SESSION['basehref']; -$env->reportType = $printingOptions['docType']; - -$text2print .= renderTestCaseForPrinting($db,$node,$printingOptions,$env, - array('level' => $level,'tplan_id' => $tplanID, - 'tproject_id' => $args->tproject_id,'prefix' => $prefix),$level); - -echo $text2print; - -/* - function: init_args - - args: - - returns: - -*/ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $args->tcase_id = intval(isset($_REQUEST['testcase_id']) ? intval($_REQUEST['testcase_id']) : 0); - $args->tcversion_id = intval(isset($_REQUEST['tcversion_id']) ? intval($_REQUEST['tcversion_id']) : 0); - $args->tproject_id = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - - $args->tproject_name = $_SESSION['testprojectName']; - $args->goback_url=isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; - - - $ofd = array('HTML' => lang_get('format_html'),'ODT' => lang_get('format_odt'), - 'MSWORD' => lang_get('format_msword')); - $args->outputFormat = isset($_REQUEST['outputFormat']) ? $_REQUEST['outputFormat'] : null; - $args->outputFormat = isset($ofd[$args->outputFormat]) ? $ofd[$args->outputFormat] : null; - - $args->outputFormatDomain = array('NONE' => '') + $ofd; - return $args; -} - -/** - * - */ -function initializeGui(&$argsObj,&$node) -{ - $guiObj = new stdClass(); - $guiObj->outputFormatDomain = $argsObj->outputFormatDomain; - $guiObj->object_name=''; - $guiObj->goback_url = !is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; - $guiObj->object_name = $node['name']; - $guiObj->page_title = sprintf(lang_get('print_testcase'),$node['name']); - $guiObj->tproject_name=$argsObj->tproject_name; - $guiObj->tproject_id=$argsObj->tproject_id; - $guiObj->tcase_id=$argsObj->tcase_id; - $guiObj->tcversion_id=$argsObj->tcversion_id; - - return $guiObj; +get_node_hierarchy_info($args->tcase_id); +$node['tcversion_id'] = $args->tcversion_id; +$gui = initializeGui($args, $node); + +// Struture defined in printDocument.php +$printingOptions = array( + 'toc' => 0, + 'body' => 1, + 'summary' => 1, + 'header' => 0, + 'headerNumbering' => 0, + 'passfail' => 0, + 'author' => 1, + 'notes' => 0, + 'requirement' => 1, + 'keyword' => 1, + 'cfields' => 1, + 'displayVersion' => 1, + 'displayDates' => 1, + 'docType' => SINGLE_TESTCASE, + 'importance' => 1, + 'platform' => 1 +); + +$level = 0; +$tplanID = 0; +$prefix = null; +$text2print = ''; +$text2print .= renderHTMLHeader($gui->page_title, $_SESSION['basehref'], + SINGLE_TESTCASE, array( + 'gui/javascript/testlink_library.js' + )); + +$env = new stdClass(); +$env->base_href = $_SESSION['basehref']; +$env->reportType = $printingOptions['docType']; + +$text2print .= renderTestCaseForPrinting($db, $node, $printingOptions, $env, + array( + 'level' => $level, + 'tplan_id' => $tplanID, + 'tproject_id' => $args->tproject_id, + 'prefix' => $prefix + ), $level); + +echo $text2print; + +/* + * function: init_args + * + * args: + * + * returns: + * + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $args = new stdClass(); + $args->tcase_id = intval( + isset($_REQUEST['testcase_id']) ? intval($_REQUEST['testcase_id']) : 0); + $args->tcversion_id = intval( + isset($_REQUEST['tcversion_id']) ? intval($_REQUEST['tcversion_id']) : 0); + $args->tproject_id = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + + $args->tproject_name = $_SESSION['testprojectName']; + $args->goback_url = isset($_REQUEST['goback_url']) ? $_REQUEST['goback_url'] : null; + + $ofd = array( + 'HTML' => lang_get('format_html'), + 'ODT' => lang_get('format_odt'), + 'MSWORD' => lang_get('format_msword') + ); + $args->outputFormat = isset($_REQUEST['outputFormat']) ? $_REQUEST['outputFormat'] : null; + $args->outputFormat = isset($ofd[$args->outputFormat]) ? $ofd[$args->outputFormat] : null; + + $args->outputFormatDomain = array( + 'NONE' => '' + ) + $ofd; + return $args; +} + +/** + */ +function initializeGui(&$argsObj, &$node) +{ + $guiObj = new stdClass(); + $guiObj->outputFormatDomain = $argsObj->outputFormatDomain; + $guiObj->object_name = ''; + $guiObj->goback_url = ! is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; + $guiObj->object_name = $node['name']; + $guiObj->page_title = sprintf(lang_get('print_testcase'), $node['name']); + $guiObj->tproject_name = $argsObj->tproject_name; + $guiObj->tproject_id = $argsObj->tproject_id; + $guiObj->tcase_id = $argsObj->tcase_id; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + return $guiObj; } diff --git a/lib/testcases/tcSearch.php b/lib/testcases/tcSearch.php index 8b7817d92c..d69485a068 100644 --- a/lib/testcases/tcSearch.php +++ b/lib/testcases/tcSearch.php @@ -1,526 +1,615 @@ -getTcSearchSkeleton($args); -$gui = (object)array_merge((array)$ga,(array)$gx); - -initSearch($gui,$args,$tproject_mgr); - -$map = null; -$emptyTestProject = true; - -if ($args->tprojectID && $args->doAction == 'doSearch') { - $tables = tlObjectWithDB::getDBTables(array('cfield_design_values','nodes_hierarchy', - 'requirements','req_coverage','tcsteps', - 'testcase_keywords','tcversions','users')); - - $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tprojectID); - $gui->tcasePrefix .= $tcase_cfg->glue_character; - - $from = array('by_keyword_id' => ' ', 'by_custom_field' => ' ', 'by_requirement_doc_id' => '', 'users' => ''); - $tcaseID = null; - $emptyTestProject = false; - - if($args->targetTestCase != "" && strcmp($args->targetTestCase,$gui->tcasePrefix) != 0) { - if (strpos($args->targetTestCase,$tcase_cfg->glue_character) === false) { - $args->targetTestCase = $gui->tcasePrefix . $args->targetTestCase; - } - - $tcaseID = $tcase_mgr->getInternalID($args->targetTestCase); - $filter['by_tc_id'] = " AND NH_TCV.parent_id = " . intval($tcaseID); - } - else { - $tproject_mgr->get_all_testcases_id($args->tprojectID,$a_tcid); - - if(!is_null($a_tcid)) { - $filter['by_tc_id'] = " AND NH_TCV.parent_id IN (" . implode(",",$a_tcid) . ") "; - } - else { - // Force Nothing extracted, because test project - // has no test case defined - $emptyTestProject = true; - $filter['by_tc_id'] = " AND 1 = 0 "; - } - } - - if($args->version) { - $filter['by_version'] = " AND TCV.version = {$args->version} "; - } - - if($args->keyword_id) { - $from['by_keyword_id'] = " JOIN {$tables['testcase_keywords']} KW ON KW.testcase_id = NH_TC.id "; - $filter['by_keyword_id'] = " AND KW.keyword_id = " . $args->keyword_id; - } - - - $useOr = false; - $filterSpecial = null; - $feOp = " AND "; - $filterSpecial['tricky'] = " 1=1 "; - if($args->jolly != "") { - // $filterSpecial['trick'] = " 1=1 "; - $useOr = true; - $feOp = " OR "; - $filterSpecial['tricky'] = " 1=0 "; - $args->steps = $args->expected_results = $args->jolly; - } - - if($args->steps != "") { - $args->steps = $db->prepare_string($args->steps); - $filterSpecial['by_steps'] = $feOp . " TCSTEPS.actions like '%{$args->steps}%' "; - } - - if($args->expected_results != "") { - $args->expected_results = $db->prepare_string($args->expected_results); - $filterSpecial['by_expected_results'] = $feOp . " TCSTEPS.expected_results like '%{$args->expected_results}%' "; - } - - $k2w = array('name' => 'NH_TC', 'summary' => 'TCV', 'preconditions' => 'TCV'); - $jollyEscaped = $db->prepare_string($args->jolly); - foreach($k2w as $kf => $alias) { - if($args->$kf != "" || $args->jolly != '') { - if( $args->jolly == '' ) { - $args->$kf = $db->prepare_string($args->$kf); - } - $filterSpecial[$kf] = " {$feOp} {$alias}.{$kf} like "; - $filterSpecial[$kf] .= ($args->jolly == '') ? " '%{$args->$kf}%' " : " '%{$jollyEscaped}%' "; - } - } - - $otherFilters = ''; - if(!is_null($filterSpecial)) { - $otherFilters = " AND (" . implode("",$filterSpecial) . ")"; - } - - - if($args->custom_field_id > 0) { - - // Need to understand custom type to fomat the value - - $args->custom_field_id = $db->prepare_string($args->custom_field_id); - - $cf_def = $gui->design_cf[$args->custom_field_id]; - $from['by_custom_field']= " JOIN {$tables['cfield_design_values']} CFD " . - " ON CFD.node_id=NH_TCV.id "; - - $filter['by_custom_field'] = " AND CFD.field_id={$args->custom_field_id} "; - - switch($gui->cf_types[$cf_def['type']]) { - case 'date': - $args->custom_field_value = $tproject_mgr->cfield_mgr->cfdate2mktime($args->custom_field_value); - - $filter['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; - break; - - case 'datetime': - $args->custom_field_value = $tproject_mgr->cfield_mgr->cfdatetime2mktime($args->custom_field_value); - - $filter['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; - break; - - default: - $args->custom_field_value = $db->prepare_string($args->custom_field_value); - $filter['by_custom_field'] .= " AND CFD.value like '%{$args->custom_field_value}%' "; - break; - - } - } - - if($args->requirement_doc_id != "") { - $args->requirement_doc_id = $db->prepare_string($args->requirement_doc_id); - $from['by_requirement_doc_id'] = " JOIN {$tables['req_coverage']} RC" . - " ON RC.testcase_id = NH_TC.id " . - " JOIN {$tables['requirements']} REQ " . - " ON REQ.id=RC.req_id " ; - $filter['by_requirement_doc_id'] = " AND REQ.req_doc_id like '%{$args->requirement_doc_id}%' "; - } - - if( $args->importance > 0) - { - $filter['importance'] = " AND TCV.importance = {$args->importance} "; - } - - if( $args->status > 0) - { - $filter['status'] = " AND TCV.status = {$args->status} "; - } - - - $args->created_by = trim($args->created_by); - $from['users'] = ''; - if( $args->created_by != '' ) - { - $from['users'] .= " JOIN {$tables['users']} AUTHOR ON AUTHOR.id = TCV.author_id "; - $filter['author'] = " AND ( AUTHOR.login LIKE '%{$args->created_by}%' OR " . - " AUTHOR.first LIKE '%{$args->created_by}%' OR " . - " AUTHOR.last LIKE '%{$args->created_by}%') "; - } - - $args->edited_by = trim($args->edited_by); - if( $args->edited_by != '' ) - { - $from['users'] .= " JOIN {$tables['users']} UPDATER ON UPDATER.id = TCV.updater_id "; - $filter['modifier'] = " AND ( UPDATER.login LIKE '%{$args->edited_by}%' OR " . - " UPDATER.first LIKE '%{$args->edited_by}%' OR " . - " UPDATER.last LIKE '%{$args->edited_by}%') "; - } - - $sqlFields = " SELECT NH_TC.id AS testcase_id,NH_TC.name,TCV.id AS tcversion_id," . - " TCV.summary, TCV.version, TCV.tc_external_id "; - - // Count Test Cases NOT Test Case Versions - // ATTENTION: - // Keywords are stored AT TEST CASE LEVEL, not test case version. - $sqlCount = "SELECT COUNT(DISTINCT(NH_TC.id)) "; - - // search fails if test case has 0 steps - Added LEFT OUTER - $sqlPart2 = " FROM {$tables['nodes_hierarchy']} NH_TC " . - " JOIN {$tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = NH_TC.id " . - " JOIN {$tables['tcversions']} TCV ON NH_TCV.id = TCV.id " . - " LEFT OUTER JOIN {$tables['nodes_hierarchy']} NH_TCSTEPS ON NH_TCSTEPS.parent_id = NH_TCV.id " . - " LEFT OUTER JOIN {$tables['tcsteps']} TCSTEPS ON NH_TCSTEPS.id = TCSTEPS.id " . - " {$from['by_keyword_id']} {$from['by_custom_field']} {$from['by_requirement_doc_id']} " . - " {$from['users']} " . - " WHERE 1=1 "; - - - // if user fill in test case [external] id filter, and we were not able to get tcaseID, do any query is useless - $applyFilters = true; - if( !is_null($filter) && isset($filter['by_tc_id']) && !is_null($tcaseID) && ($tcaseID <= 0) ) - { - // get the right feedback message - $applyFilters = false; - $gui->warning_msg = $tcaseID == 0 ? lang_get('testcase_does_not_exists') : lang_get('prefix_does_not_exists'); - } - - if( $applyFilters ) - { - if ($filter) - { - $sqlPart2 .= implode("",$filter); - } - - $sqlPart2 .= $otherFilters; - - - // Count results - $sql = $sqlCount . $sqlPart2; - - $gui->row_qty = $db->fetchOneValue($sql); - if ($gui->row_qty) - { - if ($gui->row_qty <= $tcase_cfg->search->max_qty_for_display) - { - $sql = $sqlFields . $sqlPart2; - $map = $db->fetchRowsIntoMap($sql,'testcase_id'); - } - else - { - $gui->warning_msg = lang_get('too_wide_search_criteria'); - } - } - } +getTcSearchSkeleton($args); +$gui = (object) array_merge((array) $ga, (array) $gx); + +initSearch($gui, $args, $tproject_mgr); + +$map = null; +$emptyTestProject = true; + +if ($args->tprojectID && $args->doAction == 'doSearch') { + $tables = tlObjectWithDB::getDBTables( + array( + 'cfield_design_values', + 'nodes_hierarchy', + 'requirements', + 'req_coverage', + 'tcsteps', + 'testcase_keywords', + 'tcversions', + 'users' + )); + + $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($args->tprojectID); + $gui->tcasePrefix .= $tcase_cfg->glue_character; + + $from = array( + 'by_keyword_id' => ' ', + 'by_custom_field' => ' ', + 'by_requirement_doc_id' => '', + 'users' => '' + ); + $tcaseID = null; + $emptyTestProject = false; + + if ($args->targetTestCase != "" && + strcmp($args->targetTestCase, $gui->tcasePrefix) != 0) { + if (strpos($args->targetTestCase, $tcase_cfg->glue_character) === false) { + $args->targetTestCase = $gui->tcasePrefix . $args->targetTestCase; + } + + $tcaseID = $tcaseMgr->getInternalID($args->targetTestCase); + $filter['by_tc_id'] = " AND NH_TCV.parent_id = " . intval($tcaseID); + } else { + $tproject_mgr->get_all_testcases_id($args->tprojectID, $a_tcid); + + if (! is_null($a_tcid)) { + $filter['by_tc_id'] = " AND NH_TCV.parent_id IN (" . + implode(",", $a_tcid) . ") "; + } else { + // Force Nothing extracted, because test project + // has no test case defined + $emptyTestProject = true; + $filter['by_tc_id'] = " AND 1 = 0 "; + } + } + + if ($args->version) { + $filter['by_version'] = " AND TCV.version = {$args->version} "; + } + + if ($args->keyword_id) { + $from['by_keyword_id'] = " JOIN {$tables['testcase_keywords']} KW ON KW.testcase_id = NH_TC.id "; + $filter['by_keyword_id'] = " AND KW.keyword_id = " . $args->keyword_id; + } + + $useOr = false; + $filterSpecial = null; + $feOp = " AND "; + $filterSpecial['tricky'] = " 1=1 "; + if ($args->jolly != "") { + $useOr = true; + $feOp = " OR "; + $filterSpecial['tricky'] = " 1=0 "; + $args->steps = $args->expected_results = $args->jolly; + } + + if ($args->steps != "") { + $args->steps = $db->prepare_string($args->steps); + $filterSpecial['by_steps'] = $feOp . + " TCSTEPS.actions like '%{$args->steps}%' "; + } + + if ($args->expected_results != "") { + $args->expected_results = $db->prepare_string($args->expected_results); + $filterSpecial['by_expected_results'] = $feOp . + " TCSTEPS.expected_results like '%{$args->expected_results}%' "; + } + + $k2w = array( + 'name' => 'NH_TC', + 'summary' => 'TCV', + 'preconditions' => 'TCV' + ); + $jollyEscaped = $db->prepare_string($args->jolly); + foreach ($k2w as $kf => $alias) { + if ($args->$kf != "" || $args->jolly != '') { + if ($args->jolly == '') { + $args->$kf = $db->prepare_string($args->$kf); + } + $filterSpecial[$kf] = " {$feOp} {$alias}.{$kf} like "; + $filterSpecial[$kf] .= ($args->jolly == '') ? " '%{$args->$kf}%' " : " '%{$jollyEscaped}%' "; + } + } + + $otherFilters = ''; + if (! is_null($filterSpecial)) { + $otherFilters = " AND (" . implode("", $filterSpecial) . ")"; + } + + if ($args->custom_field_id > 0) { + + // Need to understand custom type to fomat the value + + $args->custom_field_id = $db->prepare_string($args->custom_field_id); + + $cf_def = $gui->design_cf[$args->custom_field_id]; + $from['by_custom_field'] = " JOIN {$tables['cfield_design_values']} CFD " . + " ON CFD.node_id=NH_TCV.id "; + + $filter['by_custom_field'] = " AND CFD.field_id={$args->custom_field_id} "; + + switch ($gui->cf_types[$cf_def['type']]) { + case 'date': + $args->custom_field_value = $tproject_mgr->cfield_mgr->cfdate2mktime( + $args->custom_field_value); + + $filter['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; + break; + + case 'datetime': + $args->custom_field_value = $tproject_mgr->cfield_mgr->cfdatetime2mktime( + $args->custom_field_value); + + $filter['by_custom_field'] .= " AND CFD.value = {$args->custom_field_value}"; + break; + + default: + $args->custom_field_value = $db->prepare_string( + $args->custom_field_value); + $filter['by_custom_field'] .= " AND CFD.value like '%{$args->custom_field_value}%' "; + break; + } + } + + if ($args->requirement_doc_id != "") { + $args->requirement_doc_id = $db->prepare_string( + $args->requirement_doc_id); + $from['by_requirement_doc_id'] = " JOIN {$tables['req_coverage']} RC" . + " ON RC.testcase_id = NH_TC.id " . + " JOIN {$tables['requirements']} REQ " . " ON REQ.id=RC.req_id "; + $filter['by_requirement_doc_id'] = " AND REQ.req_doc_id like '%{$args->requirement_doc_id}%' "; + } + + if ($args->importance > 0) { + $filter['importance'] = " AND TCV.importance = {$args->importance} "; + } + + if ($args->status > 0) { + $filter['status'] = " AND TCV.status = {$args->status} "; + } + + $args->created_by = trim($args->created_by); + $from['users'] = ''; + if ($args->created_by != '') { + $from['users'] .= " JOIN {$tables['users']} AUTHOR ON AUTHOR.id = TCV.author_id "; + $filter['author'] = " AND ( AUTHOR.login LIKE '%{$args->created_by}%' OR " . + " AUTHOR.first LIKE '%{$args->created_by}%' OR " . + " AUTHOR.last LIKE '%{$args->created_by}%') "; + } + + $args->edited_by = trim($args->edited_by); + if ($args->edited_by != '') { + $from['users'] .= " JOIN {$tables['users']} UPDATER ON UPDATER.id = TCV.updater_id "; + $filter['modifier'] = " AND ( UPDATER.login LIKE '%{$args->edited_by}%' OR " . + " UPDATER.first LIKE '%{$args->edited_by}%' OR " . + " UPDATER.last LIKE '%{$args->edited_by}%') "; + } + + $sqlFields = " SELECT NH_TC.id AS testcase_id,NH_TC.name,TCV.id AS tcversion_id," . + " TCV.summary, TCV.version, TCV.tc_external_id "; + + // Count Test Cases NOT Test Case Versions + // ATTENTION: + // Keywords are stored AT TEST CASE LEVEL, not test case version. + $sqlCount = "SELECT COUNT(DISTINCT(NH_TC.id)) "; + + // search fails if test case has 0 steps - Added LEFT OUTER + $sqlPart2 = " FROM {$tables['nodes_hierarchy']} NH_TC " . + " JOIN {$tables['nodes_hierarchy']} NH_TCV ON NH_TCV.parent_id = NH_TC.id " . + " JOIN {$tables['tcversions']} TCV ON NH_TCV.id = TCV.id " . + " LEFT OUTER JOIN {$tables['nodes_hierarchy']} NH_TCSTEPS ON NH_TCSTEPS.parent_id = NH_TCV.id " . + " LEFT OUTER JOIN {$tables['tcsteps']} TCSTEPS ON NH_TCSTEPS.id = TCSTEPS.id " . + " {$from['by_keyword_id']} {$from['by_custom_field']} {$from['by_requirement_doc_id']} " . + " {$from['users']} " . " WHERE 1=1 "; + + // if user fill in test case [external] id filter, and we were not able to get tcaseID, do any query is useless + $applyFilters = true; + if (! is_null($filter) && isset($filter['by_tc_id']) && ! is_null($tcaseID) && + ($tcaseID <= 0)) { + // get the right feedback message + $applyFilters = false; + $gui->warning_msg = $tcaseID == 0 ? lang_get('testcase_does_not_exists') : lang_get( + 'prefix_does_not_exists'); + } + + if ($applyFilters) { + if ($filter) { + $sqlPart2 .= implode("", $filter); + } + + $sqlPart2 .= $otherFilters; + + // Count results + $sql = $sqlCount . $sqlPart2; + + $gui->row_qty = $db->fetchOneValue($sql); + if ($gui->row_qty) { + if ($gui->row_qty <= $tcase_cfg->search->max_qty_for_display) { + $sql = $sqlFields . $sqlPart2; + $map = $db->fetchRowsIntoMap($sql, 'testcase_id'); + } else { + $gui->warning_msg = lang_get('too_wide_search_criteria'); + } + } + } +} + +if ($gui->doSearch) { + $gui->pageTitle .= " - " . lang_get('match_count') . " : " . $gui->row_qty; +} + +if ($gui->row_qty > 0) { + if ($map) { + $tcaseMgr = new testcase($db); + $tcase_set = array_keys($map); + $options = array( + 'output_format' => 'path_as_string' + ); + $gui->path_info = $tproject_mgr->tree_manager->get_full_path_verbose( + $tcase_set, $options); + $gui->resultSet = $map; + } +} elseif ($emptyTestProject) { + $gui->warning_msg = lang_get('empty_testproject'); +} else { + $gui->warning_msg = lang_get('no_records_found'); +} + +$img = $smarty->getImages(); +$table = buildExtTable($gui, $charset, $img['edit_icon'], $img['history_small']); +if (! is_null($table)) { + $gui->tableSet[] = $table; +} + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $tpl); + +/** + */ +function buildExtTable($gui, $charset, $edit_icon, $history_icon) +{ + $table = null; + $designCfg = getWebEditorCfg('design'); + $designType = $designCfg['type']; + + if (null != $gui->resultSet && count($gui->resultSet) > 0) { + $labels = array( + 'test_suite' => lang_get('test_suite'), + 'test_case' => lang_get('test_case') + ); + $columns = array(); + + $columns[] = array( + 'title_key' => 'test_suite' + ); + $columns[] = array( + 'title_key' => 'test_case', + 'type' => 'text' + ); + + $columns[] = array( + 'title_key' => 'summary' + ); + + // Extract the relevant data and build a matrix + $matrixData = array(); + + $titleSeperator = config_get('gui_title_separator_1'); + + foreach ($gui->resultSet as $result) { + $rowData = array(); + $rowData[] = htmlentities($gui->path_info[$result['testcase_id']], + ENT_QUOTES, $charset); + + // build test case link + $history_link = "" . + " "; + $edit_link = "" . + " "; + $tcaseName = htmlentities($gui->tcasePrefix, ENT_QUOTES, $charset) . + $result['tc_external_id'] . " [v" . $result['version'] . "]" . + $titleSeperator . + htmlentities($result['name'], ENT_QUOTES, $charset); + + $rowData[] = $history_link . $edit_link . $tcaseName; + $rowData[] = ($designType == 'none' ? nl2br($result['summary']) : $result['summary']); + + $matrixData[] = $rowData; + } + + $table = new tlExtTable($columns, $matrixData, + 'tl_table_test_case_search'); + + $table->setGroupByColumnName($labels['test_suite']); + $table->setSortByColumnName($labels['test_case']); + $table->sortDirection = 'DESC'; + + $table->showToolbar = true; + $table->allowMultiSort = false; + $table->toolbarRefreshButton = false; + $table->toolbarShowAllColumnsButton = false; + + $table->addCustomBehaviour('text', array( + 'render' => 'columnWrap' + )); + $table->storeTableState = false; + } + return $table; +} + +/** + */ +function initArgs(&$tprojectMgr) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 10 + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "status" => array( + tlInputParameter::INT_N + ), + "keyword_id" => array( + tlInputParameter::INT_N + ), + "version" => array( + tlInputParameter::INT_N, + 999 + ), + "custom_field_id" => array( + tlInputParameter::INT_N + ), + "name" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "created_by" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "edited_by" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "summary" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "steps" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "expected_results" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "custom_field_value" => array( + tlInputParameter::STRING_N, + 0, + 20 + ), + "targetTestCase" => array( + tlInputParameter::STRING_N, + 0, + 30 + ), + "preconditions" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "requirement_doc_id" => array( + tlInputParameter::STRING_N, + 0, + 32 + ), + "importance" => array( + tlInputParameter::INT_N + ), + "creation_date_from" => array( + tlInputParameter::STRING_N + ), + "creation_date_to" => array( + tlInputParameter::STRING_N + ), + "modification_date_from" => array( + tlInputParameter::STRING_N + ), + "modification_date_to" => array( + tlInputParameter::STRING_N + ), + "jolly" => array( + tlInputParameter::STRING_N + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + // sanitize targetTestCase against XSS + // remove all blanks + // remove some html entities + // remove () + $tt = array( + ' ', + '<', + '>', + '(', + ')' + ); + $args->targetTestCase = str_replace($tt, '', $args->targetTestCase); + + $args->userID = intval(isset($_SESSION['userID']) ? $_SESSION['userID'] : 0); + + if (is_null($args->tproject_id) || intval($args->tproject_id) <= 0) { + $args->tprojectID = intval( + isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); + $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; + } else { + $args->tprojectID = intval($args->tproject_id); + $info = $tprojectMgr->get_by_id($args->tprojectID); + $args->tprojectName = $info['name']; + } + + if ($args->tprojectID <= 0) { + throw new Exception( + "Error Processing Request - Invalid Test project id " . __FILE__); + } + + // convert "creation date from" to iso format for database usage + $k2w = array( + 'creation_date_from' => '', + 'creation_date_to' => " 23:59:59", + 'modification_date_from' => '', + 'modification_date_to' => " 23:59:59" + ); + + $k2f = array( + 'creation_date_from' => ' creation_ts >= ', + 'creation_date_to' => 'creation_ts <= ', + 'modification_date_from' => ' modification_ts >= ', + 'modification_date_to' => ' modification_ts <= ' + ); + + $dateFormat = config_get('date_format'); + $filter = null; + foreach ($k2w as $key => $value) { + if (isset($args->$key) && $args->$key != '') { + $da = split_localized_date($args->$key, $dateFormat); + if ($da != null) { + $args->$key = $da['year'] . "-" . $da['month'] . "-" . $da['day'] . + $value; // set date in iso format + $filter[$key] = " AND TCV.{$k2f[$key]} '{$args->$key}' "; + } + } + } + + return array( + $args, + $filter + ); +} + +/** + */ +function initializeGui(&$argsObj, &$tprojectMgr) +{ + $gui = new stdClass(); + + $gui->pageTitle = lang_get('caption_search_form'); + $gui->warning_msg = ''; + $gui->path_info = null; + $gui->resultSet = null; + $gui->tableSet = null; + $gui->bodyOnLoad = null; + $gui->bodyOnUnload = null; + $gui->refresh_tree = false; + $gui->hilite_testcase_name = false; + $gui->show_match_count = false; + $gui->row_qty = 0; + $gui->doSearch = ($argsObj->doAction == 'doSearch'); + $gui->tproject_id = intval($argsObj->tprojectID); + + $gui->mainCaption = lang_get('testproject') . " " . $argsObj->tprojectName; + + $gui->creation_date_from = null; + $gui->creation_date_to = null; + $gui->modification_date_from = null; + $gui->modification_date_to = null; + $gui->search_important_notice = sprintf(lang_get('search_important_notice'), + $argsObj->tprojectName); + + // need to set values that where used on latest search (if any was done) + // $gui->importance = config_get('testcase_importance_default'); + return $gui; +} + +/** + */ +function initSearch(&$gui, &$argsObj, &$tprojectMgr) +{ + $gui->design_cf = $tprojectMgr->cfield_mgr->get_linked_cfields_at_design( + $argsObj->tprojectID, cfield_mgr::ENABLED, null, 'testcase'); + + $gui->cf_types = $tprojectMgr->cfield_mgr->custom_field_types; + $gui->filter_by['design_scope_custom_fields'] = ! is_null($gui->design_cf); + + $gui->keywords = $tprojectMgr->getKeywords($argsObj->tprojectID); + $gui->filter_by['keyword'] = ! is_null($gui->keywords); + + $oo = $tprojectMgr->getOptions($argsObj->tprojectID); + $gui->filter_by['requirement_doc_id'] = $oo->requirementsEnabled; + + $gui->importance = intval($argsObj->importance); + $gui->status = intval($argsObj->status); + $gui->tcversion = (is_null($argsObj->version) || $argsObj->version == '') ? '' : intval( + $argsObj->version); + + $gui->tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tprojectID) . + config_get('testcase_cfg')->glue_character; + + $gui->targetTestCase = (is_null($argsObj->targetTestCase) || + $argsObj->targetTestCase == '') ? $gui->tcasePrefix : $argsObj->targetTestCase; + + $txtin = array( + "created_by", + "edited_by", + "jolly" + ); + $jollyKilled = array( + "summary", + "steps", + "expected_results", + "preconditions", + "name" + ); + $txtin = array_merge($txtin, $jollyKilled); + + foreach ($txtin as $key) { + $gui->$key = $argsObj->$key; + } + + if ($argsObj->jolly != '') { + foreach ($jollyKilled as $key) { + $gui->$key = ''; + } + } } - -if($gui->doSearch) -{ - $gui->pageTitle .= " - " . lang_get('match_count') . " : " . $gui->row_qty; -} - -if($gui->row_qty > 0) -{ - if ($map) - { - $tcase_mgr = new testcase($db); - $tcase_set = array_keys($map); - $options = array('output_format' => 'path_as_string'); - $gui->path_info = $tproject_mgr->tree_manager->get_full_path_verbose($tcase_set, $options); - $gui->resultSet = $map; - } -} -else if ($emptyTestProject) -{ - $gui->warning_msg = lang_get('empty_testproject'); -} -else -{ - $gui->warning_msg = lang_get('no_records_found'); -} - -$img = $smarty->getImages(); -$table = buildExtTable($gui, $charset, $img['edit_icon'], $img['history_small']); -if (!is_null($table)) -{ - $gui->tableSet[] = $table; -} - - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $tpl); - -/** - * - * - */ -function buildExtTable($gui, $charset, $edit_icon, $history_icon) { - $table = null; - $designCfg = getWebEditorCfg('design'); - $designType = $designCfg['type']; - - if(null != $gui->resultSet && count($gui->resultSet) > 0) { - $labels = array('test_suite' => lang_get('test_suite'), 'test_case' => lang_get('test_case')); - $columns = array(); - - $columns[] = array('title_key' => 'test_suite'); - $columns[] = array('title_key' => 'test_case', 'type' => 'text'); - - $columns[] = array('title_key' => 'summary'); - - // Extract the relevant data and build a matrix - $matrixData = array(); - - $titleSeperator = config_get('gui_title_separator_1'); - - foreach($gui->resultSet as $result) - { - $rowData = array(); - $rowData[] = htmlentities($gui->path_info[$result['testcase_id']], ENT_QUOTES, $charset); - - // build test case link - $history_link = "" . - " "; - $edit_link = "" . - " "; - $tcaseName = htmlentities($gui->tcasePrefix, ENT_QUOTES, $charset) . $result['tc_external_id'] . - " [v" . $result['version'] . "]" . $titleSeperator . - htmlentities($result['name'], ENT_QUOTES, $charset); - - $rowData[] = $history_link . $edit_link . $tcaseName; - $rowData[] = ($designType == 'none' ? nl2br($result['summary']) : $result['summary']); - - $matrixData[] = $rowData; - } - - $table = new tlExtTable($columns, $matrixData, 'tl_table_test_case_search'); - - $table->setGroupByColumnName($labels['test_suite']); - $table->setSortByColumnName($labels['test_case']); - $table->sortDirection = 'DESC'; - - $table->showToolbar = true; - $table->allowMultiSort = false; - $table->toolbarRefreshButton = false; - $table->toolbarShowAllColumnsButton = false; - - $table->addCustomBehaviour('text', array('render' => 'columnWrap')); - $table->storeTableState = false; - } - return($table); -} - - -/** - * - */ -function init_args(&$tprojectMgr) -{ - $_REQUEST=strings_stripSlashes($_REQUEST); - - $args = new stdClass(); - $iParams = array("doAction" => array(tlInputParameter::STRING_N,0,10), - "tproject_id" => array(tlInputParameter::INT_N), - "status" => array(tlInputParameter::INT_N), - "keyword_id" => array(tlInputParameter::INT_N), - "version" => array(tlInputParameter::INT_N,999), - "custom_field_id" => array(tlInputParameter::INT_N), - "name" => array(tlInputParameter::STRING_N,0,50), - "created_by" => array(tlInputParameter::STRING_N,0,50), - "edited_by" => array(tlInputParameter::STRING_N,0,50), - "summary" => array(tlInputParameter::STRING_N,0,50), - "steps" => array(tlInputParameter::STRING_N,0,50), - "expected_results" => array(tlInputParameter::STRING_N,0,50), - "custom_field_value" => array(tlInputParameter::STRING_N,0,20), - "targetTestCase" => array(tlInputParameter::STRING_N,0,30), - "preconditions" => array(tlInputParameter::STRING_N,0,50), - "requirement_doc_id" => array(tlInputParameter::STRING_N,0,32), - "importance" => array(tlInputParameter::INT_N), - "creation_date_from" => array(tlInputParameter::STRING_N), - "creation_date_to" => array(tlInputParameter::STRING_N), - "modification_date_from" => array(tlInputParameter::STRING_N), - "modification_date_to" => array(tlInputParameter::STRING_N), - "jolly" => array(tlInputParameter::STRING_N)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - // sanitize targetTestCase against XSS - // remove all blanks - // remove some html entities - // remove () - $tt = array(' ','<','>','(',')'); - $args->targetTestCase = str_replace($tt,'',$args->targetTestCase); - - $args->userID = intval(isset($_SESSION['userID']) ? $_SESSION['userID'] : 0); - - if(is_null($args->tproject_id) || intval($args->tproject_id) <= 0) - { - $args->tprojectID = intval(isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0); - $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; - } - else - { - $args->tprojectID = intval($args->tproject_id); - $info = $tprojectMgr->get_by_id($args->tprojectID); - $args->tprojectName = $info['name']; - } - - if($args->tprojectID <= 0) - { - throw new Exception("Error Processing Request - Invalid Test project id " . __FILE__); - } - - // convert "creation date from" to iso format for database usage - $k2w = array('creation_date_from' => '','creation_date_to' => " 23:59:59", - 'modification_date_from' => '', 'modification_date_to' => " 23:59:59"); - - $k2f = array('creation_date_from' => ' creation_ts >= ', - 'creation_date_to' => 'creation_ts <= ', - 'modification_date_from' => ' modification_ts >= ', - 'modification_date_to' => ' modification_ts <= '); - - - $dateFormat = config_get('date_format'); - $filter = null; - foreach($k2w as $key => $value) - { - if (isset($args->$key) && $args->$key != '') - { - $da = split_localized_date($args->$key, $dateFormat); - if ($da != null) - { - $args->$key = $da['year'] . "-" . $da['month'] . "-" . $da['day'] . $value; // set date in iso format - $filter[$key] = " AND TCV.{$k2f[$key]} '{$args->$key}' "; - } - } - } - - return array($args,$filter); -} - - -/** - * - * - */ -function initializeGui(&$argsObj,&$tprojectMgr) -{ - $gui = new stdClass(); - - $gui->pageTitle = lang_get('caption_search_form'); - $gui->warning_msg = ''; - $gui->path_info = null; - $gui->resultSet = null; - $gui->tableSet = null; - $gui->bodyOnLoad = null; - $gui->bodyOnUnload = null; - $gui->refresh_tree = false; - $gui->hilite_testcase_name = false; - $gui->show_match_count = false; - $gui->row_qty = 0; - $gui->doSearch = ($argsObj->doAction == 'doSearch'); - $gui->tproject_id = intval($argsObj->tprojectID); - - // ---------------------------------------------------- - $gui->mainCaption = lang_get('testproject') . " " . $argsObj->tprojectName; - - $gui->creation_date_from = null; - $gui->creation_date_to = null; - $gui->modification_date_from = null; - $gui->modification_date_to = null; - $gui->search_important_notice = sprintf(lang_get('search_important_notice'),$argsObj->tprojectName); - - // need to set values that where used on latest search (if any was done) - // $gui->importance = config_get('testcase_importance_default'); - - return $gui; -} - -/** - * - */ -function initSearch(&$gui,&$argsObj,&$tprojectMgr) -{ - $gui->design_cf = $tprojectMgr->cfield_mgr->get_linked_cfields_at_design($argsObj->tprojectID, - cfield_mgr::ENABLED,null,'testcase'); - - $gui->cf_types = $tprojectMgr->cfield_mgr->custom_field_types; - $gui->filter_by['design_scope_custom_fields'] = !is_null($gui->design_cf); - - $gui->keywords = $tprojectMgr->getKeywords($argsObj->tprojectID); - $gui->filter_by['keyword'] = !is_null($gui->keywords); - - $oo = $tprojectMgr->getOptions($argsObj->tprojectID); - $gui->filter_by['requirement_doc_id'] = $oo->requirementsEnabled; - - $gui->importance = intval($argsObj->importance); - $gui->status = intval($argsObj->status); - $gui->tcversion = (is_null($argsObj->version) || $argsObj->version == '') ? '' : intval($argsObj->version); - - $gui->tcasePrefix = $tprojectMgr->getTestCasePrefix($argsObj->tprojectID) . config_get('testcase_cfg')->glue_character; - - - $gui->targetTestCase = (is_null($argsObj->targetTestCase) || $argsObj->targetTestCase == '') ? - $gui->tcasePrefix : $argsObj->targetTestCase; - - - $txtin = array("created_by","edited_by","jolly"); - $jollyKilled = array("summary","steps","expected_results","preconditions","name"); - $txtin = array_merge($txtin, $jollyKilled); - - foreach($txtin as $key ) - { - $gui->$key = $argsObj->$key; - } - - if($argsObj->jolly != '') - { - foreach($jollyKilled as $key) - { - $gui->$key = ''; - } - } - -} \ No newline at end of file diff --git a/lib/testcases/tcSearchForm.php b/lib/testcases/tcSearchForm.php index a1e72e5cbb..8497f2b971 100644 --- a/lib/testcases/tcSearchForm.php +++ b/lib/testcases/tcSearchForm.php @@ -1,83 +1,89 @@ -assign('gui',$gui); -$smarty->display($templateCfg->template_dir . 'tcSearchForm.tpl'); -/** - * - * - */ -function init_args() -{ - $args = new stdClass(); - $args->tprojectID = isset($_SESSION['testprojectID']) ? intval($_SESSION['testprojectID']) : 0; - $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; - - if($args->tprojectID <= 0) - { - throw new Exception("Error Processing Request - Invalid Test project id " . __FILE__); - } - - return $args; +assign('gui', $gui); +$smarty->display($templateCfg->template_dir . 'tcSearchForm.tpl'); + +/** + */ +function initArgs() +{ + $args = new stdClass(); + $args->tprojectID = isset($_SESSION['testprojectID']) ? intval( + $_SESSION['testprojectID']) : 0; + $args->tprojectName = isset($_SESSION['testprojectName']) ? $_SESSION['testprojectName'] : 0; + + if ($args->tprojectID <= 0) { + throw new Exception( + "Error Processing Request - Invalid Test project id " . __FILE__); + } + + return $args; +} + +function initializeGui(&$dbHandler, &$argsObj) +{ + $tproject_mgr = new testproject($dbHandler); + + $gui = new stdClass(); + $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tprojectID) . + config_get('testcase_cfg')->glue_character; + $gui->mainCaption = lang_get('testproject') . " " . $argsObj->tprojectName; + $gui->importance = config_get('testcase_importance_default'); + $gui->creation_date_from = null; + $gui->creation_date_to = null; + $gui->modification_date_from = null; + $gui->modification_date_to = null; + $gui->search_important_notice = sprintf(lang_get('search_important_notice'), + $argsObj->tprojectName); + + $gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design( + $argsObj->tprojectID, cfield_mgr::ENABLED, null, 'testcase'); + + $gui->keywords = $tproject_mgr->getKeywords($argsObj->tprojectID); + + $gui->filter_by['design_scope_custom_fields'] = ! is_null($gui->design_cf); + $gui->filter_by['keyword'] = ! is_null($gui->keywords); + + $reqSpecSet = $tproject_mgr->genComboReqSpec($argsObj->tprojectID); + $gui->filter_by['requirement_doc_id'] = ! is_null($reqSpecSet); + + $gui->option_importance = array( + 0 => '', + HIGH => lang_get('high_importance'), + MEDIUM => lang_get('medium_importance'), + LOW => lang_get('low_importance') + ); + + $dummy = getConfigAndLabels('testCaseStatus', 'code'); + $gui->domainTCStatus = array( + 0 => '' + ) + $dummy['lbl']; + return $gui; } - -function initializeGui(&$dbHandler,&$argsObj) -{ - - $tproject_mgr = new testproject($dbHandler); - - $gui = new stdClass(); - $gui->tcasePrefix = $tproject_mgr->getTestCasePrefix($argsObj->tprojectID) . config_get('testcase_cfg')->glue_character; - $gui->mainCaption = lang_get('testproject') . " " . $argsObj->tprojectName; - $gui->importance = config_get('testcase_importance_default'); - $gui->creation_date_from = null; - $gui->creation_date_to = null; - $gui->modification_date_from = null; - $gui->modification_date_to = null; - $gui->search_important_notice = sprintf(lang_get('search_important_notice'),$argsObj->tprojectName); - - $gui->design_cf = $tproject_mgr->cfield_mgr->get_linked_cfields_at_design($argsObj->tprojectID,cfield_mgr::ENABLED,null,'testcase'); - - $gui->keywords = $tproject_mgr->getKeywords($argsObj->tprojectID); - - $gui->filter_by['design_scope_custom_fields'] = !is_null($gui->design_cf); - $gui->filter_by['keyword'] = !is_null($gui->keywords); - - $reqSpecSet = $tproject_mgr->genComboReqSpec($argsObj->tprojectID); - $gui->filter_by['requirement_doc_id'] = !is_null($reqSpecSet); - - $gui->option_importance = array(0 => '',HIGH => lang_get('high_importance'),MEDIUM => lang_get('medium_importance'), - LOW => lang_get('low_importance')); - - - $dummy = getConfigAndLabels('testCaseStatus','code'); - $gui->domainTCStatus = array(0 => '') + $dummy['lbl']; - return $gui; -} \ No newline at end of file diff --git a/lib/testcases/testcaseCommands.class.php b/lib/testcases/testcaseCommands.class.php index fc007308c1..6023ab56e9 100644 --- a/lib/testcases/testcaseCommands.class.php +++ b/lib/testcases/testcaseCommands.class.php @@ -1,1542 +1,1659 @@ -db = $db; - $this->tcaseMgr = new testcase($db); - $this->tcaseMgr->setTestProject($tproject_id); - - $this->tprojectMgr = &$this->tcaseMgr->tproject_mgr; - $this->tproject_id = $tproject_id; - - $this->execution_types = $this->tcaseMgr->get_execution_types(); - $this->grants = new stdClass(); - - $g2c = array('mgt_modify_tc','mgt_view_req','testplan_planning', - 'req_tcase_link_management','mgt_modify_req', - 'testproject_delete_executed_testcases', - 'testproject_edit_executed_testcases'); - foreach($g2c as $grant) { - $this->grants->$grant = $userObj->hasRight($db,$grant,$tproject_id); - } - - $this->grants->requirement_mgmt = $this->grants->mgt_modify_req || - $this->grants->req_tcase_link_management; - - } - - function setTemplateCfg($cfg) { - $this->templateCfg=$cfg; - } - - /** - * - * - */ - function initGuiBean(&$argsObj) { - $obj = new stdClass(); - $obj->action = ''; - $obj->attachments = null; - $obj->cleanUpWebEditor = false; - $obj->containerID = ''; - $obj->direct_link = null; - $obj->execution_types = $this->execution_types; - - - $obj->grants = $this->grants; - - $key = 'req_tcase_link_management'; - $obj->$key = $obj->grants->$key; - - $obj->has_been_executed = false; - $obj->initWebEditorFromTemplate = false; - - $obj->main_descr = ''; - $obj->name = ''; - $obj->path_info = null; - $obj->refreshTree = 0; - $obj->sqlResult = ''; - $obj->step_id = -1; - $obj->step_set = ''; - $obj->steps = ''; - - $dummy = testcase::getLayout(); - $obj->tableColspan = $dummy->tableToDisplayTestCaseSteps->colspan; - - $tck = array('tcase_id','tcversion_id','tplan_id'); - foreach ($tck as $pkey) { - $obj->$pkey = property_exists($argsObj,$pkey) ? $argsObj->$pkey : -1; - } - - $obj->viewerArgs = null; - - $p2check = 'goback_url'; - $obj->$p2check = ''; - if( property_exists($argsObj,$p2check) ) { - $obj->$p2check = !is_null($argsObj->$p2check) ? $argsObj->$p2check : ''; - } - - $p2check = 'show_mode'; - if( property_exists($argsObj,$p2check) ) { - $obj->$p2check = !is_null($argsObj->$p2check) ? $argsObj->$p2check : 'show'; - } - - // need to check where is used - $obj->loadOnCancelURL = "archiveData.php?edit=testcase&show_mode={$obj->show_mode}&id=%s&version_id=%s"; - - if( property_exists($obj, 'tplan_id') ) { - $obj->loadOnCancelURL .= "&tplan_id={$obj->tplan_id}"; - } - - if( property_exists($obj, 'show_mode') ) { - $obj->loadOnCancelURL .= "&show_mode={$obj->show_mode}"; - } - - $obj->codeTrackerEnabled = $this->tprojectMgr->isCodeTrackerEnabled($this->tproject_id); - - return $obj; - } - - /** - * initialize common test case information, useful when working on steps - * - */ - function initTestCaseBasicInfo(&$argsObj,&$guiObj,$opt=null) { - - $my['opt'] = array('accessByStepID' => true); - $my['opt'] = array_merge($my['opt'],(array)$opt); - - // Security - // https://cxsecurity.com/issue/WLB-2019110139 - if (intval($argsObj->tcase_id) == 0 && - intval($argsObj->tcversion_id) ==0) { - die("Error Processing Request:" . __METHOD__); - } - - $greenCard = array('tcase_id' => $argsObj->tcase_id, 'tcversion_id' => $argsObj->tcversion_id); - - if( $my['opt']['accessByStepID'] ) { - foreach($greenCard as $ky) { - // this logic need to be explained BETTER - if($ky == 0) { - $greenCard = $this->tcaseMgr->getIdCardByStepID($argsObj->step_id); - break; - } - } - } - - $gopt = array('output' => 'full_without_steps','renderGhost' => true, - 'renderImageInline' => true,'renderVariables' => true); - - $tcaseInfo = $this->tcaseMgr->get_by_id($greenCard['tcase_id'],$greenCard['tcversion_id'],null,$gopt); - - - $external = $this->tcaseMgr->getExternalID($greenCard['tcase_id'],$argsObj->testproject_id); - $tcaseInfo[0]['tc_external_id'] = $external[0]; - $guiObj->testcase = $tcaseInfo[0]; - - if(!isset($guiObj->testcase['ghost'])) { - $guiObj->testcase['ghost'] = null; - } - $guiObj->authorObj = tlUser::getByID($this->db,$guiObj->testcase['author_id']); - - $guiObj->updaterObj = null; - if( !is_null($guiObj->testcase['updater_id']) ) { - $guiObj->updaterObj = tlUser::getByID($this->db,$guiObj->testcase['updater_id']); - } - } - - - - - - /** - * - * - */ - function create(&$argsObj,&$otCfg) - { - $parentKeywords = array(); - $guiObj = $this->initGuiBean($argsObj); - $guiObj->initWebEditorFromTemplate = true; - - $guiObj->containerID = $argsObj->container_id; - if($argsObj->container_id > 0) { - $pnode_info = $this->tcaseMgr->tree_manager->get_node_hierarchy_info($argsObj->container_id); - $node_descr = array_flip($this->tcaseMgr->tree_manager->get_available_node_types()); - $guiObj->parent_info['name'] = $pnode_info['name']; - $guiObj->parent_info['description'] = lang_get($node_descr[$pnode_info['node_type_id']]); - - // get keywords - $tsuiteMgr = new testsuite($this->db); - $parentKeywords = $tsuiteMgr->getKeywords($argsObj->container_id); - } - $sep_1 = config_get('gui_title_separator_1'); - $sep_2 = config_get('gui_title_separator_2'); - $guiObj->main_descr = $guiObj->parent_info['description'] . $sep_1 . $guiObj->parent_info['name'] . - $sep_2 . lang_get('title_new_tc'); - - - $otCfg->to->map = array(); - keywords_opt_transf_cfg($otCfg,implode(',',array_keys((array)$parentKeywords))); - - $guiObj->tc = array('id' => 0, 'name' => '', 'importance' => config_get('testcase_importance_default'), - 'status' => null, 'estimated_exec_duration' => null, - 'execution_type' => TESTCASE_EXECUTION_TYPE_MANUAL); - - $guiObj->opt_cfg=$otCfg; - $templateCfg = templateConfiguration('tcNew'); - $guiObj->template=$templateCfg->default_template; - - - $cfPlaces = $this->tcaseMgr->buildCFLocationMap(); - foreach($cfPlaces as $locationKey => $locationFilter) { - $guiObj->cf[$locationKey] = - $this->tcaseMgr->html_table_of_custom_field_inputs(null,null,'design','',null,null, - $argsObj->testproject_id,$locationFilter, $_REQUEST); - } - - $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . - "lib/testcases/archiveData.php?id=" . intval($argsObj->container_id); - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->cancelActionJS .= "&tplan_id={$guiObj->tplan_id}"; - } - - if( property_exists($guiObj, 'show_mode') ) { - $guiObj->cancelActionJS .= "&show_mode={$guiObj->show_mode}"; - } - - - $guiObj->cancelActionJS .= - '&edit=testsuite&level=testsuite&containerType=testsuite' . "'"; - - return $guiObj; - } - - /** - * - * - */ - function doCreate(&$argsObj,&$otCfg,$oWebEditorKeys,$request) { - $guiObj = $this->create($argsObj,$otCfg,$oWebEditorKeys); - - // compute order - $new_order = config_get('treemenu_default_testcase_order'); - $co = $this->tcaseMgr->tree_manager->getBottomOrder($argsObj->container_id,array('node_type' => 'testcase')); - if( $co > 0){ - $new_order = $co+1; - } - - $options = array('check_duplicate_name' => config_get('check_names_for_duplicates'), - 'action_on_duplicate_name' => 'block', - 'status' => $argsObj->tc_status, - 'estimatedExecDuration' => $argsObj->estimated_execution_duration); - - $tcase = $this->tcaseMgr->create($argsObj->container_id,$argsObj->name,$argsObj->summary,$argsObj->preconditions, - $argsObj->tcaseSteps,$argsObj->user_id,$argsObj->assigned_keywords_list, - $new_order,testcase::AUTOMATIC_ID, - $argsObj->exec_type,$argsObj->importance,$options); - - if($tcase['status_ok']) { - $guiObj->actionOK = true; - if($argsObj->stay_here) { - $cf_map = $this->tcaseMgr->cfield_mgr->get_linked_cfields_at_design($argsObj->testproject_id,ENABLED, - NO_FILTER_SHOW_ON_EXEC,'testcase'); - - $this->tcaseMgr->cfield_mgr->design_values_to_db($_REQUEST,$tcase['tcversion_id'],$cf_map); - - $guiObj->user_feedback = sprintf(lang_get('tc_created'),$argsObj->name); - $guiObj->sqlResult = 'ok'; - $guiObj->initWebEditorFromTemplate = true; - $guiObj->cleanUpWebEditor = true; - $opt_list = ''; - } - else { - // we will not return to caller - $argsObj->tcase_id = $tcase['id']; - $argsObj->tcversion_id = $tcase['tcversion_id']; - - // BAD Choice Custom fields are written to db on $this->show() - $this->show($argsObj,$request, array('status_ok' => 1)); - } - } - elseif(isset($tcase['msg'])) { - $guiObj->actionOK = false; - $guiObj->user_feedback = lang_get('error_tc_add'); - $guiObj->user_feedback .= '' . $tcase['msg']; - $guiObj->sqlResult = 'ko'; - $opt_list = $argsObj->assigned_keywords_list; - $guiObj->initWebEditorFromTemplate = false; - } - - keywords_opt_transf_cfg($otCfg, $opt_list); - $guiObj->opt_cfg=$otCfg; - $templateCfg = templateConfiguration('tcNew'); - $guiObj->template=$templateCfg->default_template; - return $guiObj; - } - - - /* - function: edit (Test Case) - - args: - - returns: - - */ - function edit(&$argsObj,&$otCfg,$oWebEditorKeys) { - $guiObj = $this->initGuiBean($argsObj); - $otCfg->to->map = - $this->tcaseMgr->get_keywords_map($argsObj->tcase_id,$argsObj->tcversion_id, - array('orderByClause' =>" ORDER BY keyword ASC ")); - - keywords_opt_transf_cfg($otCfg, $argsObj->assigned_keywords_list); - - $gopt = array('renderImageInline' => false, 'renderImageInline' => false, - 'caller' => __METHOD__); - - $tc_data = $this->tcaseMgr->get_by_id($argsObj->tcase_id,$argsObj->tcversion_id,null,$gopt); - foreach($oWebEditorKeys as $key) - { - $guiObj->$key = isset($tc_data[0][$key]) ? $tc_data[0][$key] : ''; - $argsObj->$key = $guiObj->$key; - } - - $cf_smarty = null; - $cfPlaces = $this->tcaseMgr->buildCFLocationMap(); - foreach($cfPlaces as $locationKey => $locationFilter) - { - $cf_smarty[$locationKey] = - $this->tcaseMgr->html_table_of_custom_field_inputs($argsObj->tcase_id,null,'design','', - $argsObj->tcversion_id,null,null,$locationFilter); - } - - $templateCfg = templateConfiguration('tcEdit'); - $guiObj->cf = $cf_smarty; - $guiObj->tc=$tc_data[0]; - $guiObj->opt_cfg=$otCfg; - - $guiObj->cancelActionJS = - 'location.href=fRoot+' . "'" . - "lib/testcases/archiveData.php?version_id=" . - $argsObj->tcversion_id . - "&tcversion_id=" . $argsObj->tcversion_id . - '&edit=testcase&id=' . intval($argsObj->tcase_id); - - if( property_exists($argsObj, 'tplan_id') ) { - $guiObj->cancelActionJS .= "&tplan_id={$argsObj->tplan_id}"; - } - - if( property_exists($argsObj, 'show_mode') ) { - $guiObj->cancelActionJS .= "&show_mode={$argsObj->show_mode}"; - } - - $guiObj->cancelActionJS .= "'"; - - $guiObj->template=$templateCfg->default_template; - return $guiObj; - } - - - /* - function: doUpdate - - args: - - returns: - - */ - function doUpdate(&$argsObj,$request) { - $options = array('status' => $argsObj->tc_status, - 'estimatedExecDuration' => $argsObj->estimated_execution_duration); - - $ret = $this->tcaseMgr->update($argsObj->tcase_id, $argsObj->tcversion_id, $argsObj->name, - $argsObj->summary, $argsObj->preconditions, $argsObj->tcaseSteps, - $argsObj->user_id, $argsObj->assigned_keywords_list, - testcase::DEFAULT_ORDER, $argsObj->exec_type, - $argsObj->importance,$options); - - $this->show($argsObj,$request,$ret); - return; - } - - - /** - * doAdd2testplan - * - */ - function doAdd2testplan(&$argsObj,$request) { - $smartyObj = new TLSmarty(); - $smartyObj->assign('attachments',null); - $guiObj = $this->initGuiBean($argsObj); - $guiObj->refreshTree = $argsObj->refreshTree? 1 : 0; - - $tplan_mgr = new testplan($this->db); - - // $request['add2tplanid'] - // main key: testplan id - // sec key : platform_id - $item2link = null; - if( isset($request['add2tplanid']) ) - { - foreach($request['add2tplanid'] as $tplan_id => $platformSet) - { - foreach($platformSet as $platform_id => $dummy) - { - $item2link = null; - $item2link['tcversion'][$argsObj->tcase_id] = $argsObj->tcversion_id; - $item2link['platform'][$platform_id] = $platform_id; - $item2link['items'][$argsObj->tcase_id][$platform_id] = $argsObj->tcversion_id; - $tplan_mgr->link_tcversions($tplan_id,$item2link,$argsObj->user_id); - } - } - - $identity = new stdClass(); - $identity->tproject_id = $argsObj->tproject_id; - $identity->id = $argsObj->tcase_id; - $identity->version_id = $argsObj->tcversion_id; - - $this->tcaseMgr->show($smartyObj,$guiObj,$identity,$this->grants); - exit(); - } - return $guiObj; - } - - /** - * add2testplan - is really needed???? 20090308 - franciscom - TO DO - * - */ - function add2testplan(&$argsObj,$request) - { - } - - - /** - * - * - * @internal revisions - */ - function delete(&$argsObj,$request) { - - $guiObj = $this->initGuiBean($argsObj); - $guiObj->delete_message = ''; - $cfg = config_get('testcase_cfg'); - - $guiObj->exec_status_quo = $this->tcaseMgr->get_exec_status($argsObj->tcase_id,null, - array('addExecIndicator' => true)); - $guiObj->delete_enabled = 1; - if( $guiObj->exec_status_quo['executed'] && !$this->grants->testproject_delete_executed_testcases ) - { - $guiObj->delete_message = lang_get('system_blocks_delete_executed_tc_when'); - $guiObj->delete_enabled = 0; - } - // need to remove 'executed' key, in order to do not have side effects on Viewer logic (template). - unset($guiObj->exec_status_quo['executed']); - - - $guiObj->delete_mode = 'single'; - $guiObj->display_platform = false; - - // Need to understand if platform column has to be displayed on GUI - if( !is_null($guiObj->exec_status_quo) ) - { - // key level 1 : Test Case Version ID - // key level 2 : Test Plan ID - // key level 3 : Platform ID - - $versionSet = array_keys($guiObj->exec_status_quo); - $stop = false; - foreach($versionSet as $version_id) - { - $tplanSet = array_keys($guiObj->exec_status_quo[$version_id]); - foreach($tplanSet as $tplan_id) - { - if( ($guiObj->display_platform = !isset($guiObj->exec_status_quo[$version_id][$tplan_id][0])) ) - { - $stop = true; - break; - } - } - if($stop) - { - break; - } - } - } - - $tcinfo = $this->tcaseMgr->get_by_id($argsObj->tcase_id); - list($prefix,$root) = $this->tcaseMgr->getPrefix($argsObj->tcase_id,$argsObj->testproject_id); - $prefix .= $cfg->glue_character; - $external_id = $prefix . $tcinfo[0]['tc_external_id']; - - $guiObj->title = lang_get('title_del_tc'); - $guiObj->testcase_name = $tcinfo[0]['name']; - $guiObj->testcase_id = $argsObj->tcase_id; - $guiObj->tcversion_id = testcase::ALL_VERSIONS; - $guiObj->refreshTree = 1; - $guiObj->main_descr = lang_get('title_del_tc') . TITLE_SEP . $external_id . TITLE_SEP . $tcinfo[0]['name']; - - $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . - 'lib/testcases/archiveData.php?version_id=undefined&' . - 'edit=testcase&id=' . intval($guiObj->testcase_id) . "'"; - - - $templateCfg = templateConfiguration('tcDelete'); - $guiObj->template = $templateCfg->default_template; - return $guiObj; - } - - /** - * - * - */ - function doDelete(&$argsObj,$request) { - $cfg = config_get('testcase_cfg'); - - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->delete_message = ''; - $guiObj->action = 'deleted'; - $guiObj->sqlResult = 'ok'; - $guiObj->delete_mode = 'single'; - - $tcinfo = $this->tcaseMgr->get_by_id($argsObj->tcase_id,$argsObj->tcversion_id); - list($prefix,$root) = $this->tcaseMgr->getPrefix($argsObj->tcase_id,$argsObj->testproject_id); - $prefix .= $cfg->glue_character; - $external_id = $prefix . $tcinfo[0]['tc_external_id']; - if (!$this->tcaseMgr->delete($argsObj->tcase_id,$argsObj->tcversion_id)) - { - $guiObj->action = ''; - $guiObj->sqlResult = $this->tcaseMgr->db->error_msg(); - } - - $guiObj->main_descr = lang_get('title_del_tc') . ":" . $external_id . TITLE_SEP . htmlspecialchars($tcinfo[0]['name']); - - if($argsObj->tcversion_id == testcase::ALL_VERSIONS) - { - $guiObj->refreshTree = 1; - logAuditEvent(TLS("audit_testcase_deleted",$external_id),"DELETE",$argsObj->tcase_id,"testcases"); - $guiObj->user_feedback = sprintf(lang_get('tc_deleted'), ":" . $external_id . TITLE_SEP . $tcinfo[0]['name']); - } - else{ - $guiObj->main_descr .= " " . lang_get('version') . " " . $tcinfo[0]['version']; - // When deleting JUST one version, there is no need to refresh tree - $guiObj->refreshTree = 0; - logAuditEvent(TLS("audit_testcase_version_deleted",$tcinfo[0]['version'],$external_id),"DELETE",$argsObj->tcase_id,"testcases"); - $guiObj->user_feedback = sprintf(lang_get('tc_version_deleted'),$tcinfo[0]['name'],$tcinfo[0]['version']); - } - - $guiObj->testcase_name = $tcinfo[0]['name']; - $guiObj->testcase_id = $argsObj->tcase_id; - - $templateCfg = templateConfiguration('tcDelete'); - $guiObj->template=$templateCfg->default_template; - return $guiObj; - } - - - /** - * createStep - * - */ - function createStep(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $guiObj->main_descr = sprintf(lang_get('create_step'), $guiObj->testcase['tc_external_id'] . ':' . - $guiObj->testcase['name'], $guiObj->testcase['version']); - - $max_step = $this->tcaseMgr->get_latest_step_number($argsObj->tcversion_id); - $max_step++;; - - $guiObj->step_number = $max_step; - $guiObj->step_exec_type = $guiObj->testcase['execution_type']; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $guiObj->step_set = $this->tcaseMgr->get_step_numbers($argsObj->tcversion_id); - $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",",array_keys($guiObj->step_set)); - $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL,$argsObj->tcase_id,$argsObj->tcversion_id); - - // Get all existent steps - $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); - - $templateCfg = templateConfiguration('tcStepEdit'); - $guiObj->template = $templateCfg->default_template; - $guiObj->action = __FUNCTION__; - - return $guiObj; - } - - /** - * doCreateStep - * - */ - function doCreateStep(&$argsObj,$request,$doAndExit=false) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - $guiObj->main_descr = sprintf(lang_get('create_step'), $guiObj->testcase['tc_external_id'] . ':' . - $guiObj->testcase['name'], $guiObj->testcase['version']); - - - $new_step = $this->tcaseMgr->get_latest_step_number($argsObj->tcversion_id); - $new_step++; - $op = $this->tcaseMgr->create_step($argsObj->tcversion_id,$new_step, - $argsObj->steps,$argsObj->expected_results,$argsObj->exec_type); - - $guiObj->doExit = false; - if( $op['status_ok'] ) - { - $guiObj->doExit = $doAndExit; - $guiObj->user_feedback = sprintf(lang_get('step_number_x_created'),$argsObj->step_number); - $guiObj->step_exec_type = $guiObj->testcase['execution_type']; - $guiObj->cleanUpWebEditor = true; - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - $this->initTestCaseBasicInfo($argsObj,$guiObj); - } - - if(!$guiObj->doExit) - { - $guiObj->action = __FUNCTION__; - - // Get all existent steps - $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); - $max_step = $this->tcaseMgr->get_latest_step_number($argsObj->tcversion_id); - $max_step++;; - $guiObj->step_number = $max_step; - - $guiObj->step_set = $this->tcaseMgr->get_step_numbers($argsObj->tcversion_id); - $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",",array_keys($guiObj->step_set)); - $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL,$argsObj->tcase_id,$argsObj->tcversion_id); - - $templateCfg = templateConfiguration('tcStepEdit'); - $guiObj->template=$templateCfg->default_template; - } - return $guiObj; - } - - /** - * doCreateStepAndExit - * - */ - function doCreateStepAndExit(&$argsObj,$request) { - $guiObj = $this->doCreateStep($argsObj,$request,true); - if($guiObj->doExit) { - // when working on step, refreshing tree is nonsense - $argsObj->refreshTree = 0; - - $opt= array('updateCFOnDB' => !self::UPDATECFONDB); - $this->show($argsObj,$request,array('status_ok' => true),$opt); - exit(); - } else { - return $guiObj; - } - } - - - - - /** - * editStep - * - */ - function editStep(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $stepInfo = $this->tcaseMgr->get_step_by_id($argsObj->step_id); - - $oWebEditorKeys = array('steps' => 'actions', 'expected_results' => 'expected_results'); - foreach($oWebEditorKeys as $key => $field) - { - $argsObj->$key = $stepInfo[$field]; - $guiObj->$key = $stepInfo[$field]; - } - - $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'),$stepInfo['step_number'], - $guiObj->testcase['tc_external_id'] . ':' . - $guiObj->testcase['name'], $guiObj->testcase['version']); - - $guiObj->tcase_id = $argsObj->tcase_id; - $guiObj->tcversion_id = $argsObj->tcversion_id; - $guiObj->step_id = $argsObj->step_id; - $guiObj->step_exec_type = $stepInfo['execution_type']; - $guiObj->step_number = $stepInfo['step_number']; - - // Get all existent steps - $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); - - $guiObj->step_set = $this->tcaseMgr->get_step_numbers($argsObj->tcversion_id); - $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",",array_keys($guiObj->step_set)); - - $templateCfg = templateConfiguration('tcStepEdit'); - $guiObj->template=$templateCfg->default_template; - $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL,$argsObj->tcase_id,$argsObj->tcversion_id); - - return $guiObj; - } - - /** - * doUpdateStep - * - */ - function doUpdateStep(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - try { - $stepInfo = $this->tcaseMgr->get_step_by_id($argsObj->step_id); - } - catch (Exception $e) { - echo $e->getMessage(); - } - - - $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'),$stepInfo['step_number'], - $guiObj->testcase['tc_external_id'] . ':' . - $guiObj->testcase['name'], $guiObj->testcase['version']); - - $op = $this->tcaseMgr->update_step($argsObj->step_id,$argsObj->step_number,$argsObj->steps, - $argsObj->expected_results,$argsObj->exec_type); - - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $guiObj->tcversion_id = $argsObj->tcversion_id; - $guiObj->step_id = $argsObj->step_id; - $guiObj->step_number = $stepInfo['step_number']; - $guiObj->step_exec_type = $argsObj->exec_type; - - - $guiObj = $this->editStep($argsObj,$request); - return $guiObj; - } - - /** - * doUpdateStepAndExit - * - */ - function doUpdateStepAndExit(&$argsObj,$request) { - $this->doUpdateStep($argsObj,$request); - - // when working on step, refreshing tree is nonsense - $argsObj->refreshTree = 0; - $opt= array('updateCFOnDB' => !self::UPDATECFONDB); - $this->show($argsObj,$request,array('status_ok' => true),$opt); - } - - - /** - * doReorderSteps - * - */ - function doReorderSteps(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->main_descr = lang_get('test_case'); - $this->tcaseMgr->set_step_number($argsObj->step_set); - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $argsObj->refreshTree = 0; - $opt= array('updateCFOnDB' => !self::UPDATECFONDB); - $this->show($argsObj,$request,array('status_ok' => true),$opt); - exit(); - - } - - - /** - * doDeleteStep - * - */ - function doDeleteStep(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - - $guiObj->main_descr = lang_get('test_case'); - $guiObj->viewerArgs = array(); - $guiObj->refreshTree = 0; - $step_node = $this->tcaseMgr->tree_manager->get_node_hierarchy_info($argsObj->step_id); - - $tcversion_node = $this->tcaseMgr->tree_manager->get_node_hierarchy_info($step_node['parent_id']); - $argsObj->tcversion_id = $step_node['parent_id']; - $argsObj->tcase_id = $tcversion_node['parent_id']; - - $guiObj->user_feedback = ''; - $op = $this->tcaseMgr->delete_step_by_id($argsObj->step_id); - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $argsObj->refreshTree = 0; - $opt= array('updateCFOnDB' => !self::UPDATECFONDB); - $this->show($argsObj,$request,array('status_ok' => true),$opt); - exit(); - } - - /** - * doCopyStep - * - */ - function doCopyStep(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - // need to document difference bewteen these two similar concepts - $guiObj->action = __FUNCTION__; - $guiObj->operation = 'doUpdateStep'; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'),$argsObj->step_number, - $guiObj->testcase['tc_external_id'] . ':' . - $guiObj->testcase['name'], $guiObj->testcase['version']); - - $new_step = $this->tcaseMgr->get_latest_step_number($argsObj->tcversion_id); - $new_step++; - - $source_info = $this->tcaseMgr->get_steps($argsObj->tcversion_id,$argsObj->step_number); - $source_info = current($source_info); - $op = $this->tcaseMgr->create_step($argsObj->tcversion_id,$new_step,$source_info['actions'], - $source_info['expected_results'],$source_info['execution_type']); - - if( $op['status_ok'] ) - { - $guiObj->user_feedback = sprintf(lang_get('step_number_x_created_as_copy'),$new_step,$argsObj->step_number); - $guiObj->step_exec_type = TESTCASE_EXECUTION_TYPE_MANUAL; - - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - $this->initTestCaseBasicInfo($argsObj,$guiObj); - } - - - // Get all existent steps - $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); - - // After copy I would like to return to target step in edit mode, - // is enough to set $guiObj->step_number to target test step --> FOUND THIS is WRONG - // generated BUGID 4410 - $guiObj->step_number = $argsObj->step_number; - $guiObj->step_id = $argsObj->step_id; - - $guiObj->step_set = $this->tcaseMgr->get_step_numbers($argsObj->tcversion_id); - $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",",array_keys($guiObj->step_set)); - $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL,$argsObj->tcase_id,$argsObj->tcversion_id); - - $templateCfg = templateConfiguration('tcStepEdit'); - $guiObj->template=$templateCfg->default_template; - return $guiObj; - } - - - - /** - * doInsertStep - * - */ - function doInsertStep(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - // Get all existent steps - info needed to do renumbering - $stepNumberSet = array(); - $existentSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); - $stepsQty = count($existentSteps); - for($idx=0; $idx < $stepsQty; $idx++) - { - $stepNumberSet[$idx] = $existentSteps[$idx]['step_number']; - $stepIDSet[$idx] = $existentSteps[$idx]['id']; - } - - $stepInfo = $this->tcaseMgr->get_step_by_id($argsObj->step_id); - $newStepNumber = $stepInfo['step_number'] + 1; - $op = $this->tcaseMgr->create_step($argsObj->tcversion_id,$newStepNumber,'',''); - $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'),$newStepNumber, - $guiObj->testcase['tc_external_id'] . ':' . - $guiObj->testcase['name'], $guiObj->testcase['version']); - - if( $op['status_ok'] ) - { - $guiObj->user_feedback = sprintf(lang_get('step_number_x_created'),$newStepNumber); - $guiObj->step_exec_type = TESTCASE_EXECUTION_TYPE_MANUAL; - $guiObj->cleanUpWebEditor = true; - - // renumber steps only if new step hits an existent step number - $hitPos = array_search($newStepNumber, $stepNumberSet); - if( $hitPos !== FALSE ) - { - // Process starts from this position - $just_renumbered = array('pos' => $hitPos, 'value' => $newStepNumber+1); - $renumbered[$stepIDSet[$hitPos]] = $just_renumbered['value']; - - // now check if new renumbered collides with next - // if not nothing needs to be done - // if yes need to loop - $startFrom = $hitPos +1; - $endOn = count($stepNumberSet); - for($jdx = $startFrom; $jdx < $endOn; $jdx++) - { - if( $stepNumberSet[$jdx] == $just_renumbered['value'] ) - { - $just_renumbered['value']++; - $renumbered[$stepIDSet[$jdx]] = $just_renumbered['value']; - } - } - $this->tcaseMgr->set_step_number($renumbered); - } - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - $this->initTestCaseBasicInfo($argsObj,$guiObj); - } - - // Get all existent steps - updated - $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); - $guiObj->action = __FUNCTION__; - $guiObj->step_number = $newStepNumber; - $guiObj->step_id = $op['id']; - - $guiObj->step_set = $this->tcaseMgr->get_step_numbers($argsObj->tcversion_id); - $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",",array_keys($guiObj->step_set)); - $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL,$argsObj->tcase_id,$argsObj->tcversion_id); - $templateCfg = templateConfiguration('tcStepEdit'); - $guiObj->template=$templateCfg->default_template; - return $guiObj; - } - - - /** - * - */ - function doResequenceSteps(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - // Get all existent steps - info needed to do renumbering - $stepNumberSet = array(); - $stepSet = $this->tcaseMgr->get_steps($argsObj->tcversion_id); - $stepsQty = count($stepSet); - for($idx=0; $idx < $stepsQty; $idx++) { - $renumbered[$stepSet[$idx]['id']] = $idx+1; - } - $this->tcaseMgr->set_step_number($renumbered); - - $guiObj->template = - "archiveData.php?version_id={$guiObj->tcversion_id}&" . - "tcversion_id={$guiObj->tcversion_id}&" . - "edit=testcase&id={$guiObj->tcase_id}&" . - "show_mode={$guiObj->show_mode}"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - $guiObj->user_feedback = ''; - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - return $guiObj; - } - - - /** - * - * - * - */ - function setImportance(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $this->tcaseMgr->setImportance($argsObj->tcversion_id,$argsObj->importance); - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - - // set up for rendering - $guiObj->template = - "archiveData.php?version_id={$guiObj->tcversion_id}&" . - "tcversion_id={$guiObj->tcversion_id}&". - "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - $guiObj->user_feedback = ''; - return $guiObj; - } - - /** - * - * - * - */ - function setStatus(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $this->tcaseMgr->setStatus($argsObj->tcversion_id,$argsObj->status); - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - - // set up for rendering - $guiObj->template = - "archiveData.php?version_id={$guiObj->tcversion_id}&" . - "tcversion_id={$guiObj->tcversion_id}&" . - "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - $guiObj->user_feedback = ''; - return $guiObj; - } - - /** - * - * - * - */ - function setExecutionType(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $opx = array('updSteps' => $argsObj->applyExecTypeChangeToAllSteps); - $this->tcaseMgr->setExecutionType($argsObj->tcversion_id,$argsObj->exec_type,$opx); - - - - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - - // - - - - // set up for rendering - $guiObj->template = - "archiveData.php?version_id={$guiObj->tcversion_id}&" . - "tcversion_id={$guiObj->tcversion_id}&" . - "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - $guiObj->user_feedback = ''; - return $guiObj; - } - - /** - * - * - * - */ - function setEstimatedExecDuration(&$argsObj,$request) - { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $this->tcaseMgr->setEstimatedExecDuration($argsObj->tcversion_id,$argsObj->estimatedExecDuration); - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - - // set up for rendering - $guiObj->template = - "archiveData.php?version_id={$guiObj->tcversion_id}&" . - "tcversion_id={$guiObj->tcversion_id}&" . - "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - $guiObj->user_feedback = ''; - return $guiObj; - } - - - - /** - * - * - */ - function show(&$argsObj,$request,$userFeedback,$opt=null) { - $smartyObj = new TLSmarty(); - - $options = array('updateCFOnDB' => true, - 'updateTPlanLinkToTCV' => false); - $options = array_merge($options,(array)$opt); - - $updateCFOnDB = $options['updateCFOnDB']; - - $guiObj = $this->initGuiBean($argsObj); - $identity = $this->buildIdentity($argsObj); - - $guiObj->uploadOp = $argsObj->uploadOp; - - $guiObj->viewerArgs=array(); - $guiObj->refreshTree = ($argsObj->refreshTree - && $userFeedback['status_ok']) ? 1 : 0; - $guiObj->has_been_executed = $argsObj->has_been_executed; - $guiObj->steps_results_layout = config_get('spec_cfg')->steps_results_layout; - $guiObj->user_feedback = ''; - - $guiObj->direct_link = - $this->tcaseMgr->buildDirectWebLink($_SESSION['basehref'], - $argsObj->tcase_id, - $argsObj->testproject_id); - - if($userFeedback['status_ok']) { - if( $options['updateTPlanLinkToTCV'] ) { - $guiObj->updateTPlanLinkToTCV = true; - $guiObj->show_mode = 'editOnExec'; - - // @20190127 the only useful thing there may be is the Rabbit - $guiObj->additionalURLPar = - "&updateTCVToThis=" . $identity->version_id . - "&followTheWhiteRabbit=1"; - $guiObj->closeMyWindow = 1; - - } - - $guiObj->user_feedback = ''; - if($updateCFOnDB) { - $cfCtx = array('tproject_id' => $identity->tproject_id, 'enabled' => 1, - 'node_type' => 'testcase'); - $cf_map = $this->tcaseMgr->cfield_mgr->getLinkedCfieldsAtDesign($cfCtx); - - - $this->tcaseMgr->cfield_mgr->design_values_to_db($request,$identity->version_id,$cf_map); - } - - $guiObj->attachments[$identity->version_id] = - getAttachmentInfosFrom($this->tcaseMgr,$identity->version_id); - } - else { - $guiObj->viewerArgs['user_feedback'] = $guiObj->user_feedback = $userFeedback['msg']; - } - - $guiObj->viewerArgs['refreshTree'] = $guiObj->refreshTree; - $guiObj->viewerArgs['user_feedback'] = $guiObj->user_feedback; - - $this->tcaseMgr->show($smartyObj,$guiObj,$identity,$this->grants); - exit(); - } - - - /** - * - */ - private function buildIdentity($cred) { - $idy= new stdClass(); - if( property_exists($cred, 'tproject_id') ) { - $idy->tproject_id = $cred->tproject_id; - } - else if( property_exists($cred, 'testproject_id')) { - $idy->tproject_id = $cred->testproject_id; - } - else { - throw new Exception(__METHOD__ . ' EXCEPTION: test project ID, is mandatory'); - } - $idy->tproject_id = intval($idy->tproject_id); - $idy->id = intval($cred->tcase_id); - $idy->version_id = $cred->tcversion_id; - return $idy; - } - - - /** - * - * - */ - function doAddRelation(&$argsObj,&$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - - $this->initTestCaseBasicInfo($argsObj,$guiObj,array('accessByStepID' => false)); - - if($argsObj->destination_tcase_id >0) { - $relTypeInfo = explode('_',$argsObj->relation_type); - $source_id = $argsObj->tcase_id; - $destination_id = $argsObj->destination_tcase_id; - if( $relTypeInfo[1] == "destination" ) { - $source_id = $argsObj->destination_tcase_id; - $destination_id = $argsObj->tcase_id; - } - - $ret = $this->tcaseMgr->addRelation($source_id, $destination_id,$relTypeInfo[0], $argsObj->user_id); - $guiObj->user_feedback = sprintf(lang_get($ret['msg']), $argsObj->relation_destination_tcase); - } - else { - $guiObj->user_feedback = sprintf(lang_get('testcase_doesnot_exists'), $argsObj->relation_destination_tcase); - } - - // set up for rendering - // It's OK put fixed 0 on version_id other functions on the chain to do the display know how to manage this - $guiObj->template = "archiveData.php?version_id=0&" . - "tcversion_id={$guiObj->tcversion_id}&" . - "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - if($guiObj->user_feedback != '') { - $guiObj->template .= "&add_relation_feedback_msg=" . urlencode($guiObj->user_feedback); - } - return $guiObj; - } - - /** - * - * - */ - function doDeleteRelation(&$argsObj,&$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - - $this->initTestCaseBasicInfo($argsObj,$guiObj,array('accessByStepID' => false)); - - if($argsObj->relation_id >0) { - $this->tcaseMgr->deleteRelationByID($argsObj->relation_id); - } - - // set up for rendering - $guiObj->template = "archiveData.php?edit=testcase&" . - "id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . - "&caller=delRel"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - return $guiObj; - } - - - - /** - * doUpdateStepAndExit - * - */ - function doUpdateStepAndInsert(&$argsObj,$request) { - $this->doUpdateStep($argsObj,$request); - return $this->doInsertStep($argsObj,$request); - } - - - /** - * - * - */ - function removeKeyword(&$argsObj,&$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - - $this->initTestCaseBasicInfo($argsObj,$guiObj,array('accessByStepID' => false)); - - if($argsObj->tckw_link_id > 0) { - $this->tcaseMgr->deleteKeywordsByLink( - $guiObj->tcase_id, $argsObj->tckw_link_id,testcase::AUDIT_ON); - } - - // set up for rendering - $guiObj->template = "archiveData.php?edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . "&caller=removeKeyword"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - return $guiObj; - } - - - function freeze(&$argsObj,$request) { - $argsObj->isOpen = 0; - return $this->setIsOpen($argsObj,$request); - } - - function unfreeze(&$argsObj,$request) { - $argsObj->isOpen = 1; - return $this->setIsOpen($argsObj,$request); - } - - /** - * - */ - function setIsOpen(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $this->tcaseMgr->setIsOpen(null,$argsObj->tcversion_id,$argsObj->isOpen); - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - - // set up for rendering - $guiObj->template = - "archiveData.php?version_id={$guiObj->tcversion_id}&" . - "tcversion_id={$guiObj->tcversion_id}&" . - "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - $guiObj->user_feedback = ''; - return $guiObj; - } - - - /** - * - */ - function setActiveAttr(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - $this->tcaseMgr->update_active_status($argsObj->tcase_id, - $argsObj->tcversion_id, $argsObj->activeAttr); - - $this->tcaseMgr->update_last_modified($argsObj->tcversion_id,$argsObj->user_id); - - $lk = 'audit_tcversion_deactivated'; - $pre = 'DE'; - if( $argsObj->activeAttr ) { - $pre = ''; - $lk = 'audit_tcversion_activated'; - } - - logAuditEvent(TLS($lk,$guiObj->testcase['name'], - $guiObj->testcase['version']), - "{$pre}ACTIVATE","testcases"); - - $this->show($argsObj,$request, array('status_ok' => 1)); - exit(); - } - - - /** - * - * - */ - function addKeyword(&$argsObj,&$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - - $this->initTestCaseBasicInfo($argsObj,$guiObj,array('accessByStepID' => false)); - - $tcExternalID = $guiObj->testcase['tc_external_id']; - if( null != $argsObj->free_keywords || 1==1) { - $this->tcaseMgr->addKeywords($guiObj->tcase_id,$guiObj->tcversion_id, - $argsObj->free_keywords); - - $info = $this->tprojectMgr->get_by_id($this->tproject_id); - $cfx = config_get('keywords')->byTestProject; - if( isset($cfx[$info['prefix']]) && - $cfx[$info['prefix']]['addTCLinkIntoITS'] && - $info['issue_tracker_enabled'] ) { - - $it_mgr = new tlIssueTracker($this->db); - $argsObj->itsCfg = $it_mgr->getLinkedTo($this->tproject_id); - $its = $it_mgr->getInterfaceObject($this->tproject_id); - if( method_exists($its,'addNote') ) { - $dl = sprintf(lang_get('dlToTCSpecPVCode'), $tcExternalID) . - ' ' . lang_get('dlToTCSpecPV') . ' ' . - $this->tcaseMgr->buildDirectWebLink($_SESSION['basehref'], - $argsObj->tcase_id,$argsObj->testproject_id); - - // Get keyword for human beins - $tbl = tlObject::getDBTables(array('keywords')); - $inClause = "'" . implode("','",$argsObj->free_keywords) . - "'"; - $sql = "SELECT id,keyword FROM {$tbl['keywords']} - WHERE id IN($inClause) "; - $kwSet = $this->db->fetchRowsIntoMap($sql,'id'); - - $strToDel = isset($cfx[$info['prefix']]['prefix']) ? - $cfx[$info['prefix']]['prefix'] : ''; - $strToDel = trim($strToDel); - foreach( $argsObj->free_keywords as $kw ) { - if( '' == $strToDel ) { - $kwv = $kwSet[$kw]['keyword']; - } else { - $kwv = str_replace($strToDel,'', - $kwSet[$kw]['keyword']); - } - try { - $opStatus = $its->addNote($kwv,$dl); - } catch(Exception $e) { - echo 'Silent Failure?'; - } - } - - - } - } - } - - // set up for rendering - $guiObj->template = "archiveData.php?edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . "&caller=addKeyword"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - return $guiObj; - } - - /** - * - * @used by tcEdit.php - * - */ - function updateTPlanLinkToTCV($argsObj,$request) { - - $this->tcaseMgr->updateLatestTPlanLinkToTCV($argsObj->tcversion_id,$argsObj->tplan_id); - - $opt = array('updateTPlanLinkToTCV' => true); - - $this->show($argsObj,$request, array('status_ok' => 1),$opt); - } - - /** - * doStepOperationExit - * - */ - function doStepOperationExit(&$argsObj,$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - $guiObj->step_exec_type = $argsObj->exec_type; - $guiObj->tcversion_id = $argsObj->tcversion_id; - - $this->initTestCaseBasicInfo($argsObj,$guiObj); - $guiObj->main_descr = sprintf(lang_get('create_step'), $guiObj->testcase['tc_external_id'] . ':' . - $guiObj->testcase['name'], $guiObj->testcase['version']); - $guiObj->cleanUpWebEditor = true; - $this->initTestCaseBasicInfo($argsObj,$guiObj); - - // when working on step, refreshing tree is nonsense - $argsObj->refreshTree = 0; - - $opt= array('updateCFOnDB' => !self::UPDATECFONDB); - $this->show($argsObj,$request,array('status_ok' => true),$opt); - exit(); - } - - /** - * - * - */ - function addPlatform(&$argsObj,&$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - - $this->initTestCaseBasicInfo($argsObj,$guiObj,array('accessByStepID' => false)); - - if( null != $argsObj->free_platforms ) { - $this->tcaseMgr->addPlatforms($guiObj->tcase_id, - $guiObj->tcversion_id, $argsObj->free_platforms); - } - - // set up for rendering - $guiObj->template = "archiveData.php?edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . "&caller=addPlatform"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - - return $guiObj; - } - - /** - * - * - */ - function removePlatform(&$argsObj,&$request) { - $guiObj = $this->initGuiBean($argsObj); - $guiObj->user_feedback = ''; - - $this->initTestCaseBasicInfo($argsObj,$guiObj,array('accessByStepID' => false)); - - if($argsObj->tcplat_link_id > 0) { - $this->tcaseMgr->deletePlatformsByLink( - $guiObj->tcase_id, $argsObj->tcplat_link_id,testcase::AUDIT_ON); - } - - // set up for rendering - $guiObj->template = - "archiveData.php?edit=testcase&id={$guiObj->tcase_id}" . - "&show_mode={$guiObj->show_mode}" . - "&caller=removePlatform"; - - if( property_exists($guiObj, 'tplan_id') ) { - $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; - } - return $guiObj; - } - - - - - -} // end class +db = $db; + $this->tcaseMgr = new testcase($db); + $this->tcaseMgr->setTestProject($tproject_id); + + $this->tprojectMgr = &$this->tcaseMgr->tproject_mgr; + $this->tproject_id = $tproject_id; + + $this->execution_types = $this->tcaseMgr->get_execution_types(); + $this->grants = new stdClass(); + + $g2c = array( + 'mgt_modify_tc', + 'mgt_view_req', + 'testplan_planning', + 'req_tcase_link_management', + 'mgt_modify_req', + 'testproject_delete_executed_testcases', + 'testproject_edit_executed_testcases' + ); + foreach ($g2c as $grant) { + $this->grants->$grant = $userObj->hasRight($db, $grant, $tproject_id); + } + + $this->grants->requirement_mgmt = $this->grants->mgt_modify_req || + $this->grants->req_tcase_link_management; + + $this->tables = $this->tcaseMgr->getDBTables( + array( + 'keywords', + 'platforms' + )); + } + + public function setTemplateCfg($cfg) + { + $this->templateCfg = $cfg; + } + + /** + */ + public function initGuiBean(&$argsObj) + { + $obj = new stdClass(); + $obj->action = ''; + $obj->attachments = null; + $obj->cleanUpWebEditor = false; + $obj->containerID = ''; + $obj->direct_link = null; + $obj->execution_types = $this->execution_types; + + $obj->grants = $this->grants; + + $key = 'req_tcase_link_management'; + $obj->$key = $obj->grants->$key; + + $obj->has_been_executed = false; + $obj->initWebEditorFromTemplate = false; + + $obj->main_descr = ''; + $obj->name = ''; + $obj->path_info = null; + $obj->refreshTree = 0; + $obj->sqlResult = ''; + $obj->step_id = - 1; + $obj->step_set = ''; + $obj->steps = ''; + + $dummy = testcase::getLayout(); + $obj->tableColspan = $dummy->tableToDisplayTestCaseSteps->colspan; + + $tck = array( + 'tcase_id', + 'tcversion_id', + 'tplan_id' + ); + foreach ($tck as $pkey) { + $obj->$pkey = property_exists($argsObj, $pkey) ? $argsObj->$pkey : - 1; + } + + $obj->viewerArgs = null; + + $p2check = 'goback_url'; + $obj->$p2check = ''; + if (property_exists($argsObj, $p2check)) { + $obj->$p2check = ! is_null($argsObj->$p2check) ? $argsObj->$p2check : ''; + } + + $p2check = 'show_mode'; + if (property_exists($argsObj, $p2check)) { + $obj->$p2check = ! is_null($argsObj->$p2check) ? $argsObj->$p2check : 'show'; + } + + // need to check where is used + $obj->loadOnCancelURL = "archiveData.php?edit=testcase&show_mode={$obj->show_mode}&id=%s&version_id=%s"; + + if (property_exists($obj, 'tplan_id')) { + $obj->loadOnCancelURL .= "&tplan_id={$obj->tplan_id}"; + } + + if (property_exists($obj, 'show_mode')) { + $obj->loadOnCancelURL .= "&show_mode={$obj->show_mode}"; + } + + $obj->codeTrackerEnabled = $this->tprojectMgr->isCodeTrackerEnabled( + $this->tproject_id); + + return $obj; + } + + /** + * initialize common test case information, useful when working on steps + */ + public function initTestCaseBasicInfo(&$argsObj, &$guiObj, $opt = null) + { + $my['opt'] = array( + 'accessByStepID' => true + ); + $my['opt'] = array_merge($my['opt'], (array) $opt); + + // Security + // https://cxsecurity.com/issue/WLB-2019110139 + if (intval($argsObj->tcase_id) == 0 && + intval($argsObj->tcversion_id) == 0) { + die("Error Processing Request:" . __METHOD__); + } + + $greenCard = array( + 'tcase_id' => $argsObj->tcase_id, + 'tcversion_id' => $argsObj->tcversion_id + ); + + if ($my['opt']['accessByStepID']) { + foreach ($greenCard as $ky) { + // this logic need to be explained BETTER + if ($ky == 0) { + $greenCard = $this->tcaseMgr->getIdCardByStepID( + $argsObj->step_id); + break; + } + } + } + + $gopt = [ + 'output' => 'full_without_steps', + 'renderGhost' => true, + 'renderImageInline' => true, + 'renderVariables' => true, + 'tproject_id' => intval($argsObj->testproject_id) + ]; + + $tcaseInfo = $this->tcaseMgr->get_by_id($greenCard['tcase_id'], + $greenCard['tcversion_id'], null, $gopt); + + $external = $this->tcaseMgr->getExternalID($greenCard['tcase_id'], + $argsObj->testproject_id); + $tcaseInfo[0]['tc_external_id'] = $external[0]; + $guiObj->testcase = $tcaseInfo[0]; + + if (! isset($guiObj->testcase['ghost'])) { + $guiObj->testcase['ghost'] = null; + } + $guiObj->authorObj = tlUser::getByID($this->db, + $guiObj->testcase['author_id']); + + $guiObj->updaterObj = null; + if (! is_null($guiObj->testcase['updater_id'])) { + $guiObj->updaterObj = tlUser::getByID($this->db, + $guiObj->testcase['updater_id']); + } + + $cfCtx = array( + 'scope' => 'design', + 'tproject_id' => $argsObj->testproject_id, + 'link_id' => $argsObj->tcversion_id + ); + + $cfPlaces = $this->tcaseMgr->buildCFLocationMap(); + foreach ($cfPlaces as $cfpKey => $cfpFilter) { + $guiObj->cfieldsDesignTime[$cfpKey] = $this->tcaseMgr->htmlTableOfCFValues( + $argsObj->tcase_id, $cfCtx, $cfpFilter); + } + } + + /** + */ + public function create(&$argsObj, &$otCfg) + { + $parentKeywords = array(); + $guiObj = $this->initGuiBean($argsObj); + $guiObj->initWebEditorFromTemplate = true; + + $guiObj->containerID = $argsObj->container_id; + if ($argsObj->container_id > 0) { + $pnode_info = $this->tcaseMgr->tree_manager->get_node_hierarchy_info( + $argsObj->container_id); + $node_descr = array_flip( + $this->tcaseMgr->tree_manager->get_available_node_types()); + $guiObj->parent_info['name'] = $pnode_info['name']; + $guiObj->parent_info['description'] = lang_get( + $node_descr[$pnode_info['node_type_id']]); + + // get keywords + $tsuiteMgr = new testsuite($this->db); + $parentKeywords = $tsuiteMgr->getKeywords($argsObj->container_id); + } + $sep_1 = config_get('gui_title_separator_1'); + $sep_2 = config_get('gui_title_separator_2'); + $guiObj->main_descr = $guiObj->parent_info['description'] . $sep_1 . + $guiObj->parent_info['name'] . $sep_2 . lang_get('title_new_tc'); + + $otCfg->to->map = array(); + keywords_opt_transf_cfg($otCfg, + implode(',', array_keys((array) $parentKeywords))); + + $guiObj->tc = array( + 'id' => 0, + 'name' => '', + 'importance' => config_get('testcase_importance_default'), + 'status' => null, + 'estimated_exec_duration' => null, + 'execution_type' => TESTCASE_EXECUTION_TYPE_MANUAL + ); + + $guiObj->opt_cfg = $otCfg; + $templateCfg = templateConfiguration('tcNew'); + $guiObj->template = $templateCfg->default_template; + + $cfPlaces = $this->tcaseMgr->buildCFLocationMap(); + foreach ($cfPlaces as $locationKey => $locationFilter) { + $guiObj->cf[$locationKey] = $this->tcaseMgr->html_table_of_custom_field_inputs( + null, null, 'design', '', null, null, $argsObj->testproject_id, + $locationFilter, $_REQUEST); + } + + $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . + "lib/testcases/archiveData.php?id=" . intval($argsObj->container_id); + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->cancelActionJS .= "&tplan_id={$guiObj->tplan_id}"; + } + + if (property_exists($guiObj, 'show_mode')) { + $guiObj->cancelActionJS .= "&show_mode={$guiObj->show_mode}"; + } + + $guiObj->cancelActionJS .= '&edit=testsuite&level=testsuite&containerType=testsuite' . + "'"; + + return $guiObj; + } + + /** + */ + public function doCreate(&$argsObj, &$otCfg, $oWebEditorKeys, $request) + { + $guiObj = $this->create($argsObj, $otCfg, $oWebEditorKeys); + + // compute order + $new_order = config_get('treemenu_default_testcase_order'); + $co = $this->tcaseMgr->tree_manager->getBottomOrder( + $argsObj->container_id, array( + 'node_type' => 'testcase' + )); + if ($co > 0) { + $new_order = $co + 1; + } + + $options = array( + 'check_duplicate_name' => config_get('check_names_for_duplicates'), + 'action_on_duplicate_name' => 'block', + 'status' => $argsObj->tc_status, + 'estimatedExecDuration' => $argsObj->estimated_execution_duration + ); + + $tcase = $this->tcaseMgr->create($argsObj->container_id, $argsObj->name, + $argsObj->summary, $argsObj->preconditions, $argsObj->tcaseSteps, + $argsObj->user_id, $argsObj->assigned_keywords_list, $new_order, + testcase::AUTOMATIC_ID, $argsObj->exec_type, $argsObj->importance, + $options); + + if ($tcase['status_ok']) { + $guiObj->actionOK = true; + if ($argsObj->stay_here) { + $cf_map = $this->tcaseMgr->cfield_mgr->get_linked_cfields_at_design( + $argsObj->testproject_id, cfield_mgr::ENABLED, + NO_FILTER_SHOW_ON_EXEC, 'testcase'); + + $this->tcaseMgr->cfield_mgr->design_values_to_db($_REQUEST, + $tcase['tcversion_id'], $cf_map); + + $guiObj->user_feedback = sprintf(lang_get('tc_created'), + $argsObj->name); + $guiObj->sqlResult = 'ok'; + $guiObj->initWebEditorFromTemplate = true; + $guiObj->cleanUpWebEditor = true; + $opt_list = ''; + } else { + // we will not return to caller + $argsObj->tcase_id = $tcase['id']; + $argsObj->tcversion_id = $tcase['tcversion_id']; + + // BAD Choice Custom fields are written to db on $this->show() + $this->show($argsObj, $request, array( + 'status_ok' => 1 + )); + } + } elseif (isset($tcase['msg'])) { + $guiObj->actionOK = false; + $guiObj->user_feedback = lang_get('error_tc_add'); + $guiObj->user_feedback .= '' . $tcase['msg']; + $guiObj->sqlResult = 'ko'; + $opt_list = $argsObj->assigned_keywords_list; + $guiObj->initWebEditorFromTemplate = false; + } + + keywords_opt_transf_cfg($otCfg, $opt_list); + $guiObj->opt_cfg = $otCfg; + $templateCfg = templateConfiguration('tcNew'); + $guiObj->template = $templateCfg->default_template; + return $guiObj; + } + + /* + * function: edit (Test Case) + * + * args: + * + * returns: + * + */ + public function edit(&$argsObj, &$otCfg, $oWebEditorKeys) + { + $guiObj = $this->initGuiBean($argsObj); + $otCfg->to->map = $this->tcaseMgr->get_keywords_map($argsObj->tcase_id, + $argsObj->tcversion_id, + array( + 'orderByClause' => " ORDER BY keyword ASC " + )); + + keywords_opt_transf_cfg($otCfg, $argsObj->assigned_keywords_list); + + $gopt = [ + 'renderImageInline' => false, + 'renderImageInline' => false, + 'caller' => __METHOD__ + ]; + + $tc_data = $this->tcaseMgr->get_by_id($argsObj->tcase_id, + $argsObj->tcversion_id, null, $gopt); + foreach ($oWebEditorKeys as $key) { + $guiObj->$key = isset($tc_data[0][$key]) ? $tc_data[0][$key] : ''; + $argsObj->$key = $guiObj->$key; + } + + $cf_smarty = null; + $cfPlaces = $this->tcaseMgr->buildCFLocationMap(); + + // To skip in an elegant way?? + $hideCode = $cfPlaces['hide_because_is_used_as_variable']['location']; + unset($cfPlaces['hide_because_is_used_as_variable']); + + foreach ($cfPlaces as $locationKey => $locationFilter) { + switch ($locationKey) { + case 'standard_location': + $std = $locationFilter['location']; + $locationFilter['location'] = [ + $std, + $hideCode + ]; + break; + } + + $cf_smarty[$locationKey] = $this->tcaseMgr->html_table_of_custom_field_inputs( + $argsObj->tcase_id, null, 'design', '', $argsObj->tcversion_id, + null, null, $locationFilter); + } + + $templateCfg = templateConfiguration('tcEdit'); + $guiObj->cf = $cf_smarty; + $guiObj->tc = $tc_data[0]; + $guiObj->opt_cfg = $otCfg; + + $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . + "lib/testcases/archiveData.php?version_id=" . $argsObj->tcversion_id . + "&tcversion_id=" . $argsObj->tcversion_id . '&edit=testcase&id=' . + intval($argsObj->tcase_id); + + if (property_exists($argsObj, 'tplan_id')) { + $guiObj->cancelActionJS .= "&tplan_id={$argsObj->tplan_id}"; + } + + if (property_exists($argsObj, 'show_mode')) { + $guiObj->cancelActionJS .= "&show_mode={$argsObj->show_mode}"; + } + + $guiObj->cancelActionJS .= "'"; + + $guiObj->template = $templateCfg->default_template; + return $guiObj; + } + + /* + * function: doUpdate + * + * args: + * + * returns: + * + */ + public function doUpdate(&$argsObj, $request) + { + $options = array( + 'status' => $argsObj->tc_status, + 'estimatedExecDuration' => $argsObj->estimated_execution_duration + ); + + $ret = $this->tcaseMgr->update($argsObj->tcase_id, + $argsObj->tcversion_id, $argsObj->name, $argsObj->summary, + $argsObj->preconditions, $argsObj->tcaseSteps, $argsObj->user_id, + $argsObj->assigned_keywords_list, testcase::DEFAULT_ORDER, + $argsObj->exec_type, $argsObj->importance, $options); + + $this->show($argsObj, $request, $ret); + } + + /** + * doAdd2testplan + */ + public function doAdd2testplan(&$argsObj, $request) + { + $smartyObj = new TLSmarty(); + $smartyObj->assign('attachments', null); + $guiObj = $this->initGuiBean($argsObj); + $guiObj->refreshTree = $argsObj->refreshTree ? 1 : 0; + + $tplan_mgr = new testplan($this->db); + + // $request['add2tplanid'] + // main key: testplan id + // sec key : platform_id + $item2link = null; + if (isset($request['add2tplanid'])) { + foreach ($request['add2tplanid'] as $tplan_id => $platformSet) { + foreach ($platformSet as $platform_id => $dummy) { + $item2link = null; + $item2link['tcversion'][$argsObj->tcase_id] = $argsObj->tcversion_id; + $item2link['platform'][$platform_id] = $platform_id; + $item2link['items'][$argsObj->tcase_id][$platform_id] = $argsObj->tcversion_id; + $tplan_mgr->link_tcversions($tplan_id, $item2link, + $argsObj->user_id); + } + } + + $identity = new stdClass(); + $identity->tproject_id = $argsObj->tproject_id; + $identity->id = $argsObj->tcase_id; + $identity->version_id = $argsObj->tcversion_id; + + $this->tcaseMgr->show($smartyObj, $guiObj, $identity, $this->grants); + exit(); + } + return $guiObj; + } + + /** + * + * @internal revisions + */ + public function delete(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->delete_message = ''; + $cfg = config_get('testcase_cfg'); + + $guiObj->exec_status_quo = $this->tcaseMgr->getExecStatus( + $argsObj->tcase_id, null, array( + 'addExecIndicator' => true + )); + $guiObj->delete_enabled = 1; + if ($guiObj->exec_status_quo['executed'] && + ! $this->grants->testproject_delete_executed_testcases) { + $guiObj->delete_message = lang_get( + 'system_blocks_delete_executed_tc_when'); + $guiObj->delete_enabled = 0; + } + // need to remove 'executed' key, in order to do not have side effects on Viewer logic (template). + unset($guiObj->exec_status_quo['executed']); + + $guiObj->delete_mode = 'single'; + $guiObj->display_platform = false; + + // Need to understand if platform column has to be displayed on GUI + if (! is_null($guiObj->exec_status_quo)) { + // key level 1 : Test Case Version ID + // key level 2 : Test Plan ID + // key level 3 : Platform ID + + $versionSet = array_keys($guiObj->exec_status_quo); + $stop = false; + foreach ($versionSet as $version_id) { + $tplanSet = array_keys($guiObj->exec_status_quo[$version_id]); + foreach ($tplanSet as $tplan_id) { + if ($guiObj->display_platform = ! isset( + $guiObj->exec_status_quo[$version_id][$tplan_id][0])) { + $stop = true; + break; + } + } + if ($stop) { + break; + } + } + } + + $tcinfo = $this->tcaseMgr->get_by_id($argsObj->tcase_id); + list ($prefix,) = $this->tcaseMgr->getPrefix($argsObj->tcase_id, + $argsObj->testproject_id); + $prefix .= $cfg->glue_character; + $external_id = $prefix . $tcinfo[0]['tc_external_id']; + + $guiObj->title = lang_get('title_del_tc'); + $guiObj->testcase_name = $tcinfo[0]['name']; + $guiObj->testcase_id = $argsObj->tcase_id; + $guiObj->tcversion_id = testcase::ALL_VERSIONS; + $guiObj->refreshTree = 1; + $guiObj->main_descr = lang_get('title_del_tc') . TITLE_SEP . $external_id . + TITLE_SEP . $tcinfo[0]['name']; + + $guiObj->cancelActionJS = 'location.href=fRoot+' . "'" . + 'lib/testcases/archiveData.php?version_id=undefined&' . + 'edit=testcase&id=' . intval($guiObj->testcase_id) . "'"; + + $templateCfg = templateConfiguration('tcDelete'); + $guiObj->template = $templateCfg->default_template; + return $guiObj; + } + + /** + */ + public function doDelete(&$argsObj, $request) + { + $cfg = config_get('testcase_cfg'); + + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->delete_message = ''; + $guiObj->action = 'deleted'; + $guiObj->sqlResult = 'ok'; + $guiObj->delete_mode = 'single'; + + $tcinfo = $this->tcaseMgr->get_by_id($argsObj->tcase_id, + $argsObj->tcversion_id); + list ($prefix,) = $this->tcaseMgr->getPrefix($argsObj->tcase_id, + $argsObj->testproject_id); + $prefix .= $cfg->glue_character; + $external_id = $prefix . $tcinfo[0]['tc_external_id']; + if (! $this->tcaseMgr->delete($argsObj->tcase_id, $argsObj->tcversion_id)) { + $guiObj->action = ''; + $guiObj->sqlResult = $this->tcaseMgr->db->error_msg(); + } + + $guiObj->main_descr = lang_get('title_del_tc') . ":" . $external_id . + TITLE_SEP . htmlspecialchars($tcinfo[0]['name']); + + if ($argsObj->tcversion_id == testcase::ALL_VERSIONS) { + $guiObj->refreshTree = 1; + logAuditEvent(TLS("audit_testcase_deleted", $external_id), "DELETE", + $argsObj->tcase_id, "testcases"); + $guiObj->user_feedback = sprintf(lang_get('tc_deleted'), + ":" . $external_id . TITLE_SEP . $tcinfo[0]['name']); + } else { + $guiObj->main_descr .= " " . lang_get('version') . " " . + $tcinfo[0]['version']; + // When deleting JUST one version, there is no need to refresh tree + $guiObj->refreshTree = 0; + logAuditEvent( + TLS("audit_testcase_version_deleted", $tcinfo[0]['version'], + $external_id), "DELETE", $argsObj->tcase_id, "testcases"); + $guiObj->user_feedback = sprintf(lang_get('tc_version_deleted'), + $tcinfo[0]['name'], $tcinfo[0]['version']); + } + + $guiObj->testcase_name = $tcinfo[0]['name']; + $guiObj->testcase_id = $argsObj->tcase_id; + + $templateCfg = templateConfiguration('tcDelete'); + $guiObj->template = $templateCfg->default_template; + return $guiObj; + } + + /** + * createStep + */ + public function createStep(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $guiObj->main_descr = sprintf(lang_get('create_step'), + $guiObj->testcase['tc_external_id'] . ':' . $guiObj->testcase['name'], + $guiObj->testcase['version']); + + $max_step = $this->tcaseMgr->get_latest_step_number( + $argsObj->tcversion_id); + $max_step ++; + + $guiObj->step_number = $max_step; + $guiObj->step_exec_type = $guiObj->testcase['execution_type']; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $guiObj->step_set = $this->tcaseMgr->get_step_numbers( + $argsObj->tcversion_id); + $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",", + array_keys($guiObj->step_set)); + $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL, + $argsObj->tcase_id, $argsObj->tcversion_id); + + // Get all existent steps + $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); + + $templateCfg = templateConfiguration('tcStepEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->action = __FUNCTION__; + + return $guiObj; + } + + /** + * doCreateStep + */ + public function doCreateStep(&$argsObj, $request, $doAndExit = false) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + $guiObj->main_descr = sprintf(lang_get('create_step'), + $guiObj->testcase['tc_external_id'] . ':' . $guiObj->testcase['name'], + $guiObj->testcase['version']); + + $new_step = $this->tcaseMgr->get_latest_step_number( + $argsObj->tcversion_id); + $new_step ++; + $op = $this->tcaseMgr->create_step($argsObj->tcversion_id, $new_step, + $argsObj->steps, $argsObj->expected_results, $argsObj->exec_type); + + $guiObj->doExit = false; + if ($op['status_ok']) { + $guiObj->doExit = $doAndExit; + $guiObj->user_feedback = sprintf(lang_get('step_number_x_created'), + $argsObj->step_number); + $guiObj->step_exec_type = $guiObj->testcase['execution_type']; + $guiObj->cleanUpWebEditor = true; + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + $this->initTestCaseBasicInfo($argsObj, $guiObj); + } + + if (! $guiObj->doExit) { + $guiObj->action = __FUNCTION__; + + // Get all existent steps + $guiObj->tcaseSteps = $this->tcaseMgr->get_steps( + $argsObj->tcversion_id); + $max_step = $this->tcaseMgr->get_latest_step_number( + $argsObj->tcversion_id); + $max_step ++; + $guiObj->step_number = $max_step; + + $guiObj->step_set = $this->tcaseMgr->get_step_numbers( + $argsObj->tcversion_id); + $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",", + array_keys($guiObj->step_set)); + $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL, + $argsObj->tcase_id, $argsObj->tcversion_id); + + $templateCfg = templateConfiguration('tcStepEdit'); + $guiObj->template = $templateCfg->default_template; + } + return $guiObj; + } + + /** + * doCreateStepAndExit + */ + public function doCreateStepAndExit(&$argsObj, $request) + { + $guiObj = $this->doCreateStep($argsObj, $request, true); + if ($guiObj->doExit) { + // when working on step, refreshing tree is nonsense + $argsObj->refreshTree = 0; + + $opt = array( + 'updateCFOnDB' => ! self::UPDATECFONDB + ); + $this->show($argsObj, $request, array( + 'status_ok' => true + ), $opt); + exit(); + } else { + return $guiObj; + } + } + + /** + * editStep + */ + public function editStep(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $stepInfo = $this->tcaseMgr->get_step_by_id($argsObj->step_id); + + $oWebEditorKeys = array( + 'steps' => 'actions', + 'expected_results' => 'expected_results' + ); + foreach ($oWebEditorKeys as $key => $field) { + $argsObj->$key = $stepInfo[$field]; + $guiObj->$key = $stepInfo[$field]; + } + + $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'), + $stepInfo['step_number'], + $guiObj->testcase['tc_external_id'] . ':' . $guiObj->testcase['name'], + $guiObj->testcase['version']); + + $guiObj->tcase_id = $argsObj->tcase_id; + $guiObj->tcversion_id = $argsObj->tcversion_id; + $guiObj->step_id = $argsObj->step_id; + $guiObj->step_exec_type = $stepInfo['execution_type']; + $guiObj->step_number = $stepInfo['step_number']; + + // Get all existent steps + $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); + + $guiObj->step_set = $this->tcaseMgr->get_step_numbers( + $argsObj->tcversion_id); + $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",", + array_keys($guiObj->step_set)); + + $templateCfg = templateConfiguration('tcStepEdit'); + $guiObj->template = $templateCfg->default_template; + $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL, + $argsObj->tcase_id, $argsObj->tcversion_id); + + return $guiObj; + } + + /** + * doUpdateStep + */ + public function doUpdateStep(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + try { + $stepInfo = $this->tcaseMgr->get_step_by_id($argsObj->step_id); + } catch (Exception $e) { + echo $e->getMessage(); + } + + $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'), + $stepInfo['step_number'], + $guiObj->testcase['tc_external_id'] . ':' . $guiObj->testcase['name'], + $guiObj->testcase['version']); + + $this->tcaseMgr->update_step($argsObj->step_id, $argsObj->step_number, + $argsObj->steps, $argsObj->expected_results, $argsObj->exec_type); + + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $guiObj->tcversion_id = $argsObj->tcversion_id; + $guiObj->step_id = $argsObj->step_id; + $guiObj->step_number = $stepInfo['step_number']; + $guiObj->step_exec_type = $argsObj->exec_type; + + return $this->editStep($argsObj, $request); + } + + /** + * doUpdateStepAndExit + */ + public function doUpdateStepAndExit(&$argsObj, $request) + { + $this->doUpdateStep($argsObj, $request); + + // when working on step, refreshing tree is nonsense + $argsObj->refreshTree = 0; + $opt = array( + 'updateCFOnDB' => ! self::UPDATECFONDB + ); + $this->show($argsObj, $request, array( + 'status_ok' => true + ), $opt); + } + + /** + * doReorderSteps + */ + public function doReorderSteps(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->main_descr = lang_get('test_case'); + $this->tcaseMgr->set_step_number($argsObj->step_set); + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $argsObj->refreshTree = 0; + $opt = array( + 'updateCFOnDB' => ! self::UPDATECFONDB + ); + $this->show($argsObj, $request, array( + 'status_ok' => true + ), $opt); + exit(); + } + + /** + * doDeleteStep + */ + public function doDeleteStep(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + + $guiObj->main_descr = lang_get('test_case'); + $guiObj->viewerArgs = array(); + $guiObj->refreshTree = 0; + $step_node = $this->tcaseMgr->tree_manager->get_node_hierarchy_info( + $argsObj->step_id); + + $tcversion_node = $this->tcaseMgr->tree_manager->get_node_hierarchy_info( + $step_node['parent_id']); + $argsObj->tcversion_id = $step_node['parent_id']; + $argsObj->tcase_id = $tcversion_node['parent_id']; + + $guiObj->user_feedback = ''; + $this->tcaseMgr->delete_step_by_id($argsObj->step_id); + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $argsObj->refreshTree = 0; + $opt = array( + 'updateCFOnDB' => ! self::UPDATECFONDB + ); + $this->show($argsObj, $request, array( + 'status_ok' => true + ), $opt); + exit(); + } + + /** + * doCopyStep + */ + public function doCopyStep(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + // need to document difference bewteen these two similar concepts + $guiObj->action = __FUNCTION__; + $guiObj->operation = 'doUpdateStep'; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'), + $argsObj->step_number, + $guiObj->testcase['tc_external_id'] . ':' . $guiObj->testcase['name'], + $guiObj->testcase['version']); + + $new_step = $this->tcaseMgr->get_latest_step_number( + $argsObj->tcversion_id); + $new_step ++; + + $source_info = $this->tcaseMgr->get_steps($argsObj->tcversion_id, + $argsObj->step_number); + $source_info = current($source_info); + $op = $this->tcaseMgr->create_step($argsObj->tcversion_id, $new_step, + $source_info['actions'], $source_info['expected_results'], + $source_info['execution_type']); + + if ($op['status_ok']) { + $guiObj->user_feedback = sprintf( + lang_get('step_number_x_created_as_copy'), $new_step, + $argsObj->step_number); + $guiObj->step_exec_type = TESTCASE_EXECUTION_TYPE_MANUAL; + + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + $this->initTestCaseBasicInfo($argsObj, $guiObj); + } + + // Get all existent steps + $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); + + // After copy I would like to return to target step in edit mode, + // is enough to set $guiObj->step_number to target test step --> FOUND THIS is WRONG + // generated BUGID 4410 + $guiObj->step_number = $argsObj->step_number; + $guiObj->step_id = $argsObj->step_id; + + $guiObj->step_set = $this->tcaseMgr->get_step_numbers( + $argsObj->tcversion_id); + $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",", + array_keys($guiObj->step_set)); + $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL, + $argsObj->tcase_id, $argsObj->tcversion_id); + + $templateCfg = templateConfiguration('tcStepEdit'); + $guiObj->template = $templateCfg->default_template; + return $guiObj; + } + + /** + * doInsertStep + */ + public function doInsertStep(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + // Get all existent steps - info needed to do renumbering + $stepNumberSet = array(); + $existentSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); + $stepsQty = count($existentSteps); + for ($idx = 0; $idx < $stepsQty; $idx ++) { + $stepNumberSet[$idx] = $existentSteps[$idx]['step_number']; + $stepIDSet[$idx] = $existentSteps[$idx]['id']; + } + + $stepInfo = $this->tcaseMgr->get_step_by_id($argsObj->step_id); + $newStepNumber = $stepInfo['step_number'] + 1; + $op = $this->tcaseMgr->create_step($argsObj->tcversion_id, + $newStepNumber, '', ''); + $guiObj->main_descr = sprintf(lang_get('edit_step_number_x'), + $newStepNumber, + $guiObj->testcase['tc_external_id'] . ':' . $guiObj->testcase['name'], + $guiObj->testcase['version']); + + if ($op['status_ok']) { + $guiObj->user_feedback = sprintf(lang_get('step_number_x_created'), + $newStepNumber); + $guiObj->step_exec_type = TESTCASE_EXECUTION_TYPE_MANUAL; + $guiObj->cleanUpWebEditor = true; + + // renumber steps only if new step hits an existent step number + $hitPos = array_search($newStepNumber, $stepNumberSet); + if ($hitPos !== false) { + // Process starts from this position + $just_renumbered = array( + 'pos' => $hitPos, + 'value' => $newStepNumber + 1 + ); + $renumbered[$stepIDSet[$hitPos]] = $just_renumbered['value']; + + // now check if new renumbered collides with next + // if not nothing needs to be done + // if yes need to loop + $startFrom = $hitPos + 1; + $endOn = count($stepNumberSet); + for ($jdx = $startFrom; $jdx < $endOn; $jdx ++) { + if ($stepNumberSet[$jdx] == $just_renumbered['value']) { + $just_renumbered['value'] ++; + $renumbered[$stepIDSet[$jdx]] = $just_renumbered['value']; + } + } + $this->tcaseMgr->set_step_number($renumbered); + } + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + $this->initTestCaseBasicInfo($argsObj, $guiObj); + } + + // Get all existent steps - updated + $guiObj->tcaseSteps = $this->tcaseMgr->get_steps($argsObj->tcversion_id); + $guiObj->action = __FUNCTION__; + $guiObj->step_number = $newStepNumber; + $guiObj->step_id = $op['id']; + + $guiObj->step_set = $this->tcaseMgr->get_step_numbers( + $argsObj->tcversion_id); + $guiObj->step_set = is_null($guiObj->step_set) ? '' : implode(",", + array_keys($guiObj->step_set)); + $guiObj->loadOnCancelURL = sprintf($guiObj->loadOnCancelURL, + $argsObj->tcase_id, $argsObj->tcversion_id); + $templateCfg = templateConfiguration('tcStepEdit'); + $guiObj->template = $templateCfg->default_template; + return $guiObj; + } + + /** + */ + public function doResequenceSteps(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + if ($argsObj->stepSeq != '') { + $xx = explode('&', $argsObj->stepSeq); + $point = 1; + foreach ($xx as $step_id) { + $renumbered[$step_id] = $point ++; + } + } else { + // Get all existent steps - info needed to do renumbering + $stepSet = $this->tcaseMgr->get_steps($argsObj->tcversion_id); + $stepsQty = count($stepSet); + for ($idx = 0; $idx < $stepsQty; $idx ++) { + $renumbered[$stepSet[$idx]['id']] = $idx + 1; + } + } + + $this->tcaseMgr->set_step_number($renumbered); + + $guiObj->template = "archiveData.php?version_id={$guiObj->tcversion_id}&" . + "tcversion_id={$guiObj->tcversion_id}&" . + "edit=testcase&id={$guiObj->tcase_id}&" . + "show_mode={$guiObj->show_mode}"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + $guiObj->user_feedback = ''; + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + return $guiObj; + } + + /** + */ + public function setImportance(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $this->tcaseMgr->setImportance($argsObj->tcversion_id, + $argsObj->importance); + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + + // set up for rendering + $guiObj->template = "archiveData.php?version_id={$guiObj->tcversion_id}&" . + "tcversion_id={$guiObj->tcversion_id}&" . + "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + $guiObj->user_feedback = ''; + return $guiObj; + } + + /** + */ + public function setStatus(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $this->tcaseMgr->setStatus($argsObj->tcversion_id, $argsObj->status); + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + + // set up for rendering + $guiObj->template = "archiveData.php?version_id={$guiObj->tcversion_id}&" . + "tcversion_id={$guiObj->tcversion_id}&" . + "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + $guiObj->user_feedback = ''; + return $guiObj; + } + + /** + */ + public function setExecutionType(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $opx = array( + 'updSteps' => $argsObj->applyExecTypeChangeToAllSteps + ); + $this->tcaseMgr->setExecutionType($argsObj->tcversion_id, + $argsObj->exec_type, $opx); + + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + + // set up for rendering + $guiObj->template = "archiveData.php?version_id={$guiObj->tcversion_id}&" . + "tcversion_id={$guiObj->tcversion_id}&" . + "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + $guiObj->user_feedback = ''; + return $guiObj; + } + + /** + */ + public function setEstimatedExecDuration(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $this->tcaseMgr->setEstimatedExecDuration($argsObj->tcversion_id, + $argsObj->estimatedExecDuration); + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + + // set up for rendering + $guiObj->template = "archiveData.php?version_id={$guiObj->tcversion_id}&" . + "tcversion_id={$guiObj->tcversion_id}&" . + "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + $guiObj->user_feedback = ''; + return $guiObj; + } + + /** + */ + public function show(&$argsObj, $request, $userFeedback, $opt = null) + { + $smartyObj = new TLSmarty(); + + $options = array( + 'updateCFOnDB' => true, + 'updateTPlanLinkToTCV' => false + ); + $options = array_merge($options, (array) $opt); + + $updateCFOnDB = $options['updateCFOnDB']; + + $guiObj = $this->initGuiBean($argsObj); + $identity = $this->buildIdentity($argsObj); + + $guiObj->uploadOp = property_exists($argsObj, 'uploadOp') ? $argsObj->uploadOp : ''; + + $guiObj->viewerArgs = array(); + $guiObj->refreshTree = ($argsObj->refreshTree && + $userFeedback['status_ok']) ? 1 : 0; + $guiObj->has_been_executed = $argsObj->has_been_executed; + $guiObj->steps_results_layout = config_get('spec_cfg')->steps_results_layout; + $guiObj->user_feedback = ''; + + $guiObj->direct_link = $this->tcaseMgr->buildDirectWebLink( + $_SESSION['basehref'], $argsObj->tcase_id, $argsObj->testproject_id); + + if ($userFeedback['status_ok']) { + if ($options['updateTPlanLinkToTCV']) { + $guiObj->updateTPlanLinkToTCV = true; + $guiObj->show_mode = 'editOnExec'; + + // @20190127 the only useful thing there may be is the Rabbit + $guiObj->additionalURLPar = "&updateTCVToThis=" . + $identity->version_id . "&followTheWhiteRabbit=1"; + $guiObj->closeMyWindow = 1; + } + + $guiObj->user_feedback = ''; + if ($updateCFOnDB) { + $cfCtx = array( + 'tproject_id' => $identity->tproject_id, + 'enabled' => 1, + 'node_type' => 'testcase' + ); + $cf_map = $this->tcaseMgr->cfield_mgr->getLinkedCfieldsAtDesign( + $cfCtx); + + $this->tcaseMgr->cfield_mgr->design_values_to_db($request, + $identity->version_id, $cf_map); + } + + $guiObj->attachments[$identity->version_id] = getAttachmentInfosFrom( + $this->tcaseMgr, $identity->version_id); + } else { + $guiObj->viewerArgs['user_feedback'] = $guiObj->user_feedback = $userFeedback['msg']; + } + + $guiObj->viewerArgs['refreshTree'] = $guiObj->refreshTree; + $guiObj->viewerArgs['user_feedback'] = $guiObj->user_feedback; + + $this->tcaseMgr->show($smartyObj, $guiObj, $identity, $this->grants); + exit(); + } + + /** + */ + private function buildIdentity($cred) + { + $idy = new stdClass(); + if (property_exists($cred, 'tproject_id')) { + $idy->tproject_id = $cred->tproject_id; + } elseif (property_exists($cred, 'testproject_id')) { + $idy->tproject_id = $cred->testproject_id; + } else { + throw new Exception( + __METHOD__ . ' EXCEPTION: test project ID, is mandatory'); + } + $idy->tproject_id = intval($idy->tproject_id); + $idy->id = intval($cred->tcase_id); + $idy->version_id = $cred->tcversion_id; + return $idy; + } + + /** + */ + public function doAddRelation(&$argsObj, &$request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + + $this->initTestCaseBasicInfo($argsObj, $guiObj, + array( + 'accessByStepID' => false + )); + + if ($argsObj->destination_tcase_id > 0) { + $relTypeInfo = explode('_', $argsObj->relation_type); + $source_id = $argsObj->tcase_id; + $destination_id = $argsObj->destination_tcase_id; + if ($relTypeInfo[1] == "destination") { + $source_id = $argsObj->destination_tcase_id; + $destination_id = $argsObj->tcase_id; + } + + $ret = $this->tcaseMgr->addRelation($source_id, $destination_id, + $relTypeInfo[0], $argsObj->user_id); + $guiObj->user_feedback = sprintf(lang_get($ret['msg']), + $argsObj->relation_destination_tcase); + } else { + $guiObj->user_feedback = sprintf( + lang_get('testcase_doesnot_exists'), + $argsObj->relation_destination_tcase); + } + + // set up for rendering + // It's OK put fixed 0 on version_id other functions on the chain to do the display know how to manage this + $guiObj->template = "archiveData.php?version_id=0&" . + "tcversion_id={$guiObj->tcversion_id}&" . + "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + if ($guiObj->user_feedback != '') { + $guiObj->template .= "&add_relation_feedback_msg=" . + urlencode($guiObj->user_feedback); + } + return $guiObj; + } + + /** + */ + public function doDeleteRelation(&$argsObj, &$request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + + $this->initTestCaseBasicInfo($argsObj, $guiObj, + array( + 'accessByStepID' => false + )); + + if ($argsObj->relation_id > 0) { + $this->tcaseMgr->deleteRelationByID($argsObj->relation_id); + } + + // set up for rendering + $guiObj->template = "archiveData.php?edit=testcase&" . + "id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . + "&caller=delRel"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + return $guiObj; + } + + /** + * doUpdateStepAndExit + */ + public function doUpdateStepAndInsert(&$argsObj, $request) + { + $this->doUpdateStep($argsObj, $request); + return $this->doInsertStep($argsObj, $request); + } + + /** + */ + public function removeKeyword(&$argsObj, &$request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + + $this->initTestCaseBasicInfo($argsObj, $guiObj, + array( + 'accessByStepID' => false + )); + + if ($argsObj->tckw_link_id > 0) { + $this->tcaseMgr->deleteKeywordsByLink($guiObj->tcase_id, + $argsObj->tckw_link_id, testcase::AUDIT_ON); + } + + // set up for rendering + $guiObj->template = "archiveData.php?edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . + "&caller=removeKeyword"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + return $guiObj; + } + + public function freeze(&$argsObj, $request) + { + $argsObj->isOpen = 0; + return $this->setIsOpen($argsObj, $request); + } + + public function unfreeze(&$argsObj, $request) + { + $argsObj->isOpen = 1; + return $this->setIsOpen($argsObj, $request); + } + + /** + */ + public function setIsOpen(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $this->tcaseMgr->setIsOpen(null, $argsObj->tcversion_id, + $argsObj->isOpen); + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + + // set up for rendering + $guiObj->template = "archiveData.php?version_id={$guiObj->tcversion_id}&" . + "tcversion_id={$guiObj->tcversion_id}&" . + "edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + $guiObj->user_feedback = ''; + return $guiObj; + } + + /** + */ + public function setActiveAttr(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + $this->tcaseMgr->updateActiveStatus($argsObj->tcase_id, + $argsObj->tcversion_id, $argsObj->activeAttr); + + $this->tcaseMgr->update_last_modified($argsObj->tcversion_id, + $argsObj->user_id); + + $lk = 'audit_tcversion_deactivated'; + $pre = 'DE'; + if ($argsObj->activeAttr) { + $pre = ''; + $lk = 'audit_tcversion_activated'; + } + + logAuditEvent( + TLS($lk, $guiObj->testcase['name'], $guiObj->testcase['version']), + "{$pre}ACTIVATE", "testcases"); + + $this->show($argsObj, $request, [ + 'status_ok' => 1 + ], [ + 'updateCFOnDB' => false + ]); + exit(); + } + + /** + */ + public function addKeyword(&$argsObj, &$request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + + $this->initTestCaseBasicInfo($argsObj, $guiObj, + array( + 'accessByStepID' => false + )); + + $tcExternalID = $guiObj->testcase['tc_external_id']; + if (null != $argsObj->free_keywords && count($argsObj->free_keywords) > 0) { + $this->tcaseMgr->addKeywords($guiObj->tcase_id, + $guiObj->tcversion_id, $argsObj->free_keywords); + + $info = $this->tprojectMgr->get_by_id($this->tproject_id); + $cfx = config_get('keywords')->byTestProject; + if (isset($cfx[$info['prefix']]) && + $cfx[$info['prefix']]['addTCLinkIntoITS'] && + $info['issue_tracker_enabled']) { + + $it_mgr = new tlIssueTracker($this->db); + $argsObj->itsCfg = $it_mgr->getLinkedTo($this->tproject_id); + $its = $it_mgr->getInterfaceObject($this->tproject_id); + if (method_exists($its, 'addNote')) { + $dl = sprintf(lang_get('dlToTCSpecPVCode'), $tcExternalID) . + ' ' . lang_get('dlToTCSpecPV') . ' ' . + $this->tcaseMgr->buildDirectWebLink( + $_SESSION['basehref'], $argsObj->tcase_id, + $argsObj->testproject_id); + + // Get keyword for human beins + $tbl = tlObject::getDBTables(array( + 'keywords' + )); + $inClause = "'" . implode("','", $argsObj->free_keywords) . + "'"; + $sql = "SELECT id,keyword FROM {$tbl['keywords']} + WHERE id IN($inClause) "; + $kwSet = $this->db->fetchRowsIntoMap($sql, 'id'); + + $strToDel = isset($cfx[$info['prefix']]['prefix']) ? $cfx[$info['prefix']]['prefix'] : ''; + $strToDel = trim($strToDel); + foreach ($argsObj->free_keywords as $kw) { + if ('' == $strToDel) { + $kwv = $kwSet[$kw]['keyword']; + } else { + $kwv = str_replace($strToDel, '', + $kwSet[$kw]['keyword']); + } + try { + $its->addNote($kwv, $dl); + } catch (Exception $e) { + echo 'Silent Failure?'; + } + } + } + } + } + + // set up for rendering + $guiObj->template = "archiveData.php?edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . + "&caller=addKeyword"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + return $guiObj; + } + + /** + * + * @used by tcEdit.php + * @use tcaseMgr->updateLatestTPlanLinkToTCV() + */ + public function updateTPlanLinkToTCV($argsObj, $request) + { + $this->tcaseMgr->updateLatestTPlanLinkToTCV($argsObj->tcversion_id, + $argsObj->tplan_id); + + $opt = array( + 'updateTPlanLinkToTCV' => true + ); + + $this->show($argsObj, $request, array( + 'status_ok' => 1 + ), $opt); + } + + /** + * doStepOperationExit + */ + public function doStepOperationExit(&$argsObj, $request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + $guiObj->step_exec_type = $argsObj->exec_type; + $guiObj->tcversion_id = $argsObj->tcversion_id; + + $this->initTestCaseBasicInfo($argsObj, $guiObj); + $guiObj->main_descr = sprintf(lang_get('create_step'), + $guiObj->testcase['tc_external_id'] . ':' . $guiObj->testcase['name'], + $guiObj->testcase['version']); + $guiObj->cleanUpWebEditor = true; + $this->initTestCaseBasicInfo($argsObj, $guiObj); + + // when working on step, refreshing tree is nonsense + $argsObj->refreshTree = 0; + + $opt = array( + 'updateCFOnDB' => ! self::UPDATECFONDB + ); + $this->show($argsObj, $request, array( + 'status_ok' => true + ), $opt); + exit(); + } + + /** + */ + public function addPlatform(&$argsObj, &$request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + + $this->initTestCaseBasicInfo($argsObj, $guiObj, + array( + 'accessByStepID' => false + )); + + if (null != $argsObj->free_platforms) { + $this->tcaseMgr->addPlatforms($guiObj->tcase_id, + $guiObj->tcversion_id, $argsObj->free_platforms); + } + + // set up for rendering + $guiObj->template = "archiveData.php?edit=testcase&id={$guiObj->tcase_id}&show_mode={$guiObj->show_mode}" . + "&caller=addPlatform"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + + return $guiObj; + } + + /** + */ + public function removePlatform(&$argsObj, &$request) + { + $guiObj = $this->initGuiBean($argsObj); + $guiObj->user_feedback = ''; + + $this->initTestCaseBasicInfo($argsObj, $guiObj, + array( + 'accessByStepID' => false + )); + + if ($argsObj->tcplat_link_id > 0) { + $this->tcaseMgr->deletePlatformsByLink($guiObj->tcase_id, + $argsObj->tcplat_link_id, testcase::AUDIT_ON); + } + + // set up for rendering + $guiObj->template = "archiveData.php?edit=testcase&id={$guiObj->tcase_id}" . + "&show_mode={$guiObj->show_mode}" . "&caller=removePlatform"; + + if (property_exists($guiObj, 'tplan_id')) { + $guiObj->template .= "&tplan_id={$guiObj->tplan_id}"; + } + return $guiObj; + } +} diff --git a/lib/usermanagement/rolesEdit.php b/lib/usermanagement/rolesEdit.php index 5dd50bd197..ab9b5aac6f 100644 --- a/lib/usermanagement/rolesEdit.php +++ b/lib/usermanagement/rolesEdit.php @@ -1,313 +1,361 @@ -basehref,$editorCfg) ; -$owebeditor->Value = getItemTemplateContents('role_template', $owebeditor->InstanceName, null); -$canManage = $args->user->hasRight($db,"role_management") ? true : false; - -switch ($args->doAction) { - case 'create': - $gui->main_title = $lbl["action_{$args->doAction}_role"]; - break; - - case 'edit': - $op->role = tlRole::getByID($db,$args->roleid); - $gui->main_title = $lbl["action_{$args->doAction}_role"]; - break; - - case 'doCreate': - case 'doUpdate': - case 'duplicate': - if ($canManage) { - $op = doOperation($db,$args,$args->doAction); - $templateCfg->template = $op->template; - } - break; - - default: - break; -} - -$gui = complete_gui($db,$gui,$args,$op->role,$owebeditor); -$gui->userFeedback = $op->userFeedback; - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -// $smarty->assign('highlight',$gui->highlight); -renderGui($smarty,$args,$templateCfg); - -/** - * - */ -function init_args() -{ - $_REQUEST = strings_stripSlashes($_REQUEST); - - $iParams = array("rolename" => array("POST",tlInputParameter::STRING_N,0,100), - "roleid" => array("REQUEST",tlInputParameter::INT_N), - "doAction" => array("REQUEST",tlInputParameter::STRING_N,0,100), - "notes" => array("POST",tlInputParameter::STRING_N), - "grant" => array("POST",tlInputParameter::ARRAY_STRING_N)); - - $args = new stdClass(); - $pParams = I_PARAMS($iParams,$args); - $args->basehref = $_SESSION['basehref']; - $args->user = $_SESSION['currentUser']; - - return $args; -} - -/** - * - */ -function doOperation(&$dbHandler,$argsObj,$operation) -{ - $op = new stdClass(); - $op->role = new tlRole(); - $op->userFeedback = null; - $op->template = 'rolesEdit.tpl'; - - switch($operation) - { - - case 'doCreate': - case 'doUpdate': - $rights = implode("','",array_keys($argsObj->grant)); - $op->role->rights = tlRight::getAll($dbHandler,"WHERE description IN ('{$rights}')"); - $op->role->name = $argsObj->rolename; - $op->role->description = $argsObj->notes; - $op->role->dbID = $argsObj->roleid; - break; - - case 'duplicate': - $op->role = tlRole::getByID($dbHandler,$argsObj->roleid); - $op->role->dbID = null; - $op->role->name = generateUniqueName($op->role->name); - - break; - } - - - $result = $op->role->writeToDB($dbHandler); - if ($result >= tl::OK) - { - $auditCfg = null; - switch($operation) - { - case 'doCreate': - case 'duplicate': - $auditCfg['msg'] = "audit_role_created"; - $auditCfg['activity'] = "CREATE"; - break; - - case 'doUpdate': - $auditCfg['msg'] = "audit_role_saved"; - $auditCfg['activity'] = "SAVE"; - break; - } - - logAuditEvent(TLS($auditCfg['msg'],$op->role->name),$auditCfg['activity'],$op->role->dbID,"roles"); - $op->template = null; - } - else - { - $op->userFeedback = getRoleErrorMessage($result); - } - - return $op; -} - - -function renderGui(&$smartyObj,&$argsObj,$templateCfg) -{ - $doRender = false; - switch($argsObj->doAction) - { - case "edit": - case "create": - $doRender = true; - $tpl = $templateCfg->default_template; - break; - - case "doCreate": - case "doUpdate": - if(!is_null($templateCfg->template)) - { - $doRender = true; - $tpl = $templateCfg->template; - } - else - { - header("Location: rolesView.php"); - exit(); - } - break; - - case "duplicate": - header("Location: rolesView.php"); - exit(); - break; - } - - if($doRender) - { - $smartyObj->display($templateCfg->template_dir . $tpl); - } -} - - -/* - function: getRightsCfg - - args : - - - returns: object - -*/ -function getRightsCfg() -{ - $cfg = new stdClass(); - $cfg->tplan_mgmt = config_get('rights_tp'); - $cfg->tcase_mgmt = config_get('rights_mgttc'); - $cfg->kword_mgmt = config_get('rights_kw'); - $cfg->tproject_mgmt = config_get('rights_product'); - $cfg->user_mgmt = config_get('rights_users'); - $cfg->req_mgmt = config_get('rights_req'); - $cfg->cfield_mgmt = config_get('rights_cf'); - $cfg->system_mgmt = config_get('rights_system'); - $cfg->platform_mgmt = config_get('rights_platforms'); - $cfg->issuetracker_mgmt = config_get('rights_issuetrackers'); - $cfg->codetracker_mgmt = config_get('rights_codetrackers'); - $cfg->execution = config_get('rights_executions'); - - return $cfg; -} - - -function initialize_gui(&$argsObj,$editorType) -{ - $gui = new stdClass(); - $gui->checkboxStatus = null; - $gui->userFeedback = null; - $gui->affectedUsers = null; - $gui->highlight = initialize_tabsmenu(); - $gui->editorType = $editorType; - $gui->roleCanBeEdited = ($argsObj->roleid != TL_ROLES_ADMIN); - - return $gui; -} - -/** - * - */ -function initialize_op() -{ - $op = new stdClass(); - $op->role = new tlRole(); - $op->userFeedback = ''; - - return $op; -} - -/** - * - */ -function complete_gui(&$dbHandler,&$guiObj,&$argsObj,&$roleObj,&$webEditorObj) -{ - $actionCfg['operation'] = array('create' => 'doCreate', 'edit' => 'doUpdate', - 'doCreate' => 'doCreate', 'doUpdate' => 'doUpdate', - 'duplicate' => 'duplicate'); - - $actionCfg['highlight'] = array('create' => 'create_role', 'edit' => 'edit_role', - 'doCreate' => 'create_role', - 'doUpdate' => 'edit_role', - 'duplicate' => 'create_role'); - - $guiObj->highlight = new stdClass(); - $kp = $actionCfg['highlight'][$argsObj->doAction]; - $guiObj->highlight->$kp = 1; - $guiObj->operation = $actionCfg['operation'][$argsObj->doAction]; - - $guiObj->role = $roleObj; - $guiObj->grants = getGrantsForUserMgmt($dbHandler,$_SESSION['currentUser']); - $guiObj->grants->mgt_view_events = $argsObj->user->hasRight($db,"mgt_view_events"); - $guiObj->rightsCfg = getRightsCfg(); - - $guiObj->disabledAttr = $guiObj->roleCanBeEdited ? ' ' : ' disabled="disabled" '; - - // Create status for all checkboxes and set to unchecked - foreach ($guiObj->rightsCfg as $grantDetails) { - foreach ($grantDetails as $grantCode => $grantDescription) { - $guiObj->checkboxStatus[$grantCode] = "" . $guiObj->disabledAttr; - } - } - - if($roleObj->dbID) - { - $webEditorObj->Value = $roleObj->description; - - // build checked attribute for checkboxes - if(sizeof($roleObj->rights)) - { - foreach($roleObj->rights as $key => $right) - { - $guiObj->checkboxStatus[$right->name] = ' checked="checked" ' . $guiObj->disabledAttr; - } - } - //get all users which are affected by changing the role definition - $guiObj->affectedUsers = $roleObj->getAllUsersWithRole($dbHandler); - } - - $guiObj->notes = $webEditorObj->CreateHTML(); - return $guiObj; -} - -function generateUniqueName($s) -{ - // sorry for the magic, but anyway user has to edit role to provide desired name - // IMHO this quick & dirty solution is OK - return substr($s . ' - Copy - ' . substr(sha1(rand()), 0, 50),0,100); -} - - -/** - * - */ -function initLabels() -{ - $tg = array('action_create_role' => null,'action_edit_role' => null); - $labels = init_labels($tg); - return $labels; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"role_management"); +basehref, $editorCfg); +$owebeditor->Value = getItemTemplateContents('role_template', + $owebeditor->InstanceName, null); +$canManage = $args->user->hasRight($db, "role_management") ? true : false; + +switch ($args->doAction) { + case 'create': + $gui->main_title = $lbl["action_{$args->doAction}_role"]; + break; + + case 'edit': + $op->role = tlRole::getByID($db, $args->roleid); + $gui->main_title = $lbl["action_{$args->doAction}_role"]; + break; + + case 'doCreate': + case 'doUpdate': + case 'duplicate': + if ($canManage) { + $op = doOperation($db, $args, $args->doAction); + $templateCfg->template = $op->template; + } + break; + + default: + break; +} + +$gui = completeGui($db, $gui, $args, $op->role, $owebeditor); +$gui->userFeedback = $op->userFeedback; + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +renderGui($smarty, $args, $templateCfg); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "rolename" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 100 + ), + "roleid" => array( + "REQUEST", + tlInputParameter::INT_N + ), + "doAction" => array( + "REQUEST", + tlInputParameter::STRING_N, + 0, + 100 + ), + "notes" => array( + "POST", + tlInputParameter::STRING_N + ), + "grant" => array( + "POST", + tlInputParameter::ARRAY_STRING_N + ) + ); + + $args = new stdClass(); + I_PARAMS($iParams, $args); + $args->basehref = $_SESSION['basehref']; + $args->user = $_SESSION['currentUser']; + + return $args; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param string $operation + * @return stdClass + */ +function doOperation(&$dbHandler, $argsObj, $operation) +{ + $op = new stdClass(); + $op->role = new tlRole(); + $op->userFeedback = null; + $op->template = 'rolesEdit.tpl'; + + switch ($operation) { + case 'doCreate': + case 'doUpdate': + $rights = implode("','", array_keys($argsObj->grant)); + $op->role->rights = tlRight::getAll($dbHandler, + "WHERE description IN ('{$rights}')"); + $op->role->name = $argsObj->rolename; + $op->role->description = $argsObj->notes; + $op->role->dbID = $argsObj->roleid; + break; + + case 'duplicate': + $op->role = tlRole::getByID($dbHandler, $argsObj->roleid); + $op->role->dbID = null; + $op->role->name = generateUniqueName($op->role->name); + + break; + } + + $result = $op->role->writeToDB($dbHandler); + if ($result >= tl::OK) { + $auditCfg = null; + switch ($operation) { + case 'doCreate': + case 'duplicate': + $auditCfg['msg'] = "audit_role_created"; + $auditCfg['activity'] = "CREATE"; + break; + + case 'doUpdate': + $auditCfg['msg'] = "audit_role_saved"; + $auditCfg['activity'] = "SAVE"; + break; + } + + logAuditEvent(TLS($auditCfg['msg'], $op->role->name), + $auditCfg['activity'], $op->role->dbID, "roles"); + $op->template = null; + } else { + $op->userFeedback = getRoleErrorMessage($result); + } + + return $op; +} + +/** + * + * @param TLSmarty $smartyObj + * @param stdClass $argsObj + * @param stdClass $templateCfg + */ +function renderGui(&$smartyObj, &$argsObj, $templateCfg) +{ + $doRender = false; + switch ($argsObj->doAction) { + case "edit": + case "create": + $doRender = true; + $tpl = $templateCfg->default_template; + break; + + case "doCreate": + case "doUpdate": + if (! is_null($templateCfg->template)) { + $doRender = true; + $tpl = $templateCfg->template; + } else { + header("Location: rolesView.php"); + exit(); + } + break; + + case "duplicate": + header("Location: rolesView.php"); + exit(); + break; + } + + if ($doRender) { + $smartyObj->display($templateCfg->template_dir . $tpl); + } +} + +/** + * + * @return stdClass + */ +function getRightsCfg() +{ + $cfg = new stdClass(); + $cfg->tplan_mgmt = config_get('rights_tp'); + $cfg->tcase_mgmt = config_get('rights_mgttc'); + $cfg->kword_mgmt = config_get('rights_kw'); + $cfg->tproject_mgmt = config_get('rights_product'); + $cfg->user_mgmt = config_get('rights_users'); + $cfg->req_mgmt = config_get('rights_req'); + $cfg->cfield_mgmt = config_get('rights_cf'); + $cfg->system_mgmt = config_get('rights_system'); + $cfg->platform_mgmt = config_get('rights_platforms'); + $cfg->issuetracker_mgmt = config_get('rights_issuetrackers'); + $cfg->codetracker_mgmt = config_get('rights_codetrackers'); + $cfg->execution = config_get('rights_executions'); + + return $cfg; +} + +/** + * + * @param stdClass $argsObj + * @param string $editorType + * @return stdClass + */ +function initializeGui(&$argsObj, $editorType) +{ + $gui = new stdClass(); + $gui->checkboxStatus = null; + $gui->userFeedback = null; + $gui->affectedUsers = null; + $gui->highlight = initializeTabsmenu(); + $gui->editorType = $editorType; + $gui->roleCanBeEdited = ($argsObj->roleid != TL_ROLES_ADMIN); + + return $gui; +} + +/** + * + * @return stdClass + */ +function initializeOp() +{ + $op = new stdClass(); + $op->role = new tlRole(); + $op->userFeedback = ''; + + return $op; +} + +/** + * + * @param database $dbHandler + * @param stdClass $guiObj + * @param stdClass $argsObj + * @param tlRole $roleObj + * @param ckeditorInterface $webEditorObj + * @return stdClass + */ +function completeGui(&$dbHandler, &$guiObj, &$argsObj, &$roleObj, &$webEditorObj) +{ + $actionCfg['operation'] = array( + 'create' => 'doCreate', + 'edit' => 'doUpdate', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate', + 'duplicate' => 'duplicate' + ); + + $actionCfg['highlight'] = array( + 'create' => 'create_role', + 'edit' => 'edit_role', + 'doCreate' => 'create_role', + 'doUpdate' => 'edit_role', + 'duplicate' => 'create_role' + ); + + $guiObj->highlight = new stdClass(); + $kp = $actionCfg['highlight'][$argsObj->doAction]; + $guiObj->highlight->$kp = 1; + $guiObj->operation = $actionCfg['operation'][$argsObj->doAction]; + + $guiObj->role = $roleObj; + $guiObj->grants = getGrantsForUserMgmt($dbHandler, $_SESSION['currentUser']); + $guiObj->grants->mgt_view_events = $argsObj->user->hasRight($db, + "mgt_view_events"); + $guiObj->rightsCfg = getRightsCfg(); + + $guiObj->disabledAttr = $guiObj->roleCanBeEdited ? ' ' : ' disabled="disabled" '; + + // Create status for all checkboxes and set to unchecked + foreach ($guiObj->rightsCfg as $grantDetails) { + foreach ($grantDetails as $grantCode => $grantDescription) { + $guiObj->checkboxStatus[$grantCode] = "" . $guiObj->disabledAttr; + } + } + + if ($roleObj->dbID) { + $webEditorObj->Value = $roleObj->description; + + // build checked attribute for checkboxes + if (count($roleObj->rights)) { + foreach ($roleObj->rights as $right) { + $guiObj->checkboxStatus[$right->name] = ' checked="checked" ' . + $guiObj->disabledAttr; + } + } + // get all users which are affected by changing the role definition + $guiObj->affectedUsers = $roleObj->getAllUsersWithRole($dbHandler); + } + + $guiObj->notes = $webEditorObj->CreateHTML(); + return $guiObj; +} + +/** + * + * @param boolean $s + * @return string + */ +function generateUniqueName($s) +{ + // sorry for the magic, but anyway user has to edit role to provide desired name + // IMHO this quick & dirty solution is OK + return substr($s . ' - Copy - ' . substr(sha1(rand()), 0, 50), 0, 100); +} + +/** + * + * @return array + */ +function initLabels() +{ + $tg = array( + 'action_create_role' => null, + 'action_edit_role' => null + ); + + return init_labels($tg); +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "role_management"); } diff --git a/lib/usermanagement/rolesView.php b/lib/usermanagement/rolesView.php index 926e0c8e60..7a56128166 100644 --- a/lib/usermanagement/rolesView.php +++ b/lib/usermanagement/rolesView.php @@ -1,119 +1,131 @@ -doAction) -{ - case 'delete': - $role = tlRole::getByID($db,$args->roleid,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); - if ($role) - { - $gui->affectedUsers = $role->getAllUsersWithRole($db); - $doDelete = (sizeof($gui->affectedUsers) == 0); - } - break; - - default: - break; +doAction) { + case 'delete': + $role = tlRole::getByID($db, $args->roleid, + tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); + if ($role) { + $gui->affectedUsers = $role->getAllUsersWithRole($db); + $doDelete = empty($gui->affectedUsers); + } + break; + + default: + break; +} + +$userFeedback = null; +if ($doDelete) { + // CSRF check + if (! is_null($args->csrfid) && ! is_null($args->csrftoken) && + csrfguard_validate_token($args->csrfid, $args->csrftoken)) { + // only NON SYSTEM ROLES CAN be deleted + if ($args->roleid > TL_LAST_SYSTEM_ROLE) { + $userFeedback = deleteRole($db, $args->roleid); + checkSessionValid($db); // refresh the current user + } + } else { + $msg = lang_get('CSRF_attack'); + tLog($msg, 'ERROR'); + die($msg); + } +} + +$gui->roles = tlRole::getAll($db, null, null, null, + tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('sqlResult', $userFeedback); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return object returns the arguments for the page + */ +function initArgs() +{ + $iParams = array( + "roleid" => array( + tlInputParameter::INT_N + ), + "csrfid" => array( + tlInputParameter::STRING_N, + 0, + 30 + ), + "csrftoken" => array( + tlInputParameter::STRING_N, + 0, + 128 + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 15 + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + $args->currentUser = $_SESSION['currentUser']; + + return $args; +} + +/** + * + * @param database $db + * @param stdClass $args + * @return stdClass + */ +function initializeGui(&$db, &$args) +{ + $gui = new stdClass(); + $gui->highlight = initializeTabsmenu(); + $gui->highlight->view_roles = 1; + $gui->grants = getGrantsForUserMgmt($db, $args->currentUser); + $gui->affectedUsers = null; + $gui->roleid = $args->roleid; + $gui->main_title = lang_get('role_management'); + $gui->role_id_replacement = config_get('role_replace_for_deleted_roles'); + $cfg = getWebEditorCfg('role'); + $gui->editorType = $cfg['type']; + + return $gui; +} + +/** + * + * @param database $db + * resource the database connection handle + * @param tlUser $user + * the current active user + * @return string returns true if the page can be accessed + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "role_management"); } - -$userFeedback = null; -if($doDelete) -{ - // CSRF check - if( !is_null($args->csrfid) && !is_null($args->csrftoken) && - csrfguard_validate_token($args->csrfid,$args->csrftoken) ) - { - // only NON SYSTEM ROLES CAN be deleted - if($args->roleid > TL_LAST_SYSTEM_ROLE) - { - $userFeedback = deleteRole($db,$args->roleid); - checkSessionValid($db); //refresh the current user - } - } - else - { - $msg = lang_get('CSRF_attack'); - tLog($msg,'ERROR'); - die($msg); - } -} - -$gui->roles = tlRole::getAll($db,null,null,null,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); - - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->assign('sqlResult',$userFeedback); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * @return object returns the arguments for the page - */ -function init_args() -{ - $iParams = array("roleid" => array(tlInputParameter::INT_N), - "csrfid" => array(tlInputParameter::STRING_N,0,30), - "csrftoken" => array(tlInputParameter::STRING_N,0,128), - "doAction" => array(tlInputParameter::STRING_N,0,15)); - - $args = new stdClass(); - $pParams = R_PARAMS($iParams,$args); - $args->currentUser = $_SESSION['currentUser']; - - return $args; -} - -/** - * - */ -function initializeGui(&$db,&$args) -{ - $gui = new stdClass(); - $gui->highlight = initialize_tabsmenu(); - $gui->highlight->view_roles = 1; - $gui->grants = getGrantsForUserMgmt($db,$args->currentUser); - $gui->affectedUsers = null; - $gui->roleid = $args->roleid; - $gui->main_title = lang_get('role_management'); - $gui->role_id_replacement = config_get('role_replace_for_deleted_roles'); - $cfg = getWebEditorCfg('role'); - $gui->editorType = $cfg['type']; - - return $gui; -} - - -/** - * @param $db resource the database connection handle - * @param $user the current active user - * - * @return boolean returns true if the page can be accessed - */ -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"role_management"); -} \ No newline at end of file diff --git a/lib/usermanagement/userInfo.php b/lib/usermanagement/userInfo.php index 753431e2ec..683fcb0ee5 100644 --- a/lib/usermanagement/userInfo.php +++ b/lib/usermanagement/userInfo.php @@ -1,184 +1,234 @@ -optLocale = config_get('locales'); - -$user = new tlUser($args->userID); -$user->readFromDB($db); - -$op = new stdClass(); -$op->auditMsg = null; -$op->user_feedback = null; -$op->status = tl::OK; -$update_title_bar = 0; - - -$doUpdate = false; -switch($args->doAction) { - case 'editUser': - $doUpdate = true; - foreach($args->user as $key => $value) { - $user->$key = $value; - } - $op->status = tl::OK; - $op->auditMsg = "audit_user_saved"; - $op->user_feedback = lang_get('result_user_changed'); - $update_title_bar = 1; - break; - - case 'changePassword': - $op = changePassword($db,$args,$user); - $doUpdate = false; - logAuditEvent(TLS($op->auditMsg,$user->login),"SAVE",$user->dbID,"users"); - break; - - case 'genAPIKey': - $op = generateAPIKey($args,$user); - break; +optLocale = config_get('locales'); + +$user = new tlUser($args->userID); +$user->readFromDB($db); + +$op = new stdClass(); +$op->auditMsg = null; +$op->user_feedback = null; +$op->status = tl::OK; +$update_title_bar = 0; + +$doUpdate = false; +switch ($args->doAction) { + case 'editUser': + $doUpdate = true; + foreach ($args->user as $key => $value) { + $user->$key = $value; + } + $op->status = tl::OK; + $op->auditMsg = "audit_user_saved"; + $op->user_feedback = lang_get('result_user_changed'); + $update_title_bar = 1; + break; + + case 'changePassword': + $op = changePassword($db, $args, $user); + $doUpdate = false; + logAuditEvent(TLS($op->auditMsg, $user->login), "SAVE", $user->dbID, + "users"); + break; + + case 'genAPIKey': + $op = generateAPIKey($args, $user); + break; +} + +if ($doUpdate) { + $op->status = $user->writeToDB($db); + if ($op->status >= tl::OK) { + logAuditEvent(TLS($op->auditMsg, $user->login), "SAVE", $user->dbID, + "users"); + $_SESSION['currentUser'] = $user; + setUserSession($db, $user->login, $args->userID, $user->globalRoleID, + $user->emailAddress, $user->locale); + } +} + +$loginHistory = new stdClass(); +$loginHistory->failed = $g_tlLogger->getAuditEventsFor($args->userID, "users", + "LOGIN_FAILED", 10); +$loginHistory->ok = $g_tlLogger->getAuditEventsFor($args->userID, "users", + "LOGIN", 10); + +if ($op->status != tl::OK && empty($op->user_feedback)) { + $op->user_feedback = getUserErrorMessage($op->status); +} +$user->readFromDB($db); + +// set a string if not generated key yet +if (null == $user->userApiKey) { + $user->userApiKey = TLS('none'); +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->assign('external_password_mgmt', + tlUser::isPasswordMgtExternal($user->authentication)); +$smarty->assign('user', $user); +$smarty->assign('api_ui_show', $user); +$smarty->assign('mgt_view_events', $user->hasRight($db, "mgt_view_events")); +$smarty->assign('loginHistory', $loginHistory); +$smarty->assign('user_feedback', $op->user_feedback); +$smarty->assign('update_title_bar', $update_title_bar); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $iParams = array( + "firstName" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 30 + ), + "lastName" => array( + "REQUEST", + tlInputParameter::STRING_N, + 0, + 30 + ), + "emailAddress" => array( + "REQUEST", + tlInputParameter::STRING_N, + 0, + 100 + ), + "locale" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 10 + ), + "oldpassword" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 32 + ), + "newpassword" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 32 + ), + "doAction" => array( + "POST", + tlInputParameter::STRING_N, + 0, + 15, + null, + 'checkDoAction' + ), + "userinfo_token" => array( + tlInputParameter::STRING_N, + 0, + 255 + ) + ); + + $pParams = I_PARAMS($iParams); + + $args = new stdClass(); + $args->user = new stdClass(); + $args->user->firstName = $pParams["firstName"]; + $args->user->lastName = $pParams["lastName"]; + $args->user->emailAddress = $pParams["emailAddress"]; + $args->user->locale = $pParams["locale"]; + $args->oldpassword = $pParams["oldpassword"]; + $args->newpassword = $pParams["newpassword"]; + $args->doAction = $pParams["doAction"]; + $args->userinfo_token = $pParams["userinfo_token"]; + + $args->userID = isset($_SESSION['currentUser']) ? $_SESSION['currentUser']->dbID : 0; + + return $args; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param tlUser $userMgr + * @return stdClass object with properties: status, user_feedback: string message for on screen feedback, + * auditMsg: to be written by logAudid + */ +function changePassword(&$dbHandler, &$argsObj, &$userMgr) +{ + $op = new stdClass(); + $op->status = $userMgr->comparePassword($dbHandler, $argsObj->oldpassword); + $op->user_feedback = ''; + $op->auditMsg = ''; + if ($op->status == tl::OK) { + $userMgr->setPassword($argsObj->newpassword, $userMgr->authentication); + $userMgr->writePasswordToDB($dbHandler); + $op->user_feedback = lang_get('result_password_changed'); + $op->auditMsg = "audit_user_pwd_saved"; + } + return $op; +} + +/** + * + * @param stdClass $argsObj + * @param tlUser $user + * @return stdClass + */ +function generateAPIKey(&$argsObj, &$user) +{ + $op = new stdClass(); + $op->status = tl::OK; + $op->user_feedback = null; + if ($user) { + $APIKey = new APIKey(); + if ($APIKey->addKeyForUser($argsObj->userID) < tl::OK) { + logAuditEvent(TLS("audit_user_apikey_set", $user->login), "CREATE", + $user->login, "users"); + $op->user_feedback = lang_get('result_apikey_create_ok'); + } + } + return $op; +} + +/** + * check function for tlInputParameter doAction + * + * @param string $input + * @return boolean + */ +function checkDoAction($input) +{ + $domain = array_flip(array( + 'editUser', + 'changePassword', + 'genAPIKey' + )); + return isset($domain[$input]) ? true : false; } - -if($doUpdate) { - $op->status = $user->writeToDB($db); - if ($op->status >= tl::OK) { - logAuditEvent(TLS($op->auditMsg,$user->login),"SAVE",$user->dbID,"users"); - $_SESSION['currentUser'] = $user; - setUserSession($db,$user->login, $args->userID, $user->globalRoleID, $user->emailAddress, $user->locale); - } -} - -$loginHistory = new stdClass(); -$loginHistory->failed = $g_tlLogger->getAuditEventsFor($args->userID,"users","LOGIN_FAILED",10); -$loginHistory->ok = $g_tlLogger->getAuditEventsFor($args->userID,"users","LOGIN",10); - -if ($op->status != tl::OK && empty($op->user_feedback)) { - $op->user_feedback = getUserErrorMessage($op->status); -} -$user->readFromDB($db); - -// set a string if not generated key yet -if (null == $user->userApiKey) { - $user->userApiKey = TLS('none'); -} - - - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->assign('external_password_mgmt',tlUser::isPasswordMgtExternal($user->authentication)); -$smarty->assign('user',$user); -$smarty->assign('api_ui_show',$user); -$smarty->assign('mgt_view_events',$user->hasRight($db,"mgt_view_events")); -$smarty->assign('loginHistory', $loginHistory); -$smarty->assign('user_feedback', $op->user_feedback); -$smarty->assign('update_title_bar',$update_title_bar); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - -/** - * - */ -function init_args() -{ - $iParams = array("firstName" => array("POST",tlInputParameter::STRING_N,0,30), - "lastName" => array("REQUEST",tlInputParameter::STRING_N,0,30), - "emailAddress" => array("REQUEST",tlInputParameter::STRING_N,0,100), - "locale" => array("POST",tlInputParameter::STRING_N,0,10), - "oldpassword" => array("POST",tlInputParameter::STRING_N,0,32), - "newpassword" => array("POST",tlInputParameter::STRING_N,0,32), - "doAction" => array("POST",tlInputParameter::STRING_N,0,15,null,'checkDoAction'), - "userinfo_token" => array(tlInputParameter::STRING_N, 0, 255)); - - $pParams = I_PARAMS($iParams); - - $args = new stdClass(); - $args->user = new stdClass(); - $args->user->firstName = $pParams["firstName"]; - $args->user->lastName = $pParams["lastName"]; - $args->user->emailAddress = $pParams["emailAddress"]; - $args->user->locale = $pParams["locale"]; - $args->oldpassword = $pParams["oldpassword"]; - $args->newpassword = $pParams["newpassword"]; - $args->doAction = $pParams["doAction"]; - $args->userinfo_token = $pParams["userinfo_token"]; - - $args->userID = isset($_SESSION['currentUser']) ? $_SESSION['currentUser']->dbID : 0; - - return $args; -} - -/* - function: changePassword - - args: - - returns: object with properties: - status - user_feedback: string message for on screen feedback - auditMsg: to be written by logAudid - -*/ -function changePassword(&$dbHandler,&$argsObj,&$userMgr) -{ - $op = new stdClass(); - $op->status = $userMgr->comparePassword($dbHandler,$argsObj->oldpassword); - $op->user_feedback = ''; - $op->auditMsg = ''; - if ($op->status == tl::OK) { - $userMgr->setPassword($argsObj->newpassword,$userMgr->authentication); - $userMgr->writePasswordToDB($dbHandler); - $op->user_feedback = lang_get('result_password_changed'); - $op->auditMsg = "audit_user_pwd_saved"; - } - return $op; -} - -/** - * - * - */ -function generateAPIKey(&$argsObj,&$user) -{ - $op = new stdClass(); - $op->status = tl::OK; - $op->user_feedback = null; - if ($user) - { - $APIKey = new APIKey(); - if ($APIKey->addKeyForUser($argsObj->userID) < tl::OK) - { - logAuditEvent(TLS("audit_user_apikey_set",$user->login),"CREATE",$user->login,"users"); - $op->user_feedback = lang_get('result_apikey_create_ok'); - } - } - return $op; -} - -/** - * check function for tlInputParameter doAction - * - */ -function checkDoAction($input) -{ - $domain = array_flip(array('editUser','changePassword','genAPIKey')); - $status_ok = isset($domain[$input]) ? true : false; - return $status_ok; -} \ No newline at end of file diff --git a/lib/usermanagement/usersAssign.php b/lib/usermanagement/usersAssign.php index d1c73eed25..cd4cb481da 100644 --- a/lib/usermanagement/usersAssign.php +++ b/lib/usermanagement/usersAssign.php @@ -1,600 +1,647 @@ -getImages(); - -$templateCfg = templateConfiguration(); - -$assignRolesFor = null; -$featureMgr = null; -$userFeatureRoles = null; -$doInitGui = true; - -$tprojectMgr = new testproject($db); -$tplanMgr = new testplan($db); - -$args = init_args($tprojectMgr); -$gui = initializeGui($db,$args); - -$lbl = initLabels(); - -$target = new stdClass(); -$target->testprojectID = null; -$target->testplanID = null; - - -switch($args->featureType) { - case "testproject": - $gui->highlight->assign_users_tproject = 1; - $gui->roles_updated = $lbl["test_project_user_roles_updated"]; - $gui->not_for_you = $lbl["testproject_roles_assign_disabled"]; - $gui->main_title = $lbl["assign_tproject_roles"]; - - $assignRolesFor = $args->featureType; - $target->testprojectID = $args->featureID > 0 ? $args->featureID : null; - $featureMgr = &$tprojectMgr; - break; - - case "testplan": - $gui->highlight->assign_users_tplan = 1; - $gui->roles_updated = lang_get("test_plan_user_roles_updated"); - $gui->not_for_you = lang_get("testplan_roles_assign_disabled"); - $gui->main_title = $lbl["assign_tplan_roles"]; - - $assignRolesFor = $args->featureType; - $target->testprojectID = $args->testprojectID; - $featureMgr = &$tplanMgr; - - $accessKey = 'private'; - if( $tprojectMgr->getPublicAttr($args->testprojectID) ) - { - $accessKey = 'public'; - } - $gui->tprojectAccessTypeImg = ''; - break; -} - -if ($args->featureID && $args->doUpdate && $featureMgr) { - if(checkRightsForUpdate($db,$args->user,$args->testprojectID,$args->featureType,$args->featureID)) { - doUpdate($db,$args,$featureMgr); - if( $gui->user_feedback == '' ) { - $gui->user_feedback = $gui->roles_updated; - } - } -} -// ------------------------------------------------------------------ -// Important: -// Must be done here after having done update, to get current information -$gui->users = tlUser::getAll($db,"WHERE active=1",null,null,tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); -checkSessionValid($db); -$args->user = $_SESSION['currentUser']; -// ------------------------------------------------------------------ - -switch($assignRolesFor) { - case 'testproject': - $info = getTestProjectEffectiveRoles($db,$tprojectMgr,$args,$gui->users); - list($gui->userFeatureRoles,$gui->features,$gui->featureID) = $info; - $target->testprojectID = $gui->featureID; - break; - - case 'testplan': - $info = getTestPlanEffectiveRoles($db,$tplanMgr,$tprojectMgr,$args,$gui->users); - if( is_null($info) ) { - $gui->user_feedback = lang_get('no_test_plans_available'); - } - list($gui->userFeatureRoles,$gui->features,$gui->featureID)=$info; - break; - -} - - -$gui->grants = getGrantsForUserMgmt($db,$args->user,$target->testprojectID,-1); -$gui->accessTypeImg = ''; - - -if(is_null($gui->features) || count($gui->features) == 0) { - $gui->features = null; - if( $gui->user_feedback == '' ) { - $gui->user_feedback = $gui->not_for_you; - } -} else { - $accessKey = 'vorsicht'; - if( isset($gui->features[$gui->featureID]) ) { - $accessKey = $gui->features[$gui->featureID]['is_public'] ? 'public' : 'private'; - $gui->accessTypeImg = ''; - } - $gui->accessTypeImg = ''; -} - -$gui->hintImg = ''; - -$smarty->assign('gui',$gui); -$smarty->display($templateCfg->template_dir . $templateCfg->default_template); - - -/** - * - * - */ -function init_args(&$tprojMgr) { - $iParams = array( - "featureType" => array(tlInputParameter::STRING_N,0,100), - "featureID" => array(tlInputParameter::INT_N), - "userRole" => array(tlInputParameter::ARRAY_INT), - "do_update" => array(tlInputParameter::STRING_N,0,100) - ); - - $pParams = R_PARAMS($iParams); - - $args = new stdClass(); - $args->featureType = $pParams["featureType"]; - $args->featureID = $pParams["featureID"]; - $args->map_userid_roleid = $pParams["userRole"]; - $args->doUpdate = ($pParams["do_update"] != "") ? 1 : 0; - - $args->testprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; - - $args->testplanID = 0; - - switch( $args->featureType ) { - case 'testproject': - break; - - case 'testplan': - if( $args->testprojectID <= 0 ) { - throw new Exception("INVALID Test Project ID", 1); - } - break; - } - - if( ($fid = intval($args->featureID)) > 0 ) { - $prop = $args->featureType .'ID'; - $args->$prop = $fid; - } - - - $prjInfo = $tprojMgr->get_by_id($args->testprojectID,array('output' => 'name')); - - $args->testprojectName = $prjInfo['name']; - - $args->user = $_SESSION['currentUser']; - $args->userID = $args->user->dbID; - - return $args; -} - - -/** - * checks runned when tyring to run this page, - * to be sure user has rights to use it. - * - */ -function checkRights(&$db,&$user) { - $result = false; - - $tprojMgr = new testproject($db); - $args = init_args($tprojMgr); - $answers = new stdClass(); - $answers->role_management = $user->hasRight($db,"role_management"); - - // Two checks needed: - // First on current test project without using test plan rights - // if this fails then check again adding current test plan - $answers->testplan_user_role_assignment = $user->hasRight($db,"testplan_user_role_assignment",$args->testprojectID,-1); - if($answers->testplan_user_role_assignment != "yes") { - $targetTestPlanID = null; - if($args->featureType == 'testplan') { - $targetTestPlanID = $args->featureID; - } - $answers->testplan_user_role_assignment = $user->hasRight($db,"testplan_user_role_assignment",null,$targetTestPlanID); - } - - $answers->user_role_assignment = $user->hasRight($db,"user_role_assignment",null,-1); - - foreach($answers as $key => $value) { - $answers->$key = $value == "yes" ? true : false; - $result = $result | $answers->$key; - } - - if(!$result && ($args->featureType == 'testproject')) { - $feature2check = $args->featureID; - if($args->featureID == 0 || is_null($args->featureID)) { - $feature2check = $args->testprojectID; - } - if($user->hasRight($db,"testproject_user_role_assignment",$feature2check,-1) == "yes") - { - $result = true; - } - } - - return $result; -} - -/** - * checkRightsForUpdate - * - */ -function checkRightsForUpdate(&$dbHandler,&$user,$testprojectID,$featureType,$featureID) -{ - $yes_no = "no"; - switch($featureType) - { - case 'testproject': - if($user->hasRight($dbHandler,"user_role_assignment",$featureID) == "yes" || - $user->hasRight($dbHandler,"testproject_user_role_assignment",$featureID,-1,true) == "yes") - { - $yes_no = "yes"; - } - break; - - case 'testplan': - $yes_no = $user->hasRight($dbHandler,"testplan_user_role_assignment", - $testprojectID,$featureID); - break; - } - - return ($yes_no == 'yes'); -} - - -/** - * getTestProjectEffectiveRoles - * - */ -function getTestProjectEffectiveRoles($dbHandler,&$objMgr,&$argsObj,$users) { - $features = null; - $gui_cfg = config_get('gui'); - - // Accessible means user has a role on test project ? - $opt = array('output' => 'map_of_map_full', 'order_by' => $gui_cfg->tprojects_combo_order_by); - $testprojects = $objMgr->get_accessible_for_user($argsObj->userID,$opt); - - - // We need to populate the combo box with test project where current logged user ($argsObj->userID) - // has right enough to assign user role. - // - $features = array(); - $idSet = $key2loop = array_keys($testprojects); - $rolesCache = null; - foreach($idSet as $tk) { - // $rolesCache[$testprojects[$tk]['effective_role']][] = $tk; - if(!isset($rolesCache[$testprojects[$tk]['effective_role']])) { - $rolesCache[$testprojects[$tk]['effective_role']] = new tlRole($testprojects[$tk]['effective_role']); - $rolesCache[$testprojects[$tk]['effective_role']]->readFromDB($dbHandler); - } - } - - foreach($key2loop as $idx) { - $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight("user_role_assignment"); - if($answer == false) { - $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight("testproject_user_role_assignment"); - } - - if($answer == true) { - $features[$idx] = $testprojects[$idx]; - } - } - - // If have no a test project ID, try to figure out which test project to show - // Try with session info, if failed go to first test project available. - if (!$argsObj->featureID) { - if ($argsObj->testprojectID) { - $argsObj->featureID = $argsObj->testprojectID; - } else if (sizeof($features)) { - $xx = current($features); - $argsObj->featureID = $xx['id']; - } - } - - // get private/public status for feature2check - $featureIsPublic = 1; - $key2loop = array_keys($testprojects); - foreach($key2loop as $ppx) { - if( $testprojects[$ppx]['id'] == $argsObj->featureID ) { - $featureIsPublic = $testprojects[$ppx]['is_public']; - break; - } - } - - foreach($users as &$user) { - $user->readTestProjectRoles($dbHandler,$argsObj->featureID); - } - $effectiveRoles = get_tproject_effective_role($dbHandler, - array('id' => $argsObj->featureID, - 'is_public' => $featureIsPublic),null,$users); - - return array($effectiveRoles,$features,$argsObj->featureID); -} - - - -/** - * getTestPlanEffectiveRoles - * - */ -function getTestPlanEffectiveRoles(&$dbHandler,&$tplanMgr,$tprojectMgr,&$argsObj,&$users) { - $features = array(); - $activeTestplans = $tprojectMgr->get_all_testplans($argsObj->testprojectID, array('plan_status' => 1)); - - - $ret = null; - $status_ok = !is_null($activeTestplans); - if($status_ok) { - $myAccessibleSet = $argsObj->user->getAccessibleTestPlans($dbHandler, - $argsObj->testprojectID,null,array('output' =>'map')); - - // we want to change map key, from testplan id to a sequential index - // to maintain old logic - $activeKeys = array_keys($activeTestplans); - $myKeys = array_keys((array)$myAccessibleSet); - $key2remove = $key2remove_diff = array_diff($activeKeys,$myKeys); - if( !is_null($key2remove) ) { - foreach($key2remove as $target) { - unset($activeTestplans[$target]); - } - } - - - - if($argsObj->user->hasRight($dbHandler,"mgt_users")) { - $features = $activeTestplans; - } else { - $features = array(); - $key2loop = array_keys($activeTestplans); - foreach($key2loop as $idx) { - if($argsObj->user->hasRight($dbHandler,"testplan_user_role_assignment",null,$activeTestplans[$idx]['id']) == "yes") { - $features[$idx] = $activeTestplans[$idx]; - } - } - } - - // if nothing special was selected, - // use the one in the session or the first - if (!$argsObj->featureID) { - if (sizeof($features)) { - if ($argsObj->testplanID) { - $key2loop = array_keys($features); - foreach($key2loop as $idx) { - if ($argsObj->testplanID == $features[$idx]['id']) { - $argsObj->featureID = $argsObj->testplanID; - } - } - } - if (!$argsObj->featureID) { - $xx = current($features); - $argsObj->featureID = $xx['id']; - } - } - } - - foreach($users as &$user) { - $user->readTestProjectRoles($dbHandler,$argsObj->testprojectID); - $user->readTestPlanRoles($dbHandler,$argsObj->featureID); - } - - $tproject_info = $tprojectMgr->get_by_id($argsObj->testprojectID); - $effectiveRoles = get_tplan_effective_role($dbHandler,$argsObj->featureID,$tproject_info,null,$users); - $ret = array($effectiveRoles,$features,$argsObj->featureID); - } - return $ret; -} - - - -/** - * getTestPlanEffectiveRoles - * - */ -function getTestPlanEffectiveRolesNEW(&$dbHandler,&$tplanMgr,$tprojectMgr,&$argsObj,&$users) -{ - $features = array(); - $activeTestplans = $tprojectMgr->get_all_testplans($argsObj->testprojectID, array('plan_status' => 1)); - - - $ret = null; - $status_ok = !is_null($activeTestplans); - if($status_ok) - { - $myAccessibleSet = $argsObj->user->getAccessibleTestPlans($dbHandler,$argsObj->testprojectID,null, - array('output' =>'map')); - - - //echo __LINE__; - //echo __FUNCTION__; - //new dBug($myAccessibleSet); - - // we want to change map key, from testplan id to a sequential index to maintain old logic - $activeKeys = array_keys($activeTestplans); - $myKeys = array_keys((array)$myAccessibleSet); - $key2remove = $key2remove_diff = array_diff($activeKeys,$myKeys); - if( !is_null($key2remove) ) - { - foreach($key2remove as $target) - { - unset($activeTestplans[$target]); - } - } - - // $activeTestplans = array_values($activeTestplans); - //new dBug($activeTestplans); - - // 2013-04-01 - // now is not clear why this logic is right - // - // analisys has to go from detail (test plan) to general - // Step 1 - check if user has specific role on test plan - // Step 2 - If Step 1 fails - // check if user has specific role on test project - // that contains the test project - // Step 3 - If Step 2 fails - // check Global Role. - // - - if($argsObj->user->hasRight($dbHandler,"mgt_users")) - { - $features = $activeTestplans; - } - else - { - //$loop2do = sizeof($activeTestplans); - //for($idx = 0; $idx < $loop2do; $idx++) - $features = array(); - $key2loop = array_keys($activeTestplans); - foreach($key2loop as $idx) - { - // Humm!!, think we need to check testplan_user_role_assignment and not "testplan_planning" - if($argsObj->user->hasRight($dbHandler,"testplan_user_role_assignment",null, - $activeTestplans[$idx]['id']) == "yes") - { - $features[$idx] = $activeTestplans[$idx]; - } - } - } - - //if nothing special was selected, use the one in the session or the first - if (!$argsObj->featureID) - { - if (sizeof($features)) - { - if ($argsObj->testplanID) - { - // $loop2do = sizeof($features); - // for($idx = 0; $idx < $loop2do; $idx++) - $key2loop = array_keys($features); - foreach($key2loop as $idx) - { - if ($argsObj->testplanID == $features[$idx]['id']) - { - $argsObj->featureID = $argsObj->testplanID; - } - } - } - if (!$argsObj->featureID) - { - $xx = current($features); - $argsObj->featureID = $xx['id']; - } - } - } - - foreach($users as &$user) - { - $user->readTestProjectRoles($dbHandler,$argsObj->testprojectID); - $user->readTestPlanRoles($dbHandler,$argsObj->featureID); - } - - $tproject_info = $tprojectMgr->get_by_id($argsObj->testprojectID); - - $effectiveRoles = get_tplan_effective_role($dbHandler,$argsObj->featureID,$tproject_info,null,$users); - - // it seems that here is the best place to check if current logged user - // can manege roles on current selected test plan. - // why I did not find this before ??? - $features = array(); - $key2loop = array_keys($activeTestplans); - foreach($key2loop as $idx) - { - $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight("user_role_assignment"); - //echo 'Question is: user_role_assignment - ANSWER IS:' . $answer . '
    '; - - if($answer == false) - { - $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight("testproject_user_role_assignment"); - // echo 'Question is: testproject_user_role_assignment - ANSWER IS:' . $answer . '
    '; - } - - if($answer == true) - { - $features[$idx] = $testprojects[$idx]; - } - - - // Humm!!, think we need to check testplan_user_role_assignment and not "testplan_planning" - if($argsObj->user->hasRight($dbHandler,"testplan_user_role_assignment",null, - $activeTestplans[$idx]['id']) == "yes") - { - $features[$idx] = $activeTestplans[$idx]; - } - } - - - - $ret = array($effectiveRoles,$features,$argsObj->featureID); - } - return $ret; -} - - -function doUpdate(&$dbHandler,&$argsObj,&$featureMgr) -{ - $featureMgr->deleteUserRoles($argsObj->featureID, - array_keys($argsObj->map_userid_roleid)); - foreach($argsObj->map_userid_roleid as $user_id => $role_id) - { - if ($role_id) - { - $featureMgr->addUserRole($user_id,$argsObj->featureID,$role_id); - } - } -} - -function initializeGui(&$dbHandler,$argsObj) -{ - $gui = new stdClass(); - - $gui->highlight = initialize_tabsmenu(); - $gui->user_feedback = ''; - $gui->no_features = ''; - $gui->roles_updated = ''; - $gui->tproject_name = $argsObj->testprojectName; - $gui->featureType = $argsObj->featureType; - $gui->optRights = tlRole::getAll($dbHandler,null,null,null,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); - $gui->features = null; - $gui->featureID = null; - $gui->role_colour = null; - $gui->tprojectAccessTypeImg = ''; - - $guiCfg = config_get('gui'); - if($guiCfg->usersAssignGlobalRoleColoring == ENABLED) - { - $gui->role_colour = tlRole::getRoleColourCfg($dbHandler); - } - return $gui; -} - -/** - * - */ -function initLabels() -{ - $tg = array('test_project_user_roles_updated' => null, - 'testproject_roles_assign_disabled' => null, - 'assign_tproject_roles' => null, - 'assign_tplan_roles' => null); - $labels = init_labels($tg); - return $labels; +getImages(); + +$templateCfg = templateConfiguration(); + +$assignRolesFor = null; +$featureMgr = null; +$userFeatureRoles = null; +$doInitGui = true; + +$tprojectMgr = new testproject($db); +$tplanMgr = new testplan($db); + +$args = initArgs($tprojectMgr); +$gui = initializeGui($db, $args); + +$lbl = initLabels(); + +$target = new stdClass(); +$target->testprojectID = null; +$target->testplanID = null; + +switch ($args->featureType) { + case "testproject": + $gui->highlight->assign_users_tproject = 1; + $gui->roles_updated = $lbl["test_project_user_roles_updated"]; + $gui->not_for_you = $lbl["testproject_roles_assign_disabled"]; + $gui->main_title = $lbl["assign_tproject_roles"]; + + $assignRolesFor = $args->featureType; + $target->testprojectID = $args->featureID > 0 ? $args->featureID : null; + $featureMgr = &$tprojectMgr; + break; + + case "testplan": + $gui->highlight->assign_users_tplan = 1; + $gui->roles_updated = lang_get("test_plan_user_roles_updated"); + $gui->not_for_you = lang_get("testplan_roles_assign_disabled"); + $gui->main_title = $lbl["assign_tplan_roles"]; + + $assignRolesFor = $args->featureType; + $target->testprojectID = $args->testprojectID; + $featureMgr = &$tplanMgr; + + $accessKey = 'private'; + if ($tprojectMgr->getPublicAttr($args->testprojectID)) { + $accessKey = 'public'; + } + $gui->tprojectAccessTypeImg = ''; + break; +} + +if ($args->featureID && $args->doUpdate && $featureMgr && + checkRightsForUpdate($db, $args->user, $args->testprojectID, + $args->featureType, $args->featureID)) { + doUpdate($db, $args, $featureMgr); + if ($gui->user_feedback == '') { + $gui->user_feedback = $gui->roles_updated; + } +} + +// Important: +// Must be done here after having done update, to get current information +$gui->users = tlUser::getAll($db, "WHERE active=1", null, null, + tlUser::TLOBJ_O_GET_DETAIL_MINIMUM); +checkSessionValid($db); +$args->user = $_SESSION['currentUser']; + +switch ($assignRolesFor) { + case 'testproject': + $info = getTestProjectEffectiveRoles($db, $tprojectMgr, $args, + $gui->users); + list ($gui->userFeatureRoles, $gui->features, $gui->featureID) = $info; + $target->testprojectID = $gui->featureID; + break; + + case 'testplan': + $info = getTestPlanEffectiveRoles($db, $tplanMgr, $tprojectMgr, $args, + $gui->users); + if (is_null($info)) { + $gui->user_feedback = lang_get('no_test_plans_available'); + } + list ($gui->userFeatureRoles, $gui->features, $gui->featureID) = $info; + break; +} + +$gui->grants = getGrantsForUserMgmt($db, $args->user, $target->testprojectID, + - 1); +$gui->accessTypeImg = ''; + +if (is_null($gui->features) || count($gui->features) == 0) { + $gui->features = null; + if ($gui->user_feedback == '') { + $gui->user_feedback = $gui->not_for_you; + } +} else { + $accessKey = 'vorsicht'; + if (isset($gui->features[$gui->featureID])) { + $accessKey = $gui->features[$gui->featureID]['is_public'] ? 'public' : 'private'; + $gui->accessTypeImg = ''; + } + $gui->accessTypeImg = ''; +} + +$gui->hintImg = ''; + +$smarty->assign('gui', $gui); +$smarty->display($templateCfg->template_dir . $templateCfg->default_template); + +/** + * + * @param testproject $tprojMgr + * @return stdClass + */ +function initArgs(&$tprojMgr) +{ + $iParams = array( + "featureType" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "featureID" => array( + tlInputParameter::INT_N + ), + "userRole" => array( + tlInputParameter::ARRAY_INT + ), + "do_update" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + + $pParams = R_PARAMS($iParams); + + $args = new stdClass(); + $args->featureType = $pParams["featureType"]; + $args->featureID = $pParams["featureID"]; + $args->map_userid_roleid = $pParams["userRole"]; + $args->doUpdate = ($pParams["do_update"] != "") ? 1 : 0; + + $args->testprojectID = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : 0; + + $args->testplanID = 0; + + switch ($args->featureType) { + case 'testproject': + break; + + case 'testplan': + if ($args->testprojectID <= 0) { + throw new Exception("INVALID Test Project ID", 1); + } + break; + } + + if (($fid = intval($args->featureID)) > 0) { + $prop = $args->featureType . 'ID'; + $args->$prop = $fid; + } + + $prjInfo = $tprojMgr->get_by_id($args->testprojectID, + array( + 'output' => 'name' + )); + + $args->testprojectName = $prjInfo['name']; + + $args->user = $_SESSION['currentUser']; + $args->userID = $args->user->dbID; + + return $args; +} + +/** + * checks runned when tyring to run this page, + * to be sure user has rights to use it. + * + * @param database $db + * @param tlUser $user + * @return boolean + */ +function checkRights(&$db, &$user) +{ + $result = false; + + $tprojMgr = new testproject($db); + $args = initArgs($tprojMgr); + $answers = new stdClass(); + $answers->role_management = $user->hasRight($db, "role_management"); + + // Two checks needed: + // First on current test project without using test plan rights + // if this fails then check again adding current test plan + $answers->testplan_user_role_assignment = $user->hasRight($db, + "testplan_user_role_assignment", $args->testprojectID, - 1); + if ($answers->testplan_user_role_assignment != "yes") { + $targetTestPlanID = null; + if ($args->featureType == 'testplan') { + $targetTestPlanID = $args->featureID; + } + $answers->testplan_user_role_assignment = $user->hasRight($db, + "testplan_user_role_assignment", null, $targetTestPlanID); + } + + $answers->user_role_assignment = $user->hasRight($db, "user_role_assignment", + null, - 1); + + foreach ($answers as $key => $value) { + $answers->$key = $value == "yes" ? true : false; + $result |= $answers->$key; + } + + if (! $result && ($args->featureType == 'testproject')) { + $feature2check = $args->featureID; + if ($args->featureID == 0 || is_null($args->featureID)) { + $feature2check = $args->testprojectID; + } + if ($user->hasRight($db, "testproject_user_role_assignment", + $feature2check, - 1) == "yes") { + $result = true; + } + } + + return $result; +} + +/** + * checkRightsForUpdate + * + * @param database $dbHandler + * @param tlUser $user + * @param int $testprojectID + * @param testproject $featureType + * @param int $featureID + * @return boolean + */ +function checkRightsForUpdate(&$dbHandler, &$user, $testprojectID, $featureType, + $featureID) +{ + $yes_no = "no"; + switch ($featureType) { + case 'testproject': + if ($user->hasRight($dbHandler, "user_role_assignment", $featureID) == + "yes" || + $user->hasRight($dbHandler, "testproject_user_role_assignment", + $featureID, - 1, true) == "yes") { + $yes_no = "yes"; + } + break; + + case 'testplan': + $yes_no = $user->hasRight($dbHandler, + "testplan_user_role_assignment", $testprojectID, $featureID); + break; + } + + return $yes_no == 'yes'; +} + +/** + * getTestProjectEffectiveRoles + * + * @param database $dbHandler + * @param testproject $objMgr + * @param stdClass $argsObj + * @param array $users + * @return array + */ +function getTestProjectEffectiveRoles($dbHandler, &$objMgr, &$argsObj, $users) +{ + $features = null; + $gui_cfg = config_get('gui'); + + // Accessible means user has a role on test project ? + $opt = array( + 'output' => 'map_of_map_full', + 'order_by' => $gui_cfg->tprojects_combo_order_by + ); + $testprojects = $objMgr->get_accessible_for_user($argsObj->userID, $opt); + + // We need to populate the combo box with test project where current logged user ($argsObj->userID) + // has right enough to assign user role. + // + $features = array(); + $idSet = $key2loop = array_keys($testprojects); + $rolesCache = null; + foreach ($idSet as $tk) { + if (! isset($rolesCache[$testprojects[$tk]['effective_role']])) { + $rolesCache[$testprojects[$tk]['effective_role']] = new tlRole( + $testprojects[$tk]['effective_role']); + $rolesCache[$testprojects[$tk]['effective_role']]->readFromDB( + $dbHandler); + } + } + + foreach ($key2loop as $idx) { + $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight( + "user_role_assignment"); + if (! $answer) { + $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight( + "testproject_user_role_assignment"); + } + + if ($answer) { + $features[$idx] = $testprojects[$idx]; + } + } + + // If have no a test project ID, try to figure out which test project to show + // Try with session info, if failed go to first test project available. + if (! $argsObj->featureID) { + if ($argsObj->testprojectID) { + $argsObj->featureID = $argsObj->testprojectID; + } elseif (count($features)) { + $xx = current($features); + $argsObj->featureID = $xx['id']; + } + } + + // get private/public status for feature2check + $featureIsPublic = 1; + $key2loop = array_keys($testprojects); + foreach ($key2loop as $ppx) { + if ($testprojects[$ppx]['id'] == $argsObj->featureID) { + $featureIsPublic = $testprojects[$ppx]['is_public']; + break; + } + } + + foreach ($users as &$user) { + $user->readTestProjectRoles($dbHandler, $argsObj->featureID); + } + $effectiveRoles = get_tproject_effective_role($dbHandler, + array( + 'id' => $argsObj->featureID, + 'is_public' => $featureIsPublic + ), null, $users); + + return array( + $effectiveRoles, + $features, + $argsObj->featureID + ); +} + +/** + * getTestPlanEffectiveRoles + * + * @param database $dbHandler + * @param testplan $tplanMgr + * @param testproject $tprojectMgr + * @param stdClass $argsObj + * @param array $users + * @return array + */ +function getTestPlanEffectiveRoles(&$dbHandler, &$tplanMgr, $tprojectMgr, + &$argsObj, &$users) +{ + $features = array(); + $activeTestplans = $tprojectMgr->get_all_testplans($argsObj->testprojectID, + array( + 'plan_status' => 1 + )); + + $ret = null; + $status_ok = ! is_null($activeTestplans); + if ($status_ok) { + $myAccessibleSet = $argsObj->user->getAccessibleTestPlans($dbHandler, + $argsObj->testprojectID, null, array( + 'output' => 'map' + )); + + // we want to change map key, from testplan id to a sequential index + // to maintain old logic + $activeKeys = array_keys($activeTestplans); + $myKeys = array_keys((array) $myAccessibleSet); + $key2remove = array_diff($activeKeys, $myKeys); + if (! is_null($key2remove)) { + foreach ($key2remove as $target) { + unset($activeTestplans[$target]); + } + } + + if ($argsObj->user->hasRight($dbHandler, "mgt_users")) { + $features = $activeTestplans; + } else { + $features = array(); + $key2loop = array_keys($activeTestplans); + foreach ($key2loop as $idx) { + if ($argsObj->user->hasRight($dbHandler, + "testplan_user_role_assignment", null, + $activeTestplans[$idx]['id']) == "yes") { + $features[$idx] = $activeTestplans[$idx]; + } + } + } + + // if nothing special was selected, + // use the one in the session or the first + if (! $argsObj->featureID && count($features)) { + if ($argsObj->testplanID) { + $key2loop = array_keys($features); + foreach ($key2loop as $idx) { + if ($argsObj->testplanID == $features[$idx]['id']) { + $argsObj->featureID = $argsObj->testplanID; + } + } + } + if (! $argsObj->featureID) { + $xx = current($features); + $argsObj->featureID = $xx['id']; + } + } + + foreach ($users as &$user) { + $user->readTestProjectRoles($dbHandler, $argsObj->testprojectID); + $user->readTestPlanRoles($dbHandler, $argsObj->featureID); + } + + $tproject_info = $tprojectMgr->get_by_id($argsObj->testprojectID); + $effectiveRoles = get_tplan_effective_role($dbHandler, + $argsObj->featureID, $tproject_info, null, $users); + $ret = array( + $effectiveRoles, + $features, + $argsObj->featureID + ); + } + return $ret; +} + +/** + * getTestPlanEffectiveRoles + * + * @param database $dbHandler + * @param testplan $tplanMgr + * @param testproject $tprojectMgr + * @param stdClass $argsObj + * @param array $users + * @return array + */ +function getTestPlanEffectiveRolesNEW(&$dbHandler, &$tplanMgr, $tprojectMgr, + &$argsObj, &$users) +{ + $features = array(); + $activeTestplans = $tprojectMgr->get_all_testplans($argsObj->testprojectID, + array( + 'plan_status' => 1 + )); + + $ret = null; + $status_ok = ! is_null($activeTestplans); + if ($status_ok) { + $myAccessibleSet = $argsObj->user->getAccessibleTestPlans($dbHandler, + $argsObj->testprojectID, null, array( + 'output' => 'map' + )); + + // we want to change map key, from testplan id to a sequential index to maintain old logic + $activeKeys = array_keys($activeTestplans); + $myKeys = array_keys((array) $myAccessibleSet); + $key2remove = array_diff($activeKeys, $myKeys); + if (! is_null($key2remove)) { + foreach ($key2remove as $target) { + unset($activeTestplans[$target]); + } + } + // 2013-04-01 + // now is not clear why this logic is right + // + // analisys has to go from detail (test plan) to general + // Step 1 - check if user has specific role on test plan + // Step 2 - If Step 1 fails + // check if user has specific role on test project + // that contains the test project + // Step 3 - If Step 2 fails + // check Global Role. + // + + if ($argsObj->user->hasRight($dbHandler, "mgt_users")) { + $features = $activeTestplans; + } else { + // $loop2do = count($activeTestplans); + // for($idx = 0; $idx < $loop2do; $idx++) + $features = array(); + $key2loop = array_keys($activeTestplans); + foreach ($key2loop as $idx) { + // Humm!!, think we need to check testplan_user_role_assignment and not "testplan_planning" + if ($argsObj->user->hasRight($dbHandler, + "testplan_user_role_assignment", null, + $activeTestplans[$idx]['id']) == "yes") { + $features[$idx] = $activeTestplans[$idx]; + } + } + } + + // if nothing special was selected, use the one in the session or the first + if (! $argsObj->featureID && count($features)) { + if ($argsObj->testplanID) { + $key2loop = array_keys($features); + foreach ($key2loop as $idx) { + if ($argsObj->testplanID == $features[$idx]['id']) { + $argsObj->featureID = $argsObj->testplanID; + } + } + } + if (! $argsObj->featureID) { + $xx = current($features); + $argsObj->featureID = $xx['id']; + } + } + + foreach ($users as &$user) { + $user->readTestProjectRoles($dbHandler, $argsObj->testprojectID); + $user->readTestPlanRoles($dbHandler, $argsObj->featureID); + } + + $tproject_info = $tprojectMgr->get_by_id($argsObj->testprojectID); + + $effectiveRoles = get_tplan_effective_role($dbHandler, + $argsObj->featureID, $tproject_info, null, $users); + + // it seems that here is the best place to check if current logged user + // can manege roles on current selected test plan. + // why I did not find this before ??? + $features = array(); + $key2loop = array_keys($activeTestplans); + foreach ($key2loop as $idx) { + $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight( + "user_role_assignment"); + + if (! $answer) { + $answer = $rolesCache[$testprojects[$idx]['effective_role']]->hasRight( + "testproject_user_role_assignment"); + } + + if ($answer) { + $features[$idx] = $testprojects[$idx]; + } + + // Humm!!, think we need to check testplan_user_role_assignment and not "testplan_planning" + if ($argsObj->user->hasRight($dbHandler, + "testplan_user_role_assignment", null, + $activeTestplans[$idx]['id']) == "yes") { + $features[$idx] = $activeTestplans[$idx]; + } + } + + $ret = array( + $effectiveRoles, + $features, + $argsObj->featureID + ); + } + return $ret; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param testproject $featureMgr + */ +function doUpdate(&$dbHandler, &$argsObj, &$featureMgr) +{ + $featureMgr->deleteUserRoles($argsObj->featureID, + array_keys($argsObj->map_userid_roleid)); + foreach ($argsObj->map_userid_roleid as $user_id => $role_id) { + if ($role_id) { + $featureMgr->addUserRole($user_id, $argsObj->featureID, $role_id); + } + } +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$dbHandler, $argsObj) +{ + $gui = new stdClass(); + + $gui->highlight = initializeTabsmenu(); + $gui->user_feedback = ''; + $gui->no_features = ''; + $gui->roles_updated = ''; + $gui->tproject_name = $argsObj->testprojectName; + $gui->featureType = $argsObj->featureType; + $gui->optRights = tlRole::getAll($dbHandler, null, null, null, + tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); + $gui->features = null; + $gui->featureID = null; + $gui->role_colour = null; + $gui->tprojectAccessTypeImg = ''; + + $guiCfg = config_get('gui'); + if ($guiCfg->usersAssignGlobalRoleColoring == ENABLED) { + $gui->role_colour = tlRole::getRoleColourCfg($dbHandler); + } + return $gui; +} + +/** + * array + */ +function initLabels() +{ + $tg = array( + 'test_project_user_roles_updated' => null, + 'testproject_roles_assign_disabled' => null, + 'assign_tproject_roles' => null, + 'assign_tplan_roles' => null + ); + return init_labels($tg); } diff --git a/lib/usermanagement/usersEdit.php b/lib/usermanagement/usersEdit.php index a6017dfc5b..0531acae63 100644 --- a/lib/usermanagement/usersEdit.php +++ b/lib/usermanagement/usersEdit.php @@ -1,485 +1,556 @@ - 'doCreate', 'edit' => 'doUpdate', - 'doCreate' => 'doCreate', 'doUpdate' => 'doUpdate', - 'resetPassword' => 'doUpdate', - 'genAPIKey' => 'doUpdate'); - -switch($args->doAction) -{ - case "edit": - $highlight->edit_user = 1; - break; - - case "doCreate": - $highlight->create_user = 1; - $gui->op = doCreate($db,$args); - $gui->user = $gui->op->user; - $templateCfg->template = $gui->op->template; - $gui->main_title = $lbl['action_create_user']; - break; - - case "doUpdate": - $highlight->edit_user = 1; - $sessionUserID = $_SESSION['currentUser']->dbID; - $gui->op = doUpdate($db,$args,$sessionUserID); - $gui->user = $gui->op->user; - $gui->main_title = $lbl['action_edit_user']; - break; - - case "resetPassword": - $highlight->edit_user = 1; - $passwordSendMethod = config_get('password_reset_send_method'); - $gui->op = createNewPassword($db,$args,$gui->user,$passwordSendMethod); - $gui->main_title = $lbl['action_edit_user']; - break; - - case "genAPIKey": - $highlight->edit_user = 1; - $gui->op = createNewAPIKey($db,$args,$gui->user); - $gui->main_title = $lbl['action_edit_user']; - break; - - case "create": - default: - $highlight->create_user = 1; - $gui->user = new tlUser(); - $gui->main_title = $lbl['action_create_user']; - break; + 'doCreate', + 'edit' => 'doUpdate', + 'doCreate' => 'doCreate', + 'doUpdate' => 'doUpdate', + 'resetPassword' => 'doUpdate', + 'genAPIKey' => 'doUpdate' +); + +switch ($args->doAction) { + case "edit": + $highlight->edit_user = 1; + break; + + case "doCreate": + $highlight->create_user = 1; + $gui->op = doCreate($db, $args); + $gui->user = $gui->op->user; + $templateCfg->template = $gui->op->template; + $gui->main_title = $lbl['action_create_user']; + break; + + case "doUpdate": + $highlight->edit_user = 1; + $sessionUserID = $_SESSION['currentUser']->dbID; + $gui->op = doUpdate($db, $args, $sessionUserID); + $gui->user = $gui->op->user; + $gui->main_title = $lbl['action_edit_user']; + break; + + case "resetPassword": + $highlight->edit_user = 1; + $passwordSendMethod = config_get('password_reset_send_method'); + $gui->op = createNewPassword($db, $args, $gui->user, $passwordSendMethod); + $gui->main_title = $lbl['action_edit_user']; + break; + + case "genAPIKey": + $highlight->edit_user = 1; + $gui->op = createNewAPIKey($db, $args, $gui->user); + $gui->main_title = $lbl['action_edit_user']; + break; + + case "create": + default: + $highlight->create_user = 1; + $gui->user = new tlUser(); + $gui->main_title = $lbl['action_create_user']; + break; +} + +$gui->op->operation = $actionOperation[$args->doAction]; +$roles = tlRole::getAll($db, null, null, null, + tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); +unset($roles[TL_ROLES_UNDEFINED]); + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); + +$smarty->assign('highlight', $highlight); +$smarty->assign('operation', $gui->op->operation); +$smarty->assign('user_feedback', $gui->op->user_feedback); +$smarty->assign('external_password_mgmt', + tlUser::isPasswordMgtExternal($gui->user->authentication)); +$smarty->assign('optRights', $roles); +renderGui($smarty, $args, $templateCfg); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $iParams = array( + "delete" => array( + tlInputParameter::INT_N + ), + "user" => array( + tlInputParameter::INT_N + ), + "user_id" => array( + tlInputParameter::INT_N + ), + "rights_id" => array( + tlInputParameter::INT_N + ), + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 30 + ), + "firstName" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "lastName" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "emailAddress" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "locale" => array( + tlInputParameter::STRING_N, + 0, + 10 + ), + "login" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "password" => array( + tlInputParameter::STRING_N, + 0, + 32 + ), + "authentication" => array( + tlInputParameter::STRING_N, + 0, + 10 + ), + "user_is_active" => array( + tlInputParameter::CB_BOOL + ) + ); + + $args = new stdClass(); + R_PARAMS($iParams, $args); + + $date_format = config_get('date_format'); + + // convert expiration date to ISO format to write to db + $dk = 'expiration_date'; + $args->$dk = null; + if (isset($_REQUEST[$dk]) && $_REQUEST[$dk] != '') { + $da = split_localized_date($_REQUEST[$dk], $date_format); + if ($da != null) { + // set date in iso format + $args->$dk = $da['year'] . "-" . $da['month'] . "-" . $da['day']; + } + } + + $args->user = $_SESSION['currentUser']; + return $args; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass object with following members + * user: tlUser object + * status: + * template: will be used by viewer logic. + * null -> viewer logic will choose template + * other value -> viever logic will use this template. + */ +function doCreate(&$dbHandler, &$argsObj) +{ + $op = new stdClass(); + $op->user = new tlUser(); + $op->status = $op->user->setPassword($argsObj->password); + $op->template = 'usersEdit.tpl'; + $op->operation = ''; + + $statusOk = false; + if ($op->status >= tl::OK) { + initializeUserProperties($op->user, $argsObj); + $op->status = $op->user->writeToDB($dbHandler); + if ($op->status >= tl::OK) { + tlUser::setExpirationDate($dbHandler, $op->user->dbID, + $argsObj->expiration_date); + + $statusOk = true; + $op->template = null; + logAuditEvent(TLS("audit_user_created", $op->user->login), "CREATE", + $op->user->dbID, "users"); + $op->user_feedback = sprintf(lang_get('user_created'), + $op->user->login); + } + } + + if (! $statusOk) { + $op->operation = 'create'; + $op->user_feedback = getUserErrorMessage($op->status); + } + + return $op; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param int $sessionUserID + * @return stdClass + */ +function doUpdate(&$dbHandler, &$argsObj, $sessionUserID) +{ + $op = new stdClass(); + $op->user_feedback = ''; + $op->user = new tlUser($argsObj->user_id); + $op->status = $op->user->readFromDB($dbHandler); + if ($op->status >= tl::OK) { + initializeUserProperties($op->user, $argsObj); + $op->status = $op->user->writeToDB($dbHandler); + if ($op->status >= tl::OK) { + tlUser::setExpirationDate($dbHandler, $op->user->dbID, + $argsObj->expiration_date); + + logAuditEvent(TLS("audit_user_saved", $op->user->login), "SAVE", + $op->user->dbID, "users"); + + if ($sessionUserID == $argsObj->user_id) { + $_SESSION['currentUser'] = $op->user; + setUserSession($dbHandler, $op->user->login, $argsObj->user_id, + $op->user->globalRoleID, $op->user->emailAddress, + $op->user->locale); + + if (! $argsObj->user_is_active) { + header("Location: ../../logout.php"); + exit(); + } + } + } + $op->user_feedback = getUserErrorMessage($op->status); + } + return $op; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param tlUser $userObj + * @param string $newPasswordSendMethod + * @return stdClass + */ +function createNewPassword(&$dbHandler, &$argsObj, &$userObj, + $newPasswordSendMethod) +{ + $op = new stdClass(); + $op->user_feedback = ''; + $op->new_password = ''; + + // Try to validate mail configuration + // + // From Zend Documentation + // You may find you also want to match IP addresses, Local hostnames, or a combination of all allowed types. + // This can be done by passing a parameter to Zend_Validate_Hostname when you instantiate it. + // The paramter should be an integer which determines what types of hostnames are allowed. + // You are encouraged to use the Zend_Validate_Hostname constants to do this. + // The Zend_Validate_Hostname constants are: ALLOW_DNS to allow only DNS hostnames, ALLOW_IP to allow IP addresses, + // ALLOW_LOCAL to allow local network names, and ALLOW_ALL to allow all three types. + // + $validator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL); + $smtp_host = config_get('smtp_host'); + + $password_on_screen = ($newPasswordSendMethod == 'display_on_screen'); + if ($validator->isValid($smtp_host) || $password_on_screen) { + $dummy = resetPassword($dbHandler, $argsObj->user_id, + $newPasswordSendMethod); + + $op->user_feedback = $dummy['msg']; + $op->status = $dummy['status']; + $op->new_password = $dummy['password']; + if ($op->status >= tl::OK) { + logAuditEvent(TLS("audit_pwd_reset_requested", $userObj->login), + "PWD_RESET", $argsObj->user_id, "users"); + $op->user_feedback = lang_get('password_reseted'); + if ($password_on_screen) { + $op->user_feedback = lang_get('password_set') . + $dummy['password']; + } + } else { + $op->user_feedback = sprintf( + lang_get('password_cannot_be_reseted_reason'), + $op->user_feedback); + } + } else { + $op->status = tl::ERROR; + $op->user_feedback = lang_get( + 'password_cannot_be_reseted_invalid_smtp_hostname'); + } + return $op; +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @param tlUser $userObj + * @return stdClass + */ +function createNewAPIKey(&$dbHandler, &$argsObj, &$userObj) +{ + $op = new stdClass(); + $op->user_feedback = ''; + + // Try to validate mail configuration + // + // From Zend Documentation + // You may find you also want to match IP addresses, Local hostnames, or a combination of all allowed types. + // This can be done by passing a parameter to Zend_Validate_Hostname when you instantiate it. + // The paramter should be an integer which determines what types of hostnames are allowed. + // You are encouraged to use the Zend_Validate_Hostname constants to do this. + // The Zend_Validate_Hostname constants are: ALLOW_DNS to allow only DNS hostnames, ALLOW_IP to allow IP addresses, + // ALLOW_LOCAL to allow local network names, and ALLOW_ALL to allow all three types. + // + $validator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL); + $smtp_host = config_get('smtp_host'); + $op->status = tl::ERROR; + + // We need to validate at least that user mail is NOT EMPTY + if ($validator->isValid($smtp_host)) { + $APIKey = new APIKey(); + if ($APIKey->addKeyForUser($argsObj->user_id) >= tl::OK) { + logAuditEvent(TLS("audit_user_apikey_set", $userObj->login), + "CREATE", $userObj->login, "users"); + $op->user_feedback = lang_get('apikey_by_mail'); + $op->status = tl::OK; + + // now send by mail + $ak = $APIKey->getAPIKey($argsObj->user_id); + $msgBody = lang_get('your_apikey_is') . "\n\n" . $ak . "\n\n" . + lang_get('contact_admin'); + $mail_op = @email_send(config_get('from_email'), + $userObj->emailAddress, lang_get('mail_apikey_subject'), + $msgBody); + } + } else { + $op->status = tl::ERROR; + $op->user_feedback = lang_get( + 'apikey_cannot_be_reseted_invalid_smtp_hostname'); + } + return $op; +} + +/** + * initialize members for a user object. + * + * @param tlUser $userObj + * data read from DB + * @param stdClass $argsObj + * data entry from User Interface + */ +function initializeUserProperties(&$userObj, &$argsObj) +{ + if (! is_null($argsObj->login)) { + $userObj->login = $argsObj->login; + } + $userObj->emailAddress = $argsObj->emailAddress; + + // The Black List - Jon Bokenkamp + $reddington = array( + '/', + '\\', + ':', + '*', + '?', + '<', + '>', + '|' + ); + $userObj->firstName = str_replace($reddington, '', $argsObj->firstName); + $userObj->lastName = str_replace($reddington, '', $argsObj->lastName); + + $userObj->globalRoleID = $argsObj->rights_id; + $userObj->locale = $argsObj->locale; + $userObj->isActive = $argsObj->user_is_active; + $userObj->authentication = trim($argsObj->authentication); +} + +/** + * + * @param database $dbHandler + * @param int $roleID + * @return + */ +function decodeRoleId(&$dbHandler, $roleID) +{ + $roleInfo = tlRole::getByID($dbHandler, $roleID); + return $roleInfo->name; +} + +/** + * + * @param TLSmarty $smartyObj + * @param stdClass $argsObj + * @param stdClass $templateCfg + */ +function renderGui(&$smartyObj, &$argsObj, $templateCfg) +{ + $doRender = false; + switch ($argsObj->doAction) { + case "edit": + case "create": + case "resetPassword": + case "genAPIKey": + $doRender = true; + $tpl = $templateCfg->default_template; + break; + + case "doCreate": + case "doUpdate": + if (! is_null($templateCfg->template)) { + $doRender = true; + $tpl = $templateCfg->template; + } else { + header("Location: usersView.php"); + exit(); + } + break; + } + + if ($doRender) { + $smartyObj->display($templateCfg->template_dir . $tpl); + } +} + +/** + * + * @param database $dbHandler + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui(&$dbHandler, &$argsObj) +{ + $userObj = &$argsObj->user; + + $guiObj = new stdClass(); + + $guiObj->user = null; + switch ($argsObj->doAction) { + case 'edit': + // Because we can arrive with login, we need to check if we can get + // id from login + if (strlen(trim($argsObj->login)) > 0) { + $argsObj->user_id = tlUser::doesUserExist($dbHandler, + $argsObj->login); + } + + if (is_null($argsObj->user_id) || intval($argsObj->user_id) <= 0) { + // need to manage some sort of error message + $guiObj->op = new stdClass(); + $guiObj->op->status = tl::ERROR; + $guiObj->op->user_feedback = sprintf( + lang_get('login_does_not_exist'), $argsObj->login); + } else { + $guiObj->user = new tlUser(intval($argsObj->user_id)); + $guiObj->user->readFromDB($dbHandler); + } + $guiObj->main_title = lang_get("action_{$argsObj->doAction}_user"); + break; + + case "resetPassword": + case "genAPIKey": + $guiObj->user = new tlUser($argsObj->user_id); + $guiObj->user->readFromDB($dbHandler); + break; + } + + $guiObj->op = new stdClass(); + $guiObj->op->user_feedback = ''; + $guiObj->op->status = tl::OK; + + $guiObj->authCfg = config_get('authentication'); + $guiObj->auth_method_opt = array( + lang_get('default_auth_method') . "(" . + $guiObj->authCfg['domain'][$guiObj->authCfg['method']]['description'] . + ")" => '' + ); + + $dummy = array_keys($guiObj->authCfg['domain']); + foreach ($dummy as $xc) { + // description => html option value + $guiObj->auth_method_opt[$xc] = $xc; + } + + $guiObj->auth_method_opt = array_flip($guiObj->auth_method_opt); + + $guiObj->optLocale = config_get('locales'); + + $guiObj->grants = getGrantsForUserMgmt($dbHandler, $userObj); + + $guiObj->grants->mgt_view_events = $userObj->hasRight($dbHandler, + "mgt_view_events"); + + $guiObj->expiration_date = $argsObj->expiration_date; + + $noExpirationUsers = array_flip(config_get('noExpDateUsers')); + $guiObj->expDateEnabled = true; + if (! is_null($guiObj->user)) { + $guiObj->expDateEnabled = ! isset( + $noExpirationUsers[$guiObj->user->login]); + } + + return $guiObj; +} + +/** + * + * @return array + */ +function initLabels() +{ + $tg = array( + 'action_create_user' => null, + 'action_edit_user' => null + ); + return init_labels($tg); +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'mgt_users'); } - -$gui->op->operation = $actionOperation[$args->doAction]; -$roles = tlRole::getAll($db,null,null,null,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); -unset($roles[TL_ROLES_UNDEFINED]); - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); - -$smarty->assign('highlight',$highlight); -$smarty->assign('operation',$gui->op->operation); -$smarty->assign('user_feedback',$gui->op->user_feedback); -$smarty->assign('external_password_mgmt', tlUser::isPasswordMgtExternal($gui->user->authentication)); -$smarty->assign('optRights',$roles); -renderGui($smarty,$args,$templateCfg); - - -/** - * - * - */ -function init_args() -{ - $_REQUEST=strings_stripSlashes($_REQUEST); - $iParams = array("delete" => array(tlInputParameter::INT_N), - "user" => array(tlInputParameter::INT_N), - "user_id" => array(tlInputParameter::INT_N), - "rights_id" => array(tlInputParameter::INT_N), - "doAction" => array(tlInputParameter::STRING_N,0,30), - "firstName" => array(tlInputParameter::STRING_N,0,50), - "lastName" => array(tlInputParameter::STRING_N,0,50), - "emailAddress" => array(tlInputParameter::STRING_N,0,100), - "locale" => array(tlInputParameter::STRING_N,0,10), - "login" => array(tlInputParameter::STRING_N,0,100), - "password" => array(tlInputParameter::STRING_N,0,32), - "authentication" => array(tlInputParameter::STRING_N,0,10), - "user_is_active" => array(tlInputParameter::CB_BOOL)); - - $args = new stdClass(); - R_PARAMS($iParams,$args); - - $date_format = config_get('date_format'); - - // convert expiration date to ISO format to write to db - $dk = 'expiration_date'; - $args->$dk = null; - if (isset($_REQUEST[$dk]) && $_REQUEST[$dk] != '') - { - $da = split_localized_date($_REQUEST[$dk], $date_format); - if ($da != null) - { - // set date in iso format - $args->$dk = $da['year'] . "-" . $da['month'] . "-" . $da['day']; - } - } - - $args->user = $_SESSION['currentUser']; - return $args; -} - -/* - function: doCreate - - args: - - returns: object with following members - user: tlUser object - status: - template: will be used by viewer logic. - null -> viewer logic will choose template - other value -> viever logic will use this template. - - - -*/ -function doCreate(&$dbHandler,&$argsObj) -{ - $op = new stdClass(); - $op->user = new tlUser(); - $op->status = $op->user->setPassword($argsObj->password); - $op->template = 'usersEdit.tpl'; - $op->operation = ''; - - $statusOk = false; - if ($op->status >= tl::OK) - { - initializeUserProperties($op->user,$argsObj); - $op->status = $op->user->writeToDB($dbHandler); - if($op->status >= tl::OK) - { - tlUser::setExpirationDate($dbHandler,$op->user->dbID,$argsObj->expiration_date); - - $statusOk = true; - $op->template = null; - logAuditEvent(TLS("audit_user_created",$op->user->login),"CREATE",$op->user->dbID,"users"); - $op->user_feedback = sprintf(lang_get('user_created'),$op->user->login); - } - } - - if (!$statusOk) - { - $op->operation = 'create'; - $op->user_feedback = getUserErrorMessage($op->status); - } - - return $op; -} - -/** - * - */ -function doUpdate(&$dbHandler,&$argsObj,$sessionUserID) -{ - $op = new stdClass(); - $op->user_feedback = ''; - $op->user = new tlUser($argsObj->user_id); - $op->status = $op->user->readFromDB($dbHandler); - if ($op->status >= tl::OK) - { - initializeUserProperties($op->user,$argsObj); - $op->status = $op->user->writeToDB($dbHandler); - if ($op->status >= tl::OK) - { - tlUser::setExpirationDate($dbHandler,$op->user->dbID,$argsObj->expiration_date); - - logAuditEvent(TLS("audit_user_saved",$op->user->login),"SAVE",$op->user->dbID,"users"); - - if ($sessionUserID == $argsObj->user_id) - { - $_SESSION['currentUser'] = $op->user; - setUserSession($dbHandler,$op->user->login, $argsObj->user_id, - $op->user->globalRoleID, $op->user->emailAddress, $op->user->locale); - - if (!$argsObj->user_is_active) - { - header("Location: ../../logout.php"); - exit(); - } - } - } - $op->user_feedback = getUserErrorMessage($op->status); - } - return $op; -} - -/** - * - */ -function createNewPassword(&$dbHandler,&$argsObj,&$userObj,$newPasswordSendMethod) -{ - $op = new stdClass(); - $op->user_feedback = ''; - $op->new_password = ''; - - // Try to validate mail configuration - // - // From Zend Documentation - // You may find you also want to match IP addresses, Local hostnames, or a combination of all allowed types. - // This can be done by passing a parameter to Zend_Validate_Hostname when you instantiate it. - // The paramter should be an integer which determines what types of hostnames are allowed. - // You are encouraged to use the Zend_Validate_Hostname constants to do this. - // The Zend_Validate_Hostname constants are: ALLOW_DNS to allow only DNS hostnames, ALLOW_IP to allow IP addresses, - // ALLOW_LOCAL to allow local network names, and ALLOW_ALL to allow all three types. - // - $validator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL); - $smtp_host = config_get( 'smtp_host' ); - - $password_on_screen = ($newPasswordSendMethod == 'display_on_screen'); - if( $validator->isValid($smtp_host) || $password_on_screen ) - { - $dummy = resetPassword($dbHandler,$argsObj->user_id,$newPasswordSendMethod); - - $op->user_feedback = $dummy['msg']; - $op->status = $dummy['status']; - $op->new_password = $dummy['password']; - if ($op->status >= tl::OK) - { - logAuditEvent(TLS("audit_pwd_reset_requested",$userObj->login),"PWD_RESET",$argsObj->user_id,"users"); - $op->user_feedback = lang_get('password_reseted'); - if( $password_on_screen ) - { - $op->user_feedback = lang_get('password_set') . $dummy['password']; - } - } - else - { - $op->user_feedback = sprintf(lang_get('password_cannot_be_reseted_reason'),$op->user_feedback); - } - } - else - { - $op->status = tl::ERROR; - $op->user_feedback = lang_get('password_cannot_be_reseted_invalid_smtp_hostname'); - } - return $op; -} - -/** - * - */ -function createNewAPIKey(&$dbHandler,&$argsObj,&$userObj) -{ - $op = new stdClass(); - $op->user_feedback = ''; - - // Try to validate mail configuration - // - // From Zend Documentation - // You may find you also want to match IP addresses, Local hostnames, or a combination of all allowed types. - // This can be done by passing a parameter to Zend_Validate_Hostname when you instantiate it. - // The paramter should be an integer which determines what types of hostnames are allowed. - // You are encouraged to use the Zend_Validate_Hostname constants to do this. - // The Zend_Validate_Hostname constants are: ALLOW_DNS to allow only DNS hostnames, ALLOW_IP to allow IP addresses, - // ALLOW_LOCAL to allow local network names, and ALLOW_ALL to allow all three types. - // - $validator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL); - $smtp_host = config_get( 'smtp_host' ); - $op->status = tl::ERROR; - - // We need to validate at least that user mail is NOT EMPTY - if( $validator->isValid($smtp_host) ) - { - $APIKey = new APIKey(); - if ($APIKey->addKeyForUser($argsObj->user_id) >= tl::OK) - { - logAuditEvent(TLS("audit_user_apikey_set",$userObj->login),"CREATE", - $userObj->login,"users"); - $op->user_feedback = lang_get('apikey_by_mail'); - $op->status = tl::OK; - - // now send by mail - $ak = $APIKey->getAPIKey($argsObj->user_id); - $msgBody = lang_get('your_apikey_is') . "\n\n" . $ak . - "\n\n" . lang_get('contact_admin'); - $mail_op = @email_send(config_get('from_email'), - $userObj->emailAddress,lang_get('mail_apikey_subject'),$msgBody); - } - } - else - { - $op->status = tl::ERROR; - $op->user_feedback = lang_get('apikey_cannot_be_reseted_invalid_smtp_hostname'); - } - return $op; -} - - - -/* - function: initializeUserProperties - initialize members for a user object. - - args: userObj: data read from DB - argsObj: data entry from User Interface - - returns: - - -*/ -function initializeUserProperties(&$userObj,&$argsObj) -{ - if (!is_null($argsObj->login)) - { - $userObj->login = $argsObj->login; - } - $userObj->emailAddress = $argsObj->emailAddress; - - // The Black List - Jon Bokenkamp - $reddington = array('/','\\',':','*','?','<','>','|'); - $userObj->firstName = str_replace($reddington,'',$argsObj->firstName); - $userObj->lastName = str_replace($reddington,'',$argsObj->lastName); - - $userObj->globalRoleID = $argsObj->rights_id; - $userObj->locale = $argsObj->locale; - $userObj->isActive = $argsObj->user_is_active; - $userObj->authentication = trim($argsObj->authentication); -} - -function decodeRoleId(&$dbHandler,$roleID) -{ - $roleInfo = tlRole::getByID($dbHandler,$roleID); - return $roleInfo->name; -} - -function renderGui(&$smartyObj,&$argsObj,$templateCfg) -{ - $doRender = false; - switch($argsObj->doAction) - { - case "edit": - case "create": - case "resetPassword": - case "genAPIKey": - $doRender = true; - $tpl = $templateCfg->default_template; - break; - - case "doCreate": - case "doUpdate": - if(!is_null($templateCfg->template)) - { - $doRender = true; - $tpl = $templateCfg->template; - } - else - { - header("Location: usersView.php"); - exit(); - } - break; - - } - - if($doRender) - { - $smartyObj->display($templateCfg->template_dir . $tpl); - } -} - - -/** - * - */ -function initializeGui(&$dbHandler,&$argsObj) -{ - $userObj = &$argsObj->user; - - $guiObj = new stdClass(); - - $guiObj->user = null; - switch($argsObj->doAction) - { - case 'edit': - // Because we can arrive with login, we need to check if we can get - // id from login - if(strlen(trim($argsObj->login)) > 0) - { - $argsObj->user_id = tlUser::doesUserExist($dbHandler,$argsObj->login); - } - - if( is_null($argsObj->user_id) || intval($argsObj->user_id) <= 0) - { - // need to manage some sort of error message - $guiObj->op = new stdClass(); - $guiObj->op->status = tl::ERROR; - $guiObj->op->user_feedback = - sprintf(lang_get('login_does_not_exist'),$argsObj->login); - } - else - { - $guiObj->user = new tlUser(intval($argsObj->user_id)); - $guiObj->user->readFromDB($dbHandler); - } - $guiObj->main_title = lang_get("action_{$argsObj->doAction}_user"); - break; - - case "resetPassword": - case "genAPIKey": - $guiObj->user = new tlUser($argsObj->user_id); - $guiObj->user->readFromDB($dbHandler); - break; - } - - $guiObj->op = new stdClass(); - $guiObj->op->user_feedback = ''; - $guiObj->op->status = tl::OK; - - $guiObj->authCfg = config_get('authentication'); - $guiObj->auth_method_opt = array(lang_get('default_auth_method') . - "(" . $guiObj->authCfg['domain'][$guiObj->authCfg['method']]['description'] . ")" => ''); - - $dummy = array_keys($guiObj->authCfg['domain']); - foreach($dummy as $xc) - { - // description => html option value - $guiObj->auth_method_opt[$xc] = $xc; - } - - $guiObj->auth_method_opt = array_flip($guiObj->auth_method_opt); - - $guiObj->optLocale = config_get('locales'); - - $guiObj->grants = getGrantsForUserMgmt($dbHandler,$userObj); - - $guiObj->grants->mgt_view_events = - $userObj->hasRight($dbHandler,"mgt_view_events"); - - $guiObj->expiration_date = $argsObj->expiration_date; - - $noExpirationUsers = array_flip(config_get('noExpDateUsers')); - $guiObj->expDateEnabled = true; - if( !is_null($guiObj->user) ) - { - $guiObj->expDateEnabled = !isset($noExpirationUsers[$guiObj->user->login]); - } - - return $guiObj; -} - -/** - * - */ -function initLabels() -{ - $tg = array('action_create_user' => null,'action_edit_user' => null); - $labels = init_labels($tg); - return $labels; -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'mgt_users'); -} \ No newline at end of file diff --git a/lib/usermanagement/usersExport.php b/lib/usermanagement/usersExport.php index 4697984bee..46cf115255 100644 --- a/lib/usermanagement/usersExport.php +++ b/lib/usermanagement/usersExport.php @@ -1,108 +1,124 @@ -doAction ) -{ - case 'doExport': - doExport($db,$gui->export_filename); - break; - - default: - break; +doAction) { + case 'doExport': + doExport($db, $gui->export_filename); + break; + + default: + break; +} + +$tplCfg = templateConfiguration(); +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); +$smarty->display($tplCfg->tpl); + +/** + * + * @return stdClass + */ +function initArgs() +{ + $args = new stdClass(); + $_REQUEST = strings_stripSlashes($_REQUEST); + + $iParams = array( + "doAction" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "export_filename" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "goback_url" => array( + tlInputParameter::STRING_N, + 0, + 2048 + ) + ); + + R_PARAMS($iParams, $args); + $args->userID = $_SESSION['userID']; + + return $args; +} + +/** + * + * @param stdClass $argsObj + * @return stdClass + */ +function initializeGui($argsObj) +{ + $gui = new stdClass(); + $gui->page_title = lang_get('export_users'); + $gui->do_it = 1; + $gui->nothing_todo_msg = ''; + $gui->goback_url = ! is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; + $gui->export_filename = is_null($argsObj->export_filename) ? 'users.xml' : $argsObj->export_filename; + $gui->exportTypes = array( + 'XML' => 'XML' + ); + return $gui; +} + +/** + * + * @param database $dbHandler + * @param string $filename + * where to export + */ +function doExport(&$dbHandler, $filename) +{ + $adodbXML = new ADODB_XML("1.0", "ISO-8859-1"); + $adodbXML->setRootTagName('users'); + $adodbXML->setRowTagName('user'); + + $tables = tlObjectWithDB::getDBTables(array( + 'users' + )); + $fieldSet = 'id,login,role_id,email,first,last,locale,' . + 'default_testproject_id,active,expiration_date'; + $sql = " SELECT {$fieldSet} FROM {$tables['users']} "; + + $content = $adodbXML->ConvertToXMLString($dbHandler->db, $sql); + downloadContentsToFile($content, $filename); + exit(); +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, "mgt_users"); } - -$tplCfg = templateConfiguration(); -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); -$smarty->display($tplCfg->tpl); - - -/* - function: init_args() - - args: - - returns: - -*/ -function init_args() -{ - $args = new stdClass(); - $_REQUEST = strings_stripSlashes($_REQUEST); - - $iParams = array("doAction" => array(tlInputParameter::STRING_N,0,50), - "export_filename" => array(tlInputParameter::STRING_N,0,100), - "goback_url" => array(tlInputParameter::STRING_N,0,2048)); - - R_PARAMS($iParams,$args); - $args->userID = $_SESSION['userID']; - - return $args; -} - -/** - * - */ -function initializeGui($argsObj) -{ - $gui = new stdClass(); - $gui->page_title = lang_get('export_users'); - $gui->do_it = 1; - $gui->nothing_todo_msg = ''; - $gui->goback_url = !is_null($argsObj->goback_url) ? $argsObj->goback_url : ''; - $gui->export_filename = is_null($argsObj->export_filename) ? 'users.xml' : $argsObj->export_filename; - $gui->exportTypes = array('XML' => 'XML'); - return $gui; -} - - -/* - function: doExport() - - args: dbHandler - filename: where to export - - returns: - - -*/ -function doExport(&$dbHandler,$filename) -{ - $adodbXML = new ADODB_XML("1.0", "ISO-8859-1"); - $adodbXML->setRootTagName('users'); - $adodbXML->setRowTagName('user'); - - $tables = tlObjectWithDB::getDBTables(array('users')); - $fieldSet = 'id,login,role_id,email,first,last,locale,' . - 'default_testproject_id,active,expiration_date'; - $sql = " SELECT {$fieldSet} FROM {$tables['users']} "; - - $content = $adodbXML->ConvertToXMLString($dbHandler->db, $sql); - downloadContentsToFile($content,$filename); - exit(); -} - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,"mgt_users"); -} \ No newline at end of file diff --git a/lib/usermanagement/usersView.php b/lib/usermanagement/usersView.php index 82cf140a09..1316efb1db 100644 --- a/lib/usermanagement/usersView.php +++ b/lib/usermanagement/usersView.php @@ -1,303 +1,368 @@ -operation) -{ - case 'disable': - // user cannot disable => inactivate itself - if ($args->user_id != $args->currentUserID) - { - $user = new tlUser($args->user_id); - $gui->result = $user->readFromDB($db); - if ($gui->result >= tl::OK) - { - $gui->result = $user->setActive($db,0); - if ($gui->result >= tl::OK) - { - logAuditEvent(TLS("audit_user_disabled",$user->login),"DISABLE",$args->user_id,"users"); - $gui->user_feedback = sprintf(lang_get('user_disabled'),$user->login); - } - } - } - if ($gui->result != tl::OK) - { - $gui->user_feedback = lang_get('error_user_not_disabled'); - } - break; - - default: - break; +operation) { + case 'disable': + // user cannot disable => inactivate itself + if ($args->user_id != $args->currentUserID) { + // some minor CSRF protection checking referer + $refe = $_SERVER['HTTP_REFERER']; + $target = trim($_SESSION['basehref'], '/') . + '/lib/usermanagement/usersView.php'; + if (strpos($refe, $target) === false) { + // No good + exit(); + } + + $user = new tlUser($args->user_id); + $gui->result = $user->readFromDB($db); + if ($gui->result >= tl::OK) { + $gui->result = $user->setActive($db, 0); + if ($gui->result >= tl::OK) { + logAuditEvent(TLS("audit_user_disabled", $user->login), + "DISABLE", $args->user_id, "users"); + $gui->user_feedback = sprintf(lang_get('user_disabled'), + $user->login); + } + } + } + if ($gui->result != tl::OK) { + $gui->user_feedback = lang_get('error_user_not_disabled'); + } + break; + + default: + break; +} + +$gui->images = $smarty->getImages(); +$gui->matrix = getAllUsersForGrid($db); +$gui->tableSet[] = buildMatrix($gui, $args); + +$tplCfg = templateConfiguration(); +$smarty->assign('gui', $gui); +$smarty->display($tplCfg->tpl); + +/** + * + * @param database $dbHandler + * @return stdClass[] + */ +function initEnv(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + + // input from GET['HelloString3'], + // type: string, + // minLen: 1, + // maxLen: 15, + // regular expression: null + // checkFunction: applys checks via checkFooOrBar() to ensure its either 'foo' or 'bar' + // normalization: done via normFunction() which replaces ',' with '.' + // "HelloString3" => array("GET",tlInputParameter::STRING_N,1,15,'checkFooOrBar','normFunction'), + // + $iParams = array( + "operation" => array( + tlInputParameter::STRING_N, + 0, + 50 + ), + "user" => array( + tlInputParameter::INT_N + ) + ); + + $pParams = R_PARAMS($iParams); + $args = new stdClass(); + $args->operation = $pParams["operation"]; + $args->user_id = $pParams['user']; + + $args->currentUser = $_SESSION['currentUser']; + $args->currentUserID = $_SESSION['currentUser']->dbID; + $args->basehref = $_SESSION['basehref']; + + $gui = new stdClass(); + $gui->grants = getGrantsForUserMgmt($dbHandler, $args->currentUser); + $gui->main_title = lang_get('title_user_mgmt'); + $gui->result = null; + $gui->action = null; + $gui->user_feedback = ''; + $gui->update_title_bar = 0; + $gui->reload = 0; + + $gui->basehref = $args->basehref; + + $gui->highlight = initializeTabsmenu(); + $gui->highlight->view_users = 1; + + return array( + $args, + $gui + ); +} + +/** + * using configuration parameter ($g_role_colour) + * creates a map with following structure: + * key: role name + * value: colour + * + * If name is not defined on $g_role_colour (this normally happens for user + * defined roles), will be added with '' as colour (means default colour). + * + * @param database $db + * reference to db object + * @return array + */ +function getRoleColourCfg(&$db) +{ + $role_colour = config_get('role_colour'); + $roles = tlRole::getAll($db, null, null, null, + tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); + unset($roles[TL_ROLES_UNDEFINED]); + foreach ($roles as $roleObj) { + if (! isset($role_colour[$roleObj->name])) { + $role_colour[$roleObj->name] = ''; + } + } + return $role_colour; +} + +/** + * Builds ext-js rich table to display matrix results + * + * @param stdClass $guiObj + * @param stdClass $argsObj + * @return tlExtTable + */ +function buildMatrix(&$guiObj, &$argsObj) +{ + // th_first_name,th_last_name,th_email + // IMPORTANT DEVELOPER NOTICE + // Column order is same that present on query on getAllUsersForGrid() + // + // Where col_id is not specified, col_id will be generated this way: 'id_' . $v['title_key']. + // Example: id_th_first_name. + // + // 'tlType' => TestLinkType: will be analized and mapped accordingly on tlExtTable::buildColumns() + // + $columns = array( + array( + 'title_key' => 'th_login', + 'col_id' => 'handle', + 'width' => 100 + ), + array( + 'title_key' => 'th_first_name', + 'width' => 150 + ), + array( + 'title_key' => 'th_last_name', + 'width' => 150 + ), + array( + 'title_key' => 'th_email', + 'width' => 150 + ), + array( + 'title_key' => 'th_role', + 'width' => 150 + ), + array( + 'title_key' => 'th_locale', + 'width' => 150 + ), + array( + 'title_key' => 'th_active', + 'type' => 'oneZeroImage', + 'width' => 50 + ), + array( + 'title_key' => 'expiration_date', + 'width' => 50 + ), + array( + 'title' => 'disableUser', + 'tlType' => 'disableUser', + 'width' => 150 + ), + array( + 'hidden' => true, + 'title' => 'hidden_role_id', + 'col_id' => 'role_id' + ), + array( + 'hidden' => true, + 'title' => 'hidden_user_id', + 'col_id' => 'user_id' + ), + array( + 'hidden' => true, + 'title' => 'hidden_login', + 'col_id' => 'login' + ), + array( + 'hidden' => true, + 'title' => 'hidden_is_special', + 'col_id' => 'is_special' + ) + ); + + init_labels( + array( + 'th_login' => null, + 'th_first_name' => null, + 'th_last_name' => null, + 'expiration' => null, + 'th_email' => null + )); + + $loop2do = count($guiObj->matrix); + + // login added as workaround for SORTING, because the whole string is used then user_id + // in url takes precedence over the login displayed + $actionUrl = '' . + $guiObj->matrix[$zdx]['login'] . ""; + } + + $matrix = new tlExtTable($columns, $guiObj->matrix, 'tl_users_list'); + + // => addCustomBehaviour(columnType, ); + $matrix->addCustomBehaviour('oneZeroImage', + array( + 'render' => 'oneZeroImageRenderer' + )); + $matrix->moreViewConfig = " ,getRowClass: function(record, index) {" . + " var x = record.get('role_id');" . " return('roleCode'+x); " . " } "; + + $matrix->setImages($guiObj->images); + $matrix->allowMultiSort = false; + $matrix->sortDirection = 'DESC'; + $matrix->showToolbar = true; + $matrix->toolbarShowAllColumnsButton = true; + unset($columns); + + return $matrix; +} + +/** + * check function for tlInputParameter user_order_by + * + * @param + * $input + * @return boolean + */ +function checkUserOrderBy($input) +{ + $domain = array_flip(array( + 'order_by_role', + 'order_by_login' + )); + return isset($domain[$input]) ? true : false; +} + +/** + * + * @param database $dbHandler + * @return number|string + */ +function getAllUsersForGrid(&$dbHandler) +{ + $tables = tlObject::getDBTables(array( + 'users', + 'roles' + )); + + // Column extraction order is CRITIC for correct behaviour of Ext-JS + $sql = " SELECT '' AS handle,U.first,U.last,U.email,R.description," . + " U.locale,U.active,U.expiration_date," . + " /* this columns will not visible on GUI */ " . + " '' AS place_holder,R.id AS role_id,U.id AS user_id,U.login, 0 AS is_special " . + " FROM {$tables['users']} U " . + " JOIN {$tables['roles']} R ON U.role_id = R.id ORDER BY U.login "; + + $users = $dbHandler->get_recordset($sql); + + // because we need to render this on EXT-JS, we have issues with role + // due to <, then we are going to escape values in description column + $loop2do = count($users); + $dummy = ''; + for ($idx = 0; $idx < $loop2do; $idx ++) { + $users[$idx]['description'] = htmlentities($users[$idx]['description']); + + // localize dates + $ed = trim($users[$idx]['expiration_date']); + if ($ed != '') { + $users[$idx]['expiration_date'] = localize_dateOrTimeStamp(null, + $dummy, 'date_format', $ed); + } + } + + // Still need to understand why, but with MSSQL we use on ADODB + // fetch mode = ADODB_FETCH_BOTH, this generates numeric AND literal keys + // on row maps => for each column on result set we get to elements on row map. + // example 0,handle,1,first, and so on. + // This drives crazy EXT-JS grid + if (! is_null($users) && $dbHandler->dbType == 'mssql') { + $clean = array(); + foreach ($users as $row) { + $cr = array(); + $elem = array_keys($row); + foreach ($elem as $accessKey) { + if (! is_numeric($accessKey)) { + $cr[$accessKey] = $row[$accessKey]; + } + } + $clean[] = $cr; + } + $users = $clean; + } + + if (config_get('demoMode')) { + $loop2do = count($users); + $specialK = array_flip((array) config_get('demoSpecialUsers')); + for ($idx = 0; $idx < $loop2do; $idx ++) { + $users[$idx]['is_special'] = isset($specialK[$users[$idx]['login']]) ? 1 : 0; + } + } + + return $users; +} + +/** + * + * @param database $db + * @param tlUser $user + * @return string + */ +function checkRights(&$db, &$user) +{ + return $user->hasRight($db, 'mgt_users'); } - -$gui->images = $smarty->getImages(); -$gui->matrix = getAllUsersForGrid($db); -$gui->tableSet[] = buildMatrix($gui, $args); - -$tplCfg = templateConfiguration(); -$smarty->assign('gui',$gui); -$smarty->display($tplCfg->tpl); - - -/** - * - */ -function initEnv(&$dbHandler) -{ - $_REQUEST=strings_stripSlashes($_REQUEST); - - // input from GET['HelloString3'], - // type: string, - // minLen: 1, - // maxLen: 15, - // regular expression: null - // checkFunction: applys checks via checkFooOrBar() to ensure its either 'foo' or 'bar' - // normalization: done via normFunction() which replaces ',' with '.' - // "HelloString3" => array("GET",tlInputParameter::STRING_N,1,15,'checkFooOrBar','normFunction'), - // - $iParams = array("operation" => array(tlInputParameter::STRING_N,0,50), - "user" => array(tlInputParameter::INT_N)); - - $pParams = R_PARAMS($iParams); - $args = new stdClass(); - $args->operation = $pParams["operation"]; - $args->user_id = $pParams['user']; - - $args->currentUser = $_SESSION['currentUser']; - $args->currentUserID = $_SESSION['currentUser']->dbID; - $args->basehref = $_SESSION['basehref']; - - - $gui = new stdClass(); - $gui->grants = getGrantsForUserMgmt($dbHandler,$args->currentUser); - $gui->main_title = lang_get('title_user_mgmt'); - $gui->result = null; - $gui->action = null; - $gui->user_feedback = ''; - $gui->update_title_bar = 0; - $gui->reload = 0; - - $gui->basehref = $args->basehref; - - $gui->highlight = initialize_tabsmenu(); - $gui->highlight->view_users = 1; - - return array($args,$gui); -} - -/* - function: getRoleColourCfg - using configuration parameter ($g_role_colour) - creates a map with following structure: - key: role name - value: colour - - If name is not defined on $g_role_colour (this normally - happens for user defined roles), will be added with '' as colour (means default colour). - - args: db: reference to db object - - returns: map - -*/ -function getRoleColourCfg(&$db) -{ - $role_colour = config_get('role_colour'); - $roles = tlRole::getAll($db,null,null,null,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM); - unset($roles[TL_ROLES_UNDEFINED]); - foreach($roles as $roleObj) - { - if(!isset($role_colour[$roleObj->name])) - { - $role_colour[$roleObj->name] = ''; - } - } - return $role_colour; -} - - -/** - * Builds ext-js rich table to display matrix results - * - * - * return tlExtTable - * - */ -function buildMatrix(&$guiObj,&$argsObj) -{ - // th_first_name,th_last_name,th_email - // IMPORTANT DEVELOPER NOTICE - // Column order is same that present on query on getAllUsersForGrid() - // - // Where col_id is not specified, col_id will be generated this way: 'id_' . $v['title_key']. - // Example: id_th_first_name. - // - // 'tlType' => TestLinkType: will be analized and mapped accordingly on tlExtTable::buildColumns() - // - $columns = array(array('title_key' => 'th_login', 'col_id' => 'handle', 'width' => 100), - array('title_key' => 'th_first_name', 'width' => 150), - array('title_key' => 'th_last_name', 'width' => 150), - array('title_key' => 'th_email', 'width' => 150), - array('title_key' => 'th_role', 'width' => 150), - array('title_key' => 'th_locale', 'width' => 150), - array('title_key' => 'th_active', 'type' => 'oneZeroImage', 'width' => 50), - array('title_key' => 'expiration_date', 'width' => 50), - array('title' => 'disableUser', 'tlType' => 'disableUser', 'width' => 150), - array('hidden' => true, 'title' => 'hidden_role_id', 'col_id' => 'role_id'), - array('hidden' => true, 'title' => 'hidden_user_id', 'col_id' => 'user_id'), - array('hidden' => true, 'title' => 'hidden_login', 'col_id' => 'login'), - array('hidden' => true, 'title' => 'hidden_is_special', 'col_id' => 'is_special')); - - $lbl = init_labels(array('th_login' => null,'th_first_name' => null, - 'th_last_name' => null,'expiration' => null, - 'th_email' => null)); - - $loop2do = count($guiObj->matrix); - - // login added as workaround for SORTING, because the whole string is used then user_id - // in url takes precedence over the login displayed - $actionUrl = '' . $guiObj->matrix[$zdx]['login'] . - ""; - } - - - $matrix = new tlExtTable($columns, $guiObj->matrix, 'tl_users_list'); - - // => addCustomBehaviour(columnType, ); - $matrix->addCustomBehaviour('oneZeroImage', array('render' => 'oneZeroImageRenderer')); - $matrix->moreViewConfig = " ,getRowClass: function(record, index) {" . - " var x = record.get('role_id');" . - " return('roleCode'+x); " . - " } " ; - - $matrix->setImages($guiObj->images); - $matrix->allowMultiSort = false; - $matrix->sortDirection = 'DESC'; - $matrix->showToolbar = true; - $matrix->toolbarShowAllColumnsButton = true; - unset($columns); - - return $matrix; -} - - - -/** - * check function for tlInputParameter user_order_by - * - */ -function checkUserOrderBy($input) -{ - $domain = array_flip(array('order_by_role','order_by_login')); - - $status_ok = isset($domain[$input]) ? true : false; - return $status_ok; -} - -/** - * - */ -function getAllUsersForGrid(&$dbHandler) -{ - $tables = tlObject::getDBTables(array('users','roles')); - - // Column extraction order is CRITIC for correct behaviour of Ext-JS - $sql = " SELECT '' AS handle,U.first,U.last,U.email,R.description," . - " U.locale,U.active,U.expiration_date," . - " /* this columns will not visible on GUI */ " . - " '' AS place_holder,R.id AS role_id,U.id AS user_id,U.login, 0 AS is_special " . - " FROM {$tables['users']} U " . - " JOIN {$tables['roles']} R ON U.role_id = R.id ORDER BY U.login "; - - $users = $dbHandler->get_recordset($sql); - - // because we need to render this on EXT-JS, we have issues with role - // due to <, then we are going to escape values in description column - $loop2do = count($users); - $dummy = ''; - for($idx=0; $idx < $loop2do; $idx++) - { - $users[$idx]['description'] = htmlentities($users[$idx]['description']); - - // localize dates - $ed = trim($users[$idx]['expiration_date']); - if($ed != '') - { - $users[$idx]['expiration_date'] = - localize_dateOrTimeStamp(null,$dummy,'date_format',$ed); - } - } - - - // Still need to understand why, but with MSSQL we use on ADODB - // fetch mode = ADODB_FETCH_BOTH, this generates numeric AND literal keys - // on row maps => for each column on result set we get to elements on row map. - // example 0,handle,1,first, and so on. - // This drives crazy EXT-JS grid - if(!is_null($users) && $dbHandler->dbType == 'mssql') - { - $clean = array(); - foreach($users as $row) - { - $cr = array(); - $elem = array_keys($row); - foreach($elem as $accessKey) - { - if(!is_numeric($accessKey)) - { - $cr[$accessKey] = $row[$accessKey]; - } - } - $clean[] = $cr; - } - $users = $clean; - } - - if( config_get('demoMode') ) - { - $loop2do = count($users); - $specialK = array_flip((array)config_get('demoSpecialUsers')); - for($idx=0; $idx < $loop2do; $idx++) - { - $users[$idx]['is_special'] = isset($specialK[$users[$idx]['login']]) ? 1 : 0; - } - } - - return $users; -} - - - -function checkRights(&$db,&$user) -{ - return $user->hasRight($db,'mgt_users'); -} \ No newline at end of file diff --git a/linkto.php b/linkto.php index dfef527482..cc658a6afe 100644 --- a/linkto.php +++ b/linkto.php @@ -1,427 +1,434 @@ -/linkto.php?tprojectPrefix=KAOS&item=testcase&id=KAOS-4 - * - * - direct link to requirement REQ-002 in test project KAOS: - * http:///linkto.php?tprojectPrefix=KAOS&item=req&id=REQ-002 - * - * - direct link to requirement specification REQ-SPEC-AK89 in test project KAOS: - * http:///linkto.php?tprojectPrefix=KAOS&item=reqspec&id=REQ-SPEC-AK89 - * - * Anchors: - * If anchors are set (in scope, etc.) in the linked document, you can specify these - * by using &anchor=anchorname, e.g. - * http:///linkto.php?tprojectPrefix=KAOS&item=testcase&id=KAOS-4&anchor=importantpart - * - * Specials: - * - tree for requirement specification or test specification - * are expanded to the level of the item you created the link to - * - if a user has no right to view item he is redirected to main page - * - if item does not exist an errormessage shows - * - * @package TestLink - * @author asimon - * @copyright 2007-2017, TestLink community - * @link http://www.teamst.org/index.php - * - */ - -// use output buffer to prevent headers/data from being sent before -// cookies are set, else it will fail -ob_start(); - -// some session and settings stuff from original index.php -require_once('lib/functions/configCheck.php'); -checkConfiguration(); -require_once('config.inc.php'); -require_once('common.php'); -require_once('attachments.inc.php'); -require_once('requirements.inc.php'); -require_once('testcase.class.php'); -require_once('testproject.class.php'); -require_once('users.inc.php'); -testlinkInitPage($db, true); - -$smarty = new TLSmarty(); - -// display outer or inner frame? -if (!isset($_GET['load'])) -{ - // display outer frame, pass parameters to next script call for inner frame - // Direct link to testcase where TC ID prefix contains an '&' (the ampersand symbol), does not link - // - // ATTENTION: - // Because we are going to recreate an URL with paramenters on the URL, we need - // to use urlencode() on data we have got. - // - - $args = init_args(); - $args->tproject_id = 0; - if( $args->status_ok ) - { - $user = $_SESSION['currentUser']; - if($args->tprojectPrefix != '') - { - $hasRight = checkTestProject($db,$user,$args); - if( $hasRight ) - { - $gui = new stdClass(); - $gui->titleframe = 'lib/general/navBar.php?caller=linkto'; - $gui->navbar_height = config_get('navbar_height'); - - if( $args->tproject_id > 0) - { - $gui->titleframe .= '&testproject=' . $args->tproject_id; - } - $gui->title = lang_get('main_page_title'); - $gui->mainframe = 'linkto.php?' . buildLink($args); - $smarty->assign('gui', $gui); - $smarty->display('main.tpl'); - } - } - } -} -else -{ - // - // inner frame, parameters passed - // figure out what to display - // - // key: item, value: url to tree management page - $itemCode = array('req' => 'lib/requirements/reqSpecListTree.php', - 'reqspec' => 'lib/requirements/reqSpecListTree.php', - 'testcase' => 'lib/testcases/listTestCases.php?feature=edit_tc', - 'testsuite' => 'lib/testcases/listTestCases.php?feature=edit_tc'); - - - $op = array('status_ok' => true, 'msg' => ''); - - // First check for keys in _GET that MUST EXIST - // key: key on _GET, value: labelID defined on strings.txt - $mustKeys = array('tprojectPrefix' => 'testproject_not_set', - 'item' => 'item_not_set', 'id' => 'id_not_set'); - - foreach($mustKeys as $key => $labelID) - { - $op['status_ok'] = isset($_GET[$key]); - if( !$op['status_ok']) - { - $op['msg'] = lang_get($labelID); - break; - } - } - - $args = init_args(); - if($op['status_ok']) - { - $tproject = new testproject($db); - $tproject_data = $tproject->get_by_prefix($args->tprojectPrefix); - if(($op['status_ok'] = !is_null($tproject_data))) - { - $tproject->setSessionProject($tproject_data['id']); - $op['status_ok'] = isset($itemCode[$args->item]); - $op['msg'] = sprintf(lang_get('invalid_item'),$args->item); - } - else - { - $op['msg'] = sprintf(lang_get('testproject_not_found'),$args->tprojectPrefix); - } - } - - if($op['status_ok']) - { - // Build name of function to call for doing the job. - $pfn = 'process_' . $args->item; - $jump_to = $pfn($db, $args->id, $tproject_data['id'], $args->tprojectPrefix, $args->version); - $op['status_ok'] = !is_null($jump_to['url']); - $op['msg'] = $jump_to['msg']; - } - - if($op['status_ok']) - { - // need to set test project item on Navbar - // add anchor to URL - $url = $jump_to['url'] . $args->anchor; - - $smarty->assign('title', lang_get('main_page_title')); - $smarty->assign('treewidth', TL_FRMWORKAREA_LEFT_FRAME_WIDTH); - $smarty->assign('workframe', $url); - $smarty->assign('treeframe', $itemCode[$args->item]); - $smarty->display('frmInner.tpl'); - } - else - { - echo $op['msg']; - ob_end_flush(); - exit(); - } -} -ob_end_flush(); - - -/** - * - * - */ -function checkTestProject(&$db,&$user,&$args) -{ - $hasRight = false; - $tproject_mgr = new testproject($db); - $item_info = $tproject_mgr->get_by_prefix($args->tprojectPrefix); - - if(($op['status_ok'] = !is_null($item_info))) - { - $args->tproject_id = intval($item_info['id']); - switch($args->item) - { - case 'testcase': - case 'testsuite': - $hasRight = $user->hasRight($db,'mgt_view_tc',$args->tproject_id); - break; - - case 'req': - case 'reqspec': - $hasRight = $user->hasRight($db,'mgt_view_req',$args->tproject_id); - break; - - default: - // need to fail!! - break; - - } - } - return $hasRight; -} - - -/** - * - */ -function init_args() -{ - $args = new stdClass(); - $args->tprojectPrefix = isset($_GET['tprojectPrefix']) ? $_GET['tprojectPrefix'] : null; - $args->id = isset($_GET['id']) ? $_GET['id'] : null; - - $args->anchor = isset($_GET['anchor']) ? $_GET['anchor'] : null; - $args->version = isset($_GET['version']) ? $_GET['version'] : null; - $args->item = isset($_GET['item']) ? $_GET['item'] : null; - - $args->status_ok = !is_null($args->tprojectPrefix) && !is_null($args->id) && !is_null($args->item); - - return $args; -} - -/** - * - */ -function buildLink(&$argsObj) -{ - - // link => $item . $id . $version . $tprojectPrefix . '&load' . $anchor; - $lk = isset($_GET['item']) ? "item=" . $_GET['item'] : ''; - $lk .= isset($_GET['id']) ? "&id=" . urlencode($_GET['id']) : ''; - $lk .= isset($_GET['version']) ? "&version=" . $_GET['version'] : ''; - $lk .= isset($_GET['tprojectPrefix']) ? "&tprojectPrefix=" . urlencode($_GET['tprojectPrefix']) : ''; - $lk .= '&load' . (isset($_GET['anchor']) ? '&anchor=' . $_GET['anchor'] : ""); - - return $lk; -} - - - - -/** - * process_testcase - * - */ -function process_testcase(&$dbHandler,$externalID, $tprojectID, $tprojectPrefix, $version) -{ - $ret = array(); - $ret['url'] = null; - $ret['msg'] = sprintf(lang_get('testcase_not_found'), $externalID, $tprojectPrefix); - - $tcase_mgr = new testcase($dbHandler); - $tcaseID = $tcase_mgr->getInternalID($externalID); - if($tcaseID > 0) - { - $ret['url'] = "lib/testcases/archiveData.php?edit=testcase&id={$tcaseID}"; - $ret['msg'] = 'ok'; - - $ckCfg = config_get('cookie'); - $ckCfg->prefix .= 'ys-tproject_'; - $cookie = buildCookie($dbHandler,$tcaseID,$tprojectID,$ckCfg->prefix); - - $ckObj = new stdClass(); - $ckObj->name = $cookie['value']; - $ckObj->value = $cookie['path']; - tlSetCookie($ckObj); - } - - return $ret; -} - - -/** - * process_req - * - * @internal revisions - */ -function process_req(&$dbHandler, $docID, $tprojectID, $tprojectPrefix, $version) -{ - $ret = array('url' => null, 'msg' => null); - - // First step: get this requirement's database ID by its Doc-ID (only if this Doc-ID exists). - $req_mgr = new requirement_mgr($dbHandler); - $req = $req_mgr->getByDocID($docID, $tprojectID); - $req = is_null($req) ? null : current($req); - $req_id = is_null($req) ? null : $req['id']; - $version_id = null; - - if (is_null($req_id)) - { - $ret['msg'] = sprintf(lang_get('req_not_found'), $docID, $tprojectPrefix); - } - - // Second step: If the requirement exists and a version was given, we have to check here if this specific version exists, too. - if(!is_null($req_id) && !is_null($version) && is_numeric($version)) - { - $req = $req_mgr->get_by_id($req_id, null, $version); - $req = is_null($req) ? null : current($req); - - // does this requirement really have the correct version number? - $version_id = !is_null($req) && ($req['version'] == $version) ? $req['version_id'] : null; - - if (is_null($version_id)) - { - // add direct link to current version to output - $req_url = $_SESSION['basehref'] . 'linkto.php?load&tprojectPrefix=' . - urlencode($tprojectPrefix) . '&item=req&id=' . urlencode($docID); - $ret['msg'] = sprintf(lang_get('req_version_not_found'), $version, $docID, $tprojectPrefix); - $ret['msg'] .= sprintf(" %s", lang_get('direct_link_on_wrong_version')); - $req_id = null; - } - } - - // Third and last step: set cookie and build the link (only if the requested item really was found). - if(!is_null($req_id)) - { - $ret['url'] = "lib/requirements/reqView.php?item=requirement&requirement_id=$req_id"; - - // link to open in requirement frame must include version - if (!is_null($version_id)) - { - $ret['url'] .= "&req_version_id=$version_id"; - } - - $ckCfg = config_get('cookie'); - $ckCfg->prefix .= 'requirement_spec'; - $cookie = buildCookie($dbHandler,$req_id,$tprojectID,$ckCfg->prefix); - - $ckObj = new stdClass(); - $ckObj->name = $cookie['value']; - $ckObj->value = $cookie['path']; - tlSetCookie($ckObj); - } - - return $ret; -} - - - -/** - * process_reqspec - * - */ -function process_reqspec(&$dbHandler, $docID, $tprojectID, $tprojectPrefix, $version) -{ - $ret = array(); - $ret['url'] = null; - $ret['msg'] = sprintf(lang_get('req_spec_not_found'), $docID,$tprojectPrefix); - - $reqspec_mgr = new requirement_spec_mgr($dbHandler); - $reqSpec = $reqspec_mgr->getByDocID($docID,$tprojectID); - - if( !is_null($reqSpec) ) - { - $reqSpec = current($reqSpec); - $id = $reqSpec['id']; - $ret['url'] = "lib/requirements/reqSpecView.php?req_spec_id={$id}"; - - $ckCfg = config_get('cookie'); - $ckCfg->prefix .= 'ys-requirement_spec'; - $cookie = buildCookie($dbHandler,$id,$tprojectID,$ckCfg->prefix); - - $ckObj = new stdClass(); - $ckObj->name = $cookie['value']; - $ckObj->value = $cookie['path']; - tlSetCookie($ckObj); - } - return $ret; -} - - - -/** - * - * - */ -function buildCookie(&$dbHandler,$itemID,$tprojectID,$cookiePrefix) -{ - $tree_mgr = new tree($dbHandler); - $path = $tree_mgr->get_path($itemID); - $parents = array(); - $parents[] = $tprojectID; - foreach($path as $node) - { - $parents[] = $node['id']; - } - array_pop($parents); - $cookieInfo['path'] = 'a:s%3A/' . implode("/", $parents); - $cookieInfo['value'] = $cookiePrefix . $tprojectID . '_ext-comp-1001' ; - return $cookieInfo; -} - - -/** - * process_testsuite - * - * http://localhost/development/gitorious/testlink/linkto.php?tprojectPrefix=333&item=testsuite&id=2894 - - */ -function process_testsuite(&$dbHandler,$tsuiteID, $tprojectID, $tprojectPrefix) -{ - $ret = array(); - $ret['url'] = null; - $ret['msg'] = sprintf(lang_get('testsuite_not_found'), $tsuiteID, $tprojectPrefix); - - $ret['url'] = 'lib/testcases/archiveData.php?print_scope=test_specification' . - '&edit=testsuite&level=testsuite&containerType=testsuite&id=' . $tsuiteID; - - $ret['msg'] = 'ok'; - - $ckCfg = config_get('cookie'); - $ckCfg->prefix .= 'ys-tproject_'; - $cookie = buildCookie($dbHandler,$tsuiteID,$tprojectID,$ckCfg->prefix); - - $ckObj = new stdClass(); - $ckObj->name = $cookie['value']; - $ckObj->value = $cookie['path']; - tlSetCookie($ckObj); - - - return $ret; +/linkto.php?tprojectPrefix=KAOS&item=testcase&id=KAOS-4 + * http:///linkto.php?testcase=KAOS-4 + * + * + * - direct link to requirement REQ-002 in test project KAOS: + * http:///linkto.php?tprojectPrefix=KAOS&item=req&id=REQ-002 + * + * - direct link to requirement specification REQ-SPEC-AK89 in test project KAOS: + * http:///linkto.php?tprojectPrefix=KAOS&item=reqspec&id=REQ-SPEC-AK89 + * + * Anchors: + * If anchors are set (in scope, etc.) in the linked document, you can specify these + * by using &anchor=anchorname, e.g. + * http:///linkto.php?tprojectPrefix=KAOS&item=testcase&id=KAOS-4&anchor=importantpart + * + * Specials: + * - tree for requirement specification or test specification + * are expanded to the level of the item you created the link to + * - if a user has no right to view item he is redirected to main page + * - if item does not exist an errormessage shows + * + * @package TestLink + * @author asimon + * @copyright 2007-2023, TestLink community + * @link http://www.teamst.org/index.php + * + */ + +// use output buffer to prevent headers/data from being sent before +// cookies are set, else it will fail +ob_start(); + +// some session and settings stuff from original index.php +require_once 'lib/functions/configCheck.php'; +checkConfiguration(); +require_once 'config.inc.php'; +require_once 'common.php'; +require_once 'attachments.inc.php'; +require_once 'requirements.inc.php'; +require_once 'testcase.class.php'; +require_once 'testproject.class.php'; +require_once 'users.inc.php'; +testlinkInitPage($db, true); + +$smarty = new TLSmarty(); + +// display outer or inner frame? +if (! isset($_GET['load'])) { + // display outer frame, pass parameters to next script call for inner frame + // Direct link to testcase where TC ID prefix contains an '&' (the ampersand symbol), does not link + // + // ATTENTION: + // Because we are going to recreate an URL with paramenters on the URL, we need + // to use urlencode() on data we have got. + // + + $args = init_args(); + $args->tproject_id = 0; + if ($args->status_ok) { + $user = $_SESSION['currentUser']; + if ($args->tprojectPrefix != '') { + $hasRight = checkTestProject($db, $user, $args); + if ($hasRight) { + $gui = new stdClass(); + $gui->titleframe = 'lib/general/navBar.php?caller=linkto'; + $gui->navbar_height = config_get('navbar_height'); + + if ($args->tproject_id > 0) { + $gui->titleframe .= '&testproject=' . $args->tproject_id; + } + $gui->title = lang_get('main_page_title'); + $gui->mainframe = 'linkto.php?' . buildLink($args); + $smarty->assign('gui', $gui); + $smarty->display('main.tpl'); + } + } + } +} else { + // inner frame, parameters passed + // figure out what to display + // + // key: item, value: url to tree management page + $itemCode = array( + 'req' => 'lib/requirements/reqSpecListTree.php', + 'reqspec' => 'lib/requirements/reqSpecListTree.php', + 'testcase' => 'lib/testcases/listTestCases.php?feature=edit_tc', + 'testsuite' => 'lib/testcases/listTestCases.php?feature=edit_tc' + ); + + $op = [ + 'status_ok' => true, + 'msg' => '' + ]; + + $args = init_args(); + if (! $args->status_ok) { + // key: key on _GET, value: labelID defined on strings.txt + $mustKeys = [ + 'tprojectPrefix' => 'testproject_not_set', + 'item' => 'item_not_set', + 'id' => 'id_not_set' + ]; + + foreach ($mustKeys as $key => $labelID) { + $op['status_ok'] = isset($_GET[$key]); + if (! $op['status_ok']) { + $op['msg'] = __FILE__ . ' >> ' . lang_get($labelID); + break; + } + } + } + + if ($op['status_ok']) { + $tproject = new testproject($db); + $tproject_data = $tproject->get_by_prefix($args->tprojectPrefix); + if ($op['status_ok'] = ! is_null($tproject_data)) { + $tproject->setSessionProject($tproject_data['id']); + $op['status_ok'] = isset($itemCode[$args->item]); + $op['msg'] = sprintf(lang_get('invalid_item'), $args->item); + } else { + $op['msg'] = sprintf(lang_get('testproject_not_found'), + $args->tprojectPrefix); + } + } + + if ($op['status_ok']) { + // Build name of function to call for doing the job. + $pfn = 'process_' . $args->item; + $jump_to = $pfn($db, $args->id, $tproject_data['id'], + $args->tprojectPrefix, $args->version); + $op['status_ok'] = ! is_null($jump_to['url']); + $op['msg'] = $jump_to['msg']; + } + + if ($op['status_ok']) { + // need to set test project item on Navbar + // add anchor to URL + $url = $jump_to['url'] . $args->anchor; + + $smarty->assign('title', lang_get('main_page_title')); + $smarty->assign('treewidth', TL_FRMWORKAREA_LEFT_FRAME_WIDTH); + $smarty->assign('workframe', $url); + $smarty->assign('treeframe', $itemCode[$args->item]); + $smarty->display('frmInner.tpl'); + } else { + echo $op['msg']; + ob_end_flush(); + exit(); + } +} +ob_end_flush(); + +/** + */ +function checkTestProject(&$db, &$user, &$args) +{ + $hasRight = false; + $tproject_mgr = new testproject($db); + $item_info = $tproject_mgr->get_by_prefix($args->tprojectPrefix); + + if ($op['status_ok'] = ! is_null($item_info)) { + $args->tproject_id = intval($item_info['id']); + switch ($args->item) { + case 'testcase': + case 'testsuite': + $hasRight = $user->hasRight($db, 'mgt_view_tc', + $args->tproject_id); + break; + + case 'req': + case 'reqspec': + $hasRight = $user->hasRight($db, 'mgt_view_req', + $args->tproject_id); + break; + + default: + // need to fail!! + break; + } + } + return $hasRight; +} + +/** + */ +function init_args() +{ + $args = new stdClass(); + $args->tprojectPrefix = isset($_GET['tprojectPrefix']) ? $_GET['tprojectPrefix'] : null; + $args->id = isset($_GET['id']) ? $_GET['id'] : null; + + $args->anchor = isset($_GET['anchor']) ? $_GET['anchor'] : null; + $args->version = isset($_GET['version']) ? $_GET['version'] : null; + $args->item = isset($_GET['item']) ? $_GET['item'] : null; + + $args->status_ok = ! is_null($args->tprojectPrefix) && ! is_null($args->id) && + ! is_null($args->item); + if (! $args->status_ok) { + // new try + // http:///linkto.php?testcase=KAOS-4 + $args->id = isset($_GET['testcase']) ? $_GET['testcase'] : null; + $args->testcase = $args->id; + $args->item = 'testcase'; + $glue = config_get('testcase_cfg')->glue_character; + $pieces = explode($glue, $args->testcase); + if (count($pieces) != 2) { + return $args->status_ok; + } + $args->tprojectPrefix = $pieces[0]; + $args->status_ok = ! is_null($args->tprojectPrefix) && + ! is_null($args->id) && ! is_null($args->item); + } + return $args; +} + +/** + */ +function buildLink(&$argsObj) +{ + + // link => $item . $id . $version . $tprojectPrefix . '&load' . $anchor; + $key2loop = [ + "item", + "id", + "version", + "tprojectPrefix", + "testcase", + "anchor" + ]; + $lk = 'load'; + foreach ($key2loop as $key) { + $value = ''; + if (isset($_GET[$key])) { + $value = $_GET[$key]; + } + if (property_exists($argsObj, $key)) { + $value = $argsObj->$key; + } + + if ($key == "tprojectPrefix" || $key == "testcase" || $key == "id") { + $value = urlencode($value); + } + $lk .= "&" . $key . "=" . $value; + } + + return $lk; +} + +/** + * process_testcase + */ +function process_testcase(&$dbHandler, $externalID, $tprojectID, $tprojectPrefix, + $version) +{ + $ret = array(); + $ret['url'] = null; + $ret['msg'] = sprintf(lang_get('testcase_not_found'), $externalID, + $tprojectPrefix); + + $tcaseMgr = new testcase($dbHandler); + $tcaseID = $tcaseMgr->getInternalID($externalID); + if ($tcaseID > 0) { + $ret['url'] = "lib/testcases/archiveData.php?edit=testcase&id={$tcaseID}"; + $ret['msg'] = 'ok'; + + $ckCfg = config_get('cookie'); + $ckCfg->prefix .= 'ys-tproject_'; + $cookie = buildCookie($dbHandler, $tcaseID, $tprojectID, $ckCfg->prefix); + + $ckObj = new stdClass(); + $ckObj->name = $cookie['value']; + $ckObj->value = $cookie['path']; + tlSetCookie($ckObj); + } + + return $ret; +} + +/** + * process_req + * + * @internal revisions + */ +function process_req(&$dbHandler, $docID, $tprojectID, $tprojectPrefix, $version) +{ + $ret = array( + 'url' => null, + 'msg' => null + ); + + // First step: get this requirement's database ID by its Doc-ID (only if this Doc-ID exists). + $req_mgr = new requirement_mgr($dbHandler); + $req = $req_mgr->getByDocID($docID, $tprojectID); + $req = is_null($req) ? null : current($req); + $req_id = is_null($req) ? null : $req['id']; + $version_id = null; + + if (is_null($req_id)) { + $ret['msg'] = sprintf(lang_get('req_not_found'), $docID, $tprojectPrefix); + } + + // Second step: If the requirement exists and a version was given, we have to check here if this specific version exists, too. + if (! is_null($req_id) && ! is_null($version) && is_numeric($version)) { + $req = $req_mgr->get_by_id($req_id, null, $version); + $req = is_null($req) ? null : current($req); + + // does this requirement really have the correct version number? + $version_id = ! is_null($req) && ($req['version'] == $version) ? $req['version_id'] : null; + + if (is_null($version_id)) { + // add direct link to current version to output + $req_url = $_SESSION['basehref'] . 'linkto.php?load&tprojectPrefix=' . + urlencode($tprojectPrefix) . '&item=req&id=' . urlencode($docID); + $ret['msg'] = sprintf(lang_get('req_version_not_found'), $version, + $docID, $tprojectPrefix); + $ret['msg'] .= sprintf(" %s", + lang_get('direct_link_on_wrong_version')); + $req_id = null; + } + } + + // Third and last step: set cookie and build the link (only if the requested item really was found). + if (! is_null($req_id)) { + $ret['url'] = "lib/requirements/reqView.php?item=requirement&requirement_id=$req_id"; + + // link to open in requirement frame must include version + if (! is_null($version_id)) { + $ret['url'] .= "&req_version_id=$version_id"; + } + + $ckCfg = config_get('cookie'); + $ckCfg->prefix .= 'requirement_spec'; + $cookie = buildCookie($dbHandler, $req_id, $tprojectID, $ckCfg->prefix); + + $ckObj = new stdClass(); + $ckObj->name = $cookie['value']; + $ckObj->value = $cookie['path']; + tlSetCookie($ckObj); + } + + return $ret; +} + +/** + * process_reqspec + */ +function process_reqspec(&$dbHandler, $docID, $tprojectID, $tprojectPrefix, + $version) +{ + $ret = array(); + $ret['url'] = null; + $ret['msg'] = sprintf(lang_get('req_spec_not_found'), $docID, + $tprojectPrefix); + + $reqspec_mgr = new requirement_spec_mgr($dbHandler); + $reqSpec = $reqspec_mgr->getByDocID($docID, $tprojectID); + + if (! is_null($reqSpec)) { + $reqSpec = current($reqSpec); + $id = $reqSpec['id']; + $ret['url'] = "lib/requirements/reqSpecView.php?req_spec_id={$id}"; + + $ckCfg = config_get('cookie'); + $ckCfg->prefix .= 'ys-requirement_spec'; + $cookie = buildCookie($dbHandler, $id, $tprojectID, $ckCfg->prefix); + + $ckObj = new stdClass(); + $ckObj->name = $cookie['value']; + $ckObj->value = $cookie['path']; + tlSetCookie($ckObj); + } + return $ret; +} + +/** + */ +function buildCookie(&$dbHandler, $itemID, $tprojectID, $cookiePrefix) +{ + $tree_mgr = new tree($dbHandler); + $path = $tree_mgr->get_path($itemID); + $parents = array(); + $parents[] = $tprojectID; + foreach ($path as $node) { + $parents[] = $node['id']; + } + array_pop($parents); + $cookieInfo['path'] = 'a:s%3A/' . implode("/", $parents); + $cookieInfo['value'] = $cookiePrefix . $tprojectID . '_ext-comp-1001'; + return $cookieInfo; +} + +/** + * process_testsuite + * + * http://localhost/development/gitorious/testlink/linkto.php?tprojectPrefix=333&item=testsuite&id=2894 + */ +function process_testsuite(&$dbHandler, $tsuiteID, $tprojectID, $tprojectPrefix) +{ + $ret = array(); + $ret['url'] = null; + $ret['msg'] = sprintf(lang_get('testsuite_not_found'), $tsuiteID, + $tprojectPrefix); + + $ret['url'] = 'lib/testcases/archiveData.php?print_scope=test_specification' . + '&edit=testsuite&level=testsuite&containerType=testsuite&id=' . $tsuiteID; + + $ret['msg'] = 'ok'; + + $ckCfg = config_get('cookie'); + $ckCfg->prefix .= 'ys-tproject_'; + $cookie = buildCookie($dbHandler, $tsuiteID, $tprojectID, $ckCfg->prefix); + + $ckObj = new stdClass(); + $ckObj->name = $cookie['value']; + $ckObj->value = $cookie['path']; + tlSetCookie($ckObj); + + return $ret; } diff --git a/lnl.php b/lnl.php index d74a7f3056..fbbc6b8e48 100644 --- a/lnl.php +++ b/lnl.php @@ -1,269 +1,296 @@ -light) { - case 'red': - // can not find user or item - break; - - case 'green': - $reportCfg = config_get('reports_list'); - $what2launch = null; - $cfg = isset($reportCfg[$args->type]) ? $reportCfg[$args->type] : null; - - switch($args->type) { - case 'exec': - $what2launch = "lib/execute/execPrint.php" . - "?id={$args->id}&apikey=$args->apikey"; - break; - - case 'file': - $what2launch = "lib/attachments/attachmentdownload.php" . - "?id={$args->id}&apikey=$args->apikey"; - break; - - case 'metricsdashboard': - $what2launch = "lib/results/metricsDashboard.php?apikey=$args->apikey"; - break; - - case 'test_report': - $param = "&type={$args->type}&level=testproject" . - "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . - "&header=y&summary=y&toc=y&body=y&passfail=y&cfields=y&metrics=y&author=y" . - "&requirement=y&keyword=y¬es=y&headerNumbering=y&format=" . FORMAT_HTML; - $what2launch = "lib/results/printDocument.php?apikey=$args->apikey{$param}"; - break; - - case 'testreport_onbuild': - $param = "&type={$args->type}&level=testproject" . - "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}&build_id={$args->build_id}" . - "&header=y&summary=y&toc=y&body=y&passfail=y&cfields=y&metrics=y&author=y" . - "&requirement=y&keyword=y¬es=y&headerNumbering=y&format=" . FORMAT_HTML; - $what2launch = "lib/results/printDocument.php?apikey=$args->apikey{$param}"; - break; - - case 'test_plan': - $param = "&type={$args->type}&level=testproject" . - "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . - "&header=y&summary=y&toc=y&body=y&passfail=n&cfields=y&metrics=y&author=y" . - "&requirement=y&keyword=y¬es=y&headerNumbering=y&format=" . FORMAT_HTML; - $what2launch = "lib/results/printDocument.php?apikey=$args->apikey{$param}"; - break; - - case 'testspec': - $param = "&type={$args->type}&level=testproject&id={$args->tproject_id}" . - "&tproject_id={$args->tproject_id}" . - "&header=y&summary=y&toc=y&body=y&cfields=y&author=y". - "&requirement=y&keyword=y&headerNumbering=y&format=" . FORMAT_HTML; - $what2launch = $cfg['url'] . "?apikey=$args->apikey{$param}"; - break; - - - case 'metrics_tp_general': - $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . - "&format=" . FORMAT_HTML; - $what2launch = $cfg['url'] . "?apikey=$args->apikey{$param}"; - break; - - case 'list_tc_failed': - case 'list_tc_blocked': - case 'list_tc_not_run': - $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . - "&format={$args->format}"; - $what2launch = $cfg['url'] ."&apikey=$args->apikey{$param}"; - break; - - case 'results_matrix'; - $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . - "&format={$args->format}"; - $what2launch = $cfg['url'] ."?apikey=$args->apikey{$param}"; - break; - - - case 'results_by_tester_per_build'; - $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}&format=" . FORMAT_HTML; - $what2launch = $cfg['url'] ."?apikey=$args->apikey{$param}"; - break; - - case 'charts_basic': - $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}&format=" . FORMAT_HTML; - $what2launch = $cfg['url'] ."?apikey=$args->apikey{$param}"; - break; - - case 'abslatest_results_matrix'; - $param = "&tproject_id={$args->tproject_id}" . - "&tplan_id={$args->tplan_id}" . - "&format={$args->format}"; - $what2launch = $cfg['url'] ."?apikey=$args->apikey{$param}"; - break; - - case 'report_exec_timeline'; - $param = "&tproject_id={$args->tproject_id}" . - "&tplan_id={$args->tplan_id}" . - "&format={$args->format}"; - $what2launch = $cfg['url'] ."?apikey=$args->apikey{$param}"; - break; - - default: - $needle = 'list_tc_'; - $nl = strlen($needle); - if(strpos($key,$needle) !== FALSE) { - $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . - "&format={$args->format}"; - $what2launch = $cfg['url'] ."&apikey=$args->apikey{$param}"; - } else { - - $awl = config_get('accessWithoutLogin'); - if( !isset($awl[$args->type]) ) { - echo 'ABORTING - UNKNOWN TYPE:' . $args->type; - die(); - } - - $conf = $awl[$args->type]; - $param = ""; - foreach($args->use as $prop => $useIt) { - $param .= "&$prop={$args->$prop}"; - } - $what2launch = $conf['url'] ."&apikey=$args->apikey{$param}"; - } - break; - } - - if(!is_null($what2launch)) { - // changed to be able to get XLS file using wget - // redirect(TL_BASE_HREF . $what2launch); - //echo $what2launch; - //die(); - header('Location:' . TL_BASE_HREF . $what2launch); - exit(); - } - break; - - default: - // ?? - break; -} - - - -/** - * - */ -function init_args(&$dbHandler) { - - $_REQUEST = strings_stripSlashes($_REQUEST); - $args = new stdClass(); - - try { - // ATTENTION - give a look to $tlCfg->reports_list - // format domain: see reports.cfg.php FORMAT_* - $typeSize = 30; - $userAPIkeyLen = 32; - $objectAPIkeyLen = 64; - - $iParams = array("apikey" => array(tlInputParameter::STRING_N, - $userAPIkeyLen,$objectAPIkeyLen), - "tproject_id" => array(tlInputParameter::INT_N), - "tplan_id" => array(tlInputParameter::INT_N), - "build_id" => array(tlInputParameter::INT_N), - "level" => array(tlInputParameter::STRING_N,0,16), - "type" => array(tlInputParameter::STRING_N,0,$typeSize), - 'id' => array(tlInputParameter::INT_N), - 'format' => array(tlInputParameter::STRING_N,0,1), - 'entities' => array(tlInputParameter::STRING_N,0,3)); - } catch (Exception $e) { - echo $e->getMessage(); - exit(); - } - - R_PARAMS($iParams,$args); - - $args->format = intval($args->format); - $args->format = ($args->format <= 0) ? FORMAT_HTML : $args->format; - - $args->envCheckMode = $args->type == 'file' ? 'hippie' : 'paranoic'; - $args->light = 'red'; - $opt = array('setPaths' => true,'clearSession' => true); - - // what to use when is custom - $masks = array('tproject_id' => 1, 'tplan_id' => 2, 'build_id' => 4); - $args->use = $masks; - foreach($masks as $kx => $mm) { - $args->use[$kx] = (($args->entities & $mm) > 0); - } - - // validate apikey to avoid SQL injection - $args->apikey = trim($args->apikey); - $akl = strlen($args->apikey); - - switch($akl) { - case $userAPIkeyLen: - case $objectAPIkeyLen: - break; - - default: - throw new Exception("Aborting - Bad API Key lenght", 1); - break; - } - - if($akl == $userAPIkeyLen) { - $args->debug = 'USER-APIKEY'; - setUpEnvForRemoteAccess($dbHandler,$args->apikey,null,$opt); - $user = tlUser::getByAPIKey($dbHandler,$args->apikey); - $args->light = (count($user) == 1) ? 'green' : 'red'; - } else { - if(is_null($args->type) || trim($args->type) == '') { - throw new Exception("Aborting - Bad type", 1); - } - - if($args->type == 'exec') { - $tex = DB_TABLE_PREFIX . 'executions'; - $sql = "SELECT testplan_id FROM $tex WHERE id=" . intval($args->id); - $rs = $dbHandler->get_recordset($sql); - - if( is_null($rs) ) { - die(__FILE__ . '-' . __LINE__); - } - - $rs = $rs[0]; - $tpl = DB_TABLE_PREFIX . 'testplans'; - $sql = "SELECT api_key FROM $tpl WHERE id=" . intval($rs['testplan_id']); - $rs = $dbHandler->get_recordset($sql); - if( is_null($rs) ) { - die(__FILE__ . '-' . __LINE__); - } - $rs = $rs[0]; - $args->apikey = $rs['api_key']; - $args->envCheckMode = 'hippie'; - } - - $args->debug = 'OBJECT-APIKEY'; - $kerberos = new stdClass(); - $kerberos->args = $args; - $kerberos->method = null; - - if( setUpEnvForAnonymousAccess($dbHandler,$args->apikey,$kerberos,$opt) ) { - $args->light = 'green'; - } - } - return $args; +light) { + case 'red': + // can not find user or item + break; + + case 'green': + $reportCfg = config_get('reports_list'); + $what2launch = null; + $cfg = isset($reportCfg[$args->type]) ? $reportCfg[$args->type] : null; + + switch ($args->type) { + case 'exec': + $what2launch = "lib/execute/execPrint.php" . + "?id={$args->id}&apikey=$args->apikey"; + break; + + case 'file': + $what2launch = "lib/attachments/attachmentdownload.php" . + "?id={$args->id}&apikey=$args->apikey"; + break; + + case 'metricsdashboard': + $what2launch = "lib/results/metricsDashboard.php?apikey=$args->apikey"; + break; + + case 'test_report': + $param = "&type={$args->type}&level=testproject" . + "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . + "&header=y&summary=y&toc=y&body=y&passfail=y&cfields=y&metrics=y&author=y" . + "&requirement=y&keyword=y¬es=y&headerNumbering=y&format=" . + FORMAT_HTML; + $what2launch = "lib/results/printDocument.php?apikey=$args->apikey{$param}"; + break; + + case 'testreport_onbuild': + $param = "&type={$args->type}&level=testproject" . + "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}&build_id={$args->build_id}" . + "&header=y&summary=y&toc=y&body=y&passfail=y&cfields=y&metrics=y&author=y" . + "&requirement=y&keyword=y¬es=y&headerNumbering=y&format=" . + FORMAT_HTML; + $what2launch = "lib/results/printDocument.php?apikey=$args->apikey{$param}"; + break; + + case 'test_plan': + $param = "&type={$args->type}&level=testproject" . + "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . + "&header=y&summary=y&toc=y&body=y&passfail=n&cfields=y&metrics=y&author=y" . + "&requirement=y&keyword=y¬es=y&headerNumbering=y&format=" . + FORMAT_HTML; + $what2launch = "lib/results/printDocument.php?apikey=$args->apikey{$param}"; + break; + + case 'testspec': + $param = "&type={$args->type}&level=testproject&id={$args->tproject_id}" . + "&tproject_id={$args->tproject_id}" . + "&header=y&summary=y&toc=y&body=y&cfields=y&author=y" . + "&requirement=y&keyword=y&headerNumbering=y&format=" . + FORMAT_HTML; + $what2launch = $cfg['url'] . "?apikey=$args->apikey{$param}"; + break; + + case 'metrics_tp_general': + $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . + "&format=" . FORMAT_HTML; + $what2launch = $cfg['url'] . "?apikey=$args->apikey{$param}"; + break; + + case 'list_tc_failed': + case 'list_tc_blocked': + case 'list_tc_not_run': + $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . + "&format={$args->format}"; + $what2launch = $cfg['url'] . "&apikey=$args->apikey{$param}"; + break; + + case 'results_matrix': + $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . + "&format={$args->format}"; + $what2launch = $cfg['url'] . "?apikey=$args->apikey{$param}"; + break; + + case 'results_by_tester_per_build': + case 'charts_basic': + $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}&format=" . + FORMAT_HTML; + $what2launch = $cfg['url'] . "?apikey=$args->apikey{$param}"; + break; + + case 'abslatest_results_matrix': + case 'report_exec_timeline': + $param = "&tproject_id={$args->tproject_id}" . + "&tplan_id={$args->tplan_id}" . "&format={$args->format}"; + $what2launch = $cfg['url'] . "?apikey=$args->apikey{$param}"; + break; + + default: + $needle = 'list_tc_'; + $nl = strlen($needle); + if (strpos($key, $needle) !== false) { + $param = "&tproject_id={$args->tproject_id}&tplan_id={$args->tplan_id}" . + "&format={$args->format}"; + $what2launch = $cfg['url'] . "&apikey=$args->apikey{$param}"; + } else { + + $awl = config_get('accessWithoutLogin'); + if (! isset($awl[$args->type])) { + echo 'ABORTING - UNKNOWN TYPE:' . $args->type; + die(); + } + + $conf = $awl[$args->type]; + $param = ""; + foreach ($args->use as $prop => $useIt) { + $param .= "&$prop={$args->$prop}"; + } + $what2launch = $conf['url'] . "&apikey=$args->apikey{$param}"; + } + break; + } + + if (! is_null($what2launch)) { + // changed to be able to get XLS file using wget + // redirect(TL_BASE_HREF . $what2launch); + // echo $what2launch; + // die(); + header('Location:' . TL_BASE_HREF . $what2launch); + exit(); + } + break; + + default: + // ?? + break; +} + +/** + */ +function init_args(&$dbHandler) +{ + $_REQUEST = strings_stripSlashes($_REQUEST); + $args = new stdClass(); + + try { + // ATTENTION - give a look to $tlCfg->reports_list + // format domain: see reports.cfg.php FORMAT_* + $typeSize = 30; + $userAPIkeyLen = 32; + $objectAPIkeyLen = 64; + + $iParams = array( + "apikey" => array( + tlInputParameter::STRING_N, + $userAPIkeyLen, + $objectAPIkeyLen + ), + "tproject_id" => array( + tlInputParameter::INT_N + ), + "tplan_id" => array( + tlInputParameter::INT_N + ), + "build_id" => array( + tlInputParameter::INT_N + ), + "level" => array( + tlInputParameter::STRING_N, + 0, + 16 + ), + "type" => array( + tlInputParameter::STRING_N, + 0, + $typeSize + ), + 'id' => array( + tlInputParameter::INT_N + ), + 'format' => array( + tlInputParameter::STRING_N, + 0, + 1 + ), + 'entities' => array( + tlInputParameter::STRING_N, + 0, + 3 + ) + ); + } catch (Exception $e) { + echo $e->getMessage(); + exit(); + } + + R_PARAMS($iParams, $args); + + $args->format = intval($args->format); + $args->format = ($args->format <= 0) ? FORMAT_HTML : $args->format; + + $args->envCheckMode = $args->type == 'file' ? 'hippie' : 'paranoic'; + $args->light = 'red'; + $opt = array( + 'setPaths' => true, + 'clearSession' => true + ); + + // what to use when is custom + $masks = array( + 'tproject_id' => 1, + 'tplan_id' => 2, + 'build_id' => 4 + ); + $args->use = $masks; + foreach ($masks as $kx => $mm) { + $args->use[$kx] = (($args->entities & $mm) > 0); + } + + // validate apikey to avoid SQL injection + $args->apikey = trim($args->apikey); + $akl = strlen($args->apikey); + + switch ($akl) { + case $userAPIkeyLen: + case $objectAPIkeyLen: + break; + + default: + throw new Exception("Aborting - Bad API Key lenght", 1); + break; + } + + if ($akl == $userAPIkeyLen) { + $args->debug = 'USER-APIKEY'; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, null, $opt); + $user = tlUser::getByAPIKey($dbHandler, $args->apikey); + $args->light = (count($user) == 1) ? 'green' : 'red'; + } else { + if (is_null($args->type) || trim($args->type) == '') { + throw new Exception("Aborting - Bad type", 1); + } + + if ($args->type == 'exec') { + $tex = DB_TABLE_PREFIX . 'executions'; + $sql = "SELECT testplan_id FROM $tex WHERE id=" . intval($args->id); + $rs = $dbHandler->get_recordset($sql); + + if (is_null($rs)) { + die(__FILE__ . '-' . __LINE__); + } + + $rs = $rs[0]; + $tpl = DB_TABLE_PREFIX . 'testplans'; + $sql = "SELECT api_key FROM $tpl WHERE id=" . + intval($rs['testplan_id']); + $rs = $dbHandler->get_recordset($sql); + if (is_null($rs)) { + die(__FILE__ . '-' . __LINE__); + } + $rs = $rs[0]; + $args->apikey = $rs['api_key']; + $args->envCheckMode = 'hippie'; + } + + $args->debug = 'OBJECT-APIKEY'; + $kerberos = new stdClass(); + $kerberos->args = $args; + $kerberos->method = null; + + if (setUpEnvForAnonymousAccess($dbHandler, $args->apikey, $kerberos, + $opt)) { + $args->light = 'green'; + } + } + return $args; } diff --git a/locale/cs_CZ/description.php b/locale/cs_CZ/description.php index 6ca232640e..d8b7715f70 100644 --- a/locale/cs_CZ/description.php +++ b/locale/cs_CZ/description.php @@ -1,56 +1,56 @@ -Možnosti vytvoÅ™ení dokumentu

    Tato tabulka umožnuje uživatelům zvolit testovací případy, které mají být zobrazeny. Pouze zvolené testovací sady budou zobrazeny dle požadovaného obsahu. Pokud budete chtít zmÄ›nit obsah nebo zvolit jinou -testovací sadu, proveÄte zmÄ›nu obsahu Äásti navigace, popřípadÄ› zvolte ze stromové struktury jinou +testovací sadu, proveÄte zmÄ›nu obsahu Äásti navigace, popřípadÄ› zvolte ze stromové struktury jinou testovací sadu.

    -

    HlaviÄka dokumentu: Uživatelé mohou zvolit, které informace budou zobrazeny v hlaviÄce dokumentu. -Informace v hlaviÄce dokumentu mohou obsahovat: Úvod, Obsah, Odkazy, +

    HlaviÄka dokumentu: Uživatelé mohou zvolit, které informace budou zobrazeny v hlaviÄce dokumentu. +Informace v hlaviÄce dokumentu mohou obsahovat: Úvod, Obsah, Odkazy, Metodologii testování a omezení testů.

    Detaily testovacího případu: Uživatelé mohou zvolit, které informace budou zobrazeny v popisu testovacích případů. Informace v popisu testovacích případů mohou obsahovat: pÅ™ehled o testovacím případu, kroky testovacího skriptu, oÄekávané výsledky a klíÄová slova.

    StruÄný obsah testovacího případu: Uživatelé nemohou vypnout zobrazení shrnutí testovacího případu, pokud se rozhodli zobrazit jeho detail, -jelikož je standartnÄ› jeho souÄástí. Pokud nebude vybrán k zobrazení detail testovacího případu, bude umožnÄ›no zobrazení pouze jeho shrnutí v případné +jelikož je standartnÄ› jeho souÄástí. Pokud nebude vybrán k zobrazení detail testovacího případu, bude umožnÄ›no zobrazení pouze jeho shrnutí v případné kombinaci s dalšími možnostmi.

    Možnosti rejstříku obsahu: Pokud je zvoleno uživatelem, TestLink vloží do rejstřiku obsahu seznam všech názvů testovacích připadů s interním hypertextovým odkazem.

    -

    Výstupní formát: Můžete zvolit ze dvou možností: HTML nebo MS Word. V druhém případÄ› Váš prohlížeÄ spustí komponentu MS wordu.

    "; - -// testPlan.html +

    Výstupní formát: Můžete zvolit ze dvou možností: HTML nebo MS Word. V druhém případÄ› Váš prohlížeÄ spustí komponentu MS wordu.

    "; + +// testPlan.html $TLS_hlp_testPlan = "

    Testovací Plán

    Obecný popis

    @@ -58,20 +58,20 @@ do přísluÅ¡ných sestavení produktu v daném Äase a sledování jeho výsledků.

    Provádění testů

    -

    Tato sekce umožňuje uživatelům provádět testovací případy (zapsat jejich výsledek) a +

    Tato sekce umožňuje uživatelům provádět testovací případy (zapsat jejich výsledek) a případně vytisknout si testovací sadu z aktuálního testovacího plánu. V této sekci mohou uživatelé také sledovat výsledky -prováděných testovacích případů.

    +prováděných testovacích případů.

    Správa testovacích plánů

    -

    Tato sekce, dostupná pouze uživatelům v roli 'Vedoucí', umožňuje spravovat testovací plány. +

    Tato sekce, dostupná pouze uživatelům v roli 'Vedoucí', umožňuje spravovat testovací plány. Správa testovacích plánů zahrnuje jejich vytváření, mazání, nebo úpravu. Dále přidávání, mazání, nebo úpravu testovacích připadů v rámci testovacího plánu, vytváření sestavení, nebo nastavení oprávnění přístupu k plánu.
    -Uživatelé v roli 'Vedoucí' mohou také nastavovat prioritu/rizika a vlastnictví pro sady testovacích případů nebo vytvářet testovací milníky.

    - -

    UpozornÄ›ní: Je možné, že uživatelé nemusí mít dostupný seznam testovacích plánů. -V tomto případÄ› budou odkazy v úvodním oknÄ› nefunkÄní (neplatí pro uživatele s oprávnÄ›ním 'Vedoucí' nebo vyšší). Pokud se -dostanete do takovéto situace, kontaktujte uživatele s rolí 'Vedoucí' popřípadÄ› 'Administrátor', který vám nastaví oprávnÄ›ní, nebo pro Vás vytvoří testovací plán.

    "; +Uživatelé v roli 'Vedoucí' mohou také nastavovat prioritu/rizika a vlastnictví pro sady testovacích případů nebo vytvářet testovací milníky.

    -// custom_fields.html +

    UpozornÄ›ní: Je možné, že uživatelé nemusí mít dostupný seznam testovacích plánů. +V tomto případÄ› budou odkazy v úvodním oknÄ› nefunkÄní (neplatí pro uživatele s oprávnÄ›ním 'Vedoucí' nebo vyšší). Pokud se +dostanete do takovéto situace, kontaktujte uživatele s rolí 'Vedoucí' popřípadÄ› 'Administrátor', který vám nastaví oprávnÄ›ní, nebo pro Vás vytvoří testovací plán.

    "; + +// custom_fields.html $TLS_hlp_customFields = "

    Uživatelská pole

    Fakta o možnostech využití uživatelských polí:

      @@ -88,7 +88,7 @@
    • Jméno uživatelského pole
    • Název popisku promÄ›nné (například: Tato hodnota je pÅ™edána funkci lang_get() API , nebo zobrazena v případÄ›, že pro ni neexistuje pÅ™eklad).
    • Typ uživatelského pole (Å™etÄ›zec, celé Äíslo, desetiné Äíslo, výÄet, email)
    • -
    • VýÄet možných hodnot (například: ÄŒERVENÃ|ŽLUTÃ|MODRÃ), vztahuje se k seznamu s možností vícenásobného výbÄ›ru, +
    • VýÄet možných hodnot (například: ÄŒERVENÃ|ŽLUTÃ|MODRÃ), vztahuje se k seznamu s možností vícenásobného výbÄ›ru, nebo rozbalovacímu seznamu.
      Použijte oddÄ›lovací znak ('|') pro oddÄ›lení položek ve výÄtu možností. Prázdný Å™etÄ›zec může být použit jako jedna z položek ve výÄtu.
    • @@ -106,14 +106,14 @@
    • Povolí uživatelské pole pro zobrazení a editaci v rámci návrhu testovaciho plánu.. Uživatel může mÄ›nit hodnotu uživatelského pole pÅ™i návrhu testovacího plánu. (pÅ™iÅ™azení testovacích případů do testovacího plánu)
    • Dostupné pro: Uživatel zvolí k jaké komponentÄ› Testlinku se pole vztahuje.
    -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

    Provádění testovacích případů

    Umožňuje uživatelům 'provádÄ›t' testovací případy. Samotné provedení testovacího pÅ™ipadu v ramci aplikace Testlink spoÄívá v nastavení jeho stavu (úspěšný, neúspěšný, ...).

    -

    Aplikace Testlink umožňuje přístup do nástrojů pro správu chyb (závisí na konfiguraci). Uživatelé pak mohou k testovacím případům přiřadit chyby a sledovat jejich stav.

    "; - -//bug_add.html +

    Aplikace Testlink umožňuje přístup do nástrojů pro správu chyb (závisí na konfiguraci). Uživatelé pak mohou k testovacím případům přiřadit chyby a sledovat jejich stav.

    "; + +// bug_add.html $TLS_hlp_btsIntegration = "

    Přiřazení chyby k testovacímu případu

    (pouze v případě, že je nastaveno propojení s nástrojem pro správu chyb) TestLink poskytuje jednoduchou integraci s nástroji pro správu chyb (BTS). Neumožňuje však vložit chybu v rámci aplikace Testlink do BTS ani automatické předávaní ID chyby mezi BTS a Testlink. @@ -122,7 +122,7 @@

  • Vložit novou chybu.
  • Zobrazit informace o již existující chybÄ›.
  • -

    +

    Postup pro vložení chyby

    @@ -131,95 +131,92 @@

  • Krok 2: poznamenejte si ID chyby v rámci BTS.
  • Krok 3: napiÅ¡te ID chyby do vstupního pole v Testlinku.
  • Krok 4: použijte tlaÄítko PÅ™idat chybu.
  • - + Po uzavÅ™ení stránky 'PÅ™idat chybu' uvidíte informace o chybÄ› navázané na testovací případ, pro který byla chyba pÅ™idána. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Nastavení sestavení a filtrů pro zobrazení výsledků provedení testů

    -

    Levý ovládací panel se skládá ze seznamu testovacích případů navázaných na aktuální " . -"testovací plán a tabulku s možnostmi nastavení a filtrování. Tyto filtry umožňují uživateli " . -"omezit nabízenou sadu testovacích případů pÅ™ed tím, než budou provedeny." . -"Nastavte filtr, stisknÄ›te tlaÄítko \"Použít\" a zvolte přísluÅ¡ný testovací případ " . -"z menu.

    +

    Levý ovládací panel se skládá ze seznamu testovacích případů navázaných na aktuální " . + "testovací plán a tabulku s možnostmi nastavení a filtrování. Tyto filtry umožňují uživateli " . + "omezit nabízenou sadu testovacích případů pÅ™ed tím, než budou provedeny." . + "Nastavte filtr, stisknÄ›te tlaÄítko \"Použít\" a zvolte přísluÅ¡ný testovací případ " . + "z menu.

    Sestavení

    -

    Uživatelé musí zvolit, pro které sestavení se jim budou výsledky zobrazovat. " . -"Sestavení jsou základní komponentou pro aktuální testovací plán. Každý testovací případ " . -"může být v rámci jednoho sestavení proveden vícekrát. Pro filtrování ale bude použit pouze poslední výsledek. +

    Uživatelé musí zvolit, pro které sestavení se jim budou výsledky zobrazovat. " . + "Sestavení jsou základní komponentou pro aktuální testovací plán. Každý testovací případ " . + "může být v rámci jednoho sestavení proveden vícekrát. Pro filtrování ale bude použit pouze poslední výsledek.
    Sestavení mohou být vytvořena uživateli s rolí 'Vedoucí' nebo vyšší na stránce Správa sestavení.

    Filtrování dle ID testovacího případu

    -

    Uživatelé mohou filtrovat testovací případy podle jejich unikátního oznaÄení (ID). Toto ID je automaticky generováno pÅ™i vytváření testovacího případu. Pokud bude vstupní pole u filtru ID prázdné, nebude tento filtr aplikován.

    +

    Uživatelé mohou filtrovat testovací případy podle jejich unikátního oznaÄení (ID). Toto ID je automaticky generováno pÅ™i vytváření testovacího případu. Pokud bude vstupní pole u filtru ID prázdné, nebude tento filtr aplikován.

    Filtrování dle priority

    -

    Uživatelé mohou filtrovat testovací případy dle jejich priority. Důležitost každého testovacího případu ve zvoleném testovacím plánu je kombinována s jeho urgentností. Například testovací případ s prioritou 'Vysoká' " . -"je zobrazen v případÄ›, že důležitost je nastavena na úroveň VYSOKà a urgentnost je nastavena na úroveň STŘEDNÃ, popřípadÄ› obrácenÄ›.

    +

    Uživatelé mohou filtrovat testovací případy dle jejich priority. Důležitost každého testovacího případu ve zvoleném testovacím plánu je kombinována s jeho urgentností. Například testovací případ s prioritou 'Vysoká' " . + "je zobrazen v případÄ›, že důležitost je nastavena na úroveň VYSOKà a urgentnost je nastavena na úroveň STŘEDNÃ, popřípadÄ› obrácenÄ›.

    Filtrování dle výsledků

    -

    Uživatelé mohou filtrovat testovací případy dle jejich výsledků. Výsledkem se myslí stav testovacího případu ve zvoleném sestavení. Testovací případ může být například ve stavu: úspěšný, neúspěšný, blokován, nebo neproveden." . -"Tento filtr je standartně vypnut.

    +

    Uživatelé mohou filtrovat testovací případy dle jejich výsledků. Výsledkem se myslí stav testovacího případu ve zvoleném sestavení. Testovací případ může být například ve stavu: úspěšný, neúspěšný, blokován, nebo neproveden." . + "Tento filtr je standartně vypnut.

    Uživatelské filtry

    -

    Uživatelé mohou filtrovat testovací případy dle jim přiřazeného uživatele. Do výsledků je také možné zahrnout " . -"\"nepřiřazené\" testovací případy zaškrtnutím příslušného pole.

    "; -/* -

    Most Current Result

    -

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted -by the build that is chosen from the dropdown box. In this state the tree will display -the test cases status. -
    Example: User selects build 2 from the dropdown box and doesn't check the 'most -current' checkbox. All test cases will be shown with their status from build 2. -So, if test case 1 passed in build 2 it will be colored green. -
    If the user decideds to check the 'most current' checkbox the tree will be -colored by the test cases most recent result. -
    Ex: User selects build 2 from the dropdown box and this time checks -the 'most current' checkbox. All test cases will be shown with most current -status. So, if test case 1 passed in build 3, even though the user has also selected -build 2, it will be colored green.

    - */ - - -// newest_tcversions.html +

    Uživatelé mohou filtrovat testovací případy dle jim přiřazeného uživatele. Do výsledků je také možné zahrnout " . + "\"nepřiřazené\" testovací případy zaškrtnutím příslušného pole.

    "; +/* + *

    Most Current Result

    + *

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted + * by the build that is chosen from the dropdown box. In this state the tree will display + * the test cases status. + *
    Example: User selects build 2 from the dropdown box and doesn't check the 'most + * current' checkbox. All test cases will be shown with their status from build 2. + * So, if test case 1 passed in build 2 it will be colored green. + *
    If the user decideds to check the 'most current' checkbox the tree will be + * colored by the test cases most recent result. + *
    Ex: User selects build 2 from the dropdown box and this time checks + * the 'most current' checkbox. All test cases will be shown with most current + * status. So, if test case 1 passed in build 3, even though the user has also selected + * build 2, it will be colored green.

    + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Nejnovější verze testovacích případů v aktuálním testovacím plánu

    -

    Všechny testovací případy navázané do testovacího plánu budou analyzovány a zobrazí se seznam testovacích případů, které jsou dostupné v novější verzi.

    "; - - -// requirementsCoverage.html +

    Všechny testovací případy navázané do testovacího plánu budou analyzovány a zobrazí se seznam testovacích případů, které jsou dostupné v novější verzi.

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Pokrytí požadavků


    Tato funkÄnost umožnuje mapovat pokrytí testovacích případů uživateli nebo specifikacemi požadavků. K této funkÄnosti se dostanete odkazem \"Specifikace požadavků\" na úvodní obrazovce.

    Specifikace požadavků

    -

    Požadavky jsou uspořádány v rámci dokumentu specifikace požadavků, který je definována v rámci +

    Požadavky jsou uspořádány v rámci dokumentu specifikace požadavků, který je definována v rámci testovacího projektu.
    TestLink nepodporuje verzování dokumentu specifikace požadavků ani samotných požadavků. Verze dokumentu nebo požadavků by tak měla být zaznamenána v jejich názvu. -Uživatel může také připadně přidat popis nebo poznámku v rámci jejich b>obsahu
    .

    +Uživatel může také připadně přidat popis nebo poznámku v rámci jejich b>obsahu
    .

    -

    PoÄet importovaných požadavků slouží pro vyhodnocení pokrytí požadavků v případÄ›, že nÄ›které z požadavků nebyly pÅ™idány (importovány). -Hodnota 0 vyjadÅ™uje aktuální poÄet požadavků zahrnutých do metrik.

    +

    PoÄet importovaných požadavků slouží pro vyhodnocení pokrytí požadavků v případÄ›, že nÄ›které z požadavků nebyly pÅ™idány (importovány). +Hodnota 0 vyjadÅ™uje aktuální poÄet požadavků zahrnutých do metrik.

    Například SRS obsahuje 200 požadavků, ale pouze 50 je jich přidano do testlinku. Pokrytí testy je pak z 25% (za předpokladu, že budou všechny přidané požadavky zahrnuty do testování).

    Požadavky

    KliknÄ›te na jméno vytvoÅ™eného dokumentu specifikace požadavků. Pro dokument pak můžete vytvářet, upravovat, mazat, nebo importovat požadavky. Každý požadavek má jméno, specifikaci a stav. -Stav může být \"Normální\" or \"Netestovatelné\". Netestovatelné požadavky nejsou zahrnuty do výpoÄtu metrik. Tento parametr může být použit pro neimplementované prvky i pro Å¡patnÄ› definované požadavky.

    +Stav může být \"Normální\" or \"Netestovatelné\". Netestovatelné požadavky nejsou zahrnuty do výpoÄtu metrik. Tento parametr může být použit pro neimplementované prvky i pro Å¡patnÄ› definované požadavky.

    -

    Můžete vytvořit nové testovací případy přímo na stránce specifikace požadavků pro vybrané požadavky použitím automatické funkce. Tyto testovací případy budou vytvořeny v testovací sadě, jejíž jméno je definováno konfigurací Testlinku (standartní nastavení je: $tlCfg->req_cfg->default_testsuite_name = +

    Můžete vytvořit nové testovací případy přímo na stránce specifikace požadavků pro vybrané požadavky použitím automatické funkce. Tyto testovací případy budou vytvořeny v testovací sadě, jejíž jméno je definováno konfigurací Testlinku (standartní nastavení je: $tlCfg->req_cfg->default_testsuite_name = 'Test suite created by Requirement - Auto';). Titulek a obsah jsou zkopírovány do těchto testovacích případů.

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Možnost 'Uložit uživatelská pole'

    Pokud nadefinujete a přiřadite uživatelská pole do testovacího projektu s nastavením:
    'Zobrazit v testovacím plánu=ano' a
    'Povolit v testovacím plánu=ano'
    budou tyto pole dostupné POUZE pro testovací případy navázané do testovacího plánu. -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/cs_CZ/texts.php b/locale/cs_CZ/texts.php index ebefa23ae9..191dda8637 100644 --- a/locale/cs_CZ/texts.php +++ b/locale/cs_CZ/texts.php @@ -1,92 +1,90 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * ------------------------------------------------------------------------------------ */ - -//If this file is coded in UTF-8 then enable the following line -$TLS_STRINGFILE_CHARSET = "UTF-8"; - -$TLS_htmltext_title['assignReqs'] = "Přiřazení požadavků k testovacím případům"; -$TLS_htmltext['assignReqs'] = "

    ÚÄel

    -

    Uživatel může nastavit pÅ™iÅ™azení mezi požadavky a testovacími případy. PÅ™iÅ™azení může být definováno jako vazba 0..n až 0..n. tzn. Jeden požadavek může být pÅ™iÅ™azen k žádnému, jednomu, popřípadÄ› k více testovacím případům a naopak. Tato pÅ™iÅ™azení pomáhají zjistit pokrytí požadavků testovacími případy -a k urÄení testovacích případů, jejichž provádÄ›ní nebylo dokonÄeno z důvodu chyby. +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * ------------------------------------------------------------------------------------ */ + +// If this file is coded in UTF-8 then enable the following line +$TLS_STRINGFILE_CHARSET = "UTF-8"; + +$TLS_htmltext_title['assignReqs'] = "PÅ™iÅ™azení požadavků k testovacím případům"; +$TLS_htmltext['assignReqs'] = "

    ÚÄel

    +

    Uživatel může nastavit pÅ™iÅ™azení mezi požadavky a testovacími případy. PÅ™iÅ™azení může být definováno jako vazba 0..n až 0..n. tzn. Jeden požadavek může být pÅ™iÅ™azen k žádnému, jednomu, popřípadÄ› k více testovacím případům a naopak. Tato pÅ™iÅ™azení pomáhají zjistit pokrytí požadavků testovacími případy +a k urÄení testovacích případů, jejichž provádÄ›ní nebylo dokonÄeno z důvodu chyby. Tyto informace pak mohou sloužit jako podklad pro další plánování testů.

    Jak na to

      -
    1. Vyberte testovací případ ze stromové struktury v navigaÄním panelu na levé stranÄ›. Rozbalovací seznam s dokumenty specifikace +
    2. Vyberte testovací případ ze stromové struktury v navigaÄním panelu na levé stranÄ›. Rozbalovací seznam s dokumenty specifikace požadavků bude zobrazen v horní Äasti pracovní plochy
    3. -
    4. V případě, že je dostupno více dokumentů se specifikacemi požadavků, zvolte jeden z nich. +
    5. V případě, že je dostupno více dokumentů se specifikacemi požadavků, zvolte jeden z nich. TestLink automaticky obnoví obsah stránky.
    6. Ve stÅ™ední Äásti pracovní plochy se zobrazí vÅ¡echny požadavky (ze zvoleného dokumentu se specifikacemi požadavků), které jsou již k testovacímu případu pÅ™iÅ™azeny. Ve spodní Äásti 'Dostupné požadavky' se zobrazí vÅ¡echny - požadavky, které k testovacímu případu - pÅ™iÅ™azeny nejsou. Uživatel může oznaÄit požadavky, které se vztahují ke zvolenému testovacímu případu + požadavky, které k testovacímu případu + pÅ™iÅ™azeny nejsou. Uživatel může oznaÄit požadavky, které se vztahují ke zvolenému testovacímu případu a stisknout tlaÄítko 'PÅ™iÅ™adit'. OznaÄené požadavky se poté zobrazí ve stÅ™ední Äasti pracovní plochy 'PÅ™iÅ™azené požadavky'.
    7. -
    "; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Specifikace testů"; -$TLS_htmltext['editTc'] = "

    ÚÄel

    -

    Specifikace testovacích případů umožňuje uživatelům zobrazení a úpravu všech existujících " . - "testovacích sad a testovacích případů. Testovací případy jsou verzovány a všechny " . - "předcházející verze jsou zde dostupné. Je možné je zobrazit a dále s nimi pracovat.

    +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Specifikace testů"; +$TLS_htmltext['editTc'] = "

    ÚÄel

    +

    Specifikace testovacích případů umožňuje uživatelům zobrazení a úpravu všech existujících " . + "testovacích sad a testovacích případů. Testovací případy jsou verzovány a všechny " . + "předcházející verze jsou zde dostupné. Je možné je zobrazit a dále s nimi pracovat.

    Jak na to

      -
    1. Zvolte testovací projekt ze stromové struktury v navigaÄním panelu na levé stranÄ› (hlavní uzel stromové struktury). Poznámka: " . - "ZmÄ›nu aktivního testovacího projektu můžete kdykoliv provést pomocí rozbalovacího " . - "seznamu v pravém horním rohu.
    2. -
    3. Kliknutím na tlaÄítko Nová navázaná testovací sada vytvoÅ™te novou testovací sadu. Testovací sady Vám pomohou" . - "vytvoÅ™it strukturu vaÅ¡ich testovacích dokumentů dle VaÅ¡ich zvyklostí (funkÄní/nefunkÄní " . - "testy, komponenty produktu nebo funkÄnosti, zmÄ›nové požadavky, apod.). Popis " . - "testovací sady by mÄ›l obsahovat popis obsahu vložených testovacích případů, základní nastavení, " . - "odkazy na související dokumentaci, popis omezení popřípadÄ› dalších užiteÄných informací v rámci testovací sady. ObecnÄ› definováno, " . - "vÅ¡echny poznámky, které se vztahují k testovacím případům pÅ™iÅ™azených do popisovaných testovacích sad. Testovací sady vycházejí z " . - "filozofie adresářů. V rámci aktuálního testovacího projektu tak mohou mezi nimi uživatelé pÅ™esunovat nebo kopírovat testovací případy, popřípadÄ› samotné testovací sady. " . - ". Testovací sady mohou být také importovány, nebo exportovány (vÄetnÄ› testovacích případů, které obsahují).
    4. " . - "
    5. V panelu navigace zvolte ve stromové struktuÅ™e vytvoÅ™enou testovací sadu a stisknutím " . - "tlaÄítka VytvoÅ™it testovací případ vytvoÅ™te nový testovací případ. Testovací případ popisuje " . - "scénář testu, oÄekávané výsledky a obsah uživatelsky definovaných polí " . - " (další informace jsou dostupné v uživatelském manuálu). Pro zvýšení pÅ™ehledu " . - "je možné pÅ™iÅ™adit k testovacímu případu klíÄová slova.
    6. -
    7. Testovací případy můžete procházet a editovat za pomoci stromové struktury navigaÄního panelu na levé stranÄ› obrazovky. Pro každý testovací případ +
    8. Zvolte testovací projekt ze stromové struktury v navigaÄním panelu na levé stranÄ› (hlavní uzel stromové struktury). Poznámka: " . + "ZmÄ›nu aktivního testovacího projektu můžete kdykoliv provést pomocí rozbalovacího " . + "seznamu v pravém horním rohu.
    9. +
    10. Kliknutím na tlaÄítko Nová navázaná testovací sada vytvoÅ™te novou testovací sadu. Testovací sady Vám pomohou" . + "vytvoÅ™it strukturu vaÅ¡ich testovacích dokumentů dle VaÅ¡ich zvyklostí (funkÄní/nefunkÄní " . + "testy, komponenty produktu nebo funkÄnosti, zmÄ›nové požadavky, apod.). Popis " . + "testovací sady by mÄ›l obsahovat popis obsahu vložených testovacích případů, základní nastavení, " . + "odkazy na související dokumentaci, popis omezení popřípadÄ› dalších užiteÄných informací v rámci testovací sady. ObecnÄ› definováno, " . + "vÅ¡echny poznámky, které se vztahují k testovacím případům pÅ™iÅ™azených do popisovaných testovacích sad. Testovací sady vycházejí z " . + "filozofie adresářů. V rámci aktuálního testovacího projektu tak mohou mezi nimi uživatelé pÅ™esunovat nebo kopírovat testovací případy, popřípadÄ› samotné testovací sady. " . + ". Testovací sady mohou být také importovány, nebo exportovány (vÄetnÄ› testovacích případů, které obsahují).
    11. " . + "
    12. V panelu navigace zvolte ve stromové struktuÅ™e vytvoÅ™enou testovací sadu a stisknutím " . + "tlaÄítka VytvoÅ™it testovací případ vytvoÅ™te nový testovací případ. Testovací případ popisuje " . + "scénář testu, oÄekávané výsledky a obsah uživatelsky definovaných polí " . + " (další informace jsou dostupné v uživatelském manuálu). Pro zvýšení pÅ™ehledu " . + "je možné pÅ™iÅ™adit k testovacímu případu klíÄová slova.
    13. +
    14. Testovací případy můžete procházet a editovat za pomoci stromové struktury navigaÄního panelu na levé stranÄ› obrazovky. Pro každý testovací případ se ukládá jeho historie.
    15. Po dokonÄení definice testovacích případů, pÅ™iÅ™aÄte testovací sadu do požadovaného testovacího plánu.
    16. + \"javascript:open_help_window('glossary','$locale');\">testovacího plánu.
    -

    V Testlinku jsou testovací případy organizovány za pomoci testovacích sad." . -"Testovací sady mohou být vnoÅ™ené v jiných testovacích sadách a je tak umožnÄ›no vytvářet si strukturu testovacích sad. - Informace o struktuÅ™e testovacich sad mohou být vytiÅ¡tÄ›ny spoleÄnÄ› s testovacími případy.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Vyhledávání testovacích případů"; -$TLS_htmltext['searchTc'] = "

    ÚÄel

    +

    V Testlinku jsou testovací případy organizovány za pomoci testovacích sad." . + "Testovací sady mohou být vnoÅ™ené v jiných testovacích sadách a je tak umožnÄ›no vytvářet si strukturu testovacích sad. + Informace o struktuÅ™e testovacich sad mohou být vytiÅ¡tÄ›ny spoleÄnÄ› s testovacími případy.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Vyhledávání testovacích případů"; +$TLS_htmltext['searchTc'] = "

    ÚÄel

    Vyhledání testovacích případů v závislosti na klíÄových slovech a/nebo zadaných textových Å™etÄ›zců. Vyhledávání nerozliÅ¡uje velká/malá písmena. Výsledky vyhledávání budou obsahovat pouze testovací případy z aktuálního testovacího projektu.

    @@ -97,12 +95,11 @@
  • Zvolte požadované klíÄové slovo, popřípadÄ› hodnotu 'Nepoužito', pokud nechcete podle klíÄového slova vyhledávat.
  • StisknÄ›te tlaÄítko 'Vyhledat'.
  • VÅ¡echny testovací případy, které odpovídají parametrům vyhledávání budou zobrazeny. Testovací případy mohou být následnÄ› editovány, pokud využijete odkazu v názvu testovacího případu.
  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "VytiÅ¡tÄ›ní specifikace testů"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    ÚÄel

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Vytištění specifikace testů"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    ÚÄel

    Uživatel zde může vytisknout jednotlivé testovací případy, všechny testovací případy v testovací sadě, nebo všechny testovací případy v projektu.

    Jak na to

      @@ -114,12 +111,11 @@
    1. Pro vytisknutí zobrazených informací využijte možnosti tisku vaÅ¡eho prohlížeÄe.
      Upozornění: Ujistěte se, že jste pro tisk zvolili pravý rámec HTML stránky.

    2. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Návrh specifikace požadavků"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    V této sekci můžete spravovat dokumenty specifikací požadavků.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Návrh specifikace požadavků"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    V této sekci můžete spravovat dokumenty specifikací požadavků.

    Specifikace požadavků

    @@ -134,18 +130,17 @@

    Požadavky

    -

    KliknÄ›te na jméno dokumentu specifikace požadavků. Pokud zatím nebyl dokument specifikace vytvoÅ™en, kliknÄ›te na projekt ve stromové struktuÅ™e navigaÄního panelu, +

    KliknÄ›te na jméno dokumentu specifikace požadavků. Pokud zatím nebyl dokument specifikace vytvoÅ™en, kliknÄ›te na projekt ve stromové struktuÅ™e navigaÄního panelu, a dokument specifikace požadavků vytvoÅ™te. Pro dokument pak můžete vytvářet, upravovat, mazat, nebo importovat požadavky. Každý požadavek má jméno, specifikaci a stav. Stav může být nastaven na \"Normalní\" nebo \"Netestovatelné\". Netestovatelné požadavky nejsou zahrnuty do výpoÄtu metrik. Tento parametr může být použit pro neimplementované prvky i pro Å¡patnÄ› specifikované požadavky.

    -

    Můžete vytvořit nové testovací případy přímo na stránce specifikace požadavků pro vybrané požadavky použitím automatické funkce. Testovací případy budou vytvořeny v testovací sadě, jejíž jméno je definováno konfigurací Testlinku (standartní nastavení je: $tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). Titulek a obsah jsou zkopírovány do těchto testovacích případů.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "PÅ™iÅ™azení klíÄových slov"; -$TLS_htmltext['keywordsAssign'] = "

    ÚÄel

    -

    Na stránce pÅ™iÅ™azení klíÄových slov si mohou uživatelé dávkovÄ› pÅ™iÅ™adit k existující +

    Můžete vytvořit nové testovací případy přímo na stránce specifikace požadavků pro vybrané požadavky použitím automatické funkce. Testovací případy budou vytvořeny v testovací sadě, jejíž jméno je definováno konfigurací Testlinku (standartní nastavení je: $tlCfg->req_cfg->default_testsuite_name = +'Test suite created by Requirement - Auto';). Titulek a obsah jsou zkopírovány do těchto testovacích případů.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "PÅ™iÅ™azení klíÄových slov"; +$TLS_htmltext['keywordsAssign'] = "

    ÚÄel

    +

    Na stránce pÅ™iÅ™azení klíÄových slov si mohou uživatelé dávkovÄ› pÅ™iÅ™adit k existující testovací sadÄ› nebo testovacímu případu klíÄové slova

    Jak na to

    @@ -158,19 +153,18 @@

    Důležité informace ohlednÄ› pÅ™iÅ™azení kliÄových slov k testovacímu plánu

    PÅ™iÅ™azená klíÄová slova k definici testovacích případů se projeví v testovacích plánech pouze a jen v případÄ›, že testovací plán obsahuje poslední verzi testovacích případů. Pokud testovací plán obsahuje starší verze testovacích případů, pÅ™iÅ™azení klíÄových slov se v nÄ›m NEPROJEVÃ.

    -

    TestLink používá tento postup k tomu, aby starší verze testovacích případů v testovacím plánu nebyly dotÄeny pÅ™iÅ™azením, které provedete nad -jejich novÄ›jšími verzemi. Pokud budete chtít provést aktualizaci testovacích případů ve vaÅ¡em testovacím plánu, -proveÄte PŘED pÅ™iÅ™azením klíÄových slov nejdříve kontrolu testovacích případů za -pomoci funkce 'Aktualizovat upravené testovací případy'.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Provedení testovacího případu"; -$TLS_htmltext['executeTest'] = "

    ÚÄel

    - -

    Umožňuje uživatelům provádět testovací případy. Uživatel může v rámci aktuálního sestavení nastavit -výsledek testu. Pro více informací o nastavení a možnostech filtrovaní se podívejte do nápovědy " . - "(klikněte na ikonu otazníku).

    +

    TestLink používá tento postup k tomu, aby starší verze testovacích případů v testovacím plánu nebyly dotÄeny pÅ™iÅ™azením, které provedete nad +jejich novÄ›jšími verzemi. Pokud budete chtít provést aktualizaci testovacích případů ve vaÅ¡em testovacím plánu, +proveÄte PŘED pÅ™iÅ™azením klíÄových slov nejdříve kontrolu testovacích případů za +pomoci funkce 'Aktualizovat upravené testovací případy'.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Provedení testovacího případu"; +$TLS_htmltext['executeTest'] = "

    ÚÄel

    + +

    Umožňuje uživatelům provádět testovací případy. Uživatel může v rámci aktuálního sestavení nastavit +výsledek testu. Pro více informací o nastavení a možnostech filtrovaní se podívejte do nápovědy " . + "(klikněte na ikonu otazníku).

    Jak na to

    @@ -181,13 +175,13 @@
  • Vyplňte výsledky pro testovací případ spoleÄnÄ› s jakýmikoliv poznámkami, nebo nalezenými chybami.
  • Uložte výsledky.
  • -

    Poznámka: TestLink musí být nastaven na spolupráci s vaším nástrojem pro správu chyb, -pokud chcete vytvořit nebo sledovat stav chyby přímo z prostředí Testlinku.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Popis testovacích přehledů a metrik"; -$TLS_htmltext['showMetrics'] = "

    PÅ™ehledy se vztahují k testovacímu projektu " . - "(nastavenému v horní Äásti navigaÄního panelu). Testovací projekt nastavený pro generování pÅ™ehledů se může liÅ¡it +

    Poznámka: TestLink musí být nastaven na spolupráci s vaším nástrojem pro správu chyb, +pokud chcete vytvořit nebo sledovat stav chyby přímo z prostředí Testlinku.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Popis testovacích přehledů a metrik"; +$TLS_htmltext['showMetrics'] = "

    PÅ™ehledy se vztahují k testovacímu projektu " . + "(nastavenému v horní Äásti navigaÄního panelu). Testovací projekt nastavený pro generování pÅ™ehledů se může liÅ¡it od aktuálnÄ› zvoleného testovacího plánu pro provádÄ›ní testů. Pro pÅ™ehled můžete zvolit formát ve které bude vygenerován:

    • Normal - pÅ™ehled je zobrazen v prohlížeÄi
    • @@ -206,11 +200,11 @@

      'Poslední výsledek testu' je výraz použitý v mnoha přehledech, jeho význam je následující:

        -
      • Podle poÅ™adí, ve kterém jsou sestavení pÅ™idána do testovacího plánu, se stanoví které sestavení je poslední aktuální. Výsledky +
      • Podle poÅ™adí, ve kterém jsou sestavení pÅ™idána do testovacího plánu, se stanoví které sestavení je poslední aktuální. Výsledky z posledního aktuálního sestavení budou nadÅ™azené nad výsledky starších sestavení. Například, pokud oznaÄíte testovací případ jako 'Neúspěšný' v sestavení 1, a dále ho oznaÄíte jako 'úspěšný' v sestavení 2, bude posledním výsledkem 'úspěšný'.
      • Pokud je testovací případ proveden v rámci jednoho sestavení vícekrát, poslední aktuální provedení bude -nadÅ™azeno vÅ¡em ostatním provedením v sestavení. Například, pokud je sestavení 3 použito ve vaÅ¡em týmu, tester 1 oznaÄí testovací případ jako +nadÅ™azeno vÅ¡em ostatním provedením v sestavení. Například, pokud je sestavení 3 použito ve vaÅ¡em týmu, tester 1 oznaÄí testovací případ jako 'úspěšný' ve dvÄ› hodiny a tester 2 ho oznaÄí jako 'neúspěšný' ve tÅ™i hodiny, bude zobrazen jako 'neúspěšný'.
      • Testovací případy zobrazené jako 'nespuÅ¡tÄ›né' v rámci sestavení se neberou v úvahu. Například, pokud oznaÄíte testovací případ jako 'úspěšný' v sestavení 1, nebudete ho provádÄ›t v sestavení 2, bude jeho poslední výsledek oznaÄen jako 'úspěšný'.
      • @@ -236,32 +230,32 @@

        Parametrizovaný přehled

        Tento pÅ™ehled je složen ze stránky pro nastavení parametrů pÅ™ehledu a stránky, která zobrazí výsledky na základÄ› nastavených parametrů. Stránka s nastavením parametrů obsahuje parametry, které můžete použít k řízení obsahu pÅ™ehledu, který bude zobrazen. VÅ¡echny parametry mají základní -nastavení, které maximalizuje poÄet testovacích případů a sestavení, ze kterých bude pÅ™ehled sestaven. ZmÄ›nou parametrů může uživatel filtrovat -výsledky a tím vygenerovat specifický pÅ™ehled dle kombinace vlastníka testovacích případů, klíÄového slova, +nastavení, které maximalizuje poÄet testovacích případů a sestavení, ze kterých bude pÅ™ehled sestaven. ZmÄ›nou parametrů může uživatel filtrovat +výsledky a tím vygenerovat specifický pÅ™ehled dle kombinace vlastníka testovacích případů, klíÄového slova, testovacích sad, nebo sestavení.

          -
        • KlíÄové slovo Může být vybráno maximalnÄ› jedno klíÄové slovo. StandartnÄ› není vybráno žádné klíÄové slovo. Pokud není +
        • KlíÄové slovo Může být vybráno maximalnÄ› jedno klíÄové slovo. StandartnÄ› není vybráno žádné klíÄové slovo. Pokud není zvoleno klíÄové slovo, nebude se brát na klíÄová slova a jejich pÅ™iÅ™azení ve vyhodnocení ohled. KlíÄová slova můžete pÅ™iÅ™adit -na stránce správy klíÄových slov nebo specifikace testů. KlíÄová slova jsou pÅ™iÅ™azená k testovacím případům pro vÅ¡echny jejich verze +na stránce správy klíÄových slov nebo specifikace testů. KlíÄová slova jsou pÅ™iÅ™azená k testovacím případům pro vÅ¡echny jejich verze a vÅ¡echny testovací plány. Pokud Vás zajímají pouze testovací případy pro specifické klíÄové slovo, nastavte adekvátnÄ› tento parametr.
        • Vlastník testovacích případů Může být vybrán maximalnÄ› jeden vlastník. StandartnÄ› není vybrán žádný vlastník. Pokud není -vlastník vybrán, nebude se brát na pÅ™iÅ™azení vlastníka k testovacím případům a jeho vyhodnocení ohled. AktuálnÄ› není možné -vyhledávat 'nepÅ™iÅ™azené' testovací případy. Vlastníky pro zvolený testovací plán je možné pÅ™iÅ™adit na stránce 'PÅ™iÅ™azení provedení -testovacího případu'. Pokud Vás zajímají pouze testovací případy provedené urÄitým testerem, +vlastník vybrán, nebude se brát na pÅ™iÅ™azení vlastníka k testovacím případům a jeho vyhodnocení ohled. AktuálnÄ› není možné +vyhledávat 'nepÅ™iÅ™azené' testovací případy. Vlastníky pro zvolený testovací plán je možné pÅ™iÅ™adit na stránce 'PÅ™iÅ™azení provedení +testovacího případu'. Pokud Vás zajímají pouze testovací případy provedené urÄitým testerem, nastavte adekvátnÄ› tento parametr.
        • Testovací sada Vybráno může být více testovacích sad najednou. StandartnÄ› jsou vybrány vÅ¡echny testovací sady. -Pouze data z vybraných testovacích sad budou použity v pÅ™ehledu. Pokud Vás zajímají pouze výsledky pro specifickou testovací sadu(y) +Pouze data z vybraných testovacích sad budou použity v pÅ™ehledu. Pokud Vás zajímají pouze výsledky pro specifickou testovací sadu(y) nastavte adekvátnÄ› tento parametr.
        • Sestavení Vybráno může být jedno nebo více sestavení. StandartnÄ› jsou vybrány vÅ¡echna sestavení. Pouze provedení testovacích -případů z vybraných sestavení budou použity v pÅ™ehledu. Například, pokud chcete zobrazit kolik testovacích případů bylo provedeno +případů z vybraných sestavení budou použity v pÅ™ehledu. Například, pokud chcete zobrazit kolik testovacích případů bylo provedeno v posledních tÅ™ech sestavení, adektvátnÄ› nastavte tento parametr. -Kombinace vybraného klíÄového slova, vlastníka testovacích případů a testovacích sad má přímou souvislost s testovacími případy z vybraného +Kombinace vybraného klíÄového slova, vlastníka testovacích případů a testovacích sad má přímou souvislost s testovacími případy z vybraného testovacího plánu, které budou použity pro výpoÄet metrik v rámci testovací sady a testovacího plánu. Například, pokud vyberete pÅ™iÅ™azen = 'Greg', klíÄové slovo='Priorita 1', a vÅ¡echny dostupné testovací sady, budou v rámci pÅ™ehledu zobrazeny pouze testovací případy -s prioritou 1 a s vlastníkem Greg. Celkový poÄet testovacích případů zobrazený v pÅ™ehledu '# Testovacích případů' bude ovlivnÄ›no tÄ›mito -parametry. VýbÄ›r sestavení může ovlivňit zda bude testovací případ považován za 'úspěšný', 'neúspěšný', 'blokovaný', nebo 'neprovedený'. Pro +s prioritou 1 a s vlastníkem Greg. Celkový poÄet testovacích případů zobrazený v pÅ™ehledu '# Testovacích případů' bude ovlivnÄ›no tÄ›mito +parametry. VýbÄ›r sestavení může ovlivňit zda bude testovací případ považován za 'úspěšný', 'neúspěšný', 'blokovaný', nebo 'neprovedený'. Pro další informace se podívejte na definici výrazu 'Poslední výsledek testu'.

        StisknÄ›te tlaÄítko 'Odeslat' pro odeslání dotazu a zobrazení stránky s výsledkem.

        @@ -271,54 +265,53 @@
      • Parametry použité pÅ™i výbÄ›ru dat pro pÅ™ehled
      • Celkové souÄty za celý testovací plán
      • Rozpad jednotlivých testovacích sad na celkové souÄty (# Testovacích případů / úspěšné / neúspěšné / blokované / neprovedené) a seznam -provedení testovacích případů v dané testovací sadÄ›. Pokud byl testovací případ proveden vícekrát v nÄ›kolika sestavení, budou zobrazena +provedení testovacích případů v dané testovací sadÄ›. Pokud byl testovací případ proveden vícekrát v nÄ›kolika sestavení, budou zobrazena vÅ¡echna provedení pro zvolené sestavení. Celkové souÄty vÅ¡ak budou pro zvolené sestavení a testovací sady zahrnovat pouze 'Poslední výsledek testů'.
      • PÅ™ehledy blokovaných, neúspěšných a neprovedených testů

        Tyto přehledy zobrazují všechny aktuálně blokované, neúspěšné nebo neprovedené testovací případy. 'Poslední výsledek testu' -výraz (který byl popsán v popisu Celkového přehledu testovacího plánu) je opět použit ke stanovení, zda +výraz (který byl popsán v popisu Celkového přehledu testovacího plánu) je opět použit ke stanovení, zda může být testovací případ považován za blokovaný, neúspěšný, nebo neprovedený. V případě, že je testlink konfigurován pro použití s nástrojem pro správu chyb, budou chyby zobrazeny v přehledech blokovaných a neúspěšných testovacích případů.

        Testovací přehled

        -

        Zobrazí stav každého testovacího případu pro každé sestavení. V případě, že byl v rámci jednoho sestavení proveden testovací případ vícekrát, +

        Zobrazí stav každého testovacího případu pro každé sestavení. V případÄ›, že byl v rámci jednoho sestavení proveden testovací případ vícekrát, poslední aktuální výsledek provedení bude zobrazen. Pokud pracujete s velkým poÄtem údajů, doporuÄuje se exportovat tento pÅ™ehled do formátu MS Excel pro jeho snažší využití.

        Grafy - Celkový přehled testovacího plánu

        -

        'Poslední výsledek testu' je výraz, který je použit pro vÅ¡echny ÄtyÅ™i zobrazené grafy. Pro usnadnÄ›ní vizualizace metrik aktuálního +

        'Poslední výsledek testu' je výraz, který je použit pro vÅ¡echny ÄtyÅ™i zobrazené grafy. Pro usnadnÄ›ní vizualizace metrik aktuálního testovacího plánu jsou grafy animovány. Následující ÄtyÅ™i grafy budou zobrazeny :

        • KoláÄový graf s celkovým poÄtem úspěšných / neúspěšných / blokovaných / a neprovedených testovacích případů
        • Sloupce grafu s výsledky dle klíÄového slovav
        • Sloupce grafu s výsledky dle vlastníka testovacích případů
        • Sloupce grafu s výsledky dle hlavní testovací sady
        -

        Sloupce v grafech jsou barevnÄ› rozliÅ¡eny, aby mohl uživatel rozliÅ¡it pÅ™ibližný poÄet +

        Sloupce v grafech jsou barevnÄ› rozliÅ¡eny, aby mohl uživatel rozliÅ¡it pÅ™ibližný poÄet úspěšných, neúspěšných, blokovaných a neprovedených testovacích případů.

        Stránka tohoto pÅ™ehledu vyžaduje, aby mÄ›l Váš prohlížeÄ nainstalováno flash rozšíření (http://www.maani.us), aby byly výsledky zobrazeny v grafické podobÄ›.

        Celkový poÄet chyb pro každý testovací případ

        Tento přehled zobrazuje každý testovací případ, ke kterému jsou přiřazeny jakékoliv chyby v rámci celého testovacího projektu. -Tento přehled je dostupný pouze v případě, že je připojen nástroj pro správu chyb.

        "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Přidat / Odebrat testovací případy v rámci testovacího plánu"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

        ÚÄel

        +Tento přehled je dostupný pouze v případě, že je připojen nástroj pro správu chyb.

        "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Přidat / Odebrat testovací případy v rámci testovacího plánu"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

        ÚÄel

        Umožňuje uživatelům (v roli 'Vedoucí' nebo vyšší) přidávat nebo odebírat testovací případy v rámci aktuálního testovacího plánu.

        Jak na to

        1. Klikněte na testovací sadu pro zobrazení všech jejich vnořených testovacích sad a k nim přiřazených testovacích případů.
        2. -
        3. Po zvolení vybraných testovacích případů kliknÄ›te na tlaÄítko 'PÅ™idat / Odebrat testovací případ' pro jejich pÅ™idání nebo odebrání. +
        4. Po zvolení vybraných testovacích případů kliknÄ›te na tlaÄítko 'PÅ™idat / Odebrat testovací případ' pro jejich pÅ™idání nebo odebrání. Poznámka: Do testovací sady není možné pÅ™idat testovací případy, které již obsahuje.
        5. -
        "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Přiřazení uživatelů k provádění testů"; -$TLS_htmltext['tc_exec_assignment'] = "

        ÚÄel

        +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Přiřazení uživatelů k provádění testů"; +$TLS_htmltext['tc_exec_assignment'] = "

        ÚÄel

        Tato stránka umožňuje vedoucím testů přiřadit uživatele ke konkrétnímu testu v rámci testovacího plánu.

        Jak na to

        @@ -327,16 +320,15 @@
      • Vyberte uživatele k pÅ™iÅ™azení.
      • StisknÄ›te tlaÄítko pro potvrzení pÅ™iÅ™azení.
      • OtevÅ™ete stránku provedení testovacích případů pro ověření pÅ™iÅ™azení. Můžete využít nastavení filtru na uživatele.
      • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Aktualizace testovacích případů v testovacím plánu."; -$TLS_htmltext['planUpdateTC'] = "

        ÚÄel

        +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Aktualizace testovacích případů v testovacím plánu."; +$TLS_htmltext['planUpdateTC'] = "

        ÚÄel

        Tato stránka umožňuje aktualizovat testovací případ na novÄ›jší (nebo jinou než aktuální) verzi v případÄ›, že se specifikace -testovacího případu zmÄ›nila. ÄŒasto se stává, že se bÄ›hem testování zmÄ›ní definice funkÄnosti." . - " Uživatel zmÄ›ní specifikaci testovacího případu, která je poté potÅ™eba také zpropagovat do testovacího plánu. V opaÄném případÄ›" . - " bude testovací plán obsahovat původní verzi testovacího případu, aby se zajistila konzistence verze testovacího případu a jeho výsledku.

        +testovacího případu zmÄ›nila. ÄŒasto se stává, že se bÄ›hem testování zmÄ›ní definice funkÄnosti." . + " Uživatel zmÄ›ní specifikaci testovacího případu, která je poté potÅ™eba také zpropagovat do testovacího plánu. V opaÄném případÄ›" . + " bude testovací plán obsahovat původní verzi testovacího případu, aby se zajistila konzistence verze testovacího případu a jeho výsledku.

        Jak na to

          @@ -344,30 +336,28 @@
        1. Zvolte novou verzi z rozbalovacího menu pro zvolený testovací případ.
        2. StisknÄ›te tlaÄítko 'Aktualizovat testovací plán' pro odeslání zmÄ›n.
        3. Ověření aktualizace: Otevřete stránku provedení testovacích případů, kde bude zobrazen text testovacího případu/prípadů.
        4. -
        "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Nastavení vysoké nebo nízké urgentnosti pro testovací případy a sady"; -$TLS_htmltext['test_urgency'] = "

        ÚÄel

        -

        TestLink umožnuje nastavení urgentnosti testovací sady k ovlivnění priority testovacích případů. +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Nastavení vysoké nebo nízké urgentnosti pro testovací případy a sady"; +$TLS_htmltext['test_urgency'] = "

        ÚÄel

        +

        TestLink umožnuje nastavení urgentnosti testovací sady k ovlivnÄ›ní priority testovacích případů. Priorita testovacích případů v rámci testovacího plánu závisí na jejich důležitosti a urgentnosti. - Vedoucí testů by mÄ›l nastavit sadu testovacích případů, které se budou prioritnÄ› provádÄ›t. V Äasové tísni by pak mÄ›lo být zajiÅ¡tÄ›no povedení + Vedoucí testů by mÄ›l nastavit sadu testovacích případů, které se budou prioritnÄ› provádÄ›t. V Äasové tísni by pak mÄ›lo být zajiÅ¡tÄ›no povedení tÄ›ch nejdůležitÄ›jších testů.

        Jak na to

        1. V navigaÄním panelu na levé stranÄ› zvolte testovací sadu.
        2. Vyberte úroveň urgentnosti (vysoká, střední nebo nízká). Přednastavena je střední úroveň. Můžete - snížit prioritu u testovacích případů, u kterých nebude důležitost změněna, popřípadě zvýšit v případě + snížit prioritu u testovacích případů, u kterých nebude důležitost změněna, popřípadě zvýšit v případě jejich větších změn.
        3. StisknÄ›te tlaÄítko 'Uložit' pro odeslání zmÄ›n.
        -

        Pro příklad, u testovacího případu s vysokou důležitostí v testovací sadě s nízkou urgentností bude " . - "výsledkem střední priorita."; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTcDocumentation'] = "Plan add testcase documentation"; -$TLS_htmltext['planAddTcDocumentation'] = "

        @TODO Plan add testcase documentation

        "; +

        Pro příklad, u testovacího případu s vysokou důležitostí v testovací sadě s nízkou urgentností bude " . + "výsledkem střední priorita."; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTcDocumentation'] = "Plan add testcase documentation"; +$TLS_htmltext['planAddTcDocumentation'] = "

        @TODO Plan add testcase documentation

        "; ?> diff --git a/locale/de_DE/description.php b/locale/de_DE/description.php index bd299f8efd..2719ebae71 100644 --- a/locale/de_DE/description.php +++ b/locale/de_DE/description.php @@ -1,60 +1,59 @@ -Optionen für das zu generierende Dokument -

        Diese Optionen erlauben es dem Benutzer Testfälle zu filtern bevor sie angezeigt werden. Wird das +

        Diese Optionen erlauben es dem Benutzer Testfälle zu filtern bevor sie angezeigt werden. Wird das Häkchen gesetzt, werden die Daten angezeigt. Um die angezeigten Daten zu verändern, de-/aktiveren Sie das Feld, klicken Sie den Filter an und selektieren die gewünschte Datenstufe des Baums.

        -

        Dokument Header: Benutzer können Dokument-Header-Informationen filtern. -Dokument-Header-Informationen umfassen: Einleitungen, Inhalte, Referenzen, +

        Dokument Header: Benutzer können Dokument-Header-Informationen filtern. +Dokument-Header-Informationen umfassen: Einleitungen, Inhalte, Referenzen, Test Methodologien und Test Einschränkungen.

        Testfall-Inhalt: Benutzer können den Testfall-Inhalt-Informationen filtern. Testfall-Inhalt-Informationen umfassen: Zusammenfassungen, Testschritten, erwartete Ergebnisse und Schlüsselwörter.

        -

        Testfall Zusammenfassung: Benutzer können Informationen der Testfall-Zusammenfassung aus dem Testfall-Titel filtern, -jedoch nicht aus dem Testfall-Inhalt. Für eine kurze Zusammenfassung mit Titel (ohne Testschritte, -erwartete Ergebnisse und Schlüsselwörter) ist die Testfall-Zusammenfassung nur Teilweise vom +

        Testfall Zusammenfassung: Benutzer können Informationen der Testfall-Zusammenfassung aus dem Testfall-Titel filtern, +jedoch nicht aus dem Testfall-Inhalt. Für eine kurze Zusammenfassung mit Titel (ohne Testschritte, +erwartete Ergebnisse und Schlüsselwörter) ist die Testfall-Zusammenfassung nur Teilweise vom Testfall-Inhalt getrennt. Wenn der Benutzer sich entscheidet den Testfall-Inhalt anzuschauen, wird die Testfall-Zusammenfassung mit angezeigt.

        -

        Inhaltsverzeichnis: TestLink fügt eine Liste aller Titel mit internen Hypertext-Links hinzu, wenn diese +

        Inhaltsverzeichnis: TestLink fügt eine Liste aller Titel mit internen Hypertext-Links hinzu, wenn diese Option aktiviert ist.

        -

        Ausgabeformat: Es gibt zwei Möglichkeiten: HTML und MS Word. Der Browser ruft MS Word im zweiten Fall auf.

        "; - -// testPlan.html +

        Ausgabeformat: Es gibt zwei Möglichkeiten: HTML und MS Word. Der Browser ruft MS Word im zweiten Fall auf.

        "; + +// testPlan.html $TLS_hlp_testPlan = "

        Test Plan

        Allgemein

        @@ -63,40 +62,24 @@

        Testausführung

        -

        In diesem Abschnitt können Benutzer Testfälle ausführen, Test Ergebnisse erzeugen -und die Test-Suite des Test Plans drucken. Ebenfalls können Sie die Ergebnisse -ihrer Testausführung verfolgen.

        +

        In diesem Abschnitt können Benutzer Testfälle ausführen, Test Ergebnisse erzeugen +und die Test-Suite des Test Plans drucken. Ebenfalls können Sie die Ergebnisse +ihrer Testausführung verfolgen.

        Test Plan Verwaltung

        -

        In diesem Abschnitt, der nur gesondert zugänglich ist, können Test Pläne administriert werden. -Test Plan Administratoren können Test Pläne erzeugen, bearbeiten und löschen sowie Testfälle -hinzufügen, bearbeiten, löschen und aktualisieren. Außerdem können Builds erzeugt werden und +

        In diesem Abschnitt, der nur gesondert zugänglich ist, können Test Pläne administriert werden. +Test Plan Administratoren können Test Pläne erzeugen, bearbeiten und löschen sowie Testfälle +hinzufügen, bearbeiten, löschen und aktualisieren. Außerdem können Builds erzeugt werden und Benutzern den Zugriff auf ausgewählte Test Pläne ermöglichen.
        Benutzer mit entsprechender Berechtigung können Priorität und Risiko bestimmen, den Besitz -an Test-Suiten (Kategorien) erwerben und Testmeilensteine erstellen.

        +an Test-Suiten (Kategorien) erwerben und Testmeilensteine erstellen.

        -

        Hinweis: Es ist möglich, dass ein leeres Dropdown ohne Test Plan erscheint. +

        Hinweis: Es ist möglich, dass ein leeres Dropdown ohne Test Plan erscheint. In diesem Fall werden alle Verknüpfungen (außer die freigegebenen) getrennt. Wenn das der Fall ist, - setzen Sie sich bitte mit einem Admin in Verbindung. Dieser kann Ihnen die nötigen -Zugriffsrechte gewähren oder einen Test Plan für Sie erstellen.

        "; - - - - - - - - - - - - - - - - - -// custom_fields.html + setzen Sie sich bitte mit einem Admin in Verbindung. Dieser kann Ihnen die nötigen +Zugriffsrechte gewähren oder einen Test Plan für Sie erstellen.

        "; + +// custom_fields.html $TLS_hlp_customFields = "

        Benutzerdefiniertes Feld

        Einige Fakten über die Implementierung von benutzerdefinierten Feldern:

          @@ -113,9 +96,9 @@
        • Name des benutzerdefinierten Feldes
        • Titel/Variablen Name (z.B: Das ist der Wert der an lang_get() API übermittelt oder angezeigt wird, wenn keine Übersetzung gefunden wurde.).
        • Typ des benutzerdefinierten Feldes (string, numeric, float, enum, email)
        • -
        • Mögliche Werte der Aufzählung (z.B.: ROT|GELB|BLAU) , anwendbar auf die Liste, Multiselektions Liste +
        • Mögliche Werte der Aufzählung (z.B.: ROT|GELB|BLAU) , anwendbar auf die Liste, Multiselektions Liste und auf Kombotypen.
          -Um mögliche Werte der Aufzählung zu trennen, kann der senkrechte Strich ('|') benutzt werden. Eine leere Zeichenfolge +Um mögliche Werte der Aufzählung zu trennen, kann der senkrechte Strich ('|') benutzt werden. Eine leere Zeichenfolge gilt als möglicher Wert.
        • Standard-Wert: NOCH NICHT UMGESETZT
        • @@ -132,28 +115,28 @@
        • Aktiviere beim Test Plan Entwurf. Der Wert kann beim Entwurf des Test Plans noch geändert werden (füge Testfälle dem Test Plan hinzu).
        • Verfügbar für: Nutzer wählt aus, welcher Art von Item das Feld folgt.
        -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

        Testfall Ausführung

        -

        Erlaubt es Nutzern Testfälle auszuführen. Die Ausführung selbst ist lediglich +

        Erlaubt es Nutzern Testfälle auszuführen. Die Ausführung selbst ist lediglich das Zuweisen eines Ergebnisses (OK, Fehlgeschlagen, Blockiert) an einen Testfall im ausgewählten Build.

        -

        Zugang zu einem Bug Tracking System kann konfiguriert werden. Nutzer kann neue BUGs hinzufügen und -existierende durchsuchen. Der Installationsanleitung können weitere Informationen entnommen werden.

        "; - -//bug_add.html +

        Zugang zu einem Bug Tracking System kann konfiguriert werden. Nutzer kann neue BUGs hinzufügen und +existierende durchsuchen. Der Installationsanleitung können weitere Informationen entnommen werden.

        "; + +// bug_add.html $TLS_hlp_btsIntegration = "

        Füge den Testfällen BUGs hinzu.

        (falls konfiguriert) TestLink hat nur eine einfache Integration in ein Bug Tracking Systems (BTS), -es ist nicht möglich eine Anfrage zum Erstellen eines BUGs an das BTs zu schicken. -Ebenfalls ist es nicht möglich die BUG ID zu bekommen. -Die Integration wird durch Verknüpfungen der BTS-Webseiten ermöglicht. +es ist nicht möglich eine Anfrage zum Erstellen eines BUGs an das BTs zu schicken. +Ebenfalls ist es nicht möglich die BUG ID zu bekommen. +Die Integration wird durch Verknüpfungen der BTS-Webseiten ermöglicht. Die folgenden Funktionen können aufgerufen werden:

        • Füge neuen BUG ein.
        • Zeige BUG Informationen.
        -

        +

        Wie man einen BUG hinzufügt

        @@ -162,19 +145,19 @@

      • Schritt 2: Notiere Sie die BUGID die vom BTS zugewiesen wurde.
      • Schritt 3: Schreibe Sie die BUGID in das Feld.
      • Schritt 4: Nutzen Sie die Schaltfläche 'BUG hinzufügen'.
      • -
      +
    Die relevanten BUG Daten werden auf der Ausführung Seite angezeigt, nachdem die 'BUG hinzufügen' Seite geschlossen wurde. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Einstellungen

    Die Einstellungen erlauben es einen Test Plan, Build oder eine Plattform (falls vorhanden) auszuwählen um sie auszuführen.

    Test Plan

    -Der geforderte Test Plan kann ausgewählt werden. Nach Auswahl des Test Plans +Der geforderte Test Plan kann ausgewählt werden. Nach Auswahl des Test Plans werden die entsprechenden Builds angezeigt und die Filter zurückgesetzt.

    Plattform

    @@ -184,89 +167,86 @@

    Das gewünschte Build, auf dem die Testfälle ausgeführt werden sollen, kann ausgewählt werden.

    Filter

    -

    Filter bieten die Möglichkeit die Testfälle nach eigenen Wünschen anzuzeigen bevor sie ausgeführt werden. +

    Filter bieten die Möglichkeit die Testfälle nach eigenen Wünschen anzuzeigen bevor sie ausgeführt werden. Mit bestimmten Filtern bzw. drücken der Schaltfläche 'Anwenden' kann die Anzahl der Testfall-Sätze reduziert werden.

    -

    Erweiterte Filter erlauben es schon angewandte Filter mit einer Reihe von Werten zu spezifizieren. +

    Erweiterte Filter erlauben es schon angewandte Filter mit einer Reihe von Werten zu spezifizieren. Das wird mit einem STRG-Klick in der Multi-Select ListBox erreicht.

    Stichwort-Filter

    -

    Nach Stichworten von Testfällen kann gefiltert werden. Mutliple Stichwörte " . -"können über den STRG-Klick ausgewählt werden " . -"Bei mehrfach ausgewählten Stichworten können die Testfälle angezeigt werden, ". -"die alle (Optionsfeld \"UND\") oder mindestens eins (Optionsfeld \"ODER\") ". -"dieser Stichworte beinhalten.

    +

    Nach Stichworten von Testfällen kann gefiltert werden. Mutliple Stichwörte " . + "können über den STRG-Klick ausgewählt werden " . + "Bei mehrfach ausgewählten Stichworten können die Testfälle angezeigt werden, " . + "die alle (Optionsfeld \"UND\") oder mindestens eins (Optionsfeld \"ODER\") " . + "dieser Stichworte beinhalten.

    Prioritäts-Filter

    -

    Testfälle können nach Test Prioritäten gefiltert werden. Die Test Prioritäten lauten \"Testfall Wichtung\" " . -"und \"Test Dringlichkeit\" in dem aktuellen Test Plan.

    +

    Testfälle können nach Test Prioritäten gefiltert werden. Die Test Prioritäten lauten \"Testfall Wichtung\" " . + "und \"Test Dringlichkeit\" in dem aktuellen Test Plan.

    Benutzer-Filter

    -

    Es kann nach Testfällen gefiltert werden, die zugewiesen an \"jemand\" oder nicht " . -"zugewiesen an \"niemand\" sind. Es kann auch nach bestimmten Testern gefiltert werden. ". -"Falls nach einem Tester gefiltert wird, können zusätzlich auch die Testfälle angezeigt werden, ". -"die nicht zugewiesen sind (erweiterte Filter sind anwählbar).

    +

    Es kann nach Testfällen gefiltert werden, die zugewiesen an \"jemand\" oder nicht " . + "zugewiesen an \"niemand\" sind. Es kann auch nach bestimmten Testern gefiltert werden. " . + "Falls nach einem Tester gefiltert wird, können zusätzlich auch die Testfälle angezeigt werden, " . + "die nicht zugewiesen sind (erweiterte Filter sind anwählbar).

    Ergebnis-Filter

    -

    Testfälle können nach ihren Ergebnissen gefiltert werden (erweiterte Filter sind anwählbar). -Es kann gefiltert werden nach dem Ergebnis \"des ausgewählten Builds für die Ausführung\", ". -" \"der letzten Ausführung\", \"ALLER Builds\", " . -"\"BELIEBIGES Build\" und \"eines bestimmten Builds\". Falls ein \"bestimmtes Build\" ausgewählt wurde" . -"muss das Build bestimmt werden.

    "; - - -// newest_tcversions.html +

    Testfälle können nach ihren Ergebnissen gefiltert werden (erweiterte Filter sind anwählbar). +Es kann gefiltert werden nach dem Ergebnis \"des ausgewählten Builds für die Ausführung\", " . + " \"der letzten Ausführung\", \"ALLER Builds\", " . + "\"BELIEBIGES Build\" und \"eines bestimmten Builds\". Falls ein \"bestimmtes Build\" ausgewählt wurde" . + "muss das Build bestimmt werden.

    "; + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Neuere Versionen von verknüpften Testfällen

    Alle, dem Test Plan verknüpfte Testfälle, werden analysiert und gelistet, die die neuesten Versionen der Testfälle anzeigt (im Vergleich zum aktuellen Satz des Test Plans). -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Anforderungsabdeckung


    Diese Funktion erlaubt das Aufstellen einer Abdeckung von Benutzer- oder Systemanforderungen mit Testfällen. Auf der Hauptseite kann über \"Anforderungsspezifikation\" navigiert werden.

    Anforderungsspezifikation

    -

    Anforderungen sind nach dem Dokument 'Anforderung Spezifikation', welches mit dem Test Plan -verbunden ist, gruppiert.
    TestLink unterstützt nicht beide Versionen für die Anf.Spezifikation und +

    Anforderungen sind nach dem Dokument 'Anforderung Spezifikation', welches mit dem Test Plan +verbunden ist, gruppiert.
    TestLink unterstützt nicht beide Versionen für die Anf.Spezifikation und der Anforderung selbst. Also sollte die Version des Dokuments erst nach der Spezifikation eingefügt werden. Titel. -Der Nutzer kann dem Feld Inhalt eine kurze Beschreibung oder Notiz hinzufügen.

    +Der Nutzer kann dem Feld Inhalt eine kurze Beschreibung oder Notiz hinzufügen.

    -

    Überschrieben Anzahl von Anf. Dient der Evaluation der Anf. -Abdeckung, falls nicht alle Anforderungen importiert wurden. -Der Wert 0 bedeutet, dass die aktuelle Anzahl der Anf. für Metriken genutzt wird.

    -

    Beispielsweise SRS beinhaltet 200 Anforderungen aber nur 50 sind in TestLink hinzugefügt worden. Die Test +

    Überschrieben Anzahl von Anf. Dient der Evaluation der Anf. +Abdeckung, falls nicht alle Anforderungen importiert wurden. +Der Wert 0 bedeutet, dass die aktuelle Anzahl der Anf. für Metriken genutzt wird.

    +

    Beispielsweise SRS beinhaltet 200 Anforderungen aber nur 50 sind in TestLink hinzugefügt worden. Die Test Abdeckung ist 25% (falls alle importierten Anforderungen getestet werden).

    Anforderungen

    -

    Mit einem Klick auf den Titel der erstellten Anf. Spezifikation kann für das Dokument Anforderungen erstellt, +

    Mit einem Klick auf den Titel der erstellten Anf. Spezifikation kann für das Dokument Anforderungen erstellt, bearbeitet, gelöscht oder importiert werden. Jede Anforderung hat einen Titel, Inhalt und Status. -Der Status sollte \"Normal\" oder \"Nicht testbar\" sein. Nicht testbare Anforderungen gehen in die Metriken -nicht ein. Dieser Parameter kann für noch nicht implementierte Funktionen und -falsch entworfenen Anforderungen genutzt werden.

    +Der Status sollte \"Normal\" oder \"Nicht testbar\" sein. Nicht testbare Anforderungen gehen in die Metriken +nicht ein. Dieser Parameter kann für noch nicht implementierte Funktionen und +falsch entworfenen Anforderungen genutzt werden.

    -

    Mit der Nutzung von Multi Aktionen mit abgehakten Anforderungen auf der Spezifikations Umgebung, -können neue Testfälle für Anforderungen erstellt werden. Diese Testfälle werden der Test Suite mit dem -konfigurierten Namen (Standard ist: $tlCfg->req_cfg->default_testsuite_name = +

    Mit der Nutzung von Multi Aktionen mit abgehakten Anforderungen auf der Spezifikations Umgebung, +können neue Testfälle für Anforderungen erstellt werden. Diese Testfälle werden der Test Suite mit dem +konfigurierten Namen (Standard ist: $tlCfg->req_cfg->default_testsuite_name = \"Test Suite erstellt über Anforderung - Auto\";). hinzugefügt. Titel und Inhalt werden in die Testfälle kopiert.

    -"; - +"; + $TLS_hlp_req_coverage_table = "

    Abdeckung:

    Ein Wert von bspw. \"40% (8/20)\" bedeutet das 20 Testfälle für diese Anforderung noch zu erstellen sind, -um sie vollständig abzudecken. Die 8 Testfälle, die schon erstellt und mit dieser Anforderung verknüpft +um sie vollständig abzudecken. Die 8 Testfälle, die schon erstellt und mit dieser Anforderung verknüpft wurden, ergeben eine Abdeckung von 40 Prozent. -"; - - -// req_edit +"; + +// req_edit $TLS_hlp_req_edit = "

    Interne Links im Inhalt:

    -

    Interne Links können genutzt werden um Links zu anderen Anforderungen/Anforderungsspezifikation -mit einer speziellen Syntax zu erstellen. +

    Interne Links können genutzt werden um Links zu anderen Anforderungen/Anforderungsspezifikation +mit einer speziellen Syntax zu erstellen. Das Verhalten der internen Links kann über die Konfigurationsdatei angepasst werden.

    Benutzung: @@ -279,22 +259,21 @@ Diese Syntax funktioniert auch für Anforderungsspezifikationen.

    Änderungsprotokoll/Revisionierung:

    -

    Immer wenn eine Änderung an einer Anforderung vorgenommen wird fragt Testlink nach einer Protokollierung der Änderung. +

    Immer wenn eine Änderung an einer Anforderung vorgenommen wird fragt Testlink nach einer Protokollierung der Änderung. Das Änderungsprotokoll dient der Rückverfolgbarkeit (Traceability).

    -

    Wenn sich nur der Inhalt der Anforderung ändert, steht es dem Autor frei, -ob er eine neue Revision erstellen möchte oder nicht. Sollte sich etwas außer dem Inhalt ändern ist der Autor gezwungen, +

    Wenn sich nur der Inhalt der Anforderung ändert, steht es dem Autor frei, +ob er eine neue Revision erstellen möchte oder nicht. Sollte sich etwas außer dem Inhalt ändern ist der Autor gezwungen, eine neue Revision zu erstellen.

    -"; - - -// req_view +"; + +// req_view $TLS_hlp_req_view = "

    Direkte Links:

    -

    Um Anforderungsdokumente so leicht wie möglich mit anderen teilen zu können -bietet TestLink die Möglichkeit einen direkten Link zu diesem Dokument zu erzeugen. +

    Um Anforderungsdokumente so leicht wie möglich mit anderen teilen zu können +bietet TestLink die Möglichkeit einen direkten Link zu diesem Dokument zu erzeugen. Klicken Sie dazu das Globus Icon an.

    Verlauf anzeigen:

    -

    Dieses Feature erlaubt es Revisionen/Versionen von Anforderungen zu vergleichen, +

    Dieses Feature erlaubt es Revisionen/Versionen von Anforderungen zu vergleichen, sofern mehr als eine Revision/Version der Anforderung existiert. Die Übersicht zeigt das Änderungsprotokoll, das Datum und den Autor der letzten Änderung für jede Revision/Version.

    @@ -317,18 +296,17 @@

    Zeigt alle Testfälle, die mit der Anforderung verknüpft wurden.

    Beziehungen:

    -

    Beziehungen werden benutzt um Beziehungen zwischen Anforderungen zu modellieren. -Benutzerdefinierte Beziehungen und die Möglichkeit Beziehungen zwischen Anforderungen +

    Beziehungen werden benutzt um Beziehungen zwischen Anforderungen zu modellieren. +Benutzerdefinierte Beziehungen und die Möglichkeit Beziehungen zwischen Anforderungen in verschiedenen Projekten herstellen zu können, werden in der Konfigurationsdatei konfiguriert.

    -

    Setzt man die Beziehung \"Anforderung A ist Vater von Anforderung B\", +

    Setzt man die Beziehung \"Anforderung A ist Vater von Anforderung B\", wird Testlink die Beziehung \"Anforderung B ist Kind von Anforderung A\" implizit setzen.

    -"; - - -// req_spec_edit +"; + +// req_spec_edit $TLS_hlp_req_spec_edit = "

    Interne Links im Inhalt:

    -

    Interne Links können genutzt werden um Links zu anderen Anforderungen/Anforderungsspezifikation -mit einer speziellen Syntax zu erstellen. +

    Interne Links können genutzt werden um Links zu anderen Anforderungen/Anforderungsspezifikation +mit einer speziellen Syntax zu erstellen. Das Verhalten der internen Links kann über die Konfigurationsdatei angepasst werden.

    Benutzung: @@ -339,40 +317,36 @@

    Das Testprojekt und ein Anker der zu verlinkenden Anforderung kann ebenfalls angegeben werden:
    [req tproj=<tproj_prefix> anchor=<anker_name>]Anf_Dokument_ID[/req]
    Diese Syntax funktioniert auch für Anforderungsspezifikationen.

    -"; - - +"; + $TLS_hlp_req_coverage_table = "

    Abdeckung:

    Ein Wert von z.B. \"40% (8/20)\" bedeutet, dass 20 Testfälle erstellt werden müssen um die Anforderung komplett durch Testfälle abzudecken. Acht dieser Testfälle wurden bereits erstellt und der Anforderung zugewiesen, was einer Abdeckung von 40% entspricht. -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Bezüglich 'Speichere Benutzerdefinierte Felder'

    -Falls zum Test Project definiert und zugewiesen,
    +Falls zum Test Project definiert und zugewiesen,
    Benutzerdefinierte Felder mit:
    'Zeige bei der Test Plan Entwurf=Wahr' und
    'Aktiviere beim Test Plan Entwurf=Wahr'
    Es werden auf der Seite NUR Testfälle angezeigt die mit dem Test Plan verknüpft sind. -"; - - -// resultsByTesterPerBuild.tpl +"; + +// resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = "Zusatzinformationen über Tester:
    Bei einem Klick auf den Tester Namen in dieser Tabelle öffnet sich eine detaillierte Übersicht über alle dem jew. Tester zugewiesene Testfälle und dessen Testprozess.

    Hinweis:
    -Diese Übersicht zeigt die Testfälle, die einem bestimmten Nutzer zugewiesen sind und +Diese Übersicht zeigt die Testfälle, die einem bestimmten Nutzer zugewiesen sind und basierend auf dem jeweiligen aktiven Build ausgeführt wurden. Auch wenn der Testfall von einem anderen Nutzer ausgeführt wurde, erscheint der Testfall bei dem zugewiesenen Nutzer als Ausgeführt. -"; - - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/de_DE/strings.txt b/locale/de_DE/strings.txt index 973f54869e..0577ee14ba 100644 --- a/locale/de_DE/strings.txt +++ b/locale/de_DE/strings.txt @@ -48,9 +48,12 @@ $TLS_last_update = "Letztes Übersetzungs-Update: 18.08.2011 (TestLink 1.9.4)"; // ----- General terms (used wide) ---------------------------- $TLS_active_click_to_change = "Aktiv (klicken, um zu deaktivieren) "; $TLS_api_key = "API Schlüssel"; -$TLS_builds = "Builds"; +$TLS_builds = "Builds"; +$TLS_build = 'Build'; $TLS_active_builds = "Aktive Builds"; $TLS_all_active_builds = "[Alle aktiven Builds]"; +$TLS_exec_result = 'Ergebnis'; +$TLS_exec_attachments = 'Ausführungsanhänge'; $TLS_inactive_click_to_change = "Deaktiviert (klicken, um zu aktivieren)"; $TLS_default_auth_method = "Standard"; $TLS_authentication_method = "Authentifizierungs-Methode"; @@ -76,6 +79,12 @@ $TLS_availability = "Verfügbarkeit"; $TLS_build = "Build"; $TLS_check_uncheck_all = "Auswahl invertieren"; $TLS_check_uncheck_all_for_remove = "alle zum Entfernen an-/abwählen"; +$TLS_click_to_set_open = "Geschlossen (zum Öffnen klicken)"; +$TLS_click_to_set_closed = "Öffnen (zum Schließen klicken)"; +$TLS_click_to_disable = "Aktiviert (zum Deaktivieren klicken)"; +$TLS_click_to_enable = "Deaktiviert (zum Aktivieren klicken)"; +$TLS_current_testcase = "Dieser Testfall"; +$TLS_this_tcversion = "Diese Testfallversion "; $TLS_confirm = "Bestätigen"; $TLS_config = "Konfiguration"; $TLS_created_by = "Erstellt von"; @@ -119,6 +128,7 @@ $TLS_manual = "Manuell"; $TLS_Milestone = "Meilenstein"; $TLS_months = "Monate"; $TLS_monitor = "Beobachten"; +$TLS_monitor_set = "Beobachter"; $TLS_name = "Name"; $TLS_navigator_add_remove_tcase_to_tplan = "Testfälle Hinzufügen/Entfernen"; $TLS_navigator_tc_exec_assignment = "Ausführungs Zuweisung"; @@ -597,6 +607,8 @@ $TLS_btn_create_testsuite = "Erstellen"; $TLS_tc_keywords = "Stichwörter"; $TLS_title_create = "Erstellen"; $TLS_warning_empty_testsuite_name = "Sie müssen der Testsuite einen Namen geben!"; +// ----- gui/templates/script_add.tpl ----- +// ----- gui/templates/script_delete.tpl ----- @@ -766,6 +778,7 @@ $TLS_collapse_tree = "Baum zuklappen"; $TLS_filter_active_inactive = "Aktiv/Inaktiv"; $TLS_show_only_active_testcases = "Nur aktive Testfälle"; $TLS_show_only_inactive_testcases = "Nur inaktive Testfälle"; +$TLS_show_whole_spec_on_right_panel = 'Vollständig anzeigen (im rechten Bereich)'; $TLS_test_grouped_by = "Tests gruppiert nach"; $TLS_mode_test_suite = "Testsuite"; $TLS_mode_req_coverage = "Bedarf"; @@ -1110,6 +1123,10 @@ $TLS_delete_bug = "Bug löschen"; $TLS_del_bug_warning_msg = "Sind Sie sicher, dass Sie diesen Bug wirklich löschen wollen?"; +// gui/templates/inc_show_script_table.tpl +$TLS_caption_scripttable = "Relevante Testskripte"; +$TLS_delete_script ="Skriptlink aus TestLink löschen"; +$TLS_del_script_warning_msg = "Diesen Skriptlink wirklich aus der TestLink-Datenbank löschen?"; // gui/templates/reqSpecList.tpl $TLS_btn_assign_tc = "Testfall zuweisen"; @@ -1656,6 +1673,8 @@ $TLS_btn_del_this_version = "Version löschen"; $TLS_btn_execute_automatic_testcase = "Automatisierte Testfälle durchführen"; $TLS_btn_mv_cp = "Verschieben / Kopieren"; $TLS_btn_new_version = "Neue Version erstellen"; + + $TLS_btn_new_sibling = "Neuer Geschwisterknoten"; $TLS_hint_new_sibling = "Erzeuge Testfall in der aktuellen Test Suite"; $TLS_hint_new_version = "Erzeuge neue version"; @@ -3660,6 +3679,86 @@ $TLS_plugin_installed = "undefined"; $TLS_confirm_install_header = "undefined"; $TLS_confirm_install_text = "undefined"; $TLS_confirm_uninstall_header = "undefined"; -$TLS_confirm_uninstall_text = "undefined"; +$TLS_confirm_uninstall_text = "undefined"; + + +// ----- lib/requirements/reqMonitorOverview.php ----- +$TLS_caption_req_monitor_overview = "Übersicht über den Anforderungsmonitor"; +$TLS_on_turn_off = 'Ein (klicken, um die Überwachung zu beenden)'; +$TLS_off_turn_on = 'Aus (klicken, um die Überwachung zu starten)'; + +$TLS_mail_subject_req_delete_version = 'Anforderungsversion gelöscht Deleted [%docid%-%title%]'; + +$TLS_mail_subject_req_delete = 'Anforderung gelöscht [%docid%-%title%]'; + +$TLS_mail_subject_req_new_version = 'Neue Version der Anforderung [%docid%-%title%]'; + +$TLS_hint_add_testcases_to_testplan_status = 'Testfälle mit der neuesten Version und einem Status mit einem dieser Werte (%s) werden auf diesem Bildschirm nicht angezeigt'; + +$TLS_assignments = "Aufgaben:"; + +$TLS_btn_bulk_mon = 'Massenüberwachung'; +$TLS_bulk_monitoring = $TLS_btn_bulk_mon; +$TLS_monitoring = 'Überwachung'; +$TLS_btn_toogle_mon = 'Überwachung umschalten'; +$TLS_btn_start_mon = 'Überwachung starten'; +$TLS_btn_stop_mon = 'Überwachung stoppen'; + +$TLS_issueTracker_connection_ok = 'Test der Verbindung OK'; +$TLS_issueTracker_connection_ko = 'Test der Verbindung nicht OK'; +$TLS_codeTracker_connection_ok = 'Test der Verbindung OK'; +$TLS_codeTracker_connection_ko = 'Test der Verbindung nicht OK'; + +$TLS_click_passed = "Klicken Sie hier, um es auf „Bestanden“ zu setzen."; +$TLS_click_failed = "Klicken Sie hier, um es auf „Fehlgeschlagen“ zu setzen"; +$TLS_click_blocked = "Klicken Sie hier, um es auf „Blockiert“ zu setzen"; + +$TLS_click_passed_next = "Klicken Sie, um auf „Bestanden“ zu setzen und zum nächsten zu gelangen"; +$TLS_click_failed_next = "Klicken Sie, um das Feld auf „Fehlgeschlagen“ zu setzen und zum nächsten zu gelangen"; +$TLS_click_blocked_next = "Klicken Sie, um es auf „Blockiert“ zu setzen und zum nächsten zu gelangen"; + +$TLS_send_spreadsheet_by_email = 'Tabellenkalkulation per E-Mail an mich selbst senden'; +$TLS_send_by_email_to_me = 'Per E-Mail an mich selbst senden'; + +$TLS_search_items = "Suchen"; +$TLS_req_document_id = "Anf.-Dokument-ID"; +$TLS_id = 'ID'; + +$TLS_search_words_or = 'Die Suche muss mindestens EIN Wort ergeben (ODER)'; +$TLS_search_words_and = 'Die Suche muss mit ALLEN Wörtern übereinstimmen (UND)'; +$TLS_search_words_placeholder = 'Geben Sie hier die Wörter ein, nach denen Sie suchen möchten'; +$TLS_search_words_on_attr = "Das Wort/die Wörter werden anhand eines oder mehrerer Attribute gesucht, die Sie unten auswählen"; + +$TLS_search_other_attr = "Sie können Suchkriterien festlegen, die keine Wörter verwenden. Diese Suchkriterien werden jedoch im UND-Modus hinzugefügt"; + +$TLS_search_created_by_ph = 'Wert wird nach Login/Vorname/Nachname gesucht'; +$TLS_multiple_entities_search = 'Suche nach mehreren Entitäten'; +$TLS_no_access_to_feature = 'Sie verfügen nicht über ausreichende Rechte, um auf die Funktion zuzugreifen'; + +$TLS_applyExecTypeChangeToAllSteps = "Auf alle Schritte anwenden"; + +$TLS_user_bulk_action = 'Benutzer für Massenaktionen'; +$TLS_title_issuetracker_mgmt = 'Issue-Tracker'; +$TLS_title_codetracker_mgmt = 'Code-Tracker'; +$TLS_desc_cfield_assignment = "Benutzerdefinierte Feldzuweisung"; +$TLS_desc_exec_assign_testcases = "Testfallausführung zuweisen"; +$TLS_expiration_date = 'Verfallsdatum'; +$TLS_system_descr = "Test- und Anforderungsmanagement-Software"; +$TLS_poweredBy = 'Erstellt von'; +$TLS_expiration = 'Ablauf'; +$TLS_no_user_selected = 'Sie müssen mindestens einen Benutzer auswählen'; + +$TLS_can_not_delete_relation_tcversion_frozen = "Löschen deaktiviert: Testfallversion ist eingefroren"; +$TLS_can_not_delete_relation_related_tcversion_frozen = "Löschen deaktiviert: Die zugehörige Testfallversion ist eingefroren."; +$TLS_obsolete = 'veraltet'; + +$TLS_tcversion_executed_keyword_assignment_blocked = 'Testfallversion wurde ausgeführt, die Systemkonfiguration erlaubt keine Schlüsselwortverwaltung'; + +$TLS_select_keywords = 'Schlüsselwörter auswählen'; + +$TLS_issueReporter = 'Gemeldet von'; +$TLS_issueHandler = 'Zugewiesen an / Bearbeitet von'; + + // ----- END ------------------------------------------------------------------ ?> diff --git a/locale/de_DE/texts.php b/locale/de_DE/texts.php index a04c2b3cfa..cd86cf3a72 100644 --- a/locale/de_DE/texts.php +++ b/locale/de_DE/texts.php @@ -1,52 +1,47 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * @package TestLink - * @author Martin Havlat, Julian Krien - * @copyright 2003-2009, TestLink community - * @version CVS: $Id: texts.php,v 1.14 2010/06/24 17:25:57 asimon83 Exp $ - * @link http://www.teamst.org/index.php - * - * @internal Revisions: - * 20100517 - Julian - update of header according to en_GB texts.php - * - * Edit by: devwag00\fixccey 06.05.2014 - * - * Edit by: sschiele@mesnet.de 2014-08-19 - * Fixed String formatting errors - **/ - - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Anwendungsfehler"; -$TLS_htmltext['error'] = "

    Es ist ein unerwarteter Fehler aufgetreten. Bitte " . - "überprüfen Sie den Event Viewer und/oder Log-Dateien für weitere Details." . - "

    Sie können das Problem gerne melden. Besuchen Sie hierzu bitte unsere " . - - "Webseite.

    "; - - - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['assignReqs'] = "Zuweisung von Anforderungen an Testfälle"; -$TLS_htmltext['assignReqs'] = "

    Zweck:

    -

    Benutzer können eine Beziehung zwischen Anforderungen und Testfällen herstellen. +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * @package TestLink + * @author Martin Havlat, Julian Krien + * @copyright 2003-2009, TestLink community + * @version CVS: $Id: texts.php,v 1.14 2010/06/24 17:25:57 asimon83 Exp $ + * @link http://www.teamst.org/index.php + * + * @internal Revisions: + * 20100517 - Julian - update of header according to en_GB texts.php + * + * Edit by: devwag00\fixccey 06.05.2014 + * + * Edit by: sschiele@mesnet.de 2014-08-19 + * Fixed String formatting errors + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Anwendungsfehler"; +$TLS_htmltext['error'] = "

    Es ist ein unerwarteter Fehler aufgetreten. Bitte " . + "überprüfen Sie den Event Viewer und/oder Log-Dateien für weitere Details." . + "

    Sie können das Problem gerne melden. Besuchen Sie hierzu bitte unsere " . + + "Webseite.

    "; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['assignReqs'] = "Zuweisung von Anforderungen an Testfälle"; +$TLS_htmltext['assignReqs'] = "

    Zweck:

    +

    Benutzer können eine Beziehung zwischen Anforderungen und Testfällen herstellen. Ein Testfall kann keiner, einer oder mehreren Anforderungen zugewiesen werden oder umgekehrt. Diese Zuweisungen erlauben es später eine Aussage darüber zu treffen, welche Anforderungen abhängig von den Testergebnissen erfolgreich umgesetzt wurden.

    @@ -64,47 +59,46 @@ Anforderungen, für die Sie eine Testfallzuweisung hinzufügen bzw. entfernen möchten und klicken Sie anschließend den entsprechenden Button. -"; +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Testfälle bearbeiten"; +$TLS_htmltext['editTc'] = "

    Die Testspezifikation ermöglicht das " . + "Anzeigen und Editieren aller vorhandenen Test Suiten und Testfälle. Testfälle sind " . + "versioniert. Alle früheren Versionen sind noch für die Einsicht und Verwaltung " . + "verfügbar.

    -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Testfälle bearbeiten"; -$TLS_htmltext['editTc'] = "

    Die Testspezifikation ermöglicht das " . - "Anzeigen und Editieren aller vorhandenen Test Suiten und Testfälle. Testfälle sind " . - "versioniert. Alle früheren Versionen sind noch für die Einsicht und Verwaltung " . - "verfügbar.

    -

    Anweisung:

      -
    1. Wähle das Test Projekt in der Baum Navigation (oberster Knoten). Bitte beachten: " . - "Sie können über die Drop-Down Liste in der Ecke oben rechts das aktive Projekt ändern.
    2. -
    3. Mit einem Klick auf Neue Testsuite erzeugen Sie eine neue Test Suite. Test Suiten " . - "strukturieren Test Dokumente entsprechend Ihrer Konventionen (funktionale/ nicht-funktionale " . - "Tests, Produktkomponenten oder Funktionen, Änderungswünsche etc.). Die Beschreibung einer " . - "Test Suite kann den Inhalt der eingefügten Testfälle, die Standard Konfiguration ". - "Verknüpfungen zu relevanten Dokumenten, Beschränkungen und andere nützliche Informationen beinhalten. Im Allgemeinen, " . - " alle Anmerkungen die Gemeinsamkeiten haben mit den Testfall-Kindknoten. Test Suiten folgen " . - "dem "Datei/Ornder" System, sodass Nutzer Test Suiten im Test Projekt " . - "verschieben und kopieren können. Sie können auch importiert und exportiert werden (mit den Testfällen).
    4. -
    5. Test Suiten sind skalierbare Ordner. Nutzer können Test Suiten im Test Projekt " . - "verschieben und kopieren. Sie können importiert und exportiert werden (mit den Testfällen). -
    6. Durch Auswahl der neu erstellten Test Suite in dem Navigations-Baum können neue Testfälle mit ". - "Testfälle erstellen erstellt werden. Ein Testfall in einem Test Projekt spezifiziert ein bestimmtes Testszenario, " . - "die erwarteten Ergebnisse und benutzerdefinierte Felder (siehe Benutzeranleitung). " . - "Für eine verbesserte Nachvollziehbarkeit ". - "(Traceability) können den Testfällen Stichwörter zugewiesen werden.
    7. +
    8. Wähle das Test Projekt in der Baum Navigation (oberster Knoten). Bitte beachten: " . + "Sie können über die Drop-Down Liste in der Ecke oben rechts das aktive Projekt ändern.
    9. +
    10. Mit einem Klick auf Neue Testsuite erzeugen Sie eine neue Test Suite. Test Suiten " . + "strukturieren Test Dokumente entsprechend Ihrer Konventionen (funktionale/ nicht-funktionale " . + "Tests, Produktkomponenten oder Funktionen, Änderungswünsche etc.). Die Beschreibung einer " . + "Test Suite kann den Inhalt der eingefügten Testfälle, die Standard Konfiguration " . + "Verknüpfungen zu relevanten Dokumenten, Beschränkungen und andere nützliche Informationen beinhalten. Im Allgemeinen, " . + " alle Anmerkungen die Gemeinsamkeiten haben mit den Testfall-Kindknoten. Test Suiten folgen " . + "dem "Datei/Ornder" System, sodass Nutzer Test Suiten im Test Projekt " . + "verschieben und kopieren können. Sie können auch importiert und exportiert werden (mit den Testfällen).
    11. +
    12. Test Suiten sind skalierbare Ordner. Nutzer können Test Suiten im Test Projekt " . + "verschieben und kopieren. Sie können importiert und exportiert werden (mit den Testfällen). +
    13. Durch Auswahl der neu erstellten Test Suite in dem Navigations-Baum können neue Testfälle mit " . + "Testfälle erstellen erstellt werden. Ein Testfall in einem Test Projekt spezifiziert ein bestimmtes Testszenario, " . + "die erwarteten Ergebnisse und benutzerdefinierte Felder (siehe Benutzeranleitung). " . + "Für eine verbesserte Nachvollziehbarkeit " . + "(Traceability) können den Testfällen Stichwörter zugewiesen werden.
    14. In der Baumansicht auf der linken Seite können Sie die Daten bearbeiten. Jeder Testfall speichert den eigenen Verlauf.
    15. Die erstellte Test Spezifikation kann einem Test Plan zugewiesen werden, wenn die Testfälle fertig sind.
    16. + \"javascript:open_help_window('glossary','$locale');\">Test Plan zugewiesen werden, wenn die Testfälle fertig sind.
    -

    Mit TestLink können Testfälle in Test Suiten gegliedert werden." . -"Test Suiten können in Test Suiten verschachtelt werden, sodass hierarchische Einstufungen möglich sind. - Diese Information kann dann mit den Testfällen ausgedruckt werden.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Testfälle suchen"; -$TLS_htmltext['searchTc'] = "

    Zweck:

    +

    Mit TestLink können Testfälle in Test Suiten gegliedert werden." . + "Test Suiten können in Test Suiten verschachtelt werden, sodass hierarchische Einstufungen möglich sind. + Diese Information kann dann mit den Testfällen ausgedruckt werden.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Testfälle suchen"; +$TLS_htmltext['searchTc'] = "

    Zweck:

    Suche von Testfällen anhand frei definierbaren Parametern.

    Anweisung:

    @@ -120,17 +114,13 @@

    - Es werden nur Testfälle innerhalb des aktuellen Testprojekts durchsucht.
    - Die Suche ist unabhängig von \"Groß- und Kleinschreibung\".
    -- Leere Felder der Suchmaske werden nicht berücksichtigt.

    "; - - - - - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Anforderungen suchen"; -$TLS_htmltext['searchReq'] = "

    Zweck:

    +- Leere Felder der Suchmaske werden nicht berücksichtigt.

    "; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Anforderungen suchen"; +$TLS_htmltext['searchReq'] = "

    Zweck:

    Suche von Anforderungen anhand frei-definierbare Parametern.

    @@ -147,12 +137,12 @@

    - Es werden nur Anforderungen innerhalb des aktuellen Testprojekts durchsucht.
    - Die Suche ist unabhängig von \"Groß- und Kleinschreibung\".
    -- Leere Felder der Suchmaske werden nicht berücksichtigt.

    "; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Anforderungsspezifikationen suchen"; -$TLS_htmltext['searchReqSpec'] = "

    Zweck:

    +- Leere Felder der Suchmaske werden nicht berücksichtigt.

    "; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Anforderungsspezifikationen suchen"; +$TLS_htmltext['searchReqSpec'] = "

    Zweck:

    Suche von Anforderungsspezifikationen anhand frei definierbaren Parametern.

    @@ -171,14 +161,12 @@

    - Es werden nur Anforderungsspezifikationen innerhalb des aktuellen Testprojekts durchsucht.
    - Die Suche ist unabhängig von \"Groß- und Kleinschreibung\".
    -- Leere Felder der Suchmaske werden nicht berücksichtigt.

    "; -/* end contribution */ - - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Testspezifikation erstellen"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Zweck:

    +- Leere Felder der Suchmaske werden nicht berücksichtigt.

    "; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Testspezifikation erstellen"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Zweck:

    Hier können alle Testfälle eines Test Projekts, einer Test Suite oder ein einzelner Testfall ausgedruckt werden.

    @@ -189,52 +177,52 @@ dann auf einen Testfall, eine Test Suite oder ein Test Projekt. Eine druckbare Seitenansicht wird angezeigt.

  • Ändern Sie über die \"Zeige als\" drop-box in der Navigationsleiste den Anzeigemodus als -HTML, OpenOffice oder einem Microsoft Word Dokument. +HTML, OpenOffice oder einem Microsoft Word Dokument. Die Hilfe bietet weitere Informationen.

  • Über den Browser können die Daten dann gedruckt werden.
    Hinweis: Achten Sie darauf nur den Rahmen auf der rechten Seite zu drucken.

  • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Anforderungen definieren"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    Sie können Anforderungsspezifikations Dokumente verwalten.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Anforderungen definieren"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    Sie können Anforderungsspezifikations Dokumente verwalten.

    Anforderungspezifikation

    -

    Anforderungen sind nach dem Dokument 'Anforderungspezifikation', welches mit dem Test Plan -verbunden ist, gruppiert.
    TestLink unterstützt noch keine Versionen für beide, die Anf.Spezifikation und +

    Anforderungen sind nach dem Dokument 'Anforderungspezifikation', welches mit dem Test Plan +verbunden ist, gruppiert.
    TestLink unterstützt noch keine Versionen für beide, die Anf.Spezifikation und der Anforderung selbst. Also sollte die Version des Dokuments erst nach der Spezifikation eingefügt werden. Titel. -Der Nutzer kann in dem Feld Inhalt eine kurze Beschreibung oder Notiz hinzufügen.

    +Der Nutzer kann in dem Feld Inhalt eine kurze Beschreibung oder Notiz hinzufügen.

    -

    Überschriebene Anzahl von Anf. Dient der Evaluation der Anf. -Abdeckung, falls nicht alle Anforderungen importiert wurden. -Der Wert 0 bedeutet, dass die aktuelle Anzahl der Anf. für Metriken genutzt wird.

    -

    Beispielsweise SRS beinhaltet 200 Anforderungen aber nur 50 sind in TestLink hinzugefügt worden. Die Test +

    Überschriebene Anzahl von Anf. Dient der Evaluation der Anf. +Abdeckung, falls nicht alle Anforderungen importiert wurden. +Der Wert 0 bedeutet, dass die aktuelle Anzahl der Anf. für Metriken genutzt wird.

    +

    Beispielsweise SRS beinhaltet 200 Anforderungen aber nur 50 sind in TestLink hinzugefügt worden. Die Test Abdeckung ist 25% (falls alle importierten Anforderungen getestet werden).

    Anforderungen

    -

    Klicken Sie auf den Namen einer vorhandenen Anforderungs Spezifikation. Falls keine existiert kann mit einem Klick auf den " . - "Projekt Knoten eine neue erstellt werden. Es können Anforderungen erstellt bearbeitet, gelöscht oder importiert werden. +

    Klicken Sie auf den Namen einer vorhandenen Anforderungs Spezifikation. Falls keine existiert kann mit einem Klick auf den " . + "Projekt Knoten eine neue erstellt werden. Es können Anforderungen erstellt bearbeitet, gelöscht oder importiert werden. Jede Anforderung hat einen Titel, Inhalt und Status. -Der Status sollte 'Normal' oder 'nicht testbar' sein. Nicht testbare Anforderungen gehen nicht in die Metriken -ein. Dieser Parameter kann für noch nicht implementierte Funktionen und falsch entworfene Anforderungen genutzt werden.

    +Der Status sollte 'Normal' oder 'nicht testbar' sein. Nicht testbare Anforderungen gehen nicht in die Metriken +ein. Dieser Parameter kann für noch nicht implementierte Funktionen und falsch entworfene Anforderungen genutzt werden.

    -

    Mit der Nutzung von Multi Aktionen mit abgehakten Anforderungen auf der Spezifikations Umgebung, -können neue Testfälle für Anforderungen erstellt werden. Diese Testfälle werden der Test Suite mit dem +

    Mit der Nutzung von Multi Aktionen mit abgehakten Anforderungen auf der Spezifikations Umgebung, +können neue Testfälle für Anforderungen erstellt werden. Diese Testfälle werden der Test Suite mit dem konfigurierten Namen (Standard ist: \$tlCfg->req_cfg->default_testsuite_name = 'Test Suite erstellt über Anforderung - Auto';) hinzugefügt. Titel und Inhalt -werden in die Testfälle kopiert.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Anforderungsspezifikation erstellen"; //printReq +werden in die Testfälle kopiert.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Anforderungsspezifikation erstellen"; // printReq $TLS_htmltext['printReqSpec'] = "

    Zweck:

    Hier können alle Anforderungen eines Test Projekts, einer Anforderungsspezifikation oder einzelne Anforderungen ausgedruckt werden.

    @@ -243,75 +231,74 @@

    Anweisung:

    1. -

      Wählen Sie die Teile einer Anforderung aus die Sie angezeigt haben wollen und klicken +

      Wählen Sie die Teile einer Anforderung aus die Sie angezeigt haben wollen und klicken dann auf eine Anforderungen, eine Anforderungsspezifikation oder ein Test Projekt. Eine druckbare Seitenansicht wird angezeigt.

    2. Ändern Sie über die \"Zeige als\" drop-box in der Navigationsleiste den Anzeigemodus als -HTML, OpenOffice Writer oder einem Microsoft Word Dokument. +HTML, OpenOffice Writer oder einem Microsoft Word Dokument. Die Hilfe bietet weitere Informationen.

    3. Über den Browser können die Daten dann gedruckt werden.
      Hinweis: Achten Sie bitte darauf nur den Rahmen auf der rechten Seite zu drucken.

    4. -
    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Stichwörter zuweisen"; -$TLS_htmltext['keywordsAssign'] = "

    Zweck:

    -

    Auf der Stichwort zuweisen Seite können Nutzer stapelweise Stichwörter den vorhandenen +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Stichwörter zuweisen"; +$TLS_htmltext['keywordsAssign'] = "

    Zweck:

    +

    Auf der Stichwort zuweisen Seite können Nutzer stapelweise Stichwörter den vorhandenen Testfällen und Suiten zuweisen.

    Um Stichwörter zuzuweisen:

    1. Wählen Sie zuerst links in der Baumansicht eine Test Suite oder ein Testfall aus.
    2. -
    3. Auf der rechten Seite erscheinen Boxen mit verfügbaren und - zugewiesenen Stichwörtern. Damit ist eine schnelle Zuweisung +
    4. Auf der rechten Seite erscheinen Boxen mit verfügbaren und + zugewiesenen Stichwörtern. Damit ist eine schnelle Zuweisung an einzelne Testfälle möglich.
    5. -
    6. Die Auswahl auf der unteren Seite erlaubt ein detailliertes Zuweisen +
    7. Die Auswahl auf der unteren Seite erlaubt ein detailliertes Zuweisen an einen Testfall.

    Wichtiger Hinweis zu Stichwort Zuweisungen bei Test Plänen:

    -

    Stichwort Zuweisungen, die in der Test Spezifikation gemacht werden, haben nur Auswirkungen +

    Stichwort Zuweisungen, die in der Test Spezifikation gemacht werden, haben nur Auswirkungen auf Testfälle des Test Plans und nur wenn der Test Plan die neuste Version des Testfalls -enthält. Wenn der Test Plan eine ältere Version eines Testfalls enthält, erscheinen die gemachten Zuweisungen nicht +enthält. Wenn der Test Plan eine ältere Version eines Testfalls enthält, erscheinen die gemachten Zuweisungen nicht in dem Test Plan.

    Das ist in TestLink so umgesetzt, damit ältere Testfall Versionen im Test Plan nicht von Stichwort -Zuweisungen neuerer Testfall Versionen betroffen sind. -Testfälle des Test Plans können durch ein klick auf Testfälle aktualisieren aktualisiert werden. Dies -sollte möglichst vor dem Zuweisen von Stichwörtern geschehen.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Testfälle ausführen"; -$TLS_htmltext['executeTest'] = "

    Zweck:

    - -

    Erlaubt es dem Nutzer Testfälle auszuführen. Der Nutzer kann das Testfall Ergebnis +Zuweisungen neuerer Testfall Versionen betroffen sind. +Testfälle des Test Plans können durch ein klick auf Testfälle aktualisieren aktualisiert werden. Dies +sollte möglichst vor dem Zuweisen von Stichwörtern geschehen.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Testfälle ausführen"; +$TLS_htmltext['executeTest'] = "

    Zweck:

    + +

    Erlaubt es dem Nutzer Testfälle auszuführen. Der Nutzer kann das Testfall Ergebnis für ein bestimmtes Build zuweisen. In der Hilfe stehen weitere Informationen über Filter und -Einstellungen. " . - "(Klicken Sie bitte auf das Fragezeichen Symbol).

    +Einstellungen. " . + "(Klicken Sie bitte auf das Fragezeichen Symbol).

    Anweisung:

    1. Es muss vorher für den Test Plan ein Build definiert sein.
    2. Wählen Sie bitte ein Build über die Drop-Down Box aus.
    3. Über Filter-Optionen können Sie die Baumansicht auf einige wenige - Testfälle reduzieren. Änderungen der Filter-Optionen müssen über - die Schaltfläche 'Anwenden' gespeichert werden.
    4. + Testfälle reduzieren. Änderungen der Filter-Optionen müssen über + die Schaltfläche 'Anwenden' gespeichert werden.
    5. Wählen Sie, durch einen Klick, einen Testfall aus der Baumansicht.
    6. Tragen Sie das Testergebnis und ggf. Notizen oder BUGs ein.
    7. Speichern
    -

    Hinweis: Sie können Problem-Reports direkt über die Oberfläche erstellen/verfolgen möchten. Dazu muss TestLink -vorher konfiguriert werden, damit es mit Ihrem BUG-Tracker arbeitet.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Testberichte und Metriken"; -$TLS_htmltext['showMetrics'] = "

    Reports sind mit dem Test Plan verbunden" . - "(definiert in dem oberen Navigator). Dieser Test Plan kann sich vom aktuellen Test Plan +

    Hinweis: Sie können Problem-Reports direkt über die Oberfläche erstellen/verfolgen möchten. Dazu muss TestLink +vorher konfiguriert werden, damit es mit Ihrem BUG-Tracker arbeitet.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Testberichte und Metriken"; +$TLS_htmltext['showMetrics'] = "

    Reports sind mit dem Test Plan verbunden" . + "(definiert in dem oberen Navigator). Dieser Test Plan kann sich vom aktuellen Test Plan der Ausführung unterscheiden. Sie können ebenfalls ein Reportformat auswählen:

      @@ -330,7 +317,7 @@

      In dem Dokument 'Test Plan' können Inhalt und Dokumentenstruktur definiert werden.

      Test Report

      -

      In dem Dokument 'Test Report' können Inhalt und Dokumentenstruktur definiert werden. +

      In dem Dokument 'Test Report' können Inhalt und Dokumentenstruktur definiert werden. Testfälle mit den Ergebnissen sind mit eingeschlossen.

      Allgemeine Test Plan Metriken

      @@ -346,16 +333,16 @@
    • Wenn ein Testfall mehrmals auf dem selben Build ausgeführt wird, so hat die letzte Ausführung vorrang. Wenn bspw. das Build 3 dem Team und Tester freigegeben wird und Tester 1 es um 14 Uhr als 'OK' markiert und Tester 2 um 15 Uhr als 'Fehlgeschlagen' markiert, so erscheint das Ergebnis als 'Fehlgeschlagen'.
    • -
    • Testfälle die als 'nicht getestet' markiert sind werden nicht berücksichtigt. Wenn z.B ein Testfall in +
    • Testfälle die als 'nicht getestet' markiert sind werden nicht berücksichtigt. Wenn z.B ein Testfall in Build 1 als 'OK' markiert wird und in Build 2 nicht ausgeführt wird, so wird das zuletzt markierte Ergebnis 'OK' übernommen.

    Die folgenden Tabellen werden gezeigt:

    • Ergebnis nach Top-Level Test Suiten - Listet die Ergebnisse von Suites höchster Ebene. Es werden aufgelistet: Alle Testfälle, 'OK', 'Fehlgeschlagen', + Listet die Ergebnisse von Suites höchster Ebene. Es werden aufgelistet: Alle Testfälle, 'OK', 'Fehlgeschlagen', 'Blockiert', 'nicht getestet' und (x-Prozent) vollständig. Vollständige Testfälle sind Testfälle die als 'OK', - 'Fehlgeschlagen' oder 'Blockiert' markiert wurden. + 'Fehlgeschlagen' oder 'Blockiert' markiert wurden. Ergebnisse von Suites höchster Ebener beinhalten alle erbenden Kind-Suites.
    • Ergebnisse nach Schlüsselwörter Listet alle Schlüsselwörter und zugehörigen Ergebnisse des aktuellen Test Plans, @@ -368,40 +355,40 @@

      Der gesamte Build Status

      Listet die Ergebnisse der Ausführung aller Builds. Jeweils für jedes Build: die Anzahl aller Testfälle, Anzahl aller Testfälle mit 'OK', % 'OK', Anzahl aller Testfälle mit 'Fehlgeschlagen', % 'Fehlgeschlagen', - % 'Blockiert' , 'nicht getestet' und % 'nicht getestet'. Wenn ein Testfall mehrmals auf dem selben Build + % 'Blockiert' , 'nicht getestet' und % 'nicht getestet'. Wenn ein Testfall mehrmals auf dem selben Build ausgeführt wurde, so wird das zuletzt markierte Ergebnis übernommen.

      Query Metriken

      Dieser Report besteht aus einer Query Form Seite und einer Query Ergebnis Seite, die die befragten Daten enthält. Die Query Form Seite ist eine Query Seite mit vier Bedienelementen. Jedem Bedienelement ist ein Standardwert -zugeordnet, der die Anzahl an angefragt Testfällen und Builds maximiert. Die Änderung der +zugeordnet, der die Anzahl an angefragt Testfällen und Builds maximiert. Die Änderung der Bedienelemnte erlaubt es dem Nutzer die Ergebnisse zu filtern und spezifische Reports für bestimmte Besitzer, Stichwörter, Suiten und Build Kombinationen zu generieren.

        -
      • Stichwörter 0->1 Stichwörter können ausgewählt werden. Standardmäßig sind keine Stichwörter ausgewählt. +
      • Stichwörter 0->1 Stichwörter können ausgewählt werden. Standardmäßig sind keine Stichwörter ausgewählt. Ist ein Stichwort nicht ausgewählt, werden alle Testfälle unabhängig von Stichwort Zuweisungen berücksichtigt. Stichwörter sind in der Test Spezifikation oder Stichwort Verwaltung zugeordnet. Stichwort Zuweisungen bei Testfällen -umfassen alle Test Pläne und alle Testfall-Versionen. Wenn Sie an Testergebnissen für ein bestimmtes Stichwort interessiert sind, +umfassen alle Test Pläne und alle Testfall-Versionen. Wenn Sie an Testergebnissen für ein bestimmtes Stichwort interessiert sind, dann können Sie dieses Bedienelement ändern.
      • -
      • Besitzer 0->1 Besitzer können ausgewählt werden. Standardmäßig sind keine Besitzer ausgewählt. +
      • Besitzer 0->1 Besitzer können ausgewählt werden. Standardmäßig sind keine Besitzer ausgewählt. Ist kein Besitzer ausgewählt, werden alle Testfälle unabhängig von Besitzer Zuweisungen berücksichtigt. -Zurzeit gibt es keine Funktion um nach nicht zugewiesenen Testfällen zu suchen. Besitzern werden Testfälle per Test Plan -über die 'Testfälle an Benutzer zuweisen' Seite zugewiesen. Wenn Sie an Testergebnissen eines bestimmtes Testers interessiert sind, +Zurzeit gibt es keine Funktion um nach nicht zugewiesenen Testfällen zu suchen. Besitzern werden Testfälle per Test Plan +über die 'Testfälle an Benutzer zuweisen' Seite zugewiesen. Wenn Sie an Testergebnissen eines bestimmtes Testers interessiert sind, dann können Sie dieses Bedienelement ändern.
      • Top-Level Suite 0->n Suiten höchster Ebene können ausgewählt werden. Standardmäßig sind alle Suiten -ausgewählt. Nur Suiten die selektiert sind, werden für die Ergebnis-Metriken abgefragt. Wenn Sie nur an einer +ausgewählt. Nur Suiten die selektiert sind, werden für die Ergebnis-Metriken abgefragt. Wenn Sie nur an einer bestimmten Suite interessiert sind, dann sollte Sie dieses Bedienelement geändert werden.
      • -
      • Builds 1->n Builds können ausgewählt werden. Standardmäßig sind alle ausgewählt. Für Metriken werden nur -die selektierten Ausführungen eines Builds berücksichtigt. -Wenn Sie bspw. wissen möchten wieviele Testfälle auf den letzten 3 Builds ausgeführt wurden, sollte dieses -Bedienelement geändert werden. -Die Auswahl der Stichwörter, Besitzer und Top Level Suite schreibt die Anzahl der Testfälle eines Test Plans vor, +
      • Builds 1->n Builds können ausgewählt werden. Standardmäßig sind alle ausgewählt. Für Metriken werden nur +die selektierten Ausführungen eines Builds berücksichtigt. +Wenn Sie bspw. wissen möchten wieviele Testfälle auf den letzten 3 Builds ausgeführt wurden, sollte dieses +Bedienelement geändert werden. +Die Auswahl der Stichwörter, Besitzer und Top Level Suite schreibt die Anzahl der Testfälle eines Test Plans vor, womit die Metrik per Suite oder Metrik per Test Plan berechnet werden. Wenn z.B der Besitzer 'Greg', Stichwort='Priorität 1', und alle wählbaren Test Suiten ausgewählt sind, werden nur Priorität 1 Testfälle, die Greg zugewiesen sind berücksichtigt. Die Anzahl der Testfälle im Test Report werden durch diese 3 Bedienelemente -beeinflusst. -Die Auswahl der Builds beeinflusst ob ein Testfall als 'OK', 'Fehlgeschlagen', 'Blockiert', oder 'nicht getestet' gilt. +beeinflusst. +Die Auswahl der Builds beeinflusst ob ein Testfall als 'OK', 'Fehlgeschlagen', 'Blockiert', oder 'nicht getestet' gilt. Bitte sehen Sie nach unter 'Letztes Test Ergebnis'.

      Klicken Sie das Element 'Abschicken' an um mit der Anfrage fortzufahren und die Ausgabe zu betrachten.

      @@ -412,20 +399,20 @@
    • Summe für den gesamten Test Plan
    • ein per Test Suite Abbau aller (Summe / bestandenen / fehlgeschlagenen / blockierten / nicht getesten) und aller Ausführungen. Wenn eine Testfall auf mehreren Builds ausgeführt wurde werden alle Ausführungen der -ausgewählten Builds angezeigt. Allerdings, die Zusammenfassung der Suite beinhaltet nur das Letzte Testergebnis +ausgewählten Builds angezeigt. Allerdings, die Zusammenfassung der Suite beinhaltet nur das Letzte Testergebnis des ausgewählten Builds.
    • Blockierte, fehlgeschlagene und nicht getestete Testfall Reports

      -

      Diese Reports zeigen alle zurzeit blockierten, fehlerhaften oder nicht getesteten Testfälle an. Die -'Letztes Test Ergebnis' Logik (welches oben unter 'Allgemeine Test Plan Metriken' beschrieben ist) -wird angewendet um zu bestimmen ob ein Testfall als 'Fehlgeschlagen', 'Blockiert', oder 'nicht getestet' -betrachtet werden soll. Testergebnisse von fehlgeschlagenen und blockierten Testfällen zeigen +

      Diese Reports zeigen alle zurzeit blockierten, fehlerhaften oder nicht getesteten Testfälle an. Die +'Letztes Test Ergebnis' Logik (welches oben unter 'Allgemeine Test Plan Metriken' beschrieben ist) +wird angewendet um zu bestimmen ob ein Testfall als 'Fehlgeschlagen', 'Blockiert', oder 'nicht getestet' +betrachtet werden soll. Testergebnisse von fehlgeschlagenen und blockierten Testfällen zeigen die zugehörigen BUGs an, falls ein BUG Tracking System genutzt wird.

      Test Reports

      -

      Zeigt den Status von allen Testfällen der Builds. Wenn mehrere Testfälle auf dem selben Build ausgeführt -wurde dann wird das aktuellste Test Ergebnis angezeigt. Es wird empfohlen den Bericht in das Excel-Format +

      Zeigt den Status von allen Testfällen der Builds. Wenn mehrere Testfälle auf dem selben Build ausgeführt +wurde dann wird das aktuellste Test Ergebnis angezeigt. Es wird empfohlen den Bericht in das Excel-Format zu exportieren, um bei großen Datenmengen die Daten einfacher durchzusehen.

      Charts - Allgemeine Test Plan Metriken

      @@ -441,27 +428,26 @@ bestandenen, fehlgeschlagenen, blockierten, nicht getesteten Testfällen erkennen kann.

      Gesamtanzahl der Bugs für jeden Testfall

      -

      Dieser Bericht zeigt den jeden Testfall mit allen für das ganze Projekt geordneten BUGs. Dieser -Bericht ist nur verfügbar wenn ein BUG Tracking System verbunden ist.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Testfälle hinzufügen / entfernen"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      Zweck:

      +

      Dieser Bericht zeigt den jeden Testfall mit allen für das ganze Projekt geordneten BUGs. Dieser +Bericht ist nur verfügbar wenn ein BUG Tracking System verbunden ist.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Testfälle hinzufügen / entfernen"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      Zweck:

      Nutzer mit entsprechenden Rechten können dem Testplan Testfälle hinzufügen oder Testfälle aus dem Testplan entfernen.

      Anweisung:

        -
      1. Wählen Sie im Baum auf der linken Seite eine Test Suite aus, um alle in der Test Suite +
      2. Wählen Sie im Baum auf der linken Seite eine Test Suite aus, um alle in der Test Suite enthaltenen Testfälle angezeigt zu bekommen.
      3. Wählen Sie alle Testfälle, die Sie hinzufügen bzw. entfernen wollen und klicken Sie auf den \"Hinzufügen / Entfernen der ausgewählten Testfälle\" Button
      4. -
      "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Testfälle an Benutzer zuweisen"; -$TLS_htmltext['tc_exec_assignment'] = "

      Zweck:

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Testfälle an Benutzer zuweisen"; +$TLS_htmltext['tc_exec_assignment'] = "

      Zweck:

      Diese Seite erlaubt es Leitern den Nutzern bestimmte Tests im Test Plan zuzuweisen.

      Anweisung:

      @@ -469,7 +455,7 @@
    • Wählen Sie eine zu testende Test Suite oder Testfall aus.
    • Wählen Sie einen Tester aus.
    • Über die Schaltfläche 'Speichern' wird die Zuweisung übernommen.
    • -
    • Öffnen Sie die Ausführungs-Seite um die Zuweisung zu verifizieren. Es ist möglich nach +
    • Öffnen Sie die Ausführungs-Seite um die Zuweisung zu verifizieren. Es ist möglich nach Nutzern zu Filtern.
    • @@ -479,16 +465,16 @@
    • Sind Testfälle zugewiesen, erscheint eine Schaltfläche worüber die Zuweisung der Testfällen entzogen werden kann. Nach einem Klick auf die Schaltfläche sind alle Testfälle nicht mehr zugewiesen.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Verlinkte Testfälle aktualisieren"; -$TLS_htmltext['planUpdateTC'] = "

      Zweck

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Verlinkte Testfälle aktualisieren"; +$TLS_htmltext['planUpdateTC'] = "

      Zweck

      Diese Seite erlaubt das Aktualisieren von Testfällen auf eine neue (andere) Version, wenn die Test Spezifikation -sich geändert hat. Oft klären sich Funktionalitäten während dem Testen." . - " Der Nutzer ändert die Test Spezifikation, jedoch müssen Änderungen im Test Plans übernommen werden. Andernfalls" . - " wird die originale Version im Test Plan behalten, um den richtigen Bezug der Testergebnisse auf den korrekten ". - "Text eines Testfalls sicherzustellen.

      +sich geändert hat. Oft klären sich Funktionalitäten während dem Testen." . + " Der Nutzer ändert die Test Spezifikation, jedoch müssen Änderungen im Test Plans übernommen werden. Andernfalls" . + " wird die originale Version im Test Plan behalten, um den richtigen Bezug der Testergebnisse auf den korrekten " . + "Text eines Testfalls sicherzustellen.

      Anweisung:

        @@ -496,13 +482,13 @@
      1. Wählen Sie die neue Version des bestimmten Testfalls über die Kombo-Box aus.
      2. Klicken Sie auf 'Aktualisiere Test Plan' um die Änderungen zu übernehmen.
      3. Um zu prüfen: Öffnen Sie die Ausführungs-Seite, um die Texte der Testfälle zu betrachten.
      4. -
      "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Dringlichkeit der Tests bestimmen"; -$TLS_htmltext['test_urgency'] = "

      Zweck:

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Dringlichkeit der Tests bestimmen"; +$TLS_htmltext['test_urgency'] = "

      Zweck:

      Um die Testprioritäten von Testfällen vorzugeben, ist es in TestLink möglich die Dringlichkeit einer Test Suite zu setzen. - Die Testpriorität hängt sowohl von der Wichtigkeit der Testfälle als auch von der im Test Plan definierten + Die Testpriorität hängt sowohl von der Wichtigkeit der Testfälle als auch von der im Test Plan definierten Dringlichkeit ab. Der Test Leiter sollte einen Satz von Testfällen spezifizieren, die als erstes getestet werden können. Das hilft sicherzustellen, dass auch beim Testen unter Zeitdruck die wichtigsten Tests berücksichtigt werden.

      @@ -512,15 +498,15 @@
    • Wählen Sie im Navigator auf der linken Fensterseite eine Test Suite aus, um die Dringlichkeit eines Produkts/Bauteilmerkmals zu setzen.
    • Wählen Sie ein Dringlichkeits Niveau (hoch, mittel oder niedrig) aus. Mittel ist der Standardwert. - Sie können die Priorität für unberührte Teile Produkts vermindern und für Bauteile mit + Sie können die Priorität für unberührte Teile Produkts vermindern und für Bauteile mit signifikanten Änderungen steigern.
    • Klicken Sie auf 'Speichern', um die Änderungen zu übernehmen.
    • -

      Zum Beispiel: Ein Testfall mit einer hohen Wichtigkeit in einer Test Suite mit niedriger Dringlichkeit " . - "bekommt mittlere Priorität."; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTcDocumentation'] = "Plan add testcase documentation"; -$TLS_htmltext['planAddTcDocumentation'] = "

      @TODO Plan add testcase documentation

      "; - +

      Zum Beispiel: Ein Testfall mit einer hohen Wichtigkeit in einer Test Suite mit niedriger Dringlichkeit " . + "bekommt mittlere Priorität."; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTcDocumentation'] = "Plan add testcase documentation"; +$TLS_htmltext['planAddTcDocumentation'] = "

      @TODO Plan add testcase documentation

      "; + ?> diff --git a/locale/en_GB/description.php b/locale/en_GB/description.php index 5f93947a46..ce894d4bad 100644 --- a/locale/en_GB/description.php +++ b/locale/en_GB/description.php @@ -1,34 +1,33 @@ -Options for a generated document

      This table allows the user to filter test cases before they are viewed. If @@ -36,8 +35,8 @@ presented, check or uncheck, click on Filter, and select the desired data level from the tree.

      -

      Document Header: Users can filter out Document Header information. -Document Header information includes: Introduction, Scope, References, +

      Document Header: Users can filter out Document Header information. +Document Header information includes: Introduction, Scope, References, Test Methodology, and Test Limitations.

      Test Case Body: Users can filter out Test Case Body information. Test Case Body information @@ -52,35 +51,35 @@

      Table of Content: TestLink inserts list of all titles with internal hypertext links if checked.

      -

      Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component -in second case.

      "; - -// testPlan.html +

      Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component +in second case.

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      Test Plan

      General

      -

      A test plan is a systematic approach to testing a system such as software. You can organize testing activity with +

      A test plan is a systematic approach to testing a system such as software. You can organize testing activity with particular builds of product in time and trace results.

      Test Execution

      -

      This section is where users can execute test cases (write test results) and -print Test case suite of the Test Plan. This section is where users can track -the results of their test case execution.

      +

      This section is where users can execute test cases (write test results) and +print Test case suite of the Test Plan. This section is where users can track +the results of their test case execution.

      Test Plan Management

      -

      This section, which is only lead accessible, allows users to administrate test plans. -Administering test plans involves creating/editing/deleting plans, -adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can +

      This section, which is only lead accessible, allows users to administrate test plans. +Administering test plans involves creating/editing/deleting plans, +adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can see which plan.
      -Users with lead permissions may also set the priority/risk and ownership of -Test case suites (categories) and create testing milestones.

      - -

      Note: It is possible that users may not see a dropdown containing any Test plans. -In this situation all links (except lead enabled ones) will be unlinked. If you -are in this situation you must contact a lead or admin to grant you the proper -project rights or create a Test Plan for you.

      "; - -// custom_fields.html +Users with lead permissions may also set the priority/risk and ownership of +Test case suites (categories) and create testing milestones.

      + +

      Note: It is possible that users may not see a dropdown containing any Test plans. +In this situation all links (except lead enabled ones) will be unlinked. If you +are in this situation you must contact a lead or admin to grant you the proper +project rights or create a Test Plan for you.

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      Custom Fields

      Following are some facts about the implementation of custom fields:

        @@ -99,7 +98,7 @@
      • Caption variable name (eg: This is the value that is supplied to lang_get() API , or displayed as-is if not found in language file).
      • Custom field type (string, numeric, float, enum, email)
      • -
      • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list +
      • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list and combo types.
        Use the pipe ('|') character to separate possible values for an enumeration. One of the possible values @@ -119,16 +118,16 @@
      • Enable on test plan design. User can change the value during Test Plan design (add test cases to test plan)
      • Available for. User choose to what kind of item the field belows.
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      Executing Test Cases

      Allows users to 'execute' test cases. Execution itself is merely assigning a test case a result (pass,fail,blocked) against a selected build.

      Access to a bug tracking system could be configured. User can directly add a new bugs -and browse existing ones then. See Installation manual for more.

      "; - -//bug_add.html +and browse existing ones then. See Installation manual for more.

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      Add Bugs to Test Case

      (only if it is configured) TestLink has a very simple integration with Bug Tracking Systems (BTS), @@ -138,7 +137,7 @@

    • Insert new bug.
    • Display existent bug info.
    -

    +

    Process to add a bug

    @@ -147,12 +146,12 @@

  • Step 2: write down the BUGID assigned by BTS.
  • Step 3: write BUGID on the input field.
  • Step 4: use add bug button.
  • - + After closing the add bug page, you will see relevant bug data on the execute page. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Settings

    Settings allows you to select the test plan, build and platform (if available) to @@ -178,77 +177,74 @@

    Keyword Filter

    -

    You can filter test cases by the keywords that have been assigned. You can choose " . -"multiple keywords by using CTRL-Click. If you chose more than one keyword you can " . -"decide whether only test cases are shown that have all chosen keywords assigned " . -"(radiobutton \"And\") or at least one of the chosen keywords (radiobutton \"Or\").

    +

    You can filter test cases by the keywords that have been assigned. You can choose " . + "multiple keywords by using CTRL-Click. If you chose more than one keyword you can " . + "decide whether only test cases are shown that have all chosen keywords assigned " . + "(radiobutton \"And\") or at least one of the chosen keywords (radiobutton \"Or\").

    Priority Filter

    -

    You can filter test cases by test priority. The test priority is \"test case importance\" " . -"combined with \"test urgency\" within the current test plan.

    +

    You can filter test cases by test priority. The test priority is \"test case importance\" " . + "combined with \"test urgency\" within the current test plan.

    User Filter

    -

    You can filter test cases that are not assigned (\"Nobody\") or assigned to \"Somebody\". " . -"You can also filter test cases that are assigned to a specific tester. If you chose a specific " . -"tester you also have the possibility to show test cases that are unassigned in addition to " . -"those test cases (advanced Filters are available).

    +

    You can filter test cases that are not assigned (\"Nobody\") or assigned to \"Somebody\". " . + "You can also filter test cases that are assigned to a specific tester. If you chose a specific " . + "tester you also have the possibility to show test cases that are unassigned in addition to " . + "those test cases (advanced Filters are available).

    Result Filter

    -

    You can filter test cases by result (advanced Filters are available). You can filter by " . -"result \"on chosen build for execution\", \"on latest execution\", \"on ALL builds\", " . -"\"on ANY build\" and \"on specific build\". If \"specific build\" is chosen you then can " . -"specify the build.

    "; - - -// newest_tcversions.html +

    You can filter test cases by result (advanced Filters are available). You can filter by " . + "result \"on chosen build for execution\", \"on latest execution\", \"on ALL builds\", " . + "\"on ANY build\" and \"on specific build\". If \"specific build\" is chosen you then can " . + "specify the build.

    "; + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Newest versions of linked Test Cases

    The whole set of Test Cases linked to Test Plan is analyzed, and a list of Test Cases which have a newest version is displayed (against the current set of the Test Plan). -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Requirements Coverage


    This feature allows to map a coverage of user or system requirements by test cases. Navigate via link \"Requirement Specification\" in main screen.

    Requirements Specification

    -

    Requirements are grouped by 'Requirements Specification' document which is related to -Test Project.
    TestLink doesn't support versions for both Requirements Specification -and Requirements itself. So, version of document should be added after +

    Requirements are grouped by 'Requirements Specification' document which is related to +Test Project.
    TestLink doesn't support versions for both Requirements Specification +and Requirements itself. So, version of document should be added after a Specification Title. -An user can add simple description or notes to Scope field.

    +An user can add simple description or notes to Scope field.

    -

    Overwritten count of REQs serves for -evaluation Req. coverage in case that not all requirements are added (imported) in. -The value 0 means that current count of requirements is used for metrics.

    -

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test +

    Overwritten count of REQs serves for +evaluation Req. coverage in case that not all requirements are added (imported) in. +The value 0 means that current count of requirements is used for metrics.

    +

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test coverage is 25% (if all these added requirements will be tested).

    Requirements

    Click on title of a created Requirements Specification. You can create, edit, delete or import requirements for the document. Each requirement has title, scope and status. Status should be \"Normal\" or \"Not testable\". Not testable requirements are not counted -to metrics. This parameter should be used for both unimplemented features and -wrong designed requirements.

    +to metrics. This parameter should be used for both unimplemented features and +wrong designed requirements.

    -

    You can create new test cases for requirements by using multi action with checked +

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite -with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = +with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Title and Scope are copied to these Test cases.

    -"; - +"; + $TLS_hlp_req_coverage_table = "

    Coverage:

    -A value of e.g. \"40% (8/20)\" means that 20 Test Cases have to be created for this Requirement -to test it completely. 8 of those have already been created and linked to this Requirement, which +A value of e.g. \"40% (8/20)\" means that 20 Test Cases have to be created for this Requirement +to test it completely. 8 of those have already been created and linked to this Requirement, which makes a coverage of 40 percent. -"; - - -// req_edit +"; + +// req_edit $TLS_hlp_req_edit = "

    Internal links on scope:

    -

    Internal links serve the purpose of creating links to other requirements/requirement specifications +

    Internal links serve the purpose of creating links to other requirements/requirement specifications with a special syntax. Internal Links behaviour can be changed in the config file.

    Usage: @@ -256,7 +252,7 @@ Link to requirements: [req]req_doc_id[/req]
    Link to requirement specifications: [req_spec]req_spec_doc_id[/req_spec]

    -

    The test project of the requirement / requirement specification, a version and an anchor +

    The test project of the requirement / requirement specification, a version and an anchor to jump to can also be specified:
    [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
    This syntax also works for requirement specifications (version attribute has no effect).
    @@ -264,12 +260,11 @@

    Log message for changes:

    Whenever a change is made Testlink will ask for a log message. This log message served the purpose of traceability. -If only the scope of the requirement has changed you are free to decide whether to create a new revision or not. +If only the scope of the requirement has changed you are free to decide whether to create a new revision or not. Whenever anything besides the scope is changed you are forced to create a new revision.

    -"; - - -// req_view +"; + +// req_view $TLS_hlp_req_view = "

    Direct Links:

    To easily share this document with others simply click the globe icon at the top of this document to create a direct link.

    @@ -281,17 +276,16 @@

    Shows all linked test cases for this requirement.

    Relations:

    -

    Requirement Relations are used to model relationships between requirements. -Custom relations and the option to allow relations between requirements of +

    Requirement Relations are used to model relationships between requirements. +Custom relations and the option to allow relations between requirements of different test projects can be configured on the config file. -If you set the relation \"Requirement A is parent of Requirement B\", +If you set the relation \"Requirement A is parent of Requirement B\", Testlink will set the relation \"Requirement B is child of Requirement A\" implicitly.

    -"; - - -// req_spec_edit +"; + +// req_spec_edit $TLS_hlp_req_spec_edit = "

    Internal links on scope:

    -

    Internal links serve the purpose of creating links to other requirements/requirement specifications +

    Internal links serve the purpose of creating links to other requirements/requirement specifications with a special syntax. Internal Links behaviour can be changed in the config file.

    Usage: @@ -299,37 +293,34 @@ Link to requirements: [req]req_doc_id[/req]
    Link to requirement specifications: [req_spec]req_spec_doc_id[/req_spec]

    -

    The test project of the requirement / requirement specification, a version and an anchor +

    The test project of the requirement / requirement specification, a version and an anchor to jump to can also be specified:
    [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
    This syntax also works for requirement specifications (version attribute has no effect).
    If you do not specify a version the whole requirement including all versions will be shown.

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Regarding 'Save Custom Fields'

    -If you have defined and assigned to Test Project,
    +If you have defined and assigned to Test Project,
    Custom Fields with:
    'Display on test plan design=true' and
    'Enable on test plan design=true'
    you will see these in this page ONLY for Test Cases linked to Test Plan. -"; - - -// resultsByTesterPerBuild.tpl +"; + +// resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = "More information about testers:
    If you click on a tester name in this table, you will get a more detailed overview about all Test Cases assigned to that user and his testing progress.

    Note:
    -This Report shows those test cases, which are assigned to a specific user and have been executed -based on each active build. Even if a test case has been executed by another user than the assigned user, +This Report shows those test cases, which are assigned to a specific user and have been executed +based on each active build. Even if a test case has been executed by another user than the assigned user, the test case will appear as executed for the assigned user. -"; - - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/en_GB/strings.txt b/locale/en_GB/strings.txt index 606a4d6c67..b230d0ec64 100644 --- a/locale/en_GB/strings.txt +++ b/locale/en_GB/strings.txt @@ -39,6 +39,7 @@ $TLS_last_update = "English is the default development language and is always up $TLS_active_click_to_change = 'Active (click to set inactive)'; $TLS_api_key = 'API Key'; $TLS_builds = 'Builds'; +$TLS_build = 'Build'; $TLS_active_builds = 'Active Builds'; $TLS_all_active_builds = '[All Active Builds]'; $TLS_bad_line_skipped = 'Line has been skipped (unable to import it)'; @@ -68,6 +69,7 @@ $TLS_asc = "Ascending"; $TLS_any = "Any"; $TLS_all = "All"; +$TLS_any_bracketed = "[Any]"; $TLS_alt_delete = "delete"; $TLS_assigned_by = "Assigned by"; $TLS_attribute = "Attribute"; @@ -94,6 +96,7 @@ $TLS_edited_by = "Edited by"; $TLS_days = "days"; $TLS_desc = "Descending"; $TLS_description = "Description"; +$TLS_delete_confirm_question = "Are you sure you want to delete"; $TLS_doc_id = "Document ID"; $TLS_doc_id_short = "Doc ID"; $TLS_destination_top = "Destination position top"; @@ -124,6 +127,7 @@ $TLS_importance = "Importance"; $TLS_imported = "Imported"; $TLS_important_notice = "Important Notice"; $TLS_its_duedate_with_separator = "Due date: "; +$TLS_hint_mail_for_tester = "Additional text to be sent in the notification email"; $TLS_hint_like_search_on_name = 'Search wil be done on NAME in LIKE %value%'; $TLS_keyword = "Keyword"; @@ -180,6 +184,7 @@ $TLS_req_specification = "Req. Specification"; $TLS_revision = "revision"; $TLS_revision_short = "rev"; $TLS_revision_tag = "[r%s]"; +$TLS_version_tag = "[v%s]"; $TLS_version_revision_tag = "[v%sr%s]"; $TLS_srs = "SRS"; @@ -217,6 +222,8 @@ $TLS_max_size_file_msg = "Max. allowed file size: %s KB"; $TLS_due_since = "Assigned Since (days)"; $TLS_parent_child = 'is Parent or is Child'; $TLS_blocks_depends = 'Blocks or Depends'; +$TLS_automation_script = 'Automates or is Automated By (Depends)'; + $TLS_unknown_code = 'unknown code: %s'; $TLS_in_percent = "[%]"; $TLS_user_has_no_right_for_action = 'User has not needed right to do requested action'; @@ -267,6 +274,7 @@ $TLS_update_hint = "check to update value when saving"; // Buttons (used wide) $TLS_btn_apply = "Apply"; $TLS_btn_add = "Add"; +$TLS_btn_addAndLink = "Add and Write in Issue Tracker"; $TLS_btn_add_to_testsuites_deep = "Add also to children (Test Suites)"; $TLS_btn_add_to_testplans = "Add to Test Plans"; $TLS_btn_assign = "Assign"; @@ -298,6 +306,7 @@ $TLS_btn_print = "Print"; $TLS_btn_print_view = "Print view"; $TLS_btn_reset_filter = "Reset Filter"; $TLS_btn_save = "Save"; +$TLS_btn_save_adding_tcv = "Save linking test cases"; $TLS_btn_save_and_exit = "Save & exit"; $TLS_btn_save_and_insert = "Save & insert"; $TLS_btn_search_filter = "Search/Filter"; @@ -376,6 +385,12 @@ $TLS_child_of = "is child of"; $TLS_blocks = "blocks"; $TLS_depends = "depends on"; $TLS_related_to = "is related to"; +$TLS_automates_also = "automates also"; +$TLS_is_automated_by = "is automated by"; +$TLS_executed_me_and_also = "execute me and also"; + + + $TLS_tcase_relation_hint = "PREFIX-ID"; $TLS_tcase_relation_help = "(if you are linking to a test on same test project, you can write ONLY the ID)"; @@ -867,7 +882,6 @@ $TLS_btn_hide_cf = "Hide Custom Fields"; $TLS_btn_show_cf = "Show Custom Fields"; $TLS_btn_export_testplan_tree = 'Export Test Plan'; $TLS_btn_export_testplan_tree_for_results = 'Export for results import'; - $TLS_expand_tree = "Expand tree"; $TLS_collapse_tree = "Collapse tree"; $TLS_filter_active_inactive = "Active/Inactive"; @@ -1175,7 +1189,9 @@ $TLS_del_script_warning_msg = "Really delete this script link from TestLink Data // gui/templates/reqSpecList.tpl +$TLS_btn_assign_tc = "Assign test case"; $TLS_no_docs = "No available documents."; +$TLS_req_list_docs = "List of documents"; @@ -1691,6 +1707,7 @@ $TLS_warning_editing_executed_tc = "Warning! This Test Case version has been exe $TLS_warning_unsaved = "You will lose any unsaved changes!"; $TLS_warning_estimated_execution_duration_format = "Estimated execution duration accepts only numeric or float values"; +$TLS_info_testautomation_done = "IMPORTANT INFO: there is a test script to execute this test automatically"; // ----- gui/templates/planOwner.tpl ----- @@ -1712,7 +1729,6 @@ $TLS_btn_new_version_from_latest = "Create New Version From Latest"; $TLS_btn_new_sibling = "New sibling"; $TLS_hint_new_sibling = "Create another test case under current Test Suite"; $TLS_hint_new_version = "Create a new version"; - $TLS_can_not_edit_tc = "You can not edit this version because it has been executed"; $TLS_can_not_edit_frozen_tc = "You can not edit this version because it has been frozen"; $TLS_can_not_delete_relation_frozen_tc = "You can not delete this relation : testcase has been frozen"; @@ -3148,10 +3164,12 @@ $TLS_API_UPDATER_INSUFFICIENT_RIGHTS = "User %s " . "has insufficient rights to perform this action" . " - Details: right %s, test project id: %s, test plan id: %s"; +$TLS_API_MUST_BE_ADMIN = "This function is restricted to site admin."; $TLS_API_NO_DEV_KEY = "No devKey provided."; $TLS_API_NO_TCASEID = "No Test Case ID (internal id) provided."; $TLS_API_NO_TCASEEXTERNALID = "No Test Case External ID provided."; +$TLS_API_NO_TCASEVERSIONID = "No Test Case Version ID provided."; $TLS_API_NO_TPLANID = "No test plan id provided."; $TLS_API_NO_TEST_MODE = "A test Mode must be provided!"; $TLS_API_NO_STATUS = "No status provided."; @@ -3161,15 +3179,21 @@ $TLS_API_NO_TESTSUITEID = "No testsuiteid provided."; $TLS_API_NO_TPID = "No testplan ID (testplanid) provided."; $TLS_API_NO_TCID = "No testcase ID (testcaseid) provided."; $TLS_API_NO_PLATFORMID = "No Platform ID (platformid) provided."; +$TLS_API_NO_USERID = "No user ID (userid) provided."; +$TLS_API_NO_ROLEID = "No role ID (roleid) provided."; $TLS_API_INVALID_TCASEID = "The Test Case ID (testcaseid: %s) provided does not exist!"; +$TLS_API_INVALID_TCASEVERSIONID = "The Test Case Version ID (testcaseversionid: %s) provided does not exist!"; $TLS_API_INVALID_BUILDID = "The Build ID (buildid: %s) provided does not exist!"; $TLS_API_INVALID_TPLANID = "The Test Plan ID (%s) provided does not exist!"; $TLS_API_INVALID_STATUS = "The status code (%s) provided is not valid!"; $TLS_API_INVALID_TESTCASE_EXTERNAL_ID = "Test Case External ID (%s) does not exist!"; +$TLS_API_INVALID_USERID = "The user ID (userid: %s) provided does not exist!"; +$TLS_API_INVALID_ROLEID = "The role ID (roleid: %s) provided does not exist!"; $TLS_API_TCASEID_NOT_INTEGER = "Test Case ID (testcaseid) must be an integer!"; +$TLS_API_TCASEVERSIONID_NOT_INTEGER = "Test Case Version ID (testcaseversionid) must be an integer!"; $TLS_API_TESTCASENAME_NOT_STRING = "testcasename must be a string!"; $TLS_API_NO_BUILD_FOR_TPLANID = "No builds exist for the test plan id provided! " . "A build must be created to report results."; @@ -3295,6 +3319,11 @@ $TLS_API_ITS_NOT_FOUND = "Unable to find the ITS %s."; $TLS_API_TSUITE_NOT_ON_TCASE_TPROJ = "Test Suite does not belong to Test Case's Test Project"; +$TLS_API_USER_LOGIN_DOESNOT_EXIST = "User login (login: %s) provided does not exist."; + +$TLS_API_ROLE_NAME_DOESNOT_EXIST = "Role name (role: %s) provided does not exist."; +$TLS_API_ROLE_SETTING_ERROR = "An error occured while setting a role (role: %s) to a user (userid: %s). Please consult log files."; + // ----- audit log entries ----- $TLS_audit_login_succeeded = "Login for '{%1}' from '{%2}' succeeded"; $TLS_audit_login_failed = "Login for '{%1}' from '{%2}' failed"; @@ -3731,6 +3760,8 @@ $TLS_issue_tplan = "Test Plan: %s"; $TLS_issue_build = " Build: %s"; $TLS_issue_platform = " Platform: %s"; $TLS_issue_exec_result = " Execution Status: %s"; +$TLS_tc_name = "TC name: %s"; +$TLS_tc_external_id = "TC ID: %s"; $TLS_issue_generated_description = "Execution ID: %s \n Tester: %s \n Test Plan: %s \n"; $TLS_issue_subject_projectname = "Test Project"; @@ -3750,10 +3781,19 @@ $TLS_fogbugz_bug_created = "FOGBUGZ Issue Created (summary:%s) on project:%s"; $TLS_youtrack_bug_created = "YOUTRACK Issue Created (summary:%s) on project with id:%s"; $TLS_mantis_bug_created = "MANTIS Issue %s - Created (summary:%s) on project with key:%s"; $TLS_bugzilla_bug_created = "BUGZILLA Issue Created (summary:%s) on product:%s"; + $TLS_gitlab_bug_created = "GITLAB Issue Created (summary:%s) on project with identifier:%s"; $TLS_gitlab_bug_comment = "GITLAB Commented Issue (summary:%s)"; + $TLS_kaiten_bug_created = "KAITEN Issue Created (summary:%s) on project with identifier:%s"; $TLS_kaiten_bug_comment = "KAITEN Commented Issue (summary:%s)"; + +$TLS_tuleap_bug_created = "TULEAP Issue Created (summary:%s) on project with identifier:%s"; +$TLS_tuleap_bug_comment = "TULEAP Commented Issue (summary:%s)"; + +$TLS_trello_bug_created = "TRELLO Card/Issue Created (summary:%s) on Board/List: %s/%s"; +$TLS_trello_bug_comment = "TRELLO Commented Card/Issue (summary:%s)"; + $TLS_bts_check_ok = "Connection is OK"; $TLS_bts_check_ko = "Connection is KO (detailed messages on TestLink Event Log), check configuration"; $TLS_check_bts_connection = "Check connection"; @@ -3815,7 +3855,7 @@ $TLS_demo_usage = "This is a DEMO SITE, use it with RESPECT.
    " . "If you find TestLink useful think about supporting our work
    "; -// reqSpecMoveOrCopy.tpl +// ----- reqSpecMoveOrCopy.tpl $TLS_remove_kw_msgbox_title = "Remove keyword"; @@ -3859,7 +3899,7 @@ $TLS_assignments = "Assignments:"; $TLS_btn_bulk_mon = 'Bulk Monitoring'; $TLS_bulk_monitoring = $TLS_btn_bulk_mon; $TLS_monitoring = 'Monitoring'; -$TLS_btn_toogle_mon = 'Toogle Monitoring'; +$TLS_btn_toogle_mon = 'Toggle Monitoring'; $TLS_btn_start_mon = 'Start Monitoring'; $TLS_btn_stop_mon = 'Stop Monitoring'; @@ -3890,7 +3930,7 @@ $TLS_search_words_on_attr = "Word/Words will be searched among one or more attri $TLS_search_other_attr = "You can set search criteria that does not use words. But This search criteria will be added on AND mode"; -$TLS_search_created_by_ph = 'value will be searched on login/firts name/last name'; +$TLS_search_created_by_ph = 'value will be searched on login/first name/last name'; $TLS_multiple_entities_search = 'Search on multiple entities'; $TLS_no_access_to_feature = 'You do not have enough rights to access the feature'; @@ -4000,12 +4040,22 @@ $TLS_info_resultsTCAbsoluteLatest_report = $TLS_resultsTCAbsoluteLatest; $TLS_resultsTCAbsoluteLatest_title = "Latest execution in Selected Platform computed over all builds"; -$TLS_FILE_UPLOAD_allowed_filenames_regexp = 'File Name does not pass REGEXP checkings'; +$TLS_FILE_UPLOAD_allowed_filenames_regexp = 'File Name does not pass REGEXP checkings
    %filename% '; $TLS_FILE_UPLOAD_empty_extension = 'Empty Extension is Forbidden'; -$TLS_FILE_UPLOAD_allowed_files = 'File does not pass allowed file type check'; +$TLS_FILE_UPLOAD_allowed_files = 'File does not pass allowed file type check
    %filename% '; + +$TLS_file_upload_ok = 'File Upload OK
    %filename%'; +$TLS_file_upload_step_exec_ok ='File Upload OK - Test Case Step: %step%
    %filename%'; +$TLS_file_upload_step_exec_ko ='File Upload Issue - Test Case Step: %step%'; + +$TLS_file_upload_ko = '

    File Upload Issue

    '; +$TLS_file_upload_tclevel_ok = '

    File Upload on Test Case Execution

    '; +$TLS_file_upload_steplevel_ok = '

    File Upload on Step Execution

    '; +$TLS_file_upload_steplevel_ko = '

    File Upload Issue on Step Execution

    '; + + -$TLS_file_upload_ko = 'File Upload Issue'; $TLS_pleaseOpenTSuite = 'Test Suite Details contain CRITIC information for ' . 'execution.
    Please open the section and ' . 'read it carefully.
    Thanks!'; @@ -4036,6 +4086,102 @@ $TLS_baseline_l1l2 = 'Baselines Level 1 & Level 2 Test Suites'; $TLS_baselineTimestamp = 'Baseline taken on:'; $TLS_on_design = 'On Design'; $TLS_on_exec = 'On Execution'; +$TLS_platform_open_for_exec = 'Open For Execution'; + $TLS_note_platform_filter = 'Test Cases are filtered by following platforms'; $TLS_testers_qty = 'Number of testers'; -// ----- END ------------------------------------------------------------------ +$TLS_menu_manage_aliens = 'Manage Alien References'; +$TLS_btn_create_alien = 'Create'; +$TLS_th_alien = 'name/code'; +$TLS_btn_assign_alien_to_tc = 'Assign to Test Cases'; +$TLS_link_type = 'Link Type'; +$TLS_realtime = 'Real time'; +$TLS_snapshot = 'Snapshot'; +$TLS_change_to_realtime = 'Snapshot (Click to change to Real time)'; +$TLS_change_to_snapshot = 'Real time (Click to change to Snapshot'; +$TLS_alien = 'Alien References'; +$TLS_aliens = 'Alien References'; +$TLS_select_aliens = 'Select Alien References'; + +$TLS_remove_platform_msgbox_title = "Remove Platform"; +$TLS_remove_platform_msgbox_msg = "Really Remove Platform #%i?"; +$TLS_remove_alien_msgbox_title = "Remove Alien Reference"; +$TLS_remove_alien_msgbox_msg = "Really Remove Alien Reference #%i?"; + +$TLS_audit_alien_assignment_removed_tc = "Alien Reference '{%1}' was removed from Test Case '{%2}'"; + +$TLS_reference_not_found = "Test Link Warning!! - reference does not exist"; + +$TLS_external_ref = "Reference"; +$TLS_hint_list_of_external_ref = 'REFX,REFY,REFZ'; + +$TLS_alien_reportedBy = 'Reported By'; +$TLS_alien_handledBy = 'Assigned To / Handled By'; +$TLS_alien_version = 'Version'; +$TLS_alien_fixedInVersion = 'Fixed In Version'; +$TLS_alien_statusVerbose = 'Status'; +$TLS_before_summary = "Before summary"; +$TLS_remove_plat_msgbox_title = 'Remove Platform'; +$TLS_remove_plat_msgbox_msg = "Remove Platform?"; +$TLS_img_title_remove_platform = 'Remove Platform'; + +$TLS_testing_a_fix = 'Fix'; +$TLS_testing_a_regression = 'Regression'; +$TLS_testing_a_requirement = 'Req / WBS'; +$TLS_testing_a_workpackage = 'Workpackage'; + +$TLS_relTypeVerbose = ' '; + +$TLS_alien_relTypeVerbose = 'Relation Type'; +$TLS_createAlien = 'Create Alien Reference'; +$TLS_img_title_remove_alien = 'Remove Alien Reference'; +$TLS_sdlcSanityChecks = 'SLDC Sanity Checks'; +$TLS_show_calendar = 'Show Calendar'; +$TLS_delete_keyword = 'Delete Keyword'; + +$TLS_report_test_automation = 'Report Test Automation Spec'; +$TLS_btn_report_test_automation = 'Report Test Automation Spec'; +$TLS_before_summary = "Before summary"; +$TLS_before_preconditions = "Before Preconditions"; +$TLS_after_title = "After Title"; +$TLS_after_summary = "After summary"; +$TLS_after_preconditions = "After Preconditions"; +$TLS_hide_because_is_used_as_variable = 'Hide (will be used as variable/snippet)'; + +$TLS_enabled = 'Enabled'; +$TLS_disabled = 'Disabled'; + +$TLS_optional = 'Optional'; +$TLS_mandatory = 'Mandatory'; +$TLS_upload_on_execution = 'Upload on Execution is '; +$TLS_and = 'and'; + +$TLS_upload_on_exec_is_enabled = 'Upload On Exec IS ENABLED'; +$TLS_upload_on_exec_is_mandatory = 'Upload On Exec IS Mandatory'; +$TLS_mandatory_upload_warning = 'One of more Steps requires that you upload files.\n Are you sure you can save the execution result?'; + +$TLS_simplexml_load_file_wrapper_error = "Please provide these text to your TestLink Administrator
    - XML Load Failed
    "; + +$TLS_tc_has_relations = 'Test Case has relations. Click to jump to section'; +$TLS_doubleclick_to_edit = 'Double-click to edit'; +$TLS_click_to_copy_ghost_to_clipboard = "Click to copy ghost string to clipboard"; +$TLS_do_not_execute = 'Do not execute'; + + +$TLS_audit_cfield_location_changed = 'Audit - Custom Field Location Changed'; + +// @see const.inc.php +#$TLS_testCaseStatus_hint_draft = ''; +#$TLS_testCaseStatus_hint_readyForReview = ''; +#$TLS_testCaseStatus_hint_reviewInProgress = ''; +#$TLS_testCaseStatus_hint_rework = ''; +$TLS_testCaseStatus_hint_obsolete = 'Do not execute this test case'; +#$TLS_testCaseStatus_hint_future = ''; +#$TLS_testCaseStatus_hint_final = ''; + +$TLS_pleaseWait = 'Please Wait'; +$TLS_exec_stats_on_testproject = 'Execution Activity for Test project'; +$TLS_link_report_exec_stats_on_testproject = 'Execution Activity'; +$TLS_exec_qty = "Numer of Executions"; + +// ----- END ----------------------------------------------------- diff --git a/locale/en_GB/texts.php b/locale/en_GB/texts.php index 9e000b91bc..e7b6b237ba 100644 --- a/locale/en_GB/texts.php +++ b/locale/en_GB/texts.php @@ -1,39 +1,36 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * @package TestLink - * @author Martin Havlat - * @copyright 2003-2009, TestLink community - * @version CVS: $Id: texts.php,v 1.29 2010/07/22 14:14:44 asimon83 Exp $ - * @link http://www.teamst.org/index.php - * - **/ - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Application error"; -$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . - "logs for details.

    You are welcome to report the problem. Please visit our " . - "website.

    "; - - - -$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; -$TLS_htmltext['assignReqs'] = "

    Purpose:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * @package TestLink + * @author Martin Havlat + * @copyright 2003-2009, TestLink community + * @version CVS: $Id: texts.php,v 1.29 2010/07/22 14:14:44 asimon83 Exp $ + * @link http://www.teamst.org/index.php + * + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Application error"; +$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . + "logs for details.

    You are welcome to report the problem. Please visit our " . + "website.

    "; + +$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; +$TLS_htmltext['assignReqs'] = "

    Purpose:

    Users can set relations between requirements and test cases. A test designer could define relations 0..n to 0..n. I.e. One test case could be assigned to none, one or more requirements and vice versa. Such traceability matrix helps to investigate test coverage @@ -44,7 +41,7 @@

    1. Choose an Test Case in tree at the left. The combo box with list of Requirements Specifications is shown at the top of the workarea.
    2. -
    3. Choose a Requirements Specification Document if more once defined. +
    4. Choose a Requirements Specification Document if more once defined. TestLink automatically reloads the page.
    5. A middle block of workarea lists all requirements (from choosen Specification), which are connected with the test case. Bottom block 'Available Requirements' lists all @@ -54,49 +51,47 @@ the middle block 'Assigned Requirements'.

    Warning:

    -A frozen requirement cannot be modified to update coverage. According to this fact, frozen requirements are listed but associated checkboxed are disabled."; +A frozen requirement cannot be modified to update coverage. According to this fact, frozen requirements are listed but associated checkboxed are disabled."; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Test Specification"; +$TLS_htmltext['editTc'] = "

    The Test Specification allows users to view " . + "and edit all of the existing Test Suites and Test Cases. " . + "Test Cases are versioned and all of the previous versions are available and can be " . + "viewed and managed here.

    - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Test Specification"; -$TLS_htmltext['editTc'] = "

    The Test Specification allows users to view " . - "and edit all of the existing Test Suites and Test Cases. " . - "Test Cases are versioned and all of the previous versions are available and can be " . - "viewed and managed here.

    -

    Getting Started:

      -
    1. Select your Test Project in the navigation tree (the root node). Please note: " . - "You can always change the active Test Project by selecting a different one from the " . - "drop-down list in the top-right corner.
    2. -
    3. Create a new Test Suite by clicking on Create (Test Suite Operations). Test Suites can " . - "bring structure to your test documents according to your conventions (functional/non-functional " . - "tests, product components or features, change requests, etc.). The description of " . - "a Test Suite could hold the scope of the included test cases, default configuration, " . - "links to relevant documents, limitations and other useful information. In general, " . - "all annotations that are common to the Child Test Cases. Test Suites follow " . - "the "folder" metaphor, thus users can move and copy Test Suites within " . - "the Test project. Also, they can be imported or exported (including the contained Test cases).
    4. -
    5. Test Suites are scalable folders. Users can move or copy Test Suites within " . - "the Test project. Test Suites can be imported or exported (include Test Cases). -
    6. Select your newly created Test Suite in the navigation tree and create " . - "a new Test Case by clicking on Create (Test Case Operations). A Test Case specifies " . - "a particular testing scenario, expected results and custom fields defined " . - "in the Test Project (refer to the user manual for more information). It is also possible " . - "to assign keywords for improved traceability.
    7. +
    8. Select your Test Project in the navigation tree (the root node). Please note: " . + "You can always change the active Test Project by selecting a different one from the " . + "drop-down list in the top-right corner.
    9. +
    10. Create a new Test Suite by clicking on Create (Test Suite Operations). Test Suites can " . + "bring structure to your test documents according to your conventions (functional/non-functional " . + "tests, product components or features, change requests, etc.). The description of " . + "a Test Suite could hold the scope of the included test cases, default configuration, " . + "links to relevant documents, limitations and other useful information. In general, " . + "all annotations that are common to the Child Test Cases. Test Suites follow " . + "the "folder" metaphor, thus users can move and copy Test Suites within " . + "the Test project. Also, they can be imported or exported (including the contained Test cases).
    11. +
    12. Test Suites are scalable folders. Users can move or copy Test Suites within " . + "the Test project. Test Suites can be imported or exported (include Test Cases). +
    13. Select your newly created Test Suite in the navigation tree and create " . + "a new Test Case by clicking on Create (Test Case Operations). A Test Case specifies " . + "a particular testing scenario, expected results and custom fields defined " . + "in the Test Project (refer to the user manual for more information). It is also possible " . + "to assign keywords for improved traceability.
    14. Navigate via the tree view on the left side and edit data. Each Test case stores own history.
    15. Assign your created Test Specification to a Test Plan when your Test cases are ready.
    16. + \"javascript:open_help_window('glossary','$locale');\">Test Plan when your Test cases are ready.
    -

    With TestLink you can organize Test Cases into Test Suites." . -"Test Suites can be nested within other test suites, enabling you to create hierarchies of Test Suites. - You can then print this information together with the Test Cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; -$TLS_htmltext['searchTc'] = "

    Purpose:

    +

    With TestLink you can organize Test Cases into Test Suites." . + "Test Suites can be nested within other test suites, enabling you to create hierarchies of Test Suites. + You can then print this information together with the Test Cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; +$TLS_htmltext['searchTc'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result include just test cases from actual Test Project.

    @@ -108,13 +103,13 @@
  • Choose required keyword or left value 'Not applied'.
  • Click the Search button.
  • All fulfilled test cases are shown. You can modify Test Cases via 'Title' link.
  • -"; - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Requirement Search Page"; -$TLS_htmltext['searchReq'] = "

    Purpose:

    +"; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Requirement Search Page"; +$TLS_htmltext['searchReq'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result includes just requirements from actual Test Project.

    @@ -132,12 +127,12 @@

    - Only requirements within the current project will be searched.
    - The search is case-insensitive.
    -- Empty fields are not considered.

    "; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Requirement Specification Search Page"; -$TLS_htmltext['searchReqSpec'] = "

    Purpose:

    +- Empty fields are not considered.

    "; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Requirement Specification Search Page"; +$TLS_htmltext['searchReqSpec'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result includes just requirement specifications from actual Test Project.

    @@ -155,35 +150,33 @@

    - Only requirement specifications within the current project will be searched.
    - The search is case-insensitive.
    -- Empty fields are not considered.

    "; -/* end contribution */ - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Purpose:

    +- Empty fields are not considered.

    "; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Purpose:

    From here you can print a single test case, all the test cases within a test suite, or all the test cases in a test project or plan.

    Get Started:

    1. -

      Select the parts of the test cases you want to display, and then click on a test case, +

      Select the parts of the test cases you want to display, and then click on a test case, test suite, or the test project. A printable page will be displayed.

    2. -
    3. Use the \"Show As\" drop-box in the navigation pane to specify whether you want -the information displayed as HTML, OpenOffice Writer or in a Micosoft Word document. +

    4. Use the \"Show As\" drop-box in the navigation pane to specify whether you want +the information displayed as HTML, OpenOffice Writer or in a Micosoft Word document. See help for more information.

    5. Use your browser's print functionality to actually print the information.
      Note: Make sure to only print the right-hand frame.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    Requirements Specification

    @@ -201,8 +194,8 @@

    Requirements

    -

    Click the title of an existing Requirements Specification. If none exist, " . - "click on the project node to create one. You can create, edit, delete +

    Click the title of an existing Requirements Specification. If none exist, " . + "click on the project node to create one. You can create, edit, delete or import requirements for the document. Each requirement has a title, scope and status. A status should be either 'Normal' or 'Not testable'. Not testable requirements are not counted to metrics. This parameter should be used for both unimplemented features and @@ -211,34 +204,32 @@

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite with name defined in configuration (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). Title and Scope are copied to these Test cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Print Requirement Specification"; //printReq +'Test suite created by Requirement - Auto';)
    . Title and Scope are copied to these Test cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Print Requirement Specification"; // printReq $TLS_htmltext['printReqSpec'] = "

    Purpose:

    You can generate document with the requirements within a requirement specification, or all the requirements in a test project.

    Get Started:

    1. -

      Select the parts of the requirements you want to display, and then click on a +

      Select the parts of the requirements you want to display, and then click on a requirement specification, or the test project. A printable page will be displayed.

    2. -
    3. Use the \"Show As\" drop-box in the navigation pane to specify whether you want -the information displayed as HTML, or in a Pseudo Micosoft Word document. +

    4. Use the \"Show As\" drop-box in the navigation pane to specify whether you want +the information displayed as HTML, or in a Pseudo Micosoft Word document. See help for more information.

    5. Use your browser's print functionality to actually print the information.
      Note: Make sure to only print the right-hand frame.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; -$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; +$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    The Keyword Assignment page is the place where users can batch assign keywords to the existing Test Suite or Test Case

    @@ -262,16 +253,15 @@

    TestLink uses this approach so that older versions of test cases in test plans are not affected by keyword assignments you make to the most recent version of the test case. If you want your test cases in your test plan to be updated, first verify they are up to date using the 'Update -Modified Test Cases' functionality BEFORE making keyword assignments.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Test Case Execution"; -$TLS_htmltext['executeTest'] = "

    Purpose:

    +Modified Test Cases' functionality BEFORE making keyword assignments.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Test Case Execution"; +$TLS_htmltext['executeTest'] = "

    Purpose:

    Allows user to execute Test cases. User can assign Test result -to Test Case for a Build. See help for more information about filters and settings " . - "(click on the question mark icon).

    +to Test Case for a Build. See help for more information about filters and settings " . + "(click on the question mark icon).

    Get started:

    @@ -279,19 +269,19 @@
  • User must have defined a Build for the Test Plan.
  • Select a Build from the drop down box
  • If you want to see only a few testcases instead of the whole tree, - you can choose which filters to apply. Click the \"Apply\"-Button - after you have changed the filters.
  • + you can choose which filters to apply. Click the \"Apply\"-Button + after you have changed the filters.
  • Click on a test case in the tree menu.
  • Fill out the test case result and any applicable notes or bugs.
  • Save results.
  • -

    Note: TestLink must be configured to collaborate with your Bug tracker -if you would like to create/trace a problem report directly from the GUI.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; -$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . - "(defined in top of navigator). This Test Plan could differ from the +

    Note: TestLink must be configured to collaborate with your Bug tracker +if you would like to create/trace a problem report directly from the GUI.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; +$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . + "(defined in top of navigator). This Test Plan could differ from the current Test Plan for execution. You can also select a Report format:

    • HTML - report is displayed in web page
    • @@ -370,12 +360,11 @@

      Total Bugs For Each Test Case

      This report shows each test case with all of the bugs filed against it for the entire project. -This report is only available if a Bug Tracking System is connected.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      Purpose:

      +This report is only available if a Bug Tracking System is connected.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      Purpose:

      Allows user (with lead level permissions) to add or remove test cases into a Test plan.

      To add or remove Test cases:

      @@ -383,11 +372,11 @@
    • Click on a test suite to see all of its test suites and all of its test cases.
    • When you are done click the 'Add / Remove Test Cases' button to add or remove the test cases. Note: Is not possible to add the same test case multiple times.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; -$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; +$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      This page allows test leaders to assign users to particular tests within the Test Plan.

      Get Started

      @@ -396,15 +385,15 @@
    • Select a planned tester.
    • Click the 'Save' button to submit assignment.
    • Open execution page to verify assignment. You can set-up a filter for users.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; -$TLS_htmltext['planUpdateTC'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; +$TLS_htmltext['planUpdateTC'] = "

      Purpose

      This page allows updating a Test case to a newer (different) version if a Test -Specification is changed. It often happens that some functionality is clarified during testing." . - " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . - " plan holds original version to be sure, that results refer to the correct text of a Test case.

      +Specification is changed. It often happens that some functionality is clarified during testing." . + " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . + " plan holds original version to be sure, that results refer to the correct text of a Test case.

      Get Started

        @@ -412,14 +401,13 @@
      1. Choose a new version from the combo-box menu for a particular Test case.
      2. Click the 'Update Test plan' button to submit changes.
      3. To verify: Open execution page to view text of the test case(s).
      4. -
      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; -$TLS_htmltext['test_urgency'] = "

      Purpose

      -

      TestLink allows setting the urgency of a Test Suite to affect the testing Priority of test cases. - Test priority depends on both Importance of Test cases and Urgency defined in +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; +$TLS_htmltext['test_urgency'] = "

      Purpose

      +

      TestLink allows setting the urgency of a Test Suite to affect the testing Priority of test cases. + Test priority depends on both Importance of Test cases and Urgency defined in the Test Plan. Test leader should specify a set of test cases that could be tested at first. It helps to ensure that testing will cover the most important tests also under time pressure.

      @@ -433,10 +421,9 @@ significant changes.
    • Click the 'Save' button to submit changes.
    • -

      For example, a Test case with a High importance in a Test suite with Low urgency " . - "will be Medium priority."; - - -// ------------------------------------------------------------------------------------------ - +

      For example, a Test case with a High importance in a Test suite with Low urgency " . + "will be Medium priority."; + +// ------------------------------------------------------------------------------------------ + ?> diff --git a/locale/en_US/description.php b/locale/en_US/description.php index 96859369be..968fb791e8 100644 --- a/locale/en_US/description.php +++ b/locale/en_US/description.php @@ -1,32 +1,31 @@ -Options for a generated document

      This table allows the user to filter test cases before they are viewed. If @@ -34,8 +33,8 @@ presented, check or uncheck, click on Filter, and select the desired data level from the tree.

      -

      Document Header: Users can filter out Document Header information. -Document Header information includes: Introduction, Scope, References, +

      Document Header: Users can filter out Document Header information. +Document Header information includes: Introduction, Scope, References, Test Methodology, and Test Limitations.

      Test Case Body: Users can filter out Test Case Body information. Test Case Body information @@ -50,35 +49,35 @@

      Table of Content: TestLink inserts list of all titles with internal hypertext links if checked.

      -

      Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component -in second case.

      "; - -// testPlan.html +

      Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component +in second case.

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      Test Plan

      General

      -

      A test plan is a systematic approach to testing a system such as software. You can organize testing activity with +

      A test plan is a systematic approach to testing a system such as software. You can organize testing activity with particular builds of product in time and trace results.

      Test Execution

      -

      This section is where users can execute test cases (write test results) and -print Test case suite of the Test Plan. This section is where users can track -the results of their test case execution.

      +

      This section is where users can execute test cases (write test results) and +print Test case suite of the Test Plan. This section is where users can track +the results of their test case execution.

      Test Plan Management

      -

      This section, which is only lead accessible, allows users to administrate test plans. -Administering test plans involves creating/editing/deleting plans, -adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can +

      This section, which is only lead accessible, allows users to administrate test plans. +Administering test plans involves creating/editing/deleting plans, +adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can see which plan.
      -Users with lead permissions may also set the priority/risk and ownership of -Test case suites (categories) and create testing milestones.

      - -

      Note: It is possible that users may not see a dropdown containing any Test plans. -In this situation all links (except lead enabled ones) will be unlinked. If you -are in this situation you must contact a lead or admin to grant you the proper -project rights or create a Test Plan for you.

      "; - -// custom_fields.html +Users with lead permissions may also set the priority/risk and ownership of +Test case suites (categories) and create testing milestones.

      + +

      Note: It is possible that users may not see a dropdown containing any Test plans. +In this situation all links (except lead enabled ones) will be unlinked. If you +are in this situation you must contact a lead or admin to grant you the proper +project rights or create a Test Plan for you.

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      Custom Fields

      Following are some facts about the implementation of custom fields:

        @@ -97,7 +96,7 @@
      • Caption variable name (eg: This is the value that is supplied to lang_get() API , or displayed as-is if not found in language file).
      • Custom field type (string, numeric, float, enum, email)
      • -
      • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list +
      • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list and combo types.
        Use the pipe ('|') character to separate possible values for an enumeration. One of the possible values @@ -117,16 +116,16 @@
      • Enable on test plan design. User can change the value during Test Plan design (add test cases to test plan)
      • Available for. User choose to what kind of item the field belows.
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      Executing Test Cases

      Allows users to 'execute' test cases. Execution itself is merely assigning a test case a result (pass,fail,blocked) against a selected build.

      Access to a bug tracking system could be configured. User can directly add a new bugs -and browse existing ones then. See Installation manual for more.

      "; - -//bug_add.html +and browse existing ones then. See Installation manual for more.

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      Add Bugs to Test Case

      (only if it is configured) TestLink has a very simple integration with Bug Tracking Systems (BTS), @@ -136,7 +135,7 @@

    • Insert new bug.
    • Display existent bug info.
    -

    +

    Process to add a bug

    @@ -145,12 +144,12 @@

  • Step 2: write down the BUGID assigned by BTS.
  • Step 3: write BUGID on the input field.
  • Step 4: use add bug button.
  • - + After closing the add bug page, you will see relevant bug data on the execute page. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Settings

    Settings allows you to select the test plan, build and platform (if available) to @@ -176,77 +175,74 @@

    Keyword Filter

    -

    You can filter test cases by the keywords that have been assigned. You can choose " . -"multiple keywords by using CTRL-Click. If you chose more than one keyword you can " . -"decide whether only test cases are shown that have all chosen keywords assigned " . -"(radiobutton \"And\") or at least one of the chosen keywords (radiobutton \"Or\").

    +

    You can filter test cases by the keywords that have been assigned. You can choose " . + "multiple keywords by using CTRL-Click. If you chose more than one keyword you can " . + "decide whether only test cases are shown that have all chosen keywords assigned " . + "(radiobutton \"And\") or at least one of the chosen keywords (radiobutton \"Or\").

    Priority Filter

    -

    You can filter test cases by test priority. The test priority is \"test case importance\" " . -"combined with \"test urgency\" within the current test plan.

    +

    You can filter test cases by test priority. The test priority is \"test case importance\" " . + "combined with \"test urgency\" within the current test plan.

    User Filter

    -

    You can filter test cases that are not assigned (\"Nobody\") or assigned to \"Somebody\". " . -"You can also filter test cases that are assigned to a specific tester. If you chose a specific " . -"tester you also have the possibility to show test cases that are unassigned in addition to " . -"those test cases (advanced Filters are available).

    +

    You can filter test cases that are not assigned (\"Nobody\") or assigned to \"Somebody\". " . + "You can also filter test cases that are assigned to a specific tester. If you chose a specific " . + "tester you also have the possibility to show test cases that are unassigned in addition to " . + "those test cases (advanced Filters are available).

    Result Filter

    -

    You can filter test cases by result (advanced Filters are available). You can filter by " . -"result \"on chosen build for execution\", \"on latest execution\", \"on ALL builds\", " . -"\"on ANY build\" and \"on specific build\". If \"specific build\" is chosen you then can " . -"specify the build.

    "; - - -// newest_tcversions.html +

    You can filter test cases by result (advanced Filters are available). You can filter by " . + "result \"on chosen build for execution\", \"on latest execution\", \"on ALL builds\", " . + "\"on ANY build\" and \"on specific build\". If \"specific build\" is chosen you then can " . + "specify the build.

    "; + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Newest versions of linked Test Cases

    The whole set of Test Cases linked to Test Plan is analyzed, and a list of Test Cases which have a newest version is displayed (against the current set of the Test Plan). -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Requirements Coverage


    This feature allows to map a coverage of user or system requirements by test cases. Navigate via link \"Requirement Specification\" in main screen.

    Requirements Specification

    -

    Requirements are grouped by 'Requirements Specification' document which is related to -Test Project.
    TestLink doesn't support versions for both Requirements Specification -and Requirements itself. So, version of document should be added after +

    Requirements are grouped by 'Requirements Specification' document which is related to +Test Project.
    TestLink doesn't support versions for both Requirements Specification +and Requirements itself. So, version of document should be added after a Specification Title. -An user can add simple description or notes to Scope field.

    +An user can add simple description or notes to Scope field.

    -

    Overwritten count of REQs serves for -evaluation Req. coverage in case that not all requirements are added (imported) in. -The value 0 means that current count of requirements is used for metrics.

    -

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test +

    Overwritten count of REQs serves for +evaluation Req. coverage in case that not all requirements are added (imported) in. +The value 0 means that current count of requirements is used for metrics.

    +

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test coverage is 25% (if all these added requirements will be tested).

    Requirements

    Click on title of a created Requirements Specification. You can create, edit, delete or import requirements for the document. Each requirement has title, scope and status. Status should be \"Normal\" or \"Not testable\". Not testable requirements are not counted -to metrics. This parameter should be used for both unimplemented features and -wrong designed requirements.

    +to metrics. This parameter should be used for both unimplemented features and +wrong designed requirements.

    -

    You can create new test cases for requirements by using multi action with checked +

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite -with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = +with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Title and Scope are copied to these Test cases.

    -"; - +"; + $TLS_hlp_req_coverage_table = "

    Coverage:

    -A value of e.g. \"40% (8/20)\" means that 20 Test Cases have to be created for this Requirement -to test it completely. 8 of those have already been created and linked to this Requirement, which +A value of e.g. \"40% (8/20)\" means that 20 Test Cases have to be created for this Requirement +to test it completely. 8 of those have already been created and linked to this Requirement, which makes a coverage of 40 percent. -"; - - -// req_edit +"; + +// req_edit $TLS_hlp_req_edit = "

    Internal links on scope:

    -

    Internal links serve the purpose of creating links to other requirements/requirement specifications +

    Internal links serve the purpose of creating links to other requirements/requirement specifications with a special syntax. Internal Links behaviour can be changed in the config file.

    Usage: @@ -261,12 +257,11 @@

    Log message for changes:

    Whenever a change is made Testlink will ask for a log message. This log message served the purpose of traceability. -If only the scope of the requirement has changed you are free to decide whether to create a new revision or not. +If only the scope of the requirement has changed you are free to decide whether to create a new revision or not. Whenever anything besides the scope is changed you are forced to create a new revision.

    -"; - - -// req_view +"; + +// req_view $TLS_hlp_req_view = "

    Direct Links:

    To easily share this document with others simply click the globe icon at the top of this document to create a direct link.

    @@ -278,17 +273,16 @@

    Shows all linked test cases for this requirement.

    Relations:

    -

    Requirement Relations are used to model relationships between requirements. -Custom relations and the option to allow relations between requirements of +

    Requirement Relations are used to model relationships between requirements. +Custom relations and the option to allow relations between requirements of different test projects can be configured on the config file. -If you set the relation \"Requirement A is parent of Requirement B\", +If you set the relation \"Requirement A is parent of Requirement B\", Testlink will set the relation \"Requirement B is child of Requirement A\" implicitly.

    -"; - - -// req_spec_edit +"; + +// req_spec_edit $TLS_hlp_req_spec_edit = "

    Internal links on scope:

    -

    Internal links serve the purpose of creating links to other requirements/requirement specifications +

    Internal links serve the purpose of creating links to other requirements/requirement specifications with a special syntax. Internal Links behaviour can be changed in the config file.

    Usage: @@ -300,32 +294,29 @@ to jump to can also be specified:
    [req tproj=<tproj_prefix> anchor=<anchor_name>]req_doc_id[/req]
    This syntax also works for requirement specifications.

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Regarding 'Save Custom Fields'

    -If you have defined and assigned to Test Project,
    +If you have defined and assigned to Test Project,
    Custom Fields with:
    'Display on test plan design=true' and
    'Enable on test plan design=true'
    you will see these in this page ONLY for Test Cases linked to Test Plan. -"; - - -// resultsByTesterPerBuild.tpl +"; + +// resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = "More information about testers:
    If you click on a tester name in this table, you will get a more detailed overview about all Test Cases assigned to that user and his testing progress.

    Note:
    -This Report shows those test cases, which are assigned to a specific user and have been executed -based on each active build. Even if a test case has been executed by another user than the assigned user, +This Report shows those test cases, which are assigned to a specific user and have been executed +based on each active build. Even if a test case has been executed by another user than the assigned user, the test case will appear as executed for the assigned user. -"; - - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/en_US/strings.txt b/locale/en_US/strings.txt index db58daff31..7ef0844478 100644 --- a/locale/en_US/strings.txt +++ b/locale/en_US/strings.txt @@ -3640,6 +3640,7 @@ $TLS_gitlab_bug_created = "GITLAB Issue Created (summary:%s) on project with ide $TLS_gitlab_bug_comment = "GITLAB Commented Issue (summary:%s)"; $TLS_kaiten_bug_created = "KAITEN Issue Created (summary:%s) on project with identifier:%s"; $TLS_kaiten_bug_comment = "KAITEN Commented Issue (summary:%s)"; +$TLS_tuleap_bug_created = "TULEAP Issue Created (summary:%s) on project with identifier:%s"; $TLS_bts_check_ok = "Connection is OK"; $TLS_bts_check_ko = "Connection is KO (detailed messages on TestLink Event Log), check configuration"; $TLS_check_bts_connection = "Check connection"; diff --git a/locale/en_US/texts.php b/locale/en_US/texts.php index b40bcf4be4..a3fefb4070 100644 --- a/locale/en_US/texts.php +++ b/locale/en_US/texts.php @@ -1,41 +1,38 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * @package TestLink - * @author Martin Havlat - * @copyright 2003-2009, TestLink community - * @link http://www.teamst.org/index.php - * - * @internal Revisions: - * 20110327 - BUGID 4349 - Julian - Update with en_GB files - * - **/ - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Application error"; -$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . - "logs for details.

    You are welcome to report the problem. Please visit our " . - "website.

    "; - - - -$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; -$TLS_htmltext['assignReqs'] = "

    Purpose:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * @package TestLink + * @author Martin Havlat + * @copyright 2003-2009, TestLink community + * @link http://www.teamst.org/index.php + * + * @internal Revisions: + * 20110327 - BUGID 4349 - Julian - Update with en_GB files + * + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Application error"; +$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . + "logs for details.

    You are welcome to report the problem. Please visit our " . + "website.

    "; + +$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; +$TLS_htmltext['assignReqs'] = "

    Purpose:

    Users can set relations between requirements and test cases. A test designer could define relations 0..n to 0..n. I.e. One test case could be assigned to none, one or more requirements and vice versa. Such traceability matrix helps to investigate test coverage @@ -46,7 +43,7 @@

    1. Choose an Test Case in tree at the left. The combo box with list of Requirements Specifications is shown at the top of the workarea.
    2. -
    3. Choose a Requirements Specification Document if more once defined. +
    4. Choose a Requirements Specification Document if more once defined. TestLink automatically reloads the page.
    5. A middle block of workarea lists all requirements (from choosen Specification), which are connected with the test case. Bottom block 'Available Requirements' lists all @@ -54,49 +51,47 @@ to the current test case. A designer could mark requirements which are covered by this test case and then click the button 'Assign'. These new assigned test case are shown in the middle block 'Assigned Requirements'.
    6. -
    "; +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Test Specification"; +$TLS_htmltext['editTc'] = "

    The Test Specification allows users to view " . + "and edit all of the existing Test Suites and Test Cases. " . + "Test Cases are versioned and all of the previous versions are available and can be " . + "viewed and managed here.

    - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Test Specification"; -$TLS_htmltext['editTc'] = "

    The Test Specification allows users to view " . - "and edit all of the existing Test Suites and Test Cases. " . - "Test Cases are versioned and all of the previous versions are available and can be " . - "viewed and managed here.

    -

    Getting Started:

      -
    1. Select your Test Project in the navigation tree (the root node). Please note: " . - "You can always change the active Test Project by selecting a different one from the " . - "drop-down list in the top-right corner.
    2. -
    3. Create a new Test Suite by clicking on Create (Test Suite Operations). Test Suites can " . - "bring structure to your test documents according to your conventions (functional/non-functional " . - "tests, product components or features, change requests, etc.). The description of " . - "a Test Suite could hold the scope of the included test cases, default configuration, " . - "links to relevant documents, limitations and other useful information. In general, " . - "all annotations that are common to the Child Test Cases. Test Suites follow " . - "the "folder" metaphor, thus users can move and copy Test Suites within " . - "the Test project. Also, they can be imported or exported (including the contained Test cases).
    4. -
    5. Test Suites are scalable folders. Users can move or copy Test Suites within " . - "the Test project. Test Suites can be imported or exported (include Test Cases). -
    6. Select your newly created Test Suite in the navigation tree and create " . - "a new Test Case by clicking on Create (Test Case Operations). A Test Case specifies " . - "a particular testing scenario, expected results and custom fields defined " . - "in the Test Project (refer to the user manual for more information). It is also possible " . - "to assign keywords for improved traceability.
    7. +
    8. Select your Test Project in the navigation tree (the root node). Please note: " . + "You can always change the active Test Project by selecting a different one from the " . + "drop-down list in the top-right corner.
    9. +
    10. Create a new Test Suite by clicking on Create (Test Suite Operations). Test Suites can " . + "bring structure to your test documents according to your conventions (functional/non-functional " . + "tests, product components or features, change requests, etc.). The description of " . + "a Test Suite could hold the scope of the included test cases, default configuration, " . + "links to relevant documents, limitations and other useful information. In general, " . + "all annotations that are common to the Child Test Cases. Test Suites follow " . + "the "folder" metaphor, thus users can move and copy Test Suites within " . + "the Test project. Also, they can be imported or exported (including the contained Test cases).
    11. +
    12. Test Suites are scalable folders. Users can move or copy Test Suites within " . + "the Test project. Test Suites can be imported or exported (include Test Cases). +
    13. Select your newly created Test Suite in the navigation tree and create " . + "a new Test Case by clicking on Create (Test Case Operations). A Test Case specifies " . + "a particular testing scenario, expected results and custom fields defined " . + "in the Test Project (refer to the user manual for more information). It is also possible " . + "to assign keywords for improved traceability.
    14. Navigate via the tree view on the left side and edit data. Each Test case stores own history.
    15. Assign your created Test Specification to a Test Plan when your Test cases are ready.
    16. + \"javascript:open_help_window('glossary','$locale');\">Test Plan when your Test cases are ready.
    -

    With TestLink you can organize Test Cases into Test Suites." . -"Test Suites can be nested within other test suites, enabling you to create hierarchies of Test Suites. - You can then print this information together with the Test Cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; -$TLS_htmltext['searchTc'] = "

    Purpose:

    +

    With TestLink you can organize Test Cases into Test Suites." . + "Test Suites can be nested within other test suites, enabling you to create hierarchies of Test Suites. + You can then print this information together with the Test Cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; +$TLS_htmltext['searchTc'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result include just test cases from actual Test Project.

    @@ -108,13 +103,13 @@
  • Choose required keyword or left value 'Not applied'.
  • Click the Search button.
  • All fulfilled test cases are shown. You can modify Test Cases via 'Title' link.
  • -"; - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Requirement Search Page"; -$TLS_htmltext['searchReq'] = "

    Purpose:

    +"; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Requirement Search Page"; +$TLS_htmltext['searchReq'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result includes just requirements from actual Test Project.

    @@ -126,12 +121,12 @@
  • Choose required keyword or leave value 'Not applied'.
  • Click the 'Find' button.
  • All fulfilling requirements are shown. You can modify requirements via 'Title' link.
  • -"; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Requirement Specification Search Page"; -$TLS_htmltext['searchReqSpec'] = "

    Purpose:

    +"; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Requirement Specification Search Page"; +$TLS_htmltext['searchReqSpec'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result includes just requirement specifications from actual Test Project.

    @@ -143,35 +138,33 @@
  • Choose required keyword or leave value 'Not applied'.
  • Click the 'Find' button.
  • All fulfilling requirements are shown. You can modify requirement specifications via 'Title' link.
  • -"; -/* end contribution */ - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Purpose:

    +"; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Purpose:

    From here you can print a single test case, all the test cases within a test suite, or all the test cases in a test project or plan.

    Get Started:

    1. -

      Select the parts of the test cases you want to display, and then click on a test case, +

      Select the parts of the test cases you want to display, and then click on a test case, test suite, or the test project. A printable page will be displayed.

    2. -
    3. Use the \"Show As\" drop-box in the navigation pane to specify whether you want -the information displayed as HTML, OpenOffice Writer or in a Micosoft Word document. +

    4. Use the \"Show As\" drop-box in the navigation pane to specify whether you want +the information displayed as HTML, OpenOffice Writer or in a Micosoft Word document. See help for more information.

    5. Use your browser's print functionality to actually print the information.
      Note: Make sure to only print the right-hand frame.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    Requirements Specification

    @@ -189,8 +182,8 @@

    Requirements

    -

    Click the title of an existing Requirements Specification. If none exist, " . - "click on the project node to create one. You can create, edit, delete +

    Click the title of an existing Requirements Specification. If none exist, " . + "click on the project node to create one. You can create, edit, delete or import requirements for the document. Each requirement has a title, scope and status. A status should be either 'Normal' or 'Not testable'. Not testable requirements are not counted to metrics. This parameter should be used for both unimplemented features and @@ -199,34 +192,32 @@

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite with name defined in configuration (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). Title and Scope are copied to these Test cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Print Requirement Specification"; //printReq +'Test suite created by Requirement - Auto';)
    . Title and Scope are copied to these Test cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Print Requirement Specification"; // printReq $TLS_htmltext['printReqSpec'] = "

    Purpose:

    From here you can print a single requirement, all the requirements within a requirement specification, or all the requirements in a test project.

    Get Started:

    1. -

      Select the parts of the requirements you want to display, and then click on a requirement, +

      Select the parts of the requirements you want to display, and then click on a requirement, requirement specification, or the test project. A printable page will be displayed.

    2. -
    3. Use the \"Show As\" drop-box in the navigation pane to specify whether you want -the information displayed as HTML, OpenOffice Writer or in a Micosoft Word document. +

    4. Use the \"Show As\" drop-box in the navigation pane to specify whether you want +the information displayed as HTML, OpenOffice Writer or in a Micosoft Word document. See help for more information.

    5. Use your browser's print functionality to actually print the information.
      Note: Make sure to only print the right-hand frame.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; -$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; +$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    The Keyword Assignment page is the place where users can batch assign keywords to the existing Test Suite or Test Case

    @@ -250,16 +241,15 @@

    TestLink uses this approach so that older versions of test cases in test plans are not affected by keyword assignments you make to the most recent version of the test case. If you want your test cases in your test plan to be updated, first verify they are up to date using the 'Update -Modified Test Cases' functionality BEFORE making keyword assignments.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Test Case Execution"; -$TLS_htmltext['executeTest'] = "

    Purpose:

    +Modified Test Cases' functionality BEFORE making keyword assignments.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Test Case Execution"; +$TLS_htmltext['executeTest'] = "

    Purpose:

    Allows user to execute Test cases. User can assign Test result -to Test Case for a Build. See help for more information about filters and settings " . - "(click on the question mark icon).

    +to Test Case for a Build. See help for more information about filters and settings " . + "(click on the question mark icon).

    Get started:

    @@ -267,19 +257,19 @@
  • User must have defined a Build for the Test Plan.
  • Select a Build from the drop down box
  • If you want to see only a few testcases instead of the whole tree, - you can choose which filters to apply. Click the \"Apply\"-Button - after you have changed the filters.
  • + you can choose which filters to apply. Click the \"Apply\"-Button + after you have changed the filters.
  • Click on a test case in the tree menu.
  • Fill out the test case result and any applicable notes or bugs.
  • Save results.
  • -

    Note: TestLink must be configured to collaborate with your Bug tracker -if you would like to create/trace a problem report directly from the GUI.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; -$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . - "(defined in top of navigator). This Test Plan could differ from the +

    Note: TestLink must be configured to collaborate with your Bug tracker +if you would like to create/trace a problem report directly from the GUI.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; +$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . + "(defined in top of navigator). This Test Plan could differ from the current Test Plan for execution. You can also select a Report format:

    • Normal - report is displayed in web page
    • @@ -403,12 +393,11 @@

      Total Bugs For Each Test Case

      This report shows each test case with all of the bugs filed against it for the entire project. -This report is only available if a Bug Tracking System is connected.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      Purpose:

      +This report is only available if a Bug Tracking System is connected.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      Purpose:

      Allows user (with lead level permissions) to add or remove test cases into a Test plan.

      To add or remove Test cases:

      @@ -416,11 +405,11 @@
    • Click on a test suite to see all of its test suites and all of its test cases.
    • When you are done click the 'Add / Remove Test Cases' button to add or remove the test cases. Note: Is not possible to add the same test case multiple times.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; -$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; +$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      This page allows test leaders to assign users to particular tests within the Test Plan.

      Get Started

      @@ -429,15 +418,15 @@
    • Select a planned tester.
    • Click the 'Save' button to submit assignment.
    • Open execution page to verify assignment. You can set-up a filter for users.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; -$TLS_htmltext['planUpdateTC'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; +$TLS_htmltext['planUpdateTC'] = "

      Purpose

      This page allows updating a Test case to a newer (different) version if a Test -Specification is changed. It often happens that some functionality is clarified during testing." . - " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . - " plan holds original version to be sure, that results refer to the correct text of a Test case.

      +Specification is changed. It often happens that some functionality is clarified during testing." . + " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . + " plan holds original version to be sure, that results refer to the correct text of a Test case.

      Get Started

        @@ -445,14 +434,13 @@
      1. Choose a new version from the combo-box menu for a particular Test case.
      2. Click the 'Update Test plan' button to submit changes.
      3. To verify: Open execution page to view text of the test case(s).
      4. -
      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; -$TLS_htmltext['test_urgency'] = "

      Purpose

      -

      TestLink allows setting the urgency of a Test Suite to affect the testing Priority of test cases. - Test priority depends on both Importance of Test cases and Urgency defined in +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; +$TLS_htmltext['test_urgency'] = "

      Purpose

      +

      TestLink allows setting the urgency of a Test Suite to affect the testing Priority of test cases. + Test priority depends on both Importance of Test cases and Urgency defined in the Test Plan. Test leader should specify a set of test cases that could be tested at first. It helps to ensure that testing will cover the most important tests also under time pressure.

      @@ -466,10 +454,9 @@ significant changes.
    • Click the 'Save' button to submit changes.
    • -

      For example, a Test case with a High importance in a Test suite with Low urgency " . - "will be Medium priority."; - - -// ------------------------------------------------------------------------------------------ - +

      For example, a Test case with a High importance in a Test suite with Low urgency " . + "will be Medium priority."; + +// ------------------------------------------------------------------------------------------ + ?> diff --git a/locale/es_AR/description.php b/locale/es_AR/description.php index cffbfe8ae1..5e943d6e72 100644 --- a/locale/es_AR/description.php +++ b/locale/es_AR/description.php @@ -1,40 +1,40 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - * ----------------------------------------------------------------------------------- */ - -// printFilter.html +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + * ----------------------------------------------------------------------------------- */ + +// printFilter.html $TLS_hlp_generateDocOptions = "

      Opciones para generar el documento

      Esta tabla permite al usuario filtrar los casos de prueba antes de verlos. -Si los datos están seleccionados se mostrarán. Para cambiar los datos presentados, +Si los datos están seleccionados se mostrarán. Para cambiar los datos presentados, marcar o desmarcar, haga clic en el Filtro, y seleccione el nivel de datos que desee en el árbol.

      -

      Cabecera del documento: Los usuarios pueden filtrar la información de cabecera del documento. -La información de cabecera del documento incluye: Introducción, alcance, referencias, +

      Cabecera del documento: Los usuarios pueden filtrar la información de cabecera del documento. +La información de cabecera del documento incluye: Introducción, alcance, referencias, Metodología de prueba y limitaciones de prueba.

      Cuerpo del caso de prueba: Los usuarios pueden filtrar la información del cuerpo del caso de prueba. @@ -44,39 +44,39 @@ desde el titulo del caso de prueba, sin embargo, no pueden filtrar la información del resumen desde el cuerpo del caso de prueba. El resumen ha sido solo parcialmente separado del cuerpo del caso de prueba Body a fin de apoyar los títulos de visión con un breve resumen y la ausencia de -Pasos, resultados esperados, y las keywords. Si el usuario decide ver el cuerpo del caso de prueba, +Pasos, resultados esperados, y las keywords. Si el usuario decide ver el cuerpo del caso de prueba, el resumen también se incluirá.

      Tabla de contenido: Testlink inserta una lista con todos los titulos con enlaces internos.

      -

      Formato de salida: Hay dos posibilidades: HTML y MS Word. El explorador llama al MS Word en segundo caso.

      "; - -// testPlan.html +

      Formato de salida: Hay dos posibilidades: HTML y MS Word. El explorador llama al MS Word en segundo caso.

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      Plan de pruebas

      General

      Un plan de pruebas es una aproximación sistemática a testear el sistema como un software. -Puede organizar las actividades de testeo para una build en particular de un producto en timpo +Puede organizar las actividades de testeo para una build en particular de un producto en timpo y con resultados de seguimiento.

      Ejecución

      Esta sección es donde los usuarios pueden ejecutar los casos de prueba (escribir los resultados) e imprimir la Suite de pruebas del plan de pruebas. Aquí es donde los usuarios pueden seguir -el resultado de la ejecución de sus casos de prueba.

      +el resultado de la ejecución de sus casos de prueba.

      Administración del plan de pruebas

      -

      Esta sección, a la cual solo un líder puede acceder, permite administrar lo planes de pruebas. -Administrar planes de pruebas involucra crear/editar/borrar planes, agregar/editar/borrar/actualizar +

      Esta sección, a la cual solo un líder puede acceder, permite administrar lo planes de pruebas. +Administrar planes de pruebas involucra crear/editar/borrar planes, agregar/editar/borrar/actualizar casos de prueba en planes, crear builds así como definir quien puede ver cada plan.
      -Los lideres (usuarios con permisos de lider) también pueden establecer la prioridad/riesgo -y la propiedad de las suites de pruebas (categorías) y crear hitos de prueba.

      - -

      Nota: Es posible que los usuarios no puedan ver un desplegable con todos los planes de pruebas. -En esta situación todos los vínculos (excepto los habilitados por un lider) serán desvinculados. Si está -en esta situación debe contactar a un lider o administrador lead or admin para concederle -los derechos adecuados del proyecto o crear un plan de pruebas para usted.

      "; - -// custom_fields.html +Los lideres (usuarios con permisos de lider) también pueden establecer la prioridad/riesgo +y la propiedad de las suites de pruebas (categorías) y crear hitos de prueba.

      + +

      Nota: Es posible que los usuarios no puedan ver un desplegable con todos los planes de pruebas. +En esta situación todos los vínculos (excepto los habilitados por un lider) serán desvinculados. Si está +en esta situación debe contactar a un lider o administrador lead or admin para concederle +los derechos adecuados del proyecto o crear un plan de pruebas para usted.

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      Campos personalizados

      Los siguientes son algunos hechos acerca de la implementación de campos personalizados:

        @@ -95,7 +95,7 @@
      • Tipo(string, numeric, float, enum, email)
      • Valores posible de enumeración(ej: rojo|amarillo|azul), aplicables a la lista, la lista de selección múltiple y los tipos de combo.
        -Utilice el carácter ('|') para separar los posibles valores de una enumeración. +Utilice el carácter ('|') para separar los posibles valores de una enumeración. Uno de los posibles valores puede ser una cadena vacía.
      • Valor por defecto (NO IMPLEMENTADO AUN).
      • @@ -104,7 +104,7 @@ (NO IMPLEMENTADO AUN)
      • Todos los campos personalizados son actualmente guardados en un campo de tipo VARCHAR(255) en la base de datos.
      • Mostrar en al especificación de pruebas.
      • -
      • Habilitar en la especificación de pruebas. El usuario puede cambiar el valor durante el diseño de +
      • Habilitar en la especificación de pruebas. El usuario puede cambiar el valor durante el diseño de la especificación de casos de prueba.
      • Mostrar en la ejecución.
      • Habilitar en la ejecución. El usuario puede cambiar el valor durante la ejecución.
      • @@ -112,26 +112,26 @@
      • Habilitar en el diseño del plan de pruebas. El usuario puede cambiar el valor durante el diseño del plan de pruebas (agregar casos de prueba al plan de pruebas)
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      Ejecutando casos de prueba

      -

      Permite a los usuario a 'ejecutar' los casos de prueba. La ejecución en sí no es más que la +

      Permite a los usuario a 'ejecutar' los casos de prueba. La ejecución en sí no es más que la asignación a un caso de prueba de un resultado (pasa, falla, bloqueado) contra una build seleccionada.

      El acceso a un BTS puede ser configurado.Los usuarios pueden agregar un bug nuevo directamente o navegar -por los existentes.

      "; - -//bug_add.html +por los existentes.

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      Agregar bugs al caso de prueba

      (Sólo si está configurado) -Testlink tiene una integración muy simple con un BTS,no es capaz de enviar ni la solicitud de creación de bug de BTS, -ni recuperar el ID del bug.La integración se realiza mediante enlaces a las páginas de BTS, que llama a las siguientes +Testlink tiene una integración muy simple con un BTS,no es capaz de enviar ni la solicitud de creación de bug de BTS, +ni recuperar el ID del bug.La integración se realiza mediante enlaces a las páginas de BTS, que llama a las siguientes características:

      • Insertar bug nuevo.
      • Mostrar información de bug existente.
      -

      +

      Proceso para agregar un bug

      @@ -140,107 +140,104 @@

    • Paso 2: escribe abajo del BUG ID asignado por el BTS.
    • Paso 3: escribe BUG ID en el campo de entrada.
    • Paso 4: use el botón 'agregar bug'.
    • -
    + Luego de cerrar la página de adición de bugs, verá los datos relevantes del bug en la página de ejecución. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Instalar filtos y builds para la ejecución

    -

    El panel izquierdo consta de: un navegador por los casos de prueba asignados al plan de pruebas actual " . -"y una tabla con configuraciones y filtros.Estos filtros permiten al usuario " . -"refinar el conjunto ofrecido de casos de prueba antes de ser ejecutados." . -"establezca su filtro, presione el botón \"Aplicar\" y seleccione el caso de prueba apropiado en el árbol.

    +

    El panel izquierdo consta de: un navegador por los casos de prueba asignados al plan de pruebas actual " . + "y una tabla con configuraciones y filtros.Estos filtros permiten al usuario " . + "refinar el conjunto ofrecido de casos de prueba antes de ser ejecutados." . + "establezca su filtro, presione el botón \"Aplicar\" y seleccione el caso de prueba apropiado en el árbol.

    Build

    -

    Los usuarios pueden elegir una build que se conectará con el resultado de la prueba. " . -"Las Builds son el componente básico para el plan de pruebas actual.Cada caso de prueba " . -"puede ser corrido mas veces por build.Sin embargo, sólo el último resultado es tomado en cuenta. +

    Los usuarios pueden elegir una build que se conectará con el resultado de la prueba. " . + "Las Builds son el componente básico para el plan de pruebas actual.Cada caso de prueba " . + "puede ser corrido mas veces por build.Sin embargo, sólo el último resultado es tomado en cuenta.
    Las builds pueden ser creadas por lideres usando la página de creacion de build.

    Filtro de ID

    -

    Los usuarios pueden filtrar los casos de prueba por un identificador único. Este ID es creado automáticamente -durante el tiempo de creación.La caja vacía significa que el filtro no se aplica.

    +

    Los usuarios pueden filtrar los casos de prueba por un identificador único. Este ID es creado automáticamente +durante el tiempo de creación.La caja vacía significa que el filtro no se aplica.

    Filtro de prioridad

    -

    Los usuarios pueden filtrar los casos de prueba por la prioridad. Cada importancia del" . -"caso de prueba es combinada con la urgencia del mismo dentro del plan de pruebas actual." . -"Por ejemplo la prioridad 'ALTA' en el caso de prueba se muetra si la importancia" . -"o urgencia es alto y su segundo atributo es por lo menos 'MEDIA'.

    +

    Los usuarios pueden filtrar los casos de prueba por la prioridad. Cada importancia del" . + "caso de prueba es combinada con la urgencia del mismo dentro del plan de pruebas actual." . + "Por ejemplo la prioridad 'ALTA' en el caso de prueba se muetra si la importancia" . + "o urgencia es alto y su segundo atributo es por lo menos 'MEDIA'.

    Filtro de resultado

    -

    Los usuarios pueden filtrar los casos de prueba por los resultados.Los resultados +

    Los usuarios pueden filtrar los casos de prueba por los resultados.Los resultados son lo que pasó con ese caso de prueba durante una build en particular.Los casos de prueba pueden pasar, fallar, ser bloqueados o no ejecutados.Este filtro está desactivado por defecto.

    Filtro de usuario

    -

    Los usuarios pueden filtrar los casos de prueba por su asignado.El recuadro permite incluír también " . -"casos de prueba \"sin asignar\" dentro del resultado.

    "; -/* -

    Resultado más reciente

    -

    Por defecto o si el recuadro de 'Más reciente' está desmarcado, el árbol se ordenará -por la build que se elija en menú desplegable. Es esta condición el árbol mostrará -el estado de los casos de prueba.

    - */ - - -// newest_tcversions.html +

    Los usuarios pueden filtrar los casos de prueba por su asignado.El recuadro permite incluír también " . + "casos de prueba \"sin asignar\" dentro del resultado.

    "; +/* + *

    Resultado más reciente

    + *

    Por defecto o si el recuadro de 'Más reciente' está desmarcado, el árbol se ordenará + * por la build que se elija en menú desplegable. Es esta condición el árbol mostrará + * el estado de los casos de prueba.

    + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Nuevas versiones de Casos de prueba vinculados

    -

    El conjunto de casos de prueba vinculados al plan de pruebas es analizado, y se muestra una lista de -casos de prueba que tienen una versión más reciente (en contra de la serie -actual del plan de pruebas).

    "; - - -// requirementsCoverage.html +

    El conjunto de casos de prueba vinculados al plan de pruebas es analizado, y se muestra una lista de +casos de prueba que tienen una versión más reciente (en contra de la serie +actual del plan de pruebas).

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Cobertura de requerimientos


    -

    Esta característica permite asignar cobertura de usuario o requerimientos de sistema por caso de prueba. +

    Esta característica permite asignar cobertura de usuario o requerimientos de sistema por caso de prueba. Navegar a través de \"Especificación de requerimientos\" en la pantalla principal.

    Especificación de requerimientos

    Los requerimientos estan agrupados por el documento'Especificación de requerimientos', el cual está relacionado al proyecto.
    -Un usuario puede añadir una descripción simple o una nota al campo 'Alcance'.

    +Un usuario puede añadir una descripción simple o una nota al campo 'Alcance'.

    Sobreescribir el contador de Reqs

    sirve para evaluar la cobertura de Reqs.en caso de que no todos los requerimientos se añadan a Testlink. El valor 0 significa que el contador actual de Reqs. se utiliza para las métricas. -

    Por ejemplo SRS incluye 200 requerimientos pero solo 50 son añadidos a Testlink. La cobertura +

    Por ejemplo SRS incluye 200 requerimientos pero solo 50 son añadidos a Testlink. La cobertura de Test es del 25% (si todos estos requerimientos añadidos se testearan).

    Requerimientos

    -

    Haga click en el titulo de la Especificación de requerimientos creada,si no hay nada existente haga -click en el proyecto para crear uno. Puede crear, editar, eliminar o importar requerimientos +

    Haga click en el titulo de la Especificación de requerimientos creada,si no hay nada existente haga +click en el proyecto para crear uno. Puede crear, editar, eliminar o importar requerimientos de un documento. Cada requerimiento tiene un titulo, un alcance y un estado. -El estado debe ser 'Normal' o 'No testeable'. Los requerimientos no testeables no tienen contador +El estado debe ser 'Normal' o 'No testeable'. Los requerimientos no testeables no tienen contador para las métricas. Este parámetro debe ser usado tanto para características que no se han implementado como para requerimientos mal diseñados.

    -

    Puede crear nuevos casos de prueba para requerimientos usando la multi acción con requerimientos +

    Puede crear nuevos casos de prueba para requerimientos usando la multi acción con requerimientos marcados dentro de la pantalla de especificación. Estos casos de prueba son creados dentro de la Suite de pruebas con nombre definido en la configuración. El título y alcance son copiados de estos casos de prueba.

    -"; - +"; + $TLS_hlp_req_coverage_table = "

    Cobertura:

    -Un valor de ejemplo \"40% (8/20)\" significa que 20 casos de prueba tienen qeu ser creados para este requisito -para probarlo completamente. 8 de ellos han sido ya creados y vinculados a este requisito, que +Un valor de ejemplo \"40% (8/20)\" significa que 20 casos de prueba tienen qeu ser creados para este requisito +para probarlo completamente. 8 de ellos han sido ya creados y vinculados a este requisito, que hace una cobertura del 40 porciento. -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Con respecto a 'Guardar campos personalizado'

    -Si se han definido y asignado al proyecto,
    +Si se han definido y asignado al proyecto,
    Campos personalizados con:
    'Display on test plan design=true' y
    'Enable on test plan design=true'
    podrá ver estos solo en esta página para casos de prueba relacionados con el Plan de pruebas. -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/es_AR/texts.php b/locale/es_AR/texts.php index ccd0294340..e0e40066da 100644 --- a/locale/es_AR/texts.php +++ b/locale/es_AR/texts.php @@ -1,32 +1,31 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * ------------------------------------------------------------------------------------ */ - -$TLS_htmltext_title['assignReqs'] = "Asignar requerimientos a los Casos de prueba"; -$TLS_htmltext['assignReqs'] = "

    Propósito:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * ------------------------------------------------------------------------------------ */ +$TLS_htmltext_title['assignReqs'] = "Asignar requerimientos a los Casos de prueba"; +$TLS_htmltext['assignReqs'] = "

    Propósito:

    Los usuarios pueden establecer relaciones entre los Requerimientos y los Casos de prueba. Un diseñador podria definir relaciones 0..n a 0..n I.e. un Caso de prueba podria ser asignado a ninguno, uno o más Requerimientos y viceversa.La trazabilidad ayuda a investigar la cobertura de los requerimientos de los Casos de prueba @@ -34,21 +33,21 @@

    Primeros pasos:

      -
    1. Seleccione un Caso de prueba en el árbol de la izquierda. El cuadro combinado - con la lista de Especificaciones de Requerimientos se muestra en la parte superior +
    2. Seleccione un Caso de prueba en el árbol de la izquierda. El cuadro combinado + con la lista de Especificaciones de Requerimientos se muestra en la parte superior del área de trabajo .
    3. -
    4. Seleccione un documento de especificaciones de requerimientos si se encuentra definido +
    5. Seleccione un documento de especificaciones de requerimientos si se encuentra definido una vez más.Testlink automáticamente recarga la página.
    6. El bloque intermedio del área de trabajo registra todos los requerimientos - (de la especificación seleccionada), los cuales están unidos al Caso de prueba. - El bloque de fondo 'Requerimientos disponibles' lista todos los requerimientos que no poseen - relación al Caso de prueba actual. Un diseñador podría marcar requerimientos, los cuales están - cubiertos por el Caso de prueba, luego haga clic en la botón ‘Asignar’. Estos nuevos casos de + (de la especificación seleccionada), los cuales están unidos al Caso de prueba. + El bloque de fondo 'Requerimientos disponibles' lista todos los requerimientos que no poseen + relación al Caso de prueba actual. Un diseñador podría marcar requerimientos, los cuales están + cubiertos por el Caso de prueba, luego haga clic en la botón ‘Asignar’. Estos nuevos casos de prueba asignados se muestran en el bloque intermedio de “Requerimientos Asignadosâ€.
    7. -
    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Buscar Especificación de Requerimientos"; //printReq +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Buscar Especificación de Requerimientos"; // printReq $TLS_htmltext['searchReqSpec'] = "

    Propósito:

    Navegar acorde a keywords y/o palabras buscadas. La busqueda no es un caso sensible. El resultado incluye solo las especificaciones de requerimientos del proyecto actual.

    @@ -60,11 +59,11 @@
  • Elige la keyword requerida o deje el valor en 'no aplicado'.
  • Haga click en el botón 'Encontrar'.
  • Se muestran todos los requisitos de cumplimiento.Puede modificar las especificaciones de requerimientos por medio del vínculo 'Titulo'.
  • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Buscar Requerimientos"; -$TLS_htmltext['searchReq'] = "

    Próposito:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Buscar Requerimientos"; +$TLS_htmltext['searchReq'] = "

    Próposito:

    Navegar acorde a keywords y/o palabras buscadas. La busqueda no es un caso sensible. El resultado incluye solo @@ -76,65 +75,64 @@

  • Escribe la palabra a buscar en el cuadro apropiado. Deje campos sin usar en blanco.
  • Elige la keyword requerida o deje el valor en 'no aplicado'.
  • Haga click en el botón 'Encontrar'.
  • -
  • Se muestran todos los requerimientos de cumplimiento.Puede modificar los requerimientos +
  • Se muestran todos los requerimientos de cumplimiento.Puede modificar los requerimientos por medio del vínculo 'Titulo'.
  • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Imprimir Especificación de Requerimientos"; //printReq +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Imprimir Especificación de Requerimientos"; // printReq $TLS_htmltext['printReqSpec'] = "

    Propósito:

    -

    Desde aquí usted puede imprimir un requerimiento en particular, todos los requerimientos +

    Desde aquí usted puede imprimir un requerimiento en particular, todos los requerimientos dentro de la especificación de requerimientos,o todos los requerimientos del Proyecto.

    Comenzar:

    1. -

      Seleccione las partes de los requerimientos que desea mostrar y, a continuación, haga clic en un requerimiento, +

      Seleccione las partes de los requerimientos que desea mostrar y, a continuación, haga clic en un requerimiento, en el requerimiento de especificación, o en el proyecto. Una página para imprimir en la pantalla.

    2. -
    3. Use el cuadro \"Mostrar como\" en el panel de navegación para especificar si quiere que la información +

    4. Use el cuadro \"Mostrar como\" en el panel de navegación para especificar si quiere que la información se muestre como HTML, documento de word o de OpenOffice.

    5. Use la función de imprimir de su explorer para imprimir la información actual.
      Nota: Aseguresé de solo imprimir el marco de la mano derecha.

    6. -
    "; - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Especificaciones de pruebas"; -$TLS_htmltext['editTc'] = "

    Propósito:

    -

    La Especificación de pruebas permite a los usuarios ver y editar todos las " . - "Suites de pruebas y los Casos de prueba existentes. Los Casos de prueba están versionados y todas " . - "las versiones anteriores estan disponibles y pueden ser vistas y gestionadas desde aquí.

    +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Especificaciones de pruebas"; +$TLS_htmltext['editTc'] = "

    Propósito:

    +

    La Especificación de pruebas permite a los usuarios ver y editar todos las " . + "Suites de pruebas y los Casos de prueba existentes. Los Casos de prueba están versionados y todas " . + "las versiones anteriores estan disponibles y pueden ser vistas y gestionadas desde aquí.

    Primeros pasos:

      -
    1. Seleccione un Proyecto en el árbol de navegación. Tenga en cuenta que " . - "Siempre puede cambiar el Proyecto activo seleccionando uno diferente en la " . - "lista desplegable que está en la esquina superior-derecha de la página.
    2. -
    3. Cree una nueva Suite de prueba haciendo click en (Crear nueva Suite de prueba).La Suite de prueba puede " . - "brindarle estructura a sus Documentos, de acuerdo a sus convenciones. La descripción de " . - "una Suite de prueba podría tener el alcance de los Casos de prueba incluídos, configuración por defecto, " . - "enlaces a documentos relevantes, limitaciones y otra información util. En general, " . - "todas las anotaciones son comunes a los Casos de prueba.
    4. -
    5. Las Suites de pruebas son carpetas escalables. Por lo cual un usuario puede mover y copiar Suites de pruebas dentro del " . - "Proyecto. Además, pueden ser importadas o exportadas (incluyendo los Casos de prueba). -
    6. Seleccione su Suite de pruebas recien creada en el árbol de navegación y cree " . - "un nuevo Caso de prueba haciendo click en Crear Casos de prueba. Un Caso de prueba precisa " . - "un escenario de testing particular, resultados esperados y campos personalizados definidos " . - "en el Proyecto. También es posible " . - "asignar Palabras Clave para una mejor trazabilidad.
    7. -
    8. Navegue a través de la vista de árbol en el lado izquierdo y edite datos. Los Casos de prueba +
    9. Seleccione un Proyecto en el árbol de navegación. Tenga en cuenta que " . + "Siempre puede cambiar el Proyecto activo seleccionando uno diferente en la " . + "lista desplegable que está en la esquina superior-derecha de la página.
    10. +
    11. Cree una nueva Suite de prueba haciendo click en (Crear nueva Suite de prueba).La Suite de prueba puede " . + "brindarle estructura a sus Documentos, de acuerdo a sus convenciones. La descripción de " . + "una Suite de prueba podría tener el alcance de los Casos de prueba incluídos, configuración por defecto, " . + "enlaces a documentos relevantes, limitaciones y otra información util. En general, " . + "todas las anotaciones son comunes a los Casos de prueba.
    12. +
    13. Las Suites de pruebas son carpetas escalables. Por lo cual un usuario puede mover y copiar Suites de pruebas dentro del " . + "Proyecto. Además, pueden ser importadas o exportadas (incluyendo los Casos de prueba). +
    14. Seleccione su Suite de pruebas recien creada en el árbol de navegación y cree " . + "un nuevo Caso de prueba haciendo click en Crear Casos de prueba. Un Caso de prueba precisa " . + "un escenario de testing particular, resultados esperados y campos personalizados definidos " . + "en el Proyecto. También es posible " . + "asignar Palabras Clave para una mejor trazabilidad.
    15. +
    16. Navegue a través de la vista de árbol en el lado izquierdo y edite datos. Los Casos de prueba almacenan su propio historial.
    -

    con Testlink organize los Casos de prueba dentro de las Suites de pruebas." . -"Las Suites de pruebas se pueden anidar dentro de otras Suites, esto permite crear jerarquias de Casos de prueba. - Entonces usted puede imprimir esta información junto con los Casos de prueba.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Busar Casos de prueba"; -$TLS_htmltext['searchTc'] = "

    Próposito:

    +

    con Testlink organize los Casos de prueba dentro de las Suites de pruebas." . + "Las Suites de pruebas se pueden anidar dentro de otras Suites, esto permite crear jerarquias de Casos de prueba. + Entonces usted puede imprimir esta información junto con los Casos de prueba.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Busar Casos de prueba"; +$TLS_htmltext['searchTc'] = "

    Próposito:

    Navegar acorde a Palabras Clave y/o palabras buscadas.El resultado incluye solo los Casos de prueba del Proyecto actual.

    @@ -145,12 +143,11 @@
  • Elige las Palabras Clave requeridas o el valor 'No aplicado'.
  • Haga Click en el botón 'Buscar'.
  • Todos los Casos de prueba cumplidos se muestran. Usted puede modificar los Casos de prueba a través del 'Titulo'.
  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificación de pruebas"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Propósito:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificación de pruebas"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Propósito:

    Desde aquí usted puede imprimir un Caso de prueba en particular, todos los Casos de prueba dentro de la Suite de pruebas, o todos los Casos de prueba del Proyecto o del Plan de pruebas.

    Comenzar:

    @@ -164,12 +161,11 @@
  • Use la función de imprimir de su explorer para imprimir la información actual.
    Nota: Aseguresé de solo imprimir el marco de la mano derecha.

  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Diseño de Especificación de Requerimientos"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    Usted puede administrar los documentos de especificación de requerimientos.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Diseño de Especificación de Requerimientos"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    Usted puede administrar los documentos de especificación de requerimientos.

    Especificación de requerimientos

    @@ -181,52 +177,50 @@

    Sobreescribir el contador de Reqs sirve para evaluar la cobertura de Reqs.en caso de que no todos los requerimientos se añadan a Testlink. El valor 0 significa que el contador actual de Reqs. se utiliza para las métricas. -

    Por ejemplo SRS incluye 200 requerimientos pero solo 50 son añadidos a Testlink. La cobertura +

    Por ejemplo SRS incluye 200 requerimientos pero solo 50 son añadidos a Testlink. La cobertura de las pruebas es del 25% (si todos estos requerimientos añadidos se testearan).

    Requerimientos

    -

    Haga click en el titulo de la Especificación de requerimientos creada,si no hay nada existente haga -click en el proyecto para crear uno. Puede crear, editar, eliminar o importar requerimientos +

    Haga click en el titulo de la Especificación de requerimientos creada,si no hay nada existente haga +click en el proyecto para crear uno. Puede crear, editar, eliminar o importar requerimientos de un documento. Cada requerimiento tiene un titulo, un alcance y un estado. -El estado debe ser 'Normal' o 'No testeable'. Los requerimientos no testeables no tienen contador +El estado debe ser 'Normal' o 'No testeable'. Los requerimientos no testeables no tienen contador para las métricas. Este parámetro debe ser usado tanto para características que no se han implementado como para requerimientos mal diseñados.

    Puede crear nuevos casos de prueba para requerimientos mediante el uso de acciones multiples con requerimientos seleccionados dentro de la pantalla de especificación. Estos casos de prueba son creados dentro de la Suite de pruebas -con un nombre definido en la configuración. El Titulo y alcance son copiados a estos casos de prueba.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Asignación de Keywords"; -$TLS_htmltext['keywordsAssign'] = "

    Propósito:

    -

    La página de asignación de Keywords es donde los usuarios pueden asignar Keywords a la Suite +con un nombre definido en la configuración. El Titulo y alcance son copiados a estos casos de prueba.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Asignación de Keywords"; +$TLS_htmltext['keywordsAssign'] = "

    Propósito:

    +

    La página de asignación de Keywords es donde los usuarios pueden asignar Keywords a la Suite de pruebas existente o a un Caso de prueba

    Para asignar Keywords:

    1. Seleccione una Suite de pruebas, un Caso de pruebas en la vista de árbol de la izquierda.
    2. -
    3. El cuadro de nivel superior que aparece en el lado derecho le permitirá asignar +
    4. El cuadro de nivel superior que aparece en el lado derecho le permitirá asignar palabras clave a disposición de todos los casos de prueba en particular.
    5. Las selecciones siguientes le permiten asignar los casos de prueba a un nivel más específico

    Información importante respecto a las asignaciones de Keywords en los planes de pruebas:

    -

    Las asignaciones de keywords que realice en el pliego de condiciones sólo tienen efecto en casos de +

    Las asignaciones de keywords que realice en el pliego de condiciones sólo tienen efecto en casos de prueba en su plan de pruebas si y sólo si el plan de pruebas contiene la versión más reciente del caso de prueba. De otra manera si el Plan de pruebas contiene versiones antiguas del Caso de prueba, las asignaciones que haga ahora no apareceran en el Plan de pruebas.

    -

    Testlink utiliza este enfoque para que las versiones anteriores de casos de prueba en los planes de -prueba no sean afectadas por las asignaciones de Keywords que realice en la versión más reciente -del caso de prueba. Si usted quiere que se actualizen sus casos de prueba dentro del plan de pruebas, PRIMERO verifique -que ellos esten al día usando la funcionalidad de 'Actualizar Casos de prueba modificados' ANTES de realizar asignaciones de Keywords.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Ejecución de casos de prueba"; -$TLS_htmltext['executeTest'] = "

    Propósito:

    +

    Testlink utiliza este enfoque para que las versiones anteriores de casos de prueba en los planes de +prueba no sean afectadas por las asignaciones de Keywords que realice en la versión más reciente +del caso de prueba. Si usted quiere que se actualizen sus casos de prueba dentro del plan de pruebas, PRIMERO verifique +que ellos esten al día usando la funcionalidad de 'Actualizar Casos de prueba modificados' ANTES de realizar asignaciones de Keywords.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Ejecución de casos de prueba"; +$TLS_htmltext['executeTest'] = "

    Propósito:

    Permite a los usuarios ejecutar casos de prueba. Los usuarios pueden asignarle un resultado por build a los casos de prueba.

    @@ -239,13 +233,13 @@
  • Rellen el resultado del caso de prueba y el de notas aplicables o bugs.
  • Guarde los resultados.
  • -

    Nota: Testlink debe estar configurado para colaborar con su seguidor de bug -si usted desea crear / trazar un problema directamente reportado desde la GUI.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Descripción del reporte y de las métricas"; -$TLS_htmltext['showMetrics'] = "

    Los reportes están relacionados con el plan de pruebas" . - "(definido en la parte superior del navegador). Este plan de pruebas podría diferir del +

    Nota: Testlink debe estar configurado para colaborar con su seguidor de bug +si usted desea crear / trazar un problema directamente reportado desde la GUI.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Descripción del reporte y de las métricas"; +$TLS_htmltext['showMetrics'] = "

    Los reportes están relacionados con el plan de pruebas" . + "(definido en la parte superior del navegador). Este plan de pruebas podría diferir del plan de pruebas actual para ejecutar. También puede seleccionar un formato de reporte:

    • Normal - el reporte se muetras en la página web
    • @@ -264,11 +258,11 @@

      'Ultimo resultado de prueba' es un concepto usado en varios reportes, y es determinado de la siguiente manera:

        -
      • El orden en que se agregan builds al plan de pruebas determina que build es la más reciente. Los resultados de la más +
      • El orden en que se agregan builds al plan de pruebas determina que build es la más reciente. Los resultados de la más reciente build tendrá precedencia sobre las antiguas builds. Por ejemplo, si marca una prueba como 'fail' en la build 1, y marca ésta como 'pass' en la build 2, el resultado final de la prueba será 'pass'.
      • Si un caso de prueba es ejecutado en tiempos multiples en la misma build, la ejecución más reciente tendrá precedencia.
      • -
      • Los casos de pruebas listados como 'not run' en contra de una build no se tienen en cuenta. Por ejemplo, si marcas un +
      • Los casos de pruebas listados como 'not run' en contra de una build no se tienen en cuenta. Por ejemplo, si marcas un caso como 'pass' en la build 1, y no lo ejecutas en la build 2, el resultado final del caso sera considerado como 'pass'

      En las siguientes tablas se muestran:

      @@ -286,7 +280,7 @@

      El estado general de la build

      Muestra los resultados de la ejecución de todas las builds. Para cada build, el total de casos de prueba, -el total de los pass, % pass, el total de los fail, % fail, blocked, % blocked, not run, %not run. Si un caso +el total de los pass, % pass, el total de los fail, % fail, blocked, % blocked, not run, %not run. Si un caso de prueba ha sido ejecutado dos veces en la misma build, la más reciente ejecución se tomará en cuenta.

      Consulta de métricas

      @@ -298,18 +292,18 @@
      • keyword 0->1 keywords pueden ser seleccionadas. Por defecto - no hay keywords seleccionadas. Si una keyword no está seleccionada, entonces todos los casos de prueba seran considerados independietemente de las asignaciones de keywords. Keywords are assigned -Las keywords están asignadas en la especificación de pruebas o en la administración de keywords. Las keywords asignadas a los -casos de prueba abarcan a todos los planes de pruebas, y abarcan a lo largo de todas las verisones de los casos de prueba. +Las keywords están asignadas en la especificación de pruebas o en la administración de keywords. Las keywords asignadas a los +casos de prueba abarcan a todos los planes de pruebas, y abarcan a lo largo de todas las verisones de los casos de prueba. Si está interesado en los resultados para una keyword específica debe alterar este control.
      • owner 0->1 dueños pueden ser seleccionado. Por defecto - no hay dueño seleccionado. Si no hay dueños seleccionados, -entonces todos los casos de prueba seran considerados independietemente de las asignaciones de dueños. Actualmente no hay +entonces todos los casos de prueba seran considerados independietemente de las asignaciones de dueños. Actualmente no hay una funcionalidad para buscar un caso de prueba 'no asignado'. La propiedad es asignada a través de la página 'Asignar ejecución de caso de prueba', y se realiza en base a un plan por prueba. Si está interesado en el trabajo hecho por un tester en específico débe modificar este control.
      • Suite de nivel superior 0->n suites de nivel superior se pueden elegir. Por defecto - todas están seleccionadas. -Solamente las suites que son seleccionadas serán consultadas para los resultados y métricas. Si está interesado solamente +Solamente las suites que son seleccionadas serán consultadas para los resultados y métricas. Si está interesado solamente en los resultados para una suite específica usted debe alterar este control.
      • -
      • Builds 1->n builds pueden ser seleccionadas. Por defecto - todas las builds están seleccionadas. +
      • Builds 1->n builds pueden ser seleccionadas. Por defecto - todas las builds están seleccionadas. Sólo las ejecuciones realizadas en las builds seleccionadas seran tomadas en cuenta en producción de las métricas. Por ejemplo - si desea ver cuando casos de prueba fueron ejecutados en las ultimas 3 builds débe alterar este control.
      @@ -330,33 +324,32 @@ logicamente es el usado para determinar si un caso de prueba se considera 'bloqueado', 'falló', o 'no ejecutado'.

      Reporte

      -

      Ver el estado de cada caso de prueba en cada build. Si un caso de pruebas fue ejecutado varias veces en +

      Ver el estado de cada caso de prueba en cada build. Si un caso de pruebas fue ejecutado varias veces en la misma build, solamente el resultado de ejecución más reciente será tenido en cuenta.Se recomienda -para exportar este reporte el formato Excel para una facil navegación o si se está utilizando +para exportar este reporte el formato Excel para una facil navegación o si se está utilizando un conjunto de datos grande.

      Listas - Métricas generales del plan de pruebas

      -

      'Ultimo resultado de prueba' la lógica se utiliza para las cuatro cartas que se pueden ver.Los gráficos -se han animado para ayudar al usuario a visualizar los parámetros del plan de pruebas en curso. +

      'Ultimo resultado de prueba' la lógica se utiliza para las cuatro cartas que se pueden ver.Los gráficos +se han animado para ayudar al usuario a visualizar los parámetros del plan de pruebas en curso. Las cuatro gráficas proporcionan son :

      • Pie de tabla general 'paso / no / bloqueado / y no ejecutado' de los casos de prueba
      • Gráfico de barras de los resultados por keyword
      • Gráfico de barras de los resultados por dueño
      • Gráfico de barras de los resultados por suite de nivel superior
      -

      Las barras de los gráficos de barras son de color de manera que el usuario puede identificar el +

      Las barras de los gráficos de barras son de color de manera que el usuario puede identificar el número aproximado de 'paso, no, bloqueado, y no ejecutado' de los casos de prueba.

      Esta página de reporte requiere un plugin flash es su explorador web (by http://www.maani.us) para mostrar los resultados en un formato gráfico.

      Total de bugs para cada Caso de prueba

      Este reporte muestra cada caso de prueba con with todos los errores reportados en contra de todo el proyecto.. -Este reporte esta disponible si el sistema de seguimiento de bugs esta conectado (bugzilla por ej.).

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Añadir / Quitar Casos de prueba del plan de pruebas"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      Propósito:

      +Este reporte esta disponible si el sistema de seguimiento de bugs esta conectado (bugzilla por ej.).

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Añadir / Quitar Casos de prueba del plan de pruebas"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      Propósito:

      Permite a los líderes añadir o quitar casos de prueba dentro del plan de pruebas.

      Comenzar:

      @@ -364,11 +357,11 @@
    • Haga click en una suite de pruebas para ver todas las suites de pruebas y todos los casos de prueba.
    • Cuando termine haga click en el botón 'Añadir / quitar casos de prueba'para agregar o eliminar los casos de prueba. Nota: No es posible añadir el mismo caso de prueba varias veces.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Asignar Usuarios a la ejecuciones"; -$TLS_htmltext['tc_exec_assignment'] = "

      Propósito

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Asignar Usuarios a la ejecuciones"; +$TLS_htmltext['tc_exec_assignment'] = "

      Propósito

      Esta página le permite a los líderes asignar usuarios a casos particulares dentro del plan de pruebas.

      Comenzar:

      @@ -377,13 +370,12 @@
    • Seleccione un usuario previsto.
    • Presione el boton para presentar la asignación.
    • Abra la página de ejecución para verificar la asignación. Puede establecer filtros a los usuarios.
    • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Actualizar los casos de prueba en los planes de prueba"; -$TLS_htmltext['planUpdateTC'] = "

      Propósito

      -

      Esta página permite actualizar los casos de prueba a una version nueva (diferente) +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Actualizar los casos de prueba en los planes de prueba"; +$TLS_htmltext['planUpdateTC'] = "

      Propósito

      +

      Esta página permite actualizar los casos de prueba a una version nueva (diferente) en el caso de que la especificación de pruebas halla cambiado. Sucede a menudo que algunas funciones se aclaran durante el testeo. Los usuarios modifican la especificación de pruebas, pero los cambios necesitan propagarse al plan de pruebas también. De otro modo. los planes de prueba mantienen las versiones originale para estar seguro que los resultados se refieren al texto correco del caso de prueba.

      @@ -394,14 +386,13 @@
    • Elige una nueva versión para un caso de prueba en particular.
    • Presione el botón 'Actualizar plan de pruebas' para realizar cambios.
    • Para verificar: abra la página de ejecución para ver el texto del caso de prueba.
    • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Especificar la urgencia de la prueba"; -$TLS_htmltext['test_urgency'] = "

      Propósito

      -

      Testlink permite establecer urgencias en la Suite de pruebas para afectar la prioridad de testeo de los casos de prueba. - La prioridad de pruebas depende de la importancia de un caso de prueba +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Especificar la urgencia de la prueba"; +$TLS_htmltext['test_urgency'] = "

      Propósito

      +

      Testlink permite establecer urgencias en la Suite de pruebas para afectar la prioridad de testeo de los casos de prueba. + La prioridad de pruebas depende de la importancia de un caso de prueba y la urgencia definida en el plan de pruebas. El lider de pruebas debe especificar los casos de prueba que deberían ser testeados en primer lugar. Esto ayuda a asegurar que el testeo cubrirá las pruebas más importantes bajo la presión del tiempo.

      @@ -411,14 +402,13 @@
    • Elige una Suite de pruebas para establecer la urgencia de un producto/componente, en el navegador en el lado izquierdo de la ventana.
    • Elige un nivel de urgencia (alta, media o baja). Por defecto es media. Puede - bajarle prioridad a partes sin tocar del producto e incrementarle a componentes con + bajarle prioridad a partes sin tocar del producto e incrementarle a componentes con cambios significativos.
    • Presione el boton 'Guardar' para realizar los cambios.
    • -

      Por ejemplo, un caso de prueba con alta importancia en una suite de pruebas de baja importancia -tiene prioridad media."; - - -// ------------------------------------------------------------------------------------------ - -?> \ No newline at end of file +

      Por ejemplo, un caso de prueba con alta importancia en una suite de pruebas de baja importancia +tiene prioridad media."; + +// ------------------------------------------------------------------------------------------ + +?> diff --git a/locale/es_ES/description.php b/locale/es_ES/description.php index e53d6b1297..b6c94b5922 100644 --- a/locale/es_ES/description.php +++ b/locale/es_ES/description.php @@ -1,40 +1,40 @@ -Opciones para generar un documento

      Esta tabla permite al usuario filtrar los casos de prueba antes de ser visualizados. Si @@ -42,8 +42,8 @@ presentados, marca o desmarca, pulsa en el Filtro y selecciona el nivel de información deseada desde el árbol.

      -

      Cabecera del Documento: Los usuarios pueden filtrar la información de la cabecera. -La información de la cabecera incluye: Introducción, Alcance, Referencias, +

      Cabecera del Documento: Los usuarios pueden filtrar la información de la cabecera. +La información de la cabecera incluye: Introducción, Alcance, Referencias, Metodología de Pruebas y Limitaciones de Pruebas.

      Cuerpo del Caso de Prueba: Los usuarios pueden filtrar la información del cuerpo de los Casos de Prueba. La información del cuerpo de los Casos de Prueba @@ -58,35 +58,35 @@

      Ãndice de Contenidos: TestLink inserta una lista con todos los títulos con enlaces internos si está seleccionado.

      -

      Formato de Salida: Hay dos posibilidades: HTML y MS Word. El navegador llama al componente MS Word -en segundo caso.

      "; - -// testPlan.html +

      Formato de Salida: Hay dos posibilidades: HTML y MS Word. El navegador llama al componente MS Word +en segundo caso.

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      Plan de Pruebas

      General

      -

      Un plan de pruebas es una aproximación sistemática al testing de sistemas tales como el software. Puedes organizar las actividades de testing con +

      Un plan de pruebas es una aproximación sistemática al testing de sistemas tales como el software. Puedes organizar las actividades de testing con builds particulares para cada producto en concreto con resultados trazables en el tiempo.

      Ejecución de Pruebas

      -

      Esta sección es donde los usuarios pueden ejecutar Casos de Prueba (escribir los resultados de las pruebas) e -imprimir la suite de Casos de Prueba del Plan de Pruebas. Esta sección es donde los usuarios pueden realizar un seguimiento de -los resultados de sus ejecuciones de casos de prueba.

      +

      Esta sección es donde los usuarios pueden ejecutar Casos de Prueba (escribir los resultados de las pruebas) e +imprimir la suite de Casos de Prueba del Plan de Pruebas. Esta sección es donde los usuarios pueden realizar un seguimiento de +los resultados de sus ejecuciones de casos de prueba.

      Gestión del Plan de Pruebas

      -

      Esta sección, a la que sólo pueden acceder usuarios con determinados privilegios, permite a los usuarios administrar planes de pruebas. -La administración de planes de pruebas incluye la crear/editar/borrar planes, -añadir/editar/borrar/actualizar casos de prueba en planes, crear builds así como definir quién puede +

      Esta sección, a la que sólo pueden acceder usuarios con determinados privilegios, permite a los usuarios administrar planes de pruebas. +La administración de planes de pruebas incluye la crear/editar/borrar planes, +añadir/editar/borrar/actualizar casos de prueba en planes, crear builds así como definir quién puede ver cada plan.
      -Los usuarios con suficientes permisos pueden además establecer la prioridad/riesgo y la propiedad de -las suites de Casos de Prueba (categorías) y crear hitos de prueba.

      - -

      Nota: Es posible que los usuarios no vean ninguna lista desplegable conteniendo Planes de Pruebas. -En ese caso, todos los enlaces (excepto aquellos habilitados) estarán desenlazados. Si estás -en esa situación debes ponerte en contacto con el administrados para que te proporcione los -privilegios de proyecto pertinentes o para crear un Plan de Pruebas para ti.

      "; - -// custom_fields.html +Los usuarios con suficientes permisos pueden además establecer la prioridad/riesgo y la propiedad de +las suites de Casos de Prueba (categorías) y crear hitos de prueba.

      + +

      Nota: Es posible que los usuarios no vean ninguna lista desplegable conteniendo Planes de Pruebas. +En ese caso, todos los enlaces (excepto aquellos habilitados) estarán desenlazados. Si estás +en esa situación debes ponerte en contacto con el administrados para que te proporcione los +privilegios de proyecto pertinentes o para crear un Plan de Pruebas para ti.

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      Campos Personalizados

      Información relacionada con la implementación de campos personalizados:

        @@ -98,11 +98,11 @@
      • El número de campos personalizados no está limitado.
      -

      La definición de un campo personalizado incluye los siguientes atributos +

      La definición de un campo personalizado incluye los siguientes atributos lógicos:

      • Nombre del campo personalizado
      • -
      • Nombre de la variable (ej: Este valor es el que se +
      • Nombre de la variable (ej: Este valor es el que se proporciona a la API lang_get() , o se muestra tal y como está si no se encuentra en el fichero del idioma).
      • Tipo de campo personalizado (cadena de caracteres, numérico, float, enumeración, email)
      • Valores de la enumeración (ej: ROJO|AMARILLO|AZUL), aplicable los tipos lista, lista de selección múltiple @@ -125,16 +125,16 @@
      • Habilitar en el diseño del plan de pruebas. Los usuarios pueden cambiar el valor durante el diseño del Plan de Pruebas (añadir casos de prueba al plan de pruebas)
      • Disponible para. El usuario selecciona a qué tipo de elemento sigue el campo.
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      Ejecutar Casos de Prueba

      Permite a los usuarios 'ejecutar' casos de prueba. La ejecución en sí misma es simplemente asignar un resultado a un caso de prueba (pasado,fallado,bloqueado) asociado a una build en concreto.

      El acceso al sistema de gestión de defectos debe ser configurado. El usuario puede añadir nuevos defectos directamente -y seleccionarlos de entre los existentes. Consulta el manual de Instalación para más detalles.

      "; - -//bug_add.html +y seleccionarlos de entre los existentes. Consulta el manual de Instalación para más detalles.

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      Añadir defectos a los Caso de Prueba

      (sólo si está configurado) TestLink tiene un sistema muy simple de integración con Gestores de Defectos, @@ -144,7 +144,7 @@

    • Insertar nuevo defecto.
    • Mostrar información de un defecto existente.
    -

    +

    Proceso para añadir un nuevo defecto

    @@ -153,12 +153,12 @@

  • Paso 2: apunta el ID del defecto asignado por el Gestor de Defectos.
  • Paso 3: escribe el ID del Defecto en el campo de entrada.
  • Paso 4: usa el botón de añadir defecto.
  • - + Después de cerrar la pantalla para añadir un defecto verás información importante del defecto en la pantalla de ejecución. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Opciones de Configuración

    Las opciones de configuración te permiten seleccionar el plan de pruebas, la build y la plataforma (si existe) a @@ -184,99 +184,94 @@

    Filtro de Keyword

    -

    Puedes filtrar casos de prueba por las keywords asignadas. Puedes elegir " . -"múltiples keywords usando CTRL-Clic. Si eliges más de una keyword puedes " . -"decidir si se muestran sólo los casos de prueba que tienen asignadas todas las keywords seleccionadas " . -"(opción \"Y\") o al memos una de las keywords seleccionadas (opción \"O\").

    +

    Puedes filtrar casos de prueba por las keywords asignadas. Puedes elegir " . + "múltiples keywords usando CTRL-Clic. Si eliges más de una keyword puedes " . + "decidir si se muestran sólo los casos de prueba que tienen asignadas todas las keywords seleccionadas " . + "(opción \"Y\") o al memos una de las keywords seleccionadas (opción \"O\").

    Filtro de Prioridad

    -

    Puedes filtrar los casos de prueba por prioridad. La prioridad de prueba es la \"importancia del caso de prueba\" " . -"combinada con la \"urgencia de prueba\" dentro del plan de pruebas actual.

    +

    Puedes filtrar los casos de prueba por prioridad. La prioridad de prueba es la \"importancia del caso de prueba\" " . + "combinada con la \"urgencia de prueba\" dentro del plan de pruebas actual.

    Filtro de Usuario

    -

    Puedes filtrar casos de prueba que no están asignados (\"Nadie\") o asignados a \"Alguien\". " . -"También puedes filtrar casos de prueba asignados a un tester en concreto. Si eliges un tester " . -"en concreto tienes la posibilidad de mostrar además los casos de prueba sin asignar " . -"(hay disponibles Filtros Avanzados).

    +

    Puedes filtrar casos de prueba que no están asignados (\"Nadie\") o asignados a \"Alguien\". " . + "También puedes filtrar casos de prueba asignados a un tester en concreto. Si eliges un tester " . + "en concreto tienes la posibilidad de mostrar además los casos de prueba sin asignar " . + "(hay disponibles Filtros Avanzados).

    Filtro de Resultado

    -

    Puedes filtrar casos de prueba por resultado (hay disponibles Filtros Avanzados). Puedes filtrar por " . -"resultado \"en la build seleccionada para ejecución\", \"en la última ejecución\", \"en TODAS las builds\", " . -"\"en CUALQUIER build\" y \"en una build en concreto\". Si se selecciona \"en una build en concreto\" puedes " . -"especificar la build.

    "; - - -// newest_tcversions.html +

    Puedes filtrar casos de prueba por resultado (hay disponibles Filtros Avanzados). Puedes filtrar por " . + "resultado \"en la build seleccionada para ejecución\", \"en la última ejecución\", \"en TODAS las builds\", " . + "\"en CUALQUIER build\" y \"en una build en concreto\". Si se selecciona \"en una build en concreto\" puedes " . + "especificar la build.

    "; + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Últimas versiones de los Casos de Prueba asignados

    El total de casos de prueba asociados al Plan de Pruebas es analizado, y se muestra una lista de los Casos de Prueba con su versión más reciente (junto con la selección actual incluida en el Plan de Pruebas). -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Cobertura de Requisitos


    Esta funcionalidad permite relacionar requisitos de usuario o de sistema con casos de prueba. Puedes acceder a través del enlace \"Especificación de Requisitos\" de la pantalla principal.

    Especificación de Requisitos

    -

    Los requisitos están agrupados por documento de 'Especificación de Requisitos', que están relacionados al -Proyecto de Pruebas.
    TestLink no soporta versiones para la Especificación de Requisitos -y los Requisitos en sí mismos. Por tanto, la versión del documento debe ser añadida después de +

    Los requisitos están agrupados por documento de 'Especificación de Requisitos', que están relacionados al +Proyecto de Pruebas.
    TestLink no soporta versiones para la Especificación de Requisitos +y los Requisitos en sí mismos. Por tanto, la versión del documento debe ser añadida después de un Título de Especificación. -Un usuario puede añadir una simple descripción o notas al campo Descripción.

    +Un usuario puede añadir una simple descripción o notas al campo Descripción.

    -

    Sobreescribir el contador de REQs sirve para -evaluar la cobertura de Req. en caso de que no todos los requisitos estén añadidos (importados) a TestLink. -El valor 0 significa que el valor actual de requisitos es el que se usará para las métricas.

    -

    Ejemplo. El campo muestra un valor de 200 requisitos pero sólo 50 son añadidos a TestLink. La cobertura +

    Sobreescribir el contador de REQs sirve para +evaluar la cobertura de Req. en caso de que no todos los requisitos estén añadidos (importados) a TestLink. +El valor 0 significa que el valor actual de requisitos es el que se usará para las métricas.

    +

    Ejemplo. El campo muestra un valor de 200 requisitos pero sólo 50 son añadidos a TestLink. La cobertura de pruebas es del 25% (si todos los requisitos añadidos son probados).

    Requisitos

    Pulsa en el título de una Especificación de Requisitos. Puedes crear, editar, borrar o importar requisitos en el documento. Cada requisito tiene título, descripción y estado. El estado puede ser \"Normal\" o \"No testable\". Los requisitos No testables no son tenidos en cuenta en -las métricas. Este parámetro debería ser usado tanto para funcionalidades no implementadas como para -requisitos mal diseñados.

    +las métricas. Este parámetro debería ser usado tanto para funcionalidades no implementadas como para +requisitos mal diseñados.

    -

    Puedes crear nuevos casos de prueba desde los requsititos usando la acción múltiple con los requisitos +

    Puedes crear nuevos casos de prueba desde los requsititos usando la acción múltiple con los requisitos seleccionados en la pantalla de especificación. Estos Casos de Prueba son creados dentro de la Suite de Pruebas -con el nombre definido en la configuración (por defecto es: $tlCfg->req_cfg->default_testsuite_name = +con el nombre definido en la configuración (por defecto es: $tlCfg->req_cfg->default_testsuite_name = \"Título del Documento de Especificación de Requisitos + (generado automáticamente desde espec. req.)\";). Título y Descripción son copiados a estos casos de prueba.

    -"; - +"; + $TLS_hlp_req_coverage_table = "

    Cobertura:

    -Un valor de, por ejemplo, \"40% (8/20)\" significa que se deben crear 20 Casos de Prueba para probar este -Requisito completamente. 8 de lo cuales ya han sido creados y enlazados a este Requisito, lo cual hace +Un valor de, por ejemplo, \"40% (8/20)\" significa que se deben crear 20 Casos de Prueba para probar este +Requisito completamente. 8 de lo cuales ya han sido creados y enlazados a este Requisito, lo cual hace que la cobertura sea del 40%. -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    En relación con 'Guardar Campos personalizados'

    -Si has definidos y asignado Campos Personalizados al
    +Si has definidos y asignado Campos Personalizados al
    Proyecto de Pruebas con las opciones:
    'Mostrar en plan de pruebas' y
    'Habilitar en el diseño del plan de pruebas'
    los verás en esta pantalla SÓLO para los Casos de Prueba asignados al Plan de Pruebas. -"; - - -// resultsByTesterPerBuild.tpl +"; + +// resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = "Más información sobre los testers:
    Si pulsas en el nombre de un tester en esta tabla, verás un resumen más detallado de todos los Casos de Prueba asignados a ese usuario y su progreso de ejecución de pruebas.

    Nota:
    -Este informe muestra los casos de prueba que están asignados a un usuario en concreto y que han sido ejecutados -en la build activa. Incluso si un caso de prueba ha sido ejecutado por un usuario diferente al que tiene asignado, +Este informe muestra los casos de prueba que están asignados a un usuario en concreto y que han sido ejecutados +en la build activa. Incluso si un caso de prueba ha sido ejecutado por un usuario diferente al que tiene asignado, el caso de prueba will aparecerá como ejecutado por el usuario asignado. -"; - - -// req_edit +"; + +// req_edit $TLS_hlp_req_edit = "

    Enlaces Internos:

    -

    Los Enlaces Internos sirven para crear enlaces a otros requisitos/especificaciones de requisitos +

    Los Enlaces Internos sirven para crear enlaces a otros requisitos/especificaciones de requisitos con una sintaxis especial. El comportamiento de los Enlaces Internos puede ser modificado en el archivo de configuración.

    Uso: @@ -292,12 +287,11 @@

    Mensaje de registro para cambios:

    Siempre que se realiza un cambio, TestLink pregunta si se desea incluir un mensaje de registro. Este mensaje sirve para mantener la trazabilidad. -Si sólo ha cambiado la descripción del requisito eres libre de decidir si creas una nueva revisión o no. +Si sólo ha cambiado la descripción del requisito eres libre de decidir si creas una nueva revisión o no. Si se modifica algo más que la descripción se obliga a crear una nueva revisión.

    -"; - - -// req_view +"; + +// req_view $TLS_hlp_req_view = "

    Enlaces Directos:

    Para compartir fácilmente este documento con otros, simplemente pulsa el icono del blobo terráqueo de la parte superior de este documento para crear un enlace directo.

    @@ -310,17 +304,16 @@

    Muestra todos los casos de prueba asignados a este requisito.

    Relaciones:

    -

    Las Relaciones se usan para crear un modelo de relaciones entre requisitos. -Las relaciones personalizadas y la posibilidad de relacionar requisitos entre +

    Las Relaciones se usan para crear un modelo de relaciones entre requisitos. +Las relaciones personalizadas y la posibilidad de relacionar requisitos entre diferentes proyectos de prueba pueden ser configuradas en el archivo de configuración. -Si estableces la relación \"El Requisito A es padre del Requisito B\", +Si estableces la relación \"El Requisito A es padre del Requisito B\", TestLink establecerá la relación \"El Requisito B es hijo del Requisito A\" de forma implícita.

    -"; - - -// req_spec_edit +"; + +// req_spec_edit $TLS_hlp_req_spec_edit = "

    Enlaces Internos:

    -

    Los Enlaces Internos sirven para crear enlaces a otros requisitos/especificaciones de requisitos +

    Los Enlaces Internos sirven para crear enlaces a otros requisitos/especificaciones de requisitos con una sintaxis especial. El comportamiento de los Enlaces Internos puede ser modificado en el archivo de configuración.

    Uso: @@ -333,11 +326,10 @@ [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
    Esta sintaxis también funciona para especificaciones de requisitos (el atributo versión no se tiene en cuenta).
    Si no especificas una versión, se mostrará el requisito completo incluyendo todas las versiones.

    -"; - - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/es_ES/texts.php b/locale/es_ES/texts.php index 202c9b4388..e80fa430fb 100644 --- a/locale/es_ES/texts.php +++ b/locale/es_ES/texts.php @@ -1,45 +1,42 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * @package TestLink - * @author Martin Havlat - * @copyright 2003-2009, TestLink community - * @version CVS: $Id: texts.php,v 1.29 2010/07/22 14:14:44 asimon83 Exp $ - * @link http://www.teamst.org/index.php - * - * ------------------------------------------------------------------------------------- - * Spanish (es_ES) translation - * ------------------------------------------------------------------------------------- - * Translated by: Jesus Hernandez - * Date: 2014/11/04 - * ------------------------------------------------------------------------------------- - **/ - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Error de la Aplicación"; -$TLS_htmltext['error'] = "

    Ha ocurrido un error inesperado. Por favor, consulta el visor de eventos o " . - "los mensajes de registro para más detalles.

    Agradeceríamos que informaras del error. Por favor, visita nuestra " . - "web.

    "; - - - -$TLS_htmltext_title['assignReqs'] = "Asignar Requisitos a un Caso de Prueba"; -$TLS_htmltext['assignReqs'] = "

    Propósito:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * @package TestLink + * @author Martin Havlat + * @copyright 2003-2009, TestLink community + * @version CVS: $Id: texts.php,v 1.29 2010/07/22 14:14:44 asimon83 Exp $ + * @link http://www.teamst.org/index.php + * + * ------------------------------------------------------------------------------------- + * Spanish (es_ES) translation + * ------------------------------------------------------------------------------------- + * Translated by: Jesus Hernandez + * Date: 2014/11/04 + * ------------------------------------------------------------------------------------- + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Error de la Aplicación"; +$TLS_htmltext['error'] = "

    Ha ocurrido un error inesperado. Por favor, consulta el visor de eventos o " . + "los mensajes de registro para más detalles.

    Agradeceríamos que informaras del error. Por favor, visita nuestra " . + "web.

    "; + +$TLS_htmltext_title['assignReqs'] = "Asignar Requisitos a un Caso de Prueba"; +$TLS_htmltext['assignReqs'] = "

    Propósito:

    Esta funcionalidad permite establecer relaciones entre los Requisitos y los Casos de Prueba. Un diseñador podría definir relaciones 0..n a 0..n. Por ejemplo, un Caso de Prueba podría estar asignado a ninguno, a uno o a muchos Requisitos y viceversa. Esta matriz de trazabilidad ayuda @@ -53,54 +50,52 @@

  • Selecciona una Especificación de Requisitos si hay más de una definida. TestLink recarga la página automáticamente.
  • Aparecen dos bloques: 'Requisitos Asignados' que es la lista de todos los Requisitos de la Especificación - seleccionada que están asignados al Caso de Prueba y 'Requisitos Disponibles' que es la lista de todos los + seleccionada que están asignados al Caso de Prueba y 'Requisitos Disponibles' que es la lista de todos los Requisitos que no están asignados al Caso de Prueba actual. Un diseñador podría marcar Requisitos que están cubiertos por este Caso de Prueba y hacer click en el botón 'Asignar'. Estos nuevos Requisitos asignados al Caso de Prueba se mostrarán en el bloque de 'Requisitos Asignados'.
  • -"; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Especificación de Pruebas"; -$TLS_htmltext['editTc'] = "

    Propósito:

    -

    La Especificación de Pruebas permite a los usuarios ver y editar todo el contenido existente para " . - "Suites de Pruebas y Casos de Prueba. Los Casos de Prueba son versionados y todas " . - "las versiones anteriores están disponibles y pueden ser vistas y gestionadas desde aquí.

    +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Especificación de Pruebas"; +$TLS_htmltext['editTc'] = "

    Propósito:

    +

    La Especificación de Pruebas permite a los usuarios ver y editar todo el contenido existente para " . + "Suites de Pruebas y Casos de Prueba. Los Casos de Prueba son versionados y todas " . + "las versiones anteriores están disponibles y pueden ser vistas y gestionadas desde aquí.

    Primeros pasos:

      -
    1. Selecciona tu Proyecto de Pruebas en el árbol de navegación (el nodo raíz). Por favor, ten en cuenta que: " . - "Siempre puedes cambiar el Proyecto de Pruebas activo seleccionando uno diferente en la " . - "lista desplegable de la esquina superior derecha.
    2. -
    3. Crea una nueva Suite de Pruebas pulsando en Nueva Suite de Pruebas. Las Suites de Pruebas pueden " . - "ser utilizadas para estructurar tus documentos de prueba de acuerdo a tus necesidades (tets funcionales/no funcionales, " . - "componentes del producto o características, peticiones de cambio, etc.). La descripción de " . - "una Suite de Pruebas puede contener el alcance de los casos de prueba incluidos, la configuración por defecto, " . - "enlaces a documentos importantes, limitaciones y otra información de utilidad. En general, " . - "todas las anotaciones que son comunes a los Casos de Prueba incluidos. Las Suites de Pruebas se comportan " . - "como un directorio escalable, por lo que los usuarios pueden mover y copiar las Suites de Pruebas dentro " . - "del Proyecto de Pruebas. Además, las Suites de Pruebas pueden ser importadas o exportadas (incluyendo los casos de prueba que contienen).
    4. -
    5. Las Suites de Pruebas son directorios escalables. Los usuarios pueden mover y copiar las Suites de Pruebas dentro " . - "del Proyecto de Pruebas. Las Suites de Pruebas podrían ser importadas o exportadas (incluidos los Casos de Prueba). -
    6. Seleccionar tu recien creada Suite de Pruebas en el árbol de navegación y crea " . - "un nuevo Caso de Prueba pulsando en Crear Caso de Prueba. Un Caso de Prueba especifica " . - "un escenario de pruebas en particular, resultados esperados y campos personalizados definidos " . - "en el Proyecto de Pruebas (consulta el manual de usuario para más información). Además es posible " . - "asignar keywords para mejorar la trazabilidad.
    7. +
    8. Selecciona tu Proyecto de Pruebas en el árbol de navegación (el nodo raíz). Por favor, ten en cuenta que: " . + "Siempre puedes cambiar el Proyecto de Pruebas activo seleccionando uno diferente en la " . + "lista desplegable de la esquina superior derecha.
    9. +
    10. Crea una nueva Suite de Pruebas pulsando en Nueva Suite de Pruebas. Las Suites de Pruebas pueden " . + "ser utilizadas para estructurar tus documentos de prueba de acuerdo a tus necesidades (tets funcionales/no funcionales, " . + "componentes del producto o características, peticiones de cambio, etc.). La descripción de " . + "una Suite de Pruebas puede contener el alcance de los casos de prueba incluidos, la configuración por defecto, " . + "enlaces a documentos importantes, limitaciones y otra información de utilidad. En general, " . + "todas las anotaciones que son comunes a los Casos de Prueba incluidos. Las Suites de Pruebas se comportan " . + "como un directorio escalable, por lo que los usuarios pueden mover y copiar las Suites de Pruebas dentro " . + "del Proyecto de Pruebas. Además, las Suites de Pruebas pueden ser importadas o exportadas (incluyendo los casos de prueba que contienen).
    11. +
    12. Las Suites de Pruebas son directorios escalables. Los usuarios pueden mover y copiar las Suites de Pruebas dentro " . + "del Proyecto de Pruebas. Las Suites de Pruebas podrían ser importadas o exportadas (incluidos los Casos de Prueba). +
    13. Seleccionar tu recien creada Suite de Pruebas en el árbol de navegación y crea " . + "un nuevo Caso de Prueba pulsando en Crear Caso de Prueba. Un Caso de Prueba especifica " . + "un escenario de pruebas en particular, resultados esperados y campos personalizados definidos " . + "en el Proyecto de Pruebas (consulta el manual de usuario para más información). Además es posible " . + "asignar keywords para mejorar la trazabilidad.
    14. Navega por la vista en árbol del lado izquierdo y edite la información. Los Casos de Prueba almacenan su propio historial.
    15. Asigna tu Especificación de Pruebas al Plan de Pruebas cuando tus Casos de Prueba estén preparados.
    16. + \"javascript:open_help_window('glossary','$locale');\">Plan de Pruebas cuando tus Casos de Prueba estén preparados.
    -

    Con TestLink organizas los casos de prueba en suites de pruebas." . -"Las Suites de Pruebas pueden ser anidadas dentro de otras suites de pruebas, permitiendote crear jerarquías de suites de pruebas. - Entonces puedes imprimir esta información junto con los casos de prueba.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Pantalla de Búsqueda de Caso de Prueba"; -$TLS_htmltext['searchTc'] = "

    Propósito:

    +

    Con TestLink organizas los casos de prueba en suites de pruebas." . + "Las Suites de Pruebas pueden ser anidadas dentro de otras suites de pruebas, permitiendote crear jerarquías de suites de pruebas. + Entonces puedes imprimir esta información junto con los casos de prueba.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Pantalla de Búsqueda de Caso de Prueba"; +$TLS_htmltext['searchTc'] = "

    Propósito:

    Navegación según las keywords y/o las palabras buscadas. La búsqueda no distingue minúsculas de mayúsculas. Los resultados incluyen sólo los casos de prueba del Proyecto de Pruebas actual.

    @@ -112,12 +107,12 @@
  • Elige la keyword requerida o escribe el valor 'No aplica'.
  • Pulsa el botón 'Buscar'.
  • Todos los casos de prueba que cumplen los criterios son mostrados. Puedes modificar los casos de prueba mediante el enlace 'Título'.
  • -"; - -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Pantalla de Búsqueda de Requisitos"; -$TLS_htmltext['searchReq'] = "

    Propósito:

    +"; + +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Pantalla de Búsqueda de Requisitos"; +$TLS_htmltext['searchReq'] = "

    Propósito:

    Navegación según las keywords y/o las palabras buscadas. La búsqueda no distingue minúsculas de mayúsculas. Los resultados incluyen sólo los requisitos del Proyecto de Pruebas actual.

    @@ -135,12 +130,12 @@

    - Sólo se buscarán requisitos del proyecto de pruebas actual.
    - La búsqueda no distingue minúsculas de mayúsculas.
    -- Los campos vacíos no se tienen en cuenta.

    "; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Pantalla de Búsqueda de Especificación de Requisitos"; -$TLS_htmltext['searchReqSpec'] = "

    Propósito:

    +- Los campos vacíos no se tienen en cuenta.

    "; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Pantalla de Búsqueda de Especificación de Requisitos"; +$TLS_htmltext['searchReqSpec'] = "

    Propósito:

    Navegación según las keywords y/o las palabras buscadas. La búsqueda no distingue minúsculas de mayúsculas. Los resultados incluyen sólo las especificaciones de requisitos del Proyecto de Pruebas actual.

    @@ -158,13 +153,12 @@

    - Sólo se buscarán requisitos del proyecto de pruebas actual.
    - La búsqueda no distingue minúsculas de mayúsculas.
    -- Los campos vacíos no se tienen en cuenta.

    "; -/* end contribution */ - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificación de Pruebas"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Propósito:

    +- Los campos vacíos no se tienen en cuenta.

    "; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificación de Pruebas"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Propósito:

    Desde aquí puedes imprimir un único caso de prueba, todos los casos de prueba de una suite de pruebas, o todos los casos de prueba de un proyecto de pruebas o plan.

    Primeros pasos:

    @@ -181,19 +175,18 @@
  • Usa la funcionalidad de imprimir de tu navegador para imprimir la información.
    Nota: Asegúrate de imprimir únicamente el marco derecho de la pantalla.

  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Diseño de Especificación de Requisitos"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    Puedes gestionar documentos de Especificación de Requisitos.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Diseño de Especificación de Requisitos"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    Puedes gestionar documentos de Especificación de Requisitos.

    Especificación de Requisitos

    -

    Los requisitos están agrupados por documento de Especificación de Requisitos que están relacionados al +

    Los requisitos están agrupados por documento de Especificación de Requisitos que están relacionados al Proyecto de Pruebas.

    -

    Los documentos de Especificación de Requisitos pueden estar ordenados jerárquicamente. +

    Los documentos de Especificación de Requisitos pueden estar ordenados jerárquicamente. Crear el nivel superior de los documentos de Especificación de Requisitos ulsando en el nodo del proyecto.

    TestLink no soporta (aún) versiones para la Especificación de Requisitos @@ -201,11 +194,11 @@ un Título de Especificación. Un usuario puede añadir una simple descripción o notas al campo Descripción.

    -

    Sobreescribir el contador de REQs sirve para -evaluar la cobertura de Req. en caso de que no todos los requisitos estén añadidos a TestLink. +

    Sobreescribir el contador de REQs sirve para +evaluar la cobertura de Req. en caso de que no todos los requisitos estén añadidos a TestLink. El valor 0 significa que el valor actual de requisitos es el que se usará para las métricas.

    -

    Ejemplo. El campo muestra un valor de 200 requisitos pero sólo 50 son añadidos a TestLink. La cobertura +

    Ejemplo. El campo muestra un valor de 200 requisitos pero sólo 50 son añadidos a TestLink. La cobertura de pruebas es del 25% (si todos los requisitos añadidos son probados).

    Requisitos

    @@ -213,25 +206,24 @@

    Pulsa en el título de una Especificación de Requisitos. Puedes crrar, editar, borrar o importar requisitos en el documento. Cada requisito tiene título, descripción y estado. El estado puede ser 'Normal' o 'No testable'. Los requisitos No testables no son tenidos en cuenta en -las métricas. Este parámetro debería ser usado tanto para funcionalidades no implementadas como para +las métricas. Este parámetro debería ser usado tanto para funcionalidades no implementadas como para requisitos mal diseñados.

    -

    Puedes crear nuevos casos de prueba desde los requsititos usando la acción múltiple con los requisitos +

    Puedes crear nuevos casos de prueba desde los requsititos usando la acción múltiple con los requisitos seleccionados en la pantalla de especificación. Estos Casos de Prueba son creados dentro de la Suite de Pruebas con el nombre definido en la configuración (por defecto es: \$tlCfg->req_cfg->default_testsuite_name = 'Título del Documento de Especificación de Requisitos + (generado automáticamente desde espec. req.)';). -Título y Descripción son copiados a estos Casos de Prueba.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Imprimir Especificación de Requisitos"; //printReq +Título y Descripción son copiados a estos Casos de Prueba.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Imprimir Especificación de Requisitos"; // printReq $TLS_htmltext['printReqSpec'] = "

    Propósito:

    Desde aquí puedes imprimir un requisito, todos los requisitos de la especificación de requisitos, o todos los requisitos del proyecto de pruebas.

    Primeros pasos:

    1. -

      Selecciona las partes de los requisitos que quieres mostrar y luego pulsa en un requisito, +

      Selecciona las partes de los requisitos que quieres mostrar y luego pulsa en un requisito, especificación de requisito o proyecto de pruebas. Se mostrará una página imprimible.

    2. Usa la lista desplegable \"Mostrar como\" del panel de navegación para especificar si quieres @@ -242,12 +234,11 @@

    3. Usa la funcionalidad de imprimir de tu navegador para imprimir la información.
      Nota: Asegúrate de imprimir únicamente el marco derecho de la pantalla.

    4. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Asignación de Keyword"; -$TLS_htmltext['keywordsAssign'] = "

    Propósito:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Asignación de Keyword"; +$TLS_htmltext['keywordsAssign'] = "

    Propósito:

    La pantalla de asignación de Keywords es el lugar en el que los usuarios pueden asignar keywords a la Suite de Pruebas existente o a un Caso de Prueba

    @@ -271,16 +262,15 @@

    TestLink usa este enfoque para que las versiones antiguas de los casos de prueba en los planes de pruebas no se vean afectadas por asignaciones de keywords que realices en las versiones más nuevas de los casos de prueba. Si quieres que los casos de prueba de tu plan de pruebas estén actualizados, primero verifica que están al día utilizando la funcionalidad -'Actualizar Casos de Prueba Modificados' ANTES de realizar la asignación de keyword.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Ejecución de Caso de Prueba"; -$TLS_htmltext['executeTest'] = "

    Propósito:

    +'Actualizar Casos de Prueba Modificados' ANTES de realizar la asignación de keyword.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Ejecución de Caso de Prueba"; +$TLS_htmltext['executeTest'] = "

    Propósito:

    Permite al usuario ejecutar casos de prueba. El usuario puede asignar resultados de prueba -a un Caso de Prueba para cada Build. Consulta la ayuda para más información sobre filtrado y configuración " . - "(pulsa en el icono con el signo de interrogación).

    +a un Caso de Prueba para cada Build. Consulta la ayuda para más información sobre filtrado y configuración " . + "(pulsa en el icono con el signo de interrogación).

    Primeros pasos:

    @@ -294,13 +284,13 @@
  • Selecciona el resultado del caso de prueba y completa las notas y la asignación de defectos.
  • Guarda los resultados.
  • -

    Nota: TestLink debe ser configurado para trabajar con un Gestor de Defectos -si quieres crear/enlazar un defecto directamente desde la interfaz.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Descripción de Informes de Pruebas y Métricas"; -$TLS_htmltext['showMetrics'] = "

    Los informes están relacionados a un Plan de Pruebas " . - "(definido en la parte superior del Navegador). Este Plan de Pruebas puede ser diferente al +

    Nota: TestLink debe ser configurado para trabajar con un Gestor de Defectos +si quieres crear/enlazar un defecto directamente desde la interfaz.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Descripción de Informes de Pruebas y Métricas"; +$TLS_htmltext['showMetrics'] = "

    Los informes están relacionados a un Plan de Pruebas " . + "(definido en la parte superior del Navegador). Este Plan de Pruebas puede ser diferente al Plan de Pruebas actual para la ejecución. Además puedes seleccionar el formato del informe:

    • Normal - el informe es mostrado en una página web
    • @@ -424,12 +414,11 @@

      Defectos Totales para cada Caso de Prueba

      Este informe muestra cada caso de prueba con todos los defectos asociados en todo el proyecto de pruebas. -Este informe sólo está disponible si hay un Gestor de Defectos conectado.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Añadir / Quitar Casos de Prueba del Plan de Pruebas"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      Propósito:

      +Este informe sólo está disponible si hay un Gestor de Defectos conectado.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Añadir / Quitar Casos de Prueba del Plan de Pruebas"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      Propósito:

      Los usuarios con privilegios (con un nivel de permisos determinado) pueden añadir o quitar casos de prueba de un Plan de Pruebas.

      Añadir o quitar Casos de Prueba:

      @@ -437,11 +426,11 @@
    • Pulsa en una suite de pruebas para ver todas sus suites de pruebas y todos sus casos de prueba.
    • Posteriormente pulsa el botón 'Añadir / Quitar Casos de Prueba' para añadir o quitar los casos de prueba. Nota: No es posible añadir el mismo caso de prueba varias veces.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Asignar Testers a la ejecución de pruebas"; -$TLS_htmltext['tc_exec_assignment'] = "

      Propósito

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Asignar Testers a la ejecución de pruebas"; +$TLS_htmltext['tc_exec_assignment'] = "

      Propósito

      Esta pantalla permite a los líderes de pruebas asignar usuarios a casos de prueba particulares del Plan de Pruebas.

      Primeros pasos:

      @@ -450,15 +439,15 @@
    • Selecciona un tester.
    • Pulsa el botón para confirmar la asignación.
    • Abre la pantalla de ejecución para verificar la asignación. Puedes configurar un filtro para los usuarios.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Actualizar Casos de Prueba del Plan de Pruebas"; -$TLS_htmltext['planUpdateTC'] = "

      Propósito

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Actualizar Casos de Prueba del Plan de Pruebas"; +$TLS_htmltext['planUpdateTC'] = "

      Propósito

      Esta pantalla permite actualizar el Caso de Prueba a una nueva (diferente) versión en el caso de que la Especificación -de Pruebas haya cambiado. Sucede a menudo que alguna funcionalidad se clarifica durante el testing." . - " El usuario modifica la Especificación de Pruebas, pero es necesario que los cambios se propaguen también al Plan de Pruebas. En otro caso, el Plan" . - " de Pruebas mantiene la versión original para estar seguros de que el resultado se refiere al text correcto del Caso de Prueba.

      +de Pruebas haya cambiado. Sucede a menudo que alguna funcionalidad se clarifica durante el testing." . + " El usuario modifica la Especificación de Pruebas, pero es necesario que los cambios se propaguen también al Plan de Pruebas. En otro caso, el Plan" . + " de Pruebas mantiene la versión original para estar seguros de que el resultado se refiere al text correcto del Caso de Prueba.

      Primeros pasos:

        @@ -466,14 +455,13 @@
      1. Elige una nueva versión para un Caso de Prueba en particular.
      2. Pulsa el botón 'Actualizar Plan de Pruebas' para aplicar los cambios.
      3. Para verificar: Abre la pantalla de ejecución para ver el text del caso de prueba.
      4. -
      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Especificar casos de prueba con alta o baja urgencia"; -$TLS_htmltext['test_urgency'] = "

      Propósito

      -

      TestLink permite establecer la urgencia de una Suite de Pruebas para afectar a la Prioridad de los casos de prueba. - La prioridad de los casos depende tanto de la Importancia de los casos de prueba como de la Urgencia definida en +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Especificar casos de prueba con alta o baja urgencia"; +$TLS_htmltext['test_urgency'] = "

      Propósito

      +

      TestLink permite establecer la urgencia de una Suite de Pruebas para afectar a la Prioridad de los casos de prueba. + La prioridad de los casos depende tanto de la Importancia de los casos de prueba como de la Urgencia definida en el Plan de Pruebas. El lider de pruebas debe especificar el conjunto de casos de prueba que deberían ser probados en primer lugar. Esto ayuda a asegurar que el testing cubrirá los casos de prueba más importantes incluso bajo presiones de tiempo.

      @@ -487,10 +475,9 @@ cambios significativos.
    • Pulsa el botón 'Guardar' para aplicar los cambios.
    • -

      Por ejemplo, un Caso de Prueba con una importancia Alta en una Suite de Pruebas con Baja urgencia " . - "tendrá una prioridad Media."; - - -// ------------------------------------------------------------------------------------------ - +

      Por ejemplo, un Caso de Prueba con una importancia Alta en una Suite de Pruebas con Baja urgencia " . + "tendrá una prioridad Media."; + +// ------------------------------------------------------------------------------------------ + ?> diff --git a/locale/fi_FI/description.php b/locale/fi_FI/description.php index ac742120c8..c28ce84bc5 100644 --- a/locale/fi_FI/description.php +++ b/locale/fi_FI/description.php @@ -1,33 +1,32 @@ -Vaihtoehtoja asiakirjan luomiseen

      Tällä taulukolla käyttäjät voivat suodattattaa testitapauksia, ennen niiden näyttämistä. Jos valitut (tarkastettu) tiedot tulevat näkyviin. Muuttaaksesi tietojen esitystä, valitse tai poista, valitse Suodatus ja valitse haluamasi tiedot hakemistosta.

      @@ -40,9 +39,9 @@

      Sisällysluettelo: TestLink lisätään luettelo kaikista Nimekkeitä sisäiseen hypertekstilinkkejä jos tarkastetaan..

      -

      Esitysmuoto: Mahdollisuuksia on kaksi: HTML ja MS Word. Selain vaatii MS Word-osan toisessa tapauksessa.

      "; - -// testPlan.html +

      Esitysmuoto: Mahdollisuuksia on kaksi: HTML ja MS Word. Selain vaatii MS Word-osan toisessa tapauksessa.

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      Test Plan

      Yleistä

      @@ -55,9 +54,9 @@

      Tämä osio, joka on vain johtaa saatavilla avulla käyttäjät voivat hallita testi suunnitelmia. Hallinnoivan testi suunnitelmiin kuuluu luodaan tai muokataan / poistetaan suunnitelmien lisääminen / muokkaaminen / poistaminen / tai päivittäminen testi tapauksissa suunnitelmiin, luomalla rakentuu sekä määritellään, kuka voi nähdä, mitkä suunnitelman.
      Käyttäjät, joilla on johtaa oikeudet voivat myös asettaa ensisijaiset / riski-ja omistajanvaihdoksiin Test tapauksessa suites (luokat) ja luoda testaus välietapit.

      -

      Huom: On mahdollista, että käyttäjät eivät voi nähdä Avattavan sisältävät kaikki Test suunnitelmia. Tässä tilanteessa kaikki linkit (paitsi johtaa käytössä ones) on purettu. Jos olet tässä tilanteessa sinun tulee ottaa yhteyttä lyijyä tai admin myöntää sinulle oikea hanke oikeuksia tai luo Test Plan sinulle.

      "; - -// custom_fields.html +

      Huom: On mahdollista, että käyttäjät eivät voi nähdä Avattavan sisältävät kaikki Test suunnitelmia. Tässä tilanteessa kaikki linkit (paitsi johtaa käytössä ones) on purettu. Jos olet tässä tilanteessa sinun tulee ottaa yhteyttä lyijyä tai admin myöntää sinulle oikea hanke oikeuksia tai luo Test Plan sinulle.

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      Custom Fields

      Seuraavassa on joitakin faktoja täytäntöönpanoa mukautetun aloilla:

        @@ -90,14 +89,14 @@
      • Ota Testisivulla suunnitelman suunnitteluun. Käyttäjä voi muuttaa arvoa aikana Test Plan suunnittelu (lisää testi tapauksissa testi suunnitelma)
      • Käytettävissä. Käyttäjä päättää, millaisia erä alalla belows.
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      Executing Test Cases

      Avulla käyttäjät voivat 'toteuttaa' testi tapauksissa. Suorittaminen itse on vain siirtää testin tapauksessa tulos (siirrä, epäonnistuvat, estetty) vastaan valitun rakentaa.

      -

      Mahdollisuus käyttää vianseurantajärjestelmä voi kokoonpanosta. Käyttäjä voi suoraan lisätä uuden vikoja ja selata exesting kuin silloin.

      "; - -// bug_add.html +

      Mahdollisuus käyttää vianseurantajärjestelmä voi kokoonpanosta. Käyttäjä voi suoraan lisätä uuden vikoja ja selata exesting kuin silloin.

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      Lisää Bugs Test asia

      (vain, jos se on määritetty) TestLink on hyvin yksinkertainen integrointi Bug Tracking System (BTS), ei voi joko lähettää virheraportin creationg pyynnön BTS, ei saada takaisin bug id. Integroituminen tapahtuu linkkejä sivut BTS, että puhelut seuraavat ominaisuudet: @@ -117,51 +116,52 @@

    Kun sulkemalla lisätä bug sivulla, näet asiaa bug koskevat tiedot suorittaa sivulle. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Setup Suodatetaan ja Rakenna Testiparametrien täytäntöönpano

    -

    Vasemmanpuoleisessa ruudussa koostuu alkaen navigaattorin avulla testi tapauksissa osoitettu nykyinen". -"Test suunnitella ja taulukko, asetukset ja suodatetaan. Nämä suodattimet avulla käyttäjä ". -"tarkentaa tarjotaan joukko testi tapauksissa, ennen kuin ne on suoritettu." . -"Setup suodattimesi paina \"Käytä\" painiketta ja valitse sopiva Testitapaus" . -" puu-valikosta.

    +

    Vasemmanpuoleisessa ruudussa koostuu alkaen navigaattorin avulla testi tapauksissa osoitettu nykyinen" . + "Test suunnitella ja taulukko, asetukset ja suodatetaan. Nämä suodattimet avulla käyttäjä " . + "tarkentaa tarjotaan joukko testi tapauksissa, ennen kuin ne on suoritettu." . + "Setup suodattimesi paina \"Käytä\" painiketta ja valitse sopiva Testitapaus" . + " puu-valikosta.

    Build

    -

    Käyttäjien tulee valita rakentaa, jotka liittyvät testin tuloksen. ". -" Rakentaa ovat perusasetuksen komponentin nykyinen Test Plan. Jokainen testi " . -"voidaan käyttää useamman kerran per rakentaa. Kuitenkin viime tuloksia on laskea vain. +

    Käyttäjien tulee valita rakentaa, jotka liittyvät testin tuloksen. " . + " Rakentaa ovat perusasetuksen komponentin nykyinen Test Plan. Jokainen testi " . + "voidaan käyttää useamman kerran per rakentaa. Kuitenkin viime tuloksia on laskea vain.
    Rakentaa voidaan luoda johtaa käyttämällä Luo uusi Rakenna sivulla.

    Test Case ID filter

    Käyttäjät voivat testata suodattimet tapauksissa yksilöllinen tunniste. Tämä tunnus on luotu automaattisesti luoda aikaa. Tyhjä laatikko tarkoittaa, että suodatin ei sovelleta.

    Priority filter

    -

    Käyttäjät voivat testata suodattimet tapauksissa testi etusijalla. Jokainen testi tapauksessa tärkeää on yhdistää" . - "kanssa testi kiireellisissä nykyisen Test suunnitelma. Esimerkiksi 'KORKEAT' ensisijaisia testin tapauksessa" . -"näkyy, jos merkitystä tai kiireellisyys on KORKEA ja toisen attribuutin on vähintään 'KESKITASO' tasolla.

    +

    Käyttäjät voivat testata suodattimet tapauksissa testi etusijalla. Jokainen testi tapauksessa tärkeää on yhdistää" . + "kanssa testi kiireellisissä nykyisen Test suunnitelma. Esimerkiksi 'KORKEAT' ensisijaisia testin tapauksessa" . + "näkyy, jos merkitystä tai kiireellisyys on KORKEA ja toisen attribuutin on vähintään 'KESKITASO' tasolla.

    Result filter

    -

    Käyttäjät voivat testata suodattimet tapauksissa tuloksia. Tulokset ovat mitä tapahtui, että testi tapauksessa aikana erityisesti rakentaa. Testitapauksia voi kulkea, epäonnistuvat, on estetty, tai ei saa olla päällä. " . -"Tämä suodin on oletusarvona pois päältä.

    +

    Käyttäjät voivat testata suodattimet tapauksissa tuloksia. Tulokset ovat mitä tapahtui, että testi tapauksessa aikana erityisesti rakentaa. Testitapauksia voi kulkea, epäonnistuvat, on estetty, tai ei saa olla päällä. " . + "Tämä suodin on oletusarvona pois päältä.

    User filter

    -

    Käyttäjät voivat testata suodattimet tapauksissa niiden siirronsaajalle. Tarkastus-box mahdollistaa sisällyttää myös " . -"\"Vapaana\" testit osaksi johtanut asettaa lisäksi.

    "; -/* -

    Useimmat Nykyinen tulos

    -

    Oletusarvon tai jos 'viimeisintä' valintaruutu ei ole valittu, puussa on LAJITTELE luontinumero että valitaan avattavasta laatikko. Tässä tilassa puun näyttää testin tapauksissa tila. -
    Esimerkki: Käyttäjä valitsee rakentaa 2 avattavasta kentästä ja ei tarkistaaksesi 'viimeisintä' valintaruutu. Kaikki testin tapauksissa näkyy niiden tilan rakentaa 2. Joten, jos testi 1 hyväksyttiin rakentaa 2 on väritetty vihreäksi. -
    Jos käyttäjä decideds tarkistaaksesi 'viimeisintä' valintaruutu puussa on värillinen, että testi tapauksissa viimeisimmän tuloksen. -
    Esim: Käyttäjä valitsee rakentaa 2 avattavasta ruutuun ja tällä kertaa tarkistaa 'viimeisintä' valintaruutu. Kaikki testin tapauksissa näkyy useimpien nykyinen tila. Joten, jos testi 1 hyväksyttiin rakentaa 3, vaikka käyttäjä on myös valittu rakentaa 2, se on väritetty vihreäksi.

    "; */ - -// newest_tcversions.html +

    Käyttäjät voivat testata suodattimet tapauksissa niiden siirronsaajalle. Tarkastus-box mahdollistaa sisällyttää myös " . + "\"Vapaana\" testit osaksi johtanut asettaa lisäksi.

    "; +/* + *

    Useimmat Nykyinen tulos

    + *

    Oletusarvon tai jos 'viimeisintä' valintaruutu ei ole valittu, puussa on LAJITTELE luontinumero että valitaan avattavasta laatikko. Tässä tilassa puun näyttää testin tapauksissa tila. + *
    Esimerkki: Käyttäjä valitsee rakentaa 2 avattavasta kentästä ja ei tarkistaaksesi 'viimeisintä' valintaruutu. Kaikki testin tapauksissa näkyy niiden tilan rakentaa 2. Joten, jos testi 1 hyväksyttiin rakentaa 2 on väritetty vihreäksi. + *
    Jos käyttäjä decideds tarkistaaksesi 'viimeisintä' valintaruutu puussa on värillinen, että testi tapauksissa viimeisimmän tuloksen. + *
    Esim: Käyttäjä valitsee rakentaa 2 avattavasta ruutuun ja tällä kertaa tarkistaa 'viimeisintä' valintaruutu. Kaikki testin tapauksissa näkyy useimpien nykyinen tila. Joten, jos testi 1 hyväksyttiin rakentaa 3, vaikka käyttäjä on myös valittu rakentaa 2, se on väritetty vihreäksi.

    "; + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Uusimmat versiot liittyvät Test Cases

    Koko joukko Test asiat liittyvät Test Plan on analysoitu, ja luettelo Test asiat, joilla on uusin versio näkyy (verrattuna nykyiseen, että Test Plan). -

    "; - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Vaatimukset Kattavuus


    Tämä ominaisuus mahdollistaa kartta kattavuuskerrointa käyttäjän tai järjestelmän vaatimuksia testi tapauksissa. Navigoi kautta linkki \"Requirement Specification\" vuonna päänäyttö.

    @@ -178,19 +178,19 @@

    Klikkaa otsikkoa, joka luotiin eritelmä. Voit luoda, muokata, poistaa tai maahantuonnin vaatimukset asiakirjan. Jokainen vaatimus on nimi, laajuus ja tila. Tila olisi \ Normaali \ tai \ Ei testable \. Ei testable vaatimuksia ei lasketa ja käyttötiedot Tämä parametri olisi käyttää sekä unimplemented ominaisuuksia ja väärin suunnitellut vaatimukset.

    Voit luoda uuden testin tapauksissa vaatimukset käyttämällä useita toimia tarkastetaan vaatimukset eritelmän näytöllä. Nämä Test Cases luodaan osaksi testausohjelmisto nimi määritelty kokoonpano i (oletus on: $ tlCfg-req cfg-default testsuite nimi = \ testausohjelmisto luoma Vaatimus - Auto \;) / i. Otsikko ja Soveltamisala kopioidaan nämä testitapauksia

    -"; - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Mitä 'Tallenna Custom Fields'

    Jos olet määrittänyt, ja niihin liitetään Test Project,
    Custom Fields kanssa:
    'Näyttö testi suunnitelman suunnittelu = true' ja
    'Ota Testisivulla suunnitelman suunnittelu = true'
    näette nämä tällä sivulla AINOASTAAN Test asiat liittyvät Test Plan. -"; - -// xxx.html -$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +$TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/fi_FI/texts.php b/locale/fi_FI/texts.php index 5aa3519840..f48720382a 100644 --- a/locale/fi_FI/texts.php +++ b/locale/fi_FI/texts.php @@ -1,79 +1,77 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * Revisions history is not stored for the file - * - * - * @package TestLink - * @author Kirsi Mäkinen, Jan-Erik Finlander, Juho Kauppi, Heikki Alonen, Jari Ahonen, Otto Moilanen - * @copyright 2003-2009, TestLink community - * @version CVS: $Id: texts.php,v 1.3 2010/06/24 17:25:55 asimon83 Exp $ - * @link http://www.teamst.org/index.php - * - **/ - - -$TLS_htmltext_title['assignReqs'] = "Määritä Testitapauksen vaatimukset "; +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * Revisions history is not stored for the file + * + * + * @package TestLink + * @author Kirsi Mäkinen, Jan-Erik Finlander, Juho Kauppi, Heikki Alonen, Jari Ahonen, Otto Moilanen + * @copyright 2003-2009, TestLink community + * @version CVS: $Id: texts.php,v 1.3 2010/06/24 17:25:55 asimon83 Exp $ + * @link http://www.teamst.org/index.php + * + **/ +$TLS_htmltext_title['assignReqs'] = "Määritä Testitapauksen vaatimukset "; $TLS_htmltext['assignReqs'] = "

    Tarkoitus:

    Käyttäjät voivat asettaa suhteita ja testata tapauksissa. Testi suunnittelija voi määrittää suhteet 0..n to 0..n. I.e. Yksi testi tapauksessa voitaisiin antaa, ei yhtään, yhden tai useamman vaatimuksen ja päinvastoin. Tällainen jäljitettävyys matriisi auttaa tutkimaan testin kattavuuden vaatimukset ja selvittää, mitkä onnistuneesti jättänyt aikana testaus. Tämä analysoida toimii varmistetaan, että kaikki määritellyt odotukset ovat täyttyneet.

    Aloita:

      -
    1. Valitse Test Asia on puu milloin vasemmalle. Yhdistelmäruutuun ruutuun luettelo Vaatimukset Tekniset näkyy yläosassa olevaa workarea.
    2. +
    3. Valitse Test Asia on puu milloin vasemmalle. Yhdistelmäruutuun ruutuun luettelo Vaatimukset Tekniset näkyy yläosassa olevaa workarea.
    4. Valitse eritelmä Asiakirja jos useammat kerran määritelty.
    5. Valitse eritelmä Asiakirja jos useammat kerran määritelty. TestLink automaattisesti reloads sivua.
    6. Keski block workarea luetellaan kaikki vaatimukset (alkaen valittu Specification), jotka liittyvät testin tapauksessa. Pohja estää 'Saatavilla olevat vaatimukset' luetellaan kaikki vaatimukset, jotka eivät ole suhteessa nykyisen testin tapauksessa. Suunnittelija voi tavaramerkin vaatimuksia, jotka kuuluvat tämän testin, ja valitse sitten painiketta 'Valitse'. Nämä uudet sidottuja testi näkyvät keskellä estää 'sidotut Vaatimukset'.
    7. -
    "; - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['editTc'] = "Testauseritelmä"; +"; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['editTc'] = "Testauseritelmä"; $TLS_htmltext['editTc'] = "

    Tarkoitus:

    Tarkoitus:

    -

    The Testauseritelmä avulla käyttäjät voivat tarkastella ja muokata kaikki nykyiset " . -"Test Suites ja Testi Cases. Test Cases ovat versioidut ja kaikki " . -"ja aiemmat versiot ovat saatavilla ja niitä voidaan tarkastella ja hallinnoi täällä.

    +

    The Testauseritelmä avulla käyttäjät voivat tarkastella ja muokata kaikki nykyiset " . + "Test Suites ja Testi Cases. Test Cases ovat versioidut ja kaikki " . + "ja aiemmat versiot ovat saatavilla ja niitä voidaan tarkastella ja hallinnoi täällä.

    Aloita:

      -
    1. Valitse Test projekti navigointisivun puu (root node). Huomaa: " . -"Voit aina muuttaa aktivoida Test Project valitsemalla eri jokin " . -"alasvetovalikossa yläkulmasta oikeaan alakulmaan.
    2. -
    3. Luo uusi testausohjelmisto klikkaamalla New Child testausohjelmisto. testausohjelmisto voi " . -"saatettava rakenteen testi-asiakirjojen mukaan teidän yleissopimusten (toiminnallinen / ei-toiminnallinen" . -"testit, tuotteen osia tai toimintoja, muuttaa jne.). Kuvaus" . -"testiohjelmisto voisi pitää soveltamisalaan mukana testi tapauksissa oletuskokoonpanoon," . -"linkkejä asiaan liittyvät asiakirjat, rajoituksia ja muita hyödyllisiä tietoja. Yleensä" . -"kaikki merkinnät, jotka ovat yhteisiä lapsen Test Cases. Test Suites seurata" . -"me "kansio" metafora, näin käyttäjät voivat siirtää ja kopioida Test Suites kanssa" . -"Testaus-hankkeeseen. Lisäksi niitä voidaan tuoda maahan tai viedään maasta (mukaan lukien suljettuun testitapauksia).
    4. -
    5. Test sviitit ovat skaalattavat kansioihin. Käyttäjä voi siirtää tai kopioida Test Suites kanssa " . -"Testaus-hankkeeseen. Test sviittiä voidaan tuoda maahan tai viedä maasta (myös testitapauksia). -
    6. Valitse uudesta Test Suite-navigointi puu ja luoda" . -"uusi Test Asia klikkaamalla Luo Test Case. Test Case täsmennetään " . -"erityisesti testausta skenaario ja odotetut tulokset sekä mukautetut kentät määritelty " . -"että Test Project (katso käyttöohjetta lisätietoja). On myös mahdollista " . -"siirtää avainsanat parantaa jäljitettävyyttä.
    7. +
    8. Valitse Test projekti navigointisivun puu (root node). Huomaa: " . + "Voit aina muuttaa aktivoida Test Project valitsemalla eri jokin " . + "alasvetovalikossa yläkulmasta oikeaan alakulmaan.
    9. +
    10. Luo uusi testausohjelmisto klikkaamalla New Child testausohjelmisto. testausohjelmisto voi " . + "saatettava rakenteen testi-asiakirjojen mukaan teidän yleissopimusten (toiminnallinen / ei-toiminnallinen" . + "testit, tuotteen osia tai toimintoja, muuttaa jne.). Kuvaus" . + "testiohjelmisto voisi pitää soveltamisalaan mukana testi tapauksissa oletuskokoonpanoon," . + "linkkejä asiaan liittyvät asiakirjat, rajoituksia ja muita hyödyllisiä tietoja. Yleensä" . + "kaikki merkinnät, jotka ovat yhteisiä lapsen Test Cases. Test Suites seurata" . + "me "kansio" metafora, näin käyttäjät voivat siirtää ja kopioida Test Suites kanssa" . + "Testaus-hankkeeseen. Lisäksi niitä voidaan tuoda maahan tai viedään maasta (mukaan lukien suljettuun testitapauksia).
    11. +
    12. Test sviitit ovat skaalattavat kansioihin. Käyttäjä voi siirtää tai kopioida Test Suites kanssa " . + "Testaus-hankkeeseen. Test sviittiä voidaan tuoda maahan tai viedä maasta (myös testitapauksia). +
    13. Valitse uudesta Test Suite-navigointi puu ja luoda" . + "uusi Test Asia klikkaamalla Luo Test Case. Test Case täsmennetään " . + "erityisesti testausta skenaario ja odotetut tulokset sekä mukautetut kentät määritelty " . + "että Test Project (katso käyttöohjetta lisätietoja). On myös mahdollista " . + "siirtää avainsanat parantaa jäljitettävyyttä.
    14. Navigoi kautta puunäkymässä vasemmalla puolella ja muokata tietoja. Testitapauksia tallentaa oman historian.
    15. Anna teidän luonut testauseritelmä on Test Plan kun testitapauksia ovat valmiita.
    16. +\"javascript:open_help_window('glossary','$locale');\">Test Plan kun testitapauksia ovat valmiita.
    -

    TestLinkillä voit järjestellä testi tapauksissa osaksi testi sviittiä." . -"Test sviittiä voidaan nested muiden koe-sviittiä, joiden avulla voit luoda hierarkioita testityypin sviittiä. Voit tulostaa nämä tiedot yhdessä testin tapauksissa.

    "; - -$TLS_htmltext_title['searchTc'] = "Test Case hakusivu"; +

    TestLinkillä voit järjestellä testi tapauksissa osaksi testi sviittiä." . + "Test sviittiä voidaan nested muiden koe-sviittiä, joiden avulla voit luoda hierarkioita testityypin sviittiä. Voit tulostaa nämä tiedot yhdessä testin tapauksissa.

    "; + +$TLS_htmltext_title['searchTc'] = "Test Case hakusivu"; $TLS_htmltext['searchTc'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not @@ -86,11 +84,11 @@

  • Valitse vaaditaan avainsanan tai vasemmalle arvo 'Ei sovelleta'.
  • Napsauta Hae-painiketta.
  • Kaikki täyttyvät testi tapaukset ovat osoittaneet. Voit muokata testi tapauksissa kautta 'Otsikko'-linkkiä.
  • -";// - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['printTestSpec'] = "Tulosta testauseritelmä"; //printTC.html +"; // + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['printTestSpec'] = "Tulosta testauseritelmä"; // printTC.html $TLS_htmltext['printTestSpec'] = "

    Tarkoitus:

    Täältä voit tulostaa yhden testin tapauksessa kaikki testin tapauksissa testi sviitti tai kaikki testin tapauksissa testi hanketta tai suunnitelmaa.

    Aloita:

    @@ -105,11 +103,11 @@
  • Käytä selaimen tulosta toiminnot itse tulostaa tiedot.
    Huomaa: Varmista, että vain tulostaa oikean käden kanssa.

  • -";// - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['reqSpecMgmt'] = "Vaatimukset Eritelmä Design"; //printTC.html +"; // + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['reqSpecMgmt'] = "Vaatimukset Eritelmä Design"; // printTC.html $TLS_htmltext['reqSpecMgmt'] = "

    Voit hallita vaatimustarve määrittely asiakirjoja.

    Requirements Specification

    @@ -122,14 +120,15 @@

    Vaatimukset

    -

    Klikkaa otsikkoa olemassa oleva eritelmä. Jos ei ole," . "klikkaa hankkeen node luoda sellainen. Voit luoda, muokata, poistaa tai maahantuonnin vaatimukset asiakirjan. Jokainen vaatimus on otsikko, laajuus ja tila. A-asema olisi joko 'Normaali' tai 'Epävakaa'. Epävakaa vaatimuksia ei lasketa ja käyttötiedot Tämä parametri olisi käyttää sekä unimplemented ominaisuuksia ja väärin suunnitellut vaatimukset.

    +

    Klikkaa otsikkoa olemassa oleva eritelmä. Jos ei ole," . + "klikkaa hankkeen node luoda sellainen. Voit luoda, muokata, poistaa tai maahantuonnin vaatimukset asiakirjan. Jokainen vaatimus on otsikko, laajuus ja tila. A-asema olisi joko 'Normaali' tai 'Epävakaa'. Epävakaa vaatimuksia ei lasketa ja käyttötiedot Tämä parametri olisi käyttää sekä unimplemented ominaisuuksia ja väärin suunnitellut vaatimukset.

    Voit luoda uuden testin tapauksissa vaatimukset käyttämällä useita toimia tarkastetaan vaatimukset eritelmän näytöllä. Nämä Test Cases luodaan osaksi testausohjelmisto nimi määritelty kokoonpanoasetuksia (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Testausohjelmisto luotu Vaatimus - Auto';). Otsikko ja Soveltamisala kopioidaan nämä testitapauksia.

    ";// - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; +'Testausohjelmisto luotu Vaatimus - Auto';)
    . Otsikko ja Soveltamisala kopioidaan nämä testitapauksia.

    "; // + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; $TLS_htmltext['keywordsAssign'] = "

    Tarkoitus:

    Avainsanatyökalun Tehtävä sivu on paikka, jossa käyttäjät voivat erän antaa avainsanoja nykyisten testausohjelmisto tai Test Case

    @@ -143,10 +142,10 @@

    Tärkeitä tietoja Hakusanat Luokitusprosessin vuonna Test Plans:

    Hakusanalla tehtäviä teet sen eritelmän vain vaikutus testi tapauksissa sinun Test suunnitelmia jos ja vain jos testin suunnitelma sisältää uusimman version Testaus tapauksessa. Muuten, jos testi suunnitelma sisältää vanhoja versioita testin tapauksessa tehtäviä teet nyt ei näy testissä suunnitelma.

    -

    TestLink käyttää tätä lähestymistapaa niin, että vanhemmat versiot testi tapauksissa testi suunnitelmat eivät vaikuta Hakusanalla tehtäviä teet sen viimeisintä versiota testin tapauksessa. Jos haluat testata tapauksissa testi-suunnitelma on päivitetty, ensin vahvistaa ne ovat ajan tasalla käyttämällä 'Päivitä Modified Test Cases' toiminnallisuutta, ennen kuin teet Hakusanalla toimeksiannoissa.

    "; - -$TLS_htmltext_title['executeTest'] = "Test Case Execution"; -$TLS_htmltext['executeTest'] = "

    Tarkoitus:

    +

    TestLink käyttää tätä lähestymistapaa niin, että vanhemmat versiot testi tapauksissa testi suunnitelmat eivät vaikuta Hakusanalla tehtäviä teet sen viimeisintä versiota testin tapauksessa. Jos haluat testata tapauksissa testi-suunnitelma on päivitetty, ensin vahvistaa ne ovat ajan tasalla käyttämällä 'Päivitä Modified Test Cases' toiminnallisuutta, ennen kuin teet Hakusanalla toimeksiannoissa.

    "; + +$TLS_htmltext_title['executeTest'] = "Test Case Execution"; +$TLS_htmltext['executeTest'] = "

    Tarkoitus:

    Avulla käyttäjä voi suorittaa testitapauksia. Käyttäjä voi määrittää Testitulos Test Johdanto Build. Katso ohjeesta lisätietoja suodatin ja asetukset. (klikkaa kysymysmerkki-kuvake).

    @@ -159,14 +158,13 @@
  • Täytä testin tapauksessa johtaa ja muussa sovellettavassa muistiinpanoja tai vikoja.
  • Tallenna tulokset.
  • -

    Note: Huom: TestLink on configurated tehdä yhteistyötä teidän Bug tracker jos haluat luoda / jäljittää ongelmaraportti suoraan niiden GUI.

    "; - - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['showMetrics'] = "Kuvaus testausselosteet ja Metrics"; -$TLS_htmltext['showMetrics'] = "

    Raportit liittyvät testisuunnittelmaan". -"(määritelty alkuun navigator). Tämä Test Plan voisi poiketa nykyisen testin suunnitelman toteutusta varten. Voit myös valita raportin muodossa:

    +

    Note: Huom: TestLink on configurated tehdä yhteistyötä teidän Bug tracker jos haluat luoda / jäljittää ongelmaraportti suoraan niiden GUI.

    "; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['showMetrics'] = "Kuvaus testausselosteet ja Metrics"; +$TLS_htmltext['showMetrics'] = "

    Raportit liittyvät testisuunnittelmaan" . + "(määritelty alkuun navigator). Tämä Test Plan voisi poiketa nykyisen testin suunnitelman toteutusta varten. Voit myös valita raportin muodossa:

    • Normal - raportti näkyy Web-sivuna
    • OpenOffice Writer - raportti tuodaan OpenOffice Writeriin
    • @@ -242,9 +240,9 @@

      Palkit vuonna palkki kartat ovat värillisiä sellainen, että käyttäjä voi tunnistaa arvioitu määrä kulkea, epäonnistuvat, estetty, ei näytetä tapauksissa.

      Yhteensä Bugs Kunkin Test Case

      -

      Tämä raportti osoittaa kunkin testin tapauksessa kaikki virheraportit arkistoida sitä vastaan koko hankkeeseen. Tämä raportti on käytettävissä vain, jos Vianjäljitysjärjestelmä on kytketty.

      "; - -$TLS_htmltext_title['planAddTC'] = "Lisää / Poista testitapauksia Test Plan"; // testSetAdd +

      Tämä raportti osoittaa kunkin testin tapauksessa kaikki virheraportit arkistoida sitä vastaan koko hankkeeseen. Tämä raportti on käytettävissä vain, jos Vianjäljitysjärjestelmä on kytketty.

      "; + +$TLS_htmltext_title['planAddTC'] = "Lisää / Poista testitapauksia Test Plan"; // testSetAdd $TLS_htmltext['planAddTC'] = "

      Tarkoitus:

      Avulla käyttäjä (ja johtaa tason oikeudet) lisätä tai poistaa testi tapauksissa osaksi Test suunnitelma.

      @@ -252,9 +250,9 @@
      1. Klikkaa testausohjelmisto nähdä kaikki se testi sviittiä ja kaikki sen testin tapauksissa.
      2. Kun olet valmis, klikkaa *Lisää / Poista' Test Cases-painiketta lisätä tai poistaa testin tapauksissa. Huomautus: Ei ole mahdollista lisätä samassa testissä tapauksessa useita kertoja.
      3. -
      "; - -$TLS_htmltext_title['tc_exec_assignment'] = "Anna Tester testata täytäntöönpano"; +"; + +$TLS_htmltext_title['tc_exec_assignment'] = "Anna Tester testata täytäntöönpano"; $TLS_htmltext['tc_exec_assignment'] = "

      Tarkoitus

      Tämän sivun avulla testi johtajia määrittää käyttäjät erityisesti testeistä Testaus suunnitelma.

      @@ -264,12 +262,11 @@
    • Valitse suunniteltua testauslaite.
    • Napsauta Tallenna-painiketta esittämään luokitukseen.
    • Avaa suorittamisen sivu tarkistaa assignment. Voit perustaa suodatin käyttäjille.
    • -";// - - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['reqSpecMgmt'] = "Vaatimukset Eritelmä Design"; //printTC.html +"; // + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['reqSpecMgmt'] = "Vaatimukset Eritelmä Design"; // printTC.html $TLS_htmltext['reqSpecMgmt'] = "

      Voit hallita Requirement Specification asiakirjoja.

      Requirements Specification

      @@ -290,19 +287,19 @@ A-asema olisi joko 'Normaali' tai 'Ei testavissa'. Ei testable vaatimuksia ei lasketa ja käyttötiedot. Tämä parametri olisi käyttää sekä unimplemented ominaisuuksia ja väärin suunnitellut vaatimukset.

      Voit luoda uuden testin tapauksissa vaatimukset käyttämällä useita toimia tarkastetaan vaatimukset eritelmän näytöllä. Nämä Test Cases luodaan osaksi testausohjelmisto nimi määritelty kokoonpanoasetuksia (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Testausohjelmisto luotu Vaatimus - Auto';). Otsikko ja Soveltamisala kopioidaan nämä testitapauksia.

      "; - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; - -$TLS_htmltext['keywordsAssign'] = "

      Purpose:

      +'Testausohjelmisto luotu Vaatimus - Auto';)
      . Otsikko ja Soveltamisala kopioidaan nämä testitapauksia.

      "; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; + +$TLS_htmltext['keywordsAssign'] = "

      Purpose:

      The Keyword Assignment page is the place where users can batch assign keywords to the existing Test Suite or Test Case

      - +

      To Assign Keywords:

      @@ -324,7 +321,7 @@ - +

      Important Information Regarding Keyword Assignments in Test Plans:

      @@ -344,13 +341,11 @@ test cases in your test plan to be updated, first verify they are up to date using the 'Update -Modified Test Cases' functionality BEFORE making keyword assignments.

      "; - - - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['keywordsAssign'] = "Hakusanalla Tehtävä"; +Modified Test Cases' functionality BEFORE making keyword assignments.

      "; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['keywordsAssign'] = "Hakusanalla Tehtävä"; $TLS_htmltext['keywordsAssign'] = "

      Tarkoitus:

      Avainsanatyökalun Tehtävä sivu on paikka, jossa käyttäjät voivat erän antaa avainsanoja nykyisten testausohjelmisto tai Test asia

      @@ -363,9 +358,9 @@

      Tärkeitä tietoja Hakusanat Luokitusprosessin vuonna Test Plans:

      Hakusanalla tehtäviä teet sen eritelmän vain vaikutus testi tapauksissa sinun Test suunnitelmia jos ja vain jos testin suunnitelma sisältää uusimman version Testaus tapauksessa. Muuten, jos testi suunnitelma sisältää vanhoja versioita testin tapauksessa tehtäviä teet nyt ei näy testi suunnitelmassa.

      -

      TestLink käyttää tätä lähestymistapaa niin, että vanhemmat versiot testi tapauksissa testi suunnitelmat eivät vaikuta Hakusanalla tehtäviä teet sen viimeisintä versiota testin tapauksessa.Jos haluat testata tapauksissa testi-suunnitelma on päivitetty, ensin vahvistaa ne ovat ajan tasalla käyttämällä 'Päivitä Modified Test Cases' toiminnallisuutta, ennen kuin teet Hakusanalla toimeksiannoissa.

      "; - -$TLS_htmltext_title['executeTest'] = "Test Asia Suoritusaika"; +

      TestLink käyttää tätä lähestymistapaa niin, että vanhemmat versiot testi tapauksissa testi suunnitelmat eivät vaikuta Hakusanalla tehtäviä teet sen viimeisintä versiota testin tapauksessa.Jos haluat testata tapauksissa testi-suunnitelma on päivitetty, ensin vahvistaa ne ovat ajan tasalla käyttämällä 'Päivitä Modified Test Cases' toiminnallisuutta, ennen kuin teet Hakusanalla toimeksiannoissa.

      "; + +$TLS_htmltext_title['executeTest'] = "Test Asia Suoritusaika"; $TLS_htmltext['executeTest'] = "

      Purpose:

      Avulla käyttäjä voi suorittaa testitapauksia. Käyttäjä voi määrittää Testitulos Test Johdanto Build. Katso ohjeesta lisätietoja suodatin ja asetukset. (klikkaa kysymysmerkki-kuvake).

      @@ -379,12 +374,13 @@
    • Täytä testin tapauksessa johtaa ja muussa sovellettavassa muistiinpanoja tai vikoja.
    • Tallenna tulokset.
    • -

      Huom: TestLink on configurated tehdä yhteistyötä teidän Bug tracker jos haluat luoda / jäljittää ongelmaraportti suoraan niiden GU

      ";// - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['showMetrics'] = "Kuvaus testausselosteet ja mittarit"; -$TLS_htmltext['showMetrics'] = "

      Raportit liittyvät testisuunnitelmaan " ." +

      Huom: TestLink on configurated tehdä yhteistyötä teidän Bug tracker jos haluat luoda / jäljittää ongelmaraportti suoraan niiden GU

      "; // + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['showMetrics'] = "Kuvaus testausselosteet ja mittarit"; +$TLS_htmltext['showMetrics'] = "

      Raportit liittyvät testisuunnitelmaan " . + " (määritelty alkuun navigator). Tämä Test Plan voisi poiketa nykyisen testin suunnitelman toteutusta varten. Voit myös valita raportin muodossa:

      • Normaalitila - raportti näkyy Web-sivun
      • @@ -463,13 +459,11 @@

        Palkit vuonna palkki kartat ovat värillisiä sellainen, että käyttäjä voi tunnistaa arvioitu määrä kulkea, epäonnistuvat, estetty, ei näytetä tapauksissa.

        Yhteensä Bugs jokaisessa testissä asia

        -

        Tämä raportti osoittaa kunkin testin tapauksessa kaikki virheraportit arkistoida sitä vastaan koko hankkeeseen. Tämä raportti on käytettävissä vain, jos Vianjäljitysjärjestelmä on kytketty.

        "; - - - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['planAddTC'] = "Lisää / Poista testitapauksia Testisuunnitelmaan"; //testSetAdd +

        Tämä raportti osoittaa kunkin testin tapauksessa kaikki virheraportit arkistoida sitä vastaan koko hankkeeseen. Tämä raportti on käytettävissä vain, jos Vianjäljitysjärjestelmä on kytketty.

        "; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['planAddTC'] = "Lisää / Poista testitapauksia Testisuunnitelmaan"; // testSetAdd $TLS_htmltext['planAddTC'] = "

        Päämäärä

        Avulla käyttäjä (ja johtaa tason oikeudet) lisätä tai poistaa testi tapauksissa osaksi Test suunnitelma.

        @@ -477,13 +471,11 @@
        1. Klikkaa testausohjelmistoa nähdäksesi kaikki se testi sviittiä ja kaikki sen testitapaukset.
        2. Kun olet valmis, klikkaa 'Lisää / Poista' Test Cases-painiketta lisätä tai poistaa testin tapauksissa. Huomautus: Ei ole mahdollista lisätä samassa testissä tapauksessa useita kertoja.
        3. -
        ";// - - - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['tc_exec_assignment'] = "Anna Testaajan testata täytäntöönpano"; +"; // + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['tc_exec_assignment'] = "Anna Testaajan testata täytäntöönpano"; $TLS_htmltext['tc_exec_assignment'] = "

        Päämäärä

        Tämän sivun avulla testi johtajia määrittää käyttäjät erityisesti testeistä Testaus suunnitelma.

        @@ -493,12 +485,11 @@
      • Valitse suunniteltu testaaja.
      • Napsauta Tallenna-painiketta esittämään luokitukseen.
      • Avaa suorittamisen sivu tarkistaa assignment. Voit perustaa suodatin käyttäjille.
      • -"; - -// ------------------------------------------------------------------------------------------ - - -$TLS_htmltext_title['planUpdateTC'] = "Päivitä Testitapaukset, Testisuunnitelmassa"; +"; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['planUpdateTC'] = "Päivitä Testitapaukset, Testisuunnitelmassa"; $TLS_htmltext['planUpdateTC'] = "

        Päämäärä

        Tämän sivun avulla päivittäminen Test tapauksessa uudempaan (eri) versio, jos testauseritelmä on muuttunut. On harvinaista, että joitakin toimintoja on selkeytetty testauksen aikana. . Käyttäjä muuttaa testauseritelmä, mutta muutoksia on propagoivat Test Plan liikaa. Muuten Test suunnitelma omistaa alkuperäinen versio, jotta voitaisiin varmistaa, että tulokset viittaavat oikea teksti testin tapauksessa.

        @@ -508,14 +499,13 @@
      • Valitse uusi versio combo-box-valikosta tietyn Testitapauksessa mukaan.
      • Napsauta Päivitä testisuunnitelma-painiketta esittää muutoksia.
      • Voit tarkistaa: Avaa suorittamisen sivun avulla voit katsella teksti testin tapauksessa.
      • -"; - - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; - -$TLS_htmltext['test_urgency'] = "

        Purpose

        +"; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; + +$TLS_htmltext['test_urgency'] = "

        Purpose

        TestLink allows setting the urgency of a Test Suite to affect the testing Priority of test cases. @@ -527,7 +517,7 @@ also under time pressure.

        - +

        Aloita

        @@ -547,14 +537,10 @@ -

        For example, a Test case with a High importance in a Test suite with Low urgency " . - -"will be Medium priority."; - - - -// ------------------------------------------------------------------------------------------ - - - -?> \ No newline at end of file +

        For example, a Test case with a High importance in a Test suite with Low urgency " . + + "will be Medium priority."; + +// ------------------------------------------------------------------------------------------ + +?> diff --git a/locale/fr_FR/description.php b/locale/fr_FR/description.php index 781899a94e..2f0a465794 100644 --- a/locale/fr_FR/description.php +++ b/locale/fr_FR/description.php @@ -1,50 +1,49 @@ - comment only with "//" except header - * 2. for JS string you must use \\n to get \n for end of line - * - * ******************************************************************************************** - * - **/ - - -// printFilter.html + comment only with "//" except header + * 2. for JS string you must use \\n to get \n for end of line + * + * ******************************************************************************************** + * + **/ + +// printFilter.html $TLS_hlp_generateDocOptions = "

        Options pour un document généré

        -

        Cette table permet à l’utilisateur de filtrer les fiches de test avant leur visualisation. Si elles sont sélectionnées, leurs données sont affichées. Pour modifier les données présentées, cocher ou décocher, cliquer sur Filtre et sélectionner le niveau des données voulues depuis l’arborescence.

        "; - -// testPlan.html +

        Cette table permet à l’utilisateur de filtrer les fiches de test avant leur visualisation. Si elles sont sélectionnées, leurs données sont affichées. Pour modifier les données présentées, cocher ou décocher, cliquer sur Filtre et sélectionner le niveau des données voulues depuis l’arborescence.

        "; + +// testPlan.html $TLS_hlp_testPlan = "

        Campagne de test

        Général

        Une campagne de test est une approche systématique pour tester un système ou un logiciel. Il est possible d’organiser l’activité de test avec des versions du produit, des plateformes pour tracer les résultats.

        Campagne de test

        -

        Cette section permet l'administration des campagnes de test. Administrer les campagnes de test implique la création/modification/suppression de campagnes, l’ajout/modification/suppression de version du produit pour chaque campagne et l’ajout/modification/suppression d'indicateurs d'avancement.

        +

        Cette section permet l'administration des campagnes de test. Administrer les campagnes de test implique la création/modification/suppression de campagnes, l’ajout/modification/suppression de version du produit pour chaque campagne et l’ajout/modification/suppression d'indicateurs d'avancement.

        Contenu de la campagne de test

        -

        Cette section permet la définition du contenu d'une campagne de test. Gérer le contenu d'une campagne de test implique la définition des platesformes utilisées dans la campagne, la définition des fiches de test utilisées dans la campagne, l'assignation éventuelle des fiches de test à des utilisateurs liés à la campagne et la définition de l'urgence des tests. Au cours de la réalisation, les versions de fiches de tests peuvent également être mises à jour si de nouvelles versions de fiches de tests ont été créées.

        +

        Cette section permet la définition du contenu d'une campagne de test. Gérer le contenu d'une campagne de test implique la définition des platesformes utilisées dans la campagne, la définition des fiches de test utilisées dans la campagne, l'assignation éventuelle des fiches de test à des utilisateurs liés à la campagne et la définition de l'urgence des tests. Au cours de la réalisation, les versions de fiches de tests peuvent également être mises à jour si de nouvelles versions de fiches de tests ont été créées.

        Exécution des fiches de test

        -

        Cette section est celle où les utilisateurs peuvent exécuter les fiches de test (écrire des résultats de test) et imprimer les suites de test de la campagne de test. Cette section est où les utilisateurs peuvent tracer les résultats de leur exécution de fiches de test.

        - -

        Remarque: Il est possible que les utilisateurs puissent ne pas voir de liste déroulante avec les campagnes de test. Dans ce cas, tous les liens (sauf ceux actifs pour le test leader) seront indisponibles. Si tel est le cas, veuillez contacter le test leader ou l’administrateur pour vous donner les droits du projet qui convienne ou pour vous créer une campagne de test.

        "; +

        Cette section est celle où les utilisateurs peuvent exécuter les fiches de test (écrire des résultats de test) et imprimer les suites de test de la campagne de test. Cette section est où les utilisateurs peuvent tracer les résultats de leur exécution de fiches de test.

        -// custom_fields.html +

        Remarque: Il est possible que les utilisateurs puissent ne pas voir de liste déroulante avec les campagnes de test. Dans ce cas, tous les liens (sauf ceux actifs pour le test leader) seront indisponibles. Si tel est le cas, veuillez contacter le test leader ou l’administrateur pour vous donner les droits du projet qui convienne ou pour vous créer une campagne de test.

        "; + +// custom_fields.html $TLS_hlp_customFields = "

        Champs personnalisés

        Les points suivants sont des éléments sur l’implémentation des champs personnalisés:

          @@ -76,14 +75,14 @@
        • Activer sur la conception de campagne de test; l’utilisateur peut modifier la valeur pendant la conception de la campagne de test (ajouter des fiches de test à la campagne);
        • Disponible pour; l’utilisateur choisit quelle sorte d’objet est sous le champ.
        -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

        Exécution de fiches de test

        Permet aux utilisateurs d’exécuter les fiches de test. L’exécution en elle-même n’est qu’une assignation à chaque version d'une fiche de test d’un résultat (réussi, en échec, bloqué) pour une version du produit (livraison) donnée.

        -

        L’accès à un système de gestion d’anomalie peut être configuré. L’utilisateur peut alors ajouter directement de nouvelles anomalies et rechercher celles existantes. Voir le manuel d’installation pour d’avantage de détails.

        "; - -//bug_add.html +

        L’accès à un système de gestion d’anomalie peut être configuré. L’utilisateur peut alors ajouter directement de nouvelles anomalies et rechercher celles existantes. Voir le manuel d’installation pour d’avantage de détails.

        "; + +// bug_add.html $TLS_hlp_btsIntegration = "

        Ajout d’anomalie à la fiche de test

        (Seulement si c’est configuré) TestLink a une intégration très simple avec les systèmes de gestion d’anomalies, qui n’est ni capable d’envoyer de requête de création au système, ni récupérer le bug id. L’intégration est faite par des liens aux pages du système de gestion d’anomalie, qui appelle les fonctionnalités suivantes: @@ -91,7 +90,7 @@

      • Insertion d’une nouvelle anomalie;
      • Affichage des informations de l’anomalie.
      -

      +

      Processus d’ajout d’anomalie

      @@ -100,12 +99,12 @@

    • Etape 2: retenir le BUGID assigné par le système;
    • Etape 3: renseigner le champ de Testlink avec le BUGID récupéré;
    • Etape 4: utiliser le bouton d’ajout d’anomalie.
    • -
    + Après fermeture de la fenêtre d’ajout d’anomalie, l’anomalie apparaît dans la page d’exécution. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Propriétés

    Les propriétés permettent de sélectionner la campagne de test, le Build et la plateforme (si disponible) à exécuter.

    @@ -129,45 +128,42 @@

    Il est possible de filtrer les fiches de test par mots-clés qui leurs ont été affectés. Il est possible de choisir plusieurs mots-clés en utilisant CTRL-Clic. Si vous choisissez plus d’un mot-clé, vous pouvez choisir le mode \"And\" ou \"Or\" pour le filtre.

    Filtre de priorité

    -

    Il est possible de filtrer les fiches de test par priorité, \"Criticité de fiches de test\" combiné à \"Urgence de test\" dans la campagne de test courante.

    +

    Il est possible de filtrer les fiches de test par priorité, \"Criticité de fiches de test\" combiné à \"Urgence de test\" dans la campagne de test courante.

    Filtre utilisateur

    Il est possible de filtrer les fiches de test affectées ou non à quelqu’un, et également à un utilisateur spécifique (avec inclusion des fiches non affectés ou non - les filtres avancés sont disponibles).

    Filtre de résultat

    -

    Il est possible de filtrer les fiches de test par résultat(les filtres avancés sont disponibles), sur la version du produit choisi pour l’exécution, sur la dernière exécution, sur tous les versions du produit, n’importe quel version du produit ou sur une version du produit spécifique.

    "; - - -// newest_tcversions.html +

    Il est possible de filtrer les fiches de test par résultat(les filtres avancés sont disponibles), sur la version du produit choisi pour l’exécution, sur la dernière exécution, sur tous les versions du produit, n’importe quel version du produit ou sur une version du produit spécifique.

    "; + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Dernière version des fiches de test liée

    Toute la série des fiches de test liées à une campagne de test est analysée, et la liste des fiches de test qui ont une nouvelle version disponible est affichée (par rapport aux versions courantes liées à la campagne de test). -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Récapitulatif d'exigences


    La fonctionnalité permet de cartographier la couverture des exigences utilisateur ou système par fiche de test.

    Dossier d’exigences

    -

    Les exigences sont regroupées dans des dossiers d’exigences qui sont liés au projet de test.
    TestLink ne supporte pas les versions des dossiers d’exigences et en même temps des exigences: il faut d’abord faire une version d’exigence avec d’effectuer celle du dossier.Titre. Un utilisateur peut ajouter une simple description ou des notes au champ Périmètre.

    +

    Les exigences sont regroupées dans des dossiers d’exigences qui sont liés au projet de test.
    TestLink ne supporte pas les versions des dossiers d’exigences et en même temps des exigences: il faut d’abord faire une version d’exigence avec d’effectuer celle du dossier.Titre. Un utilisateur peut ajouter une simple description ou des notes au champ Périmètre.

    -

    Le comptage surchargé d’exigences sert d’évaluation à la couverture d’exigences dans le cas où toutes les exigences ne sont pas ajoutées (importées). La valeur 0 signifie que le comptage courant d’exigences est utilisé pour les métriques.

    +

    Le comptage surchargé d’exigences sert d’évaluation à la couverture d’exigences dans le cas où toutes les exigences ne sont pas ajoutées (importées). La valeur 0 signifie que le comptage courant d’exigences est utilisé pour les métriques.

    Par exemple: le dossier d’exigences compte 200 exigences mais seulement 50 sont ajoutées dans Testlink. La couverture est de 25% (si toutes les exigences ajoutées sont testées).

    Exigences

    -

    Cliquer sur le titre des dossiers d’exigences créés. il est possible de créer, modifier, supprimer ou importer les exigences du cahier de test. Chaque exigence a un titre, un contexte et un statut. Le statut peut être \"Normal\" ou \"Non testable\". Les exigences non testables ne sont pas comptées dans les métriques. Ce paramètre peut être utilisé pour des fonctionnalités non implémentées et des exigences mal conçues.

    +

    Cliquer sur le titre des dossiers d’exigences créés. il est possible de créer, modifier, supprimer ou importer les exigences du cahier de test. Chaque exigence a un titre, un contexte et un statut. Le statut peut être \"Normal\" ou \"Non testable\". Les exigences non testables ne sont pas comptées dans les métriques. Ce paramètre peut être utilisé pour des fonctionnalités non implémentées et des exigences mal conçues.

    -

    Il est possible de créer de nouvelles fiches de test pour les exigences en utilisant l’action multiple avec les exigences sélectionnées dans l’écran du cahier. Les fiches de test sont créés dans le dossier de test avec le nom défini en configuration (par défaut: $tlCfg->req_cfg->default_testsuite_name = +

    Il est possible de créer de nouvelles fiches de test pour les exigences en utilisant l’action multiple avec les exigences sélectionnées dans l’écran du cahier. Les fiches de test sont créés dans le dossier de test avec le nom défini en configuration (par défaut: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Le titre et le contexte sont copiés dans le cas de test.

    -"; - +"; + $TLS_hlp_req_coverage_table = "

    Couverture:

    Une valeur de \"40% (8/20)\" signifie que 20 fiches de test doivent être créés pour l’exigence pour la tester entièrement, 8 de ces fiches sont déjà créés et liés à l’exigence, ce qui fait une couverture de 40%. -"; - - -// req_edit +"; + +// req_edit $TLS_hlp_req_edit = "

    Liens internes au contexte:

    Les liens internes permettent de créer des liens vers d’autres exigences ou dossiers d’exigences avec une syntaxe spéciale. Le comportement des liens internes peuvent être changé dans le fichier de configuration.

    @@ -182,10 +178,9 @@

    Message de log pour modifications:

    Pour toute modification, Testlink demande un message de log servant à la traçabilité. Si le périmètre de l’exigence a été modifié, vous être libre de choisir de créer ou non une nouvelle révision. Quand toute autre modification est apportée, la création d’une nouvelle révision est obligatoire.

    -"; - - -// req_view +"; + +// req_view $TLS_hlp_req_view = "

    Liens directs:

    Pour facilement partager le document avec d’autres personnes, cliquer tout simplement sur l’icône globe en haut du document pour créer un lien direct.

    @@ -197,10 +192,9 @@

    Relations:

    Les relations d’exigence sont utilisées pour modéliser les relations entre les exigences. Les relations personnalisées et l’option pour autoriser les relations entre exigences de différents projets de test peuvent être configurées dans le fichier de configuration. Si vous définissez une relation \"Exigence A est parent de Exigence B\", Testlink définit implicitement la relation \"Exigence B est enfant de Exigence A\".

    -"; - - -// req_spec_edit +"; + +// req_spec_edit $TLS_hlp_req_spec_edit = "

    Liens internes dans le contexte:

    Les liens internes permettent de créer des liens vers d’autres exigences/dossiers d’exigences avec une syntaxe spéciale. Le comportement des liens internes peut être modifié dans le fichier de configuration.

    @@ -213,28 +207,25 @@ [req tproj=<tproj_prefix> anchor=<anchor_name>version=<version_number>]req_doc_id[/req]
    Cette syntaxe fonctionne également pour les dossiers d’exigences (l’attribut de version n’a aucun effet).
    Si vous ne voulez pas définir une version, l’exigence avec toutes ses versions est affichée.

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Concernant ’Enregistrer les champs personnalisés’

    -Si des champs personnalisés ont été définis et affectés au projet, avec:
    +Si des champs personnalisés ont été définis et affectés au projet, avec:
    ’Afficher sur la conception de campagne de test=true’ et
    ’Activer sur la conception de campagne de test=true’
    Les champs sont visibles sur la page uniquement pour les fiches de test liées à la campagne de test. -"; - - -// resultsByTesterPerBuild.tpl +"; + +// resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = "Plus d’informations à propos des testeurs:
    Si vous cliquez sur le nom d’un testeur dans le tableau, un récapitulatif détaillé de toutes les fiches de test affectées à l’utilisateur et sa progression d'avancement sont affichés.

    Remarque:
    Le rapport affiche les fiches de test qui sont affectées à un utilisateur spécifique et qui ont été exécutées pour chaque version du produit active. Même si une fiche de test a été exécutée par un autre utilisateur que l’utilisateur affecté, la fiche de test est affichée comme exécutée pour l’utilisateur affecté. -"; - - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/fr_FR/strings.txt b/locale/fr_FR/strings.txt index 01c1acd5a3..9095c590b2 100644 --- a/locale/fr_FR/strings.txt +++ b/locale/fr_FR/strings.txt @@ -314,7 +314,9 @@ $TLS_btn_remove_all_users = "Supprimer TOUTES les affectations"; $TLS_btn_apply_assign = "Appliquer l'affectation"; $TLS_btn_save_assign = "Sauver les affectations"; $TLS_btn_remove = "Supprimer"; -$TLS_btn_remove_assignments = "Supprimer les affections"; +$TLS_btn_remove_assignments = "Supprimer les affectations"; +$TLS_btn_remove_current_assignments = "Supprimer toutes les affectations"; + // Status (used wide) @@ -1606,10 +1608,10 @@ $TLS_info_milestone_create_no_prio = "Les indicateurs d'avancement prennent en c "de la campagne de test"; $TLS_th_date_format = "Date cible"; -$TLS_th_perc_a_prio = "Exéutions réalisées avec priorité haute [0-100%]"; -$TLS_th_perc_b_prio = "Exéutions réalisées avec priorité moyenne [0-100%]"; -$TLS_th_perc_c_prio = "Exéutions réalisées avec priorité basse [0-100%]"; -$TLS_th_perc_testcases = "Exéutions réalisées [0-100%]"; +$TLS_th_perc_a_prio = "Exécutions réalisées avec priorité haute [0-100%]"; +$TLS_th_perc_b_prio = "Exécutions réalisées avec priorité moyenne [0-100%]"; +$TLS_th_perc_c_prio = "Exécutions réalisées avec priorité basse [0-100%]"; +$TLS_th_perc_testcases = "Exécutions réalisées [0-100%]"; $TLS_title_existing_milestones = "Indicateurs d'avancement existants"; $TLS_title_milestones = "Indicateurs d'avancement de la campagne de test"; $TLS_delete_milestone = "Supprimer l'indicateur d'avancement"; @@ -1915,10 +1917,7 @@ $TLS_info_report_milestones_no_prio = "Ce rapport affiche les indicateurs d'avan $TLS_info_res_by_kw = "Ce rapport affiche les résultats pour chaque mot-clé. Si une fiche de test a de multiples " . "mots-clés affectés, cela est comptabilisé pour chaque mot-clé affecté."; $TLS_info_gen_test_rep = "Information générale
    " . - "Tous les rapports considèrent uniquement la dernière exécution pour chaque fiche de test.
    " . - "Vous pouvez ordonner les tables en cliquant sur les titres de colonne."; - - + "Tous les rapports se basent uniquement la dernière exécution pour chaque fiche de test.
    "; // ----- gui/templates/resultsMoreBuilds_query_form.tpl ----- $TLS_results_latest = "Derniers résultats uniquement"; @@ -2632,7 +2631,7 @@ $TLS_kw_update_fails = "Erreur lors de la mise à jour du mot-clé"; $TLS_assigned_kword = "Mot-clés affectés"; $TLS_available_kword = "Mot-clés disponibles"; $TLS_kword_to_be_assigned_to_testcases = "Mots-clés à affecter aux fiches de test"; -$TLS_assignToFilteredTestCases = 'Affecter SEULEMENT aux fiches de test filtrées'; +$TLS_assignToFilteredTestCases = 'Affecter seulement aux fiches de test filtrées'; @@ -3149,10 +3148,12 @@ $TLS_API_UPDATER_INSUFFICIENT_RIGHTS = "L'utilisateur %s " . "n'a pas les droits suffisants pour effectuer cette action." . " - Détails : droit %s, Identifiant Projet : %s, Identifiant Campagne de test : %s"; +$TLS_API_MUST_BE_ADMIN = "Cette fonction est réservée aux administrateurs du site."; $TLS_API_NO_DEV_KEY = "Aucune clé de développeur fournie."; $TLS_API_NO_TCASEID = "Aucun ID de fiche de test(ID interne) fourni."; $TLS_API_NO_TCASEEXTERNALID = "Aucun ID externe de fiche de test fourni."; +$TLS_API_NO_TCASEVERSIONID = "Aucun ID de version de fiche de test fourni."; $TLS_API_NO_TPLANID = "Aucun ID de campagne de test fourni."; $TLS_API_NO_TEST_MODE = "Le mode de test est obligatoire."; $TLS_API_NO_STATUS = "Aucun état fourni."; @@ -3162,15 +3163,21 @@ $TLS_API_NO_TESTSUITEID = "Aucun ID de dossier de test fourni."; $TLS_API_NO_TPID = "Aucun ID de campagne de test fourni."; $TLS_API_NO_TCID = "Aucun ID de fiche de test fourni."; $TLS_API_NO_PLATFORMID = "Aucun ID de plateforme fourni."; +$TLS_API_NO_USERID = "Aucun ID utilisateur fourni."; +$TLS_API_NO_ROLEID = "Aucun ID de rôle fourni."; $TLS_API_INVALID_TCASEID = "ID de fiche de test %s inconnu."; +$TLS_API_INVALID_TCASEID = "ID de version de fiche de test %s inconnu."; $TLS_API_INVALID_BUILDID = "ID de version du produit %s inconnu."; $TLS_API_INVALID_TPLANID = "ID de campagne de test %s inconnu."; $TLS_API_INVALID_STATUS = "Etat %s non valide."; $TLS_API_INVALID_TESTCASE_EXTERNAL_ID = "ID externe de fiche de test %s inconnu."; +$TLS_API_INVALID_USERID = "ID utilisateur %s inconnu."; +$TLS_API_INVALID_ROLEID = "ID de rôle %s inconnu."; $TLS_API_TCASEID_NOT_INTEGER = "L’ID de fiche de test doit être un entier."; +$TLS_API_TCASEID_NOT_INTEGER = "L’ID de version de fiche de test doit être un entier."; $TLS_API_TESTCASENAME_NOT_STRING = "Le nom de la fiche de test doit être une chaine de caractères."; $TLS_API_NO_BUILD_FOR_TPLANID = "Aucune version du produit existante pour l’ID de campagne de test fourni. " . "Une version du produit doit être créée pour pouvoir enregistrer des résultats."; @@ -3296,6 +3303,11 @@ $TLS_API_ITS_NOT_FOUND = "ITS %s introuvable."; $TLS_API_TSUITE_NOT_ON_TCASE_TPROJ = "Le dossier de tests n'appartient pas à ce projet"; +$TLS_API_USER_LOGIN_DOESNOT_EXIST = "Login utilisateur %s inconnu."; + +$TLS_API_ROLE_NAME_DOESNOT_EXIST = "Role %s inconnu."; +$TLS_API_ROLE_SETTING_ERROR = "Une erreur est survenue lors de l'affectation du rôle %s à l'utilisateur %s. Veuillez consulter les logs."; + // ----- audit log entries ----- $TLS_audit_login_succeeded = "Identification pour '{%1}' depuis '{%2}' réussie"; $TLS_audit_login_failed = "Identification pour '{%1}' depuis '{%2}' échouée"; @@ -3732,6 +3744,8 @@ $TLS_issue_tplan = "Campagne : %s"; $TLS_issue_build = " Version du produit : %s"; $TLS_issue_platform = " Platforme : %s"; $TLS_issue_exec_result = " Résultat d’exécution : %s"; +$TLS_tc_name = "Test : %s"; +$TLS_tc_external_id = "Test ID : %s"; $TLS_issue_generated_description = " ID d’exécution : %s \n Testeur : %s \n Campagne : %s \n"; $TLS_issue_subject_projectname = "Projet"; @@ -3755,6 +3769,8 @@ $TLS_gitlab_bug_created = "Anomalie créée par GITLAB (résumé:%s) dans le pro $TLS_gitlab_bug_comment = "Anomalie commentée par GITLAB (résumé:%s)"; $TLS_kaiten_bug_created = "Anomalie créée par KAITEN (résumé:%s) dans le projet d’identifiant : %s"; $TLS_kaiten_bug_comment = "Anomalie commentée par KAITEN (résumé:%s)"; +$TLS_tuleap_bug_created = "Anomalie créée par TULEAP (résumé : %s) dans le projet d’identifiant : %s"; +$TLS_tuleap_bug_comment = "Anomalie commentée par TULEAP (résumé : %s)"; $TLS_bts_check_ok = "La connexion est OK"; $TLS_bts_check_ko = "La connexion est KO (plus de détails dans le moniteur d’événements). Vérifier la configuration"; $TLS_check_bts_connection = "Vérifier la connexion"; @@ -3835,6 +3851,9 @@ $TLS_add_link_to_tlexec_print_view = "Ajouter un lien dans le gestionnaire d’a $TLS_dl2tl = "Lien pour l’exécution TestLink : "; $TLS_dl2tlpv = "Lien pour la vue d’impression de l’exécution TestLink : "; +$TLS_dlToTCSpecPV = 'Lien vers un cas de test Testlink : '; +$TLS_dlToTCSpecPVCode = '[TLTC@:%s]'; + $TLS_old_style_login = 'Ecran de connexion bien connu'; $TLS_new_style_login = 'Essayer le nouvel écran de connexion'; @@ -3947,6 +3966,9 @@ $TLS_copy_attachments_from_latest_exec = 'Copier les pièces attachées de la de $TLS_exec_tree_counters_logic = 'Compter les dernières exécutions à partir de'; $TLS_use_latest_exec_on_testplan_for_counters = 'Campagne'; $TLS_use_latest_exec_on_contex_for_counters = 'Build sélectionné'; +$TLS_use_latest_exec_on_testplan_plat_for_counters = + 'Campagne de la plateforme'; + $TLS_download_csv = 'Télécharger en CSV'; $TLS_select_platforms = "Sélectionner les plateformes"; @@ -3957,6 +3979,88 @@ $TLS_createKW = 'Créer un mot-clé'; $TLS_btn_create_and_link = 'Créer & Lier'; $TLS_create_keyword_and_link = 'Créer un mot clé et le lier à une version de cas de test'; -$TLS_export_skel = 'Exporter un skelete'; - -// ----- END ------------------------------------------------------------------ +$TLS_export_skel = 'Exporter un squelette'; + +$TLS_title_res_by_kw_on_plat = "Résultats de la plateforme par mot clé : "; +$TLS_title_res_by_prio_on_plat = "Résultats de la plateforme par priorité : "; +$TLS_title_res_by_tl_testsuite_on_plat = + "Résultats de la plateforme par dossier de premier niveau : "; + +$TLS_title_res_by_tl_testsuite = "Résultats par dossier de premier niveau"; + +$TLS_title_res_by_build_on_plat = "Résultats de la plateforme par version :"; +$TLS_title_res_build = 'Résultats par version'; +$TLS_info_neverRunByPP_report = + "Ce rapport montre les cas de tests n’ayant pas été exécutés pour chaque version active de la campagne (et de la plateforme) sélectionnée.
    + Les cas de tests avec ou sans testeur affecté + sont pris en compte.
    "; + +$TLS_neverRunByPP_title = "Cas de test jamais exécutés"; +$TLS_link_report_never_run = "Cas de test jamais exécutés"; + + +$TLS_commit_id = 'id de commit'; +$TLS_branch = 'branche'; +$TLS_tag = 'étiquette'; +$TLS_release_candidate = 'release candidate'; + +$TLS_allowed_files = "Fichiers autorisés :"; +$TLS_allowed_filenames_regexp = "Le nom du fichier doit respecter cette expression régulière :"; + +$TLS_info_xls_report_results_by_status = '!!La feuille de calcul produite contient le résumé de cas de test'; + + +$TLS_link_report_test_absolute_latest_exec = "Absolute Latest Execution Matrix"; +$TLS_latest_execution = "Latest execution on Context"; +$TLS_resultsTCAbsoluteLatest = "La dernière exécution est calculée à partir de toutes les versions"; +$TLS_info_resultsTCAbsoluteLatest_report = $TLS_resultsTCAbsoluteLatest; + +$TLS_resultsTCAbsoluteLatest_title = "La dernière exécution pour la plateforme sélectionnée est calculée à partir de toutes les versions"; + +$TLS_FILE_UPLOAD_allowed_filenames_regexp = 'Le nom du fichier ne respecte pas l’expression régulière'; + +$TLS_FILE_UPLOAD_empty_extension = 'L’extension de fichier est obligatoire'; +$TLS_FILE_UPLOAD_allowed_files = 'Ce type de fichier n’est pas autorisé'; + +$TLS_file_upload_ko = 'Problème lors du téléversement'; +$TLS_pleaseOpenTSuite = 'La section Détails du dossier de test contient des informations importantes pour ' . + 'l’exécution.
    Veuillez la lire '. + 'avec attention.
    Merci.'; + +$TLS_delete_frozen_tcversion = "Suppression d’une version de cas de test figée"; +$TLS_right_delete_frozen_tcversion = "Suppression d’une version de cas de test figée"; +$TLS_copy_only_latest_version = 'Copier seulement la dernière version en tant que première version'; +$TLS_qty = 'Qté'; +$TLS_yyyy_mm = 'Année/Mois'; +$TLS_yyyy_mm_dd = 'Date'; +$TLS_hh = 'Heure'; +$TLS_execTimelineStats_report = 'Statistiques d’exécution par date/heure'; +$TLS_link_report_exec_timeline = 'Exécution par date/heure'; +$TLS_qty_of_executions = 'Exécutions de cas de test'; +$TLS_assignOnlyToTestCasesInTestSuite = 'Affecter seulement aux enfants directs de ce dossier de test'; +$TLS_target_kword = "Mots clés cibles (Affecter/Supprimer)"; +$TLS_link_report_by_tsuite = "Métriques de niveau 1 et 2 des campagnes de tests"; +$TLS_metrics_by_l1l2_testsuite = "Métriques de niveau 1 et 2 des campagnes de tests"; +$TLS_you_can_sort = "Vous pouvez trier le tableau en cliquant sur les en-têtes de colonne."; +$TLS_firstExec = 'Première exécution : '; +$TLS_latestExec = 'Dernière exécution : '; +$TLS_section_link_report_by_tsuite_on_plat = "Résultats par plateforme : "; +$TLS_l1l2 = 'Top : Child (Test Suites)'; +$TLS_not_enough_rights = 'Vous ne disposez pas d’assez de droits pour utiliser cette fonction'; +$TLS_saveForBaseline = 'Sauver pour créer la référence'; +$TLS_baseline_saved_ok = 'Référence sauvée'; +$TLS_baseline_l1l2 = 'Référence de niveau1 et 2 des campagne de tests'; +$TLS_baselineTimestamp = 'Référence prise le :'; +$TLS_on_design = 'On Design'; +$TLS_on_exec = 'On Execution'; +$TLS_note_platform_filter = 'Les cas de test sont filtrés suivant les platformes suivantes'; +$TLS_testers_qty = 'Nombre de testeurs'; + +$TLS_report_test_automation = 'Rapport de spécification des tests automatiques'; +$TLS_btn_report_test_automation = 'Rapport de spécification des tests automatiques'; +$TLS_before_summary = "Avant le résumé"; +$TLS_before_preconditions = "Avant les pré-conditions"; +$TLS_after_title = "Après le titre"; +$TLS_after_summary = "Après le résumé"; +$TLS_after_preconditions = "Après les pré-conditions"; +// ----- END ----------------------------------------------------- diff --git a/locale/fr_FR/texts.php b/locale/fr_FR/texts.php index 17934e5896..a5c68f528b 100644 --- a/locale/fr_FR/texts.php +++ b/locale/fr_FR/texts.php @@ -1,39 +1,36 @@ - comment only with "//" except header - * 2. for JS string you must use \\n to get \n for end of line - * - * ******************************************************************************************** - * - **/ - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Erreur applicative"; -$TLS_htmltext['error'] = "

    Une erreur inattendue est survenue. Merci de consulter le moniteur d'événement ou " . - "les fichiers de logs pour plus de détails.

    Nous vous incitons à signaler le problème. Merci d'utiliser notre " . - "gestionnaire d'anomalies.

    "; - - - -$TLS_htmltext_title['assignReqs'] = "Lier les exigences aux fiches de test"; -$TLS_htmltext['assignReqs'] = "

    Objectif :

    + comment only with "//" except header + * 2. for JS string you must use \\n to get \n for end of line + * + * ******************************************************************************************** + * + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Erreur applicative"; +$TLS_htmltext['error'] = "

    Une erreur inattendue est survenue. Merci de consulter le moniteur d'événement ou " . + "les fichiers de logs pour plus de détails.

    Nous vous incitons à signaler le problème. Merci d'utiliser notre " . + "gestionnaire d'anomalies.

    "; + +$TLS_htmltext_title['assignReqs'] = "Lier les exigences aux fiches de test"; +$TLS_htmltext['assignReqs'] = "

    Objectif :

    Les utilisateurs peuvent créer des relations entre exigences et fiches de test. Un concepteur de test peut -définir des relations 0..n vers 0..n. Par exemple, une fiche de test peut être affectée à une ou plusieurs +définir des relations 0..n vers 0..n. Par exemple, une fiche de test peut être affectée à une ou plusieurs exigences, ou aucune, et inversement. Tout comme la matrice de traçabilité aide à rechercher la couverture des tests d'une exigence et trouver lesquelles ont successivement échoué pendant les tests, l'analyse sert à confirmer que toutes les attentes définies ont été rencontrées.

    @@ -41,60 +38,58 @@
    1. Choisissez une fiche de test dans l'arborescence à gauche. La combo box avec la liste des dossiers d'exigences est affichée en haut de l'espace de travail.
    2. -
    3. Choisissez un dossier d'exigence si plus d'un est défini. +
    4. Choisissez un dossier d'exigence si plus d'un est défini. TestLink recharge la page automatiquement.
    5. Un bloc au milieu de l'espace de travail liste toutes les exigences (des spécifications choisies), qui - sont liées à la fiche de test. Le bloc du dessous 'Exigences disponibles' liste toutes les exigences qui - n'ont pas de relation avec la fiche de test sélectionnée. Un concepteur peut marquer les exigences qui sont - couvertes par cette fiche de test et alors cliquer sur le bouton 'Affecter'. Cette nouvelle fiche de test + sont liées à la fiche de test. Le bloc du dessous 'Exigences disponibles' liste toutes les exigences qui + n'ont pas de relation avec la fiche de test sélectionnée. Un concepteur peut marquer les exigences qui sont + couvertes par cette fiche de test et alors cliquer sur le bouton 'Affecter'. Cette nouvelle fiche de test affectée est affichée dans le bloc du milieu 'Exigences affectées'.

    Attention :

    -Une exigence verrouillée ne peut pas voir sa couverture modifiée. En conséquence, les exigences verrouillées sont listées mais les cases à cocher correspondantes sont désactivées."; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Cahier de test"; -$TLS_htmltext['editTc'] = "

    Objectif :

    -

    La Cahier de Test autorise les utilisateurs à voir et éditer tous les " . - "Dossiers de Test et Fiches de Tests existants. Les fiches de test ont une version " . - " et toutes les versions précédentes sont disponibles et peuvent être vues et gérées ici.

    +Une exigence verrouillée ne peut pas voir sa couverture modifiée. En conséquence, les exigences verrouillées sont listées mais les cases à cocher correspondantes sont désactivées."; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Cahier de test"; +$TLS_htmltext['editTc'] = "

    Objectif :

    +

    La Cahier de Test autorise les utilisateurs à voir et éditer tous les " . + "Dossiers de Test et Fiches de Tests existants. Les fiches de test ont une version " . + " et toutes les versions précédentes sont disponibles et peuvent être vues et gérées ici.

    Pour commencer :

      -
    1. Sélectionner votre projet dans l'arborescence (le noeud racine). Veuillez noter : " . - "Vous pouvez toujours changer le projet actif en sélectionnant un projet différent dans la " . - "liste déroulante dans le coin en haut à droite.
    2. " . - "
    3. Créer un dossier de test en cliquant sur Créer dans Opérations sur les dossiers de tests. " . - "Les dossiers de test peuvent apporter une structure à vos documents de test conformément à vos normes " . - "(tests fonctionnels/non-fonctionnels, composants du produit ou fonctionnalités, requêtes de modifications, etc.). " . - "La description d'un dossier de test peut contenir le contexte des fiches de tests inclus, configuration par défaut," . - "des liens vers les documents utiles, les limitations et autres informations utiles. En général, " . - "toutes les annotations sont communes aux fiches de tests enfants. Les dossiers de test suivent " . - "le 'dossier' métaphore, ses utilisateurs peuvent déplacer ou copier les dossiers de test à l'intérieur " . - "du projet. De plus, ils peuvent les importer ou les exporter (incluant le contenu des fiches de tests).
    4. -
    5. Les dossiers de tests sont des dossiers divisibles. L'utilisateur peut déplacer ou copier les dossiers de tests à l'intérieur " . - "du projet. Les dossiers de tests peuvent être importés ou exportés (incluant les fiches de tests). -
    6. Sélectionnez votre nouveau dossier de test dans l'arborescence et créer une nouvelle fiche de test en " . - "cliquant sur Créer dans Opérations sur les fiches de tests.. Une fiche de test spécifie " . - " une fiche de test particuliere, les résultats attendus et la définition des champs personnalisés " . - "dans le projet (se référer au manuel utilisateur pour plus d'information). Il est également possible " . - "d'affecter des mots clés pour améliorer la traçabilité.
    7. +
    8. Sélectionner votre projet dans l'arborescence (le noeud racine). Veuillez noter : " . + "Vous pouvez toujours changer le projet actif en sélectionnant un projet différent dans la " . + "liste déroulante dans le coin en haut à droite.
    9. " . + "
    10. Créer un dossier de test en cliquant sur Créer dans Opérations sur les dossiers de tests. " . + "Les dossiers de test peuvent apporter une structure à vos documents de test conformément à vos normes " . + "(tests fonctionnels/non-fonctionnels, composants du produit ou fonctionnalités, requêtes de modifications, etc.). " . + "La description d'un dossier de test peut contenir le contexte des fiches de tests inclus, configuration par défaut," . + "des liens vers les documents utiles, les limitations et autres informations utiles. En général, " . + "toutes les annotations sont communes aux fiches de tests enfants. Les dossiers de test suivent " . + "le 'dossier' métaphore, ses utilisateurs peuvent déplacer ou copier les dossiers de test à l'intérieur " . + "du projet. De plus, ils peuvent les importer ou les exporter (incluant le contenu des fiches de tests).
    11. +
    12. Les dossiers de tests sont des dossiers divisibles. L'utilisateur peut déplacer ou copier les dossiers de tests à l'intérieur " . + "du projet. Les dossiers de tests peuvent être importés ou exportés (incluant les fiches de tests). +
    13. Sélectionnez votre nouveau dossier de test dans l'arborescence et créer une nouvelle fiche de test en " . + "cliquant sur Créer dans Opérations sur les fiches de tests.. Une fiche de test spécifie " . + " une fiche de test particuliere, les résultats attendus et la définition des champs personnalisés " . + "dans le projet (se référer au manuel utilisateur pour plus d'information). Il est également possible " . + "d'affecter des mots clés pour améliorer la traçabilité.
    14. Naviguez via l'arborescence sur le côté gauche et éditer les données. Les fiches de tests stockent leur propre historique.
    15. Affectez votre fiche de test créée à la Campagne de test lorsque votre fiche de test est prête.
    16. + \"javascript:open_help_window('glossary','$locale');\">Campagne de test lorsque votre fiche de test est prête.
    -

    Avec TestLink vous pouvez organiser les fiches de tests dans des dossiers de tests." . -"Les dossiers de tests peuvent être imbriqués dans d'autres dossiers de tests. Habituez-vous à créer des hiérarchies de dossiers de tests. - Vous pouvez alors imprimer cette information avec les fiches de tests.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Page de recherche de fiches de test"; -$TLS_htmltext['searchTc'] = "

    Objectif :

    +

    Avec TestLink vous pouvez organiser les fiches de tests dans des dossiers de tests." . + "Les dossiers de tests peuvent être imbriqués dans d'autres dossiers de tests. Habituez-vous à créer des hiérarchies de dossiers de tests. + Vous pouvez alors imprimer cette information avec les fiches de tests.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Page de recherche de fiches de test"; +$TLS_htmltext['searchTc'] = "

    Objectif :

    -

    Navigation selon des mots clés et/ou des phrases. La recherche n'est pas +

    Navigation selon des mots clés et/ou des phrases. La recherche n'est pas sensible à la casse. Le résultat inclut seulement les fiches de tests du projet actuel.

    Pour rechercher :

    @@ -104,13 +99,13 @@
  • Choisir le mot clé requit ou laisser la valeur 'Non appliqué'.
  • Cliquer sur le bouton Rechercher.
  • Toutes les fiches de tests remplissant les conditions sont affichées. Vous pouvez modifier les fiches de test via le lien 'Titre'.
  • -"; - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Page de recherche d'exigences"; -$TLS_htmltext['searchReq'] = "

    Objectif :

    +"; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Page de recherche d'exigences"; +$TLS_htmltext['searchReq'] = "

    Objectif :

    Navigation conformément aux mots-clés et/ou chaînes recherchées. La recherche n'est pas sensible à la casse. Le résultat inclut juste les exigences du projet de test actuel.

    @@ -131,12 +126,12 @@
  • La recherche n'est pas sensible à la casse.
  • Les champs vides sont ignorés.
  • -"; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Page de recherche de dossier d'exigence"; -$TLS_htmltext['searchReqSpec'] = "

    Objectif :

    +"; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Page de recherche de dossier d'exigence"; +$TLS_htmltext['searchReqSpec'] = "

    Objectif :

    Navigation conformément aux mots-clés et/ou chaînes recherchées. La recherche n'est pas sensible à la casse. Le résultat inclut juste les dossiers d'exigences du projet de test actuel.

    @@ -156,35 +151,33 @@
  • Seules les dossiers d'exigences dans le projet courant seront recherchées.
  • >
  • La recherche n'est pas sensible à la casse.
  • Les champs vides sont ignorés.
  • -"; -/* end contribution */ - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Imprimer un ensemble de fiches de test"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Objectif :

    +"; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Imprimer un ensemble de fiches de test"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Objectif :

    D'ici vous pouvez imprimer une fiche de test seule, toutes les fiche de tests d'un dossier de tests, ou toutes les fiches de test du projet ou de la campagne de test.

    Commencement :

    1. -

      Sélectionner la partie de la fiche de test que vous voulez afficher, et cliquer sur une fiche de test, +

      Sélectionner la partie de la fiche de test que vous voulez afficher, et cliquer sur une fiche de test, une dossier de tests, ou un projet. Une page imprimable sera affichée.

    2. -
    3. Utilisez la drop-box \"Afficher comme\" dans le cadre de navigation pour spécifier si vous voulez -afficher les informations en HTML, document OpenOffice ou document Microsoft. +

    4. Utilisez la drop-box \"Afficher comme\" dans le cadre de navigation pour spécifier si vous voulez +afficher les informations en HTML, document OpenOffice ou document Microsoft. Voir aide pour plus d'informations.

    5. Utiliser la fonctionnalité d'impression de votre navigateur pour imprimer les informations.
      Note: Faîtes attention à n'imprimer que le cadre à droite.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Conception du dossier d'exigences"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    Vous pouvez gérer le dossier d'exigences.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Conception du dossier d'exigences"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    Vous pouvez gérer le dossier d'exigences.

    Dossier d'exigences

    @@ -209,34 +202,32 @@

    Vous pouvez créer une nouvelle fiche de test pour les exigences en utilisant l'action multiple en sélectionnant les exigences dans l'écran des spécifications. Ces fiches de test sont créés dans le dossier de test avec un nom configuré de la sorte (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Séquence de test créée par exigence - Auto';). Le titre et le périmètre sont copiés dans cette fiche de test.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Imprimer le dossier d'exigences"; //printReq +'Séquence de test créée par exigence - Auto';)
    . Le titre et le périmètre sont copiés dans cette fiche de test.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Imprimer le dossier d'exigences"; // printReq $TLS_htmltext['printReqSpec'] = "

    Objectif :

    Il est possible d'imprimer une seule exigence, toutes les exigences d'un dossier d'exigences, ou toutes les exigences d'un projet.

    Pour commencer :

    1. -

      Sélectionner la partie des exigences que vous voulez afficher, et cliquer sur une exigence, +

      Sélectionner la partie des exigences que vous voulez afficher, et cliquer sur une exigence, un dossier d'exigences, ou un projet. Une page imprimable sera affichée.

    2. -
    3. Utilisez la drop-box \"Afficher comme\" dans le cadre de navigation pour spécifier si vous voulez -afficher les informations en HTML, document OpenOffice ou document Microsoft. +

    4. Utilisez la drop-box \"Afficher comme\" dans le cadre de navigation pour spécifier si vous voulez +afficher les informations en HTML, document OpenOffice ou document Microsoft. Voir aide pour plus d'informations.

    5. Utiliser la fonctionnalité d'impression de votre navigateur pour imprimer les informations.
      Note : Faîtes attention à n'imprimer que le cadre à droite.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Affectation des mots-clés"; -$TLS_htmltext['keywordsAssign'] = "

    Objectif :

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Affectation des mots-clés"; +$TLS_htmltext['keywordsAssign'] = "

    Objectif :

    La page d'affectation des mots-clés est l'endroit où les utilisateurs peuvent affecter par lot les mots clés à un dossier de test ou une fiche de test existante.

    @@ -258,16 +249,15 @@

    TestLink utilise cette approche afin que les anciennes versions des fiches de test dans les campagnes de test ne soient pas impactées par l'affectation des mots-clés faite sur la version la plus récente de la fiche de test. Si vous voulez que vos fiches de tests dans votre campagne de test soient mis à jour, vérifier d'abord que les fiches de tests ont été mis à jour en utilisant la fonctionnalité -'Mise à jour des versions de fiches de test à exécuter ' AVANT de faire l'affectation des mots clés.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Gestion des exécutions"; -$TLS_htmltext['executeTest'] = "

    Objectif :

    +'Mise à jour des versions de fiches de test à exécuter ' AVANT de faire l'affectation des mots clés.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Gestion des exécutions"; +$TLS_htmltext['executeTest'] = "

    Objectif :

    Autorise l'utilisateur à exécuter les fiches de tests. L'utilisateur peut affecter les résultats de test -à des versions de fiche de tests pour la version du produit. Voir l'aide pour plus d'informations à propos des filtres et des actions " . - "(cliquer sur l'icône point d'interrogation).

    +à des versions de fiche de tests pour la version du produit. Voir l'aide pour plus d'informations à propos des filtres et des actions " . + "(cliquer sur l'icône point d'interrogation).

    Pour commencer :

    @@ -276,18 +266,18 @@
  • Sélectionner une version du produit à évaluer dans la liste.
  • Si vous voulez voir que quelques fiches de test à la place de toute l'arborescence, il est possible d'appliquer un filtre. Cliquer sur le bouton \"Appliquer\" - après avoir renseigné les filtres.
  • + après avoir renseigné les filtres.
  • Cliquer sur une fiche de test dans l'arborescence.
  • Remplir le résultat de l'exécution de la fiche de test et toutes notes pertinentes.
  • Sauvegarder les résultats.
  • -

    Remarque : TestLink doit être configuré pour interagir avec votre gestionnaire d'anomalie -si vous voulez créer/tracer un rapport de problème directement depuis la GUI.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Description des rapports et métriques de test"; -$TLS_htmltext['showMetrics'] = "

    Les rapports sont reliés à une campagne de test " . - "(définie en haut du navigateur). La campagne de test peut différer de la campagne +

    Remarque : TestLink doit être configuré pour interagir avec votre gestionnaire d'anomalie +si vous voulez créer/tracer un rapport de problème directement depuis la GUI.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Description des rapports et métriques de test"; +$TLS_htmltext['showMetrics'] = "

    Les rapports sont reliés à une campagne de test " . + "(définie en haut du navigateur). La campagne de test peut différer de la campagne de test courante pour l'exécution. Vous pouvez aussi sélectionner un format de rapport :

    • Normal - le rapport est affiché en une page web;
    • @@ -300,8 +290,8 @@

      La 'Derniere exécution' d'une fiche de test est un concept utilisé dans plusieurs rapports, et qui est déterminé comme suit :

      • L'ordre dans lequel les versions du produit sont ajoutées à une campagne de test détermine quel version du produit est la plus récente. Les résultats -enregistrés pour la version du produit la plus récente ont préséance sur les résultats liés à des versions du produit plus anciennes. -Par exemple, si vous marquez un test comme 'échoué' dans une version du produit 1, et marqué à 'réussi' dans une version du produit 2, +enregistrés pour la version du produit la plus récente ont préséance sur les résultats liés à des versions du produit plus anciennes. +Par exemple, si vous marquez un test comme 'échoué' dans une version du produit 1, et marqué à 'réussi' dans une version du produit 2, la 'Derniere exécution' sera considérée 'réussi'.
      • Si une fiche de test est exécutée de multiple fois sur la même version du produit, l'exécution la plus récente aura préséance. Par exemple, si la version du produit 3 est affectée à votre équipe et que le testeur 1 enregistre une exécution 'réussi' à 2PM, @@ -325,7 +315,7 @@

        Métriques généraux de la Campagne

        Cette page vous montre seulement le statut le plus à jour d'une campagne de test par version du produit, dossier de test, priorité, mot-clé et indicateurs d'avancement. -Le statut le plus à jour est déterminé par la version du produit la plus récente pour l'exécution de fiche de tests. +Le statut le plus à jour est déterminé par la version du produit la plus récente pour l'exécution de fiche de tests. Si une fiche de test a été exécutée pour de multiples versions du produit, seulement le dernier résultat est pris en compte.

        @@ -337,7 +327,7 @@

        Rapports des cas de test bloqués, échoués et non exécutés

        Ces rapports montrent toutes les fiches de tests actuellement bloquées, échouées ou non exécutées. La 'Derniere exécution' - est de nouveau employée pour déterminer si une fiche de test peut être considérée bloquée, échouée ou non exécutée. Les rapports sur les + est de nouveau employée pour déterminer si une fiche de test peut être considérée bloquée, échouée ou non exécutée. Les rapports sur les fiches de test bloquées et échouées affichent les anomalies associées si l'utilisateur utilise un gestionnaire d'anomalies intégré.

        Matrice de résultats de test

        @@ -357,12 +347,11 @@

        Matrice des anomalies par fiche de test

        Ce rapport montre, pour chaque fiche de test, toutes les anomalies liées, pour la totalité du projet. -Ce rapport est disponible seulement si un système de gestion des anomalies est connecté.

        "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Ajouter/Retirer fiches de test"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

        Objectif :

        +Ce rapport est disponible seulement si un système de gestion des anomalies est connecté.

        "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Ajouter/Retirer fiches de test"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

        Objectif :

        Permet à l'utilisateur d'ajouter ou de supprimer des fiches de test dans la campagne de test.

        Pour ajouter ou supprimer des fiches de tests :

        @@ -371,11 +360,11 @@
      • Cocher les fiches de test à ajouter/supprimer.
      • Lorsque c'est fait, cliquez sur le bouton 'ajouter/retirer la sélection' pour ajouter ou supprimer les fiches de tests. Remarque : Ce n'est pas possible d'ajouter le même cas de test plusieurs fois.
      • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Assignation d'exécution de fiches de test"; -$TLS_htmltext['tc_exec_assignment'] = "

        Objectif

        +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Assignation d'exécution de fiches de test"; +$TLS_htmltext['tc_exec_assignment'] = "

        Objectif

        Cette page autorise le test leader à affecter l'exécution des fiches de tests à des utilisateurs dans la campagne de test.

        Pour commencer :

        @@ -384,15 +373,15 @@
      • Sélectionner un ou plusieurs testeurs.
      • Cliquez sur le bouton 'Enregistrer' pour enregistrer l'affectation.
      • Cliquez sur 'Envoyer les assignations par email aux testeurs' pour notifier les utilisateurs des affectations réalisées.
      • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Mise à jour des versions de fiches de test à exécuter"; -$TLS_htmltext['planUpdateTC'] = "

        Objectif

        +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Mise à jour des versions de fiches de test à exécuter"; +$TLS_htmltext['planUpdateTC'] = "

        Objectif

        Cette page autorise la mise à jour d'une fiche de test vers une nouvelle (différente) version si une exigence -de test est changée. Cela arrive souvent lorsque certaines fonctionnalités sont clarifiées pendant la phase de test." . - " L'utilisateur modifie le cahier de test, mais les changements doivent être propagés à la campagne de test réalisée. Autrement la campagne" . - " de test détient la version erronée pour être sûr que les résultats renvoient au bon texte d'une fiche de test.

        +de test est changée. Cela arrive souvent lorsque certaines fonctionnalités sont clarifiées pendant la phase de test." . + " L'utilisateur modifie le cahier de test, mais les changements doivent être propagés à la campagne de test réalisée. Autrement la campagne" . + " de test détient la version erronée pour être sûr que les résultats renvoient au bon texte d'une fiche de test.

        Pour commencer :

          @@ -400,14 +389,13 @@
        1. Choisissez une nouvelle version dans le menu à choix multiples pour chque fiche de test à mettre à jour.
        2. Cliquez sur le bouton 'mettre à jour la campagne de test' pour soumettre les changements.
        3. Pour vérifier : Ouvrez la page d'exécution pour voir le texte de la fiche de test.
        4. -
        "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Définition de l'urgence de test"; -$TLS_htmltext['test_urgency'] = "

        Objectif

        -

        TestLink autorise à changer l'urgence des tests pour modifier la priorité de chaque fiche de tests. - La priorité d'un test dépend de la criticité de la fiche de test et de l'urgence définie dans +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Définition de l'urgence de test"; +$TLS_htmltext['test_urgency'] = "

        Objectif

        +

        TestLink autorise à changer l'urgence des tests pour modifier la priorité de chaque fiche de tests. + La priorité d'un test dépend de la criticité de la fiche de test et de l'urgence définie dans la campagne de test. Le test leader peut spécifier un ensemble de fiches de tests qui peuvent être testées prioritairement. Cela aide à s'assurer que les tests les plus importants sont réalisés malgré une contrainte de temps.

        @@ -421,10 +409,9 @@ des changements significatifs.
      • Cliquez sur le bouton 'Définir l'urgence pour les fiches de test' pour soumettre les changements.
      • -

        Par exemple, une fiche de test avec une haute criticité dans une suite de tests avec une urgence basse " . - "sera de priorité moyenne."; - - -// ------------------------------------------------------------------------------------------ - -?> \ No newline at end of file +

        Par exemple, une fiche de test avec une haute criticité dans une suite de tests avec une urgence basse " . + "sera de priorité moyenne."; + +// ------------------------------------------------------------------------------------------ + +?> diff --git a/locale/id_ID/description.php b/locale/id_ID/description.php index 1d02aa6909..0c7742eb68 100644 --- a/locale/id_ID/description.php +++ b/locale/id_ID/description.php @@ -1,31 +1,31 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - * ----------------------------------------------------------------------------------- */ - -// printFilter.html +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + * ----------------------------------------------------------------------------------- */ + +// printFilter.html $TLS_hlp_generateDocOptions = "

        Options for a generated document

        This table allows the user to filter test cases before they are viewed. If @@ -33,8 +33,8 @@ presented, check or uncheck, click on Filter, and select the desired data level from the tree.

        -

        Document Header: Users can filter out Document Header information. -Document Header information includes: Introduction, Scope, References, +

        Document Header: Users can filter out Document Header information. +Document Header information includes: Introduction, Scope, References, Test Methodology, and Test Limitations.

        Test Case Body: Users can filter out Test Case Body information. Test Case Body information @@ -49,35 +49,35 @@

        Table of Content: TestLink inserts list of all titles with internal hypertext links if checked.

        -

        Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component -in second case.

        "; - -// testPlan.html +

        Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component +in second case.

        "; + +// testPlan.html $TLS_hlp_testPlan = "

        Test Plan

        General

        -

        A test plan is a systematic approach to testing a system such as software. You can organize testing activity with +

        A test plan is a systematic approach to testing a system such as software. You can organize testing activity with particular builds of product in time and trace results.

        Test Execution

        -

        This section is where users can execute test cases (write test results) and -print Test case suite of the Test Plan. This section is where users can track -the results of their test case execution.

        +

        This section is where users can execute test cases (write test results) and +print Test case suite of the Test Plan. This section is where users can track +the results of their test case execution.

        Test Plan Management

        -

        This section, which is only lead accessible, allows users to administrate test plans. -Administering test plans involves creating/editing/deleting plans, -adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can +

        This section, which is only lead accessible, allows users to administrate test plans. +Administering test plans involves creating/editing/deleting plans, +adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can see which plan.
        -Users with lead permissions may also set the priority/risk and ownership of -Test case suites (categories) and create testing milestones.

        - -

        Note: It is possible that users may not see a dropdown containing any Test plans. -In this situation all links (except lead enabled ones) will be unlinked. If you -are in this situation you must contact a lead or admin to grant you the proper -project rights or create a Test Plan for you.

        "; - -// custom_fields.html +Users with lead permissions may also set the priority/risk and ownership of +Test case suites (categories) and create testing milestones.

        + +

        Note: It is possible that users may not see a dropdown containing any Test plans. +In this situation all links (except lead enabled ones) will be unlinked. If you +are in this situation you must contact a lead or admin to grant you the proper +project rights or create a Test Plan for you.

        "; + +// custom_fields.html $TLS_hlp_customFields = "

        Custom Fields

        Following are some facts about the implementation of custom fields:

          @@ -96,7 +96,7 @@
        • Caption variable name (eg: This is the value that is supplied to lang_get() API , or displayed as-is if not found in language file).
        • Custom field type (string, numeric, float, enum, email)
        • -
        • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list +
        • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list and combo types.
          Use the pipe ('|') character to separate possible values for an enumeration. One of the possible values @@ -116,16 +116,16 @@
        • Enable on test plan design. User can change the value during Test Plan design (add test cases to test plan)
        • Available for. User choose to what kind of item the field belows.
        -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

        Executing Test Cases

        Allows users to 'execute' test cases. Execution itself is merely assigning a test case a result (pass,fail,blocked) against a selected build.

        Access to a bug tracking system could be configured. User can directly add a new bugs -and browse exesting ones then.

        "; - -//bug_add.html +and browse exesting ones then.

        "; + +// bug_add.html $TLS_hlp_btsIntegration = "

        Add Bugs to Test Case

        (only if it is configured) TestLink has a very simple integration with Bug Tracking Systems (BTS), @@ -135,7 +135,7 @@

      • Insert new bug.
      • Display existent bug info.
      -

      +

      Process to add a bug

      @@ -144,111 +144,108 @@

    • Step 2: write down the BUGID assigned by BTS.
    • Step 3: write BUGID on the input field.
    • Step 4: use add bug button.
    • -
    + After closing the add bug page, you will see relevant bug data on the execute page. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Setup Filter and Build for test execution

    -

    The left pane consists from navigator through test cases assigned to the current " . -"Test plan and table with settings and filter. These filters allows the user " . -"to refine offered set of test cases before they are executed." . -"Setup your filter, press the \"Apply\" button and select appropriate Test Case " . -"from tree menu.

    +

    The left pane consists from navigator through test cases assigned to the current " . + "Test plan and table with settings and filter. These filters allows the user " . + "to refine offered set of test cases before they are executed." . + "Setup your filter, press the \"Apply\" button and select appropriate Test Case " . + "from tree menu.

    Build

    -

    Users must choose a build that will be connected with a test result. " . -"Builds are the basic component for the current Test Plan. Each test case " . -"may be run more times per build. However the last results is count only. +

    Users must choose a build that will be connected with a test result. " . + "Builds are the basic component for the current Test Plan. Each test case " . + "may be run more times per build. However the last results is count only.
    Builds can be created by leads using the Create New Build page.

    Test Case ID filter

    -

    Users can filter test cases by unique identifier. This ID is created automatically -during create time. Empty box means that the filter doesn't apply.

    +

    Users can filter test cases by unique identifier. This ID is created automatically +during create time. Empty box means that the filter doesn't apply.

    Priority filter

    -

    Users can filter test cases by test priority. Each test case importance is combined" . -"with test urgency within the current Test plan. For example 'HIGH' priority test case " . -"is shown if importance or urgency is HIGH and second attribute is at least MEDIUM level.

    +

    Users can filter test cases by test priority. Each test case importance is combined" . + "with test urgency within the current Test plan. For example 'HIGH' priority test case " . + "is shown if importance or urgency is HIGH and second attribute is at least MEDIUM level.

    Result filter

    -

    Users can filter test cases by results. Results are what happened to that test -case during a particular build. Test cases can pass, fail, be blocked, or not be run." . -"This filter is disabled by default.

    +

    Users can filter test cases by results. Results are what happened to that test +case during a particular build. Test cases can pass, fail, be blocked, or not be run." . + "This filter is disabled by default.

    User filter

    -

    Users can filter test cases by their assignee. The check-box allows to include also " . -"\"unassigned\" tests into the resulted set in addtion.

    "; -/* -

    Most Current Result

    -

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted -by the build that is chosen from the dropdown box. In this state the tree will display -the test cases status. -
    Example: User selects build 2 from the dropdown box and doesn't check the 'most -current' checkbox. All test cases will be shown with their status from build 2. -So, if test case 1 passed in build 2 it will be colored green. -
    If the user decideds to check the 'most current' checkbox the tree will be -colored by the test cases most recent result. -
    Ex: User selects build 2 from the dropdown box and this time checks -the 'most current' checkbox. All test cases will be shown with most current -status. So, if test case 1 passed in build 3, even though the user has also selected -build 2, it will be colored green.

    - */ - - -// newest_tcversions.html +

    Users can filter test cases by their assignee. The check-box allows to include also " . + "\"unassigned\" tests into the resulted set in addtion.

    "; +/* + *

    Most Current Result

    + *

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted + * by the build that is chosen from the dropdown box. In this state the tree will display + * the test cases status. + *
    Example: User selects build 2 from the dropdown box and doesn't check the 'most + * current' checkbox. All test cases will be shown with their status from build 2. + * So, if test case 1 passed in build 2 it will be colored green. + *
    If the user decideds to check the 'most current' checkbox the tree will be + * colored by the test cases most recent result. + *
    Ex: User selects build 2 from the dropdown box and this time checks + * the 'most current' checkbox. All test cases will be shown with most current + * status. So, if test case 1 passed in build 3, even though the user has also selected + * build 2, it will be colored green.

    + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Newest versions of linked Test Cases

    The whole set of Test Cases linked to Test Plan is analyzed, and a list of Test Cases which have a newest version is displayed (against the current set of the Test Plan). -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Requirements Coverage


    This feature allows to map a coverage of user or system requirements by test cases. Navigate via link \"Requirement Specification\" in main screen.

    Requirements Specification

    -

    Requirements are grouped by 'Requirements Specification' document which is related to -Test Project.
    TestLink doesn't support versions for both Requirements Specification -and Requirements itself. So, version of document should be added after +

    Requirements are grouped by 'Requirements Specification' document which is related to +Test Project.
    TestLink doesn't support versions for both Requirements Specification +and Requirements itself. So, version of document should be added after a Specification Title. -An user can add simple description or notes to Scope field.

    +An user can add simple description or notes to Scope field.

    -

    Overwritten count of REQs serves for -evaluation Req. coverage in case that not all requirements are added (imported) in. -The value 0 means that current count of requirements is used for metrics.

    -

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test +

    Overwritten count of REQs serves for +evaluation Req. coverage in case that not all requirements are added (imported) in. +The value 0 means that current count of requirements is used for metrics.

    +

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test coverage is 25% (if all these added requirements will be tested).

    Requirements

    Click on title of a created Requirements Specification. You can create, edit, delete or import requirements for the document. Each requirement has title, scope and status. Status should be \"Normal\" or \"Not testable\". Not testable requirements are not counted -to metrics. This parameter should be used for both unimplemented features and -wrong designed requirements.

    +to metrics. This parameter should be used for both unimplemented features and +wrong designed requirements.

    -

    You can create new test cases for requirements by using multi action with checked +

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite -with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = +with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Title and Scope are copied to these Test cases.

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Regarding 'Save Custom Fields'

    -If you have defined and assigned to Test Project,
    +If you have defined and assigned to Test Project,
    Custom Fields with:
    'Display on test plan design=true' and
    'Enable on test plan design=true'
    you will see these in this page ONLY for Test Cases linked to Test Plan. -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/id_ID/texts.php b/locale/id_ID/texts.php index d173c4587d..a2d326fded 100644 --- a/locale/id_ID/texts.php +++ b/locale/id_ID/texts.php @@ -1,33 +1,31 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * ------------------------------------------------------------------------------------ */ - - -$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; -$TLS_htmltext['assignReqs'] = "

    Purpose:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * ------------------------------------------------------------------------------------ */ +$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; +$TLS_htmltext['assignReqs'] = "

    Purpose:

    Users can set relations between requirements and test cases. A test designer could define relations 0..n to 0..n. I.e. One test case could be assigned to none, one or more requirements and vice versa. Such traceability matrix helps to investigate test coverage @@ -38,7 +36,7 @@

    1. Choose an Test Case in tree at the left. The combo box with list of Requirements Specifications is shown at the top of workarea.
    2. -
    3. Choose a Requirements Specification Document if more once defined. +
    4. Choose a Requirements Specification Document if more once defined. TestLink automatically reload the page.
    5. A middle block of workarea lists all requirements (from choosen Specification), which are connected with the test case. Bottom block 'Available Requirements' lists all @@ -46,50 +44,48 @@ to the current test case. A designer could mark requirements which are covered by this test case and then click the button 'Assign'. These new assigned test case are shown in the middle block 'Assigned Requirements'.
    6. -
    "; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Test Specification"; -$TLS_htmltext['editTc'] = "

    Purpose:

    +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Test Specification"; +$TLS_htmltext['editTc'] = "

    Purpose:

    Purpose:

    -

    The Test Specification allows users to view and edit all of the existing " . - "Test Suites and Test Cases. Test Cases are versioned and all " . - "of the previous versions are available and can be viewed and managed here.

    +

    The Test Specification allows users to view and edit all of the existing " . + "Test Suites and Test Cases. Test Cases are versioned and all " . + "of the previous versions are available and can be viewed and managed here.

    Getting Started:

      -
    1. Select your Test Project in the navigation tree (the root node). Please note: " . - "You can always change the activate Test Project by selecting a different one from the " . - "drop-down list in the top-right corner.
    2. -
    3. Create a new Test Suite by clicking on New Child Test Suite. Test Suites can " . - "bring structure to your test documents according to your conventions (functional/non-functional " . - "tests, product components or features, change requests, etc.). The description of " . - "a Test Suite could hold the scope of the included test cases, default configuration, " . - "links to relevant documents, limitations and other useful information. In general, " . - "all annotations that are common to the Child Test Cases. Test Suites follow " . - "the "folder" metaphor, thus users can move and copy Test Suites within " . - "the Test project. Also, they can be imported or exported (including the contained Test cases).
    4. -
    5. Test suites are scalable folders. User can move or copy Test Suites within " . - "the Test project. Test suites could be imported or exported (include Test cases). -
    6. Select your newly created Test Suite in the navigation tree and create " . - "a new Test Case by clicking on Create Test Case. A Test Case specifies " . - "a particular testing scenario, expected results and custom fields defined " . - "in the Test Project (refer to the user manual for more information). It is also possible " . - "to assign keywords for improved traceability.
    7. +
    8. Select your Test Project in the navigation tree (the root node). Please note: " . + "You can always change the activate Test Project by selecting a different one from the " . + "drop-down list in the top-right corner.
    9. +
    10. Create a new Test Suite by clicking on New Child Test Suite. Test Suites can " . + "bring structure to your test documents according to your conventions (functional/non-functional " . + "tests, product components or features, change requests, etc.). The description of " . + "a Test Suite could hold the scope of the included test cases, default configuration, " . + "links to relevant documents, limitations and other useful information. In general, " . + "all annotations that are common to the Child Test Cases. Test Suites follow " . + "the "folder" metaphor, thus users can move and copy Test Suites within " . + "the Test project. Also, they can be imported or exported (including the contained Test cases).
    11. +
    12. Test suites are scalable folders. User can move or copy Test Suites within " . + "the Test project. Test suites could be imported or exported (include Test cases). +
    13. Select your newly created Test Suite in the navigation tree and create " . + "a new Test Case by clicking on Create Test Case. A Test Case specifies " . + "a particular testing scenario, expected results and custom fields defined " . + "in the Test Project (refer to the user manual for more information). It is also possible " . + "to assign keywords for improved traceability.
    14. Navigate via the tree view on the left side and edit data. Test cases stores own history.
    15. Assign your created Test Specification to Test Plan when your Test cases are ready.
    16. + \"javascript:open_help_window('glossary','$locale');\">Test Plan when your Test cases are ready.
    -

    With TestLink you organize test cases into test suites." . -"Test suites can be nested within other test suites, enabling you to create hierarchies of test suites. - You can then print this information together with the test cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; -$TLS_htmltext['searchTc'] = "

    Purpose:

    +

    With TestLink you organize test cases into test suites." . + "Test suites can be nested within other test suites, enabling you to create hierarchies of test suites. + You can then print this information together with the test cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; +$TLS_htmltext['searchTc'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result include just test cases from actual Test Project.

    @@ -101,12 +97,11 @@
  • Choose required keyword or left value 'Not applied'.
  • Click the Search button.
  • All fulfilled test cases are shown. You can modify test cases via 'Title' link.
  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Purpose:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Purpose:

    From here you can print a single test case, all the test cases within a test suite, or all the test cases in a test project or plan.

    Get Started:

    @@ -121,12 +116,11 @@
  • Use your browser's print functionality to actually print the information.
    Note: Make sure to only print the right-hand frame.

  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    Requirements Specification

    @@ -154,12 +148,11 @@

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite with name defined in configuration (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). Title and Scope are copied to these Test cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; -$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    +'Test suite created by Requirement - Auto';)
    . Title and Scope are copied to these Test cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; +$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    The Keyword Assignment page is the place where users can batch assign keywords to the existing Test Suite or Test Case

    @@ -183,16 +176,15 @@

    TestLink uses this approach so that older versions of test cases in test plans are not effected by keyword assignments you make to the most recent version of the test case. If you want your test cases in your test plan to be updated, first verify they are up to date using the 'Update -Modified Test Cases' functionality BEFORE making keyword assignments.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Test Case Execution"; -$TLS_htmltext['executeTest'] = "

    Purpose:

    +Modified Test Cases' functionality BEFORE making keyword assignments.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Test Case Execution"; +$TLS_htmltext['executeTest'] = "

    Purpose:

    Allows user to execute Test cases. User can assign Test result -to Test Case for Build. See help for more information about filter and settings " . - "(click on the question mark icon).

    +to Test Case for Build. See help for more information about filter and settings " . + "(click on the question mark icon).

    Get started:

    @@ -203,13 +195,13 @@
  • Fill out the test case result and any applicable notes or bugs.
  • Save results.
  • -

    Note: TestLink must be configurated to collaborate with your Bug tracker -if you would like to create/trace a problem report directly from the GUI.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; -$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . - "(defined in top of navigator). This Test Plan could differs from the +

    Note: TestLink must be configurated to collaborate with your Bug tracker +if you would like to create/trace a problem report directly from the GUI.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; +$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . + "(defined in top of navigator). This Test Plan could differs from the current Test Plan for execution. You can also select Report format:

    • Normal - report is displayed in web page
    • @@ -326,12 +318,11 @@

      Total Bugs For Each Test Case

      This report shows each test case with all of the bugs filed against it for the entire project. -This report is only available if a Bug Tracking System is connected.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      Purpose:

      +This report is only available if a Bug Tracking System is connected.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      Purpose:

      Allows user (with lead level permissions) to add or remove test cases into a Test plan.

      To add or remove Test cases:

      @@ -339,11 +330,11 @@
    • Click on a test suite to see all of its test suites and all of its test cases.
    • When you are done click the 'Add / Remove Test Cases' button to add or remove the test cases. Note: Is not possible to add the same test case multiple times.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; -$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; +$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      This page allows test leaders to assign users to particular tests within the Test Plan.

      Get Started

      @@ -352,16 +343,15 @@
    • Select a planned tester.
    • Press button to submit assignement.
    • Open execution page to verify assignment. You can set-up a filter for users.
    • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; -$TLS_htmltext['planUpdateTC'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; +$TLS_htmltext['planUpdateTC'] = "

      Purpose

      This page allows update Test case to a newer (different) version in the case that Test -Specification is changed. It often happens that some functionality is clarified during testing." . - " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . - " plan holds original version to be sure, that results refer to the correct text of a Test case.

      +Specification is changed. It often happens that some functionality is clarified during testing." . + " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . + " plan holds original version to be sure, that results refer to the correct text of a Test case.

      Get Started

        @@ -369,14 +359,13 @@
      1. Choose a new version from bombo boxmenu for particular Test case.
      2. Press button 'Update Test plan' to submit changes.
      3. To verify: Open execution page to view text of the test case(s).
      4. -
      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; -$TLS_htmltext['test_urgency'] = "

      Purpose

      -

      TestLink allows set urgency of Test Suite to affect a testing Priority of test cases. - Test priority depends on both Importance of Test cases and Urgency defined in +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; +$TLS_htmltext['test_urgency'] = "

      Purpose

      +

      TestLink allows set urgency of Test Suite to affect a testing Priority of test cases. + Test priority depends on both Importance of Test cases and Urgency defined in the Test Plan. Test leader should specify a set of test cases that could be tested at first. It helps to assure that testing will cover the most important tests also under time pressure.

      @@ -390,10 +379,9 @@ significant changes.
    • Press the button 'Save' to submit changes.
    • -

      For example, a Test case with a High importance in a Test suite with Low urgency " . - "will be Medium priority."; - - -// ------------------------------------------------------------------------------------------ - -?> \ No newline at end of file +

      For example, a Test case with a High importance in a Test suite with Low urgency " . + "will be Medium priority."; + +// ------------------------------------------------------------------------------------------ + +?> diff --git a/locale/it_IT/description.php b/locale/it_IT/description.php index db462dd05f..3dc1e3dee2 100644 --- a/locale/it_IT/description.php +++ b/locale/it_IT/description.php @@ -1,31 +1,31 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - * ----------------------------------------------------------------------------------- */ - -// printFilter.html +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + * ----------------------------------------------------------------------------------- */ + +// printFilter.html $TLS_hlp_generateDocOptions = "

      Options for a generated document

      This table allows the user to filter test cases before they are viewed. If @@ -33,8 +33,8 @@ presented, check or uncheck, click on Filter, and select the desired data level from the tree.

      -

      Document Header: Users can filter out Document Header information. -Document Header information includes: Introduction, Scope, References, +

      Document Header: Users can filter out Document Header information. +Document Header information includes: Introduction, Scope, References, Test Methodology, and Test Limitations.

      Test Case Body: Users can filter out Test Case Body information. Test Case Body information @@ -49,35 +49,35 @@

      Table of Content: TestLink inserts list of all titles with internal hypertext links if checked.

      -

      Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component -in second case.

      "; - -// testPlan.html +

      Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component +in second case.

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      Test Plan

      General

      -

      A test plan is a systematic approach to testing a system such as software. You can organize testing activity with +

      A test plan is a systematic approach to testing a system such as software. You can organize testing activity with particular builds of product in time and trace results.

      Test Execution

      -

      This section is where users can execute test cases (write test results) and -print Test case suite of the Test Plan. This section is where users can track -the results of their test case execution.

      +

      This section is where users can execute test cases (write test results) and +print Test case suite of the Test Plan. This section is where users can track +the results of their test case execution.

      Test Plan Management

      -

      This section, which is only lead accessible, allows users to administrate test plans. -Administering test plans involves creating/editing/deleting plans, -adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can +

      This section, which is only lead accessible, allows users to administrate test plans. +Administering test plans involves creating/editing/deleting plans, +adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can see which plan.
      -Users with lead permissions may also set the priority/risk and ownership of -Test case suites (categories) and create testing milestones.

      - -

      Note: It is possible that users may not see a dropdown containing any Test plans. -In this situation all links (except lead enabled ones) will be unlinked. If you -are in this situation you must contact a lead or admin to grant you the proper -project rights or create a Test Plan for you.

      "; - -// custom_fields.html +Users with lead permissions may also set the priority/risk and ownership of +Test case suites (categories) and create testing milestones.

      + +

      Note: It is possible that users may not see a dropdown containing any Test plans. +In this situation all links (except lead enabled ones) will be unlinked. If you +are in this situation you must contact a lead or admin to grant you the proper +project rights or create a Test Plan for you.

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      Custom Fields

      Following are some facts about the implementation of custom fields:

        @@ -96,7 +96,7 @@
      • Caption variable name (eg: This is the value that is supplied to lang_get() API , or displayed as-is if not found in language file).
      • Custom field type (string, numeric, float, enum, email)
      • -
      • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list +
      • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list and combo types.
        Use the pipe ('|') character to separate possible values for an enumeration. One of the possible values @@ -116,16 +116,16 @@
      • Enable on test plan design. User can change the value during Test Plan design (add test cases to test plan)
      • Available for. User choose to what kind of item the field belows.
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      Executing Test Cases

      Allows users to 'execute' test cases. Execution itself is merely assigning a test case a result (pass,fail,blocked) against a selected build.

      Access to a bug tracking system could be configured. User can directly add a new bugs -and browse exesting ones then.

      "; - -//bug_add.html +and browse exesting ones then.

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      Add Bugs to Test Case

      (only if it is configured) TestLink has a very simple integration with Bug Tracking Systems (BTS), @@ -135,7 +135,7 @@

    • Insert new bug.
    • Display existent bug info.
    -

    +

    Process to add a bug

    @@ -144,111 +144,108 @@

  • Step 2: write down the BUGID assigned by BTS.
  • Step 3: write BUGID on the input field.
  • Step 4: use add bug button.
  • - + After closing the add bug page, you will see relevant bug data on the execute page. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    Setup Filter and Build for test execution

    -

    The left pane consists from navigator through test cases assigned to the current " . -"Test plan and table with settings and filter. These filters allows the user " . -"to refine offered set of test cases before they are executed." . -"Setup your filter, press the \"Apply\" button and select appropriate Test Case " . -"from tree menu.

    +

    The left pane consists from navigator through test cases assigned to the current " . + "Test plan and table with settings and filter. These filters allows the user " . + "to refine offered set of test cases before they are executed." . + "Setup your filter, press the \"Apply\" button and select appropriate Test Case " . + "from tree menu.

    Build

    -

    Users must choose a build that will be connected with a test result. " . -"Builds are the basic component for the current Test Plan. Each test case " . -"may be run more times per build. However the last results is count only. +

    Users must choose a build that will be connected with a test result. " . + "Builds are the basic component for the current Test Plan. Each test case " . + "may be run more times per build. However the last results is count only.
    Builds can be created by leads using the Create New Build page.

    Test Case ID filter

    -

    Users can filter test cases by unique identifier. This ID is created automatically -during create time. Empty box means that the filter doesn't apply.

    +

    Users can filter test cases by unique identifier. This ID is created automatically +during create time. Empty box means that the filter doesn't apply.

    Priority filter

    -

    Users can filter test cases by test priority. Each test case importance is combined" . -"with test urgency within the current Test plan. For example 'HIGH' priority test case " . -"is shown if importance or urgency is HIGH and second attribute is at least MEDIUM level.

    +

    Users can filter test cases by test priority. Each test case importance is combined" . + "with test urgency within the current Test plan. For example 'HIGH' priority test case " . + "is shown if importance or urgency is HIGH and second attribute is at least MEDIUM level.

    Result filter

    -

    Users can filter test cases by results. Results are what happened to that test -case during a particular build. Test cases can pass, fail, be blocked, or not be run." . -"This filter is disabled by default.

    +

    Users can filter test cases by results. Results are what happened to that test +case during a particular build. Test cases can pass, fail, be blocked, or not be run." . + "This filter is disabled by default.

    User filter

    -

    Users can filter test cases by their assignee. The check-box allows to include also " . -"\"unassigned\" tests into the resulted set in addtion.

    "; -/* -

    Most Current Result

    -

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted -by the build that is chosen from the dropdown box. In this state the tree will display -the test cases status. -
    Example: User selects build 2 from the dropdown box and doesn't check the 'most -current' checkbox. All test cases will be shown with their status from build 2. -So, if test case 1 passed in build 2 it will be colored green. -
    If the user decideds to check the 'most current' checkbox the tree will be -colored by the test cases most recent result. -
    Ex: User selects build 2 from the dropdown box and this time checks -the 'most current' checkbox. All test cases will be shown with most current -status. So, if test case 1 passed in build 3, even though the user has also selected -build 2, it will be colored green.

    - */ - - -// newest_tcversions.html +

    Users can filter test cases by their assignee. The check-box allows to include also " . + "\"unassigned\" tests into the resulted set in addtion.

    "; +/* + *

    Most Current Result

    + *

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted + * by the build that is chosen from the dropdown box. In this state the tree will display + * the test cases status. + *
    Example: User selects build 2 from the dropdown box and doesn't check the 'most + * current' checkbox. All test cases will be shown with their status from build 2. + * So, if test case 1 passed in build 2 it will be colored green. + *
    If the user decideds to check the 'most current' checkbox the tree will be + * colored by the test cases most recent result. + *
    Ex: User selects build 2 from the dropdown box and this time checks + * the 'most current' checkbox. All test cases will be shown with most current + * status. So, if test case 1 passed in build 3, even though the user has also selected + * build 2, it will be colored green.

    + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    Newest versions of linked Test Cases

    The whole set of Test Cases linked to Test Plan is analyzed, and a list of Test Cases which have a newest version is displayed (against the current set of the Test Plan). -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    Requirements Coverage


    This feature allows to map a coverage of user or system requirements by test cases. Navigate via link \"Requirement Specification\" in main screen.

    Requirements Specification

    -

    Requirements are grouped by 'Requirements Specification' document which is related to -Test Project.
    TestLink doesn't support versions for both Requirements Specification -and Requirements itself. So, version of document should be added after +

    Requirements are grouped by 'Requirements Specification' document which is related to +Test Project.
    TestLink doesn't support versions for both Requirements Specification +and Requirements itself. So, version of document should be added after a Specification Title. -An user can add simple description or notes to Scope field.

    +An user can add simple description or notes to Scope field.

    -

    Overwritten count of REQs serves for -evaluation Req. coverage in case that not all requirements are added (imported) in. -The value 0 means that current count of requirements is used for metrics.

    -

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test +

    Overwritten count of REQs serves for +evaluation Req. coverage in case that not all requirements are added (imported) in. +The value 0 means that current count of requirements is used for metrics.

    +

    E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test coverage is 25% (if all these added requirements will be tested).

    Requirements

    Click on title of a created Requirements Specification. You can create, edit, delete or import requirements for the document. Each requirement has title, scope and status. Status should be \"Normal\" or \"Not testable\". Not testable requirements are not counted -to metrics. This parameter should be used for both unimplemented features and -wrong designed requirements.

    +to metrics. This parameter should be used for both unimplemented features and +wrong designed requirements.

    -

    You can create new test cases for requirements by using multi action with checked +

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite -with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = +with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Title and Scope are copied to these Test cases.

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    Regarding 'Save Custom Fields'

    -If you have defined and assigned to Test Project,
    +If you have defined and assigned to Test Project,
    Custom Fields with:
    'Display on test plan design=true' and
    'Enable on test plan design=true'
    you will see these in this page ONLY for Test Cases linked to Test Plan. -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/it_IT/texts.php b/locale/it_IT/texts.php index 1c2900d1d6..c7fdeece80 100644 --- a/locale/it_IT/texts.php +++ b/locale/it_IT/texts.php @@ -1,33 +1,31 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * ------------------------------------------------------------------------------------ */ - - -$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; -$TLS_htmltext['assignReqs'] = "

    Purpose:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * ------------------------------------------------------------------------------------ */ +$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; +$TLS_htmltext['assignReqs'] = "

    Purpose:

    Users can set relations between requirements and test cases. A designer could define relations 0..n to 0..n. I.e. One test case could be assigned to none, one or more test cases and vice versa. Such traceability matrix helps to investigate test coverage @@ -38,7 +36,7 @@

    1. Choose an Test Case in tree at the left. The combo box with list of Requirements Specifications is shown at the top of workarea.
    2. -
    3. Choose a Requirements Specification Document if more once defined. +
    4. Choose a Requirements Specification Document if more once defined. TestLink automatically reload the page.
    5. A middle block of workarea lists all requirements (from choosen Specification), which are connected with the test case. Bottom block 'Available Requirements' lists all @@ -46,50 +44,48 @@ to the current test case. A designer could mark requirements which are covered by this test case and then click the button 'Assign'. These new assigned test case are shown in the middle block 'Assigned Requirements'.
    6. -
    "; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Test Specification"; -$TLS_htmltext['editTc'] = "

    Purpose:

    +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Test Specification"; +$TLS_htmltext['editTc'] = "

    Purpose:

    Purpose:

    -

    The Test Specification allows users to view and edit all of the existing " . - "Test Suites and Test Cases. Test Cases are versioned and all " . - "of the previous versions are available and can be viewed and managed here.

    +

    The Test Specification allows users to view and edit all of the existing " . + "Test Suites and Test Cases. Test Cases are versioned and all " . + "of the previous versions are available and can be viewed and managed here.

    Getting Started:

      -
    1. Select your Test Project in the navigation tree (the root node). Please note: " . - "You can always change the activate Test Project by selecting a different one from the " . - "drop-down list in the top-right corner.
    2. -
    3. Create a new Test Suite by clicking on New Child Test Suite. Test Suites can " . - "bring structure to your test documents according to your conventions (functional/non-functional " . - "tests, product components or features, change requests, etc.). The description of " . - "a Test Suite could hold the scope of the included test cases, default configuration, " . - "links to relevant documents, limitations and other useful information. In general, " . - "all annotations that are common to the Child Test Cases. Test Suites follow " . - "the "folder" metaphor, thus users can move and copy Test Suites within " . - "the Test project. Also, they can be imported or exported (including the contained Test cases).
    4. -
    5. Test suites are scalable folders. User can move or copy Test Suites within " . - "the Test project. Test suites could be imported or exported (include Test cases). -
    6. Select your newly created Test Suite in the navigation tree and create " . - "a new Test Case by clicking on Create Test Case. A Test Case specifies " . - "a particular testing scenario, expected results and custom fields defined " . - "in the Test Project (refer to the user manual for more information). It is also possible " . - "to assign keywords for improved traceability.
    7. +
    8. Select your Test Project in the navigation tree (the root node). Please note: " . + "You can always change the activate Test Project by selecting a different one from the " . + "drop-down list in the top-right corner.
    9. +
    10. Create a new Test Suite by clicking on New Child Test Suite. Test Suites can " . + "bring structure to your test documents according to your conventions (functional/non-functional " . + "tests, product components or features, change requests, etc.). The description of " . + "a Test Suite could hold the scope of the included test cases, default configuration, " . + "links to relevant documents, limitations and other useful information. In general, " . + "all annotations that are common to the Child Test Cases. Test Suites follow " . + "the "folder" metaphor, thus users can move and copy Test Suites within " . + "the Test project. Also, they can be imported or exported (including the contained Test cases).
    11. +
    12. Test suites are scalable folders. User can move or copy Test Suites within " . + "the Test project. Test suites could be imported or exported (include Test cases). +
    13. Select your newly created Test Suite in the navigation tree and create " . + "a new Test Case by clicking on Create Test Case. A Test Case specifies " . + "a particular testing scenario, expected results and custom fields defined " . + "in the Test Project (refer to the user manual for more information). It is also possible " . + "to assign keywords for improved traceability.
    14. Navigate via the tree view on the left side and edit data. Test cases stores own history.
    15. Assign your created Test Specification to Test Plan when your Test cases are ready.
    16. + \"javascript:open_help_window('glossary','$locale');\">Test Plan when your Test cases are ready.
    -

    With TestLink you organize test cases into test suites." . -"Test suites can be nested within other test suites, enabling you to create hierarchies of test suites. - You can then print this information together with the test cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; -$TLS_htmltext['searchTc'] = "

    Purpose:

    +

    With TestLink you organize test cases into test suites." . + "Test suites can be nested within other test suites, enabling you to create hierarchies of test suites. + You can then print this information together with the test cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; +$TLS_htmltext['searchTc'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result include just test cases from actual Test Project.

    @@ -101,12 +97,11 @@
  • Choose required keyword or left value 'Not applied'.
  • Click the Search button.
  • All fulfilled test cases are shown. You can modify test cases via 'Title' link.
  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    Purpose:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    Purpose:

    From here you can print a single test case, all the test cases within a test suite, or all the test cases in a test project or plan.

    Get Started:

    @@ -121,12 +116,11 @@
  • Use your browser's print functionality to actually print the information.
    Note: Make sure to only print the right-hand frame.

  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    You can manage Requirement Specification documents.

    Requirements Specification

    @@ -154,12 +148,11 @@

    You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite with name defined in configuration (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). Title and Scope are copied to these Test cases.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; -$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    +'Test suite created by Requirement - Auto';)
    . Title and Scope are copied to these Test cases.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; +$TLS_htmltext['keywordsAssign'] = "

    Purpose:

    The Keyword Assignment page is the place where users can batch assign keywords to the existing Test Suite or Test Case

    @@ -183,16 +176,15 @@

    TestLink uses this approach so that older versions of test cases in test plans are not effected by keyword assignments you make to the most recent version of the test case. If you want your test cases in your test plan to be updated, first verify they are up to date using the 'Update -Modified Test Cases' functionality BEFORE making keyword assignments.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Test Case Execution"; -$TLS_htmltext['executeTest'] = "

    Purpose:

    +Modified Test Cases' functionality BEFORE making keyword assignments.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Test Case Execution"; +$TLS_htmltext['executeTest'] = "

    Purpose:

    Allows user to execute Test cases. User can assign Test result -to Test Case for Build. See help for more information about filter and settings " . - "(click on the question mark icon).

    +to Test Case for Build. See help for more information about filter and settings " . + "(click on the question mark icon).

    Get started:

    @@ -203,13 +195,13 @@
  • Fill out the test case result and any applicable notes or bugs.
  • Save results.
  • -

    Note: TestLink must be configurated to collaborate with your Bug tracker -if you would like to create/trace a problem report directly from the GUI.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; -$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . - "(defined in top of navigator). This Test Plan could differs from the +

    Note: TestLink must be configurated to collaborate with your Bug tracker +if you would like to create/trace a problem report directly from the GUI.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; +$TLS_htmltext['showMetrics'] = "

    Reports are related to a Test Plan " . + "(defined in top of navigator). This Test Plan could differs from the current Test Plan for execution. You can also select Report format:

    • Normal - report is displayed in web page
    • @@ -326,12 +318,11 @@

      Total Bugs For Each Test Case

      This report shows each test case with all of the bugs filed against it for the entire project. -This report is only available if a Bug Tracking System is connected.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      Purpose:

      +This report is only available if a Bug Tracking System is connected.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      Purpose:

      Allows user (with lead level permissions) to add or remove test cases into a Test plan.

      To add or remove Test cases:

      @@ -339,11 +330,11 @@
    • Click on a test suite to see all of its test suites and all of its test cases.
    • When you are done click the 'Add / Remove Test Cases' button to add or remove the test cases. Note: Is not possible to add the same test case multiple times.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; -$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; +$TLS_htmltext['tc_exec_assignment'] = "

      Purpose

      This page allows test leaders to assign users to particular tests within the Test Plan.

      Get Started

      @@ -352,16 +343,15 @@
    • Select a planned tester.
    • Press button to submit assignement.
    • Open execution page to verify assignment. You can set-up a filter for users.
    • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; -$TLS_htmltext['planUpdateTC'] = "

      Purpose

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; +$TLS_htmltext['planUpdateTC'] = "

      Purpose

      This page allows update Test case to a newer (different) version in the case that Test -Specification is changed. It often happens that some functionality is clarified during testing." . - " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . - " plan holds original version to be sure, that results refer to the correct text of a Test case.

      +Specification is changed. It often happens that some functionality is clarified during testing." . + " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . + " plan holds original version to be sure, that results refer to the correct text of a Test case.

      Get Started

        @@ -369,14 +359,13 @@
      1. Choose a new version from bombo boxmenu for particular Test case.
      2. Press button 'Update Test plan' to submit changes.
      3. To verify: Open execution page to view text of the test case(s).
      4. -
      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; -$TLS_htmltext['test_urgency'] = "

      Purpose

      -

      TestLink allows set urgency of Test Suite to affect a testing Priority of test cases. - Test priority depends on both Importance of Test cases and Urgency defined in +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; +$TLS_htmltext['test_urgency'] = "

      Purpose

      +

      TestLink allows set urgency of Test Suite to affect a testing Priority of test cases. + Test priority depends on both Importance of Test cases and Urgency defined in the Test Plan. Test leader should specify a set of test cases that could be tested at first. It helps to assure that testing will cover the most important tests also under time pressure.

      @@ -390,10 +379,9 @@ significant changes.
    • Press the button 'Save' to submit changes.
    • -

      For example, a Test case with a High importance in a Test suite with Low urgency " . - "will be Medium priority."; - - -// ------------------------------------------------------------------------------------------ - -?> \ No newline at end of file +

      For example, a Test case with a High importance in a Test suite with Low urgency " . + "will be Medium priority."; + +// ------------------------------------------------------------------------------------------ + +?> diff --git a/locale/ja_JP/description.php b/locale/ja_JP/description.php index a93e030dde..74696d5c0b 100644 --- a/locale/ja_JP/description.php +++ b/locale/ja_JP/description.php @@ -1,54 +1,54 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - *------------------------------------------------------------------- - * Japanese translation - *------------------------------------------------------------------- - * Testing Engineer's Forum (TEF) in Japan - * Working Group of TestLink Japanese Translation Project - * - * http://blues.se.uec.ac.jp/swtest/ - * http://testlinkjp.org/ - * http://sourceforge.jp/projects/testlinkjp/ - * - * Adviser: - * Yasuharu NISHI - * - * Core member: - * Atsushi Nagata, AZMA Daisuke, Hiromi Nishiyama, - * Kaname Mochizuki, Kaoru Nakamura, Kunio Murakami, - * Lumina Nishihara, Marino Suda, Masahide Katsumata, - * Masami Ichikawa, Masataka Yoneta, Sadahiko Hantani, - * Shinichi Sugiyama, Shinsuke Matsuki, Shizuka Ban, - * Takahiro Wada, Toshinori Sawaguchi, Toshiyuki Kawanishi, - * Yasuhiko Okada, Yoichi Kunihiro, Yoshihiro Yoshimura, - * Yukiko Kajino - * - * ----------------------------------------------------------------------------------- */ - -// printFilter.html +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + *------------------------------------------------------------------- + * Japanese translation + *------------------------------------------------------------------- + * Testing Engineer's Forum (TEF) in Japan + * Working Group of TestLink Japanese Translation Project + * + * http://blues.se.uec.ac.jp/swtest/ + * http://testlinkjp.org/ + * http://sourceforge.jp/projects/testlinkjp/ + * + * Adviser: + * Yasuharu NISHI + * + * Core member: + * Atsushi Nagata, AZMA Daisuke, Hiromi Nishiyama, + * Kaname Mochizuki, Kaoru Nakamura, Kunio Murakami, + * Lumina Nishihara, Marino Suda, Masahide Katsumata, + * Masami Ichikawa, Masataka Yoneta, Sadahiko Hantani, + * Shinichi Sugiyama, Shinsuke Matsuki, Shizuka Ban, + * Takahiro Wada, Toshinori Sawaguchi, Toshiyuki Kawanishi, + * Yasuhiko Okada, Yoichi Kunihiro, Yoshihiro Yoshimura, + * Yukiko Kajino + * + * ----------------------------------------------------------------------------------- */ + +// printFilter.html $TLS_hlp_generateDocOptions = "

      テストケースå°åˆ·ã®ã‚ªãƒ—ション

      ã“ã®è¡¨ã«ã‚ˆã‚Šè¡¨ç¤ºã™ã‚‹ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’フィルタリングã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ @@ -75,9 +75,9 @@

      目次: TestLinkã¯å…¨ã¦ã®ã‚¿ã‚¤ãƒˆãƒ«ã®ãƒªã‚¹ãƒˆã‚’内部ã®ãƒã‚¤ãƒ‘ーリンク付ãã§æŒ¿å…¥ã—ã¾ã™ã€‚

      出力形å¼: HTMLã¨MS Wordã®ï¼’種類ã®å½¢å¼ã§å‡ºåŠ›ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ -後者ã®å ´åˆã€ãƒ–ラウザãŒMS Wordã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚’自動ã§èª­ã¿è¾¼ã¿ã€ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’表示ã—ã¾ã™ã€‚

      "; - -// testPlan.html +後者ã®å ´åˆã€ãƒ–ラウザãŒMS Wordã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚’自動ã§èª­ã¿è¾¼ã¿ã€ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’表示ã—ã¾ã™ã€‚

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      テスト計画

      一般

      @@ -99,9 +99,9 @@

      注æ„: テスト計画ã®ãƒ‰ãƒ­ãƒƒãƒ—ダウンã«ãƒ†ã‚¹ãƒˆè¨ˆç”»ãŒè¡¨ç¤ºã•れãªã„å ´åˆã‚‚ã‚りã¾ã™ã€‚ ã“ã®ã‚ˆã†ãªçжæ³ã§ã¯ã€ã™ã¹ã¦ã®ãƒªãƒ³ã‚¯ã‚’使用ã™ã‚‹ã“ã¨ãŒã§ããªã„ã§ã—ょã†ã€‚ テストリーダもã—ãã¯ç®¡ç†è€…ã«é€£çµ¡ã‚’å–ã‚Šã€æ¨©é™ã‚’アサインã—ã¦ã‚‚らã†ã‹ -æ–°ãŸãªãƒ†ã‚¹ãƒˆè¨ˆç”»ã‚’作æˆã—ã¦ã‚‚らã†ã‚ˆã†ã«ã—ã¦ãã ã•ã„。

      "; - -// custom_fields.html +æ–°ãŸãªãƒ†ã‚¹ãƒˆè¨ˆç”»ã‚’作æˆã—ã¦ã‚‚らã†ã‚ˆã†ã«ã—ã¦ãã ã•ã„。

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      カスタムフィールド

      カスタムフィールドã¯ä»¥ä¸‹ã®ã‚ˆã†ã«å®Ÿè£…ã•れã¦ã„ã¾ã™:

        @@ -145,16 +145,16 @@
      • カスタムフィールドãŒã©ã‚“ãªé …ç›®ã«å±žã—ã¦ã„ã‚‹ã‹ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ã€‚
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      テストケースã®å®Ÿè¡Œ

      ユーザã«ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ã€Œå®Ÿè¡Œã€ã‚’許å¯ã—ã¾ã™ã€‚実行自体ã¯å˜ã«ãƒ“ルドã«å¯¾ã™ã‚‹çµæžœ (æˆåŠŸ,失敗,ブロック)をテストケースã«å‰²ã‚Šå½“ã¦ã¦ã„ã¾ã™ã€‚

      ãƒã‚°ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã‚ˆã†ã«è¨­å®šã™ã‚‹ã“ã¨ã‚‚å¯èƒ½ã§ã™ã€‚ -ãã®å ´åˆã¯ã€ãƒã‚°ã®ç™»éŒ²ã‚„ã€æ—¢å­˜ãƒã‚°ã®é–²è¦§ãªã©ã‚’ãŠã“ãªã†ã“ã¨ãŒã§ãã¾ã™ã€‚

      "; - -//bug_add.html +ãã®å ´åˆã¯ã€ãƒã‚°ã®ç™»éŒ²ã‚„ã€æ—¢å­˜ãƒã‚°ã®é–²è¦§ãªã©ã‚’ãŠã“ãªã†ã“ã¨ãŒã§ãã¾ã™ã€‚

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      テストケースã¨ãƒã‚°ã‚’関連付ã‘ã‚‹

      設定を行ã£ãŸå ´åˆ TestLinkã¯ãƒã‚°ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ (BTS)ã¨ã‚¹ãƒ ãƒ¼ã‚ºã«é€£æºã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚
      @@ -176,21 +176,19 @@

    ãƒã‚°è¿½åŠ ãƒšãƒ¼ã‚¸ã‚’é–‰ã˜ãŸå¾Œã€å•題ã¨ãªã£ã¦ã„ã‚‹ãƒã‚°ã®ãƒ‡ãƒ¼ã‚¿ã‚’テスト実行ページã‹ã‚‰ç¢ºã‹ã‚ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    テスト実行時ã®ãƒ“ルドã«ã‚ˆã‚‹ãƒ•ィルタリング

    -

    å·¦å´ã®ã‚·ãƒ¼ãƒˆã¯ã€ç¾åœ¨ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«ã‚¢ã‚µã‚¤ãƒ³ã•れã¦ã„るテストケースã®ãƒŠãƒ“ゲータã€ãŠã‚ˆã³ã€" . -"設定ã¨ãƒ•ィルターã®ä¸€è¦§è¡¨ã§æ§‹æˆã•れã¦ã„ã¾ã™ã€‚" . -"ã“ã®ãƒ•ィルターを使用ã™ã‚‹ã“ã¨ã§ãƒ†ã‚¹ãƒˆå®Ÿè¡Œå‰ã«ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’絞り込むã“ã¨ãŒã§ãã¾ã™ã€‚" . -"フィルターã®ã‚ªãƒ–ションを設定ã—「é©ç”¨ã€ãƒœã‚¿ãƒ³ã‚’クリックã—ãŸå¾Œã«ã€" . -"ツリーã‹ã‚‰ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’é¸æŠžã—ã¾ã™ã€‚

    +

    å·¦å´ã®ã‚·ãƒ¼ãƒˆã¯ã€ç¾åœ¨ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«ã‚¢ã‚µã‚¤ãƒ³ã•れã¦ã„るテストケースã®ãƒŠãƒ“ゲータã€ãŠã‚ˆã³ã€" . "設定ã¨ãƒ•ィルターã®ä¸€è¦§è¡¨ã§æ§‹æˆã•れã¦ã„ã¾ã™ã€‚" . + "ã“ã®ãƒ•ィルターを使用ã™ã‚‹ã“ã¨ã§ãƒ†ã‚¹ãƒˆå®Ÿè¡Œå‰ã«ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’絞り込むã“ã¨ãŒã§ãã¾ã™ã€‚" . + "フィルターã®ã‚ªãƒ–ションを設定ã—「é©ç”¨ã€ãƒœã‚¿ãƒ³ã‚’クリックã—ãŸå¾Œã«ã€" . + "ツリーã‹ã‚‰ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’é¸æŠžã—ã¾ã™ã€‚

    ビルドã«ã‚ˆã‚‹ãƒ•ィルター

    -

    ビルドã”ã¨ã«ãƒ•ィルターをã‹ã‘ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚" . -"テストケースã®è¨˜éŒ²ã‚’追ã†ãŸã‚ã®æœ€ã‚‚基本的ãªè¦ç´ ãŒãƒ“ルドã§ã™ã€‚" . -"å„テストケースã¯ã€ãƒ“ルド内ã§è¤‡æ•°å›žå®Ÿè¡Œã§ãã¾ã™ãŒæœ€çµ‚çµæžœã¨ã—ã¦ç™»éŒ²ã•れるã®ã¯1ã¤ã®ã¿ã§ã™ã€‚ +

    ビルドã”ã¨ã«ãƒ•ィルターをã‹ã‘ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚" . "テストケースã®è¨˜éŒ²ã‚’追ã†ãŸã‚ã®æœ€ã‚‚基本的ãªè¦ç´ ãŒãƒ“ルドã§ã™ã€‚" . + "å„テストケースã¯ã€ãƒ“ルド内ã§è¤‡æ•°å›žå®Ÿè¡Œã§ãã¾ã™ãŒæœ€çµ‚çµæžœã¨ã—ã¦ç™»éŒ²ã•れるã®ã¯1ã¤ã®ã¿ã§ã™ã€‚
    æ–°è¦ãƒ“ルドã®ä½œæˆã®ãƒšãƒ¼ã‚¸ã§æ–°è¦ãƒ“ルドを作æˆã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    テストケースã®IDã«ã‚ˆã‚‹ãƒ•ィルター

    @@ -198,42 +196,40 @@ ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ä½œæˆæ™‚ã«è‡ªå‹•ã§ç”Ÿæˆã•れã¾ã™ã€‚ã“ã®æ¬„を空欄ã«ã™ã‚‹ã¨ãƒ•ィルターãŒç„¡åйã«ãªã‚Šã¾ã™ã€‚

    優先度ã«ã‚ˆã‚‹ãƒ•ィルター

    -

    優先度ã«ã‚ˆã‚Šãƒ•ィルターをã‹ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚å„テストケースã«ã¯é‡è¦åº¦ãŒã€" . -"テスト計画ã«ã¯ç·Šæ€¥åº¦ãŒè¨­å®šã•れã€å„ªå…ˆåº¦ãŒè¨ˆç®—ã•れã¾ã™ã€‚例ãˆã°ã€å„ªå…ˆåº¦ã€Œé«˜ã€ã®ãƒ•ィルターã§ã¯ã€" . -"é‡è¦åº¦ã‚‚ã—ãã¯ç·Šæ€¥åº¦ãŒã€Œé«˜ã€ã§ã€ã‚‚ã†ä¸€æ–¹ãŒã€Œä¸­ã€ä»¥ä¸Šã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’表示ã—ã¾ã™ã€‚

    +

    優先度ã«ã‚ˆã‚Šãƒ•ィルターをã‹ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚å„テストケースã«ã¯é‡è¦åº¦ãŒã€" . + "テスト計画ã«ã¯ç·Šæ€¥åº¦ãŒè¨­å®šã•れã€å„ªå…ˆåº¦ãŒè¨ˆç®—ã•れã¾ã™ã€‚例ãˆã°ã€å„ªå…ˆåº¦ã€Œé«˜ã€ã®ãƒ•ィルターã§ã¯ã€" . + "é‡è¦åº¦ã‚‚ã—ãã¯ç·Šæ€¥åº¦ãŒã€Œé«˜ã€ã§ã€ã‚‚ã†ä¸€æ–¹ãŒã€Œä¸­ã€ä»¥ä¸Šã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’表示ã—ã¾ã™ã€‚

    çµæžœã«ã‚ˆã‚‹ãƒ•ィルター

    テストã®çµæžœã”ã¨ã«ãƒ•ィルターをã‹ã‘ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ ãƒ†ã‚¹ãƒˆçµæžœã¯ã€ã‚るビルドã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®å®Ÿè¡Œä¸­ã«ä½•ãŒã‚ã£ãŸã‹ã‚’表ã—ã¦ã„ã¾ã™ã€‚ -テストケースã®å®Ÿè¡Œã®çµæžœã¨ã—ã¦ã€æˆåŠŸã€å¤±æ•—ã€ãƒ–ãƒ­ãƒƒã‚¯ã€æœªå®Ÿè¡Œã®ã„ãšã‚Œã‹ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒä¸Žãˆã‚‰ã‚Œã¾ã™ã€‚" . -"ã“ã®ãƒ•ィルターã¯ãƒ‡ãƒ•ォルトã§ã¯ç„¡åйã«ãªã£ã¦ã„ã¾ã™ã€‚

    +テストケースã®å®Ÿè¡Œã®çµæžœã¨ã—ã¦ã€æˆåŠŸã€å¤±æ•—ã€ãƒ–ãƒ­ãƒƒã‚¯ã€æœªå®Ÿè¡Œã®ã„ãšã‚Œã‹ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒä¸Žãˆã‚‰ã‚Œã¾ã™ã€‚" . + "ã“ã®ãƒ•ィルターã¯ãƒ‡ãƒ•ォルトã§ã¯ç„¡åйã«ãªã£ã¦ã„ã¾ã™ã€‚

    テスト担当者ã«ã‚ˆã‚‹ãƒ•ィルター

    -

    アサイン計画ã«å¾“ã£ã¦ãƒ•ィルターをã‹ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã¾ãŸã€ã€Œæœªã‚¢ã‚µã‚¤ãƒ³ã‚’å«ã‚€ã€ã®" . -"ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã«ã‚ˆã‚Šã€ã‚¢ã‚µã‚¤ãƒ³ã•れã¦ã„ãªã„テストケースを表示ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    "; -/* -

    最新ã®çµæžœ

    -

    デフォルトã€ã¾ãŸã¯ã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ãŒã‚ªãƒ•ã®å ´åˆã€ãƒ„リー㯠-ドロップダウンボックスã‹ã‚‰é¸æŠžã•れãŸãƒ“ルドã«ã‚ˆã£ã¦ã‚½ãƒ¼ãƒˆã•れã¾ã™ã€‚ -ã“ã®çŠ¶æ…‹ã§ã¯ã€ãƒ„リーã«ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒè¡¨ç¤ºã•れã¾ã™ã€‚ -
    例: ユーザーãŒã€ãƒ‰ãƒ­ãƒƒãƒ—ダウンボックスã‹ã‚‰ãƒ“ルド2ã‚’é¸æŠžã—ã€ã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’ -オフã«ã—ã¾ã™ã€‚ビルド2ã‹ã‚‰ã™ã¹ã¦ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã¨ã¨ã‚‚ã«è¡¨ç¤ºã•れã¾ã™ã€‚ -ã—ãŸãŒã£ã¦ã€ãƒ“ルド2ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹1ãŒãƒ‘スã™ã‚‹ã¨ç·‘色ã«ãªã‚Šã¾ã™ã€‚ -
    ユーザãŒã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’オンã«ã—ãŸå ´åˆã€ -ãƒ„ãƒªãƒ¼ã¯æœ€æ–°ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®çµæžœã§è‰²ãŒæ±ºã¾ã‚Šã¾ã™ã€‚ -
    例: ユーザーãŒãƒ‰ãƒ­ãƒƒãƒ—ダウンボックスã‹ã‚‰ãƒ“ルド2ã‚’é¸æŠžã—ã€ä»Šå›žã¯ã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¾ã™ã€‚ -ã™ã¹ã¦ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯æœ€æ–°ã®çŠ¶æ…‹ã§è¡¨ç¤ºã•れã¾ã™ã€‚ ã—ãŸãŒã£ã¦ã€ãƒ“ルド3ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹1ãŒãƒ‘スã—ãŸå ´åˆã€ -ユーザーãŒãƒ“ルド2ã‚‚é¸æŠžã—ã¦ã„ã¦ã‚‚ã€ç·‘色ã«ãªã‚Šã¾ã™ã€‚

    - */ - - -// newest_tcversions.html +

    アサイン計画ã«å¾“ã£ã¦ãƒ•ィルターをã‹ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã¾ãŸã€ã€Œæœªã‚¢ã‚µã‚¤ãƒ³ã‚’å«ã‚€ã€ã®" . + "ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã«ã‚ˆã‚Šã€ã‚¢ã‚µã‚¤ãƒ³ã•れã¦ã„ãªã„テストケースを表示ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    "; +/* + *

    最新ã®çµæžœ

    + *

    デフォルトã€ã¾ãŸã¯ã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ãŒã‚ªãƒ•ã®å ´åˆã€ãƒ„リー㯠+ * ドロップダウンボックスã‹ã‚‰é¸æŠžã•れãŸãƒ“ルドã«ã‚ˆã£ã¦ã‚½ãƒ¼ãƒˆã•れã¾ã™ã€‚ + * ã“ã®çŠ¶æ…‹ã§ã¯ã€ãƒ„リーã«ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒè¡¨ç¤ºã•れã¾ã™ã€‚ + *
    例: ユーザーãŒã€ãƒ‰ãƒ­ãƒƒãƒ—ダウンボックスã‹ã‚‰ãƒ“ルド2ã‚’é¸æŠžã—ã€ã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’ + * オフã«ã—ã¾ã™ã€‚ビルド2ã‹ã‚‰ã™ã¹ã¦ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã¨ã¨ã‚‚ã«è¡¨ç¤ºã•れã¾ã™ã€‚ + * ã—ãŸãŒã£ã¦ã€ãƒ“ルド2ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹1ãŒãƒ‘スã™ã‚‹ã¨ç·‘色ã«ãªã‚Šã¾ã™ã€‚ + *
    ユーザãŒã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’オンã«ã—ãŸå ´åˆã€ + * ãƒ„ãƒªãƒ¼ã¯æœ€æ–°ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®çµæžœã§è‰²ãŒæ±ºã¾ã‚Šã¾ã™ã€‚ + *
    例: ユーザーãŒãƒ‰ãƒ­ãƒƒãƒ—ダウンボックスã‹ã‚‰ãƒ“ルド2ã‚’é¸æŠžã—ã€ä»Šå›žã¯ã€Œæœ€æ–°ã€ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’ãƒã‚§ãƒƒã‚¯ã—ã¾ã™ã€‚ + * ã™ã¹ã¦ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯æœ€æ–°ã®çŠ¶æ…‹ã§è¡¨ç¤ºã•れã¾ã™ã€‚ ã—ãŸãŒã£ã¦ã€ãƒ“ルド3ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹1ãŒãƒ‘スã—ãŸå ´åˆã€ + * ユーザーãŒãƒ“ルド2ã‚‚é¸æŠžã—ã¦ã„ã¦ã‚‚ã€ç·‘色ã«ãªã‚Šã¾ã™ã€‚

    + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®æœ€æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’関連ã¥ã‘ã‚‹

    テスト計画ã«é–¢é€£ã¥ã‘られãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹å…¨ä½“ã¯åˆ†æžã•ã‚Œã€æœ€æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ä¸€è¦§ãŒè¡¨ç¤ºã•れã¾ã™ã€‚ -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    è¦ä»¶ç¶²ç¾…率


    ã“ã®æ©Ÿèƒ½ã‚’用ã„ã‚‹ã¨ã€ãƒ¦ãƒ¼ã‚¶ã¾ãŸã¯ã‚·ã‚¹ãƒ†ãƒ ãŒè¦æ±‚ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ç¶²ç¾…率ã®ãƒžãƒƒãƒ—を作æˆã§ãã¾ã™ã€‚ @@ -266,17 +262,16 @@ (デフォルトã§ã¯ \$tlCfg->req_cfg->default_testsuite_name = 'Test suite created by Requirement - Auto';)。 タイトルã¨ã‚¹ã‚³ãƒ¼ãƒ—ã¯ä½œæˆã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«ã‚³ãƒ”ーã•れã¾ã™ã€‚

    -"; - +"; + $TLS_hlp_req_coverage_table = "

    網羅率:

    \"40% (8/20)\"ã¨ã‚ã£ãŸå ´åˆã€20ã¯ã“ã®è¦ä»¶ã‹ã‚‰ç”Ÿæˆã•れるã¹ãテストケース数〠8ã¯ã€æ—¢ã«ã“ã®è¦ä»¶ã‹ã‚‰ç”Ÿæˆã•れã€ç´ä»˜ã‘られãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹æ•°ã€ã“れらã®å€¤ã‹ã‚‰ç¶²ç¾…率ã¯40%ã¨ãªã‚Šã¾ã™ã€‚ -"; - - -// req_edit +"; + +// req_edit $TLS_hlp_req_edit = "

    スコープã®å†…部リンク:

    -

    内部リンクã¯ã€ä»–ã®è¦ä»¶/è¦ä»¶ä»•様ã¸ã®ãƒªãƒ³ã‚¯ã‚’作æˆã™ã‚‹ç›®çš„ã§ç‰¹åˆ¥ãªæ§‹æ–‡ã§æä¾›ã•れã¾ã™ã€‚ +

    内部リンクã¯ã€ä»–ã®è¦ä»¶/è¦ä»¶ä»•様ã¸ã®ãƒªãƒ³ã‚¯ã‚’作æˆã™ã‚‹ç›®çš„ã§ç‰¹åˆ¥ãªæ§‹æ–‡ã§æä¾›ã•れã¾ã™ã€‚ 内部リンクã®å‹•作ã¯ã€è¨­å®šãƒ•ァイルã§å¤‰æ›´ã§ãã¾ã™ã€‚

    使用方法: @@ -294,10 +289,9 @@

    変更ãŒè¡Œã‚れるãŸã³ã«ã€Testlinkã¯ãƒ­ã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’è¦æ±‚ã—ã¾ã™ã€‚ ã“ã®ãƒ­ã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã¯ã€ãƒˆãƒ¬ãƒ¼ã‚µãƒ“ãƒªãƒ†ã‚£ã‚’ç›®çš„ã«æä¾›ã•れã¾ã™ã€‚ è¦ä»¶ã®ã‚¹ã‚³ãƒ¼ãƒ—ã®ã¿ãŒå¤‰æ›´ã•れãŸå ´åˆã¯ã€æ–°ã—ã„リビジョンを作æˆã™ã‚‹ã‹ã©ã†ã‹ã‚’è‡ªç”±ã«æ±ºã‚ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ スコープ以外ã®ã‚‚ã®ãŒå¤‰æ›´ã•れãŸå ´åˆã¯ã€å¸¸ã«æ–°ã—ã„リビジョンを作æˆã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

    -"; - - -// req_view +"; + +// req_view $TLS_hlp_req_view = "

    ダイレクトリンク:

    ã“ã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ç°¡å˜ã«å…±æœ‰ã™ã‚‹ã«ã¯ã€ã“ã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã®ä¸Šéƒ¨ã«ã‚る地çƒã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’クリックã—ã¦ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆãƒªãƒ³ã‚¯ã‚’作æˆã—ã¾ã™ã€‚

    @@ -314,12 +308,11 @@ 設定ファイルã§è¨­å®šã§ãã¾ã™ã€‚ 「è¦ä»¶Aã¯è¦ä»¶Bã®è¦ªã€ã¨ã„ã†é–¢é€£ã‚’設定ã™ã‚‹å ´åˆã€ Testlinkã¯ã€ã€Œè¦ä»¶Bã¯è¦ä»¶Aã®å­ã€ã¨ã„ã†é–¢é€£ã‚’暗黙的ã«è¨­å®šã—ã¾ã™

    -"; - - -// req_spec_edit +"; + +// req_spec_edit $TLS_hlp_req_spec_edit = "

    スコープã®å†…部リンク:

    -

    内部リンクã¯ã€ä»–ã®è¦ä»¶/è¦ä»¶ä»•様ã¸ã®ãƒªãƒ³ã‚¯ã‚’作æˆã™ã‚‹ç›®çš„ã§ç‰¹åˆ¥ãªæ§‹æ–‡ã§æä¾›ã•れã¾ã™ã€‚ +

    内部リンクã¯ã€ä»–ã®è¦ä»¶/è¦ä»¶ä»•様ã¸ã®ãƒªãƒ³ã‚¯ã‚’作æˆã™ã‚‹ç›®çš„ã§ç‰¹åˆ¥ãªæ§‹æ–‡ã§æä¾›ã•れã¾ã™ã€‚ 内部リンクã®å‹•作ã¯ã€è¨­å®šãƒ•ァイルã§å¤‰æ›´ã§ãã¾ã™ã€‚

    使用方法: @@ -332,39 +325,23 @@ [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
    ã“ã®æ§‹æ–‡ã¯ã€è¦ä»¶ä»•様ã§ã‚‚機能ã—ã¾ã™ (ãƒãƒ¼ã‚¸ãƒ§ãƒ³å±žæ€§ã¯åŠ¹æžœãŒã‚りã¾ã›ã‚“)。
    ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’指定ã—ãªã„å ´åˆã¯ã€ã™ã¹ã¦ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å«ã‚€è¦ä»¶å…¨ä½“ãŒè¡¨ç¤ºã•れã¾ã™ã€‚

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    「カスタムフィールドã®ä¿å­˜ã€ã«ã¤ã„ã¦

    例ãˆã°ã€ä»¥ä¸‹ã®ã‚«ã‚¹ã‚¿ãƒ ãƒ•ィールドをテストプロジェクトã§å®šç¾©ãŠã‚ˆã³ã‚¢ã‚µã‚¤ãƒ³ã™ã‚‹ã¨ã—ã¾ã™ã€‚
    「テスト計画ã§è¡¨ç¤ºã™ã‚‹=trueã€ãŠã‚ˆã³
    ã€Œãƒ†ã‚¹ãƒˆè¨ˆç”»ã§æœ‰åйã«ã™ã‚‹=trueã€
    ã“ã®ã¨ãã€ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«ã‚¢ã‚µã‚¤ãƒ³ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«é–¢ã™ã‚‹ãƒšãƒ¼ã‚¸ã®ã¿ã§ã“ã®ã‚«ã‚¹ã‚¿ãƒ ãƒ•ィールドãŒè¡¨ç¤ºã•れã„ã¾ã™ã€‚ -"; - - -// resultsByTesterPerBuild.tpl +"; + +// resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = <<<'TLS_hlp_results_by_tester_per_build_table' テスト担当者ã«é–¢ã™ã‚‹æ›´ãªã‚‹æƒ…å ±
    担当者åをクリックã™ã‚‹ã¨ã€ãã®æ‹…当者ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸå…¨ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¨ã€å½¼ã®ãƒ†ã‚¹ãƒˆé€²æ—ã«é–¢ã™ã‚‹è©³ç´°ã‚’見るã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚

    注記
    ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã€æŒ‡å®šã®æ‹…当者ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã€æ´»æ€§åŒ–中ã®å„ビルドã«åŸºã¥ã„ã¦å®Ÿè¡Œã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’表示ã—ã¾ã™ã€‚
    ãŸã ã—ã€å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸã®ã¨ã¯åˆ¥ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå®Ÿæ–½ã—ãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯ã€åˆ¥ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®å®Ÿæ–½ã‚±ãƒ¼ã‚¹ã¨ã—ã¦è¡¨ç¤ºã•れã¾ã™ã€‚ -TLS_hlp_results_by_tester_per_build_table; -/* -"More information about testers:
    -If you click on a tester name in this table, you will get a more detailed overview -about all Test Cases assigned to that user and his testing progress.

    -Note:
    -This Report shows those test cases, which are assigned to a specific user and have been executed -based on each active build. Even if a test case has been executed by another user than the assigned user, -the test case will appear as executed for the assigned user. -"; -*/ - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +TLS_hlp_results_by_tester_per_build_table; + ?> diff --git a/locale/ja_JP/strings.txt b/locale/ja_JP/strings.txt index 17ea26b0c9..bd8b959298 100644 --- a/locale/ja_JP/strings.txt +++ b/locale/ja_JP/strings.txt @@ -87,6 +87,8 @@ $TLS_click_to_set_closed = "オープン (クリックã™ã‚‹ã¨ã‚¯ãƒ­ãƒ¼ã‚º)"; / $TLS_click_to_disable = "ä½¿ç”¨å¯ (クリックã—ã¦ä½¿ç”¨ä¸å¯ã«)"; // "Enabled (click to disable)"; $TLS_click_to_enable = "使用ä¸å¯ (クリックã—ã¦ä½¿ç”¨å¯ã«)"; // "Disabled (click to enable)"; $TLS_current_testcase = "ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯"; // "This test case"; +$TLS_this_tcversion = "ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯"; // "This test case version "; + $TLS_confirm = "確èª"; $TLS_config= "æ§‹æˆ"; // "Configuration"; $TLS_created_by= "作æˆè€…"; // "Created by"; @@ -277,6 +279,7 @@ $TLS_update_hint = "ãƒã‚§ãƒƒã‚¯ã™ã‚‹ã¨ã€ä¿å­˜æ™‚ã«å€¤ã‚’æ›´æ–°ã—ã¾ã™"; // Buttons (used wide) $TLS_btn_apply = "é©ç”¨"; $TLS_btn_add = "追加"; +$TLS_btn_add_to_testsuites_deep = "å­è¦ç´ ã«ã‚‚追加 (テストスイート)"; $TLS_btn_add_to_testplans = "テスト計画ã«è¿½åŠ "; $TLS_btn_assign = "割り当ã¦"; $TLS_btn_cancel = "キャンセル"; @@ -324,6 +327,12 @@ $TLS_btn_reorder_steps = "ステップ順åºã‚’決定"; // "Reorder Steps"; $TLS_btn_resequence_steps = "ステップ順åºã®æ•´ç†"; // "Resequence Steps"; $TLS_btn_view_history = "履歴ã®è¡¨ç¤º"; $TLS_btn_testcases_table_view = 'テストケースを表形å¼ã§è¡¨ç¤º'; // 'Test Cases table view'; +$TLS_btn_remove_all_users = "ã™ã¹ã¦ã®å‰²ã‚Šå½“ã¦ã‚’削除"; +$TLS_btn_apply_assign = "割り当ã¦ã‚’é©ç”¨"; +$TLS_btn_save_assign = "割り当ã¦ã‚’ä¿å­˜"; +$TLS_btn_remove = "削除"; +$TLS_btn_remove_assignments = "割り当ã¦ã‚’削除"; +$TLS_btn_remove_current_assignments = "ã™ã¹ã¦ã®å‰²ã‚Šå½“ã¦ã‚’削除 (存在ã™ã‚‹å ´åˆ)"; // Status (used wide) @@ -398,6 +407,7 @@ $TLS_CSRF_attack = "クロスサイトリクエストå½é€ æ”»æ’ƒ!"; // "Cross-S $TLS_already_exists_not_updated = "æ—¢ã«å­˜åœ¨ã—ã¾ã™ => æ›´æ–°ã—ã¾ã›ã‚“"; // "Already exists => not updated"; $TLS_assign_exec_task_to_me = "自分ã«å‰²ã‚Šå½“ã¦"; // "Assign task to me"; $TLS_chosen_blank_option = " "; +$TLS_already_exists_skipped = "æ—¢ã«å­˜åœ¨ã—ã¾ã™ => スキップã—ã¾ã—ãŸ"; $TLS_monitorable = '監視å¯èƒ½'; // 'Monitorable'; TODO 機能ãŒã¾ã ã‚ˆã分ã‹ã£ã¦ãªã„ã®ã§ã€è¨³ãŒé©åˆ‡ã‹ä¸æ˜Ž @@ -484,7 +494,7 @@ $TLS_warn_demo = "申ã—訳ã‚りã¾ã›ã‚“。ã“ã®æ©Ÿèƒ½ã¯ãƒ‡ãƒ¢ã§ã¯ä½¿ç”¨ // Installation/Migration (TODO remove - migration is English only) $TLS_start_warning = '---- 警告 ----'; -$TLS_testlink_warning = 'Test Link 警告'; +$TLS_testlink_warning = 'TestLink 警告'; $TLS_testcase_name_too_long = 'テストケースåãŒé•·ã™ãŽã¾ã™(%s 文字) > %s => ã¯çœç•¥ã•れã¾ã—ãŸ'; $TLS_original_name = 'å…ƒã®åå‰'; $TLS_end_warning = '---- *** ----'; @@ -535,6 +545,8 @@ $TLS_urgency_low = "低"; $TLS_test_importance = "テストé‡è¦åº¦"; $TLS_testcases_assigned_to_user = 'テストプロジェクト: %s - テストケースã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„るユーザー: %s'; $TLS_assigned_on = 'å‰²ã‚Šå½“ã¦æ—¥æ™‚'; +$TLS_code_mgmt = "コード管ç†"; +$TLS_code_link_tl_to_cts = "登録済ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¯ãƒªãƒ—トã«ãƒªãƒ³ã‚¯"; $TLS_access_denied = 'ã‚¢ã‚¯ã‚»ã‚¹ãŒæ‹’å¦ã•れã¾ã—ãŸ'; $TLS_access_denied_feedback = 'è¦æ±‚ã—ãŸã‚¢ã‚¤ãƒ†ãƒ ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã«ã¯ä»¥ä¸‹ã®æ¨©é™ã®ã†ã¡1ã¤å¿…è¦ã§ã™:'; @@ -612,7 +624,7 @@ $TLS_password_reseted = "æ–°ã—ã„パスワードをメールã§é€ä¿¡ã—ã¾ã— $TLS_please_login = "ログインã—ã¦ãã ã•ã„ ..."; $TLS_session_expired = "ã‚»ãƒƒã‚·ãƒ§ãƒ³ã®æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã¾ã™! å†åº¦ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„。"; $TLS_your_first_login = "よã†ã“ãTestLinkã¸! ã‚ãªãŸã¯ Guest アカウントを使用ã—ã¦ã„ã¾ã™ã€‚ ログインã—ã¦ãã ã•ã„ ..."; - +$TLS_oauth_login = "次ã§ãƒ­ã‚°ã‚¤ãƒ³ "; // ----- newest_tcversions.php ----- $TLS_no_linked_tcversions = "関連付ã‘られã¦ã„るテストケースã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒã‚りã¾ã›ã‚“"; @@ -688,7 +700,7 @@ $TLS_linked_but_not_executed = "ã„ãã¤ã‹ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ã¨ãƒªãƒ³ã‚¯ã—ã¾ $TLS_question_del_testsuite = "削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹?"; $TLS_th_link_exec_status = "リンクãŠã‚ˆã³å®Ÿè¡Œã®çжæ³"; $TLS_testsuite_successfully_deleted = "ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã¯æ­£å¸¸ã«å‰Šé™¤ã•れã¾ã—ãŸã€‚"; -$TLS_no_links = "No dependent data."; +$TLS_no_links = "ä¾å­˜ã™ã‚‹ãƒ‡ãƒ¼ã‚¿ã¯ã‚りã¾ã›ã‚“。"; $TLS_linked_and_executed = "ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«å«ã¾ã‚Œã€ã‹ã¤å®Ÿè¡Œæ¸ˆã¿ã§ã™"; // "Test case was added into a Test Plan and has results."; // $TLS_system_blocks_tsuite_delete_due_to_exec_tc = @@ -706,6 +718,21 @@ $TLS_title_create = "作æˆ"; $TLS_warning_empty_testsuite_name = "テストスイートã«åå‰ã‚’ã¤ã‘ã¦ãã ã•ã„"; +// ----- gui/templates/script_add.tpl ----- +$TLS_script_id = "テストスクリプトå"; +$TLS_title_script_add = "テストスクリプトã¸ã®ãƒªãƒ³ã‚¯ã‚’追加"; +$TLS_btn_add_script = "テストスクリプトã®ãƒªãƒ³ã‚¯ã‚’追加"; +$TLS_link_cts_create_script = "コード管ç†ã‚·ã‚¹ãƒ†ãƒ ã«ã‚¢ã‚¯ã‚»ã‚¹ "; +$TLS_cts_project_key = "プロジェクトキー"; +$TLS_cts_repo_name = "リãƒã‚¸ãƒˆãƒªå"; +$TLS_cts_script_path = "テストスクリプトã®ãƒ‘ス"; +$TLS_cts_branch_name = "ブランãƒå (ä»»æ„)"; + +// ----- gui/templates/script_delete.tpl ----- +$TLS_title_delete_script = "テストスクリプトã®ãƒªãƒ³ã‚¯ã‚’削除"; + + + // ----- gui/templates/bug_add.tpl ----- $TLS_btn_add_bug = "ä¸å…·åˆã®è¿½åŠ "; @@ -781,6 +808,7 @@ $TLS_node_type_dbtable_testcases = $TLS_testcase; // ----- gui/templates/containerView.tpl ----- $TLS_testsuite_operations = "テストスイートæ“作"; $TLS_testcase_operations = "テストケースæ“作"; +$TLS_testcase_version_operations = "テストケースã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³æ“作"; $TLS_alt_del_testsuite = "ã“ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’削除"; $TLS_alt_edit_testsuite = "ã“ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®ãƒ‡ãƒ¼ã‚¿ã¨ã‚¿ã‚¤ãƒˆãƒ«ã‚’修正"; $TLS_alt_move_cp_testsuite = "ã“ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’ä»–ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã«ç§»å‹•ã¾ãŸã¯ã‚³ãƒ”ー"; @@ -873,7 +901,7 @@ $TLS_was_success = " æˆåŠŸã—ã¾ã—ãŸ"; // ----- gui/templates/inc_filter_panel.tpl ----- $TLS_logical_and = "ã‹ã¤"; $TLS_logical_or = "ã¾ãŸã¯"; -$TLS_not_linked = "リンクã•れã¦ã„ã¾ã›ã‚“"; +$TLS_not_linked = "リンクã•れã¦ã„ãªã„"; $TLS_btn_apply_filter = $TLS_btn_apply; $TLS_btn_reset_filters = "抽出æ¡ä»¶ãƒªã‚»ãƒƒãƒˆ"; // "Reset Filters"; $TLS_caption_nav_filters = "フィルター"; @@ -1253,6 +1281,11 @@ $TLS_delete_bug = "ä¸å…·åˆå ±å‘Šã®å‰Šé™¤"; $TLS_del_bug_warning_msg = "ã“ã®ä¸å…·åˆã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"; +// gui/templates/inc_show_script_table.tpl +$TLS_caption_scripttable = "関連ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚¹ã‚¯ãƒªãƒ—ト"; +$TLS_delete_script ="TestLink ã‹ã‚‰ã‚¹ã‚¯ãƒªãƒ—トã®ãƒªãƒ³ã‚¯ã‚’削除"; +$TLS_del_script_warning_msg = "TestLink データベースã‹ã‚‰ã‚¹ã‚¯ãƒªãƒ—トã®ãƒªãƒ³ã‚¯ã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹?"; + // gui/templates/reqSpecList.tpl $TLS_btn_assign_tc = "テストケースã®å‰²ã‚Šå½“ã¦"; @@ -1294,6 +1327,7 @@ $TLS_menu_view_roles = "役割ã®ä¸€è¦§"; $TLS_th_execution_rights = 'テストケースã®å®Ÿæ–½'; $TLS_th_cf_rights = "カスタムフィールドã«é–¢ã™ã‚‹æ¨©é™"; $TLS_th_issuetracker_rights = '課題追跡'; +$TLS_th_codetracker_rights = 'コード追跡'; $TLS_th_kw_rights = $TLS_keyword; $TLS_th_mgttc_rights = "テストケース管ç†ã«é–¢ã™ã‚‹æ¨©é™"; $TLS_th_platform_rights = $TLS_platform; @@ -1335,6 +1369,10 @@ $TLS_jolly_hint = "タイトルã€è¦ç´„ã€å‰ææ¡ä»¶ã€ã‚¹ãƒ†ãƒƒãƒ—ã€æ“作 +// ----- gui/templates/testcases/tcBulkOp.tpl ----- +$TLS_force_frozen_testcases_versions = "強制的ã«ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å‡çµã™ã‚‹"; + + // ----- gui/templates/testcases/searchData.tpl ----- $TLS_too_wide_search_criteria = "çµæžœãŒå¤šã™ãŽã¾ã™ã€‚" . "よりé™å®šçš„ãªæ¤œç´¢æ¡ä»¶ã‚’設定ã—ã¦ãã ã•ã„。"; @@ -1346,7 +1384,9 @@ $TLS_too_wide_search_criteria = "çµæžœãŒå¤šã™ãŽã¾ã™ã€‚" . // ----- gui/templates/tcDelete.tpl ----- $TLS_btn_yes_iw2del = "ã¯ã„ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’削除ã—ã¾ã™ï¼"; +$TLS_btn_yes_iw2del_version = "ã¯ã„ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’削除ã—ã¾ã™ï¼"; $TLS_question_del_tc = "テストケースを削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"; +$TLS_question_del_tc_version = "ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"; $TLS_th_executed = "実行済ã¿"; $TLS_th_linked_to_tplan = "関連付ã‘られãŸãƒ†ã‚¹ãƒˆè¨ˆç”»"; $TLS_th_version = "ãƒãƒ¼ã‚¸ãƒ§ãƒ³"; @@ -1537,7 +1577,9 @@ $TLS_test_exec_by = "実施者"; $TLS_test_exec_expected_r = "期待値"; $TLS_test_exec_last_run_date = "最近ã®å®Ÿè¡Œ: "; $TLS_test_exec_notes = "備考 / 説明"; +$TLS_test_exec_notes_latest_created_build = "最終 (作æˆã•れãŸ) ビルドã®å®Ÿè¡Œè¨˜éŒ²"; $TLS_test_exec_on_build = "ビルド"; + $TLS_test_exec_result = "çµæžœ"; $TLS_test_exec_steps = "ステップ"; $TLS_test_exec_summary = "è¦ç´„"; @@ -1554,6 +1596,7 @@ $TLS_edit_execution = "å®Ÿè¡Œçµæžœã®ç·¨é›†"; // "edit execution"; $TLS_platform_description = "プラットフォームã®èª¬æ˜Ž"; $TLS_remoteExecFeeback = "リモートテストケース実行フィードãƒãƒƒã‚¯"; +$TLS_updateLinkToLatestTCVersion = 'リンクã•れ㟠TCV ã‚’æœ€æ–°ã«æ›´æ–°ã™ã‚‹'; // ----- gui/templates/inc_testsuite_viewer_ro.tpl ----- $TLS_keywords = "キーワード"; @@ -1561,17 +1604,17 @@ $TLS_keywords = "キーワード"; // ----- gui/templates/navBar.tpl ----- $TLS_access_doc = "文書"; -$TLS_home = "ホーム"; +$TLS_home = "デスクトップ"; $TLS_link_logout = "ログアウト"; $TLS_product = "テストプロジェクト"; $TLS_product_role = "テストプロジェクトã§ã®å½¹å‰²"; -$TLS_search_testcase = "検索"; -$TLS_title_edit_personal_data = "個人情報"; -$TLS_title_admin = "ユーザーã®ç®¡ç†"; +$TLS_search_testcase = "ID ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’検索"; +$TLS_title_edit_personal_data = "個人設定"; +$TLS_title_admin = "ユーザー/役割"; $TLS_title_events = "イベント"; -$TLS_title_execute = "実行"; -$TLS_title_results = "çµæžœ"; -$TLS_title_specification = "仕様"; +$TLS_title_execute = "テスト実行"; +$TLS_title_results = "ãƒ†ã‚¹ãƒˆçµæžœ"; +$TLS_title_specification = "テスト仕様"; $TLS_title_requirements = "è¦ä»¶"; $TLS_full_text_search = "全テキスト検索"; $TLS_session_inactivity_timeout_at = "セッションタイムアウト"; @@ -1764,11 +1807,14 @@ $TLS_testproject_enable_inventory = '機器機能'; $TLS_testproject_enable_priority = $TLS_enable_priority; $TLS_testproject_enable_requirements = $TLS_enable_requirements; $TLS_testproject_issue_tracker_integration = '課題追跡システムã¨ã®é€£æº'; // 'Issue Tracker Integration'; -$TLS_issue_tracker = '課題追跡・登録å'; // 'Issue Tracker'; -$TLS_testproject_reqmgr_integration = 'Req. Mgmt. System Integration'; +$TLS_testproject_code_tracker_integration = 'コード追跡システムã¨ã®é€£æº'; +$TLS_issue_tracker = '課題追跡システム'; // 'Issue Tracker'; +$TLS_code_tracker = 'コード追跡システム'; +$TLS_testproject_reqmgr_integration = 'è¦ä»¶ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ã¨ã®é€£æº'; $TLS_reqmgrsystem = 'è¦ä»¶ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ '; -$TLS_no_rms_defined = ' >> è¦ä»¶ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ãŒå®šç¾©ã•れã¦ã„ã¾ã›ã‚“ <<'; -$TLS_no_issuetracker_defined = '<èª²é¡Œè¿½è·¡ã‚·ã‚¹ãƒ†ãƒ ãŒæœªç™»éŒ²ã§ã™ã€‚「課題追跡システムã€ã§ç™»éŒ²ã—ã¦ãã ã•ã„>'; // ' >> There are no Issue Tracker Systems defined <<'; +$TLS_no_rms_defined = ' >> è¦ä»¶ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ãŒæœªç™»éŒ²ã§ã™ <<'; +$TLS_no_issuetracker_defined = ' >> èª²é¡Œè¿½è·¡ã‚·ã‚¹ãƒ†ãƒ ãŒæœªç™»éŒ²ã§ã™ã€‚「課題追跡システムã€ã§ç™»éŒ²ã—ã¦ãã ã•ã„ <<'; // ' >> There are no Issue Tracker Systems defined <<';$TLS_no_codetracker_defined = ' >> There are no Code Tracker Systems defined <<'; +$TLS_no_codetracker_defined = ' >> ã‚³ãƒ¼ãƒ‰è¿½è·¡ã‚·ã‚¹ãƒ†ãƒ ãŒæœªç™»éŒ²ã§ã™ã€‚「コード追跡システムã€ã§ç™»éŒ²ã—ã¦ãã ã•ã„ <<'; $TLS_info_failed_loc_prod = "ã“ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã¯ãƒ­ãƒ¼ã‚«ãƒ©ã‚¤ã‚ºã«å¤±æ•—ã—ã¾ã—ãŸ"; @@ -1840,11 +1886,14 @@ $TLS_btn_del_this_version = "ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’削除"; $TLS_btn_execute_automatic_testcase = "自動化ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’実行"; $TLS_btn_mv_cp = "移動 / コピー"; $TLS_btn_new_version = "æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ä½œæˆ"; +$TLS_btn_new_version_from_latest = "最終ã‹ã‚‰æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’作æˆ"; $TLS_btn_new_sibling = "æ–°è¦ä½œæˆ"; // "New sibling"; $TLS_hint_new_sibling = "ç¾åœ¨ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®ä¸‹ã«åˆ¥ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’作æˆ"; $TLS_hint_new_version = "æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’作æˆ"; $TLS_can_not_edit_tc = "ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯å®Ÿè¡Œä¸­ãªã®ã§ç·¨é›†ã§ãã¾ã›ã‚“"; +$TLS_can_not_edit_frozen_tc = "å‡çµã•れã¦ã„ã‚‹ãŸã‚ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’編集ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“"; +$TLS_can_not_delete_relation_frozen_tc = "ã“ã®é–¢ä¿‚を削除ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ : テストケースã¯å‡çµã•れã¦ã„ã¾ã™"; $TLS_deactivate_this_tcversion = "ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’無効ã«ã™ã‚‹"; $TLS_execution_type = '実行タイプ'; $TLS_execution_type_short_descr = '実行方法'; @@ -2419,6 +2468,9 @@ $TLS_warning_empty_first_name = "åを入力ã—ã¦ãã ã•ã„"; $TLS_warning_empty_last_name = "姓を入力ã—ã¦ãã ã•ã„"; $TLS_warning_empty_login = "ログインåを入力ã—ã¦ãã ã•ã„"; +// usersExport.php +$TLS_export_users = "ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ›¸ã出ã—"; + // ----- gui/templates/attachmentupload.tpl ----- $TLS_attachment_upload_ok = "ファイルをアップロードã—ã¾ã—ãŸ"; @@ -2453,7 +2505,6 @@ $TLS_warning_users_will_be_reset = "ユーザーãŒå½¹å‰²ã‚’与ãˆã‚‰ã‚Œã¾ã™:" // ----- gui/templates/tcexport.tpl ----- $TLS_export_filename = "書ã出ã—ファイルå"; - $TLS_export_tcase_items_title = "書ã出ã™é …目群"; $TLS_export_tcase_external_id = "テストケース 外部ID"; // 'Export Test Case external ID'; $TLS_export_with_prefix = "テストケースプレフィックス付ã"; // 'With Test Case prefix'; @@ -2541,6 +2592,7 @@ $TLS_href_plan_define_priority = "テスト計画優先度"; $TLS_href_my_testcase_assignments = "è‡ªåˆ†ãŒæ‹…当ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹"; $TLS_href_platform_management = "プラットフォームã®ç®¡ç†"; $TLS_href_issuetracker_management = "課題追跡システム"; // "Issue Tracker Management"; +$TLS_href_codetracker_management = "コード追跡システム"; $TLS_href_reqmgrsystem_management = "è¦æ±‚管ç†ã‚·ã‚¹ãƒ†ãƒ ç®¡ç†"; $TLS_system_config = "システム"; // 'System'; $TLS_title_plugins = "プラグイン"; // 'Plugins'; @@ -2610,6 +2662,16 @@ $TLS_progress_absolute = "進æ—"; $TLS_test_plan_progress = "テスト計画進æ—"; $TLS_project_progress = "テストプロジェクト進æ—"; +// ----- lib/testcases/script_add.php ----- +$TLS_script_added = "テストスクリプトã®ãƒªãƒ³ã‚¯ã‚’追加ã—ã¾ã—ãŸ"; +$TLS_error_code_does_not_exist_on_cts = "CTS ã«ã‚¹ã‚¯ãƒªãƒ—トã®ãƒªãƒ³ã‚¯ '%s' ã¯å­˜åœ¨ã—ã¾ã›ã‚“!"; +$TLS_error_script_not_added = "テストスクリプトã®ãƒªãƒ³ã‚¯ã‚’ DB ã«è¿½åŠ ã§ãã¾ã›ã‚“ã§ã—ãŸ"; + +// ----- lib/testcases/script_delete.php ----- +$TLS_scriptdeleting_was_ok = "テストスクリプトã®ãƒªãƒ³ã‚¯ãŒå‰Šé™¤ã•れã¾ã—ãŸ!"; +$TLS_error_script_not_deleted = "テストスクリプトã®ãƒªãƒ³ã‚¯ã‚’ DB ã‹ã‚‰å‰Šé™¤ã§ãã¾ã›ã‚“ã§ã—ãŸ"; + + // ----- lib/execute/bug_add.php ----- $TLS_bug_added = "ä¸å…·åˆå ±å‘ŠãŒè¿½åŠ ã•れã¾ã—ãŸ"; $TLS_bug_created = "ä¸å…·åˆã‚’追加ã—ã¾ã—ãŸ"; @@ -2632,7 +2694,7 @@ $TLS_error_ldap_update_failed = "LDAPã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ"; $TLS_error_ldap_user_not_found = "LDAPユーザーãŒå­˜åœ¨ã—ã¾ã›ã‚“"; $TLS_error_ldap_start_tls_failed = "LDAPãŒTLSã®é–‹å§‹ã«å¤±æ•—ã—ã¾ã—ãŸ"; $TLS_unknown_authentication_method = "èªè¨¼æ–¹æ³• %s ã®å‡¦ç†æ–¹æ³•ãŒä¸æ˜Ž"; - +$TLS_tluser_account_expired = "ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯æœŸé™åˆ‡ã‚Œã§ã™"; // ----- lib/functions/product.inc.php ----- $TLS_info_product_delete_fails = "テストプロジェクトã®å‰Šé™¤ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"; @@ -2697,6 +2759,8 @@ $TLS_desc_events_mgt = "イベント管ç†"; $TLS_desc_issuetrackers_view = "課題追跡ã®å‚照(読ã¿å–りã®ã¿ï¼‰"; // "Issue Tracker view (read only access)"; $TLS_desc_issuetrackers_management = "課題追跡システム"; // "Issue Tracker Management"; +$TLS_desc_codetrackers_view = "コード追跡ã®å‚ç…§ (読ã¿å–りã®ã¿)"; +$TLS_desc_codetrackers_management = "コード追跡システム"; $TLS_desc_reqmgrsystems_view = "è¦ä»¶ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ã®å‚照(読ã¿å–りã®ã¿ï¼‰"; // "Req. Management System view (read only access)"; $TLS_desc_reqmgrsystems_management = "è¦ä»¶ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ç®¡ç†"; // "Req. Management System Management"; $TLS_desc_req_tcase_link_management = "è¦ä»¶ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®å‰²ã‚Šå½“ã¦"; // "Requirement-Test Case Assignment"; @@ -2728,6 +2792,9 @@ $TLS_desc_testplan_execute = "テスト計画ã®å®Ÿè¡Œ"; $TLS_desc_testplan_metrics = "ãƒ†ã‚¹ãƒˆè¨ˆç”»ã®æŒ‡æ¨™"; $TLS_desc_testplan_planning = "テスト計画ã®ãƒ—ランニング"; $TLS_desc_user_role_assignment = "役割ã®å‰²ã‚Šå½“ã¦"; +$TLS_desc_testproject_user_role_assignment = "テストプロジェクトレベルã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å½¹å‰²ã‚’割り当ã¦"; +$TLS_desc_testplan_user_role_assignment = "テスト計画レベルã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å½¹å‰²ã‚’割り当ã¦"; +$TLS_desc_monitor_requirement = 'è¦ä»¶ã®ãƒ¢ãƒ‹ã‚¿ãƒ¼'; $TLS_error_duplicate_rolename = "ãã®åç§°ã®å½¹å‰²ã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™!"; @@ -2754,7 +2821,8 @@ $TLS_right_testplan_show_testcases_newest_versions = 'ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®æœ€ $TLS_right_testcase_freeze = 'テストケースãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®å‡çµ/å‡çµè§£é™¤'; - +$TLS_right_testproject_add_remove_keywords_executed_tcversions = + '実行済ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‹ã‚‰ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’追加/削除'; @@ -2794,6 +2862,9 @@ $TLS_create_new_version = "%sã®æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’作æˆã—ã¾ã—ãŸ"; $TLS_testcase_name_already_exists = "åŒã˜ã‚¿ã‚¤ãƒˆãƒ«ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒæ—¢ã«å­˜åœ¨ã—ã¦ã„ã¾ã™!"; $TLS_created_with_title = "以下ã®ã‚¿ã‚¤ãƒˆãƒ«ã§ä½œæˆã—ã¾ã—㟠(%s)"; $TLS_the_format_tc_xml_import = ""; +$TLS_relation_added = "%s ã¸ã®é–¢ä¿‚ãŒè¿½åŠ ã•れã¾ã—ãŸ"; +$TLS_related_tcase_not_open = "%s ã¸ã®é–¢ä¿‚ã¯è¿½åŠ ã§ãã¾ã›ã‚“ : ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯å‡çµã•れã¦ã„ã¾ã™"; +$TLS_relation_already_exists = "%s ã¸ã®é–¢ä¿‚ã¯è¿½åŠ ã§ãã¾ã›ã‚“ : ã“ã®é–¢ä¿‚ã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™"; // ----- lib/functions/testsuite.class.php ----- @@ -3021,6 +3092,8 @@ $TLS_opt_req_cf = "è¦ä»¶ã‚«ã‚¹ã‚¿ãƒ ãƒ•ィールド"; $TLS_opt_req_relations = "è¦ä»¶é–¢é€£"; $TLS_opt_req_linked_tcs = "è¦ä»¶é–¢é€£ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹"; $TLS_opt_req_coverage = "è¦ä»¶ç¶²ç¾…率"; +$TLS_opt_cfexec_comb = "å®Ÿè¡Œçµæžœã‚’カスタムフィールドã§åˆ†é›¢ã™ã‚‹"; + $TLS_title_tc_print_navigator = "ナビゲータ - テスト仕様ã®å°åˆ·"; $TLS_title_req_print_navigator = "ナビゲータ - è¦ä»¶å®šç¾©ã®å°åˆ·"; $TLS_title_tp_print_navigator = "ナビゲータ - テスト計画ã®å°åˆ·"; @@ -3099,6 +3172,8 @@ $TLS_bulk_Assignment_done = '%s ã®å‰²ã‚Šå½“ã¦ãŒå®Œäº†'; // å…ƒ TLS_bulk_assi $TLS_req_title_bulk_assign = '複数ã®è¦ä»¶ã‚’割り当ã¦'; $TLS_req_title_available = '利用å¯èƒ½ãªè¦ä»¶'; $TLS_no_req_spec_available = "ã“ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã«ã¯è¦ä»¶å®šç¾©ãŒå­˜åœ¨ã—ã¦ã„ã¾ã›ã‚“"; +$TLS_req_on_req_spec = 'å­è¦ä»¶ã€‚ ( %s ) è¦ä»¶ä»•様: %s'; + // ----- requirement relations (BUGID 1748) @@ -3127,7 +3202,8 @@ $TLS_error_deleting_rel = "関連を削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"; $TLS_delete_rel_msgbox_title = "関連ã®å‰Šé™¤"; $TLS_delete_rel_msgbox_msg = "関連 #%i を削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹?"; $TLS_img_title_delete_relation = "クリックã™ã‚‹ã¨ã“ã®é–¢é€£ã‚’削除ã—ã¾ã™ã€‚"; - +$TLS_img_title_relation_frozen = "リンクã•れãŸè¦ä»¶ã®ï¼‘ã¤ãŒå‡çµã•れã¦ã„ã‚‹ãŸã‚ã€é–¢ä¿‚ã¯å‰Šé™¤ã§ãã¾ã›ã‚“。"; +$TLS_rel_add_error_dest_frozen = "宛先ã®è¦ä»¶ãŒå‡çµã•れã¦ã„ã‚‹ãŸã‚ã€é–¢ä¿‚ã¯è¿½åŠ ã§ãã¾ã›ã‚“。"; // ----- configured requirement relation types (BUGID 1748) ----- $TLS_req_rel_is_parent_of = "ã“ã®ç•ªå·ã®è¦ª"; @@ -3219,6 +3295,7 @@ $TLS_link_report_free_testcases_on_testproject = "ã©ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«ã‚‚割 $TLS_no_linked_req_cf='カスタムフィールド情報ã®è¦ä»¶ã¯ã‚りã¾ã›ã‚“'; + $TLS_format_html = 'HTML'; $TLS_format_odt = 'OpenOffice Writer'; $TLS_format_ods = 'OpenOffice Calc'; @@ -3287,6 +3364,7 @@ $TLS_req_spec_ko_on_tcimport = "è¦ä»¶å®šç¾©=%s ã¯å­˜åœ¨ã—ã¾ã›ã‚“。" . $TLS_req_not_in_DB_on_tcimport = "è¦ä»¶ 文書ID=%s ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«ãƒªãƒ³ã‚¯ã§ãã¾ã›ã‚“。" . "存在ã—ã¾ã›ã‚“。"; +$TLS_skip_testcase_import = 'テストケースをå–り込ã¿ã¾ã›ã‚“'; $TLS_update_last_testcase_version = '最新ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ‡ãƒ¼ã‚¿ã‚’æ›´æ–°ã—ã¾ã™'; $TLS_create_new_testcase_version = 'æ–°ãŸãªãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’作æˆã—ã¾ã™'; $TLS_generate_new_testcase = 'æ–°ãŸãªãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’ç•°ãªã‚‹åå‰ã§ä½œæˆã—ã¾ã™'; @@ -3303,6 +3381,8 @@ $TLS_same_name = "テストケースåãŒåŒã˜"; // 'has same name'; $TLS_duplicate_criteria = "テストケースã®é‡è¤‡ã¨ã¿ãªã™æ¡ä»¶"; // "Consider Test Case as duplicate if"; $TLS_hit_with_same_external_ID = "å–り込ã¿ã§ãã¾ã›ã‚“ - åŒã˜å¤–部 ID ã®æ—¢å­˜ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒã‚りã¾ã™:"; $TLS_attachment_skipped_during_import = "添付ファイル \"%s\" ã¯ã€æ—¢ã«ãƒ†ã‚¹ãƒˆè¦ç´ ã«ãƒªãƒ³ã‚¯ã•れã¦ã„ã‚‹ãŸã‚無視ã•れã¾ã—ãŸ"; +$TLS_keywords_assignment_skipped_during_import = "ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¸ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã®å‰²ã‚Šå½“ã¦ãŒè¨±å¯ã•れã¦ã„ãªã„ãŸã‚ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã®å‰²ã‚Šå½“ã¦ã¯ç„¡è¦–ã•れã¾ã—ãŸ"; +$TLS_req_assignment_skipped_during_import = "ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¸ã®è¦ä»¶ã®å‰²ã‚Šå½“ã¦ãŒè¨±å¯ã•れã¦ã„ãªã„ãŸã‚ã€è¦ä»¶ã®ãƒªãƒ³ã‚¯ã¯ç„¡è¦–ã•れã¾ã—ãŸ"; // ----- lib/testcases/tcexport.php ----- $TLS_no_testcases_to_export = "書ã出ã™ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒã‚りã¾ã›ã‚“。"; @@ -3551,14 +3631,12 @@ $TLS_API_NO_TESTSUITENAME = "テストスイートåãŒè¨­å®šã•れã¦ã„ã¾ã› $TLS_API_TESTSUITENAME_NOT_STRING = "テストスイートåã¯æ–‡å­—列ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™!"; $TLS_API_TESTSUITE_DONOTBELONGTO_TESTPROJECT = "(親ã¨ã—ã¦é¸æŠžã•れãŸ)id:%s ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクト(name=%s / id=%s)é…下ã«ã‚りã¾ã›ã‚“。"; - $TLS_API_CUSTOMFIELD_HAS_NOT_DESIGN_SCOPE="カスタムフィールド (name:%s) ã¯è¨­è¨ˆæ™‚ã«ä½¿ç”¨ã§ãるよã†ã«è¨­å®šã•れã¦ã„ã¾ã›ã‚“。"; $TLS_API_CUSTOMFIELD_NOT_ASSIGNED_TO_TESTPROJECT="カスタムフィールド (name:%s) ã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクト (name=%s / id=%s) ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ã¾ã›ã‚“。"; $TLS_API_TESTPROJECTNAME_DOESNOT_EXIST="テストプロジェクト (name:%s) ã¯å­˜åœ¨ã—ã¾ã›ã‚“。"; $TLS_API_TESTPLANNAME_DOESNOT_EXIST="テスト計画 (name:%s) ã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクト (name:%s) ã«å­˜åœ¨ã—ã¾ã›ã‚“。"; $TLS_API_INVALID_PARENT_TESTSUITEID = "テストスイートID (%s)ã¯ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆ (name:%s) ã® PARENT (親テストスイート) パラメータã¨ã—ã¦æŒ‡å®šã•れã¾ã—ãŸãŒã€ã“ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã¯ã‚·ã‚¹ãƒ†ãƒ å†…ã«å­˜åœ¨ã—ã¾ã›ã‚“!"; - $TLS_API_NO_TESTCASE_FOUND = "検索æ¡ä»¶ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"; $TLS_API_TESTPLANNAME_ALREADY_EXISTS="テスト計画 (name:%s) ã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクト (name:%s) ã«æ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚"; @@ -3653,6 +3731,8 @@ $TLS_audit_cfield_deactivated = "カスタムフィールド '{%1}' 㯠テス $TLS_audit_cfield_required_on = "カスタムフィールド '{%1}' ã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクト '{%2}' ã§å¿…é ˆã«è¨­å®šã•れã¾ã—ãŸ"; $TLS_audit_cfield_required_off = "カスタムフィールド '{%1}' ã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクト '{%2}' ã§ä»»æ„ã«è¨­å®šã•れã¾ã—ãŸ"; +$TLS_audit_tcversion_activated = "テストケース '{%1}' ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {%2} ã¯æœ‰åйã«ãªã‚Šã¾ã—ãŸ"; +$TLS_audit_tcversion_deactivated = "テストケース '{%1}' ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {%2} ã¯ç„¡åйã«ãªã‚Šã¾ã—ãŸ"; $TLS_audit_user_apikey_set = "API-Key '{%1}' ã¯ç”Ÿæˆã•れã¾ã—ãŸ"; $TLS_audit_user_saved = "ユーザー '{%1}' ã¯ä¿å­˜ã•れã¾ã—ãŸ"; @@ -3686,6 +3766,9 @@ $TLS_audit_all_keyword_assignments_removed_tc = "å…¨ã¦ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã¯ãƒ† $TLS_audit_exec_saved = "テストケース '{%1}' ãŒãƒ“ルド '{%2}' / テスト計画 '{%3}' ã§å®Ÿæ–½ã•れã¾ã—ãŸã€‚"; $TLS_audit_exec_deleted = "テストケース '{%1}' / ビルド '{%2}' / テスト計画 '{%3}' ã®å®Ÿæ–½ãŒå‰Šé™¤ã•れã¾ã—ãŸã€‚"; +$TLS_audit_testcasescript_added = "テストスクリプトã®ãƒªãƒ³ã‚¯ '{%1}' ãŒè¿½åŠ ã•れã¾ã—ãŸ"; +$TLS_audit_testcasescript_deleted = "テストスクリプトã®ãƒªãƒ³ã‚¯ '{%1}' ãŒå‰Šé™¤ã•れã¾ã—㟠" . + "from Test Case: {%2} from Test Project: {%3}"; $TLS_audit_executionbug_added = "ä¸å…·åˆ '{%1}' ã¯å®Ÿè¡Œä¸­ã«è¿½åŠ ã•れã¾ã—ãŸ"; $TLS_audit_executionbug_deleted = "ä¸å…·åˆ '{%1}' ãŒä»¥ä¸‹ã®å®Ÿæ–½ã‹ã‚‰å‰Šé™¤ã•れã¾ã—ãŸã€‚ " . "id: {%2} / テストケース: {%3} " . @@ -3709,7 +3792,16 @@ $TLS_audit_requirement_created = "è¦ä»¶ '{%1}' ã¯ä½œæˆã•れã¾ã—ãŸ"; $TLS_audit_requirement_saved = "è¦ä»¶ '{%1}' ã¯ä¿å­˜ã•れã¾ã—ãŸ"; $TLS_audit_requirement_deleted = "è¦ä»¶ '{%1}' ã¯å‰Šé™¤ã•れã¾ã—ãŸ"; $TLS_audit_req_assigned_tc = "タイトル '{%1}' ã®è¦ä»¶ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ '{%2}' ã«å‰²ã‚Šå½“ã¦ã•れã¾ã—ãŸ"; + $TLS_audit_req_assignment_removed_tc = "タイトル '{%1}' ã®è¦ä»¶ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ '{%2}' ã‹ã‚‰å‰²ã‚Šå½“ã¦ã‚’解除ã•れã¾ã—ãŸ"; + + +$TLS_audit_reqv_assigned_tcv = "è¦ä»¶ タイトル '{%1}'' (ãƒãƒ¼ã‚¸ãƒ§ãƒ³: {%2}) 㯠テストケース '{%3}' (ãƒãƒ¼ã‚¸ãƒ§ãƒ³:{%4}) ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¾ã—㟠"; + +$TLS_audit_reqv_assignment_removed_tcv = "è¦ä»¶ タイトル '{%1}'' (Version: {%2}) 㯠テストケース '{%3}' (ãƒãƒ¼ã‚¸ãƒ§ãƒ³:{%4}) ã‹ã‚‰å‰²ã‚Šå½“ã¦ã‚’解除ã•れã¾ã—㟠"; + + + $TLS_audit_requirement_copy = "è¦ä»¶ '文書ID:{%1}' ãŒè¦ä»¶ '文書ID:{%2}' ã®ã‚³ãƒ”ーã¨ã—ã¦ä½œæˆã•れã¾ã—ãŸ"; $TLS_audit_req_version_deleted = " ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {%1} ã®è¦ä»¶ '文書ID:{%2}' - {%3} ã¯å‰Šé™¤ã•れã¾ã—ãŸã€‚"; $TLS_audit_req_version_frozen = " ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {%1} ã®è¦ä»¶ '文書ID:{%2}' - {%3} ã¯å‡çµã•れã¾ã—ãŸã€‚"; @@ -3722,6 +3814,9 @@ $TLS_audit_testcase_deleted = "テストケース '{%1}' ãŒå‰Šé™¤ã•れã¾ã— $TLS_audit_testcase_version_deleted = "ãƒãƒ¼ã‚¸ãƒ§ãƒ³ {%1} ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ '{%2}' ãŒå‰Šé™¤ã•れã¾ã—ãŸã€‚"; $TLS_audit_build_created = $TLS_testproject . " '{%1}' - " . $TLS_testplan . " '{%2}' - ビルド '{%3}' ã¯ä½œæˆã•れã¾ã—ãŸ";$TLS_audit_build_saved = $TLS_testproject . " '{%1}' - " . $TLS_testplan . " '{%2}' - ビルド '{%3}' ã¯ä¿å­˜ã•れã¾ã—ãŸ";$TLS_audit_build_deleted = $TLS_testproject . " '{%1}' - " . $TLS_testplan . " '{%2}' - ビルド '{%3}' ã¯å‰Šé™¤ã•れã¾ã—ãŸ"; +$TLS_audit_build_saved = $TLS_testproject . " '{%1}' - " . $TLS_testplan . " '{%2}' - ビルト '{%3}' ã¯ä¿å­˜ã•れã¾ã—ãŸ"; +$TLS_audit_build_deleted = $TLS_testproject . " '{%1}' - " . $TLS_testplan . " '{%2}' - ビルド '{%3}' ã¯å‰Šé™¤ã•れã¾ã—ãŸ"; + $TLS_audit_testplan_deleted = $TLS_testproject . " '{%1}' - テスト計画 '{%2}' ã¯å‰Šé™¤ã•れã¾ã—ãŸ"; $TLS_audit_testplan_saved = $TLS_testproject . " '{%1}' - テスト計画 '{%2}' ã¯ä¿å­˜ã•れã¾ã—ãŸ"; $TLS_audit_testplan_created = $TLS_testproject . " '{%1}' - テスト計画 '{%2}' ã¯ä½œæˆã•れã¾ã—ãŸ"; @@ -3907,7 +4002,9 @@ $TLS_warning_delete_step = "

    ã‚¹ãƒ†ãƒƒãƒ—ç•ªå· %s を削除ã—ã¾ã™

    $TLS_warning_step_number = " ステップ番å·ã¯æ•°å­—ã§ > 0 ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™"; $TLS_warning_step_number_already_exists = " ã‚¹ãƒ†ãƒƒãƒ—ç•ªå· %s ã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™"; -$TLS_BTS_integration_failure = " ä¸å…·åˆè¿½è·¡ã‚·ã‚¹ãƒ†ãƒ : %s - ã¨ã®çµ±åˆã«å¤±æ•—ã—ã¾ã—ãŸ"; +$TLS_CTS_integration_failure = " コード追跡システム: %s - ã¨ã®é€£æºã«å¤±æ•—ã—ã¾ã—ãŸ"; +$TLS_BTS_integration_failure = " ä¸å…·åˆè¿½è·¡ã‚·ã‚¹ãƒ†ãƒ : %s - ã¨ã®é€£æºã«å¤±æ•—ã—ã¾ã—ãŸ"; +$TLS_CTS_connect_to_database_fails = "コード追跡データベースã¸ã®æŽ¥ç¶šã«å¤±æ•—ã—ã¾ã—ãŸ: %s"; $TLS_BTS_connect_to_database_fails = "ä¸å…·åˆè¿½è·¡ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¸ã®æŽ¥ç¶šã«å¤±æ•—ã—ã¾ã—ãŸ: %s"; $TLS_MANTIS_status_not_configured = 'Mantis DB ã‹ã‚‰ä¸å…·åˆã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹: %s ã®å–å¾—ã—ã¾ã—ãŸãŒã€int_mantis.php ã§ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒè¨­å®šã•れã¦ã„ã¾ã›ã‚“。'; $TLS_testcase_set_copied = "%s ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒã‚³ãƒ”ーã•れã¾ã—ãŸ"; @@ -3962,12 +4059,10 @@ $TLS_no_platforms_on_tproject = "入力ファイルã¯ãƒ—ラットフォーム $TLS_platform_not_on_tproject = "入力ファイルã¯ãƒ—ラットフォーム %s をテスト計画ã«ãƒªãƒ³ã‚¯ã™ã‚‹ã“ã¨ã‚’è¦æ±‚ã—ã¦ã„ã¾ã™ãŒã€å¯¾è±¡ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã«å­˜åœ¨ã—ã¾ã›ã‚“。"; $TLS_platform_linked = "プラットフォーム %s ã¯ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«ãƒªãƒ³ã‚¯ã•れã¾ã—ãŸã€‚"; $TLS_tcase_link_updated = "テストケース (%s) - ãƒãƒ¼ã‚¸ãƒ§ãƒ³ (%s) ãŒæ—¢ã«ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«ãƒªãƒ³ã‚¯ã•れã¦ã„ã¾ã™ã€‚実施順ã®ã¿æ›´æ–°ã•れã¾ã—ãŸã€‚"; - $TLS_cant_link_to_tplan_feedback = "外部 id %s ãƒãƒ¼ã‚¸ãƒ§ãƒ³ %s ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«ãƒªãƒ³ã‚¯ã§ãã¾ã›ã‚“ "; $TLS_tcversion_status_forbidden = "ステータス %s ã¯è¨±å¯ã•れã¦ã„ã¾ã›ã‚“。"; - // ----- lib/execute/execExport.php ----- $TLS_export_execution_set = 'å®Ÿæ–½ã‚»ãƒƒãƒˆã®æ›¸ã出ã—'; @@ -4032,6 +4127,26 @@ $TLS_used_on_testproject = '下記ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã§ä½¿ç”¨ä¸­'; / $TLS_issuetracker_invalid_type = "課題追跡タイプ %s ã¯æœªå®šç¾©ã§ã™"; // 'Issue Tracker type %s is unknown'; $TLS_issuetracker_interface_not_implemented = '課題追跡インターフェース %s ãŒå®Ÿè£…ã•れã¦ã„ãªã„/利用ã§ããªã„'; +// codeTrackerView.tpl +$TLS_th_codetracker = 'コード追跡'; +$TLS_th_codetracker_type = 'タイプ'; +$TLS_th_codetracker_env = '環境'; +$TLS_warning_empty_codetracker_name = "コード追跡åã¯ç©ºã«ã§ãã¾ã›ã‚“!"; +$TLS_warning_empty_codetracker_type = "コード追跡タイプã¯ç©ºã«ã§ãã¾ã›ã‚“!"; + +// codeTrackerCommands.class.php +$TLS_codetracker_management = $TLS_href_codetracker_management; +$TLS_codetracker_deleted = "コード追跡 %s% ã¯å‰Šé™¤ã•れã¾ã—ãŸ"; + +// codeTrackerEdit.tpl +$TLS_codetracker_show_cfg_example = '設定例を表示'; +$TLS_codetracker_cfg_example = '設定例'; +$TLS_used_on_testproject = 'テストプロジェクトã§ä½¿ç”¨æ¸ˆ'; + +// getcodetrackertemplate.php +$TLS_codetracker_invalid_type = 'コード追跡タイプ %s ã¯æœªå®šç¾©ã§ã™'; +$TLS_codetracker_interface_not_implemented = 'コード追跡インターフェース %s ã¯å®Ÿè£…ã•れã¦ã„ãªã„ã‹åˆ©ç”¨ã§ãã¾ã›ã‚“'; + // logger.class.php $TLS_mail_logger_email_subject = ' :: TestLink ã‹ã‚‰ã®ãƒ­ã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ :: '; @@ -4052,6 +4167,17 @@ $TLS_issue_platform = " プラットフォーム: %s"; // " Platform: %s"; $TLS_issue_exec_result = " 実施ステータス: %s"; // " Execution Status: %s"; $TLS_issue_generated_description = "実施ID: %s \n テスト担当者: %s \n テスト計画: %s \n"; // "Execution ID: %s \n Tester: %s \n Test Plan: %s \n"; +$TLS_issue_subject_projectname = "テストプロジェクト"; +$TLS_issue_subject_planname = "テスト計画:"; +$TLS_issue_subject_buildname = "ビルド:"; +$TLS_issue_subject_platfname = "プラットフォーム:"; +$TLS_issue_subject_tcname = "テストケース:"; +$TLS_issue_subject_tcpathname = $TLS_issue_subject_tcname; +$TLS_issue_subject_execon = "実行 (ISO タイムスタンプ): "; + + + + $TLS_jira_bug_created = "JIRA ã«èª²é¡Œ (åç§°:%s) を作æˆã—ã¾ã—ãŸã€‚キー:%s"; $TLS_redmine_bug_created = "REDMINE ã«èª²é¡Œ (åç§°:%s) ã‚’ã€identifier:%s ã§ä½œæˆã—ã¾ã—ãŸã€‚"; $TLS_fogbugz_bug_created = "FOGBUGZ ã«èª²é¡Œ (åç§°:%s) を作æˆã—ã¾ã—ãŸ:%s"; @@ -4155,10 +4281,17 @@ $TLS_hint_you_need_to_be_logged = "リンクを使用ã™ã‚‹ã«ã¯ãƒ­ã‚°ã‚¤ãƒ³ã™ $TLS_security_check_ko = 'セキュリティãƒã‚§ãƒƒã‚¯ã«å¤±æ•—ã—ã¾ã—ãŸ'; $TLS_issue_on_step = "ステップ番å·å®Ÿæ–½æ™‚ã«å•題 "; -$TLS_add_link_to_tlexec = "ä¸å…·åˆå ±å‘Šã«ã€ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹å®Ÿè¡Œçµæžœã¸ã®ãƒªãƒ³ã‚¯ã‚’設ã‘ã‚‹"; +$TLS_add_link_to_tlexec = "ä¸å…·åˆãƒ»èª²é¡Œå ±å‘Šã«ã€ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹å®Ÿè¡Œçµæžœã¸ã®ãƒªãƒ³ã‚¯ã‚’設ã‘ã‚‹"; // "Add Link in Issue Tracker to Test Case Execution Feature"; +$TLS_add_link_to_tlexec_print_view = "ä¸å…·åˆãƒ»èª²é¡Œå ±å‘Šã«ã€ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹å®Ÿè¡Œçµæžœå°åˆ·è¡¨ç¤ºã¸ã®ãƒªãƒ³ã‚¯ã‚’設ã‘ã‚‹"; + $TLS_dl2tl = 'TestLinkå®Ÿè¡Œçµæžœã¸ '; // 'Link to TestLink Execution: '; // ":" ã®å¾Œã®åŠè§’スペースã¯å¿…è¦ã‚‰ã—ã„。無ã„ã¨ã€ãƒªãƒ³ã‚¯ãŒç”Ÿæˆã•れãªã„。":"自体ã¯ä¸è¦ã€‚ +$TLS_dl2tlpv = 'TestLink å®Ÿè¡Œçµæžœå°åˆ·è¡¨ç¤ºã¸: '; + +$TLS_dlToTCSpecPV = 'TestLink テストケースã¸: '; +$TLS_dlToTCSpecPVCode = '[TLTC@:%s]'; + $TLS_old_style_login = '従æ¥ã®ãƒ­ã‚°ã‚¤ãƒ³ç”»é¢'; $TLS_new_style_login = 'æ–°ã—ã„ログイン画é¢ã‚’ã”利用下ã•ã„'; @@ -4191,6 +4324,8 @@ $TLS_btn_stop_mon = "ãƒ¢ãƒ‹ã‚¿ãƒªãƒ³ã‚°åœæ­¢"; // 'Stop Monitoring'; $TLS_issueTracker_connection_ok = "接続テストOK"; // 'Test Connection OK'; $TLS_issueTracker_connection_ko = "接続テストNG!!"; // 'Test Connection KO'; +$TLS_codeTracker_connection_ok = '接続テストOK'; +$TLS_codeTracker_connection_ko = '接続テストNG'; $TLS_click_passed = "æˆåŠŸ"; // "Click to set to passed"; $TLS_click_failed = "失敗"; // "Click to set to failed"; @@ -4219,5 +4354,147 @@ $TLS_multiple_entities_search = 'è¤‡æ•°ã‚¨ãƒ³ãƒ†ã‚£ãƒ†ã‚£ã§æ¤œç´¢'; $TLS_no_access_to_feature = 'ã“ã®æ©Ÿèƒ½ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“'; $TLS_applyExecTypeChangeToAllSteps = "ã™ã¹ã¦ã®ã‚¹ãƒ†ãƒƒãƒ—ã«é©ç”¨"; -// ----- END ------------------------------------------------------------------ -?> + +$TLS_user_bulk_action = '一括æ“作ã®å¯¾è±¡ãƒ¦ãƒ¼ã‚¶ãƒ¼'; +$TLS_title_issuetracker_mgmt = '課題追跡'; +$TLS_title_codetracker_mgmt = 'コード追跡'; +$TLS_desc_cfield_assignment = "カスタムフィールド割り当ã¦"; +$TLS_desc_exec_assign_testcases = "テストケース実行ã®å‰²ã‚Šå½“ã¦"; +$TLS_expiration_date = 'æœŸé™æ—¥'; +$TLS_system_descr = "テスト & è¦ä»¶ç®¡ç†ã‚½ãƒ•トウェア"; +$TLS_poweredBy = 'Powered by'; +$TLS_expiration = '期é™åˆ‡ã‚Œ'; +$TLS_no_user_selected = 'å°‘ãªãã¨ã‚‚ï¼‘äººãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’é¸æŠžã—ã¦ãã ã•ã„'; + +$TLS_can_not_delete_relation_tcversion_frozen = "削除ã§ãã¾ã›ã‚“: テストケースã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯å‡çµã•れã¦ã„ã¾ã™"; +$TLS_can_not_delete_relation_related_tcversion_frozen = "削除ã§ãã¾ã›ã‚“: 関連ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯å‡çµã•れã¦ã„ã¾ã™"; +$TLS_obsolete = '廃止'; + +$TLS_tcversion_executed_keyword_assignment_blocked = ' テストケースã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒå®Ÿè¡Œã•れã¾ã—ãŸã€‚システム設定ã§ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã®ç®¡ç†ãŒè¨±å¯ã•れã¦ã„ã¾ã›ã‚“'; + +$TLS_select_keywords = 'ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’é¸æŠž'; + +$TLS_issueReporter = '報告者'; +$TLS_issueHandler = '割り当ã¦å…ˆ / 処ç†è€…'; + +$TLS_API_MISSING_REQUIRED_PROP = 'å¿…è¦ãªå…¥åŠ›/引数ãŒã‚りã¾ã›ã‚“:'; +$TLS_API_TESTPLAN_ID_DOES_NOT_EXIST = 'ID: %s ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ãŒå­˜åœ¨ã—ã¾ã›ã‚“'; +$TLS_API_TESTPLAN_APIKEY_DOES_NOT_EXIST = 'API KEY: %s ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ãŒå­˜åœ¨ã—ã¾ã›ã‚“'; + +$TLS_tcvqty_with_kw = 'ã“ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’使用ã—ã¦ã„るテストケースãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®æ•°'; +$TLS_can_not_delete_a_frozen_relation = 'å‡çµã•れãŸé–¢ä¿‚ã¯å‰Šé™¤ã§ãã¾ã›ã‚“'; + +$TLS_cannot_link_latest_version_reason_has_been_exec = '最新ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ %s ã¸ã®ãƒªãƒ³ã‚¯ã¯è¿½åŠ ã§ãã¾ã›ã‚“ - ç†ç”±: 実行済ã§ã™'; + +$TLS_reqLinkingDisabledAfterExec = 'システム設定ã§ã€å®Ÿè¡Œæ¸ˆãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®è¦ä»¶ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãƒªãƒ³ã‚¯ç®¡ç†ã‚’ブロックã—ã¾ã™'; +$TLS_yourRoleHasReqLinkingDisabled = 'ã‚ãªãŸã®å½¹å‰²ã«ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¸ã®è¦ä»¶ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãƒªãƒ³ã‚¯æ¨©é™ãŒã‚りã¾ã›ã‚“'; + +$TLS_restAPIExecParameters = "REST API ãŒå®Ÿè¡Œã®å¼•数を作æˆ"; + +$TLS_can_not_delete_relation_because_this_is_not_the_latest = +"削除ã§ãã¾ã›ã‚“: テストケースã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒæœ€æ–°ã§ã¯ã‚りã¾ã›ã‚“"; + +$TLS_updateLinkToThisTCVersion = 'リンクã•れ㟠TCV ã‚’ã“ã‚Œã«æ›´æ–°'; +$TLS_hasNewestVersionMsg = '注æ„: ã“れã¯åˆ©ç”¨å¯èƒ½ãªæœ€æ–°ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã¯ã‚りã¾ã›ã‚“'; + +$TLS_saveStepsForPartialExec = '実行途中ã®ã‚¹ãƒ†ãƒƒãƒ—ã®ä¿å­˜'; +$TLS_partialExecNothingToSave = '実行途中ã®ã‚¹ãƒ†ãƒƒãƒ—ã®ä¿å­˜: ä¿å­˜ã™ã‚‹å‚™è€ƒã‚„çµæžœã¯ã‚りã¾ã›ã‚“'; + +$TLS_partialExecNoAttachmentsWarning = '注æ„: 実行途中ã®ã‚¹ãƒ†ãƒƒãƒ—ã®ä¿å­˜æ™‚ã¯ã€æ·»ä»˜ãƒ•ァイルã¯ä¿å­˜ã•れã¾ã›ã‚“'; + +$TLS_testcase_has_no_name = 'テストケースã®åå‰ãŒç©ºã§ã™'; +$TLS_copy_attachments_from_latest_exec = '最新ã®å®Ÿè¡Œã®æ·»ä»˜ãƒ•ァイルをコピー'; + +$TLS_exec_tree_counters_logic = 'ツリーカウンター 最終実行'; +$TLS_use_latest_exec_on_testplan_for_counters = 'テスト計画'; +$TLS_use_latest_exec_on_contex_for_counters = 'é¸æŠžã•れãŸãƒ“ルド'; +$TLS_use_latest_exec_on_testplan_plat_for_counters = + 'プラットフォームã®ãƒ†ã‚¹ãƒˆè¨ˆç”»'; + + +$TLS_download_csv = 'CSV ã¨ã—ã¦ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰'; +$TLS_select_platforms = "ãƒ—ãƒ©ãƒƒãƒˆãƒ•ã‚©ãƒ¼ãƒ ã‚’é¸æŠž"; +$TLS_without_platforms = "プラットフォームãªã—"; +$TLS_platforms = "プラットフォーム"; +$TLS_tcv_qty = "リンクã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®æ•°"; +$TLS_createKW = 'キーワードを作æˆ'; + +$TLS_btn_create_and_link = 'ä½œæˆ & リンク'; +$TLS_create_keyword_and_link = 'キーワードを作æˆã—㦠TCV ã«ãƒªãƒ³ã‚¯'; +$TLS_export_skel = 'スケルトンを書ã出ã—'; + +$TLS_title_res_by_kw_on_plat = "プラットフォームã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰åˆ¥çµæžœ: "; +$TLS_title_res_by_prio_on_plat = "プラットフォームã®å„ªå…ˆåº¦åˆ¥çµæžœ: "; +$TLS_title_res_by_tl_testsuite_on_plat = + "プラットフォームã®ãƒˆãƒƒãƒ—ãƒ¬ãƒ™ãƒ«ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆåˆ¥çµæžœ: "; + +$TLS_title_res_by_tl_testsuite = "ãƒˆãƒƒãƒ—ãƒ¬ãƒ™ãƒ«ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆåˆ¥çµæžœ"; + +$TLS_title_res_by_build_on_plat = "プラットフォームã®ãƒ“ãƒ«ãƒ‰åˆ¥çµæžœ:"; +$TLS_title_res_build = 'ãƒ“ãƒ«ãƒ‰åˆ¥çµæžœ'; +$TLS_info_neverRunByPP_report = + "ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã€é¸æŠžã•れãŸãƒ†ã‚¹ãƒˆè¨ˆç”» (ã¨ãƒ—ラットフォーム) ã«å­˜åœ¨ã™ã‚‹ãれãžã‚Œã®æœ‰åйãªãƒ“ルドã§ã€å®Ÿè¡Œã•れã¦ã„ãªã„テストケースを表示ã—ã¾ã™ã€‚
    + テスト者ã®å‰²ã‚Šå½“ã¦ãŒã‚ã‚‹ / ãªã„ テストケース + ãŒè€ƒæ…®ã•れã¾ã™
    "; + +$TLS_neverRunByPP_title = "一度も実行ã•れã¦ã„ãªã„テストケース"; +$TLS_link_report_never_run = "一度も実行ã•れã¦ã„ãªã„テストケース"; + + +$TLS_commit_id = 'コミット id'; +$TLS_branch = 'ブランãƒ'; +$TLS_tag = 'ã‚¿ã‚°'; +$TLS_release_candidate = 'リリース候補'; + +$TLS_allowed_files = "許å¯ã•れãŸãƒ•ァイル:"; +$TLS_allowed_filenames_regexp = "ファイルåã¯ã“ã®æ­£è¦è¡¨ç¾ã§æ¤œè¨¼ã•れã¾ã™:"; + +$TLS_info_xls_report_results_by_status = '!!スプレッドシートã®å‡ºåŠ›ã«ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®æ¦‚è¦ãŒå«ã¾ã‚Œã¾ã™'; + + +$TLS_link_report_test_absolute_latest_exec = "çµ¶å¯¾çš„ãªæœ€æ–°ã®å®Ÿè¡ŒæŒ‡æ¨™"; +$TLS_latest_execution = "ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã®æœ€æ–°å®Ÿè¡Œ"; +$TLS_resultsTCAbsoluteLatest = "最新ã®å®Ÿè¡Œã¯ã™ã¹ã¦ã®ãƒ“ルドã«ã‚ãŸã£ã¦è¨ˆç®—ã•れã¾ã™"; +$TLS_info_resultsTCAbsoluteLatest_report = $TLS_resultsTCAbsoluteLatest; + +$TLS_resultsTCAbsoluteLatest_title = "é¸æŠžã—ãŸãƒ—ラットフォームã§ã€ã™ã¹ã¦ã®ãƒ“ルドã«ã‚ãŸã£ã¦è¨ˆç®—ã•ã‚ŒãŸæœ€æ–°ã®å®Ÿè¡Œ"; + +$TLS_FILE_UPLOAD_allowed_filenames_regexp = 'ファイルåãŒæ­£è¦è¡¨ç¾ã®ãƒã‚§ãƒƒã‚¯ã‚’パスã—ã¦ã„ã¾ã›ã‚“'; + +$TLS_FILE_UPLOAD_empty_extension = 'ç©ºã®æ‹¡å¼µå­ã¯è¨±å¯ã•れã¦ã„ã¾ã›ã‚“'; +$TLS_FILE_UPLOAD_allowed_files = 'ファイルãŒãƒ•ァイル形å¼ã®ãƒã‚§ãƒƒã‚¯ã‚’パスã—ã¦ã„ã¾ã›ã‚“'; + +$TLS_file_upload_ko = 'ファイルアップロードã®å•題'; +$TLS_pleaseOpenTSuite = 'テストスイートã®è©³ç´°ã«ã€å®Ÿè¡Œä¸Šã®é‡è¦ãªæƒ…å ±ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ ' . + '
    セクションを開ã„ã¦ã‚ˆãã”覧ãã ã•ã„。 ' . + '
    ã‚りãŒã¨ã†ã”ã–ã„ã¾ã™!'; + +$TLS_delete_frozen_tcversion = "å‡çµã•れ㟠TCãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’削除"; +$TLS_right_delete_frozen_tcversion = "å‡çµã•れ㟠TCãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’削除"; +$TLS_copy_only_latest_version = '最新ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã¿ãƒãƒ¼ã‚¸ãƒ§ãƒ³ 1 ã¨ã—ã¦ã‚³ãƒ”ー'; +$TLS_qty = 'æ•°'; +$TLS_yyyy_mm = 'å¹´/月'; +$TLS_yyyy_mm_dd = '日付'; +$TLS_hh = '時間'; +$TLS_execTimelineStats_report = '日付/時間別ã®å®Ÿè¡Œçµ±è¨ˆæƒ…å ±'; +$TLS_link_report_exec_timeline = '日付/時間別ã®å®Ÿè¡Œ'; +$TLS_qty_of_executions = 'テストケースã®å®Ÿè¡Œ'; +$TLS_assignOnlyToTestCasesInTestSuite = 'ã“ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®ç›´æŽ¥ã®å­ã«ã®ã¿å‰²ã‚Šå½“ã¦'; +$TLS_target_kword = "対象キーワード (割り当ã¦/削除)"; +$TLS_link_report_by_tsuite = "レベル 1 & レベル 2 ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆåˆ¥ã®æŒ‡æ¨™"; +$TLS_metrics_by_l1l2_testsuite = "レベル 1 & レベル 2 ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆåˆ¥ã®æŒ‡æ¨™"; +$TLS_you_can_sort = "列見出ã—をクリックã—ã¦è¡¨ã®ä¸¦ã³æ›¿ãˆãŒã§ãã¾ã™ã€‚"; +$TLS_firstExec = 'åˆå›žå®Ÿè¡Œ: '; +$TLS_latestExec = '最終実行: '; +$TLS_section_link_report_by_tsuite_on_plat = "プラットフォームã®çµæžœ: "; +$TLS_l1l2 = 'トップ : å­ (テストスイート)'; +$TLS_not_enough_rights = '機能ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ãŸã‚ã«å¿…è¦ãªæ¨©é™ãŒã‚りã¾ã›ã‚“'; +$TLS_saveForBaseline = 'ベースラインを作æˆã™ã‚‹ãŸã‚ã«ä¿å­˜'; +$TLS_baseline_saved_ok = 'ベースラインをä¿å­˜ã—ã¾ã—ãŸ'; +$TLS_baseline_l1l2 = 'レベル 1 & レベル 2 テストスイートã®ãƒ™ãƒ¼ã‚¹ãƒ©ã‚¤ãƒ³'; +$TLS_baselineTimestamp = 'ベースラインå–å¾—:'; +$TLS_on_design = '設計'; +$TLS_on_exec = '実行'; +$TLS_note_platform_filter = 'ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯æ¬¡ã®ãƒ—ラットフォームã§ãƒ•ィルターã•れã¾ã™'; +$TLS_testers_qty = 'ãƒ†ã‚¹ãƒˆè€…ã®æ•°'; +// ----- END ------------------------------------------------------------------ \ No newline at end of file diff --git a/locale/ja_JP/texts.php b/locale/ja_JP/texts.php index a6bee60c1c..dc853baa21 100644 --- a/locale/ja_JP/texts.php +++ b/locale/ja_JP/texts.php @@ -1,64 +1,61 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - *------------------------------------------------------------------- - * Japanese translation - *------------------------------------------------------------------- - * Testing Engineer's Forum (TEF) in Japan - * Working Group of TestLink Japanese Translation Project - * - * http://blues.se.uec.ac.jp/swtest/ - * http://testlinkjp.org/ - * http://sourceforge.jp/projects/testlinkjp/ - * - * Adviser: - * Yasuharu NISHI - * - * Core member: - * Atsushi Nagata, AZMA Daisuke, Hiromi Nishiyama, - * Kaname Mochizuki, Kaoru Nakamura, Kunio Murakami, - * Lumina Nishihara, Marino Suda, Masahide Katsumata, - * Masami Ichikawa, Masataka Yoneta, Sadahiko Hantani, - * Shinichi Sugiyama, Shinsuke Matsuki, Shizuka Ban, - * Takahiro Wada, Toshinori Sawaguchi, Toshiyuki Kawanishi, - * Yasuhiko Okada, Yoichi Kunihiro, Yoshihiro Yoshimura, - * Yukiko Kajino - * - **/ - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Application error"; -$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . - "logs for details.

    You are welcome to report the problem. Please visit our " . - "website.

    "; - - - -$TLS_htmltext_title['assignReqs'] = "テストケースã«è¦ä»¶ã‚’割り当ã¦ã‚‹"; -$TLS_htmltext['assignReqs'] = "

    目的:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + *------------------------------------------------------------------- + * Japanese translation + *------------------------------------------------------------------- + * Testing Engineer's Forum (TEF) in Japan + * Working Group of TestLink Japanese Translation Project + * + * http://blues.se.uec.ac.jp/swtest/ + * http://testlinkjp.org/ + * http://sourceforge.jp/projects/testlinkjp/ + * + * Adviser: + * Yasuharu NISHI + * + * Core member: + * Atsushi Nagata, AZMA Daisuke, Hiromi Nishiyama, + * Kaname Mochizuki, Kaoru Nakamura, Kunio Murakami, + * Lumina Nishihara, Marino Suda, Masahide Katsumata, + * Masami Ichikawa, Masataka Yoneta, Sadahiko Hantani, + * Shinichi Sugiyama, Shinsuke Matsuki, Shizuka Ban, + * Takahiro Wada, Toshinori Sawaguchi, Toshiyuki Kawanishi, + * Yasuhiko Okada, Yoichi Kunihiro, Yoshihiro Yoshimura, + * Yukiko Kajino + * + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Application error"; +$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . + "logs for details.

    You are welcome to report the problem. Please visit our " . + "website.

    "; + +$TLS_htmltext_title['assignReqs'] = "テストケースã«è¦ä»¶ã‚’割り当ã¦ã‚‹"; +$TLS_htmltext['assignReqs'] = "

    目的:

    ã“ã®æ©Ÿèƒ½ã«ã‚ˆã‚Šã€è¦ä»¶ã¨ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’関連付ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ テスト設計者ã¯0..n対0..nã®é–¢é€£ä»˜ã‘を定義ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ ã™ãªã‚ã¡ã€è¦ä»¶ã«é–¢é€£ä»˜ã‘られãªã„テストケースãŒã‚ã£ãŸã‚Šã€è¤‡æ•°ã® @@ -80,46 +77,41 @@ ã“ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’é¸æŠžã—ã€ã€Œå‰²ã‚Šå½“ã¦ã€ãƒœã‚¿ãƒ³ã‚’クリックã™ã‚‹ã“ã¨ã«ã‚ˆã‚Šã€ テスト設計者ã¯è¦ä»¶ã«å°ã‚’ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ æ–°è¦ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯ä¸­å¤®ã®ãƒ–ロックã®ã€Œå‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸè¦ä»¶ã€ã«è¡¨ç¤ºã•れã¾ã™ã€‚ -"; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "テスト仕様"; -$TLS_htmltext['editTc'] = "

    目的:

    -

    テスト仕様 ã¯ã€å­˜åœ¨ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆãã—ã¦ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ ã®". - "情報を閲覧ã—ãŸã‚Šå¤‰æ›´ã—ãŸã‚Šã™ã‚‹å ´æ‰€ã§ã™ã€‚" . - "ç•°ãªã‚‹ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’見るã“ã¨ã‚‚ã§ãã¾ã™ã€‚

    + +

    Warning:

    +å‡çµã•れãŸè¦ä»¶ã¯å¤‰æ›´ã—ã¦ã€ç¶²ç¾…率を更新ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。ãã®ãŸã‚ã€å‡çµã•れãŸè¦ä»¶ã¯ä¸€è¦§ã«è¡¨ç¤ºã•れã¾ã™ãŒã€é–¢é€£ä»˜ã‘ã®ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã¯ç„¡åйã«ãªã‚Šã¾ã™ã€‚"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "テスト仕様"; +$TLS_htmltext['editTc'] = "

    目的:

    +

    テスト仕様 ã¯ã€å­˜åœ¨ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆãã—ã¦ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ ã®" . "情報を閲覧ã—ãŸã‚Šå¤‰æ›´ã—ãŸã‚Šã™ã‚‹å ´æ‰€ã§ã™ã€‚" . + "ç•°ãªã‚‹ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’見るã“ã¨ã‚‚ã§ãã¾ã™ã€‚

    ã‚„ã£ã¦ã¿ã¾ã—ょã†ï¼:

      -
    1. テストプロジェクトをナビゲーションã®ãƒ„リーã‹ã‚‰é¸æŠžã—ã¾ã™ã€‚メモ: " . - "テストプロジェクトã®å¤‰æ›´ã¯å³ä¸Šã®" . +
    2. テストプロジェクトをナビゲーションã®ãƒ„リーã‹ã‚‰é¸æŠžã—ã¾ã™ã€‚メモ: " . "テストプロジェクトã®å¤‰æ›´ã¯å³ä¸Šã®" . "ãƒ—ãƒ«ãƒ€ã‚¦ãƒ³ãƒªã‚¹ãƒˆã‚’é¸æŠžã™ã‚‹ã“ã¨ã§ãŠã“ãªã†ã“ã¨ãŒã§ãã¾ã™ã€‚
    3. -
    4. å­ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆä½œæˆãƒœã‚¿ãƒ³ã‚’クリックã—ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’作æˆã—ã¾ã™ã€‚" . - "ã“ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã«å¾“ã£ã¦ãƒ†ã‚¹ãƒˆãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆãŒæ§‹æˆã•れã¾ã™" . - "(例ãˆã°ã€æ©Ÿèƒ½/éžæ©Ÿèƒ½ãªã©ã®ç¨®åˆ¥ã€ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚„フィーãƒãƒ£ãƒ¼ã«ã‚ˆã‚‹åˆ†åˆ¥ãªã©)。" . - "テストスイートã«ã¯ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã€ç’°å¢ƒè¨­å®šã€é–¢é€£ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã¸ã®å‚ç…§ã€" . - "制é™äº‹é …ã‚„ã€ãれ以外ã«å¿…è¦ãªæƒ…報を記載ã—ã¾ã™ã€‚" . - "一般ã«ã€é…下ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«é–¢ã™ã‚‹æ§˜ã€…ãªå…±é€šã™ã‚‹èª¬æ˜Žã‚’記述ã™ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ã€‚" . - "
    5. テストスイートã¯ã€Œãƒ•ォルダーã€ã®ã‚ˆã†ãªæ¦‚念ã¨ã‚‚ã„ãˆã¾ã™ã€‚ 従ã£ã¦ã€ãƒ†ã‚¹ãƒˆãƒ—ロジェクト内ã§" . - "テストスイートã®ã‚³ãƒ”ーや移動ãŒã§ãã¾ã™ã€‚ã¾ãŸã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚‚å¯èƒ½ã§ã™ (テストケースをå«ã‚ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™)。
    6. -
    7. 作æˆã—ãŸãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’ナビゲーションã‹ã‚‰é¸æŠžã—ã€" . - "テストケースを作æˆãƒœã‚¿ãƒ³ã‚’クリックã™ã‚‹ã“ã¨ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’作æˆã—ã¾ã™ã€‚" . - "テストケースã«ã¯ã€ãƒ†ã‚¹ãƒˆã‚·ãƒŠãƒªã‚ªã€æœŸå¾…çµæžœã‚„テストプロジェクトã§å®šç¾©ã•れãŸã‚«ã‚¹ã‚¿ãƒ ãƒ•ィールド" . - "ãªã©ã‚’記載ã—ã¾ã™ (詳ã—ãã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã‚’å‚ç…§ã—ã¦ãã ã•ã„。" . - "ã¾ãŸã€ãƒˆãƒ¬ãƒ¼ã‚µãƒ“リティã®ãŸã‚ã«ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’割り当ã¦ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚
    8. +
    9. å­ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆä½œæˆãƒœã‚¿ãƒ³ã‚’クリックã—ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’作æˆã—ã¾ã™ã€‚" . + "ã“ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã«å¾“ã£ã¦ãƒ†ã‚¹ãƒˆãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆãŒæ§‹æˆã•れã¾ã™" . "(例ãˆã°ã€æ©Ÿèƒ½/éžæ©Ÿèƒ½ãªã©ã®ç¨®åˆ¥ã€ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚„フィーãƒãƒ£ãƒ¼ã«ã‚ˆã‚‹åˆ†åˆ¥ãªã©)。" . + "テストスイートã«ã¯ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã€ç’°å¢ƒè¨­å®šã€é–¢é€£ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã¸ã®å‚ç…§ã€" . "制é™äº‹é …ã‚„ã€ãれ以外ã«å¿…è¦ãªæƒ…報を記載ã—ã¾ã™ã€‚" . + "一般ã«ã€é…下ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«é–¢ã™ã‚‹æ§˜ã€…ãªå…±é€šã™ã‚‹èª¬æ˜Žã‚’記述ã™ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ã€‚" . + "
    10. テストスイートã¯ã€Œãƒ•ォルダーã€ã®ã‚ˆã†ãªæ¦‚念ã¨ã‚‚ã„ãˆã¾ã™ã€‚ 従ã£ã¦ã€ãƒ†ã‚¹ãƒˆãƒ—ロジェクト内ã§" . + "テストスイートã®ã‚³ãƒ”ーや移動ãŒã§ãã¾ã™ã€‚ã¾ãŸã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚‚å¯èƒ½ã§ã™ (テストケースをå«ã‚ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™)。
    11. +
    12. 作æˆã—ãŸãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’ナビゲーションã‹ã‚‰é¸æŠžã—ã€" . "テストケースを作æˆãƒœã‚¿ãƒ³ã‚’クリックã™ã‚‹ã“ã¨ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’作æˆã—ã¾ã™ã€‚" . + "テストケースã«ã¯ã€ãƒ†ã‚¹ãƒˆã‚·ãƒŠãƒªã‚ªã€æœŸå¾…çµæžœã‚„テストプロジェクトã§å®šç¾©ã•れãŸã‚«ã‚¹ã‚¿ãƒ ãƒ•ィールド" . + "ãªã©ã‚’記載ã—ã¾ã™ (詳ã—ãã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã‚’å‚ç…§ã—ã¦ãã ã•ã„。" . + "ã¾ãŸã€ãƒˆãƒ¬ãƒ¼ã‚µãƒ“リティã®ãŸã‚ã«ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’割り当ã¦ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚
    13. 左横ã«ã‚るツリーã§ãƒ‡ãƒ¼ã‚¿ç·¨é›†ã‚’指示ã—ã¾ã™ã€‚
    14. 作æˆã—ãŸãƒ†ã‚¹ãƒˆä»•æ§˜æ›¸ã‚’ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒæº–備完了ã—ãŸã¨ã㫠テスト計画ã«å‰²ã‚Šå½“ã¦ã¾ã™ã€‚
    -

    TestLinkã§ã¯ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã«ã‚ˆã£ã¦ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’æ•´ç†ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã§ã—ょã†ã€‚" . -"テストスイートã¯å…¥ã‚Œå­çжã«ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã®ã§ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®éšŽå±¤ã‚’作るã“ã¨ãŒã§ãã¾ã™ã€‚ -ã“ã®æƒ…å ±ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¨ã¨ã‚‚ã«å°åˆ·ã•れã¾ã™ã€‚

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "テストケース検索ページ"; -$TLS_htmltext['searchTc'] = "

    目的:

    +

    TestLinkã§ã¯ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã«ã‚ˆã£ã¦ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’æ•´ç†ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã§ã—ょã†ã€‚" . + "テストスイートã¯å…¥ã‚Œå­çжã«ã™ã‚‹ã“ã¨ãŒã§ãã‚‹ã®ã§ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®éšŽå±¤ã‚’作るã“ã¨ãŒã§ãã¾ã™ã€‚ +ã“ã®æƒ…å ±ã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¨ã¨ã‚‚ã«å°åˆ·ã•れã¾ã™ã€‚

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "テストケース検索ページ"; +$TLS_htmltext['searchTc'] = "

    目的:

    キーワードã«å¿œã˜ãŸæ¤œç´¢æ–‡å­—列を入力ã—ã¦ãã ã•ã„ã€‚å¤§æ–‡å­—å°æ–‡å­—ã¯åŒºåˆ¥ã—ã¾ã›ã‚“。 æ¤œç´¢çµæžœã¯ã€ç¾åœ¨ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ã¿ã‚’å«ã¿ã¾ã™ã€‚

    @@ -132,13 +124,13 @@
  • å¿…è¦ãªã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’é¸ã¶ã‹ã€ç©ºæ¬„('é©ç”¨ã—ãªã„'ã®æ„)ã‚’é¸ã‚“ã§ä¸‹ã•ã„。
  • 検索ボタンをクリックã—ã¦ä¸‹ã•ã„。
  • å…¨ã¦ã®æ¡ä»¶ã«ä¸€è‡´ã—ãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒè¡¨ç¤ºã•れã¾ã™ã€‚'タイトル'をクリックã™ã‚‹ã“ã¨ã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’編集ã§ãã¾ã™ã€‚
  • -"; - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "è¦ä»¶æ¤œç´¢"; -$TLS_htmltext['searchReq'] = "

    目的:

    +"; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "è¦ä»¶æ¤œç´¢"; +$TLS_htmltext['searchReq'] = "

    目的:

    キーワードã«å¿œã˜ãŸæ¤œç´¢æ–‡å­—列を入力ã—ã¦ãã ã•ã„ã€‚å¤§æ–‡å­—å°æ–‡å­—ã¯åŒºåˆ¥ã—ã¾ã›ã‚“。 æ¤œç´¢çµæžœã¯ã€ç¾åœ¨ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã®è¦ä»¶ã®ã¿ã‚’å«ã¿ã¾ã™ã€‚

    @@ -150,12 +142,18 @@
  • å¿…è¦ãªã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’é¸ã¶ã‹ã€ç©ºæ¬„('é©ç”¨ã—ãªã„'ã®æ„)ã‚’é¸ã‚“ã§ä¸‹ã•ã„。
  • 検索ボタンをクリックã—ã¦ä¸‹ã•ã„。
  • å…¨ã¦ã®æ¡ä»¶ã«ä¸€è‡´ã—ãŸè¦ä»¶ãŒè¡¨ç¤ºã•れã¾ã™ã€‚'タイトル'をクリックã™ã‚‹ã“ã¨ã§è¦ä»¶ã‚’編集ã§ãã¾ã™ã€‚
  • -"; + + +

    注:

    -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "è¦ä»¶ä»•様検索"; -$TLS_htmltext['searchReqSpec'] = "

    目的:

    +

    - ç¾åœ¨ã®ãƒ—ロジェクト内ã®è¦ä»¶ã®ã¿ãŒæ¤œç´¢ã•れã¾ã™ã€‚
    +- 検索ã¯å¤§æ–‡å­—å°æ–‡å­—を区別ã—ã¾ã›ã‚“。
    +- 空ã®ãƒ•ィールドã¯è€ƒæ…®ã•れã¾ã›ã‚“。

    "; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "è¦ä»¶ä»•様検索"; +$TLS_htmltext['searchReqSpec'] = "

    目的:

    キーワードã«å¿œã˜ãŸæ¤œç´¢æ–‡å­—列を入力ã—ã¦ãã ã•ã„ã€‚å¤§æ–‡å­—å°æ–‡å­—ã¯åŒºåˆ¥ã—ã¾ã›ã‚“。 æ¤œç´¢çµæžœã¯ã€ç¾åœ¨ã®ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã®è¦ä»¶ä»•様ã®ã¿ã‚’å«ã¿ã¾ã™ã€‚

    @@ -167,13 +165,18 @@
  • å¿…è¦ãªã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’é¸ã¶ã‹ã€ç©ºæ¬„('é©ç”¨ã—ãªã„'ã®æ„)ã‚’é¸ã‚“ã§ä¸‹ã•ã„。
  • 検索ボタンをクリックã—ã¦ä¸‹ã•ã„。
  • å…¨ã¦ã®æ¡ä»¶ã«ä¸€è‡´ã—ãŸè¦ä»¶ä»•様ãŒè¡¨ç¤ºã•れã¾ã™ã€‚'タイトル'をクリックã™ã‚‹ã“ã¨ã§è¦ä»¶ä»•様を編集ã§ãã¾ã™ã€‚
  • -"; -/* end contribution */ + +

    注:

    -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "テスト仕様ã®å°åˆ·"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    目的:

    +

    - ç¾åœ¨ã®ãƒ—ロジェクト内ã®è¦ä»¶ã®ã¿ãŒæ¤œç´¢ã•れã¾ã™ã€‚
    +- 検索ã¯å¤§æ–‡å­—å°æ–‡å­—を区別ã—ã¾ã›ã‚“。
    +- 空ã®ãƒ•ィールドã¯è€ƒæ…®ã•れã¾ã›ã‚“。

    "; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "テスト仕様ã®å°åˆ·"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    目的:

    ã“ã®æ©Ÿèƒ½ã¯å„々ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã€ã¾ãŸã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクト/テスト計画全体ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’å°åˆ·ã§ãã¾ã™ã€‚

    ã‚„ã£ã¦ã¿ã¾ã—ょã†ï¼:

      @@ -182,17 +185,16 @@

    1. ナビゲーションシートã®ã€Œå°åˆ·ã‚ªãƒ—ションã€ãƒ†ãƒ¼ãƒ–ルã«ã¦å°åˆ·ãƒ‡ãƒ¼ã‚¿ã®ç¯„囲ã¨ãƒ•ォーマットを -設定ã—ã¾ã™ã€‚HTMLã‚‚ã—ãã¯MS Wordå½¢å¼ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚詳ã—ãã¯ãƒ˜ãƒ«ãƒ—ã‚’å‚ç…§ã—ã¦ãã ã•ã„。

    2. ブラウザã®å°åˆ·æ©Ÿèƒ½ã«ã¦ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’å°åˆ·ã—ã¾ã™ã€‚
      - 注æ„: å°åˆ·ã§ãã‚‹ã®ã¯ãƒ–ラウザã®å³å´ã®ã¿ã§ã™ã€‚

    3. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "è¦ä»¶ä»•様ã®è¨­è¨ˆ"; -$TLS_htmltext['reqSpecMgmt'] = "

    è¦ä»¶ä»•様ドキュメントを管ç†ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    + 注: å°åˆ·ã§ãã‚‹ã®ã¯ãƒ–ラウザã®å³å´ã®ã¿ã§ã™ã€‚

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "è¦ä»¶ä»•様ã®è¨­è¨ˆ"; +$TLS_htmltext['reqSpecMgmt'] = "

    è¦ä»¶ä»•様ドキュメントを管ç†ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    è¦ä»¶ä»•様

    @@ -221,15 +223,14 @@ ã“れらã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯ã€TestLinkã®è¨­å®šã§å®šç¾©ã•れãŸåå‰ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã«æ ¼ç´ã•れã¾ã™ (デフォルトã§ã¯ \$tlCfg->req_cfg->default_testsuite_name = 'Test suite created by Requirement - Auto';)。 -タイトルã¨ã‚¹ã‚³ãƒ¼ãƒ—ã¯ä½œæˆã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«ã‚³ãƒ”ーã•れã¾ã™ã€‚

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "è¦ä»¶ä»•様ã®å°åˆ·"; //printReq +タイトルã¨ã‚¹ã‚³ãƒ¼ãƒ—ã¯ä½œæˆã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«ã‚³ãƒ”ーã•れã¾ã™ã€‚

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "è¦ä»¶ä»•様ã®å°åˆ·"; // printReq $TLS_htmltext['printReqSpec'] = "

    目的:

    ã“ã“ã‹ã‚‰ã€å˜ä¸€ã®è¦ä»¶ã€è¦ä»¶ä»•様内ã®ã™ã¹ã¦ã®è¦ä»¶ã€ ã¾ãŸã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクトã®ã™ã¹ã¦ã®è¦ä»¶ã‚’å°åˆ·ã§ãã¾ã™ã€‚

    -

    最åˆã«:

    +

    ã‚„ã£ã¦ã¿ã¾ã—ょã†ï¼:

    1. 表示ã™ã‚‹è¦ä»¶ã®éƒ¨åˆ†ã‚’é¸æŠžã—ã€è¦ä»¶ã€è¦ä»¶ä»•様ã€ã¾ãŸã¯ãƒ†ã‚¹ãƒˆãƒ—ロジェクトをクリックã—ã¾ã™ã€‚ @@ -243,12 +244,11 @@

    2. å®Ÿéš›ã«æƒ…報をå°åˆ·ã™ã‚‹ã®ã¯ã€ãƒ–ラウザã®å°åˆ·æ©Ÿèƒ½ã‚’使用ã—ã¾ã™ã€‚
      注: å³å´ã®ãƒ•レームã®ã¿ã‚’å°åˆ·ã—ã¦ãã ã•ã„。

    3. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "キーワードã®å‰²ã‚Šå½“ã¦"; -$TLS_htmltext['keywordsAssign'] = "

    目的:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "キーワードã®å‰²ã‚Šå½“ã¦"; +$TLS_htmltext['keywordsAssign'] = "

    目的:

    キーワードã®å‰²ã‚Šå½“ã¦ã§ã¯ã€ãƒ¦ãƒ¼ã‚¶ãŒä¸€æ‹¬ã—ã¦ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã¾ãŸã¯ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã« 一括ã—ã¦ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’割り当ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    @@ -272,16 +272,15 @@ TestLinkã¯ãƒ†ã‚¹ãƒˆè¨ˆç”»ã®å¤ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯ã€æœ€æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«å¯¾ã—ã¦ãŠã“ãªã£ãŸã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã®å‰²ã‚Šå½“ã¦ã«ã‚ˆã£ã¦å½±éŸ¿ã‚’å—ã‘ã¾ã›ã‚“。 ã‚‚ã—ãƒ†ã‚¹ãƒˆè¨ˆç”»ãŒæ›´æ–°ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’å¿…è¦ã¨ã™ã‚‹ãªã‚‰ã°ã€ã¯ã˜ã‚ã«ã€Œä¿®æ­£ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®æ›´æ–°ã€ 機能をキーワードã®å‰²ã‚Šå½“ã¦ã‚’行ã†å‰ã«ä½¿ç”¨ã—ã€å¯¾è±¡ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒæœ€æ–°ã§ã‚ã‚‹ã“ã¨ã‚’確ã‹ã‚ã¦ãã ã•ã„。 -

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "テストケース実行"; -$TLS_htmltext['executeTest'] = "

    目的:

    +

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "テストケース実行"; +$TLS_htmltext['executeTest'] = "

    目的:

    å„テスト担当者ãŒãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹å®Ÿè¡Œã‚’ãŠã“ãªã†ã“ã¨ãŒã§ãã¾ã™ã€‚ -ユーザã¯ãƒ†ã‚¹ãƒˆçµæžœã‚’ビルドã”ã¨ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«ç™»éŒ²ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚" . - "フィルタã€è¨­å®š(セッティング)ã«ã¤ã„ã¦ã®ã„ã‚ã„ã‚ãªæƒ…å ±ã«ã¤ã„ã¦ã¯ãƒ˜ãƒ«ãƒ—ã‚’å‚ç…§ã—ã¦ãã ã•ã„。(「?ã€ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’クリックã—ã¦ãã ã•ã„。)

    +ユーザã¯ãƒ†ã‚¹ãƒˆçµæžœã‚’ビルドã”ã¨ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«ç™»éŒ²ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚" . + "フィルタã€è¨­å®š(セッティング)ã«ã¤ã„ã¦ã®ã„ã‚ã„ã‚ãªæƒ…å ±ã«ã¤ã„ã¦ã¯ãƒ˜ãƒ«ãƒ—ã‚’å‚ç…§ã—ã¦ãã ã•ã„。(「?ã€ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’クリックã—ã¦ãã ã•ã„。)

    ã‚„ã£ã¦ã¿ã¾ã—ょã†ï¼

    @@ -292,24 +291,30 @@
  • ãƒ†ã‚¹ãƒˆçµæžœãŠã‚ˆã³ã€å‚™è€ƒã€ãƒã‚°ãªã©ã‚’登録ã—ã¾ã™ã€‚
  • çµæžœã‚’ä¿å­˜ã—ã¾ã™ã€‚
  • -

    備考: GUI上ã‹ã‚‰éšœå®³ãƒ¬ãƒãƒ¼ãƒˆã‚’作æˆã™ã‚‹ãŸã‚ã«ã¯ã€ -ãƒã‚°ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ã¨é€£æºã™ã‚‹ã‚ˆã†ã«TestLinkを設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "テストレãƒãƒ¼ãƒˆã¨æŒ‡æ¨™ã®æ¦‚è¦"; -$TLS_htmltext['showMetrics'] = "

    レãƒãƒ¼ãƒˆã¯ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«é–¢é€£ã—ã¦ã„ã¾ã™ã€‚ " . +

    注: GUI上ã‹ã‚‰éšœå®³ãƒ¬ãƒãƒ¼ãƒˆã‚’作æˆã™ã‚‹ãŸã‚ã«ã¯ã€ +ãƒã‚°ç®¡ç†ã‚·ã‚¹ãƒ†ãƒ ã¨é€£æºã™ã‚‹ã‚ˆã†ã«TestLinkを設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "テストレãƒãƒ¼ãƒˆã¨æŒ‡æ¨™ã®æ¦‚è¦"; +$TLS_htmltext['showMetrics'] = "

    レãƒãƒ¼ãƒˆã¯ãƒ†ã‚¹ãƒˆè¨ˆç”»ã«é–¢é€£ã—ã¦ã„ã¾ã™ã€‚ " . "(上段ã®ãƒŠãƒ“ゲータã«ã‚ˆã‚Šé¸æŠžã—ã¾ã™)。ç¾åœ¨å®Ÿè¡Œä¸­ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ä»¥å¤–ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ã‚’é¸æŠžã—㦠レãƒãƒ¼ãƒˆã‚’表示ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚以下ã®ãƒ¬ãƒãƒ¼ãƒˆãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒã§ãã¾ã™:

      -
    • Normal - Webページã«ãƒ¬ãƒãƒ¼ãƒˆã‚’表示
    • -
    • MS Excel - Microsoft Excel用ã«ãƒ¬ãƒãƒ¼ãƒˆã‚’エクスãƒãƒ¼ãƒˆ
    • -
    • HTML email - ユーザã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã«Eメールã§ãƒ¬ãƒãƒ¼ãƒˆã‚’é€ä¿¡
    • -
    • グラフ - グラフã«ã‚ˆã‚‹ãƒ¬ãƒãƒ¼ãƒˆã‚’表示 (Flash技術を使用)
    • +
    • HTML - Webページã«ãƒ¬ãƒãƒ¼ãƒˆã‚’表示
    • +
    • MS Word ライク - Microsoft Wordã«ãƒ¬ãƒãƒ¼ãƒˆã‚’インãƒãƒ¼ãƒˆ
    • +
    • メール (HTML) - ユーザã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«ãƒ¡ãƒ¼ãƒ«ã§ãƒ¬ãƒãƒ¼ãƒˆã‚’é€ä¿¡

    「å°åˆ·ã€ãƒœã‚¿ãƒ³ã¯1種類ã®ãƒ¬ãƒãƒ¼ãƒˆã®ã¿ã‚’å°åˆ·ã—ã¾ã™ã€‚

    数種類ã®ãƒ¬ãƒãƒ¼ãƒˆã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã„ã‹ã«å„レãƒãƒ¼ãƒˆã®æ©Ÿèƒ½ã‚’説明ã—ã¾ã™ã€‚

    +

    テスト計画

    +

    ドキュメント「テスト計画ã€ã«ã¯ã€å†…容ã¨ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã®æ§‹æˆã‚’定義ã™ã‚‹ã‚ªãƒ—ションãŒã‚りã¾ã™ã€‚

    + +

    テストレãƒãƒ¼ãƒˆ

    +

    ドキュメント「テストレãƒãƒ¼ãƒˆã€ã«ã¯ã€å†…容ã¨ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã®æ§‹æˆã‚’定義ã™ã‚‹ã‚ªãƒ—ションãŒã‚りã¾ã™ã€‚ +テストケースã¨å…±ã«ãƒ†ã‚¹ãƒˆçµæžœã‚‚å«ã¿ã¾ã™ã€‚

    +

    一般的ãªãƒ†ã‚¹ãƒˆè¨ˆç”»æŒ‡æ¨™

    ã“ã®ãƒšãƒ¼ã‚¸ã§ã¯ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã€ã‚ªãƒ¼ãƒŠãƒ¼ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã”ã¨ã®ãƒ†ã‚¹ãƒˆè¨ˆç”»ã®æœ€æ–°ã® ステータスを表示ã—ã¾ã™ã€‚「最新ã®ãƒ†ã‚¹ãƒˆçµæžœã€ã¨ã¯ã€æœ€å¾Œã«å®Ÿè¡Œã—ãŸãƒ“ルドã«é–¢ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®çµæžœã‚’示ã—ã¦ã„ã¾ã™ã€‚ @@ -345,29 +350,6 @@

    全ビルドã®å®Ÿè¡Œçµæžœã‚’一覧表示ã—ã¾ã™ã€‚å„ビルドã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®åˆè¨ˆæ•°ã€æˆåŠŸæ•°ã€æˆåŠŸçŽ‡ã€å¤±æ•—æ•°ã€å¤±æ•—率ã€ãƒ–ロック数ã€ãƒ–ãƒ­ãƒƒã‚¯çŽ‡ã€æœªå®Ÿè¡Œæ•°ã€æœªå®Ÿè¡ŒçŽ‡ã‚’è¡¨ç¤ºã—ã¾ã™ã€‚ ã‚‚ã—ã€åŒä¸€ãƒ“ルドã§ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒè¤‡æ•°å›žå®Ÿè¡Œã•れã¦ã„ã‚‹å ´åˆã¯ã€æœ€æ–°ã®ãƒ†ã‚¹ãƒˆçµæžœãŒè€ƒæ…®ã•れã¾ã™ã€‚

    -

    クエリー指標

    -

    ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã‚¯ã‚¨ãƒªãƒ¼å…¥åŠ›ãƒšãƒ¼ã‚¸ã¨ã€ã‚¯ã‚¨ãƒªãƒ¼ãƒ‡ãƒ¼ã‚¿ã‚’å«ã‚€ã‚¯ã‚¨ãƒªãƒ¼å‡ºåŠ›ãƒšãƒ¼ã‚¸ã‹ã‚‰æ§‹æˆã•れã¾ã™ã€‚ -4種類ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã§ã‚¯ã‚¨ãƒªãƒ¼ã‚’å½¢æˆã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ -デフォルトã§ã¯ã€æœ€ã‚‚多ãã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¨ãƒ“ãƒ«ãƒ‰ã‚’é¸æŠžã™ã‚‹ã‚ˆã†ã«å„コントロールãŒè¨­å®šã•れã¦ã„ã¾ã™ã€‚ -コントロールを変更ã™ã‚‹ã“ã¨ã«ã‚ˆã‚Šã€æŒ‡å®šã—ãŸã‚ªãƒ¼ãƒŠãƒ¼ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã€ -ビルドã®çµ„ã¿åˆã‚ã›ã«ã‚ˆã£ã¦ã€ãƒ¬ãƒãƒ¼ãƒˆã«è¡¨ç¤ºã•ã‚Œã‚‹çµæžœã‚’フィルタリングã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    - -
      -
    • キーワード: 0個ã¾ãŸã¯1個ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚デフォルトã§ã¯ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã¯é¸æŠžã•れã¦ã„ã¾ã›ã‚“。キーワードãŒé¸æŠžã•れã¦ã„ãªã„å ´åˆã¯ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰å‰²ã‚Šå½“ã¦ã«é–¢ä¿‚ãªãã€å…¨ã¦ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«é–¢ã™ã‚‹çµæžœã‚’表示ã—ã¾ã™ã€‚キーワードã¯ãƒ†ã‚¹ãƒˆä»•様ã®ãƒšãƒ¼ã‚¸ã‚‚ã—ãã¯ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ç®¡ç†ã®ãƒšãƒ¼ã‚¸ã§å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚キーワードã¯ã€å…¨ãƒ†ã‚¹ãƒˆè¨ˆç”»ã®å…¨ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«ã‚ãŸã£ã¦å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚特定ã®ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ãŒå‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„るテストケースã®çµæžœã«èˆˆå‘³ãŒã‚ã‚‹å ´åˆã¯ã€ã“ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã«ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’設定ã—ã¦ãã ã•ã„。
    • -
    • オーナー: 0人ã¾ãŸã¯1人ã®ã‚ªãƒ¼ãƒŠãƒ¼ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚デフォルトã§ã¯ã‚ªãƒ¼ãƒŠãƒ¼ã¯é¸æŠžã•れã¦ã„ã¾ã›ã‚“。 オーナーãŒé¸æŠžã•れã¦ã„ãªã„å ´åˆã¯ã€ã‚ªãƒ¼ãƒŠãƒ¼ã®å‰²ã‚Šå½“ã¦ã«é–¢ä¿‚ãªãã€å…¨ã¦ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«é–¢ã™ã‚‹çµæžœã‚’表示ã—ã¾ã™ã€‚「未割り当ã¦ã€ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¯æ¤œç´¢ã§ãã¾ã›ã‚“。オーナーシップã¯ã€ã€Œãƒ†ã‚¹ãƒˆå®Ÿè¡Œã®å‰²ã‚Šå½“ã¦ã€ã®ãƒšãƒ¼ã‚¸ã§å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ãŒã§ãã€ãƒ†ã‚¹ãƒˆè¨ˆç”»ã”ã¨ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¾ã™ã€‚特定ã®ãƒ†ã‚¹ã‚¿ãƒ¼ãŒå®Ÿè¡Œã—ãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®çµæžœã«èˆˆå‘³ãŒã‚ã‚‹å ´åˆã¯ã€ã“ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã«ã‚ªãƒ¼ãƒŠãƒ¼ã‚’設定ã—ã¦ãã ã•ã„。
    • -
    • トップレベルテストスイート: 0個ã‹ã‚‰n個ã®ãƒˆãƒƒãƒ—ãƒ¬ãƒ™ãƒ«ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚デフォルトã§ã¯å…¨ã¦ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆãŒé¸æŠžã•れã¦ã„ã¾ã™ã€‚é¸æŠžã—ãŸãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®çµæžœã®ã¿ãŒã€ã‚¯ã‚¨ãƒªãƒ¼ã®æŒ‡æ¨™ã«å映ã•れã¾ã™ã€‚特定ã®ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®çµæžœã«èˆˆå‘³ãŒã‚ã‚‹å ´åˆã¯ã€ã“ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚’æ“作ã—ã¦ãã ã•ã„。
    • -
    • ビルド: 1個ã‹ã‚‰n個ã®ãƒ“ãƒ«ãƒ‰ã‚’é¸æŠžã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚デフォルトã§ã¯å…¨ã¦ã®ãƒ“ルドãŒé¸æŠžã•れã¦ã„ã¾ã™ã€‚é¸æŠžã—ãŸãƒ“ルドã§ã®å®Ÿè¡Œçµæžœã®ã¿ã‚’使用ã—ã¦æŒ‡æ¨™ãŒè¨ˆç®—ã•れã¾ã™ã€‚例ãˆã°ã€æœ€å¾Œã®3回ã®ãƒ“ルドã§ã€ã„ãã¤ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’実行ã—ãŸã‹ã‚’調ã¹ãŸã„å ´åˆã¯ã€ã“ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚’æ“作ã—ã¦ãã ã•ã„。 -キーワードã€ã‚ªãƒ¼ãƒŠãƒ¼ã€ãƒˆãƒƒãƒ—ãƒ¬ãƒ™ãƒ«ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’é¸æŠžã™ã‚‹ã“ã¨ã§ã€ãƒ†ã‚¹ãƒˆè¨ˆç”»ã”ã¨ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã”ã¨ã®æŒ‡æ¨™è¨ˆç®—ã«ä½¿ç”¨ã™ã‚‹ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®æ•°ã‚’変化ã•ã›ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚例ãˆã°ã€ã‚ªãƒ¼ãƒŠãƒ¼=「Gregã€ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰=「優先度1ã€ã¨å…¨ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã‚’é¸æŠžã—ãŸå ´åˆã¯ã€Gregã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸå„ªå…ˆåº¦1ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ã¿ãŒè¨ˆç®—ã«ä½¿ç”¨ã•れã¾ã™ã€‚レãƒãƒ¼ãƒˆã®ã€Œãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹æ•°ã€ã®åˆè¨ˆã¯ã€ã“れら3種類ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã«ã‚ˆã£ã¦æ±ºã‚られã¾ã™ã€‚ -ビルドã®é¸æŠžã¯ã€ã€ŒæˆåŠŸã€ã€ã€Œå¤±æ•—ã€ã€ã€Œãƒ–ロックã€ã€ã€Œæœªå®Ÿè¡Œã€ã®æ•°ã«å½±éŸ¿ã—ã¾ã™ã€‚å…ˆã«èª¬æ˜Žã—ãŸã€ã€Œæœ€æ–°ã®ãƒ†ã‚¹ãƒˆçµæžœã€ã®ãƒ«ãƒ¼ãƒ«ã‚’å‚ç…§ã—ã¦ãã ã•ã„。
    • -
    -

    「クエリーをé€ä¿¡ã€ãƒœã‚¿ãƒ³ã‚’クリックã™ã‚‹ã¨ã€ã‚¯ã‚¨ãƒªãƒ¼å‡ºåŠ›ãƒšãƒ¼ã‚¸ãŒè¡¨ç¤ºã•れã¾ã™ã€‚

    - -

    クエリ出力ページ:

    -
      -
    1. レãƒãƒ¼ãƒˆã‚’作æˆã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã—ãŸã‚¯ã‚¨ãƒªãƒ¼ãƒ‘ラメータ
    2. -
    3. テスト計画内ã®åˆè¨ˆ
    4. -
    5. スイートã”ã¨ã®å…¨å®Ÿè¡Œçµæžœã®åˆè¨ˆ (ç·ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹æ•° / æˆåŠŸ / 失敗 / ブロック / 未実行)。もã—ã€ã‚るテストケースを複数ã®ãƒ“ルドã§è¤‡æ•°å›žå®Ÿè¡Œã—ãŸå ´åˆã€é¸æŠžã—ãŸãƒ“ルドã«é–¢é€£ã™ã‚‹å…¨ã¦ã®å®Ÿè¡Œçµæžœã‚’表示ã—ã¾ã™ã€‚ã—ã‹ã—ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã®å®Ÿè¡Œçµæžœã®æ¦‚è¦ã«ã¯ã€é¸æŠžã—ãŸãƒ“ルドã«å¯¾ã™ã‚‹ã€Œæœ€æ–°ã®ãƒ†ã‚¹ãƒˆçµæžœã€ã®ã¿ãŒå映ã•れã¾ã™ã€‚
    6. -

    ブロックã€å¤±æ•—ã€æœªå®Ÿè¡Œã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ãƒ¬ãƒãƒ¼ãƒˆ

    ã“れらã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã€ãƒ–ロックã€å¤±æ•—ã€æœªå®Ÿè¡Œã®å…¨ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’表示ã—ã¾ã™ã€‚「最新ã®ãƒ†ã‚¹ãƒˆçµæžœã€ã®ãƒ«ãƒ¼ãƒ«ï¼ˆä¸€èˆ¬çš„ãªãƒ†ã‚¹ãƒˆè¨ˆç”»æŒ‡æ¨™ã®é …ã§èª¬æ˜Žã—ã¾ã—ãŸï¼‰ã«ã‚ˆã£ã¦ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ãŒãƒ–ロックã€å¤±æ•—ã€æœªå®Ÿè¡Œã«è©²å½“ã™ã‚‹ã‹ã©ã†ã‹ã‚’判定ã—ã¾ã™ã€‚TestLinkã¨ãƒã‚°ãƒˆãƒ©ãƒƒã‚­ãƒ³ã‚°ã‚·ã‚¹ãƒ†ãƒ ã‚’çµ±åˆã—ã¦ä½¿ç”¨ã—ã¦ã„ã‚‹å ´åˆã¯ã€ãƒ–ロックã¨å¤±æ•—ã®ãƒ¬ãƒãƒ¼ãƒˆã«é–¢é€£ã™ã‚‹ãƒã‚°ãŒè¡¨ç¤ºã•れã¾ã™ã€‚

    @@ -385,27 +367,25 @@
  • トップレベルスイートã”ã¨ã®çµæžœã‚’表ã‚ã™æ£’グラフ
  • 棒グラフã¯ã€æˆåŠŸã€å¤±æ•—ã€ãƒ–ãƒ­ãƒƒã‚¯ã€æœªå®Ÿè¡Œã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹æ•°ã‚’近似的ã«ç¤ºã—ã¦ã„ã¾ã™ã€‚

    -

    ã“ã®ãƒ¬ãƒãƒ¼ãƒˆãƒšãƒ¼ã‚¸ã‚’表示ã™ã‚‹ãŸã‚ã«ã¯FlashプラグインãŒå¿…è¦ã§ã™ã€‚グラフィカルã«çµæžœã‚’表示ã™ã‚‹ãŸã‚ã« http://www.maani.us ãŒæä¾›ã—ã¦ã„ã‚‹ Flash 技術を使用ã—ã¦ã„ã¾ã™ã€‚

    å„テストケースã®ãƒã‚°ã®åˆè¨ˆ

    -

    ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã€ãƒ†ã‚¹ãƒˆãƒ—ロジェクト内ã®ãƒã‚°ãŒç™ºè¦‹ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’表示ã—ã¾ã™ã€‚ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ãƒã‚°ãƒˆãƒ©ã‚­ãƒ³ã‚°ã‚·ã‚¹ãƒ†ãƒ ãŒæŽ¥ç¶šã•れã¦ã„ã‚‹å ´åˆã®ã¿è¡¨ç¤ºã•れã¾ã™ã€‚

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "テスト計画ã¸ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’追加/削除"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

    目的:

    +

    ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã€ãƒ†ã‚¹ãƒˆãƒ—ロジェクト内ã®ãƒã‚°ãŒç™ºè¦‹ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’表示ã—ã¾ã™ã€‚ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ãƒã‚°ãƒˆãƒ©ã‚­ãƒ³ã‚°ã‚·ã‚¹ãƒ†ãƒ ãŒæŽ¥ç¶šã•れã¦ã„ã‚‹å ´åˆã®ã¿è¡¨ç¤ºã•れã¾ã™ã€‚

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "テスト計画ã¸ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’追加/削除"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

    目的:

    リーダー権é™ã‚’æŒã¤ãƒ¦ãƒ¼ã‚¶ã¯ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’テスト計画ã¸è¿½åŠ ã§ãã¾ã™ã€‚

    テストケースã®è¿½åŠ /削除:

    1. テストスイートをクリックã—ã€å…¨ã¦ã€ã¾ãŸã¯1ã¤ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’表示ã—ã¾ã™ã€‚
    2. ã‚ãªãŸãŒè¿½åŠ ã‚’å®Œäº†ã—ãŸã„時ã€ã€Œãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹è¿½åŠ /削除ã€ãƒœã‚¿ãƒ³ã‚’クリックã—ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’インãƒãƒ¼ãƒˆã—ã¦ä¸‹ã•ã„。 - 注æ„:åŒã˜ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’複数回追加ã™ã‚‹ã“ã¨ã¯å‡ºæ¥ã¾ã›ã‚“。
    3. -
    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "テストケース実行担当者ã®å‰²ã‚Šå½“ã¦"; -$TLS_htmltext['tc_exec_assignment'] = "

    目的:

    + 注:åŒã˜ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’複数回追加ã™ã‚‹ã“ã¨ã¯å‡ºæ¥ã¾ã›ã‚“。 +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "テストケース実行担当者ã®å‰²ã‚Šå½“ã¦"; +$TLS_htmltext['tc_exec_assignment'] = "

    目的:

    ã“ã®æ©Ÿèƒ½ã«ã‚ˆã‚Šã€å„テストケースã«ã¤ã„ã¦å®Ÿè¡Œã™ã‚‹è²¬ä»»ã‚’æŒã¤æ‹…当者を設定ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

    ã‚„ã£ã¦ã¿ã¾ã—ょã†ï¼

    @@ -414,16 +394,15 @@
  • 対象ã®ãƒ†ã‚¹ãƒˆæ‹…å½“è€…ã‚’é¸æŠžã—ã¾ã™ã€‚
  • 割り当ã¦ã‚’確定ã™ã‚‹ãŸã‚ã«ãƒœã‚¿ãƒ³ã‚’クリックã—ã¾ã™ã€‚
  • 実行ページã§å‰²ã‚Šå½“ã¦ã®å®Œäº†ã‚’確èªã—ã¾ã™ã€‚フィルターを割り当ã¦ãŸæ‹…当者ã«ã›ã£ã¦ã„ã—ã¦ã¿ã¦ãã ã•ã„。
  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "テスト計画ã«å‰²ã‚Šå½“ã¦ãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’æ›´æ–°ã™ã‚‹"; -$TLS_htmltext['planUpdateTC'] = "

    目的

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "テスト計画ã«å‰²ã‚Šå½“ã¦ãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’æ›´æ–°ã™ã‚‹"; +$TLS_htmltext['planUpdateTC'] = "

    目的

    ã“ã®ãƒšãƒ¼ã‚¸ã§ã¯ã€ãƒ†ã‚¹ãƒˆä»•様上ã§å¤‰æ›´ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã‚’最新ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã«æ›´æ–°ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ -テストã®å®Ÿè¡ŒæœŸé–“中ã«ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’明確ã«ã—ãŸã„å ´åˆã¯å¤šãã‚ã‚‹ã§ã—ょã†ã€‚" . -"例ãˆã°ã€ã‚るテスト設計者ãŒãƒ†ã‚¹ãƒˆä»•様を変更ã—ãŸã‘れã©ã‚‚テスト計画ã«ã¯ä¸€é€šã‚Šã®ãƒ†ã‚¹ãƒˆå®Œäº†å¾Œã«å映ã•ã›ãŸã„å ´åˆã€" . -"ã‚‚ã—ãã¯ã€ãƒ†ã‚¹ãƒˆå®Ÿè¡Œã¯ã‚ªãƒªã‚¸ãƒŠãƒ«ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§è¡Œã£ãŸã‘れã©ã‚‚ã€çµæžœã¯ä¿®æ­£ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¨å…±ã«å‚ç…§ã—ãŸã„å ´åˆãªã©ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚

    +テストã®å®Ÿè¡ŒæœŸé–“中ã«ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’明確ã«ã—ãŸã„å ´åˆã¯å¤šãã‚ã‚‹ã§ã—ょã†ã€‚" . + "例ãˆã°ã€ã‚るテスト設計者ãŒãƒ†ã‚¹ãƒˆä»•様を変更ã—ãŸã‘れã©ã‚‚テスト計画ã«ã¯ä¸€é€šã‚Šã®ãƒ†ã‚¹ãƒˆå®Œäº†å¾Œã«å映ã•ã›ãŸã„å ´åˆã€" . + "ã‚‚ã—ãã¯ã€ãƒ†ã‚¹ãƒˆå®Ÿè¡Œã¯ã‚ªãƒªã‚¸ãƒŠãƒ«ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§è¡Œã£ãŸã‘れã©ã‚‚ã€çµæžœã¯ä¿®æ­£ã•れãŸãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã¨å…±ã«å‚ç…§ã—ãŸã„å ´åˆãªã©ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚

    ã‚„ã£ã¦ã¿ã¾ã—ょã†ï¼

      @@ -431,12 +410,11 @@
    1. 対象ã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«å¯¾ã—ã¦ãƒ—ルダウンメニューã‹ã‚‰æ–°ãŸãªãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’é¸æŠžã—ã¾ã™ã€‚
    2. 変更を確定ã™ã‚‹ãŸã‚ã«ã€Œãƒ†ã‚¹ãƒˆè¨ˆç”»ã‚’æ›´æ–°ã€ãƒœã‚¿ãƒ³ã‚’クリックã—ã¾ã™ã€‚
    3. 実行ページã®ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®æ–‡ç« ã‚’確èªã—ã¾ã™ã€‚
    4. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "テストã«ç·Šæ€¥åº¦ã‚’設定ã™ã‚‹"; -$TLS_htmltext['test_urgency'] = "

    目的

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "テストã«ç·Šæ€¥åº¦ã‚’設定ã™ã‚‹"; +$TLS_htmltext['test_urgency'] = "

    目的

    TestLinkã§ã¯ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã®å„ªå…ˆåº¦ã«å½±éŸ¿ã‚’与ãˆã‚‹ã€Œç·Šæ€¥åº¦ã€ã‚’テストスイートã«è¨­å®šã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ テストã®å„ªå…ˆåº¦ã¯å„テストケースã®ä½œæˆæ™‚ã«è¨­å®šã™ã‚‹é‡è¦åº¦ã¨ãƒ†ã‚¹ãƒˆè¨ˆç”»ã§å®šç¾©ã™ã‚‹é‡è¦åº¦ ã®ä¸¡æ–¹ã‹ã‚‰è¨ˆç®—ã•れã¾ã™ã€‚テストリーダーã¯å§‹ã‚ã«å®Ÿè¡Œã™ã¹ãテストを指定ã™ã¹ãã§ã—ょã†ã€‚ @@ -451,10 +429,8 @@ ã®å„ªå…ˆåº¦ã‚’上ã’ãŸã‚Šã¨ã„ã£ãŸã“ã¨ãŒå¯èƒ½ã§ã™ã€‚

  • 変更を確定ã™ã‚‹ãŸã‚ã«ã€Œä¿å­˜ã€ãƒœã‚¿ãƒ³ã‚’クリックã—ã¦ãã ã•ã„。
  • -

    一例ã¨ã—ã¦ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«é‡è¦åº¦ã€Œé«˜ã€ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã«ç·Šæ€¥åº¦ã€Œä½Žã€ã‚’設定ã™ã‚‹ã¨ " . - "優先度ã¯ã€Œä¸­ã€ã«ãªã‚Šã¾ã™ã€‚"; - - -// ------------------------------------------------------------------------------------------ - +

    一例ã¨ã—ã¦ã€ãƒ†ã‚¹ãƒˆã‚±ãƒ¼ã‚¹ã«é‡è¦åº¦ã€Œé«˜ã€ã€ãƒ†ã‚¹ãƒˆã‚¹ã‚¤ãƒ¼ãƒˆã«ç·Šæ€¥åº¦ã€Œä½Žã€ã‚’設定ã™ã‚‹ã¨ " . "優先度ã¯ã€Œä¸­ã€ã«ãªã‚Šã¾ã™ã€‚"; + +// ------------------------------------------------------------------------------------------ + ?> diff --git a/locale/ko_KR/description.php b/locale/ko_KR/description.php index a4e365e45f..56d3961cf8 100644 --- a/locale/ko_KR/description.php +++ b/locale/ko_KR/description.php @@ -1,66 +1,66 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - * ---------------------------------------------------------------------------------- - * Korean translation - *------------------------------------------------------------------- - * Translated by Jiun PARK - * (DQA Team, OPENTECH INC. R&D Center) - * E-mail : rustyheart@gmail.com - * Issued Date : 2009/05/27 - * - *------------------------------------------------------------------- - */ - -// printFilter.html +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + * ---------------------------------------------------------------------------------- + * Korean translation + *------------------------------------------------------------------- + * Translated by Jiun PARK + * (DQA Team, OPENTECH INC. R&D Center) + * E-mail : rustyheart@gmail.com + * Issued Date : 2009/05/27 + * + *------------------------------------------------------------------- + */ + +// printFilter.html $TLS_hlp_generateDocOptions = "

    문서 ìƒì„±ì˜ 옵션

    -

    사용ìžëŠ” 문서를 ë³´ê¸°ì „ì— ì´ í…Œì´ë¸”ì„ ì‚¬ìš©í•˜ì—¬ 테스트 ì¼€ì´ìŠ¤ë¥¼ ì„ íƒ í•  수 있습니다. -ì„ íƒí•œ(ì²´í¬í•œ) ìžë£ŒëŠ” 표시가 ë©ë‹ˆë‹¤. í‘œì‹œë  ìžë£Œë¥¼ 변경하려면, 필터를 ì„ íƒ ë˜ëŠ” 해제한 후 +

    사용ìžëŠ” 문서를 ë³´ê¸°ì „ì— ì´ í…Œì´ë¸”ì„ ì‚¬ìš©í•˜ì—¬ 테스트 ì¼€ì´ìŠ¤ë¥¼ ì„ íƒ í•  수 있습니다. +ì„ íƒí•œ(ì²´í¬í•œ) ìžë£ŒëŠ” 표시가 ë©ë‹ˆë‹¤. í‘œì‹œë  ìžë£Œë¥¼ 변경하려면, 필터를 ì„ íƒ ë˜ëŠ” 해제한 후 트리ì—서 ì›í•˜ëŠ” ë ˆë²¨ì˜ ìžë£Œë¥¼ í´ë¦­í•˜ì‹œë©´ ë©ë‹ˆë‹¤.

    -

    문서 ë¨¸ë¦¿ë§ : ë¬¸ì„œì˜ ë¨¸ë¦¿ë§ ì •ë³´ë¥¼ ì„ íƒí•  수 있습니다. -ë¬¸ì„œì˜ ë¨¸ë¦¿ë§ ì •ë³´ì—는 다ìŒê³¼ ê°™ì€ ê²ƒë“¤ì´ ìžˆìŠµë‹ˆë‹¤ : 소개, 범위, 참고ìžë£Œ, +

    문서 ë¨¸ë¦¿ë§ : ë¬¸ì„œì˜ ë¨¸ë¦¿ë§ ì •ë³´ë¥¼ ì„ íƒí•  수 있습니다. +ë¬¸ì„œì˜ ë¨¸ë¦¿ë§ ì •ë³´ì—는 다ìŒê³¼ ê°™ì€ ê²ƒë“¤ì´ ìžˆìŠµë‹ˆë‹¤ : 소개, 범위, 참고ìžë£Œ, 테스트 방법론, 테스트 제약사항.

    -

    테스트 ì¼€ì´ìФ 본문 : 테스트 ì¼€ì´ìŠ¤ì˜ ë³¸ë¬¸ 정보를 ì„ íƒí•  수 있습니다. 테스트 ì¼€ì´ìФ +

    테스트 ì¼€ì´ìФ 본문 : 테스트 ì¼€ì´ìŠ¤ì˜ ë³¸ë¬¸ 정보를 ì„ íƒí•  수 있습니다. 테스트 ì¼€ì´ìФ 본문 정보는 다ìŒê³¼ ê°™ì€ ê²ƒë“¤ì´ ìžˆìŠµë‹ˆë‹¤ : 요약, 실행순서, 예ìƒê²°ê³¼, 키워드.

    -

    테스트 ì¼€ì´ìФ 요약 : 사용ìžëŠ” 테스트 ì¼€ì´ìФ ì œëª©ì— ìžˆëŠ” 테스트 ì¼€ì´ìФ -요약정보를 ì„ íƒí•  수 있지만, 테스트 ì¼€ì´ìФ ë³¸ë¬¸ì— ìžˆëŠ” 테스트 ì¼€ì´ìФ 요약 정보는 ì„ íƒí•  -수 없습니다. 테스트 ì¼€ì´ìФ ìš”ì•½ì€ ê°„ë‹¨í•œ 요약과 함께 ì œëª©ì„ ë³´ëŠ” ê²ƒì„ ì§€ì›í•˜ê³ , -실행순서, 예ìƒê²°ê³¼, 키워드를 ìƒëžµí•  수 있ë„ë¡ í•˜ê¸° 위해 부분ì ìœ¼ë¡œ 테스트 ì¼€ì´ìФ 본문ì—서 -분리 ë˜ì–´ 있습니다. 만약 사용ìžê°€ 테스트 ì¼€ì´ìФ ë³¸ë¬¸ì„ ë³´ê² ë‹¤ê³  ì„ íƒí•˜ë©´, 테스트 ì¼€ì´ìФ +

    테스트 ì¼€ì´ìФ 요약 : 사용ìžëŠ” 테스트 ì¼€ì´ìФ ì œëª©ì— ìžˆëŠ” 테스트 ì¼€ì´ìФ +요약정보를 ì„ íƒí•  수 있지만, 테스트 ì¼€ì´ìФ ë³¸ë¬¸ì— ìžˆëŠ” 테스트 ì¼€ì´ìФ 요약 정보는 ì„ íƒí•  +수 없습니다. 테스트 ì¼€ì´ìФ ìš”ì•½ì€ ê°„ë‹¨í•œ 요약과 함께 ì œëª©ì„ ë³´ëŠ” ê²ƒì„ ì§€ì›í•˜ê³ , +실행순서, 예ìƒê²°ê³¼, 키워드를 ìƒëžµí•  수 있ë„ë¡ í•˜ê¸° 위해 부분ì ìœ¼ë¡œ 테스트 ì¼€ì´ìФ 본문ì—서 +분리 ë˜ì–´ 있습니다. 만약 사용ìžê°€ 테스트 ì¼€ì´ìФ ë³¸ë¬¸ì„ ë³´ê² ë‹¤ê³  ì„ íƒí•˜ë©´, 테스트 ì¼€ì´ìФ ìš”ì•½ì€ í•­ìƒ í¬í•¨ë©ë‹ˆë‹¤.

    목차 : ì´ ê°’ì„ ì„ íƒí•˜ë©´ TestLink는 모든 ì œëª©ì„ ë§í¬ë¥¼ 걸어서 표시합니다.

    -

    문서 í˜•ì‹ : ë‹¤ìŒ ì„¸ 가지 형ì‹ì´ 가능합니다 : HTML, OpenOffice Writer, MS Word. HTMLì„ ì œì™¸í•œ -나머지는 브ë¼ìš°ì €ê°€ 해당 S/W ì»´í¬ë„ŒíŠ¸ë¥¼ 호출합니다.

    "; - -// testPlan.html +

    문서 í˜•ì‹ : ë‹¤ìŒ ì„¸ 가지 형ì‹ì´ 가능합니다 : HTML, OpenOffice Writer, MS Word. HTMLì„ ì œì™¸í•œ +나머지는 브ë¼ìš°ì €ê°€ 해당 S/W ì»´í¬ë„ŒíŠ¸ë¥¼ 호출합니다.

    "; + +// testPlan.html $TLS_hlp_testPlan = "

    테스트 계íš

    개요

    @@ -68,22 +68,22 @@ 테스트 활ë™ì„ ì¡°ì§í•˜ê³  결과를 ì¶”ì í•  수 있습니다.

    테스트 실행

    -

    ì´ ì„¹ì…˜ì—서 사용ìžëŠ” 테스트 ì¼€ì´ìŠ¤ë¥¼ 실행(결과를 기재)하고 테스트 계íšì„ ì¸ì‡„í•  수 있습니다. -그리고 ì´ ê³³ì—서 사용ìžëŠ” ìžì‹ ì˜ 테스트 ì¼€ì´ìФ 실행 결과를 ì¶”ì í•  수 있습니다.

    +

    ì´ ì„¹ì…˜ì—서 사용ìžëŠ” 테스트 ì¼€ì´ìŠ¤ë¥¼ 실행(결과를 기재)하고 테스트 계íšì„ ì¸ì‡„í•  수 있습니다. +그리고 ì´ ê³³ì—서 사용ìžëŠ” ìžì‹ ì˜ 테스트 ì¼€ì´ìФ 실행 결과를 ì¶”ì í•  수 있습니다.

    테스트 ê³„íš ê´€ë¦¬

    -

    리드 ê¶Œí•œì´ ìžˆëŠ” 사용ìžëŠ”, ì´ ì„¹ì…˜ì—서 테스트 계íšì„ 관리할 수 있습니다. -테스트 계íšì˜ 관리는 계íšì„ ìƒì„±/편집/삭제하고, 계íšì— 테스트 ì¼€ì´ìŠ¤ë“¤ì„ +

    리드 ê¶Œí•œì´ ìžˆëŠ” 사용ìžëŠ”, ì´ ì„¹ì…˜ì—서 테스트 계íšì„ 관리할 수 있습니다. +테스트 계íšì˜ 관리는 계íšì„ ìƒì„±/편집/삭제하고, 계íšì— 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ì¶”ê°€/편집/ì‚­ì œ/ì—…ë°ì´íЏ 하고, ë¹Œë“œë“¤ì„ ìƒì„±í•˜ëŠ” 것으로 구성 ë˜ì–´ 있습니다.
    -리드 ê¶Œí•œì´ ìžˆëŠ” 사용ìžëŠ” 우선순위/위험ë„와 테스트 ìŠ¤ìœ„íŠ¸ì˜ ë‹´ë‹¹ìž ì§€ì •, 테스팅 -마ì¼ìŠ¤í†¤ ì§€ì •ì„ í•  수 있습니다.

    - -

    노트: 드롭다운 ìƒìžì— ì–´ë–¤ 테스트 계íšë„ 표시 ë˜ì§€ ì•Šì„ ìˆ˜ 있습니다. -ì´ëŸ° 경우 리드 ê¶Œí•œì´ ìžˆëŠ” ì‚¬ëžŒì´ ì•„ë‹ˆë©´ 아래 ë§í¬ë¥¼ 사용할 수가 없습니다. -ë‹¹ì‹ ì´ ì§€ê¸ˆ ì´ëŸ° ìƒí™©ì´ë¼ë©´ ì ì ˆí•œ 프로ì íЏ ê¶Œí•œì„ ê°€ì§€ê³  있거나 테스트 계íšì„ -ìƒì„±í•  수 있는 리드 ë˜ëŠ” 관리ìžì—게 ì—°ë½ í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤.

    "; - -// custom_fields.html +리드 ê¶Œí•œì´ ìžˆëŠ” 사용ìžëŠ” 우선순위/위험ë„와 테스트 ìŠ¤ìœ„íŠ¸ì˜ ë‹´ë‹¹ìž ì§€ì •, 테스팅 +마ì¼ìŠ¤í†¤ ì§€ì •ì„ í•  수 있습니다.

    + +

    노트: 드롭다운 ìƒìžì— ì–´ë–¤ 테스트 계íšë„ 표시 ë˜ì§€ ì•Šì„ ìˆ˜ 있습니다. +ì´ëŸ° 경우 리드 ê¶Œí•œì´ ìžˆëŠ” ì‚¬ëžŒì´ ì•„ë‹ˆë©´ 아래 ë§í¬ë¥¼ 사용할 수가 없습니다. +ë‹¹ì‹ ì´ ì§€ê¸ˆ ì´ëŸ° ìƒí™©ì´ë¼ë©´ ì ì ˆí•œ 프로ì íЏ ê¶Œí•œì„ ê°€ì§€ê³  있거나 테스트 계íšì„ +ìƒì„±í•  수 있는 리드 ë˜ëŠ” 관리ìžì—게 ì—°ë½ í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤.

    "; + +// custom_fields.html $TLS_hlp_customFields = "

    ì‚¬ìš©ìž í•„ë“œ

    다ìŒì€ ì‚¬ìš©ìž í•„ë“œì˜ ì •ì˜ì— 대한 몇가지 사실 입니다 :

      @@ -98,12 +98,12 @@

      ì‚¬ìš©ìž í•„ë“œì˜ ì •ì˜ì—는 다ìŒê³¼ ê°™ì€ ë…¼ë¦¬ì ì¸ ì†ì„±ì´ í¬í•¨ë˜ì–´ 있습니다. :

      • ì‚¬ìš©ìž í•„ë“œ ì´ë¦„
      • -
      • 변수 ì´ë¦„ (예: ì´ê²ƒì€ lang_get APIì— ì˜í•´ 제공ë˜ëŠ” ê°’ì´ê±°ë‚˜, 언어 파ì¼ì—서 찾지 +
      • 변수 ì´ë¦„ (예: ì´ê²ƒì€ lang_get APIì— ì˜í•´ 제공ë˜ëŠ” ê°’ì´ê±°ë‚˜, 언어 파ì¼ì—서 찾지 못할 경우 그대로 표시 ë˜ëŠ” 값입니다).
      • ì‚¬ìš©ìž í•„ë“œ 종류 (string, numeric, float, enum, email)
      • -
      • ì—´ê±° 가능한 값들 (예: RED|YELLOW|BLUE), 목ë¡, 다중 ì„ íƒ ê°€ëŠ¥í•œ 목ë¡, +
      • ì—´ê±° 가능한 값들 (예: RED|YELLOW|BLUE), 목ë¡, 다중 ì„ íƒ ê°€ëŠ¥í•œ 목ë¡, 콤보 박스들.
        -ì—´ê±° 가능한 ê°’ë“¤ì„ êµ¬ë¶„í•˜ê¸° 위해 파ì´í”„ 문ìž('|')를 사용할 수 있습니다. +ì—´ê±° 가능한 ê°’ë“¤ì„ êµ¬ë¶„í•˜ê¸° 위해 파ì´í”„ 문ìž('|')를 사용할 수 있습니다. 값들 중 하나는 공백 문ìžì—´ì„ 사용할 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤.
      • 기본 ê°’ : *ì•„ì§ êµ¬í˜„ë˜ì§€ 않았습니다*
      • @@ -119,26 +119,26 @@
      • 테스트 ê³„íš ì„¤ê³„ì—서 사용. 사용ìžëŠ” 테스트 계íšì„ 설계하면서 ì‚¬ìš©ìž í•„ë“œì˜ ê°’ì„ ë³€ê²½í•  수 있습니다 (테스트 계íšì— 테스트 ì¼€ì´ìФ 추가)
      • 사용할 ê³³. 사용ìžëŠ” ì‚¬ìš©ìž í•„ë“œë¥¼ ì–´ë–¤ 항목 아래ì—서 사용할 ì§€ ì„ íƒí•  수 있습니다.
      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

      테스트 ì¼€ì´ìФ 실행하기

      -

      사용ìžê°€ 테스트 ì¼€ì´ìŠ¤ë¥¼ '실행' í•  수 있습니다. ì‹¤í–‰ì€ ì„ íƒí•œ ë¹Œë“œì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì— +

      사용ìžê°€ 테스트 ì¼€ì´ìŠ¤ë¥¼ '실행' í•  수 있습니다. ì‹¤í–‰ì€ ì„ íƒí•œ ë¹Œë“œì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì— ê²°ê³¼(통과, 실패, 중단)를 지정하는 ê²ƒì„ ë§í•©ë‹ˆë‹¤.

      -

      ì„¤ì •ì´ ë˜ì–´ 있다면 버그 ì¶”ì  ì‹œìŠ¤í…œì— ì ‘ì†ì´ 가능합니다. 사용ìžëŠ” 새 버그를 ì§ì ‘ -추가하고 검색할 수 있습니다.

      "; - -//bug_add.html +

      ì„¤ì •ì´ ë˜ì–´ 있다면 버그 ì¶”ì  ì‹œìŠ¤í…œì— ì ‘ì†ì´ 가능합니다. 사용ìžëŠ” 새 버그를 ì§ì ‘ +추가하고 검색할 수 있습니다.

      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

      테스트 ì¼€ì´ìŠ¤ì— ë²„ê·¸ 추가하기

      (설정 ë˜ì–´ ìžˆì„ ê²½ìš°ì—ë§Œ) -TestLink는 버그 ì¶”ì  ì‹œìŠ¤í…œ(BTS)와 아주 단순한 수준으로 통합합니다. -즉, BTSì— ë²„ê·¸ ìƒì„± 요구를 보낼 ìˆ˜ë„ ì—†ê³ , 버그 IDê°’ì„ ë°›ì•„ 올 ìˆ˜ë„ ì—†ìŠµë‹ˆë‹¤. -ì´ í†µí•©ì€ BTSì˜ íŽ˜ì´ì§€ì— 대한 ë§í¬ë¥¼ 사용하는 ê²ƒì„ ë§í•˜ë©°, 다ìŒê³¼ ê°™ì€ ê¸°ëŠ¥ì´ ìžˆìŠµë‹ˆë‹¤ : +TestLink는 버그 ì¶”ì  ì‹œìŠ¤í…œ(BTS)와 아주 단순한 수준으로 통합합니다. +즉, BTSì— ë²„ê·¸ ìƒì„± 요구를 보낼 ìˆ˜ë„ ì—†ê³ , 버그 IDê°’ì„ ë°›ì•„ 올 ìˆ˜ë„ ì—†ìŠµë‹ˆë‹¤. +ì´ í†µí•©ì€ BTSì˜ íŽ˜ì´ì§€ì— 대한 ë§í¬ë¥¼ 사용하는 ê²ƒì„ ë§í•˜ë©°, 다ìŒê³¼ ê°™ì€ ê¸°ëŠ¥ì´ ìžˆìŠµë‹ˆë‹¤ :

      • 새 버그 추가하기.
      • 기존 버그 ì •ë³´ 표시하기.
      -

      +

      버그를 추가하는 순서

      @@ -147,112 +147,108 @@

    • 2 단계 : BTSì—서 í• ë‹¹ëœ BUGID를 ì ìŠµë‹ˆë‹¤.
    • 3 단계 : ìž…ë ¥ í•„ë“œì— BUGID를 ì ìŠµë‹ˆë‹¤.
    • 4 단계 : 버그 추가 ë²„íŠ¼ì„ í´ë¦­í•©ë‹ˆë‹¤.
    • -
    + 버그 추가 í™”ë©´ì„ ë‹«ìœ¼ë©´, 실행 í™”ë©´ì— ê´€ë ¨ 버그가 표시 ë©ë‹ˆë‹¤. -

    "; - -// execFilter.html +

    "; + +// execFilter.html $TLS_hlp_executeFilter = "

    테스트 ì‹¤í–‰ì„ ìœ„í•œ 필터와 빌드 설정하기

    -

    왼쪽 í™”ë©´ì€ ë„¤ë¹„ê²Œì´í„°, 현재 테스트 계íšì— ì§€ì •ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ê³¼ í•„í„° & 설정" . -"으로 ì´ë£¨ì–´ì ¸ 있습니다. ì´ í•„í„°ë“¤ì€ ì‚¬ìš©ìžê°€ 실행하기 ì „ ì œê³µëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë¥¼ " . -"구별하기 위해 사용할 수 있습니다." . -"필터를 설정하고, \"ì ìš©\" ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ 트리 ë©”ë‰´ì— ì ì ˆí•œ 테스트 ì¼€ì´ìŠ¤ë“¤ì´ " . -"ì„ íƒë©ë‹ˆë‹¤.

    +

    왼쪽 í™”ë©´ì€ ë„¤ë¹„ê²Œì´í„°, 현재 테스트 계íšì— ì§€ì •ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ê³¼ í•„í„° & 설정" . + "으로 ì´ë£¨ì–´ì ¸ 있습니다. ì´ í•„í„°ë“¤ì€ ì‚¬ìš©ìžê°€ 실행하기 ì „ ì œê³µëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë¥¼ " . "구별하기 위해 사용할 수 있습니다." . + "필터를 설정하고, \"ì ìš©\" ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ 트리 ë©”ë‰´ì— ì ì ˆí•œ 테스트 ì¼€ì´ìŠ¤ë“¤ì´ " . + "ì„ íƒë©ë‹ˆë‹¤.

    빌드

    -

    사용ìžëŠ” 테스트 결과와 ì—°ê²°ë  ë¹Œë“œë¥¼ 반드시 ì„ íƒí•´ì•¼ 합니다. " . -"빌드는 현재 테스트 계íšì˜ 기본 요소입니다. ê°ê°ì˜ 테스트 ì¼€ì´ìŠ¤ëŠ” " . -"빌드ì—서 여러번 ì‹¤í–‰ë  ìˆ˜ 있습니다. 하지만 마지막 결과는 하나 입니다. +

    사용ìžëŠ” 테스트 결과와 ì—°ê²°ë  ë¹Œë“œë¥¼ 반드시 ì„ íƒí•´ì•¼ 합니다. " . + "빌드는 현재 테스트 계íšì˜ 기본 요소입니다. ê°ê°ì˜ 테스트 ì¼€ì´ìŠ¤ëŠ” " . + "빌드ì—서 여러번 ì‹¤í–‰ë  ìˆ˜ 있습니다. 하지만 마지막 결과는 하나 입니다.
    빌드는 새 빌드 ìƒì„± 화면ì—서 리드가 ìƒì„±í•  수 있습니다.

    테스트 ì¼€ì´ìФ ID í•„í„°

    -

    사용ìžëŠ” 유ì¼í•œ ID로 테스트 ì¼€ì´ìŠ¤ë¥¼ ì„ íƒí•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. ì´ ID는 -테스트 ì¼€ì´ìŠ¤ë¥¼ ìƒì„±í•  때 ìžë™ìœ¼ë¡œ 부여 ë©ë‹ˆë‹¤. ì´ í•„ë“œë¥¼ 공란으로 ë‘ë©´ -필터를 ì ìš©í•˜ì§€ 않겠다는 ì˜ë¯¸ê°€ ë©ë‹ˆë‹¤.

    +

    사용ìžëŠ” 유ì¼í•œ ID로 테스트 ì¼€ì´ìŠ¤ë¥¼ ì„ íƒí•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. ì´ ID는 +테스트 ì¼€ì´ìŠ¤ë¥¼ ìƒì„±í•  때 ìžë™ìœ¼ë¡œ 부여 ë©ë‹ˆë‹¤. ì´ í•„ë“œë¥¼ 공란으로 ë‘ë©´ +필터를 ì ìš©í•˜ì§€ 않겠다는 ì˜ë¯¸ê°€ ë©ë‹ˆë‹¤.

    우선순위 필터

    -

    사용ìžëŠ” 우선순위를 사용하여 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ì„ íƒí•  수 있습니다. ê°ê°ì˜ 테스트 -ì¼€ì´ìФ 중요ë„는 현재 테스트 계íšì˜ 테스트 긴급ë„와 함께 ì¡°í•©ë©ë‹ˆë‹¤. 예를 들어, -ì¤‘ìš”ë„ ë˜ëŠ” 긴급ë„ê°€ 높ìŒì´ê³  ë‘번째 ì†ì„±ì´ ì ì–´ë„ 보통 레벨ì´ë©´, ì´ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì˜ -우선순위는 '높ìŒ'ì´ ë©ë‹ˆë‹¤.

    +

    사용ìžëŠ” 우선순위를 사용하여 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ì„ íƒí•  수 있습니다. ê°ê°ì˜ 테스트 +ì¼€ì´ìФ 중요ë„는 현재 테스트 계íšì˜ 테스트 긴급ë„와 함께 ì¡°í•©ë©ë‹ˆë‹¤. 예를 들어, +ì¤‘ìš”ë„ ë˜ëŠ” 긴급ë„ê°€ 높ìŒì´ê³  ë‘번째 ì†ì„±ì´ ì ì–´ë„ 보통 레벨ì´ë©´, ì´ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì˜ +우선순위는 '높ìŒ'ì´ ë©ë‹ˆë‹¤.

    ê²°ê³¼ í•„í„°

    -

    사용ìžëŠ” ê²°ê³¼ì— ë”°ë¼ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë¥¼ ì„ íƒí•  수 있습니다. 결과는 개별 ë¹Œë“œì˜ í…ŒìŠ¤íŠ¸ -ì¼€ì´ìŠ¤ì— ì¼ì–´ë‚œ ê²ƒì„ ë§í•©ë‹ˆë‹¤. 테스트 ì¼€ì´ìŠ¤ëŠ” 통과, 실패, 중단 ë˜ëŠ” ì‹¤í–‰ì•ˆí•¨ì´ ë  ìˆ˜ -있습니다." . -"ì´ í•„í„°ì˜ ê¸°ë³¸ê°’ì€ ëª¨ë‘ ì„ íƒ ìž…ë‹ˆë‹¤.

    +

    사용ìžëŠ” ê²°ê³¼ì— ë”°ë¼ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë¥¼ ì„ íƒí•  수 있습니다. 결과는 개별 ë¹Œë“œì˜ í…ŒìŠ¤íŠ¸ +ì¼€ì´ìŠ¤ì— ì¼ì–´ë‚œ ê²ƒì„ ë§í•©ë‹ˆë‹¤. 테스트 ì¼€ì´ìŠ¤ëŠ” 통과, 실패, 중단 ë˜ëŠ” ì‹¤í–‰ì•ˆí•¨ì´ ë  ìˆ˜ +있습니다." . + "ì´ í•„í„°ì˜ ê¸°ë³¸ê°’ì€ ëª¨ë‘ ì„ íƒ ìž…ë‹ˆë‹¤.

    테스터 필터

    -

    사용ìžëŠ” 담당ìžì— ë”°ë¼ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë¥¼ ì„ íƒí•  수 있습니다. \"ì§€ì •ì•ˆëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ -í¬í•¨\" ì²´í¬ìƒìžë¥¼ 사용하면 담당ìžê°€ 없는 테스트 ì¼€ì´ìŠ¤ë„ í¬í•¨í•  수 있습니다.

    "; -/* -

    Most Current Result

    -

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted -by the build that is chosen from the dropdown box. In this state the tree will display -the test cases status. -
    Example: User selects build 2 from the dropdown box and doesn't check the 'most -current' checkbox. All test cases will be shown with their status from build 2. -So, if test case 1 passed in build 2 it will be colored green. -
    If the user decideds to check the 'most current' checkbox the tree will be -colored by the test cases most recent result. -
    Ex: User selects build 2 from the dropdown box and this time checks -the 'most current' checkbox. All test cases will be shown with most current -status. So, if test case 1 passed in build 3, even though the user has also selected -build 2, it will be colored green.

    - */ - - -// newest_tcversions.html +

    사용ìžëŠ” 담당ìžì— ë”°ë¼ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë¥¼ ì„ íƒí•  수 있습니다. \"ì§€ì •ì•ˆëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ +í¬í•¨\" ì²´í¬ìƒìžë¥¼ 사용하면 담당ìžê°€ 없는 테스트 ì¼€ì´ìŠ¤ë„ í¬í•¨í•  수 있습니다.

    "; +/* + *

    Most Current Result

    + *

    By default or if the 'most current' checkbox is unchecked, the tree will be sorted + * by the build that is chosen from the dropdown box. In this state the tree will display + * the test cases status. + *
    Example: User selects build 2 from the dropdown box and doesn't check the 'most + * current' checkbox. All test cases will be shown with their status from build 2. + * So, if test case 1 passed in build 2 it will be colored green. + *
    If the user decideds to check the 'most current' checkbox the tree will be + * colored by the test cases most recent result. + *
    Ex: User selects build 2 from the dropdown box and this time checks + * the 'most current' checkbox. All test cases will be shown with most current + * status. So, if test case 1 passed in build 3, even though the user has also selected + * build 2, it will be colored green.

    + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

    ì—°ê²°ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ì˜ ìƒˆ 버전들

    -

    테스트 계íšì— ì—°ê²°ëœ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ë“¤ì„ ë¶„ì„하여, 현재 테스트 계íšì— +

    테스트 계íšì— ì—°ê²°ëœ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ë“¤ì„ ë¶„ì„하여, 현재 테스트 계íšì— í¬í•¨ë˜ì§€ ì•Šì€ ìƒˆ ë²„ì „ì´ ìžˆëŠ” 테스트 ì¼€ì´ìŠ¤ë“¤ì„ í‘œì‹œí•©ë‹ˆë‹¤. -

    "; - - -// requirementsCoverage.html +

    "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

    요구사항 커버리지


    -

    ì´ ê¸°ëŠ¥ì„ ì‚¬ìš©í•˜ì—¬ 요구사항과 테스트 ì¼€ì´ìŠ¤ë¥¼ 매핑할 수 있습니다. +

    ì´ ê¸°ëŠ¥ì„ ì‚¬ìš©í•˜ì—¬ 요구사항과 테스트 ì¼€ì´ìŠ¤ë¥¼ 매핑할 수 있습니다. ë©”ì¸í™”ë©´ì—서 \"요구사항 명세\"를 ì„ íƒí•˜ì„¸ìš”.

    요구사항 명세

    -

    ìš”êµ¬ì‚¬í•­ì€ í…ŒìŠ¤íŠ¸ 프로ì íŠ¸ì™€ ê´€ë ¨ëœ '요구사항 명세' ë¬¸ì„œë“¤ì˜ ëª¨ìŒìž…니다.
    -TestLink는 요구사항 명세와 요구사항 ìžì²´ì— 대해 ë²„ì „ì„ ì§€ì›í•˜ì§€ 않습니다. 그래서, -ëª…ì„¸ì˜ ì œëª©ì— ë¬¸ì„œì˜ ë²„ì „ì„ ê¸°ìž¬í•˜ëŠ” ë°©ë²•ì„ ì‚¬ìš©í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤. -사용ìžëŠ” 범위 í•„ë“œì— ê°„ë‹¨í•œ 설명ì´ë‚˜ 노트를 남길 수 있습니다.

    - -

    모든 ìš”êµ¬ì‚¬í•­ì˜ ê°œìˆ˜ëŠ” 모든 ìš”êµ¬ì‚¬í•­ì´ TestLinkì— -추가 ë˜ì§€ ì•Šì•˜ì„ ê²½ìš°, ìš”êµ¬ì‚¬í•­ì˜ ì»¤ë²„ë¦¬ì§€ë¥¼ í‰ê°€í•˜ê¸° 위해 제공ë©ë‹ˆë‹¤. ì´ ê°’ì´ 0 -ì´ë©´ TestLinkì— ë“±ë¡ëœ ìš”êµ¬ì‚¬í•­ì˜ í˜„ìž¬ 개수가 ë§¤íŠ¸ë¦­ì— ì‚¬ìš©ë©ë‹ˆë‹¤.

    -

    예제) SRSì— ëª¨ë‘ 200ê°œì˜ ìš”êµ¬ì‚¬í•­ì´ ìžˆëŠ”ë°, ê·¸ 중 50개만 TestLinkì— ì¶”ê°€ ë˜ì—ˆìŠµë‹ˆë‹¤. +

    ìš”êµ¬ì‚¬í•­ì€ í…ŒìŠ¤íŠ¸ 프로ì íŠ¸ì™€ ê´€ë ¨ëœ '요구사항 명세' ë¬¸ì„œë“¤ì˜ ëª¨ìŒìž…니다.
    +TestLink는 요구사항 명세와 요구사항 ìžì²´ì— 대해 ë²„ì „ì„ ì§€ì›í•˜ì§€ 않습니다. 그래서, +ëª…ì„¸ì˜ ì œëª©ì— ë¬¸ì„œì˜ ë²„ì „ì„ ê¸°ìž¬í•˜ëŠ” ë°©ë²•ì„ ì‚¬ìš©í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤. +사용ìžëŠ” 범위 í•„ë“œì— ê°„ë‹¨í•œ 설명ì´ë‚˜ 노트를 남길 수 있습니다.

    + +

    모든 ìš”êµ¬ì‚¬í•­ì˜ ê°œìˆ˜ëŠ” 모든 ìš”êµ¬ì‚¬í•­ì´ TestLinkì— +추가 ë˜ì§€ ì•Šì•˜ì„ ê²½ìš°, ìš”êµ¬ì‚¬í•­ì˜ ì»¤ë²„ë¦¬ì§€ë¥¼ í‰ê°€í•˜ê¸° 위해 제공ë©ë‹ˆë‹¤. ì´ ê°’ì´ 0 +ì´ë©´ TestLinkì— ë“±ë¡ëœ ìš”êµ¬ì‚¬í•­ì˜ í˜„ìž¬ 개수가 ë§¤íŠ¸ë¦­ì— ì‚¬ìš©ë©ë‹ˆë‹¤.

    +

    예제) SRSì— ëª¨ë‘ 200ê°œì˜ ìš”êµ¬ì‚¬í•­ì´ ìžˆëŠ”ë°, ê·¸ 중 50개만 TestLinkì— ì¶”ê°€ ë˜ì—ˆìŠµë‹ˆë‹¤. ì¶”ê°€ëœ 50ê°œì˜ ìš”êµ¬ì‚¬í•­ì´ ëª¨ë‘ í…ŒìŠ¤íŠ¸ ë˜ì—ˆë‹¤ê³  가정하면, 테스트 커버리지는 25%ê°€ ë©ë‹ˆë‹¤.

    요구사항

    -

    ìƒì„±ëœ 요구사항 ëª…ì„¸ì˜ ì œëª©ì„ í´ë¦­í•˜ì„¸ìš”. ë‹¹ì‹ ì€ ìƒì„±, 편집, ì‚­ì œ, 문서로부터 -요구사항 가져오기를 í•  수 있습니다. ê°ê°ì˜ ìš”êµ¬ì‚¬í•­ì€ ì œëª©, 범위, ìƒíƒœë¥¼ 가집니다. -ìƒíƒœëŠ” \"보통\" ë˜ëŠ” \"테스트 í•  수 ì—†ìŒ\" 중 하나 입니다. 테스트 í•  수 없는 ìš”êµ¬ì‚¬í•­ì€ -매트릭 ê³„ì‚°ì— í¬í•¨ ë˜ì§€ 않습니다. ì´ íŒŒë¼ë¯¸í„°ëŠ” 구현ë˜ì§€ ì•Šì€ ê¸°ëŠ¥ë“¤ê³¼ 잘 못 ì„¤ê³„ëœ -요구사항들ì—ë„ ì‚¬ìš©ë©ë‹ˆë‹¤.

    - -

    ë‹¹ì‹ ì€ ëª…ì„¸ 화면ì—서 ìš”êµ¬ì‚¬í•­ë“¤ì„ ì—¬ëŸ¬ê°œ ì„ íƒí•˜ì—¬ 새로운 테스트 ì¼€ì´ìŠ¤ë“¤ì„ -ìƒì„±í•  수 있습니다. ì´ë ‡ê²Œ ìƒì„±ëœ 테스트 ì¼€ì´ìŠ¤ë“¤ì€ í™˜ê²½ì„¤ì •ì— ì •ì˜ëœ ì´ë¦„ì˜ -테스트 ìŠ¤ìœ„íŠ¸ì— í¬í•¨ë©ë‹ˆë‹¤. (기본값 : $tlCfg->req_cfg->default_testsuite_name = +

    ìƒì„±ëœ 요구사항 ëª…ì„¸ì˜ ì œëª©ì„ í´ë¦­í•˜ì„¸ìš”. ë‹¹ì‹ ì€ ìƒì„±, 편집, ì‚­ì œ, 문서로부터 +요구사항 가져오기를 í•  수 있습니다. ê°ê°ì˜ ìš”êµ¬ì‚¬í•­ì€ ì œëª©, 범위, ìƒíƒœë¥¼ 가집니다. +ìƒíƒœëŠ” \"보통\" ë˜ëŠ” \"테스트 í•  수 ì—†ìŒ\" 중 하나 입니다. 테스트 í•  수 없는 ìš”êµ¬ì‚¬í•­ì€ +매트릭 ê³„ì‚°ì— í¬í•¨ ë˜ì§€ 않습니다. ì´ íŒŒë¼ë¯¸í„°ëŠ” 구현ë˜ì§€ ì•Šì€ ê¸°ëŠ¥ë“¤ê³¼ 잘 못 ì„¤ê³„ëœ +요구사항들ì—ë„ ì‚¬ìš©ë©ë‹ˆë‹¤.

    + +

    ë‹¹ì‹ ì€ ëª…ì„¸ 화면ì—서 ìš”êµ¬ì‚¬í•­ë“¤ì„ ì—¬ëŸ¬ê°œ ì„ íƒí•˜ì—¬ 새로운 테스트 ì¼€ì´ìŠ¤ë“¤ì„ +ìƒì„±í•  수 있습니다. ì´ë ‡ê²Œ ìƒì„±ëœ 테스트 ì¼€ì´ìŠ¤ë“¤ì€ í™˜ê²½ì„¤ì •ì— ì •ì˜ëœ ì´ë¦„ì˜ +테스트 ìŠ¤ìœ„íŠ¸ì— í¬í•¨ë©ë‹ˆë‹¤. (기본값 : $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). 제목과 범위가 테스트 ì¼€ì´ìŠ¤ë¡œ 복사 ë©ë‹ˆë‹¤.

    -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

    'ì‚¬ìš©ìž í•„ë“œ 저장'ì— ê´€ë ¨í•˜ì—¬

    ì‚¬ìš©ìž í•„ë“œë¥¼ ì •ì˜í•˜ì—¬ 테스트 프로ì íŠ¸ì— ì§€ì •í•˜ë ¤ë©´ :
    '테스트 ê³„íš ì„¤ê³„ì— í‘œì‹œ=예'
    '테스트 ê³„íš ì„¤ê³„ì— ì‚¬ìš©=예'
    ì´ ì‚¬ìš©ìž í•„ë“œëŠ” 테스트 계íšì— ì—°ê²°ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ì—게만 표시 ë©ë‹ˆë‹¤. -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/ko_KR/texts.php b/locale/ko_KR/texts.php index 3ce3b5a291..b1577259d2 100644 --- a/locale/ko_KR/texts.php +++ b/locale/ko_KR/texts.php @@ -1,45 +1,44 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * ------------------------------------------------------------------------------------ - * Korean translation - *------------------------------------------------------------------- - * Translated by Jiun PARK - * (DQA Team, OPENTECH INC. R&D Center) - * E-mail : rustyheart@gmail.com - * Issued Date : 2009/05/27 - * - *------------------------------------------------------------------- - */ - -$TLS_htmltext_title['assignReqs'] = "테스트 ì¼€ì´ìŠ¤ì— ìš”êµ¬ì‚¬í•­ 지정하기"; -$TLS_htmltext['assignReqs'] = "

    목ì :

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * ------------------------------------------------------------------------------------ + * Korean translation + *------------------------------------------------------------------- + * Translated by Jiun PARK + * (DQA Team, OPENTECH INC. R&D Center) + * E-mail : rustyheart@gmail.com + * Issued Date : 2009/05/27 + * + *------------------------------------------------------------------- + */ +$TLS_htmltext_title['assignReqs'] = "테스트 ì¼€ì´ìŠ¤ì— ìš”êµ¬ì‚¬í•­ 지정하기"; +$TLS_htmltext['assignReqs'] = "

    목ì :

    사용ìžëŠ” 요구사항들과 테스트 ì¼€ì´ìŠ¤ë“¤ê°„ì˜ ê´€ê³„ë¥¼ 지정할 수 있습니다. 테스트 설계ìžëŠ” 0..nì—서 0..n까지 관계를 지정할 수 있습니다. 즉, í•˜ë‚˜ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ëŠ” ìš”êµ¬ì‚¬í•­ì„ ì§€ì •í•˜ì§€ 않거나, 하나 ì´ìƒì˜ ìš”êµ¬ì‚¬í•­ì„ ì§€ì •í•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. ì´ëŸ° 관계표시는 ìš”êµ¬ì‚¬í•­ì˜ í…ŒìŠ¤íŠ¸ 커버리지를 -조사하거나 테스트하는 ë™ì•ˆ ì–´ë–¤ ê¸°ëŠ¥ì´ ì‹¤íŒ¨ 했는지를 알아 ë‚´ëŠ”ë° ë„ì›€ì„ ì¤ë‹ˆë‹¤. +조사하거나 테스트하는 ë™ì•ˆ ì–´ë–¤ ê¸°ëŠ¥ì´ ì‹¤íŒ¨ 했는지를 알아 ë‚´ëŠ”ë° ë„ì›€ì„ ì¤ë‹ˆë‹¤. ì´ëŸ° ë¶„ì„ì„ í†µí•´ 모든 기대치가 충족 ë˜ì—ˆìŒì„ 확ì¸í•  수 있습니다.

    시작하기:

    @@ -51,47 +50,45 @@ 화면 아랫부분ì—는 테스트 ì¼€ì´ìŠ¤ì™€ ì—°ê²°ë˜ì§€ ì•Šì€ ëª¨ë“  '사용 가능한 요구사항들'ì„ í‘œì‹œ 합니다. 설계ìžëŠ” ì´ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì— ì˜í•´ ë³´ìž¥ì´ ë˜ëŠ” ìš”êµ¬ì‚¬í•­ì„ ì„ íƒí•˜ê³  '지정'ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ ë©ë‹ˆë‹¤. ì´ë ‡ê²Œ ì§€ì •ëœ ìš”êµ¬ì‚¬í•­ë“¤ì€ í™”ë©´ ì¤‘ê°„ë¶€ë¶„ì˜ 'ì§€ì •ëœ ìš”êµ¬ì‚¬í•­ë“¤'ì— í‘œì‹œ ë©ë‹ˆë‹¤. -"; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "테스트 명세"; -$TLS_htmltext['editTc'] = "

    목ì :

    -

    테스트 명세 ì—서 사용ìžëŠ” 모든 테스트 스위트들 ê³¼ 테스트 ì¼€ì´ìŠ¤ë“¤ ì„ " . - "ë³´ê³  편집할 수 있습니다. 테스트 ì¼€ì´ìŠ¤ë“¤ì—는 ë²„ì „ì´ ë©”ê²¨ì ¸ 있고, 모든 ë²„ì „ë“¤ì„ " . - "ì´ ê³³ì—서 관리할 수 있습니다.

    +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "테스트 명세"; +$TLS_htmltext['editTc'] = "

    목ì :

    +

    테스트 명세 ì—서 사용ìžëŠ” 모든 테스트 스위트들 ê³¼ 테스트 ì¼€ì´ìŠ¤ë“¤ ì„ " . + "ë³´ê³  편집할 수 있습니다. 테스트 ì¼€ì´ìŠ¤ë“¤ì—는 ë²„ì „ì´ ë©”ê²¨ì ¸ 있고, 모든 ë²„ì „ë“¤ì„ " . + "ì´ ê³³ì—서 관리할 수 있습니다.

    시작하기:

      -
    1. ì™¼ìª½ì˜ íŠ¸ë¦¬ì—서 ì œì¼ ìœ„ì— ìžˆëŠ” 테스트 프로ì íŠ¸ë¥¼ ì„ íƒí•˜ì„¸ìš”. 참고: " . - "테스트 프로ì íŠ¸ëŠ” 화면 오른쪽 ìœ„ì— ìžˆëŠ” 드롭다운 리스트박스ì—서 언제든지 변경 í•  수 있습니다.
    2. " . - "
    3. 새 테스트 스위트 ë²„íŠ¼ì„ í´ë¦­í•´ì„œ 새로운 테스트 스위트를 ìƒì„±í•©ë‹ˆë‹¤. 테스트 스위트를 사용하여 " . - "ë‹¹ì‹ ì˜ íŽ¸ì˜ì— ë”°ë¼ í…ŒìŠ¤íŠ¸ ë¬¸ì„œì˜ êµ¬ì¡°ë¥¼ 만들 수 있습니다 (기능/비기능 테스트, " . - "제품 구성 ë˜ëŠ” 사양, 변경 요구, 등). 테스트 ìŠ¤ìœ„íŠ¸ì˜ ì„¤ëª…ì—는 테스트 ì¼€ì´ìŠ¤ì˜ ë²”ìœ„, " . - "기본 환경, ê´€ë ¨ë¬¸ì„œì˜ ë§í¬, 제한사항과 다른 유용한 정보를 ë„£ì„ ìˆ˜ 있습니다. " . - "ì¼ë°˜ì ìœ¼ë¡œ, ì„¤ëª…ì˜ ê¸°ìž¬ì‚¬í•­ì€ í•˜ìœ„ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ì— ê³µí†µì ìœ¼ë¡œ ì ìš©ë©ë‹ˆë‹¤." . - "테스트 ì¼€ì´ìŠ¤ëŠ” "í´ë”" 구조를 따릅니다. 그래서 사용ìžëŠ” 테스트 프로ì íŠ¸ë‚´ì—서 " . - "테스트 스위트를 ì´ë™í•˜ê³  복사할 수 있습니다. " . - "ë˜í•œ, 테스트 스위트와 테스트 ì¼€ì´ìŠ¤ë¥¼ 가져오거나 내보낼 수 있습니다.
    4. -
    5. 테스트 스위트는 가변ì ì¸ í´ë”입니다. 테스트 프로ì íЏ ë‚´ì—서 테스트 스위트를 ì´ë™í•˜ê±°ë‚˜ 복사 í•  " . - "수 있습니다. 테스트 스위트는 케스트 ì¼€ì´ìŠ¤ë¥¼ í¬í•¨í•˜ì—¬ 가져오거나 내보낼 수 있습니다. -
    6. 트리메뉴ì—서 새로 ìƒì„±ëœ 테스트 스위트를 ì„ íƒí•˜ê³ , 테스트 ì¼€ì´ìФ ìƒì„± ë²„íŠ¼ì„ í´ë¦­í•´ì„œ 새 " . - "테스트 ì¼€ì´ìŠ¤ë¥¼ 만듭니다. 테스트 ì¼€ì´ìФì—는 테스트 시나리오, ì˜ˆìƒ ê²°ê³¼, 테스트 프로ì íŠ¸ì— " . - "ì •ì˜ëœ ì‚¬ìš©ìž í•„ë“œ(보다 ìžì„¸í•œ 정보는 ì‚¬ìš©ìž ë©”ë‰´ì–¼ì„ ì°¸ê³  하세요)를 기재합니다. " . - "ì¶”ì ì„±ì„ 높ì´ê¸° 위해 키워드 를 지정할 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤.
    7. " . - "
    8. ì™¼ìª½ì˜ íŠ¸ë¦¬ë©”ë‰´ë¥¼ ì´ìš©í•´ 테스트 ì¼€ì´ìŠ¤ë¥¼ íƒìƒ‰í•˜ê³  편집할 수 있습니다. 테스트 ì¼€ì´ìŠ¤ëŠ” ë³€ê²½ëœ ì´ë ¥ì„ 기ë¡í•©ë‹ˆë‹¤.
    9. +
    10. ì™¼ìª½ì˜ íŠ¸ë¦¬ì—서 ì œì¼ ìœ„ì— ìžˆëŠ” 테스트 프로ì íŠ¸ë¥¼ ì„ íƒí•˜ì„¸ìš”. 참고: " . + "테스트 프로ì íŠ¸ëŠ” 화면 오른쪽 ìœ„ì— ìžˆëŠ” 드롭다운 리스트박스ì—서 언제든지 변경 í•  수 있습니다.
    11. " . + "
    12. 새 테스트 스위트 ë²„íŠ¼ì„ í´ë¦­í•´ì„œ 새로운 테스트 스위트를 ìƒì„±í•©ë‹ˆë‹¤. 테스트 스위트를 사용하여 " . + "ë‹¹ì‹ ì˜ íŽ¸ì˜ì— ë”°ë¼ í…ŒìŠ¤íŠ¸ ë¬¸ì„œì˜ êµ¬ì¡°ë¥¼ 만들 수 있습니다 (기능/비기능 테스트, " . + "제품 구성 ë˜ëŠ” 사양, 변경 요구, 등). 테스트 ìŠ¤ìœ„íŠ¸ì˜ ì„¤ëª…ì—는 테스트 ì¼€ì´ìŠ¤ì˜ ë²”ìœ„, " . + "기본 환경, ê´€ë ¨ë¬¸ì„œì˜ ë§í¬, 제한사항과 다른 유용한 정보를 ë„£ì„ ìˆ˜ 있습니다. " . + "ì¼ë°˜ì ìœ¼ë¡œ, ì„¤ëª…ì˜ ê¸°ìž¬ì‚¬í•­ì€ í•˜ìœ„ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ì— ê³µí†µì ìœ¼ë¡œ ì ìš©ë©ë‹ˆë‹¤." . + "테스트 ì¼€ì´ìŠ¤ëŠ” "í´ë”" 구조를 따릅니다. 그래서 사용ìžëŠ” 테스트 프로ì íŠ¸ë‚´ì—서 " . + "테스트 스위트를 ì´ë™í•˜ê³  복사할 수 있습니다. " . + "ë˜í•œ, 테스트 스위트와 테스트 ì¼€ì´ìŠ¤ë¥¼ 가져오거나 내보낼 수 있습니다.
    13. +
    14. 테스트 스위트는 가변ì ì¸ í´ë”입니다. 테스트 프로ì íЏ ë‚´ì—서 테스트 스위트를 ì´ë™í•˜ê±°ë‚˜ 복사 í•  " . + "수 있습니다. 테스트 스위트는 케스트 ì¼€ì´ìŠ¤ë¥¼ í¬í•¨í•˜ì—¬ 가져오거나 내보낼 수 있습니다. +
    15. 트리메뉴ì—서 새로 ìƒì„±ëœ 테스트 스위트를 ì„ íƒí•˜ê³ , 테스트 ì¼€ì´ìФ ìƒì„± ë²„íŠ¼ì„ í´ë¦­í•´ì„œ 새 " . + "테스트 ì¼€ì´ìŠ¤ë¥¼ 만듭니다. 테스트 ì¼€ì´ìФì—는 테스트 시나리오, ì˜ˆìƒ ê²°ê³¼, 테스트 프로ì íŠ¸ì— " . + "ì •ì˜ëœ ì‚¬ìš©ìž í•„ë“œ(보다 ìžì„¸í•œ 정보는 ì‚¬ìš©ìž ë©”ë‰´ì–¼ì„ ì°¸ê³  하세요)를 기재합니다. " . + "ì¶”ì ì„±ì„ 높ì´ê¸° 위해 키워드 를 지정할 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤.
    16. " . + "
    17. ì™¼ìª½ì˜ íŠ¸ë¦¬ë©”ë‰´ë¥¼ ì´ìš©í•´ 테스트 ì¼€ì´ìŠ¤ë¥¼ íƒìƒ‰í•˜ê³  편집할 수 있습니다. 테스트 ì¼€ì´ìŠ¤ëŠ” ë³€ê²½ëœ ì´ë ¥ì„ 기ë¡í•©ë‹ˆë‹¤.
    18. 테스트 ì¼€ì´ìŠ¤ë“¤ì´ ì¤€ë¹„ ë˜ë©´, 만들어진 테스트 명세를 테스트 ê³„íš ì— ì§€ì •í•©ë‹ˆë‹¤.
    19. + \"javascript:open_help_window('glossary','$locale');\">테스트 ê³„íš ì— ì§€ì •í•©ë‹ˆë‹¤.
    -

    TestLink를 사용하여 테스트 ì¼€ì´ìŠ¤ë“¤ì„ í…ŒìŠ¤íŠ¸ 스위트로 정리할 수 있습니다." . -"테스트 스위트는 다른 테스트 스위트와 연계 ë  ìˆ˜ 있으며, 계층ì ì¸ 구조로 만들 수 있습니다. - ë‹¹ì‹ ì€ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì™€ 함께 테스트 스위트 ì •ë³´ë„ ê°™ì´ ì¸ì‡„ í•  수 있습니다.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "테스트 ì¼€ì´ìФ 찾기"; -$TLS_htmltext['searchTc'] = "

    목ì :

    +

    TestLink를 사용하여 테스트 ì¼€ì´ìŠ¤ë“¤ì„ í…ŒìŠ¤íŠ¸ 스위트로 정리할 수 있습니다." . + "테스트 스위트는 다른 테스트 스위트와 연계 ë  ìˆ˜ 있으며, 계층ì ì¸ 구조로 만들 수 있습니다. + ë‹¹ì‹ ì€ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì™€ 함께 테스트 스위트 ì •ë³´ë„ ê°™ì´ ì¸ì‡„ í•  수 있습니다.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "테스트 ì¼€ì´ìФ 찾기"; +$TLS_htmltext['searchTc'] = "

    목ì :

    키워드나 문ìžì—´ì´ í¬í•¨ëœ 테스트 ì¼€ì´ìŠ¤ë¥¼ 찾습니다. ì°¾ì„ ë•Œ 대소문ìžë¥¼ 구별하지 않습니다. 검색결과는 ì´ í…ŒìŠ¤íŠ¸ 프로ì íŠ¸ì— í¬í•¨ëœ 테스트 ì¼€ì´ìŠ¤ë§Œ ë³´ì—¬ ì¤ë‹ˆë‹¤.

    @@ -103,21 +100,20 @@
  • 키워드를 ì„ íƒí•˜ê±°ë‚˜ '사용 안함' 으로 내버려 둡니다.
  • ì°¾ê¸°ë²„íŠ¼ì„ í´ë¦­í•©ë‹ˆë‹¤.
  • 해당하는 테스트 ì¼€ì´ìŠ¤ë“¤ì´ í‘œì‹œë©ë‹ˆë‹¤. ì œëª©ì„ í´ë¦­í•˜ë©´ 편집할 수 있습니다.
  • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "테스트 명세 ì¸ì‡„하기"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

    목ì :

    -

    ì´ í™”ë©´ì—서 í•˜ë‚˜ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФ, 테스트 ìŠ¤ìœ„íŠ¸ì˜ ëª¨ë“  테스트 ì¼€ì´ìФ, 테스트 프로ì íŠ¸ë‚˜ +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "테스트 명세 ì¸ì‡„하기"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

    목ì :

    +

    ì´ í™”ë©´ì—서 í•˜ë‚˜ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФ, 테스트 ìŠ¤ìœ„íŠ¸ì˜ ëª¨ë“  테스트 ì¼€ì´ìФ, 테스트 프로ì íŠ¸ë‚˜ 테스트 계íšì˜ 모든 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ì¸ì‡„í•  수 있습니다.

    시작하기:

    1. -

      표시를 ì›í•˜ëŠ” ë¶€ë¶„ì„ ì˜µì…˜ì—서 ì„ íƒí•˜ê³  난 후, 테스트 ì¼€ì´ìŠ¤ë‚˜ 테스트 스위트 í˜¹ì€ +

      표시를 ì›í•˜ëŠ” ë¶€ë¶„ì„ ì˜µì…˜ì—서 ì„ íƒí•˜ê³  난 후, 테스트 ì¼€ì´ìŠ¤ë‚˜ 테스트 스위트 í˜¹ì€ í…ŒìŠ¤íŠ¸ 프로ì íŠ¸ë¥¼ ì„ íƒí•©ë‹ˆë‹¤. 그러면 ì¸ì‡„í•  수 있는 í™”ë©´ì´ ë‚˜íƒ€ë‚©ë‹ˆë‹¤.

    2. -
    3. 문서 ì˜µì…˜ì˜ \"문서 형ì‹\" ì—서 HTML, OpenOffice Writer ë˜ëŠ” MS Word 문서 중 ì›í•˜ëŠ” +

    4. 문서 ì˜µì…˜ì˜ \"문서 형ì‹\" ì—서 HTML, OpenOffice Writer ë˜ëŠ” MS Word 문서 중 ì›í•˜ëŠ” 형ì‹ì„ ì„ íƒ í•©ë‹ˆë‹¤. 보다 ìžì„¸í•œ 정보는 ë„ì›€ë§ ì„ ì°¸ê³  하세요.

      @@ -125,12 +121,11 @@
    5. í™”ë©´ì— ë‚˜íƒ€ë‚œ 정보를 브ë¼ìš°ì €ì˜ ì¸ì‡„ ê¸°ëŠ¥ì„ ì‚¬ìš©í•˜ì—¬ 실제 ì¸ì‡„하세요.
      노트: 오른쪽 화면만 출력하ë„ë¡ ì„¤ì • 하시기 ë°”ëžë‹ˆë‹¤.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "요구사항 명세 설계하기"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

    ë‹¹ì‹ ì€ ìš”êµ¬ì‚¬í•­ 명세서를 관리할 수 있습니다.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "요구사항 명세 설계하기"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

    ë‹¹ì‹ ì€ ìš”êµ¬ì‚¬í•­ 명세서를 관리할 수 있습니다.

    요구사항 명세

    @@ -139,7 +134,7 @@ 그래서, ëª…ì„¸ì˜ ì œëª© ë’¤ì— ë¬¸ì„œì˜ ë²„ì „ì„ ê¸°ìž¬ í•  수 있습니다. 사용ìžëŠ” 간단한 설명ì´ë‚˜ 노트를 범위 í•„ë“œì— ê¸°ìž¬í•  수 있습니다.

    -

    모든 ìš”êµ¬ì‚¬í•­ì˜ ê°œìˆ˜ëŠ” 모든 ìš”êµ¬ì‚¬í•­ì´ +

    모든 ìš”êµ¬ì‚¬í•­ì˜ ê°œìˆ˜ëŠ” 모든 ìš”êµ¬ì‚¬í•­ì´ TestLinkì— ì¶”ê°€ ë˜ì§€ ì•Šì•˜ì„ ê²½ìš°, ìš”êµ¬ì‚¬í•­ì˜ ì»¤ë²„ë¦¬ì§€ë¥¼ í‰ê°€í•˜ê¸° 위해 제공ë©ë‹ˆë‹¤. ì´ ê°’ì´ 0ì´ë©´ TestLinkì— ë“±ë¡ëœ ìš”êµ¬ì‚¬í•­ì˜ í˜„ìž¬ 개수가 ë§¤íŠ¸ë¦­ì— ì‚¬ìš©ë©ë‹ˆë‹¤.

    예제) SRSì— ëª¨ë‘ 200ê°œì˜ ìš”êµ¬ì‚¬í•­ì´ ìžˆëŠ”ë°, ê·¸ 중 50개만 TestLinkì— ì¶”ê°€ ë˜ì—ˆìŠµë‹ˆë‹¤. @@ -148,51 +143,49 @@

    요구사항

    -

    ì™¼ìª½ì˜ íŠ¸ë¦¬ì—서 요구사항 ëª…ì„¸ì˜ ì œëª©ì„ í´ë¦­í•˜ì„¸ìš”. 만약 등ë¡ëœ 요구사항 명세가 없으면, " . -"프로ì íЏ ì´ë¦„ì„ í´ë¦­í•´ì„œ ìƒì„± 하세요. ë‹¹ì‹ ì€ ìš”êµ¬ì‚¬í•­ì„ ìƒì„±, 편집, 삭제하거나 +

    ì™¼ìª½ì˜ íŠ¸ë¦¬ì—서 요구사항 ëª…ì„¸ì˜ ì œëª©ì„ í´ë¦­í•˜ì„¸ìš”. 만약 등ë¡ëœ 요구사항 명세가 없으면, " . + "프로ì íЏ ì´ë¦„ì„ í´ë¦­í•´ì„œ ìƒì„± 하세요. ë‹¹ì‹ ì€ ìš”êµ¬ì‚¬í•­ì„ ìƒì„±, 편집, 삭제하거나 문서로부터 가져올 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. ê°ê°ì˜ ìš”êµ¬ì‚¬í•­ì€ ì œëª©, 범위, ìƒíƒœ 값으로 ì´ë£¨ì–´ì ¸ 있습니다. -ìƒíƒœëŠ” '보통' ë˜ëŠ” '테스트 í•  수 ì—†ìŒ' 중 하나가 ë©ë‹ˆë‹¤. 테스트 í•  수 없는 ìš”êµ¬ì‚¬í•­ë“¤ì€ ë§¤íŠ¸ë¦­ì— +ìƒíƒœëŠ” '보통' ë˜ëŠ” '테스트 í•  수 ì—†ìŒ' 중 하나가 ë©ë‹ˆë‹¤. 테스트 í•  수 없는 ìš”êµ¬ì‚¬í•­ë“¤ì€ ë§¤íŠ¸ë¦­ì— í¬í•¨ë˜ì§€ 않습니다. 구현할 수 없는 기능ì´ë‚˜ 잘 못 ì„¤ê³„ëœ ìš”êµ¬ì‚¬í•­ë“¤ì„ '테스트 í•  수 ì—†ìŒ'으로 표시 하면 ë©ë‹ˆë‹¤.

    ë‹¹ì‹ ì€ ëª…ì„¸ 화면ì—서 ìš”êµ¬ì‚¬í•­ë“¤ì„ ì—¬ëŸ¬ê°œ ì„ íƒí•˜ì—¬ 새로운 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ìƒì„±í•  수 있습니다. ì´ë ‡ê²Œ ìƒì„±ëœ 테스트 ì¼€ì´ìŠ¤ë“¤ì€ í™˜ê²½ì„¤ì •ì— ì •ì˜ëœ ì´ë¦„ì˜ í…ŒìŠ¤íŠ¸ ìŠ¤ìœ„íŠ¸ì— í¬í•¨ë©ë‹ˆë‹¤. (기본값 : \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). 제목과 범위가 테스트 ì¼€ì´ìŠ¤ë¡œ 복사 ë©ë‹ˆë‹¤.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "키워드 지정하기"; -$TLS_htmltext['keywordsAssign'] = "

    목ì :

    -

    키워드 지정 í™”ë©´ì€ í…ŒìŠ¤íŠ¸ 스위트나 테스트 ì¼€ì´ìŠ¤ì— í‚¤ì›Œë“œë¥¼ +'Test suite created by Requirement - Auto';). 제목과 범위가 테스트 ì¼€ì´ìŠ¤ë¡œ 복사 ë©ë‹ˆë‹¤.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "키워드 지정하기"; +$TLS_htmltext['keywordsAssign'] = "

    목ì :

    +

    키워드 지정 í™”ë©´ì€ í…ŒìŠ¤íŠ¸ 스위트나 테스트 ì¼€ì´ìŠ¤ì— í‚¤ì›Œë“œë¥¼ ì¼ê´„ì ìœ¼ë¡œ 지정할 수 있는 곳입니다.

    키워드 지정하기:

    1. ì™¼ìª½ì˜ íŠ¸ë¦¬ì—서 테스트 스위트나 테스트 ì¼€ì´ìŠ¤ë¥¼ ì„ íƒí•©ë‹ˆë‹¤.
    2. -
    3. 오른쪽 í™”ë©´ì˜ ì œì¼ ìœ—ë¶€ë¶„ì—서 테스트 ì¼€ì´ìŠ¤ì— ì‚¬ìš©ê°€ëŠ¥í•œ 키워드를 +
    4. 오른쪽 í™”ë©´ì˜ ì œì¼ ìœ—ë¶€ë¶„ì—서 테스트 ì¼€ì´ìŠ¤ì— ì‚¬ìš©ê°€ëŠ¥í•œ 키워드를 지정할 수 있습니다.
    5. 아래 ì„ íƒí™”ë©´ì—서 보다 세부ì ìœ¼ë¡œ 지정할 수 있습니다.

    테스트 계íšì—서 키워드 ì§€ì •ì— ëŒ€í•œ 중요한 ì •ë³´:

    ëª…ì„¸ì— ëŒ€í•œ 키워드 ì§€ì •ì€ í…ŒìŠ¤íŠ¸ 계íšì— í¬í•¨ëœ 최신 ë²„ì „ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФì—ë§Œ ì˜í–¥ì„ 미칩니다. -만약 테스트 계íšì— ì´ì „ ë²„ì „ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ê°€ í¬í•¨ë˜ì–´ 있으면, ë‹¹ì‹ ì´ ì§€ì •í•œ 키워드가 +만약 테스트 계íšì— ì´ì „ ë²„ì „ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ê°€ í¬í•¨ë˜ì–´ 있으면, ë‹¹ì‹ ì´ ì§€ì •í•œ 키워드가 테스트 계íšì— 표시 ë˜ì§€ ì•Šì„ ê²ƒìž…ë‹ˆë‹¤.

    -

    TestLinkê°€ ì´ëŸ° ì ‘ê·¼ë°©ë²•ì„ ì‚¬ìš©í•˜ê¸° 때문ì—, 테스트 ì¼€ì´ìŠ¤ì— ìžˆëŠ” ì´ì „ 버전 테스트 ì¼€ì´ìŠ¤ë“¤ì€ +

    TestLinkê°€ ì´ëŸ° ì ‘ê·¼ë°©ë²•ì„ ì‚¬ìš©í•˜ê¸° 때문ì—, 테스트 ì¼€ì´ìŠ¤ì— ìžˆëŠ” ì´ì „ 버전 테스트 ì¼€ì´ìŠ¤ë“¤ì€ ê°€ìž¥ 최근 ë²„ì „ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì— ì§€ì •ëœ í‚¤ì›Œë“œê°€ 지정 ë  ìˆ˜ 없습니다. 테스트 계íšì˜ 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ì—…ë°ì´íЏ 하려면, '테스트 ì¼€ì´ìФ 버전 업그레ì´ë“œ'를 ì´ìš©í•˜ì—¬ ì—…ë°ì´íЏ 후 키워드 ì§€ì •ì„ ì‚¬ìš©í•˜ì‹œê¸° -ë°”ëžë‹ˆë‹¤.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "테스트 ì¼€ì´ìФ 실행"; -$TLS_htmltext['executeTest'] = "

    목ì :

    +ë°”ëžë‹ˆë‹¤.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "테스트 ì¼€ì´ìФ 실행"; +$TLS_htmltext['executeTest'] = "

    목ì :

    사용ìžê°€ 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ì‹¤í–‰í•˜ëŠ” 곳입니다. 사용ìžëŠ” ì„ íƒëœ ë¹Œë“œì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì— ì‹¤í–‰ê²°ê³¼ë¥¼ -지정할 수 있습니다. 필터와 ì„¤ì •ì— ëŒ€í•œ ìžì„¸í•œ ì‚¬í•­ì€ ë„움ë§ì„ 참고 하세요 " . - "(물ìŒí‘œ ì•„ì´ì½˜ì„ í´ë¦­í•˜ì„¸ìš”).

    +지정할 수 있습니다. 필터와 ì„¤ì •ì— ëŒ€í•œ ìžì„¸í•œ ì‚¬í•­ì€ ë„움ë§ì„ 참고 하세요 " . + "(물ìŒí‘œ ì•„ì´ì½˜ì„ í´ë¦­í•˜ì„¸ìš”).

    시작하기:

    @@ -203,13 +196,13 @@
  • 테스트 결과와 실행 노트 ë˜ëŠ” 버그를 기ë¡í•©ë‹ˆë‹¤.
  • 결과를 저장합니다.
  • -

    노트: GUIì—서 ì§ì ‘ 버그를 ìƒì„±/ì¶”ì í•˜ë ¤ë©´, TestLinkê°€ 버그 ì¶”ì  ì‹œìŠ¤í…œê³¼ ì—°ë™ ë˜ë„ë¡ -설정ë˜ì–´ 있어야 합니다.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "테스트 보고서와 ë§¤íŠ¸ë¦­ì— ëŒ€í•œ 설명"; -$TLS_htmltext['showMetrics'] = "

    네티게ì´í„°ì˜ ìœ—ë¶€ë¶„ì— ì„ íƒëœ " . - "테스트 계íšì— 대한 보고서들 입니다. 현재 실행하는 테스트 ê³„íš ë§ê³  다른 테스트 계íšì„ ì„ íƒí•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. +

    노트: GUIì—서 ì§ì ‘ 버그를 ìƒì„±/ì¶”ì í•˜ë ¤ë©´, TestLinkê°€ 버그 ì¶”ì  ì‹œìŠ¤í…œê³¼ ì—°ë™ ë˜ë„ë¡ +설정ë˜ì–´ 있어야 합니다.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "테스트 보고서와 ë§¤íŠ¸ë¦­ì— ëŒ€í•œ 설명"; +$TLS_htmltext['showMetrics'] = "

    네티게ì´í„°ì˜ ìœ—ë¶€ë¶„ì— ì„ íƒëœ " . + "테스트 계íšì— 대한 보고서들 입니다. 현재 실행하는 테스트 ê³„íš ë§ê³  다른 테스트 계íšì„ ì„ íƒí•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. 다ìŒê³¼ ê°™ì€ ë³´ê³ ì„œ 종류를 ì„ íƒí•  수 있습니다:

    • HTML - 웹페ì´ì§€ë¡œ 표시ë˜ëŠ” 보고서
    • @@ -231,17 +224,17 @@ 여기ì—는 테스트 ì¼€ì´ìŠ¤ë“¤ì˜ ê²°ê³¼ë„ í‘œì‹œí•  수 있습니다.

      ì¼ë°˜ì ì¸ 테스트 계íšì˜ 매트릭

      -

      ì´ íŽ˜ì´ì§€ì—서 테스트 계íšì˜ 가장 최근 ìƒíƒœë¥¼ 테스트 스위트별, 담당ìžë³„, 키워드별로 ë³¼ 수 있습니다. -'현재 ìƒíƒœ'는 가장 최근 ë¹Œë“œì˜ ì‹¤í–‰ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ì— ì˜í•´ ê²°ì • ë©ë‹ˆë‹¤. 예를 들어, 테스트 ì¼€ì´ìŠ¤ê°€ +

      ì´ íŽ˜ì´ì§€ì—서 테스트 계íšì˜ 가장 최근 ìƒíƒœë¥¼ 테스트 스위트별, 담당ìžë³„, 키워드별로 ë³¼ 수 있습니다. +'현재 ìƒíƒœ'는 가장 최근 ë¹Œë“œì˜ ì‹¤í–‰ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë“¤ì— ì˜í•´ ê²°ì • ë©ë‹ˆë‹¤. 예를 들어, 테스트 ì¼€ì´ìŠ¤ê°€ 여러 ë¹Œë“œë“¤ì— ê±¸ì³ ì‹¤í–‰ ë˜ì—ˆì„ 경우, 가장 마지막 결과만 가져 오게 ë©ë‹ˆë‹¤.

      '마지막 테스트 ê²°ê³¼'는 ë§Žì€ ë³´ê³ ì„œë“¤ì—서 사용ë˜ëŠ” ê°œë…으로, 아래와 ê°™ì´ ì •ì˜ ë©ë‹ˆë‹¤ :

        -
      • 테스트 계íšì— ì¶”ê°€ëœ ë¹Œë“œì˜ ìˆœì„œê°€ 가장 최근 빌드를 결정합니다. 가장 최근 ë¹Œë“œì˜ ê²°ê³¼ê°€ 모든 ì´ì „ -ë¹Œë“œë“¤ì˜ ê²°ê³¼ê°’ì„ ëŒ€í‘œí•©ë‹ˆë‹¤. 예를 들면, 빌드 1ì—서는 '실패'로 íŒì •하고, 빌드 2ì—서는 '통과'로 íŒì • 했다면 +
      • 테스트 계íšì— ì¶”ê°€ëœ ë¹Œë“œì˜ ìˆœì„œê°€ 가장 최근 빌드를 결정합니다. 가장 최근 ë¹Œë“œì˜ ê²°ê³¼ê°€ 모든 ì´ì „ +ë¹Œë“œë“¤ì˜ ê²°ê³¼ê°’ì„ ëŒ€í‘œí•©ë‹ˆë‹¤. 예를 들면, 빌드 1ì—서는 '실패'로 íŒì •하고, 빌드 2ì—서는 '통과'로 íŒì • 했다면 최종 결과는 '통과'ê°€ ë©ë‹ˆë‹¤.
      • -
      • 만약 테스트 ì¼€ì´ìŠ¤ê°€ ê°™ì€ ë¹Œë“œì—서 여러번 ì‹¤í–‰ë  ê²½ìš°, 가장 ìµœê·¼ì˜ ì‹¤í–‰ê²°ê³¼ ê°’ì„ ì‚¬ìš©í•©ë‹ˆë‹¤. -예를 들면, 빌드 3ì´ ì—¬ëŸ¬ë¶„ì˜ íŒ€ì— ë¦´ë¦¬ì¦ˆê°€ ë˜ê³  테스터 1ì´ ì˜¤í›„2ì‹œì— '통과' íŒì •ì„ í•˜ê³ , 테스터 2ê°€ +
      • 만약 테스트 ì¼€ì´ìŠ¤ê°€ ê°™ì€ ë¹Œë“œì—서 여러번 ì‹¤í–‰ë  ê²½ìš°, 가장 ìµœê·¼ì˜ ì‹¤í–‰ê²°ê³¼ ê°’ì„ ì‚¬ìš©í•©ë‹ˆë‹¤. +예를 들면, 빌드 3ì´ ì—¬ëŸ¬ë¶„ì˜ íŒ€ì— ë¦´ë¦¬ì¦ˆê°€ ë˜ê³  테스터 1ì´ ì˜¤í›„2ì‹œì— '통과' íŒì •ì„ í•˜ê³ , 테스터 2ê°€ 오후3ì‹œì— '실패' íŒì •ì„ í•˜ë©´, 최종 결과는 '실패'ê°€ ë©ë‹ˆë‹¤.
      • 빌드ì—서 '실행 안함'ìƒíƒœì¸ 테스트 ì¼€ì´ìŠ¤ëŠ” ê²°ê³¼ ê³„ì‚°ì— í¬í•¨ë˜ì§€ 않습니다. 예를 들면, 빌드 1ì—서 '통과'로 íŒì •하고, 빌드 2ì—서 실행하지 않으면 최종 결과는 '통과'ê°€ ë©ë‹ˆë‹¤.
      • @@ -260,36 +253,36 @@

      모든 빌드 현황

      -

      모든 ë¹Œë“œì˜ ì‹¤í–‰ê²°ê³¼ë¥¼ 표시합니다. ê°ê°ì˜ ë¹Œë“œì— ëŒ€í•´, TC합계, 통과 합계, 통과 %, 실패 합계, -실패 %, 중단 합계, 중단 %, 실행안함 합계, 실행안함 %를 표시합니다. 만약 테스트 ì¼€ì´ìŠ¤ê°€ ê°™ì€ ë¹Œë“œì—서 +

      모든 ë¹Œë“œì˜ ì‹¤í–‰ê²°ê³¼ë¥¼ 표시합니다. ê°ê°ì˜ ë¹Œë“œì— ëŒ€í•´, TC합계, 통과 합계, 통과 %, 실패 합계, +실패 %, 중단 합계, 중단 %, 실행안함 합계, 실행안함 %를 표시합니다. 만약 테스트 ì¼€ì´ìŠ¤ê°€ ê°™ì€ ë¹Œë“œì—서 ë‘번 실행ë˜ë©´, 가장 ìµœê·¼ì— ì‹¤í–‰í•œ ê²°ê³¼ê°’ì´ ê³„ì‚°ì— ì‚¬ìš© ë©ë‹ˆë‹¤.

      매트릭 질ì˜

      -

      ì´ ë³´ê³ ì„œëŠ” ì§ˆì˜ í¼ í™”ë©´ê³¼ ì¿¼ë¦¬ëœ ìžë£Œë¥¼ í¬í•¨í•œ ì§ˆì˜ ê²°ê³¼ 화면으로 구성 ë˜ì–´ 있습니다. -ì§ˆì˜ í¼ í™”ë©´ì—는 4ê°œì˜ ì»¨íŠ¸ë¡¤ë¡œ ëœ ì§ˆì˜ í™”ë©´ì´ í‘œì‹œë©ë‹ˆë‹¤. ê°ê°ì˜ ì»¨íŠ¸ë¡¤ì€ ìµœëŒ€í•œ ë§Žì€ ë¹Œë“œì™€ -테스트 ì¼€ì´ë“¤ì„ 검색할 수 있는 값으로 초기화 ë˜ì–´ 있습니다. 사용ìžê°€ ì»¨íŠ¸ë¡¤ì˜ ê°’ì„ ë³€ê²½í•˜ì—¬, +

      ì´ ë³´ê³ ì„œëŠ” ì§ˆì˜ í¼ í™”ë©´ê³¼ ì¿¼ë¦¬ëœ ìžë£Œë¥¼ í¬í•¨í•œ ì§ˆì˜ ê²°ê³¼ 화면으로 구성 ë˜ì–´ 있습니다. +ì§ˆì˜ í¼ í™”ë©´ì—는 4ê°œì˜ ì»¨íŠ¸ë¡¤ë¡œ ëœ ì§ˆì˜ í™”ë©´ì´ í‘œì‹œë©ë‹ˆë‹¤. ê°ê°ì˜ ì»¨íŠ¸ë¡¤ì€ ìµœëŒ€í•œ ë§Žì€ ë¹Œë“œì™€ +테스트 ì¼€ì´ë“¤ì„ 검색할 수 있는 값으로 초기화 ë˜ì–´ 있습니다. 사용ìžê°€ ì»¨íŠ¸ë¡¤ì˜ ê°’ì„ ë³€ê²½í•˜ì—¬, 특정 테스터, 키워드, 스위트, 빌드를 조합하여 보고서를 만들 수 있습니다.

        -
      • 키워드 0~1ê°œì˜ í‚¤ì›Œë“œë“¤ì„ ì„ íƒí•  수 있습니다. ê¸°ë³¸ê°’ì€ ì•„ë¬´ í‚¤ì›Œë“œë„ ì„ íƒí•˜ì§€ ì•Šì€ ê²ƒìž…ë‹ˆë‹¤. -만약 키워드가 지정ë˜ì§€ 않으면, 키워드 ì§€ì •ì— ìƒê´€ ì—†ì´ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ê°€ 관련 ë©ë‹ˆë‹¤. 키워드는 -테스트 명세나 키워드 관리 화면ì—서 지정할 수 있습니다. 테스트 ì¼€ì´ìŠ¤ì— í‚¤ì›Œë“œë¥¼ 지정하는 ê²ƒì€ ëª¨ë“  -테스트 계íšë“¤ê³¼ 테스트 ì¼€ì´ìŠ¤ì˜ ëª¨ë“  ë²„ì „ë“¤ì— í•´ë‹¹ ë©ë‹ˆë‹¤. 특정 í‚¤ì›Œë“œì— ëŒ€í•œ ê²°ê³¼ì— í¥ë¯¸ê°€ 있다면 +
      • 키워드 0~1ê°œì˜ í‚¤ì›Œë“œë“¤ì„ ì„ íƒí•  수 있습니다. ê¸°ë³¸ê°’ì€ ì•„ë¬´ í‚¤ì›Œë“œë„ ì„ íƒí•˜ì§€ ì•Šì€ ê²ƒìž…ë‹ˆë‹¤. +만약 키워드가 지정ë˜ì§€ 않으면, 키워드 ì§€ì •ì— ìƒê´€ ì—†ì´ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ê°€ 관련 ë©ë‹ˆë‹¤. 키워드는 +테스트 명세나 키워드 관리 화면ì—서 지정할 수 있습니다. 테스트 ì¼€ì´ìŠ¤ì— í‚¤ì›Œë“œë¥¼ 지정하는 ê²ƒì€ ëª¨ë“  +테스트 계íšë“¤ê³¼ 테스트 ì¼€ì´ìŠ¤ì˜ ëª¨ë“  ë²„ì „ë“¤ì— í•´ë‹¹ ë©ë‹ˆë‹¤. 특정 í‚¤ì›Œë“œì— ëŒ€í•œ ê²°ê³¼ì— í¥ë¯¸ê°€ 있다면 ì´ ì»¨íŠ¸ë¡¤ì„ ì„¤ì •í•˜ë©´ ë©ë‹ˆë‹¤.
      • -
      • ë‹´ë‹¹ìž 0~1ëª…ì˜ ë‹´ë‹¹ìžë¥¼ ì„ íƒí•  수 있습니다. ê¸°ë³¸ê°’ì€ ì•„ë¬´ë„ ì„ íƒí•˜ì§€ ì•Šì€ ê²ƒ 입니다. -담당ìžê°€ 지정ë˜ì§€ 않으면, ë‹´ë‹¹ìž ì§€ì •ì— ìƒê´€ ì—†ì´ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ê°€ 관련 ë©ë‹ˆë‹¤. 현재 '지정안ë¨'ì¸ -테스트 ì¼€ì´ìŠ¤ë¥¼ 검색할 수 있는 ë°©ë²•ì€ ì—†ìŠµë‹ˆë‹¤. 담당ìžëŠ” '테스트 ì¼€ì´ìФ 실행 지정' 화면ì—서 지정할 수 있으며, +
      • ë‹´ë‹¹ìž 0~1ëª…ì˜ ë‹´ë‹¹ìžë¥¼ ì„ íƒí•  수 있습니다. ê¸°ë³¸ê°’ì€ ì•„ë¬´ë„ ì„ íƒí•˜ì§€ ì•Šì€ ê²ƒ 입니다. +담당ìžê°€ 지정ë˜ì§€ 않으면, ë‹´ë‹¹ìž ì§€ì •ì— ìƒê´€ ì—†ì´ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ê°€ 관련 ë©ë‹ˆë‹¤. 현재 '지정안ë¨'ì¸ +테스트 ì¼€ì´ìŠ¤ë¥¼ 검색할 수 있는 ë°©ë²•ì€ ì—†ìŠµë‹ˆë‹¤. 담당ìžëŠ” '테스트 ì¼€ì´ìФ 실행 지정' 화면ì—서 지정할 수 있으며, 테스트 계íšì˜ 기본요소가 ë©ë‹ˆë‹¤. 만약 특정 담당ìžê°€ 실행한 ê²°ê³¼ì— í¥ë¯¸ê°€ 있다면 ì´ ì»¨íŠ¸ë¡¤ì„ ì„¤ì •í•˜ë©´ ë©ë‹ˆë‹¤.
      • 최ìƒìœ„ 스위트 0~nê°œì˜ ìµœìƒìœ„ 스위트를 ì„ íƒí•  수 있습니다. ê¸°ë³¸ê°’ì€ ëª¨ë“  스위트를 ì„ íƒí•˜ëŠ” 것입니다. -ì„ íƒí•œ 스위트들만 ê²°ê³¼ ë§¤íŠ¸ë¦­ì„ ì ˆì˜í•  때 사용합니다. 특정 ìŠ¤ìœ„íŠ¸ì— ëŒ€í•œ ê²°ê³¼ì— í¥ë¯¸ê°€ 있다면 +ì„ íƒí•œ 스위트들만 ê²°ê³¼ ë§¤íŠ¸ë¦­ì„ ì ˆì˜í•  때 사용합니다. 특정 ìŠ¤ìœ„íŠ¸ì— ëŒ€í•œ ê²°ê³¼ì— í¥ë¯¸ê°€ 있다면 ì´ ì»¨íŠ¸ë¡¤ì„ ì„¤ì •í•˜ë©´ ë©ë‹ˆë‹¤.
      • -
      • 빌드 1~nê°œì˜ ë¹Œë“œë¥¼ ì„ íƒí•  수 있습니다. ê¸°ë³¸ê°’ì€ ëª¨ë“  빌드를 ì„ íƒí•˜ëŠ” 것입니다. ì„ íƒí•œ 빌드ì—서 -ìˆ˜í–‰ëœ ê²°ê³¼ë§Œ ë§¤íŠ¸ë¦­ì„ ìƒì„±í•˜ëŠ”ë° ì‚¬ìš© ë©ë‹ˆë‹¤. 예를 들어, 마지막 3ê°œì˜ ë¹Œë“œì—서 ëª‡ê°œì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ê°€ 수행 +
      • 빌드 1~nê°œì˜ ë¹Œë“œë¥¼ ì„ íƒí•  수 있습니다. ê¸°ë³¸ê°’ì€ ëª¨ë“  빌드를 ì„ íƒí•˜ëŠ” 것입니다. ì„ íƒí•œ 빌드ì—서 +ìˆ˜í–‰ëœ ê²°ê³¼ë§Œ ë§¤íŠ¸ë¦­ì„ ìƒì„±í•˜ëŠ”ë° ì‚¬ìš© ë©ë‹ˆë‹¤. 예를 들어, 마지막 3ê°œì˜ ë¹Œë“œì—서 ëª‡ê°œì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ê°€ 수행 ë˜ì—ˆëŠ”ì§€ë¥¼ 보려면, ì´ ì»¨íŠ¸ë¡¤ì„ ì„¤ì •í•˜ì‹œë©´ ë©ë‹ˆë‹¤. -키워드, 담당ìž, 최ìƒìœ„ 스위트를 지정하는 ê²ƒì€ ìŠ¤ìœ„íŠ¸ë³„, 테스트 계íšë³„ ë§¤íŠ¸ë¦­ì„ ê³„ì‚° í•˜ëŠ”ë° ì‚¬ìš©í•˜ëŠ” 테스트 ì¼€ì´ìŠ¤ë“¤ì˜ -개수를 지정하는 것입니다. 예를 들어서, 담당ìžëŠ” 'Greg', 키워드는 'Priority 1', 그리고 모든 가능한 스위트를 지정했다면, -Gregì—게 ì§€ì •ëœ Priority 1ì¸ ê²ƒë“¤ì´ ì‚¬ìš© ë©ë‹ˆë‹¤. '# 테스트 ì¼€ì´ìФ'는 ì´ëŸ° 3가지 ì»¨íŠ¸ë¡¤ì— ì˜í•œ 테스트 ì¼€ì´ìŠ¤ë“¤ì˜ í•©ì„ -나타냅니다. 빌드 ì„ íƒì€ í…Œì´ìŠ¤ì˜ '통과', '실패', '중단', '실행안함' íŒì •ì— ì˜í–¥ì„ 미칩니다. ìœ„ì— ì„¤ëª…í•œ +키워드, 담당ìž, 최ìƒìœ„ 스위트를 지정하는 ê²ƒì€ ìŠ¤ìœ„íŠ¸ë³„, 테스트 계íšë³„ ë§¤íŠ¸ë¦­ì„ ê³„ì‚° í•˜ëŠ”ë° ì‚¬ìš©í•˜ëŠ” 테스트 ì¼€ì´ìŠ¤ë“¤ì˜ +개수를 지정하는 것입니다. 예를 들어서, 담당ìžëŠ” 'Greg', 키워드는 'Priority 1', 그리고 모든 가능한 스위트를 지정했다면, +Gregì—게 ì§€ì •ëœ Priority 1ì¸ ê²ƒë“¤ì´ ì‚¬ìš© ë©ë‹ˆë‹¤. '# 테스트 ì¼€ì´ìФ'는 ì´ëŸ° 3가지 ì»¨íŠ¸ë¡¤ì— ì˜í•œ 테스트 ì¼€ì´ìŠ¤ë“¤ì˜ í•©ì„ +나타냅니다. 빌드 ì„ íƒì€ í…Œì´ìŠ¤ì˜ '통과', '실패', '중단', '실행안함' íŒì •ì— ì˜í–¥ì„ 미칩니다. ìœ„ì— ì„¤ëª…í•œ '마지막 테스트 ê²°ê³¼' ê·œì¹™ì„ ì°¸ê³  하시기 ë°”ëžë‹ˆë‹¤.

      'ì§ˆì˜ ì‹¤í–‰' ë²„íŠ¼ì„ ëˆ„ë¥´ë©´ ê²°ê³¼ í™”ë©´ì´ í‘œì‹œ ë©ë‹ˆë‹¤.

      @@ -299,39 +292,38 @@
    • 보고서를 ìž‘ì„±í•˜ëŠ”ë° ì‚¬ìš©ëœ ì§ˆì˜ íŒŒë¼ë©”í„°
    • ì „ì²´ 테스트 계íšì˜ 합계
    • 스위트 별 ê²°ê³¼ 합계 (í•© / 통과 / 실패 / 중단 / 실행안함) 와 ìŠ¤ìœ„íŠ¸ì˜ ëª¨ë“  실행 ê²°ê³¼. -만약 테스트 ì¼€ì´ìŠ¤ê°€ 여러 빌드ì—서 여러번 실행 ë˜ì—ˆìœ¼ë©´, ì„ íƒëœ 모든 ë¹Œë“œì˜ ì‹¤í–‰ê²°ê³¼ë¥¼ 표시합니다. +만약 테스트 ì¼€ì´ìŠ¤ê°€ 여러 빌드ì—서 여러번 실행 ë˜ì—ˆìœ¼ë©´, ì„ íƒëœ 모든 ë¹Œë“œì˜ ì‹¤í–‰ê²°ê³¼ë¥¼ 표시합니다. 하지만, 테스트 ìŠ¤ìœ„íŠ¸ì˜ ìš”ì•½ì—는 '마지막 테스트 ê²°ê³¼'ë§Œ í¬í•¨ë©ë‹ˆë‹¤.
    • 중단, 실패, 실행하지 ì•Šì€ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФ 보고서들

      -

      ì´ ë³´ê³ ì„œëŠ” 현재 중단, 실패, ì‹¤í–‰ì•ˆë¨ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ë¥¼ ë³´ì—¬ ì¤ë‹ˆë‹¤. (ì¼ë°˜ì ì¸ 테스트 ê³„íš ë§¤íŠ¸ë¦­ì—서 -설명했ë˜)'마지막 테스트 ê²°ê³¼'논리는 ì´ ê³³ì—ì„œë„ ì‚¬ìš© ë©ë‹ˆë‹¤. 중단 ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФ 보고서와 ì‹¤íŒ¨ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФ +

      ì´ ë³´ê³ ì„œëŠ” 현재 중단, 실패, ì‹¤í–‰ì•ˆë¨ ëª¨ë“  테스트 ì¼€ì´ìŠ¤ë¥¼ ë³´ì—¬ ì¤ë‹ˆë‹¤. (ì¼ë°˜ì ì¸ 테스트 ê³„íš ë§¤íŠ¸ë¦­ì—서 +설명했ë˜)'마지막 테스트 ê²°ê³¼'논리는 ì´ ê³³ì—ì„œë„ ì‚¬ìš© ë©ë‹ˆë‹¤. 중단 ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФ 보고서와 ì‹¤íŒ¨ëœ í…ŒìŠ¤íŠ¸ ì¼€ì´ìФ 보고서ì—서는 버그 ì¶”ì  ì‹œìŠ¤í…œì´ ì„¤ì • ë˜ì–´ ìžˆì„ ê²½ìš° ë²„ê·¸ì˜ ì—°ê²°ê´€ê³„ë„ ë³´ì—¬ ì¤ë‹ˆë‹¤.

      테스트 보고서

      -

      테스트 ì¼€ì´ìŠ¤ì˜ ëª¨ë“  ë¹Œë“œì˜ ìƒíƒœë¥¼ ë³¼ 수 있습니다. ê°™ì€ ë¹Œë“œì—서 여러번 ì‹¤í–‰ì´ ë˜ì—ˆë‹¤ë©´ -가장 ìµœê·¼ì˜ ì‹¤í–‰ ê²°ê³¼ 사용 ë©ë‹ˆë‹¤. ìžë£Œì˜ ì–‘ì´ ë§Žë‹¤ë©´ 보다 쉽게 보고서를 보기 위해 엑셀로 내보내기 í•  ê²ƒì„ +

      테스트 ì¼€ì´ìŠ¤ì˜ ëª¨ë“  ë¹Œë“œì˜ ìƒíƒœë¥¼ ë³¼ 수 있습니다. ê°™ì€ ë¹Œë“œì—서 여러번 ì‹¤í–‰ì´ ë˜ì—ˆë‹¤ë©´ +가장 ìµœê·¼ì˜ ì‹¤í–‰ ê²°ê³¼ 사용 ë©ë‹ˆë‹¤. ìžë£Œì˜ ì–‘ì´ ë§Žë‹¤ë©´ 보다 쉽게 보고서를 보기 위해 엑셀로 내보내기 í•  ê²ƒì„ ê¶Œê³ í•©ë‹ˆë‹¤.

      차트 - ì¼ë°˜ì ì¸ 테스트 ê³„íš ë§¤íŠ¸ë¦­

      -

      차트ì—ì„œë„ '마지막 테스트 ê²°ê³¼' 논리가 사용ë©ë‹ˆë‹¤. 현재 테스트 계íšì˜ ë§¤íŠ¸ë¦­ì„ ì‹œê°ì ìœ¼ë¡œ 보여주기 위해 +

      차트ì—ì„œë„ '마지막 테스트 ê²°ê³¼' 논리가 사용ë©ë‹ˆë‹¤. 현재 테스트 계íšì˜ ë§¤íŠ¸ë¦­ì„ ì‹œê°ì ìœ¼ë¡œ 보여주기 위해 그래프가 움ì§ìž…니다. 다ìŒê³¼ ê°™ì€ 4가지 차트가 제공 ë©ë‹ˆë‹¤ :

      • 모든 성공 / 실패 / 중단 / 실행안함 ì— ëŒ€í•œ íŒŒì´ ì°¨íŠ¸
      • 키워드별 ê²°ê³¼ 막대 차트
      • 테스터별 ê²°ê³¼ 막대 차트
      • 최ìƒìœ„ 스위트별 ê²°ê³¼ 막대 차트
      -

      막대 ì°¨íŠ¸ì— ìžˆëŠ” ë§‰ëŒ€ë“¤ì€ í†µê³¼, 실패, 중단, 실행안한 ì¼€ì´ìŠ¤ë“¤ì˜ ëŒ€ê°•ì˜ ìˆ«ìžë¥¼ ì •ì˜í•  수 있으며 +

      막대 ì°¨íŠ¸ì— ìžˆëŠ” ë§‰ëŒ€ë“¤ì€ í†µê³¼, 실패, 중단, 실행안한 ì¼€ì´ìŠ¤ë“¤ì˜ ëŒ€ê°•ì˜ ìˆ«ìžë¥¼ ì •ì˜í•  수 있으며 색으로 구별 ë˜ì–´ 있습니다.

      ê° í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ì— ëŒ€í•œ 모든 버그들

      -

      ì´ ë³´ê³ ì„œëŠ” 현재 프로ì íЏì—서 버그 필드가 값으로 채워져 있는 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ë³´ì—¬ ì¤ë‹ˆë‹¤. -ì´ ë³´ê³ ì„œëŠ” 버그 ì¶”ì  ì‹œìŠ¤í…œì´ ì—°ê²° ë˜ì–´ 있어야 사용할 수 있습니다.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "테스트 계íšì— 테스트 ì¼€ì´ìФ 추가 / 삭제하기"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

      목ì :

      +

      ì´ ë³´ê³ ì„œëŠ” 현재 프로ì íЏì—서 버그 필드가 값으로 채워져 있는 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ë³´ì—¬ ì¤ë‹ˆë‹¤. +ì´ ë³´ê³ ì„œëŠ” 버그 ì¶”ì  ì‹œìŠ¤í…œì´ ì—°ê²° ë˜ì–´ 있어야 사용할 수 있습니다.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "테스트 계íšì— 테스트 ì¼€ì´ìФ 추가 / 삭제하기"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

      목ì :

      (리드 레벨 ê¶Œí•œì„ ê°€ì§„) 사용ìžëŠ” 테스트 계íšì— 테스트 ì¼€ì´ìŠ¤ë¥¼ 추가하거나 삭제할 수 있습니다..

      테스트 ì¼€ì´ìФ 추가 / 삭제하기:

      @@ -339,11 +331,11 @@
    • 테스트 스위트를 í´ë¦­í•˜ì—¬ ê·¸ì†ì— í¬í•¨ëœ 모든 테스트 스위트들과 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ë³´ì´ê²Œ 합니다.
    • 테스트 ì¼€ì´ìŠ¤ë“¤ì„ ì„ íƒí•˜ê³  '테스트 ì¼€ì´ìФ 추가 / ì‚­ì œ' ë²„íŠ¼ì„ í´ë¦­í•©ë‹ˆë‹¤. 노트: ê°™ì€ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ë¥¼ 여러번 추가 í•  수 없습니다.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "테스트를 실행할 테스터 지정하기"; -$TLS_htmltext['tc_exec_assignment'] = "

      목ì 

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "테스트를 실행할 테스터 지정하기"; +$TLS_htmltext['tc_exec_assignment'] = "

      목ì 

      ì´ í™”ë©´ì—서 테스트 리ë”는 테스트 계íšì˜ 개별 í…ŒìŠ¤íŠ¸ë“¤ì„ ì‚¬ìš©ìžì—게 지정할 수 있습니다.

      시작하기

      @@ -352,16 +344,15 @@
    • 테스터를 ì„ íƒí•©ë‹ˆë‹¤.
    • 저장 ë²„íŠ¼ì„ í´ë¦­í•©ë‹ˆë‹¤.
    • ì§€ì •ëœ ì‚¬í•­ì„ í™•ì¸í•˜ë ¤ë©´ 실행 페ì´ì§€ë¥¼ ì—´ì–´ 보시기 ë°”ëžë‹ˆë‹¤. 특정 사용ìžê°€ ì§€ì •ëœ ê²ƒë§Œ 보려면 필터를 설정하면 ë©ë‹ˆë‹¤.
    • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "테스트 계íšì˜ 테스트 ì¼€ì´ìŠ¤ë“¤ ì—…ë°ì´íŠ¸í•˜ê¸°"; -$TLS_htmltext['planUpdateTC'] = "

      목ì 

      -

      테스트 명세가 변경 ë˜ì—ˆì„ 경우, ì´ íŽ˜ì´ì§€ì—서 테스트 ì¼€ì´ìŠ¤ë¥¼ 새 버전으로 ì—…ë°ì´íЏ -í•  수 있습니다. 테스트 ë„중 ì–´ë–¤ ê¸°ëŠ¥ì´ êµ¬ì²´í™” ë˜ë©´ì„œ ì´ëŸ° 경우가 ë°œìƒí•  수 있습니다." . - " 사용ìžëŠ” 테스트 명세를 변경할 경우, 테스트 ê³„íš ì—­ì‹œ 변경해야 합니다. 그렇지 않으면, 테스트" . - " 계íšì—는 여전히 ì›ëž˜ ë²„ì „ì´ ìžˆê²Œ ë©ë‹ˆë‹¤.

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "테스트 계íšì˜ 테스트 ì¼€ì´ìŠ¤ë“¤ ì—…ë°ì´íŠ¸í•˜ê¸°"; +$TLS_htmltext['planUpdateTC'] = "

      목ì 

      +

      테스트 명세가 변경 ë˜ì—ˆì„ 경우, ì´ íŽ˜ì´ì§€ì—서 테스트 ì¼€ì´ìŠ¤ë¥¼ 새 버전으로 ì—…ë°ì´íЏ +í•  수 있습니다. 테스트 ë„중 ì–´ë–¤ ê¸°ëŠ¥ì´ êµ¬ì²´í™” ë˜ë©´ì„œ ì´ëŸ° 경우가 ë°œìƒí•  수 있습니다." . + " 사용ìžëŠ” 테스트 명세를 변경할 경우, 테스트 ê³„íš ì—­ì‹œ 변경해야 합니다. 그렇지 않으면, 테스트" . + " 계íšì—는 여전히 ì›ëž˜ ë²„ì „ì´ ìžˆê²Œ ë©ë‹ˆë‹¤.

      시작하기

        @@ -369,29 +360,27 @@
      1. 개별 테스트 ì¼€ì´ìŠ¤ì˜ ìƒˆ 버전 콤보박스ì—서 ë²„ì „ì„ ì„ íƒí•©ë‹ˆë‹¤.
      2. '테스트 ê³„íš ì—…ë°ì´íЏ'ë²„íŠ¼ì„ ëˆŒëŸ¬ 변경 ì‚¬í•­ì„ ì €ìž¥í•©ë‹ˆë‹¤.
      3. 확ì¸ë°©ë²•: 실행 페ì´ì§€ë¥¼ 열어서 테스트 ì¼€ì´ìŠ¤ì˜ ë‚´ìš©ì„ í™•ì¸í•˜ì„¸ìš”.
      4. -
      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "í…ŒìŠ¤íŠ¸ì˜ ê¸´ê¸‰ë„를 높거나 낮게 설정하기"; -$TLS_htmltext['test_urgency'] = "

      목ì 

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "í…ŒìŠ¤íŠ¸ì˜ ê¸´ê¸‰ë„를 높거나 낮게 설정하기"; +$TLS_htmltext['test_urgency'] = "

      목ì 

      TestLink는 테스트 ì¼€ì´ìŠ¤ì˜ ìš°ì„ ìˆœìœ„ì— ì˜í–¥ì„ 미치는 테스트 ìŠ¤ìœ„íŠ¸ì˜ ê¸´ê¸‰ë„를 설정할 수 있습니다. -테스트 우선순위는 테스트 ì¼€ì´ìŠ¤ì˜ ì¤‘ìš”ë„와 테스트 계íšì˜ 긴급ë„ì— ë”°ë¼ ê²°ì •ë©ë‹ˆë‹¤. 테스트 리ë”는 -특정 테스트 ì¼€ì´ìŠ¤ë“¤ì´ ë¨¼ì € 실행 ë˜ë„ë¡ ì„¤ì • í•  수 있습니다. ì´ ê¸°ëŠ¥ì€ ì‹œê°„ì´ ì´‰ë°•í•  +테스트 우선순위는 테스트 ì¼€ì´ìŠ¤ì˜ ì¤‘ìš”ë„와 테스트 계íšì˜ 긴급ë„ì— ë”°ë¼ ê²°ì •ë©ë‹ˆë‹¤. 테스트 리ë”는 +특정 테스트 ì¼€ì´ìŠ¤ë“¤ì´ ë¨¼ì € 실행 ë˜ë„ë¡ ì„¤ì • í•  수 있습니다. ì´ ê¸°ëŠ¥ì€ ì‹œê°„ì´ ì´‰ë°•í•  경우ì—ë„ ê°€ìž¥ 중요한 테스트 ë“¤ì„ ë°˜ë“œì‹œ 수행하ë„ë¡ í•˜ëŠ”ë° ë„ì›€ì„ ì¤ë‹ˆë‹¤.

      시작하기

      1. ì™¼ìª½ì˜ ë„¤ë¹„ê²Œì´í„°ì—서 긴급ㄷë„를 설정할 테스트 스위트를 ì„ íƒí•˜ì„¸ìš”.
      2. -
      3. ê¸´ê¸‰ë ˆë²¨ì„ ì„ íƒí•˜ì„¸ìš” (높ìŒ, 보통, ë‚®ìŒ). ë³´í†µì´ ê¸°ë³¸ê°’ìž…ë‹ˆë‹¤. - 사용빈ë„ê°€ 떨어지는 것ì—는 우선순위를 낮추고, 중요한 ë³€ê²½ì´ ìžˆëŠ” ê³³ì€ ìš°ì„ ìˆœìœ„ë¥¼ +
      4. ê¸´ê¸‰ë ˆë²¨ì„ ì„ íƒí•˜ì„¸ìš” (높ìŒ, 보통, ë‚®ìŒ). ë³´í†µì´ ê¸°ë³¸ê°’ìž…ë‹ˆë‹¤. + 사용빈ë„ê°€ 떨어지는 것ì—는 우선순위를 낮추고, 중요한 ë³€ê²½ì´ ìžˆëŠ” ê³³ì€ ìš°ì„ ìˆœìœ„ë¥¼ ë†’ì¼ ìˆ˜ 있습니다.
      5. '저장' ë²„íŠ¼ì„ í´ë¦­í•˜ì—¬ 변경 ì‚¬í•­ì„ ì €ìž¥í•©ë‹ˆë‹¤.
      -

      예를 들어, ë‚®ì€ ê¸´ê¸‰ë„ì˜ í…ŒìŠ¤íŠ¸ 스위트 ì•ˆì— ë†’ì€ ì¤‘ìš”ë„ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ê°€ 있으면, " . - "우선순위는 보통으로 설정 ë©ë‹ˆë‹¤."; - - -// ------------------------------------------------------------------------------------------ - -?> \ No newline at end of file +

      예를 들어, ë‚®ì€ ê¸´ê¸‰ë„ì˜ í…ŒìŠ¤íŠ¸ 스위트 ì•ˆì— ë†’ì€ ì¤‘ìš”ë„ì˜ í…ŒìŠ¤íŠ¸ ì¼€ì´ìŠ¤ê°€ 있으면, " . + "우선순위는 보통으로 설정 ë©ë‹ˆë‹¤."; + +// ------------------------------------------------------------------------------------------ + +?> diff --git a/locale/nl_NL/description.php b/locale/nl_NL/description.php index cb76ff8829..ca5bdf912a 100644 --- a/locale/nl_NL/description.php +++ b/locale/nl_NL/description.php @@ -1,43 +1,42 @@ -Opties voor een gegenereerd document -

      In deze tabel kan de gebruiker testcases filteren voordat ze worden bekeken. +

      In deze tabel kan de gebruiker testcases filteren voordat ze worden bekeken. Geselecteerde (aangevinkte) gegevens zullen worden getoond. Om de voorgestelde gegevens te wijzigen, , vink aan of uit, klikt u op Filter, en selecteer het gewenste data niveau van de boom.

      -

      Document Hoofding: Gebruikers kunnen informatie in de hoofding filteren. -Document hoofding informatie omvat: inleiding, bereik, referenties, +

      Document Hoofding: Gebruikers kunnen informatie in de hoofding filteren. +Document hoofding informatie omvat: inleiding, bereik, referenties, testmethodologie en test beperkingen.

      Testcase Body: Gebruikers kunnen testcase body informatie filteren. Testcase Body informatie @@ -52,35 +51,35 @@

      Inhoudsopgave: TestLink voegt een overzicht toe van alle geselecteerde titels met interne hyperlinks

      -

      Uitvoerformaat: Er zijn twee mogelijkheden: HTML en MS Word. Browser roept MS Word component aan -in het tweede geval.

      "; - -// testPlan.html +

      Uitvoerformaat: Er zijn twee mogelijkheden: HTML en MS Word. Browser roept MS Word component aan +in het tweede geval.

      "; + +// testPlan.html $TLS_hlp_testPlan = "

      Testplan

      Algemeen

      -

      Een testplan is een systematische aanpak voor het testen van een systeem zoals software. U kunt het testen van de activiteit organiseren +

      Een testplan is een systematische aanpak voor het testen van een systeem zoals software. U kunt het testen van de activiteit organiseren met bepaalde builds van het product in de tijd en resultaten traceren.

      Tests Uitvoeren

      -

      Dit gedeelte is waar de gebruikers testcases kunnen uitvoeren (testresultaten schrijven) en -een testcase suite van het testplan afdrukken. Deze sectie is waar gebruikers de resultaten kunnen bijhouden -van het uitvoeren van een testcase.

      +

      Dit gedeelte is waar de gebruikers testcases kunnen uitvoeren (testresultaten schrijven) en +een testcase suite van het testplan afdrukken. Deze sectie is waar gebruikers de resultaten kunnen bijhouden +van het uitvoeren van een testcase.

      Testplan beheer

      -

      Deze sectie, die alleen toegankelijk is voor leiders, stelt gebruikers in staat om testplannen te beheren. -Administratie van testplannen omvat het maken/bewerken/verwijderen van de plannen, -toevoegen/bewerken/verwijderen/updaten van testcases in de plannen, builds creëren evenals bepalen wie welke +

      Deze sectie, die alleen toegankelijk is voor leiders, stelt gebruikers in staat om testplannen te beheren. +Administratie van testplannen omvat het maken/bewerken/verwijderen van de plannen, +toevoegen/bewerken/verwijderen/updaten van testcases in de plannen, builds creëren evenals bepalen wie welke plannen kan zien.
      -Gebruikers met leider permissies kunnen ook de prioriteit/risico en de eigendom van -testcase suites (categorieën) en test mijlpalen maken.

      - -

      Opmerking: Het is mogelijk dat gebruikers geen dropdown met testplannen kunnen zien. -In deze situatie zullen alle links (behalve deze geactiveerd door een leider) losgekoppeld zijn. Als u zich -in deze situatie bent moet u contact opnemen met een leider of administrator om u de juiste rechten voor het testplan toe te kennen -or een testplan voor u aan te maken.

      "; - -// custom_fields.html +Gebruikers met leider permissies kunnen ook de prioriteit/risico en de eigendom van +testcase suites (categorieën) en test mijlpalen maken.

      + +

      Opmerking: Het is mogelijk dat gebruikers geen dropdown met testplannen kunnen zien. +In deze situatie zullen alle links (behalve deze geactiveerd door een leider) losgekoppeld zijn. Als u zich +in deze situatie bent moet u contact opnemen met een leider of administrator om u de juiste rechten voor het testplan toe te kennen +or een testplan voor u aan te maken.

      "; + +// custom_fields.html $TLS_hlp_customFields = "

      Gebruikersvelden

      Hier volgen enkele feiten over de implementatie van de gebruikersvelden:

        @@ -99,7 +98,7 @@
      • Bijschrift naam van de variabele (bijvoorbeeld: Dit is de waarde die geleverd wordt aan lang_get () API, of zo weergegeven wordt als deze niet wordt gevonden in een taalbestand).
      • Type gebruikersveld (string, numeric, float, enum, e-mail)
      • -
      • Het bepalen mogelijke waarden (bijvoorbeeld: ROOD|GEEL|BLAUW), die van toepassing zijn in een lijst +
      • Het bepalen mogelijke waarden (bijvoorbeeld: ROOD|GEEL|BLAUW), die van toepassing zijn in een lijst en combo types.
        Gebruik het pijp ('|') karakter om mogelijke waarden voor een opsomming te scheiden. Een mogelijke waarde @@ -119,16 +118,16 @@
      • Aanpassen bij testplan ontwerp. De gebruiker kan de waarde veranderen tijdens het testplan ontwerp (testgevallen aan testplan toevoegen)
      • Beschikbaar voor. De gebruiker kan kiezen om wat voor soort punt het veld gaat.
        • -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

          Testcases uitvoeren

          Hiermee kunnen gebruikers testcases 'uitvoeren'. Uitvoeren zelf is louter het toewijzen van resultaat aan een testcase (OK, gefaald, geblokkeerd) in een geselecteerde build.

          De toegang tot een bug tracking systeem kan worden geconfigureerd. De gebruiker kan dan direct nieuwe bugs toevoegen -en door bestaande bladeren. Zie installatiehandleiding voor meer informatie.

          "; - -//bug_add.html +en door bestaande bladeren. Zie installatiehandleiding voor meer informatie.

          "; + +// bug_add.html $TLS_hlp_btsIntegration = "

          Bugs toevoegen aan een testcase 

          (alleen als dit geconfigureerd is) TestLink heeft een zeer eenvoudige integratie met Bug Tracking Systems (BTS), @@ -138,7 +137,7 @@

        • Nieuwe bug toevoegen.
        • Toon bestaande bug info.
          • -

              +

            Proces om een bug toe te voegen

            @@ -147,12 +146,12 @@   
          • Stap 2: Noteer de BUGID toegewezen door BTS
          •   
          • Stap 3: Schrijf BUGID in het invoerveld
          •   
          • Stap 4: Gebruik bug  toevoegen knop
          • -  
                +  
                Na het sluiten van de bug toevoegen pagina vindt u de relevante bug gegevens op de tests uitvoeren pagina te zien. -

                "; - -// execFilter.html +

                "; + +// execFilter.html $TLS_hlp_executeFilter = "

                Instellingen

                In instellingen kunt u het testplan, build en platform (indien aanwezig) om uit te voeren selecteren @@ -178,85 +177,82 @@

                Trefwoord filter

                -

                U kunt testcases filteren op de trefwoorden die eraan zijn toegewezen. Je kan meerdere trefwoorden kiezen " . -"met CTRL-klik. Als u meer dan één trefwoord koos kun je ". -"beslissen of alleen testcases worden getoond waaraan alle gekozen trefwoorden zijn toegewezen". -"(Radiobutton \"en\") of ten minste één van de gekozen trefwoorden (radioknop \"Of\").

                +

                U kunt testcases filteren op de trefwoorden die eraan zijn toegewezen. Je kan meerdere trefwoorden kiezen " . + "met CTRL-klik. Als u meer dan één trefwoord koos kun je " . + "beslissen of alleen testcases worden getoond waaraan alle gekozen trefwoorden zijn toegewezen" . + "(Radiobutton \"en\") of ten minste één van de gekozen trefwoorden (radioknop \"Of\").

                Prioriteitsfilter

                -

                U kunt testcases filteren op test prioriteit. De test prioriteit is \"testcase belang\" ". -"gecombineerd met \"test dringendheid\" in het huidige testplan.

                +

                U kunt testcases filteren op test prioriteit. De test prioriteit is \"testcase belang\" " . + "gecombineerd met \"test dringendheid\" in het huidige testplan.

                Gebruiker filter

                -

                U kunt testcases filteren die niet zijn toegewezen (\"Niemand\") of toegewezen aan \"Iemand\". ". -"Je kunt ook testcases filteren die aan een specifieke tester zijn toegewezen. Als je een specifieke tester kiest ". -"heb je ook de mogelijkheid om testcases die niet toegewezen zijn erbij te laten zien". -"(geavanceerde filters zijn beschikbaar).

                +

                U kunt testcases filteren die niet zijn toegewezen (\"Niemand\") of toegewezen aan \"Iemand\". " . + "Je kunt ook testcases filteren die aan een specifieke tester zijn toegewezen. Als je een specifieke tester kiest " . + "heb je ook de mogelijkheid om testcases die niet toegewezen zijn erbij te laten zien" . + "(geavanceerde filters zijn beschikbaar).

                Resultaat filter

                -

                U kunt testcases filteren op resultaat (geavanceerde filters zijn beschikbaar). U kunt filteren op ". -"Resultaat \"op gekozen build \", \"op de nieuwste uitvoering\", \"op ALLE builds\", ". -"\"op om het even welke build\" en \"op specifieke build\". Als \"specifieke build\" gekozen is dan kan u". -"de build opgeven.

                "; - - -// newest_tcversions.html +

                U kunt testcases filteren op resultaat (geavanceerde filters zijn beschikbaar). U kunt filteren op " . + "Resultaat \"op gekozen build \", \"op de nieuwste uitvoering\", \"op ALLE builds\", " . + "\"op om het even welke build\" en \"op specifieke build\". Als \"specifieke build\" gekozen is dan kan u" . + "de build opgeven.

                "; + +// newest_tcversions.html $TLS_hlp_planTcModified = "

                De nieuwste versies van gekoppelde testcases

                De hele set testcases gekoppeld aan testplan wordt geanalyseerd, en een lijst van testcases waarvan de nieuwste versie wordt weergegeven (vergeleken met de huidige set van het testplan). -

                "; - - -// requirementsCoverage.html +

                "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

                Vereisten dekking


                Deze functie maakt het mogelijk om de ​​dekking in kaart te brengen van de gebruiker- of systeemvereisten door testcases Openen via link \"Vereisten specificatie\" in het hoofdscherm.

                Vereisten specificatie

                -

                Vereisten worden gegroepeerd door een 'Vereisten specificatie' document dat betrekking heeft op het -testproject.
                TestLink ondersteunt geen versiebeheer voor vereisten specificaties  -of vereisten. Dus moet de versie van document worden toegevoegd na +

                Vereisten worden gegroepeerd door een 'Vereisten specificatie' document dat betrekking heeft op het +testproject.
                TestLink ondersteunt geen versiebeheer voor vereisten specificaties +of vereisten. Dus moet de versie van document worden toegevoegd na een specificatie Titel. -Een gebruiker kan eenvoudige beschrijvingen of opmerkingen toevoegen aan het Bereik veld.

                +Een gebruiker kan eenvoudige beschrijvingen of opmerkingen toevoegen aan het Bereik veld.

                -

                Overschreven telling van vereisten dient voor -evaluatie van vereisten dekking in het geval dat niet aan alle vereisten toegevoegd (of geïmporteerd) zijn. -De waarde 0 betekent dat de huidige telling van eisen wordt gebruikt voor de statistieken.

                -

                Bv SRS omvat 200 vereisten, maar slechts 50 worden toegevoegd in TestLink. Test +

                Overschreven telling van vereisten dient voor +evaluatie van vereisten dekking in het geval dat niet aan alle vereisten toegevoegd (of geïmporteerd) zijn. +De waarde 0 betekent dat de huidige telling van eisen wordt gebruikt voor de statistieken.

                +

                Bv SRS omvat 200 vereisten, maar slechts 50 worden toegevoegd in TestLink. Test dekking is 25% (indien alle toegevoegde vereisten worden getest).

                Vereisten

                Klik op de titel van een bestaande Vereisten specificatie. U kunt vereisten maken, bewerken, verwijderen of importeren voor het document. Elke vereiste heeft een titel, bereik en status. Status moet \"normaal\" of \"Niet toetsbaar\" zijn. Niet toetsbare vereisten worden niet meegeteld -in statistieken. Deze parameter moet worden gebruikt voor niet geïmplementeerde functies en -verkeerd ontworpen vereisten.

                +in statistieken. Deze parameter moet worden gebruikt voor niet geïmplementeerde functies en +verkeerd ontworpen vereisten.

                -

                U kunt nieuwe testcases voor de vereisten aanmaken door het gebruik van multi actie met gecontroleerde +

                U kunt nieuwe testcases voor de vereisten aanmaken door het gebruik van multi actie met gecontroleerde vereisten in het specificaties scherm. Deze testcases worden gemaakt in testsuite -met de naam opgegeven in configuratie (standaard is: $tlCfg->req_cfg->default_testsuite_name = +met de naam opgegeven in configuratie (standaard is: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";) . Titel en bereik worden gekopieerd naar deze testcases.

                -"; - +"; + $TLS_hlp_req_coverage_table = "

                Dekking:

                -Een waarde van bijvoorbeeld \"40% (8/20)\" betekent dat 20 testcases moeten worden gemaakt om deze vereiste -volledig testen. 8 ervan al zijn gemaakt en gekoppeld aan deze vereiste, die +Een waarde van bijvoorbeeld \"40% (8/20)\" betekent dat 20 testcases moeten worden gemaakt om deze vereiste +volledig testen. 8 ervan al zijn gemaakt en gekoppeld aan deze vereiste, die zo een dekking van 40 procent uitmaken. -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

                Metbetrekking tot 'Gebruikersvelden opslaan'

                Als u gebruikersvelden met
                'Toon bij testplan ontwerp'
                en 'Beschikbaar bij testplan ontwerp'
                -hebt gedefinieerd en toegewezen aan een testproject,
                +hebt gedefinieerd en toegewezen aan een testproject,
                zult u deze op deze pagina alleen zien voor testcases gekoppeld aan het testplan. -"; - -// xxx.html -// $TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/nl_NL/texts.php b/locale/nl_NL/texts.php index 6b2f2e0acd..2cfb50fff6 100644 --- a/locale/nl_NL/texts.php +++ b/locale/nl_NL/texts.php @@ -1,39 +1,36 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * @package TestLink - * @author Martin Havlat - * @copyright 2003-2009, TestLink community - * @version CVS: $Id: texts.php,v 1.2 2010/06/24 17:25:53 asimon83 Exp $ - * @link http://www.teamst.org/index.php - * - **/ - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Applicatie fout"; -$TLS_htmltext [ 'error'] = "

                Er is een onverwachte fout gebeurt. Controleer event viewer of ". -"logboek voor meer informatie.

                U bent van harte welkom om het probleem te melden. Ga naar onze ". -"website

                "; - - - -$TLS_htmltext_title['assignReqs'] = "Vereisten toewijzen aan testcases"; -$TLS_htmltext['assignReqs'] = "

                Doel:

                +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * @package TestLink + * @author Martin Havlat + * @copyright 2003-2009, TestLink community + * @version CVS: $Id: texts.php,v 1.2 2010/06/24 17:25:53 asimon83 Exp $ + * @link http://www.teamst.org/index.php + * + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Applicatie fout"; +$TLS_htmltext['error'] = "

                Er is een onverwachte fout gebeurt. Controleer event viewer of " . + "logboek voor meer informatie.

                U bent van harte welkom om het probleem te melden. Ga naar onze " . + "website

                "; + +$TLS_htmltext_title['assignReqs'] = "Vereisten toewijzen aan testcases"; +$TLS_htmltext['assignReqs'] = "

                Doel:

                Gebruikers kunnen de relaties tussen vereisten en testcases aanmaken. Een test ontwerper zou kunnen 0..n tot 0..n relaties definiëren. D.w.z Één testcase kan worden toegewezen aan geen, één of meerdere vereisten en vice versa. Dergelijke traceerbaarheidsmatrix helpt test dekking te onderzoeken @@ -44,7 +41,7 @@

                1. Kies een testcase in de boomstructuur aan de linkerkant. De combobox met lijst van vereisten specificaties wordt bovenaan het werkgebied getoond.
                2. -
                3. Kies een vereisten specificatie document als er meerdere zijn gedefinieerd. +
                4. Kies een vereisten specificatie document als er meerdere zijn gedefinieerd. TestLink herlaadt automatisch de pagina.
                5. Een blok in het midden van het werkgebied lijsten toont van alle vereisten (van de gekozen specificatie), die zijn verbonden met de testcase. Het onderste blok toont een lijst met 'Beschikbare vereisten' alle @@ -52,50 +49,48 @@ de huidige test case. Een ontwerper kan vereisten die worden gedekt door deze testcase markeren en klikken op de knop 'Toewijzen'. Deze nieuwe toegewezen testcases worden getoond in het middelste blok 'Toegewezen vereisten'.
                6. -
                "; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Testspecificatie"; -$TLS_htmltext['editTc'] = "

                De Testspecificatie stelt gebruikers in staat alle bestaande testcases te ". -"en aan te passen Testsuites en Testcases.". -"Testcases hebben versiebeheer en alle vorige versies zijn beschikbaar en kunnen hier worden". -"bekeken en beheerd.

                +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Testspecificatie"; +$TLS_htmltext['editTc'] = "

                De Testspecificatie stelt gebruikers in staat alle bestaande testcases te " . + "en aan te passen Testsuites en Testcases." . + "Testcases hebben versiebeheer en alle vorige versies zijn beschikbaar en kunnen hier worden" . + "bekeken en beheerd.

                Aan de slag:

                  -
                1. Kies uw Testproject in de navigatiestructuur (de root node). Let op: ". -"U kunt altijd het actieve testproject wijzigen door een andere optie uit de". -"drop-down lijst in de rechterbovenhoek te kiezen.
                2. -
                3. Maak een nieuwe testsuite door te klikken op Nieuwe testsuite . Testsuites kunnen ". -"structuur brengen in uw testdocumenten volgens uw conventies (functioneel/niet-functioneel". -"testen, product componenten of functies, change requests, etc.). De omschrijving van ". -"een testsuite kan de reikwijdte van de inbegrepen testcases, standaardconfiguratie,". -"links naar relevante documenten, beperkingen en andere nuttige informatie bevatten. In het algemeen, " . -"alle informaties die gemeenschappelijk is voor de onderliggende testcases. Testsuites volgen ". -" zien er uit als "mappen", waardoor gebruikers testsuites kunnen verplaatsen en kopiëren binnen". -"het testproject. Ook kunnen ze worden geïmporteerd of geëxporteerd (inclusief testcases).
                4. -
                5. Testsuites zijn schaalbare mappen. Gebruikers kunnen testsuites verplaatsen of kopiëren binnen ". -"het testproject. Test Suites kunnen worden geïmporteerd of geëxporteerd (inclusief testcases). -
                6. Selecteer uw nieuwe testsuite in de navigatiestructuur en maak ". -"een nieuwe testcases door te klikken op Testcase . Een testcase specificeert ". -"een bepaalde test scenario, verwachte resultaten en de gedefiniëerde gebruikersvelden". -"In het testproject (zie de handleiding voor meer informatie). Het is ook mogelijk ". -"trefwoorden toe te wijzen voor een betere traceerbaarheid.
                7. +
                8. Kies uw Testproject in de navigatiestructuur (de root node). Let op: " . + "U kunt altijd het actieve testproject wijzigen door een andere optie uit de" . + "drop-down lijst in de rechterbovenhoek te kiezen.
                9. +
                10. Maak een nieuwe testsuite door te klikken op Nieuwe testsuite . Testsuites kunnen " . + "structuur brengen in uw testdocumenten volgens uw conventies (functioneel/niet-functioneel" . + "testen, product componenten of functies, change requests, etc.). De omschrijving van " . + "een testsuite kan de reikwijdte van de inbegrepen testcases, standaardconfiguratie," . + "links naar relevante documenten, beperkingen en andere nuttige informatie bevatten. In het algemeen, " . + "alle informaties die gemeenschappelijk is voor de onderliggende testcases. Testsuites volgen " . + " zien er uit als "mappen", waardoor gebruikers testsuites kunnen verplaatsen en kopiëren binnen" . + "het testproject. Ook kunnen ze worden geïmporteerd of geëxporteerd (inclusief testcases).
                11. +
                12. Testsuites zijn schaalbare mappen. Gebruikers kunnen testsuites verplaatsen of kopiëren binnen " . + "het testproject. Test Suites kunnen worden geïmporteerd of geëxporteerd (inclusief testcases). +
                13. Selecteer uw nieuwe testsuite in de navigatiestructuur en maak " . + "een nieuwe testcases door te klikken op Testcase . Een testcase specificeert " . + "een bepaalde test scenario, verwachte resultaten en de gedefiniëerde gebruikersvelden" . + "In het testproject (zie de handleiding voor meer informatie). Het is ook mogelijk " . + "trefwoorden toe te wijzen voor een betere traceerbaarheid.
                14. Navigeer via de boomstructuur aan de linkerkant en bewerk de data. Elke testcase heeft zijn eigen geschiedenis.
                15. Wijs uw gecreëerde testspecificatie voor Testplan wanneer uw testcases klaar zijn.
                16. + \"Javascript: open_help_window ( 'glossary', '$locale'); \">Testplan wanneer uw testcases klaar zijn.
                -

                Met TestLink kunt u testcases in testsuites organiseren. ". -"Testsuites kunnen worden genest binnen andere testsuites, zodat u hiërarchieën van testsuites kan maken. -Vervolgens kunt u deze informatie samen met de testcases afdrukken.

                "; - - -// ------------------------------------------------------------------------------------------ - -$TLS_htmltext_title['searchTc'] = "Testcase zoeken pagina"; -$TLS_htmltext['searchTc'] = "

                Doel:

                +

                Met TestLink kunt u testcases in testsuites organiseren. " . + "Testsuites kunnen worden genest binnen andere testsuites, zodat u hiërarchieën van testsuites kan maken. +Vervolgens kunt u deze informatie samen met de testcases afdrukken.

                "; + +// ------------------------------------------------------------------------------------------ + +$TLS_htmltext_title['searchTc'] = "Testcase zoeken pagina"; +$TLS_htmltext['searchTc'] = "

                Doel:

                Navigatie op basis van trefwoorden en/of gezochte strings. Het zoeken is niet hoofdlettergevoelig Resultaten bevatten enkel testcases van het actuele testproject.

                @@ -107,13 +102,13 @@
              • Kies een trefwoord of laat de waarde 'Niet van toepassing'.
              • Klik op de knop 'Zoeken'.
              • Alle uitgevoerde testcases worden getoond. U kunt testcases wijzigen via de 'titel' link.
              • -"; - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Vereisten zoek pagina"; -$TLS_htmltext['searchReq'] = "

                Doel:

                +"; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Vereisten zoek pagina"; +$TLS_htmltext['searchReq'] = "

                Doel:

                Navigatie op basis van trefwoorden en/of gezochte strings. Het zoeken is niet hoofdlettergevoelig. Resultaten bevatten enkel vereisten van het actuele testproject.

                @@ -125,12 +120,12 @@
              • Kies het gewenste trefwoord of laat de waarde 'Niet van toepassing'.
              • Klik op 'Zoeken' te klikken.
              • Alle vervulde vereisten worden getoond. U kunt vereisten aanpassen via de 'titel' link.
              • -"; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Vereisten specificatie zoekpagina"; -$TLS_htmltext['searchReqSpec'] = "

                Doel:

                +"; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Vereisten specificatie zoekpagina"; +$TLS_htmltext['searchReqSpec'] = "

                Doel:

                Navigatie op basis van trefwoorden en/of gezochte strings. Het zoeken is niet hoofdlettergevoelig. Resultaten bevatten slechts vereisten specificaties van het actuele testproject.

                @@ -142,35 +137,33 @@
              • Kies de gewenste trefwoorden of laat de waarde 'Niet van toepassing'.
              • Klik op 'Zoeken'.
              • Alle vervulde vereisten specificaties worden getoond. U kunt de vereisten specificaties te wijzigen via de 'Titel' link.
              • -"; -/* end contribution */ - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Druk testspecificatie"; //printTC.html -$TLS_htmltext [ 'printTestSpec'] = "

                Doel:

                +"; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Druk testspecificatie"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

                Doel:

                Hier kunt u een enkele testcase of alle testcases het binnen een testsuite afdrukken, of alle testcases in een testproject of testplan.

                Aan de slag:

                1. -

                  Selecteer de onderdelen van de testcases die u wilt weergeven, en klik vervolgens op een test case, +

                  Selecteer de onderdelen van de testcases die u wilt weergeven, en klik vervolgens op een test case, testsuite of het testproject. Een afdrukbare pagina wordt getoond.

                2. -
                3. Met de \"Toon als \" dropbox in het navigatiepaneel kunt u kiezen of u -de informatie wilt weergegeven als HTML, OpenOffice Writer of in een Micosoft Word document. +

                4. Met de \"Toon als \" dropbox in het navigatiepaneel kunt u kiezen of u +de informatie wilt weergegeven als HTML, OpenOffice Writer of in een Micosoft Word document. Zie help voor meer informatie

                  .
                5. Gebruik de printfunctionaliteit van uw browser om de informatie af te drukken.
                  . Opmerking: Zorg ervoor dat u enkel het rechtse frame afdrukt.

                6. -
                "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Vereisten specificatie ontwerp"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

                U kunt vereisten specificatie documenten beheren.

                +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Vereisten specificatie ontwerp"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

                U kunt vereisten specificatie documenten beheren.

                Vereisten specificatie

                @@ -188,8 +181,8 @@

                Vereisten

                -

                Klik op de titel van een bestaande vereisten specificatie. Als er geen bestaan, ". -"klik op de project node om er een te maken. U kunt vereisten maken, bewerken, verwijderen +

                Klik op de titel van een bestaande vereisten specificatie. Als er geen bestaan, " . + "klik op de project node om er een te maken. U kunt vereisten maken, bewerken, verwijderen of importeren voor het document. Elke vereiste heeft een titel, bereik en status. Een status moet ofwel 'Normaal' of 'Niet toetsbaar' zijn. Niet toetsbare vereisten worden niet meegeteld in statistieken. Deze parameter moet worden gebruikt voor niet geïmplementeerde functies en @@ -198,41 +191,39 @@

                U kunt nieuwe testcases voor de vereisten aanmaken door het gebruik van multi actie met gecontroleerde vereisten in het specificatie scherm. Deze testcases worden gemaakt in een testsuite met de naam opgegeven in configuratie (standaard is: \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';) . Titel en bereik worden gekopieerd naar deze testcases.

                "; - - -$TLS_htmltext_title['printReqSpec'] = "Print vereisten specificatie"; +'Test suite created by Requirement - Auto';)
                . Titel en bereik worden gekopieerd naar deze testcases.

                "; + +$TLS_htmltext_title['printReqSpec'] = "Print vereisten specificatie"; $TLS_htmltext['printReqSpec'] = "

                Doel:

                Vanaf hier kunt u een enkele vereiste af te drukken, alle vereisten binnen een vereisten specificatie, of alle vereisten in een testproject.

                Aan de slag:

                1. -

                  Selecteer de onderdelen van de vereisten die u wilt weergeven, en klik dan op een vereiste, +

                  Selecteer de onderdelen van de vereisten die u wilt weergeven, en klik dan op een vereiste, vereisten specificatie, of het testproject. Een afdrukbare pagina wordt getoond.

                2. -
                3. Met de \"Toon als\" drop-box in het navigatiepaneel kunt u kiezen of u -de informatie wilt weergeven als HTML, OpenOffice Writer of in een Micosoft Word document. +

                4. Met de \"Toon als\" drop-box in het navigatiepaneel kunt u kiezen of u +de informatie wilt weergeven als HTML, OpenOffice Writer of in een Micosoft Word document. Zie help voor meer informatie.

                5. Gebruik de printfunctionaliteit van uw browser om de informatie af te drukken.
                  Opmerking: Zorg ervoor dat u alleen het rechter frame afdrukt.

                6. -
                "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign']= "Trefwoorden toewijzen"; +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Trefwoorden toewijzen"; $TLS_htmltext['keywordsAssign'] = "

                Doel:

                -

                De Trefwoorden toewijzen pagina is de plaats waar gebruikers +

                De Trefwoorden toewijzen pagina is de plaats waar gebruikers trefwoorden kunnen toewijzen aan bestaande testsuites of testcases

                Om trefwoorden toe te wijzen:

                1. Selecteer een testsuite of testcase in de boomstructuur aan de linkerkant.
                2. -
                3. In het veld bovenaan rechts kunt u beschikbare +
                4. In het veld bovenaan rechts kunt u beschikbare trefwoorden toewijzen aan iedere testcase.
                5. In de selecties eronder kunt u testcases toewijzen op een meer granulair niveau.
                6. @@ -247,16 +238,15 @@

                  TestLink gebruikt deze aanpak, zodat oudere versies van testcases in testplannen niet worden beïnvloed door trefwoorden die u aan de meest recente versie van de testcase toewijst. Als u wilt dat de testcases in uw testplan worden bijgewerkt, controleer dan eerst of ze up-to-date met behulp van de 'update -gewijzigde testcases functie 'VOORDAT u trefwoorden toewijst.

                  ". - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest']= "Testcases uitvoeren"; +gewijzigde testcases functie 'VOORDAT u trefwoorden toewijst.

                  " . + + // ------------------------------------------------------------------------------------------ + $TLS_htmltext_title['executeTest'] = "Testcases uitvoeren"; $TLS_htmltext['executeTest'] = "

                  Doel:

                  Hiermee kan de gebruiker testcases uitvoeren. De gebruiker kan een testresultaat toewijzen -aan testcases voor een build. Zie help voor meer informatie over filters en instellingen ". -"(Klik op het vraagteken).

                  +aan testcases voor een build. Zie help voor meer informatie over filters en instellingen " . + "(Klik op het vraagteken).

                  Aan de slag:

                  @@ -264,19 +254,19 @@
                7. Er moet een build voor het testplan gedefinieerd zijn.
                8. Selecteer een build uit de keuzelijst
                9. Als u slechts een paar testcases wilt zien in plaats van de hele boomstructuur, -kunt u filters toepassen. Klik op de \"Toepassen\"-knop +kunt u filters toepassen. Klik op de \"Toepassen\"-knop nadat u de filters heeft veranderd.
                10. Klik op een testcase in de menustructuur.
                11. Vul het testcase resultaat en eventuele notities of bugs in.
                12. Resultaat opslaan.
                -

                Opmerking: TestLink moet worden geconfigureerd om samen te werken met je Bug tracker -als je wilt een probleem rapport rechtstreeks vanuit de GUI wilt maken of zoeken

                ". - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics']= "Beschrijving van de testrapporten en statistieken"; -$TLS_htmltext['showMetrics'] = "

                Rapporten zijn gerelateerd aan een testplan". -"(Gedefinieerd bovenaan de navigatie). Dit testplan kan anders zijn dan het +

                Opmerking: TestLink moet worden geconfigureerd om samen te werken met je Bug tracker +als je wilt een probleem rapport rechtstreeks vanuit de GUI wilt maken of zoeken

                " . + + // ------------------------------------------------------------------------------------------ + $TLS_htmltext_title['showMetrics'] = "Beschrijving van de testrapporten en statistieken"; +$TLS_htmltext['showMetrics'] = "

                Rapporten zijn gerelateerd aan een testplan" . + "(Gedefinieerd bovenaan de navigatie). Dit testplan kan anders zijn dan het huidige testplan voor de uitvoering. U kunt ook een rapport formaat kiezen:

                • Normaal- rapport wordt weergegeven in de webpagina
                • @@ -337,7 +327,7 @@

                  Dit rapport bestaat uit een query formulier pagina en een query resultaten pagina die de opgevraagde gegevens bevat. De Query Form pagina presenteert met een query pagina met 4 velden. Elk veld is ingesteld op een standaard die het aantal testcases en builds maximaliseert waarvoor de query moet worden uitgevoerd. Door de velden te veranderen -kan de gebruiker resultaten filtere en specifieke rapporten voor specifieke eigenaars, trefwoorden, testsuite +kan de gebruiker resultaten filtere en specifieke rapporten voor specifieke eigenaars, trefwoorden, testsuite en build combinaties genereren.

                    @@ -371,7 +361,7 @@
                  • De query parameters die werden gebruikt om het rapport te maken
                  • Totalen voor het gehele testplan
                  • Een uitsplitsing per suite van de totalen (som / OK / gefaald / geblokkeerd / niet uitgevoerd) en alle executies uitgevoerd -op die suite.  Als een test case niet meer dan een keer is uitgevoerd in meerdere meerdere builds zullen alle executies +op die suite.  Als een test case niet meer dan een keer is uitgevoerd in meerdere meerdere builds zullen alle executies weergegeven worden die zijn uitgevoerd met de geselecteerde build. Echter, de samenvatting voor die suite zal alleen de 'laatste testresultaten' voor de geselecteerde build bevatten.
                  • @@ -383,12 +373,12 @@ worden de bijbehorende bugs weergegeven als de gebruiker een geïntegreerd storingmeldingssysteem gebruikt.

                    Testrapport

                    -

                    De status van elke testcase in elke build. Het meest recente resultaat zal worden gebruikt +

                    De status van elke testcase in elke build. Het meest recente resultaat zal worden gebruikt als een testcase meerdere keren werd uitgevoerd op dezelfde build. Het is aan te raden om dit rapport te exporteren naar Excel-formaat voor eenvoudiger browsen als een grote dataset wordt gebruikt.

                    Grafieken - Algemene testplan statistieken

                    -

                    'Laatste testresultaat' logica wordt gebruikt voor alle vier de grafieken die u zult zien. De grafieken zijn geanimeerd om +

                    'Laatste testresultaat' logica wordt gebruikt voor alle vier de grafieken die u zult zien. De grafieken zijn geanimeerd om van de statistieken van het huidige testplan te visualiseren. De vier grafieken zijn:

                    • cirkeldiagram van de totale OK / gefaald / geblokkeerd / niet uitgevoerde testcases
                    • Staafdiagram van de resultaten per trefwoord
                    • @@ -399,11 +389,10 @@

                      Totaal aantal bugs voor elke testcase

                      Dit rapport toont elke testcase met alle bugs ervoor ingediend voor het gehele project. -Dit rapport is alleen beschikbaar als een Bug Tracking Systeem is aangesloten

                      ". - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC']= "Testcases aan testplan toevoegen en verwijderen"; // testSetAdd +Dit rapport is alleen beschikbaar als een Bug Tracking Systeem is aangesloten

                      " . + + // ------------------------------------------------------------------------------------------ + $TLS_htmltext_title['planAddTC'] = "Testcases aan testplan toevoegen en verwijderen"; // testSetAdd $TLS_htmltext['planAddTC'] = "

                      Doel:

                      Hiermee kan een gebruiker (met leider rol) testcases aan een testplan toevoegen of verwijderen.

                      @@ -412,10 +401,10 @@
                    • Klik op een testsuite om alle onderliggende testsuites en testcases te zien.
                    • Als u klaar bent klik op de 'Testcases toevoegen/verwijderen' knop om de testcases toe te voegen of te verwijderen. Let op: Het is niet mogelijk om dezelfde testcase meerdere keren toe te voegen.
                    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment']= "Testers toewijzen"; +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Testers toewijzen"; $TLS_htmltext['tc_exec_assignment'] = "

                      Doel

                      Op deze pagina kunnen testleiders testcases binnen een testplan aan testers toewijzen.

                      @@ -432,15 +421,15 @@
                    • Klik op de root-node in de boomstructuur (het testproject).
                    • Als er toegewezen testcases zijn, zal je een knop om alle toegewezen testcases te verwijderen. Als u erop klikt en bevestigd, alle testers van alle testcases verwijderd worden.
                    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC']= "Update testcases in het testplan"; -$TLS_htmltext [ 'planUpdateTC'] = "

                      Doel

                      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Update testcases in het testplan"; +$TLS_htmltext['planUpdateTC'] = "

                      Doel

                      Op deze pagina kunt een testcase bijwerken naar een nieuwere (andere) versie als een test -specificatie is veranderd. Het gebeurt vaak dat sommige functies worden verduidelijkt tijdens de test.". -"Als de gebruiker een testspecificatie wijzigt, moeten veranderingen ook doorgegeven aan het testplan. Anders behoud het ". -"testplan de originele versie om zeker te zijn, dat de resultaten verwijzen naar de juiste tekst van een testcase.

                      +specificatie is veranderd. Het gebeurt vaak dat sommige functies worden verduidelijkt tijdens de test." . + "Als de gebruiker een testspecificatie wijzigt, moeten veranderingen ook doorgegeven aan het testplan. Anders behoud het " . + "testplan de originele versie om zeker te zijn, dat de resultaten verwijzen naar de juiste tekst van een testcase.

                      Aan de slag

                        @@ -448,16 +437,15 @@
                      1. Kies een nieuwe versie in de combo-box voor een bepaalde testcase.
                      2. Klik op de knop 'testplan bijwerken' om de wijzigingen in te dienen.
                      3. Om te controleren: Open tests uitvoeren pagina om de tekst van de testcase(s) te bekijken.
                      4. -
                      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency']= "Geef testen een hoge of lage urgentie"; +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Geef testen een hoge of lage urgentie"; $TLS_htmltext['test_urgency'] = "

                      Doel

                      -

                      TestLink maakt het mogelijk om de urgentie van een testsuite testcases te beïnvloeden. -Test prioriteit is afhankelijk van zowel belang van testcase en urgentie gedefinieerd in -het testplan. De test leider moet een set van testcases selecteren die eerst moeten worden uitgevoerd. -Dit helpt omdat eerst de belangrijkste uitgevoerd worden, +

                      TestLink maakt het mogelijk om de urgentie van een testsuite testcases te beïnvloeden. +Test prioriteit is afhankelijk van zowel belang van testcase en urgentie gedefinieerd in +het testplan. De test leider moet een set van testcases selecteren die eerst moeten worden uitgevoerd. +Dit helpt omdat eerst de belangrijkste uitgevoerd worden, ook onder tijdsdruk.

                      Aan de slag

                      @@ -469,8 +457,8 @@ belangrijke wijzigingen.
                    • Klik op de knop 'Opslaan' om de wijzigingen in te dienen.
                    • -

                      Bijvoorbeeld een testcase met een groot belang in een testsuite met een lage urgentie ". -"zal Medium prioriteit krijgen."; -// ------------------------------------------------------------------------------------------ - +

                      Bijvoorbeeld een testcase met een groot belang in een testsuite met een lage urgentie " . + "zal Medium prioriteit krijgen."; +// ------------------------------------------------------------------------------------------ + ?> diff --git a/locale/pl_PL/description.php b/locale/pl_PL/description.php index 47a5edb1d6..9922e2fabf 100644 --- a/locale/pl_PL/description.php +++ b/locale/pl_PL/description.php @@ -1,82 +1,82 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - * ----------------------------------------------------------------------------------- */ - -// printFilter.html +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + * ----------------------------------------------------------------------------------- */ + +// printFilter.html $TLS_hlp_generateDocOptions = "

                      Opcje dokumentu

                      -

                      Ta tabela, umożliwia użytkownikowi selekcje przypadków testowych przed ich wyświetleniem. +

                      Ta tabela, umożliwia użytkownikowi selekcje przypadków testowych przed ich wyświetleniem. Znaczone pola będą pokazane w wygenerowanym dokumencie. -W celu zmiany wyświetlanych informacji, zaznacz lub odznacz wybrane pola, następnie kliknij przycisk Filtruj, -w dalszej kolejności na drzewie, w dolnej części okna zaznacz odpowiedni poziom szczegółów zawartych w dokumencie. +W celu zmiany wyświetlanych informacji, zaznacz lub odznacz wybrane pola, następnie kliknij przycisk Filtruj, +w dalszej kolejności na drzewie, w dolnej części okna zaznacz odpowiedni poziom szczegółów zawartych w dokumencie.

                      Nagłówek dokumentu: Użytkownik ma możliwość przefiltrowania nagłówków dokumentu. -Nagłówek powinien zawierać informacje takie jak: wprowadzenie, zakres, odnośniki do dokumentów, +Nagłówek powinien zawierać informacje takie jak: wprowadzenie, zakres, odnośniki do dokumentów, metodologie testów, ograniczenia testów.

                      -

                      Budowa przypadków testowych: Użytkownik może przefiltrować informacje wchodzące w skład budowy głównej części przypadku testowego. -W skład budowy przypadku testowego wchodzą: Cel przypadku testowego, kroki i oczekiwane rezultaty, +

                      Budowa przypadków testowych: Użytkownik może przefiltrować informacje wchodzące w skład budowy głównej części przypadku testowego. +W skład budowy przypadku testowego wchodzą: Cel przypadku testowego, kroki i oczekiwane rezultaty, autor przypadku, pola niestandardowe, powiązane słowa kluczowe i wymagania.

                      -

                      Podsumowanie testu: Użytkownik ma możliwość przefiltrowania samego celu testu poprzez tytuł przypadku testowego, +

                      Podsumowanie testu: Użytkownik ma możliwość przefiltrowania samego celu testu poprzez tytuł przypadku testowego, ale nie zostaną w ten sposób wyłączone pozostałe informacje z budowy głównej części przypadku testowego. -Podsumowanie testu zostało tylko częściowo oddzielone od budowy przypadku testowego, tak aby była możliwość wyświetlania przypadków -tylko w formie tytułu i celu testu (bez wyświetlania kroków). +Podsumowanie testu zostało tylko częściowo oddzielone od budowy przypadku testowego, tak aby była możliwość wyświetlania przypadków +tylko w formie tytułu i celu testu (bez wyświetlania kroków). Jeśli użytkownik wyświetli tylko główną część testu, będzie w nim zawarty cel testu .

                      -

                      Spis treści: Opcja ta tworzy listę wszystkich tytułów przypadków testowych +

                      Spis treści: Opcja ta tworzy listę wszystkich tytułów przypadków testowych z wewnętrznymi linkami w postaci hipertekstu (niezależne fragmenty tekstu połączone hiperlinkami).

                      -

                      Format wyjściowy: Istnieją dwie możliwości: HTML i MS Word. -Przeglądarki odwołują sie do komponentu MS Word w drugiej kolejności.

                      "; - -// testPlan.html +

                      Format wyjściowy: Istnieją dwie możliwości: HTML i MS Word. +Przeglądarki odwołują sie do komponentu MS Word w drugiej kolejności.

                      "; + +// testPlan.html $TLS_hlp_testPlan = "

                      Plan testu

                      Ogólne zasady działania

                      Plan testu jest systemowym podejściem do testowania oprogramowania. Możesz ułożyć testowanie poszczególnych struktur produktów pod względem czasu i osiągniętych rezultatów.

                      Przeprowadzanie testu

                      -

                      Fragment ten jest tym gdzie użytkownik, może przeprowadzić przypadki testowe (opisać rezultaty testu) i -wydrukować strukture przypadku testowego jako plan testu. Fragment ten umożliwia użytkownikowi śledzenie rezultatów przeprowadzanych przez niego przypadków testowych. -

                      +

                      Fragment ten jest tym gdzie użytkownik, może przeprowadzić przypadki testowe (opisać rezultaty testu) i +wydrukować strukture przypadku testowego jako plan testu. Fragment ten umożliwia użytkownikowi śledzenie rezultatów przeprowadzanych przez niego przypadków testowych. +

                      ZarzÄ…dzanie planem testu

                      -

                      Ten fragment, który jest dostępny tylko dla zarzadzających projektem, pozwala użytkownikowi na administrowanie planem testu. -Administrowanie planem testu zawiera tworzenie/edytowanie/usuwanie planów, +

                      Ten fragment, który jest dostępny tylko dla zarzadzających projektem, pozwala użytkownikowi na administrowanie planem testu. +Administrowanie planem testu zawiera tworzenie/edytowanie/usuwanie planów, dodawanie/edytowanie/usuwanie/aktualizacje przypadków testowych w planie testu, tworzenie struktur tak samo jak określenie, kto może obejrzeć który plan.
                      -Użytkownicy z pozwoleniem zarzącającego projektem mogą także określić wagę/ryzyko i przynależność -struktury przypadku testowego (kategorie) oraz tworzenie kroków przypadku testowego.

                      - -

                      Uwaga: Jest możliwe, że użytkownicy mogą nie widzieć listy dropdown zawierającej wszystkie plany testów. -W takiej systuacji wszystkie linki (za wyjątkiem tych, które zostałe aktywowane przez zarządzajacego projektem) będą nie aktywne. Jeżeli jesteś w takiej sytuacji skontaktuj się z zarządzajacym projektem -lub administratorem w celu udostępnienia odpowiednich praw do projektu lub stworzenia planu test dla ciebie.

                      "; - -// custom_fields.html +Użytkownicy z pozwoleniem zarzącającego projektem mogą także określić wagę/ryzyko i przynależność +struktury przypadku testowego (kategorie) oraz tworzenie kroków przypadku testowego.

                      + +

                      Uwaga: Jest możliwe, że użytkownicy mogą nie widzieć listy dropdown zawierającej wszystkie plany testów. +W takiej systuacji wszystkie linki (za wyjątkiem tych, które zostałe aktywowane przez zarządzajacego projektem) będą nie aktywne. Jeżeli jesteś w takiej sytuacji skontaktuj się z zarządzajacym projektem +lub administratorem w celu udostępnienia odpowiednich praw do projektu lub stworzenia planu test dla ciebie.

                      "; + +// custom_fields.html $TLS_hlp_customFields = "

                      pola niestandardowe

                      Klika faktów odnośnie wprowadzania pól niestandardowych:

                        @@ -111,16 +111,16 @@
                      • Uruchomienie projektu planu testu. Użytkownik może zmienić wartość w trakcie tworzenia planu testu ( dodawać przypadek testowy do planu testu)
                      • DostÄ™pność dla. Użytkownik wybiera rodzaju pola do którego przynależy wartość.
                      -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

                      Przeprowadzanie przypadku testowego

                      Przeprowadzanie przypadku testowego. Samo przeprowadzanie przypadku testowego jest w niewielkim stopniu powiązane od rezultatu przypadku testowego (poprawny, niepoprawny, wstrzymany) w przeciwieństwie do wyznaczonego elementu budowy.

                      Można stworzyć dostęp do systemu śledzenie błędu. Użytkownik może bezpośrednio dodawać zgłoszenia o nowych błędach - i wyszukiwać zgłoszenia o starych.

                      "; - -//bug_add.html + i wyszukiwać zgłoszenia o starych.

                      "; + +// bug_add.html $TLS_hlp_btsIntegration = "

                      Dodawanie informacji o błędzie do przypadku testowego

                      (tylko jeżeli jest skonfigurowane) TestLink ma bardzo proste powiądzenie z systemem śledzenia błędu Bug Tracking Systems (BTS), @@ -130,7 +130,7 @@

                    • Umieść nowy błąd.
                    • WyÅ›wietl informacje o istniejÄ…cym błędzie.
                    -

                    +

                    Procedura dodania błędu

                    @@ -139,67 +139,66 @@

                  • Krok 2: zapisać BUGID/ numer błędu nadany przez BTS.
                  • Krok 3: wpisać BUGID w rubryce.
                  • Krok 4: użyć przycisku dodaj błąd.
                  • -
                  +
                Po zamknięciu strony z dodawaniem nowego błędu, zobaczysz odpowiednie informacje o błędzie. -

                "; - -// execFilter.html +

                "; + +// execFilter.html $TLS_hlp_executeFilter = "

                Tworzenie filtra i budowy dla przeprowadzenia testu.

                -

                Lewe okienko zawiera opcje zarządzania przypisanymi przypadkami testowymi" . -"Plan testu i tabela z ustawieniami oraz filtrem. Filtry, które są udostepniane użytkownikowi " . -"w celu ulepszenia oferowanego zestawu " . -"Ustaw swój filtr, przycisnij 'Zatwierdź' i zaznacz odpowiedni przypadek testowy " . -"z menu drzewa .

                +

                Lewe okienko zawiera opcje zarządzania przypisanymi przypadkami testowymi" . + "Plan testu i tabela z ustawieniami oraz filtrem. Filtry, które są udostepniane użytkownikowi " . + "w celu ulepszenia oferowanego zestawu " . + "Ustaw swój filtr, przycisnij 'Zatwierdź' i zaznacz odpowiedni przypadek testowy " . + "z menu drzewa .

                Struktura

                -

                Użytkownicy muszą wybrać strukture, która będzie powiązana z wynikami testu. " . -"struktury są podstawowym komponentem dla bieżącego planu testu. Każdy przypadek testowy " . -"może być przeprowadzony więcej niż jeden raz jeżeli to wynika, z jego struktury. Jednakże liczy się tylko ostatni wynik. +

                Użytkownicy muszą wybrać strukture, która będzie powiązana z wynikami testu. " . + "struktury są podstawowym komponentem dla bieżącego planu testu. Każdy przypadek testowy " . + "może być przeprowadzony więcej niż jeden raz jeżeli to wynika, z jego struktury. Jednakże liczy się tylko ostatni wynik.
                Struktura może być tworzona poprzez zarzadzajacego projektem przez strone umożliwiającą utworzenie nowej struktury.

                Filtr Numeru ID

                -

                Użytkownicy mogą filtrować przypadek testowy poprez indywidualny numer ID. Numer ID jest nadawany automatycznie w czasie tworzenia przypadku testowego. Pusty pasek filtra oznacza, że test nie jest obowiązujący.

                +

                Użytkownicy mogą filtrować przypadek testowy poprez indywidualny numer ID. Numer ID jest nadawany automatycznie w czasie tworzenia przypadku testowego. Pusty pasek filtra oznacza, że test nie jest obowiązujący.

                Filtr Wagi

                -

                Użytkownicy moga filtrować przypadek testowy w zależności od jego wagi. Waga testu składa się z kilku zmiennych" . -"to jak test jest pilny wynika z bieżacego planu testu. Na przykład pryioryet 'WYSOKI' przypadku testowego" . -"Na przykład pryioryet 'WYSOKI' " . " jest wyznaczony w przypadkach gdy pilność lub ważność oznaczona jako 'WYSOKA', a drugi czynnik oznaczony przynajmniej 'ŚREDNI'.

                +

                Użytkownicy moga filtrować przypadek testowy w zależności od jego wagi. Waga testu składa się z kilku zmiennych" . + "to jak test jest pilny wynika z bieżacego planu testu. Na przykład pryioryet 'WYSOKI' przypadku testowego" . + "Na przykład pryioryet 'WYSOKI' " . + " jest wyznaczony w przypadkach gdy pilność lub ważność oznaczona jako 'WYSOKA', a drugi czynnik oznaczony przynajmniej 'ŚREDNI'.

                Filtrowanie rezultatów

                Użytkownicy mogą filtrować przypadki testowe poprzez rezultaty. Rezultaty są wynikem z wykonania określonej struktury przypadku testowego. -Przypadki testowe moga być (poprawne, niepoprawne, zablokowane, nieprzeprowadzone)." . -"Ten filtr jest domyślnie wyłączony.

                +Przypadki testowe moga być (poprawne, niepoprawne, zablokowane, nieprzeprowadzone)." . + "Ten filtr jest domyślnie wyłączony.

                Filtr użytkownika

                -

                Użytkownicy mogą filtrować przypadek testowy pod wględem komu zostały przypisane. Check-box przewiduje także opcję " . -"\" nieprzpisane\" testy, które w rezultatach są umieszczane jako dodatkowe.

                "; -/* -

                Najczęstsze wyniki

                -

                'Najczęstsze wyniki' według ustawień domyślnych ta opcja jest odznaczona check-boxem, wówczas drzewko będzie ułożone -zgodnie ze strukturą wybraną z listy dropdown box. W takiej sytuacji drzewko będzie ułożone -ze względu status przypadku testowego. -
                Przykład: Użytkownik, zaznaczył strukture 2 z listy dropdown box i jednocześnie zaznaczy checkbox opcje -'Najczęstsze wyniki'. Wszystkie przypadki testowe są wyświetlane uwzględniając ich status w strukturze 2. -Więc, jeżeli przypadek testowy 1 będzie określony jako poprawny w strukturze 2 będzie zaznaczony na zielono. -
                Jeżeli użytkownik zdecyduje się zaznaczyć 'najbardziej bieżące' poprzez checkbox drzewko będzie -oznaczone kolorem takim jakim oznaczone są najcześciej przypadki testowe. -
                Przykład: Użytkownik zaznaczył strukture 2 z dropdown box i zaznaczył poprzez checkbox opcje - 'najbardziej bierzące' przypadki testowe. Wtedy zostaną pokazane wszystkie przypadki testowe ze statusem -'najbardziej bierzące'. Więc, jeżeli przypadek testowy 1 jest poprawny w strukturze 3, wtedy jeżeli użytkownik zaznaczy przypadek w strukturze 2 -, to przypadek będzie oznaczony na zielono.

                - */ - - -// newest_tcversions.html +

                Użytkownicy mogą filtrować przypadek testowy pod wględem komu zostały przypisane. Check-box przewiduje także opcję " . + "\" nieprzpisane\" testy, które w rezultatach są umieszczane jako dodatkowe.

                "; +/* + *

                Najczęstsze wyniki

                + *

                'Najczęstsze wyniki' według ustawień domyślnych ta opcja jest odznaczona check-boxem, wówczas drzewko będzie ułożone + * zgodnie ze strukturą wybraną z listy dropdown box. W takiej sytuacji drzewko będzie ułożone + * ze względu status przypadku testowego. + *
                Przykład: Użytkownik, zaznaczył strukture 2 z listy dropdown box i jednocześnie zaznaczy checkbox opcje + * 'Najczęstsze wyniki'. Wszystkie przypadki testowe są wyświetlane uwzględniając ich status w strukturze 2. + * Więc, jeżeli przypadek testowy 1 będzie określony jako poprawny w strukturze 2 będzie zaznaczony na zielono. + *
                Jeżeli użytkownik zdecyduje się zaznaczyć 'najbardziej bieżące' poprzez checkbox drzewko będzie + * oznaczone kolorem takim jakim oznaczone są najcześciej przypadki testowe. + *
                Przykład: Użytkownik zaznaczył strukture 2 z dropdown box i zaznaczył poprzez checkbox opcje + * 'najbardziej bierzące' przypadki testowe. Wtedy zostaną pokazane wszystkie przypadki testowe ze statusem + * 'najbardziej bierzące'. Więc, jeżeli przypadek testowy 1 jest poprawny w strukturze 3, wtedy jeżeli użytkownik zaznaczy przypadek w strukturze 2 + * , to przypadek będzie oznaczony na zielono.

                + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

                Najnowsza wersja powiązanych przypadków testowych

                -

                Jest analizowany cały zestaw przypadków testowych powiązanych do planu testu i lista najnowszych przypadków testowych +

                Jest analizowany cały zestaw przypadków testowych powiązanych do planu testu i lista najnowszych przypadków testowych (w zakresie bieżącego zestawu planu testu). -

                "; - - -// requirementsCoverage.html +

                "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

                Zaspokojenie wymagań


                Własność systemu, która pozwala na dokumentacje pokrycia wymagań systemu lub użytkownika, przez przypadki testowe. @@ -207,14 +206,14 @@

                Zestawy wymagań

                Wymagania są zebrane w 'Zestawach wymagań' dokumencie powiązanym z -projektem testu.
                TestLink nie obejmuje jednocześnie zestawu wymagań, -a także wymagań samych w sobie. Więc, wersja dokumentu powinna być dodana po +projektem testu.
                TestLink nie obejmuje jednocześnie zestawu wymagań, +a także wymagań samych w sobie. Więc, wersja dokumentu powinna być dodana po specyfikacji Tytuł. -Użytkownik może dodać prosty opis notatki do szkicu pola.

                +Użytkownik może dodać prosty opis notatki do szkicu pola.

                Przypisana liczba wymagań służy do -oceny pokrycia wymagań,w przypadku kiedy nie wszystkie wymagania są dodane (zaimportowane). -Wartość 0 oznacza bieżącą liczbę wymagań używaną w celu określenia metryki.

                +oceny pokrycia wymagań,w przypadku kiedy nie wszystkie wymagania są dodane (zaimportowane). +Wartość 0 oznacza bieżącą liczbę wymagań używaną w celu określenia metryki.

                Przykład SRS zawiera 200 wymagań ale tylko 50 jest dodane do TestLink. Pokrycie testu jest 25% (jeżeli wszystkie te wymagania będą dodane).

                @@ -222,26 +221,25 @@

                Kliknij na tytule w celu stworzenia specyfikacji wymagań. Możesz tworzyć, edytować, usuwać lub importować zestaw wymagań dla dokumentu. Każde z wymagań ma tytuł, szkicu i status. Status powinien być \"Normalny\" lub \"Nietestowalny\". Nie testowalne wymagania nie są włączane do metryki. -Ten parametr powinien być użyty dla niezastosowanych cech i żle zaprojektowanych wymagań.

                +Ten parametr powinien być użyty dla niezastosowanych cech i żle zaprojektowanych wymagań.

                Możesz stworzyć nowy przypadek testowy. Możesz stworzyć nowe przypadeki testowe dla wymagań poprzez użycie wielo wątkowej akcji z zaznaczonymi wymaganiami w oknie specyfikacji. Te przypadki testowe są stworzone w schemacie testu -z nazwą określoną w definicji (default is: $tlCfg->req_cfg->default_testsuite_name = +z nazwą określoną w definicji (default is: $tlCfg->req_cfg->default_testsuite_name = \" Zestaw testowy stworzony według wymagań - Auto\";). Tytuł i szkic są kopiowane do tych przypadków testowych.

                -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

                Odnośnie 'Zapisz pola niestandardowe'

                -Jeżeli masz zdefiniowane i przypisane do Projektu Testu,
                +Jeżeli masz zdefiniowane i przypisane do Projektu Testu,
                pola niestandardowe z:
                'Display on test plan design=true' and
                'Enable on test plan design=true'
                Zobaczysz tą strone tylko dla tych przypadków testowych, które są przypisane do planu testu. -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/pl_PL/strings.txt b/locale/pl_PL/strings.txt index 0127bf456d..08f8bd73a0 100644 --- a/locale/pl_PL/strings.txt +++ b/locale/pl_PL/strings.txt @@ -75,7 +75,7 @@ $TLS_author = "Autor przypadku"; $TLS_automated = "Automatyczny"; $TLS_automatic = "Automatyczny"; $TLS_availability = "Dostępność"; -$TLS_build = "Wydanie"; +$TLS_build = "build"; $TLS_check_uncheck_all = "Zaznacz / odznacz wszystko"; $TLS_check_uncheck_all_for_remove = "zaznacz / odznacz wszystko do usunięcia"; $TLS_click_to_set_open = "Zamknięte (kliknij, aby otworzyć)"; @@ -810,22 +810,22 @@ $TLS_btn_apply_filter = $TLS_btn_apply; $TLS_btn_reset_filters = "Resetuj filtry"; $TLS_caption_nav_filters = "Filtry"; $TLS_caption_nav_settings = "Ustawienia"; -$TLS_current_build = "Aktualne wydanie"; +$TLS_current_build = "Aktualne build"; $TLS_filter_owner = "Przypisane do"; $TLS_filter_result = "Wynik"; $TLS_filter_on = "dnia"; $TLS_filter_tcID = "ID przypadku"; $TLS_include_unassigned_testcases = "dołącz nieprzydzielone przypadki"; $TLS_filter_result_all_builds = "wszystkie wydania"; -$TLS_filter_result_any_build = "dowolne wydanie"; -$TLS_filter_result_specific_build = "wybrane wydanie"; +$TLS_filter_result_any_build = "dowolne build"; +$TLS_filter_result_specific_build = "wybrane build"; $TLS_filter_result_latest_execution = "najnowsze wykonanie"; -$TLS_filter_result_current_build = "Wydanie wybrane do wykonania"; +$TLS_filter_result_current_build = "build wybrane do wykonania"; $TLS_filter_somebody = "ktoś"; -$TLS_exec_build = "Wydanie do wykonania"; -$TLS_assign_build = "Wydanie do przydzielenia"; +$TLS_exec_build = "build do wykonania"; +$TLS_assign_build = "build do przydzielenia"; $TLS_filter_result_all_prev_builds = "wszystkie poprzednie wydania"; -$TLS_filter_result_any_prev_build = "dowolne poprzednie wydanie"; +$TLS_filter_result_any_prev_build = "dowolne poprzednie build"; $TLS_document_id = "ID dokumentu"; $TLS_req_type = "Rodzaj wymagania"; $TLS_req_spec_type = "Rodzaj spec. wym."; @@ -907,7 +907,7 @@ $TLS_skip_frozen_req = "Pomiń zamrożone wymagania"; // gui/templates/inc_attachments.tpl -$TLS_alt_delete_build = "Kliknij tu, aby usunąć to wydanie"; +$TLS_alt_delete_build = "Kliknij tu, aby usunąć to build"; $TLS_alt_delete_attachment = "Kliknij tu, aby usunąć ten załącznik"; $TLS_attached_files = "Załączone pliki"; $TLS_attachment_feature_disabled = "załączanie wyłączone"; @@ -923,7 +923,7 @@ $TLS_show_inactive_tplans = "Pokaż nieaktywne plany testów"; $TLS_send_results = "Wyślij rezultaty"; $TLS_send_to = "Do:"; $TLS_subject = "Temat:"; -$TLS_title_active_build = "Aktywne wydanie"; +$TLS_title_active_build = "Aktywne build"; $TLS_title_report_type = "Format raportu"; $TLS_via_email = "emailem"; $TLS_link_results_import = "Importuj wyniki"; @@ -1014,9 +1014,9 @@ $TLS_enter_build_notes = "Opis"; // "Description should include: list of delivered packages, approvals, status, etc."; $TLS_open = "Otwórz"; $TLS_title_build_2 = "Zarządzanie wydaniami"; -$TLS_title_build_create = "Utwórz nowe wydanie"; -$TLS_title_build_update = "Uaktualnij wydanie"; -$TLS_title_build_edit = "Edytuj wydanie"; +$TLS_title_build_create = "Utwórz nowe build"; +$TLS_title_build_update = "Uaktualnij build"; +$TLS_title_build_edit = "Edytuj build"; $TLS_warning_empty_build_name = "Nadaj nazwę dla wydania!"; $TLS_copy_to_all_tplans = "Skopiuj do wszystkich planów testów"; $TLS_release_date = "Data publikacji"; @@ -1407,16 +1407,16 @@ $TLS_bug_create_into_bts = "Utwórz raport defektu"; $TLS_bug_copy_from_latest_exec = "Skopiuj zagadnienia z ostatniego wykonania"; $TLS_btn_export_testcases = "Eksport przypadków testowych"; -$TLS_build_is_closed = "Wydanie jest zamknięte"; +$TLS_build_is_closed = "build jest zamknięte"; $TLS_builds_notes = "Opis wydania"; $TLS_bulk_tc_status_management = "Hurtowe zarządzanie statusem przypadków"; -$TLS_closed_build = "wydanie zamknięte. Nie można dokonać żadnej operacji."; +$TLS_closed_build = "build zamknięte. Nie można dokonać żadnej operacji."; $TLS_date_time_run = "Data"; $TLS_details = "Szczegóły"; $TLS_edit_notes = "edytuj uwagi"; $TLS_execute_and_save_results ="Wykonaj i zapisz wynik"; -$TLS_exec_any_build = "(dowolne wydanie)"; -$TLS_exec_current_build = "(aktualne wydanie)"; +$TLS_exec_any_build = "(dowolne build)"; +$TLS_exec_current_build = "(aktualne build)"; $TLS_exec_notes = "Uwagi"; $TLS_exec_status = $TLS_status; $TLS_execution_history = "Historia wykonania przypadku"; @@ -1846,7 +1846,7 @@ $TLS_all_reqspecs_in_tproject = "Wszystkie specyfikacje wym. w projekcie testowy // ----- gui/templates/resultsAllBuilds.tpl ----- -$TLS_th_build = "Wydanie"; +$TLS_th_build = "build"; $TLS_th_tc_total = "Ilość testów"; $TLS_th_tc_assigned = "Przypisane"; $TLS_title_met_of_build = "Metryki dla wydania"; @@ -1873,14 +1873,14 @@ $TLS_info_bugs_per_tc_report = "Raport pokazuje wszystkie defekty powiązane do $TLS_info_resultsTC_report = "Raport pokazuje wyniki ostatniego wykonania przypadku testowego dla każdego z wydań, " . "analizując przypadki zarówno przypisanie jak i nie przypisane do testerów.
                " . "
                Przykład:
                Przypadek testowy był wykonany w nastepującej kolejności: " . - "
                Wydanie 1.0 - Pozytywny; Wydanie 1.0 - Negatywny
                -> Najnowsze wykonanie dla wydania 1.0 " . + "
                build 1.0 - Pozytywny; build 1.0 - Negatywny
                -> Najnowsze wykonanie dla wydania 1.0 " . "jest Negatywny ponieważ drugie wykonanie nadpisuje starsze wyniki.

                " . - "Kolumny \"[Najnowsze wydanie]\" i \"Najnowsze wykonanie\" pokazują dane tak jak przedstawiono " . + "Kolumny \"[Najnowsze build]\" i \"Najnowsze wykonanie\" pokazują dane tak jak przedstawiono " . "na przykładzie:
                Przypadek testowy był wykonany w nastepującej kolejności:
                " . - "Wydanie 1.0 - Pozytywny; Wydanie 1.0 - Negatywny; Wydanie 2.0 - Zablokowany; " . - "Wydanie 1.0 - Pozytywny
                -> [Najnowsze wydanie] : Zablokowany ponieważ Wydanie 2.0 jest " . - "najnowszym utworzonym wydaniem w którym wykonano test.
                -> \"Najnowsze wykonanie\" : " . - "Pozytywny ponieważ Wydanie 1.0 było wykonane jako ostatnie.

                Wyświetlany priorytet ". + "build 1.0 - Pozytywny; build 1.0 - Negatywny; build 2.0 - Zablokowany; " . + "build 1.0 - Pozytywny
                -> [Najnowsze build] : Zablokowany ponieważ build 2.0 jest " . + "najnowszym utworzonym buildm w którym wykonano test.
                -> \"Najnowsze wykonanie\" : " . + "Pozytywny ponieważ build 1.0 było wykonane jako ostatnie.

                Wyświetlany priorytet ". "jest wyliczany na podstawie wagi aktualnie podpiętej wersji przypadku testowego."; @@ -1914,8 +1914,8 @@ $TLS_no_blocked_with_tester = "Nie ma żadnych przypadków testowych (Z PRZYPISA // ----- gui/templates/resultsByTesterPerBuild.tpl ----- -$TLS_caption_results_by_tester_per_build = "Wyniki dla testera per wydanie"; -$TLS_results_by_tester_per_build = "Wyniki dla testera per wydanie"; +$TLS_caption_results_by_tester_per_build = "Wyniki dla testera per build"; +$TLS_results_by_tester_per_build = "Wyniki dla testera per build"; $TLS_progress = "Postęp planu [%]"; $TLS_executions_without_assignment = "Nieprzypisane wykonania"; $TLS_no_testers_per_build = "W tym planie testów nie ma przypisanych testerów w żadnym otwartym wydaniu."; @@ -2280,30 +2280,30 @@ $TLS_warning_empty_filename = "Podaj nazwę pliku"; // ----- gui/templates/buildView.tpl ----- -$TLS_alt_active_build = "Aktywne wydanie"; -$TLS_alt_edit_build = "Edytuj wydanie"; -$TLS_alt_open_build = "Otwórz wydanie"; +$TLS_alt_active_build = "Aktywne build"; +$TLS_alt_edit_build = "Edytuj build"; +$TLS_alt_open_build = "Otwórz build"; $TLS_btn_build_create = "Utwórz"; $TLS_no_builds = "Nie zdefiniowano żadnego wydania dla tego planu testów!"; $TLS_th_description = "Opis"; $TLS_th_open = "Otwórz"; $TLS_title_build_list = "Lista istniejących wydań"; -$TLS_warning_delete_build = "

                Zamierzasz usunąć wydanie: %s

                " . +$TLS_warning_delete_build = "

                Zamierzasz usunąć build: %s

                " . "

                Wszystkie powiązane z nim dane (m.in, WYNIKI TESTÓW tego wydania) również zostaną usunięte!

                " . "

                Jesteś pewien, że tego chcesz?

                "; -$TLS_builds_description = "Wydanie jest to obiekt podrzędny planu testów. Każde wydanie jest powiązane z aktywnym planem testów.
                " . +$TLS_builds_description = "build jest to obiekt podrzędny planu testów. Każde build jest powiązane z aktywnym planem testów.
                " . "Wydania są identyfikowane po tytułach. Nazwa wydania powinna odpowiadać nazwie Release Candidatów umieszczanych w svn, w gałęzi released.

                " . "Opis wydania musi zawierać:" . "
                -> numer rewizji SVN – dla zapewnienia identyfikowalności testów i kodu źródłowego." . "
                -> link do zagadnienia w Redmine, która grupuje błędy zgłaszane podczas danego cyklu testowego.
                " . "powinien też zawierać:
                ->listę dostarczonych paczek, poprawek lub funkcjonalności, statusy, itd.
                " . "
                Wydania majÄ… dwa atrybuty:
                " . - "Aktywny / Nieaktywny – okreÅ›la czy wydanie może być używane. " . + "Aktywny / Nieaktywny – okreÅ›la czy build może być używane. " . "Nieaktywnych wydaÅ„ nie ma dostÄ™pnych na stronie wykonywania testów i na stronach raportów.
                " . "Otwarte / ZamkniÄ™te – Wyniki testów mogÄ… być modyfikowane tylko dla otwarych wydaÅ„."; $TLS_build_copy_help = "JeÅ›li zaznaczysz opcjÄ™: " . "[{$TLS_copy_to_all_tplans}] " . " ,nazwa wydania bÄ™dzie sprawdzona we wszystkich planach testów tego projektu testowego" . - "i jeÅ›li taka nazwa nie istnieje, wydanie zostanie utworzone."; + "i jeÅ›li taka nazwa nie istnieje, build zostanie utworzone."; // ----- gui/templates/planView.tpl ----- @@ -2529,7 +2529,7 @@ $TLS_desc_project_inventory_management="ZarzÄ…dzanie sprzÄ™tem testowym"; $TLS_desc_role_management = "ZarzÄ…dzanie rolami"; -$TLS_desc_testplan_create_build = "Utwórz / edytuj wydanie"; +$TLS_desc_testplan_create_build = "Utwórz / edytuj build"; $TLS_desc_testplan_execute = "Wykonanie planu"; $TLS_desc_testplan_metrics = "Statystyki planu testowego"; $TLS_desc_testplan_planning = "Tworzenie planu testowego"; @@ -2673,7 +2673,7 @@ $TLS_text_counter_feedback = "Możesz wprowadzić do %d znaków. ZostaÅ‚o "; // ----- lib/general/frmWorkArea.php ----- -$TLS_create_a_build = "Utwórz wydanie"; +$TLS_create_a_build = "Utwórz build"; $TLS_no_build_warning_part1 = "Nie znaleziono wydania utworzonego dla tego planu testów."; $TLS_no_build_warning_part2 = "Najpierw go utwórz, albo zapytaj administratora."; @@ -2709,10 +2709,10 @@ $TLS_kword_to_be_assigned_to_testcases = "SÅ‚owa kluczowe do przypisania do przy // ----- lib/plan/buildNew.php ----- -$TLS_cannot_add_build = "Wydanie nie może zostać dodane!"; +$TLS_cannot_add_build = "build nie może zostać dodane!"; $TLS_cannot_delete_build = "Nie można usunąć wydania!"; $TLS_cannot_update_build = "Błąd podczas aktualizacji wydania!"; -$TLS_warning_duplicate_build = "Istnieje już wydanie z tym identyfikatorem"; +$TLS_warning_duplicate_build = "Istnieje już build z tym identyfikatorem"; @@ -2964,7 +2964,7 @@ $TLS_info_testPlanWithCF = "This Report shows all test cases with any custom fie // ----- lib/results/resultsTC.php ----- -$TLS_result_on_last_build = '[Najnowsze wydanie]'; +$TLS_result_on_last_build = '[Najnowsze build]'; $TLS_tcversion_indicator = ' [v%s]'; @@ -3783,7 +3783,7 @@ $TLS_testCaseStatus_final = 'Wersja finalna'; // printDocument.php $TLS_report_test_plan_design = 'Plan Testów'; $TLS_report_test_plan_execution = 'Raport z wykonania Planu Testów'; -$TLS_report_test_plan_execution_on_build = 'Raport z wykonania Planu Testów (wydanie)'; +$TLS_report_test_plan_execution_on_build = 'Raport z wykonania Planu Testów (build)'; $TLS_execution_time_metrics = 'Metryki czasów wykonania przypadków'; $TLS_gen_test_plan_design_report = 'Generate Test Plan Design Report'; $TLS_right_pane_test_plan_tree = "Click to display Test Plan Tree (on right pane)"; diff --git a/locale/pl_PL/texts.php b/locale/pl_PL/texts.php index 06e3d3bb9d..43b4f6e429 100644 --- a/locale/pl_PL/texts.php +++ b/locale/pl_PL/texts.php @@ -14,79 +14,74 @@ * * * Revisions history is not stored for the file - * + * * @package TestLink * @author Martin Havlat - * @copyright 2003-2009, TestLink community + * @copyright 2003-2009, TestLink community * @version CVS: $Id: texts.php,v 1.29 2010/07/22 14:14:44 asimon83 Exp $ * @link http://www.teamst.org/index.php * **/ - // -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Application error"; -$TLS_htmltext['error'] = "

                Unexpected error happens. Please check event viewer or " . - "logs for details.

                You are welcome to report the problem. Please visit our " . - "website.

                "; - - - -$TLS_htmltext_title['assignReqs'] = "Przypisywanie wymagań do przypadków testowych"; -$TLS_htmltext['assignReqs'] = "

                Cel:

                -

                Użytkownicy mogą stworzyć powiązania pomiędzy wymaganiami a przypadkami testowymi. Projektant testu może zdefiniować relacje typu 0..n do 0..n. +$TLS_htmltext_title['error'] = "Application error"; +$TLS_htmltext['error'] = "

                Unexpected error happens. Please check event viewer or " . + "logs for details.

                You are welcome to report the problem. Please visit our " . + "website.

                "; + +$TLS_htmltext_title['assignReqs'] = "Przypisywanie wymagań do przypadków testowych"; +$TLS_htmltext['assignReqs'] = "

                Cel:

                +

                Użytkownicy mogą stworzyć powiązania pomiędzy wymaganiami a przypadkami testowymi. Projektant testu może zdefiniować relacje typu 0..n do 0..n. Przypadek testowy może nie mieć powiązania lub być powiązany do jednego lub więcej wymagań i na odwrót. Taka macierz wspomaga prześledzenie pokrycia testów wzglęcem wymagań oraz znalezieniu wymagań, które nie przeszły testów pozytywnie. Taka analiza stanowi potwierdzenie, że wszystkie sprecyzowane oczekiwania zostały spełnione.

                Jak zacząć:

                1. Wybierz przypadek testowy na drzewku z lewej strony. Okienko wyboru z listą wymagań jest umieszczone na górze strony.
                2. -
                3. Wybierz dokument specyfikacji testowej jeżeli jest stworzony więcej niż jeden. +
                4. Wybierz dokument specyfikacji testowej jeżeli jest stworzony więcej niż jeden. TestLink automatycznie odświeża stronę.
                5. Środkowa część okna zawiera listę wszystkich wymagań (z wybranego dokumentu specyfikacji) które są powiązane do zaznaczonego przypadku testowego. Dolna część okna zawiera listę wszystkich wymagań, które nie mają powiązania do bieżącego przypadku testowego. Projektant może zaznaczyć wymagania, które są pokryte tym przypadkiem testowym a następnie kliknąć przycisk 'Przypisz'. Wówczas te nowo przypisane przypadki testowe będą widoczne w środkowej części okna.
                "; - // -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Specyfikacja testowa"; -$TLS_htmltext['editTc'] = "

                Specyfikacja testowa pozwala użytkownikom na wgląd " . - "i edycje wszystkich istniejących zestawów testów i przypadków testowych. " . - "Przypadki testowe są pogrupowane w wersjach, wszystkie poprzednie wersje są dostępne i mogą być " . - "oglądane oraz zarządzane z tego miejsca.

                - +$TLS_htmltext_title['editTc'] = "Specyfikacja testowa"; +$TLS_htmltext['editTc'] = "

                Specyfikacja testowa pozwala użytkownikom na wgląd " . + "i edycje wszystkich istniejących zestawów testów i przypadków testowych. " . + "Przypadki testowe są pogrupowane w wersjach, wszystkie poprzednie wersje są dostępne i mogą być " . + "oglądane oraz zarządzane z tego miejsca.

                +

                Jak zacząć:

                1. Zaznacz swój projekt testowy w nawigatorze (w głównej cześci). Proszę zwróć uwagę: " . - " Możesz zawsze zmienić aktywny projekt testowy poprzez zaznaczenie innego z " . - "rozwijanej listy w prawym górnym rogu.
                2. + " Możesz zawsze zmienić aktywny projekt testowy poprzez zaznaczenie innego z " . + "rozwijanej listy w prawym górnym rogu.
                3. Stwórz nowy zestaw testów poprzez kliknięcie Utwórz zestaw (Operacje na zestawach testów). Zestawy Testowe mogą " . - "stworzyć strukture dla twojego dokumentu testowego w zależności od przyętej konwencji (funkcjonalny / niefunkcjonalny " . - "testy, części produktu lub jego cechy, zmiana żądań, itp.). Opis " . - "Zestaw testowy może zawierać zarys zawartych przypadków testowych, domyślną konfiguracje, " . - "linki do stosownych dokumentów, ograniczeń i innych istotnych informacji. Generalnie, " . - "wszystkie adnotacje są zawarte w Dziecku Przypadku testowego. Przypadek testowy śledzi " . - "the "folder" metafore, tak więc użytkownicy moga skopiować Zestaw testów z projektu testu " . - "z projektu testu mogą być również, zaimportowane oraz eksportowane (właczając zgromadzone przypadki testowe).
                4. + "stworzyć strukture dla twojego dokumentu testowego w zależności od przyętej konwencji (funkcjonalny / niefunkcjonalny " . + "testy, części produktu lub jego cechy, zmiana żądań, itp.). Opis " . + "Zestaw testowy może zawierać zarys zawartych przypadków testowych, domyślną konfiguracje, " . + "linki do stosownych dokumentów, ograniczeń i innych istotnych informacji. Generalnie, " . + "wszystkie adnotacje są zawarte w Dziecku Przypadku testowego. Przypadek testowy śledzi " . + "the "folder" metafore, tak więc użytkownicy moga skopiować Zestaw testów z projektu testu " . + "z projektu testu mogą być również, zaimportowane oraz eksportowane (właczając zgromadzone przypadki testowe).
                5. Zestawy testów są wymiernymi folderami. Użytkownicy moga przenosić lub kopiować Zestawy Testów z " . - "Projektu testu. Zestawy testów mogą być importowane lub eksportowane (właczając Przypadki testowe). + "Projektu testu. Zestawy testów mogą być importowane lub eksportowane (właczając Przypadki testowe).
                6. Zaznacz swój nowo stworzony Zestaw Testów w nawigatorze i utwórz " . - "nowy Przypadek testowy poprzez kliknięcie na Utwórz (Dzialania na Przypadku testowym). Przypadek testowy obejmuje " . - "określony scenariusz testowania, oczekiwane rezultaty i zdefiniowane pola użytkownika " . - "w Projekcie testu (sprawdź w instrukcji użytkownika dla uzyskania więcej informacji). Jest możliwe " . - "przypisać słowa kluczowe dla zwiększenia efektywności śledzenia.
                7. + "nowy Przypadek testowy poprzez kliknięcie na Utwórz (Dzialania na Przypadku testowym). Przypadek testowy obejmuje " . + "określony scenariusz testowania, oczekiwane rezultaty i zdefiniowane pola użytkownika " . + "w Projekcie testu (sprawdź w instrukcji użytkownika dla uzyskania więcej informacji). Jest możliwe " . + "przypisać słowa kluczowe dla zwiększenia efektywności śledzenia.
                8. Zarządzaj poprzez drzewko nawigatora z lewej strony i edytuj dane. Każdy przypadek testowy zawiera własną historie.
                9. Przypisz stworzoną przez siebie Specyfikacje testową Plan Testu kiedy twój Przypadek testowy jest gotowy.
                10. + \"javascript:open_help_window('glossary','$locale');\">Plan Testu kiedy twój Przypadek testowy jest gotowy.

                Poprzez TestLink mozesz zorganizować Przypadki testowe w Zestawy testowe." . -"Zestawy testowe mogą odnosić się do innych zestawów testowych, umożliwiając Ci tworzenie hierachii Zestawów testowych. + "Zestawy testowe mogą odnosić się do innych zestawów testowych, umożliwiając Ci tworzenie hierachii Zestawów testowych. Możesz wydrukować tą informacje razem z Przypadkami testowymi.

                "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Strona wyszukiwania przypadku testowego"; -$TLS_htmltext['searchTc'] = "

                Cel:

                +$TLS_htmltext_title['searchTc'] = "Strona wyszukiwania przypadku testowego"; +$TLS_htmltext['searchTc'] = "

                Cel:

                Nawigacja poprzez słowa kluczowe lub/ i przeszukiwane ciągi. Podczas wyszukiwania nie są rozróżnione wielkości liter. Resultaty zawierają tylko przypadki testowe z aktualnego Projektu testu.

                @@ -103,8 +98,8 @@ /* contribution by asimon for 2976 */ // requirements search // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Strona wyszukiwania wymagań"; -$TLS_htmltext['searchReq'] = "

                Cel:

                +$TLS_htmltext_title['searchReq'] = "Strona wyszukiwania wymagań"; +$TLS_htmltext['searchReq'] = "

                Cel:

                Nawigacja poprzez słowa kluczowe i/lub przeszukiwane ciągi. Podczas wyszukiwania nie są rozróżnione wielkości liter. Rezultaty zawierają tylko wymagania dla bieżącego projektu.

                @@ -126,8 +121,8 @@ // requirement specification search // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Strona wyszukiwania specyfikacji wymagań"; -$TLS_htmltext['searchReqSpec'] = "

                Cel:

                +$TLS_htmltext_title['searchReqSpec'] = "Strona wyszukiwania specyfikacji wymagań"; +$TLS_htmltext['searchReqSpec'] = "

                Cel:

                Nawigacja poprzez słowa kluczowe i/lub przeszukiwane ciągi. To wyszukiwanie nie rozróżnia wielkich i małych liter. Rezultaty zawierają tylko specyfikacje wymagań dla aktualnego Projektu testu.

                @@ -148,20 +143,19 @@ - Puste pola nie sa brane pod uwagÄ™.

                "; /* end contribution */ - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Wydrukuj dpecyfikacje testowÄ…"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

                Cel:

                +$TLS_htmltext_title['printTestSpec'] = "Wydrukuj dpecyfikacje testowÄ…"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

                Cel:

                Z tego punktu możesz wydrukować pojedyńczy przypadek testowy, wszystkie przypadki testowe w zestawie testowym, lub wszystkie przypadki testowe w projekcie testowym lub planie testu.

                Jak zacząć:

                1. -

                  Zaznacz części Przypadku testowego który chcesz obejrzeć następnie kliknij na przypadek testowy, +

                  Zaznacz części Przypadku testowego który chcesz obejrzeć następnie kliknij na przypadek testowy, zestaw testowy lub projekt testowy. Zostanie wyświetlona strona do druku.

                2. -
                3. Użyj opcji \"pokaż jako\" drop-box w nawigatorze w celu sprecyzowania jakie chcesz aby informacje -były wyświetlone w postaci HTML, OpenOffice Writer lub w postaci dokumentu Micosoft Word. +

                4. Użyj opcji \"pokaż jako\" drop-box w nawigatorze w celu sprecyzowania jakie chcesz aby informacje +były wyświetlone w postaci HTML, OpenOffice Writer lub w postaci dokumentu Micosoft Word. See help w celu uzyskania więcej informacji.

                5. @@ -170,14 +164,13 @@
                "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Projekt dokumentu specyfikacji wymagań"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

                Możesz zarządzać dokumentami specyfikacji wymagań.

                +$TLS_htmltext_title['reqSpecMgmt'] = "Projekt dokumentu specyfikacji wymagań"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

                Możesz zarządzać dokumentami specyfikacji wymagań.

                Specyfikacja wymagań

                -

                Wymagania są zebrane w Dokumencie Specyfikacja Wymagań, które jest powiązane z +

                Wymagania są zebrane w Dokumencie Specyfikacja Wymagań, które jest powiązane z Projektem Testu.
                TestLink nie realizuje (jeszcze) jednocześnie Specyfikacji Wymagań i Wymagań samych w sobie. Więc, wersja dokumentu powinna być dodana po Specyfikacji Tytuł. Użytkownik może dodać prosty opis lub notatkę Szkic pola.

                @@ -185,36 +178,35 @@

                Przypisana liczba wymagań służy do oceny pokrycia wymagań w przypadku jeżeli nie wszystkie wymagania są zawarte w TestLinku. Wartość 0 oznacza bieżącą liczne wymagań, która jest użyta dla metryki.

                -

                Na przykład: SRS zawiera 200 wymagań, ale tylko 50 jest dodane do TestLink. Pokrycie testu jest +

                Na przykład: SRS zawiera 200 wymagań, ale tylko 50 jest dodane do TestLink. Pokrycie testu jest 25% (podsumowując 50 dodanych wymagań będzie testowane).

                Wymagania

                Kliknij na tytule Specyfikacji Wymagań. Jeżeli żaden nie istnieje, " . - "kliknij na oknie projektu w celu utworzenia. Możesz tworzyć, edytować, usuwać, + "kliknij na oknie projektu w celu utworzenia. Możesz tworzyć, edytować, usuwać, lub importować wymagania dla dokumentu. Każde z wymagań ma tytuł, szkic i status. -Status powinien być 'Normalny' lub 'Nietestowalny'. Nietestowalne wymagania nie są wliczane do metryki. +Status powinien być 'Normalny' lub 'Nietestowalny'. Nietestowalne wymagania nie są wliczane do metryki. Ten parametr powinien być użyty dla zarówno cech wymagań które nie zostały zaimplementowane jak i źle zaprojektowane.

                -

                Możesz stworzyć nowy przypadek testowy dla wymagań poprzez zaznaczenie wymagań w oknie specyfikacji. -Te Przypadki testowe są stworzone w Zestawie testowym. +

                Możesz stworzyć nowy przypadek testowy dla wymagań poprzez zaznaczenie wymagań w oknie specyfikacji. +Te Przypadki testowe są stworzone w Zestawie testowym. z nazwą zdefiniowaną w konfiguracji(default is: \$tlCfg->req_cfg->default_testsuite_name = 'Zestaw testowy stworzony według wymagań - Auto';). Tytuł i Szkic sa kopiowane do tych Przypadków testowych.

                "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Wydrukuj specyfikacje wymagań"; //printReq +$TLS_htmltext_title['printReqSpec'] = "Wydrukuj specyfikacje wymagań"; // printReq $TLS_htmltext['printReqSpec'] = "

                Cel:

                Z tego punktu możesz wydrukować pojedyńcze wymaganie, wszystkie wymagania z specyfikacja wymagań lub wszystkie wymagania w projekcie testu.

                Jak zacząć:

                1. -

                  Zaznacz częśc wymagań, które chcesz wyświetlić i kliknij na wymaganiu, +

                  Zaznacz częśc wymagań, które chcesz wyświetlić i kliknij na wymaganiu, specyfikacji wymagań lub na projekcie testu. Będzie pokazana wersja do druku strony.

                2. -
                3. Użyj opcji \"pokaż jako\" drop-box w nawigatorze w celu sprecyzowania jakie chcesz aby informacje -były wyświetlone w postaci HTML, OpenOffice Writer lub w postaci dokumentu Micosoft Word. +

                4. Użyj opcji \"pokaż jako\" drop-box w nawigatorze w celu sprecyzowania jakie chcesz aby informacje +były wyświetlone w postaci HTML, OpenOffice Writer lub w postaci dokumentu Micosoft Word. See help for more information.

                5. @@ -223,42 +215,40 @@
                "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Przypisywanie słów kluczowych"; -$TLS_htmltext['keywordsAssign'] = "

                Cel:

                +$TLS_htmltext_title['keywordsAssign'] = "Przypisywanie słów kluczowych"; +$TLS_htmltext['keywordsAssign'] = "

                Cel:

                To jest stroną gdzie można obserwować powiązanie słów kluczowych do istniejących przypadeków testowych i Zestawów Testowych

                W celu przypisania słów kluczowych:

                  -
                1. Zaznacz Zestaw Testów lub przypadek testowy na drzewku +
                2. Zaznacz Zestaw Testów lub przypadek testowy na drzewku z lewej strony.
                3. -
                4. Rubryka, która pojawia się w prawym górnym rogu pozwala na przypisanie +
                5. Rubryka, która pojawia się w prawym górnym rogu pozwala na przypisanie dowolnego słowa kluczowego do każdego przypadku testowego.
                6. -
                7. Selekcja poniżej pozwoli przypisać przypadeku testowy z +
                8. Selekcja poniżej pozwoli przypisać przypadeku testowy z uwzględnieniem bardziej szczegółowego poziomu.

                Ważne informacje dotyczące przypisywania słów kluczowych w Planie testu:

                -

                Przypisywanie słów kluczowych, którego dokonujesz w specyfikacji dotyczy wyłacznie przypadków testowych, -w twoich planach testów wtedy i tylko wtedy gdy plan testu zawiera najnowszą wersje przypadku testowego. +

                Przypisywanie słów kluczowych, którego dokonujesz w specyfikacji dotyczy wyłacznie przypadków testowych, +w twoich planach testów wtedy i tylko wtedy gdy plan testu zawiera najnowszą wersje przypadku testowego. W przeciwnym wypadku, plan testu, który zawiera starszą wersje przypadku testowego poprzez przypisanie którego dokonujesz, nie pojawi się w planie testu.

                To zastosowanie ma w Testlinku ma takie znaczenie, że przypisania słów kluczowych dokonane w planach testu będą dotyczyć wyłącznie najnowszych wersji przypadków testowych, a nie starszych przypadków testowych. -Jeżeli chcesz, aby twój przypadek testowy był uaktualniony w planie testu, wcześniej sprawdź czy przypadki testowe zostały uaktualnione +Jeżeli chcesz, aby twój przypadek testowy był uaktualniony w planie testu, wcześniej sprawdź czy przypadki testowe zostały uaktualnione poprzez opcje 'uaktualnij modyfikowane przypadki testowe' przed dokonaniem przypisania słów kluczowych.

                "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Wykonanie Przypadku Testowego"; -$TLS_htmltext['executeTest'] = "

                Cel:

                +$TLS_htmltext_title['executeTest'] = "Wykonanie Przypadku Testowego"; +$TLS_htmltext['executeTest'] = "

                Cel:

                -

                Pozwala użytkownikowi na przeprowadzanie przypadków testowych. Użytkownik może przypisać wynik testu +

                Pozwala użytkownikowi na przeprowadzanie przypadków testowych. Użytkownik może przypisać wynik testu do przypadku testowego dla struktury. Sprawdź pomoc w celu uzyskania więcej informacji o filtrach i ustawieniach. " . -"(kliknij na ikone znaku zapytania).

                - + "(kliknij na ikone znaku zapytania).

                +

                Jak zacząć:

                  @@ -266,18 +256,18 @@
                1. Zaznacz strukture z listy down box
                2. Jeżeli chcesz zobaczyć tylko kilka przypadków testowych zamist całego drzewka możesz wybrać stosowne filtry. Kliknij na przycisk \"Zastosuj\"- - po zmianie filtrów.
                3. + po zmianie filtrów.
                4. Kiliknij na przypadku testowym na drzewku.
                5. Uzupełnij wynik przypadku testowego i wszystkie odpowiednie notatki i błędy.
                6. Zapisz rezultat.
                -

                Uwaga: TestLink musi być tak stworzony aby współdziałac z twoim systemem śledzenia błędów BTS +

                Uwaga: TestLink musi być tak stworzony aby współdziałac z twoim systemem śledzenia błędów BTS Jeżeli chcesz stworzyć/ śledzić problem bezpośrednio z GUI.

                "; // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Opis raportów testu i metryki"; -$TLS_htmltext['showMetrics'] = "

                Raporty są połączone z Planem Testu " . - "(zdefiniowane na górze nawigatora). Ten Plan Testu może się różnić +$TLS_htmltext_title['showMetrics'] = "Opis raportów testu i metryki"; +$TLS_htmltext['showMetrics'] = "

                Raporty są połączone z Planem Testu " . + "(zdefiniowane na górze nawigatora). Ten Plan Testu może się różnić od Planu Testu do wykonania. Możesz wybrac odpowiedni format raportu:

                • Normal - raport jest wyÅ›wietlany na stronie
                • @@ -308,10 +298,10 @@
                • Polecenie w którym struktury sÄ… dodawane do planu testu okreÅ›la, które struktury sÄ… najczÄ™stsze. Wyniki z najnowszych struktur majÄ… pierwszeÅ„stwo przed starszymi wersjami struktur. Dla przykÅ‚adu, jezeli zaznaczysz test jako 'Niepoprawny' w strukturze 1 i zaznaczysz jako 'poprawny' w strukturze 2,najnowsza wersja bÄ™dzie 'poprawny'.
                • -
                • Jeżeli przypadek testowy jest przeprowadzany wielokrotnie na tej samej strukturze to ostani przeprowadzony test jest ważniejszy. +
                • Jeżeli przypadek testowy jest przeprowadzany wielokrotnie na tej samej strukturze to ostani przeprowadzony test jest ważniejszy. Dla przykÅ‚adu, jeżeli struktura 3 jest przypisana do twojego zespoÅ‚u i tester 1 zaznaczy test jako 'poprawny' o drugiej popoÅ‚udniu, a tester 2 oznaczy jako 'niepoprawny' o 3 popoÅ‚udniu - test jest oznaczany jako 'niepoprawny'.
                • -
                • Przypadki testowe wymienine jako 'nie przeprowadzone' w przeciwieÅ„stwie do struktury nie sÄ… brane pod uwagÄ™. Na przykÅ‚ad, jeżeli zaznaczysz przypadek +
                • Przypadki testowe wymienine jako 'nie przeprowadzone' w przeciwieÅ„stwie do struktury nie sÄ… brane pod uwagÄ™. Na przykÅ‚ad, jeżeli zaznaczysz przypadek jako 'poprawny' w strukturze 1 i nie przeprowdzisz go w strukturze 2, ostatni wynik bÄ™dzie okreÅ›lany jako 'poprawny'.

                Następujące tabale są wyświetlane:

                @@ -323,12 +313,12 @@
              • Rezultaty wedÅ‚ug słów kluczowych Lista wszystkich wszystkich słów kluczowych przypisanych do bierzacego planu testu i powiÄ…zanych z nim rezultatów.
              • Rezultaty wedÅ‚ug wÅ‚aÅ›cicela - Lista wedÅ‚ug, której każdy wÅ‚aÅ›cicel ma przypadki testowe przypisane do swojego planu testu. Przypadki testowe, które + Lista wedÅ‚ug, której każdy wÅ‚aÅ›cicel ma przypadki testowe przypisane do swojego planu testu. Przypadki testowe, które sÄ… nieprzydzielone sÄ… zebrane i okreÅ›lone jako 'nieprzydzielone'.

              Ogólny status struktury

              -

              Lista wyników dla przeprowadzonych realizacji każdej struktury. +

              Lista wyników dla przeprowadzonych realizacji każdej struktury. Dla każdej struktury, wszystkich przypadków testowych, wszystkich poprawnych, % poprawnych, wszystkich niepoprawnych, % niepoprawnych, zablokowanych, % zablokowanych, nieprzeprowadzonych, %nieprzeprowadzonych. Jeżeli test byl przeprowadzony dwukrotnie na tej samej strukturze, wynik ostatniego przeprowadzonego testu będzie brany pod uwagę.

              @@ -336,13 +326,13 @@

              Metryka Zapytania

              Ten raport zawiera formularz strony zapytania i stronę wyników zapytania z danymi zapytania. formularz strony zapytania prezentuje strone zapytania z czteroma czynnikami kontrolnymi. -Każdy czynnik kontrolny jest ustawiony domyślnie zwiększając liczbę przypadków testowych i struktur +Każdy czynnik kontrolny jest ustawiony domyślnie zwiększając liczbę przypadków testowych i struktur na podstawie których wykonywane są zapytania. Czynniki alarmujące -umożliwiają użytkownikowi filtrowanie rezultatów, generowanie określonych raportów dla określonych własciceli, słów kluczowych, zestawów +umożliwiają użytkownikowi filtrowanie rezultatów, generowanie określonych raportów dla określonych własciceli, słów kluczowych, zestawów i układów struktur.

                -
              • sÅ‚owa kluczowe 0->1 sÅ‚owa kluczowe mogÄ… być zaznaczone. DomyÅ›lnie, żadne sÅ‚owo kluczowe nie jest zaznaczone. Jeżeli żadne sÅ‚owo kluczowo nie jest zaznaczone, +
              • sÅ‚owa kluczowe 0->1 sÅ‚owa kluczowe mogÄ… być zaznaczone. DomyÅ›lnie, żadne sÅ‚owo kluczowe nie jest zaznaczone. Jeżeli żadne sÅ‚owo kluczowo nie jest zaznaczone, wszystkie przypadki testowe bÄ™dÄ… rozważane bez wzglÄ™du na przypisanie do sÅ‚owa kluczowego. SÅ‚owa kluczowe sÄ… przypisane w specyfikacji testu lub stronach zarzÄ…dzania sÅ‚owami kluczowymi. SÅ‚owa kluczowe przypisane do przypadków testowych rozciÄ…gajÄ… siÄ™ na wszystkie plany testów, i rozciÄ…gajÄ… siÄ™ na wszystkie wersje przypadku testowego. Jeżeli jesteÅ› zainteresowany rezultatammi dla okreÅ›lonego sÅ‚owa kluczowego użyjesz tej kontrolki.
              • @@ -351,16 +341,16 @@ wyszukiwanie 'nieprzypisanych' przypadków testowych. Posiadanie jest przypisane poprzez strone 'Przypisz przeprowadzenie przypadku testowego', i jest wykonane na podstawie podstaw planu testu. Jeżeli jesteÅ› zainteresowany pracÄ… wykonanÄ… przez okreÅ›lonego testera, wykorzystasz tÄ… opcje kontroli.
              • zestaw najwyższego poziomu 0->n zestawy najwyższego poziomu mogÄ… być zaznaczane. DomyÅ›lnie - wszystkie zestawy sÄ… zaznaczone. -Tylko zestawy, które zostaÅ‚y zaznaczone bÄ™dÄ… zapytywane dla uzyskania wyników metryki. Jeżeli jesteÅ› zainteresowany w rezultatach +Tylko zestawy, które zostaÅ‚y zaznaczone bÄ™dÄ… zapytywane dla uzyskania wyników metryki. Jeżeli jesteÅ› zainteresowany w rezultatach dla okreÅ›lonego zestawu użyjesz tej opcji kontroli.
              • -
              • Struktury struktury 1->n mogÄ… zostać wybrane. DomyÅ›lnie - wszystkie struktury sÄ… zaznaczone. Tylko wykonania -przeprowadzone na strukturach zaznaczonych przez ciebie bÄ™dÄ… brane pod uwagÄ™ podczas tworzenia metryki. Na przykÅ‚ad - jeżeli chcesz +
              • Struktury struktury 1->n mogÄ… zostać wybrane. DomyÅ›lnie - wszystkie struktury sÄ… zaznaczone. Tylko wykonania +przeprowadzone na strukturach zaznaczonych przez ciebie bÄ™dÄ… brane pod uwagÄ™ podczas tworzenia metryki. Na przykÅ‚ad - jeżeli chcesz zobaczyć ile przypadków testowych zostaÅ‚o przeprowadzone na ostanich trzech strukturach - użyjesz tej opcji kontroli. Wybór sÅ‚owa kluczowego, wÅ‚aÅ›ciciela i zestaw najwyższego poziomu okreÅ›li liczbÄ™ przypadków testowych dla twojego planu testu sÄ… używane do obliczenia poprzez zestaw i poprzez metrykÄ™ planu. Na przyklad, jeżeli okreÅ›lisz wlaÅ›ciciel = 'Greg', -SÅ‚owo kluczowe='Prioritet 1', i wszystkie dostÄ™pne zestawy testowe, które maja pryiorytet 1 przypadki testowe przypisane do Grega bÄ™dÄ… brane pod uwagÄ™. +SÅ‚owo kluczowe='Prioritet 1', i wszystkie dostÄ™pne zestawy testowe, które maja pryiorytet 1 przypadki testowe przypisane do Grega bÄ™dÄ… brane pod uwagÄ™. Liczba'# przypadków testowych'liczba, która zobaczysz w raporcie bÄ™dzie zmiennÄ… trzech czynników. -Wybór struktury bÄ™dzie miaÅ‚ wpÅ‚yw jeżeli przypadek jest rozważany jako 'poprawny', 'niepoprawny', 'zablokowany' lub 'nieprzeprowadzony' +Wybór struktury bÄ™dzie miaÅ‚ wpÅ‚yw jeżeli przypadek jest rozważany jako 'poprawny', 'niepoprawny', 'zablokowany' lub 'nieprzeprowadzony' ProszÄ™ odnieÅ› siÄ™ do zasad 'Wyników ostatniego testu' takich jak pojawiajÄ… siÄ™ powyżej.

              Kliknij na przycisk 'Zastosuj' aby kontynuować z zapytaniem i wyświetlić strone produkcyjną.

              @@ -370,41 +360,40 @@
            • parametry zapytania użyte do stworzenia raportu
            • podsumowanie caÅ‚ego planu testu
            • przypadajÄ…cych na strukture podsumowanie wszystkich niepowodzeÅ„ (caÅ‚oÅ›ci / poprawnych / niepoprawnych / zablokowanych / nieprzeprowadzonych) i wszystich przeprowadzonych testów -na tej strukturze. Jeżeli test zostaÅ‚ przeprowadzony wiÄ™cej niż jeden raz na wielu strukturach - wszystkie wyniki przeprowadzonych testów bÄ™dÄ… pokazane -tam gdzie zostaÅ‚y zapisane w przeciwieÅ„stwie do struktury. Jednakże, podsumowanie dla zestawu testów bÄ™dzie +na tej strukturze. Jeżeli test zostaÅ‚ przeprowadzony wiÄ™cej niż jeden raz na wielu strukturach - wszystkie wyniki przeprowadzonych testów bÄ™dÄ… pokazane +tam gdzie zostaÅ‚y zapisane w przeciwieÅ„stwie do struktury. Jednakże, podsumowanie dla zestawu testów bÄ™dzie zawieraÅ‚o jedynie 'wynik ostatniego testu' dla zaznaczonych struktur.
            • Raporty o Zablokowanych, Niepoprawnych, Nieprzeprowadzonych przypadkach testowych

              Takie raporty pokazują wszystkie zablokowane, niepoprawne lub nieprzeprowadzone przypadki testowe. Logika 'Ostatnich Wyników testu' (która jest opisana jako ogólna metryka planu testu) jest ponownie stosowana w celu określenia czy - przypadek testowy jest rozważany jako zablokowany, niepoprawny lub nieprzeprowadzony. Raporty o przypadkach testowych oznaczonych jako zablokowanych i niepoprawnych będą + przypadek testowy jest rozważany jako zablokowany, niepoprawny lub nieprzeprowadzony. Raporty o przypadkach testowych oznaczonych jako zablokowanych i niepoprawnych będą wyświetlane w połaczeniu z błędami jeżeli użytkownik używa zintegrowanego systemu śledzenia błędu.

              Raport testu

              -

              Pokazuje status każdego przypadku testowego na każdej strukturze. Jeżeli na tej samej strukturze wielokrtonie przeprowadzany ten sam przypadek testowy -to ostani wynik będzie obowiązujacy. W przypadku gdy będzie użyta duża ilośc danych, +

              Pokazuje status każdego przypadku testowego na każdej strukturze. Jeżeli na tej samej strukturze wielokrtonie przeprowadzany ten sam przypadek testowy +to ostani wynik będzie obowiązujacy. W przypadku gdy będzie użyta duża ilośc danych, zalecane jest weksportowanie raportu do formatu excel w celu zaprwnienia łatwości przeglądania.

              Wykresy - Ogólna Metryka Planu Testu

              -

              Logika 'Wynik ostatniego testu' jest stosowana dla wszystkich czterech wykresów, które zobaczysz. Wykresy są animacją, która jest stosowana w celu pomocy użytkownikowi +

              Logika 'Wynik ostatniego testu' jest stosowana dla wszystkich czterech wykresów, które zobaczysz. Wykresy są animacją, która jest stosowana w celu pomocy użytkownikowi wizualizacji metryki obecnego planu testu. Cztery wykresy możliwe do zastosowania to :

              • Wykres koÅ‚owy ogólny poprawnych / niepoprawny / zablokowany/ przypadków testowych nieprzeprowadzonych
              • Wykres sÅ‚upkowy rezultatów zrealizowany wedÅ‚ug sÅ‚owa kluczowego
              • Wykres sÅ‚upkowy rezultatów zrealizowany wedÅ‚ug wÅ‚aÅ›cicela
              • Wykres sÅ‚upkowy rezultatów zrealizowany wedÅ‚ug Najwyższego poziomu zestawu
              -

              Słupki w wykresie słupkowym są pokolorowane tak aby użytkownik mógł szybko oszacować liczbe przypadków testowych +

              Słupki w wykresie słupkowym są pokolorowane tak aby użytkownik mógł szybko oszacować liczbe przypadków testowych poprawnych, niepoprawnych, zablokowanych, nieprzeprowadzonych.

              Suma błedów dla przypadku testowego

              Ten raport pokazuje każdy przypadek testowy ze wszystkimi błędami jakie pojawiają się w całym projekcie testu. Ten raport jest dostępny tylko wtedy gdy jest podłaczony system śledzenia błędu.

              "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Dodaj /Usuń przypadek testowy z Planu Testu"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

              Cel:

              +$TLS_htmltext_title['planAddTC'] = "Dodaj /Usuń przypadek testowy z Planu Testu"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

              Cel:

              Pozwala użytkownikowi (z odpowiednim poziomem dostępu) dodawać lub usuwać przypadki testowe z Planu Testu.

              Dodawanie lub usuwanie przypadków testowych:

              @@ -415,8 +404,8 @@ "; // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Przypisywanie testerów do przeprowadzanych testów"; -$TLS_htmltext['tc_exec_assignment'] = "

              Cel

              +$TLS_htmltext_title['tc_exec_assignment'] = "Przypisywanie testerów do przeprowadzanych testów"; +$TLS_htmltext['tc_exec_assignment'] = "

              Cel

              Ta strona pozwala kierownikowi testów na przypisanie użytkowników do poszczególnych testów według Planu Testu.

              Rozpoczęcie

              @@ -428,12 +417,12 @@ "; // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Zaktualizuj Przypadek Testowy w Planie Testu"; -$TLS_htmltext['planUpdateTC'] = "

              Cel

              +$TLS_htmltext_title['planUpdateTC'] = "Zaktualizuj Przypadek Testowy w Planie Testu"; +$TLS_htmltext['planUpdateTC'] = "

              Cel

              Ta strona pozwala użytkownikowi na uaktalnienie Przypadku Testowego do nowszej (innej) wersji jeżeli Specyfikacja Testu została zmieniona. Często zdarza się, że pewne funkcjonalności zostają wyjaśnione podczas testowania." . - " Użytkownicy zmieniają specyfikacje testową, ale plany testowania musza być także przeniesione na Plan Testu. W przeciwnym wypadku Plan" . - " testu zawiera pierwotną wersje, rezultaty odnoszą się do pierwotnej treści przypadku testowego.

              + " Użytkownicy zmieniają specyfikacje testową, ale plany testowania musza być także przeniesione na Plan Testu. W przeciwnym wypadku Plan" . + " testu zawiera pierwotną wersje, rezultaty odnoszą się do pierwotnej treści przypadku testowego.

              Rozpoczęcie

                @@ -443,13 +432,12 @@
              1. W celu weryfikacji: Otwórz strone przeprowadzenia testu, aby zobaczyć treść przypadku testowego.
              "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Określanie testów według wysokiej lub niskiej pilności"; -$TLS_htmltext['test_urgency'] = "

              Cel

              +$TLS_htmltext_title['test_urgency'] = "Określanie testów według wysokiej lub niskiej pilności"; +$TLS_htmltext['test_urgency'] = "

              Cel

              TestLink pozwala użytkownikowi na ustawnie pilności zestawu testów w celu ustanowienia pierwszeństwa w testowania przypadków testowych. Priorytet testu zależy zarówno od ważności przypadku testowego tak samo jak pilności określonej - w Planie Testu. Kierownik testu powinien określić zestaw przypadków testowych, które powinny być testowane + w Planie Testu. Kierownik testu powinien określić zestaw przypadków testowych, które powinny być testowane jako pierwsze. To rozwiązanie pozwala zapewnić, że najbardziej istotne testy zostaną przeprowadzone nie zależnie od stopnia realizacji testów w czasie.

              Rozpoczęcie

              @@ -460,8 +448,7 @@
            • Kliknij przycisk 'Zapisz' w celu zapisu zmian.
            • Na przykÅ‚ad przypadek testowy z wysokÄ… ważnoÅ›cia w zestawie testowym oznaczonym jako niski " . - "bÄ™dzie miaÅ‚ ważność Å›redniÄ…."; - + "bÄ™dzie miaÅ‚ ważność Å›redniÄ…."; // ------------------------------------------------------------------------------------------ diff --git a/locale/pt_BR/description.php b/locale/pt_BR/description.php index b7504e988d..7669d3175c 100644 --- a/locale/pt_BR/description.php +++ b/locale/pt_BR/description.php @@ -1,25 +1,25 @@ Opções para a geração do documento -

              Esta tabela permite ao usuário filtrar os casos de teste antes de serem visualizados. -Se selecionado (marcado) os dados serão exibidos. Para alterar os dados -apresentados, marque ou desmarque clicando no Filtro, e selecione o nível +

              Esta tabela permite ao usuário filtrar os casos de teste antes de serem visualizados. +Se selecionado (marcado) os dados serão exibidos. Para alterar os dados +apresentados, marque ou desmarque clicando no Filtro, e selecione o nível desejado na árvore de dados.

              -

              Cabeçalho do Documento: Os usuários podem filtrar informações no cabeçalho do documento. -As informações do cabeçalho do documento incluem: Introdução, Escopo, +

              Cabeçalho do Documento: Os usuários podem filtrar informações no cabeçalho do documento. +As informações do cabeçalho do documento incluem: Introdução, Escopo, Referências, Metodologia de Teste, e Limitações de Teste.

              -

              Corpo do Caso de Teste: Os usuários podem filtrar informações do corpo do Caso de Teste. As informações do corpo do Caso de Teste +

              Corpo do Caso de Teste: Os usuários podem filtrar informações do corpo do Caso de Teste. As informações do corpo do Caso de Teste incluem: Resumo, Passos, Resultados Esperados, e Palavras-chave

              -

              Resumo do Caso de Teste: Os usuários podem filtrar informações do Resumo do Caso de Teste através do Título do Caso de Teste, -no entanto, eles não podem filtrar informações do Resumo do Caso de Teste através do Corpo de um Caso de Teste. -O resumo do Caso de Teste foi apenas parcialmente separado do corpo do Caso de Teste a fim de apoiar a visualização -do Título com um breve resumo e a ausência de Passos, Resultados Esperados, -e Palavras-chave. Se um usuário decidir ver o corpo do Caso de Teste, o Resumo do Caso de Teste +

              Resumo do Caso de Teste: Os usuários podem filtrar informações do Resumo do Caso de Teste através do Título do Caso de Teste, +no entanto, eles não podem filtrar informações do Resumo do Caso de Teste através do Corpo de um Caso de Teste. +O resumo do Caso de Teste foi apenas parcialmente separado do corpo do Caso de Teste a fim de apoiar a visualização +do Título com um breve resumo e a ausência de Passos, Resultados Esperados, +e Palavras-chave. Se um usuário decidir ver o corpo do Caso de Teste, o Resumo do Caso de Teste será sempre incluído.

              Tabela de Conteúdo: O TestLink insere uma lista com todos os títulos com seus links internos checados.

              -

              Formatos de Saída: Existem várias possibilidades: HTML, OpenOffice Writer, OpenOffice Calc, Excel, +

              Formatos de Saída: Existem várias possibilidades: HTML, OpenOffice Writer, OpenOffice Calc, Excel, Word ou por E-mail (HTML).

              "; // testPlan.html $TLS_hlp_testPlan = "

              Plano de Teste

              Geral

              -

              O Plano de Teste é uma abordagem sistemática ao teste de um sistema de software. Você pode organizar a atividade de teste com +

              O Plano de Teste é uma abordagem sistemática ao teste de um sistema de software. Você pode organizar a atividade de teste com versões particulares do produto em tempo e resultados rastreáveis.

              Execução do Teste

              -

              Esta é a seção onde os usuários podem executar os Casos de Teste (escrever os resultados dos testes) -e imprimir a Suíte de Casos de Teste do Plano de Teste. Nesta seção os usuários podem -acompanhar os resultados da sua execução dos Caso de Teste.

              +

              Esta é a seção onde os usuários podem executar os Casos de Teste (escrever os resultados dos testes) +e imprimir a Suíte de Casos de Teste do Plano de Teste. Nesta seção os usuários podem +acompanhar os resultados da sua execução dos Caso de Teste.

              Gerenciamento do Plano de Teste

              -

              Esta seção, somente acessível aos líderes, permite que os usuários possam administrar os planos de teste. -A administração de planos de teste envolve a criação/edição/exclusão de planos, acréscimo/edição -/exclusão/atualização dos casos de teste dos planos, criando versões, bem como definindo quem pode +

              Esta seção, somente acessível aos líderes, permite que os usuários possam administrar os planos de teste. +A administração de planos de teste envolve a criação/edição/exclusão de planos, acréscimo/edição +/exclusão/atualização dos casos de teste dos planos, criando versões, bem como definindo quem pode ver qual plano.
              -Usuários com permissão de líder poderão também definir a prioridade/risco e a propriedade das -suites de caso de teste (categorias) e criar marcos de teste.

              +Usuários com permissão de líder poderão também definir a prioridade/risco e a propriedade das +suites de caso de teste (categorias) e criar marcos de teste.

              -

              Nota: É possível que os usuários não possam ver uma lista suspensa que contenha os planos de teste. -Nesta situação, todos os links (exceto para os líderes ativos) serão desvinculados. Se você -estiver nesta situação, contate a administração do TestLink para lhe conceder os -direitos de projeto adequado ou criar um Plano de Teste para você.

              "; +

              Nota: É possível que os usuários não possam ver uma lista suspensa que contenha os planos de teste. +Nesta situação, todos os links (exceto para os líderes ativos) serão desvinculados. Se você +estiver nesta situação, contate a administração do TestLink para lhe conceder os +direitos de projeto adequado ou criar um Plano de Teste para você.

              "; // custom_fields.html $TLS_hlp_customFields = "

              Campos Personalizados

              @@ -96,13 +96,13 @@ atributos:

              • Nome do Campo personalizado.
              • -
              • Capturar o nome da variável (ex: Este é o valor que é fornecido para a API lang_get(), +
              • Capturar o nome da variável (ex: Este é o valor que é fornecido para a API lang_get(), ou exibido como se não for encontrado no arquivo de linguagem).
              • Tipo do Campo personalizado (string, numérico, float, enum, email).
              • -
              • Possibilidade de enumerar os valores (ex: RED|YELLOW|BLUE), aplicável a uma lista, lista de multiseleção +
              • Possibilidade de enumerar os valores (ex: RED|YELLOW|BLUE), aplicável a uma lista, lista de multiseleção e tipos de combo.
                Utilize o caractere pipe ('|') para -separar os possíveis valores para uma enumeração. Um dos possíveis valores pode ser +separar os possíveis valores para uma enumeração. Um dos possíveis valores pode ser uma string vazia.
              • Valor default: NÃO IMPLEMENTADO AINDA.
              • @@ -123,22 +123,22 @@ // execMain.html $TLS_hlp_executeMain = "

                Executar Casos de Teste

                -

                Permite aos usuários 'executar' os Casos de Teste. Execução propriamente -dita é apenas a atribuição do resultado de um Caso de Teste (Passou, +

                Permite aos usuários 'executar' os Casos de Teste. Execução propriamente +dita é apenas a atribuição do resultado de um Caso de Teste (Passou, Com Falha ou Bloqueado) de uma compilação selecionada.

                O acesso a um Bugtracking pode ser configurado. O usuário pode adicionar diretamente novos bugs e navegar pelos existentes. Consulte o manual de instalação para maiores detalhes.

                "; -//bug_add.html +// bug_add.html $TLS_hlp_btsIntegration = "

                Adicionar Bugs ao Caso de Teste

                (Somente se estiver configurado) -O TestLink possui uma integração muito simples com os sistemas de Bugtracking, -mas não é capaz de enviar um pedido de abertura de bug ao Bugtracking ou receber de volta o ID do Bug. +O TestLink possui uma integração muito simples com os sistemas de Bugtracking, +mas não é capaz de enviar um pedido de abertura de bug ao Bugtracking ou receber de volta o ID do Bug. A integração é feita utilizando um link para a página do Bugtracking, com as seguintes características:

                • Inserir novo Bug.
                • Exibição das informações do bug.
                -

                +

                Processo para adicionar um novo bug

                @@ -147,7 +147,7 @@

              • Passo 2: Anote o ID do Bug gerado pelo Bugtracking.
              • Passo 3: Escreva o ID do Bug no campo de entrada.
              • Passo 4: Clique no botão Adicionar Bug
              • -
              +
            Depois de fechar a página de Adição de Bug, os dados relevantes do bug serão exibidos na página de execução.

            "; @@ -155,11 +155,11 @@ // execFilter.html $TLS_hlp_executeFilter = "

            Configurações

            -

            Em Configurações é possível que você selecione o plano de teste, a build e +

            Em Configurações é possível que você selecione o plano de teste, a build e a plataforma (se disponível) para ser executado.

            Plano de Teste

            -

            Você pode escolher o Plano de Teste necessário. De acordo com o plano de teste escolhido, as apropriadas +

            Você pode escolher o Plano de Teste necessário. De acordo com o plano de teste escolhido, as apropriadas builds serão exibidas. Depois de escolher um plano de teste, os filtros serão reiniciados.

            Plataformas

            @@ -170,85 +170,82 @@

            Filtros

            Filtros proporcionam a oportunidade de influenciar ainda mais o conjunto de casos de teste mostrados. -Através dos Filtros é possível diminuir o conjunto de Casos de Teste exibidos. Selecione +Através dos Filtros é possível diminuir o conjunto de Casos de Teste exibidos. Selecione os filtros desejados e clique no botão \"Aplicar\".

            -

            Os Filtros Avançados permitem que você especifique um conjunto de valores para filtros aplicáveis +

            Os Filtros Avançados permitem que você especifique um conjunto de valores para filtros aplicáveis ​​usando Ctrl + Clique dentro de cada ListBox.

            Filtro de Palavra-chave

            Você pode filtrar os Casos de Teste pelas Palavras-chave que foram atribuídas. Você pode escolher " . -"múltiplas Palavras-chave utilizando Ctrl + Clique. Se você escolher mais de uma palavra-chave, você pode " . -"decidir se somente serão exibidos os Casos de Teste que contém todas as Palavras-chave aselecionadas " . -"(botão \"E\") ou pelo menos uma das Palavras-chave escolhidas (botão \"OU\").

            + "múltiplas Palavras-chave utilizando Ctrl + Clique. Se você escolher mais de uma palavra-chave, você pode " . + "decidir se somente serão exibidos os Casos de Teste que contém todas as Palavras-chave aselecionadas " . + "(botão \"E\") ou pelo menos uma das Palavras-chave escolhidas (botão \"OU\").

            Filtro de Prioridade

            Você pode filtrar os Casos de Teste pela prioridade do Teste. A proridade do Teste é a \"importância do Caso de Teste\" " . -"combinado com \"a urgência do Teste\" dentro do Plano de Teste atual.

            + "combinado com \"a urgência do Teste\" dentro do Plano de Teste atual.

            Filtro de Usuário

            Você pode filtrar os Casos de Teste que não estão atribuídos (\"Ninguém\") ou atribuídos a \"Alguém\". " . -"Você também pode filtrar os Casos de Teste que são atribuídos a um testador específico. Se você escolheu um testador " . -"específico, também existe a possibilidade de mostrar os Casos de Teste que estão por serem atribuídos " . -"(Filtros avançados estão disponíveis).

            + "Você também pode filtrar os Casos de Teste que são atribuídos a um testador específico. Se você escolheu um testador " . + "específico, também existe a possibilidade de mostrar os Casos de Teste que estão por serem atribuídos " . + "(Filtros avançados estão disponíveis).

            Filtro de Resultado

            Você pode filtrar os Casos de Teste pelos resultados (Filtros avançados estão disponíveis). Você pode filtrar por " . -"resultado \"na build escolhida para a execução\", \"na última execução\", \"em TODAS as Builds\", " . -"\"em QUALQUER build\" e \"em uma build específica\". Se \"uma build específica\" for escolhida, então você pode " . -"especificar a build.

            "; - + "resultado \"na build escolhida para a execução\", \"na última execução\", \"em TODAS as Builds\", " . + "\"em QUALQUER build\" e \"em uma build específica\". Se \"uma build específica\" for escolhida, então você pode " . + "especificar a build.

            "; // newest_tcversions.html $TLS_hlp_planTcModified = "

            Versões mais recentes do Caso de Teste

            -

            Todo o conjunto de Casos de Teste ligados ao Plano de Teste é analisado, e uma lista de Casos +

            Todo o conjunto de Casos de Teste ligados ao Plano de Teste é analisado, e uma lista de Casos de Teste que têm uma versão mais recente é exibida (contra o conjunto atual do Plano de Teste).

            "; - // requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

            Cobertura de Requisitos


            -

            Este recurso permite mapear uma cobertura de usuário ou requisitos do sistema +

            Este recurso permite mapear uma cobertura de usuário ou requisitos do sistema por Casos de Teste. Navegue através do link \"Especificar Requisitos\" na tela principal.

            Especificação de Requisitos

            -

            Os Requisitos estão agrupados no documento 'Especificação de Requisitos' que está relacionado ao -Projeto de Teste.
            O TestLink ainda não suporta versões para a Especificação de Requisitos e -também para os Requisitos. Assim, a versão do documento deve ser adicionada depois do +

            Os Requisitos estão agrupados no documento 'Especificação de Requisitos' que está relacionado ao +Projeto de Teste.
            O TestLink ainda não suporta versões para a Especificação de Requisitos e +também para os Requisitos. Assim, a versão do documento deve ser adicionada depois do Título da Especificação. -O usuário pode adicionar uma descrição simples ou uma nota no campo Escopo.

            +O usuário pode adicionar uma descrição simples ou uma nota no campo Escopo.

            -

            Sobrescrever o contador de Requisitos serve para avaliar a cobertura dos requisitos no caso +

            Sobrescrever o contador de Requisitos serve para avaliar a cobertura dos requisitos no caso de nem todos os requisitos estarem adicionados ao TestLink. -

            O valor 0 significa que a contagem atual de requisitos é usado para métricas.

            -

            Ex: SRS inclui 200 requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes +

            O valor 0 significa que a contagem atual de requisitos é usado para métricas.

            +

            Ex: SRS inclui 200 requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes é de 25% (se todos estes requisitos forem testados).

            Requisitos

            -

            Clique no título para criar uma Especificação de Requisitos. Você pode criar, editar, deletar +

            Clique no título para criar uma Especificação de Requisitos. Você pode criar, editar, deletar ou importar requisitos para este documento. Cada Requisito tem título, escopo e status. O status deve ser \"Válido\" ou \"Não testado\". Requisitos não testados não são contabilizados -para as métricas. Este parâmetro deve ser utilizado para características não implementadas -e requisitos modelados incorretamente.

            +para as métricas. Este parâmetro deve ser utilizado para características não implementadas +e requisitos modelados incorretamente.

            -

            Você pode criar novos Casos de Teste para os requisitos utilizando multi ações para os requisitos -ativos na tela de especificação de requisitos. Estes Casos de Teste são criados dentro da Suíte de -Teste com nome definido na configuração (padrão é: $tlCfg->req_cfg->default_testsuite_name = +

            Você pode criar novos Casos de Teste para os requisitos utilizando multi ações para os requisitos +ativos na tela de especificação de requisitos. Estes Casos de Teste são criados dentro da Suíte de +Teste com nome definido na configuração (padrão é: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Título e Escopo são copiados destes Casos de Teste.

            "; $TLS_hlp_req_coverage_table = "

            Cobertura:

            Um valor por ex. de \"40% (8/20)\" significa que 20 Casos de Teste devem ser criados para testar completamente este -Requisito. 8 destes já foram criados e associados ao Requisito, com +Requisito. 8 destes já foram criados e associados ao Requisito, com a cobertura de 40 %. "; - // req_edit $TLS_hlp_req_edit = "

            Links internos no Escopo:

            -

            Links internos servem ao propósito da criação de links a outros requisitos / especificações de requisitos +

            Links internos servem ao propósito da criação de links a outros requisitos / especificações de requisitos com uma sintaxe especial. O comportamento dos Links internos pode ser alterado no arquivo de configuração.

            Uso: @@ -256,7 +253,7 @@ Link para Requisitos: [req]req_doc_id[/req]
            Link para Especificação de Requisitos: [req_spec]req_spec_doc_id[/req_spec]

            -

            O Projeto de Teste do Requisito / Especificação de Requisitos, uma versão e uma âncora +

            O Projeto de Teste do Requisito / Especificação de Requisitos, uma versão e uma âncora também podem ser especificados:
            [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
            Esta sintaxe também funciona para as especificações de requisito (atributos de versão não tem nenhum efeito).
            @@ -268,7 +265,6 @@ Sempre que alguma coisa além do escopo é alterado, você é forçado a criar uma nova revisão.

            "; - // req_view $TLS_hlp_req_view = "

            Links Diretos:

            É fácil compartilhar este documento com outros, basta clicar no ícone do globo no topo deste documento para criar um link direto.

            @@ -284,14 +280,13 @@

            Relações de Requisitos são usados ​​para relacionamentos de modelos entre os requisitos. Relações personalizadas e a opção de permitir relações entre os requisitos de diferentes projetos de teste podem ser configurados no arquivo de configuração. -Se você definir a relação \"Requisito A é pai do Requisito B\", +Se você definir a relação \"Requisito A é pai do Requisito B\", o Testlink irá definir a relação \"Requisito B é filho do Requisito A\" implicitamente.

            "; - // req_spec_edit $TLS_hlp_req_spec_edit = "

            Links internos no Escopo:

            -

            Links internos servem ao propósito da criação de links a outros requisitos / especificações de requisitos +

            Links internos servem ao propósito da criação de links a outros requisitos / especificações de requisitos com uma sintaxe especial. O comportamento dos Links internos pode ser alterado no arquivo de configuração.

            Uso: @@ -299,24 +294,22 @@ Link para Requisitos: [req]req_doc_id[/req]
            Link para Especificação de Requisitos: [req_spec]req_spec_doc_id[/req_spec]

            -

            O Projeto de Teste do Requisito / Especificação de Requisitos, uma versão e uma âncora +

            O Projeto de Teste do Requisito / Especificação de Requisitos, uma versão e uma âncora também podem ser especificados:
            [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
            Esta sintaxe também funciona para as especificações de requisito (atributos de versão não tem nenhum efeito).
            Se você não especificar a versão do Requisito completo, todas as versões serão exibidas.

            "; - // planAddTC_m1.tpl $TLS_hlp_planAddTC = "

            Sobre 'Campos personalizados salvos'

            -Se você tiver definido e atribuído ao Projeto de Teste,
            +Se você tiver definido e atribuído ao Projeto de Teste,
            Campos Personalizados com:
            'Exibição no desenho do Plano de Teste=true' e
            'Habilitar no desenho do Plano de Teste=true'
            você irá ver nesta página APENAS os Casos de Teste ligados ao Plano de Teste. "; - // resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = "Mais informações sobre os testadores
            Se você clicar no nome do testador nesta tabela, você irá ter uma visão mais detalhada @@ -327,9 +320,8 @@ o caso de teste irá aparecer como executado pelo usuário atribuído. "; - // xxx.html -//$TLS_hlp_xxx = ""; +// $TLS_hlp_xxx = ""; // ----- END ------------------------------------------------------------------ ?> diff --git a/locale/pt_BR/texts.php b/locale/pt_BR/texts.php index 878a617aaa..3431c06683 100644 --- a/locale/pt_BR/texts.php +++ b/locale/pt_BR/texts.php @@ -14,87 +14,82 @@ * * * Revisions history is not stored for the file - * + * * @package TestLink * @author Martin Havlat - * @copyright 2003-2009, TestLink community + * @copyright 2003-2009, TestLink community * @version CVS: $Id: texts.php,v 1.29 2010/07/22 14:14:44 asimon83 Exp $ * @link http://www.teamst.org/index.php * **/ - // -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Erro na Aplicação"; -$TLS_htmltext['error'] = "

            Um erro inesperado ocorreu. Por favor, verifique o event viewer ou " . - "logs para detalhes.

            Você está convidado para relatar o problema. Por favor, visite nosso " . - "website.

            "; - - - -$TLS_htmltext_title['assignReqs'] = "Atribuir Requisitos aos Casos de Teste"; -$TLS_htmltext['assignReqs'] = "

            Purpose:

            -

            Usuários podem criar relacionamentos entre requisitos e casos de teste. Um arquiteto pode -definir relacionamentos 0..n para 0..n. I.e. Um caso de teste pode ser atribuído para nenhum, um ou mais -requisitos e vice versa. Assim a matriz de rastreabilidade ajuda a investigar a cobertura -dos requisitos de teste e finalmente quais falharam durante os testes. Esta +$TLS_htmltext_title['error'] = "Erro na Aplicação"; +$TLS_htmltext['error'] = "

            Um erro inesperado ocorreu. Por favor, verifique o event viewer ou " . + "logs para detalhes.

            Você está convidado para relatar o problema. Por favor, visite nosso " . + "website.

            "; + +$TLS_htmltext_title['assignReqs'] = "Atribuir Requisitos aos Casos de Teste"; +$TLS_htmltext['assignReqs'] = "

            Purpose:

            +

            Usuários podem criar relacionamentos entre requisitos e casos de teste. Um arquiteto pode +definir relacionamentos 0..n para 0..n. I.e. Um caso de teste pode ser atribuído para nenhum, um ou mais +requisitos e vice versa. Assim a matriz de rastreabilidade ajuda a investigar a cobertura +dos requisitos de teste e finalmente quais falharam durante os testes. Esta análise serve como entrada para o próximo planejamento de teste.

            Iniciar:

              -
            1. Escolha um Caso de Teste na árvore à esquerda. O combo box com a lista de +
            2. Escolha um Caso de Teste na árvore à esquerda. O combo box com a lista de Especificações de Requisitos é exibido no topo da área de trabalho.
            3. -
            4. Escolha um documento de Especificação se mais de um estiver definido. +
            5. Escolha um documento de Especificação se mais de um estiver definido. O TestLink recarregará a página automaticamente.
            6. -
            7. Um bloco ao centro da área de trabalho lista todos os requisitos (para a Especificação selecionada), que - está conectada ao caso de teste. O bloco abaixo 'Requisitos Disponíveis' lista todos - os requisitos que não estão relacionados - ao caso de teste selecionado. Um arquiteto pode selecionar requisitos que são cobertos por este - caso de teste e então clicar em 'Atribuir'. Este novo caso de teste atribuído será exibido no +
            8. Um bloco ao centro da área de trabalho lista todos os requisitos (para a Especificação selecionada), que + está conectada ao caso de teste. O bloco abaixo 'Requisitos Disponíveis' lista todos + os requisitos que não estão relacionados + ao caso de teste selecionado. Um arquiteto pode selecionar requisitos que são cobertos por este + caso de teste e então clicar em 'Atribuir'. Este novo caso de teste atribuído será exibido no bloco central 'Requisitos atribuídos'.
            "; - // -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Especificação de Teste"; -$TLS_htmltext['editTc'] = "

            A Especificação de Teste permite aos usuários visualizar " . - "e editar todas as Suítes de Teste e Casos de Teste existentes. " . - "Os Casos de Teste são versionados e todas as versões anteriores estão disponíveis e podem ser " . - "visualizadas e gerenciadas aqui.

            - +$TLS_htmltext_title['editTc'] = "Especificação de Teste"; +$TLS_htmltext['editTc'] = "

            A Especificação de Teste permite aos usuários visualizar " . + "e editar todas as Suítes de Teste e Casos de Teste existentes. " . + "Os Casos de Teste são versionados e todas as versões anteriores estão disponíveis e podem ser " . + "visualizadas e gerenciadas aqui.

            +

            Iniciar:

            1. Selecione seu Projeto de Teste na árvore de navegação (o nó principal). Observe: " . - "Você sempre poderá trocar o Projeto de Teste ativo selecionando um diferente da " . - "lista drop-down do canto superior esquerdo.
            2. + "Você sempre poderá trocar o Projeto de Teste ativo selecionando um diferente da " . + "lista drop-down do canto superior esquerdo.
            3. Crie uma nova Suíte de Teste clicando em Nova Suíte de Teste. As Suítes de Teste podem " . - "trazer a estrutura da sua documentação de teste conforme suas convenções (testes funcionais/não-funcionais " . - ", produtos, componentes ou características, solicitações de mudança, etc.). A descrição da " . - "Suíte de Teste poderia conter o escopo dos Casos de Teste incluídos, configuração padrão, " . - "links para documentos relevantes, limitações e outras informações usuais. Em geral, " . - "todas anotações que são comuns às Suítes de Teste. As Suítes de Teste seguem " . - "a metáfora do "diretório", assim os usuários podem mover e copiar Suítes de Teste dentro " . - "do Projeto de Teste. Além disso, eles podem ser importados ou exportados (incluindo os Casos de Teste nele contidos).
            4. + "trazer a estrutura da sua documentação de teste conforme suas convenções (testes funcionais/não-funcionais " . + ", produtos, componentes ou características, solicitações de mudança, etc.). A descrição da " . + "Suíte de Teste poderia conter o escopo dos Casos de Teste incluídos, configuração padrão, " . + "links para documentos relevantes, limitações e outras informações usuais. Em geral, " . + "todas anotações que são comuns às Suítes de Teste. As Suítes de Teste seguem " . + "a metáfora do "diretório", assim os usuários podem mover e copiar Suítes de Teste dentro " . + "do Projeto de Teste. Além disso, eles podem ser importados ou exportados (incluindo os Casos de Teste nele contidos).
            5. Suítes de Teste são pastas escaláveis. Os usuários podem mover ou copiar Suítes de Teste dentro " . - "do Projeto de Teste. Suítes de Teste podem ser importadas ou exportadas (incluindo os Casos de Teste). + "do Projeto de Teste. Suítes de Teste podem ser importadas ou exportadas (incluindo os Casos de Teste).
            6. Selecione sua mais nova Suíte de Teste criada na árvore de navegação e crie " . - "um novo Caso de Teste clicando em Criar Caso(s) de Teste. Um Caso de Teste especifica " . - "um cenário de testes particular, resultados esperados e campos personalizados definidos " . - "no Projeto de Teste (consulte o manual do usuário para maiores informações). Também é possível " . - "atribuir palavras-chave para melhorar a rastreabilidade.
            7. + "um novo Caso de Teste clicando em Criar Caso(s) de Teste. Um Caso de Teste especifica " . + "um cenário de testes particular, resultados esperados e campos personalizados definidos " . + "no Projeto de Teste (consulte o manual do usuário para maiores informações). Também é possível " . + "atribuir palavras-chave para melhorar a rastreabilidade.
            8. Navegue pela árvore de navegação do lado esquerdo e edite os dados. Os Casos de Teste armazenam histórico próprio.
            9. Atribua suas Especificações de Teste criadas ao Test Plan quando seus Casos de Teste estiverem prontos.
            10. + \"javascript:open_help_window('glossary','$locale');\">Test Plan quando seus Casos de Teste estiverem prontos.

            Com o TestLink você organiza os Casos de Teste em Suítes de Teste." . -" Suítes de Teste podem ser aninhadas em outras Suítes de Teste, permitindo a você criar hierarquias de Suítes de Teste. + " Suítes de Teste podem ser aninhadas em outras Suítes de Teste, permitindo a você criar hierarquias de Suítes de Teste. Então você pode imprimir esta informação juntamente com o Caso de Teste.

            "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Página de Busca de Casos de Teste"; -$TLS_htmltext['searchTc'] = "

            Objetivo:

            +$TLS_htmltext_title['searchTc'] = "Página de Busca de Casos de Teste"; +$TLS_htmltext['searchTc'] = "

            Objetivo:

            Navegue de acordo com palavras-chave e/ou strings procuradas. A busca não é case sensitive. Os resultados incluem apenas Casos de Teste do Projeto de Teste atual.

            @@ -111,10 +106,10 @@ /* contribution by asimon for 2976 */ // requirements search // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Página de Busca de Requisitos"; -$TLS_htmltext['searchReq'] = "

            Objetivo:

            +$TLS_htmltext_title['searchReq'] = "Página de Busca de Requisitos"; +$TLS_htmltext['searchReq'] = "

            Objetivo:

            -

            Navegue de acordo com as palavras-chave e/ou strings procuradas. A busca não é +

            Navegue de acordo com as palavras-chave e/ou strings procuradas. A busca não é case sensitive. Os resultados incluem somente requisitos do projeto de teste atual.

            Para Pesquisar:

            @@ -134,10 +129,10 @@ // requirement specification search // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Página de Busca de Especificação de Requisitos"; -$TLS_htmltext['searchReqSpec'] = "

            Objetivo:

            +$TLS_htmltext_title['searchReqSpec'] = "Página de Busca de Especificação de Requisitos"; +$TLS_htmltext['searchReqSpec'] = "

            Objetivo:

            -

            Navegue de acordo com as palavras-chave e/ou strings procuradas. A busca não é +

            Navegue de acordo com as palavras-chave e/ou strings procuradas. A busca não é case sensitive. Os resultados incluem somente requisitos do projeto de teste atual.

            Para Pesquisar:

            @@ -156,20 +151,19 @@ - Campos vazios não são considerados.

            "; /* end contribution */ - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificação de Testes"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

            Objetivo:

            -

            A partir daqui você pode imprimir um único caso de teste, todos os casos de teste dentro de uma suite +$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificação de Testes"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

            Objetivo:

            +

            A partir daqui você pode imprimir um único caso de teste, todos os casos de teste dentro de uma suite ou todos os casos de teste de um Projeto de Teste ou Plano de Teste.

            Iniciar:

            1. -

              Selecione os campos dos casos de teste que você deseja exibir, e então clique em um Caso de Teste, +

              Selecione os campos dos casos de teste que você deseja exibir, e então clique em um Caso de Teste, Suíte de Teste, ou Projeto de Teste. Uma página pronta para impressão será exibida.

            2. -
            3. Use a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer -a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. +

            4. Use a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer +a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. Veja Ajuda para maiores informações.

            5. @@ -178,53 +172,51 @@
            "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Modelar Especificação de Requisitos"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

            Você pode gerenciar documentos de Especificação de Requisitos.

            +$TLS_htmltext_title['reqSpecMgmt'] = "Modelar Especificação de Requisitos"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

            Você pode gerenciar documentos de Especificação de Requisitos.

            Especificação de Requisitos

            -

            Requisitos estão agrupados por documentos de Especificação de Requisitos , os quais estão relacionados ao -Projeto de Teste.
            O TestLink não suporta (ainda) versões para Especificação de Requisitos +

            Requisitos estão agrupados por documentos de Especificação de Requisitos , os quais estão relacionados ao +Projeto de Teste.
            O TestLink não suporta (ainda) versões para Especificação de Requisitos e também Requisitos. Logo, a versão do documento deve ser inserida após o Título da Especificação. -Um usuário pode inserir uma descrição simples ou notas no campo Escopo.

            +Um usuário pode inserir uma descrição simples ou notas no campo Escopo.

            -

            Sobrescrever o contador de Requisitos serve para +

            Sobrescrever o contador de Requisitos serve para avaliar a cobertura dos requisitos no caso de nem todos os requisitos estarem adicionados ao TestLink. -O valor 0 significa que o contador de requisitos atual é utilizado +O valor 0 significa que o contador de requisitos atual é utilizado para métricas.

            -

            E.g. SRS inclui 200 requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes é de +

            E.g. SRS inclui 200 requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes é de 25% (se todos estes requisitos forem testados).

            Requisitos

            Clique no título da Especificação de Requisitos criada, e se nenhuma existir, " . - "clique no nó do projeto para criar uma. Você pode criar, editar, excluir + "clique no nó do projeto para criar uma. Você pode criar, editar, excluir ou importar requisitos para o documento. Cada requisito tem um título, escopo e status. -O status deve ser 'Válido' ou 'Não testável'. Requisitos Não Testáveis não são contabilizados -para métricas. Este parâmetro deve ser utilizado para características não implementadas e +O status deve ser 'Válido' ou 'Não testável'. Requisitos Não Testáveis não são contabilizados +para métricas. Este parâmetro deve ser utilizado para características não implementadas e requisitos modelados incorretamente.

            -

            Você pode criar novos casos de teste para os requisitos utilizando multi ações para os -requisitos ativos na tela de especificação de requisitos. Estes Casos de Teste são criados dentro da Suíte de Teste -com nome definido na configuração (default is: \$tlCfg->req_cfg->default_testsuite_name = +

            Você pode criar novos casos de teste para os requisitos utilizando multi ações para os +requisitos ativos na tela de especificação de requisitos. Estes Casos de Teste são criados dentro da Suíte de Teste +com nome definido na configuração (default is: \$tlCfg->req_cfg->default_testsuite_name = 'Test suite created by Requirement - Auto';). Título e Escopo são copiados destes Casos de Teste.

            "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Imprimir documento de Especificação de Requisitos"; //printReq +$TLS_htmltext_title['printReqSpec'] = "Imprimir documento de Especificação de Requisitos"; // printReq $TLS_htmltext['printReqSpec'] = "

            Objetivo:

            -

            Através desta opção você pode imprimir um requisito único, todos os requisitos de uma Especificação de Requisitos, +

            Através desta opção você pode imprimir um requisito único, todos os requisitos de uma Especificação de Requisitos, ou todos os requisitos de um Projeto de Teste.

            Iniciar:

            1. -

              Selecione as partes dos requisitos que você deseja exibir, e então clique em requisito, +

              Selecione as partes dos requisitos que você deseja exibir, e então clique em requisito, especificação de requisito ou projeto de teste. A visualização da impressão será exibida.

            2. -
            3. Utilize a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer -a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. +

            4. Utilize a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer +a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. Veja Ajuda para mais informações.

            5. @@ -233,43 +225,41 @@
            "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Atribuição de Palavras-chave"; -$TLS_htmltext['keywordsAssign'] = "

            Objetivo:

            -

            A página de Atribuição de Palavras-chave é o lugar onde os usuários podem +$TLS_htmltext_title['keywordsAssign'] = "Atribuição de Palavras-chave"; +$TLS_htmltext['keywordsAssign'] = "

            Objetivo:

            +

            A página de Atribuição de Palavras-chave é o lugar onde os usuários podem atribuir em lotes Palavras-chave às Suítes de Teste ou Casos de Teste.

            Para Atribuir Palavras-chave:

              -
            1. Selecione uma Suíte de Teste ou Caso de Teste na árvore +
            2. Selecione uma Suíte de Teste ou Caso de Teste na árvore à esquerda.
            3. -
            4. O box no topo da página que exibe informações do lado direito - permitirá a você atribuir palavras-chave para os casos de +
            5. O box no topo da página que exibe informações do lado direito + permitirá a você atribuir palavras-chave para os casos de teste individualmente.
            6. -
            7. A seleção abaixo permite a você atribuir Casos de Teste em um +
            8. A seleção abaixo permite a você atribuir Casos de Teste em um nivel mais granular.

            Informação Importante quanto à Atribuição de Palavras-chave nos Planos de Teste:

            -

            Atribuir Palavras-chave à Suíte de Teste afetará somente Casos de Teste -no seu Plano de Teste somente se o Plano de Teste conter a última versão do Caso de Teste. -Caso contrário, se o Plano de Teste conter versões mais antigas do Caso de Teste, as atribuições que você +

            Atribuir Palavras-chave à Suíte de Teste afetará somente Casos de Teste +no seu Plano de Teste somente se o Plano de Teste conter a última versão do Caso de Teste. +Caso contrário, se o Plano de Teste conter versões mais antigas do Caso de Teste, as atribuições que você fez NÃO aparecerão no Plano de Teste.

            -

            O TestLink usa esta abordagem para que versões mais antigas dos Casos de Teste nos Planos de Teste não sejam afetadas pela atribuição -de Palavras-chave que você fez nas versões mais recentes dos Casos de Teste. Se você deseja seus -Casos de Teste no seu Plano de Teste sejam atualizados, primeiro verifique se eles estão atualizados +

            O TestLink usa esta abordagem para que versões mais antigas dos Casos de Teste nos Planos de Teste não sejam afetadas pela atribuição +de Palavras-chave que você fez nas versões mais recentes dos Casos de Teste. Se você deseja seus +Casos de Teste no seu Plano de Teste sejam atualizados, primeiro verifique se eles estão atualizados utilizando a funcionalidade 'Atualizar Versão dos Casos de Teste' antes de fazer a atribuição das Palavras-chave.

            "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Execução dos Casos de Teste"; -$TLS_htmltext['executeTest'] = "

            Objetivo:

            +$TLS_htmltext_title['executeTest'] = "Execução dos Casos de Teste"; +$TLS_htmltext['executeTest'] = "

            Objetivo:

            -

            Permite aos usuários executar os Casos de Teste. O usuário pode atribuir resultados +

            Permite aos usuários executar os Casos de Teste. O usuário pode atribuir resultados aos Casos de Teste nos Ciclo de Teste. Veja a ajuda para mais informações sobre filtros e configurações " . - "(clique no ícone interrogação).

            + "(clique no ícone interrogação).

            Iniciar:

            @@ -277,19 +267,19 @@
          • O usuário deve definir um Ciclo de Teste para o Plano de Teste.
          • Selecionar um Ciclo de Teste no menu drop down
          • Se você quiser ver apenas alguns poucos casos de teste, em vez de toda a árvore, - você pode escolher quais filtros aplicar. Clique no botão \"Aplicar\" - depois que você alterar os filtros.
          • + você pode escolher quais filtros aplicar. Clique no botão \"Aplicar\" + depois que você alterar os filtros.
          • Clique no Caso de Teste no menu em árvore.
          • Preencha o resultado do Caso de Teste, suas respectivas notas e/ou bugs.
          • Salve os resultados.
          • -

            Nota: O TestLink deve ser configurado para colaborar com seu Bugtracker +

            Nota: O TestLink deve ser configurado para colaborar com seu Bugtracker se você quiser criar ou rastrear problemas reportados diretamente da GUI.

            "; // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Descrição dos Relatórios de Teste e Métricas"; -$TLS_htmltext['showMetrics'] = "

            Os relatórios estão relacionados a um Plano de Teste " . - "(definido no topo do navegador). Este Plano de Teste pode diferir do Plano de Teste +$TLS_htmltext_title['showMetrics'] = "Descrição dos Relatórios de Teste e Métricas"; +$TLS_htmltext['showMetrics'] = "

            Os relatórios estão relacionados a um Plano de Teste " . + "(definido no topo do navegador). Este Plano de Teste pode diferir do Plano de Teste corrente para execução. Você também pode selecionar formatos dos relatórios:

            • Normal - relatório é exibido em uma página web
            • @@ -312,69 +302,69 @@

              Métricas Gerais do Plano de Teste

              Esta página exibe somente o último status de um Plano de Teste por Suíte de Teste, Testador e palavras-chave. -O 'último status' é determinado pelo ciclo mais recente onde os Casos de Teste foram executados. Por exemplo, +O 'último status' é determinado pelo ciclo mais recente onde os Casos de Teste foram executados. Por exemplo, se um Caso de Teste foi executado em diversos Ciclos de Teste, somente o último resultado é considerado.

              O 'último resultado' é um conceito utilizado em vários relatórios, e é determinado como a seguir:

                -
              • A ordem em cada Ciclo de Teste (Baseline) é adicionada ao Plano de Teste determina qual é o Ciclo de Teste mais recente. Os resultados do Ciclo de Teste -mais recente prevalecerão sobre os Ciclos de Teste mais antigos. Por exemplo, se você marcar um teste com +
              • A ordem em cada Ciclo de Teste (Baseline) é adicionada ao Plano de Teste determina qual é o Ciclo de Teste mais recente. Os resultados do Ciclo de Teste +mais recente prevalecerão sobre os Ciclos de Teste mais antigos. Por exemplo, se você marcar um teste com status 'Falhou' no Ciclo de Teste 1 e no Ciclo de Teste 2 como 'Passou', o último resultado será 'Passou'.
              • -
              • Se um Caso de Teste é executado diversas vezes em um mesmo Ciclo de Teste, o resultado mais recente -prevalecerá. Por exemplo, se o Ciclo de Teste 3 é liberado para a equipe de testes e o Testador 1 marcar 'Passou' as 14h, +
              • Se um Caso de Teste é executado diversas vezes em um mesmo Ciclo de Teste, o resultado mais recente +prevalecerá. Por exemplo, se o Ciclo de Teste 3 é liberado para a equipe de testes e o Testador 1 marcar 'Passou' as 14h, e o Testador 2 marcar 'Falhou' as 15h, isto aparecerá como 'Falhou'.
              • -
              • Casos de Teste marcados como 'Não Executado' no último Ciclo de Teste não serão considerados. Por exemplo, se você marcar -um Caso de Teste como 'Passou' no Ciclo de Teste 1 e não executar no Ciclo de Teste 2, o último resultado será considerado como +
              • Casos de Teste marcados como 'Não Executado' no último Ciclo de Teste não serão considerados. Por exemplo, se você marcar +um Caso de Teste como 'Passou' no Ciclo de Teste 1 e não executar no Ciclo de Teste 2, o último resultado será considerado como 'Passou'.

              As seguintes tabelas são exibidas:

              • Resultados por Suíte de Teste de Nível Top - Lista os resultados Top de cada Suíte de Teste. Total de Casos de Teste, Passou, Falhou, Bloqueado, Não Executado e o percentual + Lista os resultados Top de cada Suíte de Teste. Total de Casos de Teste, Passou, Falhou, Bloqueado, Não Executado e o percentual completo são listados. Um Caso de Teste 'completo' é aquele que foi marcado como Passou, Falhou ou Bloqueado. Os resultados das suites de nível superior incluem as suites filho.
              • Resultados por Palavra-chave - Lista todas as palavras-chave que estão atribuídas aos Casos de Teste no Plano de Teste corrente, e os resultados associados + Lista todas as palavras-chave que estão atribuídas aos Casos de Teste no Plano de Teste corrente, e os resultados associados à eles.
              • Resultados por Testador - Lista cada Testador que tem Casos de Teste associados a ele no Plano de Teste corrente. Os Casos de Teste que + Lista cada Testador que tem Casos de Teste associados a ele no Plano de Teste corrente. Os Casos de Teste que não estão atribuídos são computados abaixo com a descrição 'desatribuir'.

              Status Geral dos Ciclos de Teste

              -

              Lista os resultados de execução para cada Ciclo de Teste. Para cada Ciclo de Teste, o total de Casos de Teste, total com Passou, -% Passou, total Falha, % Falha, Bloqueado, % Bloqueado, Não Executado e % Não Executado. Se um Caso de Teste foi executado +

              Lista os resultados de execução para cada Ciclo de Teste. Para cada Ciclo de Teste, o total de Casos de Teste, total com Passou, +% Passou, total Falha, % Falha, Bloqueado, % Bloqueado, Não Executado e % Não Executado. Se um Caso de Teste foi executado duas vezes no mesmo Ciclo de Teste, a execução mais recente será considerada.

              Métricas da Consulta

              Este relatório consiste em uma página com o formulário de consulta e uma página com os resultados, a qual contém os dados da consulta. -A página com o formulário de consulta apresenta 4 controles. Cada controle está definido com um valor padrão que -maximiza o número de Casos de Teste e Ciclos de Teste que a consulta deve ser executada. Alterar os controles -permite aos usuários filtrar os resultados e gerar relatório específicos para Testadores em específico, palavras-chave, Suítes de Teste +A página com o formulário de consulta apresenta 4 controles. Cada controle está definido com um valor padrão que +maximiza o número de Casos de Teste e Ciclos de Teste que a consulta deve ser executada. Alterar os controles +permite aos usuários filtrar os resultados e gerar relatório específicos para Testadores em específico, palavras-chave, Suítes de Teste e combinação de Ciclos de Teste.

                -
              • Palavras-chave 0->1 palavras-chave podem ser selecionadas. Por padrão, nenhuma palavra-chave é selecionada. Se uma palavra-chave não é -selecionada, então todos os Casos de Teste serão considerados indiferentemente de atribuição de palavras-chave. Palavras-chave são atribuídas -na especificação de testes ou na página Gerenciar Palavra-chave. Palavras-chave atribuídas aos Casos de Teste alcançam todos os Planos de Teste, e -também todas as versões de um Caso de Teste. Se você está interessado nos resultados para uma palavra-chave específica, +
              • Palavras-chave 0->1 palavras-chave podem ser selecionadas. Por padrão, nenhuma palavra-chave é selecionada. Se uma palavra-chave não é +selecionada, então todos os Casos de Teste serão considerados indiferentemente de atribuição de palavras-chave. Palavras-chave são atribuídas +na especificação de testes ou na página Gerenciar Palavra-chave. Palavras-chave atribuídas aos Casos de Teste alcançam todos os Planos de Teste, e +também todas as versões de um Caso de Teste. Se você está interessado nos resultados para uma palavra-chave específica, você deverá alterar este controle.
              • -
              • Testador 0->1 Testadores podem ser selecionados. Por padrão, nenhum Testador é selecionado. Se um Testador não é selecionado, -então todos os Casos de Teste serão considerados indiferentes de Testador atribuído. Atualmente não há funcionalidade -para buscar por Casos de Teste não atribuídos. O Testador é atribuído através da página 'Atribuir Casos de Teste para Execução' -e é feito com base no Plano de Teste. Se você está interessado em trabalhar com um Testador em específico, você deve +
              • Testador 0->1 Testadores podem ser selecionados. Por padrão, nenhum Testador é selecionado. Se um Testador não é selecionado, +então todos os Casos de Teste serão considerados indiferentes de Testador atribuído. Atualmente não há funcionalidade +para buscar por Casos de Teste não atribuídos. O Testador é atribuído através da página 'Atribuir Casos de Teste para Execução' +e é feito com base no Plano de Teste. Se você está interessado em trabalhar com um Testador em específico, você deve alterar este controle.
              • -
              • Suíte de Teste de Nível Top 0->n Suítes de Teste de Nível Top podem ser selecionadas. Por padrão, todas as Suítes de Teste são selecionadas. -Apenas as Suítes de Teste que são selecioadas serão consultadas para as métricas do resultado. Se você está somente interessado em resultados +
              • Suíte de Teste de Nível Top 0->n Suítes de Teste de Nível Top podem ser selecionadas. Por padrão, todas as Suítes de Teste são selecionadas. +Apenas as Suítes de Teste que são selecioadas serão consultadas para as métricas do resultado. Se você está somente interessado em resultados para uma Suíte de Teste específica você precisa alterar este controle.
              • -
              • Ciclos de Teste 1->n Ciclos de Teste podem ser selecionados. Por padrão, todos os Ciclos de Teste são selecionados. Somente execuções -realizadas no Ciclo de Teste que você selecionou serão consideradas quando produzirem métricas. Por exemplo, se você quiser -ver quantos Casos de Teste foram executados nos últimos 3 Ciclos de Teste, você precisa alterar este controle. A seleção de Palavra-chave, -Testador e Suíte de Teste de Nível Top ditarão o número de Casos de Teste do seu Plano de Teste e serão usados para -calcular as métricas por Suite de Teste e Plano de Teste. Por exemplo, se você selecionar o Testador = 'José', palavra-chave = -'Prioridade 1', e todas as Suítes de Teste disponíveis, somente Casos de Teste com Prioridade 1 atribuídos para José serão considerados. +
              • Ciclos de Teste 1->n Ciclos de Teste podem ser selecionados. Por padrão, todos os Ciclos de Teste são selecionados. Somente execuções +realizadas no Ciclo de Teste que você selecionou serão consideradas quando produzirem métricas. Por exemplo, se você quiser +ver quantos Casos de Teste foram executados nos últimos 3 Ciclos de Teste, você precisa alterar este controle. A seleção de Palavra-chave, +Testador e Suíte de Teste de Nível Top ditarão o número de Casos de Teste do seu Plano de Teste e serão usados para +calcular as métricas por Suite de Teste e Plano de Teste. Por exemplo, se você selecionar o Testador = 'José', palavra-chave = +'Prioridade 1', e todas as Suítes de Teste disponíveis, somente Casos de Teste com Prioridade 1 atribuídos para José serão considerados. O '# de Casos de Teste' totais que você verá neste relatório serão influenciados por estes 3 controles. -A seleção dos Ciclos de Teste influenciarão se o Caso de Teste é considerado 'Passou', 'Falhou', 'Bloqueado', ou 'Não Executado'. +A seleção dos Ciclos de Teste influenciarão se o Caso de Teste é considerado 'Passou', 'Falhou', 'Bloqueado', ou 'Não Executado'. Favor classificar com a regra 'Somente os últimos resultados' à medida em que elas aparecem acima.

              Pressione o botão 'Executar Pesquisa' para prosseguir com a consulta e exibir a página com os resultados.

              @@ -383,42 +373,41 @@
              1. o parâmetro da consulta utilizado para criar o relatório
              2. totais para todo o Plano de Teste
              3. -
              4. por um conjunto de particionamento dos totais (Soma / Passou / Falhou / Bloqueado / Não Executado) e todas execuções realizadas -na Suíte de Teste. Se um Caso de Teste foi executado mais de uma vez em múltiplos Ciclos de Teste, todas as execuções que foram gravadas serão -exibidas nos Ciclos de Teste selecionados. No entanto, o resumo para esta Suíte de Teste somente incluirá o último resultado para +
              5. por um conjunto de particionamento dos totais (Soma / Passou / Falhou / Bloqueado / Não Executado) e todas execuções realizadas +na Suíte de Teste. Se um Caso de Teste foi executado mais de uma vez em múltiplos Ciclos de Teste, todas as execuções que foram gravadas serão +exibidas nos Ciclos de Teste selecionados. No entanto, o resumo para esta Suíte de Teste somente incluirá o último resultado para o Ciclo de Teste selecionado.

              Relatórios de Casos de Teste Bloqueados, com Falha e Não Executados

              -

              Estes relatórios exibem todos os Casos de Teste Bloqueados, com Falha e Não Executados. A lógica do último resultado dos testes -(que está descrita nas Métricas Gerais do Plano de Teste) é novamente empregada para determinar se um Caso de Teste deve ser -considerado Bloquedo, com Falha ou Não Executado. Casos de Teste Bloqueado e com Falha exibirão os bugs associados se o usuário +

              Estes relatórios exibem todos os Casos de Teste Bloqueados, com Falha e Não Executados. A lógica do último resultado dos testes +(que está descrita nas Métricas Gerais do Plano de Teste) é novamente empregada para determinar se um Caso de Teste deve ser +considerado Bloquedo, com Falha ou Não Executado. Casos de Teste Bloqueado e com Falha exibirão os bugs associados se o usuário estiver utilizando um sistema de Bugtracking.

              Relatório de Testes

              -

              Exibe o status de cada Caso de Teste em todos os Ciclos de Teste. O resultado da execução mais recente será utilizado -se um Caso de Teste for executado múltiplas vezes em um mesmo Ciclo de Teste. É recomendado exportar este relatório para +

              Exibe o status de cada Caso de Teste em todos os Ciclos de Teste. O resultado da execução mais recente será utilizado +se um Caso de Teste for executado múltiplas vezes em um mesmo Ciclo de Teste. É recomendado exportar este relatório para o formato em Excel para um fácil manuseio se um grande conjunto de dados estiver em utilização.

              Gráficos - Métricas Gerais do Plano de Teste

              -

              A lógica do último resultado é utilizada para todos os gráficos que você verá. Os gráficos são animados para ajudar +

              A lógica do último resultado é utilizada para todos os gráficos que você verá. Os gráficos são animados para ajudar o usuário à visualizar as métricas do Plano de Teste atual. Os quatro gráficos fornecidos são:

              • Gráfico de pizza com todos os Casos de Teste com status Passou, com Falha, Bloqueados e Não Executados
              • Gráfico de barras com os Resultados por palavra-chave
              • Gráfico de barras com os Resultados por Testador
              • Gráfico de barras com os Resultados por Suítes Nível Top
              -

              As seções e barras dos gráficos são coloridos de modo que o usuário possa identificar o número aproximado de Casos de Teste com status +

              As seções e barras dos gráficos são coloridos de modo que o usuário possa identificar o número aproximado de Casos de Teste com status Passou, Falhou, Bloqueado e Não Executado.

              Bugs por Casos de Teste

              -

              Este relatório exibe cada Caso de Teste com todos os bugs abertos para ele em todo o projeto. +

              Este relatório exibe cada Caso de Teste com todos os bugs abertos para ele em todo o projeto. Este relatório está disponível somente se um Bugtracking estiver conectado.

              "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Adicionar / Remover Casos de Teste do Plano de Teste"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

              Objetivo:

              +$TLS_htmltext_title['planAddTC'] = "Adicionar / Remover Casos de Teste do Plano de Teste"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

              Objetivo:

              Permite aos usuários (com perfil de Líder de Testes) a adicionar ou remover Casos de Teste do Plano de Teste.

              Para adicionar ou remover Casos de Teste:

              @@ -429,8 +418,8 @@ "; // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Atribuir Testadores à Execução de Testes"; -$TLS_htmltext['tc_exec_assignment'] = "

              Objetivo:

              +$TLS_htmltext_title['tc_exec_assignment'] = "Atribuir Testadores à Execução de Testes"; +$TLS_htmltext['tc_exec_assignment'] = "

              Objetivo:

              Esta página permite aos Líderes de Teste atribuir usuários a testes específicos dentro do Plano de Teste.

              Iniciar:

              @@ -442,12 +431,12 @@ "; // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Atualizar Casos de Teste no Plano de Teste"; -$TLS_htmltext['planUpdateTC'] = "

              Objetivo:

              -

              Esta página permite atualizar Casos de Teste para uma versão mais nova (diferente) da +$TLS_htmltext_title['planUpdateTC'] = "Atualizar Casos de Teste no Plano de Teste"; +$TLS_htmltext['planUpdateTC'] = "

              Objetivo:

              +

              Esta página permite atualizar Casos de Teste para uma versão mais nova (diferente) da Especificação de Casos de Teste quando alterada. Isto frequentemente acontece quando uma funcionalidade é alterada durante os testes." . - " O usuário modifica a Especificação de Teste, mas as alterações precisam se propagar ao Plano de Teste também. De qualquer forma," . - " o Plano de Teste mantém as versões originais para garantir que os resultados se referem ao texto correto dos Casos de Teste.

              + " O usuário modifica a Especificação de Teste, mas as alterações precisam se propagar ao Plano de Teste também. De qualquer forma," . + " o Plano de Teste mantém as versões originais para garantir que os resultados se referem ao texto correto dos Casos de Teste.

              Iniciar:

                @@ -457,29 +446,27 @@
              1. Para comprovar: abra a página de execução para verificar o texto do(s) Caso(s) de Teste.
              "; - // ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Especificar testes com prioridade alta ou baixa"; -$TLS_htmltext['test_urgency'] = "

              Objetivo:

              -

              O TestLink permite definir a urgência das Suítes de Teste para afetar a prioridade dos Casos de Teste. - A priorização dos testes depende da importância do Caso de Teste e da urgência definida no Plano de Teste. - O Líder de Teste deve especificar um conjunto de Casos de Teste que devem ser testados primeiro. Isso - ajuda a assegurar que os testes cobrirão os requisitos mais importantes também sob a +$TLS_htmltext_title['test_urgency'] = "Especificar testes com prioridade alta ou baixa"; +$TLS_htmltext['test_urgency'] = "

              Objetivo:

              +

              O TestLink permite definir a urgência das Suítes de Teste para afetar a prioridade dos Casos de Teste. + A priorização dos testes depende da importância do Caso de Teste e da urgência definida no Plano de Teste. + O Líder de Teste deve especificar um conjunto de Casos de Teste que devem ser testados primeiro. Isso + ajuda a assegurar que os testes cobrirão os requisitos mais importantes também sob a pressão do tempo.

              Iniciar:

                -
              1. Escolha uma Suíte de Teste para definiar a urgência do produto/componente no navegador +
              2. Escolha uma Suíte de Teste para definiar a urgência do produto/componente no navegador ao lado esquerdo da janela.
              3. -
              4. Escolha o nível de urgência (Alta, Média ou Baixa). O nível médio é o padrão. Você pode - diminuir a prioridade para partes do produtos não alteradas e aumentar para componentes +
              5. Escolha o nível de urgência (Alta, Média ou Baixa). O nível médio é o padrão. Você pode + diminuir a prioridade para partes do produtos não alteradas e aumentar para componentes com mudanças significativas.
              6. Pressione o botão 'Salvar' para aplicar as alterações.
              -

              Por exemplo, um Caso de Teste com prioridade Alta em uma Suíte de Teste com urgência Baixa +

              Por exemplo, um Caso de Teste com prioridade Alta em uma Suíte de Teste com urgência Baixa será de prioridade Média."; - // ------------------------------------------------------------------------------------------ ?> diff --git a/locale/pt_PT/description.php b/locale/pt_PT/description.php index 93f5693585..6ac91d9d64 100644 --- a/locale/pt_PT/description.php +++ b/locale/pt_PT/description.php @@ -1,87 +1,87 @@ -Opções para a produção do documento -

              Esta tabela permite ao utilizador filtrar os Casos de Teste antes de serem visualizados. -Se selecionado (marcado) os dados serão mostrados. Para alterar os dados -apresentados, marque ou desmarque clicando no Filtro, e selecione o nível +

              Esta tabela permite ao utilizador filtrar os Casos de Teste antes de serem visualizados. +Se selecionado (marcado) os dados serão mostrados. Para alterar os dados +apresentados, marque ou desmarque clicando no Filtro, e selecione o nível desejado na árvore de dados.

              -

              Cabeçalho do Documento: Os utilizadores podem filtrar informações no cabeçalho do documento. -As informações do cabeçalho do documento incluem: Introdução, Âmbito, +

              Cabeçalho do Documento: Os utilizadores podem filtrar informações no cabeçalho do documento. +As informações do cabeçalho do documento incluem: Introdução, Âmbito, Referências, Metodologia de Teste, e Limitações de Teste.

              -

              Corpo do Caso de Teste: Os utilizadores podem filtrar informações do corpo do Caso de Teste. As informações do corpo do Caso de Teste +

              Corpo do Caso de Teste: Os utilizadores podem filtrar informações do corpo do Caso de Teste. As informações do corpo do Caso de Teste incluem: Resumo, Passos, Resultados Esperados, e Palavras Chave

              -

              Resumo do Caso de Teste: Os utilizadores podem filtrar informações do Resumo do Caso de Teste através do Título do Caso de Teste, -no entanto, eles não podem filtrar informações do Resumo do Caso de Teste através do Corpo de um Caso de Teste. -O resumo do Caso de Teste foi apenas parcialmente separado do corpo do Caso de Teste a fim de apoiar a visualização -do Título com um breve resumo e a ausência de Passos, Resultados Esperados, -e Palavras Chave. Se um utilizador decidir ver o corpo do Caso de Teste, o Resumo do Caso de Teste +

              Resumo do Caso de Teste: Os utilizadores podem filtrar informações do Resumo do Caso de Teste através do Título do Caso de Teste, +no entanto, eles não podem filtrar informações do Resumo do Caso de Teste através do Corpo de um Caso de Teste. +O resumo do Caso de Teste foi apenas parcialmente separado do corpo do Caso de Teste a fim de apoiar a visualização +do Título com um breve resumo e a ausência de Passos, Resultados Esperados, +e Palavras Chave. Se um utilizador decidir ver o corpo do Caso de Teste, o Resumo do Caso de Teste será sempre incluído.

              Tabela de Conteúdo: O TestLink insere uma lista com todos os títulos com seus links internos marcados.

              -

              Formatos de Saída: Existem várias possibilidades: HTML, OpenOffice Writer, OpenOffice Calc, Excel, -Word ou por E-mail (HTML).

              "; - -// testPlan.html +

              Formatos de Saída: Existem várias possibilidades: HTML, OpenOffice Writer, OpenOffice Calc, Excel, +Word ou por E-mail (HTML).

              "; + +// testPlan.html $TLS_hlp_testPlan = "

              Plano de Teste

              Geral

              -

              O Plano de Teste é uma abordagem sistemática ao teste de um sistema de software. Você pode organizar a atividade de teste com +

              O Plano de Teste é uma abordagem sistemática ao teste de um sistema de software. Você pode organizar a atividade de teste com versões particulares do produto em tempo e resultados rastreáveis.

              Execução do Teste

              -

              Esta é a secção onde os utilizadores podem executar os Casos de Teste (escrever os resultados dos testes) -e imprimir a Suite de Casos de Teste do Plano de Teste. Nesta secção os utilizadores podem -acompanhar os resultados da sua execução dos Casos de Teste.

              +

              Esta é a secção onde os utilizadores podem executar os Casos de Teste (escrever os resultados dos testes) +e imprimir a Suite de Casos de Teste do Plano de Teste. Nesta secção os utilizadores podem +acompanhar os resultados da sua execução dos Casos de Teste.

              Gestão do Plano de Teste

              -

              Esta secção, apenas acessível aos líderes, permite que os utilizadores possam administrar os Planos de Testes. -A administração de Planos de Testes envolve a criação/edição/eliminação de Planos, acréscimo/edição -/eliminação/atualização dos Casos de Teste dos Planos, criando versões, bem como definindo quem pode +

              Esta secção, apenas acessível aos líderes, permite que os utilizadores possam administrar os Planos de Testes. +A administração de Planos de Testes envolve a criação/edição/eliminação de Planos, acréscimo/edição +/eliminação/atualização dos Casos de Teste dos Planos, criando versões, bem como definindo quem pode ver qual Plano.
              -Utilizadores com permissão de líder poderão também definir a prioridade/risco e a propriedade das -Suites de Caso de Teste (categorias) e criar marcos de teste.

              - -

              Nota: É possível que os utilizadores não possam ver uma lista suspensa que contenha os Planos de Testes. -Nesta situação, todos os links (exceto para os líderes ativos) estarão desativados. Se você -estiver nesta situação, contacte a administração do TestLink para lhe conceder os -direitos de Projecto adequado ou criar um Plano de Teste para você.

              "; - -// custom_fields.html +Utilizadores com permissão de líder poderão também definir a prioridade/risco e a propriedade das +Suites de Caso de Teste (categorias) e criar marcos de teste.

              + +

              Nota: É possível que os utilizadores não possam ver uma lista suspensa que contenha os Planos de Testes. +Nesta situação, todos os links (exceto para os líderes ativos) estarão desativados. Se você +estiver nesta situação, contacte a administração do TestLink para lhe conceder os +direitos de Projecto adequado ou criar um Plano de Teste para você.

              "; + +// custom_fields.html $TLS_hlp_customFields = "

              Campos Personalizados

              Seguem alguns factos sobre a implementação de Campos Personalizados:

                @@ -97,13 +97,13 @@ atributos:

                • Nome do Campo Personalizado.
                • -
                • Capturar o nome da variável (ex: Este é o valor que é fornecido para a API lang_get(), +
                • Capturar o nome da variável (ex: Este é o valor que é fornecido para a API lang_get(), ou mostrado como se não for encontrado no ficheiro de linguagem).
                • Tipo do Campo Personalizado (texto, numérico, decimal, enumeração, email).
                • -
                • Possibilidade de enumerar os valores (ex: RED|YELLOW|BLUE), aplicável a uma lista, lista de multiseleção +
                • Possibilidade de enumerar os valores (ex: RED|YELLOW|BLUE), aplicável a uma lista, lista de multiseleção e tipos de combo.
                  Utilize o caractere pipe ('|') para -separar os possíveis valores para uma enumeração. Um dos possíveis valores pode ser +separar os possíveis valores para uma enumeração. Um dos possíveis valores pode ser um texto vazio.
                • Valor por omissão: NÃO IMPLEMENTADO AINDA.
                • @@ -120,26 +120,26 @@
                • Ativado no Planeamento do Plano de Teste. O utilizador pode alterar o valor durante o planeamento do Plano de Teste (adicionar Casos de Teste ao Plano de Teste).
                • Disponível para o utilizador escolher o tipo de campo.
                -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

                Executar Casos de Teste

                -

                Permite aos utilizadores 'Executar' os Casos de Teste. Uma Execução propriamente -dita é apenas a atribuição do resultado de um Caso de Teste (Passou, +

                Permite aos utilizadores 'Executar' os Casos de Teste. Uma Execução propriamente +dita é apenas a atribuição do resultado de um Caso de Teste (Passou, Falhado ou Bloqueado) de uma compilação selecionada.

                -

                O acesso a um Gestor de Ocorrências (Bugtracker) pode ser configurado. O utilizador pode adicionar diretamente novas Ocorrências e navegar pelas existentes. Consulte o manual de instalação para mais detalhes.

                "; - -//bug_add.html +

                O acesso a um Gestor de Ocorrências (Bugtracker) pode ser configurado. O utilizador pode adicionar diretamente novas Ocorrências e navegar pelas existentes. Consulte o manual de instalação para mais detalhes.

                "; + +// bug_add.html $TLS_hlp_btsIntegration = "

                Adicionar Ocorrências ao Caso de Teste

                (apenas se estiver configurado) -O TestLink possui uma integração muito simples com os sistemas de Gestão de Ocorrências, -mas não é capaz de enviar um pedido de abertura de Ocorrência ao Gestor de Ocorrências ou receber de volta o ID da Ocorrência. +O TestLink possui uma integração muito simples com os sistemas de Gestão de Ocorrências, +mas não é capaz de enviar um pedido de abertura de Ocorrência ao Gestor de Ocorrências ou receber de volta o ID da Ocorrência. A integração é feita utilizando um link para a página do Gestor de Ocorrências, com as seguintes características:

                • Inserir nova Ocorrência.
                • Exibição das informações da Ocorrência.
                -

                +

                Processo para adicionar uma nova Ocorrência

                @@ -148,15 +148,15 @@

              • Passo 2: Anote o ID da Ocorrência gerada pelo Gestor de Ocorrências.
              • Passo 3: Escreva o ID da Ocorrência no campo de entrada.
              • Passo 4: Clique no botão Adicionar Ocorrência
              • -
              +
            Depois de fechar a página de Adição de Ocorrência, os dados relevantes da Ocorrência serão mostrados na página de execução. -

            "; - -// execFilter.html +

            "; + +// execFilter.html $TLS_hlp_executeFilter = "

            Configurações

            -

            Em Configurações é possível que você selecione o Plano de Teste, a Build e +

            Em Configurações é possível que você selecione o Plano de Teste, a Build e a Plataforma (se disponível) para ser executado.

            Plano de Teste

            @@ -170,85 +170,82 @@

            Filtros

            Os Filtros proporcionam a oportunidade de influenciar ainda mais o conjunto de Casos de Teste mostrados. -Através dos Filtros é possível diminuir o conjunto de Casos de Teste mostrados. Selecione +Através dos Filtros é possível diminuir o conjunto de Casos de Teste mostrados. Selecione os filtros desejados e clique no botão \"Aplicar\".

            -

            Os Filtros Avançados permitem que você especifique um conjunto de valores para filtros aplicáveis +

            Os Filtros Avançados permitem que você especifique um conjunto de valores para filtros aplicáveis ​​usando Ctrl + Clique dentro de cada ListBox.

            Filtro de Palavra Chave

            -

            Você pode filtrar os Casos de Teste pelas Palavras Chave que foram atribuídas. Você pode escolher " . -"múltiplas Palavras Chave utilizando Ctrl + Clique. Se você escolher mais que uma Palavra Chave, você pode " . -"decidir se apenas serão mostrados os Casos de Teste que contêm todas as Palavras Chave selecionadas " . -"(botão \"E\") ou pelo menos uma das Palavras Chave escolhidas (botão \"OU\").

            +

            Você pode filtrar os Casos de Teste pelas Palavras Chave que foram atribuídas. Você pode escolher " . + "múltiplas Palavras Chave utilizando Ctrl + Clique. Se você escolher mais que uma Palavra Chave, você pode " . + "decidir se apenas serão mostrados os Casos de Teste que contêm todas as Palavras Chave selecionadas " . + "(botão \"E\") ou pelo menos uma das Palavras Chave escolhidas (botão \"OU\").

            Filtro de Prioridade

            -

            Você pode filtrar os Casos de Teste pela prioridade do Teste. A prioridade do Teste é a \"importância do Caso de Teste\" " . -"combinado com \"a urgência do Teste\" dentro do Plano de Teste atual.

            +

            Você pode filtrar os Casos de Teste pela prioridade do Teste. A prioridade do Teste é a \"importância do Caso de Teste\" " . + "combinado com \"a urgência do Teste\" dentro do Plano de Teste atual.

            Filtro de Utilizador

            -

            Você pode filtrar os Casos de Teste que não estão atribuídos (\"Ninguém\") ou atribuídos a \"Alguém\". " . -"Você também pode filtrar os Casos de Teste que são atribuídos a um Testador específico. Se você escolheu um Testador " . -"específico, também existe a possibilidade de mostrar os Casos de Teste que estão por serem atribuídos " . -"(Filtros avançados estão disponíveis).

            +

            Você pode filtrar os Casos de Teste que não estão atribuídos (\"Ninguém\") ou atribuídos a \"Alguém\". " . + "Você também pode filtrar os Casos de Teste que são atribuídos a um Testador específico. Se você escolheu um Testador " . + "específico, também existe a possibilidade de mostrar os Casos de Teste que estão por serem atribuídos " . + "(Filtros avançados estão disponíveis).

            Filtro de Resultado

            -

            Você pode filtrar os Casos de Teste pelos resultados (Filtros avançados estão disponíveis). Você pode filtrar por " . -"resultado \"na Build escolhida para a execução\", \"na última execução\", \"em TODAS as Builds\", " . -"\"em QUALQUER Build\" e \"em uma Build específica\". Se \"uma Build específica\" for escolhida, então você pode " . -"especificar a Build.

            "; - - -// newest_tcversions.html +

            Você pode filtrar os Casos de Teste pelos resultados (Filtros avançados estão disponíveis). Você pode filtrar por " . + "resultado \"na Build escolhida para a execução\", \"na última execução\", \"em TODAS as Builds\", " . + "\"em QUALQUER Build\" e \"em uma Build específica\". Se \"uma Build específica\" for escolhida, então você pode " . + "especificar a Build.

            "; + +// newest_tcversions.html $TLS_hlp_planTcModified = "

            Versões mais recentes do Caso de Teste

            -

            Todo o conjunto de Casos de Teste ligados ao Plano de Teste é analisado, e uma lista de Casos +

            Todo o conjunto de Casos de Teste ligados ao Plano de Teste é analisado, e uma lista de Casos de Teste que têm uma versão mais recente é mostrada (contra o conjunto atual do Plano de Teste). -

            "; - - -// requirementsCoverage.html +

            "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

            Cobertura de Requisitos


            -

            Este recurso permite mapear uma cobertura de utilizador ou Requisitos do sistema +

            Este recurso permite mapear uma cobertura de utilizador ou Requisitos do sistema por Casos de Teste. Navegue através do link \"Especificar Requisitos\" na tela principal.

            Especificação de Requisitos

            -

            Os Requisitos estão agrupados no documento 'Especificação de Requisitos' que está relacionado ao -Projecto de Testes.
            O TestLink ainda não suporta versões para a Especificação de Requisitos e -também para os Requisitos. Assim, a versão do documento deve ser adicionada depois do +

            Os Requisitos estão agrupados no documento 'Especificação de Requisitos' que está relacionado ao +Projecto de Testes.
            O TestLink ainda não suporta versões para a Especificação de Requisitos e +também para os Requisitos. Assim, a versão do documento deve ser adicionada depois do Título da Especificação. -O utilizador pode adicionar uma descrição simples ou uma nota no campo Âmbito.

            +O utilizador pode adicionar uma descrição simples ou uma nota no campo Âmbito.

            -

            Sobrescrever o contador de Requisitos serve para avaliar a cobertura dos Requisitos no caso +

            Sobrescrever o contador de Requisitos serve para avaliar a cobertura dos Requisitos no caso de nem todos os Requisitos estarem adicionados ao TestLink. -

            O valor 0 significa que a contagem atual de Requisitos é usado para métricas.

            -

            Ex: SRS inclui 200 Requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes +

            O valor 0 significa que a contagem atual de Requisitos é usado para métricas.

            +

            Ex: SRS inclui 200 Requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes é de 25% (se todos estes Requisitos forem testados).

            Requisitos

            -

            Clique no título para criar uma Especificação de Requisitos. Você pode criar, editar, apagar +

            Clique no título para criar uma Especificação de Requisitos. Você pode criar, editar, apagar ou importar Requisitos para este documento. Cada Requisito tem título, âmbito e status. O status deve ser \"Válido\" ou \"Não testado\". Requisitos não testados não são contabilizados -para as métricas. Este parâmetro deve ser utilizado para características não implementadas -e Requisitos modelados incorretamente.

            +para as métricas. Este parâmetro deve ser utilizado para características não implementadas +e Requisitos modelados incorretamente.

            -

            Você pode criar novos Casos de Teste para os Requisitos utilizando multi ações para os Requisitos -ativos na tela de especificação de Requisitos. Estes Casos de Teste são criados dentro da Suite de -Teste com nome definido na configuração (padrão é: $tlCfg->req_cfg->default_testsuite_name = +

            Você pode criar novos Casos de Teste para os Requisitos utilizando multi ações para os Requisitos +ativos na tela de especificação de Requisitos. Estes Casos de Teste são criados dentro da Suite de +Teste com nome definido na configuração (padrão é: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Título e Âmbito são copiados destes Casos de Teste.

            -"; - +"; + $TLS_hlp_req_coverage_table = "

            Cobertura:

            Um valor por ex. de \"40% (8/20)\" significa que 20 Casos de Teste devem ser criados para testar completamente este -Requisito. 8 destes já foram criados e associados ao Requisito, com +Requisito. 8 destes já foram criados e associados ao Requisito, com a cobertura de 40 %. -"; - - -// req_edit +"; + +// req_edit $TLS_hlp_req_edit = "

            Links internos no Âmbito:

            -

            Links internos servem ao propósito da criação de links a outros Requisitos / especificações de Requisitos +

            Links internos servem ao propósito da criação de links a outros Requisitos / especificações de Requisitos com uma sintaxe especial. O comportamento dos Links internos pode ser alterado no ficheiro de configuração.

            Uso: @@ -256,7 +253,7 @@ Link para Requisitos: [req]req_doc_id[/req]
            Link para Especificação de Requisitos: [req_spec]req_spec_doc_id[/req_spec]

            -

            O Projecto de Testes do Requisito / Especificação de Requisitos, uma versão e uma âncora +

            O Projecto de Testes do Requisito / Especificação de Requisitos, uma versão e uma âncora também podem ser especificados:
            [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
            Esta sintaxe também funciona para as especificações de Requisito (atributos de versão não tem nenhum efeito).
            @@ -266,10 +263,9 @@

            Sempre que uma alteração é feita, o Testlink irá pedir uma mensagem de log. Esta mensagem de log serve como rastreabilidade. Se apenas o âmbito do Requisito mudou, você pode decidir se deseja criar uma nova revisão ou não. Sempre que alguma coisa além do âmbito é alterado, você é forçado a criar uma nova revisão.

            -"; - - -// req_view +"; + +// req_view $TLS_hlp_req_view = "

            Links Diretos:

            É fácil compartilhar este documento com outros, basta clicar no ícone do globo no topo deste documento para criar um link direto.

            @@ -284,14 +280,13 @@

            Relações de Requisitos são usados ​​para relacionamentos de modelos entre os Requisitos. Relações personalizadas e a opção de permitir relações entre os Requisitos de diferentes Projectos de Testes podem ser configurados no ficheiro de configuração. -Se você definir a relação \"Requisito A é pai do Requisito B\", +Se você definir a relação \"Requisito A é pai do Requisito B\", o Testlink irá definir a relação \"Requisito B é filho do Requisito A\" implicitamente.

            -"; - - -// req_spec_edit +"; + +// req_spec_edit $TLS_hlp_req_spec_edit = "

            Links internos no Âmbito:

            -

            Links internos servem ao propósito da criação de links a outros Requisitos / especificações de Requisitos +

            Links internos servem ao propósito da criação de links a outros Requisitos / especificações de Requisitos com uma sintaxe especial. O comportamento dos Links internos pode ser alterado no ficheiro de configuração.

            Uso: @@ -299,25 +294,23 @@ Link para Requisitos: [req]req_doc_id[/req]
            Link para Especificação de Requisitos: [req_spec]req_spec_doc_id[/req_spec]

            -

            O Projecto de Testes do Requisito / Especificação de Requisitos, uma versão e uma âncora +

            O Projecto de Testes do Requisito / Especificação de Requisitos, uma versão e uma âncora também podem ser especificados:
            [req tproj=<tproj_prefix> anchor=<anchor_name> version=<version_number>]req_doc_id[/req]
            Esta sintaxe também funciona para as especificações de Requisito (atributos de versão não tem nenhum efeito).
            Se você não especificar a versão do Requisito completo, todas as versões serão mostradas.

            -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

            Sobre 'Campos Personalizados salvos'

            -Se você tiver definido e atribuído ao Projecto de Testes,
            +Se você tiver definido e atribuído ao Projecto de Testes,
            Campos Personalizados com:
            'Mostrar no desenho do Plano de Teste=true' e
            'Activar no desenho do Plano de Teste=true'
            você irá ver nesta página APENAS os Casos de Teste ligados ao Plano de Teste. -"; - - -// resultsByTesterPerBuild.tpl +"; + +// resultsByTesterPerBuild.tpl $TLS_hlp_results_by_tester_per_build_table = "Mais informações sobre os Testadores
            Se você clicar no nome do Testador nesta tabela, você irá ter uma visão mais detalhada sobre todos os Casos de Teste atribuídos para esse utilizador e o seu progresso de teste.

            @@ -325,11 +318,10 @@ Este relatório mostra os Casos de Teste, que são atribuídos a um utilizador específico e foram executados com base em cada Build ativa. Mesmo se um Caso de Teste foi executado por outro utilizador que não o utilizador atribuído, o Caso de Teste irá aparecer como executado pelo utilizador atribuído. -"; - - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/pt_PT/texts.php b/locale/pt_PT/texts.php index ef9d546729..39b27adbae 100644 --- a/locale/pt_PT/texts.php +++ b/locale/pt_PT/texts.php @@ -1,100 +1,95 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * @package TestLink - * @author Martin Havlat - * @copyright 2003-2009, TestLink community - * @version GIT: $Id: texts.php,v 1.9.17 2017/02/20 23:54:34 HelioGuilherme66 Exp $ - * @link http://www.testlink.org/ - * - **/ - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['error'] = "Erro na Aplicação"; -$TLS_htmltext['error'] = "

            Ocorreu um erro inesperado. Por favor, verifique o event viewer ou " . - "logs para detalhes.

            Você está convidado para relatar o problema. Por favor, visite nosso " . - "website.

            "; - - - -$TLS_htmltext_title['assignReqs'] = "Atribuir Requisitos aos Casos de Teste"; -$TLS_htmltext['assignReqs'] = "

            Objetivo:

            -

            Os utilizadores podem criar relacionamentos entre requisitos e casos de teste. Um arquiteto pode -definir relacionamentos 0..n para 0..n., isto é, um caso de teste pode ser atribuído para nenhum, um ou mais -requisitos e vice versa. Assim a matriz de rastreabilidade ajuda a investigar a cobertura -dos requisitos de teste e finalmente quais falharam durante os testes. Esta +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * @package TestLink + * @author Martin Havlat + * @copyright 2003-2009, TestLink community + * @version GIT: $Id: texts.php,v 1.9.17 2017/02/20 23:54:34 HelioGuilherme66 Exp $ + * @link http://www.testlink.org/ + * + **/ + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['error'] = "Erro na Aplicação"; +$TLS_htmltext['error'] = "

            Ocorreu um erro inesperado. Por favor, verifique o event viewer ou " . + "logs para detalhes.

            Você está convidado para relatar o problema. Por favor, visite nosso " . + "website.

            "; + +$TLS_htmltext_title['assignReqs'] = "Atribuir Requisitos aos Casos de Teste"; +$TLS_htmltext['assignReqs'] = "

            Objetivo:

            +

            Os utilizadores podem criar relacionamentos entre requisitos e casos de teste. Um arquiteto pode +definir relacionamentos 0..n para 0..n., isto é, um caso de teste pode ser atribuído para nenhum, um ou mais +requisitos e vice versa. Assim a matriz de rastreabilidade ajuda a investigar a cobertura +dos requisitos de teste e finalmente quais falharam durante os testes. Esta análise serve como entrada para o próximo planeamento de teste.

            Iniciar:

              -
            1. Escolha um Caso de Teste na árvore à esquerda. O combo box com a lista de +
            2. Escolha um Caso de Teste na árvore à esquerda. O combo box com a lista de Especificações de Requisitos é exibido no topo da área de trabalho.
            3. -
            4. Escolha um documento de Especificação se mais de um estiver definido. +
            5. Escolha um documento de Especificação se mais de um estiver definido. O TestLink recarregará a página automaticamente.
            6. -
            7. Um bloco ao centro da área de trabalho lista todos os requisitos (para a Especificação selecionada), que - está conectada ao caso de teste. O bloco abaixo 'Requisitos Disponíveis' lista todos - os requisitos que não estão relacionados - ao caso de teste selecionado. Um arquiteto pode selecionar requisitos que são cobertos por este - caso de teste e então clicar em 'Atribuir'. Este novo caso de teste atribuído será exibido no +
            8. Um bloco ao centro da área de trabalho lista todos os requisitos (para a Especificação selecionada), que + está conectada ao caso de teste. O bloco abaixo 'Requisitos Disponíveis' lista todos + os requisitos que não estão relacionados + ao caso de teste selecionado. Um arquiteto pode selecionar requisitos que são cobertos por este + caso de teste e então clicar em 'Atribuir'. Este novo caso de teste atribuído será exibido no bloco central 'Requisitos atribuídos'.
            9. -
            "; +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Especificação de Teste"; +$TLS_htmltext['editTc'] = "

            A Especificação de Teste permite aos utilizadores visualizar " . + "e editar todas as Suites de Teste e Casos de Teste existentes. " . + "Os Casos de Teste são versionados e todas as versões anteriores estão disponíveis e podem ser " . + "visualizadas e geridas aqui.

            - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Especificação de Teste"; -$TLS_htmltext['editTc'] = "

            A Especificação de Teste permite aos utilizadores visualizar " . - "e editar todas as Suites de Teste e Casos de Teste existentes. " . - "Os Casos de Teste são versionados e todas as versões anteriores estão disponíveis e podem ser " . - "visualizadas e geridas aqui.

            -

            Iniciar:

              -
            1. Selecione o seu Projecto de Testes na árvore de navegação (o nó principal). Observe: " . - "Você poderá sempre trocar o Projecto de Testes activo selecionando um diferente da " . - "lista drop-down do canto superior esquerdo.
            2. -
            3. Crie uma nova Suite de Teste clicando em Nova Suite de Teste. As Suites de Teste podem " . - "trazer a estrutura da sua documentação de teste conforme suas convenções (testes funcionais/não-funcionais " . - ", produtos, componentes ou características, solicitações de mudança, etc.). A descrição da " . - "Suite de Teste poderia conter o âmbito dos Casos de Teste incluídos, configuração padrão, " . - "links para documentos relevantes, limitações e outras informações habituais. Em geral, " . - "todas anotações que são comuns às Suites de Teste. As Suites de Teste seguem " . - "a metáfora do "diretório", assim os utilizadores podem mover e copiar Suites de Teste dentro " . - "do Projecto de Testes. Além disso, eles podem ser importados ou exportados (incluindo os Casos de Teste nele contidos).
            4. -
            5. Suites de Teste são pastas escaláveis. Os utilizadores podem mover ou copiar Suites de Teste dentro " . - "do Projecto de Testes. Suites de Teste podem ser importadas ou exportadas (incluindo os Casos de Teste). -
            6. Selecione sua mais nova Suite de Teste criada na árvore de navegação e crie " . - "um novo Caso de Teste clicando em Criar Caso(s) de Teste. Um Caso de Teste especifica " . - "um cenário de testes particular, resultados esperados e campos personalizados definidos " . - "no Projecto de Testes (consulte o manual do utilizador para maiores informações). Também é possível " . - "atribuir Palavras Chave para melhorar a rastreabilidade.
            7. +
            8. Selecione o seu Projecto de Testes na árvore de navegação (o nó principal). Observe: " . + "Você poderá sempre trocar o Projecto de Testes activo selecionando um diferente da " . + "lista drop-down do canto superior esquerdo.
            9. +
            10. Crie uma nova Suite de Teste clicando em Nova Suite de Teste. As Suites de Teste podem " . + "trazer a estrutura da sua documentação de teste conforme suas convenções (testes funcionais/não-funcionais " . + ", produtos, componentes ou características, solicitações de mudança, etc.). A descrição da " . + "Suite de Teste poderia conter o âmbito dos Casos de Teste incluídos, configuração padrão, " . + "links para documentos relevantes, limitações e outras informações habituais. Em geral, " . + "todas anotações que são comuns às Suites de Teste. As Suites de Teste seguem " . + "a metáfora do "diretório", assim os utilizadores podem mover e copiar Suites de Teste dentro " . + "do Projecto de Testes. Além disso, eles podem ser importados ou exportados (incluindo os Casos de Teste nele contidos).
            11. +
            12. Suites de Teste são pastas escaláveis. Os utilizadores podem mover ou copiar Suites de Teste dentro " . + "do Projecto de Testes. Suites de Teste podem ser importadas ou exportadas (incluindo os Casos de Teste). +
            13. Selecione sua mais nova Suite de Teste criada na árvore de navegação e crie " . + "um novo Caso de Teste clicando em Criar Caso(s) de Teste. Um Caso de Teste especifica " . + "um cenário de testes particular, resultados esperados e campos personalizados definidos " . + "no Projecto de Testes (consulte o manual do utilizador para maiores informações). Também é possível " . + "atribuir Palavras Chave para melhorar a rastreabilidade.
            14. Navegue pela árvore de navegação do lado esquerdo e edite os dados. Os Casos de Teste armazenam histórico próprio.
            15. Atribua as suas Especificações de Teste criadas ao Plano de Teste quando seus Casos de Teste estiverem prontos.
            16. + \"javascript:open_help_window('glossary','$locale');\">Plano de Teste quando seus Casos de Teste estiverem prontos.
            -

            Com o TestLink você organiza os Casos de Teste em Suites de Teste." . -" Suites de Teste podem ser aninhadas em outras Suites de Teste, permitindo a você criar hierarquias de Suites de Teste. - Então você pode imprimir esta informação juntamente com o Caso de Teste.

            "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Página de Pesquisa de Casos de Teste"; -$TLS_htmltext['searchTc'] = "

            Objetivo:

            +

            Com o TestLink você organiza os Casos de Teste em Suites de Teste." . + " Suites de Teste podem ser aninhadas em outras Suites de Teste, permitindo a você criar hierarquias de Suites de Teste. + Então você pode imprimir esta informação juntamente com o Caso de Teste.

            "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Página de Pesquisa de Casos de Teste"; +$TLS_htmltext['searchTc'] = "

            Objetivo:

            Navegue de acordo com Palavras Chave e/ou texto procuradas. A pesquisa não é sensível a maiúsculas. Os resultados incluem apenas Casos de Teste do Projecto de Testes atual.

            @@ -105,15 +100,15 @@
          • Escolha Palavras Chave exigidas ou deixe o valor do campo 'Não aplicado'.
          • Clique no botão Pesquisar.
          • Todos os Casos de Teste cobertos são exibidos. Você pode modificar os Casos de Teste via link 'Título'.
          • -"; - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Página de Pesquisa de Requisitos"; -$TLS_htmltext['searchReq'] = "

            Objetivo:

            - -

            Navegue de acordo com as Palavras Chave e/ou cadeias de texto procuradas. A pesquisa não é +"; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Página de Pesquisa de Requisitos"; +$TLS_htmltext['searchReq'] = "

            Objetivo:

            + +

            Navegue de acordo com as Palavras Chave e/ou cadeias de texto procuradas. A pesquisa não é sensível a maiúsculas. Os resultados apenas incluem requisitos do Projecto de Testes atual.

            Para Pesquisar:

            @@ -129,14 +124,14 @@

            - Apenas os Requisitos dentro do projecto atual serão pesquisados.
            - A pesquisa é sensível a maiúsculas.
            -- Campos vazios não são considerados.

            "; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Página de Pesquisa de Especificação de Requisitos"; -$TLS_htmltext['searchReqSpec'] = "

            Objetivo:

            - -

            Navegue de acordo com as Palavras Chave e/ou cadeias de texto procuradas. A pesquisa não é +- Campos vazios não são considerados.

            "; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Página de Pesquisa de Especificação de Requisitos"; +$TLS_htmltext['searchReqSpec'] = "

            Objetivo:

            + +

            Navegue de acordo com as Palavras Chave e/ou cadeias de texto procuradas. A pesquisa não é sensível a maiúsculas. Os resultados apenas incluem requisitos do Projecto de Testes atual.

            Para Pesquisar:

            @@ -152,123 +147,118 @@

            - Apenas as Especificações de Requisito dentro do projecto atual serão pesquisados.
            - A pesquisa é sensível a maiúsculas.
            -- Campos vazios não são considerados.

            "; -/* end contribution */ - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificação de Testes"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

            Objetivo:

            -

            A partir daqui você pode imprimir um único caso de teste, todos os casos de teste dentro de uma suite +- Campos vazios não são considerados.

            "; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Imprimir Especificação de Testes"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

            Objetivo:

            +

            A partir daqui você pode imprimir um único caso de teste, todos os casos de teste dentro de uma suite ou todos os casos de teste de um Projecto de Testes ou Plano de Teste.

            Iniciar:

            1. -

              Selecione os campos dos casos de teste que você deseja exibir, e então clique em um Caso de Teste, +

              Selecione os campos dos casos de teste que você deseja exibir, e então clique em um Caso de Teste, Suite de Teste, ou Projecto de Testes. Uma página pronta para impressão será exibida.

            2. -
            3. Use a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer -a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. +

            4. Use a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer +a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. Veja Ajuda para maiores informações.

            5. Use a funcionalidade de impressão do seu browser para imprimir a informação presente.
              Nota: Certifique-se de imprimir apenas o frame direito.

            6. -
            "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Modelar Especificação de Requisitos"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

            Você pode gerir documentos de Especificação de Requisitos.

            +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Modelar Especificação de Requisitos"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

            Você pode gerir documentos de Especificação de Requisitos.

            Especificação de Requisitos

            -

            Requisitos estão agrupados por documentos de Especificação de Requisitos , os quais estão relacionados ao -Projecto de Testes.
            O TestLink não suporta (ainda) versões para Especificação de Requisitos +

            Requisitos estão agrupados por documentos de Especificação de Requisitos , os quais estão relacionados ao +Projecto de Testes.
            O TestLink não suporta (ainda) versões para Especificação de Requisitos e também Requisitos. Logo, a versão do documento deve ser inserida após o Título da Especificação. -Um utilizador pode inserir uma descrição simples ou notas no campo Âmbito.

            +Um utilizador pode inserir uma descrição simples ou notas no campo Âmbito.

            -

            Sobrescrever o contador de Requisitos serve para +

            Sobrescrever o contador de Requisitos serve para avaliar a cobertura dos requisitos no caso de nem todos os requisitos estarem adicionados ao TestLink. -O valor 0 significa que o contador de requisitos atual é utilizado +O valor 0 significa que o contador de requisitos atual é utilizado para métricas.

            -

            E.g. SRS inclui 200 requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes é de +

            E.g. SRS inclui 200 requisitos, mas somente 50 são adicionados ao Plano de Teste. A cobertura de testes é de 25% (se todos estes requisitos forem testados).

            Requisitos

            -

            Clique no título da Especificação de Requisitos criada, e se nenhuma existir, " . - "clique no nó do projecto para criar uma. Você pode criar, editar, excluir +

            Clique no título da Especificação de Requisitos criada, e se nenhuma existir, " . + "clique no nó do projecto para criar uma. Você pode criar, editar, excluir ou importar requisitos para o documento. Cada Requisito tem um título, âmbito e estado. -O estado deve ser 'Válido' ou 'Não testável'. Requisitos Não Testáveis não são contabilizados -para métricas. Este parâmetro deve ser utilizado para características não implementadas e +O estado deve ser 'Válido' ou 'Não testável'. Requisitos Não Testáveis não são contabilizados +para métricas. Este parâmetro deve ser utilizado para características não implementadas e requisitos modelados incorretamente.

            -

            Você pode criar novos casos de teste para os requisitos utilizando multi ações para os -requisitos ativos na tela de especificação de requisitos. Estes Casos de Teste são criados dentro da Suite de Teste -com nome definido na configuração (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). Título e Âmbito são copiados destes Casos de Teste.

            "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Imprimir documento de Especificação de Requisitos"; //printReq +

            Você pode criar novos casos de teste para os requisitos utilizando multi ações para os +requisitos ativos na tela de especificação de requisitos. Estes Casos de Teste são criados dentro da Suite de Teste +com nome definido na configuração (default is: \$tlCfg->req_cfg->default_testsuite_name = +'Test suite created by Requirement - Auto';). Título e Âmbito são copiados destes Casos de Teste.

            "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Imprimir documento de Especificação de Requisitos"; // printReq $TLS_htmltext['printReqSpec'] = "

            Objetivo:

            -

            Através desta opção você pode imprimir um Requisito único, todos os requisitos de uma Especificação de Requisitos, +

            Através desta opção você pode imprimir um Requisito único, todos os requisitos de uma Especificação de Requisitos, ou todos os requisitos de um Projecto de Testes.

            Iniciar:

            1. -

              Selecione as partes dos requisitos que você deseja exibir, e então clique em Requisito, +

              Selecione as partes dos requisitos que você deseja exibir, e então clique em Requisito, Especificação de Requisitos ou Projecto de Testes. A visualização da impressão será exibida.

            2. -
            3. Utilize a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer -a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. +

            4. Utilize a drop-box \"Mostrar Como\" no painel de navegação para especificar se você quer +a informação exibida como HTML, como documento do Open Office Writer ou num documento do Microsoft Word. Veja Ajuda para mais informações.

            5. Use a funcionalidade de impressão do seu browser para imprimir a informação presente.
              Nota: Certifique-se de imprimir apenas o frame direito.

            6. -
            "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Atribuição de Palavras Chave"; -$TLS_htmltext['keywordsAssign'] = "

            Objetivo:

            -

            A página de Atribuição de Palavras Chave é o lugar onde os utilizadores podem +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Atribuição de Palavras Chave"; +$TLS_htmltext['keywordsAssign'] = "

            Objetivo:

            +

            A página de Atribuição de Palavras Chave é o lugar onde os utilizadores podem atribuir em lotes Palavras Chave às Suites de Teste ou Casos de Teste.

            Para Atribuir Palavras Chave:

              -
            1. Selecione uma Suite de Teste ou Caso de Teste na árvore +
            2. Selecione uma Suite de Teste ou Caso de Teste na árvore à esquerda.
            3. -
            4. A caixa no topo da página que mostra informações do lado direito - permitirá a você atribuir Palavras Chave para os casos de +
            5. A caixa no topo da página que mostra informações do lado direito + permitirá a você atribuir Palavras Chave para os casos de teste individualmente.
            6. -
            7. A seleção abaixo permite a você atribuir Casos de Teste num +
            8. A seleção abaixo permite a você atribuir Casos de Teste num nível mais granular.

            Informação Importante quanto à Atribuição de Palavras Chave nos Planos de Testes:

            -

            Atribuir Palavras Chave à Suite de Teste afetará Casos de Teste -no seu Plano de Teste apenas se o Plano de Teste conter a última versão do Caso de Teste. -Caso contrário, se o Plano de Teste conter versões mais antigas do Caso de Teste, as atribuições que você +

            Atribuir Palavras Chave à Suite de Teste afetará Casos de Teste +no seu Plano de Teste apenas se o Plano de Teste conter a última versão do Caso de Teste. +Caso contrário, se o Plano de Teste conter versões mais antigas do Caso de Teste, as atribuições que você fez NÃO aparecerão no Plano de Teste.

            -

            O TestLink usa esta abordagem para que versões mais antigas dos Casos de Teste nos Planos de Testes não sejam afetadas pela atribuição -de Palavras Chave que você fez nas versões mais recentes dos Casos de Teste. Se você deseja seus -Casos de Teste no seu Plano de Teste sejam atualizados, primeiro verifique se eles estão atualizados -utilizando a funcionalidade 'Atualizar Versão dos Casos de Teste' antes de fazer a atribuição das Palavras Chave.

            "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Execução dos Casos de Teste"; -$TLS_htmltext['executeTest'] = "

            Objetivo:

            - -

            Permite aos utilizadores executar os Casos de Teste. O Utilizador pode atribuir resultados -aos Casos de Teste nos Ciclo de Teste. Veja a ajuda para mais informações sobre filtros e configurações " . - "(clique no ícone interrogação).

            +

            O TestLink usa esta abordagem para que versões mais antigas dos Casos de Teste nos Planos de Testes não sejam afetadas pela atribuição +de Palavras Chave que você fez nas versões mais recentes dos Casos de Teste. Se você deseja seus +Casos de Teste no seu Plano de Teste sejam atualizados, primeiro verifique se eles estão atualizados +utilizando a funcionalidade 'Atualizar Versão dos Casos de Teste' antes de fazer a atribuição das Palavras Chave.

            "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Execução dos Casos de Teste"; +$TLS_htmltext['executeTest'] = "

            Objetivo:

            + +

            Permite aos utilizadores executar os Casos de Teste. O Utilizador pode atribuir resultados +aos Casos de Teste nos Ciclo de Teste. Veja a ajuda para mais informações sobre filtros e configurações " . + "(clique no ícone interrogação).

            Iniciar:

            @@ -276,19 +266,19 @@
          • O utilizador deve definir um Ciclo de Teste para o Plano de Teste.
          • Selecionar um Ciclo de Teste no menu drop down
          • Se você quiser ver apenas alguns poucos casos de teste, em vez de toda a árvore, - você pode escolher quais filtros aplicar. Clique no botão \"Aplicar\" - depois que você alterar os filtros.
          • + você pode escolher quais filtros aplicar. Clique no botão \"Aplicar\" + depois que você alterar os filtros.
          • Clique no Caso de Teste no menu em árvore.
          • Preencha o resultado do Caso de Teste, suas respetivas notas e/ou Ocorrências.
          • Grave os resultados.
          • -

            Nota: O TestLink deve ser configurado para colaborar com seu Gestor de Ocorrências -se você quiser criar ou rastrear problemas reportados diretamente da GUI.

            "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Descrição dos Relatórios de Teste e Métricas"; -$TLS_htmltext['showMetrics'] = "

            Os relatórios estão relacionados a um Plano de Teste " . - "(definido no topo do navegador). Este Plano de Teste pode diferir do Plano de Teste +

            Nota: O TestLink deve ser configurado para colaborar com seu Gestor de Ocorrências +se você quiser criar ou rastrear problemas reportados diretamente da GUI.

            "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Descrição dos Relatórios de Teste e Métricas"; +$TLS_htmltext['showMetrics'] = "

            Os relatórios estão relacionados a um Plano de Teste " . + "(definido no topo do navegador). Este Plano de Teste pode diferir do Plano de Teste corrente para execução. Você também pode selecionar formatos dos relatórios:

            • Normal - relatório é exibido em uma página web
            • @@ -311,69 +301,69 @@

              Métricas Gerais do Plano de Teste

              Esta página apenas mostra o último estado de um Plano de Teste por Suite de Teste, Testador e Palavras Chave. -O 'último estado' é determinado pelo ciclo mais recente onde os Casos de Teste foram executados. Por exemplo, +O 'último estado' é determinado pelo ciclo mais recente onde os Casos de Teste foram executados. Por exemplo, se um Caso de Teste foi executado em diversos Ciclos de Teste, apenas o último resultado é considerado.

              O 'último resultado' é um conceito utilizado em vários relatórios, e é determinado como a seguir:

                -
              • A ordem em cada Ciclo de Teste (Meta) é adicionada ao Plano de Teste determina qual é o Ciclo de Teste mais recente. Os resultados do Ciclo de Teste -mais recente prevalecerão sobre os Ciclos de Teste mais antigos. Por exemplo, se você marcar um teste com +
              • A ordem em cada Ciclo de Teste (Meta) é adicionada ao Plano de Teste determina qual é o Ciclo de Teste mais recente. Os resultados do Ciclo de Teste +mais recente prevalecerão sobre os Ciclos de Teste mais antigos. Por exemplo, se você marcar um teste com estado 'Falhou' no Ciclo de Teste 1 e no Ciclo de Teste 2 como 'Passou', o último resultado será 'Passou'.
              • -
              • Se um Caso de Teste é executado diversas vezes em um mesmo Ciclo de Teste, o resultado mais recente -prevalecerá. Por exemplo, se o Ciclo de Teste 3 é libertado para a equipa de testes e o Testador 1 marcar 'Passou' as 14h, +
              • Se um Caso de Teste é executado diversas vezes em um mesmo Ciclo de Teste, o resultado mais recente +prevalecerá. Por exemplo, se o Ciclo de Teste 3 é libertado para a equipa de testes e o Testador 1 marcar 'Passou' as 14h, e o Testador 2 marcar 'Falhou' as 15h, isto aparecerá como 'Falhou'.
              • -
              • os Casos de Teste marcados como 'Não Executado' no último Ciclo de Teste não serão considerados. Por exemplo, se você marcar -um Caso de Teste como 'Passou' no Ciclo de Teste 1 e não executar no Ciclo de Teste 2, o último resultado será considerado como +
              • os Casos de Teste marcados como 'Não Executado' no último Ciclo de Teste não serão considerados. Por exemplo, se você marcar +um Caso de Teste como 'Passou' no Ciclo de Teste 1 e não executar no Ciclo de Teste 2, o último resultado será considerado como 'Passou'.

              As seguintes tabelas são mostradas:

              • Resultados por Suite de Teste de Nível Top - Lista os resultados Topo de cada Suite de Teste. Total de Casos de Teste, Passou, Falhou, Bloqueado, Não Executado e a percentagem + Lista os resultados Topo de cada Suite de Teste. Total de Casos de Teste, Passou, Falhou, Bloqueado, Não Executado e a percentagem de completos são listados. Um Caso de Teste 'completo' é aquele que foi marcado como Passou, Falhou ou Bloqueado. Os resultados das suites de nível superior incluem as suites filho.
              • Resultados por Palavra Chave - Lista todas as Palavras Chave que estão atribuídas aos Casos de Teste no Plano de Teste corrente, e os resultados associados + Lista todas as Palavras Chave que estão atribuídas aos Casos de Teste no Plano de Teste corrente, e os resultados associados a eles.
              • Resultados por Testador - Lista cada Testador que tem Casos de Teste associados a ele no Plano de Teste corrente. Os Casos de Teste que + Lista cada Testador que tem Casos de Teste associados a ele no Plano de Teste corrente. Os Casos de Teste que não estão atribuídos são calculados abaixo com a descrição 'desatribuir'.

              Estado Geral dos Ciclos de Teste

              -

              Lista os resultados de execução para cada Ciclo de Teste. Para cada Ciclo de Teste, o total de Casos de Teste, total com Passou, -% Passou, total Falha, % Falha, Bloqueado, % Bloqueado, Não Executado e % Não Executado. Se um Caso de Teste foi executado +

              Lista os resultados de execução para cada Ciclo de Teste. Para cada Ciclo de Teste, o total de Casos de Teste, total com Passou, +% Passou, total Falha, % Falha, Bloqueado, % Bloqueado, Não Executado e % Não Executado. Se um Caso de Teste foi executado duas vezes no mesmo Ciclo de Teste, será considerada a execução mais recente.

              Métricas da Consulta

              Este relatório consiste numa página com o formulário de consulta e uma página com os resultados, a qual contém os dados da consulta. -A página com o formulário de consulta apresenta 4 controlos. Cada controlo está definido com um valor padrão que -maximiza o número de Casos de Teste e Ciclos de Teste que a consulta deve ser executada. Alterar os controlos -permite aos utilizadores filtrar os resultados e gerar relatórios específicos para Testadores em específico, Palavras Chave, Suites de Teste +A página com o formulário de consulta apresenta 4 controlos. Cada controlo está definido com um valor padrão que +maximiza o número de Casos de Teste e Ciclos de Teste que a consulta deve ser executada. Alterar os controlos +permite aos utilizadores filtrar os resultados e gerar relatórios específicos para Testadores em específico, Palavras Chave, Suites de Teste e combinação de Ciclos de Teste.

                -
              • Palavras Chave 0->1 Palavras Chave podem ser selecionadas. Por padrão, nenhuma Palavra Chave é selecionada. Se uma Palavra Chave não é -selecionada, então todos os Casos de Teste serão considerados indiferentemente de atribuição de Palavras Chave. Palavras Chave são atribuídas -na especificação de testes ou na página Gerir Palavra Chave. Palavras Chave atribuídas aos Casos de Teste alcançam todos os Planos de Testes, e -também todas as versões de um Caso de Teste. Se você está interessado nos resultados para uma Palavra Chave específica, +
              • Palavras Chave 0->1 Palavras Chave podem ser selecionadas. Por padrão, nenhuma Palavra Chave é selecionada. Se uma Palavra Chave não é +selecionada, então todos os Casos de Teste serão considerados indiferentemente de atribuição de Palavras Chave. Palavras Chave são atribuídas +na especificação de testes ou na página Gerir Palavra Chave. Palavras Chave atribuídas aos Casos de Teste alcançam todos os Planos de Testes, e +também todas as versões de um Caso de Teste. Se você está interessado nos resultados para uma Palavra Chave específica, você deverá alterar este controlo.
              • -
              • Testador 0->1 Testadores podem ser selecionados. Por padrão, nenhum Testador é selecionado. Se um Testador não é selecionado, -então todos os Casos de Teste serão considerados indiferentes de Testador atribuído. Atualmente não há funcionalidade -para pesquisar por Casos de Teste não atribuídos. O Testador é atribuído através da página 'Atribuir Casos de Teste para Execução' -e é feito com base no Plano de Teste. Se você está interessado em trabalhar com um Testador em específico, você deve +
              • Testador 0->1 Testadores podem ser selecionados. Por padrão, nenhum Testador é selecionado. Se um Testador não é selecionado, +então todos os Casos de Teste serão considerados indiferentes de Testador atribuído. Atualmente não há funcionalidade +para pesquisar por Casos de Teste não atribuídos. O Testador é atribuído através da página 'Atribuir Casos de Teste para Execução' +e é feito com base no Plano de Teste. Se você está interessado em trabalhar com um Testador em específico, você deve alterar este controlo.
              • -
              • Suite de Teste de Nível Topo 0->n Suites de Teste de Nível Topo podem ser selecionadas. Por padrão, todas as Suites de Teste são selecionadas. -Apenas as Suites de Teste que são selecionadas serão consultadas para as métricas do resultado. Se você está apenas interessado em resultados +
              • Suite de Teste de Nível Topo 0->n Suites de Teste de Nível Topo podem ser selecionadas. Por padrão, todas as Suites de Teste são selecionadas. +Apenas as Suites de Teste que são selecionadas serão consultadas para as métricas do resultado. Se você está apenas interessado em resultados para uma Suite de Teste específica você precisa alterar este controlo.
              • -
              • Ciclos de Teste 1->n Ciclos de Teste podem ser selecionados. Por padrão, todos os Ciclos de Teste são selecionados. Apenas as execuções -realizadas no Ciclo de Teste que você selecionou serão consideradas quando produzirem métricas. Por exemplo, se você quiser -ver quantos Casos de Teste foram executados nos últimos 3 Ciclos de Teste, você precisa alterar este controlo. A seleção de Palavra Chave, -Testador e Suite de Teste de Nível Topo ditarão o número de Casos de Teste do seu Plano de Teste e serão usados para -calcular as métricas por Suite de Teste e Plano de Teste. Por exemplo, se você selecionar o Testador = 'José', Palavra Chave = -'Prioridade 1', e todas as Suites de Teste disponíveis, apenas Casos de Teste com Prioridade 1 atribuídos para José serão considerados. +
              • Ciclos de Teste 1->n Ciclos de Teste podem ser selecionados. Por padrão, todos os Ciclos de Teste são selecionados. Apenas as execuções +realizadas no Ciclo de Teste que você selecionou serão consideradas quando produzirem métricas. Por exemplo, se você quiser +ver quantos Casos de Teste foram executados nos últimos 3 Ciclos de Teste, você precisa alterar este controlo. A seleção de Palavra Chave, +Testador e Suite de Teste de Nível Topo ditarão o número de Casos de Teste do seu Plano de Teste e serão usados para +calcular as métricas por Suite de Teste e Plano de Teste. Por exemplo, se você selecionar o Testador = 'José', Palavra Chave = +'Prioridade 1', e todas as Suites de Teste disponíveis, apenas Casos de Teste com Prioridade 1 atribuídos para José serão considerados. O '# de Casos de Teste' totais que você verá neste relatório serão influenciados por estes 3 controlos. -A seleção dos Ciclos de Teste influenciarão se o Caso de Teste é considerado 'Passou', 'Falhou', 'Bloqueado', ou 'Não Executado'. +A seleção dos Ciclos de Teste influenciarão se o Caso de Teste é considerado 'Passou', 'Falhou', 'Bloqueado', ou 'Não Executado'. Favor classificar com a regra 'Apenas os últimos resultados' à medida em que elas aparecem acima.

              Pressione o botão 'Executar Pesquisa' para prosseguir com a consulta e exibir a página com os resultados.

              @@ -382,42 +372,41 @@
              1. o parâmetro da consulta utilizado para criar o relatório
              2. totais para todo o Plano de Teste
              3. -
              4. por um conjunto de particionamento dos totais (Soma / Passou / Falhou / Bloqueado / Não Executado) e todas execuções realizadas -na Suite de Teste. Se um Caso de Teste foi executado mais de uma vez em múltiplos Ciclos de Teste, todas as execuções que foram gravadas serão -exibidas nos Ciclos de Teste selecionados. No entanto, o resumo para esta Suite de Teste apenas incluirá o último resultado para +
              5. por um conjunto de particionamento dos totais (Soma / Passou / Falhou / Bloqueado / Não Executado) e todas execuções realizadas +na Suite de Teste. Se um Caso de Teste foi executado mais de uma vez em múltiplos Ciclos de Teste, todas as execuções que foram gravadas serão +exibidas nos Ciclos de Teste selecionados. No entanto, o resumo para esta Suite de Teste apenas incluirá o último resultado para o Ciclo de Teste selecionado.

              Relatórios de Casos de Teste Bloqueados, com Falha e Não Executados

              -

              Estes relatórios mostram todos os Casos de Teste Bloqueados, com Falha e Não Executados. A lógica do último resultado dos testes -(que está descrita nas Métricas Gerais do Plano de Teste) é novamente empregada para determinar se um Caso de Teste deve ser -considerado Bloqueado, com Falha ou Não Executado. Casos de Teste Bloqueado e com Falha exibirão as Ocorrências associadas se o Utilizador +

              Estes relatórios mostram todos os Casos de Teste Bloqueados, com Falha e Não Executados. A lógica do último resultado dos testes +(que está descrita nas Métricas Gerais do Plano de Teste) é novamente empregada para determinar se um Caso de Teste deve ser +considerado Bloqueado, com Falha ou Não Executado. Casos de Teste Bloqueado e com Falha exibirão as Ocorrências associadas se o Utilizador estiver utilizando um sistema de Gestão de Falhas.

              Relatório de Testes

              -

              Mostra o estado de cada Caso de Teste em todos os Ciclos de Teste. O resultado da execução mais recente será utilizado -se um Caso de Teste for executado múltiplas vezes em um mesmo Ciclo de Teste. É recomendado exportar este relatório para +

              Mostra o estado de cada Caso de Teste em todos os Ciclos de Teste. O resultado da execução mais recente será utilizado +se um Caso de Teste for executado múltiplas vezes em um mesmo Ciclo de Teste. É recomendado exportar este relatório para o formato em Excel para um fácil manuseio se um grande conjunto de dados estiver em utilização.

              Gráficos - Métricas Gerais do Plano de Teste

              -

              A lógica do último resultado é utilizada para todos os gráficos que você verá. Os gráficos são animados para ajudar +

              A lógica do último resultado é utilizada para todos os gráficos que você verá. Os gráficos são animados para ajudar o utilizador à visualizar as métricas do Plano de Teste atual. Os quatro gráficos fornecidos são:

              • Gráfico de pizza com todos os Casos de Teste com estado Passou, com Falha, Bloqueados e Não Executados
              • Gráfico de barras com os Resultados por Palavra Chave
              • Gráfico de barras com os Resultados por Testador
              • Gráfico de barras com os Resultados por Suites Nível Topo
              -

              As secções e barras dos gráficos são coloridos de modo que o utilizador possa identificar o número aproximado de Casos de Teste com estado +

              As secções e barras dos gráficos são coloridos de modo que o utilizador possa identificar o número aproximado de Casos de Teste com estado Passou, Falhou, Bloqueado e Não Executado.

              Ocorrências por Casos de Teste

              -

              Este relatório mostra cada Caso de Teste com todos as Ocorrências abertas para ele em todo o projecto. -Este relatório apenas está disponível se estiver conectado um Gestor de Ocorrências.

              "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Adicionar / Remover Casos de Teste do Plano de Teste"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

              Objetivo:

              +

              Este relatório mostra cada Caso de Teste com todos as Ocorrências abertas para ele em todo o projecto. +Este relatório apenas está disponível se estiver conectado um Gestor de Ocorrências.

              "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Adicionar / Remover Casos de Teste do Plano de Teste"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

              Objetivo:

              Permite aos utilizadores (com perfil de Líder de Testes) adicionar ou remover Casos de Teste do Plano de Teste.

              Para adicionar ou remover Casos de Teste:

              @@ -425,11 +414,11 @@
            • Clique na Suite de Teste para ver todas as Suites de Teste e todos os seus Casos de Teste.
            • Quando tiver terminado, clique no botão 'Adicionar / Remover Casos de Teste' para adicionar ou remover os Casos de Teste selecionados. Nota: não é possível adicionar o mesmo Caso de Teste múltiplas vezes.
            • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Atribuir Testadores à Execução de Testes"; -$TLS_htmltext['tc_exec_assignment'] = "

              Objetivo:

              +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Atribuir Testadores à Execução de Testes"; +$TLS_htmltext['tc_exec_assignment'] = "

              Objetivo:

              Esta página permite aos Líderes de Teste atribuir utilizadores a testes específicos dentro do Plano de Teste.

              Iniciar:

              @@ -438,15 +427,15 @@
            • Selecione o Testador conforme planeamento.
            • Pressione o botão 'Gravar' para aplicar a atribuição.
            • Abra a página de execução para verificar a atribuição. Você pode estabelecer um filtro por utilizadores.
            • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Actualizar Casos de Teste no Plano de Teste"; -$TLS_htmltext['planUpdateTC'] = "

              Objetivo:

              -

              Esta página permite atualizar Casos de Teste para uma versão mais nova (diferente) da -Especificação de Casos de Teste quando alterada. Isto frequentemente acontece quando uma funcionalidade é alterada durante os testes." . - " O Utilizador modifica a Especificação de Teste, mas as alterações precisam se propagar ao Plano de Teste também. De qualquer forma," . - " o Plano de Teste mantém as versões originais para garantir que os resultados se referem ao texto correto dos Casos de Teste.

              +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Actualizar Casos de Teste no Plano de Teste"; +$TLS_htmltext['planUpdateTC'] = "

              Objetivo:

              +

              Esta página permite atualizar Casos de Teste para uma versão mais nova (diferente) da +Especificação de Casos de Teste quando alterada. Isto frequentemente acontece quando uma funcionalidade é alterada durante os testes." . + " O Utilizador modifica a Especificação de Teste, mas as alterações precisam se propagar ao Plano de Teste também. De qualquer forma," . + " o Plano de Teste mantém as versões originais para garantir que os resultados se referem ao texto correto dos Casos de Teste.

              Iniciar:

                @@ -454,31 +443,29 @@
              1. Escolha uma nova versão no combo box para um Caso de Teste específico.
              2. Pressione o botão 'Actualizar Plano de Teste' para aplicar alterações.
              3. Para comprovar: abra a página de execução para verificar o texto do(s) Caso(s) de Teste.
              4. -
              "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Especificar testes com prioridade alta ou baixa"; -$TLS_htmltext['test_urgency'] = "

              Objetivo:

              -

              O TestLink permite definir a urgência das Suites de Teste para afetar a prioridade dos Casos de Teste. - A priorização dos testes depende da importância do Caso de Teste e da urgência definida no Plano de Teste. - O Líder de Teste deve especificar um conjunto de Casos de Teste que devem ser testados primeiro. Isso - ajuda a assegurar que os testes cobrirão os requisitos mais importantes também sob a +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Especificar testes com prioridade alta ou baixa"; +$TLS_htmltext['test_urgency'] = "

              Objetivo:

              +

              O TestLink permite definir a urgência das Suites de Teste para afetar a prioridade dos Casos de Teste. + A priorização dos testes depende da importância do Caso de Teste e da urgência definida no Plano de Teste. + O Líder de Teste deve especificar um conjunto de Casos de Teste que devem ser testados primeiro. Isso + ajuda a assegurar que os testes cobrirão os requisitos mais importantes também sob a pressão do tempo.

              Iniciar:

                -
              1. Escolha uma Suite de Teste para definir a urgência do produto/componente no navegador +
              2. Escolha uma Suite de Teste para definir a urgência do produto/componente no navegador ao lado esquerdo da janela.
              3. -
              4. Escolha o nível de urgência (Alta, Média ou Baixa). O nível médio é o padrão. Você pode - diminuir a prioridade para partes do produtos não alteradas e aumentar para componentes +
              5. Escolha o nível de urgência (Alta, Média ou Baixa). O nível médio é o padrão. Você pode + diminuir a prioridade para partes do produtos não alteradas e aumentar para componentes com mudanças significativas.
              6. Pressione o botão 'Gravar' para aplicar as alterações.
              -

              Por exemplo, um Caso de Teste com prioridade Alta em uma Suite de Teste com urgência Baixa - será de prioridade Média."; - - -// ------------------------------------------------------------------------------------------ - +

              Por exemplo, um Caso de Teste com prioridade Alta em uma Suite de Teste com urgência Baixa + será de prioridade Média."; + +// ------------------------------------------------------------------------------------------ + ?> diff --git a/locale/ru_RU/description.php b/locale/ru_RU/description.php index db462dd05f..3dc1e3dee2 100644 --- a/locale/ru_RU/description.php +++ b/locale/ru_RU/description.php @@ -1,31 +1,31 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - * ----------------------------------------------------------------------------------- */ - -// printFilter.html +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + * ----------------------------------------------------------------------------------- */ + +// printFilter.html $TLS_hlp_generateDocOptions = "

              Options for a generated document

              This table allows the user to filter test cases before they are viewed. If @@ -33,8 +33,8 @@ presented, check or uncheck, click on Filter, and select the desired data level from the tree.

              -

              Document Header: Users can filter out Document Header information. -Document Header information includes: Introduction, Scope, References, +

              Document Header: Users can filter out Document Header information. +Document Header information includes: Introduction, Scope, References, Test Methodology, and Test Limitations.

              Test Case Body: Users can filter out Test Case Body information. Test Case Body information @@ -49,35 +49,35 @@

              Table of Content: TestLink inserts list of all titles with internal hypertext links if checked.

              -

              Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component -in second case.

              "; - -// testPlan.html +

              Output format: There are two possibilities: HTML and MS Word. Browser calls MS word component +in second case.

              "; + +// testPlan.html $TLS_hlp_testPlan = "

              Test Plan

              General

              -

              A test plan is a systematic approach to testing a system such as software. You can organize testing activity with +

              A test plan is a systematic approach to testing a system such as software. You can organize testing activity with particular builds of product in time and trace results.

              Test Execution

              -

              This section is where users can execute test cases (write test results) and -print Test case suite of the Test Plan. This section is where users can track -the results of their test case execution.

              +

              This section is where users can execute test cases (write test results) and +print Test case suite of the Test Plan. This section is where users can track +the results of their test case execution.

              Test Plan Management

              -

              This section, which is only lead accessible, allows users to administrate test plans. -Administering test plans involves creating/editing/deleting plans, -adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can +

              This section, which is only lead accessible, allows users to administrate test plans. +Administering test plans involves creating/editing/deleting plans, +adding/editing/deleting/updating test cases in plans, creating builds as well as defining who can see which plan.
              -Users with lead permissions may also set the priority/risk and ownership of -Test case suites (categories) and create testing milestones.

              - -

              Note: It is possible that users may not see a dropdown containing any Test plans. -In this situation all links (except lead enabled ones) will be unlinked. If you -are in this situation you must contact a lead or admin to grant you the proper -project rights or create a Test Plan for you.

              "; - -// custom_fields.html +Users with lead permissions may also set the priority/risk and ownership of +Test case suites (categories) and create testing milestones.

              + +

              Note: It is possible that users may not see a dropdown containing any Test plans. +In this situation all links (except lead enabled ones) will be unlinked. If you +are in this situation you must contact a lead or admin to grant you the proper +project rights or create a Test Plan for you.

              "; + +// custom_fields.html $TLS_hlp_customFields = "

              Custom Fields

              Following are some facts about the implementation of custom fields:

                @@ -96,7 +96,7 @@
              • Caption variable name (eg: This is the value that is supplied to lang_get() API , or displayed as-is if not found in language file).
              • Custom field type (string, numeric, float, enum, email)
              • -
              • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list +
              • Enumeration possible values (eg: RED|YELLOW|BLUE), applicable to list, multiselection list and combo types.
                Use the pipe ('|') character to separate possible values for an enumeration. One of the possible values @@ -116,16 +116,16 @@
              • Enable on test plan design. User can change the value during Test Plan design (add test cases to test plan)
              • Available for. User choose to what kind of item the field belows.
              -"; - -// execMain.html +"; + +// execMain.html $TLS_hlp_executeMain = "

              Executing Test Cases

              Allows users to 'execute' test cases. Execution itself is merely assigning a test case a result (pass,fail,blocked) against a selected build.

              Access to a bug tracking system could be configured. User can directly add a new bugs -and browse exesting ones then.

              "; - -//bug_add.html +and browse exesting ones then.

              "; + +// bug_add.html $TLS_hlp_btsIntegration = "

              Add Bugs to Test Case

              (only if it is configured) TestLink has a very simple integration with Bug Tracking Systems (BTS), @@ -135,7 +135,7 @@

            • Insert new bug.
            • Display existent bug info.
            -

            +

            Process to add a bug

            @@ -144,111 +144,108 @@

          • Step 2: write down the BUGID assigned by BTS.
          • Step 3: write BUGID on the input field.
          • Step 4: use add bug button.
          • -
          +
        After closing the add bug page, you will see relevant bug data on the execute page. -

        "; - -// execFilter.html +

        "; + +// execFilter.html $TLS_hlp_executeFilter = "

        Setup Filter and Build for test execution

        -

        The left pane consists from navigator through test cases assigned to the current " . -"Test plan and table with settings and filter. These filters allows the user " . -"to refine offered set of test cases before they are executed." . -"Setup your filter, press the \"Apply\" button and select appropriate Test Case " . -"from tree menu.

        +

        The left pane consists from navigator through test cases assigned to the current " . + "Test plan and table with settings and filter. These filters allows the user " . + "to refine offered set of test cases before they are executed." . + "Setup your filter, press the \"Apply\" button and select appropriate Test Case " . + "from tree menu.

        Build

        -

        Users must choose a build that will be connected with a test result. " . -"Builds are the basic component for the current Test Plan. Each test case " . -"may be run more times per build. However the last results is count only. +

        Users must choose a build that will be connected with a test result. " . + "Builds are the basic component for the current Test Plan. Each test case " . + "may be run more times per build. However the last results is count only.
        Builds can be created by leads using the Create New Build page.

        Test Case ID filter

        -

        Users can filter test cases by unique identifier. This ID is created automatically -during create time. Empty box means that the filter doesn't apply.

        +

        Users can filter test cases by unique identifier. This ID is created automatically +during create time. Empty box means that the filter doesn't apply.

        Priority filter

        -

        Users can filter test cases by test priority. Each test case importance is combined" . -"with test urgency within the current Test plan. For example 'HIGH' priority test case " . -"is shown if importance or urgency is HIGH and second attribute is at least MEDIUM level.

        +

        Users can filter test cases by test priority. Each test case importance is combined" . + "with test urgency within the current Test plan. For example 'HIGH' priority test case " . + "is shown if importance or urgency is HIGH and second attribute is at least MEDIUM level.

        Result filter

        -

        Users can filter test cases by results. Results are what happened to that test -case during a particular build. Test cases can pass, fail, be blocked, or not be run." . -"This filter is disabled by default.

        +

        Users can filter test cases by results. Results are what happened to that test +case during a particular build. Test cases can pass, fail, be blocked, or not be run." . + "This filter is disabled by default.

        User filter

        -

        Users can filter test cases by their assignee. The check-box allows to include also " . -"\"unassigned\" tests into the resulted set in addtion.

        "; -/* -

        Most Current Result

        -

        By default or if the 'most current' checkbox is unchecked, the tree will be sorted -by the build that is chosen from the dropdown box. In this state the tree will display -the test cases status. -
        Example: User selects build 2 from the dropdown box and doesn't check the 'most -current' checkbox. All test cases will be shown with their status from build 2. -So, if test case 1 passed in build 2 it will be colored green. -
        If the user decideds to check the 'most current' checkbox the tree will be -colored by the test cases most recent result. -
        Ex: User selects build 2 from the dropdown box and this time checks -the 'most current' checkbox. All test cases will be shown with most current -status. So, if test case 1 passed in build 3, even though the user has also selected -build 2, it will be colored green.

        - */ - - -// newest_tcversions.html +

        Users can filter test cases by their assignee. The check-box allows to include also " . + "\"unassigned\" tests into the resulted set in addtion.

        "; +/* + *

        Most Current Result

        + *

        By default or if the 'most current' checkbox is unchecked, the tree will be sorted + * by the build that is chosen from the dropdown box. In this state the tree will display + * the test cases status. + *
        Example: User selects build 2 from the dropdown box and doesn't check the 'most + * current' checkbox. All test cases will be shown with their status from build 2. + * So, if test case 1 passed in build 2 it will be colored green. + *
        If the user decideds to check the 'most current' checkbox the tree will be + * colored by the test cases most recent result. + *
        Ex: User selects build 2 from the dropdown box and this time checks + * the 'most current' checkbox. All test cases will be shown with most current + * status. So, if test case 1 passed in build 3, even though the user has also selected + * build 2, it will be colored green.

        + */ + +// newest_tcversions.html $TLS_hlp_planTcModified = "

        Newest versions of linked Test Cases

        The whole set of Test Cases linked to Test Plan is analyzed, and a list of Test Cases which have a newest version is displayed (against the current set of the Test Plan). -

        "; - - -// requirementsCoverage.html +

        "; + +// requirementsCoverage.html $TLS_hlp_requirementsCoverage = "

        Requirements Coverage


        This feature allows to map a coverage of user or system requirements by test cases. Navigate via link \"Requirement Specification\" in main screen.

        Requirements Specification

        -

        Requirements are grouped by 'Requirements Specification' document which is related to -Test Project.
        TestLink doesn't support versions for both Requirements Specification -and Requirements itself. So, version of document should be added after +

        Requirements are grouped by 'Requirements Specification' document which is related to +Test Project.
        TestLink doesn't support versions for both Requirements Specification +and Requirements itself. So, version of document should be added after a Specification Title. -An user can add simple description or notes to Scope field.

        +An user can add simple description or notes to Scope field.

        -

        Overwritten count of REQs serves for -evaluation Req. coverage in case that not all requirements are added (imported) in. -The value 0 means that current count of requirements is used for metrics.

        -

        E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test +

        Overwritten count of REQs serves for +evaluation Req. coverage in case that not all requirements are added (imported) in. +The value 0 means that current count of requirements is used for metrics.

        +

        E.g. SRS includes 200 requirements but only 50 are added in TestLink. Test coverage is 25% (if all these added requirements will be tested).

        Requirements

        Click on title of a created Requirements Specification. You can create, edit, delete or import requirements for the document. Each requirement has title, scope and status. Status should be \"Normal\" or \"Not testable\". Not testable requirements are not counted -to metrics. This parameter should be used for both unimplemented features and -wrong designed requirements.

        +to metrics. This parameter should be used for both unimplemented features and +wrong designed requirements.

        -

        You can create new test cases for requirements by using multi action with checked +

        You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite -with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = +with name defined in configuration (default is: $tlCfg->req_cfg->default_testsuite_name = \"Test suite created by Requirement - Auto\";). Title and Scope are copied to these Test cases.

        -"; - - -// planAddTC_m1.tpl +"; + +// planAddTC_m1.tpl $TLS_hlp_planAddTC = "

        Regarding 'Save Custom Fields'

        -If you have defined and assigned to Test Project,
        +If you have defined and assigned to Test Project,
        Custom Fields with:
        'Display on test plan design=true' and
        'Enable on test plan design=true'
        you will see these in this page ONLY for Test Cases linked to Test Plan. -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ -?> \ No newline at end of file +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ +?> diff --git a/locale/ru_RU/texts.php b/locale/ru_RU/texts.php index 1c2900d1d6..c7fdeece80 100644 --- a/locale/ru_RU/texts.php +++ b/locale/ru_RU/texts.php @@ -1,33 +1,31 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * ------------------------------------------------------------------------------------ */ - - -$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; -$TLS_htmltext['assignReqs'] = "

        Purpose:

        +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * ------------------------------------------------------------------------------------ */ +$TLS_htmltext_title['assignReqs'] = "Assign Requirements to Test Case"; +$TLS_htmltext['assignReqs'] = "

        Purpose:

        Users can set relations between requirements and test cases. A designer could define relations 0..n to 0..n. I.e. One test case could be assigned to none, one or more test cases and vice versa. Such traceability matrix helps to investigate test coverage @@ -38,7 +36,7 @@

        1. Choose an Test Case in tree at the left. The combo box with list of Requirements Specifications is shown at the top of workarea.
        2. -
        3. Choose a Requirements Specification Document if more once defined. +
        4. Choose a Requirements Specification Document if more once defined. TestLink automatically reload the page.
        5. A middle block of workarea lists all requirements (from choosen Specification), which are connected with the test case. Bottom block 'Available Requirements' lists all @@ -46,50 +44,48 @@ to the current test case. A designer could mark requirements which are covered by this test case and then click the button 'Assign'. These new assigned test case are shown in the middle block 'Assigned Requirements'.
        6. -
        "; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "Test Specification"; -$TLS_htmltext['editTc'] = "

        Purpose:

        +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "Test Specification"; +$TLS_htmltext['editTc'] = "

        Purpose:

        Purpose:

        -

        The Test Specification allows users to view and edit all of the existing " . - "Test Suites and Test Cases. Test Cases are versioned and all " . - "of the previous versions are available and can be viewed and managed here.

        +

        The Test Specification allows users to view and edit all of the existing " . + "Test Suites and Test Cases. Test Cases are versioned and all " . + "of the previous versions are available and can be viewed and managed here.

        Getting Started:

          -
        1. Select your Test Project in the navigation tree (the root node). Please note: " . - "You can always change the activate Test Project by selecting a different one from the " . - "drop-down list in the top-right corner.
        2. -
        3. Create a new Test Suite by clicking on New Child Test Suite. Test Suites can " . - "bring structure to your test documents according to your conventions (functional/non-functional " . - "tests, product components or features, change requests, etc.). The description of " . - "a Test Suite could hold the scope of the included test cases, default configuration, " . - "links to relevant documents, limitations and other useful information. In general, " . - "all annotations that are common to the Child Test Cases. Test Suites follow " . - "the "folder" metaphor, thus users can move and copy Test Suites within " . - "the Test project. Also, they can be imported or exported (including the contained Test cases).
        4. -
        5. Test suites are scalable folders. User can move or copy Test Suites within " . - "the Test project. Test suites could be imported or exported (include Test cases). -
        6. Select your newly created Test Suite in the navigation tree and create " . - "a new Test Case by clicking on Create Test Case. A Test Case specifies " . - "a particular testing scenario, expected results and custom fields defined " . - "in the Test Project (refer to the user manual for more information). It is also possible " . - "to assign keywords for improved traceability.
        7. +
        8. Select your Test Project in the navigation tree (the root node). Please note: " . + "You can always change the activate Test Project by selecting a different one from the " . + "drop-down list in the top-right corner.
        9. +
        10. Create a new Test Suite by clicking on New Child Test Suite. Test Suites can " . + "bring structure to your test documents according to your conventions (functional/non-functional " . + "tests, product components or features, change requests, etc.). The description of " . + "a Test Suite could hold the scope of the included test cases, default configuration, " . + "links to relevant documents, limitations and other useful information. In general, " . + "all annotations that are common to the Child Test Cases. Test Suites follow " . + "the "folder" metaphor, thus users can move and copy Test Suites within " . + "the Test project. Also, they can be imported or exported (including the contained Test cases).
        11. +
        12. Test suites are scalable folders. User can move or copy Test Suites within " . + "the Test project. Test suites could be imported or exported (include Test cases). +
        13. Select your newly created Test Suite in the navigation tree and create " . + "a new Test Case by clicking on Create Test Case. A Test Case specifies " . + "a particular testing scenario, expected results and custom fields defined " . + "in the Test Project (refer to the user manual for more information). It is also possible " . + "to assign keywords for improved traceability.
        14. Navigate via the tree view on the left side and edit data. Test cases stores own history.
        15. Assign your created Test Specification to Test Plan when your Test cases are ready.
        16. + \"javascript:open_help_window('glossary','$locale');\">Test Plan when your Test cases are ready.
        -

        With TestLink you organize test cases into test suites." . -"Test suites can be nested within other test suites, enabling you to create hierarchies of test suites. - You can then print this information together with the test cases.

        "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; -$TLS_htmltext['searchTc'] = "

        Purpose:

        +

        With TestLink you organize test cases into test suites." . + "Test suites can be nested within other test suites, enabling you to create hierarchies of test suites. + You can then print this information together with the test cases.

        "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "Test Case Search Page"; +$TLS_htmltext['searchTc'] = "

        Purpose:

        Navigation according to keywords and/or searched strings. The search is not case sensitive. Result include just test cases from actual Test Project.

        @@ -101,12 +97,11 @@
      • Choose required keyword or left value 'Not applied'.
      • Click the Search button.
      • All fulfilled test cases are shown. You can modify test cases via 'Title' link.
      • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; //printTC.html -$TLS_htmltext['printTestSpec'] = "

        Purpose:

        +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "Print Test Specification"; // printTC.html +$TLS_htmltext['printTestSpec'] = "

        Purpose:

        From here you can print a single test case, all the test cases within a test suite, or all the test cases in a test project or plan.

        Get Started:

        @@ -121,12 +116,11 @@
      • Use your browser's print functionality to actually print the information.
        Note: Make sure to only print the right-hand frame.

      • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; //printTC.html -$TLS_htmltext['reqSpecMgmt'] = "

        You can manage Requirement Specification documents.

        +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "Requirements Specification Design"; // printTC.html +$TLS_htmltext['reqSpecMgmt'] = "

        You can manage Requirement Specification documents.

        Requirements Specification

        @@ -154,12 +148,11 @@

        You can create new test cases for requirements by using multi action with checked requirements within the specification screen. These Test Cases are created into Test Suite with name defined in configuration (default is: \$tlCfg->req_cfg->default_testsuite_name = -'Test suite created by Requirement - Auto';). Title and Scope are copied to these Test cases.

        "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; -$TLS_htmltext['keywordsAssign'] = "

        Purpose:

        +'Test suite created by Requirement - Auto';)
        . Title and Scope are copied to these Test cases.

        "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "Keyword Assignment"; +$TLS_htmltext['keywordsAssign'] = "

        Purpose:

        The Keyword Assignment page is the place where users can batch assign keywords to the existing Test Suite or Test Case

        @@ -183,16 +176,15 @@

        TestLink uses this approach so that older versions of test cases in test plans are not effected by keyword assignments you make to the most recent version of the test case. If you want your test cases in your test plan to be updated, first verify they are up to date using the 'Update -Modified Test Cases' functionality BEFORE making keyword assignments.

        "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "Test Case Execution"; -$TLS_htmltext['executeTest'] = "

        Purpose:

        +Modified Test Cases' functionality BEFORE making keyword assignments.

        "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "Test Case Execution"; +$TLS_htmltext['executeTest'] = "

        Purpose:

        Allows user to execute Test cases. User can assign Test result -to Test Case for Build. See help for more information about filter and settings " . - "(click on the question mark icon).

        +to Test Case for Build. See help for more information about filter and settings " . + "(click on the question mark icon).

        Get started:

        @@ -203,13 +195,13 @@
      • Fill out the test case result and any applicable notes or bugs.
      • Save results.
      • -

        Note: TestLink must be configurated to collaborate with your Bug tracker -if you would like to create/trace a problem report directly from the GUI.

        "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; -$TLS_htmltext['showMetrics'] = "

        Reports are related to a Test Plan " . - "(defined in top of navigator). This Test Plan could differs from the +

        Note: TestLink must be configurated to collaborate with your Bug tracker +if you would like to create/trace a problem report directly from the GUI.

        "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "Description of Test Reports and Metrics"; +$TLS_htmltext['showMetrics'] = "

        Reports are related to a Test Plan " . + "(defined in top of navigator). This Test Plan could differs from the current Test Plan for execution. You can also select Report format:

        • Normal - report is displayed in web page
        • @@ -326,12 +318,11 @@

          Total Bugs For Each Test Case

          This report shows each test case with all of the bugs filed against it for the entire project. -This report is only available if a Bug Tracking System is connected.

          "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd -$TLS_htmltext['planAddTC'] = "

          Purpose:

          +This report is only available if a Bug Tracking System is connected.

          "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "Add / Remove Test cases to Test Plan"; // testSetAdd +$TLS_htmltext['planAddTC'] = "

          Purpose:

          Allows user (with lead level permissions) to add or remove test cases into a Test plan.

          To add or remove Test cases:

          @@ -339,11 +330,11 @@
        • Click on a test suite to see all of its test suites and all of its test cases.
        • When you are done click the 'Add / Remove Test Cases' button to add or remove the test cases. Note: Is not possible to add the same test case multiple times.
        • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; -$TLS_htmltext['tc_exec_assignment'] = "

          Purpose

          +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "Assign Testers to test execution"; +$TLS_htmltext['tc_exec_assignment'] = "

          Purpose

          This page allows test leaders to assign users to particular tests within the Test Plan.

          Get Started

          @@ -352,16 +343,15 @@
        • Select a planned tester.
        • Press button to submit assignement.
        • Open execution page to verify assignment. You can set-up a filter for users.
        • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; -$TLS_htmltext['planUpdateTC'] = "

          Purpose

          +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "Update Test Cases in the Test Plan"; +$TLS_htmltext['planUpdateTC'] = "

          Purpose

          This page allows update Test case to a newer (different) version in the case that Test -Specification is changed. It often happens that some functionality is clarified during testing." . - " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . - " plan holds original version to be sure, that results refer to the correct text of a Test case.

          +Specification is changed. It often happens that some functionality is clarified during testing." . + " User modifies Test Specification, but changes needs to propagate to Test Plan too. Otherwise Test" . + " plan holds original version to be sure, that results refer to the correct text of a Test case.

          Get Started

            @@ -369,14 +359,13 @@
          1. Choose a new version from bombo boxmenu for particular Test case.
          2. Press button 'Update Test plan' to submit changes.
          3. To verify: Open execution page to view text of the test case(s).
          4. -
          "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; -$TLS_htmltext['test_urgency'] = "

          Purpose

          -

          TestLink allows set urgency of Test Suite to affect a testing Priority of test cases. - Test priority depends on both Importance of Test cases and Urgency defined in +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "Specify tests with high or low urgency"; +$TLS_htmltext['test_urgency'] = "

          Purpose

          +

          TestLink allows set urgency of Test Suite to affect a testing Priority of test cases. + Test priority depends on both Importance of Test cases and Urgency defined in the Test Plan. Test leader should specify a set of test cases that could be tested at first. It helps to assure that testing will cover the most important tests also under time pressure.

          @@ -390,10 +379,9 @@ significant changes.
        • Press the button 'Save' to submit changes.
        • -

          For example, a Test case with a High importance in a Test suite with Low urgency " . - "will be Medium priority."; - - -// ------------------------------------------------------------------------------------------ - -?> \ No newline at end of file +

          For example, a Test case with a High importance in a Test suite with Low urgency " . + "will be Medium priority."; + +// ------------------------------------------------------------------------------------------ + +?> diff --git a/locale/tl_lang_parser.php b/locale/tl_lang_parser.php index 395fd60867..5814ce2f86 100644 --- a/locale/tl_lang_parser.php +++ b/locale/tl_lang_parser.php @@ -1,245 +1,208 @@ #!/usr/bin/php - report.txt (RECOMMENDED) - * - * @package TestLink - * @author Martin Havlat, Julian Krien - * @copyright 2003, TestLink community - * @version CVS: $Id: tl_lang_parser.php,v 1.2.6.3 2010/12/12 10:34:14 mx-julian Exp $ - * @link http://www.teamst.org/index.php - * - * @internal Revisions: - * 20100517 - Julian - major changes: improved robustness, script can now also be - * used to update texts.php and description.php - * - **/ - -/** Set path to your en_GB english file */ -$file_eng = 'en_GB/strings.txt'; -//$file_eng = 'en_GB/texts.php'; -//$file_eng = 'en_GB/description.php'; - -/** Set true if you would like to have original file with 'bck' extension */ -$do_backup_file = FALSE; - - - - -// --------------------------------------------------------------------------- -if ($argc < 1) -{ - echo 'Usage: #tl_lang_parser.php '; - exit; -} -else - $file_lang_old = $argv[1]; - -$out = ''; // data for output file -$var_counter = 0; -$var_counter_new = 0; -$new_vars = array(); - -echo "===== Start TestLink lang_parser =====\n"; - -// read english file -if (file_exists($file_eng) && is_readable ($file_eng)) -{ - echo "Master file ($file_eng) is readable OK.\n"; - $lines_eng = file( $file_eng ); -} -else -{ - echo "Master File ($file_eng) is not readable. Exit.\n"; - exit; -} -// read language file -if (file_exists($file_lang_old) && is_readable ($file_lang_old)) -{ - echo "File to be updated ({$file_lang_old}) is readable OK.\n"; - $lines_lang_old = file( $file_lang_old ); -} -else -{ - echo "File to be updated ({$file_lang_old}) is not readable. Exit.\n"; - exit; -} - -$lines_eng_count = sizeof($lines_eng); -$lines_old_count = sizeof($lines_lang_old); -echo "Master file lines = ".($lines_eng_count+1)."\n"; -echo "File to update lines = ".($lines_old_count+1)."\n"; - -// find end of english header: -for( $i = 0; $i < $lines_eng_count; $i++ ) -{ - // parse revision of master file - if (preg_match('/\$Id.+v\s(\S+)\s.*/', $lines_eng[$i], $eng_revision) ) - { - $revision_comment = $eng_revision[1]; - echo "Master file revision: ".$revision_comment."\n"; - } - // search for "*/" at the end of a line - if (preg_match("/\*\//", $lines_eng[$i]) ) - { - echo "Master file: End of header is line = ".($i+1)."\n"; - $begin_line = $i + 1; - $i = $lines_eng_count; - } -} - -// copy existing localization file header -for( $i = 0; $i < $lines_old_count; $i++ ) -{ - if (preg_match("/\*\//", $lines_lang_old[$i]) ) - { - echo "File to be updated: End of header is line = ".($i+1)."\n"; - $begin_line_old = $i + 1; - $i = $lines_old_count; - $out .= " * Scripted update according en_GB string file (version: ".$revision_comment.") \n"; - $out .= " *\n **/\n"; - } - else - $out .= $lines_lang_old[$i]; -} - - -// compile output array based on english file -for( $i = $begin_line; $i < $lines_eng_count; $i++ ) -{ -// echo "$i >> {$lines_eng[$i]}\n"; - - // copy comments: - if (preg_match("/^\/\//", $lines_eng[$i]) ) - { - echo "\n\n=line ".($i+1)."=\nCopy comment to file to be updated:\n". - "-----------------------------------------\n". - trim($lines_eng[$i]). "\n". - "-----------------------------------------\n"; - $out .= $lines_eng[$i]; - } - - // copy empty line - elseif (preg_match('/^([\s\t]*)$/', $lines_eng[$i])) - { - echo "\n\n=line ".($i+1)."=\nCopy empty line to file to be updated\n"; - $out .= "\r\n"; - } - - // parse a line with variable definition - elseif (preg_match('/^\$TLS_(\w+\[?\'?\w*\'?\]?)[\s]*=[\s]*(.*)$/', $lines_eng[$i], $parsed_line)) - { - $var_counter++; - $var_name = '$TLS_'.$parsed_line[1]; - $bLocalized = FALSE; - $localizedLine = ''; -// print_r($parsed_line); - echo "\n\n=line ".($i+1)."=\nFound variable '$var_name' on master file\n"; - - // get localized value if defined - parse old localized strings - for( $k = $begin_line_old; $k < $lines_old_count; $k++ ) - { - if (preg_match('/^\\'.addcslashes($var_name,'\'\[\]').'[\s]*=[\s]*.+$/', $lines_lang_old[$k])) - { - echo "Found localization for variable '$var_name' on file to be updated (line ".($k+1).")\n"; - $bLocalized = TRUE; - $localizedLine = $lines_lang_old[$k]; - - // check if localized value exceed to more lines - semicolon is not found - while (!(preg_match('/;[\s]*$/', $lines_lang_old[$k]) - || preg_match('/;[\s]*[\/]{2}/', $lines_lang_old[$k]))) - { - $k++; - //echo "\t(line $k)Found localization for variable $var_name extends to this line\n"; - $localizedLine .= $lines_lang_old[$k]; - } - $k = $lines_old_count; // exit more parsing old file - } - } - - if ($bLocalized) - { - echo "Keep existing localization on file to be updated:\n". - "-----------------------------------------\n". - trim($localizedLine). "\n". - "-----------------------------------------\n"; - $out .= $localizedLine; - } - else - { - echo "Localization doesn't exists. Copy from master file to file to be updated:\n". - "-----------------------------------------\n". - trim($lines_eng[$i]). "\n"; - //add a todo on newly added lines - //$out .= trim($lines_eng[$i]). " //TODO: localize\r\n"; - $out .= $lines_eng[$i]; - $var_counter_new++; - $new_vars[$i] = $var_name; - - // check multiline value (check semicolon or semicolon with comment) - while (!(preg_match('/^(.*);[\s]*$/', $lines_eng[$i]) - || preg_match('/^(.*);[\s]*[\/]{2}/', $lines_eng[$i]))) - { - $i++; - echo trim($lines_eng[$i]). "\n"; - //add a todo on newly added lines - //$out .= trim($lines_eng[$i]). " //TODO: localize\r\n"; - $out .= $lines_eng[$i]; - } - echo "-----------------------------------------\n"; - - } - } - - // end of file - elseif (preg_match('/^\?\>/', $lines_eng[$i])) - { - $out .= "?>"; - } - - // skip unused multiline values - // must be a multiline value if it is no variable/comment/empty line/end of file - elseif (preg_match('/^.*/', $lines_eng[$i])) - echo "\n\n=line ".($i+1)."=\nSkipped line (expected unused multiline value on master file)\n"; - - // something wrong? - else - { - echo "\n\n=line ".($i+1)."=\nERROR: please fix this line\n" . $lines_eng[$i]; - exit; - } -} - - -// create backup if defined -if ($do_backup_file) - rename($file_lang_old, $file_lang_old.'.bck'); - - -// save output -$fp = fopen($file_lang_old, "w"); -fwrite($fp, $out); -fclose($fp); - -echo "\n\nUpdated file: ".$file_lang_old; -echo "\nCompleted! The script has parsed $var_counter strings and add $var_counter_new new variables.\n"; -echo implode("\n", $new_vars); -echo "\n\n===== Bye =====\n"; - + report.txt (RECOMMENDED) + * + * @package TestLink + * @author Martin Havlat, Julian Krien + * @copyright 2003, TestLink community + * @version CVS: $Id: tl_lang_parser.php,v 1.2.6.3 2010/12/12 10:34:14 mx-julian Exp $ + * @link http://www.teamst.org/index.php + * + * @internal Revisions: + * 20100517 - Julian - major changes: improved robustness, script can now also be + * used to update texts.php and description.php + * + */ + +/** + * Set path to your en_GB english file + */ +$file_eng = 'en_GB/strings.txt'; + +/** + * Set true if you would like to have original file with 'bck' extension + */ +$do_backup_file = false; + +// --------------------------------------------------------------------------- +if ($argc < 1) { + echo 'Usage: #tl_lang_parser.php '; + exit(); +} else { + $file_lang_old = $argv[1]; +} + +$out = ''; // data for output file +$var_counter = 0; +$var_counter_new = 0; +$new_vars = array(); + +echo "===== Start TestLink lang_parser =====\n"; + +// read english file +if (file_exists($file_eng) && is_readable($file_eng)) { + echo "Master file ($file_eng) is readable OK.\n"; + $lines_eng = file($file_eng); +} else { + echo "Master File ($file_eng) is not readable. Exit.\n"; + exit(); +} +// read language file +if (file_exists($file_lang_old) && is_readable($file_lang_old)) { + echo "File to be updated ({$file_lang_old}) is readable OK.\n"; + $lines_lang_old = file($file_lang_old); +} else { + echo "File to be updated ({$file_lang_old}) is not readable. Exit.\n"; + exit(); +} + +$lines_eng_count = count($lines_eng); +$lines_old_count = count($lines_lang_old); +echo "Master file lines = " . ($lines_eng_count + 1) . "\n"; +echo "File to update lines = " . ($lines_old_count + 1) . "\n"; + +// find end of english header: +for ($i = 0; $i < $lines_eng_count; $i ++) { + // parse revision of master file + if (preg_match('/\$Id.+v\s(\S+)\s.*/', $lines_eng[$i], $eng_revision)) { + $revision_comment = $eng_revision[1]; + echo "Master file revision: " . $revision_comment . "\n"; + } + // search for "*/" at the end of a line + if (preg_match("/\*\//", $lines_eng[$i])) { + echo "Master file: End of header is line = " . ($i + 1) . "\n"; + $begin_line = $i + 1; + $i = $lines_eng_count; + } +} + +// copy existing localization file header +for ($i = 0; $i < $lines_old_count; $i ++) { + if (preg_match("/\*\//", $lines_lang_old[$i])) { + echo "File to be updated: End of header is line = " . ($i + 1) . "\n"; + $begin_line_old = $i + 1; + $i = $lines_old_count; + $out .= " * Scripted update according en_GB string file (version: " . + $revision_comment . ") \n"; + $out .= " *\n **/\n"; + } else { + $out .= $lines_lang_old[$i]; + } +} + +// compile output array based on english file +for ($i = $begin_line; $i < $lines_eng_count; $i ++) { + + // copy comments: + if (preg_match("/^\/\//", $lines_eng[$i])) { + echo "\n\n=line " . ($i + 1) . "=\nCopy comment to file to be updated:\n" . + "-----------------------------------------\n" . trim($lines_eng[$i]) . + "\n" . "-----------------------------------------\n"; + $out .= $lines_eng[$i]; + } // copy empty line + elseif (preg_match('/^([\s\t]*)$/', $lines_eng[$i])) { + echo "\n\n=line " . ($i + 1) . + "=\nCopy empty line to file to be updated\n"; + $out .= "\r\n"; + } // parse a line with variable definition + elseif (preg_match('/^\$TLS_(\w+\[?\'?\w*\'?\]?)[\s]*=[\s]*(.*)$/', + $lines_eng[$i], $parsed_line)) { + $var_counter ++; + $var_name = '$TLS_' . $parsed_line[1]; + $bLocalized = false; + $localizedLine = ''; + echo "\n\n=line " . ($i + 1) . + "=\nFound variable '$var_name' on master file\n"; + + // get localized value if defined - parse old localized strings + for ($k = $begin_line_old; $k < $lines_old_count; $k ++) { + if (preg_match( + '/^\\' . addcslashes($var_name, '\'\[\]') . '[\s]*=[\s]*.+$/', + $lines_lang_old[$k])) { + echo "Found localization for variable '$var_name' on file to be updated (line " . + ($k + 1) . ")\n"; + $bLocalized = true; + $localizedLine = $lines_lang_old[$k]; + + // check if localized value exceed to more lines - semicolon is not found + while (! (preg_match('/;[\s]*$/', $lines_lang_old[$k]) || + preg_match('/;[\s]*[\/]{2}/', $lines_lang_old[$k]))) { + $k ++; + $localizedLine .= $lines_lang_old[$k]; + } + $k = $lines_old_count; // exit more parsing old file + } + } + + if ($bLocalized) { + echo "Keep existing localization on file to be updated:\n" . + "-----------------------------------------\n" . + trim($localizedLine) . "\n" . + "-----------------------------------------\n"; + $out .= $localizedLine; + } else { + echo "Localization doesn't exists. Copy from master file to file to be updated:\n" . + "-----------------------------------------\n" . + trim($lines_eng[$i]) . "\n"; + $out .= $lines_eng[$i]; + $var_counter_new ++; + $new_vars[$i] = $var_name; + + // check multiline value (check semicolon or semicolon with comment) + while (! (preg_match('/^(.*);[\s]*$/', $lines_eng[$i]) || + preg_match('/^(.*);[\s]*[\/]{2}/', $lines_eng[$i]))) { + $i ++; + echo trim($lines_eng[$i]) . "\n"; + $out .= $lines_eng[$i]; + } + echo "-----------------------------------------\n"; + } + } // end of file + elseif (preg_match('/^\?\>/', $lines_eng[$i])) { + $out .= "?>"; + } // skip unused multiline values + // must be a multiline value if it is no variable/comment/empty line/end of file + elseif (preg_match('/^.*/', $lines_eng[$i])) { + echo "\n\n=line " . ($i + 1) . + "=\nSkipped line (expected unused multiline value on master file)\n"; + } // something wrong? + else { + echo "\n\n=line " . ($i + 1) . "=\nERROR: please fix this line\n" . + $lines_eng[$i]; + exit(); + } +} + +// create backup if defined +if ($do_backup_file) { + rename($file_lang_old, $file_lang_old . '.bck'); +} + +// save output +$fp = fopen($file_lang_old, "w"); +fwrite($fp, $out); +fclose($fp); + +echo "\n\nUpdated file: " . $file_lang_old; +echo "\nCompleted! The script has parsed $var_counter strings and add $var_counter_new new variables.\n"; +echo implode("\n", $new_vars); +echo "\n\n===== Bye =====\n"; + ?> diff --git a/locale/zh_CN/description.php b/locale/zh_CN/description.php index d3353c4d76..ab519d88a3 100644 --- a/locale/zh_CN/description.php +++ b/locale/zh_CN/description.php @@ -1,33 +1,33 @@ -/ directory. - * This directory is obsolete now. It serves as source for localization contributors only. - * - * ----------------------------------------------------------------------------------- */ - -// printFilter.html //已校对 +/ directory. + * This directory is obsolete now. It serves as source for localization contributors only. + * + * ----------------------------------------------------------------------------------- */ + +// printFilter.html //已校对 $TLS_hlp_generateDocOptions = "

          æ ¹æ®é€‰é¡¹ç”Ÿæˆçš„æ–‡æ¡£

          该列表å…许用户在æµè§ˆæµ‹è¯•用例之å‰å¯¹å…¶è¿›è¡Œè¿‡æ»¤ã€‚ @@ -47,9 +47,9 @@

          表中的内容:TestLink通过内部超连接æ’入所有标题

          -

          导出格å¼ï¼šå…±æœ‰ä¸¤ç§æ ¼å¼ï¼šHTMLå’ŒMS wordã€‚åœ¨ç¬¬äºŒç§æƒ…况下,æµè§ˆå™¨ä¼šè‡ªåŠ¨è°ƒç”¨MS word组件。

          "; - -// testPlan.html //已校对 +

          导出格å¼ï¼šå…±æœ‰ä¸¤ç§æ ¼å¼ï¼šHTMLå’ŒMS wordã€‚åœ¨ç¬¬äºŒç§æƒ…况下,æµè§ˆå™¨ä¼šè‡ªåŠ¨è°ƒç”¨MS word组件。

          "; + +// testPlan.html //已校对 $TLS_hlp_testPlan = "

          测试计划

          一般定义

          @@ -65,9 +65,9 @@ æƒé™é«˜çš„用户å¯ä»¥è®¾ç½®æµ‹è¯•套件(类别)的优先级ï¼é£Žé™©å’Œæ‰€æœ‰å…³ç³»(测试套件由è°è´Ÿè´£),以åŠåˆ›å»ºæµ‹è¯•里程碑。

          注æ„:用户有å¯èƒ½çœ‹ä¸åˆ°ä¸€ä¸ªåŒ…å«ä»»ä½•测试计划的下拉èœå•。 -åœ¨è¿™ç§æƒ…况下,所有的链接(除了管ç†äººå‘˜å¼€å¯çš„)都ä¸èƒ½ä½¿ç”¨ã€‚å¦‚æžœå‡ºçŽ°è¿™ç§æƒ…况,你一定è¦è·Ÿé¢†å¯¼æˆ–管ç†å‘˜è”系,给你在项目中授予适当的æƒé™æˆ–者为你创建一个测试计划。

          "; - -// custom_fields.html //已校对 +åœ¨è¿™ç§æƒ…况下,所有的链接(除了管ç†äººå‘˜å¼€å¯çš„)都ä¸èƒ½ä½¿ç”¨ã€‚å¦‚æžœå‡ºçŽ°è¿™ç§æƒ…况,你一定è¦è·Ÿé¢†å¯¼æˆ–管ç†å‘˜è”系,给你在项目中授予适当的æƒé™æˆ–者为你创建一个测试计划。

          "; + +// custom_fields.html //已校对 $TLS_hlp_customFields = "

          自定义字段

          䏋颿˜¯å…³äºŽè‡ªå®šä¹‰å­—段应用的一些实例:

            @@ -101,14 +101,14 @@
          • å¯ç”¨æµ‹è¯•计划设计。当设计测试计划(呿µ‹è¯•计划中添加测试用例时),用户å¯ä»¥å¯¹å…¶è¿›è¡Œä¿®æ”¹
          • å¯ç”¨äºŽã€‚用户选择什么类型的字段选项。
          -"; - -// execMain.html //已校对 +"; + +// execMain.html //已校对 $TLS_hlp_executeMain = "

          执行测试用例

          å…è®¸ç”¨æˆ·æ‰§è¡Œæµ‹è¯•ç”¨ä¾‹ã€‚æ‰§è¡Œæœ¬èº«åªæ˜¯ä¸€ä¸ªå¯¹é€‰æ‹©çš„æž„建的测试用例分é…结果(通过,失败,é”定)的过程

          -

          通过é…ç½®å¯ä»¥å…³è”到缺陷跟踪系统。用户å¯ä»¥ç›´æŽ¥æ–°å»ºé—®é¢˜å’Œæµè§ˆå·²ç»å­˜åœ¨çš„问题。更多信æ¯è¯·æŸ¥çœ‹å®‰è£…手册。

          "; - -//bug_add.html //已校对 +

          通过é…ç½®å¯ä»¥å…³è”到缺陷跟踪系统。用户å¯ä»¥ç›´æŽ¥æ–°å»ºé—®é¢˜å’Œæµè§ˆå·²ç»å­˜åœ¨çš„问题。更多信æ¯è¯·æŸ¥çœ‹å®‰è£…手册。

          "; + +// bug_add.html //已校对 $TLS_hlp_btsIntegration = "

          给测试用例添加问题

          (仅在已ç»é…置好的情况下) TestLink 仅仅简å•地跟缺陷跟踪系统(BTS)进行了集æˆï¼Œå³ä¸èƒ½å‘BTSå‘é€åˆ›å»ºbug的请求,也ä¸èƒ½å–回bug idå·ã€‚ @@ -117,7 +117,7 @@

        • 添加新问题
        • 显示已存在问题的信æ¯
        -

        +

        添加问题的过程

        @@ -126,59 +126,55 @@

      • 第二步: 记下BTS指定的BUGID
      • 第三步: å°†BUGID写入输入框中
      • 第四步: 点击添加问题按钮
      • -
      +
    关闭添加问题页é¢åŽï¼Œä½ å°†åœ¨æ‰§è¡Œé¡µé¢ä¸Šçœ‹è§ä¸€ä¸ªé—®é¢˜æ•°æ®çš„å…³è”ä¿¡æ¯ã€‚ -

    "; - -// execFilter.html //已校对 +

    "; + +// execFilter.html //已校对 $TLS_hlp_executeFilter = "

    设置过滤器并构建测试的实施

    -

    å·¦æ–¹æ¡†ä¸­åŒ…å«æŒ‡æ´¾ç»™å½“å‰é¡¹ç›®æµ‹è¯•计划的测试用例的导航,左方框的列表包å«äº†æµ‹è¯•用例筛选的过滤器。" . -"这些过滤器å…许用户æç‚¼å‡ºçš„一组测试用例,然åŽåŽ»æ‰§è¡Œã€‚" . -"设置好过滤器,然åŽç‚¹å‡» \"应用\" 按钮并从树形èœå•中选择适当的测试用例 " . -"

    +

    å·¦æ–¹æ¡†ä¸­åŒ…å«æŒ‡æ´¾ç»™å½“å‰é¡¹ç›®æµ‹è¯•计划的测试用例的导航,左方框的列表包å«äº†æµ‹è¯•用例筛选的过滤器。" . "这些过滤器å…许用户æç‚¼å‡ºçš„一组测试用例,然åŽåŽ»æ‰§è¡Œã€‚" . + "设置好过滤器,然åŽç‚¹å‡» \"应用\" 按钮并从树形èœå•中选择适当的测试用例 " . + "

    构建

    -

    用户必须选择一个用æ¥å’Œæµ‹è¯•结果建立连接的构建。" . -"æž„å»ºæ˜¯å½“å‰æµ‹è¯•计划的基本组件。在æ¯ä¸ªæž„建中æ¯ä¸€ä¸ªæµ‹è¯•用例都å¯èƒ½è¢«æ‰§è¡Œå¤šæ¬¡ã€‚" . -"然而统计时åªè®¡å…¥æœ€ç»ˆçš„æ‰§è¡Œç»“果。 +

    用户必须选择一个用æ¥å’Œæµ‹è¯•结果建立连接的构建。" . "æž„å»ºæ˜¯å½“å‰æµ‹è¯•计划的基本组件。在æ¯ä¸ªæž„建中æ¯ä¸€ä¸ªæµ‹è¯•用例都å¯èƒ½è¢«æ‰§è¡Œå¤šæ¬¡ã€‚" . + "然而统计时åªè®¡å…¥æœ€ç»ˆçš„æ‰§è¡Œç»“果。
    项目负责人å¯ä»¥åœ¨æ–°å»ºæž„建页é¢åˆ›å»ºæž„建。

    测试用例ID过滤器

    -

    用户å¯ä»¥è¿‡æ»¤æµ‹è¯•用例通过唯一的标识符。该ID在创建测试用例的时候自动生æˆã€‚ -空列表æ„味ç€è¿˜æ²¡æœ‰åº”用过滤器。

    +

    用户å¯ä»¥è¿‡æ»¤æµ‹è¯•用例通过唯一的标识符。该ID在创建测试用例的时候自动生æˆã€‚ +空列表æ„味ç€è¿˜æ²¡æœ‰åº”用过滤器。

    优先级过滤器

    -

    用户å¯ä»¥é€šè¿‡ä¼˜å…ˆçº§æ¥è¿‡æ»¤æµ‹è¯•用例。æ¯ä¸ªæµ‹è¯•用例的é‡è¦æ€§è¿˜ä¸Žè¯¥æµ‹è¯•ç”¨ä¾‹åœ¨å½“å‰æµ‹è¯•计划里的紧急程度有关。" . -"例如'HIGH'优先级的测试用例会显示那些如果é‡è¦ç¨‹åº¦æ˜¯HIGH,在测试计划中的紧急程度至少是MEDIUM级别的测试用例。

    +

    用户å¯ä»¥é€šè¿‡ä¼˜å…ˆçº§æ¥è¿‡æ»¤æµ‹è¯•用例。æ¯ä¸ªæµ‹è¯•用例的é‡è¦æ€§è¿˜ä¸Žè¯¥æµ‹è¯•ç”¨ä¾‹åœ¨å½“å‰æµ‹è¯•计划里的紧急程度有关。" . + "例如'HIGH'优先级的测试用例会显示那些如果é‡è¦ç¨‹åº¦æ˜¯HIGH,在测试计划中的紧急程度至少是MEDIUM级别的测试用例。

    结果过滤器

    -

    用户å¯ä»¥é€šè¿‡æµ‹è¯•结果过滤测试用例。测试结果是测试用例基于æŸä¸€æž„建的产物。测试用例的状æ€åŒ…括通过,失败,é”定或者尚未è¿è¡Œã€‚" . -"该过滤器默认情况下是ç¦ç”¨çš„。

    +

    用户å¯ä»¥é€šè¿‡æµ‹è¯•结果过滤测试用例。测试结果是测试用例基于æŸä¸€æž„建的产物。测试用例的状æ€åŒ…括通过,失败,é”定或者尚未è¿è¡Œã€‚" . + "该过滤器默认情况下是ç¦ç”¨çš„。

    用户过滤器

    -

    用户å¯ä»¥æ ¹æ®æµ‹è¯•用例的指派者æ¥è¿‡æ»¤æµ‹è¯•用例。å¤é€‰æ¡†å…许包å«\"未指派\"的选项。

    "; -/* -

    当å‰ç»“æžœ

    -

    默认情况下或者没有选择å¤é€‰æ¡†é‡Œçš„"most current"选项时,树形目录将按照下拉选择框里选择的构建排åºã€‚ -这时树形目录将显示测试用例的状æ€ã€‚
    -例如:用户从下æ¥é€‰æ‹©æ¡†é‡Œé€‰æ‹©æž„建2而且没有选择å¤é€‰æ¡†é‡Œçš„"most current"。 -所有测试用例会显示它们在构建2里的执行状æ€ã€‚ -因此,如果测试用例1在构建2里执行通过的情况下,它会显示为绿色。 -
    如果用户选择了å¤é€‰æ¡†é‡Œçš„"most current",那么树形目录里的测试用例将根æ®ä»–们最新的执行结果显示相应的颜色。 -
    例如:如果用户选择了构建2而且选择了å¤é€‰æ¡†é‡Œçš„"most current",那么所有的测试用例将根æ®ä»–ä»¬æœ€è¿‘çš„çŠ¶æ€æ˜¾ç¤ºã€‚ -因此,如果测试用例1在构建3里通过,å³ä½¿ç”¨æˆ·é€‰æ‹©äº†æž„建2,它也会显示为绿色。

    - */ - - -// newest_tcversions.html //已校对 +

    用户å¯ä»¥æ ¹æ®æµ‹è¯•用例的指派者æ¥è¿‡æ»¤æµ‹è¯•用例。å¤é€‰æ¡†å…许包å«\"未指派\"的选项。

    "; +/* + *

    当å‰ç»“æžœ

    + *

    默认情况下或者没有选择å¤é€‰æ¡†é‡Œçš„"most current"选项时,树形目录将按照下拉选择框里选择的构建排åºã€‚ + * 这时树形目录将显示测试用例的状æ€ã€‚
    + * 例如:用户从下æ¥é€‰æ‹©æ¡†é‡Œé€‰æ‹©æž„建2而且没有选择å¤é€‰æ¡†é‡Œçš„"most current"。 + * 所有测试用例会显示它们在构建2里的执行状æ€ã€‚ + * 因此,如果测试用例1在构建2里执行通过的情况下,它会显示为绿色。 + *
    如果用户选择了å¤é€‰æ¡†é‡Œçš„"most current",那么树形目录里的测试用例将根æ®ä»–们最新的执行结果显示相应的颜色。 + *
    例如:如果用户选择了构建2而且选择了å¤é€‰æ¡†é‡Œçš„"most current",那么所有的测试用例将根æ®ä»–ä»¬æœ€è¿‘çš„çŠ¶æ€æ˜¾ç¤ºã€‚ + * 因此,如果测试用例1在构建3里通过,å³ä½¿ç”¨æˆ·é€‰æ‹©äº†æž„建2,它也会显示为绿色。

    + */ + +// newest_tcversions.html //已校对 $TLS_hlp_planTcModified = "

    è¢«å…³è”æµ‹è¯•用例的最新版本

    通过分æžä¸Žæµ‹è¯•计划关è”的所有测试用例,那些有最新版本的测试用例将被罗列出æ¥ï¼ˆç›¸å¯¹åº”äºŽå½“å‰æµ‹è¯•计划的测试用例) -

    "; - - -// requirementsCoverage.html //已校对 +

    "; + +// requirementsCoverage.html //已校对 $TLS_hlp_requirementsCoverage = "

    需求覆盖


    这个功能å…è®¸é€šè¿‡æµ‹è¯•ç”¨ä¾‹æ¥æ˜ å°„对用户或系统需求的覆盖度。 @@ -187,37 +183,36 @@

    需求规格

    éœ€æ±‚æ˜¯æ ¹æ®æµ‹è¯•项目中相关è”çš„'需求规约'文档æ¥ç»„织的。
    TestLink 䏿”¯æŒå³åŒ…å«éœ€æ±‚规约åˆåŒ…å«éœ€æ±‚的版本。 -å› æ­¤ï¼Œåªæœ‰åˆ›å»ºå¥½è§„çº¦ä¹‹åŽæ‰èƒ½å¾€é‡Œæ·»åŠ éœ€æ±‚æ–‡æ¡£ç‰ˆæœ¬ã€‚ +å› æ­¤ï¼Œåªæœ‰åˆ›å»ºå¥½è§„çº¦ä¹‹åŽæ‰èƒ½å¾€é‡Œæ·»åŠ éœ€æ±‚æ–‡æ¡£ç‰ˆæœ¬ã€‚ 标题. -用户于å¯ä»¥å‘范围中添加简å•çš„æè¿°æˆ–注释。

    +用户于å¯ä»¥å‘范围中添加简å•çš„æè¿°æˆ–注释。

    -

    需求覆盖数目用æ¥è¯„估需求覆盖度如果并没有把所有的需求添加(导入)到TestLink的情况下。 -需求覆盖数目的值0 指的是当å‰è¢«ç”¨æ¥åšç»“果分æžçš„需求数é‡ã€‚

    -

    例如 SRS 包å«200个需求,但是仅有50个被添加到TestLink。测试覆盖度为25%(如果这些被添加的需求都将被测试)。 +

    需求覆盖数目用æ¥è¯„估需求覆盖度如果并没有把所有的需求添加(导入)到TestLink的情况下。 +需求覆盖数目的值0 指的是当å‰è¢«ç”¨æ¥åšç»“果分æžçš„需求数é‡ã€‚

    +

    例如 SRS 包å«200个需求,但是仅有50个被添加到TestLink。测试覆盖度为25%(如果这些被添加的需求都将被测试)。

    需求

    点击需求规约的标题,你就å¯ä»¥åˆ›å»ºï¼Œç¼–辑,删除和导入需求文档。æ¯ä¸ªéœ€æ±‚都有标题,范围和状æ€ã€‚ 状æ€åŒ…括 \"有效的\" å’Œ \"ä¸å¯æµ‹è¯•çš„\". ä¸å¯æµ‹è¯•的需求ä¸ä¼šè¢«ç»Ÿè®¡åº¦é‡ã€‚ -è¯¥å‚æ•°ç”¨äºŽæ²¡æœ‰å®žçŽ°çš„åŠŸèƒ½å’Œé”™è¯¯çš„è®¾è®¡éœ€æ±‚ã€‚

    +è¯¥å‚æ•°ç”¨äºŽæ²¡æœ‰å®žçŽ°çš„åŠŸèƒ½å’Œé”™è¯¯çš„è®¾è®¡éœ€æ±‚ã€‚

    ä½ å¯ä»¥åœ¨éœ€æ±‚规约页é¢é€šè¿‡å¤šç§æŸ¥çœ‹éœ€æ±‚的途径为需求创建新的测试用例。 -这些测试用例被包å«åœ¨é€šè¿‡é…置命å的测试套件里。(默认是: $tlCfg->req_cfg->default_testsuite_name = +这些测试用例被包å«åœ¨é€šè¿‡é…置命å的测试套件里。(默认是: $tlCfg->req_cfg->default_testsuite_name = \"通过需求创建测试套件 - 自动\";). 标题和范围被å¤åˆ¶åˆ°è¿™äº›æµ‹è¯•用例。

    -"; - - -// planAddTC_m1.tpl //已校对 +"; + +// planAddTC_m1.tpl //已校对 $TLS_hlp_planAddTC = "

    关于'ä¿å­˜è‡ªå®šä¹‰å­—段'

    -如果你已ç»å®šä¹‰è€Œä¸”指派了关键字到测试项目中,
    +如果你已ç»å®šä¹‰è€Œä¸”指派了关键字到测试项目中,
    自定义字段具有:
    '在测试计划设计里显示=true' 和
    'å¯ç”¨æµ‹è¯•计划设计=true'
    ä½ å°†åªèƒ½åœ¨å·²ç»ä¸Žæµ‹è¯•计划建立关è”的测试用例页é¢çœ‹åˆ°è¿™äº›å…³é”®å­—。 -"; - -// xxx.html -//$TLS_hlp_xxx = ""; - -// ----- END ------------------------------------------------------------------ +"; + +// xxx.html +// $TLS_hlp_xxx = ""; + +// ----- END ------------------------------------------------------------------ ?> diff --git a/locale/zh_CN/strings.txt b/locale/zh_CN/strings.txt index bd4f4adaa6..96dfffb6b3 100644 --- a/locale/zh_CN/strings.txt +++ b/locale/zh_CN/strings.txt @@ -33,7 +33,7 @@ $TLS_STRINGFILE_CHARSET = "UTF-8"; // Last Update of this file -$TLS_last_update = "Last localization update: 2016-01-05 (Testlink 1.9.15)"; +$TLS_last_update = "Last localization update: 2021-06-15 (Testlink 1.9.19)"; // ----- General terms (used wide) ---------------------------- $TLS_active_click_to_change = '活动 (点击åŽè®¾ç½®ä¸ºä¸æ´»åЍ)'; @@ -1759,10 +1759,10 @@ $TLS_title_testproject_management = "测试项目管ç†"; $TLS_tcase_id_prefix = "测试用例标识"; $TLS_testproject_alt_delete = "删除测试项目"; // Warning!!! - if JS string you must use \\n to get \n -$TLS_popup_product_delete = "警告ï¼è¿™ä¸ªæ“作会永久删除与产å“相关的所有数æ®ï¼ˆåŒ…括测试结果ã€è®¡åˆ’等)。' . +$TLS_popup_product_delete = '警告ï¼è¿™ä¸ªæ“作会永久删除与产å“相关的所有数æ®ï¼ˆåŒ…括测试结果ã€è®¡åˆ’等)。' . '你也å¯ä»¥ä½¿ç”¨ç¦ç”¨æ¥ä»£æ›¿åˆ é™¤æ“作。\\n' . '建议:首先备份数æ®åº“。\\n' . - '你确定è¦åˆ é™¤è¿™ä¸ªäº§å“å—?"; + '你确定è¦åˆ é™¤è¿™ä¸ªäº§å“å—?'; $TLS_th_reqmgrsystem_short = '需求管ç†ç³»ç»Ÿ'; $TLS_available_test_projects = '(%d 个测试项目)'; diff --git a/locale/zh_CN/texts.php b/locale/zh_CN/texts.php index 7648003b8c..5c747d7510 100644 --- a/locale/zh_CN/texts.php +++ b/locale/zh_CN/texts.php @@ -1,39 +1,37 @@ -] and $TLS_help_title[] - * or - * $TLS_instruct[] and $TLS_instruct_title[] - * - * - * Revisions history is not stored for the file - * - * ------------------------------------------------------------------------------------ */ -$TLS_htmltext_title['error'] = "Application error"; -$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . - "logs for details.

    You are welcome to report the problem. Please visit our " . - "website.

    "; - - - -$TLS_htmltext_title['assignReqs'] = "分é…需求给测试用例"; //已校对 -$TLS_htmltext['assignReqs'] = "

    目的:

    +] and $TLS_help_title[] + * or + * $TLS_instruct[] and $TLS_instruct_title[] + * + * + * Revisions history is not stored for the file + * + * ------------------------------------------------------------------------------------ */ +$TLS_htmltext_title['error'] = "Application error"; +$TLS_htmltext['error'] = "

    Unexpected error happens. Please check event viewer or " . + "logs for details.

    You are welcome to report the problem. Please visit our " . + "website.

    "; + +$TLS_htmltext_title['assignReqs'] = "分é…需求给测试用例"; // 已校对 +$TLS_htmltext['assignReqs'] = "

    目的:

    用户å¯ä»¥è®¾ç½®æµ‹è¯•套件和需求规约之间的关系. 设计者å¯ä»¥æŠŠæ­¤å¤„çš„æµ‹è¯•å¥—ä»¶å’Œéœ€æ±‚è§„çº¦ä¸€ä¸€å…³è” .例如:一个测试用例å¯ä»¥è¢«å…³è”到零个ã€ä¸€ä¸ªã€å¤šä¸ªæµ‹è¯•套件,å之亦然. 这些å¯è¿½è¸ªçš„æ¨¡åž‹å¸®åŠ©æˆ‘ä»¬åŽ»ç ”ç©¶æµ‹è¯•ç”¨ä¾‹å¯¹éœ€æ±‚çš„è¦†ç›–æƒ…å†µ,并且找出测试用例是å¦é€šè¿‡çš„æƒ…况.这些分æžç”¨æ¥éªŒè¯æµ‹è¯•的覆盖程度是å¦è¾¾åˆ°é¢„期的结果。

    @@ -47,42 +45,38 @@ 底部的'有效的需求'列出了所有尚未关è”åˆ°å½“å‰æµ‹è¯•用例的需求. 测试设计者å¯ä»¥ç‚¹å‡»'指派'按钮把需求指派到测试用例.这些新关è”çš„ 测试用例会在工作区中间的'已指派的需求'中显示. -"; - - -// -------------------------------------------------------------------------------------- -$TLS_htmltext_title['editTc'] = "测试规范"; //已校对 -$TLS_htmltext['editTc'] = "

    目的:

    +"; + +// -------------------------------------------------------------------------------------- +$TLS_htmltext_title['editTc'] = "测试规范"; // 已校对 +$TLS_htmltext['editTc'] = "

    目的:

    目的:

    -

    测试规范 å…许用户查看和编辑所有现有的" . - "测试套件 å’Œ 测试用例. 测试用例默认使用当å‰ç‰ˆæœ¬.". - "所有以å‰çš„历å²ç‰ˆæœ¬éƒ½æ˜¯å¯ç”¨çš„,并且å¯ä»¥åœ¨è¿™é‡Œè¿›è¡ŒæŸ¥çœ‹å’Œç®¡ç†.

    +

    测试规范 å…许用户查看和编辑所有现有的" . "测试套件 å’Œ 测试用例. 测试用例默认使用当å‰ç‰ˆæœ¬." . + "所有以å‰çš„历å²ç‰ˆæœ¬éƒ½æ˜¯å¯ç”¨çš„,并且å¯ä»¥åœ¨è¿™é‡Œè¿›è¡ŒæŸ¥çœ‹å’Œç®¡ç†.

    开始:

      -
    1. 从å³ä¸Šè§’的下拉èœå•中选择你的测试项目. 注æ„: " . - "你永远å¯ä»¥ä»Žå³ä¸Šè§’的下拉èœå•选择改å˜å½“å‰çš„æµ‹è¯•项目." . - ".
    2. +
    3. 从å³ä¸Šè§’的下拉èœå•中选择你的测试项目. 注æ„: " . "你永远å¯ä»¥ä»Žå³ä¸Šè§’的下拉èœå•选择改å˜å½“å‰çš„æµ‹è¯•项目." . + ".
    4. 点击\"测试规范\",ç„¶åŽä»Žä¸­é€‰æ‹©ä¸€ä¸ªæµ‹è¯•套件
    5. -
    6. 点击 新建测试套件将创建一个新的测试套件的å­é›†. " . - "测试套件å­é›†å¯ä»¥ä¸ºä½ çš„æµ‹è¯•文档归类,å½’ç±»å¯ä»¥æ˜¯æŒ‰ç…§ä½ çš„éœ€è¦æ¥è¿›è¡Œ(功能/éžåŠŸèƒ½, 产å“部件, 产å“功能, 需求更改, 等等)." . - "测试套件å­é›†çš„æè¿°ä¸­ä¸­åŒ…å«äº†ç›¸å…³çš„æµ‹è¯•用例的作用域,默认的系统é…置信æ¯ç­‰,他们还å¯èƒ½åŒ…å«å’Œå…¶ä»–一些文档资料链接, æµ‹è¯•å±€é™æ€§, 或者其他信æ¯.通常这些注释是测试套件å­é›†æ‰€å…±åŒå…·æœ‰çš„. 他们构æˆäº†ä¸€ä¸ªæµ‹è¯•套件的文件夹的概念,测试套件å­é›†æ˜¯å¯ä»¥æ‰©å……的文件夹. 用户å¯ä»¥åœ¨åŒä¸€ä¸ªæµ‹è¯•计划里移动或者å¤åˆ¶å®ƒä»¬.åŒæ—¶, 他们å¯ä»¥ä½œä¸ºä¸€ä¸ªæ•´ä½“(包括其中的测试用例)输出或者输入到其他格å¼." .".
    7. -
    8. 在导航树中选择一个刚创建的新的测试套件" . - "ç„¶åŽç‚¹å‡»åˆ›å»ºæµ‹è¯•用例. å°±å¯ä»¥åœ¨è¿™ä¸ªæµ‹è¯•套件å­é›†é‡Œåˆ›å»ºä¸€ä¸ªæ–°çš„æµ‹è¯•用例." . - "一个测试用例定义了一个特有的测试过程,它包括测试的环境, 步骤, 期望的结果, 测试项目中的自定义字段(å‚è§ç”¨æˆ·æ‰‹å†Œ), 还å¯ä»¥ç»™æµ‹è¯•用例指派一个" . - "关键字 以方便跟踪查询.
    9. +
    10. 点击 新建测试套件将创建一个新的测试套件的å­é›†. " . + "测试套件å­é›†å¯ä»¥ä¸ºä½ çš„æµ‹è¯•文档归类,å½’ç±»å¯ä»¥æ˜¯æŒ‰ç…§ä½ çš„éœ€è¦æ¥è¿›è¡Œ(功能/éžåŠŸèƒ½, 产å“部件, 产å“功能, 需求更改, 等等)." . + "测试套件å­é›†çš„æè¿°ä¸­ä¸­åŒ…å«äº†ç›¸å…³çš„æµ‹è¯•用例的作用域,默认的系统é…置信æ¯ç­‰,他们还å¯èƒ½åŒ…å«å’Œå…¶ä»–一些文档资料链接, æµ‹è¯•å±€é™æ€§, 或者其他信æ¯.通常这些注释是测试套件å­é›†æ‰€å…±åŒå…·æœ‰çš„. 他们构æˆäº†ä¸€ä¸ªæµ‹è¯•套件的文件夹的概念,测试套件å­é›†æ˜¯å¯ä»¥æ‰©å……的文件夹. 用户å¯ä»¥åœ¨åŒä¸€ä¸ªæµ‹è¯•计划里移动或者å¤åˆ¶å®ƒä»¬.åŒæ—¶, 他们å¯ä»¥ä½œä¸ºä¸€ä¸ªæ•´ä½“(包括其中的测试用例)输出或者输入到其他格å¼." . + ".
    11. +
    12. 在导航树中选择一个刚创建的新的测试套件" . "ç„¶åŽç‚¹å‡»åˆ›å»ºæµ‹è¯•用例. å°±å¯ä»¥åœ¨è¿™ä¸ªæµ‹è¯•套件å­é›†é‡Œåˆ›å»ºä¸€ä¸ªæ–°çš„æµ‹è¯•用例." . + "一个测试用例定义了一个特有的测试过程,它包括测试的环境, 步骤, 期望的结果, 测试项目中的自定义字段(å‚è§ç”¨æˆ·æ‰‹å†Œ), 还å¯ä»¥ç»™æµ‹è¯•用例指派一个" . + "关键字 以方便跟踪查询.
    13. ä»Žå·¦è¾¹çš„å¯¼èˆªæ ‘é‡Œé€‰æ‹©å’Œç¼–è¾‘æ•°æ®æ¥å®žçŽ°å¯¼èˆªåŠŸèƒ½. 测试用例å¯ä»¥ä¿å­˜è‡ªå·±çš„æ‰€æœ‰åކå².
    14. 测试用例编写完毕åŽ, ä½ å¯ä»¥æŠŠå®ƒçš„æµ‹è¯•规范关è”到 测试计划 .
    15. + \"javascript:open_help_window('glossary','$locale');\">测试计划 .
    -

    TestLinkå¯ä»¥å¸®ä½ æ•´ç†æµ‹è¯•套件,å¯ä»¥æŠŠæµ‹è¯•套件分类æˆä¸ºä¸åŒçš„æµ‹è¯•套件å­é›†. 测试套件å­é›†è¿˜å¯ä»¥åŒ…嫿›´ä¸‹çº§çš„æµ‹è¯•案例å­é›†. - 因此你å¯ä»¥æŠŠè¿™äº›æ‰€æœ‰çš„ä¿¡æ¯æ‰“å°æˆå†Œ." ."

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchTc'] = "测试用例æœç´¢é¡µ"; //已校对 -$TLS_htmltext['searchTc'] = "

    目的:

    +

    TestLinkå¯ä»¥å¸®ä½ æ•´ç†æµ‹è¯•套件,å¯ä»¥æŠŠæµ‹è¯•套件分类æˆä¸ºä¸åŒçš„æµ‹è¯•套件å­é›†. 测试套件å­é›†è¿˜å¯ä»¥åŒ…嫿›´ä¸‹çº§çš„æµ‹è¯•案例å­é›†. + 因此你å¯ä»¥æŠŠè¿™äº›æ‰€æœ‰çš„ä¿¡æ¯æ‰“å°æˆå†Œ." . "

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchTc'] = "测试用例æœç´¢é¡µ"; // 已校对 +$TLS_htmltext['searchTc'] = "

    目的:

    按照关键字和æœç´¢å­—符串æ¥è¿›è¡Œæœç´¢. 英文æœç´¢æ˜¯ä¸åŒºåˆ†å¤§å°å†™. 结果åªåŒ…æ‹¬å½“å‰æµ‹è¯•项目中已有的测试用例.

    @@ -93,26 +87,13 @@
  • 选择必须的关键字或者让该æ ç›®ç•™ç©ºä¸º'ä¸ä½¿ç”¨'.
  • 点击“查找â€.
  • æ‰€æœ‰ç¬¦åˆæœç´¢æ¡ä»¶çš„æµ‹è¯•用例就会显示出æ¥. ä½ å¯ä»¥ç‚¹å‡»'标题'链接开始对测试用例进行其它æ“作.
  • -"; - - - - - - - - - - - - - - -/* contribution by asimon for 2976 */ -// requirements search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReq'] = "Requirement Search Page"; -$TLS_htmltext['searchReq'] = "

    Purpose:

    +"; + +/* contribution by asimon for 2976 */ +// requirements search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReq'] = "Requirement Search Page"; +$TLS_htmltext['searchReq'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result includes just requirements from actual Test Project.

    @@ -130,12 +111,12 @@

    - Only requirements within the current project will be searched.
    - The search is case-insensitive.
    -- Empty fields are not considered.

    "; - -// requirement specification search -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['searchReqSpec'] = "Requirement Specification Search Page"; -$TLS_htmltext['searchReqSpec'] = "

    Purpose:

    +- Empty fields are not considered.

    "; + +// requirement specification search +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['searchReqSpec'] = "Requirement Specification Search Page"; +$TLS_htmltext['searchReqSpec'] = "

    Purpose:

    Navigation according to keywords and/or searched strings. The search is not case sensitive. Result includes just requirement specifications from actual Test Project.

    @@ -153,13 +134,12 @@

    - Only requirement specifications within the current project will be searched.
    - The search is case-insensitive.
    -- Empty fields are not considered.

    "; -/* end contribution */ - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printTestSpec'] = "打å°éœ€æ±‚规约"; //printTC.html //已校对 -$TLS_htmltext['printTestSpec'] = "

    目的:

    +- Empty fields are not considered.

    "; +/* end contribution */ + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printTestSpec'] = "打å°éœ€æ±‚规约"; // printTC.html //已校对 +$TLS_htmltext['printTestSpec'] = "

    目的:

    在这里你å¯ä»¥æ‰“å°å•个测试用例,测试套件中的所有测试用例或者测试项目或测试计划中的所有测试用例.

    开始:

      @@ -171,21 +151,20 @@
    1. 使用æµè§ˆå™¨çš„æ‰“å°åŠŸèƒ½æ¥è¾“出信æ¯.
      注æ„:ä¿è¯åªæ‰“å°å³è¾¹çš„æ¡†æž¶.

    2. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['reqSpecMgmt'] = "需求规约设计"; //printTC.html //已校对 -$TLS_htmltext['reqSpecMgmt'] = "

    ä½ å¯ä»¥ç®¡ç†éœ€æ±‚规约文档.

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['reqSpecMgmt'] = "需求规约设计"; // printTC.html //已校对 +$TLS_htmltext['reqSpecMgmt'] = "

    ä½ å¯ä»¥ç®¡ç†éœ€æ±‚规约文档.

    需求规约

    需求是由需求规约文档æ¥çº¦å®šçš„,ç„¶åŽå…³è”到测试项目. -
    TestLink(当å‰ç‰ˆæœ¬)æš‚æ—¶è¿˜ä¸æ”¯æŒéœ€æ±‚规约版本中包å«äº†éœ€æ±‚本身的情况. +
    TestLink(当å‰ç‰ˆæœ¬)æš‚æ—¶è¿˜ä¸æ”¯æŒéœ€æ±‚规约版本中包å«äº†éœ€æ±‚本身的情况. 所以,文档的版本必须在需求规约之åŽåˆ›å»ºæ ‡é¢˜. 用户å¯ä»¥æ·»åŠ ç®€å•æè¿°åˆ° 范围 区域.

    -

    需求覆盖数目 +

    需求覆盖数目 是为了统计需求覆盖率而使用的,å¦‚æžœä¸æŠŠæ‰€æœ‰çš„éœ€æ±‚æäº¤åˆ°TestLink管ç†ï¼Œ0那么当å‰ç»“果分æžä¸­éœ€æ±‚的数é‡ä»¥TestLink管ç†èµ·æ¥çš„éœ€æ±‚ä¸ºä¾æ®.

    例如: SRS项目中包括200个需求,ä½†æ˜¯åªæœ‰50个需求被TestLink管ç†èµ·æ¥.那么测试覆盖率就是25%(如果所有的测试需求被测试的情况下).

    @@ -194,49 +173,32 @@

    点击已创建的需求规约,如果项目中还ä¸å­˜åœ¨éœ€æ±‚规约先创建一个.ç„¶åŽä½ å°±å¯ä»¥ä¸ºè¿™ä¸ªéœ€æ±‚规约创建具体的需求。æ¯ä¸ªéœ€æ±‚包括标题,范围和当å‰çжæ€.需求的状æ€å¯ä»¥æ˜¯'有效的'或者'ä¸å¯æµ‹è¯•çš„'.ä¸å¯æµ‹è¯•çš„éœ€æ±‚åœ¨ç»“æžœåˆ†æžæ—¶ä¸è¢«è®¡å…¥ç»Ÿè®¡æ•°æ®ã€‚è¿™ä¸ªå‚æ•°å¯ä»¥è¢«ç”¨æ¥è®¾ç½®é‚£äº›ä¸å¯å®žæ–½çš„功能特点或者错误的需求.

    用户å¯ä»¥åœ¨éœ€æ±‚界é¢ä¸­ä½¿ç”¨å·²åˆ›å»ºçš„需求自动创建测试用例.这些测试用例被创建到å字定义在é…置文件(default is: \$tlCfg->req_cfg->default_testsuite_name ='Test suite created by Requirement - Auto';) -中的测试套件中. 标题和范围被å¤åˆ¶åˆ°æµ‹è¯•用例中.

    "; - - - - - - - - - - - - - - - - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['printReqSpec'] = "Print Requirement Specification"; //printReq +中的测试套件中. 标题和范围被å¤åˆ¶åˆ°æµ‹è¯•用例中.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['printReqSpec'] = "Print Requirement Specification"; // printReq $TLS_htmltext['printReqSpec'] = "

    Purpose:

    You can generate document with the requirements within a requirement specification, or all the requirements in a test project.

    Get Started:

    1. -

      Select the parts of the requirements you want to display, and then click on a +

      Select the parts of the requirements you want to display, and then click on a requirement specification, or the test project. A printable page will be displayed.

    2. -
    3. Use the \"Show As\" drop-box in the navigation pane to specify whether you want -the information displayed as HTML, or in a Pseudo Micosoft Word document. +

    4. Use the \"Show As\" drop-box in the navigation pane to specify whether you want +the information displayed as HTML, or in a Pseudo Micosoft Word document. See help for more information.

    5. Use your browser's print functionality to actually print the information.
      Note: Make sure to only print the right-hand frame.

    6. -
    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['keywordsAssign'] = "指派关键字"; //已校对 -$TLS_htmltext['keywordsAssign'] = "

    目的:

    +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['keywordsAssign'] = "指派关键字"; // 已校对 +$TLS_htmltext['keywordsAssign'] = "

    目的:

    在该功能中用户å¯ä»¥æ‰¹é‡åœ°æŠŠå…³é”®å­—设置到现有的测试用例和测试套件中

    @@ -251,18 +213,17 @@

    å½“ä¸”ä»…å½“æµ‹è¯•è®¡åˆ’ä¸­åŒ…å«æœ€æ–°ç‰ˆæœ¬çš„æµ‹è¯•用例时,你指派的关键字æ‰èƒ½å½±å“到你的测试用例上. 如果你的测试计划中包å«çš„æ˜¯æ—§ç‰ˆæœ¬çš„æµ‹è¯•用例,你设置的关键字将ä¸ä¼šè¢«çœ‹åˆ°ã€‚

    -

    TestLink会使用这ç§è¦æ±‚,以至于你对最新版本的测试用例指派的关键字对测试计划中的旧版本没什么影å“. +

    TestLink会使用这ç§è¦æ±‚,以至于你对最新版本的测试用例指派的关键字对测试计划中的旧版本没什么影å“. å¦‚æžœä½ å¸Œæœ›æµ‹è¯•è®¡åˆ’ä¸­çš„å…³é”®å­—åŠæ—¶æ›´æ–°ï¼Œé¦–先使用'更新修改的测试用例'æ¥éªŒè¯æ˜¯å¦æ˜¯æœ€æ–°ç‰ˆæœ¬ -在指派关键字之å‰.

    "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['executeTest'] = "测试用例执行"; //已校对 -$TLS_htmltext['executeTest'] = "

    目的:

    +在指派关键字之å‰.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['executeTest'] = "测试用例执行"; // 已校对 +$TLS_htmltext['executeTest'] = "

    目的:

    å…许用户执行测试用例.用户为了构建的需è¦å¯ä»¥æŠŠæµ‹è¯•结果和相关测试用例关è”èµ·æ¥. - 查看关于过滤器和设置的更多帮助 " . - "(点击?按钮).

    + 查看关于过滤器和设置的更多帮助 " . + "(点击?按钮).

    开始:

    @@ -273,12 +234,12 @@
  • 完善测试用例的结果和任何åˆé€‚的记录或问题报告.
  • ä¿å­˜ç»“æžœ.
  • -

    注æ„:如果你打算直接创建/跟踪问题,必须先é…ç½®TestLinkå…³è”到相关的bug跟踪工具.

    "; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['showMetrics'] = "测试报告和统计数æ®"; //已校对 -$TLS_htmltext['showMetrics'] = "

    关于测试计划的报告" . - "(在导航æ¡é‡Œå®šä¹‰äº†). 这个测试计划å¯èƒ½ä¸Žå½“剿‰§è¡Œçš„æµ‹è¯•计划ä¸åŒ. å¯ä»¥é€‰æ‹©çš„æ ¼å¼æœ‰:

    +

    注æ„:如果你打算直接创建/跟踪问题,必须先é…ç½®TestLinkå…³è”到相关的bug跟踪工具.

    "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['showMetrics'] = "测试报告和统计数æ®"; // 已校对 +$TLS_htmltext['showMetrics'] = "

    关于测试计划的报告" . + "(在导航æ¡é‡Œå®šä¹‰äº†). 这个测试计划å¯èƒ½ä¸Žå½“剿‰§è¡Œçš„æµ‹è¯•计划ä¸åŒ. å¯ä»¥é€‰æ‹©çš„æ ¼å¼æœ‰:

    • HTML - 报告显示为网页格å¼
    • MS Excel - 报告输出为 Microsoft Excel
    • @@ -353,12 +314,11 @@

      æ¯ä¸ªæµ‹è¯•用例报告的错误总数

      -

      该报表显示了æ¯ä¸ªæµ‹è¯•用例所å‘现的所有错误. 包括全部项目中的所有错误. è¯¥æŠ¥è¡¨åªæœ‰åœ¨å’Œé”™è¯¯è·Ÿè¸ªç³»ç»Ÿæ•´åˆäº†ä»¥åŽæ‰å¯è§.

      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planAddTC'] = "添加/删除测试用例到测试计划"; // testSetAdd //已校对 -$TLS_htmltext['planAddTC'] = "

      目的:

      +

      该报表显示了æ¯ä¸ªæµ‹è¯•用例所å‘现的所有错误. 包括全部项目中的所有错误. è¯¥æŠ¥è¡¨åªæœ‰åœ¨å’Œé”™è¯¯è·Ÿè¸ªç³»ç»Ÿæ•´åˆäº†ä»¥åŽæ‰å¯è§.

      "; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planAddTC'] = "添加/删除测试用例到测试计划"; // testSetAdd //已校对 +$TLS_htmltext['planAddTC'] = "

      目的:

      用户å¯ä»¥ä»Žæµ‹è¯•计划中添加或者删除测试用例(用户的级别至少为项目ç»ç†).

      添加ï¼åˆ é™¤æµ‹è¯•用例的步骤:

      @@ -366,11 +326,11 @@
    • ç‚¹å‡»æµ‹è¯•å¥—ä»¶æŸ¥çœ‹å®ƒçš„æ‰€æœ‰çš„å­æµ‹è¯•å¥—ä»¶ä»¥åŠæ‰€æœ‰çš„æµ‹è¯•用例.
    • 当你点击\"添加/删除测试用例\"æ¥æ·»åŠ æˆ–è€…åˆ é™¤æµ‹è¯•ç”¨ä¾‹æ—¶ 注æ„: ä¸å¯èƒ½å¤šæ¬¡æ·»åŠ ç›¸åŒçš„æµ‹è¯•用例.
    • -"; - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['tc_exec_assignment'] = "ç»™æµ‹è¯•å‘˜åˆ†é…æµ‹è¯•任务"; //已校对 -$TLS_htmltext['tc_exec_assignment'] = "

      目的

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['tc_exec_assignment'] = "ç»™æµ‹è¯•å‘˜åˆ†é…æµ‹è¯•任务"; // 已校对 +$TLS_htmltext['tc_exec_assignment'] = "

      目的

      管ç†è€…é€šè¿‡è¯¥é¡µé¢æ¥å¯¹æµ‹è¯•人员分é…具体测试任务.

      开始

      @@ -379,16 +339,14 @@
    • 选择该项目的测试员.
    • 点击'ä¿å­˜'按钮æäº¤.
    • 打开测试员的执行页é¢éªŒè¯å…³è”的情况.å¯ä»¥ä¸ºä½¿ç”¨è€…设置过滤器.
    • -"; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['planUpdateTC'] = "更新测试计划中的测试用例"; //已校对 -$TLS_htmltext['planUpdateTC'] = "

      目的

      +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['planUpdateTC'] = "更新测试计划中的测试用例"; // 已校对 +$TLS_htmltext['planUpdateTC'] = "

      目的

      如果需求规约版本改å˜äº†ï¼Œå¯ä»¥é€šè¿‡è¯¥é¡µé¢å¯¹æµ‹è¯•用例进行更新. - 在测试过程中ç»å¸¸å‘生添加新的需求的情况." . - " 例如:用户更改了测试规约,但是这个改å˜éœ€è¦ä¼ è¾¾åˆ°æµ‹è¯•计划中. " . - " å¦åˆ™æµ‹è¯•è®¡åˆ’ç»§ç»­ä½¿ç”¨ç€æ—§ç‰ˆæœ¬çš„需求规约,æµ‹è¯•ç»“æžœè¿˜åœ¨å…³è”æµ‹è¯•用例中的字段.

      + 在测试过程中ç»å¸¸å‘生添加新的需求的情况." . " 例如:用户更改了测试规约,但是这个改å˜éœ€è¦ä¼ è¾¾åˆ°æµ‹è¯•计划中. " . + " å¦åˆ™æµ‹è¯•è®¡åˆ’ç»§ç»­ä½¿ç”¨ç€æ—§ç‰ˆæœ¬çš„需求规约,æµ‹è¯•ç»“æžœè¿˜åœ¨å…³è”æµ‹è¯•用例中的字段.

      开始

        @@ -396,13 +354,12 @@
      1. 从å¤é€‰æ¡†ä¸­ä¸ºæŒ‡å®šçš„æµ‹è¯•用例选择新版本.
      2. 点击'更新测试计划'æ¥æäº¤æ”¹å˜.
      3. éªŒè¯æ–¹æ³•:查看执行页é¢ä¸­çš„æµ‹è¯•用例(集).
      4. -
      "; - - -// ------------------------------------------------------------------------------------------ -$TLS_htmltext_title['test_urgency'] = "设置测试的紧急程度"; -$TLS_htmltext['test_urgency'] = "

      目的

      -

      TestLinkå…许设置测试套件的紧急程度æ¥å½±å“测试用例执行的优先级. +"; + +// ------------------------------------------------------------------------------------------ +$TLS_htmltext_title['test_urgency'] = "设置测试的紧急程度"; +$TLS_htmltext['test_urgency'] = "

      目的

      +

      TestLinkå…许设置测试套件的紧急程度æ¥å½±å“测试用例执行的优先级. 测试的优先级å–决于测试用例的é‡è¦ç¨‹åº¦å’Œå®šä¹‰åœ¨æµ‹è¯•计划中的紧急程度两个方é¢. 项目领导者å¯ä»¥è®¾ç½®å“ªä¸€å¥—测试用例先被执行. 使用这个功能æ¥ç¡®ä¿åœ¨æ—¶é—´å…许的情 况下覆盖最é‡è¦çš„æµ‹è¯•用例.

      @@ -414,9 +371,8 @@ 执行的产å“的严é‡ç¨‹åº¦.
    • 点击'ä¿å­˜'æ¥æäº¤æ”¹å˜.
    • -

      例如:一个'高'紧急程度的测试套件中的'低'紧急程度的测试用例在执行时是'中'级别 " .""; - - -// ------------------------------------------------------------------------------------------ - +

      例如:一个'高'紧急程度的测试套件中的'低'紧急程度的测试用例在执行时是'中'级别 " . ""; + +// ------------------------------------------------------------------------------------------ + ?> diff --git a/login.php b/login.php index 4bdf0ba914..ac57efdce5 100644 --- a/login.php +++ b/login.php @@ -1,415 +1,569 @@ - we will redirect to login screen with some message -doBlockingChecks($db,$gui); - -switch($args->action) { - case 'doLogin': - case 'ajaxlogin': - doSessionStart(true); - - // When doing ajax login we need to skip control regarding session already open - // that we use when doing normal login. - // If we do not proceed this way we will enter an infinite loop - $options = new stdClass(); - $options->doSessionExistsCheck = ($args->action =='doLogin'); - $op = doAuthorize($db,$args->login,$args->pwd,$options); - $doAuthPostProcess = true; - $gui->draw = true; - break; - - case 'ajaxcheck': - processAjaxCheck($db); - break; - - - case 'oauth': - // If code is empty then break - if (!isset($args->oauth_code)){ - renderLoginScreen($gui); - die(); - } - - // Switch between oauth providers - if (!include_once('lib/functions/oauth_providers/'.$args->oauth_name.'.php')) { - die("Oauth client doesn't exist"); - } - - $oau = config_get('OAuthServers'); - foreach ($oau as $oprov) { - if (strcmp($oprov['oauth_name'],$args->oauth_name) == 0){ - $oauth_params = $oprov; - break; - } - } - - $user_token = oauth_get_token($oauth_params, $args->oauth_code); - if($user_token->status['status'] == tl::OK) { - doSessionStart(true); - $op = doAuthorize($db,$user_token->options->user,'oauth',$user_token->options); - $doAuthPostProcess = true; - } else { - $gui->note = $user_token->status['msg']; - $gui->draw=true; - renderLoginScreen($gui); - die(); - } - break; - - case 'loginform': - $doRenderLoginScreen = true; - $gui->draw = true; - $op = null; - - // unfortunatelly we use $args->note in order to do some logic. - if( ($args->note=trim($args->note)) == "" ) { - if( $gui->authCfg['SSO_enabled'] ) { - doSessionStart(true); - $doAuthPostProcess = true; - - switch ($gui->authCfg['SSO_method']) { - case 'CLIENT_CERTIFICATE': - $op = doSSOClientCertificate($db,$_SERVER,$gui->authCfg); - break; - - case 'WEBSERVER_VAR': - //DEBUGsyslogOnCloud('Trying to execute SSO using SAML'); - $op = doSSOWebServerVar($db,$gui->authCfg); - break; - } - } - } - break; -} - -if( $doAuthPostProcess ) { - list($doRenderLoginScreen,$gui->note) = authorizePostProcessing($args,$op); -} - -if( $doRenderLoginScreen ) { - renderLoginScreen($gui); -} - -/** - * - * - */ -function init_args() { - $pwdInputLen = config_get('loginPagePasswordMaxLenght'); - - // 2010904 - eloff - Why is req and reqURI parameters to the login? - $iParams = array("note" => array(tlInputParameter::STRING_N,0,255), - "tl_login" => array(tlInputParameter::STRING_N,0,100), - "tl_password" => array(tlInputParameter::STRING_N,0,$pwdInputLen), - "req" => array(tlInputParameter::STRING_N,0,4000), - "reqURI" => array(tlInputParameter::STRING_N,0,4000), - "action" => array(tlInputParameter::STRING_N,0, 10), - "destination" => array(tlInputParameter::STRING_N, 0, 255), - "loginform_token" => array(tlInputParameter::STRING_N, 0, 255), - "viewer" => array(tlInputParameter::STRING_N, 0, 3), - "oauth" => array(tlInputParameter::STRING_N,0,100), - "code" => array(tlInputParameter::STRING_N,0,4000), - "state" => array(tlInputParameter::STRING_N,0,100), - ); - $pParams = R_PARAMS($iParams); - - $args = new stdClass(); - $args->note = $pParams['note']; - $args->login = $pParams['tl_login']; - - $args->pwd = $pParams['tl_password']; - $args->ssodisable = getSSODisable(); - $args->reqURI = urlencode($pParams['req']); - $args->preqURI = urlencode($pParams['reqURI']); - $args->destination = urldecode($pParams['destination']); - $args->loginform_token = urldecode($pParams['loginform_token']); - - $args->viewer = $pParams['viewer']; - - $k2c = array('ajaxcheck' => 'do','ajaxlogin' => 'do'); - if (isset($k2c[$pParams['action']])) { - $args->action = $pParams['action']; - } else if (!is_null($args->login)) { - $args->action = 'doLogin'; - // This 'if' branch may be removed in later versions. Kept for compatibility - } else if (!is_null($pParams['oauth']) && $pParams['oauth']) { - $args->action = 'oauth'; - $args->oauth_name = $pParams['oauth']; - $args->oauth_code = $pParams['code']; - } else if (!is_null($pParams['state']) && !is_null($pParams['code'])) { - $args->action = 'oauth'; - $args->oauth_name = $pParams['state']; - $args->oauth_code = $pParams['code']; - } else { - $args->action = 'loginform'; - } - - // whitelist oauth_name - if (strcasecmp($args->action,'oauth') == 0) { - validateOauth($args->oauth_name); - } - - return $args; -} - -/** - * - */ -function validateOauth($name) { - $name = trim($name); - $oauthServers = config_get('OAuthServers'); - $whitelistOK = false; - foreach ($oauthServers as $serverCfg) { - if (strcasecmp($name, $serverCfg['oauth_name']) == 0) { - $whitelistOK = true; - break; - } - } - - if ($whitelistOK == false) { - die("Invalid Oauth Service"); - } -} - -/** - * - * - */ -function init_gui(&$db,$args) { - $gui = new stdClass(); - $gui->viewer = $args->viewer; - - $secCfg = config_get('config_check_warning_frequence'); - $gui->securityNotes = ''; - if( (strcmp($secCfg, 'ALWAYS') == 0) || - (strcmp($secCfg, 'ONCE_FOR_SESSION') == 0 && !isset($_SESSION['getSecurityNotesDone'])) ) { - $_SESSION['getSecurityNotesDone'] = 1; - $gui->securityNotes = getSecurityNotes($db); - } - - $gui->authCfg = config_get('authentication'); - $gui->user_self_signup = config_get('user_self_signup'); - - // Oauth buttons - $oau = config_get('OAuthServers'); - $gui->oauth = array(); - foreach ($oau as $oauth_prov) { - if ($oauth_prov['oauth_enabled']) { - $name = $oauth_prov['oauth_name']; - $gui->oauth[$name] = new stdClass(); - $gui->oauth[$name]->name = ucfirst($name); - $gui->oauth[$name]->link = oauth_link($oauth_prov); - $gui->oauth[$name]->icon = $name . '.png'; - } - } - - $gui->external_password_mgmt = false; - $domain = $gui->authCfg['domain']; - $mm = $gui->authCfg['method']; - if( isset($domain[$mm]) ) { - $ac = $domain[$mm]; - $gui->external_password_mgmt = !$ac['allowPasswordManagement']; - } - - $gui->login_disabled = (('LDAP' == $gui->authCfg['method']) && !checkForLDAPExtension()) ? 1 : 0; - - switch($args->note) { - case 'expired': - if(!isset($_SESSION)) { - session_start(); - } - session_unset(); - session_destroy(); - $gui->note = lang_get('session_expired'); - $gui->reqURI = null; - break; - - case 'first': - $gui->note = lang_get('your_first_login'); - $gui->reqURI = null; - break; - - case 'lost': - $gui->note = lang_get('passwd_lost'); - $gui->reqURI = null; - break; - - default: - $gui->note = ''; - break; - } - - $gui->ssodisable = 0; - if(property_exists($args,'ssodisable')) { - $gui->ssodisable = $args->ssodisable; - } - - $gui->reqURI = $args->reqURI ? $args->reqURI : $args->preqURI; - $gui->destination = $args->destination; - $gui->pwdInputMaxLenght = config_get('loginPagePasswordMaxLenght'); - - - // Random Background - $imgSet = array(); - $imgSet[] = "wp-testing04.jpg"; - $imgSet[] = "Fedora-24-Default-Wallpaper-1.png"; - $imgSet[] = "fedora-76343.jpg"; - $imgSet[] = "fedora-hex_0.002.png"; - $imgSet[] = "fedora-x.jpeg"; - $imgSet[] = "fedora21.png-1024x640.jpg"; - $imgSet[] = "fedora28.png"; - - $itemQty = count($imgSet)-1; - $ixx = rand(0,$itemQty); - - $gui->loginBackgroundImg = - "gui/templates/dashio/img/login/" . $imgSet[$ixx]; - - return $gui; -} - - -/** - * doBlockingChecks - * - * wrong Schema version will BLOCK ANY login action - * - * @param &$dbHandler DataBase Handler - * @param &$guiObj some gui elements that will be used to give feedback - * - */ -function doBlockingChecks(&$dbHandler,&$guiObj) { - $op = checkSchemaVersion($dbHandler); - if( $op['status'] < tl::OK ) { - // Houston we have a problem - // This check to kill session was added to avoid following situation - // TestLink 1.9.5 installed - // Install TestLink 1.9.6 in another folder, pointing to same OLD DB - // you logged in TL 1.9.5 => session is created - // you try to login to 1.9.6, you get the Update DB Schema message but - // anyway because a LIVE AND VALID session you are allowed to login => BAD - if(isset($op['kill_session']) && $op['kill_session']) { - session_unset(); - session_destroy(); - } - - $guiObj->draw = false; - $guiObj->note = $op['msg']; - renderLoginScreen($guiObj); - die(); - } -} - - -/** - * renderLoginScreen - * simple piece of code used to clean up code layout - * - * @global $g_tlLogger - * @param stdClassObject $guiObj - */ -function renderLoginScreen($guiObj) { - global $g_tlLogger; - $templateCfg = templateConfiguration(); - $logPeriodToDelete = config_get('removeEventsOlderThan'); - $g_tlLogger->deleteEventsFor(null, strtotime("-{$logPeriodToDelete} days UTC")); - - $smarty = new TLSmarty(); - $smarty->assign('gui', $guiObj); - - $templ = config_get('tpl'); - $tpl = $templ['login']; - - $smarty->display($tpl); -} - - -/** - * - * @param stdClassObject $argsObj - * @param hash $op - */ -function authorizePostProcessing($argsObj,$op) { - $note = null; - $renderLoginScreen = false; - if($op['status'] == tl::OK) { - // Login successful, redirect to destination - logAuditEvent(TLS("audit_login_succeeded",$argsObj->login, - $_SERVER['REMOTE_ADDR']),"LOGIN",$_SESSION['currentUser']->dbID,"users"); - - if ($argsObj->action == 'ajaxlogin') { - echo json_encode(array('success' => true)); - } else { - // If destination param is set redirect to given page ... - if (!empty($argsObj->destination) && preg_match("/linkto.php/", $argsObj->destination)) { - redirect($argsObj->destination); - } else { - // ... or show main page - $_SESSION['viewer'] = $argsObj->viewer; - $ad = $argsObj->ssodisable ? '&ssodisable=1' : ''; - $ad .= ($argsObj->preqURI ? "&reqURI=".urlencode($argsObj->preqURI) :""); - - $rul = $_SESSION['basehref'] . - "index.php?caller=login&viewer={$argsObj->viewer}" . $ad; - - redirect($rul); - } - exit(); // hmm seems is useless - } - } else { - $note = ''; - if(!$argsObj->ssodisable) { - $note = is_null($op['msg']) ? lang_get('bad_user_passwd') : $op['msg']; - } - - if($argsObj->action == 'ajaxlogin') { - echo json_encode(array('success' => false,'reason' => $note)); - } else { - $renderLoginScreen = true; - } - } - - return array($renderLoginScreen,$note); -} - -/** - * - * - */ -function processAjaxCheck(&$dbHandler) { - // Send a json reply, include localized strings for use in js to display a login form. - doSessionStart(true); - echo json_encode(array('validSession' => checkSessionValid($dbHandler, false), - 'username_label' => lang_get('login_name'), - 'password_label' => lang_get('password'), - 'login_label' => lang_get('btn_login'), - 'timeout_info' => lang_get('timeout_info'))); - + we will redirect to login screen with some message +doBlockingChecks($db, $gui); + +switch ($args->action) { + case 'doLogin': + case 'ajaxlogin': + doSessionStart(true); + + // When doing ajax login we need to skip control regarding session already open + // that we use when doing normal login. + // If we do not proceed this way we will enter an infinite loop + $options = new stdClass(); + $options->doSessionExistsCheck = ($args->action == 'doLogin'); + $op = doAuthorize($db, $args->login, $args->pwd, $options); + $doAuthPostProcess = true; + $gui->draw = true; + break; + + case 'ajaxcheck': + processAjaxCheck($db); + break; + + case 'oauth': + // If code is empty then break + if (! isset($args->oauth_code)) { + renderLoginScreen($gui); + die(); + } + + // Switch between oauth providers + // validate providers + $includeOK = false; + $oauth_params = getOAuthProviderCfg($args->oauth_name); + if ($oauth_params != null) { + $g2i = $args->oauth_name . '.php'; + if (! include_once $g2i) { + die("Oauth client doesn't exist"); + } else { + $includeOK = true; + } + } + + // No good! + if (! $includeOK) { + renderLoginScreen($gui); + die(); + } + + $user_token = oauth_get_token($oauth_params, $args->oauth_code); + if ($user_token->status['status'] == tl::OK) { + doSessionStart(true); + $op = doAuthorize($db, $user_token->options->user, 'oauth', + $user_token->options); + $doAuthPostProcess = true; + } else { + $gui->note = $user_token->status['msg']; + $gui->draw = true; + renderLoginScreen($gui); + die(); + } + break; + + case 'loginform': + $doRenderLoginScreen = true; + $gui->draw = true; + $op = null; + + // unfortunatelly we use $args->note in order to do some logic. + if ((($args->note = trim($args->note)) == "") && + ($gui->authCfg['SSO_enabled'])) { + doSessionStart(true); + $doAuthPostProcess = true; + + switch ($gui->authCfg['SSO_method']) { + case 'CLIENT_CERTIFICATE': + $op = doSSOClientCertificate($db, $_SERVER, $gui->authCfg); + break; + + case 'WEBSERVER_VAR': + $op = doSSOWebServerVar($db, $gui->authCfg); + break; + } + } + break; +} + +if ($doAuthPostProcess) { + list ($doRenderLoginScreen, $gui->note) = authorizePostProcessing($args, $op); +} + +if ($doRenderLoginScreen) { + renderLoginScreen($gui); +} + +/** + * Initialize arguments + * + * @return stdClass + */ +function initArgs() +{ + $pwdInputLen = config_get('loginPagePasswordMaxLenght'); + + // 2010904 - eloff - Why is req and reqURI parameters to the login? + $iParams = array( + "note" => array( + tlInputParameter::STRING_N, + 0, + 255 + ), + "tl_login" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "tl_password" => array( + tlInputParameter::STRING_N, + 0, + $pwdInputLen + ), + "req" => array( + tlInputParameter::STRING_N, + 0, + 4000 + ), + "reqURI" => array( + tlInputParameter::STRING_N, + 0, + 4000 + ), + "action" => array( + tlInputParameter::STRING_N, + 0, + 10 + ), + "destination" => array( + tlInputParameter::STRING_N, + 0, + 255 + ), + "loginform_token" => array( + tlInputParameter::STRING_N, + 0, + 255 + ), + "viewer" => array( + tlInputParameter::STRING_N, + 0, + 3 + ), + "oauth" => array( + tlInputParameter::STRING_N, + 0, + 100 + ), + "code" => array( + tlInputParameter::STRING_N, + 0, + 4000 + ), + "state" => array( + tlInputParameter::STRING_N, + 0, + 100 + ) + ); + $pParams = R_PARAMS($iParams); + + $args = new stdClass(); + $args->note = $pParams['note']; + $args->login = $pParams['tl_login']; + + $args->pwd = $pParams['tl_password']; + $args->ssodisable = getSSODisable(); + $args->reqURI = urlencode($pParams['req']); + $args->preqURI = urlencode($pParams['reqURI']); + $args->destination = urldecode($pParams['destination']); + $args->loginform_token = urldecode($pParams['loginform_token']); + + $args->viewer = ''; + + $k2c = array( + 'ajaxcheck' => 'do', + 'ajaxlogin' => 'do' + ); + if (isset($k2c[$pParams['action']])) { + $args->action = $pParams['action']; + } elseif (! is_null($args->login)) { + $args->action = 'doLogin'; + // This 'if' branch may be removed in later versions. Kept for compatibility + } elseif (! is_null($pParams['oauth']) && $pParams['oauth']) { + $args->action = 'oauth'; + $args->oauth_name = $pParams['oauth']; + $args->oauth_code = $pParams['code']; + } elseif (! is_null($pParams['state']) && ! is_null($pParams['code'])) { + + // We use state to undertand the provider when the redirect url + // can not have query string, as happens with Microsoft + // state will be 'testlink provider id'$$$state(random string) + // + // read https://auth0.com/docs/protocols/oauth2/oauth-state + // + $args->action = 'oauth'; + $args->oauth_name = explode('$$$', $pParams['state']); + $args->oauth_name = $args->oauth_name[0]; + $args->oauth_code = $pParams['code']; + } else { + $args->action = 'loginform'; + } + + // whitelist oauth_name + if (strcasecmp($args->action, 'oauth') == 0) { + validateOauth($args->oauth_name); + } + + return $args; +} + +/** + * Validate Oauth + */ +function validateOauth($name) +{ + $name = trim($name); + $oauthServers = config_get('OAuthServers'); + $whitelistOK = false; + foreach ($oauthServers as $serverCfg) { + if (strcasecmp($name, $serverCfg['oauth_name']) == 0) { + $whitelistOK = true; + break; + } + } + + if (! $whitelistOK) { + die("Invalid Oauth Service"); + } +} + +/** + * Initialize the interface + * + * @param database $db + * @param stdClass $args + * @return stdClass + */ +function initGui(&$db, $args) +{ + $gui = new stdClass(); + $gui->viewer = $args->viewer; + + $secCfg = config_get('config_check_warning_frequence'); + $gui->securityNotes = ''; + if ((strcmp($secCfg, 'ALWAYS') == 0) || + (strcmp($secCfg, 'ONCE_FOR_SESSION') == 0 && + ! isset($_SESSION['getSecurityNotesDone']))) { + $_SESSION['getSecurityNotesDone'] = 1; + $gui->securityNotes = getSecurityNotes($db); + } + + $gui->authCfg = config_get('authentication'); + $gui->user_self_signup = config_get('user_self_signup'); + + // Oauth buttons + $oau = config_get('OAuthServers'); + $gui->oauth = array(); + foreach ($oau as $oauth_prov) { + if ($oauth_prov['oauth_enabled']) { + $name = $oauth_prov['oauth_name']; + $gui->oauth[$name] = new stdClass(); + $gui->oauth[$name]->name = ucfirst($name); + $gui->oauth[$name]->link = oauthLink($oauth_prov); + $gui->oauth[$name]->icon = $name . '.png'; + } + } + + if (isset($gui->authCfg['sso_only']) && $gui->authCfg['sso_only']) { + $gui->external_password_mgmt = true; + } else { + $gui->external_password_mgmt = false; + $domain = $gui->authCfg['domain']; + $mm = $gui->authCfg['method']; + if (isset($domain[$mm])) { + $ac = $domain[$mm]; + $gui->external_password_mgmt = ! $ac['allowPasswordManagement']; + } + } + + $gui->login_disabled = (('LDAP' == $gui->authCfg['method']) && + ! checkForLDAPExtension()) ? 1 : 0; + + switch ($args->note) { + case 'expired': + if (! isset($_SESSION)) { + session_start(); + } + session_unset(); + session_destroy(); + $gui->note = lang_get('session_expired'); + $gui->reqURI = null; + break; + + case 'first': + $gui->note = lang_get('your_first_login'); + $gui->reqURI = null; + break; + + case 'lost': + $gui->note = lang_get('passwd_lost'); + $gui->reqURI = null; + break; + + default: + $gui->note = ''; + break; + } + + $gui->ssodisable = 0; + if (property_exists($args, 'ssodisable')) { + $gui->ssodisable = $args->ssodisable; + } + + $gui->reqURI = $args->reqURI ? $args->reqURI : $args->preqURI; + $gui->destination = $args->destination; + $gui->pwdInputMaxLenght = config_get('loginPagePasswordMaxLenght'); + + // Random Background + $imgSet = array(); + $imgSet[] = "wp-testing04.jpg"; + $imgSet[] = "Fedora-24-Default-Wallpaper-1.png"; + $imgSet[] = "fedora-76343.jpg"; + $imgSet[] = "fedora-hex_0.002.png"; + $imgSet[] = "fedora-x.jpeg"; + $imgSet[] = "fedora21.png-1024x640.jpg"; + $imgSet[] = "fedora28.png"; + + $itemQty = count($imgSet) - 1; + $ixx = rand(0, $itemQty); + + $gui->loginBackgroundImg = "gui/templates/dashio/img/login/" . $imgSet[$ixx]; + + return $gui; +} + +/** + * doBlockingChecks + * + * wrong Schema version will BLOCK ANY login action + * + * @param + * database &$dbHandler DataBase Handler + * @param + * stdClass &$guiObj some gui elements that will be used to give feedback + */ +function doBlockingChecks(&$dbHandler, &$guiObj) +{ + $op = checkSchemaVersion($dbHandler); + if ($op['status'] < tl::OK) { + // Houston we have a problem + // This check to kill session was added to avoid following situation + // TestLink 1.9.5 installed + // Install TestLink 1.9.6 in another folder, pointing to same OLD DB + // you logged in TL 1.9.5 => session is created + // you try to login to 1.9.6, you get the Update DB Schema message but + // anyway because a LIVE AND VALID session you are allowed to login => BAD + if (isset($op['kill_session']) && $op['kill_session']) { + session_unset(); + session_destroy(); + } + + $guiObj->draw = false; + $guiObj->note = $op['msg']; + renderLoginScreen($guiObj); + die(); + } +} + +/** + * Render login screen + * + * simple piece of code used to clean up code layout + * + * @global tlLogger $g_tlLogger + * @param stdClass $guiObj + */ +function renderLoginScreen($guiObj) +{ + global $g_tlLogger; + + $logPeriodToDelete = config_get('removeEventsOlderThan'); + $g_tlLogger->deleteEventsFor(null, + strtotime("-{$logPeriodToDelete} days UTC")); + + $smarty = new TLSmarty(); + $smarty->assign('gui', $guiObj); + + $templ = config_get('tpl'); + $tpl = $templ['login']; + + $smarty->display($tpl); +} + +/** + * authorizePostProcessing + * + * @param stdClass $argsObj + * @param array $op + * @return array + */ +function authorizePostProcessing($argsObj, $op) +{ + $note = null; + $renderLoginScreen = false; + if ($op['status'] == tl::OK) { + // Login successful, redirect to destination + logAuditEvent( + TLS("audit_login_succeeded", $argsObj->login, + $_SERVER['REMOTE_ADDR']), "LOGIN", + $_SESSION['currentUser']->dbID, "users"); + + if ($argsObj->action == 'ajaxlogin') { + echo json_encode(array( + 'success' => true + )); + } else { + // If destination param is set redirect to given page ... + if (! empty($argsObj->destination)) { + + // 1) remove host.port from TL_BASE_HREF -> base_folder + // https://hsgdshdjs:80/bsbsbb + // http://fjljfld:8080/Hhhhs + // http://hjhsjdhshdk/ + // $baseURL = str_replace('://',':',TL_BASE_HREF); + $basePieces = explode(':', TL_BASE_HREF); + $howManyPieces = count($basePieces); + switch ($howManyPieces) { + case 2: + case 3: + break; + default: + echo 'Security Check Failure'; + die(); + break; + } + + // http: hjhsjdhshdk/ + // http: hjhsjdhshdk/base_folder + // https: hsgdshdjs: >> 80/bsbsbb + // http: fjljfld: >> 8080/Hhhhs + $dummy = explode('/', $basePieces[$howManyPieces - 1]); + $baseFolder = '/'; + $compo = trim($dummy[1]); + if ($compo != '') { + $baseFolder .= $compo . '/'; + } + + // 2) check base_folder/linkto.php + $where = strpos($argsObj->destination, + $baseFolder . 'linkto.php'); + $checkOK = ($where !== false) && ($where == 0); + if (! $checkOK) { + echo 'Security Check Failure'; + die(); + } + + // 3) validate content after linkto.php? + $dummy = explode($baseFolder . 'linkto.php?'); + $afterLinkTo = $baseFolder . 'linkto.php?' . + cleanInput($dummy[1]); + redirect($afterLinkTo); + } else { + // ... or show main page + $_SESSION['viewer'] = $argsObj->viewer; + $ad = $argsObj->ssodisable ? '&ssodisable=1' : ''; + $ad .= ($argsObj->preqURI ? "&reqURI=" . + urlencode($argsObj->preqURI) : ""); + + $rul = $_SESSION['basehref'] . + "index.php?caller=login&viewer={$argsObj->viewer}" . $ad; + + redirect($rul); + } + exit(); // hmm seems is useless + } + } else { + $note = ''; + if (! $argsObj->ssodisable) { + $note = is_null($op['msg']) ? lang_get('bad_user_passwd') : $op['msg']; + } + + if ($argsObj->action == 'ajaxlogin') { + echo json_encode(array( + 'success' => false, + 'reason' => $note + )); + } else { + $renderLoginScreen = true; + } + } + + return array( + $renderLoginScreen, + $note + ); +} + +/** + * Perform Ajax check + * + * @param database $dbHandler + */ +function processAjaxCheck(&$dbHandler) +{ + // Send a json reply, include localized strings for use in js to display a login form. + doSessionStart(true); + echo json_encode( + array( + 'validSession' => checkSessionValid($dbHandler, false), + 'username_label' => lang_get('login_name'), + 'password_label' => lang_get('password'), + 'login_label' => lang_get('btn_login'), + 'timeout_info' => lang_get('timeout_info') + )); +} + +/** + * Clean input + * + * @param string $input + * @return string|array|NULL + * @see https://css-tricks.com/snippets/php/sanitize-database-inputs/ + */ +function cleanInput($input) +{ + $search = array( + '@]*?>.*?@si', // Strip out javascript + '@<[\/\!]*?[^<>]*?>@si', // Strip out HTML tags + '@]*?>.*?@siU', // Strip style tags properly + '@@' // Strip multi-line comments + ); + + return preg_replace($search, '', $input); } diff --git a/logout.php b/logout.php index 93bcd14302..603b64f997 100644 --- a/logout.php +++ b/logout.php @@ -1,49 +1,52 @@ -userID) { - logAuditEvent(TLS("audit_user_logout",$args->userName),"LOGOUT",$args->userID,"users"); -} -session_unset(); -session_destroy(); - -$authCfg = config_get('authentication'); -if(isset($authCfg['SSO_enabled']) && $authCfg['SSO_enabled'] - && $args->ssodisable == FALSE) { - redirect($authCfg['SSO_logout_destination']); -} else { - $std = "login.php?note=logout&viewer={$args->viewer}"; - $std .= $args->ssodisable ? "&ssodisable" : ''; - - $xx = config_get('logoutUrl'); - $lo = is_null($xx) || trim($xx) == '' ? $std : $xx; - redirect($lo); -} -exit(); - - -/** - * - */ -function init_args() { - $args = new stdClass(); - - $args->userID = isset($_SESSION['userID']) ? $_SESSION['userID'] : null; - $args->userName = $args->userID ? $_SESSION['currentUser']->getDisplayName() : ""; - - $args->viewer = isset($_GET['viewer']) ? $_GET['viewer'] : ''; - $args->ssodisable = getSSODisable(); - - return $args; -} \ No newline at end of file +userID) { + logAuditEvent(TLS("audit_user_logout", $args->userName), "LOGOUT", + $args->userID, "users"); +} +session_unset(); +session_destroy(); + +$authCfg = config_get('authentication'); +if (isset($authCfg['SSO_enabled']) && $authCfg['SSO_enabled'] && + ! $args->ssodisable) { + redirect($authCfg['SSO_logout_destination']); +} else { + $std = "login.php?note=logout&viewer={$args->viewer}"; + $std .= $args->ssodisable ? "&ssodisable" : ''; + + $xx = config_get('logoutUrl'); + $lo = is_null($xx) || trim($xx) == '' ? $std : $xx; + redirect($lo); +} +exit(); + +/** + * Initializes the arguments + * + * @return stdClass + */ +function init_args() +{ + $args = new stdClass(); + + $args->userID = isset($_SESSION['userID']) ? $_SESSION['userID'] : null; + $args->userName = $args->userID ? $_SESSION['currentUser']->getDisplayName() : ""; + + $args->viewer = isset($_GET['viewer']) ? $_GET['viewer'] : ''; + $args->ssodisable = getSSODisable(); + + return $args; +} diff --git a/lostPassword.php b/lostPassword.php index 09117ab142..cb1febfda2 100644 --- a/lostPassword.php +++ b/lostPassword.php @@ -1,80 +1,97 @@ -external_password_mgmt = 0; -$gui->page_title = lang_get('page_title_lost_passwd'); -$gui->note = lang_get('your_info_for_passwd'); -$gui->password_mgmt_feedback = ''; -$gui->login = $args->login; -$gui->viewer = $args->viewer; - -$op = doDBConnect($db,database::ONERROREXIT); - -$userID = false; -if ($args->login != "") { - $userID = tlUser::doesUserExist($db,$args->login); - if (!$userID) { - $gui->note = lang_get('bad_user'); - } else { - // need to know if auth method for user allows reset - $user = new tlUser(intval($userID)); - $user->readFromDB($db); - if(tlUser::isPasswordMgtExternal($user->authentication,$user->authentication)) { - $gui->external_password_mgmt = 1; - $gui->password_mgmt_feedback = sprintf(lang_get('password_mgmt_feedback'),trim($args->login)); - } - } -} - -if(!$gui->external_password_mgmt && $userID) { - $result = resetPassword($db,$userID); - $gui->note = $result['msg']; - if ($result['status'] >= tl::OK) { - $user = new tlUser($userID); - if ($user->readFromDB($db) >= tl::OK) { - logAuditEvent(TLS("audit_pwd_reset_requested",$user->login),"PWD_RESET",$userID,"users"); - } - redirect(TL_BASE_HREF ."login.php?note=lost&viewer={$args->viewer}"); - exit(); - } else if ($result['status'] == tlUser::E_EMAILLENGTH) { - $gui->note = lang_get('mail_empty_address'); - } else if ($note != "") { - $gui->note = getUserErrorMessage($result['status']); - } -} - -$smarty = new TLSmarty(); -$smarty->assign('gui',$gui); - -$tpl = str_replace('.php','.tpl',basename($_SERVER['SCRIPT_NAME'])); -if( $args->viewer == 'new' ) { - $tpl = 'lostPassword-model-marcobiedermann.tpl'; -} -$tpl = 'login/' . $tpl; -$smarty->display($tpl); - -/** - * - */ -function init_args() -{ - $iParams = array("login" => array('POST',tlInputParameter::STRING_N,0,30), - "viewer" => array('GET',tlInputParameter::STRING_N, 0, 3)); - - $args = new stdClass(); - I_PARAMS($iParams,$args); - return $args; -} -?> \ No newline at end of file +external_password_mgmt = 0; +$gui->page_title = lang_get('page_title_lost_passwd'); +$gui->note = lang_get('your_info_for_passwd'); +$gui->password_mgmt_feedback = ''; +$gui->login = $args->login; +$gui->viewer = $args->viewer; + +$op = doDBConnect($db, database::ONERROREXIT); + +$userID = false; +if ($args->login != "") { + $userID = tlUser::doesUserExist($db, $args->login); + if (! $userID) { + $gui->note = lang_get('bad_user'); + } else { + // need to know if auth method for user allows reset + $user = new tlUser(intval($userID)); + $user->readFromDB($db); + if (tlUser::isPasswordMgtExternal($user->authentication, + $user->authentication)) { + $gui->external_password_mgmt = 1; + $gui->password_mgmt_feedback = sprintf( + lang_get('password_mgmt_feedback'), trim($args->login)); + } + } +} + +if (! $gui->external_password_mgmt && $userID) { + $result = resetPassword($db, $userID); + $gui->note = $result['msg']; + if ($result['status'] >= tl::OK) { + $user = new tlUser($userID); + if ($user->readFromDB($db) >= tl::OK) { + logAuditEvent(TLS("audit_pwd_reset_requested", $user->login), + "PWD_RESET", $userID, "users"); + } + redirect(TL_BASE_HREF . "login.php?note=lost&viewer={$args->viewer}"); + exit(); + } elseif ($result['status'] == tlUser::E_EMAILLENGTH) { + $gui->note = lang_get('mail_empty_address'); + } elseif ($note != "") { + $gui->note = getUserErrorMessage($result['status']); + } +} + +$smarty = new TLSmarty(); +$smarty->assign('gui', $gui); + +$tpl = str_replace('.php', '.tpl', basename($_SERVER['SCRIPT_NAME'])); +if ($args->viewer == 'new') { + $tpl = 'lostPassword-model-marcobiedermann.tpl'; +} +$tpl = 'login/' . $tpl; +$smarty->display($tpl); + +/** + * Initializes the arguments + * + * @return stdClass + */ +function init_args() +{ + $iParams = array( + "login" => array( + 'POST', + tlInputParameter::STRING_N, + 0, + 30 + ), + "viewer" => array( + 'GET', + tlInputParameter::STRING_N, + 0, + 3 + ) + ); + + $args = new stdClass(); + I_PARAMS($iParams, $args); + return $args; +} +?> diff --git a/ltcp.php b/ltcp.php new file mode 100644 index 0000000000..abfacc4191 --- /dev/null +++ b/ltcp.php @@ -0,0 +1,155 @@ + array( + tlInputParameter::STRING_N, + $userAPIkeyLen, + $userAPIkeyLen + ), + "testcase" => array( + tlInputParameter::STRING_N, + 0, + 64 + ) + ]; + } catch (Exception $e) { + echo $e->getMessage(); + exit(); + } + + R_PARAMS($iParams, $args); + $opt = array( + 'setPaths' => true, + 'clearSession' => true + ); + + // validate apikey to avoid SQL injection + $args->apikey = trim($args->apikey); + $akl = strlen($args->apikey); + switch ($akl) { + case $userAPIkeyLen: + $args->debug = 'USER-APIKEY'; + setUpEnvForRemoteAccess($dbHandler, $args->apikey, null, $opt); + + // returns array element are arrays NOT USER OBJECT!!! + $userSearch = tlUser::getByAPIKey($dbHandler, $args->apikey); + $args->light = 'red'; + if (count($userSearch) == 1) { + $args->light = 'green'; + $userData = current($userSearch); + $user = new tlUser($userData['id']); + $user->readFromDB($dbHandler); + } + break; + + default: + throw new Exception("Aborting - Bad API Key lenght", 1); + break; + } + + $commonText = " - The call signature does not pass the system Checks - operation can not be fullfilled"; + if ($args->light == 'red') { + echo "LTCP-01" . $commonText; + die(); + } + + // c94048220527a3d038db5c19e1156c08 + + // need to extract testcase information + // PREFIX-NUMBER-VERSION + // example: PPT-8989-2 + // + // Frome prefix we will get testproject info + // in order to check user rights + // + // Trying to mitigate SQL injection I will get prefix of + // all test projects then check array + $tbl = DB_TABLE_PREFIX . 'testprojects'; + $sql = "SELECT prefix,id FROM $tbl "; + $rs = $dbHandler->fetchRowsIntoMap($sql, 'prefix'); + $testCasePieces = explode('-', $args->testcase); + + if (count($testCasePieces) != 3) { + echo "LTCP-02" . $commonText; + die(); + } + + $prjPrefix = trim($testCasePieces[0]); + if (! isset($rs[$prjPrefix])) { + echo "LTCP-03" . $commonText; + die(); + } + + $tproject_id = intval($rs[$prjPrefix]['id']); + + // Check rights on test project + $canRead = $user->hasRight($dbHandler, "mgt_view_tc", $tproject_id, null, + true); + if (! $canRead) { + echo "LTCP-04 - System Checks do not allow operation requested"; + die(); + } + + // everything is OK, now need to launch + // https:///lib/testcases/tcPrint.php?show_mode=&testcase_id=72510&tcversion_id=72511 + // + $externalID = $testCasePieces[0] . '-' . $testCasePieces[1]; + $tcaseMgr = new testcase($dbHandler); + $testcase_id = $tcaseMgr->getInternalID($externalID); + $allTCVID = $tcaseMgr->getAllVersionsID($testcase_id); + $idSet = implode(',', $allTCVID); + $tcaseVersionNumber = intval($testCasePieces[2]); + $tbl = DB_TABLE_PREFIX . 'tcversions'; + $sql = " SELECT version,id FROM $tbl + WHERE id IN ($idSet) + AND version = $tcaseVersionNumber"; + $rs = (array) $dbHandler->fetchRowsIntoMap($sql, 'version'); + if (count($rs) != 1) { + die(); + } + $tcversion_id = intval($rs[$tcaseVersionNumber]['id']); + + $url2call = "testcase_id=%TC%&tcversion_id=%TCV%"; + $url2call = str_replace([ + "%TC%", + "%TCV%" + ], [ + $testcase_id, + $tcversion_id + ], $url2call); + + $what2launch = "/lib/testcases/tcPrint.php?$url2call"; + header('Location:' . TL_BASE_HREF . $what2launch); + exit(); +} diff --git a/ltx.php b/ltx.php index 569141f0bf..9c9f1ec9dd 100644 --- a/ltx.php +++ b/ltx.php @@ -1,486 +1,426 @@ -tproject_id = 0; - - if( $args->status_ok ) - { - if($args->tplan_id != '') - { - $hasRight = checkTestPlan($db,$args->user,$args); - if( $hasRight ) - { - $lof = 'launch_outer_' . $args->item; - $lof($smarty,$args); - } - } - } - else - { - echo lang_get('security_check_ko'); - ob_end_flush(); - exit(); - } -} -else -{ - // - // inner frame, parameters passed - // figure out what to display - // - // key: item, value: url to tree management page - $driver = isset($_GET['item']) ? $_GET['item'] : null; - if(is_null($driver)) - { - die(); - } - - $lif = 'launch_inner_' . $driver; - $lif($db,$smarty); -} -ob_end_flush(); - - -/** - * - * - */ -function checkTestPlan(&$db,&$user,&$args) -{ - $hasRight = false; - $tplan_mgr = new testplan($db); - - $item_info = $tplan_mgr->get_by_id($args->tplan_id,array( 'output' => 'minimun')); - if(($op['status_ok'] = !is_null($item_info))) - { - $args->tproject_id = intval($item_info['tproject_id']); - - switch($args->item) - { - case 'exec': - case 'xta2m': - $hasRight = $user->hasRight($db,'testplan_execute', - $args->tproject_id,$args->tplan_id); - break; - - - default: - // need to fail!! - break; - } - } - return $hasRight; -} - - -/** - * - */ -function init_args(&$dbHandler) -{ - $args = new stdClass(); - $args->tplan_id = intval(isset($_GET['tplan_id']) ? $_GET['tplan_id'] : null); - $args->tcversion_id = intval(isset($_GET['tcversion_id']) ? $_GET['tcversion_id'] : null); - $args->platform_id = intval(isset($_GET['platform_id']) ? $_GET['platform_id'] : null); - $args->build_id = intval(isset($_GET['build_id']) ? $_GET['build_id'] : null); - - $args->anchor = isset($_GET['anchor']) ? $_GET['anchor'] : null; - $args->item = isset($_GET['item']) ? $_GET['item'] : null; - - $args->feature_id = isset($_GET['feature_id']) ? $_GET['feature_id'] : null; - - - $args->target_user_id = intval(isset($_GET['user_id']) ? $_GET['user_id'] : null); - $args->user = $_SESSION['currentUser']; - $args->user_id = $_SESSION['userID']; - - // status depends on access request - $cfn = 'check_'; - switch($args->item) - { - case 'exec': - $cfn .= $args->item; - $args->status_ok = ($args->build_id >0); - break; - - case 'xta2m': - $cfn .= $args->item; - $args->status_ok = ($args->target_user_id >0 && $args->tplan_id >0); - break; - - default: - $cfn = ''; - $args->status_ok = false; - break; - } - - if($args->status_ok && $cfn != '') - { - $cfn($dbHandler,$args); - } - return $args; -} - -/** - * - */ -function build_link_exec(&$argsObj) -{ - $lk = isset($_GET['item']) ? "item=" . $_GET['item'] : ''; - - if($argsObj->feature_id >0) - { - $lk .= "&feature_id=" . $argsObj->feature_id; - } - else - { - $lk .= "&tplan_id=" . $argsObj->tplan_id . "&platform_id=" . $argsObj->platform_id; - "&tcversion_id=" . $argsObj->tcversion_id; - } - $lk .= "&build_id=" . $argsObj->build_id; - $lk .= '&load' . (isset($_GET['anchor']) ? '&anchor=' . $_GET['anchor'] : ""); - - return $lk; -} - - - - -/** - * - * - */ -function process_exec(&$dbHandler,$context) -{ - $ret = array(); - $ret['url'] = null; - $ret['msg'] = 'ko'; - - $treeMgr = new tree($dbHandler); - $info = $treeMgr->get_node_hierarchy_info($context['tcversion_id']); - - $ret['url'] = "lib/execute/execSetResults.php?level=testcase" . - "&version_id=" . $context['tcversion_id'] . - "&id=" . $info['parent_id'] . - "&setting_testplan=" . $context['setting_testplan'] . - "&setting_build=" . $context['setting_build'] . - "&setting_platform=" . $context['setting_platform']; - - - - $ret['msg'] = 'ok'; - return $ret; -} - -/** - * xta2m: eXecution Tasks Assigned TO Me - * - */ -function process_xta2m(&$dbHandler,$context) -{ - $ret = array(); - $ret['url'] = null; - $ret['msg'] = 'ko'; - - $treeMgr = new tree($dbHandler); - $info = $treeMgr->get_node_hierarchy_info($context['tcversion_id']); - - $ret['url'] = "lib/execute/execSetResults.php?level=testcase" . - "&version_id=" . $context['tcversion_id'] . - "&id=" . $info['parent_id'] . - "&setting_testplan=" . $context['setting_testplan'] . - "&setting_build=" . $context['setting_build'] . - "&setting_platform=" . $context['setting_platform']; - - - - $ret['msg'] = 'ok'; - return $ret; -} - -/** - * - * - */ -function check_exec(&$dbHandler,&$argsObj) -{ - - if( $argsObj->feature_id >0 ) - { - // get missing data - $tb = DB_TABLE_PREFIX . 'testplan_tcversions'; - $sql = "SELECT testplan_id,platform_id,tcversion_id " . - "FROM {$tb} WHERE id=" . $argsObj->feature_id; - - $rs = $dbHandler->get_recordset($sql); - $argsObj->tplan_id = $rs[0]['testplan_id']; - $argsObj->tcversion_id = $rs[0]['tcversion_id']; - $argsObj->platform_id = $rs[0]['platform_id']; - } - else - { - $argsObj->status_ok = ($argsObj->tplan_id > 0) && - ($argsObj->tcversion_id >0); - } -} - -/** - * - * - */ -function check_xta2m(&$dbHandler,&$argsObj) -{ - $argsObj->status_ok = ($argsObj->target_user_id > 0 && - $argsObj->tplan_id >0); - - if($argsObj->target_user_id != $argsObj->user_id) - { - $argsObj->status_ok = false; - } -} - - -/** - * - * - */ -function launch_inner_exec(&$dbHandler,&$tplMgr) -{ - $itemCode = array('exec' => 'lib/execute/execNavigator.php'); - $op = array('status_ok' => true, 'msg' => ''); - - // First check for keys in _GET that MUST EXIST - // key: key on _GET, value: labelID defined on strings.txt - $mandatoryKeys = array('item' => 'item_not_set', - 'build_id' => 'build_id_not_set'); - - foreach($mandatoryKeys as $key => $labelID) - { - $op['status_ok'] = isset($_GET[$key]); - if( !$op['status_ok']) - { - $op['msg'] = lang_get($labelID); - break; - } - } - - if( $op['status_ok'] ) - { - $op['status_ok'] = isset($_GET['feature_id']); - if( !$op['status_ok'] ) - { - $keySet = array('tplan_id' => 'testplan_not_set', - 'tcversion_id' => 'tcversion_id', - 'platform_id' => 'platform_id_not_set'); - - foreach($keySet as $key => $labelID) - { - $op['status_ok'] = isset($_GET[$key]); - if( !$op['status_ok']) - { - $op['msg'] = lang_get($labelID); - break; - } - } - } - } - - $args = init_args($dbHandler); - if($op['status_ok']) - { - // Set Environment - $tplan_mgr = new testplan($dbHandler); - $info = $tplan_mgr->get_by_id($args->tplan_id,array('output' => 'minimun')); - - if(is_null($info)) - { - die('ltx - tplan info does not exist'); - } - - $tproject_mgr = new testproject($dbHandler); - $tproject_mgr->setSessionProject($info['tproject_id']); - $op['status_ok'] = true; - } - - if($op['status_ok']) - { - // Build name of function to call for doing the job. - $pfn = 'process_' . $args->item; - - $ctx = array(); - $ctx['setting_testplan'] = $args->tplan_id; - $ctx['setting_build'] = $args->build_id; - $ctx['setting_platform'] = $args->platform_id; - $ctx['tcversion_id'] = $args->tcversion_id; - $ctx['tcase_id'] = 0; - $ctx['user_id'] = $args->user_id; - - $jump_to = $pfn($dbHandler,$ctx); - $op['status_ok'] = !is_null($jump_to['url']); - $op['msg'] = $jump_to['msg']; - } - - if($op['status_ok']) - { - $treeframe = $itemCode[$args->item] . - '?loadExecDashboard=0' . - '&setting_testplan=' . $args->tplan_id . - '&setting_build=' . $args->build_id . - '&setting_platform=' . $args->platform_id; - - $tplMgr->assign('title', lang_get('main_page_title')); - $tplMgr->assign('treewidth', TL_FRMWORKAREA_LEFT_FRAME_WIDTH); - $tplMgr->assign('workframe', $jump_to['url']); - $tplMgr->assign('treeframe', $treeframe); - $tplMgr->display('frmInner.tpl'); - } - else - { - echo $op['msg']; - ob_end_flush(); - exit(); - } -} // function end - -/** - * xta2m: eXecution Tasks Assigned TO Me - * - */ -function launch_inner_xta2m(&$dbHandler,&$tplMgr) -{ - $args = init_args($dbHandler); - - //if($args->status_ok == FALSE) - //{ - // echo 'NOOO'; - // ob_end_flush(); - // exit(); - //} - - $jt = $_SESSION['basehref'] . '/lib/testcases/' . - 'tcAssignedToUser.php?user_id=' . $args->target_user_id . - - $k2c = array('tplan_id','build_id'); - foreach($k2c as $tg) - { - if( property_exists($args,$tg) && $args->$tg > 0 ) - { - $jt .= "&$tg=" . $args->$tg; - } - } - - $tplMgr->assign('workframe', $jt); - $tplMgr->display('workframe.tpl'); -} - -/** - * - */ -function launch_outer_exec(&$tplMgr,$argsObj) -{ - $gui = new stdClass(); - $gui->titleframe = 'lib/general/navBar.php?caller=linkto'; - $gui->navbar_height = config_get('navbar_height'); - - if( $argsObj->tproject_id > 0) - { - $gui->titleframe .= '&testproject=' . $argsObj->tproject_id; - } - $gui->title = lang_get('main_page_title'); - $gui->mainframe = 'ltx.php?' . build_link_exec($argsObj); - - $tplMgr->assign('gui', $gui); - $tplMgr->display('main.tpl'); -} - -/** - * - */ -function launch_outer_xta2m(&$tplMgr,$argsObj) -{ - $gui = new stdClass(); - $gui->titleframe = 'lib/general/navBar.php?caller=linkto'; - $gui->navbar_height = config_get('navbar_height'); - - if( $argsObj->tproject_id > 0) - { - $gui->titleframe .= '&testproject=' . $argsObj->tproject_id; - } - $gui->title = lang_get('main_page_title'); - $gui->mainframe = 'ltx.php?item=xta2m&load=1' . - '&user_id=' . $argsObj->target_user_id . - '&tplan_id=' . $argsObj->tplan_id; - - $tplMgr->assign('gui', $gui); - $tplMgr->display('main.tpl'); -} - -/** - * - * - */ -function buildCookie(&$dbHandler,$itemID,$tprojectID,$cookiePrefix) -{ - $tree_mgr = new tree($dbHandler); - $path = $tree_mgr->get_path($itemID); - $parents = array(); - $parents[] = $tprojectID; - foreach($path as $node) - { - $parents[] = $node['id']; - } - array_pop($parents); - $cookieInfo['path'] = 'a:s%3A/' . implode("/", $parents); - $cookieInfo['value'] = $cookiePrefix . $tprojectID . '_ext-comp-1001' ; - return $cookieInfo; +tproject_id = 0; + + if ($args->status_ok) { + if ($args->tplan_id != '') { + $hasRight = checkTestPlan($db, $args->user, $args); + if ($hasRight) { + $lof = 'launch_outer_' . $args->item; + $lof($smarty, $args); + } + } + } else { + echo lang_get('security_check_ko'); + ob_end_flush(); + exit(); + } +} else { + // + // inner frame, parameters passed + // figure out what to display + // + // key: item, value: url to tree management page + $driver = isset($_GET['item']) ? $_GET['item'] : null; + if (is_null($driver)) { + die(); + } + + $lif = 'launch_inner_' . $driver; + $lif($db, $smarty); +} +ob_end_flush(); + +/** + */ +function checkTestPlan(&$db, &$user, &$args) +{ + $hasRight = false; + $tplan_mgr = new testplan($db); + + $item_info = $tplan_mgr->get_by_id($args->tplan_id, + array( + 'output' => 'minimun' + )); + if ($op['status_ok'] = ! is_null($item_info)) { + $args->tproject_id = intval($item_info['tproject_id']); + + switch ($args->item) { + case 'exec': + case 'xta2m': + $hasRight = $user->hasRight($db, 'testplan_execute', + $args->tproject_id, $args->tplan_id); + break; + + default: + // need to fail!! + break; + } + } + return $hasRight; +} + +/** + */ +function init_args(&$dbHandler) +{ + $args = new stdClass(); + $args->tplan_id = intval( + isset($_GET['tplan_id']) ? $_GET['tplan_id'] : null); + $args->tcversion_id = intval( + isset($_GET['tcversion_id']) ? $_GET['tcversion_id'] : null); + $args->platform_id = intval( + isset($_GET['platform_id']) ? $_GET['platform_id'] : null); + $args->build_id = intval( + isset($_GET['build_id']) ? $_GET['build_id'] : null); + + $args->anchor = isset($_GET['anchor']) ? $_GET['anchor'] : null; + $args->item = isset($_GET['item']) ? $_GET['item'] : null; + + $args->feature_id = isset($_GET['feature_id']) ? $_GET['feature_id'] : null; + + $args->target_user_id = intval( + isset($_GET['user_id']) ? $_GET['user_id'] : null); + $args->user = $_SESSION['currentUser']; + $args->user_id = $_SESSION['userID']; + + // status depends on access request + $cfn = 'check_'; + switch ($args->item) { + case 'exec': + $cfn .= $args->item; + $args->status_ok = ($args->build_id > 0); + break; + + case 'xta2m': + $cfn .= $args->item; + $args->status_ok = ($args->target_user_id > 0 && $args->tplan_id > 0); + break; + + default: + $cfn = ''; + $args->status_ok = false; + break; + } + + if ($args->status_ok && $cfn != '') { + $cfn($dbHandler, $args); + } + return $args; +} + +/** + */ +function build_link_exec(&$argsObj) +{ + $lk = isset($_GET['item']) ? "item=" . $_GET['item'] : ''; + + if ($argsObj->feature_id > 0) { + $lk .= "&feature_id=" . $argsObj->feature_id; + } else { + $lk .= "&tplan_id=" . $argsObj->tplan_id . "&platform_id=" . + $argsObj->platform_id . "&tcversion_id=" . $argsObj->tcversion_id; + } + $lk .= "&build_id=" . $argsObj->build_id; + $lk .= '&load' . (isset($_GET['anchor']) ? '&anchor=' . $_GET['anchor'] : ""); + + return $lk; +} + +/** + */ +function process_exec(&$dbHandler, $context) +{ + $ret = array(); + + $treeMgr = new tree($dbHandler); + $info = $treeMgr->get_node_hierarchy_info($context['tcversion_id']); + + $ret['url'] = "lib/execute/execSetResults.php?level=testcase" . + "&version_id=" . $context['tcversion_id'] . "&id=" . $info['parent_id'] . + "&setting_testplan=" . $context['setting_testplan'] . "&setting_build=" . + $context['setting_build'] . "&setting_platform=" . + $context['setting_platform']; + + $ret['msg'] = 'ok'; + return $ret; +} + +/** + * xta2m: eXecution Tasks Assigned TO Me + */ +function process_xta2m(&$dbHandler, $context) +{ + $ret = array(); + + $treeMgr = new tree($dbHandler); + $info = $treeMgr->get_node_hierarchy_info($context['tcversion_id']); + + $ret['url'] = "lib/execute/execSetResults.php?level=testcase" . + "&version_id=" . $context['tcversion_id'] . "&id=" . $info['parent_id'] . + "&setting_testplan=" . $context['setting_testplan'] . "&setting_build=" . + $context['setting_build'] . "&setting_platform=" . + $context['setting_platform']; + + $ret['msg'] = 'ok'; + return $ret; +} + +/** + */ +function check_exec(&$dbHandler, &$argsObj) +{ + if ($argsObj->feature_id > 0) { + // get missing data + $tb = DB_TABLE_PREFIX . 'testplan_tcversions'; + $sql = "SELECT testplan_id,platform_id,tcversion_id " . + "FROM {$tb} WHERE id=" . $argsObj->feature_id; + + $rs = $dbHandler->get_recordset($sql); + $argsObj->tplan_id = $rs[0]['testplan_id']; + $argsObj->tcversion_id = $rs[0]['tcversion_id']; + $argsObj->platform_id = $rs[0]['platform_id']; + } else { + $argsObj->status_ok = ($argsObj->tplan_id > 0) && + ($argsObj->tcversion_id > 0); + } +} + +/** + */ +function check_xta2m(&$dbHandler, &$argsObj) +{ + $argsObj->status_ok = ($argsObj->target_user_id > 0 && $argsObj->tplan_id > 0); + + if ($argsObj->target_user_id != $argsObj->user_id) { + $argsObj->status_ok = false; + } +} + +/** + */ +function launch_inner_exec(&$dbHandler, &$tplMgr) +{ + $itemCode = array( + 'exec' => 'lib/execute/execNavigator.php' + ); + $op = array( + 'status_ok' => true, + 'msg' => '' + ); + + // First check for keys in _GET that MUST EXIST + // key: key on _GET, value: labelID defined on strings.txt + $mandatoryKeys = array( + 'item' => 'item_not_set', + 'build_id' => 'build_id_not_set' + ); + + foreach ($mandatoryKeys as $key => $labelID) { + $op['status_ok'] = isset($_GET[$key]); + if (! $op['status_ok']) { + $op['msg'] = lang_get($labelID); + break; + } + } + + if ($op['status_ok']) { + $op['status_ok'] = isset($_GET['feature_id']); + if (! $op['status_ok']) { + $keySet = array( + 'tplan_id' => 'testplan_not_set', + 'tcversion_id' => 'tcversion_id', + 'platform_id' => 'platform_id_not_set' + ); + + foreach ($keySet as $key => $labelID) { + $op['status_ok'] = isset($_GET[$key]); + if (! $op['status_ok']) { + $op['msg'] = lang_get($labelID); + break; + } + } + } + } + + $args = init_args($dbHandler); + if ($op['status_ok']) { + // Set Environment + $tplan_mgr = new testplan($dbHandler); + $info = $tplan_mgr->get_by_id($args->tplan_id, + array( + 'output' => 'minimun' + )); + + if (is_null($info)) { + die('ltx - tplan info does not exist'); + } + + $tproject_mgr = new testproject($dbHandler); + $tproject_mgr->setSessionProject($info['tproject_id']); + $op['status_ok'] = true; + } + + if ($op['status_ok']) { + // Build name of function to call for doing the job. + $pfn = 'process_' . $args->item; + + $ctx = array(); + $ctx['setting_testplan'] = $args->tplan_id; + $ctx['setting_build'] = $args->build_id; + $ctx['setting_platform'] = $args->platform_id; + $ctx['tcversion_id'] = $args->tcversion_id; + $ctx['tcase_id'] = 0; + $ctx['user_id'] = $args->user_id; + + $jump_to = $pfn($dbHandler, $ctx); + $op['status_ok'] = ! is_null($jump_to['url']); + $op['msg'] = $jump_to['msg']; + } + + if ($op['status_ok']) { + $treeframe = $itemCode[$args->item] . '?loadExecDashboard=0' . + '&setting_testplan=' . $args->tplan_id . '&setting_build=' . + $args->build_id . '&setting_platform=' . $args->platform_id; + + $tplMgr->assign('title', lang_get('main_page_title')); + $tplMgr->assign('treewidth', TL_FRMWORKAREA_LEFT_FRAME_WIDTH); + $tplMgr->assign('workframe', $jump_to['url']); + $tplMgr->assign('treeframe', $treeframe); + $tplMgr->display('frmInner.tpl'); + } else { + echo $op['msg']; + ob_end_flush(); + exit(); + } +} + +// function end + +/** + * xta2m: eXecution Tasks Assigned TO Me + */ +function launch_inner_xta2m(&$dbHandler, &$tplMgr) +{ + $args = init_args($dbHandler); + + $jt = $_SESSION['basehref'] . '/lib/testcases/' . + 'tcAssignedToUser.php?user_id=' . $args->target_user_id . + + $k2c = array( + 'tplan_id', + 'build_id' + ); + foreach ($k2c as $tg) { + if (property_exists($args, $tg) && $args->$tg > 0) { + $jt .= "&$tg=" . $args->$tg; + } + } + + $tplMgr->assign('workframe', $jt); + $tplMgr->display('workframe.tpl'); +} + +/** + */ +function launch_outer_exec(&$tplMgr, $argsObj) +{ + $gui = new stdClass(); + $gui->titleframe = 'lib/general/navBar.php?caller=linkto'; + $gui->navbar_height = config_get('navbar_height'); + + if ($argsObj->tproject_id > 0) { + $gui->titleframe .= '&testproject=' . $argsObj->tproject_id; + } + $gui->title = lang_get('main_page_title'); + $gui->mainframe = 'ltx.php?' . build_link_exec($argsObj); + + $tplMgr->assign('gui', $gui); + $tplMgr->display('main.tpl'); +} + +/** + */ +function launch_outer_xta2m(&$tplMgr, $argsObj) +{ + $gui = new stdClass(); + $gui->titleframe = 'lib/general/navBar.php?caller=linkto'; + $gui->navbar_height = config_get('navbar_height'); + + if ($argsObj->tproject_id > 0) { + $gui->titleframe .= '&testproject=' . $argsObj->tproject_id; + } + $gui->title = lang_get('main_page_title'); + $gui->mainframe = 'ltx.php?item=xta2m&load=1' . '&user_id=' . + $argsObj->target_user_id . '&tplan_id=' . $argsObj->tplan_id; + + $tplMgr->assign('gui', $gui); + $tplMgr->display('main.tpl'); +} + +/** + */ +function buildCookie(&$dbHandler, $itemID, $tprojectID, $cookiePrefix) +{ + $tree_mgr = new tree($dbHandler); + $path = $tree_mgr->get_path($itemID); + $parents = array(); + $parents[] = $tprojectID; + foreach ($path as $node) { + $parents[] = $node['id']; + } + array_pop($parents); + $cookieInfo['path'] = 'a:s%3A/' . implode("/", $parents); + $cookieInfo['value'] = $cookiePrefix . $tprojectID . '_ext-comp-1001'; + return $cookieInfo; } diff --git a/microsoftoauth.php b/microsoftoauth.php deleted file mode 100644 index 9ef85a506f..0000000000 --- a/microsoftoauth.php +++ /dev/null @@ -1,325 +0,0 @@ - we will redirect to login screen with some message -doBlockingChecks($db,$gui); - -switch($args->action) -{ - case 'oauth': - //If code is empty then break - if (!isset($_GET['code'])){ - renderLoginScreen($gui); - die(); - } - - //Switch between oauth providers - if (!include_once('lib/functions/oauth_providers/microsoft.php')) { - die("Oauth client doesn't exist"); - } - - $oau = config_get('OAuthServers'); - foreach ($oau as $oprov) { - if (strcmp($oprov['oauth_name'],'microsoft') == 0){ - $oauth_params = $oprov; - break; - } - } - - $user_token = oauth_get_token($oauth_params, $_GET['code']); - if($user_token->status['status'] == tl::OK) { - doSessionStart(true); - $op = doAuthorize($db,$user_token->options->user,'oauth',$user_token->options); - $doAuthPostProcess = true; - } else { - $gui->note = $user_token->status['msg']; - $gui->draw=true; - renderLoginScreen($gui); - die(); - } - break; - -} - -if( $doAuthPostProcess ) -{ - list($doRenderLoginScreen,$gui->note) = authorizePostProcessing($args,$op); - if ($doRenderLoginScreen) - { - $gui->draw = true; - } -} - -if( $doRenderLoginScreen ) -{ - renderLoginScreen($gui); -} - -/** - * - * - */ -function init_args() -{ - $pwdInputLen = config_get('loginPagePasswordMaxLenght'); - - // 2010904 - eloff - Why is req and reqURI parameters to the login? - $iParams = array("note" => array(tlInputParameter::STRING_N,0,255), - "tl_login" => array(tlInputParameter::STRING_N,0,100), - "tl_password" => array(tlInputParameter::STRING_N,0,$pwdInputLen), - "req" => array(tlInputParameter::STRING_N,0,4000), - "reqURI" => array(tlInputParameter::STRING_N,0,4000), - "action" => array(tlInputParameter::STRING_N,0, 10), - "destination" => array(tlInputParameter::STRING_N, 0, 255), - "loginform_token" => array(tlInputParameter::STRING_N, 0, 255), - "viewer" => array(tlInputParameter::STRING_N, 0, 3), - "oauth" => array(tlInputParameter::STRING_N,0,100), - ); - $pParams = R_PARAMS($iParams); - - $args = new stdClass(); - $args->note = $pParams['note']; - $args->login = $pParams['tl_login']; - - $args->pwd = $pParams['tl_password']; - $args->ssodisable = getSSODisable(); - $args->reqURI = urlencode($pParams['req']); - $args->preqURI = urlencode($pParams['reqURI']); - $args->destination = urldecode($pParams['destination']); - $args->loginform_token = urldecode($pParams['loginform_token']); - - $args->viewer = $pParams['viewer']; - - $args->action = 'oauth'; - - return $args; -} - -/** - * - * - */ -function init_gui(&$db,$args) -{ - $gui = new stdClass(); - $gui->viewer = $args->viewer; - - $secCfg = config_get('config_check_warning_frequence'); - $gui->securityNotes = ''; - if( (strcmp($secCfg, 'ALWAYS') == 0) || - (strcmp($secCfg, 'ONCE_FOR_SESSION') == 0 && !isset($_SESSION['getSecurityNotesDone'])) ) - { - $_SESSION['getSecurityNotesDone'] = 1; - $gui->securityNotes = getSecurityNotes($db); - } - - $gui->authCfg = config_get('authentication'); - $gui->user_self_signup = config_get('user_self_signup'); - - // Oauth buttons - $oau = config_get('OAuthServers'); - $gui->oauth = array(); - foreach ($oau as $oauth_prov) { - if ($oauth_prov['oauth_enabled']) { - $name = $oauth_prov['oauth_name']; - $gui->oauth[$name] = new stdClass(); - $gui->oauth[$name]->name = ucfirst($name); - $gui->oauth[$name]->link = oauth_link($oauth_prov); - $gui->oauth[$name]->icon = $name . '.png'; - } - } - $gui->external_password_mgmt = false; - $domain = $gui->authCfg['domain']; - $mm = $gui->authCfg['method']; - if( isset($domain[$mm]) ) { - $ac = $domain[$mm]; - $gui->external_password_mgmt = !$ac['allowPasswordManagement']; - } - - $gui->login_disabled = (('LDAP' == $gui->authCfg['method']) && !checkForLDAPExtension()) ? 1 : 0; - - switch($args->note) { - case 'expired': - if(!isset($_SESSION)) { - session_start(); - } - session_unset(); - session_destroy(); - $gui->note = lang_get('session_expired'); - $gui->reqURI = null; - break; - - case 'first': - $gui->note = lang_get('your_first_login'); - $gui->reqURI = null; - break; - - case 'lost': - $gui->note = lang_get('passwd_lost'); - $gui->reqURI = null; - break; - - default: - $gui->note = ''; - break; - } - - $gui->ssodisable = 0; - if(property_exists($args,'ssodisable')) { - $gui->ssodisable = $args->ssodisable; - } - - $gui->reqURI = $args->reqURI ? $args->reqURI : $args->preqURI; - $gui->destination = $args->destination; - $gui->pwdInputMaxLenght = config_get('loginPagePasswordMaxLenght'); - - return $gui; -} - - -/** - * doBlockingChecks - * - * wrong Schema version will BLOCK ANY login action - * - * @param &$dbHandler DataBase Handler - * @param &$guiObj some gui elements that will be used to give feedback - * - */ -function doBlockingChecks(&$dbHandler,&$guiObj) -{ - $op = checkSchemaVersion($dbHandler); - if( $op['status'] < tl::OK ) - { - // Houston we have a problem - // This check to kill session was added to avoid following situation - // TestLink 1.9.5 installed - // Install TestLink 1.9.6 in another folder, pointing to same OLD DB - // you logged in TL 1.9.5 => session is created - // you try to login to 1.9.6, you get the Update DB Schema message but - // anyway because a LIVE AND VALID session you are allowed to login => BAD - if(isset($op['kill_session']) && $op['kill_session']) - { - session_unset(); - session_destroy(); - } - - $guiObj->draw = false; - $guiObj->note = $op['msg']; - renderLoginScreen($guiObj); - die(); - } -} - - -/** - * renderLoginScreen - * simple piece of code used to clean up code layout - * - * @global $g_tlLogger - * @param stdClassObject $guiObj - */ -function renderLoginScreen($guiObj) -{ - global $g_tlLogger; - $templateCfg = templateConfiguration(); - $logPeriodToDelete = config_get('removeEventsOlderThan'); - $g_tlLogger->deleteEventsFor(null, strtotime("-{$logPeriodToDelete} days UTC")); - - $smarty = new TLSmarty(); - $smarty->assign('gui', $guiObj); - - $tpl = str_replace('.php','.tpl',basename($_SERVER['SCRIPT_NAME'])); - $tpl = 'login-model-marcobiedermann.tpl'; - $smarty->display($tpl); -} - - -/** - * - * @param stdClassObject $argsObj - * @param hash $op - */ -function authorizePostProcessing($argsObj,$op) -{ - $note = null; - $renderLoginScreen = false; - if($op['status'] == tl::OK) - { - // Login successful, redirect to destination - logAuditEvent(TLS("audit_login_succeeded",$argsObj->login, - $_SERVER['REMOTE_ADDR']),"LOGIN",$_SESSION['currentUser']->dbID,"users"); - - if ($argsObj->action == 'ajaxlogin') - { - echo json_encode(array('success' => true)); - } - else - { - // If destination param is set redirect to given page ... - if (!empty($argsObj->destination) && preg_match("/linkto.php/", $argsObj->destination)) - { - redirect($argsObj->destination); - } - else - { - // ... or show main page - $_SESSION['viewer'] = $argsObj->viewer; - $ad = $argsObj->ssodisable ? '&ssodisable=1' : ''; - $ad .= ($argsObj->preqURI ? "&reqURI=".urlencode($argsObj->preqURI) :""); - - $rul = $_SESSION['basehref'] . - "index.php?caller=login&viewer={$argsObj->viewer}" . $ad; - - redirect($rul); - } - exit(); // hmm seems is useless - } - } - else - { - $note = ''; - if(!$argsObj->ssodisable) - { - $note = is_null($op['msg']) ? lang_get('bad_user_passwd') : $op['msg']; - } - - if($argsObj->action == 'ajaxlogin') - { - echo json_encode(array('success' => false,'reason' => $note)); - } - else - { - $renderLoginScreen = true; - } - } - - return array($renderLoginScreen,$note); -} - diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 0000000000..4f34416d7f --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,16 @@ +{ + "systemParams": "darwin-x64-72", + "modulesFolders": [ + "node_modules" + ], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [ + "tablednd@^1.0.3" + ], + "lockfileEntries": { + "tablednd@^1.0.3": "https://registry.yarnpkg.com/tablednd/-/tablednd-1.0.3.tgz#0c6992bc7fe3ec5e880344684479c9df90c0442b" + }, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/node_modules/tablednd/.hound.yml b/node_modules/tablednd/.hound.yml new file mode 100644 index 0000000000..9c56266719 --- /dev/null +++ b/node_modules/tablednd/.hound.yml @@ -0,0 +1,3 @@ +jshint: + config_file: .jshintrc + ignore_file: .jshintignore diff --git a/node_modules/tablednd/.jshintignore b/node_modules/tablednd/.jshintignore new file mode 100644 index 0000000000..00df06f324 --- /dev/null +++ b/node_modules/tablednd/.jshintignore @@ -0,0 +1,6 @@ +Gruntfile.js +node_modules/**/*.js +**/*.min.js +**/*.rc1.js + + diff --git a/node_modules/tablednd/.jshintrc b/node_modules/tablednd/.jshintrc new file mode 100644 index 0000000000..be03f9c952 --- /dev/null +++ b/node_modules/tablednd/.jshintrc @@ -0,0 +1,11 @@ +{ + "laxbreak": true, + "expr": true, + + "globals": { + "jQuery": true, + "console": true, + "module": true, + "document": true + } +} diff --git a/node_modules/tablednd/.npmignore b/node_modules/tablednd/.npmignore new file mode 100644 index 0000000000..8c53f03933 --- /dev/null +++ b/node_modules/tablednd/.npmignore @@ -0,0 +1,53 @@ + + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + + +node_modules \ No newline at end of file diff --git a/third_party/codeplex-phpexcel-1.7.6/PHPExcel/Shared/PDF/fonts/.noencode b/node_modules/tablednd/.scannerwork/.sonar_lock similarity index 100% rename from third_party/codeplex-phpexcel-1.7.6/PHPExcel/Shared/PDF/fonts/.noencode rename to node_modules/tablednd/.scannerwork/.sonar_lock diff --git a/node_modules/tablednd/.scannerwork/report-task.txt b/node_modules/tablednd/.scannerwork/report-task.txt new file mode 100644 index 0000000000..728c4b2a66 --- /dev/null +++ b/node_modules/tablednd/.scannerwork/report-task.txt @@ -0,0 +1,7 @@ +organization=isocra +projectKey=TableDnD +serverUrl=https://sonarcloud.io +serverVersion=7.0.0.35052 +dashboardUrl=https://sonarcloud.io/dashboard/index/TableDnD +ceTaskId=AWCoUo8vzhfPH6zNRdlG +ceTaskUrl=https://sonarcloud.io/api/ce/task?id=AWCoUo8vzhfPH6zNRdlG diff --git a/node_modules/tablednd/Gruntfile.js b/node_modules/tablednd/Gruntfile.js new file mode 100644 index 0000000000..ad310a6c97 --- /dev/null +++ b/node_modules/tablednd/Gruntfile.js @@ -0,0 +1,37 @@ +module.exports = function(grunt) { + + grunt.initConfig({ + pkg: grunt.file.readJSON('bower.json'), + uglify: { + options: { + banner: '/*! jquery.tablednd.js <%= grunt.template.today("dd-mm-yyyy") %> */\n' + }, + dist: { + files: { + 'dist/jquery.tablednd.min.js': ['js/jquery.tablednd.js'] + } + } + }, + jshint: { + all: { + options: { + reporterOutput: "", + jshintrc: true + }, + src: 'js/*.js' + } + } + }); + + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + + grunt.registerTask('copyToDist', function() { + grunt.file.copy('js/jquery.tablednd.js', 'dist/jquery.tablednd.js'); + }); + + grunt.registerTask('test', ['jshint']); + + grunt.registerTask('default', ['jshint', 'copyToDist', 'uglify']); + +}; diff --git a/node_modules/tablednd/MIT-LICENSE.txt b/node_modules/tablednd/MIT-LICENSE.txt new file mode 100644 index 0000000000..fb40abd8e6 --- /dev/null +++ b/node_modules/tablednd/MIT-LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) Denis Howlett +Copyright 2012 Nick Lombard - nickl- and other contributors +https://github.com/isocra/TableDnD + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/tablednd/README.md b/node_modules/tablednd/README.md new file mode 100644 index 0000000000..43a6b30bb2 --- /dev/null +++ b/node_modules/tablednd/README.md @@ -0,0 +1,59 @@ +# TableDnD +[![npm version](https://badge.fury.io/js/tablednd.svg)](https://badge.fury.io/js/tablednd) +[![CDNJS version](https://img.shields.io/cdnjs/v/TableDnD.svg)](https://cdnjs.com/libraries/TableDnD) +[![jsDelivr Hits](https://data.jsdelivr.com/v1/package/gh/isocra/TableDnD/badge?style=rounded)](https://www.jsdelivr.com/package/gh/isocra/TableDnD) + +## Installation + +TableDnD is easy to install: +``` +npm install --save tablednd +``` +or +``` +yarn add tablednd +``` +or +``` +bower install https://github.com/isocra/TableDnD.git +``` +Alternatively you can simply reference from CDNJS: +```html + +``` +or +```html + +``` +You'll also need to include [jQuery](https://jquery.com) before you include this plugin (so that jQuery is defined). + +--- + +## Getting Started + +Let's create a simple table. The HTML for the table is very straight forward (no Javascript, pure HTML, we haven't added `thead` or `tbody` elements, but it works fine with these too): + +```html + + + + + + + +
      1Onesome text
      2Twosome text
      3Threesome text
      4Foursome text
      5Fivesome text
      6Sixsome text
      +``` +To add in the "draggability" all we need to do is add a line to the `$(document).ready(...)` function as follows: +```html + +``` +Basically we get the table element and call `tableDnD`. If you try this, you'll see that the rows are now draggable. + +In the example above we're not setting any parameters at all so we get the default settings. There are a number of parameters you can set in order to control the look and feel of the table and also to add custom behaviour on drag or on drop. The parameters are specified as a map in the usual way and are described the [full documentation](http://isocra.github.io/TableDnD): + +You can also play and experiment with TableDnD using this [jsFiddle](http://jsfiddle.net/DenisHo/dxpLrcd9/embedded/result/). Here you get the documentation, plus live examples. diff --git a/node_modules/tablednd/__tests__/test.jquery.tablednd.js b/node_modules/tablednd/__tests__/test.jquery.tablednd.js new file mode 100644 index 0000000000..e90a6fe092 --- /dev/null +++ b/node_modules/tablednd/__tests__/test.jquery.tablednd.js @@ -0,0 +1,30 @@ +const $ = require('jquery') +window.jQuery = $; +const tableDnD = require('../js/jquery.tablednd'); + +beforeEach(function() { + document.body.innerHTML = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; +}); + +test('Creates a TableDnD table', function() { + var $table = $('#table1'); + var table = $table.tableDnD(); + expect(table).not.toBeUndefined(); +}); + +test('Simulate drag and drop', function() { + var $table = $('#table1'); + var table = $table.tableDnD(); + var $row = $('#row1'); + TestUtils.Simulate.dragStart($row, {dataTransfer: null}); + +}); diff --git a/node_modules/tablednd/bower.json b/node_modules/tablednd/bower.json new file mode 100644 index 0000000000..3b8db3187e --- /dev/null +++ b/node_modules/tablednd/bower.json @@ -0,0 +1,14 @@ +{ + "name": "TableDnD", + "version": "0.9.1", + "main": [ + "./js/jquery.tablednd.js" + ], + "ignore": [ + "LICENCE", "README.md" + ], + "dependencies": { + "jquery": ">= 1.7.0" + }, + "homepage": "https://github.com/isocra/TableDnD" +} \ No newline at end of file diff --git a/node_modules/tablednd/dist/jquery.tablednd.0.9.2.js b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.js new file mode 100644 index 0000000000..27a1be5e98 --- /dev/null +++ b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.js @@ -0,0 +1,675 @@ +/** + * TableDnD plug-in for JQuery, allows you to drag and drop table rows + * You can set up various options to control how the system will work + * Copyright (c) Denis Howlett + * Licensed like jQuery, see http://docs.jquery.com/License. + * + * Configuration options: + * + * onDragStyle + * This is the style that is assigned to the row during drag. There are limitations to the styles that can be + * associated with a row (such as you can't assign a border--well you can, but it won't be + * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as + * a map (as used in the jQuery css(...) function). + * onDropStyle + * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations + * to what you can do. Also this replaces the original style, so again consider using onDragClass which + * is simply added and then removed on drop. + * onDragClass + * This class is added for the duration of the drag and then removed when the row is dropped. It is more + * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default + * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your + * stylesheet. + * onDrop + * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table + * and the row that was dropped. You can work out the new order of the rows by using + * table.rows. + * onDragStart + * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the + * table and the row which the user has started to drag. + * onDragStop + * Pass a function that will be called when the user stops dragging regardless of if the rows have been + * rearranged. The function takes 2 parameters: the table and the row which the user was dragging. + * onAllowDrop + * Pass a function that will be called as a row is over another row. If the function returns true, allow + * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under + * the cursor. It returns a boolean: true allows the drop, false doesn't allow it. + * scrollAmount + * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the + * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, + * FF3 beta + * dragHandle + * This is a jQuery mach string for one or more cells in each row that is draggable. If you + * specify this, then you are responsible for setting cursor: move in the CSS and only these cells + * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where + * the whole row is draggable. + * + * Other ways to control behaviour: + * + * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows + * that you don't want to be draggable. + * + * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form + * []=&[]= so that you can send this back to the server. The table must have + * an ID as must all the rows. + * + * Other methods: + * + * $("...").tableDnDUpdate() + * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells). + * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again. + * The table maintains the original configuration (so you don't have to specify it again). + * + * $("...").tableDnDSerialize() + * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be + * called from anywhere and isn't dependent on the currentTable being set up correctly before calling + * + * Known problems: + * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0 + * + * Version 0.2: 2008-02-20 First public version + * Version 0.3: 2008-02-07 Added onDragStart option + * Made the scroll amount configurable (default is 5 as before) + * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes + * Added onAllowDrop to control dropping + * Fixed a bug which meant that you couldn't set the scroll amount in both directions + * Added serialize method + * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row + * draggable + * Improved the serialize method to use a default (and settable) regular expression. + * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table + * Version 0.6: 2011-12-02 Added support for touch devices + * Version 0.7 2012-04-09 Now works with jQuery 1.7 and supports touch, tidied up tabs and spaces + */ +!function ($, window, document, undefined) { +// Determine if this is a touch device +var hasTouch = 'ontouchstart' in document.documentElement, + startEvent = 'touchstart mousedown', + moveEvent = 'touchmove mousemove', + endEvent = 'touchend mouseup'; + +$(document).ready(function () { + function parseStyle(css) { + var objMap = {}, + parts = css.match(/([^;:]+)/g) || []; + while (parts.length) + objMap[parts.shift()] = parts.shift().trim(); + + return objMap; + } + $('table').each(function () { + if ($(this).data('table') == 'dnd') { + + $(this).tableDnD({ + onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null, + onDropStyle: $(this).data('ondropstyle') && parseStyle($(this).data('ondropstyle')) || null, + onDragClass: $(this).data('ondragclass') == undefined && "tDnD_whileDrag" || $(this).data('ondragclass'), + onDrop: $(this).data('ondrop') && new Function('table', 'row', $(this).data('ondrop')), // 'return eval("'+$(this).data('ondrop')+'");') || null, + onDragStart: $(this).data('ondragstart') && new Function('table', 'row' ,$(this).data('ondragstart')), // 'return eval("'+$(this).data('ondragstart')+'");') || null, + onDragStop: $(this).data('ondragstop') && new Function('table', 'row' ,$(this).data('ondragstop')), + scrollAmount: $(this).data('scrollamount') || 5, + sensitivity: $(this).data('sensitivity') || 10, + hierarchyLevel: $(this).data('hierarchylevel') || 0, + indentArtifact: $(this).data('indentartifact') || '
       
      ', + autoWidthAdjust: $(this).data('autowidthadjust') || true, + autoCleanRelations: $(this).data('autocleanrelations') || true, + jsonPretifySeparator: $(this).data('jsonpretifyseparator') || '\t', + serializeRegexp: $(this).data('serializeregexp') && new RegExp($(this).data('serializeregexp')) || /[^\-]*$/, + serializeParamName: $(this).data('serializeparamname') || false, + dragHandle: $(this).data('draghandle') || null + }); + } + + + }); +}); + +jQuery.tableDnD = { + /** Keep hold of the current table being dragged */ + currentTable: null, + /** Keep hold of the current drag object if any */ + dragObject: null, + /** The current mouse offset */ + mouseOffset: null, + /** Remember the old value of X and Y so that we don't do too much processing */ + oldX: 0, + oldY: 0, + + /** Actually build the structure */ + build: function(options) { + // Set up the defaults if any + + this.each(function() { + // This is bound to each matching table, set up the defaults and override with user options + this.tableDnDConfig = $.extend({ + onDragStyle: null, + onDropStyle: null, + // Add in the default class for whileDragging + onDragClass: "tDnD_whileDrag", + onDrop: null, + onDragStart: null, + onDragStop: null, + scrollAmount: 5, + /** Sensitivity setting will throttle the trigger rate for movement detection */ + sensitivity: 10, + /** Hierarchy level to support parent child. 0 switches this functionality off */ + hierarchyLevel: 0, + /** The html artifact to prepend the first cell with as indentation */ + indentArtifact: '
       
      ', + /** Automatically adjust width of first cell */ + autoWidthAdjust: true, + /** Automatic clean-up to ensure relationship integrity */ + autoCleanRelations: true, + /** Specify a number (4) as number of spaces or any indent string for JSON.stringify */ + jsonPretifySeparator: '\t', + /** The regular expression to use to trim row IDs */ + serializeRegexp: /[^\-]*$/, + /** If you want to specify another parameter name instead of the table ID */ + serializeParamName: false, + /** If you give the name of a class here, then only Cells with this class will be draggable */ + dragHandle: null + }, options || {}); + + // Now make the rows draggable + $.tableDnD.makeDraggable(this); + // Prepare hierarchy support + this.tableDnDConfig.hierarchyLevel + && $.tableDnD.makeIndented(this); + }); + + // Don't break the chain + return this; + }, + makeIndented: function (table) { + var config = table.tableDnDConfig, + rows = table.rows, + firstCell = $(rows).first().find('td:first')[0], + indentLevel = 0, + cellWidth = 0, + longestCell, + tableStyle; + + if ($(table).hasClass('indtd')) + return null; + + tableStyle = $(table).addClass('indtd').attr('style'); + $(table).css({whiteSpace: "nowrap"}); + + for (var w = 0; w < rows.length; w++) { + if (cellWidth < $(rows[w]).find('td:first').text().length) { + cellWidth = $(rows[w]).find('td:first').text().length; + longestCell = w; + } + } + $(firstCell).css({width: 'auto'}); + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').prepend(config.indentArtifact); + firstCell && $(firstCell).css({width: firstCell.offsetWidth}); + tableStyle && $(table).css(tableStyle); + + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').children(':first').remove(); + + config.hierarchyLevel + && $(rows).each(function () { + indentLevel = $(this).data('level') || 0; + indentLevel <= config.hierarchyLevel + && $(this).data('level', indentLevel) + || $(this).data('level', 0); + for (var i = 0; i < $(this).data('level'); i++) + $(this).find('td:first').prepend(config.indentArtifact); + }); + + return this; + }, + /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ + makeDraggable: function(table) { + var config = table.tableDnDConfig; + + config.dragHandle + // We only need to add the event to the specified cells + && $(config.dragHandle, table).each(function() { + // The cell is bound to "this" + $(this).bind(startEvent, function(e) { + $.tableDnD.initialiseDrag($(this).parents('tr')[0], table, this, e, config); + return false; + }); + }) + // For backwards compatibility, we add the event to the whole row + // get all the rows as a wrapped set + || $(table.rows).each(function() { + // Iterate through each row, the row is bound to "this" + if (! $(this).hasClass("nodrag")) { + $(this).bind(startEvent, function(e) { + if (e.target.tagName == "TD") { + $.tableDnD.initialiseDrag(this, table, this, e, config); + return false; + } + }).css("cursor", "move"); // Store the tableDnD object + } else { + $(this).css("cursor", ""); // Remove the cursor if we don't have the nodrag class + } + }); + }, + currentOrder: function() { + var rows = this.currentTable.rows; + return $.map(rows, function (val) { + return ($(val).data('level') + val.id).replace(/\s/g, ''); + }).join(''); + }, + initialiseDrag: function(dragObject, table, target, e, config) { + this.dragObject = dragObject; + this.currentTable = table; + this.mouseOffset = this.getMouseOffset(target, e); + this.originalOrder = this.currentOrder(); + + // Now we need to capture the mouse up and mouse move event + // We can use bind so that we don't interfere with other event handlers + $(document) + .bind(moveEvent, this.mousemove) + .bind(endEvent, this.mouseup); + + // Call the onDragStart method if there is one + config.onDragStart + && config.onDragStart(table, target); + }, + updateTables: function() { + this.each(function() { + // this is now bound to each matching table + if (this.tableDnDConfig) + $.tableDnD.makeDraggable(this); + }); + }, + /** Get the mouse coordinates from the event (allowing for browser differences) */ + mouseCoords: function(e) { + if (e.originalEvent.changedTouches) + return { + x: e.originalEvent.changedTouches[0].clientX, + y: e.originalEvent.changedTouches[0].clientY + }; + + if(e.pageX || e.pageY) + return { + x: e.pageX, + y: e.pageY + }; + + return { + x: e.clientX + document.body.scrollLeft - document.body.clientLeft, + y: e.clientY + document.body.scrollTop - document.body.clientTop + }; + }, + /** Given a target element and a mouse eent, get the mouse offset from that element. + To do this we need the element's position and the mouse position */ + getMouseOffset: function(target, e) { + var mousePos, + docPos; + + e = e || window.event; + + docPos = this.getPosition(target); + mousePos = this.mouseCoords(e); + + return { + x: mousePos.x - docPos.x, + y: mousePos.y - docPos.y + }; + }, + /** Get the position of an element by going up the DOM tree and adding up all the offsets */ + getPosition: function(element) { + var left = 0, + top = 0; + + // Safari fix -- thanks to Luis Chato for this! + // Safari 2 doesn't correctly grab the offsetTop of a table row + // this is detailed here: + // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ + // the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. + // note that firefox will return a text node as a first child, so designing a more thorough + // solution may need to take that into account, for now this seems to work in firefox, safari, ie + if (element.offsetHeight == 0) + element = element.firstChild; // a table cell + + while (element.offsetParent) { + left += element.offsetLeft; + top += element.offsetTop; + element = element.offsetParent; + } + + left += element.offsetLeft; + top += element.offsetTop; + + return { + x: left, + y: top + }; + }, + autoScroll: function (mousePos) { + var config = this.currentTable.tableDnDConfig, + yOffset = window.pageYOffset, + windowHeight = window.innerHeight + ? window.innerHeight + : document.documentElement.clientHeight + ? document.documentElement.clientHeight + : document.body.clientHeight; + + // Windows version + // yOffset=document.body.scrollTop; + if (document.all) + if (typeof document.compatMode != 'undefined' + && document.compatMode != 'BackCompat') + yOffset = document.documentElement.scrollTop; + else if (typeof document.body != 'undefined') + yOffset = document.body.scrollTop; + + mousePos.y - yOffset < config.scrollAmount + && window.scrollBy(0, - config.scrollAmount) + || windowHeight - (mousePos.y - yOffset) < config.scrollAmount + && window.scrollBy(0, config.scrollAmount); + + }, + moveVerticle: function (moving, currentRow) { + + if (0 != moving.vertical + // If we're over a row then move the dragged row to there so that the user sees the + // effect dynamically + && currentRow + && this.dragObject != currentRow + && this.dragObject.parentNode == currentRow.parentNode) + 0 > moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow.nextSibling) + || 0 < moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow); + + }, + moveHorizontal: function (moving, currentRow) { + var config = this.currentTable.tableDnDConfig, + currentLevel; + + if (!config.hierarchyLevel + || 0 == moving.horizontal + // We only care if moving left or right on the current row + || !currentRow + || this.dragObject != currentRow) + return null; + + currentLevel = $(currentRow).data('level'); + + 0 < moving.horizontal + && currentLevel > 0 + && $(currentRow).find('td:first').children(':first').remove() + && $(currentRow).data('level', --currentLevel); + + 0 > moving.horizontal + && currentLevel < config.hierarchyLevel + && $(currentRow).prev().data('level') >= currentLevel + && $(currentRow).children(':first').prepend(config.indentArtifact) + && $(currentRow).data('level', ++currentLevel); + + }, + mousemove: function(e) { + var dragObj = $($.tableDnD.dragObject), + config = $.tableDnD.currentTable.tableDnDConfig, + currentRow, + mousePos, + moving, + x, + y; + + e && e.preventDefault(); + + if (!$.tableDnD.dragObject) + return false; + + // prevent touch device screen scrolling + e.type == 'touchmove' + && event.preventDefault(); // TODO verify this is event and not really e + + // update the style to show we're dragging + config.onDragClass + && dragObj.addClass(config.onDragClass) + || dragObj.css(config.onDragStyle); + + mousePos = $.tableDnD.mouseCoords(e); + x = mousePos.x - $.tableDnD.mouseOffset.x; + y = mousePos.y - $.tableDnD.mouseOffset.y; + + // auto scroll the window + $.tableDnD.autoScroll(mousePos); + + currentRow = $.tableDnD.findDropTargetRow(dragObj, y); + moving = $.tableDnD.findDragDirection(x, y); + + $.tableDnD.moveVerticle(moving, currentRow); + $.tableDnD.moveHorizontal(moving, currentRow); + + return false; + }, + findDragDirection: function (x,y) { + var sensitivity = this.currentTable.tableDnDConfig.sensitivity, + oldX = this.oldX, + oldY = this.oldY, + xMin = oldX - sensitivity, + xMax = oldX + sensitivity, + yMin = oldY - sensitivity, + yMax = oldY + sensitivity, + moving = { + horizontal: x >= xMin && x <= xMax ? 0 : x > oldX ? -1 : 1, + vertical : y >= yMin && y <= yMax ? 0 : y > oldY ? -1 : 1 + }; + + // update the old value + if (moving.horizontal != 0) + this.oldX = x; + if (moving.vertical != 0) + this.oldY = y; + + return moving; + }, + /** We're only worried about the y position really, because we can only move rows up and down */ + findDropTargetRow: function(draggedRow, y) { + var rowHeight = 0, + rows = this.currentTable.rows, + config = this.currentTable.tableDnDConfig, + rowY = 0, + row = null; + + for (var i = 0; i < rows.length; i++) { + row = rows[i]; + rowY = this.getPosition(row).y; + rowHeight = parseInt(row.offsetHeight) / 2; + if (row.offsetHeight == 0) { + rowY = this.getPosition(row.firstChild).y; + rowHeight = parseInt(row.firstChild.offsetHeight) / 2; + } + // Because we always have to insert before, we need to offset the height a bit + if (y > (rowY - rowHeight) && y < (rowY + rowHeight)) + // that's the row we're over + // If it's the same as the current row, ignore it + if (draggedRow.is(row) + || (config.onAllowDrop + && !config.onAllowDrop(draggedRow, row)) + // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) + || $(row).hasClass("nodrop")) + return null; + else + return row; + } + return null; + }, + processMouseup: function() { + if (!this.currentTable || !this.dragObject) + return null; + + var config = this.currentTable.tableDnDConfig, + droppedRow = this.dragObject, + parentLevel = 0, + myLevel = 0; + + // Unbind the event handlers + $(document) + .unbind(moveEvent, this.mousemove) + .unbind(endEvent, this.mouseup); + + config.hierarchyLevel + && config.autoCleanRelations + && $(this.currentTable.rows).first().find('td:first').children().each(function () { + myLevel = $(this).parents('tr:first').data('level'); + myLevel + && $(this).parents('tr:first').data('level', --myLevel) + && $(this).remove(); + }) + && config.hierarchyLevel > 1 + && $(this.currentTable.rows).each(function () { + myLevel = $(this).data('level'); + if (myLevel > 1) { + parentLevel = $(this).prev().data('level'); + while (myLevel > parentLevel + 1) { + $(this).find('td:first').children(':first').remove(); + $(this).data('level', --myLevel); + } + } + }); + + // If we have a dragObject, then we need to release it, + // The row will already have been moved to the right place so we just reset stuff + config.onDragClass + && $(droppedRow).removeClass(config.onDragClass) + || $(droppedRow).css(config.onDropStyle); + + this.dragObject = null; + // Call the onDrop method if there is one + config.onDrop + && this.originalOrder != this.currentOrder() + && $(droppedRow).hide().fadeIn('fast') + && config.onDrop(this.currentTable, droppedRow); + + // Call the onDragStop method if there is one + config.onDragStop + && config.onDragStop(this.currentTable, droppedRow); + + this.currentTable = null; // let go of the table too + }, + mouseup: function(e) { + e && e.preventDefault(); + $.tableDnD.processMouseup(); + return false; + }, + jsonize: function(pretify) { + var table = this.currentTable; + if (pretify) + return JSON.stringify( + this.tableData(table), + null, + table.tableDnDConfig.jsonPretifySeparator + ); + return JSON.stringify(this.tableData(table)); + }, + serialize: function() { + return $.param(this.tableData(this.currentTable)); + }, + serializeTable: function(table) { + var result = ""; + var paramName = table.tableDnDConfig.serializeParamName || table.id; + var rows = table.rows; + for (var i=0; i 0) result += "&"; + var rowId = rows[i].id; + if (rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { + rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; + result += paramName + '[]=' + rowId; + } + } + return result; + }, + serializeTables: function() { + var result = []; + $('table').each(function() { + this.id && result.push($.param($.tableDnD.tableData(this))); + }); + return result.join('&'); + }, + tableData: function (table) { + var config = table.tableDnDConfig, + previousIDs = [], + currentLevel = 0, + indentLevel = 0, + rowID = null, + data = {}, + getSerializeRegexp, + paramName, + currentID, + rows; + + if (!table) + table = this.currentTable; + if (!table || !table.rows || !table.rows.length) + return {error: { code: 500, message: "Not a valid table."}}; + if (!table.id && !config.serializeParamName) + return {error: { code: 500, message: "No serializable unique id provided."}}; + + rows = config.autoCleanRelations + && table.rows + || $.makeArray(table.rows); + paramName = config.serializeParamName || table.id; + currentID = paramName; + + getSerializeRegexp = function (rowId) { + if (rowId && config && config.serializeRegexp) + return rowId.match(config.serializeRegexp)[0]; + return rowId; + }; + + data[currentID] = []; + !config.autoCleanRelations + && $(rows[0]).data('level') + && rows.unshift({id: 'undefined'}); + + + + for (var i=0; i < rows.length; i++) { + if (config.hierarchyLevel) { + indentLevel = $(rows[i]).data('level') || 0; + if (indentLevel == 0) { + currentID = paramName; + previousIDs = []; + } + else if (indentLevel > currentLevel) { + previousIDs.push([currentID, currentLevel]); + currentID = getSerializeRegexp(rows[i-1].id); + } + else if (indentLevel < currentLevel) { + for (var h = 0; h < previousIDs.length; h++) { + if (previousIDs[h][1] == indentLevel) + currentID = previousIDs[h][0]; + if (previousIDs[h][1] >= currentLevel) + previousIDs[h][1] = 0; + } + } + currentLevel = indentLevel; + + if (!$.isArray(data[currentID])) + data[currentID] = []; + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + else { + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + } + return data; + } +}; + +jQuery.fn.extend( + { + tableDnD : $.tableDnD.build, + tableDnDUpdate : $.tableDnD.updateTables, + tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD), + tableDnDSerializeAll : $.tableDnD.serializeTables, + tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD) + } +); + +}(jQuery, window, window.document); diff --git a/node_modules/tablednd/dist/jquery.tablednd.0.9.2.min.js b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.min.js new file mode 100644 index 0000000000..d6adb5ce1d --- /dev/null +++ b/node_modules/tablednd/dist/jquery.tablednd.0.9.2.min.js @@ -0,0 +1,2 @@ +/*! jquery.tablednd.js 27-12-2017 */ +!function(a,b,c,d){var e=("ontouchstart"in c.documentElement,"touchstart mousedown"),f="touchmove mousemove",g="touchend mouseup";a(c).ready(function(){function b(a){for(var b={},c=a.match(/([^;:]+)/g)||[];c.length;)b[c.shift()]=c.shift().trim();return b}a("table").each(function(){"dnd"==a(this).data("table")&&a(this).tableDnD({onDragStyle:a(this).data("ondragstyle")&&b(a(this).data("ondragstyle"))||null,onDropStyle:a(this).data("ondropstyle")&&b(a(this).data("ondropstyle"))||null,onDragClass:a(this).data("ondragclass")==d&&"tDnD_whileDrag"||a(this).data("ondragclass"),onDrop:a(this).data("ondrop")&&new Function("table","row",a(this).data("ondrop")),onDragStart:a(this).data("ondragstart")&&new Function("table","row",a(this).data("ondragstart")),onDragStop:a(this).data("ondragstop")&&new Function("table","row",a(this).data("ondragstop")),scrollAmount:a(this).data("scrollamount")||5,sensitivity:a(this).data("sensitivity")||10,hierarchyLevel:a(this).data("hierarchylevel")||0,indentArtifact:a(this).data("indentartifact")||'
       
      ',autoWidthAdjust:a(this).data("autowidthadjust")||!0,autoCleanRelations:a(this).data("autocleanrelations")||!0,jsonPretifySeparator:a(this).data("jsonpretifyseparator")||" ",serializeRegexp:a(this).data("serializeregexp")&&new RegExp(a(this).data("serializeregexp"))||/[^\-]*$/,serializeParamName:a(this).data("serializeparamname")||!1,dragHandle:a(this).data("draghandle")||null})})}),jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldX:0,oldY:0,build:function(b){return this.each(function(){this.tableDnDConfig=a.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,onDragStop:null,scrollAmount:5,sensitivity:10,hierarchyLevel:0,indentArtifact:'
       
      ',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:" ",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},b||{}),a.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&a.tableDnD.makeIndented(this)}),this},makeIndented:function(b){var c,d,e=b.tableDnDConfig,f=b.rows,g=a(f).first().find("td:first")[0],h=0,i=0;if(a(b).hasClass("indtd"))return null;d=a(b).addClass("indtd").attr("style"),a(b).css({whiteSpace:"nowrap"});for(var j=0;ja.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,b.nextSibling)||00&&a(c).find("td:first").children(":first").remove()&&a(c).data("level",--d),void(0>b.horizontal&&d=d&&a(c).children(":first").prepend(e.indentArtifact)&&a(c).data("level",++d))):null},mousemove:function(b){var c,d,e,f,g,h=a(a.tableDnD.dragObject),i=a.tableDnD.currentTable.tableDnDConfig;return b&&b.preventDefault(),a.tableDnD.dragObject?("touchmove"==b.type&&event.preventDefault(),i.onDragClass&&h.addClass(i.onDragClass)||h.css(i.onDragStyle),d=a.tableDnD.mouseCoords(b),f=d.x-a.tableDnD.mouseOffset.x,g=d.y-a.tableDnD.mouseOffset.y,a.tableDnD.autoScroll(d),c=a.tableDnD.findDropTargetRow(h,g),e=a.tableDnD.findDragDirection(f,g),a.tableDnD.moveVerticle(e,c),a.tableDnD.moveHorizontal(e,c),!1):!1},findDragDirection:function(a,b){var c=this.currentTable.tableDnDConfig.sensitivity,d=this.oldX,e=this.oldY,f=d-c,g=d+c,h=e-c,i=e+c,j={horizontal:a>=f&&g>=a?0:a>d?-1:1,vertical:b>=h&&i>=b?0:b>e?-1:1};return 0!=j.horizontal&&(this.oldX=a),0!=j.vertical&&(this.oldY=b),j},findDropTargetRow:function(b,c){for(var d=0,e=this.currentTable.rows,f=this.currentTable.tableDnDConfig,g=0,h=null,i=0;ig-d&&g+d>c)return b.is(h)||f.onAllowDrop&&!f.onAllowDrop(b,h)||a(h).hasClass("nodrop")?null:h;return null},processMouseup:function(){if(!this.currentTable||!this.dragObject)return null;var b=this.currentTable.tableDnDConfig,d=this.dragObject,e=0,h=0;a(c).unbind(f,this.mousemove).unbind(g,this.mouseup),b.hierarchyLevel&&b.autoCleanRelations&&a(this.currentTable.rows).first().find("td:first").children().each(function(){h=a(this).parents("tr:first").data("level"),h&&a(this).parents("tr:first").data("level",--h)&&a(this).remove()})&&b.hierarchyLevel>1&&a(this.currentTable.rows).each(function(){if(h=a(this).data("level"),h>1)for(e=a(this).prev().data("level");h>e+1;)a(this).find("td:first").children(":first").remove(),a(this).data("level",--h)}),b.onDragClass&&a(d).removeClass(b.onDragClass)||a(d).css(b.onDropStyle),this.dragObject=null,b.onDrop&&this.originalOrder!=this.currentOrder()&&a(d).hide().fadeIn("fast")&&b.onDrop(this.currentTable,d),b.onDragStop&&b.onDragStop(this.currentTable,d),this.currentTable=null},mouseup:function(b){return b&&b.preventDefault(),a.tableDnD.processMouseup(),!1},jsonize:function(a){var b=this.currentTable;return a?JSON.stringify(this.tableData(b),null,b.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(b))},serialize:function(){return a.param(this.tableData(this.currentTable))},serializeTable:function(a){for(var b="",c=a.tableDnDConfig.serializeParamName||a.id,d=a.rows,e=0;e0&&(b+="&");var f=d[e].id;f&&a.tableDnDConfig&&a.tableDnDConfig.serializeRegexp&&(f=f.match(a.tableDnDConfig.serializeRegexp)[0],b+=c+"[]="+f)}return b},serializeTables:function(){var b=[];return a("table").each(function(){this.id&&b.push(a.param(a.tableDnD.tableData(this)))}),b.join("&")},tableData:function(b){var c,d,e,f,g=b.tableDnDConfig,h=[],i=0,j=0,k=null,l={};if(b||(b=this.currentTable),!b||!b.rows||!b.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!b.id&&!g.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};f=g.autoCleanRelations&&b.rows||a.makeArray(b.rows),d=g.serializeParamName||b.id,e=d,c=function(a){return a&&g&&g.serializeRegexp?a.match(g.serializeRegexp)[0]:a},l[e]=[],!g.autoCleanRelations&&a(f[0]).data("level")&&f.unshift({id:"undefined"});for(var m=0;mi)h.push([e,i]),e=c(f[m-1].id);else if(i>j)for(var n=0;n=i&&(h[n][1]=0);i=j,a.isArray(l[e])||(l[e]=[]),k=c(f[m].id),k&&l[e].push(k)}else k=c(f[m].id),k&&l[e].push(k);return l}},jQuery.fn.extend({tableDnD:a.tableDnD.build,tableDnDUpdate:a.tableDnD.updateTables,tableDnDSerialize:a.proxy(a.tableDnD.serialize,a.tableDnD),tableDnDSerializeAll:a.tableDnD.serializeTables,tableDnDData:a.proxy(a.tableDnD.tableData,a.tableDnD)})}(jQuery,window,window.document); \ No newline at end of file diff --git a/node_modules/tablednd/dist/jquery.tablednd.js b/node_modules/tablednd/dist/jquery.tablednd.js new file mode 100644 index 0000000000..d91f17532c --- /dev/null +++ b/node_modules/tablednd/dist/jquery.tablednd.js @@ -0,0 +1,601 @@ +/** + * TableDnD plug-in for JQuery, allows you to drag and drop table rows + * You can set up various options to control how the system will work + * Copyright (c) Denis Howlett + * License: MIT. + * See https://github.com/isocra/TableDnD + */ + +/*jshint -W054 */ + +!function ($, window, document, undefined) { + +var startEvent = 'touchstart mousedown', + moveEvent = 'touchmove mousemove', + endEvent = 'touchend mouseup'; + +$(document).ready(function () { + function parseStyle(css) { + var objMap = {}, + parts = css.match(/([^;:]+)/g) || []; + while (parts.length) + objMap[parts.shift()] = parts.shift().trim(); + + return objMap; + } + $('table').each(function () { + if ($(this).data('table') === 'dnd') { + + $(this).tableDnD({ + onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null, + onDropStyle: $(this).data('ondropstyle') && parseStyle($(this).data('ondropstyle')) || null, + onDragClass: $(this).data('ondragclass') === undefined && "tDnD_whileDrag" || $(this).data('ondragclass'), + onDrop: $(this).data('ondrop') && new Function('table', 'row', $(this).data('ondrop')), // 'return eval("'+$(this).data('ondrop')+'");') || null, + onDragStart: $(this).data('ondragstart') && new Function('table', 'row' ,$(this).data('ondragstart')), // 'return eval("'+$(this).data('ondragstart')+'");') || null, + onDragStop: $(this).data('ondragstop') && new Function('table', 'row' ,$(this).data('ondragstop')), + scrollAmount: $(this).data('scrollamount') || 5, + sensitivity: $(this).data('sensitivity') || 10, + hierarchyLevel: $(this).data('hierarchylevel') || 0, + indentArtifact: $(this).data('indentartifact') || '
       
      ', + autoWidthAdjust: $(this).data('autowidthadjust') || true, + autoCleanRelations: $(this).data('autocleanrelations') || true, + jsonPretifySeparator: $(this).data('jsonpretifyseparator') || '\t', + serializeRegexp: $(this).data('serializeregexp') && new RegExp($(this).data('serializeregexp')) || /[^\-]*$/, + serializeParamName: $(this).data('serializeparamname') || false, + dragHandle: $(this).data('draghandle') || null + }); + } + + + }); +}); + +jQuery.tableDnD = { + /** Keep hold of the current table being dragged */ + currentTable: null, + /** Keep hold of the current drag object if any */ + dragObject: null, + /** The current mouse offset */ + mouseOffset: null, + /** Remember the old value of X and Y so that we don't do too much processing */ + oldX: 0, + oldY: 0, + + /** Actually build the structure */ + build: function(options) { + // Set up the defaults if any + + this.each(function() { + // This is bound to each matching table, set up the defaults and override with user options + this.tableDnDConfig = $.extend({ + onDragStyle: null, + onDropStyle: null, + // Add in the default class for whileDragging + onDragClass: "tDnD_whileDrag", + onDrop: null, + onDragStart: null, + onDragStop: null, + scrollAmount: 5, + /** Sensitivity setting will throttle the trigger rate for movement detection */ + sensitivity: 10, + /** Hierarchy level to support parent child. 0 switches this functionality off */ + hierarchyLevel: 0, + /** The html artifact to prepend the first cell with as indentation */ + indentArtifact: '
       
      ', + /** Automatically adjust width of first cell */ + autoWidthAdjust: true, + /** Automatic clean-up to ensure relationship integrity */ + autoCleanRelations: true, + /** Specify a number (4) as number of spaces or any indent string for JSON.stringify */ + jsonPretifySeparator: '\t', + /** The regular expression to use to trim row IDs */ + serializeRegexp: /[^\-]*$/, + /** If you want to specify another parameter name instead of the table ID */ + serializeParamName: false, + /** If you give the name of a class here, then only Cells with this class will be draggable */ + dragHandle: null + }, options || {}); + + // Now make the rows draggable + $.tableDnD.makeDraggable(this); + // Prepare hierarchy support + this.tableDnDConfig.hierarchyLevel + && $.tableDnD.makeIndented(this); + }); + + // Don't break the chain + return this; + }, + makeIndented: function (table) { + var config = table.tableDnDConfig, + rows = table.rows, + firstCell = $(rows).first().find('td:first')[0], + indentLevel = 0, + cellWidth = 0, + longestCell, + tableStyle; + + if ($(table).hasClass('indtd')) + return null; + + tableStyle = $(table).addClass('indtd').attr('style'); + $(table).css({whiteSpace: "nowrap"}); + + for (var w = 0; w < rows.length; w++) { + if (cellWidth < $(rows[w]).find('td:first').text().length) { + cellWidth = $(rows[w]).find('td:first').text().length; + longestCell = w; + } + } + $(firstCell).css({width: 'auto'}); + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').prepend(config.indentArtifact); + firstCell && $(firstCell).css({width: firstCell.offsetWidth}); + tableStyle && $(table).css(tableStyle); + + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').children(':first').remove(); + + config.hierarchyLevel + && $(rows).each(function () { + indentLevel = $(this).data('level') || 0; + indentLevel <= config.hierarchyLevel + && $(this).data('level', indentLevel) + || $(this).data('level', 0); + for (var i = 0; i < $(this).data('level'); i++) + $(this).find('td:first').prepend(config.indentArtifact); + }); + + return this; + }, + /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ + makeDraggable: function(table) { + var config = table.tableDnDConfig; + + config.dragHandle + // We only need to add the event to the specified cells + && $(config.dragHandle, table).each(function() { + // The cell is bound to "this" + $(this).bind(startEvent, function(e) { + $.tableDnD.initialiseDrag($(this).parents('tr')[0], table, this, e, config); + return false; + }); + }) + // For backwards compatibility, we add the event to the whole row + // get all the rows as a wrapped set + || $(table.rows).each(function() { + // Iterate through each row, the row is bound to "this" + if (! $(this).hasClass("nodrag")) { + $(this).bind(startEvent, function(e) { + if (e.target.tagName === "TD") { + $.tableDnD.initialiseDrag(this, table, this, e, config); + return false; + } + }).css("cursor", "move"); // Store the tableDnD object + } else { + $(this).css("cursor", ""); // Remove the cursor if we don't have the nodrag class + } + }); + }, + currentOrder: function() { + var rows = this.currentTable.rows; + return $.map(rows, function (val) { + return ($(val).data('level') + val.id).replace(/\s/g, ''); + }).join(''); + }, + initialiseDrag: function(dragObject, table, target, e, config) { + this.dragObject = dragObject; + this.currentTable = table; + this.mouseOffset = this.getMouseOffset(target, e); + this.originalOrder = this.currentOrder(); + + // Now we need to capture the mouse up and mouse move event + // We can use bind so that we don't interfere with other event handlers + $(document) + .bind(moveEvent, this.mousemove) + .bind(endEvent, this.mouseup); + + // Call the onDragStart method if there is one + config.onDragStart + && config.onDragStart(table, target); + }, + updateTables: function() { + this.each(function() { + // this is now bound to each matching table + if (this.tableDnDConfig) + $.tableDnD.makeDraggable(this); + }); + }, + /** Get the mouse coordinates from the event (allowing for browser differences) */ + mouseCoords: function(e) { + if (e.originalEvent.changedTouches) + return { + x: e.originalEvent.changedTouches[0].clientX, + y: e.originalEvent.changedTouches[0].clientY + }; + + if(e.pageX || e.pageY) + return { + x: e.pageX, + y: e.pageY + }; + + return { + x: e.clientX + document.body.scrollLeft - document.body.clientLeft, + y: e.clientY + document.body.scrollTop - document.body.clientTop + }; + }, + /** Given a target element and a mouse eent, get the mouse offset from that element. + To do this we need the element's position and the mouse position */ + getMouseOffset: function(target, e) { + var mousePos, + docPos; + + e = e || window.event; + + docPos = this.getPosition(target); + mousePos = this.mouseCoords(e); + + return { + x: mousePos.x - docPos.x, + y: mousePos.y - docPos.y + }; + }, + /** Get the position of an element by going up the DOM tree and adding up all the offsets */ + getPosition: function(element) { + var left = 0, + top = 0; + + // Safari fix -- thanks to Luis Chato for this! + // Safari 2 doesn't correctly grab the offsetTop of a table row + // this is detailed here: + // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ + // the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. + // note that firefox will return a text node as a first child, so designing a more thorough + // solution may need to take that into account, for now this seems to work in firefox, safari, ie + if (element.offsetHeight === 0) + element = element.firstChild; // a table cell + + while (element.offsetParent) { + left += element.offsetLeft; + top += element.offsetTop; + element = element.offsetParent; + } + + left += element.offsetLeft; + top += element.offsetTop; + + return { + x: left, + y: top + }; + }, + autoScroll: function (mousePos) { + var config = this.currentTable.tableDnDConfig, + yOffset = window.pageYOffset, + windowHeight = window.innerHeight + ? window.innerHeight + : document.documentElement.clientHeight + ? document.documentElement.clientHeight + : document.body.clientHeight; + + // Windows version + // yOffset=document.body.scrollTop; + if (document.all) + if (typeof document.compatMode !== 'undefined' + && document.compatMode !== 'BackCompat') + yOffset = document.documentElement.scrollTop; + else if (typeof document.body !== 'undefined') + yOffset = document.body.scrollTop; + + mousePos.y - yOffset < config.scrollAmount + && window.scrollBy(0, - config.scrollAmount) + || windowHeight - (mousePos.y - yOffset) < config.scrollAmount + && window.scrollBy(0, config.scrollAmount); + + }, + moveVerticle: function (moving, currentRow) { + + if (0 !== moving.vertical + // If we're over a row then move the dragged row to there so that the user sees the + // effect dynamically + && currentRow + && this.dragObject !== currentRow + && this.dragObject.parentNode === currentRow.parentNode) + 0 > moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow.nextSibling) + || 0 < moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow); + + }, + moveHorizontal: function (moving, currentRow) { + var config = this.currentTable.tableDnDConfig, + currentLevel; + + if (!config.hierarchyLevel + || 0 === moving.horizontal + // We only care if moving left or right on the current row + || !currentRow + || this.dragObject !== currentRow) + return null; + + currentLevel = $(currentRow).data('level'); + + 0 < moving.horizontal + && currentLevel > 0 + && $(currentRow).find('td:first').children(':first').remove() + && $(currentRow).data('level', --currentLevel); + + 0 > moving.horizontal + && currentLevel < config.hierarchyLevel + && $(currentRow).prev().data('level') >= currentLevel + && $(currentRow).children(':first').prepend(config.indentArtifact) + && $(currentRow).data('level', ++currentLevel); + + }, + mousemove: function(e) { + var dragObj = $($.tableDnD.dragObject), + config = $.tableDnD.currentTable.tableDnDConfig, + currentRow, + mousePos, + moving, + x, + y; + + e && e.preventDefault(); + + if (!$.tableDnD.dragObject) + return false; + + // prevent touch device screen scrolling + e.type === 'touchmove' + && event.preventDefault(); // TODO verify this is event and not really e + + // update the style to show we're dragging + config.onDragClass + && dragObj.addClass(config.onDragClass) + || dragObj.css(config.onDragStyle); + + mousePos = $.tableDnD.mouseCoords(e); + x = mousePos.x - $.tableDnD.mouseOffset.x; + y = mousePos.y - $.tableDnD.mouseOffset.y; + + // auto scroll the window + $.tableDnD.autoScroll(mousePos); + + currentRow = $.tableDnD.findDropTargetRow(dragObj, y); + moving = $.tableDnD.findDragDirection(x, y); + + $.tableDnD.moveVerticle(moving, currentRow); + $.tableDnD.moveHorizontal(moving, currentRow); + + return false; + }, + findDragDirection: function (x,y) { + var sensitivity = this.currentTable.tableDnDConfig.sensitivity, + oldX = this.oldX, + oldY = this.oldY, + xMin = oldX - sensitivity, + xMax = oldX + sensitivity, + yMin = oldY - sensitivity, + yMax = oldY + sensitivity, + moving = { + horizontal: x >= xMin && x <= xMax ? 0 : x > oldX ? -1 : 1, + vertical : y >= yMin && y <= yMax ? 0 : y > oldY ? -1 : 1 + }; + + // update the old value + if (moving.horizontal !== 0) + this.oldX = x; + if (moving.vertical !== 0) + this.oldY = y; + + return moving; + }, + /** We're only worried about the y position really, because we can only move rows up and down */ + findDropTargetRow: function(draggedRow, y) { + var rowHeight = 0, + rows = this.currentTable.rows, + config = this.currentTable.tableDnDConfig, + rowY = 0, + row = null; + + for (var i = 0; i < rows.length; i++) { + row = rows[i]; + rowY = this.getPosition(row).y; + rowHeight = parseInt(row.offsetHeight) / 2; + if (row.offsetHeight === 0) { + rowY = this.getPosition(row.firstChild).y; + rowHeight = parseInt(row.firstChild.offsetHeight) / 2; + } + // Because we always have to insert before, we need to offset the height a bit + if (y > (rowY - rowHeight) && y < (rowY + rowHeight)) + // that's the row we're over + // If it's the same as the current row, ignore it + if (draggedRow.is(row) + || (config.onAllowDrop + && !config.onAllowDrop(draggedRow, row)) + // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) + || $(row).hasClass("nodrop")) + return null; + else + return row; + } + return null; + }, + processMouseup: function() { + if (!this.currentTable || !this.dragObject) + return null; + + var config = this.currentTable.tableDnDConfig, + droppedRow = this.dragObject, + parentLevel = 0, + myLevel = 0; + + // Unbind the event handlers + $(document) + .unbind(moveEvent, this.mousemove) + .unbind(endEvent, this.mouseup); + + config.hierarchyLevel + && config.autoCleanRelations + && $(this.currentTable.rows).first().find('td:first').children().each(function () { + myLevel = $(this).parents('tr:first').data('level'); + myLevel + && $(this).parents('tr:first').data('level', --myLevel) + && $(this).remove(); + }) + && config.hierarchyLevel > 1 + && $(this.currentTable.rows).each(function () { + myLevel = $(this).data('level'); + if (myLevel > 1) { + parentLevel = $(this).prev().data('level'); + while (myLevel > parentLevel + 1) { + $(this).find('td:first').children(':first').remove(); + $(this).data('level', --myLevel); + } + } + }); + + // If we have a dragObject, then we need to release it, + // The row will already have been moved to the right place so we just reset stuff + config.onDragClass + && $(droppedRow).removeClass(config.onDragClass) + || $(droppedRow).css(config.onDropStyle); + + this.dragObject = null; + // Call the onDrop method if there is one + config.onDrop + && this.originalOrder !== this.currentOrder() + && $(droppedRow).hide().fadeIn('fast') + && config.onDrop(this.currentTable, droppedRow); + + // Call the onDragStop method if there is one + config.onDragStop + && config.onDragStop(this.currentTable, droppedRow); + + this.currentTable = null; // let go of the table too + }, + mouseup: function(e) { + e && e.preventDefault(); + $.tableDnD.processMouseup(); + return false; + }, + jsonize: function(pretify) { + var table = this.currentTable; + if (pretify) + return JSON.stringify( + this.tableData(table), + null, + table.tableDnDConfig.jsonPretifySeparator + ); + return JSON.stringify(this.tableData(table)); + }, + serialize: function() { + return $.param(this.tableData(this.currentTable)); + }, + serializeTable: function(table) { + var result = ""; + var paramName = table.tableDnDConfig.serializeParamName || table.id; + var rows = table.rows; + for (var i=0; i 0) result += "&"; + var rowId = rows[i].id; + if (rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { + rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; + result += paramName + '[]=' + rowId; + } + } + return result; + }, + serializeTables: function() { + var result = []; + $('table').each(function() { + this.id && result.push($.param($.tableDnD.tableData(this))); + }); + return result.join('&'); + }, + tableData: function (table) { + var config = table.tableDnDConfig, + previousIDs = [], + currentLevel = 0, + indentLevel = 0, + rowID = null, + data = {}, + getSerializeRegexp, + paramName, + currentID, + rows; + + if (!table) + table = this.currentTable; + if (!table || !table.rows || !table.rows.length) + return {error: { code: 500, message: "Not a valid table."}}; + if (!table.id && !config.serializeParamName) + return {error: { code: 500, message: "No serializable unique id provided."}}; + + rows = config.autoCleanRelations + && table.rows + || $.makeArray(table.rows); + paramName = config.serializeParamName || table.id; + currentID = paramName; + + getSerializeRegexp = function (rowId) { + if (rowId && config && config.serializeRegexp) + return rowId.match(config.serializeRegexp)[0]; + return rowId; + }; + + data[currentID] = []; + !config.autoCleanRelations + && $(rows[0]).data('level') + && rows.unshift({id: 'undefined'}); + + + + for (var i=0; i < rows.length; i++) { + if (config.hierarchyLevel) { + indentLevel = $(rows[i]).data('level') || 0; + if (indentLevel === 0) { + currentID = paramName; + previousIDs = []; + } + else if (indentLevel > currentLevel) { + previousIDs.push([currentID, currentLevel]); + currentID = getSerializeRegexp(rows[i-1].id); + } + else if (indentLevel < currentLevel) { + for (var h = 0; h < previousIDs.length; h++) { + if (previousIDs[h][1] === indentLevel) + currentID = previousIDs[h][0]; + if (previousIDs[h][1] >= currentLevel) + previousIDs[h][1] = 0; + } + } + currentLevel = indentLevel; + + if (!$.isArray(data[currentID])) + data[currentID] = []; + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + else { + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + } + return data; + } +}; + +jQuery.fn.extend( + { + tableDnD : $.tableDnD.build, + tableDnDUpdate : $.tableDnD.updateTables, + tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD), + tableDnDSerializeAll : $.tableDnD.serializeTables, + tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD) + } +); + +}(jQuery, window, window.document); diff --git a/node_modules/tablednd/dist/jquery.tablednd.min.js b/node_modules/tablednd/dist/jquery.tablednd.min.js new file mode 100644 index 0000000000..225739f4b3 --- /dev/null +++ b/node_modules/tablednd/dist/jquery.tablednd.min.js @@ -0,0 +1,2 @@ +/*! jquery.tablednd.js 30-12-2017 */ +!function(a,b,c,d){var e="touchstart mousedown",f="touchmove mousemove",g="touchend mouseup";a(c).ready(function(){function b(a){for(var b={},c=a.match(/([^;:]+)/g)||[];c.length;)b[c.shift()]=c.shift().trim();return b}a("table").each(function(){"dnd"===a(this).data("table")&&a(this).tableDnD({onDragStyle:a(this).data("ondragstyle")&&b(a(this).data("ondragstyle"))||null,onDropStyle:a(this).data("ondropstyle")&&b(a(this).data("ondropstyle"))||null,onDragClass:a(this).data("ondragclass")===d&&"tDnD_whileDrag"||a(this).data("ondragclass"),onDrop:a(this).data("ondrop")&&new Function("table","row",a(this).data("ondrop")),onDragStart:a(this).data("ondragstart")&&new Function("table","row",a(this).data("ondragstart")),onDragStop:a(this).data("ondragstop")&&new Function("table","row",a(this).data("ondragstop")),scrollAmount:a(this).data("scrollamount")||5,sensitivity:a(this).data("sensitivity")||10,hierarchyLevel:a(this).data("hierarchylevel")||0,indentArtifact:a(this).data("indentartifact")||'
       
      ',autoWidthAdjust:a(this).data("autowidthadjust")||!0,autoCleanRelations:a(this).data("autocleanrelations")||!0,jsonPretifySeparator:a(this).data("jsonpretifyseparator")||"\t",serializeRegexp:a(this).data("serializeregexp")&&new RegExp(a(this).data("serializeregexp"))||/[^\-]*$/,serializeParamName:a(this).data("serializeparamname")||!1,dragHandle:a(this).data("draghandle")||null})})}),jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldX:0,oldY:0,build:function(b){return this.each(function(){this.tableDnDConfig=a.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,onDragStop:null,scrollAmount:5,sensitivity:10,hierarchyLevel:0,indentArtifact:'
       
      ',autoWidthAdjust:!0,autoCleanRelations:!0,jsonPretifySeparator:"\t",serializeRegexp:/[^\-]*$/,serializeParamName:!1,dragHandle:null},b||{}),a.tableDnD.makeDraggable(this),this.tableDnDConfig.hierarchyLevel&&a.tableDnD.makeIndented(this)}),this},makeIndented:function(b){var c,d,e=b.tableDnDConfig,f=b.rows,g=a(f).first().find("td:first")[0],h=0,i=0;if(a(b).hasClass("indtd"))return null;d=a(b).addClass("indtd").attr("style"),a(b).css({whiteSpace:"nowrap"});for(var j=0;ja.vertical&&this.dragObject.parentNode.insertBefore(this.dragObject,b.nextSibling)||00&&a(c).find("td:first").children(":first").remove()&&a(c).data("level",--d),0>b.horizontal&&d=d&&a(c).children(":first").prepend(e.indentArtifact)&&a(c).data("level",++d)},mousemove:function(b){var c,d,e,f,g,h=a(a.tableDnD.dragObject),i=a.tableDnD.currentTable.tableDnDConfig;return b&&b.preventDefault(),!!a.tableDnD.dragObject&&("touchmove"===b.type&&event.preventDefault(),i.onDragClass&&h.addClass(i.onDragClass)||h.css(i.onDragStyle),d=a.tableDnD.mouseCoords(b),f=d.x-a.tableDnD.mouseOffset.x,g=d.y-a.tableDnD.mouseOffset.y,a.tableDnD.autoScroll(d),c=a.tableDnD.findDropTargetRow(h,g),e=a.tableDnD.findDragDirection(f,g),a.tableDnD.moveVerticle(e,c),a.tableDnD.moveHorizontal(e,c),!1)},findDragDirection:function(a,b){var c=this.currentTable.tableDnDConfig.sensitivity,d=this.oldX,e=this.oldY,f=d-c,g=d+c,h=e-c,i=e+c,j={horizontal:a>=f&&a<=g?0:a>d?-1:1,vertical:b>=h&&b<=i?0:b>e?-1:1};return 0!==j.horizontal&&(this.oldX=a),0!==j.vertical&&(this.oldY=b),j},findDropTargetRow:function(b,c){for(var d=0,e=this.currentTable.rows,f=this.currentTable.tableDnDConfig,g=0,h=null,i=0;ig-d&&c1&&a(this.currentTable.rows).each(function(){if((h=a(this).data("level"))>1)for(e=a(this).prev().data("level");h>e+1;)a(this).find("td:first").children(":first").remove(),a(this).data("level",--h)}),b.onDragClass&&a(d).removeClass(b.onDragClass)||a(d).css(b.onDropStyle),this.dragObject=null,b.onDrop&&this.originalOrder!==this.currentOrder()&&a(d).hide().fadeIn("fast")&&b.onDrop(this.currentTable,d),b.onDragStop&&b.onDragStop(this.currentTable,d),this.currentTable=null},mouseup:function(b){return b&&b.preventDefault(),a.tableDnD.processMouseup(),!1},jsonize:function(a){var b=this.currentTable;return a?JSON.stringify(this.tableData(b),null,b.tableDnDConfig.jsonPretifySeparator):JSON.stringify(this.tableData(b))},serialize:function(){return a.param(this.tableData(this.currentTable))},serializeTable:function(a){for(var b="",c=a.tableDnDConfig.serializeParamName||a.id,d=a.rows,e=0;e0&&(b+="&");var f=d[e].id;f&&a.tableDnDConfig&&a.tableDnDConfig.serializeRegexp&&(f=f.match(a.tableDnDConfig.serializeRegexp)[0],b+=c+"[]="+f)}return b},serializeTables:function(){var b=[];return a("table").each(function(){this.id&&b.push(a.param(a.tableDnD.tableData(this)))}),b.join("&")},tableData:function(b){var c,d,e,f,g=b.tableDnDConfig,h=[],i=0,j=0,k=null,l={};if(b||(b=this.currentTable),!b||!b.rows||!b.rows.length)return{error:{code:500,message:"Not a valid table."}};if(!b.id&&!g.serializeParamName)return{error:{code:500,message:"No serializable unique id provided."}};f=g.autoCleanRelations&&b.rows||a.makeArray(b.rows),d=g.serializeParamName||b.id,e=d,c=function(a){return a&&g&&g.serializeRegexp?a.match(g.serializeRegexp)[0]:a},l[e]=[],!g.autoCleanRelations&&a(f[0]).data("level")&&f.unshift({id:"undefined"});for(var m=0;mi)h.push([e,i]),e=c(f[m-1].id);else if(j=i&&(h[n][1]=0);i=j,a.isArray(l[e])||(l[e]=[]),k=c(f[m].id),k&&l[e].push(k)}else(k=c(f[m].id))&&l[e].push(k);return l}},jQuery.fn.extend({tableDnD:a.tableDnD.build,tableDnDUpdate:a.tableDnD.updateTables,tableDnDSerialize:a.proxy(a.tableDnD.serialize,a.tableDnD),tableDnDSerializeAll:a.tableDnD.serializeTables,tableDnDData:a.proxy(a.tableDnD.tableData,a.tableDnD)})}(jQuery,window,window.document); \ No newline at end of file diff --git a/node_modules/tablednd/images/updown2.gif b/node_modules/tablednd/images/updown2.gif new file mode 100644 index 0000000000..3cb4482d69 Binary files /dev/null and b/node_modules/tablednd/images/updown2.gif differ diff --git a/node_modules/tablednd/index.html b/node_modules/tablednd/index.html new file mode 100644 index 0000000000..d3c380b31b --- /dev/null +++ b/node_modules/tablednd/index.html @@ -0,0 +1,527 @@ + + + + Table Drag and Drop jQuery plugin + + + + +
      +

      Table Drag and Drop jQuery plugin

      +

      This page contains documentation and tests for the TableDnD jQuery plug-in. For more information and +to post comments, please go to isocra.com. +

      +

      If you have issues or bug reports, then you can post them at the TableDnD plug page.

      + +

      How do I use it?

      +
        +
      1. Since TableDnD is a jquery pligin you will need to include jquery in your page first. +

        No need for any downloads simply reference jQuery from the Google CDN + (Content Distribution Network) + All scripts are included at the bottom of the page, to facilitate quicker rendering of the HTML for more responsive pages. + The following is the way we are linking to jQuery in the examples and this method can be recommended for use in your implementations too.

        +
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
      2. +
      3. You will also need a copy of the TableDnD plugin (current version 0.9) which you can reference in + the normal fashion, anywhere after jQuery.
      4. +
      5. In true jQuery style, the typical way to initialise the tabes is in the $(document).ready code black function. + Use a selector to select your table and then call the following (You can optionally specify a set of properties (described below). +
        tableDnD()
        +
      6. +
      + +

      A basic table

      +
      +
      +
      Col1
      Row1
      Row2
      Row3
      + + + + + + +
      1Onesome text
      2Twosome text
      3Threesome text
      4Foursome text
      5Fivesome text
      6Sixsome text
      +

    +

    The HTML for the table is very straight forward (no Javascript, pure HTML):

    + +
    +<table id="table-1" cellspacing="0" cellpadding="2">
    +    <tr id="1"><td>1</td><td>One</td><td>some text</td></tr>
    +    <tr id="2"><td>2</td><td>Two</td><td>some text</td></tr>
    +    <tr id="3"><td>3</td><td>Three</td><td>some text</td></tr>
    +    <tr id="4"><td>4</td><td>Four</td><td>some text</td></tr>
    +    <tr id="5"><td>5</td><td>Five</td><td>some text</td></tr>
    +    <tr id="6"><td>6</td><td>Six</td><td>some text</td></tr>
    +</table>
    +
    +

    To add in the "draggability" all we need to do is add a line to the $(document).ready(...) function +as follows:

    +
    +<script type="text/javascript">
    +$(document).ready(function() {
    +    // Initialise the table
    +    $("#table-1").tableDnD();
    +});
    +</script>
    +
    +

    In the example above we're not setting any parameters at all so we get the default settings. There are a number + of parameters you can set in order to control the look and feel of the table and also to add custom behaviour + on drag or on drop. The parameters are specified as a map in the usual way and are described below:

    + +

    Settings

    +
    +
    onDragStyle
    +
    This is the style that is assigned to the row during drag. There are limitations to the styles that can be + associated with a row (such as you can't assign a border—well you can, but it won't be + displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as + a map (as used in the jQuery css(...) function).
    +
    onDropStyle
    +
    This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations + to what you can do. Also this replaces the original style, so again consider using onDragClass which + is simply added and then removed on drop.
    +
    onDragClass
    +
    This class is added for the duration of the drag and then removed when the row is dropped. It is more + flexible than using onDragStyle since it can be inherited by the row cells and other content. The default + is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your + stylesheet.
    +
    onDrop
    +
    Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table + and the row that was dropped. You can work out the new order of the rows by using + table.tBodies[0].rows.
    +
    onDragStart
    +
    Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the + table and the row which the user has started to drag.
    +
    scrollAmount
    +
    This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the + window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, + FF3 beta)
    +
    + +

    OnDrag custom table

    +

    This second table has has an onDrop function applied as well as an onDragClass. The javascript to set this up is +as follows:

    + +
    +$(document).ready(function() {
    +
    +	// Initialise the first table (as before)
    +	$("#table-1").tableDnD();
    +
    +	// Make a nice striped effect on the table
    +	$("#table-2 tr:even').addClass('alt')");
    +
    +	// Initialise the second table specifying a dragClass and an onDrop function that will display an alert
    +	$("#table-2").tableDnD({
    +	    onDragClass: "myDragClass",
    +	    onDrop: function(table, row) {
    +            var rows = table.tBodies[0].rows;
    +            var debugStr = "Row dropped was "+row.id+". New order: ";
    +            for (var i=0; i<rows.length; i++) {
    +                debugStr += rows[i].id+" ";
    +            }
    +	        $(table).parent().find('.result').text(debugStr);
    +	    },
    +		onDragStart: function(table, row) {
    +			$(table).parent().find('.result').text("Started dragging row "+row.id);
    +		}
    +	});
    +});
    +
    +
    + + + + + + + + + + + + + + + +
    1OneVCN
    2TwoVCN
    3ThreeVCN
    4FourVCN
    5FiveVCN
    6SixVCN
    7SevenVCN
    8EightVCN
    9NineVCN
    10TenVCN
    11ElevenVCN
    12TwelveVCN
    13ThirteenVCN
    14FourteenVCN
    +
     
    +
    +

    Communicating with the back-end

    +

    Generally once the user has dropped a row, you need to inform the server of the new order. To do this, we've + added a method called serialize(). It takes no parameters but knows the current table from the + context. The method returns a string of the form tableId[]=rowId1&tableId[]=rowId2&tableId[]=rowId3... + You can then use this as part of an Ajax load. +

    +

    + Since version 0.9, instead of manually creating the serialized data string we instead use jQuery's param method which has the added benefit of url encoding the data string as well. +

    +

    This third table demonstrates calling the serialize function inside onDrop (as shown below). It also + demonstrates the "nodrop" class on row 3 and "nodrag" class on row 5, so you can't pick up row 5 and + you can't drop any row on row 3 (but you can drag it).

    +
    +    $('#table-3').tableDnD({
    +        onDrop: function(table, row) {
    +            alert($.tableDnD.serialize());
    +        }
    +    });
    +
    +
    +
    +

    Ajax result

    +

    Drag and drop in this table to test out serialise and using JQuery.load()

    +
    + + + + + + + + +
    1One
    2Two
    3Three (Can't drop on this row)
    4Four (Can't drop on this row)
    5Five
    6Six (Can't drag this row)
    7Seven
    +
    +
    +

    Multiple tbody table

    +

    This table has multiple TBODYs. The functionality isn't quite working properly. You can only drag the rows inside their +own TBODY, you can't drag them outside it. Now this might or might not be what you want, but unfortunately if you then drop a row outside its TBODY you get a Javascript error because inserting after a sibling doesn't work. This will be fixed in the next version. The header rows all have the classes "nodrop" and "nodrag" so that they can't be dragged or dropped on.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    H1H2H3
    4.1One
    4.2Two
    4.3Three
    4.4Four
    4.5Five
    4.6Six
    H1H2H3
    5.1One
    5.2Two
    5.3Three
    5.4Four
    5.5Five
    5.6Six
    H1H2H3
    6.1One
    6.2Two
    6.3Three
    6.4Four
    6.5Five
    6.6Six
    +
    +

    Identify rows

    +

    +The following table demonstrates the use of the default regular expression. The rows have IDs of the +form table5-row-1, table5-row-2, etc., but the regular expression is /[^\-]*$/ (this is the same +as used in the NestedSortable plugin for consistency). +This removes everything before and including the last hyphen, so the serialised string just has 1, 2, 3 etc. +You can replace the regular expression by setting the serializeRegexp option, you can also just set it +to null to stop this behaviour. +

    +
    +    $('#table-5').tableDnD({
    +        onDrop: function(table, row) {
    +            alert($.tableDnD.serialize());
    +        },
    +        dragHandle: ".dragHandle"
    +    });
    +
    +
    + + + + + + + +
     1Onesome text
     2Twosome text
     3Threesome text
     4Foursome text
     5Fivesome text
     6Sixsome text
    +
    +
    +

    In fact you will notice that I have also set the dragHandle on this table. This has two effects: firstly only +the cell with the drag handle class is draggable and secondly it doesn't automatically add the cursor: move +style to the row (or the drag handle cell), so you are responsible for setting up the style as you see fit.

    +

    Here I've actually added an extra effect which adds a background image to the first cell in the row whenever +you enter it using the jQuery hover function as follows:

    +
    +    $("#table-5 tr").hover(function() {
    +          $(this.cells[0]).addClass('showDragHandle');
    +    }, function() {
    +          $(this.cells[0]).removeClass('showDragHandle');
    +    });
    +
    +

    This provides a better visualisation of what you can do to the row and where you need to go to drag it (I hope).

    + +

    Meta table (auto configure)

    + + +
    + + + + + + + + +
    Basic example with extra fancy
    row styles bot this trick really
    only works with single column
    because it looses the corumn
    width when displaying a table
    in block style unfortunately
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameStart DateEnd DateActions
    Present Sprints
    First round - in sprint2012-09-192012-09-27
    Future Sprints
    Second round2012-09-282012-10-04
    Third round2012-10-042012-10-11
    Fourth round2012-10-132012-10-20
    Release prep2012-10-202012-10-27
    Fifth run2012-10-272012-11-03
    Sixth run2012-11-032012-11-10
    Seventh run2012-11-102012-11-17
    Release 2 prep2012-11-172012-11-24
    Past Sprints
    Backlog creation - complete2012-09-012012-09-08
    No Sprints
    + Hide JSON
    +
    + +

    Hierarchy table

    +

    This table allows row order to be dragged horizontally and placed in a hierarchy under a parent row (since version 0.9). We also get a chance to look at the new jsonize method for JSON serialized form of the data.

    +

    In the onDrop event handler we pass the JSON as data to jquery through a HTTP POST ajax call to the server:

    +
    +    $.post("server/ajaxJSONTest.php", $.tableDnD.jsonize(), function (data) {
    +        $('#table-7-response').html('
    '+ data); + }); +
    +

    On the back-end we have a PHP example that simply retrieves the JSON POST data from the built in stream php://input, decodes the payload and proceeds to build the hierarchy through recursion.

    +

    To keep the data simple and also stay compatible with the http variable methods as mentioned previously the data structure is formed with separate collections. If a parent has children the children first level are listed and if any of the children have subsequent children an additional collection is created for the first level of these.

    +

    The following hierarchy for example would generate 3 collections:

    +
      +
    • 7.00
        +
      • 7.01
      • +
      • 7.02
        • 7.03
      • +
    • +
    • 7.04
    • +
    +

    In JSON the dataset looks like this:

    +Hide JSON
    +{
    +	"table-7": [
    +		"7.00",
    +		"7.04"
    +	],
    +	"7.00": [
    +		"7.01",
    +		"7.02"
    +	],
    +	"7.02": [
    +		"7.03"
    +	]
    +}
    +
    +

    We use the setting hierarchyLevel to indicate how many levels are supported, the example uses 4 levels deep. When populating the table you can use the the data-leve tag to indicate at which level the current row is represented at.

    + +
    +
    +
    +

    Ajax result

    +

    Drag and drop in this table to test out hierarcies and using JSON payload.

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    7.0 One
    7.1 Two
    7.2 Three
    7.3 Four
    7.4 Five
    7.5 Six
    7.6 Seven
    7.7 Eight
    7.8 Nine
    7.9 Ten
    7.0 One
    7.1 Two
    7.2 Three
    7.3 Four
    7.4 Five
    7.5 Six
    7.6 Seven
    7.7 Eight
    7.8 Nine
    7.9 Ten
    + Hide JSON
    +
    + + + + + + + + diff --git a/node_modules/tablednd/js/jquery.tablednd.0.6.min.js b/node_modules/tablednd/js/jquery.tablednd.0.6.min.js new file mode 100644 index 0000000000..7201855a3c --- /dev/null +++ b/node_modules/tablednd/js/jquery.tablednd.0.6.min.js @@ -0,0 +1 @@ +var hasTouch='ontouchstart'in document.documentElement,startEvent=hasTouch?'touchstart':'mousedown',moveEvent=hasTouch?'touchmove':'mousemove',endEvent=hasTouch?'touchend':'mouseup';jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldY:0,build:function(options){this.each(function(){this.tableDnDConfig=jQuery.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,scrollAmount:5,serializeRegexp:/[^\-]*$/,serializeParamName:null,dragHandle:null},options||{});jQuery.tableDnD.makeDraggable(this)});return this},makeDraggable:function(table){var config=table.tableDnDConfig;if(config.dragHandle){var cells=jQuery("td."+table.tableDnDConfig.dragHandle,table);cells.each(function(){jQuery(this).bind(startEvent,function(ev){jQuery.tableDnD.initialiseDrag(this.parentNode,table,this,ev,config);return false})})}else{var rows=jQuery("tr",table);rows.each(function(){var row=jQuery(this);if(!row.hasClass("nodrag")){row.bind(startEvent,function(ev){if(ev.target.tagName=="TD"){jQuery.tableDnD.initialiseDrag(this,table,this,ev,config);return false}}).css("cursor","move")}})}},initialiseDrag:function(dragObject,table,target,evnt,config){jQuery.tableDnD.dragObject=dragObject;jQuery.tableDnD.currentTable=table;jQuery.tableDnD.mouseOffset=jQuery.tableDnD.getMouseOffset(target,evnt);jQuery.tableDnD.originalOrder=jQuery.tableDnD.serialize();jQuery(document).bind(moveEvent,jQuery.tableDnD.mousemove).bind(endEvent,jQuery.tableDnD.mouseup);if(config.onDragStart){config.onDragStart(table,target)}},updateTables:function(){this.each(function(){if(this.tableDnDConfig){jQuery.tableDnD.makeDraggable(this)}})},mouseCoords:function(ev){if(ev.pageX||ev.pageY){return{x:ev.pageX,y:ev.pageY}}return{x:ev.clientX+document.body.scrollLeft-document.body.clientLeft,y:ev.clientY+document.body.scrollTop-document.body.clientTop}},getMouseOffset:function(target,ev){ev=ev||window.event;var docPos=this.getPosition(target);var mousePos=this.mouseCoords(ev);return{x:mousePos.x-docPos.x,y:mousePos.y-docPos.y}},getPosition:function(e){var left=0;var top=0;if(e.offsetHeight==0){e=e.firstChild}while(e.offsetParent){left+=e.offsetLeft;top+=e.offsetTop;e=e.offsetParent}left+=e.offsetLeft;top+=e.offsetTop;return{x:left,y:top}},mousemove:function(ev){if(jQuery.tableDnD.dragObject==null){return}if(ev.type=='touchmove'){event.preventDefault()}var dragObj=jQuery(jQuery.tableDnD.dragObject);var config=jQuery.tableDnD.currentTable.tableDnDConfig;var mousePos=jQuery.tableDnD.mouseCoords(ev);var y=mousePos.y-jQuery.tableDnD.mouseOffset.y;var yOffset=window.pageYOffset;if(document.all){if(typeof document.compatMode!='undefined'&&document.compatMode!='BackCompat'){yOffset=document.documentElement.scrollTop}else if(typeof document.body!='undefined'){yOffset=document.body.scrollTop}}if(mousePos.y-yOffsetjQuery.tableDnD.oldY;jQuery.tableDnD.oldY=y;if(config.onDragClass){dragObj.addClass(config.onDragClass)}else{dragObj.css(config.onDragStyle)}var currentRow=jQuery.tableDnD.findDropTargetRow(dragObj,y);if(currentRow){if(movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow.nextSibling)}else if(!movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow)}}}return false},findDropTargetRow:function(draggedRow,y){var rows=jQuery.tableDnD.currentTable.rows;for(var i=0;irowY-rowHeight)&&(y<(rowY+rowHeight))){if(row==draggedRow){return null}var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onAllowDrop){if(config.onAllowDrop(draggedRow,row)){return row}else{return null}}else{var nodrop=jQuery(row).hasClass("nodrop");if(!nodrop){return row}else{return null}}return row}}return null},mouseup:function(e){if(jQuery.tableDnD.currentTable&&jQuery.tableDnD.dragObject){jQuery(document).unbind(moveEvent,jQuery.tableDnD.mousemove).unbind(endEvent,jQuery.tableDnD.mouseup);var droppedRow=jQuery.tableDnD.dragObject;var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onDragClass){jQuery(droppedRow).removeClass(config.onDragClass)}else{jQuery(droppedRow).css(config.onDropStyle)}jQuery.tableDnD.dragObject=null;var newOrder=jQuery.tableDnD.serialize();if(config.onDrop&&(jQuery.tableDnD.originalOrder!=newOrder)){config.onDrop(jQuery.tableDnD.currentTable,droppedRow)}jQuery.tableDnD.currentTable=null}},serialize:function(){if(jQuery.tableDnD.currentTable){return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable)}else{return"Error: No Table id set, you need to set an id on your table and every row"}},serializeTable:function(table){var result="";var tableId=table.id;var rows=table.rows;for(var i=0;i0)result+="&";var rowId=rows[i].id;if(rowId&&rowId&&table.tableDnDConfig&&table.tableDnDConfig.serializeRegexp){rowId=rowId.match(table.tableDnDConfig.serializeRegexp)[0]}result+=tableId+'[]='+rowId}return result},serializeTables:function(){var result="";this.each(function(){result+=jQuery.tableDnD.serializeTable(this)});return result}};jQuery.fn.extend({tableDnD:jQuery.tableDnD.build,tableDnDUpdate:jQuery.tableDnD.updateTables,tableDnDSerialize:jQuery.tableDnD.serializeTables}); \ No newline at end of file diff --git a/node_modules/tablednd/js/jquery.tablednd.0.7.min.js b/node_modules/tablednd/js/jquery.tablednd.0.7.min.js new file mode 100644 index 0000000000..09b0bedb14 --- /dev/null +++ b/node_modules/tablednd/js/jquery.tablednd.0.7.min.js @@ -0,0 +1 @@ +var hasTouch='ontouchstart'in document.documentElement,startEvent=hasTouch?'touchstart':'mousedown',moveEvent=hasTouch?'touchmove':'mousemove',endEvent=hasTouch?'touchend':'mouseup';if(hasTouch){$.each("touchstart touchmove touchend".split(" "),function(i,name){jQuery.event.fixHooks[name]=jQuery.event.mouseHooks});alert("has Touch")}jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldY:0,build:function(options){this.each(function(){this.tableDnDConfig=jQuery.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,scrollAmount:5,serializeRegexp:/[^\-]*$/,serializeParamName:null,dragHandle:null},options||{});jQuery.tableDnD.makeDraggable(this)});return this},makeDraggable:function(table){var config=table.tableDnDConfig;if(config.dragHandle){var cells=jQuery(table.tableDnDConfig.dragHandle,table);cells.each(function(){jQuery(this).bind(startEvent,function(ev){jQuery.tableDnD.initialiseDrag(jQuery(this).parents('tr')[0],table,this,ev,config);return false})})}else{var rows=jQuery("tr",table);rows.each(function(){var row=jQuery(this);if(!row.hasClass("nodrag")){row.bind(startEvent,function(ev){if(ev.target.tagName=="TD"){jQuery.tableDnD.initialiseDrag(this,table,this,ev,config);return false}}).css("cursor","move")}})}},initialiseDrag:function(dragObject,table,target,evnt,config){jQuery.tableDnD.dragObject=dragObject;jQuery.tableDnD.currentTable=table;jQuery.tableDnD.mouseOffset=jQuery.tableDnD.getMouseOffset(target,evnt);jQuery.tableDnD.originalOrder=jQuery.tableDnD.serialize();jQuery(document).bind(moveEvent,jQuery.tableDnD.mousemove).bind(endEvent,jQuery.tableDnD.mouseup);if(config.onDragStart){config.onDragStart(table,target)}},updateTables:function(){this.each(function(){if(this.tableDnDConfig){jQuery.tableDnD.makeDraggable(this)}})},mouseCoords:function(ev){if(ev.pageX||ev.pageY){return{x:ev.pageX,y:ev.pageY}}return{x:ev.clientX+document.body.scrollLeft-document.body.clientLeft,y:ev.clientY+document.body.scrollTop-document.body.clientTop}},getMouseOffset:function(target,ev){ev=ev||window.event;var docPos=this.getPosition(target);var mousePos=this.mouseCoords(ev);return{x:mousePos.x-docPos.x,y:mousePos.y-docPos.y}},getPosition:function(e){var left=0;var top=0;if(e.offsetHeight==0){e=e.firstChild}while(e.offsetParent){left+=e.offsetLeft;top+=e.offsetTop;e=e.offsetParent}left+=e.offsetLeft;top+=e.offsetTop;return{x:left,y:top}},mousemove:function(ev){if(jQuery.tableDnD.dragObject==null){return}if(ev.type=='touchmove'){event.preventDefault()}var dragObj=jQuery(jQuery.tableDnD.dragObject);var config=jQuery.tableDnD.currentTable.tableDnDConfig;var mousePos=jQuery.tableDnD.mouseCoords(ev);var y=mousePos.y-jQuery.tableDnD.mouseOffset.y;var yOffset=window.pageYOffset;if(document.all){if(typeof document.compatMode!='undefined'&&document.compatMode!='BackCompat'){yOffset=document.documentElement.scrollTop}else if(typeof document.body!='undefined'){yOffset=document.body.scrollTop}}if(mousePos.y-yOffsetjQuery.tableDnD.oldY;jQuery.tableDnD.oldY=y;if(config.onDragClass){dragObj.addClass(config.onDragClass)}else{dragObj.css(config.onDragStyle)}var currentRow=jQuery.tableDnD.findDropTargetRow(dragObj,y);if(currentRow){if(movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow.nextSibling)}else if(!movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow)}}}return false},findDropTargetRow:function(draggedRow,y){var rows=jQuery.tableDnD.currentTable.rows;for(var i=0;irowY-rowHeight)&&(y<(rowY+rowHeight))){if(row==draggedRow){return null}var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onAllowDrop){if(config.onAllowDrop(draggedRow,row)){return row}else{return null}}else{var nodrop=jQuery(row).hasClass("nodrop");if(!nodrop){return row}else{return null}}return row}}return null},mouseup:function(e){if(jQuery.tableDnD.currentTable&&jQuery.tableDnD.dragObject){jQuery(document).unbind(moveEvent,jQuery.tableDnD.mousemove).unbind(endEvent,jQuery.tableDnD.mouseup);var droppedRow=jQuery.tableDnD.dragObject;var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onDragClass){jQuery(droppedRow).removeClass(config.onDragClass)}else{jQuery(droppedRow).css(config.onDropStyle)}jQuery.tableDnD.dragObject=null;var newOrder=jQuery.tableDnD.serialize();if(config.onDrop&&(jQuery.tableDnD.originalOrder!=newOrder)){config.onDrop(jQuery.tableDnD.currentTable,droppedRow)}jQuery.tableDnD.currentTable=null}},serialize:function(){if(jQuery.tableDnD.currentTable){return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable)}else{return"Error: No Table id set, you need to set an id on your table and every row"}},serializeTable:function(table){var result="";var tableId=table.id;var rows=table.rows;for(var i=0;i0)result+="&";var rowId=rows[i].id;if(rowId&&rowId&&table.tableDnDConfig&&table.tableDnDConfig.serializeRegexp){rowId=rowId.match(table.tableDnDConfig.serializeRegexp)[0]}result+=tableId+'[]='+rowId}return result},serializeTables:function(){var result="";this.each(function(){result+=jQuery.tableDnD.serializeTable(this)});return result}};jQuery.fn.extend({tableDnD:jQuery.tableDnD.build,tableDnDUpdate:jQuery.tableDnD.updateTables,tableDnDSerialize:jQuery.tableDnD.serializeTables}); \ No newline at end of file diff --git a/node_modules/tablednd/js/jquery.tablednd.0.8.min.js b/node_modules/tablednd/js/jquery.tablednd.0.8.min.js new file mode 100644 index 0000000000..681ae71188 --- /dev/null +++ b/node_modules/tablednd/js/jquery.tablednd.0.8.min.js @@ -0,0 +1 @@ +(function($){var hasTouch="ontouchstart" in document.documentElement,startEvent=hasTouch?"touchstart":"mousedown",moveEvent=hasTouch?"touchmove":"mousemove",endEvent=hasTouch?"touchend":"mouseup";if(hasTouch){$.each("touchstart touchmove touchend".split(" "),function(i,name){jQuery.event.fixHooks[name]=jQuery.event.mouseHooks})}jQuery.tableDnD={currentTable:null,dragObject:null,mouseOffset:null,oldY:0,build:function(options){this.each(function(){this.tableDnDConfig=jQuery.extend({onDragStyle:null,onDropStyle:null,onDragClass:"tDnD_whileDrag",onDrop:null,onDragStart:null,scrollAmount:5,serializeRegexp:/[^\-]*$/,serializeParamName:null,dragHandle:null},options||{});jQuery.tableDnD.makeDraggable(this)});return this},makeDraggable:function(table){var config=table.tableDnDConfig;if(config.dragHandle){var cells=jQuery(table.tableDnDConfig.dragHandle,table);cells.each(function(){jQuery(this).bind(startEvent,function(ev){jQuery.tableDnD.initialiseDrag(jQuery(this).parents("tr")[0],table,this,ev,config);return false})})}else{var rows=jQuery("tr",table);rows.each(function(){var row=jQuery(this);if(!row.hasClass("nodrag")){row.bind(startEvent,function(ev){if(ev.target.tagName=="TD"){jQuery.tableDnD.initialiseDrag(this,table,this,ev,config);return false}}).css("cursor","move")}})}},initialiseDrag:function(dragObject,table,target,evnt,config){jQuery.tableDnD.dragObject=dragObject;jQuery.tableDnD.currentTable=table;jQuery.tableDnD.mouseOffset=jQuery.tableDnD.getMouseOffset(target,evnt);jQuery.tableDnD.originalOrder=jQuery.tableDnD.serialize();jQuery(document).bind(moveEvent,jQuery.tableDnD.mousemove).bind(endEvent,jQuery.tableDnD.mouseup);if(config.onDragStart){config.onDragStart(table,target)}},updateTables:function(){this.each(function(){if(this.tableDnDConfig){jQuery.tableDnD.makeDraggable(this)}})},mouseCoords:function(ev){if(ev.pageX||ev.pageY){return{x:ev.pageX,y:ev.pageY}}return{x:ev.clientX+document.body.scrollLeft-document.body.clientLeft,y:ev.clientY+document.body.scrollTop-document.body.clientTop}},getMouseOffset:function(target,ev){ev=ev||window.event;var docPos=this.getPosition(target);var mousePos=this.mouseCoords(ev);return{x:mousePos.x-docPos.x,y:mousePos.y-docPos.y}},getPosition:function(e){var left=0;var top=0;if(e.offsetHeight==0){e=e.firstChild}while(e.offsetParent){left+=e.offsetLeft;top+=e.offsetTop;e=e.offsetParent}left+=e.offsetLeft;top+=e.offsetTop;return{x:left,y:top}},mousemove:function(ev){if(jQuery.tableDnD.dragObject==null){return}if(ev.type=="touchmove"){event.preventDefault()}var dragObj=jQuery(jQuery.tableDnD.dragObject);var config=jQuery.tableDnD.currentTable.tableDnDConfig;var mousePos=jQuery.tableDnD.mouseCoords(ev);var y=mousePos.y-jQuery.tableDnD.mouseOffset.y;var yOffset=window.pageYOffset;if(document.all){if(typeof document.compatMode!="undefined"&&document.compatMode!="BackCompat"){yOffset=document.documentElement.scrollTop}else{if(typeof document.body!="undefined"){yOffset=document.body.scrollTop}}}if(mousePos.y-yOffsetjQuery.tableDnD.oldY;jQuery.tableDnD.oldY=y;if(config.onDragClass){dragObj.addClass(config.onDragClass)}else{dragObj.css(config.onDragStyle)}var currentRow=jQuery.tableDnD.findDropTargetRow(dragObj,y);if(currentRow){if(movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow.nextSibling)}else{if(!movingDown&&jQuery.tableDnD.dragObject!=currentRow){jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject,currentRow)}}}}return false},findDropTargetRow:function(draggedRow,y){var rows=jQuery.tableDnD.currentTable.rows;for(var i=0;irowY-rowHeight)&&(y<(rowY+rowHeight))){if(row==draggedRow){return null}var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onAllowDrop){if(config.onAllowDrop(draggedRow,row)){return row}else{return null}}else{var nodrop=jQuery(row).hasClass("nodrop");if(!nodrop){return row}else{return null}}return row}}return null},mouseup:function(e){if(jQuery.tableDnD.currentTable&&jQuery.tableDnD.dragObject){jQuery(document).unbind(moveEvent,jQuery.tableDnD.mousemove).unbind(endEvent,jQuery.tableDnD.mouseup);var droppedRow=jQuery.tableDnD.dragObject;var config=jQuery.tableDnD.currentTable.tableDnDConfig;if(config.onDragClass){jQuery(droppedRow).removeClass(config.onDragClass)}else{jQuery(droppedRow).css(config.onDropStyle)}jQuery.tableDnD.dragObject=null;var newOrder=jQuery.tableDnD.serialize();if(config.onDrop&&(jQuery.tableDnD.originalOrder!=newOrder)){config.onDrop(jQuery.tableDnD.currentTable,droppedRow)}jQuery.tableDnD.currentTable=null}},jsonize:function(){if(jQuery.tableDnD.currentTable){return jQuery.tableDnD.jsonizeTable(jQuery.tableDnD.currentTable)}else{return"Error: No Table id set, you need to set an id on your table and every row"}},jsonizeTable:function(table){var result="{";var tableId=table.id;var rows=table.rows;result+='"'+tableId+'" : [';for(var i=0;i0){result+="&"}var rowId=rows[i].id;if(rowId&&table.tableDnDConfig&&table.tableDnDConfig.serializeRegexp){rowId=rowId.match(table.tableDnDConfig.serializeRegexp)[0]}result+=paramName+"[]="+rowId}return result},serializeTables:function(){var result="";this.each(function(){result+=jQuery.tableDnD.serializeTable(this)});return result}};jQuery.fn.extend({tableDnD:jQuery.tableDnD.build,tableDnDUpdate:jQuery.tableDnD.updateTables,tableDnDSerialize:jQuery.tableDnD.serializeTables})})(jQuery); \ No newline at end of file diff --git a/node_modules/tablednd/js/jquery.tablednd.0.9.rc1.js b/node_modules/tablednd/js/jquery.tablednd.0.9.rc1.js new file mode 100644 index 0000000000..8eb2cf0ac3 --- /dev/null +++ b/node_modules/tablednd/js/jquery.tablednd.0.9.rc1.js @@ -0,0 +1,664 @@ +/** + * TableDnD plug-in for JQuery, allows you to drag and drop table rows + * You can set up various options to control how the system will work + * Copyright (c) Denis Howlett + * Licensed like jQuery, see http://docs.jquery.com/License. + * + * Configuration options: + * + * onDragStyle + * This is the style that is assigned to the row during drag. There are limitations to the styles that can be + * associated with a row (such as you can't assign a border--well you can, but it won't be + * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as + * a map (as used in the jQuery css(...) function). + * onDropStyle + * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations + * to what you can do. Also this replaces the original style, so again consider using onDragClass which + * is simply added and then removed on drop. + * onDragClass + * This class is added for the duration of the drag and then removed when the row is dropped. It is more + * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default + * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your + * stylesheet. + * onDrop + * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table + * and the row that was dropped. You can work out the new order of the rows by using + * table.rows. + * onDragStart + * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the + * table and the row which the user has started to drag. + * onAllowDrop + * Pass a function that will be called as a row is over another row. If the function returns true, allow + * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under + * the cursor. It returns a boolean: true allows the drop, false doesn't allow it. + * scrollAmount + * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the + * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, + * FF3 beta + * dragHandle + * This is a jQuery mach string for one or more cells in each row that is draggable. If you + * specify this, then you are responsible for setting cursor: move in the CSS and only these cells + * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where + * the whole row is draggable. + * + * Other ways to control behaviour: + * + * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows + * that you don't want to be draggable. + * + * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form + * []=&[]= so that you can send this back to the server. The table must have + * an ID as must all the rows. + * + * Other methods: + * + * $("...").tableDnDUpdate() + * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells). + * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again. + * The table maintains the original configuration (so you don't have to specify it again). + * + * $("...").tableDnDSerialize() + * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be + * called from anywhere and isn't dependent on the currentTable being set up correctly before calling + * + * Known problems: + * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0 + * + * Version 0.2: 2008-02-20 First public version + * Version 0.3: 2008-02-07 Added onDragStart option + * Made the scroll amount configurable (default is 5 as before) + * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes + * Added onAllowDrop to control dropping + * Fixed a bug which meant that you couldn't set the scroll amount in both directions + * Added serialize method + * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row + * draggable + * Improved the serialize method to use a default (and settable) regular expression. + * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table + * Version 0.6: 2011-12-02 Added support for touch devices + * Version 0.7 2012-04-09 Now works with jQuery 1.7 and supports touch, tidied up tabs and spaces + */ +!function ($, window, document, undefined) { +// Determine if this is a touch device +var hasTouch = 'ontouchstart' in document.documentElement, + startEvent = hasTouch ? 'touchstart' : 'mousedown', + moveEvent = hasTouch ? 'touchmove' : 'mousemove', + endEvent = hasTouch ? 'touchend' : 'mouseup'; + +// If we're on a touch device, then wire up the events +// see http://stackoverflow.com/a/8456194/1316086 +hasTouch + && $.each("touchstart touchmove touchend".split(" "), function(i, name) { + $.event.fixHooks[name] = $.event.mouseHooks; + }); + + +$(document).ready(function () { + function parseStyle(css) { + var objMap = {}, + parts = css.match(/([^;:]+)/g) || []; + while (parts.length) + objMap[parts.shift()] = parts.shift().trim(); + + return objMap; + } + $('table').each(function () { + if ($(this).data('table') == 'dnd') { + + $(this).tableDnD({ + onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null, + onDropStyle: $(this).data('ondropstyle') && parseStyle($(this).data('ondropstyle')) || null, + onDragClass: $(this).data('ondragclass') == undefined && "tDnD_whileDrag" || $(this).data('ondragclass'), + onDrop: $(this).data('ondrop') && new Function('table', 'row', $(this).data('ondrop')), // 'return eval("'+$(this).data('ondrop')+'");') || null, + onDragStart: $(this).data('ondragstart') && new Function('table', 'row' ,$(this).data('ondragstart')), // 'return eval("'+$(this).data('ondragstart')+'");') || null, + scrollAmount: $(this).data('scrollamount') || 5, + sensitivity: $(this).data('sensitivity') || 10, + hierarchyLevel: $(this).data('hierarchylevel') || 0, + indentArtifact: $(this).data('indentartifact') || '
     
    ', + autoWidthAdjust: $(this).data('autowidthadjust') || true, + autoCleanRelations: $(this).data('autocleanrelations') || true, + jsonPretifySeparator: $(this).data('jsonpretifyseparator') || '\t', + serializeRegexp: $(this).data('serializeregexp') && new RegExp($(this).data('serializeregexp')) || /[^\-]*$/, + serializeParamName: $(this).data('serializeparamname') || false, + dragHandle: $(this).data('draghandle') || null + }); + } + + + }); +}); + +window.jQuery.tableDnD = { + /** Keep hold of the current table being dragged */ + currentTable: null, + /** Keep hold of the current drag object if any */ + dragObject: null, + /** The current mouse offset */ + mouseOffset: null, + /** Remember the old value of X and Y so that we don't do too much processing */ + oldX: 0, + oldY: 0, + + /** Actually build the structure */ + build: function(options) { + // Set up the defaults if any + + this.each(function() { + // This is bound to each matching table, set up the defaults and override with user options + this.tableDnDConfig = $.extend({ + onDragStyle: null, + onDropStyle: null, + // Add in the default class for whileDragging + onDragClass: "tDnD_whileDrag", + onDrop: null, + onDragStart: null, + scrollAmount: 5, + /** Sensitivity setting will throttle the trigger rate for movement detection */ + sensitivity: 10, + /** Hierarchy level to support parent child. 0 switches this functionality off */ + hierarchyLevel: 0, + /** The html artifact to prepend the first cell with as indentation */ + indentArtifact: '
     
    ', + /** Automatically adjust width of first cell */ + autoWidthAdjust: true, + /** Automatic clean-up to ensure relationship integrity */ + autoCleanRelations: true, + /** Specify a number (4) as number of spaces or any indent string for JSON.stringify */ + jsonPretifySeparator: '\t', + /** The regular expression to use to trim row IDs */ + serializeRegexp: /[^\-]*$/, + /** If you want to specify another parameter name instead of the table ID */ + serializeParamName: false, + /** If you give the name of a class here, then only Cells with this class will be draggable */ + dragHandle: null + }, options || {}); + + // Now make the rows draggable + $.tableDnD.makeDraggable(this); + // Prepare hierarchy support + this.tableDnDConfig.hierarchyLevel + && $.tableDnD.makeIndented(this); + }); + + // Don't break the chain + return this; + }, + makeIndented: function (table) { + var config = table.tableDnDConfig, + rows = table.rows, + firstCell = $(rows).first().find('td:first')[0], + indentLevel = 0, + cellWidth = 0, + longestCell, + tableStyle; + + if ($(table).hasClass('indtd')) + return null; + + tableStyle = $(table).addClass('indtd').attr('style'); + $(table).css({whiteSpace: "nowrap"}); + + for (var w = 0; w < rows.length; w++) { + if (cellWidth < $(rows[w]).find('td:first').text().length) { + cellWidth = $(rows[w]).find('td:first').text().length; + longestCell = w; + } + } + $(firstCell).css({width: 'auto'}); + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').prepend(config.indentArtifact); + firstCell && $(firstCell).css({width: firstCell.offsetWidth}); + tableStyle && $(table).css(tableStyle); + + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').children(':first').remove(); + + config.hierarchyLevel + && $(rows).each(function () { + indentLevel = $(this).data('level') || 0; + indentLevel <= config.hierarchyLevel + && $(this).data('level', indentLevel) + || $(this).data('level', 0); + for (var i = 0; i < $(this).data('level'); i++) + $(this).find('td:first').prepend(config.indentArtifact); + }); + + return this; + }, + /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ + makeDraggable: function(table) { + var config = table.tableDnDConfig; + + config.dragHandle + // We only need to add the event to the specified cells + && $(config.dragHandle, table).each(function() { + // The cell is bound to "this" + $(this).bind(startEvent, function(e) { + $.tableDnD.initialiseDrag($(this).parents('tr')[0], table, this, e, config); + return false; + }); + }) + // For backwards compatibility, we add the event to the whole row + // get all the rows as a wrapped set + || $(table.rows).each(function() { + // Iterate through each row, the row is bound to "this" + if (! $(this).hasClass("nodrag")) { + $(this).bind(startEvent, function(e) { + if (e.target.tagName == "TD") { + $.tableDnD.initialiseDrag(this, table, this, e, config); + return false; + } + }).css("cursor", "move"); // Store the tableDnD object + } + }); + }, + currentOrder: function() { + var rows = this.currentTable.rows; + return $.map(rows, function (val) { + return ($(val).data('level') + val.id).replace(/\s/g, ''); + }).join(''); + }, + initialiseDrag: function(dragObject, table, target, e, config) { + this.dragObject = dragObject; + this.currentTable = table; + this.mouseOffset = this.getMouseOffset(target, e); + this.originalOrder = this.currentOrder(); + + // Now we need to capture the mouse up and mouse move event + // We can use bind so that we don't interfere with other event handlers + $(document) + .bind(moveEvent, this.mousemove) + .bind(endEvent, this.mouseup); + + // Call the onDragStart method if there is one + config.onDragStart + && config.onDragStart(table, target); + }, + updateTables: function() { + this.each(function() { + // this is now bound to each matching table + if (this.tableDnDConfig) + $.tableDnD.makeDraggable(this); + }); + }, + /** Get the mouse coordinates from the event (allowing for browser differences) */ + mouseCoords: function(e) { + if(e.pageX || e.pageY) + return { + x: e.pageX, + y: e.pageY + }; + + return { + x: e.clientX + document.body.scrollLeft - document.body.clientLeft, + y: e.clientY + document.body.scrollTop - document.body.clientTop + }; + }, + /** Given a target element and a mouse eent, get the mouse offset from that element. + To do this we need the element's position and the mouse position */ + getMouseOffset: function(target, e) { + var mousePos, + docPos; + + e = e || window.event; + + docPos = this.getPosition(target); + mousePos = this.mouseCoords(e); + + return { + x: mousePos.x - docPos.x, + y: mousePos.y - docPos.y + }; + }, + /** Get the position of an element by going up the DOM tree and adding up all the offsets */ + getPosition: function(element) { + var left = 0, + top = 0; + + // Safari fix -- thanks to Luis Chato for this! + // Safari 2 doesn't correctly grab the offsetTop of a table row + // this is detailed here: + // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ + // the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. + // note that firefox will return a text node as a first child, so designing a more thorough + // solution may need to take that into account, for now this seems to work in firefox, safari, ie + if (element.offsetHeight == 0) + element = element.firstChild; // a table cell + + while (element.offsetParent) { + left += element.offsetLeft; + top += element.offsetTop; + element = element.offsetParent; + } + + left += element.offsetLeft; + top += element.offsetTop; + + return { + x: left, + y: top + }; + }, + autoScroll: function (mousePos) { + var config = this.currentTable.tableDnDConfig, + yOffset = window.pageYOffset, + windowHeight = window.innerHeight + ? window.innerHeight + : document.documentElement.clientHeight + ? document.documentElement.clientHeight + : document.body.clientHeight; + + // Windows version + // yOffset=document.body.scrollTop; + if (document.all) + if (typeof document.compatMode != 'undefined' + && document.compatMode != 'BackCompat') + yOffset = document.documentElement.scrollTop; + else if (typeof document.body != 'undefined') + yOffset = document.body.scrollTop; + + mousePos.y - yOffset < config.scrollAmount + && window.scrollBy(0, - config.scrollAmount) + || windowHeight - (mousePos.y - yOffset) < config.scrollAmount + && window.scrollBy(0, config.scrollAmount); + + }, + moveVerticle: function (moving, currentRow) { + + if (0 != moving.vertical + // If we're over a row then move the dragged row to there so that the user sees the + // effect dynamically + && currentRow + && this.dragObject != currentRow + && this.dragObject.parentNode == currentRow.parentNode) + 0 > moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow.nextSibling) + || 0 < moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow); + + }, + moveHorizontal: function (moving, currentRow) { + var config = this.currentTable.tableDnDConfig, + currentLevel; + + if (!config.hierarchyLevel + || 0 == moving.horizontal + // We only care if moving left or right on the current row + || !currentRow + || this.dragObject != currentRow) + return null; + + currentLevel = $(currentRow).data('level'); + + 0 < moving.horizontal + && currentLevel > 0 + && $(currentRow).find('td:first').children(':first').remove() + && $(currentRow).data('level', --currentLevel); + + 0 > moving.horizontal + && currentLevel < config.hierarchyLevel + && $(currentRow).prev().data('level') >= currentLevel + && $(currentRow).children(':first').prepend(config.indentArtifact) + && $(currentRow).data('level', ++currentLevel); + + }, + mousemove: function(e) { + var dragObj = $($.tableDnD.dragObject), + config = $.tableDnD.currentTable.tableDnDConfig, + currentRow, + mousePos, + moving, + x, + y; + + e && e.preventDefault(); + + if (!$.tableDnD.dragObject) + return false; + + // prevent touch device screen scrolling + e.type == 'touchmove' + && event.preventDefault(); // TODO verify this is event and not really e + + // update the style to show we're dragging + config.onDragClass + && dragObj.addClass(config.onDragClass) + || dragObj.css(config.onDragStyle); + + mousePos = $.tableDnD.mouseCoords(e); + x = mousePos.x - $.tableDnD.mouseOffset.x; + y = mousePos.y - $.tableDnD.mouseOffset.y; + + // auto scroll the window + $.tableDnD.autoScroll(mousePos); + + currentRow = $.tableDnD.findDropTargetRow(dragObj, y); + moving = $.tableDnD.findDragDirection(x, y); + + $.tableDnD.moveVerticle(moving, currentRow); + $.tableDnD.moveHorizontal(moving, currentRow); + + return false; + }, + findDragDirection: function (x,y) { + var sensitivity = this.currentTable.tableDnDConfig.sensitivity, + oldX = this.oldX, + oldY = this.oldY, + xMin = oldX - sensitivity, + xMax = oldX + sensitivity, + yMin = oldY - sensitivity, + yMax = oldY + sensitivity, + moving = { + horizontal: x >= xMin && x <= xMax ? 0 : x > oldX ? -1 : 1, + vertical : y >= yMin && y <= yMax ? 0 : y > oldY ? -1 : 1 + }; + + // update the old value + if (moving.horizontal != 0) + this.oldX = x; + if (moving.vertical != 0) + this.oldY = y; + + return moving; + }, + /** We're only worried about the y position really, because we can only move rows up and down */ + findDropTargetRow: function(draggedRow, y) { + var rowHeight = 0, + rows = this.currentTable.rows, + config = this.currentTable.tableDnDConfig, + rowY = 0, + row = null; + + for (var i = 0; i < rows.length; i++) { + row = rows[i]; + rowY = this.getPosition(row).y; + rowHeight = parseInt(row.offsetHeight) / 2; + if (row.offsetHeight == 0) { + rowY = this.getPosition(row.firstChild).y; + rowHeight = parseInt(row.firstChild.offsetHeight) / 2; + } + // Because we always have to insert before, we need to offset the height a bit + if (y > (rowY - rowHeight) && y < (rowY + rowHeight)) + // that's the row we're over + // If it's the same as the current row, ignore it + if (row == draggedRow + || (config.onAllowDrop + && !config.onAllowDrop(draggedRow, row)) + // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) + || $(row).hasClass("nodrop")) + return null; + else + return row; + } + return null; + }, + processMouseup: function() { + var config = this.currentTable.tableDnDConfig, + droppedRow = this.dragObject, + parentLevel = 0, + myLevel = 0; + + if (!this.currentTable || !droppedRow) + return null; + + // Unbind the event handlers + $(document) + .unbind(moveEvent, this.mousemove) + .unbind(endEvent, this.mouseup); + + config.hierarchyLevel + && config.autoCleanRelations + && $(this.currentTable.rows).first().find('td:first').children().each(function () { + myLevel = $(this).parents('tr:first').data('level'); + myLevel + && $(this).parents('tr:first').data('level', --myLevel) + && $(this).remove(); + }) + && config.hierarchyLevel > 1 + && $(this.currentTable.rows).each(function () { + myLevel = $(this).data('level'); + if (myLevel > 1) { + parentLevel = $(this).prev().data('level'); + while (myLevel > parentLevel + 1) { + $(this).find('td:first').children(':first').remove(); + $(this).data('level', --myLevel); + } + } + }); + + // If we have a dragObject, then we need to release it, + // The row will already have been moved to the right place so we just reset stuff + config.onDragClass + && $(droppedRow).removeClass(config.onDragClass) + || $(droppedRow).css(config.onDropStyle); + + this.dragObject = null; + // Call the onDrop method if there is one + config.onDrop + && this.originalOrder != this.currentOrder() + && $(droppedRow).hide().fadeIn('fast') + && config.onDrop(this.currentTable, droppedRow); + + this.currentTable = null; // let go of the table too + }, + mouseup: function(e) { + e && e.preventDefault(); + $.tableDnD.processMouseup(); + return false; + }, + jsonize: function(pretify) { + var table = this.currentTable; + if (pretify) + return JSON.stringify( + this.tableData(table), + null, + table.tableDnDConfig.jsonPretifySeparator + ); + return JSON.stringify(this.tableData(table)); + }, + serialize: function() { + return $.param(this.tableData(this.currentTable)); + }, + serializeTable: function(table) { + var result = ""; + var paramName = table.tableDnDConfig.serializeParamName || table.id; + var rows = table.rows; + for (var i=0; i 0) result += "&"; + var rowId = rows[i].id; + if (rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { + rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; + result += paramName + '[]=' + rowId; + } + } + return result; + }, + serializeTables: function() { + var result = []; + $('table').each(function() { + this.id && result.push($.param(this.tableData(this))); + }); + return result.join('&'); + }, + tableData: function (table) { + var config = table.tableDnDConfig, + previousIDs = [], + currentLevel = 0, + indentLevel = 0, + rowID = null, + data = {}, + getSerializeRegexp, + paramName, + currentID, + rows; + + if (!table) + table = this.currentTable; + if (!table || !table.id || !table.rows || !table.rows.length) + return {error: { code: 500, message: "Not a valid table, no serializable unique id provided."}}; + + rows = config.autoCleanRelations + && table.rows + || $.makeArray(table.rows); + paramName = config.serializeParamName || table.id; + currentID = paramName; + + getSerializeRegexp = function (rowId) { + if (rowId && config && config.serializeRegexp) + return rowId.match(config.serializeRegexp)[0]; + return rowId; + }; + + data[currentID] = []; + !config.autoCleanRelations + && $(rows[0]).data('level') + && rows.unshift({id: 'undefined'}); + + + + for (var i=0; i < rows.length; i++) { + if (config.hierarchyLevel) { + indentLevel = $(rows[i]).data('level') || 0; + if (indentLevel == 0) { + currentID = paramName; + previousIDs = []; + } + else if (indentLevel > currentLevel) { + previousIDs.push([currentID, currentLevel]); + currentID = getSerializeRegexp(rows[i-1].id); + } + else if (indentLevel < currentLevel) { + for (var h = 0; h < previousIDs.length; h++) { + if (previousIDs[h][1] == indentLevel) + currentID = previousIDs[h][0]; + if (previousIDs[h][1] >= currentLevel) + previousIDs[h][1] = 0; + } + } + currentLevel = indentLevel; + + if (!$.isArray(data[currentID])) + data[currentID] = []; + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + else { + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + } + return data; + } +}; + +window.jQuery.fn.extend( + { + tableDnD : $.tableDnD.build, + tableDnDUpdate : $.tableDnD.updateTables, + tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD), + tableDnDSerializeAll : $.tableDnD.serializeTables, + tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD) + } +); + +}(window.jQuery, window, window.document); diff --git a/node_modules/tablednd/js/jquery.tablednd.js b/node_modules/tablednd/js/jquery.tablednd.js new file mode 100644 index 0000000000..d91f17532c --- /dev/null +++ b/node_modules/tablednd/js/jquery.tablednd.js @@ -0,0 +1,601 @@ +/** + * TableDnD plug-in for JQuery, allows you to drag and drop table rows + * You can set up various options to control how the system will work + * Copyright (c) Denis Howlett + * License: MIT. + * See https://github.com/isocra/TableDnD + */ + +/*jshint -W054 */ + +!function ($, window, document, undefined) { + +var startEvent = 'touchstart mousedown', + moveEvent = 'touchmove mousemove', + endEvent = 'touchend mouseup'; + +$(document).ready(function () { + function parseStyle(css) { + var objMap = {}, + parts = css.match(/([^;:]+)/g) || []; + while (parts.length) + objMap[parts.shift()] = parts.shift().trim(); + + return objMap; + } + $('table').each(function () { + if ($(this).data('table') === 'dnd') { + + $(this).tableDnD({ + onDragStyle: $(this).data('ondragstyle') && parseStyle($(this).data('ondragstyle')) || null, + onDropStyle: $(this).data('ondropstyle') && parseStyle($(this).data('ondropstyle')) || null, + onDragClass: $(this).data('ondragclass') === undefined && "tDnD_whileDrag" || $(this).data('ondragclass'), + onDrop: $(this).data('ondrop') && new Function('table', 'row', $(this).data('ondrop')), // 'return eval("'+$(this).data('ondrop')+'");') || null, + onDragStart: $(this).data('ondragstart') && new Function('table', 'row' ,$(this).data('ondragstart')), // 'return eval("'+$(this).data('ondragstart')+'");') || null, + onDragStop: $(this).data('ondragstop') && new Function('table', 'row' ,$(this).data('ondragstop')), + scrollAmount: $(this).data('scrollamount') || 5, + sensitivity: $(this).data('sensitivity') || 10, + hierarchyLevel: $(this).data('hierarchylevel') || 0, + indentArtifact: $(this).data('indentartifact') || '
     
    ', + autoWidthAdjust: $(this).data('autowidthadjust') || true, + autoCleanRelations: $(this).data('autocleanrelations') || true, + jsonPretifySeparator: $(this).data('jsonpretifyseparator') || '\t', + serializeRegexp: $(this).data('serializeregexp') && new RegExp($(this).data('serializeregexp')) || /[^\-]*$/, + serializeParamName: $(this).data('serializeparamname') || false, + dragHandle: $(this).data('draghandle') || null + }); + } + + + }); +}); + +jQuery.tableDnD = { + /** Keep hold of the current table being dragged */ + currentTable: null, + /** Keep hold of the current drag object if any */ + dragObject: null, + /** The current mouse offset */ + mouseOffset: null, + /** Remember the old value of X and Y so that we don't do too much processing */ + oldX: 0, + oldY: 0, + + /** Actually build the structure */ + build: function(options) { + // Set up the defaults if any + + this.each(function() { + // This is bound to each matching table, set up the defaults and override with user options + this.tableDnDConfig = $.extend({ + onDragStyle: null, + onDropStyle: null, + // Add in the default class for whileDragging + onDragClass: "tDnD_whileDrag", + onDrop: null, + onDragStart: null, + onDragStop: null, + scrollAmount: 5, + /** Sensitivity setting will throttle the trigger rate for movement detection */ + sensitivity: 10, + /** Hierarchy level to support parent child. 0 switches this functionality off */ + hierarchyLevel: 0, + /** The html artifact to prepend the first cell with as indentation */ + indentArtifact: '
     
    ', + /** Automatically adjust width of first cell */ + autoWidthAdjust: true, + /** Automatic clean-up to ensure relationship integrity */ + autoCleanRelations: true, + /** Specify a number (4) as number of spaces or any indent string for JSON.stringify */ + jsonPretifySeparator: '\t', + /** The regular expression to use to trim row IDs */ + serializeRegexp: /[^\-]*$/, + /** If you want to specify another parameter name instead of the table ID */ + serializeParamName: false, + /** If you give the name of a class here, then only Cells with this class will be draggable */ + dragHandle: null + }, options || {}); + + // Now make the rows draggable + $.tableDnD.makeDraggable(this); + // Prepare hierarchy support + this.tableDnDConfig.hierarchyLevel + && $.tableDnD.makeIndented(this); + }); + + // Don't break the chain + return this; + }, + makeIndented: function (table) { + var config = table.tableDnDConfig, + rows = table.rows, + firstCell = $(rows).first().find('td:first')[0], + indentLevel = 0, + cellWidth = 0, + longestCell, + tableStyle; + + if ($(table).hasClass('indtd')) + return null; + + tableStyle = $(table).addClass('indtd').attr('style'); + $(table).css({whiteSpace: "nowrap"}); + + for (var w = 0; w < rows.length; w++) { + if (cellWidth < $(rows[w]).find('td:first').text().length) { + cellWidth = $(rows[w]).find('td:first').text().length; + longestCell = w; + } + } + $(firstCell).css({width: 'auto'}); + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').prepend(config.indentArtifact); + firstCell && $(firstCell).css({width: firstCell.offsetWidth}); + tableStyle && $(table).css(tableStyle); + + for (w = 0; w < config.hierarchyLevel; w++) + $(rows[longestCell]).find('td:first').children(':first').remove(); + + config.hierarchyLevel + && $(rows).each(function () { + indentLevel = $(this).data('level') || 0; + indentLevel <= config.hierarchyLevel + && $(this).data('level', indentLevel) + || $(this).data('level', 0); + for (var i = 0; i < $(this).data('level'); i++) + $(this).find('td:first').prepend(config.indentArtifact); + }); + + return this; + }, + /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ + makeDraggable: function(table) { + var config = table.tableDnDConfig; + + config.dragHandle + // We only need to add the event to the specified cells + && $(config.dragHandle, table).each(function() { + // The cell is bound to "this" + $(this).bind(startEvent, function(e) { + $.tableDnD.initialiseDrag($(this).parents('tr')[0], table, this, e, config); + return false; + }); + }) + // For backwards compatibility, we add the event to the whole row + // get all the rows as a wrapped set + || $(table.rows).each(function() { + // Iterate through each row, the row is bound to "this" + if (! $(this).hasClass("nodrag")) { + $(this).bind(startEvent, function(e) { + if (e.target.tagName === "TD") { + $.tableDnD.initialiseDrag(this, table, this, e, config); + return false; + } + }).css("cursor", "move"); // Store the tableDnD object + } else { + $(this).css("cursor", ""); // Remove the cursor if we don't have the nodrag class + } + }); + }, + currentOrder: function() { + var rows = this.currentTable.rows; + return $.map(rows, function (val) { + return ($(val).data('level') + val.id).replace(/\s/g, ''); + }).join(''); + }, + initialiseDrag: function(dragObject, table, target, e, config) { + this.dragObject = dragObject; + this.currentTable = table; + this.mouseOffset = this.getMouseOffset(target, e); + this.originalOrder = this.currentOrder(); + + // Now we need to capture the mouse up and mouse move event + // We can use bind so that we don't interfere with other event handlers + $(document) + .bind(moveEvent, this.mousemove) + .bind(endEvent, this.mouseup); + + // Call the onDragStart method if there is one + config.onDragStart + && config.onDragStart(table, target); + }, + updateTables: function() { + this.each(function() { + // this is now bound to each matching table + if (this.tableDnDConfig) + $.tableDnD.makeDraggable(this); + }); + }, + /** Get the mouse coordinates from the event (allowing for browser differences) */ + mouseCoords: function(e) { + if (e.originalEvent.changedTouches) + return { + x: e.originalEvent.changedTouches[0].clientX, + y: e.originalEvent.changedTouches[0].clientY + }; + + if(e.pageX || e.pageY) + return { + x: e.pageX, + y: e.pageY + }; + + return { + x: e.clientX + document.body.scrollLeft - document.body.clientLeft, + y: e.clientY + document.body.scrollTop - document.body.clientTop + }; + }, + /** Given a target element and a mouse eent, get the mouse offset from that element. + To do this we need the element's position and the mouse position */ + getMouseOffset: function(target, e) { + var mousePos, + docPos; + + e = e || window.event; + + docPos = this.getPosition(target); + mousePos = this.mouseCoords(e); + + return { + x: mousePos.x - docPos.x, + y: mousePos.y - docPos.y + }; + }, + /** Get the position of an element by going up the DOM tree and adding up all the offsets */ + getPosition: function(element) { + var left = 0, + top = 0; + + // Safari fix -- thanks to Luis Chato for this! + // Safari 2 doesn't correctly grab the offsetTop of a table row + // this is detailed here: + // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ + // the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. + // note that firefox will return a text node as a first child, so designing a more thorough + // solution may need to take that into account, for now this seems to work in firefox, safari, ie + if (element.offsetHeight === 0) + element = element.firstChild; // a table cell + + while (element.offsetParent) { + left += element.offsetLeft; + top += element.offsetTop; + element = element.offsetParent; + } + + left += element.offsetLeft; + top += element.offsetTop; + + return { + x: left, + y: top + }; + }, + autoScroll: function (mousePos) { + var config = this.currentTable.tableDnDConfig, + yOffset = window.pageYOffset, + windowHeight = window.innerHeight + ? window.innerHeight + : document.documentElement.clientHeight + ? document.documentElement.clientHeight + : document.body.clientHeight; + + // Windows version + // yOffset=document.body.scrollTop; + if (document.all) + if (typeof document.compatMode !== 'undefined' + && document.compatMode !== 'BackCompat') + yOffset = document.documentElement.scrollTop; + else if (typeof document.body !== 'undefined') + yOffset = document.body.scrollTop; + + mousePos.y - yOffset < config.scrollAmount + && window.scrollBy(0, - config.scrollAmount) + || windowHeight - (mousePos.y - yOffset) < config.scrollAmount + && window.scrollBy(0, config.scrollAmount); + + }, + moveVerticle: function (moving, currentRow) { + + if (0 !== moving.vertical + // If we're over a row then move the dragged row to there so that the user sees the + // effect dynamically + && currentRow + && this.dragObject !== currentRow + && this.dragObject.parentNode === currentRow.parentNode) + 0 > moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow.nextSibling) + || 0 < moving.vertical + && this.dragObject.parentNode.insertBefore(this.dragObject, currentRow); + + }, + moveHorizontal: function (moving, currentRow) { + var config = this.currentTable.tableDnDConfig, + currentLevel; + + if (!config.hierarchyLevel + || 0 === moving.horizontal + // We only care if moving left or right on the current row + || !currentRow + || this.dragObject !== currentRow) + return null; + + currentLevel = $(currentRow).data('level'); + + 0 < moving.horizontal + && currentLevel > 0 + && $(currentRow).find('td:first').children(':first').remove() + && $(currentRow).data('level', --currentLevel); + + 0 > moving.horizontal + && currentLevel < config.hierarchyLevel + && $(currentRow).prev().data('level') >= currentLevel + && $(currentRow).children(':first').prepend(config.indentArtifact) + && $(currentRow).data('level', ++currentLevel); + + }, + mousemove: function(e) { + var dragObj = $($.tableDnD.dragObject), + config = $.tableDnD.currentTable.tableDnDConfig, + currentRow, + mousePos, + moving, + x, + y; + + e && e.preventDefault(); + + if (!$.tableDnD.dragObject) + return false; + + // prevent touch device screen scrolling + e.type === 'touchmove' + && event.preventDefault(); // TODO verify this is event and not really e + + // update the style to show we're dragging + config.onDragClass + && dragObj.addClass(config.onDragClass) + || dragObj.css(config.onDragStyle); + + mousePos = $.tableDnD.mouseCoords(e); + x = mousePos.x - $.tableDnD.mouseOffset.x; + y = mousePos.y - $.tableDnD.mouseOffset.y; + + // auto scroll the window + $.tableDnD.autoScroll(mousePos); + + currentRow = $.tableDnD.findDropTargetRow(dragObj, y); + moving = $.tableDnD.findDragDirection(x, y); + + $.tableDnD.moveVerticle(moving, currentRow); + $.tableDnD.moveHorizontal(moving, currentRow); + + return false; + }, + findDragDirection: function (x,y) { + var sensitivity = this.currentTable.tableDnDConfig.sensitivity, + oldX = this.oldX, + oldY = this.oldY, + xMin = oldX - sensitivity, + xMax = oldX + sensitivity, + yMin = oldY - sensitivity, + yMax = oldY + sensitivity, + moving = { + horizontal: x >= xMin && x <= xMax ? 0 : x > oldX ? -1 : 1, + vertical : y >= yMin && y <= yMax ? 0 : y > oldY ? -1 : 1 + }; + + // update the old value + if (moving.horizontal !== 0) + this.oldX = x; + if (moving.vertical !== 0) + this.oldY = y; + + return moving; + }, + /** We're only worried about the y position really, because we can only move rows up and down */ + findDropTargetRow: function(draggedRow, y) { + var rowHeight = 0, + rows = this.currentTable.rows, + config = this.currentTable.tableDnDConfig, + rowY = 0, + row = null; + + for (var i = 0; i < rows.length; i++) { + row = rows[i]; + rowY = this.getPosition(row).y; + rowHeight = parseInt(row.offsetHeight) / 2; + if (row.offsetHeight === 0) { + rowY = this.getPosition(row.firstChild).y; + rowHeight = parseInt(row.firstChild.offsetHeight) / 2; + } + // Because we always have to insert before, we need to offset the height a bit + if (y > (rowY - rowHeight) && y < (rowY + rowHeight)) + // that's the row we're over + // If it's the same as the current row, ignore it + if (draggedRow.is(row) + || (config.onAllowDrop + && !config.onAllowDrop(draggedRow, row)) + // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) + || $(row).hasClass("nodrop")) + return null; + else + return row; + } + return null; + }, + processMouseup: function() { + if (!this.currentTable || !this.dragObject) + return null; + + var config = this.currentTable.tableDnDConfig, + droppedRow = this.dragObject, + parentLevel = 0, + myLevel = 0; + + // Unbind the event handlers + $(document) + .unbind(moveEvent, this.mousemove) + .unbind(endEvent, this.mouseup); + + config.hierarchyLevel + && config.autoCleanRelations + && $(this.currentTable.rows).first().find('td:first').children().each(function () { + myLevel = $(this).parents('tr:first').data('level'); + myLevel + && $(this).parents('tr:first').data('level', --myLevel) + && $(this).remove(); + }) + && config.hierarchyLevel > 1 + && $(this.currentTable.rows).each(function () { + myLevel = $(this).data('level'); + if (myLevel > 1) { + parentLevel = $(this).prev().data('level'); + while (myLevel > parentLevel + 1) { + $(this).find('td:first').children(':first').remove(); + $(this).data('level', --myLevel); + } + } + }); + + // If we have a dragObject, then we need to release it, + // The row will already have been moved to the right place so we just reset stuff + config.onDragClass + && $(droppedRow).removeClass(config.onDragClass) + || $(droppedRow).css(config.onDropStyle); + + this.dragObject = null; + // Call the onDrop method if there is one + config.onDrop + && this.originalOrder !== this.currentOrder() + && $(droppedRow).hide().fadeIn('fast') + && config.onDrop(this.currentTable, droppedRow); + + // Call the onDragStop method if there is one + config.onDragStop + && config.onDragStop(this.currentTable, droppedRow); + + this.currentTable = null; // let go of the table too + }, + mouseup: function(e) { + e && e.preventDefault(); + $.tableDnD.processMouseup(); + return false; + }, + jsonize: function(pretify) { + var table = this.currentTable; + if (pretify) + return JSON.stringify( + this.tableData(table), + null, + table.tableDnDConfig.jsonPretifySeparator + ); + return JSON.stringify(this.tableData(table)); + }, + serialize: function() { + return $.param(this.tableData(this.currentTable)); + }, + serializeTable: function(table) { + var result = ""; + var paramName = table.tableDnDConfig.serializeParamName || table.id; + var rows = table.rows; + for (var i=0; i 0) result += "&"; + var rowId = rows[i].id; + if (rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { + rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; + result += paramName + '[]=' + rowId; + } + } + return result; + }, + serializeTables: function() { + var result = []; + $('table').each(function() { + this.id && result.push($.param($.tableDnD.tableData(this))); + }); + return result.join('&'); + }, + tableData: function (table) { + var config = table.tableDnDConfig, + previousIDs = [], + currentLevel = 0, + indentLevel = 0, + rowID = null, + data = {}, + getSerializeRegexp, + paramName, + currentID, + rows; + + if (!table) + table = this.currentTable; + if (!table || !table.rows || !table.rows.length) + return {error: { code: 500, message: "Not a valid table."}}; + if (!table.id && !config.serializeParamName) + return {error: { code: 500, message: "No serializable unique id provided."}}; + + rows = config.autoCleanRelations + && table.rows + || $.makeArray(table.rows); + paramName = config.serializeParamName || table.id; + currentID = paramName; + + getSerializeRegexp = function (rowId) { + if (rowId && config && config.serializeRegexp) + return rowId.match(config.serializeRegexp)[0]; + return rowId; + }; + + data[currentID] = []; + !config.autoCleanRelations + && $(rows[0]).data('level') + && rows.unshift({id: 'undefined'}); + + + + for (var i=0; i < rows.length; i++) { + if (config.hierarchyLevel) { + indentLevel = $(rows[i]).data('level') || 0; + if (indentLevel === 0) { + currentID = paramName; + previousIDs = []; + } + else if (indentLevel > currentLevel) { + previousIDs.push([currentID, currentLevel]); + currentID = getSerializeRegexp(rows[i-1].id); + } + else if (indentLevel < currentLevel) { + for (var h = 0; h < previousIDs.length; h++) { + if (previousIDs[h][1] === indentLevel) + currentID = previousIDs[h][0]; + if (previousIDs[h][1] >= currentLevel) + previousIDs[h][1] = 0; + } + } + currentLevel = indentLevel; + + if (!$.isArray(data[currentID])) + data[currentID] = []; + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + else { + rowID = getSerializeRegexp(rows[i].id); + rowID && data[currentID].push(rowID); + } + } + return data; + } +}; + +jQuery.fn.extend( + { + tableDnD : $.tableDnD.build, + tableDnDUpdate : $.tableDnD.updateTables, + tableDnDSerialize : $.proxy($.tableDnD.serialize, $.tableDnD), + tableDnDSerializeAll : $.tableDnD.serializeTables, + tableDnDData : $.proxy($.tableDnD.tableData, $.tableDnD) + } +); + +}(jQuery, window, window.document); diff --git a/node_modules/tablednd/package.json b/node_modules/tablednd/package.json new file mode 100644 index 0000000000..ace4bbaec9 --- /dev/null +++ b/node_modules/tablednd/package.json @@ -0,0 +1,35 @@ +{ + "name": "tablednd", + "version": "1.0.3", + "description": "JQuery plugin for dragging and droping rows in a table", + "main": "Gruntfile.js", + "dependencies": {}, + "devDependencies": { + "grunt": "^0.4.5", + "grunt-contrib-jshint": "^0.10.0", + "grunt-contrib-uglify": "^0.6.0", + "jshint": "^2.9.3", + "release-it": "^5.2.0" + }, + "scripts": { + "test": "grunt test", + "release": "release-it" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/isocra/TableDnD.git" + }, + "keywords": [ + "tablednd", + "drag", + "and", + "drop", + "jquery" + ], + "author": "DenisH", + "license": "MIT", + "bugs": { + "url": "https://github.com/isocra/TableDnD/issues" + }, + "homepage": "https://github.com/isocra/TableDnD#readme" +} diff --git a/node_modules/tablednd/server/ajaxJSONTest.php b/node_modules/tablednd/server/ajaxJSONTest.php new file mode 100644 index 0000000000..60b2a63fce --- /dev/null +++ b/node_modules/tablednd/server/ajaxJSONTest.php @@ -0,0 +1,13 @@ +The server says: your row order was
    +"; + if (isset($result["$value"])) + show_results($result, $value, $indent.implode(' ', array_fill(0, 12, ''))); + } +} +?> +See the PHP Source
    diff --git a/node_modules/tablednd/server/ajaxJSONTest_php.html b/node_modules/tablednd/server/ajaxJSONTest_php.html new file mode 100644 index 0000000000..cc648a7252 --- /dev/null +++ b/node_modules/tablednd/server/ajaxJSONTest_php.html @@ -0,0 +1,3 @@ + +The server says: your row order was<br/>
    <?php
    $result 
    json_decode(file_get_contents('php://input'), true);
    show_results($result"table-7");
    function 
    show_results($result$id$indent null) {
        foreach(
    $result[$id] as $value) {
            echo 
    "$indent$value<br/>";
            if (isset(
    $result["$value"]))
                
    show_results($result$value$indent.implode('&nbsp;'array_fill(012'')));
        }
    }
    ?>
    See the <a href="server/ajaxJSONTest_php.html" target="_BLANK">PHP Source</a><br/>
    +
    \ No newline at end of file diff --git a/node_modules/tablednd/server/ajaxTest.php b/node_modules/tablednd/server/ajaxTest.php new file mode 100644 index 0000000000..6bf4f45bac --- /dev/null +++ b/node_modules/tablednd/server/ajaxTest.php @@ -0,0 +1,8 @@ +The server says: your row order was
    +"; +} +?> +See the PHP Source
    \ No newline at end of file diff --git a/node_modules/tablednd/server/ajaxTest_php.html b/node_modules/tablednd/server/ajaxTest_php.html new file mode 100644 index 0000000000..e3b23064cb --- /dev/null +++ b/node_modules/tablednd/server/ajaxTest_php.html @@ -0,0 +1,4 @@ + + +The server says: your row order was<br/>
    <?php
    $result 
    $_REQUEST["table-3"];
    foreach(
    $result as $value) {
        echo 
    "$value<br/>";
    }
    ?>
    See the <a href="server/ajaxTest_php.html" target="_BLANK">PHP Source</a><br/>
    +
    diff --git a/node_modules/tablednd/serverExample.html b/node_modules/tablednd/serverExample.html new file mode 100644 index 0000000000..72ca39ff30 --- /dev/null +++ b/node_modules/tablednd/serverExample.html @@ -0,0 +1,39 @@ + + + + + TableDnD Server Example + + + + + + + + + + + + + +
    1Onesome text
    2Twosome text
    3Threesome text
    4Foursome text
    5Fivesome text
    6Sixsome text
    + +
    + + // TODO serialise doesn't work very well with a form does it!!! + +

    +
    + + + + + diff --git a/node_modules/tablednd/stripe.html b/node_modules/tablednd/stripe.html new file mode 100644 index 0000000000..1bc5cc24f0 --- /dev/null +++ b/node_modules/tablednd/stripe.html @@ -0,0 +1,294 @@ + + + + Table Drag and Drop jQuery plugin + + + +
    +

    Table Drag and Drop jQuery plugin

    +

    This page contains documentation and tests for the TableDnD jQuery plug-in. For more information and +to post comments, please go to isocra.com. +

    +

    If you have issues or bug reports, then you can post them at the TableDnD plug page +at plugins.jquery.com

    + +

    How do I use it?

    +
      +
    1. Download Download jQuery (version 1.2 or above), then the TableDnD plugin (current version 0.4).
    2. +
    3. Reference both scripts in your HTML page in the normal way.
    4. +
    5. In true jQuery style, the typical way to initialise the tabes is in the $(document).ready function. Use a selector to select your table and then call tableDnD(). You can optionally specify a set of properties (described below).
    6. +
    +
    +
    + + + + + + + +
    1Onesome text
    2Twosome text
    3Threesome text
    4Foursome text
    5Fivesome text
    6Sixsome text
    +
    +

    The HTML for the table is very straight forward (no Javascript, pure HTML):

    + +
    +<table id="table-1" cellspacing="0" cellpadding="2">
    +    <tr id="1"><td>1</td><td>One</td><td>some text</td></tr>
    +    <tr id="2"><td>2</td><td>Two</td><td>some text</td></tr>
    +    <tr id="3"><td>3</td><td>Three</td><td>some text</td></tr>
    +    <tr id="4"><td>4</td><td>Four</td><td>some text</td></tr>
    +    <tr id="5"><td>5</td><td>Five</td><td>some text</td></tr>
    +    <tr id="6"><td>6</td><td>Six</td><td>some text</td></tr>
    +</table>
    +
    +

    To add in the "draggability" all we need to do is add a line to the $(document).ready(...) function +as follows:

    +
    +<script type="text/javascript">
    +$(document).ready(function() {
    +    // Initialise the table
    +    $("#table-1").tableDnD();
    +});
    +</script>
    +
    +

    In the example above we're not setting any parameters at all so we get the default settings. There are a number + of parameters you can set in order to control the look and feel of the table and also to add custom behaviour + on drag or on drop. The parameters are specified as a map in the usual way and are described below:

    + +
    +
    onDragStyle
    +
    This is the style that is assigned to the row during drag. There are limitations to the styles that can be + associated with a row (such as you can't assign a border—well you can, but it won't be + displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as + a map (as used in the jQuery css(...) function).
    +
    onDropStyle
    +
    This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations + to what you can do. Also this replaces the original style, so again consider using onDragClass which + is simply added and then removed on drop.
    +
    onDragClass
    +
    This class is added for the duration of the drag and then removed when the row is dropped. It is more + flexible than using onDragStyle since it can be inherited by the row cells and other content. The default + is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your + stylesheet.
    +
    onDrop
    +
    Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table + and the row that was dropped. You can work out the new order of the rows by using + table.tBodies[0].rows.
    +
    onDragStart
    +
    Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the + table and the row which the user has started to drag.
    +
    scrollAmount
    +
    This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the + window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, + FF3 beta)
    +
    +

    This second table has has an onDrop function applied as well as an onDragClass. The javascript to set this up is +as follows:

    +
    +$(document).ready(function() {
    +
    +	// Initialise the first table (as before)
    +	$("#table-1").tableDnD();
    +
    +	// Make a nice striped effect on the table
    +	$("#table-2 tr:even').addClass('alt')");
    +
    +	// Initialise the second table specifying a dragClass and an onDrop function that will display an alert
    +	$("#table-2").tableDnD({
    +	    onDragClass: "myDragClass",
    +	    onDrop: function(table, row) {
    +            var rows = table.tBodies[0].rows;
    +            var debugStr = "Row dropped was "+row.id+". New order: ";
    +            for (var i=0; i<rows.length; i++) {
    +                debugStr += rows[i].id+" ";
    +            }
    +	        $(#debugArea).html(debugStr);
    +	    },
    +		onDragStart: function(table, row) {
    +			$(#debugArea).html("Started dragging row "+row.id);
    +		}
    +	});
    +});
    +
    +
    +
     
    + + + + + + + + + + + + + + + +
    1One
    2Two
    3Three
    4Four
    5Five
    6Six
    7Seven
    8Eight
    9Nine
    10Ten
    11Eleven
    12Twelve
    13Thirteen
    14Fourteen
    +
    +

    What to do afterwards?

    +

    Generally once the user has dropped a row, you need to inform the server of the new order. To do this, we've + added a method called serialise(). It takes no parameters but knows the current table from the + context. The method returns a string of the form tableId[]=rowId1&tableId[]=rowId2&tableId[]=rowId3... + You can then use this as part of an Ajax load. +

    +

    This third table demonstrates calling the serialise function inside onDrop (as shown below). It also + demonstrates the "nodrop" class on row 3 and "nodrag" class on row 5, so you can't pick up row 5 and + you can't drop any row on row 3 (but you can drag it).

    +
    +    $('#table-3').tableDnD({
    +        onDrop: function(table, row) {
    +            alert($.tableDnD.serialize());
    +        }
    +    });
    +
    +
    +
    +

    Ajax result

    +

    Drag and drop in this table to test out serialise and using JQuery.load()

    +
    + + + + + + + +
    1One
    2Two
    3Three (Can't drop on this row)
    4Four
    5Five (Can't drag this row)
    6Six
    +
    +

    This table has multiple TBODYs. The functionality isn't quite working properly. You can only drag the rows inside their +own TBODY, you can't drag them outside it. Now this might or might not be what you want, but unfortunately if you then drop a row outside its TBODY you get a Javascript error because inserting after a sibling doesn't work. This will be fixed in the next version. The header rows all have the classes "nodrop" and "nodrag" so that they can't be dragged or dropped on.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    H1H2H3
    4.1One
    4.2Two
    4.3Three
    4.4Four
    4.5Five
    4.6Six
    H1H2H3
    5.1One
    5.2Two
    5.3Three
    5.4Four
    5.5Five
    5.6Six
    H1H2H3
    6.1One
    6.2Two
    6.3Three
    6.4Four
    6.5Five
    6.6Six
    +
    +

    +The following table demonstrates the use of the default regular expression. The rows have IDs of the +form table5-row-1, table5-row-2, etc., but the regular expression is /[^\-]*$/ (this is the same +as used in the NestedSortable plugin for consistency). +This removes everything before and including the last hyphen, so the serialised string just has 1, 2, 3 etc. +You can replace the regular expression by setting the serializeRegexp option, you can also just set it +to null to stop this behaviour. +

    +
    +    $('#table-5').tableDnD({
    +        onDrop: function(table, row) {
    +            alert($.tableDnD.serialize());
    +        },
    +        dragHandle: "dragHandle"
    +    });
    +
    +
    + + + + + + + +
     1Onesome text
     2Twosome text
     3Threesome text
     4Foursome text
     5Fivesome text
     6Sixsome text
    +
    +

    In fact you will notice that I have also set the dragHandle on this table. This has two effects: firstly only +the cell with the drag handle class is draggable and secondly it doesn't automatically add the cursor: move +style to the row (or the drag handle cell), so you are responsible for setting up the style as you see fit.

    +

    Here I've actually added an extra effect which adds a background image to the first cell in the row whenever +you enter it using the jQuery hover function as follows:

    +
    +    $("#table-5 tr").hover(function() {
    +          $(this.cells[0]).addClass('showDragHandle');
    +    }, function() {
    +          $(this.cells[0]).removeClass('showDragHandle');
    +    });
    +
    +

    This provides a better visualisation of what you can do to the row and where you need to go to drag it (I hope).

    +

    Version History

    + + + + + +
    0.22008-02-20First public release
    0.32008-02-27Added onDragStart option
    Made the scroll amount configurable (default is 5 as before)
    0.42008-03-28Fixed the scrollAmount so that if you set this to zero then it switches off this functionality
    Fixed the auto-scrolling in IE6 thanks to Phil
    Changed the NoDrop attribute to the class "nodrop" (so any row with this class won't allow dropping)
    Changed the NoDrag attribute to the class "nodrag" (so any row with this class can't be dragged)
    Added support for multiple TBODYs--though it's still not perfect
    Added onAllowDrop to allow the developer to customise this behaviour
    Added a serialize() method to return the order of the rows in a form suitable for POSTing back to the server
    0.52008-06-04Changed so that if you specify a dragHandle class it doesn't make the whole row
    draggable
    Improved the serialize method to use a default (and settable) regular expression.
    Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
    +
    + + + + + \ No newline at end of file diff --git a/node_modules/tablednd/tablednd.css b/node_modules/tablednd/tablednd.css new file mode 100644 index 0000000000..c25b07917a --- /dev/null +++ b/node_modules/tablednd/tablednd.css @@ -0,0 +1,340 @@ +body { + color:#333333; + font-family:'Lucida Grande',Verdana,Arial,Sans-Serif; + font-size: 80%; + margin: 1em 9em; + background-color: #F0F0F0; + line-height: 1.4em; +} + +p { + padding: 10px; + width: 60%; +} + +h1 { + color: #114477; +} + +pre.prettyprint { + margin-bottom: 20px; + padding-top: 15px; +} +.prettyprint { + background-color: #F7F7F9; +} +code, pre { + display: block; + margin: 0 0 10px; + line-height: 20px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +pre { + word-break: break-all; + word-wrap: break-word; + white-space: pre; + font-weight:normal; +} + +pre, code { + color:#264A94; + font-family:Monaco,Lucida Console,monospace; + font-size:90%; +} + +div#page { + background-color: white; + padding: 1em; +} + +.tableDemo { + background-color: white; + border: 1px solid #666699; + margin-right: 10px; + padding: 6px; +} + +.tableDemo table { + border: 1px solid silver; +} + +.tableDemo td { + padding: 2px 6px +} + +.tableDemo th { + color: white; + text-shadow: 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A, 0 0 18px #29215A; + border-bottom: 8px double #29215A; + padding: 10px; +} + +#table-2 th { + background-color: #29215A; + color: white; +} + +#table-2 td, th { + padding-right: 8px; +} + +.category td { + background-color: #E4EBF3; +} + +table { + /*position: relative;*/ + border-collapse: separate; + border-spacing: 0; +} +tr { + /*position: relative;*/ + + /*display: block;*/ +} + +.tDnD_whileDrag { + /*z-index: 500;*/ + /*width: 90%;*/ + /*margin: -10px;*/ + /*display: table-cell;*/ + /*color: transparent;*/ + /*width: 0px*/ +} +.tDnD_whileDrag td { + background-color: #eee; + /*-webkit-box-shadow: 11px 5px 12px 2px #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ + -webkit-box-shadow: 6px 3px 5px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset; + /*-moz-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ + /*-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ +} +/*.tDnD_whileDrag td:first-child {*/ + /*-webkit-box-shadow: 5px 4px 5px 1px #111, 0 1px 0 #ccc inset, 1px -1px 0 #ccc inset;*/ + /*-moz-box-shadow: 6px 3px 5px 2px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, 1px 0 0 #ccc inset;*/ + /*-box-shadow: 6px 3px 5px 2px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, 1px 0 0 #ccc inset;*/ +/*}*/ +.tDnD_whileDrag td:last-child { + /*-webkit-box-shadow: 8px 7px 12px 0 #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ + -webkit-box-shadow: 1px 8px 6px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset; + /*-moz-box-shadow: 0 9px 4px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, -1px 0 0 #ccc inset;*/ + /*-box-shadow: 0 9px 4px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, -1px 0 0 #ccc inset;*/ +} + /*-webkit-box-shadow: 0 0 40px white, 10px 10px 15px black;*/ + /*-moz-box-shadow: 0 4px 2px -3px rgba(0, 0, 0, 0.5) inset;*/ + /*-webkit-box-shadow: 0px 0px 40px 1px white, 0px 1px 1px 1px black;*/ + /*box-shadow: 0 4px 2px -3px rgba(0, 0, 0, 0.5) inset;*/ +/*}*/ +/*.tDnD_whileDrag {*/ + /*background-color: black;*/ + /*position: relative;*/ + /*display: block;*/ + /*-moz-box-shadow: 0px 0px 40px 1px white, 0px 1px 1px 1px black;*/ + /*-webkit-box-shadow: 0px 0px 40px 1px white, 0px 1px 1px 1px black;*/ + /*box-shadow: 0px 0px 40px 1px white, 0px 1px 1px 1px black ;*/ + /*-webkit-box-shadow: 0 15px 10px -10px black, 0 1px 4px black, 0 0 10px darkgray inset;*/ + /*-webkit-box-shadow: 0 15px 10px -10px black, 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;*/ + /*-moz-box-shadow: 0 15px 10px -10px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;*/ + /*box-shadow: 0 15px 10px -10px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;*/ + /*width: 90%;*/ + /*height: 90%;*/ + /*width: 100%;*/ + /*overflow: visible;*/ + /*z-index: 10000;*/ + /*opacity: .4;*/ + /*border-collapse: separate;*/ + /*filter:Alpha(Opacity=50);*/ + /*width: auto;*/ + +/*}*/ + +/*tr.alt td {*/ + /*width:200px*/ + /*-ms-transform:rotate(1deg;*/ + /*-moz-transform: rotate(0deg);*/ + /*-webkit-transform:rotate(0deg);*/ + /*-o-transform:rotate(0deg);*/ +/*}*/ + +tr.alt td { + background-color: #ecf6fc; + padding-top: 5px; + padding-bottom: 5px; +} + +td { + padding-top: 5px; + padding-bottom: 5px; + white-space: nowrap; +} + +tr.myDragClass td { + /*position: fixed;*/ + color: yellow; + text-shadow: 0 0 10px black, 0 0 10px black, 0 0 8px black, 0 0 6px black, 0 0 6px black; + background-color: #999; + -webkit-box-shadow: 0 12px 14px -12px #111 inset, 0 -2px 2px -1px #333 inset; +} +tr.myDragClass td:first-child { + -webkit-box-shadow: 0 12px 14px -12px #111 inset, 12px 0 14px -12px #111 inset, 0 -2px 2px -1px #333 inset; +} +tr.myDragClass td:last-child { + -webkit-box-shadow: 0 12px 14px -12px #111 inset, -12px 0 14px -12px #111 inset, 0 -2px 2px -1px #333 inset; +} + +#table-2 { + margin: 0 0 1em 0; padding: 0 +} +tr.nodrop td { + border-bottom: 1px solid #00bb00; + color: #00bb00; +} +tr.nodrag td { + border-bottom: 1px solid #FF6600; + color: #FF6600; +} +div.result { + background-color: #F7F7F9; +} + +tr.alt tr:after, .group:after { + visibility: hidden; + display: block; + content: ""; + clear: both; + height: 0; +} + +table input, +tr td input, +tr input, +tr.myDragClass input, +tbody tr td input { + z-index: -10; + text-align: right; + float: right; + height: 12px; + margin-top: 5px; + margin-bottom: 5px; +} + +td.dragHandle { + +} + +td.showDragHandle { + background-image: url(images/updown2.gif); + background-repeat: no-repeat; + background-position: center center; + cursor: move; +} + +.versionHistory td { + vertical-align: top; + padding: 0.3em; + white-space: normal; +} + +div.indent { + width: 30px; + float: left; +} + +#sprintlist_table th { + color: white; + /*border-style: ;*/ + text-shadow: 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600, 0 0 18px #FF6600; + border-bottom: 14px double #FF6600; + padding: 10px; +} +.sprintlist-drag td.small_buttons div button { + /*font-size: xx-small;*/ + /*height: 18px;*/ + /*color: #333;*/ + /*cursor: pointer;*/ + /*background-color: #f4a460;*/ + box-shadow:0px 2px 3px black inset; + -moz-box-shadow:0px 2px 3px black inset; + -webkit-box-shadow:0px 2px 3px black inset; + /*border: 0px;*/ + /*background-color: #ccc;*/ +} +.sprintlist-drag td.small_buttons div button:first-child { + box-shadow:2px 2px 3px black inset; + -moz-box-shadow:2px 2px 3px black inset; + -webkit-box-shadow:2px 2px 3px black inset; + /*border: 0px;*/ + /*background-color: #ccc;*/ +} +.sprintlist-drag td { + background-color: #f4a460; + /*-webkit-box-shadow: 11px 5px 12px 2px #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ + -webkit-box-shadow: 6px 3px 5px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset; + /*-moz-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ + /*-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ +} +.sprintlist-drag td:last-child { + /*-webkit-box-shadow: 8px 7px 12px 0 #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;*/ + -webkit-box-shadow: 1px 8px 6px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset; +/*-moz-box-shadow: 0 9px 4px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, -1px 0 0 #ccc inset;*/ +} +tr.group_heading td eojq + border-bottom: 8px double #FF6600; + color: #FF6600; + font-size: larger; + font-weight: bolder; +} + +td.small_buttons div { + display: inline-block; + position: relative; +} +#table-6 tr { + background-color: red; + z-index: -1000; + /*background-color:rgb(165, 182, 229);*/ + display: block; + margin-bottom: 5px; + /*box-shadow:0 0 0 black;*/ + /*-moz-box-shadow:0 0 0 black,;*/ + /*-webkit-box-shadow:0 0 0 black;*/ +} +#table-6 td { + padding:5px; + /*text-align:left;*/ +} + +#table-7 { + border: #000000 solid 1px; +} + +td.small_buttons div button { + font-size: xx-small; + height: 18px; + color: #333; + cursor: pointer; + background-color: whiteSmoke; +} + +td.small_buttons div button:first-child +{ + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +td.small_buttons div button:last-child +{ + margin-left: -2px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-topright: 4px; +} diff --git a/node_modules/tablednd/test/test.jquery.tablednd.js b/node_modules/tablednd/test/test.jquery.tablednd.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/tablednd/yarn.lock b/node_modules/tablednd/yarn.lock new file mode 100644 index 0000000000..e0e1e5828a --- /dev/null +++ b/node_modules/tablednd/yarn.lock @@ -0,0 +1,3177 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0-beta.35": + version "7.0.0-beta.36" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.36.tgz#2349d7ec04b3a06945ae173280ef8579b63728e4" + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +"@types/node@*": + version "8.5.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.2.tgz#83b8103fa9a2c2e83d78f701a9aa7c9539739aa5" + +abab@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +acorn-globals@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" + dependencies: + acorn "^5.0.0" + +acorn@^5.0.0, acorn@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^5.1.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-escapes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" + +ansi-regex@^0.2.0, ansi-regex@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.1.0, ansi-styles@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + dependencies: + default-require-extensions "^1.0.0" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +"argparse@~ 0.1.11": + version "0.1.16" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-0.1.16.tgz#cfd01e0fbba3d6caed049fbd758d40f65196f57c" + dependencies: + underscore "~1.7.0" + underscore.string "~2.4.0" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + +async@^1.4.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^2.1.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" + dependencies: + lodash "^4.14.0" + +async@~0.1.22: + version "0.1.22" + resolved "https://registry.yarnpkg.com/async/-/async-0.1.22.tgz#0fc1aaa088a0e3ef0ebe2d8831bab0dcf8845061" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.2.1, aws4@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.0.0, babel-core@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.0" + debug "^2.6.8" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.7" + slash "^1.0.0" + source-map "^0.5.6" + +babel-generator@^6.18.0, babel-generator@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.6" + trim-right "^1.0.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-jest@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.0.4.tgz#533c46de37d7c9d7612f408c76314be9277e0c26" + dependencies: + babel-plugin-istanbul "^4.1.5" + babel-preset-jest "^22.0.3" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-istanbul@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz#6760cdd977f411d3e175bb064f2bc327d99b2b6e" + dependencies: + find-up "^2.1.0" + istanbul-lib-instrument "^1.7.5" + test-exclude "^4.1.1" + +babel-plugin-jest-hoist@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.0.3.tgz#62cde5fe962fd41ae89c119f481ca5cd7dd48bb4" + +babel-plugin-syntax-object-rest-spread@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-preset-jest@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-22.0.3.tgz#e2bb6f6b4a509d3ea0931f013db78c5a84856693" + dependencies: + babel-plugin-jest-hoist "^22.0.3" + babel-plugin-syntax-object-rest-spread "^6.13.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.18.0, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.18.0, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +bindings@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +boom@4.x.x: + version "4.3.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + dependencies: + hoek "4.x.x" + +boom@5.x.x: + version "5.2.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + dependencies: + hoek "4.x.x" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +browser-process-hrtime@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" + +browser-resolve@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + dependencies: + resolve "1.1.7" + +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + dependencies: + pako "~0.2.0" + +bser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" + dependencies: + node-int64 "^0.4.0" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" + dependencies: + ansi-styles "^1.1.0" + escape-string-regexp "^1.0.0" + has-ansi "^0.1.0" + strip-ansi "^0.3.0" + supports-color "^0.2.0" + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + +ci-info@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.2.tgz#03561259db48d0474c8bdc90f5b47b068b6bbfb4" + +cli@0.6.x: + version "0.6.6" + resolved "https://registry.yarnpkg.com/cli/-/cli-0.6.6.tgz#02ad44a380abf27adac5e6f0cdd7b043d74c53e3" + dependencies: + exit "0.1.2" + glob "~ 3.2.1" + +cli@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cli/-/cli-1.0.1.tgz#22817534f24bfa4950c34d532d48ecbc621b8c14" + dependencies: + exit "0.1.2" + glob "^7.1.1" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +coffee-script@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.3.3.tgz#150d6b4cb522894369efed6a2101c20bc7f4a4f4" + +color-convert@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + dependencies: + color-name "^1.1.1" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +colors@~0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +console-browserify@1.1.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +content-type-parser@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" + +convert-source-map@^1.4.0, convert-source-map@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +core-js@^2.4.0, core-js@^2.5.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +cryptiles@3.x.x: + version "3.1.2" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" + dependencies: + boom "5.x.x" + +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" + +"cssstyle@>= 0.2.37 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + dependencies: + cssom "0.3.x" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +dateformat@1.0.2-1.2.3: + version "1.0.2-1.2.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.2-1.2.3.tgz#b0220c02de98617433b72851cf47de3df2cdbee9" + +debug@^2.2.0, debug@^2.6.8: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + dependencies: + strip-bom "^2.0.0" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + +diff@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" + +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.0.tgz#81fe5df81b3f057052cde3a9fa9bf536a85b9ab0" + +domhandler@2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + dependencies: + domelementtype "1" + +domutils@1.5: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +entities@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" + +entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.5.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.5.6" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +"esprima@~ 1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + +estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +eventemitter2@~0.4.13: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + +exec-sh@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.1.tgz#163b98a6e89e6b65b47c2a28d215bc1f63989c38" + dependencies: + merge "^1.1.3" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit@0.1.2, exit@0.1.x, exit@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +expect@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-22.0.3.tgz#bb486de7d41bf3eb60d3b16dfd1c158a4d91ddfa" + dependencies: + ansi-styles "^3.2.0" + jest-diff "^22.0.3" + jest-get-type "^22.0.3" + jest-matcher-utils "^22.0.3" + jest-message-util "^22.0.3" + jest-regex-util "^22.0.3" + +extend@~3.0.0, extend@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fb-watchman@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" + dependencies: + bser "^2.0.0" + +figures@^1.0.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fileset@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" + dependencies: + glob "^7.0.3" + minimatch "^3.0.3" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +findup-sync@~0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.1.3.tgz#7f3e7a97b82392c653bf06589bd85190e93c3683" + dependencies: + glob "~3.2.9" + lodash "~2.4.1" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.39" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.0.2, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +getobject@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/getobject/-/getobject-0.1.0.tgz#047a449789fa160d018f5486ed91320b6ec7885c" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +"glob@~ 3.2.1", glob@~3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + dependencies: + inherits "2" + minimatch "0.3" + +glob@~3.1.21: + version "3.1.21" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" + dependencies: + graceful-fs "~1.2.0" + inherits "1" + minimatch "~0.2.11" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +graceful-fs@~1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + +grunt-contrib-jshint@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/grunt-contrib-jshint/-/grunt-contrib-jshint-0.10.0.tgz#57ebccca87e8f327af6645d8a3c586d4845e4d81" + dependencies: + hooker "~0.2.3" + jshint "~2.5.0" + +grunt-contrib-uglify@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/grunt-contrib-uglify/-/grunt-contrib-uglify-0.6.0.tgz#3a271d4dc4daba64691d0d0d08550ec54a7ec0ab" + dependencies: + chalk "^0.5.1" + lodash "^2.4.1" + maxmin "^1.0.0" + uglify-js "^2.4.0" + uri-path "0.0.2" + +grunt-legacy-log-utils@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz#c0706b9dd9064e116f36f23fe4e6b048672c0f7e" + dependencies: + colors "~0.6.2" + lodash "~2.4.1" + underscore.string "~2.3.3" + +grunt-legacy-log@~0.1.0: + version "0.1.3" + resolved "https://registry.yarnpkg.com/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz#ec29426e803021af59029f87d2f9cd7335a05531" + dependencies: + colors "~0.6.2" + grunt-legacy-log-utils "~0.1.1" + hooker "~0.2.3" + lodash "~2.4.1" + underscore.string "~2.3.3" + +grunt-legacy-util@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz#93324884dbf7e37a9ff7c026dff451d94a9e554b" + dependencies: + async "~0.1.22" + exit "~0.1.1" + getobject "~0.1.0" + hooker "~0.2.3" + lodash "~0.9.2" + underscore.string "~2.2.1" + which "~1.0.5" + +grunt@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/grunt/-/grunt-0.4.5.tgz#56937cd5194324adff6d207631832a9d6ba4e7f0" + dependencies: + async "~0.1.22" + coffee-script "~1.3.3" + colors "~0.6.2" + dateformat "1.0.2-1.2.3" + eventemitter2 "~0.4.13" + exit "~0.1.1" + findup-sync "~0.1.2" + getobject "~0.1.0" + glob "~3.1.21" + grunt-legacy-log "~0.1.0" + grunt-legacy-util "~0.2.0" + hooker "~0.2.3" + iconv-lite "~0.2.11" + js-yaml "~2.0.5" + lodash "~0.9.2" + minimatch "~0.2.12" + nopt "~1.0.10" + rimraf "~2.2.8" + underscore.string "~2.2.1" + which "~1.0.5" + +gzip-size@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-1.0.0.tgz#66cf8b101047227b95bace6ea1da0c177ed5c22f" + dependencies: + browserify-zlib "^0.1.4" + concat-stream "^1.4.1" + +handlebars@^4.0.3: + version "4.0.11" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +has-ansi@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" + dependencies: + ansi-regex "^0.2.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hawk@3.1.3, hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hawk@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + dependencies: + boom "4.x.x" + cryptiles "3.x.x" + hoek "4.x.x" + sntp "2.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hoek@4.x.x: + version "4.2.0" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hooker@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/hooker/-/hooker-0.2.3.tgz#b834f723cc4a242aa65963459df6d984c5d3d959" + +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +html-encoding-sniffer@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + dependencies: + whatwg-encoding "^1.0.1" + +htmlparser2@3.8.x: + version "3.8.3" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" + dependencies: + domelementtype "1" + domhandler "2.3" + domutils "1.5" + entities "1.0" + readable-stream "1.1" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +iconv-lite@~0.2.11: + version "0.2.11" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.2.11.tgz#1ce60a3a57864a292d1321ff4609ca4bb965adc8" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" + +inherits@2, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +invariant@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + +is-ci@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" + dependencies: + ci-info "^1.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +istanbul-api@^1.1.14: + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.2.1.tgz#0c60a0515eb11c7d65c6b50bba2c6e999acd8620" + dependencies: + async "^2.1.4" + fileset "^2.0.2" + istanbul-lib-coverage "^1.1.1" + istanbul-lib-hook "^1.1.0" + istanbul-lib-instrument "^1.9.1" + istanbul-lib-report "^1.1.2" + istanbul-lib-source-maps "^1.2.2" + istanbul-reports "^1.1.3" + js-yaml "^3.7.0" + mkdirp "^0.5.1" + once "^1.4.0" + +istanbul-lib-coverage@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da" + +istanbul-lib-hook@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b" + dependencies: + append-transform "^0.4.0" + +istanbul-lib-instrument@^1.7.5, istanbul-lib-instrument@^1.8.0, istanbul-lib-instrument@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz#250b30b3531e5d3251299fdd64b0b2c9db6b558e" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.1.1" + semver "^5.3.0" + +istanbul-lib-report@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.2.tgz#922be27c13b9511b979bd1587359f69798c1d425" + dependencies: + istanbul-lib-coverage "^1.1.1" + mkdirp "^0.5.1" + path-parse "^1.0.5" + supports-color "^3.1.2" + +istanbul-lib-source-maps@^1.2.1, istanbul-lib-source-maps@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.2.tgz#750578602435f28a0c04ee6d7d9e0f2960e62c1c" + dependencies: + debug "^3.1.0" + istanbul-lib-coverage "^1.1.1" + mkdirp "^0.5.1" + rimraf "^2.6.1" + source-map "^0.5.3" + +istanbul-reports@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.3.tgz#3b9e1e8defb6d18b1d425da8e8b32c5a163f2d10" + dependencies: + handlebars "^4.0.3" + +jest-changed-files@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-22.0.3.tgz#3771315acfa24a0ed7e6c545de620db6f1b2d164" + dependencies: + throat "^4.0.0" + +jest-cli@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.0.4.tgz#0052abaad45c57861c05da8ab5d27bad13ad224d" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + is-ci "^1.0.10" + istanbul-api "^1.1.14" + istanbul-lib-coverage "^1.1.1" + istanbul-lib-instrument "^1.8.0" + istanbul-lib-source-maps "^1.2.1" + jest-changed-files "^22.0.3" + jest-config "^22.0.4" + jest-environment-jsdom "^22.0.4" + jest-get-type "^22.0.3" + jest-haste-map "^22.0.3" + jest-message-util "^22.0.3" + jest-regex-util "^22.0.3" + jest-resolve-dependencies "^22.0.3" + jest-runner "^22.0.4" + jest-runtime "^22.0.4" + jest-snapshot "^22.0.3" + jest-util "^22.0.4" + jest-worker "^22.0.3" + micromatch "^2.3.11" + node-notifier "^5.1.2" + realpath-native "^1.0.0" + rimraf "^2.5.4" + slash "^1.0.0" + string-length "^2.0.0" + strip-ansi "^4.0.0" + which "^1.2.12" + yargs "^10.0.3" + +jest-config@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.0.4.tgz#9c2a46c0907b1a1af54d9cdbf18e99b447034e11" + dependencies: + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^22.0.4" + jest-environment-node "^22.0.4" + jest-get-type "^22.0.3" + jest-jasmine2 "^22.0.4" + jest-regex-util "^22.0.3" + jest-resolve "^22.0.4" + jest-util "^22.0.4" + jest-validate "^22.0.3" + pretty-format "^22.0.3" + +jest-diff@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-22.0.3.tgz#ffed5aba6beaf63bb77819ba44dd520168986321" + dependencies: + chalk "^2.0.1" + diff "^3.2.0" + jest-get-type "^22.0.3" + pretty-format "^22.0.3" + +jest-docblock@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.0.3.tgz#c33aa22682b9fc68a5373f5f82994428a2ded601" + dependencies: + detect-newline "^2.1.0" + +jest-environment-jsdom@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.0.4.tgz#5723d4e724775ed38948de792e62f2d6a7f452df" + dependencies: + jest-mock "^22.0.3" + jest-util "^22.0.4" + jsdom "^11.5.1" + +jest-environment-node@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.0.4.tgz#068671f85a545f96a5469be3a3dd228fca79c709" + dependencies: + jest-mock "^22.0.3" + jest-util "^22.0.4" + +jest-get-type@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.0.3.tgz#fa894b677c0fcd55eff3fd8ee28c7be942e32d36" + +jest-haste-map@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.0.3.tgz#c9ecb5c871c5465d4bde4139e527fa0dc784aa2d" + dependencies: + fb-watchman "^2.0.0" + graceful-fs "^4.1.11" + jest-docblock "^22.0.3" + jest-worker "^22.0.3" + micromatch "^2.3.11" + sane "^2.0.0" + +jest-jasmine2@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.0.4.tgz#f7c0965116efe831ec674dc954b0134639b3dcee" + dependencies: + callsites "^2.0.0" + chalk "^2.0.1" + expect "^22.0.3" + graceful-fs "^4.1.11" + jest-diff "^22.0.3" + jest-matcher-utils "^22.0.3" + jest-message-util "^22.0.3" + jest-snapshot "^22.0.3" + source-map-support "^0.5.0" + +jest-leak-detector@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-22.0.3.tgz#b64904f0e8954a11edb79b0809ff4717fa762d99" + dependencies: + pretty-format "^22.0.3" + optionalDependencies: + weak "^1.0.1" + +jest-matcher-utils@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.0.3.tgz#2ec15ca1af7dcabf4daddc894ccce224b948674e" + dependencies: + chalk "^2.0.1" + jest-get-type "^22.0.3" + pretty-format "^22.0.3" + +jest-message-util@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-22.0.3.tgz#bf674b2762ef2dd53facf2136423fcca264976df" + dependencies: + "@babel/code-frame" "^7.0.0-beta.35" + chalk "^2.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + stack-utils "^1.0.1" + +jest-mock@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.0.3.tgz#c875e47b5b729c6c020a2fab317b275c0cf88961" + +jest-regex-util@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-22.0.3.tgz#c5c10229de5ce2b27bf4347916d95b802ae9aa4d" + +jest-resolve-dependencies@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-22.0.3.tgz#202ddf370069702cd1865a1952fcc7e52c92720e" + dependencies: + jest-regex-util "^22.0.3" + +jest-resolve@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.0.4.tgz#a6e47f55e9388c7341b5e9732aedc6fe30906121" + dependencies: + browser-resolve "^1.11.2" + chalk "^2.0.1" + +jest-runner@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.0.4.tgz#3aa43a31b05ce8271539df580c2eb916023d3367" + dependencies: + jest-config "^22.0.4" + jest-docblock "^22.0.3" + jest-haste-map "^22.0.3" + jest-jasmine2 "^22.0.4" + jest-leak-detector "^22.0.3" + jest-message-util "^22.0.3" + jest-runtime "^22.0.4" + jest-util "^22.0.4" + jest-worker "^22.0.3" + throat "^4.0.0" + +jest-runtime@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.0.4.tgz#8f69aa7b5fbb3acd35dc262cbf654e563f69b7b4" + dependencies: + babel-core "^6.0.0" + babel-jest "^22.0.4" + babel-plugin-istanbul "^4.1.5" + chalk "^2.0.1" + convert-source-map "^1.4.0" + graceful-fs "^4.1.11" + jest-config "^22.0.4" + jest-haste-map "^22.0.3" + jest-regex-util "^22.0.3" + jest-resolve "^22.0.4" + jest-util "^22.0.4" + json-stable-stringify "^1.0.1" + micromatch "^2.3.11" + realpath-native "^1.0.0" + slash "^1.0.0" + strip-bom "3.0.0" + write-file-atomic "^2.1.0" + yargs "^10.0.3" + +jest-snapshot@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-22.0.3.tgz#a949b393781d2fdb4773f6ea765dd67ad1da291e" + dependencies: + chalk "^2.0.1" + jest-diff "^22.0.3" + jest-matcher-utils "^22.0.3" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^22.0.3" + +jest-util@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.0.4.tgz#d920a513e0645aaab030cee38e4fe7d5bed8bb6d" + dependencies: + callsites "^2.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.11" + is-ci "^1.0.10" + jest-message-util "^22.0.3" + jest-validate "^22.0.3" + mkdirp "^0.5.1" + +jest-validate@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.0.3.tgz#2850d949a36c48b1a40f7eebae1d8539126f7829" + dependencies: + chalk "^2.0.1" + jest-get-type "^22.0.3" + leven "^2.1.0" + pretty-format "^22.0.3" + +jest-worker@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.0.3.tgz#30433faca67814a8f80559f75ab2ceaa61332fd2" + dependencies: + merge-stream "^1.0.1" + +jest@^22.0.4: + version "22.0.4" + resolved "https://registry.yarnpkg.com/jest/-/jest-22.0.4.tgz#d3cf560ece6b825b115dce80b9826ceb40f87961" + dependencies: + jest-cli "^22.0.4" + +js-tokens@^3.0.0, js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.7.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-2.0.5.tgz#a25ae6509999e97df278c6719da11bd0687743a8" + dependencies: + argparse "~ 0.1.11" + esprima "~ 1.0.2" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsdom@^11.5.1: + version "11.5.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.5.1.tgz#5df753b8d0bca20142ce21f4f6c039f99a992929" + dependencies: + abab "^1.0.3" + acorn "^5.1.2" + acorn-globals "^4.0.0" + array-equal "^1.0.0" + browser-process-hrtime "^0.1.2" + content-type-parser "^1.0.1" + cssom ">= 0.3.2 < 0.4.0" + cssstyle ">= 0.2.37 < 0.3.0" + domexception "^1.0.0" + escodegen "^1.9.0" + html-encoding-sniffer "^1.0.1" + left-pad "^1.2.0" + nwmatcher "^1.4.3" + parse5 "^3.0.2" + pn "^1.0.0" + request "^2.83.0" + request-promise-native "^1.0.3" + sax "^1.2.1" + symbol-tree "^3.2.1" + tough-cookie "^2.3.3" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.1" + whatwg-url "^6.3.0" + xml-name-validator "^2.0.1" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jshint@^2.9.3: + version "2.9.5" + resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.9.5.tgz#1e7252915ce681b40827ee14248c46d34e9aa62c" + dependencies: + cli "~1.0.0" + console-browserify "1.1.x" + exit "0.1.x" + htmlparser2 "3.8.x" + lodash "3.7.x" + minimatch "~3.0.2" + shelljs "0.3.x" + strip-json-comments "1.0.x" + +jshint@~2.5.0: + version "2.5.11" + resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.5.11.tgz#e2d95858bbb1aa78300108a2e81099fb095622e0" + dependencies: + cli "0.6.x" + console-browserify "1.1.x" + exit "0.1.x" + htmlparser2 "3.8.x" + minimatch "1.0.x" + shelljs "0.3.x" + strip-json-comments "1.0.x" + underscore "1.6.x" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +left-pad@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee" + +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + +lodash@3.7.x: + version "3.7.0" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45" + +lodash@^2.4.1, lodash@~2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" + +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +lodash@~0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-0.9.2.tgz#8f3499c5245d346d682e5b0d3b40767e09f1a92c" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + dependencies: + tmpl "1.0.x" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +maxmin@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/maxmin/-/maxmin-1.1.0.tgz#71365e84a99dd8f8b3f7d5fde2f00d1e7f73be61" + dependencies: + chalk "^1.0.0" + figures "^1.0.1" + gzip-size "^1.0.0" + pretty-bytes "^1.0.0" + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + +meow@^3.1.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + dependencies: + readable-stream "^2.0.1" + +merge@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" + +micromatch@^2.1.5, micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: + version "2.1.17" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + dependencies: + mime-db "~1.30.0" + +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-1.0.0.tgz#e0dd2120b49e1b724ce8d714c520822a9438576d" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimatch@~0.2.11, minimatch@~0.2.12: + version "0.2.14" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +"mkdirp@>=0.5 0", mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +nan@^2.0.5, nan@^2.3.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + +node-notifier@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.1.2.tgz#2fa9e12605fa10009d44549d6fcd8a63dde0e4ff" + dependencies: + growly "^1.3.0" + semver "^5.3.0" + shellwords "^0.1.0" + which "^1.2.12" + +node-pre-gyp@^0.6.39: + version "0.6.39" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" + dependencies: + detect-libc "^1.0.2" + hawk "3.1.3" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +nwmatcher@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" + +oauth-sign@~0.8.1, oauth-sign@~0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@^1.3.0, once@^1.3.3, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse5@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + dependencies: + "@types/node" "*" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.0.0.tgz#1cf5a30b0d806cd18f88fc41a6b5d4ad615b3ba9" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +pretty-bytes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84" + dependencies: + get-stdin "^4.0.1" + meow "^3.1.0" + +pretty-format@^22.0.3: + version "22.0.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.0.3.tgz#a2bfa59fc33ad24aa4429981bb52524b41ba5dd7" + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + +private@^0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +punycode@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +qs@~6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +randomatic@^1.1.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +rc@^1.1.7: + version "1.2.2" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@1.1: + version "1.1.13" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +realpath-native@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.0.tgz#7885721a83b43bd5327609f0ddecb2482305fdf0" + dependencies: + util.promisify "^1.0.0" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request-promise-core@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" + dependencies: + lodash "^4.13.1" + +request-promise-native@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" + dependencies: + request-promise-core "1.1.1" + stealthy-require "^1.1.0" + tough-cookie ">=2.3.3" + +request@2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +request@^2.83.0: + version "2.83.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +rimraf@~2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +sane@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-2.2.0.tgz#d6d2e2fcab00e3d283c93b912b7c3a20846f1d56" + dependencies: + anymatch "^1.3.0" + exec-sh "^0.2.0" + fb-watchman "^2.0.0" + minimatch "^3.0.2" + minimist "^1.1.1" + walker "~1.0.5" + watch "~0.18.0" + optionalDependencies: + fsevents "^1.1.1" + +sax@^1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +shelljs@0.3.x: + version "0.3.0" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.3.0.tgz#3596e6307a781544f591f37da618360f31db57b1" + +shellwords@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sntp@2.x.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" + dependencies: + hoek "4.x.x" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map-support@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.0.tgz#2018a7ad2bdf8faf2691e5fddab26bed5a2bacab" + dependencies: + source-map "^0.6.0" + +source-map@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stack-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.1.tgz#d4f33ab54e8e38778b0ca5cfd3b3afb12db68620" + +stealthy-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +stringstream@~0.0.4, stringstream@~0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" + dependencies: + ansi-regex "^0.2.1" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +supports-color@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + dependencies: + has-flag "^2.0.0" + +symbol-tree@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + +tar-pack@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +test-exclude@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26" + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +tough-cookie@>=2.3.3, tough-cookie@^2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" + dependencies: + punycode "^1.4.1" + +tr46@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + dependencies: + punycode "^2.1.0" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +uglify-js@^2.4.0, uglify-js@^2.6: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +underscore.string@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.2.1.tgz#d7c0fa2af5d5a1a67f4253daee98132e733f0f19" + +underscore.string@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.3.3.tgz#71c08bf6b428b1133f37e78fa3a21c82f7329b0d" + +underscore.string@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b" + +underscore@1.6.x: + version "1.6.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" + +underscore@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + +uri-path@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/uri-path/-/uri-path-0.0.2.tgz#803eb01f2feb17927dcce0f6187e72b75f53f554" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util.promisify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +uuid@^3.0.0, uuid@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + dependencies: + makeerror "1.0.x" + +watch@~0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986" + dependencies: + exec-sh "^0.2.0" + minimist "^1.2.0" + +weak@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/weak/-/weak-1.0.1.tgz#ab99aab30706959aa0200cb8cf545bb9cb33b99e" + dependencies: + bindings "^1.2.1" + nan "^2.0.5" + +webidl-conversions@^4.0.1, webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + +whatwg-encoding@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3" + dependencies: + iconv-lite "0.4.19" + +whatwg-url@^6.3.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.0.tgz#08fdf2b9e872783a7a1f6216260a1d66cc722e08" + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.0" + webidl-conversions "^4.0.1" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@^1.2.12, which@^1.2.9: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + +which@~1.0.5: + version "1.0.9" + resolved "https://registry.yarnpkg.com/which/-/which-1.0.9.tgz#460c1da0f810103d0321a9b633af9e575e64486f" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-atomic@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +xml-name-validator@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yargs-parser@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" + dependencies: + camelcase "^4.1.0" + +yargs@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.0.3.tgz#6542debd9080ad517ec5048fb454efe9e4d4aaae" + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^8.0.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" diff --git a/package.json b/package.json new file mode 100644 index 0000000000..0eafedb4ff --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "tablednd": "^1.0.3" + } +} diff --git a/plugin.php b/plugin.php index 94da5c3c78..a6e4a58a9a 100644 --- a/plugin.php +++ b/plugin.php @@ -1,43 +1,45 @@ -name = 'TLTest'; - $this->description = 'Test Plugin'; - - $this->version = '1.0'; - - $this->author = 'Testlink'; - $this->contact = 'raja@star-systems.in'; - $this->url = 'http://www.collab.net'; - } - - function config() - { - return array( - 'config1' => '', - 'config2' => 0 - ); - } - - function hooks() - { - $hooks = array( - 'EVENT_TEST_SUITE_CREATE' => 'testsuite_create', - 'EVENT_TEST_PROJECT_CREATE' => 'testproject_create', - 'EVENT_TEST_PROJECT_UPDATE' => 'testproject_update', - 'EVENT_TEST_CASE_UPDATE' => 'testcase_update', - 'EVENT_TEST_REQUIREMENT_CREATE' => 'testrequirement_create', - 'EVENT_TEST_REQUIREMENT_UPDATE' => 'testrequirement_update', - 'EVENT_TEST_REQUIREMENT_DELETE' => 'testrequirement_delete', - 'EVENT_EXECUTE_TEST' => 'testExecute', - 'EVENT_LEFTMENU_TOP' => 'top_link', - 'EVENT_LEFTMENU_BOTTOM' => 'bottom_link', - 'EVENT_RIGHTMENU_TOP' => 'right_top_link', - 'EVENT_RIGHTMENU_BOTTOM' => 'right_bottom_link', - 'EVENT_TESTRUN_DISPLAY' => 'testrun_display_block' - ); - return $hooks; - } - - function testsuite_create($args) - { - $arg = func_get_args(); // To get all the arguments - $db = $this->db; // To show how to get a Database Connection - echo plugin_lang_get("testsuite_display_message"); - tLog("Im in testsuite create", "WARNING"); - } - - function testproject_create() - { - $arg = func_get_args(); // To get all the arguments - tLog("In TestProject Create with id: " . $arg[1] . ", name: " . $arg[2] . ", prefix: " . $arg[3], "WARNING"); - } - - function testproject_update() - { - $arg = func_get_args(); // To get all the arguments - tLog("In TestProject Update with id: " . $arg[1] . ", name: " . $arg[2] . ", prefix: " . $arg[3], "WARNING"); - } - - function testcase_update() - { - $arg = func_get_args(); // To get all the arguments - tLog("In TestCase Update with id: " . $arg[1] . ", planid: " . $arg[2] . ", title: " . $arg[3] . ", summary" . $arg[4], "WARNING"); - } - - function testrequirement_create() - { - $arg = func_get_args(); // To get all the arguments - tLog("In TestRequirement Create with id: " . $arg[1], "WARNING"); - } - - function testrequirement_update() - { - $arg = func_get_args(); // To get all the arguments - tLog("In TestRequirement Update with id: " . $arg[1], "WARNING"); - } - - function testrequirement_delete() - { - $arg = func_get_args(); // To get all the arguments - tLog("In TestRequirement Delete with id: " . $arg[1], "WARNING"); - } - - function testExecute() { - $arg = func_get_args(); // To get all the arguments - tLog("In TestRun with testrunid: " . $arg[1] . ", planid: " . $arg[2] . ", buildid: " . $arg[3] . ", testcaseid: " . $arg[4] . ", Notes: " . $arg[5] . ", Status: " . $arg[6], "WARNING"); - } - - function testrun_display_block() { - $args = func_get_args(); - // $args details: $arg[1] -> Testplan Id, $arg[2] -> Build Id, $arg[3] ->TestCase Id, $arg[4] -> TestCase Version Id - return ''; - } - - function bottom_link() - { - $tLink['href'] = ''; - $tLink['label'] = plugin_lang_get('left_bottom_link'); - return $tLink; - } - - function top_link() - { - $tLink['href'] = plugin_page('config.php'); - $tLink['label'] = plugin_lang_get('config'); - return $tLink; - } - - function right_top_link() - { - $tLink['href'] = ''; - $tLink['label'] = plugin_lang_get('right_top_link'); - return $tLink; - } - - function right_bottom_link() - { - $tLink['href'] = ''; - $tLink['label'] = plugin_lang_get('right_bottom_link'); - return $tLink; - } - +name = 'TLTest'; + $this->description = 'Test Plugin'; + + $this->version = '1.0'; + + $this->author = 'Testlink'; + $this->contact = 'raja@star-systems.in'; + $this->url = 'http://www.collab.net'; + } + + function config() + { + return array( + 'config1' => '', + 'config2' => 0 + ); + } + + function hooks() + { + return array( + 'EVENT_TEST_SUITE_CREATE' => 'testsuite_create', + 'EVENT_TEST_PROJECT_CREATE' => 'testproject_create', + 'EVENT_TEST_PROJECT_UPDATE' => 'testproject_update', + 'EVENT_TEST_CASE_UPDATE' => 'testcase_update', + 'EVENT_TEST_REQUIREMENT_CREATE' => 'testrequirement_create', + 'EVENT_TEST_REQUIREMENT_UPDATE' => 'testrequirement_update', + 'EVENT_TEST_REQUIREMENT_DELETE' => 'testrequirement_delete', + 'EVENT_EXECUTE_TEST' => 'testExecute', + 'EVENT_LEFTMENU_TOP' => 'top_link', + 'EVENT_LEFTMENU_BOTTOM' => 'bottom_link', + 'EVENT_RIGHTMENU_TOP' => 'right_top_link', + 'EVENT_RIGHTMENU_BOTTOM' => 'right_bottom_link', + 'EVENT_TESTRUN_DISPLAY' => 'testrun_display_block' + ); + } + + function testsuite_create($args) + { + $arg = func_get_args(); // To get all the arguments + $db = $this->db; // To show how to get a Database Connection + echo plugin_lang_get("testsuite_display_message"); + tLog("Im in testsuite create", "WARNING"); + } + + function testproject_create() + { + $arg = func_get_args(); // To get all the arguments + tLog( + "In TestProject Create with id: " . $arg[1] . ", name: " . $arg[2] . + ", prefix: " . $arg[3], "WARNING"); + } + + function testproject_update() + { + $arg = func_get_args(); // To get all the arguments + tLog( + "In TestProject Update with id: " . $arg[1] . ", name: " . $arg[2] . + ", prefix: " . $arg[3], "WARNING"); + } + + function testcase_update() + { + $arg = func_get_args(); // To get all the arguments + tLog( + "In TestCase Update with id: " . $arg[1] . ", planid: " . $arg[2] . + ", title: " . $arg[3] . ", summary" . $arg[4], "WARNING"); + } + + function testrequirement_create() + { + $arg = func_get_args(); // To get all the arguments + tLog("In TestRequirement Create with id: " . $arg[1], "WARNING"); + } + + function testrequirement_update() + { + $arg = func_get_args(); // To get all the arguments + tLog("In TestRequirement Update with id: " . $arg[1], "WARNING"); + } + + function testrequirement_delete() + { + $arg = func_get_args(); // To get all the arguments + tLog("In TestRequirement Delete with id: " . $arg[1], "WARNING"); + } + + function testExecute() + { + $arg = func_get_args(); // To get all the arguments + tLog( + "In TestRun with testrunid: " . $arg[1] . ", planid: " . $arg[2] . + ", buildid: " . $arg[3] . ", testcaseid: " . $arg[4] . ", Notes: " . + $arg[5] . ", Status: " . $arg[6], "WARNING"); + } + + function testrun_display_block() + { + $args = func_get_args(); + // $args details: $arg[1] -> Testplan Id, $arg[2] -> Build Id, $arg[3] ->TestCase Id, $arg[4] -> TestCase Version Id + return ''; + } + + function bottom_link() + { + $tLink['href'] = ''; + $tLink['label'] = plugin_lang_get('left_bottom_link'); + return $tLink; + } + + function top_link() + { + $tLink['href'] = plugin_page('config.php'); + $tLink['label'] = plugin_lang_get('config'); + return $tLink; + } + + function right_top_link() + { + $tLink['href'] = ''; + $tLink['label'] = plugin_lang_get('right_top_link'); + return $tLink; + } + + function right_bottom_link() + { + $tLink['href'] = ''; + $tLink['label'] = plugin_lang_get('right_bottom_link'); + return $tLink; + } } diff --git a/plugins/TLTest/pages/config.php b/plugins/TLTest/pages/config.php index befbdd7952..248a15568c 100644 --- a/plugins/TLTest/pages/config.php +++ b/plugins/TLTest/pages/config.php @@ -1,37 +1,36 @@ -message = plugin_lang_get('config_page_saved'); // Confirm message - - // Assign to Smarty - $smarty->assign('gui',$gui); - $smarty->display(plugin_file_path('config.tpl')); - return; -} - -$gui->headerMessage = plugin_lang_get('config_page_header_message'); -$gui->title = plugin_lang_get('config_page_title'); -$gui->labelConfig1 = plugin_lang_get('config_label_config1'); -$gui->labelConfig2 = plugin_lang_get('config_label_config2'); -$gui->config1 = plugin_config_get('config1', '', $_SESSION['testprojectID']); -$gui->config2 = plugin_config_get('config2', '', $_SESSION['testprojectID']); -$gui->labelSaveConfig = plugin_lang_get('config_label_save_button'); - -$smarty->assign('gui',$gui); -$smarty->display(plugin_file_path('config.tpl')); \ No newline at end of file +message = plugin_lang_get('config_page_saved'); // Confirm message + + // Assign to Smarty + $smarty->assign('gui', $gui); + $smarty->display(plugin_file_path('config.tpl')); + return; +} + +$gui->headerMessage = plugin_lang_get('config_page_header_message'); +$gui->title = plugin_lang_get('config_page_title'); +$gui->labelConfig1 = plugin_lang_get('config_label_config1'); +$gui->labelConfig2 = plugin_lang_get('config_label_config2'); +$gui->config1 = plugin_config_get('config1', '', $_SESSION['testprojectID']); +$gui->config2 = plugin_config_get('config2', '', $_SESSION['testprojectID']); +$gui->labelSaveConfig = plugin_lang_get('config_label_save_button'); + +$smarty->assign('gui', $gui); +$smarty->display(plugin_file_path('config.tpl')); diff --git a/third_party/DataTables-1.10.24/DataTables-1.10.24/images/favicon.ico b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/favicon.ico new file mode 100644 index 0000000000..6eeaa2a0d3 Binary files /dev/null and b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/favicon.ico differ diff --git a/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_asc.png b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_asc.png new file mode 100644 index 0000000000..e1ba61a805 Binary files /dev/null and b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_asc.png differ diff --git a/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_asc_disabled.png b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_asc_disabled.png new file mode 100644 index 0000000000..c9fdd8a150 Binary files /dev/null and b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_asc_disabled.png differ diff --git a/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_both.png b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_both.png new file mode 100644 index 0000000000..af5bc7c5a1 Binary files /dev/null and b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_both.png differ diff --git a/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_desc.png b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_desc.png new file mode 100644 index 0000000000..0e156deb5f Binary files /dev/null and b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_desc.png differ diff --git a/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_desc_disabled.png b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_desc_disabled.png new file mode 100644 index 0000000000..fb11dfe24a Binary files /dev/null and b/third_party/DataTables-1.10.24/DataTables-1.10.24/images/sort_desc_disabled.png differ diff --git a/third_party/DataTables-1.10.24/README-media/download-choice-01.png b/third_party/DataTables-1.10.24/README-media/download-choice-01.png new file mode 100644 index 0000000000..908299a83f Binary files /dev/null and b/third_party/DataTables-1.10.24/README-media/download-choice-01.png differ diff --git a/third_party/DataTables-1.10.24/README-media/download-choice-02.png b/third_party/DataTables-1.10.24/README-media/download-choice-02.png new file mode 100644 index 0000000000..c071495f4c Binary files /dev/null and b/third_party/DataTables-1.10.24/README-media/download-choice-02.png differ diff --git a/third_party/DataTables-1.10.24/README-media/zip-content.png b/third_party/DataTables-1.10.24/README-media/zip-content.png new file mode 100644 index 0000000000..0ab60ecf8a Binary files /dev/null and b/third_party/DataTables-1.10.24/README-media/zip-content.png differ diff --git a/third_party/DataTables-1.10.24/README.md b/third_party/DataTables-1.10.24/README.md new file mode 100644 index 0000000000..23d3689abc --- /dev/null +++ b/third_party/DataTables-1.10.24/README.md @@ -0,0 +1,15 @@ +#20210530 + +#DataTables Download Builder Choices +These are the choices made when creating the DataTables download selecting additional packages + +![](./README-media/zip-content.png) + +![](./README-media/download-choice-01.png) + +![](./README-media/download-choice-02.png) + + +#Why DataTables-1.10.24/images/ ? +Because I've missed the sort icons!! +I need to understand how to be able to use JUST images as folder diff --git a/third_party/DataTables-1.10.24/datatables.css b/third_party/DataTables-1.10.24/datatables.css new file mode 100644 index 0000000000..b0301e1a9c --- /dev/null +++ b/third_party/DataTables-1.10.24/datatables.css @@ -0,0 +1,1063 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-1.10.24/b-1.7.0/b-html5-1.7.0/fh-3.1.8/r-2.2.7 + * + * Included libraries: + * DataTables 1.10.24, Buttons 1.7.0, HTML5 export 1.7.0, FixedHeader 3.1.8, Responsive 2.2.7 + */ + +/* + * Table styles + */ +table.dataTable { + width: 100%; + margin: 0 auto; + clear: both; + border-collapse: separate; + border-spacing: 0; + /* + * Header and footer styles + */ + /* + * Body styles + */ +} +table.dataTable thead th, +table.dataTable tfoot th { + font-weight: bold; +} +table.dataTable thead th, +table.dataTable thead td { + padding: 10px 18px; + border-bottom: 1px solid #111; +} +table.dataTable thead th:active, +table.dataTable thead td:active { + outline: none; +} +table.dataTable tfoot th, +table.dataTable tfoot td { + padding: 10px 18px 6px 18px; + border-top: 1px solid #111; +} +table.dataTable thead .sorting, +table.dataTable thead .sorting_asc, +table.dataTable thead .sorting_desc, +table.dataTable thead .sorting_asc_disabled, +table.dataTable thead .sorting_desc_disabled { + cursor: pointer; + *cursor: hand; + background-repeat: no-repeat; + background-position: center right; +} +table.dataTable thead .sorting { + background-image: url("DataTables-1.10.24/images/sort_both.png"); +} +table.dataTable thead .sorting_asc { + background-image: url("DataTables-1.10.24/images/sort_asc.png") !important; +} +table.dataTable thead .sorting_desc { + background-image: url("DataTables-1.10.24/images/sort_desc.png") !important; +} +table.dataTable thead .sorting_asc_disabled { + background-image: url("DataTables-1.10.24/images/sort_asc_disabled.png"); +} +table.dataTable thead .sorting_desc_disabled { + background-image: url("DataTables-1.10.24/images/sort_desc_disabled.png"); +} +table.dataTable tbody tr { + background-color: #ffffff; +} +table.dataTable tbody tr.selected { + background-color: #B0BED9; +} +table.dataTable tbody th, +table.dataTable tbody td { + padding: 8px 10px; +} +table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td { + border-top: 1px solid #ddd; +} +table.dataTable.row-border tbody tr:first-child th, +table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th, +table.dataTable.display tbody tr:first-child td { + border-top: none; +} +table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td { + border-top: 1px solid #ddd; + border-right: 1px solid #ddd; +} +table.dataTable.cell-border tbody tr th:first-child, +table.dataTable.cell-border tbody tr td:first-child { + border-left: 1px solid #ddd; +} +table.dataTable.cell-border tbody tr:first-child th, +table.dataTable.cell-border tbody tr:first-child td { + border-top: none; +} +table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd { + background-color: #f9f9f9; +} +table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected { + background-color: #acbad4; +} +table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { + background-color: #f6f6f6; +} +table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected { + background-color: #aab7d1; +} +table.dataTable.order-column tbody tr > .sorting_1, +table.dataTable.order-column tbody tr > .sorting_2, +table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1, +table.dataTable.display tbody tr > .sorting_2, +table.dataTable.display tbody tr > .sorting_3 { + background-color: #fafafa; +} +table.dataTable.order-column tbody tr.selected > .sorting_1, +table.dataTable.order-column tbody tr.selected > .sorting_2, +table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1, +table.dataTable.display tbody tr.selected > .sorting_2, +table.dataTable.display tbody tr.selected > .sorting_3 { + background-color: #acbad5; +} +table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 { + background-color: #f1f1f1; +} +table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 { + background-color: #f3f3f3; +} +table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 { + background-color: whitesmoke; +} +table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 { + background-color: #a6b4cd; +} +table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 { + background-color: #a8b5cf; +} +table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 { + background-color: #a9b7d1; +} +table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 { + background-color: #fafafa; +} +table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 { + background-color: #fcfcfc; +} +table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 { + background-color: #fefefe; +} +table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 { + background-color: #acbad5; +} +table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 { + background-color: #aebcd6; +} +table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 { + background-color: #afbdd8; +} +table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 { + background-color: #eaeaea; +} +table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 { + background-color: #ececec; +} +table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 { + background-color: #efefef; +} +table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 { + background-color: #a2aec7; +} +table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 { + background-color: #a3b0c9; +} +table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 { + background-color: #a5b2cb; +} +table.dataTable.no-footer { + border-bottom: 1px solid #111; +} +table.dataTable.nowrap th, table.dataTable.nowrap td { + white-space: nowrap; +} +table.dataTable.compact thead th, +table.dataTable.compact thead td { + padding: 4px 17px; +} +table.dataTable.compact tfoot th, +table.dataTable.compact tfoot td { + padding: 4px; +} +table.dataTable.compact tbody th, +table.dataTable.compact tbody td { + padding: 4px; +} +table.dataTable th.dt-left, +table.dataTable td.dt-left { + text-align: left; +} +table.dataTable th.dt-center, +table.dataTable td.dt-center, +table.dataTable td.dataTables_empty { + text-align: center; +} +table.dataTable th.dt-right, +table.dataTable td.dt-right { + text-align: right; +} +table.dataTable th.dt-justify, +table.dataTable td.dt-justify { + text-align: justify; +} +table.dataTable th.dt-nowrap, +table.dataTable td.dt-nowrap { + white-space: nowrap; +} +table.dataTable thead th.dt-head-left, +table.dataTable thead td.dt-head-left, +table.dataTable tfoot th.dt-head-left, +table.dataTable tfoot td.dt-head-left { + text-align: left; +} +table.dataTable thead th.dt-head-center, +table.dataTable thead td.dt-head-center, +table.dataTable tfoot th.dt-head-center, +table.dataTable tfoot td.dt-head-center { + text-align: center; +} +table.dataTable thead th.dt-head-right, +table.dataTable thead td.dt-head-right, +table.dataTable tfoot th.dt-head-right, +table.dataTable tfoot td.dt-head-right { + text-align: right; +} +table.dataTable thead th.dt-head-justify, +table.dataTable thead td.dt-head-justify, +table.dataTable tfoot th.dt-head-justify, +table.dataTable tfoot td.dt-head-justify { + text-align: justify; +} +table.dataTable thead th.dt-head-nowrap, +table.dataTable thead td.dt-head-nowrap, +table.dataTable tfoot th.dt-head-nowrap, +table.dataTable tfoot td.dt-head-nowrap { + white-space: nowrap; +} +table.dataTable tbody th.dt-body-left, +table.dataTable tbody td.dt-body-left { + text-align: left; +} +table.dataTable tbody th.dt-body-center, +table.dataTable tbody td.dt-body-center { + text-align: center; +} +table.dataTable tbody th.dt-body-right, +table.dataTable tbody td.dt-body-right { + text-align: right; +} +table.dataTable tbody th.dt-body-justify, +table.dataTable tbody td.dt-body-justify { + text-align: justify; +} +table.dataTable tbody th.dt-body-nowrap, +table.dataTable tbody td.dt-body-nowrap { + white-space: nowrap; +} + +table.dataTable, +table.dataTable th, +table.dataTable td { + box-sizing: content-box; +} + +/* + * Control feature layout + */ +.dataTables_wrapper { + position: relative; + clear: both; + *zoom: 1; + zoom: 1; +} +.dataTables_wrapper .dataTables_length { + float: left; +} +.dataTables_wrapper .dataTables_length select { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + padding: 4px; +} +.dataTables_wrapper .dataTables_filter { + float: right; + text-align: right; +} +.dataTables_wrapper .dataTables_filter input { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + margin-left: 3px; +} +.dataTables_wrapper .dataTables_info { + clear: both; + float: left; + padding-top: 0.755em; +} +.dataTables_wrapper .dataTables_paginate { + float: right; + text-align: right; + padding-top: 0.25em; +} +.dataTables_wrapper .dataTables_paginate .paginate_button { + box-sizing: border-box; + display: inline-block; + min-width: 1.5em; + padding: 0.5em 1em; + margin-left: 2px; + text-align: center; + text-decoration: none !important; + cursor: pointer; + *cursor: hand; + color: #333 !important; + border: 1px solid transparent; + border-radius: 2px; +} +.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover { + color: #333 !important; + border: 1px solid #979797; + background-color: white; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc)); + /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%); + /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%); + /* FF3.6+ */ + background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%); + /* IE10+ */ + background: -o-linear-gradient(top, white 0%, #dcdcdc 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, white 0%, #dcdcdc 100%); + /* W3C */ +} +.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active { + cursor: default; + color: #666 !important; + border: 1px solid transparent; + background: transparent; + box-shadow: none; +} +.dataTables_wrapper .dataTables_paginate .paginate_button:hover { + color: white !important; + border: 1px solid #111; + background-color: #585858; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111)); + /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #585858 0%, #111 100%); + /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, #585858 0%, #111 100%); + /* FF3.6+ */ + background: -ms-linear-gradient(top, #585858 0%, #111 100%); + /* IE10+ */ + background: -o-linear-gradient(top, #585858 0%, #111 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, #585858 0%, #111 100%); + /* W3C */ +} +.dataTables_wrapper .dataTables_paginate .paginate_button:active { + outline: none; + background-color: #2b2b2b; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c)); + /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* FF3.6+ */ + background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* IE10+ */ + background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%); + /* W3C */ + box-shadow: inset 0 0 3px #111; +} +.dataTables_wrapper .dataTables_paginate .ellipsis { + padding: 0 1em; +} +.dataTables_wrapper .dataTables_processing { + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 40px; + margin-left: -50%; + margin-top: -25px; + padding-top: 20px; + text-align: center; + font-size: 1.2em; + background-color: white; + background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0))); + background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); + background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); + background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); + background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); + background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); +} +.dataTables_wrapper .dataTables_length, +.dataTables_wrapper .dataTables_filter, +.dataTables_wrapper .dataTables_info, +.dataTables_wrapper .dataTables_processing, +.dataTables_wrapper .dataTables_paginate { + color: #333; +} +.dataTables_wrapper .dataTables_scroll { + clear: both; +} +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody { + *margin-top: -1px; + -webkit-overflow-scrolling: touch; +} +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td { + vertical-align: middle; +} +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing { + height: 0; + overflow: hidden; + margin: 0 !important; + padding: 0 !important; +} +.dataTables_wrapper.no-footer .dataTables_scrollBody { + border-bottom: 1px solid #111; +} +.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable, +.dataTables_wrapper.no-footer div.dataTables_scrollBody > table { + border-bottom: none; +} +.dataTables_wrapper:after { + visibility: hidden; + display: block; + content: ""; + clear: both; + height: 0; +} + +@media screen and (max-width: 767px) { + .dataTables_wrapper .dataTables_info, +.dataTables_wrapper .dataTables_paginate { + float: none; + text-align: center; + } + .dataTables_wrapper .dataTables_paginate { + margin-top: 0.5em; + } +} +@media screen and (max-width: 640px) { + .dataTables_wrapper .dataTables_length, +.dataTables_wrapper .dataTables_filter { + float: none; + text-align: center; + } + .dataTables_wrapper .dataTables_filter { + margin-top: 0.5em; + } +} + + +@keyframes dtb-spinner { + 100% { + transform: rotate(360deg); + } +} +@-o-keyframes dtb-spinner { + 100% { + -o-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-ms-keyframes dtb-spinner { + 100% { + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-webkit-keyframes dtb-spinner { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-moz-keyframes dtb-spinner { + 100% { + -moz-transform: rotate(360deg); + transform: rotate(360deg); + } +} +div.dt-button-info { + position: fixed; + top: 50%; + left: 50%; + width: 400px; + margin-top: -100px; + margin-left: -200px; + background-color: white; + border: 2px solid #111; + box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.3); + border-radius: 3px; + text-align: center; + z-index: 21; +} +div.dt-button-info h2 { + padding: 0.5em; + margin: 0; + font-weight: normal; + border-bottom: 1px solid #ddd; + background-color: #f3f3f3; +} +div.dt-button-info > div { + padding: 1em; +} + +div.dt-button-collection-title { + text-align: center; + padding: 0.3em 0 0.5em; + font-size: 0.9em; +} + +div.dt-button-collection-title:empty { + display: none; +} + +button.dt-button, +div.dt-button, +a.dt-button, +input.dt-button { + position: relative; + display: inline-block; + box-sizing: border-box; + margin-right: 0.333em; + margin-bottom: 0.333em; + padding: 0.5em 1em; + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 2px; + cursor: pointer; + font-size: 0.88em; + line-height: 1.6em; + color: black; + white-space: nowrap; + overflow: hidden; + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: -webkit-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background: -moz-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* FF3.6 */ + background: -ms-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* IE10 */ + background: -o-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(230, 230, 230, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + text-decoration: none; + outline: none; + text-overflow: ellipsis; +} +button.dt-button.disabled, +div.dt-button.disabled, +a.dt-button.disabled, +input.dt-button.disabled { + cursor: default; + opacity: 0.4; +} +button.dt-button:active:not(.disabled), button.dt-button.active:not(.disabled), +div.dt-button:active:not(.disabled), +div.dt-button.active:not(.disabled), +a.dt-button:active:not(.disabled), +a.dt-button.active:not(.disabled), +input.dt-button:active:not(.disabled), +input.dt-button.active:not(.disabled) { + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: -webkit-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background: -moz-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* FF3.6 */ + background: -ms-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* IE10 */ + background: -o-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(179, 179, 179, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); + box-shadow: inset 1px 1px 3px #999999; +} +button.dt-button:active:not(.disabled):hover:not(.disabled), button.dt-button.active:not(.disabled):hover:not(.disabled), +div.dt-button:active:not(.disabled):hover:not(.disabled), +div.dt-button.active:not(.disabled):hover:not(.disabled), +a.dt-button:active:not(.disabled):hover:not(.disabled), +a.dt-button.active:not(.disabled):hover:not(.disabled), +input.dt-button:active:not(.disabled):hover:not(.disabled), +input.dt-button.active:not(.disabled):hover:not(.disabled) { + box-shadow: inset 1px 1px 3px #999999; + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: -webkit-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background: -moz-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* FF3.6 */ + background: -ms-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* IE10 */ + background: -o-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(128, 128, 128, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); +} +button.dt-button:hover, +div.dt-button:hover, +a.dt-button:hover, +input.dt-button:hover { + text-decoration: none; +} +button.dt-button:hover:not(.disabled), +div.dt-button:hover:not(.disabled), +a.dt-button:hover:not(.disabled), +input.dt-button:hover:not(.disabled) { + border: 1px solid #666; + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: -webkit-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background: -moz-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* FF3.6 */ + background: -ms-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* IE10 */ + background: -o-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(153, 153, 153, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); +} +button.dt-button:focus:not(.disabled), +div.dt-button:focus:not(.disabled), +a.dt-button:focus:not(.disabled), +input.dt-button:focus:not(.disabled) { + border: 1px solid #426c9e; + text-shadow: 0 1px 0 #c4def1; + outline: none; + background-color: #79ace9; + /* Fallback */ + background: -webkit-linear-gradient(top, #d1e2f7 0%, #79ace9 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background: -moz-linear-gradient(top, #d1e2f7 0%, #79ace9 100%); + /* FF3.6 */ + background: -ms-linear-gradient(top, #d1e2f7 0%, #79ace9 100%); + /* IE10 */ + background: -o-linear-gradient(top, #d1e2f7 0%, #79ace9 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, #d1e2f7 0%, #79ace9 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="#d1e2f7", EndColorStr="#79ace9"); +} + +.dt-button embed { + outline: none; +} + +div.dt-buttons { + position: relative; + float: left; +} +div.dt-buttons.buttons-right { + float: right; +} + +div.dataTables_layout_cell div.dt-buttons { + float: none; +} +div.dataTables_layout_cell div.dt-buttons.buttons-right { + float: none; +} + +div.dt-button-collection { + position: absolute; + top: 0; + left: 0; + width: 150px; + margin-top: 3px; + padding: 8px 8px 4px 8px; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.4); + background-color: white; + overflow: hidden; + z-index: 2002; + border-radius: 5px; + box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.3); + box-sizing: border-box; +} +div.dt-button-collection button.dt-button, +div.dt-button-collection div.dt-button, +div.dt-button-collection a.dt-button { + position: relative; + left: 0; + right: 0; + width: 100%; + display: block; + float: none; + margin-bottom: 4px; + margin-right: 0; +} +div.dt-button-collection button.dt-button:active:not(.disabled), div.dt-button-collection button.dt-button.active:not(.disabled), +div.dt-button-collection div.dt-button:active:not(.disabled), +div.dt-button-collection div.dt-button.active:not(.disabled), +div.dt-button-collection a.dt-button:active:not(.disabled), +div.dt-button-collection a.dt-button.active:not(.disabled) { + background-color: #dadada; + /* Fallback */ + background: -webkit-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* Chrome 10+, Saf5.1+, iOS 5+ */ + background: -moz-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* FF3.6 */ + background: -ms-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* IE10 */ + background: -o-linear-gradient(top, #f0f0f0 0%, #dadada 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, #f0f0f0 0%, #dadada 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="#f0f0f0", EndColorStr="#dadada"); + box-shadow: inset 1px 1px 3px #666; +} +div.dt-button-collection.fixed { + position: fixed; + top: 50%; + left: 50%; + margin-left: -75px; + border-radius: 0; +} +div.dt-button-collection.fixed.two-column { + margin-left: -200px; +} +div.dt-button-collection.fixed.three-column { + margin-left: -225px; +} +div.dt-button-collection.fixed.four-column { + margin-left: -300px; +} +div.dt-button-collection > :last-child { + display: block !important; + -webkit-column-gap: 8px; + -moz-column-gap: 8px; + -ms-column-gap: 8px; + -o-column-gap: 8px; + column-gap: 8px; +} +div.dt-button-collection > :last-child > * { + -webkit-column-break-inside: avoid; + break-inside: avoid; +} +div.dt-button-collection.two-column { + width: 400px; +} +div.dt-button-collection.two-column > :last-child { + padding-bottom: 1px; + -webkit-column-count: 2; + -moz-column-count: 2; + -ms-column-count: 2; + -o-column-count: 2; + column-count: 2; +} +div.dt-button-collection.three-column { + width: 450px; +} +div.dt-button-collection.three-column > :last-child { + padding-bottom: 1px; + -webkit-column-count: 3; + -moz-column-count: 3; + -ms-column-count: 3; + -o-column-count: 3; + column-count: 3; +} +div.dt-button-collection.four-column { + width: 600px; +} +div.dt-button-collection.four-column > :last-child { + padding-bottom: 1px; + -webkit-column-count: 4; + -moz-column-count: 4; + -ms-column-count: 4; + -o-column-count: 4; + column-count: 4; +} +div.dt-button-collection .dt-button { + border-radius: 0; +} + +div.dt-button-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + /* Fallback */ + background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* IE10 Consumer Preview */ + background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Firefox */ + background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Opera */ + background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); + /* Webkit (Safari/Chrome 10) */ + background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* Webkit (Chrome 11+) */ + background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* W3C Markup, IE10 Release Preview */ + z-index: 2001; +} + +@media screen and (max-width: 640px) { + div.dt-buttons { + float: none !important; + text-align: center; + } +} +button.dt-button.processing, +div.dt-button.processing, +a.dt-button.processing { + color: rgba(0, 0, 0, 0.2); +} +button.dt-button.processing:after, +div.dt-button.processing:after, +a.dt-button.processing:after { + position: absolute; + top: 50%; + left: 50%; + width: 16px; + height: 16px; + margin: -8px 0 0 -8px; + box-sizing: border-box; + display: block; + content: " "; + border: 2px solid #282828; + border-radius: 50%; + border-left-color: transparent; + border-right-color: transparent; + animation: dtb-spinner 1500ms infinite linear; + -o-animation: dtb-spinner 1500ms infinite linear; + -ms-animation: dtb-spinner 1500ms infinite linear; + -webkit-animation: dtb-spinner 1500ms infinite linear; + -moz-animation: dtb-spinner 1500ms infinite linear; +} + + +table.fixedHeader-floating { + position: fixed !important; + background-color: white; +} + +table.fixedHeader-floating.no-footer { + border-bottom-width: 0; +} + +table.fixedHeader-locked { + position: absolute !important; + background-color: white; +} + +@media print { + table.fixedHeader-floating { + display: none; + } +} + + +table.dataTable.dtr-inline.collapsed > tbody > tr > td.child, +table.dataTable.dtr-inline.collapsed > tbody > tr > th.child, +table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty { + cursor: default !important; +} +table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before, +table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before, +table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before { + display: none !important; +} +table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control, +table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control { + position: relative; + padding-left: 30px; + cursor: pointer; +} +table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control:before, +table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control:before { + top: 50%; + left: 5px; + height: 1em; + width: 1em; + margin-top: -9px; + display: block; + position: absolute; + color: white; + border: 0.15em solid white; + border-radius: 1em; + box-shadow: 0 0 0.2em #444; + box-sizing: content-box; + text-align: center; + text-indent: 0 !important; + font-family: "Courier New", Courier, monospace; + line-height: 1em; + content: "+"; + background-color: #31b131; +} +table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td.dtr-control:before, +table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th.dtr-control:before { + content: "-"; + background-color: #d33333; +} +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control, +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control { + padding-left: 27px; +} +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control:before, +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control:before { + left: 4px; + height: 14px; + width: 14px; + border-radius: 14px; + line-height: 14px; + text-indent: 3px; +} +table.dataTable.dtr-column > tbody > tr > td.dtr-control, +table.dataTable.dtr-column > tbody > tr > th.dtr-control, +table.dataTable.dtr-column > tbody > tr > td.control, +table.dataTable.dtr-column > tbody > tr > th.control { + position: relative; + cursor: pointer; +} +table.dataTable.dtr-column > tbody > tr > td.dtr-control:before, +table.dataTable.dtr-column > tbody > tr > th.dtr-control:before, +table.dataTable.dtr-column > tbody > tr > td.control:before, +table.dataTable.dtr-column > tbody > tr > th.control:before { + top: 50%; + left: 50%; + height: 0.8em; + width: 0.8em; + margin-top: -0.5em; + margin-left: -0.5em; + display: block; + position: absolute; + color: white; + border: 0.15em solid white; + border-radius: 1em; + box-shadow: 0 0 0.2em #444; + box-sizing: content-box; + text-align: center; + text-indent: 0 !important; + font-family: "Courier New", Courier, monospace; + line-height: 1em; + content: "+"; + background-color: #31b131; +} +table.dataTable.dtr-column > tbody > tr.parent td.dtr-control:before, +table.dataTable.dtr-column > tbody > tr.parent th.dtr-control:before, +table.dataTable.dtr-column > tbody > tr.parent td.control:before, +table.dataTable.dtr-column > tbody > tr.parent th.control:before { + content: "-"; + background-color: #d33333; +} +table.dataTable > tbody > tr.child { + padding: 0.5em 1em; +} +table.dataTable > tbody > tr.child:hover { + background: transparent !important; +} +table.dataTable > tbody > tr.child ul.dtr-details { + display: inline-block; + list-style-type: none; + margin: 0; + padding: 0; +} +table.dataTable > tbody > tr.child ul.dtr-details > li { + border-bottom: 1px solid #efefef; + padding: 0.5em 0; +} +table.dataTable > tbody > tr.child ul.dtr-details > li:first-child { + padding-top: 0; +} +table.dataTable > tbody > tr.child ul.dtr-details > li:last-child { + border-bottom: none; +} +table.dataTable > tbody > tr.child span.dtr-title { + display: inline-block; + min-width: 75px; + font-weight: bold; +} +div.dtr-modal { + position: fixed; + box-sizing: border-box; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 100; + padding: 10em 1em; +} +div.dtr-modal div.dtr-modal-display { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 50%; + height: 50%; + overflow: auto; + margin: auto; + z-index: 102; + overflow: auto; + background-color: #f5f5f7; + border: 1px solid black; + border-radius: 0.5em; + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6); +} +div.dtr-modal div.dtr-modal-content { + position: relative; + padding: 1em; +} +div.dtr-modal div.dtr-modal-close { + position: absolute; + top: 6px; + right: 6px; + width: 22px; + height: 22px; + border: 1px solid #eaeaea; + background-color: #f9f9f9; + text-align: center; + border-radius: 3px; + cursor: pointer; + z-index: 12; +} +div.dtr-modal div.dtr-modal-close:hover { + background-color: #eaeaea; +} +div.dtr-modal div.dtr-modal-background { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 101; + background: rgba(0, 0, 0, 0.6); +} + +@media screen and (max-width: 767px) { + div.dtr-modal div.dtr-modal-display { + width: 95%; + } +} + + diff --git a/third_party/DataTables-1.10.24/datatables.js b/third_party/DataTables-1.10.24/datatables.js new file mode 100644 index 0000000000..8b483d9232 --- /dev/null +++ b/third_party/DataTables-1.10.24/datatables.js @@ -0,0 +1,21242 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-1.10.24/b-1.7.0/b-html5-1.7.0/fh-3.1.8/r-2.2.7 + * + * Included libraries: + * DataTables 1.10.24, Buttons 1.7.0, HTML5 export 1.7.0, FixedHeader 3.1.8, Responsive 2.2.7 + */ + +/*! DataTables 1.10.24 + * ©2008-2021 SpryMedia Ltd - datatables.net/license + */ + +/** + * @summary DataTables + * @description Paginate, search and order HTML tables + * @version 1.10.24 + * @file jquery.dataTables.js + * @author SpryMedia Ltd + * @contact www.datatables.net + * @copyright Copyright 2008-2021 SpryMedia Ltd. + * + * This source file is free software, available under the following license: + * MIT license - http://datatables.net/license + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +/*jslint evil: true, undef: true, browser: true */ +/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ + +(function( factory ) { + "use strict"; + + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + module.exports = function (root, $) { + if ( ! root ) { + // CommonJS environments without a window global must pass a + // root. This will give an error otherwise + root = window; + } + + if ( ! $ ) { + $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window + require('jquery') : + require('jquery')( root ); + } + + return factory( $, root, root.document ); + }; + } + else { + // Browser + factory( jQuery, window, document ); + } +} +(function( $, window, document, undefined ) { + "use strict"; + + /** + * DataTables is a plug-in for the jQuery Javascript library. It is a highly + * flexible tool, based upon the foundations of progressive enhancement, + * which will add advanced interaction controls to any HTML table. For a + * full list of features please refer to + * [DataTables.net](href="http://datatables.net). + * + * Note that the `DataTable` object is not a global variable but is aliased + * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may + * be accessed. + * + * @class + * @param {object} [init={}] Configuration object for DataTables. Options + * are defined by {@link DataTable.defaults} + * @requires jQuery 1.7+ + * + * @example + * // Basic initialisation + * $(document).ready( function { + * $('#example').dataTable(); + * } ); + * + * @example + * // Initialisation with configuration options - in this case, disable + * // pagination and sorting. + * $(document).ready( function { + * $('#example').dataTable( { + * "paginate": false, + * "sort": false + * } ); + * } ); + */ + var DataTable = function ( options ) + { + /** + * Perform a jQuery selector action on the table's TR elements (from the tbody) and + * return the resulting jQuery object. + * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on + * @param {object} [oOpts] Optional parameters for modifying the rows to be included + * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter + * criterion ("applied") or all TR elements (i.e. no filter). + * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. + * Can be either 'current', whereby the current sorting of the table is used, or + * 'original' whereby the original order the data was read into the table is used. + * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page + * ("current") or not ("all"). If 'current' is given, then order is assumed to be + * 'current' and filter is 'applied', regardless of what they might be given as. + * @returns {object} jQuery object, filtered by the given selector. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Highlight every second row + * oTable.$('tr:odd').css('backgroundColor', 'blue'); + * } ); + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Filter to rows with 'Webkit' in them, add a background colour and then + * // remove the filter, thus highlighting the 'Webkit' rows only. + * oTable.fnFilter('Webkit'); + * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); + * oTable.fnFilter(''); + * } ); + */ + this.$ = function ( sSelector, oOpts ) + { + return this.api(true).$( sSelector, oOpts ); + }; + + + /** + * Almost identical to $ in operation, but in this case returns the data for the matched + * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes + * rather than any descendants, so the data can be obtained for the row/cell. If matching + * rows are found, the data returned is the original data array/object that was used to + * create the row (or a generated array if from a DOM source). + * + * This method is often useful in-combination with $ where both functions are given the + * same parameters and the array indexes will match identically. + * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on + * @param {object} [oOpts] Optional parameters for modifying the rows to be included + * @param {string} [oOpts.filter=none] Select elements that meet the current filter + * criterion ("applied") or all elements (i.e. no filter). + * @param {string} [oOpts.order=current] Order of the data in the processed array. + * Can be either 'current', whereby the current sorting of the table is used, or + * 'original' whereby the original order the data was read into the table is used. + * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page + * ("current") or not ("all"). If 'current' is given, then order is assumed to be + * 'current' and filter is 'applied', regardless of what they might be given as. + * @returns {array} Data for the matched elements. If any elements, as a result of the + * selector, were not TR, TD or TH elements in the DataTable, they will have a null + * entry in the array. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Get the data from the first row in the table + * var data = oTable._('tr:first'); + * + * // Do something useful with the data + * alert( "First cell is: "+data[0] ); + * } ); + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Filter to 'Webkit' and get all data for + * oTable.fnFilter('Webkit'); + * var data = oTable._('tr', {"search": "applied"}); + * + * // Do something with the data + * alert( data.length+" rows matched the search" ); + * } ); + */ + this._ = function ( sSelector, oOpts ) + { + return this.api(true).rows( sSelector, oOpts ).data(); + }; + + + /** + * Create a DataTables Api instance, with the currently selected tables for + * the Api's context. + * @param {boolean} [traditional=false] Set the API instance's context to be + * only the table referred to by the `DataTable.ext.iApiIndex` option, as was + * used in the API presented by DataTables 1.9- (i.e. the traditional mode), + * or if all tables captured in the jQuery object should be used. + * @return {DataTables.Api} + */ + this.api = function ( traditional ) + { + return traditional ? + new _Api( + _fnSettingsFromNode( this[ _ext.iApiIndex ] ) + ) : + new _Api( this ); + }; + + + /** + * Add a single new row or multiple rows of data to the table. Please note + * that this is suitable for client-side processing only - if you are using + * server-side processing (i.e. "bServerSide": true), then to add data, you + * must add it to the data source, i.e. the server-side, through an Ajax call. + * @param {array|object} data The data to be added to the table. This can be: + *
      + *
    • 1D array of data - add a single row with the data provided
    • + *
    • 2D array of arrays - add multiple rows in a single call
    • + *
    • object - data object when using mData
    • + *
    • array of objects - multiple data objects when using mData
    • + *
    + * @param {bool} [redraw=true] redraw the table or not + * @returns {array} An array of integers, representing the list of indexes in + * aoData ({@link DataTable.models.oSettings}) that have been added to + * the table. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * // Global var for counter + * var giCount = 2; + * + * $(document).ready(function() { + * $('#example').dataTable(); + * } ); + * + * function fnClickAddRow() { + * $('#example').dataTable().fnAddData( [ + * giCount+".1", + * giCount+".2", + * giCount+".3", + * giCount+".4" ] + * ); + * + * giCount++; + * } + */ + this.fnAddData = function( data, redraw ) + { + var api = this.api( true ); + + /* Check if we want to add multiple rows or not */ + var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ? + api.rows.add( data ) : + api.row.add( data ); + + if ( redraw === undefined || redraw ) { + api.draw(); + } + + return rows.flatten().toArray(); + }; + + + /** + * This function will make DataTables recalculate the column sizes, based on the data + * contained in the table and the sizes applied to the columns (in the DOM, CSS or + * through the sWidth parameter). This can be useful when the width of the table's + * parent element changes (for example a window resize). + * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable( { + * "sScrollY": "200px", + * "bPaginate": false + * } ); + * + * $(window).on('resize', function () { + * oTable.fnAdjustColumnSizing(); + * } ); + * } ); + */ + this.fnAdjustColumnSizing = function ( bRedraw ) + { + var api = this.api( true ).columns.adjust(); + var settings = api.settings()[0]; + var scroll = settings.oScroll; + + if ( bRedraw === undefined || bRedraw ) { + api.draw( false ); + } + else if ( scroll.sX !== "" || scroll.sY !== "" ) { + /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ + _fnScrollDraw( settings ); + } + }; + + + /** + * Quickly and simply clear a table + * @param {bool} [bRedraw=true] redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) + * oTable.fnClearTable(); + * } ); + */ + this.fnClearTable = function( bRedraw ) + { + var api = this.api( true ).clear(); + + if ( bRedraw === undefined || bRedraw ) { + api.draw(); + } + }; + + + /** + * The exact opposite of 'opening' a row, this function will close any rows which + * are currently 'open'. + * @param {node} nTr the table row to 'close' + * @returns {int} 0 on success, or 1 if failed (can't find the row) + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnClose = function( nTr ) + { + this.api( true ).row( nTr ).child.hide(); + }; + + + /** + * Remove a row for the table + * @param {mixed} target The index of the row from aoData to be deleted, or + * the TR element you want to delete + * @param {function|null} [callBack] Callback function + * @param {bool} [redraw=true] Redraw the table or not + * @returns {array} The row that was deleted + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Immediately remove the first row + * oTable.fnDeleteRow( 0 ); + * } ); + */ + this.fnDeleteRow = function( target, callback, redraw ) + { + var api = this.api( true ); + var rows = api.rows( target ); + var settings = rows.settings()[0]; + var data = settings.aoData[ rows[0][0] ]; + + rows.remove(); + + if ( callback ) { + callback.call( this, settings, data ); + } + + if ( redraw === undefined || redraw ) { + api.draw(); + } + + return data; + }; + + + /** + * Restore the table to it's original state in the DOM by removing all of DataTables + * enhancements, alterations to the DOM structure of the table and event listeners. + * @param {boolean} [remove=false] Completely remove the table from the DOM + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * // This example is fairly pointless in reality, but shows how fnDestroy can be used + * var oTable = $('#example').dataTable(); + * oTable.fnDestroy(); + * } ); + */ + this.fnDestroy = function ( remove ) + { + this.api( true ).destroy( remove ); + }; + + + /** + * Redraw the table + * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) + * oTable.fnDraw(); + * } ); + */ + this.fnDraw = function( complete ) + { + // Note that this isn't an exact match to the old call to _fnDraw - it takes + // into account the new data, but can hold position. + this.api( true ).draw( complete ); + }; + + + /** + * Filter the input based on data + * @param {string} sInput String to filter the table on + * @param {int|null} [iColumn] Column to limit filtering to + * @param {bool} [bRegex=false] Treat as regular expression or not + * @param {bool} [bSmart=true] Perform smart filtering or not + * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) + * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sometime later - filter... + * oTable.fnFilter( 'test string' ); + * } ); + */ + this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) + { + var api = this.api( true ); + + if ( iColumn === null || iColumn === undefined ) { + api.search( sInput, bRegex, bSmart, bCaseInsensitive ); + } + else { + api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); + } + + api.draw(); + }; + + + /** + * Get the data for the whole table, an individual row or an individual cell based on the + * provided parameters. + * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as + * a TR node then the data source for the whole row will be returned. If given as a + * TD/TH cell node then iCol will be automatically calculated and the data for the + * cell returned. If given as an integer, then this is treated as the aoData internal + * data index for the row (see fnGetPosition) and the data for that row used. + * @param {int} [col] Optional column index that you want the data of. + * @returns {array|object|string} If mRow is undefined, then the data for all rows is + * returned. If mRow is defined, just data for that row, and is iCol is + * defined, only data for the designated cell is returned. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * // Row data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('tr').click( function () { + * var data = oTable.fnGetData( this ); + * // ... do something with the array / object of data for the row + * } ); + * } ); + * + * @example + * // Individual cell data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('td').click( function () { + * var sData = oTable.fnGetData( this ); + * alert( 'The cell clicked on had the value of '+sData ); + * } ); + * } ); + */ + this.fnGetData = function( src, col ) + { + var api = this.api( true ); + + if ( src !== undefined ) { + var type = src.nodeName ? src.nodeName.toLowerCase() : ''; + + return col !== undefined || type == 'td' || type == 'th' ? + api.cell( src, col ).data() : + api.row( src ).data() || null; + } + + return api.data().toArray(); + }; + + + /** + * Get an array of the TR nodes that are used in the table's body. Note that you will + * typically want to use the '$' API method in preference to this as it is more + * flexible. + * @param {int} [iRow] Optional row index for the TR element you want + * @returns {array|node} If iRow is undefined, returns an array of all TR elements + * in the table's body, or iRow is defined, just the TR element requested. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Get the nodes from the table + * var nNodes = oTable.fnGetNodes( ); + * } ); + */ + this.fnGetNodes = function( iRow ) + { + var api = this.api( true ); + + return iRow !== undefined ? + api.row( iRow ).node() : + api.rows().nodes().flatten().toArray(); + }; + + + /** + * Get the array indexes of a particular cell from it's DOM element + * and column index including hidden columns + * @param {node} node this can either be a TR, TD or TH in the table's body + * @returns {int} If nNode is given as a TR, then a single index is returned, or + * if given as a cell, an array of [row index, column index (visible), + * column index (all)] is given. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * $('#example tbody td').click( function () { + * // Get the position of the current data from the node + * var aPos = oTable.fnGetPosition( this ); + * + * // Get the data array for this row + * var aData = oTable.fnGetData( aPos[0] ); + * + * // Update the data array and return the value + * aData[ aPos[1] ] = 'clicked'; + * this.innerHTML = 'clicked'; + * } ); + * + * // Init DataTables + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnGetPosition = function( node ) + { + var api = this.api( true ); + var nodeName = node.nodeName.toUpperCase(); + + if ( nodeName == 'TR' ) { + return api.row( node ).index(); + } + else if ( nodeName == 'TD' || nodeName == 'TH' ) { + var cell = api.cell( node ).index(); + + return [ + cell.row, + cell.columnVisible, + cell.column + ]; + } + return null; + }; + + + /** + * Check to see if a row is 'open' or not. + * @param {node} nTr the table row to check + * @returns {boolean} true if the row is currently open, false otherwise + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnIsOpen = function( nTr ) + { + return this.api( true ).row( nTr ).child.isShown(); + }; + + + /** + * This function will place a new row directly after a row which is currently + * on display on the page, with the HTML contents that is passed into the + * function. This can be used, for example, to ask for confirmation that a + * particular record should be deleted. + * @param {node} nTr The table row to 'open' + * @param {string|node|jQuery} mHtml The HTML to put into the row + * @param {string} sClass Class to give the new TD cell + * @returns {node} The row opened. Note that if the table row passed in as the + * first parameter, is not found in the table, this method will silently + * return. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnOpen = function( nTr, mHtml, sClass ) + { + return this.api( true ) + .row( nTr ) + .child( mHtml, sClass ) + .show() + .child()[0]; + }; + + + /** + * Change the pagination - provides the internal logic for pagination in a simple API + * function. With this function you can have a DataTables table go to the next, + * previous, first or last pages. + * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" + * or page number to jump to (integer), note that page 0 is the first page. + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * oTable.fnPageChange( 'next' ); + * } ); + */ + this.fnPageChange = function ( mAction, bRedraw ) + { + var api = this.api( true ).page( mAction ); + + if ( bRedraw === undefined || bRedraw ) { + api.draw(false); + } + }; + + + /** + * Show a particular column + * @param {int} iCol The column whose display should be changed + * @param {bool} bShow Show (true) or hide (false) the column + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Hide the second column after initialisation + * oTable.fnSetColumnVis( 1, false ); + * } ); + */ + this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) + { + var api = this.api( true ).column( iCol ).visible( bShow ); + + if ( bRedraw === undefined || bRedraw ) { + api.columns.adjust().draw(); + } + }; + + + /** + * Get the settings for a particular table for external manipulation + * @returns {object} DataTables settings object. See + * {@link DataTable.models.oSettings} + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * var oSettings = oTable.fnSettings(); + * + * // Show an example parameter from the settings + * alert( oSettings._iDisplayStart ); + * } ); + */ + this.fnSettings = function() + { + return _fnSettingsFromNode( this[_ext.iApiIndex] ); + }; + + + /** + * Sort the table by a particular column + * @param {int} iCol the data index to sort on. Note that this will not match the + * 'display index' if you have hidden data entries + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sort immediately with columns 0 and 1 + * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); + * } ); + */ + this.fnSort = function( aaSort ) + { + this.api( true ).order( aaSort ).draw(); + }; + + + /** + * Attach a sort listener to an element for a given column + * @param {node} nNode the element to attach the sort listener to + * @param {int} iColumn the column that a click on this node will sort on + * @param {function} [fnCallback] callback function when sort is run + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sort on column 1, when 'sorter' is clicked on + * oTable.fnSortListener( document.getElementById('sorter'), 1 ); + * } ); + */ + this.fnSortListener = function( nNode, iColumn, fnCallback ) + { + this.api( true ).order.listener( nNode, iColumn, fnCallback ); + }; + + + /** + * Update a table cell or row - this method will accept either a single value to + * update the cell with, an array of values with one element for each column or + * an object in the same format as the original data source. The function is + * self-referencing in order to make the multi column updates easier. + * @param {object|array|string} mData Data to update the cell/row with + * @param {node|int} mRow TR element you want to update or the aoData index + * @param {int} [iColumn] The column to update, give as null or undefined to + * update a whole row. + * @param {bool} [bRedraw=true] Redraw the table or not + * @param {bool} [bAction=true] Perform pre-draw actions or not + * @returns {int} 0 on success, 1 on error + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell + * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row + * } ); + */ + this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) + { + var api = this.api( true ); + + if ( iColumn === undefined || iColumn === null ) { + api.row( mRow ).data( mData ); + } + else { + api.cell( mRow, iColumn ).data( mData ); + } + + if ( bAction === undefined || bAction ) { + api.columns.adjust(); + } + + if ( bRedraw === undefined || bRedraw ) { + api.draw(); + } + return 0; + }; + + + /** + * Provide a common method for plug-ins to check the version of DataTables being used, in order + * to ensure compatibility. + * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the + * formats "X" and "X.Y" are also acceptable. + * @returns {boolean} true if this version of DataTables is greater or equal to the required + * version, or false if this version of DataTales is not suitable + * @method + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * alert( oTable.fnVersionCheck( '1.9.0' ) ); + * } ); + */ + this.fnVersionCheck = _ext.fnVersionCheck; + + + var _that = this; + var emptyInit = options === undefined; + var len = this.length; + + if ( emptyInit ) { + options = {}; + } + + this.oApi = this.internal = _ext.internal; + + // Extend with old style plug-in API methods + for ( var fn in DataTable.ext.internal ) { + if ( fn ) { + this[fn] = _fnExternApiFunc(fn); + } + } + + this.each(function() { + // For each initialisation we want to give it a clean initialisation + // object that can be bashed around + var o = {}; + var oInit = len > 1 ? // optimisation for single table case + _fnExtend( o, options, true ) : + options; + + /*global oInit,_that,emptyInit*/ + var i=0, iLen, j, jLen, k, kLen; + var sId = this.getAttribute( 'id' ); + var bInitHandedOff = false; + var defaults = DataTable.defaults; + var $this = $(this); + + + /* Sanity check */ + if ( this.nodeName.toLowerCase() != 'table' ) + { + _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); + return; + } + + /* Backwards compatibility for the defaults */ + _fnCompatOpts( defaults ); + _fnCompatCols( defaults.column ); + + /* Convert the camel-case defaults to Hungarian */ + _fnCamelToHungarian( defaults, defaults, true ); + _fnCamelToHungarian( defaults.column, defaults.column, true ); + + /* Setting up the initialisation object */ + _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); + + + + /* Check to see if we are re-initialising a table */ + var allSettings = DataTable.settings; + for ( i=0, iLen=allSettings.length ; i').appendTo($this); + } + oSettings.nTHead = thead[0]; + + var tbody = $this.children('tbody'); + if ( tbody.length === 0 ) { + tbody = $('').appendTo($this); + } + oSettings.nTBody = tbody[0]; + + var tfoot = $this.children('tfoot'); + if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) { + // If we are a scrolling table, and no footer has been given, then we need to create + // a tfoot element for the caption element to be appended to + tfoot = $('').appendTo($this); + } + + if ( tfoot.length === 0 || tfoot.children().length === 0 ) { + $this.addClass( oClasses.sNoFooter ); + } + else if ( tfoot.length > 0 ) { + oSettings.nTFoot = tfoot[0]; + _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); + } + + /* Check if there is data passing into the constructor */ + if ( oInit.aaData ) { + for ( i=0 ; i/g; + + // This is not strict ISO8601 - Date.parse() is quite lax, although + // implementations differ between browsers. + var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/; + + // Escape regular expression special characters + var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); + + // http://en.wikipedia.org/wiki/Foreign_exchange_market + // - \u20BD - Russian ruble. + // - \u20a9 - South Korean Won + // - \u20BA - Turkish Lira + // - \u20B9 - Indian Rupee + // - R - Brazil (R$) and South Africa + // - fr - Swiss Franc + // - kr - Swedish krona, Norwegian krone and Danish krone + // - \u2009 is thin space and \u202F is narrow no-break space, both used in many + // - Ƀ - Bitcoin + // - Ξ - Ethereum + // standards as thousands separators. + var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi; + + + var _empty = function ( d ) { + return !d || d === true || d === '-' ? true : false; + }; + + + var _intVal = function ( s ) { + var integer = parseInt( s, 10 ); + return !isNaN(integer) && isFinite(s) ? integer : null; + }; + + // Convert from a formatted number with characters other than `.` as the + // decimal place, to a Javascript number + var _numToDecimal = function ( num, decimalPoint ) { + // Cache created regular expressions for speed as this function is called often + if ( ! _re_dic[ decimalPoint ] ) { + _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); + } + return typeof num === 'string' && decimalPoint !== '.' ? + num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : + num; + }; + + + var _isNumber = function ( d, decimalPoint, formatted ) { + var strType = typeof d === 'string'; + + // If empty return immediately so there must be a number if it is a + // formatted string (this stops the string "k", or "kr", etc being detected + // as a formatted number for currency + if ( _empty( d ) ) { + return true; + } + + if ( decimalPoint && strType ) { + d = _numToDecimal( d, decimalPoint ); + } + + if ( formatted && strType ) { + d = d.replace( _re_formatted_numeric, '' ); + } + + return !isNaN( parseFloat(d) ) && isFinite( d ); + }; + + + // A string without HTML in it can be considered to be HTML still + var _isHtml = function ( d ) { + return _empty( d ) || typeof d === 'string'; + }; + + + var _htmlNumeric = function ( d, decimalPoint, formatted ) { + if ( _empty( d ) ) { + return true; + } + + var html = _isHtml( d ); + return ! html ? + null : + _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? + true : + null; + }; + + + var _pluck = function ( a, prop, prop2 ) { + var out = []; + var i=0, ien=a.length; + + // Could have the test in the loop for slightly smaller code, but speed + // is essential here + if ( prop2 !== undefined ) { + for ( ; i') + .css( { + position: 'fixed', + top: 0, + left: $(window).scrollLeft()*-1, // allow for scrolling + height: 1, + width: 1, + overflow: 'hidden' + } ) + .append( + $('
    ') + .css( { + position: 'absolute', + top: 1, + left: 1, + width: 100, + overflow: 'scroll' + } ) + .append( + $('
    ') + .css( { + width: '100%', + height: 10 + } ) + ) + ) + .appendTo( 'body' ); + + var outer = n.children(); + var inner = outer.children(); + + // Numbers below, in order, are: + // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth + // + // IE6 XP: 100 100 100 83 + // IE7 Vista: 100 100 100 83 + // IE 8+ Windows: 83 83 100 83 + // Evergreen Windows: 83 83 100 83 + // Evergreen Mac with scrollbars: 85 85 100 85 + // Evergreen Mac without scrollbars: 100 100 100 100 + + // Get scrollbar width + browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; + + // IE6/7 will oversize a width 100% element inside a scrolling element, to + // include the width of the scrollbar, while other browsers ensure the inner + // element is contained without forcing scrolling + browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; + + // In rtl text layout, some browsers (most, but not all) will place the + // scrollbar on the left, rather than the right. + browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; + + // IE8- don't provide height and width for getBoundingClientRect + browser.bBounding = n[0].getBoundingClientRect().width ? true : false; + + n.remove(); + } + + $.extend( settings.oBrowser, DataTable.__browser ); + settings.oScroll.iBarWidth = DataTable.__browser.barWidth; + } + + + /** + * Array.prototype reduce[Right] method, used for browsers which don't support + * JS 1.6. Done this way to reduce code size, since we iterate either way + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnReduce ( that, fn, init, start, end, inc ) + { + var + i = start, + value, + isSet = false; + + if ( init !== undefined ) { + value = init; + isSet = true; + } + + while ( i !== end ) { + if ( ! that.hasOwnProperty(i) ) { + continue; + } + + value = isSet ? + fn( value, that[i], i, that ) : + that[i]; + + isSet = true; + i += inc; + } + + return value; + } + + /** + * Add a column to the list used for the table with default values + * @param {object} oSettings dataTables settings object + * @param {node} nTh The th element for this column + * @memberof DataTable#oApi + */ + function _fnAddColumn( oSettings, nTh ) + { + // Add column to aoColumns array + var oDefaults = DataTable.defaults.column; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mData": oDefaults.mData ? oDefaults.mData : iCol, + idx: iCol + } ); + oSettings.aoColumns.push( oCol ); + + // Add search object for column specific search. Note that the `searchCols[ iCol ]` + // passed into extend can be undefined. This allows the user to give a default + // with only some of the parameters defined, and also not give a default + var searchCols = oSettings.aoPreSearchCols; + searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); + + // Use the default column options function to initialise classes etc + _fnColumnOptions( oSettings, iCol, $(nTh).data() ); + } + + + /** + * Apply options for a column + * @param {object} oSettings dataTables settings object + * @param {int} iCol column index to consider + * @param {object} oOptions object with sType, bVisible and bSearchable etc + * @memberof DataTable#oApi + */ + function _fnColumnOptions( oSettings, iCol, oOptions ) + { + var oCol = oSettings.aoColumns[ iCol ]; + var oClasses = oSettings.oClasses; + var th = $(oCol.nTh); + + // Try to get width information from the DOM. We can't get it from CSS + // as we'd need to parse the CSS stylesheet. `width` option can override + if ( ! oCol.sWidthOrig ) { + // Width attribute + oCol.sWidthOrig = th.attr('width') || null; + + // Style attribute + var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); + if ( t ) { + oCol.sWidthOrig = t[1]; + } + } + + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) + { + // Backwards compatibility + _fnCompatCols( oOptions ); + + // Map camel case parameters to their Hungarian counterparts + _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); + + /* Backwards compatibility for mDataProp */ + if ( oOptions.mDataProp !== undefined && !oOptions.mData ) + { + oOptions.mData = oOptions.mDataProp; + } + + if ( oOptions.sType ) + { + oCol._sManualType = oOptions.sType; + } + + // `class` is a reserved word in Javascript, so we need to provide + // the ability to use a valid name for the camel case input + if ( oOptions.className && ! oOptions.sClass ) + { + oOptions.sClass = oOptions.className; + } + if ( oOptions.sClass ) { + th.addClass( oOptions.sClass ); + } + + $.extend( oCol, oOptions ); + _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); + + /* iDataSort to be applied (backwards compatibility), but aDataSort will take + * priority if defined + */ + if ( oOptions.iDataSort !== undefined ) + { + oCol.aDataSort = [ oOptions.iDataSort ]; + } + _fnMap( oCol, oOptions, "aDataSort" ); + } + + /* Cache the data get and set functions for speed */ + var mDataSrc = oCol.mData; + var mData = _fnGetObjectDataFn( mDataSrc ); + var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; + + var attrTest = function( src ) { + return typeof src === 'string' && src.indexOf('@') !== -1; + }; + oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( + attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) + ); + oCol._setter = null; + + oCol.fnGetData = function (rowData, type, meta) { + var innerData = mData( rowData, type, undefined, meta ); + + return mRender && type ? + mRender( innerData, type, rowData, meta ) : + innerData; + }; + oCol.fnSetData = function ( rowData, val, meta ) { + return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); + }; + + // Indicate if DataTables should read DOM data as an object or array + // Used in _fnGetRowElements + if ( typeof mDataSrc !== 'number' ) { + oSettings._rowReadObject = true; + } + + /* Feature sorting overrides column specific when off */ + if ( !oSettings.oFeatures.bSort ) + { + oCol.bSortable = false; + th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called + } + + /* Check that the class assignment is correct for sorting */ + var bAsc = $.inArray('asc', oCol.asSorting) !== -1; + var bDesc = $.inArray('desc', oCol.asSorting) !== -1; + if ( !oCol.bSortable || (!bAsc && !bDesc) ) + { + oCol.sSortingClass = oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; + } + else if ( bAsc && !bDesc ) + { + oCol.sSortingClass = oClasses.sSortableAsc; + oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; + } + else if ( !bAsc && bDesc ) + { + oCol.sSortingClass = oClasses.sSortableDesc; + oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; + } + else + { + oCol.sSortingClass = oClasses.sSortable; + oCol.sSortingClassJUI = oClasses.sSortJUI; + } + } + + + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( settings ) + { + /* Not interested in doing column width calculation if auto-width is disabled */ + if ( settings.oFeatures.bAutoWidth !== false ) + { + var columns = settings.aoColumns; + + _fnCalculateColumnWidths( settings ); + for ( var i=0 , iLen=columns.length ; i